| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- /*
- Copyright (C) 2025 QuantumNous
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
- For commercial licensing, please contact support@quantumnous.com
- */
- import { useState, useCallback, useRef, useEffect } from 'react';
- import { useTranslation } from 'react-i18next';
- import {
- DEFAULT_MESSAGES,
- getDefaultMessages,
- DEFAULT_CONFIG,
- DEBUG_TABS,
- MESSAGE_STATUS,
- } from '../../constants/playground.constants';
- import {
- loadConfig,
- saveConfig,
- loadMessages,
- saveMessages,
- } from '../../components/playground/configStorage';
- import { processIncompleteThinkTags } from '../../helpers';
- export const usePlaygroundState = () => {
- const { t } = useTranslation();
- // 使用惰性初始化,确保只在组件首次挂载时加载配置和消息
- const [savedConfig] = useState(() => loadConfig());
- const [initialMessages] = useState(() => {
- const loaded = loadMessages();
- // 检查是否是旧的中文默认消息,如果是则清除
- if (
- loaded &&
- loaded.length === 2 &&
- loaded[0].id === '2' &&
- loaded[1].id === '3'
- ) {
- const hasOldChinese =
- loaded[0].content === '你好' ||
- loaded[1].content === '你好,请问有什么可以帮助您的吗?' ||
- loaded[1].content === '你好!很高兴见到你。有什么我可以帮助你的吗?';
- if (hasOldChinese) {
- // 清除旧的默认消息
- localStorage.removeItem('playground_messages');
- return null;
- }
- }
- return loaded;
- });
- // 基础配置状态
- const [inputs, setInputs] = useState(
- savedConfig.inputs || DEFAULT_CONFIG.inputs,
- );
- const [parameterEnabled, setParameterEnabled] = useState(
- savedConfig.parameterEnabled || DEFAULT_CONFIG.parameterEnabled,
- );
- const [showDebugPanel, setShowDebugPanel] = useState(
- savedConfig.showDebugPanel || DEFAULT_CONFIG.showDebugPanel,
- );
- const [customRequestMode, setCustomRequestMode] = useState(
- savedConfig.customRequestMode || DEFAULT_CONFIG.customRequestMode,
- );
- const [customRequestBody, setCustomRequestBody] = useState(
- savedConfig.customRequestBody || DEFAULT_CONFIG.customRequestBody,
- );
- // UI状态
- const [showSettings, setShowSettings] = useState(false);
- const [models, setModels] = useState([]);
- const [groups, setGroups] = useState([]);
- const [status, setStatus] = useState({});
- // 消息相关状态 - 使用加载的消息或默认消息初始化
- const [message, setMessage] = useState(
- () => initialMessages || getDefaultMessages(t),
- );
- // 当语言改变时,如果是默认消息则更新
- useEffect(() => {
- // 只在没有保存的消息时才更新默认消息
- if (!initialMessages) {
- setMessage(getDefaultMessages(t));
- }
- }, [t, initialMessages]); // 当语言改变时
- // 调试状态
- const [debugData, setDebugData] = useState({
- request: null,
- response: null,
- timestamp: null,
- previewRequest: null,
- previewTimestamp: null,
- });
- const [activeDebugTab, setActiveDebugTab] = useState(DEBUG_TABS.PREVIEW);
- const [previewPayload, setPreviewPayload] = useState(null);
- // 编辑状态
- const [editingMessageId, setEditingMessageId] = useState(null);
- const [editValue, setEditValue] = useState('');
- // Refs
- const sseSourceRef = useRef(null);
- const chatRef = useRef(null);
- const saveConfigTimeoutRef = useRef(null);
- const saveMessagesTimeoutRef = useRef(null);
- // 配置更新函数
- const handleInputChange = useCallback((name, value) => {
- setInputs((prev) => ({ ...prev, [name]: value }));
- }, []);
- const handleParameterToggle = useCallback((paramName) => {
- setParameterEnabled((prev) => ({
- ...prev,
- [paramName]: !prev[paramName],
- }));
- }, []);
- // 消息保存函数 - 改为立即保存,可以接受参数
- const saveMessagesImmediately = useCallback(
- (messagesToSave) => {
- // 如果提供了参数,使用参数;否则使用当前状态
- saveMessages(messagesToSave || message);
- },
- [message],
- );
- // 配置保存
- const debouncedSaveConfig = useCallback(() => {
- if (saveConfigTimeoutRef.current) {
- clearTimeout(saveConfigTimeoutRef.current);
- }
- saveConfigTimeoutRef.current = setTimeout(() => {
- const configToSave = {
- inputs,
- parameterEnabled,
- showDebugPanel,
- customRequestMode,
- customRequestBody,
- };
- saveConfig(configToSave);
- }, 1000);
- }, [
- inputs,
- parameterEnabled,
- showDebugPanel,
- customRequestMode,
- customRequestBody,
- ]);
- // 配置导入/重置
- const handleConfigImport = useCallback((importedConfig) => {
- if (importedConfig.inputs) {
- setInputs((prev) => ({ ...prev, ...importedConfig.inputs }));
- }
- if (importedConfig.parameterEnabled) {
- setParameterEnabled((prev) => ({
- ...prev,
- ...importedConfig.parameterEnabled,
- }));
- }
- if (typeof importedConfig.showDebugPanel === 'boolean') {
- setShowDebugPanel(importedConfig.showDebugPanel);
- }
- if (importedConfig.customRequestMode) {
- setCustomRequestMode(importedConfig.customRequestMode);
- }
- if (importedConfig.customRequestBody) {
- setCustomRequestBody(importedConfig.customRequestBody);
- }
- // 如果导入的配置包含消息,也恢复消息
- if (importedConfig.messages && Array.isArray(importedConfig.messages)) {
- setMessage(importedConfig.messages);
- }
- }, []);
- const handleConfigReset = useCallback((options = {}) => {
- const { resetMessages = false } = options;
- setInputs(DEFAULT_CONFIG.inputs);
- setParameterEnabled(DEFAULT_CONFIG.parameterEnabled);
- setShowDebugPanel(DEFAULT_CONFIG.showDebugPanel);
- setCustomRequestMode(DEFAULT_CONFIG.customRequestMode);
- setCustomRequestBody(DEFAULT_CONFIG.customRequestBody);
- // 只有在明确指定时才重置消息
- if (resetMessages) {
- setMessage([]);
- setTimeout(() => {
- setMessage(getDefaultMessages(t));
- }, 0);
- }
- }, []);
- // 清理定时器
- useEffect(() => {
- return () => {
- if (saveConfigTimeoutRef.current) {
- clearTimeout(saveConfigTimeoutRef.current);
- }
- };
- }, []);
- // 页面首次加载时,若最后一条消息仍处于 LOADING/INCOMPLETE 状态,自动修复
- useEffect(() => {
- if (!Array.isArray(message) || message.length === 0) return;
- const lastMsg = message[message.length - 1];
- if (
- lastMsg.status === MESSAGE_STATUS.LOADING ||
- lastMsg.status === MESSAGE_STATUS.INCOMPLETE
- ) {
- const processed = processIncompleteThinkTags(
- lastMsg.content || '',
- lastMsg.reasoningContent || '',
- );
- const fixedLastMsg = {
- ...lastMsg,
- status: MESSAGE_STATUS.COMPLETE,
- content: processed.content,
- reasoningContent: processed.reasoningContent || null,
- isThinkingComplete: true,
- };
- const updatedMessages = [...message.slice(0, -1), fixedLastMsg];
- setMessage(updatedMessages);
- // 保存修复后的消息列表
- setTimeout(() => saveMessagesImmediately(updatedMessages), 0);
- }
- }, []);
- return {
- // 配置状态
- inputs,
- parameterEnabled,
- showDebugPanel,
- customRequestMode,
- customRequestBody,
- // UI状态
- showSettings,
- models,
- groups,
- status,
- // 消息状态
- message,
- // 调试状态
- debugData,
- activeDebugTab,
- previewPayload,
- // 编辑状态
- editingMessageId,
- editValue,
- // Refs
- sseSourceRef,
- chatRef,
- saveConfigTimeoutRef,
- // 更新函数
- setInputs,
- setParameterEnabled,
- setShowDebugPanel,
- setCustomRequestMode,
- setCustomRequestBody,
- setShowSettings,
- setModels,
- setGroups,
- setStatus,
- setMessage,
- setDebugData,
- setActiveDebugTab,
- setPreviewPayload,
- setEditingMessageId,
- setEditValue,
- // 处理函数
- handleInputChange,
- handleParameterToggle,
- debouncedSaveConfig,
- saveMessagesImmediately,
- handleConfigImport,
- handleConfigReset,
- };
- };
|