diff --git a/docs/patches.md b/docs/patches.md index b8c5903a0cc2..5beccb490869 100644 --- a/docs/patches.md +++ b/docs/patches.md @@ -14,9 +14,35 @@ This file tracks patches deployed in this fork before they are accepted upstream | Patch | Status | Source | Imported Branch | Deployment Branch | Data Impact | Rollback | | --- | --- | --- | --- | --- | --- | --- | | PR 4810 - Ali wan2.7 and HappyHorse video support | `Deployed` | https://github.com/QuantumNous/new-api/pull/4810 | `vendor/pr-4810` + `compat/pr-4810` | `deploy/main` | No patch-specific migration; task-plugin usage and billing configuration change | Revert the compatibility merge to restore the official Alibaba plugin; no data cleanup expected | +| Wan 2.7 image generation protocol guard | `Proposed` | Local fix for desirecore/desirecore#3522 | `codex/wan27-image-route-rc33` | `codex/release-rc33` candidate | No schema migration; two exact model IDs ignore stale `qwen.sync_image_models` when selecting image protocol | Revert the scoped fix; the persisted setting remains unchanged | ## Patch Entries +### Wan 2.7 image generation protocol guard + +- Status: `Proposed`; not merged or deployed. +- Source: local fix for https://github.com/desirecore/desirecore/issues/3522, commit `39b1b0b`. +- Patch branch: `codex/wan27-image-route-rc33`. +- Candidate branch: `codex/release-rc33`; production branch `deploy/main` remains unchanged. +- Owner: desirecore. + +#### Purpose and evidence + +The candidate's `Ali` image adaptor chooses the legacy `text2image/image-synthesis` endpoint when a persisted `qwen.sync_image_models` list from before Wan 2.7 omits these model IDs. Alibaba documents Wan 2.7 synchronous image generation at `multimodal-generation/generation`. A regression test first reproduced the old endpoint, async header, and prompt-only body under that persisted-list fixture; the patch makes both exact Wan 2.7 image IDs use the documented synchronous protocol. The live gateway's actual setting and selected channel remain unverified, so this patch is a targeted candidate, not proof that issue #3522 is resolved. + +#### Data compatibility and rollback + +- No database, migration, log schema, API response, or billing change. +- Existing `qwen.sync_image_models` data is preserved. Only `wan2.7-image` and `wan2.7-image-pro` bypass a stale list for image generation routing. +- Revert the scoped commit to restore previous behavior; no data cleanup is needed. Do not promote before testing against a representative copy of the gateway configuration and performing real requests to both models. +- Compare a future official upstream fix to this exact-model guard, then remove the local delta once the official behavior is verified. + +#### Verification + +- `go test ./relay/channel/ali -count=1` passed on the rc.33 candidate branch. +- Real model generation through a candidate image has not yet been tested; production deployment has not occurred. + + ### PR 4810 - Ali wan2.7 and HappyHorse video support - Status: `Deployed` diff --git a/docs/release-rc33.md b/docs/release-rc33.md index 27629064fa02..c19eb9f616b6 100644 --- a/docs/release-rc33.md +++ b/docs/release-rc33.md @@ -6,7 +6,8 @@ - Candidate branch: `codex/release-rc33`. - Intended fork version: `v1.0.0-rc.33-desirecore.1`. - Production predecessor: `v1.0.0-rc.30-desirecore.2` at `f98e7b6b44d5a2f1bcd1932d5576fc6e5215bcc4`. -- Only imported runtime delta: PR 4810 Alibaba compatibility commits `f6520a996` and `7d7cf46e7`, cherry-picked with provenance as `b7b3a10f5` and `3ba267dd3`. Plugin code and its original tests are identical to the production predecessor. +- Imported runtime delta: PR 4810 Alibaba compatibility commits `f6520a996` and `7d7cf46e7`, cherry-picked with provenance as `b7b3a10f5` and `3ba267dd3`. Plugin code and its original tests are identical to the production predecessor. +- Proposed additional candidate delta: exact-model Wan 2.7 image generation routing guard for stale persisted `qwen.sync_image_models`; see `docs/patches.md`. This has not been merged into the tested image or deployed. - Other fork changes: maintenance rules, patch register, this handoff, manual GHCR/Tencent publishing workflow, exclusions for fork tags in official publishing workflows, and a focused Qwen Claude regression test. - Official rc.33 is a prerelease and its release notes do not recommend production use. This candidate is for acceptance testing first. @@ -18,6 +19,7 @@ - In `relaykit/`, `GOWORK=off go test ./...` and `GOWORK=off go build ./...`: passed. - `TEST_MYSQL_DSN='' TEST_POSTGRES_DSN='' go test ./model -count=1`: passed against local disposable MySQL 8.0.46 and PostgreSQL 15.18, plus embedded SQLite 3.50.4. Ports: 127.0.0.1:13316 and 127.0.0.1:15436; database: `newapi_test`. Test credentials are local fixtures, not production credentials. - Compared schema/model definitions, startup migration code, and Go dependencies with the production predecessor: no changes. Two model files change routing-name normalization only. +- For the proposed Wan 2.7 image guard: `go test ./relay/channel/ali -count=1` passed on a branch based on this rc.33 candidate. Its regression test first failed under a simulated old persisted sync-model list, then passed with the scoped fix. This does not verify the live gateway setting or image generation. ## Operator Acceptance Required @@ -29,5 +31,6 @@ Image publication is not deployment rehearsal. The following have NOT been perfo 4. Verify Alibaba video creation, historical task polling, results, decimal durations, pre-consume, settlement and failure refunds. Check both JSON and multipart requests and current prices. 5. Rehearse rollback against the upgraded test database and configuration. Do not assume an older image alone is sufficient; restoring a snapshot after new writes requires reconciliation. 6. Record operator results, exact tested image digest and acceptance before production promotion. Keep production `deploy/main` and the existing release tags unchanged until acceptance. +7. If the proposed Wan 2.7 image guard is included, inspect the candidate's effective Alibaba channel type, upstream model mapping, base URL and persisted `qwen.sync_image_models`. Then call `wan2.7-image` and `wan2.7-image-pro` through `/v1/images/generations` and verify actual image output. The same HTTP 400 can have other gateway configuration causes; the local regression test alone cannot close issue #3522. Publish candidate images under a `test-` tag prefix. Record the immutable build SHA and digest from the workflow run in the acceptance record; do not deploy an unverified rebuilt image under the same version. diff --git a/relay/channel/ali/adaptor.go b/relay/channel/ali/adaptor.go index 14d77156c746..1b721a5973b6 100644 --- a/relay/channel/ali/adaptor.go +++ b/relay/channel/ali/adaptor.go @@ -62,6 +62,12 @@ var syncModels = []string{ } func isSyncImageModel(modelName string) bool { + // Wan 2.7 generation uses the multimodal endpoint even when an older + // persisted sync_image_models setting has not gained these model IDs. + // https://help.aliyun.com/zh/model-studio/wan-image-generation-and-editing-api-reference + if modelName == "wan2.7-image" || modelName == "wan2.7-image-pro" { + return true + } return model_setting.IsSyncImageModel(modelName) } diff --git a/relay/channel/ali/adaptor_test.go b/relay/channel/ali/adaptor_test.go index ec44c5968aab..a6e43abc3e23 100644 --- a/relay/channel/ali/adaptor_test.go +++ b/relay/channel/ali/adaptor_test.go @@ -19,6 +19,7 @@ import ( relayhelper "github.com/QuantumNous/new-api/relay/helper" "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" + "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/system_setting" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" @@ -169,6 +170,46 @@ func TestMappedAliImageModelUsesUpstreamProtocol(t *testing.T) { assert.IsType(t, &AliImageRequest{}, converted) } +func TestWan27ImageGenerationUsesMultimodalEndpointAfterStoredSyncListUpgrade(t *testing.T) { + settings := model_setting.GetQwenSettings() + previous := settings.SyncImageModels + settings.SyncImageModels = []string{"z-image", "qwen-image", "wan2.6"} + t.Cleanup(func() { settings.SyncImageModels = previous }) + + for _, model := range []string{"wan2.7-image", "wan2.7-image-pro"} { + t.Run(model, func(t *testing.T) { + c, _ := gin.CreateTestContext(httptest.NewRecorder()) + c.Request = httptest.NewRequest(http.MethodPost, "/v1/images/generations", nil) + info := &relaycommon.RelayInfo{ + RelayMode: constant.RelayModeImagesGenerations, + ChannelMeta: &relaycommon.ChannelMeta{ + ChannelBaseUrl: "https://dashscope.aliyuncs.com", + UpstreamModelName: model, + }, + } + adaptor := &Adaptor{} + url, err := adaptor.GetRequestURL(info) + require.NoError(t, err) + assert.Equal(t, "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation", url) + + header := http.Header{} + require.NoError(t, adaptor.SetupRequestHeader(c, &header, info)) + assert.Empty(t, header.Get("X-DashScope-Async")) + + converted, err := adaptor.ConvertImageRequest(c, info, dto.ImageRequest{Model: model, Prompt: "portrait"}) + require.NoError(t, err) + assert.True(t, adaptor.IsSyncImageModel) + request, ok := converted.(*AliImageRequest) + require.True(t, ok) + input, ok := request.Input.(AliImageInput) + require.True(t, ok) + require.Len(t, input.Messages, 1) + assert.Equal(t, "user", input.Messages[0].Role) + assert.Equal(t, []AliMediaContent{{Text: "portrait"}}, input.Messages[0].Content) + }) + } +} + func TestAliImageHandlerHonorsRequestResponseFormat(t *testing.T) { imageBytes := []byte("ali-image") var downloads atomic.Int32