完成世界书、骰子、apiconfig页面处理

This commit is contained in:
2026-04-30 01:35:10 +08:00
parent a3e3711b2b
commit ba9b925c32
4602 changed files with 785225 additions and 23 deletions

View File

@@ -0,0 +1,203 @@
// frontend-react/src/Store/SideBarLeft/ApiConfigSlice.jsx
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
// 定义初始状态
const initialState = {
profiles: [], // 所有配置文件列表
currentProfile: null, // 当前加载的配置文件
activeMap: {}, // 当前激活的配置映射 { category: profileId }
loading: false,
error: null,
notification: {
show: false,
message: '',
type: 'info' // 'success', 'error', 'info'
}
};
// 创建 Store
const useApiConfigStore = create(
devtools(
(set, get) => ({
...initialState,
// Action: 获取所有配置文件列表
fetchProfiles: async () => {
set({ loading: true, error: null });
try {
const response = await fetch('/api/api-config/profiles');
if (!response.ok) {
throw new Error('Failed to fetch profiles');
}
const data = await response.json();
set({ profiles: data, loading: false });
} catch (err) {
set({ error: err.message, loading: false });
get().showNotification('error', `获取配置文件列表失败: ${err.message}`);
}
},
// Action: 获取单个配置文件
fetchProfile: async (profileId) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/api-config/profiles/${profileId}`);
if (!response.ok) {
throw new Error('Failed to fetch profile');
}
const data = await response.json();
set({ currentProfile: data, loading: false });
return data;
} catch (err) {
set({ error: err.message, loading: false });
get().showNotification('error', `获取配置文件失败: ${err.message}`);
throw err;
}
},
// Action: 保存配置文件(批量、增量)
saveProfile: async (profileId, name, apisToSave) => {
set({ loading: true, error: null });
try {
const response = await fetch('/api/api-config/profiles', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
profileId,
name,
apis: apisToSave // 只包含要保存的 API
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || 'Failed to save profile');
}
const result = await response.json();
// 更新本地状态
set(state => {
const existingIndex = state.profiles.findIndex(p => p.id === profileId);
let newProfiles = [...state.profiles];
if (existingIndex >= 0) {
// 更新现有配置文件
newProfiles[existingIndex] = {
id: result.id,
name: result.name
};
} else {
// 添加新配置文件
newProfiles.push({
id: result.id,
name: result.name
});
}
return {
profiles: newProfiles,
currentProfile: result,
loading: false
};
});
get().showNotification('success', '配置文件已保存');
return result;
} catch (err) {
set({ error: err.message, loading: false });
get().showNotification('error', `保存失败: ${err.message}`);
throw err;
}
},
// Action: 删除配置文件
deleteProfile: async (profileId) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/api-config/profiles/${profileId}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error('Failed to delete profile');
}
set(state => ({
profiles: state.profiles.filter(p => p.id !== profileId),
currentProfile: state.currentProfile?.id === profileId ? null : state.currentProfile,
loading: false
}));
get().showNotification('success', '配置文件已删除');
} catch (err) {
set({ error: err.message, loading: false });
get().showNotification('error', `删除失败: ${err.message}`);
throw err;
}
},
// Action: 测试连接并获取模型列表
testConnection: async (apiUrl, apiKey, category) => {
try {
const response = await fetch('/api/api-config/test-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ apiUrl, apiKey, category, model: '' })
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || 'Connection test failed');
}
const data = await response.json();
if (data.success) {
return data.models || [];
} else {
throw new Error(data.message || '获取模型列表失败');
}
} catch (err) {
get().showNotification('error', `连接测试失败: ${err.message}`);
throw err;
}
},
// Action: 设置激活配置
setActiveConfig: async (category, profileId) => {
try {
set(state => ({
activeMap: { ...state.activeMap, [category]: profileId }
}));
get().showNotification('success', '已设置为默认配置');
} catch (err) {
get().showNotification('error', `设置失败: ${err.message}`);
throw err;
}
},
// Action: 显示通知
showNotification: (type, message) => {
set({ notification: { show: true, type, message } });
setTimeout(() => {
set({ notification: { ...get().notification, show: false } });
}, 3000);
},
}),
{
name: 'ApiConfigStore',
partialize: (state) => ({
activeMap: state.activeMap
})
}
)
);
// 导出 Hook
export default useApiConfigStore;

View File

@@ -0,0 +1,356 @@
import { create } from 'zustand';
const usePresetStore = create((set, get) => ({
// 预设选择
selectedPreset: '',
// 核心参数
parameters: {
temperature: 1.0,
frequency_penalty: 0.0,
presence_penalty: 0.0,
top_p: 1.0,
top_k: 0,
max_context: 1000000,
max_tokens: 30000,
max_context_unlocked: false,
stream_openai: true,
seed: -1,
n: 1
},
// 可用的预设列表 - 初始为空,将从后端加载
presets: [],
// 是否正在加载预设列表
isLoadingPresets: false,
// 参数设置折叠状态
isParametersExpanded: true,
// 预设组件列表
promptComponents: [
{
identifier: "dialogueExamples",
name: "Chat Examples",
system_prompt: true,
marker: true,
enabled: true,
role: 0
},
{
identifier: "chatHistory",
name: "Chat History",
system_prompt: true,
marker: true,
enabled: true,
role: 0
},
{
identifier: "worldInfoAfter",
name: "World Info (after)",
system_prompt: true,
marker: true,
enabled: true,
role: 0
},
{
identifier: "worldInfoBefore",
name: "World Info (before)",
system_prompt: true,
marker: true,
enabled: true,
role: 0
},
{
identifier: "charDescription",
name: "Char Description",
system_prompt: true,
marker: true,
enabled: true,
role: 0
},
{
identifier: "charPersonality",
name: "Char Personality",
system_prompt: true,
marker: true,
enabled: true,
role: 0
},
{
identifier: "scenario",
name: "Scenario",
system_prompt: true,
marker: true,
enabled: true,
role: 0
},
{
identifier: "personaDescription",
name: "Persona Description",
system_prompt: true,
marker: true,
enabled: true,
role: 0
}
],
// 从后端加载预设列表
fetchPresets: async () => {
set({ isLoadingPresets: true });
try {
const response = await fetch('/api/presets');
const data = await response.json();
// 转换为预设对象数组
const presetList = data.presets.map(preset => ({
id: preset.name,
name: preset.name,
description: preset.description,
component_count: preset.component_count,
temperature: preset.temperature
}));
set({ presets: presetList, isLoadingPresets: false });
} catch (error) {
console.error('Failed to fetch presets:', error);
set({ isLoadingPresets: false });
}
},
// 设置选中的预设
setSelectedPreset: async (presetId) => {
try {
// 从后端获取预设的完整内容
const response = await fetch(`/api/presets/${presetId}`);
const presetData = await response.json();
// 记录原始数据用于调试
console.log('从后端获取的预设数据:', presetData);
// 提取参数并更新状态,确保所有参数都有默认值
const parameters = {
temperature: presetData.temperature !== undefined ? presetData.temperature : 1.0,
frequency_penalty: presetData.frequency_penalty !== undefined ? presetData.frequency_penalty : 0.0,
presence_penalty: presetData.presence_penalty !== undefined ? presetData.presence_penalty : 0.0,
top_p: presetData.top_p !== undefined ? presetData.top_p : 1.0,
top_k: presetData.top_k !== undefined ? presetData.top_k : 0,
max_context: presetData.openai_max_context !== undefined ? presetData.openai_max_context :
(presetData.max_context !== undefined ? presetData.max_context : 1000000),
max_tokens: presetData.openai_max_tokens !== undefined ? presetData.openai_max_tokens :
(presetData.max_tokens !== undefined ? presetData.max_tokens : 30000),
max_context_unlocked: presetData.max_context_unlocked !== undefined ? presetData.max_context_unlocked : false,
stream_openai: presetData.stream_openai !== undefined ? presetData.stream_openai : true,
seed: presetData.seed !== undefined ? presetData.seed : -1,
n: presetData.n !== undefined ? presetData.n : 1
};
// 记录映射后的参数用于调试
console.log('映射后的参数:', parameters);
// 处理预设组件
let components = [];
if (presetData.prompts && Array.isArray(presetData.prompts)) {
// 获取当前角色的prompt_order添加更严格的检查
const currentOrder = (presetData.prompt_order &&
Array.isArray(presetData.prompt_order) &&
presetData.prompt_order.length > 0 &&
presetData.prompt_order[0] &&
presetData.prompt_order[0].order &&
Array.isArray(presetData.prompt_order[0].order))
? presetData.prompt_order[0].order
: [];
// 根据prompt_order排序组件
components = presetData.prompts.map(prompt => {
const orderItem = currentOrder.find(item => item && item.identifier === prompt.identifier);
return {
...prompt,
enabled: orderItem ? orderItem.enabled : true,
role: prompt.role !== undefined ? prompt.role : (prompt.system_prompt ? 0 : 1)
};
});
// 如果有prompt_order按照它排序
if (currentOrder.length > 0) {
components.sort((a, b) => {
const indexA = currentOrder.findIndex(item => item && item.identifier === a.identifier);
const indexB = currentOrder.findIndex(item => item && item.identifier === b.identifier);
return indexA - indexB;
});
}
}
// 更新状态,确保参数容器展开
set({
selectedPreset: presetId,
parameters,
promptComponents: components,
isParametersExpanded: true // 确保参数容器展开
});
} catch (error) {
console.error('Failed to load preset:', error);
}
},
// 更新参数
updateParameter: ({ name, value }) => set((state) => ({
parameters: { ...state.parameters, [name]: value }
})),
// 添加预设
addPreset: (preset) => set((state) => ({
presets: [...state.presets, preset]
})),
// 保存当前设置为预设
saveCurrentAsPreset: async ({ name }) => {
const state = get();
try {
// 构建预设数据
const presetData = {
...state.parameters,
prompts: state.promptComponents.map(component => ({
identifier: component.identifier,
name: component.name,
content: component.content || '',
role: component.role,
system_prompt: component.system_prompt,
marker: component.marker
})),
prompt_order: [{
order: state.promptComponents.map(component => ({
identifier: component.identifier,
enabled: component.enabled !== false
}))
}]
};
// 发送到后端
const response = await fetch('/api/presets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
preset_name: name,
...presetData
})
});
if (!response.ok) {
throw new Error('Failed to save preset');
}
const result = await response.json();
// 添加到本地预设列表
const newPreset = {
id: name,
name,
description: '',
component_count: state.promptComponents.length,
temperature: state.parameters.temperature
};
set((state) => ({
presets: [...state.presets, newPreset],
selectedPreset: name
}));
return result;
} catch (error) {
console.error('Failed to save preset:', error);
throw error;
}
},
// 编辑预设名称
editPresetName: async (presetId, newName) => {
try {
const response = await fetch(`/api/presets/${presetId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: newName
})
});
if (!response.ok) {
throw new Error('Failed to update preset name');
}
// 更新本地状态
set((state) => ({
presets: state.presets.map(preset =>
preset.id === presetId ? { ...preset, name: newName } : preset
)
}));
return await response.json();
} catch (error) {
console.error('Failed to update preset name:', error);
throw error;
}
},
// 切换参数设置折叠状态
toggleParametersExpanded: () => set((state) => ({
isParametersExpanded: !state.isParametersExpanded
})),
// 设置预设组件列表
setPromptComponents: (components) => set({ promptComponents: components }),
// 更新组件
updateComponent: (index, updatedComponent) => set((state) => {
const newComponents = [...state.promptComponents];
newComponents[index] = { ...newComponents[index], ...updatedComponent };
return { promptComponents: newComponents };
}),
// 切换组件启用状态
toggleComponentEnabled: (index) => set((state) => {
const newComponents = [...state.promptComponents];
newComponents[index] = {
...newComponents[index],
enabled: !newComponents[index].enabled
};
return { promptComponents: newComponents };
}),
// 添加新组件
addComponent: (component) => set((state) => ({
promptComponents: [...state.promptComponents, component]
})),
// 删除组件
removeComponent: (index) => set((state) => {
const newComponents = [...state.promptComponents];
newComponents.splice(index, 1);
return { promptComponents: newComponents };
}),
// 移动组件位置
moveComponent: (fromIndex, toIndex) => set((state) => {
const newComponents = [...state.promptComponents];
const [movedComponent] = newComponents.splice(fromIndex, 1);
newComponents.splice(toIndex, 0, movedComponent);
return { promptComponents: newComponents };
}),
// 获取当前预设的prompt_order
getPromptOrder: () => {
const { promptComponents } = get();
return promptComponents.map(component => ({
identifier: component.identifier,
enabled: component.enabled !== false
}));
}
}));
export default usePresetStore;

