From 0f1765b226b3ca0d0fd8032a78561d25684a243a Mon Sep 17 00:00:00 2001 From: easyai Date: Wed, 22 Jul 2026 03:37:28 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix(gemini):=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E5=9B=BE=E5=83=8F=E7=94=9F=E6=88=90=E9=85=8D=E7=BD=AE=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/internal/clients/clients_test.go | 19 +++++++++++++++++++ apps/api/internal/clients/gemini.go | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/apps/api/internal/clients/clients_test.go b/apps/api/internal/clients/clients_test.go index 700f6d9..7f528fb 100644 --- a/apps/api/internal/clients/clients_test.go +++ b/apps/api/internal/clients/clients_test.go @@ -1306,6 +1306,25 @@ func TestGeminiClientImageGenerateBuildsNativeImageBody(t *testing.T) { } } +func TestGeminiGenerationConfigInitializesMissingImageConfig(t *testing.T) { + config := geminiGenerationConfig(map[string]any{ + "aspect_ratio": "16:9", + "resolution": "4K", + }, true) + + imageConfig, ok := config["imageConfig"].(map[string]any) + if !ok { + t.Fatalf("imageConfig should be initialized, got %+v", config) + } + if imageConfig["aspectRatio"] != "16:9" || imageConfig["imageSize"] != "4K" { + t.Fatalf("unexpected imageConfig: %+v", imageConfig) + } + modalities, ok := config["responseModalities"].([]any) + if !ok || len(modalities) != 1 || modalities[0] != "IMAGE" { + t.Fatalf("image response modality should be initialized, got %+v", config) + } +} + func TestGeminiClientImageEditPreservesNativeContentsAndFileData(t *testing.T) { var captured map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/apps/api/internal/clients/gemini.go b/apps/api/internal/clients/gemini.go index 8cfe74f..6dc86bc 100644 --- a/apps/api/internal/clients/gemini.go +++ b/apps/api/internal/clients/gemini.go @@ -195,14 +195,23 @@ func geminiApplyRequestOptions(body map[string]any, request Request, imageRespon func geminiGenerationConfig(body map[string]any, imageResponse bool) map[string]any { source := mapFromAny(firstPresent(body["generationConfig"], body["generation_config"])) out := cloneMapAny(source) + if out == nil { + out = map[string]any{} + } if aspectRatio := firstNonEmptyString(body["aspect_ratio"], body["aspectRatio"]); aspectRatio != "" { imageConfig := cloneMapAny(mapFromAny(firstPresent(out["imageConfig"], out["image_config"]))) + if imageConfig == nil { + imageConfig = map[string]any{} + } imageConfig["aspectRatio"] = aspectRatio out["imageConfig"] = imageConfig delete(out, "image_config") } if imageSize := firstNonEmptyString(body["resolution"], body["imageSize"], body["image_size"], body["size"]); imageSize != "" { imageConfig := cloneMapAny(mapFromAny(firstPresent(out["imageConfig"], out["image_config"]))) + if imageConfig == nil { + imageConfig = map[string]any{} + } imageConfig["imageSize"] = imageSize out["imageConfig"] = imageConfig delete(out, "image_config") -- 2.54.0 From 293ef24bb73b90708fb9b17438beeb56882bcce7 Mon Sep 17 00:00:00 2001 From: easyai Date: Wed, 22 Jul 2026 03:37:34 +0800 Subject: [PATCH 2/5] =?UTF-8?q?build(api):=20=E5=8D=87=E7=BA=A7=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E6=BC=8F=E6=B4=9E=E7=9A=84=20Go=20=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/go.mod | 4 ++-- apps/api/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/api/go.mod b/apps/api/go.mod index c7a95f7..b51f472 100644 --- a/apps/api/go.mod +++ b/apps/api/go.mod @@ -36,7 +36,7 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect go.uber.org/goleak v1.3.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/text v0.37.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/text v0.39.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/apps/api/go.sum b/apps/api/go.sum index 006bff2..1229fec 100644 --- a/apps/api/go.sum +++ b/apps/api/go.sum @@ -73,10 +73,10 @@ golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -- 2.54.0 From 84aea01b5b81cc14f33dc8cc7e09a907f3eff62d Mon Sep 17 00:00:00 2001 From: easyai Date: Wed, 22 Jul 2026 03:39:48 +0800 Subject: [PATCH 3/5] =?UTF-8?q?build(web):=20=E4=BF=AE=E5=A4=8D=E9=AB=98?= =?UTF-8?q?=E5=8D=B1=E9=97=B4=E6=8E=A5=E4=BE=9D=E8=B5=96=E6=BC=8F=E6=B4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 fast-uri 约束到已修复的 3.1.3,确保高危依赖审计通过。 --- pnpm-lock.yaml | 15 ++++++++------- pnpm-workspace.yaml | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 524987d..1314d12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,17 +6,18 @@ settings: overrides: '@babel/core@<=7.29.0': 7.29.6 - '@nx/js>picomatch': 4.0.5 - '@nx/vite>picomatch': 4.0.5 - '@nx/workspace>picomatch': 4.0.5 axios@<1.18.0: 1.18.0 brace-expansion@>=2.0.0 <2.1.2: 2.1.2 dompurify@<=3.4.10: 3.4.11 esbuild@>=0.27.3 <0.28.1: 0.28.1 + fast-uri@>=3.0.0 <3.1.3: 3.1.3 form-data@>=4.0.0 <4.0.6: 4.0.6 js-yaml@<4.3.0: 4.3.0 mermaid@>=11.0.0-alpha.1 <=11.14.0: 11.15.0 minimatch@>=9.0.0 <9.0.7: 9.0.7 + '@nx/js>picomatch': 4.0.5 + '@nx/vite>picomatch': 4.0.5 + '@nx/workspace>picomatch': 4.0.5 tmp@<0.2.7: 0.2.7 importers: @@ -3034,8 +3035,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -7181,7 +7182,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -7850,7 +7851,7 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-uri@3.1.2: {} + fast-uri@3.1.3: {} fdir@6.5.0(picomatch@4.0.4): optionalDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 012fa01..f407d9b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,6 +12,7 @@ overrides: 'brace-expansion@>=2.0.0 <2.1.2': 2.1.2 'dompurify@<=3.4.10': 3.4.11 'esbuild@>=0.27.3 <0.28.1': 0.28.1 + 'fast-uri@>=3.0.0 <3.1.3': 3.1.3 'form-data@>=4.0.0 <4.0.6': 4.0.6 'js-yaml@<4.3.0': 4.3.0 'mermaid@>=11.0.0-alpha.1 <=11.14.0': 11.15.0 -- 2.54.0 From c72c43aaaa6894eeae59650d2640dda9c4f414ad Mon Sep 17 00:00:00 2001 From: easyai Date: Wed, 22 Jul 2026 03:50:59 +0800 Subject: [PATCH 4/5] =?UTF-8?q?fix(universal):=20=E5=85=BC=E5=AE=B9=20serv?= =?UTF-8?q?er-main=20=E8=84=9A=E6=9C=AC=E8=AF=AD=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充历史脚本依赖的参数预处理上下文,并识别嵌套同步结果,使迁移后的 Universal 图片通道可按原配置执行。 --- apps/api/internal/clients/universal.go | 21 +++++++++++ apps/api/internal/clients/universal_test.go | 41 +++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/apps/api/internal/clients/universal.go b/apps/api/internal/clients/universal.go index f2a998a..976a20e 100644 --- a/apps/api/internal/clients/universal.go +++ b/apps/api/internal/clients/universal.go @@ -44,6 +44,8 @@ func (c UniversalClient) Run(ctx context.Context, request Request) (Response, er if err != nil { return Response{}, annotateResponseError(err, submitRequestID, startedAt, time.Now()) } + submitResult = universalEffectiveResult(submitResult) + submitRequestID = firstNonEmptyString(submitRequestID, requestIDFromResult(submitResult)) if isUniversalSuccess(submitResult) && submitResult["data"] != nil { return Response{ Result: normalizeUniversalResult(request, submitResult, ""), @@ -157,6 +159,7 @@ func (c UniversalClient) universalPollUntilDone(ctx context.Context, executor *s if err != nil { return nil, "", annotateResponseError(err, firstNonEmptyString(pollRequestID, requestID, upstreamTaskID), pollStarted, pollFinished) } + result = universalEffectiveResult(result) lastResult = result requestID = firstNonEmptyString(pollRequestID, requestID, requestIDFromResult(result), upstreamTaskID) if isUniversalSuccess(result) { @@ -239,6 +242,10 @@ func universalScriptContext(request Request, modelType string, payload map[strin return selectedBase + "/" + strings.TrimLeft(path, "/") } context["creatRequestURL"] = context["createRequestURL"] + context["processedParams"] = cloneBody(request.Body) + context["preProcessParams"] = func(params map[string]any, _ ...string) map[string]any { + return cloneMapAny(params) + } context["resolveGetTaskURL"] = func(taskID string) string { return resolveUniversalTaskURL(request.Candidate.PlatformConfig, taskID) } @@ -389,6 +396,20 @@ func universalStatus(result map[string]any) string { return strings.ToLower(strings.TrimSpace(firstNonEmptyString(result["status"], result["state"], result["task_status"]))) } +func universalEffectiveResult(result map[string]any) map[string]any { + nested, ok := result["result"].(map[string]any) + if !ok || nested == nil || isUniversalFailure(result) { + return result + } + out := cloneMapAny(nested) + for _, key := range []string{"status", "request_id", "requestId", "upstream_task_id", "task_id", "taskId", "id"} { + if out[key] == nil && result[key] != nil { + out[key] = result[key] + } + } + return out +} + func universalTaskID(result map[string]any) string { return firstNonEmptyString(result["upstream_task_id"], result["task_id"], result["taskId"], result["id"]) } diff --git a/apps/api/internal/clients/universal_test.go b/apps/api/internal/clients/universal_test.go index 325004d..f08d084 100644 --- a/apps/api/internal/clients/universal_test.go +++ b/apps/api/internal/clients/universal_test.go @@ -92,6 +92,47 @@ func TestUniversalClientDefaultSubmitAndPoll(t *testing.T) { } } +func TestUniversalClientSupportsServerMainScriptContextAndNestedResult(t *testing.T) { + request := Request{ + Kind: "images.generations", + ModelType: "image_generate", + Model: "custom-image", + Body: map[string]any{"model": "custom-image", "prompt": "hello"}, + Candidate: testUniversalCandidate(map[string]any{ + "customGetParamsScript": map[string]any{ + "image_generate": `async function getParams(params, context) { + const processed = await context.preProcessParams(params, context.type); + return { prompt: processed.prompt + "-" + context.processedParams.prompt }; + }`, + }, + "customSubmitScript": map[string]any{ + "image_generate": `async function submitTask(payload) { + return { + status: "success", + result: { + status: "success", + data: [{ url: "https://cdn.example/" + payload.prompt + ".png" }] + } + }; + }`, + }, + }), + } + + response, err := (UniversalClient{}).Run(context.Background(), request) + if err != nil { + t.Fatalf("run failed: %v", err) + } + data, ok := response.Result["data"].([]any) + if !ok || len(data) != 1 { + t.Fatalf("unexpected nested result: %#v", response.Result) + } + image, ok := data[0].(map[string]any) + if !ok || image["url"] != "https://cdn.example/hello-hello.png" { + t.Fatalf("unexpected image result: %#v", response.Result) + } +} + func TestUniversalClientResumeSkipsSubmit(t *testing.T) { request := Request{ Kind: "videos.generations", -- 2.54.0 From 16149260c7acc4c55c1e3f6bdbe226ba4fcea6e9 Mon Sep 17 00:00:00 2001 From: easyai Date: Wed, 22 Jul 2026 03:56:00 +0800 Subject: [PATCH 5/5] =?UTF-8?q?fix(universal):=20=E6=98=A0=E5=B0=84?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E4=B8=8A=E6=B8=B8=E6=A8=A1=E5=9E=8B=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 让 server-main 参数预处理兼容上下文使用平台绑定的真实模型名,避免把用户侧别名提交给上游。 --- apps/api/internal/clients/universal.go | 8 ++++++-- apps/api/internal/clients/universal_test.go | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/api/internal/clients/universal.go b/apps/api/internal/clients/universal.go index 976a20e..4d1b4da 100644 --- a/apps/api/internal/clients/universal.go +++ b/apps/api/internal/clients/universal.go @@ -242,9 +242,13 @@ func universalScriptContext(request Request, modelType string, payload map[strin return selectedBase + "/" + strings.TrimLeft(path, "/") } context["creatRequestURL"] = context["createRequestURL"] - context["processedParams"] = cloneBody(request.Body) + processedParams := cloneBody(request.Body) + processedParams["model"] = upstreamModelName(request.Candidate) + context["processedParams"] = processedParams context["preProcessParams"] = func(params map[string]any, _ ...string) map[string]any { - return cloneMapAny(params) + processed := cloneMapAny(params) + processed["model"] = upstreamModelName(request.Candidate) + return processed } context["resolveGetTaskURL"] = func(taskID string) string { return resolveUniversalTaskURL(request.Candidate.PlatformConfig, taskID) diff --git a/apps/api/internal/clients/universal_test.go b/apps/api/internal/clients/universal_test.go index f08d084..42579cf 100644 --- a/apps/api/internal/clients/universal_test.go +++ b/apps/api/internal/clients/universal_test.go @@ -102,7 +102,7 @@ func TestUniversalClientSupportsServerMainScriptContextAndNestedResult(t *testin "customGetParamsScript": map[string]any{ "image_generate": `async function getParams(params, context) { const processed = await context.preProcessParams(params, context.type); - return { prompt: processed.prompt + "-" + context.processedParams.prompt }; + return { prompt: processed.prompt + "-" + processed.model + "-" + context.processedParams.model }; }`, }, "customSubmitScript": map[string]any{ @@ -128,7 +128,7 @@ func TestUniversalClientSupportsServerMainScriptContextAndNestedResult(t *testin t.Fatalf("unexpected nested result: %#v", response.Result) } image, ok := data[0].(map[string]any) - if !ok || image["url"] != "https://cdn.example/hello-hello.png" { + if !ok || image["url"] != "https://cdn.example/hello-provider-model-provider-model.png" { t.Fatalf("unexpected image result: %#v", response.Result) } } -- 2.54.0