{"version":3,"file":"toolCallContinuation.mjs","sources":["../../../src/utils/toolCallContinuation.ts"],"sourcesContent":["/**\n * toolCallContinuation — Utilities for detecting and classifying LLM finish\n * reasons across providers. Used to detect max_tokens truncation and normalize\n * stop reason extraction from provider-specific metadata formats.\n *\n * @module\n */\n\nimport type { AIMessageChunk } from '@langchain/core/messages';\nimport { FinishReasons } from '@/common';\n\n/**\n * Extract the finish/stop reason from an AIMessageChunk's response_metadata.\n * Centralizes provider-specific metadata access patterns.\n *\n * Provider formats:\n * - OpenAI/Azure: `response_metadata.finish_reason`\n * - Anthropic direct: `response_metadata.stop_reason`\n * - Bedrock invoke: `response_metadata.stopReason`\n * - Bedrock streaming: `response_metadata.messageStop.stopReason`\n * - VertexAI/Google: `response_metadata.finishReason`\n *\n * @param chunk - The final accumulated AIMessageChunk\n * @returns The finish reason string, or undefined if not found\n */\nexport function extractFinishReason(\n  chunk: AIMessageChunk | undefined\n): string | undefined {\n  if (!chunk || !('response_metadata' in chunk)) return undefined;\n\n  const meta = chunk.response_metadata as Record<string, unknown>;\n  if (!meta) return undefined;\n\n  // Bedrock streaming nests stopReason inside messageStop: { stopReason: '...' }\n  const messageStop = meta.messageStop as Record<string, unknown> | undefined;\n\n  return (\n    (meta.finish_reason as string | undefined) ?? // OpenAI/Azure\n    (meta.stop_reason as string | undefined) ?? // Anthropic direct API\n    (meta.stopReason as string | undefined) ?? // Bedrock invoke (non-streaming)\n    (messageStop?.stopReason as string | undefined) ?? // Bedrock streaming\n    (meta.finishReason as string | undefined) // VertexAI/Google\n  );\n}\n\n/**\n * Check if a finish reason indicates the response was truncated by token limits.\n *\n * @param reason - The finish reason from extractFinishReason()\n * @returns true if the response was cut short by max_tokens\n */\nexport function isMaxTokensFinish(reason: string | undefined): boolean {\n  if (!reason) return false;\n  return reason === FinishReasons.MAX_TOKENS || reason === FinishReasons.LENGTH;\n}\n"],"names":[],"mappings":";;;AAAA;;;;;;AAMG;AAKH;;;;;;;;;;;;;AAaG;AACG,SAAU,mBAAmB,CACjC,KAAiC,EAAA;IAEjC,IAAI,CAAC,KAAK,IAAI,EAAE,mBAAmB,IAAI,KAAK,CAAC;AAAE,QAAA,OAAO,SAAS;AAE/D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,iBAA4C;AAC/D,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,SAAS;;AAG3B,IAAA,MAAM,WAAW,GAAG,IAAI,CAAC,WAAkD;AAE3E,IAAA,QACG,IAAI,CAAC,aAAoC;QACzC,IAAI,CAAC,WAAkC;QACvC,IAAI,CAAC,UAAiC;QACtC,WAAW,EAAE,UAAiC;QAC9C,IAAI,CAAC,YAAmC;;AAE7C;AAEA;;;;;AAKG;AACG,SAAU,iBAAiB,CAAC,MAA0B,EAAA;AAC1D,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,KAAK;IACzB,OAAO,MAAM,KAAK,aAAa,CAAC,UAAU,IAAI,MAAM,KAAK,aAAa,CAAC,MAAM;AAC/E;;;;"}