본문으로 건너뛰기

생성 훅

TanStack AI는 이미지, 오디오, 음성, 전사, 요약, 동영상 등 모든 생성 유형을 위한 프레임워크 훅을 제공합니다. 각 훅은 서버 엔드포인트에 연결하고 로딩, 오류, 결과 상태를 관리합니다.

새로고침 및 연결 끊김 이후에도 유지: 모든 생성 훅은 useChat과 동일한 persistence 옵션을 사용하므로 장시간 실행의 상태와 결과가 페이지 새로고침이나 연결 끊김 이후에도 돌아옵니다. 생성에서 threadId는 대화가 아니라 연속 실행이 채우는 슬롯(product-7-hero)의 이름으로 사용되며, 이를 기준으로 복원됩니다. 자세한 내용은 Id map을 참조합니다. 설정 방법은 Generation Persistence를 참조합니다.

개요

생성 훅은 모든 미디어 유형에서 일관된 API를 공유합니다.

입력결과 타입
useGenerateImageImageGenerateInputImageGenerationResult
useGenerateAudioAudioGenerateInputAudioGenerationResult
useGenerateSpeechSpeechGenerateInputTTSResult
useTranscriptionTranscriptionGenerateInputTranscriptionResult
useSummarizeSummarizeGenerateInputSummarizationResult
useGenerateVideoVideoGenerateInputVideoGenerateResult
useGeneration일반 TInput일반 TResult

모든 훅은 generate, result, isLoading, error, status, stop, reset, runId(진행 중인 작업의 ID 또는 null)로 구성된 동일한 핵심 형태를 반환합니다. connection(스트리밍 전송) 또는 fetcher(직접 비동기 호출) 중 하나를 제공합니다.

서버 설정

클라이언트에서 훅을 사용하기 전에 생성을 실행하고 결과를 SSE로 반환하는 서버 엔드포인트가 필요합니다. 다음은 최소한의 이미지 생성 엔드포인트입니다.

// routes/api/generate/image.ts
import { generateImage, toServerSentEventsResponse } from '@tanstack/ai'
import { openaiImage } from '@tanstack/ai-openai'

export async function POST(req: Request) {
const { prompt, size, numberOfImages } = await req.json()

const stream = generateImage({
adapter: openaiImage('dall-e-3'),
prompt,
size,
numberOfImages,
stream: true,
})

return toServerSentEventsResponse(stream)
}

모든 생성 유형에 동일한 패턴을 적용할 수 있습니다. generateImagegenerateSpeech, generateTranscription, summarize 또는 generateVideo로 바꾸면 됩니다. 서버 측 세부 사항은 개별 미디어 가이드를 참조합니다.

useGenerateImage

이미지 생성을 실행하고 결과를 렌더링합니다.

import { useGenerateImage, fetchServerSentEvents } from '@tanstack/ai-react'
import { useState } from 'react'

function ImageGenerator() {
const [prompt, setPrompt] = useState('')
const { generate, result, isLoading, error, reset } = useGenerateImage({
connection: fetchServerSentEvents('/api/generate/image'),
})

return (
<div>
<input
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Describe an image..."
/>
<button
onClick={() => generate({ prompt })}
disabled={isLoading || !prompt.trim()}
>
{isLoading ? 'Generating...' : 'Generate'}
</button>

{error && <p>Error: {error.message}</p>}

{result?.images.map((img, i) => (
<img
key={i}
src={img.url || `data:image/png;base64,${img.b64Json}`}
alt={img.revisedPrompt || 'Generated image'}
/>
))}

{result && <button onClick={reset}>Clear</button>}
</div>
)
}

generate 함수는 ImageGenerateInput을 받습니다.

필드타입설명
promptstring원하는 이미지의 텍스트 설명(필수)
numberOfImagesnumber생성할 이미지 수
sizestringWIDTHxHEIGHT 형식의 이미지 크기(예: "1024x1024")
modelOptionsRecord<string, any>모델별 옵션

useGenerateAudio

텍스트 프롬프트로 음악이나 음향 효과를 생성합니다.

fetcher를 사용할 때는 options.headers를 POST 요청에 전개합니다. 해당 헤더에 x-byok-* 키가 포함되도록 byok를 전달합니다.

import { useGenerateAudio } from '@tanstack/ai-react'
import { defineByok, defaultByokStorage } from '@tanstack/ai-client/byok'

