TanStack AI 支持在消息中使用多模态内容,允许您将图像、音频、视频和文档与文本一起发送给支持这些模态的 AI 模型。
向 AI 模型发送消息时,您可以包含不同类型的内容
多模态消息使用 ContentPart 类型来表示不同的内容类型
import type { ContentPart, ImagePart, TextPart } from '@tanstack/ai'
// Text content
const textPart: TextPart = {
type: 'text',
content: 'What do you see in this image?'
}
// Image from base64 data (mimeType is required for data sources)
const imagePart: ImagePart = {
type: 'image',
source: {
type: 'data',
value: 'base64EncodedImageData...',
mimeType: 'image/jpeg' // Required for data sources
},
metadata: {
// Provider-specific metadata
detail: 'high' // OpenAI detail level
}
}
// Image from URL (mimeType is optional for URL sources)
const imageUrlPart: ImagePart = {
type: 'image',
source: {
type: 'url',
value: 'https://example.com/image.jpg',
mimeType: 'image/jpeg' // Optional hint for URL sources
}
}
消息的 content 可以是字符串或 ContentPart 数组
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const response = await chat({
adapter: openaiText('gpt-5.2'),
messages: [
{
role: 'user',
content: [
{ type: 'text', content: 'What is in this image?' },
{
type: 'image',
source: {
type: 'url',
value: 'https://example.com/photo.jpg'
}
}
]
}
]
})
OpenAI 在其视觉和音频模型中支持图像和音频
import { openaiText } from '@tanstack/ai-openai'
const adapter = openaiText()
// Image with detail level metadata
const message = {
role: 'user' ,
content: [
{ type: 'text' , content: 'Describe this image' },
{
type: 'image' ,
source: { type: 'data' , value: imageBase64, mimeType: 'image/jpeg' },
metadata: { detail: 'high' } // 'auto' | 'low' | 'high'
}
]
}
模型支持的模态
Anthropic 的 Claude 模型支持图像和 PDF 文档
import { anthropicText } from '@tanstack/ai-anthropic'
const adapter = anthropicText()
// Image with mimeType in source
const imageMessage = {
role: 'user' ,
content: [
{ type: 'text' , content: 'What do you see?' },
{
type: 'image' ,
source: { type: 'data' , value: imageBase64, mimeType: 'image/jpeg' }
}
]
}
// PDF document
const docMessage = {
role: 'user',
content: [
{ type: 'text', content: 'Summarize this document' },
{
type: 'document',
source: { type: 'data', value: pdfBase64, mimeType: 'application/pdf' }
}
]
}
支持的模态
Google 的 Gemini 模型支持广泛的模态
import { geminiText } from '@tanstack/ai-gemini'
const adapter = geminiText()
// Image with mimeType in source
const message = {
role: 'user',
content: [
{ type: 'text', content: 'Analyze this image' },
{
type: 'image',
source: { type: 'data', value: imageBase64, mimeType: 'image/png' }
}
]
}
支持的模态
Ollama 支持兼容模型中的图像
import { ollamaText } from '@tanstack/ai-ollama'
const adapter = ollamaText('https://:11434')
// Image as base64
const message = {
role: 'user',
content: [
{ type: 'text', content: 'What is in this image?' },
{
type: 'image',
source: { type: 'data', value: imageBase64, mimeType: 'image/jpeg' }
}
]
}
注意:Ollama 的支持因模型而异。请查阅特定模型的文档以获取多模态功能。
内容可以作为内联数据或 URL 提供
对于内联 base64 编码的内容,请使用 type: 'data'。 需要 mimeType 字段,以确保提供商接收到正确的内容类型信息
const imagePart = {
type: 'image',
source: {
type: 'data',
value: 'iVBORw0KGgoAAAANSUhEUgAAAAUA...', // Base64 string
mimeType: 'image/png' // Required for data sources
}
}
const audioPart = {
type: 'audio',
source: {
type: 'data',
value: 'base64AudioData...',
mimeType: 'audio/mp3' // Required for data sources
}
}
对于托管在 URL 上的内容,请使用 type: 'url'。 mimeType 字段是 可选的,因为提供商通常可以从 URL 或响应标头推断它
const imagePart = {
type: 'image' ,
source: {
type: 'url' ,
value: 'https://example.com/image.jpg',
mimeType: 'image/jpeg' // Optional hint
}
}
注意:并非所有提供商都支持基于 URL 的内容的所有模态。请查阅提供商文档以获取具体信息。
字符串内容继续像以前一样工作
// This still works
const message = {
role: 'user',
content: 'Hello, world!'
}
// And this works for multimodal
const multimodalMessage = {
role: 'user',
content: [
{ type: 'text', content: 'Hello, world!' },
{ type: 'image', source: { type: 'url', value: '...' } }
]
}
多模态类型已完全类型化。提供特定元数据类型可用
import type {
ContentPart,
ImagePart,
DocumentPart,
AudioPart,
VideoPart,
TextPart
} from '@tanstack/ai'
// Provider-specific metadata types
import type { OpenAIImageMetadata } from '@tanstack/ai-openai'
import type { AnthropicImageMetadata } from '@tanstack/ai-anthropic'
import type { GeminiMediaMetadata } from '@tanstack/ai-gemini'
从外部来源(如 request.json())接收消息时,数据被类型化为 any,这可能会绕过 TypeScript 的类型检查。使用 assertMessages 来恢复类型安全
import { chat, assertMessages } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
// In an API route handler
const { messages: incomingMessages } = await request.json()
const adapter = openaiText('gpt-5.2')
// Assert incoming messages are compatible with gpt-5.2 (text + image only)
const typedMessages = assertMessages({ adapter }, incomingMessages)
// Now TypeScript will properly check any additional messages you add
const stream = chat({
adapter,
messages: [
...typedMessages,
// This will error if you try to add unsupported content types
{
role: 'user',
content: [
{ type: 'text', content: 'What do you see?' },
{ type: 'image', source: { type: 'url', value: '...' } }
]
}
]
})
注意:assertMessages 仅是一个类型级别的断言。它不执行运行时验证。对于消息内容的运行时验证,请使用像 Zod 这样的模式验证库。
使用适当的源类型:对于小内容或需要内联包含内容时,使用 data。对于大文件或内容已托管时,使用 url。
包含元数据:提供相关元数据(如 mimeType 或 detail)以帮助模型正确处理内容。
检查模型支持:并非所有模型都支持所有模态。验证您使用的模型是否支持您想要发送的内容类型。
优雅地处理错误:当模型不支持特定模态时,它可能会引发错误。在您的应用程序中处理这些情况。
在使用 ChatClient 从 @tanstack/ai-client 时,您可以使用 sendMessage 方法直接从您的 UI 发送多模态消息。
sendMessage 方法接受一个简单的字符串或一个 MultimodalContent 对象
import { ChatClient, fetchServerSentEvents } from '@tanstack/ai-client'
const client = new ChatClient({
connection: fetchServerSentEvents('/api/chat'),
})
// Simple text message
await client.sendMessage('Hello!')
// Multimodal message with image
await client.sendMessage({
content: [
{ type: 'text', content: 'What is in this image?' },
{
type: 'image',
source: { type: 'url', value: 'https://example.com/photo.jpg' }
}
]
})
您可以为消息提供自定义 ID
await client.sendMessage({
content: 'Hello!',
id: 'custom-message-id-123'
})
第二个参数允许您传递该特定请求的附加主体参数。这些参数将与客户端的基本主体配置浅合并,每条消息的参数优先
const client = new ChatClient({
connection: fetchServerSentEvents('/api/chat'),
body: { model: 'gpt-5' }, // Base body params
})
// Override model for this specific message
await client.sendMessage('Analyze this complex problem', {
model: 'gpt-5',
temperature: 0.2,
})
以下是如何在 React 组件中使用多模态消息
import { useChat } from '@tanstack/ai-react'
import { fetchServerSentEvents } from '@tanstack/ai-client'
import { useState } from 'react'
function ChatWithImages() {
const [imageUrl, setImageUrl] = useState('')
const { sendMessage, messages } = useChat({
connection: fetchServerSentEvents('/api/chat'),
})
const handleSendWithImage = () => {
if (imageUrl) {
sendMessage({
content: [
{ type: 'text', content: 'What do you see in this image?' },
{ type: 'image', source: { type: 'url', value: imageUrl } }
]
})
}
}
return (
<div>
<input
type="url"
placeholder="Image URL"
value={imageUrl}
onChange={(e) => setImageUrl(e.target.value)}
/>
<button onClick={handleSendWithImage}>Send with Image</button>
</div>
)
}
以下是如何处理文件上传并将它们作为多模态内容发送
import { useChat } from '@tanstack/ai-react'
import { fetchServerSentEvents } from '@tanstack/ai-client'
function ChatWithFileUpload() {
const { sendMessage } = useChat({
connection: fetchServerSentEvents('/api/chat'),
})
const handleFileUpload = async (file: File) => {
// Convert file to base64
const base64 = await new Promise<string>((resolve) => {
const reader = new FileReader()
reader.onload = () => {
const result = reader.result as string
// Remove data URL prefix (e.g., "data:image/png;base64,")
resolve(result.split(',')[1])
}
reader.readAsDataURL(file)
})
// Determine content type based on file type
const type = file.type.startsWith('image/')
? 'image'
: file.type.startsWith('audio/')
? 'audio'
: file.type.startsWith('video/')
? 'video'
: 'document'
await sendMessage({
content: [
{ type: 'text', content: `Please analyze this ${type}` },
{
type,
source: { type: 'data', value: base64 },
metadata: { mimeType: file.type }
}
]
})
}
return (
<input
type="file"
accept="image/*,audio/*,video/*,.pdf"
onChange={(e) => {
const file = e.target.files?.[0]
if (file) handleFileUpload(file)
}}
/>
)
}