Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/api/ai-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ npm install @tanstack/ai-client
The main client class for managing chat state.

```typescript
import { ChatClient } from "@tanstack/ai-client";
import { fetchServerSentEvents } from "@tanstack/ai-client";
import { ChatClient, fetchServerSentEvents } from "@tanstack/ai-client";

const client = new ChatClient({
connection: fetchServerSentEvents("/api/chat"),
Expand Down Expand Up @@ -268,7 +267,7 @@ type ToolResultState =
Configure stream processing with chunk strategies:

```typescript
import { ImmediateStrategy } from "@tanstack/ai-client";
import { ImmediateStrategy, fetchServerSentEvents } from "@tanstack/ai-client";

const client = new ChatClient({
connection: fetchServerSentEvents("/api/chat"),
Expand Down
259 changes: 259 additions & 0 deletions docs/api/ai-solid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# @tanstack/ai-solid API

SolidJS primitives for TanStack AI, providing convenient SolidJS bindings for the headless client.

## Installation

```bash
npm install @tanstack/ai-solid
```

## `useChat(options?)`

Main primitive for managing chat state in SolidJS.

```typescript
import { useChat, fetchServerSentEvents } from "@tanstack/ai-solid";

function ChatComponent() {
const { messages, sendMessage, isLoading, error, addToolApprovalResponse } =
useChat({
connection: fetchServerSentEvents("/api/chat"),
initialMessages: [],
onToolCall: async ({ toolName, input }) => {
// Handle client tool execution
return { result: "..." };
},
});

return <div>{/* Chat UI */}</div>;
}
```

### Options

Extends `ChatClientOptions` but omits state change callbacks (handled by SolidJS signals):

- `connection` - Connection adapter (required)
- `initialMessages?` - Initial messages array
- `id?` - Unique identifier for this chat instance
- `body?` - Additional body parameters to send
- `onResponse?` - Callback when response is received
- `onChunk?` - Callback when stream chunk is received
- `onFinish?` - Callback when response finishes
- `onError?` - Callback when error occurs
- `onToolCall?` - Callback for client-side tool execution
- `streamProcessor?` - Stream processing configuration

### Returns

```typescript
interface UseChatReturn {
messages: Accessor<UIMessage[]>;
sendMessage: (content: string) => 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: Accessor<boolean>;
error: Accessor<Error | undefined>;
setMessages: (messages: UIMessage[]) => void;
clear: () => void;
}
```

**Note:** Unlike React, `messages`, `isLoading`, and `error` are SolidJS `Accessor` functions, so you need to call them to get their values (e.g., `messages()` instead of just `messages`).

## Connection Adapters

Re-exported from `@tanstack/ai-client` for convenience:

```typescript
import {
fetchServerSentEvents,
fetchHttpStream,
stream,
type ConnectionAdapter,
} from "@tanstack/ai-solid";
```

## Example: Basic Chat

```typescript
import { createSignal, For } from "solid-js";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-solid";

export function Chat() {
const [input, setInput] = createSignal("");

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>
<For each={messages()}>
{(message) => (
<div>
<strong>{message.role}:</strong>
<For each={message.parts}>
{(part) => {
if (part.type === "thinking") {
return (
<div class="text-sm text-gray-500 italic">
💭 Thinking: {part.content}
</div>
);
}
if (part.type === "text") {
return <span>{part.content}</span>;
}
return null;
}}
</For>
</div>
)}
</For>
</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

```typescript
import { For, Show } from "solid-js";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-solid";

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

return (
<div>
<For each={messages()}>
{(message) => (
<For each={message.parts}>
{(part) => (
<Show
when={
part.type === "tool-call" &&
part.state === "approval-requested" &&
part.approval
}
>
<div>
<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>
</Show>
)}
</For>
)}
</For>
</div>
);
}
```

## Example: Client Tools

```typescript
import { useChat, fetchServerSentEvents } from "@tanstack/ai-solid";
import { createSignal } from "solid-js";

export function ChatWithClientTools() {
const [notification, setNotification] = createSignal("");

const { messages, sendMessage } = useChat({
connection: fetchServerSentEvents("/api/chat"),
onToolCall: async ({ toolName, input }) => {
switch (toolName) {
case "updateUI":
// Update SolidJS state
setNotification(input.message);
return { success: true };

case "saveToLocalStorage":
localStorage.setItem(input.key, input.value);
return { saved: true };

default:
throw new Error(`Unknown tool: ${toolName}`);
}
},
});

// ... rest of component
}
```

## Types

All types are re-exported from `@tanstack/ai-client`:

- `UIMessage`
- `MessagePart`
- `TextPart`
- `ThinkingPart`
- `ToolCallPart`
- `ToolResultPart`
- `ChatClientOptions`
- `ConnectionAdapter`
- `ChatRequestBody`

## Next Steps

- [Getting Started](../getting-started/quick-start) - Learn the basics
- [Tools Guide](../guides/tools) - Learn about tools
- [Client Tools](../guides/client-tools) - Learn about client-side tools
41 changes: 35 additions & 6 deletions docs/api/ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const myTool = tool({
// Tool implementation
return { result: "..." };
},
requiresApproval: false, // Optional
needsApproval: false, // Optional
});
```

Expand All @@ -121,15 +121,43 @@ const myTool = tool({
- `description` - Tool description for the model
- `inputSchema` - Zod schema for input validation
- `execute` - Async function to execute the tool
- `requiresApproval?` - Whether tool requires user approval
- `needsApproval?` - Whether tool requires user approval

### Returns

A `Tool` object.

## `toStreamResponse(stream)`
## `toServerSentEventsStream(stream, abortController?)`

Converts a stream to an HTTP Response.
Converts a stream to a ReadableStream in Server-Sent Events format.

```typescript
import { toServerSentEventsStream, chat } from "@tanstack/ai";
import { openai } from "@tanstack/ai-openai";

const stream = chat({
adapter: openai(),
messages: [...],
model: "gpt-4o",
});
const readableStream = toServerSentEventsStream(stream);
```

### Parameters

- `stream` - Async iterable of `StreamChunk`
- `abortController?` - Optional AbortController to abort when stream is cancelled

### Returns

A `ReadableStream<Uint8Array>` in Server-Sent Events format. Each chunk is:
- Prefixed with `"data: "`
- Followed by `"\n\n"`
- Stream ends with `"data: [DONE]\n\n"`

## `toStreamResponse(stream, init?)`

Converts a stream to an HTTP Response with proper SSE headers.

```typescript
import { toStreamResponse, chat } from "@tanstack/ai";
Expand All @@ -146,10 +174,11 @@ return toStreamResponse(stream);
### Parameters

- `stream` - Async iterable of `StreamChunk`
- `init?` - Optional ResponseInit options (including `abortController`)

### Returns

A `Response` object suitable for HTTP endpoints.
A `Response` object suitable for HTTP endpoints with SSE headers (`Content-Type: text/event-stream`, `Cache-Control: no-cache`, `Connection: keep-alive`).

## `maxIterations(count)`

Expand Down Expand Up @@ -228,7 +257,7 @@ interface Tool {
parameters: Record<string, any>;
};
execute?: (args: any) => Promise<any> | any;
requiresApproval?: boolean;
needsApproval?: boolean;
}
```

Expand Down
4 changes: 2 additions & 2 deletions docs/guides/client-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const saveToLocalStorage = tool({
Pass client tools to the chat (they won't execute on the server):

```typescript
import { chat, toStreamResponse } from "@tanstack/ai";
import { chat, toServerSentEventsStream } from "@tanstack/ai";
import { openai } from "@tanstack/ai-openai";
import { updateUI, saveToLocalStorage } from "./tools";

Expand All @@ -54,7 +54,7 @@ export async function POST(request: Request) {
tools: [updateUI, saveToLocalStorage], // Model knows about these tools
});

return toStreamResponse(stream);
return toServerSentEventsStream(stream);
}
```

Expand Down
Loading
Loading