@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
@@ -1,6 +1,5 @@
1
1
  import OpenAI, { ClientOptions } from "openai";
2
2
  import { EmbedderConfig, ImageModelConfig, ModelConfig, ModelPricing, SpeechModelConfig, TranscriptionModelConfig } from "@warlock.js/ai";
3
-
4
3
  //#region ../../packages/ai-openai/src/config.type.d.ts
5
4
  /**
6
5
  * Configuration for the OpenAI SDK adapter.
@@ -32,7 +31,7 @@ import { EmbedderConfig, ImageModelConfig, ModelConfig, ModelPricing, SpeechMode
32
31
  * },
33
32
  * });
34
33
  */
35
- type OpenAISDKConfig = Omit<ClientOptions, "provider"> & {
34
+ export type OpenAISDKConfig = Omit<ClientOptions, "provider"> & {
36
35
  /**
37
36
  * Free-form upstream label. `ClientOptions.provider` is omitted above
38
37
  * on purpose: openai v7 added its own `provider?: Provider` key (an
@@ -63,7 +62,7 @@ type OpenAISDKConfig = Omit<ClientOptions, "provider"> & {
63
62
  * // Route through OpenRouter to a model that rejects strict json_schema:
64
63
  * openai.model({ name: "some-legacy-model", responseFormat: "json_object" });
65
64
  */
66
- type OpenAIResponseFormat = "json_schema" | "json_object" | "text";
65
+ export type OpenAIResponseFormat = "json_schema" | "json_object" | "text";
67
66
  /**
68
67
  * Which OpenAI endpoint a model speaks.
69
68
  *
@@ -75,7 +74,7 @@ type OpenAIResponseFormat = "json_schema" | "json_object" | "text";
75
74
  * only: `OpenAISDK.model()` throws for any other provider label, so the
76
75
  * wrapper packages (deepseek, groq, xai, mistral) can never reach it.
77
76
  */
78
- type OpenAIApi = "chat" | "responses";
77
+ export type OpenAIApi = "chat" | "responses";
79
78
  /**
80
79
  * Per-model configuration for `OpenAISDK.model()`. Extends the neutral
81
80
  * `ModelConfig` with OpenAI-specific capability overrides.
@@ -88,7 +87,7 @@ type OpenAIApi = "chat" | "responses";
88
87
  * // Reasoning model + tools on the Responses API (direct OpenAI only):
89
88
  * openai.model({ name: "gpt-5.6", api: "responses" });
90
89
  */
91
- type OpenAIModelConfig = ModelConfig & {
90
+ export type OpenAIModelConfig = ModelConfig & {
92
91
  /**
93
92
  * Wire API to use. Default `"chat"` (Chat Completions). `"responses"`
94
93
  * selects the stateless Responses adapter and is accepted only on the
@@ -179,7 +178,7 @@ type OpenAIModelConfig = ModelConfig & {
179
178
  * openai.embedder({ name: "text-embedding-3-small" });
180
179
  * openai.embedder({ name: "text-embedding-3-large", dimensions: 256 });
181
180
  */
182
- type OpenAIEmbedderConfig = EmbedderConfig;
181
+ export type OpenAIEmbedderConfig = EmbedderConfig;
183
182
  /**
184
183
  * Per-model configuration for `OpenAISDK.image()`. Mirrors the neutral
185
184
  * {@link ImageModelConfig} — `name` is the image model id, passed
@@ -192,7 +191,7 @@ type OpenAIEmbedderConfig = EmbedderConfig;
192
191
  * openai.image({ name: "gpt-image-1" });
193
192
  * openai.image({ name: "dall-e-3", pricing: { perImage: 0.04 } });
194
193
  */
195
- type OpenAIImageConfig = ImageModelConfig;
194
+ export type OpenAIImageConfig = ImageModelConfig;
196
195
  /**
197
196
  * Per-model configuration for `OpenAISDK.speech()`. Mirrors the neutral
198
197
  * {@link SpeechModelConfig} — `name` is the TTS model id, passed
@@ -204,7 +203,7 @@ type OpenAIImageConfig = ImageModelConfig;
204
203
  * @example
205
204
  * openai.speech({ name: "tts-1", voice: "alloy", pricing: { perMillionCharacters: 15 } });
206
205
  */
207
- type OpenAISpeechConfig = SpeechModelConfig;
206
+ export type OpenAISpeechConfig = SpeechModelConfig;
208
207
  /**
209
208
  * Per-model configuration for `OpenAISDK.transcribe()`. Mirrors the
210
209
  * neutral {@link TranscriptionModelConfig} — `name` is the STT model
@@ -216,7 +215,6 @@ type OpenAISpeechConfig = SpeechModelConfig;
216
215
  * @example
217
216
  * openai.transcribe({ name: "whisper-1", pricing: { perMinute: 0.006 } });
218
217
  */
219
- type OpenAITranscriptionConfig = TranscriptionModelConfig;
218
+ export type OpenAITranscriptionConfig = TranscriptionModelConfig;
220
219
  //#endregion
221
- export { OpenAIEmbedderConfig, OpenAIImageConfig, OpenAIModelConfig, OpenAISDKConfig, OpenAISpeechConfig, OpenAITranscriptionConfig };
222
220
  //# sourceMappingURL=config.type.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/config.type.ts"],"mappings":";;;;;AAyCA;;;;;;;;;;;;;;;;AAeuC;AAiBvC;;;;AAAgC;AAahC;;;;AAAqB;AAcrB;;KA3DY,eAAA,GAAkB,IAAA,CAAK,aAAA;EA2DH;;;;;;;;EAlD9B,QAAA;EAwDM;;;;;EAlDN,OAAA,GAAU,MAAA,SAAe,YAAA;AAAA;;;;;;;;;;AA8HpB;AAaP;;;;KA1HY,oBAAA;AAwIZ;;;;AAAgD;AAahD;;;;AAAkD;AAalD;AA1BA,KA3HY,SAAA;;;AAqJoD;;;;;;;;;;KAvIpD,iBAAA,GAAoB,WAAA;;;;;;EAM9B,GAAA,GAAM,SAAA;;;;;;;;EAQN,MAAA;;;;;;;;;;;;;;EAcA,cAAA,GAAiB,oBAAA;;;;;;;;;EASjB,gBAAA;;;;;;;;;;;EAWA,SAAA;;;;;;;;EAQA,cAAA;;;;;;EAMA,oBAAA,GAAuB,OAAA,CACrB,MAAA,CAAO,IAAA,CAAK,WAAA,CAAY,0BAAA;;;;;;;;;EAW1B,GAAA;;;;;;;;EAQA,KAAA;AAAA;;;;;;;;;;;KAaU,oBAAA,GAAuB,cAAc;;;;;;;;;;;;;KAcrC,iBAAA,GAAoB,gBAAgB;;;;;;;;;;;;KAapC,kBAAA,GAAqB,iBAAiB;;;;;;;;;;;;KAatC,yBAAA,GAA4B,wBAAwB"}
1
+ {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/config.type.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAyCY,kBAAkB,KAAK;;;;;;;;;EASjC;;;;;;EAMA,UAAU,eAAe;;;;;;;;;;;;;;;;YAiBf;;;;;;;;;;;;YAaA;;;;;;;;;;;;;YAcA,oBAAoB;;;;;;EAM9B,MAAM;;;;;;;;EAQN;;;;;;;;;;;;;;EAcA,iBAAiB;;;;;;;;;EASjB;;;;;;;;;;;EAWA;;;;;;;;EAQA;;;;;;EAMA,uBAAuB,QACrB,OAAO,KAAK,YAAY;;;;;;;;;EAW1B;;;;;;;;EAQA;;;;;;;;;;;;YAaU,uBAAuB;;;;;;;;;;;;;YAcvB,oBAAoB;;;;;;;;;;;;YAapB,qBAAqB;;;;;;;;;;;;YAarB,4BAA4B"}
@@ -1,7 +1,6 @@
1
1
  import { OpenAIEmbedderConfig } from "./config.type.mjs";
2
2
  import OpenAI from "openai";
3
3
  import { EmbedderContract, EmbeddingBatchResult, EmbeddingResult } from "@warlock.js/ai";
4
-
5
4
  //#region ../../packages/ai-openai/src/embedder.d.ts
6
5
  /**
7
6
  * OpenAI-backed implementation of `EmbedderContract`.
@@ -28,7 +27,7 @@ import { EmbedderContract, EmbeddingBatchResult, EmbeddingResult } from "@warloc
28
27
  * const { vector, dimensions, usage } = await embedder.embed("Hello world");
29
28
  * const { vectors } = await embedder.embedMany(["doc 1", "doc 2"]);
30
29
  */
31
- declare class OpenAIEmbedder implements EmbedderContract {
30
+ export declare class OpenAIEmbedder implements EmbedderContract {
32
31
  readonly name: string;
33
32
  readonly provider = "openai";
34
33
  dimensions: number;
@@ -52,5 +51,4 @@ declare class OpenAIEmbedder implements EmbedderContract {
52
51
  private request;
53
52
  }
54
53
  //#endregion
55
- export { OpenAIEmbedder };
56
54
  //# sourceMappingURL=embedder.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"embedder.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/embedder.ts"],"mappings":";;;;;;;AAyCA;;;;;;;;;;;;;;;;;;;;;;;cAAa,cAAA,YAA0B,gBAAA;EAAA,SACrB,IAAA;EAAA,SACA,QAAA;EACT,UAAA;EAAA,iBAEU,MAAA;EA8CJ;;;;;EAAA,iBAvCI,oBAAA;EAAA,iBACA,MAAA;cAEE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,oBAAA;EAO9B,KAAA,CAAM,KAAA,WAAgB,OAAA,CAAQ,eAAA;EA6B9B,SAAA,CAAU,MAAA,aAAmB,OAAA,CAAQ,oBAAA;;;;;;;UAgBpC,OAAA;AAAA"}
1
+ {"version":3,"file":"embedder.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/embedder.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAyCa,0BAA0B;WACrB;WACA;EACT;mBAEU;;;;;;mBAOA;mBACA;EAEjB,YAAmB,QAAQ,QAAQ,QAAQ;EAO9B,MAAM,gBAAgB,QAAQ;EA6B9B,UAAU,mBAAmB,QAAQ;;;;;;;UAgBpC"}
package/esm/image.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { OpenAIImageConfig } from "./config.type.mjs";
2
2
  import OpenAI from "openai";
3
3
  import { ImageGenerationOptions, ImageGenerationResponse, ImageModelContract, ImageModelPricing } from "@warlock.js/ai";
4
-
5
4
  //#region ../../packages/ai-openai/src/image.d.ts
6
5
  /**
7
6
  * OpenAI-backed implementation of `ImageModelContract`.
@@ -31,7 +30,7 @@ import { ImageGenerationOptions, ImageGenerationResponse, ImageModelContract, Im
31
30
  * const model = new OpenAIImageModel(client, { name: "gpt-image-1" }, "openai");
32
31
  * const { images, usage } = await model.generate("a teal ceramic mug, studio light");
33
32
  */
34
- declare class OpenAIImageModel implements ImageModelContract {
33
+ export declare class OpenAIImageModel implements ImageModelContract {
35
34
  readonly name: string;
36
35
  readonly provider: string;
37
36
  readonly pricing?: ImageModelPricing;
@@ -49,5 +48,4 @@ declare class OpenAIImageModel implements ImageModelContract {
49
48
  private toGeneratedImage;
50
49
  }
51
50
  //#endregion
52
- export { OpenAIImageModel };
53
51
  //# sourceMappingURL=image.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/image.ts"],"mappings":";;;;;;;AAwDA;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,gBAAA,YAA4B,kBAAA;EAAA,SACvB,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,iBAAA;EAAA,iBAET,MAAA;EAAA,iBACA,MAAA;cAEE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,iBAAA,EAAmB,QAAA;EAOjD,QAAA,CACX,MAAA,UACA,OAAA,GAAU,sBAAA,GACT,OAAA,CAAQ,uBAAA;;;;;;;;UA+EH,gBAAA;AAAA"}
1
+ {"version":3,"file":"image.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/image.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAwDa,4BAA4B;WACvB;WACA;WACA,UAAU;mBAET;mBACA;EAEjB,YAAmB,QAAQ,QAAQ,QAAQ,mBAAmB;EAOjD,SACX,gBACA,UAAU,yBACT,QAAQ;;;;;;;;UA+EH"}
package/esm/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { OpenAIEmbedderConfig, OpenAIImageConfig, OpenAISDKConfig, OpenAISpeechConfig, OpenAITranscriptionConfig } from "./config.type.mjs";
2
2
  import { OpenAISDK } from "./sdk.mjs";
3
- import { OPENAI_MODEL_PRICING, openAIPricingFor } from "./known-pricing.mjs";
3
+ import { OPENAI_MODEL_PRICING, OPENAI_SERVER_TOOL_PRICING, openAIPricingFor } from "./known-pricing.mjs";
4
4
  import { OpenAIEmbedder } from "./embedder.mjs";
5
5
  import { OpenAIImageModel } from "./image.mjs";
6
6
  import { OpenAISpeechModel } from "./speech.mjs";
7
7
  import { OpenAITranscriptionModel } from "./transcription.mjs";
8
- import { OpenAICallOptions } from "./provider-options.mjs";
9
- export { OPENAI_MODEL_PRICING, type OpenAICallOptions, OpenAIEmbedder, type OpenAIEmbedderConfig, type OpenAIImageConfig, OpenAIImageModel, OpenAISDK, type OpenAISDKConfig, type OpenAISpeechConfig, OpenAISpeechModel, type OpenAITranscriptionConfig, OpenAITranscriptionModel, openAIPricingFor };
8
+ import { OpenAICallOptions, QwenCallOptions } from "./provider-options.mjs";
9
+ import { KIMI_SERVER_TOOL_PRICING, KimiFetchOutput, KimiSearchOutput, KimiSearchResult, KimiSearchToolOptions, KimiToolOptions, kimiFetchTool, kimiSearchTool } from "./kimi-tools.mjs";
10
+ export { KIMI_SERVER_TOOL_PRICING, type KimiFetchOutput, type KimiSearchOutput, type KimiSearchResult, type KimiSearchToolOptions, type KimiToolOptions, OPENAI_MODEL_PRICING, OPENAI_SERVER_TOOL_PRICING, type OpenAICallOptions, OpenAIEmbedder, type OpenAIEmbedderConfig, type OpenAIImageConfig, OpenAIImageModel, OpenAISDK, type OpenAISDKConfig, type OpenAISpeechConfig, OpenAISpeechModel, type OpenAITranscriptionConfig, OpenAITranscriptionModel, type QwenCallOptions, kimiFetchTool, kimiSearchTool, openAIPricingFor };
package/esm/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
- import { OPENAI_MODEL_PRICING, openAIPricingFor } from "./known-pricing.mjs";
1
+ import { OPENAI_MODEL_PRICING, OPENAI_SERVER_TOOL_PRICING, openAIPricingFor } from "./known-pricing.mjs";
2
2
  import { OpenAIEmbedder } from "./embedder.mjs";
3
3
  import { OpenAIImageModel } from "./image.mjs";
4
4
  import { OpenAISpeechModel } from "./speech.mjs";
5
5
  import { OpenAITranscriptionModel } from "./transcription.mjs";
6
6
  import { OpenAISDK } from "./sdk.mjs";
7
+ import { KIMI_SERVER_TOOL_PRICING, kimiFetchTool, kimiSearchTool } from "./kimi-tools.mjs";
7
8
 
8
- export { OPENAI_MODEL_PRICING, OpenAIEmbedder, OpenAIImageModel, OpenAISDK, OpenAISpeechModel, OpenAITranscriptionModel, openAIPricingFor };
9
+ export { KIMI_SERVER_TOOL_PRICING, OPENAI_MODEL_PRICING, OPENAI_SERVER_TOOL_PRICING, OpenAIEmbedder, OpenAIImageModel, OpenAISDK, OpenAISpeechModel, OpenAITranscriptionModel, kimiFetchTool, kimiSearchTool, openAIPricingFor };
@@ -0,0 +1,69 @@
1
+ import { ClientOptions } from "openai";
2
+ import { ServerToolPricing, ToolContract } from "@warlock.js/ai";
3
+ //#region ../../packages/ai-openai/src/kimi-tools.d.ts
4
+ /** USD per billable REST request, verified 2026-10-18:
5
+ * https://platform.kimi.ai/docs/pricing/websearch
6
+ * HTTP 200 + nonempty search_results / nonblank markdown only. Taxes excluded.
7
+ * Namespaced rates avoid charging the generic counters a second time. */
8
+ export declare const KIMI_SERVER_TOOL_PRICING: Readonly<ServerToolPricing>;
9
+ export type KimiToolOptions = Pick<ClientOptions, "baseURL" | "fetch" | "timeout" | "defaultHeaders"> & {
10
+ apiKey: NonNullable<ClientOptions["apiKey"]>;
11
+ /** Total snippet / page / chunk characters returned to the model. Default 12,000. */
12
+ maxContentChars?: number;
13
+ /** Per-request USD rates keyed like KIMI_SERVER_TOOL_PRICING. {} leaves calls unpriced. */
14
+ pricing?: ServerToolPricing;
15
+ };
16
+ export type KimiSearchToolOptions = KimiToolOptions & {
17
+ /** Default "basic". Pro returns ranked passages and supports site/date constraints. */
18
+ mode?: "basic" | "pro";
19
+ /** 1–20, default 5. */
20
+ limit?: number;
21
+ /** 1–60 seconds, default 30. */
22
+ timeoutSeconds?: number;
23
+ /** Basic only; default false. */
24
+ includeContent?: boolean;
25
+ /** Pro only; at most five sites, combined with OR. */
26
+ sites?: string[];
27
+ /** Pro only; dates accept YYYY, YYYY-MM or YYYY-MM-DD (validated by Kimi). */
28
+ timeWindow?: {
29
+ start?: string;
30
+ end?: string;
31
+ };
32
+ };
33
+ export type KimiSearchResult = {
34
+ title: string;
35
+ url: string;
36
+ snippet: string;
37
+ siteName: string;
38
+ date: string;
39
+ authority: string;
40
+ text?: string;
41
+ chunks?: {
42
+ text: string;
43
+ score: number;
44
+ }[];
45
+ };
46
+ export type KimiSearchOutput = {
47
+ results: KimiSearchResult[];
48
+ truncated: boolean;
49
+ };
50
+ export type KimiFetchOutput = {
51
+ url: string;
52
+ title: string;
53
+ markdown: string;
54
+ truncated: boolean;
55
+ };
56
+ /** Standalone first-party REST search, registered as an ordinary client function tool.
57
+ * Wire contract: https://platform.kimi.ai/docs/api/tools-search and tools-search-pro.
58
+ * Reuse the API key / baseURL configuration passed to OpenAISDK; the default URL
59
+ * here is Moonshot, independent of the model used by the agent. */
60
+ export declare function kimiSearchTool(options: KimiSearchToolOptions): ToolContract<{
61
+ query: string;
62
+ }, KimiSearchOutput>;
63
+ /** First-party URL-to-Markdown REST tool.
64
+ * https://platform.kimi.ai/docs/api/tools-fetch — only http/https inputs. */
65
+ export declare function kimiFetchTool(options: KimiToolOptions): ToolContract<{
66
+ url: string;
67
+ }, KimiFetchOutput>;
68
+ //#endregion
69
+ //# sourceMappingURL=kimi-tools.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kimi-tools.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/kimi-tools.ts"],"mappings":";;;;;;;qBAuBa,0BAA0B,SAAS;YAMpC,kBAAkB,KAC5B;EAGA,QAAQ,YAAY;;EAEpB;;EAEA,UAAU;;YAGA,wBAAwB;;EAElC;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;IAAe;IAAgB;;;YAGrB;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;IAAW;IAAc;;;YAEf;EAAqB,SAAS;EAAoB;;YAClD;EAAoB;EAAa;EAAe;EAAkB;;;;;;wBAQ9D,eACd,SAAS,wBACR;EAAe;GAAiB;;;wBA2FnB,cACd,SAAS,kBACR;EAAe;GAAe"}
@@ -0,0 +1,256 @@
1
+ import { wrapOpenAIError } from "./utils/wrap-openai-error.mjs";
2
+ import OpenAI, { APIError } from "openai";
3
+ import { AgentCancelledError, InvalidRequestError, ProviderAuthError, ProviderError, ProviderRateLimitError, ProviderTimeoutError, computeCost, tool } from "@warlock.js/ai";
4
+
5
+ //#region ../../packages/ai-openai/src/kimi-tools.ts
6
+ /** USD per billable REST request, verified 2026-10-18:
7
+ * https://platform.kimi.ai/docs/pricing/websearch
8
+ * HTTP 200 + nonempty search_results / nonblank markdown only. Taxes excluded.
9
+ * Namespaced rates avoid charging the generic counters a second time. */
10
+ const KIMI_SERVER_TOOL_PRICING = Object.freeze({
11
+ kimiSearchRequests: .002,
12
+ kimiSearchProRequests: .003,
13
+ kimiFetchRequests: .002
14
+ });
15
+ /** Standalone first-party REST search, registered as an ordinary client function tool.
16
+ * Wire contract: https://platform.kimi.ai/docs/api/tools-search and tools-search-pro.
17
+ * Reuse the API key / baseURL configuration passed to OpenAISDK; the default URL
18
+ * here is Moonshot, independent of the model used by the agent. */
19
+ function kimiSearchTool(options) {
20
+ const mode = options.mode ?? "basic";
21
+ if (mode !== "basic" && mode !== "pro") throw new InvalidRequestError("Invalid Kimi search mode.");
22
+ const limit = integer(options.limit ?? 5, 1, 20, "limit");
23
+ const timeout = integer(options.timeoutSeconds ?? 30, 1, 60, "timeoutSeconds");
24
+ if (mode === "basic" && (options.sites !== void 0 || options.timeWindow !== void 0)) throw new InvalidRequestError("Kimi sites and timeWindow require mode: pro.");
25
+ if (mode === "pro" && options.includeContent !== void 0) throw new InvalidRequestError("Kimi includeContent is available only in basic mode.");
26
+ if (options.sites && (options.sites.length > 5 || options.sites.some((site) => !site || /[\s()]/u.test(site)))) throw new InvalidRequestError("Kimi sites accepts at most five nonempty sites without whitespace or parentheses.");
27
+ const maxChars = contentLimit(options);
28
+ const client = createClient(options);
29
+ const counter = mode === "pro" ? "kimiSearchProRequests" : "kimiSearchRequests";
30
+ return meteredTool({
31
+ name: mode === "pro" ? "kimi_search_pro" : "kimi_search",
32
+ description: "Search the web with Kimi and return cited sources and relevant text.",
33
+ input: stringInput("query"),
34
+ async execute({ query }, ctx, record) {
35
+ const body = {
36
+ text_query: query,
37
+ limit,
38
+ timeout_seconds: timeout,
39
+ ...mode === "basic" ? { include_content: options.includeContent ?? false } : {
40
+ ...options.sites !== void 0 ? { sites: options.sites } : {},
41
+ ...options.timeWindow !== void 0 ? { time_window: options.timeWindow } : {}
42
+ }
43
+ };
44
+ const raw = await request(client, mode === "pro" ? "/tools/search_pro" : "/tools/search", body, ctx);
45
+ if (!isObject(raw.data) || !Array.isArray(raw.data.search_results)) throw new ProviderError("Kimi returned invalid search_results.");
46
+ record(raw.status === 200 && raw.data.search_results.length > 0);
47
+ const budget = textBudget(maxChars);
48
+ return {
49
+ results: raw.data.search_results.slice(0, limit).map((value) => {
50
+ if (!isObject(value)) throw new ProviderError("Kimi returned an invalid search result.");
51
+ const result = {
52
+ title: stringValue(value.title),
53
+ url: stringValue(value.url),
54
+ snippet: budget.take(stringValue(value.snippet)),
55
+ siteName: stringValue(value.site_name),
56
+ date: stringValue(value.date),
57
+ authority: stringValue(value.authority)
58
+ };
59
+ if (mode === "basic" && options.includeContent) result.text = budget.take(stringValue(value.text));
60
+ if (mode === "pro") {
61
+ if (!Array.isArray(value.chunks)) throw new ProviderError("Kimi returned invalid search chunks.");
62
+ result.chunks = value.chunks.map((chunk) => {
63
+ if (!isObject(chunk) || typeof chunk.score !== "number" || !Number.isFinite(chunk.score)) throw new ProviderError("Kimi returned an invalid search chunk.");
64
+ return {
65
+ text: budget.take(stringValue(chunk.text)),
66
+ score: chunk.score
67
+ };
68
+ }).filter((chunk) => chunk.text.length > 0);
69
+ }
70
+ return result;
71
+ }),
72
+ truncated: budget.truncated || raw.data.search_results.length > limit
73
+ };
74
+ }
75
+ }, counter, options.pricing);
76
+ }
77
+ /** First-party URL-to-Markdown REST tool.
78
+ * https://platform.kimi.ai/docs/api/tools-fetch — only http/https inputs. */
79
+ function kimiFetchTool(options) {
80
+ const client = createClient(options);
81
+ const maxChars = contentLimit(options);
82
+ return meteredTool({
83
+ name: "kimi_fetch",
84
+ description: "Read a web page with Kimi and return its title, URL and Markdown body.",
85
+ input: stringInput("url"),
86
+ async execute({ url }, ctx, record) {
87
+ let parsed;
88
+ try {
89
+ parsed = new URL(url);
90
+ } catch {
91
+ throw new InvalidRequestError("Kimi fetch requires an http/https URL.");
92
+ }
93
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new InvalidRequestError("Kimi fetch requires an http/https URL.");
94
+ const raw = await request(client, "/tools/fetch", { url }, ctx);
95
+ if (!isObject(raw.data) || typeof raw.data.markdown !== "string") throw new ProviderError("Kimi returned invalid fetch content.");
96
+ record(raw.status === 200 && raw.data.markdown.trim().length > 0);
97
+ return {
98
+ url: stringValue(raw.data.url),
99
+ title: stringValue(raw.data.title),
100
+ markdown: raw.data.markdown.slice(0, maxChars),
101
+ truncated: raw.data.markdown.length > maxChars
102
+ };
103
+ }
104
+ }, "kimiFetchRequests", options.pricing);
105
+ }
106
+ function createClient(options) {
107
+ const { apiKey, baseURL, fetch, timeout, defaultHeaders } = options;
108
+ if (!apiKey) throw new ProviderAuthError("Kimi tools require an explicitly configured API key.");
109
+ return new OpenAI({
110
+ apiKey,
111
+ baseURL: baseURL ?? "https://api.moonshot.ai/v1",
112
+ fetch,
113
+ timeout,
114
+ defaultHeaders,
115
+ maxRetries: 0
116
+ });
117
+ }
118
+ async function request(client, path, body, ctx) {
119
+ try {
120
+ ctx?.signal?.throwIfAborted();
121
+ const response = await client.post(path, {
122
+ body,
123
+ signal: ctx?.signal
124
+ }).withResponse();
125
+ return {
126
+ data: response.data,
127
+ status: response.response.status
128
+ };
129
+ } catch (thrown) {
130
+ if (ctx?.signal?.aborted) throw new AgentCancelledError("Kimi tool request cancelled.", { cause: thrown });
131
+ if (thrown instanceof APIError) {
132
+ const context = {
133
+ status: thrown.status,
134
+ type: thrown.type,
135
+ requestId: thrown.headers?.get("x-msh-track-id")
136
+ };
137
+ if (thrown.status === 408 || thrown.status === 504) throw new ProviderTimeoutError(thrown.message, {
138
+ cause: thrown,
139
+ context
140
+ });
141
+ if (thrown.status === 429) {
142
+ const retry = Number(thrown.headers?.get("retry-after") ?? NaN);
143
+ throw new ProviderRateLimitError(thrown.message, {
144
+ cause: thrown,
145
+ context,
146
+ retryAfter: Number.isFinite(retry) && retry >= 0 ? retry * 1e3 : void 0
147
+ });
148
+ }
149
+ if (thrown.status === 403 && !thrown.type) throw new ProviderAuthError(thrown.message, {
150
+ cause: thrown,
151
+ context
152
+ });
153
+ }
154
+ throw wrapOpenAIError(thrown);
155
+ }
156
+ }
157
+ /** Each invoke owns its ledger, including concurrent calls and post-response failures.
158
+ * ToolContract.invoke is the existing accounting boundary used by agents/reports. */
159
+ function meteredTool(config, counter, prices = KIMI_SERVER_TOOL_PRICING) {
160
+ const rate = prices[counter];
161
+ if (rate !== void 0 && (!Number.isFinite(rate) || rate < 0)) throw new InvalidRequestError("Kimi per-call pricing must be finite and nonnegative.");
162
+ return {
163
+ ...tool({
164
+ ...config,
165
+ execute: (input, ctx) => config.execute(input, ctx, () => {})
166
+ }),
167
+ async invoke(input, ctx) {
168
+ let usage = {
169
+ input: 0,
170
+ output: 0,
171
+ total: 0
172
+ };
173
+ const result = await tool({
174
+ ...config,
175
+ execute: (validated, context) => config.execute(validated, context, (billable) => {
176
+ const count = billable ? 1 : 0;
177
+ usage = {
178
+ input: 0,
179
+ output: 0,
180
+ total: 0,
181
+ serverToolUse: {
182
+ [counter]: count,
183
+ [counter === "kimiFetchRequests" ? "webFetchRequests" : "webSearchRequests"]: count
184
+ }
185
+ };
186
+ if (rate !== void 0) usage.cost = computeCost(usage, {
187
+ input: 0,
188
+ output: 0,
189
+ serverTools: { [counter]: rate }
190
+ });
191
+ })
192
+ }).invoke(input, ctx);
193
+ return {
194
+ ...result,
195
+ usage,
196
+ report: {
197
+ ...result.report,
198
+ usage
199
+ }
200
+ };
201
+ }
202
+ };
203
+ }
204
+ function integer(value, min, max, name) {
205
+ if (!Number.isInteger(value) || value < min || value > max) throw new InvalidRequestError(`Kimi ${name} must be an integer from ${min} to ${max}.`);
206
+ return value;
207
+ }
208
+ function contentLimit(options) {
209
+ return integer(options.maxContentChars ?? 12e3, 1, Number.MAX_SAFE_INTEGER, "maxContentChars");
210
+ }
211
+ function isObject(value) {
212
+ return typeof value === "object" && value !== null && !Array.isArray(value);
213
+ }
214
+ function stringValue(value) {
215
+ if (typeof value !== "string") throw new ProviderError("Kimi returned an invalid text field.");
216
+ return value;
217
+ }
218
+ function textBudget(max) {
219
+ return {
220
+ truncated: false,
221
+ take(text) {
222
+ const result = text.slice(0, max);
223
+ if (text.length > max) this.truncated = true;
224
+ max -= result.length;
225
+ return result;
226
+ }
227
+ };
228
+ }
229
+ function stringInput(key) {
230
+ return {
231
+ jsonSchema: {
232
+ type: "object",
233
+ properties: { [key]: {
234
+ type: "string",
235
+ minLength: 1
236
+ } },
237
+ required: [key],
238
+ additionalProperties: false
239
+ },
240
+ "~standard": {
241
+ version: 1,
242
+ vendor: "kimi",
243
+ validate(value) {
244
+ if (!isObject(value) || typeof value[key] !== "string" || !value[key].trim()) return { issues: [{
245
+ message: `${key} must be a nonempty string`,
246
+ path: [key]
247
+ }] };
248
+ return { value: { [key]: value[key] } };
249
+ }
250
+ }
251
+ };
252
+ }
253
+
254
+ //#endregion
255
+ export { KIMI_SERVER_TOOL_PRICING, kimiFetchTool, kimiSearchTool };
256
+ //# sourceMappingURL=kimi-tools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kimi-tools.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/kimi-tools.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport {\n AgentCancelledError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n computeCost,\n tool,\n type ServerToolPricing,\n type ToolConfig,\n type ToolContext,\n type ToolContract,\n type Usage,\n} from \"@warlock.js/ai\";\nimport OpenAI, { APIError, type ClientOptions } from \"openai\";\nimport { wrapOpenAIError } from \"./utils/wrap-openai-error\";\n\n/** USD per billable REST request, verified 2026-10-18:\n * https://platform.kimi.ai/docs/pricing/websearch\n * HTTP 200 + nonempty search_results / nonblank markdown only. Taxes excluded.\n * Namespaced rates avoid charging the generic counters a second time. */\nexport const KIMI_SERVER_TOOL_PRICING: Readonly<ServerToolPricing> = Object.freeze({\n kimiSearchRequests: 0.002,\n kimiSearchProRequests: 0.003,\n kimiFetchRequests: 0.002,\n});\n\nexport type KimiToolOptions = Pick<\n ClientOptions,\n \"baseURL\" | \"fetch\" | \"timeout\" | \"defaultHeaders\"\n> & {\n apiKey: NonNullable<ClientOptions[\"apiKey\"]>;\n /** Total snippet / page / chunk characters returned to the model. Default 12,000. */\n maxContentChars?: number;\n /** Per-request USD rates keyed like KIMI_SERVER_TOOL_PRICING. {} leaves calls unpriced. */\n pricing?: ServerToolPricing;\n};\n\nexport type KimiSearchToolOptions = KimiToolOptions & {\n /** Default \"basic\". Pro returns ranked passages and supports site/date constraints. */\n mode?: \"basic\" | \"pro\";\n /** 1–20, default 5. */\n limit?: number;\n /** 1–60 seconds, default 30. */\n timeoutSeconds?: number;\n /** Basic only; default false. */\n includeContent?: boolean;\n /** Pro only; at most five sites, combined with OR. */\n sites?: string[];\n /** Pro only; dates accept YYYY, YYYY-MM or YYYY-MM-DD (validated by Kimi). */\n timeWindow?: { start?: string; end?: string };\n};\n\nexport type KimiSearchResult = {\n title: string;\n url: string;\n snippet: string;\n siteName: string;\n date: string;\n authority: string;\n text?: string;\n chunks?: { text: string; score: number }[];\n};\nexport type KimiSearchOutput = { results: KimiSearchResult[]; truncated: boolean };\nexport type KimiFetchOutput = { url: string; title: string; markdown: string; truncated: boolean };\n\ntype Counter = \"kimiSearchRequests\" | \"kimiSearchProRequests\" | \"kimiFetchRequests\";\n\n/** Standalone first-party REST search, registered as an ordinary client function tool.\n * Wire contract: https://platform.kimi.ai/docs/api/tools-search and tools-search-pro.\n * Reuse the API key / baseURL configuration passed to OpenAISDK; the default URL\n * here is Moonshot, independent of the model used by the agent. */\nexport function kimiSearchTool(\n options: KimiSearchToolOptions,\n): ToolContract<{ query: string }, KimiSearchOutput> {\n const mode = options.mode ?? \"basic\";\n if (mode !== \"basic\" && mode !== \"pro\")\n throw new InvalidRequestError(\"Invalid Kimi search mode.\");\n const limit = integer(options.limit ?? 5, 1, 20, \"limit\");\n const timeout = integer(options.timeoutSeconds ?? 30, 1, 60, \"timeoutSeconds\");\n if (mode === \"basic\" && (options.sites !== undefined || options.timeWindow !== undefined)) {\n throw new InvalidRequestError(\"Kimi sites and timeWindow require mode: pro.\");\n }\n if (mode === \"pro\" && options.includeContent !== undefined) {\n throw new InvalidRequestError(\"Kimi includeContent is available only in basic mode.\");\n }\n if (\n options.sites &&\n (options.sites.length > 5 || options.sites.some((site) => !site || /[\\s()]/u.test(site)))\n ) {\n throw new InvalidRequestError(\n \"Kimi sites accepts at most five nonempty sites without whitespace or parentheses.\",\n );\n }\n const maxChars = contentLimit(options);\n const client = createClient(options);\n const counter = mode === \"pro\" ? \"kimiSearchProRequests\" : \"kimiSearchRequests\";\n return meteredTool(\n {\n name: mode === \"pro\" ? \"kimi_search_pro\" : \"kimi_search\",\n description: \"Search the web with Kimi and return cited sources and relevant text.\",\n input: stringInput(\"query\"),\n async execute({ query }, ctx, record) {\n const body = {\n text_query: query,\n limit,\n timeout_seconds: timeout,\n ...(mode === \"basic\"\n ? { include_content: options.includeContent ?? false }\n : {\n ...(options.sites !== undefined ? { sites: options.sites } : {}),\n ...(options.timeWindow !== undefined ? { time_window: options.timeWindow } : {}),\n }),\n };\n const raw = await request(\n client,\n mode === \"pro\" ? \"/tools/search_pro\" : \"/tools/search\",\n body,\n ctx,\n );\n if (!isObject(raw.data) || !Array.isArray(raw.data.search_results)) {\n throw new ProviderError(\"Kimi returned invalid search_results.\");\n }\n record(raw.status === 200 && raw.data.search_results.length > 0);\n const budget = textBudget(maxChars);\n const results = raw.data.search_results.slice(0, limit).map((value): KimiSearchResult => {\n if (!isObject(value)) throw new ProviderError(\"Kimi returned an invalid search result.\");\n const result: KimiSearchResult = {\n title: stringValue(value.title),\n url: stringValue(value.url),\n snippet: budget.take(stringValue(value.snippet)),\n siteName: stringValue(value.site_name),\n date: stringValue(value.date),\n authority: stringValue(value.authority),\n };\n if (mode === \"basic\" && options.includeContent)\n result.text = budget.take(stringValue(value.text));\n if (mode === \"pro\") {\n if (!Array.isArray(value.chunks))\n throw new ProviderError(\"Kimi returned invalid search chunks.\");\n result.chunks = value.chunks\n .map((chunk) => {\n if (\n !isObject(chunk) ||\n typeof chunk.score !== \"number\" ||\n !Number.isFinite(chunk.score)\n ) {\n throw new ProviderError(\"Kimi returned an invalid search chunk.\");\n }\n return { text: budget.take(stringValue(chunk.text)), score: chunk.score };\n })\n .filter((chunk) => chunk.text.length > 0);\n }\n return result;\n });\n return { results, truncated: budget.truncated || raw.data.search_results.length > limit };\n },\n },\n counter,\n options.pricing,\n );\n}\n\n/** First-party URL-to-Markdown REST tool.\n * https://platform.kimi.ai/docs/api/tools-fetch — only http/https inputs. */\nexport function kimiFetchTool(\n options: KimiToolOptions,\n): ToolContract<{ url: string }, KimiFetchOutput> {\n const client = createClient(options);\n const maxChars = contentLimit(options);\n return meteredTool(\n {\n name: \"kimi_fetch\",\n description: \"Read a web page with Kimi and return its title, URL and Markdown body.\",\n input: stringInput(\"url\"),\n async execute({ url }, ctx, record) {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new InvalidRequestError(\"Kimi fetch requires an http/https URL.\");\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new InvalidRequestError(\"Kimi fetch requires an http/https URL.\");\n }\n const raw = await request(client, \"/tools/fetch\", { url }, ctx);\n if (!isObject(raw.data) || typeof raw.data.markdown !== \"string\") {\n throw new ProviderError(\"Kimi returned invalid fetch content.\");\n }\n record(raw.status === 200 && raw.data.markdown.trim().length > 0);\n return {\n url: stringValue(raw.data.url),\n title: stringValue(raw.data.title),\n markdown: raw.data.markdown.slice(0, maxChars),\n truncated: raw.data.markdown.length > maxChars,\n };\n },\n },\n \"kimiFetchRequests\",\n options.pricing,\n );\n}\n\nfunction createClient(options: KimiToolOptions): OpenAI {\n const { apiKey, baseURL, fetch, timeout, defaultHeaders } = options;\n if (!apiKey) throw new ProviderAuthError(\"Kimi tools require an explicitly configured API key.\");\n return new OpenAI({\n apiKey,\n baseURL: baseURL ?? \"https://api.moonshot.ai/v1\",\n fetch,\n timeout,\n defaultHeaders,\n maxRetries: 0,\n });\n}\n\nasync function request(client: OpenAI, path: string, body: object, ctx?: ToolContext) {\n try {\n ctx?.signal?.throwIfAborted();\n // No automatic retries: a lost response does not prove the vendor did not bill it.\n const response = await client.post<unknown>(path, { body, signal: ctx?.signal }).withResponse();\n return { data: response.data, status: response.response.status };\n } catch (thrown) {\n if (ctx?.signal?.aborted) {\n throw new AgentCancelledError(\"Kimi tool request cancelled.\", { cause: thrown });\n }\n if (thrown instanceof APIError) {\n const context = {\n status: thrown.status,\n type: thrown.type,\n requestId: thrown.headers?.get(\"x-msh-track-id\"),\n };\n if (thrown.status === 408 || thrown.status === 504)\n throw new ProviderTimeoutError(thrown.message, { cause: thrown, context });\n if (thrown.status === 429) {\n const retry = Number(thrown.headers?.get(\"retry-after\") ?? Number.NaN);\n throw new ProviderRateLimitError(thrown.message, {\n cause: thrown,\n context,\n retryAfter: Number.isFinite(retry) && retry >= 0 ? retry * 1000 : undefined,\n });\n }\n if (thrown.status === 403 && !thrown.type)\n throw new ProviderAuthError(thrown.message, { cause: thrown, context });\n }\n throw wrapOpenAIError(thrown);\n }\n}\n\n/** Each invoke owns its ledger, including concurrent calls and post-response failures.\n * ToolContract.invoke is the existing accounting boundary used by agents/reports. */\nfunction meteredTool<TInput, TOutput>(\n config: Omit<ToolConfig<TInput, TOutput>, \"execute\"> & {\n execute(\n input: TInput,\n ctx: ToolContext | undefined,\n record: (billable: boolean) => void,\n ): Promise<TOutput>;\n },\n counter: Counter,\n prices: ServerToolPricing = KIMI_SERVER_TOOL_PRICING,\n): ToolContract<TInput, TOutput> {\n const rate = prices[counter];\n if (rate !== undefined && (!Number.isFinite(rate) || rate < 0))\n throw new InvalidRequestError(\"Kimi per-call pricing must be finite and nonnegative.\");\n const base = tool({\n ...config,\n execute: (input: TInput, ctx?: ToolContext) => config.execute(input, ctx, () => {}),\n });\n return {\n ...base,\n async invoke(input, ctx) {\n let usage: Usage = { input: 0, output: 0, total: 0 };\n const invocation = tool({\n ...config,\n execute: (validated: TInput, context?: ToolContext) =>\n config.execute(validated, context, (billable) => {\n const count = billable ? 1 : 0;\n usage = {\n input: 0,\n output: 0,\n total: 0,\n serverToolUse: {\n [counter]: count,\n [counter === \"kimiFetchRequests\" ? \"webFetchRequests\" : \"webSearchRequests\"]: count,\n },\n };\n if (rate !== undefined)\n usage.cost = computeCost(usage, {\n input: 0,\n output: 0,\n serverTools: { [counter]: rate },\n });\n }),\n });\n const result = await invocation.invoke(input, ctx);\n return { ...result, usage, report: { ...result.report, usage } };\n },\n };\n}\n\nfunction integer(value: number, min: number, max: number, name: string): number {\n if (!Number.isInteger(value) || value < min || value > max)\n throw new InvalidRequestError(`Kimi ${name} must be an integer from ${min} to ${max}.`);\n return value;\n}\nfunction contentLimit(options: KimiToolOptions): number {\n return integer(options.maxContentChars ?? 12_000, 1, Number.MAX_SAFE_INTEGER, \"maxContentChars\");\n}\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\nfunction stringValue(value: unknown): string {\n if (typeof value !== \"string\") throw new ProviderError(\"Kimi returned an invalid text field.\");\n return value;\n}\nfunction textBudget(max: number) {\n return {\n truncated: false,\n take(text: string) {\n const result = text.slice(0, max);\n if (text.length > max) this.truncated = true;\n max -= result.length;\n return result;\n },\n };\n}\nfunction stringInput<K extends string>(\n key: K,\n): StandardSchemaV1<Record<K, string>> & { jsonSchema: Record<string, unknown> } {\n return {\n jsonSchema: {\n type: \"object\",\n properties: { [key]: { type: \"string\", minLength: 1 } },\n required: [key],\n additionalProperties: false,\n },\n \"~standard\": {\n version: 1,\n vendor: \"kimi\",\n validate(value) {\n if (!isObject(value) || typeof value[key] !== \"string\" || !value[key].trim()) {\n return { issues: [{ message: `${key} must be a nonempty string`, path: [key] }] };\n }\n return { value: { [key]: value[key] } as Record<K, string> };\n },\n },\n };\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { ModelPricing } from "@warlock.js/ai";
2
-
3
2
  //#region ../../packages/ai-openai/src/known-pricing.d.ts
4
3
  /**
5
4
  * Built-in OpenAI API list prices, USD per 1,000,000 tokens, keyed by
@@ -19,7 +18,7 @@ import { ModelPricing } from "@warlock.js/ai";
19
18
  * - Promotional, Batch, Flex and Priority pricing, and the regional
20
19
  * (data-residency) uplift.
21
20
  */
22
- declare const OPENAI_MODEL_PRICING: Readonly<Record<string, ModelPricing>>;
21
+ export declare const OPENAI_MODEL_PRICING: Readonly<Record<string, ModelPricing>>;
23
22
  /**
24
23
  * Look up the built-in list price for an OpenAI model id (exact match).
25
24
  * Returns `undefined` for unknown ids, so cost stays unknown rather than
@@ -29,7 +28,12 @@ declare const OPENAI_MODEL_PRICING: Readonly<Record<string, ModelPricing>>;
29
28
  * openAIPricingFor("gpt-4o-mini"); // { input: 0.15, output: 0.6, cachedInput: 0.075 }
30
29
  * openAIPricingFor("gpt-5.5"); // undefined — priced by prompt length
31
30
  */
32
- declare function openAIPricingFor(modelName: string): ModelPricing | undefined;
31
+ export declare function openAIPricingFor(modelName: string): ModelPricing | undefined;
32
+ /** USD per request for web_search (NOT non-reasoning web_search_preview).
33
+ * Search content tokens are additional; preview variants can have other rates.
34
+ * https://developers.openai.com/api/docs/pricing#built-in-tools — verified 2026-10-18. */
35
+ export declare const OPENAI_SERVER_TOOL_PRICING: {
36
+ webSearch: number;
37
+ };
33
38
  //#endregion
34
- export { OPENAI_MODEL_PRICING, openAIPricingFor };
35
39
  //# sourceMappingURL=known-pricing.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"known-pricing.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/known-pricing.ts"],"mappings":";;;;;AAoBA;;;;;;;;;;;;AAAuE;AA4BvE;;;cA5Ba,oBAAA,EAAsB,QAAA,CAAS,MAAA,SAAe,YAAA;AA4BM;;;;;;;;;AAAA,iBAAjD,gBAAA,CAAiB,SAAA,WAAoB,YAAY"}
1
+ {"version":3,"file":"known-pricing.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/known-pricing.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;qBAoBa,sBAAsB,SAAS,eAAe;;;;;;;;;;wBA4B3C,iBAAiB,oBAAoB;;;;qBAOxC;EAA+B"}
@@ -111,7 +111,11 @@ const OPENAI_MODEL_PRICING = {
111
111
  function openAIPricingFor(modelName) {
112
112
  return OPENAI_MODEL_PRICING[modelName];
113
113
  }
114
+ /** USD per request for web_search (NOT non-reasoning web_search_preview).
115
+ * Search content tokens are additional; preview variants can have other rates.
116
+ * https://developers.openai.com/api/docs/pricing#built-in-tools — verified 2026-10-18. */
117
+ const OPENAI_SERVER_TOOL_PRICING = { webSearch: .01 };
114
118
 
115
119
  //#endregion
116
- export { OPENAI_MODEL_PRICING, openAIPricingFor };
120
+ export { OPENAI_MODEL_PRICING, OPENAI_SERVER_TOOL_PRICING, openAIPricingFor };
117
121
  //# sourceMappingURL=known-pricing.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"known-pricing.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/known-pricing.ts"],"sourcesContent":["import type { ModelPricing } from \"@warlock.js/ai\";\n\n/**\n * Built-in OpenAI API list prices, USD per 1,000,000 tokens, keyed by\n * exact model id. `cachedInput` is the cached-input rate. None of these\n * models charges for cache writes.\n *\n * Source: https://developers.openai.com/api/docs/pricing (the target of\n * platform.openai.com/docs/pricing), Standard tier, fetched 2026-10-17.\n *\n * Applied only when the SDK's provider label is `\"openai\"`, and only on\n * an exact id match — dated snapshots can carry other prices. Left out on\n * purpose, so pass `pricing` yourself for them:\n *\n * - Models priced by prompt length (a higher rate above 272K input\n * tokens): the gpt-6 / gpt-5.6 / gpt-5.5 / gpt-5.4 families and the\n * `-pro` models. A flat `ModelPricing` cannot express the tiers.\n * - Promotional, Batch, Flex and Priority pricing, and the regional\n * (data-residency) uplift.\n */\nexport const OPENAI_MODEL_PRICING: Readonly<Record<string, ModelPricing>> = {\n \"gpt-5.4-mini\": { input: 0.75, output: 4.5, cachedInput: 0.075 },\n \"gpt-5.4-nano\": { input: 0.2, output: 1.25, cachedInput: 0.02 },\n \"gpt-5.2\": { input: 1.75, output: 14, cachedInput: 0.175 },\n \"gpt-5.1\": { input: 1.25, output: 10, cachedInput: 0.125 },\n \"gpt-5\": { input: 1.25, output: 10, cachedInput: 0.125 },\n \"gpt-5-mini\": { input: 0.25, output: 2, cachedInput: 0.025 },\n \"gpt-5-nano\": { input: 0.05, output: 0.4, cachedInput: 0.005 },\n \"gpt-4.1\": { input: 2, output: 8, cachedInput: 0.5 },\n \"gpt-4.1-mini\": { input: 0.4, output: 1.6, cachedInput: 0.1 },\n \"gpt-4.1-nano\": { input: 0.1, output: 0.4, cachedInput: 0.025 },\n \"gpt-4o\": { input: 2.5, output: 10, cachedInput: 1.25 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6, cachedInput: 0.075 },\n o3: { input: 2, output: 8, cachedInput: 0.5 },\n \"o4-mini\": { input: 1.1, output: 4.4, cachedInput: 0.275 },\n \"o3-mini\": { input: 1.1, output: 4.4, cachedInput: 0.55 },\n o1: { input: 15, output: 60, cachedInput: 7.5 },\n};\n\n/**\n * Look up the built-in list price for an OpenAI model id (exact match).\n * Returns `undefined` for unknown ids, so cost stays unknown rather than\n * wrong.\n *\n * @example\n * openAIPricingFor(\"gpt-4o-mini\"); // { input: 0.15, output: 0.6, cachedInput: 0.075 }\n * openAIPricingFor(\"gpt-5.5\"); // undefined — priced by prompt length\n */\nexport function openAIPricingFor(modelName: string): ModelPricing | undefined {\n return OPENAI_MODEL_PRICING[modelName];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,MAAa,uBAA+D;CAC1E,gBAAgB;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CAC/D,gBAAgB;EAAE,OAAO;EAAK,QAAQ;EAAM,aAAa;CAAK;CAC9D,WAAW;EAAE,OAAO;EAAM,QAAQ;EAAI,aAAa;CAAM;CACzD,WAAW;EAAE,OAAO;EAAM,QAAQ;EAAI,aAAa;CAAM;CACzD,SAAS;EAAE,OAAO;EAAM,QAAQ;EAAI,aAAa;CAAM;CACvD,cAAc;EAAE,OAAO;EAAM,QAAQ;EAAG,aAAa;CAAM;CAC3D,cAAc;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CAC7D,WAAW;EAAE,OAAO;EAAG,QAAQ;EAAG,aAAa;CAAI;CACnD,gBAAgB;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAI;CAC5D,gBAAgB;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAM;CAC9D,UAAU;EAAE,OAAO;EAAK,QAAQ;EAAI,aAAa;CAAK;CACtD,eAAe;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CAC9D,IAAI;EAAE,OAAO;EAAG,QAAQ;EAAG,aAAa;CAAI;CAC5C,WAAW;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAM;CACzD,WAAW;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAK;CACxD,IAAI;EAAE,OAAO;EAAI,QAAQ;EAAI,aAAa;CAAI;AAChD;;;;;;;;;;AAWA,SAAgB,iBAAiB,WAA6C;CAC5E,OAAO,qBAAqB;AAC9B"}
1
+ {"version":3,"file":"known-pricing.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/known-pricing.ts"],"sourcesContent":["import type { ModelPricing } from \"@warlock.js/ai\";\n\n/**\n * Built-in OpenAI API list prices, USD per 1,000,000 tokens, keyed by\n * exact model id. `cachedInput` is the cached-input rate. None of these\n * models charges for cache writes.\n *\n * Source: https://developers.openai.com/api/docs/pricing (the target of\n * platform.openai.com/docs/pricing), Standard tier, fetched 2026-10-17.\n *\n * Applied only when the SDK's provider label is `\"openai\"`, and only on\n * an exact id match — dated snapshots can carry other prices. Left out on\n * purpose, so pass `pricing` yourself for them:\n *\n * - Models priced by prompt length (a higher rate above 272K input\n * tokens): the gpt-6 / gpt-5.6 / gpt-5.5 / gpt-5.4 families and the\n * `-pro` models. A flat `ModelPricing` cannot express the tiers.\n * - Promotional, Batch, Flex and Priority pricing, and the regional\n * (data-residency) uplift.\n */\nexport const OPENAI_MODEL_PRICING: Readonly<Record<string, ModelPricing>> = {\n \"gpt-5.4-mini\": { input: 0.75, output: 4.5, cachedInput: 0.075 },\n \"gpt-5.4-nano\": { input: 0.2, output: 1.25, cachedInput: 0.02 },\n \"gpt-5.2\": { input: 1.75, output: 14, cachedInput: 0.175 },\n \"gpt-5.1\": { input: 1.25, output: 10, cachedInput: 0.125 },\n \"gpt-5\": { input: 1.25, output: 10, cachedInput: 0.125 },\n \"gpt-5-mini\": { input: 0.25, output: 2, cachedInput: 0.025 },\n \"gpt-5-nano\": { input: 0.05, output: 0.4, cachedInput: 0.005 },\n \"gpt-4.1\": { input: 2, output: 8, cachedInput: 0.5 },\n \"gpt-4.1-mini\": { input: 0.4, output: 1.6, cachedInput: 0.1 },\n \"gpt-4.1-nano\": { input: 0.1, output: 0.4, cachedInput: 0.025 },\n \"gpt-4o\": { input: 2.5, output: 10, cachedInput: 1.25 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6, cachedInput: 0.075 },\n o3: { input: 2, output: 8, cachedInput: 0.5 },\n \"o4-mini\": { input: 1.1, output: 4.4, cachedInput: 0.275 },\n \"o3-mini\": { input: 1.1, output: 4.4, cachedInput: 0.55 },\n o1: { input: 15, output: 60, cachedInput: 7.5 },\n};\n\n/**\n * Look up the built-in list price for an OpenAI model id (exact match).\n * Returns `undefined` for unknown ids, so cost stays unknown rather than\n * wrong.\n *\n * @example\n * openAIPricingFor(\"gpt-4o-mini\"); // { input: 0.15, output: 0.6, cachedInput: 0.075 }\n * openAIPricingFor(\"gpt-5.5\"); // undefined — priced by prompt length\n */\nexport function openAIPricingFor(modelName: string): ModelPricing | undefined {\n return OPENAI_MODEL_PRICING[modelName];\n}\n\n/** USD per request for web_search (NOT non-reasoning web_search_preview).\n * Search content tokens are additional; preview variants can have other rates.\n * https://developers.openai.com/api/docs/pricing#built-in-tools — verified 2026-10-18. */\nexport const OPENAI_SERVER_TOOL_PRICING = { webSearch: 0.01 };\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,MAAa,uBAA+D;CAC1E,gBAAgB;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CAC/D,gBAAgB;EAAE,OAAO;EAAK,QAAQ;EAAM,aAAa;CAAK;CAC9D,WAAW;EAAE,OAAO;EAAM,QAAQ;EAAI,aAAa;CAAM;CACzD,WAAW;EAAE,OAAO;EAAM,QAAQ;EAAI,aAAa;CAAM;CACzD,SAAS;EAAE,OAAO;EAAM,QAAQ;EAAI,aAAa;CAAM;CACvD,cAAc;EAAE,OAAO;EAAM,QAAQ;EAAG,aAAa;CAAM;CAC3D,cAAc;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CAC7D,WAAW;EAAE,OAAO;EAAG,QAAQ;EAAG,aAAa;CAAI;CACnD,gBAAgB;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAI;CAC5D,gBAAgB;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAM;CAC9D,UAAU;EAAE,OAAO;EAAK,QAAQ;EAAI,aAAa;CAAK;CACtD,eAAe;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CAC9D,IAAI;EAAE,OAAO;EAAG,QAAQ;EAAG,aAAa;CAAI;CAC5C,WAAW;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAM;CACzD,WAAW;EAAE,OAAO;EAAK,QAAQ;EAAK,aAAa;CAAK;CACxD,IAAI;EAAE,OAAO;EAAI,QAAQ;EAAI,aAAa;CAAI;AAChD;;;;;;;;;;AAWA,SAAgB,iBAAiB,WAA6C;CAC5E,OAAO,qBAAqB;AAC9B;;;;AAKA,MAAa,6BAA6B,EAAE,WAAW,IAAK"}