@tanstack/openai-base 0.1.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.
Files changed (110) hide show
  1. package/dist/esm/adapters/chat-completions-text.d.ts +76 -0
  2. package/dist/esm/adapters/chat-completions-text.js +411 -0
  3. package/dist/esm/adapters/chat-completions-text.js.map +1 -0
  4. package/dist/esm/adapters/chat-completions-tool-converter.d.ts +24 -0
  5. package/dist/esm/adapters/chat-completions-tool-converter.js +29 -0
  6. package/dist/esm/adapters/chat-completions-tool-converter.js.map +1 -0
  7. package/dist/esm/adapters/image.d.ts +32 -0
  8. package/dist/esm/adapters/image.js +69 -0
  9. package/dist/esm/adapters/image.js.map +1 -0
  10. package/dist/esm/adapters/responses-text.d.ts +115 -0
  11. package/dist/esm/adapters/responses-text.js +635 -0
  12. package/dist/esm/adapters/responses-text.js.map +1 -0
  13. package/dist/esm/adapters/responses-tool-converter.d.ts +35 -0
  14. package/dist/esm/adapters/responses-tool-converter.js +27 -0
  15. package/dist/esm/adapters/responses-tool-converter.js.map +1 -0
  16. package/dist/esm/adapters/summarize.d.ts +28 -0
  17. package/dist/esm/adapters/summarize.js +74 -0
  18. package/dist/esm/adapters/summarize.js.map +1 -0
  19. package/dist/esm/adapters/transcription.d.ts +39 -0
  20. package/dist/esm/adapters/transcription.js +139 -0
  21. package/dist/esm/adapters/transcription.js.map +1 -0
  22. package/dist/esm/adapters/tts.d.ts +26 -0
  23. package/dist/esm/adapters/tts.js +65 -0
  24. package/dist/esm/adapters/tts.js.map +1 -0
  25. package/dist/esm/adapters/video.d.ts +48 -0
  26. package/dist/esm/adapters/video.js +192 -0
  27. package/dist/esm/adapters/video.js.map +1 -0
  28. package/dist/esm/index.d.ts +15 -0
  29. package/dist/esm/index.js +65 -0
  30. package/dist/esm/index.js.map +1 -0
  31. package/dist/esm/tools/apply-patch-tool.d.ts +11 -0
  32. package/dist/esm/tools/apply-patch-tool.js +17 -0
  33. package/dist/esm/tools/apply-patch-tool.js.map +1 -0
  34. package/dist/esm/tools/code-interpreter-tool.d.ts +11 -0
  35. package/dist/esm/tools/code-interpreter-tool.js +22 -0
  36. package/dist/esm/tools/code-interpreter-tool.js.map +1 -0
  37. package/dist/esm/tools/computer-use-tool.d.ts +11 -0
  38. package/dist/esm/tools/computer-use-tool.js +23 -0
  39. package/dist/esm/tools/computer-use-tool.js.map +1 -0
  40. package/dist/esm/tools/custom-tool.d.ts +11 -0
  41. package/dist/esm/tools/custom-tool.js +23 -0
  42. package/dist/esm/tools/custom-tool.js.map +1 -0
  43. package/dist/esm/tools/file-search-tool.d.ts +11 -0
  44. package/dist/esm/tools/file-search-tool.js +30 -0
  45. package/dist/esm/tools/file-search-tool.js.map +1 -0
  46. package/dist/esm/tools/function-tool.d.ts +15 -0
  47. package/dist/esm/tools/function-tool.js +24 -0
  48. package/dist/esm/tools/function-tool.js.map +1 -0
  49. package/dist/esm/tools/image-generation-tool.d.ts +11 -0
  50. package/dist/esm/tools/image-generation-tool.js +27 -0
  51. package/dist/esm/tools/image-generation-tool.js.map +1 -0
  52. package/dist/esm/tools/index.d.ts +27 -0
  53. package/dist/esm/tools/local-shell-tool.d.ts +11 -0
  54. package/dist/esm/tools/local-shell-tool.js +17 -0
  55. package/dist/esm/tools/local-shell-tool.js.map +1 -0
  56. package/dist/esm/tools/mcp-tool.d.ts +12 -0
  57. package/dist/esm/tools/mcp-tool.js +31 -0
  58. package/dist/esm/tools/mcp-tool.js.map +1 -0
  59. package/dist/esm/tools/shell-tool.d.ts +11 -0
  60. package/dist/esm/tools/shell-tool.js +17 -0
  61. package/dist/esm/tools/shell-tool.js.map +1 -0
  62. package/dist/esm/tools/tool-choice.d.ts +17 -0
  63. package/dist/esm/tools/tool-converter.d.ts +6 -0
  64. package/dist/esm/tools/tool-converter.js +61 -0
  65. package/dist/esm/tools/tool-converter.js.map +1 -0
  66. package/dist/esm/tools/web-search-preview-tool.d.ts +11 -0
  67. package/dist/esm/tools/web-search-preview-tool.js +20 -0
  68. package/dist/esm/tools/web-search-preview-tool.js.map +1 -0
  69. package/dist/esm/tools/web-search-tool.d.ts +11 -0
  70. package/dist/esm/tools/web-search-tool.js +16 -0
  71. package/dist/esm/tools/web-search-tool.js.map +1 -0
  72. package/dist/esm/types/config.d.ts +4 -0
  73. package/dist/esm/types/message-metadata.d.ts +19 -0
  74. package/dist/esm/types/provider-options.d.ts +40 -0
  75. package/dist/esm/utils/client.d.ts +3 -0
  76. package/dist/esm/utils/client.js +8 -0
  77. package/dist/esm/utils/client.js.map +1 -0
  78. package/dist/esm/utils/schema-converter.d.ts +12 -0
  79. package/dist/esm/utils/schema-converter.js +65 -0
  80. package/dist/esm/utils/schema-converter.js.map +1 -0
  81. package/package.json +57 -0
  82. package/src/adapters/chat-completions-text.ts +817 -0
  83. package/src/adapters/chat-completions-tool-converter.ts +70 -0
  84. package/src/adapters/image.ts +158 -0
  85. package/src/adapters/responses-text.ts +1147 -0
  86. package/src/adapters/responses-tool-converter.ts +77 -0
  87. package/src/adapters/summarize.ts +174 -0
  88. package/src/adapters/transcription.ts +194 -0
  89. package/src/adapters/tts.ts +124 -0
  90. package/src/adapters/video.ts +385 -0
  91. package/src/index.ts +24 -0
  92. package/src/tools/apply-patch-tool.ts +32 -0
  93. package/src/tools/code-interpreter-tool.ts +39 -0
  94. package/src/tools/computer-use-tool.ts +38 -0
  95. package/src/tools/custom-tool.ts +33 -0
  96. package/src/tools/file-search-tool.ts +51 -0
  97. package/src/tools/function-tool.ts +44 -0
  98. package/src/tools/image-generation-tool.ts +51 -0
  99. package/src/tools/index.ts +41 -0
  100. package/src/tools/local-shell-tool.ts +32 -0
  101. package/src/tools/mcp-tool.ts +47 -0
  102. package/src/tools/shell-tool.ts +30 -0
  103. package/src/tools/tool-choice.ts +31 -0
  104. package/src/tools/tool-converter.ts +68 -0
  105. package/src/tools/web-search-preview-tool.ts +39 -0
  106. package/src/tools/web-search-tool.ts +38 -0
  107. package/src/types/config.ts +5 -0
  108. package/src/utils/client.ts +8 -0
  109. package/src/utils/request-options.ts +16 -0
  110. package/src/utils/schema-converter.ts +89 -0