const byok = defineByok({ storage: defaultByokStorage() })

function AudioGenerator() {
const { generate, result, isLoading, error } = useGenerateAudio({
byok,
byokProvider: () => 'fal',
fetcher: async (input, options) => {
return fetch('/api/generate/audio', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
body: JSON.stringify(input),
signal: options?.signal,
})
},
})

return (
<div>
<button
onClick={() => generate({ prompt: 'An upbeat electronic track' })}
disabled={isLoading}
>
{isLoading ? 'Generating...' : 'Generate'}
</button>
{error && <p>Error: {error.message}</p>}
{result?.audio.url && <audio src={result.audio.url} controls />}
</div>
)
}

서버에서는 헤더(또는 환경 변수)를 읽은 다음 generateAudio를 실행합니다.

import { generateAudio, toServerSentEventsResponse } from '@tanstack/ai'
import { falAudio } from '@tanstack/ai-fal'
import { falByok } from '@tanstack/ai-fal/byok'
import { byokMissing, getByokKey } from '@tanstack/ai/byok/server'

export async function POST(request: Request) {
const apiKey = getByokKey(request, falByok)
if (!apiKey) return byokMissing(falByok)

const body: unknown = await request.json()
if (typeof body !== 'object' || body === null || !('prompt' in body)) {
return new Response('Bad request', { status: 400 })
}
const prompt = body.prompt
if (typeof prompt !== 'string') {
return new Response('Bad request', { status: 400 })
}

const stream = generateAudio({
adapter: falAudio('fal-ai/minimax-music/v2.6', { apiKey }),
prompt,
stream: true,
})
return toServerSentEventsResponse(stream)
}

generate 함수는 AudioGenerateInput을 받습니다.

필드타입설명
promptstring원하는 오디오의 텍스트 설명(필수)
durationnumber원하는 재생 시간(초)
modelOptionsRecord<string, any>모델별 옵션

defineByok와 저장 UI에 대한 자세한 내용은 Bring Your Own Key를 참조합니다.

useGenerateSpeech

텍스트를 음성으로 변환하고 재생합니다.

import { useGenerateSpeech, fetchServerSentEvents } from '@tanstack/ai-react'
import { useRef } from 'react'

function SpeechGenerator() {
const audioRef = useRef<HTMLAudioElement>(null)
const { generate, result, isLoading, error } = useGenerateSpeech({
connection: fetchServerSentEvents('/api/generate/speech'),
})

return (
<div>
<button
onClick={() => generate({ text: 'Hello, welcome to TanStack AI!', voice: 'alloy' })}
disabled={isLoading}
>
{isLoading ? 'Generating...' : 'Generate Speech'}
</button>

{error && <p>Error: {error.message}</p>}

{result && (
<audio
ref={audioRef}
src={`data:audio/${result.format};base64,${result.audio}`}
controls
autoPlay
/>
)}
</div>
)
}

generate 함수는 SpeechGenerateInput을 받습니다.

필드타입설명
textstring음성으로 변환할 텍스트(필수)
voicestring사용할 음성(예: "alloy", "echo")
format'mp3' | 'opus' | 'aac' | 'flac' | 'wav' | 'pcm'출력 오디오 형식
speednumber오디오 속도(0.25~4.0)
modelOptionsRecord<string, any>모델별 옵션

TTSResult에는 audio(base64 인코딩), format, 그리고 선택적으로 durationcontentType이 포함됩니다.

useTranscription

오디오 파일을 텍스트로 전사합니다.

import { useTranscription, fetchServerSentEvents } from '@tanstack/ai-react'

function Transcriber() {
const { generate, result, isLoading, error } = useTranscription({
connection: fetchServerSentEvents('/api/transcribe'),
})

const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) {
const reader = new FileReader()
reader.onload = () => {
generate({ audio: reader.result as string, language: 'en' })
}
reader.readAsDataURL(file)
}
}

return (
<div>
<input type="file" accept="audio/*" onChange={handleFile} />

{isLoading && <p>Transcribing...</p>}
{error && <p>Error: {error.message}</p>}

{result && (
<div>
<h3>Transcription</h3>
<p>{result.text}</p>
{result.language && <p>Language: {result.language}</p>}
{result.duration && <p>Duration: {result.duration}s</p>}
</div>
)}
</div>
)
}

