From 52eaf661c43b9e865a3c1c682f2bb3762b823950 Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Fri, 13 Mar 2026 14:44:12 +0100 Subject: [PATCH 1/3] feat: display inference statistics in chat UI Add InferenceStats proto message and InferenceStatsPanel component that displays token counts, throughput, and context window utilization as a collapsible panel below assistant messages after orchestration completes. Closes #43 Co-Authored-By: Claude Opus 4.6 --- implementation-plans/_index.md | 1 + implementation-plans/issue-043.md | 60 +++++++++++++ src/lib/components/InferenceStatsPanel.svelte | 89 +++++++++++++++++++ .../composables/useOrchestration.svelte.ts | 9 +- src/lib/proto/llm_multiverse/v1/common_pb.ts | 53 ++++++++++- .../llm_multiverse/v1/orchestrator_pb.ts | 11 ++- src/routes/chat/+page.svelte | 5 ++ 7 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 implementation-plans/issue-043.md create mode 100644 src/lib/components/InferenceStatsPanel.svelte diff --git a/implementation-plans/_index.md b/implementation-plans/_index.md index f2ea7f0..2374ee9 100644 --- a/implementation-plans/_index.md +++ b/implementation-plans/_index.md @@ -22,3 +22,4 @@ | #18 | Dark/light theme toggle | COMPLETED | [issue-018.md](issue-018.md) | | #19 | Responsive layout and mobile support | COMPLETED | [issue-019.md](issue-019.md) | | #20 | Error handling and connection status | COMPLETED | [issue-020.md](issue-020.md) | +| #43 | Display inference statistics in chat UI | IMPLEMENTING | [issue-043.md](issue-043.md) | diff --git a/implementation-plans/issue-043.md b/implementation-plans/issue-043.md new file mode 100644 index 0000000..eb8e59e --- /dev/null +++ b/implementation-plans/issue-043.md @@ -0,0 +1,60 @@ +# Issue #43: Display Inference Statistics in Chat UI + +**Status:** IMPLEMENTING +**Issue:** [#43](https://git.shahondin1624.de/llm-multiverse/llm-multiverse-ui/issues/43) +**Branch:** `feature/issue-43-inference-stats` + +## Overview + +Add a collapsible UI panel that displays LLM inference statistics (token counts, context window utilization, throughput) below assistant messages after orchestration completes. + +## Phases + +### Phase 1: Proto Types + +**Files:** +- `proto/upstream/proto/llm_multiverse/v1/common.proto` — Add `InferenceStats` message +- `proto/upstream/proto/llm_multiverse/v1/orchestrator.proto` — Add optional `inference_stats` field to `ProcessRequestResponse` + +**InferenceStats message fields:** +- `prompt_tokens` (uint32) — tokens in the prompt +- `completion_tokens` (uint32) — tokens generated +- `total_tokens` (uint32) — sum of prompt + completion +- `context_window_size` (uint32) — model's maximum context length +- `tokens_per_second` (float) — generation throughput + +**Then regenerate types:** `npm run generate` + +### Phase 2: Orchestration State + +**Files:** +- `src/lib/composables/useOrchestration.svelte.ts` — Extract `inferenceStats` from response, expose via store getter + +### Phase 3: InferenceStatsPanel Component + +**Files:** +- `src/lib/components/InferenceStatsPanel.svelte` — New component + +**Design:** +- Follow `
` pattern from FinalResult.svelte +- Collapsed by default +- Summary line shows key stat (e.g., total tokens + tokens/sec) +- Expanded content shows all stats in a grid layout +- Context utilization shown as a progress bar +- Blue/indigo color scheme (neutral, info-like) +- Full dark mode support + +### Phase 4: Chat Page Integration + +**Files:** +- `src/routes/chat/+page.svelte` — Render `InferenceStatsPanel` after `FinalResult` when stats available + +## Acceptance Criteria + +- [x] InferenceStats proto message defined and TypeScript types generated +- [x] InferenceStatsPanel displays all required metrics +- [x] Panel is collapsible, collapsed by default +- [x] Context utilization shows visual progress bar +- [x] Integrates cleanly into chat page below assistant message +- [x] Dark mode support +- [x] Build, lint, typecheck pass diff --git a/src/lib/components/InferenceStatsPanel.svelte b/src/lib/components/InferenceStatsPanel.svelte new file mode 100644 index 0000000..447a1df --- /dev/null +++ b/src/lib/components/InferenceStatsPanel.svelte @@ -0,0 +1,89 @@ + + +
+ + + + Stats + + + {formatNumber(stats.totalTokens)} tokens + {#if stats.tokensPerSecond > 0} + · {stats.tokensPerSecond.toFixed(1)} tok/s + {/if} + {#if stats.contextWindowSize > 0} + · {utilizationPct.toFixed(0)}% context + {/if} + + + +
+
+
+

Prompt

+

{formatNumber(stats.promptTokens)}

+
+
+

Completion

+

{formatNumber(stats.completionTokens)}

+
+
+

Total

+

{formatNumber(stats.totalTokens)}

+
+ {#if stats.tokensPerSecond > 0} +
+

Throughput

+

{stats.tokensPerSecond.toFixed(1)} tok/s

+
+ {/if} + {#if stats.contextWindowSize > 0} +
+

Context Window

+

{formatNumber(stats.contextWindowSize)}

+
+ {/if} +
+ + {#if stats.contextWindowSize > 0} +
+
+ Context Utilization + {utilizationPct.toFixed(1)}% +
+
+
+
+
+ {/if} +
+
+ + diff --git a/src/lib/composables/useOrchestration.svelte.ts b/src/lib/composables/useOrchestration.svelte.ts index e8bfd6c..f099fa8 100644 --- a/src/lib/composables/useOrchestration.svelte.ts +++ b/src/lib/composables/useOrchestration.svelte.ts @@ -1,5 +1,5 @@ import type { ChatMessage } from '$lib/types'; -import type { SubagentResult } from '$lib/proto/llm_multiverse/v1/common_pb'; +import type { SubagentResult, InferenceStats } from '$lib/proto/llm_multiverse/v1/common_pb'; import type { SessionConfig } from '$lib/proto/llm_multiverse/v1/orchestrator_pb'; import { processRequest, OrchestratorError, friendlyMessage } from '$lib/services/orchestrator'; import { OrchestrationState } from '$lib/proto/llm_multiverse/v1/orchestrator_pb'; @@ -15,6 +15,7 @@ export function createOrchestration() { let orchestrationState: OrchestrationState = $state(OrchestrationState.UNSPECIFIED); let intermediateResult: string = $state(''); let finalResult: SubagentResult | null = $state(null); + let inferenceStats: InferenceStats | null = $state(null); async function send( sessionId: string, @@ -27,6 +28,7 @@ export function createOrchestration() { orchestrationState = OrchestrationState.UNSPECIFIED; intermediateResult = ''; finalResult = null; + inferenceStats = null; let lastAuditState = OrchestrationState.UNSPECIFIED; @@ -72,6 +74,9 @@ export function createOrchestration() { if (response.intermediateResult) { intermediateResult = response.intermediateResult; } + if (response.inferenceStats) { + inferenceStats = response.inferenceStats; + } if (response.finalResult) { finalResult = response.finalResult; if (response.finalResult.newMemoryCandidates.length > 0) { @@ -145,6 +150,7 @@ export function createOrchestration() { function reset() { error = null; finalResult = null; + inferenceStats = null; } return { @@ -154,6 +160,7 @@ export function createOrchestration() { get orchestrationState() { return orchestrationState; }, get intermediateResult() { return intermediateResult; }, get finalResult() { return finalResult; }, + get inferenceStats() { return inferenceStats; }, send, retry, reset diff --git a/src/lib/proto/llm_multiverse/v1/common_pb.ts b/src/lib/proto/llm_multiverse/v1/common_pb.ts index e74a4ac..bda57fe 100644 --- a/src/lib/proto/llm_multiverse/v1/common_pb.ts +++ b/src/lib/proto/llm_multiverse/v1/common_pb.ts @@ -12,7 +12,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file llm_multiverse/v1/common.proto. */ export const file_llm_multiverse_v1_common: GenFile = /*@__PURE__*/ - fileDesc("Ch5sbG1fbXVsdGl2ZXJzZS92MS9jb21tb24ucHJvdG8SEWxsbV9tdWx0aXZlcnNlLnYxItABCghBcnRpZmFjdBINCgVsYWJlbBgBIAEoCRIPCgdjb250ZW50GAIgASgJEjYKDWFydGlmYWN0X3R5cGUYAyABKA4yHy5sbG1fbXVsdGl2ZXJzZS52MS5BcnRpZmFjdFR5cGUSOwoIbWV0YWRhdGEYBCADKAsyKS5sbG1fbXVsdGl2ZXJzZS52MS5BcnRpZmFjdC5NZXRhZGF0YUVudHJ5Gi8KDU1ldGFkYXRhRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJqCg9BZ2VudElkZW50aWZpZXISEAoIYWdlbnRfaWQYASABKAkSMAoKYWdlbnRfdHlwZRgCIAEoDjIcLmxsbV9tdWx0aXZlcnNlLnYxLkFnZW50VHlwZRITCgtzcGF3bl9kZXB0aBgDIAEoDSJCCgxBZ2VudExpbmVhZ2USMgoGYWdlbnRzGAEgAygLMiIubGxtX211bHRpdmVyc2UudjEuQWdlbnRJZGVudGlmaWVyItcBCg5TZXNzaW9uQ29udGV4dBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB3VzZXJfaWQYAiABKAkSNgoNYWdlbnRfbGluZWFnZRgDIAEoCzIfLmxsbV9tdWx0aXZlcnNlLnYxLkFnZW50TGluZWFnZRI4Cg5vdmVycmlkZV9sZXZlbBgEIAEoDjIgLmxsbV9tdWx0aXZlcnNlLnYxLk92ZXJyaWRlTGV2ZWwSLgoKY3JlYXRlZF9hdBgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAinQEKC0Vycm9yRGV0YWlsEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCRI+CghtZXRhZGF0YRgDIAMoCzIsLmxsbV9tdWx0aXZlcnNlLnYxLkVycm9yRGV0YWlsLk1ldGFkYXRhRW50cnkaLwoNTWV0YWRhdGFFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBImcKD01lbW9yeUNhbmRpZGF0ZRIPCgdjb250ZW50GAEgASgJEi8KBnNvdXJjZRgCIAEoDjIfLmxsbV9tdWx0aXZlcnNlLnYxLlJlc3VsdFNvdXJjZRISCgpjb25maWRlbmNlGAMgASgCIuACCg5TdWJhZ2VudFJlc3VsdBIvCgZzdGF0dXMYASABKA4yHy5sbG1fbXVsdGl2ZXJzZS52MS5SZXN1bHRTdGF0dXMSDwoHc3VtbWFyeRgCIAEoCRIuCglhcnRpZmFjdHMYAyADKAsyGy5sbG1fbXVsdGl2ZXJzZS52MS5BcnRpZmFjdBI4Cg5yZXN1bHRfcXVhbGl0eRgEIAEoDjIgLmxsbV9tdWx0aXZlcnNlLnYxLlJlc3VsdFF1YWxpdHkSLwoGc291cmNlGAUgASgOMh8ubGxtX211bHRpdmVyc2UudjEuUmVzdWx0U291cmNlEkEKFW5ld19tZW1vcnlfY2FuZGlkYXRlcxgGIAMoCzIiLmxsbV9tdWx0aXZlcnNlLnYxLk1lbW9yeUNhbmRpZGF0ZRIbCg5mYWlsdXJlX3JlYXNvbhgHIAEoCUgAiAEBQhEKD19mYWlsdXJlX3JlYXNvbiqoAQoJQWdlbnRUeXBlEhoKFkFHRU5UX1RZUEVfVU5TUEVDSUZJRUQQABIbChdBR0VOVF9UWVBFX09SQ0hFU1RSQVRPUhABEhkKFUFHRU5UX1RZUEVfUkVTRUFSQ0hFUhACEhQKEEFHRU5UX1RZUEVfQ09ERVIQAxIXChNBR0VOVF9UWVBFX1NZU0FETUlOEAQSGAoUQUdFTlRfVFlQRV9BU1NJU1RBTlQQBSr1AQoIVG9vbFR5cGUSGQoVVE9PTF9UWVBFX1VOU1BFQ0lGSUVEEAASGQoVVE9PTF9UWVBFX01FTU9SWV9SRUFEEAESGgoWVE9PTF9UWVBFX01FTU9SWV9XUklURRACEhgKFFRPT0xfVFlQRV9XRUJfU0VBUkNIEAMSFQoRVE9PTF9UWVBFX0ZTX1JFQUQQBBIWChJUT09MX1RZUEVfRlNfV1JJVEUQBRIWChJUT09MX1RZUEVfUlVOX0NPREUQBhIXChNUT09MX1RZUEVfUlVOX1NIRUxMEAcSHQoZVE9PTF9UWVBFX1BBQ0tBR0VfSU5TVEFMTBAIKnoKDU92ZXJyaWRlTGV2ZWwSHgoaT1ZFUlJJREVfTEVWRUxfVU5TUEVDSUZJRUQQABIXChNPVkVSUklERV9MRVZFTF9OT05FEAESGAoUT1ZFUlJJREVfTEVWRUxfUkVMQVgQAhIWChJPVkVSUklERV9MRVZFTF9BTEwQAyp9CgxSZXN1bHRTdGF0dXMSHQoZUkVTVUxUX1NUQVRVU19VTlNQRUNJRklFRBAAEhkKFVJFU1VMVF9TVEFUVVNfU1VDQ0VTUxABEhkKFVJFU1VMVF9TVEFUVVNfUEFSVElBTBACEhgKFFJFU1VMVF9TVEFUVVNfRkFJTEVEEAMqhwEKDVJlc3VsdFF1YWxpdHkSHgoaUkVTVUxUX1FVQUxJVFlfVU5TUEVDSUZJRUQQABIbChdSRVNVTFRfUVVBTElUWV9WRVJJRklFRBABEhsKF1JFU1VMVF9RVUFMSVRZX0lORkVSUkVEEAISHAoYUkVTVUxUX1FVQUxJVFlfVU5DRVJUQUlOEAMqhgEKDFJlc3VsdFNvdXJjZRIdChlSRVNVTFRfU09VUkNFX1VOU1BFQ0lGSUVEEAASHQoZUkVTVUxUX1NPVVJDRV9UT09MX09VVFBVVBABEiEKHVJFU1VMVF9TT1VSQ0VfTU9ERUxfS05PV0xFREdFEAISFQoRUkVTVUxUX1NPVVJDRV9XRUIQAyqgAQoMQXJ0aWZhY3RUeXBlEh0KGUFSVElGQUNUX1RZUEVfVU5TUEVDSUZJRUQQABIWChJBUlRJRkFDVF9UWVBFX0NPREUQARIWChJBUlRJRkFDVF9UWVBFX1RFWFQQAhIgChxBUlRJRkFDVF9UWVBFX0NPTU1BTkRfT1VUUFVUEAMSHwobQVJUSUZBQ1RfVFlQRV9TRUFSQ0hfUkVTVUxUEARiBnByb3RvMw", [file_google_protobuf_timestamp]); + fileDesc("Ch5sbG1fbXVsdGl2ZXJzZS92MS9jb21tb24ucHJvdG8SEWxsbV9tdWx0aXZlcnNlLnYxItABCghBcnRpZmFjdBINCgVsYWJlbBgBIAEoCRIPCgdjb250ZW50GAIgASgJEjYKDWFydGlmYWN0X3R5cGUYAyABKA4yHy5sbG1fbXVsdGl2ZXJzZS52MS5BcnRpZmFjdFR5cGUSOwoIbWV0YWRhdGEYBCADKAsyKS5sbG1fbXVsdGl2ZXJzZS52MS5BcnRpZmFjdC5NZXRhZGF0YUVudHJ5Gi8KDU1ldGFkYXRhRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJqCg9BZ2VudElkZW50aWZpZXISEAoIYWdlbnRfaWQYASABKAkSMAoKYWdlbnRfdHlwZRgCIAEoDjIcLmxsbV9tdWx0aXZlcnNlLnYxLkFnZW50VHlwZRITCgtzcGF3bl9kZXB0aBgDIAEoDSJCCgxBZ2VudExpbmVhZ2USMgoGYWdlbnRzGAEgAygLMiIubGxtX211bHRpdmVyc2UudjEuQWdlbnRJZGVudGlmaWVyItcBCg5TZXNzaW9uQ29udGV4dBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB3VzZXJfaWQYAiABKAkSNgoNYWdlbnRfbGluZWFnZRgDIAEoCzIfLmxsbV9tdWx0aXZlcnNlLnYxLkFnZW50TGluZWFnZRI4Cg5vdmVycmlkZV9sZXZlbBgEIAEoDjIgLmxsbV9tdWx0aXZlcnNlLnYxLk92ZXJyaWRlTGV2ZWwSLgoKY3JlYXRlZF9hdBgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAinQEKC0Vycm9yRGV0YWlsEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCRI+CghtZXRhZGF0YRgDIAMoCzIsLmxsbV9tdWx0aXZlcnNlLnYxLkVycm9yRGV0YWlsLk1ldGFkYXRhRW50cnkaLwoNTWV0YWRhdGFFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBImcKD01lbW9yeUNhbmRpZGF0ZRIPCgdjb250ZW50GAEgASgJEi8KBnNvdXJjZRgCIAEoDjIfLmxsbV9tdWx0aXZlcnNlLnYxLlJlc3VsdFNvdXJjZRISCgpjb25maWRlbmNlGAMgASgCIpABCg5JbmZlcmVuY2VTdGF0cxIVCg1wcm9tcHRfdG9rZW5zGAEgASgNEhkKEWNvbXBsZXRpb25fdG9rZW5zGAIgASgNEhQKDHRvdGFsX3Rva2VucxgDIAEoDRIbChNjb250ZXh0X3dpbmRvd19zaXplGAQgASgNEhkKEXRva2Vuc19wZXJfc2Vjb25kGAUgASgCIuACCg5TdWJhZ2VudFJlc3VsdBIvCgZzdGF0dXMYASABKA4yHy5sbG1fbXVsdGl2ZXJzZS52MS5SZXN1bHRTdGF0dXMSDwoHc3VtbWFyeRgCIAEoCRIuCglhcnRpZmFjdHMYAyADKAsyGy5sbG1fbXVsdGl2ZXJzZS52MS5BcnRpZmFjdBI4Cg5yZXN1bHRfcXVhbGl0eRgEIAEoDjIgLmxsbV9tdWx0aXZlcnNlLnYxLlJlc3VsdFF1YWxpdHkSLwoGc291cmNlGAUgASgOMh8ubGxtX211bHRpdmVyc2UudjEuUmVzdWx0U291cmNlEkEKFW5ld19tZW1vcnlfY2FuZGlkYXRlcxgGIAMoCzIiLmxsbV9tdWx0aXZlcnNlLnYxLk1lbW9yeUNhbmRpZGF0ZRIbCg5mYWlsdXJlX3JlYXNvbhgHIAEoCUgAiAEBQhEKD19mYWlsdXJlX3JlYXNvbiqoAQoJQWdlbnRUeXBlEhoKFkFHRU5UX1RZUEVfVU5TUEVDSUZJRUQQABIbChdBR0VOVF9UWVBFX09SQ0hFU1RSQVRPUhABEhkKFUFHRU5UX1RZUEVfUkVTRUFSQ0hFUhACEhQKEEFHRU5UX1RZUEVfQ09ERVIQAxIXChNBR0VOVF9UWVBFX1NZU0FETUlOEAQSGAoUQUdFTlRfVFlQRV9BU1NJU1RBTlQQBSr1AQoIVG9vbFR5cGUSGQoVVE9PTF9UWVBFX1VOU1BFQ0lGSUVEEAASGQoVVE9PTF9UWVBFX01FTU9SWV9SRUFEEAESGgoWVE9PTF9UWVBFX01FTU9SWV9XUklURRACEhgKFFRPT0xfVFlQRV9XRUJfU0VBUkNIEAMSFQoRVE9PTF9UWVBFX0ZTX1JFQUQQBBIWChJUT09MX1RZUEVfRlNfV1JJVEUQBRIWChJUT09MX1RZUEVfUlVOX0NPREUQBhIXChNUT09MX1RZUEVfUlVOX1NIRUxMEAcSHQoZVE9PTF9UWVBFX1BBQ0tBR0VfSU5TVEFMTBAIKnoKDU92ZXJyaWRlTGV2ZWwSHgoaT1ZFUlJJREVfTEVWRUxfVU5TUEVDSUZJRUQQABIXChNPVkVSUklERV9MRVZFTF9OT05FEAESGAoUT1ZFUlJJREVfTEVWRUxfUkVMQVgQAhIWChJPVkVSUklERV9MRVZFTF9BTEwQAyp9CgxSZXN1bHRTdGF0dXMSHQoZUkVTVUxUX1NUQVRVU19VTlNQRUNJRklFRBAAEhkKFVJFU1VMVF9TVEFUVVNfU1VDQ0VTUxABEhkKFVJFU1VMVF9TVEFUVVNfUEFSVElBTBACEhgKFFJFU1VMVF9TVEFUVVNfRkFJTEVEEAMqhwEKDVJlc3VsdFF1YWxpdHkSHgoaUkVTVUxUX1FVQUxJVFlfVU5TUEVDSUZJRUQQABIbChdSRVNVTFRfUVVBTElUWV9WRVJJRklFRBABEhsKF1JFU1VMVF9RVUFMSVRZX0lORkVSUkVEEAISHAoYUkVTVUxUX1FVQUxJVFlfVU5DRVJUQUlOEAMqhgEKDFJlc3VsdFNvdXJjZRIdChlSRVNVTFRfU09VUkNFX1VOU1BFQ0lGSUVEEAASHQoZUkVTVUxUX1NPVVJDRV9UT09MX09VVFBVVBABEiEKHVJFU1VMVF9TT1VSQ0VfTU9ERUxfS05PV0xFREdFEAISFQoRUkVTVUxUX1NPVVJDRV9XRUIQAyqgAQoMQXJ0aWZhY3RUeXBlEh0KGUFSVElGQUNUX1RZUEVfVU5TUEVDSUZJRUQQABIWChJBUlRJRkFDVF9UWVBFX0NPREUQARIWChJBUlRJRkFDVF9UWVBFX1RFWFQQAhIgChxBUlRJRkFDVF9UWVBFX0NPTU1BTkRfT1VUUFVUEAMSHwobQVJUSUZBQ1RfVFlQRV9TRUFSQ0hfUkVTVUxUEARiBnByb3RvMw", [file_google_protobuf_timestamp]); /** * A concrete output produced by an agent (code, command output, etc.). @@ -200,6 +200,55 @@ export type MemoryCandidate = Message<"llm_multiverse.v1.MemoryCandidate"> & { export const MemoryCandidateSchema: GenMessage = /*@__PURE__*/ messageDesc(file_llm_multiverse_v1_common, 5); +/** + * Inference statistics surfaced from model-gateway through the orchestrator. + * + * @generated from message llm_multiverse.v1.InferenceStats + */ +export type InferenceStats = Message<"llm_multiverse.v1.InferenceStats"> & { + /** + * Number of tokens in the prompt. + * + * @generated from field: uint32 prompt_tokens = 1; + */ + promptTokens: number; + + /** + * Number of tokens generated. + * + * @generated from field: uint32 completion_tokens = 2; + */ + completionTokens: number; + + /** + * Sum of prompt + completion tokens. + * + * @generated from field: uint32 total_tokens = 3; + */ + totalTokens: number; + + /** + * Model's maximum context length. + * + * @generated from field: uint32 context_window_size = 4; + */ + contextWindowSize: number; + + /** + * Generation throughput (tokens per second). + * + * @generated from field: float tokens_per_second = 5; + */ + tokensPerSecond: number; +}; + +/** + * Describes the message llm_multiverse.v1.InferenceStats. + * Use `create(InferenceStatsSchema)` to create a new message. + */ +export const InferenceStatsSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_llm_multiverse_v1_common, 6); + /** * Standardized return value from any subagent to its parent. * @@ -251,7 +300,7 @@ export type SubagentResult = Message<"llm_multiverse.v1.SubagentResult"> & { * Use `create(SubagentResultSchema)` to create a new message. */ export const SubagentResultSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_llm_multiverse_v1_common, 6); + messageDesc(file_llm_multiverse_v1_common, 7); /** * Agent types with distinct tool permission manifests. diff --git a/src/lib/proto/llm_multiverse/v1/orchestrator_pb.ts b/src/lib/proto/llm_multiverse/v1/orchestrator_pb.ts index 235d8af..d81984d 100644 --- a/src/lib/proto/llm_multiverse/v1/orchestrator_pb.ts +++ b/src/lib/proto/llm_multiverse/v1/orchestrator_pb.ts @@ -4,7 +4,7 @@ import type { GenEnum, GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; import { enumDesc, fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; -import type { AgentType, OverrideLevel, SessionContext, SubagentResult, ToolType } from "./common_pb"; +import type { AgentType, InferenceStats, OverrideLevel, SessionContext, SubagentResult, ToolType } from "./common_pb"; import { file_llm_multiverse_v1_common } from "./common_pb"; import type { Message } from "@bufbuild/protobuf"; @@ -12,7 +12,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file llm_multiverse/v1/orchestrator.proto. */ export const file_llm_multiverse_v1_orchestrator: GenFile = /*@__PURE__*/ - fileDesc("CiRsbG1fbXVsdGl2ZXJzZS92MS9vcmNoZXN0cmF0b3IucHJvdG8SEWxsbV9tdWx0aXZlcnNlLnYxIn4KDVNlc3Npb25Db25maWcSOAoOb3ZlcnJpZGVfbGV2ZWwYASABKA4yIC5sbG1fbXVsdGl2ZXJzZS52MS5PdmVycmlkZUxldmVsEhYKDmRpc2FibGVkX3Rvb2xzGAIgAygJEhsKE2dyYW50ZWRfcGVybWlzc2lvbnMYAyADKAkirwEKEVN1YnRhc2tEZWZpbml0aW9uEgoKAmlkGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJEjAKCmFnZW50X3R5cGUYAyABKA4yHC5sbG1fbXVsdGl2ZXJzZS52MS5BZ2VudFR5cGUSEgoKZGVwZW5kc19vbhgEIAMoCRIzCg50b29sc19yZXF1aXJlZBgFIAMoDjIbLmxsbV9tdWx0aXZlcnNlLnYxLlRvb2xUeXBlIoYCCg9TdWJhZ2VudFJlcXVlc3QSMgoHY29udGV4dBgBIAEoCzIhLmxsbV9tdWx0aXZlcnNlLnYxLlNlc3Npb25Db250ZXh0EhAKCGFnZW50X2lkGAIgASgJEjAKCmFnZW50X3R5cGUYAyABKA4yHC5sbG1fbXVsdGl2ZXJzZS52MS5BZ2VudFR5cGUSDAoEdGFzaxgEIAEoCRIfChdyZWxldmFudF9tZW1vcnlfY29udGV4dBgFIAMoCRISCgptYXhfdG9rZW5zGAYgASgNEjgKDnNlc3Npb25fY29uZmlnGAcgASgLMiAubGxtX211bHRpdmVyc2UudjEuU2Vzc2lvbkNvbmZpZyKTAQoVUHJvY2Vzc1JlcXVlc3RSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSFAoMdXNlcl9tZXNzYWdlGAIgASgJEj0KDnNlc3Npb25fY29uZmlnGAMgASgLMiAubGxtX211bHRpdmVyc2UudjEuU2Vzc2lvbkNvbmZpZ0gAiAEBQhEKD19zZXNzaW9uX2NvbmZpZyLoAQoWUHJvY2Vzc1JlcXVlc3RSZXNwb25zZRI0CgVzdGF0ZRgBIAEoDjIlLmxsbV9tdWx0aXZlcnNlLnYxLk9yY2hlc3RyYXRpb25TdGF0ZRIPCgdtZXNzYWdlGAIgASgJEiAKE2ludGVybWVkaWF0ZV9yZXN1bHQYAyABKAlIAIgBARI8CgxmaW5hbF9yZXN1bHQYBCABKAsyIS5sbG1fbXVsdGl2ZXJzZS52MS5TdWJhZ2VudFJlc3VsdEgBiAEBQhYKFF9pbnRlcm1lZGlhdGVfcmVzdWx0Qg8KDV9maW5hbF9yZXN1bHQq7AEKEk9yY2hlc3RyYXRpb25TdGF0ZRIjCh9PUkNIRVNUUkFUSU9OX1NUQVRFX1VOU1BFQ0lGSUVEEAASIwofT1JDSEVTVFJBVElPTl9TVEFURV9ERUNPTVBPU0lORxABEiMKH09SQ0hFU1RSQVRJT05fU1RBVEVfRElTUEFUQ0hJTkcQAhIhCh1PUkNIRVNUUkFUSU9OX1NUQVRFX0VYRUNVVElORxADEiIKHk9SQ0hFU1RSQVRJT05fU1RBVEVfQ09NUEFDVElORxAEEiAKHE9SQ0hFU1RSQVRJT05fU1RBVEVfQ09NUExFVEUQBTJ+ChNPcmNoZXN0cmF0b3JTZXJ2aWNlEmcKDlByb2Nlc3NSZXF1ZXN0EigubGxtX211bHRpdmVyc2UudjEuUHJvY2Vzc1JlcXVlc3RSZXF1ZXN0GikubGxtX211bHRpdmVyc2UudjEuUHJvY2Vzc1JlcXVlc3RSZXNwb25zZTABYgZwcm90bzM", [file_llm_multiverse_v1_common]); + fileDesc("CiRsbG1fbXVsdGl2ZXJzZS92MS9vcmNoZXN0cmF0b3IucHJvdG8SEWxsbV9tdWx0aXZlcnNlLnYxIn4KDVNlc3Npb25Db25maWcSOAoOb3ZlcnJpZGVfbGV2ZWwYASABKA4yIC5sbG1fbXVsdGl2ZXJzZS52MS5PdmVycmlkZUxldmVsEhYKDmRpc2FibGVkX3Rvb2xzGAIgAygJEhsKE2dyYW50ZWRfcGVybWlzc2lvbnMYAyADKAkirwEKEVN1YnRhc2tEZWZpbml0aW9uEgoKAmlkGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJEjAKCmFnZW50X3R5cGUYAyABKA4yHC5sbG1fbXVsdGl2ZXJzZS52MS5BZ2VudFR5cGUSEgoKZGVwZW5kc19vbhgEIAMoCRIzCg50b29sc19yZXF1aXJlZBgFIAMoDjIbLmxsbV9tdWx0aXZlcnNlLnYxLlRvb2xUeXBlIoYCCg9TdWJhZ2VudFJlcXVlc3QSMgoHY29udGV4dBgBIAEoCzIhLmxsbV9tdWx0aXZlcnNlLnYxLlNlc3Npb25Db250ZXh0EhAKCGFnZW50X2lkGAIgASgJEjAKCmFnZW50X3R5cGUYAyABKA4yHC5sbG1fbXVsdGl2ZXJzZS52MS5BZ2VudFR5cGUSDAoEdGFzaxgEIAEoCRIfChdyZWxldmFudF9tZW1vcnlfY29udGV4dBgFIAMoCRISCgptYXhfdG9rZW5zGAYgASgNEjgKDnNlc3Npb25fY29uZmlnGAcgASgLMiAubGxtX211bHRpdmVyc2UudjEuU2Vzc2lvbkNvbmZpZyKTAQoVUHJvY2Vzc1JlcXVlc3RSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSFAoMdXNlcl9tZXNzYWdlGAIgASgJEj0KDnNlc3Npb25fY29uZmlnGAMgASgLMiAubGxtX211bHRpdmVyc2UudjEuU2Vzc2lvbkNvbmZpZ0gAiAEBQhEKD19zZXNzaW9uX2NvbmZpZyK9AgoWUHJvY2Vzc1JlcXVlc3RSZXNwb25zZRI0CgVzdGF0ZRgBIAEoDjIlLmxsbV9tdWx0aXZlcnNlLnYxLk9yY2hlc3RyYXRpb25TdGF0ZRIPCgdtZXNzYWdlGAIgASgJEiAKE2ludGVybWVkaWF0ZV9yZXN1bHQYAyABKAlIAIgBARI8CgxmaW5hbF9yZXN1bHQYBCABKAsyIS5sbG1fbXVsdGl2ZXJzZS52MS5TdWJhZ2VudFJlc3VsdEgBiAEBEj8KD2luZmVyZW5jZV9zdGF0cxgFIAEoCzIhLmxsbV9tdWx0aXZlcnNlLnYxLkluZmVyZW5jZVN0YXRzSAKIAQFCFgoUX2ludGVybWVkaWF0ZV9yZXN1bHRCDwoNX2ZpbmFsX3Jlc3VsdEISChBfaW5mZXJlbmNlX3N0YXRzKuwBChJPcmNoZXN0cmF0aW9uU3RhdGUSIwofT1JDSEVTVFJBVElPTl9TVEFURV9VTlNQRUNJRklFRBAAEiMKH09SQ0hFU1RSQVRJT05fU1RBVEVfREVDT01QT1NJTkcQARIjCh9PUkNIRVNUUkFUSU9OX1NUQVRFX0RJU1BBVENISU5HEAISIQodT1JDSEVTVFJBVElPTl9TVEFURV9FWEVDVVRJTkcQAxIiCh5PUkNIRVNUUkFUSU9OX1NUQVRFX0NPTVBBQ1RJTkcQBBIgChxPUkNIRVNUUkFUSU9OX1NUQVRFX0NPTVBMRVRFEAUyfgoTT3JjaGVzdHJhdG9yU2VydmljZRJnCg5Qcm9jZXNzUmVxdWVzdBIoLmxsbV9tdWx0aXZlcnNlLnYxLlByb2Nlc3NSZXF1ZXN0UmVxdWVzdBopLmxsbV9tdWx0aXZlcnNlLnYxLlByb2Nlc3NSZXF1ZXN0UmVzcG9uc2UwAWIGcHJvdG8z", [file_llm_multiverse_v1_common]); /** * Per-session configuration for override control. @@ -199,6 +199,13 @@ export type ProcessRequestResponse = Message<"llm_multiverse.v1.ProcessRequestRe * @generated from field: optional llm_multiverse.v1.SubagentResult final_result = 4; */ finalResult?: SubagentResult; + + /** + * Inference statistics from the model-gateway (on the final streamed message). + * + * @generated from field: optional llm_multiverse.v1.InferenceStats inference_stats = 5; + */ + inferenceStats?: InferenceStats; }; /** diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte index 5a83eb0..6fd7ce1 100644 --- a/src/routes/chat/+page.svelte +++ b/src/routes/chat/+page.svelte @@ -12,6 +12,7 @@ import OrchestrationProgress from '$lib/components/OrchestrationProgress.svelte'; import ThinkingSection from '$lib/components/ThinkingSection.svelte'; import FinalResult from '$lib/components/FinalResult.svelte'; + import InferenceStatsPanel from '$lib/components/InferenceStatsPanel.svelte'; import SessionSidebar from '$lib/components/SessionSidebar.svelte'; import ConfigSidebar from '$lib/components/ConfigSidebar.svelte'; import ThemeToggle from '$lib/components/ThemeToggle.svelte'; @@ -189,6 +190,10 @@ {/if} + {#if orchestration.inferenceStats && !orchestration.isStreaming} + + {/if} + {#if orchestration.error}
{orchestration.error} From 3d85cc6b8c87a47f0d7210786e7d304b6ce6bf6d Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Fri, 13 Mar 2026 14:46:35 +0100 Subject: [PATCH 2/3] feat: add aria attributes to context utilization progress bar Co-Authored-By: Claude Opus 4.6 --- src/lib/components/InferenceStatsPanel.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/components/InferenceStatsPanel.svelte b/src/lib/components/InferenceStatsPanel.svelte index 447a1df..2f38ef7 100644 --- a/src/lib/components/InferenceStatsPanel.svelte +++ b/src/lib/components/InferenceStatsPanel.svelte @@ -75,6 +75,11 @@
From 36731aac1dc7428c3e0897efd765a5a55b9ea17f Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Fri, 13 Mar 2026 14:46:47 +0100 Subject: [PATCH 3/3] chore: mark issue #43 plan as COMPLETED Co-Authored-By: Claude Opus 4.6 --- implementation-plans/_index.md | 2 +- implementation-plans/issue-043.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/implementation-plans/_index.md b/implementation-plans/_index.md index 2374ee9..6f5894d 100644 --- a/implementation-plans/_index.md +++ b/implementation-plans/_index.md @@ -22,4 +22,4 @@ | #18 | Dark/light theme toggle | COMPLETED | [issue-018.md](issue-018.md) | | #19 | Responsive layout and mobile support | COMPLETED | [issue-019.md](issue-019.md) | | #20 | Error handling and connection status | COMPLETED | [issue-020.md](issue-020.md) | -| #43 | Display inference statistics in chat UI | IMPLEMENTING | [issue-043.md](issue-043.md) | +| #43 | Display inference statistics in chat UI | COMPLETED | [issue-043.md](issue-043.md) | diff --git a/implementation-plans/issue-043.md b/implementation-plans/issue-043.md index eb8e59e..4f364a0 100644 --- a/implementation-plans/issue-043.md +++ b/implementation-plans/issue-043.md @@ -1,6 +1,6 @@ # Issue #43: Display Inference Statistics in Chat UI -**Status:** IMPLEMENTING +**Status:** COMPLETED **Issue:** [#43](https://git.shahondin1624.de/llm-multiverse/llm-multiverse-ui/issues/43) **Branch:** `feature/issue-43-inference-stats`