diff --git a/frontend-web/src/App.tsx b/frontend-web/src/App.tsx index 277f3974..e2c8695c 100644 --- a/frontend-web/src/App.tsx +++ b/frontend-web/src/App.tsx @@ -7,6 +7,7 @@ import { QueryClientProvider } from "@/components/query-client-provider"; import { AuthSyncWatcher } from "@/core/auth/sync"; import { I18nProvider } from "@/core/i18n/context"; import { detectLocale } from "@/core/i18n/locale"; +import { resolveForcedThemeForPath } from "@/core/page-layout/chats-iframe-route"; import { ChatProvider } from "@/contexts/ChatContext"; import { ThemeProvider } from "@/contexts/ThemeContext"; @@ -29,7 +30,7 @@ import { NotFoundPage } from "./pages/NotFoundPage"; function AppProviders({ children }: { children: React.ReactNode }) { const locale = useMemo(() => detectLocale(), []); const location = useLocation(); - const forcedTheme = location.pathname === "/landing" ? "light" : undefined; + const forcedTheme = resolveForcedThemeForPath(location.pathname); return ( { + assert.equal( + isChatsIframeWorkspacePath("/page/workspace/chats_iframe/new"), + true, + ); + assert.equal( + isChatsIframeWorkspacePath("/page/workspace/chats_iframe/thread-123"), + true, + ); + assert.equal(isChatsIframeWorkspacePath("/page/workspace/chats/new"), false); + assert.equal(isChatsIframeWorkspacePath("/landing"), false); +}); + +test("forces light theme for landing and chats_iframe routes only", () => { + assert.equal(resolveForcedThemeForPath("/landing"), "light"); + assert.equal( + resolveForcedThemeForPath("/page/workspace/chats_iframe/new"), + "light", + ); + assert.equal( + resolveForcedThemeForPath("/page/workspace/chats_iframe/thread-123"), + "light", + ); + assert.equal(resolveForcedThemeForPath("/page/workspace/chats/new"), undefined); +}); diff --git a/frontend-web/src/core/page-layout/chats-iframe-route.ts b/frontend-web/src/core/page-layout/chats-iframe-route.ts new file mode 100644 index 00000000..30c6ba78 --- /dev/null +++ b/frontend-web/src/core/page-layout/chats-iframe-route.ts @@ -0,0 +1,16 @@ +export function isChatsIframeWorkspacePath(pathname: string): boolean { + return ( + pathname === "/page/workspace/chats_iframe/new" || + pathname.startsWith("/page/workspace/chats_iframe/") + ); +} + +export function resolveForcedThemeForPath( + pathname: string, +): "light" | undefined { + if (pathname === "/landing" || isChatsIframeWorkspacePath(pathname)) { + return "light"; + } + + return undefined; +} diff --git a/frontend-web/src/pages/PageRoutes.tsx b/frontend-web/src/pages/PageRoutes.tsx index 96400566..1e9b0e43 100644 --- a/frontend-web/src/pages/PageRoutes.tsx +++ b/frontend-web/src/pages/PageRoutes.tsx @@ -3,8 +3,10 @@ import { Navigate, Route, Routes } from "react-router-dom"; import { PageSidebar } from "@/components/page-sidebar"; import { PageSidebarDark } from "@/components/page-sidebar-dark"; +import { isChatsIframeWorkspacePath } from "@/core/page-layout/chats-iframe-route"; import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled"; import { useTheme } from "@/contexts/ThemeContext"; +import { useLocation } from "react-router-dom"; import WorkspaceRoutes from "./WorkspaceRoutes"; import StrategyRoutes from "./StrategyRoutes"; @@ -16,17 +18,20 @@ import NotebookRoutes from "./NotebookRoutes"; function PageLayout() { const [collapsed, setCollapsed] = useState(false); const sidebarEnabled = useMemo(() => readSidebarEnabled(), []); + const location = useLocation(); const { theme } = useTheme(); const isDark = theme === "dark"; + const pageSidebarAllowed = + sidebarEnabled && !isChatsIframeWorkspacePath(location.pathname); // h-full(100%)而非 h-screen(100vh):配合 globals.css 的 html/body/#root height:100% // 高度链,根布局跟随容器高度。全屏下 100% 解析到视口,与 h-screen 等价;无界等嵌套 // 下容器不占满整屏时,高度才能正确收缩而非锁死整屏。 return (
- {sidebarEnabled &&
} + {pageSidebarAllowed &&
}
- {sidebarEnabled && ( + {pageSidebarAllowed && ( isDark ? ( } diff --git a/frontend-web/src/pages/WorkspaceRoutes.tsx b/frontend-web/src/pages/WorkspaceRoutes.tsx index 462b5d5b..bb7df093 100644 --- a/frontend-web/src/pages/WorkspaceRoutes.tsx +++ b/frontend-web/src/pages/WorkspaceRoutes.tsx @@ -67,6 +67,7 @@ export function WorkspaceLayout({ children, pageSidebarCollapsed, pageSidebarHidden = false, + workspaceSidebarHidden = false, headerHidden = false, compactWorkspaceSidebar = false, wrapperOverflowHidden = false, @@ -76,6 +77,7 @@ export function WorkspaceLayout({ children: ReactNode; pageSidebarCollapsed: boolean; pageSidebarHidden?: boolean; + workspaceSidebarHidden?: boolean; headerHidden?: boolean; compactWorkspaceSidebar?: boolean; wrapperOverflowHidden?: boolean; @@ -109,7 +111,9 @@ export function WorkspaceLayout({ data-page-sidebar-collapsed={pageSidebarCollapsed} defaultOpen > - + {!workspaceSidebarHidden && ( + + )} @@ -205,7 +209,11 @@ export default function WorkspaceRoutes({ + @@ -217,6 +225,8 @@ export default function WorkspaceRoutes({ element={ diff --git a/offline-backend-20260512/backend/config.yaml b/offline-backend-20260512/backend/config.yaml index 0f1baf03..1967c081 100644 --- a/offline-backend-20260512/backend/config.yaml +++ b/offline-backend-20260512/backend/config.yaml @@ -36,7 +36,6 @@ models: # supports_vision: true # supports_thinking: true - # - name: deepseek-v4-flash # display_name: DeepSeek V4 Flash # use: langchain_openai:ChatOpenAI @@ -118,7 +117,7 @@ models: display_name: deepseek-ai/DeepSeek-V4-Flash use: langchain_openai:ChatOpenAI model: deepseek-ai/DeepSeek-V4-Flash - api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt + api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt base_url: https://api.siliconflow.cn/v1/ max_tokens: 4096 temperature: 1.0 @@ -128,7 +127,7 @@ models: display_name: Pro/MiniMaxAI/MiniMax-M2.5 use: langchain_openai:ChatOpenAI model: Pro/MiniMaxAI/MiniMax-M2.5 - api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt + api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt base_url: https://api.siliconflow.cn/v1/ max_tokens: 4096 temperature: 1.0 @@ -138,7 +137,7 @@ models: display_name: inclusionAI/Ling-flash-2.0 use: langchain_openai:ChatOpenAI model: inclusionAI/Ling-flash-2.0 - api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt + api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt base_url: https://api.siliconflow.cn/v1/ max_tokens: 4096 temperature: 1.0 @@ -148,7 +147,7 @@ models: display_name: deepseek-ai/DeepSeek-V4-Pro use: langchain_openai:ChatOpenAI model: deepseek-ai/DeepSeek-V4-Pro - api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt + api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt base_url: https://api.siliconflow.cn/v1/ max_tokens: 4096 temperature: 1.0 @@ -254,14 +253,14 @@ skills: skill_evolution: enabled: true curator: - enabled: true # 每 24 小时自动清理 + 合并 skill + enabled: true # 每 24 小时自动清理 + 合并 skill interval_hours: 24 review_worker: - enabled: true # 被动复盘开关 - min_tool_calls: 5 # 触发阈值:本轮至少 5 次工具调用,与 prompt 中"≥5 步"硬条件一致 - max_messages: 60 # 传给 LLM 的最大消息条数 - timeout_seconds: 120 # 单次 review 超时(秒) - model_name: # 留空则使用主模型 + enabled: true # 被动复盘开关 + min_tool_calls: 5 # 触发阈值:本轮至少 5 次工具调用,与 prompt 中"≥5 步"硬条件一致 + max_messages: 60 # 传给 LLM 的最大消息条数 + timeout_seconds: 120 # 单次 review 超时(秒) + model_name: # 留空则使用主模型 agents_api: enabled: true @@ -272,31 +271,30 @@ auth_login: # 口令不对直接 401。password 留空则默认口令为 123ewq。仅作用于用户名直登。 username_login: require_password: false - password: "" + password: '' # token 换登录:前端用 ?authToken=上游token 进入,后端调用 token_info_url # (把该 token 直接拼成 Authorization: Bearer 头)换出用户名,再登录; # 用户名不在库里时自动注册。 token_login: enabled: false - token_info_url: "https://ch1.b.uat.4.cn/consumer/login/getTokenInfo" + token_info_url: 'https://ch1.b.uat.4.cn/consumer/login/getTokenInfo' # 已弃用:鉴权头现由前端传入的 token 拼成 "Bearer ",此项不再生效,保留仅兼容。 - service_authorization: "Bearer admin" + service_authorization: 'Bearer admin' timeout_seconds: 10 # 上游是 HTTPS。UAT/内网常用自签或私有 CA 证书,默认 verify=true 会握手失败导致 # 永远 401。此处 uat 域名按自签处理,先关校验;生产请置 true 或配 ca_cert_path。 verify_ssl: false - ca_cert_path: "" # 可选:PEM 格式 CA 包路径,设置后优先生效并启用校验 + ca_cert_path: '' # 可选:PEM 格式 CA 包路径,设置后优先生效并启用校验 # 日志系统入口(仅管理员可见):右下角「设置」菜单中会多出一个「日志系统」入口, # 点击后在新标签页打开下面的 url。enabled 关掉则不显示该入口。 - database: backend: sqlite sqlite_dir: .deer-flow/data - pool_size: 20 # 基础连接池(postgres/mysql 生效;sqlite 忽略) - max_overflow: 20 # 高峰期可临时超出 pool_size 的额外连接数,真实上限 = pool_size + max_overflow - pool_timeout: 30 # 池满时等待空闲连接的秒数,超时报错 + pool_size: 20 # 基础连接池(postgres/mysql 生效;sqlite 忽略) + max_overflow: 20 # 高峰期可临时超出 pool_size 的额外连接数,真实上限 = pool_size + max_overflow + pool_timeout: 30 # 池满时等待空闲连接的秒数,超时报错 echo_sql: false checkpointer: @@ -308,35 +306,38 @@ checkpointer: # users/{user_id}/memory_config.json > .deer-flow/memory_user_overrides.json > 此处 memory: enabled: true - version: v1 # 记忆版本: 'v1'(原版 DeerFlow, memory.json + LLM 自动提取) - # 'v2'(USER.md/MEMORY.md + 可选 Hindsight) - provider: hindsight # 'builtin' | 'hindsight'(外部 provider 名) - # builtin 始终启用,这里只决定 external 选哪个 - # (version=v1 时此字段忽略) + version: + v1 # 记忆版本: 'v1'(原版 DeerFlow, memory.json + LLM 自动提取) + # 'v2'(USER.md/MEMORY.md + 可选 Hindsight) + provider: + hindsight # 'builtin' | 'hindsight'(外部 provider 名) + # builtin 始终启用,这里只决定 external 选哪个 + # (version=v1 时此字段忽略) # V1 专属配置 —— version=v1 时生效 v1: - max_facts: 100 # 最多保留的事实条数,超出时驱逐最低置信度的事实 + max_facts: 100 # 最多保留的事实条数,超出时驱逐最低置信度的事实 fact_confidence_threshold: 0.7 # 事实置信度阈值,低于此值不保存 - debounce_seconds: 30 # 对话结束后延迟多少秒才触发 LLM 提取 - model_name: "" # 用于事实提取的 LLM 模型名;空则继承全局默认模型 - token_budget: 2000 # 注入系统提示词的 token 预算 + debounce_seconds: 30 # 对话结束后延迟多少秒才触发 LLM 提取 + model_name: '' # 用于事实提取的 LLM 模型名;空则继承全局默认模型 + token_budget: 2000 # 注入系统提示词的 token 预算 builtin: enabled: true memory_char_limit: 2200 user_char_limit: 1375 deduplicate_on_load: true - hindsight: # provider=hindsight 时生效,需另行部署 Hindsight 服务 - mode: local_external # 'cloud' | 'local_embedded' | 'local_external' + hindsight: # provider=hindsight 时生效,需另行部署 Hindsight 服务 + mode: local_external # 'cloud' | 'local_embedded' | 'local_external' api_url: http://47.88.25.99:18888 - api_key: "" # 部署 Hindsight 后填,或改成 $HINDSIGHT_API_KEY 并设同名环境变量 - # 注意:写成 $VAR 但环境变量未设置会导致配置加载失败 - bank_id_template: "deerflow-user-{user_id}" - work_bank_id_template: "deerflow-work-{user_id}-{agent_id}" + api_key: + '' # 部署 Hindsight 后填,或改成 $HINDSIGHT_API_KEY 并设同名环境变量 + # 注意:写成 $VAR 但环境变量未设置会导致配置加载失败 + bank_id_template: 'deerflow-user-{user_id}' + work_bank_id_template: 'deerflow-work-{user_id}-{agent_id}' memory_mode: context # 'context' | 'tools' | 'hybrid' - bank_mission: "你是一个中文记忆助手,负责从对话中提炼和存储重要信息。所有记忆内容必须使用中文记录。" - bank_retain_mission: "请从以下对话中提炼重要信息,包括用户的偏好、需求、背景知识和关键事实。所有提炼的内容必须用中文表达。" + bank_mission: '你是一个中文记忆助手,负责从对话中提炼和存储重要信息。所有记忆内容必须使用中文记录。' + bank_retain_mission: '请从以下对话中提炼重要信息,包括用户的偏好、需求、背景知识和关键事实。所有提炼的内容必须用中文表达。' recall_budget: mid # 'low' | 'mid' | 'high' recall_max_tokens: 4096 recall_max_input_chars: 800 @@ -344,8 +345,8 @@ memory: auto_retain: true retain_async: true retain_every_n_turns: 1 - retain_user_prefix: "User" - retain_assistant_prefix: "Assistant" + retain_user_prefix: 'User' + retain_assistant_prefix: 'Assistant' # background_extraction:预留功能,代码尚未实现 —— 仅设计文档存在。 # 待对应版本支持后,取消下面整段注释即可启用(漏记兜底)。 @@ -363,7 +364,7 @@ memory: max_tokens: 2000 include_builtin: true include_hindsight: true - context_tag: "memory-context" # 包裹 Hindsight recall 的标签名 + context_tag: 'memory-context' # 包裹 Hindsight recall 的标签名 security: scan_content: true # 注入/外渗模式扫描 @@ -381,5 +382,5 @@ ai_writing: log_system: enabled: true - url: "https://your-log-system.example.com" - label: "日志系统" + url: 'https://your-log-system.example.com' + label: '日志系统'