UNPKG

@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;" />

271 lines (269 loc) 13.3 kB
require("reflect-metadata"); const require_runtime = require('../../../../_virtual/_rolldown/runtime.cjs'); const require_runtime$1 = require('../../core/runtime.cjs'); const require_client = require('../../intelligence-platform/client.cjs'); const require_json_response = require('../shared/json-response.cjs'); const require_resolve_intelligence_user = require('../shared/resolve-intelligence-user.cjs'); let _copilotkit_shared = require("@copilotkit/shared"); //#region src/v2/runtime/handlers/intelligence/memories.ts const MISSING_INTELLIGENCE_MESSAGE = "Missing CopilotKitIntelligence configuration. Memory operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options."; /** Allowed `kind` vocabulary the platform's memory endpoints accept. */ const MEMORY_KINDS = new Set([ "topical", "episodic", "operational" ]); /** Allowed `scope` vocabulary the platform's memory endpoints accept. */ const MEMORY_SCOPES = new Set(["user", "project"]); /** * Maps a thrown error to a `Response`. * * For a {@link PlatformRequestError}, forward only client-actionable **4xx** * statuses verbatim (e.g. 404 missing/wrong-scope memory, 409 conflict, 422 * unprocessable) so a `useMemories` consumer can branch on them — a flat 500 * would erase that distinction. A platform **5xx** (or any non-4xx / malformed * status) means the runtime is healthy but its dependency failed, so it surfaces * as `502 Bad Gateway` rather than echoing the upstream status as if the runtime * itself broke — and this also avoids a `new Response(..., { status })` * `RangeError` on an out-of-range status. Non-platform throws stay 500. */ function memoryErrorResponse(error, message) { if (error instanceof require_client.PlatformRequestError) { const { status } = error; if (Number.isInteger(status) && status >= 400 && status <= 499) return require_json_response.errorResponse(message, status); return require_json_response.errorResponse(message, 502); } return require_json_response.errorResponse(message, 500); } async function parseJsonBody(request) { try { return await request.json(); } catch (error) { _copilotkit_shared.logger.error({ err: error }, "Malformed JSON in memory request body"); return require_json_response.errorResponse("Invalid request body", 400); } } /** * Extracts and validates the create/supersede body fields the platform's * memory endpoints require. Returns a `Response` (400) on invalid input. */ function parseMemoryBody(body) { const { content, kind, scope, sourceThreadIds } = body; if (typeof content !== "string" || typeof kind !== "string") return require_json_response.errorResponse("Memory requires string `content` and `kind`", 400); if (!MEMORY_KINDS.has(kind)) return require_json_response.errorResponse("Memory `kind` must be one of: topical, episodic, operational", 400); if (scope !== void 0 && typeof scope !== "string") return require_json_response.errorResponse("Memory `scope` must be a string when provided", 400); if (typeof scope === "string" && !MEMORY_SCOPES.has(scope)) return require_json_response.errorResponse("Memory `scope` must be one of: user, project", 400); if (sourceThreadIds !== void 0 && (!Array.isArray(sourceThreadIds) || !sourceThreadIds.every((id) => typeof id === "string"))) return require_json_response.errorResponse("Memory `sourceThreadIds` must be an array of strings when provided", 400); return { content, kind, ...typeof scope === "string" ? { scope } : {}, ...Array.isArray(sourceThreadIds) ? { sourceThreadIds } : {} }; } /** * Validates the recall body: `query` required non-empty string (trimmed); * `limit` optional finite positive integer; `scope` optional and in the known * scopes. Returns a 400 Response on invalid input. The returned `query` is the * trimmed value so a whitespace-padded query is never forwarded to the platform. */ function parseRecallBody(body) { const { query, limit, scope } = body; const trimmedQuery = typeof query === "string" ? query.trim() : query; if (typeof trimmedQuery !== "string" || trimmedQuery.length === 0) return require_json_response.errorResponse("Recall requires a non-empty string `query`", 400); if (limit !== void 0 && !(typeof limit === "number" && Number.isInteger(limit) && limit > 0)) return require_json_response.errorResponse("Recall `limit` must be a positive integer", 400); if (scope !== void 0 && typeof scope !== "string") return require_json_response.errorResponse("Recall `scope` must be a string when provided", 400); if (typeof scope === "string" && !MEMORY_SCOPES.has(scope)) return require_json_response.errorResponse("Recall `scope` must be one of: user, project", 400); return { query: trimmedQuery, ...typeof limit === "number" ? { limit } : {}, ...typeof scope === "string" ? { scope } : {} }; } /** * Lists the resolved user's long-term memories via the Intelligence platform. * * Mirrors {@link handleListThreads}: requires a `CopilotKitIntelligence` * runtime, resolves the user with `identifyUser` (never trusting a * client-supplied id), and proxies to the platform's `GET /api/memories` * with the project API key + resolved user. The `?includeInvalidated=true` * query is forwarded so callers can opt into retired rows. The response is * the platform's `{ memories }` envelope, which the client memory store * consumes directly. */ async function handleListMemories({ runtime, request }) { if (require_runtime$1.isIntelligenceRuntime(runtime)) try { const includeInvalidated = new URL(request.url).searchParams.get("includeInvalidated") === "true"; const user = await require_resolve_intelligence_user.resolveIntelligenceUser({ runtime, request }); if (require_json_response.isHandlerResponse(user)) return user; const data = await runtime.intelligence.listMemories({ userId: user.id, ...includeInvalidated ? { includeInvalidated: true } : {} }); if (data == null || typeof data !== "object" || !Array.isArray(data.memories)) { _copilotkit_shared.logger.error({ data }, "listMemories: platform returned a response without a `memories` array"); return require_json_response.errorResponse("Memory platform returned an invalid list response", 502); } return Response.json(data); } catch (error) { _copilotkit_shared.logger.error({ err: error }, "Error listing memories"); return memoryErrorResponse(error, "Failed to list memories"); } return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422); } /** * Semantically recalls the resolved user's memories via the platform (`POST * /api/memories/recall`, hybrid RAG). Mirrors {@link handleListMemories}: * requires a `CopilotKitIntelligence` runtime, resolves the user with * `identifyUser` (never a client-supplied id), proxies with the project API * key + resolved user. Body `{ query, limit?, scope? }`; response `{ memories }`, * each optionally carrying `score`. */ async function handleRecallMemories({ runtime, request }) { if (!require_runtime$1.isIntelligenceRuntime(runtime)) return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422); try { const body = await parseJsonBody(request); if (require_json_response.isHandlerResponse(body)) return body; const fields = parseRecallBody(body); if (require_json_response.isHandlerResponse(fields)) return fields; const user = await require_resolve_intelligence_user.resolveIntelligenceUser({ runtime, request }); if (require_json_response.isHandlerResponse(user)) return user; const data = await runtime.intelligence.recallMemories({ userId: user.id, ...fields }); if (data == null || typeof data !== "object" || !Array.isArray(data.memories)) { _copilotkit_shared.logger.error({ data }, "recallMemories: platform returned a response without a `memories` array"); return require_json_response.errorResponse("Memory platform returned an invalid recall response", 502); } return Response.json(data); } catch (error) { _copilotkit_shared.logger.error({ err: error }, "Error recalling memories"); return memoryErrorResponse(error, "Failed to recall memories"); } } /** * Mints memory-realtime join credentials (platform `POST * /api/memories/subscribe`). Mirrors {@link handleSubscribeToThreads}: requires * a `CopilotKitIntelligence` runtime and resolves the user with `identifyUser` * (never a client-supplied id). Returns `{ joinToken, joinCode }` — memory needs * the `joinCode` here (unlike threads, where it rides the thread-list response) * because the client builds the `user_meta:memories:<joinCode>` channel topic * from it. * * When the platform also resolves a project scope, the response additionally * carries `projectJoinToken` / `projectJoinCode`, which the client uses to open * a second `project_meta:memories:<projectJoinCode>` channel. These are * optional: absent project scope → both fields are omitted (silent-degrade * contract; the client opens only the user channel). */ async function handleSubscribeToMemories({ runtime, request }) { if (require_runtime$1.isIntelligenceRuntime(runtime)) try { const user = await require_resolve_intelligence_user.resolveIntelligenceUser({ runtime, request }); if (require_json_response.isHandlerResponse(user)) return user; const credentials = await runtime.intelligence.ɵsubscribeToMemories({ userId: user.id }); return Response.json({ joinToken: credentials.joinToken, joinCode: credentials.joinCode, ...credentials.projectJoinToken !== void 0 ? { projectJoinToken: credentials.projectJoinToken } : {}, ...credentials.projectJoinCode !== void 0 ? { projectJoinCode: credentials.projectJoinCode } : {} }); } catch (error) { _copilotkit_shared.logger.error({ err: error }, "Error subscribing to memories"); return memoryErrorResponse(error, "Failed to subscribe to memories"); } return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422); } /** * Creates a memory for the resolved user (platform `POST /api/memories`). * Identity comes from `identifyUser`, never the request body. Returns 201 * with the stored memory (the client store applies it server-authoritatively). */ async function handleCreateMemory({ runtime, request }) { if (!require_runtime$1.isIntelligenceRuntime(runtime)) return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422); try { const body = await parseJsonBody(request); if (require_json_response.isHandlerResponse(body)) return body; const fields = parseMemoryBody(body); if (require_json_response.isHandlerResponse(fields)) return fields; const user = await require_resolve_intelligence_user.resolveIntelligenceUser({ runtime, request }); if (require_json_response.isHandlerResponse(user)) return user; const data = await runtime.intelligence.createMemory({ userId: user.id, ...fields }); return Response.json(data, { status: 201 }); } catch (error) { _copilotkit_shared.logger.error({ err: error }, "Error creating memory"); return memoryErrorResponse(error, "Failed to create memory"); } } /** * Supersedes a memory (platform `PATCH /api/memories/:id`): retires `:id` and * inserts the new content atomically; the response carries `retiredId`. */ async function handleUpdateMemory({ runtime, request, memoryId }) { if (!require_runtime$1.isIntelligenceRuntime(runtime)) return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422); try { const body = await parseJsonBody(request); if (require_json_response.isHandlerResponse(body)) return body; const fields = parseMemoryBody(body); if (require_json_response.isHandlerResponse(fields)) return fields; const user = await require_resolve_intelligence_user.resolveIntelligenceUser({ runtime, request }); if (require_json_response.isHandlerResponse(user)) return user; const data = await runtime.intelligence.updateMemory({ userId: user.id, id: memoryId, ...fields }); return Response.json(data); } catch (error) { _copilotkit_shared.logger.error({ err: error }, "Error updating memory"); return memoryErrorResponse(error, "Failed to update memory"); } } /** * Retires (forgets) a memory (platform `DELETE /api/memories/:id`). Non-lossy * on the platform side; returns 204. */ async function handleRemoveMemory({ runtime, request, memoryId }) { if (!require_runtime$1.isIntelligenceRuntime(runtime)) return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422); try { const user = await require_resolve_intelligence_user.resolveIntelligenceUser({ runtime, request }); if (require_json_response.isHandlerResponse(user)) return user; await runtime.intelligence.removeMemory({ userId: user.id, id: memoryId }); return new Response(null, { status: 204 }); } catch (error) { _copilotkit_shared.logger.error({ err: error }, "Error removing memory"); return memoryErrorResponse(error, "Failed to remove memory"); } } //#endregion exports.handleCreateMemory = handleCreateMemory; exports.handleListMemories = handleListMemories; exports.handleRecallMemories = handleRecallMemories; exports.handleRemoveMemory = handleRemoveMemory; exports.handleSubscribeToMemories = handleSubscribeToMemories; exports.handleUpdateMemory = handleUpdateMemory; //# sourceMappingURL=memories.cjs.map