change to next

This commit is contained in:
xiongxiao
2026-01-15 12:32:07 +08:00
parent 62b5c5841a
commit b2f315a459
122 changed files with 9396 additions and 9354 deletions

127
web/src/app/chat/index.tsx Normal file
View File

@@ -0,0 +1,127 @@
import { app } from '@/agent/index.ts'
import { useStudioStore } from '../studio/store';
import { useShallow } from 'zustand/shallow';
import { useState } from 'react';
import { query } from '@/modules/query.ts'
import { QueryViewMessages } from '../query-view';
import { toast } from 'react-toastify';
export const Chat = () => {
const studioStore = useStudioStore(useShallow((state) => ({
routes: state.routes,
showRightPanel: state.showRightPanel,
setShowRightPanel: state.setShowRightPanel,
addMessage: state.addMessage,
})));
const [text, setText] = useState('');
const [isLoading, setIsLoading] = useState(false);
const onSend = async () => {
if (!text.trim() || isLoading) return;
setIsLoading(true);
const { routes } = studioStore;
let callPrompts = '';
const toolsList = routes.map((r, index) =>
`${index + 1}. 工具名称: ${r.id}\n 描述: ${r.description}`
).join('\n\n');
callPrompts = `你是一个 AI 助手,你可以使用以下工具来帮助用户完成任务:
${toolsList}
## 回复规则
1. 如果用户的请求可以使用上述工具完成,请返回 JSON 格式数据
2. 如果没有合适的工具,请直接分析并回答用户问题
## JSON 数据格式
\`\`\`json
{
"id": "工具的id",
"payload": {
// 工具所需的参数(如果需要)
// 例如: "id": "xxx", "name": "xxx"
}
}
\`\`\`
注意:
- payload 中包含工具执行所需的所有参数
- 如果工具不需要参数payload 可以为空对象 {}
- 确保返回的 id 与上述工具列表中的工具名称完全匹配`
const res = await query.post({
path: 'ai',
payload: {
messages: [
{
role: 'system',
content: callPrompts
},
{
role: 'user',
content: text
}
],
isJson: true
}
})
setText('');
console.log('发送消息', text, res);
if (res.code === 200) {
// 处理返回结果
const payload = res.data?.action;
if (payload) {
const route = routes.find(r => r.id === payload.id);
const { path, key } = route || {};
const { id, ...otherParams } = payload.payload || {};
const action = { path, key, ...otherParams }
let response;
if (route) {
response = await app.run(action);
// toast.success('工具调用成功');
} else {
console.error('未找到对应工具', payload.id);
toast.error('未找到对应工具');
return
}
if (route?.metadata?.viewItem) {
// 自动打开右侧面板
if (!studioStore.showRightPanel) {
studioStore.setShowRightPanel(true);
}
const viewItem = route.metadata.viewItem;
viewItem.response = response;
viewItem.action = action;
viewItem.description = route.description || viewItem.description;
// @ts-ignore
viewItem._id = Date.now();
studioStore.addMessage(viewItem);
}
}
}
setIsLoading(false);
}
return <div className="h-full flex flex-col border-l border-gray-300 bg-white">
<div style={{ height: '3rem' }} className="flex items-center justify-between px-4 border-b border-gray-300 bg-gray-50">
<div className="text-sm text-gray-600"></div>
</div>
<div style={{ height: 'calc(100% - 3rem)' }} className="overflow-auto">
<QueryViewMessages type="component" />
</div>
<div className="flex items-center gap-2 px-4 py-3 border-t border-gray-300 bg-white">
<input
type="text"
placeholder="输入消息..."
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && onSend()}
className="flex-1 px-3 py-2 border border-gray-300 rounded-md bg-white text-black placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-800 transition-all"
/>
<button
onClick={onSend}
disabled={isLoading}
className="px-4 py-2 bg-black hover:bg-gray-900 disabled:bg-gray-400 text-white font-medium rounded-md transition-colors duration-200 flex-shrink-0"
>
{isLoading ? '发送中...' : '发送'}
</button>
</div>
</div>
}