From 65b4e1ec6c797813f6ce4bf2d78d8ba207b3d24f Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 17 Jan 2026 04:12:29 +0800 Subject: [PATCH 1/3] feat(codex): enable instruction toggling and update role terminology - Added conditional logic for Codex instruction injection based on configuration. - Updated role terminology from "user" to "developer" for better alignment with context. --- .../codex/claude/codex_claude_request.go | 28 ++++++++++--------- .../codex/gemini/codex_gemini_request.go | 2 +- .../chat-completions/codex_openai_request.go | 8 ++++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/internal/translator/codex/claude/codex_claude_request.go b/internal/translator/codex/claude/codex_claude_request.go index a3157833..17f2f674 100644 --- a/internal/translator/codex/claude/codex_claude_request.go +++ b/internal/translator/codex/claude/codex_claude_request.go @@ -52,7 +52,7 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool) systemsResult := rootResult.Get("system") if systemsResult.IsArray() { systemResults := systemsResult.Array() - message := `{"type":"message","role":"user","content":[]}` + message := `{"type":"message","role":"developer","content":[]}` for i := 0; i < len(systemResults); i++ { systemResult := systemResults[i] systemTypeResult := systemResult.Get("type") @@ -245,21 +245,23 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool) template, _ = sjson.Set(template, "include", []string{"reasoning.encrypted_content"}) // Add a first message to ignore system instructions and ensure proper execution. - inputResult := gjson.Get(template, "input") - if inputResult.Exists() && inputResult.IsArray() { - inputResults := inputResult.Array() - newInput := "[]" - for i := 0; i < len(inputResults); i++ { - if i == 0 { - firstText := inputResults[i].Get("content.0.text") - firstInstructions := "EXECUTE ACCORDING TO THE FOLLOWING INSTRUCTIONS!!!" - if firstText.Exists() && firstText.String() != firstInstructions { - newInput, _ = sjson.SetRaw(newInput, "-1", `{"type":"message","role":"user","content":[{"type":"input_text","text":"EXECUTE ACCORDING TO THE FOLLOWING INSTRUCTIONS!!!"}]}`) + if misc.GetCodexInstructionsEnabled() { + inputResult := gjson.Get(template, "input") + if inputResult.Exists() && inputResult.IsArray() { + inputResults := inputResult.Array() + newInput := "[]" + for i := 0; i < len(inputResults); i++ { + if i == 0 { + firstText := inputResults[i].Get("content.0.text") + firstInstructions := "EXECUTE ACCORDING TO THE FOLLOWING INSTRUCTIONS!!!" + if firstText.Exists() && firstText.String() != firstInstructions { + newInput, _ = sjson.SetRaw(newInput, "-1", `{"type":"message","role":"user","content":[{"type":"input_text","text":"EXECUTE ACCORDING TO THE FOLLOWING INSTRUCTIONS!!!"}]}`) + } } + newInput, _ = sjson.SetRaw(newInput, "-1", inputResults[i].Raw) } - newInput, _ = sjson.SetRaw(newInput, "-1", inputResults[i].Raw) + template, _ = sjson.SetRaw(template, "input", newInput) } - template, _ = sjson.SetRaw(template, "input", newInput) } return []byte(template) diff --git a/internal/translator/codex/gemini/codex_gemini_request.go b/internal/translator/codex/gemini/codex_gemini_request.go index fe5c0a5f..d7d0a109 100644 --- a/internal/translator/codex/gemini/codex_gemini_request.go +++ b/internal/translator/codex/gemini/codex_gemini_request.go @@ -95,7 +95,7 @@ func ConvertGeminiRequestToCodex(modelName string, inputRawJSON []byte, _ bool) // System instruction -> as a user message with input_text parts sysParts := root.Get("system_instruction.parts") if sysParts.IsArray() { - msg := `{"type":"message","role":"user","content":[]}` + msg := `{"type":"message","role":"developer","content":[]}` arr := sysParts.Array() for i := 0; i < len(arr); i++ { p := arr[i] diff --git a/internal/translator/codex/openai/chat-completions/codex_openai_request.go b/internal/translator/codex/openai/chat-completions/codex_openai_request.go index b68d2792..40f56f88 100644 --- a/internal/translator/codex/openai/chat-completions/codex_openai_request.go +++ b/internal/translator/codex/openai/chat-completions/codex_openai_request.go @@ -33,7 +33,7 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b rawJSON := bytes.Clone(inputRawJSON) userAgent := misc.ExtractCodexUserAgent(rawJSON) // Start with empty JSON object - out := `{}` + out := `{"instructions":""}` // Stream must be set to true out, _ = sjson.Set(out, "stream", stream) @@ -98,7 +98,9 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b // Extract system instructions from first system message (string or text object) messages := gjson.GetBytes(rawJSON, "messages") _, instructions := misc.CodexInstructionsForModel(modelName, "", userAgent) - out, _ = sjson.Set(out, "instructions", instructions) + if misc.GetCodexInstructionsEnabled() { + out, _ = sjson.Set(out, "instructions", instructions) + } // if messages.IsArray() { // arr := messages.Array() // for i := 0; i < len(arr); i++ { @@ -141,7 +143,7 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b msg := `{}` msg, _ = sjson.Set(msg, "type", "message") if role == "system" { - msg, _ = sjson.Set(msg, "role", "user") + msg, _ = sjson.Set(msg, "role", "developer") } else { msg, _ = sjson.Set(msg, "role", role) } From 384578a88ccc489b02f4919fe86d85d5979b71f1 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 17 Jan 2026 04:44:09 +0800 Subject: [PATCH 2/3] feat(cliproxy, gemini): improve ID matching logic and enrich normalized model output - Enhanced ID matching in `cliproxy` by adding additional conditions to better handle ID equality cases. - Updated `gemini` handlers to include `displayName` and `description` in normalized models for enriched metadata. --- sdk/api/handlers/gemini/gemini_handlers.go | 8 ++++++-- sdk/cliproxy/service.go | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sdk/api/handlers/gemini/gemini_handlers.go b/sdk/api/handlers/gemini/gemini_handlers.go index c4a04c85..27d8d1f5 100644 --- a/sdk/api/handlers/gemini/gemini_handlers.go +++ b/sdk/api/handlers/gemini/gemini_handlers.go @@ -56,8 +56,12 @@ func (h *GeminiAPIHandler) GeminiModels(c *gin.Context) { for k, v := range model { normalizedModel[k] = v } - if name, ok := normalizedModel["name"].(string); ok && name != "" && !strings.HasPrefix(name, "models/") { - normalizedModel["name"] = "models/" + name + if name, ok := normalizedModel["name"].(string); ok && name != "" { + if !strings.HasPrefix(name, "models/") { + normalizedModel["name"] = "models/" + name + } + normalizedModel["displayName"] = name + normalizedModel["description"] = name } if _, ok := normalizedModel["supportedGenerationMethods"]; !ok { normalizedModel["supportedGenerationMethods"] = defaultMethods diff --git a/sdk/cliproxy/service.go b/sdk/cliproxy/service.go index 7a06ae78..5b343e49 100644 --- a/sdk/cliproxy/service.go +++ b/sdk/cliproxy/service.go @@ -1212,6 +1212,9 @@ func rewriteModelInfoName(name, oldID, newID string) string { if strings.EqualFold(oldID, newID) { return name } + if strings.EqualFold(trimmed, oldID) { + return newID + } if strings.HasSuffix(trimmed, "/"+oldID) { prefix := strings.TrimSuffix(trimmed, oldID) return prefix + newID From bc7167e9feb191096e25ce989e6e05d1008c6690 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 17 Jan 2026 05:05:24 +0800 Subject: [PATCH 3/3] feat(runtime): add model alias support and enhance payload rule matching - Introduced `payloadModelAliases` and `payloadModelCandidates` functions to support model aliases for improved flexibility. - Updated rule matching logic to handle multiple model candidates. - Refactored variable naming in executor to improve code clarity and consistency. --- .../runtime/executor/antigravity_executor.go | 21 +++-- internal/runtime/executor/payload_helpers.go | 80 ++++++++++++++++++- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/internal/runtime/executor/antigravity_executor.go b/internal/runtime/executor/antigravity_executor.go index 7ae50e39..47113cfc 100644 --- a/internal/runtime/executor/antigravity_executor.go +++ b/internal/runtime/executor/antigravity_executor.go @@ -517,8 +517,8 @@ func (e *AntigravityExecutor) convertStreamToNonStream(stream []byte) []byte { } if usageResult := responseNode.Get("usageMetadata"); usageResult.Exists() { usageRaw = usageResult.Raw - } else if usageResult := root.Get("usageMetadata"); usageResult.Exists() { - usageRaw = usageResult.Raw + } else if usageMetadataResult := root.Get("usageMetadata"); usageMetadataResult.Exists() { + usageRaw = usageMetadataResult.Raw } if partsResult := responseNode.Get("candidates.0.content.parts"); partsResult.IsArray() { @@ -642,7 +642,6 @@ func (e *AntigravityExecutor) ExecuteStream(ctx context.Context, auth *cliproxya err = errReq return nil, err } - httpResp, errDo := httpClient.Do(httpReq) if errDo != nil { recordAPIResponseError(ctx, e.cfg, errDo) @@ -1004,10 +1003,10 @@ func FetchAntigravityModels(ctx context.Context, auth *cliproxyauth.Auth, cfg *c case "chat_20706", "chat_23310", "gemini-2.5-flash-thinking", "gemini-3-pro-low", "gemini-2.5-pro": continue } - cfg := modelConfig[modelID] + modelCfg := modelConfig[modelID] modelName := modelID - if cfg != nil && cfg.Name != "" { - modelName = cfg.Name + if modelCfg != nil && modelCfg.Name != "" { + modelName = modelCfg.Name } modelInfo := ®istry.ModelInfo{ ID: modelID, @@ -1021,12 +1020,12 @@ func FetchAntigravityModels(ctx context.Context, auth *cliproxyauth.Auth, cfg *c Type: antigravityAuthType, } // Look up Thinking support from static config using upstream model name. - if cfg != nil { - if cfg.Thinking != nil { - modelInfo.Thinking = cfg.Thinking + if modelCfg != nil { + if modelCfg.Thinking != nil { + modelInfo.Thinking = modelCfg.Thinking } - if cfg.MaxCompletionTokens > 0 { - modelInfo.MaxCompletionTokens = cfg.MaxCompletionTokens + if modelCfg.MaxCompletionTokens > 0 { + modelInfo.MaxCompletionTokens = modelCfg.MaxCompletionTokens } } models = append(models, modelInfo) diff --git a/internal/runtime/executor/payload_helpers.go b/internal/runtime/executor/payload_helpers.go index b4e03c40..364e2ee9 100644 --- a/internal/runtime/executor/payload_helpers.go +++ b/internal/runtime/executor/payload_helpers.go @@ -25,6 +25,7 @@ func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, root string if model == "" { return payload } + candidates := payloadModelCandidates(cfg, model, protocol) out := payload source := original if len(source) == 0 { @@ -34,7 +35,7 @@ func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, root string // Apply default rules: first write wins per field across all matching rules. for i := range rules.Default { rule := &rules.Default[i] - if !payloadRuleMatchesModel(rule, model, protocol) { + if !payloadRuleMatchesModels(rule, protocol, candidates) { continue } for path, value := range rule.Params { @@ -59,7 +60,7 @@ func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, root string // Apply default raw rules: first write wins per field across all matching rules. for i := range rules.DefaultRaw { rule := &rules.DefaultRaw[i] - if !payloadRuleMatchesModel(rule, model, protocol) { + if !payloadRuleMatchesModels(rule, protocol, candidates) { continue } for path, value := range rule.Params { @@ -88,7 +89,7 @@ func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, root string // Apply override rules: last write wins per field across all matching rules. for i := range rules.Override { rule := &rules.Override[i] - if !payloadRuleMatchesModel(rule, model, protocol) { + if !payloadRuleMatchesModels(rule, protocol, candidates) { continue } for path, value := range rule.Params { @@ -106,7 +107,7 @@ func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, root string // Apply override raw rules: last write wins per field across all matching rules. for i := range rules.OverrideRaw { rule := &rules.OverrideRaw[i] - if !payloadRuleMatchesModel(rule, model, protocol) { + if !payloadRuleMatchesModels(rule, protocol, candidates) { continue } for path, value := range rule.Params { @@ -128,6 +129,18 @@ func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, root string return out } +func payloadRuleMatchesModels(rule *config.PayloadRule, protocol string, models []string) bool { + if rule == nil || len(models) == 0 { + return false + } + for _, model := range models { + if payloadRuleMatchesModel(rule, model, protocol) { + return true + } + } + return false +} + func payloadRuleMatchesModel(rule *config.PayloadRule, model, protocol string) bool { if rule == nil { return false @@ -150,6 +163,65 @@ func payloadRuleMatchesModel(rule *config.PayloadRule, model, protocol string) b return false } +func payloadModelCandidates(cfg *config.Config, model, protocol string) []string { + model = strings.TrimSpace(model) + if model == "" { + return nil + } + candidates := []string{model} + if cfg == nil { + return candidates + } + aliases := payloadModelAliases(cfg, model, protocol) + if len(aliases) == 0 { + return candidates + } + seen := map[string]struct{}{strings.ToLower(model): struct{}{}} + for _, alias := range aliases { + alias = strings.TrimSpace(alias) + if alias == "" { + continue + } + key := strings.ToLower(alias) + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + candidates = append(candidates, alias) + } + return candidates +} + +func payloadModelAliases(cfg *config.Config, model, protocol string) []string { + if cfg == nil { + return nil + } + model = strings.TrimSpace(model) + if model == "" { + return nil + } + channel := strings.ToLower(strings.TrimSpace(protocol)) + if channel == "" { + return nil + } + entries := cfg.OAuthModelAlias[channel] + if len(entries) == 0 { + return nil + } + aliases := make([]string, 0, 2) + for _, entry := range entries { + if !strings.EqualFold(strings.TrimSpace(entry.Name), model) { + continue + } + alias := strings.TrimSpace(entry.Alias) + if alias == "" { + continue + } + aliases = append(aliases, alias) + } + return aliases +} + // buildPayloadPath combines an optional root path with a relative parameter path. // When root is empty, the parameter path is used as-is. When root is non-empty, // the parameter path is treated as relative to root.