kimi-vercel-ai-sdk-provider 0.2.0
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/LICENSE +198 -0
- package/README.md +871 -0
- package/dist/index.d.mts +1317 -0
- package/dist/index.d.ts +1317 -0
- package/dist/index.js +2764 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2734 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +70 -0
- package/src/__tests__/caching.test.ts +97 -0
- package/src/__tests__/chat.test.ts +386 -0
- package/src/__tests__/code-integration.test.ts +562 -0
- package/src/__tests__/code-provider.test.ts +289 -0
- package/src/__tests__/code.test.ts +427 -0
- package/src/__tests__/core.test.ts +172 -0
- package/src/__tests__/files.test.ts +185 -0
- package/src/__tests__/integration.test.ts +457 -0
- package/src/__tests__/provider.test.ts +188 -0
- package/src/__tests__/tools.test.ts +519 -0
- package/src/chat/index.ts +42 -0
- package/src/chat/kimi-chat-language-model.ts +829 -0
- package/src/chat/kimi-chat-messages.ts +297 -0
- package/src/chat/kimi-chat-response.ts +84 -0
- package/src/chat/kimi-chat-settings.ts +216 -0
- package/src/code/index.ts +66 -0
- package/src/code/kimi-code-language-model.ts +669 -0
- package/src/code/kimi-code-messages.ts +303 -0
- package/src/code/kimi-code-provider.ts +239 -0
- package/src/code/kimi-code-settings.ts +193 -0
- package/src/code/kimi-code-types.ts +354 -0
- package/src/core/errors.ts +140 -0
- package/src/core/index.ts +36 -0
- package/src/core/types.ts +148 -0
- package/src/core/utils.ts +210 -0
- package/src/files/attachment-processor.ts +276 -0
- package/src/files/file-utils.ts +257 -0
- package/src/files/index.ts +24 -0
- package/src/files/kimi-file-client.ts +292 -0
- package/src/index.ts +122 -0
- package/src/kimi-provider.ts +263 -0
- package/src/tools/builtin-tools.ts +273 -0
- package/src/tools/index.ts +33 -0
- package/src/tools/prepare-tools.ts +306 -0
- package/src/version.ts +4 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File utility functions for Kimi API.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Constants
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* File extensions supported by Kimi's file upload API.
|
|
12
|
+
*/
|
|
13
|
+
export const SUPPORTED_FILE_EXTENSIONS = [
|
|
14
|
+
// Documents
|
|
15
|
+
'.pdf',
|
|
16
|
+
'.txt',
|
|
17
|
+
'.csv',
|
|
18
|
+
'.doc',
|
|
19
|
+
'.docx',
|
|
20
|
+
'.xls',
|
|
21
|
+
'.xlsx',
|
|
22
|
+
'.ppt',
|
|
23
|
+
'.pptx',
|
|
24
|
+
'.md',
|
|
25
|
+
'.epub',
|
|
26
|
+
'.mobi',
|
|
27
|
+
'.html',
|
|
28
|
+
'.json',
|
|
29
|
+
'.log',
|
|
30
|
+
'.dot',
|
|
31
|
+
'.ini',
|
|
32
|
+
'.conf',
|
|
33
|
+
'.yaml',
|
|
34
|
+
'.yml',
|
|
35
|
+
// Images
|
|
36
|
+
'.jpeg',
|
|
37
|
+
'.jpg',
|
|
38
|
+
'.png',
|
|
39
|
+
'.bmp',
|
|
40
|
+
'.gif',
|
|
41
|
+
'.svg',
|
|
42
|
+
'.svgz',
|
|
43
|
+
'.webp',
|
|
44
|
+
'.ico',
|
|
45
|
+
'.xbm',
|
|
46
|
+
'.dib',
|
|
47
|
+
'.pjp',
|
|
48
|
+
'.tif',
|
|
49
|
+
'.tiff',
|
|
50
|
+
'.pjpeg',
|
|
51
|
+
'.avif',
|
|
52
|
+
'.apng',
|
|
53
|
+
'.jfif',
|
|
54
|
+
// Code files
|
|
55
|
+
'.go',
|
|
56
|
+
'.h',
|
|
57
|
+
'.c',
|
|
58
|
+
'.cpp',
|
|
59
|
+
'.cxx',
|
|
60
|
+
'.cc',
|
|
61
|
+
'.cs',
|
|
62
|
+
'.java',
|
|
63
|
+
'.js',
|
|
64
|
+
'.css',
|
|
65
|
+
'.jsp',
|
|
66
|
+
'.php',
|
|
67
|
+
'.py',
|
|
68
|
+
'.py3',
|
|
69
|
+
'.asp',
|
|
70
|
+
'.ts',
|
|
71
|
+
'.tsx'
|
|
72
|
+
] as const;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* MIME types supported by Kimi's file upload API.
|
|
76
|
+
*/
|
|
77
|
+
export const SUPPORTED_MIME_TYPES = {
|
|
78
|
+
// Documents
|
|
79
|
+
'application/pdf': 'file-extract',
|
|
80
|
+
'text/plain': 'file-extract',
|
|
81
|
+
'text/csv': 'file-extract',
|
|
82
|
+
'application/msword': 'file-extract',
|
|
83
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'file-extract',
|
|
84
|
+
'application/vnd.ms-excel': 'file-extract',
|
|
85
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'file-extract',
|
|
86
|
+
'application/vnd.ms-powerpoint': 'file-extract',
|
|
87
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'file-extract',
|
|
88
|
+
'text/markdown': 'file-extract',
|
|
89
|
+
'text/html': 'file-extract',
|
|
90
|
+
'application/json': 'file-extract',
|
|
91
|
+
'application/epub+zip': 'file-extract',
|
|
92
|
+
'text/yaml': 'file-extract',
|
|
93
|
+
'application/x-yaml': 'file-extract',
|
|
94
|
+
// Code files (treated as text)
|
|
95
|
+
'text/javascript': 'file-extract',
|
|
96
|
+
'text/typescript': 'file-extract',
|
|
97
|
+
'text/x-python': 'file-extract',
|
|
98
|
+
'text/x-java': 'file-extract',
|
|
99
|
+
'text/x-c': 'file-extract',
|
|
100
|
+
'text/x-c++': 'file-extract',
|
|
101
|
+
'text/css': 'file-extract',
|
|
102
|
+
// Images
|
|
103
|
+
'image/jpeg': 'image',
|
|
104
|
+
'image/png': 'image',
|
|
105
|
+
'image/gif': 'image',
|
|
106
|
+
'image/webp': 'image',
|
|
107
|
+
'image/svg+xml': 'image',
|
|
108
|
+
'image/bmp': 'image',
|
|
109
|
+
'image/tiff': 'image',
|
|
110
|
+
'image/avif': 'image',
|
|
111
|
+
'image/apng': 'image',
|
|
112
|
+
'image/x-icon': 'image',
|
|
113
|
+
// Videos
|
|
114
|
+
'video/mp4': 'video',
|
|
115
|
+
'video/webm': 'video',
|
|
116
|
+
'video/ogg': 'video',
|
|
117
|
+
'video/quicktime': 'video'
|
|
118
|
+
} as const;
|
|
119
|
+
|
|
120
|
+
export type SupportedMimeType = keyof typeof SUPPORTED_MIME_TYPES;
|
|
121
|
+
export type FilePurpose = 'file-extract' | 'image' | 'video';
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Type Guards
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if a media type is for image files.
|
|
129
|
+
*/
|
|
130
|
+
export function isImageMediaType(mediaType: string): boolean {
|
|
131
|
+
return mediaType.startsWith('image/');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Check if a media type is for video files.
|
|
136
|
+
*/
|
|
137
|
+
export function isVideoMediaType(mediaType: string): boolean {
|
|
138
|
+
return mediaType.startsWith('video/');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if a media type should use file-extract purpose.
|
|
143
|
+
*/
|
|
144
|
+
export function isFileExtractMediaType(mediaType: string): boolean {
|
|
145
|
+
// Check explicit types
|
|
146
|
+
if (mediaType in SUPPORTED_MIME_TYPES) {
|
|
147
|
+
return SUPPORTED_MIME_TYPES[mediaType as SupportedMimeType] === 'file-extract';
|
|
148
|
+
}
|
|
149
|
+
// Check text/* and application/* prefixes
|
|
150
|
+
if (mediaType.startsWith('text/') || mediaType === 'application/pdf') {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check if a media type is for document files (PDFs, Word, etc.).
|
|
158
|
+
*/
|
|
159
|
+
export function isDocumentMediaType(mediaType: string): boolean {
|
|
160
|
+
const documentTypes = [
|
|
161
|
+
'application/pdf',
|
|
162
|
+
'application/msword',
|
|
163
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
164
|
+
'application/vnd.ms-excel',
|
|
165
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
166
|
+
'application/vnd.ms-powerpoint',
|
|
167
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
168
|
+
'application/epub+zip'
|
|
169
|
+
];
|
|
170
|
+
return documentTypes.includes(mediaType);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// Utility Functions
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get the file purpose based on media type.
|
|
179
|
+
*/
|
|
180
|
+
export function getPurposeFromMediaType(mediaType: string): FilePurpose {
|
|
181
|
+
if (isImageMediaType(mediaType)) {
|
|
182
|
+
return 'image';
|
|
183
|
+
}
|
|
184
|
+
if (isVideoMediaType(mediaType)) {
|
|
185
|
+
return 'video';
|
|
186
|
+
}
|
|
187
|
+
return 'file-extract';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get MIME type from file extension.
|
|
192
|
+
*/
|
|
193
|
+
export function getMediaTypeFromExtension(extension: string): string {
|
|
194
|
+
const ext = extension.toLowerCase().startsWith('.') ? extension.toLowerCase() : `.${extension.toLowerCase()}`;
|
|
195
|
+
|
|
196
|
+
const extensionToMime: Record<string, string> = {
|
|
197
|
+
// Documents
|
|
198
|
+
'.pdf': 'application/pdf',
|
|
199
|
+
'.txt': 'text/plain',
|
|
200
|
+
'.csv': 'text/csv',
|
|
201
|
+
'.doc': 'application/msword',
|
|
202
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
203
|
+
'.xls': 'application/vnd.ms-excel',
|
|
204
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
205
|
+
'.ppt': 'application/vnd.ms-powerpoint',
|
|
206
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
207
|
+
'.md': 'text/markdown',
|
|
208
|
+
'.html': 'text/html',
|
|
209
|
+
'.json': 'application/json',
|
|
210
|
+
'.epub': 'application/epub+zip',
|
|
211
|
+
'.yaml': 'text/yaml',
|
|
212
|
+
'.yml': 'text/yaml',
|
|
213
|
+
'.log': 'text/plain',
|
|
214
|
+
'.ini': 'text/plain',
|
|
215
|
+
'.conf': 'text/plain',
|
|
216
|
+
// Code
|
|
217
|
+
'.js': 'text/javascript',
|
|
218
|
+
'.ts': 'text/typescript',
|
|
219
|
+
'.tsx': 'text/typescript',
|
|
220
|
+
'.py': 'text/x-python',
|
|
221
|
+
'.java': 'text/x-java',
|
|
222
|
+
'.c': 'text/x-c',
|
|
223
|
+
'.cpp': 'text/x-c++',
|
|
224
|
+
'.h': 'text/x-c',
|
|
225
|
+
'.css': 'text/css',
|
|
226
|
+
'.go': 'text/plain',
|
|
227
|
+
'.php': 'text/plain',
|
|
228
|
+
// Images
|
|
229
|
+
'.jpeg': 'image/jpeg',
|
|
230
|
+
'.jpg': 'image/jpeg',
|
|
231
|
+
'.png': 'image/png',
|
|
232
|
+
'.gif': 'image/gif',
|
|
233
|
+
'.webp': 'image/webp',
|
|
234
|
+
'.svg': 'image/svg+xml',
|
|
235
|
+
'.bmp': 'image/bmp',
|
|
236
|
+
'.tif': 'image/tiff',
|
|
237
|
+
'.tiff': 'image/tiff',
|
|
238
|
+
'.avif': 'image/avif',
|
|
239
|
+
'.apng': 'image/apng',
|
|
240
|
+
'.ico': 'image/x-icon',
|
|
241
|
+
// Videos
|
|
242
|
+
'.mp4': 'video/mp4',
|
|
243
|
+
'.webm': 'video/webm',
|
|
244
|
+
'.ogg': 'video/ogg',
|
|
245
|
+
'.mov': 'video/quicktime'
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
return extensionToMime[ext] ?? 'application/octet-stream';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Extract file extension from URL or filename.
|
|
253
|
+
*/
|
|
254
|
+
export function getExtensionFromPath(path: string): string | null {
|
|
255
|
+
const match = path.match(/\.([^./?#]+)(?:[?#]|$)/);
|
|
256
|
+
return match ? `.${match[1].toLowerCase()}` : null;
|
|
257
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File handling module for Kimi API.
|
|
3
|
+
* Provides utilities for uploading and extracting content from files.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { type Attachment, type ProcessedAttachment, processAttachments } from './attachment-processor';
|
|
8
|
+
export {
|
|
9
|
+
SUPPORTED_FILE_EXTENSIONS,
|
|
10
|
+
SUPPORTED_MIME_TYPES,
|
|
11
|
+
getMediaTypeFromExtension,
|
|
12
|
+
getPurposeFromMediaType,
|
|
13
|
+
isDocumentMediaType,
|
|
14
|
+
isFileExtractMediaType,
|
|
15
|
+
isImageMediaType,
|
|
16
|
+
isVideoMediaType
|
|
17
|
+
} from './file-utils';
|
|
18
|
+
export {
|
|
19
|
+
type FileUploadOptions,
|
|
20
|
+
type FileUploadResult,
|
|
21
|
+
type KimiFile,
|
|
22
|
+
KimiFileClient,
|
|
23
|
+
type KimiFileClientConfig
|
|
24
|
+
} from './kimi-file-client';
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi File API client for uploading and managing files.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FetchFunction } from '@ai-sdk/provider-utils';
|
|
7
|
+
import { getExtensionFromPath, getMediaTypeFromExtension, getPurposeFromMediaType } from './file-utils';
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A file object returned by the Kimi File API.
|
|
15
|
+
*/
|
|
16
|
+
export interface KimiFile {
|
|
17
|
+
/** Unique file identifier */
|
|
18
|
+
id: string;
|
|
19
|
+
/** File size in bytes */
|
|
20
|
+
bytes: number;
|
|
21
|
+
/** Unix timestamp of creation */
|
|
22
|
+
created_at: number;
|
|
23
|
+
/** Original filename */
|
|
24
|
+
filename: string;
|
|
25
|
+
/** Object type (always 'file') */
|
|
26
|
+
object: 'file';
|
|
27
|
+
/** File purpose (file-extract, image, video) */
|
|
28
|
+
purpose: 'file-extract' | 'image' | 'video';
|
|
29
|
+
/** Processing status */
|
|
30
|
+
status: 'ok' | 'error' | 'processing';
|
|
31
|
+
/** Status details if error */
|
|
32
|
+
status_details?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Options for uploading a file.
|
|
37
|
+
*/
|
|
38
|
+
export interface FileUploadOptions {
|
|
39
|
+
/** The file data as a Buffer, Uint8Array, or string (base64) */
|
|
40
|
+
data: Uint8Array | string;
|
|
41
|
+
/** The filename */
|
|
42
|
+
filename: string;
|
|
43
|
+
/** MIME type of the file */
|
|
44
|
+
mediaType?: string;
|
|
45
|
+
/** Purpose of the file (defaults based on mediaType) */
|
|
46
|
+
purpose?: 'file-extract' | 'image' | 'video';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Result of a file upload operation.
|
|
51
|
+
*/
|
|
52
|
+
export interface FileUploadResult {
|
|
53
|
+
/** The uploaded file object */
|
|
54
|
+
file: KimiFile;
|
|
55
|
+
/** The extracted content (for file-extract purpose) */
|
|
56
|
+
content?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Configuration for the file client.
|
|
61
|
+
*/
|
|
62
|
+
export interface KimiFileClientConfig {
|
|
63
|
+
/** Base URL for the API */
|
|
64
|
+
baseURL: string;
|
|
65
|
+
/** Function to get authorization headers */
|
|
66
|
+
headers: () => Record<string, string | undefined>;
|
|
67
|
+
/** Custom fetch implementation */
|
|
68
|
+
fetch?: FetchFunction;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// File Client
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Client for interacting with Kimi's File API.
|
|
77
|
+
*
|
|
78
|
+
* Supports uploading files for content extraction, image understanding,
|
|
79
|
+
* and video understanding.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const client = new KimiFileClient({
|
|
84
|
+
* baseURL: 'https://api.moonshot.ai/v1',
|
|
85
|
+
* headers: () => ({
|
|
86
|
+
* Authorization: `Bearer ${apiKey}`,
|
|
87
|
+
* }),
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* // Upload a PDF and extract content
|
|
91
|
+
* const result = await client.uploadAndExtract({
|
|
92
|
+
* data: pdfBuffer,
|
|
93
|
+
* filename: 'document.pdf',
|
|
94
|
+
* mediaType: 'application/pdf',
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* console.log(result.content); // Extracted text content
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export class KimiFileClient {
|
|
101
|
+
private readonly config: KimiFileClientConfig;
|
|
102
|
+
|
|
103
|
+
constructor(config: KimiFileClientConfig) {
|
|
104
|
+
this.config = config;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Upload a file to the Kimi API.
|
|
109
|
+
*/
|
|
110
|
+
async upload(options: FileUploadOptions): Promise<KimiFile> {
|
|
111
|
+
const { data, filename, mediaType, purpose } = options;
|
|
112
|
+
|
|
113
|
+
// Determine MIME type
|
|
114
|
+
const resolvedMediaType =
|
|
115
|
+
mediaType ?? getMediaTypeFromExtension(getExtensionFromPath(filename) ?? '') ?? 'application/octet-stream';
|
|
116
|
+
|
|
117
|
+
// Determine purpose
|
|
118
|
+
const resolvedPurpose = purpose ?? getPurposeFromMediaType(resolvedMediaType);
|
|
119
|
+
|
|
120
|
+
// Create form data
|
|
121
|
+
const formData = new FormData();
|
|
122
|
+
|
|
123
|
+
// Convert data to Blob
|
|
124
|
+
const fileData = typeof data === 'string' ? base64ToUint8Array(data) : data;
|
|
125
|
+
const blob = new Blob([new Uint8Array(fileData).buffer as ArrayBuffer], { type: resolvedMediaType });
|
|
126
|
+
|
|
127
|
+
formData.append('file', blob, filename);
|
|
128
|
+
formData.append('purpose', resolvedPurpose);
|
|
129
|
+
|
|
130
|
+
const fetchFn = this.config.fetch ?? fetch;
|
|
131
|
+
const headers = this.config.headers();
|
|
132
|
+
|
|
133
|
+
const response = await fetchFn(`${this.config.baseURL}/files`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: {
|
|
136
|
+
...Object.fromEntries(
|
|
137
|
+
Object.entries(headers).filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
138
|
+
)
|
|
139
|
+
// Don't set Content-Type - let the browser set it with boundary for FormData
|
|
140
|
+
},
|
|
141
|
+
body: formData
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
const errorBody = await response.text();
|
|
146
|
+
throw new Error(`Failed to upload file: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return (await response.json()) as KimiFile;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get the content of an uploaded file (for file-extract purpose).
|
|
154
|
+
*/
|
|
155
|
+
async getContent(fileId: string): Promise<string> {
|
|
156
|
+
const fetchFn = this.config.fetch ?? fetch;
|
|
157
|
+
const headers = this.config.headers();
|
|
158
|
+
|
|
159
|
+
const response = await fetchFn(`${this.config.baseURL}/files/${fileId}/content`, {
|
|
160
|
+
method: 'GET',
|
|
161
|
+
headers: Object.fromEntries(
|
|
162
|
+
Object.entries(headers).filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
163
|
+
)
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
const errorBody = await response.text();
|
|
168
|
+
throw new Error(`Failed to get file content: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return response.text();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Upload a file and extract its content in one operation.
|
|
176
|
+
* Only works for files with purpose="file-extract".
|
|
177
|
+
*/
|
|
178
|
+
async uploadAndExtract(options: FileUploadOptions): Promise<FileUploadResult> {
|
|
179
|
+
const file = await this.upload({
|
|
180
|
+
...options,
|
|
181
|
+
purpose: options.purpose ?? 'file-extract'
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Wait for processing if needed
|
|
185
|
+
let currentFile = file;
|
|
186
|
+
let attempts = 0;
|
|
187
|
+
const maxAttempts = 30; // 30 seconds max wait
|
|
188
|
+
const pollInterval = 1000; // 1 second
|
|
189
|
+
|
|
190
|
+
while (currentFile.status === 'processing' && attempts < maxAttempts) {
|
|
191
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
192
|
+
currentFile = await this.getFile(file.id);
|
|
193
|
+
attempts++;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (currentFile.status === 'error') {
|
|
197
|
+
throw new Error(`File processing failed: ${currentFile.status_details ?? 'Unknown error'}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (currentFile.status === 'processing') {
|
|
201
|
+
throw new Error('File processing timed out');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Get content for file-extract purpose
|
|
205
|
+
let content: string | undefined;
|
|
206
|
+
if (currentFile.purpose === 'file-extract') {
|
|
207
|
+
content = await this.getContent(file.id);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { file: currentFile, content };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get file information.
|
|
215
|
+
*/
|
|
216
|
+
async getFile(fileId: string): Promise<KimiFile> {
|
|
217
|
+
const fetchFn = this.config.fetch ?? fetch;
|
|
218
|
+
const headers = this.config.headers();
|
|
219
|
+
|
|
220
|
+
const response = await fetchFn(`${this.config.baseURL}/files/${fileId}`, {
|
|
221
|
+
method: 'GET',
|
|
222
|
+
headers: Object.fromEntries(
|
|
223
|
+
Object.entries(headers).filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
224
|
+
)
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
const errorBody = await response.text();
|
|
229
|
+
throw new Error(`Failed to get file: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return (await response.json()) as KimiFile;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* List all uploaded files.
|
|
237
|
+
*/
|
|
238
|
+
async listFiles(): Promise<KimiFile[]> {
|
|
239
|
+
const fetchFn = this.config.fetch ?? fetch;
|
|
240
|
+
const headers = this.config.headers();
|
|
241
|
+
|
|
242
|
+
const response = await fetchFn(`${this.config.baseURL}/files`, {
|
|
243
|
+
method: 'GET',
|
|
244
|
+
headers: Object.fromEntries(
|
|
245
|
+
Object.entries(headers).filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
246
|
+
)
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
const errorBody = await response.text();
|
|
251
|
+
throw new Error(`Failed to list files: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const result = (await response.json()) as { data: KimiFile[] };
|
|
255
|
+
return result.data;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Delete a file.
|
|
260
|
+
*/
|
|
261
|
+
async deleteFile(fileId: string): Promise<void> {
|
|
262
|
+
const fetchFn = this.config.fetch ?? fetch;
|
|
263
|
+
const headers = this.config.headers();
|
|
264
|
+
|
|
265
|
+
const response = await fetchFn(`${this.config.baseURL}/files/${fileId}`, {
|
|
266
|
+
method: 'DELETE',
|
|
267
|
+
headers: Object.fromEntries(
|
|
268
|
+
Object.entries(headers).filter((entry): entry is [string, string] => entry[1] !== undefined)
|
|
269
|
+
)
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
if (!response.ok) {
|
|
273
|
+
const errorBody = await response.text();
|
|
274
|
+
throw new Error(`Failed to delete file: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ============================================================================
|
|
280
|
+
// Helper Functions
|
|
281
|
+
// ============================================================================
|
|
282
|
+
|
|
283
|
+
function base64ToUint8Array(base64: string): Uint8Array {
|
|
284
|
+
// Handle data URLs
|
|
285
|
+
const base64Data = base64.includes(',') ? base64.split(',')[1] : base64;
|
|
286
|
+
const binaryString = atob(base64Data);
|
|
287
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
288
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
289
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
290
|
+
}
|
|
291
|
+
return bytes;
|
|
292
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi (Moonshot AI) Provider for Vercel AI SDK
|
|
3
|
+
*
|
|
4
|
+
* A native implementation of the Kimi AI provider for the Vercel AI SDK,
|
|
5
|
+
* supporting all Kimi-specific features including web search, code interpreter,
|
|
6
|
+
* reasoning/thinking models, and Kimi Code premium coding service.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
* @module kimi-vercel-ai-sdk-provider
|
|
10
|
+
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Kimi Provider (Standard API)
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
export type { KimiProvider, KimiProviderSettings } from './kimi-provider';
|
|
18
|
+
export { createKimi, kimi } from './kimi-provider';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Kimi Code Provider (Premium Coding API)
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
export type {
|
|
25
|
+
ExtendedThinkingConfig,
|
|
26
|
+
KimiCodeCapabilities,
|
|
27
|
+
KimiCodeModelId,
|
|
28
|
+
KimiCodeProvider,
|
|
29
|
+
KimiCodeProviderOptions,
|
|
30
|
+
KimiCodeProviderSettings,
|
|
31
|
+
KimiCodeSettings,
|
|
32
|
+
ReasoningEffort
|
|
33
|
+
} from './code';
|
|
34
|
+
export {
|
|
35
|
+
KIMI_CODE_BASE_URL,
|
|
36
|
+
KIMI_CODE_DEFAULT_MODEL,
|
|
37
|
+
KIMI_CODE_THINKING_MODEL,
|
|
38
|
+
KimiCodeLanguageModel,
|
|
39
|
+
createKimiCode,
|
|
40
|
+
inferKimiCodeCapabilities,
|
|
41
|
+
kimiCode,
|
|
42
|
+
kimiCodeProviderOptionsSchema
|
|
43
|
+
} from './code';
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Chat Model
|
|
47
|
+
// ============================================================================
|
|
48
|
+
|
|
49
|
+
export type {
|
|
50
|
+
KimiCachingConfig,
|
|
51
|
+
KimiChatModelId,
|
|
52
|
+
KimiChatSettings,
|
|
53
|
+
KimiExtendedUsage,
|
|
54
|
+
KimiModelCapabilities,
|
|
55
|
+
KimiProviderOptions
|
|
56
|
+
} from './chat';
|
|
57
|
+
export {
|
|
58
|
+
KimiChatLanguageModel,
|
|
59
|
+
inferModelCapabilities,
|
|
60
|
+
kimiCachingConfigSchema,
|
|
61
|
+
kimiProviderOptionsSchema
|
|
62
|
+
} from './chat';
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// File Handling
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
export type {
|
|
69
|
+
Attachment,
|
|
70
|
+
FileUploadOptions,
|
|
71
|
+
FileUploadResult,
|
|
72
|
+
KimiFile,
|
|
73
|
+
KimiFileClientConfig,
|
|
74
|
+
ProcessedAttachment
|
|
75
|
+
} from './files';
|
|
76
|
+
export {
|
|
77
|
+
KimiFileClient,
|
|
78
|
+
SUPPORTED_FILE_EXTENSIONS,
|
|
79
|
+
SUPPORTED_MIME_TYPES,
|
|
80
|
+
getMediaTypeFromExtension,
|
|
81
|
+
getPurposeFromMediaType,
|
|
82
|
+
isDocumentMediaType,
|
|
83
|
+
isFileExtractMediaType,
|
|
84
|
+
isImageMediaType,
|
|
85
|
+
isVideoMediaType,
|
|
86
|
+
processAttachments
|
|
87
|
+
} from './files';
|
|
88
|
+
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// Built-in Tools
|
|
91
|
+
// ============================================================================
|
|
92
|
+
|
|
93
|
+
export type {
|
|
94
|
+
KimiBuiltinTool,
|
|
95
|
+
KimiCodeInterpreterConfig,
|
|
96
|
+
KimiCodeInterpreterToolOptions,
|
|
97
|
+
KimiWebSearchConfig,
|
|
98
|
+
KimiWebSearchToolConfig,
|
|
99
|
+
KimiWebSearchToolOptions
|
|
100
|
+
} from './tools';
|
|
101
|
+
export {
|
|
102
|
+
KIMI_CODE_INTERPRETER_TOOL_NAME,
|
|
103
|
+
KIMI_WEB_SEARCH_TOOL_NAME,
|
|
104
|
+
createCodeInterpreterTool,
|
|
105
|
+
createKimiWebSearchTool,
|
|
106
|
+
createWebSearchTool,
|
|
107
|
+
kimiTools
|
|
108
|
+
} from './tools';
|
|
109
|
+
|
|
110
|
+
// ============================================================================
|
|
111
|
+
// Errors
|
|
112
|
+
// ============================================================================
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
KimiAuthenticationError,
|
|
116
|
+
KimiContentFilterError,
|
|
117
|
+
KimiContextLengthError,
|
|
118
|
+
KimiError,
|
|
119
|
+
KimiModelNotFoundError,
|
|
120
|
+
KimiRateLimitError,
|
|
121
|
+
KimiValidationError
|
|
122
|
+
} from './core';
|