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