[Bug][Hermes] README's MEMORY_TENCENTDB_LLM_* vars never reach the Gateway — L1 extraction fails silently while health stays green
Summary
The Hermes-side README tells users to configure the Gateway's LLM with MEMORY_TENCENTDB_LLM_API_KEY / _BASE_URL / _MODEL. No code path ever maps those names onto what the Gateway actually reads (TDAI_LLM_API_KEY / _BASE_URL / _MODEL, MemoryCore/src/gateway/config.ts:588-590). Following the documented setup produces a Gateway that boots, reports status: ok, and runs its pipeline with failed=0 — while every L1 extraction has failed and no memory is ever written.
Two defects here, and the second is the one that costs time:
- the documented variable names are not the ones the code reads;
- the resulting failure is swallowed, so every health signal stays green.
Reproduction
Install the provider per
hermes-plugin/memory/memory_tencentdb/README.md, so Hermes spawns the Gateway as its sidecar.Configure exactly what the README's setup block (lines 146-148) prescribes:
export MEMORY_TENCENTDB_LLM_API_KEY="sk-..." export MEMORY_TENCENTDB_LLM_BASE_URL="https://api.openai.com/v1" export MEMORY_TENCENTDB_LLM_MODEL="gpt-4o"Capture several turns in one session so the L1 threshold is reached (warm-up: the first conversation triggers it):
curl -X POST http://127.0.0.1:8420/capture \ -H 'Content-Type: application/json' \ -d '{"session_key":"probe","user_content":"...","assistant_content":"..."}'Observe:
GET /healthreports"status":"ok"; the pipeline worker'scompletedcount climbs withfailed: 0anddeadLettered: 0; but the L1 record table in the SQLite store stays at 0 rows indefinitely.
Setting the TDAI_LLM_* names instead makes L1 extraction produce records immediately on the same conversation, so the local setup is not otherwise at fault.
Evidence
(1) Documented names — hermes-plugin/memory/memory_tencentdb/README.md:146-148, repeated in that file's env table (lines 236-238), in the config schema the provider advertises to Hermes (__init__.py:975 / 981 / 987), and in scripts/install_hermes_memory_tencentdb.sh:362-364:
export MEMORY_TENCENTDB_LLM_API_KEY="sk-..."
export MEMORY_TENCENTDB_LLM_BASE_URL="https://api.openai.com/v1" # optional
export MEMORY_TENCENTDB_LLM_MODEL="gpt-4o" # optional(2) Names the Gateway actually reads — MemoryCore/src/gateway/config.ts:588-590:
baseUrl: env("TDAI_LLM_BASE_URL") ?? str(llmConfig, "baseUrl") ?? "https://api.openai.com/v1",
apiKey: env("TDAI_LLM_API_KEY") ?? str(llmConfig, "apiKey") ?? "",
model: env("TDAI_LLM_MODEL") ?? str(llmConfig, "model") ?? "gpt-4o",A repo-wide grep for MEMORY_TENCENTDB_LLM over *.py / *.ts / *.sh returns only those schema and documentation strings and the installer's commented examples — there is no bridge. The provider itself writes no environment variables at all (grep -n 'os.environ\[' __init__.py supervisor.py → empty), so nothing compensates for the mismatch.
The supervisor does perform exactly this kind of translation for the gateway host/port pair (supervisor.py:224-231), which is what makes the gap easy to miss — the pattern is present in the same file, just not for the LLM trio:
env = os.environ.copy()
# The Python provider historically used MEMORY_TENCENTDB_* while
# src/gateway/config.ts reads TDAI_GATEWAY_*. Export both so a
# non-default supervisor port cannot accidentally spawn a child
# that still binds the default 8420.
env["MEMORY_TENCENTDB_GATEWAY_PORT"] = str(self._port)
env["MEMORY_TENCENTDB_GATEWAY_HOST"] = str(self._host)
env["TDAI_GATEWAY_PORT"] = str(self._port)
env["TDAI_GATEWAY_HOST"] = str(self._host)(3) The failure is swallowed. With the documented names set, the only witness is a debug-level line during extraction:
[l1-extractor] LLM extraction failed: You didn't provide an API key
...
[pipeline-worker] Stopped (consumed=8, completed=8, failed=0, deadLettered=0)That shutdown line came from a run in which zero L1 records were written.
Mechanically: l1-extractor.ts:214-218 catches the LLM error and returns { success: false, ... }. pipeline-factory.ts:604-652 accumulates extractedCount / storedCount, never branches on l1Result.success, then calls markL1ExtractionComplete unconditionally (:658) and logs L1 complete. The task therefore returns normally, so the worker's counters record a success and no failure counter moves.
Impact
Anyone configuring the Hermes integration from its own README gets a memory system that looks healthy at every observable surface and stores nothing above L0. There is no error in the Hermes logs, no failing counter, and no non-ok status — the only witness is a debug-level log line emitted during extraction. A user could run this for weeks believing memory was accumulating.
Related
- #709 — same family (a documented environment variable that never reaches the runtime, L1 fails), different root cause: that report concerns the Docker deploy path's
MEMORY_LLM_PROTOCOL; this one concerns the Hermes provider path's LLM trio. It also shares the "terse log line" concern noted there. - #1028 —
[Bug][Hermes] memory_tencentdb tools stay advertised (and loop the LLM) while the Gateway is permanently down.
Proposal
Two independent fixes:
- Bridge the names. Either have
MemoryTencentdbProviderexportTDAI_LLM_{API_KEY,BASE_URL,MODEL}into the child process — it already owns the spawn (supervisor.py:268-269) and already does this for the port/host pair — or rename the documented variables toTDAI_LLM_*. Bridging keeps the Hermes-facing names stable, which is what the schema at__init__.py:975-987advertises. - Consider surfacing extraction failures.
pipeline-factory.ts:604-652could branch onl1Result.success(or on theemptyReasonalready carried out of the extractor) so a failed extraction is visible — whether as atasksFailedincrement, adegradedhealth field, or a WARN naming the missing credential. This is offered as a design consideration rather than a specific request: the counting semantics are the maintainers' call, and I can see arguments for treating an empty extraction as a completed no-op.
I'm happy to open a PR for the first fix (bridging the names) if that direction looks right. I'd rather not presume on the second, since it's a design decision that may be better made on your side.
Source: TencentCloud/TencentDB-Agent-Memory