From 28de737c264aba22cad80f72e15441586f7a58ff Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Mon, 8 Jun 2026 14:09:18 +0200 Subject: [PATCH] docs(usage): note Ollama batch-token semantics + Prometheus/billing query divergence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-8 claude-review (no blockers; comment-only): - 🟡 Documented that Ollama's /api/embed prompt_eval_count is assumed batch-level total and is unverified against a live instance (Ollama isn't the Cloud billing provider); if it proves last-item-only, switch to per-item summing. The char estimate already covers versions that omit the field. - 🟡 Noted on the astrolabe_embedding_tokens_total counter that operation="query" is recorded pre-Qdrant, so it can legitimately exceed the billing-store tokens_embedded aggregate when a search fails post-embed — dashboards shouldn't alert on that healthy gap. Deferred (reviewer: "minor nit, acceptable"): record_indexing_usage awaited in the task group — the group awaits all child tasks regardless, the write is best-effort + fast, and start_soon would need the tg threaded into the closure for marginal gain. Deck #284. Co-Authored-By: Claude Opus 4.8 (1M context) --- nextcloud_mcp_server/observability/metrics.py | 6 ++++++ nextcloud_mcp_server/providers/ollama.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/nextcloud_mcp_server/observability/metrics.py b/nextcloud_mcp_server/observability/metrics.py index 99688f20..8e18adb8 100644 --- a/nextcloud_mcp_server/observability/metrics.py +++ b/nextcloud_mcp_server/observability/metrics.py @@ -342,6 +342,12 @@ embedding_chars_total = Counter( # measure, Deck #67). On a dedicated counter (not folded into the chunk/request # metrics above) so query embeds don't inflate indexing dashboards; labelled by # operation = index | query. Always emitted, independent of USAGE_METERING_ENABLED. +# +# Dashboard note: operation="query" is recorded at embed time (before Qdrant / +# verify-on-read), whereas the billing-store tokens_embedded row is written only +# after the search fully succeeds. So this counter can legitimately exceed the +# billing aggregate when a search fails post-embed — don't alert on that gap as +# a divergence bug. embedding_tokens_total = Counter( "astrolabe_embedding_tokens_total", "Total embedding tokens consumed (provider-reported or estimated)", diff --git a/nextcloud_mcp_server/providers/ollama.py b/nextcloud_mcp_server/providers/ollama.py index b541750d..ebd3b6bf 100644 --- a/nextcloud_mcp_server/providers/ollama.py +++ b/nextcloud_mcp_server/providers/ollama.py @@ -164,6 +164,13 @@ class OllamaProvider(Provider): if self._dimension is None and data["embeddings"]: self._dimension = len(data["embeddings"][0]) + # ``prompt_eval_count`` is assumed to be the batch-level total for a + # multi-input /api/embed call. Ollama's API docs aren't explicit + # about batch aggregation; if a version reports only the last + # input's tokens this understates the batch. Unverified against a + # live instance — Ollama isn't the Cloud billing provider (Mistral + # is). If it proves last-item-only, switch to per-item requests and + # sum. The char-based estimate covers versions that omit the field. prompt_eval = data.get("prompt_eval_count") total_tokens += ( round(prompt_eval)