@combycode/llm-sdk 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/index.browser.js +83 -5
- package/dist/index.js +83 -5
- package/dist/llm/providers/builtin-tools.d.ts +15 -0
- package/dist/llm/providers/openai/responses.d.ts +5 -1
- package/dist/llm/providers/xai/responses.d.ts +4 -0
- package/dist/plugins/model-catalog/catalog.d.ts +5 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ All notable changes to `@combycode/llm-sdk` are documented here. The format foll
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.4.0] - 2026-07-06
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **xAI hosted code-execution files** now surface on `response.files` (parity with the other
|
|
13
|
+
providers). xAI returns code-interpreter output files inline inside the `code_interpreter_call`
|
|
14
|
+
`logs` JSON (`output_files:[{file_name, mime_type, data:[…bytes]}]`) and only when the request
|
|
15
|
+
asks for them — so `XAIResponsesAdapter` now sends `include:['code_interpreter_call.outputs']`
|
|
16
|
+
when `code_interpreter` is used and extracts the inline bytes into `FileOutput`. Verified live on
|
|
17
|
+
`complete()` and `stream()`. `OpenAIResponsesAdapter.filesFromOutputItem` is now a `protected`
|
|
18
|
+
overridable method so Responses-compatible providers can extend file extraction.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **Adapter-sourced builtin-tool capabilities in the catalog.** Every tool-capable (chat-family)
|
|
22
|
+
model now carries `capabilities.builtinTools` — `['web_search', 'code_interpreter']` for
|
|
23
|
+
anthropic/openai/google/xai, `['web_search']` for openrouter (it doesn't route hosted code
|
|
24
|
+
execution) — injected at catalog load from a single provider map (`PROVIDER_BUILTIN_TOOLS`) that
|
|
25
|
+
mirrors what the adapters actually support. New `catalog.builtinToolsFor()` /
|
|
26
|
+
`supportsBuiltinTool()` accessors and `select('web_search')` / `select('code_interpreter')`
|
|
27
|
+
queries (`search` stays an alias for `web_search`). Non-tool models (embeddings/tts/image) get
|
|
28
|
+
none. A reliable per-model source can refine this later.
|
|
29
|
+
|
|
9
30
|
## [1.3.0] - 2026-07-06
|
|
10
31
|
|
|
11
32
|
### Added
|
package/dist/index.browser.js
CHANGED
|
@@ -22105,6 +22105,15 @@ var catalog_default5 = {
|
|
|
22105
22105
|
}
|
|
22106
22106
|
};
|
|
22107
22107
|
|
|
22108
|
+
// src/llm/providers/builtin-tools.ts
|
|
22109
|
+
var PROVIDER_BUILTIN_TOOLS = {
|
|
22110
|
+
anthropic: ["web_search", "code_interpreter"],
|
|
22111
|
+
openai: ["web_search", "code_interpreter"],
|
|
22112
|
+
google: ["web_search", "code_interpreter"],
|
|
22113
|
+
xai: ["web_search", "code_interpreter"],
|
|
22114
|
+
openrouter: ["web_search"]
|
|
22115
|
+
};
|
|
22116
|
+
|
|
22108
22117
|
// src/plugins/model-catalog/catalog.ts
|
|
22109
22118
|
var PROVIDER_DEFAULT_CATALOGS = [
|
|
22110
22119
|
catalog_default,
|
|
@@ -22142,6 +22151,13 @@ var DEFAULT_REASONING = {
|
|
|
22142
22151
|
encryptedContent: false,
|
|
22143
22152
|
summaryAvailable: false
|
|
22144
22153
|
};
|
|
22154
|
+
function withBuiltinTools(provider, caps) {
|
|
22155
|
+
if (caps.builtinTools !== void 0) return caps;
|
|
22156
|
+
if (!caps.toolUse) return caps;
|
|
22157
|
+
const tools = PROVIDER_BUILTIN_TOOLS[provider];
|
|
22158
|
+
if (!tools || tools.length === 0) return caps;
|
|
22159
|
+
return { ...caps, builtinTools: [...tools] };
|
|
22160
|
+
}
|
|
22145
22161
|
var ModelCatalog = class {
|
|
22146
22162
|
models = /* @__PURE__ */ new Map();
|
|
22147
22163
|
/** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
|
|
@@ -22160,7 +22176,7 @@ var ModelCatalog = class {
|
|
|
22160
22176
|
supportedApis: info.supportedApis ?? [info.preferredApi ?? "completions"],
|
|
22161
22177
|
contextWindow: info.contextWindow,
|
|
22162
22178
|
maxOutput: info.maxOutput,
|
|
22163
|
-
capabilities: { ...DEFAULT_CAPABILITIES, ...info.capabilities },
|
|
22179
|
+
capabilities: withBuiltinTools(provider, { ...DEFAULT_CAPABILITIES, ...info.capabilities }),
|
|
22164
22180
|
reasoning: { ...DEFAULT_REASONING, ...info.reasoning },
|
|
22165
22181
|
mediaOnly: info.mediaOnly,
|
|
22166
22182
|
tokenizer: info.tokenizer,
|
|
@@ -22214,6 +22230,15 @@ var ModelCatalog = class {
|
|
|
22214
22230
|
supportsTools(provider, model) {
|
|
22215
22231
|
return this.get(provider, model)?.capabilities.toolUse ?? false;
|
|
22216
22232
|
}
|
|
22233
|
+
/** Hosted server-side builtin tools this model supports (e.g. `['web_search',
|
|
22234
|
+
* 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
|
|
22235
|
+
builtinToolsFor(provider, model) {
|
|
22236
|
+
return [...this.get(provider, model)?.capabilities.builtinTools ?? []];
|
|
22237
|
+
}
|
|
22238
|
+
/** Whether this model supports a specific hosted builtin tool. */
|
|
22239
|
+
supportsBuiltinTool(provider, model, tool) {
|
|
22240
|
+
return this.get(provider, model)?.capabilities.builtinTools?.includes(tool) ?? false;
|
|
22241
|
+
}
|
|
22217
22242
|
supportsPreviousResponseId(provider, model) {
|
|
22218
22243
|
const info = this.get(provider, model);
|
|
22219
22244
|
if (info && !info.supportedApis.some((a) => a === "responses" || a === "interactions")) {
|
|
@@ -26216,7 +26241,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26216
26241
|
let text = "";
|
|
26217
26242
|
for (const item of output) {
|
|
26218
26243
|
const type = item.type;
|
|
26219
|
-
files.push(...
|
|
26244
|
+
files.push(...this.filesFromOutputItem(item));
|
|
26220
26245
|
if (type === "message") {
|
|
26221
26246
|
const itemContent = item.content ?? [];
|
|
26222
26247
|
for (const c of itemContent) {
|
|
@@ -26318,7 +26343,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26318
26343
|
}
|
|
26319
26344
|
if (type === "response.output_item.done") {
|
|
26320
26345
|
const item = data.item;
|
|
26321
|
-
for (const file of
|
|
26346
|
+
for (const file of this.filesFromOutputItem(item)) events.push({ type: "file", file });
|
|
26322
26347
|
if (item?.type === "function_call") {
|
|
26323
26348
|
events.push({ type: "tool_call_end", id: item.call_id ?? "" });
|
|
26324
26349
|
}
|
|
@@ -26354,6 +26379,12 @@ var OpenAIResponsesAdapter = class {
|
|
|
26354
26379
|
createStreamParser() {
|
|
26355
26380
|
return (event) => this.parseStreamEvent(event);
|
|
26356
26381
|
}
|
|
26382
|
+
/** Hosted code-execution output files from one output item. Overridable so
|
|
26383
|
+
* Responses-compatible providers with a different file shape (e.g. xAI, which
|
|
26384
|
+
* embeds files in the code-interpreter `logs` payload) can extend it. */
|
|
26385
|
+
filesFromOutputItem(item) {
|
|
26386
|
+
return filesFromResponsesOutputItem(item);
|
|
26387
|
+
}
|
|
26357
26388
|
parseUsage(u) {
|
|
26358
26389
|
if (!u) return emptyUsage();
|
|
26359
26390
|
const input = u.input_tokens ?? 0;
|
|
@@ -26918,6 +26949,29 @@ var XAIMediaAdapter = class {
|
|
|
26918
26949
|
};
|
|
26919
26950
|
|
|
26920
26951
|
// src/llm/providers/xai/responses.ts
|
|
26952
|
+
function xaiCodeExecFiles(item) {
|
|
26953
|
+
if (item.type !== "code_interpreter_call") return [];
|
|
26954
|
+
const files = [];
|
|
26955
|
+
for (const out of item.outputs ?? []) {
|
|
26956
|
+
if (out.type !== "logs" || typeof out.logs !== "string") continue;
|
|
26957
|
+
let parsed;
|
|
26958
|
+
try {
|
|
26959
|
+
parsed = JSON.parse(out.logs);
|
|
26960
|
+
} catch {
|
|
26961
|
+
continue;
|
|
26962
|
+
}
|
|
26963
|
+
for (const f of parsed.output_files ?? []) {
|
|
26964
|
+
if (!Array.isArray(f.data)) continue;
|
|
26965
|
+
files.push({
|
|
26966
|
+
data: bytesToBase64(Uint8Array.from(f.data)),
|
|
26967
|
+
...typeof f.file_name === "string" ? { name: f.file_name } : {},
|
|
26968
|
+
...typeof f.mime_type === "string" ? { mimeType: f.mime_type } : {},
|
|
26969
|
+
source: "code_execution"
|
|
26970
|
+
});
|
|
26971
|
+
}
|
|
26972
|
+
}
|
|
26973
|
+
return files;
|
|
26974
|
+
}
|
|
26921
26975
|
var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
26922
26976
|
name = "xai";
|
|
26923
26977
|
constructor(config) {
|
|
@@ -26937,8 +26991,20 @@ var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
|
26937
26991
|
if (!req.model.includes("multi-agent")) {
|
|
26938
26992
|
delete body.reasoning;
|
|
26939
26993
|
}
|
|
26994
|
+
const usesCodeInterpreter = req.tools?.some(
|
|
26995
|
+
(t) => !isFunctionTool(t) && t.type === "code_interpreter"
|
|
26996
|
+
);
|
|
26997
|
+
if (usesCodeInterpreter) {
|
|
26998
|
+
const include = /* @__PURE__ */ new Set([...body.include ?? [], "code_interpreter_call.outputs"]);
|
|
26999
|
+
body.include = [...include];
|
|
27000
|
+
}
|
|
26940
27001
|
return result;
|
|
26941
27002
|
}
|
|
27003
|
+
/** xAI embeds code-execution files inline in the `logs` payload — extend the base
|
|
27004
|
+
* extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
|
|
27005
|
+
filesFromOutputItem(item) {
|
|
27006
|
+
return [...super.filesFromOutputItem(item), ...xaiCodeExecFiles(item)];
|
|
27007
|
+
}
|
|
26942
27008
|
};
|
|
26943
27009
|
|
|
26944
27010
|
// src/util/json-schema.ts
|
|
@@ -36863,12 +36929,16 @@ var DEFAULT_TAGS = {
|
|
|
36863
36929
|
huge: "context:large"
|
|
36864
36930
|
};
|
|
36865
36931
|
var CAP_KEYS = {
|
|
36866
|
-
search: "webSearch",
|
|
36867
36932
|
vision: "vision",
|
|
36868
36933
|
tools: "toolUse",
|
|
36869
36934
|
audio: "audio",
|
|
36870
36935
|
structured: "structuredOutput"
|
|
36871
36936
|
};
|
|
36937
|
+
var BUILTIN_TOOL_KEYS = {
|
|
36938
|
+
search: "web_search",
|
|
36939
|
+
web_search: "web_search",
|
|
36940
|
+
code_interpreter: "code_interpreter"
|
|
36941
|
+
};
|
|
36872
36942
|
var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
36873
36943
|
"price",
|
|
36874
36944
|
"context",
|
|
@@ -36878,7 +36948,8 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
36878
36948
|
"status",
|
|
36879
36949
|
"provider",
|
|
36880
36950
|
"active",
|
|
36881
|
-
...Object.keys(CAP_KEYS)
|
|
36951
|
+
...Object.keys(CAP_KEYS),
|
|
36952
|
+
...Object.keys(BUILTIN_TOOL_KEYS)
|
|
36882
36953
|
]);
|
|
36883
36954
|
function parseNum(v) {
|
|
36884
36955
|
const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
|
|
@@ -36947,6 +37018,13 @@ function matches(m, c, th, tier) {
|
|
|
36947
37018
|
case "active":
|
|
36948
37019
|
return isNo(c.value) ? m.active === false : m.active !== false;
|
|
36949
37020
|
default: {
|
|
37021
|
+
const tool = BUILTIN_TOOL_KEYS[c.key];
|
|
37022
|
+
if (tool) {
|
|
37023
|
+
const inList = m.capabilities.builtinTools?.includes(tool) ?? false;
|
|
37024
|
+
const legacy = tool === "web_search" && !!m.capabilities.webSearch;
|
|
37025
|
+
const has2 = inList || legacy;
|
|
37026
|
+
return isNo(c.value) ? !has2 : has2;
|
|
37027
|
+
}
|
|
36950
37028
|
const cap = CAP_KEYS[c.key];
|
|
36951
37029
|
const has = !!m.capabilities[cap];
|
|
36952
37030
|
return isNo(c.value) ? !has : has;
|
package/dist/index.js
CHANGED
|
@@ -22032,6 +22032,15 @@ var catalog_default5 = {
|
|
|
22032
22032
|
}
|
|
22033
22033
|
};
|
|
22034
22034
|
|
|
22035
|
+
// src/llm/providers/builtin-tools.ts
|
|
22036
|
+
var PROVIDER_BUILTIN_TOOLS = {
|
|
22037
|
+
anthropic: ["web_search", "code_interpreter"],
|
|
22038
|
+
openai: ["web_search", "code_interpreter"],
|
|
22039
|
+
google: ["web_search", "code_interpreter"],
|
|
22040
|
+
xai: ["web_search", "code_interpreter"],
|
|
22041
|
+
openrouter: ["web_search"]
|
|
22042
|
+
};
|
|
22043
|
+
|
|
22035
22044
|
// src/plugins/model-catalog/catalog.ts
|
|
22036
22045
|
var PROVIDER_DEFAULT_CATALOGS = [
|
|
22037
22046
|
catalog_default,
|
|
@@ -22069,6 +22078,13 @@ var DEFAULT_REASONING = {
|
|
|
22069
22078
|
encryptedContent: false,
|
|
22070
22079
|
summaryAvailable: false
|
|
22071
22080
|
};
|
|
22081
|
+
function withBuiltinTools(provider, caps) {
|
|
22082
|
+
if (caps.builtinTools !== void 0) return caps;
|
|
22083
|
+
if (!caps.toolUse) return caps;
|
|
22084
|
+
const tools = PROVIDER_BUILTIN_TOOLS[provider];
|
|
22085
|
+
if (!tools || tools.length === 0) return caps;
|
|
22086
|
+
return { ...caps, builtinTools: [...tools] };
|
|
22087
|
+
}
|
|
22072
22088
|
var ModelCatalog = class {
|
|
22073
22089
|
models = /* @__PURE__ */ new Map();
|
|
22074
22090
|
/** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
|
|
@@ -22087,7 +22103,7 @@ var ModelCatalog = class {
|
|
|
22087
22103
|
supportedApis: info.supportedApis ?? [info.preferredApi ?? "completions"],
|
|
22088
22104
|
contextWindow: info.contextWindow,
|
|
22089
22105
|
maxOutput: info.maxOutput,
|
|
22090
|
-
capabilities: { ...DEFAULT_CAPABILITIES, ...info.capabilities },
|
|
22106
|
+
capabilities: withBuiltinTools(provider, { ...DEFAULT_CAPABILITIES, ...info.capabilities }),
|
|
22091
22107
|
reasoning: { ...DEFAULT_REASONING, ...info.reasoning },
|
|
22092
22108
|
mediaOnly: info.mediaOnly,
|
|
22093
22109
|
tokenizer: info.tokenizer,
|
|
@@ -22141,6 +22157,15 @@ var ModelCatalog = class {
|
|
|
22141
22157
|
supportsTools(provider, model) {
|
|
22142
22158
|
return this.get(provider, model)?.capabilities.toolUse ?? false;
|
|
22143
22159
|
}
|
|
22160
|
+
/** Hosted server-side builtin tools this model supports (e.g. `['web_search',
|
|
22161
|
+
* 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
|
|
22162
|
+
builtinToolsFor(provider, model) {
|
|
22163
|
+
return [...this.get(provider, model)?.capabilities.builtinTools ?? []];
|
|
22164
|
+
}
|
|
22165
|
+
/** Whether this model supports a specific hosted builtin tool. */
|
|
22166
|
+
supportsBuiltinTool(provider, model, tool) {
|
|
22167
|
+
return this.get(provider, model)?.capabilities.builtinTools?.includes(tool) ?? false;
|
|
22168
|
+
}
|
|
22144
22169
|
supportsPreviousResponseId(provider, model) {
|
|
22145
22170
|
const info = this.get(provider, model);
|
|
22146
22171
|
if (info && !info.supportedApis.some((a) => a === "responses" || a === "interactions")) {
|
|
@@ -26143,7 +26168,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26143
26168
|
let text = "";
|
|
26144
26169
|
for (const item of output) {
|
|
26145
26170
|
const type = item.type;
|
|
26146
|
-
files.push(...
|
|
26171
|
+
files.push(...this.filesFromOutputItem(item));
|
|
26147
26172
|
if (type === "message") {
|
|
26148
26173
|
const itemContent = item.content ?? [];
|
|
26149
26174
|
for (const c of itemContent) {
|
|
@@ -26245,7 +26270,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26245
26270
|
}
|
|
26246
26271
|
if (type === "response.output_item.done") {
|
|
26247
26272
|
const item = data.item;
|
|
26248
|
-
for (const file of
|
|
26273
|
+
for (const file of this.filesFromOutputItem(item)) events.push({ type: "file", file });
|
|
26249
26274
|
if (item?.type === "function_call") {
|
|
26250
26275
|
events.push({ type: "tool_call_end", id: item.call_id ?? "" });
|
|
26251
26276
|
}
|
|
@@ -26281,6 +26306,12 @@ var OpenAIResponsesAdapter = class {
|
|
|
26281
26306
|
createStreamParser() {
|
|
26282
26307
|
return (event) => this.parseStreamEvent(event);
|
|
26283
26308
|
}
|
|
26309
|
+
/** Hosted code-execution output files from one output item. Overridable so
|
|
26310
|
+
* Responses-compatible providers with a different file shape (e.g. xAI, which
|
|
26311
|
+
* embeds files in the code-interpreter `logs` payload) can extend it. */
|
|
26312
|
+
filesFromOutputItem(item) {
|
|
26313
|
+
return filesFromResponsesOutputItem(item);
|
|
26314
|
+
}
|
|
26284
26315
|
parseUsage(u) {
|
|
26285
26316
|
if (!u) return emptyUsage();
|
|
26286
26317
|
const input = u.input_tokens ?? 0;
|
|
@@ -26845,6 +26876,29 @@ var XAIMediaAdapter = class {
|
|
|
26845
26876
|
};
|
|
26846
26877
|
|
|
26847
26878
|
// src/llm/providers/xai/responses.ts
|
|
26879
|
+
function xaiCodeExecFiles(item) {
|
|
26880
|
+
if (item.type !== "code_interpreter_call") return [];
|
|
26881
|
+
const files = [];
|
|
26882
|
+
for (const out of item.outputs ?? []) {
|
|
26883
|
+
if (out.type !== "logs" || typeof out.logs !== "string") continue;
|
|
26884
|
+
let parsed;
|
|
26885
|
+
try {
|
|
26886
|
+
parsed = JSON.parse(out.logs);
|
|
26887
|
+
} catch {
|
|
26888
|
+
continue;
|
|
26889
|
+
}
|
|
26890
|
+
for (const f of parsed.output_files ?? []) {
|
|
26891
|
+
if (!Array.isArray(f.data)) continue;
|
|
26892
|
+
files.push({
|
|
26893
|
+
data: bytesToBase64(Uint8Array.from(f.data)),
|
|
26894
|
+
...typeof f.file_name === "string" ? { name: f.file_name } : {},
|
|
26895
|
+
...typeof f.mime_type === "string" ? { mimeType: f.mime_type } : {},
|
|
26896
|
+
source: "code_execution"
|
|
26897
|
+
});
|
|
26898
|
+
}
|
|
26899
|
+
}
|
|
26900
|
+
return files;
|
|
26901
|
+
}
|
|
26848
26902
|
var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
26849
26903
|
name = "xai";
|
|
26850
26904
|
constructor(config) {
|
|
@@ -26864,8 +26918,20 @@ var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
|
26864
26918
|
if (!req.model.includes("multi-agent")) {
|
|
26865
26919
|
delete body.reasoning;
|
|
26866
26920
|
}
|
|
26921
|
+
const usesCodeInterpreter = req.tools?.some(
|
|
26922
|
+
(t) => !isFunctionTool(t) && t.type === "code_interpreter"
|
|
26923
|
+
);
|
|
26924
|
+
if (usesCodeInterpreter) {
|
|
26925
|
+
const include = /* @__PURE__ */ new Set([...body.include ?? [], "code_interpreter_call.outputs"]);
|
|
26926
|
+
body.include = [...include];
|
|
26927
|
+
}
|
|
26867
26928
|
return result;
|
|
26868
26929
|
}
|
|
26930
|
+
/** xAI embeds code-execution files inline in the `logs` payload — extend the base
|
|
26931
|
+
* extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
|
|
26932
|
+
filesFromOutputItem(item) {
|
|
26933
|
+
return [...super.filesFromOutputItem(item), ...xaiCodeExecFiles(item)];
|
|
26934
|
+
}
|
|
26869
26935
|
};
|
|
26870
26936
|
|
|
26871
26937
|
// src/util/json-schema.ts
|
|
@@ -36790,12 +36856,16 @@ var DEFAULT_TAGS = {
|
|
|
36790
36856
|
huge: "context:large"
|
|
36791
36857
|
};
|
|
36792
36858
|
var CAP_KEYS = {
|
|
36793
|
-
search: "webSearch",
|
|
36794
36859
|
vision: "vision",
|
|
36795
36860
|
tools: "toolUse",
|
|
36796
36861
|
audio: "audio",
|
|
36797
36862
|
structured: "structuredOutput"
|
|
36798
36863
|
};
|
|
36864
|
+
var BUILTIN_TOOL_KEYS = {
|
|
36865
|
+
search: "web_search",
|
|
36866
|
+
web_search: "web_search",
|
|
36867
|
+
code_interpreter: "code_interpreter"
|
|
36868
|
+
};
|
|
36799
36869
|
var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
36800
36870
|
"price",
|
|
36801
36871
|
"context",
|
|
@@ -36805,7 +36875,8 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
36805
36875
|
"status",
|
|
36806
36876
|
"provider",
|
|
36807
36877
|
"active",
|
|
36808
|
-
...Object.keys(CAP_KEYS)
|
|
36878
|
+
...Object.keys(CAP_KEYS),
|
|
36879
|
+
...Object.keys(BUILTIN_TOOL_KEYS)
|
|
36809
36880
|
]);
|
|
36810
36881
|
function parseNum(v) {
|
|
36811
36882
|
const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
|
|
@@ -36874,6 +36945,13 @@ function matches(m, c, th, tier) {
|
|
|
36874
36945
|
case "active":
|
|
36875
36946
|
return isNo(c.value) ? m.active === false : m.active !== false;
|
|
36876
36947
|
default: {
|
|
36948
|
+
const tool = BUILTIN_TOOL_KEYS[c.key];
|
|
36949
|
+
if (tool) {
|
|
36950
|
+
const inList = m.capabilities.builtinTools?.includes(tool) ?? false;
|
|
36951
|
+
const legacy = tool === "web_search" && !!m.capabilities.webSearch;
|
|
36952
|
+
const has2 = inList || legacy;
|
|
36953
|
+
return isNo(c.value) ? !has2 : has2;
|
|
36954
|
+
}
|
|
36877
36955
|
const cap = CAP_KEYS[c.key];
|
|
36878
36956
|
const has = !!m.capabilities[cap];
|
|
36879
36957
|
return isNo(c.value) ? !has : has;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Hosted server-side tools each provider's chat models support.
|
|
2
|
+
*
|
|
3
|
+
* This is ADAPTER-SOURCED: it reflects what the provider adapters actually map
|
|
4
|
+
* the unified `{ type: 'web_search' | 'code_interpreter' }` builtins onto (each
|
|
5
|
+
* to the provider's native shape), verified by the live tools-parity test. It is
|
|
6
|
+
* applied PROVIDER-LEVEL to tool-capable (chat-family) models at catalog load —
|
|
7
|
+
* a deliberate first pass; a reliable per-model source can refine it later.
|
|
8
|
+
*
|
|
9
|
+
* Coverage (verified live 2026-07):
|
|
10
|
+
* - web_search → anthropic, openai, google, xai, openrouter
|
|
11
|
+
* - code_interpreter → anthropic, openai, google, xai (NOT openrouter: it
|
|
12
|
+
* proxies function tools + its own plugins, but does not route hosted code
|
|
13
|
+
* execution — confirmed against the API). */
|
|
14
|
+
import type { ProviderName } from '../types/provider';
|
|
15
|
+
export declare const PROVIDER_BUILTIN_TOOLS: Record<ProviderName, readonly string[]>;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { SSEEvent } from '../../../network/types';
|
|
6
6
|
import type { ProviderAdapter, ProviderHttpRequest } from '../../types/provider';
|
|
7
7
|
import type { NormalizedRequest } from '../../types/request';
|
|
8
|
-
import { type CompletionResponse, type Usage } from '../../types/response';
|
|
8
|
+
import { type CompletionResponse, type FileOutput, type Usage } from '../../types/response';
|
|
9
9
|
import type { StreamEvent } from '../../types/stream';
|
|
10
10
|
export interface OpenAIResponsesAdapterConfig {
|
|
11
11
|
apiKey: string;
|
|
@@ -28,5 +28,9 @@ export declare class OpenAIResponsesAdapter implements ProviderAdapter {
|
|
|
28
28
|
/** Stateless — each output item finalizes with all its file annotations in a
|
|
29
29
|
* single response.output_item.done event. */
|
|
30
30
|
createStreamParser(): (event: SSEEvent) => StreamEvent[];
|
|
31
|
+
/** Hosted code-execution output files from one output item. Overridable so
|
|
32
|
+
* Responses-compatible providers with a different file shape (e.g. xAI, which
|
|
33
|
+
* embeds files in the code-interpreter `logs` payload) can extend it. */
|
|
34
|
+
protected filesFromOutputItem(item: Record<string, unknown>): FileOutput[];
|
|
31
35
|
protected parseUsage(u: Record<string, unknown> | undefined): Usage;
|
|
32
36
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ProviderAdapter, ProviderHttpRequest } from '../../types/provider';
|
|
9
9
|
import type { NormalizedRequest } from '../../types/request';
|
|
10
|
+
import type { FileOutput } from '../../types/response';
|
|
10
11
|
import { OpenAIResponsesAdapter } from '../openai/responses';
|
|
11
12
|
export interface XAIResponsesAdapterConfig {
|
|
12
13
|
apiKey: string;
|
|
@@ -17,4 +18,7 @@ export declare class XAIResponsesAdapter extends OpenAIResponsesAdapter {
|
|
|
17
18
|
constructor(config: XAIResponsesAdapterConfig);
|
|
18
19
|
baseURL(): string;
|
|
19
20
|
buildRequest(req: NormalizedRequest): ProviderHttpRequest;
|
|
21
|
+
/** xAI embeds code-execution files inline in the `logs` payload — extend the base
|
|
22
|
+
* extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
|
|
23
|
+
protected filesFromOutputItem(item: Record<string, unknown>): FileOutput[];
|
|
20
24
|
}
|
|
@@ -140,6 +140,11 @@ export declare class ModelCatalog {
|
|
|
140
140
|
getPreferredApi(provider: string, model: string): ApiType | null;
|
|
141
141
|
supportsApi(provider: string, model: string, api: ApiType): boolean;
|
|
142
142
|
supportsTools(provider: string, model: string): boolean;
|
|
143
|
+
/** Hosted server-side builtin tools this model supports (e.g. `['web_search',
|
|
144
|
+
* 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
|
|
145
|
+
builtinToolsFor(provider: string, model: string): string[];
|
|
146
|
+
/** Whether this model supports a specific hosted builtin tool. */
|
|
147
|
+
supportsBuiltinTool(provider: string, model: string, tool: string): boolean;
|
|
143
148
|
supportsPreviousResponseId(provider: string, model: string): boolean;
|
|
144
149
|
/** Server-state retention as a duration string ("30d", "72h"), or null if unsupported. */
|
|
145
150
|
getStateRetention(provider: string, model: string): string | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@combycode/llm-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Unified, pluggable AI SDK for accessing the LLMs of every major provider (Anthropic, OpenAI, Google, xAI, OpenRouter) through one API. Cross-environment: Node, Bun, and the browser.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|