forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroute.ts
More file actions
174 lines (160 loc) · 7.31 KB
/
Copy pathroute.ts
File metadata and controls
174 lines (160 loc) · 7.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import {
CopilotRuntime,
ExperimentalEmptyAdapter,
copilotRuntimeNextJSAppRouterEndpoint,
} from "@copilotkit/runtime";
import { LangGraphAgent } from "@copilotkit/runtime/langgraph";
const LANGGRAPH_URL =
process.env.LANGGRAPH_DEPLOYMENT_URL || "http://localhost:8123";
console.log("[copilotkit/route] Initializing CopilotKit runtime");
console.log(`[copilotkit/route] LANGGRAPH_URL: ${LANGGRAPH_URL}`);
console.log(
`[copilotkit/route] LANGSMITH_API_KEY: ${process.env.LANGSMITH_API_KEY ? "set" : "not set"}`,
);
function createAgent(
graphId: string = "sample_agent",
options: { recursionLimit?: number } = {},
) {
// LangGraph's `recursion_limit` defaults to 25 (langchain_core), and
// `with_config` in Python doesn't propagate when the graph is invoked via
// the langgraph server's runs API — the wrapper isn't visible to the
// assistant config. Bake the limit into `assistantConfig` here so it
// travels with every run we kick off through this route.
return new LangGraphAgent({
deploymentUrl: LANGGRAPH_URL,
graphId,
langsmithApiKey: process.env.LANGSMITH_API_KEY || "",
assistantConfig: { recursion_limit: options.recursionLimit ?? 100 },
});
}
// Cells that share the neutral default "helpful, concise assistant" graph.
// These cells are chrome / UI / docs demos — the agent has no specialized
// behavior. Each still gets its own registered name so per-cell frontend
// tool/component registrations scope correctly.
const neutralAssistantCells = [
"human_in_the_loop",
"shared-state-read",
"shared-state-write",
"prebuilt-sidebar",
"prebuilt-popup",
"chat-slots",
"chat-customization-css",
"headless-simple",
// NOTE: `beautiful-chat` is NOT in this list — it has its own dedicated
// runtime at /api/copilotkit-beautiful-chat (needed for openGenerativeUI +
// a2ui + mcpApps combined-runtime shape). See the beautiful-chat page.tsx.
];
const agents: Record<string, LangGraphAgent> = {};
for (const name of neutralAssistantCells) {
agents[name] = createAgent();
}
// Dedicated-graph agents.
agents["tool-rendering"] = createAgent("tool_rendering");
agents["tool-rendering-default-catchall"] = createAgent("tool_rendering");
agents["tool-rendering-custom-catchall"] = createAgent("tool_rendering");
agents["tool-rendering-reasoning-chain"] = createAgent(
"tool_rendering_reasoning_chain",
);
// Declarative Generative UI (A2UI — Dynamic Schema) demo uses its own graph.
agents["declarative-gen-ui"] = createAgent("a2ui_dynamic");
// Declarative Generative UI (A2UI — Fixed Schema) demo.
agents["a2ui-fixed-schema"] = createAgent("a2ui_fixed");
// Dedicated graphs for stub cells (ported from 4084).
agents["shared-state-streaming"] = createAgent("shared_state_streaming");
agents["subagents"] = createAgent("subagents");
// Basic cells with their own dedicated graphs (neutral assistant variants
// split out of main.py so main.py stays a pure default).
agents["agentic_chat"] = createAgent("agentic_chat");
agents["frontend_tools"] = createAgent("frontend_tools");
agents["threadid-frontend-tool-roundtrip"] = createAgent("frontend_tools");
// Frontend Tools (Async) — dedicated cell demonstrating an async useFrontendTool
// handler (simulated client-side notes DB query). Backend has no tools; the
// frontend registers `query_notes` via useFrontendTool and the agent awaits
// its returned result.
agents["frontend-tools-async"] = createAgent("frontend_tools_async");
agents["gen-ui-agent"] = createAgent("gen_ui_agent");
// Tool-Based Generative UI — chart-viz system prompt lives in its own graph.
agents["gen-ui-tool-based"] = createAgent("gen_ui_tool_based");
// Reasoning variants. The Custom demo (`reasoning-custom`) and the
// Default demo (`reasoning-default`) both share the same backend graph;
// the only difference is whether the frontend overrides the
// `messageView.reasoningMessage` slot.
agents["reasoning-custom"] = createAgent("reasoning_agent");
agents["reasoning-default"] = createAgent("reasoning_agent");
// Interrupt variants.
agents["gen-ui-interrupt"] = createAgent("interrupt_agent");
agents["interrupt-headless"] = createAgent("interrupt_agent");
// HITL dedicated (tools=[] + tailored system prompt).
agents["hitl-in-chat"] = createAgent("hitl_in_chat");
// In-App HITL — async frontend-tool + app-level modal (outside chat).
agents["hitl-in-app"] = createAgent("hitl_in_app");
// Shared state R+W and read-only agent context.
agents["shared-state-read-write"] = createAgent("shared_state_read_write");
agents["readonly-state-agent-context"] = createAgent(
"readonly_state_agent_context",
);
// Also register a default
agents["default"] = createAgent();
console.log(
`[copilotkit/route] Registered ${Object.keys(agents).length} agent names: ${Object.keys(agents).join(", ")}`,
);
export const POST = async (req: NextRequest) => {
const url = req.url;
const contentType = req.headers.get("content-type");
console.log(`[copilotkit/route] POST ${url} (content-type: ${contentType})`);
try {
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
endpoint: "/api/copilotkit",
serviceAdapter: new ExperimentalEmptyAdapter(),
runtime: new CopilotRuntime({
// @ts-ignore -- Published CopilotRuntime agents type wraps Record in MaybePromise<NonEmptyRecord<...>> which rejects plain Records; fixed in source, pending release
agents,
// NOTE: A2UI is intentionally NOT enabled here. The A2UI cells
// (declarative-gen-ui and a2ui-fixed-schema) each live on their own
// dedicated runtime endpoint (/api/copilotkit-declarative-gen-ui and
// /api/copilotkit-a2ui-fixed-schema respectively), mirroring the
// beautiful-chat topology. The dynamic-schema cells
// (declarative-gen-ui, beautiful-chat) set `a2ui.injectA2UITool: true`
// so the runtime injects `generate_a2ui` and the agent's
// CopilotKitMiddleware auto-executes it; the fixed-schema cell sets
// `false` because that agent owns its `display_flight` tool explicitly.
// OpenGenerativeUI lives in /api/copilotkit-ogui for the same reason.
// MCP Apps is in /api/copilotkit-mcp-apps.
}),
});
const response = await handleRequest(req);
console.log(`[copilotkit/route] Response status: ${response.status}`);
return response;
} catch (error: any) {
console.error(`[copilotkit/route] ERROR: ${error.message}`);
console.error(`[copilotkit/route] Stack: ${error.stack}`);
return NextResponse.json(
{ error: error.message, stack: error.stack },
{ status: 500 },
);
}
};
export const GET = async () => {
console.log("[copilotkit/route] GET /api/copilotkit (health probe)");
let langGraphStatus = "unknown";
try {
const res = await fetch(`${LANGGRAPH_URL}/ok`, {
signal: AbortSignal.timeout(3000),
});
langGraphStatus = res.ok ? "reachable" : `error (${res.status})`;
} catch (e: any) {
langGraphStatus = `unreachable (${e.message})`;
}
return NextResponse.json({
status: "ok",
langgraph_url: LANGGRAPH_URL,
langgraph_status: langGraphStatus,
env: {
OPENAI_API_KEY: process.env.OPENAI_API_KEY ? "set" : "NOT SET",
LANGSMITH_API_KEY: process.env.LANGSMITH_API_KEY ? "set" : "NOT SET",
NODE_ENV: process.env.NODE_ENV,
},
});
};