본문으로 건너뛰기

@tanstack/ai-octane

@tanstack/ai-octane을 설치한 다음 React에서와 같은 방식으로 useChat을 호출합니다. 훅 모듈은 .tsrx이며 Octane 플러그인에서 컴파일됩니다.

설치

npm install @tanstack/ai-octane octane

octane은 필수 peer입니다. 이 패키지는 .svelte를 제공하는 Svelte 패키지처럼 컴파일되지 않은 소스를 게시합니다.

useChat(options)

Octane 컴포넌트에서 채팅 상태를 관리합니다.

import { useState } from 'octane'
import { useChat, fetchServerSentEvents } from '@tanstack/ai-octane'
import {
createChatClientOptions,
type InferChatMessages,
} from '@tanstack/ai-client'
import { toolDefinition } from '@tanstack/ai'
import { z } from 'zod'

const updateUIDef = toolDefinition({
name: 'updateUI',
description: 'Show a notification in the UI',
inputSchema: z.object({ message: z.string() }),
})

export function ChatComponent() {
const [notification, setNotification] = useState<string | null>(null)
const updateUI = updateUIDef.client((input) => {
setNotification(input.message)
return { success: true }
})
const tools = [updateUI]

const chatOptions = createChatClientOptions({
connection: fetchServerSentEvents('/api/chat'),
tools,
})

type ChatMessages = InferChatMessages<typeof chatOptions>

const { messages, sendMessage, isLoading, error, addToolApprovalResponse } =
useChat(chatOptions)

return (
<div>
{notification}
{isLoading ? 'Loading' : null}
{error ? error.message : null}
<button onClick={() => void sendMessage('hi')} type="button">
Send
</button>
<button
onClick={() =>
void addToolApprovalResponse({ id: 'approval-1', approved: true })
}
type="button"
>
Approve
</button>
{messages.length}
</div>
)
}

대응하는 서버 라우트는 계속 chat({ adapter, messages })를 실행하고 SSE를 반환합니다. Octane 빠른 시작을 참조합니다.

먼저 전달하는 옵션

@tanstack/ai-clientChatClientOptions를 확장합니다. connection 또는 fetcher 중 하나만 전달합니다.

  • connection 또는 fetcher - 훅이 서버와 통신하는 방법
  • tools? - .client()에서 생성한 클라이언트 도구 구현
  • threadId? - 이 채팅의 유일한 식별자입니다. 영속성이 켜져 있으면 필수입니다.
  • initialMessages? - 시작할 대화 기록
  • forwardedProps? - AG-UI forwardedProps 필드로 서버에 전송하는 JSON

나중에 추가하는 옵션

  • live? - 마운트 시 구독하고 언마운트 시 구독 해제
  • queue? - 턴이 진행 중일 때 sendMessage가 실행되면 수행할 작업입니다. 기본값은 대기열에 추가하는 것입니다.
  • interrupts? - 타입이 지정된 인터럽트 정의
  • context? - 클라이언트 도구를 위한 클라이언트 전용 런타임 컨텍스트입니다. 서버로 전송되지 않습니다.
  • onResponse? / onChunk? / onFinish? / onError? / onInterruptStateChange?
  • devtools? - 표시 옵션입니다. 훅은 항상 framework: 'octane' 태그를 지정합니다.
  • body? - 더 이상 권장되지 않습니다. forwardedProps를 사용합니다.

클라이언트 도구는 자동으로 실행됩니다. onToolCall 콜백은 없습니다.

connection 또는 fetcher를 변경하면 활성 ChatClient가 업데이트됩니다. threadId를 변경하면 새 클라이언트가 생성됩니다.

반환값

import type { UIMessage } from '@tanstack/ai-octane'
import type { ModelMessage } from '@tanstack/ai/client'
import type {
MultimodalContent,
ChatClientState,
ConnectionStatus,
QueuedMessage,
SendMessageOptions,
} from '@tanstack/ai-client'

