150 lines
8.5 KiB
Go
150 lines
8.5 KiB
Go
package plugins_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/pkg/jsplugin"
|
|
builtinplugins "github.com/QuantumNous/new-api/plugins"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAlibabaWan3(t *testing.T) {
|
|
source, err := builtinplugins.Source("alibaba")
|
|
require.NoError(t, err)
|
|
registry := jsplugin.NewRegistry()
|
|
plugin, err := registry.RegisterFactory(source, jsplugin.Options{Key: "alibaba"})
|
|
require.NoError(t, err)
|
|
|
|
roundTrip := func(t *testing.T, value any) map[string]any {
|
|
encoded, marshalErr := common.Marshal(value)
|
|
require.NoError(t, marshalErr)
|
|
var decoded map[string]any
|
|
require.NoError(t, common.Unmarshal(encoded, &decoded))
|
|
return decoded
|
|
}
|
|
submitCtx := func(model, upstream string, body map[string]any) map[string]any {
|
|
return map[string]any{"model": model, "upstreamModel": upstream, "baseUrl": "https://dashscope.aliyuncs.com", "apiKey": "k", "requestBody": body}
|
|
}
|
|
usageCtx := func(purpose string, body map[string]any) map[string]any {
|
|
return map[string]any{"model": "wan3.0-video", "upstreamModel": "wan3.0-video", "usagePurpose": purpose, "requestBody": body}
|
|
}
|
|
decodeResponses := func(model string, body map[string]any) (map[string]any, error) {
|
|
value, callErr := plugin.Engine.CallPath(t.Context(), "protocols", []string{"openai_responses", "decodeRequest"}, map[string]any{"model": model, "body": map[string]any{"kind": "json", "value": body}, "stream": false})
|
|
if callErr != nil {
|
|
return nil, callErr
|
|
}
|
|
return roundTrip(t, value), nil
|
|
}
|
|
|
|
t.Run("duration -1 becomes the auto_duration marker in every decoder", func(t *testing.T) {
|
|
resolved, callErr := decodeResponses("wan3.0-video", map[string]any{"model": "wan3.0-video", "input": "a cat", "duration": -1})
|
|
require.NoError(t, callErr)
|
|
requestBody := resolved["requestBody"].(map[string]any)
|
|
assert.Equal(t, true, requestBody["auto_duration"])
|
|
assert.NotContains(t, requestBody, "duration")
|
|
|
|
value, callErr := plugin.Engine.CallPath(t.Context(), "protocols", []string{"openai_video", "decodeRequest"}, map[string]any{"model": "wan3.0-video", "body": map[string]any{"kind": "json", "value": map[string]any{"model": "wan3.0-video", "prompt": "a cat", "seconds": -1}}})
|
|
require.NoError(t, callErr)
|
|
requestBody = roundTrip(t, value)["requestBody"].(map[string]any)
|
|
assert.Equal(t, true, requestBody["auto_duration"])
|
|
assert.NotContains(t, requestBody, "seconds")
|
|
assert.NotContains(t, requestBody, "duration")
|
|
|
|
value, callErr = plugin.Engine.CallPath(t.Context(), "native", []string{"createVideoTask"}, map[string]any{"body": map[string]any{"kind": "json", "value": map[string]any{
|
|
"model": "wan3.0-video", "input": map[string]any{"prompt": "a cat"}, "parameters": map[string]any{"duration": -1, "ratio": "16:9"},
|
|
}}})
|
|
require.NoError(t, callErr)
|
|
requestBody = roundTrip(t, value)["requestBody"].(map[string]any)
|
|
assert.Equal(t, true, requestBody["auto_duration"])
|
|
assert.NotContains(t, requestBody, "duration")
|
|
parameters := requestBody["metadata"].(map[string]any)["parameters"].(map[string]any)
|
|
assert.Equal(t, "16:9", parameters["ratio"])
|
|
assert.NotContains(t, parameters, "duration")
|
|
})
|
|
|
|
t.Run("auto_duration submits -1 upstream and bills 30 seconds up front", func(t *testing.T) {
|
|
body := map[string]any{"model": "wan3.0-video", "prompt": "a cat", "auto_duration": true}
|
|
value, callErr := plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("wan3.0-video", "wan3.0-video", body))
|
|
require.NoError(t, callErr)
|
|
parameters := roundTrip(t, value)["body"].(map[string]any)["parameters"].(map[string]any)
|
|
assert.Equal(t, float64(-1), parameters["duration"])
|
|
|
|
value, callErr = plugin.Engine.Call(t.Context(), "extractUsage", usageCtx("facts", body))
|
|
require.NoError(t, callErr)
|
|
assert.Equal(t, map[string]any{"seconds": float64(30), "resolution": "1080P"}, roundTrip(t, value))
|
|
|
|
value, callErr = plugin.Engine.Call(t.Context(), "extractUsage", usageCtx("billing_ratios", body))
|
|
require.NoError(t, callErr)
|
|
assert.Equal(t, float64(30), roundTrip(t, value)["seconds"])
|
|
|
|
_, callErr = plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("wan2.7-t2v", "wan2.7-t2v", map[string]any{"model": "wan2.7-t2v", "prompt": "a cat", "auto_duration": true}))
|
|
require.ErrorContains(t, callErr, "only supported by wan3.0")
|
|
})
|
|
|
|
// /v1/video/generations has no plugin decoder, so -1 reaches the host's
|
|
// request validation as a vendor parameter; duration-auto@1 lets it through.
|
|
t.Run("metadata parameters keep -1 on /v1/video/generations", func(t *testing.T) {
|
|
body, facts, _ := submitAlibabaRequest(t, plugin, "wan3.0-video", map[string]any{
|
|
"model": "wan3.0-video", "prompt": "a cat", "metadata": map[string]any{"parameters": map[string]any{"duration": -1}},
|
|
})
|
|
assert.Equal(t, float64(-1), body["parameters"].(map[string]any)["duration"])
|
|
assert.Equal(t, float64(30), facts["seconds"])
|
|
})
|
|
|
|
t.Run("channel-mapped alias resolves defaults from the upstream model", func(t *testing.T) {
|
|
direct, callErr := plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("wan3.0-video", "wan3.0-video", map[string]any{"model": "wan3.0-video", "prompt": "a cat"}))
|
|
require.NoError(t, callErr)
|
|
alias, callErr := plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("my-wan3", "wan3.0-video", map[string]any{"model": "my-wan3", "prompt": "a cat"}))
|
|
require.NoError(t, callErr)
|
|
assert.Equal(t, roundTrip(t, direct)["body"], roundTrip(t, alias)["body"])
|
|
assert.Equal(t, "1080P", roundTrip(t, alias)["body"].(map[string]any)["parameters"].(map[string]any)["resolution"])
|
|
})
|
|
|
|
t.Run("size maps to a resolution tier and unknown sizes are rejected", func(t *testing.T) {
|
|
value, callErr := plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("wan3.0-video", "wan3.0-video", map[string]any{"model": "wan3.0-video", "prompt": "a cat", "size": "1280*720"}))
|
|
require.NoError(t, callErr)
|
|
parameters := roundTrip(t, value)["body"].(map[string]any)["parameters"].(map[string]any)
|
|
assert.Equal(t, "720P", parameters["resolution"])
|
|
assert.NotContains(t, parameters, "size")
|
|
assert.Equal(t, "16:9", parameters["ratio"])
|
|
|
|
_, callErr = plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("wan3.0-video", "wan3.0-video", map[string]any{"model": "wan3.0-video", "prompt": "a cat", "size": "1000*1000"}))
|
|
require.ErrorContains(t, callErr, "invalid size")
|
|
_, callErr = plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("wan3.0-video", "wan3.0-video", map[string]any{"model": "wan3.0-video", "prompt": "a cat", "duration": 31}))
|
|
require.ErrorContains(t, callErr, "between 2 and 30")
|
|
})
|
|
|
|
t.Run("image-only input stays rejected for t2v models and accepted for wan3.0", func(t *testing.T) {
|
|
imageOnly := []any{map[string]any{"type": "input_image", "image_url": "https://cdn.example/first.png"}}
|
|
_, callErr := decodeResponses("wan2.7-t2v", map[string]any{"model": "wan2.7-t2v", "input": imageOnly})
|
|
require.ErrorContains(t, callErr, "input is required")
|
|
|
|
resolved, callErr := decodeResponses("wan3.0-video", map[string]any{"model": "wan3.0-video", "input": imageOnly})
|
|
require.NoError(t, callErr)
|
|
assert.Equal(t, "image_to_video", resolved["action"])
|
|
|
|
value, callErr := plugin.Engine.Call(t.Context(), "buildSubmitRequest", submitCtx("wan3.0-video", "wan3.0-video", map[string]any{"model": "wan3.0-video", "prompt": "", "images": []any{"https://cdn.example/first.png"}}))
|
|
require.NoError(t, callErr)
|
|
input := roundTrip(t, value)["body"].(map[string]any)["input"].(map[string]any)
|
|
assert.Equal(t, []any{map[string]any{"type": "first_frame", "url": "https://cdn.example/first.png"}}, input["media"])
|
|
assert.NotContains(t, input, "img_url")
|
|
})
|
|
|
|
t.Run("completion facts read the wan3.0 usage block", func(t *testing.T) {
|
|
value, callErr := plugin.Engine.Call(t.Context(), "extractUsageOnComplete", map[string]any{}, map[string]any{}, map[string]any{
|
|
"output": map[string]any{"task_status": "SUCCEEDED", "video_url": "https://upstream.example/v.mp4"},
|
|
"usage": map[string]any{"video_count": 1, "duration": 7.5, "output_video_duration": 7.5, "SR": 720, "ratio": "16:9"},
|
|
})
|
|
require.NoError(t, callErr)
|
|
assert.Equal(t, map[string]any{"seconds": 7.5, "resolution": "720P"}, roundTrip(t, value))
|
|
|
|
value, callErr = plugin.Engine.Call(t.Context(), "extractUsageOnComplete", map[string]any{}, map[string]any{}, map[string]any{
|
|
"output": map[string]any{"task_status": "SUCCEEDED", "duration": 5, "resolution": "1080p"},
|
|
})
|
|
require.NoError(t, callErr)
|
|
assert.Equal(t, map[string]any{"seconds": float64(5), "resolution": "1080P"}, roundTrip(t, value))
|
|
})
|
|
}
|