@tanstack/openai-base 0.9.13 → 0.9.15

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 (51) hide show
  1. package/dist/esm/adapters/chat-completions-text.d.ts +10 -0
  2. package/dist/esm/adapters/chat-completions-text.js +47 -1
  3. package/dist/esm/adapters/chat-completions-text.js.map +1 -1
  4. package/dist/esm/adapters/responses-text.js +1 -1
  5. package/dist/esm/index.js +1 -0
  6. package/dist/esm/tools/apply-patch-tool.js +3 -2
  7. package/dist/esm/tools/apply-patch-tool.js.map +1 -1
  8. package/dist/esm/tools/code-interpreter-tool.js +4 -3
  9. package/dist/esm/tools/code-interpreter-tool.js.map +1 -1
  10. package/dist/esm/tools/computer-use-tool.js +4 -3
  11. package/dist/esm/tools/computer-use-tool.js.map +1 -1
  12. package/dist/esm/tools/custom-tool.js +4 -3
  13. package/dist/esm/tools/custom-tool.js.map +1 -1
  14. package/dist/esm/tools/file-search-tool.js +4 -3
  15. package/dist/esm/tools/file-search-tool.js.map +1 -1
  16. package/dist/esm/tools/image-generation-tool.d.ts +2 -4
  17. package/dist/esm/tools/image-generation-tool.js +6 -7
  18. package/dist/esm/tools/image-generation-tool.js.map +1 -1
  19. package/dist/esm/tools/index.js +14 -0
  20. package/dist/esm/tools/local-shell-tool.js +3 -2
  21. package/dist/esm/tools/local-shell-tool.js.map +1 -1
  22. package/dist/esm/tools/mcp-tool.js +7 -6
  23. package/dist/esm/tools/mcp-tool.js.map +1 -1
  24. package/dist/esm/tools/openai-provider-tool.d.ts +20 -0
  25. package/dist/esm/tools/openai-provider-tool.js +49 -0
  26. package/dist/esm/tools/openai-provider-tool.js.map +1 -0
  27. package/dist/esm/tools/shell-tool.js +4 -3
  28. package/dist/esm/tools/shell-tool.js.map +1 -1
  29. package/dist/esm/tools/tool-converter.js +7 -18
  30. package/dist/esm/tools/tool-converter.js.map +1 -1
  31. package/dist/esm/tools/web-search-preview-tool.d.ts +1 -2
  32. package/dist/esm/tools/web-search-preview-tool.js +5 -5
  33. package/dist/esm/tools/web-search-preview-tool.js.map +1 -1
  34. package/dist/esm/tools/web-search-tool.d.ts +2 -4
  35. package/dist/esm/tools/web-search-tool.js +6 -7
  36. package/dist/esm/tools/web-search-tool.js.map +1 -1
  37. package/package.json +3 -3
  38. package/src/adapters/chat-completions-text.ts +65 -0
  39. package/src/tools/apply-patch-tool.ts +9 -5
  40. package/src/tools/code-interpreter-tool.ts +17 -8
  41. package/src/tools/computer-use-tool.ts +14 -7
  42. package/src/tools/custom-tool.ts +14 -7
  43. package/src/tools/file-search-tool.ts +14 -7
  44. package/src/tools/image-generation-tool.ts +19 -11
  45. package/src/tools/local-shell-tool.ts +9 -5
  46. package/src/tools/mcp-tool.ts +19 -9
  47. package/src/tools/openai-provider-tool.ts +72 -0
  48. package/src/tools/shell-tool.ts +17 -9
  49. package/src/tools/tool-converter.ts +28 -43
  50. package/src/tools/web-search-preview-tool.ts +17 -8
  51. package/src/tools/web-search-tool.ts +18 -10
@@ -1,3 +1,4 @@
1
+ import { openAIProviderTool } from './openai-provider-tool'
1
2
  import type { ApplyPatchTool as ApplyPatchToolConfig } from 'openai/resources/responses/responses'
2
3
  import type { Tool } from '@tanstack/ai'
3
4
 