interface UseChatReturn {
messages: Array<UIMessage>
sendMessage: (
content: string | MultimodalContent,
options?: SendMessageOptions,
) => Promise<void>
append: (message: ModelMessage | UIMessage) => Promise<void>
addToolResult: (result: {
toolCallId: string
tool: string
output: unknown
state?: 'output-available' | 'output-error'
errorText?: string
}) => Promise<void>
addToolApprovalResponse: (response: {
id: string
approved: boolean
}) => Promise<void>
reload: () => Promise<void>
stop: () => void
isLoading: boolean
error: Error | undefined
status: ChatClientState
isSubscribed: boolean
connectionStatus: ConnectionStatus
sessionGenerating: boolean
setMessages: (messages: Array<UIMessage>) => void
clear: () => void
queue: Array<QueuedMessage>
cancelQueued: (id: string) => void
runId: string | null
}

queue에는 실행 중인 동안 대기하는 전송이 저장됩니다. runId는 현재 진행 중인 턴이며, 없으면 null입니다. interrupts를 전달하면 인터럽트 헬퍼(interrupts, resolveInterrupts, cancelInterrupts, retryInterrupts)가 같은 객체에 포함됩니다.

연결 어댑터

@tanstack/ai-client에서 다시 내보냅니다.

import {
fetchServerSentEvents,
fetchHttpStream,
stream,
type ConnectionAdapter,
} from '@tanstack/ai-octane'

예시: 기본 채팅

import { useState } from 'octane'
import { useChat, fetchServerSentEvents } from '@tanstack/ai-octane'

export function Chat() {
const [input, setInput] = useState('')
const { messages, sendMessage, isLoading } = useChat({
connection: fetchServerSentEvents('/api/chat'),
})

return (
<div>
{messages.map((message) => (
<div key={message.id}>
<strong>{message.role}:</strong>
{message.parts
.filter((part) => part.type === 'text')
.map((part) => part.content)
.join('')}
</div>
))}
<input
value={input}
disabled={isLoading}
onInput={(event) => setInput(event.currentTarget.value)}
/>
<button
disabled={isLoading}
onClick={() => {
void sendMessage(input)
setInput('')
}}
type="button"
>
Send
</button>
</div>
)
}

예시: 도구 승인

import { useChat, fetchServerSentEvents } from '@tanstack/ai-octane'

export function ChatWithApproval() {
const { messages, sendMessage, addToolApprovalResponse } = useChat({
connection: fetchServerSentEvents('/api/chat'),
})

return (
<div>
<button onClick={() => void sendMessage('run the tool')} type="button">
Send
</button>
{messages.map((message) =>
message.parts.map((part) => {
if (
part.type !== 'tool-call' ||
part.state !== 'approval-requested' ||
!part.approval
) {
return null
}
const approvalId = part.approval.id
return (
<div key={part.id}>
<p>Approve: {part.name}</p>
<button
onClick={() =>
void addToolApprovalResponse({
id: approvalId,
approved: true,
})
}
type="button"
>
Approve
</button>
<button
onClick={() =>
void addToolApprovalResponse({
id: approvalId,
approved: false,
})
}
type="button"
>
Deny
</button>
</div>
)
}),
)}
</div>
)
}

기타 훅

이 패키지는 useRealtimeChat, useMcpAppBridge, useGeneration, useGenerateImage, useGenerateAudio, useGenerateSpeech, useGenerateVideo, useTranscription, useSummarize, useAudioRecorder도 내보냅니다.

./mcp-apps React AppRenderer 서브패스는 이 패키지에 없습니다. useMcpAppBridge는 포함됩니다.

타입

@tanstack/ai-client에서 다시 내보냅니다.

  • UIMessage<TTools>
  • ChatClientOptions<TTools, TContext>
  • InferChatMessages<T>
  • QueuedMessage, SendMessageOptions, WhenBusy

다음 단계