Skip to content

Commit 6363c3f

Browse files
refactor: token usage styles (#228)
1 parent 8af305d commit 6363c3f

File tree

4 files changed

+112
-106
lines changed

4 files changed

+112
-106
lines changed

src/features/alerts/components/__tests__/table-alerts.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ vi.mock("@untitled-ui/icons-react", async () => {
1212
>("@untitled-ui/icons-react");
1313
return {
1414
...original,
15-
ArrowDown: () => <div data-testid="icon-arrow-down" />,
16-
ArrowUp: () => <div data-testid="icon-arrow-up" />,
15+
Download01: () => <div data-testid="icon-arrow-down" />,
16+
Upload01: () => <div data-testid="icon-arrow-up" />,
1717
};
1818
});
1919

src/features/alerts/components/table-alert-token-usage.tsx

+9-103
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { TokenUsage, TokenUsageAggregate } from "@/api/generated";
2-
import { formatCurrency } from "@/lib/currency";
1+
import { TokenUsageAggregate } from "@/api/generated";
32
import { TextLinkButton, Tooltip, TooltipTrigger } from "@stacklok/ui-kit";
4-
import { ArrowDown, ArrowUp } from "@untitled-ui/icons-react";
3+
import { Download01, Upload01 } from "@untitled-ui/icons-react";
4+
import { TokenUsageByProviders } from "./token-usage-by-providers";
55

66
function Icons({
77
input_tokens = 0,
@@ -11,113 +11,19 @@ function Icons({
1111
output_tokens: number | null;
1212
}) {
1313
return (
14-
<div className="flex tabular-nums gap-1 items-center">
15-
<div className="flex items-center">
16-
<ArrowUp className="size-4" />
14+
<div className="flex tabular-nums gap-4 items-center">
15+
<div className="flex items-center gap-1">
16+
<Download01 className="size-4" />
1717
{input_tokens}
1818
</div>
19-
<div className="flex items-center">
20-
<ArrowDown className="size-4" />
21-
{output_tokens}
19+
<div className="flex items-center gap-1">
20+
<Upload01 className="size-4" />
21+
<span className="block">{output_tokens}</span>
2222
</div>
2323
</div>
2424
);
2525
}
2626

27-
function validateUsage(usage: TokenUsage | null): usage is {
28-
input_tokens: number;
29-
output_tokens: number;
30-
input_cost: number;
31-
output_cost: number;
32-
} {
33-
return Boolean(
34-
typeof usage?.input_tokens !== "undefined" &&
35-
typeof usage.input_cost !== "undefined" &&
36-
typeof usage.output_tokens !== "undefined" &&
37-
typeof usage.output_cost !== "undefined",
38-
);
39-
}
40-
41-
function UsageIcon({
42-
iconType: iconType,
43-
...props
44-
}: {
45-
iconType: "input" | "output";
46-
className?: string;
47-
}) {
48-
switch (iconType) {
49-
case "input":
50-
return <ArrowUp {...props} />;
51-
case "output":
52-
return <ArrowDown {...props} />;
53-
default:
54-
iconType satisfies never;
55-
}
56-
}
57-
58-
function UsageRow({
59-
cost,
60-
tokens,
61-
type,
62-
}: {
63-
type: "input" | "output";
64-
tokens: number;
65-
cost: number;
66-
}) {
67-
return (
68-
<li className="min-w-40 flex items-center border-b border-b-gray-800 py-1 my-1 list-none">
69-
<UsageIcon iconType={type} className="size-4 text-gray-50" />
70-
71-
<div className="text-gray-50">{tokens}</div>
72-
73-
<div className="ml-auto text-gray-25">
74-
{formatCurrency(cost, { currency: "USD" })}
75-
</div>
76-
</li>
77-
);
78-
}
79-
80-
function UsageRows({
81-
input_cost,
82-
input_tokens,
83-
output_cost,
84-
output_tokens,
85-
}: {
86-
input_tokens: number;
87-
output_tokens: number;
88-
input_cost: number;
89-
output_cost: number;
90-
}) {
91-
return (
92-
<>
93-
<UsageRow type={"input"} tokens={input_tokens} cost={input_cost} />
94-
<UsageRow type={"output"} tokens={output_tokens} cost={output_cost} />
95-
</>
96-
);
97-
}
98-
99-
function TokenUsageByProviders({ tokens_by_model }: TokenUsageAggregate) {
100-
return Object.values(tokens_by_model).map(
101-
({ provider_type, token_usage: modelTokenUsage, model }) => {
102-
if (!validateUsage(modelTokenUsage)) return null;
103-
104-
return (
105-
<div>
106-
<div>
107-
<b>Model:</b> {model}
108-
</div>
109-
<div>
110-
<b>Provider:</b> {provider_type}
111-
</div>
112-
<ul className="list-none mt-2">
113-
<UsageRows {...modelTokenUsage} />
114-
</ul>
115-
</div>
116-
);
117-
},
118-
);
119-
}
120-
12127
export function TableAlertTokenUsage({
12228
usage,
12329
}: {

src/features/alerts/components/table-alerts.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export function TableAlerts() {
185185
<Column width={150}>Type</Column>
186186
<Column>Event</Column>
187187
<Column width={325}>Issue Detected</Column>
188-
<Column width={150}>Token usage</Column>
188+
<Column width={200}>Token usage</Column>
189189
</Row>
190190
</TableHeader>
191191
<TableBody>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { TokenUsage, TokenUsageAggregate } from "@/api/generated";
2+
import { formatCurrency } from "@/lib/currency";
3+
4+
import { ArrowDown, ArrowUp } from "@untitled-ui/icons-react";
5+
6+
function validateUsage(usage: TokenUsage | null): usage is {
7+
input_tokens: number;
8+
output_tokens: number;
9+
input_cost: number;
10+
output_cost: number;
11+
} {
12+
return Boolean(
13+
typeof usage?.input_tokens !== "undefined" &&
14+
typeof usage.input_cost !== "undefined" &&
15+
typeof usage.output_tokens !== "undefined" &&
16+
typeof usage.output_cost !== "undefined",
17+
);
18+
}
19+
20+
function UsageIcon({
21+
iconType: iconType,
22+
...props
23+
}: {
24+
iconType: "input" | "output";
25+
className?: string;
26+
}) {
27+
switch (iconType) {
28+
case "input":
29+
return <ArrowUp {...props} />;
30+
case "output":
31+
return <ArrowDown {...props} />;
32+
default:
33+
iconType satisfies never;
34+
}
35+
}
36+
37+
function UsageRow({
38+
cost,
39+
tokens,
40+
type,
41+
}: {
42+
type: "input" | "output";
43+
tokens: number;
44+
cost: number;
45+
}) {
46+
return (
47+
<li className="min-w-40 flex items-center border-b border-b-gray-900 last:border-b-0 py-1 my-1 list-none">
48+
<UsageIcon iconType={type} className="size-4 text-gray-50" />
49+
50+
<div className="text-gray-50">{tokens}</div>
51+
52+
<div className="ml-auto text-gray-25">
53+
{formatCurrency(cost, { currency: "USD" })}
54+
</div>
55+
</li>
56+
);
57+
}
58+
59+
function UsageRows({
60+
input_cost,
61+
input_tokens,
62+
output_cost,
63+
output_tokens,
64+
}: {
65+
input_tokens: number;
66+
output_tokens: number;
67+
input_cost: number;
68+
output_cost: number;
69+
}) {
70+
return (
71+
<>
72+
<UsageRow type={"input"} tokens={input_tokens} cost={input_cost} />
73+
<UsageRow type={"output"} tokens={output_tokens} cost={output_cost} />
74+
</>
75+
);
76+
}
77+
78+
export function TokenUsageByProviders({
79+
tokens_by_model,
80+
}: TokenUsageAggregate) {
81+
return Object.values(tokens_by_model).map(
82+
({ provider_type, token_usage: modelTokenUsage, model }) => {
83+
if (!validateUsage(modelTokenUsage)) return null;
84+
85+
return (
86+
<div>
87+
<div>
88+
<b>Model:</b> {model}
89+
</div>
90+
<div>
91+
<b>Provider:</b> {provider_type}
92+
</div>
93+
<ul className="list-none mt-2">
94+
<UsageRows {...modelTokenUsage} />
95+
</ul>
96+
</div>
97+
);
98+
},
99+
);
100+
}

0 commit comments

Comments
 (0)