feat(openai): 完善 Chat 与 Responses 参数转发
原生 Chat/Responses 改为透明转发,保留标准工具结构并保护调用方显式参数。补齐 Responses 到 Chat 的兼容转换、协议路由边界、完整响应和流式事件,并同步更新 Swagger、回归测试与真实验收脚本。 验证: - cd apps/api && env -u AI_GATEWAY_TEST_DATABASE_URL go test ./... -count=1 - pnpm openapi - pnpm lint - pnpm test - pnpm build - gofmt -l 无输出 - git diff --check 通过 风险: - Chat 回退无法等价表达的 Responses 原生能力现在会返回 unsupported_response_parameter - 真实供应商 E2E 因本地没有已启用的平台模型候选而未完成
This commit is contained in:
@@ -1012,13 +1012,11 @@ func TestOpenAIClientChatRequestNormalizesToolContext(t *testing.T) {
|
||||
}
|
||||
assistant, _ := messages[0].(map[string]any)
|
||||
if _, ok := assistant["functionCall"]; ok {
|
||||
t.Fatalf("functionCall should be converted away: %+v", assistant)
|
||||
t.Fatalf("functionCall alias should be converted away: %+v", assistant)
|
||||
}
|
||||
toolCalls, _ := assistant["tool_calls"].([]any)
|
||||
toolCall, _ := toolCalls[0].(map[string]any)
|
||||
function, _ := toolCall["function"].(map[string]any)
|
||||
function, _ := assistant["function_call"].(map[string]any)
|
||||
if function["name"] != "lookup" || function["arguments"] != `{"q":"weather"}` {
|
||||
t.Fatalf("unexpected normalized tool call: %+v", assistant)
|
||||
t.Fatalf("unexpected normalized legacy function call: %+v", assistant)
|
||||
}
|
||||
toolMessage, _ := messages[1].(map[string]any)
|
||||
if toolMessage["tool_call_id"] != "call_0" || toolMessage["toolCallId"] != nil {
|
||||
@@ -1116,14 +1114,18 @@ func TestOpenAIClientChatResponseNormalizesToolCallFormats(t *testing.T) {
|
||||
if message["content"] != "calling tools" {
|
||||
t.Fatalf("tool_use block should be removed from content: %+v", message)
|
||||
}
|
||||
for _, key := range []string{"toolCalls", "function_call"} {
|
||||
for _, key := range []string{"toolCalls"} {
|
||||
if _, ok := message[key]; ok {
|
||||
t.Fatalf("%s should be converted away: %+v", key, message)
|
||||
}
|
||||
}
|
||||
legacyFunction, _ := message["function_call"].(map[string]any)
|
||||
if legacyFunction["name"] != "legacy_lookup" || legacyFunction["arguments"] != "{\"city\":\"NYC\"}" {
|
||||
t.Fatalf("canonical legacy function_call should be preserved: %+v", message)
|
||||
}
|
||||
toolCalls, _ := message["tool_calls"].([]any)
|
||||
if len(toolCalls) != 3 {
|
||||
t.Fatalf("expected 3 normalized tool calls, got %+v", message)
|
||||
if len(toolCalls) != 2 {
|
||||
t.Fatalf("expected 2 normalized tool calls plus legacy function_call, got %+v", message)
|
||||
}
|
||||
assertToolCall := func(index int, id string, name string, arguments string) {
|
||||
t.Helper()
|
||||
@@ -1134,8 +1136,7 @@ func TestOpenAIClientChatResponseNormalizesToolCallFormats(t *testing.T) {
|
||||
}
|
||||
}
|
||||
assertToolCall(0, "call_camel", "camel_lookup", "{\"city\":\"SF\"}")
|
||||
assertToolCall(1, "call_1", "legacy_lookup", "{\"city\":\"NYC\"}")
|
||||
assertToolCall(2, "toolu_1", "anthropic_lookup", "{\"city\":\"Boston\"}")
|
||||
assertToolCall(1, "toolu_1", "anthropic_lookup", "{\"city\":\"Boston\"}")
|
||||
}
|
||||
|
||||
func TestOpenAIClientChatStreamContract(t *testing.T) {
|
||||
@@ -1393,19 +1394,21 @@ func TestOpenAIClientChatStreamNormalizesToolCallFormats(t *testing.T) {
|
||||
if len(captured) != 3 {
|
||||
t.Fatalf("unexpected captured events: %+v", captured)
|
||||
}
|
||||
for _, event := range captured {
|
||||
for index, event := range captured {
|
||||
choices, _ := event.Event["choices"].([]any)
|
||||
choice, _ := choices[0].(map[string]any)
|
||||
delta, _ := choice["delta"].(map[string]any)
|
||||
if _, ok := delta["function_call"]; ok {
|
||||
t.Fatalf("function_call should be converted away: %+v", event.Event)
|
||||
}
|
||||
if _, ok := delta["functionCall"]; ok {
|
||||
t.Fatalf("functionCall should be converted away: %+v", event.Event)
|
||||
}
|
||||
if _, ok := delta["toolCall"]; ok {
|
||||
t.Fatalf("toolCall should be converted away: %+v", event.Event)
|
||||
}
|
||||
if index < 2 {
|
||||
if _, ok := delta["function_call"]; !ok {
|
||||
t.Fatalf("canonical legacy function_call should be preserved: %+v", event.Event)
|
||||
}
|
||||
}
|
||||
}
|
||||
choices, _ := response.Result["choices"].([]any)
|
||||
choice, _ := choices[0].(map[string]any)
|
||||
|
||||
Reference in New Issue
Block a user