更新ios
This commit is contained in:
@@ -263,10 +263,10 @@ const StockCodeLink = memo(({ code, exchange, navigation }) => {
|
||||
}
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
const stockCode = exchangeSuffix ? `${code}.${exchangeSuffix}` : code;
|
||||
const fullStockCode = exchangeSuffix ? `${code}.${exchangeSuffix}` : code;
|
||||
navigation?.navigate('StockDetail', {
|
||||
code: stockCode,
|
||||
name: stockCode, // 名称可以在详情页获取
|
||||
stockCode: fullStockCode,
|
||||
stockName: fullStockCode, // 名称可以在详情页获取
|
||||
});
|
||||
}, [code, exchangeSuffix, navigation]);
|
||||
|
||||
|
||||
@@ -2662,27 +2662,36 @@ A股交易时间: 上午 9:30-11:30,下午 13:00-15:00
|
||||
# 将 assistant 消息添加到历史(包含 tool_calls)
|
||||
messages.append(assistant_message)
|
||||
|
||||
# 构建当前轮次的步骤列表
|
||||
current_round_steps = []
|
||||
for tc in native_tool_calls:
|
||||
try:
|
||||
args = json.loads(tc.function.arguments) if tc.function.arguments else {}
|
||||
except:
|
||||
args = {}
|
||||
current_round_steps.append({
|
||||
"tool": tc.function.name,
|
||||
"arguments": args,
|
||||
"reason": f"调用 {tc.function.name}"
|
||||
})
|
||||
|
||||
# 如果是第一次工具调用,发送计划事件
|
||||
if step_index == 0:
|
||||
# 构建计划数据
|
||||
plan_data = {
|
||||
"goal": f"分析用户问题:{user_query[:50]}...",
|
||||
"reasoning": "使用工具获取相关数据进行分析",
|
||||
"steps": []
|
||||
"steps": current_round_steps
|
||||
}
|
||||
for tc in native_tool_calls:
|
||||
try:
|
||||
args = json.loads(tc.function.arguments) if tc.function.arguments else {}
|
||||
except:
|
||||
args = {}
|
||||
plan_data["steps"].append({
|
||||
"tool": tc.function.name,
|
||||
"arguments": args,
|
||||
"reason": f"调用 {tc.function.name}"
|
||||
})
|
||||
|
||||
yield self._format_sse("plan", plan_data)
|
||||
yield self._format_sse("status", {"stage": "executing", "message": f"开始执行 {len(native_tool_calls)} 个工具调用"})
|
||||
else:
|
||||
# 多轮调用时,发送 plan_update 事件追加新步骤
|
||||
yield self._format_sse("plan_update", {
|
||||
"new_steps": current_round_steps,
|
||||
"round": step_index // len(native_tool_calls) + 1 if native_tool_calls else 1,
|
||||
"message": f"模型决定继续调用 {len(native_tool_calls)} 个工具"
|
||||
})
|
||||
yield self._format_sse("status", {"stage": "executing", "message": f"继续执行第 {step_index + 1} 步"})
|
||||
|
||||
# 执行每个工具调用
|
||||
for tool_call in native_tool_calls:
|
||||
@@ -2795,18 +2804,29 @@ A股交易时间: 上午 9:30-11:30,下午 13:00-15:00
|
||||
# 将 assistant 消息添加到历史
|
||||
messages.append({"role": "assistant", "content": assistant_message.content})
|
||||
|
||||
# 构建当前轮次的步骤列表
|
||||
current_round_steps = [
|
||||
{"tool": tc["name"], "arguments": tc["arguments"], "reason": f"调用 {tc['name']}"}
|
||||
for tc in text_tool_calls
|
||||
]
|
||||
|
||||
# 如果是第一次工具调用,发送计划事件
|
||||
if step_index == 0:
|
||||
plan_data = {
|
||||
"goal": f"分析用户问题:{user_query[:50]}...",
|
||||
"reasoning": "使用工具获取相关数据进行分析",
|
||||
"steps": [
|
||||
{"tool": tc["name"], "arguments": tc["arguments"], "reason": f"调用 {tc['name']}"}
|
||||
for tc in text_tool_calls
|
||||
]
|
||||
"steps": current_round_steps
|
||||
}
|
||||
yield self._format_sse("plan", plan_data)
|
||||
yield self._format_sse("status", {"stage": "executing", "message": f"开始执行 {len(text_tool_calls)} 个工具调用"})
|
||||
else:
|
||||
# 多轮调用时,发送 plan_update 事件追加新步骤
|
||||
yield self._format_sse("plan_update", {
|
||||
"new_steps": current_round_steps,
|
||||
"round": step_index // len(text_tool_calls) + 1 if text_tool_calls else 1,
|
||||
"message": f"模型决定继续调用 {len(text_tool_calls)} 个工具"
|
||||
})
|
||||
yield self._format_sse("status", {"stage": "executing", "message": f"继续执行第 {step_index + 1} 步"})
|
||||
|
||||
# 执行每个工具调用
|
||||
for tc in text_tool_calls:
|
||||
|
||||
@@ -389,6 +389,7 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
);
|
||||
|
||||
case MessageTypes.AGENT_PLAN:
|
||||
const planStepsToShow = message.plan?.steps || [];
|
||||
return (
|
||||
<Flex justify="flex-start" w="100%">
|
||||
<HStack align="start" spacing={3} maxW={{ base: '95%', md: '85%', lg: '80%' }} w="100%">
|
||||
@@ -420,13 +421,13 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
<Text fontSize="sm" fontWeight="medium" color="purple.300">
|
||||
执行计划
|
||||
</Text>
|
||||
{message.plan?.steps && (
|
||||
{planStepsToShow.length > 0 && (
|
||||
<Badge
|
||||
bgGradient="linear(to-r, purple.500, pink.500)"
|
||||
color="white"
|
||||
fontSize="xs"
|
||||
>
|
||||
{message.plan.steps.length} 个步骤
|
||||
{planStepsToShow.length} 个步骤
|
||||
</Badge>
|
||||
)}
|
||||
</HStack>
|
||||
@@ -437,38 +438,41 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{message.plan?.steps && (
|
||||
{planStepsToShow.length > 0 && (
|
||||
<VStack align="stretch" spacing={2}>
|
||||
{message.plan.steps.map((step, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: idx * 0.1 }}
|
||||
>
|
||||
<HStack
|
||||
p={2}
|
||||
bg="rgba(255, 255, 255, 0.03)"
|
||||
borderRadius="md"
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 255, 255, 0.1)"
|
||||
spacing={2}
|
||||
<AnimatePresence>
|
||||
{planStepsToShow.map((step, idx) => (
|
||||
<motion.div
|
||||
key={`plan-step-${idx}-${step.tool}`}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: idx * 0.1 }}
|
||||
layout
|
||||
>
|
||||
<Badge
|
||||
bg="rgba(139, 92, 246, 0.2)"
|
||||
color="purple.300"
|
||||
fontSize="xs"
|
||||
minW="24px"
|
||||
textAlign="center"
|
||||
<HStack
|
||||
p={2}
|
||||
bg="rgba(255, 255, 255, 0.03)"
|
||||
borderRadius="md"
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 255, 255, 0.1)"
|
||||
spacing={2}
|
||||
>
|
||||
{idx + 1}
|
||||
</Badge>
|
||||
<Text fontSize="xs" color="gray.400" flex={1}>
|
||||
{step.tool}
|
||||
</Text>
|
||||
</HStack>
|
||||
</motion.div>
|
||||
))}
|
||||
<Badge
|
||||
bg="rgba(139, 92, 246, 0.2)"
|
||||
color="purple.300"
|
||||
fontSize="xs"
|
||||
minW="24px"
|
||||
textAlign="center"
|
||||
>
|
||||
{idx + 1}
|
||||
</Badge>
|
||||
<Text fontSize="xs" color="gray.400" flex={1}>
|
||||
{step.tool}
|
||||
</Text>
|
||||
</HStack>
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</VStack>
|
||||
)}
|
||||
</CardBody>
|
||||
@@ -479,10 +483,42 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
);
|
||||
|
||||
case MessageTypes.AGENT_EXECUTING:
|
||||
const steps = message.plan?.steps || [];
|
||||
// 从 plan 中获取步骤列表,如果没有 plan,则从 stepResults 中构建
|
||||
const planSteps = message.plan?.steps || [];
|
||||
const completedSteps = message.stepResults || [];
|
||||
const currentStepIndex = completedSteps.length;
|
||||
const progress = steps.length > 0 ? (completedSteps.length / steps.length) * 100 : 0;
|
||||
|
||||
// 如果有当前步骤但不在 planSteps 中,需要动态添加
|
||||
const currentStepInfo = message.currentStep;
|
||||
const activeStepIndex = message.currentStepIndex;
|
||||
|
||||
// 构建完整的步骤列表:已完成的步骤 + 当前步骤(如果有)
|
||||
// 这样即使在多轮调用时 plan 还没更新,也能显示当前正在执行的步骤
|
||||
let displaySteps = [...planSteps];
|
||||
|
||||
// 如果 planSteps 为空但有 completedSteps,从 completedSteps 构建
|
||||
if (displaySteps.length === 0 && completedSteps.length > 0) {
|
||||
displaySteps = completedSteps.map(s => ({ tool: s.tool, arguments: {} }));
|
||||
}
|
||||
|
||||
// 如果当前有正在执行的步骤,且不在显示列表中,添加它
|
||||
if (currentStepInfo && activeStepIndex !== null && activeStepIndex !== undefined) {
|
||||
while (displaySteps.length <= activeStepIndex) {
|
||||
if (displaySteps.length === activeStepIndex) {
|
||||
displaySteps.push({ tool: currentStepInfo.tool, arguments: currentStepInfo.arguments });
|
||||
} else {
|
||||
// 填充中间可能缺失的步骤(从已完成结果中获取)
|
||||
const stepResult = completedSteps[displaySteps.length];
|
||||
if (stepResult) {
|
||||
displaySteps.push({ tool: stepResult.tool, arguments: {} });
|
||||
} else {
|
||||
displaySteps.push({ tool: '未知步骤', arguments: {} });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const totalSteps = Math.max(displaySteps.length, completedSteps.length + (activeStepIndex !== null ? 1 : 0));
|
||||
const progress = totalSteps > 0 ? (completedSteps.length / totalSteps) * 100 : 0;
|
||||
|
||||
return (
|
||||
<Flex justify="flex-start" w="100%">
|
||||
@@ -520,8 +556,13 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
正在执行
|
||||
</Text>
|
||||
<Badge colorScheme="blue" fontSize="xs">
|
||||
{completedSteps.length} / {steps.length}
|
||||
{completedSteps.length}{totalSteps > 0 ? ` / ${totalSteps}` : ''}
|
||||
</Badge>
|
||||
{activeStepIndex !== null && activeStepIndex !== undefined && currentStepInfo && (
|
||||
<Text fontSize="xs" color="blue.200" ml={2}>
|
||||
→ {currentStepInfo.tool}
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{/* 进度条 */}
|
||||
@@ -539,15 +580,17 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
{/* 步骤列表 */}
|
||||
<VStack align="stretch" spacing={2}>
|
||||
<AnimatePresence>
|
||||
{steps.map((step, idx) => {
|
||||
{displaySteps.map((step, idx) => {
|
||||
const result = completedSteps[idx];
|
||||
const isCompleted = idx < completedSteps.length;
|
||||
const isRunning = idx === currentStepIndex;
|
||||
const isPending = idx > currentStepIndex;
|
||||
const isRunning = activeStepIndex !== null && activeStepIndex !== undefined
|
||||
? idx === activeStepIndex
|
||||
: idx === completedSteps.length;
|
||||
const isPending = !isCompleted && !isRunning;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={idx}
|
||||
key={`step-${idx}-${step.tool}`}
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: idx * 0.05 }}
|
||||
@@ -641,6 +684,15 @@ const MessageRenderer = ({ message, userAvatar }) => {
|
||||
{result.execution_time.toFixed(2)}s
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* 执行失败时显示错误提示 */}
|
||||
{result?.status === 'failed' && result?.error && (
|
||||
<Tooltip label={result.error}>
|
||||
<Text fontSize="xs" color="red.400" cursor="help">
|
||||
失败
|
||||
</Text>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
@@ -59,6 +59,16 @@ export interface UserMessage extends BaseMessage {
|
||||
files?: MessageFile[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前执行步骤信息
|
||||
*/
|
||||
export interface CurrentStep {
|
||||
/** 工具名称 */
|
||||
tool: string;
|
||||
/** 工具参数 */
|
||||
arguments: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Agent 消息接口
|
||||
*/
|
||||
@@ -79,6 +89,10 @@ export interface AgentMessage extends BaseMessage {
|
||||
execution_time?: number;
|
||||
error?: string;
|
||||
}>;
|
||||
/** 当前正在执行的步骤索引(用于 AGENT_EXECUTING 类型)*/
|
||||
currentStepIndex?: number | null;
|
||||
/** 当前正在执行的步骤信息(用于 AGENT_EXECUTING 类型)*/
|
||||
currentStep?: CurrentStep | null;
|
||||
/** 深度思考内容(用于 AGENT_DEEP_THINKING 类型)*/
|
||||
thinkingContent?: string;
|
||||
/** 是否正在思考中 */
|
||||
|
||||
@@ -126,6 +126,7 @@ export const useAgentChat = ({
|
||||
isDeepThinking: boolean; // 是否正在进行深度思考
|
||||
plan: any;
|
||||
stepResults: any[];
|
||||
currentStep: { tool: string; arguments: any } | null; // 当前正在执行的步骤
|
||||
sessionId: string | null;
|
||||
sessionTitle: string | null;
|
||||
}>({
|
||||
@@ -135,6 +136,7 @@ export const useAgentChat = ({
|
||||
isDeepThinking: false,
|
||||
plan: null,
|
||||
stepResults: [],
|
||||
currentStep: null,
|
||||
sessionId: null,
|
||||
sessionTitle: null,
|
||||
});
|
||||
@@ -196,6 +198,7 @@ export const useAgentChat = ({
|
||||
isDeepThinking: false,
|
||||
plan: null,
|
||||
stepResults: [],
|
||||
currentStep: null,
|
||||
sessionId: null,
|
||||
sessionTitle: null,
|
||||
};
|
||||
@@ -409,11 +412,35 @@ export const useAgentChat = ({
|
||||
});
|
||||
break;
|
||||
|
||||
case 'plan_update':
|
||||
// 多轮工具调用时,追加新的步骤到计划中
|
||||
if (data?.new_steps && streamStateRef.current.plan) {
|
||||
streamStateRef.current.plan.steps = [
|
||||
...(streamStateRef.current.plan.steps || []),
|
||||
...data.new_steps
|
||||
];
|
||||
// 更新 AGENT_PLAN 消息
|
||||
updateLastMessage(MessageTypes.AGENT_PLAN, {
|
||||
plan: { ...streamStateRef.current.plan },
|
||||
});
|
||||
// 更新 AGENT_EXECUTING 消息
|
||||
updateLastMessage(MessageTypes.AGENT_EXECUTING, {
|
||||
plan: { ...streamStateRef.current.plan },
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'step_start':
|
||||
// 步骤开始执行 - 保留已完成的步骤结果,更新当前执行状态
|
||||
// 步骤开始执行 - 记录当前步骤,更新执行状态
|
||||
streamStateRef.current.currentStep = {
|
||||
tool: data?.tool || '工具',
|
||||
arguments: data?.arguments || {},
|
||||
};
|
||||
updateLastMessage(MessageTypes.AGENT_EXECUTING, {
|
||||
content: `正在执行步骤 ${(data?.step_index || 0) + 1}: ${data?.tool || '工具'}...`,
|
||||
stepResults: [...streamStateRef.current.stepResults], // 保留已完成的步骤
|
||||
stepResults: [...streamStateRef.current.stepResults],
|
||||
currentStepIndex: data?.step_index,
|
||||
currentStep: streamStateRef.current.currentStep,
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -424,11 +451,15 @@ export const useAgentChat = ({
|
||||
tool: data.tool,
|
||||
status: data.status,
|
||||
result: data.result,
|
||||
error: data.error,
|
||||
execution_time: data.execution_time,
|
||||
});
|
||||
streamStateRef.current.currentStep = null;
|
||||
updateLastMessage(MessageTypes.AGENT_EXECUTING, {
|
||||
content: `已完成 ${streamStateRef.current.stepResults.length} 个步骤`,
|
||||
stepResults: [...streamStateRef.current.stepResults],
|
||||
currentStepIndex: null,
|
||||
currentStep: null,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user