diff --git a/implementation-plans/_index.md b/implementation-plans/_index.md
index 0279062..97bd704 100644
--- a/implementation-plans/_index.md
+++ b/implementation-plans/_index.md
@@ -8,3 +8,4 @@
| #4 | gRPC-Web client service layer | COMPLETED | [issue-004.md](issue-004.md) |
| #5 | Chat page layout and message list component | COMPLETED | [issue-005.md](issue-005.md) |
| #6 | Message input with send and keyboard shortcuts | COMPLETED | [issue-006.md](issue-006.md) |
+| #7 | Streaming response rendering | COMPLETED | [issue-007.md](issue-007.md) |
diff --git a/implementation-plans/issue-007.md b/implementation-plans/issue-007.md
new file mode 100644
index 0000000..01054f0
--- /dev/null
+++ b/implementation-plans/issue-007.md
@@ -0,0 +1,16 @@
+---
+---
+
+# Issue #7: Streaming response rendering
+
+**Status:** COMPLETED
+**Issue:** https://git.shahondin1624.de/llm-multiverse/llm-multiverse-ui/issues/7
+**Branch:** `feature/issue-7-streaming-response`
+
+## Acceptance Criteria
+
+- [x] Chat UI connected to gRPC-Web client service from #4
+- [x] Streaming responses rendered in real-time as chunks arrive
+- [x] `message` field displayed progressively
+- [x] Handles stream completion and errors gracefully
+- [x] Loading indicator shown while waiting for first chunk
diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte
index e5c1496..92bb49f 100644
--- a/src/routes/chat/+page.svelte
+++ b/src/routes/chat/+page.svelte
@@ -2,11 +2,16 @@
import type { ChatMessage } from '$lib/types';
import MessageList from '$lib/components/MessageList.svelte';
import MessageInput from '$lib/components/MessageInput.svelte';
+ import { processRequest, OrchestratorError } from '$lib/services/orchestrator';
let messages: ChatMessage[] = $state([]);
let isStreaming = $state(false);
+ let error: string | null = $state(null);
+ let sessionId = $state(crypto.randomUUID());
+
+ async function handleSend(content: string) {
+ error = null;
- function handleSend(content: string) {
const userMessage: ChatMessage = {
id: crypto.randomUUID(),
role: 'user',
@@ -14,6 +19,40 @@
timestamp: new Date()
};
messages.push(userMessage);
+
+ const assistantMessage: ChatMessage = {
+ id: crypto.randomUUID(),
+ role: 'assistant',
+ content: '',
+ timestamp: new Date()
+ };
+ messages.push(assistantMessage);
+
+ isStreaming = true;
+
+ try {
+ for await (const response of processRequest(sessionId, content)) {
+ const idx = messages.length - 1;
+ messages[idx] = {
+ ...messages[idx],
+ content: response.message
+ };
+ }
+ } catch (err) {
+ const msg =
+ err instanceof OrchestratorError
+ ? `Error (${err.code}): ${err.message}`
+ : 'An unexpected error occurred';
+ error = msg;
+ // Update the assistant message with the error
+ const idx = messages.length - 1;
+ messages[idx] = {
+ ...messages[idx],
+ content: `⚠ ${msg}`
+ };
+ } finally {
+ isStreaming = false;
+ }
}
@@ -23,5 +62,25 @@