feat: introduce custom provider example and remove redundant debug logs

- Added `examples/custom-provider/main.go` showcasing custom executor and translator integration using the SDK.
- Removed redundant debug logs from translator modules to enhance code cleanliness.
- Updated SDK documentation with new usage and advanced examples.
- Expanded the management API with new endpoints, including request logging and GPT-5 Codex features.
This commit is contained in:
Luis Pater
2025-09-22 03:37:53 +08:00
parent d5ad5fab87
commit f81898c906
54 changed files with 907 additions and 101 deletions

View File

@@ -9,7 +9,6 @@ import (
"bytes"
. "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/claude/gemini"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -31,7 +30,6 @@ import (
// Returns:
// - []byte: The transformed request data in Claude Code API format
func ConvertGeminiCLIRequestToClaude(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertGeminiCLIRequestToClaude")
rawJSON := bytes.Clone(inputRawJSON)
modelResult := gjson.GetBytes(rawJSON, "model")

View File

@@ -8,7 +8,6 @@ import (
"context"
. "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/claude/gemini"
log "github.com/sirupsen/logrus"
"github.com/tidwall/sjson"
)
@@ -26,7 +25,6 @@ import (
// Returns:
// - []string: A slice of strings, each containing a Gemini-compatible JSON response wrapped in a response object
func ConvertClaudeResponseToGeminiCLI(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertClaudeResponseToGeminiCLI")
outputs := ConvertClaudeResponseToGemini(ctx, modelName, originalRequestRawJSON, requestRawJSON, rawJSON, param)
// Wrap each converted response in a "response" object to match Gemini CLI API structure
newOutputs := make([]string, 0)
@@ -51,7 +49,6 @@ func ConvertClaudeResponseToGeminiCLI(ctx context.Context, modelName string, ori
// Returns:
// - string: A Gemini-compatible JSON response wrapped in a response object
func ConvertClaudeResponseToGeminiCLINonStream(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) string {
log.Debug("ConvertClaudeResponseToGeminiCLINonStream")
strJSON := ConvertClaudeResponseToGeminiNonStream(ctx, modelName, originalRequestRawJSON, requestRawJSON, rawJSON, param)
// Wrap the converted response in a "response" object to match Gemini CLI API structure
json := `{"response": {}}`

View File

@@ -13,7 +13,6 @@ import (
"strings"
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -37,7 +36,6 @@ import (
// Returns:
// - []byte: The transformed request data in Claude Code API format
func ConvertGeminiRequestToClaude(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertGeminiRequestToClaude")
rawJSON := bytes.Clone(inputRawJSON)
// Base Claude Code API template with default max_tokens value
out := `{"model":"","max_tokens":32000,"messages":[]}`

View File

@@ -12,7 +12,6 @@ import (
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -54,7 +53,6 @@ type ConvertAnthropicResponseToGeminiParams struct {
// Returns:
// - []string: A slice of strings, each containing a Gemini-compatible JSON response
func ConvertClaudeResponseToGemini(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertClaudeResponseToGemini")
if *param == nil {
*param = &ConvertAnthropicResponseToGeminiParams{
Model: modelName,
@@ -323,7 +321,6 @@ func convertMapToJSON(m map[string]interface{}) string {
// Returns:
// - string: A Gemini-compatible JSON response containing all message content and metadata
func ConvertClaudeResponseToGeminiNonStream(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertClaudeResponseToGeminiNonStream")
// Base Gemini response template for non-streaming with default values
template := `{"candidates":[{"content":{"role":"model","parts":[]},"finishReason":"STOP"}],"usageMetadata":{"trafficType":"PROVISIONED_THROUGHPUT"},"modelVersion":"","createTime":"","responseId":""}`

View File

@@ -12,7 +12,6 @@ import (
"math/big"
"strings"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -35,7 +34,6 @@ import (
// Returns:
// - []byte: The transformed request data in Claude Code API format
func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertOpenAIRequestToClaude")
rawJSON := bytes.Clone(inputRawJSON)
// Base Claude Code API template with default max_tokens value

View File

@@ -13,7 +13,6 @@ import (
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -52,7 +51,6 @@ type ToolCallAccumulator struct {
// Returns:
// - []string: A slice of strings, each containing an OpenAI-compatible JSON response
func ConvertClaudeResponseToOpenAI(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertClaudeResponseToOpenAI")
if *param == nil {
*param = &ConvertAnthropicResponseToOpenAIParams{
CreatedAt: 0,
@@ -280,7 +278,6 @@ func mapAnthropicStopReasonToOpenAI(anthropicReason string) string {
// Returns:
// - string: An OpenAI-compatible JSON response containing all message content and metadata
func ConvertClaudeResponseToOpenAINonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertClaudeResponseToOpenAINonStream")
chunks := make([][]byte, 0)

View File

@@ -6,7 +6,6 @@ import (
"math/big"
"strings"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -22,7 +21,6 @@ import (
// - max_output_tokens -> max_tokens
// - stream passthrough via parameter
func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertOpenAIResponsesRequestToClaude")
rawJSON := bytes.Clone(inputRawJSON)
// Base Claude message payload

View File

@@ -8,7 +8,6 @@ import (
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -43,7 +42,6 @@ func emitEvent(event string, payload string) string {
// ConvertClaudeResponseToOpenAIResponses converts Claude SSE to OpenAI Responses SSE events.
func ConvertClaudeResponseToOpenAIResponses(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertClaudeResponseToOpenAIResponses")
if *param == nil {
*param = &claudeToResponsesState{FuncArgsBuf: make(map[int]*strings.Builder), FuncNames: make(map[int]string), FuncCallIDs: make(map[int]string)}
}
@@ -391,7 +389,6 @@ func ConvertClaudeResponseToOpenAIResponses(ctx context.Context, modelName strin
// ConvertClaudeResponseToOpenAIResponsesNonStream aggregates Claude SSE into a single OpenAI Responses JSON.
func ConvertClaudeResponseToOpenAIResponsesNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertClaudeResponseToOpenAIResponsesNonStream")
// Aggregate Claude SSE lines into a single OpenAI Responses JSON (non-stream)
// We follow the same aggregation logic as the streaming variant but produce
// one final object matching docs/out.json structure.

View File

@@ -12,7 +12,6 @@ import (
"strings"
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -36,7 +35,6 @@ import (
// Returns:
// - []byte: The transformed request data in internal client format
func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertClaudeRequestToCodex")
rawJSON := bytes.Clone(inputRawJSON)
template := `{"model":"","instructions":"","input":[]}`

View File

@@ -11,7 +11,6 @@ import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -37,7 +36,6 @@ var (
// Returns:
// - []string: A slice of strings, each containing a Claude Code-compatible JSON response
func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertCodexResponseToClaude")
if *param == nil {
hasToolCall := false
*param = &hasToolCall
@@ -179,7 +177,6 @@ func ConvertCodexResponseToClaude(_ context.Context, _ string, originalRequestRa
// Returns:
// - string: A Claude Code-compatible JSON response containing all message content and metadata
func ConvertCodexResponseToClaudeNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, _ []byte, _ *any) string {
log.Debug("ConvertCodexResponseToClaudeNonStream")
return ""
}

View File

@@ -9,7 +9,6 @@ import (
"bytes"
. "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/codex/gemini"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -31,7 +30,6 @@ import (
// Returns:
// - []byte: The transformed request data in Codex API format
func ConvertGeminiCLIRequestToCodex(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertGeminiRequestToCodex")
rawJSON := bytes.Clone(inputRawJSON)
rawJSON = []byte(gjson.GetBytes(rawJSON, "request").Raw)

View File

@@ -8,7 +8,6 @@ import (
"context"
. "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/codex/gemini"
log "github.com/sirupsen/logrus"
"github.com/tidwall/sjson"
)
@@ -26,7 +25,6 @@ import (
// Returns:
// - []string: A slice of strings, each containing a Gemini-compatible JSON response wrapped in a response object
func ConvertCodexResponseToGeminiCLI(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertCodexResponseToGeminiCLI")
outputs := ConvertCodexResponseToGemini(ctx, modelName, originalRequestRawJSON, requestRawJSON, rawJSON, param)
newOutputs := make([]string, 0)
for i := 0; i < len(outputs); i++ {
@@ -50,7 +48,6 @@ func ConvertCodexResponseToGeminiCLI(ctx context.Context, modelName string, orig
// Returns:
// - string: A Gemini-compatible JSON response wrapped in a response object
func ConvertCodexResponseToGeminiCLINonStream(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) string {
log.Debug("ConvertCodexResponseToGeminiCLINonStream")
// log.Debug(string(rawJSON))
strJSON := ConvertCodexResponseToGeminiNonStream(ctx, modelName, originalRequestRawJSON, requestRawJSON, rawJSON, param)
json := `{"response": {}}`

View File

@@ -15,7 +15,6 @@ import (
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -38,7 +37,6 @@ import (
// Returns:
// - []byte: The transformed request data in Codex API format
func ConvertGeminiRequestToCodex(modelName string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertGeminiRequestToCodex")
rawJSON := bytes.Clone(inputRawJSON)
// Base template
out := `{"model":"","instructions":"","input":[]}`

View File

@@ -11,7 +11,6 @@ import (
"encoding/json"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -42,7 +41,6 @@ type ConvertCodexResponseToGeminiParams struct {
// Returns:
// - []string: A slice of strings, each containing a Gemini-compatible JSON response
func ConvertCodexResponseToGemini(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertCodexResponseToGemini")
if *param == nil {
*param = &ConvertCodexResponseToGeminiParams{
Model: modelName,
@@ -154,7 +152,6 @@ func ConvertCodexResponseToGemini(_ context.Context, modelName string, originalR
// Returns:
// - string: A Gemini-compatible JSON response containing all message content and metadata
func ConvertCodexResponseToGeminiNonStream(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertCodexResponseToGeminiNonStream")
scanner := bufio.NewScanner(bytes.NewReader(rawJSON))
buffer := make([]byte, 10240*1024)
scanner.Buffer(buffer, 10240*1024)

View File

@@ -13,7 +13,6 @@ import (
"strings"
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -31,7 +30,6 @@ import (
// Returns:
// - []byte: The transformed request data in OpenAI Responses API format
func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertOpenAIRequestToCodex")
rawJSON := bytes.Clone(inputRawJSON)
// Start with empty JSON object
out := `{}`

View File

@@ -11,7 +11,6 @@ import (
"context"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -43,7 +42,6 @@ type ConvertCliToOpenAIParams struct {
// Returns:
// - []string: A slice of strings, each containing an OpenAI-compatible JSON response
func ConvertCodexResponseToOpenAI(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertCodexResponseToOpenAI")
if *param == nil {
*param = &ConvertCliToOpenAIParams{
Model: modelName,
@@ -168,7 +166,6 @@ func ConvertCodexResponseToOpenAI(_ context.Context, modelName string, originalR
// Returns:
// - string: An OpenAI-compatible JSON response containing all message content and metadata
func ConvertCodexResponseToOpenAINonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertCodexResponseToOpenAINonStream")
scanner := bufio.NewScanner(bytes.NewReader(rawJSON))
buffer := make([]byte, 10240*1024)
scanner.Buffer(buffer, 10240*1024)

View File

@@ -4,13 +4,11 @@ import (
"bytes"
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
func ConvertOpenAIResponsesRequestToCodex(modelName string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertOpenAIResponsesRequestToCodex")
rawJSON := bytes.Clone(inputRawJSON)
rawJSON, _ = sjson.SetBytes(rawJSON, "stream", true)

View File

@@ -6,7 +6,6 @@ import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -14,7 +13,6 @@ import (
// ConvertCodexResponseToOpenAIResponses converts OpenAI Chat Completions streaming chunks
// to OpenAI Responses SSE events (response.*).
func ConvertCodexResponseToOpenAIResponses(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertCodexResponseToOpenAIResponses")
if bytes.HasPrefix(rawJSON, []byte("data:")) {
rawJSON = bytes.TrimSpace(rawJSON[5:])
if typeResult := gjson.GetBytes(rawJSON, "type"); typeResult.Exists() {
@@ -31,7 +29,6 @@ func ConvertCodexResponseToOpenAIResponses(ctx context.Context, modelName string
// ConvertCodexResponseToOpenAIResponsesNonStream builds a single Responses JSON
// from a non-streaming OpenAI Chat Completions response.
func ConvertCodexResponseToOpenAIResponsesNonStream(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertCodexResponseToOpenAIResponsesNonStream")
scanner := bufio.NewScanner(bytes.NewReader(rawJSON))
buffer := make([]byte, 10240*1024)
scanner.Buffer(buffer, 10240*1024)

View File

@@ -12,7 +12,6 @@ import (
client "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -36,7 +35,6 @@ import (
// Returns:
// - []byte: The transformed request data in Gemini CLI API format
func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertClaudeRequestToCLI")
rawJSON := bytes.Clone(inputRawJSON)
var pathsToDelete []string
root := gjson.ParseBytes(rawJSON)

View File

@@ -12,7 +12,6 @@ import (
"fmt"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -43,7 +42,6 @@ type Params struct {
// Returns:
// - []string: A slice of strings, each containing a Claude Code-compatible JSON response
func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertGeminiCLIResponseToClaude")
if *param == nil {
*param = &Params{
HasFirstResponse: false,
@@ -254,6 +252,5 @@ func ConvertGeminiCLIResponseToClaude(_ context.Context, _ string, originalReque
// Returns:
// - string: A Claude-compatible JSON response.
func ConvertGeminiCLIResponseToClaudeNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, _ []byte, _ *any) string {
log.Debug("ConvertGeminiCLIResponseToClaudeNonStream")
return ""
}

View File

@@ -32,7 +32,6 @@ import (
// Returns:
// - []byte: The transformed request data in Gemini API format
func ConvertGeminiRequestToGeminiCLI(_ string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertGeminiRequestToGeminiCLI")
rawJSON := bytes.Clone(inputRawJSON)
template := ""
template = `{"project":"","request":{},"model":""}`

View File

@@ -8,7 +8,6 @@ package gemini
import (
"context"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -30,7 +29,6 @@ import (
// Returns:
// - []string: The transformed request data in Gemini API format
func ConvertGeminiCliRequestToGemini(ctx context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) []string {
log.Debug("ConvertGeminiCliRequestToGemini")
if alt, ok := ctx.Value("alt").(string); ok {
var chunk []byte
if alt == "" {
@@ -70,7 +68,6 @@ func ConvertGeminiCliRequestToGemini(ctx context.Context, _ string, originalRequ
// Returns:
// - string: A Gemini-compatible JSON response containing the response data
func ConvertGeminiCliRequestToGeminiNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertGeminiCliRequestToGeminiNonStream")
responseResult := gjson.GetBytes(rawJSON, "response")
if responseResult.Exists() {
return responseResult.Raw

View File

@@ -12,7 +12,6 @@ import (
"time"
. "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/openai/chat-completions"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -37,7 +36,6 @@ type convertCliResponseToOpenAIChatParams struct {
// Returns:
// - []string: A slice of strings, each containing an OpenAI-compatible JSON response
func ConvertCliResponseToOpenAI(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertCliResponseToOpenAI")
if *param == nil {
*param = &convertCliResponseToOpenAIChatParams{
UnixTimestamp: 0,
@@ -148,7 +146,6 @@ func ConvertCliResponseToOpenAI(_ context.Context, _ string, originalRequestRawJ
// Returns:
// - string: An OpenAI-compatible JSON response containing all message content and metadata
func ConvertCliResponseToOpenAINonStream(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) string {
log.Debug("ConvertCliResponseToOpenAINonStream")
responseResult := gjson.GetBytes(rawJSON, "response")
if responseResult.Exists() {
return ConvertGeminiResponseToOpenAINonStream(ctx, modelName, originalRequestRawJSON, requestRawJSON, []byte(responseResult.Raw), param)

View File

@@ -5,11 +5,9 @@ import (
. "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini-cli/gemini"
. "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/openai/responses"
log "github.com/sirupsen/logrus"
)
func ConvertOpenAIResponsesRequestToGeminiCLI(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertOpenAIResponsesRequestToGeminiCLI")
rawJSON := bytes.Clone(inputRawJSON)
rawJSON = ConvertOpenAIResponsesRequestToGemini(modelName, rawJSON, stream)
return ConvertGeminiRequestToGeminiCLI(modelName, rawJSON, stream)

View File

@@ -9,7 +9,6 @@ import (
)
func ConvertGeminiCLIResponseToOpenAIResponses(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertGeminiCLIResponseToOpenAIResponses")
responseResult := gjson.GetBytes(rawJSON, "response")
if responseResult.Exists() {
rawJSON = []byte(responseResult.Raw)
@@ -18,7 +17,6 @@ func ConvertGeminiCLIResponseToOpenAIResponses(ctx context.Context, modelName st
}
func ConvertGeminiCLIResponseToOpenAIResponsesNonStream(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) string {
log.Debug("ConvertGeminiCLIResponseToOpenAIResponsesNonStream")
responseResult := gjson.GetBytes(rawJSON, "response")
if responseResult.Exists() {
rawJSON = []byte(responseResult.Raw)

View File

@@ -12,7 +12,6 @@ import (
client "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -29,7 +28,6 @@ import (
// Returns:
// - []byte: The transformed request in Gemini CLI format.
func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertClaudeRequestToGemini")
rawJSON := bytes.Clone(inputRawJSON)
var pathsToDelete []string
root := gjson.ParseBytes(rawJSON)

View File

@@ -12,7 +12,6 @@ import (
"fmt"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -42,7 +41,6 @@ type Params struct {
// Returns:
// - []string: A slice of strings, each containing a Claude-compatible JSON response.
func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertGeminiResponseToClaude")
if *param == nil {
*param = &Params{
IsGlAPIKey: false,
@@ -248,6 +246,5 @@ func ConvertGeminiResponseToClaude(_ context.Context, _ string, originalRequestR
// Returns:
// - string: A Claude-compatible JSON response.
func ConvertGeminiResponseToClaudeNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, _ []byte, _ *any) string {
log.Debug("ConvertGeminiResponseToClaudeNonStream")
return ""
}

View File

@@ -17,7 +17,6 @@ import (
// It extracts the model name, system instruction, message contents, and tool declarations
// from the raw JSON request and returns them in the format expected by the internal client.
func ConvertGeminiCLIRequestToGemini(_ string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertGeminiCLIRequestToGemini")
rawJSON := bytes.Clone(inputRawJSON)
modelResult := gjson.GetBytes(rawJSON, "model")
rawJSON = []byte(gjson.GetBytes(rawJSON, "request").Raw)

View File

@@ -26,7 +26,6 @@ import (
// Returns:
// - []string: A slice of strings, each containing a Gemini CLI-compatible JSON response.
func ConvertGeminiResponseToGeminiCLI(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) []string {
log.Debug("ConvertGeminiResponseToGeminiCLI")
if bytes.Equal(rawJSON, []byte("[DONE]")) {
return []string{}
}
@@ -46,7 +45,6 @@ func ConvertGeminiResponseToGeminiCLI(_ context.Context, _ string, originalReque
// Returns:
// - string: A Gemini CLI-compatible JSON response.
func ConvertGeminiResponseToGeminiCLINonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertGeminiResponseToGeminiCLINonStream")
json := `{"response": {}}`
rawJSON, _ = sjson.SetRawBytes([]byte(json), "response", rawJSON)
return string(rawJSON)

View File

@@ -18,7 +18,6 @@ import (
//
// It keeps the payload otherwise unchanged.
func ConvertGeminiRequestToGemini(_ string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertClaudeRequestToGemini")
rawJSON := bytes.Clone(inputRawJSON)
// Fast path: if no contents field, return as-is
contents := gjson.GetBytes(rawJSON, "contents")

View File

@@ -9,8 +9,6 @@ import (
// PassthroughGeminiResponseStream forwards Gemini responses unchanged.
func PassthroughGeminiResponseStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) []string {
log.Debug("PassthroughGeminiResponseStream")
if bytes.HasPrefix(rawJSON, []byte("data:")) {
rawJSON = bytes.TrimSpace(rawJSON[5:])
}
@@ -24,6 +22,5 @@ func PassthroughGeminiResponseStream(_ context.Context, _ string, originalReques
// PassthroughGeminiResponseNonStream forwards Gemini responses unchanged.
func PassthroughGeminiResponseNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("PassthroughGeminiResponseNonStream")
return string(rawJSON)
}

View File

@@ -25,7 +25,6 @@ import (
// Returns:
// - []byte: The transformed request data in Gemini API format
func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertOpenAIRequestToGemini")
rawJSON := bytes.Clone(inputRawJSON)
// Base envelope
out := []byte(`{"contents":[],"generationConfig":{"thinkingConfig":{"include_thoughts":true}}}`)

View File

@@ -37,7 +37,6 @@ type convertGeminiResponseToOpenAIChatParams struct {
// Returns:
// - []string: A slice of strings, each containing an OpenAI-compatible JSON response
func ConvertGeminiResponseToOpenAI(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertGeminiResponseToOpenAI")
if *param == nil {
*param = &convertGeminiResponseToOpenAIChatParams{
UnixTimestamp: 0,
@@ -180,7 +179,6 @@ func ConvertGeminiResponseToOpenAI(_ context.Context, _ string, originalRequestR
// Returns:
// - string: An OpenAI-compatible JSON response containing all message content and metadata
func ConvertGeminiResponseToOpenAINonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertGeminiResponseToOpenAINonStream")
var unixTimestamp int64
template := `{"id":"","object":"chat.completion","created":123456,"model":"model","choices":[{"index":0,"message":{"role":"assistant","content":null,"reasoning_content":null,"tool_calls":null},"finish_reason":null,"native_finish_reason":null}]}`
if modelVersionResult := gjson.GetBytes(rawJSON, "modelVersion"); modelVersionResult.Exists() {

View File

@@ -10,7 +10,6 @@ import (
)
func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertOpenAIResponsesRequestToGemini")
rawJSON := bytes.Clone(inputRawJSON)
// Note: modelName and stream parameters are part of the fixed method signature

View File

@@ -7,7 +7,6 @@ import (
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -44,7 +43,6 @@ func emitEvent(event string, payload string) string {
// ConvertGeminiResponseToOpenAIResponses converts Gemini SSE chunks into OpenAI Responses SSE events.
func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertGeminiResponseToOpenAIResponses")
if *param == nil {
*param = &geminiToResponsesState{
FuncArgsBuf: make(map[int]*strings.Builder),
@@ -424,7 +422,6 @@ func ConvertGeminiResponseToOpenAIResponses(_ context.Context, modelName string,
// ConvertGeminiResponseToOpenAIResponsesNonStream aggregates Gemini response JSON into a single OpenAI Responses JSON object.
func ConvertGeminiResponseToOpenAIResponsesNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertGeminiResponseToOpenAIResponsesNonStream")
root := gjson.ParseBytes(rawJSON)
// Base response scaffold

View File

@@ -10,7 +10,6 @@ import (
"encoding/json"
"strings"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -19,7 +18,6 @@ import (
// It extracts the model name, system instruction, message contents, and tool declarations
// from the raw JSON request and returns them in the format expected by the OpenAI API.
func ConvertClaudeRequestToOpenAI(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertClaudeRequestToOpenAI")
rawJSON := bytes.Clone(inputRawJSON)
// Base OpenAI Chat Completions API template
out := `{"model":"","messages":[]}`

View File

@@ -59,7 +59,6 @@ type ToolCallAccumulator struct {
// Returns:
// - []string: A slice of strings, each containing an Anthropic-compatible JSON response.
func ConvertOpenAIResponseToClaude(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertOpenAIResponseToClaude")
if *param == nil {
*param = &ConvertOpenAIResponseToAnthropicParams{
MessageID: "",
@@ -453,6 +452,5 @@ func mapOpenAIFinishReasonToAnthropic(openAIReason string) string {
// Returns:
// - string: An Anthropic-compatible JSON response.
func ConvertOpenAIResponseToClaudeNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, _ []byte, _ *any) string {
log.Debug("ConvertOpenAIResponseToClaudeNonStream")
return ""
}

View File

@@ -18,7 +18,6 @@ import (
// It extracts the model name, generation config, message contents, and tool declarations
// from the raw JSON request and returns them in the format expected by the OpenAI API.
func ConvertGeminiCLIRequestToOpenAI(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertGeminiCLIRequestToOpenAI")
rawJSON := bytes.Clone(inputRawJSON)
rawJSON = []byte(gjson.GetBytes(rawJSON, "request").Raw)
rawJSON, _ = sjson.SetBytes(rawJSON, "model", modelName)

View File

@@ -26,7 +26,6 @@ import (
// Returns:
// - []string: A slice of strings, each containing a Gemini-compatible JSON response.
func ConvertOpenAIResponseToGeminiCLI(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertOpenAIResponseToGeminiCLI")
outputs := ConvertOpenAIResponseToGemini(ctx, modelName, originalRequestRawJSON, requestRawJSON, rawJSON, param)
newOutputs := make([]string, 0)
for i := 0; i < len(outputs); i++ {
@@ -48,7 +47,6 @@ func ConvertOpenAIResponseToGeminiCLI(ctx context.Context, modelName string, ori
// Returns:
// - string: A Gemini-compatible JSON response.
func ConvertOpenAIResponseToGeminiCLINonStream(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) string {
log.Debug("ConvertOpenAIResponseToGeminiCLINonStream")
strJSON := ConvertOpenAIResponseToGeminiNonStream(ctx, modelName, originalRequestRawJSON, requestRawJSON, rawJSON, param)
json := `{"response": {}}`
strJSON, _ = sjson.SetRaw(json, "response", strJSON)

View File

@@ -21,7 +21,6 @@ import (
// It extracts the model name, generation config, message contents, and tool declarations
// from the raw JSON request and returns them in the format expected by the OpenAI API.
func ConvertGeminiRequestToOpenAI(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertGeminiRequestToOpenAI")
rawJSON := bytes.Clone(inputRawJSON)
// Base OpenAI Chat Completions API template
out := `{"model":"","messages":[]}`

View File

@@ -12,7 +12,6 @@ import (
"strconv"
"strings"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -47,7 +46,6 @@ type ToolCallAccumulator struct {
// Returns:
// - []string: A slice of strings, each containing a Gemini-compatible JSON response.
func ConvertOpenAIResponseToGemini(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertOpenAIResponseToGemini")
if *param == nil {
*param = &ConvertOpenAIResponseToGeminiParams{
ToolCallsAccumulator: nil,
@@ -514,7 +512,6 @@ func tryParseNumber(s string) (interface{}, bool) {
// Returns:
// - string: A Gemini-compatible JSON response.
func ConvertOpenAIResponseToGeminiNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertOpenAIResponseToGeminiNonStream")
root := gjson.ParseBytes(rawJSON)
// Base Gemini response template

View File

@@ -4,8 +4,6 @@ package chat_completions
import (
"bytes"
log "github.com/sirupsen/logrus"
)
// ConvertOpenAIRequestToOpenAI converts an OpenAI Chat Completions request (raw JSON)
@@ -19,6 +17,5 @@ import (
// Returns:
// - []byte: The transformed request data in Gemini CLI API format
func ConvertOpenAIRequestToOpenAI(modelName string, inputRawJSON []byte, _ bool) []byte {
log.Debug("ConvertOpenAIRequestToOpenAI")
return bytes.Clone(inputRawJSON)
}

View File

@@ -27,7 +27,6 @@ import (
// Returns:
// - []string: A slice of strings, each containing an OpenAI-compatible JSON response
func ConvertOpenAIResponseToOpenAI(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertOpenAIResponseToOpenAI")
if bytes.HasPrefix(rawJSON, []byte("data:")) {
rawJSON = bytes.TrimSpace(rawJSON[5:])
}
@@ -51,6 +50,5 @@ func ConvertOpenAIResponseToOpenAI(_ context.Context, _ string, originalRequestR
// Returns:
// - string: An OpenAI-compatible JSON response containing all message content and metadata
func ConvertOpenAIResponseToOpenAINonStream(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) string {
log.Debug("ConvertOpenAIResponseToOpenAINonStream")
return string(rawJSON)
}

View File

@@ -28,7 +28,6 @@ import (
// Returns:
// - []byte: The transformed request data in OpenAI chat completions format
func ConvertOpenAIResponsesRequestToOpenAIChatCompletions(modelName string, inputRawJSON []byte, stream bool) []byte {
log.Debug("ConvertOpenAIResponsesRequestToOpenAIChatCompletions")
rawJSON := bytes.Clone(inputRawJSON)
// Base OpenAI chat completions template with default values
out := `{"model":"","messages":[],"stream":false}`

View File

@@ -7,7 +7,6 @@ import (
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -42,7 +41,6 @@ func emitRespEvent(event string, payload string) string {
// ConvertOpenAIChatCompletionsResponseToOpenAIResponses converts OpenAI Chat Completions streaming chunks
// to OpenAI Responses SSE events (response.*).
func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context, modelName string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string {
log.Debug("ConvertOpenAIChatCompletionsResponseToOpenAIResponses")
if *param == nil {
*param = &oaiToResponsesState{
FuncArgsBuf: make(map[int]*strings.Builder),
@@ -518,7 +516,6 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context,
// ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream builds a single Responses JSON
// from a non-streaming OpenAI Chat Completions response.
func ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
log.Debug("ConvertOpenAIChatCompletionsResponseToOpenAIResponsesNonStream")
root := gjson.ParseBytes(rawJSON)
// Basic response scaffold