@tanstack/openai-base 0.9.9 → 0.9.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/esm/adapters/chat-completions-text.js +844 -962
  2. package/dist/esm/adapters/chat-completions-text.js.map +1 -1
  3. package/dist/esm/adapters/chat-completions-tool-converter.js +54 -38
  4. package/dist/esm/adapters/chat-completions-tool-converter.js.map +1 -1
  5. package/dist/esm/adapters/responses-text.d.ts +22 -8
  6. package/dist/esm/adapters/responses-text.js +1193 -1319
  7. package/dist/esm/adapters/responses-text.js.map +1 -1
  8. package/dist/esm/adapters/responses-tool-converter.js +50 -34
  9. package/dist/esm/adapters/responses-tool-converter.js.map +1 -1
  10. package/dist/esm/index.d.ts +1 -1
  11. package/dist/esm/index.js +4 -43
  12. package/dist/esm/tools/apply-patch-tool.js +20 -13
  13. package/dist/esm/tools/apply-patch-tool.js.map +1 -1
  14. package/dist/esm/tools/code-interpreter-tool.js +26 -18
  15. package/dist/esm/tools/code-interpreter-tool.js.map +1 -1
  16. package/dist/esm/tools/computer-use-tool.js +26 -19
  17. package/dist/esm/tools/computer-use-tool.js.map +1 -1
  18. package/dist/esm/tools/custom-tool.js +23 -21
  19. package/dist/esm/tools/custom-tool.js.map +1 -1
  20. package/dist/esm/tools/file-search-tool.js +30 -30
  21. package/dist/esm/tools/file-search-tool.js.map +1 -1
  22. package/dist/esm/tools/function-tool.js +44 -31
  23. package/dist/esm/tools/function-tool.js.map +1 -1
  24. package/dist/esm/tools/image-generation-tool.js +30 -23
  25. package/dist/esm/tools/image-generation-tool.js.map +1 -1
  26. package/dist/esm/tools/local-shell-tool.js +20 -13
  27. package/dist/esm/tools/local-shell-tool.js.map +1 -1
  28. package/dist/esm/tools/mcp-tool.js +31 -25
  29. package/dist/esm/tools/mcp-tool.js.map +1 -1
  30. package/dist/esm/tools/shell-tool.js +25 -21
  31. package/dist/esm/tools/shell-tool.js.map +1 -1
  32. package/dist/esm/tools/tool-converter.js +37 -46
  33. package/dist/esm/tools/tool-converter.js.map +1 -1
  34. package/dist/esm/tools/web-search-preview-tool.js +26 -15
  35. package/dist/esm/tools/web-search-preview-tool.js.map +1 -1
  36. package/dist/esm/tools/web-search-tool.js +27 -15
  37. package/dist/esm/tools/web-search-tool.js.map +1 -1
  38. package/dist/esm/usage.js +88 -76
  39. package/dist/esm/usage.js.map +1 -1
  40. package/dist/esm/utils/request-options.js +20 -9
  41. package/dist/esm/utils/request-options.js.map +1 -1
  42. package/dist/esm/utils/schema-converter.js +193 -146
  43. package/dist/esm/utils/schema-converter.js.map +1 -1
  44. package/package.json +7 -7
  45. package/src/adapters/responses-text.ts +84 -57
  46. package/src/index.ts +4 -1
  47. package/dist/esm/index.js.map +0 -1
@@ -1,31 +1,37 @@
1
+ //#region src/tools/mcp-tool.ts
1
2
  function validateMCPtool(tool) {
2
- if (!tool.server_url && !tool.connector_id) {
3
- throw new Error("Either server_url or connector_id must be provided.");
4
- }
5
- if (tool.connector_id && tool.server_url) {
6
- throw new Error("Only one of server_url or connector_id can be provided.");
7
- }
3
+ if (!tool.server_url && !tool.connector_id) throw new Error("Either server_url or connector_id must be provided.");
4
+ if (tool.connector_id && tool.server_url) throw new Error("Only one of server_url or connector_id can be provided.");
8
5
  }
6
+ /**
7
+ * Converts a standard Tool to OpenAI MCPTool format
8
+ */
9
9
  function convertMCPToolToAdapterFormat(tool) {
10
- const metadata = tool.metadata;
11
- const mcpTool2 = {
12
- ...metadata,
13
- type: "mcp"
14
- };
15
- validateMCPtool(mcpTool2);
16
- return mcpTool2;
10
+ const mcpTool = {
11
+ ...tool.metadata,
12
+ type: "mcp"
13
+ };
14
+ validateMCPtool(mcpTool);
15
+ return mcpTool;
17
16
  }
17
+ /**
18
+ * Creates a standard Tool from MCPTool parameters.
19
+ *
20
+ * Base (non-branded) factory. Providers that need branded return types should
21
+ * re-wrap this in their own package.
22
+ */
18
23
  function mcpTool(toolData) {
19
- validateMCPtool({ ...toolData });
20
- return {
21
- name: "mcp",
22
- description: toolData.server_description || "",
23
- metadata: toolData
24
- };
24
+ validateMCPtool({
25
+ ...toolData,
26
+ type: "mcp"
27
+ });
28
+ return {
29
+ name: "mcp",
30
+ description: toolData.server_description || "",
31
+ metadata: toolData
32
+ };
25
33
  }
26
- export {
27
- convertMCPToolToAdapterFormat,
28
- mcpTool,
29
- validateMCPtool
30
- };
31
- //# sourceMappingURL=mcp-tool.js.map
34
+ //#endregion
35
+ export { convertMCPToolToAdapterFormat, mcpTool, validateMCPtool };
36
+
37
+ //# sourceMappingURL=mcp-tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tool.js","sources":["../../../src/tools/mcp-tool.ts"],"sourcesContent":["import type { Tool as SDKTool } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\ntype MCPToolConfig = SDKTool.Mcp\n\nexport type { MCPToolConfig }\n\n/** @deprecated Renamed to `MCPToolConfig`. Will be removed in a future release. */\nexport type MCPTool = MCPToolConfig\n\nexport function validateMCPtool(tool: MCPToolConfig) {\n if (!tool.server_url && !tool.connector_id) {\n throw new Error('Either server_url or connector_id must be provided.')\n }\n if (tool.connector_id && tool.server_url) {\n throw new Error('Only one of server_url or connector_id can be provided.')\n }\n}\n\n/**\n * Converts a standard Tool to OpenAI MCPTool format\n */\nexport function convertMCPToolToAdapterFormat(tool: Tool): MCPToolConfig {\n const metadata = tool.metadata as Omit<MCPToolConfig, 'type'>\n\n const mcpTool: MCPToolConfig = {\n ...metadata,\n type: 'mcp',\n }\n\n validateMCPtool(mcpTool)\n return mcpTool\n}\n\n/**\n * Creates a standard Tool from MCPTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function mcpTool(toolData: Omit<MCPToolConfig, 'type'>): Tool {\n validateMCPtool({ ...toolData, type: 'mcp' })\n\n return {\n name: 'mcp',\n description: toolData.server_description || '',\n metadata: toolData,\n }\n}\n"],"names":["mcpTool"],"mappings":"AAUO,SAAS,gBAAgB,MAAqB;AACnD,MAAI,CAAC,KAAK,cAAc,CAAC,KAAK,cAAc;AAC1C,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,MAAI,KAAK,gBAAgB,KAAK,YAAY;AACxC,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACF;AAKO,SAAS,8BAA8B,MAA2B;AACvE,QAAM,WAAW,KAAK;AAEtB,QAAMA,WAAyB;AAAA,IAC7B,GAAG;AAAA,IACH,MAAM;AAAA,EAAA;AAGR,kBAAgBA,QAAO;AACvB,SAAOA;AACT;AAQO,SAAS,QAAQ,UAA6C;AACnE,kBAAgB,EAAE,GAAG,SAAsB,CAAC;AAE5C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa,SAAS,sBAAsB;AAAA,IAC5C,UAAU;AAAA,EAAA;AAEd;"}
1
+ {"version":3,"file":"mcp-tool.js","names":[],"sources":["../../../src/tools/mcp-tool.ts"],"sourcesContent":["import type { Tool as SDKTool } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\ntype MCPToolConfig = SDKTool.Mcp\n\nexport type { MCPToolConfig }\n\n/** @deprecated Renamed to `MCPToolConfig`. Will be removed in a future release. */\nexport type MCPTool = MCPToolConfig\n\nexport function validateMCPtool(tool: MCPToolConfig) {\n if (!tool.server_url && !tool.connector_id) {\n throw new Error('Either server_url or connector_id must be provided.')\n }\n if (tool.connector_id && tool.server_url) {\n throw new Error('Only one of server_url or connector_id can be provided.')\n }\n}\n\n/**\n * Converts a standard Tool to OpenAI MCPTool format\n */\nexport function convertMCPToolToAdapterFormat(tool: Tool): MCPToolConfig {\n const metadata = tool.metadata as Omit<MCPToolConfig, 'type'>\n\n const mcpTool: MCPToolConfig = {\n ...metadata,\n type: 'mcp',\n }\n\n validateMCPtool(mcpTool)\n return mcpTool\n}\n\n/**\n * Creates a standard Tool from MCPTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function mcpTool(toolData: Omit<MCPToolConfig, 'type'>): Tool {\n validateMCPtool({ ...toolData, type: 'mcp' })\n\n return {\n name: 'mcp',\n description: toolData.server_description || '',\n metadata: toolData,\n }\n}\n"],"mappings":";AAUA,SAAgB,gBAAgB,MAAqB;CACnD,IAAI,CAAC,KAAK,cAAc,CAAC,KAAK,cAC5B,MAAM,IAAI,MAAM,qDAAqD;CAEvE,IAAI,KAAK,gBAAgB,KAAK,YAC5B,MAAM,IAAI,MAAM,yDAAyD;AAE7E;;;;AAKA,SAAgB,8BAA8B,MAA2B;CAGvE,MAAM,UAAyB;EAC7B,GAHe,KAAK;EAIpB,MAAM;CACR;CAEA,gBAAgB,OAAO;CACvB,OAAO;AACT;;;;;;;AAQA,SAAgB,QAAQ,UAA6C;CACnE,gBAAgB;EAAE,GAAG;EAAU,MAAM;CAAM,CAAC;CAE5C,OAAO;EACL,MAAM;EACN,aAAa,SAAS,sBAAsB;EAC5C,UAAU;CACZ;AACF"}
@@ -1,25 +1,29 @@
1
+ //#region src/tools/shell-tool.ts
2
+ /**
3
+ * Converts a standard Tool to OpenAI ShellTool format, preserving any
4
+ * `environment` (container config + skills) stored in metadata.
5
+ */
1
6
  function convertShellToolToAdapterFormat(tool) {
2
- const metadata = tool.metadata ?? {};
3
- return {
4
- type: "shell",
5
- ...metadata.environment !== void 0 && {
6
- environment: metadata.environment
7
- }
8
- };
7
+ const metadata = tool.metadata ?? {};
8
+ return {
9
+ type: "shell",
10
+ ...metadata.environment !== void 0 && { environment: metadata.environment }
11
+ };
9
12
  }
