* fix(memory): report non-mapping Honcho backend_config values as ValueError
failure_policy, workspace_overrides and user_peer_overrides were read with a
falsy-only `or {}` fallback, so a truthy non-mapping (a bare string, a YAML
list) reached .get/.items and escaped as a bare AttributeError from inside
backend construction. Route all three through one _mapping helper that keeps
falsy values meaning "unset" and names the offending key as a ValueError, the
posture the mem0 and OpenViking backends already take.
* fix(memory): name the Honcho numeric knobs that cannot be cast
timeout_seconds / connect_timeout_seconds / message_char_limit /
max_injection_chars still reached float() / int() with a YAML null or a
mapping, so the operator got a TypeError naming neither the key nor the
config file. Narrow all four through one _number helper that keeps falsy
values meaning "unset" the way the sibling api_key / storage_path scalars
already do, and reports a value that cannot be cast as a ValueError on the
key. Numeric strings keep parsing, since that is what float/int accept.
* fix(memory): report non-numeric backend_config knobs by name in mem0 and OpenViking
mem0 casts top_k, score_threshold, max_injection_chars and timeout_seconds, and
OpenViking casts timeout_seconds, max_seen_message_ids, retrieval.top_k and
retrieval.max_injection_chars, straight through int()/float(). A knob written
without a value in YAML therefore escapes as "TypeError: int() argument must be
a string..." from inside backend construction, naming neither which knob nor
which file is wrong, and a non-numeric value escapes as the equally anonymous
"could not convert string to float".
Both now resolve numeric knobs through a helper that keeps a value-less key at
its default, the way the same dicts already treat failure_policy and
allow_insecure_http, and turns an uncastable value into a ValueError that names
the knob. OpenViking's score_threshold keeps None as a meaningful value rather
than a default to fall back to.
mem0 memory backend
Uses mem0 (Platform hosted API, or any API-compatible self-hosted server) as DeerFlow's memory store. Fully stateless in-process: dedup, fact extraction, and storage are server-side, so it is safe for multi-worker Gateway deployments.
Configuration
memory:
enabled: true
injection_enabled: true
manager_class: mem0
mode: middleware # or "tool"
backend_config:
api_key_env: MEM0_API_KEY # key read from env, never in config.yaml
base_url: https://api.mem0.ai # or your self-hosted mem0 server
allow_insecure_http: false # true only for trusted local HTTP dev
top_k: 8
score_threshold: 0.1
max_injection_chars: 12000
timeout_seconds: 10
startup_policy: fail_fast # fail_fast | tolerate
failure_policy:
read: fail_open # fail_open | fail_closed
write: log_and_drop # log_and_drop | raise
Set the key in the environment: export MEM0_API_KEY=...
base_url must use HTTPS because every request carries the API key. For a
trusted local-development server that only exposes HTTP, opt in explicitly
with allow_insecure_http: true; do not use that setting across an untrusted
network.
Identity mapping
| DeerFlow | mem0 |
|---|---|
user_id |
user_id |
agent_name |
agent_id |
thread_id |
run_id |
Limitations
mode: middlewarerecall is query-less (theget_contextcontract carries no query): the bucket's most recenttop_kmemories are injected. For query-aware semantic recall usemode: tool.mode: toolretains the passive per-turn write middleware for this backend, because mem0 extracts and deduplicates facts from conversations throughadd(). The agent still gains query-awarememory_search, while new conversations continue accumulating memory even though fact CRUD is not available.- Fact CRUD,
import_memory, and Settings-page memory editing are not implemented (gateway returns 501). DeerMem remains the default backend. - No migration of existing DeerMem data.
log_and_dropwrite policy is at-most-once: a failed write is dropped.memory_add/memory_update/memory_deleteare backed by fact CRUD, which this backend does not implement; they return a clear unsupported-operation error. Conversation writes still happen through the retained middleware.
Async execution and failure behavior
The mem0 HTTP client is synchronous for compatibility with the
MemoryManager contract. DeerFlow offloads it at every async boundary: the
async middleware uses the manager's a* methods, and Gateway memory routes run
sync management calls in worker threads. A slow mem0 request therefore does
not block unrelated ASGI handlers or SSE heartbeats.
failure_policy.read: fail_open logs a recall failure and continues without
new memory context. fail_closed propagates the backend error through prompt
construction and aborts the run instead of silently degrading.