Skip to content

Handler Factories

Handler factories create platform-agnostic request handlers that accept a generic HttpEvent and return a JsonResponse (or SseResponse for streaming). Customers provide agent resolution, authentication, and stores; the framework handles request parsing, error handling, and response formatting.

Import

import {
  createAgentExecuteHandler,
  createAgentExecuteStreamHandler,
  createConversationHandler,
  createTraceHandler,
  createKnowledgeBaseAdminHandlers,
  HttpEvent,
  AgentResolver,
  CorsOptions,
} from "@modernpath/agent-framework";

Common Types

HttpEvent

interface HttpEvent {
  pathParameters?: Record<string, string | undefined>;
  queryStringParameters?: Record<string, string | undefined>;
  headers?: Record<string, string | undefined>;
  body?: string | null;
  method?: string;
}

JsonResponse

interface JsonResponse {
  statusCode: number;
  headers: Record<string, string>;
  body: string;
}

CorsOptions

interface CorsOptions {
  origin?: string;        // Default: "*"
  allowHeaders?: string;  // Default: "content-type, authorization"
  allowMethods?: string;  // Default: "POST, OPTIONS"
  maxAgeSeconds?: number; // Default: 600
}

AgentResolver

type AgentResolver = (agentType: string) => BaseAgent;

createAgentExecuteHandler

function createAgentExecuteHandler(opts: {
  resolveAgent: AgentResolver;
  getUserId: (event: HttpEvent) => Promise<number> | number;
  cors?: CorsOptions;
}): (event: HttpEvent) => Promise<JsonResponse>

Creates a handler for synchronous agent execution. Accepts POST requests with an AgentExecuteRequestBody:

interface AgentExecuteRequestBody {
  agentType: string;
  prompt: string;
  parameters?: Record<string, any>;
  history?: AgentContext["history"];
  metadata?: AgentContext["metadata"];
}

Expected path parameters: auditingId

Response: AgentResult on success (200), error on failure (400/500).

createAgentExecuteStreamHandler

function createAgentExecuteStreamHandler(opts: {
  resolveAgent: AgentResolver;
  getUserId: (event: HttpEvent) => Promise<number> | number;
  cors?: CorsOptions;
}): (event: HttpEvent) => Promise<SseResponse>

Creates a streaming (SSE) handler for agent execution. Returns a SseResponse:

interface SseResponse {
  statusCode: number;
  headers: Record<string, string>;
  stream: AsyncIterable<string>;
}

SSE Event Types:

Event Description
{ type: "delta", text: "" } Initial empty delta so UIs can show progress immediately.
{ type: "final", response: AgentResult } The complete agent result.
{ type: "error", error: string } Error message if execution fails.
[DONE] Signals the end of the stream.

createConversationHandler

function createConversationHandler(opts: {
  resolveAgent: (agentName: string) => BaseAgent;
  getUserId: (event: HttpEvent) => Promise<number> | number;
  conversationStore: ConversationStore;
  defaultAgentName?: string;
  defaultParameters?: Record<string, any>;
  maxHistoryMessages?: number; // Default: 50
  cors?: CorsOptions;
}): (event: HttpEvent) => Promise<JsonResponse>

Creates a handler for multi-turn conversation endpoints:

Method Path Parameters Description
POST -- Create conversation. Body: { agentName?, title?, metadata? }
GET -- List conversations. Query: agentName, limit, offset
GET conversationId Get conversation + messages
POST conversationId, subResource: "messages" Send message. Body: { prompt, parameters?, metadata? }
DELETE conversationId Delete conversation

The send-message endpoint loads conversation history, executes the agent, and saves both user and assistant messages.

createTraceHandler

function createTraceHandler(opts: {
  traceStore: TraceStore;
  cors?: CorsOptions;
}): (event: HttpEvent) => Promise<JsonResponse>

Creates a read-only handler for trace/session/config-snapshot endpoints:

Method Path Parameters Description
GET resource: "traces" List traces. Query: agentName, sessionId, userId, status, tags, after, before, limit, offset
GET resource: "traces", resourceId Get trace + spans
GET resource: "sessions" List sessions. Query: userId, tags, limit, offset
GET resource: "sessions", resourceId Get session + its traces
GET resource: "config-snapshots", resourceId Get config snapshot

createKnowledgeBaseAdminHandlers

function createKnowledgeBaseAdminHandlers(opts: {
  gemini: GeminiClient;
  cfg?: KnowledgeBaseAdminServiceConfig;
  cors?: CorsOptions;
}): {
  listStores: (event: HttpEvent) => Promise<JsonResponse>;
  createStore: (event: HttpEvent) => Promise<JsonResponse>;
  deleteStore: (event: HttpEvent) => Promise<JsonResponse>;
  listDocuments: (event: HttpEvent) => Promise<JsonResponse>;
  uploadJson: (event: HttpEvent) => Promise<JsonResponse>;
  deleteDocument: (event: HttpEvent) => Promise<JsonResponse>;
  groundTruthRedirect: (event: HttpEvent) => Promise<JsonResponse>;
}

Returns an object of individual handlers for knowledge base administration. Each handler is a separate function, allowing flexible routing.

Handler Method Path Params Description
listStores GET -- List all knowledge base stores
createStore POST -- Create a store. Body: { displayName }
deleteStore DELETE storeName Delete a store
listDocuments GET storeName List documents in a store
uploadJson POST storeName Upload document. Body: { filename, mimeType, contentBase64 }
deleteDocument DELETE storeName, docName Delete a document. Query: deleteGroundTruth
groundTruthRedirect GET storeName, docName Redirect to ground truth download URL

Code Example

import {
  createAgentExecuteHandler,
  createTraceHandler,
  createConversationHandler,
} from "@modernpath/agent-framework";

// Agent execution
const agentHandler = createAgentExecuteHandler({
  resolveAgent: (type) => {
    if (type === "incident-resolver") return incidentResolverAgent;
    if (type === "qa-chat") return qaChatAgent;
    throw new Error(`Unknown agent: ${type}`);
  },
  getUserId: async (event) => {
    const token = event.headers?.authorization?.replace("Bearer ", "");
    return await verifyToken(token);
  },
  cors: { origin: "https://app.example.com" },
});

// Trace queries
const traceHandler = createTraceHandler({
  traceStore: firestoreTraceStore,
});

// Conversations
const conversationHandler = createConversationHandler({
  resolveAgent: (name) => agentFactory.resolve(name),
  getUserId: (event) => parseInt(event.headers?.["x-user-id"] ?? "0"),
  conversationStore: firestoreConversationStore,
  defaultAgentName: "qa-chat",
  maxHistoryMessages: 50,
});