mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-28 00:56:26 +00:00
feat: app module
This commit is contained in:
25
client/src/service/ai/openai.ts
Normal file
25
client/src/service/ai/openai.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Configuration, OpenAIApi } from 'openai';
|
||||
|
||||
export const getSystemOpenAiKey = () => {
|
||||
return process.env.ONEAPI_KEY || '';
|
||||
};
|
||||
|
||||
export const getOpenAIApi = () => {
|
||||
return new OpenAIApi(
|
||||
new Configuration({
|
||||
basePath: process.env.ONEAPI_URL
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/* openai axios config */
|
||||
export const axiosConfig = () => {
|
||||
return {
|
||||
baseURL: process.env.ONEAPI_URL, // 此处仅对非 npm 模块有效
|
||||
httpsAgent: global.httpsAgent,
|
||||
headers: {
|
||||
Authorization: `Bearer ${getSystemOpenAiKey()}`,
|
||||
auth: process.env.OPENAI_BASE_URL_AUTH || ''
|
||||
}
|
||||
};
|
||||
};
|
@@ -1,122 +1,79 @@
|
||||
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { sseResponseEventEnum } from '@/constants/chat';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { parseStreamChunk } from '@/utils/adapt';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { sseResponse } from '../utils/tools';
|
||||
|
||||
interface ConfigType {
|
||||
headers?: { [key: string]: string };
|
||||
hold?: boolean;
|
||||
}
|
||||
interface ResponseDataType {
|
||||
code: number;
|
||||
message: string;
|
||||
data: any;
|
||||
interface Props {
|
||||
res: NextApiResponse; // 用于流转发
|
||||
url: string;
|
||||
data: Record<string, any>;
|
||||
}
|
||||
export const moduleFetch = ({ url, data, res }: Props) =>
|
||||
new Promise<Record<string, any>>(async (resolve, reject) => {
|
||||
try {
|
||||
const baseUrl = `http://localhost:3000/api`;
|
||||
const requestUrl = url.startsWith('/') ? `${baseUrl}${url}` : url;
|
||||
const response = await fetch(requestUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
/**
|
||||
* 请求开始
|
||||
*/
|
||||
function requestStart(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
|
||||
if (config.headers) {
|
||||
config.headers.rootkey = process.env.ROOT_KEY;
|
||||
}
|
||||
if (!response?.body) {
|
||||
throw new Error('Request Error');
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
const responseType = response.headers.get('content-type');
|
||||
if (responseType && responseType.includes('application/json')) {
|
||||
const jsonResponse = await response.json();
|
||||
return resolve(jsonResponse?.data || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功,检查请求头
|
||||
*/
|
||||
function responseSuccess(response: AxiosResponse<ResponseDataType>) {
|
||||
return response;
|
||||
}
|
||||
/**
|
||||
* 响应数据检查
|
||||
*/
|
||||
function checkRes(data: ResponseDataType) {
|
||||
if (data === undefined) {
|
||||
return Promise.reject('服务器异常');
|
||||
} else if (data.code < 200 || data.code >= 400) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return data.data;
|
||||
}
|
||||
const reader = response.body?.getReader();
|
||||
|
||||
/**
|
||||
* 响应错误
|
||||
*/
|
||||
function responseError(err: any) {
|
||||
if (!err) {
|
||||
return Promise.reject({ message: '未知错误' });
|
||||
}
|
||||
if (typeof err === 'string') {
|
||||
return Promise.reject({ message: err });
|
||||
}
|
||||
return Promise.reject(err);
|
||||
}
|
||||
let chatResponse = {};
|
||||
|
||||
/* 创建请求实例 */
|
||||
export const instance = axios.create({
|
||||
timeout: 60000, // 超时时间
|
||||
baseURL: `http://localhost:${process.env.PORT || 3000}/api`,
|
||||
headers: {
|
||||
rootkey: process.env.ROOT_KEY
|
||||
}
|
||||
});
|
||||
const read = async () => {
|
||||
try {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
return resolve(chatResponse);
|
||||
}
|
||||
const chunkResponse = parseStreamChunk(value);
|
||||
|
||||
/* 请求拦截 */
|
||||
instance.interceptors.request.use(requestStart, (err) => Promise.reject(err));
|
||||
/* 响应拦截 */
|
||||
instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err));
|
||||
|
||||
function request(url: string, data: any, config: ConfigType, method: Method): any {
|
||||
/* 去空 */
|
||||
for (const key in data) {
|
||||
if (data[key] === null || data[key] === undefined) {
|
||||
delete data[key];
|
||||
chunkResponse.forEach((item) => {
|
||||
// parse json data
|
||||
const data = (() => {
|
||||
try {
|
||||
return JSON.parse(item.data);
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
if (item.event === sseResponseEventEnum.moduleFetchResponse) {
|
||||
chatResponse = {
|
||||
...chatResponse,
|
||||
...data
|
||||
};
|
||||
} else if (item.event === sseResponseEventEnum.answer && data?.choices?.[0]?.delta) {
|
||||
sseResponse({
|
||||
res,
|
||||
event: sseResponseEventEnum.answer,
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
});
|
||||
read();
|
||||
} catch (err: any) {
|
||||
reject(getErrText(err, '请求异常'));
|
||||
}
|
||||
};
|
||||
read();
|
||||
} catch (err: any) {
|
||||
console.log(err);
|
||||
reject(getErrText(err, '请求异常'));
|
||||
}
|
||||
}
|
||||
|
||||
return instance
|
||||
.request({
|
||||
url,
|
||||
method,
|
||||
data: method === 'GET' ? null : data,
|
||||
params: method === 'GET' ? data : null, // get请求不携带data,params放在url上
|
||||
...config // 用户自定义配置,可以覆盖前面的配置
|
||||
})
|
||||
.then((res) => checkRes(res.data))
|
||||
.catch((err) => responseError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* api请求方式
|
||||
* @param {String} url
|
||||
* @param {Any} params
|
||||
* @param {Object} config
|
||||
* @returns
|
||||
*/
|
||||
export function GET<T = { data: any }>(
|
||||
url: string,
|
||||
params = {},
|
||||
config: ConfigType = {}
|
||||
): Promise<T> {
|
||||
return request(url, params, config, 'GET');
|
||||
}
|
||||
|
||||
export function POST<T = { data: any }>(
|
||||
url: string,
|
||||
data = {},
|
||||
config: ConfigType = {}
|
||||
): Promise<T> {
|
||||
return request(url, data, config, 'POST');
|
||||
}
|
||||
|
||||
export function PUT<T = { data: any }>(
|
||||
url: string,
|
||||
data = {},
|
||||
config: ConfigType = {}
|
||||
): Promise<T> {
|
||||
return request(url, data, config, 'PUT');
|
||||
}
|
||||
|
||||
export function DELETE<T = { data: any }>(url: string, config: ConfigType = {}): Promise<T> {
|
||||
return request(url, {}, config, 'DELETE');
|
||||
}
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { sseResponseEventEnum } from '@/constants/chat';
|
||||
import { NextApiResponse } from 'next';
|
||||
import {
|
||||
openaiError,
|
||||
@@ -6,7 +7,7 @@ import {
|
||||
ERROR_RESPONSE,
|
||||
ERROR_ENUM
|
||||
} from './errorCode';
|
||||
import { clearCookie } from './utils/tools';
|
||||
import { clearCookie, sseResponse } from './utils/tools';
|
||||
|
||||
export interface ResponseType<T = any> {
|
||||
code: number;
|
||||
@@ -61,3 +62,41 @@ export const jsonRes = <T = any>(
|
||||
data: data !== undefined ? data : null
|
||||
});
|
||||
};
|
||||
|
||||
export const sseErrRes = (res: NextApiResponse, error: any) => {
|
||||
const errResponseKey = typeof error === 'string' ? error : error?.message;
|
||||
|
||||
// Specified error
|
||||
if (ERROR_RESPONSE[errResponseKey]) {
|
||||
// login is expired
|
||||
if (errResponseKey === ERROR_ENUM.unAuthorization) {
|
||||
clearCookie(res);
|
||||
}
|
||||
|
||||
return sseResponse({
|
||||
res,
|
||||
event: sseResponseEventEnum.error,
|
||||
data: JSON.stringify(ERROR_RESPONSE[errResponseKey])
|
||||
});
|
||||
}
|
||||
|
||||
let msg = error?.message || '请求错误';
|
||||
if (typeof error === 'string') {
|
||||
msg = error;
|
||||
} else if (proxyError[error?.code]) {
|
||||
msg = '接口连接异常';
|
||||
} else if (error?.response?.data?.error?.message) {
|
||||
msg = error?.response?.data?.error?.message;
|
||||
} else if (openaiAccountError[error?.response?.data?.error?.code]) {
|
||||
msg = openaiAccountError[error?.response?.data?.error?.code];
|
||||
} else if (openaiError[error?.response?.statusText]) {
|
||||
msg = openaiError[error.response.statusText];
|
||||
}
|
||||
console.log(error);
|
||||
|
||||
sseResponse({
|
||||
res,
|
||||
event: sseResponseEventEnum.error,
|
||||
data: JSON.stringify({ message: msg })
|
||||
});
|
||||
};
|
||||
|
@@ -79,7 +79,7 @@ export const sseResponse = ({
|
||||
data
|
||||
}: {
|
||||
res: NextApiResponse;
|
||||
event?: `${sseResponseEventEnum}`;
|
||||
event?: string;
|
||||
data: string;
|
||||
}) => {
|
||||
event && res.write(`event: ${event}\n`);
|
||||
|
Reference in New Issue
Block a user