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
157 lines (140 loc) · 5.67 KB
/
Copy pathroute.ts
File metadata and controls
157 lines (140 loc) · 5.67 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
import { NextRequest, NextResponse } from "next/server";
import {
CopilotRuntime,
ExperimentalEmptyAdapter,
copilotRuntimeNextJSAppRouterEndpoint,
} from "@copilotkit/runtime";
import { LangGraphAgent } from "@copilotkit/runtime/langgraph";
import crypto from "node:crypto";
// Emit a structured server-side error log with a correlation id so the
// 500 we return to the client carries no stack/message details (which
// can leak internal config, prompts, or upstream URLs) while operators
// can still grep logs for the same `errorId` to find the full failure.
function logRouteError(err: unknown): string {
const error = err instanceof Error ? err : new Error(String(err));
const errorId = crypto.randomUUID();
console.error(
JSON.stringify({
at: new Date().toISOString(),
level: "error",
phase: "setup",
errorId,
message: error.message,
stack: error.stack,
}),
);
return errorId;
}
const AGENT_URL = process.env.AGENT_URL || "http://localhost:8123";
console.log("[copilotkit/route] Initializing CopilotKit runtime");
console.log(`[copilotkit/route] AGENT_URL: ${AGENT_URL}`);
function createAgent(graphId: string = "sample_agent") {
return new LangGraphAgent({
deploymentUrl: `${AGENT_URL}/`,
graphId,
});
}
const agentNames = [
"agentic_chat",
"human_in_the_loop",
"gen-ui-tool-based",
"gen-ui-agent",
"shared-state-read",
"shared-state-write",
"shared-state-streaming",
"prebuilt-sidebar",
"prebuilt-popup",
"chat-slots",
"chat-customization-css",
"headless-simple",
];
const agents: Record<string, LangGraphAgent> = {};
for (const name of agentNames) {
agents[name] = createAgent();
}
// Dedicated-graph agents for tool-rendering + reasoning demos.
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",
);
agents["agentic-chat-reasoning"] = createAgent("reasoning_agent");
agents["reasoning-default-render"] = createAgent("reasoning_agent");
// Interrupt variants — share the dedicated `interrupt_agent` graph that uses
// langgraph's `interrupt()` primitive inside `schedule_meeting`.
agents["gen-ui-interrupt"] = createAgent("interrupt_agent");
agents["interrupt-headless"] = createAgent("interrupt_agent");
// Dedicated-graph agents — each cell has its own LangGraph graph with a
// tailored system prompt (tools=[], CopilotKitMiddleware attached).
agents["frontend_tools"] = createAgent("frontend_tools");
agents["frontend-tools-async"] = createAgent("frontend_tools_async");
agents["hitl-in-app"] = createAgent("hitl_in_app");
// In-Chat HITL via the high-level `useHumanInTheLoop` hook — backend
// agent has zero tools; the frontend-registered `book_call` tool is
// injected into the LLM's tool list by `CopilotKitMiddleware`. Both
// the canonical `hitl-in-chat` demo and the `hitl-in-chat-booking`
// alias share the same backend graph.
agents["hitl-in-chat"] = createAgent("hitl_in_chat");
agents["hitl-in-chat-booking"] = createAgent("hitl_in_chat");
// HITL step-selection: dedicated graph with tools=[] + CopilotKitMiddleware.
// The `human_in_the_loop` alias in the neutral-assistant loop above maps to
// `sample_agent` which has 7+ backend tools and a custom AgentState — the
// frontend-only `generate_task_steps` tool (from useHumanInTheLoop) is the
// ONLY tool this demo needs, so a minimal graph avoids state/tool contention.
agents["human_in_the_loop"] = createAgent("hitl_steps");
agents["readonly-state-agent-context"] = createAgent(
"readonly_state_agent_context",
);
// Shared State (Read + Write) — bidirectional shared state between UI and
// agent. UI writes `preferences` via agent.setState; middleware reads them
// into the system prompt; agent writes `notes` back via the `set_notes` tool.
agents["shared-state-read-write"] = createAgent("shared_state_read_write");
// Sub-Agents — supervisor delegates to research_agent / writing_agent /
// critique_agent (each a full create_agent under the hood). Every delegation
// is appended to `state.delegations` for live UI rendering.
agents["subagents"] = createAgent("subagents");
agents["default"] = createAgent();
console.log(
`[copilotkit/route] Registered ${Object.keys(agents).length} agent names: ${Object.keys(agents).join(", ")}`,
);
export const POST = async (req: NextRequest) => {
try {
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
endpoint: "/api/copilotkit",
serviceAdapter: new ExperimentalEmptyAdapter(),
runtime: new CopilotRuntime({
// @ts-ignore
agents,
}),
});
const response = await handleRequest(req);
console.log(`[copilotkit/route] Response status: ${response.status}`);
return response;
} catch (error: unknown) {
// Log full message + stack server-side under a correlation id; return
// only the id to the client so we don't leak internal details (upstream
// URLs, env-driven config, prompts, etc.) into HTTP responses.
const errorId = logRouteError(error);
return NextResponse.json(
{ error: "internal runtime error", errorId },
{ status: 500 },
);
}
};
export const GET = async () => {
let agentStatus = "unknown";
try {
const res = await fetch(`${AGENT_URL}/ok`, {
signal: AbortSignal.timeout(3000),
});
agentStatus = res.ok ? "reachable" : `error (${res.status})`;
} catch (e: unknown) {
agentStatus = `unreachable (${(e as Error).message})`;
}
return NextResponse.json({
status: "ok",
agent_url: AGENT_URL,
agent_status: agentStatus,
});
};