| | import { Toast } from '@douyinfe/semi-ui'; |
| | import { toastConstants } from '../constants'; |
| | import React from 'react'; |
| | import { toast } from 'react-toastify'; |
| | import { THINK_TAG_REGEX, MESSAGE_ROLES } from '../constants/playground.constants'; |
| |
|
| | const HTMLToastContent = ({ htmlContent }) => { |
| | return <div dangerouslySetInnerHTML={{ __html: htmlContent }} />; |
| | }; |
| | export default HTMLToastContent; |
| | export function isAdmin() { |
| | let user = localStorage.getItem('user'); |
| | if (!user) return false; |
| | user = JSON.parse(user); |
| | return user.role >= 10; |
| | } |
| |
|
| | export function isRoot() { |
| | let user = localStorage.getItem('user'); |
| | if (!user) return false; |
| | user = JSON.parse(user); |
| | return user.role >= 100; |
| | } |
| |
|
| | export function getSystemName() { |
| | let system_name = localStorage.getItem('system_name'); |
| | if (!system_name) return 'New API'; |
| | return system_name; |
| | } |
| |
|
| | export function getLogo() { |
| | let logo = localStorage.getItem('logo'); |
| | if (!logo) return '/logo.png'; |
| | return logo; |
| | } |
| |
|
| | export function getUserIdFromLocalStorage() { |
| | let user = localStorage.getItem('user'); |
| | if (!user) return -1; |
| | user = JSON.parse(user); |
| | return user.id; |
| | } |
| |
|
| | export function getFooterHTML() { |
| | return localStorage.getItem('footer_html'); |
| | } |
| |
|
| | export async function copy(text) { |
| | let okay = true; |
| | try { |
| | await navigator.clipboard.writeText(text); |
| | } catch (e) { |
| | try { |
| | |
| | var _input = window.document.createElement('input'); |
| | _input.value = text; |
| | window.document.body.appendChild(_input); |
| | _input.select(); |
| | window.document.execCommand('Copy'); |
| | window.document.body.removeChild(_input); |
| | } catch (e) { |
| | okay = false; |
| | console.error(e); |
| | } |
| | } |
| | return okay; |
| | } |
| |
|
| | export function isMobile() { |
| | return window.innerWidth <= 600; |
| | } |
| |
|
| | let showErrorOptions = { autoClose: toastConstants.ERROR_TIMEOUT }; |
| | let showWarningOptions = { autoClose: toastConstants.WARNING_TIMEOUT }; |
| | let showSuccessOptions = { autoClose: toastConstants.SUCCESS_TIMEOUT }; |
| | let showInfoOptions = { autoClose: toastConstants.INFO_TIMEOUT }; |
| | let showNoticeOptions = { autoClose: false }; |
| |
|
| | if (isMobile()) { |
| | showErrorOptions.position = 'top-center'; |
| | |
| |
|
| | showSuccessOptions.position = 'top-center'; |
| | |
| |
|
| | showInfoOptions.position = 'top-center'; |
| | |
| |
|
| | showNoticeOptions.position = 'top-center'; |
| | |
| | } |
| |
|
| | export function showError(error) { |
| | console.error(error); |
| | if (error.message) { |
| | if (error.name === 'AxiosError') { |
| | switch (error.response.status) { |
| | case 401: |
| | |
| | localStorage.removeItem('user'); |
| | |
| | window.location.href = '/login?expired=true'; |
| | break; |
| | case 429: |
| | Toast.error('错误:请求次数过多,请稍后再试!'); |
| | break; |
| | case 500: |
| | Toast.error('错误:服务器内部错误,请联系管理员!'); |
| | break; |
| | case 405: |
| | Toast.info('本站仅作演示之用,无服务端!'); |
| | break; |
| | default: |
| | Toast.error('错误:' + error.message); |
| | } |
| | return; |
| | } |
| | Toast.error('错误:' + error.message); |
| | } else { |
| | Toast.error('错误:' + error); |
| | } |
| | } |
| |
|
| | export function showWarning(message) { |
| | Toast.warning(message); |
| | } |
| |
|
| | export function showSuccess(message) { |
| | Toast.success(message); |
| | } |
| |
|
| | export function showInfo(message) { |
| | Toast.info(message); |
| | } |
| |
|
| | export function showNotice(message, isHTML = false) { |
| | if (isHTML) { |
| | toast(<HTMLToastContent htmlContent={message} />, showNoticeOptions); |
| | } else { |
| | Toast.info(message); |
| | } |
| | } |
| |
|
| | export function openPage(url) { |
| | window.open(url); |
| | } |
| |
|
| | export function removeTrailingSlash(url) { |
| | if (!url) return ''; |
| | if (url.endsWith('/')) { |
| | return url.slice(0, -1); |
| | } else { |
| | return url; |
| | } |
| | } |
| |
|
| | export function getTodayStartTimestamp() { |
| | var now = new Date(); |
| | now.setHours(0, 0, 0, 0); |
| | return Math.floor(now.getTime() / 1000); |
| | } |
| |
|
| | export function timestamp2string(timestamp) { |
| | let date = new Date(timestamp * 1000); |
| | let year = date.getFullYear().toString(); |
| | let month = (date.getMonth() + 1).toString(); |
| | let day = date.getDate().toString(); |
| | let hour = date.getHours().toString(); |
| | let minute = date.getMinutes().toString(); |
| | let second = date.getSeconds().toString(); |
| | if (month.length === 1) { |
| | month = '0' + month; |
| | } |
| | if (day.length === 1) { |
| | day = '0' + day; |
| | } |
| | if (hour.length === 1) { |
| | hour = '0' + hour; |
| | } |
| | if (minute.length === 1) { |
| | minute = '0' + minute; |
| | } |
| | if (second.length === 1) { |
| | second = '0' + second; |
| | } |
| | return ( |
| | year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second |
| | ); |
| | } |
| |
|
| | export function timestamp2string1(timestamp, dataExportDefaultTime = 'hour') { |
| | let date = new Date(timestamp * 1000); |
| | |
| | let month = (date.getMonth() + 1).toString(); |
| | let day = date.getDate().toString(); |
| | let hour = date.getHours().toString(); |
| | if (day === '24') { |
| | console.log('timestamp', timestamp); |
| | } |
| | if (month.length === 1) { |
| | month = '0' + month; |
| | } |
| | if (day.length === 1) { |
| | day = '0' + day; |
| | } |
| | if (hour.length === 1) { |
| | hour = '0' + hour; |
| | } |
| | let str = month + '-' + day; |
| | if (dataExportDefaultTime === 'hour') { |
| | str += ' ' + hour + ':00'; |
| | } else if (dataExportDefaultTime === 'week') { |
| | let nextWeek = new Date(timestamp * 1000 + 6 * 24 * 60 * 60 * 1000); |
| | let nextMonth = (nextWeek.getMonth() + 1).toString(); |
| | let nextDay = nextWeek.getDate().toString(); |
| | if (nextMonth.length === 1) { |
| | nextMonth = '0' + nextMonth; |
| | } |
| | if (nextDay.length === 1) { |
| | nextDay = '0' + nextDay; |
| | } |
| | str += ' - ' + nextMonth + '-' + nextDay; |
| | } |
| | return str; |
| | } |
| |
|
| | export function downloadTextAsFile(text, filename) { |
| | let blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); |
| | let url = URL.createObjectURL(blob); |
| | let a = document.createElement('a'); |
| | a.href = url; |
| | a.download = filename; |
| | a.click(); |
| | } |
| |
|
| | export const verifyJSON = (str) => { |
| | try { |
| | JSON.parse(str); |
| | } catch (e) { |
| | return false; |
| | } |
| | return true; |
| | }; |
| |
|
| | export function verifyJSONPromise(value) { |
| | try { |
| | JSON.parse(value); |
| | return Promise.resolve(); |
| | } catch (e) { |
| | return Promise.reject('不是合法的 JSON 字符串'); |
| | } |
| | } |
| |
|
| | export function shouldShowPrompt(id) { |
| | let prompt = localStorage.getItem(`prompt-${id}`); |
| | return !prompt; |
| | } |
| |
|
| | export function setPromptShown(id) { |
| | localStorage.setItem(`prompt-${id}`, 'true'); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | export function compareObjects(oldObject, newObject) { |
| | const changedProperties = []; |
| |
|
| | |
| | for (const key in oldObject) { |
| | if (oldObject.hasOwnProperty(key) && newObject.hasOwnProperty(key)) { |
| | if (oldObject[key] !== newObject[key]) { |
| | changedProperties.push({ |
| | key: key, |
| | oldValue: oldObject[key], |
| | newValue: newObject[key], |
| | }); |
| | } |
| | } |
| | } |
| |
|
| | return changedProperties; |
| | } |
| |
|
| | |
| |
|
| | |
| | let messageId = 4; |
| | export const generateMessageId = () => `${messageId++}`; |
| |
|
| | |
| | export const getTextContent = (message) => { |
| | if (!message || !message.content) return ''; |
| |
|
| | if (Array.isArray(message.content)) { |
| | const textContent = message.content.find(item => item.type === 'text'); |
| | return textContent?.text || ''; |
| | } |
| | return typeof message.content === 'string' ? message.content : ''; |
| | }; |
| |
|
| | |
| | export const processThinkTags = (content, reasoningContent = '') => { |
| | if (!content || !content.includes('<think>')) { |
| | return { content, reasoningContent }; |
| | } |
| |
|
| | const thoughts = []; |
| | const replyParts = []; |
| | let lastIndex = 0; |
| | let match; |
| |
|
| | THINK_TAG_REGEX.lastIndex = 0; |
| | while ((match = THINK_TAG_REGEX.exec(content)) !== null) { |
| | replyParts.push(content.substring(lastIndex, match.index)); |
| | thoughts.push(match[1]); |
| | lastIndex = match.index + match[0].length; |
| | } |
| | replyParts.push(content.substring(lastIndex)); |
| |
|
| | const processedContent = replyParts.join('').replace(/<\/?think>/g, '').trim(); |
| | const thoughtsStr = thoughts.join('\n\n---\n\n'); |
| | const processedReasoningContent = reasoningContent && thoughtsStr |
| | ? `${reasoningContent}\n\n---\n\n${thoughtsStr}` |
| | : reasoningContent || thoughtsStr; |
| |
|
| | return { |
| | content: processedContent, |
| | reasoningContent: processedReasoningContent |
| | }; |
| | }; |
| |
|
| | |
| | export const processIncompleteThinkTags = (content, reasoningContent = '') => { |
| | if (!content) return { content: '', reasoningContent }; |
| |
|
| | const lastOpenThinkIndex = content.lastIndexOf('<think>'); |
| | if (lastOpenThinkIndex === -1) { |
| | return processThinkTags(content, reasoningContent); |
| | } |
| |
|
| | const fragmentAfterLastOpen = content.substring(lastOpenThinkIndex); |
| | if (!fragmentAfterLastOpen.includes('</think>')) { |
| | const unclosedThought = fragmentAfterLastOpen.substring('<think>'.length).trim(); |
| | const cleanContent = content.substring(0, lastOpenThinkIndex); |
| | const processedReasoningContent = unclosedThought |
| | ? reasoningContent ? `${reasoningContent}\n\n---\n\n${unclosedThought}` : unclosedThought |
| | : reasoningContent; |
| |
|
| | return processThinkTags(cleanContent, processedReasoningContent); |
| | } |
| |
|
| | return processThinkTags(content, reasoningContent); |
| | }; |
| |
|
| | |
| | export const buildMessageContent = (textContent, imageUrls = [], imageEnabled = false) => { |
| | if (!textContent && (!imageUrls || imageUrls.length === 0)) { |
| | return ''; |
| | } |
| |
|
| | const validImageUrls = imageUrls.filter(url => url && url.trim() !== ''); |
| |
|
| | if (imageEnabled && validImageUrls.length > 0) { |
| | return [ |
| | { type: 'text', text: textContent || '' }, |
| | ...validImageUrls.map(url => ({ |
| | type: 'image_url', |
| | image_url: { url: url.trim() } |
| | })) |
| | ]; |
| | } |
| |
|
| | return textContent || ''; |
| | }; |
| |
|
| | |
| | export const createMessage = (role, content, options = {}) => ({ |
| | role, |
| | content, |
| | createAt: Date.now(), |
| | id: generateMessageId(), |
| | ...options |
| | }); |
| |
|
| | |
| | export const createLoadingAssistantMessage = () => createMessage( |
| | MESSAGE_ROLES.ASSISTANT, |
| | '', |
| | { |
| | reasoningContent: '', |
| | isReasoningExpanded: true, |
| | isThinkingComplete: false, |
| | hasAutoCollapsed: false, |
| | status: 'loading' |
| | } |
| | ); |
| |
|
| | |
| | export const hasImageContent = (message) => { |
| | return message && |
| | Array.isArray(message.content) && |
| | message.content.some(item => item.type === 'image_url'); |
| | }; |
| |
|
| | |
| | export const formatMessageForAPI = (message) => { |
| | if (!message) return null; |
| |
|
| | return { |
| | role: message.role, |
| | content: message.content |
| | }; |
| | }; |
| |
|
| | |
| | export const isValidMessage = (message) => { |
| | return message && |
| | message.role && |
| | (message.content || message.content === ''); |
| | }; |
| |
|
| | |
| | export const getLastUserMessage = (messages) => { |
| | if (!Array.isArray(messages)) return null; |
| |
|
| | for (let i = messages.length - 1; i >= 0; i--) { |
| | if (messages[i].role === MESSAGE_ROLES.USER) { |
| | return messages[i]; |
| | } |
| | } |
| | return null; |
| | }; |
| |
|
| | |
| | export const getLastAssistantMessage = (messages) => { |
| | if (!Array.isArray(messages)) return null; |
| |
|
| | for (let i = messages.length - 1; i >= 0; i--) { |
| | if (messages[i].role === MESSAGE_ROLES.ASSISTANT) { |
| | return messages[i]; |
| | } |
| | } |
| | return null; |
| | }; |
| |
|