feat(aia): Complete AIA V2.0 with universal streaming capabilities

Major Updates:
- Add StreamingService with OpenAI Compatible format (backend/common/streaming)
- Upgrade Chat component V2 with Ant Design X integration
- Implement AIA module with 12 intelligent agents
- Create AgentHub with 100% prototype V11 restoration
- Create ChatWorkspace with streaming response support
- Add ThinkingBlock for deep thinking display
- Add useAIStream Hook for OpenAI Compatible stream handling

Backend Common Capabilities (~400 lines):
- OpenAIStreamAdapter: SSE adapter with OpenAI format
- StreamingService: unified streaming service
- Support content and reasoning_content dual streams
- Deep thinking tag processing (<think>...</think>)

Frontend Common Capabilities (~2000 lines):
- AIStreamChat: modern streaming chat component
- ThinkingBlock: collapsible deep thinking display
- ConversationList: conversation management with grouping
- useAIStream: OpenAI Compatible stream handler Hook
- useConversations: conversation state management Hook
- Modern design styles (Ultramodern theme)

AIA Module Frontend (~1500 lines):
- AgentHub: 12 agent cards with timeline design
- ChatWorkspace: fullscreen immersive chat interface
- AgentCard: theme-colored cards (blue/yellow/teal/purple)
- 5 phases, 12 agents configuration
- Responsive layout (desktop + mobile)

AIA Module Backend (~900 lines):
- agentService: 12 agents config with system prompts
- conversationService: refactored with StreamingService
- attachmentService: file upload skeleton (30k token limit)
- 12 API endpoints with authentication
- Full CRUD for conversations and messages

Documentation:
- AIA module status and development guide
- Universal capabilities catalog (11 services)
- Quick reference card for developers
- System overview updates

Testing:
- Stream response verified (HTTP 200)
- Authentication working correctly
- Auto conversation creation working
- Deep thinking display working
- Message input and send working

Status: Core features completed (85%), attachment and history loading pending
This commit is contained in:
2026-01-14 19:09:28 +08:00
parent 4ed67a8846
commit 3d35e9c58b
38 changed files with 8448 additions and 335 deletions

View File

@@ -0,0 +1,143 @@
/**
* AgentHub - 智能体大厅主界面
*
* 100%还原原型图V11
* - 最大宽度 760px居中
* - 头部搜索框
* - 时间轴设计(左侧序号圆点+连接线)
* - 5个阶段12个智能体卡片
*/
import React, { useState, useMemo } from 'react';
import { BrainCircuit, Search } from 'lucide-react';
import { AgentCard } from './AgentCard';
import { AGENTS, PHASES } from '../constants';
import type { AgentConfig } from '../types';
import '../styles/agent-hub.css';
interface AgentHubProps {
onAgentSelect: (agent: AgentConfig) => void;
}
export const AgentHub: React.FC<AgentHubProps> = ({ onAgentSelect }) => {
const [searchValue, setSearchValue] = useState('');
// 按阶段分组智能体
const agentsByPhase = useMemo(() => {
const grouped: Record<number, AgentConfig[]> = {};
AGENTS.forEach(agent => {
if (!grouped[agent.phase]) {
grouped[agent.phase] = [];
}
grouped[agent.phase].push(agent);
});
return grouped;
}, []);
// 搜索提交
const handleSearch = () => {
if (searchValue.trim()) {
// 默认进入第一个智能体并携带搜索内容
const firstAgent = AGENTS[0];
onAgentSelect({ ...firstAgent, initialQuery: searchValue } as any);
}
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
handleSearch();
}
};
return (
<div className="agent-hub">
{/* 主体内容 */}
<main className="hub-main">
{/* 头部搜索区 */}
<div className="hub-header">
<div className="header-title">
<div className="title-icon">
<BrainCircuit size={24} />
</div>
<h1 className="title-text">
<span className="title-badge">DeepSeek</span>
</h1>
</div>
<div className="search-box">
<input
type="text"
placeholder="输入研究想法例如SGLT2抑制剂对心衰患者预后的影响..."
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
onKeyDown={handleKeyDown}
className="search-input"
/>
<button className="search-btn" onClick={handleSearch}>
<Search size={20} />
</button>
</div>
</div>
{/* 流水线模块 */}
<div className="pipeline-container">
{PHASES.map((phase, phaseIndex) => {
const isLast = phaseIndex === PHASES.length - 1;
const agents = agentsByPhase[phase.phase] || [];
// 根据阶段确定列数
let gridCols = 'grid-cols-3';
if (phase.phase === 2) gridCols = 'grid-cols-3'; // 4个卡片每行3个
if (phase.phase === 3) gridCols = 'grid-cols-3'; // 1个卡片
if (phase.phase === 4) gridCols = 'grid-cols-3'; // 2个卡片
if (phase.phase === 5) gridCols = 'grid-cols-3'; // 2个卡片
return (
<div
key={phase.phase}
className={`pipeline-stage ${isLast ? 'last-stage' : ''}`}
>
{/* 左侧时间轴 */}
<div className="timeline-marker">
<div className={`timeline-dot theme-${phase.theme}`}>
{phase.phase}
</div>
{!isLast && <div className="timeline-line" />}
</div>
{/* 阶段内容 */}
<div className="stage-content">
<h2 className="stage-title">
{phase.name}
{phase.isTool && (
<span className="tool-badge"></span>
)}
</h2>
<div className={`agents-grid ${gridCols}`}>
{agents.map(agent => (
<AgentCard
key={agent.id}
agent={agent}
onClick={onAgentSelect}
/>
))}
</div>
</div>
</div>
);
})}
</div>
</main>
{/* 底部 */}
<footer className="hub-footer">
<p>&copy; 2025 - </p>
</footer>
</div>
);
};
export default AgentHub;