mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-19 04:40:52 +08:00
Fixed: #137
refactor: simplify ConvertCodexResponseToClaudeNonStream by removing bufio.Scanner usage and restructuring response parsing logic
This commit is contained in:
@@ -7,7 +7,6 @@
|
|||||||
package claude
|
package claude
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -180,56 +179,58 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - string: A Claude Code-compatible JSON response containing all message content and metadata
|
// - string: A Claude Code-compatible JSON response containing all message content and metadata
|
||||||
func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, originalRequestRawJSON, _ []byte, rawJSON []byte, _ *any) string {
|
func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, originalRequestRawJSON, _ []byte, rawJSON []byte, _ *any) string {
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(rawJSON))
|
|
||||||
buffer := make([]byte, 20_971_520)
|
|
||||||
scanner.Buffer(buffer, 20_971_520)
|
|
||||||
revNames := buildReverseMapFromClaudeOriginalShortToOriginal(originalRequestRawJSON)
|
revNames := buildReverseMapFromClaudeOriginalShortToOriginal(originalRequestRawJSON)
|
||||||
|
|
||||||
for scanner.Scan() {
|
rootResult := gjson.ParseBytes(rawJSON)
|
||||||
line := scanner.Bytes()
|
if rootResult.Get("type").String() != "response.completed" {
|
||||||
if !bytes.HasPrefix(line, dataTag) {
|
return ""
|
||||||
continue
|
}
|
||||||
}
|
|
||||||
payload := bytes.TrimSpace(line[len(dataTag):])
|
|
||||||
if len(payload) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rootResult := gjson.ParseBytes(payload)
|
responseData := rootResult.Get("response")
|
||||||
if rootResult.Get("type").String() != "response.completed" {
|
if !responseData.Exists() {
|
||||||
continue
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData := rootResult.Get("response")
|
response := map[string]interface{}{
|
||||||
if !responseData.Exists() {
|
"id": responseData.Get("id").String(),
|
||||||
continue
|
"type": "message",
|
||||||
}
|
"role": "assistant",
|
||||||
|
"model": responseData.Get("model").String(),
|
||||||
|
"content": []interface{}{},
|
||||||
|
"stop_reason": nil,
|
||||||
|
"stop_sequence": nil,
|
||||||
|
"usage": map[string]interface{}{
|
||||||
|
"input_tokens": responseData.Get("usage.input_tokens").Int(),
|
||||||
|
"output_tokens": responseData.Get("usage.output_tokens").Int(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
response := map[string]interface{}{
|
var contentBlocks []interface{}
|
||||||
"id": responseData.Get("id").String(),
|
hasToolCall := false
|
||||||
"type": "message",
|
|
||||||
"role": "assistant",
|
|
||||||
"model": responseData.Get("model").String(),
|
|
||||||
"content": []interface{}{},
|
|
||||||
"stop_reason": nil,
|
|
||||||
"stop_sequence": nil,
|
|
||||||
"usage": map[string]interface{}{
|
|
||||||
"input_tokens": responseData.Get("usage.input_tokens").Int(),
|
|
||||||
"output_tokens": responseData.Get("usage.output_tokens").Int(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var contentBlocks []interface{}
|
if output := responseData.Get("output"); output.Exists() && output.IsArray() {
|
||||||
hasToolCall := false
|
output.ForEach(func(_, item gjson.Result) bool {
|
||||||
|
switch item.Get("type").String() {
|
||||||
if output := responseData.Get("output"); output.Exists() && output.IsArray() {
|
case "reasoning":
|
||||||
output.ForEach(func(_, item gjson.Result) bool {
|
thinkingBuilder := strings.Builder{}
|
||||||
switch item.Get("type").String() {
|
if summary := item.Get("summary"); summary.Exists() {
|
||||||
case "reasoning":
|
if summary.IsArray() {
|
||||||
thinkingBuilder := strings.Builder{}
|
summary.ForEach(func(_, part gjson.Result) bool {
|
||||||
if summary := item.Get("summary"); summary.Exists() {
|
if txt := part.Get("text"); txt.Exists() {
|
||||||
if summary.IsArray() {
|
thinkingBuilder.WriteString(txt.String())
|
||||||
summary.ForEach(func(_, part gjson.Result) bool {
|
} else {
|
||||||
|
thinkingBuilder.WriteString(part.String())
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
thinkingBuilder.WriteString(summary.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if thinkingBuilder.Len() == 0 {
|
||||||
|
if content := item.Get("content"); content.Exists() {
|
||||||
|
if content.IsArray() {
|
||||||
|
content.ForEach(func(_, part gjson.Result) bool {
|
||||||
if txt := part.Get("text"); txt.Exists() {
|
if txt := part.Get("text"); txt.Exists() {
|
||||||
thinkingBuilder.WriteString(txt.String())
|
thinkingBuilder.WriteString(txt.String())
|
||||||
} else {
|
} else {
|
||||||
@@ -238,114 +239,96 @@ func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, original
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
thinkingBuilder.WriteString(summary.String())
|
thinkingBuilder.WriteString(content.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if thinkingBuilder.Len() == 0 {
|
|
||||||
if content := item.Get("content"); content.Exists() {
|
|
||||||
if content.IsArray() {
|
|
||||||
content.ForEach(func(_, part gjson.Result) bool {
|
|
||||||
if txt := part.Get("text"); txt.Exists() {
|
|
||||||
thinkingBuilder.WriteString(txt.String())
|
|
||||||
} else {
|
|
||||||
thinkingBuilder.WriteString(part.String())
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
thinkingBuilder.WriteString(content.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if thinkingBuilder.Len() > 0 {
|
|
||||||
contentBlocks = append(contentBlocks, map[string]interface{}{
|
|
||||||
"type": "thinking",
|
|
||||||
"thinking": thinkingBuilder.String(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
case "message":
|
|
||||||
if content := item.Get("content"); content.Exists() {
|
|
||||||
if content.IsArray() {
|
|
||||||
content.ForEach(func(_, part gjson.Result) bool {
|
|
||||||
if part.Get("type").String() == "output_text" {
|
|
||||||
text := part.Get("text").String()
|
|
||||||
if text != "" {
|
|
||||||
contentBlocks = append(contentBlocks, map[string]interface{}{
|
|
||||||
"type": "text",
|
|
||||||
"text": text,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
text := content.String()
|
|
||||||
if text != "" {
|
|
||||||
contentBlocks = append(contentBlocks, map[string]interface{}{
|
|
||||||
"type": "text",
|
|
||||||
"text": text,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "function_call":
|
|
||||||
hasToolCall = true
|
|
||||||
name := item.Get("name").String()
|
|
||||||
if original, ok := revNames[name]; ok {
|
|
||||||
name = original
|
|
||||||
}
|
|
||||||
|
|
||||||
toolBlock := map[string]interface{}{
|
|
||||||
"type": "tool_use",
|
|
||||||
"id": item.Get("call_id").String(),
|
|
||||||
"name": name,
|
|
||||||
"input": map[string]interface{}{},
|
|
||||||
}
|
|
||||||
|
|
||||||
if argsStr := item.Get("arguments").String(); argsStr != "" {
|
|
||||||
var args interface{}
|
|
||||||
if err := json.Unmarshal([]byte(argsStr), &args); err == nil {
|
|
||||||
toolBlock["input"] = args
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentBlocks = append(contentBlocks, toolBlock)
|
|
||||||
}
|
}
|
||||||
return true
|
if thinkingBuilder.Len() > 0 {
|
||||||
})
|
contentBlocks = append(contentBlocks, map[string]interface{}{
|
||||||
}
|
"type": "thinking",
|
||||||
|
"thinking": thinkingBuilder.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case "message":
|
||||||
|
if content := item.Get("content"); content.Exists() {
|
||||||
|
if content.IsArray() {
|
||||||
|
content.ForEach(func(_, part gjson.Result) bool {
|
||||||
|
if part.Get("type").String() == "output_text" {
|
||||||
|
text := part.Get("text").String()
|
||||||
|
if text != "" {
|
||||||
|
contentBlocks = append(contentBlocks, map[string]interface{}{
|
||||||
|
"type": "text",
|
||||||
|
"text": text,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
text := content.String()
|
||||||
|
if text != "" {
|
||||||
|
contentBlocks = append(contentBlocks, map[string]interface{}{
|
||||||
|
"type": "text",
|
||||||
|
"text": text,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "function_call":
|
||||||
|
hasToolCall = true
|
||||||
|
name := item.Get("name").String()
|
||||||
|
if original, ok := revNames[name]; ok {
|
||||||
|
name = original
|
||||||
|
}
|
||||||
|
|
||||||
if len(contentBlocks) > 0 {
|
toolBlock := map[string]interface{}{
|
||||||
response["content"] = contentBlocks
|
"type": "tool_use",
|
||||||
}
|
"id": item.Get("call_id").String(),
|
||||||
|
"name": name,
|
||||||
|
"input": map[string]interface{}{},
|
||||||
|
}
|
||||||
|
|
||||||
if stopReason := responseData.Get("stop_reason"); stopReason.Exists() && stopReason.String() != "" {
|
if argsStr := item.Get("arguments").String(); argsStr != "" {
|
||||||
response["stop_reason"] = stopReason.String()
|
var args interface{}
|
||||||
} else if hasToolCall {
|
if err := json.Unmarshal([]byte(argsStr), &args); err == nil {
|
||||||
response["stop_reason"] = "tool_use"
|
toolBlock["input"] = args
|
||||||
} else {
|
}
|
||||||
response["stop_reason"] = "end_turn"
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if stopSequence := responseData.Get("stop_sequence"); stopSequence.Exists() && stopSequence.String() != "" {
|
contentBlocks = append(contentBlocks, toolBlock)
|
||||||
response["stop_sequence"] = stopSequence.Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
if responseData.Get("usage.input_tokens").Exists() || responseData.Get("usage.output_tokens").Exists() {
|
|
||||||
response["usage"] = map[string]interface{}{
|
|
||||||
"input_tokens": responseData.Get("usage.input_tokens").Int(),
|
|
||||||
"output_tokens": responseData.Get("usage.output_tokens").Int(),
|
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
|
})
|
||||||
responseJSON, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return string(responseJSON)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
if len(contentBlocks) > 0 {
|
||||||
|
response["content"] = contentBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
if stopReason := responseData.Get("stop_reason"); stopReason.Exists() && stopReason.String() != "" {
|
||||||
|
response["stop_reason"] = stopReason.String()
|
||||||
|
} else if hasToolCall {
|
||||||
|
response["stop_reason"] = "tool_use"
|
||||||
|
} else {
|
||||||
|
response["stop_reason"] = "end_turn"
|
||||||
|
}
|
||||||
|
|
||||||
|
if stopSequence := responseData.Get("stop_sequence"); stopSequence.Exists() && stopSequence.String() != "" {
|
||||||
|
response["stop_sequence"] = stopSequence.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseData.Get("usage.input_tokens").Exists() || responseData.Get("usage.output_tokens").Exists() {
|
||||||
|
response["usage"] = map[string]interface{}{
|
||||||
|
"input_tokens": responseData.Get("usage.input_tokens").Int(),
|
||||||
|
"output_tokens": responseData.Get("usage.output_tokens").Int(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responseJSON, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(responseJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildReverseMapFromClaudeOriginalShortToOriginal builds a map[short]original from original Claude request tools.
|
// buildReverseMapFromClaudeOriginalShortToOriginal builds a map[short]original from original Claude request tools.
|
||||||
|
|||||||
Reference in New Issue
Block a user