@tanstack/openai-base 0.2.1 → 0.3.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/README.md +121 -0
- package/dist/esm/adapters/chat-completions-text.d.ts +49 -21
- package/dist/esm/adapters/chat-completions-text.js +480 -68
- package/dist/esm/adapters/chat-completions-text.js.map +1 -1
- package/dist/esm/adapters/chat-completions-tool-converter.d.ts +8 -4
- package/dist/esm/adapters/chat-completions-tool-converter.js.map +1 -1
- package/dist/esm/adapters/responses-text.d.ts +46 -33
- package/dist/esm/adapters/responses-text.js +661 -142
- package/dist/esm/adapters/responses-text.js.map +1 -1
- package/dist/esm/index.d.ts +2 -9
- package/dist/esm/index.js +4 -16
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tools/apply-patch-tool.d.ts +2 -2
- package/dist/esm/tools/apply-patch-tool.js.map +1 -1
- package/dist/esm/tools/code-interpreter-tool.d.ts +3 -2
- package/dist/esm/tools/code-interpreter-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.d.ts +2 -2
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/custom-tool.d.ts +2 -2
- package/dist/esm/tools/custom-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.d.ts +2 -2
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-tool.d.ts +2 -2
- package/dist/esm/tools/function-tool.js.map +1 -1
- package/dist/esm/tools/image-generation-tool.d.ts +3 -2
- package/dist/esm/tools/image-generation-tool.js.map +1 -1
- package/dist/esm/tools/local-shell-tool.d.ts +3 -2
- package/dist/esm/tools/local-shell-tool.js.map +1 -1
- package/dist/esm/tools/mcp-tool.d.ts +3 -2
- package/dist/esm/tools/mcp-tool.js.map +1 -1
- package/dist/esm/tools/shell-tool.d.ts +2 -2
- package/dist/esm/tools/shell-tool.js.map +1 -1
- package/dist/esm/tools/web-search-preview-tool.d.ts +2 -2
- package/dist/esm/tools/web-search-preview-tool.js.map +1 -1
- package/dist/esm/tools/web-search-tool.d.ts +2 -2
- package/dist/esm/tools/web-search-tool.js.map +1 -1
- package/package.json +6 -6
- package/src/adapters/chat-completions-text.ts +605 -117
- package/src/adapters/chat-completions-tool-converter.ts +9 -5
- package/src/adapters/responses-text.ts +869 -210
- package/src/index.ts +2 -12
- package/src/tools/apply-patch-tool.ts +2 -2
- package/src/tools/code-interpreter-tool.ts +4 -2
- package/src/tools/computer-use-tool.ts +2 -2
- package/src/tools/custom-tool.ts +2 -2
- package/src/tools/file-search-tool.ts +3 -3
- package/src/tools/function-tool.ts +2 -2
- package/src/tools/image-generation-tool.ts +4 -2
- package/src/tools/local-shell-tool.ts +4 -2
- package/src/tools/mcp-tool.ts +4 -2
- package/src/tools/shell-tool.ts +2 -2
- package/src/tools/web-search-preview-tool.ts +2 -2
- package/src/tools/web-search-tool.ts +2 -2
- package/dist/esm/adapters/image.d.ts +0 -32
- package/dist/esm/adapters/image.js +0 -89
- package/dist/esm/adapters/image.js.map +0 -1
- package/dist/esm/adapters/summarize.d.ts +0 -28
- package/dist/esm/adapters/summarize.js +0 -112
- package/dist/esm/adapters/summarize.js.map +0 -1
- package/dist/esm/adapters/transcription.d.ts +0 -34
- package/dist/esm/adapters/transcription.js +0 -131
- package/dist/esm/adapters/transcription.js.map +0 -1
- package/dist/esm/adapters/tts.d.ts +0 -26
- package/dist/esm/adapters/tts.js +0 -78
- package/dist/esm/adapters/tts.js.map +0 -1
- package/dist/esm/adapters/video.d.ts +0 -72
- package/dist/esm/adapters/video.js +0 -238
- package/dist/esm/adapters/video.js.map +0 -1
- package/dist/esm/types/config.d.ts +0 -4
- package/dist/esm/utils/client.d.ts +0 -3
- package/dist/esm/utils/client.js +0 -8
- package/dist/esm/utils/client.js.map +0 -1
- package/src/adapters/image.ts +0 -158
- package/src/adapters/summarize.ts +0 -174
- package/src/adapters/transcription.ts +0 -194
- package/src/adapters/tts.ts +0 -124
- package/src/adapters/video.ts +0 -385
- package/src/types/config.ts +0 -5
- package/src/utils/client.ts +0 -8
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# @tanstack/openai-base
|
|
2
|
+
|
|
3
|
+
Shared base adapters for providers that drive the official `openai` SDK
|
|
4
|
+
against a different `baseURL`.
|
|
5
|
+
|
|
6
|
+
## TL;DR
|
|
7
|
+
|
|
8
|
+
Several providers ship endpoints that the official `openai` Node SDK can
|
|
9
|
+
talk to verbatim — you just point it at a different `baseURL`. xAI's Grok
|
|
10
|
+
endpoint, Groq's `/openai/v1` endpoint, and OpenAI itself all fall in this
|
|
11
|
+
camp. This package contains the shared logic for both wire formats those
|
|
12
|
+
endpoints expose:
|
|
13
|
+
|
|
14
|
+
- `OpenAIBaseChatCompletionsTextAdapter` — for `/v1/chat/completions`
|
|
15
|
+
- `OpenAIBaseResponsesTextAdapter` — for `/v1/responses`
|
|
16
|
+
|
|
17
|
+
Provider packages (`@tanstack/ai-openai`, `@tanstack/ai-groq`,
|
|
18
|
+
`@tanstack/ai-grok`) construct an `OpenAI` client with their own `baseURL`,
|
|
19
|
+
pass it to the relevant base adapter, and override a small set of
|
|
20
|
+
protected hooks for SDK-shape variance and provider-specific quirks.
|
|
21
|
+
|
|
22
|
+
## Why this package exists
|
|
23
|
+
|
|
24
|
+
Every text adapter in TanStack AI — regardless of provider — emits
|
|
25
|
+
[AG-UI](https://github.com/CopilotKit/ag-ui) events (`RUN_STARTED`,
|
|
26
|
+
`TEXT_MESSAGE_*`, `TOOL_CALL_*`, `RUN_FINISHED`, …) as its output stream.
|
|
27
|
+
That is the universal unification.
|
|
28
|
+
|
|
29
|
+
Input protocols differ. The OpenAI Chat Completions and Responses wire
|
|
30
|
+
formats both have multiple implementers in the ecosystem, so it pays to
|
|
31
|
+
write the streaming-chunk assembly, partial-JSON tool-arg buffering,
|
|
32
|
+
tool-call deduplication, and structured-output coercion once and share
|
|
33
|
+
it. That shared code lives here.
|
|
34
|
+
|
|
35
|
+
Providers whose native API doesn't match either OpenAI wire format
|
|
36
|
+
(Anthropic, Gemini, Ollama's native API, OpenRouter's own SDK) extend
|
|
37
|
+
`BaseTextAdapter` from `@tanstack/ai` directly — there's nothing to
|
|
38
|
+
share, so they don't pay the indirection cost.
|
|
39
|
+
|
|
40
|
+
## What goes here vs. in `@tanstack/ai-openai`
|
|
41
|
+
|
|
42
|
+
| Belongs in `@tanstack/openai-base` | Belongs in `@tanstack/ai-openai` |
|
|
43
|
+
| --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
44
|
+
| Logic for the Chat Completions wire format | OpenAI-specific tool types (`web_search_preview`, `code_interpreter`, `local_shell`, `apply_patch`, `computer_use`, `mcp`, …) |
|
|
45
|
+
| Logic for the Responses wire format | OpenAI model metadata, model lists, capability matrices |
|
|
46
|
+
| Streaming chunk assembly, AG-UI lifecycle, partial-JSON tool-arg buffering, tool-call deduplication | OpenAI-only request/response fields that no other consumer of the base sets |
|
|
47
|
+
| Schema converters and structured-output coercion that all consumers accept | OpenAI's media adapters (image/TTS/video/transcription) that Grok/Groq don't implement |
|
|
48
|
+
|
|
49
|
+
**Rule of thumb**: if a field would be useful to at least two of the
|
|
50
|
+
consuming packages (`ai-openai`, `ai-grok`, `ai-groq`), it belongs here.
|
|
51
|
+
Otherwise it belongs in the provider's own package, plumbed in via a
|
|
52
|
+
subclass override or a hook.
|
|
53
|
+
|
|
54
|
+
## How providers extend the bases
|
|
55
|
+
|
|
56
|
+
The base constructor takes a pre-built `OpenAI` client. Subclasses
|
|
57
|
+
construct the SDK with their own `baseURL` (and any other client
|
|
58
|
+
options) and pass it to `super`:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
class GrokTextAdapter extends OpenAIBaseChatCompletionsTextAdapter<…> {
|
|
62
|
+
constructor(config: GrokConfig, model: TModel) {
|
|
63
|
+
super(model, 'grok', new OpenAI(withGrokDefaults(config)))
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Per-provider quirks are handled via protected hooks:
|
|
69
|
+
|
|
70
|
+
- `convertMessage`, `mapOptionsToRequest` — bridge request-shape
|
|
71
|
+
differences (extra fields, omitted fields, alternative encodings).
|
|
72
|
+
- `extractReasoning` — surface a provider's reasoning channel into the
|
|
73
|
+
shared `REASONING_*` AG-UI lifecycle.
|
|
74
|
+
- `transformStructuredOutput`, `makeStructuredOutputCompatible` —
|
|
75
|
+
adjust structured-output handling for provider quirks (e.g. Groq's
|
|
76
|
+
schema-shape requirements).
|
|
77
|
+
- `processStreamChunks` — wrap the shared chunk processor for last-mile
|
|
78
|
+
fixups (e.g. Groq's `x_groq.usage` → `chunk.usage` promotion).
|
|
79
|
+
- `extractTextFromResponse` — pull the assistant text out of the
|
|
80
|
+
provider's non-streaming response shape.
|
|
81
|
+
|
|
82
|
+
Each provider typically overrides 2–6 hooks and inherits everything else.
|
|
83
|
+
|
|
84
|
+
## Architecture
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
@tanstack/ai
|
|
88
|
+
└── BaseTextAdapter (abstract — emits AG-UI events)
|
|
89
|
+
│
|
|
90
|
+
├── @tanstack/openai-base::OpenAIBaseChatCompletionsTextAdapter
|
|
91
|
+
│ ├── ai-groq
|
|
92
|
+
│ └── ai-grok
|
|
93
|
+
│
|
|
94
|
+
├── @tanstack/openai-base::OpenAIBaseResponsesTextAdapter
|
|
95
|
+
│ └── ai-openai (Responses is OpenAI's preferred API)
|
|
96
|
+
│
|
|
97
|
+
├── ai-anthropic::AnthropicTextAdapter extends BaseTextAdapter directly
|
|
98
|
+
├── ai-gemini::GeminiTextAdapter extends BaseTextAdapter directly
|
|
99
|
+
├── ai-ollama::OllamaTextAdapter extends BaseTextAdapter directly
|
|
100
|
+
└── ai-openrouter (text + responses) extends BaseTextAdapter directly
|
|
101
|
+
(uses @openrouter/sdk natively)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Note: `ai-openai` ships only the Responses-based text adapter. For pure
|
|
105
|
+
Chat Completions use cases without OpenAI-specific behaviour, use
|
|
106
|
+
`ai-grok` or `ai-groq`, or build a new provider package extending
|
|
107
|
+
`OpenAIBaseChatCompletionsTextAdapter`.
|
|
108
|
+
|
|
109
|
+
## Direct use
|
|
110
|
+
|
|
111
|
+
Most users don't import from this package directly; they install a
|
|
112
|
+
provider package and the adapter from there does the work.
|
|
113
|
+
|
|
114
|
+
If you're building an adapter for a new endpoint that the official
|
|
115
|
+
`openai` SDK can talk to verbatim (vLLM, Together, Fireworks, a
|
|
116
|
+
self-hosted gateway, …), import the abstract adapters from this package
|
|
117
|
+
and subclass them. The existing providers are worked examples —
|
|
118
|
+
`@tanstack/ai-grok` is the simplest (xAI's API is a near-direct OpenAI
|
|
119
|
+
Chat Completions clone), `@tanstack/ai-groq` shows the
|
|
120
|
+
`processStreamChunks` and `makeStructuredOutputCompatible` override
|
|
121
|
+
pattern.
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
import { BaseTextAdapter, StructuredOutputOptions, StructuredOutputResult } from '@tanstack/ai/adapters';
|
|
2
|
-
import { default as
|
|
2
|
+
import { default as OpenAI } from 'openai';
|
|
3
|
+
import { ChatCompletionChunk, ChatCompletionContentPart, ChatCompletionCreateParamsStreaming, ChatCompletionMessageParam } from 'openai/resources/chat/completions/completions';
|
|
3
4
|
import { ContentPart, DefaultMessageMetadataByModality, Modality, ModelMessage, StreamChunk, TextOptions } from '@tanstack/ai';
|
|
4
|
-
import { OpenAICompatibleClientConfig } from '../types/config.js';
|
|
5
5
|
/**
|
|
6
|
-
* OpenAI
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* extend this class and only need to:
|
|
11
|
-
* - Set `baseURL` in the config
|
|
12
|
-
* - Lock the generic type parameters to provider-specific types
|
|
13
|
-
* - Override specific methods for quirks
|
|
14
|
-
*
|
|
15
|
-
* All methods that build requests or process responses are `protected` so subclasses
|
|
16
|
-
* can override them.
|
|
6
|
+
* Shared implementation of the OpenAI Chat Completions API. Holds the
|
|
7
|
+
* stream-accumulator + AG-UI lifecycle logic and calls the OpenAI SDK
|
|
8
|
+
* directly. Subclasses (ai-openai, ai-grok, ai-groq) construct an OpenAI
|
|
9
|
+
* client with their provider-specific `baseURL` / headers and pass it in.
|
|
17
10
|
*/
|
|
18
|
-
export declare class
|
|
11
|
+
export declare abstract class OpenAIBaseChatCompletionsTextAdapter<TModel extends string, TProviderOptions extends Record<string, unknown> = Record<string, unknown>, TInputModalities extends ReadonlyArray<Modality> = ReadonlyArray<Modality>, TMessageMetadata extends DefaultMessageMetadataByModality = DefaultMessageMetadataByModality, TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>> extends BaseTextAdapter<TModel, TProviderOptions, TInputModalities, TMessageMetadata, TToolCapabilities> {
|
|
19
12
|
readonly kind: "text";
|
|
20
13
|
readonly name: string;
|
|
21
|
-
protected client:
|
|
22
|
-
constructor(
|
|
14
|
+
protected client: OpenAI;
|
|
15
|
+
constructor(model: TModel, name: string, client: OpenAI);
|
|
23
16
|
chatStream(options: TextOptions<TProviderOptions>): AsyncIterable<StreamChunk>;
|
|
24
17
|
/**
|
|
25
18
|
* Generate structured output using the provider's JSON Schema response format.
|
|
@@ -34,36 +27,71 @@ export declare class OpenAICompatibleChatCompletionsTextAdapter<TModel extends s
|
|
|
34
27
|
* We apply provider-specific transformations for structured output compatibility.
|
|
35
28
|
*/
|
|
36
29
|
structuredOutput(options: StructuredOutputOptions<TProviderOptions>): Promise<StructuredOutputResult<unknown>>;
|
|
30
|
+
/**
|
|
31
|
+
* Stream structured output. Single Chat Completions request with
|
|
32
|
+
* `response_format: json_schema` + `stream: true`. Emits the standard
|
|
33
|
+
* AG-UI lifecycle (`RUN_STARTED` → `REASONING_*?` → `TEXT_MESSAGE_*`
|
|
34
|
+
* carrying raw JSON deltas → terminal `CUSTOM 'structured-output.complete'`
|
|
35
|
+
* → `RUN_FINISHED`). Subclasses use the same SDK-call / reasoning /
|
|
36
|
+
* structured-output-transform hooks as `chatStream` / `structuredOutput` —
|
|
37
|
+
* no per-subclass override should be needed.
|
|
38
|
+
*/
|
|
39
|
+
structuredOutputStream(options: StructuredOutputOptions<TProviderOptions>): AsyncIterable<StreamChunk>;
|
|
40
|
+
/**
|
|
41
|
+
* Cross-SDK abort detection for `structuredOutputStream`. Default duck-types
|
|
42
|
+
* on `name === 'APIUserAbortError'` (OpenAI SDK), `code === 'ERR_CANCELED'`,
|
|
43
|
+
* and standard `AbortError`s. Subclasses with proprietary error types (e.g.
|
|
44
|
+
* `@openrouter/sdk`'s `RequestAbortedError`) override to extend the check.
|
|
45
|
+
*/
|
|
46
|
+
protected isAbortError(error: unknown): boolean;
|
|
37
47
|
/**
|
|
38
48
|
* Applies provider-specific transformations for structured output compatibility.
|
|
39
49
|
* Override this in subclasses to handle provider-specific quirks.
|
|
40
50
|
*/
|
|
41
51
|
protected makeStructuredOutputCompatible(schema: Record<string, any>, originalRequired?: Array<string>): Record<string, any>;
|
|
52
|
+
/**
|
|
53
|
+
* Extract reasoning content from a stream chunk. Default returns
|
|
54
|
+
* `undefined` because the OpenAI Chat Completions chunk shape doesn't
|
|
55
|
+
* carry reasoning. The chunk param is typed `unknown` so an override can
|
|
56
|
+
* narrow to its own SDK chunk type without an `as` dance — the base only
|
|
57
|
+
* passes through `processStreamChunks`'s structurally-iterated chunk.
|
|
58
|
+
*/
|
|
59
|
+
protected extractReasoning(_chunk: unknown): {
|
|
60
|
+
text: string;
|
|
61
|
+
} | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Final shaping pass applied to parsed structured-output JSON before it is
|
|
64
|
+
* returned to the caller. Default converts `null` values to `undefined` so
|
|
65
|
+
* the result aligns with the original Zod schema's optional-field
|
|
66
|
+
* semantics. Subclasses with different conventions (OpenRouter historically
|
|
67
|
+
* preserves nulls) can override.
|
|
68
|
+
*/
|
|
69
|
+
protected transformStructuredOutput(parsed: unknown): unknown;
|
|
42
70
|
/**
|
|
43
71
|
* Processes streamed chunks from the Chat Completions API and yields AG-UI events.
|
|
44
72
|
* Override this in subclasses to handle provider-specific stream behavior.
|
|
45
73
|
*/
|
|
46
|
-
protected processStreamChunks(stream: AsyncIterable<
|
|
74
|
+
protected processStreamChunks(stream: AsyncIterable<ChatCompletionChunk>, options: TextOptions, aguiState: {
|
|
47
75
|
runId: string;
|
|
76
|
+
threadId: string;
|
|
48
77
|
messageId: string;
|
|
49
|
-
timestamp: number;
|
|
50
78
|
hasEmittedRunStarted: boolean;
|
|
51
79
|
}): AsyncIterable<StreamChunk>;
|
|
52
80
|
/**
|
|
53
81
|
* Maps common TextOptions to Chat Completions API request format.
|
|
54
82
|
* Override this in subclasses to add provider-specific options.
|
|
55
83
|
*/
|
|
56
|
-
protected mapOptionsToRequest(options: TextOptions):
|
|
84
|
+
protected mapOptionsToRequest(options: TextOptions): ChatCompletionCreateParamsStreaming;
|
|
57
85
|
/**
|
|
58
86
|
* Converts a single ModelMessage to the Chat Completions API message format.
|
|
59
87
|
* Override this in subclasses to handle provider-specific message formats.
|
|
60
88
|
*/
|
|
61
|
-
protected convertMessage(message: ModelMessage):
|
|
89
|
+
protected convertMessage(message: ModelMessage): ChatCompletionMessageParam;
|
|
62
90
|
/**
|
|
63
91
|
* Converts a single ContentPart to the Chat Completions API content part format.
|
|
64
92
|
* Override this in subclasses to handle additional content types or provider-specific metadata.
|
|
65
93
|
*/
|
|
66
|
-
protected convertContentPart(part: ContentPart):
|
|
94
|
+
protected convertContentPart(part: ContentPart): ChatCompletionContentPart | null;
|
|
67
95
|
/**
|
|
68
96
|
* Normalizes message content to an array of ContentPart.
|
|
69
97
|
* Handles backward compatibility with string content.
|