generate 함수는 TranscriptionGenerateInput을 받습니다.

필드타입설명
audiostring | File | Blob | ArrayBuffer오디오 데이터 -- base64 문자열, File, Blob 또는 ArrayBuffer(필수)
languagestringISO-639-1 형식의 언어(예: "en")
promptstring전사를 안내하는 선택적 프롬프트
responseFormat'json' | 'text' | 'srt' | 'verbose_json' | 'vtt'일반 출력 형식
modelOptionsRecord<string, any>모델별 옵션

useSummarize

구성 가능한 출력 스타일로 긴 텍스트를 요약합니다.

import { useSummarize, fetchServerSentEvents } from '@tanstack/ai-react'
import { useState } from 'react'

function Summarizer() {
const [text, setText] = useState('')
const { generate, result, isLoading, error } = useSummarize({
connection: fetchServerSentEvents('/api/summarize'),
})

return (
<div>
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Paste text to summarize..."
rows={8}
/>
<button
onClick={() => generate({ text, style: 'bullet-points', maxLength: 200 })}
disabled={isLoading || !text.trim()}
>
{isLoading ? 'Summarizing...' : 'Summarize'}
</button>

{error && <p>Error: {error.message}</p>}

{result && (
<div>
<h3>Summary</h3>
<p>{result.summary}</p>
</div>
)}
</div>
)
}

generate 함수는 SummarizeGenerateInput을 받습니다.

필드타입설명
textstring요약할 텍스트(필수)
maxLengthnumber요약의 최대 길이
style'bullet-points' | 'paragraph' | 'concise'요약 스타일
focusArray<string>집중할 주제
modelOptionsRecord<string, any>모델별 옵션

useGenerateVideo

동영상 생성은 비동기 방식입니다. 서버에서 작업을 생성한 다음 완료될 때까지 상태를 폴링합니다. 훅은 전체 수명 주기를 관리하고 jobIdvideoStatus를 노출하므로 진행률을 표시할 수 있습니다.

import { useGenerateVideo, fetchServerSentEvents } from '@tanstack/ai-react'

function VideoGenerator() {
const { generate, result, jobId, videoStatus, isLoading, error } =
useGenerateVideo({
connection: fetchServerSentEvents('/api/generate/video'),
onStatusUpdate: (status) => {
console.log(`Video ${status.jobId}: ${status.status} (${status.progress}%)`)
},
})

return (
<div>
<button
onClick={() => generate({ prompt: 'A flying car over a city', duration: 5 })}
disabled={isLoading}
>
{isLoading ? 'Generating...' : 'Generate Video'}
</button>

{isLoading && videoStatus && (
<div>
<p>Job: {jobId}</p>
<p>Status: {videoStatus.status}</p>
{videoStatus.progress != null && (
<progress value={videoStatus.progress} max={100} />
)}
</div>
)}

{error && <p>Error: {error.message}</p>}

{result && (
<video src={result.url} controls autoPlay style={{ maxWidth: '100%' }} />
)}
</div>
)
}

generate 함수는 VideoGenerateInput을 받습니다.

필드타입설명
promptstring원하는 동영상의 텍스트 설명(필수)
sizestring동영상 크기 -- 형식은 공급자에 따라 다름(예: "16:9", "1280x720")
durationnumber동영상 재생 시간(초)
modelOptionsRecord<string, any>모델별 옵션

useGenerateVideo는 표준 속성 외에 두 가지 속성을 추가로 반환합니다.

속성타입설명
jobIdstring | null현재 작업 ID. 서버가 동영상 작업을 생성할 때 설정됩니다.
videoStatusVideoStatusInfo | nullstatus, progress, jobId가 포함된 실시간 상태 업데이트

VideoStatusInfo 타입은 다음과 같습니다.

interface VideoStatusInfo {
jobId: string
status: 'pending' | 'processing' | 'completed' | 'failed'
progress?: number // 0-100
url?: string // Set when completed
error?: string // Set when failed
}

세밀한 추적을 위해 훅에 onJobCreatedonStatusUpdate 콜백도 전달할 수 있습니다.

기본 훅: useGeneration

모든 특화 훅은 useGeneration을 기반으로 합니다. 기본 제공 훅에 맞지 않는 사용자 지정 생성 유형이 있을 때 직접 사용합니다.

