{"version":3,"sources":["../src/lib/utils.ts","../src/components/metrics/Metrics.tsx"],"names":["extendTailwindMerge","clsx","ChartJS","CategoryScale","LinearScale","PointElement","LineElement","Title","Tooltip","Legend","useState","usePipecatClientTransportState","useRTVIClientEvent","RTVIEvent","jsxs","Fragment","jsx","Line"],"mappings":"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","file":"metrics.cjs","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst customTwMerge = extendTailwindMerge({\n  extend: {\n    classGroups: {\n      shadow: [\n        {\n          shadow: [\"xshort\", \"short\", \"long\", \"xlong\"],\n        },\n      ],\n      button: [\n        {\n          button: [\"sm\", \"md\", \"lg\", \"xl\"],\n        },\n        {\n          \"button-icon\": [\"sm\", \"md\", \"lg\", \"xl\"],\n        },\n      ],\n    },\n    theme: {\n      spacing: [\n        \"element-xs\",\n        \"element-md\",\n        \"element-sm\",\n        \"element-lg\",\n        \"element-xl\",\n        \"element-2xl\",\n      ],\n    },\n  },\n} as Parameters<typeof extendTailwindMerge>[0]);\n\nexport function cn(...inputs: ClassValue[]) {\n  return customTwMerge(clsx(inputs));\n}\n","import { RTVIEvent } from \"@pipecat-ai/client-js\";\nimport {\n  usePipecatClientTransportState,\n  useRTVIClientEvent,\n} from \"@pipecat-ai/client-react\";\nimport {\n  CategoryScale,\n  Chart as ChartJS,\n  type ChartOptions,\n  Legend,\n  LinearScale,\n  LineElement,\n  PointElement,\n  Title,\n  Tooltip,\n} from \"chart.js\";\nimport { useState } from \"react\";\nimport { Line } from \"react-chartjs-2\";\n\nimport { cn } from \"@/lib/utils\";\n\n// Register Chart.js components\nChartJS.register(\n  CategoryScale,\n  LinearScale,\n  PointElement,\n  LineElement,\n  Title,\n  Tooltip,\n  Legend,\n);\n\ninterface ProcessingMetric {\n  processor: string;\n  value: number;\n}\n\ninterface MetricData {\n  timestamp: string;\n  value: number;\n}\n\ninterface MetricsState {\n  [processorName: string]: MetricData[];\n}\n\ninterface TokenMetrics {\n  completion_tokens: number;\n  prompt_tokens: number;\n  total_tokens: number;\n}\n\ninterface Props {\n  chartOptions?: ChartOptions<\"line\">;\n  classNames?: {\n    container?: string;\n    heading?: string;\n    metricsContainer?: string;\n    metricsCard?: string;\n    metricsTitle?: string;\n    metricsChart?: string;\n    tokenContainer?: string;\n    tokenCard?: string;\n    tokenType?: string;\n    tokenValue?: string;\n  };\n  ignoreProcessorNames?: string[];\n  noPromptTokens?: boolean;\n  noCompletionTokens?: boolean;\n  noTotalTokens?: boolean;\n}\n\nexport const Metrics: React.FC<Props> = ({\n  chartOptions = {},\n  classNames = {},\n  ignoreProcessorNames = [],\n  noPromptTokens = false,\n  noCompletionTokens = false,\n  noTotalTokens = false,\n}) => {\n  const [ttfbMetrics, setTtfbMetrics] = useState<MetricsState>({});\n  const [tokenMetrics, setTokenMetrics] = useState<Partial<TokenMetrics>>({});\n\n  const transportState = usePipecatClientTransportState();\n\n  useRTVIClientEvent(RTVIEvent.Connected, () => {\n    setTtfbMetrics({});\n    setTokenMetrics({\n      completion_tokens: 0,\n      prompt_tokens: 0,\n      total_tokens: 0,\n    });\n  });\n\n  useRTVIClientEvent(RTVIEvent.Metrics, (data) => {\n    // Handle processing metrics\n    if (data?.ttfb && Array.isArray(data.ttfb)) {\n      const timestamp = new Date().toISOString();\n\n      setTtfbMetrics((prevMetrics) => {\n        const newMetrics = { ...prevMetrics };\n\n        (data.ttfb ?? []).forEach((item: ProcessingMetric) => {\n          const { processor, value } = item;\n\n          if (ignoreProcessorNames.includes(processor)) {\n            return; // Skip ignored processors\n          }\n\n          if (!newMetrics[processor]) {\n            newMetrics[processor] = [];\n          }\n\n          // Limit array to last 100 entries to prevent excessive memory use\n          const updatedMetrics = [\n            ...newMetrics[processor],\n            { timestamp, value },\n          ].slice(-100);\n\n          newMetrics[processor] = updatedMetrics;\n        });\n\n        return newMetrics;\n      });\n    }\n\n    // Handle token metrics\n    // @ts-expect-error - tokens type not defined\n    const tokens = data?.tokens;\n    if (tokens && Array.isArray(tokens) && tokens.length > 0) {\n      const tokenData = tokens[0];\n\n      setTokenMetrics((prev) => ({\n        completion_tokens:\n          prev.completion_tokens +\n          (noCompletionTokens ? 0 : tokenData.completion_tokens || 0),\n        prompt_tokens:\n          prev.prompt_tokens +\n          (noPromptTokens ? 0 : tokenData.prompt_tokens || 0),\n        total_tokens:\n          prev.total_tokens + (noTotalTokens ? 0 : tokenData.total_tokens || 0),\n      }));\n    }\n  });\n\n  const generateChartData = (processorName: string, data: MetricData[]) => {\n    return {\n      labels: data.map((d) => {\n        const date = new Date(d.timestamp);\n        return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}`;\n      }),\n      datasets: [\n        {\n          label: `TTFB (${processorName})`,\n          data: data.map((d) => d.value * 1000), // Convert to ms for better readability\n          borderColor: getColorForProcessor(processorName),\n          backgroundColor: getColorForProcessor(processorName, 0.2),\n          tension: 0.4,\n        },\n      ],\n    };\n  };\n\n  // Simple function to generate consistent colors based on processor name\n  const getColorForProcessor = (processor: string, alpha = 1) => {\n    const hash = processor.split(\"\").reduce((acc, char) => {\n      return char.charCodeAt(0) + ((acc << 5) - acc);\n    }, 0);\n    const h = Math.abs(hash) % 360;\n    return `hsla(${h}, 70%, 50%, ${alpha})`;\n  };\n\n  const lineChartOptions: ChartOptions<\"line\"> = {\n    responsive: true,\n    maintainAspectRatio: false,\n    scales: {\n      y: {\n        title: {\n          display: true,\n          text: \"Time (ms)\",\n        },\n        beginAtZero: true,\n      },\n      x: {\n        title: {\n          display: true,\n          text: \"Time\",\n        },\n        ticks: {\n          maxRotation: 0,\n          autoSkip: true,\n          maxTicksLimit: 10,\n        },\n      },\n    },\n    plugins: {\n      tooltip: {\n        callbacks: {\n          label: function (context) {\n            return `${context.dataset.label}: ${context.parsed.y.toFixed(2)} ms`;\n          },\n        },\n      },\n    },\n    ...chartOptions,\n  };\n\n  const isConnecting =\n    transportState === \"authenticating\" || transportState === \"connecting\";\n  const isConnected =\n    transportState === \"connected\" || transportState === \"ready\";\n\n  const hasTokenMetrics = Object.keys(tokenMetrics).length > 0;\n  const hasMetrics = Object.keys(ttfbMetrics).length > 0;\n\n  const tokenCardClassName = cn(\n    \"bg-card rounded-md p-3 shadow-sm\",\n    classNames.tokenCard,\n  );\n  const tokenTypeClassName = cn(\n    \"text-sm text-muted-foreground\",\n    classNames.tokenType,\n  );\n  const tokenValueClassName = cn(\"text-2xl font-medium\", classNames.tokenValue);\n\n  if (hasMetrics || hasTokenMetrics) {\n    return (\n      <div\n        className={cn(\n          \"@container/metrics grid gap-6 items-start p-4 max-h-full overflow-auto\",\n          classNames.container,\n        )}\n      >\n        {hasTokenMetrics && (\n          <>\n            <h2 className={cn(\"text-xl font-semibold\", classNames.heading)}>\n              Token Usage\n            </h2>\n            <div\n              className={cn(\n                \"grid grid-cols-1 @xl/metrics:grid-cols-2 @3xl/metrics:grid-cols-3 gap-4\",\n                classNames.tokenContainer,\n              )}\n            >\n              {!noPromptTokens && (\n                <div className={tokenCardClassName}>\n                  <div className={tokenTypeClassName}>Prompt Tokens</div>\n                  <div className={tokenValueClassName}>\n                    {tokenMetrics.prompt_tokens}\n                  </div>\n                </div>\n              )}\n              {!noCompletionTokens && (\n                <div className={tokenCardClassName}>\n                  <div className={tokenTypeClassName}>Completion Tokens</div>\n                  <div className={tokenValueClassName}>\n                    {tokenMetrics.completion_tokens}\n                  </div>\n                </div>\n              )}\n              {!noTotalTokens && (\n                <div className={tokenCardClassName}>\n                  <div className={tokenTypeClassName}>Total Tokens</div>\n                  <div className={tokenValueClassName}>\n                    {tokenMetrics.total_tokens}\n                  </div>\n                </div>\n              )}\n            </div>\n          </>\n        )}\n        {hasMetrics && (\n          <>\n            <h2 className={cn(\"text-xl font-semibold\", classNames.heading)}>\n              TTFB Metrics\n            </h2>\n            <div\n              className={cn(\n                \"grid grid-cols-1 @xl/metrics:grid-cols-2 @3xl/metrics:grid-cols-3 gap-4\",\n                classNames.metricsContainer,\n              )}\n            >\n              {Object.entries(ttfbMetrics).map(([processorName, data]) => (\n                <div\n                  key={processorName}\n                  className={cn(\n                    \"bg-card border rounded-lg shadow-sm p-3 h-60\",\n                    classNames.metricsCard,\n                  )}\n                >\n                  <h3 className={cn(\"mb-2\", classNames.metricsTitle)}>\n                    {processorName}\n                  </h3>\n                  <div className={cn(\"h-44\", classNames.metricsChart)}>\n                    <Line\n                      data={generateChartData(processorName, data)}\n                      options={lineChartOptions}\n                    />\n                  </div>\n                </div>\n              ))}\n            </div>\n          </>\n        )}\n      </div>\n    );\n  }\n\n  if (isConnecting) {\n    return (\n      <div\n        className={cn(\n          \"flex items-center justify-center h-full text-muted-foreground text-sm\",\n          classNames.container,\n        )}\n      >\n        Connecting to agent...\n      </div>\n    );\n  }\n\n  if (!isConnected) {\n    return (\n      <div\n        className={cn(\n          \"flex items-center justify-center h-full text-muted-foreground text-center\",\n          classNames.container,\n        )}\n      >\n        <div className=\"p-4\">\n          <div className=\"mb-2\">Not connected to agent</div>\n          <p className=\"text-sm max-w-md\">\n            Connect to an agent to view metrics in real-time.\n          </p>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className={cn(\n        \"flex items-center justify-center h-full text-muted-foreground text-sm\",\n        classNames.container,\n      )}\n    >\n      Waiting for metrics data...\n    </div>\n  );\n};\n\nexport default Metrics;\n"]}