mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-09-03 06:35:00 +08:00
perf(util): introduce GetGJSONBytesNoCopy for efficient JSON parsing without data duplication
- Added `GetGJSONBytesNoCopy` in `internal/util` for safe, no-copy JSON parse operations leveraging `unsafe`. - Replaced `gjson.GetBytes` with the new helper in key payload processing paths (`kimi_executor`, `vertex_payload_helpers`, etc.) to improve performance. - Added unit tests for behavior validation, including edge cases with empty input.
This commit is contained in:
@@ -832,7 +832,7 @@ func setPayloadValueIfDifferent(payload []byte, path string, value any) []byte {
|
||||
if expected.Raw == "" {
|
||||
return payload
|
||||
}
|
||||
if current.Raw == expected.Raw {
|
||||
if len(current.Indexes) == 0 && current.Raw == expected.Raw {
|
||||
return payload
|
||||
}
|
||||
updated, errSet := sjson.SetRawBytes(payload, path, []byte(expected.Raw))
|
||||
|
||||
@@ -36,7 +36,7 @@ func SetBoolIfDifferent(payload []byte, path string, value bool) []byte {
|
||||
// SetRawIfDifferent updates path only when the existing raw JSON is identical.
|
||||
func SetRawIfDifferent(payload []byte, path string, value []byte) []byte {
|
||||
current := gjson.GetBytes(payload, path)
|
||||
if current.Exists() && current.Raw == string(value) {
|
||||
if current.Exists() && len(current.Indexes) == 0 && current.Raw == string(value) {
|
||||
return payload
|
||||
}
|
||||
updated, errSet := sjson.SetRawBytes(payload, path, value)
|
||||
|
||||
@@ -92,6 +92,38 @@ func TestApplyPayloadConfigReusesCanonicalOverrides(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyPayloadConfigProjectionOverrideWritesEveryMatch(t *testing.T) {
|
||||
cfg := &config.Config{Payload: config.PayloadConfig{
|
||||
Override: []config.PayloadRule{{
|
||||
Models: []config.PayloadModelRule{{Name: "gpt-test", Protocol: "openai"}},
|
||||
Params: map[string]any{"items.#.value": []any{1, 2}},
|
||||
}},
|
||||
}}
|
||||
input := []byte(`{"items":[{"value":1},{"value":2}]}`)
|
||||
output := ApplyPayloadConfigWithRoot(cfg, "gpt-test", "openai", "", input, nil, "", "")
|
||||
for _, path := range []string{"items.0.value", "items.1.value"} {
|
||||
if got := gjson.GetBytes(output, path).Raw; got != `[1,2]` {
|
||||
t.Fatalf("%s = %s, want [1,2]", path, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyPayloadConfigProjectionOverrideRawWritesEveryMatch(t *testing.T) {
|
||||
cfg := &config.Config{Payload: config.PayloadConfig{
|
||||
OverrideRaw: []config.PayloadRule{{
|
||||
Models: []config.PayloadModelRule{{Name: "gpt-test", Protocol: "openai"}},
|
||||
Params: map[string]any{"items.#.value": `[1,2]`},
|
||||
}},
|
||||
}}
|
||||
input := []byte(`{"items":[{"value":1},{"value":2}]}`)
|
||||
output := ApplyPayloadConfigWithRoot(cfg, "gpt-test", "openai", "", input, nil, "", "")
|
||||
for _, path := range []string{"items.0.value", "items.1.value"} {
|
||||
if got := gjson.GetBytes(output, path).Raw; got != `[1,2]` {
|
||||
t.Fatalf("%s = %s, want [1,2]", path, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyPayloadConfigNormalizesByteSliceOverride(t *testing.T) {
|
||||
cfg := &config.Config{Payload: config.PayloadConfig{
|
||||
Override: []config.PayloadRule{{
|
||||
|
||||
@@ -3,6 +3,7 @@ package helps
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
@@ -14,7 +15,7 @@ func StripVertexOpenAIResponsesToolCallIDs(payload []byte, sourceFormat string)
|
||||
return payload
|
||||
}
|
||||
|
||||
contents := gjson.GetBytes(payload, "contents")
|
||||
contents := util.GetGJSONBytesNoCopy(payload, "contents")
|
||||
if !contents.IsArray() || !vertexContentsHaveToolCallIDs(contents) {
|
||||
return payload
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ func normalizeKimiToolMessageLinks(body []byte) ([]byte, error) {
|
||||
return body, nil
|
||||
}
|
||||
|
||||
messages := gjson.GetBytes(body, "messages")
|
||||
messages := util.GetGJSONBytesNoCopy(body, "messages")
|
||||
if !messages.Exists() || !messages.IsArray() {
|
||||
return body, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package signature
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
@@ -32,7 +33,7 @@ func SanitizeGeminiRequestThoughtSignatures(payload []byte, contentsPath string)
|
||||
contentsPath = "contents"
|
||||
}
|
||||
|
||||
contents := gjson.GetBytes(payload, contentsPath)
|
||||
contents := util.GetGJSONBytesNoCopy(payload, contentsPath)
|
||||
if !contents.IsArray() || !geminiContentsThoughtSignaturesNeedSanitize(contents) {
|
||||
return payload
|
||||
}
|
||||
|
||||
16
internal/util/gjson.go
Normal file
16
internal/util/gjson.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// GetGJSONBytesNoCopy returns a GJSON result that may reference data directly.
|
||||
// Callers must not retain the result or mutate data while using it.
|
||||
func GetGJSONBytesNoCopy(data []byte, path string) gjson.Result {
|
||||
if len(data) == 0 {
|
||||
return gjson.Result{}
|
||||
}
|
||||
return gjson.Get(unsafe.String(unsafe.SliceData(data), len(data)), path)
|
||||
}
|
||||
17
internal/util/gjson_test.go
Normal file
17
internal/util/gjson_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetGJSONBytesNoCopy(t *testing.T) {
|
||||
input := []byte(`{"request":{"contents":[{"role":"user"}]}}`)
|
||||
contents := GetGJSONBytesNoCopy(input, "request.contents")
|
||||
if !contents.IsArray() || contents.Get("0.role").String() != "user" {
|
||||
t.Fatalf("request.contents = %s, want user content array", contents.Raw)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGJSONBytesNoCopyEmptyInput(t *testing.T) {
|
||||
if result := GetGJSONBytesNoCopy(nil, "contents"); result.Exists() {
|
||||
t.Fatalf("empty input result = %s, want missing", result.Raw)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user