* 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.
Honcho memory backend
Uses Honcho (self-hosted or hosted, v3 API) as DeerFlow's user-model memory store. Honcho covers the user dimension of memory — long-term user modeling, preferences, and a cross-session working representation — built by Honcho's own server-side deriver. Ingestion is cheap plain message writes; this backend makes no LLM calls locally.
Configuration
memory:
enabled: true
injection_enabled: true
manager_class: honcho
mode: middleware # or "tool"
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
message_char_limit: 8000
max_injection_chars: 6000
timeout_seconds: 10
connect_timeout_seconds: 3
failure_policy:
read: fail_open # fail_open | fail_closed
A configured api_key over a plain-http:// base URL is rejected at startup
unless you explicitly set allow_insecure_http: true (local-development opt-in,
same posture as the mem0 backend). Use HTTPS for any non-local deployment.
Config errors (bad URL, insecure key combination) fail fast at Gateway startup;
connectivity is deliberately not probed, so a temporarily unreachable Honcho
does not block startup — reads then degrade per failure_policy.read.
Multi-user isolation
Every operation resolves a workspace from user_id:
workspace_overrides[user_id] on exact match, else
workspace_prefix + <stable id>. The stable id is the sanitized user id plus
an 8-hex-char SHA-256 suffix of the original id, so distinct raw ids that
sanitize identically cannot silently merge into one workspace. Honcho scopes
all queries to a single workspace, so under the default derivation users
cannot see each other's memory by construction.
- A missing or empty
user_idfails closed: writes become no-ops and reads return empty — there is never a shared fallback workspace. - A
workspace_overridesentry deliberately shared across users shares that workspace's search index (searchis workspace-scoped;get_context/get_memorystay peer-scoped). - Session ids reuse the same collision-resistant derivation
(
df-+ stable id ofthread_id).
Recall and search
mode: middlewarerecall is query-less:get_contextfetches the user's working representation (up to 25 conclusions) and injects it truncated tomax_injection_chars.mode: tooladds query-awarememory_searchbacked by Honcho's workspace-scoped search, while the passive per-turn write middleware is retained because Honcho's deriver learns fromadd()writes.
Limitations
- No DeerMem-style fact CRUD:
create_fact/delete_fact/update_fact,import_memory, and Settings-page fact editing are not implemented (get_memoryreturns a minimal DeerMem-shaped document with the working representation as theworkContextsummary and an empty fact list).memory_add/memory_update/memory_deletereturn a clear unsupported-operation error; conversation writes still happen through the retained middleware. DeerMem remains the default backend. - No migration of existing DeerMem data.
- Writes are fire-and-forget per call: a failed write is logged and dropped
(at-most-once); nothing is buffered locally, so
shutdown_flushis a no-op. agent_nameis not mapped — Honcho models the user, not per-agent facts.
Async execution and failure behavior
The Honcho HTTP client is synchronous for compatibility with the
MemoryManager contract. DeerFlow offloads it at every async boundary via
asyncio.to_thread (the manager's a* methods), so a slow Honcho request
never blocks ASGI handlers or SSE heartbeats.
failure_policy.read: fail_open (default) 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.