删除调试打印信息

This commit is contained in:
PingChuan
2025-07-02 10:28:47 +08:00
parent ce851cc448
commit 6698542839
2 changed files with 422 additions and 72 deletions
+6 -6
View File
@@ -36,12 +36,12 @@ const getDifyApiUrl = () => {
const getAppId = () => {
const rawAppId = getEnvVar('NEXT_PUBLIC_APP_ID', '');
const extractedAppId = extractAppId(rawAppId);
console.log('🔧 Chat Config Debug:', {
rawAppId,
extractedAppId,
difyApiUrl: getDifyApiUrl(),
hasApiKey: !!getEnvVar('NEXT_PUBLIC_APP_KEY', ''),
});
// console.log('🔧 Chat Config Debug:', {
// rawAppId,
// extractedAppId,
// difyApiUrl: getDifyApiUrl(),
// hasApiKey: !!getEnvVar('NEXT_PUBLIC_APP_KEY', ''),
// });
return extractedAppId;
};
+416 -66
View File
@@ -108,7 +108,40 @@ export type IOnWorkflowFinished = (workflowFinished: WorkflowFinishedResponse) =
export type IOnNodeStarted = (nodeStarted: NodeStartedResponse) => void;
export type IOnNodeFinished = (nodeFinished: NodeFinishedResponse) => void;
// 处理流式响应
/**
* 处理服务器发送事件 (SSE) 流式响应
*
* 这是核心的流式响应处理函数,负责:
* - 解析 SSE 数据流
* - 处理各种事件类型(消息、思考、文件、工作流等)
* - 错误处理和状态管理
* - 实时更新 UI
*
* @param response - fetch API 返回的 Response 对象
* @param onData - 处理消息数据的回调函数
* @param onCompleted - 流式响应完成时的回调函数
* @param onThought - 处理 Agent 思考过程的回调函数
* @param onMessageEnd - 处理消息结束事件的回调函数
* @param onMessageReplace - 处理消息替换事件的回调函数
* @param onFile - 处理文件事件的回调函数
* @param onWorkflowStarted - 处理工作流开始事件的回调函数
* @param onWorkflowFinished - 处理工作流完成事件的回调函数
* @param onNodeStarted - 处理节点开始事件的回调函数
* @param onNodeFinished - 处理节点完成事件的回调函数
* @param onError - 处理错误的回调函数
*
* @example
* ```typescript
* const response = await fetch('/api/chat-messages', options);
* handleStream(
* response,
* (message, isFirst, info) => console.log('收到消息:', message),
* () => console.log('流式响应完成'),
* (thought) => console.log('AI思考:', thought),
* // ... 其他回调
* );
* ```
*/
const handleStream = (
response: Response,
onData: IOnData,
@@ -123,12 +156,6 @@ const handleStream = (
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?.('网络响应错误');
@@ -142,19 +169,12 @@ const handleStream = (
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;
}
@@ -163,31 +183,14 @@ const handleStream = (
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);
@@ -219,14 +222,7 @@ const handleStream = (
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,
@@ -269,10 +265,6 @@ const handleStream = (
// 保留最后一行(可能是不完整的消息)
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);
@@ -287,10 +279,8 @@ const handleStream = (
}
if (!hasError) {
// console.log('🔄 [handleStream] 继续读取下一块...');
read();
} else {
// console.log('🛑 [handleStream] 因错误停止读取');
}
}).catch(err => {
console.error('❌ [handleStream] 读取流时出错:', err);
@@ -301,7 +291,34 @@ const handleStream = (
read();
};
// 基础请求函数
/**
* 基础 HTTP 请求函数
*
* 提供统一的请求配置和错误处理:
* - 自动添加认证头
* - 统一的 URL 处理
* - 错误状态码处理
* - 自动添加用户 ID
*
* @param url - 请求的 URL 路径(相对于 API 基础 URL)
* @param fetchOptions - fetch API 的配置选项
* @param needAllResponseContent - 是否需要返回完整的响应内容而不是 JSON
* @returns Promise<any> - 返回解析后的响应数据
*
* @throws {Error} 当请求失败时抛出错误
*
* @example
* ```typescript
* // 发送 GET 请求
* const data = await baseFetch('conversations', { method: 'GET' });
*
* // 发送 POST 请求
* const result = await baseFetch('chat-messages', {
* method: 'POST',
* body: { query: 'Hello' }
* });
* ```
*/
const baseFetch = (url: string, fetchOptions: any, needAllResponseContent: boolean = false) => {
const options = Object.assign({}, baseOptions, fetchOptions);
@@ -365,7 +382,42 @@ const baseFetch = (url: string, fetchOptions: any, needAllResponseContent: boole
});
};
// SSE请求处理
/**
* 发送 SSE (Server-Sent Events) POST 请求
*
* 专门用于处理流式响应的 POST 请求:
* - 配置 SSE 相关的请求头
* - 设置 AbortController 用于取消请求
* - 调用 handleStream 处理流式响应
* - 自动添加用户 ID 到请求体
*
* @param url - 请求的 URL 路径
* @param fetchOptions - fetch 配置选项
* @param callbacks - 包含各种事件回调的对象
* @param callbacks.onData - 处理消息数据的回调
* @param callbacks.onCompleted - 流式响应完成时的回调
* @param callbacks.onThought - 处理思考过程的回调
* @param callbacks.onFile - 处理文件的回调
* @param callbacks.onMessageEnd - 处理消息结束的回调
* @param callbacks.onMessageReplace - 处理消息替换的回调
* @param callbacks.onError - 处理错误的回调
* @param callbacks.getAbortController - 获取中止控制器的回调
* @param callbacks.onWorkflowStarted - 处理工作流开始的回调
* @param callbacks.onWorkflowFinished - 处理工作流完成的回调
* @param callbacks.onNodeStarted - 处理节点开始的回调
* @param callbacks.onNodeFinished - 处理节点完成的回调
*
* @example
* ```typescript
* ssePost('chat-messages', {
* body: { query: 'Hello', response_mode: 'streaming' }
* }, {
* onData: (message, isFirst, info) => updateUI(message),
* onCompleted: () => setLoading(false),
* onError: (error) => showError(error)
* });
* ```
*/
export const ssePost = (
url: string,
fetchOptions: any,
@@ -429,11 +481,6 @@ export const ssePost = (
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) => {
@@ -464,7 +511,28 @@ export const ssePost = (
});
};
// 获取会话列表
/**
* 获取用户的会话列表
*
* 从 Dify API 获取当前用户的所有会话:
* - 自动分页获取(最多100条)
* - 包含会话 ID、名称、输入参数等信息
* - 按时间倒序排列
*
* @returns Promise<any> - 包含会话列表的响应对象
* @returns Promise<any>.data - 会话数组
* @returns Promise<any>.has_more - 是否还有更多数据
* @returns Promise<any>.limit - 每页限制数量
*
* @throws {Error} 当获取会话列表失败时抛出错误
*
* @example
* ```typescript
* const response = await fetchConversations();
* const conversations = response.data;
* console.log('会话数量:', conversations.length);
* ```
*/
export const fetchConversations = async () => {
const user = CHAT_CONFIG.generateUserId();
const params = new URLSearchParams({
@@ -486,7 +554,29 @@ export const fetchConversations = async () => {
});
};
// 获取聊天消息列表
/**
* 获取指定会话的聊天消息列表
*
* 从 Dify API 获取特定会话的消息历史:
* - 支持分页加载(最多20条)
* - 包含用户消息和 AI 回复
* - 按时间顺序排列
*
* @param conversationId - 会话 ID
* @returns Promise<any> - 包含消息列表的响应对象
* @returns Promise<any>.data - 消息数组
* @returns Promise<any>.has_more - 是否还有更多历史消息
* @returns Promise<any>.limit - 每页限制数量
*
* @throws {Error} 当获取消息列表失败时抛出错误
*
* @example
* ```typescript
* const response = await fetchChatList('conv-123');
* const messages = response.data;
* console.log('消息数量:', messages.length);
* ```
*/
export const fetchChatList = async (conversationId: string) => {
const user = CHAT_CONFIG.generateUserId();
const params = new URLSearchParams({
@@ -509,7 +599,29 @@ export const fetchChatList = async (conversationId: string) => {
});
};
// 获取应用参数
/**
* 获取应用参数配置
*
* 从 Dify API 获取应用的配置信息:
* - 用户输入表单配置
* - 开场白设置
* - 文件上传配置
* - 其他应用级别设置
*
* @returns Promise<any> - 包含应用参数的响应对象
* @returns Promise<any>.user_input_form - 用户输入表单配置
* @returns Promise<any>.opening_statement - 开场白内容
* @returns Promise<any>.file_upload - 文件上传配置
*
* @throws {Error} 当获取应用参数失败时抛出错误
*
* @example
* ```typescript
* const params = await fetchAppParams();
* const { user_input_form, opening_statement } = params.data;
* console.log('开场白:', opening_statement);
* ```
*/
export const fetchAppParams = async () => {
return fetch(`${CHAT_CONFIG.API_URL}/parameters`, {
method: 'GET',
@@ -524,7 +636,31 @@ export const fetchAppParams = async () => {
});
};
// 更新反馈
/**
* 更新消息反馈[未使用]
*
* 向 Dify API 提交用户对 AI 回复的反馈:
* - 支持点赞/点踩评价
* - 可添加文字反馈内容
* - 用于改进 AI 回复质量
*
* @param params - 反馈参数对象
* @param params.url - 包含消息 ID 的 URL
* @param params.body - 反馈内容
* @param params.body.rating - 评分:'like' | 'dislike' | null
* @param params.body.content - 文字反馈内容(可选)
* @returns Promise<any> - 反馈提交结果
*
* @throws {Error} 当提交反馈失败时抛出错误
*
* @example
* ```typescript
* await updateFeedback({
* url: '/messages/msg-123/feedbacks',
* body: { rating: 'like', content: '回答很好' }
* });
* ```
*/
export const updateFeedback = async ({ url, body }: { url: string; body: Feedbacktype }) => {
const messageId = url.split('/').pop(); // 从URL中提取messageId
@@ -546,7 +682,26 @@ export const updateFeedback = async ({ url, body }: { url: string; body: Feedbac
});
};
// 生成会话名称
/**
* 生成会话名称[未使用]
*
* 让 AI 根据会话内容自动生成合适的会话名称:
* - 基于会话中的消息内容
* - 生成简洁有意义的标题
* - 用于替换默认的"新对话"名称
*
* @param id - 会话 ID
* @returns Promise<any> - 包含生成名称的响应对象
* @returns Promise<any>.name - 生成的会话名称
*
* @throws {Error} 当生成名称失败时抛出错误
*
* @example
* ```typescript
* const result = await generateConversationName('conv-123');
* console.log('生成的名称:', result.name);
* ```
*/
export const generateConversationName = async (id: string) => {
return fetch(`${CHAT_CONFIG.API_URL}/conversations/${id}/name`, {
method: 'POST',
@@ -566,7 +721,31 @@ export const generateConversationName = async (id: string) => {
});
};
// 重命名会话
/**
* 重命名会话
*
* 更新会话的显示名称:
* - 支持手动设置名称
* - 支持 AI 自动生成名称
* - 更新后在会话列表中显示新名称
*
* @param id - 会话 ID
* @param name - 新的会话名称(当 autoGenerate 为 false 时使用)
* @param autoGenerate - 是否使用 AI 自动生成名称,默认为 false
* @returns Promise<any> - 重命名结果
* @returns Promise<any>.name - 最终的会话名称
*
* @throws {Error} 当重命名失败时抛出错误
*
* @example
* ```typescript
* // 手动设置名称
* await renameConversation('conv-123', '关于编程的讨论');
*
* // AI 自动生成名称
* await renameConversation('conv-123', '', true);
* ```
*/
export const renameConversation = async (id: string, name: string, autoGenerate: boolean = false) => {
return fetch(`${CHAT_CONFIG.API_URL}/conversations/${id}/name`, {
method: 'POST',
@@ -587,7 +766,25 @@ export const renameConversation = async (id: string, name: string, autoGenerate:
});
};
// 删除会话
/**
* 删除会话
*
* 从用户的会话列表中永久删除指定会话:
* - 删除会话及其所有消息
* - 操作不可逆
* - 删除后从会话列表中移除
*
* @param id - 要删除的会话 ID
* @returns Promise<any> - 删除操作结果
*
* @throws {Error} 当删除会话失败时抛出错误
*
* @example
* ```typescript
* await deleteConversation('conv-123');
* console.log('会话已删除');
* ```
*/
export const deleteConversation = async (id: string) => {
return fetch(`${CHAT_CONFIG.API_URL}/conversations/${id}`, {
method: 'DELETE',
@@ -606,7 +803,43 @@ export const deleteConversation = async (id: string) => {
});
};
// 文件上传
/**
* 上传文件到 Dify API[未使用]
*
* 使用 XMLHttpRequest 上传文件:
* - 支持文件上传进度监控
* - 自动添加认证头和用户 ID
* - 返回文件 ID 用于后续引用
*
* @param fetchOptions - 上传配置选项
* @param fetchOptions.method - HTTP 方法(通常为 'POST'
* @param fetchOptions.url - 上传 URL(可选,会自动构建)
* @param fetchOptions.data - FormData 对象,包含要上传的文件
* @param fetchOptions.headers - 额外的请求头
* @param fetchOptions.xhr - XMLHttpRequest 实例
* @param fetchOptions.onprogress - 上传进度回调函数
* @returns Promise<any> - 返回包含文件 ID 的对象
* @returns Promise<any>.id - 上传后的文件 ID
*
* @throws {Error} 当文件上传失败时抛出错误
*
* @example
* ```typescript
* const formData = new FormData();
* formData.append('file', fileBlob);
*
* const xhr = new XMLHttpRequest();
* const result = await upload({
* data: formData,
* xhr: xhr,
* onprogress: (event) => {
* const progress = (event.loaded / event.total) * 100;
* console.log('上传进度:', progress + '%');
* }
* });
* console.log('文件ID:', result.id);
* ```
*/
export const upload = (fetchOptions: any): Promise<any> => {
const urlWithPrefix = `${CHAT_CONFIG.API_URL}/files/upload`;
@@ -652,32 +885,149 @@ export const upload = (fetchOptions: any): Promise<any> => {
});
};
// 公共请求函数
/**
* 通用 HTTP 请求函数
*
* 基于 baseFetch 的通用请求封装:
* - 合并基础配置和自定义选项
* - 统一的错误处理
* - 支持所有 HTTP 方法
*
* @param url - 请求 URL 路径
* @param options - 请求配置选项,默认为空对象
* @param needAllResponseContent - 是否返回完整响应内容,默认为 false
* @returns Promise<any> - 请求响应数据
*
* @example
* ```typescript
* const data = await request('conversations', { method: 'GET' });
* ```
*/
export const request = (url: string, options = {}, needAllResponseContent = false) => {
return baseFetch(url, { ...baseOptions, ...options }, needAllResponseContent);
};
// GET 请求
/**
* 发送 GET 请求
*
* @param url - 请求 URL 路径
* @param options - 额外的请求配置选项,默认为空对象
* @returns Promise<any> - 请求响应数据
*
* @example
* ```typescript
* const conversations = await get('conversations');
* ```
*/
export const get = (url: string, options = {}) => {
return request(url, { ...options, method: 'GET' });
};
// POST 请求
/**
* 发送 POST 请求
*
* @param url - 请求 URL 路径
* @param options - 额外的请求配置选项,默认为空对象
* @returns Promise<any> - 请求响应数据
*
* @example
* ```typescript
* const result = await post('chat-messages', {
* body: { query: 'Hello' }
* });
* ```
*/
export const post = (url: string, options = {}) => {
return request(url, { ...options, method: 'POST' });
};
// PUT 请求
/**
* 发送 PUT 请求
*
* @param url - 请求 URL 路径
* @param options - 额外的请求配置选项,默认为空对象
* @returns Promise<any> - 请求响应数据
*
* @example
* ```typescript
* const result = await put('conversations/123', {
* body: { name: '新名称' }
* });
* ```
*/
export const put = (url: string, options = {}) => {
return request(url, { ...options, method: 'PUT' });
};
// DELETE 请求
/**
* 发送 DELETE 请求
*
* @param url - 请求 URL 路径
* @param options - 额外的请求配置选项,默认为空对象
* @returns Promise<any> - 请求响应数据
*
* @example
* ```typescript
* await del('conversations/123');
* ```
*/
export const del = (url: string, options = {}) => {
return request(url, { ...options, method: 'DELETE' });
};
// 发送聊天消息
/**
* 发送聊天消息
*
* 向 Dify API 发送聊天消息并处理流式响应:
* - 自动设置流式响应模式
* - 支持文件附件
* - 支持会话输入参数
* - 处理各种类型的响应事件
*
* @param body - 消息请求体
* @param body.query - 用户的问题文本
* @param body.conversation_id - 会话 ID(可选,用于继续现有会话)
* @param body.files - 附件文件列表(可选)
* @param body.inputs - 会话输入参数(可选)
* @param callbacks - 事件回调函数集合
* @param callbacks.onData - 处理消息数据的回调,必需
* @param callbacks.onCompleted - 流式响应完成时的回调,必需
* @param callbacks.onFile - 处理文件的回调(可选)
* @param callbacks.onThought - 处理思考过程的回调(可选)
* @param callbacks.onMessageEnd - 处理消息结束的回调(可选)
* @param callbacks.onMessageReplace - 处理消息替换的回调(可选)
* @param callbacks.onError - 处理错误的回调(可选)
* @param callbacks.getAbortController - 获取中止控制器的回调(可选)
* @param callbacks.onWorkflowStarted - 处理工作流开始的回调(可选)
* @param callbacks.onNodeStarted - 处理节点开始的回调(可选)
* @param callbacks.onNodeFinished - 处理节点完成的回调(可选)
* @param callbacks.onWorkflowFinished - 处理工作流完成的回调(可选)
* @returns Promise<void> - 异步操作完成
*
* @example
* ```typescript
* await sendChatMessage({
* query: '你好,请介绍一下自己',
* conversation_id: 'conv-123'
* }, {
* onData: (message, isFirst, info) => {
* console.log('收到消息:', message);
* updateChatUI(message, info.messageId);
* },
* onCompleted: (hasError) => {
* console.log('对话完成', hasError ? '有错误' : '成功');
* setLoading(false);
* },
* onThought: (thought) => {
* console.log('AI思考:', thought.thought);
* },
* onError: (error) => {
* console.error('发送失败:', error);
* showErrorMessage(error);
* }
* });
* ```
*/
export const sendChatMessage = async (
body: Record<string, any>,
{