@ottocode/sdk 0.1.312 → 0.1.313
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 +1 -1
- package/src/config/src/index.ts +27 -3
- package/src/config/src/manager.ts +13 -1
- package/src/providers/src/catalog-manual.ts +4 -24
- package/src/providers/src/catalog.ts +93 -21
- package/src/providers/src/model-merge.ts +2 -2
- package/src/providers/src/moonshot-client.ts +85 -0
- package/src/providers/src/utils.ts +9 -2
package/package.json
CHANGED
package/src/config/src/index.ts
CHANGED
|
@@ -51,6 +51,12 @@ const DEFAULTS: {
|
|
|
51
51
|
providers: DEFAULT_PROVIDER_SETTINGS,
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
const LOCAL_DEFAULT_OVERRIDE_KEYS = [
|
|
55
|
+
'agent',
|
|
56
|
+
'provider',
|
|
57
|
+
'model',
|
|
58
|
+
] satisfies Array<keyof OttoConfig['defaults']>;
|
|
59
|
+
|
|
54
60
|
export async function loadConfig(
|
|
55
61
|
projectRootInput?: string,
|
|
56
62
|
): Promise<OttoConfig> {
|
|
@@ -72,7 +78,7 @@ export async function loadConfig(
|
|
|
72
78
|
DEFAULTS,
|
|
73
79
|
globalCfg,
|
|
74
80
|
globalSkillsCfg ? { skills: globalSkillsCfg } : undefined,
|
|
75
|
-
|
|
81
|
+
filterProjectConfig(projectCfg),
|
|
76
82
|
);
|
|
77
83
|
|
|
78
84
|
await ensureDir(dataDir);
|
|
@@ -97,14 +103,32 @@ export async function loadConfig(
|
|
|
97
103
|
|
|
98
104
|
type JsonObject = Record<string, unknown>;
|
|
99
105
|
|
|
100
|
-
function
|
|
106
|
+
function filterProjectConfig(
|
|
101
107
|
config: JsonObject | undefined,
|
|
102
108
|
): JsonObject | undefined {
|
|
103
109
|
if (!config) return undefined;
|
|
104
|
-
const { providers: _providers, skills: _skills, ...rest } = config;
|
|
110
|
+
const { providers: _providers, skills: _skills, defaults, ...rest } = config;
|
|
111
|
+
const localDefaults = pickLocalDefaults(defaults);
|
|
112
|
+
if (localDefaults) {
|
|
113
|
+
return { ...rest, defaults: localDefaults };
|
|
114
|
+
}
|
|
105
115
|
return rest;
|
|
106
116
|
}
|
|
107
117
|
|
|
118
|
+
function pickLocalDefaults(defaults: unknown): JsonObject | undefined {
|
|
119
|
+
if (!defaults || typeof defaults !== 'object' || Array.isArray(defaults)) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
const source = defaults as JsonObject;
|
|
123
|
+
const picked: JsonObject = {};
|
|
124
|
+
for (const key of LOCAL_DEFAULT_OVERRIDE_KEYS) {
|
|
125
|
+
if (Object.hasOwn(source, key)) {
|
|
126
|
+
picked[key] = source[key];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return Object.keys(picked).length > 0 ? picked : undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
108
132
|
async function readJsonOptional(file: string): Promise<JsonObject | undefined> {
|
|
109
133
|
const f = Bun.file(file);
|
|
110
134
|
if (!(await f.exists())) return undefined;
|
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
|
|
29
29
|
export type Scope = 'global' | 'local';
|
|
30
30
|
|
|
31
|
+
const LOCAL_DEFAULT_UPDATE_KEYS = new Set(['agent', 'provider', 'model']);
|
|
32
|
+
|
|
31
33
|
export type DebugConfig = {
|
|
32
34
|
enabled: boolean;
|
|
33
35
|
scopes: string[];
|
|
@@ -95,6 +97,16 @@ export async function writeDefaults(
|
|
|
95
97
|
}>,
|
|
96
98
|
projectRoot?: string,
|
|
97
99
|
) {
|
|
100
|
+
const scopedUpdates =
|
|
101
|
+
scope === 'local'
|
|
102
|
+
? (Object.fromEntries(
|
|
103
|
+
Object.entries(updates).filter(([key]) =>
|
|
104
|
+
LOCAL_DEFAULT_UPDATE_KEYS.has(key),
|
|
105
|
+
),
|
|
106
|
+
) as typeof updates)
|
|
107
|
+
: updates;
|
|
108
|
+
if (Object.keys(scopedUpdates).length === 0) return;
|
|
109
|
+
|
|
98
110
|
const filePath = getConfigFilePath(scope, projectRoot);
|
|
99
111
|
const existing = await readJsonFile(filePath);
|
|
100
112
|
const prevDefaults =
|
|
@@ -103,7 +115,7 @@ export async function writeDefaults(
|
|
|
103
115
|
: {};
|
|
104
116
|
const next = {
|
|
105
117
|
...existing,
|
|
106
|
-
defaults: { ...prevDefaults, ...
|
|
118
|
+
defaults: { ...prevDefaults, ...scopedUpdates },
|
|
107
119
|
};
|
|
108
120
|
await writeConfigFile(filePath, next);
|
|
109
121
|
}
|
|
@@ -14,20 +14,6 @@ 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
|
-
|
|
31
17
|
const XAI_GROK_CLI_MODELS: ModelInfo[] = [
|
|
32
18
|
{
|
|
33
19
|
id: 'grok-build',
|
|
@@ -168,13 +154,10 @@ export function appendXaiGrokCliModels<T extends { models: ModelInfo[] }>(
|
|
|
168
154
|
return { ...entry, models: [...mergedModels, ...missingModels] };
|
|
169
155
|
}
|
|
170
156
|
|
|
171
|
-
export function
|
|
172
|
-
|
|
173
|
-
): T | undefined {
|
|
157
|
+
export function applyOfficialKimiCatalogMetadata<
|
|
158
|
+
T extends ProviderCatalogEntry,
|
|
159
|
+
>(entry: T | undefined): T | undefined {
|
|
174
160
|
if (!entry) return undefined;
|
|
175
|
-
const hasKimiK27Code = entry.models.some(
|
|
176
|
-
(model) => model.id === KIMI_K2_7_CODE_MODEL.id,
|
|
177
|
-
);
|
|
178
161
|
const env = Array.from(
|
|
179
162
|
new Set(['KIMI_API_KEY', 'MOONSHOT_API_KEY', ...(entry.env ?? [])]),
|
|
180
163
|
);
|
|
@@ -183,9 +166,6 @@ export function appendOfficialKimiModels<T extends ProviderCatalogEntry>(
|
|
|
183
166
|
label: entry.label === 'Moonshot AI' ? 'Kimi' : entry.label,
|
|
184
167
|
env,
|
|
185
168
|
doc: 'https://platform.kimi.ai/docs/api/overview.md',
|
|
186
|
-
models: hasKimiK27Code
|
|
187
|
-
? entry.models
|
|
188
|
-
: [...entry.models, KIMI_K2_7_CODE_MODEL],
|
|
189
169
|
};
|
|
190
170
|
}
|
|
191
171
|
|
|
@@ -202,7 +182,7 @@ export function mergeManualCatalog(
|
|
|
202
182
|
if (xaiEntry) {
|
|
203
183
|
merged.xai = xaiEntry;
|
|
204
184
|
}
|
|
205
|
-
const moonshotEntry =
|
|
185
|
+
const moonshotEntry = applyOfficialKimiCatalogMetadata(merged.moonshot);
|
|
206
186
|
if (moonshotEntry) {
|
|
207
187
|
merged.moonshot = moonshotEntry;
|
|
208
188
|
}
|
|
@@ -2343,13 +2343,13 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
2343
2343
|
lastUpdated: '2026-04-27',
|
|
2344
2344
|
openWeights: false,
|
|
2345
2345
|
cost: {
|
|
2346
|
-
input: 0.
|
|
2347
|
-
output: 3.
|
|
2348
|
-
cacheRead: 0.
|
|
2346
|
+
input: 0.68,
|
|
2347
|
+
output: 3.41,
|
|
2348
|
+
cacheRead: 0.34,
|
|
2349
2349
|
},
|
|
2350
2350
|
limit: {
|
|
2351
|
-
context:
|
|
2352
|
-
output:
|
|
2351
|
+
context: 262142,
|
|
2352
|
+
output: 262142,
|
|
2353
2353
|
},
|
|
2354
2354
|
},
|
|
2355
2355
|
{
|
|
@@ -3231,7 +3231,7 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
3231
3231
|
cacheRead: 0.135,
|
|
3232
3232
|
},
|
|
3233
3233
|
limit: {
|
|
3234
|
-
context:
|
|
3234
|
+
context: 163840,
|
|
3235
3235
|
output: 16384,
|
|
3236
3236
|
},
|
|
3237
3237
|
},
|
|
@@ -3398,12 +3398,12 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
3398
3398
|
lastUpdated: '2026-04-24',
|
|
3399
3399
|
openWeights: true,
|
|
3400
3400
|
cost: {
|
|
3401
|
-
input: 0.
|
|
3402
|
-
output: 0.
|
|
3403
|
-
cacheRead: 0.
|
|
3401
|
+
input: 0.098,
|
|
3402
|
+
output: 0.196,
|
|
3403
|
+
cacheRead: 0.02,
|
|
3404
3404
|
},
|
|
3405
3405
|
limit: {
|
|
3406
|
-
context:
|
|
3406
|
+
context: 1048575,
|
|
3407
3407
|
output: 131072,
|
|
3408
3408
|
},
|
|
3409
3409
|
},
|
|
@@ -4873,12 +4873,11 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
4873
4873
|
lastUpdated: '2026-01',
|
|
4874
4874
|
openWeights: true,
|
|
4875
4875
|
cost: {
|
|
4876
|
-
input: 0.
|
|
4877
|
-
output:
|
|
4878
|
-
cacheRead: 0.09,
|
|
4876
|
+
input: 0.375,
|
|
4877
|
+
output: 2.025,
|
|
4879
4878
|
},
|
|
4880
4879
|
limit: {
|
|
4881
|
-
context:
|
|
4880
|
+
context: 256000,
|
|
4882
4881
|
output: 262144,
|
|
4883
4882
|
},
|
|
4884
4883
|
},
|
|
@@ -4899,9 +4898,35 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
4899
4898
|
lastUpdated: '2026-04-21',
|
|
4900
4899
|
openWeights: true,
|
|
4901
4900
|
cost: {
|
|
4902
|
-
input: 0.
|
|
4903
|
-
output: 3.
|
|
4904
|
-
cacheRead: 0.
|
|
4901
|
+
input: 0.68,
|
|
4902
|
+
output: 3.41,
|
|
4903
|
+
cacheRead: 0.34,
|
|
4904
|
+
},
|
|
4905
|
+
limit: {
|
|
4906
|
+
context: 262142,
|
|
4907
|
+
output: 262142,
|
|
4908
|
+
},
|
|
4909
|
+
},
|
|
4910
|
+
{
|
|
4911
|
+
id: 'moonshotai/kimi-k2.7-code',
|
|
4912
|
+
ownedBy: 'moonshot',
|
|
4913
|
+
label: 'Kimi K2.7 Code',
|
|
4914
|
+
modalities: {
|
|
4915
|
+
input: ['text', 'image'],
|
|
4916
|
+
output: ['text'],
|
|
4917
|
+
},
|
|
4918
|
+
toolCall: true,
|
|
4919
|
+
reasoningText: true,
|
|
4920
|
+
attachment: true,
|
|
4921
|
+
temperature: true,
|
|
4922
|
+
knowledge: '2025-01',
|
|
4923
|
+
releaseDate: '2026-06-12',
|
|
4924
|
+
lastUpdated: '2026-06-12',
|
|
4925
|
+
openWeights: true,
|
|
4926
|
+
cost: {
|
|
4927
|
+
input: 0.75,
|
|
4928
|
+
output: 3.5,
|
|
4929
|
+
cacheRead: 0.16,
|
|
4905
4930
|
},
|
|
4906
4931
|
limit: {
|
|
4907
4932
|
context: 262144,
|
|
@@ -7633,12 +7658,12 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
7633
7658
|
lastUpdated: '2026-04-22',
|
|
7634
7659
|
openWeights: true,
|
|
7635
7660
|
cost: {
|
|
7636
|
-
input: 0.
|
|
7637
|
-
output:
|
|
7661
|
+
input: 0.2885,
|
|
7662
|
+
output: 3.17,
|
|
7638
7663
|
},
|
|
7639
7664
|
limit: {
|
|
7640
|
-
context:
|
|
7641
|
-
output:
|
|
7665
|
+
context: 262140,
|
|
7666
|
+
output: 262140,
|
|
7642
7667
|
},
|
|
7643
7668
|
},
|
|
7644
7669
|
{
|
|
@@ -10924,6 +10949,32 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
10924
10949
|
output: 131072,
|
|
10925
10950
|
},
|
|
10926
10951
|
},
|
|
10952
|
+
{
|
|
10953
|
+
id: 'glm-5.2',
|
|
10954
|
+
ownedBy: 'zai',
|
|
10955
|
+
label: 'GLM-5.2',
|
|
10956
|
+
modalities: {
|
|
10957
|
+
input: ['text'],
|
|
10958
|
+
output: ['text'],
|
|
10959
|
+
},
|
|
10960
|
+
toolCall: true,
|
|
10961
|
+
reasoningText: true,
|
|
10962
|
+
attachment: false,
|
|
10963
|
+
temperature: true,
|
|
10964
|
+
releaseDate: '2026-06-13',
|
|
10965
|
+
lastUpdated: '2026-06-13',
|
|
10966
|
+
openWeights: false,
|
|
10967
|
+
cost: {
|
|
10968
|
+
input: 0,
|
|
10969
|
+
output: 0,
|
|
10970
|
+
cacheRead: 0,
|
|
10971
|
+
cacheWrite: 0,
|
|
10972
|
+
},
|
|
10973
|
+
limit: {
|
|
10974
|
+
context: 1000000,
|
|
10975
|
+
output: 131072,
|
|
10976
|
+
},
|
|
10977
|
+
},
|
|
10927
10978
|
{
|
|
10928
10979
|
id: 'glm-5v-turbo',
|
|
10929
10980
|
ownedBy: 'zai',
|
|
@@ -12338,6 +12389,27 @@ export const catalog: Partial<Record<BuiltInProviderId, ProviderCatalogEntry>> =
|
|
|
12338
12389
|
output: 262144,
|
|
12339
12390
|
},
|
|
12340
12391
|
},
|
|
12392
|
+
{
|
|
12393
|
+
id: 'kimi-k2.7-code',
|
|
12394
|
+
ownedBy: 'moonshot',
|
|
12395
|
+
label: 'kimi-k2.7-code',
|
|
12396
|
+
modalities: {
|
|
12397
|
+
input: ['text', 'image'],
|
|
12398
|
+
output: ['text'],
|
|
12399
|
+
},
|
|
12400
|
+
toolCall: true,
|
|
12401
|
+
reasoningText: true,
|
|
12402
|
+
attachment: true,
|
|
12403
|
+
temperature: false,
|
|
12404
|
+
knowledge: '2025-01',
|
|
12405
|
+
releaseDate: '2026-06-12',
|
|
12406
|
+
lastUpdated: '2026-06-12',
|
|
12407
|
+
openWeights: true,
|
|
12408
|
+
limit: {
|
|
12409
|
+
context: 262144,
|
|
12410
|
+
output: 262144,
|
|
12411
|
+
},
|
|
12412
|
+
},
|
|
12341
12413
|
{
|
|
12342
12414
|
id: 'minimax-m2',
|
|
12343
12415
|
ownedBy: 'minimax',
|
|
@@ -5,8 +5,8 @@ import type { ModelInfo } from '../../types/src/index.ts';
|
|
|
5
5
|
*
|
|
6
6
|
* Cached entries override fields for overlapping ids (so remote updates like
|
|
7
7
|
* pricing/limits still apply), while embedded/manual-only models (for example
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* the manual xai grok-cli models) are always retained even when a stale cache
|
|
9
|
+
* does not include them.
|
|
10
10
|
*/
|
|
11
11
|
export function mergeModelLists(
|
|
12
12
|
baseModels: ModelInfo[] | undefined,
|
|
@@ -15,6 +15,90 @@ export function readKimiApiKeyFromEnv(): string {
|
|
|
15
15
|
return process.env.KIMI_API_KEY || process.env.MOONSHOT_API_KEY || '';
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Kimi/Moonshot streaming responses report token usage on the final chunk's
|
|
20
|
+
* `choices[0].usage` instead of the OpenAI-standard top-level `usage` field.
|
|
21
|
+
* The AI SDK openai-compatible parser only reads top-level `usage`, so we
|
|
22
|
+
* hoist choice-level usage to the top level when it is missing.
|
|
23
|
+
*/
|
|
24
|
+
export function hoistKimiSseUsage(line: string): string {
|
|
25
|
+
const hasCarriageReturn = line.endsWith('\r');
|
|
26
|
+
const raw = hasCarriageReturn ? line.slice(0, -1) : line;
|
|
27
|
+
if (!raw.startsWith('data:')) return line;
|
|
28
|
+
const payload = raw.slice(5).trim();
|
|
29
|
+
if (!payload || payload === '[DONE]') return line;
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(payload) as {
|
|
32
|
+
usage?: unknown;
|
|
33
|
+
choices?: Array<{ usage?: unknown } | null>;
|
|
34
|
+
};
|
|
35
|
+
if (!parsed || typeof parsed !== 'object' || parsed.usage != null) {
|
|
36
|
+
return line;
|
|
37
|
+
}
|
|
38
|
+
const choiceUsage = Array.isArray(parsed.choices)
|
|
39
|
+
? parsed.choices.find((choice) => choice?.usage != null)?.usage
|
|
40
|
+
: undefined;
|
|
41
|
+
if (choiceUsage == null) return line;
|
|
42
|
+
parsed.usage = choiceUsage;
|
|
43
|
+
return `data: ${JSON.stringify(parsed)}${hasCarriageReturn ? '\r' : ''}`;
|
|
44
|
+
} catch {
|
|
45
|
+
return line;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Wraps fetch so Kimi SSE chunks carrying `choices[0].usage` are rewritten to
|
|
51
|
+
* expose a top-level `usage` field the AI SDK can parse.
|
|
52
|
+
*/
|
|
53
|
+
export function createKimiUsageFetch(
|
|
54
|
+
baseFetch: typeof fetch = fetch,
|
|
55
|
+
): typeof fetch {
|
|
56
|
+
const wrappedFetch = async (
|
|
57
|
+
input: Parameters<typeof fetch>[0],
|
|
58
|
+
init?: Parameters<typeof fetch>[1],
|
|
59
|
+
): Promise<Response> => {
|
|
60
|
+
const response = await baseFetch(input, init);
|
|
61
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
62
|
+
if (
|
|
63
|
+
!response.ok ||
|
|
64
|
+
!response.body ||
|
|
65
|
+
!contentType.includes('text/event-stream')
|
|
66
|
+
) {
|
|
67
|
+
return response;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const decoder = new TextDecoder();
|
|
71
|
+
const encoder = new TextEncoder();
|
|
72
|
+
let buffered = '';
|
|
73
|
+
const transform = new TransformStream<Uint8Array, Uint8Array>({
|
|
74
|
+
transform(chunk, controller) {
|
|
75
|
+
buffered += decoder.decode(chunk, { stream: true });
|
|
76
|
+
const lines = buffered.split('\n');
|
|
77
|
+
buffered = lines.pop() ?? '';
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
controller.enqueue(encoder.encode(`${hoistKimiSseUsage(line)}\n`));
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
flush(controller) {
|
|
83
|
+
buffered += decoder.decode();
|
|
84
|
+
if (buffered.length) {
|
|
85
|
+
controller.enqueue(encoder.encode(hoistKimiSseUsage(buffered)));
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const headers = new Headers(response.headers);
|
|
91
|
+
headers.delete('content-length');
|
|
92
|
+
headers.delete('content-encoding');
|
|
93
|
+
return new Response(response.body.pipeThrough(transform), {
|
|
94
|
+
status: response.status,
|
|
95
|
+
statusText: response.statusText,
|
|
96
|
+
headers,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
return wrappedFetch as typeof fetch;
|
|
100
|
+
}
|
|
101
|
+
|
|
18
102
|
export function createKimiModel(model: string, config?: KimiProviderConfig) {
|
|
19
103
|
const entry = catalog.moonshot;
|
|
20
104
|
const oauthAccess = config?.oauth?.access;
|
|
@@ -34,6 +118,7 @@ export function createKimiModel(model: string, config?: KimiProviderConfig) {
|
|
|
34
118
|
name: 'Kimi',
|
|
35
119
|
baseURL,
|
|
36
120
|
headers,
|
|
121
|
+
fetch: createKimiUsageFetch(),
|
|
37
122
|
});
|
|
38
123
|
|
|
39
124
|
return instance(model);
|
|
@@ -45,18 +45,25 @@ const PREFERRED_FAST_MODELS: Partial<Record<ProviderId, string[]>> = {
|
|
|
45
45
|
xai: ['grok-code-fast-1', 'grok-4-fast'],
|
|
46
46
|
zai: ['glm-4.5-flash'],
|
|
47
47
|
copilot: ['gpt-4.1-mini'],
|
|
48
|
+
moonshot: ['kimi-k2-turbo-preview'],
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
const PREFERRED_FAST_MODELS_OAUTH: Partial<Record<ProviderId, string[]>> = {
|
|
51
52
|
openai: ['gpt-5.4-mini'],
|
|
52
53
|
anthropic: ['claude-haiku-4-5'],
|
|
54
|
+
moonshot: ['kimi-k2.7-code'],
|
|
53
55
|
};
|
|
54
56
|
|
|
57
|
+
function preferredFastModelKey(provider: ProviderId): ProviderId {
|
|
58
|
+
return resolveBuiltInProviderCatalogId(provider) ?? provider;
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
export function getFastModel(provider: ProviderId): string | undefined {
|
|
56
62
|
const providerModels = getProviderModels(provider);
|
|
57
63
|
if (!providerModels.length) return undefined;
|
|
58
64
|
|
|
59
|
-
const preferred =
|
|
65
|
+
const preferred =
|
|
66
|
+
PREFERRED_FAST_MODELS[preferredFastModelKey(provider)] ?? [];
|
|
60
67
|
for (const modelId of preferred) {
|
|
61
68
|
if (providerModels.some((m) => m.id === modelId)) {
|
|
62
69
|
return modelId;
|
|
@@ -86,7 +93,7 @@ export function getFastModelForAuth(
|
|
|
86
93
|
|
|
87
94
|
const preferredMap =
|
|
88
95
|
authType === 'oauth' ? PREFERRED_FAST_MODELS_OAUTH : PREFERRED_FAST_MODELS;
|
|
89
|
-
const preferred = preferredMap[provider] ?? [];
|
|
96
|
+
const preferred = preferredMap[preferredFastModelKey(provider)] ?? [];
|
|
90
97
|
for (const modelId of preferred) {
|
|
91
98
|
if (filteredModels.some((m) => m.id === modelId)) {
|
|
92
99
|
return modelId;
|