@clinebot/llms 0.0.20 → 0.0.21
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/dist/config-browser.d.ts +1 -0
- package/dist/config-browser.d.ts.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.browser.js +5 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -12
- package/dist/models/generated-access.d.ts +1 -0
- package/dist/models/generated-access.d.ts.map +1 -0
- package/dist/models/generated-provider-loaders.d.ts +1 -0
- package/dist/models/generated-provider-loaders.d.ts.map +1 -0
- package/dist/models/generated.d.ts +1 -0
- package/dist/models/generated.d.ts.map +1 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/models-dev-catalog.d.ts +1 -0
- package/dist/models/models-dev-catalog.d.ts.map +1 -0
- package/dist/models/providers/aihubmix.d.ts +1 -0
- package/dist/models/providers/aihubmix.d.ts.map +1 -0
- package/dist/models/providers/anthropic.d.ts +1 -0
- package/dist/models/providers/anthropic.d.ts.map +1 -0
- package/dist/models/providers/asksage.d.ts +1 -0
- package/dist/models/providers/asksage.d.ts.map +1 -0
- package/dist/models/providers/baseten.d.ts +1 -0
- package/dist/models/providers/baseten.d.ts.map +1 -0
- package/dist/models/providers/bedrock.d.ts +1 -0
- package/dist/models/providers/bedrock.d.ts.map +1 -0
- package/dist/models/providers/cerebras.d.ts +1 -0
- package/dist/models/providers/cerebras.d.ts.map +1 -0
- package/dist/models/providers/claude-code.d.ts +1 -0
- package/dist/models/providers/claude-code.d.ts.map +1 -0
- package/dist/models/providers/cline.d.ts +1 -0
- package/dist/models/providers/cline.d.ts.map +1 -0
- package/dist/models/providers/deepseek.d.ts +1 -0
- package/dist/models/providers/deepseek.d.ts.map +1 -0
- package/dist/models/providers/dify.d.ts +1 -0
- package/dist/models/providers/dify.d.ts.map +1 -0
- package/dist/models/providers/doubao.d.ts +1 -0
- package/dist/models/providers/doubao.d.ts.map +1 -0
- package/dist/models/providers/fireworks.d.ts +1 -0
- package/dist/models/providers/fireworks.d.ts.map +1 -0
- package/dist/models/providers/gemini.d.ts +1 -0
- package/dist/models/providers/gemini.d.ts.map +1 -0
- package/dist/models/providers/groq.d.ts +1 -0
- package/dist/models/providers/groq.d.ts.map +1 -0
- package/dist/models/providers/hicap.d.ts +1 -0
- package/dist/models/providers/hicap.d.ts.map +1 -0
- package/dist/models/providers/huawei-cloud-maas.d.ts +1 -0
- package/dist/models/providers/huawei-cloud-maas.d.ts.map +1 -0
- package/dist/models/providers/huggingface.d.ts +1 -0
- package/dist/models/providers/huggingface.d.ts.map +1 -0
- package/dist/models/providers/index.d.ts +1 -0
- package/dist/models/providers/index.d.ts.map +1 -0
- package/dist/models/providers/litellm.d.ts +1 -0
- package/dist/models/providers/litellm.d.ts.map +1 -0
- package/dist/models/providers/lmstudio.d.ts +1 -0
- package/dist/models/providers/lmstudio.d.ts.map +1 -0
- package/dist/models/providers/minimax.d.ts +1 -0
- package/dist/models/providers/minimax.d.ts.map +1 -0
- package/dist/models/providers/mistral.d.ts +1 -0
- package/dist/models/providers/mistral.d.ts.map +1 -0
- package/dist/models/providers/moonshot.d.ts +1 -0
- package/dist/models/providers/moonshot.d.ts.map +1 -0
- package/dist/models/providers/nebius.d.ts +1 -0
- package/dist/models/providers/nebius.d.ts.map +1 -0
- package/dist/models/providers/nous-research.d.ts +1 -0
- package/dist/models/providers/nous-research.d.ts.map +1 -0
- package/dist/models/providers/oca.d.ts +1 -0
- package/dist/models/providers/oca.d.ts.map +1 -0
- package/dist/models/providers/ollama.d.ts +1 -0
- package/dist/models/providers/ollama.d.ts.map +1 -0
- package/dist/models/providers/openai-codex.d.ts +1 -0
- package/dist/models/providers/openai-codex.d.ts.map +1 -0
- package/dist/models/providers/openai.d.ts +1 -0
- package/dist/models/providers/openai.d.ts.map +1 -0
- package/dist/models/providers/opencode.d.ts +1 -0
- package/dist/models/providers/opencode.d.ts.map +1 -0
- package/dist/models/providers/openrouter.d.ts +1 -0
- package/dist/models/providers/openrouter.d.ts.map +1 -0
- package/dist/models/providers/qwen-code.d.ts +1 -0
- package/dist/models/providers/qwen-code.d.ts.map +1 -0
- package/dist/models/providers/qwen.d.ts +1 -0
- package/dist/models/providers/qwen.d.ts.map +1 -0
- package/dist/models/providers/requesty.d.ts +1 -0
- package/dist/models/providers/requesty.d.ts.map +1 -0
- package/dist/models/providers/sambanova.d.ts +1 -0
- package/dist/models/providers/sambanova.d.ts.map +1 -0
- package/dist/models/providers/sapaicore.d.ts +1 -0
- package/dist/models/providers/sapaicore.d.ts.map +1 -0
- package/dist/models/providers/together.d.ts +1 -0
- package/dist/models/providers/together.d.ts.map +1 -0
- package/dist/models/providers/vercel-ai-gateway.d.ts +1 -0
- package/dist/models/providers/vercel-ai-gateway.d.ts.map +1 -0
- package/dist/models/providers/vertex.d.ts +1 -0
- package/dist/models/providers/vertex.d.ts.map +1 -0
- package/dist/models/providers/xai.d.ts +1 -0
- package/dist/models/providers/xai.d.ts.map +1 -0
- package/dist/models/providers/zai.d.ts +1 -0
- package/dist/models/providers/zai.d.ts.map +1 -0
- package/dist/models/query.d.ts +1 -0
- package/dist/models/query.d.ts.map +1 -0
- package/dist/models/registry.d.ts +1 -0
- package/dist/models/registry.d.ts.map +1 -0
- package/dist/models/schemas/index.d.ts +1 -0
- package/dist/models/schemas/index.d.ts.map +1 -0
- package/dist/models/schemas/model.d.ts +1 -0
- package/dist/models/schemas/model.d.ts.map +1 -0
- package/dist/models/schemas/query.d.ts +1 -0
- package/dist/models/schemas/query.d.ts.map +1 -0
- package/dist/providers/handlers/ai-sdk-community.d.ts +1 -0
- package/dist/providers/handlers/ai-sdk-community.d.ts.map +1 -0
- package/dist/providers/handlers/ai-sdk-provider-base.d.ts +1 -0
- package/dist/providers/handlers/ai-sdk-provider-base.d.ts.map +1 -0
- package/dist/providers/handlers/anthropic-base.d.ts +1 -0
- package/dist/providers/handlers/anthropic-base.d.ts.map +1 -0
- package/dist/providers/handlers/asksage.d.ts +1 -0
- package/dist/providers/handlers/asksage.d.ts.map +1 -0
- package/dist/providers/handlers/auth.d.ts +1 -0
- package/dist/providers/handlers/auth.d.ts.map +1 -0
- package/dist/providers/handlers/base.d.ts +1 -0
- package/dist/providers/handlers/base.d.ts.map +1 -0
- package/dist/providers/handlers/bedrock-base.d.ts +1 -0
- package/dist/providers/handlers/bedrock-base.d.ts.map +1 -0
- package/dist/providers/handlers/bedrock-client.d.ts +1 -0
- package/dist/providers/handlers/bedrock-client.d.ts.map +1 -0
- package/dist/providers/handlers/community-sdk.d.ts +1 -0
- package/dist/providers/handlers/community-sdk.d.ts.map +1 -0
- package/dist/providers/handlers/fetch-base.d.ts +1 -0
- package/dist/providers/handlers/fetch-base.d.ts.map +1 -0
- package/dist/providers/handlers/gemini-base.d.ts +1 -0
- package/dist/providers/handlers/gemini-base.d.ts.map +1 -0
- package/dist/providers/handlers/index.d.ts +1 -0
- package/dist/providers/handlers/index.d.ts.map +1 -0
- package/dist/providers/handlers/openai-base.d.ts +1 -0
- package/dist/providers/handlers/openai-base.d.ts.map +1 -0
- package/dist/providers/handlers/openai-responses.d.ts +1 -0
- package/dist/providers/handlers/openai-responses.d.ts.map +1 -0
- package/dist/providers/handlers/providers.d.ts +1 -0
- package/dist/providers/handlers/providers.d.ts.map +1 -0
- package/dist/providers/handlers/r1-base.d.ts +1 -0
- package/dist/providers/handlers/r1-base.d.ts.map +1 -0
- package/dist/providers/handlers/registry.d.ts +1 -0
- package/dist/providers/handlers/registry.d.ts.map +1 -0
- package/dist/providers/handlers/vertex.d.ts +1 -0
- package/dist/providers/handlers/vertex.d.ts.map +1 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/public.browser.d.ts +1 -0
- package/dist/providers/public.browser.d.ts.map +1 -0
- package/dist/providers/public.d.ts +1 -0
- package/dist/providers/public.d.ts.map +1 -0
- package/dist/providers/shared/openai-compatible.d.ts +1 -0
- package/dist/providers/shared/openai-compatible.d.ts.map +1 -0
- package/dist/providers/transform/ai-sdk-community-format.d.ts +1 -0
- package/dist/providers/transform/ai-sdk-community-format.d.ts.map +1 -0
- package/dist/providers/transform/anthropic-format.d.ts +1 -0
- package/dist/providers/transform/anthropic-format.d.ts.map +1 -0
- package/dist/providers/transform/content-format.d.ts +1 -0
- package/dist/providers/transform/content-format.d.ts.map +1 -0
- package/dist/providers/transform/gemini-format.d.ts +1 -0
- package/dist/providers/transform/gemini-format.d.ts.map +1 -0
- package/dist/providers/transform/index.d.ts +1 -0
- package/dist/providers/transform/index.d.ts.map +1 -0
- package/dist/providers/transform/openai-format.d.ts +1 -0
- package/dist/providers/transform/openai-format.d.ts.map +1 -0
- package/dist/providers/transform/r1-format.d.ts +1 -0
- package/dist/providers/transform/r1-format.d.ts.map +1 -0
- package/dist/providers/types/config.d.ts +1 -0
- package/dist/providers/types/config.d.ts.map +1 -0
- package/dist/providers/types/handler.d.ts +1 -0
- package/dist/providers/types/handler.d.ts.map +1 -0
- package/dist/providers/types/index.d.ts +1 -0
- package/dist/providers/types/index.d.ts.map +1 -0
- package/dist/providers/types/messages.d.ts +1 -0
- package/dist/providers/types/messages.d.ts.map +1 -0
- package/dist/providers/types/model-info.d.ts +1 -0
- package/dist/providers/types/model-info.d.ts.map +1 -0
- package/dist/providers/types/provider-ids.d.ts +1 -1
- package/dist/providers/types/provider-ids.d.ts.map +1 -0
- package/dist/providers/types/settings.d.ts +1 -0
- package/dist/providers/types/settings.d.ts.map +1 -0
- package/dist/providers/types/stream.d.ts +1 -0
- package/dist/providers/types/stream.d.ts.map +1 -0
- package/dist/providers/utils/index.d.ts +1 -0
- package/dist/providers/utils/index.d.ts.map +1 -0
- package/dist/providers/utils/retry.d.ts +1 -0
- package/dist/providers/utils/retry.d.ts.map +1 -0
- package/dist/providers/utils/stream-processor.d.ts +1 -0
- package/dist/providers/utils/stream-processor.d.ts.map +1 -0
- package/dist/providers/utils/tool-processor.d.ts +1 -0
- package/dist/providers/utils/tool-processor.d.ts.map +1 -0
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +2 -3
- package/src/catalog.ts +0 -20
- package/src/config-browser.ts +0 -11
- package/src/config.ts +0 -49
- package/src/index.browser.ts +0 -9
- package/src/index.ts +0 -10
- package/src/live-providers.test.ts +0 -138
- package/src/models/generated-access.ts +0 -41
- package/src/models/generated-provider-loaders.ts +0 -166
- package/src/models/generated.ts +0 -11785
- package/src/models/index.ts +0 -271
- package/src/models/models-dev-catalog.test.ts +0 -161
- package/src/models/models-dev-catalog.ts +0 -168
- package/src/models/providers/aihubmix.ts +0 -19
- package/src/models/providers/anthropic.ts +0 -60
- package/src/models/providers/asksage.ts +0 -19
- package/src/models/providers/baseten.ts +0 -21
- package/src/models/providers/bedrock.ts +0 -30
- package/src/models/providers/cerebras.ts +0 -24
- package/src/models/providers/claude-code.ts +0 -51
- package/src/models/providers/cline.ts +0 -25
- package/src/models/providers/deepseek.ts +0 -33
- package/src/models/providers/dify.ts +0 -17
- package/src/models/providers/doubao.ts +0 -33
- package/src/models/providers/fireworks.ts +0 -34
- package/src/models/providers/gemini.ts +0 -43
- package/src/models/providers/groq.ts +0 -33
- package/src/models/providers/hicap.ts +0 -18
- package/src/models/providers/huawei-cloud-maas.ts +0 -18
- package/src/models/providers/huggingface.ts +0 -22
- package/src/models/providers/index.ts +0 -162
- package/src/models/providers/litellm.ts +0 -19
- package/src/models/providers/lmstudio.ts +0 -22
- package/src/models/providers/minimax.ts +0 -34
- package/src/models/providers/mistral.ts +0 -19
- package/src/models/providers/moonshot.ts +0 -34
- package/src/models/providers/nebius.ts +0 -24
- package/src/models/providers/nous-research.ts +0 -21
- package/src/models/providers/oca.ts +0 -30
- package/src/models/providers/ollama.ts +0 -18
- package/src/models/providers/openai-codex.ts +0 -46
- package/src/models/providers/openai.ts +0 -43
- package/src/models/providers/opencode.ts +0 -28
- package/src/models/providers/openrouter.ts +0 -24
- package/src/models/providers/qwen-code.ts +0 -33
- package/src/models/providers/qwen.ts +0 -34
- package/src/models/providers/requesty.ts +0 -23
- package/src/models/providers/sambanova.ts +0 -23
- package/src/models/providers/sapaicore.ts +0 -34
- package/src/models/providers/together.ts +0 -35
- package/src/models/providers/vercel-ai-gateway.ts +0 -23
- package/src/models/providers/vertex.ts +0 -36
- package/src/models/providers/xai.ts +0 -34
- package/src/models/providers/zai.ts +0 -25
- package/src/models/query.ts +0 -407
- package/src/models/registry.ts +0 -511
- package/src/models/schemas/index.ts +0 -62
- package/src/models/schemas/model.ts +0 -308
- package/src/models/schemas/query.ts +0 -336
- package/src/providers/browser.ts +0 -4
- package/src/providers/handlers/ai-sdk-community.ts +0 -229
- package/src/providers/handlers/ai-sdk-provider-base.ts +0 -203
- package/src/providers/handlers/anthropic-base.test.ts +0 -30
- package/src/providers/handlers/anthropic-base.ts +0 -387
- package/src/providers/handlers/asksage.test.ts +0 -103
- package/src/providers/handlers/asksage.ts +0 -138
- package/src/providers/handlers/auth.test.ts +0 -19
- package/src/providers/handlers/auth.ts +0 -121
- package/src/providers/handlers/base.test.ts +0 -230
- package/src/providers/handlers/base.ts +0 -310
- package/src/providers/handlers/bedrock-base.ts +0 -390
- package/src/providers/handlers/bedrock-client.ts +0 -100
- package/src/providers/handlers/codex.test.ts +0 -160
- package/src/providers/handlers/community-sdk.test.ts +0 -321
- package/src/providers/handlers/community-sdk.ts +0 -391
- package/src/providers/handlers/fetch-base.ts +0 -68
- package/src/providers/handlers/gemini-base.test.ts +0 -261
- package/src/providers/handlers/gemini-base.ts +0 -307
- package/src/providers/handlers/index.ts +0 -67
- package/src/providers/handlers/openai-base.ts +0 -341
- package/src/providers/handlers/openai-responses.test.ts +0 -259
- package/src/providers/handlers/openai-responses.ts +0 -634
- package/src/providers/handlers/providers.test.ts +0 -120
- package/src/providers/handlers/providers.ts +0 -563
- package/src/providers/handlers/r1-base.ts +0 -283
- package/src/providers/handlers/registry.ts +0 -185
- package/src/providers/handlers/vertex.test.ts +0 -124
- package/src/providers/handlers/vertex.ts +0 -302
- package/src/providers/index.ts +0 -534
- package/src/providers/public.browser.ts +0 -20
- package/src/providers/public.ts +0 -51
- package/src/providers/shared/openai-compatible.ts +0 -63
- package/src/providers/transform/ai-sdk-community-format.test.ts +0 -73
- package/src/providers/transform/ai-sdk-community-format.ts +0 -115
- package/src/providers/transform/anthropic-format.ts +0 -230
- package/src/providers/transform/content-format.ts +0 -34
- package/src/providers/transform/format-conversion.test.ts +0 -413
- package/src/providers/transform/gemini-format.ts +0 -262
- package/src/providers/transform/index.ts +0 -22
- package/src/providers/transform/openai-format.ts +0 -290
- package/src/providers/transform/r1-format.ts +0 -287
- package/src/providers/types/config.ts +0 -396
- package/src/providers/types/handler.ts +0 -92
- package/src/providers/types/index.ts +0 -120
- package/src/providers/types/messages.ts +0 -162
- package/src/providers/types/model-info.test.ts +0 -57
- package/src/providers/types/model-info.ts +0 -65
- package/src/providers/types/provider-ids.test.ts +0 -12
- package/src/providers/types/provider-ids.ts +0 -89
- package/src/providers/types/settings.test.ts +0 -49
- package/src/providers/types/settings.ts +0 -533
- package/src/providers/types/stream.ts +0 -117
- package/src/providers/utils/index.ts +0 -27
- package/src/providers/utils/retry.test.ts +0 -140
- package/src/providers/utils/retry.ts +0 -188
- package/src/providers/utils/stream-processor.test.ts +0 -232
- package/src/providers/utils/stream-processor.ts +0 -472
- package/src/providers/utils/tool-processor.test.ts +0 -235
- package/src/providers/utils/tool-processor.ts +0 -146
- package/src/sdk.ts +0 -264
- package/src/types.ts +0 -79
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import * as modelProviderExports from "../../models/providers/index";
|
|
2
|
-
import type { ModelCollection } from "../../models/schemas/index";
|
|
3
|
-
import { BUILT_IN_PROVIDER, normalizeProviderId } from "../types/provider-ids";
|
|
4
|
-
|
|
5
|
-
const DEFAULT_FALLBACK_PROVIDER_IDS = [
|
|
6
|
-
BUILT_IN_PROVIDER.CLINE,
|
|
7
|
-
BUILT_IN_PROVIDER.ANTHROPIC,
|
|
8
|
-
BUILT_IN_PROVIDER.OPENAI_NATIVE,
|
|
9
|
-
BUILT_IN_PROVIDER.GEMINI,
|
|
10
|
-
BUILT_IN_PROVIDER.OPENROUTER,
|
|
11
|
-
] as const;
|
|
12
|
-
|
|
13
|
-
function isModelCollection(value: unknown): value is ModelCollection {
|
|
14
|
-
if (!value || typeof value !== "object") {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const maybeCollection = value as Partial<ModelCollection>;
|
|
19
|
-
return (
|
|
20
|
-
typeof maybeCollection.provider === "object" &&
|
|
21
|
-
typeof maybeCollection.models === "object"
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function dedupe(values: readonly string[]): string[] {
|
|
26
|
-
return [...new Set(values)];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function buildProviderEnvKeys(): Record<string, readonly string[]> {
|
|
30
|
-
const envKeysByProvider: Record<string, readonly string[]> = {};
|
|
31
|
-
|
|
32
|
-
for (const value of Object.values(modelProviderExports)) {
|
|
33
|
-
if (!isModelCollection(value)) {
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const providerId = value.provider.id;
|
|
38
|
-
envKeysByProvider[providerId] = dedupe(value.provider.env ?? []);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return envKeysByProvider;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const ENV_KEYS_BY_PROVIDER = buildProviderEnvKeys();
|
|
45
|
-
const DEFAULT_FALLBACK_ENV_KEYS = dedupe(
|
|
46
|
-
DEFAULT_FALLBACK_PROVIDER_IDS.flatMap(
|
|
47
|
-
(providerId) => ENV_KEYS_BY_PROVIDER[providerId] ?? [],
|
|
48
|
-
),
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
function readTrimmed(
|
|
52
|
-
env: Record<string, string | undefined>,
|
|
53
|
-
key: string,
|
|
54
|
-
): string | undefined {
|
|
55
|
-
const value = env[key];
|
|
56
|
-
if (typeof value !== "string") {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
const trimmed = value.trim();
|
|
60
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function resolveFromKeys(
|
|
64
|
-
keys: readonly string[],
|
|
65
|
-
env: Record<string, string | undefined>,
|
|
66
|
-
): string | undefined {
|
|
67
|
-
for (const key of keys) {
|
|
68
|
-
const value = readTrimmed(env, key);
|
|
69
|
-
if (value) {
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export { normalizeProviderId };
|
|
77
|
-
|
|
78
|
-
export function getProviderEnvKeys(providerId: string): readonly string[] {
|
|
79
|
-
return ENV_KEYS_BY_PROVIDER[normalizeProviderId(providerId)] ?? [];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function resolveApiKeyForProvider(
|
|
83
|
-
providerId: string,
|
|
84
|
-
explicitApiKey: string | undefined,
|
|
85
|
-
env: Record<string, string | undefined> = process.env,
|
|
86
|
-
): string | undefined {
|
|
87
|
-
const normalizedProviderId = normalizeProviderId(providerId);
|
|
88
|
-
const explicit = explicitApiKey?.trim();
|
|
89
|
-
if (explicit) {
|
|
90
|
-
return explicit;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const providerKey = resolveFromKeys(
|
|
94
|
-
getProviderEnvKeys(normalizedProviderId),
|
|
95
|
-
env,
|
|
96
|
-
);
|
|
97
|
-
if (providerKey) {
|
|
98
|
-
return providerKey;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// LM Studio local runtime typically does not require auth.
|
|
102
|
-
if (normalizedProviderId === BUILT_IN_PROVIDER.LMSTUDIO) {
|
|
103
|
-
return "noop";
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return resolveFromKeys(DEFAULT_FALLBACK_ENV_KEYS, env);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function getMissingApiKeyError(providerId: string): string {
|
|
110
|
-
const expectedKeys = [
|
|
111
|
-
...new Set([
|
|
112
|
-
...getProviderEnvKeys(providerId),
|
|
113
|
-
...DEFAULT_FALLBACK_ENV_KEYS,
|
|
114
|
-
]),
|
|
115
|
-
];
|
|
116
|
-
const keysMessage =
|
|
117
|
-
expectedKeys.length > 0
|
|
118
|
-
? expectedKeys.join(", ")
|
|
119
|
-
: "provider-specific API key env var";
|
|
120
|
-
return `Missing API key for provider "${normalizeProviderId(providerId)}". Set apiKey explicitly or one of: ${keysMessage}.`;
|
|
121
|
-
}
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { ApiStream, ProviderConfig } from "../types/index";
|
|
3
|
-
import { BaseHandler } from "./base";
|
|
4
|
-
|
|
5
|
-
class TestHandler extends BaseHandler {
|
|
6
|
-
getMessages(): unknown {
|
|
7
|
-
return [];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
createMessage(): ApiStream {
|
|
11
|
-
throw new Error("not implemented");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
public computeCost(
|
|
15
|
-
inputTokens: number,
|
|
16
|
-
outputTokens: number,
|
|
17
|
-
cacheReadTokens = 0,
|
|
18
|
-
cacheWriteTokens = 0,
|
|
19
|
-
): number | undefined {
|
|
20
|
-
return this.calculateCost(
|
|
21
|
-
inputTokens,
|
|
22
|
-
outputTokens,
|
|
23
|
-
cacheReadTokens,
|
|
24
|
-
cacheWriteTokens,
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public computeCostFromInclusiveInput(
|
|
29
|
-
inputTokens: number,
|
|
30
|
-
outputTokens: number,
|
|
31
|
-
cacheReadTokens = 0,
|
|
32
|
-
cacheWriteTokens = 0,
|
|
33
|
-
): number | undefined {
|
|
34
|
-
return this.calculateCostFromInclusiveInput(
|
|
35
|
-
inputTokens,
|
|
36
|
-
outputTokens,
|
|
37
|
-
cacheReadTokens,
|
|
38
|
-
cacheWriteTokens,
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public exposeAbortSignal(): AbortSignal {
|
|
43
|
-
return this.getAbortSignal();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public normalizeBadRequest(error: unknown): Error | undefined {
|
|
47
|
-
return this.normalizeOpenAICompatibleBadRequest(error);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
describe("BaseHandler.calculateCost", () => {
|
|
52
|
-
it("uses known model pricing when modelInfo is not provided", () => {
|
|
53
|
-
const config: ProviderConfig = {
|
|
54
|
-
providerId: "anthropic",
|
|
55
|
-
modelId: "claude-sonnet-test",
|
|
56
|
-
apiKey: "test-key",
|
|
57
|
-
knownModels: {
|
|
58
|
-
"claude-sonnet-test": {
|
|
59
|
-
id: "claude-sonnet-test",
|
|
60
|
-
pricing: {
|
|
61
|
-
input: 3,
|
|
62
|
-
output: 15,
|
|
63
|
-
cacheRead: 0.3,
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
const handler = new TestHandler(config);
|
|
69
|
-
|
|
70
|
-
const cost = handler.computeCost(1_000_000, 1_000_000, 100_000);
|
|
71
|
-
|
|
72
|
-
expect(cost).toBeCloseTo(18.03, 6);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("does not charge cache reads twice when input already includes them", () => {
|
|
76
|
-
const config: ProviderConfig = {
|
|
77
|
-
providerId: "openai-native",
|
|
78
|
-
modelId: "gpt-test",
|
|
79
|
-
apiKey: "test-key",
|
|
80
|
-
knownModels: {
|
|
81
|
-
"gpt-test": {
|
|
82
|
-
id: "gpt-test",
|
|
83
|
-
pricing: {
|
|
84
|
-
input: 1,
|
|
85
|
-
output: 2,
|
|
86
|
-
cacheRead: 0.5,
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
const handler = new TestHandler(config);
|
|
92
|
-
|
|
93
|
-
const cost = handler.computeCostFromInclusiveInput(100, 40, 25);
|
|
94
|
-
|
|
95
|
-
expect(cost).toBeCloseTo(0.0001675, 10);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("does not charge cache writes twice when input already includes them", () => {
|
|
99
|
-
const config: ProviderConfig = {
|
|
100
|
-
providerId: "openai-native",
|
|
101
|
-
modelId: "gpt-test",
|
|
102
|
-
apiKey: "test-key",
|
|
103
|
-
knownModels: {
|
|
104
|
-
"gpt-test": {
|
|
105
|
-
id: "gpt-test",
|
|
106
|
-
pricing: {
|
|
107
|
-
input: 1,
|
|
108
|
-
output: 2,
|
|
109
|
-
cacheRead: 0.5,
|
|
110
|
-
cacheWrite: 1.25,
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
const handler = new TestHandler(config);
|
|
116
|
-
|
|
117
|
-
const cost = handler.computeCostFromInclusiveInput(100, 40, 25, 10);
|
|
118
|
-
|
|
119
|
-
expect(cost).toBeCloseTo(0.00017, 10);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe("BaseHandler abort signal wiring", () => {
|
|
124
|
-
it("does not let a stale request signal abort a newer request", () => {
|
|
125
|
-
const logger = {
|
|
126
|
-
debug: vi.fn(),
|
|
127
|
-
warn: vi.fn(),
|
|
128
|
-
};
|
|
129
|
-
const request1 = new AbortController();
|
|
130
|
-
const handler = new TestHandler({
|
|
131
|
-
providerId: "openrouter",
|
|
132
|
-
modelId: "mock-model",
|
|
133
|
-
apiKey: "test-key",
|
|
134
|
-
baseUrl: "https://example.com/v1",
|
|
135
|
-
abortSignal: request1.signal,
|
|
136
|
-
logger,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const signal1 = handler.exposeAbortSignal();
|
|
140
|
-
expect(signal1.aborted).toBe(false);
|
|
141
|
-
|
|
142
|
-
const request2 = new AbortController();
|
|
143
|
-
handler.setAbortSignal(request2.signal);
|
|
144
|
-
const signal2 = handler.exposeAbortSignal();
|
|
145
|
-
expect(signal2).not.toBe(signal1);
|
|
146
|
-
expect(signal2.aborted).toBe(false);
|
|
147
|
-
|
|
148
|
-
request1.abort(new Error("stale timeout"));
|
|
149
|
-
|
|
150
|
-
expect(signal1.aborted).toBe(true);
|
|
151
|
-
expect(signal2.aborted).toBe(false);
|
|
152
|
-
expect(logger.warn).toHaveBeenCalledWith(
|
|
153
|
-
"Provider request abort signal fired",
|
|
154
|
-
expect.objectContaining({
|
|
155
|
-
reason: expect.objectContaining({ message: "stale timeout" }),
|
|
156
|
-
}),
|
|
157
|
-
);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("creates a fresh controller for each request", () => {
|
|
161
|
-
const handler = new TestHandler({
|
|
162
|
-
providerId: "openrouter",
|
|
163
|
-
modelId: "mock-model",
|
|
164
|
-
apiKey: "test-key",
|
|
165
|
-
baseUrl: "https://example.com/v1",
|
|
166
|
-
abortSignal: new AbortController().signal,
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
const signal1 = handler.exposeAbortSignal();
|
|
170
|
-
const signal2 = handler.exposeAbortSignal();
|
|
171
|
-
|
|
172
|
-
expect(signal2).not.toBe(signal1);
|
|
173
|
-
expect(signal1.aborted).toBe(false);
|
|
174
|
-
expect(signal2.aborted).toBe(false);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe("BaseHandler.normalizeOpenAICompatibleBadRequest", () => {
|
|
179
|
-
it("rewrites provider metadata prompt-limit errors into a helpful message", () => {
|
|
180
|
-
const handler = new TestHandler({
|
|
181
|
-
providerId: "openrouter",
|
|
182
|
-
modelId: "anthropic/claude-sonnet-4.6",
|
|
183
|
-
apiKey: "test-key",
|
|
184
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const error = Object.assign(new Error("400 Provider returned error"), {
|
|
188
|
-
status: 400,
|
|
189
|
-
error: {
|
|
190
|
-
message: "Provider returned error",
|
|
191
|
-
code: 400,
|
|
192
|
-
metadata: {
|
|
193
|
-
provider_name: "Anthropic",
|
|
194
|
-
raw: JSON.stringify({
|
|
195
|
-
type: "error",
|
|
196
|
-
error: {
|
|
197
|
-
type: "invalid_request_error",
|
|
198
|
-
message: "prompt is too long: 1102640 tokens > 1000000 maximum",
|
|
199
|
-
},
|
|
200
|
-
request_id: "req_123",
|
|
201
|
-
}),
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const normalized = handler.normalizeBadRequest(error);
|
|
207
|
-
|
|
208
|
-
expect(normalized?.message).toBe(
|
|
209
|
-
"Anthropic request was rejected (HTTP 400). Prompt is too long: 1102640 tokens exceeds the 1000000 token limit. Request ID: req_123.",
|
|
210
|
-
);
|
|
211
|
-
expect(normalized?.cause).toBe(error);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("returns undefined for non-400 errors", () => {
|
|
215
|
-
const handler = new TestHandler({
|
|
216
|
-
providerId: "openrouter",
|
|
217
|
-
modelId: "anthropic/claude-sonnet-4.6",
|
|
218
|
-
apiKey: "test-key",
|
|
219
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const normalized = handler.normalizeBadRequest(
|
|
223
|
-
Object.assign(new Error("500 Provider returned error"), {
|
|
224
|
-
status: 500,
|
|
225
|
-
}),
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
expect(normalized).toBeUndefined();
|
|
229
|
-
});
|
|
230
|
-
});
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base Handler
|
|
3
|
-
*
|
|
4
|
-
* Abstract base class that provides common functionality for all handlers.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { nanoid } from "nanoid";
|
|
8
|
-
import type {
|
|
9
|
-
ApiHandler,
|
|
10
|
-
ApiStream,
|
|
11
|
-
ApiStreamUsageChunk,
|
|
12
|
-
HandlerModelInfo,
|
|
13
|
-
ModelInfo,
|
|
14
|
-
ProviderConfig,
|
|
15
|
-
} from "../types";
|
|
16
|
-
import type { Message, ToolDefinition } from "../types/messages";
|
|
17
|
-
import type { ApiStreamChunk } from "../types/stream";
|
|
18
|
-
|
|
19
|
-
export const DEFAULT_REQUEST_HEADERS: Record<string, string> = {
|
|
20
|
-
"HTTP-Referer": "https://cline.bot",
|
|
21
|
-
"X-Title": "Cline",
|
|
22
|
-
"X-IS-MULTIROOT": "false",
|
|
23
|
-
"X-CLIENT-TYPE": "cline-sdk",
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
interface OpenAICompatibleProviderErrorShape {
|
|
27
|
-
status?: number;
|
|
28
|
-
message?: string;
|
|
29
|
-
error?: {
|
|
30
|
-
message?: string;
|
|
31
|
-
code?: number;
|
|
32
|
-
metadata?: {
|
|
33
|
-
raw?: string;
|
|
34
|
-
provider_name?: string;
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
response?: {
|
|
38
|
-
status?: number;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const controllerIds = new WeakMap<AbortController, string>();
|
|
43
|
-
let controllerIdCounter = 0;
|
|
44
|
-
|
|
45
|
-
function getControllerId(controller: AbortController): string {
|
|
46
|
-
let id = controllerIds.get(controller);
|
|
47
|
-
if (!id) {
|
|
48
|
-
id = `abort_${++controllerIdCounter}`;
|
|
49
|
-
controllerIds.set(controller, id);
|
|
50
|
-
}
|
|
51
|
-
return id;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function serializeAbortReason(reason: unknown): unknown {
|
|
55
|
-
return reason instanceof Error
|
|
56
|
-
? { name: reason.name, message: reason.message }
|
|
57
|
-
: reason;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Base handler class with common functionality
|
|
62
|
-
*/
|
|
63
|
-
export abstract class BaseHandler implements ApiHandler {
|
|
64
|
-
protected config: ProviderConfig;
|
|
65
|
-
protected abortController: AbortController | undefined;
|
|
66
|
-
private abortSignalSequence = 0;
|
|
67
|
-
|
|
68
|
-
constructor(config: ProviderConfig) {
|
|
69
|
-
this.config = config;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
abstract getMessages(systemPrompt: string, messages: Message[]): unknown;
|
|
73
|
-
|
|
74
|
-
abstract createMessage(
|
|
75
|
-
systemPrompt: string,
|
|
76
|
-
messages: Message[],
|
|
77
|
-
tools?: ToolDefinition[],
|
|
78
|
-
): ApiStream;
|
|
79
|
-
|
|
80
|
-
getModel(): HandlerModelInfo {
|
|
81
|
-
const modelId = this.config.modelId;
|
|
82
|
-
return {
|
|
83
|
-
id: modelId,
|
|
84
|
-
info: { ...(this.config.modelInfo ?? {}), id: modelId },
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async getApiStreamUsage(): Promise<ApiStreamUsageChunk | undefined> {
|
|
89
|
-
return undefined;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
protected getAbortSignal(): AbortSignal {
|
|
93
|
-
const controller = new AbortController();
|
|
94
|
-
this.abortController = controller;
|
|
95
|
-
controller.signal.addEventListener(
|
|
96
|
-
"abort",
|
|
97
|
-
() => {
|
|
98
|
-
if (this.abortController === controller) {
|
|
99
|
-
this.abortController = undefined;
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
{ once: true },
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
const configSignal = this.config.abortSignal;
|
|
106
|
-
if (configSignal) {
|
|
107
|
-
if (configSignal.aborted) {
|
|
108
|
-
this.logAbort("debug", "Provider request inherited aborted signal", {
|
|
109
|
-
controllerId: getControllerId(controller),
|
|
110
|
-
reason: serializeAbortReason(configSignal.reason),
|
|
111
|
-
});
|
|
112
|
-
controller.abort(configSignal.reason);
|
|
113
|
-
} else {
|
|
114
|
-
const signalId = ++this.abortSignalSequence;
|
|
115
|
-
configSignal.addEventListener(
|
|
116
|
-
"abort",
|
|
117
|
-
() => {
|
|
118
|
-
this.logAbort("warn", "Provider request abort signal fired", {
|
|
119
|
-
controllerId: getControllerId(controller),
|
|
120
|
-
signalId,
|
|
121
|
-
reason: serializeAbortReason(configSignal.reason),
|
|
122
|
-
});
|
|
123
|
-
controller.abort(configSignal.reason);
|
|
124
|
-
},
|
|
125
|
-
{ once: true },
|
|
126
|
-
);
|
|
127
|
-
this.logAbort("debug", "Provider request attached abort signal", {
|
|
128
|
-
controllerId: getControllerId(controller),
|
|
129
|
-
signalId,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return controller.signal;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
abort(): void {
|
|
138
|
-
this.abortController?.abort();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
setAbortSignal(signal: AbortSignal | undefined): void {
|
|
142
|
-
this.config.abortSignal = signal;
|
|
143
|
-
if (signal?.aborted) {
|
|
144
|
-
this.logAbort("debug", "Provider handler received pre-aborted signal", {
|
|
145
|
-
controllerId: this.abortController
|
|
146
|
-
? getControllerId(this.abortController)
|
|
147
|
-
: undefined,
|
|
148
|
-
reason: serializeAbortReason(signal.reason),
|
|
149
|
-
});
|
|
150
|
-
this.abortController?.abort(signal.reason);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private logAbort(
|
|
155
|
-
level: "debug" | "warn",
|
|
156
|
-
message: string,
|
|
157
|
-
metadata?: Record<string, unknown>,
|
|
158
|
-
): void {
|
|
159
|
-
this.config.logger?.[level]?.(message, {
|
|
160
|
-
providerId: this.config.providerId,
|
|
161
|
-
modelId: this.config.modelId,
|
|
162
|
-
...metadata,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
protected supportsPromptCache(modelInfo?: ModelInfo): boolean {
|
|
167
|
-
const resolvedModelInfo =
|
|
168
|
-
modelInfo ??
|
|
169
|
-
this.config.modelInfo ??
|
|
170
|
-
this.config.knownModels?.[this.config.modelId];
|
|
171
|
-
const pricing = resolvedModelInfo?.pricing;
|
|
172
|
-
|
|
173
|
-
return (
|
|
174
|
-
resolvedModelInfo?.capabilities?.includes("prompt-cache") === true ||
|
|
175
|
-
this.config.capabilities?.includes("prompt-cache") === true ||
|
|
176
|
-
typeof pricing?.cacheRead === "number" ||
|
|
177
|
-
typeof pricing?.cacheWrite === "number"
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
protected calculateCost(
|
|
182
|
-
inputTokens: number,
|
|
183
|
-
outputTokens: number,
|
|
184
|
-
cacheReadTokens = 0,
|
|
185
|
-
cacheWriteTokens = 0,
|
|
186
|
-
): number | undefined {
|
|
187
|
-
const pricing = (
|
|
188
|
-
this.config.modelInfo ?? this.config.knownModels?.[this.config.modelId]
|
|
189
|
-
)?.pricing;
|
|
190
|
-
if (!pricing?.input || !pricing?.output) {
|
|
191
|
-
return undefined;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return (
|
|
195
|
-
(inputTokens / 1_000_000) * pricing.input +
|
|
196
|
-
(outputTokens / 1_000_000) * pricing.output +
|
|
197
|
-
(cacheReadTokens > 0
|
|
198
|
-
? (cacheReadTokens / 1_000_000) * (pricing.cacheRead ?? 0)
|
|
199
|
-
: 0) +
|
|
200
|
-
(cacheWriteTokens > 0
|
|
201
|
-
? (cacheWriteTokens / 1_000_000) *
|
|
202
|
-
(pricing.cacheWrite ?? pricing.input * 1.25)
|
|
203
|
-
: 0)
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
protected calculateCostFromInclusiveInput(
|
|
208
|
-
inputTokens: number,
|
|
209
|
-
outputTokens: number,
|
|
210
|
-
cacheReadTokens = 0,
|
|
211
|
-
cacheWriteTokens = 0,
|
|
212
|
-
): number | undefined {
|
|
213
|
-
return this.calculateCost(
|
|
214
|
-
Math.max(0, inputTokens - cacheReadTokens - cacheWriteTokens),
|
|
215
|
-
outputTokens,
|
|
216
|
-
cacheReadTokens,
|
|
217
|
-
cacheWriteTokens,
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
protected createResponseId(): string {
|
|
222
|
-
return nanoid();
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
protected withResponseId<T extends ApiStreamChunk>(
|
|
226
|
-
chunk: T,
|
|
227
|
-
responseId: string,
|
|
228
|
-
): T {
|
|
229
|
-
return { ...chunk, id: responseId };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
protected *withResponseIdForAll(
|
|
233
|
-
chunks: Iterable<ApiStreamChunk>,
|
|
234
|
-
responseId: string,
|
|
235
|
-
): Generator<ApiStreamChunk> {
|
|
236
|
-
for (const chunk of chunks) {
|
|
237
|
-
yield { ...chunk, id: responseId };
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
protected getRequestHeaders(): Record<string, string> {
|
|
242
|
-
return {
|
|
243
|
-
...DEFAULT_REQUEST_HEADERS,
|
|
244
|
-
...(this.config.headers ?? {}),
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
protected normalizeOpenAICompatibleBadRequest(
|
|
249
|
-
error: unknown,
|
|
250
|
-
): Error | undefined {
|
|
251
|
-
const rawError = error as OpenAICompatibleProviderErrorShape | undefined;
|
|
252
|
-
const status =
|
|
253
|
-
rawError?.status ??
|
|
254
|
-
rawError?.response?.status ??
|
|
255
|
-
rawError?.error?.code ??
|
|
256
|
-
(typeof rawError?.message === "string" && rawError.message.includes("400")
|
|
257
|
-
? 400
|
|
258
|
-
: undefined);
|
|
259
|
-
if (status !== 400) {
|
|
260
|
-
return undefined;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const rawMetadata = rawError?.error?.metadata?.raw;
|
|
264
|
-
const parsedRaw = this.parseRawProviderError(rawMetadata);
|
|
265
|
-
const detail =
|
|
266
|
-
parsedRaw?.error?.message?.trim() ||
|
|
267
|
-
rawError?.error?.message?.trim() ||
|
|
268
|
-
rawError?.message?.trim() ||
|
|
269
|
-
"Provider returned error";
|
|
270
|
-
const providerName =
|
|
271
|
-
rawError?.error?.metadata?.provider_name?.trim() || "Provider";
|
|
272
|
-
const requestId = parsedRaw?.request_id?.trim();
|
|
273
|
-
const normalizedMessage = this.rewriteProviderBadRequestDetail(detail);
|
|
274
|
-
const suffix = requestId ? ` Request ID: ${requestId}.` : "";
|
|
275
|
-
return new Error(
|
|
276
|
-
`${providerName} request was rejected (HTTP 400). ${normalizedMessage}${suffix}`,
|
|
277
|
-
{
|
|
278
|
-
cause: error instanceof Error ? error : undefined,
|
|
279
|
-
},
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private parseRawProviderError(
|
|
284
|
-
raw: string | undefined,
|
|
285
|
-
): { error?: { message?: string }; request_id?: string } | undefined {
|
|
286
|
-
if (!raw) {
|
|
287
|
-
return undefined;
|
|
288
|
-
}
|
|
289
|
-
try {
|
|
290
|
-
return JSON.parse(raw) as {
|
|
291
|
-
error?: { message?: string };
|
|
292
|
-
request_id?: string;
|
|
293
|
-
};
|
|
294
|
-
} catch {
|
|
295
|
-
return undefined;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
private rewriteProviderBadRequestDetail(detail: string): string {
|
|
300
|
-
const promptTooLongMatch = detail.match(
|
|
301
|
-
/prompt is too long:\s*([\d,]+)\s*tokens?\s*>\s*([\d,]+)\s*maximum/i,
|
|
302
|
-
);
|
|
303
|
-
if (promptTooLongMatch) {
|
|
304
|
-
const actual = promptTooLongMatch[1];
|
|
305
|
-
const maximum = promptTooLongMatch[2];
|
|
306
|
-
return `Prompt is too long: ${actual} tokens exceeds the ${maximum} token limit.`;
|
|
307
|
-
}
|
|
308
|
-
return detail.endsWith(".") ? detail : `${detail}.`;
|
|
309
|
-
}
|
|
310
|
-
}
|