更新ios

This commit is contained in:
2026-01-18 09:40:17 +08:00
parent 4313a8cc1a
commit 9f2a7877d5
5 changed files with 178 additions and 61 deletions

View File

@@ -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]);

View File

@@ -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:

View File

@@ -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>
);

View File

@@ -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;
/** 是否正在思考中 */

View File

@@ -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;