13
+ /**
14
+ * Creates a standard Tool from ShellTool parameters.
15
+ *
16
+ * Base (non-branded) factory. Providers that need branded return types should
17
+ * re-wrap this in their own package.
18
+ */
10
19
  function shellTool(config = {}) {
11
- return {
12
- name: "shell",
13
- description: "Execute shell commands",
14
- metadata: {
15
- ...config.environment !== void 0 && {
16
- environment: config.environment
17
- }
18
- }
19
- };
20
+ return {
21
+ name: "shell",
22
+ description: "Execute shell commands",
23
+ metadata: { ...config.environment !== void 0 && { environment: config.environment } }
24
+ };
20
25
  }
21
- export {
22
- convertShellToolToAdapterFormat,
23
- shellTool
24
- };
25
- //# sourceMappingURL=shell-tool.js.map
26
+ //#endregion
27
+ export { convertShellToolToAdapterFormat, shellTool };
28
+
29
+ //# sourceMappingURL=shell-tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shell-tool.js","sources":["../../../src/tools/shell-tool.ts"],"sourcesContent":["import type { FunctionShellTool as ShellToolConfig } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\nexport type { ShellToolConfig }\n\n/** @deprecated Renamed to `ShellToolConfig`. Will be removed in a future release. */\nexport type ShellTool = ShellToolConfig\n\n/**\n * Config accepted by {@link shellTool}. `environment` mirrors the OpenAI\n * Responses API shell tool environment (e.g. `container_auto` + `skills`).\n * Typed via indexed access so it tracks the installed SDK without naming the\n * union members directly.\n */\nexport interface ShellToolFactoryConfig {\n environment?: NonNullable<ShellToolConfig['environment']>\n}\n\n/**\n * Converts a standard Tool to OpenAI ShellTool format, preserving any\n * `environment` (container config + skills) stored in metadata.\n */\nexport function convertShellToolToAdapterFormat(tool: Tool): ShellToolConfig {\n const metadata = (tool.metadata ?? {}) as ShellToolFactoryConfig\n return {\n type: 'shell',\n ...(metadata.environment !== undefined && {\n environment: metadata.environment,\n }),\n }\n}\n\n/**\n * Creates a standard Tool from ShellTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function shellTool(config: ShellToolFactoryConfig = {}): Tool {\n return {\n name: 'shell',\n description: 'Execute shell commands',\n metadata: {\n ...(config.environment !== undefined && {\n environment: config.environment,\n }),\n },\n }\n}\n"],"names":[],"mappings":"AAsBO,SAAS,gCAAgC,MAA6B;AAC3E,QAAM,WAAY,KAAK,YAAY,CAAA;AACnC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,GAAI,SAAS,gBAAgB,UAAa;AAAA,MACxC,aAAa,SAAS;AAAA,IAAA;AAAA,EACxB;AAEJ;AAQO,SAAS,UAAU,SAAiC,IAAU;AACnE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,MACR,GAAI,OAAO,gBAAgB,UAAa;AAAA,QACtC,aAAa,OAAO;AAAA,MAAA;AAAA,IACtB;AAAA,EACF;AAEJ;"}
1
+ {"version":3,"file":"shell-tool.js","names":[],"sources":["../../../src/tools/shell-tool.ts"],"sourcesContent":["import type { FunctionShellTool as ShellToolConfig } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\nexport type { ShellToolConfig }\n\n/** @deprecated Renamed to `ShellToolConfig`. Will be removed in a future release. */\nexport type ShellTool = ShellToolConfig\n\n/**\n * Config accepted by {@link shellTool}. `environment` mirrors the OpenAI\n * Responses API shell tool environment (e.g. `container_auto` + `skills`).\n * Typed via indexed access so it tracks the installed SDK without naming the\n * union members directly.\n */\nexport interface ShellToolFactoryConfig {\n environment?: NonNullable<ShellToolConfig['environment']>\n}\n\n/**\n * Converts a standard Tool to OpenAI ShellTool format, preserving any\n * `environment` (container config + skills) stored in metadata.\n */\nexport function convertShellToolToAdapterFormat(tool: Tool): ShellToolConfig {\n const metadata = (tool.metadata ?? {}) as ShellToolFactoryConfig\n return {\n type: 'shell',\n ...(metadata.environment !== undefined && {\n environment: metadata.environment,\n }),\n }\n}\n\n/**\n * Creates a standard Tool from ShellTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function shellTool(config: ShellToolFactoryConfig = {}): Tool {\n return {\n name: 'shell',\n description: 'Execute shell commands',\n metadata: {\n ...(config.environment !== undefined && {\n environment: config.environment,\n }),\n },\n }\n}\n"],"mappings":";;;;;AAsBA,SAAgB,gCAAgC,MAA6B;CAC3E,MAAM,WAAY,KAAK,YAAY,CAAC;CACpC,OAAO;EACL,MAAM;EACN,GAAI,SAAS,gBAAgB,KAAA,KAAa,EACxC,aAAa,SAAS,YACxB;CACF;AACF;;;;;;;AAQA,SAAgB,UAAU,SAAiC,CAAC,GAAS;CACnE,OAAO;EACL,MAAM;EACN,aAAa;EACb,UAAU,EACR,GAAI,OAAO,gBAAgB,KAAA,KAAa,EACtC,aAAa,OAAO,YACtB,EACF;CACF;AACF"}
@@ -10,52 +10,43 @@ import { convertMCPToolToAdapterFormat } from "./mcp-tool.js";
10
10
  import { convertShellToolToAdapterFormat } from "./shell-tool.js";
11
11
  import { convertWebSearchPreviewToolToAdapterFormat } from "./web-search-preview-tool.js";
12
12
  import { convertWebSearchToolToAdapterFormat } from "./web-search-tool.js";
