基于 shiy-temp分支修改
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card, Collapse, Tag, Spin, Typography, Button } from 'antd';
|
||||
import { ToolOutlined, ThunderboltOutlined, CheckCircleOutlined, LoadingOutlined } from '@ant-design/icons';
|
||||
import type { ThoughtItem } from '../../types/dify_chat';
|
||||
import Markdown from './markdown';
|
||||
import '../../styles/components/chat-with-llm/thought-process.css';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
const { Text, Paragraph } = Typography;
|
||||
|
||||
interface ThoughtProcessProps {
|
||||
thought: ThoughtItem;
|
||||
isFinished: boolean;
|
||||
allToolIcons?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 思考过程组件
|
||||
* 展示AI的思考过程和工具调用
|
||||
*/
|
||||
export default function ThoughtProcess({
|
||||
thought,
|
||||
isFinished,
|
||||
allToolIcons = {}
|
||||
}: ThoughtProcessProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const { tool_name, tool_input, tool_output, thought: thoughtText, observation } = thought;
|
||||
|
||||
/**
|
||||
* 获取工具图标
|
||||
*/
|
||||
const getToolIcon = (toolName?: string) => {
|
||||
if (!toolName) return <ToolOutlined />;
|
||||
|
||||
// 如果有自定义图标映射
|
||||
if (allToolIcons[toolName]) {
|
||||
return <span>{allToolIcons[toolName]}</span>;
|
||||
}
|
||||
|
||||
// 根据工具名称返回默认图标
|
||||
switch (toolName.toLowerCase()) {
|
||||
case 'search':
|
||||
case 'web_search':
|
||||
return '🔍';
|
||||
case 'calculator':
|
||||
case 'math':
|
||||
return '🧮';
|
||||
case 'code':
|
||||
case 'python':
|
||||
return '💻';
|
||||
case 'image':
|
||||
case 'vision':
|
||||
return '👁️';
|
||||
case 'file':
|
||||
case 'document':
|
||||
return '📄';
|
||||
default:
|
||||
return <ToolOutlined />;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取状态图标和颜色
|
||||
*/
|
||||
const getStatusInfo = () => {
|
||||
if (isFinished && observation) {
|
||||
return {
|
||||
icon: <CheckCircleOutlined />,
|
||||
color: 'success',
|
||||
text: '已完成'
|
||||
};
|
||||
} else if (!isFinished) {
|
||||
return {
|
||||
icon: <LoadingOutlined spin />,
|
||||
color: 'processing',
|
||||
text: '执行中'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
icon: <ThunderboltOutlined />,
|
||||
color: 'default',
|
||||
text: '等待中'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const statusInfo = getStatusInfo();
|
||||
|
||||
/**
|
||||
* 格式化工具输入
|
||||
*/
|
||||
const formatToolInput = (input?: string) => {
|
||||
if (!input) return '无输入参数';
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(input);
|
||||
return (
|
||||
<pre className="bg-gray-50 p-2 rounded text-sm overflow-x-auto">
|
||||
{JSON.stringify(parsed, null, 2)}
|
||||
</pre>
|
||||
);
|
||||
} catch {
|
||||
return <Text code>{input}</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化工具输出
|
||||
*/
|
||||
const formatToolOutput = (output?: string) => {
|
||||
if (!output) return '暂无输出';
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(output);
|
||||
return (
|
||||
<pre className="bg-gray-50 p-2 rounded text-sm overflow-x-auto">
|
||||
{JSON.stringify(parsed, null, 2)}
|
||||
</pre>
|
||||
);
|
||||
} catch {
|
||||
return <Markdown content={output} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
size="small"
|
||||
className="my-2 border-l-4 border-l-blue-400 bg-blue-50"
|
||||
bodyStyle={{ padding: '12px' }}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-lg">{getToolIcon(tool_name)}</span>
|
||||
<Text strong className="text-blue-700">
|
||||
{tool_name || '工具调用'}
|
||||
</Text>
|
||||
<Tag
|
||||
color={statusInfo.color as any}
|
||||
icon={statusInfo.icon}
|
||||
className="ml-2"
|
||||
>
|
||||
{statusInfo.text}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
{(tool_input || tool_output) && (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
className="text-blue-600"
|
||||
>
|
||||
{expanded ? '收起详情' : '查看详情'}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 思考内容 */}
|
||||
{thoughtText && (
|
||||
<div className="mb-2">
|
||||
<Markdown content={thoughtText} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 工具详情 */}
|
||||
{expanded && (tool_input || tool_output) && (
|
||||
<Collapse
|
||||
ghost
|
||||
size="small"
|
||||
className="bg-white rounded"
|
||||
>
|
||||
{tool_input && (
|
||||
<Panel
|
||||
header={
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
📥 输入参数
|
||||
</span>
|
||||
}
|
||||
key="input"
|
||||
>
|
||||
{formatToolInput(tool_input)}
|
||||
</Panel>
|
||||
)}
|
||||
|
||||
{tool_output && (
|
||||
<Panel
|
||||
header={
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
📤 执行结果
|
||||
</span>
|
||||
}
|
||||
key="output"
|
||||
>
|
||||
{formatToolOutput(tool_output)}
|
||||
</Panel>
|
||||
)}
|
||||
</Collapse>
|
||||
)}
|
||||
|
||||
{/* 观察结果 */}
|
||||
{observation && observation !== tool_output && (
|
||||
<div className="mt-2 p-2 bg-green-50 rounded border border-green-200">
|
||||
<Text className="text-green-700 text-sm font-medium">💡 观察结果:</Text>
|
||||
<div className="mt-1">
|
||||
<Markdown content={observation} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 加载状态 */}
|
||||
{!isFinished && !observation && (
|
||||
<div className="flex items-center justify-center py-2 text-blue-600">
|
||||
<Spin size="small" className="mr-2" />
|
||||
<Text className="text-sm">正在执行工具调用...</Text>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user