基于 shiy-temp分支修改

This commit is contained in:
pingchuan
2025-06-04 11:18:52 +08:00
parent 87ad3376fe
commit af33de09db
36 changed files with 6293 additions and 105 deletions
+220
View File
@@ -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>
);
}