{"version":3,"file":"contextAnalytics.cjs","sources":["../../../src/utils/contextAnalytics.ts"],"sourcesContent":["/**\n * Context Analytics Utility\n *\n * Provides context analytics data for observability/traces.\n * No console logging - just data structures for event emission.\n */\n\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { TokenCounter } from '@/types/run';\n\n/** Context analytics data for traces */\nexport interface ContextAnalytics {\n  /** Total messages in context */\n  messageCount: number;\n  /** Total tokens in context */\n  totalTokens: number;\n  /** Maximum allowed context tokens */\n  maxContextTokens?: number;\n  /** Instruction/system tokens */\n  instructionTokens?: number;\n  /** Context utilization percentage (0-100) */\n  utilizationPercent?: number;\n  /** Breakdown by message type */\n  breakdown?: Record<string, { tokens: number; percent: number }>;\n}\n\n/**\n * Resolve the message type string from a BaseMessage or message-like object.\n *\n * Uses the same duck-typing contract as LangChain's `isBaseMessage`:\n * prefer `_getType()` (internal), fallback to `getType()` (public).\n * This handles cross-package BaseMessage instances where `instanceof`\n * checks may fail but prototype methods are still present.\n */\nfunction resolveMessageType(msg: BaseMessage): string {\n  if (typeof msg._getType === 'function') {\n    return msg._getType();\n  }\n  if (typeof msg.getType === 'function') {\n    return msg.getType();\n  }\n  // Last resort: infer from known LangChain serialization fields\n  return (\n    ((msg as unknown as Record<string, unknown>).type as string) ?? 'unknown'\n  );\n}\n\n/**\n * Build context analytics for traces (no logging).\n *\n * @param messages - Array of BaseMessage (may originate from different @langchain/core installations)\n * @param options  - Token counting and context budget info\n */\nexport function buildContextAnalytics(\n  messages: BaseMessage[],\n  options: {\n    tokenCounter?: TokenCounter;\n    maxContextTokens?: number;\n    instructionTokens?: number;\n    indexTokenCountMap?: Record<string, number | undefined>;\n  }\n): ContextAnalytics {\n  const {\n    tokenCounter,\n    maxContextTokens,\n    instructionTokens,\n    indexTokenCountMap,\n  } = options;\n\n  let totalTokens = 0;\n  const breakdown: Record<string, { tokens: number; percent: number }> = {};\n\n  for (let i = 0; i < messages.length; i++) {\n    const msg = messages[i];\n    const type = resolveMessageType(msg);\n\n    let tokens = 0;\n    if (indexTokenCountMap && indexTokenCountMap[i] != null) {\n      tokens = indexTokenCountMap[i]!;\n    } else if (tokenCounter) {\n      try {\n        tokens = tokenCounter(msg);\n      } catch {\n        const content =\n          typeof msg.content === 'string'\n            ? msg.content\n            : JSON.stringify(msg.content);\n        tokens = Math.ceil(content.length / 4);\n      }\n    }\n\n    totalTokens += tokens;\n\n    if (!breakdown[type]) {\n      breakdown[type] = { tokens: 0, percent: 0 };\n    }\n    breakdown[type].tokens += tokens;\n  }\n\n  for (const type of Object.keys(breakdown)) {\n    breakdown[type].percent =\n      totalTokens > 0\n        ? Math.round((breakdown[type].tokens / totalTokens) * 1000) / 10\n        : 0;\n  }\n\n  let utilizationPercent: number | undefined;\n  if (maxContextTokens && maxContextTokens > 0) {\n    utilizationPercent =\n      Math.round((totalTokens / maxContextTokens) * 1000) / 10;\n  }\n\n  return {\n    messageCount: messages.length,\n    totalTokens,\n    maxContextTokens,\n    instructionTokens,\n    utilizationPercent,\n    breakdown,\n  };\n}\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAqBH;;;;;;;AAOG;AACH,SAAS,kBAAkB,CAAC,GAAgB,EAAA;AAC1C,IAAA,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,UAAU,EAAE;AACtC,QAAA,OAAO,GAAG,CAAC,QAAQ,EAAE;IACvB;AACA,IAAA,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU,EAAE;AACrC,QAAA,OAAO,GAAG,CAAC,OAAO,EAAE;IACtB;;AAEA,IAAA,QACI,GAA0C,CAAC,IAAe,IAAI,SAAS;AAE7E;AAEA;;;;;AAKG;AACG,SAAU,qBAAqB,CACnC,QAAuB,EACvB,OAKC,EAAA;IAED,MAAM,EACJ,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,GACnB,GAAG,OAAO;IAEX,IAAI,WAAW,GAAG,CAAC;IACnB,MAAM,SAAS,GAAwD,EAAE;AAEzE,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACxC,QAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC;AACvB,QAAA,MAAM,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC;QAEpC,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,kBAAkB,IAAI,kBAAkB,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE;AACvD,YAAA,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAE;QACjC;aAAO,IAAI,YAAY,EAAE;AACvB,YAAA,IAAI;AACF,gBAAA,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC;YAC5B;AAAE,YAAA,MAAM;AACN,gBAAA,MAAM,OAAO,GACX,OAAO,GAAG,CAAC,OAAO,KAAK;sBACnB,GAAG,CAAC;sBACJ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;gBACjC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;YACxC;QACF;QAEA,WAAW,IAAI,MAAM;AAErB,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;AACpB,YAAA,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;QAC7C;AACA,QAAA,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM;IAClC;IAEA,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE;AACzC,QAAA,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO;AACrB,YAAA,WAAW,GAAG;AACZ,kBAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,WAAW,IAAI,IAAI,CAAC,GAAG;kBAC5D,CAAC;IACT;AAEA,IAAA,IAAI,kBAAsC;AAC1C,IAAA,IAAI,gBAAgB,IAAI,gBAAgB,GAAG,CAAC,EAAE;QAC5C,kBAAkB;AAChB,YAAA,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,gBAAgB,IAAI,IAAI,CAAC,GAAG,EAAE;IAC5D;IAEA,OAAO;QACL,YAAY,EAAE,QAAQ,CAAC,MAAM;QAC7B,WAAW;QACX,gBAAgB;QAChB,iBAAiB;QACjB,kBAAkB;QAClB,SAAS;KACV;AACH;;;;"}