取消统一转发Dify请求,改为由客户端直接发送
This commit is contained in:
@@ -9,3 +9,4 @@ node_modules
|
|||||||
.history
|
.history
|
||||||
|
|
||||||
logs/
|
logs/
|
||||||
|
docreview-frontend-deploy.tar.gz
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* 提供三个选项卡:评查结果、AI智能分析、文件信息
|
* 提供三个选项卡:评查结果、AI智能分析、文件信息
|
||||||
*/
|
*/
|
||||||
import { ReactNode, useState } from 'react';
|
import { ReactNode, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from '@remix-run/react';
|
||||||
import { loadingBarService } from '~/components/ui/LoadingBar';
|
import { loadingBarService } from '~/components/ui/LoadingBar';
|
||||||
import { DOCUMENT_URL } from "~/api/axios-client";
|
import { DOCUMENT_URL } from "~/api/axios-client";
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ const configs: Record<string, ApiConfig> = {
|
|||||||
// 生产环境
|
// 生产环境
|
||||||
production: {
|
production: {
|
||||||
// postgrest
|
// postgrest
|
||||||
baseUrl: 'http://10.79.97.16:8000',
|
baseUrl: 'http://10.79.97.17:8000',
|
||||||
// minio
|
// minio
|
||||||
documentUrl: 'http://10.76.244.156:9000/docauditai/',
|
documentUrl: 'http://10.76.244.156:9000/docauditai/',
|
||||||
// 文件上传
|
// 文件上传
|
||||||
uploadUrl: 'http://10.79.97.16:8000/admin/documents/upload',
|
uploadUrl: 'http://10.79.97.17:8000/admin/documents/upload',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 备用配置 (可以根据需要添加更多环境)
|
// 备用配置 (可以根据需要添加更多环境)
|
||||||
|
|||||||
+28
-24
@@ -28,40 +28,47 @@ const extractAppId = (appUrl: string): string => {
|
|||||||
return appUrl;
|
return appUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取配置值并添加调试日志
|
// 获取Dify API配置
|
||||||
const getApiUrl = () => {
|
const getDifyApiUrl = () => {
|
||||||
// 在Remix中,我们使用本地API路由作为代理,而不是直接访问Dify API
|
return getEnvVar('NEXT_PUBLIC_API_URL', 'https://api.dify.ai/v1');
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
// 客户端:使用相对路径访问本地API
|
|
||||||
return '/api';
|
|
||||||
} else {
|
|
||||||
// 服务端:也使用相对路径
|
|
||||||
return '/api';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAppId = () => {
|
const getAppId = () => {
|
||||||
const rawAppId = getEnvVar('NEXT_PUBLIC_APP_ID', '');
|
const rawAppId = getEnvVar('NEXT_PUBLIC_APP_ID', '');
|
||||||
const extractedAppId = extractAppId(rawAppId);
|
const extractedAppId = extractAppId(rawAppId);
|
||||||
// console.log('🔧 Chat Config Debug:', {
|
console.log('🔧 Chat Config Debug:', {
|
||||||
// rawAppId,
|
rawAppId,
|
||||||
// extractedAppId,
|
extractedAppId,
|
||||||
// apiUrl: getApiUrl(),
|
difyApiUrl: getDifyApiUrl(),
|
||||||
// hasApiKey: !!getEnvVar('NEXT_PUBLIC_APP_KEY', ''),
|
hasApiKey: !!getEnvVar('NEXT_PUBLIC_APP_KEY', ''),
|
||||||
// difyApiUrl: getEnvVar('NEXT_PUBLIC_API_URL', ''),
|
});
|
||||||
// });
|
|
||||||
return extractedAppId;
|
return extractedAppId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 生成用户ID (模拟服务端逻辑)
|
||||||
|
const generateUserId = () => {
|
||||||
|
const appId = getAppId();
|
||||||
|
// 生成或获取会话ID (可以使用localStorage或其他方式)
|
||||||
|
let sessionId = '';
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
sessionId = localStorage.getItem('dify_session_id') || '';
|
||||||
|
if (!sessionId) {
|
||||||
|
sessionId = 'sess_' + Math.random().toString(36).substring(2, 15);
|
||||||
|
localStorage.setItem('dify_session_id', sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `user_${appId}:${sessionId}`;
|
||||||
|
};
|
||||||
|
|
||||||
// 聊天应用配置
|
// 聊天应用配置
|
||||||
export const CHAT_CONFIG = {
|
export const CHAT_CONFIG = {
|
||||||
// API相关配置 - 使用本地API路由作为代理
|
// API相关配置 - 直接使用Dify API
|
||||||
API_URL: getApiUrl(),
|
API_URL: getDifyApiUrl(),
|
||||||
APP_ID: getAppId(),
|
APP_ID: getAppId(),
|
||||||
API_KEY: getEnvVar('NEXT_PUBLIC_APP_KEY', ''),
|
API_KEY: getEnvVar('NEXT_PUBLIC_APP_KEY', ''),
|
||||||
|
|
||||||
// Dify API 配置(用于服务端)
|
// 用户生成函数
|
||||||
DIFY_API_URL: getEnvVar('NEXT_PUBLIC_API_URL', 'https://api.dify.ai/v1'),
|
generateUserId,
|
||||||
|
|
||||||
// 应用信息
|
// 应用信息
|
||||||
APP_INFO: {
|
APP_INFO: {
|
||||||
@@ -76,9 +83,6 @@ export const CHAT_CONFIG = {
|
|||||||
isShowPrompt: false,
|
isShowPrompt: false,
|
||||||
promptTemplate: 'I want you to act as a javascript console.',
|
promptTemplate: 'I want you to act as a javascript console.',
|
||||||
|
|
||||||
// API相关
|
|
||||||
API_PREFIX: '/api',
|
|
||||||
|
|
||||||
// 本地化
|
// 本地化
|
||||||
LOCALE_COOKIE_NAME: 'locale',
|
LOCALE_COOKIE_NAME: 'locale',
|
||||||
|
|
||||||
|
|||||||
@@ -113,8 +113,6 @@ export default function useConversation() {
|
|||||||
isSetToLocalStorage = true,
|
isSetToLocalStorage = true,
|
||||||
newConversationName = ''
|
newConversationName = ''
|
||||||
) => {
|
) => {
|
||||||
// console.log('🔄 设置当前会话ID:', { id, appId, isSetToLocalStorage });
|
|
||||||
|
|
||||||
doSetCurrConversationId(id);
|
doSetCurrConversationId(id);
|
||||||
|
|
||||||
if (isSetToLocalStorage && id !== '-1') {
|
if (isSetToLocalStorage && id !== '-1') {
|
||||||
@@ -130,18 +128,10 @@ export default function useConversation() {
|
|||||||
|
|
||||||
globalThis.localStorage?.setItem(storageConversationIdKey, JSON.stringify(conversationIdInfo));
|
globalThis.localStorage?.setItem(storageConversationIdKey, JSON.stringify(conversationIdInfo));
|
||||||
|
|
||||||
// console.log('💾 会话ID已保存到localStorage:', {
|
|
||||||
// appUrlKey,
|
|
||||||
// conversationId: id,
|
|
||||||
// fullStorage: conversationIdInfo
|
|
||||||
// });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存会话ID到本地存储失败:', error);
|
console.error('保存会话ID到本地存储失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不进行URL导航,保持单页面应用模式
|
|
||||||
// console.log('✅ 会话切换完成,当前会话ID:', id);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import chatInputStyles from "~/styles/components/chat-with-llm/chat-input.css?ur
|
|||||||
import chatSidebarStyles from "~/styles/components/chat-with-llm/sidebar.css?url";
|
import chatSidebarStyles from "~/styles/components/chat-with-llm/sidebar.css?url";
|
||||||
import chatThoughtProcessStyles from "~/styles/components/chat-with-llm/thought-process.css?url";
|
import chatThoughtProcessStyles from "~/styles/components/chat-with-llm/thought-process.css?url";
|
||||||
import chatMarkdownStyles from "~/styles/components/chat-with-llm/markdown.css?url";
|
import chatMarkdownStyles from "~/styles/components/chat-with-llm/markdown.css?url";
|
||||||
|
// 导入测试文件用于调试
|
||||||
|
import "~/utils/dify-test.client";
|
||||||
|
|
||||||
export function links() {
|
export function links() {
|
||||||
return [
|
return [
|
||||||
@@ -32,6 +34,11 @@ export const meta: MetaFunction = () => {
|
|||||||
/**
|
/**
|
||||||
* 聊天主页面
|
* 聊天主页面
|
||||||
* 实现单页面应用模式,所有会话切换都在同一页面内完成
|
* 实现单页面应用模式,所有会话切换都在同一页面内完成
|
||||||
|
*
|
||||||
|
* 调试说明:
|
||||||
|
* - 打开浏览器开发者工具的控制台
|
||||||
|
* - 输入 window.testDify() 可以测试Dify API连接
|
||||||
|
* - 查看网络选项卡可以监控API请求
|
||||||
*/
|
*/
|
||||||
export default function ChatWithLLMIndex() {
|
export default function ChatWithLLMIndex() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "@remix-run/react";
|
||||||
import { type MetaFunction } from "@remix-run/node";
|
import { type MetaFunction } from "@remix-run/node";
|
||||||
|
|
||||||
export const meta: MetaFunction = () => {
|
export const meta: MetaFunction = () => {
|
||||||
|
|||||||
+250
-176
@@ -6,9 +6,10 @@ import { unicodeToChar } from '../utils/chat-utils';
|
|||||||
const baseOptions = {
|
const baseOptions = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
mode: 'cors' as RequestMode,
|
mode: 'cors' as RequestMode,
|
||||||
credentials: 'include' as RequestCredentials,
|
credentials: 'omit' as RequestCredentials,
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
'Content-Type': ContentType.json,
|
'Content-Type': ContentType.json,
|
||||||
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
||||||
}),
|
}),
|
||||||
redirect: 'follow' as RequestRedirect,
|
redirect: 'follow' as RequestRedirect,
|
||||||
};
|
};
|
||||||
@@ -300,101 +301,71 @@ const handleStream = (
|
|||||||
read();
|
read();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 基础Fetch函数
|
// 基础请求函数
|
||||||
const baseFetch = (url: string, fetchOptions: any, needAllResponseContent: boolean = false) => {
|
const baseFetch = (url: string, fetchOptions: any, needAllResponseContent: boolean = false) => {
|
||||||
const options = Object.assign({}, baseOptions, fetchOptions);
|
const options = Object.assign({}, baseOptions, fetchOptions);
|
||||||
|
|
||||||
// 构建完整URL - 修复重复的/v1问题
|
// 直接构建Dify API URL
|
||||||
// CHAT_CONFIG.API_URL 已经包含了 /v1,所以不需要再添加 API_PREFIX
|
const urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`;
|
||||||
let urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`;
|
|
||||||
|
|
||||||
|
// 确保Authorization头存在
|
||||||
const { method, params, body } = options;
|
if (CHAT_CONFIG.API_KEY && options.headers) {
|
||||||
|
|
||||||
// 处理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}`;
|
options.headers['Authorization'] = `Bearer ${CHAT_CONFIG.API_KEY}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.race([
|
const { body } = options;
|
||||||
new Promise((resolve, reject) => {
|
if (body && typeof body === 'object') {
|
||||||
setTimeout(() => {
|
// 为所有请求添加user参数
|
||||||
reject(new Error('请求超时'));
|
const bodyWithUser = {
|
||||||
}, SSE_TIMEOUT);
|
...body,
|
||||||
}),
|
user: CHAT_CONFIG.generateUserId(),
|
||||||
new Promise((resolve, reject) => {
|
};
|
||||||
fetch(urlWithPrefix, options)
|
options.body = JSON.stringify(bodyWithUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(urlWithPrefix, options)
|
||||||
.then((res: Response) => {
|
.then((res: Response) => {
|
||||||
const resClone = res.clone();
|
if (!res.ok) {
|
||||||
|
console.error('❌ Request failed:', {
|
||||||
// console.log('📥 API Response:', {
|
status: res.status,
|
||||||
// status: res.status,
|
statusText: res.statusText,
|
||||||
// statusText: res.statusText,
|
url: urlWithPrefix
|
||||||
// url: urlWithPrefix
|
});
|
||||||
// });
|
if (res.status === 422) {
|
||||||
|
return res.text().then(text => {
|
||||||
// 错误处理
|
let errorMessage = text;
|
||||||
if (!/^(2|3)\d{2}$/.test(res.status.toString())) {
|
|
||||||
try {
|
try {
|
||||||
const bodyJson = res.json();
|
const data = JSON.parse(text);
|
||||||
switch (res.status) {
|
errorMessage = data.message || data.error || text;
|
||||||
case 401:
|
} catch (e) {
|
||||||
console.error('❌ Invalid token');
|
// 如果不是JSON,使用原始文本
|
||||||
break;
|
}
|
||||||
default:
|
throw new Error(errorMessage);
|
||||||
bodyJson.then((data: any) => {
|
|
||||||
console.error('❌ API Error:', data.message);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
throw new Error(`${res.status}: ${res.statusText}`);
|
||||||
catch (e) {
|
|
||||||
console.error('❌ Response Error:', e);
|
|
||||||
}
|
|
||||||
return Promise.reject(resClone);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理删除API(204状态码)
|
if (needAllResponseContent) {
|
||||||
if (res.status === 204) {
|
return res.text().then(text => {
|
||||||
resolve({ result: 'success' });
|
try {
|
||||||
return;
|
return JSON.parse(text);
|
||||||
|
} catch (e) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回数据
|
const data = res.json();
|
||||||
const contentType = res.headers.get('Content-Type') || '';
|
return data;
|
||||||
const data = contentType.includes('application/octet-stream') ? res.blob() : res.json();
|
|
||||||
|
|
||||||
resolve(needAllResponseContent ? resClone : data);
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('❌ Fetch Error:', err);
|
console.error('❌ Request error:', err.message);
|
||||||
reject(err);
|
throw err;
|
||||||
});
|
});
|
||||||
}),
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// SSE POST 请求
|
// SSE请求处理
|
||||||
export const ssePost = (
|
export const ssePost = (
|
||||||
url: string,
|
url: string,
|
||||||
fetchOptions: any,
|
fetchOptions: any,
|
||||||
@@ -430,10 +401,9 @@ export const ssePost = (
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
}, fetchOptions);
|
}, fetchOptions);
|
||||||
|
|
||||||
// 修复URL构建逻辑,与baseFetch保持一致
|
// 直接构建Dify API URL
|
||||||
const urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`;
|
const urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`;
|
||||||
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
if (getAbortController)
|
if (getAbortController)
|
||||||
getAbortController(controller);
|
getAbortController(controller);
|
||||||
@@ -442,25 +412,28 @@ export const ssePost = (
|
|||||||
...options.headers,
|
...options.headers,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Accept': ContentType.stream,
|
'Accept': ContentType.stream,
|
||||||
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (CHAT_CONFIG.API_KEY) {
|
|
||||||
options.headers['Authorization'] = `Bearer ${CHAT_CONFIG.API_KEY}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.signal = controller.signal;
|
options.signal = controller.signal;
|
||||||
|
|
||||||
const { body } = options;
|
const { body } = options;
|
||||||
if (body && typeof body === 'object')
|
if (body && typeof body === 'object') {
|
||||||
options.body = JSON.stringify(body);
|
// 为SSE请求添加user参数
|
||||||
|
const bodyWithUser = {
|
||||||
|
...body,
|
||||||
|
user: CHAT_CONFIG.generateUserId(),
|
||||||
|
};
|
||||||
|
options.body = JSON.stringify(bodyWithUser);
|
||||||
|
}
|
||||||
|
|
||||||
return fetch(urlWithPrefix, options)
|
return fetch(urlWithPrefix, options)
|
||||||
.then((res: Response) => {
|
.then((res: Response) => {
|
||||||
// console.log('📡 SSE Response:', {
|
console.log('📡 SSE Response:', {
|
||||||
// status: res.status,
|
status: res.status,
|
||||||
// statusText: res.statusText,
|
statusText: res.statusText,
|
||||||
// url: urlWithPrefix
|
url: urlWithPrefix
|
||||||
// });
|
});
|
||||||
|
|
||||||
if (!/^(2|3)\d{2}$/.test(res.status.toString())) {
|
if (!/^(2|3)\d{2}$/.test(res.status.toString())) {
|
||||||
res.json().then((data: any) => {
|
res.json().then((data: any) => {
|
||||||
@@ -491,6 +464,194 @@ export const ssePost = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取会话列表
|
||||||
|
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) => {
|
export const request = (url: string, options = {}, needAllResponseContent = false) => {
|
||||||
return baseFetch(url, { ...baseOptions, ...options }, needAllResponseContent);
|
return baseFetch(url, { ...baseOptions, ...options }, needAllResponseContent);
|
||||||
@@ -567,90 +728,3 @@ export const sendChatMessage = async (
|
|||||||
onNodeFinished
|
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,85 @@
|
|||||||
|
import { CHAT_CONFIG } from '../config/chat';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试Dify API连接
|
||||||
|
* 这个文件可以用来调试和测试前端直接调用Dify API的功能
|
||||||
|
*/
|
||||||
|
export const testDifyConnection = async () => {
|
||||||
|
console.log('🔧 Dify Configuration:', {
|
||||||
|
apiUrl: CHAT_CONFIG.API_URL,
|
||||||
|
appId: CHAT_CONFIG.APP_ID,
|
||||||
|
hasApiKey: !!CHAT_CONFIG.API_KEY,
|
||||||
|
apiKeyPreview: CHAT_CONFIG.API_KEY ? `${CHAT_CONFIG.API_KEY.substring(0, 10)}...` : 'No API Key',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!CHAT_CONFIG.API_URL || !CHAT_CONFIG.APP_ID || !CHAT_CONFIG.API_KEY) {
|
||||||
|
console.error('❌ Dify配置不完整,请检查环境变量');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = CHAT_CONFIG.generateUserId();
|
||||||
|
console.log('👤 Generated User ID:', user);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 测试获取应用参数
|
||||||
|
console.log('📋 测试获取应用参数...');
|
||||||
|
const response = await fetch(`${CHAT_CONFIG.API_URL}/parameters`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('❌ 获取应用参数失败:', response.status, response.statusText);
|
||||||
|
const errorText = await response.text();
|
||||||
|
console.error('错误详情:', errorText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('✅ 成功获取应用参数:', data);
|
||||||
|
|
||||||
|
// 测试获取会话列表
|
||||||
|
console.log('💬 测试获取会话列表...');
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
user,
|
||||||
|
limit: '10',
|
||||||
|
first_id: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const conversationsResponse = await fetch(`${CHAT_CONFIG.API_URL}/conversations?${params}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!conversationsResponse.ok) {
|
||||||
|
console.error('❌ 获取会话列表失败:', conversationsResponse.status, conversationsResponse.statusText);
|
||||||
|
const errorText = await conversationsResponse.text();
|
||||||
|
console.error('错误详情:', errorText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conversationsData = await conversationsResponse.json();
|
||||||
|
console.log('✅ 成功获取会话列表:', conversationsData);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 测试Dify连接时发生错误:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在浏览器控制台中可以调用这个函数进行测试
|
||||||
|
* 使用方法:
|
||||||
|
* 1. 打开浏览器控制台
|
||||||
|
* 2. 输入: window.testDify()
|
||||||
|
* 3. 查看输出结果
|
||||||
|
*/
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
(window as any).testDify = testDifyConnection;
|
||||||
|
}
|
||||||
Generated
+1
@@ -6,6 +6,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "remix_docreview",
|
"name": "remix_docreview",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.6.1",
|
||||||
"@codemirror/lang-javascript": "^6.2.3",
|
"@codemirror/lang-javascript": "^6.2.3",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@react-pdf-viewer/core": "^3.12.0",
|
"@react-pdf-viewer/core": "^3.12.0",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"typecheck": "tsc"
|
"typecheck": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.6.1",
|
||||||
"@codemirror/lang-javascript": "^6.2.3",
|
"@codemirror/lang-javascript": "^6.2.3",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@react-pdf-viewer/core": "^3.12.0",
|
"@react-pdf-viewer/core": "^3.12.0",
|
||||||
|
|||||||
Generated
+12337
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -42,6 +42,6 @@ export default defineConfig({
|
|||||||
// 防止依赖预构建时触发页面刷新导致路由中断
|
// 防止依赖预构建时触发页面刷新导致路由中断
|
||||||
force: false,
|
force: false,
|
||||||
// 预构建这些依赖,避免首次加载时出现重新构建
|
// 预构建这些依赖,避免首次加载时出现重新构建
|
||||||
include: ['react-pdf', 'pdfjs-dist', 'dayjs', '@remix-run/node', 'react-dom', 'axios', 'dayjs/plugin/utc', 'react-router-dom', 'jszip', 'ahooks', 'antd', 'immer', '@ant-design/icons', 'react-markdown', 'remark-math', 'remark-breaks', 'rehype-katex','remark-gfm'],
|
include: ['react-pdf', 'pdfjs-dist', 'dayjs', '@remix-run/node', 'react-dom', 'axios', 'dayjs/plugin/utc', '@remix-run/react', 'jszip', 'ahooks', 'antd', 'immer', '@ant-design/icons', 'react-markdown', 'remark-math', 'remark-breaks', 'rehype-katex', 'remark-gfm'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user