@copilotkit/runtime
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
398 lines (396 loc) • 13.6 kB
JavaScript
require("reflect-metadata");
const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
const require_runtime$1 = require('./runtime.cjs');
const require_channel_manager = require('./channel-manager.cjs');
const require_hooks = require('./hooks.cjs');
const require_fetch_cors = require('./fetch-cors.cjs');
const require_fetch_router = require('./fetch-router.cjs');
const require_middleware = require('./middleware.cjs');
const require_handle_run = require('../handlers/handle-run.cjs');
const require_handle_suggest = require('../handlers/handle-suggest.cjs');
const require_handle_connect = require('../handlers/handle-connect.cjs');
const require_handle_stop = require('../handlers/handle-stop.cjs');
const require_get_runtime_info = require('../handlers/get-runtime-info.cjs');
const require_handle_transcribe = require('../handlers/handle-transcribe.cjs');
const require_handle_debug_events = require('../handlers/handle-debug-events.cjs');
const require_threads = require('../handlers/intelligence/threads.cjs');
const require_memories = require('../handlers/intelligence/memories.cjs');
const require_annotate = require('../handlers/intelligence/annotate.cjs');
const require_single_route_helpers = require('../endpoints/single-route-helpers.cjs');
const require_instance_created = require('../telemetry/instance-created.cjs');
let _copilotkit_shared = require("@copilotkit/shared");
//#region src/v2/runtime/core/fetch-handler.ts
/**
* Managed Channel managers keyed by runtime instance. Guarantees a single
* manager (and thus a single activation) per runtime: creating the handler more
* than once for the same runtime reuses the existing manager instead of
* constructing a second one.
*/
const channelManagers = /* @__PURE__ */ new WeakMap();
/**
* Look up (or lazily CREATE) the {@link ChannelManager} for an Intelligence
* runtime. First creation constructs the manager and caches it; subsequent
* lookups reuse the cached instance so there is exactly one manager per runtime.
*
* Activation is NOT triggered here. Constructing the manager opens no
* transport — the persistent gateway socket is opened lazily on the first
* {@link ChannelManager.ready} call (see the factory TSDoc). This keeps the
* generic Fetch handler serverless/edge-safe: creating it (e.g. at
* Cloudflare-Worker module scope or per Next.js App Router isolate) never
* performs network I/O and never mints a listener the host cannot own.
*
* Caching the un-activated manager is correct: a later `ready()` activates it
* once (idempotently), and an up-front misconfiguration (duplicate/missing
* channel names) surfaces as a rejected `ready()` rather than a throw at
* creation. A manager that has been {@link ChannelManager.stop}ped stays
* stopped on reuse — its latches short-circuit any later `activate()`/`ready()`.
*
* @param runtime - The Intelligence runtime whose Channels the manager drives.
* @param engine - Optional injected activation engine (test seam); when
* omitted the manager uses its default Realtime Gateway engine.
* @returns The runtime's (un-activated) Channel manager.
*/
function getOrCreateChannelManager(runtime, engine) {
const existing = channelManagers.get(runtime);
if (existing) return existing;
const manager = new require_channel_manager.ChannelManager({
intelligence: runtime.intelligence,
channels: runtime.channels,
log: (msg, meta) => _copilotkit_shared.logger.warn(meta instanceof Error ? { err: meta } : { meta }, msg),
...engine ? { activateChannel: engine } : {}
});
channelManagers.set(runtime, manager);
return manager;
}
function createCopilotRuntimeHandler(options) {
const { runtime, basePath, mode = "multi-route", cors, hooks } = options;
require_instance_created.fireInstanceCreatedTelemetry({ runtime });
const corsConfig = resolveCorsConfig(cors);
const handler = async (request) => {
const path = new URL(request.url, "http://localhost").pathname;
const requestOrigin = request.headers.get("origin");
const baseCtx = {
request,
path,
runtime
};
let route;
try {
if (corsConfig) {
const preflight = require_fetch_cors.handleCors(request, corsConfig);
if (preflight) return preflight;
}
request = await require_hooks.runOnRequest(hooks, {
...baseCtx,
request
});
try {
const maybeModified = await require_middleware.callBeforeRequestMiddleware({
runtime,
request,
path
});
if (maybeModified) request = maybeModified;
} catch (mwError) {
_copilotkit_shared.logger.error({
err: mwError,
url: request.url,
path
}, "Error running before request middleware");
if (mwError instanceof Response) return maybeAddCors(mwError, corsConfig, requestOrigin);
throw mwError;
}
let response;
if (mode === "single-route") {
const resolved = await resolveSingleRoute(request, basePath, path);
route = resolved.route;
const { methodCall } = resolved;
request = await require_hooks.runOnBeforeHandler(hooks, {
request,
path,
runtime,
route
});
if (route.method === "agent/run" || route.method === "agent/suggest" || route.method === "agent/connect" || route.method === "transcribe") request = require_single_route_helpers.createJsonRequest(request, methodCall.body);
response = await dispatchRoute(runtime, request, route, { threadEndpointsEnabled: false });
} else {
const matched = require_fetch_router.matchRoute(path, basePath);
if (!matched) throw jsonResponse({ error: "Not found" }, 404);
const methodError = validateHttpMethod(request.method, matched);
if (methodError) {
route = matched;
throw methodError;
}
route = matched;
request = await require_hooks.runOnBeforeHandler(hooks, {
request,
path,
runtime,
route
});
response = await dispatchRoute(runtime, request, route, { threadEndpointsEnabled: true });
}
response = await require_hooks.runOnResponse(hooks, {
request,
response,
path,
runtime,
route
});
response = maybeAddCors(response, corsConfig, requestOrigin);
require_middleware.callAfterRequestMiddleware({
runtime,
response: response.clone(),
path
}).catch((error) => {
_copilotkit_shared.logger.error({
err: error,
url: request.url,
path
}, "Error running after request middleware");
});
return response;
} catch (error) {
if (error instanceof Response) return maybeAddCors(await require_hooks.runOnResponse(hooks, {
request,
response: error,
path,
runtime,
route: route ?? { method: "info" }
}), corsConfig, requestOrigin);
try {
const errorResponse = await require_hooks.runOnError(hooks, {
request,
error,
path,
runtime,
route
});
if (errorResponse) return maybeAddCors(errorResponse, corsConfig, requestOrigin);
} catch (hookError) {
_copilotkit_shared.logger.error({
err: hookError,
originalErr: error,
url: request.url,
path
}, "onError hook threw");
}
_copilotkit_shared.logger.error({
err: error,
url: request.url,
path
}, "Unhandled error in CopilotKit runtime handler");
return maybeAddCors(jsonResponse({ error: "internal_error" }, 500), corsConfig, requestOrigin);
}
};
if (require_runtime$1.isIntelligenceRuntime(runtime) && runtime.channels && runtime.channels.length > 0 && options.activateChannels !== false) handler.channels = getOrCreateChannelManager(runtime, options.__channelEngine);
return handler;
}
function dispatchRoute(runtime, request, route, options) {
switch (route.method) {
case "agent/run": return require_handle_run.handleRunAgent({
runtime,
request,
agentId: route.agentId
});
case "agent/suggest": return require_handle_suggest.handleSuggestAgent({
runtime,
request,
agentId: route.agentId
});
case "agent/connect": return require_handle_connect.handleConnectAgent({
runtime,
request,
agentId: route.agentId
});
case "agent/stop": return require_handle_stop.handleStopAgent({
runtime,
request,
agentId: route.agentId,
threadId: route.threadId
});
case "info": return require_get_runtime_info.handleGetRuntimeInfo({
runtime,
request,
threadEndpointsEnabled: options.threadEndpointsEnabled
});
case "transcribe": return require_handle_transcribe.handleTranscribe({
runtime,
request
});
case "threads/clear": return Promise.resolve(require_threads.handleClearThreads({
runtime,
request
}));
case "threads/list": return require_threads.handleListThreads({
runtime,
request
});
case "memories/list": return request.method.toUpperCase() === "POST" ? require_memories.handleCreateMemory({
runtime,
request
}) : require_memories.handleListMemories({
runtime,
request
});
case "memories/subscribe": return require_memories.handleSubscribeToMemories({
runtime,
request
});
case "memories/mutate": return request.method.toUpperCase() === "DELETE" ? require_memories.handleRemoveMemory({
runtime,
request,
memoryId: route.memoryId
}) : require_memories.handleUpdateMemory({
runtime,
request,
memoryId: route.memoryId
});
case "threads/subscribe": return require_threads.handleSubscribeToThreads({
runtime,
request
});
case "threads/update":
if (request.method.toUpperCase() === "DELETE") return require_threads.handleDeleteThread({
runtime,
request,
threadId: route.threadId
});
return require_threads.handleUpdateThread({
runtime,
request,
threadId: route.threadId
});
case "threads/archive": return require_threads.handleArchiveThread({
runtime,
request,
threadId: route.threadId
});
case "threads/messages": return require_threads.handleGetThreadMessages({
runtime,
request,
threadId: route.threadId
});
case "threads/events": return require_threads.handleGetThreadEvents({
runtime,
request,
threadId: route.threadId
});
case "threads/state": return require_threads.handleGetThreadState({
runtime,
request,
threadId: route.threadId
});
case "annotate": return require_annotate.handleAnnotate({
runtime,
request
});
case "cpk-debug-events": return Promise.resolve(require_handle_debug_events.handleDebugEvents({
runtime,
request
}));
default: throw jsonResponse({
error: "Not found",
method: route.method
}, 404);
}
}
async function resolveSingleRoute(request, basePath, pathname) {
if (basePath) {
const normalizedBase = basePath.length > 1 && basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
if (!pathname.startsWith(normalizedBase)) throw jsonResponse({ error: "Not found" }, 404);
}
if (request.method !== "POST") throw jsonResponse({ error: "Method not allowed" }, 405, { Allow: "POST" });
const methodCall = await require_single_route_helpers.parseMethodCall(request);
let route;
switch (methodCall.method) {
case "agent/run":
route = {
method: "agent/run",
agentId: require_single_route_helpers.expectString(methodCall.params, "agentId")
};
break;
case "agent/suggest":
route = {
method: "agent/suggest",
agentId: require_single_route_helpers.expectString(methodCall.params, "agentId")
};
break;
case "agent/connect":
route = {
method: "agent/connect",
agentId: require_single_route_helpers.expectString(methodCall.params, "agentId")
};
break;
case "agent/stop":
route = {
method: "agent/stop",
agentId: require_single_route_helpers.expectString(methodCall.params, "agentId"),
threadId: require_single_route_helpers.expectString(methodCall.params, "threadId")
};
break;
case "info":
route = { method: "info" };
break;
case "transcribe":
route = { method: "transcribe" };
break;
default: {
const _exhaustive = methodCall.method;
throw jsonResponse({
error: "Not found",
method: _exhaustive
}, 404);
}
}
return {
route,
methodCall
};
}
function validateHttpMethod(httpMethod, route) {
const method = httpMethod.toUpperCase();
switch (route.method) {
case "info":
case "threads/list":
case "threads/messages":
case "threads/events":
case "threads/state":
case "cpk-debug-events":
if (method === "GET") return null;
return jsonResponse({ error: "Method not allowed" }, 405, { Allow: "GET" });
case "memories/list":
if (method === "GET" || method === "POST") return null;
return jsonResponse({ error: "Method not allowed" }, 405, { Allow: "GET, POST" });
case "memories/mutate":
if (method === "PATCH" || method === "DELETE") return null;
return jsonResponse({ error: "Method not allowed" }, 405, { Allow: "PATCH, DELETE" });
case "threads/update":
if (method === "PATCH" || method === "DELETE") return null;
return jsonResponse({ error: "Method not allowed" }, 405, { Allow: "PATCH, DELETE" });
default:
if (method === "POST") return null;
return jsonResponse({ error: "Method not allowed" }, 405, { Allow: "POST" });
}
}
function resolveCorsConfig(cors) {
if (!cors) return null;
if (cors === true) return {};
return cors;
}
function maybeAddCors(response, config, requestOrigin) {
if (!config) return response;
return require_fetch_cors.addCorsHeaders(response, config, requestOrigin);
}
function jsonResponse(body, status, extraHeaders) {
return new Response(JSON.stringify(body), {
status,
headers: {
"Content-Type": "application/json",
...extraHeaders
}
});
}
//#endregion
exports.createCopilotRuntimeHandler = createCopilotRuntimeHandler;
//# sourceMappingURL=fetch-handler.cjs.map