UNPKG

@agentforce/adk

Version:

AgentForce Agent Development Kit - A powerful framework for building AI agents and servers

207 lines (179 loc) 7.65 kB
import type { AgentForceAgent } from "../agent"; import { Hono } from "hono"; /** * Starts an HTTP server for the agent using Hono (terminal method) * Compatible with Bun, Node.js, and Deno runtimes via Hono * @param this - The AgentForceAgent instance (bound context) * @param host - The host address to bind the server to (default: "0.0.0.0") * @param port - The port number to listen on (default: 3000) * @returns {Promise<void>} This is a terminal method that starts the server - does not return the agent */ export async function serve(this: AgentForceAgent, host: string = "0.0.0.0", port: number = 3000): Promise<void> { // Validate inputs if (!host || typeof host !== "string") { throw new Error("Host must be a non-empty string"); } if (typeof port !== "number" || port <= 0 || port > 65535) { throw new Error("Port must be a valid number between 1 and 65535"); } // Get agent information for logging const agentName = this.getName(); const currentModel = this.getModel(); const currentProvider = this.getProvider(); // Get the agent's logger instance const logger = this.getLogger(); // Log server startup information using structured logging logger.debug({ agentName, provider: currentProvider, model: currentModel, host, port, action: "server_starting", }, `Starting server for agent: ${agentName}`); // Create Hono app with HTTP request logging middleware const app = new Hono(); // Add middleware to log all HTTP requests app.use("*", async (c, next) => { logger.info({ httpMethod: c.req.method, route: c.req.path, }); await next(); }); // Default route - enhanced to handle prompt query parameter app.get("/", async (c) => { const prompt = c.req.query("prompt"); if (prompt) { try { // Execute the agent with the provided prompt const response = await this.prompt(prompt).getResponse(); return c.json({ status: "ok", agent: agentName, prompt: prompt, response: response, }); } catch (error) { logger.error({ agentName, prompt, error: error instanceof Error ? error.message : String(error), action: "prompt_execution_failed", }, "Failed to execute agent with prompt"); return c.json({ status: "error", agent: agentName, prompt: prompt, error: error instanceof Error ? error.message : "Unknown error", }, 500); } } return c.json({ status: "ok", agent: agentName }); }); // Start the server using runtime-appropriate method try { if (typeof Bun !== "undefined") { // Bun runtime const server = Bun.serve({ hostname: host, port: port, fetch: app.fetch, }); logger.info({ hostname: server.hostname, port: server.port, runtime: "bun", action: "server_started", }, `🚀 Agent server running at http://${server.hostname}:${server.port}`); } else if (typeof (globalThis as any).Deno !== "undefined") { // Deno runtime (globalThis as any).Deno.serve({ hostname: host, port: port, }, app.fetch); logger.info({ hostname: host, port: port, runtime: "deno", action: "server_started", }, `🚀 Agent server running at http://${host}:${port}`); } else { // Node.js runtime - use built-in http module const http = await import("node:http"); const server = http.createServer(async (req, res) => { // Convert Node.js request to Web API Request const url = `http://${host}:${port}${req.url || "/"}`; const headers: Record<string, string> = {}; // Copy headers from Node.js request for (const [key, value] of Object.entries(req.headers)) { if (typeof value === "string") { headers[key] = value; } else if (Array.isArray(value)) { headers[key] = value.join(", "); } } // Handle request body for POST/PUT/PATCH let body: string | undefined; if (req.method === "POST" || req.method === "PUT" || req.method === "PATCH") { const chunks: Buffer[] = []; for await (const chunk of req) { chunks.push(chunk); } body = Buffer.concat(chunks).toString(); } // Create Web API Request const request = new Request(url, { method: req.method || "GET", headers, body: body, }); try { // Process request with Hono app const response = await app.fetch(request); // Set response status and headers res.statusCode = response.status; response.headers.forEach((value, key) => { res.setHeader(key, value); }); // Handle response body if (response.body) { const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; if (value) { res.write(value); } } } res.end(); } catch (error) { logger.error("Error processing request:", error); res.statusCode = 500; res.setHeader("Content-Type", "application/json"); res.write(JSON.stringify({ error: "Internal Server Error" })); res.end(); } }); server.listen(port, host, () => { logger.info({ hostname: host, port: port, runtime: "nodejs", action: "server_started", }, `🚀 Agent server running at http://${host}:${port}`); }); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error({ agentName, error: errorMessage, action: "server_start_failed", }, `Failed to start agent server: ${errorMessage}`); // Since serve() returns Promise<void>, we can't return an error message // The error is already logged, so we just return to allow graceful handling return; } }