## ADDED Requirements ### Requirement: Streaming chat response 服务层 SHALL 使用 `fetch` + `ReadableStream` 实现流式对话,通过 `AsyncGenerator` 供上层消费。 #### Scenario: Successful streaming - **WHEN** 调用 `speak(sessionId, audioBlob)` 发起请求 - **THEN** 依次 yield `transcript`、`token`(多次)、`done` 事件 #### Scenario: Empty response - **WHEN** 后端返回空的 stream(0 tokens) - **THEN** generator 正常结束,不抛异常 ### Requirement: Request cancellation 每个流式请求 SHALL 支持通过 `AbortController` 取消。 #### Scenario: User cancels during streaming - **WHEN** 流式输出进行中调用 `signal.abort()` - **THEN** fetch 请求被取消,generator 抛出 `AbortError`,上层保留已接收内容 ### Requirement: Error handling 请求失败时 SHALL 统一抛出错误,不区分错误类型(v1 简化版)。 #### Scenario: Request fails - **WHEN** 网络断开、超时、或后端返回错误 - **THEN** 抛出 Error,上层将消息状态设为 `error`,显示重试按钮 ### Requirement: Adapter interface 服务层 SHALL 通过 `DialogueAPI` 接口抽象后端通信,支持 mock 和真实 API 切换。 ```typescript interface DialogueAPI { createSession(config: SessionConfig): Promise speak(sessionId: string, audioBlob: Blob, signal: AbortSignal): AsyncGenerator getReport(sessionId: string): Promise } ``` #### Scenario: Using mock adapter - **WHEN** `mode` 为 `preview` 时 - **THEN** 使用 `MockDialogueAPI`,模拟 transcript + 逐 token 输出 #### Scenario: Using real adapter - **WHEN** `mode` 为 `real` 时 - **THEN** 使用 `RealDialogueAPI`,对接后端 3 个接口 ### Requirement: SSE response parsing 服务层 SHALL 解析后端返回的 SSE 格式流。 #### Scenario: Parse SSE events - **WHEN** 后端返回 `event: transcript\ndata: {...}\n\n` 格式 - **THEN** 根据 event type 解析为对应的 SSEEvent 对象(transcript / token / done)