@tanstack/openai-base 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/esm/adapters/chat-completions-text.d.ts +2 -2
- package/dist/esm/adapters/chat-completions-text.js +183 -80
- package/dist/esm/adapters/chat-completions-text.js.map +1 -1
- package/dist/esm/adapters/chat-completions-tool-converter.js +3 -1
- package/dist/esm/adapters/chat-completions-tool-converter.js.map +1 -1
- package/dist/esm/adapters/image.js +33 -13
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/responses-text.d.ts +4 -5
- package/dist/esm/adapters/responses-text.js +260 -135
- package/dist/esm/adapters/responses-text.js.map +1 -1
- package/dist/esm/adapters/responses-tool-converter.js +3 -1
- package/dist/esm/adapters/responses-tool-converter.js.map +1 -1
- package/dist/esm/adapters/summarize.js +63 -25
- package/dist/esm/adapters/summarize.js.map +1 -1
- package/dist/esm/adapters/transcription.d.ts +0 -5
- package/dist/esm/adapters/transcription.js +53 -61
- package/dist/esm/adapters/transcription.js.map +1 -1
- package/dist/esm/adapters/tts.js +27 -14
- package/dist/esm/adapters/tts.js.map +1 -1
- package/dist/esm/adapters/video.d.ts +24 -0
- package/dist/esm/adapters/video.js +72 -26
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/index.d.ts +0 -2
- package/dist/esm/index.js +4 -2
- package/dist/esm/tools/apply-patch-tool.d.ts +8 -3
- package/dist/esm/tools/apply-patch-tool.js.map +1 -1
- package/dist/esm/tools/code-interpreter-tool.d.ts +9 -4
- package/dist/esm/tools/code-interpreter-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.d.ts +9 -4
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/custom-tool.d.ts +6 -4
- package/dist/esm/tools/custom-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.d.ts +11 -4
- package/dist/esm/tools/file-search-tool.js +3 -2
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-tool.d.ts +4 -2
- package/dist/esm/tools/function-tool.js.map +1 -1
- package/dist/esm/tools/image-generation-tool.d.ts +16 -5
- package/dist/esm/tools/image-generation-tool.js +4 -3
- package/dist/esm/tools/image-generation-tool.js.map +1 -1
- package/dist/esm/tools/index.d.ts +13 -13
- package/dist/esm/tools/local-shell-tool.d.ts +8 -3
- package/dist/esm/tools/local-shell-tool.js.map +1 -1
- package/dist/esm/tools/mcp-tool.d.ts +10 -5
- package/dist/esm/tools/mcp-tool.js.map +1 -1
- package/dist/esm/tools/shell-tool.d.ts +8 -3
- package/dist/esm/tools/shell-tool.js.map +1 -1
- package/dist/esm/tools/web-search-preview-tool.d.ts +13 -5
- package/dist/esm/tools/web-search-preview-tool.js +2 -3
- package/dist/esm/tools/web-search-preview-tool.js.map +1 -1
- package/dist/esm/tools/web-search-tool.d.ts +14 -5
- package/dist/esm/tools/web-search-tool.js +4 -1
- package/dist/esm/tools/web-search-tool.js.map +1 -1
- package/dist/esm/utils/request-options.d.ts +14 -0
- package/dist/esm/utils/request-options.js +8 -0
- package/dist/esm/utils/request-options.js.map +1 -0
- package/package.json +16 -19
- package/dist/esm/types/message-metadata.d.ts +0 -19
- package/dist/esm/types/provider-options.d.ts +0 -40
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcription.js","sources":["../../../src/adapters/transcription.ts"],"sourcesContent":["import { BaseTranscriptionAdapter } from '@tanstack/ai/adapters'\nimport { generateId } from '@tanstack/ai-utils'\nimport { createOpenAICompatibleClient } from '../utils/client'\nimport type {\n TranscriptionOptions,\n TranscriptionResult,\n TranscriptionSegment,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { OpenAICompatibleClientConfig } from '../types/config'\n\n/**\n * OpenAI-Compatible Transcription (Speech-to-Text) Adapter\n *\n * A generalized base class for providers that implement OpenAI-compatible audio\n * transcription APIs. Providers can extend this class and only need to:\n * - Set `baseURL` in the config\n * - Lock the generic type parameters to provider-specific types\n * - Override audio handling or response mapping methods as needed\n *\n * All methods that handle audio input or map response formats are `protected`\n * so subclasses can override them.\n */\nexport class OpenAICompatibleTranscriptionAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, any>,\n> extends BaseTranscriptionAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n protected client: OpenAI_SDK\n\n constructor(\n config: OpenAICompatibleClientConfig,\n model: TModel,\n name: string = 'openai-compatible',\n ) {\n super(
|
|
1
|
+
{"version":3,"file":"transcription.js","sources":["../../../src/adapters/transcription.ts"],"sourcesContent":["import { BaseTranscriptionAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { base64ToArrayBuffer, generateId } from '@tanstack/ai-utils'\nimport { createOpenAICompatibleClient } from '../utils/client'\nimport type {\n TranscriptionOptions,\n TranscriptionResult,\n TranscriptionSegment,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { OpenAICompatibleClientConfig } from '../types/config'\n\n/**\n * OpenAI-Compatible Transcription (Speech-to-Text) Adapter\n *\n * A generalized base class for providers that implement OpenAI-compatible audio\n * transcription APIs. Providers can extend this class and only need to:\n * - Set `baseURL` in the config\n * - Lock the generic type parameters to provider-specific types\n * - Override audio handling or response mapping methods as needed\n *\n * All methods that handle audio input or map response formats are `protected`\n * so subclasses can override them.\n */\nexport class OpenAICompatibleTranscriptionAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, any>,\n> extends BaseTranscriptionAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n protected client: OpenAI_SDK\n\n constructor(\n config: OpenAICompatibleClientConfig,\n model: TModel,\n name: string = 'openai-compatible',\n ) {\n super(model, {})\n this.name = name\n this.client = createOpenAICompatibleClient(config)\n }\n\n async transcribe(\n options: TranscriptionOptions<TProviderOptions>,\n ): Promise<TranscriptionResult> {\n const { model, audio, language, prompt, responseFormat, modelOptions } =\n options\n\n // Convert audio input to File object\n const file = this.prepareAudioFile(audio)\n\n // Build request\n const request: OpenAI_SDK.Audio.TranscriptionCreateParams = {\n model,\n file,\n language,\n prompt,\n response_format: this.mapResponseFormat(responseFormat),\n ...modelOptions,\n }\n\n // Call API - use verbose_json to get timestamps when available\n const useVerbose =\n responseFormat === 'verbose_json' ||\n (!responseFormat && this.shouldDefaultToVerbose(model))\n\n try {\n options.logger.request(\n `activity=transcription provider=${this.name} model=${model} verbose=${useVerbose}`,\n { provider: this.name, model },\n )\n if (useVerbose) {\n const response = await this.client.audio.transcriptions.create({\n ...request,\n response_format: 'verbose_json',\n })\n\n return {\n id: generateId(this.name),\n model,\n text: response.text,\n language: response.language,\n duration: response.duration,\n segments: response.segments?.map(\n (seg): TranscriptionSegment => ({\n id: seg.id,\n start: seg.start,\n end: seg.end,\n text: seg.text,\n // The OpenAI SDK types `avg_logprob` as `number`, so call Math.exp\n // directly. Previously this was guarded with `seg.avg_logprob ?`\n // which treated `0` (perfect-confidence) as missing.\n confidence: Math.exp(seg.avg_logprob),\n }),\n ),\n words: response.words?.map((w) => ({\n word: w.word,\n start: w.start,\n end: w.end,\n })),\n }\n } else {\n const response = await this.client.audio.transcriptions.create(request)\n\n return {\n id: generateId(this.name),\n model,\n text: typeof response === 'string' ? response : response.text,\n language,\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.transcribe fatal`, {\n error: toRunErrorPayload(error, `${this.name}.transcribe failed`),\n source: `${this.name}.transcribe`,\n })\n throw error\n }\n }\n\n protected prepareAudioFile(audio: string | File | Blob | ArrayBuffer): File {\n // If already a File, return it\n if (typeof File !== 'undefined' && audio instanceof File) {\n return audio\n }\n\n // If Blob, convert to File\n if (typeof Blob !== 'undefined' && audio instanceof Blob) {\n this.ensureFileSupport()\n return new File([audio], 'audio.mp3', {\n type: audio.type || 'audio/mpeg',\n })\n }\n\n // If ArrayBuffer, convert to File\n if (typeof ArrayBuffer !== 'undefined' && audio instanceof ArrayBuffer) {\n this.ensureFileSupport()\n return new File([audio], 'audio.mp3', { type: 'audio/mpeg' })\n }\n\n // If base64 string, decode and convert to File\n if (typeof audio === 'string') {\n this.ensureFileSupport()\n\n // Check if it's a data URL\n if (audio.startsWith('data:')) {\n const parts = audio.split(',')\n const header = parts[0]\n const base64Data = parts[1] || ''\n const mimeMatch = header?.match(/data:([^;]+)/)\n const mimeType = mimeMatch?.[1] || 'audio/mpeg'\n const bytes = base64ToArrayBuffer(base64Data)\n const extension = mimeType.split('/')[1] || 'mp3'\n return new File([bytes], `audio.${extension}`, { type: mimeType })\n }\n\n // Assume raw base64\n const bytes = base64ToArrayBuffer(audio)\n return new File([bytes], 'audio.mp3', { type: 'audio/mpeg' })\n }\n\n throw new Error('Invalid audio input type')\n }\n\n /**\n * Checks that the global `File` constructor is available.\n * Throws a descriptive error in environments that lack it (e.g. Node < 20).\n */\n private ensureFileSupport(): void {\n if (typeof File === 'undefined') {\n throw new Error(\n '`File` is not available in this environment. ' +\n 'Use Node.js 20 or newer, or pass a File object directly.',\n )\n }\n }\n\n /**\n * Whether the adapter should default to verbose_json when no response format is specified.\n * Override in provider-specific subclasses for model-specific behavior.\n */\n protected shouldDefaultToVerbose(_model: string): boolean {\n return false\n }\n\n protected mapResponseFormat(\n format?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt',\n ): OpenAI_SDK.Audio.TranscriptionCreateParams['response_format'] {\n if (!format) return 'json'\n return format as OpenAI_SDK.Audio.TranscriptionCreateParams['response_format']\n }\n}\n"],"names":["bytes"],"mappings":"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"}
|
package/dist/esm/adapters/tts.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { BaseTTSAdapter } from "@tanstack/ai/adapters";
|
|
2
|
-
import {
|
|
2
|
+
import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
|
|
3
|
+
import { arrayBufferToBase64, generateId } from "@tanstack/ai-utils";
|
|
3
4
|
import { createOpenAICompatibleClient } from "../utils/client.js";
|
|
4
5
|
class OpenAICompatibleTTSAdapter extends BaseTTSAdapter {
|
|
5
6
|
constructor(config, model, name = "openai-compatible") {
|
|
6
|
-
super(
|
|
7
|
+
super(model, {});
|
|
7
8
|
this.name = name;
|
|
8
9
|
this.client = createOpenAICompatibleClient(config);
|
|
9
10
|
}
|
|
@@ -20,18 +21,30 @@ class OpenAICompatibleTTSAdapter extends BaseTTSAdapter {
|
|
|
20
21
|
speed,
|
|
21
22
|
...modelOptions
|
|
22
23
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
try {
|
|
25
|
+
options.logger.request(
|
|
26
|
+
`activity=tts provider=${this.name} model=${model} format=${request.response_format ?? "default"} voice=${request.voice}`,
|
|
27
|
+
{ provider: this.name, model }
|
|
28
|
+
);
|
|
29
|
+
const response = await this.client.audio.speech.create(request);
|
|
30
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
31
|
+
const base64 = arrayBufferToBase64(arrayBuffer);
|
|
32
|
+
const outputFormat = request.response_format || "mp3";
|
|
33
|
+
const contentType = this.getContentType(outputFormat);
|
|
34
|
+
return {
|
|
35
|
+
id: generateId(this.name),
|
|
36
|
+
model,
|
|
37
|
+
audio: base64,
|
|
38
|
+
format: outputFormat,
|
|
39
|
+
contentType
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
options.logger.errors(`${this.name}.generateSpeech fatal`, {
|
|
43
|
+
error: toRunErrorPayload(error, `${this.name}.generateSpeech failed`),
|
|
44
|
+
source: `${this.name}.generateSpeech`
|
|
45
|
+
});
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
35
48
|
}
|
|
36
49
|
validateAudioInput(text) {
|
|
37
50
|
if (text.length > 4096) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tts.js","sources":["../../../src/adapters/tts.ts"],"sourcesContent":["import { BaseTTSAdapter } from '@tanstack/ai/adapters'\nimport { generateId } from '@tanstack/ai-utils'\nimport { createOpenAICompatibleClient } from '../utils/client'\nimport type { TTSOptions, TTSResult } from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { OpenAICompatibleClientConfig } from '../types/config'\n\n/**\n * OpenAI-Compatible Text-to-Speech Adapter\n *\n * A generalized base class for providers that implement OpenAI-compatible TTS APIs.\n * Providers can extend this class and only need to:\n * - Set `baseURL` in the config\n * - Lock the generic type parameters to provider-specific types\n * - Override validation methods or request building for provider-specific constraints\n *\n * All methods that validate inputs or build requests are `protected` so subclasses\n * can override them.\n */\nexport class OpenAICompatibleTTSAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, any>,\n> extends BaseTTSAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n protected client: OpenAI_SDK\n\n constructor(\n config: OpenAICompatibleClientConfig,\n model: TModel,\n name: string = 'openai-compatible',\n ) {\n super(
|
|
1
|
+
{"version":3,"file":"tts.js","sources":["../../../src/adapters/tts.ts"],"sourcesContent":["import { BaseTTSAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { arrayBufferToBase64, generateId } from '@tanstack/ai-utils'\nimport { createOpenAICompatibleClient } from '../utils/client'\nimport type { TTSOptions, TTSResult } from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { OpenAICompatibleClientConfig } from '../types/config'\n\n/**\n * OpenAI-Compatible Text-to-Speech Adapter\n *\n * A generalized base class for providers that implement OpenAI-compatible TTS APIs.\n * Providers can extend this class and only need to:\n * - Set `baseURL` in the config\n * - Lock the generic type parameters to provider-specific types\n * - Override validation methods or request building for provider-specific constraints\n *\n * All methods that validate inputs or build requests are `protected` so subclasses\n * can override them.\n */\nexport class OpenAICompatibleTTSAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, any>,\n> extends BaseTTSAdapter<TModel, TProviderOptions> {\n readonly name: string\n\n protected client: OpenAI_SDK\n\n constructor(\n config: OpenAICompatibleClientConfig,\n model: TModel,\n name: string = 'openai-compatible',\n ) {\n super(model, {})\n this.name = name\n this.client = createOpenAICompatibleClient(config)\n }\n\n async generateSpeech(\n options: TTSOptions<TProviderOptions>,\n ): Promise<TTSResult> {\n const { model, text, voice, format, speed, modelOptions } = options\n\n // Validate inputs\n this.validateAudioInput(text)\n this.validateSpeed(speed)\n this.validateInstructions(model, modelOptions)\n\n // Build request\n const request: OpenAI_SDK.Audio.SpeechCreateParams = {\n model,\n input: text,\n voice: (voice || 'alloy') as OpenAI_SDK.Audio.SpeechCreateParams['voice'],\n response_format: format,\n speed,\n ...modelOptions,\n }\n\n try {\n options.logger.request(\n `activity=tts provider=${this.name} model=${model} format=${request.response_format ?? 'default'} voice=${request.voice}`,\n { provider: this.name, model },\n )\n const response = await this.client.audio.speech.create(request)\n\n // Convert response to base64. Buffer is Node-only; use atob fallback in\n // browser/edge runtimes where the SDK can run.\n const arrayBuffer = await response.arrayBuffer()\n const base64 = arrayBufferToBase64(arrayBuffer)\n\n const outputFormat = (request.response_format as string) || 'mp3'\n const contentType = this.getContentType(outputFormat)\n\n return {\n id: generateId(this.name),\n model,\n audio: base64,\n format: outputFormat,\n contentType,\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.generateSpeech fatal`, {\n error: toRunErrorPayload(error, `${this.name}.generateSpeech failed`),\n source: `${this.name}.generateSpeech`,\n })\n throw error\n }\n }\n\n protected validateAudioInput(text: string): void {\n if (text.length > 4096) {\n throw new Error('Input text exceeds maximum length of 4096 characters.')\n }\n }\n\n protected validateSpeed(speed?: number): void {\n if (speed !== undefined) {\n if (speed < 0.25 || speed > 4.0) {\n throw new Error('Speed must be between 0.25 and 4.0.')\n }\n }\n }\n\n protected validateInstructions(\n _model: string,\n _modelOptions?: TProviderOptions,\n ): void {\n // Default: no instructions validation — subclasses can override\n }\n\n protected getContentType(format: string): string {\n const contentTypes: Record<string, string> = {\n mp3: 'audio/mpeg',\n opus: 'audio/opus',\n aac: 'audio/aac',\n flac: 'audio/flac',\n wav: 'audio/wav',\n pcm: 'audio/pcm',\n }\n return contentTypes[format] || 'audio/mpeg'\n }\n}\n"],"names":[],"mappings":";;;;AAoBO,MAAM,mCAGH,eAAyC;AAAA,EAKjD,YACE,QACA,OACA,OAAe,qBACf;AACA,UAAM,OAAO,EAAE;AACf,SAAK,OAAO;AACZ,SAAK,SAAS,6BAA6B,MAAM;AAAA,EACnD;AAAA,EAEA,MAAM,eACJ,SACoB;AACpB,UAAM,EAAE,OAAO,MAAM,OAAO,QAAQ,OAAO,iBAAiB;AAG5D,SAAK,mBAAmB,IAAI;AAC5B,SAAK,cAAc,KAAK;AACxB,SAAK,qBAAqB,OAAO,YAAY;AAG7C,UAAM,UAA+C;AAAA,MACnD;AAAA,MACA,OAAO;AAAA,MACP,OAAQ,SAAS;AAAA,MACjB,iBAAiB;AAAA,MACjB;AAAA,MACA,GAAG;AAAA,IAAA;AAGL,QAAI;AACF,cAAQ,OAAO;AAAA,QACb,yBAAyB,KAAK,IAAI,UAAU,KAAK,WAAW,QAAQ,mBAAmB,SAAS,UAAU,QAAQ,KAAK;AAAA,QACvH,EAAE,UAAU,KAAK,MAAM,MAAA;AAAA,MAAM;AAE/B,YAAM,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,OAAO,OAAO;AAI9D,YAAM,cAAc,MAAM,SAAS,YAAA;AACnC,YAAM,SAAS,oBAAoB,WAAW;AAE9C,YAAM,eAAgB,QAAQ,mBAA8B;AAC5D,YAAM,cAAc,KAAK,eAAe,YAAY;AAEpD,aAAO;AAAA,QACL,IAAI,WAAW,KAAK,IAAI;AAAA,QACxB;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ,SAAS,OAAgB;AAGvB,cAAQ,OAAO,OAAO,GAAG,KAAK,IAAI,yBAAyB;AAAA,QACzD,OAAO,kBAAkB,OAAO,GAAG,KAAK,IAAI,wBAAwB;AAAA,QACpE,QAAQ,GAAG,KAAK,IAAI;AAAA,MAAA,CACrB;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEU,mBAAmB,MAAoB;AAC/C,QAAI,KAAK,SAAS,MAAM;AACtB,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AAAA,EACF;AAAA,EAEU,cAAc,OAAsB;AAC5C,QAAI,UAAU,QAAW;AACvB,UAAI,QAAQ,QAAQ,QAAQ,GAAK;AAC/B,cAAM,IAAI,MAAM,qCAAqC;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AAAA,EAEU,qBACR,QACA,eACM;AAAA,EAER;AAAA,EAEU,eAAe,QAAwB;AAC/C,UAAM,eAAuC;AAAA,MAC3C,KAAK;AAAA,MACL,MAAM;AAAA,MACN,KAAK;AAAA,MACL,MAAM;AAAA,MACN,KAAK;AAAA,MACL,KAAK;AAAA,IAAA;AAEP,WAAO,aAAa,MAAM,KAAK;AAAA,EACjC;AACF;"}
|
|
@@ -29,6 +29,30 @@ export declare class OpenAICompatibleVideoAdapter<TModel extends string, TProvid
|
|
|
29
29
|
* @experimental Video generation is an experimental feature and may change.
|
|
30
30
|
*/
|
|
31
31
|
createVideoJob(options: VideoGenerationOptions<TProviderOptions>): Promise<VideoJobResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Returns the underlying OpenAI Videos resource. Pulled out as a protected
|
|
34
|
+
* accessor so subclasses targeting forks of the SDK can swap the access
|
|
35
|
+
* path without forcing each call site to cast through `any`.
|
|
36
|
+
*/
|
|
37
|
+
protected getVideosClient(): {
|
|
38
|
+
create: (req: Record<string, any>) => Promise<{
|
|
39
|
+
id: string;
|
|
40
|
+
}>;
|
|
41
|
+
retrieve: (id: string) => Promise<{
|
|
42
|
+
id: string;
|
|
43
|
+
status: string;
|
|
44
|
+
progress?: number;
|
|
45
|
+
url?: string;
|
|
46
|
+
expires_at?: number;
|
|
47
|
+
error?: {
|
|
48
|
+
message?: string;
|
|
49
|
+
};
|
|
50
|
+
}>;
|
|
51
|
+
downloadContent?: (id: string) => Promise<Response>;
|
|
52
|
+
content?: (id: string) => Promise<unknown>;
|
|
53
|
+
getContent?: (id: string) => Promise<unknown>;
|
|
54
|
+
download?: (id: string) => Promise<unknown>;
|
|
55
|
+
};
|
|
32
56
|
/**
|
|
33
57
|
* Get the current status of a video generation job.
|
|
34
58
|
*
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { BaseVideoAdapter } from "@tanstack/ai/adapters";
|
|
2
|
+
import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
|
|
3
|
+
import { arrayBufferToBase64 } from "@tanstack/ai-utils";
|
|
2
4
|
import { createOpenAICompatibleClient } from "../utils/client.js";
|
|
5
|
+
const LARGE_MEDIA_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
6
|
+
function warnIfLargeMediaBuffer(byteLength, source, providerName) {
|
|
7
|
+
if (byteLength <= LARGE_MEDIA_BUFFER_BYTES) return;
|
|
8
|
+
console.warn(
|
|
9
|
+
`[${providerName}.${source}] downloaded ${(byteLength / 1024 / 1024).toFixed(1)} MiB into memory before base64 encoding. Workers/serverless runtimes commonly run out of memory above ~10 MiB. Consider streaming the video through a CDN or your own storage layer instead.`
|
|
10
|
+
);
|
|
11
|
+
}
|
|
3
12
|
class OpenAICompatibleVideoAdapter extends BaseVideoAdapter {
|
|
4
13
|
constructor(config, model, name = "openai-compatible") {
|
|
5
14
|
super(config, model);
|
|
@@ -19,13 +28,21 @@ class OpenAICompatibleVideoAdapter extends BaseVideoAdapter {
|
|
|
19
28
|
this.validateVideoSeconds(model, seconds);
|
|
20
29
|
const request = this.buildRequest(options);
|
|
21
30
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
options.logger.request(
|
|
32
|
+
`activity=video.create provider=${this.name} model=${model} size=${request.size ?? "default"} seconds=${request.seconds ?? "default"}`,
|
|
33
|
+
{ provider: this.name, model }
|
|
34
|
+
);
|
|
35
|
+
const videosClient = this.getVideosClient();
|
|
36
|
+
const response = await videosClient.create(request);
|
|
24
37
|
return {
|
|
25
38
|
jobId: response.id,
|
|
26
39
|
model
|
|
27
40
|
};
|
|
28
41
|
} catch (error) {
|
|
42
|
+
options.logger.errors(`${this.name}.createVideoJob fatal`, {
|
|
43
|
+
error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),
|
|
44
|
+
source: `${this.name}.createVideoJob`
|
|
45
|
+
});
|
|
29
46
|
if (error?.message?.includes("videos") || error?.code === "invalid_api") {
|
|
30
47
|
throw new Error(
|
|
31
48
|
`Video generation API is not available. The API may require special access. Original error: ${error.message}`
|
|
@@ -34,6 +51,14 @@ class OpenAICompatibleVideoAdapter extends BaseVideoAdapter {
|
|
|
34
51
|
throw error;
|
|
35
52
|
}
|
|
36
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns the underlying OpenAI Videos resource. Pulled out as a protected
|
|
56
|
+
* accessor so subclasses targeting forks of the SDK can swap the access
|
|
57
|
+
* path without forcing each call site to cast through `any`.
|
|
58
|
+
*/
|
|
59
|
+
getVideosClient() {
|
|
60
|
+
return this.client.videos;
|
|
61
|
+
}
|
|
37
62
|
/**
|
|
38
63
|
* Get the current status of a video generation job.
|
|
39
64
|
*
|
|
@@ -41,8 +66,8 @@ class OpenAICompatibleVideoAdapter extends BaseVideoAdapter {
|
|
|
41
66
|
*/
|
|
42
67
|
async getVideoStatus(jobId) {
|
|
43
68
|
try {
|
|
44
|
-
const
|
|
45
|
-
const response = await
|
|
69
|
+
const videosClient = this.getVideosClient();
|
|
70
|
+
const response = await videosClient.retrieve(jobId);
|
|
46
71
|
return {
|
|
47
72
|
jobId,
|
|
48
73
|
status: this.mapStatus(response.status),
|
|
@@ -67,34 +92,40 @@ class OpenAICompatibleVideoAdapter extends BaseVideoAdapter {
|
|
|
67
92
|
*/
|
|
68
93
|
async getVideoUrl(jobId) {
|
|
69
94
|
try {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
95
|
+
const videosClient = this.getVideosClient();
|
|
96
|
+
const videoInfo = await videosClient.retrieve(jobId);
|
|
97
|
+
if (videoInfo.url) {
|
|
98
|
+
return {
|
|
99
|
+
jobId,
|
|
100
|
+
url: videoInfo.url,
|
|
101
|
+
expiresAt: videoInfo.expires_at ? new Date(videoInfo.expires_at) : void 0
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (typeof videosClient.downloadContent === "function") {
|
|
105
|
+
const contentResponse = await videosClient.downloadContent(jobId);
|
|
74
106
|
const videoBlob = await contentResponse.blob();
|
|
75
107
|
const buffer = await videoBlob.arrayBuffer();
|
|
76
|
-
|
|
108
|
+
warnIfLargeMediaBuffer(
|
|
109
|
+
buffer.byteLength,
|
|
110
|
+
"video.downloadContent",
|
|
111
|
+
this.name
|
|
112
|
+
);
|
|
113
|
+
const base64 = arrayBufferToBase64(buffer);
|
|
77
114
|
const mimeType = contentResponse.headers.get("content-type") || "video/mp4";
|
|
78
115
|
return {
|
|
79
116
|
jobId,
|
|
80
117
|
url: `data:${mimeType};base64,${base64}`,
|
|
81
118
|
expiresAt: void 0
|
|
82
119
|
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
response = await
|
|
87
|
-
} else if (typeof
|
|
88
|
-
response = await
|
|
120
|
+
}
|
|
121
|
+
let response;
|
|
122
|
+
if (typeof videosClient.content === "function") {
|
|
123
|
+
response = await videosClient.content(jobId);
|
|
124
|
+
} else if (typeof videosClient.getContent === "function") {
|
|
125
|
+
response = await videosClient.getContent(jobId);
|
|
126
|
+
} else if (typeof videosClient.download === "function") {
|
|
127
|
+
response = await videosClient.download(jobId);
|
|
89
128
|
} else {
|
|
90
|
-
const videoInfo = await client.videos.retrieve(jobId);
|
|
91
|
-
if (videoInfo.url) {
|
|
92
|
-
return {
|
|
93
|
-
jobId,
|
|
94
|
-
url: videoInfo.url,
|
|
95
|
-
expiresAt: videoInfo.expires_at ? new Date(videoInfo.expires_at * 1e3) : void 0
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
129
|
const baseUrl = this.clientConfig.baseURL || "https://api.openai.com/v1";
|
|
99
130
|
const apiKey = this.clientConfig.apiKey;
|
|
100
131
|
const contentResponse = await fetch(
|
|
@@ -120,7 +151,8 @@ class OpenAICompatibleVideoAdapter extends BaseVideoAdapter {
|
|
|
120
151
|
}
|
|
121
152
|
const videoBlob = await contentResponse.blob();
|
|
122
153
|
const buffer = await videoBlob.arrayBuffer();
|
|
123
|
-
|
|
154
|
+
warnIfLargeMediaBuffer(buffer.byteLength, "video.fetch", this.name);
|
|
155
|
+
const base64 = arrayBufferToBase64(buffer);
|
|
124
156
|
const mimeType = contentResponse.headers.get("content-type") || "video/mp4";
|
|
125
157
|
return {
|
|
126
158
|
jobId,
|
|
@@ -128,10 +160,24 @@ class OpenAICompatibleVideoAdapter extends BaseVideoAdapter {
|
|
|
128
160
|
expiresAt: void 0
|
|
129
161
|
};
|
|
130
162
|
}
|
|
163
|
+
const fallthroughBlob = typeof response?.blob === "function" ? await response.blob() : response instanceof Blob ? response : null;
|
|
164
|
+
if (!fallthroughBlob) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Video content download via SDK fall-through returned an unexpected shape (no blob()). Override getVideoUrl() in your subclass to handle this provider.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const fallthroughBuffer = await fallthroughBlob.arrayBuffer();
|
|
170
|
+
warnIfLargeMediaBuffer(
|
|
171
|
+
fallthroughBuffer.byteLength,
|
|
172
|
+
"video.sdkFallthrough",
|
|
173
|
+
this.name
|
|
174
|
+
);
|
|
175
|
+
const fallthroughBase64 = arrayBufferToBase64(fallthroughBuffer);
|
|
176
|
+
const fallthroughMime = (typeof response?.headers?.get === "function" ? response.headers.get("content-type") : void 0) || fallthroughBlob.type || "video/mp4";
|
|
131
177
|
return {
|
|
132
178
|
jobId,
|
|
133
|
-
url:
|
|
134
|
-
expiresAt:
|
|
179
|
+
url: `data:${fallthroughMime};base64,${fallthroughBase64}`,
|
|
180
|
+
expiresAt: void 0
|
|
135
181
|
};
|
|
136
182
|
} catch (error) {
|
|
137
183
|
if (error.status === 404) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.js","sources":["../../../src/adapters/video.ts"],"sourcesContent":["import { BaseVideoAdapter } from '@tanstack/ai/adapters'\nimport { createOpenAICompatibleClient } from '../utils/client'\nimport type {\n VideoGenerationOptions,\n VideoJobResult,\n VideoStatusResult,\n VideoUrlResult,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { OpenAICompatibleClientConfig } from '../types/config'\n\n/**\n * OpenAI-Compatible Video Generation Adapter\n *\n * A generalized base class for providers that implement OpenAI-compatible video\n * generation APIs. Uses a job/polling architecture for async video generation.\n *\n * Providers can extend this class and only need to:\n * - Set `baseURL` in the config\n * - Lock the generic type parameters to provider-specific types\n * - Override validation or request building methods as needed\n *\n * All methods that validate inputs, build requests, or map responses are `protected`\n * so subclasses can override them.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport class OpenAICompatibleVideoAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, any>,\n TModelProviderOptionsByName extends Record<string, any> = Record<string, any>,\n TModelSizeByName extends Record<string, string> = Record<string, string>,\n> extends BaseVideoAdapter<\n TModel,\n TProviderOptions,\n TModelProviderOptionsByName,\n TModelSizeByName\n> {\n readonly name: string\n\n protected client: OpenAI_SDK\n protected clientConfig: OpenAICompatibleClientConfig\n\n constructor(\n config: OpenAICompatibleClientConfig,\n model: TModel,\n name: string = 'openai-compatible',\n ) {\n super(config, model)\n this.name = name\n this.clientConfig = config\n this.client = createOpenAICompatibleClient(config)\n }\n\n /**\n * Create a new video generation job.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n async createVideoJob(\n options: VideoGenerationOptions<TProviderOptions>,\n ): Promise<VideoJobResult> {\n const { model, size, duration, modelOptions } = options\n\n // Validate inputs\n this.validateVideoSize(model, size)\n const seconds = duration ?? (modelOptions as any)?.seconds\n this.validateVideoSeconds(model, seconds)\n\n // Build request\n const request = this.buildRequest(options)\n\n try {\n const client = this.client as any\n const response = await client.videos.create(request)\n\n return {\n jobId: response.id,\n model,\n }\n } catch (error: any) {\n if (error?.message?.includes('videos') || error?.code === 'invalid_api') {\n throw new Error(\n `Video generation API is not available. The API may require special access. ` +\n `Original error: ${error.message}`,\n )\n }\n throw error\n }\n }\n\n /**\n * Get the current status of a video generation job.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n async getVideoStatus(jobId: string): Promise<VideoStatusResult> {\n try {\n const client = this.client as any\n const response = await client.videos.retrieve(jobId)\n\n return {\n jobId,\n status: this.mapStatus(response.status),\n progress: response.progress,\n error: response.error?.message,\n }\n } catch (error: any) {\n if (error.status === 404) {\n return {\n jobId,\n status: 'failed',\n error: 'Job not found',\n }\n }\n throw error\n }\n }\n\n /**\n * Get the URL to download/view the generated video.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n async getVideoUrl(jobId: string): Promise<VideoUrlResult> {\n try {\n const client = this.client as any\n\n let response: any\n\n if (typeof client.videos?.downloadContent === 'function') {\n // OpenAI SDK's downloadContent returns raw video bytes as a Response\n const contentResponse = await client.videos.downloadContent(jobId)\n const videoBlob = await contentResponse.blob()\n const buffer = await videoBlob.arrayBuffer()\n const base64 = Buffer.from(buffer).toString('base64')\n const mimeType =\n contentResponse.headers.get('content-type') || 'video/mp4'\n return {\n jobId,\n url: `data:${mimeType};base64,${base64}`,\n expiresAt: undefined,\n }\n } else if (typeof client.videos?.content === 'function') {\n response = await client.videos.content(jobId)\n } else if (typeof client.videos?.getContent === 'function') {\n response = await client.videos.getContent(jobId)\n } else if (typeof client.videos?.download === 'function') {\n response = await client.videos.download(jobId)\n } else {\n // Fallback: check if retrieve returns the URL directly\n const videoInfo = await client.videos.retrieve(jobId)\n if (videoInfo.url) {\n return {\n jobId,\n url: videoInfo.url,\n expiresAt: videoInfo.expires_at\n ? new Date(videoInfo.expires_at * 1000)\n : undefined,\n }\n }\n\n // Fetch and return a data URL\n const baseUrl = this.clientConfig.baseURL || 'https://api.openai.com/v1'\n const apiKey = this.clientConfig.apiKey\n\n const contentResponse = await fetch(\n `${baseUrl}/videos/${jobId}/content`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n },\n )\n\n if (!contentResponse.ok) {\n const contentType = contentResponse.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n const errorData = await contentResponse.json().catch(() => ({}))\n throw new Error(\n errorData.error?.message ||\n `Failed to get video content: ${contentResponse.status}`,\n )\n }\n throw new Error(\n `Failed to get video content: ${contentResponse.status}`,\n )\n }\n\n const videoBlob = await contentResponse.blob()\n const buffer = await videoBlob.arrayBuffer()\n const base64 = Buffer.from(buffer).toString('base64')\n const mimeType =\n contentResponse.headers.get('content-type') || 'video/mp4'\n\n return {\n jobId,\n url: `data:${mimeType};base64,${base64}`,\n expiresAt: undefined,\n }\n }\n\n return {\n jobId,\n url: response.url,\n expiresAt: response.expires_at\n ? new Date(response.expires_at * 1000)\n : undefined,\n }\n } catch (error: any) {\n if (error.status === 404) {\n throw new Error(`Video job not found: ${jobId}`)\n }\n if (error.status === 400) {\n throw new Error(\n `Video is not ready for download. Check status first. Job ID: ${jobId}`,\n )\n }\n throw error\n }\n }\n\n protected buildRequest(\n options: VideoGenerationOptions<TProviderOptions>,\n ): Record<string, any> {\n const { model, prompt, size, duration, modelOptions } = options\n\n const request: Record<string, any> = {\n model,\n prompt,\n }\n\n if (size) {\n request['size'] = size\n } else if ((modelOptions as any)?.size) {\n request['size'] = (modelOptions as any).size\n }\n\n const seconds = duration ?? (modelOptions as any)?.seconds\n if (seconds !== undefined) {\n request['seconds'] = String(seconds)\n }\n\n return request\n }\n\n protected validateVideoSize(_model: string, _size?: string): void {\n // Default: no size validation — subclasses can override\n }\n\n protected validateVideoSeconds(\n _model: string,\n _seconds?: number | string,\n ): void {\n // Default: no duration validation — subclasses can override\n }\n\n protected mapStatus(\n apiStatus: string,\n ): 'pending' | 'processing' | 'completed' | 'failed' {\n switch (apiStatus) {\n case 'queued':\n case 'pending':\n return 'pending'\n case 'processing':\n case 'in_progress':\n return 'processing'\n case 'completed':\n case 'succeeded':\n return 'completed'\n case 'failed':\n case 'error':\n case 'cancelled':\n return 'failed'\n default:\n return 'processing'\n }\n }\n}\n"],"names":[],"mappings":";;AA2BO,MAAM,qCAKH,iBAKR;AAAA,EAMA,YACE,QACA,OACA,OAAe,qBACf;AACA,UAAM,QAAQ,KAAK;AACnB,SAAK,OAAO;AACZ,SAAK,eAAe;AACpB,SAAK,SAAS,6BAA6B,MAAM;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eACJ,SACyB;AACzB,UAAM,EAAE,OAAO,MAAM,UAAU,iBAAiB;AAGhD,SAAK,kBAAkB,OAAO,IAAI;AAClC,UAAM,UAAU,YAAa,cAAsB;AACnD,SAAK,qBAAqB,OAAO,OAAO;AAGxC,UAAM,UAAU,KAAK,aAAa,OAAO;AAEzC,QAAI;AACF,YAAM,SAAS,KAAK;AACpB,YAAM,WAAW,MAAM,OAAO,OAAO,OAAO,OAAO;AAEnD,aAAO;AAAA,QACL,OAAO,SAAS;AAAA,QAChB;AAAA,MAAA;AAAA,IAEJ,SAAS,OAAY;AACnB,UAAI,OAAO,SAAS,SAAS,QAAQ,KAAK,OAAO,SAAS,eAAe;AACvE,cAAM,IAAI;AAAA,UACR,8FACqB,MAAM,OAAO;AAAA,QAAA;AAAA,MAEtC;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAe,OAA2C;AAC9D,QAAI;AACF,YAAM,SAAS,KAAK;AACpB,YAAM,WAAW,MAAM,OAAO,OAAO,SAAS,KAAK;AAEnD,aAAO;AAAA,QACL;AAAA,QACA,QAAQ,KAAK,UAAU,SAAS,MAAM;AAAA,QACtC,UAAU,SAAS;AAAA,QACnB,OAAO,SAAS,OAAO;AAAA,MAAA;AAAA,IAE3B,SAAS,OAAY;AACnB,UAAI,MAAM,WAAW,KAAK;AACxB,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,UACR,OAAO;AAAA,QAAA;AAAA,MAEX;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,OAAwC;AACxD,QAAI;AACF,YAAM,SAAS,KAAK;AAEpB,UAAI;AAEJ,UAAI,OAAO,OAAO,QAAQ,oBAAoB,YAAY;AAExD,cAAM,kBAAkB,MAAM,OAAO,OAAO,gBAAgB,KAAK;AACjE,cAAM,YAAY,MAAM,gBAAgB,KAAA;AACxC,cAAM,SAAS,MAAM,UAAU,YAAA;AAC/B,cAAM,SAAS,OAAO,KAAK,MAAM,EAAE,SAAS,QAAQ;AACpD,cAAM,WACJ,gBAAgB,QAAQ,IAAI,cAAc,KAAK;AACjD,eAAO;AAAA,UACL;AAAA,UACA,KAAK,QAAQ,QAAQ,WAAW,MAAM;AAAA,UACtC,WAAW;AAAA,QAAA;AAAA,MAEf,WAAW,OAAO,OAAO,QAAQ,YAAY,YAAY;AACvD,mBAAW,MAAM,OAAO,OAAO,QAAQ,KAAK;AAAA,MAC9C,WAAW,OAAO,OAAO,QAAQ,eAAe,YAAY;AAC1D,mBAAW,MAAM,OAAO,OAAO,WAAW,KAAK;AAAA,MACjD,WAAW,OAAO,OAAO,QAAQ,aAAa,YAAY;AACxD,mBAAW,MAAM,OAAO,OAAO,SAAS,KAAK;AAAA,MAC/C,OAAO;AAEL,cAAM,YAAY,MAAM,OAAO,OAAO,SAAS,KAAK;AACpD,YAAI,UAAU,KAAK;AACjB,iBAAO;AAAA,YACL;AAAA,YACA,KAAK,UAAU;AAAA,YACf,WAAW,UAAU,aACjB,IAAI,KAAK,UAAU,aAAa,GAAI,IACpC;AAAA,UAAA;AAAA,QAER;AAGA,cAAM,UAAU,KAAK,aAAa,WAAW;AAC7C,cAAM,SAAS,KAAK,aAAa;AAEjC,cAAM,kBAAkB,MAAM;AAAA,UAC5B,GAAG,OAAO,WAAW,KAAK;AAAA,UAC1B;AAAA,YACE,QAAQ;AAAA,YACR,SAAS;AAAA,cACP,eAAe,UAAU,MAAM;AAAA,YAAA;AAAA,UACjC;AAAA,QACF;AAGF,YAAI,CAAC,gBAAgB,IAAI;AACvB,gBAAM,cAAc,gBAAgB,QAAQ,IAAI,cAAc;AAC9D,cAAI,aAAa,SAAS,kBAAkB,GAAG;AAC7C,kBAAM,YAAY,MAAM,gBAAgB,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AAC/D,kBAAM,IAAI;AAAA,cACR,UAAU,OAAO,WACf,gCAAgC,gBAAgB,MAAM;AAAA,YAAA;AAAA,UAE5D;AACA,gBAAM,IAAI;AAAA,YACR,gCAAgC,gBAAgB,MAAM;AAAA,UAAA;AAAA,QAE1D;AAEA,cAAM,YAAY,MAAM,gBAAgB,KAAA;AACxC,cAAM,SAAS,MAAM,UAAU,YAAA;AAC/B,cAAM,SAAS,OAAO,KAAK,MAAM,EAAE,SAAS,QAAQ;AACpD,cAAM,WACJ,gBAAgB,QAAQ,IAAI,cAAc,KAAK;AAEjD,eAAO;AAAA,UACL;AAAA,UACA,KAAK,QAAQ,QAAQ,WAAW,MAAM;AAAA,UACtC,WAAW;AAAA,QAAA;AAAA,MAEf;AAEA,aAAO;AAAA,QACL;AAAA,QACA,KAAK,SAAS;AAAA,QACd,WAAW,SAAS,aAChB,IAAI,KAAK,SAAS,aAAa,GAAI,IACnC;AAAA,MAAA;AAAA,IAER,SAAS,OAAY;AACnB,UAAI,MAAM,WAAW,KAAK;AACxB,cAAM,IAAI,MAAM,wBAAwB,KAAK,EAAE;AAAA,MACjD;AACA,UAAI,MAAM,WAAW,KAAK;AACxB,cAAM,IAAI;AAAA,UACR,gEAAgE,KAAK;AAAA,QAAA;AAAA,MAEzE;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEU,aACR,SACqB;AACrB,UAAM,EAAE,OAAO,QAAQ,MAAM,UAAU,iBAAiB;AAExD,UAAM,UAA+B;AAAA,MACnC;AAAA,MACA;AAAA,IAAA;AAGF,QAAI,MAAM;AACR,cAAQ,MAAM,IAAI;AAAA,IACpB,WAAY,cAAsB,MAAM;AACtC,cAAQ,MAAM,IAAK,aAAqB;AAAA,IAC1C;AAEA,UAAM,UAAU,YAAa,cAAsB;AACnD,QAAI,YAAY,QAAW;AACzB,cAAQ,SAAS,IAAI,OAAO,OAAO;AAAA,IACrC;AAEA,WAAO;AAAA,EACT;AAAA,EAEU,kBAAkB,QAAgB,OAAsB;AAAA,EAElE;AAAA,EAEU,qBACR,QACA,UACM;AAAA,EAER;AAAA,EAEU,UACR,WACmD;AACnD,YAAQ,WAAA;AAAA,MACN,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IAAA;AAAA,EAEb;AACF;"}
|
|
1
|
+
{"version":3,"file":"video.js","sources":["../../../src/adapters/video.ts"],"sourcesContent":["import { BaseVideoAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { arrayBufferToBase64 } from '@tanstack/ai-utils'\nimport { createOpenAICompatibleClient } from '../utils/client'\nimport type {\n VideoGenerationOptions,\n VideoJobResult,\n VideoStatusResult,\n VideoUrlResult,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { OpenAICompatibleClientConfig } from '../types/config'\n\n/**\n * Threshold for emitting a \"this download will probably OOM serverless\n * runtimes\" warning. Anything larger than this (in bytes) gets surfaced via\n * console.warn — workers and small isolates routinely run out of memory once\n * a downloaded video is base64-encoded (the encoded form is ~33% larger and\n * resides in V8 heap rather than streaming through the runtime's network\n * layer).\n */\nconst LARGE_MEDIA_BUFFER_BYTES = 10 * 1024 * 1024\n\nfunction warnIfLargeMediaBuffer(\n byteLength: number,\n source: string,\n providerName: string,\n): void {\n if (byteLength <= LARGE_MEDIA_BUFFER_BYTES) return\n // No InternalLogger plumbed through to these download paths yet; surface\n // via console.warn so Workers / Lambda dashboards still capture it.\n console.warn(\n `[${providerName}.${source}] downloaded ${(byteLength / 1024 / 1024).toFixed(1)} MiB into memory before base64 encoding. ` +\n `Workers/serverless runtimes commonly run out of memory above ~10 MiB. ` +\n `Consider streaming the video through a CDN or your own storage layer instead.`,\n )\n}\n\n/**\n * OpenAI-Compatible Video Generation Adapter\n *\n * A generalized base class for providers that implement OpenAI-compatible video\n * generation APIs. Uses a job/polling architecture for async video generation.\n *\n * Providers can extend this class and only need to:\n * - Set `baseURL` in the config\n * - Lock the generic type parameters to provider-specific types\n * - Override validation or request building methods as needed\n *\n * All methods that validate inputs, build requests, or map responses are `protected`\n * so subclasses can override them.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport class OpenAICompatibleVideoAdapter<\n TModel extends string,\n TProviderOptions extends object = Record<string, any>,\n TModelProviderOptionsByName extends Record<string, any> = Record<string, any>,\n TModelSizeByName extends Record<string, string> = Record<string, string>,\n> extends BaseVideoAdapter<\n TModel,\n TProviderOptions,\n TModelProviderOptionsByName,\n TModelSizeByName\n> {\n readonly name: string\n\n protected client: OpenAI_SDK\n protected clientConfig: OpenAICompatibleClientConfig\n\n constructor(\n config: OpenAICompatibleClientConfig,\n model: TModel,\n name: string = 'openai-compatible',\n ) {\n super(config, model)\n this.name = name\n this.clientConfig = config\n this.client = createOpenAICompatibleClient(config)\n }\n\n /**\n * Create a new video generation job.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n async createVideoJob(\n options: VideoGenerationOptions<TProviderOptions>,\n ): Promise<VideoJobResult> {\n const { model, size, duration, modelOptions } = options\n\n // Validate inputs\n this.validateVideoSize(model, size)\n const seconds = duration ?? (modelOptions as any)?.seconds\n this.validateVideoSeconds(model, seconds)\n\n // Build request\n const request = this.buildRequest(options)\n\n try {\n options.logger.request(\n `activity=video.create provider=${this.name} model=${model} size=${request.size ?? 'default'} seconds=${request.seconds ?? 'default'}`,\n { provider: this.name, model },\n )\n // The video API on the OpenAI SDK is still experimental and shipped on\n // some SDK versions but not others; access through `videosClient` lets\n // subclasses override the entry point or supply a polyfill without\n // forcing every call site through `as any`.\n const videosClient = this.getVideosClient()\n const response = await videosClient.create(request)\n\n return {\n jobId: response.id,\n model,\n }\n } catch (error: any) {\n options.logger.errors(`${this.name}.createVideoJob fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),\n source: `${this.name}.createVideoJob`,\n })\n if (error?.message?.includes('videos') || error?.code === 'invalid_api') {\n throw new Error(\n `Video generation API is not available. The API may require special access. ` +\n `Original error: ${error.message}`,\n )\n }\n throw error\n }\n }\n\n /**\n * Returns the underlying OpenAI Videos resource. Pulled out as a protected\n * accessor so subclasses targeting forks of the SDK can swap the access\n * path without forcing each call site to cast through `any`.\n */\n protected getVideosClient(): {\n create: (req: Record<string, any>) => Promise<{ id: string }>\n retrieve: (id: string) => Promise<{\n id: string\n status: string\n progress?: number\n url?: string\n expires_at?: number\n error?: { message?: string }\n }>\n downloadContent?: (id: string) => Promise<Response>\n content?: (id: string) => Promise<unknown>\n getContent?: (id: string) => Promise<unknown>\n download?: (id: string) => Promise<unknown>\n } {\n return (this.client as unknown as { videos: any }).videos\n }\n\n /**\n * Get the current status of a video generation job.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n async getVideoStatus(jobId: string): Promise<VideoStatusResult> {\n try {\n const videosClient = this.getVideosClient()\n const response = await videosClient.retrieve(jobId)\n\n return {\n jobId,\n status: this.mapStatus(response.status),\n progress: response.progress,\n error: response.error?.message,\n }\n } catch (error: any) {\n if (error.status === 404) {\n return {\n jobId,\n status: 'failed',\n error: 'Job not found',\n }\n }\n throw error\n }\n }\n\n /**\n * Get the URL to download/view the generated video.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n async getVideoUrl(jobId: string): Promise<VideoUrlResult> {\n try {\n const videosClient = this.getVideosClient()\n\n // Prefer retrieve() because many openai-compatible backends (and the\n // aimock test harness) return the URL directly on the video resource\n // and do not implement a separate /content endpoint. Subclasses can\n // override this method if they need to download raw bytes via\n // downloadContent()/content().\n const videoInfo = await videosClient.retrieve(jobId)\n if (videoInfo.url) {\n return {\n jobId,\n url: videoInfo.url,\n expiresAt: videoInfo.expires_at\n ? new Date(videoInfo.expires_at)\n : undefined,\n }\n }\n\n // SDK download fall-through: try the various possible method names in\n // decreasing order of modernity.\n if (typeof videosClient.downloadContent === 'function') {\n const contentResponse = await videosClient.downloadContent(jobId)\n const videoBlob = await contentResponse.blob()\n const buffer = await videoBlob.arrayBuffer()\n warnIfLargeMediaBuffer(\n buffer.byteLength,\n 'video.downloadContent',\n this.name,\n )\n const base64 = arrayBufferToBase64(buffer)\n const mimeType =\n contentResponse.headers.get('content-type') || 'video/mp4'\n return {\n jobId,\n url: `data:${mimeType};base64,${base64}`,\n expiresAt: undefined,\n }\n }\n\n // The remaining SDK fall-throughs all return a binary payload\n // (Blob/Response/ArrayBuffer-shaped), NOT an `{ url, expires_at }`\n // object the way the bottom return assumed. Convert to a data URL\n // here so the caller actually receives a usable URL.\n let response: any\n if (typeof videosClient.content === 'function') {\n response = await videosClient.content(jobId)\n } else if (typeof videosClient.getContent === 'function') {\n response = await videosClient.getContent(jobId)\n } else if (typeof videosClient.download === 'function') {\n response = await videosClient.download(jobId)\n } else {\n // Last resort: raw fetch with auth header.\n const baseUrl = this.clientConfig.baseURL || 'https://api.openai.com/v1'\n const apiKey = this.clientConfig.apiKey\n\n const contentResponse = await fetch(\n `${baseUrl}/videos/${jobId}/content`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n },\n )\n\n if (!contentResponse.ok) {\n const contentType = contentResponse.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n const errorData = await contentResponse.json().catch(() => ({}))\n throw new Error(\n errorData.error?.message ||\n `Failed to get video content: ${contentResponse.status}`,\n )\n }\n throw new Error(\n `Failed to get video content: ${contentResponse.status}`,\n )\n }\n\n const videoBlob = await contentResponse.blob()\n const buffer = await videoBlob.arrayBuffer()\n warnIfLargeMediaBuffer(buffer.byteLength, 'video.fetch', this.name)\n const base64 = arrayBufferToBase64(buffer)\n const mimeType =\n contentResponse.headers.get('content-type') || 'video/mp4'\n\n return {\n jobId,\n url: `data:${mimeType};base64,${base64}`,\n expiresAt: undefined,\n }\n }\n\n // The fall-through SDK methods produce a Blob-ish or fetch-`Response`-ish\n // object. Read it as bytes and wrap in a data URL so callers see an\n // actual playable URL instead of the API endpoint URL (which is what\n // `response.url` would be on a fetch Response).\n const fallthroughBlob =\n typeof response?.blob === 'function'\n ? await response.blob()\n : response instanceof Blob\n ? response\n : null\n if (!fallthroughBlob) {\n throw new Error(\n `Video content download via SDK fall-through returned an unexpected shape (no blob()). ` +\n `Override getVideoUrl() in your subclass to handle this provider.`,\n )\n }\n const fallthroughBuffer = await fallthroughBlob.arrayBuffer()\n warnIfLargeMediaBuffer(\n fallthroughBuffer.byteLength,\n 'video.sdkFallthrough',\n this.name,\n )\n const fallthroughBase64 = arrayBufferToBase64(fallthroughBuffer)\n const fallthroughMime =\n (typeof response?.headers?.get === 'function'\n ? response.headers.get('content-type')\n : undefined) ||\n fallthroughBlob.type ||\n 'video/mp4'\n return {\n jobId,\n url: `data:${fallthroughMime};base64,${fallthroughBase64}`,\n expiresAt: undefined,\n }\n } catch (error: any) {\n if (error.status === 404) {\n throw new Error(`Video job not found: ${jobId}`)\n }\n if (error.status === 400) {\n throw new Error(\n `Video is not ready for download. Check status first. Job ID: ${jobId}`,\n )\n }\n throw error\n }\n }\n\n protected buildRequest(\n options: VideoGenerationOptions<TProviderOptions>,\n ): Record<string, any> {\n const { model, prompt, size, duration, modelOptions } = options\n\n const request: Record<string, any> = {\n model,\n prompt,\n }\n\n if (size) {\n request['size'] = size\n } else if ((modelOptions as any)?.size) {\n request['size'] = (modelOptions as any).size\n }\n\n const seconds = duration ?? (modelOptions as any)?.seconds\n if (seconds !== undefined) {\n request['seconds'] = String(seconds)\n }\n\n return request\n }\n\n protected validateVideoSize(_model: string, _size?: string): void {\n // Default: no size validation — subclasses can override\n }\n\n protected validateVideoSeconds(\n _model: string,\n _seconds?: number | string,\n ): void {\n // Default: no duration validation — subclasses can override\n }\n\n protected mapStatus(\n apiStatus: string,\n ): 'pending' | 'processing' | 'completed' | 'failed' {\n switch (apiStatus) {\n case 'queued':\n case 'pending':\n return 'pending'\n case 'processing':\n case 'in_progress':\n return 'processing'\n case 'completed':\n case 'succeeded':\n return 'completed'\n case 'failed':\n case 'error':\n case 'cancelled':\n return 'failed'\n default:\n return 'processing'\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,8 +2,6 @@ export { makeStructuredOutputCompatible } from './utils/schema-converter.js';
|
|
|
2
2
|
export { createOpenAICompatibleClient } from './utils/client.js';
|
|
3
3
|
export type { OpenAICompatibleClientConfig } from './types/config.js';
|
|
4
4
|
export * from './tools/index.js';
|
|
5
|
-
export * from './types/message-metadata.js';
|
|
6
|
-
export * from './types/provider-options.js';
|
|
7
5
|
export { OpenAICompatibleChatCompletionsTextAdapter } from './adapters/chat-completions-text.js';
|
|
8
6
|
export { convertFunctionToolToChatCompletionsFormat, convertToolsToChatCompletionsFormat, type ChatCompletionFunctionTool, } from './adapters/chat-completions-tool-converter.js';
|
|
9
7
|
export { OpenAICompatibleResponsesTextAdapter } from './adapters/responses-text.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import { applyPatchTool, convertApplyPatchToolToAdapterFormat } from "./tools/ap
|
|
|
4
4
|
import { codeInterpreterTool, convertCodeInterpreterToolToAdapterFormat } from "./tools/code-interpreter-tool.js";
|
|
5
5
|
import { computerUseTool, convertComputerUseToolToAdapterFormat } from "./tools/computer-use-tool.js";
|
|
6
6
|
import { convertCustomToolToAdapterFormat, customTool } from "./tools/custom-tool.js";
|
|
7
|
-
import { convertFileSearchToolToAdapterFormat, fileSearchTool } from "./tools/file-search-tool.js";
|
|
7
|
+
import { convertFileSearchToolToAdapterFormat, fileSearchTool, validateMaxNumResults } from "./tools/file-search-tool.js";
|
|
8
8
|
import { convertFunctionToolToAdapterFormat } from "./tools/function-tool.js";
|
|
9
|
-
import { convertImageGenerationToolToAdapterFormat, imageGenerationTool } from "./tools/image-generation-tool.js";
|
|
9
|
+
import { convertImageGenerationToolToAdapterFormat, imageGenerationTool, validatePartialImages } from "./tools/image-generation-tool.js";
|
|
10
10
|
import { convertLocalShellToolToAdapterFormat, localShellTool } from "./tools/local-shell-tool.js";
|
|
11
11
|
import { convertMCPToolToAdapterFormat, mcpTool, validateMCPtool } from "./tools/mcp-tool.js";
|
|
12
12
|
import { convertShellToolToAdapterFormat, shellTool } from "./tools/shell-tool.js";
|
|
@@ -59,6 +59,8 @@ export {
|
|
|
59
59
|
mcpTool,
|
|
60
60
|
shellTool,
|
|
61
61
|
validateMCPtool,
|
|
62
|
+
validateMaxNumResults,
|
|
63
|
+
validatePartialImages,
|
|
62
64
|
webSearchPreviewTool,
|
|
63
65
|
webSearchTool
|
|
64
66
|
};
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { default as OpenAI } from 'openai';
|
|
2
2
|
import { Tool } from '@tanstack/ai';
|
|
3
|
-
export type
|
|
3
|
+
export type ApplyPatchToolConfig = OpenAI.Responses.ApplyPatchTool;
|
|
4
|
+
/** @deprecated Renamed to `ApplyPatchToolConfig`. Will be removed in a future release. */
|
|
5
|
+
export type ApplyPatchTool = ApplyPatchToolConfig;
|
|
4
6
|
/**
|
|
5
7
|
* Converts a standard Tool to OpenAI ApplyPatchTool format
|
|
6
8
|
*/
|
|
7
|
-
export declare function convertApplyPatchToolToAdapterFormat(_tool: Tool):
|
|
9
|
+
export declare function convertApplyPatchToolToAdapterFormat(_tool: Tool): ApplyPatchToolConfig;
|
|
8
10
|
/**
|
|
9
|
-
* Creates a standard Tool from ApplyPatchTool parameters
|
|
11
|
+
* Creates a standard Tool from ApplyPatchTool parameters.
|
|
12
|
+
*
|
|
13
|
+
* Base (non-branded) factory. Providers that need branded return types should
|
|
14
|
+
* re-wrap this in their own package.
|
|
10
15
|
*/
|
|
11
16
|
export declare function applyPatchTool(): Tool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-patch-tool.js","sources":["../../../src/tools/apply-patch-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nexport type
|
|
1
|
+
{"version":3,"file":"apply-patch-tool.js","sources":["../../../src/tools/apply-patch-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nexport type ApplyPatchToolConfig = OpenAI.Responses.ApplyPatchTool\n\n/** @deprecated Renamed to `ApplyPatchToolConfig`. Will be removed in a future release. */\nexport type ApplyPatchTool = ApplyPatchToolConfig\n\n/**\n * Converts a standard Tool to OpenAI ApplyPatchTool format\n */\nexport function convertApplyPatchToolToAdapterFormat(\n _tool: Tool,\n): ApplyPatchToolConfig {\n return {\n type: 'apply_patch',\n }\n}\n\n/**\n * Creates a standard Tool from ApplyPatchTool 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 applyPatchTool(): Tool {\n return {\n name: 'apply_patch',\n description: 'Apply a patch to modify files',\n metadata: {},\n }\n}\n"],"names":[],"mappings":"AAWO,SAAS,qCACd,OACsB;AACtB,SAAO;AAAA,IACL,MAAM;AAAA,EAAA;AAEV;AAQO,SAAS,iBAAuB;AACrC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU,CAAA;AAAA,EAAC;AAEf;"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { Tool } from '@tanstack/ai';
|
|
2
2
|
import { default as OpenAI } from 'openai';
|
|
3
|
-
export type
|
|
3
|
+
export type CodeInterpreterToolConfig = OpenAI.Responses.Tool.CodeInterpreter;
|
|
4
|
+
/** @deprecated Renamed to `CodeInterpreterToolConfig`. Will be removed in a future release. */
|
|
5
|
+
export type CodeInterpreterTool = CodeInterpreterToolConfig;
|
|
4
6
|
/**
|
|
5
7
|
* Converts a standard Tool to OpenAI CodeInterpreterTool format
|
|
6
8
|
*/
|
|
7
|
-
export declare function convertCodeInterpreterToolToAdapterFormat(tool: Tool):
|
|
9
|
+
export declare function convertCodeInterpreterToolToAdapterFormat(tool: Tool): CodeInterpreterToolConfig;
|
|
8
10
|
/**
|
|
9
|
-
* Creates a standard Tool from CodeInterpreterTool parameters
|
|
11
|
+
* Creates a standard Tool from CodeInterpreterTool parameters.
|
|
12
|
+
*
|
|
13
|
+
* Base (non-branded) factory. Providers that need branded return types should
|
|
14
|
+
* re-wrap this in their own package.
|
|
10
15
|
*/
|
|
11
|
-
export declare function codeInterpreterTool(container:
|
|
16
|
+
export declare function codeInterpreterTool(container: CodeInterpreterToolConfig): Tool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-interpreter-tool.js","sources":["../../../src/tools/code-interpreter-tool.ts"],"sourcesContent":["import type { Tool } from '@tanstack/ai'\nimport type OpenAI from 'openai'\n\nexport type
|
|
1
|
+
{"version":3,"file":"code-interpreter-tool.js","sources":["../../../src/tools/code-interpreter-tool.ts"],"sourcesContent":["import type { Tool } from '@tanstack/ai'\nimport type OpenAI from 'openai'\n\nexport type CodeInterpreterToolConfig = OpenAI.Responses.Tool.CodeInterpreter\n\n/** @deprecated Renamed to `CodeInterpreterToolConfig`. Will be removed in a future release. */\nexport type CodeInterpreterTool = CodeInterpreterToolConfig\n\n/**\n * Converts a standard Tool to OpenAI CodeInterpreterTool format\n */\nexport function convertCodeInterpreterToolToAdapterFormat(\n tool: Tool,\n): CodeInterpreterToolConfig {\n const metadata = tool.metadata as CodeInterpreterToolConfig\n return {\n type: 'code_interpreter',\n container: metadata.container,\n }\n}\n\n/**\n * Creates a standard Tool from CodeInterpreterTool 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 codeInterpreterTool(\n container: CodeInterpreterToolConfig,\n): Tool {\n return {\n name: 'code_interpreter',\n description: 'Execute code in a sandboxed environment',\n metadata: {\n type: 'code_interpreter',\n container,\n },\n }\n}\n"],"names":[],"mappings":"AAWO,SAAS,0CACd,MAC2B;AAC3B,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW,SAAS;AAAA,EAAA;AAExB;AAQO,SAAS,oBACd,WACM;AACN,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,MACR,MAAM;AAAA,MACN;AAAA,IAAA;AAAA,EACF;AAEJ;"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { default as OpenAI } from 'openai';
|
|
2
2
|
import { Tool } from '@tanstack/ai';
|
|
3
|
-
export type
|
|
3
|
+
export type ComputerUseToolConfig = OpenAI.Responses.ComputerTool;
|
|
4
|
+
/** @deprecated Renamed to `ComputerUseToolConfig`. Will be removed in a future release. */
|
|
5
|
+
export type ComputerUseTool = ComputerUseToolConfig;
|
|
4
6
|
/**
|
|
5
7
|
* Converts a standard Tool to OpenAI ComputerUseTool format
|
|
6
8
|
*/
|
|
7
|
-
export declare function convertComputerUseToolToAdapterFormat(tool: Tool):
|
|
9
|
+
export declare function convertComputerUseToolToAdapterFormat(tool: Tool): ComputerUseToolConfig;
|
|
8
10
|
/**
|
|
9
|
-
* Creates a standard Tool from ComputerUseTool parameters
|
|
11
|
+
* Creates a standard Tool from ComputerUseTool parameters.
|
|
12
|
+
*
|
|
13
|
+
* Base (non-branded) factory. Providers that need branded return types should
|
|
14
|
+
* re-wrap this in their own package.
|
|
10
15
|
*/
|
|
11
|
-
export declare function computerUseTool(toolData:
|
|
16
|
+
export declare function computerUseTool(toolData: ComputerUseToolConfig): Tool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computer-use-tool.js","sources":["../../../src/tools/computer-use-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nexport type
|
|
1
|
+
{"version":3,"file":"computer-use-tool.js","sources":["../../../src/tools/computer-use-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nexport type ComputerUseToolConfig = OpenAI.Responses.ComputerTool\n\n/** @deprecated Renamed to `ComputerUseToolConfig`. Will be removed in a future release. */\nexport type ComputerUseTool = ComputerUseToolConfig\n\n/**\n * Converts a standard Tool to OpenAI ComputerUseTool format\n */\nexport function convertComputerUseToolToAdapterFormat(\n tool: Tool,\n): ComputerUseToolConfig {\n const metadata = tool.metadata as ComputerUseToolConfig\n return {\n type: 'computer_use_preview',\n display_height: metadata.display_height,\n display_width: metadata.display_width,\n environment: metadata.environment,\n }\n}\n\n/**\n * Creates a standard Tool from ComputerUseTool 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 computerUseTool(toolData: ComputerUseToolConfig): Tool {\n return {\n name: 'computer_use_preview',\n description: 'Control a virtual computer',\n metadata: {\n ...toolData,\n },\n }\n}\n"],"names":[],"mappings":"AAWO,SAAS,sCACd,MACuB;AACvB,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,gBAAgB,SAAS;AAAA,IACzB,eAAe,SAAS;AAAA,IACxB,aAAa,SAAS;AAAA,EAAA;AAE1B;AAQO,SAAS,gBAAgB,UAAuC;AACrE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,MACR,GAAG;AAAA,IAAA;AAAA,EACL;AAEJ;"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { default as OpenAI } from 'openai';
|
|
2
2
|
import { Tool } from '@tanstack/ai';
|
|
3
|
-
export type
|
|
3
|
+
export type CustomToolConfig = OpenAI.Responses.CustomTool;
|
|
4
|
+
/** @deprecated Renamed to `CustomToolConfig`. Will be removed in a future release. */
|
|
5
|
+
export type CustomTool = CustomToolConfig;
|
|
4
6
|
/**
|
|
5
7
|
* Converts a standard Tool to OpenAI CustomTool format
|
|
6
8
|
*/
|
|
7
|
-
export declare function convertCustomToolToAdapterFormat(tool: Tool):
|
|
9
|
+
export declare function convertCustomToolToAdapterFormat(tool: Tool): CustomToolConfig;
|
|
8
10
|
/**
|
|
9
|
-
* Creates a standard Tool from CustomTool parameters
|
|
11
|
+
* Creates a standard Tool from CustomTool parameters.
|
|
10
12
|
*/
|
|
11
|
-
export declare function customTool(toolData:
|
|
13
|
+
export declare function customTool(toolData: CustomToolConfig): Tool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-tool.js","sources":["../../../src/tools/custom-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nexport type
|
|
1
|
+
{"version":3,"file":"custom-tool.js","sources":["../../../src/tools/custom-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nexport type CustomToolConfig = OpenAI.Responses.CustomTool\n\n/** @deprecated Renamed to `CustomToolConfig`. Will be removed in a future release. */\nexport type CustomTool = CustomToolConfig\n\n/**\n * Converts a standard Tool to OpenAI CustomTool format\n */\nexport function convertCustomToolToAdapterFormat(tool: Tool): CustomToolConfig {\n const metadata = tool.metadata as CustomToolConfig\n return {\n type: 'custom',\n name: metadata.name,\n description: metadata.description,\n format: metadata.format,\n }\n}\n\n/**\n * Creates a standard Tool from CustomTool parameters.\n */\nexport function customTool(toolData: CustomToolConfig): Tool {\n return {\n name: 'custom',\n description: toolData.description || 'A custom tool',\n metadata: {\n ...toolData,\n },\n }\n}\n"],"names":[],"mappings":"AAWO,SAAS,iCAAiC,MAA8B;AAC7E,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,SAAS;AAAA,IACf,aAAa,SAAS;AAAA,IACtB,QAAQ,SAAS;AAAA,EAAA;AAErB;AAKO,SAAS,WAAW,UAAkC;AAC3D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa,SAAS,eAAe;AAAA,IACrC,UAAU;AAAA,MACR,GAAG;AAAA,IAAA;AAAA,EACL;AAEJ;"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { default as OpenAI } from 'openai';
|
|
2
2
|
import { Tool } from '@tanstack/ai';
|
|
3
|
-
|
|
3
|
+
declare const validateMaxNumResults: (maxNumResults: number | undefined) => void;
|
|
4
|
+
export type FileSearchToolConfig = OpenAI.Responses.FileSearchTool;
|
|
5
|
+
/** @deprecated Renamed to `FileSearchToolConfig`. Will be removed in a future release. */
|
|
6
|
+
export type FileSearchTool = FileSearchToolConfig;
|
|
4
7
|
/**
|
|
5
8
|
* Converts a standard Tool to OpenAI FileSearchTool format
|
|
6
9
|
*/
|
|
7
|
-
export declare function convertFileSearchToolToAdapterFormat(tool: Tool):
|
|
10
|
+
export declare function convertFileSearchToolToAdapterFormat(tool: Tool): FileSearchToolConfig;
|
|
8
11
|
/**
|
|
9
|
-
* Creates a standard Tool from FileSearchTool parameters
|
|
12
|
+
* Creates a standard Tool from FileSearchTool parameters.
|
|
13
|
+
*
|
|
14
|
+
* Validates max_num_results. Base (non-branded) factory; providers that need
|
|
15
|
+
* branded return types should re-wrap in their own package.
|
|
10
16
|
*/
|
|
11
|
-
export declare function fileSearchTool(toolData:
|
|
17
|
+
export declare function fileSearchTool(toolData: FileSearchToolConfig): Tool;
|
|
18
|
+
export { validateMaxNumResults };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const validateMaxNumResults = (maxNumResults) => {
|
|
2
|
-
if (maxNumResults && (maxNumResults < 1 || maxNumResults > 50)) {
|
|
2
|
+
if (maxNumResults !== void 0 && (maxNumResults < 1 || maxNumResults > 50)) {
|
|
3
3
|
throw new Error("max_num_results must be between 1 and 50.");
|
|
4
4
|
}
|
|
5
5
|
};
|
|
@@ -25,6 +25,7 @@ function fileSearchTool(toolData) {
|
|
|
25
25
|
}
|
|
26
26
|
export {
|
|
27
27
|
convertFileSearchToolToAdapterFormat,
|
|
28
|
-
fileSearchTool
|
|
28
|
+
fileSearchTool,
|
|
29
|
+
validateMaxNumResults
|
|
29
30
|
};
|
|
30
31
|
//# sourceMappingURL=file-search-tool.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-search-tool.js","sources":["../../../src/tools/file-search-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nconst validateMaxNumResults = (maxNumResults: number | undefined) => {\n if (maxNumResults
|
|
1
|
+
{"version":3,"file":"file-search-tool.js","sources":["../../../src/tools/file-search-tool.ts"],"sourcesContent":["import type OpenAI from 'openai'\nimport type { Tool } from '@tanstack/ai'\n\nconst validateMaxNumResults = (maxNumResults: number | undefined) => {\n if (\n maxNumResults !== undefined &&\n (maxNumResults < 1 || maxNumResults > 50)\n ) {\n throw new Error('max_num_results must be between 1 and 50.')\n }\n}\n\nexport type FileSearchToolConfig = OpenAI.Responses.FileSearchTool\n\n/** @deprecated Renamed to `FileSearchToolConfig`. Will be removed in a future release. */\nexport type FileSearchTool = FileSearchToolConfig\n\n/**\n * Converts a standard Tool to OpenAI FileSearchTool format\n */\nexport function convertFileSearchToolToAdapterFormat(\n tool: Tool,\n): FileSearchToolConfig {\n const metadata = tool.metadata as FileSearchToolConfig\n return {\n type: 'file_search',\n vector_store_ids: metadata.vector_store_ids,\n max_num_results: metadata.max_num_results,\n ranking_options: metadata.ranking_options,\n filters: metadata.filters,\n }\n}\n\n/**\n * Creates a standard Tool from FileSearchTool parameters.\n *\n * Validates max_num_results. Base (non-branded) factory; providers that need\n * branded return types should re-wrap in their own package.\n */\nexport function fileSearchTool(toolData: FileSearchToolConfig): Tool {\n validateMaxNumResults(toolData.max_num_results)\n return {\n name: 'file_search',\n description: 'Search files in vector stores',\n metadata: {\n ...toolData,\n },\n }\n}\n\nexport { validateMaxNumResults }\n"],"names":[],"mappings":"AAGA,MAAM,wBAAwB,CAAC,kBAAsC;AACnE,MACE,kBAAkB,WACjB,gBAAgB,KAAK,gBAAgB,KACtC;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAUO,SAAS,qCACd,MACsB;AACtB,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,kBAAkB,SAAS;AAAA,IAC3B,iBAAiB,SAAS;AAAA,IAC1B,iBAAiB,SAAS;AAAA,IAC1B,SAAS,SAAS;AAAA,EAAA;AAEtB;AAQO,SAAS,eAAe,UAAsC;AACnE,wBAAsB,SAAS,eAAe;AAC9C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,MACR,GAAG;AAAA,IAAA;AAAA,EACL;AAEJ;"}
|