diff --git a/pkg/llmproxy/auth/kiro/runtime_helpers.go b/pkg/llmproxy/auth/kiro/runtime_helpers.go new file mode 100644 index 0000000000..9da8835f25 --- /dev/null +++ b/pkg/llmproxy/auth/kiro/runtime_helpers.go @@ -0,0 +1,164 @@ +package kiro + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + "net/http" + "net/url" + "strings" + "sync" + + "github.com/google/uuid" +) + +// Constants used across the kiro auth package for AWS CodeWhisperer +// runtime endpoint construction. +const ( + DefaultKiroRegion = "us-east-1" + pathGetUsageLimits = "getUsageLimits" + pathListAvailableModels = "listAvailableModels" +) + +// ProfileARN is a parsed representation of an AWS CodeWhisperer profile ARN. +type ProfileARN struct { + Raw string + Partition string + Service string + Region string + AccountID string + ResourceType string + ResourceID string +} + +// ParseProfileARN parses a profile ARN of the form: +// +// arn::codewhisperer:::/ +// +// Returns nil if the ARN is malformed or not for CodeWhisperer. +func ParseProfileARN(arn string) *ProfileARN { + if arn == "" { + return nil + } + parts := strings.Split(arn, ":") + if len(parts) < 6 || parts[0] != "arn" || parts[1] == "" || parts[2] != "codewhisperer" { + return nil + } + region := strings.TrimSpace(parts[3]) + if region == "" || !strings.Contains(region, "-") { + return nil + } + resource := strings.Join(parts[5:], ":") + resourceType := resource + resourceID := "" + if idx := strings.Index(resource, "/"); idx > 0 { + resourceType = resource[:idx] + resourceID = resource[idx+1:] + } + return &ProfileARN{ + Raw: arn, + Partition: parts[1], + Service: parts[2], + Region: region, + AccountID: parts[4], + ResourceType: resourceType, + ResourceID: resourceID, + } +} + +// ExtractRegionFromProfileArn returns the region embedded in a CodeWhisperer +// profile ARN, or "" if the ARN is unparseable. +func ExtractRegionFromProfileArn(profileArn string) string { + parsed := ParseProfileARN(profileArn) + if parsed == nil { + return "" + } + return parsed.Region +} + +// GetKiroAPIEndpoint returns the Q runtime endpoint for the given AWS region, +// falling back to DefaultKiroRegion when region is empty. +func GetKiroAPIEndpoint(region string) string { + if region == "" { + region = DefaultKiroRegion + } + return "https://q." + region + ".amazonaws.com" +} + +// buildURL composes a runtime URL from endpoint, path, and optional query +// parameters. Empty parameter values are skipped. +func buildURL(endpoint, path string, queryParams map[string]string) string { + fullURL := fmt.Sprintf("%s/%s", endpoint, path) + if len(queryParams) == 0 { + return fullURL + } + values := url.Values{} + for key, value := range queryParams { + if strings.TrimSpace(value) == "" { + continue + } + values.Set(key, value) + } + if encoded := values.Encode(); encoded != "" { + fullURL += "?" + encoded + } + return fullURL +} + +// GenerateAccountKey derives a stable short hash from a seed string, +// suitable for use as a per-account cache key. +func GenerateAccountKey(seed string) string { + hash := sha256.Sum256([]byte(seed)) + return hex.EncodeToString(hash[:8]) +} + +// GetAccountKey produces a stable account key derived from clientID +// (preferred) or refreshToken; falls back to a UUID if both are empty. +func GetAccountKey(clientID, refreshToken string) string { + if clientID != "" { + return GenerateAccountKey(clientID) + } + if refreshToken != "" { + return GenerateAccountKey(refreshToken) + } + return GenerateAccountKey(uuid.New().String()) +} + +// Process-wide FingerprintManager singleton. Created on first use. +var ( + globalFingerprintManager *FingerprintManager + globalFingerprintManagerOnce sync.Once +) + +// GetGlobalFingerprintManager returns the process-wide FingerprintManager, +// initializing it on first call. +func GetGlobalFingerprintManager() *FingerprintManager { + globalFingerprintManagerOnce.Do(func() { + globalFingerprintManager = NewFingerprintManager() + }) + return globalFingerprintManager +} + +// setRuntimeHeaders applies the Authorization, user-agent, and AWS SDK +// invocation headers to req using the per-account fingerprint. +func setRuntimeHeaders(req *http.Request, accessToken string, accountKey string) { + fp := GetGlobalFingerprintManager().GetFingerprint(accountKey) + req.Header.Set("Authorization", "Bearer "+accessToken) + req.Header.Set("x-amz-user-agent", fp.BuildAmzUserAgent()) + req.Header.Set("User-Agent", fp.BuildUserAgent()) + req.Header.Set("amz-sdk-invocation-id", uuid.New().String()) + req.Header.Set("amz-sdk-request", "attempt=1; max=1") +} + +// FetchProfileArn is the exported wrapper around the internal sso_oidc.go +// fetchProfileArn helper. It delegates to the unexported method so callers +// outside this file (such as oauth_web.go) can resolve a profile ARN +// without exposing transport details. clientID and refreshToken are +// accepted for forward compatibility (per-account fingerprinting) but +// the underlying API call only requires the access token. +func (c *SSOOIDCClient) FetchProfileArn(ctx context.Context, accessToken, clientID, refreshToken string) string { + _ = clientID + _ = refreshToken + return c.fetchProfileArn(ctx, accessToken) +} diff --git a/pkg/llmproxy/config/config.go b/pkg/llmproxy/config/config.go index b9a7265bdf..cac08bfdc0 100644 --- a/pkg/llmproxy/config/config.go +++ b/pkg/llmproxy/config/config.go @@ -200,6 +200,9 @@ type RemoteManagement struct { SecretKey string `yaml:"secret-key"` // DisableControlPanel skips serving and syncing the bundled management UI when true. DisableControlPanel bool `yaml:"disable-control-panel"` + // DisableAutoUpdatePanel skips the periodic auto-update of the bundled + // management UI asset while still serving the control panel itself. + DisableAutoUpdatePanel bool `yaml:"disable-auto-update-panel"` // PanelGitHubRepository overrides the GitHub repository used to fetch the management panel asset. // Accepts either a repository URL (https://github.com/org/repo) or an API releases endpoint. PanelGitHubRepository string `yaml:"panel-github-repository"` diff --git a/pkg/llmproxy/store/gitstore.go b/pkg/llmproxy/store/gitstore.go index e183e7a5aa..88c3eacf8b 100644 --- a/pkg/llmproxy/store/gitstore.go +++ b/pkg/llmproxy/store/gitstore.go @@ -13,11 +13,12 @@ import ( "time" "github.com/go-git/go-git/v6" + gitclient "github.com/go-git/go-git/v6/plumbing/client" "github.com/go-git/go-git/v6/config" "github.com/go-git/go-git/v6/plumbing" "github.com/go-git/go-git/v6/plumbing/object" "github.com/go-git/go-git/v6/plumbing/transport" - "github.com/go-git/go-git/v6/plumbing/transport/http" + githttp "github.com/go-git/go-git/v6/plumbing/transport/http" cliproxyauth "github.com/kooshapari/CLIProxyAPI/v7/sdk/cliproxy/auth" ) @@ -120,7 +121,7 @@ func (s *GitTokenStore) EnsureRepository() error { s.dirLock.Unlock() return fmt.Errorf("git token store: create repo dir: %w", errMk) } - if _, errClone := git.PlainClone(repoDir, &git.CloneOptions{Auth: authMethod, URL: s.remote}); errClone != nil { + if _, errClone := git.PlainClone(repoDir, &git.CloneOptions{ClientOptions: authMethod, URL: s.remote}); errClone != nil { if errors.Is(errClone, transport.ErrEmptyRemoteRepository) { _ = os.RemoveAll(gitDir) repo, errInit := git.PlainInit(repoDir, false) @@ -176,7 +177,7 @@ func (s *GitTokenStore) EnsureRepository() error { s.dirLock.Unlock() return fmt.Errorf("git token store: worktree: %w", errWorktree) } - if errPull := worktree.Pull(&git.PullOptions{Auth: authMethod, RemoteName: "origin"}); errPull != nil { + if errPull := worktree.Pull(&git.PullOptions{ClientOptions: authMethod, RemoteName: "origin"}); errPull != nil { switch { case errors.Is(errPull, git.NoErrAlreadyUpToDate), errors.Is(errPull, git.ErrUnstagedChanges), @@ -538,7 +539,9 @@ func (s *GitTokenStore) repoDirSnapshot() string { return s.repoDir } -func (s *GitTokenStore) gitAuth() transport.AuthMethod { +// gitAuth returns transport client options carrying HTTP basic auth derived +// from the configured username/password, or nil when no credentials are set. +func (s *GitTokenStore) gitAuth() []gitclient.Option { if s.username == "" && s.password == "" { return nil } @@ -546,7 +549,9 @@ func (s *GitTokenStore) gitAuth() transport.AuthMethod { if user == "" { user = "git" } - return &http.BasicAuth{Username: user, Password: s.password} + return []gitclient.Option{ + gitclient.WithHTTPAuth(&githttp.BasicAuth{Username: user, Password: s.password}), + } } func (s *GitTokenStore) relativeToRepo(path string) (string, error) { @@ -637,7 +642,7 @@ func (s *GitTokenStore) commitAndPushLocked(message string, relPaths ...string) return errRewrite } s.maybeRunGC(repo) - if err = repo.Push(&git.PushOptions{Auth: s.gitAuth(), Force: true}); err != nil { + if err = repo.Push(&git.PushOptions{ClientOptions: s.gitAuth(), Force: true}); err != nil { if errors.Is(err, git.NoErrAlreadyUpToDate) { return nil } diff --git a/pkg/llmproxy/thinking/provider/codex/apply.go b/pkg/llmproxy/thinking/provider/codex/apply.go index bbfb02d20a..8d01432a8b 100644 --- a/pkg/llmproxy/thinking/provider/codex/apply.go +++ b/pkg/llmproxy/thinking/provider/codex/apply.go @@ -7,8 +7,6 @@ package codex import ( - "strings" - "github.com/kooshapari/CLIProxyAPI/v7/pkg/llmproxy/registry" "github.com/kooshapari/CLIProxyAPI/v7/pkg/llmproxy/thinking" "github.com/tidwall/gjson" diff --git a/pkg/llmproxy/translator/antigravity/claude/antigravity_claude_request.go b/pkg/llmproxy/translator/antigravity/claude/antigravity_claude_request.go index ab5b0ecd76..d7f3ca3741 100644 --- a/pkg/llmproxy/translator/antigravity/claude/antigravity_claude_request.go +++ b/pkg/llmproxy/translator/antigravity/claude/antigravity_claude_request.go @@ -56,7 +56,7 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _ continue } partJSON := `{}` - partJSON, _ = sjson.SetBytesM(partJSON, "text", systemPrompt) + partJSON, _ = sjson.SetBytes(partJSON, "text", systemPrompt) systemInstructionJSON, _ = sjson.SetRaw(systemInstructionJSON, "parts.-1", partJSON) hasSystemInstruction = true } @@ -65,7 +65,7 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _ systemPrompt := strings.TrimSpace(systemResult.String()) if systemPrompt != "" { systemInstructionJSON = `{"role":"user","parts":[{"text":""}]}` - systemInstructionJSON, _ = sjson.SetBytesM(systemInstructionJSON, "parts.0.text", systemPrompt) + systemInstructionJSON, _ = sjson.SetBytes(systemInstructionJSON, "parts.0.text", systemPrompt) hasSystemInstruction = true } } @@ -380,7 +380,7 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _ continue } partJSON := `{}` - partJSON, _ = sjson.SetBytesM(partJSON, "text", prompt) + partJSON, _ = sjson.SetBytes(partJSON, "text", prompt) clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON) contentsJSON, _ = sjson.SetRaw(contentsJSON, "-1", clientContentJSON) hasContents = true @@ -526,7 +526,7 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _ maxTokens = limit } } - out, _ = sjson.SetBytesM(out, "request.generationConfig.maxOutputTokens", maxTokens) + out, _ = sjson.SetBytes(out, "request.generationConfig.maxOutputTokens", maxTokens) } out = common.AttachDefaultSafetySettings(out, "request.safetySettings") diff --git a/pkg/llmproxy/translator/antigravity/openai/chat-completions/antigravity_openai_response.go b/pkg/llmproxy/translator/antigravity/openai/chat-completions/antigravity_openai_response.go index 846664f48e..9b36aecd30 100644 --- a/pkg/llmproxy/translator/antigravity/openai/chat-completions/antigravity_openai_response.go +++ b/pkg/llmproxy/translator/antigravity/openai/chat-completions/antigravity_openai_response.go @@ -103,7 +103,7 @@ func ConvertAntigravityResponseToOpenAI(_ context.Context, _ string, originalReq } promptTokenCount := usageResult.Get("promptTokenCount").Int() - cachedTokenCount thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int() - template, _ = sjson.SetBytesM(template, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) + template, _ = sjson.SetBytes(template, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) if thoughtsTokenCount > 0 { template, _ = sjson.SetBytes(template, "usage.completion_tokens_details.reasoning_tokens", thoughtsTokenCount) } diff --git a/pkg/llmproxy/translator/claude/gemini/claude_gemini_request.go b/pkg/llmproxy/translator/claude/gemini/claude_gemini_request.go index cd363ce364..95f1e62e88 100644 --- a/pkg/llmproxy/translator/claude/gemini/claude_gemini_request.go +++ b/pkg/llmproxy/translator/claude/gemini/claude_gemini_request.go @@ -14,6 +14,7 @@ import ( "strings" "github.com/google/uuid" + "github.com/kooshapari/CLIProxyAPI/v7/pkg/llmproxy/registry" "github.com/kooshapari/CLIProxyAPI/v7/pkg/llmproxy/thinking" "github.com/kooshapari/CLIProxyAPI/v7/pkg/llmproxy/util" "github.com/tidwall/gjson" diff --git a/pkg/llmproxy/translator/claude/openai/chat-completions/claude_openai_request.go b/pkg/llmproxy/translator/claude/openai/chat-completions/claude_openai_request.go index 121a888a36..5bdca3e7c6 100644 --- a/pkg/llmproxy/translator/claude/openai/chat-completions/claude_openai_request.go +++ b/pkg/llmproxy/translator/claude/openai/chat-completions/claude_openai_request.go @@ -60,7 +60,7 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream userID := fmt.Sprintf("user_%s_account_%s_session_%s", user, account, session) // Base Claude Code API template with default max_tokens value - out := fmt.Sprintf(`{"model":"","max_tokens":32000,"messages":[],"metadata":{"user_id":"%s"}}`, userID) + out := []byte(fmt.Sprintf(`{"model":"","max_tokens":32000,"messages":[],"metadata":{"user_id":"%s"}}`, userID)) root := gjson.ParseBytes(rawJSON) @@ -72,13 +72,13 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream if ok { switch budget { case 0: - out, _ = sjson.SetBytesM(out, "thinking.type", "disabled") + out, _ = sjson.SetBytes(out, "thinking.type", "disabled") case -1: - out, _ = sjson.SetBytesM(out, "thinking.type", "enabled") + out, _ = sjson.SetBytes(out, "thinking.type", "enabled") default: if budget > 0 { - out, _ = sjson.SetBytesM(out, "thinking.type", "enabled") - out, _ = sjson.SetBytesM(out, "thinking.budget_tokens", budget) + out, _ = sjson.SetBytes(out, "thinking.type", "enabled") + out, _ = sjson.SetBytes(out, "thinking.budget_tokens", budget) } } } @@ -99,19 +99,19 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream } // Model mapping to specify which Claude Code model to use - out, _ = sjson.SetBytesM(out, "model", modelName) + out, _ = sjson.SetBytes(out, "model", modelName) // Max tokens configuration with fallback to default value if maxTokens := root.Get("max_tokens"); maxTokens.Exists() { - out, _ = sjson.SetBytesM(out, "max_tokens", maxTokens.Int()) + out, _ = sjson.SetBytes(out, "max_tokens", maxTokens.Int()) } // Temperature setting for controlling response randomness if temp := root.Get("temperature"); temp.Exists() { - out, _ = sjson.SetBytesM(out, "temperature", temp.Float()) + out, _ = sjson.SetBytes(out, "temperature", temp.Float()) } else if topP := root.Get("top_p"); topP.Exists() { // Top P setting for nucleus sampling (filtered out if temperature is set) - out, _ = sjson.SetBytesM(out, "top_p", topP.Float()) + out, _ = sjson.SetBytes(out, "top_p", topP.Float()) } // Stop sequences configuration for custom termination conditions @@ -123,15 +123,15 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream return true }) if len(stopSequences) > 0 { - out, _ = sjson.SetBytesM(out, "stop_sequences", stopSequences) + out, _ = sjson.SetBytes(out, "stop_sequences", stopSequences) } } else { - out, _ = sjson.SetBytesM(out, "stop_sequences", []string{stop.String()}) + out, _ = sjson.SetBytes(out, "stop_sequences", []string{stop.String()}) } } // Stream configuration to enable or disable streaming responses - out, _ = sjson.SetBytesM(out, "stream", stream) + out, _ = sjson.SetBytes(out, "stream", stream) // Process messages and transform them to Claude Code format if messages := root.Get("messages"); messages.Exists() && messages.IsArray() { @@ -144,43 +144,43 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream switch role { case "system": if systemMessageIndex == -1 { - systemMsg := `{"role":"user","content":[]}` - out, _ = sjson.SetRaw(out, "messages.-1", systemMsg) + systemMsg := []byte(`{"role":"user","content":[]}`) + out, _ = sjson.SetRawBytes(out, "messages.-1", systemMsg) systemMessageIndex = messageIndex messageIndex++ } if contentResult.Exists() && contentResult.Type == gjson.String && contentResult.String() != "" { - textPart := `{"type":"text","text":""}` - textPart, _ = sjson.SetBytesM(textPart, "text", contentResult.String()) - out, _ = sjson.SetRaw(out, fmt.Sprintf("messages.%d.content.-1", systemMessageIndex), textPart) + textPart := []byte(`{"type":"text","text":""}`) + textPart, _ = sjson.SetBytes(textPart, "text", contentResult.String()) + out, _ = sjson.SetRawBytes(out, fmt.Sprintf("messages.%d.content.-1", systemMessageIndex), textPart) } else if contentResult.Exists() && contentResult.IsArray() { contentResult.ForEach(func(_, part gjson.Result) bool { if part.Get("type").String() == "text" { - textPart := `{"type":"text","text":""}` - textPart, _ = sjson.SetBytesM(textPart, "text", part.Get("text").String()) - out, _ = sjson.SetRaw(out, fmt.Sprintf("messages.%d.content.-1", systemMessageIndex), textPart) + textPart := []byte(`{"type":"text","text":""}`) + textPart, _ = sjson.SetBytes(textPart, "text", part.Get("text").String()) + out, _ = sjson.SetRawBytes(out, fmt.Sprintf("messages.%d.content.-1", systemMessageIndex), textPart) } return true }) } case "user", "assistant": - msg := `{"role":"","content":[]}` - msg, _ = sjson.SetBytesM(msg, "role", role) + msg := []byte(`{"role":"","content":[]}`) + msg, _ = sjson.SetBytes(msg, "role", role) // Handle content based on its type (string or array) if contentResult.Exists() && contentResult.Type == gjson.String && contentResult.String() != "" { - part := `{"type":"text","text":""}` - part, _ = sjson.SetBytesM(part, "text", contentResult.String()) - msg, _ = sjson.SetRaw(msg, "content.-1", part) + part := []byte(`{"type":"text","text":""}`) + part, _ = sjson.SetBytes(part, "text", contentResult.String()) + msg, _ = sjson.SetRawBytes(msg, "content.-1", part) } else if contentResult.Exists() && contentResult.IsArray() { contentResult.ForEach(func(_, part gjson.Result) bool { partType := part.Get("type").String() switch partType { case "text": - textPart := `{"type":"text","text":""}` - textPart, _ = sjson.SetBytesM(textPart, "text", part.Get("text").String()) - msg, _ = sjson.SetRaw(msg, "content.-1", textPart) + textPart := []byte(`{"type":"text","text":""}`) + textPart, _ = sjson.SetBytes(textPart, "text", part.Get("text").String()) + msg, _ = sjson.SetRawBytes(msg, "content.-1", textPart) case "image_url": // Convert OpenAI image format to Claude Code format @@ -193,10 +193,10 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream mediaType := strings.TrimPrefix(mediaTypePart, "data:") data := parts[1] - imagePart := `{"type":"image","source":{"type":"base64","media_type":"","data":""}}` - imagePart, _ = sjson.SetBytesM(imagePart, "source.media_type", mediaType) - imagePart, _ = sjson.SetBytesM(imagePart, "source.data", data) - msg, _ = sjson.SetRaw(msg, "content.-1", imagePart) + imagePart := []byte(`{"type":"image","source":{"type":"base64","media_type":"","data":""}}`) + imagePart, _ = sjson.SetBytes(imagePart, "source.media_type", mediaType) + imagePart, _ = sjson.SetBytes(imagePart, "source.data", data) + msg, _ = sjson.SetRawBytes(msg, "content.-1", imagePart) } } } @@ -214,9 +214,9 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream } function := toolCall.Get("function") - toolUse := `{"type":"tool_use","id":"","name":"","input":{}}` - toolUse, _ = sjson.SetBytesM(toolUse, "id", toolCallID) - toolUse, _ = sjson.SetBytesM(toolUse, "name", function.Get("name").String()) + toolUse := []byte(`{"type":"tool_use","id":"","name":"","input":{}}`) + toolUse, _ = sjson.SetBytes(toolUse, "id", toolCallID) + toolUse, _ = sjson.SetBytes(toolUse, "name", function.Get("name").String()) // Parse arguments for the tool call if args := function.Get("arguments"); args.Exists() { @@ -224,24 +224,24 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream if argsStr != "" && gjson.Valid(argsStr) { argsJSON := gjson.Parse(argsStr) if argsJSON.IsObject() { - toolUse, _ = sjson.SetRaw(toolUse, "input", argsJSON.Raw) + toolUse, _ = sjson.SetRawBytes(toolUse, "input", argsJSON.Raw) } else { - toolUse, _ = sjson.SetRaw(toolUse, "input", "{}") + toolUse, _ = sjson.SetRawBytes(toolUse, "input", "{}") } } else { - toolUse, _ = sjson.SetRaw(toolUse, "input", "{}") + toolUse, _ = sjson.SetRawBytes(toolUse, "input", "{}") } } else { - toolUse, _ = sjson.SetRaw(toolUse, "input", "{}") + toolUse, _ = sjson.SetRawBytes(toolUse, "input", "{}") } - msg, _ = sjson.SetRaw(msg, "content.-1", toolUse) + msg, _ = sjson.SetRawBytes(msg, "content.-1", toolUse) } return true }) } - out, _ = sjson.SetRaw(out, "messages.-1", msg) + out, _ = sjson.SetRawBytes(out, "messages.-1", msg) messageIndex++ case "tool": @@ -249,10 +249,10 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream toolCallID := message.Get("tool_call_id").String() content := message.Get("content").String() - msg := `{"role":"user","content":[{"type":"tool_result","tool_use_id":"","content":""}]}` - msg, _ = sjson.SetBytesM(msg, "content.0.tool_use_id", toolCallID) - msg, _ = sjson.SetBytesM(msg, "content.0.content", content) - out, _ = sjson.SetRaw(out, "messages.-1", msg) + msg := []byte(`{"role":"user","content":[{"type":"tool_result","tool_use_id":"","content":""}]}`) + msg, _ = sjson.SetBytes(msg, "content.0.tool_use_id", toolCallID) + msg, _ = sjson.SetBytes(msg, "content.0.content", content) + out, _ = sjson.SetRawBytes(out, "messages.-1", msg) messageIndex++ } return true @@ -265,18 +265,18 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream tools.ForEach(func(_, tool gjson.Result) bool { if tool.Get("type").String() == "function" { function := tool.Get("function") - anthropicTool := `{"name":"","description":""}` - anthropicTool, _ = sjson.SetBytesM(anthropicTool, "name", function.Get("name").String()) - anthropicTool, _ = sjson.SetBytesM(anthropicTool, "description", function.Get("description").String()) + anthropicTool := []byte(`{"name":"","description":""}`) + anthropicTool, _ = sjson.SetBytes(anthropicTool, "name", function.Get("name").String()) + anthropicTool, _ = sjson.SetBytes(anthropicTool, "description", function.Get("description").String()) // Convert parameters schema for the tool if parameters := function.Get("parameters"); parameters.Exists() { - anthropicTool, _ = sjson.SetRaw(anthropicTool, "input_schema", parameters.Raw) + anthropicTool, _ = sjson.SetRawBytes(anthropicTool, "input_schema", parameters.Raw) } else if parameters := function.Get("parametersJsonSchema"); parameters.Exists() { - anthropicTool, _ = sjson.SetRaw(anthropicTool, "input_schema", parameters.Raw) + anthropicTool, _ = sjson.SetRawBytes(anthropicTool, "input_schema", parameters.Raw) } - out, _ = sjson.SetRaw(out, "tools.-1", anthropicTool) + out, _ = sjson.SetRawBytes(out, "tools.-1", anthropicTool) hasAnthropicTools = true } return true @@ -296,17 +296,17 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream case "none": // Don't set tool_choice, Claude Code will not use tools case "auto": - out, _ = sjson.SetRaw(out, "tool_choice", `{"type":"auto"}`) + out, _ = sjson.SetRawBytes(out, "tool_choice", `{"type":"auto"}`) case "required": - out, _ = sjson.SetRaw(out, "tool_choice", `{"type":"any"}`) + out, _ = sjson.SetRawBytes(out, "tool_choice", `{"type":"any"}`) } case gjson.JSON: // Specific tool choice mapping if toolChoice.Get("type").String() == "function" { functionName := toolChoice.Get("function.name").String() toolChoiceJSON := `{"type":"tool","name":""}` - toolChoiceJSON, _ = sjson.SetBytesM(toolChoiceJSON, "name", functionName) - out, _ = sjson.SetRaw(out, "tool_choice", toolChoiceJSON) + toolChoiceJSON, _ = sjson.SetBytes(toolChoiceJSON, "name", functionName) + out, _ = sjson.SetRawBytes(out, "tool_choice", toolChoiceJSON) } default: } diff --git a/pkg/llmproxy/translator/claude/openai/responses/claude_openai-responses_request.go b/pkg/llmproxy/translator/claude/openai/responses/claude_openai-responses_request.go index 41f4913890..a7245e6612 100644 --- a/pkg/llmproxy/translator/claude/openai/responses/claude_openai-responses_request.go +++ b/pkg/llmproxy/translator/claude/openai/responses/claude_openai-responses_request.go @@ -48,7 +48,7 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte userID := fmt.Sprintf("user_%s_account_%s_session_%s", user, account, session) // Base Claude message payload - out := fmt.Sprintf(`{"model":"","max_tokens":32000,"messages":[],"metadata":{"user_id":"%s"}}`, userID) + out := []byte(fmt.Sprintf(`{"model":"","max_tokens":32000,"messages":[],"metadata":{"user_id":"%s"}}`, userID)) root := gjson.ParseBytes(rawJSON) @@ -60,13 +60,13 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte if ok { switch budget { case 0: - out, _ = sjson.SetBytesM(out, "thinking.type", "disabled") + out, _ = sjson.SetBytes(out, "thinking.type", "disabled") case -1: - out, _ = sjson.SetBytesM(out, "thinking.type", "enabled") + out, _ = sjson.SetBytes(out, "thinking.type", "enabled") default: if budget > 0 { - out, _ = sjson.SetBytesM(out, "thinking.type", "enabled") - out, _ = sjson.SetBytesM(out, "thinking.budget_tokens", budget) + out, _ = sjson.SetBytes(out, "thinking.type", "enabled") + out, _ = sjson.SetBytes(out, "thinking.budget_tokens", budget) } } } @@ -85,15 +85,15 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte } // Model - out, _ = sjson.SetBytesM(out, "model", modelName) + out, _ = sjson.SetBytes(out, "model", modelName) // Max tokens if mot := root.Get("max_output_tokens"); mot.Exists() { - out, _ = sjson.SetBytesM(out, "max_tokens", mot.Int()) + out, _ = sjson.SetBytes(out, "max_tokens", mot.Int()) } // Stream - out, _ = sjson.SetBytesM(out, "stream", stream) + out, _ = sjson.SetBytes(out, "stream", stream) // instructions -> as a leading message (use role user for Claude API compatibility) instructionsText := "" @@ -101,9 +101,9 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte if instr := root.Get("instructions"); instr.Exists() && instr.Type == gjson.String { instructionsText = instr.String() if instructionsText != "" { - sysMsg := `{"role":"user","content":""}` - sysMsg, _ = sjson.SetBytesM(sysMsg, "content", instructionsText) - out, _ = sjson.SetRaw(out, "messages.-1", sysMsg) + sysMsg := []byte(`{"role":"user","content":""}`) + sysMsg, _ = sjson.SetBytes(sysMsg, "content", instructionsText) + out, _ = sjson.SetRawBytes(out, "messages.-1", sysMsg) } } @@ -127,9 +127,9 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte } instructionsText = builder.String() if instructionsText != "" { - sysMsg := `{"role":"user","content":""}` - sysMsg, _ = sjson.SetBytesM(sysMsg, "content", instructionsText) - out, _ = sjson.SetRaw(out, "messages.-1", sysMsg) + sysMsg := []byte(`{"role":"user","content":""}`) + sysMsg, _ = sjson.SetBytes(sysMsg, "content", instructionsText) + out, _ = sjson.SetRawBytes(out, "messages.-1", sysMsg) extractedFromSystem = true } } @@ -141,9 +141,9 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte // input can be a raw string for compatibility with OpenAI Responses API. if instructionsText == "" { if input := root.Get("input"); input.Exists() && input.Type == gjson.String { - msg := `{"role":"user","content":""}` - msg, _ = sjson.SetBytesM(msg, "content", input.String()) - out, _ = sjson.SetRaw(out, "messages.-1", msg) + msg := []byte(`{"role":"user","content":""}`) + msg, _ = sjson.SetBytes(msg, "content", input.String()) + out, _ = sjson.SetRawBytes(out, "messages.-1", msg) } } @@ -174,8 +174,8 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte if t := part.Get("text"); t.Exists() { txt := t.String() textAggregate.WriteString(txt) - contentPart := `{"type":"text","text":""}` - contentPart, _ = sjson.SetBytesM(contentPart, "text", txt) + contentPart := []byte(`{"type":"text","text":""}`) + contentPart, _ = sjson.SetBytes(contentPart, "text", txt) partsJSON = append(partsJSON, contentPart) } if ptype == "input_text" { @@ -202,13 +202,13 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte data = mediaAndData[1] } if data != "" { - contentPart = `{"type":"image","source":{"type":"base64","media_type":"","data":""}}` - contentPart, _ = sjson.SetBytesM(contentPart, "source.media_type", mediaType) - contentPart, _ = sjson.SetBytesM(contentPart, "source.data", data) + contentPart = []byte(`{"type":"image","source":{"type":"base64","media_type":"","data":""}}`) + contentPart, _ = sjson.SetBytes(contentPart, "source.media_type", mediaType) + contentPart, _ = sjson.SetBytes(contentPart, "source.data", data) } } else { - contentPart = `{"type":"image","source":{"type":"url","url":""}}` - contentPart, _ = sjson.SetBytesM(contentPart, "source.url", url) + contentPart = []byte(`{"type":"image","source":{"type":"url","url":""}}`) + contentPart, _ = sjson.SetBytes(contentPart, "source.url", url) } if contentPart != "" { partsJSON = append(partsJSON, contentPart) @@ -251,26 +251,26 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte } if len(partsJSON) > 0 { - msg := `{"role":"","content":[]}` - msg, _ = sjson.SetBytesM(msg, "role", role) + msg := []byte(`{"role":"","content":[]}`) + msg, _ = sjson.SetBytes(msg, "role", role) // Preserve legacy single-text flattening, but keep structured arrays when // image/thinking content is present. if len(partsJSON) == 1 && !hasImage && !hasRedactedThinking { // Preserve legacy behavior for single text content msg, _ = sjson.Delete(msg, "content") textPart := gjson.Parse(partsJSON[0]) - msg, _ = sjson.SetBytesM(msg, "content", textPart.Get("text").String()) + msg, _ = sjson.SetBytes(msg, "content", textPart.Get("text").String()) } else { for _, partJSON := range partsJSON { - msg, _ = sjson.SetRaw(msg, "content.-1", partJSON) + msg, _ = sjson.SetRawBytes(msg, "content.-1", partJSON) } } - out, _ = sjson.SetRaw(out, "messages.-1", msg) + out, _ = sjson.SetRawBytes(out, "messages.-1", msg) } else if textAggregate.Len() > 0 || role == "system" { - msg := `{"role":"","content":""}` - msg, _ = sjson.SetBytesM(msg, "role", role) - msg, _ = sjson.SetBytesM(msg, "content", textAggregate.String()) - out, _ = sjson.SetRaw(out, "messages.-1", msg) + msg := []byte(`{"role":"","content":""}`) + msg, _ = sjson.SetBytes(msg, "role", role) + msg, _ = sjson.SetBytes(msg, "content", textAggregate.String()) + out, _ = sjson.SetRawBytes(out, "messages.-1", msg) } case "function_call": @@ -283,8 +283,8 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte argsStr := item.Get("arguments").String() toolUse := `{"type":"tool_use","id":"","name":"","input":{}}` - toolUse, _ = sjson.SetBytesM(toolUse, "id", callID) - toolUse, _ = sjson.SetBytesM(toolUse, "name", name) + toolUse, _ = sjson.SetBytes(toolUse, "id", callID) + toolUse, _ = sjson.SetBytes(toolUse, "name", name) if argsStr != "" && gjson.Valid(argsStr) { argsJSON := gjson.Parse(argsStr) if argsJSON.IsObject() { @@ -298,19 +298,19 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte pendingReasoning = "" } asst, _ = sjson.SetRaw(asst, "content.-1", toolUse) - out, _ = sjson.SetRaw(out, "messages.-1", asst) + out, _ = sjson.SetRawBytes(out, "messages.-1", asst) case "function_call_output": // Map to user tool_result callID := item.Get("call_id").String() outputStr := item.Get("output").String() toolResult := `{"type":"tool_result","tool_use_id":"","content":""}` - toolResult, _ = sjson.SetBytesM(toolResult, "tool_use_id", callID) - toolResult, _ = sjson.SetBytesM(toolResult, "content", outputStr) + toolResult, _ = sjson.SetBytes(toolResult, "tool_use_id", callID) + toolResult, _ = sjson.SetBytes(toolResult, "content", outputStr) usr := `{"role":"user","content":[]}` usr, _ = sjson.SetRaw(usr, "content.-1", toolResult) - out, _ = sjson.SetRaw(out, "messages.-1", usr) + out, _ = sjson.SetRawBytes(out, "messages.-1", usr) case "reasoning": // Preserve reasoning history so Claude thinking-enabled requests keep // thinking/redacted_thinking before tool_use blocks. @@ -328,7 +328,7 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte if pendingReasoning != "" { asst := `{"role":"assistant","content":[]}` asst, _ = sjson.SetRaw(asst, "content.-1", buildRedactedThinkingPart(pendingReasoning)) - out, _ = sjson.SetRaw(out, "messages.-1", asst) + out, _ = sjson.SetRawBytes(out, "messages.-1", asst) } // tools mapping: parameters -> input_schema @@ -337,10 +337,10 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte tools.ForEach(func(_, tool gjson.Result) bool { tJSON := `{"name":"","description":"","input_schema":{}}` if n := tool.Get("name"); n.Exists() { - tJSON, _ = sjson.SetBytesM(tJSON, "name", n.String()) + tJSON, _ = sjson.SetBytes(tJSON, "name", n.String()) } if d := tool.Get("description"); d.Exists() { - tJSON, _ = sjson.SetBytesM(tJSON, "description", d.String()) + tJSON, _ = sjson.SetBytes(tJSON, "description", d.String()) } if params := tool.Get("parameters"); params.Exists() { @@ -353,7 +353,7 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte return true }) if gjson.Parse(toolsJSON).IsArray() && len(gjson.Parse(toolsJSON).Array()) > 0 { - out, _ = sjson.SetRaw(out, "tools", toolsJSON) + out, _ = sjson.SetRawBytes(out, "tools", toolsJSON) } } @@ -363,18 +363,18 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte case gjson.String: switch toolChoice.String() { case "auto": - out, _ = sjson.SetRaw(out, "tool_choice", `{"type":"auto"}`) + out, _ = sjson.SetRawBytes(out, "tool_choice", `{"type":"auto"}`) case "none": // Leave unset; implies no tools case "required": - out, _ = sjson.SetRaw(out, "tool_choice", `{"type":"any"}`) + out, _ = sjson.SetRawBytes(out, "tool_choice", `{"type":"any"}`) } case gjson.JSON: if toolChoice.Get("type").String() == "function" { fn := toolChoice.Get("function.name").String() toolChoiceJSON := `{"name":"","type":"tool"}` - toolChoiceJSON, _ = sjson.SetBytesM(toolChoiceJSON, "name", fn) - out, _ = sjson.SetRaw(out, "tool_choice", toolChoiceJSON) + toolChoiceJSON, _ = sjson.SetBytes(toolChoiceJSON, "name", fn) + out, _ = sjson.SetRawBytes(out, "tool_choice", toolChoiceJSON) } default: @@ -447,7 +447,7 @@ func extractThinkingLikeText(part gjson.Result) string { } func buildRedactedThinkingPart(text string) string { - part := `{"type":"redacted_thinking","data":""}` - part, _ = sjson.SetBytesM(part, "data", text) + part := []byte(`{"type":"redacted_thinking","data":""}`) + part, _ = sjson.SetBytes(part, "data", text) return part } diff --git a/pkg/llmproxy/translator/codex/claude/codex_claude_request.go b/pkg/llmproxy/translator/codex/claude/codex_claude_request.go index d5ba82cee3..441f400539 100644 --- a/pkg/llmproxy/translator/codex/claude/codex_claude_request.go +++ b/pkg/llmproxy/translator/codex/claude/codex_claude_request.go @@ -261,7 +261,7 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool) } minimalTool := fmt.Sprintf(`{"type":"function","name":"%s","description":"%s","parameters":{"type":"object","properties":{}}}`, toolName, toolDesc) - template, _ = sjson.SetRaw(template, "tools.-1", minimalTool) + template, _ = sjson.SetRawBytes(template, "tools.-1", []byte(minimalTool)) continue } // Special handling: Codex sends "custom" type tools (e.g., apply_patch with Lark grammar) @@ -275,11 +275,11 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool) } minimalTool := fmt.Sprintf(`{"type":"function","name":"%s","description":"%s","parameters":{"type":"object","properties":{}}}`, toolName, toolDesc) - template, _ = sjson.SetRaw(template, "tools.-1", minimalTool) + template, _ = sjson.SetRawBytes(template, "tools.-1", []byte(minimalTool)) continue } - tool := toolResult.Raw - tool, _ = sjson.SetBytesM(tool, "type", "function") + tool := []byte(toolResult.Raw) + tool, _ = sjson.SetBytes(tool, "type", "function") // Apply shortened name if needed if v := toolResult.Get("name"); v.Exists() { name := v.String() diff --git a/pkg/llmproxy/translator/codex/claude/codex_claude_response.go b/pkg/llmproxy/translator/codex/claude/codex_claude_response.go index 330873c0a1..f589c3276a 100644 --- a/pkg/llmproxy/translator/codex/claude/codex_claude_response.go +++ b/pkg/llmproxy/translator/codex/claude/codex_claude_response.go @@ -61,72 +61,72 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa rootResult := gjson.ParseBytes(rawJSON) typeResult := rootResult.Get("type") typeStr := typeResult.String() - template := "" + var template []byte switch typeStr { case "response.created": - template = `{"type":"message_start","message":{"id":"","type":"message","role":"assistant","model":"claude-opus-4-1-20250805","stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0},"content":[],"stop_reason":null}}` - template, _ = sjson.SetBytesM(template, "message.model", rootResult.Get("response.model").String()) - template, _ = sjson.SetBytesM(template, "message.id", rootResult.Get("response.id").String()) + template = []byte(`{"type":"message_start","message":{"id":"","type":"message","role":"assistant","model":"claude-opus-4-1-20250805","stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0},"content":[],"stop_reason":null}}`) + template, _ = sjson.SetBytes(template, "message.model", rootResult.Get("response.model").String()) + template, _ = sjson.SetBytes(template, "message.id", rootResult.Get("response.id").String()) output = "event: message_start\n" output += fmt.Sprintf("data: %s\n\n", template) case "response.reasoning_summary_part.added": - template = `{"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template = []byte(`{"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) output = "event: content_block_start\n" output += fmt.Sprintf("data: %s\n\n", template) case "response.reasoning_summary_text.delta": - template = `{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":""}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) - template, _ = sjson.SetBytesM(template, "delta.thinking", rootResult.Get("delta").String()) + template = []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":""}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template, _ = sjson.SetBytes(template, "delta.thinking", rootResult.Get("delta").String()) output = "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) case "response.reasoning_summary_part.done": - template = `{"type":"content_block_stop","index":0}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template = []byte(`{"type":"content_block_stop","index":0}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex++ output = "event: content_block_stop\n" output += fmt.Sprintf("data: %s\n\n", template) case "response.content_part.added": - template = `{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template = []byte(`{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) output = "event: content_block_start\n" output += fmt.Sprintf("data: %s\n\n", template) case "response.output_text.delta": - template = `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":""}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) - template, _ = sjson.SetBytesM(template, "delta.text", rootResult.Get("delta").String()) + template = []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":""}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template, _ = sjson.SetBytes(template, "delta.text", rootResult.Get("delta").String()) output = "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) case "response.content_part.done": - template = `{"type":"content_block_stop","index":0}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template = []byte(`{"type":"content_block_stop","index":0}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex++ output = "event: content_block_stop\n" output += fmt.Sprintf("data: %s\n\n", template) case "response.completed": - template = `{"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}` + template = []byte(`{"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`) p := (*param).(*ConvertCodexResponseToClaudeParams).HasToolCall stopReason := rootResult.Get("response.stop_reason").String() if p { - template, _ = sjson.SetBytesM(template, "delta.stop_reason", "tool_use") + template, _ = sjson.SetBytes(template, "delta.stop_reason", "tool_use") } else if stopReason == "max_tokens" || stopReason == "stop" { - template, _ = sjson.SetBytesM(template, "delta.stop_reason", stopReason) + template, _ = sjson.SetBytes(template, "delta.stop_reason", stopReason) } else { - template, _ = sjson.SetBytesM(template, "delta.stop_reason", "end_turn") + template, _ = sjson.SetBytes(template, "delta.stop_reason", "end_turn") } inputTokens, outputTokens, cachedTokens := extractResponsesUsage(rootResult.Get("response.usage")) - template, _ = sjson.SetBytesM(template, "usage.input_tokens", inputTokens) - template, _ = sjson.SetBytesM(template, "usage.output_tokens", outputTokens) + template, _ = sjson.SetBytes(template, "usage.input_tokens", inputTokens) + template, _ = sjson.SetBytes(template, "usage.output_tokens", outputTokens) if cachedTokens > 0 { - template, _ = sjson.SetBytesM(template, "usage.cache_read_input_tokens", cachedTokens) + template, _ = sjson.SetBytes(template, "usage.cache_read_input_tokens", cachedTokens) } output = "event: message_delta\n" @@ -139,9 +139,9 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa itemType := itemResult.Get("type").String() if itemType == "function_call" { (*param).(*ConvertCodexResponseToClaudeParams).HasToolCall = true - template = `{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) - template, _ = sjson.SetBytesM(template, "content_block.id", itemResult.Get("call_id").String()) + template = []byte(`{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template, _ = sjson.SetBytes(template, "content_block.id", itemResult.Get("call_id").String()) { // Restore original tool name if shortened name := itemResult.Get("name").String() @@ -149,14 +149,14 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa if orig, ok := rev[name]; ok { name = orig } - template, _ = sjson.SetBytesM(template, "content_block.name", name) + template, _ = sjson.SetBytes(template, "content_block.name", name) } output = "event: content_block_start\n" output += fmt.Sprintf("data: %s\n\n", template) - template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template = []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) output += "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) @@ -165,8 +165,8 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa itemResult := rootResult.Get("item") itemType := itemResult.Get("type").String() if itemType == "function_call" { - template = `{"type":"content_block_stop","index":0}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template = []byte(`{"type":"content_block_stop","index":0}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex++ output = "event: content_block_stop\n" @@ -174,9 +174,9 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa } case "response.function_call_arguments.delta": (*param).(*ConvertCodexResponseToClaudeParams).HasReceivedArgumentsDelta = true - template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) - template, _ = sjson.SetBytesM(template, "delta.partial_json", rootResult.Get("delta").String()) + template = []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template, _ = sjson.SetBytes(template, "delta.partial_json", rootResult.Get("delta").String()) output += "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) @@ -186,9 +186,9 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa return nil } else { // No deltas were received; emit the full arguments as a single delta. - template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` - template, _ = sjson.SetBytesM(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) - template, _ = sjson.SetBytesM(template, "delta.partial_json", rootResult.Get("arguments").String()) + template = []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}`) + template, _ = sjson.SetBytes(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex) + template, _ = sjson.SetBytes(template, "delta.partial_json", rootResult.Get("arguments").String()) output += "event: content_block_delta\n" output += fmt.Sprintf("data: %s\n\n", template) @@ -224,14 +224,14 @@ func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, original return "" } - out := `{"id":"","type":"message","role":"assistant","model":"","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}` - out, _ = sjson.SetBytesM(out, "id", responseData.Get("id").String()) - out, _ = sjson.SetBytesM(out, "model", responseData.Get("model").String()) + out := []byte(`{"id":"","type":"message","role":"assistant","model":"","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}`) + out, _ = sjson.SetBytes(out, "id", responseData.Get("id").String()) + out, _ = sjson.SetBytes(out, "model", responseData.Get("model").String()) inputTokens, outputTokens, cachedTokens := extractResponsesUsage(responseData.Get("usage")) - out, _ = sjson.SetBytesM(out, "usage.input_tokens", inputTokens) - out, _ = sjson.SetBytesM(out, "usage.output_tokens", outputTokens) + out, _ = sjson.SetBytes(out, "usage.input_tokens", inputTokens) + out, _ = sjson.SetBytes(out, "usage.output_tokens", outputTokens) if cachedTokens > 0 { - out, _ = sjson.SetBytesM(out, "usage.cache_read_input_tokens", cachedTokens) + out, _ = sjson.SetBytes(out, "usage.cache_read_input_tokens", cachedTokens) } hasToolCall := false @@ -272,9 +272,9 @@ func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, original } } if thinkingBuilder.Len() > 0 { - block := `{"type":"thinking","thinking":""}` - block, _ = sjson.SetBytesM(block, "thinking", thinkingBuilder.String()) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"thinking","thinking":""}`) + block, _ = sjson.SetBytes(block, "thinking", thinkingBuilder.String()) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } case "message": if content := item.Get("content"); content.Exists() { @@ -283,9 +283,9 @@ func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, original if part.Get("type").String() == "output_text" { text := part.Get("text").String() if text != "" { - block := `{"type":"text","text":""}` - block, _ = sjson.SetBytesM(block, "text", text) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"text","text":""}`) + block, _ = sjson.SetBytes(block, "text", text) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } } return true @@ -293,9 +293,9 @@ func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, original } else { text := content.String() if text != "" { - block := `{"type":"text","text":""}` - block, _ = sjson.SetBytesM(block, "text", text) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"text","text":""}`) + block, _ = sjson.SetBytes(block, "text", text) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } } } @@ -306,9 +306,9 @@ func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, original name = original } - toolBlock := `{"type":"tool_use","id":"","name":"","input":{}}` - toolBlock, _ = sjson.SetBytesM(toolBlock, "id", item.Get("call_id").String()) - toolBlock, _ = sjson.SetBytesM(toolBlock, "name", name) + toolBlock := []byte(`{"type":"tool_use","id":"","name":"","input":{}}`) + toolBlock, _ = sjson.SetBytes(toolBlock, "id", item.Get("call_id").String()) + toolBlock, _ = sjson.SetBytes(toolBlock, "name", name) inputRaw := "{}" if argsStr := item.Get("arguments").String(); argsStr != "" && gjson.Valid(argsStr) { argsJSON := gjson.Parse(argsStr) @@ -316,23 +316,23 @@ func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, original inputRaw = argsJSON.Raw } } - toolBlock, _ = sjson.SetRaw(toolBlock, "input", inputRaw) - out, _ = sjson.SetRaw(out, "content.-1", toolBlock) + toolBlock, _ = sjson.SetRawBytes(toolBlock, "input", inputRaw) + out, _ = sjson.SetRawBytes(out, "content.-1", toolBlock) } return true }) } if stopReason := responseData.Get("stop_reason"); stopReason.Exists() && stopReason.String() != "" { - out, _ = sjson.SetBytesM(out, "stop_reason", stopReason.String()) + out, _ = sjson.SetBytes(out, "stop_reason", stopReason.String()) } else if hasToolCall { - out, _ = sjson.SetBytesM(out, "stop_reason", "tool_use") + out, _ = sjson.SetBytes(out, "stop_reason", "tool_use") } else { - out, _ = sjson.SetBytesM(out, "stop_reason", "end_turn") + out, _ = sjson.SetBytes(out, "stop_reason", "end_turn") } if stopSequence := responseData.Get("stop_sequence"); stopSequence.Exists() && stopSequence.String() != "" { - out, _ = sjson.SetRaw(out, "stop_sequence", stopSequence.Raw) + out, _ = sjson.SetRawBytes(out, "stop_sequence", stopSequence.Raw) } return out diff --git a/pkg/llmproxy/translator/codex/gemini/codex_gemini_response.go b/pkg/llmproxy/translator/codex/gemini/codex_gemini_response.go index 3ba7c10639..662d5c1f34 100644 --- a/pkg/llmproxy/translator/codex/gemini/codex_gemini_response.go +++ b/pkg/llmproxy/translator/codex/gemini/codex_gemini_response.go @@ -110,22 +110,22 @@ func ConvertCodexResponseToGemini(_ context.Context, modelName string, originalR switch typeStr { case "response.created": // Handle response creation - set model and response ID - template, _ = sjson.SetBytesM(template, "modelVersion", rootResult.Get("response.model").String()) - template, _ = sjson.SetBytesM(template, "responseId", rootResult.Get("response.id").String()) + template, _ = sjson.SetBytes(template, "modelVersion", rootResult.Get("response.model").String()) + template, _ = sjson.SetBytes(template, "responseId", rootResult.Get("response.id").String()) (*param).(*ConvertCodexResponseToGeminiParams).ResponseID = rootResult.Get("response.id").String() case "response.reasoning_summary_text.delta": // Handle reasoning/thinking content delta - part := `{"thought":true,"text":""}` - part, _ = sjson.SetBytesM(part, "text", rootResult.Get("delta").String()) - template, _ = sjson.SetRaw(template, "candidates.0.content.parts.-1", part) + part := []byte(`{"thought":true,"text":""}`) + part, _ = sjson.SetBytes(part, "text", rootResult.Get("delta").String()) + template, _ = sjson.SetRawBytes(template, "candidates.0.content.parts.-1", part) case "response.output_text.delta": // Handle regular text content delta - part := `{"text":""}` - part, _ = sjson.SetBytesM(part, "text", rootResult.Get("delta").String()) - template, _ = sjson.SetRaw(template, "candidates.0.content.parts.-1", part) + part := []byte(`{"text":""}`) + part, _ = sjson.SetBytes(part, "text", rootResult.Get("delta").String()) + template, _ = sjson.SetRawBytes(template, "candidates.0.content.parts.-1", part) case "response.completed": // Handle response completion with usage metadata - template, _ = sjson.SetBytesM(template, "usageMetadata.promptTokenCount", rootResult.Get("response.usage.input_tokens").Int()) - template, _ = sjson.SetBytesM(template, "usageMetadata.candidatesTokenCount", rootResult.Get("response.usage.output_tokens").Int()) + template, _ = sjson.SetBytes(template, "usageMetadata.promptTokenCount", rootResult.Get("response.usage.input_tokens").Int()) + template, _ = sjson.SetBytes(template, "usageMetadata.candidatesTokenCount", rootResult.Get("response.usage.output_tokens").Int()) totalTokens := rootResult.Get("response.usage.input_tokens").Int() + rootResult.Get("response.usage.output_tokens").Int() - template, _ = sjson.SetBytesM(template, "usageMetadata.totalTokenCount", totalTokens) + template, _ = sjson.SetBytes(template, "usageMetadata.totalTokenCount", totalTokens) default: return []string{} } diff --git a/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_request.go b/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_request.go index 17fcdee201..2275774008 100644 --- a/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_request.go +++ b/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_request.go @@ -34,7 +34,7 @@ func ConvertOpenAIResponsesRequestToCodex(modelName string, inputRawJSON []byte, inputResult := gjson.GetBytes(rawJSON, "input") if inputResult.Type == gjson.String { - input, _ := sjson.SetBytesM(`[{"type":"message","role":"user","content":[{"type":"input_text","text":""}]}]`, "0.content.0.text", inputResult.String()) + input, _ := sjson.SetBytes(`[{"type":"message","role":"user","content":[{"type":"input_text","text":""}]}]`, "0.content.0.text", inputResult.String()) rawJSON, _ = sjson.SetRawBytes(rawJSON, "input", []byte(input)) } @@ -198,10 +198,10 @@ func normalizeResponseTools(rawJSON []byte, nameMap map[string]string) []byte { if name != fn.Get("name").String() { changed = true fnRaw := fn.Raw - fnRaw, _ = sjson.SetBytesM(fnRaw, "name", name) - item := `{}` - item, _ = sjson.SetBytesM(item, "type", "function") - item, _ = sjson.SetRaw(item, "function", fnRaw) + fnRaw, _ = sjson.SetBytes(fnRaw, "name", name) + item := []byte(`{}`) + item, _ = sjson.SetBytes(item, "type", "function") + item, _ = sjson.SetRawBytes(item, "function", fnRaw) result = append(result, item) } else { result = append(result, t.Raw) @@ -214,7 +214,7 @@ func normalizeResponseTools(rawJSON []byte, nameMap map[string]string) []byte { out := "[]" for _, item := range result { - out, _ = sjson.SetRaw(out, "-1", item) + out, _ = sjson.SetRawBytes(out, "-1", item) } rawJSON, _ = sjson.SetRawBytes(rawJSON, "tools", []byte(out)) return rawJSON @@ -246,7 +246,7 @@ func normalizeResponseToolChoice(rawJSON []byte, nameMap map[string]string) []by return rawJSON } - updated, _ := sjson.SetBytesM(tc.Raw, "function.name", name) + updated, _ := sjson.SetBytes(tc.Raw, "function.name", name) rawJSON, _ = sjson.SetRawBytes(rawJSON, "tool_choice", []byte(updated)) return rawJSON } diff --git a/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_response.go b/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_response.go index 539bada71d..d2e401174c 100644 --- a/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_response.go +++ b/pkg/llmproxy/translator/codex/openai/responses/codex_openai-responses_response.go @@ -42,7 +42,7 @@ func ConvertCodexResponseToOpenAIResponsesNonStream(_ context.Context, modelName template := responseResult.Raw if responseResult.Get("instructions").Exists() { instructions := gjson.GetBytes(originalRequestRawJSON, "instructions").String() - template, _ = sjson.SetBytesM(template, "instructions", instructions) + template, _ = sjson.SetBytes(template, "instructions", instructions) } return template } diff --git a/pkg/llmproxy/translator/gemini-cli/claude/gemini-cli_claude_request.go b/pkg/llmproxy/translator/gemini-cli/claude/gemini-cli_claude_request.go index fc587070cb..326318aa2d 100644 --- a/pkg/llmproxy/translator/gemini-cli/claude/gemini-cli_claude_request.go +++ b/pkg/llmproxy/translator/gemini-cli/claude/gemini-cli_claude_request.go @@ -40,7 +40,7 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] // Build output Gemini CLI request JSON out := `{"model":"","request":{"contents":[]}}` - out, _ = sjson.SetBytesM(out, "model", modelName) + out, _ = sjson.SetBytes(out, "model", modelName) // system instruction if systemResult := gjson.GetBytes(rawJSON, "system"); systemResult.IsArray() { @@ -51,7 +51,7 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] textResult := systemPromptResult.Get("text") if textResult.Type == gjson.String { part := `{"text":""}` - part, _ = sjson.SetBytesM(part, "text", textResult.String()) + part, _ = sjson.SetBytes(part, "text", textResult.String()) systemInstruction, _ = sjson.SetRaw(systemInstruction, "parts.-1", part) hasSystemParts = true } @@ -62,7 +62,7 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] out, _ = sjson.SetRaw(out, "request.systemInstruction", systemInstruction) } } else if systemResult.Type == gjson.String { - out, _ = sjson.SetBytesM(out, "request.systemInstruction.parts.-1.text", systemResult.String()) + out, _ = sjson.SetBytes(out, "request.systemInstruction.parts.-1.text", systemResult.String()) } // contents @@ -78,7 +78,7 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] } contentJSON := `{"role":"","parts":[]}` - contentJSON, _ = sjson.SetBytesM(contentJSON, "role", role) + contentJSON, _ = sjson.SetBytes(contentJSON, "role", role) contentsResult := messageResult.Get("content") if contentsResult.IsArray() { @@ -86,7 +86,7 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] switch contentResult.Get("type").String() { case "text": part := `{"text":""}` - part, _ = sjson.SetBytesM(part, "text", contentResult.Get("text").String()) + part, _ = sjson.SetBytes(part, "text", contentResult.Get("text").String()) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) case "tool_use": @@ -101,8 +101,8 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] sanitizedArgs = functionArgs } part := `{"thoughtSignature":"","functionCall":{"name":"","args":{}}}` - part, _ = sjson.SetBytesM(part, "thoughtSignature", geminiCLIClaudeThoughtSignature) - part, _ = sjson.SetBytesM(part, "functionCall.name", functionName) + part, _ = sjson.SetBytes(part, "thoughtSignature", geminiCLIClaudeThoughtSignature) + part, _ = sjson.SetBytes(part, "functionCall.name", functionName) part, _ = sjson.SetRaw(part, "functionCall.args", sanitizedArgs) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) } @@ -119,8 +119,8 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] } responseData := contentResult.Get("content").Raw part := `{"functionResponse":{"name":"","response":{"result":""}}}` - part, _ = sjson.SetBytesM(part, "functionResponse.name", funcName) - part, _ = sjson.SetBytesM(part, "functionResponse.response.result", responseData) + part, _ = sjson.SetBytes(part, "functionResponse.name", funcName) + part, _ = sjson.SetBytes(part, "functionResponse.response.result", responseData) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) case "image": @@ -130,8 +130,8 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] data := source.Get("data").String() if mimeType != "" && data != "" { part := `{"inlineData":{"mime_type":"","data":""}}` - part, _ = sjson.SetBytesM(part, "inlineData.mime_type", mimeType) - part, _ = sjson.SetBytesM(part, "inlineData.data", data) + part, _ = sjson.SetBytes(part, "inlineData.mime_type", mimeType) + part, _ = sjson.SetBytes(part, "inlineData.data", data) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) } } @@ -141,7 +141,7 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] out, _ = sjson.SetRaw(out, "request.contents.-1", contentJSON) } else if contentsResult.Type == gjson.String { part := `{"text":""}` - part, _ = sjson.SetBytesM(part, "text", contentsResult.String()) + part, _ = sjson.SetBytes(part, "text", contentsResult.String()) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) out, _ = sjson.SetRaw(out, "request.contents.-1", contentJSON) } @@ -183,24 +183,24 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] case "enabled": if b := t.Get("budget_tokens"); b.Exists() && b.Type == gjson.Number { budget := int(b.Int()) - out, _ = sjson.SetBytesM(out, "request.generationConfig.thinkingConfig.thinkingBudget", budget) - out, _ = sjson.SetBytesM(out, "request.generationConfig.thinkingConfig.includeThoughts", true) + out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.thinkingBudget", budget) + out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.includeThoughts", true) } case "adaptive": // Keep adaptive as a high level sentinel; ApplyThinking resolves it // to model-specific max capability. - out, _ = sjson.SetBytesM(out, "request.generationConfig.thinkingConfig.thinkingLevel", "high") - out, _ = sjson.SetBytesM(out, "request.generationConfig.thinkingConfig.includeThoughts", true) + out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.thinkingLevel", "high") + out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.includeThoughts", true) } } if v := gjson.GetBytes(rawJSON, "temperature"); v.Exists() && v.Type == gjson.Number { - out, _ = sjson.SetBytesM(out, "request.generationConfig.temperature", v.Num) + out, _ = sjson.SetBytes(out, "request.generationConfig.temperature", v.Num) } if v := gjson.GetBytes(rawJSON, "top_p"); v.Exists() && v.Type == gjson.Number { - out, _ = sjson.SetBytesM(out, "request.generationConfig.topP", v.Num) + out, _ = sjson.SetBytes(out, "request.generationConfig.topP", v.Num) } if v := gjson.GetBytes(rawJSON, "top_k"); v.Exists() && v.Type == gjson.Number { - out, _ = sjson.SetBytesM(out, "request.generationConfig.topK", v.Num) + out, _ = sjson.SetBytes(out, "request.generationConfig.topK", v.Num) } outBytes := []byte(out) diff --git a/pkg/llmproxy/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_response.go b/pkg/llmproxy/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_response.go index b6ae1d5faf..4738322288 100644 --- a/pkg/llmproxy/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_response.go +++ b/pkg/llmproxy/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_response.go @@ -105,7 +105,7 @@ func ConvertCliResponseToOpenAI(_ context.Context, _ string, originalRequestRawJ } promptTokenCount := usageResult.Get("promptTokenCount").Int() thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int() - template, _ = sjson.SetBytesM(template, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) + template, _ = sjson.SetBytes(template, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) if thoughtsTokenCount > 0 { template, _ = sjson.SetBytes(template, "usage.completion_tokens_details.reasoning_tokens", thoughtsTokenCount) } diff --git a/pkg/llmproxy/translator/gemini/claude/gemini_claude_request.go b/pkg/llmproxy/translator/gemini/claude/gemini_claude_request.go index d1dbc633b6..5b97422bc7 100644 --- a/pkg/llmproxy/translator/gemini/claude/gemini_claude_request.go +++ b/pkg/llmproxy/translator/gemini/claude/gemini_claude_request.go @@ -33,7 +33,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) // Build output Gemini CLI request JSON out := `{"contents":[]}` - out, _ = sjson.SetBytesM(out, "model", modelName) + out, _ = sjson.SetBytes(out, "model", modelName) // system instruction if systemResult := gjson.GetBytes(rawJSON, "system"); systemResult.IsArray() { @@ -44,7 +44,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) textResult := systemPromptResult.Get("text") if textResult.Type == gjson.String { part := `{"text":""}` - part, _ = sjson.SetBytesM(part, "text", textResult.String()) + part, _ = sjson.SetBytes(part, "text", textResult.String()) systemInstruction, _ = sjson.SetRaw(systemInstruction, "parts.-1", part) hasSystemParts = true } @@ -55,7 +55,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) out, _ = sjson.SetRaw(out, "system_instruction", systemInstruction) } } else if systemResult.Type == gjson.String { - out, _ = sjson.SetBytesM(out, "system_instruction.parts.-1.text", systemResult.String()) + out, _ = sjson.SetBytes(out, "system_instruction.parts.-1.text", systemResult.String()) } // contents @@ -71,7 +71,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) } contentJSON := `{"role":"","parts":[]}` - contentJSON, _ = sjson.SetBytesM(contentJSON, "role", role) + contentJSON, _ = sjson.SetBytes(contentJSON, "role", role) contentsResult := messageResult.Get("content") if contentsResult.IsArray() { @@ -85,7 +85,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) return true } part := `{"text":""}` - part, _ = sjson.SetBytesM(part, "text", text) + part, _ = sjson.SetBytes(part, "text", text) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) case "tool_use": @@ -100,8 +100,8 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) sanitizedArgs = functionArgs } part := `{"thoughtSignature":"","functionCall":{"name":"","args":{}}}` - part, _ = sjson.SetBytesM(part, "thoughtSignature", geminiClaudeThoughtSignature) - part, _ = sjson.SetBytesM(part, "functionCall.name", functionName) + part, _ = sjson.SetBytes(part, "thoughtSignature", geminiClaudeThoughtSignature) + part, _ = sjson.SetBytes(part, "functionCall.name", functionName) part, _ = sjson.SetRaw(part, "functionCall.args", sanitizedArgs) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) } @@ -118,8 +118,8 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) } responseData := contentResult.Get("content").Raw part := `{"functionResponse":{"name":"","response":{"result":""}}}` - part, _ = sjson.SetBytesM(part, "functionResponse.name", funcName) - part, _ = sjson.SetBytesM(part, "functionResponse.response.result", responseData) + part, _ = sjson.SetBytes(part, "functionResponse.name", funcName) + part, _ = sjson.SetBytes(part, "functionResponse.response.result", responseData) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) } return true @@ -132,7 +132,7 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) // Skip empty text parts to avoid Gemini API error if strings.TrimSpace(text) != "" { part := `{"text":""}` - part, _ = sjson.SetBytesM(part, "text", text) + part, _ = sjson.SetBytes(part, "text", text) contentJSON, _ = sjson.SetRaw(contentJSON, "parts.-1", part) out, _ = sjson.SetRaw(out, "contents.-1", contentJSON) } @@ -176,24 +176,24 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) case "enabled": if b := t.Get("budget_tokens"); b.Exists() && b.Type == gjson.Number { budget := int(b.Int()) - out, _ = sjson.SetBytesM(out, "generationConfig.thinkingConfig.thinkingBudget", budget) - out, _ = sjson.SetBytesM(out, "generationConfig.thinkingConfig.includeThoughts", true) + out, _ = sjson.SetBytes(out, "generationConfig.thinkingConfig.thinkingBudget", budget) + out, _ = sjson.SetBytes(out, "generationConfig.thinkingConfig.includeThoughts", true) } case "adaptive": // Keep adaptive as a high level sentinel; ApplyThinking resolves it // to model-specific max capability. - out, _ = sjson.SetBytesM(out, "generationConfig.thinkingConfig.thinkingLevel", "high") - out, _ = sjson.SetBytesM(out, "generationConfig.thinkingConfig.includeThoughts", true) + out, _ = sjson.SetBytes(out, "generationConfig.thinkingConfig.thinkingLevel", "high") + out, _ = sjson.SetBytes(out, "generationConfig.thinkingConfig.includeThoughts", true) } } if v := gjson.GetBytes(rawJSON, "temperature"); v.Exists() && v.Type == gjson.Number { - out, _ = sjson.SetBytesM(out, "generationConfig.temperature", v.Num) + out, _ = sjson.SetBytes(out, "generationConfig.temperature", v.Num) } if v := gjson.GetBytes(rawJSON, "top_p"); v.Exists() && v.Type == gjson.Number { - out, _ = sjson.SetBytesM(out, "generationConfig.topP", v.Num) + out, _ = sjson.SetBytes(out, "generationConfig.topP", v.Num) } if v := gjson.GetBytes(rawJSON, "top_k"); v.Exists() && v.Type == gjson.Number { - out, _ = sjson.SetBytesM(out, "generationConfig.topK", v.Num) + out, _ = sjson.SetBytes(out, "generationConfig.topK", v.Num) } result := []byte(out) diff --git a/pkg/llmproxy/translator/gemini/common/sanitize.go b/pkg/llmproxy/translator/gemini/common/sanitize.go index 15f7f558dc..69685cae3e 100644 --- a/pkg/llmproxy/translator/gemini/common/sanitize.go +++ b/pkg/llmproxy/translator/gemini/common/sanitize.go @@ -42,7 +42,7 @@ func SanitizeOpenAIInputForGemini(raw string) string { // NormalizeOpenAIFunctionSchemaForGemini builds a Gemini-safe parametersJsonSchema // from OpenAI function schema inputs and enforces a deterministic root shape. func NormalizeOpenAIFunctionSchemaForGemini(params gjson.Result, strict bool) string { - out := `{"type":"OBJECT","properties":{}}` + out := []byte(`{"type":"OBJECT","properties":{}}`) if params.Exists() { raw := strings.TrimSpace(params.Raw) if params.Type == gjson.String { @@ -52,12 +52,12 @@ func NormalizeOpenAIFunctionSchemaForGemini(params gjson.Result, strict bool) st out = SanitizeParametersJSONSchemaForGemini(raw) } } - out, _ = sjson.SetBytesM(out, "type", "OBJECT") - if !gjson.Get(out, "properties").Exists() { - out, _ = sjson.SetRaw(out, "properties", `{}`) + out, _ = sjson.SetBytes(out, "type", "OBJECT") + if !gjson.GetBytes(out, "properties").Exists() { + out, _ = sjson.SetRawBytes(out, "properties", `{}`) } if strict { - out, _ = sjson.SetBytesM(out, "additionalProperties", false) + out, _ = sjson.SetBytes(out, "additionalProperties", false) } return out } diff --git a/pkg/llmproxy/translator/gemini/openai/chat-completions/gemini_openai_response.go b/pkg/llmproxy/translator/gemini/openai/chat-completions/gemini_openai_response.go index f2cc156657..788e737574 100644 --- a/pkg/llmproxy/translator/gemini/openai/chat-completions/gemini_openai_response.go +++ b/pkg/llmproxy/translator/gemini/openai/chat-completions/gemini_openai_response.go @@ -108,7 +108,7 @@ func ConvertGeminiResponseToOpenAI(_ context.Context, _ string, originalRequestR } promptTokenCount := usageResult.Get("promptTokenCount").Int() - cachedTokenCount thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int() - baseTemplate, _ = sjson.SetBytesM(baseTemplate, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) + baseTemplate, _ = sjson.SetBytes(baseTemplate, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) if thoughtsTokenCount > 0 { baseTemplate, _ = sjson.SetBytes(baseTemplate, "usage.completion_tokens_details.reasoning_tokens", thoughtsTokenCount) } @@ -308,7 +308,7 @@ func ConvertGeminiResponseToOpenAINonStream(_ context.Context, _ string, origina promptTokenCount := usageResult.Get("promptTokenCount").Int() thoughtsTokenCount := usageResult.Get("thoughtsTokenCount").Int() cachedTokenCount := usageResult.Get("cachedContentTokenCount").Int() - template, _ = sjson.SetBytesM(template, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) + template, _ = sjson.SetBytes(template, "usage.prompt_tokens", promptTokenCount+thoughtsTokenCount) if thoughtsTokenCount > 0 { template, _ = sjson.SetBytes(template, "usage.completion_tokens_details.reasoning_tokens", thoughtsTokenCount) } diff --git a/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_request.go b/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_request.go index 40c6d29c03..ec4679fffd 100644 --- a/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_request.go +++ b/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_request.go @@ -204,7 +204,7 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte textValue := text.String() if strings.TrimSpace(textValue) != "" { partJSON = `{"text":""}` - partJSON, _ = sjson.SetBytesM(partJSON, "text", textValue) + partJSON, _ = sjson.SetBytes(partJSON, "text", textValue) } } case "input_image": @@ -291,8 +291,8 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte } one := `{"role":"","parts":[{"text":""}]}` - one, _ = sjson.SetBytesM(one, "role", effRole) - one, _ = sjson.SetBytesM(one, "parts.0.text", contentText) + one, _ = sjson.SetBytes(one, "role", effRole) + one, _ = sjson.SetBytes(one, "parts.0.text", contentText) out, _ = sjson.SetRaw(out, "contents.-1", one) } diff --git a/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_response.go b/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_response.go index 0d5ff5ae9c..8703232d6b 100644 --- a/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_response.go +++ b/pkg/llmproxy/translator/gemini/openai/responses/gemini_openai-responses_response.go @@ -136,25 +136,25 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, } full := st.ReasoningBuf.String() textDone := `{"type":"response.reasoning_summary_text.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"text":""}` - textDone, _ = sjson.SetBytesM(textDone, "sequence_number", nextSeq()) - textDone, _ = sjson.SetBytesM(textDone, "item_id", st.ReasoningItemID) - textDone, _ = sjson.SetBytesM(textDone, "output_index", st.ReasoningIndex) - textDone, _ = sjson.SetBytesM(textDone, "text", full) + textDone, _ = sjson.SetBytes(textDone, "sequence_number", nextSeq()) + textDone, _ = sjson.SetBytes(textDone, "item_id", st.ReasoningItemID) + textDone, _ = sjson.SetBytes(textDone, "output_index", st.ReasoningIndex) + textDone, _ = sjson.SetBytes(textDone, "text", full) out = append(out, emitEvent("response.reasoning_summary_text.done", textDone)) partDone := `{"type":"response.reasoning_summary_part.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}` - partDone, _ = sjson.SetBytesM(partDone, "sequence_number", nextSeq()) - partDone, _ = sjson.SetBytesM(partDone, "item_id", st.ReasoningItemID) - partDone, _ = sjson.SetBytesM(partDone, "output_index", st.ReasoningIndex) - partDone, _ = sjson.SetBytesM(partDone, "part.text", full) + partDone, _ = sjson.SetBytes(partDone, "sequence_number", nextSeq()) + partDone, _ = sjson.SetBytes(partDone, "item_id", st.ReasoningItemID) + partDone, _ = sjson.SetBytes(partDone, "output_index", st.ReasoningIndex) + partDone, _ = sjson.SetBytes(partDone, "part.text", full) out = append(out, emitEvent("response.reasoning_summary_part.done", partDone)) itemDone := `{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"reasoning","encrypted_content":"","summary":[{"type":"summary_text","text":""}]}}` - itemDone, _ = sjson.SetBytesM(itemDone, "sequence_number", nextSeq()) - itemDone, _ = sjson.SetBytesM(itemDone, "item.id", st.ReasoningItemID) - itemDone, _ = sjson.SetBytesM(itemDone, "output_index", st.ReasoningIndex) - itemDone, _ = sjson.SetBytesM(itemDone, "item.encrypted_content", st.ReasoningEnc) - itemDone, _ = sjson.SetBytesM(itemDone, "item.summary.0.text", full) + itemDone, _ = sjson.SetBytes(itemDone, "sequence_number", nextSeq()) + itemDone, _ = sjson.SetBytes(itemDone, "item.id", st.ReasoningItemID) + itemDone, _ = sjson.SetBytes(itemDone, "output_index", st.ReasoningIndex) + itemDone, _ = sjson.SetBytes(itemDone, "item.encrypted_content", st.ReasoningEnc) + itemDone, _ = sjson.SetBytes(itemDone, "item.summary.0.text", full) out = append(out, emitEvent("response.output_item.done", itemDone)) st.ReasoningClosed = true @@ -169,22 +169,22 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, } fullText := st.ItemTextBuf.String() done := `{"type":"response.output_text.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"text":"","logprobs":[]}` - done, _ = sjson.SetBytesM(done, "sequence_number", nextSeq()) - done, _ = sjson.SetBytesM(done, "item_id", st.CurrentMsgID) - done, _ = sjson.SetBytesM(done, "output_index", st.MsgIndex) - done, _ = sjson.SetBytesM(done, "text", fullText) + done, _ = sjson.SetBytes(done, "sequence_number", nextSeq()) + done, _ = sjson.SetBytes(done, "item_id", st.CurrentMsgID) + done, _ = sjson.SetBytes(done, "output_index", st.MsgIndex) + done, _ = sjson.SetBytes(done, "text", fullText) out = append(out, emitEvent("response.output_text.done", done)) partDone := `{"type":"response.content_part.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}` - partDone, _ = sjson.SetBytesM(partDone, "sequence_number", nextSeq()) - partDone, _ = sjson.SetBytesM(partDone, "item_id", st.CurrentMsgID) - partDone, _ = sjson.SetBytesM(partDone, "output_index", st.MsgIndex) - partDone, _ = sjson.SetBytesM(partDone, "part.text", fullText) + partDone, _ = sjson.SetBytes(partDone, "sequence_number", nextSeq()) + partDone, _ = sjson.SetBytes(partDone, "item_id", st.CurrentMsgID) + partDone, _ = sjson.SetBytes(partDone, "output_index", st.MsgIndex) + partDone, _ = sjson.SetBytes(partDone, "part.text", fullText) out = append(out, emitEvent("response.content_part.done", partDone)) final := `{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"completed","content":[{"type":"output_text","text":""}],"role":"assistant"}}` - final, _ = sjson.SetBytesM(final, "sequence_number", nextSeq()) - final, _ = sjson.SetBytesM(final, "output_index", st.MsgIndex) - final, _ = sjson.SetBytesM(final, "item.id", st.CurrentMsgID) - final, _ = sjson.SetBytesM(final, "item.content.0.text", fullText) + final, _ = sjson.SetBytes(final, "sequence_number", nextSeq()) + final, _ = sjson.SetBytes(final, "output_index", st.MsgIndex) + final, _ = sjson.SetBytes(final, "item.id", st.CurrentMsgID) + final, _ = sjson.SetBytes(final, "item.content.0.text", fullText) out = append(out, emitEvent("response.output_item.done", final)) st.MsgClosed = true @@ -209,15 +209,15 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, } created := `{"type":"response.created","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"in_progress","background":false,"error":null,"output":[]}}` - created, _ = sjson.SetBytesM(created, "sequence_number", nextSeq()) - created, _ = sjson.SetBytesM(created, "response.id", st.ResponseID) - created, _ = sjson.SetBytesM(created, "response.created_at", st.CreatedAt) + created, _ = sjson.SetBytes(created, "sequence_number", nextSeq()) + created, _ = sjson.SetBytes(created, "response.id", st.ResponseID) + created, _ = sjson.SetBytes(created, "response.created_at", st.CreatedAt) out = append(out, emitEvent("response.created", created)) inprog := `{"type":"response.in_progress","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"in_progress"}}` - inprog, _ = sjson.SetBytesM(inprog, "sequence_number", nextSeq()) - inprog, _ = sjson.SetBytesM(inprog, "response.id", st.ResponseID) - inprog, _ = sjson.SetBytesM(inprog, "response.created_at", st.CreatedAt) + inprog, _ = sjson.SetBytes(inprog, "sequence_number", nextSeq()) + inprog, _ = sjson.SetBytes(inprog, "response.id", st.ResponseID) + inprog, _ = sjson.SetBytes(inprog, "response.created_at", st.CreatedAt) out = append(out, emitEvent("response.in_progress", inprog)) st.Started = true @@ -244,24 +244,24 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, st.NextIndex++ st.ReasoningItemID = fmt.Sprintf("rs_%s_%d", st.ResponseID, st.ReasoningIndex) item := `{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"reasoning","status":"in_progress","encrypted_content":"","summary":[]}}` - item, _ = sjson.SetBytesM(item, "sequence_number", nextSeq()) - item, _ = sjson.SetBytesM(item, "output_index", st.ReasoningIndex) - item, _ = sjson.SetBytesM(item, "item.id", st.ReasoningItemID) - item, _ = sjson.SetBytesM(item, "item.encrypted_content", st.ReasoningEnc) + item, _ = sjson.SetBytes(item, "sequence_number", nextSeq()) + item, _ = sjson.SetBytes(item, "output_index", st.ReasoningIndex) + item, _ = sjson.SetBytes(item, "item.id", st.ReasoningItemID) + item, _ = sjson.SetBytes(item, "item.encrypted_content", st.ReasoningEnc) out = append(out, emitEvent("response.output_item.added", item)) partAdded := `{"type":"response.reasoning_summary_part.added","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}` - partAdded, _ = sjson.SetBytesM(partAdded, "sequence_number", nextSeq()) - partAdded, _ = sjson.SetBytesM(partAdded, "item_id", st.ReasoningItemID) - partAdded, _ = sjson.SetBytesM(partAdded, "output_index", st.ReasoningIndex) + partAdded, _ = sjson.SetBytes(partAdded, "sequence_number", nextSeq()) + partAdded, _ = sjson.SetBytes(partAdded, "item_id", st.ReasoningItemID) + partAdded, _ = sjson.SetBytes(partAdded, "output_index", st.ReasoningIndex) out = append(out, emitEvent("response.reasoning_summary_part.added", partAdded)) } if t := part.Get("text"); t.Exists() && t.String() != "" { st.ReasoningBuf.WriteString(t.String()) msg := `{"type":"response.reasoning_summary_text.delta","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"delta":""}` - msg, _ = sjson.SetBytesM(msg, "sequence_number", nextSeq()) - msg, _ = sjson.SetBytesM(msg, "item_id", st.ReasoningItemID) - msg, _ = sjson.SetBytesM(msg, "output_index", st.ReasoningIndex) - msg, _ = sjson.SetBytesM(msg, "delta", t.String()) + msg, _ = sjson.SetBytes(msg, "sequence_number", nextSeq()) + msg, _ = sjson.SetBytes(msg, "item_id", st.ReasoningItemID) + msg, _ = sjson.SetBytes(msg, "output_index", st.ReasoningIndex) + msg, _ = sjson.SetBytes(msg, "delta", t.String()) out = append(out, emitEvent("response.reasoning_summary_text.delta", msg)) } return true @@ -277,24 +277,24 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, st.NextIndex++ st.CurrentMsgID = fmt.Sprintf("msg_%s_0", st.ResponseID) item := `{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"in_progress","content":[],"role":"assistant"}}` - item, _ = sjson.SetBytesM(item, "sequence_number", nextSeq()) - item, _ = sjson.SetBytesM(item, "output_index", st.MsgIndex) - item, _ = sjson.SetBytesM(item, "item.id", st.CurrentMsgID) + item, _ = sjson.SetBytes(item, "sequence_number", nextSeq()) + item, _ = sjson.SetBytes(item, "output_index", st.MsgIndex) + item, _ = sjson.SetBytes(item, "item.id", st.CurrentMsgID) out = append(out, emitEvent("response.output_item.added", item)) partAdded := `{"type":"response.content_part.added","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}` - partAdded, _ = sjson.SetBytesM(partAdded, "sequence_number", nextSeq()) - partAdded, _ = sjson.SetBytesM(partAdded, "item_id", st.CurrentMsgID) - partAdded, _ = sjson.SetBytesM(partAdded, "output_index", st.MsgIndex) + partAdded, _ = sjson.SetBytes(partAdded, "sequence_number", nextSeq()) + partAdded, _ = sjson.SetBytes(partAdded, "item_id", st.CurrentMsgID) + partAdded, _ = sjson.SetBytes(partAdded, "output_index", st.MsgIndex) out = append(out, emitEvent("response.content_part.added", partAdded)) st.ItemTextBuf.Reset() } st.TextBuf.WriteString(t.String()) st.ItemTextBuf.WriteString(t.String()) msg := `{"type":"response.output_text.delta","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"delta":"","logprobs":[]}` - msg, _ = sjson.SetBytesM(msg, "sequence_number", nextSeq()) - msg, _ = sjson.SetBytesM(msg, "item_id", st.CurrentMsgID) - msg, _ = sjson.SetBytesM(msg, "output_index", st.MsgIndex) - msg, _ = sjson.SetBytesM(msg, "delta", t.String()) + msg, _ = sjson.SetBytes(msg, "sequence_number", nextSeq()) + msg, _ = sjson.SetBytes(msg, "item_id", st.CurrentMsgID) + msg, _ = sjson.SetBytes(msg, "output_index", st.MsgIndex) + msg, _ = sjson.SetBytes(msg, "delta", t.String()) out = append(out, emitEvent("response.output_text.delta", msg)) return true } @@ -327,40 +327,40 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, // Emit item.added for function call item := `{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"function_call","status":"in_progress","arguments":"","call_id":"","name":""}}` - item, _ = sjson.SetBytesM(item, "sequence_number", nextSeq()) - item, _ = sjson.SetBytesM(item, "output_index", idx) - item, _ = sjson.SetBytesM(item, "item.id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) - item, _ = sjson.SetBytesM(item, "item.call_id", st.FuncCallIDs[idx]) - item, _ = sjson.SetBytesM(item, "item.name", name) + item, _ = sjson.SetBytes(item, "sequence_number", nextSeq()) + item, _ = sjson.SetBytes(item, "output_index", idx) + item, _ = sjson.SetBytes(item, "item.id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) + item, _ = sjson.SetBytes(item, "item.call_id", st.FuncCallIDs[idx]) + item, _ = sjson.SetBytes(item, "item.name", name) out = append(out, emitEvent("response.output_item.added", item)) // Emit arguments delta (full args in one chunk). // When Gemini omits args, emit "{}" to keep Responses streaming event order consistent. if argsJSON != "" { ad := `{"type":"response.function_call_arguments.delta","sequence_number":0,"item_id":"","output_index":0,"delta":""}` - ad, _ = sjson.SetBytesM(ad, "sequence_number", nextSeq()) - ad, _ = sjson.SetBytesM(ad, "item_id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) - ad, _ = sjson.SetBytesM(ad, "output_index", idx) - ad, _ = sjson.SetBytesM(ad, "delta", argsJSON) + ad, _ = sjson.SetBytes(ad, "sequence_number", nextSeq()) + ad, _ = sjson.SetBytes(ad, "item_id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) + ad, _ = sjson.SetBytes(ad, "output_index", idx) + ad, _ = sjson.SetBytes(ad, "delta", argsJSON) out = append(out, emitEvent("response.function_call_arguments.delta", ad)) } // Gemini emits the full function call payload at once, so we can finalize it immediately. if !st.FuncDone[idx] { fcDone := `{"type":"response.function_call_arguments.done","sequence_number":0,"item_id":"","output_index":0,"arguments":""}` - fcDone, _ = sjson.SetBytesM(fcDone, "sequence_number", nextSeq()) - fcDone, _ = sjson.SetBytesM(fcDone, "item_id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) - fcDone, _ = sjson.SetBytesM(fcDone, "output_index", idx) - fcDone, _ = sjson.SetBytesM(fcDone, "arguments", argsJSON) + fcDone, _ = sjson.SetBytes(fcDone, "sequence_number", nextSeq()) + fcDone, _ = sjson.SetBytes(fcDone, "item_id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) + fcDone, _ = sjson.SetBytes(fcDone, "output_index", idx) + fcDone, _ = sjson.SetBytes(fcDone, "arguments", argsJSON) out = append(out, emitEvent("response.function_call_arguments.done", fcDone)) itemDone := `{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}}` - itemDone, _ = sjson.SetBytesM(itemDone, "sequence_number", nextSeq()) - itemDone, _ = sjson.SetBytesM(itemDone, "output_index", idx) - itemDone, _ = sjson.SetBytesM(itemDone, "item.id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) - itemDone, _ = sjson.SetBytesM(itemDone, "item.arguments", argsJSON) - itemDone, _ = sjson.SetBytesM(itemDone, "item.call_id", st.FuncCallIDs[idx]) - itemDone, _ = sjson.SetBytesM(itemDone, "item.name", st.FuncNames[idx]) + itemDone, _ = sjson.SetBytes(itemDone, "sequence_number", nextSeq()) + itemDone, _ = sjson.SetBytes(itemDone, "output_index", idx) + itemDone, _ = sjson.SetBytes(itemDone, "item.id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) + itemDone, _ = sjson.SetBytes(itemDone, "item.arguments", argsJSON) + itemDone, _ = sjson.SetBytes(itemDone, "item.call_id", st.FuncCallIDs[idx]) + itemDone, _ = sjson.SetBytes(itemDone, "item.name", st.FuncNames[idx]) out = append(out, emitEvent("response.output_item.done", itemDone)) st.FuncDone[idx] = true @@ -402,19 +402,19 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, args = b.String() } fcDone := `{"type":"response.function_call_arguments.done","sequence_number":0,"item_id":"","output_index":0,"arguments":""}` - fcDone, _ = sjson.SetBytesM(fcDone, "sequence_number", nextSeq()) - fcDone, _ = sjson.SetBytesM(fcDone, "item_id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) - fcDone, _ = sjson.SetBytesM(fcDone, "output_index", idx) - fcDone, _ = sjson.SetBytesM(fcDone, "arguments", args) + fcDone, _ = sjson.SetBytes(fcDone, "sequence_number", nextSeq()) + fcDone, _ = sjson.SetBytes(fcDone, "item_id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) + fcDone, _ = sjson.SetBytes(fcDone, "output_index", idx) + fcDone, _ = sjson.SetBytes(fcDone, "arguments", args) out = append(out, emitEvent("response.function_call_arguments.done", fcDone)) itemDone := `{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}}` - itemDone, _ = sjson.SetBytesM(itemDone, "sequence_number", nextSeq()) - itemDone, _ = sjson.SetBytesM(itemDone, "output_index", idx) - itemDone, _ = sjson.SetBytesM(itemDone, "item.id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) - itemDone, _ = sjson.SetBytesM(itemDone, "item.arguments", args) - itemDone, _ = sjson.SetBytesM(itemDone, "item.call_id", st.FuncCallIDs[idx]) - itemDone, _ = sjson.SetBytesM(itemDone, "item.name", st.FuncNames[idx]) + itemDone, _ = sjson.SetBytes(itemDone, "sequence_number", nextSeq()) + itemDone, _ = sjson.SetBytes(itemDone, "output_index", idx) + itemDone, _ = sjson.SetBytes(itemDone, "item.id", fmt.Sprintf("fc_%s", st.FuncCallIDs[idx])) + itemDone, _ = sjson.SetBytes(itemDone, "item.arguments", args) + itemDone, _ = sjson.SetBytes(itemDone, "item.call_id", st.FuncCallIDs[idx]) + itemDone, _ = sjson.SetBytes(itemDone, "item.name", st.FuncNames[idx]) out = append(out, emitEvent("response.output_item.done", itemDone)) st.FuncDone[idx] = true @@ -425,71 +425,71 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, // Build response.completed with aggregated outputs and request echo fields completed := `{"type":"response.completed","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"completed","background":false,"error":null}}` - completed, _ = sjson.SetBytesM(completed, "sequence_number", nextSeq()) - completed, _ = sjson.SetBytesM(completed, "response.id", st.ResponseID) - completed, _ = sjson.SetBytesM(completed, "response.created_at", st.CreatedAt) + completed, _ = sjson.SetBytes(completed, "sequence_number", nextSeq()) + completed, _ = sjson.SetBytes(completed, "response.id", st.ResponseID) + completed, _ = sjson.SetBytes(completed, "response.created_at", st.CreatedAt) if reqJSON := pickRequestJSON(originalRequestRawJSON, requestRawJSON); len(reqJSON) > 0 { req := unwrapRequestRoot(gjson.ParseBytes(reqJSON)) if v := req.Get("instructions"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.instructions", v.String()) + completed, _ = sjson.SetBytes(completed, "response.instructions", v.String()) } if v := req.Get("max_output_tokens"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.max_output_tokens", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.max_output_tokens", v.Int()) } if v := req.Get("max_tool_calls"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.max_tool_calls", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.max_tool_calls", v.Int()) } if v := req.Get("model"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.model", v.String()) + completed, _ = sjson.SetBytes(completed, "response.model", v.String()) } if v := req.Get("parallel_tool_calls"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.parallel_tool_calls", v.Bool()) + completed, _ = sjson.SetBytes(completed, "response.parallel_tool_calls", v.Bool()) } if v := req.Get("previous_response_id"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.previous_response_id", v.String()) + completed, _ = sjson.SetBytes(completed, "response.previous_response_id", v.String()) } if v := req.Get("prompt_cache_key"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.prompt_cache_key", v.String()) + completed, _ = sjson.SetBytes(completed, "response.prompt_cache_key", v.String()) } if v := req.Get("reasoning"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.reasoning", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.reasoning", v.Value()) } if v := req.Get("safety_identifier"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.safety_identifier", v.String()) + completed, _ = sjson.SetBytes(completed, "response.safety_identifier", v.String()) } if v := req.Get("service_tier"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.service_tier", v.String()) + completed, _ = sjson.SetBytes(completed, "response.service_tier", v.String()) } if v := req.Get("store"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.store", v.Bool()) + completed, _ = sjson.SetBytes(completed, "response.store", v.Bool()) } if v := req.Get("temperature"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.temperature", v.Float()) + completed, _ = sjson.SetBytes(completed, "response.temperature", v.Float()) } if v := req.Get("text"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.text", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.text", v.Value()) } if v := req.Get("tool_choice"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.tool_choice", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.tool_choice", v.Value()) } if v := req.Get("tools"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.tools", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.tools", v.Value()) } if v := req.Get("top_logprobs"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.top_logprobs", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.top_logprobs", v.Int()) } if v := req.Get("top_p"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.top_p", v.Float()) + completed, _ = sjson.SetBytes(completed, "response.top_p", v.Float()) } if v := req.Get("truncation"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.truncation", v.String()) + completed, _ = sjson.SetBytes(completed, "response.truncation", v.String()) } if v := req.Get("user"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.user", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.user", v.Value()) } if v := req.Get("metadata"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.metadata", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.metadata", v.Value()) } } @@ -498,16 +498,16 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, for idx := 0; idx < st.NextIndex; idx++ { if st.ReasoningOpened && idx == st.ReasoningIndex { item := `{"id":"","type":"reasoning","encrypted_content":"","summary":[{"type":"summary_text","text":""}]}` - item, _ = sjson.SetBytesM(item, "id", st.ReasoningItemID) - item, _ = sjson.SetBytesM(item, "encrypted_content", st.ReasoningEnc) - item, _ = sjson.SetBytesM(item, "summary.0.text", st.ReasoningBuf.String()) + item, _ = sjson.SetBytes(item, "id", st.ReasoningItemID) + item, _ = sjson.SetBytes(item, "encrypted_content", st.ReasoningEnc) + item, _ = sjson.SetBytes(item, "summary.0.text", st.ReasoningBuf.String()) outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) continue } if st.MsgOpened && idx == st.MsgIndex { item := `{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}` - item, _ = sjson.SetBytesM(item, "id", st.CurrentMsgID) - item, _ = sjson.SetBytesM(item, "content.0.text", st.TextBuf.String()) + item, _ = sjson.SetBytes(item, "id", st.CurrentMsgID) + item, _ = sjson.SetBytes(item, "content.0.text", st.TextBuf.String()) outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) continue } @@ -518,10 +518,10 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, args = b.String() } item := `{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}` - item, _ = sjson.SetBytesM(item, "id", fmt.Sprintf("fc_%s", callID)) - item, _ = sjson.SetBytesM(item, "arguments", args) - item, _ = sjson.SetBytesM(item, "call_id", callID) - item, _ = sjson.SetBytesM(item, "name", st.FuncNames[idx]) + item, _ = sjson.SetBytes(item, "id", fmt.Sprintf("fc_%s", callID)) + item, _ = sjson.SetBytes(item, "arguments", args) + item, _ = sjson.SetBytes(item, "call_id", callID) + item, _ = sjson.SetBytes(item, "name", st.FuncNames[idx]) outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) } } @@ -533,24 +533,24 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, if um := root.Get("usageMetadata"); um.Exists() { // input tokens = prompt + thoughts input := um.Get("promptTokenCount").Int() + um.Get("thoughtsTokenCount").Int() - completed, _ = sjson.SetBytesM(completed, "response.usage.input_tokens", input) + completed, _ = sjson.SetBytes(completed, "response.usage.input_tokens", input) // cached token details: align with OpenAI "cached_tokens" semantics. - completed, _ = sjson.SetBytesM(completed, "response.usage.input_tokens_details.cached_tokens", um.Get("cachedContentTokenCount").Int()) + completed, _ = sjson.SetBytes(completed, "response.usage.input_tokens_details.cached_tokens", um.Get("cachedContentTokenCount").Int()) // output tokens if v := um.Get("candidatesTokenCount"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens", v.Int()) } else { - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens", 0) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens", 0) } if v := um.Get("thoughtsTokenCount"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens_details.reasoning_tokens", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens_details.reasoning_tokens", v.Int()) } else { - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens_details.reasoning_tokens", 0) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens_details.reasoning_tokens", 0) } if v := um.Get("totalTokenCount"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.usage.total_tokens", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.usage.total_tokens", v.Int()) } else { - completed, _ = sjson.SetBytesM(completed, "response.usage.total_tokens", 0) + completed, _ = sjson.SetBytes(completed, "response.usage.total_tokens", 0) } } @@ -577,7 +577,7 @@ func ConvertGeminiResponseToOpenAIResponsesNonStream(_ context.Context, _ string if !strings.HasPrefix(id, "resp_") { id = fmt.Sprintf("resp_%s", id) } - resp, _ = sjson.SetBytesM(resp, "id", id) + resp, _ = sjson.SetBytes(resp, "id", id) // created_at: map from createTime if available createdAt := time.Now().Unix() @@ -586,75 +586,75 @@ func ConvertGeminiResponseToOpenAIResponsesNonStream(_ context.Context, _ string createdAt = t.Unix() } } - resp, _ = sjson.SetBytesM(resp, "created_at", createdAt) + resp, _ = sjson.SetBytes(resp, "created_at", createdAt) // Echo request fields when present; fallback model from response modelVersion if reqJSON := pickRequestJSON(originalRequestRawJSON, requestRawJSON); len(reqJSON) > 0 { req := unwrapRequestRoot(gjson.ParseBytes(reqJSON)) if v := req.Get("instructions"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "instructions", v.String()) + resp, _ = sjson.SetBytes(resp, "instructions", v.String()) } if v := req.Get("max_output_tokens"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "max_output_tokens", v.Int()) + resp, _ = sjson.SetBytes(resp, "max_output_tokens", v.Int()) } if v := req.Get("max_tool_calls"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "max_tool_calls", v.Int()) + resp, _ = sjson.SetBytes(resp, "max_tool_calls", v.Int()) } if v := req.Get("model"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "model", v.String()) + resp, _ = sjson.SetBytes(resp, "model", v.String()) } else if v = root.Get("modelVersion"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "model", v.String()) + resp, _ = sjson.SetBytes(resp, "model", v.String()) } if v := req.Get("parallel_tool_calls"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "parallel_tool_calls", v.Bool()) + resp, _ = sjson.SetBytes(resp, "parallel_tool_calls", v.Bool()) } if v := req.Get("previous_response_id"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "previous_response_id", v.String()) + resp, _ = sjson.SetBytes(resp, "previous_response_id", v.String()) } if v := req.Get("prompt_cache_key"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "prompt_cache_key", v.String()) + resp, _ = sjson.SetBytes(resp, "prompt_cache_key", v.String()) } if v := req.Get("reasoning"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "reasoning", v.Value()) + resp, _ = sjson.SetBytes(resp, "reasoning", v.Value()) } if v := req.Get("safety_identifier"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "safety_identifier", v.String()) + resp, _ = sjson.SetBytes(resp, "safety_identifier", v.String()) } if v := req.Get("service_tier"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "service_tier", v.String()) + resp, _ = sjson.SetBytes(resp, "service_tier", v.String()) } if v := req.Get("store"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "store", v.Bool()) + resp, _ = sjson.SetBytes(resp, "store", v.Bool()) } if v := req.Get("temperature"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "temperature", v.Float()) + resp, _ = sjson.SetBytes(resp, "temperature", v.Float()) } if v := req.Get("text"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "text", v.Value()) + resp, _ = sjson.SetBytes(resp, "text", v.Value()) } if v := req.Get("tool_choice"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "tool_choice", v.Value()) + resp, _ = sjson.SetBytes(resp, "tool_choice", v.Value()) } if v := req.Get("tools"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "tools", v.Value()) + resp, _ = sjson.SetBytes(resp, "tools", v.Value()) } if v := req.Get("top_logprobs"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "top_logprobs", v.Int()) + resp, _ = sjson.SetBytes(resp, "top_logprobs", v.Int()) } if v := req.Get("top_p"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "top_p", v.Float()) + resp, _ = sjson.SetBytes(resp, "top_p", v.Float()) } if v := req.Get("truncation"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "truncation", v.String()) + resp, _ = sjson.SetBytes(resp, "truncation", v.String()) } if v := req.Get("user"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "user", v.Value()) + resp, _ = sjson.SetBytes(resp, "user", v.Value()) } if v := req.Get("metadata"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "metadata", v.Value()) + resp, _ = sjson.SetBytes(resp, "metadata", v.Value()) } } else if v := root.Get("modelVersion"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "model", v.String()) + resp, _ = sjson.SetBytes(resp, "model", v.String()) } // Build outputs from candidates[0].content.parts @@ -697,14 +697,14 @@ func ConvertGeminiResponseToOpenAIResponsesNonStream(_ context.Context, _ string args := fc.Get("args") callID := fmt.Sprintf("call_%x_%d", time.Now().UnixNano(), atomic.AddUint64(&funcCallIDCounter, 1)) itemJSON := `{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}` - itemJSON, _ = sjson.SetBytesM(itemJSON, "id", fmt.Sprintf("fc_%s", callID)) - itemJSON, _ = sjson.SetBytesM(itemJSON, "call_id", callID) - itemJSON, _ = sjson.SetBytesM(itemJSON, "name", name) + itemJSON, _ = sjson.SetBytes(itemJSON, "id", fmt.Sprintf("fc_%s", callID)) + itemJSON, _ = sjson.SetBytes(itemJSON, "call_id", callID) + itemJSON, _ = sjson.SetBytes(itemJSON, "name", name) argsStr := "" if args.Exists() { argsStr = args.Raw } - itemJSON, _ = sjson.SetBytesM(itemJSON, "arguments", argsStr) + itemJSON, _ = sjson.SetBytes(itemJSON, "arguments", argsStr) appendOutput(itemJSON) return true } @@ -716,11 +716,11 @@ func ConvertGeminiResponseToOpenAIResponsesNonStream(_ context.Context, _ string if reasoningText.Len() > 0 || reasoningEncrypted != "" { rid := strings.TrimPrefix(id, "resp_") itemJSON := `{"id":"","type":"reasoning","encrypted_content":""}` - itemJSON, _ = sjson.SetBytesM(itemJSON, "id", fmt.Sprintf("rs_%s", rid)) - itemJSON, _ = sjson.SetBytesM(itemJSON, "encrypted_content", reasoningEncrypted) + itemJSON, _ = sjson.SetBytes(itemJSON, "id", fmt.Sprintf("rs_%s", rid)) + itemJSON, _ = sjson.SetBytes(itemJSON, "encrypted_content", reasoningEncrypted) if reasoningText.Len() > 0 { summaryJSON := `{"type":"summary_text","text":""}` - summaryJSON, _ = sjson.SetBytesM(summaryJSON, "text", reasoningText.String()) + summaryJSON, _ = sjson.SetBytes(summaryJSON, "text", reasoningText.String()) itemJSON, _ = sjson.SetRaw(itemJSON, "summary", "[]") itemJSON, _ = sjson.SetRaw(itemJSON, "summary.-1", summaryJSON) } @@ -730,8 +730,8 @@ func ConvertGeminiResponseToOpenAIResponsesNonStream(_ context.Context, _ string // Assistant message output item if haveMessage { itemJSON := `{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}` - itemJSON, _ = sjson.SetBytesM(itemJSON, "id", fmt.Sprintf("msg_%s_0", strings.TrimPrefix(id, "resp_"))) - itemJSON, _ = sjson.SetBytesM(itemJSON, "content.0.text", messageText.String()) + itemJSON, _ = sjson.SetBytes(itemJSON, "id", fmt.Sprintf("msg_%s_0", strings.TrimPrefix(id, "resp_"))) + itemJSON, _ = sjson.SetBytes(itemJSON, "content.0.text", messageText.String()) appendOutput(itemJSON) } @@ -739,18 +739,18 @@ func ConvertGeminiResponseToOpenAIResponsesNonStream(_ context.Context, _ string if um := root.Get("usageMetadata"); um.Exists() { // input tokens = prompt + thoughts input := um.Get("promptTokenCount").Int() + um.Get("thoughtsTokenCount").Int() - resp, _ = sjson.SetBytesM(resp, "usage.input_tokens", input) + resp, _ = sjson.SetBytes(resp, "usage.input_tokens", input) // cached token details: align with OpenAI "cached_tokens" semantics. - resp, _ = sjson.SetBytesM(resp, "usage.input_tokens_details.cached_tokens", um.Get("cachedContentTokenCount").Int()) + resp, _ = sjson.SetBytes(resp, "usage.input_tokens_details.cached_tokens", um.Get("cachedContentTokenCount").Int()) // output tokens if v := um.Get("candidatesTokenCount"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "usage.output_tokens", v.Int()) + resp, _ = sjson.SetBytes(resp, "usage.output_tokens", v.Int()) } if v := um.Get("thoughtsTokenCount"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "usage.output_tokens_details.reasoning_tokens", v.Int()) + resp, _ = sjson.SetBytes(resp, "usage.output_tokens_details.reasoning_tokens", v.Int()) } if v := um.Get("totalTokenCount"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "usage.total_tokens", v.Int()) + resp, _ = sjson.SetBytes(resp, "usage.total_tokens", v.Int()) } } diff --git a/pkg/llmproxy/translator/openai/claude/openai_claude_request.go b/pkg/llmproxy/translator/openai/claude/openai_claude_request.go index 3eee32fdf4..72ee854d73 100644 --- a/pkg/llmproxy/translator/openai/claude/openai_claude_request.go +++ b/pkg/llmproxy/translator/openai/claude/openai_claude_request.go @@ -255,7 +255,7 @@ func ConvertClaudeRequestToOpenAI(modelName string, inputRawJSON []byte, stream } msgJSON, _ = sjson.SetRawBytes(msgJSON, "content", contentArrayJSON) - messagesJSON, _ = sjson.SetBytesM(messagesJSON, "-1", gjson.Parse(msgJSON).Value()) + messagesJSON, _ = sjson.SetBytes(messagesJSON, "-1", gjson.ParseBytes(msgJSON).Value()) } } diff --git a/pkg/llmproxy/translator/openai/claude/openai_claude_response.go b/pkg/llmproxy/translator/openai/claude/openai_claude_response.go index d80ad3cd14..eb088f6d9a 100644 --- a/pkg/llmproxy/translator/openai/claude/openai_claude_response.go +++ b/pkg/llmproxy/translator/openai/claude/openai_claude_response.go @@ -190,15 +190,15 @@ func convertOpenAIStreamingChunkToAnthropic(rawJSON []byte, param *ConvertOpenAI param.ThinkingContentBlockIndex = param.NextContentBlockIndex param.NextContentBlockIndex++ } - contentBlockStartJSON := `{"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}` - contentBlockStartJSON, _ = sjson.SetBytesM(contentBlockStartJSON, "index", param.ThinkingContentBlockIndex) + contentBlockStartJSON := []byte(`{"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}`) + contentBlockStartJSON, _ = sjson.SetBytes(contentBlockStartJSON, "index", param.ThinkingContentBlockIndex) results = append(results, "event: content_block_start\ndata: "+contentBlockStartJSON+"\n\n") param.ThinkingContentBlockStarted = true } - thinkingDeltaJSON := `{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":""}}` - thinkingDeltaJSON, _ = sjson.SetBytesM(thinkingDeltaJSON, "index", param.ThinkingContentBlockIndex) - thinkingDeltaJSON, _ = sjson.SetBytesM(thinkingDeltaJSON, "delta.thinking", reasoningText) + thinkingDeltaJSON := []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":""}}`) + thinkingDeltaJSON, _ = sjson.SetBytes(thinkingDeltaJSON, "index", param.ThinkingContentBlockIndex) + thinkingDeltaJSON, _ = sjson.SetBytes(thinkingDeltaJSON, "delta.thinking", reasoningText) results = append(results, "event: content_block_delta\ndata: "+thinkingDeltaJSON+"\n\n") } } @@ -213,15 +213,15 @@ func convertOpenAIStreamingChunkToAnthropic(rawJSON []byte, param *ConvertOpenAI param.TextContentBlockIndex = param.NextContentBlockIndex param.NextContentBlockIndex++ } - contentBlockStartJSON := `{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}` - contentBlockStartJSON, _ = sjson.SetBytesM(contentBlockStartJSON, "index", param.TextContentBlockIndex) + contentBlockStartJSON := []byte(`{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}`) + contentBlockStartJSON, _ = sjson.SetBytes(contentBlockStartJSON, "index", param.TextContentBlockIndex) results = append(results, "event: content_block_start\ndata: "+contentBlockStartJSON+"\n\n") param.TextContentBlockStarted = true } - contentDeltaJSON := `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":""}}` - contentDeltaJSON, _ = sjson.SetBytesM(contentDeltaJSON, "index", param.TextContentBlockIndex) - contentDeltaJSON, _ = sjson.SetBytesM(contentDeltaJSON, "delta.text", content.String()) + contentDeltaJSON := []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":""}}`) + contentDeltaJSON, _ = sjson.SetBytes(contentDeltaJSON, "index", param.TextContentBlockIndex) + contentDeltaJSON, _ = sjson.SetBytes(contentDeltaJSON, "delta.text", content.String()) results = append(results, "event: content_block_delta\ndata: "+contentDeltaJSON+"\n\n") // Accumulate content @@ -277,10 +277,10 @@ func convertOpenAIStreamingChunkToAnthropic(rawJSON []byte, param *ConvertOpenAI stopTextContentBlock(param, &results) // Send content_block_start for tool_use - contentBlockStartJSON := `{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}` - contentBlockStartJSON, _ = sjson.SetBytesM(contentBlockStartJSON, "index", blockIndex) - contentBlockStartJSON, _ = sjson.SetBytesM(contentBlockStartJSON, "content_block.id", accumulator.ID) - contentBlockStartJSON, _ = sjson.SetBytesM(contentBlockStartJSON, "content_block.name", accumulator.Name) + contentBlockStartJSON := []byte(`{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"","name":"","input":{}}}`) + contentBlockStartJSON, _ = sjson.SetBytes(contentBlockStartJSON, "index", blockIndex) + contentBlockStartJSON, _ = sjson.SetBytes(contentBlockStartJSON, "content_block.id", accumulator.ID) + contentBlockStartJSON, _ = sjson.SetBytes(contentBlockStartJSON, "content_block.name", accumulator.Name) results = append(results, "event: content_block_start\ndata: "+contentBlockStartJSON+"\n\n") } @@ -305,8 +305,8 @@ func convertOpenAIStreamingChunkToAnthropic(rawJSON []byte, param *ConvertOpenAI // Send content_block_stop for thinking content if needed if param.ThinkingContentBlockStarted { - contentBlockStopJSON := `{"type":"content_block_stop","index":0}` - contentBlockStopJSON, _ = sjson.SetBytesM(contentBlockStopJSON, "index", param.ThinkingContentBlockIndex) + contentBlockStopJSON := []byte(`{"type":"content_block_stop","index":0}`) + contentBlockStopJSON, _ = sjson.SetBytes(contentBlockStopJSON, "index", param.ThinkingContentBlockIndex) results = append(results, "event: content_block_stop\ndata: "+contentBlockStopJSON+"\n\n") param.ThinkingContentBlockStarted = false param.ThinkingContentBlockIndex = -1 @@ -323,14 +323,14 @@ func convertOpenAIStreamingChunkToAnthropic(rawJSON []byte, param *ConvertOpenAI // Send complete input_json_delta with all accumulated arguments if accumulator.Arguments.Len() > 0 { - inputDeltaJSON := `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` - inputDeltaJSON, _ = sjson.SetBytesM(inputDeltaJSON, "index", blockIndex) - inputDeltaJSON, _ = sjson.SetBytesM(inputDeltaJSON, "delta.partial_json", util.FixJSON(accumulator.Arguments.String())) + inputDeltaJSON := []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}`) + inputDeltaJSON, _ = sjson.SetBytes(inputDeltaJSON, "index", blockIndex) + inputDeltaJSON, _ = sjson.SetBytes(inputDeltaJSON, "delta.partial_json", util.FixJSON(accumulator.Arguments.String())) results = append(results, "event: content_block_delta\ndata: "+inputDeltaJSON+"\n\n") } - contentBlockStopJSON := `{"type":"content_block_stop","index":0}` - contentBlockStopJSON, _ = sjson.SetBytesM(contentBlockStopJSON, "index", blockIndex) + contentBlockStopJSON := []byte(`{"type":"content_block_stop","index":0}`) + contentBlockStopJSON, _ = sjson.SetBytes(contentBlockStopJSON, "index", blockIndex) results = append(results, "event: content_block_stop\ndata: "+contentBlockStopJSON+"\n\n") delete(param.ToolCallBlockIndexes, index) } @@ -348,19 +348,19 @@ func convertOpenAIStreamingChunkToAnthropic(rawJSON []byte, param *ConvertOpenAI if usage.Exists() && usage.Type != gjson.Null { inputTokens, outputTokens, cachedTokens = extractOpenAIUsage(usage) // Send message_delta with usage - messageDeltaJSON := `{"type":"message_delta","delta":{"stop_reason":"","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}` - messageDeltaJSON, _ = sjson.SetBytesM(messageDeltaJSON, "delta.stop_reason", mapOpenAIFinishReasonToAnthropic(param.FinishReason)) - messageDeltaJSON, _ = sjson.SetBytesM(messageDeltaJSON, "usage.input_tokens", inputTokens) - messageDeltaJSON, _ = sjson.SetBytesM(messageDeltaJSON, "usage.output_tokens", outputTokens) + messageDeltaJSON := []byte(`{"type":"message_delta","delta":{"stop_reason":"","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`) + messageDeltaJSON, _ = sjson.SetBytes(messageDeltaJSON, "delta.stop_reason", mapOpenAIFinishReasonToAnthropic(param.FinishReason)) + messageDeltaJSON, _ = sjson.SetBytes(messageDeltaJSON, "usage.input_tokens", inputTokens) + messageDeltaJSON, _ = sjson.SetBytes(messageDeltaJSON, "usage.output_tokens", outputTokens) if cachedTokens > 0 { - messageDeltaJSON, _ = sjson.SetBytesM(messageDeltaJSON, "usage.cache_read_input_tokens", cachedTokens) + messageDeltaJSON, _ = sjson.SetBytes(messageDeltaJSON, "usage.cache_read_input_tokens", cachedTokens) } if len(param.Annotations) > 0 { citations := make([]interface{}, len(param.Annotations)) for i, a := range param.Annotations { citations[i] = a } - messageDeltaJSON, _ = sjson.SetBytesM(messageDeltaJSON, "citations", citations) + messageDeltaJSON, _ = sjson.SetBytes(messageDeltaJSON, "citations", citations) } results = append(results, "event: message_delta\ndata: "+messageDeltaJSON+"\n\n") param.MessageDeltaSent = true @@ -378,8 +378,8 @@ func convertOpenAIDoneToAnthropic(param *ConvertOpenAIResponseToAnthropicParams) // Ensure all content blocks are stopped before final events if param.ThinkingContentBlockStarted { - contentBlockStopJSON := `{"type":"content_block_stop","index":0}` - contentBlockStopJSON, _ = sjson.SetBytesM(contentBlockStopJSON, "index", param.ThinkingContentBlockIndex) + contentBlockStopJSON := []byte(`{"type":"content_block_stop","index":0}`) + contentBlockStopJSON, _ = sjson.SetBytes(contentBlockStopJSON, "index", param.ThinkingContentBlockIndex) results = append(results, "event: content_block_stop\ndata: "+contentBlockStopJSON+"\n\n") param.ThinkingContentBlockStarted = false param.ThinkingContentBlockIndex = -1 @@ -393,14 +393,14 @@ func convertOpenAIDoneToAnthropic(param *ConvertOpenAIResponseToAnthropicParams) blockIndex := param.toolContentBlockIndex(index) if accumulator.Arguments.Len() > 0 { - inputDeltaJSON := `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}` - inputDeltaJSON, _ = sjson.SetBytesM(inputDeltaJSON, "index", blockIndex) - inputDeltaJSON, _ = sjson.SetBytesM(inputDeltaJSON, "delta.partial_json", util.FixJSON(accumulator.Arguments.String())) + inputDeltaJSON := []byte(`{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}`) + inputDeltaJSON, _ = sjson.SetBytes(inputDeltaJSON, "index", blockIndex) + inputDeltaJSON, _ = sjson.SetBytes(inputDeltaJSON, "delta.partial_json", util.FixJSON(accumulator.Arguments.String())) results = append(results, "event: content_block_delta\ndata: "+inputDeltaJSON+"\n\n") } - contentBlockStopJSON := `{"type":"content_block_stop","index":0}` - contentBlockStopJSON, _ = sjson.SetBytesM(contentBlockStopJSON, "index", blockIndex) + contentBlockStopJSON := []byte(`{"type":"content_block_stop","index":0}`) + contentBlockStopJSON, _ = sjson.SetBytes(contentBlockStopJSON, "index", blockIndex) results = append(results, "event: content_block_stop\ndata: "+contentBlockStopJSON+"\n\n") delete(param.ToolCallBlockIndexes, index) } @@ -409,8 +409,8 @@ func convertOpenAIDoneToAnthropic(param *ConvertOpenAIResponseToAnthropicParams) // If we haven't sent message_delta yet (no usage info was received), send it now if param.FinishReason != "" && !param.MessageDeltaSent { - messageDeltaJSON := `{"type":"message_delta","delta":{"stop_reason":"","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}` - messageDeltaJSON, _ = sjson.SetBytesM(messageDeltaJSON, "delta.stop_reason", mapOpenAIFinishReasonToAnthropic(param.FinishReason)) + messageDeltaJSON := []byte(`{"type":"message_delta","delta":{"stop_reason":"","stop_sequence":null},"usage":{"input_tokens":0,"output_tokens":0}}`) + messageDeltaJSON, _ = sjson.SetBytes(messageDeltaJSON, "delta.stop_reason", mapOpenAIFinishReasonToAnthropic(param.FinishReason)) results = append(results, "event: message_delta\ndata: "+messageDeltaJSON+"\n\n") param.MessageDeltaSent = true } @@ -424,9 +424,9 @@ func convertOpenAIDoneToAnthropic(param *ConvertOpenAIResponseToAnthropicParams) func convertOpenAINonStreamingToAnthropic(rawJSON []byte) []string { root := gjson.ParseBytes(rawJSON) - out := `{"id":"","type":"message","role":"assistant","model":"","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}` - out, _ = sjson.SetBytesM(out, "id", root.Get("id").String()) - out, _ = sjson.SetBytesM(out, "model", root.Get("model").String()) + out := []byte(`{"id":"","type":"message","role":"assistant","model":"","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}`) + out, _ = sjson.SetBytes(out, "id", root.Get("id").String()) + out, _ = sjson.SetBytes(out, "model", root.Get("model").String()) // Process message content and tool calls if choices := root.Get("choices"); choices.Exists() && choices.IsArray() && len(choices.Array()) > 0 { @@ -437,24 +437,24 @@ func convertOpenAINonStreamingToAnthropic(rawJSON []byte) []string { if reasoningText == "" { continue } - block := `{"type":"thinking","thinking":""}` - block, _ = sjson.SetBytesM(block, "thinking", reasoningText) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"thinking","thinking":""}`) + block, _ = sjson.SetBytes(block, "thinking", reasoningText) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } // Handle text content if content := choice.Get("message.content"); content.Exists() && content.String() != "" { - block := `{"type":"text","text":""}` - block, _ = sjson.SetBytesM(block, "text", content.String()) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"text","text":""}`) + block, _ = sjson.SetBytes(block, "text", content.String()) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } // Handle tool calls if toolCalls := choice.Get("message.tool_calls"); toolCalls.Exists() && toolCalls.IsArray() { toolCalls.ForEach(func(_, toolCall gjson.Result) bool { toolUseBlock := `{"type":"tool_use","id":"","name":"","input":{}}` - toolUseBlock, _ = sjson.SetBytesM(toolUseBlock, "id", toolCall.Get("id").String()) - toolUseBlock, _ = sjson.SetBytesM(toolUseBlock, "name", toolCall.Get("function.name").String()) + toolUseBlock, _ = sjson.SetBytes(toolUseBlock, "id", toolCall.Get("id").String()) + toolUseBlock, _ = sjson.SetBytes(toolUseBlock, "name", toolCall.Get("function.name").String()) argsStr := util.FixJSON(toolCall.Get("function.arguments").String()) if argsStr != "" && gjson.Valid(argsStr) { @@ -468,24 +468,24 @@ func convertOpenAINonStreamingToAnthropic(rawJSON []byte) []string { toolUseBlock, _ = sjson.SetRaw(toolUseBlock, "input", "{}") } - out, _ = sjson.SetRaw(out, "content.-1", toolUseBlock) + out, _ = sjson.SetRawBytes(out, "content.-1", toolUseBlock) return true }) } // Set stop reason if finishReason := choice.Get("finish_reason"); finishReason.Exists() { - out, _ = sjson.SetBytesM(out, "stop_reason", mapOpenAIFinishReasonToAnthropic(finishReason.String())) + out, _ = sjson.SetBytes(out, "stop_reason", mapOpenAIFinishReasonToAnthropic(finishReason.String())) } } // Set usage information if usage := root.Get("usage"); usage.Exists() { inputTokens, outputTokens, cachedTokens := extractOpenAIUsage(usage) - out, _ = sjson.SetBytesM(out, "usage.input_tokens", inputTokens) - out, _ = sjson.SetBytesM(out, "usage.output_tokens", outputTokens) + out, _ = sjson.SetBytes(out, "usage.input_tokens", inputTokens) + out, _ = sjson.SetBytes(out, "usage.output_tokens", outputTokens) if cachedTokens > 0 { - out, _ = sjson.SetBytesM(out, "usage.cache_read_input_tokens", cachedTokens) + out, _ = sjson.SetBytes(out, "usage.cache_read_input_tokens", cachedTokens) } } @@ -556,8 +556,8 @@ func stopThinkingContentBlock(param *ConvertOpenAIResponseToAnthropicParams, res if !param.ThinkingContentBlockStarted { return } - contentBlockStopJSON := `{"type":"content_block_stop","index":0}` - contentBlockStopJSON, _ = sjson.SetBytesM(contentBlockStopJSON, "index", param.ThinkingContentBlockIndex) + contentBlockStopJSON := []byte(`{"type":"content_block_stop","index":0}`) + contentBlockStopJSON, _ = sjson.SetBytes(contentBlockStopJSON, "index", param.ThinkingContentBlockIndex) *results = append(*results, "event: content_block_stop\ndata: "+contentBlockStopJSON+"\n\n") param.ThinkingContentBlockStarted = false param.ThinkingContentBlockIndex = -1 @@ -575,8 +575,8 @@ func stopTextContentBlock(param *ConvertOpenAIResponseToAnthropicParams, results if !param.TextContentBlockStarted { return } - contentBlockStopJSON := `{"type":"content_block_stop","index":0}` - contentBlockStopJSON, _ = sjson.SetBytesM(contentBlockStopJSON, "index", param.TextContentBlockIndex) + contentBlockStopJSON := []byte(`{"type":"content_block_stop","index":0}`) + contentBlockStopJSON, _ = sjson.SetBytes(contentBlockStopJSON, "index", param.TextContentBlockIndex) *results = append(*results, "event: content_block_stop\ndata: "+contentBlockStopJSON+"\n\n") param.TextContentBlockStarted = false param.TextContentBlockIndex = -1 @@ -597,9 +597,9 @@ func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, origina _ = requestRawJSON root := gjson.ParseBytes(rawJSON) - out := `{"id":"","type":"message","role":"assistant","model":"","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}` - out, _ = sjson.SetBytesM(out, "id", root.Get("id").String()) - out, _ = sjson.SetBytesM(out, "model", root.Get("model").String()) + out := []byte(`{"id":"","type":"message","role":"assistant","model":"","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}`) + out, _ = sjson.SetBytes(out, "id", root.Get("id").String()) + out, _ = sjson.SetBytes(out, "model", root.Get("model").String()) hasToolCall := false stopReasonSet := false @@ -608,7 +608,7 @@ func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, origina choice := choices.Array()[0] if finishReason := choice.Get("finish_reason"); finishReason.Exists() { - out, _ = sjson.SetBytesM(out, "stop_reason", mapOpenAIFinishReasonToAnthropic(finishReason.String())) + out, _ = sjson.SetBytes(out, "stop_reason", mapOpenAIFinishReasonToAnthropic(finishReason.String())) stopReasonSet = true } @@ -619,9 +619,9 @@ func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, origina if reasoningText == "" { continue } - block := `{"type":"thinking","thinking":""}` - block, _ = sjson.SetBytesM(block, "thinking", reasoningText) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"thinking","thinking":""}`) + block, _ = sjson.SetBytes(block, "thinking", reasoningText) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } } @@ -630,17 +630,17 @@ func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, origina if contentResult.IsArray() { for _, item := range contentResult.Array() { if item.Get("type").String() == "text" { - block := `{"type":"text","text":""}` - block, _ = sjson.SetBytesM(block, "text", item.Get("text").String()) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"text","text":""}`) + block, _ = sjson.SetBytes(block, "text", item.Get("text").String()) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } } } else if contentResult.Type == gjson.String { textContent := contentResult.String() if textContent != "" { - block := `{"type":"text","text":""}` - block, _ = sjson.SetBytesM(block, "text", textContent) - out, _ = sjson.SetRaw(out, "content.-1", block) + block := []byte(`{"type":"text","text":""}`) + block, _ = sjson.SetBytes(block, "text", textContent) + out, _ = sjson.SetRawBytes(out, "content.-1", block) } } } @@ -650,8 +650,8 @@ func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, origina toolCalls.ForEach(func(_, toolCall gjson.Result) bool { hasToolCall = true toolUseBlock := `{"type":"tool_use","id":"","name":"","input":{}}` - toolUseBlock, _ = sjson.SetBytesM(toolUseBlock, "id", toolCall.Get("id").String()) - toolUseBlock, _ = sjson.SetBytesM(toolUseBlock, "name", toolCall.Get("function.name").String()) + toolUseBlock, _ = sjson.SetBytes(toolUseBlock, "id", toolCall.Get("id").String()) + toolUseBlock, _ = sjson.SetBytes(toolUseBlock, "name", toolCall.Get("function.name").String()) argsStr := util.FixJSON(toolCall.Get("function.arguments").String()) if argsStr != "" && gjson.Valid(argsStr) { @@ -665,7 +665,7 @@ func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, origina toolUseBlock, _ = sjson.SetRaw(toolUseBlock, "input", "{}") } - out, _ = sjson.SetRaw(out, "content.-1", toolUseBlock) + out, _ = sjson.SetRawBytes(out, "content.-1", toolUseBlock) return true }) } @@ -687,25 +687,25 @@ func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, origina return true }) if len(citations) > 0 { - out, _ = sjson.SetBytesM(out, "citations", citations) + out, _ = sjson.SetBytes(out, "citations", citations) } } } if respUsage := root.Get("usage"); respUsage.Exists() { inputTokens, outputTokens, cachedTokens := extractOpenAIUsage(respUsage) - out, _ = sjson.SetBytesM(out, "usage.input_tokens", inputTokens) - out, _ = sjson.SetBytesM(out, "usage.output_tokens", outputTokens) + out, _ = sjson.SetBytes(out, "usage.input_tokens", inputTokens) + out, _ = sjson.SetBytes(out, "usage.output_tokens", outputTokens) if cachedTokens > 0 { - out, _ = sjson.SetBytesM(out, "usage.cache_read_input_tokens", cachedTokens) + out, _ = sjson.SetBytes(out, "usage.cache_read_input_tokens", cachedTokens) } } if !stopReasonSet { if hasToolCall { - out, _ = sjson.SetBytesM(out, "stop_reason", "tool_use") + out, _ = sjson.SetBytes(out, "stop_reason", "tool_use") } else { - out, _ = sjson.SetBytesM(out, "stop_reason", "end_turn") + out, _ = sjson.SetBytes(out, "stop_reason", "end_turn") } } diff --git a/pkg/llmproxy/translator/openai/gemini/openai_gemini_response.go b/pkg/llmproxy/translator/openai/gemini/openai_gemini_response.go index 18c617538a..7bbbb54aaa 100644 --- a/pkg/llmproxy/translator/openai/gemini/openai_gemini_response.go +++ b/pkg/llmproxy/translator/openai/gemini/openai_gemini_response.go @@ -234,7 +234,7 @@ func ConvertOpenAIResponseToGemini(_ context.Context, _ string, originalRequestR for i, a := range anns { citations[i] = a } - template, _ = sjson.SetBytesM(template, "candidates.0.groundingMetadata.citations", citations) + template, _ = sjson.SetBytes(template, "candidates.0.groundingMetadata.citations", citations) } // Add groundingMetadata if annotations were accumulated @@ -243,7 +243,7 @@ func ConvertOpenAIResponseToGemini(_ context.Context, _ string, originalRequestR for i, a := range anns { citations[i] = a } - template, _ = sjson.SetBytesM(template, "candidates.0.groundingMetadata.citations", citations) + template, _ = sjson.SetBytes(template, "candidates.0.groundingMetadata.citations", citations) } // If we have accumulated tool calls, output them now @@ -653,7 +653,7 @@ func ConvertOpenAIResponseToGeminiNonStream(_ context.Context, _ string, origina return true }) if len(citations) > 0 { - out, _ = sjson.SetBytesM(out, "candidates.0.groundingMetadata.citations", citations) + out, _ = sjson.SetBytes(out, "candidates.0.groundingMetadata.citations", citations) } } @@ -671,7 +671,7 @@ func ConvertOpenAIResponseToGeminiNonStream(_ context.Context, _ string, origina return true }) if len(citations) > 0 { - out, _ = sjson.SetBytesM(out, "candidates.0.groundingMetadata.citations", citations) + out, _ = sjson.SetBytes(out, "candidates.0.groundingMetadata.citations", citations) } } diff --git a/pkg/llmproxy/translator/openai/openai/responses/openai_openai-responses_response.go b/pkg/llmproxy/translator/openai/openai/responses/openai_openai-responses_response.go index 689cbabfb2..1ec0dcabf2 100644 --- a/pkg/llmproxy/translator/openai/openai/responses/openai_openai-responses_response.go +++ b/pkg/llmproxy/translator/openai/openai/responses/openai_openai-responses_response.go @@ -77,7 +77,7 @@ type oaiToResponsesState struct { // responseIDCounter provides a process-wide unique counter for synthesized response identifiers. var responseIDCounter uint64 -func emitRespEvent(event string, payload string) string { +func emitRespEvent(event string, payload []byte) string { return fmt.Sprintf("event: %s\ndata: %s", event, payload) } @@ -91,23 +91,23 @@ func emitCompletionEvents(st *oaiToResponsesState) []string { return st.Seq } - completed := `{"type":"response.completed","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"completed","background":false,"error":null}}` - completed, _ = sjson.SetBytesM(completed, "sequence_number", nextSeq()) - completed, _ = sjson.SetBytesM(completed, "response.id", st.ResponseID) - completed, _ = sjson.SetBytesM(completed, "response.created_at", st.Created) + completed := []byte(`{"type":"response.completed","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"completed","background":false,"error":null}}`) + completed, _ = sjson.SetBytes(completed, "sequence_number", nextSeq()) + completed, _ = sjson.SetBytes(completed, "response.id", st.ResponseID) + completed, _ = sjson.SetBytes(completed, "response.created_at", st.Created) if st.UsageSeen { - completed, _ = sjson.SetBytesM(completed, "response.usage.input_tokens", st.PromptTokens) - completed, _ = sjson.SetBytesM(completed, "response.usage.input_tokens_details.cached_tokens", st.CachedTokens) - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens", st.CompletionTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.input_tokens", st.PromptTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.input_tokens_details.cached_tokens", st.CachedTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens", st.CompletionTokens) if st.ReasoningTokens > 0 { - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens_details.reasoning_tokens", st.ReasoningTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens_details.reasoning_tokens", st.ReasoningTokens) } total := st.TotalTokens if total == 0 { total = st.PromptTokens + st.CompletionTokens } - completed, _ = sjson.SetBytesM(completed, "response.usage.total_tokens", total) + completed, _ = sjson.SetBytes(completed, "response.usage.total_tokens", total) } st.CompletionSent = true @@ -214,39 +214,39 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, st.StopSeen = false st.Annotations = make(map[int][]interface{}) // response.created - created := `{"type":"response.created","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"in_progress","background":false,"error":null,"output":[]}}` - created, _ = sjson.SetBytesM(created, "sequence_number", nextSeq()) - created, _ = sjson.SetBytesM(created, "response.id", st.ResponseID) - created, _ = sjson.SetBytesM(created, "response.created_at", st.Created) + created := []byte(`{"type":"response.created","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"in_progress","background":false,"error":null,"output":[]}}`) + created, _ = sjson.SetBytes(created, "sequence_number", nextSeq()) + created, _ = sjson.SetBytes(created, "response.id", st.ResponseID) + created, _ = sjson.SetBytes(created, "response.created_at", st.Created) out = append(out, emitRespEvent("response.created", created)) - inprog := `{"type":"response.in_progress","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"in_progress"}}` - inprog, _ = sjson.SetBytesM(inprog, "sequence_number", nextSeq()) - inprog, _ = sjson.SetBytesM(inprog, "response.id", st.ResponseID) - inprog, _ = sjson.SetBytesM(inprog, "response.created_at", st.Created) + inprog := []byte(`{"type":"response.in_progress","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"in_progress"}}`) + inprog, _ = sjson.SetBytes(inprog, "sequence_number", nextSeq()) + inprog, _ = sjson.SetBytes(inprog, "response.id", st.ResponseID) + inprog, _ = sjson.SetBytes(inprog, "response.created_at", st.Created) out = append(out, emitRespEvent("response.in_progress", inprog)) st.Started = true } stopReasoning := func(text string) { // Emit reasoning done events - textDone := `{"type":"response.reasoning_summary_text.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"text":""}` - textDone, _ = sjson.SetBytesM(textDone, "sequence_number", nextSeq()) - textDone, _ = sjson.SetBytesM(textDone, "item_id", st.ReasoningID) - textDone, _ = sjson.SetBytesM(textDone, "output_index", st.ReasoningIndex) - textDone, _ = sjson.SetBytesM(textDone, "text", text) + textDone := []byte(`{"type":"response.reasoning_summary_text.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"text":""}`) + textDone, _ = sjson.SetBytes(textDone, "sequence_number", nextSeq()) + textDone, _ = sjson.SetBytes(textDone, "item_id", st.ReasoningID) + textDone, _ = sjson.SetBytes(textDone, "output_index", st.ReasoningIndex) + textDone, _ = sjson.SetBytes(textDone, "text", text) out = append(out, emitRespEvent("response.reasoning_summary_text.done", textDone)) - partDone := `{"type":"response.reasoning_summary_part.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}` - partDone, _ = sjson.SetBytesM(partDone, "sequence_number", nextSeq()) - partDone, _ = sjson.SetBytesM(partDone, "item_id", st.ReasoningID) - partDone, _ = sjson.SetBytesM(partDone, "output_index", st.ReasoningIndex) - partDone, _ = sjson.SetBytesM(partDone, "part.text", text) + partDone := []byte(`{"type":"response.reasoning_summary_part.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}`) + partDone, _ = sjson.SetBytes(partDone, "sequence_number", nextSeq()) + partDone, _ = sjson.SetBytes(partDone, "item_id", st.ReasoningID) + partDone, _ = sjson.SetBytes(partDone, "output_index", st.ReasoningIndex) + partDone, _ = sjson.SetBytes(partDone, "part.text", text) out = append(out, emitRespEvent("response.reasoning_summary_part.done", partDone)) - outputItemDone := `{"type":"response.output_item.done","item":{"id":"","type":"reasoning","encrypted_content":"","summary":[{"type":"summary_text","text":""}]},"output_index":0,"sequence_number":0}` - outputItemDone, _ = sjson.SetBytesM(outputItemDone, "sequence_number", nextSeq()) - outputItemDone, _ = sjson.SetBytesM(outputItemDone, "item.id", st.ReasoningID) - outputItemDone, _ = sjson.SetBytesM(outputItemDone, "output_index", st.ReasoningIndex) - outputItemDone, _ = sjson.SetBytesM(outputItemDone, "item.summary.text", text) + outputItemDone := []byte(`{"type":"response.output_item.done","item":{"id":"","type":"reasoning","encrypted_content":"","summary":[{"type":"summary_text","text":""}]},"output_index":0,"sequence_number":0}`) + outputItemDone, _ = sjson.SetBytes(outputItemDone, "sequence_number", nextSeq()) + outputItemDone, _ = sjson.SetBytes(outputItemDone, "item.id", st.ReasoningID) + outputItemDone, _ = sjson.SetBytes(outputItemDone, "output_index", st.ReasoningIndex) + outputItemDone, _ = sjson.SetBytes(outputItemDone, "item.summary.text", text) out = append(out, emitRespEvent("response.output_item.done", outputItemDone)) st.Reasonings = append(st.Reasonings, oaiToResponsesStateReasoning{ReasoningID: st.ReasoningID, ReasoningData: text}) @@ -266,29 +266,29 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, st.ReasoningBuf.Reset() } if !st.MsgItemAdded[idx] { - item := `{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"in_progress","content":[],"role":"assistant"}}` - item, _ = sjson.SetBytesM(item, "sequence_number", nextSeq()) - item, _ = sjson.SetBytesM(item, "output_index", idx) - item, _ = sjson.SetBytesM(item, "item.id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) + item := []byte(`{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"in_progress","content":[],"role":"assistant"}}`) + item, _ = sjson.SetBytes(item, "sequence_number", nextSeq()) + item, _ = sjson.SetBytes(item, "output_index", idx) + item, _ = sjson.SetBytes(item, "item.id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) out = append(out, emitRespEvent("response.output_item.added", item)) st.MsgItemAdded[idx] = true } if !st.MsgContentAdded[idx] { - part := `{"type":"response.content_part.added","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}` - part, _ = sjson.SetBytesM(part, "sequence_number", nextSeq()) - part, _ = sjson.SetBytesM(part, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) - part, _ = sjson.SetBytesM(part, "output_index", idx) - part, _ = sjson.SetBytesM(part, "content_index", 0) + part := []byte(`{"type":"response.content_part.added","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}`) + part, _ = sjson.SetBytes(part, "sequence_number", nextSeq()) + part, _ = sjson.SetBytes(part, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) + part, _ = sjson.SetBytes(part, "output_index", idx) + part, _ = sjson.SetBytes(part, "content_index", 0) out = append(out, emitRespEvent("response.content_part.added", part)) st.MsgContentAdded[idx] = true } - msg := `{"type":"response.output_text.delta","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"delta":"","logprobs":[]}` - msg, _ = sjson.SetBytesM(msg, "sequence_number", nextSeq()) - msg, _ = sjson.SetBytesM(msg, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) - msg, _ = sjson.SetBytesM(msg, "output_index", idx) - msg, _ = sjson.SetBytesM(msg, "content_index", 0) - msg, _ = sjson.SetBytesM(msg, "delta", c.String()) + msg := []byte(`{"type":"response.output_text.delta","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"delta":"","logprobs":[]}`) + msg, _ = sjson.SetBytes(msg, "sequence_number", nextSeq()) + msg, _ = sjson.SetBytes(msg, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) + msg, _ = sjson.SetBytes(msg, "output_index", idx) + msg, _ = sjson.SetBytes(msg, "content_index", 0) + msg, _ = sjson.SetBytes(msg, "delta", c.String()) out = append(out, emitRespEvent("response.output_text.delta", msg)) // aggregate for response.output if st.MsgTextBuf[idx] == nil { @@ -318,24 +318,24 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, if st.ReasoningID == "" { st.ReasoningID = fmt.Sprintf("rs_%s_%d", st.ResponseID, idx) st.ReasoningIndex = idx - item := `{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"reasoning","status":"in_progress","summary":[]}}` - item, _ = sjson.SetBytesM(item, "sequence_number", nextSeq()) - item, _ = sjson.SetBytesM(item, "output_index", idx) - item, _ = sjson.SetBytesM(item, "item.id", st.ReasoningID) + item := []byte(`{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"reasoning","status":"in_progress","summary":[]}}`) + item, _ = sjson.SetBytes(item, "sequence_number", nextSeq()) + item, _ = sjson.SetBytes(item, "output_index", idx) + item, _ = sjson.SetBytes(item, "item.id", st.ReasoningID) out = append(out, emitRespEvent("response.output_item.added", item)) - part := `{"type":"response.reasoning_summary_part.added","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}` - part, _ = sjson.SetBytesM(part, "sequence_number", nextSeq()) - part, _ = sjson.SetBytesM(part, "item_id", st.ReasoningID) - part, _ = sjson.SetBytesM(part, "output_index", st.ReasoningIndex) + part := []byte(`{"type":"response.reasoning_summary_part.added","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}`) + part, _ = sjson.SetBytes(part, "sequence_number", nextSeq()) + part, _ = sjson.SetBytes(part, "item_id", st.ReasoningID) + part, _ = sjson.SetBytes(part, "output_index", st.ReasoningIndex) out = append(out, emitRespEvent("response.reasoning_summary_part.added", part)) } // Append incremental text to reasoning buffer st.ReasoningBuf.WriteString(rc.String()) - msg := `{"type":"response.reasoning_summary_text.delta","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"delta":""}` - msg, _ = sjson.SetBytesM(msg, "sequence_number", nextSeq()) - msg, _ = sjson.SetBytesM(msg, "item_id", st.ReasoningID) - msg, _ = sjson.SetBytesM(msg, "output_index", st.ReasoningIndex) - msg, _ = sjson.SetBytesM(msg, "delta", rc.String()) + msg := []byte(`{"type":"response.reasoning_summary_text.delta","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"delta":""}`) + msg, _ = sjson.SetBytes(msg, "sequence_number", nextSeq()) + msg, _ = sjson.SetBytes(msg, "item_id", st.ReasoningID) + msg, _ = sjson.SetBytes(msg, "output_index", st.ReasoningIndex) + msg, _ = sjson.SetBytes(msg, "delta", rc.String()) out = append(out, emitRespEvent("response.reasoning_summary_text.delta", msg)) } @@ -352,27 +352,27 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, if b := st.MsgTextBuf[idx]; b != nil { fullText = b.String() } - done := `{"type":"response.output_text.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"text":"","logprobs":[]}` - done, _ = sjson.SetBytesM(done, "sequence_number", nextSeq()) - done, _ = sjson.SetBytesM(done, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) - done, _ = sjson.SetBytesM(done, "output_index", idx) - done, _ = sjson.SetBytesM(done, "content_index", 0) - done, _ = sjson.SetBytesM(done, "text", fullText) + done := []byte(`{"type":"response.output_text.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"text":"","logprobs":[]}`) + done, _ = sjson.SetBytes(done, "sequence_number", nextSeq()) + done, _ = sjson.SetBytes(done, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) + done, _ = sjson.SetBytes(done, "output_index", idx) + done, _ = sjson.SetBytes(done, "content_index", 0) + done, _ = sjson.SetBytes(done, "text", fullText) out = append(out, emitRespEvent("response.output_text.done", done)) - partDone := `{"type":"response.content_part.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}` - partDone, _ = sjson.SetBytesM(partDone, "sequence_number", nextSeq()) - partDone, _ = sjson.SetBytesM(partDone, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) - partDone, _ = sjson.SetBytesM(partDone, "output_index", idx) - partDone, _ = sjson.SetBytesM(partDone, "content_index", 0) - partDone, _ = sjson.SetBytesM(partDone, "part.text", fullText) + partDone := []byte(`{"type":"response.content_part.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}`) + partDone, _ = sjson.SetBytes(partDone, "sequence_number", nextSeq()) + partDone, _ = sjson.SetBytes(partDone, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) + partDone, _ = sjson.SetBytes(partDone, "output_index", idx) + partDone, _ = sjson.SetBytes(partDone, "content_index", 0) + partDone, _ = sjson.SetBytes(partDone, "part.text", fullText) out = append(out, emitRespEvent("response.content_part.done", partDone)) - itemDone := `{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}}` - itemDone, _ = sjson.SetBytesM(itemDone, "sequence_number", nextSeq()) - itemDone, _ = sjson.SetBytesM(itemDone, "output_index", idx) - itemDone, _ = sjson.SetBytesM(itemDone, "item.id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) - itemDone, _ = sjson.SetBytesM(itemDone, "item.content.0.text", fullText) + itemDone := []byte(`{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}}`) + itemDone, _ = sjson.SetBytes(itemDone, "sequence_number", nextSeq()) + itemDone, _ = sjson.SetBytes(itemDone, "output_index", idx) + itemDone, _ = sjson.SetBytes(itemDone, "item.id", fmt.Sprintf("msg_%s_%d", st.ResponseID, idx)) + itemDone, _ = sjson.SetBytes(itemDone, "item.content.0.text", fullText) out = append(out, emitRespEvent("response.output_item.done", itemDone)) st.MsgItemDone[idx] = true } @@ -394,13 +394,13 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, } if shouldEmitItem && effectiveCallID != "" { - o := `{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"function_call","status":"in_progress","arguments":"","call_id":"","name":""}}` - o, _ = sjson.SetBytesM(o, "sequence_number", nextSeq()) - o, _ = sjson.SetBytesM(o, "output_index", idx) - o, _ = sjson.SetBytesM(o, "item.id", fmt.Sprintf("fc_%s", effectiveCallID)) - o, _ = sjson.SetBytesM(o, "item.call_id", effectiveCallID) + o := []byte(`{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"function_call","status":"in_progress","arguments":"","call_id":"","name":""}}`) + o, _ = sjson.SetBytes(o, "sequence_number", nextSeq()) + o, _ = sjson.SetBytes(o, "output_index", idx) + o, _ = sjson.SetBytes(o, "item.id", fmt.Sprintf("fc_%s", effectiveCallID)) + o, _ = sjson.SetBytes(o, "item.call_id", effectiveCallID) name := st.FuncNames[idx] - o, _ = sjson.SetBytesM(o, "item.name", name) + o, _ = sjson.SetBytes(o, "item.name", name) out = append(out, emitRespEvent("response.output_item.added", o)) } @@ -417,11 +417,11 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, refCallID = newCallID } if refCallID != "" { - ad := `{"type":"response.function_call_arguments.delta","sequence_number":0,"item_id":"","output_index":0,"delta":""}` - ad, _ = sjson.SetBytesM(ad, "sequence_number", nextSeq()) - ad, _ = sjson.SetBytesM(ad, "item_id", fmt.Sprintf("fc_%s", refCallID)) - ad, _ = sjson.SetBytesM(ad, "output_index", idx) - ad, _ = sjson.SetBytesM(ad, "delta", args.String()) + ad := []byte(`{"type":"response.function_call_arguments.delta","sequence_number":0,"item_id":"","output_index":0,"delta":""}`) + ad, _ = sjson.SetBytes(ad, "sequence_number", nextSeq()) + ad, _ = sjson.SetBytes(ad, "item_id", fmt.Sprintf("fc_%s", refCallID)) + ad, _ = sjson.SetBytes(ad, "output_index", idx) + ad, _ = sjson.SetBytes(ad, "delta", args.String()) out = append(out, emitRespEvent("response.function_call_arguments.delta", ad)) } st.FuncArgsBuf[idx].WriteString(args.String()) @@ -453,32 +453,32 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, if b := st.MsgTextBuf[i]; b != nil { fullText = b.String() } - done := `{"type":"response.output_text.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"text":"","logprobs":[]}` - done, _ = sjson.SetBytesM(done, "sequence_number", nextSeq()) - done, _ = sjson.SetBytesM(done, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) - done, _ = sjson.SetBytesM(done, "output_index", i) - done, _ = sjson.SetBytesM(done, "content_index", 0) - done, _ = sjson.SetBytesM(done, "text", fullText) + done := []byte(`{"type":"response.output_text.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"text":"","logprobs":[]}`) + done, _ = sjson.SetBytes(done, "sequence_number", nextSeq()) + done, _ = sjson.SetBytes(done, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) + done, _ = sjson.SetBytes(done, "output_index", i) + done, _ = sjson.SetBytes(done, "content_index", 0) + done, _ = sjson.SetBytes(done, "text", fullText) out = append(out, emitRespEvent("response.output_text.done", done)) - partDone := `{"type":"response.content_part.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}` - partDone, _ = sjson.SetBytesM(partDone, "sequence_number", nextSeq()) - partDone, _ = sjson.SetBytesM(partDone, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) - partDone, _ = sjson.SetBytesM(partDone, "output_index", i) - partDone, _ = sjson.SetBytesM(partDone, "content_index", 0) - partDone, _ = sjson.SetBytesM(partDone, "part.text", fullText) + partDone := []byte(`{"type":"response.content_part.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}`) + partDone, _ = sjson.SetBytes(partDone, "sequence_number", nextSeq()) + partDone, _ = sjson.SetBytes(partDone, "item_id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) + partDone, _ = sjson.SetBytes(partDone, "output_index", i) + partDone, _ = sjson.SetBytes(partDone, "content_index", 0) + partDone, _ = sjson.SetBytes(partDone, "part.text", fullText) if anns := st.Annotations[i]; len(anns) > 0 { - partDone, _ = sjson.SetBytesM(partDone, "part.annotations", anns) + partDone, _ = sjson.SetBytes(partDone, "part.annotations", anns) } out = append(out, emitRespEvent("response.content_part.done", partDone)) - itemDone := `{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}}` - itemDone, _ = sjson.SetBytesM(itemDone, "sequence_number", nextSeq()) - itemDone, _ = sjson.SetBytesM(itemDone, "output_index", i) - itemDone, _ = sjson.SetBytesM(itemDone, "item.id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) - itemDone, _ = sjson.SetBytesM(itemDone, "item.content.0.text", fullText) + itemDone := []byte(`{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}}`) + itemDone, _ = sjson.SetBytes(itemDone, "sequence_number", nextSeq()) + itemDone, _ = sjson.SetBytes(itemDone, "output_index", i) + itemDone, _ = sjson.SetBytes(itemDone, "item.id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) + itemDone, _ = sjson.SetBytes(itemDone, "item.content.0.text", fullText) if anns := st.Annotations[i]; len(anns) > 0 { - itemDone, _ = sjson.SetBytesM(itemDone, "item.content.0.annotations", anns) + itemDone, _ = sjson.SetBytes(itemDone, "item.content.0.annotations", anns) } out = append(out, emitRespEvent("response.output_item.done", itemDone)) st.MsgItemDone[i] = true @@ -513,101 +513,101 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, if b := st.FuncArgsBuf[i]; b != nil && b.Len() > 0 { args = b.String() } - fcDone := `{"type":"response.function_call_arguments.done","sequence_number":0,"item_id":"","output_index":0,"arguments":""}` - fcDone, _ = sjson.SetBytesM(fcDone, "sequence_number", nextSeq()) - fcDone, _ = sjson.SetBytesM(fcDone, "item_id", fmt.Sprintf("fc_%s", callID)) - fcDone, _ = sjson.SetBytesM(fcDone, "output_index", i) - fcDone, _ = sjson.SetBytesM(fcDone, "arguments", args) + fcDone := []byte(`{"type":"response.function_call_arguments.done","sequence_number":0,"item_id":"","output_index":0,"arguments":""}`) + fcDone, _ = sjson.SetBytes(fcDone, "sequence_number", nextSeq()) + fcDone, _ = sjson.SetBytes(fcDone, "item_id", fmt.Sprintf("fc_%s", callID)) + fcDone, _ = sjson.SetBytes(fcDone, "output_index", i) + fcDone, _ = sjson.SetBytes(fcDone, "arguments", args) out = append(out, emitRespEvent("response.function_call_arguments.done", fcDone)) - itemDone := `{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}}` - itemDone, _ = sjson.SetBytesM(itemDone, "sequence_number", nextSeq()) - itemDone, _ = sjson.SetBytesM(itemDone, "output_index", i) - itemDone, _ = sjson.SetBytesM(itemDone, "item.id", fmt.Sprintf("fc_%s", callID)) - itemDone, _ = sjson.SetBytesM(itemDone, "item.arguments", args) - itemDone, _ = sjson.SetBytesM(itemDone, "item.call_id", callID) - itemDone, _ = sjson.SetBytesM(itemDone, "item.name", st.FuncNames[i]) + itemDone := []byte(`{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}}`) + itemDone, _ = sjson.SetBytes(itemDone, "sequence_number", nextSeq()) + itemDone, _ = sjson.SetBytes(itemDone, "output_index", i) + itemDone, _ = sjson.SetBytes(itemDone, "item.id", fmt.Sprintf("fc_%s", callID)) + itemDone, _ = sjson.SetBytes(itemDone, "item.arguments", args) + itemDone, _ = sjson.SetBytes(itemDone, "item.call_id", callID) + itemDone, _ = sjson.SetBytes(itemDone, "item.name", st.FuncNames[i]) out = append(out, emitRespEvent("response.output_item.done", itemDone)) st.FuncItemDone[i] = true st.FuncArgsDone[i] = true } } - completed := `{"type":"response.completed","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"completed","background":false,"error":null}}` - completed, _ = sjson.SetBytesM(completed, "sequence_number", nextSeq()) - completed, _ = sjson.SetBytesM(completed, "response.id", st.ResponseID) - completed, _ = sjson.SetBytesM(completed, "response.created_at", st.Created) + completed := []byte(`{"type":"response.completed","sequence_number":0,"response":{"id":"","object":"response","created_at":0,"status":"completed","background":false,"error":null}}`) + completed, _ = sjson.SetBytes(completed, "sequence_number", nextSeq()) + completed, _ = sjson.SetBytes(completed, "response.id", st.ResponseID) + completed, _ = sjson.SetBytes(completed, "response.created_at", st.Created) // Inject original request fields into response as per docs/response.completed.json. reqRawJSON := pickRequestJSON(originalRequestRawJSON, requestRawJSON) if reqRawJSON != nil { req := gjson.ParseBytes(reqRawJSON) if v := req.Get("instructions"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.instructions", v.String()) + completed, _ = sjson.SetBytes(completed, "response.instructions", v.String()) } if v := req.Get("max_output_tokens"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.max_output_tokens", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.max_output_tokens", v.Int()) } if v := req.Get("max_tool_calls"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.max_tool_calls", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.max_tool_calls", v.Int()) } if v := req.Get("model"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.model", v.String()) + completed, _ = sjson.SetBytes(completed, "response.model", v.String()) } if v := req.Get("parallel_tool_calls"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.parallel_tool_calls", v.Bool()) + completed, _ = sjson.SetBytes(completed, "response.parallel_tool_calls", v.Bool()) } if v := req.Get("previous_response_id"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.previous_response_id", v.String()) + completed, _ = sjson.SetBytes(completed, "response.previous_response_id", v.String()) } if v := req.Get("prompt_cache_key"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.prompt_cache_key", v.String()) + completed, _ = sjson.SetBytes(completed, "response.prompt_cache_key", v.String()) } if v := req.Get("reasoning"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.reasoning", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.reasoning", v.Value()) } if v := req.Get("safety_identifier"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.safety_identifier", v.String()) + completed, _ = sjson.SetBytes(completed, "response.safety_identifier", v.String()) } if v := req.Get("service_tier"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.service_tier", v.String()) + completed, _ = sjson.SetBytes(completed, "response.service_tier", v.String()) } if v := req.Get("store"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.store", v.Bool()) + completed, _ = sjson.SetBytes(completed, "response.store", v.Bool()) } if v := req.Get("temperature"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.temperature", v.Float()) + completed, _ = sjson.SetBytes(completed, "response.temperature", v.Float()) } if v := req.Get("text"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.text", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.text", v.Value()) } if v := req.Get("tool_choice"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.tool_choice", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.tool_choice", v.Value()) } if v := req.Get("tools"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.tools", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.tools", v.Value()) } if v := req.Get("top_logprobs"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.top_logprobs", v.Int()) + completed, _ = sjson.SetBytes(completed, "response.top_logprobs", v.Int()) } if v := req.Get("top_p"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.top_p", v.Float()) + completed, _ = sjson.SetBytes(completed, "response.top_p", v.Float()) } if v := req.Get("truncation"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.truncation", v.String()) + completed, _ = sjson.SetBytes(completed, "response.truncation", v.String()) } if v := req.Get("user"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.user", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.user", v.Value()) } if v := req.Get("metadata"); v.Exists() { - completed, _ = sjson.SetBytesM(completed, "response.metadata", v.Value()) + completed, _ = sjson.SetBytes(completed, "response.metadata", v.Value()) } } // Build response.output using aggregated buffers outputsWrapper := `{"arr":[]}` if len(st.Reasonings) > 0 { for _, r := range st.Reasonings { - item := `{"id":"","type":"reasoning","summary":[{"type":"summary_text","text":""}]}` - item, _ = sjson.SetBytesM(item, "id", r.ReasoningID) - item, _ = sjson.SetBytesM(item, "summary.0.text", r.ReasoningData) + item := []byte(`{"id":"","type":"reasoning","summary":[{"type":"summary_text","text":""}]}`) + item, _ = sjson.SetBytes(item, "id", r.ReasoningID) + item, _ = sjson.SetBytes(item, "summary.0.text", r.ReasoningData) outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) } } @@ -629,9 +629,9 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, if b := st.MsgTextBuf[i]; b != nil { txt = b.String() } - item := `{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}` - item, _ = sjson.SetBytesM(item, "id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) - item, _ = sjson.SetBytesM(item, "content.0.text", txt) + item := []byte(`{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}`) + item, _ = sjson.SetBytes(item, "id", fmt.Sprintf("msg_%s_%d", st.ResponseID, i)) + item, _ = sjson.SetBytes(item, "content.0.text", txt) outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) } } @@ -655,29 +655,29 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, } callID := st.FuncCallIDs[i] name := st.FuncNames[i] - item := `{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}` - item, _ = sjson.SetBytesM(item, "id", fmt.Sprintf("fc_%s", callID)) - item, _ = sjson.SetBytesM(item, "arguments", args) - item, _ = sjson.SetBytesM(item, "call_id", callID) - item, _ = sjson.SetBytesM(item, "name", name) + item := []byte(`{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}`) + item, _ = sjson.SetBytes(item, "id", fmt.Sprintf("fc_%s", callID)) + item, _ = sjson.SetBytes(item, "arguments", args) + item, _ = sjson.SetBytes(item, "call_id", callID) + item, _ = sjson.SetBytes(item, "name", name) outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) } } if gjson.Get(outputsWrapper, "arr.#").Int() > 0 { - completed, _ = sjson.SetRaw(completed, "response.output", gjson.Get(outputsWrapper, "arr").Raw) + completed, _ = sjson.SetRawBytes(completed, "response.output", gjson.Get(outputsWrapper, "arr").Raw) } if st.UsageSeen { - completed, _ = sjson.SetBytesM(completed, "response.usage.input_tokens", st.PromptTokens) - completed, _ = sjson.SetBytesM(completed, "response.usage.input_tokens_details.cached_tokens", st.CachedTokens) - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens", st.CompletionTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.input_tokens", st.PromptTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.input_tokens_details.cached_tokens", st.CachedTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens", st.CompletionTokens) if st.ReasoningTokens > 0 { - completed, _ = sjson.SetBytesM(completed, "response.usage.output_tokens_details.reasoning_tokens", st.ReasoningTokens) + completed, _ = sjson.SetBytes(completed, "response.usage.output_tokens_details.reasoning_tokens", st.ReasoningTokens) } total := st.TotalTokens if total == 0 { total = st.PromptTokens + st.CompletionTokens } - completed, _ = sjson.SetBytesM(completed, "response.usage.total_tokens", total) + completed, _ = sjson.SetBytes(completed, "response.usage.total_tokens", total) } out = append(out, emitRespEvent("response.completed", completed)) st.CompletionSent = true @@ -696,96 +696,95 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream(_ context.Co root := unwrapOpenAIChatCompletionResult(gjson.ParseBytes(rawJSON)) // Basic response scaffold - resp := `{"id":"","object":"response","created_at":0,"status":"completed","background":false,"error":null,"incomplete_details":null}` - + resp := []byte(`{"id":"","object":"response","created_at":0,"status":"completed","background":false,"error":null,"incomplete_details":null}`) // id: use provider id if present, otherwise synthesize id := root.Get("id").String() if id == "" { id = fmt.Sprintf("resp_%x_%d", time.Now().UnixNano(), atomic.AddUint64(&responseIDCounter, 1)) } - resp, _ = sjson.SetBytesM(resp, "id", id) + resp, _ = sjson.SetBytes(resp, "id", id) // created_at: map from chat.completion created created := root.Get("created").Int() if created == 0 { created = time.Now().Unix() } - resp, _ = sjson.SetBytesM(resp, "created_at", created) + resp, _ = sjson.SetBytes(resp, "created_at", created) // Echo request fields when available (aligns with streaming path behavior) reqRawJSON := pickRequestJSON(originalRequestRawJSON, requestRawJSON) if reqRawJSON != nil { req := gjson.ParseBytes(reqRawJSON) if v := req.Get("instructions"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "instructions", v.String()) + resp, _ = sjson.SetBytes(resp, "instructions", v.String()) } if v := req.Get("max_output_tokens"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "max_output_tokens", v.Int()) + resp, _ = sjson.SetBytes(resp, "max_output_tokens", v.Int()) } else { // Also support max_tokens from chat completion style if v = req.Get("max_tokens"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "max_output_tokens", v.Int()) + resp, _ = sjson.SetBytes(resp, "max_output_tokens", v.Int()) } } if v := req.Get("max_tool_calls"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "max_tool_calls", v.Int()) + resp, _ = sjson.SetBytes(resp, "max_tool_calls", v.Int()) } if v := req.Get("model"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "model", v.String()) + resp, _ = sjson.SetBytes(resp, "model", v.String()) } else if v = root.Get("model"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "model", v.String()) + resp, _ = sjson.SetBytes(resp, "model", v.String()) } if v := req.Get("parallel_tool_calls"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "parallel_tool_calls", v.Bool()) + resp, _ = sjson.SetBytes(resp, "parallel_tool_calls", v.Bool()) } if v := req.Get("previous_response_id"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "previous_response_id", v.String()) + resp, _ = sjson.SetBytes(resp, "previous_response_id", v.String()) } if v := req.Get("prompt_cache_key"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "prompt_cache_key", v.String()) + resp, _ = sjson.SetBytes(resp, "prompt_cache_key", v.String()) } if v := req.Get("reasoning"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "reasoning", v.Value()) + resp, _ = sjson.SetBytes(resp, "reasoning", v.Value()) } if v := req.Get("safety_identifier"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "safety_identifier", v.String()) + resp, _ = sjson.SetBytes(resp, "safety_identifier", v.String()) } if v := req.Get("service_tier"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "service_tier", v.String()) + resp, _ = sjson.SetBytes(resp, "service_tier", v.String()) } if v := req.Get("store"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "store", v.Bool()) + resp, _ = sjson.SetBytes(resp, "store", v.Bool()) } if v := req.Get("temperature"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "temperature", v.Float()) + resp, _ = sjson.SetBytes(resp, "temperature", v.Float()) } if v := req.Get("text"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "text", v.Value()) + resp, _ = sjson.SetBytes(resp, "text", v.Value()) } if v := req.Get("tool_choice"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "tool_choice", v.Value()) + resp, _ = sjson.SetBytes(resp, "tool_choice", v.Value()) } if v := req.Get("tools"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "tools", v.Value()) + resp, _ = sjson.SetBytes(resp, "tools", v.Value()) } if v := req.Get("top_logprobs"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "top_logprobs", v.Int()) + resp, _ = sjson.SetBytes(resp, "top_logprobs", v.Int()) } if v := req.Get("top_p"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "top_p", v.Float()) + resp, _ = sjson.SetBytes(resp, "top_p", v.Float()) } if v := req.Get("truncation"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "truncation", v.String()) + resp, _ = sjson.SetBytes(resp, "truncation", v.String()) } if v := req.Get("user"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "user", v.Value()) + resp, _ = sjson.SetBytes(resp, "user", v.Value()) } if v := req.Get("metadata"); v.Exists() { - resp, _ = sjson.SetBytesM(resp, "metadata", v.Value()) + resp, _ = sjson.SetBytes(resp, "metadata", v.Value()) } } else if v := root.Get("model"); v.Exists() { // Fallback model from response - resp, _ = sjson.SetBytesM(resp, "model", v.String()) + resp, _ = sjson.SetBytes(resp, "model", v.String()) } // Build output list from choices[...] @@ -799,11 +798,11 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream(_ context.Co if includeReasoning { rid := strings.TrimPrefix(id, "resp_") // Prefer summary_text from reasoning_content; encrypted_content is optional - reasoningItem := `{"id":"","type":"reasoning","encrypted_content":"","summary":[]}` - reasoningItem, _ = sjson.SetBytesM(reasoningItem, "id", fmt.Sprintf("rs_%s", rid)) + reasoningItem := []byte(`{"id":"","type":"reasoning","encrypted_content":"","summary":[]}`) + reasoningItem, _ = sjson.SetBytes(reasoningItem, "id", fmt.Sprintf("rs_%s", rid)) if rcText != "" { - reasoningItem, _ = sjson.SetBytesM(reasoningItem, "summary.0.type", "summary_text") - reasoningItem, _ = sjson.SetBytesM(reasoningItem, "summary.0.text", rcText) + reasoningItem, _ = sjson.SetBytes(reasoningItem, "summary.0.type", "summary_text") + reasoningItem, _ = sjson.SetBytes(reasoningItem, "summary.0.text", rcText) } outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", reasoningItem) } @@ -814,9 +813,9 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream(_ context.Co if msg.Exists() { // Text message part if c := msg.Get("content"); c.Exists() && c.String() != "" { - item := `{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}` - item, _ = sjson.SetBytesM(item, "id", fmt.Sprintf("msg_%s_%d", id, int(choice.Get("index").Int()))) - item, _ = sjson.SetBytesM(item, "content.0.text", c.String()) + item := []byte(`{"id":"","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":""}],"role":"assistant"}`) + item, _ = sjson.SetBytes(item, "id", fmt.Sprintf("msg_%s_%d", id, int(choice.Get("index").Int()))) + item, _ = sjson.SetBytes(item, "content.0.text", c.String()) // Include annotations from message if present if anns := msg.Get("annotations"); anns.Exists() && anns.IsArray() { var annotations []interface{} @@ -831,7 +830,7 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream(_ context.Co return true }) if len(annotations) > 0 { - item, _ = sjson.SetBytesM(item, "content.0.annotations", annotations) + item, _ = sjson.SetBytes(item, "content.0.annotations", annotations) } } outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) @@ -843,11 +842,11 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream(_ context.Co callID := tc.Get("id").String() name := tc.Get("function.name").String() args := tc.Get("function.arguments").String() - item := `{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}` - item, _ = sjson.SetBytesM(item, "id", fmt.Sprintf("fc_%s", callID)) - item, _ = sjson.SetBytesM(item, "arguments", args) - item, _ = sjson.SetBytesM(item, "call_id", callID) - item, _ = sjson.SetBytesM(item, "name", name) + item := []byte(`{"id":"","type":"function_call","status":"completed","arguments":"","call_id":"","name":""}`) + item, _ = sjson.SetBytes(item, "id", fmt.Sprintf("fc_%s", callID)) + item, _ = sjson.SetBytes(item, "arguments", args) + item, _ = sjson.SetBytes(item, "call_id", callID) + item, _ = sjson.SetBytes(item, "name", name) outputsWrapper, _ = sjson.SetRaw(outputsWrapper, "arr.-1", item) return true }) @@ -857,26 +856,26 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream(_ context.Co }) } if gjson.Get(outputsWrapper, "arr.#").Int() > 0 { - resp, _ = sjson.SetRaw(resp, "output", gjson.Get(outputsWrapper, "arr").Raw) + resp, _ = sjson.SetRawBytes(resp, "output", gjson.Get(outputsWrapper, "arr").Raw) } // usage mapping if usage := root.Get("usage"); usage.Exists() { // Map common tokens if usage.Get("prompt_tokens").Exists() || usage.Get("completion_tokens").Exists() || usage.Get("total_tokens").Exists() { - resp, _ = sjson.SetBytesM(resp, "usage.input_tokens", usage.Get("prompt_tokens").Int()) + resp, _ = sjson.SetBytes(resp, "usage.input_tokens", usage.Get("prompt_tokens").Int()) if d := usage.Get("prompt_tokens_details.cached_tokens"); d.Exists() { - resp, _ = sjson.SetBytesM(resp, "usage.input_tokens_details.cached_tokens", d.Int()) + resp, _ = sjson.SetBytes(resp, "usage.input_tokens_details.cached_tokens", d.Int()) } - resp, _ = sjson.SetBytesM(resp, "usage.output_tokens", usage.Get("completion_tokens").Int()) + resp, _ = sjson.SetBytes(resp, "usage.output_tokens", usage.Get("completion_tokens").Int()) // Reasoning tokens not available in Chat Completions; set only if present under output_tokens_details if d := usage.Get("output_tokens_details.reasoning_tokens"); d.Exists() { - resp, _ = sjson.SetBytesM(resp, "usage.output_tokens_details.reasoning_tokens", d.Int()) + resp, _ = sjson.SetBytes(resp, "usage.output_tokens_details.reasoning_tokens", d.Int()) } - resp, _ = sjson.SetBytesM(resp, "usage.total_tokens", usage.Get("total_tokens").Int()) + resp, _ = sjson.SetBytes(resp, "usage.total_tokens", usage.Get("total_tokens").Int()) } else { // Fallback to raw usage object if structure differs - resp, _ = sjson.SetBytesM(resp, "usage", usage.Value()) + resp, _ = sjson.SetBytes(resp, "usage", usage.Value()) } } diff --git a/pkg/llmproxy/watcher/diff/diff_generated.go b/pkg/llmproxy/watcher/diff/diff_generated.go new file mode 100644 index 0000000000..7d5799ecf0 --- /dev/null +++ b/pkg/llmproxy/watcher/diff/diff_generated.go @@ -0,0 +1,46 @@ +// Code generated by github.com/kooshapari/CLIProxyAPI/v7/cmd/codegen; DO NOT EDIT. +package diff + +// Adapted from pkg/llmproxy/auth/diff/diff_generated.go (codegen output). + +import ( + "fmt" + "github.com/kooshapari/CLIProxyAPI/v7/pkg/llmproxy/config" +) + +// BuildConfigChangeDetailsGeneratedProviders computes changes for generated dedicated providers. +func BuildConfigChangeDetailsGeneratedProviders(oldCfg, newCfg *config.Config, changes *[]string) { + if len(oldCfg.MiniMaxKey) != len(newCfg.MiniMaxKey) { + *changes = append(*changes, fmt.Sprintf("minimax: count %d -> %d", len(oldCfg.MiniMaxKey), len(newCfg.MiniMaxKey))) + } + if len(oldCfg.RooKey) != len(newCfg.RooKey) { + *changes = append(*changes, fmt.Sprintf("roo: count %d -> %d", len(oldCfg.RooKey), len(newCfg.RooKey))) + } + if len(oldCfg.KiloKey) != len(newCfg.KiloKey) { + *changes = append(*changes, fmt.Sprintf("kilo: count %d -> %d", len(oldCfg.KiloKey), len(newCfg.KiloKey))) + } + if len(oldCfg.DeepSeekKey) != len(newCfg.DeepSeekKey) { + *changes = append(*changes, fmt.Sprintf("deepseek: count %d -> %d", len(oldCfg.DeepSeekKey), len(newCfg.DeepSeekKey))) + } + if len(oldCfg.GroqKey) != len(newCfg.GroqKey) { + *changes = append(*changes, fmt.Sprintf("groq: count %d -> %d", len(oldCfg.GroqKey), len(newCfg.GroqKey))) + } + if len(oldCfg.MistralKey) != len(newCfg.MistralKey) { + *changes = append(*changes, fmt.Sprintf("mistral: count %d -> %d", len(oldCfg.MistralKey), len(newCfg.MistralKey))) + } + if len(oldCfg.SiliconFlowKey) != len(newCfg.SiliconFlowKey) { + *changes = append(*changes, fmt.Sprintf("siliconflow: count %d -> %d", len(oldCfg.SiliconFlowKey), len(newCfg.SiliconFlowKey))) + } + if len(oldCfg.OpenRouterKey) != len(newCfg.OpenRouterKey) { + *changes = append(*changes, fmt.Sprintf("openrouter: count %d -> %d", len(oldCfg.OpenRouterKey), len(newCfg.OpenRouterKey))) + } + if len(oldCfg.TogetherKey) != len(newCfg.TogetherKey) { + *changes = append(*changes, fmt.Sprintf("together: count %d -> %d", len(oldCfg.TogetherKey), len(newCfg.TogetherKey))) + } + if len(oldCfg.FireworksKey) != len(newCfg.FireworksKey) { + *changes = append(*changes, fmt.Sprintf("fireworks: count %d -> %d", len(oldCfg.FireworksKey), len(newCfg.FireworksKey))) + } + if len(oldCfg.NovitaKey) != len(newCfg.NovitaKey) { + *changes = append(*changes, fmt.Sprintf("novita: count %d -> %d", len(oldCfg.NovitaKey), len(newCfg.NovitaKey))) + } +}