From 02a40004167c8d3e7bef457cd6b3f3754e76d38c Mon Sep 17 00:00:00 2001 From: "mula.liu" Date: Fri, 17 Apr 2026 13:53:47 +0800 Subject: [PATCH] v0.1.4-p6 --- backend/.env.example | 1 - backend/api/bot_router.py | 17 ++++++++ backend/api/system_router.py | 1 - backend/core/database.py | 1 - backend/core/settings.py | 1 - backend/schemas/platform.py | 1 - backend/services/bot_service.py | 1 + backend/services/platform_settings_core.py | 11 ----- backend/services/platform_settings_service.py | 2 - .../platform_system_settings_service.py | 16 ++++++++ frontend/src/i18n/dashboard.en.ts | 6 +-- frontend/src/i18n/dashboard.zh-cn.ts | 6 +-- .../dashboard/hooks/useBotDashboardModule.ts | 5 --- .../dashboard/hooks/useDashboardBotEditor.ts | 10 +++-- .../hooks/useDashboardChatCommandDispatch.ts | 40 +------------------ .../hooks/useDashboardChatComposer.ts | 5 --- .../hooks/useDashboardConversation.ts | 2 - .../hooks/useDashboardDerivedState.ts | 4 +- .../hooks/useDashboardRuntimeControl.ts | 8 +++- .../hooks/useDashboardSystemDefaults.ts | 11 +---- frontend/src/modules/dashboard/types.ts | 1 - frontend/src/modules/platform/types.ts | 1 - frontend/src/store/appStore.ts | 2 + frontend/src/types/bot.ts | 1 + scripts/init-full-db.sh | 2 - scripts/sql/init-data.sql | 3 +- 26 files changed, 61 insertions(+), 98 deletions(-) diff --git a/backend/.env.example b/backend/.env.example index 1be2b3e..1ebbc66 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -34,7 +34,6 @@ DEFAULT_BOT_SYSTEM_TIMEZONE=Asia/Shanghai # The following platform-level items are initialized by SQL and managed in sys_setting / 平台参数: # - page_size # - chat_pull_page_size -# - command_auto_unlock_seconds # - upload_max_mb # - allowed_attachment_extensions # - workspace_download_extensions diff --git a/backend/api/bot_router.py b/backend/api/bot_router.py index 52889f9..afb229e 100644 --- a/backend/api/bot_router.py +++ b/backend/api/bot_router.py @@ -18,6 +18,7 @@ from services.bot_management_service import ( list_bots_with_cache, update_bot_record, ) +from services.image_service import list_registered_images from services.provider_service import test_provider_connection router = APIRouter() @@ -28,6 +29,22 @@ async def test_provider(payload: dict): return await test_provider_connection(payload) +@router.post("/api/bots/{bot_id}/providers/test") +async def test_bot_provider(bot_id: str, payload: dict, session: Session = Depends(get_session)): + bot = session.get(BotInstance, bot_id) + if not bot: + raise HTTPException(status_code=404, detail="Bot not found") + return await test_provider_connection(payload) + + +@router.get("/api/bots/{bot_id}/images") +def list_bot_images(bot_id: str, session: Session = Depends(get_session)): + bot = session.get(BotInstance, bot_id) + if not bot: + raise HTTPException(status_code=404, detail="Bot not found") + return list_registered_images(session) + + @router.post("/api/bots") def create_bot(payload: BotCreateRequest, session: Session = Depends(get_session)): return create_bot_record(session, payload=payload) diff --git a/backend/api/system_router.py b/backend/api/system_router.py index 89ed865..da9ad6d 100644 --- a/backend/api/system_router.py +++ b/backend/api/system_router.py @@ -34,7 +34,6 @@ def get_system_defaults(): "chat": { "pull_page_size": platform_settings.chat_pull_page_size, "page_size": platform_settings.page_size, - "command_auto_unlock_seconds": platform_settings.command_auto_unlock_seconds, }, "topic_presets": get_topic_presets()["presets"], "speech": { diff --git a/backend/core/database.py b/backend/core/database.py index 75e9362..3c9e631 100644 --- a/backend/core/database.py +++ b/backend/core/database.py @@ -45,7 +45,6 @@ REQUIRED_TABLES = ( REQUIRED_SYS_SETTING_KEYS = ( "page_size", "chat_pull_page_size", - "command_auto_unlock_seconds", "auth_token_ttl_hours", "auth_token_max_active", "upload_max_mb", diff --git a/backend/core/settings.py b/backend/core/settings.py index ab193a6..5269c8d 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -187,7 +187,6 @@ DATABASE_POOL_RECYCLE: Final[int] = _env_int("DATABASE_POOL_RECYCLE", 1800, 30, DEFAULT_UPLOAD_MAX_MB: Final[int] = 100 DEFAULT_PAGE_SIZE: Final[int] = 10 DEFAULT_CHAT_PULL_PAGE_SIZE: Final[int] = 60 -DEFAULT_COMMAND_AUTO_UNLOCK_SECONDS: Final[int] = 10 DEFAULT_AUTH_TOKEN_TTL_HOURS: Final[int] = _env_int("AUTH_TOKEN_TTL_HOURS", 24, 1, 720) DEFAULT_AUTH_TOKEN_MAX_ACTIVE: Final[int] = _env_int("AUTH_TOKEN_MAX_ACTIVE", 2, 1, 20) DEFAULT_BOT_SYSTEM_TIMEZONE: Final[str] = str( diff --git a/backend/schemas/platform.py b/backend/schemas/platform.py index 9fb356b..f15e51d 100644 --- a/backend/schemas/platform.py +++ b/backend/schemas/platform.py @@ -6,7 +6,6 @@ from pydantic import BaseModel, Field class PlatformSettingsPayload(BaseModel): page_size: int = Field(default=10, ge=1, le=100) chat_pull_page_size: int = Field(default=60, ge=10, le=500) - command_auto_unlock_seconds: int = Field(default=10, ge=1, le=600) auth_token_ttl_hours: int = Field(default=24, ge=1, le=720) auth_token_max_active: int = Field(default=2, ge=1, le=20) upload_max_mb: int = Field(default=100, ge=1, le=2048) diff --git a/backend/services/bot_service.py b/backend/services/bot_service.py index 457163d..ce30471 100644 --- a/backend/services/bot_service.py +++ b/backend/services/bot_service.py @@ -424,6 +424,7 @@ def serialize_bot_detail(bot: BotInstance) -> Dict[str, Any]: "image_tag": bot.image_tag, "llm_provider": runtime["llm_provider"], "llm_model": runtime["llm_model"], + "api_key": runtime["api_key"], "api_base": runtime["api_base"], "temperature": runtime["temperature"], "top_p": runtime["top_p"], diff --git a/backend/services/platform_settings_core.py b/backend/services/platform_settings_core.py index 52e3305..9767728 100644 --- a/backend/services/platform_settings_core.py +++ b/backend/services/platform_settings_core.py @@ -9,7 +9,6 @@ from core.settings import ( DEFAULT_AUTH_TOKEN_MAX_ACTIVE, DEFAULT_AUTH_TOKEN_TTL_HOURS, DEFAULT_CHAT_PULL_PAGE_SIZE, - DEFAULT_COMMAND_AUTO_UNLOCK_SECONDS, DEFAULT_PAGE_SIZE, DEFAULT_UPLOAD_MAX_MB, DEFAULT_WORKSPACE_DOWNLOAD_EXTENSIONS, @@ -24,7 +23,6 @@ ACTIVITY_EVENT_RETENTION_SETTING_KEY = "activity_event_retention_days" SETTING_KEYS = ( "page_size", "chat_pull_page_size", - "command_auto_unlock_seconds", "auth_token_ttl_hours", "auth_token_max_active", "upload_max_mb", @@ -52,15 +50,6 @@ SYSTEM_SETTING_DEFINITIONS: Dict[str, Dict[str, Any]] = { "is_public": True, "sort_order": 8, }, - "command_auto_unlock_seconds": { - "name": "发送按钮自动恢复秒数", - "category": "chat", - "description": "对话发送后按钮保持停止态的最长秒数,超时后自动恢复为可发送状态。", - "value_type": "integer", - "value": DEFAULT_COMMAND_AUTO_UNLOCK_SECONDS, - "is_public": True, - "sort_order": 9, - }, "auth_token_ttl_hours": { "name": "认证 Token 过期小时数", "category": "auth", diff --git a/backend/services/platform_settings_service.py b/backend/services/platform_settings_service.py index e56186e..db65d99 100644 --- a/backend/services/platform_settings_service.py +++ b/backend/services/platform_settings_service.py @@ -50,7 +50,6 @@ def get_platform_settings(session: Session) -> PlatformSettingsPayload: { "page_size": max(1, min(100, int(data["page_size"]))), "chat_pull_page_size": max(10, min(500, int(data["chat_pull_page_size"]))), - "command_auto_unlock_seconds": max(1, min(600, int(data["command_auto_unlock_seconds"]))), "auth_token_ttl_hours": max(1, min(720, int(data["auth_token_ttl_hours"]))), "auth_token_max_active": max(1, min(20, int(data["auth_token_max_active"]))), "upload_max_mb": int(data["upload_max_mb"]), @@ -70,7 +69,6 @@ def save_platform_settings(session: Session, payload: PlatformSettingsPayload) - normalized = PlatformSettingsPayload( page_size=max(1, min(100, int(payload.page_size))), chat_pull_page_size=max(10, min(500, int(payload.chat_pull_page_size))), - command_auto_unlock_seconds=max(1, min(600, int(payload.command_auto_unlock_seconds))), auth_token_ttl_hours=max(1, min(720, int(payload.auth_token_ttl_hours))), auth_token_max_active=max(1, min(20, int(payload.auth_token_max_active))), upload_max_mb=payload.upload_max_mb, diff --git a/backend/services/platform_system_settings_service.py b/backend/services/platform_system_settings_service.py index bd48eb9..9549327 100644 --- a/backend/services/platform_system_settings_service.py +++ b/backend/services/platform_system_settings_service.py @@ -15,9 +15,23 @@ from services.platform_settings_core import ( ) REQUIRED_SYSTEM_SETTING_KEYS = tuple(SYSTEM_SETTING_DEFINITIONS.keys()) +DEPRECATED_SYSTEM_SETTING_KEYS = ("command_auto_unlock_seconds",) + + +def _prune_deprecated_system_settings(session: Session) -> None: + removed = False + for key in DEPRECATED_SYSTEM_SETTING_KEYS: + row = session.get(PlatformSetting, key) + if row is None: + continue + session.delete(row) + removed = True + if removed: + session.commit() def validate_required_system_settings(session: Session) -> None: + _prune_deprecated_system_settings(session) stmt = select(PlatformSetting.key).where(PlatformSetting.key.in_(REQUIRED_SYSTEM_SETTING_KEYS)) present = { str(key or "").strip() @@ -53,6 +67,8 @@ def list_system_settings(session: Session, search: str = "") -> List[Dict[str, A def create_or_update_system_setting(session: Session, payload: SystemSettingPayload) -> Dict[str, Any]: normalized_key = _normalize_setting_key(payload.key) + if normalized_key in DEPRECATED_SYSTEM_SETTING_KEYS: + raise ValueError("Setting key has been removed") definition = SYSTEM_SETTING_DEFINITIONS.get(normalized_key, {}) row = _upsert_setting_row( session, diff --git a/frontend/src/i18n/dashboard.en.ts b/frontend/src/i18n/dashboard.en.ts index f721077..d308c2f 100644 --- a/frontend/src/i18n/dashboard.en.ts +++ b/frontend/src/i18n/dashboard.en.ts @@ -69,7 +69,7 @@ export const dashboardEn = { feedbackSaveFail: 'Failed to save feedback.', feedbackMessagePending: 'Message is not synced yet. Please retry in a moment.', sendFailMsg: (msg: string) => `Command delivery failed: ${msg}`, - providerRequired: 'Set provider/model/new API key before testing.', + providerRequired: 'Set provider/model/API key before testing.', connOk: (preview: string) => (preview ? `Connection passed, models: ${preview}` : 'Connection passed'), connFail: (msg: string) => `Failed: ${msg}`, configUpdated: 'Configuration updated (effective after bot restart).', @@ -265,8 +265,8 @@ export const dashboardEn = { baseImageReadonly: 'Base Image', modelName: 'Model Name', modelNamePlaceholder: 'e.g. qwen-plus', - newApiKey: 'New API Key (optional)', - newApiKeyPlaceholder: 'Only updated when filled', + newApiKey: 'API Key', + newApiKeyPlaceholder: 'Enter API Key', testing: 'Testing...', testModelConnection: 'Test model connection', cancel: 'Cancel', diff --git a/frontend/src/i18n/dashboard.zh-cn.ts b/frontend/src/i18n/dashboard.zh-cn.ts index d807992..0124031 100644 --- a/frontend/src/i18n/dashboard.zh-cn.ts +++ b/frontend/src/i18n/dashboard.zh-cn.ts @@ -69,7 +69,7 @@ export const dashboardZhCn = { feedbackSaveFail: '反馈保存失败。', feedbackMessagePending: '消息尚未同步,暂不可反馈。', sendFailMsg: (msg: string) => `指令发送失败:${msg}`, - providerRequired: '请填写 Provider、模型和新 API Key 后再测试。', + providerRequired: '请填写 Provider、模型和 API Key 后再测试。', connOk: (preview: string) => (preview ? `连接成功,模型: ${preview}` : '连接成功'), connFail: (msg: string) => `连接失败: ${msg}`, configUpdated: '配置已更新(重启 Bot 后生效)。', @@ -265,8 +265,8 @@ export const dashboardZhCn = { baseImageReadonly: '基础镜像', modelName: '模型名称', modelNamePlaceholder: '如 qwen-plus', - newApiKey: '新的 API Key(留空不更新)', - newApiKeyPlaceholder: '输入新 Key 才会更新', + newApiKey: 'API Key', + newApiKeyPlaceholder: '请输入 API Key', testing: '测试中...', testModelConnection: '测试模型连接', cancel: '取消', diff --git a/frontend/src/modules/dashboard/hooks/useBotDashboardModule.ts b/frontend/src/modules/dashboard/hooks/useBotDashboardModule.ts index 526d38b..481e82c 100644 --- a/frontend/src/modules/dashboard/hooks/useBotDashboardModule.ts +++ b/frontend/src/modules/dashboard/hooks/useBotDashboardModule.ts @@ -44,7 +44,6 @@ export function useBotDashboardModule({ const fileNotPreviewableLabel = locale === 'zh' ? '当前文件类型不支持预览' : 'This file type is not previewable'; const [botListPageSize, setBotListPageSize] = useState(10); const [chatPullPageSize, setChatPullPageSize] = useState(60); - const [commandAutoUnlockSeconds, setCommandAutoUnlockSeconds] = useState(10); const isZh = locale === 'zh'; const botSearchInputName = `nbot-search-${useId().replace(/:/g, '-')}`; const workspaceSearchInputName = `nbot-workspace-search-${useId().replace(/:/g, '-')}`; @@ -63,7 +62,6 @@ export function useBotDashboardModule({ notify, setBotListPageSize, setChatPullPageSize, - setCommandAutoUnlockSeconds, }); const { botListMenuOpen, @@ -435,7 +433,6 @@ export function useBotDashboardModule({ filePickerRef, interruptExecution, isInterrupting, - isSendingBlocked, jumpConversationToDate, loadInitialChatPage, onChatScroll, @@ -466,7 +463,6 @@ export function useBotDashboardModule({ canChat, isTaskRunningExternally: isBotThinking, chatPullPageSize, - commandAutoUnlockSeconds, pendingAttachments, setPendingAttachments, isUploadingAttachments, @@ -500,7 +496,6 @@ export function useBotDashboardModule({ isChatEnabled, } = useDashboardInteractionState({ canChat, - isSendingBlocked, isVoiceRecording, isVoiceTranscribing, selectedBot, diff --git a/frontend/src/modules/dashboard/hooks/useDashboardBotEditor.ts b/frontend/src/modules/dashboard/hooks/useDashboardBotEditor.ts index 9fd7345..9d00185 100644 --- a/frontend/src/modules/dashboard/hooks/useDashboardBotEditor.ts +++ b/frontend/src/modules/dashboard/hooks/useDashboardBotEditor.ts @@ -97,7 +97,7 @@ export function useDashboardBotEditor({ llm_provider: provider, llm_model: bot.llm_model || '', image_tag: bot.image_tag || '', - api_key: '', + api_key: bot.api_key || '', api_base: bot.api_base || '', temperature: clampTemperature(bot.temperature ?? 0.2), top_p: bot.top_p ?? 1, @@ -193,10 +193,14 @@ export function useDashboardBotEditor({ notify(t.providerRequired, { tone: 'warning' }); return; } + const targetBotId = String(selectedBot?.id || selectedBotId || '').trim(); + const endpoint = targetBotId + ? `${APP_ENDPOINTS.apiBase}/bots/${encodeURIComponent(targetBotId)}/providers/test` + : `${APP_ENDPOINTS.apiBase}/providers/test`; setIsTestingProvider(true); setProviderTestResult(''); try { - const res = await axios.post(`${APP_ENDPOINTS.apiBase}/providers/test`, { + const res = await axios.post(endpoint, { provider: editForm.llm_provider, model: editForm.llm_model, api_key: editForm.api_key.trim(), @@ -214,7 +218,7 @@ export function useDashboardBotEditor({ } finally { setIsTestingProvider(false); } - }, [editForm.api_base, editForm.api_key, editForm.llm_model, editForm.llm_provider, notify, t]); + }, [editForm.api_base, editForm.api_key, editForm.llm_model, editForm.llm_provider, notify, selectedBot?.id, selectedBotId, t]); const saveBot = useCallback(async (mode: 'params' | 'agent' | 'base') => { const targetBotId = String(selectedBot?.id || selectedBotId || '').trim(); diff --git a/frontend/src/modules/dashboard/hooks/useDashboardChatCommandDispatch.ts b/frontend/src/modules/dashboard/hooks/useDashboardChatCommandDispatch.ts index 0cc0f12..a8dc5ec 100644 --- a/frontend/src/modules/dashboard/hooks/useDashboardChatCommandDispatch.ts +++ b/frontend/src/modules/dashboard/hooks/useDashboardChatCommandDispatch.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState, type Dispatch, type SetStateAction } from 'react'; +import { useCallback, useState, type Dispatch, type SetStateAction } from 'react'; import axios from 'axios'; import { APP_ENDPOINTS } from '../../../config/env'; @@ -42,7 +42,6 @@ interface UseDashboardChatCommandDispatchOptions { selectedBot?: { id: string } | null; canChat: boolean; isTaskRunningExternally: boolean; - commandAutoUnlockSeconds: number; command: string; pendingAttachments: string[]; quotedReply: QuotedReply | null; @@ -62,7 +61,6 @@ export function useDashboardChatCommandDispatch({ selectedBot, canChat, isTaskRunningExternally, - commandAutoUnlockSeconds, command, pendingAttachments, quotedReply, @@ -78,40 +76,15 @@ export function useDashboardChatCommandDispatch({ t, }: UseDashboardChatCommandDispatchOptions) { const [sendingByBot, setSendingByBot] = useState>({}); - const [commandAutoUnlockDeadlineByBot, setCommandAutoUnlockDeadlineByBot] = useState>({}); const [interruptingByBot, setInterruptingByBot] = useState>({}); const [controlCommandByBot, setControlCommandByBot] = useState>({}); const selectedBotSendingCount = selectedBot ? Number(sendingByBot[selectedBot.id] || 0) : 0; - const selectedBotAutoUnlockDeadline = selectedBot ? Number(commandAutoUnlockDeadlineByBot[selectedBot.id] || 0) : 0; const activeControlCommand = selectedBot ? controlCommandByBot[selectedBot.id] || '' : ''; const isSending = selectedBotSendingCount > 0; const isTaskRunning = Boolean(selectedBot && (isSending || isTaskRunningExternally)); - const isCommandAutoUnlockWindowActive = selectedBotAutoUnlockDeadline > Date.now(); - const isSendingBlocked = isSending && isCommandAutoUnlockWindowActive; const isInterrupting = Boolean(selectedBot && interruptingByBot[selectedBot.id]); - useEffect(() => { - if (!selectedBot?.id || selectedBotAutoUnlockDeadline <= 0) return; - const remaining = selectedBotAutoUnlockDeadline - Date.now(); - if (remaining <= 0) { - setCommandAutoUnlockDeadlineByBot((prev) => { - const next = { ...prev }; - delete next[selectedBot.id]; - return next; - }); - return; - } - const timer = window.setTimeout(() => { - setCommandAutoUnlockDeadlineByBot((prev) => { - const next = { ...prev }; - delete next[selectedBot.id]; - return next; - }); - }, remaining + 20); - return () => window.clearTimeout(timer); - }, [selectedBot?.id, selectedBotAutoUnlockDeadline]); - const submitChatPayload = useCallback(async ({ commandRaw, attachmentsRaw, @@ -137,10 +110,6 @@ export function useDashboardChatCommandDispatch({ try { requestAnimationFrame(() => scrollConversationToBottom('auto')); setSendingByBot((prev) => ({ ...prev, [selectedBot.id]: Number(prev[selectedBot.id] || 0) + 1 })); - setCommandAutoUnlockDeadlineByBot((prev) => ({ - ...prev, - [selectedBot.id]: Date.now() + (commandAutoUnlockSeconds * 1000), - })); const res = await axios.post( `${APP_ENDPOINTS.apiBase}/bots/${selectedBot.id}/command`, { command: payloadText, attachments }, @@ -168,11 +137,6 @@ export function useDashboardChatCommandDispatch({ return true; } catch (error: unknown) { const msg = resolveApiErrorMessage(error, t.sendFail); - setCommandAutoUnlockDeadlineByBot((prev) => { - const next = { ...prev }; - delete next[selectedBot.id]; - return next; - }); addBotMessage(selectedBot.id, { role: 'assistant', text: t.sendFailMsg(msg), @@ -196,7 +160,6 @@ export function useDashboardChatCommandDispatch({ }, [ addBotMessage, canChat, - commandAutoUnlockSeconds, completeLeadingStagedSubmission, notify, scrollConversationToBottom, @@ -302,7 +265,6 @@ export function useDashboardChatCommandDispatch({ interruptExecution, isInterrupting, isSending, - isSendingBlocked, isTaskRunning, sendControlCommand, sendCurrentDraft, diff --git a/frontend/src/modules/dashboard/hooks/useDashboardChatComposer.ts b/frontend/src/modules/dashboard/hooks/useDashboardChatComposer.ts index e8b529e..2bf915b 100644 --- a/frontend/src/modules/dashboard/hooks/useDashboardChatComposer.ts +++ b/frontend/src/modules/dashboard/hooks/useDashboardChatComposer.ts @@ -16,7 +16,6 @@ interface UseDashboardChatComposerOptions { selectedBot?: { id: string } | null; canChat: boolean; isTaskRunningExternally: boolean; - commandAutoUnlockSeconds: number; pendingAttachments: string[]; setPendingAttachments: Dispatch>; isUploadingAttachments: boolean; @@ -41,7 +40,6 @@ export function useDashboardChatComposer({ selectedBot, canChat, isTaskRunningExternally, - commandAutoUnlockSeconds, pendingAttachments, setPendingAttachments, isUploadingAttachments, @@ -111,7 +109,6 @@ export function useDashboardChatComposer({ interruptExecution, isInterrupting, isSending, - isSendingBlocked, isTaskRunning, sendControlCommand, sendCurrentDraft, @@ -120,7 +117,6 @@ export function useDashboardChatComposer({ selectedBot, canChat, isTaskRunningExternally, - commandAutoUnlockSeconds, command, pendingAttachments, quotedReply, @@ -281,7 +277,6 @@ export function useDashboardChatComposer({ interruptExecution, isInterrupting, isSending, - isSendingBlocked, onComposerKeyDown, quoteAssistantReply, quotedReply, diff --git a/frontend/src/modules/dashboard/hooks/useDashboardConversation.ts b/frontend/src/modules/dashboard/hooks/useDashboardConversation.ts index 2d33566..53c8e31 100644 --- a/frontend/src/modules/dashboard/hooks/useDashboardConversation.ts +++ b/frontend/src/modules/dashboard/hooks/useDashboardConversation.ts @@ -14,7 +14,6 @@ interface UseDashboardConversationOptions { canChat: boolean; isTaskRunningExternally: boolean; chatPullPageSize: number; - commandAutoUnlockSeconds: number; pendingAttachments: string[]; setPendingAttachments: Dispatch>; isUploadingAttachments: boolean; @@ -50,7 +49,6 @@ export function useDashboardConversation(options: UseDashboardConversationOption selectedBot: options.selectedBot, canChat: options.canChat, isTaskRunningExternally: options.isTaskRunningExternally, - commandAutoUnlockSeconds: options.commandAutoUnlockSeconds, pendingAttachments: options.pendingAttachments, setPendingAttachments: options.setPendingAttachments, isUploadingAttachments: options.isUploadingAttachments, diff --git a/frontend/src/modules/dashboard/hooks/useDashboardDerivedState.ts b/frontend/src/modules/dashboard/hooks/useDashboardDerivedState.ts index bd15ecf..d42268c 100644 --- a/frontend/src/modules/dashboard/hooks/useDashboardDerivedState.ts +++ b/frontend/src/modules/dashboard/hooks/useDashboardDerivedState.ts @@ -30,7 +30,6 @@ interface UseDashboardBaseStateOptions { interface UseDashboardInteractionStateOptions { canChat: boolean; - isSendingBlocked?: boolean; isVoiceRecording?: boolean; isVoiceTranscribing?: boolean; selectedBot?: BotState; @@ -227,12 +226,11 @@ export function useDashboardBaseState({ export function useDashboardInteractionState({ canChat, - isSendingBlocked = false, isVoiceRecording = false, isVoiceTranscribing = false, selectedBot, }: UseDashboardInteractionStateOptions) { - const isChatEnabled = Boolean(canChat && !isSendingBlocked); + const isChatEnabled = Boolean(canChat); const canSendControlCommand = Boolean( selectedBot && canChat && !isVoiceRecording && !isVoiceTranscribing, ); diff --git a/frontend/src/modules/dashboard/hooks/useDashboardRuntimeControl.ts b/frontend/src/modules/dashboard/hooks/useDashboardRuntimeControl.ts index 2118dbc..71e5d67 100644 --- a/frontend/src/modules/dashboard/hooks/useDashboardRuntimeControl.ts +++ b/frontend/src/modules/dashboard/hooks/useDashboardRuntimeControl.ts @@ -71,15 +71,19 @@ export function useDashboardRuntimeControl({ ); const loadImageOptions = useCallback(async () => { + const targetBotId = String(selectedBotId || forcedBotId || '').trim(); + const endpoint = targetBotId + ? `${APP_ENDPOINTS.apiBase}/bots/${encodeURIComponent(targetBotId)}/images` + : `${APP_ENDPOINTS.apiBase}/images`; const [imagesRes] = await Promise.allSettled([ - axios.get(`${APP_ENDPOINTS.apiBase}/images`), + axios.get(endpoint), ]); if (imagesRes.status === 'fulfilled') { setAvailableImages(Array.isArray(imagesRes.value.data) ? imagesRes.value.data : []); } else { setAvailableImages([]); } - }, []); + }, [forcedBotId, selectedBotId]); const refresh = useCallback(async () => { const forced = String(forcedBotId || '').trim(); diff --git a/frontend/src/modules/dashboard/hooks/useDashboardSystemDefaults.ts b/frontend/src/modules/dashboard/hooks/useDashboardSystemDefaults.ts index 5061836..be68e4e 100644 --- a/frontend/src/modules/dashboard/hooks/useDashboardSystemDefaults.ts +++ b/frontend/src/modules/dashboard/hooks/useDashboardSystemDefaults.ts @@ -12,7 +12,6 @@ interface UseDashboardSystemDefaultsOptions { notify: (message: string, options?: { tone?: 'error' | 'success' | 'warning' | 'info' }) => void; setBotListPageSize: Dispatch>; setChatPullPageSize?: Dispatch>; - setCommandAutoUnlockSeconds?: Dispatch>; setVoiceCountdown?: Dispatch>; } @@ -34,18 +33,11 @@ function resolveChatPullPageSize(raw: unknown) { return Math.max(10, Math.min(500, Math.floor(configured))); } -function resolveCommandAutoUnlockSeconds(raw: unknown) { - const configured = Number(raw); - if (!Number.isFinite(configured) || configured <= 0) return 10; - return Math.max(1, Math.min(600, Math.floor(configured))); -} - export function useDashboardSystemDefaults({ isZh, notify, setBotListPageSize, setChatPullPageSize, - setCommandAutoUnlockSeconds, setVoiceCountdown, }: UseDashboardSystemDefaultsOptions) { const [botListPageSizeReady, setBotListPageSizeReady] = useState(false); @@ -66,7 +58,6 @@ export function useDashboardSystemDefaults({ setBotListPageSize(normalizePlatformPageSize(data?.chat?.page_size, 10)); setChatPullPageSize?.(resolveChatPullPageSize(data?.chat?.pull_page_size)); - setCommandAutoUnlockSeconds?.(resolveCommandAutoUnlockSeconds(data?.chat?.command_auto_unlock_seconds)); setAllowedAttachmentExtensions( parseAllowedAttachmentExtensions(data?.workspace?.allowed_attachment_extensions), ); @@ -85,7 +76,7 @@ export function useDashboardSystemDefaults({ setVoiceMaxSeconds(nextVoiceMaxSeconds); setVoiceCountdown?.(nextVoiceMaxSeconds); } - }, [setBotListPageSize, setChatPullPageSize, setCommandAutoUnlockSeconds, setVoiceCountdown]); + }, [setBotListPageSize, setChatPullPageSize, setVoiceCountdown]); useEffect(() => { let alive = true; diff --git a/frontend/src/modules/dashboard/types.ts b/frontend/src/modules/dashboard/types.ts index cbce824..cbe717b 100644 --- a/frontend/src/modules/dashboard/types.ts +++ b/frontend/src/modules/dashboard/types.ts @@ -280,7 +280,6 @@ export interface SystemDefaultsResponse { chat?: { page_size?: number; pull_page_size?: number; - command_auto_unlock_seconds?: number; }; topic_presets?: unknown; speech?: { diff --git a/frontend/src/modules/platform/types.ts b/frontend/src/modules/platform/types.ts index d5041e6..631d41a 100644 --- a/frontend/src/modules/platform/types.ts +++ b/frontend/src/modules/platform/types.ts @@ -1,7 +1,6 @@ export interface PlatformSettings { page_size: number; chat_pull_page_size: number; - command_auto_unlock_seconds: number; auth_token_ttl_hours: number; auth_token_max_active: number; upload_max_mb: number; diff --git a/frontend/src/store/appStore.ts b/frontend/src/store/appStore.ts index 83b4ef4..f413edd 100644 --- a/frontend/src/store/appStore.ts +++ b/frontend/src/store/appStore.ts @@ -71,6 +71,7 @@ export const useAppStore = create((set) => ({ image_tag: preferDefined(bot.image_tag, prev?.image_tag), llm_provider: preferDefined(bot.llm_provider, prev?.llm_provider), llm_model: preferDefined(bot.llm_model, prev?.llm_model), + api_key: preferDefined(bot.api_key, prev?.api_key), api_base: preferDefined(bot.api_base, prev?.api_base), temperature: preferDefined(bot.temperature, prev?.temperature), top_p: preferDefined(bot.top_p, prev?.top_p), @@ -108,6 +109,7 @@ export const useAppStore = create((set) => ({ enabled: preferDefined(bot.enabled, prev?.enabled), created_at: preferDefined(bot.created_at, prev?.created_at), updated_at: preferDefined(bot.updated_at, prev?.updated_at), + api_key: preferDefined(bot.api_key, prev?.api_key), logs: prev?.logs ?? bot.logs ?? [], messages: prev?.messages ?? bot.messages ?? [], events: prev?.events ?? bot.events ?? [], diff --git a/frontend/src/types/bot.ts b/frontend/src/types/bot.ts index 1188c3b..131a6e0 100644 --- a/frontend/src/types/bot.ts +++ b/frontend/src/types/bot.ts @@ -28,6 +28,7 @@ export interface BotState { image_tag?: string; llm_provider?: string; llm_model?: string; + api_key?: string; api_base?: string; temperature?: number; top_p?: number; diff --git a/scripts/init-full-db.sh b/scripts/init-full-db.sh index 8bb7a03..ac23c57 100755 --- a/scripts/init-full-db.sh +++ b/scripts/init-full-db.sh @@ -150,7 +150,6 @@ docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" exec -T \ PAGE_SIZE_JSON="10" CHAT_PULL_PAGE_SIZE_JSON="60" -COMMAND_AUTO_UNLOCK_SECONDS_JSON="10" AUTH_TOKEN_TTL_HOURS_JSON="24" AUTH_TOKEN_MAX_ACTIVE_JSON="2" UPLOAD_MAX_MB_JSON="$UPLOAD_MAX_MB" @@ -171,7 +170,6 @@ docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" exec -T \ -v ON_ERROR_STOP=1 \ -v page_size_json="$PAGE_SIZE_JSON" \ -v chat_pull_page_size_json="$CHAT_PULL_PAGE_SIZE_JSON" \ - -v command_auto_unlock_seconds_json="$COMMAND_AUTO_UNLOCK_SECONDS_JSON" \ -v auth_token_ttl_hours_json="$AUTH_TOKEN_TTL_HOURS_JSON" \ -v auth_token_max_active_json="$AUTH_TOKEN_MAX_ACTIVE_JSON" \ -v upload_max_mb_json="$UPLOAD_MAX_MB_JSON" \ diff --git a/scripts/sql/init-data.sql b/scripts/sql/init-data.sql index 04dde50..7a4364b 100644 --- a/scripts/sql/init-data.sql +++ b/scripts/sql/init-data.sql @@ -17,7 +17,6 @@ INSERT INTO sys_setting ( VALUES ('page_size', '分页大小', 'ui', '平台各类列表默认每页条数。', 'integer', :'page_size_json', TRUE, 5, NOW(), NOW()), ('chat_pull_page_size', '对话懒加载条数', 'chat', 'Bot 对话区向上懒加载时每次读取的消息条数。', 'integer', :'chat_pull_page_size_json', TRUE, 8, NOW(), NOW()), - ('command_auto_unlock_seconds', '发送按钮自动恢复秒数', 'chat', '对话发送后按钮保持停止态的最长秒数,超时后自动恢复为可发送状态。', 'integer', :'command_auto_unlock_seconds_json', TRUE, 9, NOW(), NOW()), ('auth_token_ttl_hours', '认证 Token 过期小时数', 'auth', 'Panel 与 Bot 登录 Token 的统一有效时长,单位小时。', 'integer', :'auth_token_ttl_hours_json', FALSE, 10, NOW(), NOW()), ('auth_token_max_active', '认证 Token 最大并发数', 'auth', '同一主体允许同时活跃的 Token 数量,超过时自动撤销最旧 Token。', 'integer', :'auth_token_max_active_json', FALSE, 11, NOW(), NOW()), ('upload_max_mb', '上传大小限制', 'upload', '单文件上传大小限制,单位 MB。', 'integer', :'upload_max_mb_json', FALSE, 20, NOW(), NOW()), @@ -35,6 +34,8 @@ SET sort_order = EXCLUDED.sort_order, updated_at = NOW(); +DELETE FROM sys_setting WHERE key = 'command_auto_unlock_seconds'; + INSERT INTO skill_market_item ( skill_key, display_name,