extendAdapter adds your model ids to an existing text adapter. openaiText('my-fine-tuned-model') type-checks after you register that id.
The built-in models keep their types, and your models get the types that you declare.
Wrap the factory with extendAdapter. Describe each model with createModel: the name, then the input types it can read.
// lib/ai/my-openai.ts
import { createModel, extendAdapter } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
export const myOpenai = extendAdapter(openaiText, [
createModel('my-fine-tuned-model', ['text', 'image']),
createModel('my-other-model', ['text']),
])Use myOpenai where you used openaiText:
import { chat } from '@tanstack/ai'
import { myOpenai } from './lib/ai/my-openai'
const stream = chat({
adapter: myOpenai('my-fine-tuned-model'),
messages: [{ role: 'user', content: 'Hello!' }],
threadId: 'thread-1',
})
for await (const chunk of stream) {
console.log(chunk)
}Your model name now autocompletes, and the request goes to the provider with that name.
The new factory still takes every model of the original factory. It also takes the same config argument:
import { createModel, extendAdapter } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const myOpenai = extendAdapter(openaiText, [
createModel('my-fine-tuned-model', ['text', 'image']),
])
// A built-in model, with its normal types
const builtIn = myOpenai('gpt-6.1-sol')
// Your model, with a config for the client
const custom = myOpenai('my-fine-tuned-model', {
baseURL: 'https://my-proxy.com/v1',
timeout: 30000,
})Give types to the models that your proxy serves:
import { createModel, extendAdapter } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const proxyAdapter = extendAdapter(openaiText, [
createModel('llama-3.1-70b', ['text']),
createModel('mixtral-8x7b', ['text']),
])
const adapter = proxyAdapter('llama-3.1-70b', {
baseURL: 'https://my-llm-proxy.com/v1',
})extendAdapter works with every adapter factory, for example anthropicText:
import { chat, createModel, extendAdapter } from '@tanstack/ai'
import { anthropicText } from '@tanstack/ai-anthropic'
const myAnthropic = extendAdapter(anthropicText, [
createModel('ft:claude-3-opus:my-org:custom-task:abc123', ['text', 'image']),
])
const stream = chat({
adapter: myAnthropic('ft:claude-3-opus:my-org:custom-task:abc123'),
messages: [{ role: 'user', content: 'Analyze this text.' }],
threadId: 'thread-1',
})extendAdapter changes only the types. At runtime, it returns the original factory.
A user attaches an image, but the selected model reads only text. You want to know that before you send the request. Read inputModalities on the text adapter:
import { openaiText } from '@tanstack/ai-openai'
const adapter = openaiText('gpt-6.1-sol')
console.log(adapter.inputModalities) // ['image', 'text']
if (!adapter.inputModalities?.includes('image')) {
console.log('This model cannot read images. Pick another model.')
}Do you write your own adapter? See Set inputModalities in your own adapter.
The second argument of the positional form lists the input types of the model:
import { createModel } from '@tanstack/ai'
const models = [
createModel('text-only-model', ['text']),
createModel('multimodal-model', ['text', 'image', 'audio']),
] as constThe input types are 'text', 'image', 'audio', 'video', and 'document'.
A model definition (ExtendedModelDef) has three required fields: name, input, and modelOptions. It also has two optional fields: features and tools.
To set the typed modelOptions, the features, or the provider tools, give createModel a capabilities object as the second argument:
import { createModel } from '@tanstack/ai'
import type { OpenAITextProviderOptions } from '@tanstack/ai-openai'
// Type brand for the provider options. The value is not used at runtime.
const modelOptions: OpenAITextProviderOptions = {}
const reasoner = createModel('my-reasoner', {
input: ['text'],
features: ['reasoning', 'structured_outputs'],
tools: ['web_search'],
modelOptions,
})The new factory rejects a name that is not in either list:
import { createModel, extendAdapter } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const myOpenai = extendAdapter(openaiText, [createModel('custom-model', ['text'])])
// OK: a built-in model, with its built-in types
const a1 = myOpenai('gpt-6.1-sol')
// OK: your model, with the types that you declared
const a2 = myOpenai('custom-model')
// Type error: the name is not known.
// The check works when you assign the result to a variable.
const invalid = myOpenai('nonexistent-model')Do you build a full adapter for a new provider? Extend Adapter: Advanced shows how to: