Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
143
backend/internal/runtime/executor/openai_responses_signature.go
Normal file
143
backend/internal/runtime/executor/openai_responses_signature.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/runtime/executor/helps"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/signature"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
func sanitizeOpenAIResponsesReasoningEncryptedContent(ctx context.Context, provider string, body []byte) []byte {
|
||||
inputResult := util.GetGJSONBytesNoCopy(body, "input")
|
||||
if !inputResult.Exists() || !inputResult.IsArray() {
|
||||
return body
|
||||
}
|
||||
provider = strings.TrimSpace(provider)
|
||||
if provider == "" {
|
||||
provider = "openai responses upstream"
|
||||
}
|
||||
|
||||
// Codex backend rejects store=true and does not persist items when store=false.
|
||||
// A reasoning item that still carries an id without usable encrypted_content is
|
||||
// treated as a store lookup and returns:
|
||||
// Item with id '...' not found. Items are not persisted when `store` is set to false.
|
||||
// Strip those orphan ids unless the request explicitly opts into store=true.
|
||||
stripOrphanReasoningIDs := !gjson.GetBytes(body, "store").Bool()
|
||||
|
||||
items := inputResult.Array()
|
||||
|
||||
// rebuilt accumulates the edited "input" array as JSON array bytes. It
|
||||
// stays nil while no item needs editing so the common case (nothing to
|
||||
// sanitize) does no allocation or rebuilding. Edits are applied directly
|
||||
// to each item's own raw JSON rather than re-parsing the whole body,
|
||||
// keeping the cost proportional to the item being edited.
|
||||
var rebuilt []byte
|
||||
itemsWritten := 0
|
||||
keep := func(raw string) {
|
||||
if rebuilt == nil {
|
||||
return
|
||||
}
|
||||
if itemsWritten > 0 {
|
||||
rebuilt = append(rebuilt, ',')
|
||||
}
|
||||
rebuilt = append(rebuilt, raw...)
|
||||
itemsWritten++
|
||||
}
|
||||
startRebuild := func(index int) {
|
||||
if rebuilt != nil {
|
||||
return
|
||||
}
|
||||
// First item that needs editing: start the buffer and backfill
|
||||
// it with the raw JSON of every preceding item.
|
||||
rebuilt = make([]byte, 0, len(inputResult.Raw))
|
||||
rebuilt = append(rebuilt, '[')
|
||||
for i := range index {
|
||||
keep(items[i].Raw)
|
||||
}
|
||||
}
|
||||
|
||||
for index, item := range items {
|
||||
if strings.TrimSpace(item.Get("type").String()) != "reasoning" {
|
||||
keep(item.Raw)
|
||||
continue
|
||||
}
|
||||
|
||||
encryptedContent := item.Get("encrypted_content")
|
||||
itemID := strings.TrimSpace(item.Get("id").String())
|
||||
if itemID == "" {
|
||||
itemID = fmt.Sprintf("input[%d]", index)
|
||||
}
|
||||
|
||||
if !encryptedContent.Exists() {
|
||||
if stripOrphanReasoningIDs && item.Get("id").Exists() {
|
||||
nextItem, err := sjson.Delete(item.Raw, "id")
|
||||
if err != nil {
|
||||
helps.LogWithRequestID(ctx).Debugf("%s: failed to drop orphan reasoning id at input[%d]: %v", provider, index, err)
|
||||
keep(item.Raw)
|
||||
continue
|
||||
}
|
||||
startRebuild(index)
|
||||
keep(nextItem)
|
||||
helps.LogWithRequestID(ctx).Debugf("%s: dropped orphan reasoning id at input[%d] item_id=%q reason=missing encrypted_content with store disabled", provider, index, itemID)
|
||||
continue
|
||||
}
|
||||
keep(item.Raw)
|
||||
continue
|
||||
}
|
||||
|
||||
reason := ""
|
||||
switch encryptedContent.Type {
|
||||
case gjson.String:
|
||||
rawSignature := encryptedContent.String()
|
||||
if rawSignature != strings.TrimSpace(rawSignature) {
|
||||
reason = "encrypted_content has leading or trailing whitespace"
|
||||
} else if _, err := signature.InspectGPTReasoningSignature(rawSignature); err != nil {
|
||||
reason = err.Error()
|
||||
}
|
||||
case gjson.Null:
|
||||
reason = "encrypted_content is null"
|
||||
default:
|
||||
reason = fmt.Sprintf("encrypted_content must be a string, got %s", encryptedContent.Type.String())
|
||||
}
|
||||
if reason == "" {
|
||||
keep(item.Raw)
|
||||
continue
|
||||
}
|
||||
|
||||
nextItem, err := sjson.Delete(item.Raw, "encrypted_content")
|
||||
if err != nil {
|
||||
helps.LogWithRequestID(ctx).Debugf("%s: failed to drop invalid reasoning encrypted_content at input[%d]: %v", provider, index, err)
|
||||
keep(item.Raw)
|
||||
continue
|
||||
}
|
||||
if stripOrphanReasoningIDs && item.Get("id").Exists() {
|
||||
if nextID, errID := sjson.Delete(nextItem, "id"); errID != nil {
|
||||
helps.LogWithRequestID(ctx).Debugf("%s: failed to drop reasoning id after invalid encrypted_content at input[%d]: %v", provider, index, errID)
|
||||
} else {
|
||||
nextItem = nextID
|
||||
}
|
||||
}
|
||||
|
||||
startRebuild(index)
|
||||
keep(nextItem)
|
||||
|
||||
helps.LogWithRequestID(ctx).Debugf("%s: dropped invalid reasoning encrypted_content at input[%d] item_id=%q reason=%s", provider, index, itemID, reason)
|
||||
}
|
||||
|
||||
if rebuilt == nil {
|
||||
return body
|
||||
}
|
||||
rebuilt = append(rebuilt, ']')
|
||||
|
||||
updated, err := sjson.SetRawBytes(body, "input", rebuilt)
|
||||
if err != nil {
|
||||
helps.LogWithRequestID(ctx).Debugf("%s: failed to rebuild input array while sanitizing reasoning encrypted_content: %v", provider, err)
|
||||
return body
|
||||
}
|
||||
return updated
|
||||
}
|
||||
Loading…
Reference in a new issue