@ai-sdk/moonshotai 0.0.20 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/README.md +40 -12
- package/dist/index.d.mts +62 -18
- package/dist/index.d.ts +62 -18
- package/dist/index.js +767 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +762 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +3 -0
- package/src/moonshotai-chat-language-model.ts +607 -7
- package/src/moonshotai-chat-metadata-extractor.ts +112 -0
- package/src/moonshotai-chat-options.ts +187 -9
- package/src/moonshotai-provider.ts +17 -0
- package/src/normalize-json-schema-for-mfjs.ts +103 -0
|
@@ -1,48 +1,620 @@
|
|
|
1
1
|
import { OpenAICompatibleChatLanguageModel } from '@ai-sdk/openai-compatible';
|
|
2
2
|
import type { OpenAICompatibleChatConfig } from '@ai-sdk/openai-compatible/internal';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
InvalidPromptError,
|
|
5
|
+
type JSONValue,
|
|
6
|
+
type LanguageModelV2,
|
|
7
|
+
type LanguageModelV2CallOptions,
|
|
8
|
+
type LanguageModelV2CallWarning,
|
|
9
|
+
type LanguageModelV2Prompt,
|
|
10
|
+
type LanguageModelV2StreamPart,
|
|
6
11
|
} from '@ai-sdk/provider';
|
|
12
|
+
import { parseProviderOptions } from '@ai-sdk/provider-utils';
|
|
13
|
+
import { z } from 'zod/v4';
|
|
7
14
|
import { convertMoonshotAIChatUsage } from './convert-moonshotai-chat-usage';
|
|
8
|
-
import
|
|
15
|
+
import { moonshotAIChatMetadataExtractor } from './moonshotai-chat-metadata-extractor';
|
|
16
|
+
import {
|
|
17
|
+
getMoonshotAIModelFamily,
|
|
18
|
+
isMoonshotAIKimiModel,
|
|
19
|
+
type MoonshotAIChatModelId,
|
|
20
|
+
type MoonshotAIProviderOptions,
|
|
21
|
+
moonshotaiAllMessageProviderOptions,
|
|
22
|
+
moonshotaiProviderOptions,
|
|
23
|
+
} from './moonshotai-chat-options';
|
|
24
|
+
import { normalizeJsonSchemaForMFJS } from './normalize-json-schema-for-mfjs';
|
|
25
|
+
|
|
26
|
+
function transformMoonshotRequestBody(
|
|
27
|
+
args: Record<string, any>,
|
|
28
|
+
): Record<string, any> {
|
|
29
|
+
const {
|
|
30
|
+
strictJsonSchema,
|
|
31
|
+
max_tokens: maxTokens,
|
|
32
|
+
logprobs,
|
|
33
|
+
topLogprobs,
|
|
34
|
+
...transformedArgs
|
|
35
|
+
} = args;
|
|
36
|
+
const moonshotArgs: Record<string, any> = {
|
|
37
|
+
...transformedArgs,
|
|
38
|
+
...((logprobs === true || topLogprobs != null) && { logprobs: true }),
|
|
39
|
+
...(topLogprobs != null && { top_logprobs: topLogprobs }),
|
|
40
|
+
...(maxTokens != null ? { max_completion_tokens: maxTokens } : {}),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (Array.isArray(moonshotArgs.messages)) {
|
|
44
|
+
moonshotArgs.messages = moonshotArgs.messages.map(
|
|
45
|
+
(message: Record<string, unknown>) => {
|
|
46
|
+
if (message.role !== 'system' || !Array.isArray(message.tools)) {
|
|
47
|
+
return message;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { content: _content, ...dynamicToolMessage } = message;
|
|
51
|
+
return dynamicToolMessage;
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const responseFormat = moonshotArgs.response_format;
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
responseFormat?.type === 'json_object' &&
|
|
60
|
+
moonshotArgs.messages?.some(
|
|
61
|
+
(message: Record<string, unknown>) => message.partial === true,
|
|
62
|
+
)
|
|
63
|
+
) {
|
|
64
|
+
throw new InvalidPromptError({
|
|
65
|
+
prompt: args.messages,
|
|
66
|
+
message:
|
|
67
|
+
'Moonshot AI Partial Mode cannot be combined with JSON object response format.',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
responseFormat?.type !== 'json_schema' ||
|
|
73
|
+
responseFormat.json_schema?.schema == null
|
|
74
|
+
) {
|
|
75
|
+
return moonshotArgs;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const { $schema: _$schema, ...schemaWithoutDollarSchema } =
|
|
79
|
+
responseFormat.json_schema.schema;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...moonshotArgs,
|
|
83
|
+
response_format: {
|
|
84
|
+
type: 'json_schema',
|
|
85
|
+
json_schema: {
|
|
86
|
+
name: responseFormat.json_schema.name ?? 'response',
|
|
87
|
+
strict: strictJsonSchema ?? true,
|
|
88
|
+
schema: normalizeJsonSchemaForMFJS(schemaWithoutDollarSchema),
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const moonshotAIValidatedOptionsSchema = moonshotaiProviderOptions.pick({
|
|
95
|
+
logprobs: true,
|
|
96
|
+
topLogprobs: true,
|
|
97
|
+
prediction: true,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
async function validateProviderOptions(
|
|
101
|
+
options: LanguageModelV2CallOptions,
|
|
102
|
+
): Promise<void> {
|
|
103
|
+
await parseProviderOptions({
|
|
104
|
+
provider: 'moonshotai',
|
|
105
|
+
providerOptions: options.providerOptions,
|
|
106
|
+
schema: moonshotAIValidatedOptionsSchema,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function prepareMessageOptions({
|
|
111
|
+
modelId,
|
|
112
|
+
options,
|
|
113
|
+
}: {
|
|
114
|
+
modelId: MoonshotAIChatModelId;
|
|
115
|
+
options: LanguageModelV2CallOptions;
|
|
116
|
+
}): Promise<{
|
|
117
|
+
options: LanguageModelV2CallOptions;
|
|
118
|
+
warnings: LanguageModelV2CallWarning[];
|
|
119
|
+
}> {
|
|
120
|
+
const prompt: LanguageModelV2Prompt = [];
|
|
121
|
+
const warnings: LanguageModelV2CallWarning[] = [];
|
|
122
|
+
|
|
123
|
+
for (const [index, message] of options.prompt.entries()) {
|
|
124
|
+
const messageOptions = await parseProviderOptions({
|
|
125
|
+
provider: 'moonshotai',
|
|
126
|
+
providerOptions: message.providerOptions,
|
|
127
|
+
schema: moonshotaiAllMessageProviderOptions,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (messageOptions?.partial === true && message.role !== 'assistant') {
|
|
131
|
+
throw new InvalidPromptError({
|
|
132
|
+
prompt: options.prompt,
|
|
133
|
+
message:
|
|
134
|
+
'Moonshot AI Partial Mode requires `partial: true` on an assistant message.',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (messageOptions?.tools != null && message.role !== 'system') {
|
|
139
|
+
throw new InvalidPromptError({
|
|
140
|
+
prompt: options.prompt,
|
|
141
|
+
message:
|
|
142
|
+
'Moonshot dynamic tools must be configured on a system message.',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (
|
|
147
|
+
messageOptions?.partial === true &&
|
|
148
|
+
index !== options.prompt.length - 1
|
|
149
|
+
) {
|
|
150
|
+
throw new InvalidPromptError({
|
|
151
|
+
prompt: options.prompt,
|
|
152
|
+
message:
|
|
153
|
+
'Moonshot AI Partial Mode requires the partial assistant message to be the final message.',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (messageOptions?.tools?.length && message.role === 'system') {
|
|
158
|
+
if (message.content.length > 0) {
|
|
159
|
+
throw new InvalidPromptError({
|
|
160
|
+
prompt: options.prompt,
|
|
161
|
+
message:
|
|
162
|
+
'A Moonshot dynamic-tool system message must use empty content because the API forbids content alongside tools.',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const modelFamily = getMoonshotAIModelFamily(modelId);
|
|
167
|
+
if (modelFamily !== 'kimi-k3' && modelFamily !== 'unknown') {
|
|
168
|
+
warnings.push({
|
|
169
|
+
type: 'other',
|
|
170
|
+
message: `Moonshot documents dynamic tool loading only for Kimi K3. The dynamic system message has been omitted for model "${modelId}".`,
|
|
171
|
+
});
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
prompt.push({
|
|
176
|
+
...message,
|
|
177
|
+
providerOptions: {
|
|
178
|
+
...message.providerOptions,
|
|
179
|
+
openaiCompatible: {
|
|
180
|
+
...message.providerOptions?.openaiCompatible,
|
|
181
|
+
tools: messageOptions.tools.map(tool => ({
|
|
182
|
+
type: 'function',
|
|
183
|
+
function: {
|
|
184
|
+
name: tool.name,
|
|
185
|
+
...(tool.description != null
|
|
186
|
+
? { description: tool.description }
|
|
187
|
+
: {}),
|
|
188
|
+
parameters: normalizeJsonSchemaForMFJS(
|
|
189
|
+
tool.inputSchema,
|
|
190
|
+
) as JSONValue,
|
|
191
|
+
...(tool.strict != null ? { strict: tool.strict } : {}),
|
|
192
|
+
},
|
|
193
|
+
})),
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (messageOptions?.name == null && messageOptions?.partial !== true) {
|
|
201
|
+
prompt.push(message);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (message.role === 'tool' && messageOptions?.name != null) {
|
|
206
|
+
warnings.push({
|
|
207
|
+
type: 'other',
|
|
208
|
+
message:
|
|
209
|
+
'Moonshot AI does not support message names on tool messages. The name has been omitted.',
|
|
210
|
+
});
|
|
211
|
+
prompt.push(message);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const openAICompatibleOptions = {
|
|
216
|
+
...message.providerOptions?.openaiCompatible,
|
|
217
|
+
...(messageOptions.name != null && { name: messageOptions.name }),
|
|
218
|
+
...(messageOptions.partial === true && { partial: true }),
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// The v5 OpenAI-compatible converter reads metadata from the text part
|
|
222
|
+
// for single-part user messages and from the message for every other
|
|
223
|
+
// supported message shape.
|
|
224
|
+
if (
|
|
225
|
+
message.role === 'user' &&
|
|
226
|
+
message.content.length === 1 &&
|
|
227
|
+
message.content[0].type === 'text'
|
|
228
|
+
) {
|
|
229
|
+
const textPart = message.content[0];
|
|
230
|
+
prompt.push({
|
|
231
|
+
...message,
|
|
232
|
+
content: [
|
|
233
|
+
{
|
|
234
|
+
...textPart,
|
|
235
|
+
providerOptions: {
|
|
236
|
+
...textPart.providerOptions,
|
|
237
|
+
openaiCompatible: {
|
|
238
|
+
...openAICompatibleOptions,
|
|
239
|
+
...textPart.providerOptions?.openaiCompatible,
|
|
240
|
+
...(messageOptions.name != null && {
|
|
241
|
+
name: messageOptions.name,
|
|
242
|
+
}),
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
});
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
prompt.push({
|
|
252
|
+
...message,
|
|
253
|
+
providerOptions: {
|
|
254
|
+
...message.providerOptions,
|
|
255
|
+
openaiCompatible: openAICompatibleOptions,
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
options: { ...options, prompt },
|
|
262
|
+
warnings,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const moonshotAIChatLogprobSchema = z.object({
|
|
267
|
+
token: z.string(),
|
|
268
|
+
logprob: z.number(),
|
|
269
|
+
bytes: z.array(z.number()).nullable(),
|
|
270
|
+
top_logprobs: z.array(
|
|
271
|
+
z.object({
|
|
272
|
+
token: z.string(),
|
|
273
|
+
logprob: z.number(),
|
|
274
|
+
bytes: z.array(z.number()).nullable(),
|
|
275
|
+
}),
|
|
276
|
+
),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const moonshotAIChatLogprobsSchema = z
|
|
280
|
+
.object({
|
|
281
|
+
content: z.array(moonshotAIChatLogprobSchema).nullish(),
|
|
282
|
+
})
|
|
283
|
+
.nullish();
|
|
284
|
+
|
|
285
|
+
type MoonshotAIChatLogprob = z.infer<typeof moonshotAIChatLogprobSchema>;
|
|
286
|
+
|
|
287
|
+
function extractMoonshotAIChatLogprobs(value: unknown) {
|
|
288
|
+
const result = moonshotAIChatLogprobsSchema.safeParse(
|
|
289
|
+
(value as { choices?: Array<{ logprobs?: unknown }> })?.choices?.[0]
|
|
290
|
+
?.logprobs,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
return result.success ? (result.data ?? undefined) : undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function prepareSamplingOptions({
|
|
297
|
+
modelId,
|
|
298
|
+
options,
|
|
299
|
+
}: {
|
|
300
|
+
modelId: MoonshotAIChatModelId;
|
|
301
|
+
options: LanguageModelV2CallOptions;
|
|
302
|
+
}): {
|
|
303
|
+
options: LanguageModelV2CallOptions;
|
|
304
|
+
warnings: LanguageModelV2CallWarning[];
|
|
305
|
+
} {
|
|
306
|
+
if (!isMoonshotAIKimiModel(modelId)) {
|
|
307
|
+
return { options, warnings: [] };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const warnings: LanguageModelV2CallWarning[] = [];
|
|
311
|
+
const samplingSettings = [
|
|
312
|
+
['temperature', options.temperature],
|
|
313
|
+
['topP', options.topP],
|
|
314
|
+
['frequencyPenalty', options.frequencyPenalty],
|
|
315
|
+
['presencePenalty', options.presencePenalty],
|
|
316
|
+
] as const;
|
|
317
|
+
|
|
318
|
+
for (const [setting, value] of samplingSettings) {
|
|
319
|
+
if (value != null) {
|
|
320
|
+
warnings.push({
|
|
321
|
+
type: 'unsupported-setting',
|
|
322
|
+
setting,
|
|
323
|
+
details: `${setting} is fixed by model "${modelId}" and has been omitted.`,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
options: {
|
|
330
|
+
...options,
|
|
331
|
+
temperature: undefined,
|
|
332
|
+
topP: undefined,
|
|
333
|
+
frequencyPenalty: undefined,
|
|
334
|
+
presencePenalty: undefined,
|
|
335
|
+
},
|
|
336
|
+
warnings,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function prepareToolChoiceOptions({
|
|
341
|
+
modelId,
|
|
342
|
+
options,
|
|
343
|
+
}: {
|
|
344
|
+
modelId: MoonshotAIChatModelId;
|
|
345
|
+
options: LanguageModelV2CallOptions;
|
|
346
|
+
}): {
|
|
347
|
+
options: LanguageModelV2CallOptions;
|
|
348
|
+
warnings: LanguageModelV2CallWarning[];
|
|
349
|
+
} {
|
|
350
|
+
if (
|
|
351
|
+
options.toolChoice?.type !== 'required' ||
|
|
352
|
+
!['kimi-k2.6', 'kimi-k2.7'].includes(getMoonshotAIModelFamily(modelId))
|
|
353
|
+
) {
|
|
354
|
+
return { options, warnings: [] };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
options: { ...options, toolChoice: undefined },
|
|
359
|
+
warnings: [
|
|
360
|
+
{
|
|
361
|
+
type: 'unsupported-setting',
|
|
362
|
+
setting: 'toolChoice',
|
|
363
|
+
details: `toolChoice "required" is not supported by model "${modelId}" and has been omitted; use "auto" or select a specific tool instead.`,
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function prepareReasoningOptions({
|
|
370
|
+
modelId,
|
|
371
|
+
options,
|
|
372
|
+
}: {
|
|
373
|
+
modelId: MoonshotAIChatModelId;
|
|
374
|
+
options: LanguageModelV2CallOptions;
|
|
375
|
+
}): {
|
|
376
|
+
options: LanguageModelV2CallOptions;
|
|
377
|
+
warnings: LanguageModelV2CallWarning[];
|
|
378
|
+
} {
|
|
379
|
+
const providerOptions = options.providerOptions?.moonshotai;
|
|
380
|
+
if (
|
|
381
|
+
providerOptions == null ||
|
|
382
|
+
typeof providerOptions !== 'object' ||
|
|
383
|
+
Array.isArray(providerOptions)
|
|
384
|
+
) {
|
|
385
|
+
return { options, warnings: [] };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const moonshotOptions = providerOptions as MoonshotAIProviderOptions;
|
|
389
|
+
const {
|
|
390
|
+
reasoningEffort: requestedReasoningEffort,
|
|
391
|
+
thinking: requestedThinking,
|
|
392
|
+
reasoningHistory,
|
|
393
|
+
...otherMoonshotOptions
|
|
394
|
+
} = moonshotOptions;
|
|
395
|
+
const preserveReasoning = reasoningHistory === 'preserved';
|
|
396
|
+
const warnings: LanguageModelV2CallWarning[] = [];
|
|
397
|
+
|
|
398
|
+
if (requestedThinking?.budgetTokens != null) {
|
|
399
|
+
warnings.push({
|
|
400
|
+
type: 'other',
|
|
401
|
+
message:
|
|
402
|
+
'providerOptions.moonshotai.thinking.budgetTokens is deprecated because Moonshot Chat Completions does not support budget_tokens. The option has been omitted.',
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
let thinking: { type: 'enabled' | 'disabled'; keep?: 'all' } | undefined;
|
|
407
|
+
let reasoningEffort: 'low' | 'high' | 'max' | undefined;
|
|
408
|
+
|
|
409
|
+
const warnUnsupportedReasoningEffort = () => {
|
|
410
|
+
if (requestedReasoningEffort != null) {
|
|
411
|
+
warnings.push({
|
|
412
|
+
type: 'unsupported-setting',
|
|
413
|
+
setting: 'providerOptions',
|
|
414
|
+
details: `providerOptions.moonshotai.reasoningEffort is only supported by Kimi K3 and has been omitted for model "${modelId}".`,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
switch (getMoonshotAIModelFamily(modelId)) {
|
|
420
|
+
case 'kimi-k3': {
|
|
421
|
+
if (requestedThinking != null) {
|
|
422
|
+
warnings.push({
|
|
423
|
+
type: 'unsupported-setting',
|
|
424
|
+
setting: 'providerOptions',
|
|
425
|
+
details:
|
|
426
|
+
'Kimi K3 always reasons and does not accept providerOptions.moonshotai.thinking. The option has been omitted.',
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
reasoningEffort = requestedReasoningEffort;
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case 'kimi-k2.7': {
|
|
433
|
+
warnUnsupportedReasoningEffort();
|
|
434
|
+
if (requestedThinking?.type === 'disabled') {
|
|
435
|
+
warnings.push({
|
|
436
|
+
type: 'unsupported-setting',
|
|
437
|
+
setting: 'providerOptions',
|
|
438
|
+
details:
|
|
439
|
+
'Kimi K2.7 thinking cannot be disabled. providerOptions.moonshotai.thinking has been omitted.',
|
|
440
|
+
});
|
|
441
|
+
} else if (requestedThinking?.type === 'enabled') {
|
|
442
|
+
thinking = { type: 'enabled' };
|
|
443
|
+
}
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
case 'kimi-k2.6': {
|
|
447
|
+
warnUnsupportedReasoningEffort();
|
|
448
|
+
const thinkingType = requestedThinking?.type;
|
|
449
|
+
if (thinkingType != null || preserveReasoning) {
|
|
450
|
+
thinking = {
|
|
451
|
+
type: thinkingType ?? 'enabled',
|
|
452
|
+
...(preserveReasoning ? { keep: 'all' as const } : {}),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case 'kimi-k2.5': {
|
|
458
|
+
warnUnsupportedReasoningEffort();
|
|
459
|
+
if (requestedThinking?.type != null) {
|
|
460
|
+
thinking = { type: requestedThinking.type };
|
|
461
|
+
}
|
|
462
|
+
if (preserveReasoning) {
|
|
463
|
+
warnings.push({
|
|
464
|
+
type: 'unsupported-setting',
|
|
465
|
+
setting: 'providerOptions',
|
|
466
|
+
details: `providerOptions.moonshotai.reasoningHistory 'preserved' is not supported by model "${modelId}" and has been omitted.`,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
case 'moonshot-v1': {
|
|
472
|
+
warnUnsupportedReasoningEffort();
|
|
473
|
+
if (requestedThinking != null) {
|
|
474
|
+
warnings.push({
|
|
475
|
+
type: 'unsupported-setting',
|
|
476
|
+
setting: 'providerOptions',
|
|
477
|
+
details: `providerOptions.moonshotai.thinking is not supported by model "${modelId}" and has been omitted.`,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (preserveReasoning) {
|
|
481
|
+
warnings.push({
|
|
482
|
+
type: 'unsupported-setting',
|
|
483
|
+
setting: 'providerOptions',
|
|
484
|
+
details: `providerOptions.moonshotai.reasoningHistory 'preserved' is not supported by model "${modelId}" and has been omitted.`,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case 'unknown': {
|
|
490
|
+
reasoningEffort = requestedReasoningEffort;
|
|
491
|
+
if (requestedThinking?.type != null) {
|
|
492
|
+
thinking = { type: requestedThinking.type };
|
|
493
|
+
}
|
|
494
|
+
if (preserveReasoning) {
|
|
495
|
+
warnings.push({
|
|
496
|
+
type: 'unsupported-setting',
|
|
497
|
+
setting: 'providerOptions',
|
|
498
|
+
details: `providerOptions.moonshotai.reasoningHistory 'preserved' is not supported by model "${modelId}" and has been omitted.`,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return {
|
|
506
|
+
options: {
|
|
507
|
+
...options,
|
|
508
|
+
providerOptions: {
|
|
509
|
+
...options.providerOptions,
|
|
510
|
+
moonshotai: {
|
|
511
|
+
...otherMoonshotOptions,
|
|
512
|
+
...(reasoningEffort != null ? { reasoningEffort } : {}),
|
|
513
|
+
...(thinking != null ? { thinking } : {}),
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
warnings,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
9
520
|
|
|
10
521
|
export class MoonshotAIChatLanguageModel extends OpenAICompatibleChatLanguageModel {
|
|
11
522
|
constructor(
|
|
12
523
|
modelId: MoonshotAIChatModelId,
|
|
13
524
|
config: OpenAICompatibleChatConfig,
|
|
14
525
|
) {
|
|
15
|
-
super(modelId,
|
|
526
|
+
super(modelId, {
|
|
527
|
+
...config,
|
|
528
|
+
metadataExtractor: moonshotAIChatMetadataExtractor,
|
|
529
|
+
transformRequestBody: args =>
|
|
530
|
+
transformMoonshotRequestBody(
|
|
531
|
+
config.transformRequestBody?.(args) ?? args,
|
|
532
|
+
),
|
|
533
|
+
});
|
|
16
534
|
}
|
|
17
535
|
|
|
18
536
|
async doGenerate(
|
|
19
537
|
options: Parameters<LanguageModelV2['doGenerate']>[0],
|
|
20
538
|
): Promise<Awaited<ReturnType<LanguageModelV2['doGenerate']>>> {
|
|
21
|
-
const
|
|
539
|
+
const { options: messageOptions, warnings: messageWarnings } =
|
|
540
|
+
await prepareMessageOptions({ modelId: this.modelId, options });
|
|
541
|
+
await validateProviderOptions(messageOptions);
|
|
542
|
+
|
|
543
|
+
const { options: samplingOptions, warnings: samplingWarnings } =
|
|
544
|
+
prepareSamplingOptions({
|
|
545
|
+
modelId: this.modelId,
|
|
546
|
+
options: messageOptions,
|
|
547
|
+
});
|
|
548
|
+
const { options: sanitizedOptions, warnings: reasoningWarnings } =
|
|
549
|
+
prepareReasoningOptions({
|
|
550
|
+
modelId: this.modelId,
|
|
551
|
+
options: samplingOptions,
|
|
552
|
+
});
|
|
553
|
+
const { options: toolChoiceOptions, warnings: toolChoiceWarnings } =
|
|
554
|
+
prepareToolChoiceOptions({
|
|
555
|
+
modelId: this.modelId,
|
|
556
|
+
options: sanitizedOptions,
|
|
557
|
+
});
|
|
558
|
+
const result = await super.doGenerate(toolChoiceOptions);
|
|
22
559
|
|
|
23
560
|
// @ts-expect-error accessing response body from parent result
|
|
24
561
|
const usage = result.response?.body?.usage;
|
|
562
|
+
const logprobs = extractMoonshotAIChatLogprobs(result.response?.body);
|
|
25
563
|
|
|
26
564
|
return {
|
|
27
565
|
...result,
|
|
28
566
|
usage: convertMoonshotAIChatUsage(usage),
|
|
567
|
+
providerMetadata: {
|
|
568
|
+
...result.providerMetadata,
|
|
569
|
+
moonshotai: {
|
|
570
|
+
...result.providerMetadata?.moonshotai,
|
|
571
|
+
...(logprobs != null && { logprobs }),
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
warnings: [
|
|
575
|
+
...result.warnings,
|
|
576
|
+
...messageWarnings,
|
|
577
|
+
...samplingWarnings,
|
|
578
|
+
...reasoningWarnings,
|
|
579
|
+
...toolChoiceWarnings,
|
|
580
|
+
],
|
|
29
581
|
};
|
|
30
582
|
}
|
|
31
583
|
|
|
32
584
|
async doStream(
|
|
33
585
|
options: Parameters<LanguageModelV2['doStream']>[0],
|
|
34
586
|
): Promise<Awaited<ReturnType<LanguageModelV2['doStream']>>> {
|
|
587
|
+
const { options: messageOptions, warnings: messageWarnings } =
|
|
588
|
+
await prepareMessageOptions({ modelId: this.modelId, options });
|
|
589
|
+
await validateProviderOptions(messageOptions);
|
|
590
|
+
|
|
35
591
|
const originalIncludeRawChunks = options.includeRawChunks;
|
|
592
|
+
const { options: samplingOptions, warnings: samplingWarnings } =
|
|
593
|
+
prepareSamplingOptions({
|
|
594
|
+
modelId: this.modelId,
|
|
595
|
+
options: messageOptions,
|
|
596
|
+
});
|
|
597
|
+
const { options: sanitizedOptions, warnings: reasoningWarnings } =
|
|
598
|
+
prepareReasoningOptions({
|
|
599
|
+
modelId: this.modelId,
|
|
600
|
+
options: samplingOptions,
|
|
601
|
+
});
|
|
602
|
+
const { options: toolChoiceOptions, warnings: toolChoiceWarnings } =
|
|
603
|
+
prepareToolChoiceOptions({
|
|
604
|
+
modelId: this.modelId,
|
|
605
|
+
options: sanitizedOptions,
|
|
606
|
+
});
|
|
36
607
|
|
|
37
608
|
// Enable raw chunks to capture pre-Zod usage data, since MoonshotAI
|
|
38
609
|
// returns cached_tokens at the top level of usage (not nested in
|
|
39
610
|
// prompt_tokens_details) and the parent's z.object() schema strips it.
|
|
40
611
|
const result = await super.doStream({
|
|
41
|
-
...
|
|
612
|
+
...toolChoiceOptions,
|
|
42
613
|
includeRawChunks: true,
|
|
43
614
|
});
|
|
44
615
|
|
|
45
616
|
let rawUsage: unknown = undefined;
|
|
617
|
+
const contentLogprobs: MoonshotAIChatLogprob[] = [];
|
|
46
618
|
|
|
47
619
|
return {
|
|
48
620
|
...result,
|
|
@@ -52,6 +624,20 @@ export class MoonshotAIChatLanguageModel extends OpenAICompatibleChatLanguageMod
|
|
|
52
624
|
LanguageModelV2StreamPart
|
|
53
625
|
>({
|
|
54
626
|
transform(chunk, controller) {
|
|
627
|
+
if (chunk.type === 'stream-start') {
|
|
628
|
+
controller.enqueue({
|
|
629
|
+
...chunk,
|
|
630
|
+
warnings: [
|
|
631
|
+
...chunk.warnings,
|
|
632
|
+
...messageWarnings,
|
|
633
|
+
...samplingWarnings,
|
|
634
|
+
...reasoningWarnings,
|
|
635
|
+
...toolChoiceWarnings,
|
|
636
|
+
],
|
|
637
|
+
});
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
|
|
55
641
|
if (chunk.type === 'raw') {
|
|
56
642
|
// Capture raw usage data before Zod strips cached_tokens
|
|
57
643
|
const rawValue = chunk.rawValue as Record<string, unknown>;
|
|
@@ -59,6 +645,11 @@ export class MoonshotAIChatLanguageModel extends OpenAICompatibleChatLanguageMod
|
|
|
59
645
|
rawUsage = rawValue.usage;
|
|
60
646
|
}
|
|
61
647
|
|
|
648
|
+
const logprobs = extractMoonshotAIChatLogprobs(rawValue);
|
|
649
|
+
if (logprobs?.content != null) {
|
|
650
|
+
contentLogprobs.push(...logprobs.content);
|
|
651
|
+
}
|
|
652
|
+
|
|
62
653
|
// Only forward raw chunks if originally requested
|
|
63
654
|
if (originalIncludeRawChunks) {
|
|
64
655
|
controller.enqueue(chunk);
|
|
@@ -73,6 +664,15 @@ export class MoonshotAIChatLanguageModel extends OpenAICompatibleChatLanguageMod
|
|
|
73
664
|
usage: rawUsage
|
|
74
665
|
? convertMoonshotAIChatUsage(rawUsage as any)
|
|
75
666
|
: chunk.usage,
|
|
667
|
+
providerMetadata: {
|
|
668
|
+
...chunk.providerMetadata,
|
|
669
|
+
moonshotai: {
|
|
670
|
+
...chunk.providerMetadata?.moonshotai,
|
|
671
|
+
...(contentLogprobs.length > 0 && {
|
|
672
|
+
logprobs: { content: contentLogprobs },
|
|
673
|
+
}),
|
|
674
|
+
},
|
|
675
|
+
},
|
|
76
676
|
});
|
|
77
677
|
return;
|
|
78
678
|
}
|