diff --git a/configs/dbgpt-proxy-tongyi.toml b/configs/dbgpt-proxy-tongyi.toml index 37b84def5..4c29893a6 100644 --- a/configs/dbgpt-proxy-tongyi.toml +++ b/configs/dbgpt-proxy-tongyi.toml @@ -9,6 +9,21 @@ encrypt_key = "your_secret_key" host = "0.0.0.0" port = 5670 +[service.web.agent_context] +# Agent context-window budget. Set max_context_tokens to 0 to auto-detect from +# the selected model's metadata. The effective budget shown in the UI is +# max_context_tokens - reserved_tokens. +max_context_tokens = 0 +reserved_tokens = 4096 +warning_threshold = 0.70 +error_threshold = 0.90 +critical_threshold = 0.95 +min_keep_recent_rounds = 3 +max_observation_age_rounds = 5 +truncated_observation_max_chars = 200 +min_keep_tokens = 10000 +max_compact_failures = 3 + [service.web.database] type = "sqlite" path = "pilot/meta_data/dbgpt.db" diff --git a/packages/dbgpt-serve/src/dbgpt_serve/scheduled_task/serve.py b/packages/dbgpt-serve/src/dbgpt_serve/scheduled_task/serve.py index 70e9c8fd7..809ad101e 100644 --- a/packages/dbgpt-serve/src/dbgpt_serve/scheduled_task/serve.py +++ b/packages/dbgpt-serve/src/dbgpt_serve/scheduled_task/serve.py @@ -1,6 +1,7 @@ """ScheduledTaskServe - mount routes, start scheduler, recover jobs on boot.""" import logging +import os from typing import List, Optional, Union from sqlalchemy import URL @@ -22,6 +23,35 @@ from .service.service import ScheduledTaskService logger = logging.getLogger(__name__) +# Env var name for the master switch that controls whether scheduled tasks +# are actually *executed* on this process. See _scheduler_enabled(). +_SCHEDULER_ENABLED_ENV = "DBGPT_CHAT_TASK_SCHEDULER_ENABLED" + +# In-memory jobstore sentinel. TaskScheduler treats this exact URL as a signal +# to use APScheduler's MemoryJobStore (no SQLite file, no apscheduler_jobs +# table). The business tables (dbgpt_serve_scheduled_task / _run) are the sole +# source of truth; jobs are rehydrated into memory on boot via +# _recover_jobs_from_db(). +_IN_MEMORY_JOBSTORE_URL = "sqlite:///:memory:" + + +def _scheduler_enabled() -> bool: + """Env-driven master switch for *executing* scheduled tasks. + + Controls rehydrate + triggering only — the REST API is always mounted, + so task CRUD keeps working even when this returns False. + + Reads ``DBGPT_CHAT_TASK_SCHEDULER_ENABLED``. Accepts ``1/true/yes/on`` + (case-insensitive) as enabled. Unset defaults to enabled. + + Returns: + bool: True if scheduled-task execution should run on this process. + """ + raw = os.getenv(_SCHEDULER_ENABLED_ENV) + if raw is None: + return True + return raw.strip().lower() in ("1", "true", "yes", "on") + class ScheduledTaskServe(BaseServe): """Serve component for scheduled tasks. @@ -69,8 +99,13 @@ class ScheduledTaskServe(BaseServe): # Build scheduler / service. The runner callable is the # module-level run_scheduled_task (not a bound method) so that - # APScheduler's SQLAlchemyJobStore can pickle the job state. - self._scheduler = TaskScheduler() + # APScheduler can pickle the job state if ever needed. + # + # Use an in-memory jobstore: the business tables are the single source + # of truth and jobs are rehydrated on boot (_recover_jobs_from_db), + # so there is no need for APScheduler to persist its own job table + # (avoids a stray scheduler.db / apscheduler_jobs table in prod DBs). + self._scheduler = TaskScheduler(jobstore_url=_IN_MEMORY_JOBSTORE_URL) self._service = ScheduledTaskService( scheduler=self._scheduler, runner_callable=run_scheduled_task, @@ -95,7 +130,19 @@ class ScheduledTaskServe(BaseServe): event loop is already running — the correct context for AsyncIOScheduler.start(). The runner auth secret is resolved in init_app, so the scheduler always starts here. + + Gated by DBGPT_CHAT_TASK_SCHEDULER_ENABLED: when disabled, the + scheduler is neither started nor rehydrated, but the REST API + (mounted in init_app) stays fully available. """ + if not _scheduler_enabled(): + logger.info( + "Scheduler execution disabled via %s; REST API stays " + "available, no jobs will be rehydrated or triggered.", + _SCHEDULER_ENABLED_ENV, + ) + return + if self._scheduler is None: logger.warning("Scheduler not initialised; skipping startup.") return diff --git a/web/locales/en/chat.ts b/web/locales/en/chat.ts index bb20c99cb..5d29a76da 100644 --- a/web/locales/en/chat.ts +++ b/web/locales/en/chat.ts @@ -52,7 +52,7 @@ export const ChatEn = { use_skill: 'Use Skill', use_knowledge: 'Use Knowledge Base', use_database: 'Use Database', - use_connector: 'Select Connector', + use_connector: 'Select MCP', execution_steps: 'Execution Steps', db_gpt_computer: "DB-GPT's Computer", load_skill: 'Load Skill', diff --git a/web/new-components/chat/content/ManusLeftPanel.tsx b/web/new-components/chat/content/ManusLeftPanel.tsx index 48c509a43..621a762ef 100644 --- a/web/new-components/chat/content/ManusLeftPanel.tsx +++ b/web/new-components/chat/content/ManusLeftPanel.tsx @@ -1,4 +1,5 @@ import MarkdownContext from '@/new-components/common/MarkdownContext'; +import { AttachedConnector } from '@/new-components/connector/types'; import { ApiOutlined, AppstoreOutlined, @@ -36,7 +37,6 @@ import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from ' import { useTranslation } from 'react-i18next'; import ObservationFormatter from './ObservationFormatter'; import TaskPlanCard, { TaskItem } from './TaskPlanCard'; -import { AttachedConnector } from '@/new-components/connector/types'; export type StepStatus = 'pending' | 'running' | 'completed' | 'error'; @@ -353,7 +353,7 @@ const SkillCompactCard: React.FC<{ onDownload?: () => void; }> = memo(({ skillName, onClick, onDownload }) => { const { t } = useTranslation(); - const [downloading, setDownloading] = useState(false); + const [downloading, _setDownloading] = useState(false); const [isAdded, setIsAdded] = useState(false); return (
) => string; + const progressText = getTodoStepBadge(tFn, step); + const todoTitle = getTodoStepTitle(tFn, step); return (
= memo(({ step, isActive, onClick }) => { const { t } = useTranslation(); const [isVisible, setIsVisible] = useState(false); - const [contentExpanded, setContentExpanded] = useState(false); const parsed = useMemo(() => parseSkillResourceDescription(step.description), [step.description]); React.useEffect(() => { @@ -714,7 +714,6 @@ const SkillResourceCard: React.FC<{ } const resourceName = parsed.resourcePath.split('/').pop() || parsed.resourcePath; - const hasContent = parsed.content.length > 0; return (
; }> = memo(({ section, activeStepId, onStepClick, defaultExpanded = true, stepThoughts }) => { - const { t } = useTranslation(); const [isExpanded, setIsExpanded] = useState(defaultExpanded); const completedCount = section.steps.filter(s => s.status === 'completed').length; @@ -811,10 +809,6 @@ const SectionBlock: React.FC<{ const isAllCompleted = completedCount === totalCount && totalCount > 0; const hasRunningStep = section.steps.some(s => s.status === 'running'); - const hasObservations = useMemo(() => { - return section.steps.some(step => step.description?.includes('Observation:')); - }, [section.steps]); - return (
{/* Section Header */} @@ -898,7 +892,7 @@ const ManusLeftPanel: React.FC = ({ onArtifactClick, onArtifactDownload, onViewAllFiles, - onShare, + onShare: _onShare, isCollapsed, onExpand, attachedFile, @@ -1019,7 +1013,10 @@ const ManusLeftPanel: React.FC = ({
)} {(attachedConnectors ?? []).map(c => ( -
+
@@ -1027,9 +1024,7 @@ const ManusLeftPanel: React.FC = ({
{c.display_name}
-
- {c.connector_type} -
+
{c.connector_type}
))} diff --git a/web/pages/index.tsx b/web/pages/index.tsx index 13e18149a..666aa8300 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -2564,40 +2564,6 @@ const Playground: NextPage = () => { })}
- {/* Input Area at Bottom for Chat Mode - Premium Layered Style */} -
-
-
- {/* Context Tags Area */} -
- {selectedDb && ( - setSelectedDb(null)} - className='flex items-center gap-1 bg-blue-50 border-blue-200 text-blue-700 px-3 py-1 rounded-full' - > - {getDbIcon(selectedDb.type)} {selectedDb.db_name} - - )} - {selectedKnowledge && ( - setSelectedKnowledge(null)} - className='flex items-center gap-1 bg-orange-50 border-orange-200 text-orange-700 px-3 py-1 rounded-full' - > - {selectedKnowledge.name} - - )} - {uploadedFile && ( - setUploadedFile(null)} - className='flex items-center gap-1 bg-green-50 border-green-200 text-green-700 px-3 py-1 rounded-full' - > - {uploadedFile.name} - - )} -
{/* Input Area at Bottom for Chat Mode - Hidden in read-only task replay mode */} {!router.query.from_task && (
@@ -2679,76 +2645,7 @@ const Playground: NextPage = () => { className='flex-1 resize-none !border-none !shadow-none !bg-transparent px-0 py-2' style={{ backgroundColor: 'transparent' }} /> - {/* Outer Frame - Floating Effect */} -
- {/* White Inner Box - Clean Glass Card */} -
- { - const newValue = e.target.value; - setQuery(newValue); - if (newValue === '/' && !isSkillPanelOpen && !selectedSkill) { - setIsSkillPanelOpen(true); - } - }} - onPressEnter={e => { - if (!e.shiftKey) { - e.preventDefault(); - handleStart(); - } - }} - placeholder={ - t('ask_data_question') || - 'Ask a question about your database, upload a CSV, or generate a report...' - } - autoSize={{ minRows: 2, maxRows: 6 }} - className='flex-1 resize-none !border-none !shadow-none !bg-transparent px-0 py-2' - style={{ backgroundColor: 'transparent' }} - /> - {/* Toolbar Row */} -
-
- {/* Add Button */} - -
Upload File
- - ), - icon: , - }, - { - key: 'database', - label: 'Select Data Source', - icon: , - onClick: () => setIsDbModalOpen(true), - }, - { - key: 'knowledge', - label: 'Select Knowledge Base', - icon: , - onClick: () => setIsKnowledgeModalOpen(true), - }, - ], - }} - trigger={['click']} - > - - -
-
- } - > - - - - {/* Skill Selector Button with Badge */} { - {/* Separator dot */} -
{/* Connector Selector Button */} { className='flex-shrink-0 h-9 w-9 transition-all duration-200 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800' /> -
- {/* Voice Button */} - -
)} - {/* Send Button with blue gradient + gloss animation */} - -
-