export interface Message { role: 'user' | 'assistant' | 'system'; content: string; timestamp?: number; } export interface ChatSession { id: string; title: string; timestamp: number; messages: Message[]; } const API_URL = 'https://api.siliconflow.cn/v1/chat/completions'; const API_KEY_STORAGE_KEY = 'siliconflow_api_key'; const HISTORY_STORAGE_KEY = 'manus_chat_history'; const DEMO_SESSIONS: ChatSession[] = [ { id: 'demo-1', title: 'React 组件优化指南', timestamp: Date.now() - 1000 * 60 * 60 * 2, messages: [ { role: 'user', content: '如何优化 React 组件的渲染性能?', timestamp: Date.now() - 1000 * 60 * 60 * 2 }, { role: 'assistant', content: '### React 组件性能优化策略\n\n#### 1. 减少不必要的渲染\n- **使用 `React.memo`**:对于纯展示组件,使用 `memo` 包裹,避免父组件更新导致的不必要重渲染。\n- **`useCallback` 和 `useMemo`**:缓存函数和计算结果,避免作为 props 传递时导致子组件更新。\n\n#### 2. 状态管理优化\n- **状态下放 (State Colocation)**:将状态尽可能靠近使用它的组件,避免在顶层组件管理过多状态。\n- **Context 拆分**:避免将大对象放入 Context,导致所有消费者组件在部分属性变化时重渲染。\n\n#### 3. 列表渲染\n- **使用 `key`**:确保列表项有唯一且稳定的 `key`。\n- **虚拟滚动**:对于长列表,使用 `react-window` 或 `react-virtualized` 进行虚拟渲染。\n\n#### 4. 代码分割\n- **`React.lazy` + `Suspense`**:对路由组件或大型组件进行懒加载,减小首屏 bundle 体积。', timestamp: Date.now() - 1000 * 60 * 60 * 2 + 1000 } ] }, { id: 'demo-2', title: 'Python 异步编程入门', timestamp: Date.now() - 1000 * 60 * 60 * 24, messages: [ { role: 'user', content: 'Python 中的 async/await 是什么?', timestamp: Date.now() - 1000 * 60 * 60 * 24 }, { role: 'assistant', content: '### Python 异步编程 (async/await) 简介\n\n`async` 和 `await` 是 Python 3.5 引入的用于编写并发代码的语法,基于 `asyncio` 库。\n\n#### 核心概念\n1. **协程 (Coroutine)**:使用 `async def` 定义的函数。调用它不会立即执行,而是返回一个协程对象。\n2. **事件循环 (Event Loop)**:负责调度和运行协程的机制。\n3. **Awaitable**:可以在 `await` 表达式中使用的对象(如协程、Task、Future)。\n\n#### 简单示例\n```python\nimport asyncio\n\nasync def say_hello():\n print("Hello")\n await asyncio.sleep(1) # 模拟IO操作\n print("World")\n\nasync def main():\n await asyncio.gather(say_hello(), say_hello())\n\nif __name__ == "__main__":\n asyncio.run(main())\n```\n\n#### 适用场景\n- **IO 密集型任务**:如网络请求、数据库读写。异步可以在等待 IO 时执行其他任务,极大提高吞吐量。\n- **Web 服务器**:如 FastAPI,天生支持异步,性能优越。', timestamp: Date.now() - 1000 * 60 * 60 * 24 + 1000 } ] }, { id: 'demo-3', title: 'Docker 容器化部署', timestamp: Date.now() - 1000 * 60 * 60 * 48, messages: [ { role: 'user', content: '编写一个简单的 Dockerfile', timestamp: Date.now() - 1000 * 60 * 60 * 48 }, { role: 'assistant', content: '### Node.js 应用的 Dockerfile 示例\n\n这是一个标准的多阶段构建 Dockerfile,用于优化镜像体积:\n\n```dockerfile\n# Build Stage\nFROM node:18-alpine AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\n# Production Stage\nFROM node:18-alpine\nWORKDIR /app\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/package*.json ./\nRUN npm ci --only=production\n\nEXPOSE 3000\nCMD ["node", "dist/index.js"]\n```\n\n#### 关键点解析\n- **Alpine 镜像**:使用 `alpine` 版本减小基础镜像体积。\n- **多阶段构建**:将构建环境(包含 devDependencies)和运行环境(仅生产依赖)分离。\n- **npm ci**:在 CI/CD 环境中使用,确保依赖版本与 lock 文件完全一致。', timestamp: Date.now() - 1000 * 60 * 60 * 48 + 1000 } ] } ]; export const chatService = { getApiKey(): string | null { return localStorage.getItem(API_KEY_STORAGE_KEY); }, setApiKey(key: string) { localStorage.setItem(API_KEY_STORAGE_KEY, key); }, getHistory(): ChatSession[] { const stored = localStorage.getItem(HISTORY_STORAGE_KEY); if (!stored) { this.saveHistory(DEMO_SESSIONS); return DEMO_SESSIONS; } let sessions: ChatSession[] = JSON.parse(stored); if (sessions.length === 0) { this.saveHistory(DEMO_SESSIONS); return DEMO_SESSIONS; } let hasChanges = false; // Data migration: Ensure all messages have timestamps sessions = sessions.map(session => { let sessionChanged = false; const newMessages = session.messages.map((msg, index) => { if (!msg.timestamp) { sessionChanged = true; hasChanges = true; // Fallback: Use session timestamp + small offset to maintain order const baseTime = session.timestamp || Date.now(); return { ...msg, timestamp: baseTime + (index * 1000) }; } return msg; }); if (sessionChanged) { return { ...session, messages: newMessages }; } return session; }); // Persist changes if any migration happened if (hasChanges) { this.saveHistory(sessions); } return sessions; }, saveHistory(sessions: ChatSession[]) { localStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(sessions)); }, deleteSession(sessionId: string) { const sessions = this.getHistory(); const newSessions = sessions.filter(s => s.id !== sessionId); this.saveHistory(newSessions); return newSessions; }, updateSession(updatedSession: ChatSession) { const sessions = this.getHistory(); const index = sessions.findIndex(s => s.id === updatedSession.id); if (index !== -1) { sessions[index] = updatedSession; this.saveHistory(sessions); } return sessions; }, createSession(initialMessage: string): ChatSession { const generateUUID = () => { if (typeof crypto !== 'undefined' && crypto.randomUUID) { return crypto.randomUUID(); } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }; const session: ChatSession = { id: generateUUID(), title: initialMessage.slice(0, 20) + (initialMessage.length > 20 ? '...' : ''), timestamp: Date.now(), messages: [{ role: 'user', content: initialMessage, timestamp: Date.now() }] }; return session; }, async sendMessage(messages: Message[], onChunk: (content: string) => void, signal?: AbortSignal): Promise { let apiKey = this.getApiKey(); if (!apiKey) { const input = prompt('Please enter your SiliconFlow API Key:'); if (input) { this.setApiKey(input); apiKey = input; } } try { const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey || ''}` }, body: JSON.stringify({ model: 'deepseek-ai/DeepSeek-V3', messages: messages, stream: true }), signal // Pass the abort signal to fetch }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'API request failed'); } const reader = response.body?.getReader(); const decoder = new TextDecoder('utf-8'); if (!reader) throw new Error('Response body is unavailable'); // Handle abort signal for the reader loop if (signal) { signal.addEventListener('abort', () => { reader.cancel(); }); } while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value, { stream: true }); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ') && line !== 'data: [DONE]') { try { const data = JSON.parse(line.slice(6)); const content = data.choices[0]?.delta?.content || ''; if (content) { onChunk(content); } } catch (e) { console.error('Error parsing chunk', e); } } } } } catch (error) { console.error('Chat error:', error); throw error; } } };