@clinebot/llms 0.0.0
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/README.md +198 -0
- package/dist/config-browser.d.ts +3 -0
- package/dist/config.d.ts +3 -0
- package/dist/index.browser.d.ts +4 -0
- package/dist/index.browser.js +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +7 -0
- package/dist/models/generated-access.d.ts +4 -0
- package/dist/models/generated-provider-loaders.d.ts +13 -0
- package/dist/models/generated.d.ts +14 -0
- package/dist/models/index.d.ts +43 -0
- package/dist/models/models-dev-catalog.d.ts +32 -0
- package/dist/models/providers/aihubmix.d.ts +5 -0
- package/dist/models/providers/anthropic.d.ts +53 -0
- package/dist/models/providers/asksage.d.ts +5 -0
- package/dist/models/providers/baseten.d.ts +5 -0
- package/dist/models/providers/bedrock.d.ts +7 -0
- package/dist/models/providers/cerebras.d.ts +7 -0
- package/dist/models/providers/claude-code.d.ts +4 -0
- package/dist/models/providers/cline.d.ts +34 -0
- package/dist/models/providers/deepseek.d.ts +8 -0
- package/dist/models/providers/dify.d.ts +5 -0
- package/dist/models/providers/doubao.d.ts +7 -0
- package/dist/models/providers/fireworks.d.ts +8 -0
- package/dist/models/providers/gemini.d.ts +9 -0
- package/dist/models/providers/groq.d.ts +8 -0
- package/dist/models/providers/hicap.d.ts +5 -0
- package/dist/models/providers/huawei-cloud-maas.d.ts +5 -0
- package/dist/models/providers/huggingface.d.ts +6 -0
- package/dist/models/providers/index.d.ts +45 -0
- package/dist/models/providers/litellm.d.ts +5 -0
- package/dist/models/providers/lmstudio.d.ts +5 -0
- package/dist/models/providers/minimax.d.ts +7 -0
- package/dist/models/providers/mistral.d.ts +5 -0
- package/dist/models/providers/moonshot.d.ts +7 -0
- package/dist/models/providers/nebius.d.ts +7 -0
- package/dist/models/providers/nous-research.d.ts +7 -0
- package/dist/models/providers/oca.d.ts +9 -0
- package/dist/models/providers/ollama.d.ts +5 -0
- package/dist/models/providers/openai-codex.d.ts +10 -0
- package/dist/models/providers/openai.d.ts +9 -0
- package/dist/models/providers/opencode.d.ts +10 -0
- package/dist/models/providers/openrouter.d.ts +7 -0
- package/dist/models/providers/qwen-code.d.ts +7 -0
- package/dist/models/providers/qwen.d.ts +7 -0
- package/dist/models/providers/requesty.d.ts +6 -0
- package/dist/models/providers/sambanova.d.ts +7 -0
- package/dist/models/providers/sapaicore.d.ts +7 -0
- package/dist/models/providers/together.d.ts +8 -0
- package/dist/models/providers/vercel-ai-gateway.d.ts +5 -0
- package/dist/models/providers/vertex.d.ts +7 -0
- package/dist/models/providers/xai.d.ts +8 -0
- package/dist/models/providers/zai.d.ts +7 -0
- package/dist/models/query.d.ts +181 -0
- package/dist/models/registry.d.ts +123 -0
- package/dist/models/schemas/index.d.ts +7 -0
- package/dist/models/schemas/model.d.ts +340 -0
- package/dist/models/schemas/query.d.ts +191 -0
- package/dist/providers/handlers/ai-sdk-community.d.ts +46 -0
- package/dist/providers/handlers/ai-sdk-provider-base.d.ts +32 -0
- package/dist/providers/handlers/anthropic-base.d.ts +26 -0
- package/dist/providers/handlers/asksage.d.ts +12 -0
- package/dist/providers/handlers/auth.d.ts +5 -0
- package/dist/providers/handlers/base.d.ts +55 -0
- package/dist/providers/handlers/bedrock-base.d.ts +23 -0
- package/dist/providers/handlers/bedrock-client.d.ts +4 -0
- package/dist/providers/handlers/community-sdk.d.ts +97 -0
- package/dist/providers/handlers/fetch-base.d.ts +18 -0
- package/dist/providers/handlers/gemini-base.d.ts +25 -0
- package/dist/providers/handlers/index.d.ts +19 -0
- package/dist/providers/handlers/openai-base.d.ts +54 -0
- package/dist/providers/handlers/openai-responses.d.ts +64 -0
- package/dist/providers/handlers/providers.d.ts +43 -0
- package/dist/providers/handlers/r1-base.d.ts +62 -0
- package/dist/providers/handlers/registry.d.ts +106 -0
- package/dist/providers/handlers/vertex.d.ts +32 -0
- package/dist/providers/index.d.ts +100 -0
- package/dist/providers/public.browser.d.ts +2 -0
- package/dist/providers/public.d.ts +3 -0
- package/dist/providers/shared/openai-compatible.d.ts +10 -0
- package/dist/providers/transform/ai-sdk-community-format.d.ts +9 -0
- package/dist/providers/transform/anthropic-format.d.ts +24 -0
- package/dist/providers/transform/content-format.d.ts +3 -0
- package/dist/providers/transform/gemini-format.d.ts +19 -0
- package/dist/providers/transform/index.d.ts +10 -0
- package/dist/providers/transform/openai-format.d.ts +36 -0
- package/dist/providers/transform/r1-format.d.ts +26 -0
- package/dist/providers/types/config.d.ts +261 -0
- package/dist/providers/types/handler.d.ts +71 -0
- package/dist/providers/types/index.d.ts +11 -0
- package/dist/providers/types/messages.d.ts +139 -0
- package/dist/providers/types/model-info.d.ts +32 -0
- package/dist/providers/types/provider-ids.d.ts +63 -0
- package/dist/providers/types/settings.d.ts +308 -0
- package/dist/providers/types/stream.d.ts +106 -0
- package/dist/providers/utils/index.d.ts +7 -0
- package/dist/providers/utils/retry.d.ts +38 -0
- package/dist/providers/utils/stream-processor.d.ts +110 -0
- package/dist/providers/utils/tool-processor.d.ts +34 -0
- package/dist/sdk.d.ts +18 -0
- package/dist/types.d.ts +60 -0
- package/package.json +66 -0
- package/src/catalog.ts +20 -0
- package/src/config-browser.ts +11 -0
- package/src/config.ts +49 -0
- package/src/index.browser.ts +9 -0
- package/src/index.ts +10 -0
- package/src/live-providers.test.ts +137 -0
- package/src/models/generated-access.ts +41 -0
- package/src/models/generated-provider-loaders.ts +166 -0
- package/src/models/generated.ts +11997 -0
- package/src/models/index.ts +271 -0
- package/src/models/models-dev-catalog.test.ts +161 -0
- package/src/models/models-dev-catalog.ts +161 -0
- package/src/models/providers/aihubmix.ts +19 -0
- package/src/models/providers/anthropic.ts +60 -0
- package/src/models/providers/asksage.ts +19 -0
- package/src/models/providers/baseten.ts +21 -0
- package/src/models/providers/bedrock.ts +30 -0
- package/src/models/providers/cerebras.ts +24 -0
- package/src/models/providers/claude-code.ts +51 -0
- package/src/models/providers/cline.ts +25 -0
- package/src/models/providers/deepseek.ts +33 -0
- package/src/models/providers/dify.ts +17 -0
- package/src/models/providers/doubao.ts +33 -0
- package/src/models/providers/fireworks.ts +34 -0
- package/src/models/providers/gemini.ts +43 -0
- package/src/models/providers/groq.ts +33 -0
- package/src/models/providers/hicap.ts +18 -0
- package/src/models/providers/huawei-cloud-maas.ts +18 -0
- package/src/models/providers/huggingface.ts +22 -0
- package/src/models/providers/index.ts +162 -0
- package/src/models/providers/litellm.ts +19 -0
- package/src/models/providers/lmstudio.ts +22 -0
- package/src/models/providers/minimax.ts +34 -0
- package/src/models/providers/mistral.ts +19 -0
- package/src/models/providers/moonshot.ts +34 -0
- package/src/models/providers/nebius.ts +24 -0
- package/src/models/providers/nous-research.ts +21 -0
- package/src/models/providers/oca.ts +30 -0
- package/src/models/providers/ollama.ts +18 -0
- package/src/models/providers/openai-codex.ts +30 -0
- package/src/models/providers/openai.ts +43 -0
- package/src/models/providers/opencode.ts +28 -0
- package/src/models/providers/openrouter.ts +24 -0
- package/src/models/providers/qwen-code.ts +33 -0
- package/src/models/providers/qwen.ts +34 -0
- package/src/models/providers/requesty.ts +23 -0
- package/src/models/providers/sambanova.ts +23 -0
- package/src/models/providers/sapaicore.ts +34 -0
- package/src/models/providers/together.ts +35 -0
- package/src/models/providers/vercel-ai-gateway.ts +23 -0
- package/src/models/providers/vertex.ts +36 -0
- package/src/models/providers/xai.ts +34 -0
- package/src/models/providers/zai.ts +25 -0
- package/src/models/query.ts +407 -0
- package/src/models/registry.ts +511 -0
- package/src/models/schemas/index.ts +62 -0
- package/src/models/schemas/model.ts +308 -0
- package/src/models/schemas/query.ts +336 -0
- package/src/providers/browser.ts +4 -0
- package/src/providers/handlers/ai-sdk-community.ts +226 -0
- package/src/providers/handlers/ai-sdk-provider-base.ts +193 -0
- package/src/providers/handlers/anthropic-base.ts +372 -0
- package/src/providers/handlers/asksage.test.ts +103 -0
- package/src/providers/handlers/asksage.ts +138 -0
- package/src/providers/handlers/auth.test.ts +19 -0
- package/src/providers/handlers/auth.ts +121 -0
- package/src/providers/handlers/base.test.ts +46 -0
- package/src/providers/handlers/base.ts +160 -0
- package/src/providers/handlers/bedrock-base.ts +390 -0
- package/src/providers/handlers/bedrock-client.ts +100 -0
- package/src/providers/handlers/codex.test.ts +123 -0
- package/src/providers/handlers/community-sdk.test.ts +288 -0
- package/src/providers/handlers/community-sdk.ts +392 -0
- package/src/providers/handlers/fetch-base.ts +68 -0
- package/src/providers/handlers/gemini-base.ts +302 -0
- package/src/providers/handlers/index.ts +67 -0
- package/src/providers/handlers/openai-base.ts +277 -0
- package/src/providers/handlers/openai-responses.ts +598 -0
- package/src/providers/handlers/providers.test.ts +120 -0
- package/src/providers/handlers/providers.ts +563 -0
- package/src/providers/handlers/r1-base.ts +280 -0
- package/src/providers/handlers/registry.ts +185 -0
- package/src/providers/handlers/vertex.test.ts +124 -0
- package/src/providers/handlers/vertex.ts +292 -0
- package/src/providers/index.ts +534 -0
- package/src/providers/public.browser.ts +20 -0
- package/src/providers/public.ts +51 -0
- package/src/providers/shared/openai-compatible.ts +63 -0
- package/src/providers/transform/ai-sdk-community-format.test.ts +73 -0
- package/src/providers/transform/ai-sdk-community-format.ts +115 -0
- package/src/providers/transform/anthropic-format.ts +218 -0
- package/src/providers/transform/content-format.ts +34 -0
- package/src/providers/transform/format-conversion.test.ts +310 -0
- package/src/providers/transform/gemini-format.ts +167 -0
- package/src/providers/transform/index.ts +22 -0
- package/src/providers/transform/openai-format.ts +247 -0
- package/src/providers/transform/r1-format.ts +287 -0
- package/src/providers/types/config.ts +388 -0
- package/src/providers/types/handler.ts +87 -0
- package/src/providers/types/index.ts +120 -0
- package/src/providers/types/messages.ts +158 -0
- package/src/providers/types/model-info.test.ts +57 -0
- package/src/providers/types/model-info.ts +65 -0
- package/src/providers/types/provider-ids.test.ts +12 -0
- package/src/providers/types/provider-ids.ts +89 -0
- package/src/providers/types/settings.test.ts +49 -0
- package/src/providers/types/settings.ts +533 -0
- package/src/providers/types/stream.ts +117 -0
- package/src/providers/utils/index.ts +27 -0
- package/src/providers/utils/retry.test.ts +140 -0
- package/src/providers/utils/retry.ts +188 -0
- package/src/providers/utils/stream-processor.test.ts +232 -0
- package/src/providers/utils/stream-processor.ts +472 -0
- package/src/providers/utils/tool-processor.test.ts +34 -0
- package/src/providers/utils/tool-processor.ts +111 -0
- package/src/sdk.ts +264 -0
- package/src/types.ts +79 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Configurations
|
|
3
|
+
*
|
|
4
|
+
* Pre-configured settings for all supported OpenAI-compatible providers.
|
|
5
|
+
* Model data is sourced from @clinebot/models (the single registry).
|
|
6
|
+
*/
|
|
7
|
+
/** biome-ignore-all lint/style/noNonNullAssertion: static */
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
MODELS_DEV_PROVIDER_KEY_MAP,
|
|
11
|
+
resolveProviderModelCatalogKeys,
|
|
12
|
+
} from "@clinebot/shared";
|
|
13
|
+
import {
|
|
14
|
+
fetchModelsDevProviderModels,
|
|
15
|
+
sortModelsByReleaseDate,
|
|
16
|
+
} from "../../models/models-dev-catalog";
|
|
17
|
+
import {
|
|
18
|
+
buildOpenAICompatibleProviderDefaults,
|
|
19
|
+
type OpenAICompatibleProviderDefaults,
|
|
20
|
+
} from "../shared/openai-compatible";
|
|
21
|
+
import type {
|
|
22
|
+
ModelCatalogConfig,
|
|
23
|
+
ModelInfo,
|
|
24
|
+
ProviderCapability,
|
|
25
|
+
ProviderConfig,
|
|
26
|
+
} from "../types/index";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Provider defaults for OpenAI-compatible providers
|
|
30
|
+
*/
|
|
31
|
+
export interface ProviderDefaults {
|
|
32
|
+
/** Base URL for the API */
|
|
33
|
+
baseUrl: string;
|
|
34
|
+
/** Default model ID */
|
|
35
|
+
modelId: string;
|
|
36
|
+
/** Known models with their info */
|
|
37
|
+
knownModels?: Record<string, ModelInfo>;
|
|
38
|
+
/** Capabilities this provider supports */
|
|
39
|
+
capabilities?: ProviderCapability[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const DEFAULT_MODELS_CATALOG_URL = "https://models.dev/api.json";
|
|
43
|
+
const DEFAULT_MODELS_CATALOG_CACHE_TTL_MS = 10 * 60 * 1000;
|
|
44
|
+
const DEFAULT_PRIVATE_MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
45
|
+
|
|
46
|
+
const MODELS_CATALOG_CACHE = new Map<
|
|
47
|
+
string,
|
|
48
|
+
{ expiresAt: number; data: Record<string, Record<string, ModelInfo>> }
|
|
49
|
+
>();
|
|
50
|
+
const MODELS_CATALOG_IN_FLIGHT = new Map<
|
|
51
|
+
string,
|
|
52
|
+
Promise<Record<string, Record<string, ModelInfo>>>
|
|
53
|
+
>();
|
|
54
|
+
const PRIVATE_MODELS_CACHE = new Map<
|
|
55
|
+
string,
|
|
56
|
+
{ expiresAt: number; data: Record<string, ModelInfo> }
|
|
57
|
+
>();
|
|
58
|
+
const PRIVATE_MODELS_IN_FLIGHT = new Map<
|
|
59
|
+
string,
|
|
60
|
+
Promise<Record<string, ModelInfo>>
|
|
61
|
+
>();
|
|
62
|
+
|
|
63
|
+
let generatedModelsLoader:
|
|
64
|
+
| Promise<Record<string, Record<string, ModelInfo>>>
|
|
65
|
+
| undefined;
|
|
66
|
+
|
|
67
|
+
async function loadGeneratedProviderModels(): Promise<
|
|
68
|
+
Record<string, Record<string, ModelInfo>>
|
|
69
|
+
> {
|
|
70
|
+
generatedModelsLoader ??= import("../../models/generated-access").then(
|
|
71
|
+
({ getGeneratedProviderModels }) => getGeneratedProviderModels(),
|
|
72
|
+
);
|
|
73
|
+
return generatedModelsLoader;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function mergeKnownModels(
|
|
77
|
+
providerId: string,
|
|
78
|
+
defaultKnownModels: Record<string, ModelInfo> = {},
|
|
79
|
+
liveModels: Record<string, ModelInfo> = {},
|
|
80
|
+
privateModels: Record<string, ModelInfo> = {},
|
|
81
|
+
userKnownModels: Record<string, ModelInfo> = {},
|
|
82
|
+
): Promise<Record<string, ModelInfo>> {
|
|
83
|
+
const generatedProviderModels = await loadGeneratedProviderModels();
|
|
84
|
+
const generatedKeys = resolveProviderModelCatalogKeys(providerId);
|
|
85
|
+
const generated = Object.assign(
|
|
86
|
+
{},
|
|
87
|
+
...generatedKeys.map(
|
|
88
|
+
(generatedKey) => generatedProviderModels[generatedKey] ?? {},
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
return sortModelsByReleaseDate({
|
|
92
|
+
...generated,
|
|
93
|
+
...defaultKnownModels,
|
|
94
|
+
...liveModels,
|
|
95
|
+
...privateModels,
|
|
96
|
+
...userKnownModels,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function normalizeBaseUrl(baseUrl: string | undefined): string {
|
|
101
|
+
const value = baseUrl?.trim();
|
|
102
|
+
return value && value.length > 0 ? value : "";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function resolveAuthToken(
|
|
106
|
+
config: Pick<ProviderConfig, "apiKey" | "accessToken">,
|
|
107
|
+
): string | undefined {
|
|
108
|
+
const token = config.apiKey?.trim() || config.accessToken?.trim();
|
|
109
|
+
return token && token.length > 0 ? token : undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function fingerprint(value: string): string {
|
|
113
|
+
let hash = 2166136261;
|
|
114
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
115
|
+
hash ^= value.charCodeAt(i);
|
|
116
|
+
hash +=
|
|
117
|
+
(hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
118
|
+
}
|
|
119
|
+
return (hash >>> 0).toString(16);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resolvePrivateCacheKey(
|
|
123
|
+
providerId: string,
|
|
124
|
+
config: ProviderConfig,
|
|
125
|
+
): string {
|
|
126
|
+
return `${providerId}:${normalizeBaseUrl(config.baseUrl)}:${fingerprint(resolveAuthToken(config) ?? "")}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function includeCapability(
|
|
130
|
+
capabilities: NonNullable<ModelInfo["capabilities"]>,
|
|
131
|
+
capability: NonNullable<ModelInfo["capabilities"]>[number],
|
|
132
|
+
when: boolean,
|
|
133
|
+
): void {
|
|
134
|
+
if (when && !capabilities.includes(capability)) {
|
|
135
|
+
capabilities.push(capability);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function buildModelFromPrivateSource(
|
|
140
|
+
id: string,
|
|
141
|
+
input: {
|
|
142
|
+
name?: string;
|
|
143
|
+
contextWindow?: number;
|
|
144
|
+
maxTokens?: number;
|
|
145
|
+
supportsImages?: boolean;
|
|
146
|
+
supportsPromptCache?: boolean;
|
|
147
|
+
supportsReasoning?: boolean;
|
|
148
|
+
releaseDate?: string;
|
|
149
|
+
},
|
|
150
|
+
): ModelInfo {
|
|
151
|
+
const capabilities: NonNullable<ModelInfo["capabilities"]> = [
|
|
152
|
+
"streaming",
|
|
153
|
+
"tools",
|
|
154
|
+
];
|
|
155
|
+
includeCapability(capabilities, "images", Boolean(input.supportsImages));
|
|
156
|
+
includeCapability(
|
|
157
|
+
capabilities,
|
|
158
|
+
"prompt-cache",
|
|
159
|
+
Boolean(input.supportsPromptCache),
|
|
160
|
+
);
|
|
161
|
+
includeCapability(
|
|
162
|
+
capabilities,
|
|
163
|
+
"reasoning",
|
|
164
|
+
Boolean(input.supportsReasoning),
|
|
165
|
+
);
|
|
166
|
+
return {
|
|
167
|
+
id,
|
|
168
|
+
name: input.name ?? id,
|
|
169
|
+
contextWindow: input.contextWindow,
|
|
170
|
+
maxTokens: input.maxTokens,
|
|
171
|
+
capabilities,
|
|
172
|
+
releaseDate: input.releaseDate,
|
|
173
|
+
status: "active",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
interface BasetenModelResponse {
|
|
178
|
+
id?: string;
|
|
179
|
+
object?: string;
|
|
180
|
+
supported_features?: string[];
|
|
181
|
+
context_length?: number;
|
|
182
|
+
max_completion_tokens?: number;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function fetchBasetenPrivateModels(
|
|
186
|
+
_config: ProviderConfig,
|
|
187
|
+
token: string,
|
|
188
|
+
): Promise<Record<string, ModelInfo>> {
|
|
189
|
+
const response = await fetch("https://inference.baseten.co/v1/models", {
|
|
190
|
+
method: "GET",
|
|
191
|
+
headers: {
|
|
192
|
+
Authorization: `Bearer ${token}`,
|
|
193
|
+
"Content-Type": "application/json",
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
throw new Error(`Baseten model refresh failed: HTTP ${response.status}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const payload = (await response.json()) as { data?: BasetenModelResponse[] };
|
|
201
|
+
const entries = payload?.data ?? [];
|
|
202
|
+
const models: Record<string, ModelInfo> = {};
|
|
203
|
+
for (const model of entries) {
|
|
204
|
+
const id = model.id?.trim();
|
|
205
|
+
if (!id) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (
|
|
209
|
+
id.includes("whisper") ||
|
|
210
|
+
id.includes("tts") ||
|
|
211
|
+
id.includes("embedding")
|
|
212
|
+
) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const features = model.supported_features ?? [];
|
|
216
|
+
models[id] = buildModelFromPrivateSource(id, {
|
|
217
|
+
name: id,
|
|
218
|
+
contextWindow: model.context_length,
|
|
219
|
+
maxTokens: model.max_completion_tokens,
|
|
220
|
+
supportsReasoning:
|
|
221
|
+
features.includes("reasoning") || features.includes("reasoning_effort"),
|
|
222
|
+
supportsImages: false,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return models;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
interface HicapModelResponse {
|
|
229
|
+
id?: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function fetchHicapPrivateModels(
|
|
233
|
+
_config: ProviderConfig,
|
|
234
|
+
token: string,
|
|
235
|
+
): Promise<Record<string, ModelInfo>> {
|
|
236
|
+
const response = await fetch("https://api.hicap.ai/v2/openai/models", {
|
|
237
|
+
method: "GET",
|
|
238
|
+
headers: {
|
|
239
|
+
"api-key": token,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
if (!response.ok) {
|
|
243
|
+
throw new Error(`Hicap model refresh failed: HTTP ${response.status}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const payload = (await response.json()) as { data?: HicapModelResponse[] };
|
|
247
|
+
const entries = payload?.data ?? [];
|
|
248
|
+
const models: Record<string, ModelInfo> = {};
|
|
249
|
+
for (const model of entries) {
|
|
250
|
+
const id = model.id?.trim();
|
|
251
|
+
if (!id) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
models[id] = buildModelFromPrivateSource(id, {
|
|
255
|
+
name: id,
|
|
256
|
+
contextWindow: 128_000,
|
|
257
|
+
supportsImages: true,
|
|
258
|
+
supportsPromptCache: true,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return models;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface LiteLlmModelInfoResponse {
|
|
265
|
+
model_name?: string;
|
|
266
|
+
litellm_params?: {
|
|
267
|
+
model?: string;
|
|
268
|
+
};
|
|
269
|
+
model_info?: {
|
|
270
|
+
max_output_tokens?: number;
|
|
271
|
+
max_tokens?: number;
|
|
272
|
+
max_input_tokens?: number;
|
|
273
|
+
supports_vision?: boolean;
|
|
274
|
+
supports_prompt_caching?: boolean;
|
|
275
|
+
supports_reasoning?: boolean;
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function normalizeLiteLlmBaseUrl(baseUrl: string | undefined): string {
|
|
280
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
281
|
+
if (!normalized) {
|
|
282
|
+
return "http://localhost:4000";
|
|
283
|
+
}
|
|
284
|
+
return normalized.endsWith("/v1") ? normalized.slice(0, -3) : normalized;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function fetchLiteLlmPrivateModels(
|
|
288
|
+
config: ProviderConfig,
|
|
289
|
+
token: string,
|
|
290
|
+
): Promise<Record<string, ModelInfo>> {
|
|
291
|
+
const baseUrl = normalizeLiteLlmBaseUrl(config.baseUrl);
|
|
292
|
+
const endpoint = `${baseUrl}/v1/model/info`;
|
|
293
|
+
|
|
294
|
+
const fetchWithHeaders = async (
|
|
295
|
+
headers: Record<string, string>,
|
|
296
|
+
): Promise<Response> =>
|
|
297
|
+
fetch(endpoint, {
|
|
298
|
+
method: "GET",
|
|
299
|
+
headers: {
|
|
300
|
+
accept: "application/json",
|
|
301
|
+
...headers,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
let response = await fetchWithHeaders({ "x-litellm-api-key": token });
|
|
306
|
+
if (!response.ok) {
|
|
307
|
+
response = await fetchWithHeaders({ Authorization: `Bearer ${token}` });
|
|
308
|
+
}
|
|
309
|
+
if (!response.ok) {
|
|
310
|
+
throw new Error(`LiteLLM model refresh failed: HTTP ${response.status}`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const payload = (await response.json()) as {
|
|
314
|
+
data?: LiteLlmModelInfoResponse[];
|
|
315
|
+
};
|
|
316
|
+
const entries = payload?.data ?? [];
|
|
317
|
+
const models: Record<string, ModelInfo> = {};
|
|
318
|
+
for (const model of entries) {
|
|
319
|
+
const displayName = model.model_name?.trim();
|
|
320
|
+
const actualModelId = model.litellm_params?.model?.trim();
|
|
321
|
+
const modelId = actualModelId || displayName;
|
|
322
|
+
if (!modelId) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const info = model.model_info;
|
|
326
|
+
const converted = buildModelFromPrivateSource(modelId, {
|
|
327
|
+
name: displayName ?? modelId,
|
|
328
|
+
maxTokens: info?.max_output_tokens ?? info?.max_tokens,
|
|
329
|
+
contextWindow: info?.max_input_tokens ?? info?.max_tokens,
|
|
330
|
+
supportsImages: info?.supports_vision,
|
|
331
|
+
supportsPromptCache: info?.supports_prompt_caching,
|
|
332
|
+
supportsReasoning: info?.supports_reasoning,
|
|
333
|
+
});
|
|
334
|
+
models[modelId] = converted;
|
|
335
|
+
if (displayName) {
|
|
336
|
+
models[displayName] = {
|
|
337
|
+
...converted,
|
|
338
|
+
id: displayName,
|
|
339
|
+
name: displayName,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return models;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function fetchPrivateProviderModels(
|
|
347
|
+
providerId: string,
|
|
348
|
+
config: ProviderConfig,
|
|
349
|
+
): Promise<Record<string, ModelInfo>> {
|
|
350
|
+
const token = resolveAuthToken(config);
|
|
351
|
+
if (!token) {
|
|
352
|
+
return {};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const fetcher = PRIVATE_PROVIDER_MODEL_FETCHERS[providerId];
|
|
356
|
+
if (!fetcher) {
|
|
357
|
+
return {};
|
|
358
|
+
}
|
|
359
|
+
return fetcher(config, token);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
type PrivateProviderModelFetcher = (
|
|
363
|
+
config: ProviderConfig,
|
|
364
|
+
token: string,
|
|
365
|
+
) => Promise<Record<string, ModelInfo>>;
|
|
366
|
+
|
|
367
|
+
const PRIVATE_PROVIDER_MODEL_FETCHERS: Record<
|
|
368
|
+
string,
|
|
369
|
+
PrivateProviderModelFetcher
|
|
370
|
+
> = {
|
|
371
|
+
baseten: fetchBasetenPrivateModels,
|
|
372
|
+
hicap: fetchHicapPrivateModels,
|
|
373
|
+
litellm: fetchLiteLlmPrivateModels,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
function shouldLoadPrivateModels(
|
|
377
|
+
providerId: string,
|
|
378
|
+
modelCatalog: ModelCatalogConfig | undefined,
|
|
379
|
+
config: ProviderConfig | undefined,
|
|
380
|
+
): boolean {
|
|
381
|
+
if (!config) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
if (!PRIVATE_PROVIDER_MODEL_FETCHERS[providerId]) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
if (modelCatalog?.loadPrivateOnAuth === false) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
return Boolean(resolveAuthToken(config));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function getPrivateProviderModels(
|
|
394
|
+
providerId: string,
|
|
395
|
+
modelCatalog: ModelCatalogConfig | undefined,
|
|
396
|
+
config: ProviderConfig,
|
|
397
|
+
): Promise<Record<string, ModelInfo>> {
|
|
398
|
+
const cacheTtlMs =
|
|
399
|
+
modelCatalog?.cacheTtlMs ?? DEFAULT_PRIVATE_MODELS_CACHE_TTL_MS;
|
|
400
|
+
const cacheKey = resolvePrivateCacheKey(providerId, config);
|
|
401
|
+
const now = Date.now();
|
|
402
|
+
|
|
403
|
+
const cached = PRIVATE_MODELS_CACHE.get(cacheKey);
|
|
404
|
+
if (cached && cached.expiresAt > now) {
|
|
405
|
+
return cached.data;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const inFlight = PRIVATE_MODELS_IN_FLIGHT.get(cacheKey);
|
|
409
|
+
if (inFlight) {
|
|
410
|
+
return inFlight;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const request = fetchPrivateProviderModels(providerId, config)
|
|
414
|
+
.then((data) => {
|
|
415
|
+
PRIVATE_MODELS_CACHE.set(cacheKey, {
|
|
416
|
+
data,
|
|
417
|
+
expiresAt: now + cacheTtlMs,
|
|
418
|
+
});
|
|
419
|
+
return data;
|
|
420
|
+
})
|
|
421
|
+
.finally(() => {
|
|
422
|
+
PRIVATE_MODELS_IN_FLIGHT.delete(cacheKey);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
PRIVATE_MODELS_IN_FLIGHT.set(cacheKey, request);
|
|
426
|
+
return request;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function fetchLiveModelsCatalog(
|
|
430
|
+
url: string,
|
|
431
|
+
): Promise<Record<string, Record<string, ModelInfo>>> {
|
|
432
|
+
return fetchModelsDevProviderModels(url, MODELS_DEV_PROVIDER_KEY_MAP);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export async function getLiveModelsCatalog(
|
|
436
|
+
options: Pick<ModelCatalogConfig, "url" | "cacheTtlMs"> = {},
|
|
437
|
+
): Promise<Record<string, Record<string, ModelInfo>>> {
|
|
438
|
+
const url = options.url ?? DEFAULT_MODELS_CATALOG_URL;
|
|
439
|
+
const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_MODELS_CATALOG_CACHE_TTL_MS;
|
|
440
|
+
const now = Date.now();
|
|
441
|
+
|
|
442
|
+
const cached = MODELS_CATALOG_CACHE.get(url);
|
|
443
|
+
if (cached && cached.expiresAt > now) {
|
|
444
|
+
return cached.data;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const inFlight = MODELS_CATALOG_IN_FLIGHT.get(url);
|
|
448
|
+
if (inFlight) {
|
|
449
|
+
return inFlight;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const request = fetchLiveModelsCatalog(url)
|
|
453
|
+
.then((data) => {
|
|
454
|
+
MODELS_CATALOG_CACHE.set(url, { data, expiresAt: now + cacheTtlMs });
|
|
455
|
+
return data;
|
|
456
|
+
})
|
|
457
|
+
.finally(() => {
|
|
458
|
+
MODELS_CATALOG_IN_FLIGHT.delete(url);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
MODELS_CATALOG_IN_FLIGHT.set(url, request);
|
|
462
|
+
return request;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export function clearLiveModelsCatalogCache(url?: string): void {
|
|
466
|
+
if (url) {
|
|
467
|
+
MODELS_CATALOG_CACHE.delete(url);
|
|
468
|
+
MODELS_CATALOG_IN_FLIGHT.delete(url);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
MODELS_CATALOG_CACHE.clear();
|
|
473
|
+
MODELS_CATALOG_IN_FLIGHT.clear();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export function clearPrivateModelsCatalogCache(): void {
|
|
477
|
+
PRIVATE_MODELS_CACHE.clear();
|
|
478
|
+
PRIVATE_MODELS_IN_FLIGHT.clear();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function toRuntimeProviderDefaults(
|
|
482
|
+
defaults: Record<string, OpenAICompatibleProviderDefaults>,
|
|
483
|
+
): Record<string, ProviderDefaults> {
|
|
484
|
+
return Object.fromEntries(
|
|
485
|
+
Object.entries(defaults).map(([providerId, providerDefaults]) => [
|
|
486
|
+
providerId,
|
|
487
|
+
{
|
|
488
|
+
baseUrl: providerDefaults.baseUrl,
|
|
489
|
+
modelId: providerDefaults.modelId,
|
|
490
|
+
knownModels: providerDefaults.knownModels,
|
|
491
|
+
capabilities: providerDefaults.capabilities as
|
|
492
|
+
| ProviderCapability[]
|
|
493
|
+
| undefined,
|
|
494
|
+
},
|
|
495
|
+
]),
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* All OpenAI-compatible provider configurations
|
|
501
|
+
*
|
|
502
|
+
* Model data is sourced from @clinebot/models to maintain a single source of truth.
|
|
503
|
+
*/
|
|
504
|
+
export const OPENAI_COMPATIBLE_PROVIDERS: Record<string, ProviderDefaults> =
|
|
505
|
+
toRuntimeProviderDefaults(buildOpenAICompatibleProviderDefaults());
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Get provider configuration by ID
|
|
509
|
+
*/
|
|
510
|
+
export function getProviderConfig(
|
|
511
|
+
providerId: string,
|
|
512
|
+
): ProviderDefaults | undefined {
|
|
513
|
+
return OPENAI_COMPATIBLE_PROVIDERS[providerId];
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Resolve provider configuration and optionally merge live catalog metadata
|
|
518
|
+
*/
|
|
519
|
+
export async function resolveProviderConfig(
|
|
520
|
+
providerId: string,
|
|
521
|
+
modelCatalog?: ModelCatalogConfig,
|
|
522
|
+
config?: ProviderConfig,
|
|
523
|
+
): Promise<ProviderDefaults | undefined> {
|
|
524
|
+
const defaults = getProviderConfig(providerId);
|
|
525
|
+
if (!defaults) {
|
|
526
|
+
return undefined;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const liveCatalog = modelCatalog?.loadLatestOnInit
|
|
531
|
+
? await getLiveModelsCatalog(modelCatalog)
|
|
532
|
+
: undefined;
|
|
533
|
+
const liveModels = liveCatalog?.[providerId] ?? {};
|
|
534
|
+
const privateModels =
|
|
535
|
+
config && shouldLoadPrivateModels(providerId, modelCatalog, config)
|
|
536
|
+
? await getPrivateProviderModels(providerId, modelCatalog, config)
|
|
537
|
+
: {};
|
|
538
|
+
const knownModels = await mergeKnownModels(
|
|
539
|
+
providerId,
|
|
540
|
+
defaults.knownModels,
|
|
541
|
+
liveModels,
|
|
542
|
+
privateModels,
|
|
543
|
+
config?.knownModels,
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
...defaults,
|
|
548
|
+
knownModels,
|
|
549
|
+
};
|
|
550
|
+
} catch (error) {
|
|
551
|
+
if (modelCatalog?.failOnError) {
|
|
552
|
+
throw error;
|
|
553
|
+
}
|
|
554
|
+
return defaults;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Check if a provider is OpenAI-compatible
|
|
560
|
+
*/
|
|
561
|
+
export function isOpenAICompatibleProvider(providerId: string): boolean {
|
|
562
|
+
return providerId in OPENAI_COMPATIBLE_PROVIDERS;
|
|
563
|
+
}
|