| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { |
| getUserIdFromLocalStorage, |
| showError, |
| formatMessageForAPI, |
| isValidMessage, |
| } from './utils'; |
| import axios from 'axios'; |
| import { MESSAGE_ROLES } from '../constants/playground.constants'; |
|
|
| export let API = axios.create({ |
| baseURL: import.meta.env.VITE_REACT_APP_SERVER_URL |
| ? import.meta.env.VITE_REACT_APP_SERVER_URL |
| : '', |
| headers: { |
| 'New-API-User': getUserIdFromLocalStorage(), |
| 'Cache-Control': 'no-store', |
| }, |
| }); |
|
|
| function patchAPIInstance(instance) { |
| const originalGet = instance.get.bind(instance); |
| const inFlightGetRequests = new Map(); |
|
|
| const genKey = (url, config = {}) => { |
| const params = config.params ? JSON.stringify(config.params) : '{}'; |
| return `${url}?${params}`; |
| }; |
|
|
| instance.get = (url, config = {}) => { |
| if (config?.disableDuplicate) { |
| return originalGet(url, config); |
| } |
|
|
| const key = genKey(url, config); |
| if (inFlightGetRequests.has(key)) { |
| return inFlightGetRequests.get(key); |
| } |
|
|
| const reqPromise = originalGet(url, config).finally(() => { |
| inFlightGetRequests.delete(key); |
| }); |
|
|
| inFlightGetRequests.set(key, reqPromise); |
| return reqPromise; |
| }; |
| } |
|
|
| patchAPIInstance(API); |
|
|
| export function updateAPI() { |
| API = axios.create({ |
| baseURL: import.meta.env.VITE_REACT_APP_SERVER_URL |
| ? import.meta.env.VITE_REACT_APP_SERVER_URL |
| : '', |
| headers: { |
| 'New-API-User': getUserIdFromLocalStorage(), |
| 'Cache-Control': 'no-store', |
| }, |
| }); |
|
|
| patchAPIInstance(API); |
| } |
|
|
| API.interceptors.response.use( |
| (response) => response, |
| (error) => { |
| |
| if (error.config && error.config.skipErrorHandler) { |
| return Promise.reject(error); |
| } |
| showError(error); |
| return Promise.reject(error); |
| }, |
| ); |
|
|
| |
|
|
| |
| export const buildApiPayload = ( |
| messages, |
| systemPrompt, |
| inputs, |
| parameterEnabled, |
| ) => { |
| const processedMessages = messages |
| .filter(isValidMessage) |
| .map(formatMessageForAPI) |
| .filter(Boolean); |
|
|
| |
| if (systemPrompt && systemPrompt.trim()) { |
| processedMessages.unshift({ |
| role: MESSAGE_ROLES.SYSTEM, |
| content: systemPrompt.trim(), |
| }); |
| } |
|
|
| const payload = { |
| model: inputs.model, |
| group: inputs.group, |
| messages: processedMessages, |
| stream: inputs.stream, |
| }; |
|
|
| |
| const parameterMappings = { |
| temperature: 'temperature', |
| top_p: 'top_p', |
| max_tokens: 'max_tokens', |
| frequency_penalty: 'frequency_penalty', |
| presence_penalty: 'presence_penalty', |
| seed: 'seed', |
| }; |
|
|
| Object.entries(parameterMappings).forEach(([key, param]) => { |
| const enabled = parameterEnabled[key]; |
| const value = inputs[param]; |
| const hasValue = value !== undefined && value !== null; |
|
|
| if (enabled && hasValue) { |
| payload[param] = value; |
| } |
| }); |
|
|
| return payload; |
| }; |
|
|
| |
| export const handleApiError = (error, response = null) => { |
| const errorInfo = { |
| error: error.message || '未知错误', |
| timestamp: new Date().toISOString(), |
| stack: error.stack, |
| }; |
|
|
| if (response) { |
| errorInfo.status = response.status; |
| errorInfo.statusText = response.statusText; |
| } |
|
|
| if (error.message.includes('HTTP error')) { |
| errorInfo.details = '服务器返回了错误状态码'; |
| } else if (error.message.includes('Failed to fetch')) { |
| errorInfo.details = '网络连接失败或服务器无响应'; |
| } |
|
|
| return errorInfo; |
| }; |
|
|
| |
| export const processModelsData = (data, currentModel) => { |
| const modelOptions = data.map((model) => ({ |
| label: model, |
| value: model, |
| })); |
|
|
| const hasCurrentModel = modelOptions.some( |
| (option) => option.value === currentModel, |
| ); |
| const selectedModel = |
| hasCurrentModel && modelOptions.length > 0 |
| ? currentModel |
| : modelOptions[0]?.value; |
|
|
| return { modelOptions, selectedModel }; |
| }; |
|
|
| |
| export const processGroupsData = (data, userGroup) => { |
| let groupOptions = Object.entries(data).map(([group, info]) => ({ |
| label: |
| info.desc.length > 20 ? info.desc.substring(0, 20) + '...' : info.desc, |
| value: group, |
| ratio: info.ratio, |
| fullLabel: info.desc, |
| })); |
|
|
| if (groupOptions.length === 0) { |
| groupOptions = [ |
| { |
| label: '用户分组', |
| value: '', |
| ratio: 1, |
| }, |
| ]; |
| } else if (userGroup) { |
| const userGroupIndex = groupOptions.findIndex((g) => g.value === userGroup); |
| if (userGroupIndex > -1) { |
| const userGroupOption = groupOptions.splice(userGroupIndex, 1)[0]; |
| groupOptions.unshift(userGroupOption); |
| } |
| } |
|
|
| return groupOptions; |
| }; |
|
|
| |
|
|
| export async function getOAuthState() { |
| let path = '/api/oauth/state'; |
| let affCode = localStorage.getItem('aff'); |
| if (affCode && affCode.length > 0) { |
| path += `?aff=${affCode}`; |
| } |
| const res = await API.get(path); |
| const { success, message, data } = res.data; |
| if (success) { |
| return data; |
| } else { |
| showError(message); |
| return ''; |
| } |
| } |
|
|
| export async function onDiscordOAuthClicked(client_id) { |
| const state = await getOAuthState(); |
| if (!state) return; |
| const redirect_uri = `${window.location.origin}/oauth/discord`; |
| const response_type = 'code'; |
| const scope = 'identify+openid'; |
| window.open( |
| `https://discord.com/oauth2/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope}&state=${state}`, |
| ); |
| } |
|
|
| export async function onOIDCClicked(auth_url, client_id, openInNewTab = false) { |
| const state = await getOAuthState(); |
| if (!state) return; |
| const url = new URL(auth_url); |
| url.searchParams.set('client_id', client_id); |
| url.searchParams.set('redirect_uri', `${window.location.origin}/oauth/oidc`); |
| url.searchParams.set('response_type', 'code'); |
| url.searchParams.set('scope', 'openid profile email'); |
| url.searchParams.set('state', state); |
| if (openInNewTab) { |
| window.open(url.toString(), '_blank'); |
| } else { |
| window.location.href = url.toString(); |
| } |
| } |
|
|
| export async function onGitHubOAuthClicked(github_client_id) { |
| const state = await getOAuthState(); |
| if (!state) return; |
| window.open( |
| `https://github.com/login/oauth/authorize?client_id=${github_client_id}&state=${state}&scope=user:email`, |
| ); |
| } |
|
|
| export async function onLinuxDOOAuthClicked(linuxdo_client_id) { |
| const state = await getOAuthState(); |
| if (!state) return; |
| window.open( |
| `https://connect.linux.do/oauth2/authorize?response_type=code&client_id=${linuxdo_client_id}&state=${state}`, |
| ); |
| } |
|
|
| let channelModels = undefined; |
| export async function loadChannelModels() { |
| const res = await API.get('/api/models'); |
| const { success, data } = res.data; |
| if (!success) { |
| return; |
| } |
| channelModels = data; |
| localStorage.setItem('channel_models', JSON.stringify(data)); |
| } |
|
|
| export function getChannelModels(type) { |
| if (channelModels !== undefined && type in channelModels) { |
| if (!channelModels[type]) { |
| return []; |
| } |
| return channelModels[type]; |
| } |
| let models = localStorage.getItem('channel_models'); |
| if (!models) { |
| return []; |
| } |
| channelModels = JSON.parse(models); |
| if (type in channelModels) { |
| return channelModels[type]; |
| } |
| return []; |
| } |
|
|