This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch tui-ollama-tab in repository https://gitbox.apache.org/repos/asf/camel.git
commit c58cdae15641e40bf6453b31610714263506bcfe Author: Claus Ibsen <[email protected]> AuthorDate: Thu Sep 17 12:48:51 2026 +0200 CAMEL-24794: camel-jbang - TUI Ollama tab Context panel reads the cache hit from the last request when idle llama-server clears the slot's cache count once a request completes, so the panel showed "cache hit 0%" beside a log whose last row was 97% cached. When the runner is idle the prompt size and cache hit now come from the last request (marked as such); while it is working the live slot figures stay. The turns bars start right after the label instead of being right-aligned inside a fixed width. Co-Authored-By: Claude Fable 5.1 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/dsl/jbang/core/commands/tui/OllamaMonitor.java | 8 ++++++++ .../camel/dsl/jbang/core/commands/tui/OllamaTab.java | 16 ++++++++++++---- .../dsl/jbang/core/commands/tui/OllamaMonitorTest.java | 1 + .../dsl/jbang/core/commands/tui/OllamaTabRenderTest.java | 15 ++++++++++++++- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitor.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitor.java index 00cf91d53c40..553e11d8ce71 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitor.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitor.java @@ -161,6 +161,14 @@ final class OllamaMonitor { return Math.max(0, (long) inputTokens - cachedTokens); } + /** Share of the prompt Ollama served from its cache. */ + int cacheHitPercent() { + if (inputTokens <= 0) { + return 0; + } + return (int) Math.min(100, (long) cachedTokens * 100 / inputTokens); + } + /** Share of the context window the prompt filled, or -1 when the window is unknown. */ int contextPercent() { if (contextSize <= 0) { diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTab.java index ec4b7521eacd..7769b54d46f9 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTab.java @@ -317,9 +317,16 @@ class OllamaTab extends AbstractTab { Span.styled(" " + gaugeBar(pct, gaugeWidth) + " ", pct >= 90 ? Theme.error() : Theme.info()), Span.styled(formatTokens(used) + " / " + formatTokens(size), Theme.label()), Span.styled(" (" + pct + "%)", Theme.muted()))); - lines.add(Line.from(Span.styled(" prompt " + formatTokens(slot.promptTokens()) + " · cache hit " - + slot.cacheHitPercent() + "%" - + (slot.slots() > 1 ? " · " + slot.slots() + " slots" : ""), + // the runner clears its cache count once a request is done, so when idle the last request tells + RequestEntry last = s.lastRequest(); + String promptLine; + if (slot.processing() || last == null) { + promptLine = " prompt " + formatTokens(slot.promptTokens()) + " · cache hit " + slot.cacheHitPercent() + "%"; + } else { + promptLine = " prompt " + formatTokens(last.promptTokens()) + " · cache hit " + last.cacheHitPercent() + + "% (last request)"; + } + lines.add(Line.from(Span.styled(promptLine + (slot.slots() > 1 ? " · " + slot.slots() + " slots" : ""), Theme.muted()))); List<Span> state = new ArrayList<>(); if (slot.processing()) { @@ -396,9 +403,10 @@ class OllamaTab extends AbstractTab { data[n - 1 - i] = turns.get(i).contextPercent(); } Style level = last >= 80 ? Theme.error() : last >= 50 ? Theme.warning() : Theme.info(); + // bars start right after the label and grow to the right as turns are added return Line.from( Span.styled(" turns ", Theme.muted()), - Span.styled(sparkline(data, barWidth, 100), level), + Span.styled(sparkline(data, n, 100), level), Span.styled(suffix, level)); } diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitorTest.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitorTest.java index a4783ed5fb64..60589f1933e6 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitorTest.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaMonitorTest.java @@ -77,6 +77,7 @@ class OllamaMonitorTest { "stop"); RequestEntry e = monitor.snapshot().lastRequest(); assertEquals(150, e.evaluatedTokens()); + assertEquals(98, e.cacheHitPercent()); assertEquals(150 * 1000.0 / 532, e.prefillTokensPerSecond(), 0.1); assertEquals(12_774, e.promptTokens()); assertEquals(150 * 1000.0 / 532, monitor.snapshot().totals().avgPrefillTokensPerSecond(), 0.1); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTabRenderTest.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTabRenderTest.java index 668c73bae5ed..d90b43d5dabf 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTabRenderTest.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/OllamaTabRenderTest.java @@ -100,7 +100,8 @@ class OllamaTabRenderTest { assertTrue(rendered.contains("prefill"), rendered); assertTrue(rendered.contains("TTFT 0.20s"), rendered); assertTrue(rendered.contains("Context"), rendered); - assertTrue(rendered.contains("cache hit 36%"), rendered); + // idle runner: the cache figure comes from the last request (none cached there), not the cleared slot + assertTrue(rendered.contains("cache hit 0% (last request)"), rendered); assertTrue(rendered.contains("idle"), rendered); assertTrue(rendered.contains("speculative draft-mtp"), rendered); assertTrue(rendered.contains("Host"), rendered); @@ -117,6 +118,18 @@ class OllamaTabRenderTest { assertTrue(rendered.contains("stop"), rendered); } + @Test + void workingRunnerShowsTheSlotCacheFigure() { + localServerWithModel(); + monitor.updateRunner(new RunnerInfo(23629, 58237, 262144, 1, "/blob", "llama-server")); + monitor.updateSlot(new SlotState(true, 25, 25, 9, 6, 262144, "draft-mtp", 1, Instant.now())); + monitor.recordRequest("qwen3.6:35b-a3b", new LlmClient.TokenUsage(16, 6, 22, 0, 188, 81, 12, 300), 0, "stop"); + + String rendered = TuiTestHelper.renderToString(new OllamaTab(ctx, monitor), 180, 40); + assertTrue(rendered.contains("cache hit 36%"), rendered); + assertTrue(rendered.contains("working"), rendered); + } + @Test void remoteServerHidesHostPanelAndExplainsMissingLiveState() { monitor.updateServer(new ServerInfo("http://gpu-box.lan:11434", "0.33.3", false));
