Merge branch 'gzy' into 'master'

Gzy

See merge request knowledge-base-bj/deerflow-server!114
This commit is contained in:
g zy committed 2026-06-12 08:33:39 +00:00
commit da56a53a9c
6 files changed
+116 -50

No files matched your search

+2 -1
View File
@@ -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 (
<NextThemesProvider
@@ -0,0 +1,33 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
isChatsIframeWorkspacePath,
resolveForcedThemeForPath,
} from "./chats-iframe-route.ts";
test("matches only workspace chats_iframe routes", () => {
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);
});
@@ -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;
}
+11 -6
View File
@@ -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 (
<div className="h-full flex flex-col bg-muted/20">
{sidebarEnabled && <Header collapsed={collapsed} />}
{pageSidebarAllowed && <Header collapsed={collapsed} />}
<div className="flex flex-1 overflow-hidden">
{sidebarEnabled && (
{pageSidebarAllowed && (
isDark ? (
<PageSidebarDark
collapsed={collapsed}
@@ -41,7 +46,7 @@ function PageLayout() {
)}
<main
className={`flex-1 overflow-hidden ${
!sidebarEnabled
!pageSidebarAllowed
? ""
: isDark
// 深色 sidebar:展开 118px(10 + 98 + 10 gap),收起 = 0 sidebar 完全消失,
@@ -59,9 +64,9 @@ function PageLayout() {
element={
<WorkspaceRoutes
pageSidebarCollapsed={collapsed}
pageSidebarHidden={!sidebarEnabled}
headerHidden={!sidebarEnabled}
compactWorkspaceSidebar={sidebarEnabled}
pageSidebarHidden={!pageSidebarAllowed}
headerHidden={!pageSidebarAllowed}
compactWorkspaceSidebar={pageSidebarAllowed}
pageSidebarDark={isDark}
/>
}
+12 -2
View File
@@ -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
>
<WorkspaceSidebar compactChrome={compactWorkspaceSidebar} />
{!workspaceSidebarHidden && (
<WorkspaceSidebar compactChrome={compactWorkspaceSidebar} />
)}
<SidebarInset
className={cn('min-w-0', insetOverflowHidden && 'overflow-hidden')}
>
@@ -205,7 +209,11 @@ export default function WorkspaceRoutes({
<Route
path="chats_iframe/new"
element={
<WorkspaceLayout {...layoutProps}>
<WorkspaceLayout
{...layoutProps}
pageSidebarHidden
workspaceSidebarHidden
>
<ChatRuntime>
<IframeChatPage />
</ChatRuntime>
@@ -217,6 +225,8 @@ export default function WorkspaceRoutes({
element={
<WorkspaceLayout
{...layoutProps}
pageSidebarHidden
workspaceSidebarHidden
wrapperOverflowHidden
insetOverflowHidden
>
+42 -41
View File
@@ -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> 头)换出用户名,再登录;
# 用户名不在库里时自动注册。
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 <token>",此项不再生效,保留仅兼容。
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: '日志系统'