Skip to content

Commit 71c0096

Browse files
committed
fix: dedupe tool outputs (function_call_output/tool_search_output) in Codex and OpenAI Compat executors; keep last occurrence to survive retry
1 parent 3a4124f commit 71c0096

2 files changed

Lines changed: 23 additions & 6 deletions

File tree

internal/runtime/executor/codex_executor.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,8 +1059,8 @@ func dedupeToolOutputs(body []byte) []byte {
10591059
}
10601060

10611061
arr := inputItems.Array()
1062-
seenCallIDs := make(map[string]struct{}, len(arr))
1063-
dupes := make(map[int]bool)
1062+
lastIdxByCallID := make(map[string]int, len(arr))
1063+
toolOutputIdx := make([]int, 0, len(arr))
10641064

10651065
for i, item := range arr {
10661066
typ := item.Get("type").String()
@@ -1071,11 +1071,22 @@ func dedupeToolOutputs(body []byte) []byte {
10711071
if callID == "" {
10721072
continue
10731073
}
1074-
if _, exists := seenCallIDs[callID]; exists {
1075-
dupes[i] = true
1076-
continue
1074+
toolOutputIdx = append(toolOutputIdx, i)
1075+
lastIdxByCallID[callID] = i // 最后一次出现覆盖前面的
1076+
}
1077+
1078+
// 构建保留集合:每个 call_id 只保留最后出现的那一项
1079+
keep := make(map[int]bool, len(lastIdxByCallID))
1080+
for _, idx := range lastIdxByCallID {
1081+
keep[idx] = true
1082+
}
1083+
1084+
// 统计需要移除的项
1085+
dupes := make(map[int]bool)
1086+
for _, idx := range toolOutputIdx {
1087+
if !keep[idx] {
1088+
dupes[idx] = true
10771089
}
1078-
seenCallIDs[callID] = struct{}{}
10791090
}
10801091

10811092
if len(dupes) == 0 {

internal/runtime/executor/openai_compat_executor.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,9 @@ func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *cliproxyauth.A
333333
return resp, fmt.Errorf("openai compat executor: prepare payload: %w", err)
334334
}
335335

336+
// 防御性去重:多 Key / 重试时翻译链可能重复写入 tool output。
337+
translated = dedupeToolOutputs(translated)
338+
336339
upstreamURL := strings.TrimSuffix(baseURL, "/") + endpoint
337340
parsedURL, err := url.Parse(upstreamURL)
338341
if err != nil {
@@ -443,6 +446,9 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy
443446
return nil, fmt.Errorf("openai compat executor: prepare payload: %w", err)
444447
}
445448

449+
// 防御性去重:多 Key / 重试时翻译链可能重复写入 tool output。
450+
translated = dedupeToolOutputs(translated)
451+
446452
upstreamURL := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
447453
parsedURL, err := url.Parse(upstreamURL)
448454
if err != nil {

0 commit comments

Comments
 (0)