story-kit: merge 309_story_show_token_cost_breakdown_in_expanded_work_item_detail_panel
This commit is contained in:
@@ -2,7 +2,12 @@ import * as React from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import type { AgentEvent, AgentInfo, AgentStatusValue } from "../api/agents";
|
||||
import { agentsApi, subscribeAgentStream } from "../api/agents";
|
||||
import type { TestCaseResult, TestResultsResponse } from "../api/client";
|
||||
import type {
|
||||
AgentCostEntry,
|
||||
TestCaseResult,
|
||||
TestResultsResponse,
|
||||
TokenCostResponse,
|
||||
} from "../api/client";
|
||||
import { api } from "../api/client";
|
||||
|
||||
const { useEffect, useRef, useState } = React;
|
||||
@@ -125,6 +130,7 @@ export function WorkItemDetailPanel({
|
||||
const [testResults, setTestResults] = useState<TestResultsResponse | null>(
|
||||
null,
|
||||
);
|
||||
const [tokenCost, setTokenCost] = useState<TokenCostResponse | null>(null);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const cleanupRef = useRef<(() => void) | null>(null);
|
||||
|
||||
@@ -159,6 +165,18 @@ export function WorkItemDetailPanel({
|
||||
});
|
||||
}, [storyId, pipelineVersion]);
|
||||
|
||||
// Fetch token cost on mount and when pipeline updates arrive.
|
||||
useEffect(() => {
|
||||
api
|
||||
.getTokenCost(storyId)
|
||||
.then((data) => {
|
||||
setTokenCost(data);
|
||||
})
|
||||
.catch(() => {
|
||||
// Silently ignore — token cost may not exist yet.
|
||||
});
|
||||
}, [storyId, pipelineVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
cleanupRef.current?.();
|
||||
cleanupRef.current = null;
|
||||
@@ -365,6 +383,96 @@ export function WorkItemDetailPanel({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Token Cost section */}
|
||||
<div
|
||||
data-testid="token-cost-section"
|
||||
style={{
|
||||
border: "1px solid #2a2a2a",
|
||||
borderRadius: "8px",
|
||||
padding: "10px 12px",
|
||||
background: "#161616",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.8em",
|
||||
color: "#555",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
>
|
||||
Token Cost
|
||||
</div>
|
||||
{tokenCost && tokenCost.agents.length > 0 ? (
|
||||
<div data-testid="token-cost-content">
|
||||
<div
|
||||
style={{
|
||||
fontSize: "0.75em",
|
||||
color: "#888",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
>
|
||||
Total:{" "}
|
||||
<span data-testid="token-cost-total" style={{ color: "#ccc" }}>
|
||||
${tokenCost.total_cost_usd.toFixed(6)}
|
||||
</span>
|
||||
</div>
|
||||
{tokenCost.agents.map((agent: AgentCostEntry) => (
|
||||
<div
|
||||
key={agent.agent_name}
|
||||
data-testid={`token-cost-agent-${agent.agent_name}`}
|
||||
style={{
|
||||
fontSize: "0.75em",
|
||||
color: "#888",
|
||||
padding: "4px 0",
|
||||
borderTop: "1px solid #222",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "2px",
|
||||
}}
|
||||
>
|
||||
<span style={{ color: "#ccc", fontWeight: 600 }}>
|
||||
{agent.agent_name}
|
||||
{agent.model ? (
|
||||
<span
|
||||
style={{ color: "#666", fontWeight: 400 }}
|
||||
>{` (${agent.model})`}</span>
|
||||
) : null}
|
||||
</span>
|
||||
<span style={{ color: "#aaa" }}>
|
||||
${agent.total_cost_usd.toFixed(6)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ color: "#555" }}>
|
||||
in {agent.input_tokens.toLocaleString()} / out{" "}
|
||||
{agent.output_tokens.toLocaleString()}
|
||||
{(agent.cache_creation_input_tokens > 0 ||
|
||||
agent.cache_read_input_tokens > 0) && (
|
||||
<>
|
||||
{" "}
|
||||
/ cache +
|
||||
{agent.cache_creation_input_tokens.toLocaleString()}{" "}
|
||||
read {agent.cache_read_input_tokens.toLocaleString()}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
data-testid="token-cost-empty"
|
||||
style={{ fontSize: "0.75em", color: "#444" }}
|
||||
>
|
||||
No token data recorded
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Test Results section */}
|
||||
<div
|
||||
data-testid="test-results-section"
|
||||
|
||||
Reference in New Issue
Block a user