13
- const SPECIAL_TOOL_NAMES = /* @__PURE__ */ new Set([
14
- "apply_patch",
15
- "code_interpreter",
16
- "computer_use_preview",
17
- "file_search",
18
- "image_generation",
19
- "local_shell",
20
- "mcp",
21
- "shell",
22
- "web_search_preview",
23
- "web_search",
24
- "custom"
13
+ //#region src/tools/tool-converter.ts
14
+ var SPECIAL_TOOL_NAMES = /* @__PURE__ */ new Set([
15
+ "apply_patch",
16
+ "code_interpreter",
17
+ "computer_use_preview",
18
+ "file_search",
19
+ "image_generation",
20
+ "local_shell",
21
+ "mcp",
22
+ "shell",
23
+ "web_search_preview",
24
+ "web_search",
25
+ "custom"
25
26
  ]);
27
+ /**
28
+ * Converts an array of standard Tools to OpenAI-specific format
29
+ */
26
30
  function convertToolsToProviderFormat(tools) {
27
- return tools.map((tool) => {
28
- const toolName = tool.name;
29
- if (SPECIAL_TOOL_NAMES.has(toolName)) {
30
- switch (toolName) {
31
- case "apply_patch":
32
- return convertApplyPatchToolToAdapterFormat();
33
- case "code_interpreter":
34
- return convertCodeInterpreterToolToAdapterFormat(tool);
35
- case "computer_use_preview":
36
- return convertComputerUseToolToAdapterFormat(tool);
37
- case "file_search":
38
- return convertFileSearchToolToAdapterFormat(tool);
39
- case "image_generation":
40
- return convertImageGenerationToolToAdapterFormat(tool);
41
- case "local_shell":
42
- return convertLocalShellToolToAdapterFormat();
43
- case "mcp":
44
- return convertMCPToolToAdapterFormat(tool);
45
- case "shell":
46
- return convertShellToolToAdapterFormat(tool);
47
- case "web_search_preview":
48
- return convertWebSearchPreviewToolToAdapterFormat(tool);
49
- case "web_search":
50
- return convertWebSearchToolToAdapterFormat(tool);
51
- case "custom":
52
- return convertCustomToolToAdapterFormat(tool);
53
- }
54
- }
55
- return convertFunctionToolToAdapterFormat(tool);
56
- });
31
+ return tools.map((tool) => {
32
+ const toolName = tool.name;
33
+ if (SPECIAL_TOOL_NAMES.has(toolName)) switch (toolName) {
34
+ case "apply_patch": return convertApplyPatchToolToAdapterFormat(tool);
35
+ case "code_interpreter": return convertCodeInterpreterToolToAdapterFormat(tool);
36
+ case "computer_use_preview": return convertComputerUseToolToAdapterFormat(tool);
37
+ case "file_search": return convertFileSearchToolToAdapterFormat(tool);
38
+ case "image_generation": return convertImageGenerationToolToAdapterFormat(tool);
39
+ case "local_shell": return convertLocalShellToolToAdapterFormat(tool);
40
+ case "mcp": return convertMCPToolToAdapterFormat(tool);
41
+ case "shell": return convertShellToolToAdapterFormat(tool);
42
+ case "web_search_preview": return convertWebSearchPreviewToolToAdapterFormat(tool);
43
+ case "web_search": return convertWebSearchToolToAdapterFormat(tool);
44
+ case "custom": return convertCustomToolToAdapterFormat(tool);
45
+ }
46
+ return convertFunctionToolToAdapterFormat(tool);
47
+ });
57
48
  }
58
- export {
59
- convertToolsToProviderFormat
60
- };
61
- //# sourceMappingURL=tool-converter.js.map
49
+ //#endregion
50
+ export { convertToolsToProviderFormat };
51
+
52
+ //# sourceMappingURL=tool-converter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-converter.js","sources":["../../../src/tools/tool-converter.ts"],"sourcesContent":["import { convertApplyPatchToolToAdapterFormat } from './apply-patch-tool'\nimport { convertCodeInterpreterToolToAdapterFormat } from './code-interpreter-tool'\nimport { convertComputerUseToolToAdapterFormat } from './computer-use-tool'\nimport { convertCustomToolToAdapterFormat } from './custom-tool'\nimport { convertFileSearchToolToAdapterFormat } from './file-search-tool'\nimport { convertFunctionToolToAdapterFormat } from './function-tool'\nimport { convertImageGenerationToolToAdapterFormat } from './image-generation-tool'\nimport { convertLocalShellToolToAdapterFormat } from './local-shell-tool'\nimport { convertMCPToolToAdapterFormat } from './mcp-tool'\nimport { convertShellToolToAdapterFormat } from './shell-tool'\nimport { convertWebSearchPreviewToolToAdapterFormat } from './web-search-preview-tool'\nimport { convertWebSearchToolToAdapterFormat } from './web-search-tool'\nimport type { OpenAITool } from './index'\nimport type { Tool } from '@tanstack/ai'\n\nconst SPECIAL_TOOL_NAMES = new Set([\n 'apply_patch',\n 'code_interpreter',\n 'computer_use_preview',\n 'file_search',\n 'image_generation',\n 'local_shell',\n 'mcp',\n 'shell',\n 'web_search_preview',\n 'web_search',\n 'custom',\n])\n\n/**\n * Converts an array of standard Tools to OpenAI-specific format\n */\nexport function convertToolsToProviderFormat(\n tools: Array<Tool>,\n): Array<OpenAITool> {\n return tools.map((tool) => {\n const toolName = tool.name\n\n if (SPECIAL_TOOL_NAMES.has(toolName)) {\n switch (toolName) {\n case 'apply_patch':\n return convertApplyPatchToolToAdapterFormat(tool)\n case 'code_interpreter':\n return convertCodeInterpreterToolToAdapterFormat(tool)\n case 'computer_use_preview':\n return convertComputerUseToolToAdapterFormat(tool)\n case 'file_search':\n return convertFileSearchToolToAdapterFormat(tool)\n case 'image_generation':\n return convertImageGenerationToolToAdapterFormat(tool)\n case 'local_shell':\n return convertLocalShellToolToAdapterFormat(tool)\n case 'mcp':\n return convertMCPToolToAdapterFormat(tool)\n case 'shell':\n return convertShellToolToAdapterFormat(tool)\n case 'web_search_preview':\n return convertWebSearchPreviewToolToAdapterFormat(tool)\n case 'web_search':\n return convertWebSearchToolToAdapterFormat(tool)\n case 'custom':\n return convertCustomToolToAdapterFormat(tool)\n }\n }\n\n return convertFunctionToolToAdapterFormat(tool)\n })\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAeA,MAAM,yCAAyB,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKM,SAAS,6BACd,OACmB;AACnB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,WAAW,KAAK;AAEtB,QAAI,mBAAmB,IAAI,QAAQ,GAAG;AACpC,cAAQ,UAAA;AAAA,QACN,KAAK;AACH,iBAAO,qCAAyC;AAAA,QAClD,KAAK;AACH,iBAAO,0CAA0C,IAAI;AAAA,QACvD,KAAK;AACH,iBAAO,sCAAsC,IAAI;AAAA,QACnD,KAAK;AACH,iBAAO,qCAAqC,IAAI;AAAA,QAClD,KAAK;AACH,iBAAO,0CAA0C,IAAI;AAAA,QACvD,KAAK;AACH,iBAAO,qCAAyC;AAAA,QAClD,KAAK;AACH,iBAAO,8BAA8B,IAAI;AAAA,QAC3C,KAAK;AACH,iBAAO,gCAAgC,IAAI;AAAA,QAC7C,KAAK;AACH,iBAAO,2CAA2C,IAAI;AAAA,QACxD,KAAK;AACH,iBAAO,oCAAoC,IAAI;AAAA,QACjD,KAAK;AACH,iBAAO,iCAAiC,IAAI;AAAA,MAAA;AAAA,IAElD;AAEA,WAAO,mCAAmC,IAAI;AAAA,EAChD,CAAC;AACH;"}
1
+ {"version":3,"file":"tool-converter.js","names":[],"sources":["../../../src/tools/tool-converter.ts"],"sourcesContent":["import { convertApplyPatchToolToAdapterFormat } from './apply-patch-tool'\nimport { convertCodeInterpreterToolToAdapterFormat } from './code-interpreter-tool'\nimport { convertComputerUseToolToAdapterFormat } from './computer-use-tool'\nimport { convertCustomToolToAdapterFormat } from './custom-tool'\nimport { convertFileSearchToolToAdapterFormat } from './file-search-tool'\nimport { convertFunctionToolToAdapterFormat } from './function-tool'\nimport { convertImageGenerationToolToAdapterFormat } from './image-generation-tool'\nimport { convertLocalShellToolToAdapterFormat } from './local-shell-tool'\nimport { convertMCPToolToAdapterFormat } from './mcp-tool'\nimport { convertShellToolToAdapterFormat } from './shell-tool'\nimport { convertWebSearchPreviewToolToAdapterFormat } from './web-search-preview-tool'\nimport { convertWebSearchToolToAdapterFormat } from './web-search-tool'\nimport type { OpenAITool } from './index'\nimport type { Tool } from '@tanstack/ai'\n\nconst SPECIAL_TOOL_NAMES = new Set([\n 'apply_patch',\n 'code_interpreter',\n 'computer_use_preview',\n 'file_search',\n 'image_generation',\n 'local_shell',\n 'mcp',\n 'shell',\n 'web_search_preview',\n 'web_search',\n 'custom',\n])\n\n/**\n * Converts an array of standard Tools to OpenAI-specific format\n */\nexport function convertToolsToProviderFormat(\n tools: Array<Tool>,\n): Array<OpenAITool> {\n return tools.map((tool) => {\n const toolName = tool.name\n\n if (SPECIAL_TOOL_NAMES.has(toolName)) {\n switch (toolName) {\n case 'apply_patch':\n return convertApplyPatchToolToAdapterFormat(tool)\n case 'code_interpreter':\n return convertCodeInterpreterToolToAdapterFormat(tool)\n case 'computer_use_preview':\n return convertComputerUseToolToAdapterFormat(tool)\n case 'file_search':\n return convertFileSearchToolToAdapterFormat(tool)\n case 'image_generation':\n return convertImageGenerationToolToAdapterFormat(tool)\n case 'local_shell':\n return convertLocalShellToolToAdapterFormat(tool)\n case 'mcp':\n return convertMCPToolToAdapterFormat(tool)\n case 'shell':\n return convertShellToolToAdapterFormat(tool)\n case 'web_search_preview':\n return convertWebSearchPreviewToolToAdapterFormat(tool)\n case 'web_search':\n return convertWebSearchToolToAdapterFormat(tool)\n case 'custom':\n return convertCustomToolToAdapterFormat(tool)\n }\n }\n\n return convertFunctionToolToAdapterFormat(tool)\n })\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,IAAM,qCAAqB,IAAI,IAAI;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;AAKD,SAAgB,6BACd,OACmB;CACnB,OAAO,MAAM,KAAK,SAAS;EACzB,MAAM,WAAW,KAAK;EAEtB,IAAI,mBAAmB,IAAI,QAAQ,GACjC,QAAQ,UAAR;GACE,KAAK,eACH,OAAO,qCAAqC,IAAI;GAClD,KAAK,oBACH,OAAO,0CAA0C,IAAI;GACvD,KAAK,wBACH,OAAO,sCAAsC,IAAI;GACnD,KAAK,eACH,OAAO,qCAAqC,IAAI;GAClD,KAAK,oBACH,OAAO,0CAA0C,IAAI;GACvD,KAAK,eACH,OAAO,qCAAqC,IAAI;GAClD,KAAK,OACH,OAAO,8BAA8B,IAAI;GAC3C,KAAK,SACH,OAAO,gCAAgC,IAAI;GAC7C,KAAK,sBACH,OAAO,2CAA2C,IAAI;GACxD,KAAK,cACH,OAAO,oCAAoC,IAAI;GACjD,KAAK,UACH,OAAO,iCAAiC,IAAI;EAChD;EAGF,OAAO,mCAAmC,IAAI;CAChD,CAAC;AACH"}
@@ -1,19 +1,30 @@
1
+ //#region src/tools/web-search-preview-tool.ts
2
+ /**
3
+ * Converts a standard Tool to OpenAI WebSearchPreviewTool format. Force the
4
+ * literal `type: 'web_search_preview'` instead of trusting `metadata.type`,
5
+ * since a hand-authored tool with a missing or wrong `type` would emit a
6
+ * malformed payload while the dispatcher already routed by `tool.name`.
7
+ */
1
8
  function convertWebSearchPreviewToolToAdapterFormat(tool) {
2
- const metadata = tool.metadata;
3
- return {
4
- ...metadata,
5
- type: "web_search_preview"
6
- };
9
+ return {
10
+ ...tool.metadata,
11
+ type: "web_search_preview"
12
+ };
7
13
  }
14
+ /**
15
+ * Creates a standard Tool from WebSearchPreviewTool parameters.
16
+ *
17
+ * Base (non-branded) factory. Providers that need branded return types should
18
+ * re-wrap this in their own package.
19
+ */
8
20
  function webSearchPreviewTool(toolData) {
9
- return {
10
- name: "web_search_preview",
11
- description: "Search the web (preview version)",
12
- metadata: toolData
13
- };
21
+ return {
22
+ name: "web_search_preview",
23
+ description: "Search the web (preview version)",
24
+ metadata: toolData
25
+ };
14
26
  }
15
- export {
16
- convertWebSearchPreviewToolToAdapterFormat,
17
- webSearchPreviewTool
18
- };
19
- //# sourceMappingURL=web-search-preview-tool.js.map
27
+ //#endregion
28
+ export { convertWebSearchPreviewToolToAdapterFormat, webSearchPreviewTool };
29
+
30
+ //# sourceMappingURL=web-search-preview-tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-search-preview-tool.js","sources":["../../../src/tools/web-search-preview-tool.ts"],"sourcesContent":["import type { WebSearchPreviewTool as WebSearchPreviewToolConfig } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\nexport type { WebSearchPreviewToolConfig }\n\n/** @deprecated Renamed to `WebSearchPreviewToolConfig`. Will be removed in a future release. */\nexport type WebSearchPreviewTool = WebSearchPreviewToolConfig\n\n/**\n * Converts a standard Tool to OpenAI WebSearchPreviewTool format. Force the\n * literal `type: 'web_search_preview'` instead of trusting `metadata.type`,\n * since a hand-authored tool with a missing or wrong `type` would emit a\n * malformed payload while the dispatcher already routed by `tool.name`.\n */\nexport function convertWebSearchPreviewToolToAdapterFormat(\n tool: Tool,\n): WebSearchPreviewToolConfig {\n const metadata = tool.metadata as Omit<WebSearchPreviewToolConfig, 'type'>\n return {\n ...metadata,\n type: 'web_search_preview',\n }\n}\n\n/**\n * Creates a standard Tool from WebSearchPreviewTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function webSearchPreviewTool(\n toolData: WebSearchPreviewToolConfig,\n): Tool {\n return {\n name: 'web_search_preview',\n description: 'Search the web (preview version)',\n metadata: toolData,\n }\n}\n"],"names":[],"mappings":"AAcO,SAAS,2CACd,MAC4B;AAC5B,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM;AAAA,EAAA;AAEV;AAQO,SAAS,qBACd,UACM;AACN,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EAAA;AAEd;"}
1
+ {"version":3,"file":"web-search-preview-tool.js","names":[],"sources":["../../../src/tools/web-search-preview-tool.ts"],"sourcesContent":["import type { WebSearchPreviewTool as WebSearchPreviewToolConfig } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\nexport type { WebSearchPreviewToolConfig }\n\n/** @deprecated Renamed to `WebSearchPreviewToolConfig`. Will be removed in a future release. */\nexport type WebSearchPreviewTool = WebSearchPreviewToolConfig\n\n/**\n * Converts a standard Tool to OpenAI WebSearchPreviewTool format. Force the\n * literal `type: 'web_search_preview'` instead of trusting `metadata.type`,\n * since a hand-authored tool with a missing or wrong `type` would emit a\n * malformed payload while the dispatcher already routed by `tool.name`.\n */\nexport function convertWebSearchPreviewToolToAdapterFormat(\n tool: Tool,\n): WebSearchPreviewToolConfig {\n const metadata = tool.metadata as Omit<WebSearchPreviewToolConfig, 'type'>\n return {\n ...metadata,\n type: 'web_search_preview',\n }\n}\n\n/**\n * Creates a standard Tool from WebSearchPreviewTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function webSearchPreviewTool(\n toolData: WebSearchPreviewToolConfig,\n): Tool {\n return {\n name: 'web_search_preview',\n description: 'Search the web (preview version)',\n metadata: toolData,\n }\n}\n"],"mappings":";;;;;;;AAcA,SAAgB,2CACd,MAC4B;CAE5B,OAAO;EACL,GAFe,KAAK;EAGpB,MAAM;CACR;AACF;;;;;;;AAQA,SAAgB,qBACd,UACM;CACN,OAAO;EACL,MAAM;EACN,aAAa;EACb,UAAU;CACZ;AACF"}
@@ -1,19 +1,31 @@
1
+ //#region src/tools/web-search-tool.ts
2
+ /**
3
+ * Converts a standard Tool to OpenAI WebSearchTool format. Spread `metadata`
4
+ * first, then force `type: 'web_search'` last to keep the runtime `type`
5
+ * matching the discriminator the dispatcher routed by — otherwise a tool
6
+ * authored by hand with a different `metadata.type` would emit a malformed
7
+ * payload.
8
+ */
1
9
  function convertWebSearchToolToAdapterFormat(tool) {
2
- const metadata = tool.metadata;
3
- return {
4
- ...metadata,
5
- type: "web_search"
6
- };
10
+ return {
11
+ ...tool.metadata,
12
+ type: "web_search"
13
+ };
7
14
  }
15
+ /**
16
+ * Creates a standard Tool from WebSearchTool parameters.
17
+ *
18
+ * Base (non-branded) factory. Providers that need branded return types should
19
+ * re-wrap this in their own package.
20
+ */
8
21
  function webSearchTool(toolData) {
9
- return {
10
- name: "web_search",
11
- description: "Search the web",
12
- metadata: toolData
13
- };
22
+ return {
23
+ name: "web_search",
24
+ description: "Search the web",
25
+ metadata: toolData
26
+ };
14
27
  }
15
- export {
16
- convertWebSearchToolToAdapterFormat,
17
- webSearchTool
18
- };
19
- //# sourceMappingURL=web-search-tool.js.map
28
+ //#endregion
29
+ export { convertWebSearchToolToAdapterFormat, webSearchTool };
30
+
31
+ //# sourceMappingURL=web-search-tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-search-tool.js","sources":["../../../src/tools/web-search-tool.ts"],"sourcesContent":["import type { WebSearchTool as WebSearchToolConfig } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\nexport type { WebSearchToolConfig }\n\n/** @deprecated Renamed to `WebSearchToolConfig`. Will be removed in a future release. */\nexport type WebSearchTool = WebSearchToolConfig\n\n/**\n * Converts a standard Tool to OpenAI WebSearchTool format. Spread `metadata`\n * first, then force `type: 'web_search'` last to keep the runtime `type`\n * matching the discriminator the dispatcher routed by — otherwise a tool\n * authored by hand with a different `metadata.type` would emit a malformed\n * payload.\n */\nexport function convertWebSearchToolToAdapterFormat(\n tool: Tool,\n): WebSearchToolConfig {\n const metadata = tool.metadata as Omit<WebSearchToolConfig, 'type'>\n return {\n ...metadata,\n type: 'web_search',\n }\n}\n\n/**\n * Creates a standard Tool from WebSearchTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function webSearchTool(toolData: WebSearchToolConfig): Tool {\n return {\n name: 'web_search',\n description: 'Search the web',\n metadata: toolData,\n }\n}\n"],"names":[],"mappings":"AAeO,SAAS,oCACd,MACqB;AACrB,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM;AAAA,EAAA;AAEV;AAQO,SAAS,cAAc,UAAqC;AACjE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EAAA;AAEd;"}
1
+ {"version":3,"file":"web-search-tool.js","names":[],"sources":["../../../src/tools/web-search-tool.ts"],"sourcesContent":["import type { WebSearchTool as WebSearchToolConfig } from 'openai/resources/responses/responses'\nimport type { Tool } from '@tanstack/ai'\n\nexport type { WebSearchToolConfig }\n\n/** @deprecated Renamed to `WebSearchToolConfig`. Will be removed in a future release. */\nexport type WebSearchTool = WebSearchToolConfig\n\n/**\n * Converts a standard Tool to OpenAI WebSearchTool format. Spread `metadata`\n * first, then force `type: 'web_search'` last to keep the runtime `type`\n * matching the discriminator the dispatcher routed by — otherwise a tool\n * authored by hand with a different `metadata.type` would emit a malformed\n * payload.\n */\nexport function convertWebSearchToolToAdapterFormat(\n tool: Tool,\n): WebSearchToolConfig {\n const metadata = tool.metadata as Omit<WebSearchToolConfig, 'type'>\n return {\n ...metadata,\n type: 'web_search',\n }\n}\n\n/**\n * Creates a standard Tool from WebSearchTool parameters.\n *\n * Base (non-branded) factory. Providers that need branded return types should\n * re-wrap this in their own package.\n */\nexport function webSearchTool(toolData: WebSearchToolConfig): Tool {\n return {\n name: 'web_search',\n description: 'Search the web',\n metadata: toolData,\n }\n}\n"],"mappings":";;;;;;;;AAeA,SAAgB,oCACd,MACqB;CAErB,OAAO;EACL,GAFe,KAAK;EAGpB,MAAM;CACR;AACF;;;;;;;AAQA,SAAgB,cAAc,UAAqC;CACjE,OAAO;EACL,MAAM;EACN,aAAa;EACb,UAAU;CACZ;AACF"}
package/dist/esm/usage.js CHANGED
@@ -1,83 +1,95 @@
1
1
  import { buildBaseUsage } from "@tanstack/ai";
2
+ //#region src/usage.ts
3
+ /**
4
+ * Build normalized {@link TokenUsage} from an OpenAI-compatible Chat
5
+ * Completions `usage` object.
6
+ *
7
+ * Shared by every provider that routes through
8
+ * {@link OpenAIBaseChatCompletionsTextAdapter} (OpenAI Chat Completions, Grok,
9
+ * Groq). Surfaces cached prompt tokens and reasoning/audio detail tokens when
10
+ * the provider reports them. Returns `undefined` when the provider reported no
11
+ * usage object, so callers omit the field rather than fabricating zeroed totals.
12
+ */
2
13
  function buildChatCompletionsUsage(usage) {
3
- if (!usage) return void 0;
4
- const result = buildBaseUsage({
5
- promptTokens: usage.prompt_tokens || 0,
6
- completionTokens: usage.completion_tokens || 0,
7
- totalTokens: usage.total_tokens || 0
8
- });
9
- const completionDetails = usage.completion_tokens_details;
10
- const completionTokensDetails = {
11
- ...completionDetails?.reasoning_tokens ? { reasoningTokens: completionDetails.reasoning_tokens } : {},
12
- ...completionDetails?.audio_tokens ? { audioTokens: completionDetails.audio_tokens } : {}
13
- };
14
- const promptDetails = usage.prompt_tokens_details;
15
- const promptTokensDetails = {
16
- ...promptDetails?.cached_tokens ? { cachedTokens: promptDetails.cached_tokens } : {},
17
- ...promptDetails?.audio_tokens ? { audioTokens: promptDetails.audio_tokens } : {}
18
- };
19
- if (Object.keys(completionTokensDetails).length > 0) {
20
- result.completionTokensDetails = completionTokensDetails;
21
- }
22
- if (Object.keys(promptTokensDetails).length > 0) {
23
- result.promptTokensDetails = promptTokensDetails;
24
- }
25
- const providerUsageDetails = {
26
- ...completionDetails?.accepted_prediction_tokens ? {
27
- acceptedPredictionTokens: completionDetails.accepted_prediction_tokens
28
- } : {},
29
- ...completionDetails?.rejected_prediction_tokens ? {
30
- rejectedPredictionTokens: completionDetails.rejected_prediction_tokens
31
- } : {}
32
- };
33
- if (Object.keys(providerUsageDetails).length > 0) {
34
- result.providerUsageDetails = providerUsageDetails;
35
- }
36
- return result;
14
+ if (!usage) return void 0;
15
+ const result = buildBaseUsage({
16
+ promptTokens: usage.prompt_tokens || 0,
17
+ completionTokens: usage.completion_tokens || 0,
18
+ totalTokens: usage.total_tokens || 0
19
+ });
20
+ const completionDetails = usage.completion_tokens_details;
21
+ const completionTokensDetails = {
22
+ ...completionDetails?.reasoning_tokens ? { reasoningTokens: completionDetails.reasoning_tokens } : {},
23
+ ...completionDetails?.audio_tokens ? { audioTokens: completionDetails.audio_tokens } : {}
24
+ };
25
+ const promptDetails = usage.prompt_tokens_details;
26
+ const promptTokensDetails = {
27
+ ...promptDetails?.cached_tokens ? { cachedTokens: promptDetails.cached_tokens } : {},
28
+ ...promptDetails?.audio_tokens ? { audioTokens: promptDetails.audio_tokens } : {}
29
+ };
30
+ if (Object.keys(completionTokensDetails).length > 0) result.completionTokensDetails = completionTokensDetails;
31
+ if (Object.keys(promptTokensDetails).length > 0) result.promptTokensDetails = promptTokensDetails;
32
+ const providerUsageDetails = {
33
+ ...completionDetails?.accepted_prediction_tokens ? { acceptedPredictionTokens: completionDetails.accepted_prediction_tokens } : {},
34
+ ...completionDetails?.rejected_prediction_tokens ? { rejectedPredictionTokens: completionDetails.rejected_prediction_tokens } : {}
35
+ };
36
+ if (Object.keys(providerUsageDetails).length > 0) result.providerUsageDetails = providerUsageDetails;
37
+ return result;
37
38
  }
39
+ /**
40
+ * Build normalized {@link TokenUsage} from an OpenAI Responses API
41
+ * `ResponseUsage` object.
42
+ *
43
+ * Shared by every provider that routes through
44
+ * {@link OpenAIBaseResponsesTextAdapter}. Surfaces cached prompt tokens and
45
+ * reasoning detail tokens when present. Returns `undefined` when the provider
46
+ * reported no usage object, so callers omit the field rather than fabricating
47
+ * zeroed totals.
48
+ */
38
49
  function buildResponsesUsage(usage) {
39
- if (!usage) return void 0;
40
- const result = buildBaseUsage({
41
- promptTokens: usage.input_tokens || 0,
42
- completionTokens: usage.output_tokens || 0,
43
- totalTokens: usage.total_tokens || 0
44
- });
45
- const cachedTokens = usage.input_tokens_details?.cached_tokens;
46
- if (cachedTokens && cachedTokens > 0) {
47
- result.promptTokensDetails = {
48
- ...result.promptTokensDetails,
49
- cachedTokens
50
- };
51
- }
52
- const reasoningTokens = usage.output_tokens_details?.reasoning_tokens;
53
- if (reasoningTokens && reasoningTokens > 0) {
54
- result.completionTokensDetails = {
55
- ...result.completionTokensDetails,
56
- reasoningTokens
57
- };
58
- }
59
- return result;
50
+ if (!usage) return void 0;
51
+ const result = buildBaseUsage({
52
+ promptTokens: usage.input_tokens || 0,
53
+ completionTokens: usage.output_tokens || 0,
54
+ totalTokens: usage.total_tokens || 0
55
+ });
56
+ const cachedTokens = usage.input_tokens_details?.cached_tokens;
57
+ if (cachedTokens && cachedTokens > 0) result.promptTokensDetails = {
58
+ ...result.promptTokensDetails,
59
+ cachedTokens
60
+ };
61
+ const reasoningTokens = usage.output_tokens_details?.reasoning_tokens;
62
+ if (reasoningTokens && reasoningTokens > 0) result.completionTokensDetails = {
63
+ ...result.completionTokensDetails,
64
+ reasoningTokens
65
+ };
66
+ return result;
60
67
  }
68
+ /**
69
+ * Build normalized {@link TokenUsage} from an OpenAI Images API `usage` object.
70
+ *
71
+ * Shared by every provider that generates images through the OpenAI Images SDK
72
+ * (OpenAI, Grok). Token-billed image models (e.g. gpt-image-1) report an input
73
+ * breakdown of text vs image tokens, which is surfaced on `promptTokensDetails`.
74
+ * Models that don't return usage (e.g. DALL·E) yield `undefined` so callers can
75
+ * omit the field rather than emit zeroed totals.
76
+ */
61
77
  function buildImagesUsage(usage) {
62
- if (!usage) return void 0;
63
- const result = buildBaseUsage({
64
- promptTokens: usage.input_tokens || 0,
65
- completionTokens: usage.output_tokens || 0,
66
- totalTokens: usage.total_tokens || 0
67
- });
68
- const inputDetails = usage.input_tokens_details;
69
- const promptTokensDetails = {
70
- ...inputDetails?.text_tokens ? { textTokens: inputDetails.text_tokens } : {},
71
- ...inputDetails?.image_tokens ? { imageTokens: inputDetails.image_tokens } : {}
72
- };
73
- if (Object.keys(promptTokensDetails).length > 0) {
74
- result.promptTokensDetails = promptTokensDetails;
75
- }
76
- return result;
78
+ if (!usage) return void 0;
79
+ const result = buildBaseUsage({
80
+ promptTokens: usage.input_tokens || 0,
81
+ completionTokens: usage.output_tokens || 0,
82
+ totalTokens: usage.total_tokens || 0
83
+ });
84
+ const inputDetails = usage.input_tokens_details;
85
+ const promptTokensDetails = {
86
+ ...inputDetails?.text_tokens ? { textTokens: inputDetails.text_tokens } : {},
87
+ ...inputDetails?.image_tokens ? { imageTokens: inputDetails.image_tokens } : {}
88
+ };
89
+ if (Object.keys(promptTokensDetails).length > 0) result.promptTokensDetails = promptTokensDetails;
90
+ return result;
77
91
  }
78
- export {
79
- buildChatCompletionsUsage,
80
- buildImagesUsage,
81
- buildResponsesUsage
82
- };
83
- //# sourceMappingURL=usage.js.map
92
+ //#endregion
93
+ export { buildChatCompletionsUsage, buildImagesUsage, buildResponsesUsage };
94
+
95
+ //# sourceMappingURL=usage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage.js","sources":["../../src/usage.ts"],"sourcesContent":["import { buildBaseUsage } from '@tanstack/ai'\nimport type { TokenUsage } from '@tanstack/ai'\nimport type OpenAI from 'openai'\n\n/**\n * Build normalized {@link TokenUsage} from an OpenAI-compatible Chat\n * Completions `usage` object.\n *\n * Shared by every provider that routes through\n * {@link OpenAIBaseChatCompletionsTextAdapter} (OpenAI Chat Completions, Grok,\n * Groq). Surfaces cached prompt tokens and reasoning/audio detail tokens when\n * the provider reports them. Returns `undefined` when the provider reported no\n * usage object, so callers omit the field rather than fabricating zeroed totals.\n */\nexport function buildChatCompletionsUsage(\n usage: OpenAI.Chat.Completions.ChatCompletion['usage'] | undefined | null,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const result = buildBaseUsage({\n promptTokens: usage.prompt_tokens || 0,\n completionTokens: usage.completion_tokens || 0,\n totalTokens: usage.total_tokens || 0,\n })\n\n const completionDetails = usage.completion_tokens_details\n const completionTokensDetails = {\n ...(completionDetails?.reasoning_tokens\n ? { reasoningTokens: completionDetails.reasoning_tokens }\n : {}),\n ...(completionDetails?.audio_tokens\n ? { audioTokens: completionDetails.audio_tokens }\n : {}),\n }\n\n const promptDetails = usage.prompt_tokens_details\n const promptTokensDetails = {\n ...(promptDetails?.cached_tokens\n ? { cachedTokens: promptDetails.cached_tokens }\n : {}),\n ...(promptDetails?.audio_tokens\n ? { audioTokens: promptDetails.audio_tokens }\n : {}),\n }\n\n if (Object.keys(completionTokensDetails).length > 0) {\n result.completionTokensDetails = completionTokensDetails\n }\n if (Object.keys(promptTokensDetails).length > 0) {\n result.promptTokensDetails = promptTokensDetails\n }\n\n // Predicted Outputs accepted/rejected counts have no canonical TokenUsage\n // slot but are still billed (rejected tokens included), so surface them under\n // providerUsageDetails — matching how the OpenRouter adapter exposes them.\n const providerUsageDetails = {\n ...(completionDetails?.accepted_prediction_tokens\n ? {\n acceptedPredictionTokens:\n completionDetails.accepted_prediction_tokens,\n }\n : {}),\n ...(completionDetails?.rejected_prediction_tokens\n ? {\n rejectedPredictionTokens:\n completionDetails.rejected_prediction_tokens,\n }\n : {}),\n }\n if (Object.keys(providerUsageDetails).length > 0) {\n result.providerUsageDetails = providerUsageDetails\n }\n\n return result\n}\n\n/**\n * Build normalized {@link TokenUsage} from an OpenAI Responses API\n * `ResponseUsage` object.\n *\n * Shared by every provider that routes through\n * {@link OpenAIBaseResponsesTextAdapter}. Surfaces cached prompt tokens and\n * reasoning detail tokens when present. Returns `undefined` when the provider\n * reported no usage object, so callers omit the field rather than fabricating\n * zeroed totals.\n */\nexport function buildResponsesUsage(\n usage: OpenAI.Responses.ResponseUsage | undefined | null,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const result = buildBaseUsage({\n promptTokens: usage.input_tokens || 0,\n completionTokens: usage.output_tokens || 0,\n totalTokens: usage.total_tokens || 0,\n })\n\n // Despite the SDK types marking these required, they can be undefined at runtime.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const cachedTokens = usage.input_tokens_details?.cached_tokens\n if (cachedTokens && cachedTokens > 0) {\n result.promptTokensDetails = {\n ...result.promptTokensDetails,\n cachedTokens,\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const reasoningTokens = usage.output_tokens_details?.reasoning_tokens\n if (reasoningTokens && reasoningTokens > 0) {\n result.completionTokensDetails = {\n ...result.completionTokensDetails,\n reasoningTokens,\n }\n }\n\n return result\n}\n\n/**\n * Build normalized {@link TokenUsage} from an OpenAI Images API `usage` object.\n *\n * Shared by every provider that generates images through the OpenAI Images SDK\n * (OpenAI, Grok). Token-billed image models (e.g. gpt-image-1) report an input\n * breakdown of text vs image tokens, which is surfaced on `promptTokensDetails`.\n * Models that don't return usage (e.g. DALL·E) yield `undefined` so callers can\n * omit the field rather than emit zeroed totals.\n */\nexport function buildImagesUsage(\n usage: OpenAI.Images.ImagesResponse['usage'] | undefined | null,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const result = buildBaseUsage({\n promptTokens: usage.input_tokens || 0,\n completionTokens: usage.output_tokens || 0,\n totalTokens: usage.total_tokens || 0,\n })\n\n // The SDK types input_tokens_details (and its numeric fields) as required, but\n // real responses — e.g. from DALL·E or other non-token-billed models — can\n // omit them, so treat the breakdown as optional.\n const inputDetails = usage.input_tokens_details as\n | { text_tokens?: number; image_tokens?: number }\n | undefined\n const promptTokensDetails = {\n ...(inputDetails?.text_tokens\n ? { textTokens: inputDetails.text_tokens }\n : {}),\n ...(inputDetails?.image_tokens\n ? { imageTokens: inputDetails.image_tokens }\n : {}),\n }\n if (Object.keys(promptTokensDetails).length > 0) {\n result.promptTokensDetails = promptTokensDetails\n }\n\n return result\n}\n"],"names":[],"mappings":";AAcO,SAAS,0BACd,OACwB;AACxB,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,SAAS,eAAe;AAAA,IAC5B,cAAc,MAAM,iBAAiB;AAAA,IACrC,kBAAkB,MAAM,qBAAqB;AAAA,IAC7C,aAAa,MAAM,gBAAgB;AAAA,EAAA,CACpC;AAED,QAAM,oBAAoB,MAAM;AAChC,QAAM,0BAA0B;AAAA,IAC9B,GAAI,mBAAmB,mBACnB,EAAE,iBAAiB,kBAAkB,iBAAA,IACrC,CAAA;AAAA,IACJ,GAAI,mBAAmB,eACnB,EAAE,aAAa,kBAAkB,aAAA,IACjC,CAAA;AAAA,EAAC;AAGP,QAAM,gBAAgB,MAAM;AAC5B,QAAM,sBAAsB;AAAA,IAC1B,GAAI,eAAe,gBACf,EAAE,cAAc,cAAc,cAAA,IAC9B,CAAA;AAAA,IACJ,GAAI,eAAe,eACf,EAAE,aAAa,cAAc,aAAA,IAC7B,CAAA;AAAA,EAAC;AAGP,MAAI,OAAO,KAAK,uBAAuB,EAAE,SAAS,GAAG;AACnD,WAAO,0BAA0B;AAAA,EACnC;AACA,MAAI,OAAO,KAAK,mBAAmB,EAAE,SAAS,GAAG;AAC/C,WAAO,sBAAsB;AAAA,EAC/B;AAKA,QAAM,uBAAuB;AAAA,IAC3B,GAAI,mBAAmB,6BACnB;AAAA,MACE,0BACE,kBAAkB;AAAA,IAAA,IAEtB,CAAA;AAAA,IACJ,GAAI,mBAAmB,6BACnB;AAAA,MACE,0BACE,kBAAkB;AAAA,IAAA,IAEtB,CAAA;AAAA,EAAC;AAEP,MAAI,OAAO,KAAK,oBAAoB,EAAE,SAAS,GAAG;AAChD,WAAO,uBAAuB;AAAA,EAChC;AAEA,SAAO;AACT;AAYO,SAAS,oBACd,OACwB;AACxB,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,SAAS,eAAe;AAAA,IAC5B,cAAc,MAAM,gBAAgB;AAAA,IACpC,kBAAkB,MAAM,iBAAiB;AAAA,IACzC,aAAa,MAAM,gBAAgB;AAAA,EAAA,CACpC;AAID,QAAM,eAAe,MAAM,sBAAsB;AACjD,MAAI,gBAAgB,eAAe,GAAG;AACpC,WAAO,sBAAsB;AAAA,MAC3B,GAAG,OAAO;AAAA,MACV;AAAA,IAAA;AAAA,EAEJ;AAGA,QAAM,kBAAkB,MAAM,uBAAuB;AACrD,MAAI,mBAAmB,kBAAkB,GAAG;AAC1C,WAAO,0BAA0B;AAAA,MAC/B,GAAG,OAAO;AAAA,MACV;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO;AACT;AAWO,SAAS,iBACd,OACwB;AACxB,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,SAAS,eAAe;AAAA,IAC5B,cAAc,MAAM,gBAAgB;AAAA,IACpC,kBAAkB,MAAM,iBAAiB;AAAA,IACzC,aAAa,MAAM,gBAAgB;AAAA,EAAA,CACpC;AAKD,QAAM,eAAe,MAAM;AAG3B,QAAM,sBAAsB;AAAA,IAC1B,GAAI,cAAc,cACd,EAAE,YAAY,aAAa,YAAA,IAC3B,CAAA;AAAA,IACJ,GAAI,cAAc,eACd,EAAE,aAAa,aAAa,aAAA,IAC5B,CAAA;AAAA,EAAC;AAEP,MAAI,OAAO,KAAK,mBAAmB,EAAE,SAAS,GAAG;AAC/C,WAAO,sBAAsB;AAAA,EAC/B;AAEA,SAAO;AACT;"}
1
+ {"version":3,"file":"usage.js","names":[],"sources":["../../src/usage.ts"],"sourcesContent":["import { buildBaseUsage } from '@tanstack/ai'\nimport type { TokenUsage } from '@tanstack/ai'\nimport type OpenAI from 'openai'\n\n/**\n * Build normalized {@link TokenUsage} from an OpenAI-compatible Chat\n * Completions `usage` object.\n *\n * Shared by every provider that routes through\n * {@link OpenAIBaseChatCompletionsTextAdapter} (OpenAI Chat Completions, Grok,\n * Groq). Surfaces cached prompt tokens and reasoning/audio detail tokens when\n * the provider reports them. Returns `undefined` when the provider reported no\n * usage object, so callers omit the field rather than fabricating zeroed totals.\n */\nexport function buildChatCompletionsUsage(\n usage: OpenAI.Chat.Completions.ChatCompletion['usage'] | undefined | null,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const result = buildBaseUsage({\n promptTokens: usage.prompt_tokens || 0,\n completionTokens: usage.completion_tokens || 0,\n totalTokens: usage.total_tokens || 0,\n })\n\n const completionDetails = usage.completion_tokens_details\n const completionTokensDetails = {\n ...(completionDetails?.reasoning_tokens\n ? { reasoningTokens: completionDetails.reasoning_tokens }\n : {}),\n ...(completionDetails?.audio_tokens\n ? { audioTokens: completionDetails.audio_tokens }\n : {}),\n }\n\n const promptDetails = usage.prompt_tokens_details\n const promptTokensDetails = {\n ...(promptDetails?.cached_tokens\n ? { cachedTokens: promptDetails.cached_tokens }\n : {}),\n ...(promptDetails?.audio_tokens\n ? { audioTokens: promptDetails.audio_tokens }\n : {}),\n }\n\n if (Object.keys(completionTokensDetails).length > 0) {\n result.completionTokensDetails = completionTokensDetails\n }\n if (Object.keys(promptTokensDetails).length > 0) {\n result.promptTokensDetails = promptTokensDetails\n }\n\n // Predicted Outputs accepted/rejected counts have no canonical TokenUsage\n // slot but are still billed (rejected tokens included), so surface them under\n // providerUsageDetails — matching how the OpenRouter adapter exposes them.\n const providerUsageDetails = {\n ...(completionDetails?.accepted_prediction_tokens\n ? {\n acceptedPredictionTokens:\n completionDetails.accepted_prediction_tokens,\n }\n : {}),\n ...(completionDetails?.rejected_prediction_tokens\n ? {\n rejectedPredictionTokens:\n completionDetails.rejected_prediction_tokens,\n }\n : {}),\n }\n if (Object.keys(providerUsageDetails).length > 0) {\n result.providerUsageDetails = providerUsageDetails\n }\n\n return result\n}\n\n/**\n * Build normalized {@link TokenUsage} from an OpenAI Responses API\n * `ResponseUsage` object.\n *\n * Shared by every provider that routes through\n * {@link OpenAIBaseResponsesTextAdapter}. Surfaces cached prompt tokens and\n * reasoning detail tokens when present. Returns `undefined` when the provider\n * reported no usage object, so callers omit the field rather than fabricating\n * zeroed totals.\n */\nexport function buildResponsesUsage(\n usage: OpenAI.Responses.ResponseUsage | undefined | null,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const result = buildBaseUsage({\n promptTokens: usage.input_tokens || 0,\n completionTokens: usage.output_tokens || 0,\n totalTokens: usage.total_tokens || 0,\n })\n\n // Despite the SDK types marking these required, they can be undefined at runtime.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const cachedTokens = usage.input_tokens_details?.cached_tokens\n if (cachedTokens && cachedTokens > 0) {\n result.promptTokensDetails = {\n ...result.promptTokensDetails,\n cachedTokens,\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const reasoningTokens = usage.output_tokens_details?.reasoning_tokens\n if (reasoningTokens && reasoningTokens > 0) {\n result.completionTokensDetails = {\n ...result.completionTokensDetails,\n reasoningTokens,\n }\n }\n\n return result\n}\n\n/**\n * Build normalized {@link TokenUsage} from an OpenAI Images API `usage` object.\n *\n * Shared by every provider that generates images through the OpenAI Images SDK\n * (OpenAI, Grok). Token-billed image models (e.g. gpt-image-1) report an input\n * breakdown of text vs image tokens, which is surfaced on `promptTokensDetails`.\n * Models that don't return usage (e.g. DALL·E) yield `undefined` so callers can\n * omit the field rather than emit zeroed totals.\n */\nexport function buildImagesUsage(\n usage: OpenAI.Images.ImagesResponse['usage'] | undefined | null,\n): TokenUsage | undefined {\n if (!usage) return undefined\n\n const result = buildBaseUsage({\n promptTokens: usage.input_tokens || 0,\n completionTokens: usage.output_tokens || 0,\n totalTokens: usage.total_tokens || 0,\n })\n\n // The SDK types input_tokens_details (and its numeric fields) as required, but\n // real responses — e.g. from DALL·E or other non-token-billed models — can\n // omit them, so treat the breakdown as optional.\n const inputDetails = usage.input_tokens_details as\n | { text_tokens?: number; image_tokens?: number }\n | undefined\n const promptTokensDetails = {\n ...(inputDetails?.text_tokens\n ? { textTokens: inputDetails.text_tokens }\n : {}),\n ...(inputDetails?.image_tokens\n ? { imageTokens: inputDetails.image_tokens }\n : {}),\n }\n if (Object.keys(promptTokensDetails).length > 0) {\n result.promptTokensDetails = promptTokensDetails\n }\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;AAcA,SAAgB,0BACd,OACwB;CACxB,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,MAAM,SAAS,eAAe;EAC5B,cAAc,MAAM,iBAAiB;EACrC,kBAAkB,MAAM,qBAAqB;EAC7C,aAAa,MAAM,gBAAgB;CACrC,CAAC;CAED,MAAM,oBAAoB,MAAM;CAChC,MAAM,0BAA0B;EAC9B,GAAI,mBAAmB,mBACnB,EAAE,iBAAiB,kBAAkB,iBAAiB,IACtD,CAAC;EACL,GAAI,mBAAmB,eACnB,EAAE,aAAa,kBAAkB,aAAa,IAC9C,CAAC;CACP;CAEA,MAAM,gBAAgB,MAAM;CAC5B,MAAM,sBAAsB;EAC1B,GAAI,eAAe,gBACf,EAAE,cAAc,cAAc,cAAc,IAC5C,CAAC;EACL,GAAI,eAAe,eACf,EAAE,aAAa,cAAc,aAAa,IAC1C,CAAC;CACP;CAEA,IAAI,OAAO,KAAK,uBAAuB,CAAC,CAAC,SAAS,GAChD,OAAO,0BAA0B;CAEnC,IAAI,OAAO,KAAK,mBAAmB,CAAC,CAAC,SAAS,GAC5C,OAAO,sBAAsB;CAM/B,MAAM,uBAAuB;EAC3B,GAAI,mBAAmB,6BACnB,EACE,0BACE,kBAAkB,2BACtB,IACA,CAAC;EACL,GAAI,mBAAmB,6BACnB,EACE,0BACE,kBAAkB,2BACtB,IACA,CAAC;CACP;CACA,IAAI,OAAO,KAAK,oBAAoB,CAAC,CAAC,SAAS,GAC7C,OAAO,uBAAuB;CAGhC,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,oBACd,OACwB;CACxB,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,MAAM,SAAS,eAAe;EAC5B,cAAc,MAAM,gBAAgB;EACpC,kBAAkB,MAAM,iBAAiB;EACzC,aAAa,MAAM,gBAAgB;CACrC,CAAC;CAID,MAAM,eAAe,MAAM,sBAAsB;CACjD,IAAI,gBAAgB,eAAe,GACjC,OAAO,sBAAsB;EAC3B,GAAG,OAAO;EACV;CACF;CAIF,MAAM,kBAAkB,MAAM,uBAAuB;CACrD,IAAI,mBAAmB,kBAAkB,GACvC,OAAO,0BAA0B;EAC/B,GAAG,OAAO;EACV;CACF;CAGF,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,iBACd,OACwB;CACxB,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,MAAM,SAAS,eAAe;EAC5B,cAAc,MAAM,gBAAgB;EACpC,kBAAkB,MAAM,iBAAiB;EACzC,aAAa,MAAM,gBAAgB;CACrC,CAAC;CAKD,MAAM,eAAe,MAAM;CAG3B,MAAM,sBAAsB;EAC1B,GAAI,cAAc,cACd,EAAE,YAAY,aAAa,YAAY,IACvC,CAAC;EACL,GAAI,cAAc,eACd,EAAE,aAAa,aAAa,aAAa,IACzC,CAAC;CACP;CACA,IAAI,OAAO,KAAK,mBAAmB,CAAC,CAAC,SAAS,GAC5C,OAAO,sBAAsB;CAG/B,OAAO;AACT"}
@@ -1,11 +1,22 @@
1
+ //#region src/utils/request-options.ts
2
+ /**
3
+ * Extract `headers` and `signal` from a `Request | RequestInit` for the OpenAI
4
+ * SDK's per-call `RequestOptions`. `Request` exposes `headers` as a `Headers`
5
+ * instance (HeadersInit-compatible) while `RequestInit` exposes `HeadersInit`
6
+ * directly — this helper accepts either shape so callers don't need to cast.
7
+ *
8
+ * Always returns an object (possibly empty) rather than `undefined` so test
9
+ * assertions that match the second argument shape via `expect.anything()` /
10
+ * `expect.objectContaining()` keep working when no request override was set.
11
+ */
1
12
  function extractRequestOptions(request) {
2
- if (!request) return {};
3
- return {
4
- ...request.headers !== void 0 && { headers: request.headers },
5
- ...request.signal != null && { signal: request.signal }
6
- };
13
+ if (!request) return {};
14
+ return {
15
+ ...request.headers !== void 0 && { headers: request.headers },
16
+ ...request.signal != null && { signal: request.signal }
17
+ };
7
18
  }
8
- export {
9
- extractRequestOptions
10
- };
11
- //# sourceMappingURL=request-options.js.map
19
+ //#endregion
20
+ export { extractRequestOptions };
21
+
22
+ //# sourceMappingURL=request-options.js.map