Files
vf_react/src/views/AgentChat/neuratalk/app/chat-direct/page.tsx
2025-11-22 09:57:30 +08:00

211 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// app/chat-direct/page.tsx - 直接访问版本(跳过认证)
'use client';
import React, { useState, useRef, useEffect } from 'react';
import { mcpService } from '../../services/mcp-real';
export default function DirectChatPage() {
const [messages, setMessages] = useState<any[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [tools, setTools] = useState<any[]>([]);
const messagesEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
loadTools();
}, []);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const loadTools = async () => {
try {
const availableTools = await mcpService.getTools();
setTools(availableTools);
console.log('Available tools:', availableTools);
} catch (error) {
console.error('Failed to load tools:', error);
}
};
const handleSend = async () => {
if (!input.trim() || isLoading) return;
const userMessage = {
id: Date.now().toString(),
role: 'user',
content: input,
timestamp: new Date(),
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
try {
const assistantMessage = {
id: (Date.now() + 1).toString(),
role: 'assistant',
content: '',
timestamp: new Date(),
isStreaming: true,
};
setMessages(prev => [...prev, assistantMessage]);
// 尝试流式响应
let fullContent = '';
try {
for await (const chunk of mcpService.streamMessage(userMessage.content)) {
fullContent += chunk;
setMessages(prev =>
prev.map(msg =>
msg.id === assistantMessage.id
? { ...msg, content: fullContent }
: msg
)
);
}
} catch (streamError) {
console.log('Stream failed, trying non-stream:', streamError);
// 如果流式失败,尝试普通请求
const response = await mcpService.sendMessage(userMessage.content);
fullContent = response;
}
// 更新最终消息
setMessages(prev =>
prev.map(msg =>
msg.id === assistantMessage.id
? { ...msg, content: fullContent || '没有收到回复', isStreaming: false }
: msg
)
);
} catch (error) {
console.error('Send message error:', error);
setMessages(prev => [
...prev,
{
id: Date.now().toString(),
role: 'system',
content: `错误: ${error.message}`,
timestamp: new Date(),
},
]);
} finally {
setIsLoading(false);
}
};
return (
<div className="flex h-screen bg-gray-50">
{/* 左侧工具栏 */}
<div className="w-64 bg-white border-r p-4">
<h3 className="font-bold mb-4">MCP </h3>
<div className="space-y-2">
{tools.map(tool => (
<div
key={tool.name}
className="p-2 bg-gray-100 rounded hover:bg-gray-200 cursor-pointer"
onClick={() => {
setInput(`使用工具: ${tool.name}`);
}}
>
<div className="font-medium text-sm">{tool.name}</div>
<div className="text-xs text-gray-600">{tool.description}</div>
</div>
))}
</div>
<div className="mt-6 p-3 bg-blue-50 rounded">
<div className="text-sm font-medium">Max </div>
<div className="text-xs text-gray-600">访</div>
</div>
</div>
{/* 主聊天区域 */}
<div className="flex-1 flex flex-col">
<div className="bg-white border-b p-4">
<h1 className="text-xl font-bold">AI Chat (Direct Access)</h1>
<p className="text-sm text-gray-600">MCP - </p>
</div>
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.length === 0 && (
<div className="text-center text-gray-500 mt-10">
<p></p>
<div className="mt-4 space-y-2">
<button
onClick={() => setInput('你好,介绍一下自己')}
className="text-blue-600 hover:underline block mx-auto"
>
"你好,介绍一下自己"
</button>
<button
onClick={() => setInput('列出可用的工具')}
className="text-blue-600 hover:underline block mx-auto"
>
"列出可用的工具"
</button>
<button
onClick={() => setInput('帮我写一段 Python 代码')}
className="text-blue-600 hover:underline block mx-auto"
>
"帮我写一段 Python 代码"
</button>
</div>
</div>
)}
{messages.map(msg => (
<div
key={msg.id}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-2xl px-4 py-2 rounded-lg ${
msg.role === 'user'
? 'bg-blue-600 text-white'
: msg.role === 'system'
? 'bg-red-100 text-red-800'
: 'bg-gray-100 text-gray-900'
}`}
>
<div className="whitespace-pre-wrap">{msg.content}</div>
<div className="text-xs mt-1 opacity-70">
{msg.timestamp.toLocaleTimeString()}
</div>
</div>
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="border-t p-4 bg-white">
<div className="flex space-x-2">
<input
type="text"
value={input}
onChange={e => setInput(e.target.value)}
onKeyPress={e => e.key === 'Enter' && !e.shiftKey && handleSend()}
disabled={isLoading}
placeholder="输入消息... (回车发送)"
className="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
onClick={handleSend}
disabled={!input.trim() || isLoading}
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? '发送中...' : '发送'}
</button>
</div>
<div className="mt-2 text-xs text-gray-500">
使
</div>
</div>
</div>
</div>
);
}