From 5dc936a9a45b459eb6a2a950492f24a5b4f39f0f Mon Sep 17 00:00:00 2001 From: Skyuno Date: Wed, 28 Jan 2026 23:49:02 +0800 Subject: [PATCH 1/5] fix: filter out web_search/websearch tools unsupported by Kiro API --- .../translator/kiro/claude/kiro_claude_request.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/translator/kiro/claude/kiro_claude_request.go b/internal/translator/kiro/claude/kiro_claude_request.go index f92be9d5..6edbd3d0 100644 --- a/internal/translator/kiro/claude/kiro_claude_request.go +++ b/internal/translator/kiro/claude/kiro_claude_request.go @@ -17,7 +17,6 @@ import ( "github.com/tidwall/gjson" ) - // Kiro API request structs - field order determines JSON key order // KiroPayload is the top-level request structure for Kiro API @@ -34,7 +33,6 @@ type KiroInferenceConfig struct { TopP float64 `json:"topP,omitempty"` } - // KiroConversationState holds the conversation context type KiroConversationState struct { ChatTriggerType string `json:"chatTriggerType"` // Required: "MANUAL" - must be first field @@ -378,7 +376,6 @@ func hasThinkingTagInBody(body []byte) bool { return strings.Contains(bodyStr, "") || strings.Contains(bodyStr, "") } - // IsThinkingEnabledFromHeader checks if thinking mode is enabled via Anthropic-Beta header. // Claude CLI uses "Anthropic-Beta: interleaved-thinking-2025-05-14" to enable thinking. func IsThinkingEnabledFromHeader(headers http.Header) bool { @@ -518,6 +515,15 @@ func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { for _, tool := range tools.Array() { name := tool.Get("name").String() + + // Filter out web_search/websearch tools (Kiro API doesn't support them) + // This matches the behavior in AIClient-2-API/claude-kiro.js + nameLower := strings.ToLower(name) + if nameLower == "web_search" || nameLower == "websearch" { + log.Debugf("kiro: skipping unsupported tool: %s", name) + continue + } + description := tool.Get("description").String() inputSchemaResult := tool.Get("input_schema") var inputSchema interface{} From 3c7a5afdcca5e09e3ee9bea438e57bdd0d6c511d Mon Sep 17 00:00:00 2001 From: Skyuno Date: Sat, 31 Jan 2026 23:54:31 +0800 Subject: [PATCH 2/5] feat: inject web_search alternative hint instead of silently filtering --- .../kiro/claude/kiro_claude_request.go | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/internal/translator/kiro/claude/kiro_claude_request.go b/internal/translator/kiro/claude/kiro_claude_request.go index 6edbd3d0..c04ee939 100644 --- a/internal/translator/kiro/claude/kiro_claude_request.go +++ b/internal/translator/kiro/claude/kiro_claude_request.go @@ -217,7 +217,26 @@ func BuildKiroPayload(claudeBody []byte, modelID, profileArn, origin string, isA } // Convert Claude tools to Kiro format - kiroTools := convertClaudeToolsToKiro(tools) + kiroTools, hasWebSearch := convertClaudeToolsToKiro(tools) + + // If web_search was requested but filtered, inject alternative hint + if hasWebSearch { + webSearchHint := `[CRITICAL WEB ACCESS INSTRUCTION] +You have the Fetch/read_url_content tool available. When the user asks about current events, weather, news, or any information that requires web access: +- DO NOT say you cannot search the web +- DO NOT refuse to help with web-related queries +- IMMEDIATELY use the Fetch tool to access relevant URLs +- Use well-known official websites, documentation sites, or API endpoints +- Construct appropriate URLs based on the query context + +IMPORTANT: Always attempt to fetch information FIRST before declining. You CAN access the web via Fetch.` + if systemPrompt != "" { + systemPrompt += "\n" + webSearchHint + } else { + systemPrompt = webSearchHint + } + log.Infof("kiro: injected web_search alternative hint (tool was filtered)") + } // Thinking mode implementation: // Kiro API supports official thinking/reasoning mode via tag. @@ -506,11 +525,13 @@ func ensureKiroInputSchema(parameters interface{}) interface{} { } } -// convertClaudeToolsToKiro converts Claude tools to Kiro format -func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { +// convertClaudeToolsToKiro converts Claude tools to Kiro format. +// Returns the converted tools and a boolean indicating if web_search was filtered. +func convertClaudeToolsToKiro(tools gjson.Result) ([]KiroToolWrapper, bool) { var kiroTools []KiroToolWrapper + hasWebSearch := false if !tools.IsArray() { - return kiroTools + return kiroTools, hasWebSearch } for _, tool := range tools.Array() { @@ -521,6 +542,7 @@ func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { nameLower := strings.ToLower(name) if nameLower == "web_search" || nameLower == "websearch" { log.Debugf("kiro: skipping unsupported tool: %s", name) + hasWebSearch = true continue } @@ -567,7 +589,7 @@ func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { // This prevents 500 errors when Claude Code sends too many tools kiroTools = compressToolsIfNeeded(kiroTools) - return kiroTools + return kiroTools, hasWebSearch } // processMessages processes Claude messages and builds Kiro history From 95a3e32a1260e15de8968f7b7112e0404d5ab046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ccielhaidir=E2=80=9D?= <“phoenicchi@gmail.com”> Date: Mon, 2 Feb 2026 17:53:58 +0800 Subject: [PATCH 3/5] feat: add .air.toml configuration file and update .gitignore for build artifacts fix: improve PatchOAuthModelAlias logic for handling channel aliases feat: add support for GitHub Copilot in model definitions --- .air.toml | 46 +++++++++++++++++++ .gitignore | 2 + go.mod | 4 +- .../api/handlers/management/config_lists.go | 24 ++++++---- internal/registry/model_definitions.go | 3 ++ 5 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 .air.toml diff --git a/.air.toml b/.air.toml new file mode 100644 index 00000000..dc332411 --- /dev/null +++ b/.air.toml @@ -0,0 +1,46 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "./tmp/main" + cmd = "go build -o ./tmp/main ./cmd/server" + delay = 1000 + exclude_dir = ["assets", "tmp", "vendor", "testdata", "docs", ".github", "auths", "examples"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html", "yaml", "yml"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[screen] + clear_on_rebuild = false + keep_scroll = true \ No newline at end of file diff --git a/.gitignore b/.gitignore index bab49132..e45319bb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ logs/* conv/* temp/* refs/* +tmp/* +build-errors.log # Storage backends pgstore/* diff --git a/go.mod b/go.mod index 73b40e9b..78451bc4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.24.0 require ( github.com/andybalholm/brotli v1.0.6 github.com/fsnotify/fsnotify v1.9.0 + github.com/fxamacker/cbor/v2 v2.9.0 github.com/gin-gonic/gin v1.10.1 github.com/go-git/go-git/v6 v6.0.0-20251009132922-75a182125145 github.com/google/uuid v1.6.0 @@ -13,8 +14,8 @@ require ( github.com/joho/godotenv v1.5.1 github.com/klauspost/compress v1.17.4 github.com/minio/minio-go/v7 v7.0.66 - github.com/refraction-networking/utls v1.8.2 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c + github.com/refraction-networking/utls v1.8.2 github.com/sirupsen/logrus v1.9.3 github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 @@ -41,7 +42,6 @@ require ( github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-git/gcfg/v2 v2.0.2 // indirect diff --git a/internal/api/handlers/management/config_lists.go b/internal/api/handlers/management/config_lists.go index 4e0e0284..15ec3a24 100644 --- a/internal/api/handlers/management/config_lists.go +++ b/internal/api/handlers/management/config_lists.go @@ -754,18 +754,22 @@ func (h *Handler) PatchOAuthModelAlias(c *gin.Context) { normalizedMap := sanitizedOAuthModelAlias(map[string][]config.OAuthModelAlias{channel: body.Aliases}) normalized := normalizedMap[channel] if len(normalized) == 0 { + // Only delete if channel exists, otherwise just create empty entry + if h.cfg.OAuthModelAlias != nil { + if _, ok := h.cfg.OAuthModelAlias[channel]; ok { + delete(h.cfg.OAuthModelAlias, channel) + if len(h.cfg.OAuthModelAlias) == 0 { + h.cfg.OAuthModelAlias = nil + } + h.persist(c) + return + } + } + // Create new channel with empty aliases if h.cfg.OAuthModelAlias == nil { - c.JSON(404, gin.H{"error": "channel not found"}) - return - } - if _, ok := h.cfg.OAuthModelAlias[channel]; !ok { - c.JSON(404, gin.H{"error": "channel not found"}) - return - } - delete(h.cfg.OAuthModelAlias, channel) - if len(h.cfg.OAuthModelAlias) == 0 { - h.cfg.OAuthModelAlias = nil + h.cfg.OAuthModelAlias = make(map[string][]config.OAuthModelAlias) } + h.cfg.OAuthModelAlias[channel] = []config.OAuthModelAlias{} h.persist(c) return } diff --git a/internal/registry/model_definitions.go b/internal/registry/model_definitions.go index 7bf4aae2..954ecc7f 100644 --- a/internal/registry/model_definitions.go +++ b/internal/registry/model_definitions.go @@ -19,6 +19,7 @@ import ( // - codex // - qwen // - iflow +// - github-copilot // - antigravity (returns static overrides only) func GetStaticModelDefinitionsByChannel(channel string) []*ModelInfo { key := strings.ToLower(strings.TrimSpace(channel)) @@ -39,6 +40,8 @@ func GetStaticModelDefinitionsByChannel(channel string) []*ModelInfo { return GetQwenModels() case "iflow": return GetIFlowModels() + case "github-copilot": + return GetGitHubCopilotModels() case "antigravity": cfg := GetAntigravityModelConfig() if len(cfg) == 0 { From 250f212fa33f482ea3a94204b3ecc3ab8aa6efcb Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Tue, 3 Feb 2026 01:39:57 +0800 Subject: [PATCH 4/5] fix(executor): handle "global" location in AI platform URL generation --- internal/runtime/executor/gemini_vertex_executor.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/runtime/executor/gemini_vertex_executor.go b/internal/runtime/executor/gemini_vertex_executor.go index 83456a86..2db0e37c 100644 --- a/internal/runtime/executor/gemini_vertex_executor.go +++ b/internal/runtime/executor/gemini_vertex_executor.go @@ -1003,6 +1003,8 @@ func vertexBaseURL(location string) string { loc := strings.TrimSpace(location) if loc == "" { loc = "us-central1" + } else if loc == "global" { + return "https://aiplatform.googleapis.com" } return fmt.Sprintf("https://%s-aiplatform.googleapis.com", loc) } From b9cdc2f54cfce0564079d3ca62002aadd5d5ef66 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Tue, 3 Feb 2026 01:52:35 +0800 Subject: [PATCH 5/5] chore: remove `.air.toml` configuration file and update `.gitignore` --- .air.toml | 46 ---------------------------------------------- .gitignore | 1 - 2 files changed, 47 deletions(-) delete mode 100644 .air.toml diff --git a/.air.toml b/.air.toml deleted file mode 100644 index dc332411..00000000 --- a/.air.toml +++ /dev/null @@ -1,46 +0,0 @@ -root = "." -testdata_dir = "testdata" -tmp_dir = "tmp" - -[build] - args_bin = [] - bin = "./tmp/main" - cmd = "go build -o ./tmp/main ./cmd/server" - delay = 1000 - exclude_dir = ["assets", "tmp", "vendor", "testdata", "docs", ".github", "auths", "examples"] - exclude_file = [] - exclude_regex = ["_test.go"] - exclude_unchanged = false - follow_symlink = false - full_bin = "" - include_dir = [] - include_ext = ["go", "tpl", "tmpl", "html", "yaml", "yml"] - include_file = [] - kill_delay = "0s" - log = "build-errors.log" - poll = false - poll_interval = 0 - post_cmd = [] - pre_cmd = [] - rerun = false - rerun_delay = 500 - send_interrupt = false - stop_on_error = false - -[color] - app = "" - build = "yellow" - main = "magenta" - runner = "green" - watcher = "cyan" - -[log] - main_only = false - time = false - -[misc] - clean_on_exit = false - -[screen] - clear_on_rebuild = false - keep_scroll = true \ No newline at end of file diff --git a/.gitignore b/.gitignore index e45319bb..2b9c215a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ conv/* temp/* refs/* tmp/* -build-errors.log # Storage backends pgstore/*