update pay function
This commit is contained in:
277
src/views/AgentChat/neuratalk/services/mcp-real.ts
Normal file
277
src/views/AgentChat/neuratalk/services/mcp-real.ts
Normal file
@@ -0,0 +1,277 @@
|
||||
// services/mcp-real.ts - 对接真实的 MCP 服务器接口
|
||||
import { checkAuth } from '../lib/auth';
|
||||
|
||||
// ========== 类型定义 ==========
|
||||
export interface ChatMessage {
|
||||
role: 'user' | 'assistant' | 'system';
|
||||
content: string;
|
||||
timestamp?: string;
|
||||
}
|
||||
|
||||
export interface Tool {
|
||||
name: string;
|
||||
description: string;
|
||||
input_schema: {
|
||||
type: string;
|
||||
properties: Record<string, any>;
|
||||
required?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ToolCallRequest {
|
||||
tool: string;
|
||||
arguments: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ToolCallResponse {
|
||||
tool: string;
|
||||
result: any;
|
||||
error?: string;
|
||||
execution_time?: number;
|
||||
}
|
||||
|
||||
// ========== MCP 服务类 ==========
|
||||
export class MCPService {
|
||||
// 使用相对路径,通过 Nginx 代理访问
|
||||
private baseUrl = '/mcp';
|
||||
private chatHistory: ChatMessage[] = [];
|
||||
|
||||
/**
|
||||
* 获取可用工具列表
|
||||
*/
|
||||
async getTools(): Promise<Tool[]> {
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}/tools`, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to get tools: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.tools || [];
|
||||
} catch (error) {
|
||||
console.error('Failed to get tools:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用特定工具
|
||||
*/
|
||||
async callTool(toolName: string, args: Record<string, any>): Promise<ToolCallResponse> {
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}/tools/call`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
tool: toolName,
|
||||
arguments: args,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(`Tool call failed: ${error}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Tool call error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送聊天消息(非流式)
|
||||
*/
|
||||
async sendMessage(message: string): Promise<string> {
|
||||
// 添加用户消息到历史
|
||||
this.chatHistory.push({
|
||||
role: 'user',
|
||||
content: message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}/chat`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
messages: this.chatHistory,
|
||||
stream: false,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Chat failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 添加助手回复到历史
|
||||
if (data.response) {
|
||||
this.chatHistory.push({
|
||||
role: 'assistant',
|
||||
content: data.response,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
return data.response || data.message || '抱歉,没有收到有效的回复';
|
||||
} catch (error) {
|
||||
console.error('Send message error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送聊天消息(流式响应)
|
||||
*/
|
||||
async *streamMessage(message: string): AsyncGenerator<string, void, unknown> {
|
||||
// 添加用户消息到历史
|
||||
this.chatHistory.push({
|
||||
role: 'user',
|
||||
content: message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}/chat`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
messages: this.chatHistory,
|
||||
stream: true,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Stream failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// 处理 SSE 流
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
if (!reader) {
|
||||
throw new Error('No response body');
|
||||
}
|
||||
|
||||
let buffer = '';
|
||||
let fullResponse = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
|
||||
// 保留最后一行(可能不完整)
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.slice(6).trim();
|
||||
|
||||
if (data === '[DONE]') {
|
||||
// 流结束,保存完整回复到历史
|
||||
if (fullResponse) {
|
||||
this.chatHistory.push({
|
||||
role: 'assistant',
|
||||
content: fullResponse,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
const content = parsed.content || parsed.delta?.content || '';
|
||||
fullResponse += content;
|
||||
yield content;
|
||||
} catch (e) {
|
||||
// 如果不是 JSON,直接输出
|
||||
fullResponse += data;
|
||||
yield data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Stream message error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空聊天历史
|
||||
*/
|
||||
clearHistory() {
|
||||
this.chatHistory = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天历史
|
||||
*/
|
||||
getHistory(): ChatMessage[] {
|
||||
return this.chatHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行代码(通过 code_interpreter 工具)
|
||||
*/
|
||||
async executeCode(code: string, language: string = 'python'): Promise<any> {
|
||||
return this.callTool('code_interpreter', {
|
||||
code,
|
||||
language,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件操作工具封装
|
||||
*/
|
||||
async readFile(path: string): Promise<string> {
|
||||
const result = await this.callTool('read_file', { path });
|
||||
return result.result?.content || '';
|
||||
}
|
||||
|
||||
async writeFile(path: string, content: string): Promise<void> {
|
||||
await this.callTool('write_file', { path, content });
|
||||
}
|
||||
|
||||
async listDirectory(path: string = '.'): Promise<string[]> {
|
||||
const result = await this.callTool('list_directory', { path });
|
||||
return result.result?.files || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索工具封装
|
||||
*/
|
||||
async search(query: string): Promise<any> {
|
||||
return this.callTool('search', { query });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件工具
|
||||
*/
|
||||
async createFile(path: string, content: string): Promise<void> {
|
||||
await this.callTool('create_file', { path, content });
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
export const mcpService = new MCPService();
|
||||
138
src/views/AgentChat/neuratalk/services/mcp.ts
Normal file
138
src/views/AgentChat/neuratalk/services/mcp.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
// services/mcp.ts - MCP API 调用服务
|
||||
import { callMCPApi } from '../lib/auth';
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'user' | 'assistant' | 'system';
|
||||
content: string;
|
||||
timestamp?: string;
|
||||
tools?: any[];
|
||||
}
|
||||
|
||||
export interface ChatSession {
|
||||
id: string;
|
||||
messages: ChatMessage[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export class MCPService {
|
||||
/**
|
||||
* 发送聊天消息到 MCP
|
||||
*/
|
||||
async sendMessage(message: string, sessionId?: string): Promise<any> {
|
||||
const response = await callMCPApi('/api/mcp/chat', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
message,
|
||||
sessionId: sessionId || this.generateSessionId(),
|
||||
model: 'claude-3-opus', // 或其他模型
|
||||
}),
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 流式发送消息(SSE)
|
||||
*/
|
||||
async *streamMessage(message: string, sessionId?: string) {
|
||||
const response = await callMCPApi('/api/mcp/chat/stream', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
message,
|
||||
sessionId: sessionId || this.generateSessionId(),
|
||||
stream: true,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Stream request failed');
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
if (!reader) {
|
||||
throw new Error('No response body');
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
const lines = chunk.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.slice(6);
|
||||
if (data === '[DONE]') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
yield JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse SSE data:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的 MCP 工具列表
|
||||
*/
|
||||
async getAvailableTools(): Promise<any[]> {
|
||||
const response = await callMCPApi('/api/mcp/tools');
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行代码
|
||||
*/
|
||||
async executeCode(code: string, language: string = 'python'): Promise<any> {
|
||||
const response = await callMCPApi('/api/mcp/execute', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
code,
|
||||
language,
|
||||
}),
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代码
|
||||
*/
|
||||
async generateCode(prompt: string, language: string = 'python'): Promise<any> {
|
||||
const response = await callMCPApi('/api/mcp/generate-code', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
prompt,
|
||||
language,
|
||||
}),
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天历史
|
||||
*/
|
||||
async getChatHistory(sessionId?: string): Promise<ChatMessage[]> {
|
||||
const params = sessionId ? `?sessionId=${sessionId}` : '';
|
||||
const response = await callMCPApi(`/api/mcp/history${params}`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成会话 ID
|
||||
*/
|
||||
private generateSessionId(): string {
|
||||
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
export const mcpService = new MCPService();
|
||||
Reference in New Issue
Block a user