import { useGeneration, fetchServerSentEvents } from '@tanstack/ai-react'

interface EmbeddingInput {
text: string
model?: string
}

interface EmbeddingResult {
embedding: Array<number>
model: string
usage: { totalTokens: number }
}

function EmbeddingGenerator() {
const { generate, result, isLoading, error } = useGeneration<
EmbeddingInput,
EmbeddingResult
>({
connection: fetchServerSentEvents('/api/generate/embedding'),
})

return (
<div>
<button onClick={() => generate({ text: 'Hello world' })} disabled={isLoading}>
Generate Embedding
</button>
{result && <p>Dimensions: {result.embedding.length}</p>}
</div>
)
}

옵션

UseGenerationOptions<TInput, TResult>는 다음을 받습니다.

옵션타입설명
connectionConnectConnectionAdapter스트리밍 전송(SSE, HTTP 스트림, 사용자 지정)
fetcherGenerationFetcher<TInput, TResult>직접 비동기 함수(스트리밍 프로토콜 불필요)
byokByokClient선택적 키링. 키는 x-byok-* 헤더에 들어가며 본문에는 절대 들어가지 않습니다.
byokProvider() => ProviderId | undefined선택적 공급자 슬러그. 슬러그를 반환하면 해당 키만 전송됩니다. 그렇지 않으면 body.provider를 사용합니다. 슬러그가 확인되지 않으면 generate에서 오류가 발생합니다.
threadIdstring이 생성의 안정적인 범위. persistence가 켜져 있으면 필수이며, 임시 실행에서는 선택 사항입니다.
bodyRecord<string, any>연결 요청과 함께 전송할 추가 본문 매개변수
onResult(result: TResult) => TOutput | null | void결과를 변환하거나 결과에 반응
onError(error: Error) => void오류 콜백
onProgress(progress: number, message?: string) => void진행률 업데이트(0~100)
onChunk(chunk: StreamChunk) => void청크별 콜백(연결 모드에서만)

반환 값

UseGenerationReturn<TOutput>은 다음을 제공합니다.

속성타입설명
generate(input: TInput) => Promise<void>생성 요청 실행
resultTOutput | null생성 결과 또는 null
isLoadingboolean생성이 진행 중인지 여부
errorError | undefined현재 오류(있는 경우)
statusGenerationClientState'idle' | 'generating' | 'success' | 'error'
stop() => void현재 생성 중단
reset() => void결과와 오류를 지우고 유휴 상태로 돌아감

결과 변환

onResult 콜백은 result에 저장되는 값을 변환할 수 있습니다.

import { useGenerateImage, fetchServerSentEvents } from '@tanstack/ai-react'
import type { ImageGenerationResult } from '@tanstack/ai'

const { result } = useGenerateImage({
connection: fetchServerSentEvents('/api/generate/image'),
onResult: (raw: ImageGenerationResult) => raw.images.map((img) => img.url || img.b64Json),
})
// result is now string[] instead of ImageGenerationResult

프레임워크 변형

모든 생성 훅은 React, Vue, Svelte에서 동일한 기능으로 제공됩니다. API 형태는 동일하며 명명 규칙과 반응형 프리미티브만 다릅니다.

생성 유형React (@tanstack/ai-react)Vue (@tanstack/ai-vue)Svelte (@tanstack/ai-svelte)
ImageuseGenerateImageuseGenerateImagecreateGenerateImage
AudiouseGenerateAudiouseGenerateAudiocreateGenerateAudio
SpeechuseGenerateSpeechuseGenerateSpeechcreateGenerateSpeech
TranscriptionuseTranscriptionuseTranscriptioncreateTranscription
SummarizationuseSummarizeuseSummarizecreateSummarize
VideouseGenerateVideouseGenerateVideocreateGenerateVideo
기본(범용)useGenerationuseGenerationcreateGeneration

세 패키지 모두 편의를 위해 @tanstack/ai-clientfetchServerSentEvents, fetchHttpStream, stream을 다시 내보냅니다.

Vue 참고: 반환 값은 DeepReadonly<ShallowRef<>>로 래핑됩니다. <script><template> 모두에서 .value로 액세스합니다.

Svelte 참고: 함수는 create* 명명 규칙을 사용하며 $state를 통해 Svelte 5 반응형 상태를 반환합니다.

다음 단계