diff --git a/docs/content/docs/openui-lang/examples/harnesses/vercel-eve.mdx b/docs/content/docs/openui-lang/examples/harnesses/vercel-eve.mdx index 1f7875e1d..b5f9d28fc 100644 --- a/docs/content/docs/openui-lang/examples/harnesses/vercel-eve.mdx +++ b/docs/content/docs/openui-lang/examples/harnesses/vercel-eve.mdx @@ -9,23 +9,32 @@ The agent receives the OpenUI component-library prompt when a session starts, so [View source on GitHub →](https://github.com/thesysdev/openui/tree/main/examples/harnesses/vercel-eve) -## Architecture - -```text -browser ── Eve session HTTP ──▶ Eve agent ── model + tools - ▲ │ - └── AG-UI SSE ── event adapter ─┘ - rendered by -``` - -The integration has four main pieces: - -| Piece | File | Role | -| --- | --- | --- | -| OpenUI chat | `src/app/page.tsx` | Renders `` with `agUIAdapter()` and the built-in OpenUI component library. | -| Session bridge | `src/eve-chat.ts` | Delivers turns through Eve's HTTP API, follows its resumable event stream, and returns AG-UI SSE to OpenUI. | -| Event adapter | `src/eve-stream.ts` | Maps Eve text, tool-call, and failure events to AG-UI events. | -| Agent instructions | `agent/instructions/openui.ts` | Adds the generated OpenUI system prompt when each Eve session starts. | +
+
+ +## How it connects + +| Piece | File | Role | +| ------------------ | ------------------------------ | ----------------------------------------------------------------------------------------------------------- | +| OpenUI chat | `src/app/page.tsx` | Renders `` with `agUIAdapter()` and the built-in OpenUI component library. | +| Session bridge | `src/eve-chat.ts` | Delivers turns through Eve's HTTP API, follows its resumable event stream, and returns AG-UI SSE to OpenUI. | +| Event adapter | `src/eve-stream.ts` | Maps Eve text, tool-call, and failure events to AG-UI events. | +| Agent instructions | `agent/instructions/openui.ts` | Adds the generated OpenUI system prompt when each Eve session starts. | + +The Next.js app and Eve development server start together through `withEve()`. The browser uses +Eve's same-origin session endpoints directly, so the integration does not need a custom backend +route or CORS configuration. OpenUI threads retain Eve's session ID, continuation token, and +stream cursor, preserving multi-turn context and resumable delivery. ## Connecting OpenUI @@ -69,7 +78,34 @@ export default defineDynamic({ This keeps the model's instructions synchronized with the exact component library used by the renderer. -## Translating Eve events +## The session bridge + +The browser talks to the same-origin Eve endpoints installed by `withEve()`: + +```text +POST /eve/v1/session +POST /eve/v1/session/:id +GET /eve/v1/session/:id/stream?startIndex=N +``` + +The first turn creates a session. Follow-up turns send the saved continuation token, and the +stream request resumes from the saved event index. Completed sessions clear the cursor; waiting +and failed sessions remain resumable. + +```ts +const delivered = await fetch( + state.sessionId ? `/eve/v1/session/${state.sessionId}` : "/eve/v1/session", + { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ message, continuationToken: state.continuationToken }), + }, +); + +const streamed = await fetch(`/eve/v1/session/${sessionId}/stream?startIndex=${state.streamIndex}`); +``` + +## Tool calls and streamed UI Eve emits typed session events. The adapter in `src/eve-stream.ts` converts the events OpenUI needs: @@ -83,17 +119,17 @@ session.failed -> RUN_ERROR Tool calls and text share one assistant message ID. OpenUI renders tool activity in its behind-the-scenes section and the final OpenUI Lang response in the conversation. -## Eve session protocol +The included `get_current_time` tool is a minimal example. Additional Eve tools automatically +surface through the same AG-UI tool-call mapping. -The browser talks to the same-origin Eve endpoints installed by `withEve()`: +## Thread persistence -```text -POST /eve/v1/session -POST /eve/v1/session/:id -GET /eve/v1/session/:id/stream?startIndex=N -``` +`src/thread-store.ts` keeps OpenUI thread metadata and transcripts in browser `localStorage`. +`src/eve-chat.ts` stores the Eve session ID, continuation token, and stream index under the same +thread ID. Reopening a thread therefore restores both the visible transcript and its server-side +Eve conversation. -For a follow-up turn, the bridge sends the saved continuation token and resumes from the saved stream index. A completed session clears the cursor; waiting and failed sessions remain resumable. +## Authentication and security The demo channel uses anonymous authentication for local development. Replace `none()` in `agent/channels/eve.ts` with an authenticated Eve channel before exposing the application. @@ -114,16 +150,25 @@ examples/harnesses/vercel-eve/ ## Run the example -Eve 0.11 requires Node.js 24. From the repository root: +Eve 0.11 requires Node.js 24. From the repository root, follow these steps: ```bash +# 1. Install workspace dependencies. pnpm install +# 2. Enter the example. cd examples/harnesses/vercel-eve -LLM_API_KEY=your-api-key pnpm dev + +# 3. Configure an OpenAI-compatible model provider. +cp .env.example .env +# Edit .env and set LLM_API_KEY. + +# 4. Start Next.js and the embedded Eve development server. +pnpm dev ``` -By default the agent uses OpenAI. Configure `LLM_MODEL` and `LLM_BASE_URL` to use another OpenAI-compatible endpoint. Open [http://localhost:3000](http://localhost:3000) and start a conversation. +Open [http://localhost:3000](http://localhost:3000) and start a conversation. `OPENAI_API_KEY`, +`OPENAI_MODEL`, and `OPENAI_BASE_URL` are accepted as aliases for the `LLM_*` variables. The repository scripts also expose Eve directly when you need to build or run the agent separately: diff --git a/docs/public/videos/vercel-eve.mp4 b/docs/public/videos/vercel-eve.mp4 new file mode 100644 index 000000000..5870f1f4f Binary files /dev/null and b/docs/public/videos/vercel-eve.mp4 differ diff --git a/examples/harnesses/vercel-eve/.env.example b/examples/harnesses/vercel-eve/.env.example new file mode 100644 index 000000000..90bc57b2e --- /dev/null +++ b/examples/harnesses/vercel-eve/.env.example @@ -0,0 +1,3 @@ +LLM_API_KEY=your-api-key +LLM_MODEL=gpt-5.5 +LLM_BASE_URL=https://api.openai.com/v1 diff --git a/examples/harnesses/vercel-eve/.gitignore b/examples/harnesses/vercel-eve/.gitignore index 3af2cf2c9..5c7a54e2a 100644 --- a/examples/harnesses/vercel-eve/.gitignore +++ b/examples/harnesses/vercel-eve/.gitignore @@ -1,5 +1,6 @@ node_modules .env* +!.env.example .eve .vercel .workflow-data @@ -12,4 +13,3 @@ dist # Local-only live integration smoke test (needs a running agent + API key) scripts/verify-thread-context.mjs - diff --git a/examples/harnesses/vercel-eve/README.md b/examples/harnesses/vercel-eve/README.md new file mode 100644 index 000000000..25174b94f --- /dev/null +++ b/examples/harnesses/vercel-eve/README.md @@ -0,0 +1,113 @@ +# OpenUI + Vercel Eve Harness + +A generative-UI chat application backed by a [Vercel Eve](https://github.com/vercel/eve) +agent. Eve keeps its native session and resumable-streaming protocol, while a small adapter +translates Eve events into AG-UI events that OpenUI renders as live React components. + +## Prerequisites + +- Node.js 24 +- pnpm 9 +- An API key for an OpenAI-compatible model provider + +## Run locally + +1. Install the monorepo dependencies from the repository root: + + ```bash + pnpm install + ``` + +2. Enter the example directory: + + ```bash + cd examples/harnesses/vercel-eve + ``` + +3. Copy the example environment file and add your provider configuration: + + ```bash + cp .env.example .env + ``` + + Set `LLM_API_KEY` in `.env`. `LLM_MODEL` and `LLM_BASE_URL` select the model and any + OpenAI-compatible endpoint. `OPENAI_API_KEY`, `OPENAI_MODEL`, and `OPENAI_BASE_URL` are + accepted as aliases. + +4. Start the Next.js application and embedded Eve development server: + + ```bash + pnpm dev + ``` + +5. Open [http://localhost:3000](http://localhost:3000) and start a conversation. + +## How it works + +```text +Browser / OpenUI FullScreen + │ + ├─ POST /eve/v1/session or /eve/v1/session/:id + ├─ GET /eve/v1/session/:id/stream?startIndex=N + │ + ▼ +Eve HTTP channel ──► Eve agent ──► model + tools + │ + ▼ +Eve session events ──► AG-UI adapter ──► OpenUI renderer +``` + +- `src/app/page.tsx` renders OpenUI's `` chat with the built-in component library. +- `src/eve-chat.ts` delivers turns through Eve's HTTP session protocol and persists session + cursors per OpenUI thread. +- `src/eve-stream.ts` converts Eve text, tool-call, and failure events into AG-UI events. +- `agent/instructions/openui.ts` injects the generated OpenUI Lang prompt when an Eve session + starts. +- `src/thread-store.ts` stores thread metadata, transcripts, continuation tokens, and stream + positions in browser `localStorage`. + +## Configuration + +| Environment variable | Default | Purpose | +| -------------------- | ------------------------------------------------ | ---------------------------------------------- | +| `LLM_API_KEY` | `OPENAI_API_KEY` | API key sent to the configured model provider. | +| `LLM_MODEL` | `OPENAI_MODEL` or `gpt-5.5` | Model used by the Eve agent. | +| `LLM_BASE_URL` | `OPENAI_BASE_URL` or `https://api.openai.com/v1` | OpenAI-compatible API endpoint. | + +## Eve commands + +The normal development command is `pnpm dev`. The package also exposes Eve directly: + +```bash +pnpm eve:dev +pnpm eve:build +pnpm eve:start +``` + +For a production-style Next.js run: + +```bash +pnpm build +pnpm start +``` + +## Project layout + +```text +examples/harnesses/vercel-eve/ +|- agent/agent.ts # Eve model and build configuration +|- agent/channels/eve.ts # Eve HTTP session channel +|- agent/instructions/openui.ts # Generated OpenUI Lang instructions +|- agent/tools/get_current_time.ts # Example Eve tool +|- src/app/page.tsx # OpenUI FullScreen chat +|- src/eve-chat.ts # Eve session transport and persistence +|- src/eve-stream.ts # Eve-to-AG-UI event mapping +|- src/thread-store.ts # Browser thread and transcript storage +|- next.config.ts # Installs Eve through withEve() +``` + +## Security + +The example uses Eve's `none()` channel authentication for local development. Do not expose it +publicly in that form. Configure an authenticated Eve channel, restrict network access, and apply +the provider and tool permissions appropriate for your deployment.