File size: 3,039 Bytes
af404c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { TokenUsage } from "@/types/chat";

interface Props {
  tokenUsage: TokenUsage;
  modelUsed: string;
  primaryModel?: string;
}

const MODEL_LABELS: Record<string, string> = {
  "llama-3.1-8b-instant":     "Llama 3.1 · 8B",
  "llama-3.3-70b-versatile":  "Llama 3.3 · 70B",
  "mixtral-8x7b-32768":       "Mixtral · 8×7B",
  "gemma2-9b-it":             "Gemma 2 · 9B",
};

function shortLabel(model: string): string {
  return MODEL_LABELS[model] ?? model.split("-").slice(0, 3).join("-");
}

export default function TokenUsageDisplay({ tokenUsage, modelUsed, primaryModel }: Props) {
  const isFallback = primaryModel && modelUsed !== primaryModel;
  const { prompt_tokens, completion_tokens, total_tokens } = tokenUsage;

  // Ratio bar: completion vs prompt (capped at 100%)
  const ratio = total_tokens > 0 ? Math.min((completion_tokens / total_tokens) * 100, 100) : 0;

  return (
    <div className="flex items-center gap-3 mt-2.5 flex-wrap">
      {/* Model badge */}
      <div className={`flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 ${
        isFallback
          ? "border-amber-200 bg-amber-50 text-amber-600"
          : "border-[#E8EDF2] bg-[#F8FAFC] text-[#64748B]"
      }`}>
        {isFallback ? (
          <svg width="9" height="9" viewBox="0 0 10 10" fill="none" className="flex-shrink-0">
            <path d="M5 1v4l2.5 1.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
            <circle cx="5" cy="5" r="4" stroke="currentColor" strokeWidth="1.2"/>
          </svg>
        ) : (
          <svg width="9" height="9" viewBox="0 0 10 10" fill="currentColor" className="flex-shrink-0 opacity-60">
            <circle cx="5" cy="5" r="3"/>
          </svg>
        )}
        <span className="text-[10px] font-semibold leading-none">
          {shortLabel(modelUsed)}
        </span>
        {isFallback && (
          <span className="text-[9px] font-medium uppercase tracking-wider opacity-70 leading-none">
            fallback
          </span>
        )}
      </div>

      {/* Token breakdown */}
      <div className="flex items-center gap-1.5">
        {/* Mini bar */}
        <div className="w-12 h-1.5 rounded-full bg-[#E8EDF2] overflow-hidden">
          <div
            className="h-full rounded-full bg-gradient-to-r from-[#47C3A6] to-[#14B7CC]"
            style={{ width: `${ratio}%` }}
          />
        </div>

        <span className="text-[10px] text-[#94A3B8] font-mono tabular-nums">
          <span title="Prompt tokens" className="text-[#94A3B8]">{prompt_tokens.toLocaleString()}</span>
          <span className="text-[#CBD5E1] mx-0.5">+</span>
          <span title="Completion tokens" className="text-[#47C3A6]">{completion_tokens.toLocaleString()}</span>
          <span className="text-[#CBD5E1] mx-1">·</span>
          <span title="Total tokens" className="font-semibold text-[#64748B]">{total_tokens.toLocaleString()}</span>
          <span className="text-[#CBD5E1] ml-0.5">tok</span>
        </span>
      </div>
    </div>
  );
}