update pay function
This commit is contained in:
@@ -2377,7 +2377,7 @@ MEETING_ROLES = {
|
||||
"name": "巴菲特",
|
||||
"nickname": "唱多者",
|
||||
"role_type": "bull",
|
||||
"avatar": "/avatars/buffett.png",
|
||||
"avatar": "/images/agent/巴菲特.png",
|
||||
"model": "kimi-k2-thinking",
|
||||
"color": "#10B981",
|
||||
"description": "主观多头,善于分析事件的潜在利好和长期价值",
|
||||
@@ -2403,7 +2403,7 @@ MEETING_ROLES = {
|
||||
"name": "大空头",
|
||||
"nickname": "大空头",
|
||||
"role_type": "bear",
|
||||
"avatar": "/avatars/big_short.png",
|
||||
"avatar": "/images/agent/大空头.png",
|
||||
"model": "kimi-k2-thinking",
|
||||
"color": "#EF4444",
|
||||
"description": "善于分析事件和财报中的风险因素",
|
||||
@@ -2429,7 +2429,7 @@ MEETING_ROLES = {
|
||||
"name": "量化分析员",
|
||||
"nickname": "西蒙斯",
|
||||
"role_type": "quant",
|
||||
"avatar": "/avatars/simons.png",
|
||||
"avatar": "/images/agent/simons.png",
|
||||
"model": "deepseek",
|
||||
"color": "#3B82F6",
|
||||
"description": "中性立场,使用量化工具分析技术指标",
|
||||
@@ -2454,7 +2454,7 @@ MEETING_ROLES = {
|
||||
"name": "韭菜",
|
||||
"nickname": "牢大",
|
||||
"role_type": "retail",
|
||||
"avatar": "/avatars/leek.png",
|
||||
"avatar": "/images/agent/牢大.png",
|
||||
"model": "deepmoney",
|
||||
"color": "#F59E0B",
|
||||
"description": "贪婪又讨厌亏损,热爱追涨杀跌",
|
||||
@@ -2474,7 +2474,7 @@ MEETING_ROLES = {
|
||||
"name": "基金经理",
|
||||
"nickname": "决策者",
|
||||
"role_type": "manager",
|
||||
"avatar": "/avatars/fund_manager.png",
|
||||
"avatar": "/images/agent/基金经理.png",
|
||||
"model": "deepseek",
|
||||
"color": "#8B5CF6",
|
||||
"description": "综合分析做出最终决策",
|
||||
@@ -2650,7 +2650,7 @@ async def stream_role_response(
|
||||
messages=messages,
|
||||
stream=True,
|
||||
temperature=0.7,
|
||||
max_tokens=500,
|
||||
max_tokens=2000, # 增加 token 限制以避免输出被截断
|
||||
)
|
||||
|
||||
full_content = ""
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 134 KiB |
@@ -84,6 +84,7 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
<Flex justify="flex-start">
|
||||
<HStack align="start" spacing={3} maxW="75%">
|
||||
<Avatar
|
||||
src="/images/agent/基金经理.png"
|
||||
icon={<Cpu className="w-4 h-4" />}
|
||||
size="sm"
|
||||
bgGradient="linear(to-br, purple.500, pink.500)"
|
||||
@@ -122,6 +123,7 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
<Flex justify="flex-start">
|
||||
<HStack align="start" spacing={3} maxW="75%">
|
||||
<Avatar
|
||||
src="/images/agent/基金经理.png"
|
||||
icon={<Cpu className="w-4 h-4" />}
|
||||
size="sm"
|
||||
bgGradient="linear(to-br, purple.500, pink.500)"
|
||||
|
||||
@@ -28,14 +28,63 @@ import {
|
||||
Copy,
|
||||
ThumbsUp,
|
||||
ChevronRight,
|
||||
ChevronDown,
|
||||
Database,
|
||||
Check,
|
||||
Wrench,
|
||||
AlertCircle,
|
||||
Brain,
|
||||
} from 'lucide-react';
|
||||
import { getRoleConfig, MEETING_ROLES } from '../../constants/meetingRoles';
|
||||
import { MarkdownWithCharts } from '@components/ChatBot/MarkdownWithCharts';
|
||||
|
||||
/**
|
||||
* 解析 deepmoney 格式的内容
|
||||
* 格式: <think>思考过程</think><answer>回答内容</answer>
|
||||
*
|
||||
* @param {string} content - 原始内容
|
||||
* @returns {{ thinking: string | null, answer: string }} 解析后的内容
|
||||
*/
|
||||
const parseDeepmoneyContent = (content) => {
|
||||
if (!content) return { thinking: null, answer: '' };
|
||||
|
||||
// 匹配 <think>...</think> 标签
|
||||
const thinkMatch = content.match(/<think>([\s\S]*?)<\/think>/i);
|
||||
// 匹配 <answer>...</answer> 标签
|
||||
const answerMatch = content.match(/<answer>([\s\S]*?)<\/answer>/i);
|
||||
|
||||
// 如果有 answer 标签,提取内容
|
||||
if (answerMatch) {
|
||||
return {
|
||||
thinking: thinkMatch ? thinkMatch[1].trim() : null,
|
||||
answer: answerMatch[1].trim(),
|
||||
};
|
||||
}
|
||||
|
||||
// 如果只有 think 标签但没有 answer 标签,可能正在流式输出中
|
||||
if (thinkMatch && !answerMatch) {
|
||||
// 检查 think 后面是否有其他内容
|
||||
const afterThink = content.replace(/<think>[\s\S]*?<\/think>/i, '').trim();
|
||||
// 如果 think 后面有内容但不是 answer 标签包裹的,可能是部分输出
|
||||
if (afterThink && !afterThink.startsWith('<answer>')) {
|
||||
return {
|
||||
thinking: thinkMatch[1].trim(),
|
||||
answer: afterThink.replace(/<\/?answer>/gi, '').trim(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
thinking: thinkMatch[1].trim(),
|
||||
answer: '',
|
||||
};
|
||||
}
|
||||
|
||||
// 如果没有特殊标签,返回原内容
|
||||
return {
|
||||
thinking: null,
|
||||
answer: content,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取角色图标
|
||||
*/
|
||||
@@ -281,6 +330,67 @@ const ToolCallsList = ({ toolCalls, roleColor }) => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 思考过程展示组件
|
||||
* 用于显示 deepmoney 等模型的思考过程,默认折叠
|
||||
*/
|
||||
const ThinkingBlock = ({ thinking, roleColor }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
if (!thinking) return null;
|
||||
|
||||
return (
|
||||
<Box mb={3}>
|
||||
<HStack
|
||||
spacing={2}
|
||||
cursor="pointer"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
p={2}
|
||||
bg="rgba(255, 255, 255, 0.03)"
|
||||
borderRadius="md"
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 255, 255, 0.1)"
|
||||
_hover={{ borderColor: `${roleColor}30` }}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
<Brain className="w-3 h-3" style={{ color: roleColor }} />
|
||||
<Text fontSize="xs" color="gray.400" flex={1}>
|
||||
AI 思考过程
|
||||
</Text>
|
||||
<Box
|
||||
color="gray.500"
|
||||
transition="transform 0.2s"
|
||||
transform={isExpanded ? 'rotate(180deg)' : 'rotate(0deg)'}
|
||||
>
|
||||
<ChevronDown className="w-3 h-3" />
|
||||
</Box>
|
||||
</HStack>
|
||||
|
||||
<Collapse in={isExpanded} animateOpacity>
|
||||
<Box
|
||||
mt={2}
|
||||
p={3}
|
||||
bg="rgba(0, 0, 0, 0.2)"
|
||||
borderRadius="md"
|
||||
borderLeft="2px solid"
|
||||
borderColor={`${roleColor}50`}
|
||||
maxH="200px"
|
||||
overflowY="auto"
|
||||
sx={{
|
||||
'&::-webkit-scrollbar': { width: '4px' },
|
||||
'&::-webkit-scrollbar-track': { bg: 'transparent' },
|
||||
'&::-webkit-scrollbar-thumb': { bg: 'gray.600', borderRadius: 'full' },
|
||||
}}
|
||||
>
|
||||
<Text fontSize="xs" color="gray.400" whiteSpace="pre-wrap" lineHeight="tall">
|
||||
{thinking}
|
||||
</Text>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* MeetingMessageBubble - 会议消息气泡组件
|
||||
*
|
||||
@@ -326,6 +436,7 @@ const MeetingMessageBubble = ({ message, isLatest }) => {
|
||||
>
|
||||
<Avatar
|
||||
size="sm"
|
||||
src={roleConfig.avatar}
|
||||
icon={getRoleIcon(roleConfig.roleType)}
|
||||
bg={roleConfig.color}
|
||||
boxShadow={`0 0 12px ${roleConfig.color}40`}
|
||||
@@ -444,51 +555,65 @@ const MeetingMessageBubble = ({ message, isLatest }) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 消息内容 */}
|
||||
<Box
|
||||
fontSize="sm"
|
||||
color="gray.100"
|
||||
lineHeight="tall"
|
||||
sx={{
|
||||
'& p': { mb: 2 },
|
||||
'& h1, & h2, & h3': { color: 'gray.50', fontWeight: 'bold' },
|
||||
'& ul, & ol': { pl: 4 },
|
||||
'& li': { mb: 1 },
|
||||
'& code': {
|
||||
bg: 'rgba(255,255,255,0.1)',
|
||||
px: 1,
|
||||
borderRadius: 'sm',
|
||||
},
|
||||
'& blockquote': {
|
||||
borderLeftWidth: '3px',
|
||||
borderLeftColor: roleConfig.color,
|
||||
pl: 3,
|
||||
color: 'gray.300',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
'& strong': { color: roleConfig.color },
|
||||
}}
|
||||
>
|
||||
{message.content ? (
|
||||
<MarkdownWithCharts content={message.content} variant="dark" />
|
||||
) : isStreaming ? (
|
||||
<HStack spacing={2} color="gray.500">
|
||||
<Spinner size="sm" />
|
||||
<Text>正在思考...</Text>
|
||||
</HStack>
|
||||
) : null}
|
||||
{/* 解析 deepmoney 格式的内容 */}
|
||||
{(() => {
|
||||
const parsedContent = parseDeepmoneyContent(message.content);
|
||||
return (
|
||||
<>
|
||||
{/* 思考过程(可折叠) */}
|
||||
<ThinkingBlock
|
||||
thinking={parsedContent.thinking}
|
||||
roleColor={roleConfig.color}
|
||||
/>
|
||||
|
||||
{/* 流式输出时的光标 */}
|
||||
{isStreaming && message.content && (
|
||||
<motion.span
|
||||
animate={{ opacity: [1, 0, 1] }}
|
||||
transition={{ duration: 0.8, repeat: Infinity }}
|
||||
style={{ color: roleConfig.color }}
|
||||
>
|
||||
▌
|
||||
</motion.span>
|
||||
)}
|
||||
</Box>
|
||||
{/* 消息内容 */}
|
||||
<Box
|
||||
fontSize="sm"
|
||||
color="gray.100"
|
||||
lineHeight="tall"
|
||||
sx={{
|
||||
'& p': { mb: 2 },
|
||||
'& h1, & h2, & h3': { color: 'gray.50', fontWeight: 'bold' },
|
||||
'& ul, & ol': { pl: 4 },
|
||||
'& li': { mb: 1 },
|
||||
'& code': {
|
||||
bg: 'rgba(255,255,255,0.1)',
|
||||
px: 1,
|
||||
borderRadius: 'sm',
|
||||
},
|
||||
'& blockquote': {
|
||||
borderLeftWidth: '3px',
|
||||
borderLeftColor: roleConfig.color,
|
||||
pl: 3,
|
||||
color: 'gray.300',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
'& strong': { color: roleConfig.color },
|
||||
}}
|
||||
>
|
||||
{parsedContent.answer ? (
|
||||
<MarkdownWithCharts content={parsedContent.answer} variant="dark" />
|
||||
) : isStreaming ? (
|
||||
<HStack spacing={2} color="gray.500">
|
||||
<Spinner size="sm" />
|
||||
<Text>正在思考...</Text>
|
||||
</HStack>
|
||||
) : null}
|
||||
|
||||
{/* 流式输出时的光标 */}
|
||||
{isStreaming && parsedContent.answer && (
|
||||
<motion.span
|
||||
animate={{ opacity: [1, 0, 1] }}
|
||||
transition={{ duration: 0.8, repeat: Infinity }}
|
||||
style={{ color: roleConfig.color }}
|
||||
>
|
||||
▌
|
||||
</motion.span>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<Flex mt={3} pt={3} borderTop="1px solid" borderColor="whiteAlpha.100">
|
||||
|
||||
@@ -122,7 +122,7 @@ export const MEETING_ROLES: Record<string, MeetingRoleConfig> = {
|
||||
name: '巴菲特',
|
||||
nickname: '唱多者',
|
||||
roleType: 'bull',
|
||||
avatar: '/avatars/buffett.png',
|
||||
avatar: '/images/agent/巴菲特.png',
|
||||
color: '#10B981',
|
||||
gradient: 'linear(to-br, green.400, emerald.600)',
|
||||
description: '主观多头,善于分析事件的潜在利好和长期价值',
|
||||
@@ -133,7 +133,7 @@ export const MEETING_ROLES: Record<string, MeetingRoleConfig> = {
|
||||
name: '大空头',
|
||||
nickname: '大空头',
|
||||
roleType: 'bear',
|
||||
avatar: '/avatars/big_short.png',
|
||||
avatar: '/images/agent/大空头.png',
|
||||
color: '#EF4444',
|
||||
gradient: 'linear(to-br, red.400, rose.600)',
|
||||
description: '善于分析事件和财报中的风险因素,帮助投资者避雷',
|
||||
@@ -144,7 +144,7 @@ export const MEETING_ROLES: Record<string, MeetingRoleConfig> = {
|
||||
name: '量化分析员',
|
||||
nickname: '西蒙斯',
|
||||
roleType: 'quant',
|
||||
avatar: '/avatars/simons.png',
|
||||
avatar: '/images/agent/simons.png',
|
||||
color: '#3B82F6',
|
||||
gradient: 'linear(to-br, blue.400, cyan.600)',
|
||||
description: '中性立场,使用量化分析工具分析技术指标',
|
||||
@@ -155,7 +155,7 @@ export const MEETING_ROLES: Record<string, MeetingRoleConfig> = {
|
||||
name: '韭菜',
|
||||
nickname: '牢大',
|
||||
roleType: 'retail',
|
||||
avatar: '/avatars/leek.png',
|
||||
avatar: '/images/agent/牢大.png',
|
||||
color: '#F59E0B',
|
||||
gradient: 'linear(to-br, amber.400, yellow.600)',
|
||||
description: '贪婪又讨厌亏损,热爱追涨杀跌的典型散户',
|
||||
@@ -166,7 +166,7 @@ export const MEETING_ROLES: Record<string, MeetingRoleConfig> = {
|
||||
name: '基金经理',
|
||||
nickname: '决策者',
|
||||
roleType: 'manager',
|
||||
avatar: '/avatars/fund_manager.png',
|
||||
avatar: '/images/agent/基金经理.png',
|
||||
color: '#8B5CF6',
|
||||
gradient: 'linear(to-br, purple.400, violet.600)',
|
||||
description: '总结其他人的发言做出最终决策',
|
||||
|
||||
@@ -220,15 +220,9 @@ export const TOOL_CATEGORIES: Record<ToolCategory, MCPTool[]> = {
|
||||
|
||||
/**
|
||||
* 默认选中的工具 ID 列表
|
||||
* 这些工具在页面初始化时自动选中
|
||||
* 所有工具在页面初始化时自动选中
|
||||
*/
|
||||
export const DEFAULT_SELECTED_TOOLS: string[] = [
|
||||
'search_news',
|
||||
'search_china_news',
|
||||
'search_concepts',
|
||||
'search_limit_up_stocks',
|
||||
'search_research_reports',
|
||||
];
|
||||
export const DEFAULT_SELECTED_TOOLS: string[] = MCP_TOOLS.map((tool) => tool.id);
|
||||
|
||||
/**
|
||||
* 根据 ID 查找工具配置
|
||||
|
||||
@@ -357,11 +357,20 @@ export const useInvestmentMeeting = ({
|
||||
break;
|
||||
|
||||
case 'message_complete':
|
||||
if (data.role_id) {
|
||||
// 后端可能发送 message 对象或直接 content
|
||||
const finalContent = data.message?.content || data.content;
|
||||
finishStreamingMessage(data.role_id, finalContent);
|
||||
setSpeakingRoleId(null);
|
||||
{
|
||||
// 后端发送的是 message 对象,role_id 在 message 里
|
||||
const roleId = data.role_id || data.message?.role_id;
|
||||
if (roleId) {
|
||||
// 后端可能发送 message 对象或直接 content
|
||||
const finalContent = data.message?.content || data.content;
|
||||
finishStreamingMessage(roleId, finalContent);
|
||||
setSpeakingRoleId(null);
|
||||
|
||||
// 如果是结论消息,记录下来
|
||||
if (data.message?.is_conclusion) {
|
||||
setConclusion(data.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -562,11 +571,20 @@ export const useInvestmentMeeting = ({
|
||||
break;
|
||||
|
||||
case 'message_complete':
|
||||
if (data.role_id) {
|
||||
// 后端可能发送 message 对象或直接 content
|
||||
const finalContent = data.message?.content || data.content;
|
||||
finishStreamingMessage(data.role_id, finalContent);
|
||||
setSpeakingRoleId(null);
|
||||
{
|
||||
// 后端发送的是 message 对象,role_id 在 message 里
|
||||
const roleId = data.role_id || data.message?.role_id;
|
||||
if (roleId) {
|
||||
// 后端可能发送 message 对象或直接 content
|
||||
const finalContent = data.message?.content || data.content;
|
||||
finishStreamingMessage(roleId, finalContent);
|
||||
setSpeakingRoleId(null);
|
||||
|
||||
// 如果是结论消息,记录下来
|
||||
if (data.message?.is_conclusion) {
|
||||
setConclusion(data.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user