From 38f5f31b92b68594fec38bd36ebd5099473e2706 Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Thu, 12 Mar 2026 13:48:06 +0100 Subject: [PATCH] =?UTF-8?q?refactor:=20code=20review=20improvements=20?= =?UTF-8?q?=E2=80=94=20fix=20bugs,=20extract=20shared=20utilities,=20add?= =?UTF-8?q?=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix reactivity bug: use SvelteMap instead of Map in sessions store - Fix theme listener memory leak: guard against double-init, return cleanup function - Fix transport singleton ignoring different endpoints - Fix form/button type mismatch in MessageInput - Add safer retry validation in chat page - Extract shared utilities: date formatting, session config check, result source config - Extract shared components: Backdrop, PageHeader - Extract orchestration composable from chat page (310→85 lines of script) - Consolidate AuditTimeline switch functions into config record - Move sample data to dedicated module - Add dark mode support for LineageTree SVG colors - Memoize leaf count computation in LineageTree - Update README with usage guide and project structure Co-Authored-By: Claude Opus 4.6 --- README.md | 97 +++++++- src/lib/components/AuditTimeline.svelte | 82 +++---- src/lib/components/Backdrop.svelte | 12 + src/lib/components/ConfigSidebar.svelte | 18 +- src/lib/components/FinalResult.svelte | 18 +- src/lib/components/LineageTree.svelte | 35 ++- src/lib/components/MemoryCandidateCard.svelte | 15 +- src/lib/components/MessageInput.svelte | 5 +- src/lib/components/PageHeader.svelte | 45 ++++ src/lib/components/SessionSidebar.svelte | 24 +- .../composables/useOrchestration.svelte.ts | 151 +++++++++++++ src/lib/data/sampleData.ts | 210 ++++++++++++++++++ src/lib/services/orchestrator.ts | 14 +- src/lib/stores/audit.svelte.ts | 153 +------------ src/lib/stores/memory.svelte.ts | 59 +---- src/lib/stores/sessions.svelte.ts | 15 +- src/lib/stores/theme.svelte.ts | 21 +- src/lib/types/lineage.ts | 60 ++--- src/lib/types/resultSource.ts | 23 ++ src/lib/utils/date.ts | 19 ++ src/lib/utils/sessionConfig.ts | 13 ++ src/routes/+layout.svelte | 6 +- src/routes/audit/+page.svelte | 23 +- src/routes/chat/+page.svelte | 147 ++---------- src/routes/lineage/+page.svelte | 44 +--- src/routes/memory/+page.svelte | 23 +- 26 files changed, 753 insertions(+), 579 deletions(-) create mode 100644 src/lib/components/Backdrop.svelte create mode 100644 src/lib/components/PageHeader.svelte create mode 100644 src/lib/composables/useOrchestration.svelte.ts create mode 100644 src/lib/data/sampleData.ts create mode 100644 src/lib/types/resultSource.ts create mode 100644 src/lib/utils/date.ts create mode 100644 src/lib/utils/sessionConfig.ts diff --git a/README.md b/README.md index 5419dd0..0003222 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,98 @@ # llm-multiverse-ui -Web frontend for the llm-multiverse orchestration system +Web frontend for the **llm-multiverse** orchestration system. Built with SvelteKit 5, TypeScript, Tailwind CSS v4, and gRPC-Web (Connect). + +## Prerequisites + +- Node.js 20+ +- A running llm-multiverse gRPC backend (the UI proxies requests through Vite/your production reverse proxy) + +## Getting Started + +```bash +# Install dependencies +npm install + +# Generate protobuf TypeScript definitions (only needed after .proto changes) +npm run generate + +# Start the dev server +npm run dev +``` + +The app starts at `http://localhost:5173` by default. + +## Scripts + +| Command | Description | +| ------------------- | ------------------------------------------------ | +| `npm run dev` | Start Vite dev server with HMR | +| `npm run build` | Production build | +| `npm run preview` | Preview the production build locally | +| `npm run check` | Run svelte-check for TypeScript/Svelte errors | +| `npm run lint` | Run ESLint | +| `npm run format` | Format code with Prettier | +| `npm run generate` | Regenerate protobuf TypeScript from `.proto` files | + +## Using the UI + +### Landing Page + +The root page (`/`) links to every section of the app. + +### Chat (`/chat`) + +The main interface for interacting with the orchestrator. + +- **Sessions** are listed in the left sidebar. Click **+ New Chat** to start a session, or select an existing one. On mobile, tap the hamburger menu to open the sidebar. +- Type a message and press **Enter** (or the **Send** button) to send it. While the orchestrator is processing, a progress indicator and thinking section show the current state. +- **Session Config** (gear icon / "Config" button) opens a right sidebar where you can adjust the override level, disable specific tools, grant permissions, and save/load presets. +- If a request fails, an error banner appears with a **Retry** button. +- Navigation links to Lineage, Memory, and Audit are in the header (hidden on small screens). + +### Agent Lineage (`/lineage`) + +Visualizes the agent spawn tree as an interactive SVG diagram. + +- Click a node to view agent details (ID, type, depth, children) in a side panel. +- A legend at the bottom shows agent type colors. +- Supports dark mode with theme-aware SVG colors. + +### Memory Candidates (`/memory`) + +Lists memory candidates captured during orchestration, grouped by session. + +- **Source filter** — filter by Tool Output, Model Knowledge, or Web. +- **Confidence slider** — set a minimum confidence threshold. +- Each card shows the candidate content, source badge, and a confidence bar. + +### Audit Log (`/audit`) + +Timeline view of orchestration events for each session. + +- Select a session from the dropdown, then optionally filter by event type (State Change, Tool, Error, Message). +- Events are displayed chronologically with date separators and color-coded badges. + +### Theme + +Use the theme toggle (sun/moon icon in any header) to cycle between **System**, **Light**, and **Dark** modes. The preference is persisted in localStorage. + +## Project Structure + +``` +src/ + lib/ + components/ Svelte components (Backdrop, PageHeader, MessageInput, ...) + composables/ Reactive composables (useOrchestration) + data/ Sample/demo data generators + proto/ Generated protobuf TypeScript + services/ gRPC client (orchestrator) + stores/ Svelte 5 reactive stores (sessions, audit, memory, theme, ...) + types/ TypeScript types and helpers (lineage, resultSource) + utils/ Shared utilities (date formatting, session config) + routes/ + chat/ Chat page + lineage/ Agent lineage visualization + memory/ Memory candidates browser + audit/ Audit log timeline +``` diff --git a/src/lib/components/AuditTimeline.svelte b/src/lib/components/AuditTimeline.svelte index c782f7c..d07e91b 100644 --- a/src/lib/components/AuditTimeline.svelte +++ b/src/lib/components/AuditTimeline.svelte @@ -1,5 +1,6 @@ @@ -70,18 +51,19 @@
    {#each events as event, i (event.id)} - {@const showDate = i === 0 || formatDate(event.timestamp) !== formatDate(events[i - 1].timestamp)} + {@const showDate = i === 0 || formatShortDate(event.timestamp) !== formatShortDate(events[i - 1].timestamp)} + {@const config = getEventConfig(event.eventType)} {#if showDate}
  1. - {formatDate(event.timestamp)} + {formatShortDate(event.timestamp)}
  2. {/if}
  3. @@ -91,9 +73,9 @@
    {formatTimestamp(event.timestamp)} - {typeLabel(event.eventType)} + {config.label} {#if event.state} diff --git a/src/lib/components/Backdrop.svelte b/src/lib/components/Backdrop.svelte new file mode 100644 index 0000000..76a928b --- /dev/null +++ b/src/lib/components/Backdrop.svelte @@ -0,0 +1,12 @@ + + +
    { if (e.key === 'Escape') onClose(); }} + aria-label="Close overlay" +>
    diff --git a/src/lib/components/ConfigSidebar.svelte b/src/lib/components/ConfigSidebar.svelte index 1bfd3cb..16220d1 100644 --- a/src/lib/components/ConfigSidebar.svelte +++ b/src/lib/components/ConfigSidebar.svelte @@ -4,6 +4,8 @@ import { SessionConfigSchema } from '$lib/proto/llm_multiverse/v1/orchestrator_pb'; import { create } from '@bufbuild/protobuf'; import { presetStore } from '$lib/stores/presets.svelte'; + import { isNonDefaultConfig } from '$lib/utils/sessionConfig'; + import Backdrop from '$lib/components/Backdrop.svelte'; let { config, @@ -37,11 +39,7 @@ { value: OverrideLevel.ALL, label: 'All', description: 'No enforcement' } ]; - const isNonDefault = $derived( - config.overrideLevel !== OverrideLevel.NONE || - config.disabledTools.length > 0 || - config.grantedPermissions.length > 0 - ); + const isNonDefault = $derived(isNonDefaultConfig(config)); function setOverrideLevel(level: OverrideLevel) { const updated = create(SessionConfigSchema, { @@ -120,16 +118,8 @@ } - {#if onClose} -
    { if (e.key === 'Escape') onClose?.(); }} - aria-label="Close config sidebar" - >
    + {/if}
    {#if showConfig} diff --git a/src/routes/lineage/+page.svelte b/src/routes/lineage/+page.svelte index 05665b1..f5f10ea 100644 --- a/src/routes/lineage/+page.svelte +++ b/src/routes/lineage/+page.svelte @@ -2,7 +2,8 @@ import { resolveRoute } from '$app/paths'; import { AgentType } from '$lib/proto/llm_multiverse/v1/common_pb'; import LineageTree from '$lib/components/LineageTree.svelte'; - import ThemeToggle from '$lib/components/ThemeToggle.svelte'; + import PageHeader from '$lib/components/PageHeader.svelte'; + import Backdrop from '$lib/components/Backdrop.svelte'; import type { LineageNode, SimpleAgentIdentifier } from '$lib/types/lineage'; import { buildLineageTree, @@ -10,6 +11,7 @@ agentTypeLabel, agentTypeColor } from '$lib/types/lineage'; + import { themeStore } from '$lib/stores/theme.svelte'; const chatHref = resolveRoute('/chat'); @@ -32,26 +34,7 @@
    - -
    -
    - - - ← Chat - - -

    Agent Lineage

    -
    -
    - - -
    -
    +
    @@ -63,12 +46,13 @@ Agent Types: {#each agentTypeLegend as type (type)} {@const colors = agentTypeColor(type)} + {@const colorSet = themeStore.isDark ? colors.dark : colors.light} {agentTypeLabel(type)} @@ -79,15 +63,8 @@ {#if selectedNode} {@const colors = agentTypeColor(selectedNode.agentType)} - -
    (selectedNode = null)} - onkeydown={(e) => { if (e.key === 'Escape') selectedNode = null; }} - aria-label="Close detail panel" - >
    + {@const colorSet = themeStore.isDark ? colors.dark : colors.light} + (selectedNode = null)} />