feat: improve model catalog aggregation
This commit is contained in:
@@ -127,15 +127,16 @@ func TestOpenAIClientChatContract(t *testing.T) {
|
||||
Model: "openai:gpt-4o-mini",
|
||||
Body: map[string]any{"model": "openai:gpt-4o-mini", "messages": []any{map[string]any{"role": "user", "content": "ping"}}},
|
||||
Candidate: store.RuntimeModelCandidate{
|
||||
BaseURL: server.URL,
|
||||
ModelName: "gpt-4o-mini",
|
||||
Credentials: map[string]any{"apiKey": "test-key"},
|
||||
BaseURL: server.URL,
|
||||
ModelName: "gpt-4o-mini",
|
||||
ProviderModelName: "openai-compatible-gpt-4o-mini",
|
||||
Credentials: map[string]any{"apiKey": "test-key"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("run openai client: %v", err)
|
||||
}
|
||||
if gotPath != "/chat/completions" || gotAuth != "Bearer test-key" || gotModel != "gpt-4o-mini" {
|
||||
if gotPath != "/chat/completions" || gotAuth != "Bearer test-key" || gotModel != "openai-compatible-gpt-4o-mini" {
|
||||
t.Fatalf("unexpected request path=%s auth=%s model=%s", gotPath, gotAuth, gotModel)
|
||||
}
|
||||
if response.Usage.TotalTokens != 5 || response.Result["id"] != "chatcmpl-test" {
|
||||
@@ -231,16 +232,17 @@ func TestGeminiClientChatContract(t *testing.T) {
|
||||
"messages": []any{map[string]any{"role": "user", "content": "ping"}},
|
||||
},
|
||||
Candidate: store.RuntimeModelCandidate{
|
||||
BaseURL: server.URL,
|
||||
ModelName: "gemini-2.5-flash",
|
||||
ModelType: "chat",
|
||||
Credentials: map[string]any{"apiKey": "gemini-key"},
|
||||
BaseURL: server.URL,
|
||||
ModelName: "gemini-2.5-flash",
|
||||
ProviderModelName: "gemini-compatible-2.5-flash",
|
||||
ModelType: "chat",
|
||||
Credentials: map[string]any{"apiKey": "gemini-key"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("run gemini client: %v", err)
|
||||
}
|
||||
if gotPath != "/v1beta/models/gemini-2.5-flash:generateContent" || gotKey != "gemini-key" || gotText != "ping" {
|
||||
if gotPath != "/v1beta/models/gemini-compatible-2.5-flash:generateContent" || gotKey != "gemini-key" || gotText != "ping" {
|
||||
t.Fatalf("unexpected request path=%s key=%s text=%s", gotPath, gotKey, gotText)
|
||||
}
|
||||
if response.Usage.TotalTokens != 10 || extractText(response.Result) != "gemini ok" {
|
||||
@@ -290,9 +292,10 @@ func TestVolcesClientImageEditUsesGenerationEndpoint(t *testing.T) {
|
||||
"image": "https://example.com/source.png",
|
||||
},
|
||||
Candidate: store.RuntimeModelCandidate{
|
||||
BaseURL: server.URL,
|
||||
ModelName: "doubao-seedream-4-0-250828",
|
||||
Credentials: map[string]any{"apiKey": "volces-key"},
|
||||
BaseURL: server.URL,
|
||||
ModelName: "豆包Seedream-4.0",
|
||||
ProviderModelName: "doubao-seedream-4-0-250828",
|
||||
Credentials: map[string]any{"apiKey": "volces-key"},
|
||||
Capabilities: map[string]any{
|
||||
"image_edit": map[string]any{"output_multiple_images": true},
|
||||
},
|
||||
@@ -366,9 +369,10 @@ func TestVolcesClientVideoSubmitsAndPollsTask(t *testing.T) {
|
||||
"aspect_ratio": "16:9",
|
||||
},
|
||||
Candidate: store.RuntimeModelCandidate{
|
||||
BaseURL: server.URL,
|
||||
ModelName: "doubao-seedance-2-0-260128",
|
||||
Credentials: map[string]any{"apiKey": "volces-key"},
|
||||
BaseURL: server.URL,
|
||||
ModelName: "豆包Seedance-2.0",
|
||||
ProviderModelName: "doubao-seedance-2-0-260128",
|
||||
Credentials: map[string]any{"apiKey": "volces-key"},
|
||||
PlatformConfig: map[string]any{
|
||||
"volcesPollIntervalMs": 100,
|
||||
"volcesPollTimeoutSeconds": 1,
|
||||
|
||||
@@ -22,7 +22,7 @@ func (c GeminiClient) Run(ctx context.Context, request Request) (Response, error
|
||||
}
|
||||
body := geminiBody(request)
|
||||
raw, _ := json.Marshal(body)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, geminiURL(request.Candidate.BaseURL, request.Candidate.ModelName, apiKey), bytes.NewReader(raw))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, geminiURL(request.Candidate.BaseURL, upstreamModelName(request.Candidate), apiKey), bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func (c OpenAIClient) Run(ctx context.Context, request Request) (Response, error
|
||||
return Response{}, &ClientError{Code: "unsupported_kind", Message: "unsupported openai request kind", Retryable: false}
|
||||
}
|
||||
body := cloneBody(request.Body)
|
||||
body["model"] = request.Candidate.ModelName
|
||||
body["model"] = upstreamModelName(request.Candidate)
|
||||
stream := request.Stream || boolValue(body, "stream")
|
||||
raw, _ := json.Marshal(body)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, joinURL(request.Candidate.BaseURL, endpoint), bytes.NewReader(raw))
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
||||
@@ -72,6 +73,13 @@ func IsRetryable(err error) bool {
|
||||
return errors.As(err, &clientErr) && clientErr.Retryable
|
||||
}
|
||||
|
||||
func upstreamModelName(candidate store.RuntimeModelCandidate) string {
|
||||
if name := strings.TrimSpace(candidate.ProviderModelName); name != "" {
|
||||
return name
|
||||
}
|
||||
return candidate.ModelName
|
||||
}
|
||||
|
||||
func ErrorCode(err error) string {
|
||||
var clientErr *ClientError
|
||||
if errors.As(err, &clientErr) && clientErr.Code != "" {
|
||||
|
||||
@@ -177,7 +177,7 @@ func (c VolcesClient) getJSON(ctx context.Context, baseURL string, path string,
|
||||
|
||||
func volcesImageBody(request Request) map[string]any {
|
||||
body := cleanProviderBody(request.Body)
|
||||
body["model"] = request.Candidate.ModelName
|
||||
body["model"] = upstreamModelName(request.Candidate)
|
||||
if _, ok := body["watermark"]; !ok {
|
||||
body["watermark"] = false
|
||||
}
|
||||
@@ -200,7 +200,7 @@ func volcesImageBody(request Request) map[string]any {
|
||||
|
||||
func volcesVideoBody(request Request) map[string]any {
|
||||
body := cleanProviderBody(request.Body)
|
||||
body["model"] = request.Candidate.ModelName
|
||||
body["model"] = upstreamModelName(request.Candidate)
|
||||
content := contentItems(body["content"])
|
||||
if len(content) == 0 {
|
||||
content = buildVolcesContentFromBody(body)
|
||||
@@ -515,7 +515,7 @@ func volcesVideoSuccessResult(request Request, upstreamTaskID string, raw map[st
|
||||
"id": upstreamTaskID,
|
||||
"object": "video.generation",
|
||||
"created": created,
|
||||
"model": request.Candidate.ModelName,
|
||||
"model": upstreamModelName(request.Candidate),
|
||||
"status": "succeeded",
|
||||
"upstream_task_id": upstreamTaskID,
|
||||
"data": data,
|
||||
|
||||
Reference in New Issue
Block a user