fix(semantic-memory): stop unbounded LLM retry storm on persistent extraction failures - #1513
Open
connectsudhindra-gif wants to merge 1 commit into
Conversation
…traction failures
Background semantic/profile ingestion polls every feature_update_interval_sec
(default 2s) for un-ingested messages. When a message's feature-update LLM
call failed with a non-context-length error, it was logged and skipped via
`continue` without ever being marked ingested. That message would then be
re-fetched and re-sent to the LLM on every subsequent poll cycle, forever,
at the fast idle-polling interval rather than the outer loop's error
backoff — a single persistently-failing ("poison") message could generate
thousands of LLM calls over hours with zero new user activity.
process_semantic_type now retries a feature-update failure up to
_MAX_FEATURE_UPDATE_ATTEMPTS times (mirroring the existing
_try_consolidate retry pattern) via a new _try_feature_update helper.
Once retries are exhausted, the message is marked ingested so it stops
being re-fetched, matching how non-retryable context-length errors are
already handled. debug_fail_loudly still raises instead of giving up.
Fixes MemMachine#1453.
Author
|
This is ready for review whenever a maintainer has time. Let me know if any changes are needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1453 (reported ~$200 in unexpected LLM cost from background semantic/profile ingestion running for many hours with no user activity).
Root cause, traced in
packages/server/src/memmachine_server/semantic_memory/semantic_ingestion.py:semantic_memory.py::_background_ingestion_task) polls everyfeature_update_interval_sec(default 2 seconds) for any set with un-ingested messages.is_ingestedflag — once a message is successfully processed (or hits a non-retryable context-length error), it's marked ingested and won't be re-fetched.llm_feature_updatefailed with any other exception (a transient API error, a malformed/unparseable LLM response for that specific content, etc.), the code logged it andcontinued without marking the message ingested.is_ingested=Falseforever, so the very next poll cycle (2 seconds later) fetches it again and calls the LLM again — forever, with no backoff, since the exception is swallowed locally and never reaches the outer loop's error-backoff handling.Fix
_try_feature_update, mirroring the existing_try_consolidatebounded-retry pattern already used for the consolidation path in the same file._MAX_FEATURE_UPDATE_ATTEMPTS(2, matching_MAX_CONSOLIDATION_ATTEMPTS) times.debug_fail_loudlystill raises instead of silently giving up, unchanged behavior for that flag.Test plan
test_process_single_set_gives_up_on_persistent_generic_errors: simulates a message whose feature update fails every time, asserts the LLM is called only_MAX_FEATURE_UPDATE_ATTEMPTStimes (not once, not forever), that the message ends up marked ingested, and that a second_process_single_setcall (simulating the next poll cycle) makes zero further LLM calls.test_process_single_set_retries_and_recovers_from_transient_error: fails once then succeeds, asserts the retry recovers and the feature is correctly applied.semantic_memorytest suite (uv run pytest packages/server/server_tests/memmachine_server/semantic_memory/): 349 passed, 2 skipped (pre-existing, unrelated), integration tests requiring Postgres/Neo4j deselected as usual.ruff checkandruff format --diffon both changed files: clean.