172 lines
7.8 KiB
Go
172 lines
7.8 KiB
Go
package runner
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
|
)
|
|
|
|
func volcTextCandidate(maxOutput any) store.RuntimeModelCandidate {
|
|
capability := map[string]any{}
|
|
if maxOutput != nil {
|
|
capability["max_output_tokens"] = maxOutput
|
|
}
|
|
return store.RuntimeModelCandidate{
|
|
Provider: "volces-openai", BaseURL: "https://ark.cn-beijing.volces.com/api/v3",
|
|
ModelType: "text_generate", ProviderModelName: "demo",
|
|
Capabilities: map[string]any{"text_generate": capability},
|
|
}
|
|
}
|
|
|
|
func TestDefaultVolcesOutputTokens(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
max int
|
|
want int
|
|
}{
|
|
{name: "128K", max: 131072, want: 43690},
|
|
{name: "32K", max: 32768, want: 10922},
|
|
{name: "24K below threshold", max: 24576, want: 24576},
|
|
{name: "threshold exact", max: 30720, want: 10240},
|
|
{name: "threshold minus one", max: 30719, want: 30719},
|
|
{name: "floor", max: 131071, want: 43690},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if got := defaultVolcesOutputTokens(test.max); got != test.want {
|
|
t.Fatalf("defaultVolcesOutputTokens(%d)=%d, want %d", test.max, got, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVolcesOutputProcessorInjectsCandidateSpecificDefaults(t *testing.T) {
|
|
chat := preprocessRequestWithLog("chat.completions", map[string]any{"messages": []any{}, "max_tokens": nil}, volcTextCandidate(131072))
|
|
if chat.Err != nil || chat.Body["max_tokens"] != 43690 {
|
|
t.Fatalf("unexpected Chat preprocessing: body=%+v err=%v", chat.Body, chat.Err)
|
|
}
|
|
if len(chat.Log.Changes) != 1 || chat.Log.Changes[0].CapabilityPath != "capabilities.text_generate.max_output_tokens" {
|
|
t.Fatalf("expected auditable capability source, got %+v", chat.Log.Changes)
|
|
}
|
|
|
|
responses := preprocessRequestWithLog("responses", map[string]any{"input": "hello", "max_output_tokens": nil}, volcTextCandidate(24576))
|
|
if responses.Err != nil || responses.Body["max_output_tokens"] != 24576 {
|
|
t.Fatalf("unexpected Responses preprocessing: body=%+v err=%v", responses.Body, responses.Err)
|
|
}
|
|
}
|
|
|
|
func TestVolcesOutputProcessorPreservesExplicitLimits(t *testing.T) {
|
|
for _, body := range []map[string]any{
|
|
{"messages": []any{}, "max_tokens": 1234},
|
|
{"messages": []any{}, "max_completion_tokens": 2345},
|
|
{"messages": []any{}, "max_tokens": 1234, "max_completion_tokens": 2345},
|
|
} {
|
|
result := preprocessRequestWithLog("chat.completions", body, volcTextCandidate(32768))
|
|
if result.Err != nil || len(result.Log.Changes) != 0 {
|
|
t.Fatalf("explicit Chat limit should remain unchanged: body=%+v err=%v changes=%+v", result.Body, result.Err, result.Log.Changes)
|
|
}
|
|
}
|
|
result := preprocessRequestWithLog("responses", map[string]any{"input": "hello", "max_output_tokens": 3456}, volcTextCandidate(32768))
|
|
if result.Err != nil || result.Body["max_output_tokens"] != 3456 || len(result.Log.Changes) != 0 {
|
|
t.Fatalf("explicit Responses limit should remain unchanged: %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestVolcesOutputCandidateFilterPreservesMissingCapabilityOrder(t *testing.T) {
|
|
missing := volcTextCandidate(nil)
|
|
missing.PlatformID = "missing"
|
|
valid := volcTextCandidate(32768)
|
|
valid.PlatformID = "valid"
|
|
filtered, summary, err := filterRuntimeCandidatesByOutputTokens("chat.completions", "demo", "text_generate", map[string]any{"messages": []any{}}, []store.RuntimeModelCandidate{missing, valid})
|
|
if err != nil || len(filtered) != 2 || filtered[0].PlatformID != "missing" || filtered[1].PlatformID != "valid" {
|
|
t.Fatalf("expected missing capability candidate order to be preserved: filtered=%+v summary=%+v err=%v", filtered, summary, err)
|
|
}
|
|
if summary["missingCapabilityCandidateCount"] != 1 || summary["passthroughMissingCapability"] != true {
|
|
t.Fatalf("expected missing capability audit summary, got %+v", summary)
|
|
}
|
|
missingResult := preprocessRequestWithLog("chat.completions", map[string]any{"messages": []any{}}, filtered[0])
|
|
if missingResult.Err != nil || len(missingResult.Log.Changes) != 0 {
|
|
t.Fatalf("missing capability candidate must pass through unchanged: %+v", missingResult)
|
|
}
|
|
if _, ok := missingResult.Body["max_tokens"]; ok {
|
|
t.Fatalf("missing capability candidate must not inject max_tokens: %+v", missingResult.Body)
|
|
}
|
|
result := preprocessRequestWithLog("chat.completions", map[string]any{"messages": []any{}}, filtered[1])
|
|
if result.Body["max_tokens"] != 10922 {
|
|
t.Fatalf("configured candidate must calculate its own default, got %+v", result.Body)
|
|
}
|
|
}
|
|
|
|
func TestVolcesOutputCandidateFilterPassesThroughMissingCapability(t *testing.T) {
|
|
filtered, summary, err := filterRuntimeCandidatesByOutputTokens("responses", "demo", "text_generate", map[string]any{"input": "hello"}, []store.RuntimeModelCandidate{volcTextCandidate(nil)})
|
|
if err != nil || len(filtered) != 1 {
|
|
t.Fatalf("expected missing capability candidate to remain available: filtered=%+v summary=%+v err=%v", filtered, summary, err)
|
|
}
|
|
result := preprocessRequestWithLog("responses", map[string]any{"input": "hello"}, filtered[0])
|
|
if result.Err != nil {
|
|
t.Fatalf("missing capability preprocessing must pass through: %v", result.Err)
|
|
}
|
|
if _, ok := result.Body["max_output_tokens"]; ok {
|
|
t.Fatalf("missing capability preprocessing must not inject max_output_tokens: %+v", result.Body)
|
|
}
|
|
}
|
|
|
|
func TestVolcesOutputCandidateFilterRejectsExceededKnownCapability(t *testing.T) {
|
|
_, _, err := filterRuntimeCandidatesByOutputTokens("chat.completions", "demo", "text_generate", map[string]any{"messages": []any{}, "max_tokens": 32769}, []store.RuntimeModelCandidate{volcTextCandidate(32768)})
|
|
if store.ModelCandidateErrorCode(err) != volcesOutputInvalidParameterCode {
|
|
t.Fatalf("expected invalid_parameter for explicit limit, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestVolcesOutputCandidateFilterAllowsUnknownCapabilityForExplicitLimit(t *testing.T) {
|
|
missing := volcTextCandidate(nil)
|
|
missing.PlatformID = "missing"
|
|
configured := volcTextCandidate(32768)
|
|
configured.PlatformID = "configured"
|
|
filtered, summary, err := filterRuntimeCandidatesByOutputTokens(
|
|
"chat.completions",
|
|
"demo",
|
|
"text_generate",
|
|
map[string]any{"messages": []any{}, "max_tokens": 65536},
|
|
[]store.RuntimeModelCandidate{missing, configured},
|
|
)
|
|
if err != nil || len(filtered) != 1 || filtered[0].PlatformID != "missing" {
|
|
t.Fatalf("expected unknown capability candidate to remain available: filtered=%+v summary=%+v err=%v", filtered, summary, err)
|
|
}
|
|
}
|
|
|
|
func TestVolcesOutputProcessorValidatesExplicitLimitWithoutCapability(t *testing.T) {
|
|
valid := preprocessRequestWithLog("chat.completions", map[string]any{"messages": []any{}, "max_tokens": 2048}, volcTextCandidate(nil))
|
|
if valid.Err != nil || valid.Body["max_tokens"] != 2048 {
|
|
t.Fatalf("valid explicit limit must pass through: %+v", valid)
|
|
}
|
|
invalid := preprocessRequestWithLog("chat.completions", map[string]any{"messages": []any{}, "max_tokens": 0}, volcTextCandidate(nil))
|
|
if invalid.Err == nil {
|
|
t.Fatalf("invalid explicit limit must still be rejected: %+v", invalid)
|
|
}
|
|
_, _, err := filterRuntimeCandidatesByOutputTokens(
|
|
"chat.completions",
|
|
"demo",
|
|
"text_generate",
|
|
map[string]any{"messages": []any{}, "max_tokens": 0},
|
|
[]store.RuntimeModelCandidate{volcTextCandidate(nil)},
|
|
)
|
|
if store.ModelCandidateErrorCode(err) != volcesOutputInvalidParameterCode {
|
|
t.Fatalf("candidate filter must reject an invalid explicit limit, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNonVolcesOutputProcessorDoesNotInject(t *testing.T) {
|
|
candidate := volcTextCandidate(131072)
|
|
candidate.Provider = "openai"
|
|
candidate.BaseURL = "https://api.openai.com/v1"
|
|
chat := preprocessRequestWithLog("chat.completions", map[string]any{"messages": []any{}}, candidate)
|
|
if _, ok := chat.Body["max_tokens"]; ok {
|
|
t.Fatalf("non-Volcengine Chat candidate must remain unchanged: %+v", chat.Body)
|
|
}
|
|
responses := preprocessRequestWithLog("responses", map[string]any{"input": "hello"}, candidate)
|
|
if _, ok := responses.Body["max_output_tokens"]; ok {
|
|
t.Fatalf("non-Volcengine Responses candidate must remain unchanged: %+v", responses.Body)
|
|
}
|
|
}
|