mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-14 16:08:41 +00:00
_get_firecrawl_client only read api_key from the tool config and ignored base_url, so self-hosted Firecrawl deployments were unreachable: the SDK defaulted to https://api.firecrawl.dev and raised 'No API key provided'. Now base_url is forwarded as api_url; api_url is omitted when unset so cloud behavior is unchanged. Also document the optional self-hosted base_url on both Firecrawl entries in config.example.yaml and reconcile their headers to the fastCRW house style (Cloud requires FIRECRAWL_API_KEY; self-host may need no key.).
2937 lines
143 KiB
YAML
2937 lines
143 KiB
YAML
# Configuration for the DeerFlow application
|
|
#
|
|
# Guidelines:
|
|
# - Copy this file to `config.yaml` and customize it for your environment
|
|
# - The default path of this configuration file is `config.yaml` in the project root.
|
|
# You can set `DEER_FLOW_PROJECT_ROOT` to define that root explicitly, or use
|
|
# `DEER_FLOW_CONFIG_PATH` to point at a specific config file.
|
|
# - Runtime state defaults to `.deer-flow` under the project root. Override it
|
|
# with `DEER_FLOW_HOME` when you need a different writable data directory.
|
|
# - Set `DEER_FLOW_DATE_TIMEZONE` to an IANA timezone name (e.g. `Asia/Shanghai`)
|
|
# when the conversation date injected into agents should follow a zone other
|
|
# than the server's local timezone. It is read at runtime by the date-context
|
|
# middlewares (not a config-schema field), so it can be set on a container
|
|
# without mounting a `config.yaml`.
|
|
# - Environment variables are available for all field values. Example: `api_key: $OPENAI_API_KEY`
|
|
# - The `use` path is a string that looks like "package_name.sub_package_name.module_name:class_name/variable_name".
|
|
|
|
# ============================================================================
|
|
# Config Version (used to detect outdated config files)
|
|
# ============================================================================
|
|
# Bump this number when the config schema changes.
|
|
# Run `make config-upgrade` to merge new fields into your local config.yaml.
|
|
config_version: 43
|
|
|
|
# ============================================================================
|
|
# Logging
|
|
# ============================================================================
|
|
# Log level for deerflow modules (debug/info/warning/error)
|
|
log_level: info
|
|
|
|
# Trace ids are always issued and always returned in the `X-Trace-Id` response
|
|
# header; this block controls log output only — whether records carry a
|
|
# `trace_id` field, and in which format. Off by default because enabling it
|
|
# changes the log format. Restart required (see reload_boundary.py).
|
|
logging:
|
|
enhance:
|
|
enabled: false
|
|
format: text
|
|
|
|
# ============================================================================
|
|
# Agent Extensions
|
|
# ============================================================================
|
|
# Optional AgentMiddleware classes loaded into the lead and subagent runtime
|
|
# middleware chains after built-in runtime middlewares, but before the
|
|
# safety/clarification tail. Missing packages, invalid classes, and broken
|
|
# modules fail at agent creation with an actionable import error; constructor
|
|
# errors fail at agent creation with the underlying exception.
|
|
# The same list applies to both lead and subagent runtimes; lead-only vs
|
|
# subagent-only configuration is not expressible yet. Each entry is a class
|
|
# path or `{class, kwargs}` for constructor arguments. kwargs values must be
|
|
# JSON types (object, array, string, number, boolean, or null); YAML dates
|
|
# and timestamps are coerced to ISO strings so they match JSON. Treat these
|
|
# files as trusted operator config because middleware classes execute code.
|
|
# Uncomment this block to define middlewares in config.yaml. Leaving it commented
|
|
# lets extensions_config.json remain the source of truth for this fixed-slot
|
|
# (not deprecated) middleware list. Packaged plugins use the `plugins:` block below.
|
|
# extensions:
|
|
# middlewares:
|
|
# - my_company.deerflow_middlewares:DomainGuardMiddleware
|
|
# - class: my_company.deerflow_middlewares:LatencyStampingMiddleware
|
|
# kwargs:
|
|
# header: X-DeerFlow-Latency
|
|
|
|
# ============================================================================
|
|
# Tracing / Observability (Monocle)
|
|
# ============================================================================
|
|
# Optional agent tracing via Monocle. Configured through environment variables
|
|
# (MONOCLE_TRACING, MONOCLE_EXPORTERS, OKAHU_API_KEY — like LangSmith/Langfuse),
|
|
# not config.yaml keys, and OFF by default. See README.md → "Monocle Tracing"
|
|
# for setup, what each exporter captures, and where the trace data goes.
|
|
|
|
# ============================================================================
|
|
# Token Usage
|
|
# ============================================================================
|
|
# Enable token usage collection and display.
|
|
# When enabled, DeerFlow records input/output/total tokens per model call
|
|
# and shows usage metadata in the workspace UI when providers return it.
|
|
token_usage:
|
|
enabled: true
|
|
|
|
# ============================================================================
|
|
# Token Budget — Per-run token limits
|
|
# ============================================================================
|
|
# Prevents runaway API costs by enforcing hard token limits per run.
|
|
# When warn_threshold is crossed, the agent receives an in-context warning.
|
|
# When hard_stop_threshold is crossed, tool_calls are stripped and the agent
|
|
# is forced to produce a final answer immediately.
|
|
token_budget:
|
|
enabled: false # Set to true to activate budget enforcement
|
|
max_tokens: 200000 # Total token limit (input + output) per run
|
|
max_input_tokens: null # Optional separate input-only limit
|
|
max_output_tokens: null # Optional separate output-only limit
|
|
warn_threshold: 0.8 # Warn at 80% of the budget
|
|
hard_stop_threshold: 1.0 # Force stop at 100% of the budget
|
|
|
|
# ============================================================================
|
|
# Recursion Limits
|
|
# ============================================================================
|
|
# A run's recursion_limit caps the number of LangGraph super-steps (each is at
|
|
# least one LLM call). Clients may override the default per request; invalid or
|
|
# non-positive client values fall back to this configured default.
|
|
recursion_limit: 100
|
|
|
|
# The Gateway never trusts a configured or client-supplied value above this
|
|
# ceiling, preventing runaway API cost / DoS. Raise it only if you legitimately
|
|
# run very deeply nested subagent graphs.
|
|
max_recursion_limit: 1000
|
|
|
|
|
|
# ============================================================================
|
|
# Models Configuration
|
|
# ============================================================================
|
|
# Configure available LLM models for the agent to use.
|
|
#
|
|
# Two token fields look similar but mean different things:
|
|
# - `max_tokens` is the per-call OUTPUT cap passed to the provider.
|
|
# - `context_window` is a positive integer for the total context capacity
|
|
# (prompt + completion). It drives the real-time "% context used" indicator
|
|
# in the chat UI and feeds the model's langchain profile, which fraction-based
|
|
# summarization triggers (`summarization.trigger: [{type: fraction, ...}]`)
|
|
# resolve their thresholds from. Third-party OpenAI-compatible models carry no
|
|
# built-in profile, so without `context_window` a fraction trigger degrades
|
|
# (dropped with a warning) instead of crashing the agent build.
|
|
# Leave `context_window` unset if the provider limit is unknown; the percentage
|
|
# will not render. Verify configured values against the provider's model docs.
|
|
#
|
|
# Optional per-model pricing (powers the real-cost display on the workspace
|
|
# console). Add a `pricing` block to any model entry; use ONE currency across
|
|
# all models. Mixed currencies disable cost reporting to prevent invalid sums.
|
|
# Prices are per one million tokens.
|
|
#
|
|
# pricing:
|
|
# currency: CNY # ISO code shown in the console (CNY, USD, ...)
|
|
# input_per_million: 8.0 # price per 1M input tokens (cache miss)
|
|
# output_per_million: 32.0 # price per 1M output tokens
|
|
# input_cache_hit_per_million: 0.8 # price per 1M cache-hit input tokens
|
|
# # (optional; omit → hits billed at miss price)
|
|
|
|
models:
|
|
# Example: Volcengine (Doubao) model
|
|
# - name: doubao-seed-1.8
|
|
# display_name: Doubao-Seed-1.8
|
|
# use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
|
|
# model: doubao-seed-1-8-251228
|
|
# api_base: https://ark.cn-beijing.volces.com/api/v3
|
|
# api_key: $VOLCENGINE_API_KEY
|
|
# timeout: 600.0
|
|
# max_retries: 2
|
|
# context_window: 262144 # Total prompt + completion capacity
|
|
# supports_thinking: true
|
|
# supports_vision: true
|
|
# supports_reasoning_effort: true
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: Volcengine Coding Plan (one key, multi-vendor gateway)
|
|
# The Coding Plan endpoint (/api/coding/v3) lets you access models from
|
|
# Doubao, GLM, DeepSeek, Kimi, and MiniMax with a single API key.
|
|
# Each model may differ in thinking/vision support - configure per-model.
|
|
#
|
|
# - name: glm-5.2-cp
|
|
# display_name: GLM-5.2 (Coding Plan)
|
|
# use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
|
|
# model: glm-5.2
|
|
# api_base: https://ark.cn-beijing.volces.com/api/coding/v3
|
|
# api_key: $VOLCENGINE_API_KEY
|
|
# timeout: 600.0
|
|
# max_retries: 2
|
|
# supports_thinking: true
|
|
# supports_vision: false
|
|
# supports_reasoning_effort: true
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
#
|
|
# - name: deepseek-v4-pro-cp
|
|
# display_name: DeepSeek-V4-Pro (Coding Plan)
|
|
# use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
|
|
# model: deepseek-v4-pro
|
|
# api_base: https://ark.cn-beijing.volces.com/api/coding/v3
|
|
# api_key: $VOLCENGINE_API_KEY
|
|
# timeout: 600.0
|
|
# max_retries: 2
|
|
# supports_thinking: true
|
|
# supports_vision: false
|
|
# supports_reasoning_effort: true
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: Z.AI GLM-5.3-Flash (required-thinking workaround)
|
|
#
|
|
# GLM-5.3-Flash cannot disable thinking and only accepts low/high/max effort.
|
|
# DeerFlow's current generic UI may request disabled thinking or emit
|
|
# minimal/medium, so this profile keeps thinking enabled unconditionally and
|
|
# suppresses generic effort forwarding. Keep the thinking block in the base
|
|
# extra_body: moving it to when_thinking_enabled would let background calls
|
|
# synthesize the invalid thinking.type=disabled + reasoning_effort=minimal pair.
|
|
# clear_thinking=true is intentional for this compatibility workaround: it
|
|
# avoids requiring exact historical reasoning replay after summarization.
|
|
#
|
|
# - name: glm-5.3-flash
|
|
# display_name: GLM-5.3-Flash
|
|
# use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
|
|
# model: glm-5.3-flash
|
|
# api_base: https://api.z.ai/api/paas/v4
|
|
# api_key: $ZAI_API_KEY
|
|
# timeout: 600.0
|
|
# max_retries: 2
|
|
# temperature: 1.0
|
|
# top_p: 0.95
|
|
# max_tokens: 131072
|
|
# context_window: 1000000
|
|
# supports_thinking: true
|
|
# supports_reasoning_effort: false
|
|
# supports_vision: true
|
|
# stream_usage: false
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# clear_thinking: true
|
|
# tool_stream: true
|
|
|
|
# Example: OpenAI model
|
|
# - name: gpt-4
|
|
# display_name: GPT-4
|
|
# use: langchain_openai:ChatOpenAI
|
|
# model: gpt-4
|
|
# api_key: $OPENAI_API_KEY # Use environment variable
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096 # Per-call output cap
|
|
# context_window: 128000 # Total prompt + completion capacity
|
|
# temperature: 0.7
|
|
# supports_vision: true # Enable vision support for view_image tool
|
|
|
|
# Example: OpenAI Responses API model
|
|
# - name: gpt-5-responses
|
|
# display_name: GPT-5 (Responses API)
|
|
# use: langchain_openai:ChatOpenAI
|
|
# model: gpt-5
|
|
# api_key: $OPENAI_API_KEY
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# use_responses_api: true
|
|
# output_version: responses/v1
|
|
# # Optional, forwarded to ChatOpenAI as-is (default false): send only the new
|
|
# # turn plus previous_response_id instead of replaying the full history. The
|
|
# # chained context is still billed as input tokens, and any client-side
|
|
# # history rewrite (e.g. blocked-write payload elision) is only honored when
|
|
# # the history is replayed.
|
|
# use_previous_response_id: false
|
|
# context_window: 400000
|
|
# supports_vision: true
|
|
|
|
# Example: Ollama (native provider — preserves thinking/reasoning content)
|
|
#
|
|
# IMPORTANT: Use langchain_ollama:ChatOllama instead of langchain_openai:ChatOpenAI
|
|
# for Ollama models. The OpenAI-compatible endpoint (/v1/chat/completions) does NOT
|
|
# return reasoning_content as a separate field — thinking content is either flattened
|
|
# into <think> tags or dropped entirely (ollama/ollama#15293). The native Ollama API
|
|
# (/api/chat) correctly separates thinking from response content.
|
|
#
|
|
# Install: cd backend && uv pip install 'deerflow-harness[ollama]'
|
|
#
|
|
# - name: qwen3-local
|
|
# display_name: Qwen3 32B (Ollama)
|
|
# use: langchain_ollama:ChatOllama
|
|
# model: qwen3:32b
|
|
# base_url: http://localhost:11434 # No /v1 suffix — uses native /api/chat
|
|
# num_predict: 8192
|
|
# temperature: 0.7
|
|
# reasoning: true # Passes think:true to Ollama native API
|
|
# context_window: 32768 # Match the context length served by Ollama
|
|
# supports_thinking: true
|
|
# supports_vision: false
|
|
#
|
|
# - name: gemma4-local
|
|
# display_name: Gemma 4 27B (Ollama)
|
|
# use: langchain_ollama:ChatOllama
|
|
# model: gemma4:27b
|
|
# base_url: http://localhost:11434
|
|
# num_predict: 8192
|
|
# temperature: 0.7
|
|
# reasoning: true
|
|
# context_window: 32768
|
|
# supports_thinking: true
|
|
# supports_vision: true
|
|
#
|
|
# For Docker deployments, use host.docker.internal instead of localhost:
|
|
# base_url: http://host.docker.internal:11434
|
|
|
|
# Example: Anthropic Claude model (with extended thinking)
|
|
# supports_thinking: true is required — without it, DeerFlow silently falls
|
|
# back to non-thinking mode even when the UI thinking toggle is on.
|
|
# budget_tokens is required by the Anthropic API when thinking.type=enabled
|
|
# (no server default; min 1024; must be less than max_tokens).
|
|
# - name: claude-sonnet-4
|
|
# display_name: Claude Sonnet 4
|
|
# use: langchain_anthropic:ChatAnthropic
|
|
# model: claude-sonnet-4-20250514
|
|
# api_key: $ANTHROPIC_API_KEY
|
|
# default_request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 16000 # Per-call output cap
|
|
# context_window: 200000 # Total prompt + completion capacity
|
|
# supports_vision: true
|
|
# supports_thinking: true
|
|
# when_thinking_enabled:
|
|
# thinking:
|
|
# type: enabled
|
|
# budget_tokens: 4096 # required; min 1024; must be < max_tokens
|
|
# when_thinking_disabled:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: Google Gemini model (native SDK, no thinking support)
|
|
# - name: gemini-2.5-pro
|
|
# display_name: Gemini 2.5 Pro
|
|
# use: langchain_google_genai:ChatGoogleGenerativeAI
|
|
# model: gemini-2.5-pro
|
|
# gemini_api_key: $GEMINI_API_KEY
|
|
# timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 8192
|
|
# context_window: 1048576
|
|
# supports_vision: true
|
|
|
|
# Example: Gemini model via OpenAI-compatible gateway (with thinking support)
|
|
# Use PatchedChatOpenAI so that tool-call thought_signature values on tool_calls
|
|
# are preserved across multi-turn tool-call conversations — required by the
|
|
# Gemini API when thinking is enabled. See:
|
|
# https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures
|
|
# - name: gemini-2.5-pro-thinking
|
|
# display_name: Gemini 2.5 Pro (Thinking)
|
|
# use: deerflow.models.patched_openai:PatchedChatOpenAI
|
|
# model: google/gemini-2.5-pro-preview # model name as expected by your gateway
|
|
# api_key: $GEMINI_API_KEY
|
|
# base_url: https://<your-openai-compat-gateway>/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 16384
|
|
# context_window: 1048576
|
|
# supports_thinking: true
|
|
# supports_vision: true
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: Xiaomi MiMo model (with thinking support)
|
|
# MiMo thinking mode returns reasoning_content and requires that field to be
|
|
# replayed on historical assistant messages in multi-turn agent/tool-call
|
|
# conversations. Use PatchedChatMiMo instead of plain ChatOpenAI.
|
|
# Use https://api.xiaomimimo.com/v1 with pay-as-you-go `sk-...` keys.
|
|
# Use your Token Plan regional URL (for example
|
|
# https://token-plan-cn.xiaomimimo.com/v1) with Token Plan `tp-...` keys.
|
|
# PatchedChatMiMo is model-id agnostic; use it for every MiMo thinking model
|
|
# entry you configure (for example mimo-v2.5-pro, mimo-v2.5, mimo-v2-pro,
|
|
# mimo-v2-omni, or mimo-v2-flash), including models referenced by subagent
|
|
# model overrides.
|
|
# See: https://platform.xiaomimimo.com/docs/en-US/usage-guide/passing-back-reasoning_content
|
|
# - name: mimo-v2.5-pro
|
|
# display_name: MiMo V2.5 Pro
|
|
# use: deerflow.models.patched_mimo:PatchedChatMiMo
|
|
# model: mimo-v2.5-pro
|
|
# api_key: $MIMO_API_KEY
|
|
# base_url: https://api.xiaomimimo.com/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 8192
|
|
# supports_thinking: true
|
|
# supports_vision: false
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: DeepSeek V4 model (with thinking support)
|
|
# - name: deepseek-v4
|
|
# display_name: DeepSeek V4 (Thinking)
|
|
# use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
|
|
# model: deepseek-v4-pro
|
|
# api_key: $DEEPSEEK_API_KEY
|
|
# timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 8192
|
|
# supports_thinking: true
|
|
# supports_vision: false # DeepSeek V4 does not support vision
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: Kimi K2.5 model
|
|
# - name: kimi-k2.5
|
|
# display_name: Kimi K2.5
|
|
# use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
|
|
# model: kimi-k2.5
|
|
# api_base: https://api.moonshot.cn/v1
|
|
# api_key: $MOONSHOT_API_KEY
|
|
# timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 32768
|
|
# supports_thinking: true
|
|
# supports_vision: true # Check your specific model's capabilities
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: Novita AI (OpenAI-compatible)
|
|
# Novita provides an OpenAI-compatible API with competitive pricing
|
|
# See: https://novita.ai
|
|
# - name: novita-deepseek-v3.2
|
|
# display_name: Novita DeepSeek V3.2
|
|
# use: langchain_openai:ChatOpenAI
|
|
# model: deepseek/deepseek-v3.2
|
|
# api_key: $NOVITA_API_KEY
|
|
# base_url: https://api.novita.ai/openai
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# temperature: 0.7
|
|
# supports_thinking: true
|
|
# supports_vision: true
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: StepFun (阶跃星辰) reasoning models
|
|
# StepFun provides OpenAI-compatible API with reasoning models.
|
|
# With reasoning_format: deepseek-style, the API returns reasoning_content
|
|
# (same field as DeepSeek), which must be replayed on historical assistant
|
|
# messages in multi-turn tool-call conversations.
|
|
# Use PatchedChatStepFun instead of plain ChatOpenAI.
|
|
# Docs: https://platform.stepfun.com/docs/api-reference/chat-completions
|
|
# - name: step-3.7-flash
|
|
# display_name: Step 3.7 Flash
|
|
# use: deerflow.models.patched_stepfun:PatchedChatStepFun
|
|
# model: step-3.7-flash
|
|
# api_key: $STEPFUN_API_KEY
|
|
# base_url: https://api.stepfun.com/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# supports_thinking: true
|
|
# supports_reasoning_effort: true
|
|
# supports_vision: true
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# reasoning_format: deepseek-style
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# reasoning_format: deepseek-style
|
|
|
|
# Example: MiniMax (OpenAI-compatible) - International Edition
|
|
# MiniMax provides high-performance models with 512K context window and 128K max output
|
|
# Docs: https://platform.minimax.io/docs/api-reference/text-openai-api
|
|
# - name: minimax-m3
|
|
# display_name: MiniMax M3
|
|
# use: deerflow.models.patched_minimax:PatchedChatMiniMax
|
|
# model: MiniMax-M3
|
|
# api_key: $MINIMAX_API_KEY
|
|
# base_url: https://api.minimax.io/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# temperature: 1.0 # MiniMax requires temperature in (0.0, 1.0]
|
|
# supports_vision: true
|
|
# supports_thinking: true
|
|
# # PatchedChatMiniMax is the MiniMax adapter: it enables reasoning_split and
|
|
# # maps MiniMax's structured reasoning into reasoning_content (the field
|
|
# # DeerFlow understands), and it strips the per-message `name` field that
|
|
# # DeerFlow middlewares attach — MiniMax rejects requests whose user-message
|
|
# # names differ with "user name must be consistent (2013)". Declare the
|
|
# # thinking toggle so non-thinking paths (flash mode, follow-up suggestions,
|
|
# # title/memory generation) truly disable reasoning instead of spending
|
|
# # tokens on it.
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: adaptive
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# NOTE: M2.x models always think — passing thinking:{type:disabled} has no
|
|
# effect (per MiniMax docs), so the toggle above is omitted for M2.7. The
|
|
# follow-up-suggestions endpoint strips inline <think> defensively regardless.
|
|
# Still use the PatchedChatMiniMax adapter: it strips the per-message `name`
|
|
# field DeerFlow middlewares attach, which MiniMax otherwise rejects with
|
|
# "user name must be consistent (2013)".
|
|
# - name: minimax-m2.7
|
|
# display_name: MiniMax M2.7
|
|
# use: deerflow.models.patched_minimax:PatchedChatMiniMax
|
|
# model: MiniMax-M2.7
|
|
# api_key: $MINIMAX_API_KEY
|
|
# base_url: https://api.minimax.io/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# temperature: 1.0 # MiniMax requires temperature in (0.0, 1.0]
|
|
# supports_vision: false # M2.7 is text-only; M3 supports vision
|
|
# supports_thinking: true
|
|
|
|
# - name: minimax-m2.7-highspeed
|
|
# display_name: MiniMax M2.7 Highspeed
|
|
# use: deerflow.models.patched_minimax:PatchedChatMiniMax
|
|
# model: MiniMax-M2.7-highspeed
|
|
# api_key: $MINIMAX_API_KEY
|
|
# base_url: https://api.minimax.io/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# temperature: 1.0 # MiniMax requires temperature in (0.0, 1.0]
|
|
# supports_vision: false # M2.7 is text-only; M3 supports vision
|
|
# supports_thinking: true
|
|
|
|
# Example: MiniMax (OpenAI-compatible) - CN 中国区用户
|
|
# MiniMax provides high-performance models with 512K context window and 128K max output
|
|
# Docs: https://platform.minimaxi.com/docs/api-reference/text-openai-api
|
|
# - name: minimax-m3
|
|
# display_name: MiniMax M3
|
|
# use: deerflow.models.patched_minimax:PatchedChatMiniMax
|
|
# model: MiniMax-M3
|
|
# api_key: $MINIMAX_API_KEY
|
|
# base_url: https://api.minimaxi.com/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# temperature: 1.0 # MiniMax requires temperature in (0.0, 1.0]
|
|
# supports_vision: true
|
|
# supports_thinking: true
|
|
# # PatchedChatMiniMax is the MiniMax adapter: it enables reasoning_split and
|
|
# # maps MiniMax's structured reasoning into reasoning_content (the field
|
|
# # DeerFlow understands), and it strips the per-message `name` field that
|
|
# # DeerFlow middlewares attach — MiniMax rejects requests whose user-message
|
|
# # names differ with "user name must be consistent (2013)". Declare the
|
|
# # thinking toggle so non-thinking paths (flash mode, follow-up suggestions,
|
|
# # title/memory generation) truly disable reasoning instead of spending
|
|
# # tokens on it.
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: adaptive
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# NOTE: M2.x models always think — passing thinking:{type:disabled} has no
|
|
# effect (per MiniMax docs), so the toggle above is omitted for M2.7. The
|
|
# follow-up-suggestions endpoint strips inline <think> defensively regardless.
|
|
# Still use the PatchedChatMiniMax adapter: it strips the per-message `name`
|
|
# field DeerFlow middlewares attach, which MiniMax otherwise rejects with
|
|
# "user name must be consistent (2013)".
|
|
# - name: minimax-m2.7
|
|
# display_name: MiniMax M2.7
|
|
# use: deerflow.models.patched_minimax:PatchedChatMiniMax
|
|
# model: MiniMax-M2.7
|
|
# api_key: $MINIMAX_API_KEY
|
|
# base_url: https://api.minimaxi.com/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# temperature: 1.0 # MiniMax requires temperature in (0.0, 1.0]
|
|
# supports_vision: false # M2.7 is text-only; M3 supports vision
|
|
# supports_thinking: true
|
|
|
|
# - name: minimax-m2.7-highspeed
|
|
# display_name: MiniMax M2.7 Highspeed
|
|
# use: deerflow.models.patched_minimax:PatchedChatMiniMax
|
|
# model: MiniMax-M2.7-highspeed
|
|
# api_key: $MINIMAX_API_KEY
|
|
# base_url: https://api.minimaxi.com/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 4096
|
|
# temperature: 1.0 # MiniMax requires temperature in (0.0, 1.0]
|
|
# supports_vision: false # M2.7 is text-only; M3 supports vision
|
|
# supports_thinking: true
|
|
|
|
# Example: OpenRouter (OpenAI-compatible)
|
|
# OpenRouter models use the same ChatOpenAI + base_url pattern as other OpenAI-compatible gateways.
|
|
# - name: openrouter-gemini-2.5-flash
|
|
# display_name: Gemini 2.5 Flash (OpenRouter)
|
|
# use: langchain_openai:ChatOpenAI
|
|
# model: google/gemini-2.5-flash-preview
|
|
# api_key: $OPENAI_API_KEY
|
|
# base_url: https://openrouter.ai/api/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 8192
|
|
# temperature: 0.7
|
|
|
|
# Example: Atlas Cloud (OpenAI-compatible)
|
|
# Atlas Cloud exposes a single OpenAI-compatible endpoint in front of many open
|
|
# models (DeepSeek, Qwen, Kimi, GLM, MiniMax, Llama, ...), so it uses the same
|
|
# ChatOpenAI + base_url pattern as other OpenAI-compatible gateways.
|
|
# Browse model ids at https://api.atlascloud.ai/v1/models — see https://atlascloud.ai
|
|
# - name: atlascloud-deepseek-v3.2
|
|
# display_name: DeepSeek V3.2 (Atlas Cloud)
|
|
# use: langchain_openai:ChatOpenAI
|
|
# model: deepseek-ai/DeepSeek-V3.2-Exp
|
|
# api_key: $ATLASCLOUD_API_KEY
|
|
# base_url: https://api.atlascloud.ai/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 8192
|
|
# temperature: 0.7
|
|
# supports_vision: false
|
|
#
|
|
# For reasoning models on Atlas Cloud (e.g. a Qwen3 *-thinking id), use the
|
|
# patched OpenAI-compatible adapter so reasoning_content is replayed across
|
|
# multi-turn tool-call conversations:
|
|
# - name: atlascloud-qwen3-thinking
|
|
# display_name: Qwen3 235B Thinking (Atlas Cloud)
|
|
# use: deerflow.models.patched_openai:PatchedChatOpenAI
|
|
# model: qwen/qwen3-235b-a22b-thinking-2507
|
|
# api_key: $ATLASCLOUD_API_KEY
|
|
# base_url: https://api.atlascloud.ai/v1
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 8192
|
|
# supports_thinking: true
|
|
# supports_vision: false
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: enabled
|
|
# when_thinking_disabled:
|
|
# extra_body:
|
|
# thinking:
|
|
# type: disabled
|
|
|
|
# Example: vLLM 0.19.0 (OpenAI-compatible, with reasoning toggle)
|
|
# DeerFlow's vLLM provider preserves vLLM reasoning across tool-call turns and
|
|
# toggles Qwen-style reasoning by writing
|
|
# extra_body.chat_template_kwargs.enable_thinking=true/false.
|
|
# Some reasoning models also require the server to be started with
|
|
# `vllm serve ... --reasoning-parser <parser>`.
|
|
# - name: qwen3-32b-vllm
|
|
# display_name: Qwen3 32B (vLLM)
|
|
# use: deerflow.models.vllm_provider:VllmChatModel
|
|
# model: Qwen/Qwen3-32B
|
|
# api_key: $VLLM_API_KEY
|
|
# base_url: http://localhost:8000/v1
|
|
# # Enable only when the endpoint reports cumulative usage on every stream chunk.
|
|
# cumulative_stream_usage: true
|
|
# request_timeout: 600.0
|
|
# max_retries: 2
|
|
# max_tokens: 8192
|
|
# supports_thinking: true
|
|
# supports_vision: false
|
|
# when_thinking_enabled:
|
|
# extra_body:
|
|
# chat_template_kwargs:
|
|
# enable_thinking: true
|
|
|
|
|
|
# Example: Qwen3-Coder deployed on MindIE Engine
|
|
# - name: Qwen3_Coder_480B_MindIE
|
|
# display_name: Qwen3-Coder-480B (MindIE)
|
|
# use: deerflow.models.mindie_provider:MindIEChatModel
|
|
# model: Qwen3-Coder-480B-A35B-Instruct-Client
|
|
# base_url: http://localhost:8989/v1
|
|
# api_key: $OPENAI_API_KEY
|
|
# temperature: 0
|
|
# max_retries: 1
|
|
# supports_thinking: false
|
|
# supports_vision: false
|
|
# supports_reasoning_effort: false
|
|
# # --- Advanced Network Settings ---
|
|
# # Due to MindIE's streaming limitations with tool calling, the provider
|
|
# # uses mock-streaming (awaiting full generation). Extended timeouts are required.
|
|
# read_timeout: 900.0 # 15 minutes to prevent drops during long document generation
|
|
# connect_timeout: 30.0
|
|
# write_timeout: 60.0
|
|
# pool_timeout: 30.0
|
|
|
|
# ============================================================================
|
|
# Tool Groups Configuration
|
|
# ============================================================================
|
|
# Define groups of tools for organization and access control
|
|
|
|
tool_groups:
|
|
- name: web
|
|
- name: file:read
|
|
- name: file:write
|
|
- name: bash
|
|
- name: browser
|
|
- name: knowledge
|
|
|
|
# ============================================================================
|
|
# Tools Configuration
|
|
# ============================================================================
|
|
# Configure available tools for the agent to use
|
|
|
|
tools:
|
|
# RAGFlow knowledge retrieval (read-only). Uncomment this single entry.
|
|
# `datasets` is optional. Omit it to list every tenant-visible dataset at
|
|
# search time; an explicit `datasets: []` is invalid. Empty datasets are
|
|
# skipped; searchable datasets are grouped by
|
|
# embedding model and retrieved with at most four groups in parallel. Group
|
|
# ranks are interleaved under the global `page_size` limit; scores are omitted
|
|
# when multiple groups are searched because they are not comparable.
|
|
# Configure stable IDs only to restrict scope. IDs and catalog listing never
|
|
# reach the model.
|
|
# - name: knowledge_search
|
|
# group: knowledge
|
|
# use: deerflow.community.ragflow.tools:knowledge_search_tool
|
|
# base_url: http://localhost:9380 # Docker: use a backend-reachable URL
|
|
# api_key: $RAGFLOW_API_KEY
|
|
# datasets: # Optional operator-controlled allowlist
|
|
# - 0123456789abcdef0123456789abcdef # Replace with a RAGFlow dataset ID
|
|
# timeout: 30
|
|
# page_size: 8
|
|
# similarity_threshold: 0.2
|
|
# vector_similarity_weight: 0.3
|
|
# top_k: 256
|
|
# max_chars_per_chunk: 800
|
|
# max_total_chars: 8000
|
|
|
|
# LightRAG knowledge retrieval (read-only). Alternative provider for the same
|
|
# knowledge_search tool; uncomment this entry instead of the RAGFlow one —
|
|
# duplicate names keep the first entry, so configure exactly one. Requires
|
|
# LightRAG v1.4.9+ (response envelope and citation fields). Searches the deployment's
|
|
# single indexed workspace through the data retrieval endpoint (no LLM
|
|
# generation inside LightRAG). `api_key` is optional; only omit it for
|
|
# loopback/trusted-network servers — when auth is enabled the key is sent as
|
|
# the X-API-Key header. `mode` picks the retrieval strategy: naive, local,
|
|
# global, hybrid, or mix (default mix, matching LightRAG's own default).
|
|
# Internal chunk and reference identifiers never reach the model.
|
|
# - name: knowledge_search
|
|
# group: knowledge
|
|
# use: deerflow.community.lightrag.tools:knowledge_search_tool
|
|
# base_url: http://localhost:9621 # Docker: use a backend-reachable URL
|
|
# api_key: $LIGHTRAG_API_KEY # Omit only for unauthenticated trusted servers
|
|
# mode: mix
|
|
# timeout: 30
|
|
# top_k: 60
|
|
# chunk_top_k: 8 # Optional; omit to use the server default
|
|
# max_chars_per_chunk: 800
|
|
# max_total_chars: 8000
|
|
|
|
# Web search tool (uses DuckDuckGo, no API key required)
|
|
- name: web_search
|
|
group: web
|
|
use: deerflow.community.ddg_search.tools:web_search_tool
|
|
max_results: 5
|
|
# backend: auto # DDGS backend(s): auto, duckduckgo, brave, wikipedia, etc.
|
|
# region: wt-wt # wt-wt is normalized for Wikipedia when backend includes auto/all/wikipedia.
|
|
# safesearch: moderate # on, moderate, off
|
|
|
|
# Web search tool (uses SearXNG, self-hosted, no API key required)
|
|
# SearXNG is a free internet metasearch engine which aggregates results from
|
|
# various search services. Deploy your own instance: https://github.com/searxng/searxng
|
|
# For Docker deployments, use the Docker service name instead of localhost.
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.searxng.tools:web_search_tool
|
|
# base_url: http://localhost:8088 # SearXNG instance URL (default: :8088; Docker: http://searxng:8080)
|
|
# max_results: 5 # Maximum number of search results
|
|
|
|
# Web search tool (uses Serper - Google Search API, requires SERPER_API_KEY)
|
|
# Serper provides real-time Google Search results. Sign up at https://serper.dev
|
|
# Note: set SERPER_API_KEY in your environment before starting the app.
|
|
# Avoid putting literal API keys in config.yaml; use the $VAR form instead.
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.serper.tools:web_search_tool
|
|
# max_results: 5 # capped at 10 by the Serper provider
|
|
# # api_key: $SERPER_API_KEY # Optional explicit env-var reference
|
|
|
|
# Web search tool (uses Serply - Google Search API, requires SERPLY_API_KEY)
|
|
# Serply returns live Google results and covers Google News and Google Scholar
|
|
# with the same key. Sign up at https://serply.io (docs: https://serply.io/docs)
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.serply.tools:web_search_tool
|
|
# max_results: 5 # Serply accepts 1-100 per request
|
|
# # vertical: search # search (default), news, or scholar
|
|
# # gl: us # Optional country code for the results
|
|
# # hl: en # Optional interface language
|
|
# # api_key: $SERPLY_API_KEY # Optional if the env var is set
|
|
|
|
# Web search tool (uses Brave Search API, requires BRAVE_SEARCH_API_KEY)
|
|
# Brave Search returns results from an independent index. Sign up at
|
|
# https://brave.com/search/api/ to get a key. Unlike the DuckDuckGo
|
|
# `backend: brave` option above, this calls the official Brave API directly.
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.brave.tools:web_search_tool
|
|
# max_results: 5 # Capped at 20 by the Brave Search API
|
|
# # api_key: $BRAVE_SEARCH_API_KEY # Optional if the env var is set
|
|
|
|
# Web search tool (requires Tavily API key)
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.tavily.tools:web_search_tool
|
|
# max_results: 5
|
|
# # api_key: $TAVILY_API_KEY # Set if needed
|
|
|
|
# Web search tool (uses InfoQuest, requires InfoQuest API key)
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.infoquest.tools:web_search_tool
|
|
# # Used to limit the scope of search results, only returns content within the specified time range. Set to -1 to disable time filtering
|
|
# search_time_range: 10
|
|
|
|
# Web search tool (uses Tencent Cloud Web Search API, requires a service API key)
|
|
# Create a service API key in the Tencent Cloud WSA console, then set
|
|
# TENCENTCLOUD_WSA_APIKEY in the Gateway environment. Do not use Tencent Cloud
|
|
# SecretId/SecretKey for this provider.
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.tencent_wsa.tools:web_search_tool
|
|
# max_results: 5 # 1-50; values above 10 request Tencent's Cnt option
|
|
# # (requires a Tencent plan that supports Cnt)
|
|
# # mode: 0 # Optional: 0=web, 1=VR, 2=mixed
|
|
# # api_key: $TENCENTCLOUD_WSA_APIKEY # Optional explicit env-var reference
|
|
|
|
# Web search tool (uses Exa, requires EXA_API_KEY)
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.exa.tools:web_search_tool
|
|
# max_results: 5
|
|
# search_type: auto # Options: auto, neural, keyword
|
|
# contents_max_characters: 1000
|
|
# # api_key: $EXA_API_KEY
|
|
|
|
# Web search tool (uses Firecrawl. Cloud requires FIRECRAWL_API_KEY; self-host may need no key.)
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.firecrawl.tools:web_search_tool
|
|
# max_results: 5
|
|
# # api_key: $FIRECRAWL_API_KEY
|
|
# # base_url: http://<host>:3002 # optional: self-hosted Firecrawl; no cloud API key needed
|
|
|
|
# Web search tool (uses GroundRoute, requires GROUNDROUTE_API_KEY)
|
|
# GroundRoute is a meta search layer: one API in front of six engines (Serper,
|
|
# Brave, Exa, Tavily, Firecrawl, Perplexity). It routes each query to the cheapest
|
|
# engine that clears a quality bar and fails over if one is down. Pricing is
|
|
# gain-share (you keep about half of any cache savings). Get a key at
|
|
# https://groundroute.ai/keys
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.groundroute.tools:web_search_tool
|
|
# max_results: 5 # Clamped to 1-50 by GroundRoute
|
|
# # api_key: $GROUNDROUTE_API_KEY # Optional if the env var is set
|
|
|
|
# Web search tool (uses fastCRW - Firecrawl-compatible web scraper, single binary,
|
|
# self-host or cloud. Cloud requires CRW_API_KEY; self-host may need no key.)
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.fastcrw.tools:web_search_tool
|
|
# max_results: 5
|
|
# # api_key: $CRW_API_KEY
|
|
# # base_url: https://fastcrw.com/api # default cloud; set to e.g. http://localhost:3000 for self-host
|
|
|
|
# Web search tool (uses Sofya, requires SOFYA_API_KEY)
|
|
# Sofya returns the content of the result pages, not only their snippets.
|
|
# Sign up at https://sofya.co to get a key.
|
|
# Note: set SOFYA_API_KEY in your environment before starting the app.
|
|
# Avoid putting literal API keys in config.yaml; use the $VAR form instead.
|
|
# - name: web_search
|
|
# group: web
|
|
# use: deerflow.community.sofya.tools:web_search_tool
|
|
# max_results: 5 # capped at 20 by the Sofya provider
|
|
# # search_depth: basic # basic reads the result pages, snippets returns snippets only
|
|
# contents_max_characters: 2000 # per-result content cap; 0 disables it
|
|
# # api_key: $SOFYA_API_KEY # Optional explicit env-var reference
|
|
|
|
# Web fetch tool (uses Browserless - headless Chrome, self-hosted or cloud)
|
|
# Browserless renders pages with a real headless Chrome, ideal for JavaScript-heavy
|
|
# sites and SPAs. Deploy your own: https://github.com/browserless/browserless
|
|
# For Docker deployments, use the Docker service name instead of localhost.
|
|
# NOTE: Only one web_fetch provider can be active at a time.
|
|
# Comment out the Jina AI web_fetch entry below before enabling this one.
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.browserless.tools:web_fetch_tool
|
|
# base_url: http://localhost:3032 # Browserless instance URL (default: :3032; Docker: http://browserless:3000)
|
|
# # token: $BROWSERLESS_TOKEN # API token (required for Browserless Cloud; optional for self-hosted)
|
|
# timeout_s: 30 # Request timeout in seconds
|
|
# # allow_private_addresses: false # SSRF guard: keep false in production. Set true ONLY for intentional internal targets.
|
|
# # wait_for_event: "networkidle" # Wait for a page event before returning (e.g. "load", "networkidle")
|
|
# # wait_for_timeout_ms: 2000 # Extra wait after page load in milliseconds
|
|
# # wait_for_selector: "article" # CSS selector to wait for before returning
|
|
|
|
# Web fetch tool (uses Crawl4AI - self-hosted headless Chromium, no third-party API key)
|
|
# Crawl4AI returns server-cleaned "fit" markdown directly (no readability step needed),
|
|
# ideal for JavaScript-heavy sites. Self-host it:
|
|
# docker run -d -p 11235:11235 --shm-size=1g \
|
|
# -e CRAWL4AI_API_TOKEN=$CRAWL4AI_TOKEN unclecode/crawl4ai:0.9.2
|
|
# Crawl4AI >= 0.9 is secure-by-default: a bearer token is REQUIRED on every request
|
|
# except GET /health, and a server started without CRAWL4AI_API_TOKEN binds 127.0.0.1
|
|
# only (so a container/remote DeerFlow cannot reach it at all). Set the same value in
|
|
# the server env and in `token:` below, or requests fail with HTTP 401.
|
|
# Use >= 0.8.7: earlier images, including 0.8.6, carry known pre-auth RCEs.
|
|
# For Docker deployments, use the Docker service name instead of localhost.
|
|
# NOTE: Only one web_fetch provider can be active at a time.
|
|
# Comment out the Jina AI web_fetch entry below before enabling this one.
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.crawl4ai.tools:web_fetch_tool
|
|
# base_url: http://localhost:11235 # Crawl4AI server URL (Docker: http://crawl4ai:11235)
|
|
# token: $CRAWL4AI_TOKEN # Bearer token; required by Crawl4AI >= 0.9
|
|
# timeout: 30 # Request timeout in seconds
|
|
# # allow_private_addresses: false # SSRF guard: keep false in production. Set true ONLY for intentional internal targets.
|
|
# # filter: fit # Markdown filter: fit (default) | raw | bm25 | llm
|
|
|
|
# Web capture tool (uses Browserless /screenshot to render a page as an artifact)
|
|
# Browserless captures JavaScript-heavy pages with a real headless Chrome and
|
|
# writes the screenshot into the current thread's outputs. It can run against
|
|
# a self-hosted Browserless instance without a token, or Browserless Cloud with
|
|
# BROWSERLESS_TOKEN. For Docker deployments, use the Docker service name instead
|
|
# of localhost.
|
|
# - name: web_capture
|
|
# group: web
|
|
# use: deerflow.community.browserless.tools:web_capture_tool
|
|
# base_url: http://localhost:3032 # Browserless instance URL (Docker: http://browserless:3000)
|
|
# # token: $BROWSERLESS_TOKEN # Required for Browserless Cloud; optional for self-hosted
|
|
# timeout_s: 30 # Request timeout in seconds (`timeout` also accepted)
|
|
# output_format: png # png, jpeg, or webp
|
|
# full_page: true # Capture entire page instead of viewport only
|
|
# viewport_width: 1280
|
|
# viewport_height: 720
|
|
# # wait_for_selector: "main" # CSS selector to wait for before capturing
|
|
# # wait_for_selector_timeout_ms: 5000
|
|
# # wait_for_timeout_ms: 1000 # Extra wait after navigation in milliseconds
|
|
# # best_attempt: true # Continue with current page state if waits time out
|
|
# # allow_private_addresses: false # SSRF guard: keep false in production. Set true ONLY to
|
|
# # # capture internal/private targets (loopback, RFC1918, etc.)
|
|
|
|
# Agentic browser control (stateful navigate → observe → click/type loop).
|
|
# Unlike web_fetch/web_capture (read-only, stateless), these tools keep a live
|
|
# per-thread Playwright browser so the agent can interact with JavaScript-heavy
|
|
# pages, fill forms, and follow multi-step flows. Elements are addressed by the
|
|
# numeric [ref] index returned in each page snapshot.
|
|
#
|
|
# Requires the optional dependency, installed once per environment:
|
|
# cd backend && uv sync --extra browser && uv run playwright install chromium
|
|
# make dev / Docker startup auto-detect an active browser_navigate entry and
|
|
# preserve the browser extra during dependency sync.
|
|
#
|
|
# Uncomment the whole block to enable. Launch and URL-policy settings are read
|
|
# canonically from browser_navigate and shared by every browser tool + Live.
|
|
# - name: browser_navigate
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_navigate_tool
|
|
# headless: true # Set false only for local, trusted debugging
|
|
# timeout_ms: 30000 # Per-action navigation/interaction timeout
|
|
# viewport_width: 1280
|
|
# viewport_height: 720
|
|
# # allow_private_addresses: false # SSRF guard: keep false in production
|
|
# # cdp_url: http://127.0.0.1:9222 # Attach to YOUR running Chrome (Codex-style)
|
|
# # allow_unguarded_cdp: false # REQUIRED true with cdp_url: attaching to an
|
|
# # # existing Chrome prevents DeerFlow from
|
|
# # # enforcing its subresource/redirect SSRF guard.
|
|
# # # instead of launching a private headless one.
|
|
# # # Start Chrome with --remote-debugging-port=9222
|
|
# # # so you watch the agent drive your real browser
|
|
# # # with your real login sessions. Local/trusted only;
|
|
# # # never enable for an untrusted CDP endpoint.
|
|
# # # Chrome must be version-matched to the bundled
|
|
# # # Playwright; a much newer Chrome can reject the
|
|
# # # CDP handshake. If so, use headless: false to launch
|
|
# # # Playwright's own visible browser instead.
|
|
# # Multi-worker note: browser sessions live in one Gateway worker's memory.
|
|
# # Keep GATEWAY_WORKERS=1 while this tool group is enabled.
|
|
# - name: browser_snapshot
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_snapshot_tool
|
|
# - name: browser_click
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_click_tool
|
|
# - name: browser_type
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_type_tool
|
|
# - name: browser_get_text
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_get_text_tool
|
|
# max_chars: 8000 # Truncation cap for browser_get_text output
|
|
# - name: browser_back
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_back_tool
|
|
# - name: browser_screenshot
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_screenshot_tool
|
|
# - name: browser_close
|
|
# group: browser
|
|
# use: deerflow.community.browser_automation.tools:browser_close_tool
|
|
|
|
# Web fetch tool (uses Exa)
|
|
# NOTE: Only one web_fetch provider can be active at a time.
|
|
# Comment out the Jina AI web_fetch entry below before enabling this one.
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.exa.tools:web_fetch_tool
|
|
# # api_key: $EXA_API_KEY
|
|
|
|
# Web fetch tool (uses Jina AI reader)
|
|
- name: web_fetch
|
|
group: web
|
|
use: deerflow.community.jina_ai.tools:web_fetch_tool
|
|
timeout: 10
|
|
# Optional proxy for restricted networks / Docker / WSL.
|
|
# Use host.docker.internal instead of 127.0.0.1 when the proxy runs on the host.
|
|
# proxy: $HTTPS_PROXY
|
|
# trust_env: true
|
|
|
|
# Web fetch tool (uses InfoQuest)
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.infoquest.tools:web_fetch_tool
|
|
# # Overall timeout for the entire crawling process (in seconds). Set to positive value to enable, -1 to disable
|
|
# timeout: 10
|
|
# # Waiting time after page loading (in seconds). Set to positive value to enable, -1 to disable
|
|
# fetch_time: 10
|
|
# # Timeout for navigating to the page (in seconds). Set to positive value to enable, -1 to disable
|
|
# navigation_timeout: 30
|
|
|
|
# Web fetch tool (uses Firecrawl. Cloud requires FIRECRAWL_API_KEY; self-host may need no key.)
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.firecrawl.tools:web_fetch_tool
|
|
# # api_key: $FIRECRAWL_API_KEY
|
|
# # base_url: http://<host>:3002 # optional: self-hosted Firecrawl; no cloud API key needed
|
|
|
|
# Web fetch tool (uses GroundRoute, requires GROUNDROUTE_API_KEY)
|
|
# Fetches a page's extracted text via GroundRoute mode=page.
|
|
# NOTE: Only one web_fetch provider can be active at a time.
|
|
# Comment out the Jina AI web_fetch entry above before enabling this one.
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.groundroute.tools:web_fetch_tool
|
|
# # api_key: $GROUNDROUTE_API_KEY
|
|
|
|
# Web fetch tool (uses fastCRW - Firecrawl-compatible web scraper, single binary,
|
|
# self-host or cloud. Cloud requires CRW_API_KEY; self-host may need no key.)
|
|
# NOTE: Only one web_fetch provider can be active at a time.
|
|
# Comment out the Jina AI web_fetch entry above before enabling this one.
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.fastcrw.tools:web_fetch_tool
|
|
# # api_key: $CRW_API_KEY
|
|
# # base_url: https://fastcrw.com/api # default cloud; set to e.g. http://localhost:3000 for self-host
|
|
# # allow_private_addresses: false # SSRF guard: keep false in production. Set true ONLY for intentional internal targets.
|
|
|
|
# Web fetch tool (uses Sofya, requires SOFYA_API_KEY)
|
|
# Returns the page as markdown. Also handles PDF, DOCX and other documents.
|
|
# NOTE: Only one web_fetch provider can be active at a time.
|
|
# Comment out the Jina AI web_fetch entry above before enabling this one.
|
|
# - name: web_fetch
|
|
# group: web
|
|
# use: deerflow.community.sofya.tools:web_fetch_tool
|
|
# # api_key: $SOFYA_API_KEY
|
|
|
|
# Image search tool (uses DuckDuckGo)
|
|
# Use this to find reference images before image generation
|
|
- name: image_search
|
|
group: web
|
|
use: deerflow.community.image_search.tools:image_search_tool
|
|
max_results: 5
|
|
|
|
# Image search tool (uses InfoQuest)
|
|
# - name: image_search
|
|
# group: web
|
|
# use: deerflow.community.infoquest.tools:image_search_tool
|
|
# # Used to limit the scope of image search results, only returns content within the specified time range. Set to -1 to disable time filtering
|
|
# image_search_time_range: 10
|
|
# # Image size filter. Options: "l" (large), "m" (medium), "i" (icon).
|
|
# image_size: "i"
|
|
|
|
# Image search tool (uses Serper - Google Images API, requires SERPER_API_KEY)
|
|
# Serper provides real-time Google Images results. Sign up at https://serper.dev
|
|
# Note: set SERPER_API_KEY in your environment before starting the app.
|
|
# Avoid putting literal API keys in config.yaml; use the $VAR form instead.
|
|
# - name: image_search
|
|
# group: web
|
|
# use: deerflow.community.serper.tools:image_search_tool
|
|
# max_results: 5 # capped at 10 by the Serper provider
|
|
# # api_key: $SERPER_API_KEY # Optional explicit env-var reference
|
|
|
|
# Image search tool (uses Brave Image Search API, requires BRAVE_SEARCH_API_KEY)
|
|
# Brave provides independent image results and works alongside the Brave web search tool.
|
|
# Note: set BRAVE_SEARCH_API_KEY in your environment before starting the app.
|
|
# Avoid putting literal API keys in config.yaml; use the $VAR form instead.
|
|
# - name: image_search
|
|
# group: web
|
|
# use: deerflow.community.brave.tools:image_search_tool
|
|
# max_results: 5 # capped at 200 by Brave Image Search
|
|
# # country: US
|
|
# # search_lang: en
|
|
# # safesearch: strict
|
|
# # spellcheck: true
|
|
# # api_key: $BRAVE_SEARCH_API_KEY # Optional explicit env-var reference
|
|
|
|
# File operations tools
|
|
- name: ls
|
|
group: file:read
|
|
use: deerflow.sandbox.tools:ls_tool
|
|
|
|
- name: read_file
|
|
group: file:read
|
|
use: deerflow.sandbox.tools:read_file_tool
|
|
|
|
- name: glob
|
|
group: file:read
|
|
use: deerflow.sandbox.tools:glob_tool
|
|
max_results: 200
|
|
|
|
- name: grep
|
|
group: file:read
|
|
use: deerflow.sandbox.tools:grep_tool
|
|
max_results: 100
|
|
|
|
- name: write_file
|
|
group: file:write
|
|
use: deerflow.sandbox.tools:write_file_tool
|
|
|
|
- name: str_replace
|
|
group: file:write
|
|
use: deerflow.sandbox.tools:str_replace_tool
|
|
|
|
# Bash execution tool
|
|
# Active only when using an isolated shell sandbox or when
|
|
# sandbox.allow_host_bash: true explicitly opts into host bash.
|
|
- name: bash
|
|
group: bash
|
|
use: deerflow.sandbox.tools:bash_tool
|
|
|
|
# ============================================================================
|
|
# Tool Search Configuration (Deferred Tool Loading)
|
|
# ============================================================================
|
|
# When enabled, MCP tools are not loaded into the agent's context directly.
|
|
# Instead, they are listed by name in the system prompt and discoverable
|
|
# via the tool_search tool at runtime.
|
|
# This reduces context usage and improves tool selection accuracy when
|
|
# multiple MCP servers expose a large number of tools.
|
|
|
|
tool_search:
|
|
enabled: false
|
|
# When tool_search is enabled, PR1 MCP routing metadata can auto-promote
|
|
# matching deferred MCP tool schemas before a model call. This is the maximum
|
|
# number of matched schemas promoted per model call. Valid range: 1..5.
|
|
auto_promote_top_k: 3
|
|
|
|
# ============================================================================
|
|
# Tool Output Budget Protection
|
|
# ============================================================================
|
|
# Prevents oversized tool results from blowing the model context window.
|
|
# Outputs exceeding `externalize_min_chars` are persisted to disk and replaced
|
|
# with a compact typed synopsis + file reference. The model can read the full output
|
|
# via read_file. When disk persistence is unavailable, outputs exceeding
|
|
# `fallback_max_chars` are head+tail truncated instead.
|
|
#
|
|
# `exempt_tools` prevents persist→read→persist infinite loops for read tools.
|
|
# `tool_overrides` allows per-tool threshold customization.
|
|
|
|
tool_output:
|
|
enabled: true
|
|
externalize_min_chars: 12000
|
|
# Sampling budget for the inline raw head/tail sample appended to every
|
|
# typed synopsis; ignored for binary-like output, which carries its own sample.
|
|
preview_head_chars: 2000
|
|
preview_tail_chars: 1000
|
|
fallback_max_chars: 30000
|
|
fallback_head_chars: 8000
|
|
fallback_tail_chars: 3000
|
|
storage_subdir: ".tool-results"
|
|
exempt_tools:
|
|
- read_file
|
|
- read_file_tool
|
|
# tool_overrides:
|
|
# web_search: 8000
|
|
# bash: 20000
|
|
|
|
# ============================================================================
|
|
# Suggestions Configuration
|
|
# ============================================================================
|
|
# Configure whether the agent automatically generates follow-up question
|
|
# suggestions at the end of each response.
|
|
|
|
suggestions:
|
|
enabled: true
|
|
max_suggestions: 3
|
|
|
|
|
|
# ============================================================================
|
|
# Input Polish Configuration
|
|
# ============================================================================
|
|
# Configure whether the composer can rewrite draft input before sending.
|
|
|
|
input_polish:
|
|
enabled: true
|
|
# Maximum draft length accepted by /api/input-polish.
|
|
max_chars: 4000
|
|
# Optional fast model for draft polishing. Leave null to use the default chat model.
|
|
# For best UX, set this to your lowest-latency inexpensive model.
|
|
model_name: null
|
|
|
|
|
|
# ============================================================================
|
|
# Loop Detection Configuration
|
|
# ============================================================================
|
|
# Detect and interrupt repeated identical tool-call loops.
|
|
# Frequency thresholds are safety limits for repeated use of the same tool type.
|
|
|
|
loop_detection:
|
|
enabled: true
|
|
warn_threshold: 3
|
|
hard_limit: 5
|
|
window_size: 20
|
|
# Compatibility name: bounds retained (thread_id, run_id) histories, not whole threads.
|
|
max_tracked_threads: 100
|
|
tool_freq_warn: 30
|
|
tool_freq_hard_limit: 50
|
|
# Per-tool overrides for tool_freq_warn / tool_freq_hard_limit. Values can be
|
|
# higher or lower than the global defaults. Commonly used to raise thresholds
|
|
# for high-frequency tools like bash in batch workflows (e.g. RNA-seq pipelines)
|
|
# without weakening protection on every other tool.
|
|
# tool_freq_overrides:
|
|
# bash:
|
|
# warn: 150
|
|
# hard_limit: 300
|
|
|
|
# ============================================================================
|
|
# Tool Progress State Machine Configuration (RFC #3177)
|
|
# ============================================================================
|
|
# Detects tool stagnation and repetition at the (thread, tool) level.
|
|
# Tracks consecutive "no-new-info" calls (error, partial_success, near-duplicate success).
|
|
# Three transition paths (determined by deerflow_tool_meta.recoverable_by_model):
|
|
# recoverable=true (no_results, not_found, permission): ACTIVE → WARNED (terminal; hint re-injected each call)
|
|
# recoverable=false (rate_limited, transient): ACTIVE → WARNED → BLOCKED after warn_escalation_count more
|
|
# recoverable=false + action=stop (auth, config): ACTIVE → BLOCKED immediately
|
|
# Requires ToolErrorHandlingMiddleware to be active (always on).
|
|
|
|
# tool_progress:
|
|
# enabled: false
|
|
# stagnation_threshold: 3 # Consecutive problems before WARNED
|
|
# warn_escalation_count: 2 # More problems after WARNED before BLOCKED
|
|
# inject_assessment: true
|
|
# jaccard_similarity_threshold: 0.8 # Word-set similarity threshold for near-duplicate detection
|
|
# min_word_count_for_similarity: 10 # Min unique words to apply Jaccard check
|
|
# max_tracked_threads: 100
|
|
# exempt_tools:
|
|
# - ask_clarification
|
|
# - write_todos
|
|
# - present_files
|
|
# - task
|
|
|
|
# ============================================================================
|
|
# Read-Before-Write File Gate (issue #3857)
|
|
# ============================================================================
|
|
# Blocks write_file (append / overwrite of an existing file) and str_replace
|
|
# unless the agent has read the file's current version first; any write
|
|
# invalidates earlier reads, forcing a re-read between consecutive edits.
|
|
# Deterministic guardrail against blind duplicate appends in long tasks.
|
|
|
|
read_before_write:
|
|
enabled: true
|
|
# A blocked call never ran and must be re-issued after a re-read, so its
|
|
# payload (write_file content, str_replace old_str/new_str) is dead weight in
|
|
# every later model call. Replace it with a short placeholder in model-bound
|
|
# requests; stored history, receipts, and the run journal keep the original.
|
|
elide_blocked_payloads: true
|
|
# Only elide payload fields at least this many characters long (a character
|
|
# count, not tokens: CJK text costs ~3-4x more per character than ASCII).
|
|
# 0 elides every non-empty payload.
|
|
elide_min_chars: 2000
|
|
|
|
# ============================================================================
|
|
# Provider Safety Termination Configuration
|
|
# ============================================================================
|
|
# Intercept AIMessages where the provider stopped generation for safety reasons
|
|
# (e.g. OpenAI finish_reason='content_filter', Anthropic stop_reason='refusal',
|
|
# Gemini finish_reason='SAFETY') while still returning tool_calls. The
|
|
# tool_calls in such responses are typically truncated/unreliable and must
|
|
# not be executed. See issue #3028 for the full failure mode.
|
|
#
|
|
# Detectors are loaded by class path via reflection (same pattern as
|
|
# guardrails / models / tools). The built-in set covers OpenAI-compatible
|
|
# content_filter, Anthropic refusal, and Gemini SAFETY/BLOCKLIST/
|
|
# PROHIBITED_CONTENT/SPII/RECITATION.
|
|
|
|
safety_finish_reason:
|
|
enabled: true
|
|
# Leave `detectors` unset to use the built-in detector set. Set to a
|
|
# non-empty list to fully override (use `enabled: false` to disable instead
|
|
# of providing an empty list).
|
|
#
|
|
# Example — extend the OpenAI-compatible detector for a Chinese provider
|
|
# whose gateway uses a non-standard finish_reason token:
|
|
# detectors:
|
|
# - use: deerflow.agents.middlewares.safety_termination_detectors:OpenAICompatibleContentFilterDetector
|
|
# config:
|
|
# finish_reasons: ["content_filter", "sensitive", "risk_control"]
|
|
# - use: deerflow.agents.middlewares.safety_termination_detectors:AnthropicRefusalDetector
|
|
# - use: deerflow.agents.middlewares.safety_termination_detectors:GeminiSafetyDetector
|
|
#
|
|
# Example — add a custom detector for an in-house provider:
|
|
# detectors:
|
|
# - use: my_company.deerflow_ext:WenxinSafetyDetector
|
|
# config:
|
|
# error_codes: [336003, 17, 18]
|
|
|
|
# ============================================================================
|
|
# Sandbox Configuration
|
|
# ============================================================================
|
|
# Choose between local sandbox (direct execution) or Docker-based AIO sandbox
|
|
|
|
# Option 1: Local Sandbox (Default)
|
|
# Executes commands directly on the host machine
|
|
uploads:
|
|
# Application-level upload limits enforced by the gateway and exposed to the
|
|
# frontend before file selection.
|
|
max_files: 10
|
|
max_file_size: 52428800 # 50 MiB
|
|
max_total_size: 104857600 # 100 MiB
|
|
# Automatic Office/PDF conversion runs on the backend host before sandbox
|
|
# isolation applies. Keep this disabled unless uploads come from a fully
|
|
# trusted source and you intentionally accept host-side parser risk.
|
|
auto_convert_documents: false
|
|
# Controls which PDF-to-Markdown converter is used whenever PDF conversion
|
|
# runs. Automatic upload conversion is gated separately by
|
|
# auto_convert_documents.
|
|
# auto — prefer pymupdf4llm when installed; fall back to MarkItDown for
|
|
# image-based or encrypted PDFs (recommended default).
|
|
# pymupdf4llm — always use pymupdf4llm (must be installed: uv add pymupdf4llm).
|
|
# Better heading/table extraction; faster on most files.
|
|
# markitdown — always use MarkItDown (original behaviour, no extra dependency).
|
|
pdf_converter: auto
|
|
|
|
sandbox:
|
|
use: deerflow.sandbox.local:LocalSandboxProvider
|
|
# Host bash execution is disabled by default because LocalSandboxProvider is
|
|
# not a secure isolation boundary for shell access. Enable only for fully
|
|
# trusted, single-user local workflows.
|
|
allow_host_bash: false
|
|
# Optional: Mount additional host directories into the sandbox.
|
|
# Each mount maps a host path to a virtual container path accessible by the agent.
|
|
# Note: with LocalSandboxProvider under `make up` (docker-compose), host_path is
|
|
# checked from inside the deer-flow-gateway container — you must also bind-mount
|
|
# the same directory into services.gateway.volumes in docker/docker-compose.yaml
|
|
# for this mount to take effect (see issue #3244).
|
|
# mounts:
|
|
# - host_path: /home/user/my-project # Absolute path; see note above for Docker mode
|
|
# container_path: /mnt/my-project # Virtual path inside the sandbox
|
|
# read_only: true # Whether the mount is read-only (default: false)
|
|
|
|
# Tool output truncation limits (characters).
|
|
# bash uses middle-truncation (head + tail) since errors can appear anywhere in the output.
|
|
# read_file and ls use head-truncation since their content is front-loaded.
|
|
# Set to 0 to disable truncation.
|
|
bash_output_max_chars: 20000
|
|
read_file_output_max_chars: 50000
|
|
ls_output_max_chars: 20000
|
|
|
|
# Maximum wall-clock seconds a single host bash command may run before it is
|
|
# terminated (process group and all). A blocking foreground command — e.g. a
|
|
# server started without backgrounding — is killed after this long so the
|
|
# agent's turn cannot hang. Start long-lived processes in the background with
|
|
# output redirected (e.g. `your-command > /tmp/server.log 2>&1 &`) when you
|
|
# need logs; unredirected background output is drained with bounded capture
|
|
# and excess output is discarded.
|
|
bash_command_timeout: 600
|
|
|
|
# Option 2: Container-based AIO Sandbox
|
|
# Executes commands in isolated containers (Docker or Apple Container)
|
|
# On macOS: Prefers Apple Container when available, but keeps Docker while this
|
|
# sandbox prefix still has managed Docker containers to reconcile after a mode change
|
|
# On other platforms: Uses Docker
|
|
# Uncomment to use:
|
|
# sandbox:
|
|
# use: deerflow.community.aio_sandbox:AioSandboxProvider
|
|
#
|
|
# # Optional: Container image to use (works with both Docker and Apple Container)
|
|
# # Default: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
|
|
# # The mirror's `:latest` tag is frozen on an old pre-1.9.3 digest that lacks
|
|
# # the /v1/bash/* routes required-secrets skills need (see #3921/#3922), so
|
|
# # pin an explicit version instead — recommended: 1.11.0 (multi-arch, works
|
|
# # on both x86_64 and arm64). Custom images should extend the default image
|
|
# # or implement the same AIO sandbox HTTP API used by agent-sandbox. See
|
|
# # backend/docs/CONFIGURATION.md.
|
|
# # image: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:1.11.0
|
|
#
|
|
# # Optional: Base port for sandbox containers (default: 8080)
|
|
# # port: 8080
|
|
|
|
# # Optional: Maximum number of concurrent sandbox containers (default: 3)
|
|
# # When the limit is reached the least-recently-used sandbox is evicted to
|
|
# # make room for new ones. Use a positive integer here; omit this field to use the default.
|
|
# # replicas: 3
|
|
#
|
|
# # Optional: Prefix for container names (default: deer-flow-sandbox)
|
|
# # container_prefix: deer-flow-sandbox
|
|
#
|
|
# # Optional: outbound network policy for locally managed Docker sandboxes.
|
|
# # open keeps the current behavior. isolated denies all outbound traffic.
|
|
# # allowlist permits the listed HTTP/HTTPS domains; denied public domains can
|
|
# # optionally be approved from the Human Input card. Restricted modes require
|
|
# # Docker Engine 28+ and are not supported by Apple Container or provisioner mode.
|
|
# # Private, loopback, link-local, multicast, and cloud metadata addresses are always denied.
|
|
# network:
|
|
# mode: allowlist # open | isolated | allowlist (default: open)
|
|
# allow_domains:
|
|
# - pypi.org
|
|
# - files.pythonhosted.org
|
|
# - registry.npmjs.org
|
|
# - github.com
|
|
# approval: prompt # deny | prompt
|
|
# temporary_grant_ttl: 300 # 30-3600 seconds
|
|
# proxy_image: ghcr.io/bytedance/deer-flow-sandbox-network-proxy:latest
|
|
#
|
|
# # Optional: Override whether the sandbox already sees the gateway's
|
|
# # thread workspace/uploads/outputs through shared mounts.
|
|
# # Omit this field to auto-detect from the backend (local containers: true;
|
|
# # remote/provisioner backends: false). Set true only when the deployment
|
|
# # guarantees both sides use the same thread user-data directories, such as
|
|
# # a correctly aligned shared PVC, NFS volume, hostPath, or bind mount.
|
|
# # true skips per-upload sandbox acquire/sync; false forces explicit sync.
|
|
# # thread_data_mounts: true
|
|
#
|
|
# # Optional: Additional mount directories from host to container
|
|
# # NOTE: Skills directory is automatically mounted from skills.path to skills.container_path
|
|
# # mounts:
|
|
# # # Other custom mounts
|
|
# # - host_path: /path/on/host
|
|
# # container_path: /home/user/shared
|
|
# # read_only: false
|
|
# #
|
|
# # # DeerFlow will surface configured container_path values to the agent,
|
|
# # # so it can directly read/write mounted directories such as /home/user/shared
|
|
#
|
|
# # Optional: Environment variables to inject into the sandbox container
|
|
# # Values starting with $ will be resolved from host environment variables
|
|
# # environment:
|
|
# # NODE_ENV: production
|
|
# # DEBUG: "false"
|
|
# # API_KEY: $MY_API_KEY # Reads from host's MY_API_KEY env var
|
|
# # DATABASE_URL: $DATABASE_URL # Reads from host's DATABASE_URL env var
|
|
#
|
|
# # Optional: Cross-instance container ownership (issue #4206).
|
|
# #
|
|
# # Gateway instances share sandbox containers but each keeps its own in-memory
|
|
# # warm pool. Without shared ownership state, one instance's startup
|
|
# # reconciliation adopts a container another instance is actively using and
|
|
# # later idle-destroys it — tool calls then fail with 502 / connection refused.
|
|
# #
|
|
# # Single gateway instance? Leave this out; `memory` is the default and the
|
|
# # cross-instance kill cannot happen.
|
|
# #
|
|
# # MULTIPLE gateway instances / workers sharing one container backend
|
|
# # (load-balanced deployments, Docker Compose) MUST set type: redis. Docker
|
|
# # Compose already sets DEER_FLOW_STREAM_BRIDGE_REDIS_URL, which is taken as
|
|
# # proof the deployment is multi-instance, so redis ownership is inferred even
|
|
# # if this section is omitted.
|
|
# #
|
|
# # The redis ownership store requires the optional `redis` extra. It is
|
|
# # auto-detected from this section on `make dev` and always installed in the
|
|
# # Docker image. To install it manually:
|
|
# # cd backend && uv sync --all-packages --extra redis
|
|
# #
|
|
# # ownership:
|
|
# # type: memory # single gateway instance only
|
|
# #
|
|
# # ownership:
|
|
# # type: redis # required for multi-instance / load-balanced
|
|
# # redis_url: redis://redis:6379/0
|
|
# # renewal_interval_seconds: 30 # how often an owner refreshes its leases
|
|
# # ttl_multiplier: 4 # lease TTL = interval x this (min 2, so a
|
|
# # # single missed renewal cannot expire a live
|
|
# # # owner). Liveness is deliberately independent
|
|
# # # of idle_timeout: renewal keeps running even
|
|
# # # at idle_timeout: 0.
|
|
# # key_prefix: deerflow:sandbox:owner
|
|
# #
|
|
# # NOTE: the redis ownership store is fail-closed, matching the stream bridge's
|
|
# # fail-hard policy. Redis.from_url is lazy so a down Redis does not block
|
|
# # startup, but a sandbox whose ownership cannot be published is not handed out
|
|
# # — acquiring raises instead. The alternative (proceed unowned) is exactly the
|
|
# # #4206 cross-instance kill. Run Redis with HA / a restart policy.
|
|
# #
|
|
# # NOTE: the other boundary is the lease TTL (renewal_interval_seconds x
|
|
# # ttl_multiplier). A Redis outage longer than the TTL can let a reconciling
|
|
# # instance adopt a live owner's container: a lapsed lease is indistinguishable
|
|
# # from a dead owner, so one TTL of adoption grace is the whole safety margin.
|
|
# # Size the TTL against your Redis availability target (HA Redis keeps this
|
|
# # window out of reach).
|
|
|
|
# Option 3: BoxLite micro-VM Sandbox
|
|
# Runs each sandbox as a BoxLite micro-VM. Released boxes stay in an in-process
|
|
# warm pool and can be reclaimed by the same user/thread without a cold start.
|
|
# Requires the boxlite runtime and host virtualization support (KVM on Linux,
|
|
# Hypervisor.framework on macOS).
|
|
# sandbox:
|
|
# use: deerflow.community.boxlite:BoxliteProvider
|
|
# image: python:3.12-slim
|
|
#
|
|
# # Optional: Per-box memory and CPU limits.
|
|
# # memory_mib: 1024
|
|
# # cpus: 2
|
|
#
|
|
# # Optional: Maximum active + warm BoxLite VMs per gateway process (default: 3).
|
|
# # Active boxes are never evicted; only warm-pool boxes are stopped to make room.
|
|
# # replicas: 3
|
|
#
|
|
# # Optional: Seconds before an idle warm-pool VM is stopped (default: 600).
|
|
# # Set to 0 to keep warm VMs until shutdown or replica eviction.
|
|
# # idle_timeout: 600
|
|
#
|
|
# # Optional: Skip the reclaim health check for very recently released VMs.
|
|
# # Default 0.0 keeps reliability-first validation before warm reuse.
|
|
# # health_check_skip_seconds: 0.0
|
|
#
|
|
# # Optional: Environment variables to inject into every command.
|
|
# # environment:
|
|
# # PYTHONUNBUFFERED: "1"
|
|
#
|
|
# Option 4: Provisioner-managed AIO Sandbox (docker-compose-dev)
|
|
# Each sandbox_id gets a dedicated Pod in k3s, managed by the provisioner.
|
|
# Recommended for production or advanced users who want better isolation and scalability.:
|
|
# sandbox:
|
|
# use: deerflow.community.aio_sandbox:AioSandboxProvider
|
|
# provisioner_url: http://provisioner:8002
|
|
# # API key for provisioner authentication. Must match PROVISIONER_API_KEY
|
|
# # set on the provisioner container. Both sides must have the same value set;
|
|
# # the provisioner rejects all /api/* requests when PROVISIONER_API_KEY is unset.
|
|
# # Generate a strong key: openssl rand -hex 32
|
|
# # provisioner_api_key: $PROVISIONER_API_KEY
|
|
# # Note: provisioner-created Pods use the provisioner's SANDBOX_IMAGE
|
|
# # environment variable, not sandbox.image from this config file.
|
|
|
|
# Option 5: Tenki cloud microVM Sandbox
|
|
# Runs each sandbox as an isolated Tenki cloud microVM. Released sandboxes stay
|
|
# in an in-process warm pool and are reclaimed by the same user/thread without a
|
|
# cold start. Requires the optional SDK: pip install "deerflow-harness[tenki]".
|
|
# sandbox:
|
|
# use: deerflow.community.tenki:TenkiSandboxProvider
|
|
# # api_key: $TENKI_API_KEY # falls back to TENKI_API_KEY / TENKI_AUTH_TOKEN env var
|
|
# # base_url: https://tenki.cloud # optional; SDK default when omitted
|
|
# # image: my-base-image # optional; Tenki account default base image when omitted
|
|
# # workspace_id: ws_... # optional; auto-selected if the account has exactly one
|
|
# # Migration: sandbox.project_id is gone — Tenki 1.x removed projects, so scope
|
|
# # is the workspace alone. Set workspace_id if the account has more than one.
|
|
# # A leftover project_id is ignored and logs a warning at startup.
|
|
# # cpu_cores: 2 # optional per-sandbox vCPUs
|
|
# # memory_mb: 2048 # optional per-sandbox memory
|
|
# # replicas: 3 # active + warm microVM cap per gateway process
|
|
# # idle_timeout: 600 # warm microVM idle seconds before terminate; 0 disables
|
|
# # max_duration: 14400 # Tenki sandbox lifetime in seconds; 0 uses the account default
|
|
# # sticky: false # pin the microVM to its host (only matters with pause/resume)
|
|
# # home_dir: /home/tenki # writable dir backing /mnt/user-data
|
|
# # environment: # injected into every command (and as create-time env)
|
|
# # PYTHONUNBUFFERED: "1"
|
|
|
|
# Option 6: OpenSandbox remote Sandbox
|
|
# Runs each sandbox through an OpenSandbox server. Released sandboxes stay in an
|
|
# in-process warm pool and are reclaimed only by the same user/thread. Requires
|
|
# the optional SDK: pip install "deerflow-harness[opensandbox]".
|
|
# sandbox:
|
|
# use: deerflow.community.opensandbox:OpenSandboxProvider
|
|
# image: python:3.11
|
|
# # api_key: $OPEN_SANDBOX_API_KEY # optional when the SDK env var is set
|
|
# # domain: localhost:8080 # optional; OPEN_SANDBOX_DOMAIN fallback
|
|
# # protocol: http # localhost only; use https for remote domains
|
|
# # request_timeout: 30 # management API request timeout seconds
|
|
# # ready_timeout: 30 # create/readiness timeout seconds
|
|
# # use_server_proxy: false # proxy execd/file traffic through server
|
|
# # sandbox_timeout: 14400 # remote lifetime seconds; 0 = explicit cleanup
|
|
# # bash_command_timeout: 600 # default remote command timeout seconds
|
|
# # replicas: 3 # active + warm cap per gateway process
|
|
# # idle_timeout: 600 # warm seconds before destroy; 0 disables
|
|
# # environment: # create-time and per-command defaults
|
|
# # PYTHONUNBUFFERED: "1"
|
|
|
|
# ============================================================================
|
|
# Subagents Configuration
|
|
# ============================================================================
|
|
# Configure timeouts for subagent execution
|
|
# Subagents are background workers delegated tasks by the lead agent
|
|
|
|
# Process-wide execution capacity. These fields are restart-required and are
|
|
# shared by ordinary `task` calls and durable batches. Waiting work is async;
|
|
# queued subagents do not occupy a scheduler thread.
|
|
subagent_runtime:
|
|
max_running: 3
|
|
max_queued: 64
|
|
admission_policy: queue # queue or reject when all slots are occupied
|
|
queue_timeout_seconds: 300
|
|
|
|
# subagents:
|
|
# # Default timeout (seconds) for built-in subagents (default: 1800 = 30 min).
|
|
# # Custom agents use their own timeout_seconds (default 900) unless overridden.
|
|
# timeout_seconds: 1800
|
|
# # Optional global max-turn override for all subagents.
|
|
# # Built-in defaults: general-purpose=150, bash=60. Leave unset to keep them.
|
|
# # max_turns: 120
|
|
#
|
|
# # Total number of subagent delegations allowed in one lead-agent run.
|
|
# # This is a deterministic backstop against repeated planning checkpoints
|
|
# # launching legal-sized batches forever. The default 6 allows two full
|
|
# # batches at the default concurrency of 3. Valid config range: 1-50.
|
|
# # Per-request runtime context can temporarily override this with
|
|
# # `max_total_subagents`, clamped to the same 1-50 range.
|
|
# max_total_per_run: 6
|
|
#
|
|
# # Per-run token ceiling for subagents (#3875 Phase 2). A backstop against a
|
|
# # subagent that burns tokens on trivial work. At the hard-stop threshold the
|
|
# # in-flight turn is capped (tool calls stripped, finish_reason forced to
|
|
# # "stop") so the run completes naturally with a final answer; the result is
|
|
# # stamped `completed` + `subagent_stop_reason=token_capped` so the lead and
|
|
# # UI can tell a budget-capped completion from a clean one. The 2,000,000
|
|
# # default is a generous ceiling — lower it to tighten cost controls. A
|
|
# # per-agent `token_budget` override (see `agents:` below) wins over this.
|
|
# # token_budget:
|
|
# # enabled: true
|
|
# # max_tokens: 2000000
|
|
# # warn_threshold: 0.7 # log a warning once this fraction of the budget is spent
|
|
#
|
|
# # Optional per-agent overrides (applies to both built-in and custom agents)
|
|
# agents:
|
|
# general-purpose:
|
|
# timeout_seconds: 2700 # 45 minutes for very long deep-research tasks
|
|
# max_turns: 250 # raise above the 150 default for very deep tasks
|
|
# # token_budget: # per-agent override of the global token_budget above
|
|
# # max_tokens: 3000000 # raise the ceiling for deep-research tasks
|
|
# # model: qwen3:32b # Use a specific model (default: inherit from lead agent)
|
|
# # skills: # Skill discovery/activation allowlist (default: all enabled)
|
|
# # - web-search
|
|
# # - data-analysis
|
|
# bash:
|
|
# timeout_seconds: 300 # 5 minutes for quick command execution
|
|
# max_turns: 80
|
|
# # skills: [] # No discoverable/activatable skills for bash agent
|
|
#
|
|
# # Custom subagent types: define specialized agents with their own prompts,
|
|
# # tools, skills, and model configuration. Custom agents are available via
|
|
# # the `task` tool alongside built-in types (general-purpose, bash).
|
|
# # custom_agents:
|
|
# # analysis:
|
|
# # description: "Data analysis specialist for processing datasets and generating insights"
|
|
# # system_prompt: |
|
|
# # You are a data analysis subagent. Focus on:
|
|
# # - Processing and analyzing datasets
|
|
# # - Generating visualizations
|
|
# # - Providing statistical insights
|
|
# # tools: # Tool whitelist (null = inherit all)
|
|
# # - bash
|
|
# # - read_file
|
|
# # - write_file
|
|
# # skills: # Skill discovery/activation allowlist (null = all, [] = none)
|
|
# # - data-analysis
|
|
# # - visualization
|
|
# # model: inherit # 'inherit' uses parent's model
|
|
# # max_turns: 80
|
|
# # timeout_seconds: 600
|
|
#
|
|
# # Model override: by default, subagents inherit the lead agent's model.
|
|
# # Set `model` to use a different model (e.g., a local Ollama model for cost savings).
|
|
# # The model name must match a name defined in the `models:` section above.
|
|
|
|
# Durable native-subagent batches. Disabled by default because enabling it can
|
|
# materially increase model usage and requires database.backend sqlite/postgres.
|
|
# The three limits are intentionally separate:
|
|
# - total: all persisted items in one batch
|
|
# - live: pending work admitted as queued/running at one time
|
|
# - running: items from one batch allowed to hold real execution slots
|
|
subagent_batches:
|
|
enabled: false
|
|
poll_interval_seconds: 1
|
|
lease_seconds: 120
|
|
max_items_per_batch: 5000
|
|
default_max_live_items: 100
|
|
max_live_items_per_batch: 1000
|
|
default_max_running_items: 3
|
|
max_running_items_per_batch: 64
|
|
max_attempts: 3
|
|
max_result_chars: 100000
|
|
result_preview_max_chars: 2000
|
|
|
|
# ============================================================================
|
|
# Tool Result Verification
|
|
# ============================================================================
|
|
# Deterministic receipts are stamped onto tool results and injected into the
|
|
# model context so final reports can cite executed actions. Disable them only
|
|
# if the extra provenance context is not wanted. The selective judge settings
|
|
# are reserved for acceptance-criteria review and remain off by default.
|
|
verification:
|
|
receipts_enabled: true
|
|
# Lead-chain ledger rendering: 'delegation_only' renders only while
|
|
# processing subagent results (subagent chains always render).
|
|
receipts_render_mode: "delegation_only"
|
|
judge_enabled: false
|
|
judge_model_name: null
|
|
|
|
# ============================================================================
|
|
# ACP Agents Configuration
|
|
# ============================================================================
|
|
# Configure external ACP-compatible agents for the built-in `invoke_acp_agent` tool.
|
|
|
|
# acp_agents:
|
|
# mcode:
|
|
# # MiniMax Code speaks ACP directly; no adapter package is required.
|
|
# # Install with `npm install --global @minimax-ai/code`, then run `mcode login`.
|
|
# command: mcode
|
|
# args: ["acp"]
|
|
# description: MiniMax Code for implementation, refactoring, debugging, and repository tasks
|
|
# # auto_approve_permissions: false # Enable only for trusted tasks that need MCode to edit files or run commands
|
|
# # timeout_seconds: 1800 # Abort + kill the subprocess if it doesn't respond in time (default: 1800 = 30 min)
|
|
#
|
|
# claude_code:
|
|
# # DeerFlow expects an ACP adapter here. The standard `claude` CLI does not
|
|
# # speak ACP directly. Install `claude-agent-acp` separately or use:
|
|
# command: npx
|
|
# args: ["-y", "@zed-industries/claude-agent-acp"]
|
|
# description: Claude Code for implementation, refactoring, and debugging
|
|
# model: null
|
|
# # auto_approve_permissions: false # Set to true to auto-approve ACP permission requests
|
|
# # timeout_seconds: 1800 # Abort + kill the subprocess if it doesn't respond in time (default: 1800 = 30 min)
|
|
# # env: # Optional: inject environment variables into the agent subprocess
|
|
# # ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY # $VAR resolves from host environment
|
|
#
|
|
# codex:
|
|
# # DeerFlow expects an ACP adapter here. The standard `codex` CLI does not
|
|
# # speak ACP directly. Install `codex-acp` separately or use:
|
|
# command: npx
|
|
# args: ["-y", "@zed-industries/codex-acp"]
|
|
# description: Codex CLI for repository tasks and code generation
|
|
# model: null
|
|
# # auto_approve_permissions: false # Set to true to auto-approve ACP permission requests
|
|
# # env: # Optional: inject environment variables into the agent subprocess
|
|
# # OPENAI_API_KEY: $OPENAI_API_KEY # $VAR resolves from host environment
|
|
|
|
# ============================================================================
|
|
# Skills Configuration
|
|
# ============================================================================
|
|
# Configure skills directory for specialized agent workflows
|
|
|
|
skills:
|
|
# Path to skills directory on the host (relative to project root or absolute)
|
|
# Default: skills under the project root
|
|
# Override with DEER_FLOW_SKILLS_PATH when this field is omitted.
|
|
# Uncomment to customize:
|
|
# path: /absolute/path/to/custom/skills
|
|
|
|
# Path where skills are mounted in the sandbox container
|
|
# This is used by the agent to access skills in both local and Docker sandbox
|
|
# AIO/provisioner and E2B modes require a canonical absolute non-root path
|
|
# that does not overlap reserved platform mounts. Providers snapshot this
|
|
# path for identity, mounts, metadata, and synchronization, so restart the
|
|
# Gateway after changing it.
|
|
# Default: /mnt/skills
|
|
container_path: /mnt/skills
|
|
|
|
# Deferred skill discovery (default: false)
|
|
# When enabled, only skill names appear in the system prompt (<skill_index>).
|
|
# The LLM discovers skill details on demand via the describe_skill tool.
|
|
# This keeps the system prompt compact and prefix-cache friendly when many
|
|
# skills are installed.
|
|
# deferred_discovery: true
|
|
|
|
# ============================================================================
|
|
# SkillScan Configuration
|
|
# ============================================================================
|
|
# Native deterministic skill safety scanning. This runs before the LLM skill
|
|
# scanner on skill install/update and agent-managed skill writes.
|
|
skill_scan:
|
|
# Set false to disable the new deterministic analyzers (nested-archive,
|
|
# secret-pattern, and other content-level checks). Safe archive extraction
|
|
# (path traversal, symlinks, executable-binary, total-size, and entry-count
|
|
# limits) and the LLM skill scanner still run unconditionally.
|
|
enabled: true
|
|
|
|
# Note: To restrict which skills are loaded for a specific custom agent,
|
|
# define a `skills` list in that agent's `config.yaml` (e.g. `agents/my-agent/config.yaml`):
|
|
# - Omitted or null: load all globally enabled skills (default)
|
|
# - []: disable all skills for this agent
|
|
# - ["skill-name"]: load only specific skills
|
|
|
|
# ============================================================================
|
|
# Title Generation Configuration
|
|
# ============================================================================
|
|
# Automatic conversation title generation settings
|
|
|
|
title:
|
|
enabled: true
|
|
max_words: 6
|
|
max_chars: 60
|
|
model_name: null # null = fast local fallback; set a model name to use LLM title generation
|
|
|
|
# ============================================================================
|
|
# Summarization Configuration
|
|
# ============================================================================
|
|
# Automatically summarize conversation history when token limits are approached
|
|
# This helps maintain context in long conversations without exceeding model limits
|
|
|
|
# Optional task notes and keyword recall of compacted messages. See docs/task-continuity.md.
|
|
task_continuity:
|
|
enabled: false
|
|
max_batches: 32
|
|
max_records_per_batch: 256
|
|
max_record_chars: 16000
|
|
|
|
summarization:
|
|
enabled: true
|
|
|
|
# Model to use for summarization.
|
|
# null = summarize with the model the run actually uses (the lead run's model, a
|
|
# subagent's own model, or a thread's custom-agent model), NOT models[0].
|
|
# set = that model generates; if its provider fails, compaction falls back to the
|
|
# run's own model so a broken summary provider cannot disable compaction.
|
|
# Recommended: Use a lightweight, cost-effective model like "gpt-4o-mini" or similar
|
|
model_name: null
|
|
|
|
# Trigger conditions - at least one required
|
|
# Summarization runs when ANY threshold is met (OR logic)
|
|
# You can specify a single trigger or a list of triggers
|
|
trigger:
|
|
# Trigger when token count reaches 32000
|
|
- type: tokens
|
|
value: 32000
|
|
# Uncomment to also trigger when message count reaches 50
|
|
# - type: messages
|
|
# value: 50
|
|
# Uncomment to trigger when 80% of model's max input tokens is reached.
|
|
# The percentage resolves from the SUMMARY model's declared `context_window`
|
|
# (summarization.model_name when set, else the run's own model): declare it
|
|
# on that models entry — third-party OpenAI-compatible models carry no
|
|
# built-in profile. Without it the fraction clause is dropped with a warning
|
|
# and any remaining absolute clauses keep working. If a separate summary
|
|
# model with a larger window is configured, prefer absolute `tokens`
|
|
# thresholds sized for the run model instead.
|
|
# - type: fraction
|
|
# value: 0.8
|
|
|
|
# Context retention policy after summarization
|
|
# Specifies how much recent history to preserve
|
|
keep:
|
|
# Keep the most recent 10 messages (recommended)
|
|
type: messages
|
|
value: 10
|
|
# Alternative: Keep specific token count
|
|
# type: tokens
|
|
# value: 3000
|
|
# Alternative: Keep percentage of model's max input tokens
|
|
# type: fraction
|
|
# value: 0.3
|
|
|
|
# Maximum tokens to keep when preparing messages for summarization
|
|
# Set to null to skip trimming (not recommended for very long conversations)
|
|
trim_tokens_to_summarize: 15564
|
|
|
|
# Custom summary prompt template (null = use default LangChain prompt)
|
|
# The prompt should guide the model to extract important context
|
|
summary_prompt: null
|
|
|
|
# Loaded SKILL.md references (read_file calls under skills.container_path) are
|
|
# captured into the durable skill_context channel and re-injected after
|
|
# compaction as name/path/description reminders. The full skill body is not
|
|
# persisted; the agent should re-read the file before applying instructions.
|
|
# Tool names counted as skill reads:
|
|
# Legacy preserve_recent_skill_* summarization settings are no longer used;
|
|
# skill retention is handled by this durable reference channel instead. Set
|
|
# this list to [] to disable durable skill-reference capture.
|
|
skill_file_read_tool_names:
|
|
- read_file
|
|
- read
|
|
- view
|
|
- cat
|
|
|
|
# ============================================================================
|
|
# Memory Configuration
|
|
# ============================================================================
|
|
# Global memory mechanism (pluggable + self-contained).
|
|
#
|
|
# Shared fields (host level, backend-agnostic):
|
|
# enabled - Master switch for the memory mechanism (call-site gate)
|
|
# injection_enabled - Whether to inject memory into the system prompt (call-site gate)
|
|
# shutdown_flush_timeout_seconds - Hard budget (s) to drain pending updates on Gateway graceful shutdown (default: 30)
|
|
# manager_class - Backend selector: registered name (deermem/mem0/noop/openviking) or dotted path
|
|
# backend_config - Backend-private config dict (passthrough; each backend self-interprets)
|
|
#
|
|
# DeerMem-private fields live under ``backend_config`` (NOT at the memory: top level):
|
|
# storage_path - Data root. Empty = deer-flow base_dir (factory injects absolute
|
|
# runtime_home). Absolute path = that root. Relative = CWD-relative.
|
|
# model - LLM config for memory extraction: {provider, model, api_key, base_url,
|
|
# temperature}. Omit all fields = no extraction (non-LLM ops still work).
|
|
# debounce_seconds - Debounce wait before processing queued updates (default: 30)
|
|
# max_facts - Maximum facts to store (default: 100)
|
|
# fact_confidence_threshold - Minimum confidence for storing facts (default: 0.7)
|
|
# max_injection_tokens - Token budget for memory injection (default: 2000)
|
|
# token_counting - tiktoken (accurate, network on first use) or char (network-free)
|
|
# guaranteed_categories - Fact categories always injected (default: ["correction"])
|
|
# guaranteed_token_budget - Token ceiling for guaranteed categories (default: 500)
|
|
# staleness_review_enabled - Enable staleness pruning (default: true)
|
|
# staleness_age_days - Age threshold for staleness candidates (default: 90)
|
|
# staleness_min_candidates - Minimum stale facts to trigger review (default: 3)
|
|
# staleness_max_removals_per_cycle - Safety cap on removals per cycle (default: 10)
|
|
# staleness_protected_categories - Categories exempt from staleness review (default: ["correction"])
|
|
# staleness_max_lifetime_multiplier - Creation-time cap multiplier for a fact LLM-assigned
|
|
# expected_valid_days; new facts clamped to
|
|
# staleness_age_days x multiplier. Default 20.0
|
|
# (90 x 20 = 1800d ~ 5 years) supports the very-stable
|
|
# prompt tier. (default: 20.0, range: 1.0-100.0)
|
|
# staleness_max_extension_days - Absolute ceiling (in days) on expected_valid_days after a
|
|
# lifetime extension (staleFactsToExtend). Prevents timedelta
|
|
# overflow and LLM misfire from permanently deferring a fact.
|
|
# (default: 3650, range: 90-36500)
|
|
memory:
|
|
enabled: true
|
|
injection_enabled: true # Whether to inject memory into system prompt
|
|
# Hard budget (seconds) to drain pending memory updates on Gateway graceful
|
|
# shutdown. Each pending item does one LLM call, so large IM batches may need
|
|
# more. Must fit inside the pod's K8s terminationGracePeriodSeconds (channel
|
|
# stop + this drain + buffer) or K8s SIGKILLs the drain -- set that on the
|
|
# gateway Helm deployment (see deploy/helm/deer-flow). Default 30s.
|
|
shutdown_flush_timeout_seconds: 30.0
|
|
# Memory backend selector. Either a registered backend name (matching a
|
|
# backends/<name>/ folder that exposes MANAGER_CLASS, e.g. deermem / mem0 / noop)
|
|
# or a dotted import path to a MemoryManager subclass.
|
|
manager_class: deermem
|
|
# Memory operation mode:
|
|
# middleware (default) - passive background extraction after each turn.
|
|
# tool - experimental opt-in; the model calls memory_search/memory_add/
|
|
# memory_update/memory_delete directly. This gives the model agency over
|
|
# memory writes, but effectiveness depends on model tool-use behavior.
|
|
# Normally only one mode runs at a time. A backend that needs conversation-
|
|
# level extraction may retain passive writes in tool mode while still
|
|
# exposing query-aware search (mem0 does this). Tool calls go through the
|
|
# active MemoryManager; unsupported fact CRUD returns a JSON error.
|
|
mode: middleware
|
|
# Backend-private config (a dict), passed verbatim to the backend __init__.
|
|
# Each backend self-interprets it (DeerMem parses it into DeerMemConfig).
|
|
#
|
|
# OpenViking example (replace this DeerMem backend_config block when
|
|
# manager_class is openviking). This first official-adapter integration uses
|
|
# one credential-bound OpenViking USER key for one DeerFlow user and supports
|
|
# middleware mode only. Use owner_user_id: default when DeerFlow auth is off.
|
|
#
|
|
# backend_config:
|
|
# base_url: http://openviking:1933
|
|
# owner_user_id: default
|
|
# api_key_env: OPENVIKING_API_KEY
|
|
# timeout_seconds: 30
|
|
# default_peer_id: deerflow
|
|
# max_seen_message_ids: 512 # bounded hash-only capture cursor
|
|
# startup_policy: fail_fast
|
|
# failure_policy:
|
|
# read: fail_open
|
|
# write: log_and_drop
|
|
# retrieval:
|
|
# top_k: 8
|
|
# score_threshold: 0.25
|
|
# max_injection_chars: 12000
|
|
# content_mode: overview
|
|
# injection_query: >-
|
|
# user profile preferences important entities events ongoing goals
|
|
# constraints and prior decisions
|
|
#
|
|
# For a host-installed OpenViking used by Docker DeerFlow, set base_url to
|
|
# http://host.docker.internal:1933 and allow_insecure_http: true. The bundled
|
|
# optional Compose overlay uses the internal http://openviking:1933 address.
|
|
#
|
|
# Honcho example (replace this DeerMem backend_config block when
|
|
# manager_class is honcho). This remote HTTP adapter uses Honcho's server-side
|
|
# deriver to build user-model memory representations; no local LLM calls.
|
|
#
|
|
# backend_config:
|
|
# base_url: http://localhost:8000
|
|
# # api_key: $HONCHO_API_KEY # hosted Honcho; plain-http + api_key needs allow_insecure_http: true
|
|
# workspace_prefix: deerflow-u- # one isolated workspace per user id
|
|
# # workspace_overrides: {} # map specific user ids to custom workspaces
|
|
# # user_peer_overrides: {} # map specific user ids to custom peer names
|
|
# assistant_peer: deerflow
|
|
backend_config:
|
|
storage_path: "" # empty = deer-flow base_dir (factory injects absolute runtime_home); a non-empty path is the root DIRECTORY (per-user memory under {storage_path}/users/{uid}/memory.json)
|
|
storage_class: file # file (default) or a dotted MemoryStorage class path; invalid persistent backends fail fast
|
|
strict_user_scope: false # set true in authenticated deployments after all callers propagate user_id
|
|
manifest_filename: memory.json # user-global JSON: version/revision/time + user/history only; no facts or fact index
|
|
file_lock_timeout_seconds: 10 # per-scope cross-process advisory lock timeout (single-machine local filesystem)
|
|
retrieval_adapter: fts5 # fts5 (default), empty to disable, or a dotted RetrievalPort factory(config)
|
|
debounce_seconds: 30 # Wait time before processing queued updates
|
|
# Backpressure cap on pending items. 0 = unlimited. At the cap, new
|
|
# non-signal updates are rejected (QueueFull); signal updates are always
|
|
# admitted so important memories are never shed.
|
|
queue_max_depth: 1000
|
|
model: # LLM for memory extraction; omit all fields = no extraction (non-LLM ops still work; an update raises)
|
|
# provider: openai
|
|
# model: gpt-4o-mini
|
|
# api_key: $OPENAI_API_KEY
|
|
# base_url: # optional, for OpenAI-compatible gateways (e.g. DeepSeek)
|
|
# temperature: # optional
|
|
max_facts: 100 # Maximum number of facts to store
|
|
fact_confidence_threshold: 0.7 # Minimum confidence for storing facts
|
|
# Capacity eviction defaults to the historical confidence-only ranking.
|
|
# Opt in to hybrid-v1 to score confidence (65%), explicit-confirmation
|
|
# freshness (25%, 90-day half-life), and query-driven access heat (10%,
|
|
# 30-day half-life). Actual memory_search hits count; default injection does
|
|
# not. A bounded 10% correction reserve aligns storage with guaranteed
|
|
# correction injection. Confirmation detection is batch-level: it checks
|
|
# human messages among the last six filtered messages, while the LLM binds
|
|
# that signal to a factsToReinforce ID without a second correspondence
|
|
# check. Shadow mode audits hybrid disagreements without changing which
|
|
# facts confidence-only keeps.
|
|
fact_eviction_policy: confidence # confidence | hybrid-v1
|
|
fact_eviction_shadow_enabled: false
|
|
eviction_confidence_weight: 0.65
|
|
eviction_confirmation_weight: 0.25
|
|
eviction_access_weight: 0.10
|
|
eviction_confirmation_half_life_days: 90
|
|
eviction_access_half_life_days: 30
|
|
eviction_correction_reserved_fraction: 0.10
|
|
eviction_correction_reserved_max: 10
|
|
eviction_audit_max_entries: 200 # metadata-only events per user/agent scope; 0 disables
|
|
max_injection_tokens: 2000 # Maximum tokens for memory injection
|
|
# Token counting strategy for memory-injection budgeting:
|
|
# tiktoken (default) - accurate, but the encoding BPE data may be
|
|
# downloaded from a public network endpoint on first use. In
|
|
# network-restricted environments this download can block for a long
|
|
# time (see issues #3402 / #3429). Pre-cache the encoding or set this
|
|
# to "char" to avoid it.
|
|
# char - network-free CJK-aware character-based estimate; never touches
|
|
# tiktoken. Slightly less precise budgeting, zero network I/O.
|
|
token_counting: tiktoken
|
|
# Guaranteed injection: fact categories that bypass the regular token budget
|
|
# and draw from a reserved allowance, so high-signal corrections (e.g.
|
|
# "don use pip, use uv") survive even when the budget is tight.
|
|
guaranteed_categories:
|
|
- correction
|
|
guaranteed_token_budget: 500
|
|
# Staleness review: periodically prune aged facts that may no longer reflect
|
|
# the user current situation. When triggered, the LLM reviews facts older
|
|
# than their individual review window (expected_valid_days, or
|
|
# staleness_age_days as fallback) during the normal memory-update call (same
|
|
# LLM invocation - no extra API call) and decides KEEP, REMOVE, or EXTEND
|
|
# for each. The LLM assigns expected_valid_days when creating a fact; EXTEND
|
|
# (staleFactsToExtend) recalibrates that window at review time.
|
|
staleness_review_enabled: true
|
|
staleness_age_days: 90
|
|
staleness_min_candidates: 3
|
|
staleness_max_removals_per_cycle: 10
|
|
staleness_protected_categories:
|
|
- correction
|
|
staleness_max_lifetime_multiplier: 20.0 # creation cap = staleness_age_days x multiplier (90 x 20 = 1800d)
|
|
staleness_max_extension_days: 3650 # absolute ceiling on extended expected_valid_days (~10 years)
|
|
# Memory consolidation (opt-in, lossy: source facts are replaced by a
|
|
# synthesized one; only consolidatedFrom IDs are kept). Runs in the same
|
|
# memory-update LLM call as extraction/staleness - no extra API cost.
|
|
# consolidation_enabled defaults to false because consolidation is lossy.
|
|
consolidation_enabled: false # set true to opt into fact consolidation
|
|
consolidation_min_facts: 8 # min facts in one category to trigger review (3-30)
|
|
consolidation_max_groups_per_cycle: 3 # max groups merged per update cycle (1-10)
|
|
consolidation_max_sources: 8 # max source facts per consolidation group (2-20)
|
|
# extraction_callback is a host-injected post-extraction observability hook
|
|
# (token usage, facts accepted/rejected, rejection rate). The factory
|
|
# injects a logging default; set programmatically to emit a Langfuse span.
|
|
# Message processing (externalized patterns / prompt templates):
|
|
# patterns_dir - dir with correction.yaml / reinforcement.yaml overriding
|
|
# the bundled signal-detection patterns. None (default) =
|
|
# bundled core/message_patterns/. When explicitly set, both
|
|
# files must exist (typos or missing mounts raise an error).
|
|
# prompts_dir - dir with custom memory-extraction prompt templates
|
|
# (memory_update.chat.yaml, staleness_review.yaml,
|
|
# consolidation.yaml, fact_extraction.yaml). None (default)
|
|
# = bundled core/prompts/. Supports per-agent subdirectories.
|
|
# patterns_dir: "" # empty = bundled defaults
|
|
# prompts_dir: "" # empty = bundled defaults
|
|
|
|
# ============================================================================
|
|
# Custom Agent Management API
|
|
# ============================================================================
|
|
# Controls whether the HTTP gateway exposes custom-agent SOUL/USER.md management.
|
|
# Keep this disabled unless the gateway is behind a trusted authenticated admin boundary.
|
|
agents_api:
|
|
enabled: false
|
|
|
|
# ============================================================================
|
|
# Skill Self-Evolution Configuration
|
|
# ============================================================================
|
|
# Allow the agent to autonomously create and improve skills in skills/custom/.
|
|
skill_evolution:
|
|
enabled: false # Set to true to allow agent-managed writes under skills/custom
|
|
moderation_model_name: null # Model for LLM-based security scanning (null = use default model)
|
|
security_fail_closed: true # Moderation model unavailable: true blocks all writes; false allows non-executable content with a warning (executable is always blocked)
|
|
|
|
# ============================================================================
|
|
# Checkpointer Configuration (DEPRECATED — use `database` instead)
|
|
# ============================================================================
|
|
# Legacy standalone checkpointer config. Kept for backward compatibility.
|
|
# Prefer the unified `database` section below, which drives the LangGraph
|
|
# checkpointer, LangGraph Store, and DeerFlow application data (runs,
|
|
# feedback, events) from a single backend setting.
|
|
#
|
|
# If both `checkpointer` and `database` are present, `checkpointer`
|
|
# takes precedence for the LangGraph checkpointer and Store only.
|
|
#
|
|
# checkpointer:
|
|
# type: sqlite
|
|
# connection_string: checkpoints.db
|
|
#
|
|
# checkpointer:
|
|
# type: postgres
|
|
# connection_string: postgresql://user:password@localhost:5432/deerflow
|
|
# # Optional: place LangGraph checkpointer/store tables in this schema.
|
|
# # Leave empty to use the server default search_path, usually public.
|
|
# postgres_schema: deerflow
|
|
|
|
# ============================================================================
|
|
# Database
|
|
# ============================================================================
|
|
# Unified storage backend for the LangGraph checkpointer, LangGraph Store,
|
|
# and DeerFlow application data (runs, threads metadata, feedback, etc.).
|
|
#
|
|
# backend: memory -- No persistence, data lost on restart
|
|
# backend: sqlite -- Single-node deployment, files in sqlite_dir
|
|
# backend: postgres -- Production multi-node deployment
|
|
#
|
|
# If this section is omitted or empty in config.yaml, DeerFlow uses:
|
|
# backend: sqlite
|
|
# sqlite_dir: .deer-flow/data
|
|
#
|
|
# SQLite mode uses a single deerflow.db file with WAL journal mode
|
|
# for the checkpointer, Store, and application data.
|
|
#
|
|
# Postgres mode: put your connection URL in .env as DATABASE_URL,
|
|
# then reference it here with $DATABASE_URL.
|
|
#
|
|
# Install the driver — Issue #2754 fix lands `UV_EXTRAS` in every code path:
|
|
# Local `make dev` auto-detects from `database.backend: postgres` below
|
|
# and passes `--extra postgres` to `uv sync` on every restart, so
|
|
# the extra is no longer wiped. To opt in explicitly (or layer
|
|
# extras like `postgres,ollama`), set in project-root .env:
|
|
# UV_EXTRAS=postgres
|
|
# Docker dev `make docker-start` reads `UV_EXTRAS` from project-root .env via
|
|
# `env_file`. Set:
|
|
# UV_EXTRAS=postgres
|
|
# Multiple extras (`postgres,ollama`) supported here too — see
|
|
# docker/dev-entrypoint.sh.
|
|
# Docker img build-arg `UV_EXTRAS=postgres,discord docker compose build`
|
|
# supports comma- or whitespace-separated extras at build time
|
|
# (backend/Dockerfile expands them to repeated `--extra` flags).
|
|
#
|
|
# First-time bootstrap (before `make dev`):
|
|
# cd backend && uv sync --all-packages --extra postgres
|
|
# (--all-packages propagates the extra into workspace members — see PR #2584)
|
|
#
|
|
# NOTE: When both `checkpointer` and `database` are configured,
|
|
# `checkpointer` takes precedence for the LangGraph checkpointer and Store;
|
|
# `database` still controls DeerFlow application data.
|
|
# If you use `database`, you can remove the `checkpointer` section.
|
|
# database:
|
|
# backend: sqlite
|
|
# sqlite_dir: .deer-flow/data
|
|
#
|
|
# database:
|
|
# backend: postgres
|
|
# postgres_url: $DATABASE_URL
|
|
# # Optional: place DeerFlow ORM tables and LangGraph checkpointer/store
|
|
# # tables in this schema. Leave empty to use the server default
|
|
# # search_path, usually public.
|
|
# postgres_schema: deerflow
|
|
#
|
|
# postgres_schema only takes effect for new tables. On startup DeerFlow runs
|
|
# CREATE SCHEMA IF NOT EXISTS and pins the connection search_path; it never
|
|
# moves existing tables, so third-party tables in `public` are left untouched.
|
|
#
|
|
# Migrating an EXISTING deployment from `public` to a dedicated schema:
|
|
# 1. Stop the DeerFlow services.
|
|
# 2. Move EVERY DeerFlow-owned table AND the Alembic state into the new schema
|
|
# (run as the DB owner). Missing any table strands its rows: after restart
|
|
# DeerFlow recreates an empty counterpart in the target schema and the old
|
|
# rows stay invisible in `public`. Moving the ORM tables but leaving
|
|
# `alembic_version` behind is especially dangerous -- bootstrap then treats
|
|
# the target schema as unversioned, re-baselines it, and replays migrations.
|
|
# CREATE SCHEMA IF NOT EXISTS deerflow;
|
|
# -- Application ORM tables:
|
|
# ALTER TABLE public.runs SET SCHEMA deerflow;
|
|
# ALTER TABLE public.run_events SET SCHEMA deerflow;
|
|
# ALTER TABLE public.threads_meta SET SCHEMA deerflow;
|
|
# ALTER TABLE public.feedback SET SCHEMA deerflow;
|
|
# ALTER TABLE public.users SET SCHEMA deerflow;
|
|
# ALTER TABLE public.agents SET SCHEMA deerflow;
|
|
# -- IM channel tables:
|
|
# ALTER TABLE public.channel_connections SET SCHEMA deerflow;
|
|
# ALTER TABLE public.channel_credentials SET SCHEMA deerflow;
|
|
# ALTER TABLE public.channel_oauth_states SET SCHEMA deerflow;
|
|
# ALTER TABLE public.channel_conversations SET SCHEMA deerflow;
|
|
# -- Scheduled-task tables:
|
|
# ALTER TABLE public.scheduled_tasks SET SCHEMA deerflow;
|
|
# ALTER TABLE public.scheduled_task_runs SET SCHEMA deerflow;
|
|
# -- Alembic migration state (REQUIRED -- see note above):
|
|
# ALTER TABLE public.alembic_version SET SCHEMA deerflow;
|
|
# The DeerFlow-owned set grows over time; discover any tables the list
|
|
# above misses (as the DeerFlow DB owner/role) with:
|
|
# SELECT table_name FROM information_schema.tables WHERE table_schema='public';
|
|
# LangGraph checkpointer/store table names also vary by version -- list them
|
|
# the same way, then ALTER ... SET SCHEMA deerflow for each (e.g. checkpoints,
|
|
# checkpoint_blobs, checkpoint_writes, checkpoint_migrations, store,
|
|
# store_migrations).
|
|
# 3. Set postgres_schema: deerflow here and restart.
|
|
# 4. Verify with `SHOW search_path;` and a smoke test (create a run, read
|
|
# its history). The `public` schema should gain no new DeerFlow tables.
|
|
database:
|
|
backend: sqlite
|
|
sqlite_dir: .deer-flow/data
|
|
|
|
# ============================================================================
|
|
# Inbound webhook dedupe storage (cross-pod redelivery dedup — issue #4120)
|
|
# ============================================================================
|
|
# Where the ChannelManager records inbound webhook dedupe state.
|
|
#
|
|
# backend: auto -- (default) Postgres whenever database.backend=postgres,
|
|
# including multi-replica K8s where each pod runs a single
|
|
# worker but shares one DB. Otherwise an in-process memory
|
|
# store (single-DB, single-pod).
|
|
# NOTE: multi-replica deployments must use a Postgres DB;
|
|
# if database.backend is sqlite/memory, 'auto' falls back to
|
|
# the per-pod memory store and cross-pod dedupe is disabled.
|
|
# backend: memory -- In-process store only. Per-pod: a webhook redelivered to
|
|
# a DIFFERENT replica is NOT deduped. Not recommended for
|
|
# multi-worker deployments (logs a startup WARNING).
|
|
# backend: postgres -- Share dedupe state across pods via the application DB.
|
|
# REQUIRED for any multi-replica deployment. If
|
|
# database.backend is not 'postgres', a WARNING is logged and
|
|
# the store falls back to the in-process memory store.
|
|
#
|
|
# dedupe_storage:
|
|
# backend: auto
|
|
|
|
# Recycle app ORM PostgreSQL connections before the environment's idle cutoff.
|
|
pool_recycle: 300
|
|
# App ORM PostgreSQL command timeout. Set to null to disable it or raise it
|
|
# for intentionally long commands.
|
|
command_timeout: 30
|
|
# Restart required. Use one value across every process sharing this database.
|
|
# full: current full-message checkpoints; delta: DeltaChannel for messages.
|
|
checkpoint_channel_mode: full
|
|
# Delta-mode tuning (only applies when checkpoint_channel_mode is delta).
|
|
# Restart required, like the mode itself: the cadence is compiled into each
|
|
# graph's channel table. All processes sharing this database must agree.
|
|
# snapshot_frequency: full messages snapshot every N per-step writes
|
|
# (higher = smaller checkpoints, slower materialization).
|
|
checkpoint_delta:
|
|
snapshot_frequency: 10
|
|
# Size caps for the compiled checkpoint graph caches (per process).
|
|
# Hot-reloadable, no restart needed: the value is re-read on each eviction
|
|
# check and only changes when a cache evicts, never graph semantics.
|
|
checkpoint_graph_cache:
|
|
# Gateway thread-state accessor graphs (per assistant/mode/cadence).
|
|
accessor_graph_max: 64
|
|
# Delta-mode checkpoint history cache (only used when checkpoint_channel_mode: delta).
|
|
# Pure performance policy: safe to differ across workers, never frozen.
|
|
# checkpoint_cache:
|
|
# type: memory # memory | redis (redis is Gateway/async only)
|
|
# max_entries: 128 # 0 disables the cache
|
|
# redis_url: null # or DEER_FLOW_CHECKPOINT_CACHE_REDIS_URL / REDIS_URL
|
|
# ttl_seconds: 86400 # redis leak safety net; bounds residual copies if a
|
|
# # thread-delete purge fails (purge itself is immediate).
|
|
# # 0 explicitly disables expiry (redis maxmemory only)
|
|
# key_prefix: "" # default: hash of the database identity
|
|
|
|
# ============================================================================
|
|
# Run Events Configuration
|
|
# ============================================================================
|
|
# Storage backend for run events (messages + execution traces).
|
|
#
|
|
# backend: memory -- No persistence, data lost on restart (default)
|
|
# backend: db -- SQL database via ORM, full query capability (production)
|
|
# backend: jsonl -- Append-only JSONL files (lightweight single-node persistence)
|
|
#
|
|
# run_events:
|
|
# backend: memory
|
|
# max_trace_content: 10240 # Truncation threshold for trace content (db backend, bytes)
|
|
# track_token_usage: true # Accumulate token counts to RunRow
|
|
run_events:
|
|
backend: memory
|
|
max_trace_content: 10240
|
|
track_token_usage: true
|
|
|
|
# ============================================================================
|
|
# Agent Storage Configuration
|
|
# ============================================================================
|
|
# Where custom agent DEFINITIONS (config.yaml + SOUL.md) and deployment-level
|
|
# managed subagent definitions are stored. This is separate from `database`
|
|
# (run/thread/event data) and from agent memory.
|
|
# Restart-required (the backend is captured at Gateway lifespan startup).
|
|
#
|
|
# backend: file -- Custom Agents use per-user files; managed subagents use one
|
|
# JSON file each under {base_dir}/managed-subagents/ (default).
|
|
# Node-local without a shared mount.
|
|
# backend: db -- Both definition types use the shared SQL persistence layer,
|
|
# so every node sees them. Requires database.backend to be
|
|
# 'sqlite' or 'postgres' (rejected at startup on 'memory').
|
|
#
|
|
# Switching an existing install to 'db'? Import the on-disk Custom Agents and
|
|
# managed subagents once with:
|
|
# python backend/scripts/migrate_agents_to_db.py # --dry-run to preview
|
|
# The source files are left in place, so reverting to 'file' is a clean rollback.
|
|
#
|
|
# agent_storage:
|
|
# backend: file
|
|
agent_storage:
|
|
backend: file
|
|
|
|
# ============================================================================
|
|
# Scheduled Tasks Configuration
|
|
# ============================================================================
|
|
# Background scheduler for one-time, cron, and interval agent runs.
|
|
# Poller fields (enabled, multi_instance, poll_interval_seconds, lease_seconds,
|
|
# max_concurrent_runs, min_once_delay_seconds) are restart-required.
|
|
# recursion_limit is read at dispatch and applies to the next scheduled run
|
|
# without a Gateway restart. The default (1000) matches the web UI's
|
|
# interactive budget so scheduled and interactive runs behave identically out
|
|
# of the box. Values above max_recursion_limit are clamped.
|
|
#
|
|
# The scheduler is single-instance by default. Set multi_instance: true only
|
|
# when every Gateway instance shares Postgres, run ownership heartbeats, and
|
|
# database-backed run events; startup recovery then preserves live peer runs.
|
|
#
|
|
# scheduler:
|
|
# enabled: false # Master switch for the background poller
|
|
# multi_instance: false # Opt into lease-aware recovery across Gateway instances
|
|
# poll_interval_seconds: 5 # How often to scan for due tasks
|
|
# lease_seconds: 120 # Claim lease; a crashed process's task becomes reclaimable after this
|
|
# max_concurrent_runs: 3 # Global cap on launching/running scheduled runs across multi-instance Pods
|
|
# queue_timeout_seconds: 3600 # Maximum durable queue wait before an occurrence fails
|
|
# min_once_delay_seconds: 60 # Floor for one-time run_at offset and interval every_seconds
|
|
# recursion_limit: 1000 # LangGraph super-step cap for scheduled runs (matches the web UI)
|
|
scheduler:
|
|
enabled: false
|
|
multi_instance: false # Opt into lease-aware recovery across Gateway instances
|
|
poll_interval_seconds: 5
|
|
lease_seconds: 120
|
|
max_concurrent_runs: 3
|
|
queue_timeout_seconds: 3600
|
|
min_once_delay_seconds: 60
|
|
recursion_limit: 1000
|
|
|
|
# ============================================================================
|
|
# Long-running MCP Tasks Configuration
|
|
# ============================================================================
|
|
# Durable runtime for ordinary MCP submit/status/cancel task toolsets. It is
|
|
# disabled by default; task_toolsets are configured per server in
|
|
# extensions_config.json.
|
|
# All fields are restart-required (captured at Gateway lifespan startup).
|
|
#
|
|
# mcp_tasks:
|
|
# enabled: false # Master switch for the background status poller
|
|
# poll_interval_seconds: 5 # Scan interval and default task retry interval
|
|
# lease_seconds: 120 # Expired claims become recoverable after this delay
|
|
# max_concurrent_polls: 8 # Maximum status calls started by one worker per scan
|
|
# max_poll_backoff_seconds: 300 # Cap for exponential retries after transient errors
|
|
# input_required_poll_interval_seconds: 60 # Minimum poll interval while waiting for user input
|
|
# tracking_degraded_after_errors: 3 # Consecutive errors before query API reports degraded tracking
|
|
# max_result_bytes: 65536 # Full JSON result storage limit
|
|
# result_preview_max_chars: 2000 # Text preview retained when a result exceeds the limit
|
|
mcp_tasks:
|
|
enabled: false
|
|
poll_interval_seconds: 5
|
|
lease_seconds: 120
|
|
max_concurrent_polls: 8
|
|
max_poll_backoff_seconds: 300
|
|
input_required_poll_interval_seconds: 60
|
|
tracking_degraded_after_errors: 3
|
|
max_result_bytes: 65536
|
|
result_preview_max_chars: 2000
|
|
|
|
# ============================================================================
|
|
# Run Ownership Configuration
|
|
# ============================================================================
|
|
# Controls cross-process run ownership for multi-worker deployments.
|
|
# When GATEWAY_WORKERS > 1, each worker claims runs with a lease; the heartbeat
|
|
# renews leases, and reconciliation recovers orphaned runs from crashed workers.
|
|
#
|
|
# CLOCK-SYNC REQUIREMENT (multi-worker only): reconciliation compares another
|
|
# worker's UTC lease timestamp against this worker's datetime.now(UTC). Worker
|
|
# clocks MUST be synced (NTP / chrony / systemd-timesyncd — default on K8s and
|
|
# cloud VMs) within a few seconds. grace_seconds is the skew budget; worst case
|
|
# (owning worker's heartbeat just about to fire), a peer whose clock is more
|
|
# than grace_seconds ahead can mis-reclaim a still-live run as an orphan. Raise
|
|
# grace_seconds if your environment cannot keep clocks within a few seconds;
|
|
# the trade-off is longer recovery latency for genuinely dead workers
|
|
# (lease_seconds + grace_seconds from last heartbeat to reclaim).
|
|
#
|
|
# The owning worker itself does NOT use grace_seconds as extra execution time.
|
|
# If renewal cannot be confirmed before lease_seconds expires, it cancels the
|
|
# local run and suppresses durable finalization; grace_seconds only delays when
|
|
# a peer may reclaim the expired row.
|
|
|
|
run_ownership:
|
|
lease_seconds: 30 # Seconds before a run lease expires if not renewed.
|
|
# Heartbeat renews every lease_seconds / 3.
|
|
grace_seconds: 10 # Extra seconds past expiry before reclaiming an orphaned run.
|
|
# Also the cross-worker clock-skew budget — see note above.
|
|
heartbeat_enabled: false # Set to true for GATEWAY_WORKERS > 1
|
|
|
|
# ============================================================================
|
|
# Stream Bridge Configuration
|
|
# ============================================================================
|
|
# The stream bridge carries live agent events from gateway workers to SSE
|
|
# clients. Docker Compose sets DEER_FLOW_STREAM_BRIDGE_REDIS_URL automatically,
|
|
# so Docker deployments use Redis Streams even if this section is omitted.
|
|
#
|
|
# The redis bridge requires the optional `redis` extra. It is auto-detected from
|
|
# this section on `make dev`, and always installed in the Docker image. To install
|
|
# it manually: cd backend && uv sync --all-packages --extra redis
|
|
#
|
|
# stream_bridge:
|
|
# type: memory # single-process only
|
|
# queue_maxsize: 256 # data events retained per run (min: 1). A reconnect
|
|
# # older than this window receives SSE `gap`.
|
|
# heartbeat_interval_seconds: 15 # idle interval (max: 86400) between
|
|
# # heartbeats sent to SSE, wait, and internal
|
|
# # stream consumers.
|
|
#
|
|
# stream_bridge:
|
|
# type: redis # recommended for Docker / multi-worker gateway
|
|
# redis_url: redis://redis:6379/0
|
|
# queue_maxsize: 256 # data events retained per run (Redis MAXLEN, min: 1).
|
|
# # Older valid cursors receive SSE `gap`.
|
|
# heartbeat_interval_seconds: 15 # idle interval (max: 86400) between
|
|
# # heartbeats sent to SSE, wait, and internal
|
|
# # stream consumers.
|
|
# stream_ttl_seconds: 86400 # rolling TTL for retained stream buffers.
|
|
# # Refreshed on each publish/publish_end; set 0
|
|
# # to disable. This is not a run timeout.
|
|
# recovered_stream_cleanup_delay_seconds: 60 # seconds to wait after
|
|
# # publishing END for a recovered orphan run
|
|
# # before deleting the stream key.
|
|
# max_connections: 100 # optional pool ceiling. Each live SSE client
|
|
# # holds one connection blocked in XREAD ... BLOCK
|
|
# # for up to the configured heartbeat interval, so
|
|
# # hundreds of concurrent clients open hundreds of
|
|
# # connections. Unset = redis-py default (unbounded).
|
|
#
|
|
# NOTE: the redis bridge is fail-hard in v1. Redis.from_url is lazy, so a down
|
|
# Redis does not block gateway startup, but the first publish/xread raises. A
|
|
# mid-run Redis outage fails the active run — there is no automatic retry/backoff
|
|
# or fallback to the in-memory bridge. Run Redis with HA / a restart policy.
|
|
|
|
# ============================================================================
|
|
# User-Owned IM Channel Connections
|
|
# ============================================================================
|
|
# Lets logged-in users connect their own IM accounts from the DeerFlow frontend
|
|
# while reusing the existing `channels` runtime configuration below.
|
|
#
|
|
# Security notes:
|
|
# - No public IP, OAuth callback URL, or provider webhook is required.
|
|
# - Provider bot/app credentials stay under `channels.*`.
|
|
# - `channel_connections` stores per-user bindings and one-time connect codes.
|
|
# - Telegram uses a deep link when `bot_username` is configured.
|
|
# - Slack, Discord, Feishu, DingTalk, WeChat, and WeCom use `/connect <code>`
|
|
# through the already-running bot/app.
|
|
#
|
|
# channel_connections:
|
|
# enabled: false
|
|
# # Security: keep this enabled unless you intentionally want legacy open-bot behavior.
|
|
# # Disabling it lets unbound external IM users create DeerFlow threads/runs.
|
|
# require_bound_identity: true
|
|
#
|
|
# telegram:
|
|
# enabled: false
|
|
# bot_username: $TELEGRAM_BOT_USERNAME
|
|
#
|
|
# slack:
|
|
# enabled: false
|
|
#
|
|
# discord:
|
|
# enabled: false
|
|
#
|
|
# feishu:
|
|
# enabled: false
|
|
#
|
|
# dingtalk:
|
|
# enabled: false
|
|
#
|
|
# wechat:
|
|
# enabled: false
|
|
#
|
|
# wecom:
|
|
# enabled: false
|
|
#
|
|
# buzz:
|
|
# enabled: false
|
|
|
|
# ============================================================================
|
|
# IM Channels Configuration
|
|
# ============================================================================
|
|
# Connect DeerFlow to external messaging platforms.
|
|
# All channels use outbound connections (WebSocket or polling) — no public IP required.
|
|
|
|
# channels:
|
|
# # LangGraph-compatible Gateway API base URL for thread/message management (default: http://localhost:8001/api)
|
|
# # For Docker deployments, use the Docker service name instead of localhost:
|
|
# # langgraph_url: http://gateway:8001/api
|
|
# # gateway_url: http://gateway:8001
|
|
# langgraph_url: http://localhost:8001/api
|
|
# # Gateway API URL for auxiliary queries like /models, /memory (default: http://localhost:8001)
|
|
# gateway_url: http://localhost:8001
|
|
# # Maximum queued or provider-reserved inbound messages. Must be a positive integer.
|
|
# inbound_queue_maxsize: 1000
|
|
# # Fixed number of long-lived inbound handler workers. Must be a positive integer.
|
|
# max_concurrency: 5
|
|
# # Seconds to drain accepted inbound work before cancelling active handlers.
|
|
# # Must be a non-negative finite number. Cancelled handlers are awaited; the Gateway's
|
|
# # outer shutdown timeout remains the process-level bound for incomplete cleanup.
|
|
# shutdown_grace_period_seconds: 3
|
|
# #
|
|
# # Docker Compose note:
|
|
# # If channels run inside the gateway container, use container DNS names instead
|
|
# # of localhost, for example:
|
|
# # langgraph_url: http://gateway:8001/api
|
|
# # gateway_url: http://gateway:8001
|
|
# # You can also set DEER_FLOW_CHANNELS_LANGGRAPH_URL / DEER_FLOW_CHANNELS_GATEWAY_URL.
|
|
#
|
|
# # Optional: default mobile/session settings for all IM channels
|
|
# session:
|
|
# assistant_id: lead_agent # or a custom agent name; custom agents route via lead_agent + agent_name
|
|
# config:
|
|
# recursion_limit: 100
|
|
# context:
|
|
# thinking_enabled: true
|
|
# is_plan_mode: false
|
|
# subagent_enabled: false
|
|
#
|
|
# feishu:
|
|
# enabled: false
|
|
# app_id: $FEISHU_APP_ID
|
|
# app_secret: $FEISHU_APP_SECRET
|
|
# # domain: https://open.feishu.cn # China (default)
|
|
# # domain: https://open.larksuite.com # International
|
|
#
|
|
# slack:
|
|
# enabled: false
|
|
# bot_token: $SLACK_BOT_TOKEN # xoxb-...
|
|
# app_token: $SLACK_APP_TOKEN # xapp-... (Socket Mode)
|
|
# allowed_users: [] # empty = allow all; can also be a single Slack user ID string, e.g. U123456, but list form is recommended
|
|
#
|
|
# telegram:
|
|
# enabled: false
|
|
# bot_token: $TELEGRAM_BOT_TOKEN
|
|
# allowed_users: [] # empty = allow all
|
|
# rich_messages: false # Bot API 10.1 Rich Messages for final Markdown responses
|
|
#
|
|
# wechat:
|
|
# enabled: false
|
|
# bot_token: $WECHAT_BOT_TOKEN
|
|
# ilink_bot_id: $WECHAT_ILINK_BOT_ID
|
|
# # Optional: allow first-time QR bootstrap when bot_token is absent
|
|
# qrcode_login_enabled: true
|
|
# # Optional: sent as iLink-App-Id header when provided
|
|
# ilink_app_id: ""
|
|
# # Optional: sent as SKRouteTag header when provided
|
|
# route_tag: ""
|
|
# allowed_users: [] # empty = allow all
|
|
# # Optional: timing values must be positive finite seconds
|
|
# polling_timeout: 35
|
|
# polling_retry_delay: 5
|
|
# # QR poll interval when qrcode_login_enabled is true
|
|
# qrcode_poll_interval: 2
|
|
# # QR bootstrap timeout
|
|
# qrcode_poll_timeout: 180
|
|
# # Optional: persist getupdates cursor under the gateway container volume
|
|
# state_dir: ./.deer-flow/wechat/state
|
|
# # Optional: max inbound image size in bytes before skipping download
|
|
# max_inbound_image_bytes: 20971520
|
|
# # Optional: max outbound image size in bytes before skipping upload
|
|
# max_outbound_image_bytes: 20971520
|
|
# # Optional: max inbound file size in bytes before skipping download
|
|
# max_inbound_file_bytes: 52428800
|
|
# # Optional: max outbound file size in bytes before skipping upload
|
|
# max_outbound_file_bytes: 52428800
|
|
# # Optional: allowed file extensions for regular file receive/send
|
|
# allowed_file_extensions: [".txt", ".md", ".pdf", ".csv", ".json", ".yaml", ".yml", ".xml", ".html", ".log", ".zip", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".rtf"]
|
|
#
|
|
# # Optional: channel-level session overrides
|
|
# session:
|
|
# assistant_id: mobile-agent # custom agent names are supported here too
|
|
# context:
|
|
# thinking_enabled: false
|
|
#
|
|
# # Optional: per-user overrides by user_id
|
|
# users:
|
|
# "123456789":
|
|
# assistant_id: vip-agent
|
|
# config:
|
|
# recursion_limit: 150
|
|
# context:
|
|
# thinking_enabled: true
|
|
# subagent_enabled: true
|
|
# wecom:
|
|
# enabled: false
|
|
# bot_id: $WECOM_BOT_ID
|
|
# bot_secret: $WECOM_BOT_SECRET
|
|
#
|
|
# dingtalk:
|
|
# enabled: false
|
|
# client_id: $DINGTALK_CLIENT_ID
|
|
# client_secret: $DINGTALK_CLIENT_SECRET
|
|
# allowed_users: [] # empty = allow all
|
|
# card_template_id: "" # Optional: AI Card template ID for streaming updates
|
|
#
|
|
# discord:
|
|
# enabled: false
|
|
# bot_token: $DISCORD_BOT_TOKEN
|
|
# allowed_guilds: [] # empty = allow all guilds; can also be a single guild ID
|
|
# mention_only: false # If true, only respond when the bot is mentioned
|
|
# allowed_channels: [] # Optional: channel IDs exempt from mention_only (bot responds without mention)
|
|
# thread_mode: false # If true, group a channel conversation into a thread
|
|
#
|
|
# # Buzz (https://github.com/block/buzz) — Nostr-relay team workspace.
|
|
# # DeerFlow joins as a member identity; generate a keypair and register it as
|
|
# # a relay member, then @mention or DM DeerFlow in Buzz. Requires the `buzz`
|
|
# # dependency extra (uv sync --extra buzz).
|
|
# buzz:
|
|
# enabled: false
|
|
# relay_url: wss://buzz.example.com
|
|
# private_key: $BUZZ_PRIVATE_KEY # hex or nsec1…
|
|
# allowed_users: [] # pubkeys (hex or npub) allowed to trigger runs.
|
|
# # DENY-BY-DEFAULT (unlike other channels): empty
|
|
# # means nobody, and DeerFlow logs a startup warning.
|
|
# require_mention: true # @mention needed in channels
|
|
# mention_free_channels: [] # channel UUIDs that respond to every message
|
|
|
|
# ============================================================================
|
|
# Guardrails Configuration
|
|
# ============================================================================
|
|
# Optional pre-execution authorization for tool calls.
|
|
# When enabled, every tool call passes through the configured provider
|
|
# before execution. Three options: built-in allowlist, OAP policy provider,
|
|
# or custom provider. See backend/docs/GUARDRAILS.md for full documentation.
|
|
#
|
|
# Providers are loaded by class path via resolve_variable (same as models/tools).
|
|
|
|
# --- Option 1: Built-in AllowlistProvider (zero external deps) ---
|
|
# guardrails:
|
|
# enabled: true
|
|
# provider:
|
|
# use: deerflow.guardrails.builtin:AllowlistProvider
|
|
# config:
|
|
# denied_tools: ["bash", "write_file"]
|
|
|
|
# --- Option 2: OAP passport provider (open standard, any implementation) ---
|
|
# The Open Agent Passport (OAP) spec defines passport format and decision codes.
|
|
# Any OAP-compliant provider works. Example using APort (reference implementation):
|
|
# pip install aport-agent-guardrails && aport setup --framework deerflow
|
|
# guardrails:
|
|
# enabled: true
|
|
# provider:
|
|
# use: aport_guardrails.providers.generic:OAPGuardrailProvider
|
|
|
|
# --- Option 3: Custom provider (any class with evaluate/aevaluate methods) ---
|
|
# guardrails:
|
|
# enabled: true
|
|
# provider:
|
|
# use: my_package:MyGuardrailProvider
|
|
# config:
|
|
# key: value
|
|
|
|
# ============================================================================
|
|
# Authorization Configuration
|
|
# ============================================================================
|
|
# Fine-grained resource authorization (RBAC and beyond). Disabled by default;
|
|
# every authenticated user has access to all resources.
|
|
# See RFC: https://github.com/bytedance/deer-flow/issues/4063
|
|
#
|
|
# authorization:
|
|
# enabled: true
|
|
# fail_closed: true # block on provider error / unresolved identity
|
|
# default_role: user # applied when user_role is None; built-in RBAC requires this role below
|
|
# provider:
|
|
# use: deerflow.authz.rbac:RbacAuthorizationProvider
|
|
# config:
|
|
# # A known role with no policy for a resource is unrestricted for it.
|
|
# # Define both `tools` and `routes` wherever access should be constrained.
|
|
# roles:
|
|
# admin:
|
|
# tools: {allow: "*"}
|
|
# routes: {allow: "*"}
|
|
# models: {allow: "*"}
|
|
# sandbox: {allow: "*"}
|
|
# user:
|
|
# tools: {allow: "*", deny: ["update_agent"]}
|
|
# routes: {allow: "*"}
|
|
# models: {allow: "*"}
|
|
# sandbox: {allow: "*"}
|
|
# guest:
|
|
# # web-only role: sandbox-dependent tools (read_file, bash, glob,
|
|
# # grep, write_file, ...) are omitted — with sandbox:execute denied
|
|
# # they could only ever return the deny error, so allow just the
|
|
# # non-sandbox tools.
|
|
# tools: {allow: ["web_search"]}
|
|
# routes: {allow: ["threads:read", "runs:read"]}
|
|
# models: {allow: ["gpt-4o-mini"]}
|
|
# sandbox: {allow: false} # deny sandbox execution
|
|
authorization:
|
|
enabled: false
|
|
|
|
# ============================================================================
|
|
# Circuit Breaker Configuration
|
|
# ============================================================================
|
|
# Circuit breaker for LLM calls prevents repeated requests to a failing provider.
|
|
# When the failure threshold is reached, subsequent calls fast-fail until recovery.
|
|
#
|
|
# This is useful for:
|
|
# - Avoiding rate-limit bans during provider outages
|
|
# - Reducing resource exhaustion from retry loops
|
|
# - Gracefully degrading when LLM services are unavailable
|
|
|
|
# circuit_breaker:
|
|
# # Number of consecutive failures before opening the circuit (default: 5)
|
|
# failure_threshold: 5
|
|
# # Time in seconds before attempting to recover (default: 60)
|
|
# recovery_timeout_sec: 60
|
|
|
|
# ============================================================================
|
|
# LLM Call Concurrency Configuration
|
|
# ============================================================================
|
|
# Cap the number of concurrently in-flight LLM calls process-wide. A provider
|
|
# burst-rate (limit_burst_rate) error fires on the *slope* of the request rate,
|
|
# not on a static quota - so the morning peak (e.g. 08:30) ramping from ~0 to
|
|
# full throttle in seconds gets rejected even when total RPM is within budget.
|
|
# Capping concurrency caps that slope. Retries alone make it worse (they add
|
|
# demand inside the very burst being throttled); pair this cap with the
|
|
# decorrelated-jitter backoff already built into the LLM error-handling
|
|
# middleware, and ideally an nginx `limit_req` at the ingress.
|
|
#
|
|
# 0 disables the cap (default) - existing deployments see no behavior change.
|
|
#
|
|
# Per-process, not per-cluster: the cap bounds in-flight LLM calls within ONE
|
|
# gateway process. With GATEWAY_WORKERS > 1 the aggregate cap across the
|
|
# deployment is effectively `max_concurrent_calls * GATEWAY_WORKERS`, and a
|
|
# multi-node rollout multiplies it further - size the per-process value with
|
|
# that in mind (and pair it with an nginx `limit_req` at the ingress for a
|
|
# true cluster-wide slope cap).
|
|
#
|
|
# Startup-only: the cap is captured at the first LLM run and frozen for the
|
|
# process lifetime. Editing `max_concurrent_calls` here takes effect only after
|
|
# a gateway restart; the other `llm_call.*` knobs below remain hot-reloadable.
|
|
# Freezing the cap avoids the downscale and config-freshness races that a
|
|
# runtime-mutable, process-wide/cross-loop limiter would otherwise hit (a
|
|
# lowered cap could keep admitting queued waiters, or a stale config snapshot
|
|
# constructed after a fresher one could restore a higher cap).
|
|
|
|
# llm_call:
|
|
# # Max concurrently in-flight LLM calls across the whole process (default: 0 = disabled)
|
|
# max_concurrent_calls: 0
|
|
# # Max LLM call attempts for retriable transient errors, 1 = no retry (default: 3)
|
|
# retry_max_attempts: 3
|
|
# # Base delay (ms) for the decorrelated-jitter retry backoff (default: 1000)
|
|
# retry_base_delay_ms: 1000
|
|
# # Hard cap (ms) on any single retry backoff delay (default: 8000)
|
|
# retry_cap_delay_ms: 8000
|
|
# # Backoff base (ms) used ONLY for burst-rate (limit_burst_rate) 429s - higher
|
|
# # than retry_base_delay_ms so the single burst retry lands after the throttle
|
|
# # window subsides. Ignored when the provider sends Retry-After (default: 5000)
|
|
# burst_retry_base_delay_ms: 5000
|
|
|
|
# ============================================================================
|
|
# SSO / OIDC Authentication (optional)
|
|
# ============================================================================
|
|
# Enable SSO login via any OIDC-compatible provider (Keycloak, Google, Azure AD, Okta, etc.).
|
|
# When enabled, the login page will show SSO buttons alongside the standard email/password form.
|
|
#
|
|
# Provider configuration:
|
|
# - issuer: The OIDC issuer URL (e.g. https://keycloak.example.com/realms/deerflow)
|
|
# - client_id: OAuth2 client ID from the provider
|
|
# - client_secret: OAuth2 client secret ($ENV_VAR references supported)
|
|
# - redirect_uri: Callback URL. In production, this must match what you configure in
|
|
# the provider. Defaults to a self-derived URL in development.
|
|
#
|
|
# Keycloak setup:
|
|
# 1. Create a client with type "confidential" and Standard Flow enabled
|
|
# 2. Add Valid Redirect URI: http://localhost:8001/api/v1/auth/callback/keycloak
|
|
# 3. Add Web Origin: http://localhost:8001 (or your frontend origin)
|
|
|
|
# ============================================================================
|
|
# Local (email/password) authentication
|
|
# ============================================================================
|
|
# Self-registration via POST /api/v1/auth/register is open by default: anyone
|
|
# who can reach the Gateway may create a regular user account.
|
|
#
|
|
# The OIDC provisioning policy below (allowed_email_domains, require_verified_email,
|
|
# auto_create_users) is enforced only in the SSO callback. It does NOT constrain local
|
|
# registration, so on an SSO-provisioned deployment leaving this open lets a visitor
|
|
# create an account outside that policy. Set allow_registration: false there.
|
|
#
|
|
# The first admin account is always created through /initialize regardless of this
|
|
# setting, so turning it off cannot lock you out of a fresh install.
|
|
|
|
# auth:
|
|
# local:
|
|
# allow_registration: false
|
|
# # Login throttling per client IP (in-process, per worker). Raise
|
|
# # max_login_attempts when many users share one egress IP (corporate
|
|
# # proxy / NAT) so shared-IP lockouts don't block the whole office;
|
|
# # lower it for a stricter posture (minimum 2 — one failed attempt must
|
|
# # never lock an IP, or a single typo would block the shared egress).
|
|
# # Defaults preserve the historical hardcoded policy (5 failures /
|
|
# # 5 minutes). Live-read per login, so a config reload applies without
|
|
# # a Gateway restart: lowering lockout_seconds releases an active lock
|
|
# # early and tightening max_login_attempts keeps already-counted
|
|
# # failures; a raised lockout_seconds extends a still-active lock but
|
|
# # never resurrects one that already expired.
|
|
# # max_login_attempts: 5
|
|
# # lockout_seconds: 300
|
|
#
|
|
# oidc:
|
|
# enabled: true
|
|
# # Base URL of the frontend, used for redirects after SSO callback.
|
|
# # In production behind a reverse proxy, set this to the public frontend URL
|
|
# # (e.g. https://deerflow.example.com). In development, leave unset.
|
|
# # frontend_base_url: http://localhost:3000
|
|
# providers:
|
|
# keycloak:
|
|
# display_name: Keycloak
|
|
# issuer: https://keycloak.example.com/realms/deerflow
|
|
# client_id: deerflow
|
|
# client_secret: $KEYCLOAK_CLIENT_SECRET
|
|
# # Optional: explicitly set the callback URL.
|
|
# # redirect_uri: https://deerflow.example.com/api/v1/auth/callback/keycloak
|
|
# scopes:
|
|
# - openid
|
|
# - email
|
|
# - profile
|
|
# token_endpoint_auth_method: client_secret_post
|
|
#
|
|
# # User provisioning settings (safe defaults shown below):
|
|
# auto_create_users: true # Auto-create DeerFlow account on first SSO login
|
|
# require_verified_email: true # Reject SSO logins without verified email
|
|
# # allowed_email_domains: # Restrict to specific email domains
|
|
# # - example.com
|
|
# admin_emails: [] # Auto-grant admin role to these emails
|
|
#
|
|
# # Security features (enabled by default):
|
|
# pkce_enabled: true # PKCE (S256) for authorization code flow
|
|
# nonce_enabled: true # Nonce validation in ID tokens
|
|
|
|
# --- Plugins ----------------------------------------------------------------
|
|
# Extension packages, loaded once at startup in the order listed here. Each
|
|
# entry names an install entry point as "module.path:install"; the `config`
|
|
# block is handed to that package verbatim and validated by the package itself.
|
|
#
|
|
# Prefer the extension manager over editing this list by hand:
|
|
# make extension-install SOURCE="deerflow-extension-acme==1.2.3"
|
|
# make extension-install SOURCE="git+https://github.com/acme/deerflow-extension-acme.git@<commit>"
|
|
# make extension-install SOURCE="/absolute/path/to/local-extension"
|
|
# make extension-list
|
|
# make extension-enable NAME=acme
|
|
# make extension-disable NAME=acme
|
|
# make extension-remove NAME=acme
|
|
# The installer updates backend/pyproject.toml, backend/uv.lock, and this block
|
|
# together. It also asks the operator to confirm that the source is trusted;
|
|
# extensions and their build systems execute with Gateway privileges.
|
|
#
|
|
# Every install, enable, disable, remove, or manual edit requires a Gateway
|
|
# restart. Development launchers may fetch missing locked artifacts before
|
|
# handoff; a built production Gateway never installs plugins during startup.
|
|
# Local directories are copied to backend/extensions/sources as deployable
|
|
# snapshots; use a pinned version or public HTTPS Git commit for reproducible
|
|
# remote sources. SSH Git URLs are rejected because the stock Docker builder
|
|
# does not forward host SSH credentials.
|
|
# Loading remains explicit: a package installed outside the manager does nothing
|
|
# until it is listed here, and list order fixes contribution ordering.
|
|
#
|
|
# This is deliberately separate from the `extensions:` block above (MCP servers,
|
|
# skills, config-declared middlewares). That one is backed by
|
|
# extensions_config.json, which the Gateway can rewrite through an HTTP
|
|
# endpoint; a list that causes code to be imported must stay in this
|
|
# operator-controlled file only.
|
|
#
|
|
# Plugins can contribute semantically placed middleware, task-lifecycle hooks,
|
|
# system-model observers, Gateway-lifetime services, and eager FastAPI HTTP routers.
|
|
# See examples/deerflow-extension-example for a package that exercises all five.
|
|
#
|
|
# plugins:
|
|
# - name: example # PEP 621 entry-point name
|
|
# package: deerflow-extension-example # Python distribution managed by uv
|
|
# use: deerflow_extension_example:install
|
|
# enabled: true # false skips import and registration
|
|
# required: false # true makes load failure abort Gateway startup
|
|
# # (install --required opts in)
|
|
# table_prefix: example_ext_ # optional: table-name prefix this extension
|
|
# # owns under its own MetaData/migration chain.
|
|
# # Keeps `alembic revision --autogenerate`, run
|
|
# # directly against a live database, from seeing
|
|
# # those tables and proposing to drop them.
|
|
# # (`make migrate-rev` diffs a throwaway SQLite
|
|
# # built from the migration chain, so it never
|
|
# # sees them in the first place.)
|
|
# # Rejected at startup if empty, or if it would
|
|
# # also match a host-owned table name.
|
|
# config:
|
|
# label: example # extension-private values, if any
|