mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 03:52:26 +08:00
fix(openai): keep item ID sanitization linear
This commit is contained in:
@@ -4,8 +4,10 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -60,3 +62,29 @@ func TestOpenAIGatewayService_APIKeyPassthrough_StripsInvalidInputItemIDs(t *tes
|
||||
require.Equal(t, "call_123", gjson.GetBytes(forwarded, "input.4.call_id").String())
|
||||
require.Equal(t, "item_unconstrained", gjson.GetBytes(forwarded, "input.5.id").String())
|
||||
}
|
||||
|
||||
func TestSanitizeOpenAIResponsesInputItemIDs_AllocationGrowthIsLinear(t *testing.T) {
|
||||
makeBody := func(itemCount int) []byte {
|
||||
items := make([]string, itemCount)
|
||||
for i := range items {
|
||||
items[i] = fmt.Sprintf(`{"type":"message","id":"item_%d","role":"user","content":[{"type":"input_text","text":"hello"}]}`, i)
|
||||
}
|
||||
return []byte(`{"model":"gpt-5.6-sol","input":[` + strings.Join(items, ",") + `]}`)
|
||||
}
|
||||
allocatedBytes := func(body []byte) uint64 {
|
||||
runtime.GC()
|
||||
var before, after runtime.MemStats
|
||||
runtime.ReadMemStats(&before)
|
||||
sanitized, changed, err := sanitizeOpenAIResponsesInputItemIDs(body)
|
||||
runtime.ReadMemStats(&after)
|
||||
require.NoError(t, err)
|
||||
require.True(t, changed)
|
||||
require.NotEmpty(t, sanitized)
|
||||
return after.TotalAlloc - before.TotalAlloc
|
||||
}
|
||||
|
||||
smallAllocated := allocatedBytes(makeBody(20))
|
||||
largeAllocated := allocatedBytes(makeBody(200))
|
||||
require.Less(t, largeAllocated, smallAllocated*30,
|
||||
"10x more input items must not cause quadratic whole-body allocation growth")
|
||||
}
|
||||
|
||||
@@ -29,33 +29,50 @@ func sanitizeOpenAIResponsesInputItemIDs(body []byte) ([]byte, bool, error) {
|
||||
return body, false, nil
|
||||
}
|
||||
|
||||
paths := make([]string, 0)
|
||||
items := make([][]byte, 0)
|
||||
changed := false
|
||||
var sanitizeErr error
|
||||
index := 0
|
||||
input.ForEach(func(_, item gjson.Result) bool {
|
||||
currentIndex := index
|
||||
index++
|
||||
if !item.IsObject() {
|
||||
return true
|
||||
}
|
||||
itemType := item.Get("type")
|
||||
id := item.Get("id")
|
||||
if itemType.Type == gjson.String && id.Type == gjson.String &&
|
||||
shouldStripOpenAIResponsesInputItemID(itemType.String(), id.String()) {
|
||||
paths = append(paths, fmt.Sprintf("input.%d.id", currentIndex))
|
||||
itemBody := []byte(item.Raw)
|
||||
if item.IsObject() {
|
||||
itemType := item.Get("type")
|
||||
id := item.Get("id")
|
||||
if itemType.Type == gjson.String && id.Type == gjson.String &&
|
||||
shouldStripOpenAIResponsesInputItemID(itemType.String(), id.String()) {
|
||||
itemBody, sanitizeErr = sjson.DeleteBytes(itemBody, "id")
|
||||
if sanitizeErr != nil {
|
||||
sanitizeErr = fmt.Errorf("delete input.%d.id: %w", currentIndex, sanitizeErr)
|
||||
return false
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
items = append(items, itemBody)
|
||||
return true
|
||||
})
|
||||
if len(paths) == 0 {
|
||||
if sanitizeErr != nil {
|
||||
return nil, false, sanitizeErr
|
||||
}
|
||||
if !changed {
|
||||
return body, false, nil
|
||||
}
|
||||
|
||||
sanitized := body
|
||||
for _, path := range paths {
|
||||
var err error
|
||||
sanitized, err = sjson.DeleteBytes(sanitized, path)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("delete %s: %w", path, err)
|
||||
rebuiltInput := make([]byte, 0, len(input.Raw))
|
||||
rebuiltInput = append(rebuiltInput, '[')
|
||||
for i, item := range items {
|
||||
if i > 0 {
|
||||
rebuiltInput = append(rebuiltInput, ',')
|
||||
}
|
||||
rebuiltInput = append(rebuiltInput, item...)
|
||||
}
|
||||
rebuiltInput = append(rebuiltInput, ']')
|
||||
|
||||
sanitized, err := sjson.SetRawBytes(body, "input", rebuiltInput)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("replace sanitized input: %w", err)
|
||||
}
|
||||
return sanitized, true, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user