feat(api): migrate volces client to gateway
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
||||
@@ -169,6 +170,146 @@ func TestGeminiURLAcceptsVersionedBaseURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolcesClientImageEditUsesGenerationEndpoint(t *testing.T) {
|
||||
var gotPath string
|
||||
var gotAuth string
|
||||
var gotModel string
|
||||
var gotImage string
|
||||
var gotSequential string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotPath = r.URL.Path
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode request: %v", err)
|
||||
}
|
||||
gotModel, _ = body["model"].(string)
|
||||
gotImage, _ = body["image"].(string)
|
||||
gotSequential, _ = body["sequential_image_generation"].(string)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"id": "img-volces-edit",
|
||||
"created": 123,
|
||||
"data": []any{map[string]any{"url": "https://example.com/out.png"}},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
response, err := (VolcesClient{HTTPClient: server.Client()}).Run(context.Background(), Request{
|
||||
Kind: "images.edits",
|
||||
ModelType: "image_edit",
|
||||
Model: "doubao-4.0图像编辑",
|
||||
Body: map[string]any{
|
||||
"model": "doubao-4.0图像编辑",
|
||||
"prompt": "make it brighter",
|
||||
"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"},
|
||||
Capabilities: map[string]any{
|
||||
"image_edit": map[string]any{"output_multiple_images": true},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("run volces image edit: %v", err)
|
||||
}
|
||||
if gotPath != "/images/generations" || gotAuth != "Bearer volces-key" {
|
||||
t.Fatalf("unexpected request path=%s auth=%s", gotPath, gotAuth)
|
||||
}
|
||||
if gotModel != "doubao-seedream-4-0-250828" || gotImage != "https://example.com/source.png" || gotSequential != "auto" {
|
||||
t.Fatalf("unexpected body model=%s image=%s sequential=%s", gotModel, gotImage, gotSequential)
|
||||
}
|
||||
if response.Result["id"] != "img-volces-edit" {
|
||||
t.Fatalf("unexpected response: %+v", response.Result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolcesClientVideoSubmitsAndPollsTask(t *testing.T) {
|
||||
var submitPath string
|
||||
var pollPath string
|
||||
var gotAuth string
|
||||
var gotModel string
|
||||
var gotText string
|
||||
var gotFirstFrameRole string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
switch r.Method + " " + r.URL.Path {
|
||||
case "POST /contents/generations/tasks":
|
||||
submitPath = r.URL.Path
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode request: %v", err)
|
||||
}
|
||||
gotModel, _ = body["model"].(string)
|
||||
if body["prompt"] != nil || body["first_frame"] != nil {
|
||||
t.Fatalf("video convenience fields leaked upstream: %+v", body)
|
||||
}
|
||||
content, _ := body["content"].([]any)
|
||||
textItem, _ := content[0].(map[string]any)
|
||||
gotText, _ = textItem["text"].(string)
|
||||
frameItem, _ := content[1].(map[string]any)
|
||||
gotFirstFrameRole, _ = frameItem["role"].(string)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": "cgt-test"})
|
||||
case "GET /contents/generations/tasks/cgt-test":
|
||||
pollPath = r.URL.Path
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"id": "cgt-test",
|
||||
"model": "doubao-seedance-2-0-260128",
|
||||
"status": "succeeded",
|
||||
"created_at": 456,
|
||||
"content": map[string]any{"video_url": "https://example.com/out.mp4"},
|
||||
"usage": map[string]any{"completion_tokens": 7, "total_tokens": 9},
|
||||
})
|
||||
default:
|
||||
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
response, err := (VolcesClient{HTTPClient: server.Client()}).Run(context.Background(), Request{
|
||||
Kind: "videos.generations",
|
||||
ModelType: "video_generate",
|
||||
Model: "豆包Seedance-2.0",
|
||||
Body: map[string]any{
|
||||
"model": "豆包Seedance-2.0",
|
||||
"prompt": "A clean product reveal",
|
||||
"first_frame": "https://example.com/first.png",
|
||||
"duration": 6,
|
||||
"aspect_ratio": "16:9",
|
||||
},
|
||||
Candidate: store.RuntimeModelCandidate{
|
||||
BaseURL: server.URL,
|
||||
ModelName: "doubao-seedance-2-0-260128",
|
||||
Credentials: map[string]any{"apiKey": "volces-key"},
|
||||
PlatformConfig: map[string]any{
|
||||
"volcesPollIntervalMs": 100,
|
||||
"volcesPollTimeoutSeconds": 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("run volces video: %v", err)
|
||||
}
|
||||
if submitPath != "/contents/generations/tasks" || pollPath != "/contents/generations/tasks/cgt-test" || gotAuth != "Bearer volces-key" {
|
||||
t.Fatalf("unexpected paths/auth submit=%s poll=%s auth=%s", submitPath, pollPath, gotAuth)
|
||||
}
|
||||
if gotModel != "doubao-seedance-2-0-260128" || gotFirstFrameRole != "first_frame" {
|
||||
t.Fatalf("unexpected submitted model=%s role=%s", gotModel, gotFirstFrameRole)
|
||||
}
|
||||
for _, fragment := range []string{"A clean product reveal", "--dur 6", "--ratio 16:9", "--watermark false", "--seed -1"} {
|
||||
if !strings.Contains(gotText, fragment) {
|
||||
t.Fatalf("expected text to contain %q, got %q", fragment, gotText)
|
||||
}
|
||||
}
|
||||
data, _ := response.Result["data"].([]any)
|
||||
item, _ := data[0].(map[string]any)
|
||||
if item["url"] != "https://example.com/out.mp4" || response.Usage.TotalTokens != 9 {
|
||||
t.Fatalf("unexpected response: %+v usage=%+v", response.Result, response.Usage)
|
||||
}
|
||||
}
|
||||
|
||||
func extractText(result map[string]any) string {
|
||||
choices, _ := result["choices"].([]any)
|
||||
choice, _ := choices[0].(map[string]any)
|
||||
|
||||
Reference in New Issue
Block a user