added -model

This commit is contained in:
Luxferre
2026-07-31 10:37:20 +03:00
parent fc19ca9a8f
commit 7e75a589e0
3 changed files with 35 additions and 15 deletions
+4 -3
View File
@@ -10,7 +10,7 @@ Hygate is a standalone, single-binary gateway that exposes Tencent's Hunyuan 3 (
- Reasoning content passthrough (`reasoning_content`)
- Tool/function calling, including incremental streaming of tool-call arguments
- Fibonacci backoff retry on upstream calls
- Configurable endpoint and User-Agent
- Configurable endpoint, model name, and User-Agent
- No external dependencies (standard library only)
## Installation
@@ -38,13 +38,14 @@ Alternatively, you can directly build from source by running `make`. This produc
Run the gateway:
```
hygate [-port 8080] [-endpoint https://tencent-hy3.hf.space]
hygate [-port 8080] [-endpoint https://tencent-hy3.hf.space] [-model hy3]
```
Available flags:
- `-port` — TCP port to listen on (default `8080`)
- `-endpoint` — root URL of the Hunyuan Gradio space (default `https://tencent-hy3.hf.space`)
- `-model` — exposed model name (default `hy3`)
- `-user-agent` / `-ua` — custom User-Agent sent to the upstream
Endpoints served:
@@ -72,7 +73,7 @@ Even through the Gradio API, Tencent Hy3 is a very fast and performant model whe
### Which models are reported?
A single model, `hy3`, is advertised via `/v1/models`.
By default, a single model, `hy3` (configurable via the `-model` flag), is advertised via `/v1/models`.
### Are token usage counts returned?
+16 -4
View File
@@ -10,7 +10,7 @@ import (
)
func TestHunyuanServiceEndpoint(t *testing.T) {
svc := NewHunyuanService("https://example.com/gradio/")
svc := NewHunyuanService("https://example.com/gradio/", "")
if svc.endpoint != "https://example.com/gradio" {
t.Errorf("expected trailing slash stripped, got %q", svc.endpoint)
}
@@ -21,6 +21,18 @@ func TestHunyuanServiceEndpoint(t *testing.T) {
}
}
func TestCustomModelNameConfiguration(t *testing.T) {
svc := NewHunyuanService("https://example.com/gradio", "hunyuan-custom-v1")
if svc.modelName != "hunyuan-custom-v1" {
t.Errorf("expected modelName 'hunyuan-custom-v1', got %q", svc.modelName)
}
models := svc.ListModels()
if len(models) != 1 || models[0].ID != "hunyuan-custom-v1" {
t.Fatalf("expected model ID 'hunyuan-custom-v1', got %v", models)
}
}
func TestParseGradioStreamData(t *testing.T) {
sampleJSON := `[["Hello world", "Thinking process...", null]]`
c, r, tc, ok := parseGradioStreamData(sampleJSON)
@@ -60,7 +72,7 @@ func TestHunyuanMockedCompletion(t *testing.T) {
server := httptest.NewServer(handler)
defer server.Close()
svc := NewHunyuanService(server.URL)
svc := NewHunyuanService(server.URL, "hy3")
rec := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
@@ -146,7 +158,7 @@ func TestHunyuanToolCallingNonStreaming(t *testing.T) {
server := httptest.NewServer(handler)
defer server.Close()
svc := NewHunyuanService(server.URL)
svc := NewHunyuanService(server.URL, "hy3")
rec := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
@@ -230,7 +242,7 @@ func TestHunyuanReasoningAndToolCallingStreaming(t *testing.T) {
server := httptest.NewServer(handler)
defer server.Close()
svc := NewHunyuanService(server.URL)
svc := NewHunyuanService(server.URL, "hy3")
rec := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
+15 -8
View File
@@ -375,29 +375,34 @@ func parseGradioStreamData(dataJSON string) (string, string, []ToolCall, bool) {
}
type HunyuanService struct {
endpoint string
client *http.Client
endpoint string
modelName string
client *http.Client
}
func NewHunyuanService(endpoint string) *HunyuanService {
func NewHunyuanService(endpoint string, modelName string) *HunyuanService {
cleanEndpoint := strings.TrimRight(endpoint, "/")
if modelName == "" {
modelName = "hy3"
}
return &HunyuanService{
endpoint: cleanEndpoint,
client: &http.Client{Timeout: 300 * time.Second},
endpoint: cleanEndpoint,
modelName: modelName,
client: &http.Client{Timeout: 300 * time.Second},
}
}
func (s *HunyuanService) ListModels() []ModelItem {
now := time.Now().Unix()
return []ModelItem{
{ID: "hy3", Object: "model", Created: now, OwnedBy: "hunyuan"},
{ID: s.modelName, Object: "model", Created: now, OwnedBy: "hunyuan"},
}
}
func (s *HunyuanService) Chat(w http.ResponseWriter, r *http.Request, req ChatCompletionRequest) error {
modelName := req.Model
if modelName == "" {
modelName = "hy3"
modelName = s.modelName
}
maxTokens := ResolveMaxTokens(req)
@@ -722,6 +727,7 @@ func NewMux(service *HunyuanService) *http.ServeMux {
func main() {
portFlag := flag.String("port", "8080", "Port to listen on")
endpointFlag := flag.String("endpoint", "https://tencent-hy3.hf.space", "Root URL of the Hunyuan Gradio space")
modelFlag := flag.String("model", "hy3", "Exposed model name")
uaFlag := flag.String("user-agent", DefaultUserAgent, "Custom User-Agent header")
uaShortFlag := flag.String("ua", "", "Alias for -user-agent")
flag.Parse()
@@ -731,11 +737,12 @@ func main() {
ConfiguredUserAgent = *uaShortFlag
}
service := NewHunyuanService(*endpointFlag)
service := NewHunyuanService(*endpointFlag, *modelFlag)
mux := NewMux(service)
fmt.Printf("hygate starting on port %s...\n", *portFlag)
fmt.Printf("Target Endpoint: %s\n", service.endpoint)
fmt.Printf("Model Name: %s\n", service.modelName)
fmt.Printf("User-Agent: %s\n", ConfiguredUserAgent)
fmt.Printf("Endpoints:\n")
fmt.Printf(" GET http://localhost:%s/v1/models\n", *portFlag)