@gajae-code/ai 0.1.1
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/CHANGELOG.md +2644 -0
- package/README.md +1181 -0
- package/dist/types/api-registry.d.ts +30 -0
- package/dist/types/auth-broker/client.d.ts +66 -0
- package/dist/types/auth-broker/index.d.ts +5 -0
- package/dist/types/auth-broker/refresher.d.ts +25 -0
- package/dist/types/auth-broker/remote-store.d.ts +96 -0
- package/dist/types/auth-broker/server.d.ts +32 -0
- package/dist/types/auth-broker/types.d.ts +105 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
- package/dist/types/auth-gateway/http.d.ts +39 -0
- package/dist/types/auth-gateway/index.d.ts +3 -0
- package/dist/types/auth-gateway/server.d.ts +17 -0
- package/dist/types/auth-gateway/types.d.ts +115 -0
- package/dist/types/auth-storage.d.ts +641 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/index.d.ts +49 -0
- package/dist/types/model-cache.d.ts +17 -0
- package/dist/types/model-manager.d.ts +62 -0
- package/dist/types/model-thinking.d.ts +71 -0
- package/dist/types/models.d.ts +12 -0
- package/dist/types/provider-details.d.ts +24 -0
- package/dist/types/provider-models/bundled-references.d.ts +4 -0
- package/dist/types/provider-models/descriptors.d.ts +48 -0
- package/dist/types/provider-models/google.d.ts +20 -0
- package/dist/types/provider-models/index.d.ts +5 -0
- package/dist/types/provider-models/ollama.d.ts +7 -0
- package/dist/types/provider-models/openai-compat.d.ts +237 -0
- package/dist/types/provider-models/special.d.ts +16 -0
- package/dist/types/providers/amazon-bedrock.d.ts +36 -0
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
- package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
- package/dist/types/providers/anthropic.d.ts +188 -0
- package/dist/types/providers/aws-credentials.d.ts +43 -0
- package/dist/types/providers/aws-eventstream.d.ts +38 -0
- package/dist/types/providers/aws-sigv4.d.ts +55 -0
- package/dist/types/providers/azure-openai-responses.d.ts +15 -0
- package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
- package/dist/types/providers/cursor.d.ts +42 -0
- package/dist/types/providers/error-message.d.ts +27 -0
- package/dist/types/providers/github-copilot-headers.d.ts +40 -0
- package/dist/types/providers/gitlab-duo.d.ts +27 -0
- package/dist/types/providers/google-auth.d.ts +24 -0
- package/dist/types/providers/google-gemini-cli.d.ts +72 -0
- package/dist/types/providers/google-gemini-headers.d.ts +18 -0
- package/dist/types/providers/google-shared.d.ts +163 -0
- package/dist/types/providers/google-types.d.ts +138 -0
- package/dist/types/providers/google-vertex.d.ts +7 -0
- package/dist/types/providers/google.d.ts +4 -0
- package/dist/types/providers/grammar.d.ts +1 -0
- package/dist/types/providers/kimi.d.ts +27 -0
- package/dist/types/providers/mock.d.ts +175 -0
- package/dist/types/providers/ollama.d.ts +6 -0
- package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +814 -0
- package/dist/types/providers/openai-chat-server.d.ts +16 -0
- package/dist/types/providers/openai-codex/constants.d.ts +26 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
- package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
- package/dist/types/providers/openai-codex-responses.d.ts +67 -0
- package/dist/types/providers/openai-completions-compat.d.ts +25 -0
- package/dist/types/providers/openai-completions.d.ts +33 -0
- package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
- package/dist/types/providers/openai-responses-server.d.ts +17 -0
- package/dist/types/providers/openai-responses-shared.d.ts +89 -0
- package/dist/types/providers/openai-responses.d.ts +32 -0
- package/dist/types/providers/pi-native-client.d.ts +13 -0
- package/dist/types/providers/pi-native-server.d.ts +68 -0
- package/dist/types/providers/register-builtins.d.ts +31 -0
- package/dist/types/providers/synthetic.d.ts +26 -0
- package/dist/types/providers/transform-messages.d.ts +12 -0
- package/dist/types/providers/vision-guard.d.ts +8 -0
- package/dist/types/rate-limit-utils.d.ts +19 -0
- package/dist/types/stream.d.ts +24 -0
- package/dist/types/types.d.ts +746 -0
- package/dist/types/usage/claude.d.ts +3 -0
- package/dist/types/usage/gemini.d.ts +2 -0
- package/dist/types/usage/github-copilot.d.ts +7 -0
- package/dist/types/usage/google-antigravity.d.ts +2 -0
- package/dist/types/usage/kimi.d.ts +2 -0
- package/dist/types/usage/minimax-code.d.ts +2 -0
- package/dist/types/usage/openai-codex.d.ts +3 -0
- package/dist/types/usage/shared.d.ts +1 -0
- package/dist/types/usage/zai.d.ts +2 -0
- package/dist/types/usage.d.ts +258 -0
- package/dist/types/utils/abort.d.ts +19 -0
- package/dist/types/utils/anthropic-auth.d.ts +31 -0
- package/dist/types/utils/discovery/antigravity.d.ts +61 -0
- package/dist/types/utils/discovery/codex.d.ts +38 -0
- package/dist/types/utils/discovery/cursor.d.ts +23 -0
- package/dist/types/utils/discovery/gemini.d.ts +25 -0
- package/dist/types/utils/discovery/index.d.ts +4 -0
- package/dist/types/utils/discovery/openai-compatible.d.ts +72 -0
- package/dist/types/utils/event-stream.d.ts +28 -0
- package/dist/types/utils/fireworks-model-id.d.ts +10 -0
- package/dist/types/utils/foundry.d.ts +1 -0
- package/dist/types/utils/h2-fetch.d.ts +22 -0
- package/dist/types/utils/http-inspector.d.ts +31 -0
- package/dist/types/utils/idle-iterator.d.ts +67 -0
- package/dist/types/utils/json-parse.d.ts +10 -0
- package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
- package/dist/types/utils/oauth/anthropic.d.ts +22 -0
- package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
- package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
- package/dist/types/utils/oauth/callback-server.d.ts +57 -0
- package/dist/types/utils/oauth/cerebras.d.ts +1 -0
- package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/cursor.d.ts +15 -0
- package/dist/types/utils/oauth/deepseek.d.ts +10 -0
- package/dist/types/utils/oauth/firepass.d.ts +1 -0
- package/dist/types/utils/oauth/fireworks.d.ts +1 -0
- package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
- package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
- package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
- package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
- package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
- package/dist/types/utils/oauth/huggingface.d.ts +19 -0
- package/dist/types/utils/oauth/index.d.ts +38 -0
- package/dist/types/utils/oauth/kagi.d.ts +17 -0
- package/dist/types/utils/oauth/kilo.d.ts +5 -0
- package/dist/types/utils/oauth/kimi.d.ts +21 -0
- package/dist/types/utils/oauth/litellm.d.ts +18 -0
- package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
- package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
- package/dist/types/utils/oauth/moonshot.d.ts +1 -0
- package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
- package/dist/types/utils/oauth/nvidia.d.ts +18 -0
- package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
- package/dist/types/utils/oauth/ollama.d.ts +18 -0
- package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
- package/dist/types/utils/oauth/opencode.d.ts +18 -0
- package/dist/types/utils/oauth/parallel.d.ts +17 -0
- package/dist/types/utils/oauth/perplexity.d.ts +9 -0
- package/dist/types/utils/oauth/pkce.d.ts +8 -0
- package/dist/types/utils/oauth/qianfan.d.ts +17 -0
- package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
- package/dist/types/utils/oauth/synthetic.d.ts +1 -0
- package/dist/types/utils/oauth/tavily.d.ts +17 -0
- package/dist/types/utils/oauth/together.d.ts +1 -0
- package/dist/types/utils/oauth/types.d.ts +44 -0
- package/dist/types/utils/oauth/venice.d.ts +18 -0
- package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/vllm.d.ts +16 -0
- package/dist/types/utils/oauth/xiaomi.d.ts +19 -0
- package/dist/types/utils/oauth/zai.d.ts +18 -0
- package/dist/types/utils/oauth/zenmux.d.ts +1 -0
- package/dist/types/utils/overflow.d.ts +54 -0
- package/dist/types/utils/parse-bind.d.ts +23 -0
- package/dist/types/utils/provider-response.d.ts +3 -0
- package/dist/types/utils/retry-after.d.ts +3 -0
- package/dist/types/utils/retry.d.ts +26 -0
- package/dist/types/utils/schema/adapt.d.ts +24 -0
- package/dist/types/utils/schema/compatibility.d.ts +30 -0
- package/dist/types/utils/schema/dereference.d.ts +11 -0
- package/dist/types/utils/schema/draft.d.ts +10 -0
- package/dist/types/utils/schema/equality.d.ts +4 -0
- package/dist/types/utils/schema/fields.d.ts +49 -0
- package/dist/types/utils/schema/index.d.ts +13 -0
- package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
- package/dist/types/utils/schema/meta-validator.d.ts +2 -0
- package/dist/types/utils/schema/normalize.d.ts +93 -0
- package/dist/types/utils/schema/spill.d.ts +8 -0
- package/dist/types/utils/schema/stamps.d.ts +25 -0
- package/dist/types/utils/schema/types.d.ts +4 -0
- package/dist/types/utils/schema/wire.d.ts +54 -0
- package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
- package/dist/types/utils/sse-debug.d.ts +10 -0
- package/dist/types/utils/tool-call-healing.d.ts +71 -0
- package/dist/types/utils/tool-choice.d.ts +50 -0
- package/dist/types/utils/validation.d.ts +17 -0
- package/dist/types/utils.d.ts +28 -0
- package/package.json +146 -0
- package/src/api-registry.ts +96 -0
- package/src/auth-broker/client.ts +358 -0
- package/src/auth-broker/index.ts +5 -0
- package/src/auth-broker/refresher.ts +127 -0
- package/src/auth-broker/remote-store.ts +623 -0
- package/src/auth-broker/server.ts +644 -0
- package/src/auth-broker/types.ts +127 -0
- package/src/auth-broker/wire-schemas.ts +200 -0
- package/src/auth-gateway/http.ts +194 -0
- package/src/auth-gateway/index.ts +3 -0
- package/src/auth-gateway/server.ts +717 -0
- package/src/auth-gateway/types.ts +134 -0
- package/src/auth-storage.ts +4104 -0
- package/src/cli.ts +262 -0
- package/src/index.ts +54 -0
- package/src/model-cache.ts +129 -0
- package/src/model-manager.ts +450 -0
- package/src/model-thinking.ts +691 -0
- package/src/models.json +73853 -0
- package/src/models.json.d.ts +9 -0
- package/src/models.ts +56 -0
- package/src/prompts/turn-aborted-guidance.md +4 -0
- package/src/provider-details.ts +90 -0
- package/src/provider-models/bundled-references.ts +38 -0
- package/src/provider-models/descriptors.ts +308 -0
- package/src/provider-models/google.ts +91 -0
- package/src/provider-models/index.ts +5 -0
- package/src/provider-models/ollama.ts +153 -0
- package/src/provider-models/openai-compat.ts +2275 -0
- package/src/provider-models/special.ts +67 -0
- package/src/providers/amazon-bedrock.ts +849 -0
- package/src/providers/anthropic-messages-server-schema.ts +229 -0
- package/src/providers/anthropic-messages-server.ts +677 -0
- package/src/providers/anthropic.ts +2696 -0
- package/src/providers/aws-credentials.ts +501 -0
- package/src/providers/aws-eventstream.ts +185 -0
- package/src/providers/aws-sigv4.ts +218 -0
- package/src/providers/azure-openai-responses.ts +337 -0
- package/src/providers/cursor/gen/agent_pb.ts +15274 -0
- package/src/providers/cursor/proto/agent.proto +3526 -0
- package/src/providers/cursor/proto/buf.gen.yaml +6 -0
- package/src/providers/cursor/proto/buf.yaml +17 -0
- package/src/providers/cursor.ts +2561 -0
- package/src/providers/error-message.ts +21 -0
- package/src/providers/github-copilot-headers.ts +140 -0
- package/src/providers/gitlab-duo.ts +372 -0
- package/src/providers/google-auth.ts +252 -0
- package/src/providers/google-gemini-cli.ts +795 -0
- package/src/providers/google-gemini-headers.ts +41 -0
- package/src/providers/google-shared.ts +902 -0
- package/src/providers/google-types.ts +167 -0
- package/src/providers/google-vertex.ts +88 -0
- package/src/providers/google.ts +41 -0
- package/src/providers/grammar.ts +70 -0
- package/src/providers/kimi.ts +52 -0
- package/src/providers/mock.ts +500 -0
- package/src/providers/ollama.ts +544 -0
- package/src/providers/openai-anthropic-shim.ts +138 -0
- package/src/providers/openai-chat-server-schema.ts +243 -0
- package/src/providers/openai-chat-server.ts +628 -0
- package/src/providers/openai-codex/constants.ts +43 -0
- package/src/providers/openai-codex/request-transformer.ts +161 -0
- package/src/providers/openai-codex/response-handler.ts +81 -0
- package/src/providers/openai-codex-responses.ts +2598 -0
- package/src/providers/openai-completions-compat.ts +279 -0
- package/src/providers/openai-completions.ts +1853 -0
- package/src/providers/openai-responses-server-schema.ts +290 -0
- package/src/providers/openai-responses-server.ts +1183 -0
- package/src/providers/openai-responses-shared.ts +800 -0
- package/src/providers/openai-responses.ts +621 -0
- package/src/providers/pi-native-client.ts +228 -0
- package/src/providers/pi-native-server.ts +210 -0
- package/src/providers/register-builtins.ts +412 -0
- package/src/providers/synthetic.ts +50 -0
- package/src/providers/transform-messages.ts +309 -0
- package/src/providers/vision-guard.ts +31 -0
- package/src/rate-limit-utils.ts +84 -0
- package/src/stream.ts +895 -0
- package/src/types.ts +884 -0
- package/src/usage/claude.ts +431 -0
- package/src/usage/gemini.ts +250 -0
- package/src/usage/github-copilot.ts +421 -0
- package/src/usage/google-antigravity.ts +201 -0
- package/src/usage/kimi.ts +271 -0
- package/src/usage/minimax-code.ts +31 -0
- package/src/usage/openai-codex.ts +503 -0
- package/src/usage/shared.ts +10 -0
- package/src/usage/zai.ts +247 -0
- package/src/usage.ts +183 -0
- package/src/utils/abort.ts +51 -0
- package/src/utils/anthropic-auth.ts +87 -0
- package/src/utils/discovery/antigravity.ts +261 -0
- package/src/utils/discovery/codex.ts +371 -0
- package/src/utils/discovery/cursor.ts +306 -0
- package/src/utils/discovery/gemini.ts +248 -0
- package/src/utils/discovery/index.ts +4 -0
- package/src/utils/discovery/openai-compatible.ts +224 -0
- package/src/utils/event-stream.ts +142 -0
- package/src/utils/fireworks-model-id.ts +30 -0
- package/src/utils/foundry.ts +8 -0
- package/src/utils/h2-fetch.ts +60 -0
- package/src/utils/http-inspector.ts +176 -0
- package/src/utils/idle-iterator.ts +250 -0
- package/src/utils/json-parse.ts +148 -0
- package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
- package/src/utils/oauth/anthropic.ts +200 -0
- package/src/utils/oauth/api-key-login.ts +87 -0
- package/src/utils/oauth/api-key-validation.ts +92 -0
- package/src/utils/oauth/callback-server.ts +276 -0
- package/src/utils/oauth/cerebras.ts +16 -0
- package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
- package/src/utils/oauth/cursor.ts +157 -0
- package/src/utils/oauth/deepseek.ts +53 -0
- package/src/utils/oauth/firepass.ts +24 -0
- package/src/utils/oauth/fireworks.ts +15 -0
- package/src/utils/oauth/github-copilot.ts +362 -0
- package/src/utils/oauth/gitlab-duo.ts +123 -0
- package/src/utils/oauth/google-antigravity.ts +200 -0
- package/src/utils/oauth/google-gemini-cli.ts +256 -0
- package/src/utils/oauth/google-oauth-shared.ts +110 -0
- package/src/utils/oauth/huggingface.ts +62 -0
- package/src/utils/oauth/index.ts +444 -0
- package/src/utils/oauth/kagi.ts +47 -0
- package/src/utils/oauth/kilo.ts +87 -0
- package/src/utils/oauth/kimi.ts +254 -0
- package/src/utils/oauth/litellm.ts +47 -0
- package/src/utils/oauth/lm-studio.ts +38 -0
- package/src/utils/oauth/minimax-code.ts +78 -0
- package/src/utils/oauth/moonshot.ts +16 -0
- package/src/utils/oauth/nanogpt.ts +15 -0
- package/src/utils/oauth/nvidia.ts +70 -0
- package/src/utils/oauth/oauth.html +199 -0
- package/src/utils/oauth/ollama-cloud.ts +28 -0
- package/src/utils/oauth/ollama.ts +47 -0
- package/src/utils/oauth/openai-codex.ts +299 -0
- package/src/utils/oauth/opencode.ts +49 -0
- package/src/utils/oauth/parallel.ts +46 -0
- package/src/utils/oauth/perplexity.ts +206 -0
- package/src/utils/oauth/pkce.ts +18 -0
- package/src/utils/oauth/qianfan.ts +58 -0
- package/src/utils/oauth/qwen-portal.ts +60 -0
- package/src/utils/oauth/synthetic.ts +16 -0
- package/src/utils/oauth/tavily.ts +46 -0
- package/src/utils/oauth/together.ts +16 -0
- package/src/utils/oauth/types.ts +94 -0
- package/src/utils/oauth/venice.ts +59 -0
- package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
- package/src/utils/oauth/vllm.ts +40 -0
- package/src/utils/oauth/xiaomi.ts +137 -0
- package/src/utils/oauth/zai.ts +60 -0
- package/src/utils/oauth/zenmux.ts +15 -0
- package/src/utils/overflow.ts +137 -0
- package/src/utils/parse-bind.ts +54 -0
- package/src/utils/provider-response.ts +30 -0
- package/src/utils/retry-after.ts +110 -0
- package/src/utils/retry.ts +54 -0
- package/src/utils/schema/CONSTRAINTS.md +164 -0
- package/src/utils/schema/adapt.ts +36 -0
- package/src/utils/schema/compatibility.ts +435 -0
- package/src/utils/schema/dereference.ts +98 -0
- package/src/utils/schema/draft.ts +341 -0
- package/src/utils/schema/equality.ts +97 -0
- package/src/utils/schema/fields.ts +190 -0
- package/src/utils/schema/index.ts +13 -0
- package/src/utils/schema/json-schema-validator.ts +577 -0
- package/src/utils/schema/meta-validator.ts +167 -0
- package/src/utils/schema/normalize.ts +1588 -0
- package/src/utils/schema/spill.ts +43 -0
- package/src/utils/schema/stamps.ts +97 -0
- package/src/utils/schema/types.ts +11 -0
- package/src/utils/schema/wire.ts +213 -0
- package/src/utils/schema/zod-decontaminate.ts +331 -0
- package/src/utils/sse-debug.ts +289 -0
- package/src/utils/tool-call-healing.ts +271 -0
- package/src/utils/tool-choice.ts +99 -0
- package/src/utils/validation.ts +1019 -0
- package/src/utils.ts +166 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
import { resolveOpenAICompat } from "./providers/openai-completions-compat";
|
|
2
|
+
import type { Api, Model as ApiModel, ThinkingConfig } from "./types";
|
|
3
|
+
|
|
4
|
+
/** User-facing thinking levels, ordered least to most intensive. */
|
|
5
|
+
export const enum Effort {
|
|
6
|
+
Minimal = "minimal",
|
|
7
|
+
Low = "low",
|
|
8
|
+
Medium = "medium",
|
|
9
|
+
High = "high",
|
|
10
|
+
XHigh = "xhigh",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const THINKING_EFFORTS: readonly Effort[] = [
|
|
14
|
+
Effort.Minimal,
|
|
15
|
+
Effort.Low,
|
|
16
|
+
Effort.Medium,
|
|
17
|
+
Effort.High,
|
|
18
|
+
Effort.XHigh,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const DEFAULT_REASONING_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High];
|
|
22
|
+
const DEFAULT_REASONING_EFFORTS_WITH_XHIGH: readonly Effort[] = [
|
|
23
|
+
Effort.Minimal,
|
|
24
|
+
Effort.Low,
|
|
25
|
+
Effort.Medium,
|
|
26
|
+
Effort.High,
|
|
27
|
+
Effort.XHigh,
|
|
28
|
+
];
|
|
29
|
+
const GEMINI_3_PRO_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High];
|
|
30
|
+
const GEMINI_3_FLASH_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High];
|
|
31
|
+
const GPT_5_2_PLUS_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh];
|
|
32
|
+
const GPT_5_1_CODEX_MINI_EFFORTS: readonly Effort[] = [Effort.Medium, Effort.High];
|
|
33
|
+
const CLOUDFLARE_AI_GATEWAY_BASE_URL = "https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/anthropic";
|
|
34
|
+
|
|
35
|
+
type SemVer = {
|
|
36
|
+
major: number;
|
|
37
|
+
minor: number;
|
|
38
|
+
patch: number;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type GeminiKind = "pro" | "flash";
|
|
42
|
+
type AnthropicKind = "opus" | "sonnet";
|
|
43
|
+
type OpenAIVariant = "base" | "codex" | "codex-max" | "codex-mini" | "codex-spark" | "mini" | "max" | "nano";
|
|
44
|
+
|
|
45
|
+
const CODEX_GPT_5_4_PRIORITY_BY_VARIANT: Partial<Record<OpenAIVariant, number>> = {
|
|
46
|
+
base: 0,
|
|
47
|
+
mini: 1,
|
|
48
|
+
nano: 2,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const COPILOT_GENERATED_LIMITS: Record<string, { contextWindow: number; maxTokens: number }> = {
|
|
52
|
+
"claude-opus-4.6": { contextWindow: 168000, maxTokens: 32000 },
|
|
53
|
+
"gpt-5.2": { contextWindow: 272000, maxTokens: 128000 },
|
|
54
|
+
"gpt-5.4": { contextWindow: 272000, maxTokens: 128000 },
|
|
55
|
+
"gpt-5.4-mini": { contextWindow: 272000, maxTokens: 128000 },
|
|
56
|
+
"grok-code-fast-1": { contextWindow: 192000, maxTokens: 64000 },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
interface GeminiModel {
|
|
60
|
+
family: "gemini";
|
|
61
|
+
kind: GeminiKind;
|
|
62
|
+
version: SemVer;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface AnthropicModel {
|
|
66
|
+
family: "anthropic";
|
|
67
|
+
kind: AnthropicKind;
|
|
68
|
+
version: SemVer;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface OpenAIModel {
|
|
72
|
+
family: "openai";
|
|
73
|
+
variant: OpenAIVariant;
|
|
74
|
+
version: SemVer;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface UnknownModel {
|
|
78
|
+
family: "unknown";
|
|
79
|
+
id: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
type ParsedModel = GeminiModel | AnthropicModel | OpenAIModel | UnknownModel;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Static fallback model injected when Cloudflare AI Gateway discovery
|
|
86
|
+
* returns no results. Ensures the provider always has at least one usable
|
|
87
|
+
* model entry in the catalog.
|
|
88
|
+
*/
|
|
89
|
+
export const CLOUDFLARE_FALLBACK_MODEL: ApiModel<"anthropic-messages"> = {
|
|
90
|
+
id: "claude-sonnet-4-5",
|
|
91
|
+
name: "Anthropic Sonnet 4.5",
|
|
92
|
+
api: "anthropic-messages",
|
|
93
|
+
provider: "cloudflare-ai-gateway",
|
|
94
|
+
baseUrl: CLOUDFLARE_AI_GATEWAY_BASE_URL,
|
|
95
|
+
reasoning: true,
|
|
96
|
+
input: ["text", "image"],
|
|
97
|
+
cost: {
|
|
98
|
+
input: 3,
|
|
99
|
+
output: 15,
|
|
100
|
+
cacheRead: 0.3,
|
|
101
|
+
cacheWrite: 3.75,
|
|
102
|
+
},
|
|
103
|
+
contextWindow: 200000,
|
|
104
|
+
maxTokens: 64000,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const kEnrichedModel = Symbol("model-thinking.enrichedModel");
|
|
108
|
+
type ModelWithEnriched = ApiModel<Api> & { [kEnrichedModel]?: ApiModel<Api> };
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Returns a copy of the model with canonical thinking metadata attached.
|
|
112
|
+
*
|
|
113
|
+
* This helper belongs to catalog enrichment only. Runtime consumers should
|
|
114
|
+
* trust `model.thinking` and avoid inferring capabilities on demand.
|
|
115
|
+
*/
|
|
116
|
+
export function enrichModelThinking<TApi extends Api>(model: ApiModel<TApi>): ApiModel<TApi> {
|
|
117
|
+
const tagged = model as ModelWithEnriched;
|
|
118
|
+
const cached = tagged[kEnrichedModel];
|
|
119
|
+
if (cached !== undefined) {
|
|
120
|
+
return cached as ApiModel<TApi>;
|
|
121
|
+
}
|
|
122
|
+
const normalizedThinking = normalizeThinkingConfig(model.thinking);
|
|
123
|
+
let result: ApiModel<TApi>;
|
|
124
|
+
if (!model.reasoning) {
|
|
125
|
+
result =
|
|
126
|
+
normalizedThinking === undefined && model.thinking === undefined ? model : { ...model, thinking: undefined };
|
|
127
|
+
} else {
|
|
128
|
+
const thinking = normalizedThinking ?? inferModelThinking(model);
|
|
129
|
+
result = thinkingsEqual(normalizedThinking, thinking) ? model : { ...model, thinking };
|
|
130
|
+
}
|
|
131
|
+
// Stash the enriched copy on a non-enumerable slot so callers that hand us
|
|
132
|
+
// the same reference twice skip the work. `enumerable: false` is critical:
|
|
133
|
+
// many call sites build derived models via `{ ...model, ...overrides }`,
|
|
134
|
+
// which would otherwise copy this cache slot and trick us into returning
|
|
135
|
+
// the *original* enriched model — silently discarding the overrides.
|
|
136
|
+
Object.defineProperty(tagged, kEnrichedModel, {
|
|
137
|
+
value: result,
|
|
138
|
+
enumerable: false,
|
|
139
|
+
configurable: true,
|
|
140
|
+
writable: true,
|
|
141
|
+
});
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns a copy of the model with thinking metadata recomputed from the
|
|
147
|
+
* canonical rules, replacing any existing `thinking`.
|
|
148
|
+
*/
|
|
149
|
+
export function refreshModelThinking<TApi extends Api>(model: ApiModel<TApi>): ApiModel<TApi> {
|
|
150
|
+
if (!model.reasoning) {
|
|
151
|
+
const normalizedThinking = normalizeThinkingConfig(model.thinking);
|
|
152
|
+
return normalizedThinking === undefined && model.thinking === undefined
|
|
153
|
+
? model
|
|
154
|
+
: { ...model, thinking: undefined };
|
|
155
|
+
}
|
|
156
|
+
return { ...model, thinking: inferModelThinking(model) };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Apply upstream metadata corrections to a mutable array of models.
|
|
161
|
+
*
|
|
162
|
+
* Each model is first normalized through `refreshModelThinking()` so generated
|
|
163
|
+
* catalogs keep canonical thinking metadata and policy fixes in one pass.
|
|
164
|
+
*/
|
|
165
|
+
export function applyGeneratedModelPolicies(models: ApiModel<Api>[]): void {
|
|
166
|
+
for (let index = 0; index < models.length; index++) {
|
|
167
|
+
const model = refreshModelThinking(models[index]!);
|
|
168
|
+
applyGeneratedModelPolicy(model);
|
|
169
|
+
models[index] = model;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Link OpenAI model variants to their context promotion targets.
|
|
175
|
+
*
|
|
176
|
+
* When a model's context is exhausted, the agent can promote to a sibling
|
|
177
|
+
* model with a larger context window on the same provider:
|
|
178
|
+
* - `OpenAI code backend-spark` variants promote to `gpt-5.5`.
|
|
179
|
+
* - `gpt-5.5` (270K input) promotes to `gpt-5.4` (1M input).
|
|
180
|
+
*/
|
|
181
|
+
export function linkOpenAIPromotionTargets(models: ApiModel<Api>[]): void {
|
|
182
|
+
for (const candidate of models) {
|
|
183
|
+
const parsedCandidate = parseKnownModel(candidate.id);
|
|
184
|
+
if (parsedCandidate.family !== "openai") continue;
|
|
185
|
+
let targetId: string | undefined;
|
|
186
|
+
if (parsedCandidate.variant === "codex-spark") {
|
|
187
|
+
targetId = "gpt-5.5";
|
|
188
|
+
} else if (parsedCandidate.variant === "base" && semverEqual(parsedCandidate.version, "5.5")) {
|
|
189
|
+
targetId = "gpt-5.4";
|
|
190
|
+
} else {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const fallback = models.find(
|
|
194
|
+
model => model.provider === candidate.provider && model.api === candidate.api && model.id === targetId,
|
|
195
|
+
);
|
|
196
|
+
if (!fallback) continue;
|
|
197
|
+
candidate.contextPromotionTarget = `${fallback.provider}/${fallback.id}`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Returns the supported thinking efforts declared on the model metadata.
|
|
203
|
+
*
|
|
204
|
+
* Catalog enrichment is responsible for normalizing bundled model metadata up front.
|
|
205
|
+
* Runtime callers must treat explicit `model.thinking` on custom models as authoritative
|
|
206
|
+
* so proxy-specific overrides from `models.yml` survive request construction.
|
|
207
|
+
*
|
|
208
|
+
* @throws Error when a reasoning-capable model is missing thinking metadata
|
|
209
|
+
*/
|
|
210
|
+
export function getSupportedEfforts<TApi extends Api>(model: ApiModel<TApi>): readonly Effort[] {
|
|
211
|
+
if (!model.reasoning) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
if (!model.thinking) {
|
|
215
|
+
throw new Error(`Model ${model.provider}/${model.id} is missing thinking metadata`);
|
|
216
|
+
}
|
|
217
|
+
return expandEffortRange(model.thinking);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Clamps a requested thinking level against explicit model metadata.
|
|
222
|
+
*
|
|
223
|
+
* Non-reasoning models always resolve to `undefined`.
|
|
224
|
+
*/
|
|
225
|
+
export function clampThinkingLevelForModel<TApi extends Api>(
|
|
226
|
+
model: ApiModel<TApi> | undefined,
|
|
227
|
+
requested: Effort | undefined,
|
|
228
|
+
): Effort | undefined {
|
|
229
|
+
if (!model) {
|
|
230
|
+
return requested;
|
|
231
|
+
}
|
|
232
|
+
if (!model.reasoning || requested === undefined) {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const levels = getSupportedEfforts(model);
|
|
237
|
+
if (levels.includes(requested)) {
|
|
238
|
+
return requested;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const requestedIndex = THINKING_EFFORTS.indexOf(requested);
|
|
242
|
+
if (requestedIndex === -1) {
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let clamped: Effort | undefined;
|
|
247
|
+
for (const effort of levels) {
|
|
248
|
+
if (THINKING_EFFORTS.indexOf(effort) > requestedIndex) {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
clamped = effort;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return clamped ?? levels[0];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function requireSupportedEffort<TApi extends Api>(model: ApiModel<TApi>, effort: Effort): Effort {
|
|
258
|
+
if (!model.reasoning) {
|
|
259
|
+
throw new Error(`Model ${model.provider}/${model.id} does not support thinking`);
|
|
260
|
+
}
|
|
261
|
+
const levels = getSupportedEfforts(model);
|
|
262
|
+
if (!levels.includes(effort)) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Thinking effort ${effort} is not supported by ${model.provider}/${model.id}. Supported efforts: ${levels.join(", ")}`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return effort;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Maps a normalized thinking effort to Google's `thinkingLevel` enum values. */
|
|
271
|
+
export function mapEffortToGoogleThinkingLevel<TApi extends Api>(
|
|
272
|
+
model: ApiModel<TApi>,
|
|
273
|
+
effort: Effort,
|
|
274
|
+
): "MINIMAL" | "LOW" | "MEDIUM" | "HIGH" {
|
|
275
|
+
switch (requireSupportedEffort(model, effort)) {
|
|
276
|
+
case Effort.Minimal:
|
|
277
|
+
return "MINIMAL";
|
|
278
|
+
case Effort.Low:
|
|
279
|
+
return "LOW";
|
|
280
|
+
case Effort.Medium:
|
|
281
|
+
return "MEDIUM";
|
|
282
|
+
case Effort.High:
|
|
283
|
+
case Effort.XHigh:
|
|
284
|
+
return "HIGH";
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Maps a normalized thinking effort to Anthropic adaptive effort values. */
|
|
289
|
+
export function mapEffortToAnthropicAdaptiveEffort<TApi extends Api>(
|
|
290
|
+
model: ApiModel<TApi>,
|
|
291
|
+
effort: Effort,
|
|
292
|
+
): "low" | "medium" | "high" | "xhigh" | "max" {
|
|
293
|
+
switch (requireSupportedEffort(model, effort)) {
|
|
294
|
+
case Effort.Minimal:
|
|
295
|
+
case Effort.Low:
|
|
296
|
+
return "low";
|
|
297
|
+
case Effort.Medium:
|
|
298
|
+
return "medium";
|
|
299
|
+
case Effort.High:
|
|
300
|
+
return "high";
|
|
301
|
+
case Effort.XHigh:
|
|
302
|
+
// Opus 4.7+ introduced a distinct "xhigh" effort level (between "high" and "max").
|
|
303
|
+
// The Anthropic docs scope this to the Messages API only, so Bedrock Converse and
|
|
304
|
+
// older adaptive-thinking Opus 4.6 models keep the legacy "max" alias.
|
|
305
|
+
return anthropicModelHasRealXHighEffort(model) ? "xhigh" : "max";
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Returns true for Anthropic models with Opus 4.7 API restrictions:
|
|
311
|
+
* - Sampling parameters (temperature/top_p/top_k) return 400 error
|
|
312
|
+
* - Thinking content is omitted by default (needs display: "summarized")
|
|
313
|
+
*/
|
|
314
|
+
export function hasOpus47ApiRestrictions(modelId: string): boolean {
|
|
315
|
+
const parsed = parseAnthropicModel(getCanonicalModelId(modelId));
|
|
316
|
+
if (!parsed) return false;
|
|
317
|
+
return semverGte(parsed.version, "4.7") && parsed.kind === "opus";
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function anthropicModelHasRealXHighEffort<TApi extends Api>(model: ApiModel<TApi>): boolean {
|
|
321
|
+
if (model.api !== "anthropic-messages") return false;
|
|
322
|
+
const parsedModel = parseKnownModel(model.id);
|
|
323
|
+
if (parsedModel.family !== "anthropic" || parsedModel.kind !== "opus") return false;
|
|
324
|
+
return semverGte(parsedModel.version, "4.7");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
|
|
328
|
+
const copilotLimits = model.provider === "github-copilot" ? COPILOT_GENERATED_LIMITS[model.id] : undefined;
|
|
329
|
+
if (copilotLimits) {
|
|
330
|
+
model.contextWindow = copilotLimits.contextWindow;
|
|
331
|
+
model.maxTokens = copilotLimits.maxTokens;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (
|
|
335
|
+
model.api === "openai-completions" &&
|
|
336
|
+
(model.provider === "minimax-code" || model.provider === "minimax-code-cn")
|
|
337
|
+
) {
|
|
338
|
+
model.compat = {
|
|
339
|
+
...(model.compat ?? {}),
|
|
340
|
+
supportsStore: false,
|
|
341
|
+
supportsDeveloperRole: false,
|
|
342
|
+
supportsReasoningEffort: false,
|
|
343
|
+
reasoningContentField: "reasoning_content",
|
|
344
|
+
};
|
|
345
|
+
delete model.compat.thinkingFormat;
|
|
346
|
+
}
|
|
347
|
+
model.name = scrubGeneratedModelName(model.name);
|
|
348
|
+
if (
|
|
349
|
+
model.api === "openai-completions" &&
|
|
350
|
+
model.provider === "opencode-go" &&
|
|
351
|
+
(model.id === "deepseek-v4-flash" || model.id === "deepseek-v4-pro")
|
|
352
|
+
) {
|
|
353
|
+
model.compat = {
|
|
354
|
+
...(model.compat ?? {}),
|
|
355
|
+
supportsToolChoice: false,
|
|
356
|
+
reasoningContentField: "reasoning_content",
|
|
357
|
+
requiresReasoningContentForToolCalls: true,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
const parsedModel = parseKnownModel(model.id);
|
|
361
|
+
const applyPatchToolType = inferGeneratedApplyPatchToolType(model, parsedModel);
|
|
362
|
+
if (applyPatchToolType) {
|
|
363
|
+
model.applyPatchToolType = applyPatchToolType;
|
|
364
|
+
} else {
|
|
365
|
+
delete model.applyPatchToolType;
|
|
366
|
+
}
|
|
367
|
+
if (parsedModel.family === "anthropic") {
|
|
368
|
+
applyAnthropicCatalogPolicy(model, parsedModel);
|
|
369
|
+
}
|
|
370
|
+
if (parsedModel.family === "openai") {
|
|
371
|
+
applyOpenAICatalogPolicy(model, parsedModel);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function scrubGeneratedModelName(name: string): string {
|
|
376
|
+
return name
|
|
377
|
+
.replaceAll("Claude", "Anthropic")
|
|
378
|
+
.replaceAll("claude", "anthropic")
|
|
379
|
+
.replaceAll("Codex", "OpenAI code")
|
|
380
|
+
.replaceAll("codex", "openai-code");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function applyAnthropicCatalogPolicy(model: ApiModel<Api>, parsedModel: AnthropicModel): void {
|
|
384
|
+
// Anthropic model Opus 4.5: models.dev reports 3x the correct cache pricing.
|
|
385
|
+
if (model.provider === "anthropic" && parsedModel.kind === "opus" && semverEqual(parsedModel.version, "4.5")) {
|
|
386
|
+
model.cost.cacheRead = 0.5;
|
|
387
|
+
model.cost.cacheWrite = 6.25;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Bedrock Opus 4.6: upstream metadata is stale for cache pricing and context.
|
|
391
|
+
if (model.provider === "amazon-bedrock" && parsedModel.kind === "opus" && semverEqual(parsedModel.version, "4.6")) {
|
|
392
|
+
model.cost.cacheRead = 0.5;
|
|
393
|
+
model.cost.cacheWrite = 6.25;
|
|
394
|
+
model.contextWindow = 1000000;
|
|
395
|
+
model.maxTokens = 128000;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function inferGeneratedApplyPatchToolType(
|
|
400
|
+
model: ApiModel<Api>,
|
|
401
|
+
parsedModel: ParsedModel,
|
|
402
|
+
): ApiModel<Api>["applyPatchToolType"] {
|
|
403
|
+
if (parsedModel.family !== "openai" || parsedModel.version.major !== 5) {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
if (model.provider === "openai" && model.api === "openai-responses") {
|
|
407
|
+
return "freeform";
|
|
408
|
+
}
|
|
409
|
+
if (model.provider === "openai-codex" && model.api === "openai-codex-responses") {
|
|
410
|
+
return "freeform";
|
|
411
|
+
}
|
|
412
|
+
return undefined;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function applyOpenAICatalogPolicy(model: ApiModel<Api>, parsedModel: OpenAIModel): void {
|
|
416
|
+
// OpenAI code backend models: 400K figure includes output budget; input window is 272K.
|
|
417
|
+
if (parsedModel.variant.startsWith("codex") && parsedModel.variant !== "codex-spark") {
|
|
418
|
+
model.contextWindow = 272000;
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
// GPT-5.4 mini/nano use plain OpenAI IDs on the OpenAI code backend transport, but OpenAI code backend still
|
|
422
|
+
// enforces the lower prompt budget for these variants. OpenAI code backend discovery can also
|
|
423
|
+
// report inconsistent priorities for the GPT-5.4 family, so normalize by parsed
|
|
424
|
+
// variant instead of special-casing raw model ids.
|
|
425
|
+
if (model.api === "openai-codex-responses" && semverEqual(parsedModel.version, "5.4")) {
|
|
426
|
+
const normalizedPriority = CODEX_GPT_5_4_PRIORITY_BY_VARIANT[parsedModel.variant];
|
|
427
|
+
if (normalizedPriority !== undefined) {
|
|
428
|
+
model.priority = normalizedPriority;
|
|
429
|
+
}
|
|
430
|
+
if (parsedModel.variant === "mini" || parsedModel.variant === "nano") {
|
|
431
|
+
model.contextWindow = 272000;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function inferModelThinking<TApi extends Api>(model: ApiModel<TApi>): ThinkingConfig {
|
|
437
|
+
const parsedModel = parseKnownModel(model.id);
|
|
438
|
+
const efforts = inferSupportedEfforts(parsedModel, model);
|
|
439
|
+
const minLevel = efforts[0];
|
|
440
|
+
const maxLevel = efforts.at(-1);
|
|
441
|
+
if (!minLevel || !maxLevel) {
|
|
442
|
+
throw new Error(`Model ${model.provider}/${model.id} resolved to an empty thinking range`);
|
|
443
|
+
}
|
|
444
|
+
const config: ThinkingConfig = {
|
|
445
|
+
mode: inferThinkingControlMode(model, parsedModel),
|
|
446
|
+
minLevel,
|
|
447
|
+
maxLevel,
|
|
448
|
+
};
|
|
449
|
+
// Encode explicit levels only when the inferred set has gaps the min..max range cannot represent.
|
|
450
|
+
const minIndex = THINKING_EFFORTS.indexOf(minLevel);
|
|
451
|
+
const maxIndex = THINKING_EFFORTS.indexOf(maxLevel);
|
|
452
|
+
const expandedRange = THINKING_EFFORTS.slice(minIndex, maxIndex + 1);
|
|
453
|
+
if (expandedRange.length !== efforts.length) {
|
|
454
|
+
config.levels = efforts;
|
|
455
|
+
}
|
|
456
|
+
return config;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function normalizeThinkingConfig(thinking: ThinkingConfig | undefined): ThinkingConfig | undefined {
|
|
460
|
+
if (!thinking || expandEffortRange(thinking).length === 0) {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
return thinking;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function thinkingsEqual(left: ThinkingConfig | undefined, right: ThinkingConfig | undefined): boolean {
|
|
467
|
+
if (left === right) return true;
|
|
468
|
+
if (!left || !right) return false;
|
|
469
|
+
if (left.mode !== right.mode || left.minLevel !== right.minLevel || left.maxLevel !== right.maxLevel) return false;
|
|
470
|
+
const leftLevels = left.levels;
|
|
471
|
+
const rightLevels = right.levels;
|
|
472
|
+
if (leftLevels === rightLevels) return true;
|
|
473
|
+
if (!leftLevels || !rightLevels) return false;
|
|
474
|
+
if (leftLevels.length !== rightLevels.length) return false;
|
|
475
|
+
return leftLevels.every((level, index) => level === rightLevels[index]);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function expandEffortRange(thinking: ThinkingConfig): readonly Effort[] {
|
|
479
|
+
if (thinking.levels && thinking.levels.length > 0) {
|
|
480
|
+
return thinking.levels;
|
|
481
|
+
}
|
|
482
|
+
const minIndex = THINKING_EFFORTS.indexOf(thinking.minLevel);
|
|
483
|
+
const maxIndex = THINKING_EFFORTS.indexOf(thinking.maxLevel);
|
|
484
|
+
if (minIndex === -1 || maxIndex === -1 || minIndex > maxIndex) {
|
|
485
|
+
return [];
|
|
486
|
+
}
|
|
487
|
+
return THINKING_EFFORTS.slice(minIndex, maxIndex + 1);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function inferSupportedEfforts<TApi extends Api>(parsedModel: ParsedModel, model: ApiModel<TApi>): readonly Effort[] {
|
|
491
|
+
switch (parsedModel.family) {
|
|
492
|
+
case "openai":
|
|
493
|
+
return inferOpenAISupportedEfforts(parsedModel);
|
|
494
|
+
case "gemini":
|
|
495
|
+
return inferGeminiSupportedEfforts(parsedModel);
|
|
496
|
+
case "anthropic":
|
|
497
|
+
return inferAnthropicSupportedEfforts(parsedModel, model);
|
|
498
|
+
case "unknown":
|
|
499
|
+
return inferFallbackEfforts(model);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function inferOpenAISupportedEfforts(model: OpenAIModel): readonly Effort[] {
|
|
504
|
+
if (model.variant === "codex-mini" && semverEqual(model.version, "5.1")) {
|
|
505
|
+
return GPT_5_1_CODEX_MINI_EFFORTS;
|
|
506
|
+
}
|
|
507
|
+
if (semverGte(model.version, "5.2")) {
|
|
508
|
+
return GPT_5_2_PLUS_EFFORTS;
|
|
509
|
+
}
|
|
510
|
+
return DEFAULT_REASONING_EFFORTS;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function inferGeminiSupportedEfforts(model: GeminiModel): readonly Effort[] {
|
|
514
|
+
if (!semverGte(model.version, "3.0")) {
|
|
515
|
+
return DEFAULT_REASONING_EFFORTS;
|
|
516
|
+
}
|
|
517
|
+
return model.kind === "pro" ? GEMINI_3_PRO_EFFORTS : GEMINI_3_FLASH_EFFORTS;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function inferAnthropicSupportedEfforts<TApi extends Api>(
|
|
521
|
+
parsedModel: AnthropicModel,
|
|
522
|
+
model: ApiModel<TApi>,
|
|
523
|
+
): readonly Effort[] {
|
|
524
|
+
if (
|
|
525
|
+
(model.api === "anthropic-messages" || model.api === "bedrock-converse-stream") &&
|
|
526
|
+
semverGte(parsedModel.version, "4.6")
|
|
527
|
+
) {
|
|
528
|
+
return parsedModel.kind === "opus" ? DEFAULT_REASONING_EFFORTS_WITH_XHIGH : DEFAULT_REASONING_EFFORTS;
|
|
529
|
+
}
|
|
530
|
+
return inferFallbackEfforts(model);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function inferFallbackEfforts<TApi extends Api>(model: ApiModel<TApi>): readonly Effort[] {
|
|
534
|
+
if (model.api === "anthropic-messages") {
|
|
535
|
+
return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
536
|
+
}
|
|
537
|
+
if (model.name.includes("deepseek-v4")) {
|
|
538
|
+
return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
539
|
+
}
|
|
540
|
+
if (model.api === "bedrock-converse-stream") {
|
|
541
|
+
return DEFAULT_REASONING_EFFORTS;
|
|
542
|
+
}
|
|
543
|
+
if (model.api === "openai-completions") {
|
|
544
|
+
const compat = resolveOpenAICompat(model as ApiModel<"openai-completions">);
|
|
545
|
+
if (compat.thinkingFormat === "openai" && compat.supportsReasoningEffort) {
|
|
546
|
+
return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
547
|
+
}
|
|
548
|
+
return DEFAULT_REASONING_EFFORTS;
|
|
549
|
+
}
|
|
550
|
+
// OpenAI Responses APIs encode discrete effort levels, including xhigh.
|
|
551
|
+
if (model.api === "openai-responses" || model.api === "openai-codex-responses") {
|
|
552
|
+
return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
553
|
+
}
|
|
554
|
+
return DEFAULT_REASONING_EFFORTS;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function inferThinkingControlMode<TApi extends Api>(
|
|
558
|
+
model: ApiModel<TApi>,
|
|
559
|
+
parsedModel: ParsedModel,
|
|
560
|
+
): ThinkingConfig["mode"] {
|
|
561
|
+
switch (model.api) {
|
|
562
|
+
case "google-generative-ai":
|
|
563
|
+
case "google-gemini-cli":
|
|
564
|
+
case "google-vertex":
|
|
565
|
+
return parsedModel.family === "gemini" &&
|
|
566
|
+
semverGte(parsedModel.version, "3.0") &&
|
|
567
|
+
parsedModel.version.major === 3
|
|
568
|
+
? "google-level"
|
|
569
|
+
: "budget";
|
|
570
|
+
|
|
571
|
+
case "anthropic-messages":
|
|
572
|
+
if (parsedModel.family === "anthropic") {
|
|
573
|
+
if (semverGte(parsedModel.version, "4.6")) {
|
|
574
|
+
return "anthropic-adaptive";
|
|
575
|
+
}
|
|
576
|
+
if (semverGte(parsedModel.version, "4.5")) {
|
|
577
|
+
return "anthropic-budget-effort";
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return "budget";
|
|
581
|
+
|
|
582
|
+
case "bedrock-converse-stream":
|
|
583
|
+
if (parsedModel.family === "anthropic") {
|
|
584
|
+
if (semverGte(parsedModel.version, "4.6") && parsedModel.kind === "opus") {
|
|
585
|
+
return "anthropic-adaptive";
|
|
586
|
+
}
|
|
587
|
+
if (semverGte(parsedModel.version, "4.5")) {
|
|
588
|
+
return "anthropic-budget-effort";
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return "budget";
|
|
592
|
+
|
|
593
|
+
default:
|
|
594
|
+
return "effort";
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function parseKnownModel(modelId: string): ParsedModel {
|
|
599
|
+
const canonicalId = getCanonicalModelId(modelId);
|
|
600
|
+
return (
|
|
601
|
+
parseGeminiModel(canonicalId) ??
|
|
602
|
+
parseAnthropicModel(canonicalId) ??
|
|
603
|
+
parseOpenAIModel(canonicalId) ?? { family: "unknown", id: canonicalId }
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const GEMINI_SUFFIX = "-preview";
|
|
608
|
+
function parseGeminiModel(modelId: string): GeminiModel | null {
|
|
609
|
+
if (modelId.endsWith(GEMINI_SUFFIX)) {
|
|
610
|
+
modelId = modelId.slice(0, -GEMINI_SUFFIX.length);
|
|
611
|
+
}
|
|
612
|
+
const match = /gemini-(\d+(?:\.\d+){0,2})-(pro|flash)\b/.exec(modelId);
|
|
613
|
+
if (!match) {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
const version = parseSemVer(match[1]);
|
|
617
|
+
if (!version) {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
return { family: "gemini", kind: match[2] as GeminiKind, version };
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function parseAnthropicModel(modelId: string): AnthropicModel | null {
|
|
624
|
+
const match = /claude-(opus|sonnet)-(\d{1,2}(?:[.-]\d{1,2}){0,2})\b/.exec(modelId);
|
|
625
|
+
if (!match) {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
const version = parseSemVer(match[2]);
|
|
629
|
+
if (!version) {
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
return { family: "anthropic", kind: match[1] as AnthropicKind, version };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function parseOpenAIModel(modelId: string): OpenAIModel | null {
|
|
636
|
+
const match = /gpt-(\d+(?:\.\d+){0,2})(?:-(codex-spark|codex-mini|codex-max|codex|mini|max|nano))?\b/.exec(modelId);
|
|
637
|
+
if (!match) {
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
const version = parseSemVer(match[1]);
|
|
641
|
+
if (!version) {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
return { family: "openai", variant: (match[2] as OpenAIVariant | undefined) ?? "base", version };
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function createSemVer(major: number, minor: number, patch = 0): SemVer {
|
|
648
|
+
return { major, minor, patch };
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// extend this table if we need anything more than 9.10
|
|
652
|
+
const precomputeTable: Record<string, SemVer> = {};
|
|
653
|
+
for (let major = 0; major <= 9; major++) {
|
|
654
|
+
for (let minor = 0; minor <= 10; minor++) {
|
|
655
|
+
const version = createSemVer(major, minor, 0);
|
|
656
|
+
precomputeTable[`${major}.${minor}`] = version;
|
|
657
|
+
precomputeTable[`${major}-${minor}`] = version;
|
|
658
|
+
}
|
|
659
|
+
precomputeTable[`${major}`] = createSemVer(major, 0, 0);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function parseSemVer(version: string): SemVer | null {
|
|
663
|
+
return precomputeTable[version] ?? null;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function semverGte(left: SemVer | string, right: SemVer | string): boolean {
|
|
667
|
+
return compareSemVer(left, right) >= 0;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function semverEqual(left: SemVer | string, right: SemVer | string): boolean {
|
|
671
|
+
return compareSemVer(left, right) === 0;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function compareSemVer(left: SemVer | string | null, right: SemVer | string | null): number {
|
|
675
|
+
left = typeof left === "string" ? parseSemVer(left) : left;
|
|
676
|
+
right = typeof right === "string" ? parseSemVer(right) : right;
|
|
677
|
+
if (!left || !right) return (left ? 1 : 0) - (right ? 1 : 0);
|
|
678
|
+
|
|
679
|
+
if (left.major !== right.major) {
|
|
680
|
+
return left.major - right.major;
|
|
681
|
+
}
|
|
682
|
+
if (left.minor !== right.minor) {
|
|
683
|
+
return left.minor - right.minor;
|
|
684
|
+
}
|
|
685
|
+
return left.patch - right.patch;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function getCanonicalModelId(modelId: string): string {
|
|
689
|
+
const p = modelId.lastIndexOf("/");
|
|
690
|
+
return p !== -1 ? modelId.slice(p + 1) : modelId;
|
|
691
|
+
}
|