kimi-vercel-ai-sdk-provider 0.2.0 → 0.4.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/dist/index.js CHANGED
@@ -77,7 +77,7 @@ var kimiErrorSchema = import_v4.z.union([
77
77
  error: import_v4.z.object({
78
78
  message: import_v4.z.string(),
79
79
  type: import_v4.z.string().nullish(),
80
- param: import_v4.z.any().nullish(),
80
+ param: import_v4.z.string().nullish(),
81
81
  code: import_v4.z.union([import_v4.z.string(), import_v4.z.number()]).nullish(),
82
82
  request_id: import_v4.z.string().nullish()
83
83
  })
@@ -144,6 +144,9 @@ var KimiContextLengthError = class extends KimiError {
144
144
  };
145
145
 
146
146
  // src/core/types.ts
147
+ var THINKING_MODEL_TEMPERATURE = 1;
148
+ var THINKING_MODEL_DEFAULT_MAX_TOKENS = 32768;
149
+ var STANDARD_MODEL_DEFAULT_MAX_TOKENS = 4096;
147
150
  function inferModelCapabilities(modelId) {
148
151
  const isThinkingModel = modelId.includes("-thinking");
149
152
  const isK25Model = modelId.includes("k2.5") || modelId.includes("k2-5");
@@ -158,7 +161,12 @@ function inferModelCapabilities(modelId) {
158
161
  // 256k context window
159
162
  toolCalling: true,
160
163
  jsonMode: true,
161
- structuredOutputs: true
164
+ structuredOutputs: true,
165
+ // Thinking models require temperature=1.0 for optimal reasoning
166
+ defaultTemperature: isThinkingModel ? THINKING_MODEL_TEMPERATURE : void 0,
167
+ temperatureLocked: isThinkingModel,
168
+ // Thinking models need higher token limits to avoid truncated reasoning
169
+ defaultMaxOutputTokens: isThinkingModel ? THINKING_MODEL_DEFAULT_MAX_TOKENS : STANDARD_MODEL_DEFAULT_MAX_TOKENS
162
170
  };
163
171
  }
164
172
 
@@ -306,24 +314,25 @@ function isBuiltinToolName(toolName) {
306
314
  }
307
315
  var kimiTools = {
308
316
  /**
309
- * Create a web search tool for use with Kimi models.
310
- *
311
- * @param config - Optional configuration
312
- * @returns A provider tool definition
313
- *
314
- * @example
315
- * ```ts
316
- * import { kimi, kimiTools } from 'ai-sdk-provider-kimi';
317
- *
318
- * const result = await generateText({
319
- * model: kimi('kimi-k2.5'),
320
- * tools: {
321
- * webSearch: kimiTools.webSearch(),
322
- * },
323
- * prompt: 'What are the latest AI news?',
324
- * });
325
- * ```
326
- */
317
+ * Create a web search tool for use with Kimi models.
318
+ *
319
+ * @param config - Optional configuration
320
+ * @returns A provider tool definition
321
+ *
322
+ * @example
323
+ * ```ts
324
+ * import { kimi, kimiTools } from 'kimi-vercel-ai-sdk-provider
325
+ ';
326
+ *
327
+ * const result = await generateText({
328
+ * model: kimi('kimi-k2.5'),
329
+ * tools: {
330
+ * webSearch: kimiTools.webSearch(),
331
+ * },
332
+ * prompt: 'What are the latest AI news?',
333
+ * });
334
+ * ```
335
+ */
327
336
  webSearch: (config) => {
328
337
  return {
329
338
  type: "provider",
@@ -332,24 +341,25 @@ var kimiTools = {
332
341
  };
333
342
  },
334
343
  /**
335
- * Create a code interpreter tool for use with Kimi models.
336
- *
337
- * @param config - Optional configuration
338
- * @returns A provider tool definition
339
- *
340
- * @example
341
- * ```ts
342
- * import { kimi, kimiTools } from 'ai-sdk-provider-kimi';
343
- *
344
- * const result = await generateText({
345
- * model: kimi('kimi-k2.5'),
346
- * tools: {
347
- * codeInterpreter: kimiTools.codeInterpreter(),
348
- * },
349
- * prompt: 'Calculate the factorial of 10',
350
- * });
351
- * ```
352
- */
344
+ * Create a code interpreter tool for use with Kimi models.
345
+ *
346
+ * @param config - Optional configuration
347
+ * @returns A provider tool definition
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * import { kimi, kimiTools } from 'kimi-vercel-ai-sdk-provider
352
+ ';
353
+ *
354
+ * const result = await generateText({
355
+ * model: kimi('kimi-k2.5'),
356
+ * tools: {
357
+ * codeInterpreter: kimiTools.codeInterpreter(),
358
+ * },
359
+ * prompt: 'Calculate the factorial of 10',
360
+ * });
361
+ * ```
362
+ */
353
363
  codeInterpreter: (config) => {
354
364
  return {
355
365
  type: "provider",
@@ -393,13 +403,15 @@ function prepareKimiTools({
393
403
  });
394
404
  continue;
395
405
  }
406
+ const sanitizedSchema = sanitizeToolSchema(tool.inputSchema);
396
407
  kimiTools2.push({
397
408
  type: "function",
398
409
  function: {
399
410
  name: tool.name,
400
411
  description: tool.description,
401
- parameters: tool.inputSchema,
402
- ...tool.strict != null ? { strict: tool.strict } : {}
412
+ parameters: sanitizedSchema
413
+ // Don't pass strict mode to Kimi - it may cause issues
414
+ // ...(tool.strict != null ? { strict: tool.strict } : {})
403
415
  }
404
416
  });
405
417
  }
@@ -473,6 +485,61 @@ function generateRequiredToolMessage(toolNames) {
473
485
  function generateSpecificToolMessage(toolName) {
474
486
  return `IMPORTANT INSTRUCTION: You MUST use the "${toolName}" tool to respond to this request. Do NOT use any other tool or provide a direct text response. Call the "${toolName}" tool with appropriate parameters.`;
475
487
  }
488
+ var UNSUPPORTED_SCHEMA_KEYWORDS = [
489
+ "$schema",
490
+ "$id",
491
+ "$ref",
492
+ "$defs",
493
+ "definitions",
494
+ "if",
495
+ "then",
496
+ "else",
497
+ "allOf",
498
+ "anyOf",
499
+ "oneOf",
500
+ "not",
501
+ "patternProperties",
502
+ "additionalItems",
503
+ "contains",
504
+ "propertyNames",
505
+ "const",
506
+ "contentMediaType",
507
+ "contentEncoding",
508
+ "examples",
509
+ "$comment"
510
+ ];
511
+ function sanitizeToolSchema(schema) {
512
+ if (schema === null || schema === void 0) {
513
+ return schema;
514
+ }
515
+ if (Array.isArray(schema)) {
516
+ return schema.map(sanitizeToolSchema);
517
+ }
518
+ if (typeof schema !== "object") {
519
+ return schema;
520
+ }
521
+ const sanitized = {};
522
+ const schemaObj = schema;
523
+ for (const [key, value] of Object.entries(schemaObj)) {
524
+ if (UNSUPPORTED_SCHEMA_KEYWORDS.includes(key)) {
525
+ continue;
526
+ }
527
+ if (key === "properties" && typeof value === "object" && value !== null) {
528
+ const props = {};
529
+ for (const [propKey, propValue] of Object.entries(value)) {
530
+ props[propKey] = sanitizeToolSchema(propValue);
531
+ }
532
+ sanitized[key] = props;
533
+ } else if (key === "items" && typeof value === "object") {
534
+ sanitized[key] = sanitizeToolSchema(value);
535
+ } else if (key === "additionalProperties" && typeof value === "object") {
536
+ sanitized[key] = sanitizeToolSchema(value);
537
+ } else {
538
+ sanitized[key] = value;
539
+ }
540
+ }
541
+ return sanitized;
542
+ }
476
543
  function tryConvertToKimiBuiltinTool(tool) {
477
544
  if (!tool.id.startsWith("kimi.")) {
478
545
  return void 0;
@@ -874,11 +941,24 @@ var KimiChatLanguageModel = class {
874
941
  if (toolChoiceSystemMessage) {
875
942
  messages.unshift({ role: "system", content: toolChoiceSystemMessage });
876
943
  }
944
+ const caps = this.capabilities;
945
+ let resolvedTemperature = temperature;
946
+ if (caps.temperatureLocked && caps.defaultTemperature !== void 0) {
947
+ if (temperature !== void 0 && temperature !== caps.defaultTemperature) {
948
+ warnings.push({
949
+ type: "compatibility",
950
+ feature: "temperature",
951
+ details: `Thinking models require temperature=${caps.defaultTemperature}. Your value (${temperature}) will be overridden.`
952
+ });
953
+ }
954
+ resolvedTemperature = caps.defaultTemperature;
955
+ }
956
+ const resolvedMaxTokens = maxOutputTokens ?? caps.defaultMaxOutputTokens;
877
957
  const body = (0, import_provider_utils3.removeUndefinedEntries)({
878
958
  model: this.modelId,
879
959
  messages,
880
- max_tokens: maxOutputTokens,
881
- temperature,
960
+ max_tokens: resolvedMaxTokens,
961
+ temperature: resolvedTemperature,
882
962
  top_p: topP,
883
963
  frequency_penalty: frequencyPenalty,
884
964
  presence_penalty: presencePenalty,
@@ -1293,6 +1373,21 @@ var kimiTokenUsageSchema = import_v43.z.object({
1293
1373
  reasoning_tokens: import_v43.z.number().nullish()
1294
1374
  }).nullish()
1295
1375
  }).nullish();
1376
+ var kimiContentPartSchema = import_v43.z.union([
1377
+ import_v43.z.object({
1378
+ type: import_v43.z.literal("text"),
1379
+ text: import_v43.z.string()
1380
+ }),
1381
+ import_v43.z.object({
1382
+ type: import_v43.z.literal("image_url"),
1383
+ image_url: import_v43.z.object({
1384
+ url: import_v43.z.string()
1385
+ })
1386
+ }),
1387
+ import_v43.z.looseObject({
1388
+ type: import_v43.z.string()
1389
+ })
1390
+ ]);
1296
1391
  var kimiChatResponseSchema = import_v43.z.looseObject({
1297
1392
  id: import_v43.z.string().nullish(),
1298
1393
  created: import_v43.z.number().nullish(),
@@ -1301,7 +1396,7 @@ var kimiChatResponseSchema = import_v43.z.looseObject({
1301
1396
  import_v43.z.object({
1302
1397
  message: import_v43.z.object({
1303
1398
  role: import_v43.z.string().nullish(),
1304
- content: import_v43.z.union([import_v43.z.string(), import_v43.z.array(import_v43.z.any())]).nullish(),
1399
+ content: import_v43.z.union([import_v43.z.string(), import_v43.z.array(kimiContentPartSchema)]).nullish(),
1305
1400
  reasoning_content: import_v43.z.string().nullish(),
1306
1401
  reasoning: import_v43.z.string().nullish(),
1307
1402
  tool_calls: import_v43.z.array(
@@ -1348,6 +1443,115 @@ var kimiChatChunkBaseSchema = import_v43.z.looseObject({
1348
1443
  });
1349
1444
  var kimiChatChunkSchema = import_v43.z.union([kimiChatChunkBaseSchema, kimiErrorSchema]);
1350
1445
 
1446
+ // src/files/file-cache.ts
1447
+ var FileCache = class {
1448
+ constructor(options = {}) {
1449
+ this.maxSize = options.maxSize ?? 100;
1450
+ this.ttlMs = options.ttlMs ?? 36e5;
1451
+ this.cache = /* @__PURE__ */ new Map();
1452
+ }
1453
+ /**
1454
+ * Get a cached entry by content hash.
1455
+ * Returns undefined if not found or expired.
1456
+ * Moves the entry to the end (most recently used).
1457
+ */
1458
+ get(contentHash) {
1459
+ const entry = this.cache.get(contentHash);
1460
+ if (!entry) {
1461
+ return void 0;
1462
+ }
1463
+ if (this.isExpired(entry)) {
1464
+ this.cache.delete(contentHash);
1465
+ return void 0;
1466
+ }
1467
+ this.cache.delete(contentHash);
1468
+ this.cache.set(contentHash, entry);
1469
+ return entry;
1470
+ }
1471
+ /**
1472
+ * Set a cache entry.
1473
+ * Evicts the least recently used entry if cache is full.
1474
+ */
1475
+ set(contentHash, entry) {
1476
+ this.cache.delete(contentHash);
1477
+ while (this.cache.size >= this.maxSize) {
1478
+ const oldestKey = this.cache.keys().next().value;
1479
+ if (oldestKey !== void 0) {
1480
+ this.cache.delete(oldestKey);
1481
+ } else {
1482
+ break;
1483
+ }
1484
+ }
1485
+ this.cache.set(contentHash, entry);
1486
+ }
1487
+ /**
1488
+ * Check if an entry exists and is not expired.
1489
+ */
1490
+ has(contentHash) {
1491
+ return this.get(contentHash) !== void 0;
1492
+ }
1493
+ /**
1494
+ * Delete a specific entry.
1495
+ */
1496
+ delete(contentHash) {
1497
+ return this.cache.delete(contentHash);
1498
+ }
1499
+ /**
1500
+ * Clear all entries.
1501
+ */
1502
+ clear() {
1503
+ this.cache.clear();
1504
+ }
1505
+ /**
1506
+ * Get the current cache size.
1507
+ */
1508
+ get size() {
1509
+ return this.cache.size;
1510
+ }
1511
+ /**
1512
+ * Remove all expired entries.
1513
+ */
1514
+ prune() {
1515
+ let pruned = 0;
1516
+ for (const [key, entry] of this.cache) {
1517
+ if (this.isExpired(entry)) {
1518
+ this.cache.delete(key);
1519
+ pruned++;
1520
+ }
1521
+ }
1522
+ return pruned;
1523
+ }
1524
+ /**
1525
+ * Check if an entry is expired.
1526
+ */
1527
+ isExpired(entry) {
1528
+ return Date.now() - entry.createdAt > this.ttlMs;
1529
+ }
1530
+ };
1531
+ function generateContentHash(data) {
1532
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
1533
+ let hash = 2166136261;
1534
+ for (let i = 0; i < bytes.length; i++) {
1535
+ hash ^= bytes[i];
1536
+ hash = Math.imul(hash, 16777619);
1537
+ }
1538
+ hash ^= bytes.length;
1539
+ return (hash >>> 0).toString(16).padStart(8, "0");
1540
+ }
1541
+ function generateCacheKey(data, filename) {
1542
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
1543
+ const contentHash = generateContentHash(data);
1544
+ const normalizedFilename = filename.toLowerCase().replace(/[^a-z0-9.]/g, "_");
1545
+ return `${contentHash}_${bytes.length}_${normalizedFilename}`;
1546
+ }
1547
+ var defaultCache = null;
1548
+ function getDefaultFileCache() {
1549
+ if (!defaultCache) {
1550
+ defaultCache = new FileCache();
1551
+ }
1552
+ return defaultCache;
1553
+ }
1554
+
1351
1555
  // src/files/file-utils.ts
1352
1556
  var SUPPORTED_FILE_EXTENSIONS = [
1353
1557
  // Documents
@@ -1703,8 +1907,10 @@ async function processAttachments(options) {
1703
1907
  clientConfig,
1704
1908
  autoUploadDocuments = true,
1705
1909
  uploadImages = false,
1706
- cleanupAfterExtract = false
1910
+ cleanupAfterExtract = false,
1911
+ cache = false
1707
1912
  } = options;
1913
+ const cacheInstance = cache === true ? getDefaultFileCache() : cache === false ? null : cache;
1708
1914
  const results = [];
1709
1915
  const client = new KimiFileClient(clientConfig);
1710
1916
  for (const attachment of attachments) {
@@ -1712,7 +1918,8 @@ async function processAttachments(options) {
1712
1918
  const processed = await processAttachment(attachment, client, {
1713
1919
  autoUploadDocuments,
1714
1920
  uploadImages,
1715
- cleanupAfterExtract
1921
+ cleanupAfterExtract,
1922
+ cache: cacheInstance
1716
1923
  });
1717
1924
  results.push(processed);
1718
1925
  } catch (error) {
@@ -1772,12 +1979,35 @@ async function processAttachment(attachment, client, options) {
1772
1979
  error: "No content or URL provided for document attachment"
1773
1980
  };
1774
1981
  }
1982
+ const filename = attachment.name ?? guessFilename(attachment, contentType);
1983
+ if (options.cache) {
1984
+ const cacheKey = generateCacheKey(data, filename);
1985
+ const cached = options.cache.get(cacheKey);
1986
+ if (cached) {
1987
+ return {
1988
+ original: attachment,
1989
+ type: "text-inject",
1990
+ textContent: cached.content,
1991
+ fileId: cached.fileId
1992
+ };
1993
+ }
1994
+ }
1775
1995
  const result = await client.uploadAndExtract({
1776
1996
  data,
1777
- filename: attachment.name ?? guessFilename(attachment, contentType),
1997
+ filename,
1778
1998
  mediaType: contentType,
1779
1999
  purpose: "file-extract"
1780
2000
  });
2001
+ if (options.cache && result.content) {
2002
+ const cacheKey = generateCacheKey(data, filename);
2003
+ const cacheEntry = {
2004
+ fileId: result.file.id,
2005
+ content: result.content,
2006
+ createdAt: Date.now(),
2007
+ purpose: "file-extract"
2008
+ };
2009
+ options.cache.set(cacheKey, cacheEntry);
2010
+ }
1781
2011
  if (options.cleanupAfterExtract && result.file.id) {
1782
2012
  try {
1783
2013
  await client.deleteFile(result.file.id);
@@ -1818,7 +2048,7 @@ function guessFilename(attachment, contentType) {
1818
2048
  const urlPath = attachment.url.split("?")[0];
1819
2049
  const segments = urlPath.split("/");
1820
2050
  const lastSegment = segments[segments.length - 1];
1821
- if (lastSegment && lastSegment.includes(".")) {
2051
+ if (lastSegment.includes(".")) {
1822
2052
  return lastSegment;
1823
2053
  }
1824
2054
  }
@@ -1834,7 +2064,7 @@ function guessFilename(attachment, contentType) {
1834
2064
  }
1835
2065
 
1836
2066
  // src/version.ts
1837
- var VERSION = "0.2.0".length > 0 ? "0.2.0" : "0.0.0";
2067
+ var VERSION = "0.4.0".length > 0 ? "0.4.0" : "0.0.0";
1838
2068
 
1839
2069
  // src/kimi-provider.ts
1840
2070
  var GLOBAL_BASE_URL = "https://api.moonshot.ai/v1";