2026-02-08 18:02:25 -08:00

107 lines
3.3 KiB
TypeScript

import type { UIMessage } from "ai";
import { runAgent } from "@/lib/agent-runner";
// Force Node.js runtime (required for child_process)
export const runtime = "nodejs";
// Allow streaming responses up to 10 minutes
export const maxDuration = 600;
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
// Extract the latest user message text
const lastUserMessage = messages.filter((m) => m.role === "user").pop();
const userText =
lastUserMessage?.parts
?.filter((p): p is { type: "text"; text: string } => p.type === "text")
.map((p) => p.text)
.join("\n") ?? "";
console.log("[chat] Received message:", userText);
if (!userText.trim()) {
return new Response("No message provided", { status: 400 });
}
// Create a custom SSE stream
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
const textPartId = `text-${Date.now()}`;
let started = false;
const writeEvent = (data: unknown) => {
const json = JSON.stringify(data);
console.log("[chat] SSE write:", json);
controller.enqueue(encoder.encode(`data: ${json}\n\n`));
};
console.log("[chat] Starting agent stream...");
try {
await runAgent(userText, {
onTextDelta: (delta) => {
console.log("[chat] Text delta:", delta);
if (!started) {
console.log("[chat] Writing text-start");
writeEvent({ type: "text-start", id: textPartId });
started = true;
}
writeEvent({ type: "text-delta", id: textPartId, delta });
},
onLifecycleEnd: () => {
console.log("[chat] Lifecycle end, started:", started);
if (started) {
writeEvent({ type: "text-end", id: textPartId });
}
},
onError: (err) => {
console.error("[chat] Agent error:", err);
if (!started) {
writeEvent({ type: "text-start", id: textPartId });
writeEvent({
type: "text-delta",
id: textPartId,
delta: `Error starting agent: ${err.message}`,
});
writeEvent({ type: "text-end", id: textPartId });
}
},
onClose: (code) => {
console.log("[chat] Agent closed with code:", code, "started:", started);
// If we never started text, emit an empty response
if (!started) {
writeEvent({ type: "text-start", id: textPartId });
writeEvent({
type: "text-delta",
id: textPartId,
delta: "(No response from agent)",
});
writeEvent({ type: "text-end", id: textPartId });
}
},
});
console.log("[chat] Agent stream complete");
} catch (error) {
console.error("[chat] Stream error:", error);
writeEvent({
type: "error",
error: error instanceof Error ? error.message : String(error),
});
} finally {
controller.close();
}
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
Connection: "keep-alive",
},
});
}