refactor: code review improvements — fix bugs, extract shared utilities, add README

- 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 <noreply@anthropic.com>
This commit is contained in:
shahondin1624
2026-03-12 13:48:06 +01:00
parent bb0eebff1b
commit 38f5f31b92
26 changed files with 753 additions and 579 deletions

View File

@@ -0,0 +1,151 @@
import type { ChatMessage } from '$lib/types';
import type { SubagentResult } 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';
import { sessionStore } from '$lib/stores/sessions.svelte';
import { memoryStore } from '$lib/stores/memory.svelte';
import { auditStore } from '$lib/stores/audit.svelte';
import { toastStore } from '$lib/stores/toast.svelte';
export function createOrchestration() {
let isStreaming = $state(false);
let error: string | null = $state(null);
let lastFailedContent: string | null = $state(null);
let orchestrationState: OrchestrationState = $state(OrchestrationState.UNSPECIFIED);
let intermediateResult: string = $state('');
let finalResult: SubagentResult | null = $state(null);
async function send(
sessionId: string,
content: string,
config: SessionConfig,
messages: ChatMessage[]
): Promise<ChatMessage[]> {
error = null;
lastFailedContent = null;
orchestrationState = OrchestrationState.UNSPECIFIED;
intermediateResult = '';
finalResult = null;
let lastAuditState = OrchestrationState.UNSPECIFIED;
const userMessage: ChatMessage = {
id: crypto.randomUUID(),
role: 'user',
content,
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- plain data, not reactive state
timestamp: new Date()
};
messages.push(userMessage);
const assistantMessage: ChatMessage = {
id: crypto.randomUUID(),
role: 'assistant',
content: '',
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- plain data, not reactive state
timestamp: new Date()
};
messages.push(assistantMessage);
sessionStore.updateMessages(sessionId, messages);
isStreaming = true;
try {
for await (const response of processRequest(sessionId, content, config)) {
orchestrationState = response.state;
if (response.state !== lastAuditState) {
const stateLabel = OrchestrationState[response.state] ?? String(response.state);
auditStore.addEvent(sessionId, {
eventType: 'state_change',
details: response.message || `State changed to ${stateLabel}`,
state: stateLabel
});
lastAuditState = response.state;
}
if (response.intermediateResult) {
intermediateResult = response.intermediateResult;
}
if (response.finalResult) {
finalResult = response.finalResult;
if (response.finalResult.newMemoryCandidates.length > 0) {
memoryStore.addCandidates(
sessionId,
response.finalResult.newMemoryCandidates.map((mc) => ({
content: mc.content,
source: mc.source,
confidence: mc.confidence
}))
);
}
}
const idx = messages.length - 1;
messages[idx] = {
...messages[idx],
content: response.message
};
}
} catch (err) {
const friendlyMsg =
err instanceof OrchestratorError
? friendlyMessage(err.code)
: 'An unexpected error occurred';
error = friendlyMsg;
lastFailedContent = content;
toastStore.addToast({ message: friendlyMsg, type: 'error' });
auditStore.addEvent(sessionId, {
eventType: 'error',
details: friendlyMsg
});
const idx = messages.length - 1;
messages[idx] = {
...messages[idx],
content: `\u26A0 ${friendlyMsg}`
};
} finally {
isStreaming = false;
sessionStore.updateMessages(sessionId, messages);
}
return messages;
}
function retry(
sessionId: string,
config: SessionConfig,
messages: ChatMessage[]
): { messages: ChatMessage[]; sending: boolean } {
if (!lastFailedContent) return { messages, sending: false };
const content = lastFailedContent;
// Remove the failed user+assistant pair before retrying
if (
messages.length >= 2 &&
messages[messages.length - 2].role === 'user' &&
messages[messages.length - 1].role === 'assistant' &&
messages[messages.length - 1].content.startsWith('\u26A0')
) {
messages = messages.slice(0, -2);
}
send(sessionId, content, config, messages);
return { messages, sending: true };
}
function reset() {
error = null;
finalResult = null;
}
return {
get isStreaming() { return isStreaming; },
get error() { return error; },
get lastFailedContent() { return lastFailedContent; },
get orchestrationState() { return orchestrationState; },
get intermediateResult() { return intermediateResult; },
get finalResult() { return finalResult; },
send,
retry,
reset
};
}