added -model
This commit is contained in:
@@ -10,7 +10,7 @@ Hygate is a standalone, single-binary gateway that exposes Tencent's Hunyuan 3 (
|
|||||||
- Reasoning content passthrough (`reasoning_content`)
|
- Reasoning content passthrough (`reasoning_content`)
|
||||||
- Tool/function calling, including incremental streaming of tool-call arguments
|
- Tool/function calling, including incremental streaming of tool-call arguments
|
||||||
- Fibonacci backoff retry on upstream calls
|
- Fibonacci backoff retry on upstream calls
|
||||||
- Configurable endpoint and User-Agent
|
- Configurable endpoint, model name, and User-Agent
|
||||||
- No external dependencies (standard library only)
|
- No external dependencies (standard library only)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -38,13 +38,14 @@ Alternatively, you can directly build from source by running `make`. This produc
|
|||||||
Run the gateway:
|
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:
|
Available flags:
|
||||||
|
|
||||||
- `-port` — TCP port to listen on (default `8080`)
|
- `-port` — TCP port to listen on (default `8080`)
|
||||||
- `-endpoint` — root URL of the Hunyuan Gradio space (default `https://tencent-hy3.hf.space`)
|
- `-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
|
- `-user-agent` / `-ua` — custom User-Agent sent to the upstream
|
||||||
|
|
||||||
Endpoints served:
|
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?
|
### 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?
|
### Are token usage counts returned?
|
||||||
|
|
||||||
|
|||||||
+16
-4
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestHunyuanServiceEndpoint(t *testing.T) {
|
func TestHunyuanServiceEndpoint(t *testing.T) {
|
||||||
svc := NewHunyuanService("https://example.com/gradio/")
|
svc := NewHunyuanService("https://example.com/gradio/", "")
|
||||||
if svc.endpoint != "https://example.com/gradio" {
|
if svc.endpoint != "https://example.com/gradio" {
|
||||||
t.Errorf("expected trailing slash stripped, got %q", svc.endpoint)
|
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) {
|
func TestParseGradioStreamData(t *testing.T) {
|
||||||
sampleJSON := `[["Hello world", "Thinking process...", null]]`
|
sampleJSON := `[["Hello world", "Thinking process...", null]]`
|
||||||
c, r, tc, ok := parseGradioStreamData(sampleJSON)
|
c, r, tc, ok := parseGradioStreamData(sampleJSON)
|
||||||
@@ -60,7 +72,7 @@ func TestHunyuanMockedCompletion(t *testing.T) {
|
|||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
svc := NewHunyuanService(server.URL)
|
svc := NewHunyuanService(server.URL, "hy3")
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
|
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
|
||||||
@@ -146,7 +158,7 @@ func TestHunyuanToolCallingNonStreaming(t *testing.T) {
|
|||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
svc := NewHunyuanService(server.URL)
|
svc := NewHunyuanService(server.URL, "hy3")
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
|
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
|
||||||
|
|
||||||
@@ -230,7 +242,7 @@ func TestHunyuanReasoningAndToolCallingStreaming(t *testing.T) {
|
|||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
svc := NewHunyuanService(server.URL)
|
svc := NewHunyuanService(server.URL, "hy3")
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
|
req, _ := http.NewRequest("POST", "/v1/chat/completions", nil)
|
||||||
|
|
||||||
|
|||||||
@@ -375,29 +375,34 @@ func parseGradioStreamData(dataJSON string) (string, string, []ToolCall, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HunyuanService struct {
|
type HunyuanService struct {
|
||||||
endpoint string
|
endpoint string
|
||||||
client *http.Client
|
modelName string
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHunyuanService(endpoint string) *HunyuanService {
|
func NewHunyuanService(endpoint string, modelName string) *HunyuanService {
|
||||||
cleanEndpoint := strings.TrimRight(endpoint, "/")
|
cleanEndpoint := strings.TrimRight(endpoint, "/")
|
||||||
|
if modelName == "" {
|
||||||
|
modelName = "hy3"
|
||||||
|
}
|
||||||
return &HunyuanService{
|
return &HunyuanService{
|
||||||
endpoint: cleanEndpoint,
|
endpoint: cleanEndpoint,
|
||||||
client: &http.Client{Timeout: 300 * time.Second},
|
modelName: modelName,
|
||||||
|
client: &http.Client{Timeout: 300 * time.Second},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HunyuanService) ListModels() []ModelItem {
|
func (s *HunyuanService) ListModels() []ModelItem {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
return []ModelItem{
|
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 {
|
func (s *HunyuanService) Chat(w http.ResponseWriter, r *http.Request, req ChatCompletionRequest) error {
|
||||||
modelName := req.Model
|
modelName := req.Model
|
||||||
if modelName == "" {
|
if modelName == "" {
|
||||||
modelName = "hy3"
|
modelName = s.modelName
|
||||||
}
|
}
|
||||||
maxTokens := ResolveMaxTokens(req)
|
maxTokens := ResolveMaxTokens(req)
|
||||||
|
|
||||||
@@ -722,6 +727,7 @@ func NewMux(service *HunyuanService) *http.ServeMux {
|
|||||||
func main() {
|
func main() {
|
||||||
portFlag := flag.String("port", "8080", "Port to listen on")
|
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")
|
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")
|
uaFlag := flag.String("user-agent", DefaultUserAgent, "Custom User-Agent header")
|
||||||
uaShortFlag := flag.String("ua", "", "Alias for -user-agent")
|
uaShortFlag := flag.String("ua", "", "Alias for -user-agent")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -731,11 +737,12 @@ func main() {
|
|||||||
ConfiguredUserAgent = *uaShortFlag
|
ConfiguredUserAgent = *uaShortFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
service := NewHunyuanService(*endpointFlag)
|
service := NewHunyuanService(*endpointFlag, *modelFlag)
|
||||||
mux := NewMux(service)
|
mux := NewMux(service)
|
||||||
|
|
||||||
fmt.Printf("hygate starting on port %s...\n", *portFlag)
|
fmt.Printf("hygate starting on port %s...\n", *portFlag)
|
||||||
fmt.Printf("Target Endpoint: %s\n", service.endpoint)
|
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("User-Agent: %s\n", ConfiguredUserAgent)
|
||||||
fmt.Printf("Endpoints:\n")
|
fmt.Printf("Endpoints:\n")
|
||||||
fmt.Printf(" GET http://localhost:%s/v1/models\n", *portFlag)
|
fmt.Printf(" GET http://localhost:%s/v1/models\n", *portFlag)
|
||||||
|
|||||||
Reference in New Issue
Block a user