@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
package/src/usage/zai.ts
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
UsageAmount,
|
|
3
|
+
UsageFetchContext,
|
|
4
|
+
UsageFetchParams,
|
|
5
|
+
UsageLimit,
|
|
6
|
+
UsageProvider,
|
|
7
|
+
UsageReport,
|
|
8
|
+
UsageStatus,
|
|
9
|
+
UsageWindow,
|
|
10
|
+
} from "../usage";
|
|
11
|
+
import { isRecord, toNumber } from "../utils";
|
|
12
|
+
|
|
13
|
+
const DEFAULT_ENDPOINT = "https://api.z.ai";
|
|
14
|
+
const QUOTA_PATH = "/api/monitor/usage/quota/limit";
|
|
15
|
+
const MODEL_USAGE_PATH = "/api/monitor/usage/model-usage";
|
|
16
|
+
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
17
|
+
|
|
18
|
+
function normalizeZaiBaseUrl(baseUrl?: string): string {
|
|
19
|
+
if (!baseUrl?.trim()) return DEFAULT_ENDPOINT;
|
|
20
|
+
try {
|
|
21
|
+
return new URL(baseUrl.trim()).origin;
|
|
22
|
+
} catch {
|
|
23
|
+
return DEFAULT_ENDPOINT;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ZaiUsageLimitItem {
|
|
28
|
+
type?: string;
|
|
29
|
+
usage?: number;
|
|
30
|
+
currentValue?: number;
|
|
31
|
+
percentage?: number;
|
|
32
|
+
remaining?: number;
|
|
33
|
+
nextResetTime?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ZaiQuotaPayload {
|
|
37
|
+
success?: boolean;
|
|
38
|
+
code?: number;
|
|
39
|
+
msg?: string;
|
|
40
|
+
data?: {
|
|
41
|
+
limits?: ZaiUsageLimitItem[];
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseMillis(value: unknown): number | undefined {
|
|
46
|
+
const parsed = toNumber(value);
|
|
47
|
+
if (parsed === undefined) return undefined;
|
|
48
|
+
return parsed > 1_000_000_000_000 ? parsed : parsed * 1000;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseLimitItem(value: unknown): ZaiUsageLimitItem | null {
|
|
52
|
+
if (!isRecord(value)) return null;
|
|
53
|
+
const type = typeof value.type === "string" ? value.type : undefined;
|
|
54
|
+
if (!type) return null;
|
|
55
|
+
return {
|
|
56
|
+
type,
|
|
57
|
+
usage: toNumber(value.usage),
|
|
58
|
+
currentValue: toNumber(value.currentValue),
|
|
59
|
+
percentage: toNumber(value.percentage),
|
|
60
|
+
remaining: toNumber(value.remaining),
|
|
61
|
+
nextResetTime: parseMillis(value.nextResetTime),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildUsageAmount(args: {
|
|
66
|
+
used: number | undefined;
|
|
67
|
+
limit: number | undefined;
|
|
68
|
+
remaining: number | undefined;
|
|
69
|
+
unit: UsageAmount["unit"];
|
|
70
|
+
percentage?: number;
|
|
71
|
+
}): UsageAmount {
|
|
72
|
+
const usedFraction =
|
|
73
|
+
args.percentage !== undefined
|
|
74
|
+
? Math.min(Math.max(args.percentage / 100, 0), 1)
|
|
75
|
+
: args.used !== undefined && args.limit !== undefined && args.limit > 0
|
|
76
|
+
? Math.min(args.used / args.limit, 1)
|
|
77
|
+
: undefined;
|
|
78
|
+
const remainingFraction = usedFraction !== undefined ? Math.max(1 - usedFraction, 0) : undefined;
|
|
79
|
+
return {
|
|
80
|
+
used: args.used,
|
|
81
|
+
limit: args.limit,
|
|
82
|
+
remaining: args.remaining,
|
|
83
|
+
usedFraction,
|
|
84
|
+
remainingFraction,
|
|
85
|
+
unit: args.unit,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getUsageStatus(usedFraction: number | undefined): UsageStatus | undefined {
|
|
90
|
+
if (usedFraction === undefined) return undefined;
|
|
91
|
+
if (usedFraction >= 1) return "exhausted";
|
|
92
|
+
if (usedFraction >= 0.9) return "warning";
|
|
93
|
+
return "ok";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function formatDate(value: Date): string {
|
|
97
|
+
const pad = (input: number) => String(input).padStart(2, "0");
|
|
98
|
+
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}+${pad(value.getHours())}:${pad(
|
|
99
|
+
value.getMinutes(),
|
|
100
|
+
)}:${pad(value.getSeconds())}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function buildModelUsageUrl(baseUrl: string, now: Date): string {
|
|
104
|
+
const start = new Date(now.getTime() - SEVEN_DAYS_MS);
|
|
105
|
+
const startTime = formatDate(start);
|
|
106
|
+
const endTime = formatDate(now);
|
|
107
|
+
return `${baseUrl}${MODEL_USAGE_PATH}?startTime=${encodeURIComponent(startTime)}&endTime=${encodeURIComponent(endTime)}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function fetchZaiUsage(params: UsageFetchParams, ctx: UsageFetchContext): Promise<UsageReport | null> {
|
|
111
|
+
if (params.provider !== "zai") return null;
|
|
112
|
+
const credential = params.credential;
|
|
113
|
+
if (credential.type !== "api_key" || !credential.apiKey) return null;
|
|
114
|
+
|
|
115
|
+
const baseUrl = normalizeZaiBaseUrl(params.baseUrl);
|
|
116
|
+
const url = `${baseUrl}${QUOTA_PATH}`;
|
|
117
|
+
const headers: Record<string, string> = {
|
|
118
|
+
Authorization: credential.apiKey,
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
"User-Agent": "OpenCode-Status-Plugin/1.0",
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
let payload: ZaiQuotaPayload | null = null;
|
|
124
|
+
try {
|
|
125
|
+
const response = await ctx.fetch(url, {
|
|
126
|
+
headers,
|
|
127
|
+
signal: params.signal,
|
|
128
|
+
});
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
ctx.logger?.warn("ZAI usage fetch failed", { status: response.status, statusText: response.statusText });
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
payload = (await response.json()) as ZaiQuotaPayload;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
ctx.logger?.warn("ZAI usage fetch error", { error: String(error) });
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!payload) return null;
|
|
140
|
+
if (payload.success !== true) {
|
|
141
|
+
ctx.logger?.warn("ZAI usage response invalid", { code: payload.code, message: payload.msg });
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const limitsPayload = Array.isArray(payload.data?.limits) ? payload.data?.limits : [];
|
|
146
|
+
const limits: UsageLimit[] = [];
|
|
147
|
+
|
|
148
|
+
for (const rawLimit of limitsPayload) {
|
|
149
|
+
const parsed = parseLimitItem(rawLimit);
|
|
150
|
+
if (!parsed) continue;
|
|
151
|
+
if (parsed.type === "TOKENS_LIMIT") {
|
|
152
|
+
const amount = buildUsageAmount({
|
|
153
|
+
used: parsed.currentValue,
|
|
154
|
+
limit: parsed.usage,
|
|
155
|
+
remaining: parsed.remaining,
|
|
156
|
+
percentage: parsed.percentage,
|
|
157
|
+
unit: "tokens",
|
|
158
|
+
});
|
|
159
|
+
const window: UsageWindow = {
|
|
160
|
+
id: "quota",
|
|
161
|
+
label: "Quota",
|
|
162
|
+
durationMs: SEVEN_DAYS_MS,
|
|
163
|
+
resetsAt: parsed.nextResetTime,
|
|
164
|
+
};
|
|
165
|
+
limits.push({
|
|
166
|
+
id: "zai:tokens",
|
|
167
|
+
label: "ZAI Token Quota",
|
|
168
|
+
scope: {
|
|
169
|
+
provider: params.provider,
|
|
170
|
+
windowId: window?.id ?? "quota",
|
|
171
|
+
shared: true,
|
|
172
|
+
},
|
|
173
|
+
window,
|
|
174
|
+
amount,
|
|
175
|
+
status: getUsageStatus(amount.usedFraction),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (parsed.type === "TIME_LIMIT") {
|
|
179
|
+
const window: UsageWindow = {
|
|
180
|
+
id: "quota",
|
|
181
|
+
label: "Quota",
|
|
182
|
+
durationMs: SEVEN_DAYS_MS,
|
|
183
|
+
resetsAt: parsed.nextResetTime,
|
|
184
|
+
};
|
|
185
|
+
const amount = buildUsageAmount({
|
|
186
|
+
used: parsed.currentValue,
|
|
187
|
+
limit: parsed.usage,
|
|
188
|
+
remaining: parsed.remaining,
|
|
189
|
+
percentage: parsed.percentage,
|
|
190
|
+
unit: "requests",
|
|
191
|
+
});
|
|
192
|
+
limits.push({
|
|
193
|
+
id: "zai:requests",
|
|
194
|
+
label: "ZAI Request Quota",
|
|
195
|
+
scope: {
|
|
196
|
+
provider: params.provider,
|
|
197
|
+
windowId: "quota",
|
|
198
|
+
shared: true,
|
|
199
|
+
},
|
|
200
|
+
window,
|
|
201
|
+
amount,
|
|
202
|
+
status: getUsageStatus(amount.usedFraction),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (limits.length === 0) return null;
|
|
208
|
+
|
|
209
|
+
const report: UsageReport = {
|
|
210
|
+
provider: params.provider,
|
|
211
|
+
fetchedAt: Date.now(),
|
|
212
|
+
limits,
|
|
213
|
+
metadata: {
|
|
214
|
+
endpoint: url,
|
|
215
|
+
accountId: credential.accountId,
|
|
216
|
+
email: credential.email,
|
|
217
|
+
},
|
|
218
|
+
raw: payload,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const modelUsageUrl = buildModelUsageUrl(baseUrl, new Date());
|
|
222
|
+
try {
|
|
223
|
+
const response = await ctx.fetch(modelUsageUrl, {
|
|
224
|
+
headers,
|
|
225
|
+
signal: params.signal,
|
|
226
|
+
});
|
|
227
|
+
if (response.ok) {
|
|
228
|
+
const modelUsagePayload = (await response.json()) as unknown;
|
|
229
|
+
if (isRecord(modelUsagePayload)) {
|
|
230
|
+
report.metadata = {
|
|
231
|
+
...report.metadata,
|
|
232
|
+
modelUsage: modelUsagePayload,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
ctx.logger?.debug("ZAI model usage fetch failed", { error: String(error) });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return report;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export const zaiUsageProvider: UsageProvider = {
|
|
244
|
+
id: "zai",
|
|
245
|
+
fetchUsage: fetchZaiUsage,
|
|
246
|
+
supports: params => params.provider === "zai" && params.credential.type === "api_key",
|
|
247
|
+
};
|
package/src/usage.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage reporting types for provider quota/limit endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Provides a normalized schema to represent multiple limit windows, model tiers,
|
|
5
|
+
* and shared quotas across providers.
|
|
6
|
+
*/
|
|
7
|
+
import * as z from "zod/v4";
|
|
8
|
+
import type { Provider } from "./types";
|
|
9
|
+
export type UsageUnit = "percent" | "tokens" | "requests" | "usd" | "minutes" | "bytes" | "unknown";
|
|
10
|
+
|
|
11
|
+
export type UsageStatus = "ok" | "warning" | "exhausted" | "unknown";
|
|
12
|
+
|
|
13
|
+
/** Time window for a limit (e.g. 5h, 7d, monthly). */
|
|
14
|
+
export interface UsageWindow {
|
|
15
|
+
/** Stable identifier (e.g. "5h", "7d", "monthly"). */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Human label (e.g. "5 Hour", "7 Day"). */
|
|
18
|
+
label: string;
|
|
19
|
+
/** Window duration in milliseconds, when known. */
|
|
20
|
+
durationMs?: number;
|
|
21
|
+
/** Absolute reset timestamp in milliseconds since epoch. */
|
|
22
|
+
resetsAt?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Quantitative usage data. */
|
|
26
|
+
export interface UsageAmount {
|
|
27
|
+
/** Amount used in the given unit. */
|
|
28
|
+
used?: number;
|
|
29
|
+
/** Maximum limit in the given unit. */
|
|
30
|
+
limit?: number;
|
|
31
|
+
/** Remaining amount in the given unit. */
|
|
32
|
+
remaining?: number;
|
|
33
|
+
/** Fraction used (0..1). */
|
|
34
|
+
usedFraction?: number;
|
|
35
|
+
/** Fraction remaining (0..1). */
|
|
36
|
+
remainingFraction?: number;
|
|
37
|
+
/** Unit for the amounts (percent, tokens, etc.). */
|
|
38
|
+
unit: UsageUnit;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Scope metadata describing what the limit applies to. */
|
|
42
|
+
export interface UsageScope {
|
|
43
|
+
provider: Provider;
|
|
44
|
+
accountId?: string;
|
|
45
|
+
projectId?: string;
|
|
46
|
+
orgId?: string;
|
|
47
|
+
modelId?: string;
|
|
48
|
+
tier?: string;
|
|
49
|
+
windowId?: string;
|
|
50
|
+
shared?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Normalized limit entry for a single window or quota bucket. */
|
|
54
|
+
export interface UsageLimit {
|
|
55
|
+
/** Stable identifier for this limit entry. */
|
|
56
|
+
id: string;
|
|
57
|
+
/** Human label for display. */
|
|
58
|
+
label: string;
|
|
59
|
+
scope: UsageScope;
|
|
60
|
+
window?: UsageWindow;
|
|
61
|
+
amount: UsageAmount;
|
|
62
|
+
status?: UsageStatus;
|
|
63
|
+
notes?: string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Aggregated usage report for a provider. */
|
|
67
|
+
export interface UsageReport {
|
|
68
|
+
provider: Provider;
|
|
69
|
+
fetchedAt: number;
|
|
70
|
+
limits: UsageLimit[];
|
|
71
|
+
metadata?: Record<string, unknown>;
|
|
72
|
+
raw?: unknown;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── Zod schemas (wire-shape validation for the broker `/v1/usage` endpoint) ─
|
|
76
|
+
|
|
77
|
+
export const usageUnitSchema = z.enum(["percent", "tokens", "requests", "usd", "minutes", "bytes", "unknown"]);
|
|
78
|
+
export const usageStatusSchema = z.enum(["ok", "warning", "exhausted", "unknown"]);
|
|
79
|
+
|
|
80
|
+
export const usageWindowSchema = z.object({
|
|
81
|
+
id: z.string(),
|
|
82
|
+
label: z.string(),
|
|
83
|
+
durationMs: z.number().optional(),
|
|
84
|
+
resetsAt: z.number().optional(),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export const usageAmountSchema = z.object({
|
|
88
|
+
used: z.number().optional(),
|
|
89
|
+
limit: z.number().optional(),
|
|
90
|
+
remaining: z.number().optional(),
|
|
91
|
+
usedFraction: z.number().optional(),
|
|
92
|
+
remainingFraction: z.number().optional(),
|
|
93
|
+
unit: usageUnitSchema,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export const usageScopeSchema = z.object({
|
|
97
|
+
provider: z.string(),
|
|
98
|
+
accountId: z.string().optional(),
|
|
99
|
+
projectId: z.string().optional(),
|
|
100
|
+
orgId: z.string().optional(),
|
|
101
|
+
modelId: z.string().optional(),
|
|
102
|
+
tier: z.string().optional(),
|
|
103
|
+
windowId: z.string().optional(),
|
|
104
|
+
shared: z.boolean().optional(),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export const usageLimitSchema = z.object({
|
|
108
|
+
id: z.string(),
|
|
109
|
+
label: z.string(),
|
|
110
|
+
scope: usageScopeSchema,
|
|
111
|
+
window: usageWindowSchema.optional(),
|
|
112
|
+
amount: usageAmountSchema,
|
|
113
|
+
status: usageStatusSchema.optional(),
|
|
114
|
+
notes: z.array(z.string()).optional(),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export const usageReportSchema = z.object({
|
|
118
|
+
provider: z.string(),
|
|
119
|
+
fetchedAt: z.number(),
|
|
120
|
+
limits: z.array(usageLimitSchema),
|
|
121
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
122
|
+
// `raw` is provider-specific and may be anything; the broker strips it before
|
|
123
|
+
// sending the report over the wire, so accept-but-ignore here.
|
|
124
|
+
raw: z.unknown().optional(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/** Optional logger for usage fetchers. */
|
|
128
|
+
export interface UsageLogger {
|
|
129
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
130
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Credential bundle for usage endpoints. */
|
|
134
|
+
export interface UsageCredential {
|
|
135
|
+
type: "api_key" | "oauth";
|
|
136
|
+
apiKey?: string;
|
|
137
|
+
accessToken?: string;
|
|
138
|
+
refreshToken?: string;
|
|
139
|
+
expiresAt?: number;
|
|
140
|
+
accountId?: string;
|
|
141
|
+
projectId?: string;
|
|
142
|
+
email?: string;
|
|
143
|
+
enterpriseUrl?: string;
|
|
144
|
+
metadata?: Record<string, unknown>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Parameters provided to a usage fetcher. */
|
|
148
|
+
export interface UsageFetchParams {
|
|
149
|
+
provider: Provider;
|
|
150
|
+
credential: UsageCredential;
|
|
151
|
+
baseUrl?: string;
|
|
152
|
+
signal?: AbortSignal;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Shared runtime utilities for fetchers. */
|
|
156
|
+
export interface UsageFetchContext {
|
|
157
|
+
fetch: typeof fetch;
|
|
158
|
+
logger?: UsageLogger;
|
|
159
|
+
retryWait?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Provider implementation for fetching usage information. */
|
|
163
|
+
export interface UsageProvider {
|
|
164
|
+
id: Provider;
|
|
165
|
+
fetchUsage(params: UsageFetchParams, ctx: UsageFetchContext): Promise<UsageReport | null>;
|
|
166
|
+
supports?(params: UsageFetchParams): boolean;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Strategy for usage-based credential ranking. Providers implement this to opt into smart credential selection. */
|
|
170
|
+
export interface CredentialRankingStrategy {
|
|
171
|
+
/** Extract the primary (short) and secondary (long) window limits from a usage report. */
|
|
172
|
+
findWindowLimits(report: UsageReport): {
|
|
173
|
+
primary?: UsageLimit;
|
|
174
|
+
secondary?: UsageLimit;
|
|
175
|
+
};
|
|
176
|
+
/** Fallback window durations (ms) when limits don't specify durationMs. */
|
|
177
|
+
windowDefaults: {
|
|
178
|
+
primaryMs: number;
|
|
179
|
+
secondaryMs: number;
|
|
180
|
+
};
|
|
181
|
+
/** Optional: priority boost for specific credential states (e.g., fresh 5h ticker start). */
|
|
182
|
+
hasPriorityBoost?(primary: UsageLimit | undefined): boolean;
|
|
183
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface AbortSourceTracker {
|
|
2
|
+
requestAbortController: AbortController;
|
|
3
|
+
requestSignal: AbortSignal;
|
|
4
|
+
abortLocally(reason: Error): Error;
|
|
5
|
+
getLocalAbortReason(): Error | undefined;
|
|
6
|
+
wasCallerAbort(): boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tracks whether a merged request signal was aborted by the caller or by provider-local logic.
|
|
11
|
+
*
|
|
12
|
+
* Caller aborts always take priority. When both the caller and a local watchdog fire near
|
|
13
|
+
* each other, the merged `requestSignal.reason` reflects whichever AbortController called
|
|
14
|
+
* `.abort()` first — but ordering is racy and not meaningful for upstream consumers. What
|
|
15
|
+
* matters is intent: if the caller's signal aborted, the request was cancelled by the
|
|
16
|
+
* caller, and any local watchdog reason is incidental and **MUST NOT** be surfaced as a
|
|
17
|
+
* retryable transient error (which would cause auto-retry to re-enter streaming and leave
|
|
18
|
+
* the UI showing a spinner the user already tried to cancel).
|
|
19
|
+
*/
|
|
20
|
+
export function createAbortSourceTracker(callerSignal?: AbortSignal): AbortSourceTracker {
|
|
21
|
+
const requestAbortController = new AbortController();
|
|
22
|
+
const requestSignal = callerSignal
|
|
23
|
+
? AbortSignal.any([callerSignal, requestAbortController.signal])
|
|
24
|
+
: requestAbortController.signal;
|
|
25
|
+
let localAbortReason: Error | undefined;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
requestAbortController,
|
|
29
|
+
requestSignal,
|
|
30
|
+
abortLocally(reason) {
|
|
31
|
+
localAbortReason = reason;
|
|
32
|
+
requestAbortController.abort(reason);
|
|
33
|
+
return reason;
|
|
34
|
+
},
|
|
35
|
+
getLocalAbortReason() {
|
|
36
|
+
// Caller intent dominates. Surface a local reason only when the caller did not
|
|
37
|
+
// abort, so timeout/idle-watchdog errors don't masquerade as the user's cancel.
|
|
38
|
+
if (!localAbortReason || callerSignal?.aborted) return undefined;
|
|
39
|
+
return requestSignal.reason === localAbortReason ? localAbortReason : undefined;
|
|
40
|
+
},
|
|
41
|
+
wasCallerAbort() {
|
|
42
|
+
// If the caller signal aborted, treat it as a caller abort regardless of which
|
|
43
|
+
// AbortController won the race to set `requestSignal.reason`. The previous
|
|
44
|
+
// `requestSignal.reason !== localAbortReason` heuristic flipped the result to
|
|
45
|
+
// `false` when a local watchdog fired microseconds before the user's ESC, which
|
|
46
|
+
// then routed user-initiated cancels through the auto-retry transient-error
|
|
47
|
+
// path.
|
|
48
|
+
return callerSignal?.aborted === true;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Authentication
|
|
3
|
+
*
|
|
4
|
+
* Thin helper for turning an already-resolved API key into the request-shaping
|
|
5
|
+
* config consumed by {@link buildAnthropicSearchHeaders} / {@link buildAnthropicUrl}.
|
|
6
|
+
*
|
|
7
|
+
* Credential storage and refresh live in `AuthStorage` — call
|
|
8
|
+
* `authStorage.getApiKey("anthropic", sessionId)` first, then pass the result
|
|
9
|
+
* through {@link buildAnthropicAuthConfig} for header/URL shaping.
|
|
10
|
+
*/
|
|
11
|
+
import { $env } from "@gajae-code/utils";
|
|
12
|
+
import {
|
|
13
|
+
buildAnthropicHeaders as buildProviderAnthropicHeaders,
|
|
14
|
+
normalizeAnthropicBaseUrl,
|
|
15
|
+
} from "../providers/anthropic";
|
|
16
|
+
import { isFoundryEnabled } from "./foundry";
|
|
17
|
+
|
|
18
|
+
/** Auth configuration for Anthropic */
|
|
19
|
+
export interface AnthropicAuthConfig {
|
|
20
|
+
apiKey: string;
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
isOAuth: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
26
|
+
|
|
27
|
+
function normalizeBaseUrl(baseUrl: string | undefined): string | undefined {
|
|
28
|
+
const trimmed = baseUrl?.trim();
|
|
29
|
+
return trimmed ? trimmed.replace(/\/+$/, "") : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resolveAnthropicBaseUrlFromEnv(): string | undefined {
|
|
33
|
+
if (isFoundryEnabled()) {
|
|
34
|
+
const foundryBaseUrl = normalizeBaseUrl($env.FOUNDRY_BASE_URL);
|
|
35
|
+
if (foundryBaseUrl) return foundryBaseUrl;
|
|
36
|
+
}
|
|
37
|
+
const anthropicBaseUrl = normalizeBaseUrl($env.ANTHROPIC_BASE_URL);
|
|
38
|
+
return anthropicBaseUrl || undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Checks if a token is an OAuth token by looking for sk-ant-oat prefix.
|
|
43
|
+
*/
|
|
44
|
+
export function isOAuthToken(apiKey: string): boolean {
|
|
45
|
+
return apiKey.includes("sk-ant-oat");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build an {@link AnthropicAuthConfig} from an already-resolved API key.
|
|
50
|
+
*
|
|
51
|
+
* `apiKey` is whatever the caller chose for `Authorization`/`x-api-key` —
|
|
52
|
+
* usually `authStorage.getApiKey("anthropic")`. `baseUrl` overrides the
|
|
53
|
+
* env-derived base; pass `undefined` to fall back to FOUNDRY/ANTHROPIC env
|
|
54
|
+
* resolution and finally `DEFAULT_BASE_URL`.
|
|
55
|
+
*
|
|
56
|
+
* `isOAuth` is derived from the token prefix so the helper stays pure: callers
|
|
57
|
+
* never have to thread the OAuth flag through their own resolution logic.
|
|
58
|
+
*/
|
|
59
|
+
export function buildAnthropicAuthConfig(apiKey: string, baseUrl?: string): AnthropicAuthConfig {
|
|
60
|
+
return {
|
|
61
|
+
apiKey,
|
|
62
|
+
baseUrl: normalizeBaseUrl(baseUrl) ?? resolveAnthropicBaseUrlFromEnv() ?? DEFAULT_BASE_URL,
|
|
63
|
+
isOAuth: isOAuthToken(apiKey),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Builds HTTP headers for Anthropic API requests (search variant).
|
|
69
|
+
*/
|
|
70
|
+
export function buildAnthropicSearchHeaders(auth: AnthropicAuthConfig): Record<string, string> {
|
|
71
|
+
return buildProviderAnthropicHeaders({
|
|
72
|
+
apiKey: auth.apiKey,
|
|
73
|
+
baseUrl: auth.baseUrl,
|
|
74
|
+
isOAuth: auth.isOAuth,
|
|
75
|
+
extraBetas: ["web-search-2025-03-05"],
|
|
76
|
+
stream: false,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Builds the full API URL for Anthropic messages endpoint.
|
|
82
|
+
*/
|
|
83
|
+
export function buildAnthropicUrl(auth: AnthropicAuthConfig): string {
|
|
84
|
+
const normalizedBaseUrl = normalizeAnthropicBaseUrl(auth.baseUrl);
|
|
85
|
+
const base = `${normalizedBaseUrl}/v1/messages`;
|
|
86
|
+
return `${base}?beta=true`;
|
|
87
|
+
}
|