基于 shiy-temp分支修改
This commit is contained in:
@@ -0,0 +1,580 @@
|
||||
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: 'include' as RequestCredentials,
|
||||
headers: new Headers({
|
||||
'Content-Type': ContentType.json,
|
||||
}),
|
||||
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,
|
||||
) => {
|
||||
if (!response.ok) {
|
||||
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;
|
||||
|
||||
function read() {
|
||||
let hasError = false;
|
||||
reader?.read().then((result: any) => {
|
||||
if (result.done) {
|
||||
onCompleted && onCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(result.value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
|
||||
try {
|
||||
lines.forEach((message) => {
|
||||
if (message.startsWith('data: ')) {
|
||||
try {
|
||||
bufferObj = JSON.parse(message.substring(6)) as Record<string, any>;
|
||||
}
|
||||
catch (e) {
|
||||
// 处理消息截断
|
||||
onData('', isFirstMessage, {
|
||||
conversationId: bufferObj?.conversation_id,
|
||||
messageId: bufferObj?.message_id || bufferObj?.id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (bufferObj.status === 400 || !bufferObj.event) {
|
||||
onData('', false, {
|
||||
conversationId: undefined,
|
||||
messageId: '',
|
||||
errorMessage: bufferObj?.message,
|
||||
errorCode: bufferObj?.code,
|
||||
});
|
||||
hasError = true;
|
||||
onCompleted?.(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bufferObj.event === 'message' || bufferObj.event === 'agent_message') {
|
||||
onData(unicodeToChar(bufferObj.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) {
|
||||
onThought(bufferObj as ThoughtItem);
|
||||
} else if (bufferObj.event === 'message_file' && onFile) {
|
||||
onFile(bufferObj as VisionFile);
|
||||
} else if (bufferObj.event === 'message_end' && onMessageEnd) {
|
||||
onMessageEnd(bufferObj as MessageEnd);
|
||||
} else if (bufferObj.event === 'message_replace' && onMessageReplace) {
|
||||
onMessageReplace(bufferObj as MessageReplace);
|
||||
} else if (bufferObj.event === 'workflow_started' && onWorkflowStarted) {
|
||||
onWorkflowStarted(bufferObj as WorkflowStartedResponse);
|
||||
} else if (bufferObj.event === 'workflow_finished' && onWorkflowFinished) {
|
||||
onWorkflowFinished(bufferObj as WorkflowFinishedResponse);
|
||||
} else if (bufferObj.event === 'node_started' && onNodeStarted) {
|
||||
onNodeStarted(bufferObj as NodeStartedResponse);
|
||||
} else if (bufferObj.event === 'node_finished' && onNodeFinished) {
|
||||
onNodeFinished(bufferObj as NodeFinishedResponse);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 保留最后一行(可能是不完整的消息)
|
||||
buffer = lines[lines.length - 1];
|
||||
}
|
||||
catch (err) {
|
||||
console.error('解析响应时出错:', err);
|
||||
onData('', false, {
|
||||
conversationId: undefined,
|
||||
messageId: '',
|
||||
errorMessage: `${err}`,
|
||||
});
|
||||
hasError = true;
|
||||
onCompleted?.(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasError)
|
||||
read();
|
||||
}).catch(err => {
|
||||
console.error('读取流时出错:', err);
|
||||
onError?.(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
read();
|
||||
};
|
||||
|
||||
// 基础Fetch函数
|
||||
const baseFetch = (url: string, fetchOptions: any, needAllResponseContent: boolean = false) => {
|
||||
const options = Object.assign({}, baseOptions, fetchOptions);
|
||||
|
||||
// 构建完整URL - 修复重复的/v1问题
|
||||
// CHAT_CONFIG.API_URL 已经包含了 /v1,所以不需要再添加 API_PREFIX
|
||||
let urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`;
|
||||
|
||||
|
||||
const { method, params, body } = options;
|
||||
|
||||
// 处理GET请求的查询参数
|
||||
if (method === 'GET' && params) {
|
||||
const paramsArray: string[] = [];
|
||||
Object.keys(params).forEach(key =>
|
||||
paramsArray.push(`${key}=${encodeURIComponent(params[key])}`),
|
||||
);
|
||||
if (urlWithPrefix.search(/\?/) === -1)
|
||||
urlWithPrefix += `?${paramsArray.join('&')}`;
|
||||
else
|
||||
urlWithPrefix += `&${paramsArray.join('&')}`;
|
||||
|
||||
delete options.params;
|
||||
}
|
||||
|
||||
// 处理请求体
|
||||
if (body && typeof body === 'object')
|
||||
options.body = JSON.stringify(body);
|
||||
|
||||
// 添加认证头
|
||||
if (!options.headers)
|
||||
options.headers = {};
|
||||
|
||||
if (CHAT_CONFIG.API_KEY) {
|
||||
options.headers['Authorization'] = `Bearer ${CHAT_CONFIG.API_KEY}`;
|
||||
}
|
||||
|
||||
return Promise.race([
|
||||
new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error('请求超时'));
|
||||
}, SSE_TIMEOUT);
|
||||
}),
|
||||
new Promise((resolve, reject) => {
|
||||
fetch(urlWithPrefix, options)
|
||||
.then((res: Response) => {
|
||||
const resClone = res.clone();
|
||||
|
||||
('📥 API Response:', {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
url: urlWithPrefix
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
if (!/^(2|3)\d{2}$/.test(res.status.toString())) {
|
||||
try {
|
||||
const bodyJson = res.json();
|
||||
switch (res.status) {
|
||||
case 401:
|
||||
console.error('❌ Invalid token');
|
||||
break;
|
||||
default:
|
||||
bodyJson.then((data: any) => {
|
||||
console.error('❌ API Error:', data.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('❌ Response Error:', e);
|
||||
}
|
||||
return Promise.reject(resClone);
|
||||
}
|
||||
|
||||
// 处理删除API(204状态码)
|
||||
if (res.status === 204) {
|
||||
resolve({ result: 'success' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 返回数据
|
||||
const contentType = res.headers.get('Content-Type') || '';
|
||||
const data = contentType.includes('application/octet-stream') ? res.blob() : res.json();
|
||||
|
||||
resolve(needAllResponseContent ? resClone : data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('❌ Fetch Error:', err);
|
||||
reject(err);
|
||||
});
|
||||
}),
|
||||
]);
|
||||
};
|
||||
|
||||
// SSE POST 请求
|
||||
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);
|
||||
|
||||
// 修复URL构建逻辑,与baseFetch保持一致
|
||||
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,
|
||||
};
|
||||
|
||||
if (CHAT_CONFIG.API_KEY) {
|
||||
options.headers['Authorization'] = `Bearer ${CHAT_CONFIG.API_KEY}`;
|
||||
}
|
||||
|
||||
options.signal = controller.signal;
|
||||
|
||||
const { body } = options;
|
||||
if (body && typeof body === 'object')
|
||||
options.body = JSON.stringify(body);
|
||||
|
||||
return fetch(urlWithPrefix, options)
|
||||
.then((res: Response) => {
|
||||
('📡 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 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
|
||||
});
|
||||
};
|
||||
|
||||
// 获取会话列表
|
||||
export const fetchConversations = async () => {
|
||||
return get('conversations', {
|
||||
params: { limit: 100, first_id: '' },
|
||||
});
|
||||
};
|
||||
|
||||
// 获取聊天消息列表
|
||||
export const fetchChatList = async (conversationId: string) => {
|
||||
return get('messages', {
|
||||
params: { conversation_id: conversationId, limit: 20, last_id: '' },
|
||||
});
|
||||
};
|
||||
|
||||
// 获取应用参数
|
||||
export const fetchAppParams = async () => {
|
||||
return get('parameters');
|
||||
};
|
||||
|
||||
// 更新反馈
|
||||
export const updateFeedback = async ({ url, body }: { url: string; body: Feedbacktype }) => {
|
||||
return post(url, { body });
|
||||
};
|
||||
|
||||
// 生成会话名称
|
||||
export const generateConversationName = async (id: string) => {
|
||||
return post(`conversations/${id}/name`, {
|
||||
body: { auto_generate: true },
|
||||
});
|
||||
};
|
||||
|
||||
// 重命名会话
|
||||
export const renameConversation = async (id: string, name: string, autoGenerate: boolean = false) => {
|
||||
return post(`conversations/${id}/name`, {
|
||||
body: {
|
||||
name: autoGenerate ? undefined : name,
|
||||
auto_generate: autoGenerate
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 删除会话
|
||||
export const deleteConversation = async (id: string) => {
|
||||
return del(`conversations/${id}`);
|
||||
};
|
||||
|
||||
// 文件上传
|
||||
export const upload = (fetchOptions: any): Promise<any> => {
|
||||
const urlPrefix = CHAT_CONFIG.API_PREFIX;
|
||||
const urlWithPrefix = `${CHAT_CONFIG.API_URL}${urlPrefix}/file-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}`);
|
||||
}
|
||||
|
||||
xhr.withCredentials = true;
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200)
|
||||
resolve({ id: xhr.response });
|
||||
else
|
||||
reject(xhr);
|
||||
}
|
||||
};
|
||||
xhr.upload.onprogress = options.onprogress;
|
||||
xhr.send(options.data);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
import { CHAT_CONFIG } from '../config/chat';
|
||||
|
||||
// 获取环境变量的服务端函数
|
||||
const getServerEnvVar = (name: string, defaultValue: string = '') => {
|
||||
return process.env[name] || defaultValue;
|
||||
};
|
||||
|
||||
// Dify API 客户端配置
|
||||
const DIFY_CONFIG = {
|
||||
API_URL: getServerEnvVar('NEXT_PUBLIC_API_URL', 'https://api.dify.ai/v1'),
|
||||
API_KEY: getServerEnvVar('NEXT_PUBLIC_APP_KEY', ''),
|
||||
APP_ID: (() => {
|
||||
const rawAppId = getServerEnvVar('NEXT_PUBLIC_APP_ID', '');
|
||||
// 从完整URL中提取APP ID
|
||||
const match = rawAppId.match(/\/app\/([a-f0-9-]{36})/);
|
||||
return match ? match[1] : rawAppId;
|
||||
})(),
|
||||
};
|
||||
|
||||
// console.log('🔧 Dify Client Config:', {
|
||||
// apiUrl: DIFY_CONFIG.API_URL,
|
||||
// appId: DIFY_CONFIG.APP_ID,
|
||||
// hasApiKey: !!DIFY_CONFIG.API_KEY
|
||||
// });
|
||||
|
||||
// 基础请求函数
|
||||
const difyFetch = async (endpoint: string, options: RequestInit = {}) => {
|
||||
const url = `${DIFY_CONFIG.API_URL}/${endpoint.replace(/^\//, '')}`;
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${DIFY_CONFIG.API_KEY}`,
|
||||
...options.headers,
|
||||
};
|
||||
|
||||
// console.log('🌐 Dify API Request:', {
|
||||
// url,
|
||||
// method: options.method || 'GET',
|
||||
// hasAuth: !!DIFY_CONFIG.API_KEY
|
||||
// });
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('❌ Dify API Error:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: errorText
|
||||
});
|
||||
throw new Error(`Dify API Error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
// 生成用户ID
|
||||
const generateUserId = (sessionId: string) => {
|
||||
return `user_${DIFY_CONFIG.APP_ID}:${sessionId}`;
|
||||
};
|
||||
|
||||
// Dify API 客户端
|
||||
export const difyClient = {
|
||||
// 获取应用参数
|
||||
async getApplicationParameters(user: string) {
|
||||
const response = await difyFetch('parameters', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${DIFY_CONFIG.API_KEY}`,
|
||||
},
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
|
||||
// 获取会话列表
|
||||
async getConversations(user: string) {
|
||||
const params = new URLSearchParams({
|
||||
user,
|
||||
limit: '100',
|
||||
first_id: '',
|
||||
});
|
||||
|
||||
const response = await difyFetch(`conversations?${params}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
|
||||
// 获取会话消息
|
||||
async getConversationMessages(user: string, conversationId: string) {
|
||||
const params = new URLSearchParams({
|
||||
user,
|
||||
conversation_id: conversationId,
|
||||
limit: '20',
|
||||
last_id: '',
|
||||
});
|
||||
|
||||
const response = await difyFetch(`messages?${params}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
|
||||
// 发送聊天消息
|
||||
async createChatMessage(
|
||||
inputs: Record<string, any>,
|
||||
query: string,
|
||||
user: string,
|
||||
responseMode: string = 'streaming',
|
||||
conversationId?: string,
|
||||
files?: any[]
|
||||
) {
|
||||
const body = {
|
||||
inputs,
|
||||
query,
|
||||
user,
|
||||
response_mode: responseMode,
|
||||
conversation_id: conversationId,
|
||||
files: files || [],
|
||||
};
|
||||
|
||||
const response = await difyFetch('chat-messages', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
// 对于流式响应,直接返回Response对象
|
||||
if (responseMode === 'streaming') {
|
||||
return response;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
// 重命名会话
|
||||
async renameConversation(conversationId: string, name: string, user: string, autoGenerate: boolean = false) {
|
||||
const body = {
|
||||
name,
|
||||
auto_generate: autoGenerate,
|
||||
user,
|
||||
};
|
||||
|
||||
const response = await difyFetch(`conversations/${conversationId}/name`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
|
||||
// 删除会话
|
||||
async deleteConversation(conversationId: string, user: string) {
|
||||
const body = {
|
||||
user,
|
||||
};
|
||||
|
||||
const response = await difyFetch(`conversations/${conversationId}`, {
|
||||
method: 'DELETE',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
|
||||
// 更新消息反馈
|
||||
async updateMessageFeedback(messageId: string, rating: 'like' | 'dislike' | null, user: string) {
|
||||
const body = {
|
||||
rating,
|
||||
user,
|
||||
};
|
||||
|
||||
const response = await difyFetch(`messages/${messageId}/feedbacks`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
};
|
||||
|
||||
// 工具函数
|
||||
export const difyUtils = {
|
||||
generateUserId,
|
||||
getConfig: () => DIFY_CONFIG,
|
||||
};
|
||||
Reference in New Issue
Block a user