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

@@ -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