@fudrouter/fsrouter 0.6.61 → 0.6.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/copyJs.js +13 -9
- package/open-sse/AGENTS.md +39 -0
- package/open-sse/config/anthropicHeaders.js +87 -0
- package/open-sse/config/appConstants.js +238 -0
- package/open-sse/config/audioRegistry.js +506 -0
- package/open-sse/config/cliFingerprints.js +304 -0
- package/open-sse/config/codexInstructions.js +119 -0
- package/open-sse/config/codexQuotaScopes.js +42 -0
- package/open-sse/config/constants.js +4 -0
- package/open-sse/config/contextEditing.js +68 -0
- package/open-sse/config/credentialLoader.js +83 -0
- package/open-sse/config/defaultThinkingSignature.js +12 -0
- package/open-sse/config/embeddingRegistry.js +264 -0
- package/open-sse/config/errorConfig.js +96 -0
- package/open-sse/config/freeModelCatalog.data.js +121 -0
- package/open-sse/config/freeModelCatalog.js +90 -0
- package/open-sse/config/freeTierCatalog.js +81 -0
- package/open-sse/config/googleTtsLanguages.js +62 -0
- package/open-sse/config/imageParamDefs.js +360 -0
- package/open-sse/config/imageRegistry.js +493 -0
- package/open-sse/config/kiroConstants.js +268 -0
- package/open-sse/config/mediaConfig.js +27 -0
- package/open-sse/config/mediaServiceKinds.js +71 -0
- package/open-sse/config/models.js +13 -0
- package/open-sse/config/moderationRegistry.js +71 -0
- package/open-sse/config/musicRegistry.js +99 -0
- package/open-sse/config/ocrRegistry.js +61 -0
- package/open-sse/config/ollamaModels.js +19 -0
- package/open-sse/config/providerErrorRules.js +114 -0
- package/open-sse/config/providerFieldStrips.js +35 -0
- package/open-sse/config/providerHeaderProfiles.js +191 -0
- package/open-sse/config/providerModels.js +106 -0
- package/open-sse/config/providerRegistry.js +16 -0
- package/open-sse/config/providers.js +19 -0
- package/open-sse/config/registryUtils.js +106 -0
- package/open-sse/config/rerankRegistry.js +177 -0
- package/open-sse/config/runtimeConfig.js +96 -0
- package/open-sse/config/runway.js +39 -0
- package/open-sse/config/searchRegistry.js +153 -0
- package/open-sse/config/toolCloaking.js +199 -0
- package/open-sse/config/ttsModels.js +129 -0
- package/open-sse/config/videoRegistry.js +207 -0
- package/open-sse/executors/adapta-web.js +435 -0
- package/open-sse/executors/antigravity.js +534 -0
- package/open-sse/executors/antigravityUpstreamError.js +13 -0
- package/open-sse/executors/auggie.js +437 -0
- package/open-sse/executors/azure-openai.js +32 -0
- package/open-sse/executors/azure.js +57 -0
- package/open-sse/executors/base.js +181 -0
- package/open-sse/executors/bedrock.js +633 -0
- package/open-sse/executors/blackbox-web.js +578 -0
- package/open-sse/executors/chatgpt-web.js +2042 -0
- package/open-sse/executors/chipotle.js +357 -0
- package/open-sse/executors/claude-web-with-auto-refresh.js +73 -0
- package/open-sse/executors/claude-web.js +586 -0
- package/open-sse/executors/cliproxyapi.js +296 -0
- package/open-sse/executors/cloudflare-ai.js +46 -0
- package/open-sse/executors/codebuddy-cn.js +40 -0
- package/open-sse/executors/codex.js +1324 -0
- package/open-sse/executors/commandCode.js +558 -0
- package/open-sse/executors/commandcode.js +88 -0
- package/open-sse/executors/copilot-m365-connection.js +176 -0
- package/open-sse/executors/copilot-m365-frames.js +192 -0
- package/open-sse/executors/copilot-m365-web.js +299 -0
- package/open-sse/executors/copilot-web.js +580 -0
- package/open-sse/executors/cursor.js +795 -0
- package/open-sse/executors/deepseek-web-with-auto-refresh.js +150 -0
- package/open-sse/executors/deepseek-web.js +875 -0
- package/open-sse/executors/default.js +430 -0
- package/open-sse/executors/devin-cli.js +351 -0
- package/open-sse/executors/doubao-web.js +550 -0
- package/open-sse/executors/duckduckgo-web.js +629 -0
- package/open-sse/executors/forceResponsesUpstream.js +33 -0
- package/open-sse/executors/gemini-business.js +320 -0
- package/open-sse/executors/gemini-cli.js +89 -0
- package/open-sse/executors/gemini-web.js +297 -0
- package/open-sse/executors/github.js +379 -0
- package/open-sse/executors/gitlab.js +569 -0
- package/open-sse/executors/glm.js +361 -0
- package/open-sse/executors/grok-cli.js +119 -0
- package/open-sse/executors/grok-web.js +345 -0
- package/open-sse/executors/huggingchat.js +467 -0
- package/open-sse/executors/iflow.js +108 -0
- package/open-sse/executors/index.js +78 -0
- package/open-sse/executors/inner-ai.js +527 -0
- package/open-sse/executors/kimchi.js +123 -0
- package/open-sse/executors/kimi-web.js +325 -0
- package/open-sse/executors/kimi.js +97 -0
- package/open-sse/executors/kiro.js +503 -0
- package/open-sse/executors/kiroThinking.js +44 -0
- package/open-sse/executors/lmarena.js +395 -0
- package/open-sse/executors/mimo-free.js +167 -0
- package/open-sse/executors/mimocode.js +392 -0
- package/open-sse/executors/ninerouter.js +155 -0
- package/open-sse/executors/nlpcloud.js +438 -0
- package/open-sse/executors/ollama-local.js +14 -0
- package/open-sse/executors/opencode-go.js +41 -0
- package/open-sse/executors/opencode.js +32 -0
- package/open-sse/executors/perplexity-web.js +581 -0
- package/open-sse/executors/poe-web.js +133 -0
- package/open-sse/executors/pollinations.js +95 -0
- package/open-sse/executors/puter.js +31 -0
- package/open-sse/executors/qoder.js +448 -0
- package/open-sse/executors/qwen-web.js +372 -0
- package/open-sse/executors/qwen.js +129 -0
- package/open-sse/executors/t3-chat-web.js +417 -0
- package/open-sse/executors/theoldllm.js +290 -0
- package/open-sse/executors/trae.js +418 -0
- package/open-sse/executors/v0-vercel-web.js +141 -0
- package/open-sse/executors/venice-web.js +142 -0
- package/open-sse/executors/vertex.js +131 -0
- package/open-sse/executors/vertexMedia.js +236 -0
- package/open-sse/executors/windsurf.js +560 -0
- package/open-sse/executors/xai.js +46 -0
- package/open-sse/executors/xiaomi-tokenplan.js +19 -0
- package/open-sse/executors/yuanbao-web.js +385 -0
- package/open-sse/executors/zed-hosted.js +281 -0
- package/open-sse/executors/zenmux-free.js +247 -0
- package/open-sse/handlers/audioSpeech.js +759 -0
- package/open-sse/handlers/audioTranscription.js +395 -0
- package/open-sse/handlers/audioTranslation.js +181 -0
- package/open-sse/handlers/chatCore/nonStreamingHandler.js +300 -0
- package/open-sse/handlers/chatCore/requestDetail.js +102 -0
- package/open-sse/handlers/chatCore/sseToJsonHandler.js +231 -0
- package/open-sse/handlers/chatCore/streamingHandler.js +95 -0
- package/open-sse/handlers/chatCore.js +404 -0
- package/open-sse/handlers/embeddingProviders/_base.js +4 -0
- package/open-sse/handlers/embeddingProviders/gemini.js +54 -0
- package/open-sse/handlers/embeddingProviders/index.js +23 -0
- package/open-sse/handlers/embeddingProviders/openai.js +39 -0
- package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +13 -0
- package/open-sse/handlers/embeddingsCore.js +126 -0
- package/open-sse/handlers/fetch/index.js +237 -0
- package/open-sse/handlers/imageGenerationCore.js +230 -0
- package/open-sse/handlers/imageProviders/_base.js +31 -0
- package/open-sse/handlers/imageProviders/antigravity.js +73 -0
- package/open-sse/handlers/imageProviders/blackForestLabs.js +63 -0
- package/open-sse/handlers/imageProviders/cloudflareAi.js +207 -0
- package/open-sse/handlers/imageProviders/codex.js +217 -0
- package/open-sse/handlers/imageProviders/comfyui.js +25 -0
- package/open-sse/handlers/imageProviders/falAi.js +62 -0
- package/open-sse/handlers/imageProviders/gemini.js +40 -0
- package/open-sse/handlers/imageProviders/huggingface.js +38 -0
- package/open-sse/handlers/imageProviders/index.js +44 -0
- package/open-sse/handlers/imageProviders/leonardo.js +435 -0
- package/open-sse/handlers/imageProviders/nanobanana.js +79 -0
- package/open-sse/handlers/imageProviders/openai.js +61 -0
- package/open-sse/handlers/imageProviders/runwayml.js +72 -0
- package/open-sse/handlers/imageProviders/sdwebui.js +36 -0
- package/open-sse/handlers/imageProviders/stabilityAi.js +59 -0
- package/open-sse/handlers/imageProviders/weavy.js +222 -0
- package/open-sse/handlers/moderations.js +92 -0
- package/open-sse/handlers/ocr.js +89 -0
- package/open-sse/handlers/rerank.js +223 -0
- package/open-sse/handlers/responseSanitizer/reasoning.js +144 -0
- package/open-sse/handlers/responsesHandler.js +99 -0
- package/open-sse/handlers/search/callers.js +371 -0
- package/open-sse/handlers/search/chatSearch.js +409 -0
- package/open-sse/handlers/search/index.js +201 -0
- package/open-sse/handlers/search/normalizers.js +223 -0
- package/open-sse/handlers/search.js +1056 -0
- package/open-sse/handlers/sseParser.js +469 -0
- package/open-sse/handlers/sttCore.js +193 -0
- package/open-sse/handlers/ttsCore.js +74 -0
- package/open-sse/handlers/ttsProviders/_base.js +39 -0
- package/open-sse/handlers/ttsProviders/edgeTts.js +89 -0
- package/open-sse/handlers/ttsProviders/elevenlabs.js +48 -0
- package/open-sse/handlers/ttsProviders/gemini.js +117 -0
- package/open-sse/handlers/ttsProviders/genericFormats.js +169 -0
- package/open-sse/handlers/ttsProviders/googleTts.js +54 -0
- package/open-sse/handlers/ttsProviders/index.js +50 -0
- package/open-sse/handlers/ttsProviders/localDevice.js +87 -0
- package/open-sse/handlers/ttsProviders/minimax.js +59 -0
- package/open-sse/handlers/ttsProviders/openai.js +30 -0
- package/open-sse/handlers/ttsProviders/openrouter.js +70 -0
- package/open-sse/handlers/usageExtractor.js +93 -0
- package/open-sse/handlers/videoGenerationCore.js +119 -0
- package/open-sse/handlers/videoProviders/index.js +22 -0
- package/open-sse/handlers/videoProviders/leonardo.js +511 -0
- package/open-sse/handlers/videoProviders/weavy.js +198 -0
- package/open-sse/handlers/webFetch.js +200 -0
- package/open-sse/index.js +79 -0
- package/open-sse/mcp-server/audit.js +283 -0
- package/open-sse/mcp-server/catalog.js +184 -0
- package/open-sse/mcp-server/descriptionCompressor.js +175 -0
- package/open-sse/mcp-server/httpAuthContext.js +32 -0
- package/open-sse/mcp-server/httpTransport.js +229 -0
- package/open-sse/mcp-server/index.js +33 -0
- package/open-sse/mcp-server/mcpCallerIdentity.js +21 -0
- package/open-sse/mcp-server/runtimeHeartbeat.js +110 -0
- package/open-sse/mcp-server/schemas/a2a.js +153 -0
- package/open-sse/mcp-server/schemas/audit.js +17 -0
- package/open-sse/mcp-server/schemas/index.js +166 -0
- package/open-sse/mcp-server/schemas/pickFastestModel.js +90 -0
- package/open-sse/mcp-server/schemas/toolDefinition.js +0 -0
- package/open-sse/mcp-server/schemas/toolSearch.js +32 -0
- package/open-sse/mcp-server/schemas/tools.js +1219 -0
- package/open-sse/mcp-server/scopeEnforcement.js +80 -0
- package/open-sse/mcp-server/server.js +1168 -0
- package/open-sse/mcp-server/toolCardinality.js +78 -0
- package/open-sse/mcp-server/toolSearch/catalog.js +66 -0
- package/open-sse/mcp-server/toolSearch/handler.js +20 -0
- package/open-sse/mcp-server/toolSearch/index.js +10 -0
- package/open-sse/mcp-server/toolSearch/register.js +21 -0
- package/open-sse/mcp-server/toolSearch/search.js +55 -0
- package/open-sse/mcp-server/toolSearch/signature.js +67 -0
- package/open-sse/mcp-server/tools/advancedTools.js +893 -0
- package/open-sse/mcp-server/tools/agentSkillTools.js +69 -0
- package/open-sse/mcp-server/tools/compressionTools.js +309 -0
- package/open-sse/mcp-server/tools/gamificationTools.js +134 -0
- package/open-sse/mcp-server/tools/githubSkillTools.js +102 -0
- package/open-sse/mcp-server/tools/memoryTools.js +118 -0
- package/open-sse/mcp-server/tools/notionTools.js +101 -0
- package/open-sse/mcp-server/tools/obsidianTools.js +307 -0
- package/open-sse/mcp-server/tools/pickFastestModel.js +223 -0
- package/open-sse/mcp-server/tools/pluginTools.js +181 -0
- package/open-sse/mcp-server/tools/poolTools.js +139 -0
- package/open-sse/mcp-server/tools/skillTools.js +113 -0
- package/open-sse/package.json +10 -0
- package/open-sse/providers/REGISTRY_TEMPLATE.js +98 -0
- package/open-sse/providers/capabilities.js +293 -0
- package/open-sse/providers/index.js +51 -0
- package/open-sse/providers/models/helpers.js +20 -0
- package/open-sse/providers/models/namePatterns.js +33 -0
- package/open-sse/providers/models/schema.js +40 -0
- package/open-sse/providers/pricing.js +308 -0
- package/open-sse/providers/registry/alicode-intl.js +30 -0
- package/open-sse/providers/registry/alicode.js +31 -0
- package/open-sse/providers/registry/anthropic.js +37 -0
- package/open-sse/providers/registry/antigravity.js +81 -0
- package/open-sse/providers/registry/assemblyai.js +38 -0
- package/open-sse/providers/registry/aws-polly.js +45 -0
- package/open-sse/providers/registry/azure.js +21 -0
- package/open-sse/providers/registry/black-forest-labs.js +32 -0
- package/open-sse/providers/registry/blackbox.js +41 -0
- package/open-sse/providers/registry/brave-search.js +35 -0
- package/open-sse/providers/registry/byteplus.js +35 -0
- package/open-sse/providers/registry/cartesia.js +36 -0
- package/open-sse/providers/registry/cerebras.js +31 -0
- package/open-sse/providers/registry/chutes.js +24 -0
- package/open-sse/providers/registry/claude.js +87 -0
- package/open-sse/providers/registry/cline.js +51 -0
- package/open-sse/providers/registry/clinepass.js +57 -0
- package/open-sse/providers/registry/cloudflare-ai.js +55 -0
- package/open-sse/providers/registry/codebuddy-cn.js +77 -0
- package/open-sse/providers/registry/codex.js +100 -0
- package/open-sse/providers/registry/cohere.js +27 -0
- package/open-sse/providers/registry/comfyui.js +20 -0
- package/open-sse/providers/registry/commandcode.js +43 -0
- package/open-sse/providers/registry/coqui.js +30 -0
- package/open-sse/providers/registry/cursor.js +58 -0
- package/open-sse/providers/registry/deepgram.js +33 -0
- package/open-sse/providers/registry/deepseek.js +51 -0
- package/open-sse/providers/registry/digitalocean.js +92 -0
- package/open-sse/providers/registry/edge-tts.js +24 -0
- package/open-sse/providers/registry/elevenlabs.js +35 -0
- package/open-sse/providers/registry/exa.js +50 -0
- package/open-sse/providers/registry/fal-ai.js +34 -0
- package/open-sse/providers/registry/featherless.js +34 -0
- package/open-sse/providers/registry/firecrawl.js +34 -0
- package/open-sse/providers/registry/fireworks.js +29 -0
- package/open-sse/providers/registry/gemini-cli.js +58 -0
- package/open-sse/providers/registry/gemini.js +83 -0
- package/open-sse/providers/registry/github.js +88 -0
- package/open-sse/providers/registry/gitlab.js +32 -0
- package/open-sse/providers/registry/glm-cn.js +35 -0
- package/open-sse/providers/registry/glm.js +58 -0
- package/open-sse/providers/registry/google-pse.js +35 -0
- package/open-sse/providers/registry/google-tts.js +24 -0
- package/open-sse/providers/registry/grok-cli.js +86 -0
- package/open-sse/providers/registry/grok-web.js +39 -0
- package/open-sse/providers/registry/groq.js +37 -0
- package/open-sse/providers/registry/huggingface.js +34 -0
- package/open-sse/providers/registry/hyperbolic.js +35 -0
- package/open-sse/providers/registry/ibm-bob.js +27 -0
- package/open-sse/providers/registry/iflow.js +52 -0
- package/open-sse/providers/registry/index.js +210 -0
- package/open-sse/providers/registry/inworld.js +36 -0
- package/open-sse/providers/registry/jina-ai.js +42 -0
- package/open-sse/providers/registry/jina-reader.js +34 -0
- package/open-sse/providers/registry/kilocode.js +51 -0
- package/open-sse/providers/registry/kimchi.js +49 -0
- package/open-sse/providers/registry/kimi-coding.js +65 -0
- package/open-sse/providers/registry/kimi.js +56 -0
- package/open-sse/providers/registry/kiro.js +115 -0
- package/open-sse/providers/registry/linkup.js +34 -0
- package/open-sse/providers/registry/local-device.js +24 -0
- package/open-sse/providers/registry/mimo-free.js +24 -0
- package/open-sse/providers/registry/minimax-cn.js +76 -0
- package/open-sse/providers/registry/minimax.js +83 -0
- package/open-sse/providers/registry/mistral.js +34 -0
- package/open-sse/providers/registry/mmf.js +19 -0
- package/open-sse/providers/registry/nanobanana.js +35 -0
- package/open-sse/providers/registry/nebius.js +27 -0
- package/open-sse/providers/registry/nvidia.js +43 -0
- package/open-sse/providers/registry/ollama-local.js +19 -0
- package/open-sse/providers/registry/ollama.js +36 -0
- package/open-sse/providers/registry/openai.js +84 -0
- package/open-sse/providers/registry/opencode-go.js +41 -0
- package/open-sse/providers/registry/opencode.js +25 -0
- package/open-sse/providers/registry/openrouter.js +60 -0
- package/open-sse/providers/registry/perplexity-agent.js +49 -0
- package/open-sse/providers/registry/perplexity-web.js +33 -0
- package/open-sse/providers/registry/perplexity.js +37 -0
- package/open-sse/providers/registry/playht.js +36 -0
- package/open-sse/providers/registry/qoder.js +54 -0
- package/open-sse/providers/registry/qwen.js +33 -0
- package/open-sse/providers/registry/recraft.js +24 -0
- package/open-sse/providers/registry/runwayml.js +30 -0
- package/open-sse/providers/registry/sdwebui.js +20 -0
- package/open-sse/providers/registry/searchapi.js +35 -0
- package/open-sse/providers/registry/searxng.js +35 -0
- package/open-sse/providers/registry/serper.js +35 -0
- package/open-sse/providers/registry/siliconflow.js +39 -0
- package/open-sse/providers/registry/stability-ai.js +31 -0
- package/open-sse/providers/registry/tavily.js +50 -0
- package/open-sse/providers/registry/together.js +31 -0
- package/open-sse/providers/registry/topaz.js +19 -0
- package/open-sse/providers/registry/tortoise.js +30 -0
- package/open-sse/providers/registry/venice.js +56 -0
- package/open-sse/providers/registry/vercel-ai-gateway.js +41 -0
- package/open-sse/providers/registry/vertex-partner.js +29 -0
- package/open-sse/providers/registry/vertex.js +32 -0
- package/open-sse/providers/registry/volcengine-ark.js +35 -0
- package/open-sse/providers/registry/voyage-ai.js +30 -0
- package/open-sse/providers/registry/xai.js +45 -0
- package/open-sse/providers/registry/xiaomi-mimo.js +46 -0
- package/open-sse/providers/registry/xiaomi-tokenplan.js +63 -0
- package/open-sse/providers/registry/youcom.js +35 -0
- package/open-sse/providers/schema.js +76 -0
- package/open-sse/providers/shared.js +74 -0
- package/open-sse/providers/thinkingLevels.js +48 -0
- package/open-sse/rtk/applyFilter.js +15 -0
- package/open-sse/rtk/autodetect.js +111 -0
- package/open-sse/rtk/caveman.js +100 -0
- package/open-sse/rtk/cavemanPrompts.js +78 -0
- package/open-sse/rtk/constants.js +55 -0
- package/open-sse/rtk/filters/buildOutput.js +127 -0
- package/open-sse/rtk/filters/dedupLog.js +44 -0
- package/open-sse/rtk/filters/find.js +50 -0
- package/open-sse/rtk/filters/gitDiff.js +92 -0
- package/open-sse/rtk/filters/gitLog.js +99 -0
- package/open-sse/rtk/filters/gitStatus.js +117 -0
- package/open-sse/rtk/filters/grep.js +48 -0
- package/open-sse/rtk/filters/ls.js +79 -0
- package/open-sse/rtk/filters/readNumbered.js +27 -0
- package/open-sse/rtk/filters/searchList.js +52 -0
- package/open-sse/rtk/filters/smartTruncate.js +15 -0
- package/open-sse/rtk/filters/tree.js +32 -0
- package/open-sse/rtk/headroom.js +215 -0
- package/open-sse/rtk/index.js +155 -0
- package/open-sse/rtk/paleo.js +47 -0
- package/open-sse/rtk/ponytail.js +9 -0
- package/open-sse/rtk/ponytailPrompt.js +52 -0
- package/open-sse/rtk/ponytailPrompts.js +76 -0
- package/open-sse/rtk/pxpipe.js +104 -0
- package/open-sse/rtk/registry.js +38 -0
- package/open-sse/rtk/systemInject.js +98 -0
- package/open-sse/services/accountFallback.js +323 -0
- package/open-sse/services/antigravityHeaders.js +91 -0
- package/open-sse/services/antigravityVersion.js +154 -0
- package/open-sse/services/clinepassModels.js +63 -0
- package/open-sse/services/codexQuotaFetcher.js +312 -0
- package/open-sse/services/codexVerbosity.js +30 -0
- package/open-sse/services/combo/context.js +12 -0
- package/open-sse/services/combo/fingerprintExpansion.js +51 -0
- package/open-sse/services/combo/headroomRanking.js +28 -0
- package/open-sse/services/combo/quotaExhaustionCutoff.js +87 -0
- package/open-sse/services/combo/quotaScoring.js +226 -0
- package/open-sse/services/combo/quotaShareConcurrency.js +25 -0
- package/open-sse/services/combo/quotaShareInflight.js +50 -0
- package/open-sse/services/combo/quotaShareStrategy.js +130 -0
- package/open-sse/services/combo/resolveAutoStrategy.js +231 -0
- package/open-sse/services/combo/responseValidation.js +140 -0
- package/open-sse/services/combo/runtimeUnits.js +195 -0
- package/open-sse/services/combo/sessionStickiness.js +128 -0
- package/open-sse/services/combo/shadowRouting.js +131 -0
- package/open-sse/services/combo/targetExhaustion.js +75 -0
- package/open-sse/services/combo/targetSorters.js +126 -0
- package/open-sse/services/combo/targetTimeoutRunner.js +65 -0
- package/open-sse/services/combo/validateQuality.js +271 -0
- package/open-sse/services/combo.js +571 -0
- package/open-sse/services/comboAgentMiddleware.js +91 -0
- package/open-sse/services/comboConfig.js +153 -0
- package/open-sse/services/comboManifestMetrics.js +10 -0
- package/open-sse/services/comboMetrics.js +278 -0
- package/open-sse/services/compact.js +71 -0
- package/open-sse/services/copilotModels.js +155 -0
- package/open-sse/services/emergencyFallback.js +109 -0
- package/open-sse/services/errorClassifier.js +190 -0
- package/open-sse/services/evalRouting.js +201 -0
- package/open-sse/services/freeWebSearch.js +4 -0
- package/open-sse/services/fusion.js +223 -0
- package/open-sse/services/intentClassifier.js +631 -0
- package/open-sse/services/kimchiModels.js +176 -0
- package/open-sse/services/kiroModels.js +332 -0
- package/open-sse/services/model.js +136 -0
- package/open-sse/services/modelCapabilities.js +5 -0
- package/open-sse/services/modelDeprecation.js +101 -0
- package/open-sse/services/modelFamilyFallback.js +166 -0
- package/open-sse/services/oauthCredentialManager.js +156 -0
- package/open-sse/services/pipeline.js +107 -0
- package/open-sse/services/projectId.js +306 -0
- package/open-sse/services/provider.js +169 -0
- package/open-sse/services/providerCooldownTracker.js +94 -0
- package/open-sse/services/providerDefaultRateLimit.js +11 -0
- package/open-sse/services/proxyAutoSelector.js +24 -0
- package/open-sse/services/qoderModels.js +214 -0
- package/open-sse/services/quotaFetchThrottle.js +103 -0
- package/open-sse/services/quotaMonitor.js +310 -0
- package/open-sse/services/quotaPreflight.js +266 -0
- package/open-sse/services/rateLimitManager/headers.js +56 -0
- package/open-sse/services/rateLimitManager.js +616 -0
- package/open-sse/services/rateLimitSemaphore.js +112 -0
- package/open-sse/services/responsesInputSanitizer.js +99 -0
- package/open-sse/services/slidingWindowLimiter.js +44 -0
- package/open-sse/services/streamRecovery.js +355 -0
- package/open-sse/services/taskAwareRouter.js +227 -0
- package/open-sse/services/taskAwareRouting.js +332 -0
- package/open-sse/services/thinkingBudget.js +247 -0
- package/open-sse/services/tokenRefresh/dedup.js +31 -0
- package/open-sse/services/tokenRefresh/providers.js +624 -0
- package/open-sse/services/tokenRefresh.js +254 -0
- package/open-sse/services/usage/claude.js +147 -0
- package/open-sse/services/usage/codebuddy-cn.js +138 -0
- package/open-sse/services/usage/codex.js +267 -0
- package/open-sse/services/usage/github.js +100 -0
- package/open-sse/services/usage/google.js +241 -0
- package/open-sse/services/usage/grok-cli.js +274 -0
- package/open-sse/services/usage/kiro.js +188 -0
- package/open-sse/services/usage/minimax.js +234 -0
- package/open-sse/services/usage/misc.js +288 -0
- package/open-sse/services/usage/shared.js +70 -0
- package/open-sse/services/usage.js +62 -0
- package/open-sse/services/usageDb.js +2 -0
- package/open-sse/services/webSearchRouting.js +25 -0
- package/open-sse/services/wildcardRouter.js +59 -0
- package/open-sse/shared/clineAuth.js +37 -0
- package/open-sse/shared/machineId.js +19 -0
- package/open-sse/shared/qoder/constants.js +64 -0
- package/open-sse/shared/qoder/cosy.js +175 -0
- package/open-sse/shared/qoder/encoding.js +55 -0
- package/open-sse/transformer/responsesTransformer.js +439 -0
- package/open-sse/transformer/streamToJsonConverter.js +103 -0
- package/open-sse/translator/concerns/chunk.js +11 -0
- package/open-sse/translator/concerns/finishReason.js +63 -0
- package/open-sse/translator/concerns/image.js +124 -0
- package/open-sse/translator/concerns/json.js +5 -0
- package/open-sse/translator/concerns/message.js +7 -0
- package/open-sse/translator/concerns/modality.js +155 -0
- package/open-sse/translator/concerns/paramSupport.js +75 -0
- package/open-sse/translator/concerns/prefetch.js +96 -0
- package/open-sse/translator/concerns/reasoning.js +24 -0
- package/open-sse/translator/concerns/thinking.js +53 -0
- package/open-sse/translator/concerns/thinkingUnified.js +329 -0
- package/open-sse/translator/concerns/toolCall.js +153 -0
- package/open-sse/translator/concerns/usage.js +69 -0
- package/open-sse/translator/formats/claude.js +368 -0
- package/open-sse/translator/formats/gemini.js +380 -0
- package/open-sse/translator/formats/maxTokens.js +34 -0
- package/open-sse/translator/formats/openai.js +134 -0
- package/open-sse/translator/formats/responsesApi.js +141 -0
- package/open-sse/translator/formats.js +36 -0
- package/open-sse/translator/helpers/claudeHelper.js +216 -0
- package/open-sse/translator/helpers/geminiHelper.js +372 -0
- package/open-sse/translator/helpers/imageHelper.js +34 -0
- package/open-sse/translator/helpers/maxTokensHelper.js +27 -0
- package/open-sse/translator/helpers/openaiHelper.js +130 -0
- package/open-sse/translator/helpers/responsesApiHelper.js +139 -0
- package/open-sse/translator/helpers/toolCallHelper.js +148 -0
- package/open-sse/translator/index.js +252 -0
- package/open-sse/translator/request/antigravity-to-openai.js +242 -0
- package/open-sse/translator/request/claude-to-kiro.js +478 -0
- package/open-sse/translator/request/claude-to-openai.js +263 -0
- package/open-sse/translator/request/gemini-to-openai.js +152 -0
- package/open-sse/translator/request/openai-responses.js +385 -0
- package/open-sse/translator/request/openai-to-claude.js +383 -0
- package/open-sse/translator/request/openai-to-commandcode.js +176 -0
- package/open-sse/translator/request/openai-to-cursor.js +185 -0
- package/open-sse/translator/request/openai-to-gemini.js +442 -0
- package/open-sse/translator/request/openai-to-kiro.js +607 -0
- package/open-sse/translator/request/openai-to-kiro.old.js +278 -0
- package/open-sse/translator/request/openai-to-ollama.js +195 -0
- package/open-sse/translator/request/openai-to-vertex.js +42 -0
- package/open-sse/translator/response/claude-to-openai.js +195 -0
- package/open-sse/translator/response/commandcode-to-openai.js +188 -0
- package/open-sse/translator/response/cursor-to-openai.js +30 -0
- package/open-sse/translator/response/gemini-to-openai.js +147 -0
- package/open-sse/translator/response/kiro-to-claude.js +261 -0
- package/open-sse/translator/response/kiro-to-openai.js +160 -0
- package/open-sse/translator/response/ollama-to-openai.js +134 -0
- package/open-sse/translator/response/openai-responses.js +535 -0
- package/open-sse/translator/response/openai-to-antigravity.js +123 -0
- package/open-sse/translator/response/openai-to-claude.js +265 -0
- package/open-sse/translator/schema/blocks.js +43 -0
- package/open-sse/translator/schema/defaults.js +7 -0
- package/open-sse/translator/schema/finishReasons.js +27 -0
- package/open-sse/translator/schema/index.js +8 -0
- package/open-sse/translator/schema/roles.js +16 -0
- package/open-sse/utils/agentGoalPolicy.js +85 -0
- package/open-sse/utils/aiSdkCompat.js +75 -0
- package/open-sse/utils/bypassHandler.js +298 -0
- package/open-sse/utils/cacheControlPolicy.js +189 -0
- package/open-sse/utils/claudeCloaking.js +155 -0
- package/open-sse/utils/claudeHeaderCache.js +70 -0
- package/open-sse/utils/claudeSignature.js +41 -0
- package/open-sse/utils/clientDetector.js +63 -0
- package/open-sse/utils/codebuddySanitizer.js +96 -0
- package/open-sse/utils/composerToolCalls.js +158 -0
- package/open-sse/utils/cors.js +13 -0
- package/open-sse/utils/cursorChecksum.js +149 -0
- package/open-sse/utils/cursorImages.js +224 -0
- package/open-sse/utils/cursorProtobuf.js +904 -0
- package/open-sse/utils/cursorVersionDetector.js +55 -0
- package/open-sse/utils/debugLog.js +14 -0
- package/open-sse/utils/diagnostics.js +143 -0
- package/open-sse/utils/error.js +164 -0
- package/open-sse/utils/estimateSize.js +33 -0
- package/open-sse/utils/finishReason.js +32 -0
- package/open-sse/utils/flattenToolHistory.js +68 -0
- package/open-sse/utils/headers.js +36 -0
- package/open-sse/utils/heapPressure.js +35 -0
- package/open-sse/utils/jsonToSse.js +125 -0
- package/open-sse/utils/keepaliveThreshold.js +40 -0
- package/open-sse/utils/logger.js +104 -0
- package/open-sse/utils/noThinkingAlias.js +74 -0
- package/open-sse/utils/number.js +7 -0
- package/open-sse/utils/ollamaTransform.js +85 -0
- package/open-sse/utils/opencodeHeaders.js +47 -0
- package/open-sse/utils/progressTracker.js +81 -0
- package/open-sse/utils/providerRequestLogging.js +219 -0
- package/open-sse/utils/proxyFetch.js +369 -0
- package/open-sse/utils/reasoningContentInjector.js +79 -0
- package/open-sse/utils/reasoningFields.js +70 -0
- package/open-sse/utils/requestLogger.js +260 -0
- package/open-sse/utils/responsesInputNormalization.js +92 -0
- package/open-sse/utils/responsesStatePolicy.js +52 -0
- package/open-sse/utils/responsesStreamHelpers.js +49 -0
- package/open-sse/utils/sessionManager.js +240 -0
- package/open-sse/utils/sleep.js +6 -0
- package/open-sse/utils/sse.js +14 -0
- package/open-sse/utils/sseConstants.js +23 -0
- package/open-sse/utils/sseHeartbeat.js +104 -0
- package/open-sse/utils/stream.js +517 -0
- package/open-sse/utils/streamFailureFinalization.js +134 -0
- package/open-sse/utils/streamHandler.js +250 -0
- package/open-sse/utils/streamHelpers.js +137 -0
- package/open-sse/utils/textualToolCall.js +104 -0
- package/open-sse/utils/thinkCloseMarker.js +72 -0
- package/open-sse/utils/thinkTagParser.js +146 -0
- package/open-sse/utils/toolCallArguments.js +12 -0
- package/open-sse/utils/toolDeduper.js +49 -0
- package/open-sse/utils/toolSources.js +31 -0
- package/open-sse/utils/upstreamResponseHeaders.js +25 -0
- package/open-sse/utils/urlSanitize.js +15 -0
- package/open-sse/utils/usageTracking.js +347 -0
- package/package.json +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { isFeatureFlagEnabled } from '../../dist/shared/utils/featureFlags.js';
|
|
2
|
+
const EMERGENCY_FALLBACK_FLAG_KEY = "OMNIROUTE_EMERGENCY_FALLBACK";
|
|
3
|
+
const EMERGENCY_FALLBACK_FLAG_CACHE_MS = 500;
|
|
4
|
+
let emergencyFallbackFlagCache = null;
|
|
5
|
+
let emergencyFallbackFeatureFlagResolver = isFeatureFlagEnabled;
|
|
6
|
+
const EMERGENCY_FALLBACK_CONFIG = {
|
|
7
|
+
enabled: true,
|
|
8
|
+
provider: "nvidia",
|
|
9
|
+
model: "openai/gpt-oss-120b",
|
|
10
|
+
triggerOn402: true,
|
|
11
|
+
triggerOnBudgetKeywords: true,
|
|
12
|
+
budgetKeywords: [
|
|
13
|
+
"insufficient funds",
|
|
14
|
+
"insufficient_funds",
|
|
15
|
+
"budget exceeded",
|
|
16
|
+
"budget_exceeded",
|
|
17
|
+
"quota exceeded",
|
|
18
|
+
"quota_exceeded",
|
|
19
|
+
"billing",
|
|
20
|
+
"payment required",
|
|
21
|
+
"out of credits",
|
|
22
|
+
"no credits",
|
|
23
|
+
"credit limit",
|
|
24
|
+
"spending limit",
|
|
25
|
+
"saldo insuficiente",
|
|
26
|
+
"limite de gastos",
|
|
27
|
+
"cota excedida"
|
|
28
|
+
],
|
|
29
|
+
skipForToolRequests: true,
|
|
30
|
+
maxOutputTokens: 4096
|
|
31
|
+
};
|
|
32
|
+
function isEmergencyFallbackRawEnvEnabled() {
|
|
33
|
+
const raw = process.env.OMNIROUTE_EMERGENCY_FALLBACK;
|
|
34
|
+
return raw !== "false" && raw !== "0";
|
|
35
|
+
}
|
|
36
|
+
function resetEmergencyFallbackEnvCache() {
|
|
37
|
+
emergencyFallbackFlagCache = null;
|
|
38
|
+
}
|
|
39
|
+
function setEmergencyFallbackFeatureFlagResolverForTest(resolver) {
|
|
40
|
+
emergencyFallbackFeatureFlagResolver = resolver ?? isFeatureFlagEnabled;
|
|
41
|
+
resetEmergencyFallbackEnvCache();
|
|
42
|
+
}
|
|
43
|
+
function isEmergencyFallbackEnvEnabled() {
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
if (emergencyFallbackFlagCache && emergencyFallbackFlagCache.expiresAt > now) {
|
|
46
|
+
return emergencyFallbackFlagCache.value;
|
|
47
|
+
}
|
|
48
|
+
let value;
|
|
49
|
+
try {
|
|
50
|
+
value = emergencyFallbackFeatureFlagResolver(EMERGENCY_FALLBACK_FLAG_KEY);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn(
|
|
53
|
+
"[emergencyFallback] Feature flag resolution failed; falling back to raw env:",
|
|
54
|
+
error instanceof Error ? error.message : error
|
|
55
|
+
);
|
|
56
|
+
value = isEmergencyFallbackRawEnvEnabled();
|
|
57
|
+
}
|
|
58
|
+
emergencyFallbackFlagCache = {
|
|
59
|
+
value,
|
|
60
|
+
expiresAt: now + EMERGENCY_FALLBACK_FLAG_CACHE_MS
|
|
61
|
+
};
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
function shouldUseFallback(status, errorBody, requestHasTools, config = EMERGENCY_FALLBACK_CONFIG) {
|
|
65
|
+
if (!config.enabled) return { shouldFallback: false, reason: "emergency fallback disabled" };
|
|
66
|
+
if (!isEmergencyFallbackEnvEnabled()) {
|
|
67
|
+
return {
|
|
68
|
+
shouldFallback: false,
|
|
69
|
+
reason: "emergency fallback disabled via OMNIROUTE_EMERGENCY_FALLBACK"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (config.skipForToolRequests && requestHasTools) {
|
|
73
|
+
return { shouldFallback: false, reason: "skipped: request has tools" };
|
|
74
|
+
}
|
|
75
|
+
if (config.triggerOn402 && status === 402) {
|
|
76
|
+
return {
|
|
77
|
+
shouldFallback: true,
|
|
78
|
+
reason: `HTTP 402 \u2192 emergency fallback to ${config.provider}/${config.model}`,
|
|
79
|
+
provider: config.provider,
|
|
80
|
+
model: config.model,
|
|
81
|
+
maxOutputTokens: config.maxOutputTokens
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (config.triggerOnBudgetKeywords && errorBody) {
|
|
85
|
+
const lowerBody = errorBody.toLowerCase();
|
|
86
|
+
const matched = config.budgetKeywords.find((kw) => lowerBody.includes(kw.toLowerCase()));
|
|
87
|
+
if (matched) {
|
|
88
|
+
return {
|
|
89
|
+
shouldFallback: true,
|
|
90
|
+
reason: `Budget error detected ('${matched}') \u2192 emergency fallback to ${config.provider}/${config.model}`,
|
|
91
|
+
provider: config.provider,
|
|
92
|
+
model: config.model,
|
|
93
|
+
maxOutputTokens: config.maxOutputTokens
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { shouldFallback: false, reason: "no budget error detected" };
|
|
98
|
+
}
|
|
99
|
+
function isFallbackDecision(result) {
|
|
100
|
+
return result.shouldFallback === true;
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
EMERGENCY_FALLBACK_CONFIG,
|
|
104
|
+
isEmergencyFallbackEnvEnabled,
|
|
105
|
+
isFallbackDecision,
|
|
106
|
+
resetEmergencyFallbackEnvCache,
|
|
107
|
+
setEmergencyFallbackFeatureFlagResolverForTest,
|
|
108
|
+
shouldUseFallback
|
|
109
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* errorClassifier.js — Classifies upstream errors for routing decisions.
|
|
3
|
+
* Handles: account deactivation, credits exhausted, daily quota, OAuth invalid, etc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
isAccountDeactivated,
|
|
8
|
+
isCreditsExhausted,
|
|
9
|
+
isDailyQuotaExhausted,
|
|
10
|
+
isOAuthInvalidToken,
|
|
11
|
+
} from "./accountFallback.js";
|
|
12
|
+
import { getProviderCategory } from "../config/providerRegistry.js";
|
|
13
|
+
|
|
14
|
+
// Terminal stop signals where an empty content payload is still a legitimate,
|
|
15
|
+
// successful completion (truncated at the token limit, or a tool-call turn) —
|
|
16
|
+
// NOT a silent "fake success" failure. Used to avoid rewriting a valid HTTP 200
|
|
17
|
+
// (e.g. a Claude Code `max_tokens: 1` connectivity ping) into a synthetic 502.
|
|
18
|
+
const LEGIT_EMPTY_CLAUDE_STOP = new Set(["max_tokens", "tool_use"]);
|
|
19
|
+
const LEGIT_EMPTY_OPENAI_FINISH = new Set(["length", "tool_calls"]);
|
|
20
|
+
|
|
21
|
+
export function isEmptyContentResponse(responseBody) {
|
|
22
|
+
if (!responseBody || typeof responseBody !== "object") return false;
|
|
23
|
+
|
|
24
|
+
const body = responseBody;
|
|
25
|
+
|
|
26
|
+
if (Array.isArray(body.choices)) {
|
|
27
|
+
const firstChoice = body.choices[0];
|
|
28
|
+
if (!firstChoice) return true;
|
|
29
|
+
|
|
30
|
+
const message = firstChoice.message;
|
|
31
|
+
const delta = firstChoice.delta;
|
|
32
|
+
|
|
33
|
+
const content = message?.content ?? delta?.content;
|
|
34
|
+
const reasoningContent = message?.reasoning_content ?? delta?.reasoning_content;
|
|
35
|
+
const hasToolCalls =
|
|
36
|
+
(Array.isArray(message?.tool_calls) && message.tool_calls.length > 0) ||
|
|
37
|
+
(Array.isArray(delta?.tool_calls) && delta.tool_calls.length > 0);
|
|
38
|
+
|
|
39
|
+
const hasContent = content !== null && content !== undefined && content !== "";
|
|
40
|
+
const hasReasoning =
|
|
41
|
+
reasoningContent !== null && reasoningContent !== undefined && reasoningContent !== "";
|
|
42
|
+
|
|
43
|
+
// A response truncated at the token limit (finish_reason "length") is a valid,
|
|
44
|
+
// successful completion even with empty text — do not flag it as a fake success.
|
|
45
|
+
const finishReason =
|
|
46
|
+
typeof firstChoice.finish_reason === "string" ? firstChoice.finish_reason : "";
|
|
47
|
+
if (LEGIT_EMPTY_OPENAI_FINISH.has(finishReason)) return false;
|
|
48
|
+
|
|
49
|
+
return !hasContent && !hasReasoning && !hasToolCalls;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (Array.isArray(body.content)) {
|
|
53
|
+
if (body.content.length > 0) return false;
|
|
54
|
+
// Empty content array: a response truncated at max_tokens (or one that stopped
|
|
55
|
+
// to emit a tool_use block) is a legitimate terminal state, not a silent
|
|
56
|
+
// failure. Only flag empty content when no such terminal stop_reason is present.
|
|
57
|
+
const stopReason = typeof body.stop_reason === "string" ? body.stop_reason : "";
|
|
58
|
+
return !LEGIT_EMPTY_CLAUDE_STOP.has(stopReason);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (typeof body.text === "string") {
|
|
62
|
+
return body.text.trim() === "";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if ("content" in body) {
|
|
66
|
+
const content = body.content;
|
|
67
|
+
return content === null || content === undefined || content === "";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const PROVIDER_ERROR_TYPES = {
|
|
74
|
+
RATE_LIMITED: "rate_limited",
|
|
75
|
+
UNAUTHORIZED: "unauthorized",
|
|
76
|
+
ACCOUNT_DEACTIVATED: "account_deactivated",
|
|
77
|
+
FORBIDDEN: "forbidden",
|
|
78
|
+
SERVER_ERROR: "server_error",
|
|
79
|
+
QUOTA_EXHAUSTED: "quota_exhausted",
|
|
80
|
+
PROJECT_ROUTE_ERROR: "project_route_error",
|
|
81
|
+
CONTEXT_OVERFLOW: "context_overflow",
|
|
82
|
+
OAUTH_INVALID_TOKEN: "oauth_invalid_token",
|
|
83
|
+
EMPTY_CONTENT: "empty_content",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const CONTEXT_OVERFLOW_SIGNALS = [
|
|
87
|
+
"context overflow",
|
|
88
|
+
"prompt too large",
|
|
89
|
+
"context window",
|
|
90
|
+
"maximum context",
|
|
91
|
+
"exceeds context",
|
|
92
|
+
"input too long",
|
|
93
|
+
"token limit",
|
|
94
|
+
"too many tokens",
|
|
95
|
+
"context length",
|
|
96
|
+
"exceed.*context",
|
|
97
|
+
"messages exceed",
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
export const CONTEXT_OVERFLOW_REGEX = new RegExp(CONTEXT_OVERFLOW_SIGNALS.join("|"), "i");
|
|
101
|
+
|
|
102
|
+
export function isContextOverflow(errorText) {
|
|
103
|
+
return CONTEXT_OVERFLOW_REGEX.test(String(errorText || ""));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function responseBodyToString(responseBody) {
|
|
107
|
+
if (typeof responseBody === "string") return responseBody;
|
|
108
|
+
if (responseBody !== null && typeof responseBody === "object") {
|
|
109
|
+
try {
|
|
110
|
+
return JSON.stringify(responseBody);
|
|
111
|
+
} catch {
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function shouldPreserveQuotaSignalsFor429(provider) {
|
|
119
|
+
if (!provider) return true;
|
|
120
|
+
return getProviderCategory(provider) === "oauth";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function classifyProviderError(statusCode, responseBody, provider) {
|
|
124
|
+
const bodyStr = responseBodyToString(responseBody);
|
|
125
|
+
const creditsExhausted = isCreditsExhausted(bodyStr);
|
|
126
|
+
const accountDeactivated = isAccountDeactivated(bodyStr);
|
|
127
|
+
const oauthInvalid = isOAuthInvalidToken(bodyStr);
|
|
128
|
+
const preserveQuota429 = shouldPreserveQuotaSignalsFor429(provider);
|
|
129
|
+
|
|
130
|
+
if (creditsExhausted && [400, 402, 403].includes(statusCode)) {
|
|
131
|
+
return PROVIDER_ERROR_TYPES.QUOTA_EXHAUSTED;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (creditsExhausted && statusCode === 429 && preserveQuota429) {
|
|
135
|
+
return PROVIDER_ERROR_TYPES.QUOTA_EXHAUSTED;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// API-key providers route 429 cooldowns through the resilience-aware fallback layer.
|
|
139
|
+
// OAuth providers keep their existing quota semantics because some of them encode
|
|
140
|
+
// longer quota windows as 429 responses.
|
|
141
|
+
if (statusCode === 429) {
|
|
142
|
+
if (preserveQuota429 && isDailyQuotaExhausted(bodyStr)) {
|
|
143
|
+
return PROVIDER_ERROR_TYPES.QUOTA_EXHAUSTED;
|
|
144
|
+
}
|
|
145
|
+
return PROVIDER_ERROR_TYPES.RATE_LIMITED;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (statusCode === 401) {
|
|
149
|
+
if (oauthInvalid) {
|
|
150
|
+
return PROVIDER_ERROR_TYPES.OAUTH_INVALID_TOKEN;
|
|
151
|
+
}
|
|
152
|
+
return accountDeactivated
|
|
153
|
+
? PROVIDER_ERROR_TYPES.ACCOUNT_DEACTIVATED
|
|
154
|
+
: PROVIDER_ERROR_TYPES.UNAUTHORIZED;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (statusCode === 402) return PROVIDER_ERROR_TYPES.QUOTA_EXHAUSTED;
|
|
158
|
+
if (statusCode === 403 && accountDeactivated) {
|
|
159
|
+
return PROVIDER_ERROR_TYPES.ACCOUNT_DEACTIVATED;
|
|
160
|
+
}
|
|
161
|
+
if (statusCode === 403) {
|
|
162
|
+
const p = (provider || "").toLowerCase();
|
|
163
|
+
const isCloudCodeProvider =
|
|
164
|
+
p === "antigravity" ||
|
|
165
|
+
p === "gemini-cli" ||
|
|
166
|
+
p.includes("cloudcode") ||
|
|
167
|
+
p.includes("cloud-code");
|
|
168
|
+
const recoverableProject403 =
|
|
169
|
+
bodyStr.includes("has not been used in project") ||
|
|
170
|
+
bodyStr.includes("SERVICE_DISABLED") ||
|
|
171
|
+
bodyStr.includes("accessNotConfigured") ||
|
|
172
|
+
bodyStr.includes("PERMISSION_DENIED") ||
|
|
173
|
+
/\bit is disabled\b/i.test(bodyStr) ||
|
|
174
|
+
isCloudCodeProvider;
|
|
175
|
+
if (recoverableProject403) {
|
|
176
|
+
return PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR;
|
|
177
|
+
}
|
|
178
|
+
if (provider && getProviderCategory(provider) === "apikey") {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return PROVIDER_ERROR_TYPES.FORBIDDEN;
|
|
182
|
+
}
|
|
183
|
+
if (statusCode >= 500) return PROVIDER_ERROR_TYPES.SERVER_ERROR;
|
|
184
|
+
|
|
185
|
+
if (statusCode === 400 && isContextOverflow(bodyStr)) {
|
|
186
|
+
return PROVIDER_ERROR_TYPES.CONTEXT_OVERFLOW;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { listModelEvalRunsForRouting } from "../../lib/db/evals.ts";
|
|
2
|
+
import { parseModel } from "./model.ts";
|
|
3
|
+
const DEFAULT_EVAL_ROUTING_CONFIG = {
|
|
4
|
+
enabled: false,
|
|
5
|
+
suiteIds: [],
|
|
6
|
+
maxAgeHours: 24 * 30,
|
|
7
|
+
minCases: 1,
|
|
8
|
+
qualityWeight: 0.85,
|
|
9
|
+
latencyWeight: 0.15,
|
|
10
|
+
cacheTtlMs: 6e4
|
|
11
|
+
};
|
|
12
|
+
const MAX_EVAL_ROUTING_CACHE_ENTRIES = 200;
|
|
13
|
+
const evalRoutingCache = /* @__PURE__ */ new Map();
|
|
14
|
+
function pruneEvalRoutingCache(now = Date.now()) {
|
|
15
|
+
for (const [key, entry] of evalRoutingCache) {
|
|
16
|
+
if (entry.expiresAt <= now) evalRoutingCache.delete(key);
|
|
17
|
+
}
|
|
18
|
+
while (evalRoutingCache.size > MAX_EVAL_ROUTING_CACHE_ENTRIES) {
|
|
19
|
+
const oldestKey = evalRoutingCache.keys().next().value;
|
|
20
|
+
if (!oldestKey) break;
|
|
21
|
+
evalRoutingCache.delete(oldestKey);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function isRecord(value) {
|
|
25
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
function toStringList(value, maxItems) {
|
|
28
|
+
if (!Array.isArray(value)) return [];
|
|
29
|
+
return [...new Set(value.map((entry) => String(entry || "").trim()).filter(Boolean))].slice(
|
|
30
|
+
0,
|
|
31
|
+
maxItems
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function clampNumber(value, fallback, min, max) {
|
|
35
|
+
const parsed = Number(value);
|
|
36
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
37
|
+
return Math.min(max, Math.max(min, parsed));
|
|
38
|
+
}
|
|
39
|
+
function normalizeEvalRoutingConfig(rawConfig) {
|
|
40
|
+
const raw = isRecord(rawConfig) ? rawConfig : {};
|
|
41
|
+
const qualityWeight = clampNumber(
|
|
42
|
+
raw.qualityWeight,
|
|
43
|
+
DEFAULT_EVAL_ROUTING_CONFIG.qualityWeight,
|
|
44
|
+
0,
|
|
45
|
+
1
|
|
46
|
+
);
|
|
47
|
+
const latencyWeight = clampNumber(
|
|
48
|
+
raw.latencyWeight,
|
|
49
|
+
DEFAULT_EVAL_ROUTING_CONFIG.latencyWeight,
|
|
50
|
+
0,
|
|
51
|
+
1
|
|
52
|
+
);
|
|
53
|
+
const weightTotal = qualityWeight + latencyWeight;
|
|
54
|
+
return {
|
|
55
|
+
enabled: raw.enabled === true,
|
|
56
|
+
suiteIds: toStringList(raw.suiteIds, 50),
|
|
57
|
+
maxAgeHours: clampNumber(raw.maxAgeHours, DEFAULT_EVAL_ROUTING_CONFIG.maxAgeHours, 1, 24 * 365),
|
|
58
|
+
minCases: Math.floor(
|
|
59
|
+
clampNumber(raw.minCases, DEFAULT_EVAL_ROUTING_CONFIG.minCases, 1, 1e5)
|
|
60
|
+
),
|
|
61
|
+
qualityWeight: weightTotal > 0 ? qualityWeight / weightTotal : 1,
|
|
62
|
+
latencyWeight: weightTotal > 0 ? latencyWeight / weightTotal : 0,
|
|
63
|
+
cacheTtlMs: Math.floor(
|
|
64
|
+
clampNumber(raw.cacheTtlMs, DEFAULT_EVAL_ROUTING_CONFIG.cacheTtlMs, 1e3, 3e5)
|
|
65
|
+
)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function getTargetAliases(modelStr) {
|
|
69
|
+
const parsed = parseModel(modelStr);
|
|
70
|
+
const modelId = typeof parsed.model === "string" ? parsed.model.trim() : "";
|
|
71
|
+
return [...new Set([modelStr.trim(), modelId].filter(Boolean))];
|
|
72
|
+
}
|
|
73
|
+
function buildCacheKey(targetIds, config) {
|
|
74
|
+
return JSON.stringify({
|
|
75
|
+
targetIds: [...targetIds].sort(),
|
|
76
|
+
suiteIds: [...config.suiteIds].sort(),
|
|
77
|
+
maxAgeHours: config.maxAgeHours,
|
|
78
|
+
minCases: config.minCases
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function getEvalRuns(targetIds, config) {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
pruneEvalRoutingCache(now);
|
|
84
|
+
const cacheKey = buildCacheKey(targetIds, config);
|
|
85
|
+
const cached = evalRoutingCache.get(cacheKey);
|
|
86
|
+
if (cached && cached.expiresAt > now) return cached.runs;
|
|
87
|
+
const runs = listModelEvalRunsForRouting({
|
|
88
|
+
targetIds,
|
|
89
|
+
suiteIds: config.suiteIds,
|
|
90
|
+
maxAgeHours: config.maxAgeHours
|
|
91
|
+
});
|
|
92
|
+
evalRoutingCache.set(cacheKey, { expiresAt: now + config.cacheTtlMs, runs });
|
|
93
|
+
pruneEvalRoutingCache(now);
|
|
94
|
+
return runs;
|
|
95
|
+
}
|
|
96
|
+
function dedupeLatestRunsBySuite(runs) {
|
|
97
|
+
const latest = /* @__PURE__ */ new Map();
|
|
98
|
+
for (const run of runs) {
|
|
99
|
+
const key = run.suiteId;
|
|
100
|
+
const current = latest.get(key);
|
|
101
|
+
if (!current || new Date(run.createdAt).getTime() > new Date(current.createdAt).getTime()) {
|
|
102
|
+
latest.set(key, run);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return Array.from(latest.values());
|
|
106
|
+
}
|
|
107
|
+
function calculateTargetScore(runs, config, bestLatencyMs) {
|
|
108
|
+
const validRuns = dedupeLatestRunsBySuite(runs).filter(
|
|
109
|
+
(run) => run.summary.total >= config.minCases
|
|
110
|
+
);
|
|
111
|
+
if (validRuns.length === 0) return null;
|
|
112
|
+
const totalCases = validRuns.reduce((sum, run) => sum + run.summary.total, 0);
|
|
113
|
+
if (totalCases < config.minCases) return null;
|
|
114
|
+
const passRate = validRuns.reduce((sum, run) => sum + run.summary.passRate * run.summary.total, 0) / totalCases;
|
|
115
|
+
const latencyWeight = validRuns.reduce(
|
|
116
|
+
(sum, run) => sum + Math.max(0, run.avgLatencyMs) * run.summary.total,
|
|
117
|
+
0
|
|
118
|
+
);
|
|
119
|
+
const avgLatencyMs = latencyWeight / totalCases;
|
|
120
|
+
const qualityScore = Math.max(0, Math.min(1, passRate / 100));
|
|
121
|
+
const latencyScore = bestLatencyMs && avgLatencyMs > 0 ? Math.max(0, Math.min(1, bestLatencyMs / avgLatencyMs)) : 0.5;
|
|
122
|
+
const score = qualityScore * config.qualityWeight + latencyScore * config.latencyWeight;
|
|
123
|
+
return {
|
|
124
|
+
score,
|
|
125
|
+
passRate,
|
|
126
|
+
avgLatencyMs,
|
|
127
|
+
totalCases,
|
|
128
|
+
runs: validRuns.length
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function orderTargetsByEvalScores(targets, rawConfig, log = {}) {
|
|
132
|
+
const config = normalizeEvalRoutingConfig(rawConfig);
|
|
133
|
+
if (!config.enabled || targets.length <= 1) return targets;
|
|
134
|
+
const aliasesByIndex = targets.map((target) => getTargetAliases(target.modelStr));
|
|
135
|
+
const targetIds = [...new Set(aliasesByIndex.flat())];
|
|
136
|
+
if (targetIds.length === 0) return targets;
|
|
137
|
+
let runs;
|
|
138
|
+
try {
|
|
139
|
+
runs = getEvalRuns(targetIds, config);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
log.warn?.("COMBO", "Eval-driven routing skipped because eval history could not be loaded", {
|
|
142
|
+
error: error instanceof Error ? error.message : String(error)
|
|
143
|
+
});
|
|
144
|
+
return targets;
|
|
145
|
+
}
|
|
146
|
+
if (runs.length === 0) return targets;
|
|
147
|
+
const runsByTargetId = /* @__PURE__ */ new Map();
|
|
148
|
+
for (const run of runs) {
|
|
149
|
+
const targetId = run.target.id;
|
|
150
|
+
if (!targetId) continue;
|
|
151
|
+
const bucket = runsByTargetId.get(targetId) || [];
|
|
152
|
+
bucket.push(run);
|
|
153
|
+
runsByTargetId.set(targetId, bucket);
|
|
154
|
+
}
|
|
155
|
+
const runsByIndex = aliasesByIndex.map((aliases) => {
|
|
156
|
+
const byId = /* @__PURE__ */ new Map();
|
|
157
|
+
for (const alias of aliases) {
|
|
158
|
+
for (const run of runsByTargetId.get(alias) || []) {
|
|
159
|
+
byId.set(run.id, run);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return Array.from(byId.values());
|
|
163
|
+
});
|
|
164
|
+
const candidateLatencies = runsByIndex.flatMap((targetRuns) => dedupeLatestRunsBySuite(targetRuns)).filter((run) => run.summary.total >= config.minCases && run.avgLatencyMs > 0).map((run) => run.avgLatencyMs);
|
|
165
|
+
const bestLatencyMs = candidateLatencies.length > 0 ? Math.min(...candidateLatencies) : null;
|
|
166
|
+
const entries = targets.map((target, index) => ({
|
|
167
|
+
target,
|
|
168
|
+
index,
|
|
169
|
+
score: calculateTargetScore(runsByIndex[index] || [], config, bestLatencyMs)
|
|
170
|
+
}));
|
|
171
|
+
const scoredCount = entries.filter((entry) => entry.score).length;
|
|
172
|
+
if (scoredCount === 0) return targets;
|
|
173
|
+
entries.sort((left, right) => {
|
|
174
|
+
if (left.score && right.score) {
|
|
175
|
+
const delta = right.score.score - left.score.score;
|
|
176
|
+
if (Math.abs(delta) > 1e-4) return delta;
|
|
177
|
+
return left.index - right.index;
|
|
178
|
+
}
|
|
179
|
+
if (left.score) return -1;
|
|
180
|
+
if (right.score) return 1;
|
|
181
|
+
return left.index - right.index;
|
|
182
|
+
});
|
|
183
|
+
log.info?.(
|
|
184
|
+
"COMBO",
|
|
185
|
+
`Eval-driven routing: ranked ${scoredCount}/${targets.length} targets by eval history`
|
|
186
|
+
);
|
|
187
|
+
log.debug?.(
|
|
188
|
+
"COMBO",
|
|
189
|
+
`Eval-driven routing scores: ${entries.filter((entry) => entry.score).map(
|
|
190
|
+
(entry) => `${entry.target.modelStr}=${entry.score?.score.toFixed(3)} pass=${entry.score?.passRate.toFixed(1)} cases=${entry.score?.totalCases}`
|
|
191
|
+
).join(", ")}`
|
|
192
|
+
);
|
|
193
|
+
return entries.map((entry) => entry.target);
|
|
194
|
+
}
|
|
195
|
+
function resetEvalRoutingCache() {
|
|
196
|
+
evalRoutingCache.clear();
|
|
197
|
+
}
|
|
198
|
+
export {
|
|
199
|
+
orderTargetsByEvalScores,
|
|
200
|
+
resetEvalRoutingCache
|
|
201
|
+
};
|