feat(chat): 完善 Chat Completions 兼容层

This commit is contained in:
2026-05-19 17:46:27 +08:00
parent ba419cd90a
commit 13186f8ed1
13 changed files with 1611 additions and 85 deletions
+177 -7
View File
@@ -70,15 +70,185 @@ func geminiBody(request Request) map[string]any {
return map[string]any{"contents": contents}
}
prompt := firstNonEmptyPrompt(request.Body, "")
if prompt == "" {
prompt = textFromMessages(request.Body)
if prompt != "" {
return map[string]any{
"contents": []any{map[string]any{
"role": "user",
"parts": []any{map[string]any{"text": prompt}},
}},
}
}
return map[string]any{
"contents": []any{map[string]any{
"role": "user",
"parts": []any{map[string]any{"text": prompt}},
}},
body := map[string]any{"contents": geminiContentsFromMessages(request.Body)}
if tools := geminiToolsFromOpenAITools(request.Body["tools"]); len(tools) > 0 {
body["tools"] = tools
}
contents, _ := body["contents"].([]any)
if len(contents) > 0 {
return body
}
return map[string]any{"contents": []any{map[string]any{
"role": "user",
"parts": []any{map[string]any{"text": textFromMessages(request.Body)}},
}}}
}
func geminiContentsFromMessages(body map[string]any) []any {
normalized := NormalizeChatCompletionRequestBody(body)
messages, _ := normalized["messages"].([]any)
contents := make([]any, 0, len(messages))
toolNames := map[string]string{}
for _, rawMessage := range messages {
message, _ := rawMessage.(map[string]any)
if len(message) == 0 {
continue
}
role := stringFromAny(message["role"])
if role == "tool" {
toolCallID := stringFromAny(message["tool_call_id"])
name := toolNames[toolCallID]
if name == "" {
name = toolCallID
}
if name == "" {
name = "tool"
}
contents = append(contents, map[string]any{
"role": "user",
"parts": []any{map[string]any{"functionResponse": map[string]any{
"name": name,
"response": geminiFunctionResponsePayload(message["content"]),
}}},
})
continue
}
parts := geminiTextParts(message["content"])
if role == "assistant" {
for _, rawToolCall := range toolCallsSlice(message["tool_calls"]) {
toolCall, _ := rawToolCall.(map[string]any)
function, _ := toolCall["function"].(map[string]any)
name := stringFromAny(function["name"])
if name == "" {
continue
}
if id := stringFromAny(toolCall["id"]); id != "" {
toolNames[id] = name
}
parts = append(parts, map[string]any{"functionCall": map[string]any{
"name": name,
"args": geminiFunctionArgs(function["arguments"]),
}})
}
}
if len(parts) == 0 {
continue
}
contents = append(contents, map[string]any{
"role": geminiRole(role),
"parts": parts,
})
}
return contents
}
func geminiRole(role string) string {
if role == "assistant" {
return "model"
}
return "user"
}
func geminiTextParts(content any) []any {
parts := make([]any, 0)
switch typed := content.(type) {
case string:
if strings.TrimSpace(typed) != "" {
parts = append(parts, map[string]any{"text": typed})
}
case []any:
for _, rawPart := range typed {
part, _ := rawPart.(map[string]any)
if text := stringFromAny(firstPresent(part["text"], part["content"])); strings.TrimSpace(text) != "" {
parts = append(parts, map[string]any{"text": text})
}
}
}
return parts
}
func toolCallsSlice(value any) []any {
switch typed := value.(type) {
case []any:
return typed
case map[string]any:
return []any{typed}
default:
return nil
}
}
func geminiFunctionArgs(value any) map[string]any {
if value == nil {
return map[string]any{}
}
if args, ok := value.(map[string]any); ok {
return args
}
if text, ok := value.(string); ok {
if strings.TrimSpace(text) == "" {
return map[string]any{}
}
var args map[string]any
if err := json.Unmarshal([]byte(text), &args); err == nil {
return args
}
return map[string]any{"arguments": text}
}
return map[string]any{"arguments": value}
}
func geminiFunctionResponsePayload(value any) map[string]any {
if payload, ok := value.(map[string]any); ok {
return payload
}
if text, ok := value.(string); ok {
var payload map[string]any
if err := json.Unmarshal([]byte(text), &payload); err == nil {
return payload
}
return map[string]any{"content": text}
}
if value == nil {
return map[string]any{}
}
return map[string]any{"content": value}
}
func geminiToolsFromOpenAITools(value any) []any {
tools, ok := value.([]any)
if !ok || len(tools) == 0 {
return nil
}
declarations := make([]any, 0, len(tools))
for _, rawTool := range tools {
tool, _ := rawTool.(map[string]any)
function, _ := tool["function"].(map[string]any)
name := stringFromAny(function["name"])
if name == "" {
continue
}
declaration := map[string]any{"name": name}
if description := stringFromAny(function["description"]); description != "" {
declaration["description"] = description
}
if parameters, ok := function["parameters"]; ok {
declaration["parameters"] = parameters
}
declarations = append(declarations, declaration)
}
if len(declarations) == 0 {
return nil
}
return []any{map[string]any{"functionDeclarations": declarations}}
}
func geminiResult(request Request, raw map[string]any) map[string]any {