mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-16 09:13:36 +00:00
7.4 KiB
7.4 KiB
LLM Module Structure (Refactor Runtime)
This document describes the current backend structure under server/src/modules/llm, how execution/session state works, and how the provider abstraction is designed.
High-Level Layout
server/src/modules/llm/
llm.routes.ts
llm.registry.ts
providers/
provider.interface.ts
abstract.provider.ts
base-sdk.provider.ts
base-cli.provider.ts
claude.provider.ts
codex.provider.ts
cursor.provider.ts
gemini.provider.ts
services/
llm.service.ts
sessions.service.ts
sessions-watcher.service.ts
messages-unifier.service.ts
assets.service.ts
mcp.service.ts
skills.service.ts
session-indexers/
session-indexer.interface.ts
session-indexer.utils.ts
claude.session-indexer.ts
codex.session-indexer.ts
cursor.session-indexer.ts
gemini.session-indexer.ts
index.ts
tests/
llm-unifier.providers.test.ts
llm-unifier.sessions.test.ts
llm-unifier.images.test.ts
llm-unifier.mcp.test.ts
llm-unifier.skills.test.ts
llm-unifier.messages.test.ts
Responsibilities By File Group
llm.routes.ts- HTTP API for provider runtime sessions (start/resume/stop/model/thinking), normalized session/history messages, assets upload, MCP config/probe, skills listing, indexed session CRUD/sync.
llm.registry.ts- Singleton provider registry. Instantiates one provider class per provider id.
providers/*- Runtime execution and live event collection.
- SDK family (
BaseSdkProvider) for Claude/Codex. - CLI family (
BaseCliProvider) for Cursor/Gemini.
services/llm.service.ts- Input validation + capability gating + facade over provider registry.
services/sessions.service.ts- DB-backed indexed sessions and history file parsing.
- Returns normalized message history via
messages-unifier.service.ts.
services/sessions-watcher.service.tschokidarwatchers for provider artifact folders.- On filesystem update, triggers
synchronizeProviderFile(provider, filePath).
services/messages-unifier.service.ts- Provider-specific raw event/history -> unified message contract for frontend.
services/assets.service.ts- Stores uploaded images in
.cloudcli/assets.
- Stores uploaded images in
services/mcp.service.ts- Unified MCP CRUD/probe across provider-native config formats/scopes/transports.
services/skills.service.ts- Provider-specific skill directory discovery and metadata extraction.
session-indexers/*- Scans provider artifacts from disk and upserts indexed sessions into
sessionsDB table.
- Scans provider artifacts from disk and upserts indexed sessions into
Runtime Flow (Provider Sessions)
POST /api/llm/providers/:provider/sessions/starthitsllm.routes.ts.- Route calls
llmService.startSession(...). llm.service.tsvalidates payload and capability constraints.llm.registry.tsresolves provider instance.- Provider (
BaseSdkProviderorBaseCliProvider) creates an in-memory session record and starts execution. - Stream/process output is appended as in-memory
ProviderSessionEvent[]. - Route can either:
- return
202immediately with snapshot, or - await completion via
waitForSession.
- return
- Snapshots are enriched with unified
messagesviallmMessagesUnifier.normalizeSessionEvents(...).
Indexed History Flow (Disk/DB)
- Watcher or manual sync scans provider folders.
- Provider-specific indexer extracts minimal metadata and upserts
sessionsDb. - History endpoints (
/sessions/:sessionId/history,/sessions/:sessionId/messages) read transcript path from DB. - JSON/JSONL is parsed and transformed via
llmMessagesUnifier.normalizeHistoryEntries(...).
Interface + Abstract + Base-Class Design
IProvider (interface)
providers/provider.interface.ts
- Consumer contract used by registry/service layer.
- Exposes:
launchSession,resumeSession,stopSession,waitForSessionsetSessionModel,setSessionThinkingModegetSession,listSessionslistModels
- Exposes
capabilitiesso callers can gate unsupported features before calling provider-specific logic.
AbstractProvider (abstract class)
providers/abstract.provider.ts
- Shared lifecycle state and rules:
sessions: Map<string, MutableProviderSession>sessionPreferences: Map<string, { model?, thinkingMode? }>
- Implements:
- in-memory session reads (
getSession,listSessions,waitForSession) - stop handling + session status events
- model/thinking updates with capability checks
- event ring-buffer logic (
MAX_EVENT_BUFFER_SIZE)
- in-memory session reads (
- Leaves provider execution specifics abstract (
listModels,launchSession,resumeSession).
BaseSdkProvider and BaseCliProvider
BaseSdkProvider- shared async iterable stream consumption.
- handles completion/error transitions and completion system event emission.
BaseCliProvider- shared child-process spawn + stdout/stderr line accumulation + JSON line parsing.
- graceful stop (
SIGTERMthenSIGKILL) and completion/error transitions.
Concrete provider classes
ClaudeProvider(SDK)- uses
@anthropic-ai/claude-agent-sdk. - supports runtime permission requests and emits permission events.
- image payload support via base64 content blocks.
- uses
CodexProvider(SDK)- dynamic import of
@openai/codex-sdk. - supports text +
local_imageprompt items.
- dynamic import of
CursorProvider(CLI)cursor-agentinvocation builder + model list parsing.
GeminiProvider(CLI)geminiinvocation builder + curated model catalog.
In-Memory Session Setup: How It Works
The in-memory part is inside AbstractProvider + base classes:
- Session record is created at launch/resume in memory (
Map). - Events are appended in real-time while stream/process runs.
- Snapshot endpoints read this map directly (
/providers/:provider/sessions...). - Stop/wait/model/thinking controls operate on this same in-memory handle.
- Completed sessions currently remain in map (bounded event history per session, but no map eviction).
Key characteristics:
- Process-local only (not shared across instances).
- Lost on server restart.
- Good for immediate live control and progress.
- Not the source of truth for historical transcripts (disk/DB is).
Is In-Memory Session State Necessary, Or Useless?
Short answer: not useless, but not sufficient as a durable architecture.
Why it is necessary in the current design
- You need live handles for:
stopSession(abort process/stream now),waitForSession,- real-time event buffering for immediate API responses.
- These are runtime concerns and cannot be satisfied by session-index DB rows alone.
Where it is weak
- No eviction/pruning for completed session map entries.
- No persistence across restart.
- No cross-instance coordination (if horizontally scaled, only the owning instance can control that session).
Practical conclusion
- Keep in-memory runtime state for active execution control.
- Treat DB/indexed history as the durable read model.
- If you need reliability across restarts/instances, move execution ownership to a durable worker/orchestrator and store live session metadata in a shared store.
Suggested Hardening (Incremental)
- Add session map eviction policy (TTL/LRU for completed/failed/stopped sessions).
- Add ownership metadata (
instanceId) if multiple backend instances will run. - Add explicit
activeSessionsmetric endpoint. - Optionally persist minimal runtime state (status transitions + timestamps) to DB for auditability.