@@ -0,0 +1,70 @@
1
+ import { makeStructuredOutputCompatible } from '../utils/schema-converter'
2
+ import type { JSONSchema, Tool } from '@tanstack/ai'
3
+ import type OpenAI from 'openai'
4
+
5
+ /**
6
+ * Chat Completions API tool format.
7
+ * This is distinct from the Responses API tool format.
8
+ */
9
+ export type ChatCompletionFunctionTool =
10
+ OpenAI.Chat.Completions.ChatCompletionTool
11
+
12
+ /**
13
+ * Converts a standard Tool to OpenAI Chat Completions ChatCompletionTool format.
14
+ *
15
+ * Tool schemas are already converted to JSON Schema in the ai layer.
16
+ * We apply OpenAI-compatible transformations for strict mode:
17
+ * - All properties in required array
18
+ * - Optional fields made nullable
19
+ * - additionalProperties: false
20
+ *
21
+ * This enables strict mode for all tools automatically.
22
+ */
23
+ export function convertFunctionToolToChatCompletionsFormat(
24
+ tool: Tool,
25
+ schemaConverter: (
26
+ schema: Record<string, any>,
27
+ required: Array<string>,
28
+ ) => Record<string, any> = makeStructuredOutputCompatible,
29
+ ): ChatCompletionFunctionTool {
30
+ const inputSchema = (tool.inputSchema ?? {
31
+ type: 'object',
32
+ properties: {},
33
+ required: [],
34
+ }) as JSONSchema
35
+
36
+ // Shallow-copy the converter's result before mutating: a subclass-supplied
37
+ // schemaConverter has no contract requirement to return a fresh object,
38
+ // and a passthrough `(s) => s` would otherwise have its caller's schema
39
+ // mutated by the `additionalProperties = false` assignment below.
40
+ const jsonSchema = {
41
+ ...schemaConverter(inputSchema, inputSchema.required || []),
42
+ }
43
+ jsonSchema.additionalProperties = false
44
+
45
+ return {
46
+ type: 'function',
47
+ function: {
48
+ name: tool.name,
49
+ description: tool.description,
50
+ parameters: jsonSchema,
51
+ strict: true,
52
+ },
53
+ } satisfies ChatCompletionFunctionTool
54
+ }
55
+
56
+ /**
57
+ * Converts an array of standard Tools to Chat Completions format.
58
+ * Chat Completions API primarily supports function tools.
59
+ */
60
+ export function convertToolsToChatCompletionsFormat(
61
+ tools: Array<Tool>,
62
+ schemaConverter?: (
63
+ schema: Record<string, any>,
64
+ required: Array<string>,
65
+ ) => Record<string, any>,
66
+ ): Array<ChatCompletionFunctionTool> {
67
+ return tools.map((tool) =>
68
+ convertFunctionToolToChatCompletionsFormat(tool, schemaConverter),
69
+ )
70
+ }
@@ -0,0 +1,158 @@
1
+ import { BaseImageAdapter } from '@tanstack/ai/adapters'
2
+ import { toRunErrorPayload } from '@tanstack/ai/adapter-internals'
3
+ import { generateId } from '@tanstack/ai-utils'
4
+ import { createOpenAICompatibleClient } from '../utils/client'
5
+ import type {
6
+ GeneratedImage,
7
+ ImageGenerationOptions,
8
+ ImageGenerationResult,
9
+ } from '@tanstack/ai'
10
+ import type OpenAI_SDK from 'openai'
11
+ import type { OpenAICompatibleClientConfig } from '../types/config'
12
+
13
+ /**
14
+ * OpenAI-Compatible Image Generation Adapter
15
+ *
16
+ * A generalized base class for providers that implement OpenAI-compatible image
17
+ * generation APIs. Providers like OpenAI, Grok, and others can extend this class
18
+ * and only need to:
19
+ * - Set `baseURL` in the config
20
+ * - Lock the generic type parameters to provider-specific types
21
+ * - Override validation or request building methods for provider-specific constraints
22
+ *
23
+ * All methods that validate inputs, build requests, or transform responses are
24
+ * `protected` so subclasses can override them.
25
+ */
26
+ export class OpenAICompatibleImageAdapter<
27
+ TModel extends string,
28
+ TProviderOptions extends object = Record<string, any>,
29
+ TModelProviderOptionsByName extends Record<string, any> = Record<string, any>,
30
+ TModelSizeByName extends Record<string, string> = Record<string, string>,
31
+ > extends BaseImageAdapter<
32
+ TModel,
33
+ TProviderOptions,
34
+ TModelProviderOptionsByName,
35
+ TModelSizeByName
36
+ > {
37
+ readonly kind = 'image' as const
38
+ readonly name: string
39
+
40
+ protected client: OpenAI_SDK
41
+
42
+ constructor(
43
+ config: OpenAICompatibleClientConfig,
44
+ model: TModel,
45
+ name: string = 'openai-compatible',
46
+ ) {
47
+ super(model, {})
48
+ this.name = name
49
+ this.client = createOpenAICompatibleClient(config)
50
+ }
51
+
52
+ async generateImages(
53
+ options: ImageGenerationOptions<TProviderOptions>,
54
+ ): Promise<ImageGenerationResult> {
55
+ const { model, prompt, numberOfImages, size } = options
56
+
57
+ // Validate inputs
58
+ this.validatePrompt({ prompt, model })
59
+ this.validateImageSize(model, size)
60
+ this.validateNumberOfImages(model, numberOfImages)
61
+
62
+ // Build request based on model type
63
+ const request = this.buildRequest(options)
64
+
65
+ try {
66
+ options.logger.request(
67
+ `activity=image provider=${this.name} model=${model} n=${request.n ?? 1} size=${request.size ?? 'default'}`,
68
+ { provider: this.name, model },
69
+ )
70
+ const response = await this.client.images.generate({
71
+ ...request,
72
+ stream: false,
73
+ })
74
+
75
+ return this.transformResponse(model, response)
76
+ } catch (error: unknown) {
77
+ // Narrow before logging: raw SDK errors can carry request metadata
78
+ // (including auth headers) which we must never surface to user loggers.
79
+ options.logger.errors(`${this.name}.generateImages fatal`, {
80
+ error: toRunErrorPayload(error, `${this.name}.generateImages failed`),
81
+ source: `${this.name}.generateImages`,
82
+ })
83
+ throw error
84
+ }
85
+ }
86
+
87
+ protected buildRequest(
88
+ options: ImageGenerationOptions<TProviderOptions>,
89
+ ): OpenAI_SDK.Images.ImageGenerateParams {
90
+ const { model, prompt, numberOfImages, size, modelOptions } = options
91
+
92
+ return {
93
+ model,
94
+ prompt,
95
+ n: numberOfImages ?? 1,
96
+ size: size as OpenAI_SDK.Images.ImageGenerateParams['size'],
97
+ ...modelOptions,
98
+ }
99
+ }
100
+
101
+ protected transformResponse(
102
+ model: string,
103
+ response: OpenAI_SDK.Images.ImagesResponse,
104
+ ): ImageGenerationResult {
105
+ const images: Array<GeneratedImage> = (response.data ?? []).flatMap(
106
+ (item): Array<GeneratedImage> => {
107
+ const revisedPrompt = item.revised_prompt
108
+ if (item.b64_json) {
109
+ return [{ b64Json: item.b64_json, revisedPrompt }]
110
+ }
111
+ if (item.url) {
112
+ return [{ url: item.url, revisedPrompt }]
113
+ }
114
+ return []
115
+ },
116
+ )
117
+
118
+ return {
119
+ id: generateId(this.name),
120
+ model,
121
+ images,
122
+ usage: response.usage
123
+ ? {
124
+ inputTokens: response.usage.input_tokens,
125
+ outputTokens: response.usage.output_tokens,
126
+ totalTokens: response.usage.total_tokens,
127
+ }
128
+ : undefined,
129
+ }
130
+ }
131
+
132
+ protected validatePrompt(options: { prompt: string; model: string }): void {
133
+ if (options.prompt.length === 0) {
134
+ throw new Error('Prompt cannot be empty.')
135
+ }
136
+ }
137
+
138
+ protected validateImageSize(_model: string, _size: string | undefined): void {
139
+ // Default: no size validation — subclasses can override
140
+ }
141
+
142
+ protected validateNumberOfImages(
143
+ _model: string,
144
+ numberOfImages: number | undefined,
145
+ ): void {
146
+ if (numberOfImages === undefined) return
147
+
148
+ // The base adapter only enforces "must be at least 1". Per-provider /
149
+ // per-model upper bounds vary widely (some support 4, some 10, some
150
+ // unlimited), so concrete adapter subclasses are expected to override
151
+ // this method with a model-specific cap.
152
+ if (numberOfImages < 1) {
153
+ throw new Error(
154
+ `Number of images must be at least 1. Requested: ${numberOfImages}`,
155
+ )
156
+ }
157
+ }
158
+ }