@agentforce/adk
Version:
AgentForce Agent Development Kit - A powerful framework for building AI agents and servers
207 lines (179 loc) • 7.65 kB
text/typescript
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;
}
}