From 93c49a2430712665687b25d1e9cb233ca00644dd Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Tue, 3 Feb 2026 11:38:55 -0800 Subject: [PATCH] feat: use cases --- examples/ai-agent-vercel/README.md | 34 +- examples/ai-agent-vercel/frontend/App.tsx | 256 ++++++-- examples/ai-agent-vercel/frontend/main.tsx | 8 +- examples/ai-agent-vercel/index.html | 444 ++++++++++---- examples/ai-agent-vercel/package.json | 10 +- examples/ai-agent-vercel/src/actors.ts | 223 +++++-- examples/ai-agent-vercel/src/my-tools.ts | 11 - examples/ai-agent-vercel/src/server.ts | 2 + examples/ai-agent-vercel/src/types.ts | 5 - .../ai-agent-vercel/tests/ai-agent.test.ts | 117 ---- examples/ai-agent-vercel/vitest.config.ts | 8 - examples/ai-agent/.gitignore | 2 + examples/ai-agent/README.md | 34 +- examples/ai-agent/frontend/App.tsx | 256 ++++++-- examples/ai-agent/frontend/main.tsx | 8 +- examples/ai-agent/index.html | 444 ++++++++++---- examples/ai-agent/package.json | 96 +-- examples/ai-agent/src/actors.ts | 223 +++++-- examples/ai-agent/src/my-tools.ts | 11 - examples/ai-agent/src/server.ts | 2 + examples/ai-agent/src/types.ts | 5 - examples/ai-agent/tests/ai-agent.test.ts | 117 ---- examples/ai-agent/turbo.json | 7 +- examples/ai-agent/vite.config.ts | 2 +- .../collaborative-document-vercel/README.md | 40 ++ .../api/index.ts | 3 + .../frontend/App.tsx | 496 +++++++++++++++ .../frontend/main.tsx | 2 +- .../collaborative-document-vercel/index.html | 401 +++++++++++++ .../package.json | 45 ++ .../src/actors.ts | 138 +++++ .../src/server.ts | 0 .../tsconfig.json | 27 + .../collaborative-document-vercel/turbo.json | 6 + .../collaborative-document-vercel/vercel.json | 9 + .../vite.config.ts | 6 + examples/collaborative-document/README.md | 35 ++ .../collaborative-document/frontend/App.tsx | 496 +++++++++++++++ .../collaborative-document/frontend/main.tsx | 12 + examples/collaborative-document/index.html | 401 +++++++++++++ examples/collaborative-document/package.json | 49 ++ examples/collaborative-document/src/actors.ts | 138 +++++ examples/collaborative-document/src/server.ts | 6 + examples/collaborative-document/tsconfig.json | 16 + examples/collaborative-document/turbo.json | 4 + .../collaborative-document/vite.config.ts | 7 + .../geo-distributed-database-vercel/README.md | 38 ++ .../api/index.ts | 3 + .../frontend/App.tsx | 362 +++++++++++ .../frontend/main.tsx | 12 + .../index.html | 411 +++++++++++++ .../package.json | 45 ++ .../src/actors.ts | 80 +++ .../src/server.ts | 8 + .../tsconfig.json | 27 + .../turbo.json | 6 + .../vercel.json | 9 + .../vite.config.ts | 6 + examples/geo-distributed-database/README.md | 33 + .../geo-distributed-database/frontend/App.tsx | 362 +++++++++++ .../frontend/main.tsx | 12 + examples/geo-distributed-database/index.html | 411 +++++++++++++ .../package.json | 16 +- .../geo-distributed-database/src/actors.ts | 80 +++ .../geo-distributed-database/src/server.ts | 8 + .../geo-distributed-database/tsconfig.json | 16 + examples/geo-distributed-database/turbo.json | 4 + .../vite.config.ts | 0 examples/multi-region/README.md | 34 -- examples/multi-region/frontend/App.tsx | 260 -------- examples/multi-region/index.html | 134 ----- examples/multi-region/src/actors.ts | 116 ---- examples/multi-region/src/types.ts | 18 - examples/multi-region/tests/regions.test.ts | 181 ------ examples/multi-region/tsconfig.json | 21 - examples/multi-region/turbo.json | 9 - examples/multi-region/vitest.config.ts | 7 - examples/multiplayer-game-vercel/.gitignore | 4 + examples/multiplayer-game-vercel/README.md | 38 ++ examples/multiplayer-game-vercel/api/index.ts | 3 + .../multiplayer-game-vercel/frontend/App.tsx | 349 +++++++++++ .../multiplayer-game-vercel/frontend/main.tsx | 12 + examples/multiplayer-game-vercel/index.html | 261 ++++++++ examples/multiplayer-game-vercel/package.json | 43 ++ .../multiplayer-game-vercel/src/actors.ts | 246 ++++++++ .../multiplayer-game-vercel/src/server.ts | 6 + examples/multiplayer-game-vercel/src/types.ts | 30 + .../multiplayer-game-vercel/tsconfig.json | 27 + examples/multiplayer-game-vercel/turbo.json | 6 + examples/multiplayer-game-vercel/vercel.json | 9 + .../multiplayer-game-vercel/vite.config.ts | 6 + examples/multiplayer-game/.gitignore | 2 + examples/multiplayer-game/README.md | 33 + examples/multiplayer-game/frontend/App.tsx | 349 +++++++++++ examples/multiplayer-game/frontend/main.tsx | 12 + examples/multiplayer-game/index.html | 261 ++++++++ examples/multiplayer-game/package.json | 47 ++ examples/multiplayer-game/src/actors.ts | 246 ++++++++ examples/multiplayer-game/src/server.ts | 6 + examples/multiplayer-game/src/types.ts | 30 + examples/multiplayer-game/tsconfig.json | 16 + examples/multiplayer-game/turbo.json | 4 + examples/multiplayer-game/vite.config.ts | 7 + examples/per-tenant-database/.gitignore | 2 + examples/per-tenant-database/README.md | 33 + examples/per-tenant-database/frontend/App.tsx | 375 ++++++++++++ .../per-tenant-database/frontend/main.tsx | 12 + examples/per-tenant-database/index.html | 340 +++++++++++ examples/per-tenant-database/package.json | 48 ++ examples/per-tenant-database/src/actors.ts | 104 ++++ examples/per-tenant-database/src/server.ts | 6 + .../tests/per-tenant-database.test.ts | 45 ++ examples/per-tenant-database/tsconfig.json | 16 + examples/per-tenant-database/turbo.json | 4 + examples/per-tenant-database/vite.config.ts | 7 + .../vitest.config.ts | 4 +- .../sandbox-coding-agent-vercel/.gitignore | 4 + .../sandbox-coding-agent-vercel/README.md | 46 ++ .../sandbox-coding-agent-vercel/api/index.ts | 3 + .../frontend/App.tsx | 244 ++++++++ .../frontend/main.tsx | 14 + .../sandbox-coding-agent-vercel/index.html | 340 +++++++++++ .../sandbox-coding-agent-vercel/package.json | 46 ++ .../sandbox-coding-agent-vercel/src/actors.ts | 314 ++++++++++ .../sandbox-coding-agent-vercel/src/server.ts | 8 + .../sandbox-coding-agent-vercel/tsconfig.json | 27 + .../sandbox-coding-agent-vercel/turbo.json | 6 + .../sandbox-coding-agent-vercel/vercel.json | 9 + .../vite.config.ts | 6 + examples/sandbox-coding-agent/.gitignore | 2 + examples/sandbox-coding-agent/README.md | 41 ++ .../sandbox-coding-agent/frontend/App.tsx | 244 ++++++++ .../sandbox-coding-agent/frontend/main.tsx | 14 + examples/sandbox-coding-agent/index.html | 340 +++++++++++ examples/sandbox-coding-agent/package.json | 50 ++ examples/sandbox-coding-agent/src/actors.ts | 314 ++++++++++ examples/sandbox-coding-agent/src/server.ts | 8 + examples/sandbox-coding-agent/tsconfig.json | 16 + examples/sandbox-coding-agent/turbo.json | 4 + examples/sandbox-coding-agent/vite.config.ts | 7 + pnpm-lock.yaml | 563 ++++++++++++++++-- .../packages/rivetkit/src/actor/definition.ts | 7 +- .../packages/rivetkit/tsup.config.ts | 15 +- 143 files changed, 11500 insertions(+), 1632 deletions(-) delete mode 100644 examples/ai-agent-vercel/src/my-tools.ts delete mode 100644 examples/ai-agent-vercel/src/types.ts delete mode 100644 examples/ai-agent-vercel/tests/ai-agent.test.ts delete mode 100644 examples/ai-agent-vercel/vitest.config.ts create mode 100644 examples/ai-agent/.gitignore delete mode 100644 examples/ai-agent/src/my-tools.ts delete mode 100644 examples/ai-agent/src/types.ts delete mode 100644 examples/ai-agent/tests/ai-agent.test.ts create mode 100644 examples/collaborative-document-vercel/README.md create mode 100644 examples/collaborative-document-vercel/api/index.ts create mode 100644 examples/collaborative-document-vercel/frontend/App.tsx rename examples/{multi-region => collaborative-document-vercel}/frontend/main.tsx (94%) create mode 100644 examples/collaborative-document-vercel/index.html create mode 100644 examples/collaborative-document-vercel/package.json create mode 100644 examples/collaborative-document-vercel/src/actors.ts rename examples/{multi-region => collaborative-document-vercel}/src/server.ts (100%) create mode 100644 examples/collaborative-document-vercel/tsconfig.json create mode 100644 examples/collaborative-document-vercel/turbo.json create mode 100644 examples/collaborative-document-vercel/vercel.json create mode 100644 examples/collaborative-document-vercel/vite.config.ts create mode 100644 examples/collaborative-document/README.md create mode 100644 examples/collaborative-document/frontend/App.tsx create mode 100644 examples/collaborative-document/frontend/main.tsx create mode 100644 examples/collaborative-document/index.html create mode 100644 examples/collaborative-document/package.json create mode 100644 examples/collaborative-document/src/actors.ts create mode 100644 examples/collaborative-document/src/server.ts create mode 100644 examples/collaborative-document/tsconfig.json create mode 100644 examples/collaborative-document/turbo.json create mode 100644 examples/collaborative-document/vite.config.ts create mode 100644 examples/geo-distributed-database-vercel/README.md create mode 100644 examples/geo-distributed-database-vercel/api/index.ts create mode 100644 examples/geo-distributed-database-vercel/frontend/App.tsx create mode 100644 examples/geo-distributed-database-vercel/frontend/main.tsx create mode 100644 examples/geo-distributed-database-vercel/index.html create mode 100644 examples/geo-distributed-database-vercel/package.json create mode 100644 examples/geo-distributed-database-vercel/src/actors.ts create mode 100644 examples/geo-distributed-database-vercel/src/server.ts create mode 100644 examples/geo-distributed-database-vercel/tsconfig.json create mode 100644 examples/geo-distributed-database-vercel/turbo.json create mode 100644 examples/geo-distributed-database-vercel/vercel.json create mode 100644 examples/geo-distributed-database-vercel/vite.config.ts create mode 100644 examples/geo-distributed-database/README.md create mode 100644 examples/geo-distributed-database/frontend/App.tsx create mode 100644 examples/geo-distributed-database/frontend/main.tsx create mode 100644 examples/geo-distributed-database/index.html rename examples/{multi-region => geo-distributed-database}/package.json (79%) create mode 100644 examples/geo-distributed-database/src/actors.ts create mode 100644 examples/geo-distributed-database/src/server.ts create mode 100644 examples/geo-distributed-database/tsconfig.json create mode 100644 examples/geo-distributed-database/turbo.json rename examples/{multi-region => geo-distributed-database}/vite.config.ts (100%) delete mode 100644 examples/multi-region/README.md delete mode 100644 examples/multi-region/frontend/App.tsx delete mode 100644 examples/multi-region/index.html delete mode 100644 examples/multi-region/src/actors.ts delete mode 100644 examples/multi-region/src/types.ts delete mode 100644 examples/multi-region/tests/regions.test.ts delete mode 100644 examples/multi-region/tsconfig.json delete mode 100644 examples/multi-region/turbo.json delete mode 100644 examples/multi-region/vitest.config.ts create mode 100644 examples/multiplayer-game-vercel/.gitignore create mode 100644 examples/multiplayer-game-vercel/README.md create mode 100644 examples/multiplayer-game-vercel/api/index.ts create mode 100644 examples/multiplayer-game-vercel/frontend/App.tsx create mode 100644 examples/multiplayer-game-vercel/frontend/main.tsx create mode 100644 examples/multiplayer-game-vercel/index.html create mode 100644 examples/multiplayer-game-vercel/package.json create mode 100644 examples/multiplayer-game-vercel/src/actors.ts create mode 100644 examples/multiplayer-game-vercel/src/server.ts create mode 100644 examples/multiplayer-game-vercel/src/types.ts create mode 100644 examples/multiplayer-game-vercel/tsconfig.json create mode 100644 examples/multiplayer-game-vercel/turbo.json create mode 100644 examples/multiplayer-game-vercel/vercel.json create mode 100644 examples/multiplayer-game-vercel/vite.config.ts create mode 100644 examples/multiplayer-game/.gitignore create mode 100644 examples/multiplayer-game/README.md create mode 100644 examples/multiplayer-game/frontend/App.tsx create mode 100644 examples/multiplayer-game/frontend/main.tsx create mode 100644 examples/multiplayer-game/index.html create mode 100644 examples/multiplayer-game/package.json create mode 100644 examples/multiplayer-game/src/actors.ts create mode 100644 examples/multiplayer-game/src/server.ts create mode 100644 examples/multiplayer-game/src/types.ts create mode 100644 examples/multiplayer-game/tsconfig.json create mode 100644 examples/multiplayer-game/turbo.json create mode 100644 examples/multiplayer-game/vite.config.ts create mode 100644 examples/per-tenant-database/.gitignore create mode 100644 examples/per-tenant-database/README.md create mode 100644 examples/per-tenant-database/frontend/App.tsx create mode 100644 examples/per-tenant-database/frontend/main.tsx create mode 100644 examples/per-tenant-database/index.html create mode 100644 examples/per-tenant-database/package.json create mode 100644 examples/per-tenant-database/src/actors.ts create mode 100644 examples/per-tenant-database/src/server.ts create mode 100644 examples/per-tenant-database/tests/per-tenant-database.test.ts create mode 100644 examples/per-tenant-database/tsconfig.json create mode 100644 examples/per-tenant-database/turbo.json create mode 100644 examples/per-tenant-database/vite.config.ts rename examples/{ai-agent => per-tenant-database}/vitest.config.ts (81%) create mode 100644 examples/sandbox-coding-agent-vercel/.gitignore create mode 100644 examples/sandbox-coding-agent-vercel/README.md create mode 100644 examples/sandbox-coding-agent-vercel/api/index.ts create mode 100644 examples/sandbox-coding-agent-vercel/frontend/App.tsx create mode 100644 examples/sandbox-coding-agent-vercel/frontend/main.tsx create mode 100644 examples/sandbox-coding-agent-vercel/index.html create mode 100644 examples/sandbox-coding-agent-vercel/package.json create mode 100644 examples/sandbox-coding-agent-vercel/src/actors.ts create mode 100644 examples/sandbox-coding-agent-vercel/src/server.ts create mode 100644 examples/sandbox-coding-agent-vercel/tsconfig.json create mode 100644 examples/sandbox-coding-agent-vercel/turbo.json create mode 100644 examples/sandbox-coding-agent-vercel/vercel.json create mode 100644 examples/sandbox-coding-agent-vercel/vite.config.ts create mode 100644 examples/sandbox-coding-agent/.gitignore create mode 100644 examples/sandbox-coding-agent/README.md create mode 100644 examples/sandbox-coding-agent/frontend/App.tsx create mode 100644 examples/sandbox-coding-agent/frontend/main.tsx create mode 100644 examples/sandbox-coding-agent/index.html create mode 100644 examples/sandbox-coding-agent/package.json create mode 100644 examples/sandbox-coding-agent/src/actors.ts create mode 100644 examples/sandbox-coding-agent/src/server.ts create mode 100644 examples/sandbox-coding-agent/tsconfig.json create mode 100644 examples/sandbox-coding-agent/turbo.json create mode 100644 examples/sandbox-coding-agent/vite.config.ts diff --git a/examples/ai-agent-vercel/README.md b/examples/ai-agent-vercel/README.md index 041431340a..20e4b758c0 100644 --- a/examples/ai-agent-vercel/README.md +++ b/examples/ai-agent-vercel/README.md @@ -3,42 +3,42 @@ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Frivet-gg%2Frivet%2Ftree%2Fmain%2Fexamples%2Fai-agent-vercel&project-name=ai-agent-vercel) -# AI Agent Chat +# AI Agent -Example project demonstrating AI agent integration. +Example project demonstrating queue-driven Rivet Actor AI agents with streaming Vercel AI SDK responses. ## Getting Started ```sh git clone https://github.com/rivet-dev/rivet.git cd rivet/examples/ai-agent -npm install -npm run dev +pnpm install +pnpm dev ``` - ## Features -- **AI SDK integration**: Use Vercel AI SDK with OpenAI within Rivet Actors -- **Persistent conversation state**: Message history automatically persisted across actor restarts -- **Real-time updates**: Broadcast AI responses to connected clients using actor events -- **Tool calling**: Integrate custom tools (weather lookup) that AI can invoke +- Actor-per-agent pattern with a coordinating manager Rivet Actor +- Queue-based intake using `c.queue.next` inside the run loop +- Streaming AI responses sent to the UI as they arrive +- Persistent history stored in Rivet Actor state +- Live status updates via events and polling -## Implementation +## Prerequisites -The AI agent is implemented as a Rivet Actor that maintains conversation state and integrates with OpenAI. Key implementation details: +- OpenAI API key set as `OPENAI_API_KEY` -- **Actor Definition** ([`src/backend/registry.ts`](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent/src/backend/registry.ts)): Defines the `aiAgent` actor with persistent message history -- **Custom Tools** ([`src/backend/my-tools.ts`](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent/src/backend/my-tools.ts)): Implements the weather lookup tool that the AI can invoke -- **Message Types** ([`src/backend/types.ts`](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent/src/backend/types.ts)): TypeScript types for message structure +## Implementation -## Prerequisites +The AgentManager creates and tracks agent actors, while each AI agent Rivet Actor consumes queue messages in `run` and streams responses with the Vercel AI SDK. -- OpenAI API Key (set as `OPENAI_API_KEY` environment variable) +- **Actor definitions and queues** ([`src/actors.ts`](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent/src/actors.ts)) +- **Frontend orchestration** ([`frontend/App.tsx`](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent/frontend/App.tsx)) +- **Server entry point** ([`src/server.ts`](https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent/src/server.ts)) ## Resources -Read more about [actions](/docs/actors/actions), [state](/docs/actors/state), and [events](/docs/actors/events). +Read more about [queues](https://rivet.dev/docs/actors/queues), [run handlers](https://rivet.dev/docs/actors/run), [state](https://rivet.dev/docs/actors/state), and [events](https://rivet.dev/docs/actors/events). ## License diff --git a/examples/ai-agent-vercel/frontend/App.tsx b/examples/ai-agent-vercel/frontend/App.tsx index cebc26c97f..900945b812 100644 --- a/examples/ai-agent-vercel/frontend/App.tsx +++ b/examples/ai-agent-vercel/frontend/App.tsx @@ -1,80 +1,244 @@ import { createRivetKit } from "@rivetkit/react"; import { useEffect, useState } from "react"; -import { registry } from "../src/actors.ts"; -import type { Message } from "../src/types.ts"; +import type { + AgentInfo, + AgentMessage, + AgentStatus, + registry, +} from "../src/actors.ts"; -const { useActor } = createRivetKit(`${window.location.origin}/api/rivet`); +const { useActor } = createRivetKit( + `${location.origin}/api/rivet`, +); -export function App() { - const aiAgent = useActor({ - name: "aiAgent", - key: ["default"], +type ResponseEvent = { + messageId: string; + delta: string; + content: string; + done: boolean; + error?: string; +}; + +function formatTime(timestamp: number) { + return new Date(timestamp).toLocaleTimeString(); +} + +function AgentPanel({ info }: { info: AgentInfo }) { + const agent = useActor({ + name: "agent", + key: [info.id], }); - const [messages, setMessages] = useState([]); + const [messages, setMessages] = useState([]); + const [status, setStatus] = useState(null); const [input, setInput] = useState(""); - const [isLoading, setIsLoading] = useState(false); useEffect(() => { - if (aiAgent.connection) { - aiAgent.connection.getMessages().then(setMessages); + if (!agent.connection) { + return; } - }, [aiAgent.connection]); - aiAgent.useEvent("messageReceived", (message: Message) => { - setMessages((prev) => [...prev, message]); - setIsLoading(false); + agent.connection.getHistory().then(setMessages); + agent.connection.getStatus().then(setStatus); + }, [agent.connection]); + + agent.useEvent("messageAdded", (message: AgentMessage) => { + setMessages((prev) => { + const existingIndex = prev.findIndex((item) => item.id === message.id); + if (existingIndex !== -1) { + const next = [...prev]; + next[existingIndex] = message; + return next; + } + return [...prev, message].sort( + (a, b) => a.createdAt - b.createdAt, + ); + }); + }); + + agent.useEvent("response", (payload: ResponseEvent) => { + setMessages((prev) => + prev.map((message) => + message.id === payload.messageId + ? { ...message, content: payload.content } + : message, + ), + ); + }); + + agent.useEvent("status", (nextStatus: AgentStatus) => { + setStatus(nextStatus); }); - const handleSendMessage = async () => { - if (aiAgent.connection && input.trim()) { - setIsLoading(true); + const sendMessage = async () => { + if (!agent.connection) { + return; + } - const userMessage = { role: "user", content: input, timestamp: Date.now() } as Message; - setMessages((prev) => [...prev, userMessage]); + const trimmed = input.trim(); + if (!trimmed) { + return; + } - await aiAgent.connection.sendMessage(input); - setInput(""); + await agent.connection.queue.message.send({ + text: trimmed, + sender: "Operator", + }); + setInput(""); + }; + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + sendMessage(); } }; return ( -
-
+
+
+
+

{info.name}

+

{info.id}

+
+
+ + + {status?.state ?? "idle"} + +
+
+ +
{messages.length === 0 ? ( -
- Ask the AI assistant a question to get started -
+

+ Send a message to wake this Rivet Actor. +

) : ( - messages.map((msg, i) => ( -
-
{msg.role === "user" ? "👤" : "🤖"}
-
{msg.content}
-
+ messages.map((message) => ( +
+
+ {message.sender} + + {formatTime(message.createdAt)} + +
+

{message.content}

+
)) )} - {isLoading && ( -
-
🤖
-
Thinking...
-
- )}
-
- +