@warlock.js/ai-openai 5.31.0 → 5.32.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/cjs/index.cjs +304 -8
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/config.type.d.mts +8 -10
  5. package/esm/config.type.d.mts.map +1 -1
  6. package/esm/embedder.d.mts +1 -3
  7. package/esm/embedder.d.mts.map +1 -1
  8. package/esm/image.d.mts +1 -3
  9. package/esm/image.d.mts.map +1 -1
  10. package/esm/index.d.mts +4 -3
  11. package/esm/index.mjs +3 -2
  12. package/esm/kimi-tools.d.mts +69 -0
  13. package/esm/kimi-tools.d.mts.map +1 -0
  14. package/esm/kimi-tools.mjs +256 -0
  15. package/esm/kimi-tools.mjs.map +1 -0
  16. package/esm/known-pricing.d.mts +8 -4
  17. package/esm/known-pricing.d.mts.map +1 -1
  18. package/esm/known-pricing.mjs +5 -1
  19. package/esm/known-pricing.mjs.map +1 -1
  20. package/esm/model.mjs +8 -1
  21. package/esm/model.mjs.map +1 -1
  22. package/esm/provider-options.d.mts +7 -2
  23. package/esm/provider-options.d.mts.map +1 -1
  24. package/esm/provider-options.mjs +9 -2
  25. package/esm/provider-options.mjs.map +1 -1
  26. package/esm/responses-model.mjs +11 -4
  27. package/esm/responses-model.mjs.map +1 -1
  28. package/esm/sdk.d.mts +1 -3
  29. package/esm/sdk.d.mts.map +1 -1
  30. package/esm/speech.d.mts +1 -3
  31. package/esm/speech.d.mts.map +1 -1
  32. package/esm/transcription.d.mts +1 -3
  33. package/esm/transcription.d.mts.map +1 -1
  34. package/esm/utils/hosted-output.mjs +22 -0
  35. package/esm/utils/hosted-output.mjs.map +1 -0
  36. package/esm/utils/index.mjs +1 -1
  37. package/esm/utils/prompt-cache-params.mjs +1 -1
  38. package/esm/utils/prompt-cache-params.mjs.map +1 -1
  39. package/esm/utils/reasoning-replay.mjs +1 -1
  40. package/esm/utils/reasoning-replay.mjs.map +1 -1
  41. package/esm/utils/structured-output.mjs +1 -1
  42. package/esm/utils/to-openai-messages.mjs.map +1 -1
  43. package/llms-full.txt +108 -1
  44. package/llms.txt +1 -1
  45. package/package.json +3 -3
  46. package/skills/setup-openai/SKILL.md +108 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ All notable changes to `@warlock.js/ai-openai` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.32.0 - 2026-10-18
8
+
9
+ ### Changed
10
+
11
+ - Remove the unreleased Kimi/Moonshot `$web_search` builtin echo path; reject declarations of the builtin retiring on 20 October 2026 and document migration to standalone REST tools.
12
+
13
+ - Pin the package development compiler to TypeScript 7.0.2 and use explicit Node types and source roots with supported module resolution.
14
+
15
+ ### Fixed
16
+
17
+ - Verified that Responses native token counting preserves the shared unsupported-count signal for `countInputTokens` estimation while authentication errors propagate.
18
+
19
+ ### Added
20
+
21
+ - Export `kimiSearchTool()` (Basic/Pro), `kimiFetchTool()` and verified `KIMI_SERVER_TOOL_PRICING`: authenticated, cancellable first-party REST calls with compact results, mapped errors, no automatic retries, and billable response usage/costs aggregated into agent reports.
22
+
23
+ - Retain Responses hosted output/actions/sources and count web-search calls without double-counting stream snapshots. Export opt-in `OPENAI_SERVER_TOOL_PRICING`. Support typed Qwen/DashScope `enableSearch` flags.
24
+
7
25
  ## 5.31.0 - 2026-10-18
8
26
 
9
27
  ### Fixed
package/cjs/index.cjs CHANGED
@@ -20,7 +20,7 @@ var __copyProps = (to, from, except, desc) => {
20
20
  }
