@ai-sdk/moonshotai 3.0.31 → 3.0.32
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 +6 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +683 -70
- package/dist/index.js.map +1 -1
- package/package.json +5 -6
- package/src/convert-to-moonshotai-chat-messages.ts +197 -0
- package/src/map-moonshotai-finish-reason.ts +19 -0
- package/src/moonshotai-chat-api-types.ts +161 -0
- package/src/moonshotai-chat-language-model.ts +454 -29
- package/src/moonshotai-chat-options.ts +29 -2
- package/src/moonshotai-prepare-tools.ts +98 -0
- package/src/moonshotai-provider.ts +5 -70
|
@@ -1,20 +1,75 @@
|
|
|
1
|
-
import { OpenAICompatibleChatLanguageModel } from '@ai-sdk/openai-compatible';
|
|
2
|
-
import type { OpenAICompatibleChatConfig } from '@ai-sdk/openai-compatible/internal';
|
|
3
|
-
import {
|
|
4
|
-
serializeModelOptions,
|
|
5
|
-
WORKFLOW_SERIALIZE,
|
|
6
|
-
WORKFLOW_DESERIALIZE,
|
|
7
|
-
} from '@ai-sdk/provider-utils';
|
|
8
1
|
import type {
|
|
2
|
+
APICallError,
|
|
3
|
+
LanguageModelV4,
|
|
9
4
|
LanguageModelV4CallOptions,
|
|
5
|
+
LanguageModelV4Content,
|
|
6
|
+
LanguageModelV4FinishReason,
|
|
10
7
|
LanguageModelV4GenerateResult,
|
|
11
8
|
LanguageModelV4StreamPart,
|
|
12
9
|
LanguageModelV4StreamResult,
|
|
10
|
+
SharedV4Warning,
|
|
13
11
|
} from '@ai-sdk/provider';
|
|
12
|
+
import {
|
|
13
|
+
combineHeaders,
|
|
14
|
+
createEventSourceResponseHandler,
|
|
15
|
+
createJsonErrorResponseHandler,
|
|
16
|
+
createJsonResponseHandler,
|
|
17
|
+
createLanguageModelResponseMetadata as getResponseMetadata,
|
|
18
|
+
generateId,
|
|
19
|
+
isCustomReasoning,
|
|
20
|
+
mapReasoningToProviderEffort,
|
|
21
|
+
parseProviderOptions,
|
|
22
|
+
postJsonToApi,
|
|
23
|
+
serializeModelOptions,
|
|
24
|
+
StreamingToolCallTracker,
|
|
25
|
+
WORKFLOW_SERIALIZE,
|
|
26
|
+
WORKFLOW_DESERIALIZE,
|
|
27
|
+
type FetchFunction,
|
|
28
|
+
type InferSchema,
|
|
29
|
+
type ParseResult,
|
|
30
|
+
type ResponseHandler,
|
|
31
|
+
} from '@ai-sdk/provider-utils';
|
|
32
|
+
import { convertToMoonshotAIChatMessages } from './convert-to-moonshotai-chat-messages';
|
|
14
33
|
import { convertMoonshotAIChatUsage } from './convert-moonshotai-chat-usage';
|
|
15
|
-
import
|
|
34
|
+
import { mapMoonshotAIFinishReason } from './map-moonshotai-finish-reason';
|
|
35
|
+
import {
|
|
36
|
+
moonshotAIChatChunkSchema,
|
|
37
|
+
moonshotAIChatResponseSchema,
|
|
38
|
+
moonshotAIErrorSchema,
|
|
39
|
+
type MoonshotAIChatTokenUsage,
|
|
40
|
+
} from './moonshotai-chat-api-types';
|
|
41
|
+
import {
|
|
42
|
+
getModelThinkingKeepSupport,
|
|
43
|
+
moonshotaiLanguageModelOptions,
|
|
44
|
+
type MoonshotAIChatModelId,
|
|
45
|
+
} from './moonshotai-chat-options';
|
|
46
|
+
import { prepareTools } from './moonshotai-prepare-tools';
|
|
47
|
+
|
|
48
|
+
export type MoonshotAIChatConfig = {
|
|
49
|
+
provider: string;
|
|
50
|
+
headers?: () => Record<string, string | undefined>;
|
|
51
|
+
url: (options: { modelId: string; path: string }) => string;
|
|
52
|
+
fetch?: FetchFunction;
|
|
53
|
+
includeUsage?: boolean;
|
|
54
|
+
supportsStructuredOutputs?: boolean;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export class MoonshotAIChatLanguageModel implements LanguageModelV4 {
|
|
58
|
+
readonly specificationVersion = 'v4';
|
|
59
|
+
|
|
60
|
+
readonly modelId: MoonshotAIChatModelId;
|
|
61
|
+
|
|
62
|
+
// Moonshot AI does not fetch external URLs; the AI SDK downloads and
|
|
63
|
+
// inlines URL file parts instead. ms:// file references from the Moonshot
|
|
64
|
+
// Files API are passed through natively.
|
|
65
|
+
readonly supportedUrls = {
|
|
66
|
+
'image/*': [/^ms:\/\//],
|
|
67
|
+
'video/*': [/^ms:\/\//],
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
private readonly config: MoonshotAIChatConfig;
|
|
71
|
+
private readonly failedResponseHandler: ResponseHandler<APICallError>;
|
|
16
72
|
|
|
17
|
-
export class MoonshotAIChatLanguageModel extends OpenAICompatibleChatLanguageModel {
|
|
18
73
|
static [WORKFLOW_SERIALIZE](model: MoonshotAIChatLanguageModel) {
|
|
19
74
|
return serializeModelOptions({
|
|
20
75
|
modelId: model.modelId,
|
|
@@ -24,56 +79,426 @@ export class MoonshotAIChatLanguageModel extends OpenAICompatibleChatLanguageMod
|
|
|
24
79
|
|
|
25
80
|
static [WORKFLOW_DESERIALIZE](options: {
|
|
26
81
|
modelId: MoonshotAIChatModelId;
|
|
27
|
-
config:
|
|
82
|
+
config: MoonshotAIChatConfig;
|
|
28
83
|
}) {
|
|
29
84
|
return new MoonshotAIChatLanguageModel(options.modelId, options.config);
|
|
30
85
|
}
|
|
31
86
|
|
|
32
|
-
constructor(
|
|
33
|
-
modelId
|
|
34
|
-
config
|
|
35
|
-
|
|
36
|
-
|
|
87
|
+
constructor(modelId: MoonshotAIChatModelId, config: MoonshotAIChatConfig) {
|
|
88
|
+
this.modelId = modelId;
|
|
89
|
+
this.config = config;
|
|
90
|
+
|
|
91
|
+
this.failedResponseHandler = createJsonErrorResponseHandler({
|
|
92
|
+
errorSchema: moonshotAIErrorSchema,
|
|
93
|
+
errorToMessage: error => error.error.message,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get provider(): string {
|
|
98
|
+
return this.config.provider;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private get providerOptionsName(): string {
|
|
102
|
+
return this.config.provider.split('.')[0].trim();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private async getArgs({
|
|
106
|
+
prompt,
|
|
107
|
+
maxOutputTokens,
|
|
108
|
+
temperature,
|
|
109
|
+
topP,
|
|
110
|
+
topK,
|
|
111
|
+
frequencyPenalty,
|
|
112
|
+
presencePenalty,
|
|
113
|
+
reasoning,
|
|
114
|
+
providerOptions,
|
|
115
|
+
stopSequences,
|
|
116
|
+
responseFormat,
|
|
117
|
+
seed,
|
|
118
|
+
toolChoice,
|
|
119
|
+
tools,
|
|
120
|
+
}: LanguageModelV4CallOptions) {
|
|
121
|
+
const moonshotOptions =
|
|
122
|
+
(await parseProviderOptions({
|
|
123
|
+
provider: this.providerOptionsName,
|
|
124
|
+
providerOptions,
|
|
125
|
+
schema: moonshotaiLanguageModelOptions,
|
|
126
|
+
})) ?? {};
|
|
127
|
+
|
|
128
|
+
const messages = convertToMoonshotAIChatMessages(prompt);
|
|
129
|
+
|
|
130
|
+
const allWarnings: SharedV4Warning[] = [];
|
|
131
|
+
if (topK != null) {
|
|
132
|
+
allWarnings.push({ type: 'unsupported', feature: 'topK' });
|
|
133
|
+
}
|
|
134
|
+
if (seed != null) {
|
|
135
|
+
allWarnings.push({ type: 'unsupported', feature: 'seed' });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const {
|
|
139
|
+
tools: moonshotTools,
|
|
140
|
+
toolChoice: moonshotToolChoice,
|
|
141
|
+
toolWarnings,
|
|
142
|
+
} = prepareTools({ tools, toolChoice });
|
|
143
|
+
|
|
144
|
+
// Thinking is configured through explicit provider options only.
|
|
145
|
+
const thinking = moonshotOptions.thinking;
|
|
146
|
+
|
|
147
|
+
// Moonshot has no reasoning_history field; the API silently ignores it
|
|
148
|
+
// (verified against the live API). Preserved Thinking maps to
|
|
149
|
+
// thinking.keep, which only accepts 'all' and only on some models
|
|
150
|
+
// (verified: k2.6, k2.7-code, k3 accept it; k2.5 rejects it). Other
|
|
151
|
+
// reasoningHistory values use the server default.
|
|
152
|
+
let keep: 'all' | undefined;
|
|
153
|
+
if (moonshotOptions.reasoningHistory === 'preserved') {
|
|
154
|
+
if (getModelThinkingKeepSupport(this.modelId)) {
|
|
155
|
+
keep = 'all';
|
|
156
|
+
} else {
|
|
157
|
+
allWarnings.push({
|
|
158
|
+
type: 'unsupported',
|
|
159
|
+
feature: `reasoningHistory 'preserved' is not supported by model "${this.modelId}"`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Map the generic reasoning call option to Moonshot's reasoning_effort
|
|
165
|
+
// (explicit provider options win). 'none' cannot disable Moonshot
|
|
166
|
+
// thinking from here; use thinking: { type: 'disabled' } instead.
|
|
167
|
+
if (reasoning === 'none') {
|
|
168
|
+
allWarnings.push({
|
|
169
|
+
type: 'unsupported',
|
|
170
|
+
feature:
|
|
171
|
+
'reasoning "none" (use providerOptions.moonshotai.thinking to control thinking)',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const reasoningEffort =
|
|
175
|
+
moonshotOptions.reasoningEffort ??
|
|
176
|
+
(isCustomReasoning(reasoning) && reasoning !== 'none'
|
|
177
|
+
? mapReasoningToProviderEffort({
|
|
178
|
+
reasoning,
|
|
179
|
+
effortMap: {
|
|
180
|
+
minimal: 'low',
|
|
181
|
+
low: 'low',
|
|
182
|
+
medium: 'high',
|
|
183
|
+
high: 'high',
|
|
184
|
+
xhigh: 'max',
|
|
185
|
+
},
|
|
186
|
+
warnings: allWarnings,
|
|
187
|
+
})
|
|
188
|
+
: undefined);
|
|
189
|
+
|
|
190
|
+
let response_format: Record<string, unknown> | undefined;
|
|
191
|
+
if (responseFormat?.type === 'json') {
|
|
192
|
+
if (
|
|
193
|
+
this.config.supportsStructuredOutputs === true &&
|
|
194
|
+
responseFormat.schema != null
|
|
195
|
+
) {
|
|
196
|
+
// kimi-k2.5 produces nonsensical output when the top-level `$schema`
|
|
197
|
+
// keyword injected by the AI SDK is present, even though it otherwise
|
|
198
|
+
// supports structured outputs. Strip it from the schema sent to
|
|
199
|
+
// Moonshot; the full original schema is still used for result
|
|
200
|
+
// validation.
|
|
201
|
+
const { $schema: _$schema, ...schemaWithoutDollarSchema } =
|
|
202
|
+
responseFormat.schema;
|
|
203
|
+
response_format = {
|
|
204
|
+
type: 'json_schema',
|
|
205
|
+
json_schema: {
|
|
206
|
+
name: responseFormat.name ?? 'response',
|
|
207
|
+
schema: schemaWithoutDollarSchema,
|
|
208
|
+
...(responseFormat.description != null && {
|
|
209
|
+
description: responseFormat.description,
|
|
210
|
+
}),
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
} else {
|
|
214
|
+
response_format = { type: 'json_object' };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
args: {
|
|
220
|
+
model: this.modelId,
|
|
221
|
+
max_tokens: maxOutputTokens,
|
|
222
|
+
temperature,
|
|
223
|
+
top_p: topP,
|
|
224
|
+
frequency_penalty: frequencyPenalty,
|
|
225
|
+
presence_penalty: presencePenalty,
|
|
226
|
+
response_format,
|
|
227
|
+
stop: stopSequences,
|
|
228
|
+
messages,
|
|
229
|
+
tools: moonshotTools,
|
|
230
|
+
tool_choice: moonshotToolChoice,
|
|
231
|
+
...(thinking != null || keep != null
|
|
232
|
+
? {
|
|
233
|
+
thinking: {
|
|
234
|
+
...(thinking?.type != null && { type: thinking.type }),
|
|
235
|
+
...(thinking?.budgetTokens !== undefined && {
|
|
236
|
+
budget_tokens: thinking.budgetTokens,
|
|
237
|
+
}),
|
|
238
|
+
...(keep != null && { keep }),
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
: {}),
|
|
242
|
+
...(reasoningEffort != null && {
|
|
243
|
+
reasoning_effort: reasoningEffort,
|
|
244
|
+
}),
|
|
245
|
+
...(moonshotOptions.promptCacheKey != null && {
|
|
246
|
+
prompt_cache_key: moonshotOptions.promptCacheKey,
|
|
247
|
+
}),
|
|
248
|
+
...(moonshotOptions.safetyIdentifier != null && {
|
|
249
|
+
safety_identifier: moonshotOptions.safetyIdentifier,
|
|
250
|
+
}),
|
|
251
|
+
},
|
|
252
|
+
warnings: [...allWarnings, ...toolWarnings],
|
|
253
|
+
};
|
|
37
254
|
}
|
|
38
255
|
|
|
39
256
|
async doGenerate(
|
|
40
257
|
options: LanguageModelV4CallOptions,
|
|
41
258
|
): Promise<LanguageModelV4GenerateResult> {
|
|
42
|
-
const
|
|
259
|
+
const { args, warnings } = await this.getArgs({ ...options });
|
|
260
|
+
|
|
261
|
+
const {
|
|
262
|
+
responseHeaders,
|
|
263
|
+
value: responseBody,
|
|
264
|
+
rawValue: rawResponse,
|
|
265
|
+
} = await postJsonToApi({
|
|
266
|
+
url: this.config.url({
|
|
267
|
+
path: '/chat/completions',
|
|
268
|
+
modelId: this.modelId,
|
|
269
|
+
}),
|
|
270
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
271
|
+
body: args,
|
|
272
|
+
failedResponseHandler: this.failedResponseHandler,
|
|
273
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
274
|
+
moonshotAIChatResponseSchema,
|
|
275
|
+
),
|
|
276
|
+
abortSignal: options.abortSignal,
|
|
277
|
+
fetch: this.config.fetch,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const choice = responseBody.choices[0];
|
|
281
|
+
const content: Array<LanguageModelV4Content> = [];
|
|
282
|
+
|
|
283
|
+
// reasoning content (before text):
|
|
284
|
+
const reasoning = choice.message.reasoning_content;
|
|
285
|
+
if (reasoning != null && reasoning.length > 0) {
|
|
286
|
+
content.push({ type: 'reasoning', text: reasoning });
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// tool calls:
|
|
290
|
+
if (choice.message.tool_calls != null) {
|
|
291
|
+
for (const toolCall of choice.message.tool_calls) {
|
|
292
|
+
content.push({
|
|
293
|
+
type: 'tool-call',
|
|
294
|
+
toolCallId: toolCall.id ?? generateId(),
|
|
295
|
+
toolName: toolCall.function.name,
|
|
296
|
+
input: toolCall.function.arguments ?? '',
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
43
300
|
|
|
44
|
-
//
|
|
45
|
-
const
|
|
301
|
+
// text content:
|
|
302
|
+
const text = choice.message.content;
|
|
303
|
+
if (text != null && text.length > 0) {
|
|
304
|
+
content.push({ type: 'text', text });
|
|
305
|
+
}
|
|
46
306
|
|
|
47
307
|
return {
|
|
48
|
-
|
|
49
|
-
|
|
308
|
+
content,
|
|
309
|
+
finishReason: {
|
|
310
|
+
unified: mapMoonshotAIFinishReason(choice.finish_reason),
|
|
311
|
+
raw: choice.finish_reason ?? undefined,
|
|
312
|
+
},
|
|
313
|
+
usage: convertMoonshotAIChatUsage(responseBody.usage),
|
|
314
|
+
request: { body: args },
|
|
315
|
+
response: {
|
|
316
|
+
...getResponseMetadata(responseBody),
|
|
317
|
+
headers: responseHeaders,
|
|
318
|
+
body: rawResponse,
|
|
319
|
+
},
|
|
320
|
+
warnings,
|
|
50
321
|
};
|
|
51
322
|
}
|
|
52
323
|
|
|
53
324
|
async doStream(
|
|
54
325
|
options: LanguageModelV4CallOptions,
|
|
55
326
|
): Promise<LanguageModelV4StreamResult> {
|
|
56
|
-
const
|
|
327
|
+
const { args, warnings } = await this.getArgs({ ...options });
|
|
328
|
+
|
|
329
|
+
const body = {
|
|
330
|
+
...args,
|
|
331
|
+
stream: true,
|
|
332
|
+
...(this.config.includeUsage && {
|
|
333
|
+
stream_options: { include_usage: true },
|
|
334
|
+
}),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const { responseHeaders, value: response } = await postJsonToApi({
|
|
338
|
+
url: this.config.url({
|
|
339
|
+
path: '/chat/completions',
|
|
340
|
+
modelId: this.modelId,
|
|
341
|
+
}),
|
|
342
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
343
|
+
body,
|
|
344
|
+
failedResponseHandler: this.failedResponseHandler,
|
|
345
|
+
successfulResponseHandler: createEventSourceResponseHandler(
|
|
346
|
+
moonshotAIChatChunkSchema,
|
|
347
|
+
),
|
|
348
|
+
abortSignal: options.abortSignal,
|
|
349
|
+
fetch: this.config.fetch,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
let toolCallTracker: StreamingToolCallTracker;
|
|
353
|
+
|
|
354
|
+
let finishReason: LanguageModelV4FinishReason = {
|
|
355
|
+
unified: 'other',
|
|
356
|
+
raw: undefined,
|
|
357
|
+
};
|
|
358
|
+
let usage: MoonshotAIChatTokenUsage | undefined = undefined;
|
|
359
|
+
let isFirstChunk = true;
|
|
360
|
+
let isActiveReasoning = false;
|
|
361
|
+
let isActiveText = false;
|
|
57
362
|
|
|
58
363
|
return {
|
|
59
|
-
|
|
60
|
-
stream: result.stream.pipeThrough(
|
|
364
|
+
stream: response.pipeThrough(
|
|
61
365
|
new TransformStream<
|
|
62
|
-
|
|
366
|
+
ParseResult<InferSchema<typeof moonshotAIChatChunkSchema>>,
|
|
63
367
|
LanguageModelV4StreamPart
|
|
64
368
|
>({
|
|
369
|
+
start(controller) {
|
|
370
|
+
toolCallTracker = new StreamingToolCallTracker(controller, {
|
|
371
|
+
generateId,
|
|
372
|
+
});
|
|
373
|
+
controller.enqueue({ type: 'stream-start', warnings });
|
|
374
|
+
},
|
|
375
|
+
|
|
65
376
|
transform(chunk, controller) {
|
|
66
|
-
|
|
377
|
+
// emit raw chunk if requested (before anything else):
|
|
378
|
+
if (options.includeRawChunks) {
|
|
379
|
+
controller.enqueue({ type: 'raw', rawValue: chunk.rawValue });
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// handle failed chunk parsing / validation:
|
|
383
|
+
if (!chunk.success) {
|
|
384
|
+
finishReason = { unified: 'error', raw: undefined };
|
|
385
|
+
controller.enqueue({ type: 'error', error: chunk.error });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const value = chunk.value;
|
|
389
|
+
|
|
390
|
+
// handle error chunks:
|
|
391
|
+
if ('error' in value) {
|
|
392
|
+
finishReason = { unified: 'error', raw: undefined };
|
|
393
|
+
controller.enqueue({ type: 'error', error: value.error.message });
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (isFirstChunk) {
|
|
398
|
+
isFirstChunk = false;
|
|
399
|
+
|
|
400
|
+
controller.enqueue({
|
|
401
|
+
type: 'response-metadata',
|
|
402
|
+
...getResponseMetadata(value),
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (value.usage != null) {
|
|
407
|
+
usage = value.usage;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const choice = value.choices[0];
|
|
411
|
+
|
|
412
|
+
if (choice?.finish_reason != null) {
|
|
413
|
+
finishReason = {
|
|
414
|
+
unified: mapMoonshotAIFinishReason(choice.finish_reason),
|
|
415
|
+
raw: choice.finish_reason,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (choice?.delta == null) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const delta = choice.delta;
|
|
424
|
+
|
|
425
|
+
// enqueue reasoning before text deltas:
|
|
426
|
+
const reasoningContent = delta.reasoning_content;
|
|
427
|
+
if (reasoningContent) {
|
|
428
|
+
if (!isActiveReasoning) {
|
|
429
|
+
controller.enqueue({
|
|
430
|
+
type: 'reasoning-start',
|
|
431
|
+
id: 'reasoning-0',
|
|
432
|
+
});
|
|
433
|
+
isActiveReasoning = true;
|
|
434
|
+
}
|
|
435
|
+
|
|
67
436
|
controller.enqueue({
|
|
68
|
-
|
|
69
|
-
|
|
437
|
+
type: 'reasoning-delta',
|
|
438
|
+
id: 'reasoning-0',
|
|
439
|
+
delta: reasoningContent,
|
|
70
440
|
});
|
|
71
|
-
} else {
|
|
72
|
-
controller.enqueue(chunk);
|
|
73
441
|
}
|
|
442
|
+
|
|
443
|
+
if (delta.content) {
|
|
444
|
+
if (!isActiveText) {
|
|
445
|
+
controller.enqueue({ type: 'text-start', id: 'txt-0' });
|
|
446
|
+
isActiveText = true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// end reasoning when text starts:
|
|
450
|
+
if (isActiveReasoning) {
|
|
451
|
+
controller.enqueue({
|
|
452
|
+
type: 'reasoning-end',
|
|
453
|
+
id: 'reasoning-0',
|
|
454
|
+
});
|
|
455
|
+
isActiveReasoning = false;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
controller.enqueue({
|
|
459
|
+
type: 'text-delta',
|
|
460
|
+
id: 'txt-0',
|
|
461
|
+
delta: delta.content,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (delta.tool_calls != null) {
|
|
466
|
+
// end reasoning when tool calls start:
|
|
467
|
+
if (isActiveReasoning) {
|
|
468
|
+
controller.enqueue({
|
|
469
|
+
type: 'reasoning-end',
|
|
470
|
+
id: 'reasoning-0',
|
|
471
|
+
});
|
|
472
|
+
isActiveReasoning = false;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
476
|
+
toolCallTracker.processDelta(toolCallDelta);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
flush(controller) {
|
|
482
|
+
if (isActiveReasoning) {
|
|
483
|
+
controller.enqueue({ type: 'reasoning-end', id: 'reasoning-0' });
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (isActiveText) {
|
|
487
|
+
controller.enqueue({ type: 'text-end', id: 'txt-0' });
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
toolCallTracker.flush();
|
|
491
|
+
|
|
492
|
+
controller.enqueue({
|
|
493
|
+
type: 'finish',
|
|
494
|
+
finishReason,
|
|
495
|
+
usage: convertMoonshotAIChatUsage(usage),
|
|
496
|
+
});
|
|
74
497
|
},
|
|
75
498
|
}),
|
|
76
499
|
),
|
|
500
|
+
request: { body },
|
|
501
|
+
response: { headers: responseHeaders },
|
|
77
502
|
};
|
|
78
503
|
}
|
|
79
504
|
}
|
|
@@ -13,9 +13,9 @@ export type MoonshotAIChatModelId =
|
|
|
13
13
|
|
|
14
14
|
export const moonshotaiLanguageModelOptions = z.object({
|
|
15
15
|
/**
|
|
16
|
-
* Reasoning effort for Kimi K3.
|
|
16
|
+
* Reasoning effort for Kimi K3.
|
|
17
17
|
*/
|
|
18
|
-
reasoningEffort: z.
|
|
18
|
+
reasoningEffort: z.enum(['low', 'high', 'max']).optional(),
|
|
19
19
|
|
|
20
20
|
thinking: z
|
|
21
21
|
.object({
|
|
@@ -25,8 +25,35 @@ export const moonshotaiLanguageModelOptions = z.object({
|
|
|
25
25
|
.optional(),
|
|
26
26
|
|
|
27
27
|
reasoningHistory: z.enum(['disabled', 'interleaved', 'preserved']).optional(),
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Used to cache responses for similar requests to optimize cache hit rates.
|
|
31
|
+
* Typically a session or task id.
|
|
32
|
+
*/
|
|
33
|
+
promptCacheKey: z.string().optional(),
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A stable identifier used to help Moonshot detect users violating usage
|
|
37
|
+
* policies. Recommended to hash the username or email address.
|
|
38
|
+
*/
|
|
39
|
+
safetyIdentifier: z.string().optional(),
|
|
28
40
|
});
|
|
29
41
|
|
|
30
42
|
export type MoonshotAILanguageModelOptions = z.infer<
|
|
31
43
|
typeof moonshotaiLanguageModelOptions
|
|
32
44
|
>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Whether the model accepts `thinking.keep` (Preserved Thinking). Verified
|
|
48
|
+
* against the live API: kimi-k2.6, kimi-k2.7-code(+highspeed), and kimi-k3
|
|
49
|
+
* accept `keep: 'all'`; other models reject it with a 400.
|
|
50
|
+
*/
|
|
51
|
+
export function getModelThinkingKeepSupport(
|
|
52
|
+
modelId: MoonshotAIChatModelId,
|
|
53
|
+
): boolean {
|
|
54
|
+
return (
|
|
55
|
+
modelId === 'kimi-k2.6' ||
|
|
56
|
+
modelId === 'kimi-k3' ||
|
|
57
|
+
modelId.startsWith('kimi-k2.7-code')
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UnsupportedFunctionalityError,
|
|
3
|
+
type LanguageModelV4CallOptions,
|
|
4
|
+
type SharedV4Warning,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
|
|
7
|
+
export function prepareTools({
|
|
8
|
+
tools,
|
|
9
|
+
toolChoice,
|
|
10
|
+
}: {
|
|
11
|
+
tools: LanguageModelV4CallOptions['tools'];
|
|
12
|
+
toolChoice?: LanguageModelV4CallOptions['toolChoice'];
|
|
13
|
+
}): {
|
|
14
|
+
tools:
|
|
15
|
+
| undefined
|
|
16
|
+
| Array<{
|
|
17
|
+
type: 'function';
|
|
18
|
+
function: {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string | undefined;
|
|
21
|
+
parameters: unknown;
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
};
|
|
24
|
+
}>;
|
|
25
|
+
toolChoice:
|
|
26
|
+
| { type: 'function'; function: { name: string } }
|
|
27
|
+
| 'auto'
|
|
28
|
+
| 'none'
|
|
29
|
+
| 'required'
|
|
30
|
+
| undefined;
|
|
31
|
+
toolWarnings: SharedV4Warning[];
|
|
32
|
+
} {
|
|
33
|
+
// when the tools array is empty, change it to undefined to prevent errors:
|
|
34
|
+
tools = tools?.length ? tools : undefined;
|
|
35
|
+
|
|
36
|
+
const toolWarnings: SharedV4Warning[] = [];
|
|
37
|
+
|
|
38
|
+
if (tools == null) {
|
|
39
|
+
return { tools: undefined, toolChoice: undefined, toolWarnings };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const moonshotTools: Array<{
|
|
43
|
+
type: 'function';
|
|
44
|
+
function: {
|
|
45
|
+
name: string;
|
|
46
|
+
description: string | undefined;
|
|
47
|
+
parameters: unknown;
|
|
48
|
+
strict?: boolean;
|
|
49
|
+
};
|
|
50
|
+
}> = [];
|
|
51
|
+
|
|
52
|
+
for (const tool of tools) {
|
|
53
|
+
if (tool.type === 'provider') {
|
|
54
|
+
toolWarnings.push({
|
|
55
|
+
type: 'unsupported',
|
|
56
|
+
feature: `provider-defined tool ${tool.id}`,
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
moonshotTools.push({
|
|
60
|
+
type: 'function',
|
|
61
|
+
function: {
|
|
62
|
+
name: tool.name,
|
|
63
|
+
description: tool.description,
|
|
64
|
+
parameters: tool.inputSchema,
|
|
65
|
+
...(tool.strict != null ? { strict: tool.strict } : {}),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (toolChoice == null) {
|
|
72
|
+
return { tools: moonshotTools, toolChoice: undefined, toolWarnings };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const type = toolChoice.type;
|
|
76
|
+
|
|
77
|
+
switch (type) {
|
|
78
|
+
case 'auto':
|
|
79
|
+
case 'none':
|
|
80
|
+
case 'required':
|
|
81
|
+
return { tools: moonshotTools, toolChoice: type, toolWarnings };
|
|
82
|
+
case 'tool':
|
|
83
|
+
return {
|
|
84
|
+
tools: moonshotTools,
|
|
85
|
+
toolChoice: {
|
|
86
|
+
type: 'function',
|
|
87
|
+
function: { name: toolChoice.toolName },
|
|
88
|
+
},
|
|
89
|
+
toolWarnings,
|
|
90
|
+
};
|
|
91
|
+
default: {
|
|
92
|
+
const _exhaustiveCheck: never = type;
|
|
93
|
+
throw new UnsupportedFunctionalityError({
|
|
94
|
+
functionality: `tool choice type: ${_exhaustiveCheck}`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|