@ai-sdk/moonshotai 2.0.49 → 2.0.53

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.
@@ -1,6 +1,9 @@
1
1
  import {
2
+ InvalidArgumentError,
3
+ InvalidPromptError,
2
4
  UnsupportedFunctionalityError,
3
5
  type LanguageModelV3Prompt,
6
+ type SharedV3Warning,
4
7
  } from '@ai-sdk/provider';
5
8
  import {
6
9
  convertBase64ToUint8Array,
@@ -8,18 +11,165 @@ import {
8
11
  } from '@ai-sdk/provider-utils';
9
12
 
10
13
  import type { MoonshotAIMessages } from './moonshotai-chat-api-types';
14
+ import {
15
+ getMoonshotAIModelFamily,
16
+ moonshotaiAllMessageProviderOptions,
17
+ type MoonshotAIChatModelId,
18
+ } from './moonshotai-chat-options';
19
+ import { prepareTools } from './moonshotai-prepare-tools';
20
+
21
+ function parseMoonshotAIMessageProviderOptions({
22
+ providerOptions,
23
+ providerOptionsName,
24
+ }: {
25
+ providerOptions: Record<string, unknown> | undefined;
26
+ providerOptionsName: string;
27
+ }) {
28
+ const value = providerOptions?.[providerOptionsName];
29
+ if (value == null) {
30
+ return undefined;
31
+ }
32
+
33
+ const result = moonshotaiAllMessageProviderOptions.safeParse(value);
34
+ if (!result.success) {
35
+ throw new InvalidArgumentError({
36
+ argument: 'providerOptions',
37
+ message: `invalid ${providerOptionsName} provider options`,
38
+ cause: result.error,
39
+ });
40
+ }
41
+
42
+ return result.data;
43
+ }
44
+
45
+ const supportedImageMediaTypes = [
46
+ 'image/jpeg',
47
+ 'image/png',
48
+ 'image/gif',
49
+ 'image/webp',
50
+ 'image/bmp',
51
+ 'image/heic',
52
+ 'image/heif',
53
+ ] as const;
54
+
55
+ const supportedVideoMediaTypes = [
56
+ 'video/mp4',
57
+ 'video/mpeg',
58
+ 'video/mov',
59
+ 'video/avi',
60
+ 'video/x-flv',
61
+ 'video/mpg',
62
+ 'video/webm',
63
+ 'video/wmv',
64
+ 'video/3gpp',
65
+ ] as const;
66
+
67
+ function validateMediaType({
68
+ mediaType,
69
+ supportedMediaTypes,
70
+ topLevelMediaType,
71
+ }: {
72
+ mediaType: string;
73
+ supportedMediaTypes: readonly string[];
74
+ topLevelMediaType: 'image' | 'video';
75
+ }): string {
76
+ const normalizedMediaType =
77
+ mediaType === `${topLevelMediaType}/*`
78
+ ? topLevelMediaType === 'image'
79
+ ? 'image/jpeg'
80
+ : 'video/mp4'
81
+ : mediaType;
82
+
83
+ if (!supportedMediaTypes.includes(normalizedMediaType)) {
84
+ throw new UnsupportedFunctionalityError({
85
+ functionality: `file part media type ${normalizedMediaType}`,
86
+ });
87
+ }
88
+
89
+ return normalizedMediaType;
90
+ }
11
91
 
12
92
  // Moonshot AI chat completions accepts text, image_url, and video_url content
13
93
  // parts only. Anything else (audio, PDF, other file types) throws here rather
14
94
  // than being rejected by the API with a 400.
15
- export function convertToMoonshotAIChatMessages(
16
- prompt: LanguageModelV3Prompt,
17
- ): MoonshotAIMessages {
95
+ export function convertToMoonshotAIChatMessages({
96
+ modelId,
97
+ prompt,
98
+ providerOptionsName = 'moonshotai',
99
+ responseFormat,
100
+ }: {
101
+ modelId?: MoonshotAIChatModelId;
102
+ prompt: LanguageModelV3Prompt;
103
+ providerOptionsName?: string;
104
+ responseFormat?: Record<string, unknown>;
105
+ }): {
106
+ messages: MoonshotAIMessages;
107
+ warnings: Array<SharedV3Warning>;
108
+ } {
18
109
  const messages: MoonshotAIMessages = [];
19
- for (const { role, content } of prompt) {
110
+ const warnings: Array<SharedV3Warning> = [];
111
+ const modelFamily =
112
+ modelId == null ? 'unknown' : getMoonshotAIModelFamily(modelId);
113
+
114
+ for (const [index, { role, content, providerOptions }] of prompt.entries()) {
115
+ const moonshotMessageOptions = parseMoonshotAIMessageProviderOptions({
116
+ providerOptions,
117
+ providerOptionsName,
118
+ });
119
+
120
+ if (moonshotMessageOptions?.partial === true && role !== 'assistant') {
121
+ throw new InvalidPromptError({
122
+ prompt,
123
+ message:
124
+ 'Moonshot AI Partial Mode requires `partial: true` on an assistant message.',
125
+ });
126
+ }
127
+
128
+ if (moonshotMessageOptions?.tools != null && role !== 'system') {
129
+ throw new InvalidPromptError({
130
+ prompt,
131
+ message:
132
+ 'Moonshot dynamic tools must be configured on a system message.',
133
+ });
134
+ }
135
+
20
136
  switch (role) {
21
137
  case 'system': {
22
- messages.push({ role: 'system', content });
138
+ if (moonshotMessageOptions?.tools?.length) {
139
+ if (content.length > 0) {
140
+ throw new InvalidPromptError({
141
+ prompt,
142
+ message:
143
+ 'A Moonshot dynamic-tool system message must use empty content because the API forbids content alongside tools.',
144
+ });
145
+ }
146
+
147
+ if (modelFamily !== 'kimi-k3' && modelFamily !== 'unknown') {
148
+ warnings.push({
149
+ type: 'unsupported',
150
+ feature: `dynamic tool loading for model "${modelId}"`,
151
+ details:
152
+ 'Moonshot documents dynamic tool loading only for Kimi K3. The dynamic system message has been omitted.',
153
+ });
154
+ break;
155
+ }
156
+
157
+ const { tools, toolWarnings } = prepareTools({
158
+ modelId: modelId ?? 'custom-model',
159
+ tools: moonshotMessageOptions.tools,
160
+ });
161
+ warnings.push(...toolWarnings);
162
+ messages.push({ role: 'system', tools: tools ?? [] });
163
+ break;
164
+ }
165
+
166
+ messages.push({
167
+ role: 'system',
168
+ content,
169
+ ...(moonshotMessageOptions?.name != null && {
170
+ name: moonshotMessageOptions.name,
171
+ }),
172
+ });
23
173
  break;
24
174
  }
25
175
 
@@ -28,6 +178,9 @@ export function convertToMoonshotAIChatMessages(
28
178
  messages.push({
29
179
  role: 'user',
30
180
  content: content[0].text,
181
+ ...(moonshotMessageOptions?.name != null && {
182
+ name: moonshotMessageOptions.name,
183
+ }),
31
184
  });
32
185
  break;
33
186
  }
@@ -41,10 +194,11 @@ export function convertToMoonshotAIChatMessages(
41
194
  }
42
195
  case 'file': {
43
196
  if (part.mediaType.startsWith('image/')) {
44
- const mediaType =
45
- part.mediaType === 'image/*'
46
- ? 'image/jpeg'
47
- : part.mediaType;
197
+ const mediaType = validateMediaType({
198
+ mediaType: part.mediaType,
199
+ supportedMediaTypes: supportedImageMediaTypes,
200
+ topLevelMediaType: 'image',
201
+ });
48
202
 
49
203
  return {
50
204
  type: 'image_url' as const,
@@ -58,8 +212,11 @@ export function convertToMoonshotAIChatMessages(
58
212
  }
59
213
 
60
214
  if (part.mediaType.startsWith('video/')) {
61
- const mediaType =
62
- part.mediaType === 'video/*' ? 'video/mp4' : part.mediaType;
215
+ const mediaType = validateMediaType({
216
+ mediaType: part.mediaType,
217
+ supportedMediaTypes: supportedVideoMediaTypes,
218
+ topLevelMediaType: 'video',
219
+ });
63
220
 
64
221
  return {
65
222
  type: 'video_url' as const,
@@ -94,12 +251,33 @@ export function convertToMoonshotAIChatMessages(
94
251
  }
95
252
  }
96
253
  }),
254
+ ...(moonshotMessageOptions?.name != null && {
255
+ name: moonshotMessageOptions.name,
256
+ }),
97
257
  });
98
258
 
99
259
  break;
100
260
  }
101
261
 
102
262
  case 'assistant': {
263
+ if (moonshotMessageOptions?.partial === true) {
264
+ if (index !== prompt.length - 1) {
265
+ throw new InvalidPromptError({
266
+ prompt,
267
+ message:
268
+ 'Moonshot AI Partial Mode requires the partial assistant message to be the final message.',
269
+ });
270
+ }
271
+
272
+ if (responseFormat?.type === 'json_object') {
273
+ throw new InvalidPromptError({
274
+ prompt,
275
+ message:
276
+ 'Moonshot AI Partial Mode cannot be combined with JSON object response format.',
277
+ });
278
+ }
279
+ }
280
+
103
281
  let text = '';
104
282
  let reasoning = '';
105
283
  const toolCalls: Array<{
@@ -135,6 +313,12 @@ export function convertToMoonshotAIChatMessages(
135
313
  messages.push({
136
314
  role: 'assistant',
137
315
  content: toolCalls.length > 0 ? text || null : text,
316
+ ...(moonshotMessageOptions?.name != null && {
317
+ name: moonshotMessageOptions.name,
318
+ }),
319
+ ...(moonshotMessageOptions?.partial === true && {
320
+ partial: true,
321
+ }),
138
322
  ...(reasoning.length > 0 ? { reasoning_content: reasoning } : {}),
139
323
  tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
140
324
  });
@@ -143,6 +327,13 @@ export function convertToMoonshotAIChatMessages(
143
327
  }
144
328
 
145
329
  case 'tool': {
330
+ if (moonshotMessageOptions?.name != null) {
331
+ warnings.push({
332
+ type: 'unsupported',
333
+ feature: 'message name on tool messages',
334
+ });
335
+ }
336
+
146
337
  for (const toolResponse of content) {
147
338
  if (toolResponse.type === 'tool-approval-response') {
148
339
  continue;
@@ -182,5 +373,5 @@ export function convertToMoonshotAIChatMessages(
182
373
  }
183
374
  }
184
375
 
185
- return messages;
376
+ return { messages, warnings };
186
377
  }
package/src/index.ts CHANGED
@@ -4,8 +4,11 @@ export type {
4
4
  MoonshotAIProviderSettings,
5
5
  } from './moonshotai-provider';
6
6
  export type {
7
+ MoonshotAIAssistantMessageProviderOptions,
7
8
  MoonshotAIChatModelId,
8
9
  MoonshotAILanguageModelOptions,
10
+ MoonshotAIMessageProviderOptions,
11
+ MoonshotAISystemMessageProviderOptions,
9
12
  /** @deprecated Use `MoonshotAILanguageModelOptions` instead. */
10
13
  MoonshotAILanguageModelOptions as MoonshotAIProviderOptions,
11
14
  } from './moonshotai-chat-options';
@@ -9,14 +9,31 @@ export type MoonshotAIMessage =
9
9
  | MoonshotAIAssistantMessage
10
10
  | MoonshotAIToolMessage;
11
11
 
12
- export interface MoonshotAISystemMessage {
13
- role: 'system';
14
- content: string;
12
+ export type MoonshotAISystemMessage =
13
+ | {
14
+ role: 'system';
15
+ content: string;
16
+ name?: string;
17
+ }
18
+ | {
19
+ role: 'system';
20
+ tools: Array<MoonshotAIFunctionTool>;
21
+ };
22
+
23
+ export interface MoonshotAIFunctionTool {
24
+ type: 'function';
25
+ function: {
26
+ name: string;
27
+ description: string | undefined;
28
+ parameters: unknown;
29
+ strict?: boolean;
30
+ };
15
31
  }
16
32
 
17
33
  export interface MoonshotAIUserMessage {
18
34
  role: 'user';
19
35
  content: string | Array<MoonshotAIContentPart>;
36
+ name?: string;
20
37
  }
21
38
 
22
39
  export type MoonshotAIContentPart =
@@ -42,6 +59,8 @@ export interface MoonshotAIContentPartVideo {
42
59
  export interface MoonshotAIAssistantMessage {
43
60
  role: 'assistant';
44
61
  content?: string | null;
62
+ name?: string;
63
+ partial?: true;
45
64
  reasoning_content?: string;
46
65
  tool_calls?: Array<MoonshotAIMessageToolCall>;
47
66
  }
@@ -64,19 +83,20 @@ export interface MoonshotAIToolMessage {
64
83
  // Schemas below are limited versions focused on what the implementation
65
84
  // needs. This limits breakages when the API changes and increases efficiency.
66
85
 
86
+ // Loose, nested objects included: the parsed value is returned as `usage.raw`.
67
87
  const tokenUsageSchema = z
68
- .object({
88
+ .looseObject({
69
89
  prompt_tokens: z.number().nullish(),
70
90
  completion_tokens: z.number().nullish(),
71
91
  cached_tokens: z.number().nullish(),
72
92
  total_tokens: z.number().nullish(),
73
93
  prompt_tokens_details: z
74
- .object({
94
+ .looseObject({
75
95
  cached_tokens: z.number().nullish(),
76
96
  })
77
97
  .nullish(),
78
98
  completion_tokens_details: z
79
- .object({
99
+ .looseObject({
80
100
  reasoning_tokens: z.number().nullish(),
81
101
  })
82
102
  .nullish(),
@@ -89,17 +109,41 @@ export const moonshotAIErrorSchema = z.object({
89
109
  error: z.object({
90
110
  message: z.string(),
91
111
  type: z.string().nullish(),
112
+ code: z.string().nullish(),
92
113
  }),
93
114
  });
94
115
 
95
116
  export type MoonshotAIErrorData = z.infer<typeof moonshotAIErrorSchema>;
96
117
 
118
+ const moonshotAIChatLogprobSchema = z.object({
119
+ token: z.string(),
120
+ logprob: z.number(),
121
+ bytes: z.array(z.number()).nullable(),
122
+ top_logprobs: z.array(
123
+ z.object({
124
+ token: z.string(),
125
+ logprob: z.number(),
126
+ bytes: z.array(z.number()).nullable(),
127
+ }),
128
+ ),
129
+ });
130
+
131
+ const moonshotAIChatLogprobsSchema = z
132
+ .object({
133
+ content: z.array(moonshotAIChatLogprobSchema).nullish(),
134
+ })
135
+ .nullish();
136
+
137
+ export type MoonshotAIChatLogprob = z.infer<typeof moonshotAIChatLogprobSchema>;
138
+
97
139
  export const moonshotAIChatResponseSchema = z.object({
98
140
  id: z.string().nullish(),
99
141
  created: z.number().nullish(),
100
142
  model: z.string().nullish(),
143
+ object: z.literal('chat.completion').nullish(),
101
144
  choices: z.array(
102
145
  z.object({
146
+ index: z.number().nullish(),
103
147
  message: z.object({
104
148
  role: z.literal('assistant').nullish(),
105
149
  content: z.string().nullish(),
@@ -108,6 +152,7 @@ export const moonshotAIChatResponseSchema = z.object({
108
152
  .array(
109
153
  z.object({
110
154
  id: z.string().nullish(),
155
+ type: z.literal('function').nullish(),
111
156
  function: z.object({
112
157
  name: z.string(),
113
158
  arguments: z.string(),
@@ -116,6 +161,7 @@ export const moonshotAIChatResponseSchema = z.object({
116
161
  )
117
162
  .nullish(),
118
163
  }),
164
+ logprobs: moonshotAIChatLogprobsSchema,
119
165
  finish_reason: z.string().nullish(),
120
166
  }),
121
167
  ),
@@ -129,8 +175,10 @@ export const moonshotAIChatChunkSchema = lazySchema(() =>
129
175
  id: z.string().nullish(),
130
176
  created: z.number().nullish(),
131
177
  model: z.string().nullish(),
178
+ object: z.literal('chat.completion.chunk').nullish(),
132
179
  choices: z.array(
133
180
  z.object({
181
+ index: z.number().nullish(),
134
182
  delta: z
135
183
  .object({
136
184
  role: z.literal('assistant').nullish(),
@@ -139,8 +187,9 @@ export const moonshotAIChatChunkSchema = lazySchema(() =>
139
187
  tool_calls: z
140
188
  .array(
141
189
  z.object({
142
- index: z.number(),
190
+ index: z.number().nullish(),
143
191
  id: z.string().nullish(),
192
+ type: z.literal('function').nullish(),
144
193
  function: z.object({
145
194
  name: z.string().nullish(),
146
195
  arguments: z.string().nullish(),
@@ -150,7 +199,9 @@ export const moonshotAIChatChunkSchema = lazySchema(() =>
150
199
  .nullish(),
151
200
  })
152
201
  .nullish(),
202
+ logprobs: moonshotAIChatLogprobsSchema,
153
203
  finish_reason: z.string().nullish(),
204
+ usage: tokenUsageSchema,
154
205
  }),
155
206
  ),
156
207
  usage: tokenUsageSchema,