Merge pull request #36735 from BerriAI/litellm_/interesting-meitner-e88370

refactor(ui): migrate TokenFlow and JsonViewer to shadcn
This commit is contained in:
yuneng-jiang 2026-08-13 13:04:50 -07:00 committed by GitHub
commit 0f5fa38ef2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 60 additions and 20 deletions

View file

@ -3809,11 +3809,6 @@
"count": 1
}
},
"src/components/view_logs/LogDetailsDrawer/JsonViewer.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx": {
"no-nested-ternary": {
"count": 3
@ -3863,11 +3858,6 @@
"count": 1
}
},
"src/components/view_logs/LogDetailsDrawer/TokenFlow.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/view_logs/LogDetailsDrawer/TruncatedValue.tsx": {
"no-restricted-imports": {
"count": 1

View file

@ -0,0 +1,28 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { JsonViewer } from "./JsonViewer";
describe("JsonViewer", () => {
it("should render a placeholder and no tree when the log entry carries no payload", () => {
render(<JsonViewer data={null} mode="formatted" />);
expect(screen.getByText("No data")).toBeInTheDocument();
expect(screen.queryByRole("tree")).not.toBeInTheDocument();
});
it("should render the payload as a tree exposing its keys", () => {
render(<JsonViewer data={{ model: "claude-opus-4-5", stream: true }} mode="formatted" />);
expect(screen.getByRole("tree")).toBeInTheDocument();
expect(screen.getByText(/model/)).toBeInTheDocument();
expect(screen.getByText(/stream/)).toBeInTheDocument();
expect(screen.queryByText("No data")).not.toBeInTheDocument();
});
it("should treat an empty payload as data rather than showing the placeholder", () => {
render(<JsonViewer data={{}} mode="formatted" />);
expect(screen.getByRole("tree")).toBeInTheDocument();
expect(screen.queryByText("No data")).not.toBeInTheDocument();
});
});

View file

@ -1,10 +1,7 @@
import { Typography } from "antd";
import { JsonView, defaultStyles } from "react-json-view-lite";
import "react-json-view-lite/dist/index.css";
import { JSON_MAX_HEIGHT, COLOR_BG_LIGHT, SPACING_LARGE } from "./constants";
const { Text } = Typography;
interface JsonViewerProps {
data: any;
mode: "formatted";
@ -15,7 +12,7 @@ interface JsonViewerProps {
* Uses an interactive tree component for easy navigation.
*/
export function JsonViewer({ data }: JsonViewerProps) {
if (!data) return <Text type="secondary">No data</Text>;
if (!data) return <span className="text-muted-foreground">No data</span>;
return (
<div

View file

@ -0,0 +1,29 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { TokenFlow } from "./TokenFlow";
const localised = (count: number) => count.toLocaleString();
describe("TokenFlow", () => {
it("should render the total followed by its prompt and completion breakdown", () => {
render(<TokenFlow prompt={9} completion={3} total={12} />);
expect(screen.getByText("12 (9 prompt tokens + 3 completion tokens)")).toBeInTheDocument();
});
it("should group large counts the way the reader's locale does", () => {
render(<TokenFlow prompt={1234567} completion={89012} total={1323579} />);
expect(
screen.getByText(
`${localised(1323579)} (${localised(1234567)} prompt tokens + ${localised(89012)} completion tokens)`,
),
).toBeInTheDocument();
});
it("should fall back to zero for counts the log entry does not carry", () => {
render(<TokenFlow total={12} />);
expect(screen.getByText("12 (0 prompt tokens + 0 completion tokens)")).toBeInTheDocument();
});
});

View file

@ -1,7 +1,3 @@
import { Typography } from "antd";
const { Text } = Typography;
interface TokenFlowProps {
prompt?: number;
completion?: number;
@ -14,9 +10,9 @@ interface TokenFlowProps {
*/
export function TokenFlow({ prompt = 0, completion = 0, total = 0 }: TokenFlowProps) {
return (
<Text>
<span>
{total.toLocaleString()} ({prompt.toLocaleString()} prompt tokens + {completion.toLocaleString()} completion
tokens)
</Text>
</span>
);
}