refactor llm config

This commit is contained in:
bridge
2025-12-30 22:20:30 +08:00
parent f539b21801
commit d55ada7d66
10 changed files with 235 additions and 69 deletions

View File

@@ -19,6 +19,7 @@ const socketStore = useSocketStore()
const showMenu = ref(false)
const isManualPaused = ref(false)
const menuDefaultTab = ref<'save' | 'load' | 'create' | 'delete' | 'llm'>('load')
onMounted(async () => {
// 初始化 Socket 连接
@@ -28,6 +29,15 @@ onMounted(async () => {
window.addEventListener('keydown', handleKeydown)
})
// 导出方法供 socket store 调用
function openLLMConfig() {
menuDefaultTab.value = 'llm'
showMenu.value = true
}
// 暴露给全局以便 socket store 可以调用
;(window as any).__openLLMConfig = openLLMConfig
onUnmounted(() => {
socketStore.disconnect()
window.removeEventListener('keydown', handleKeydown)
@@ -115,7 +125,8 @@ watch([showMenu, isManualPaused], ([menuVisible, manualPaused]) => {
</div>
<SystemMenu
:visible="showMenu"
:visible="showMenu"
:default-tab="menuDefaultTab"
@close="handleMenuClose"
/>
</div>

View File

@@ -8,11 +8,13 @@ const API_BASE = import.meta.env.VITE_API_TARGET || '';
export class ApiError extends Error {
public status: number;
public response: { data: any };
constructor(status: number, message: string) {
constructor(status: number, message: string, responseData?: any) {
super(message);
this.status = status;
this.name = 'ApiError';
this.response = { data: responseData || {} };
}
}
@@ -21,7 +23,21 @@ async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
const response = await fetch(url, options);
if (!response.ok) {
throw new ApiError(response.status, `Request failed: ${response.statusText}`);
// 尝试解析错误响应的 JSON
let errorData = null;
let errorMessage = `Request failed: ${response.statusText}`;
try {
errorData = await response.json();
// 如果后端返回了 detail 字段,使用它作为错误消息
if (errorData?.detail) {
errorMessage = errorData.detail;
}
} catch {
// 如果解析失败,使用默认错误消息
}
throw new ApiError(response.status, errorMessage, errorData);
}
// 假设后端总是返回 JSON

View File

@@ -7,29 +7,36 @@ import LLMConfigPanel from './game/panels/system/LLMConfigPanel.vue'
const props = defineProps<{
visible: boolean
defaultTab?: 'save' | 'load' | 'create' | 'delete' | 'llm'
}>()
const emit = defineEmits<{
(e: 'close'): void
}>()
const activeTab = ref<'save' | 'load' | 'create' | 'delete' | 'llm'>('load')
const activeTab = ref<'save' | 'load' | 'create' | 'delete' | 'llm'>(props.defaultTab || 'load')
function switchTab(tab: typeof activeTab.value) {
activeTab.value = tab
}
// Reset tab when reopening
// 监听 defaultTab 变化
watch(() => props.defaultTab, (newTab) => {
if (newTab) {
activeTab.value = newTab
}
})
// 当菜单打开时,如果有 defaultTab 就使用它
watch(() => props.visible, (val) => {
if (val) {
// Do not reset activeTab to keep user context, or reset if preferred
// activeTab.value = 'load'
if (val && props.defaultTab) {
activeTab.value = props.defaultTab
}
})
</script>
<template>
<div v-if="visible" class="system-menu-overlay" @click.self="emit('close')">
<div v-if="visible" class="system-menu-overlay">
<div class="system-menu">
<div class="menu-header">
<h2>系统菜单</h2>

View File

@@ -42,6 +42,37 @@ export const useSocketStore = defineStore('socket', () => {
uiStore.refreshDetail();
}
}
// ===== 处理 LLM 配置要求消息 =====
else if (data.type === 'llm_config_required') {
console.warn('LLM 配置要求:', data.error);
// 显示错误提示
const message = data.error || 'LLM 连接失败,请配置';
// 通过全局方法打开 LLM 配置界面
if ((window as any).__openLLMConfig) {
(window as any).__openLLMConfig();
}
// 可以选择在这里显示一个提示消息
// 需要引入 message API 或者通过其他方式
setTimeout(() => {
alert(`${message}\n\n请在打开的配置界面中完成 LLM 设置。`);
}, 500);
}
// ===== 处理游戏重新初始化消息 =====
else if (data.type === 'game_reinitialized') {
console.log('游戏重新初始化:', data.message);
// 刷新世界状态
worldStore.initialize().catch(console.error);
// 显示成功提示
setTimeout(() => {
alert(data.message || 'LLM 配置成功,游戏已重新初始化');
}, 300);
}
// ===== LLM 消息处理结束 =====
});
// Connect socket