api.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import { getUserIdFromLocalStorage, showError, formatMessageForAPI, isValidMessage } from './utils';
  2. import axios from 'axios';
  3. import { MESSAGE_ROLES } from '../constants/playground.constants';
  4. export let API = axios.create({
  5. baseURL: import.meta.env.VITE_REACT_APP_SERVER_URL
  6. ? import.meta.env.VITE_REACT_APP_SERVER_URL
  7. : '',
  8. headers: {
  9. 'New-API-User': getUserIdFromLocalStorage(),
  10. 'Cache-Control': 'no-store',
  11. },
  12. });
  13. function patchAPIInstance(instance) {
  14. const originalGet = instance.get.bind(instance);
  15. const inFlightGetRequests = new Map();
  16. const genKey = (url, config = {}) => {
  17. const params = config.params ? JSON.stringify(config.params) : '{}';
  18. return `${url}?${params}`;
  19. };
  20. instance.get = (url, config = {}) => {
  21. if (config?.disableDuplicate) {
  22. return originalGet(url, config);
  23. }
  24. const key = genKey(url, config);
  25. if (inFlightGetRequests.has(key)) {
  26. return inFlightGetRequests.get(key);
  27. }
  28. const reqPromise = originalGet(url, config).finally(() => {
  29. inFlightGetRequests.delete(key);
  30. });
  31. inFlightGetRequests.set(key, reqPromise);
  32. return reqPromise;
  33. };
  34. }
  35. patchAPIInstance(API);
  36. export function updateAPI() {
  37. API = axios.create({
  38. baseURL: import.meta.env.VITE_REACT_APP_SERVER_URL
  39. ? import.meta.env.VITE_REACT_APP_SERVER_URL
  40. : '',
  41. headers: {
  42. 'New-API-User': getUserIdFromLocalStorage(),
  43. 'Cache-Control': 'no-store',
  44. },
  45. });
  46. patchAPIInstance(API);
  47. }
  48. API.interceptors.response.use(
  49. (response) => response,
  50. (error) => {
  51. // 如果请求配置中显式要求跳过全局错误处理,则不弹出默认错误提示
  52. if (error.config && error.config.skipErrorHandler) {
  53. return Promise.reject(error);
  54. }
  55. showError(error);
  56. return Promise.reject(error);
  57. },
  58. );
  59. // playground
  60. // 构建API请求负载
  61. export const buildApiPayload = (messages, systemPrompt, inputs, parameterEnabled) => {
  62. const processedMessages = messages
  63. .filter(isValidMessage)
  64. .map(formatMessageForAPI)
  65. .filter(Boolean);
  66. // 如果有系统提示,插入到消息开头
  67. if (systemPrompt && systemPrompt.trim()) {
  68. processedMessages.unshift({
  69. role: MESSAGE_ROLES.SYSTEM,
  70. content: systemPrompt.trim()
  71. });
  72. }
  73. const payload = {
  74. model: inputs.model,
  75. group: inputs.group,
  76. messages: processedMessages,
  77. group: inputs.group,
  78. stream: inputs.stream,
  79. };
  80. // 添加启用的参数
  81. const parameterMappings = {
  82. temperature: 'temperature',
  83. top_p: 'top_p',
  84. max_tokens: 'max_tokens',
  85. frequency_penalty: 'frequency_penalty',
  86. presence_penalty: 'presence_penalty',
  87. seed: 'seed'
  88. };
  89. Object.entries(parameterMappings).forEach(([key, param]) => {
  90. if (parameterEnabled[key] && inputs[param] !== undefined && inputs[param] !== null) {
  91. payload[param] = inputs[param];
  92. }
  93. });
  94. return payload;
  95. };
  96. // 处理API错误响应
  97. export const handleApiError = (error, response = null) => {
  98. const errorInfo = {
  99. error: error.message || '未知错误',
  100. timestamp: new Date().toISOString(),
  101. stack: error.stack
  102. };
  103. if (response) {
  104. errorInfo.status = response.status;
  105. errorInfo.statusText = response.statusText;
  106. }
  107. if (error.message.includes('HTTP error')) {
  108. errorInfo.details = '服务器返回了错误状态码';
  109. } else if (error.message.includes('Failed to fetch')) {
  110. errorInfo.details = '网络连接失败或服务器无响应';
  111. }
  112. return errorInfo;
  113. };
  114. // 处理模型数据
  115. export const processModelsData = (data, currentModel) => {
  116. const modelOptions = data.map(model => ({
  117. label: model,
  118. value: model,
  119. }));
  120. const hasCurrentModel = modelOptions.some(option => option.value === currentModel);
  121. const selectedModel = hasCurrentModel && modelOptions.length > 0
  122. ? currentModel
  123. : modelOptions[0]?.value;
  124. return { modelOptions, selectedModel };
  125. };
  126. // 处理分组数据
  127. export const processGroupsData = (data, userGroup) => {
  128. let groupOptions = Object.entries(data).map(([group, info]) => ({
  129. label: info.desc.length > 20 ? info.desc.substring(0, 20) + '...' : info.desc,
  130. value: group,
  131. ratio: info.ratio,
  132. fullLabel: info.desc,
  133. }));
  134. if (groupOptions.length === 0) {
  135. groupOptions = [{
  136. label: '用户分组',
  137. value: '',
  138. ratio: 1,
  139. }];
  140. } else if (userGroup) {
  141. const userGroupIndex = groupOptions.findIndex(g => g.value === userGroup);
  142. if (userGroupIndex > -1) {
  143. const userGroupOption = groupOptions.splice(userGroupIndex, 1)[0];
  144. groupOptions.unshift(userGroupOption);
  145. }
  146. }
  147. return groupOptions;
  148. };
  149. // 原来components中的utils.js
  150. export async function getOAuthState() {
  151. let path = '/api/oauth/state';
  152. let affCode = localStorage.getItem('aff');
  153. if (affCode && affCode.length > 0) {
  154. path += `?aff=${affCode}`;
  155. }
  156. const res = await API.get(path);
  157. const { success, message, data } = res.data;
  158. if (success) {
  159. return data;
  160. } else {
  161. showError(message);
  162. return '';
  163. }
  164. }
  165. export async function onOIDCClicked(auth_url, client_id, openInNewTab = false) {
  166. const state = await getOAuthState();
  167. if (!state) return;
  168. const redirect_uri = `${window.location.origin}/oauth/oidc`;
  169. const response_type = 'code';
  170. const scope = 'openid profile email';
  171. const url = `${auth_url}?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope}&state=${state}`;
  172. if (openInNewTab) {
  173. window.open(url);
  174. } else {
  175. window.location.href = url;
  176. }
  177. }
  178. export async function onGitHubOAuthClicked(github_client_id) {
  179. const state = await getOAuthState();
  180. if (!state) return;
  181. window.open(
  182. `https://github.com/login/oauth/authorize?client_id=${github_client_id}&state=${state}&scope=user:email`,
  183. );
  184. }
  185. export async function onLinuxDOOAuthClicked(linuxdo_client_id) {
  186. const state = await getOAuthState();
  187. if (!state) return;
  188. window.open(
  189. `https://connect.linux.do/oauth2/authorize?response_type=code&client_id=${linuxdo_client_id}&state=${state}`,
  190. );
  191. }
  192. let channelModels = undefined;
  193. export async function loadChannelModels() {
  194. const res = await API.get('/api/models');
  195. const { success, data } = res.data;
  196. if (!success) {
  197. return;
  198. }
  199. channelModels = data;
  200. localStorage.setItem('channel_models', JSON.stringify(data));
  201. }
  202. export function getChannelModels(type) {
  203. if (channelModels !== undefined && type in channelModels) {
  204. if (!channelModels[type]) {
  205. return [];
  206. }
  207. return channelModels[type];
  208. }
  209. let models = localStorage.getItem('channel_models');
  210. if (!models) {
  211. return [];
  212. }
  213. channelModels = JSON.parse(models);
  214. if (type in channelModels) {
  215. return channelModels[type];
  216. }
  217. return [];
  218. }