730 lines
25 KiB
TypeScript
730 lines
25 KiB
TypeScript
import { CHAT_CONFIG, ContentType, SSE_TIMEOUT } from '../config/chat';
|
|
import type { Feedbacktype, ThoughtItem, VisionFile, MessageEnd, MessageReplace } from '../types/dify_chat';
|
|
import { unicodeToChar } from '../utils/chat-utils';
|
|
|
|
// 基础请求选项
|
|
const baseOptions = {
|
|
method: 'GET',
|
|
mode: 'cors' as RequestMode,
|
|
credentials: 'omit' as RequestCredentials,
|
|
headers: new Headers({
|
|
'Content-Type': ContentType.json,
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
}),
|
|
redirect: 'follow' as RequestRedirect,
|
|
};
|
|
|
|
// 回调接口定义
|
|
export type IOnDataMoreInfo = {
|
|
conversationId?: string;
|
|
taskId?: string;
|
|
messageId: string;
|
|
errorMessage?: string;
|
|
errorCode?: string;
|
|
}
|
|
|
|
export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void;
|
|
export type IOnThought = (thought: ThoughtItem) => void;
|
|
export type IOnFile = (file: VisionFile) => void;
|
|
export type IOnMessageEnd = (messageEnd: MessageEnd) => void;
|
|
export type IOnMessageReplace = (messageReplace: MessageReplace) => void;
|
|
export type IOnCompleted = (hasError?: boolean) => void;
|
|
export type IOnError = (msg: string, code?: string) => void;
|
|
|
|
// 工作流相关类型
|
|
export type WorkflowStartedResponse = {
|
|
task_id: string;
|
|
workflow_run_id: string;
|
|
event: string;
|
|
data: {
|
|
id: string;
|
|
workflow_id: string;
|
|
sequence_number: number;
|
|
created_at: number;
|
|
};
|
|
}
|
|
|
|
export type WorkflowFinishedResponse = {
|
|
task_id: string;
|
|
workflow_run_id: string;
|
|
event: string;
|
|
data: {
|
|
id: string;
|
|
workflow_id: string;
|
|
status: string;
|
|
outputs: any;
|
|
error: string;
|
|
elapsed_time: number;
|
|
total_tokens: number;
|
|
total_steps: number;
|
|
created_at: number;
|
|
finished_at: number;
|
|
};
|
|
}
|
|
|
|
export type NodeStartedResponse = {
|
|
task_id: string;
|
|
workflow_run_id: string;
|
|
event: string;
|
|
data: {
|
|
id: string;
|
|
node_id: string;
|
|
node_type: string;
|
|
index: number;
|
|
predecessor_node_id?: string;
|
|
inputs: any;
|
|
created_at: number;
|
|
extras?: any;
|
|
};
|
|
}
|
|
|
|
export type NodeFinishedResponse = {
|
|
task_id: string;
|
|
workflow_run_id: string;
|
|
event: string;
|
|
data: {
|
|
id: string;
|
|
node_id: string;
|
|
node_type: string;
|
|
index: number;
|
|
predecessor_node_id?: string;
|
|
inputs: any;
|
|
process_data: any;
|
|
outputs: any;
|
|
status: string;
|
|
error: string;
|
|
elapsed_time: number;
|
|
execution_metadata: {
|
|
total_tokens: number;
|
|
total_price: number;
|
|
currency: string;
|
|
};
|
|
created_at: number;
|
|
};
|
|
}
|
|
|
|
export type IOnWorkflowStarted = (workflowStarted: WorkflowStartedResponse) => void;
|
|
export type IOnWorkflowFinished = (workflowFinished: WorkflowFinishedResponse) => void;
|
|
export type IOnNodeStarted = (nodeStarted: NodeStartedResponse) => void;
|
|
export type IOnNodeFinished = (nodeFinished: NodeFinishedResponse) => void;
|
|
|
|
// 处理流式响应
|
|
const handleStream = (
|
|
response: Response,
|
|
onData: IOnData,
|
|
onCompleted?: IOnCompleted,
|
|
onThought?: IOnThought,
|
|
onMessageEnd?: IOnMessageEnd,
|
|
onMessageReplace?: IOnMessageReplace,
|
|
onFile?: IOnFile,
|
|
onWorkflowStarted?: IOnWorkflowStarted,
|
|
onWorkflowFinished?: IOnWorkflowFinished,
|
|
onNodeStarted?: IOnNodeStarted,
|
|
onNodeFinished?: IOnNodeFinished,
|
|
onError?: IOnError,
|
|
) => {
|
|
// console.log('🌊 [handleStream] 开始处理流式响应:', {
|
|
// status: response.status,
|
|
// statusText: response.statusText,
|
|
// headers: Object.fromEntries(response.headers.entries())
|
|
// });
|
|
|
|
if (!response.ok) {
|
|
console.error('❌ [handleStream] 响应错误:', response.status, response.statusText);
|
|
onError?.('网络响应错误');
|
|
throw new Error('网络响应错误');
|
|
}
|
|
|
|
const reader = response.body?.getReader();
|
|
const decoder = new TextDecoder('utf-8');
|
|
let buffer = '';
|
|
let bufferObj: Record<string, any>;
|
|
let isFirstMessage = true;
|
|
let messageCount = 0;
|
|
|
|
// console.log('📖 [handleStream] 获取reader:', !!reader);
|
|
|
|
function read() {
|
|
let hasError = false;
|
|
reader?.read().then((result: any) => {
|
|
// console.log('📨 [handleStream] 读取数据块:', {
|
|
// done: result.done,
|
|
// valueLength: result.value?.length,
|
|
// messageCount: ++messageCount
|
|
// });
|
|
|
|
if (result.done) {
|
|
// console.log('✅ [handleStream] 流式响应完成, 总消息数:', messageCount);
|
|
onCompleted && onCompleted();
|
|
return;
|
|
}
|
|
|
|
const chunk = decoder.decode(result.value, { stream: true });
|
|
buffer += chunk;
|
|
const lines = buffer.split('\n');
|
|
|
|
// console.log('🔍 [handleStream] 处理数据块:', {
|
|
// chunkLength: chunk.length,
|
|
// bufferLength: buffer.length,
|
|
// linesCount: lines.length,
|
|
// chunk: chunk.substring(0, 100) + (chunk.length > 100 ? '...' : '')
|
|
// });
|
|
|
|
try {
|
|
lines.forEach((message, index) => {
|
|
if (message.startsWith('data: ')) {
|
|
const jsonStr = message.substring(6);
|
|
// console.log(`📋 [handleStream] 解析消息 ${index}:`, {
|
|
// jsonLength: jsonStr.length,
|
|
// preview: jsonStr.substring(0, 200) + (jsonStr.length > 200 ? '...' : '')
|
|
// });
|
|
|
|
try {
|
|
bufferObj = JSON.parse(jsonStr) as Record<string, any>;
|
|
// console.log('✨ [handleStream] JSON解析成功:', {
|
|
// event: bufferObj.event,
|
|
// hasAnswer: !!bufferObj.answer,
|
|
// answerLength: bufferObj.answer?.length || 0,
|
|
// conversationId: bufferObj.conversation_id,
|
|
// messageId: bufferObj.id || bufferObj.message_id
|
|
// });
|
|
}
|
|
catch (e) {
|
|
console.warn('⚠️ [handleStream] JSON解析失败:', e, 'JSON:', jsonStr);
|
|
// 处理消息截断
|
|
onData('', isFirstMessage, {
|
|
conversationId: bufferObj?.conversation_id,
|
|
messageId: bufferObj?.message_id || bufferObj?.id,
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (bufferObj.status === 400 || !bufferObj.event) {
|
|
console.error('❌ [handleStream] 错误响应:', {
|
|
status: bufferObj.status,
|
|
event: bufferObj.event,
|
|
message: bufferObj.message,
|
|
code: bufferObj.code
|
|
});
|
|
onData('', false, {
|
|
conversationId: undefined,
|
|
messageId: '',
|
|
errorMessage: bufferObj?.message,
|
|
errorCode: bufferObj?.code,
|
|
});
|
|
hasError = true;
|
|
onCompleted?.(true);
|
|
return;
|
|
}
|
|
|
|
if (bufferObj.event === 'message' || bufferObj.event === 'agent_message') {
|
|
const answer = unicodeToChar(bufferObj.answer);
|
|
// console.log('💬 [handleStream] 处理消息事件:', {
|
|
// event: bufferObj.event,
|
|
// isFirstMessage,
|
|
// answerLength: answer.length,
|
|
// answer: answer.substring(0, 50) + (answer.length > 50 ? '...' : ''),
|
|
// conversationId: bufferObj.conversation_id,
|
|
// messageId: bufferObj.id || bufferObj.message_id
|
|
// });
|
|
|
|
onData(answer, isFirstMessage, {
|
|
conversationId: bufferObj.conversation_id,
|
|
messageId: bufferObj.id || bufferObj.message_id,
|
|
taskId: bufferObj.task_id,
|
|
});
|
|
isFirstMessage = false;
|
|
} else if (bufferObj.event === 'agent_thought' && onThought) {
|
|
// console.log('🤔 [handleStream] 处理思考事件:', bufferObj.event);
|
|
onThought(bufferObj as ThoughtItem);
|
|
} else if (bufferObj.event === 'message_file' && onFile) {
|
|
// console.log('📁 [handleStream] 处理文件事件:', bufferObj.event);
|
|
onFile(bufferObj as VisionFile);
|
|
} else if (bufferObj.event === 'message_end' && onMessageEnd) {
|
|
// console.log('🏁 [handleStream] 处理消息结束事件:', bufferObj.event);
|
|
onMessageEnd(bufferObj as MessageEnd);
|
|
} else if (bufferObj.event === 'message_replace' && onMessageReplace) {
|
|
// console.log('🔄 [handleStream] 处理消息替换事件:', bufferObj.event);
|
|
onMessageReplace(bufferObj as MessageReplace);
|
|
} else if (bufferObj.event === 'workflow_started' && onWorkflowStarted) {
|
|
// console.log('🚀 [handleStream] 处理工作流开始事件:', bufferObj.event);
|
|
onWorkflowStarted(bufferObj as WorkflowStartedResponse);
|
|
} else if (bufferObj.event === 'workflow_finished' && onWorkflowFinished) {
|
|
// console.log('🎯 [handleStream] 处理工作流完成事件:', bufferObj.event);
|
|
onWorkflowFinished(bufferObj as WorkflowFinishedResponse);
|
|
} else if (bufferObj.event === 'node_started' && onNodeStarted) {
|
|
// console.log('🔗 [handleStream] 处理节点开始事件:', bufferObj.event);
|
|
onNodeStarted(bufferObj as NodeStartedResponse);
|
|
} else if (bufferObj.event === 'node_finished' && onNodeFinished) {
|
|
// console.log('✅ [handleStream] 处理节点完成事件:', bufferObj.event);
|
|
onNodeFinished(bufferObj as NodeFinishedResponse);
|
|
} else {
|
|
// console.log('❓ [handleStream] 未知事件类型:', bufferObj.event);
|
|
}
|
|
} else if (message.trim()) {
|
|
// console.log('📝 [handleStream] 非data消息:', message.substring(0, 100));
|
|
}
|
|
});
|
|
|
|
// 保留最后一行(可能是不完整的消息)
|
|
const lastLine = lines[lines.length - 1];
|
|
buffer = lastLine;
|
|
// console.log('💾 [handleStream] 保留缓冲区:', {
|
|
// lastLineLength: lastLine.length,
|
|
// preview: lastLine.substring(0, 50)
|
|
// });
|
|
}
|
|
catch (err) {
|
|
console.error('❌ [handleStream] 解析响应时出错:', err);
|
|
onData('', false, {
|
|
conversationId: undefined,
|
|
messageId: '',
|
|
errorMessage: `${err}`,
|
|
});
|
|
hasError = true;
|
|
onCompleted?.(true);
|
|
return;
|
|
}
|
|
|
|
if (!hasError) {
|
|
// console.log('🔄 [handleStream] 继续读取下一块...');
|
|
read();
|
|
} else {
|
|
// console.log('🛑 [handleStream] 因错误停止读取');
|
|
}
|
|
}).catch(err => {
|
|
console.error('❌ [handleStream] 读取流时出错:', err);
|
|
onError?.(err.message);
|
|
});
|
|
}
|
|
|
|
read();
|
|
};
|
|
|
|
// 基础请求函数
|
|
const baseFetch = (url: string, fetchOptions: any, needAllResponseContent: boolean = false) => {
|
|
const options = Object.assign({}, baseOptions, fetchOptions);
|
|
|
|
// 直接构建Dify API URL
|
|
const urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`;
|
|
|
|
// 确保Authorization头存在
|
|
if (CHAT_CONFIG.API_KEY && options.headers) {
|
|
options.headers['Authorization'] = `Bearer ${CHAT_CONFIG.API_KEY}`;
|
|
}
|
|
|
|
const { body } = options;
|
|
if (body && typeof body === 'object') {
|
|
// 为所有请求添加user参数
|
|
const bodyWithUser = {
|
|
...body,
|
|
user: CHAT_CONFIG.generateUserId(),
|
|
};
|
|
options.body = JSON.stringify(bodyWithUser);
|
|
}
|
|
|
|
return fetch(urlWithPrefix, options)
|
|
.then((res: Response) => {
|
|
if (!res.ok) {
|
|
console.error('❌ Request failed:', {
|
|
status: res.status,
|
|
statusText: res.statusText,
|
|
url: urlWithPrefix
|
|
});
|
|
if (res.status === 422) {
|
|
return res.text().then(text => {
|
|
let errorMessage = text;
|
|
try {
|
|
const data = JSON.parse(text);
|
|
errorMessage = data.message || data.error || text;
|
|
} catch (e) {
|
|
// 如果不是JSON,使用原始文本
|
|
}
|
|
throw new Error(errorMessage);
|
|
});
|
|
}
|
|
throw new Error(`${res.status}: ${res.statusText}`);
|
|
}
|
|
|
|
if (needAllResponseContent) {
|
|
return res.text().then(text => {
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (e) {
|
|
return text;
|
|
}
|
|
});
|
|
}
|
|
|
|
const data = res.json();
|
|
return data;
|
|
})
|
|
.catch((err) => {
|
|
console.error('❌ Request error:', err.message);
|
|
throw err;
|
|
});
|
|
};
|
|
|
|
// SSE请求处理
|
|
export const ssePost = (
|
|
url: string,
|
|
fetchOptions: any,
|
|
{
|
|
onData,
|
|
onCompleted,
|
|
onThought,
|
|
onFile,
|
|
onMessageEnd,
|
|
onMessageReplace,
|
|
onWorkflowStarted,
|
|
onWorkflowFinished,
|
|
onNodeStarted,
|
|
onNodeFinished,
|
|
onError,
|
|
getAbortController,
|
|
}: {
|
|
onData: IOnData;
|
|
onCompleted?: IOnCompleted;
|
|
onThought?: IOnThought;
|
|
onFile?: IOnFile;
|
|
onMessageEnd?: IOnMessageEnd;
|
|
onMessageReplace?: IOnMessageReplace;
|
|
onError?: IOnError;
|
|
getAbortController?: (abortController: AbortController) => void;
|
|
onWorkflowStarted?: IOnWorkflowStarted;
|
|
onWorkflowFinished?: IOnWorkflowFinished;
|
|
onNodeStarted?: IOnNodeStarted;
|
|
onNodeFinished?: IOnNodeFinished;
|
|
},
|
|
) => {
|
|
const options = Object.assign({}, baseOptions, {
|
|
method: 'POST',
|
|
}, fetchOptions);
|
|
|
|
// 直接构建Dify API URL
|
|
const urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`;
|
|
|
|
const controller = new AbortController();
|
|
if (getAbortController)
|
|
getAbortController(controller);
|
|
|
|
options.headers = {
|
|
...options.headers,
|
|
'Content-Type': 'application/json',
|
|
'Accept': ContentType.stream,
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
};
|
|
|
|
options.signal = controller.signal;
|
|
|
|
const { body } = options;
|
|
if (body && typeof body === 'object') {
|
|
// 为SSE请求添加user参数
|
|
const bodyWithUser = {
|
|
...body,
|
|
user: CHAT_CONFIG.generateUserId(),
|
|
};
|
|
options.body = JSON.stringify(bodyWithUser);
|
|
}
|
|
|
|
return fetch(urlWithPrefix, options)
|
|
.then((res: Response) => {
|
|
console.log('📡 SSE Response:', {
|
|
status: res.status,
|
|
statusText: res.statusText,
|
|
url: urlWithPrefix
|
|
});
|
|
|
|
if (!/^(2|3)\d{2}$/.test(res.status.toString())) {
|
|
res.json().then((data: any) => {
|
|
console.error('❌ SSE Error:', data.message || 'Server Error');
|
|
onError?.(data.message || 'Server Error');
|
|
});
|
|
return;
|
|
}
|
|
|
|
handleStream(
|
|
res,
|
|
onData,
|
|
onCompleted,
|
|
onThought,
|
|
onMessageEnd,
|
|
onMessageReplace,
|
|
onFile,
|
|
onWorkflowStarted,
|
|
onWorkflowFinished,
|
|
onNodeStarted,
|
|
onNodeFinished,
|
|
onError
|
|
);
|
|
})
|
|
.catch((err) => {
|
|
console.error('❌ SSE Request Error:', err);
|
|
onError?.(err.message);
|
|
});
|
|
};
|
|
|
|
// 获取会话列表
|
|
export const fetchConversations = async () => {
|
|
const user = CHAT_CONFIG.generateUserId();
|
|
const params = new URLSearchParams({
|
|
user,
|
|
limit: '100',
|
|
first_id: '',
|
|
});
|
|
|
|
return fetch(`${CHAT_CONFIG.API_URL}/conversations?${params}`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
},
|
|
}).then(res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to fetch conversations: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
});
|
|
};
|
|
|
|
// 获取聊天消息列表
|
|
export const fetchChatList = async (conversationId: string) => {
|
|
const user = CHAT_CONFIG.generateUserId();
|
|
const params = new URLSearchParams({
|
|
user,
|
|
conversation_id: conversationId,
|
|
limit: '20',
|
|
last_id: '',
|
|
});
|
|
|
|
return fetch(`${CHAT_CONFIG.API_URL}/messages?${params}`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
},
|
|
}).then(res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to fetch chat list: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
});
|
|
};
|
|
|
|
// 获取应用参数
|
|
export const fetchAppParams = async () => {
|
|
return fetch(`${CHAT_CONFIG.API_URL}/parameters`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
},
|
|
}).then(res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to fetch app params: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
});
|
|
};
|
|
|
|
// 更新反馈
|
|
export const updateFeedback = async ({ url, body }: { url: string; body: Feedbacktype }) => {
|
|
const messageId = url.split('/').pop(); // 从URL中提取messageId
|
|
|
|
return fetch(`${CHAT_CONFIG.API_URL}/messages/${messageId}/feedbacks`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
},
|
|
body: JSON.stringify({
|
|
...body,
|
|
user: CHAT_CONFIG.generateUserId(),
|
|
}),
|
|
}).then(res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to update feedback: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
});
|
|
};
|
|
|
|
// 生成会话名称
|
|
export const generateConversationName = async (id: string) => {
|
|
return fetch(`${CHAT_CONFIG.API_URL}/conversations/${id}/name`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
},
|
|
body: JSON.stringify({
|
|
auto_generate: true,
|
|
user: CHAT_CONFIG.generateUserId(),
|
|
}),
|
|
}).then(res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to generate conversation name: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
});
|
|
};
|
|
|
|
// 重命名会话
|
|
export const renameConversation = async (id: string, name: string, autoGenerate: boolean = false) => {
|
|
return fetch(`${CHAT_CONFIG.API_URL}/conversations/${id}/name`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
},
|
|
body: JSON.stringify({
|
|
name: autoGenerate ? undefined : name,
|
|
auto_generate: autoGenerate,
|
|
user: CHAT_CONFIG.generateUserId(),
|
|
}),
|
|
}).then(res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to rename conversation: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
});
|
|
};
|
|
|
|
// 删除会话
|
|
export const deleteConversation = async (id: string) => {
|
|
return fetch(`${CHAT_CONFIG.API_URL}/conversations/${id}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
|
},
|
|
body: JSON.stringify({
|
|
user: CHAT_CONFIG.generateUserId(),
|
|
}),
|
|
}).then(res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to delete conversation: ${res.status}`);
|
|
}
|
|
return res.json();
|
|
});
|
|
};
|
|
|
|
// 文件上传
|
|
export const upload = (fetchOptions: any): Promise<any> => {
|
|
const urlWithPrefix = `${CHAT_CONFIG.API_URL}/files/upload`;
|
|
|
|
const defaultOptions = {
|
|
method: 'POST',
|
|
url: urlWithPrefix,
|
|
data: {},
|
|
};
|
|
|
|
const options = {
|
|
...defaultOptions,
|
|
...fetchOptions,
|
|
};
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const xhr = options.xhr;
|
|
xhr.open(options.method, options.url);
|
|
|
|
for (const key in options.headers)
|
|
xhr.setRequestHeader(key, options.headers[key]);
|
|
|
|
if (CHAT_CONFIG.API_KEY) {
|
|
xhr.setRequestHeader('Authorization', `Bearer ${CHAT_CONFIG.API_KEY}`);
|
|
}
|
|
|
|
// 添加user参数到formData
|
|
if (options.data instanceof FormData) {
|
|
options.data.append('user', CHAT_CONFIG.generateUserId());
|
|
}
|
|
|
|
xhr.withCredentials = false; // 改为false,因为直接调用Dify API
|
|
xhr.onreadystatechange = function () {
|
|
if (xhr.readyState === 4) {
|
|
if (xhr.status === 200)
|
|
resolve({ id: xhr.response });
|
|
else
|
|
reject(new Error(xhr.responseText || 'Upload failed'));
|
|
}
|
|
};
|
|
|
|
xhr.upload.onprogress = options.onprogress;
|
|
xhr.send(options.data);
|
|
});
|
|
};
|
|
|
|
// 公共请求函数
|
|
export const request = (url: string, options = {}, needAllResponseContent = false) => {
|
|
return baseFetch(url, { ...baseOptions, ...options }, needAllResponseContent);
|
|
};
|
|
|
|
// GET 请求
|
|
export const get = (url: string, options = {}) => {
|
|
return request(url, { ...options, method: 'GET' });
|
|
};
|
|
|
|
// POST 请求
|
|
export const post = (url: string, options = {}) => {
|
|
return request(url, { ...options, method: 'POST' });
|
|
};
|
|
|
|
// PUT 请求
|
|
export const put = (url: string, options = {}) => {
|
|
return request(url, { ...options, method: 'PUT' });
|
|
};
|
|
|
|
// DELETE 请求
|
|
export const del = (url: string, options = {}) => {
|
|
return request(url, { ...options, method: 'DELETE' });
|
|
};
|
|
|
|
// 发送聊天消息
|
|
export const sendChatMessage = async (
|
|
body: Record<string, any>,
|
|
{
|
|
onData,
|
|
onCompleted,
|
|
onThought,
|
|
onFile,
|
|
onError,
|
|
getAbortController,
|
|
onMessageEnd,
|
|
onMessageReplace,
|
|
onWorkflowStarted,
|
|
onNodeStarted,
|
|
onNodeFinished,
|
|
onWorkflowFinished,
|
|
}: {
|
|
onData: IOnData;
|
|
onCompleted: IOnCompleted;
|
|
onFile?: IOnFile;
|
|
onThought?: IOnThought;
|
|
onMessageEnd?: IOnMessageEnd;
|
|
onMessageReplace?: IOnMessageReplace;
|
|
onError?: IOnError;
|
|
getAbortController?: (abortController: AbortController) => void;
|
|
onWorkflowStarted?: IOnWorkflowStarted;
|
|
onNodeStarted?: IOnNodeStarted;
|
|
onNodeFinished?: IOnNodeFinished;
|
|
onWorkflowFinished?: IOnWorkflowFinished;
|
|
},
|
|
) => {
|
|
return ssePost('chat-messages', {
|
|
body: {
|
|
...body,
|
|
response_mode: 'streaming',
|
|
},
|
|
}, {
|
|
onData,
|
|
onCompleted,
|
|
onThought,
|
|
onFile,
|
|
onError,
|
|
getAbortController,
|
|
onMessageEnd,
|
|
onMessageReplace,
|
|
onNodeStarted,
|
|
onWorkflowStarted,
|
|
onWorkflowFinished,
|
|
onNodeFinished
|
|
});
|
|
};
|