@alexanderolsen/create-deepagent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +661 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/registry/frameworks/deno/.env.example +6 -0
- package/registry/frameworks/deno/README.md +137 -0
- package/registry/frameworks/deno/client/index.html +23 -0
- package/registry/frameworks/deno/client/package.json +30 -0
- package/registry/frameworks/deno/client/public/favicon.ico +0 -0
- package/registry/frameworks/deno/client/src/components/Chat.tsx +124 -0
- package/registry/frameworks/deno/client/src/components/ChatApp.tsx +129 -0
- package/registry/frameworks/deno/client/src/components/Conversation.tsx +91 -0
- package/registry/frameworks/deno/client/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/deno/client/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/deno/client/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/deno/client/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/deno/client/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/deno/client/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/deno/client/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/deno/client/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/deno/client/src/lib/agent-type.ts +4 -0
- package/registry/frameworks/deno/client/src/lib/chat/threads-client.ts +51 -0
- package/registry/frameworks/deno/client/src/main.tsx +11 -0
- package/registry/frameworks/deno/client/src/styles/globals.css +714 -0
- package/registry/frameworks/deno/client/src/vite-env.d.ts +1 -0
- package/registry/frameworks/deno/client/tsconfig.app.json +7 -0
- package/registry/frameworks/deno/client/tsconfig.json +24 -0
- package/registry/frameworks/deno/client/tsconfig.node.json +19 -0
- package/registry/frameworks/deno/client/vite.config.ts +24 -0
- package/registry/frameworks/deno/deno.json +16 -0
- package/registry/frameworks/deno/main.ts +23 -0
- package/registry/frameworks/deno/package.json +14 -0
- package/registry/frameworks/deno/server/agent/index.ts +60 -0
- package/registry/frameworks/deno/server/agent/middleware.ts +24 -0
- package/registry/frameworks/deno/server/agent/tools.ts +64 -0
- package/registry/frameworks/deno/server/registry.ts +40 -0
- package/registry/frameworks/deno/server/routes.ts +114 -0
- package/registry/frameworks/deno/server/serialize.ts +30 -0
- package/registry/frameworks/deno/server/session.ts +210 -0
- package/registry/frameworks/deno/server/threads.ts +404 -0
- package/registry/frameworks/deno.ts +17 -0
- package/registry/frameworks/hono/.env.example +6 -0
- package/registry/frameworks/hono/README.md +186 -0
- package/registry/frameworks/hono/index.html +22 -0
- package/registry/frameworks/hono/package.json +42 -0
- package/registry/frameworks/hono/src/components/Chat.tsx +124 -0
- package/registry/frameworks/hono/src/components/ChatApp.tsx +129 -0
- package/registry/frameworks/hono/src/components/Conversation.tsx +90 -0
- package/registry/frameworks/hono/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/hono/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/hono/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/hono/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/hono/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/hono/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/hono/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/hono/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/hono/src/lib/agent/types.ts +4 -0
- package/registry/frameworks/hono/src/lib/chat/threads-client.ts +57 -0
- package/registry/frameworks/hono/src/main.tsx +11 -0
- package/registry/frameworks/hono/src/styles/globals.css +714 -0
- package/registry/frameworks/hono/src/vite-env.d.ts +1 -0
- package/registry/frameworks/hono/tsconfig.app.json +22 -0
- package/registry/frameworks/hono/tsconfig.json +7 -0
- package/registry/frameworks/hono/tsconfig.worker.json +18 -0
- package/registry/frameworks/hono/vite.config.ts +16 -0
- package/registry/frameworks/hono/worker/agent/index.ts +53 -0
- package/registry/frameworks/hono/worker/agent/middleware.ts +20 -0
- package/registry/frameworks/hono/worker/agent/tools.ts +55 -0
- package/registry/frameworks/hono/worker/durable-objects/thread-session.ts +159 -0
- package/registry/frameworks/hono/worker/env.d.ts +17 -0
- package/registry/frameworks/hono/worker/index.ts +140 -0
- package/registry/frameworks/hono/worker/server/registry.ts +39 -0
- package/registry/frameworks/hono/worker/server/runs.ts +82 -0
- package/registry/frameworks/hono/worker/server/serialize.ts +30 -0
- package/registry/frameworks/hono/worker/server/threads.ts +404 -0
- package/registry/frameworks/hono/worker/tsconfig.json +4 -0
- package/registry/frameworks/hono/wrangler.jsonc +28 -0
- package/registry/frameworks/hono.ts +35 -0
- package/registry/frameworks/next/.env.example +6 -0
- package/registry/frameworks/next/README.md +173 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/commands/route.ts +21 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/history/route.ts +35 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/route.ts +13 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/state/route.ts +51 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/stream/route.ts +30 -0
- package/registry/frameworks/next/app/api/threads/route.ts +11 -0
- package/registry/frameworks/next/app/favicon.ico +0 -0
- package/registry/frameworks/next/app/globals.css +712 -0
- package/registry/frameworks/next/app/layout.tsx +34 -0
- package/registry/frameworks/next/app/page.tsx +5 -0
- package/registry/frameworks/next/components/Chat.tsx +124 -0
- package/registry/frameworks/next/components/ChatApp.tsx +129 -0
- package/registry/frameworks/next/components/Conversation.tsx +90 -0
- package/registry/frameworks/next/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/next/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/next/components/MessageThread.tsx +135 -0
- package/registry/frameworks/next/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/next/components/Subagents.tsx +120 -0
- package/registry/frameworks/next/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/next/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/next/components/ToolCall.tsx +89 -0
- package/registry/frameworks/next/eslint.config.mjs +18 -0
- package/registry/frameworks/next/lib/agent/index.ts +95 -0
- package/registry/frameworks/next/lib/agent/middleware.ts +40 -0
- package/registry/frameworks/next/lib/agent/tools.ts +66 -0
- package/registry/frameworks/next/lib/chat/threads-client.ts +57 -0
- package/registry/frameworks/next/lib/server/registry.ts +57 -0
- package/registry/frameworks/next/lib/server/serialize.ts +32 -0
- package/registry/frameworks/next/lib/server/session.ts +212 -0
- package/registry/frameworks/next/lib/server/threads.ts +406 -0
- package/registry/frameworks/next/next.config.ts +7 -0
- package/registry/frameworks/next/package.json +37 -0
- package/registry/frameworks/next/postcss.config.mjs +7 -0
- package/registry/frameworks/next/public/file.svg +1 -0
- package/registry/frameworks/next/public/globe.svg +1 -0
- package/registry/frameworks/next/public/next.svg +1 -0
- package/registry/frameworks/next/public/vercel.svg +1 -0
- package/registry/frameworks/next/public/window.svg +1 -0
- package/registry/frameworks/next/tsconfig.json +34 -0
- package/registry/frameworks/next.ts +17 -0
- package/registry/frameworks/nuxt/.env.example +3 -0
- package/registry/frameworks/nuxt/README.md +133 -0
- package/registry/frameworks/nuxt/app/app.vue +26 -0
- package/registry/frameworks/nuxt/app/assets/css/main.css +707 -0
- package/registry/frameworks/nuxt/app/components/Chat.vue +105 -0
- package/registry/frameworks/nuxt/app/components/ChatApp.vue +89 -0
- package/registry/frameworks/nuxt/app/components/ChatThread.vue +27 -0
- package/registry/frameworks/nuxt/app/components/MessageBubble.vue +60 -0
- package/registry/frameworks/nuxt/app/components/MessageBubbles.vue +213 -0
- package/registry/frameworks/nuxt/app/components/MessageList.vue +51 -0
- package/registry/frameworks/nuxt/app/components/MessageReasoning.vue +53 -0
- package/registry/frameworks/nuxt/app/components/StreamingIndicator.vue +9 -0
- package/registry/frameworks/nuxt/app/components/SubagentDetail.vue +51 -0
- package/registry/frameworks/nuxt/app/components/SubagentList.vue +49 -0
- package/registry/frameworks/nuxt/app/components/ThemeToggle.vue +43 -0
- package/registry/frameworks/nuxt/app/components/ThreadHistory.vue +65 -0
- package/registry/frameworks/nuxt/app/components/ToolCall.vue +81 -0
- package/registry/frameworks/nuxt/app/components/TypingDots.vue +14 -0
- package/registry/frameworks/nuxt/app/composables/useTheme.ts +14 -0
- package/registry/frameworks/nuxt/app/utils/streaming.ts +44 -0
- package/registry/frameworks/nuxt/app/utils/threads.ts +57 -0
- package/registry/frameworks/nuxt/nuxt.config.ts +6 -0
- package/registry/frameworks/nuxt/package.json +28 -0
- package/registry/frameworks/nuxt/public/favicon.ico +0 -0
- package/registry/frameworks/nuxt/public/robots.txt +2 -0
- package/registry/frameworks/nuxt/server/agent/index.ts +89 -0
- package/registry/frameworks/nuxt/server/agent/middleware.ts +38 -0
- package/registry/frameworks/nuxt/server/agent/tools.ts +66 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/commands.post.ts +16 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/history.post.ts +37 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/index.delete.ts +12 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.get.ts +22 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.post.ts +32 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/stream.post.ts +24 -0
- package/registry/frameworks/nuxt/server/api/threads/index.get.ts +13 -0
- package/registry/frameworks/nuxt/server/utils/runtime.ts +42 -0
- package/registry/frameworks/nuxt/server/utils/serialize.ts +30 -0
- package/registry/frameworks/nuxt/server/utils/session.ts +210 -0
- package/registry/frameworks/nuxt/server/utils/threads.ts +404 -0
- package/registry/frameworks/nuxt/tsconfig.json +18 -0
- package/registry/frameworks/nuxt.ts +17 -0
- package/registry/frameworks/vite/.env.example +20 -0
- package/registry/frameworks/vite/README.md +149 -0
- package/registry/frameworks/vite/agent/index.ts +59 -0
- package/registry/frameworks/vite/agent/middleware.ts +24 -0
- package/registry/frameworks/vite/agent/tools.ts +64 -0
- package/registry/frameworks/vite/index.html +23 -0
- package/registry/frameworks/vite/langgraph.json +16 -0
- package/registry/frameworks/vite/package.json +39 -0
- package/registry/frameworks/vite/public/favicon.ico +0 -0
- package/registry/frameworks/vite/scripts/vite-langgraph-proxy.ts +34 -0
- package/registry/frameworks/vite/src/components/Chat.tsx +124 -0
- package/registry/frameworks/vite/src/components/ChatApp.tsx +122 -0
- package/registry/frameworks/vite/src/components/Conversation.tsx +91 -0
- package/registry/frameworks/vite/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/vite/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/vite/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/vite/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/vite/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/vite/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/vite/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/vite/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/vite/src/lib/agent-type.ts +4 -0
- package/registry/frameworks/vite/src/lib/chat/threads-client.ts +114 -0
- package/registry/frameworks/vite/src/main.tsx +11 -0
- package/registry/frameworks/vite/src/styles/globals.css +714 -0
- package/registry/frameworks/vite/src/vite-env.d.ts +11 -0
- package/registry/frameworks/vite/tsconfig.app.json +24 -0
- package/registry/frameworks/vite/tsconfig.json +7 -0
- package/registry/frameworks/vite/tsconfig.node.json +21 -0
- package/registry/frameworks/vite/vercel.json +3 -0
- package/registry/frameworks/vite/vite.config.ts +24 -0
- package/registry/frameworks/vite.ts +17 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thread state helpers backed by the graph checkpointer.
|
|
3
|
+
*
|
|
4
|
+
* Implements the LangGraph SDK thread state wire-shape consumed by
|
|
5
|
+
* `client.threads.getState` / `updateState` (`GET|POST /threads/:id/state`) and
|
|
6
|
+
* `getHistory` (`POST /threads/:id/history`), aligned with the Agent Protocol
|
|
7
|
+
* thread model.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { MemorySaver } from "@langchain/langgraph";
|
|
11
|
+
import type { CompiledGraphType } from "@langchain/langgraph";
|
|
12
|
+
import type { RunnableConfig } from "@langchain/core/runnables";
|
|
13
|
+
|
|
14
|
+
import { isRecord, sanitizeForJson } from "./serialize.ts";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Compiled LangGraph instance exposed through the custom protocol server.
|
|
18
|
+
*
|
|
19
|
+
* Thread routes read and write checkpointed state through this graph's
|
|
20
|
+
* checkpointer rather than maintaining a separate thread store.
|
|
21
|
+
*/
|
|
22
|
+
export type LocalProtocolGraph = CompiledGraphType;
|
|
23
|
+
|
|
24
|
+
type StateSnapshot = Awaited<ReturnType<LocalProtocolGraph["getState"]>>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Raised when a thread has no checkpoint yet.
|
|
28
|
+
*
|
|
29
|
+
* The route handlers map this to HTTP 404 so the LangGraph SDK can bootstrap
|
|
30
|
+
* the thread via `POST /threads/:id/state` before the first run.
|
|
31
|
+
*/
|
|
32
|
+
export class ThreadNotFoundError extends Error {
|
|
33
|
+
readonly threadId: string;
|
|
34
|
+
|
|
35
|
+
constructor(threadId: string) {
|
|
36
|
+
super(`Thread ${threadId} not found`);
|
|
37
|
+
this.name = "ThreadNotFoundError";
|
|
38
|
+
this.threadId = threadId;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Graph node used when bootstrapping an empty thread.
|
|
44
|
+
*
|
|
45
|
+
* Empty `messages` updates must land on `__start__` so conditional edges are
|
|
46
|
+
* not evaluated before the first human turn exists.
|
|
47
|
+
*/
|
|
48
|
+
const INITIAL_UPDATE_NODE = "__start__";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Default graph node for non-empty state updates on an existing checkpoint.
|
|
52
|
+
*
|
|
53
|
+
* Matches the agent's model node when the client omits `as_node`.
|
|
54
|
+
*/
|
|
55
|
+
const DEFAULT_UPDATE_NODE = "model_request";
|
|
56
|
+
|
|
57
|
+
/** Build the {@link RunnableConfig} that scopes graph calls to a thread id. */
|
|
58
|
+
function threadConfig(threadId: string): RunnableConfig {
|
|
59
|
+
return { configurable: { thread_id: threadId } };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build the config passed to `getStateHistory` for root or subgraph-scoped
|
|
64
|
+
* history, matching langgraph-api's `{ thread_id, checkpoint_ns: "", ...checkpoint }`.
|
|
65
|
+
*/
|
|
66
|
+
function historyConfig(
|
|
67
|
+
threadId: string,
|
|
68
|
+
checkpoint?: Record<string, unknown> | null,
|
|
69
|
+
): RunnableConfig {
|
|
70
|
+
const configurable: Record<string, unknown> = {
|
|
71
|
+
thread_id: threadId,
|
|
72
|
+
checkpoint_ns: "",
|
|
73
|
+
};
|
|
74
|
+
if (checkpoint && isRecord(checkpoint)) {
|
|
75
|
+
Object.assign(configurable, checkpoint);
|
|
76
|
+
}
|
|
77
|
+
return { configurable };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Read the `configurable` bag from a LangGraph run config. */
|
|
81
|
+
function configurableOf(config: RunnableConfig): Record<string, unknown> {
|
|
82
|
+
return isRecord(config.configurable) ? config.configurable : {};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Return whether a {@link StateSnapshot} represents a persisted checkpoint.
|
|
87
|
+
*
|
|
88
|
+
* LangGraph returns an empty configurable bag before the first write; the SDK
|
|
89
|
+
* treats that as "thread not found" rather than an empty thread state.
|
|
90
|
+
*/
|
|
91
|
+
function threadHasCheckpoint(snapshot: StateSnapshot): boolean {
|
|
92
|
+
const checkpointId = configurableOf(snapshot.config).checkpoint_id;
|
|
93
|
+
return typeof checkpointId === "string" && checkpointId.length > 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isStateSnapshot(state: unknown): state is StateSnapshot {
|
|
97
|
+
return isRecord(state) && "values" in state && "next" in state;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function serializeTaskError(error: unknown): string | null {
|
|
101
|
+
if (error == null) return null;
|
|
102
|
+
if (error instanceof Error) return error.message;
|
|
103
|
+
if (typeof error === "string") return error;
|
|
104
|
+
return String(error);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Map a LangGraph run config to the SDK checkpoint wire shape. */
|
|
108
|
+
function runnableConfigToCheckpoint(
|
|
109
|
+
config: RunnableConfig | null | undefined,
|
|
110
|
+
fallbackThreadId?: string,
|
|
111
|
+
): Record<string, unknown> | null {
|
|
112
|
+
if (!config || !isRecord(config.configurable)) return null;
|
|
113
|
+
const c = config.configurable;
|
|
114
|
+
const thread_id =
|
|
115
|
+
typeof c.thread_id === "string" ? c.thread_id : fallbackThreadId;
|
|
116
|
+
if (!thread_id || typeof c.checkpoint_id !== "string") return null;
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
thread_id,
|
|
120
|
+
checkpoint_id: c.checkpoint_id,
|
|
121
|
+
checkpoint_ns: typeof c.checkpoint_ns === "string" ? c.checkpoint_ns : "",
|
|
122
|
+
checkpoint_map: isRecord(c.checkpoint_map) ? c.checkpoint_map : null,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function taskCheckpointFromState(
|
|
127
|
+
state: unknown,
|
|
128
|
+
): Record<string, unknown> | null {
|
|
129
|
+
if (state == null || !isRecord(state) || !isRecord(state.configurable)) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const c = state.configurable;
|
|
133
|
+
if (typeof c.thread_id !== "string") return null;
|
|
134
|
+
return {
|
|
135
|
+
thread_id: c.thread_id,
|
|
136
|
+
checkpoint_id: typeof c.checkpoint_id === "string" ? c.checkpoint_id : null,
|
|
137
|
+
checkpoint_ns: typeof c.checkpoint_ns === "string" ? c.checkpoint_ns : "",
|
|
138
|
+
checkpoint_map: isRecord(c.checkpoint_map) ? c.checkpoint_map : null,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Serialize a LangGraph {@link StateSnapshot} to the SDK `ThreadState` shape.
|
|
144
|
+
*
|
|
145
|
+
* Aligned with langgraph-api's `stateSnapshotToThreadState`, plus
|
|
146
|
+
* {@link sanitizeForJson} on `values` and nested task `result` payloads.
|
|
147
|
+
*/
|
|
148
|
+
export function serializeThreadState(
|
|
149
|
+
snapshot: StateSnapshot,
|
|
150
|
+
threadId: string,
|
|
151
|
+
): Record<string, unknown> {
|
|
152
|
+
const configurable = configurableOf(snapshot.config);
|
|
153
|
+
const checkpoint = runnableConfigToCheckpoint(snapshot.config, threadId) ?? {
|
|
154
|
+
thread_id: threadId,
|
|
155
|
+
checkpoint_id:
|
|
156
|
+
typeof configurable.checkpoint_id === "string"
|
|
157
|
+
? configurable.checkpoint_id
|
|
158
|
+
: null,
|
|
159
|
+
checkpoint_ns:
|
|
160
|
+
typeof configurable.checkpoint_ns === "string"
|
|
161
|
+
? configurable.checkpoint_ns
|
|
162
|
+
: "",
|
|
163
|
+
checkpoint_map: null,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const tasks = (snapshot.tasks ?? []).map((task) => {
|
|
167
|
+
const record = task as {
|
|
168
|
+
id?: unknown;
|
|
169
|
+
name?: unknown;
|
|
170
|
+
error?: unknown;
|
|
171
|
+
interrupts?: unknown;
|
|
172
|
+
path?: unknown;
|
|
173
|
+
result?: unknown;
|
|
174
|
+
state?: unknown;
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
id: record.id,
|
|
178
|
+
name: record.name,
|
|
179
|
+
error: serializeTaskError(record.error),
|
|
180
|
+
interrupts: Array.isArray(record.interrupts) ? record.interrupts : [],
|
|
181
|
+
path: record.path ?? null,
|
|
182
|
+
checkpoint: taskCheckpointFromState(record.state),
|
|
183
|
+
state:
|
|
184
|
+
record.state != null && isStateSnapshot(record.state)
|
|
185
|
+
? serializeThreadState(record.state, threadId)
|
|
186
|
+
: null,
|
|
187
|
+
result: record.result != null ? sanitizeForJson(record.result) : null,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const parentConfig = (snapshot as { parentConfig?: RunnableConfig })
|
|
192
|
+
.parentConfig;
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
values: sanitizeForJson(snapshot.values ?? {}),
|
|
196
|
+
next: [...(snapshot.next ?? [])],
|
|
197
|
+
tasks,
|
|
198
|
+
checkpoint,
|
|
199
|
+
metadata: { ...snapshot.metadata },
|
|
200
|
+
created_at: snapshot.createdAt ?? null,
|
|
201
|
+
parent_checkpoint: runnableConfigToCheckpoint(parentConfig, threadId),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Summary of a thread for the history sidebar. */
|
|
206
|
+
export type ThreadSummary = {
|
|
207
|
+
id: string;
|
|
208
|
+
title: string;
|
|
209
|
+
updatedAt: string | null;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const UNTITLED = "New conversation";
|
|
213
|
+
|
|
214
|
+
/** Derive a sidebar title from the first human message in a thread. */
|
|
215
|
+
function deriveTitle(values: unknown): string {
|
|
216
|
+
if (!isRecord(values) || !Array.isArray(values.messages)) return UNTITLED;
|
|
217
|
+
for (const message of values.messages) {
|
|
218
|
+
if (!isRecord(message) || message.type !== "human") continue;
|
|
219
|
+
const { content } = message;
|
|
220
|
+
const text =
|
|
221
|
+
typeof content === "string"
|
|
222
|
+
? content
|
|
223
|
+
: Array.isArray(content)
|
|
224
|
+
? content
|
|
225
|
+
.map((block) =>
|
|
226
|
+
isRecord(block) && typeof block.text === "string"
|
|
227
|
+
? block.text
|
|
228
|
+
: "",
|
|
229
|
+
)
|
|
230
|
+
.join("")
|
|
231
|
+
: "";
|
|
232
|
+
const trimmed = text.trim();
|
|
233
|
+
if (trimmed) return trimmed.slice(0, 80);
|
|
234
|
+
}
|
|
235
|
+
return UNTITLED;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* List every thread known to the checkpointer, newest first.
|
|
240
|
+
*
|
|
241
|
+
* The checkpointer is the single source of truth: thread ids are the top-level
|
|
242
|
+
* keys of {@link MemorySaver.storage}, and each thread's title/timestamp is
|
|
243
|
+
* derived from its latest checkpoint. Restarting the server clears all of this.
|
|
244
|
+
*/
|
|
245
|
+
export async function listThreads(
|
|
246
|
+
graph: LocalProtocolGraph,
|
|
247
|
+
checkpointer: MemorySaver,
|
|
248
|
+
): Promise<ThreadSummary[]> {
|
|
249
|
+
const ids = Object.keys(checkpointer.storage);
|
|
250
|
+
const summaries: ThreadSummary[] = [];
|
|
251
|
+
for (const id of ids) {
|
|
252
|
+
try {
|
|
253
|
+
const state = await getThreadState(graph, id);
|
|
254
|
+
summaries.push({
|
|
255
|
+
id,
|
|
256
|
+
title: deriveTitle(state.values),
|
|
257
|
+
updatedAt:
|
|
258
|
+
typeof state.created_at === "string" ? state.created_at : null,
|
|
259
|
+
});
|
|
260
|
+
} catch {
|
|
261
|
+
// Skip threads without a readable checkpoint.
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
summaries.sort((a, b) =>
|
|
265
|
+
(b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""),
|
|
266
|
+
);
|
|
267
|
+
return summaries;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Read checkpointed thread state for `GET /threads/:threadId/state`.
|
|
272
|
+
*
|
|
273
|
+
* @throws {@link ThreadNotFoundError} When the thread has no checkpoint yet.
|
|
274
|
+
*/
|
|
275
|
+
export async function getThreadState(
|
|
276
|
+
graph: LocalProtocolGraph,
|
|
277
|
+
threadId: string,
|
|
278
|
+
): Promise<Record<string, unknown>> {
|
|
279
|
+
const snapshot = await graph.getState(threadConfig(threadId));
|
|
280
|
+
if (!threadHasCheckpoint(snapshot)) throw new ThreadNotFoundError(threadId);
|
|
281
|
+
return serializeThreadState(snapshot, threadId);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Parse the `before` pagination cursor accepted by `POST /threads/:id/history`.
|
|
286
|
+
*/
|
|
287
|
+
function parseBeforeCursor(
|
|
288
|
+
threadId: string,
|
|
289
|
+
before: unknown,
|
|
290
|
+
): RunnableConfig | undefined {
|
|
291
|
+
if (before == null) return undefined;
|
|
292
|
+
if (typeof before === "string") {
|
|
293
|
+
return { configurable: { thread_id: threadId, checkpoint_id: before } };
|
|
294
|
+
}
|
|
295
|
+
if (!isRecord(before)) return undefined;
|
|
296
|
+
|
|
297
|
+
if (isRecord(before.configurable)) {
|
|
298
|
+
return {
|
|
299
|
+
configurable: { thread_id: threadId, ...before.configurable },
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const checkpointId = before.checkpoint_id;
|
|
304
|
+
if (typeof checkpointId !== "string") return undefined;
|
|
305
|
+
|
|
306
|
+
const cursor: RunnableConfig = {
|
|
307
|
+
configurable: { thread_id: threadId, checkpoint_id: checkpointId },
|
|
308
|
+
};
|
|
309
|
+
if (typeof before.checkpoint_ns === "string") {
|
|
310
|
+
(cursor.configurable as Record<string, unknown>).checkpoint_ns =
|
|
311
|
+
before.checkpoint_ns;
|
|
312
|
+
}
|
|
313
|
+
return cursor;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* List past thread states for `POST /threads/:threadId/history`.
|
|
318
|
+
*
|
|
319
|
+
* @throws {@link ThreadNotFoundError} When the thread has no checkpoint yet.
|
|
320
|
+
*/
|
|
321
|
+
export async function getThreadHistory(
|
|
322
|
+
graph: LocalProtocolGraph,
|
|
323
|
+
threadId: string,
|
|
324
|
+
options: {
|
|
325
|
+
limit?: number;
|
|
326
|
+
before?: unknown;
|
|
327
|
+
metadata?: Record<string, unknown>;
|
|
328
|
+
checkpoint?: Record<string, unknown> | null;
|
|
329
|
+
} = {},
|
|
330
|
+
): Promise<Record<string, unknown>[]> {
|
|
331
|
+
await getThreadState(graph, threadId);
|
|
332
|
+
|
|
333
|
+
const history: Record<string, unknown>[] = [];
|
|
334
|
+
const iterator = graph.getStateHistory(
|
|
335
|
+
historyConfig(threadId, options.checkpoint),
|
|
336
|
+
{
|
|
337
|
+
before: parseBeforeCursor(threadId, options.before),
|
|
338
|
+
limit: options.limit ?? 10,
|
|
339
|
+
...(options.metadata ? { filter: options.metadata } : {}),
|
|
340
|
+
},
|
|
341
|
+
);
|
|
342
|
+
for await (const snapshot of iterator) {
|
|
343
|
+
history.push(serializeThreadState(snapshot, threadId));
|
|
344
|
+
}
|
|
345
|
+
return history;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Choose which graph node should receive an `updateState` write. */
|
|
349
|
+
function resolveUpdateNode(options: {
|
|
350
|
+
asNode?: string;
|
|
351
|
+
values: Record<string, unknown> | null;
|
|
352
|
+
hasCheckpoint: boolean;
|
|
353
|
+
}): string {
|
|
354
|
+
if (options.asNode) return options.asNode;
|
|
355
|
+
const messages = options.values?.messages;
|
|
356
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
357
|
+
return INITIAL_UPDATE_NODE;
|
|
358
|
+
}
|
|
359
|
+
if (!options.hasCheckpoint) return INITIAL_UPDATE_NODE;
|
|
360
|
+
return DEFAULT_UPDATE_NODE;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Create or update thread state for `POST /threads/:threadId/state`.
|
|
365
|
+
*
|
|
366
|
+
* Used by the browser bootstrap and by the SDK when hydrating or editing
|
|
367
|
+
* conversation history. Applies the update at {@link resolveUpdateNode}, then
|
|
368
|
+
* returns the latest serialized snapshot.
|
|
369
|
+
*/
|
|
370
|
+
export async function updateThreadState(
|
|
371
|
+
graph: LocalProtocolGraph,
|
|
372
|
+
threadId: string,
|
|
373
|
+
options: {
|
|
374
|
+
values?: Record<string, unknown> | null;
|
|
375
|
+
checkpoint?: Record<string, unknown> | null;
|
|
376
|
+
asNode?: string;
|
|
377
|
+
} = {},
|
|
378
|
+
): Promise<Record<string, unknown>> {
|
|
379
|
+
let config = threadConfig(threadId);
|
|
380
|
+
const checkpoint = options.checkpoint;
|
|
381
|
+
if (checkpoint && typeof checkpoint.checkpoint_id === "string") {
|
|
382
|
+
config = {
|
|
383
|
+
configurable: {
|
|
384
|
+
...configurableOf(config),
|
|
385
|
+
checkpoint_id: checkpoint.checkpoint_id,
|
|
386
|
+
...(typeof checkpoint.checkpoint_ns === "string"
|
|
387
|
+
? { checkpoint_ns: checkpoint.checkpoint_ns }
|
|
388
|
+
: {}),
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const snapshot = await graph.getState(config);
|
|
394
|
+
const resolvedValues = options.values ?? { messages: [] };
|
|
395
|
+
const resolvedAsNode = resolveUpdateNode({
|
|
396
|
+
asNode: options.asNode,
|
|
397
|
+
values: resolvedValues,
|
|
398
|
+
hasCheckpoint: threadHasCheckpoint(snapshot),
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
await graph.updateState(config, resolvedValues, resolvedAsNode);
|
|
402
|
+
const updated = await graph.getState(threadConfig(threadId));
|
|
403
|
+
return serializeThreadState(updated, threadId);
|
|
404
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createFramework } from "../../src/registry/framework.js";
|
|
2
|
+
|
|
3
|
+
export const deno = createFramework({
|
|
4
|
+
id: "deno",
|
|
5
|
+
title: "Deno",
|
|
6
|
+
defaultProjectName: "deno-deepagents",
|
|
7
|
+
address: {
|
|
8
|
+
scheme: "github",
|
|
9
|
+
owner: "aolsenjazz",
|
|
10
|
+
repo: "deployment-cookbook",
|
|
11
|
+
subPath: "js-deno",
|
|
12
|
+
},
|
|
13
|
+
envFilePath: ".env",
|
|
14
|
+
packageJsonPath: "package.json",
|
|
15
|
+
agentPath: "server/agent",
|
|
16
|
+
files: [],
|
|
17
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Deploying a LangChain Agent with Cloudflare Workers
|
|
2
|
+
|
|
3
|
+
An example app that deploys a LangChain **deep agent** on
|
|
4
|
+
[Cloudflare Workers](https://developers.cloudflare.com/workers/) — streaming chat
|
|
5
|
+
UI, subagents, and thread history, all backed by the
|
|
6
|
+
[Agent Streaming Protocol](https://github.com/langchain-ai/agent-protocol/tree/main/streaming) implemented as Worker
|
|
7
|
+
routes (HTTP + SSE). The React SPA is served from the same Worker via
|
|
8
|
+
[Workers Assets](https://developers.cloudflare.com/workers/static-assets/). No
|
|
9
|
+
separate backend process — one Worker serves the SPA and the protocol API.
|
|
10
|
+
|
|
11
|
+
## Deploy to Cloudflare
|
|
12
|
+
|
|
13
|
+
1. Install dependencies and build:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd js-cloudflare
|
|
17
|
+
cp .env.example .dev.vars # set OPENAI_API_KEY for local dev
|
|
18
|
+
pnpm install
|
|
19
|
+
pnpm build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. Log in and set your secret:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx wrangler login
|
|
26
|
+
npx wrangler secret put OPENAI_API_KEY
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
3. Deploy:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm run deploy
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Wrangler uploads the Vite build (SPA) and the Worker script in one deploy.
|
|
36
|
+
`nodejs_compat` and `nodejs_compat_populate_process_env` are enabled so LangChain
|
|
37
|
+
can read `OPENAI_API_KEY` from the environment.
|
|
38
|
+
|
|
39
|
+
`wrangler.jsonc` registers the `ThreadSession` Durable Object with
|
|
40
|
+
`new_sqlite_classes`, which is required on the Workers **Free** plan (error
|
|
41
|
+
`10097` if you use the legacy `new_classes` migration instead).
|
|
42
|
+
|
|
43
|
+
Optionally enable LangSmith tracing by adding the variables from
|
|
44
|
+
[`.env.example`](./.env.example) as Worker secrets or vars.
|
|
45
|
+
|
|
46
|
+
## Required API endpoints
|
|
47
|
+
|
|
48
|
+
The app exposes the Agent Streaming Protocol under `/api/threads/...`. Routes
|
|
49
|
+
are implemented in `worker/index.ts` with [Hono](https://hono.dev).
|
|
50
|
+
|
|
51
|
+
### Minimum (streaming chat)
|
|
52
|
+
|
|
53
|
+
| Method | Path | Purpose |
|
|
54
|
+
| -------------- | --------------------------------- | -------------------------------------------------------------- |
|
|
55
|
+
| `POST` | `/api/threads/:threadId/commands` | Accept protocol commands (`run.start`, …) and start agent runs |
|
|
56
|
+
| `POST` | `/api/threads/:threadId/stream` | SSE stream of protocol events for a run |
|
|
57
|
+
| `GET` / `POST` | `/api/threads/:threadId/state` | Read and bootstrap checkpointed thread state |
|
|
58
|
+
|
|
59
|
+
### Optional (sidebar)
|
|
60
|
+
|
|
61
|
+
| Method | Path | Purpose |
|
|
62
|
+
| -------- | -------------------------------- | ----------------------------------------- |
|
|
63
|
+
| `GET` | `/api/threads` | List threads known to the checkpointer |
|
|
64
|
+
| `DELETE` | `/api/threads/:threadId` | Delete a thread's session and checkpoints |
|
|
65
|
+
| `POST` | `/api/threads/:threadId/history` | Paginated checkpoint history |
|
|
66
|
+
|
|
67
|
+
### Request flow
|
|
68
|
+
|
|
69
|
+
```mermaid
|
|
70
|
+
flowchart TB
|
|
71
|
+
subgraph browser["Browser (Vite + React)"]
|
|
72
|
+
SP["StreamProvider"]
|
|
73
|
+
HAA["HttpAgentServerAdapter"]
|
|
74
|
+
SP --- HAA
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
subgraph worker["Cloudflare Worker (Hono)"]
|
|
78
|
+
CMD["POST /api/threads/:id/commands"]
|
|
79
|
+
STR["POST /api/threads/:id/stream"]
|
|
80
|
+
STA["GET|POST /api/threads/:id/state"]
|
|
81
|
+
RUN["startAgentRun"]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
subgraph do["Durable Object (per thread)"]
|
|
85
|
+
LOG["StreamChannel event log"]
|
|
86
|
+
SSE["SSE subscriptions"]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
subgraph agent["worker/agent"]
|
|
90
|
+
AGT["createDeepAgent + MemorySaver"]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
HAA -->|POST| CMD
|
|
94
|
+
HAA -->|POST| STR
|
|
95
|
+
HAA -->|GET / POST| STA
|
|
96
|
+
CMD --> RUN
|
|
97
|
+
RUN --> AGT
|
|
98
|
+
RUN -->|publish events| LOG
|
|
99
|
+
STR --> SSE
|
|
100
|
+
LOG --> SSE
|
|
101
|
+
STA --> AGT
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
1. Bootstrap thread state (`GET`/`POST /state`).
|
|
105
|
+
2. On submit, the SDK sends `run.start` to `/commands` and receives a `run_id`.
|
|
106
|
+
3. The Worker starts the graph run and fans each protocol event into the
|
|
107
|
+
thread's **Durable Object**.
|
|
108
|
+
4. The SDK subscribes to `/stream` (SSE). The DO replays buffered events and
|
|
109
|
+
stays attached for live frames — even across Worker isolate restarts.
|
|
110
|
+
5. Subagent (`task`) runs emit namespaced events surfaced as `stream.subagents`.
|
|
111
|
+
|
|
112
|
+
## Cloudflare backend design
|
|
113
|
+
|
|
114
|
+
| Concern | Implementation |
|
|
115
|
+
| ------------- | ------------------------------------------------------- |
|
|
116
|
+
| Frontend | Vite + React SPA (`src/`) |
|
|
117
|
+
| API layer | Hono routes in `worker/index.ts` |
|
|
118
|
+
| Runtime | Workers V8 + `nodejs_compat` |
|
|
119
|
+
| SSE replay | Per-thread **Durable Object** (`ThreadSession`) |
|
|
120
|
+
| Agent runs | Worker isolate; protocol events POSTed to the DO |
|
|
121
|
+
| Static assets | Workers Assets (`wrangler.jsonc` → `assets`) |
|
|
122
|
+
| Secrets | `wrangler secret` / `.dev.vars` |
|
|
123
|
+
| Local dev | `vite` (Cloudflare Vite plugin runs the Worker runtime) |
|
|
124
|
+
|
|
125
|
+
The split between **Worker** (agent + checkpointer) and **Durable Object** (SSE
|
|
126
|
+
event log) is the main design choice on Cloudflare. Worker isolates are
|
|
127
|
+
ephemeral, so replay buffers live in Durable Objects rather than process memory.
|
|
128
|
+
|
|
129
|
+
## Production persistence
|
|
130
|
+
|
|
131
|
+
Out of the box, the agent uses an in-memory `MemorySaver` checkpointer
|
|
132
|
+
(`worker/agent/index.ts`). That works for local dev and demos, but on Cloudflare
|
|
133
|
+
(multiple isolates, cold starts) conversation state is **not durable** across
|
|
134
|
+
deploys or isolates.
|
|
135
|
+
|
|
136
|
+
For production:
|
|
137
|
+
|
|
138
|
+
1. Swap in a [durable checkpointer](https://docs.langchain.com/oss/javascript/langgraph/checkpointers#checkpointer-libraries)
|
|
139
|
+
(for example Postgres via Hyperdrive, or a custom DO-backed store).
|
|
140
|
+
2. Keep per-thread Durable Objects for SSE replay (or persist the event log to
|
|
141
|
+
DO storage / KV for long-lived reconnects).
|
|
142
|
+
|
|
143
|
+
See also: [checkpointer libraries](https://docs.langchain.com/oss/javascript/langgraph/checkpointers#checkpointer-libraries),
|
|
144
|
+
[add memory / persistence](https://docs.langchain.com/oss/javascript/langgraph/add-memory).
|
|
145
|
+
|
|
146
|
+
## Local development
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
cp .env.example .dev.vars # set OPENAI_API_KEY
|
|
150
|
+
pnpm install
|
|
151
|
+
pnpm dev
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Open [http://localhost:5173](http://localhost:5173). The Cloudflare Vite plugin
|
|
155
|
+
runs your Worker in the Workers runtime during dev, so `/api/*` routes behave
|
|
156
|
+
like production.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pnpm build # production build (client + worker)
|
|
160
|
+
pnpm preview # preview the production build locally
|
|
161
|
+
pnpm typecheck
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Project layout
|
|
165
|
+
|
|
166
|
+
- `src/components/` — chat UI (`ChatApp`, `Chat`, `MessageThread`, `Subagents`,
|
|
167
|
+
`ThreadHistory`, …).
|
|
168
|
+
- `src/lib/chat/threads-client.ts` — browser thread bootstrap and sidebar helpers.
|
|
169
|
+
- `worker/agent/` — deep agent (`createDeepAgent`) with `researcher` and
|
|
170
|
+
`math-whiz` subagents and mock tools.
|
|
171
|
+
- `worker/server/` — protocol helpers: `runs.ts` (start runs on the Worker),
|
|
172
|
+
`threads.ts` (checkpointer-backed state), `serialize.ts`, `registry.ts`.
|
|
173
|
+
- `worker/durable-objects/thread-session.ts` — per-thread SSE event log
|
|
174
|
+
(`StreamChannel` + `matchesSubscription`).
|
|
175
|
+
- `worker/index.ts` — Hono app: protocol routes + Worker export.
|
|
176
|
+
- `wrangler.jsonc` — Worker config: `nodejs_compat`, Durable Object bindings,
|
|
177
|
+
SPA asset routing (`run_worker_first: ["/api/*"]`).
|
|
178
|
+
|
|
179
|
+
## References
|
|
180
|
+
|
|
181
|
+
- [Agent Streaming Protocol](https://github.com/langchain-ai/agent-protocol/tree/main/streaming) — protocol spec consumed by `@langchain/react`'s `HttpAgentServerAdapter`
|
|
182
|
+
- [`react-custom-backend`](https://github.com/langchain-ai/streaming-cookbook) — original Vite + Hono reference for a custom protocol server
|
|
183
|
+
- [Cloudflare Workers](https://developers.cloudflare.com/workers/) — edge runtime and deployment
|
|
184
|
+
- [Durable Objects](https://developers.cloudflare.com/durable-objects/) — per-thread SSE replay storage
|
|
185
|
+
- [Workers Assets](https://developers.cloudflare.com/workers/static-assets/) — SPA hosting from the same Worker
|
|
186
|
+
- [`deepagents`](https://www.npmjs.com/package/deepagents) — coordinator + subagent orchestration used by this demo
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="h-full antialiased">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>LangChain Deep Agent · Cloudflare Workers</title>
|
|
7
|
+
<meta
|
|
8
|
+
name="description"
|
|
9
|
+
content="Deploying a LangChain deep agent with Cloudflare Workers: streaming chat, subagents, and per-thread history."
|
|
10
|
+
/>
|
|
11
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
12
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
13
|
+
<link
|
|
14
|
+
href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500&family=Geist:wght@400;500;600&display=swap"
|
|
15
|
+
rel="stylesheet"
|
|
16
|
+
/>
|
|
17
|
+
</head>
|
|
18
|
+
<body class="min-h-full">
|
|
19
|
+
<div id="root" class="h-full"></div>
|
|
20
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hono-deepagents",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"deploy": "pnpm build && wrangler deploy",
|
|
11
|
+
"typecheck": "tsc -b --noEmit",
|
|
12
|
+
"lint": "eslint ."
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@langchain/core": "^1.2.1",
|
|
16
|
+
"@langchain/langgraph": "^1.3.7",
|
|
17
|
+
"@langchain/langgraph-sdk": "^1.9.20",
|
|
18
|
+
"@langchain/openai": "^1.4.7",
|
|
19
|
+
"@langchain/protocol": "^0.0.16",
|
|
20
|
+
"@langchain/react": "^1.0.20",
|
|
21
|
+
"deepagents": "^1.10.2",
|
|
22
|
+
"hono": "^4.11.4",
|
|
23
|
+
"langchain": "^1.4.4",
|
|
24
|
+
"react": "19.2.4",
|
|
25
|
+
"react-dom": "19.2.4",
|
|
26
|
+
"zod": "^4.4.3"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@cloudflare/vite-plugin": "^1.7.0",
|
|
30
|
+
"@cloudflare/workers-types": "^4.20250613.0",
|
|
31
|
+
"@tailwindcss/vite": "^4.1.11",
|
|
32
|
+
"@types/react": "^19",
|
|
33
|
+
"@types/react-dom": "^19",
|
|
34
|
+
"@vitejs/plugin-react": "^5.1.0",
|
|
35
|
+
"eslint": "^9",
|
|
36
|
+
"tailwindcss": "^4",
|
|
37
|
+
"typescript": "^5",
|
|
38
|
+
"vite": "^7.1.12",
|
|
39
|
+
"wrangler": "^4.20.0"
|
|
40
|
+
},
|
|
41
|
+
"packageManager": "pnpm@10.29.2"
|
|
42
|
+
}
|