Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# ── Model (used by the LangGraph server) ──────────────────────────
# The graph runs the LLM, so the API key lives with the LangGraph
# process, not the Next.js app.
# ── Model (used by the LangGraph/deepagents server) ────────────────
# The deepagent graph runs the LLM, so the API key lives with the
# LangGraph process, not the Next.js app.
OPENAI_API_KEY=sk-your-key-here
OPENAI_MODEL=gpt-5.5

# ── LangGraph connection (used by the Next.js /api/chat proxy) ─────
# ── LangGraph protocol connection (used by /api/chat) ──────────────
# Local: the URL printed by `pnpm langgraph:dev` (defaults to 2024).
LANGGRAPH_API_URL=http://localhost:2024
# The graph name from langgraph.json ("agent"), or a deployed
# assistant id.
# The graph name from langgraph.json ("agent"), or a deployed assistant id.
LANGGRAPH_ASSISTANT_ID=agent

# ── LangGraph Cloud / Platform (only for a hosted deployment) ──────
Expand Down
File renamed without changes.
102 changes: 102 additions & 0 deletions examples/langchain-chat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# OpenUI + DeepAgents Chat

A generative-UI chat app where the responses are produced by a
[deepagents](https://www.npmjs.com/package/deepagents) agent and rendered live
with [OpenUI](https://openui.com).

The agent has mock **weather**, **finance**, and **research** tools. Its
LangGraph protocol stream is transformed locally into AG-UI events on a custom
`openui` channel, so the browser can use OpenUI's default stream adapter.

```
browser ──fetch /api/chat──▶ Next.js route ──protocol v2──▶ LangGraph server
▲ │ (deepagent + tools
└────── SSE (AG-UI) ◀──────────┘ + custom:openui)
parsed by default adapter
```

## How it connects

| Piece | File | Role |
| --- | --- | --- |
| Frontend | `src/app/page.tsx` | `<FullScreen apiUrl="/api/chat">` using the default AG-UI stream protocol. |
| Proxy | `src/app/api/chat/route.ts` | Converts AG-UI messages to LangChain messages, starts a protocol-v2 run, and relays `custom:openui` as AG-UI SSE. Keeps the API key + deployment URL server-side. |
| Graph | `src/agent/agent.ts` | `createDeepAgent` with the generated OpenUI system prompt, the mock tools, and the local stream transformer. |
| Transformer | `src/agent/openui-transformer.ts` | Maps root LangGraph protocol `messages` events into AG-UI events and emits them as `custom:openui`. |
| Stream helper | `src/lib/stream-openui.ts` | Talks to `/threads/:id/stream/events` and `/threads/:id/commands` using raw fetch. |
| Tools | `src/agent/tools.ts` | Mock `get_weather` / `get_stock_price` / `search_web` (no external keys needed). |
| Component library | `src/library.ts` | The OpenUI components the model is allowed to render. `pnpm generate:prompt` turns it into `src/generated/system-prompt.txt`. |

The transformer is intentionally local to this example for now. It mimics the
LangGraph protocol types instead of adding new toolkit dependencies, making it
easy to extract later if the OpenUI team wants to publish a shared integration.

## Getting started (local)

This example runs **two processes**: the LangGraph server (runs the model) and
the Next.js app (serves the UI). The default dev script starts both.

1. Create a `.env` from the template:

```bash
cp .env.example .env
```

```env
OPENAI_API_KEY=sk-your-key-here
OPENAI_MODEL=gpt-5.5
LANGGRAPH_API_URL=http://localhost:2024
LANGGRAPH_ASSISTANT_ID=agent
```

2. Start the LangGraph server and Next.js app together:

```bash
pnpm dev
```

This generates the OpenUI prompt once, starts the LangGraph server on
`:2024`, and starts Next.js on `:3000`.

If you prefer separate terminals, run:

```bash
pnpm langgraph:dev
# in another terminal
pnpm exec next dev
```

Open [http://localhost:3000](http://localhost:3000) and try a starter such as
"Weather in Tokyo" or "AAPL stock price".

> `OPENAI_API_KEY` is read by the **LangGraph server** (it runs the LLM), so it
> belongs in `.env` next to `langgraph.json`. The Next.js app only needs the
> `LANGGRAPH_*` variables.

## Using LangGraph Cloud / Platform

Deploy the graph (this folder already has a `langgraph.json`) and point the app
at the deployment instead of localhost — no app code changes:

```env
LANGGRAPH_API_URL=https://your-deployment.us.langgraph.app
LANGGRAPH_ASSISTANT_ID=agent # graph name, or a created assistant id
LANGSMITH_API_KEY=lsv2-... # auth for the deployment
```

`LANGSMITH_API_KEY` is sent as `x-api-key` from the server side only.
Restart `pnpm dev` after changing `.env`.

## Customizing

- **Change agent behavior:** update the deepagent prompt or tool list in
`src/agent/agent.ts`.
- **Use real tools:** replace the mock bodies in `src/agent/tools.ts` with real
API calls.
- **Change what the model can render:** edit `src/library.ts`, then re-run
`pnpm generate:prompt` (the dev scripts do this for you).

## Learn more

- [OpenUI docs](https://openui.com/docs) and the [LangGraph provider guide](https://www.openui.com/docs/chat/providers)
- [DeepAgents](https://www.npmjs.com/package/deepagents) and [LangGraph.js docs](https://langchain-ai.github.io/langgraphjs/)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"node_version": "20",
"dependencies": ["."],
"graphs": {
"agent": "./src/agent/graph.ts:graph"
"agent": "./src/agent/agent.ts:graph"
},
"env": ".env"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,27 @@
"private": true,
"scripts": {
"generate:prompt": "pnpm --filter @openuidev/cli build && pnpm exec openui generate src/library.ts --out src/generated/system-prompt.txt",
"langgraph:dev": "pnpm generate:prompt && langgraphjs dev",
"dev": "pnpm generate:prompt && next dev",
"dev:langgraph": "langgraphjs dev",
"dev:next": "next dev",
"dev:serve": "run-p dev:langgraph dev:next",
"langgraph:dev": "run-s generate:prompt dev:langgraph",
"dev": "run-s generate:prompt dev:serve",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"@langchain/core": "^1.1.48",
"@langchain/langgraph": "^1.3.6",
"@ag-ui/core": "^0.0.57",
"@langchain/core": "^1.2.1",
"@langchain/langgraph": "^1.4.5",
"@langchain/langgraph-sdk": "^1.9.18",
"@langchain/openai": "^1.4.7",
"@langchain/openai": "^1.5.2",
"@langchain/protocol": "^0.0.18",
"@openuidev/react-headless": "workspace:*",
"@openuidev/react-lang": "workspace:*",
"@openuidev/react-ui": "workspace:*",
"deepagents": "^1.10.5",
"langchain": "^1.5.1",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3",
Expand All @@ -32,6 +39,7 @@
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.1.6",
"npm-run-all2": "^9.0.2",
"tailwindcss": "^4",
"typescript": "^5"
}
Expand Down
49 changes: 49 additions & 0 deletions examples/langchain-chat/src/agent/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { existsSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

import { createDeepAgent } from "deepagents";

import { openUIStreamTransformer } from "./openui-transformer";
import { getStockPrice, getWeather, searchWeb } from "./tools";

/**
* The OpenUI system prompt is generated from `src/library.ts` by the OpenUI
* CLI (`pnpm generate:prompt`). It teaches the model to answer in OpenUI Lang
* so the renderer can turn each reply into live React components.
*
* It is loaded with `readFileSync` rather than a `with { type: "text" }`
* import attribute because the LangGraph dev server's CJS/tsx loader does not
* support text import attributes (it tries to evaluate the `.txt` as JS).
*/
function loadSystemPrompt(): string {
const candidates = [
// Primary: relative to the working dir (how `langgraphjs dev` runs).
join(process.cwd(), "src/generated/system-prompt.txt"),
// Fallback: relative to this module, for runners whose cwd differs.
join(dirname(fileURLToPath(import.meta.url)), "../generated/system-prompt.txt"),
];
for (const path of candidates) {
if (existsSync(path)) return readFileSync(path, "utf-8");
}
throw new Error(
"OpenUI system prompt not found. Run `pnpm generate:prompt` before starting the graph.",
);
}

const OPENUI_SYSTEM_PROMPT = loadSystemPrompt();

const MODEL = process.env.OPENAI_MODEL || "gpt-5.5";

const SYSTEM_PROMPT = [
OPENUI_SYSTEM_PROMPT,
"You are an OpenUI assistant with weather, finance, and research tools.",
"Use the tools when they help answer the user's request, then answer only in OpenUI Lang.",
].join("\n\n");

export const graph = createDeepAgent({
model: `openai:${MODEL}`,
tools: [getWeather, getStockPrice, searchWeb],
systemPrompt: SYSTEM_PROMPT,
streamTransformers: [openUIStreamTransformer],
});
Loading