mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-10-27 10:04:02 +08:00
fix(codex): reconcile replayed web_search_call history with tool declarations
- Inject a web_search tool declaration for standard Codex requests when input replays web_search_call items without a web_search tool so upstream response protection remains available during compaction. - Strip replayed web_search_call items from Responses Lite requests where top-level hosted tool declarations are omitted. Closes: #6770
This commit is contained in:
@@ -404,6 +404,81 @@ func normalizeCodexInstructions(body []byte, nativeRequest ...bool) []byte {
|
||||
|
||||
var imageGenToolJSON = []byte(`{"type":"image_generation","output_format":"png"}`)
|
||||
var imageGenToolArrayJSON = []byte(`[{"type":"image_generation","output_format":"png"}]`)
|
||||
var webSearchToolJSON = []byte(`{"type":"web_search"}`)
|
||||
var webSearchToolArrayJSON = []byte(`[{"type":"web_search"}]`)
|
||||
|
||||
func isCodexWebSearchToolType(toolType string) bool {
|
||||
switch strings.TrimSpace(toolType) {
|
||||
case "web_search", "web_search_preview", "web_search_preview_2025_03_11":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ensureWebSearchToolForHistory reconciles replayed web_search_call input items
|
||||
// with the request's tool declarations so upstream Codex does not fail with
|
||||
// "response protection is unavailable". Standard Responses requests receive a
|
||||
// {"type":"web_search"} tool declaration when missing, while Responses Lite
|
||||
// requests (which omit top-level hosted tools) strip the replayed search items.
|
||||
func ensureWebSearchToolForHistory(body []byte, headers http.Header) []byte {
|
||||
if !bytes.Contains(body, []byte("web_search_call")) {
|
||||
return body
|
||||
}
|
||||
input := util.GetGJSONBytesNoCopy(body, "input")
|
||||
if !input.IsArray() {
|
||||
return body
|
||||
}
|
||||
hasWebSearchCall := false
|
||||
input.ForEach(func(_, item gjson.Result) bool {
|
||||
if strings.TrimSpace(item.Get("type").String()) == "web_search_call" {
|
||||
hasWebSearchCall = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if !hasWebSearchCall {
|
||||
return body
|
||||
}
|
||||
|
||||
tools := util.GetGJSONBytesNoCopy(body, "tools")
|
||||
if tools.IsArray() {
|
||||
for _, t := range tools.Array() {
|
||||
if isCodexWebSearchToolType(t.Get("type").String()) {
|
||||
return body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsCodexResponsesLiteRequest(body, headers) {
|
||||
items := input.Array()
|
||||
rebuilt := make([]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
if strings.TrimSpace(item.Get("type").String()) == "web_search_call" {
|
||||
continue
|
||||
}
|
||||
rebuilt = append(rebuilt, item.Raw)
|
||||
}
|
||||
updated, errSet := sjson.SetRawBytes(body, "input", []byte("["+strings.Join(rebuilt, ",")+"]"))
|
||||
if errSet != nil {
|
||||
return body
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
if !tools.Exists() || !tools.IsArray() {
|
||||
updated, errSet := sjson.SetRawBytes(body, "tools", webSearchToolArrayJSON)
|
||||
if errSet != nil {
|
||||
return body
|
||||
}
|
||||
return updated
|
||||
}
|
||||
updated, errSet := sjson.SetRawBytes(body, "tools.-1", webSearchToolJSON)
|
||||
if errSet != nil {
|
||||
return body
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func isCodexFreePlanAuth(auth *cliproxyauth.Auth) bool {
|
||||
if auth == nil || auth.Attributes == nil {
|
||||
|
||||
Reference in New Issue
Block a user