import { streamText, Message, createDataStream } from "ai"; import { Hono } from "hono"; import { stream } from "hono/streaming"; import { processPendingToolCalls } from "./util.js"; import { agentsById, openrouter } from "./agentRegistry.js"; const app = new Hono(); app.post("/api/chat/:agent_id", async (c) => { const input: { messages: Message[] } = await c.req.json(); const agentId = c.req.param("agent_id"); const agent = agentsById[agentId]; if (!agent) { c.status(404); return c.json({ error: `No such agent: ${agentId}` }); } const dataStream = createDataStream({ execute: async (dataStreamWriter) => { // dataStreamWriter.writeData('initialized call'); // Process any pending tool calls in the messages // This modifies the messages array in place await processPendingToolCalls(input.messages, dataStreamWriter); const result = streamText({ model: openrouter(agent.modelName), maxSteps: 5, messages: [ { role: "system", content: agent.systemMessage }, ...Object.values(agentsById).map((agent) => ({ role: "system" as const, content: `Agent ${JSON.stringify({ id: agent.id, name: agent.name, description: agent.description, skills: agent.skills, })}`, })), ...input.messages, ], tools: agent.tools, onError: (error) => { console.error("Error in streamText:", error); }, }); result.mergeIntoDataStream(dataStreamWriter); }, onError: (error) => { // Error messages are masked by default for security reasons. // If you want to expose the error message to the client, you can do so here: return error instanceof Error ? error.message : String(error); }, }); // Mark the response as a v1 data stream: c.header("X-Vercel-AI-Data-Stream", "v1"); c.header("Content-Type", "text/plain; charset=utf-8"); return stream(c, (stream) => stream.pipe(dataStream.pipeThrough(new TextEncoderStream())) ); }); export default app;