@warlock.js/ai-openai 5.30.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.
- package/CHANGELOG.md +42 -1
- package/README.md +3 -3
- package/cjs/index.cjs +715 -51
- package/cjs/index.cjs.map +1 -1
- package/esm/config.type.d.mts +13 -13
- package/esm/config.type.d.mts.map +1 -1
- package/esm/embedder.d.mts +1 -3
- package/esm/embedder.d.mts.map +1 -1
- package/esm/embedder.mjs.map +1 -1
- package/esm/image.d.mts +1 -3
- package/esm/image.d.mts.map +1 -1
- package/esm/index.d.mts +4 -1
- package/esm/index.mjs +3 -1
- package/esm/kimi-tools.d.mts +69 -0
- package/esm/kimi-tools.d.mts.map +1 -0
- package/esm/kimi-tools.mjs +256 -0
- package/esm/kimi-tools.mjs.map +1 -0
- package/esm/known-pricing.d.mts +39 -0
- package/esm/known-pricing.d.mts.map +1 -0
- package/esm/known-pricing.mjs +121 -0
- package/esm/known-pricing.mjs.map +1 -0
- package/esm/model.mjs +82 -23
- package/esm/model.mjs.map +1 -1
- package/esm/provider-options.d.mts +13 -0
- package/esm/provider-options.d.mts.map +1 -0
- package/esm/provider-options.mjs +56 -0
- package/esm/provider-options.mjs.map +1 -0
- package/esm/responses-model.mjs +125 -19
- package/esm/responses-model.mjs.map +1 -1
- package/esm/sdk.d.mts +4 -5
- package/esm/sdk.d.mts.map +1 -1
- package/esm/sdk.mjs +5 -3
- package/esm/sdk.mjs.map +1 -1
- package/esm/speech.d.mts +1 -3
- package/esm/speech.d.mts.map +1 -1
- package/esm/transcription.d.mts +1 -3
- package/esm/transcription.d.mts.map +1 -1
- package/esm/transcription.mjs.map +1 -1
- package/esm/utils/build-usage.mjs +14 -2
- package/esm/utils/build-usage.mjs.map +1 -1
- package/esm/utils/hosted-output.mjs +22 -0
- package/esm/utils/hosted-output.mjs.map +1 -0
- package/esm/utils/index.mjs +2 -2
- package/esm/utils/map-responses-status.mjs.map +1 -1
- package/esm/utils/prompt-cache-params.mjs +25 -6
- package/esm/utils/prompt-cache-params.mjs.map +1 -1
- package/esm/utils/reasoning-replay.mjs +23 -1
- package/esm/utils/reasoning-replay.mjs.map +1 -1
- package/esm/utils/structured-output.mjs +1 -1
- package/esm/utils/structured-output.mjs.map +1 -1
- package/esm/utils/to-openai-messages.mjs.map +1 -1
- package/esm/utils/to-openai-tools.mjs.map +1 -1
- package/esm/utils/wrap-openai-error.mjs +6 -1
- package/esm/utils/wrap-openai-error.mjs.map +1 -1
- package/llms-full.txt +235 -37
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/skills/setup-openai/SKILL.md +235 -37
package/esm/config.type.d.mts
CHANGED
|
@@ -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
|
|
@@ -138,9 +137,11 @@ type OpenAIModelConfig = ModelConfig & {
|
|
|
138
137
|
*/
|
|
139
138
|
reasoning?: boolean;
|
|
140
139
|
/**
|
|
141
|
-
* Stable request bucket used
|
|
142
|
-
* Sent
|
|
143
|
-
*
|
|
140
|
+
* Stable request bucket used to improve prompt-cache matching, sent as
|
|
141
|
+
* `prompt_cache_key`. Sent when the SDK provider label is `"openai"`,
|
|
142
|
+
* `"xai"` or `"mistral"` — the endpoints that document the field. Other
|
|
143
|
+
* labels (Groq and DeepSeek cache automatically; custom gateways) never
|
|
144
|
+
* receive it.
|
|
144
145
|
*/
|
|
145
146
|
promptCacheKey?: string;
|
|
146
147
|
/**
|
|
@@ -177,7 +178,7 @@ type OpenAIModelConfig = ModelConfig & {
|
|
|
177
178
|
* openai.embedder({ name: "text-embedding-3-small" });
|
|
178
179
|
* openai.embedder({ name: "text-embedding-3-large", dimensions: 256 });
|
|
179
180
|
*/
|
|
180
|
-
type OpenAIEmbedderConfig = EmbedderConfig;
|
|
181
|
+
export type OpenAIEmbedderConfig = EmbedderConfig;
|
|
181
182
|
/**
|
|
182
183
|
* Per-model configuration for `OpenAISDK.image()`. Mirrors the neutral
|
|
183
184
|
* {@link ImageModelConfig} — `name` is the image model id, passed
|
|
@@ -190,7 +191,7 @@ type OpenAIEmbedderConfig = EmbedderConfig;
|
|
|
190
191
|
* openai.image({ name: "gpt-image-1" });
|
|
191
192
|
* openai.image({ name: "dall-e-3", pricing: { perImage: 0.04 } });
|
|
192
193
|
*/
|
|
193
|
-
type OpenAIImageConfig = ImageModelConfig;
|
|
194
|
+
export type OpenAIImageConfig = ImageModelConfig;
|
|
194
195
|
/**
|
|
195
196
|
* Per-model configuration for `OpenAISDK.speech()`. Mirrors the neutral
|
|
196
197
|
* {@link SpeechModelConfig} — `name` is the TTS model id, passed
|
|
@@ -202,7 +203,7 @@ type OpenAIImageConfig = ImageModelConfig;
|
|
|
202
203
|
* @example
|
|
203
204
|
* openai.speech({ name: "tts-1", voice: "alloy", pricing: { perMillionCharacters: 15 } });
|
|
204
205
|
*/
|
|
205
|
-
type OpenAISpeechConfig = SpeechModelConfig;
|
|
206
|
+
export type OpenAISpeechConfig = SpeechModelConfig;
|
|
206
207
|
/**
|
|
207
208
|
* Per-model configuration for `OpenAISDK.transcribe()`. Mirrors the
|
|
208
209
|
* neutral {@link TranscriptionModelConfig} — `name` is the STT model
|
|
@@ -214,7 +215,6 @@ type OpenAISpeechConfig = SpeechModelConfig;
|
|
|
214
215
|
* @example
|
|
215
216
|
* openai.transcribe({ name: "whisper-1", pricing: { perMinute: 0.006 } });
|
|
216
217
|
*/
|
|
217
|
-
type OpenAITranscriptionConfig = TranscriptionModelConfig;
|
|
218
|
+
export type OpenAITranscriptionConfig = TranscriptionModelConfig;
|
|
218
219
|
//#endregion
|
|
219
|
-
export { OpenAIEmbedderConfig, OpenAIImageConfig, OpenAIModelConfig, OpenAISDKConfig, OpenAISpeechConfig, OpenAITranscriptionConfig };
|
|
220
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":"
|
|
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"}
|
package/esm/embedder.d.mts
CHANGED
|
@@ -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
|
package/esm/embedder.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedder.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/embedder.ts"],"mappings":"
|
|
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/embedder.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedder.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/embedder.ts"],"sourcesContent":["import {\n EmbeddingVectorCountMismatchError,\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport type { OpenAIEmbedderConfig } from \"./config.type\";\nimport { wrapOpenAIError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\ntype EmbeddingsResponse = Awaited
|
|
1
|
+
{"version":3,"file":"embedder.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/embedder.ts"],"sourcesContent":["import {\n EmbeddingVectorCountMismatchError,\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport type { OpenAIEmbedderConfig } from \"./config.type\";\nimport { wrapOpenAIError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\ntype EmbeddingsResponse = Awaited<ReturnType<OpenAI[\"embeddings\"][\"create\"]>>;\n\n/**\n * OpenAI-backed implementation of `EmbedderContract`.\n *\n * **Role.** Converts text (or a batch of texts) into floating-point\n * vectors via OpenAI's Embeddings API. Standalone primitive — no\n * relationship to chat completions, tools, or the agent loop.\n *\n * **Dimensions.** When no `dimensions` override is supplied in config,\n * `this.dimensions` starts at `0` and is populated from the first\n * response's vector length, then cached for all subsequent calls —\n * even if a later response were to return a different length, the\n * first value wins so batches stay dimensionally consistent. Passing\n * `dimensions` in config both forwards the truncation hint to the API\n * (for models like `text-embedding-3-*`) and sets the initial value.\n *\n * **Error handling.** Raw OpenAI SDK errors are wrapped into the\n * typed `@warlock.js/ai` `AIError` hierarchy via `wrapOpenAIError` —\n * callers catch `AIError` subclasses (`ProviderRateLimitError`,\n * `ProviderAuthError`, etc.) instead of OpenAI's own classes.\n *\n * @example\n * const embedder = new OpenAIEmbedder(client, { name: \"text-embedding-3-small\" });\n * const { vector, dimensions, usage } = await embedder.embed(\"Hello world\");\n * const { vectors } = await embedder.embedMany([\"doc 1\", \"doc 2\"]);\n */\nexport class OpenAIEmbedder implements EmbedderContract {\n public readonly name: string;\n public readonly provider = \"openai\";\n public dimensions: number;\n\n private readonly client: OpenAI;\n\n /**\n * User-specified truncation hint, or `undefined` if omitted.\n * Forwarded to the API on every call so OpenAI can truncate the\n * embedding server-side for models that support it.\n */\n private readonly configuredDimensions: number | undefined;\n private readonly logger: Logger = log;\n\n public constructor(client: OpenAI, config: OpenAIEmbedderConfig) {\n this.client = client;\n this.name = config.name;\n this.configuredDimensions = config.dimensions;\n this.dimensions = config.dimensions ?? 0;\n }\n\n public async embed(input: string): Promise<EmbeddingResult> {\n const { response, usage } = await this.request(input);\n\n const [first] = response.data;\n\n if (first === undefined) {\n // A 200 whose `data` array is empty. Before this guard the next line\n // read `.embedding` off `undefined` and the caller got a bare\n // `TypeError: Cannot read properties of undefined`, naming neither the\n // provider nor what it actually returned.\n //\n // Same typed error the RAG and skill-catalog paths raise for this\n // contract violation, so one shortfall has one name wherever it\n // surfaces. Expected 1, received 0.\n throw new EmbeddingVectorCountMismatchError({\n provider: this.provider,\n expectedCount: 1,\n receivedCount: response.data.length,\n record: input,\n });\n }\n\n return {\n vector: first.embedding,\n dimensions: this.dimensions,\n usage,\n };\n }\n\n public async embedMany(inputs: string[]): Promise<EmbeddingBatchResult> {\n const { response, usage } = await this.request(inputs);\n\n return {\n vectors: response.data.map((d) => d.embedding),\n dimensions: this.dimensions,\n usage,\n };\n }\n\n /**\n * Shared transport for both `embed()` and `embedMany()` — issues the\n * `embeddings.create` call, wraps provider errors, caches dimensions\n * on the first successful response, and returns the raw response\n * plus a camelCase usage object for the caller to shape.\n */\n private async request(input: string | string[]): Promise<{\n response: EmbeddingsResponse;\n usage: EmbeddingUsage;\n }> {\n this.logger.debug(LOG_MODULE, \"embedder.request\", \"embeddings.create\", {\n model: this.name,\n batch: Array.isArray(input),\n count: Array.isArray(input) ? input.length : 1,\n });\n\n let response: EmbeddingsResponse;\n\n try {\n response = await this.client.embeddings.create({\n model: this.name,\n input,\n ...(this.configuredDimensions !== undefined\n ? { dimensions: this.configuredDimensions }\n : {}),\n });\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"embedder.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n this.logger.debug(LOG_MODULE, \"embedder.response\", \"embeddings.create returned\", {\n dimensions: response.data[0]?.embedding.length,\n usage: {\n promptTokens: response.usage.prompt_tokens,\n totalTokens: response.usage.total_tokens,\n },\n });\n\n // Cache dimensions on the first response. Once set, stays set —\n // we trust the first call to define the shape for this embedder.\n //\n // Reads the first entry optionally rather than asserting it: this runs for\n // BOTH embed() and embedMany(), and an empty `data` array is a real\n // provider response, not an impossible one. Leaving `dimensions` at 0 for\n // an empty response is correct — the callers each raise their own typed\n // error about the shortfall, and cacheing a dimension off a response that\n // carried no vectors would be inventing a shape from nothing.\n if (this.dimensions === 0) {\n const firstEmbedding = response.data[0]?.embedding;\n\n if (firstEmbedding !== undefined) {\n this.dimensions = firstEmbedding.length;\n }\n }\n\n const usage: EmbeddingUsage = {\n promptTokens: response.usage.prompt_tokens,\n totalTokens: response.usage.total_tokens,\n };\n\n return { response, usage };\n }\n}\n"],"mappings":";;;;;;AAYA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BnB,IAAa,iBAAb,MAAwD;CAetD,AAAO,YAAY,QAAgB,QAA8B;kBAbtC;gBAWO;EAGhC,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,uBAAuB,OAAO;EACnC,KAAK,aAAa,OAAO,cAAc;CACzC;CAEA,MAAa,MAAM,OAAyC;EAC1D,MAAM,EAAE,UAAU,UAAU,MAAM,KAAK,QAAQ,KAAK;EAEpD,MAAM,CAAC,SAAS,SAAS;EAEzB,IAAI,UAAU,QASZ,MAAM,IAAI,kCAAkC;GAC1C,UAAU,KAAK;GACf,eAAe;GACf,eAAe,SAAS,KAAK;GAC7B,QAAQ;EACV,CAAC;EAGH,OAAO;GACL,QAAQ,MAAM;GACd,YAAY,KAAK;GACjB;EACF;CACF;CAEA,MAAa,UAAU,QAAiD;EACtE,MAAM,EAAE,UAAU,UAAU,MAAM,KAAK,QAAQ,MAAM;EAErD,OAAO;GACL,SAAS,SAAS,KAAK,KAAK,MAAM,EAAE,SAAS;GAC7C,YAAY,KAAK;GACjB;EACF;CACF;;;;;;;CAQA,MAAc,QAAQ,OAGnB;EACD,KAAK,OAAO,MAAM,YAAY,oBAAoB,qBAAqB;GACrE,OAAO,KAAK;GACZ,OAAO,MAAM,QAAQ,KAAK;GAC1B,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS;EAC/C,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,WAAW,MAAM,KAAK,OAAO,WAAW,OAAO;IAC7C,OAAO,KAAK;IACZ;IACA,GAAI,KAAK,yBAAyB,SAC9B,EAAE,YAAY,KAAK,qBAAqB,IACxC,CAAC;GACP,CAAC;EACH,SAAS,QAAQ;GACf,MAAM,UAAU,gBAAgB,MAAM;GAEtC,KAAK,OAAO,MAAM,YAAY,kBAAkB,QAAQ,SAAS;IAC/D,MAAM,QAAQ;IACd,SAAS,QAAQ;GACnB,CAAC;GAED,MAAM;EACR;EAEA,KAAK,OAAO,MAAM,YAAY,qBAAqB,8BAA8B;GAC/E,YAAY,SAAS,KAAK,EAAE,EAAE,UAAU;GACxC,OAAO;IACL,cAAc,SAAS,MAAM;IAC7B,aAAa,SAAS,MAAM;GAC9B;EACF,CAAC;EAWD,IAAI,KAAK,eAAe,GAAG;GACzB,MAAM,iBAAiB,SAAS,KAAK,EAAE,EAAE;GAEzC,IAAI,mBAAmB,QACrB,KAAK,aAAa,eAAe;EAErC;EAEA,MAAM,QAAwB;GAC5B,cAAc,SAAS,MAAM;GAC7B,aAAa,SAAS,MAAM;EAC9B;EAEA,OAAO;GAAE;GAAU;EAAM;CAC3B;AACF"}
|
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
|
package/esm/image.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/image.ts"],"mappings":"
|
|
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,7 +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, OPENAI_SERVER_TOOL_PRICING, openAIPricingFor } from "./known-pricing.mjs";
|
|
3
4
|
import { OpenAIEmbedder } from "./embedder.mjs";
|
|
4
5
|
import { OpenAIImageModel } from "./image.mjs";
|
|
5
6
|
import { OpenAISpeechModel } from "./speech.mjs";
|
|
6
7
|
import { OpenAITranscriptionModel } from "./transcription.mjs";
|
|
7
|
-
|
|
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,7 +1,9 @@
|
|
|
1
|
+
import { OPENAI_MODEL_PRICING, OPENAI_SERVER_TOOL_PRICING, openAIPricingFor } from "./known-pricing.mjs";
|
|
1
2
|
import { OpenAIEmbedder } from "./embedder.mjs";
|
|
2
3
|
import { OpenAIImageModel } from "./image.mjs";
|
|
3
4
|
import { OpenAISpeechModel } from "./speech.mjs";
|
|
4
5
|
import { OpenAITranscriptionModel } from "./transcription.mjs";
|
|
5
6
|
import { OpenAISDK } from "./sdk.mjs";
|
|
7
|
+
import { KIMI_SERVER_TOOL_PRICING, kimiFetchTool, kimiSearchTool } from "./kimi-tools.mjs";
|
|
6
8
|
|
|
7
|
-
export { OpenAIEmbedder, OpenAIImageModel, OpenAISDK, OpenAISpeechModel, OpenAITranscriptionModel };
|
|
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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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ModelPricing } from "@warlock.js/ai";
|
|
2
|
+
//#region ../../packages/ai-openai/src/known-pricing.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Built-in OpenAI API list prices, USD per 1,000,000 tokens, keyed by
|
|
5
|
+
* exact model id. `cachedInput` is the cached-input rate. None of these
|
|
6
|
+
* models charges for cache writes.
|
|
7
|
+
*
|
|
8
|
+
* Source: https://developers.openai.com/api/docs/pricing (the target of
|
|
9
|
+
* platform.openai.com/docs/pricing), Standard tier, fetched 2026-10-17.
|
|
10
|
+
*
|
|
11
|
+
* Applied only when the SDK's provider label is `"openai"`, and only on
|
|
12
|
+
* an exact id match — dated snapshots can carry other prices. Left out on
|
|
13
|
+
* purpose, so pass `pricing` yourself for them:
|
|
14
|
+
*
|
|
15
|
+
* - Models priced by prompt length (a higher rate above 272K input
|
|
16
|
+
* tokens): the gpt-6 / gpt-5.6 / gpt-5.5 / gpt-5.4 families and the
|
|
17
|
+
* `-pro` models. A flat `ModelPricing` cannot express the tiers.
|
|
18
|
+
* - Promotional, Batch, Flex and Priority pricing, and the regional
|
|
19
|
+
* (data-residency) uplift.
|
|
20
|
+
*/
|
|
21
|
+
export declare const OPENAI_MODEL_PRICING: Readonly<Record<string, ModelPricing>>;
|
|
22
|
+
/**
|
|
23
|
+
* Look up the built-in list price for an OpenAI model id (exact match).
|
|
24
|
+
* Returns `undefined` for unknown ids, so cost stays unknown rather than
|
|
25
|
+
* wrong.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* openAIPricingFor("gpt-4o-mini"); // { input: 0.15, output: 0.6, cachedInput: 0.075 }
|
|
29
|
+
* openAIPricingFor("gpt-5.5"); // undefined — priced by prompt length
|
|
30
|
+
*/
|
|
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
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
//# sourceMappingURL=known-pricing.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|