本页还没有译文
本页 38 段还没译成한국어,下面显示的是英文原文。@tanstack/ai-preact
还没翻译译自英文(tanstack.com)本页 38 段都还没译
Preact hooks for TanStack AI, providing convenient Preact bindings for the headless client.
Installation
useRegisterWebMCPTools(tools, options?)
Register executable client tools after the Preact component mounts. Preact removes them on cleanup and replaces them when tools or options change.
For a complete setup and behavior guide, see WebMCP Tools.
import {
useRegisterWebMCPTools,
type UseRegisterWebMCPToolsOptions,
} from "@tanstack/ai-preact";
import { searchProducts } from "./tools";
const tools = [searchProducts];
const options: UseRegisterWebMCPToolsOptions<typeof tools> = {
onError(error) {
console.error(error);
},
};
function ProductsPage() {
useRegisterWebMCPTools(tools, options);
return null;
}UseRegisterWebMCPToolsOptions<TTools, TContext> contains toolOptions, context, and onError. The hook owns the registration signal.
The context field is required when a tool declares a required runtime context. Keep tools and options stable when their values do not change.
usePageWebMCPTools(options?)
Read the WebMCP tools on the page as client tools. The array starts empty and updates when the page adds or removes a tool. Pass it to useChat as tools.
import { usePageWebMCPTools } from "@tanstack/ai-preact";
export function useSameOriginPageTools() {
return usePageWebMCPTools({
filter: (tool) => tool.origin === location.origin,
});
}filter skips a tool when it returns false. onError gets a failed WebMCP read. For a complete guide, see Page WebMCP Tools in Chat.
useChat(options?)
Main hook for managing chat state in Preact with full type safety.
import { useChat, fetchServerSentEvents } from "@tanstack/ai-preact";
import {
createChatClientOptions,
type InferChatMessages
} from "@tanstack/ai-client";
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";
import { useState } from "preact/hooks";
const updateUIDef = toolDefinition({
name: "updateUI",
description: "Show a notification in the UI",
inputSchema: z.object({ message: z.string() }),
});
function ChatComponent() {
const [, setNotification] = useState<string | null>(null);
// Create client tool implementations
const updateUI = updateUIDef.client((input) => {
setNotification(input.message);
return { success: true };
});
// Create typed tools array (no 'as const' needed!)
const tools = [updateUI];
const chatOptions = createChatClientOptions({
connection: fetchServerSentEvents("/api/chat"),
tools,
});
// Fully typed messages!
type ChatMessages = InferChatMessages<typeof chatOptions>;
const { messages, sendMessage, isLoading, error, addToolApprovalResponse } =
useChat(chatOptions);
return <div>{/* Chat UI with typed messages */}</div>;
}Options
Extends ChatClientOptions from @tanstack/ai-client:
connection- Connection adapter (required)tools?- Array of client tool implementations (with.client()method)initialMessages?- Initial messages arraythreadId?- The only identity for this chat. Required when persistence is on. If omitted, minted after mount.forwardedProps?- Arbitrary client-controlled JSON forwarded to the server in the AG-UIRunAgentInput.forwardedPropsfield (e.g.,{ provider: 'openai', model: 'gpt-5.5' })body?- Deprecated. UseforwardedPropsinstead. Still works for backward compatibility; values are merged intoforwardedPropson the wirebyok?- Optional BYOK keyring fromdefineByok. On each send the client prepares the resolved provider and stampsx-byok-*request headers. Keys never go in the bodybyokProvider?- Optional function that returns the provider slug for this chat. If it returns a slug, only that key is prepared and sent. Otherwise the mergedproviderfromforwardedProps,body, and per-callsendMessagebodyis used. Later sources win. If no slug resolves, the send throws instead of attaching every stored keycontext?- Typed client-local runtime context passed to client tool implementations. This value is not serialized to the serveronResponse?- Callback when response is receivedonChunk?- Callback when stream chunk is receivedonFinish?- Callback when response finishesonError?- Callback when error occursonInterruptStateChange?- Callback when interrupt state changes; context source ishydratefor restored state andlivefor streamed or client-initiated updatesstreamProcessor?- Stream processing configuration
Note: Client tools are now automatically executed - no onToolCall callback needed!
Returns
import type { UIMessage } from "@tanstack/ai-preact";
import type { ModelMessage } from "@tanstack/ai/client";
import type {
MultimodalContent,
SendMessageOptions,
} from "@tanstack/ai-client";
interface UseChatReturn {
messages: UIMessage[];
sendMessage: (
content: string | MultimodalContent,
options?: SendMessageOptions,
) => Promise<void>;
append: (message: ModelMessage | UIMessage) => Promise<void>;
addToolResult: (result: {
toolCallId: string;
tool: string;
output: any;
state?: "output-available" | "output-error";
errorText?: string;
}) => Promise<void>;
addToolApprovalResponse: (response: {
id: string;
approved: boolean;
}) => Promise<void>;
reload: () => Promise<void>;
stop: () => void;
isLoading: boolean;
isHydrating: boolean;
error: Error | undefined;
setMessages: (messages: UIMessage[]) => void;
clear: () => void;
}useByok(client)
Subscribe to a ByokClient snapshot in Preact.
import { useByok } from "@tanstack/ai-preact";
import { byok } from "./byok";
export function KeyStatus() {
const snapshot = useByok(byok);
const openai = snapshot.status.openai;
const last4 = openai && "masked" in openai ? openai.masked : "No key";
return <p>{last4}</p>;
}snapshot has status, locked, and prompt. Call byok.update(provider, value) from your own UI to save a key. See Bring Your Own Key.
Connection Adapters
Re-exported from @tanstack/ai-client for convenience:
import {
fetchServerSentEvents,
fetchHttpStream,
fetchJson,
stream,
type ConnectionAdapter,
} from "@tanstack/ai-preact";Example: Basic Chat
import { useState } from "preact/hooks";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-preact";
export function Chat() {
const [input, setInput] = useState("");
const { messages, sendMessage, isLoading } = useChat({
connection: fetchServerSentEvents("/api/chat"),
});
const handleSubmit = (e: Event) => {
e.preventDefault();
if (input.trim() && !isLoading) {
sendMessage(input);
setInput("");
}
};
return (
<div>
<div>
{messages.map((message) => (
<div key={message.id}>
<strong>{message.role}:</strong>
{message.parts.map((part, idx) => {
if (part.type === "thinking") {
return (
<div key={idx} class="text-sm text-gray-500 italic">
💭 Thinking: {part.content}
</div>
);
}
if (part.type === "text") {
return <span key={idx}>{part.content}</span>;
}
return null;
})}
</div>
))}
</div>
<form onSubmit={handleSubmit}>
<input
value={input}
onInput={(e) => setInput(e.currentTarget.value)}
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}Example: Tool Approval
import { useChat, fetchServerSentEvents } from "@tanstack/ai-preact";
export function ChatWithApproval() {
const { messages, sendMessage, addToolApprovalResponse } = useChat({
connection: fetchServerSentEvents("/api/chat"),
});
return (
<div>
{messages.map((message) =>
message.parts.map((part) => {
if (
part.type === "tool-call" &&
part.state === "approval-requested" &&
part.approval
) {
return (
<div key={part.id}>
<p>Approve: {part.name}</p>
<button
onClick={() =>
addToolApprovalResponse({
id: part.approval!.id,
approved: true,
})
}
>
Approve
</button>
<button
onClick={() =>
addToolApprovalResponse({
id: part.approval!.id,
approved: false,
})
}
>
Deny
</button>
</div>
);
}
return null;
})
)}
</div>
);
}Example: Client Tools with Type Safety
import { useChat, fetchServerSentEvents } from "@tanstack/ai-preact";
import {
createChatClientOptions,
type InferChatMessages
} from "@tanstack/ai-client";
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";
import { useState } from "preact/hooks";
const updateUIDef = toolDefinition({
name: "updateUI",
description: "Show a notification in the UI",
inputSchema: z.object({ message: z.string(), type: z.string() }),
});
const saveToStorageDef = toolDefinition({
name: "saveToStorage",
description: "Save a value to localStorage",
inputSchema: z.object({ key: z.string(), value: z.string() }),
});
export function ChatWithClientTools() {
const [notification, setNotification] = useState<{ message: string; type: string } | null>(null);
// Create client implementations
const updateUI = updateUIDef.client((input) => {
// ✅ input is fully typed!
setNotification({ message: input.message, type: input.type });
return { success: true };
});
const saveToStorage = saveToStorageDef.client((input) => {
localStorage.setItem(input.key, input.value);
return { saved: true };
});
// Create typed tools array (no 'as const' needed!)
const tools = [updateUI, saveToStorage];
const { messages, sendMessage } = useChat({
connection: fetchServerSentEvents("/api/chat"),
tools, // ✅ Automatic execution, full type safety
});
return (
<div>
{messages.map((message) =>
message.parts.map((part) => {
if (part.type === "tool-call" && part.name === "updateUI") {
// ✅ part.input and part.output are fully typed!
return <div>Tool executed: {part.name}</div>;
}
return null;
})
)}
</div>
);
}createChatClientOptions(options)
Helper to create typed chat options (re-exported from @tanstack/ai-client).
import {
createChatClientOptions,
type InferChatMessages
} from "@tanstack/ai-client";
import { fetchServerSentEvents } from "@tanstack/ai-preact";
import { tool1, tool2 } from "./tools";
// Create typed tools array (no 'as const' needed!)
const tools = [tool1, tool2];
const chatOptions = createChatClientOptions({
connection: fetchServerSentEvents("/api/chat"),
tools,
});
type Messages = InferChatMessages<typeof chatOptions>;Types
Re-exported from @tanstack/ai-client:
UIMessage<TTools>- Message type with tool type parameterMessagePart<TTools>- Message part with tool type parameterTextPart- Text content partThinkingPart- Thinking content partToolCallPart<TTools>- Tool call part (discriminated union)ToolResultPart- Tool result partChatClientOptions<TTools, TContext>- Chat client options with typed client runtime contextConnectionAdapter- Connection adapter interfaceInferChatMessages<T>- Extract message type from options
Re-exported from @tanstack/ai:
toolDefinition()- Create isomorphic tool definitionToolDefinitionInstance- Tool definition typeClientTool- Client tool typeServerTool- Server tool type
Next Steps
- Getting Started - Learn the basics
- Tools Guide - Learn about the isomorphic tool system
- Client Tools - Learn about client-side tools