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));

Reply via email to