diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index ea88d25d9f..576da1a194 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1629,6 +1629,8 @@
"result": "Result",
"error": "Error",
"tool_error": "failed",
+ "total_tokens": "{{total}} tokens",
+ "tokens_detail": "{{prompt}} prompt + {{completion}} completion",
"tokens_used": "{{prompt}} prompt + {{completion}} completion = {{total}} tokens",
"tokens_used_with_cost": "{{prompt}} prompt + {{completion}} completion = {{total}} tokens (~${{cost}})",
"tokens_used_with_model": "{{model}}: {{prompt}} prompt + {{completion}} completion = {{total}} tokens",
diff --git a/apps/client/src/widgets/type_widgets/llm_chat/ChatMessage.tsx b/apps/client/src/widgets/type_widgets/llm_chat/ChatMessage.tsx
index 99d10370af..1c3cedf4c4 100644
--- a/apps/client/src/widgets/type_widgets/llm_chat/ChatMessage.tsx
+++ b/apps/client/src/widgets/type_widgets/llm_chat/ChatMessage.tsx
@@ -1,10 +1,18 @@
+import "./LlmChat.css";
+
import type { LlmCitation, LlmUsage } from "@triliumnext/commons";
-import { useMemo } from "preact/hooks";
import { marked } from "marked";
+import { useMemo } from "preact/hooks";
+
import { t } from "../../../services/i18n.js";
import type { ContentBlock, StoredMessage, ToolCall } from "./llm_chat_types.js";
import { getMessageText, getMessageToolCalls } from "./llm_chat_types.js";
-import "./LlmChat.css";
+
+function shortenNumber(n: number): string {
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
+ if (n >= 1_000) return `${(n / 1_000).toFixed(n >= 10_000 ? 0 : 1)}k`;
+ return n.toString();
+}
// Configure marked for safe rendering
marked.setOptions({
@@ -197,37 +205,26 @@ export default function ChatMessage({ message, isStreaming }: Props) {
)}
{message.usage && typeof message.usage.promptTokens === "number" && (
-
-
- {message.usage.model && message.usage.cost != null
- ? t("llm_chat.tokens_used_with_model_and_cost", {
- model: message.usage.model,
- prompt: message.usage.promptTokens.toLocaleString(),
- completion: message.usage.completionTokens.toLocaleString(),
- total: message.usage.totalTokens.toLocaleString(),
- cost: message.usage.cost.toFixed(4)
- })
- : message.usage.model
- ? t("llm_chat.tokens_used_with_model", {
- model: message.usage.model,
- prompt: message.usage.promptTokens.toLocaleString(),
- completion: message.usage.completionTokens.toLocaleString(),
- total: message.usage.totalTokens.toLocaleString()
- })
- : message.usage.cost != null
- ? t("llm_chat.tokens_used_with_cost", {
- prompt: message.usage.promptTokens.toLocaleString(),
- completion: message.usage.completionTokens.toLocaleString(),
- total: message.usage.totalTokens.toLocaleString(),
- cost: message.usage.cost.toFixed(4)
- })
- : t("llm_chat.tokens_used", {
- prompt: message.usage.promptTokens.toLocaleString(),
- completion: message.usage.completionTokens.toLocaleString(),
- total: message.usage.totalTokens.toLocaleString()
- })
- }
+ {message.usage.model && (
+ {message.usage.model}
+ )}
+ ·
+
+ {" "}
+ {t("llm_chat.total_tokens", { total: shortenNumber(message.usage.totalTokens) })}
+ {message.usage.cost != null && (
+ <>
+ ·
+ ~${message.usage.cost.toFixed(4)}
+ >
+ )}
)}
diff --git a/apps/client/src/widgets/type_widgets/llm_chat/LlmChat.css b/apps/client/src/widgets/type_widgets/llm_chat/LlmChat.css
index 4a725012fe..29788d0ad8 100644
--- a/apps/client/src/widgets/type_widgets/llm_chat/LlmChat.css
+++ b/apps/client/src/widgets/type_widgets/llm_chat/LlmChat.css
@@ -619,13 +619,26 @@
border-top: 1px solid var(--main-border-color);
font-size: 0.75rem;
color: var(--muted-text-color);
+ cursor: default;
}
.llm-chat-usage .bx {
font-size: 0.875rem;
}
-.llm-chat-usage-text {
+.llm-chat-usage-model {
+ font-weight: 500;
+}
+
+.llm-chat-usage-separator {
+ opacity: 0.5;
+}
+
+.llm-chat-usage-tokens {
+ font-family: var(--monospace-font-family, monospace);
+}
+
+.llm-chat-usage-cost {
font-family: var(--monospace-font-family, monospace);
}