mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-31 08:13:11 +03:00
refine(tui): clarify model telemetry
This commit is contained in:
+3
-2
@@ -1381,8 +1381,9 @@ describe("NanobotTui layout", () => {
|
|||||||
const footer = setup.captureCharFrame().split("\n").find((line) => line.includes("Ready · 1.7s")) || ""
|
const footer = setup.captureCharFrame().split("\n").find((line) => line.includes("Ready · 1.7s")) || ""
|
||||||
expect(footer).toContain("Ready · 1.7s")
|
expect(footer).toContain("Ready · 1.7s")
|
||||||
expect(footer).toContain("50 tok/s")
|
expect(footer).toContain("50 tok/s")
|
||||||
expect(footer).toContain("cache 75%")
|
expect(footer).toContain("1.2K in · 80 out")
|
||||||
expect(footer).toContain("↑1.2k ↓80")
|
expect(footer).toContain("75% cached")
|
||||||
|
expect(footer).not.toContain("TTFT")
|
||||||
expect(footer).not.toContain("enter send")
|
expect(footer).not.toContain("enter send")
|
||||||
|
|
||||||
app.accept({ event: "reasoning_delta", chat_id: "chat", text: "hidden" })
|
app.accept({ event: "reasoning_delta", chat_id: "chat", text: "hidden" })
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ describe("footerHints", () => {
|
|||||||
expect(active.chunks).toHaveLength(0)
|
expect(active.chunks).toHaveLength(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("shows measured throughput, cache ratio, token counts, and TTFT", () => {
|
test("shows measured throughput, explicit token directions, and cache ratio", () => {
|
||||||
const result = footerTelemetry({
|
const result = footerTelemetry({
|
||||||
prompt_tokens: 1200,
|
prompt_tokens: 1200,
|
||||||
completion_tokens: 80,
|
completion_tokens: 80,
|
||||||
@@ -41,10 +41,23 @@ describe("footerHints", () => {
|
|||||||
}, 120, theme)
|
}, 120, theme)
|
||||||
|
|
||||||
expect(result.chunks.map(({ text }) => text).join(""))
|
expect(result.chunks.map(({ text }) => text).join(""))
|
||||||
.toBe("50 tok/s · cache 75% · ↑1.2k ↓80 · TTFT 250ms")
|
.toBe("50 tok/s · 1.2K in · 80 out · 75% cached")
|
||||||
expect(result.chunks[0]?.fg?.toInts().slice(0, 3)).toEqual([239, 142, 48])
|
expect(result.chunks[0]?.fg?.toInts().slice(0, 3)).toEqual([239, 142, 48])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("uses familiar compact units for large token counts", () => {
|
||||||
|
const result = footerTelemetry({
|
||||||
|
prompt_tokens: 4_500_000,
|
||||||
|
completion_tokens: 19_000,
|
||||||
|
cached_tokens: 3_600_000,
|
||||||
|
generation_ms: 135_714,
|
||||||
|
measured_completion_tokens: 19_000,
|
||||||
|
}, 120, theme)
|
||||||
|
|
||||||
|
expect(result.chunks.map(({ text }) => text).join(""))
|
||||||
|
.toBe("140 tok/s · 4.5M in · 19K out · 80% cached")
|
||||||
|
})
|
||||||
|
|
||||||
test("degrades telemetry instead of guessing missing provider metrics", () => {
|
test("degrades telemetry instead of guessing missing provider metrics", () => {
|
||||||
const compact = footerTelemetry({
|
const compact = footerTelemetry({
|
||||||
prompt_tokens: 1000,
|
prompt_tokens: 1000,
|
||||||
@@ -53,7 +66,7 @@ describe("footerHints", () => {
|
|||||||
}, 60, theme)
|
}, 60, theme)
|
||||||
const unsupported = footerTelemetry({ prompt_tokens: 1000, completion_tokens: 20 }, 60, theme)
|
const unsupported = footerTelemetry({ prompt_tokens: 1000, completion_tokens: 20 }, 60, theme)
|
||||||
|
|
||||||
expect(compact.chunks.map(({ text }) => text).join("")).toBe("cache 0%")
|
expect(compact.chunks.map(({ text }) => text).join("")).toBe("0% cached")
|
||||||
expect(unsupported.chunks).toHaveLength(0)
|
expect(unsupported.chunks).toHaveLength(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+20
-14
@@ -1,6 +1,5 @@
|
|||||||
import { RGBA, StyledText, TextAttributes, type TextChunk } from "@opentui/core"
|
import { RGBA, StyledText, TextAttributes, type TextChunk } from "@opentui/core"
|
||||||
|
|
||||||
import { formatTokenCount } from "./context-panel"
|
|
||||||
import type { TokenUsage } from "./protocol"
|
import type { TokenUsage } from "./protocol"
|
||||||
|
|
||||||
export interface FooterHint {
|
export interface FooterHint {
|
||||||
@@ -53,6 +52,14 @@ export function footerTelemetry(
|
|||||||
const estimated = (usage.estimated_tokens || 0) > 0 ? "~" : ""
|
const estimated = (usage.estimated_tokens || 0) > 0 ? "~" : ""
|
||||||
parts.push(`${estimated}${value} tok/s`)
|
parts.push(`${estimated}${value} tok/s`)
|
||||||
}
|
}
|
||||||
|
if (width >= 72) {
|
||||||
|
const prompt = usage.prompt_tokens
|
||||||
|
const completion = usage.completion_tokens
|
||||||
|
if (typeof prompt === "number" || typeof completion === "number") {
|
||||||
|
parts.push(`${formatTelemetryTokens(prompt || 0)} in`)
|
||||||
|
parts.push(`${formatTelemetryTokens(completion || 0)} out`)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
typeof usage.cached_tokens === "number"
|
typeof usage.cached_tokens === "number"
|
||||||
&& typeof usage.prompt_tokens === "number"
|
&& typeof usage.prompt_tokens === "number"
|
||||||
@@ -61,19 +68,7 @@ export function footerTelemetry(
|
|||||||
const hitRate = Math.min(100, Math.max(0, Math.round(
|
const hitRate = Math.min(100, Math.max(0, Math.round(
|
||||||
usage.cached_tokens * 100 / usage.prompt_tokens,
|
usage.cached_tokens * 100 / usage.prompt_tokens,
|
||||||
)))
|
)))
|
||||||
parts.push(`cache ${hitRate}%`)
|
parts.push(`${hitRate}% cached`)
|
||||||
}
|
|
||||||
if (width >= 72) {
|
|
||||||
const prompt = usage.prompt_tokens
|
|
||||||
const completion = usage.completion_tokens
|
|
||||||
if (typeof prompt === "number" || typeof completion === "number") {
|
|
||||||
parts.push(`↑${formatTokenCount(prompt || 0)} ↓${formatTokenCount(completion || 0)}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (width >= 112 && typeof usage.ttft_ms === "number") {
|
|
||||||
const requests = Math.max(1, usage.timed_requests || 1)
|
|
||||||
const average = usage.ttft_ms / requests
|
|
||||||
parts.push(`TTFT ${average < 1000 ? `${Math.round(average)}ms` : `${(average / 1000).toFixed(1)}s`}`)
|
|
||||||
}
|
}
|
||||||
if (width >= 128 && typeof usage.cost_usd === "number" && usage.cost_usd > 0) {
|
if (width >= 128 && typeof usage.cost_usd === "number" && usage.cost_usd > 0) {
|
||||||
parts.push(`$${usage.cost_usd < 0.01 ? usage.cost_usd.toFixed(4) : usage.cost_usd.toFixed(2)}`)
|
parts.push(`$${usage.cost_usd < 0.01 ? usage.cost_usd.toFixed(4) : usage.cost_usd.toFixed(2)}`)
|
||||||
@@ -81,6 +76,17 @@ export function footerTelemetry(
|
|||||||
return footerMetrics(parts, theme)
|
return footerMetrics(parts, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatTelemetryTokens(value: number): string {
|
||||||
|
const count = Math.max(0, value)
|
||||||
|
for (const [threshold, suffix] of [[1_000_000_000, "B"], [1_000_000, "M"], [1_000, "K"]] as const) {
|
||||||
|
if (count < threshold) continue
|
||||||
|
const scaled = count / threshold
|
||||||
|
const compact = scaled >= 10 ? Math.round(scaled) : Math.round(scaled * 10) / 10
|
||||||
|
return `${compact}${suffix}`
|
||||||
|
}
|
||||||
|
return String(Math.round(count))
|
||||||
|
}
|
||||||
|
|
||||||
function footerMetrics(parts: readonly string[], theme: FooterHintTheme): StyledText {
|
function footerMetrics(parts: readonly string[], theme: FooterHintTheme): StyledText {
|
||||||
const chunks: TextChunk[] = []
|
const chunks: TextChunk[] = []
|
||||||
parts.forEach((text, index) => {
|
parts.forEach((text, index) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user