feat: add river-backed async task queue
This commit is contained in:
@@ -329,6 +329,7 @@ func TestVolcesClientVideoSubmitsAndPollsTask(t *testing.T) {
|
||||
var gotModel string
|
||||
var gotText string
|
||||
var gotFirstFrameRole string
|
||||
var submittedRemoteTaskID string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
switch r.Method + " " + r.URL.Path {
|
||||
@@ -385,6 +386,13 @@ func TestVolcesClientVideoSubmitsAndPollsTask(t *testing.T) {
|
||||
"volcesPollTimeoutSeconds": 1,
|
||||
},
|
||||
},
|
||||
OnRemoteTaskSubmitted: func(remoteTaskID string, payload map[string]any) error {
|
||||
submittedRemoteTaskID = remoteTaskID
|
||||
if payload["id"] != "cgt-test" {
|
||||
t.Fatalf("unexpected submitted payload: %+v", payload)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("run volces video: %v", err)
|
||||
@@ -392,6 +400,9 @@ func TestVolcesClientVideoSubmitsAndPollsTask(t *testing.T) {
|
||||
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 submittedRemoteTaskID != "cgt-test" {
|
||||
t.Fatalf("remote task submit callback did not receive task id, got %q", submittedRemoteTaskID)
|
||||
}
|
||||
if gotModel != "doubao-seedance-2-0-260128" || gotFirstFrameRole != "first_frame" {
|
||||
t.Fatalf("unexpected submitted model=%s role=%s", gotModel, gotFirstFrameRole)
|
||||
}
|
||||
@@ -407,6 +418,53 @@ func TestVolcesClientVideoSubmitsAndPollsTask(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolcesClientVideoResumePollsExistingTaskID(t *testing.T) {
|
||||
var submitCalled bool
|
||||
var pollPath string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method + " " + r.URL.Path {
|
||||
case "POST /contents/generations/tasks":
|
||||
submitCalled = true
|
||||
t.Fatalf("resume should skip upstream submit when remote task id exists")
|
||||
case "GET /contents/generations/tasks/cgt-existing":
|
||||
pollPath = r.URL.Path
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"id": "cgt-existing",
|
||||
"status": "succeeded",
|
||||
"created_at": 789,
|
||||
"content": map[string]any{"video_url": "https://example.com/resumed.mp4"},
|
||||
})
|
||||
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{"prompt": "resume polling", "pollIntervalMs": 100, "pollTimeoutSeconds": 1},
|
||||
RemoteTaskID: "cgt-existing",
|
||||
Candidate: store.RuntimeModelCandidate{
|
||||
BaseURL: server.URL,
|
||||
ModelName: "豆包Seedance-2.0",
|
||||
Credentials: map[string]any{"apiKey": "volces-key"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("resume volces video: %v", err)
|
||||
}
|
||||
if submitCalled || pollPath != "/contents/generations/tasks/cgt-existing" {
|
||||
t.Fatalf("resume should poll existing task only, submit=%v poll=%s", submitCalled, pollPath)
|
||||
}
|
||||
data, _ := response.Result["data"].([]any)
|
||||
item, _ := data[0].(map[string]any)
|
||||
if response.Result["upstream_task_id"] != "cgt-existing" || item["url"] != "https://example.com/resumed.mp4" {
|
||||
t.Fatalf("unexpected resumed response: %+v", response.Result)
|
||||
}
|
||||
}
|
||||
|
||||
func extractText(result map[string]any) string {
|
||||
choices, _ := result["choices"].([]any)
|
||||
choice, _ := choices[0].(map[string]any)
|
||||
|
||||
@@ -11,14 +11,17 @@ import (
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Kind string
|
||||
ModelType string
|
||||
Model string
|
||||
Body map[string]any
|
||||
Candidate store.RuntimeModelCandidate
|
||||
HTTPClient *http.Client
|
||||
Stream bool
|
||||
StreamDelta StreamDelta
|
||||
Kind string
|
||||
ModelType string
|
||||
Model string
|
||||
Body map[string]any
|
||||
Candidate store.RuntimeModelCandidate
|
||||
HTTPClient *http.Client
|
||||
RemoteTaskID string
|
||||
RemoteTaskPayload map[string]any
|
||||
OnRemoteTaskSubmitted func(remoteTaskID string, payload map[string]any) error
|
||||
Stream bool
|
||||
StreamDelta StreamDelta
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
|
||||
@@ -67,16 +67,25 @@ func (c VolcesClient) runImage(ctx context.Context, request Request, apiKey stri
|
||||
}
|
||||
|
||||
func (c VolcesClient) runVideo(ctx context.Context, request Request, apiKey string) (Response, error) {
|
||||
body := volcesVideoBody(request)
|
||||
submitStartedAt := time.Now()
|
||||
submitResult, submitRequestID, err := c.postJSON(ctx, request, request.Candidate.BaseURL, "/contents/generations/tasks", apiKey, body)
|
||||
submitFinishedAt := time.Now()
|
||||
if err != nil {
|
||||
return Response{}, annotateResponseError(err, submitRequestID, submitStartedAt, submitFinishedAt)
|
||||
}
|
||||
upstreamTaskID := strings.TrimSpace(stringFromAny(submitResult["id"]))
|
||||
submitRequestID := strings.TrimSpace(request.RemoteTaskID)
|
||||
upstreamTaskID := strings.TrimSpace(request.RemoteTaskID)
|
||||
if upstreamTaskID == "" {
|
||||
return Response{}, &ClientError{Code: "invalid_response", Message: "volces video task id is missing", RequestID: submitRequestID, Retryable: false}
|
||||
body := volcesVideoBody(request)
|
||||
submitResult, requestID, err := c.postJSON(ctx, request, request.Candidate.BaseURL, "/contents/generations/tasks", apiKey, body)
|
||||
submitRequestID = requestID
|
||||
if err != nil {
|
||||
return Response{}, annotateResponseError(err, submitRequestID, submitStartedAt, time.Now())
|
||||
}
|
||||
upstreamTaskID = strings.TrimSpace(stringFromAny(submitResult["id"]))
|
||||
if upstreamTaskID == "" {
|
||||
return Response{}, &ClientError{Code: "invalid_response", Message: "volces video task id is missing", RequestID: submitRequestID, Retryable: false}
|
||||
}
|
||||
if request.OnRemoteTaskSubmitted != nil {
|
||||
if err := request.OnRemoteTaskSubmitted(upstreamTaskID, submitResult); err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interval := volcesPollInterval(request)
|
||||
|
||||
Reference in New Issue
Block a user