21
21
  return to;
22
22
  };
23
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
24
24
  value: mod,
25
25
  enumerable: true
26
26
  }) : target, mod));
@@ -144,6 +144,10 @@ const OPENAI_MODEL_PRICING = {
144
144
  function openAIPricingFor(modelName) {
145
145
  return OPENAI_MODEL_PRICING[modelName];
146
146
  }
147
+ /** USD per request for web_search (NOT non-reasoning web_search_preview).
148
+ * Search content tokens are additional; preview variants can have other rates.
149
+ * https://developers.openai.com/api/docs/pricing#built-in-tools — verified 2026-10-18. */
150
+ const OPENAI_SERVER_TOOL_PRICING = { webSearch: .01 };
147
151
 
148
152
  //#endregion
149
153
  //#region ../../packages/ai-openai/src/utils/build-usage.ts
@@ -413,7 +417,7 @@ function mapFinishReason(raw) {
413
417
  * Groq and DeepSeek cache automatically and document no request hint, so
414
418
  * they receive nothing (checked 2026-10-17).
415
419
  */
416
- const PROMPT_CACHE_KEY_PROVIDERS = new Set([
420
+ const PROMPT_CACHE_KEY_PROVIDERS = /* @__PURE__ */ new Set([
417
421
  "openai",
418
422
  "xai",
419
423
  "mistral"
@@ -565,7 +569,7 @@ function pickReplayableReasoning(items) {
565
569
  return items.filter((item) => typeof item.encrypted_content === "string" && item.encrypted_content !== "");
566
570
  }
567
571
  /** Output item types that may follow a replayed reasoning item. */
568
- const REPLAY_SAFE_FOLLOWERS = new Set(["function_call", "message"]);
572
+ const REPLAY_SAFE_FOLLOWERS = /* @__PURE__ */ new Set(["function_call", "message"]);
569
573
  /**
570
574
  * The reasoning items of a turn that are safe to replay, in order.
571
575
  *
@@ -1238,7 +1242,7 @@ var OpenAIImageModel = class {
1238
1242
 
1239
1243
  //#endregion
1240
1244
  //#region ../../packages/ai-openai/src/provider-options.ts
1241
- const RESERVED_FIELDS = new Set([
1245
+ const RESERVED_FIELDS = /* @__PURE__ */ new Set([
1242
1246
  "model",
1243
1247
  "messages",
1244
1248
  "input",
@@ -1280,6 +1284,13 @@ function isPlainObject(value) {
1280
1284
  const prototype = Object.getPrototypeOf(value);
1281
1285
  return prototype === Object.prototype || prototype === null;
1282
1286
  }
1287
+ function compatibleSearchParams(provider, options) {
1288
+ if (provider !== "qwen" && provider !== "dashscope") return {};
1289
+ const value = options?.providerOptions?.[provider]?.enableSearch;
1290
+ if (value === void 0) return {};
1291
+ if (typeof value !== "boolean") throw new _warlock_js_ai.InvalidRequestError(`providerOptions.${provider}.enableSearch must be a boolean`);
1292
+ return { enable_search: value };
1293
+ }
1283
1294
 
1284
1295
  //#endregion
1285
1296
  //#region ../../packages/ai-openai/src/known-reasoning-models.ts
@@ -1445,6 +1456,7 @@ var OpenAIModel = class {
1445
1456
  try {
1446
1457
  response = await this.client.chat.completions.create(mergeProviderBody({
1447
1458
  model: this.name,
1459
+ ...compatibleSearchParams(this.provider, options),
1448
1460
  messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),
1449
1461
  ...this.buildSamplingParams(options),
1450
1462
  ...buildPromptCacheParams(this.provider, this.config),
@@ -1501,6 +1513,7 @@ var OpenAIModel = class {
1501
1513
  try {
1502
1514
  stream = await this.client.chat.completions.create(mergeProviderBody({
1503
1515
  model: this.name,
1516
+ ...compatibleSearchParams(this.provider, options),
1504
1517
  messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),
1505
1518
  ...this.buildSamplingParams(options),
1506
1519
  ...buildPromptCacheParams(this.provider, this.config),
@@ -1610,6 +1623,11 @@ var OpenAIModel = class {
1610
1623
  model: this.name,
1611
1624
  logger: this.logger
1612
1625
  });
1626
+ if ((this.provider === "moonshot" || this.provider === "kimi") && definitions.some((definition) => {
1627
+ const fn = definition.function;
1628
+ return definition.type === "builtin_function" && typeof fn === "object" && fn !== null && "name" in fn && fn.name === "$web_search";
1629
+ })) throw new _warlock_js_ai.InvalidRequestError("Kimi's builtin $web_search retires on 20 October 2026. Use kimiSearchTool() / kimiFetchTool() from @warlock.js/ai-openai.");
1630
+ if (this.provider === "xai" && definitions.some((definition) => definition.type === "web_search" || definition.type === "x_search")) throw new _warlock_js_ai.InvalidRequestError("xAI native web_search / x_search requires POST /v1/responses. XaiSDK currently supports Chat Completions only; use xAI's Responses API with the vendor SDK for native search.");
1613
1631
  const mapped = [...toOpenAITools(tools) ?? [], ...definitions];
1614
1632
  return mapped.length > 0 ? { tools: mapped } : {};
1615
1633
  }
@@ -1759,6 +1777,24 @@ function readReasoningDelta(delta) {
1759
1777
  }
1760
1778
  }
1761
1779
 
1780
+ //#endregion
1781
+ //#region ../../packages/ai-openai/src/utils/hosted-output.ts
1782
+ /** Installed openai 7.23.0 ResponseFunctionWebSearch (action includes search/open_page/find).
1783
+ * https://developers.openai.com/api/docs/guides/tools-web-search
1784
+ * Count output items, never URLs, sources, answer text, or function calls.
1785
+ * Fees and search content tokens are separate; preview pricing varies:
1786
+ * https://developers.openai.com/api/docs/pricing#built-in-tools */
1787
+ function hostedOutput(usage, output, pricing) {
1788
+ const hosted = output.filter((item) => item.type !== "message" && item.type !== "reasoning" && item.type !== "function_call");
1789
+ const searches = hosted.filter((item) => item.type === "web_search_call");
1790
+ if (searches.length) usage.serverToolUse = {
1791
+ ...usage.serverToolUse,
1792
+ webSearchRequests: searches.length
1793
+ };
1794
+ (0, _warlock_js_ai.attachCost)(usage, pricing);
1795
+ return hosted.length ? { openai: { output: hosted } } : void 0;
1796
+ }
1797
+
1762
1798
  //#endregion
1763
1799
  //#region ../../packages/ai-openai/src/responses-model.ts
1764
1800
  const LOG_MODULE$2 = "ai.openai";
@@ -1767,7 +1803,7 @@ const LOG_MODULE$2 = "ai.openai";
1767
1803
  * (installed SDK `ResponseOutputItem` union). The adapter maps only
1768
1804
  * `function_call` to a neutral tool call, so these fail closed.
1769
1805
  */
1770
- const CLIENT_RUN_ITEM_TYPES = new Set([
1806
+ const CLIENT_RUN_ITEM_TYPES = /* @__PURE__ */ new Set([
1771
1807
  "computer_call",
1772
1808
  "custom_tool_call",
1773
1809
  "local_shell_call",
@@ -1808,7 +1844,8 @@ const CLIENT_RUN_ITEM_TYPES = new Set([
1808
1844
  *
1809
1845
  * **Provider-defined tools.** `ai.providerTool({ provider: "openai" })`
1810
1846
  * definitions are appended to `tools` verbatim. Hosted tools run inside the
1811
- * response; their output items (`web_search_call`, …) are not replayed
1847
+ * response; their output items (`web_search_call`, …) are retained in
1848
+ * `providerMetadata.openai.output` for inspection and are not replayed
1812
1849
  * (with `store: false` the next turn only needs the answer text), and a
1813
1850
  * reasoning item that led into one is not replayed either (see
1814
1851
  * `pickTurnReasoning`).
@@ -1863,6 +1900,7 @@ var OpenAIResponsesModel = class {
1863
1900
  hasToolCalls: toolCalls !== void 0
1864
1901
  });
1865
1902
  const usage = this.extractUsage(response.usage);
1903
+ const providerMetadata = hostedOutput(usage, output, this.pricing);
1866
1904
  this.logger.debug(LOG_MODULE$2, "response", "call to responses.create succeeded", {
1867
1905
  finishReason,
1868
1906
  usage
@@ -1871,6 +1909,7 @@ var OpenAIResponsesModel = class {
1871
1909
  content,
1872
1910
  finishReason,
1873
1911
  usage,
1912
+ ...providerMetadata ? { providerMetadata } : {},
1874
1913
  ...toolCalls ? { toolCalls } : {}
1875
1914
  };
1876
1915
  }
@@ -2004,6 +2043,8 @@ var OpenAIResponsesModel = class {
2004
2043
  hasToolCalls: toolCalls.length > 0
2005
2044
  });
2006
2045
  const usage = this.extractUsage(terminal.usage);
2046
+ const output = terminal.output?.length ? terminal.output : outputItems;
2047
+ const providerMetadata = hostedOutput(usage, output, this.pricing);
2007
2048
  this.logger.debug(LOG_MODULE$2, "response", "Streaming call to responses.create succeeded", {
2008
2049
  finishReason,
2009
2050
  usage
@@ -2011,7 +2052,8 @@ var OpenAIResponsesModel = class {
2011
2052
  yield {
2012
2053
  type: "done",
2013
2054
  finishReason,
2014
- usage
2055
+ usage,
2056
+ ...providerMetadata ? { providerMetadata } : {}
2015
2057
  };
2016
2058
  }
2017
2059
  /**
@@ -2038,7 +2080,7 @@ var OpenAIResponsesModel = class {
2038
2080
  ...instructions !== void 0 ? { instructions } : {},
2039
2081
  input,
2040
2082
  store: false,
2041
- include: ["reasoning.encrypted_content"],
2083
+ include: ["reasoning.encrypted_content", ...tools?.some((tool) => tool.type === "web_search" || tool.type === "web_search_2025_08_26" || tool.type === "web_search_preview" || tool.type === "web_search_preview_2025_03_11") ? ["web_search_call.action.sources"] : []],
2042
2084
  ...maxTokens !== void 0 ? { max_output_tokens: maxTokens } : {},
2043
2085
  ...!this.capabilities.reasoning && temperature !== void 0 ? { temperature } : {},
2044
2086
  ...tools ? { tools } : {},
@@ -2489,11 +2531,265 @@ var OpenAISDK = class {
2489
2531
  };
2490
2532
 
2491
2533
  //#endregion
2534
+ //#region ../../packages/ai-openai/src/kimi-tools.ts
2535
+ /** USD per billable REST request, verified 2026-10-18:
2536
+ * https://platform.kimi.ai/docs/pricing/websearch
2537
+ * HTTP 200 + nonempty search_results / nonblank markdown only. Taxes excluded.
2538
+ * Namespaced rates avoid charging the generic counters a second time. */
2539
+ const KIMI_SERVER_TOOL_PRICING = Object.freeze({
2540
+ kimiSearchRequests: .002,
2541
+ kimiSearchProRequests: .003,
2542
+ kimiFetchRequests: .002
2543
+ });
2544
+ /** Standalone first-party REST search, registered as an ordinary client function tool.
2545
+ * Wire contract: https://platform.kimi.ai/docs/api/tools-search and tools-search-pro.
2546
+ * Reuse the API key / baseURL configuration passed to OpenAISDK; the default URL
2547
+ * here is Moonshot, independent of the model used by the agent. */
2548
+ function kimiSearchTool(options) {
2549
+ const mode = options.mode ?? "basic";
2550
+ if (mode !== "basic" && mode !== "pro") throw new _warlock_js_ai.InvalidRequestError("Invalid Kimi search mode.");
2551
+ const limit = integer(options.limit ?? 5, 1, 20, "limit");
2552
+ const timeout = integer(options.timeoutSeconds ?? 30, 1, 60, "timeoutSeconds");
2553
+ if (mode === "basic" && (options.sites !== void 0 || options.timeWindow !== void 0)) throw new _warlock_js_ai.InvalidRequestError("Kimi sites and timeWindow require mode: pro.");
2554
+ if (mode === "pro" && options.includeContent !== void 0) throw new _warlock_js_ai.InvalidRequestError("Kimi includeContent is available only in basic mode.");
2555
+ if (options.sites && (options.sites.length > 5 || options.sites.some((site) => !site || /[\s()]/u.test(site)))) throw new _warlock_js_ai.InvalidRequestError("Kimi sites accepts at most five nonempty sites without whitespace or parentheses.");
2556
+ const maxChars = contentLimit(options);
2557
+ const client = createClient(options);
2558
+ const counter = mode === "pro" ? "kimiSearchProRequests" : "kimiSearchRequests";
2559
+ return meteredTool({
2560
+ name: mode === "pro" ? "kimi_search_pro" : "kimi_search",
2561
+ description: "Search the web with Kimi and return cited sources and relevant text.",
2562
+ input: stringInput("query"),
2563
+ async execute({ query }, ctx, record) {
2564
+ const body = {
2565
+ text_query: query,
2566
+ limit,
2567
+ timeout_seconds: timeout,
2568
+ ...mode === "basic" ? { include_content: options.includeContent ?? false } : {
2569
+ ...options.sites !== void 0 ? { sites: options.sites } : {},
2570
+ ...options.timeWindow !== void 0 ? { time_window: options.timeWindow } : {}
2571
+ }
2572
+ };
2573
+ const raw = await request(client, mode === "pro" ? "/tools/search_pro" : "/tools/search", body, ctx);
2574
+ if (!isObject(raw.data) || !Array.isArray(raw.data.search_results)) throw new _warlock_js_ai.ProviderError("Kimi returned invalid search_results.");
2575
+ record(raw.status === 200 && raw.data.search_results.length > 0);
2576
+ const budget = textBudget(maxChars);
2577
+ return {
2578
+ results: raw.data.search_results.slice(0, limit).map((value) => {
2579
+ if (!isObject(value)) throw new _warlock_js_ai.ProviderError("Kimi returned an invalid search result.");
2580
+ const result = {
2581
+ title: stringValue(value.title),
2582
+ url: stringValue(value.url),
2583
+ snippet: budget.take(stringValue(value.snippet)),
2584
+ siteName: stringValue(value.site_name),
2585
+ date: stringValue(value.date),
2586
+ authority: stringValue(value.authority)
2587
+ };
2588
+ if (mode === "basic" && options.includeContent) result.text = budget.take(stringValue(value.text));
2589
+ if (mode === "pro") {
2590
+ if (!Array.isArray(value.chunks)) throw new _warlock_js_ai.ProviderError("Kimi returned invalid search chunks.");
2591
+ result.chunks = value.chunks.map((chunk) => {
2592
+ if (!isObject(chunk) || typeof chunk.score !== "number" || !Number.isFinite(chunk.score)) throw new _warlock_js_ai.ProviderError("Kimi returned an invalid search chunk.");
2593
+ return {
2594
+ text: budget.take(stringValue(chunk.text)),
2595
+ score: chunk.score
2596
+ };
2597
+ }).filter((chunk) => chunk.text.length > 0);
2598
+ }
2599
+ return result;
2600
+ }),
2601
+ truncated: budget.truncated || raw.data.search_results.length > limit
2602
+ };
2603
+ }
2604
+ }, counter, options.pricing);
2605
+ }
2606
+ /** First-party URL-to-Markdown REST tool.
2607
+ * https://platform.kimi.ai/docs/api/tools-fetch — only http/https inputs. */
2608
+ function kimiFetchTool(options) {
2609
+ const client = createClient(options);
2610
+ const maxChars = contentLimit(options);
2611
+ return meteredTool({
2612
+ name: "kimi_fetch",
2613
+ description: "Read a web page with Kimi and return its title, URL and Markdown body.",
2614
+ input: stringInput("url"),
2615
+ async execute({ url }, ctx, record) {
2616
+ let parsed;
2617
+ try {
2618
+ parsed = new URL(url);
2619
+ } catch {
2620
+ throw new _warlock_js_ai.InvalidRequestError("Kimi fetch requires an http/https URL.");
2621
+ }
2622
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new _warlock_js_ai.InvalidRequestError("Kimi fetch requires an http/https URL.");
2623
+ const raw = await request(client, "/tools/fetch", { url }, ctx);
2624
+ if (!isObject(raw.data) || typeof raw.data.markdown !== "string") throw new _warlock_js_ai.ProviderError("Kimi returned invalid fetch content.");
2625
+ record(raw.status === 200 && raw.data.markdown.trim().length > 0);
2626
+ return {
2627
+ url: stringValue(raw.data.url),
2628
+ title: stringValue(raw.data.title),
2629
+ markdown: raw.data.markdown.slice(0, maxChars),
2630
+ truncated: raw.data.markdown.length > maxChars
2631
+ };
2632
+ }
2633
+ }, "kimiFetchRequests", options.pricing);
2634
+ }
2635
+ function createClient(options) {
2636
+ const { apiKey, baseURL, fetch, timeout, defaultHeaders } = options;
2637
+ if (!apiKey) throw new _warlock_js_ai.ProviderAuthError("Kimi tools require an explicitly configured API key.");
2638
+ return new openai.default({
2639
+ apiKey,
2640
+ baseURL: baseURL ?? "https://api.moonshot.ai/v1",
2641
+ fetch,
2642
+ timeout,
2643
+ defaultHeaders,
2644
+ maxRetries: 0
2645
+ });
2646
+ }
2647
+ async function request(client, path, body, ctx) {
2648
+ try {
2649
+ ctx?.signal?.throwIfAborted();
2650
+ const response = await client.post(path, {
2651
+ body,
2652
+ signal: ctx?.signal
2653
+ }).withResponse();
2654
+ return {
2655
+ data: response.data,
2656
+ status: response.response.status
2657
+ };
2658
+ } catch (thrown) {
2659
+ if (ctx?.signal?.aborted) throw new _warlock_js_ai.AgentCancelledError("Kimi tool request cancelled.", { cause: thrown });
2660
+ if (thrown instanceof openai.APIError) {
2661
+ const context = {
2662
+ status: thrown.status,
2663
+ type: thrown.type,
2664
+ requestId: thrown.headers?.get("x-msh-track-id")
2665
+ };
2666
+ if (thrown.status === 408 || thrown.status === 504) throw new _warlock_js_ai.ProviderTimeoutError(thrown.message, {
2667
+ cause: thrown,
2668
+ context
2669
+ });
2670
+ if (thrown.status === 429) {
2671
+ const retry = Number(thrown.headers?.get("retry-after") ?? NaN);
2672
+ throw new _warlock_js_ai.ProviderRateLimitError(thrown.message, {
2673
+ cause: thrown,
2674
+ context,
2675
+ retryAfter: Number.isFinite(retry) && retry >= 0 ? retry * 1e3 : void 0
2676
+ });
2677
+ }
2678
+ if (thrown.status === 403 && !thrown.type) throw new _warlock_js_ai.ProviderAuthError(thrown.message, {
2679
+ cause: thrown,
2680
+ context
2681
+ });
2682
+ }
2683
+ throw wrapOpenAIError(thrown);
2684
+ }
2685
+ }
2686
+ /** Each invoke owns its ledger, including concurrent calls and post-response failures.
2687
+ * ToolContract.invoke is the existing accounting boundary used by agents/reports. */
2688
+ function meteredTool(config, counter, prices = KIMI_SERVER_TOOL_PRICING) {
2689
+ const rate = prices[counter];
2690
+ if (rate !== void 0 && (!Number.isFinite(rate) || rate < 0)) throw new _warlock_js_ai.InvalidRequestError("Kimi per-call pricing must be finite and nonnegative.");
2691
+ return {
2692
+ ...(0, _warlock_js_ai.tool)({
2693
+ ...config,
2694
+ execute: (input, ctx) => config.execute(input, ctx, () => {})
2695
+ }),
2696
+ async invoke(input, ctx) {
2697
+ let usage = {
2698
+ input: 0,
2699
+ output: 0,
2700
+ total: 0
2701
+ };
2702
+ const result = await (0, _warlock_js_ai.tool)({
2703
+ ...config,
2704
+ execute: (validated, context) => config.execute(validated, context, (billable) => {
2705
+ const count = billable ? 1 : 0;
2706
+ usage = {
2707
+ input: 0,
2708
+ output: 0,
2709
+ total: 0,
2710
+ serverToolUse: {
2711
+ [counter]: count,
2712
+ [counter === "kimiFetchRequests" ? "webFetchRequests" : "webSearchRequests"]: count
2713
+ }
2714
+ };
2715
+ if (rate !== void 0) usage.cost = (0, _warlock_js_ai.computeCost)(usage, {
2716
+ input: 0,
2717
+ output: 0,
2718
+ serverTools: { [counter]: rate }
2719
+ });
2720
+ })
2721
+ }).invoke(input, ctx);
2722
+ return {
2723
+ ...result,
2724
+ usage,
2725
+ report: {
2726
+ ...result.report,
2727
+ usage
2728
+ }
2729
+ };
2730
+ }
2731
+ };
2732
+ }
2733
+ function integer(value, min, max, name) {
2734
+ if (!Number.isInteger(value) || value < min || value > max) throw new _warlock_js_ai.InvalidRequestError(`Kimi ${name} must be an integer from ${min} to ${max}.`);
2735
+ return value;
2736
+ }
2737
+ function contentLimit(options) {
2738
+ return integer(options.maxContentChars ?? 12e3, 1, Number.MAX_SAFE_INTEGER, "maxContentChars");
2739
+ }
2740
+ function isObject(value) {
2741
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2742
+ }
2743
+ function stringValue(value) {
2744
+ if (typeof value !== "string") throw new _warlock_js_ai.ProviderError("Kimi returned an invalid text field.");
2745
+ return value;
2746
+ }
2747
+ function textBudget(max) {
2748
+ return {
2749
+ truncated: false,
2750
+ take(text) {
2751
+ const result = text.slice(0, max);
2752
+ if (text.length > max) this.truncated = true;
2753
+ max -= result.length;
2754
+ return result;
2755
+ }
2756
+ };
2757
+ }
2758
+ function stringInput(key) {
2759
+ return {
2760
+ jsonSchema: {
2761
+ type: "object",
2762
+ properties: { [key]: {
2763
+ type: "string",
2764
+ minLength: 1
2765
+ } },
2766
+ required: [key],
2767
+ additionalProperties: false
2768
+ },
2769
+ "~standard": {
2770
+ version: 1,
2771
+ vendor: "kimi",
2772
+ validate(value) {
2773
+ if (!isObject(value) || typeof value[key] !== "string" || !value[key].trim()) return { issues: [{
2774
+ message: `${key} must be a nonempty string`,
2775
+ path: [key]
2776
+ }] };
2777
+ return { value: { [key]: value[key] } };
2778
+ }
2779
+ }
2780
+ };
2781
+ }
2782
+
2783
+ //#endregion
2784
+ exports.KIMI_SERVER_TOOL_PRICING = KIMI_SERVER_TOOL_PRICING;
2492
2785
  exports.OPENAI_MODEL_PRICING = OPENAI_MODEL_PRICING;
2786
+ exports.OPENAI_SERVER_TOOL_PRICING = OPENAI_SERVER_TOOL_PRICING;
2493
2787
  exports.OpenAIEmbedder = OpenAIEmbedder;
2494
2788
  exports.OpenAIImageModel = OpenAIImageModel;
2495
2789
  exports.OpenAISDK = OpenAISDK;
2496
2790
  exports.OpenAISpeechModel = OpenAISpeechModel;
2497
2791
  exports.OpenAITranscriptionModel = OpenAITranscriptionModel;
2792
+ exports.kimiFetchTool = kimiFetchTool;
2793
+ exports.kimiSearchTool = kimiSearchTool;
2498
2794
  exports.openAIPricingFor = openAIPricingFor;
2499
2795
  //# sourceMappingURL=index.cjs.map