View File

@@ -0,0 +1,26 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useSideBarLeftStore = create(
persist(
(set) => ({
activeTab: 'gallery',
tabs: [
{ id: 'gallery', label: '画廊', title: '查看和管理生成的图片' },
{ id: 'character', label: '角色', title: '管理AI角色卡' },
{ id: 'api', label: 'API', title: '配置LLM和生图、向量化API连接' },
{ id: 'presets', label: '预设', title: '管理对话预设和系统提示词' },
{ id: 'worldbook', label: '世界', title: '管理世界观设定和背景知识' }
],
setActiveTab: (tab) => set({ activeTab: tab })
}),
{
name: 'sidebar-left-storage', // localStorage key
partialize: (state) => ({ activeTab: state.activeTab }) // 只保存 activeTab
}
)
);
export default useSideBarLeftStore;

View File

@@ -0,0 +1,688 @@
import { create } from 'zustand';
// LocalStorage 键名
const GLOBAL_WORLDBOOKS_KEY = 'global_worldbooks';
// 辅助函数:从 LocalStorage 加载全局世界书
const loadGlobalWorldBooks = () => {
try {
const stored = localStorage.getItem(GLOBAL_WORLDBOOKS_KEY);
return stored ? JSON.parse(stored) : [];
} catch (error) {
console.error('加载全局世界书失败:', error);
return [];
}
};
// 辅助函数:保存全局世界书到 LocalStorage
const saveGlobalWorldBooks = (globalBooks) => {
try {
localStorage.setItem(GLOBAL_WORLDBOOKS_KEY, JSON.stringify(globalBooks));
} catch (error) {
console.error('保存全局世界书失败:', error);
}
};
// 辅助函数:处理 API 响应
const handleResponse = async (response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || `请求失败: ${response.status}`);
}
return response.json().catch(() => ({}));
};
// 辅助函数:处理文件下载
const handleFileDownload = async (response, filename) => {
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
window.URL.revokeObjectURL(url);
};
// 创建世界书 store
const useWorldBookStore = create((set, get) => ({
// 状态
worldBooks: [], // 世界书列表
globalWorldBooks: loadGlobalWorldBooks(), // 从 LocalStorage 初始化全局世界书列表
currentWorldBook: null, // 当前选中的世界书
currentEntries: [], // 当前世界书的条目列表
currentEntry: null, // 当前选中的条目
loading: false, // 加载状态
error: null, // 错误信息
success: false, // 操作成功状态
message: '', // 成功或错误消息
// Actions
clearError: () => set({ error: null, message: '' }),
clearSuccess: () => set({ success: false, message: '' }),
setCurrentWorldBook: (worldBook) => set({
currentWorldBook: worldBook,
currentEntries: [],
currentEntry: null
}),
setCurrentEntry: (entry) => set({ currentEntry: entry }),
resetCurrentWorldBook: () => set({
currentWorldBook: null,
currentEntries: [],
currentEntry: null
}),
// 异步操作:切换世界书的全局状态
toggleGlobalWorldBook: async (name, isGlobal) => {
set({ loading: true, error: null, success: false });
try {
const formData = new FormData();
formData.append('is_global', isGlobal);
const response = await fetch(`/api/worldbooks/${name}`, {
method: 'PUT',
body: formData
});
const data = await handleResponse(response);
set(state => {
const updatedWorldBooks = state.worldBooks.map(wb =>
wb.name === data.name ? data : wb
);
// 更新全局世界书列表
let updatedGlobalBooks = [...state.globalWorldBooks];
const globalIndex = updatedGlobalBooks.findIndex(wb => wb.name === data.name);
if (isGlobal) {
// 如果是世界书被标记为全局
if (globalIndex === -1) {
// 如果不在全局列表中,添加它
updatedGlobalBooks = [...updatedGlobalBooks, data];
} else {
// 如果已经在全局列表中,更新它
updatedGlobalBooks[globalIndex] = data;
}
} else {
// 如果世界书不再全局,从全局列表中移除
if (globalIndex !== -1) {
updatedGlobalBooks = updatedGlobalBooks.filter(wb => wb.name !== data.name);
}
}
// 保存到 LocalStorage
saveGlobalWorldBooks(updatedGlobalBooks);
return {
loading: false,
worldBooks: updatedWorldBooks,
globalWorldBooks: updatedGlobalBooks,
currentWorldBook: state.currentWorldBook?.name === data.name
? data
: state.currentWorldBook,
success: true,
};
});
return data;
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:获取所有世界书
fetchWorldBooks: async () => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/worldbooks/`);
const data = await handleResponse(response);
// 从 LocalStorage 获取全局世界书列表
const globalBooks = loadGlobalWorldBooks();
set({
loading: false,
worldBooks: data,
globalWorldBooks: globalBooks,
error: null
});
return data;
} catch (error) {
set({
loading: false,
error: error.message
});
throw error;
}
},
// 异步操作:获取指定世界书
fetchWorldBook: async (name) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/worldbooks/${name}`);
const data = await handleResponse(response);
set({
loading: false,
currentWorldBook: data,
error: null
});
return data;
} catch (error) {
set({
loading: false,
error: error.message
});
throw error;
}
},
// 异步操作:创建世界书
createWorldBook: async ({ name, is_global, file }) => {
set({ loading: true, error: null, success: false });
try {
const formData = new FormData();
formData.append('name', name);
if (is_global !== undefined) {
formData.append('is_global', is_global);
}
if (file) {
formData.append('file', file);
}
const response = await fetch(`/api/worldbooks/`, {
method: 'POST',
body: formData
});
const data = await handleResponse(response);
set(state => {
const newWorldBooks = [...state.worldBooks, data];
let newGlobalBooks = [...state.globalWorldBooks];
// 如果是世界书被标记为全局,添加到全局列表
if (data.is_global) {
newGlobalBooks = [...newGlobalBooks, data];
saveGlobalWorldBooks(newGlobalBooks);
}
return {
loading: false,
worldBooks: newWorldBooks,
globalWorldBooks: newGlobalBooks,
currentWorldBook: data,
success: true,
};
});
return data;
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:更新世界书
updateWorldBook: async ({ name, is_global, file }) => {
set({ loading: true, error: null, success: false });
try {
const formData = new FormData();
if (is_global !== undefined) {
formData.append('is_global', is_global);
}
if (file) {
formData.append('file', file);
}
const response = await fetch(`/api/worldbooks/${name}`, {
method: 'PUT',
body: formData
});
const data = await handleResponse(response);
set(state => {
const updatedWorldBooks = state.worldBooks.map(wb =>
wb.name === data.name ? data : wb
);
// 更新全局世界书列表
let updatedGlobalBooks = [...state.globalWorldBooks];
const globalIndex = updatedGlobalBooks.findIndex(wb => wb.name === data.name);
if (data.is_global) {
// 如果是世界书被标记为全局
if (globalIndex === -1) {
// 如果不在全局列表中,添加它
updatedGlobalBooks = [...updatedGlobalBooks, data];
} else {
// 如果已经在全局列表中,更新它
updatedGlobalBooks[globalIndex] = data;
}
} else {
// 如果世界书不再全局,从全局列表中移除
if (globalIndex !== -1) {
updatedGlobalBooks = updatedGlobalBooks.filter(wb => wb.name !== data.name);
}
}
// 保存到 LocalStorage
saveGlobalWorldBooks(updatedGlobalBooks);
return {
loading: false,
worldBooks: updatedWorldBooks,
globalWorldBooks: updatedGlobalBooks,
currentWorldBook: state.currentWorldBook?.name === data.name
? data
: state.currentWorldBook,
success: true,
};
});
return data;
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:删除世界书
deleteWorldBook: async (name) => {
set({ loading: true, error: null, success: false });
try {
const response = await fetch(`/api/worldbooks/${name}`, {
method: 'DELETE'
});
await handleResponse(response);
set(state => {
const filteredWorldBooks = state.worldBooks.filter(wb => wb.name !== name);
const filteredGlobalBooks = state.globalWorldBooks.filter(wb => wb.name !== name);
// 保存到 LocalStorage
saveGlobalWorldBooks(filteredGlobalBooks);
return {
loading: false,
worldBooks: filteredWorldBooks,
globalWorldBooks: filteredGlobalBooks,
currentWorldBook: state.currentWorldBook?.name === name
? null
: state.currentWorldBook,
currentEntries: state.currentWorldBook?.name === name
? []
: state.currentEntries,
currentEntry: state.currentWorldBook?.name === name
? null
: state.currentEntry,
success: true,
};
});
return name;
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:获取世界书的所有条目
fetchWorldBookEntries: async (name) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/worldbooks/${name}/entries`);
const data = await handleResponse(response);
set(state => {
if (state.currentWorldBook?.name === name) {
return {
loading: false,
currentEntries: data,
error: null
};
}
return { loading: false, error: null };
});
return data;
} catch (error) {
set({
loading: false,
error: error.message
});
throw error;
}
},
// 异步操作:获取世界书的指定条目
fetchWorldBookEntry: async (name, uid) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/worldbooks/${name}/entries/${uid}`);
const data = await handleResponse(response);
set(state => {
if (state.currentWorldBook?.name === name) {
return {
loading: false,
currentEntry: data,
error: null
};
}
return { loading: false, error: null };
});
return data;
} catch (error) {
set({
loading: false,
error: error.message
});
throw error;
}
},
// 异步操作:创建世界书条目
createWorldBookEntry: async (name, entryData) => {
set({ loading: true, error: null, success: false });
try {
// 处理触发配置数据
const processedEntryData = { ...entryData };
if (processedEntryData.trigger_config && processedEntryData.trigger_config.triggers) {
// 创建新的触发配置对象
const triggerConfig = {
triggers: {}
};
// 处理每个触发策略
for (const [strategy, triggerInfo] of Object.entries(processedEntryData.trigger_config.triggers)) {
if (Array.isArray(triggerInfo) && triggerInfo.length >= 2) {
triggerConfig.triggers[strategy] = [
triggerInfo[0], // 是否启用
triggerInfo[1] // 配置对象
];
} else {
// 如果格式不正确,设置为不启用
triggerConfig.triggers[strategy] = [false, null];
}
}
processedEntryData.trigger_config = triggerConfig;
}
const response = await fetch(`/api/worldbooks/${name}/entries`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(processedEntryData)
});
const data = await handleResponse(response);
set(state => {
if (state.currentWorldBook?.name === name) {
return {
loading: false,
currentEntries: [...state.currentEntries, data],
success: true,
};
}
return {
loading: false,
success: true,
};
});
return data;
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:更新世界书条目
updateWorldBookEntry: async (name, uid, entryData) => {
set({ loading: true, error: null, success: false });
try {
// 处理触发配置数据
const processedEntryData = { ...entryData };
if (processedEntryData.trigger_config && processedEntryData.trigger_config.triggers) {
// 创建新的触发配置对象
const triggerConfig = {
triggers: {}
};
// 处理每个触发策略
for (const [strategy, triggerInfo] of Object.entries(processedEntryData.trigger_config.triggers)) {
if (Array.isArray(triggerInfo) && triggerInfo.length >= 2) {
triggerConfig.triggers[strategy] = [
triggerInfo[0], // 是否启用
triggerInfo[1] // 配置对象
];
} else {
// 如果格式不正确,设置为不启用
triggerConfig.triggers[strategy] = [false, null];
}
}
processedEntryData.trigger_config = triggerConfig;
}
const response = await fetch(`/api/worldbooks/${name}/entries/${uid}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(processedEntryData)
});
const data = await handleResponse(response);
set(state => {
if (state.currentWorldBook?.name === name) {
const updatedEntries = state.currentEntries.map(entry =>
entry.uid === data.uid ? data : entry
);
return {
loading: false,
currentEntries: updatedEntries,
currentEntry: state.currentEntry?.uid === data.uid
? data
: state.currentEntry,
success: true,
};
}
return {
loading: false,
success: true,
};
});
return data;
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:删除世界书条目
deleteWorldBookEntry: async (name, uid) => {
set({ loading: true, error: null, success: false });
try {
const response = await fetch(`/api/worldbooks/${name}/entries/${uid}`, {
method: 'DELETE'
});
await handleResponse(response);
set(state => {
if (state.currentWorldBook?.name === name) {
const filteredEntries = state.currentEntries.filter(entry => entry.uid !== uid);
return {
loading: false,
currentEntries: filteredEntries,
currentEntry: state.currentEntry?.uid === uid
? null
: state.currentEntry,
success: true,
};
}
return {
loading: false,
success: true,
};
});
return { name, uid };
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:导入世界书
importWorldBook: async (name, file) => {
set({ loading: true, error: null, success: false });
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`/api/worldbooks/${name}/import`, {
method: 'POST',
body: formData
});
const data = await handleResponse(response);
set(state => {
const existingIndex = state.worldBooks.findIndex(wb => wb.name === data.name);
let updatedWorldBooks;
let updatedGlobalBooks = [...state.globalWorldBooks];
if (existingIndex !== -1) {
updatedWorldBooks = [...state.worldBooks];
updatedWorldBooks[existingIndex] = data;
// 更新全局世界书列表
const globalIndex = updatedGlobalBooks.findIndex(wb => wb.name === data.name);
if (data.is_global) {
if (globalIndex === -1) {
updatedGlobalBooks = [...updatedGlobalBooks, data];
} else {
updatedGlobalBooks[globalIndex] = data;
}
} else {
if (globalIndex !== -1) {
updatedGlobalBooks = updatedGlobalBooks.filter(wb => wb.name !== data.name);
}
}
} else {
updatedWorldBooks = [...state.worldBooks, data];
// 如果是世界书被标记为全局,添加到全局列表
if (data.is_global) {
updatedGlobalBooks = [...updatedGlobalBooks, data];
}
}
// 保存到 LocalStorage
saveGlobalWorldBooks(updatedGlobalBooks);
return {
loading: false,
worldBooks: updatedWorldBooks,
globalWorldBooks: updatedGlobalBooks,
currentWorldBook: state.currentWorldBook?.name === data.name
? data
: state.currentWorldBook,
success: true,
message: '世界书导入成功'
};
});
return data;
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
// 异步操作:导出世界书
exportWorldBook: async (name, format = 'internal') => {
set({ loading: true, error: null, success: false });
try {
const response = await fetch(`/api/worldbooks/${name}/export?format=${format}`);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || `请求失败: ${response.status}`);
}
// 处理文件下载
const filename = format === 'sillytavern' ? `${name}_sillytavern.json` : `${name}.json`;
await handleFileDownload(response, filename);
set({
loading: false,
success: true,
message: '世界书导出成功'
});
return { name, format };
} catch (error) {
set({
loading: false,
error: error.message,
success: false
});
throw error;
}
},
}));
export default useWorldBookStore;

View File

@@ -0,0 +1,5 @@
// SideBarLeft 相关的 Store
export { default as useSideBarLeftStore } from './SideBarLeftSlice';
export { default as useApiConfigStore } from './ApiConfigSlice';
export { default as usePresetStore } from './PresetSlice';
export { default as useWorldBookStore } from './WorldBookSlice';