mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
优化 Responses 生图意图复用
This commit is contained in:
@@ -124,6 +124,13 @@ func openAICompatibleRequestPlatform(apiKey *service.APIKey) string {
|
||||
return service.PlatformOpenAI
|
||||
}
|
||||
|
||||
func openAIResponsesRequiredCapability(imageIntent bool, platform string) service.OpenAIEndpointCapability {
|
||||
if imageIntent && platform == service.PlatformOpenAI {
|
||||
return service.OpenAIEndpointCapabilityResponses
|
||||
}
|
||||
return service.OpenAIEndpointCapabilityChatCompletions
|
||||
}
|
||||
|
||||
func allowOpenAICompatibleMessagesDispatch(apiKey *service.APIKey) bool {
|
||||
if apiKey == nil || apiKey.Group == nil {
|
||||
return true
|
||||
@@ -363,12 +370,9 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
|
||||
// 生图意图的 /v1/responses 请求必须调度到确实支持 Responses API 的账号,否则
|
||||
// 会在 forward 阶段被静默降级为无法生图的 Chat Completions 直转(#4417)。
|
||||
// 仅对 OpenAI 平台生效:Grok 生图走独立的 forwardGrokResponses 路径,不应被过滤。
|
||||
// 使用 IsExplicitImageGenerationIntent 排除被动 image_gen namespace 声明,
|
||||
// 避免 Codex 的被动工具目录使 CC-only 账号被误过滤(#4476)。
|
||||
requiredCapability := service.OpenAIEndpointCapabilityChatCompletions
|
||||
if service.IsExplicitImageGenerationIntent("/v1/responses", reqModel, body) && requestPlatform == service.PlatformOpenAI {
|
||||
requiredCapability = service.OpenAIEndpointCapabilityResponses
|
||||
}
|
||||
// 复用前置权限与并发阶段在未修改 body 上确认的显式生图意图,避免大 tools 请求重复扫描。
|
||||
// 该判断已排除 Codex 被动 image_gen namespace,避免 CC-only 账号被误过滤(#4476)。
|
||||
requiredCapability := openAIResponsesRequiredCapability(imageIntent, requestPlatform)
|
||||
|
||||
for {
|
||||
// Streaming Forward intentionally detaches the upstream request so usage can
|
||||
|
||||
@@ -138,6 +138,39 @@ func TestOpenAIForwardSucceededForScheduling(t *testing.T) {
|
||||
}))
|
||||
}
|
||||
|
||||
func TestOpenAIResponsesRequiredCapability(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
imageIntent bool
|
||||
platform string
|
||||
want service.OpenAIEndpointCapability
|
||||
}{
|
||||
{
|
||||
name: "OpenAI explicit image intent requires Responses",
|
||||
imageIntent: true,
|
||||
platform: service.PlatformOpenAI,
|
||||
want: service.OpenAIEndpointCapabilityResponses,
|
||||
},
|
||||
{
|
||||
name: "Grok explicit image intent keeps chat capability",
|
||||
imageIntent: true,
|
||||
platform: service.PlatformGrok,
|
||||
want: service.OpenAIEndpointCapabilityChatCompletions,
|
||||
},
|
||||
{
|
||||
name: "non-image intent keeps chat capability",
|
||||
platform: service.PlatformOpenAI,
|
||||
want: service.OpenAIEndpointCapabilityChatCompletions,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.want, openAIResponsesRequiredCapability(tt.imageIntent, tt.platform))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveOpenAIMessagesMetadataSession_DoesNotDerivePromptCacheKey(t *testing.T) {
|
||||
body := []byte(`{"model":"claude-sonnet-4-5","metadata":{"user_id":"claude-code-session"},"messages":[{"role":"user","content":"hello"}]}`)
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||
)
|
||||
|
||||
var openAIResponsesImageIntentRoutingBenchmarkSink service.OpenAIEndpointCapability
|
||||
|
||||
func BenchmarkOpenAIResponsesImageIntentRouting_LargeToolsBody(b *testing.B) {
|
||||
body := buildLargeOpenAIResponsesToolsBody(32 << 20)
|
||||
if service.IsExplicitImageGenerationIntent("/v1/responses", "gpt-5.4", body) {
|
||||
b.Fatal("large tools body must not have explicit image intent")
|
||||
}
|
||||
platform := service.PlatformOpenAI
|
||||
|
||||
b.Run("reuse_once", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(body)))
|
||||
b.ReportAllocs()
|
||||
for range b.N {
|
||||
imageIntent := service.IsExplicitImageGenerationIntent("/v1/responses", "gpt-5.4", body)
|
||||
openAIResponsesImageIntentRoutingBenchmarkSink = openAIResponsesRequiredCapability(imageIntent, platform)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("rescan_twice", func(b *testing.B) {
|
||||
b.SetBytes(int64(len(body)))
|
||||
b.ReportAllocs()
|
||||
for range b.N {
|
||||
imageIntent := service.IsExplicitImageGenerationIntent("/v1/responses", "gpt-5.4", body)
|
||||
// 对照优化前路径:路由阶段会再次扫描同一份未修改的 body。
|
||||
requiredCapability := service.OpenAIEndpointCapabilityChatCompletions
|
||||
if service.IsExplicitImageGenerationIntent("/v1/responses", "gpt-5.4", body) && platform == service.PlatformOpenAI {
|
||||
requiredCapability = service.OpenAIEndpointCapabilityResponses
|
||||
}
|
||||
if imageIntent && requiredCapability != service.OpenAIEndpointCapabilityResponses {
|
||||
b.Fatal("explicit image intent must require Responses")
|
||||
}
|
||||
openAIResponsesImageIntentRoutingBenchmarkSink = requiredCapability
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func buildLargeOpenAIResponsesToolsBody(targetBytes int) []byte {
|
||||
var builder strings.Builder
|
||||
builder.Grow(targetBytes + 256)
|
||||
_, _ = builder.WriteString(`{"model":"gpt-5.4","tools":[{"type":"function","name":"search","description":"`)
|
||||
_, _ = builder.WriteString(strings.Repeat("x", targetBytes))
|
||||
_, _ = builder.WriteString(`"}],"tool_choice":"auto","input":"write code"}`)
|
||||
return []byte(builder.String())
|
||||
}
|
||||
Reference in New Issue
Block a user