@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;" />
1 lines • 6.15 kB
Source Map (JSON)
{"version":3,"file":"annotate.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/intelligence/annotate.ts"],"sourcesContent":["import { logger } from \"@copilotkit/shared\";\nimport type {\n CopilotIntelligenceRuntimeLike,\n CopilotRuntimeLike,\n} from \"../../core/runtime\";\nimport { isIntelligenceRuntime } from \"../../core/runtime\";\nimport { PlatformRequestError } from \"../../intelligence-platform/client\";\nimport { errorResponse, isHandlerResponse } from \"../shared/json-response\";\nimport { resolveIntelligenceUser } from \"../shared/resolve-intelligence-user\";\n\ninterface AnnotateHandlerParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n}\n\ninterface AnnotateBody {\n /** Discriminator identifying the annotation type (e.g. `\"user_action\"`). */\n type: string;\n /** Type-specific payload. Shape varies by `type`. */\n payload?: unknown;\n /** The thread the annotation is associated with. */\n threadId: string;\n /** Caller-supplied idempotency key. Optional — platform auto-generates one when absent. */\n clientEventId?: string;\n /** ISO-8601 client-asserted timestamp. Defaults to server NOW() when absent. */\n occurredAt?: string;\n}\n\nasync function parseJsonBody(\n request: Request,\n): Promise<Record<string, unknown> | Response> {\n try {\n return (await request.json()) as Record<string, unknown>;\n } catch (error) {\n logger.error({ err: error }, \"Malformed JSON in annotate body\");\n return errorResponse(\"Invalid request body\", 400);\n }\n}\n\nfunction requireIntelligenceRuntime(\n runtime: CopilotRuntimeLike,\n): CopilotIntelligenceRuntimeLike | Response {\n if (!isIntelligenceRuntime(runtime)) {\n return errorResponse(\n \"Missing CopilotKitIntelligence configuration. annotate requires a CopilotKitIntelligence instance to be provided in CopilotRuntime options.\",\n 422,\n );\n }\n return runtime;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\n/**\n * `POST /annotate` handler.\n *\n * Three-tier flow:\n * recordAnnotation() (frontend lib; called by useLearnFromUserAction / useLearningContainers)\n * → POST ${runtimeUrl}/annotate\n * → this handler resolves the Intel user from BFF auth\n * → intelligence.annotate(...)\n * → PUT ${apiUrl}/connector/annotate/:clientEventId\n *\n * The frontend hook may auto-generate a UUID `clientEventId` per call\n * so retries are idempotent end-to-end (the platform collapses to the\n * original row).\n */\nexport async function handleAnnotate({\n runtime,\n request,\n}: AnnotateHandlerParams): Promise<Response> {\n const intelligenceRuntime = requireIntelligenceRuntime(runtime);\n if (isHandlerResponse(intelligenceRuntime)) return intelligenceRuntime;\n\n const body = await parseJsonBody(request);\n if (isHandlerResponse(body)) return body;\n\n const user = await resolveIntelligenceUser({\n runtime: intelligenceRuntime,\n request,\n });\n if (isHandlerResponse(user)) return user;\n\n const parsed = parseAnnotateBody(body);\n if (isHandlerResponse(parsed)) return parsed;\n\n try {\n const result = await intelligenceRuntime.intelligence.annotate({\n userId: user.id,\n threadId: parsed.threadId,\n type: parsed.type,\n payload: parsed.payload,\n clientEventId: parsed.clientEventId,\n occurredAt: parsed.occurredAt,\n });\n return new Response(JSON.stringify(result), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (err) {\n logger.error({ err }, \"annotate: platform call failed\");\n // Forward the platform's HTTP status when it's a client error\n // (4xx) so the SDK author sees an actionable response instead of\n // a generic 502. 5xx and non-platform errors collapse to 502\n // (\"Bad Gateway\") because the upstream is genuinely at fault.\n if (\n err instanceof PlatformRequestError &&\n err.status >= 400 &&\n err.status < 500\n ) {\n return errorResponse(err.message, err.status);\n }\n return errorResponse(\"Failed to annotate\", 502);\n }\n}\n\nfunction parseAnnotateBody(\n body: Record<string, unknown>,\n): AnnotateBody | Response {\n if (!isNonEmptyString(body.threadId)) {\n return errorResponse(\"Valid threadId is required\", 400);\n }\n if (!isNonEmptyString(body.type)) {\n return errorResponse(\"Valid type is required\", 400);\n }\n return {\n type: body.type,\n payload: body.payload,\n threadId: body.threadId,\n clientEventId: isNonEmptyString(body.clientEventId)\n ? body.clientEventId\n : undefined,\n occurredAt: isNonEmptyString(body.occurredAt) ? body.occurredAt : undefined,\n };\n}\n"],"mappings":";;;;;;;;AA4BA,eAAe,cACb,SAC6C;AAC7C,KAAI;AACF,SAAQ,MAAM,QAAQ,MAAM;UACrB,OAAO;AACd,SAAO,MAAM,EAAE,KAAK,OAAO,EAAE,kCAAkC;AAC/D,SAAO,cAAc,wBAAwB,IAAI;;;AAIrD,SAAS,2BACP,SAC2C;AAC3C,KAAI,CAAC,sBAAsB,QAAQ,CACjC,QAAO,cACL,+IACA,IACD;AAEH,QAAO;;AAGT,SAAS,iBAAiB,OAAiC;AACzD,QAAO,OAAO,UAAU,YAAY,MAAM,SAAS;;;;;;;;;;;;;;;;AAiBrD,eAAsB,eAAe,EACnC,SACA,WAC2C;CAC3C,MAAM,sBAAsB,2BAA2B,QAAQ;AAC/D,KAAI,kBAAkB,oBAAoB,CAAE,QAAO;CAEnD,MAAM,OAAO,MAAM,cAAc,QAAQ;AACzC,KAAI,kBAAkB,KAAK,CAAE,QAAO;CAEpC,MAAM,OAAO,MAAM,wBAAwB;EACzC,SAAS;EACT;EACD,CAAC;AACF,KAAI,kBAAkB,KAAK,CAAE,QAAO;CAEpC,MAAM,SAAS,kBAAkB,KAAK;AACtC,KAAI,kBAAkB,OAAO,CAAE,QAAO;AAEtC,KAAI;EACF,MAAM,SAAS,MAAM,oBAAoB,aAAa,SAAS;GAC7D,QAAQ,KAAK;GACb,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,SAAS,OAAO;GAChB,eAAe,OAAO;GACtB,YAAY,OAAO;GACpB,CAAC;AACF,SAAO,IAAI,SAAS,KAAK,UAAU,OAAO,EAAE;GAC1C,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAChD,CAAC;UACK,KAAK;AACZ,SAAO,MAAM,EAAE,KAAK,EAAE,iCAAiC;AAKvD,MACE,eAAe,wBACf,IAAI,UAAU,OACd,IAAI,SAAS,IAEb,QAAO,cAAc,IAAI,SAAS,IAAI,OAAO;AAE/C,SAAO,cAAc,sBAAsB,IAAI;;;AAInD,SAAS,kBACP,MACyB;AACzB,KAAI,CAAC,iBAAiB,KAAK,SAAS,CAClC,QAAO,cAAc,8BAA8B,IAAI;AAEzD,KAAI,CAAC,iBAAiB,KAAK,KAAK,CAC9B,QAAO,cAAc,0BAA0B,IAAI;AAErD,QAAO;EACL,MAAM,KAAK;EACX,SAAS,KAAK;EACd,UAAU,KAAK;EACf,eAAe,iBAAiB,KAAK,cAAc,GAC/C,KAAK,gBACL;EACJ,YAAY,iBAAiB,KAAK,WAAW,GAAG,KAAK,aAAa;EACnE"}