@@ -24,9 +25,12 @@ export function convertApplyPatchToolToAdapterFormat(
24
25
  * re-wrap this in their own package.
25
26
  */
26
27
  export function applyPatchTool(): Tool {
27
- return {
28
- name: 'apply_patch',
29
- description: 'Apply a patch to modify files',
30
- metadata: {},
31
- }
28
+ return openAIProviderTool(
29
+ {
30
+ name: 'apply_patch',
31
+ description: 'Apply a patch to modify files',
32
+ metadata: {},
33
+ },
34
+ 'apply_patch',
35
+ )
32
36
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { Tool as SDKTool } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -14,7 +18,9 @@ export type CodeInterpreterTool = CodeInterpreterToolConfig
14
18
  export function convertCodeInterpreterToolToAdapterFormat(
15
19
  tool: Tool,
16
20
  ): CodeInterpreterToolConfig {
17
- const metadata = tool.metadata as CodeInterpreterToolConfig
21
+ const metadata = getOpenAIProviderToolMetadata(
22
+ tool,
23
+ ) as CodeInterpreterToolConfig
18
24
  return {
19
25
  type: 'code_interpreter',
20
26
  container: metadata.container,
@@ -30,12 +36,15 @@ export function convertCodeInterpreterToolToAdapterFormat(
30
36
  export function codeInterpreterTool(
31
37
  container: CodeInterpreterToolConfig,
32
38
  ): Tool {
33
- return {
34
- name: 'code_interpreter',
35
- description: 'Execute code in a sandboxed environment',
36
- metadata: {
37
- type: 'code_interpreter',
38
- container,
39
+ return openAIProviderTool(
40
+ {
41
+ name: 'code_interpreter',
42
+ description: 'Execute code in a sandboxed environment',
43
+ metadata: {
44
+ type: 'code_interpreter',
45
+ container,
46
+ },
39
47
  },
40
- }
48
+ 'code_interpreter',
49
+ )
41
50
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { ComputerUsePreviewTool as ComputerUseToolConfig } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -12,7 +16,7 @@ export type ComputerUseTool = ComputerUseToolConfig
12
16
  export function convertComputerUseToolToAdapterFormat(
13
17
  tool: Tool,
14
18
  ): ComputerUseToolConfig {
15
- const metadata = tool.metadata as ComputerUseToolConfig
19
+ const metadata = getOpenAIProviderToolMetadata(tool) as ComputerUseToolConfig
16
20
  return {
17
21
  type: 'computer_use_preview',
18
22
  display_height: metadata.display_height,
@@ -28,11 +32,14 @@ export function convertComputerUseToolToAdapterFormat(
28
32
  * re-wrap this in their own package.
29
33
  */
30
34
  export function computerUseTool(toolData: ComputerUseToolConfig): Tool {
31
- return {
32
- name: 'computer_use_preview',
33
- description: 'Control a virtual computer',
34
- metadata: {
35
- ...toolData,
35
+ return openAIProviderTool(
36
+ {
37
+ name: 'computer_use_preview',
38
+ description: 'Control a virtual computer',
39
+ metadata: {
40
+ ...toolData,
41
+ },
36
42
  },
37
- }
43
+ 'computer_use',
44
+ )
38
45
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { CustomTool as CustomToolConfig } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -10,7 +14,7 @@ export type CustomTool = CustomToolConfig
10
14
  * Converts a standard Tool to OpenAI CustomTool format
11
15
  */
12
16
  export function convertCustomToolToAdapterFormat(tool: Tool): CustomToolConfig {
13
- const metadata = tool.metadata as CustomToolConfig
17
+ const metadata = getOpenAIProviderToolMetadata(tool) as CustomToolConfig
14
18
  // Conditional spread: the SDK's `CustomToolConfig` declares optional
15
19
  // fields as `description?: string` (no `| undefined`) under
16
20
  // exactOptionalPropertyTypes, so we omit absent fields rather than
@@ -29,11 +33,14 @@ export function convertCustomToolToAdapterFormat(tool: Tool): CustomToolConfig {
29
33
  * Creates a standard Tool from CustomTool parameters.
30
34
  */
31
35
  export function customTool(toolData: CustomToolConfig): Tool {
32
- return {
33
- name: 'custom',
34
- description: toolData.description || 'A custom tool',
35
- metadata: {
36
- ...toolData,
36
+ return openAIProviderTool(
37
+ {
38
+ name: 'custom',
39
+ description: toolData.description || 'A custom tool',
40
+ metadata: {
41
+ ...toolData,
42
+ },
37
43
  },
38
- }
44
+ 'custom',
45
+ )
39
46
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { FileSearchTool as FileSearchToolConfig } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -21,7 +25,7 @@ export type FileSearchTool = FileSearchToolConfig
21
25
  export function convertFileSearchToolToAdapterFormat(
22
26
  tool: Tool,
23
27
  ): FileSearchToolConfig {
24
- const metadata = tool.metadata as FileSearchToolConfig
28
+ const metadata = getOpenAIProviderToolMetadata(tool) as FileSearchToolConfig
25
29
  // Conditional spread: SDK's `FileSearchToolConfig` declares the
26
30
  // optional fields without `| undefined`, so we omit absent values
27
31
  // rather than passing them through as explicit `undefined`.
@@ -46,13 +50,16 @@ export function convertFileSearchToolToAdapterFormat(
46
50
  */
47
51
  export function fileSearchTool(toolData: FileSearchToolConfig): Tool {
48
52
  validateMaxNumResults(toolData.max_num_results)
49
- return {
50
- name: 'file_search',
51
- description: 'Search files in vector stores',
52
- metadata: {
53
- ...toolData,
53
+ return openAIProviderTool(
54
+ {
55
+ name: 'file_search',
56
+ description: 'Search files in vector stores',
57
+ metadata: {
58
+ ...toolData,
59
+ },
54
60
  },
55
- }
61
+ 'file_search',
62
+ )
56
63
  }
57
64
 
58
65
  export { validateMaxNumResults }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { Tool as SDKTool } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -16,15 +20,16 @@ const validatePartialImages = (value: number | undefined) => {
16
20
 
17
21
  /**
18
22
  * Converts a standard Tool to OpenAI ImageGenerationTool format. Spread
19
- * `metadata` first, then force `type: 'image_generation'` last — otherwise a
20
- * `metadata.type` snuck in by a hand-built tool would shadow the literal and
21
- * the dispatcher (which routed by `tool.name`) would emit a tool whose
22
- * runtime `type` doesn't match `image_generation`.
23
+ * `metadata` first, then force `type: 'image_generation'` last so a stray
24
+ * `metadata.type` cannot shadow the wire discriminator.
23
25
  */
24
26
  export function convertImageGenerationToolToAdapterFormat(
25
27
  tool: Tool,
26
28
  ): ImageGenerationToolConfig {
27
- const metadata = tool.metadata as Omit<ImageGenerationToolConfig, 'type'>
29
+ const metadata = getOpenAIProviderToolMetadata(tool) as Omit<
30
+ ImageGenerationToolConfig,
31
+ 'type'
32
+ >
28
33
  return {
29
34
  ...metadata,
30
35
  type: 'image_generation',
@@ -41,13 +46,16 @@ export function imageGenerationTool(
41
46
  toolData: Omit<ImageGenerationToolConfig, 'type'>,
42
47
  ): Tool {
43
48
  validatePartialImages(toolData.partial_images)
44
- return {
45
- name: 'image_generation',
46
- description: 'Generate images based on text descriptions',
47
- metadata: {
48
- ...toolData,
49
+ return openAIProviderTool(
50
+ {
51
+ name: 'image_generation',
52
+ description: 'Generate images based on text descriptions',
53
+ metadata: {
54
+ ...toolData,
55
+ },
49
56
  },
50
- }
57
+ 'image_generation',
58
+ )
51
59
  }
52
60
 
53
61
  export { validatePartialImages }
@@ -1,3 +1,4 @@
1
+ import { openAIProviderTool } from './openai-provider-tool'
1
2
  import type { Tool as SDKTool } from 'openai/resources/responses/responses'
2
3
  import type { Tool } from '@tanstack/ai'
3
4
 
@@ -26,9 +27,12 @@ export function convertLocalShellToolToAdapterFormat(
26
27
  * re-wrap this in their own package.
27
28
  */
28
29
  export function localShellTool(): Tool {
29
- return {
30
- name: 'local_shell',
31
- description: 'Execute local shell commands',
32
- metadata: {},
33
- }
30
+ return openAIProviderTool(
31
+ {
32
+ name: 'local_shell',
33
+ description: 'Execute local shell commands',
34
+ metadata: {},
35
+ },
36
+ 'local_shell',
37
+ )
34
38
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { Tool as SDKTool } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -21,15 +25,18 @@ export function validateMCPtool(tool: MCPToolConfig) {
21
25
  * Converts a standard Tool to OpenAI MCPTool format
22
26
  */
23
27
  export function convertMCPToolToAdapterFormat(tool: Tool): MCPToolConfig {
24
- const metadata = tool.metadata as Omit<MCPToolConfig, 'type'>
28
+ const metadata = getOpenAIProviderToolMetadata(tool) as Omit<
29
+ MCPToolConfig,
30
+ 'type'
31
+ >
25
32
 
26
- const mcpTool: MCPToolConfig = {
33
+ const convertedTool: MCPToolConfig = {
27
34
  ...metadata,
28
35
  type: 'mcp',
29
36
  }
30
37
 
31
- validateMCPtool(mcpTool)
32
- return mcpTool
38
+ validateMCPtool(convertedTool)
39
+ return convertedTool
33
40
  }
34
41
 
35
42
  /**
@@ -41,9 +48,12 @@ export function convertMCPToolToAdapterFormat(tool: Tool): MCPToolConfig {
41
48
  export function mcpTool(toolData: Omit<MCPToolConfig, 'type'>): Tool {
42
49
  validateMCPtool({ ...toolData, type: 'mcp' })
43
50
 
44
- return {
45
- name: 'mcp',
46
- description: toolData.server_description || '',
47
- metadata: toolData,
48
- }
51
+ return openAIProviderTool(
52
+ {
53
+ name: 'mcp',
54
+ description: toolData.server_description || '',
55
+ metadata: toolData,
56
+ },
57
+ 'mcp',
58
+ )
49
59
  }
@@ -0,0 +1,72 @@
1
+ import type { Tool } from '@tanstack/ai'
2
+
3
+ const OPENAI_PROVIDER_TOOL_KINDS = {
4
+ apply_patch: 'openai.apply_patch',
5
+ code_interpreter: 'openai.code_interpreter',
6
+ computer_use: 'openai.computer_use',
7
+ custom: 'openai.custom',
8
+ file_search: 'openai.file_search',
9
+ image_generation: 'openai.image_generation',
10
+ local_shell: 'openai.local_shell',
11
+ mcp: 'openai.mcp',
12
+ shell: 'openai.shell',
13
+ web_search: 'openai.web_search',
14
+ web_search_preview: 'openai.web_search_preview',
15
+ } as const
16
+
17
+ export type OpenAIProviderToolKind = keyof typeof OPENAI_PROVIDER_TOOL_KINDS
18
+
19
+ export function openAIProviderTool(
20
+ tool: Tool,
21
+ toolKind: OpenAIProviderToolKind,
22
+ ): Tool {
23
+ return {
24
+ ...tool,
25
+ metadata: {
26
+ ...tool.metadata,
27
+ __kind: OPENAI_PROVIDER_TOOL_KINDS[toolKind],
28
+ },
29
+ }
30
+ }
31
+
32
+ export function getOpenAIProviderToolKind(
33
+ tool: Tool,
34
+ ): OpenAIProviderToolKind | undefined {
35
+ const toolKind = tool.metadata?.['__kind']
36
+ switch (toolKind) {
37
+ case 'openai.apply_patch':
38
+ return 'apply_patch'
39
+ case 'openai.code_interpreter':
40
+ return 'code_interpreter'
41
+ case 'openai.computer_use':
42
+ return 'computer_use'
43
+ case 'openai.custom':
44
+ return 'custom'
45
+ case 'openai.file_search':
46
+ return 'file_search'
47
+ case 'openai.image_generation':
48
+ return 'image_generation'
49
+ case 'openai.local_shell':
50
+ return 'local_shell'
51
+ case 'openai.mcp':
52
+ return 'mcp'
53
+ case 'openai.shell':
54
+ return 'shell'
55
+ case 'openai.web_search':
56
+ return 'web_search'
57
+ case 'openai.web_search_preview':
58
+ return 'web_search_preview'
59
+ default:
60
+ return undefined
61
+ }
62
+ }
63
+
64
+ /** Returns adapter metadata without the internal runtime discriminator. */
65
+ export function getOpenAIProviderToolMetadata(tool: Tool): Tool['metadata'] {
66
+ if (!tool.metadata) {
67
+ return undefined
68
+ }
69
+ const { __kind: _kind, ...metadata } = tool.metadata
70
+ void _kind
71
+ return metadata
72
+ }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { FunctionShellTool as ShellToolConfig } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -21,7 +25,8 @@ export interface ShellToolFactoryConfig {
21
25
  * `environment` (container config + skills) stored in metadata.
22
26
  */
23
27
  export function convertShellToolToAdapterFormat(tool: Tool): ShellToolConfig {
24
- const metadata = (tool.metadata ?? {}) as ShellToolFactoryConfig
28
+ const metadata = (getOpenAIProviderToolMetadata(tool) ??
29
+ {}) as ShellToolFactoryConfig
25
30
  return {
26
31
  type: 'shell',
27
32
  ...(metadata.environment !== undefined && {
@@ -37,13 +42,16 @@ export function convertShellToolToAdapterFormat(tool: Tool): ShellToolConfig {
37
42
  * re-wrap this in their own package.
38
43
  */
39
44
  export function shellTool(config: ShellToolFactoryConfig = {}): Tool {
40
- return {
41
- name: 'shell',
42
- description: 'Execute shell commands',
43
- metadata: {
44
- ...(config.environment !== undefined && {
45
- environment: config.environment,
46
- }),
45
+ return openAIProviderTool(
46
+ {
47
+ name: 'shell',
48
+ description: 'Execute shell commands',
49
+ metadata: {
50
+ ...(config.environment !== undefined && {
51
+ environment: config.environment,
52
+ }),
53
+ },
47
54
  },
48
- }
55
+ 'shell',
56
+ )
49
57
  }
@@ -7,62 +7,47 @@ import { convertFunctionToolToAdapterFormat } from './function-tool'
7
7
  import { convertImageGenerationToolToAdapterFormat } from './image-generation-tool'
8
8
  import { convertLocalShellToolToAdapterFormat } from './local-shell-tool'
9
9
  import { convertMCPToolToAdapterFormat } from './mcp-tool'
10
+ import { getOpenAIProviderToolKind } from './openai-provider-tool'
10
11
  import { convertShellToolToAdapterFormat } from './shell-tool'
11
12
  import { convertWebSearchPreviewToolToAdapterFormat } from './web-search-preview-tool'
12
13
  import { convertWebSearchToolToAdapterFormat } from './web-search-tool'
13
14
  import type { OpenAITool } from './index'
15
+ import { assertUniqueToolNames } from '@tanstack/ai/adapter-internals'
14
16
  import type { Tool } from '@tanstack/ai'
15
17
 
16
- const SPECIAL_TOOL_NAMES = new Set([
17
- 'apply_patch',
18
- 'code_interpreter',
19
- 'computer_use_preview',
20
- 'file_search',
21
- 'image_generation',
22
- 'local_shell',
23
- 'mcp',
24
- 'shell',
25
- 'web_search_preview',
26
- 'web_search',
27
- 'custom',
28
- ])
29
-
30
18
  /**
31
19
  * Converts an array of standard Tools to OpenAI-specific format
32
20
  */
33
21
  export function convertToolsToProviderFormat(
34
22
  tools: Array<Tool>,
35
23
  ): Array<OpenAITool> {
24
+ assertUniqueToolNames(tools)
36
25
  return tools.map((tool) => {
37
- const toolName = tool.name
38
-
39
- if (SPECIAL_TOOL_NAMES.has(toolName)) {
40
- switch (toolName) {
41
- case 'apply_patch':
42
- return convertApplyPatchToolToAdapterFormat(tool)
43
- case 'code_interpreter':
44
- return convertCodeInterpreterToolToAdapterFormat(tool)
45
- case 'computer_use_preview':
46
- return convertComputerUseToolToAdapterFormat(tool)
47
- case 'file_search':
48
- return convertFileSearchToolToAdapterFormat(tool)
49
- case 'image_generation':
50
- return convertImageGenerationToolToAdapterFormat(tool)
51
- case 'local_shell':
52
- return convertLocalShellToolToAdapterFormat(tool)
53
- case 'mcp':
54
- return convertMCPToolToAdapterFormat(tool)
55
- case 'shell':
56
- return convertShellToolToAdapterFormat(tool)
57
- case 'web_search_preview':
58
- return convertWebSearchPreviewToolToAdapterFormat(tool)
59
- case 'web_search':
60
- return convertWebSearchToolToAdapterFormat(tool)
61
- case 'custom':
62
- return convertCustomToolToAdapterFormat(tool)
63
- }
26
+ switch (getOpenAIProviderToolKind(tool)) {
27
+ case 'apply_patch':
28
+ return convertApplyPatchToolToAdapterFormat(tool)
29
+ case 'code_interpreter':
30
+ return convertCodeInterpreterToolToAdapterFormat(tool)
31
+ case 'computer_use':
32
+ return convertComputerUseToolToAdapterFormat(tool)
33
+ case 'custom':
34
+ return convertCustomToolToAdapterFormat(tool)
35
+ case 'file_search':
36
+ return convertFileSearchToolToAdapterFormat(tool)
37
+ case 'image_generation':
38
+ return convertImageGenerationToolToAdapterFormat(tool)
39
+ case 'local_shell':
40
+ return convertLocalShellToolToAdapterFormat(tool)
41
+ case 'mcp':
42
+ return convertMCPToolToAdapterFormat(tool)
43
+ case 'shell':
44
+ return convertShellToolToAdapterFormat(tool)
45
+ case 'web_search_preview':
46
+ return convertWebSearchPreviewToolToAdapterFormat(tool)
47
+ case 'web_search':
48
+ return convertWebSearchToolToAdapterFormat(tool)
49
+ case undefined:
50
+ return convertFunctionToolToAdapterFormat(tool)
64
51
  }
65
-
66
- return convertFunctionToolToAdapterFormat(tool)
67
52
  })
68
53
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { WebSearchPreviewTool as WebSearchPreviewToolConfig } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -9,13 +13,15 @@ export type WebSearchPreviewTool = WebSearchPreviewToolConfig
9
13
  /**
10
14
  * Converts a standard Tool to OpenAI WebSearchPreviewTool format. Force the
11
15
  * literal `type: 'web_search_preview'` instead of trusting `metadata.type`,
12
- * since a hand-authored tool with a missing or wrong `type` would emit a
13
- * malformed payload while the dispatcher already routed by `tool.name`.
16
+ * so a missing or wrong metadata type cannot produce a malformed payload.
14
17
  */
15
18
  export function convertWebSearchPreviewToolToAdapterFormat(
16
19
  tool: Tool,
17
20
  ): WebSearchPreviewToolConfig {
18
- const metadata = tool.metadata as Omit<WebSearchPreviewToolConfig, 'type'>
21
+ const metadata = getOpenAIProviderToolMetadata(tool) as Omit<
22
+ WebSearchPreviewToolConfig,
23
+ 'type'
24
+ >
19
25
  return {
20
26
  ...metadata,
21
27
  type: 'web_search_preview',
@@ -31,9 +37,12 @@ export function convertWebSearchPreviewToolToAdapterFormat(
31
37
  export function webSearchPreviewTool(
32
38
  toolData: WebSearchPreviewToolConfig,
33
39
  ): Tool {
34
- return {
35
- name: 'web_search_preview',
36
- description: 'Search the web (preview version)',
37
- metadata: toolData,
38
- }
40
+ return openAIProviderTool(
41
+ {
42
+ name: 'web_search_preview',
43
+ description: 'Search the web (preview version)',
44
+ metadata: toolData,
45
+ },
46
+ 'web_search_preview',
47
+ )
39
48
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getOpenAIProviderToolMetadata,
3
+ openAIProviderTool,
4
+ } from './openai-provider-tool'
1
5
  import type { WebSearchTool as WebSearchToolConfig } from 'openai/resources/responses/responses'
2
6
  import type { Tool } from '@tanstack/ai'
3
7
 
@@ -8,15 +12,16 @@ export type WebSearchTool = WebSearchToolConfig
8
12
 
9
13
  /**
10
14
  * Converts a standard Tool to OpenAI WebSearchTool format. Spread `metadata`
11
- * first, then force `type: 'web_search'` last to keep the runtime `type`
12
- * matching the discriminator the dispatcher routed by — otherwise a tool
13
- * authored by hand with a different `metadata.type` would emit a malformed
14
- * payload.
15
+ * first, then force `type: 'web_search'` last so a different metadata type
16
+ * cannot produce a malformed payload.
15
17
  */
16
18
  export function convertWebSearchToolToAdapterFormat(
17
19
  tool: Tool,
18
20
  ): WebSearchToolConfig {
19
- const metadata = tool.metadata as Omit<WebSearchToolConfig, 'type'>
21
+ const metadata = getOpenAIProviderToolMetadata(tool) as Omit<
22
+ WebSearchToolConfig,
23
+ 'type'
24
+ >
20
25
  return {
21
26
  ...metadata,
22
27
  type: 'web_search',
@@ -30,9 +35,12 @@ export function convertWebSearchToolToAdapterFormat(
30
35
  * re-wrap this in their own package.
31
36
  */
32
37
  export function webSearchTool(toolData: WebSearchToolConfig): Tool {
33
- return {
34
- name: 'web_search',
35
- description: 'Search the web',
36
- metadata: toolData,
37
- }
38
+ return openAIProviderTool(
39
+ {
40
+ name: 'web_search',
41
+ description: 'Search the web',
42
+ metadata: toolData,
43
+ },
44
+ 'web_search',
45
+ )
38
46
  }