{"version":3,"file":"report-stats.mjs","sources":["../../src/report-stats.ts"],"sourcesContent":["import type { AIUsageInfo, IReportActionDump } from './types';\n\nexport interface ReportModelUsageSummary {\n  modelName: string;\n  callCount: number;\n  promptTokens: number;\n  cachedInputTokens: number;\n  completionTokens: number;\n  totalTokens: number;\n  modelCallTimeMs: number;\n}\n\nexport interface ReportTokenSummary {\n  promptTokens: number;\n  cachedInputTokens: number;\n  completionTokens: number;\n  totalTokens: number;\n}\n\nexport interface ReportTimingSummary {\n  /**\n   * Elapsed time between the earliest and latest recorded task timestamps.\n   * Falls back to `wallTimeFallbackMs` only when no task timestamps exist.\n   */\n  wallTimeMs?: number;\n  wallTimeStart?: number;\n  wallTimeEnd?: number;\n  wallTimeSource: 'task-timestamps' | 'fallback' | 'unavailable';\n  /** Sum of every recorded model request duration. */\n  modelCallTimeMs: number;\n  modelCallCount: number;\n}\n\nexport interface ReportSummary {\n  timing: ReportTimingSummary;\n  tokens: ReportTokenSummary;\n  models: ReportModelUsageSummary[];\n}\n\nexport interface CollectReportSummaryOptions {\n  /** Generic fallback for reports without task timestamps, such as test time. */\n  wallTimeFallbackMs?: number;\n}\n\nconst usageKeys = [\n  'intent',\n  'slot',\n  'model_name',\n  'response_model_name',\n  'model_description',\n  'prompt_tokens',\n  'cached_input',\n  'completion_tokens',\n  'total_tokens',\n  'time_cost',\n  'request_id',\n] as const;\n\nfunction finiteNumber(value: unknown): number | undefined {\n  return typeof value === 'number' && Number.isFinite(value)\n    ? value\n    : undefined;\n}\n\nfunction usageValue(usage: AIUsageInfo, key: string): number {\n  return Math.max(0, finiteNumber(usage[key]) ?? 0);\n}\n\nfunction hasUsage(usage: AIUsageInfo | undefined): usage is AIUsageInfo {\n  return Boolean(\n    usage &&\n      usageKeys.some((key) => usage[key] !== undefined && usage[key] !== null),\n  );\n}\n\nfunction usageTotalTokens(usage: AIUsageInfo): number {\n  const explicitTotal = finiteNumber(usage.total_tokens);\n  if (explicitTotal !== undefined && explicitTotal > 0) {\n    return explicitTotal;\n  }\n  return (\n    usageValue(usage, 'prompt_tokens') + usageValue(usage, 'completion_tokens')\n  );\n}\n\nfunction usageModelName(usage: AIUsageInfo): string {\n  return (\n    usage.model_name ||\n    usage.response_model_name ||\n    usage.model_description ||\n    usage.intent ||\n    'Unknown'\n  );\n}\n\nexport function collectReportSummary(\n  report: Pick<IReportActionDump, 'executions'>,\n  options: CollectReportSummaryOptions = {},\n): ReportSummary {\n  if (!report || !Array.isArray(report.executions)) {\n    throw new Error('collectReportSummary: report.executions must be an array');\n  }\n\n  let earliestTimestamp: number | undefined;\n  let latestTimestamp: number | undefined;\n  let modelCallCount = 0;\n  let modelCallTimeMs = 0;\n  const models = new Map<string, ReportModelUsageSummary>();\n\n  const addTimestamp = (value: unknown) => {\n    const timestamp = finiteNumber(value);\n    if (timestamp === undefined) return;\n    earliestTimestamp =\n      earliestTimestamp === undefined\n        ? timestamp\n        : Math.min(earliestTimestamp, timestamp);\n    latestTimestamp =\n      latestTimestamp === undefined\n        ? timestamp\n        : Math.max(latestTimestamp, timestamp);\n  };\n\n  const addUsage = (usage: AIUsageInfo | undefined) => {\n    if (!hasUsage(usage)) return;\n\n    const modelName = usageModelName(usage);\n    const current = models.get(modelName) ?? {\n      modelName,\n      callCount: 0,\n      promptTokens: 0,\n      cachedInputTokens: 0,\n      completionTokens: 0,\n      totalTokens: 0,\n      modelCallTimeMs: 0,\n    };\n    const callTimeMs = usageValue(usage, 'time_cost');\n\n    models.set(modelName, {\n      modelName,\n      callCount: current.callCount + 1,\n      promptTokens: current.promptTokens + usageValue(usage, 'prompt_tokens'),\n      cachedInputTokens:\n        current.cachedInputTokens + usageValue(usage, 'cached_input'),\n      completionTokens:\n        current.completionTokens + usageValue(usage, 'completion_tokens'),\n      totalTokens: current.totalTokens + usageTotalTokens(usage),\n      modelCallTimeMs: current.modelCallTimeMs + callTimeMs,\n    });\n    modelCallCount += 1;\n    modelCallTimeMs += callTimeMs;\n  };\n\n  for (const execution of report.executions) {\n    for (const task of execution.tasks) {\n      addTimestamp(task.timing?.start);\n      addTimestamp(task.timing?.end);\n      addUsage(task.usage);\n      addUsage(task.searchAreaUsage);\n    }\n  }\n\n  const modelSummaries = Array.from(models.values());\n  const tokens = modelSummaries.reduce<ReportTokenSummary>(\n    (total, model) => ({\n      promptTokens: total.promptTokens + model.promptTokens,\n      cachedInputTokens: total.cachedInputTokens + model.cachedInputTokens,\n      completionTokens: total.completionTokens + model.completionTokens,\n      totalTokens: total.totalTokens + model.totalTokens,\n    }),\n    {\n      promptTokens: 0,\n      cachedInputTokens: 0,\n      completionTokens: 0,\n      totalTokens: 0,\n    },\n  );\n\n  const fallback = finiteNumber(options.wallTimeFallbackMs);\n  const hasFallback = fallback !== undefined && fallback >= 0;\n  let wallTimeMs: number | undefined;\n  let wallTimeSource: ReportTimingSummary['wallTimeSource'];\n\n  if (earliestTimestamp !== undefined && latestTimestamp !== undefined) {\n    wallTimeMs = Math.max(0, latestTimestamp - earliestTimestamp);\n    wallTimeSource = 'task-timestamps';\n  } else if (hasFallback) {\n    wallTimeMs = fallback;\n    wallTimeSource = 'fallback';\n  } else {\n    wallTimeSource = 'unavailable';\n  }\n\n  return {\n    timing: {\n      wallTimeMs,\n      wallTimeStart:\n        wallTimeSource === 'task-timestamps' ? earliestTimestamp : undefined,\n      wallTimeEnd:\n        wallTimeSource === 'task-timestamps' ? latestTimestamp : undefined,\n      wallTimeSource,\n      modelCallTimeMs,\n      modelCallCount,\n    },\n    tokens,\n    models: modelSummaries,\n  };\n}\n"],"names":["usageKeys","finiteNumber","value","Number","undefined","usageValue","usage","key","Math","hasUsage","Boolean","usageTotalTokens","explicitTotal","usageModelName","collectReportSummary","report","options","Array","Error","earliestTimestamp","latestTimestamp","modelCallCount","modelCallTimeMs","models","Map","addTimestamp","timestamp","addUsage","modelName","current","callTimeMs","execution","task","modelSummaries","tokens","total","model","fallback","hasFallback","wallTimeMs","wallTimeSource"],"mappings":"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"}