@ottocode/sdk 0.1.310 → 0.1.312
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/package.json +2 -2
- package/src/auth/src/index.ts +9 -0
- package/src/auth/src/kimi-oauth.ts +196 -0
- package/src/core/src/providers/resolver.ts +5 -3
- package/src/index.ts +19 -2
- package/src/prompts/src/agents/otto.txt +16 -14
- package/src/providers/src/catalog-manual.ts +40 -1
- package/src/providers/src/catalog.ts +200 -159
- package/src/providers/src/env.ts +15 -2
- package/src/providers/src/index.ts +9 -2
- package/src/providers/src/model-merge.ts +27 -0
- package/src/providers/src/moonshot-client.ts +31 -8
- package/src/providers/src/registry.ts +54 -15
- package/src/providers/src/utils.ts +14 -8
- package/src/providers/src/validate.ts +10 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.312",
|
|
4
4
|
"description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
|
|
5
5
|
"author": "nitishxyz",
|
|
6
6
|
"license": "MIT",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
110
110
|
"@openauthjs/openauth": "0.4.3",
|
|
111
111
|
"@openrouter/ai-sdk-provider": "1.5.4",
|
|
112
|
-
"@ottorouter/ai-sdk": "0.2.
|
|
112
|
+
"@ottorouter/ai-sdk": "0.2.7",
|
|
113
113
|
"@solana/web3.js": "1.98.4",
|
|
114
114
|
"ai": "6.0.199",
|
|
115
115
|
"ai-sdk-ollama": "3.8.3",
|
package/src/auth/src/index.ts
CHANGED
|
@@ -97,6 +97,15 @@ export {
|
|
|
97
97
|
type XaiOAuthTokens,
|
|
98
98
|
} from './xai-oauth.ts';
|
|
99
99
|
|
|
100
|
+
export {
|
|
101
|
+
refreshKimiToken,
|
|
102
|
+
requestKimiDeviceCode,
|
|
103
|
+
pollKimiDeviceCodeOnce,
|
|
104
|
+
type KimiOAuthTokens,
|
|
105
|
+
type KimiDeviceCodeResponse,
|
|
106
|
+
type KimiDevicePollResult,
|
|
107
|
+
} from './kimi-oauth.ts';
|
|
108
|
+
|
|
100
109
|
export {
|
|
101
110
|
generateWallet,
|
|
102
111
|
importWallet,
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
const KIMI_CODE_OAUTH_CLIENT_ID = '17e5f671-d194-4dfb-9706-5516cb48c098';
|
|
2
|
+
|
|
3
|
+
function kimiOAuthHost(): string {
|
|
4
|
+
return (
|
|
5
|
+
process.env.KIMI_CODE_OAUTH_HOST ??
|
|
6
|
+
process.env.KIMI_OAUTH_HOST ??
|
|
7
|
+
'https://auth.kimi.com'
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Kimi Code OAuth tokens normalized to otto's OAuth shape (expires in epoch ms). */
|
|
12
|
+
export type KimiOAuthTokens = {
|
|
13
|
+
access: string;
|
|
14
|
+
refresh: string;
|
|
15
|
+
expires: number;
|
|
16
|
+
scopes?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Response from a Kimi Code OAuth device authorization request. */
|
|
20
|
+
export type KimiDeviceCodeResponse = {
|
|
21
|
+
userCode: string;
|
|
22
|
+
deviceCode: string;
|
|
23
|
+
verificationUri: string;
|
|
24
|
+
interval: number;
|
|
25
|
+
expiresIn: number | null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Result of a single Kimi device-code token poll attempt. */
|
|
29
|
+
export type KimiDevicePollResult =
|
|
30
|
+
| { status: 'complete'; tokens: KimiOAuthTokens }
|
|
31
|
+
| { status: 'pending' }
|
|
32
|
+
| { status: 'error'; error: string };
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Request a Kimi Code OAuth device authorization code.
|
|
36
|
+
*
|
|
37
|
+
* Mirrors the kimi-cli device flow: form-encoded POST to
|
|
38
|
+
* `https://auth.kimi.com/api/oauth/device_authorization` with `client_id`.
|
|
39
|
+
*/
|
|
40
|
+
export async function requestKimiDeviceCode(): Promise<KimiDeviceCodeResponse> {
|
|
41
|
+
const response = await fetch(
|
|
42
|
+
`${kimiOAuthHost().replace(/\/$/, '')}/api/oauth/device_authorization`,
|
|
43
|
+
{
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
47
|
+
Accept: 'application/json',
|
|
48
|
+
},
|
|
49
|
+
body: new URLSearchParams({
|
|
50
|
+
client_id: KIMI_CODE_OAUTH_CLIENT_ID,
|
|
51
|
+
}).toString(),
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
const data = (await response.json().catch(() => ({}))) as Record<
|
|
55
|
+
string,
|
|
56
|
+
unknown
|
|
57
|
+
>;
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Kimi OAuth device authorization failed (${response.status})`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const userCode = data.user_code;
|
|
64
|
+
const deviceCode = data.device_code;
|
|
65
|
+
const verificationUriComplete = data.verification_uri_complete;
|
|
66
|
+
if (
|
|
67
|
+
typeof userCode !== 'string' ||
|
|
68
|
+
typeof deviceCode !== 'string' ||
|
|
69
|
+
typeof verificationUriComplete !== 'string'
|
|
70
|
+
) {
|
|
71
|
+
throw new Error('Kimi OAuth device authorization response was incomplete.');
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
userCode,
|
|
75
|
+
deviceCode,
|
|
76
|
+
verificationUri: verificationUriComplete,
|
|
77
|
+
interval: Number(data.interval ?? 5),
|
|
78
|
+
expiresIn:
|
|
79
|
+
data.expires_in === undefined || data.expires_in === null
|
|
80
|
+
? null
|
|
81
|
+
: Number(data.expires_in),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Poll the Kimi Code OAuth token endpoint once for a device-code grant.
|
|
87
|
+
*
|
|
88
|
+
* Returns `pending` while authorization is outstanding (including
|
|
89
|
+
* `slow_down`), `complete` with normalized tokens on success, and `error`
|
|
90
|
+
* for terminal failures such as `expired_token` or `access_denied`.
|
|
91
|
+
*/
|
|
92
|
+
export async function pollKimiDeviceCodeOnce(
|
|
93
|
+
deviceCode: string,
|
|
94
|
+
): Promise<KimiDevicePollResult> {
|
|
95
|
+
const response = await fetch(
|
|
96
|
+
`${kimiOAuthHost().replace(/\/$/, '')}/api/oauth/token`,
|
|
97
|
+
{
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: {
|
|
100
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
101
|
+
Accept: 'application/json',
|
|
102
|
+
},
|
|
103
|
+
body: new URLSearchParams({
|
|
104
|
+
client_id: KIMI_CODE_OAUTH_CLIENT_ID,
|
|
105
|
+
device_code: deviceCode,
|
|
106
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
107
|
+
}).toString(),
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
const data = (await response.json().catch(() => ({}))) as Record<
|
|
111
|
+
string,
|
|
112
|
+
unknown
|
|
113
|
+
>;
|
|
114
|
+
if (response.ok && typeof data.access_token === 'string') {
|
|
115
|
+
const expiresIn = Number(data.expires_in ?? 0);
|
|
116
|
+
return {
|
|
117
|
+
status: 'complete',
|
|
118
|
+
tokens: {
|
|
119
|
+
access: data.access_token,
|
|
120
|
+
refresh:
|
|
121
|
+
typeof data.refresh_token === 'string' ? data.refresh_token : '',
|
|
122
|
+
expires: Date.now() + expiresIn * 1000,
|
|
123
|
+
scopes: typeof data.scope === 'string' ? data.scope : undefined,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const errorCode =
|
|
128
|
+
typeof data.error === 'string' ? data.error : 'unknown_error';
|
|
129
|
+
if (errorCode === 'authorization_pending' || errorCode === 'slow_down') {
|
|
130
|
+
return { status: 'pending' };
|
|
131
|
+
}
|
|
132
|
+
if (errorCode === 'expired_token') {
|
|
133
|
+
return { status: 'error', error: 'Kimi OAuth code expired.' };
|
|
134
|
+
}
|
|
135
|
+
if (errorCode === 'access_denied') {
|
|
136
|
+
return { status: 'error', error: 'Kimi OAuth access denied.' };
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
status: 'error',
|
|
140
|
+
error: `Kimi OAuth token polling failed: ${errorCode}`,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Refresh a Kimi Code OAuth access token using the refresh_token grant.
|
|
146
|
+
*
|
|
147
|
+
* Mirrors the official kimi-cli flow: form-encoded POST to
|
|
148
|
+
* `https://auth.kimi.com/api/oauth/token` with `client_id`,
|
|
149
|
+
* `grant_type=refresh_token`, and `refresh_token`. Kimi rotates refresh
|
|
150
|
+
* tokens on every refresh, so callers must persist the returned tokens.
|
|
151
|
+
*/
|
|
152
|
+
export async function refreshKimiToken(
|
|
153
|
+
refreshToken: string,
|
|
154
|
+
): Promise<KimiOAuthTokens> {
|
|
155
|
+
const response = await fetch(
|
|
156
|
+
`${kimiOAuthHost().replace(/\/$/, '')}/api/oauth/token`,
|
|
157
|
+
{
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: {
|
|
160
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
161
|
+
Accept: 'application/json',
|
|
162
|
+
},
|
|
163
|
+
body: new URLSearchParams({
|
|
164
|
+
client_id: KIMI_CODE_OAUTH_CLIENT_ID,
|
|
165
|
+
grant_type: 'refresh_token',
|
|
166
|
+
refresh_token: refreshToken,
|
|
167
|
+
}).toString(),
|
|
168
|
+
},
|
|
169
|
+
);
|
|
170
|
+
const data = (await response.json().catch(() => ({}))) as Record<
|
|
171
|
+
string,
|
|
172
|
+
unknown
|
|
173
|
+
>;
|
|
174
|
+
if (!response.ok || typeof data.access_token !== 'string') {
|
|
175
|
+
const description =
|
|
176
|
+
typeof data.error_description === 'string'
|
|
177
|
+
? data.error_description
|
|
178
|
+
: `HTTP ${response.status}`;
|
|
179
|
+
if (response.status === 401 || response.status === 403) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`Kimi OAuth refresh token rejected (${description}). Run \`otto auth login kimi\` again.`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`Kimi OAuth token refresh failed: ${description}`);
|
|
185
|
+
}
|
|
186
|
+
const expiresIn = Number(data.expires_in ?? 0);
|
|
187
|
+
return {
|
|
188
|
+
access: data.access_token,
|
|
189
|
+
refresh:
|
|
190
|
+
typeof data.refresh_token === 'string' && data.refresh_token
|
|
191
|
+
? data.refresh_token
|
|
192
|
+
: refreshToken,
|
|
193
|
+
expires: Date.now() + expiresIn * 1000,
|
|
194
|
+
scopes: typeof data.scope === 'string' ? data.scope : undefined,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -7,7 +7,7 @@ import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
|
7
7
|
import {
|
|
8
8
|
catalog,
|
|
9
9
|
createMinimaxModel,
|
|
10
|
-
|
|
10
|
+
createKimiModel,
|
|
11
11
|
createOttoRouterModel,
|
|
12
12
|
createOpenAIOAuthModel,
|
|
13
13
|
createXaiModel,
|
|
@@ -34,6 +34,7 @@ export type ProviderName =
|
|
|
34
34
|
| 'zai'
|
|
35
35
|
| 'zai-coding'
|
|
36
36
|
| 'moonshot'
|
|
37
|
+
| 'kimi'
|
|
37
38
|
| 'minimax';
|
|
38
39
|
|
|
39
40
|
export type ModelConfig = {
|
|
@@ -221,10 +222,11 @@ export async function resolveModel(
|
|
|
221
222
|
});
|
|
222
223
|
}
|
|
223
224
|
|
|
224
|
-
if (provider === 'moonshot') {
|
|
225
|
-
return
|
|
225
|
+
if (provider === 'moonshot' || provider === 'kimi') {
|
|
226
|
+
return createKimiModel(model, {
|
|
226
227
|
apiKey: config.apiKey,
|
|
227
228
|
baseURL: config.baseURL,
|
|
229
|
+
oauth: config.oauth,
|
|
228
230
|
});
|
|
229
231
|
}
|
|
230
232
|
|
package/src/index.ts
CHANGED
|
@@ -160,8 +160,15 @@ export {
|
|
|
160
160
|
export type { OpenRouterProviderConfig } from './providers/src/index.ts';
|
|
161
161
|
export { createOpencodeModel } from './providers/src/index.ts';
|
|
162
162
|
export type { OpencodeProviderConfig } from './providers/src/index.ts';
|
|
163
|
-
export {
|
|
164
|
-
|
|
163
|
+
export {
|
|
164
|
+
createKimiModel,
|
|
165
|
+
createMoonshotModel,
|
|
166
|
+
readKimiApiKeyFromEnv,
|
|
167
|
+
} from './providers/src/index.ts';
|
|
168
|
+
export type {
|
|
169
|
+
KimiProviderConfig,
|
|
170
|
+
MoonshotProviderConfig,
|
|
171
|
+
} from './providers/src/index.ts';
|
|
165
172
|
export { createMinimaxModel } from './providers/src/index.ts';
|
|
166
173
|
export type { MinimaxProviderConfig } from './providers/src/index.ts';
|
|
167
174
|
export {
|
|
@@ -211,6 +218,16 @@ export {
|
|
|
211
218
|
readGrokCliAuth,
|
|
212
219
|
} from './auth/src/index.ts';
|
|
213
220
|
export type { XaiOAuthResult, XaiOAuthTokens } from './auth/src/index.ts';
|
|
221
|
+
export {
|
|
222
|
+
refreshKimiToken,
|
|
223
|
+
requestKimiDeviceCode,
|
|
224
|
+
pollKimiDeviceCodeOnce,
|
|
225
|
+
} from './auth/src/index.ts';
|
|
226
|
+
export type {
|
|
227
|
+
KimiOAuthTokens,
|
|
228
|
+
KimiDeviceCodeResponse,
|
|
229
|
+
KimiDevicePollResult,
|
|
230
|
+
} from './auth/src/index.ts';
|
|
214
231
|
export {
|
|
215
232
|
generateWallet,
|
|
216
233
|
importWallet,
|
|
@@ -1,32 +1,34 @@
|
|
|
1
|
-
You are otto, the
|
|
1
|
+
You are otto, the orchestrator agent for ottocode. You own the goal and its task queue. You are the only writer of goal state: workers never see or update goals. You verify, update task state, and dispatch work. You never edit files.
|
|
2
2
|
|
|
3
3
|
# Your job each wakeup
|
|
4
4
|
|
|
5
5
|
1. Read the goal and task list provided in the wake message (use goal_list for the latest state).
|
|
6
|
-
2. Read the recent
|
|
7
|
-
3. Verify
|
|
6
|
+
2. Read the recent worker-session conversation in the wake message. It shows what the user actually asked for, how the agent replied, and how your own previous [otto] continuations were answered.
|
|
7
|
+
3. Verify finished work mechanically. For each in_progress task whose work appears finished (the worker replied, or a delegated sub-agent returned a result):
|
|
8
8
|
- Use read, ls, tree, search, and glob to check that the claimed artifacts actually exist (files created, code changed, etc.).
|
|
9
|
-
- The agent's reply
|
|
9
|
+
- The agent's reply or the delegation result counts as evidence: if it directly confirms the work (e.g. it ran the check you asked for and reported the result), accept it.
|
|
10
10
|
- Task wording can be stale: if the user changed direction mid-goal, trust the task note and the conversation over the original task text. A task whose intent was satisfied a different way is completed, not dubious.
|
|
11
11
|
- Verification passed → set the task to completed via goal_update.
|
|
12
|
-
- Clearly
|
|
13
|
-
- Ambiguous AND not already answered in the conversation →
|
|
12
|
+
- Clearly unfinished (artifact missing) → keep the task in_progress with a note explaining what is missing.
|
|
13
|
+
- Ambiguous AND not already answered in the conversation → ask the worker to confirm via enqueue_session_message or message_subagent. Ask once: if the conversation shows you already asked and got an answer, act on the answer instead of re-asking.
|
|
14
14
|
4. Decide the next move:
|
|
15
15
|
- All tasks completed → complete the goal (goal_update with completeGoal) and stop. Do not enqueue anything.
|
|
16
|
-
- Tasks remain and the last run succeeded →
|
|
17
|
-
-
|
|
18
|
-
-
|
|
16
|
+
- Tasks remain and the last run succeeded → dispatch the next task(s): mark them in_progress via goal_update (recording the worker sessionId), then delegate with delegate_task or enqueue a short continuation into the worker session.
|
|
17
|
+
- Session reuse: when a task continues or relates to earlier work done by the same agent, pass that agent's previous child session id as reuseSessionId in delegate_task (completed tasks record it as sessionId in the goal). The worker keeps its context instead of re-discovering the codebase. Use a fresh session for unrelated work.
|
|
18
|
+
- Independent tasks may be dispatched in parallel to different agents; sequential tasks follow queue order.
|
|
19
|
+
- The last run errored → enqueue a continuation describing the error and asking the worker to recover or retry.
|
|
20
|
+
- Sub-agents are still running for tasks → do not dispatch duplicate work for those tasks.
|
|
19
21
|
5. If nothing useful can be done (no goal, nothing stale, nothing errored), do nothing and finish.
|
|
20
22
|
|
|
21
23
|
# Rules
|
|
22
24
|
|
|
23
|
-
- Never deeply judge code correctness. You check existence and obvious mechanical facts only; the
|
|
24
|
-
-
|
|
25
|
+
- Never deeply judge code correctness. You check existence and obvious mechanical facts only; the worker is the judge of its own work.
|
|
26
|
+
- You are the only writer of goal state. Workers have no goal tools; never instruct them to update goals.
|
|
25
27
|
- Never send the same (or nearly the same) continuation twice. If your previous message was answered, act on it; if it was ignored twice, mark the affected task blocked with a note and stop.
|
|
26
28
|
- If the user's latest instruction makes a task obsolete, cancel that task with a note instead of nudging the agent to do outdated work.
|
|
27
29
|
- Keep enqueued messages short and directive: which tasks remain, what to confirm, what failed.
|
|
28
|
-
- Sub-agent results are delivered to the
|
|
29
|
-
- If the user stopped the
|
|
30
|
-
- Enqueue at most one continuation per wakeup.
|
|
30
|
+
- Sub-agent results are delivered to the dispatching session automatically. Never repeat, summarize, or re-send them.
|
|
31
|
+
- If the user stopped the worker session, you will not be woken. If a wake message ever overlaps with a user abort, do nothing.
|
|
32
|
+
- Enqueue at most one continuation per worker session per wakeup.
|
|
31
33
|
- Never ask the user questions. If something needs the user, mark the task blocked with a note and stop.
|
|
32
34
|
- Be cheap: minimal tool calls, minimal output.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ottorouterCatalog,
|
|
3
3
|
type OttoRouterModelCatalogEntry,
|
|
4
|
-
} from '@ottorouter/ai-sdk';
|
|
4
|
+
} from '@ottorouter/ai-sdk/catalog';
|
|
5
5
|
import type {
|
|
6
6
|
BuiltInProviderId,
|
|
7
7
|
ModelInfo,
|
|
@@ -14,6 +14,20 @@ type CatalogMap = Partial<Record<BuiltInProviderId, ProviderCatalogEntry>>;
|
|
|
14
14
|
const OLLAMA_CLOUD_ID: BuiltInProviderId = 'ollama-cloud';
|
|
15
15
|
const OTTOROUTER_ID: BuiltInProviderId = 'ottorouter';
|
|
16
16
|
|
|
17
|
+
const KIMI_K2_7_CODE_MODEL: ModelInfo = {
|
|
18
|
+
id: 'kimi-k2.7-code',
|
|
19
|
+
ownedBy: 'moonshot',
|
|
20
|
+
label: 'Kimi K2.7 Code',
|
|
21
|
+
modalities: { input: ['text', 'image', 'video'], output: ['text'] },
|
|
22
|
+
toolCall: true,
|
|
23
|
+
reasoningText: true,
|
|
24
|
+
attachment: true,
|
|
25
|
+
temperature: 1,
|
|
26
|
+
cost: { input: 0.95, output: 4, cacheRead: 0.19 },
|
|
27
|
+
limit: { context: 262_144, output: 32_768 },
|
|
28
|
+
provider: { npm: '@ai-sdk/openai-compatible' },
|
|
29
|
+
};
|
|
30
|
+
|
|
17
31
|
const XAI_GROK_CLI_MODELS: ModelInfo[] = [
|
|
18
32
|
{
|
|
19
33
|
id: 'grok-build',
|
|
@@ -154,6 +168,27 @@ export function appendXaiGrokCliModels<T extends { models: ModelInfo[] }>(
|
|
|
154
168
|
return { ...entry, models: [...mergedModels, ...missingModels] };
|
|
155
169
|
}
|
|
156
170
|
|
|
171
|
+
export function appendOfficialKimiModels<T extends ProviderCatalogEntry>(
|
|
172
|
+
entry: T | undefined,
|
|
173
|
+
): T | undefined {
|
|
174
|
+
if (!entry) return undefined;
|
|
175
|
+
const hasKimiK27Code = entry.models.some(
|
|
176
|
+
(model) => model.id === KIMI_K2_7_CODE_MODEL.id,
|
|
177
|
+
);
|
|
178
|
+
const env = Array.from(
|
|
179
|
+
new Set(['KIMI_API_KEY', 'MOONSHOT_API_KEY', ...(entry.env ?? [])]),
|
|
180
|
+
);
|
|
181
|
+
return {
|
|
182
|
+
...entry,
|
|
183
|
+
label: entry.label === 'Moonshot AI' ? 'Kimi' : entry.label,
|
|
184
|
+
env,
|
|
185
|
+
doc: 'https://platform.kimi.ai/docs/api/overview.md',
|
|
186
|
+
models: hasKimiK27Code
|
|
187
|
+
? entry.models
|
|
188
|
+
: [...entry.models, KIMI_K2_7_CODE_MODEL],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
157
192
|
export function mergeManualCatalog(
|
|
158
193
|
base: CatalogMap,
|
|
159
194
|
): Record<BuiltInProviderId, ProviderCatalogEntry> {
|
|
@@ -167,6 +202,10 @@ export function mergeManualCatalog(
|
|
|
167
202
|
if (xaiEntry) {
|
|
168
203
|
merged.xai = xaiEntry;
|
|
169
204
|
}
|
|
205
|
+
const moonshotEntry = appendOfficialKimiModels(merged.moonshot);
|
|
206
|
+
if (moonshotEntry) {
|
|
207
|
+
merged.moonshot = moonshotEntry;
|
|
208
|
+
}
|
|
170
209
|
if (manualEntry) {
|
|
171
210
|
merged[OTTOROUTER_ID] = manualEntry;
|
|
172
211
|
}
|
|
@@ -2170,6 +2170,32 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
2170
2170
|
openrouter: {
|
|
2171
2171
|
id: 'openrouter',
|
|
2172
2172
|
models: [
|
|
2173
|
+
{
|
|
2174
|
+
id: '~anthropic/claude-fable-latest',
|
|
2175
|
+
ownedBy: 'anthropic',
|
|
2176
|
+
label: 'Claude Fable Latest',
|
|
2177
|
+
modalities: {
|
|
2178
|
+
input: ['text', 'image', 'pdf'],
|
|
2179
|
+
output: ['text'],
|
|
2180
|
+
},
|
|
2181
|
+
toolCall: true,
|
|
2182
|
+
reasoningText: true,
|
|
2183
|
+
attachment: true,
|
|
2184
|
+
temperature: false,
|
|
2185
|
+
releaseDate: '2026-06-09',
|
|
2186
|
+
lastUpdated: '2026-06-09',
|
|
2187
|
+
openWeights: false,
|
|
2188
|
+
cost: {
|
|
2189
|
+
input: 10,
|
|
2190
|
+
output: 50,
|
|
2191
|
+
cacheRead: 1,
|
|
2192
|
+
cacheWrite: 12.5,
|
|
2193
|
+
},
|
|
2194
|
+
limit: {
|
|
2195
|
+
context: 1000000,
|
|
2196
|
+
output: 128000,
|
|
2197
|
+
},
|
|
2198
|
+
},
|
|
2173
2199
|
{
|
|
2174
2200
|
id: '~anthropic/claude-haiku-latest',
|
|
2175
2201
|
ownedBy: 'anthropic',
|
|
@@ -2317,13 +2343,13 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
2317
2343
|
lastUpdated: '2026-04-27',
|
|
2318
2344
|
openWeights: false,
|
|
2319
2345
|
cost: {
|
|
2320
|
-
input: 0.
|
|
2321
|
-
output: 3.
|
|
2322
|
-
cacheRead: 0.
|
|
2346
|
+
input: 0.67,
|
|
2347
|
+
output: 3.39,
|
|
2348
|
+
cacheRead: 0.14,
|
|
2323
2349
|
},
|
|
2324
2350
|
limit: {
|
|
2325
|
-
context:
|
|
2326
|
-
output:
|
|
2351
|
+
context: 262144,
|
|
2352
|
+
output: 262144,
|
|
2327
2353
|
},
|
|
2328
2354
|
},
|
|
2329
2355
|
{
|
|
@@ -2575,6 +2601,33 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
2575
2601
|
output: 8192,
|
|
2576
2602
|
},
|
|
2577
2603
|
},
|
|
2604
|
+
{
|
|
2605
|
+
id: 'anthropic/claude-fable-5',
|
|
2606
|
+
ownedBy: 'anthropic',
|
|
2607
|
+
label: 'Claude Fable 5',
|
|
2608
|
+
modalities: {
|
|
2609
|
+
input: ['text', 'image', 'pdf'],
|
|
2610
|
+
output: ['text'],
|
|
2611
|
+
},
|
|
2612
|
+
toolCall: true,
|
|
2613
|
+
reasoningText: true,
|
|
2614
|
+
attachment: true,
|
|
2615
|
+
temperature: false,
|
|
2616
|
+
knowledge: '2026-01-31',
|
|
2617
|
+
releaseDate: '2026-06-09',
|
|
2618
|
+
lastUpdated: '2026-06-09',
|
|
2619
|
+
openWeights: false,
|
|
2620
|
+
cost: {
|
|
2621
|
+
input: 10,
|
|
2622
|
+
output: 50,
|
|
2623
|
+
cacheRead: 1,
|
|
2624
|
+
cacheWrite: 12.5,
|
|
2625
|
+
},
|
|
2626
|
+
limit: {
|
|
2627
|
+
context: 1000000,
|
|
2628
|
+
output: 128000,
|
|
2629
|
+
},
|
|
2630
|
+
},
|
|
2578
2631
|
{
|
|
2579
2632
|
id: 'anthropic/claude-haiku-4.5',
|
|
2580
2633
|
ownedBy: 'anthropic',
|
|
@@ -3178,7 +3231,7 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
3178
3231
|
cacheRead: 0.135,
|
|
3179
3232
|
},
|
|
3180
3233
|
limit: {
|
|
3181
|
-
context:
|
|
3234
|
+
context: 32768,
|
|
3182
3235
|
output: 16384,
|
|
3183
3236
|
},
|
|
3184
3237
|
},
|
|
@@ -3415,8 +3468,8 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
3415
3468
|
attachment: true,
|
|
3416
3469
|
temperature: true,
|
|
3417
3470
|
knowledge: '2025-01',
|
|
3418
|
-
releaseDate: '2025-
|
|
3419
|
-
lastUpdated: '2025-06-
|
|
3471
|
+
releaseDate: '2025-06-17',
|
|
3472
|
+
lastUpdated: '2025-06-17',
|
|
3420
3473
|
openWeights: false,
|
|
3421
3474
|
cost: {
|
|
3422
3475
|
input: 0.3,
|
|
@@ -3496,8 +3549,8 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
3496
3549
|
attachment: true,
|
|
3497
3550
|
temperature: true,
|
|
3498
3551
|
knowledge: '2025-01',
|
|
3499
|
-
releaseDate: '2025-
|
|
3500
|
-
lastUpdated: '2025-06-
|
|
3552
|
+
releaseDate: '2025-06-17',
|
|
3553
|
+
lastUpdated: '2025-06-17',
|
|
3501
3554
|
openWeights: false,
|
|
3502
3555
|
cost: {
|
|
3503
3556
|
input: 1.25,
|
|
@@ -3841,12 +3894,12 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
3841
3894
|
openWeights: true,
|
|
3842
3895
|
cost: {
|
|
3843
3896
|
input: 0.12,
|
|
3844
|
-
output: 0.
|
|
3897
|
+
output: 0.35,
|
|
3845
3898
|
cacheRead: 0.09,
|
|
3846
3899
|
},
|
|
3847
3900
|
limit: {
|
|
3848
|
-
context:
|
|
3849
|
-
output:
|
|
3901
|
+
context: 262144,
|
|
3902
|
+
output: 262144,
|
|
3850
3903
|
},
|
|
3851
3904
|
},
|
|
3852
3905
|
{
|
|
@@ -4253,7 +4306,8 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
4253
4306
|
openWeights: true,
|
|
4254
4307
|
cost: {
|
|
4255
4308
|
input: 0.15,
|
|
4256
|
-
output:
|
|
4309
|
+
output: 0.9,
|
|
4310
|
+
cacheRead: 0.05,
|
|
4257
4311
|
},
|
|
4258
4312
|
limit: {
|
|
4259
4313
|
context: 196608,
|
|
@@ -4276,12 +4330,13 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
4276
4330
|
lastUpdated: '2026-03-18',
|
|
4277
4331
|
openWeights: true,
|
|
4278
4332
|
cost: {
|
|
4279
|
-
input: 0.
|
|
4280
|
-
output: 1
|
|
4333
|
+
input: 0.25,
|
|
4334
|
+
output: 1,
|
|
4335
|
+
cacheRead: 0.05,
|
|
4281
4336
|
},
|
|
4282
4337
|
limit: {
|
|
4283
4338
|
context: 196608,
|
|
4284
|
-
output:
|
|
4339
|
+
output: 131072,
|
|
4285
4340
|
},
|
|
4286
4341
|
},
|
|
4287
4342
|
{
|
|
@@ -4844,34 +4899,9 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
4844
4899
|
lastUpdated: '2026-04-21',
|
|
4845
4900
|
openWeights: true,
|
|
4846
4901
|
cost: {
|
|
4847
|
-
input: 0.
|
|
4848
|
-
output: 3.
|
|
4849
|
-
cacheRead: 0.
|
|
4850
|
-
},
|
|
4851
|
-
limit: {
|
|
4852
|
-
context: 262142,
|
|
4853
|
-
output: 262142,
|
|
4854
|
-
},
|
|
4855
|
-
},
|
|
4856
|
-
{
|
|
4857
|
-
id: 'moonshotai/kimi-k2.6:free',
|
|
4858
|
-
ownedBy: 'moonshot',
|
|
4859
|
-
label: 'Kimi K2.6 (free)',
|
|
4860
|
-
modalities: {
|
|
4861
|
-
input: ['text', 'image'],
|
|
4862
|
-
output: ['text'],
|
|
4863
|
-
},
|
|
4864
|
-
toolCall: true,
|
|
4865
|
-
reasoningText: true,
|
|
4866
|
-
attachment: true,
|
|
4867
|
-
temperature: false,
|
|
4868
|
-
knowledge: '2025-01',
|
|
4869
|
-
releaseDate: '2026-04-21',
|
|
4870
|
-
lastUpdated: '2026-04-21',
|
|
4871
|
-
openWeights: true,
|
|
4872
|
-
cost: {
|
|
4873
|
-
input: 0,
|
|
4874
|
-
output: 0,
|
|
4902
|
+
input: 0.67,
|
|
4903
|
+
output: 3.39,
|
|
4904
|
+
cacheRead: 0.14,
|
|
4875
4905
|
},
|
|
4876
4906
|
limit: {
|
|
4877
4907
|
context: 262144,
|
|
@@ -5109,29 +5139,6 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
5109
5139
|
output: 128000,
|
|
5110
5140
|
},
|
|
5111
5141
|
},
|
|
5112
|
-
{
|
|
5113
|
-
id: 'nvidia/nemotron-nano-9b-v2',
|
|
5114
|
-
label: 'Nemotron Nano 9B v2',
|
|
5115
|
-
modalities: {
|
|
5116
|
-
input: ['text'],
|
|
5117
|
-
output: ['text'],
|
|
5118
|
-
},
|
|
5119
|
-
toolCall: true,
|
|
5120
|
-
reasoningText: true,
|
|
5121
|
-
attachment: false,
|
|
5122
|
-
temperature: true,
|
|
5123
|
-
releaseDate: '2025-08-18',
|
|
5124
|
-
lastUpdated: '2025-08-18',
|
|
5125
|
-
openWeights: true,
|
|
5126
|
-
cost: {
|
|
5127
|
-
input: 0.04,
|
|
5128
|
-
output: 0.16,
|
|
5129
|
-
},
|
|
5130
|
-
limit: {
|
|
5131
|
-
context: 131072,
|
|
5132
|
-
output: 16384,
|
|
5133
|
-
},
|
|
5134
|
-
},
|
|
5135
5142
|
{
|
|
5136
5143
|
id: 'nvidia/nemotron-nano-9b-v2:free',
|
|
5137
5144
|
label: 'Nemotron Nano 9B V2 (free)',
|
|
@@ -7649,12 +7656,13 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
7649
7656
|
lastUpdated: '2026-04-17',
|
|
7650
7657
|
openWeights: true,
|
|
7651
7658
|
cost: {
|
|
7652
|
-
input: 0.
|
|
7659
|
+
input: 0.15,
|
|
7653
7660
|
output: 1,
|
|
7661
|
+
cacheRead: 0.05,
|
|
7654
7662
|
},
|
|
7655
7663
|
limit: {
|
|
7656
|
-
context:
|
|
7657
|
-
output:
|
|
7664
|
+
context: 262144,
|
|
7665
|
+
output: 262144,
|
|
7658
7666
|
},
|
|
7659
7667
|
},
|
|
7660
7668
|
{
|
|
@@ -7772,10 +7780,10 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
7772
7780
|
lastUpdated: '2026-06-02',
|
|
7773
7781
|
openWeights: false,
|
|
7774
7782
|
cost: {
|
|
7775
|
-
input: 0.
|
|
7776
|
-
output: 1.
|
|
7777
|
-
cacheRead: 0.
|
|
7778
|
-
cacheWrite: 0.
|
|
7783
|
+
input: 0.32,
|
|
7784
|
+
output: 1.28,
|
|
7785
|
+
cacheRead: 0.064,
|
|
7786
|
+
cacheWrite: 0.4,
|
|
7779
7787
|
},
|
|
7780
7788
|
limit: {
|
|
7781
7789
|
context: 1000000,
|
|
@@ -8149,31 +8157,6 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8149
8157
|
output: 131072,
|
|
8150
8158
|
},
|
|
8151
8159
|
},
|
|
8152
|
-
{
|
|
8153
|
-
id: 'z-ai/glm-4-32b',
|
|
8154
|
-
ownedBy: 'zai',
|
|
8155
|
-
label: 'GLM 4 32B ',
|
|
8156
|
-
modalities: {
|
|
8157
|
-
input: ['text'],
|
|
8158
|
-
output: ['text'],
|
|
8159
|
-
},
|
|
8160
|
-
toolCall: true,
|
|
8161
|
-
reasoningText: false,
|
|
8162
|
-
attachment: false,
|
|
8163
|
-
temperature: true,
|
|
8164
|
-
knowledge: '2024-06-30',
|
|
8165
|
-
releaseDate: '2025-07-24',
|
|
8166
|
-
lastUpdated: '2025-07-24',
|
|
8167
|
-
openWeights: false,
|
|
8168
|
-
cost: {
|
|
8169
|
-
input: 0.1,
|
|
8170
|
-
output: 0.1,
|
|
8171
|
-
},
|
|
8172
|
-
limit: {
|
|
8173
|
-
context: 128000,
|
|
8174
|
-
output: 128000,
|
|
8175
|
-
},
|
|
8176
|
-
},
|
|
8177
8160
|
{
|
|
8178
8161
|
id: 'z-ai/glm-4.5',
|
|
8179
8162
|
ownedBy: 'zai',
|
|
@@ -8226,31 +8209,6 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8226
8209
|
output: 131070,
|
|
8227
8210
|
},
|
|
8228
8211
|
},
|
|
8229
|
-
{
|
|
8230
|
-
id: 'z-ai/glm-4.5-air:free',
|
|
8231
|
-
ownedBy: 'zai',
|
|
8232
|
-
label: 'GLM 4.5 Air (free)',
|
|
8233
|
-
modalities: {
|
|
8234
|
-
input: ['text'],
|
|
8235
|
-
output: ['text'],
|
|
8236
|
-
},
|
|
8237
|
-
toolCall: true,
|
|
8238
|
-
reasoningText: true,
|
|
8239
|
-
attachment: false,
|
|
8240
|
-
temperature: true,
|
|
8241
|
-
knowledge: '2025-04',
|
|
8242
|
-
releaseDate: '2025-07-28',
|
|
8243
|
-
lastUpdated: '2025-07-28',
|
|
8244
|
-
openWeights: true,
|
|
8245
|
-
cost: {
|
|
8246
|
-
input: 0,
|
|
8247
|
-
output: 0,
|
|
8248
|
-
},
|
|
8249
|
-
limit: {
|
|
8250
|
-
context: 131072,
|
|
8251
|
-
output: 96000,
|
|
8252
|
-
},
|
|
8253
|
-
},
|
|
8254
8212
|
{
|
|
8255
8213
|
id: 'z-ai/glm-4.5v',
|
|
8256
8214
|
ownedBy: 'zai',
|
|
@@ -8322,11 +8280,11 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8322
8280
|
cost: {
|
|
8323
8281
|
input: 0.3,
|
|
8324
8282
|
output: 0.9,
|
|
8325
|
-
cacheRead: 0.
|
|
8283
|
+
cacheRead: 0.055,
|
|
8326
8284
|
},
|
|
8327
8285
|
limit: {
|
|
8328
8286
|
context: 131072,
|
|
8329
|
-
output:
|
|
8287
|
+
output: 32768,
|
|
8330
8288
|
},
|
|
8331
8289
|
},
|
|
8332
8290
|
{
|
|
@@ -8427,7 +8385,7 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8427
8385
|
cacheRead: 0.24,
|
|
8428
8386
|
},
|
|
8429
8387
|
limit: {
|
|
8430
|
-
context:
|
|
8388
|
+
context: 262144,
|
|
8431
8389
|
output: 131072,
|
|
8432
8390
|
},
|
|
8433
8391
|
},
|
|
@@ -8456,31 +8414,6 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8456
8414
|
output: 131072,
|
|
8457
8415
|
},
|
|
8458
8416
|
},
|
|
8459
|
-
{
|
|
8460
|
-
id: 'z-ai/glm-5v-turbo',
|
|
8461
|
-
ownedBy: 'zai',
|
|
8462
|
-
label: 'GLM-5V-Turbo',
|
|
8463
|
-
modalities: {
|
|
8464
|
-
input: ['image', 'text', 'video'],
|
|
8465
|
-
output: ['text'],
|
|
8466
|
-
},
|
|
8467
|
-
toolCall: true,
|
|
8468
|
-
reasoningText: true,
|
|
8469
|
-
attachment: true,
|
|
8470
|
-
temperature: true,
|
|
8471
|
-
releaseDate: '2026-04-01',
|
|
8472
|
-
lastUpdated: '2026-04-01',
|
|
8473
|
-
openWeights: false,
|
|
8474
|
-
cost: {
|
|
8475
|
-
input: 1.2,
|
|
8476
|
-
output: 4,
|
|
8477
|
-
cacheRead: 0.24,
|
|
8478
|
-
},
|
|
8479
|
-
limit: {
|
|
8480
|
-
context: 202752,
|
|
8481
|
-
output: 131072,
|
|
8482
|
-
},
|
|
8483
|
-
},
|
|
8484
8417
|
],
|
|
8485
8418
|
label: 'OpenRouter',
|
|
8486
8419
|
env: ['OPENROUTER_API_KEY'],
|
|
@@ -8547,6 +8480,36 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8547
8480
|
npm: '@ai-sdk/anthropic',
|
|
8548
8481
|
},
|
|
8549
8482
|
},
|
|
8483
|
+
{
|
|
8484
|
+
id: 'claude-fable-5',
|
|
8485
|
+
ownedBy: 'anthropic',
|
|
8486
|
+
label: 'Claude Fable 5',
|
|
8487
|
+
modalities: {
|
|
8488
|
+
input: ['text', 'image', 'pdf'],
|
|
8489
|
+
output: ['text'],
|
|
8490
|
+
},
|
|
8491
|
+
toolCall: true,
|
|
8492
|
+
reasoningText: true,
|
|
8493
|
+
attachment: true,
|
|
8494
|
+
temperature: false,
|
|
8495
|
+
knowledge: '2026-01-31',
|
|
8496
|
+
releaseDate: '2026-06-09',
|
|
8497
|
+
lastUpdated: '2026-06-09',
|
|
8498
|
+
openWeights: false,
|
|
8499
|
+
cost: {
|
|
8500
|
+
input: 10,
|
|
8501
|
+
output: 50,
|
|
8502
|
+
cacheRead: 1,
|
|
8503
|
+
cacheWrite: 12.5,
|
|
8504
|
+
},
|
|
8505
|
+
limit: {
|
|
8506
|
+
context: 1000000,
|
|
8507
|
+
output: 128000,
|
|
8508
|
+
},
|
|
8509
|
+
provider: {
|
|
8510
|
+
npm: '@ai-sdk/anthropic',
|
|
8511
|
+
},
|
|
8512
|
+
},
|
|
8550
8513
|
{
|
|
8551
8514
|
id: 'claude-haiku-4-5',
|
|
8552
8515
|
ownedBy: 'anthropic',
|
|
@@ -8834,7 +8797,7 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8834
8797
|
cost: {
|
|
8835
8798
|
input: 0.14,
|
|
8836
8799
|
output: 0.28,
|
|
8837
|
-
cacheRead: 0.
|
|
8800
|
+
cacheRead: 0.028,
|
|
8838
8801
|
},
|
|
8839
8802
|
limit: {
|
|
8840
8803
|
context: 1000000,
|
|
@@ -8866,6 +8829,31 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
8866
8829
|
output: 128000,
|
|
8867
8830
|
},
|
|
8868
8831
|
},
|
|
8832
|
+
{
|
|
8833
|
+
id: 'deepseek-v4-pro',
|
|
8834
|
+
label: 'DeepSeek V4 Pro',
|
|
8835
|
+
modalities: {
|
|
8836
|
+
input: ['text'],
|
|
8837
|
+
output: ['text'],
|
|
8838
|
+
},
|
|
8839
|
+
toolCall: true,
|
|
8840
|
+
reasoningText: true,
|
|
8841
|
+
attachment: false,
|
|
8842
|
+
temperature: true,
|
|
8843
|
+
knowledge: '2025-05',
|
|
8844
|
+
releaseDate: '2026-04-24',
|
|
8845
|
+
lastUpdated: '2026-04-24',
|
|
8846
|
+
openWeights: true,
|
|
8847
|
+
cost: {
|
|
8848
|
+
input: 1.74,
|
|
8849
|
+
output: 3.84,
|
|
8850
|
+
cacheRead: 0.145,
|
|
8851
|
+
},
|
|
8852
|
+
limit: {
|
|
8853
|
+
context: 1000000,
|
|
8854
|
+
output: 384000,
|
|
8855
|
+
},
|
|
8856
|
+
},
|
|
8869
8857
|
{
|
|
8870
8858
|
id: 'gemini-3-flash',
|
|
8871
8859
|
ownedBy: 'google',
|
|
@@ -11154,12 +11142,38 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
11154
11142
|
output: 262144,
|
|
11155
11143
|
},
|
|
11156
11144
|
},
|
|
11145
|
+
{
|
|
11146
|
+
id: 'kimi-k2.7-code',
|
|
11147
|
+
ownedBy: 'moonshot',
|
|
11148
|
+
label: 'Kimi K2.7 Code',
|
|
11149
|
+
modalities: {
|
|
11150
|
+
input: ['text', 'image', 'video'],
|
|
11151
|
+
output: ['text'],
|
|
11152
|
+
},
|
|
11153
|
+
toolCall: true,
|
|
11154
|
+
reasoningText: true,
|
|
11155
|
+
attachment: true,
|
|
11156
|
+
temperature: false,
|
|
11157
|
+
knowledge: '2025-01',
|
|
11158
|
+
releaseDate: '2026-06-12',
|
|
11159
|
+
lastUpdated: '2026-06-12',
|
|
11160
|
+
openWeights: true,
|
|
11161
|
+
cost: {
|
|
11162
|
+
input: 0.95,
|
|
11163
|
+
output: 4,
|
|
11164
|
+
cacheRead: 0.19,
|
|
11165
|
+
},
|
|
11166
|
+
limit: {
|
|
11167
|
+
context: 262144,
|
|
11168
|
+
output: 262144,
|
|
11169
|
+
},
|
|
11170
|
+
},
|
|
11157
11171
|
],
|
|
11158
|
-
label: '
|
|
11159
|
-
env: ['MOONSHOT_API_KEY'],
|
|
11172
|
+
label: 'Kimi',
|
|
11173
|
+
env: ['KIMI_API_KEY', 'MOONSHOT_API_KEY'],
|
|
11160
11174
|
npm: '@ai-sdk/openai-compatible',
|
|
11161
11175
|
api: 'https://api.moonshot.ai/v1',
|
|
11162
|
-
doc: 'https://platform.
|
|
11176
|
+
doc: 'https://platform.kimi.ai/docs/api/overview.md',
|
|
11163
11177
|
},
|
|
11164
11178
|
minimax: {
|
|
11165
11179
|
id: 'minimax',
|
|
@@ -11351,6 +11365,33 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
11351
11365
|
copilot: {
|
|
11352
11366
|
id: 'copilot',
|
|
11353
11367
|
models: [
|
|
11368
|
+
{
|
|
11369
|
+
id: 'claude-fable-5',
|
|
11370
|
+
ownedBy: 'anthropic',
|
|
11371
|
+
label: 'Claude Fable 5',
|
|
11372
|
+
modalities: {
|
|
11373
|
+
input: ['text', 'image', 'pdf'],
|
|
11374
|
+
output: ['text'],
|
|
11375
|
+
},
|
|
11376
|
+
toolCall: true,
|
|
11377
|
+
reasoningText: true,
|
|
11378
|
+
attachment: true,
|
|
11379
|
+
temperature: false,
|
|
11380
|
+
knowledge: '2026-01-31',
|
|
11381
|
+
releaseDate: '2026-06-09',
|
|
11382
|
+
lastUpdated: '2026-06-09',
|
|
11383
|
+
openWeights: false,
|
|
11384
|
+
cost: {
|
|
11385
|
+
input: 10,
|
|
11386
|
+
output: 50,
|
|
11387
|
+
cacheRead: 1,
|
|
11388
|
+
cacheWrite: 12.5,
|
|
11389
|
+
},
|
|
11390
|
+
limit: {
|
|
11391
|
+
context: 1000000,
|
|
11392
|
+
output: 128000,
|
|
11393
|
+
},
|
|
11394
|
+
},
|
|
11354
11395
|
{
|
|
11355
11396
|
id: 'claude-haiku-4.5',
|
|
11356
11397
|
ownedBy: 'anthropic',
|
|
@@ -11579,8 +11620,8 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
11579
11620
|
attachment: true,
|
|
11580
11621
|
temperature: true,
|
|
11581
11622
|
knowledge: '2025-01',
|
|
11582
|
-
releaseDate: '2025-
|
|
11583
|
-
lastUpdated: '2025-06-
|
|
11623
|
+
releaseDate: '2025-06-17',
|
|
11624
|
+
lastUpdated: '2025-06-17',
|
|
11584
11625
|
openWeights: false,
|
|
11585
11626
|
cost: {
|
|
11586
11627
|
input: 1.25,
|
package/src/providers/src/env.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BuiltInProviderId, ProviderId } from '../../types/src/index.ts';
|
|
2
|
+
import { readKimiApiKeyFromEnv } from './moonshot-client.ts';
|
|
2
3
|
|
|
3
4
|
const ENV_VARS: Record<BuiltInProviderId, string> = {
|
|
4
5
|
openai: 'OPENAI_API_KEY',
|
|
@@ -12,15 +13,22 @@ const ENV_VARS: Record<BuiltInProviderId, string> = {
|
|
|
12
13
|
xai: 'XAI_API_KEY',
|
|
13
14
|
zai: 'ZAI_API_KEY',
|
|
14
15
|
'zai-coding': 'ZAI_CODING_API_KEY',
|
|
15
|
-
moonshot: '
|
|
16
|
+
moonshot: 'KIMI_API_KEY',
|
|
16
17
|
minimax: 'MINIMAX_API_KEY',
|
|
17
18
|
};
|
|
18
19
|
|
|
20
|
+
const KIMI_PROVIDER_IDS = new Set<ProviderId>(['kimi', 'moonshot']);
|
|
21
|
+
|
|
19
22
|
export function providerEnvVar(provider: ProviderId): string | undefined {
|
|
23
|
+
if (provider === 'kimi') return 'KIMI_API_KEY';
|
|
20
24
|
return ENV_VARS[provider as BuiltInProviderId];
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export function readEnvKey(provider: ProviderId): string | undefined {
|
|
28
|
+
if (KIMI_PROVIDER_IDS.has(provider)) {
|
|
29
|
+
const value = readKimiApiKeyFromEnv();
|
|
30
|
+
return value.length ? value : undefined;
|
|
31
|
+
}
|
|
24
32
|
if (!(provider in ENV_VARS) && provider !== 'copilot') {
|
|
25
33
|
return undefined;
|
|
26
34
|
}
|
|
@@ -39,8 +47,13 @@ export function readEnvKey(provider: ProviderId): string | undefined {
|
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
export function setEnvKey(provider: ProviderId, value: string | undefined) {
|
|
50
|
+
if (!value) return;
|
|
51
|
+
if (KIMI_PROVIDER_IDS.has(provider)) {
|
|
52
|
+
process.env.KIMI_API_KEY = value;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
42
55
|
const key = providerEnvVar(provider);
|
|
43
|
-
if (key
|
|
56
|
+
if (key) {
|
|
44
57
|
process.env[key] = value;
|
|
45
58
|
}
|
|
46
59
|
}
|
|
@@ -123,8 +123,15 @@ export {
|
|
|
123
123
|
export type { OpenRouterProviderConfig } from './openrouter-client.ts';
|
|
124
124
|
export { createOpencodeModel } from './opencode-client.ts';
|
|
125
125
|
export type { OpencodeProviderConfig } from './opencode-client.ts';
|
|
126
|
-
export {
|
|
127
|
-
|
|
126
|
+
export {
|
|
127
|
+
createKimiModel,
|
|
128
|
+
createMoonshotModel,
|
|
129
|
+
readKimiApiKeyFromEnv,
|
|
130
|
+
} from './moonshot-client.ts';
|
|
131
|
+
export type {
|
|
132
|
+
KimiProviderConfig,
|
|
133
|
+
MoonshotProviderConfig,
|
|
134
|
+
} from './moonshot-client.ts';
|
|
128
135
|
export { createMinimaxModel } from './minimax-client.ts';
|
|
129
136
|
export type { MinimaxProviderConfig } from './minimax-client.ts';
|
|
130
137
|
export { createCopilotFetch, createCopilotModel } from './copilot-client.ts';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ModelInfo } from '../../types/src/index.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Merge embedded/manual catalog models with cached (remote/local) models by id.
|
|
5
|
+
*
|
|
6
|
+
* Cached entries override fields for overlapping ids (so remote updates like
|
|
7
|
+
* pricing/limits still apply), while embedded/manual-only models (for example
|
|
8
|
+
* `kimi-k2.7-code` or the manual xai grok-cli models) are always retained even
|
|
9
|
+
* when a stale cache does not include them.
|
|
10
|
+
*/
|
|
11
|
+
export function mergeModelLists(
|
|
12
|
+
baseModels: ModelInfo[] | undefined,
|
|
13
|
+
cachedModels: ModelInfo[] | undefined,
|
|
14
|
+
): ModelInfo[] {
|
|
15
|
+
const base = baseModels ?? [];
|
|
16
|
+
const cached = cachedModels ?? [];
|
|
17
|
+
if (!cached.length) return base;
|
|
18
|
+
if (!base.length) return cached;
|
|
19
|
+
const cachedById = new Map(cached.map((model) => [model.id, model]));
|
|
20
|
+
const merged = base.map((model) => {
|
|
21
|
+
const override = cachedById.get(model.id);
|
|
22
|
+
return override ? { ...model, ...override } : model;
|
|
23
|
+
});
|
|
24
|
+
const baseIds = new Set(base.map((model) => model.id));
|
|
25
|
+
const extras = cached.filter((model) => !baseIds.has(model.id));
|
|
26
|
+
return extras.length ? [...merged, ...extras] : merged;
|
|
27
|
+
}
|
|
@@ -1,25 +1,48 @@
|
|
|
1
1
|
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
2
|
+
import type { OAuth } from '../../types/src/index.ts';
|
|
2
3
|
import { catalog } from './catalog-merged.ts';
|
|
3
4
|
|
|
4
|
-
export type
|
|
5
|
+
export type KimiProviderConfig = {
|
|
5
6
|
apiKey?: string;
|
|
6
7
|
baseURL?: string;
|
|
8
|
+
oauth?: OAuth;
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
) {
|
|
11
|
+
/** @deprecated Use `KimiProviderConfig` */
|
|
12
|
+
export type MoonshotProviderConfig = KimiProviderConfig;
|
|
13
|
+
|
|
14
|
+
export function readKimiApiKeyFromEnv(): string {
|
|
15
|
+
return process.env.KIMI_API_KEY || process.env.MOONSHOT_API_KEY || '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createKimiModel(model: string, config?: KimiProviderConfig) {
|
|
13
19
|
const entry = catalog.moonshot;
|
|
14
|
-
const
|
|
15
|
-
const
|
|
20
|
+
const oauthAccess = config?.oauth?.access;
|
|
21
|
+
const defaultApiBaseURL = entry?.api ?? 'https://api.moonshot.ai/v1';
|
|
22
|
+
const configuredBaseURL = config?.baseURL;
|
|
23
|
+
const kimiCodeBaseURL =
|
|
24
|
+
process.env.KIMI_CODE_BASE_URL ?? 'https://api.kimi.com/coding/v1';
|
|
25
|
+
const baseURL =
|
|
26
|
+
oauthAccess &&
|
|
27
|
+
(!configuredBaseURL || configuredBaseURL === defaultApiBaseURL)
|
|
28
|
+
? kimiCodeBaseURL
|
|
29
|
+
: (configuredBaseURL ?? defaultApiBaseURL);
|
|
30
|
+
const apiKey = oauthAccess || config?.apiKey || readKimiApiKeyFromEnv();
|
|
16
31
|
const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
|
|
17
32
|
|
|
18
33
|
const instance = createOpenAICompatible({
|
|
19
|
-
name:
|
|
34
|
+
name: 'Kimi',
|
|
20
35
|
baseURL,
|
|
21
36
|
headers,
|
|
22
37
|
});
|
|
23
38
|
|
|
24
39
|
return instance(model);
|
|
25
40
|
}
|
|
41
|
+
|
|
42
|
+
/** @deprecated Use `createKimiModel` */
|
|
43
|
+
export function createMoonshotModel(
|
|
44
|
+
model: string,
|
|
45
|
+
config?: MoonshotProviderConfig,
|
|
46
|
+
) {
|
|
47
|
+
return createKimiModel(model, config);
|
|
48
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { catalog } from './catalog-merged.ts';
|
|
2
|
-
import { providerEnvVar } from './env.ts';
|
|
2
|
+
import { providerEnvVar, readEnvKey } from './env.ts';
|
|
3
3
|
import { getCachedProviderCatalogEntry } from './model-catalog-cache.ts';
|
|
4
|
+
import { mergeModelLists } from './model-merge.ts';
|
|
4
5
|
import { getUnderlyingProviderKey, providerIds } from './utils.ts';
|
|
5
6
|
import type {
|
|
6
7
|
BuiltInProviderId,
|
|
@@ -94,7 +95,9 @@ function resolveCustomFamily(
|
|
|
94
95
|
return settings.family ?? 'default';
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
export
|
|
98
|
+
export const KIMI_PROVIDER_ALIAS = 'kimi' as const;
|
|
99
|
+
|
|
100
|
+
function isCatalogBuiltInProviderId(
|
|
98
101
|
value: unknown,
|
|
99
102
|
): value is BuiltInProviderId {
|
|
100
103
|
return (
|
|
@@ -103,6 +106,20 @@ export function isBuiltInProviderId(
|
|
|
103
106
|
);
|
|
104
107
|
}
|
|
105
108
|
|
|
109
|
+
export function resolveBuiltInProviderCatalogId(
|
|
110
|
+
provider: ProviderId,
|
|
111
|
+
): BuiltInProviderId | undefined {
|
|
112
|
+
if (provider === KIMI_PROVIDER_ALIAS) return 'moonshot';
|
|
113
|
+
if (isCatalogBuiltInProviderId(provider)) return provider;
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function isBuiltInProviderId(
|
|
118
|
+
value: unknown,
|
|
119
|
+
): value is BuiltInProviderId {
|
|
120
|
+
return isCatalogBuiltInProviderId(value) || value === KIMI_PROVIDER_ALIAS;
|
|
121
|
+
}
|
|
122
|
+
|
|
106
123
|
export function getProviderSettings(
|
|
107
124
|
cfg: OttoConfig,
|
|
108
125
|
provider: ProviderId,
|
|
@@ -115,23 +132,37 @@ export function getProviderDefinition(
|
|
|
115
132
|
provider: ProviderId,
|
|
116
133
|
): ResolvedProviderDefinition | undefined {
|
|
117
134
|
const settings = getProviderSettings(cfg, provider);
|
|
118
|
-
|
|
119
|
-
|
|
135
|
+
const catalogProvider = resolveBuiltInProviderCatalogId(provider);
|
|
136
|
+
if (catalogProvider) {
|
|
137
|
+
const entry = catalog[catalogProvider];
|
|
120
138
|
if (!entry) return undefined;
|
|
121
|
-
const cachedEntry = getCachedProviderCatalogEntry(
|
|
122
|
-
const models = cachedEntry?.models
|
|
139
|
+
const cachedEntry = getCachedProviderCatalogEntry(catalogProvider);
|
|
140
|
+
const models = mergeModelLists(entry.models, cachedEntry?.models);
|
|
141
|
+
const moonshotSettings =
|
|
142
|
+
provider === KIMI_PROVIDER_ALIAS
|
|
143
|
+
? (getProviderSettings(cfg, 'moonshot') ?? settings)
|
|
144
|
+
: settings;
|
|
145
|
+
const resolvedSettings =
|
|
146
|
+
provider === KIMI_PROVIDER_ALIAS
|
|
147
|
+
? (settings ?? moonshotSettings)
|
|
148
|
+
: settings;
|
|
123
149
|
return {
|
|
124
150
|
id: provider,
|
|
125
|
-
label:
|
|
151
|
+
label:
|
|
152
|
+
resolvedSettings?.label ??
|
|
153
|
+
(provider === KIMI_PROVIDER_ALIAS
|
|
154
|
+
? 'Kimi'
|
|
155
|
+
: (cachedEntry?.label ?? entry.label ?? provider)),
|
|
126
156
|
source: 'built-in',
|
|
127
|
-
compatibility: BUILTIN_COMPATIBILITY[
|
|
128
|
-
family: BUILTIN_FAMILY[
|
|
129
|
-
baseURL: normalizeOptionalText(
|
|
130
|
-
apiKey: normalizeOptionalText(
|
|
157
|
+
compatibility: BUILTIN_COMPATIBILITY[catalogProvider],
|
|
158
|
+
family: BUILTIN_FAMILY[catalogProvider],
|
|
159
|
+
baseURL: normalizeOptionalText(resolvedSettings?.baseURL) ?? entry.api,
|
|
160
|
+
apiKey: normalizeOptionalText(resolvedSettings?.apiKey),
|
|
131
161
|
apiKeyEnv:
|
|
132
|
-
normalizeOptionalText(
|
|
162
|
+
normalizeOptionalText(resolvedSettings?.apiKeyEnv) ??
|
|
163
|
+
providerEnvVar(provider),
|
|
133
164
|
models,
|
|
134
|
-
allowAnyModel:
|
|
165
|
+
allowAnyModel: catalogProvider === 'ollama-cloud',
|
|
135
166
|
};
|
|
136
167
|
}
|
|
137
168
|
|
|
@@ -171,6 +202,7 @@ export function getConfiguredProviderIds(
|
|
|
171
202
|
const includeDisabled = options?.includeDisabled === true;
|
|
172
203
|
const ids = new Set<ProviderId>([
|
|
173
204
|
...providerIds,
|
|
205
|
+
KIMI_PROVIDER_ALIAS,
|
|
174
206
|
...Object.keys(cfg.providers),
|
|
175
207
|
cfg.defaults.provider,
|
|
176
208
|
]);
|
|
@@ -224,8 +256,11 @@ export function getConfiguredProviderFamily(
|
|
|
224
256
|
const definition = getProviderDefinition(cfg, provider);
|
|
225
257
|
if (!definition) return null;
|
|
226
258
|
if (definition.source === 'custom') return definition.family;
|
|
227
|
-
|
|
228
|
-
|
|
259
|
+
const catalogProvider = resolveBuiltInProviderCatalogId(provider);
|
|
260
|
+
if (catalogProvider) {
|
|
261
|
+
return (
|
|
262
|
+
getUnderlyingProviderKey(catalogProvider, model) ?? definition.family
|
|
263
|
+
);
|
|
229
264
|
}
|
|
230
265
|
return definition.family;
|
|
231
266
|
}
|
|
@@ -245,6 +280,10 @@ export function getConfiguredProviderApiKey(
|
|
|
245
280
|
const definition = getProviderDefinition(cfg, provider);
|
|
246
281
|
if (!definition) return undefined;
|
|
247
282
|
if (definition.apiKey?.length) return definition.apiKey;
|
|
283
|
+
if (provider === KIMI_PROVIDER_ALIAS || provider === 'moonshot') {
|
|
284
|
+
const envValue = readEnvKey(provider);
|
|
285
|
+
if (envValue?.length) return envValue;
|
|
286
|
+
}
|
|
248
287
|
if (definition.apiKeyEnv?.length) {
|
|
249
288
|
const value = process.env[definition.apiKeyEnv];
|
|
250
289
|
if (value?.length) return value;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { catalog } from './catalog-merged.ts';
|
|
2
2
|
import { getCachedProviderCatalogEntry } from './model-catalog-cache.ts';
|
|
3
|
+
import { mergeModelLists } from './model-merge.ts';
|
|
3
4
|
import type {
|
|
4
5
|
BuiltInProviderId,
|
|
5
6
|
ProviderId,
|
|
@@ -7,7 +8,7 @@ import type {
|
|
|
7
8
|
ModelOwner,
|
|
8
9
|
} from '../../types/src/index.ts';
|
|
9
10
|
import { filterModelsForAuthType } from './oauth-models.ts';
|
|
10
|
-
import {
|
|
11
|
+
import { resolveBuiltInProviderCatalogId } from './registry.ts';
|
|
11
12
|
|
|
12
13
|
export const providerIds = Object.keys(catalog) as BuiltInProviderId[];
|
|
13
14
|
|
|
@@ -108,7 +109,8 @@ export function getModelNpmBinding(
|
|
|
108
109
|
provider: ProviderId,
|
|
109
110
|
model: string,
|
|
110
111
|
): string | undefined {
|
|
111
|
-
const
|
|
112
|
+
const catalogProvider = resolveBuiltInProviderCatalogId(provider);
|
|
113
|
+
const entry = catalogProvider ? catalog[catalogProvider] : undefined;
|
|
112
114
|
const modelInfo = getProviderModels(provider).find((m) => m.id === model);
|
|
113
115
|
if (modelInfo?.provider?.npm) return modelInfo.provider.npm;
|
|
114
116
|
if (entry?.npm) return entry.npm;
|
|
@@ -240,17 +242,21 @@ export function getModelInfo(
|
|
|
240
242
|
provider: ProviderId,
|
|
241
243
|
model: string,
|
|
242
244
|
): ModelInfo | undefined {
|
|
243
|
-
const
|
|
245
|
+
const catalogProvider = resolveBuiltInProviderCatalogId(provider);
|
|
246
|
+
const entry = catalogProvider ? catalog[catalogProvider] : undefined;
|
|
244
247
|
if (!entry) return undefined;
|
|
245
248
|
return getProviderModels(provider).find((m) => m.id === model);
|
|
246
249
|
}
|
|
247
250
|
|
|
248
251
|
function getProviderModels(provider: ProviderId): ModelInfo[] {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
const catalogProvider = resolveBuiltInProviderCatalogId(provider);
|
|
253
|
+
const catalogModels = catalogProvider
|
|
254
|
+
? catalog[catalogProvider]?.models
|
|
255
|
+
: undefined;
|
|
256
|
+
const cachedModels = getCachedProviderCatalogEntry(
|
|
257
|
+
catalogProvider ?? provider,
|
|
258
|
+
)?.models;
|
|
259
|
+
return mergeModelLists(catalogModels, cachedModels);
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
export function modelSupportsReasoning(
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { catalog } from './catalog-merged.ts';
|
|
2
2
|
import { getCachedProviderCatalogEntry } from './model-catalog-cache.ts';
|
|
3
|
+
import { mergeModelLists } from './model-merge.ts';
|
|
3
4
|
import type { OttoConfig, ProviderId } from '../../types/src/index.ts';
|
|
4
5
|
import {
|
|
5
6
|
getProviderDefinition,
|
|
6
7
|
hasConfiguredModel,
|
|
7
|
-
isBuiltInProviderId,
|
|
8
8
|
providerAllowsAnyModel,
|
|
9
|
+
resolveBuiltInProviderCatalogId,
|
|
9
10
|
} from './registry.ts';
|
|
10
11
|
|
|
11
12
|
export type CapabilityRequest = {
|
|
@@ -28,7 +29,9 @@ export function validateProviderModel(
|
|
|
28
29
|
if (cfg) {
|
|
29
30
|
const definition = getProviderDefinition(cfg, providerId);
|
|
30
31
|
const cachedModels =
|
|
31
|
-
getCachedProviderCatalogEntry(
|
|
32
|
+
getCachedProviderCatalogEntry(
|
|
33
|
+
resolveBuiltInProviderCatalogId(providerId) ?? providerId,
|
|
34
|
+
)?.models ?? [];
|
|
32
35
|
if (!definition) {
|
|
33
36
|
if (!cachedModels.length) {
|
|
34
37
|
throw new Error(`Provider not supported: ${providerId}`);
|
|
@@ -69,12 +72,13 @@ export function validateProviderModel(
|
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
const p = providerId;
|
|
72
|
-
const
|
|
73
|
-
|
|
75
|
+
const catalogProvider = resolveBuiltInProviderCatalogId(p);
|
|
76
|
+
const builtInEntry = catalogProvider ? catalog[catalogProvider] : undefined;
|
|
77
|
+
const cachedEntry = getCachedProviderCatalogEntry(catalogProvider ?? p);
|
|
78
|
+
if (!builtInEntry && !cachedEntry) {
|
|
74
79
|
throw new Error(`Provider not supported: ${providerId}`);
|
|
75
80
|
}
|
|
76
|
-
const models =
|
|
77
|
-
getCachedProviderCatalogEntry(p)?.models ?? builtInEntry?.models ?? [];
|
|
81
|
+
const models = mergeModelLists(builtInEntry?.models, cachedEntry?.models);
|
|
78
82
|
const entry = models.find((m: { id: string }) => m.id === modelId);
|
|
79
83
|
if (!entry) {
|
|
80
84
|
throwModelNotFound(providerId, modelId, models);
|