Skip to content

Fix #2494: [Bug] DSH adapter: a transient gateway 502 on the host-LLM path is never retried - #2496

Open
Memtensor-AI wants to merge 3 commits into
MemTensor:dev-v2.0.36from
Memtensor-AI:bugfix/autodev-2494-20261014045802002
Open

Memtensor-AI wants to merge 3 commits into
MemTensor:dev-v2.0.36from
Memtensor-AI:bugfix/autodev-2494-20261014045802002

Conversation

@Memtensor-AI

Copy link
Copy Markdown
Collaborator

Description

Fixed the DSH host-LLM adapter so a transient gateway 5xx is retried instead of silently degrading the auxiliary stage (issue #2494).

Root cause: createDeepSeekHarnessHostLlmBridge().complete() in apps/memos-local-plugin/adapters/deepseek-harness/host-llm.ts dispatched one prepared.stream() call and never retried. A transient 502 from the gateway in front of the host Messages API surfaced as a terminal finish → MemosError(LLM_UNAVAILABLE, status:502), which propagated straight out. The two host-LLM stages (retrieval.retrieval.filter.v5, capture.summarize) then degraded silently — recall filtering fell back to safeCutoff and capture summarization to the heuristic — matching the reporter's 25/96 failed calls. config.maxRetries never applied because it is consumed by the direct-provider fetcher, not the host-bridge path.

Fix: dispatch now runs through a bounded retry loop (completeAuxiliaryWithRetry): at most 3 attempts, 250 ms × attempt backoff, bounded by the existing per-call deadline(). The classifier (isRetryableAuxiliaryError) retries only LLM_UNAVAILABLE with no status (transport failure) or status ≥ 500, and never 401/402/403, provider aborts (dshCode "ABORTED"), LLM_RATE_LIMITED, LLM_TIMEOUT, malformed output, or deadline/caller aborts. Because a prepared DSH call is single-use, each attempt re-prepares via prepareAuxiliaryCall; the model-capability lookup is cached per route, so re-preparation is a cache hit and adds no round trip. Success-path behavior and the returned HostLlmCompletion shape are unchanged.

Tests/verification: added 13 regression tests to tests/unit/adapters/deepseek-harness-host-llm.test.ts covering the acceptance matrix ([502,502,ok]→3 ok; [502,ok]→2 ok; [502,502,502]→3 thrown; [ok]→1; [401/402/403]→1 thrown; statusless transport→2 ok; provider abort→1 thrown; RATE_LIMIT→1 thrown; malformed→1 thrown; deadline abort→1 thrown; caller abort during backoff→1). npx tsc -p tsconfig.json --noEmit is clean and npx vitest run tests/unit/adapters/deepseek-harness-host-llm.test.ts tests/unit/llm reports 121/121 passing. Broader suites that fail (better_sqlite3.node missing native binding) were confirmed pre-existing on a clean stash and are unrelated. An independent code review flagged the provider-abort retry gap, which is fixed and covered.

Deliverables: 2 commits on bugfix/autodev-2494-20261014045802002 (892ab4c initial fix, 7bcf73a abort exclusion), pushed to origin. opsp artifacts written under .ai-tasks/ and openspec/changes/2026-10-14-2494-.../ (proposal/spec/design/verification-report) and archived to the memos-autodev-specs repo (commit 682db96). No PR created — left to the scheduler.

Related Issue (Required): Fixes #2494

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (does not change functionality, e.g. code style improvements, linting)
  • Documentation update

How Has This Been Tested?

Not run; documentation-only change.

  • Unit Test
  • Test Script Or Test Steps (please provide)
  • Pipeline Automated API Test (please provide)

Checklist

  • I have performed a self-review of my own code
  • I have commented my code in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • I have created related documentation issue/PR in MemOS-Docs (if applicable)
  • I have linked the issue to this PR (if applicable)
  • I have mentioned the person who will review this PR

@whipser030, @hijzy please review this PR.

Reviewer Checklist

autodev and others added 2 commits October 14, 2026 15:04
…2494)

complete() dispatched a single prepared.stream() call with no retry, so a
transient 5xx from the gateway in front of the host Messages API failed the
auxiliary call outright. Recall filtering and capture summarization then
degraded silently (safeCutoff / heuristic) — 25 of 96 host calls failed on
the reporter's installation.

Add a bounded retry in the bridge: at most 3 attempts, 250ms x attempt
backoff, bounded by the existing per-call deadline. Retry only
LLM_UNAVAILABLE with no status or status >= 500; never retry 401/402/403,
rate limits, timeouts, malformed output, or deadline/caller aborts. Each
attempt re-prepares via the cached capability path because a prepared DSH
call is single-use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ensor#2494)

A provider `aborted` finish maps to LLM_UNAVAILABLE with no status, which the
status gate would treat as a retryable transport failure. A cancellation is
not transient, so exclude dshCode "ABORTED" explicitly and cover it with a
regression test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Memtensor-AI

Memtensor-AI commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator Author

🤖 Open Code Review

Target: PR #2496
Task: 6000a6d0b43d3c4f
Base: dev-v2.0.36
Head: bugfix/autodev-2494-20261014045802002
Head SHA: be5840da5d0d07389b752c5bac643928fdaea190

✅ OpenCodeReview: Review complete: 0 finding(s) across 1 selected item(s).

Generated by cloud-assistant via Open Code Review.

@Memtensor-AI

Copy link
Copy Markdown
Collaborator Author

🔧 Open Code Review requested Agent fix

Open Code Review found 2 issue(s). I have resumed the development Agent to fix them.

  • Task: 6000a6d0b43d3c4f
  • Fix attempt: 1/2
  • Finding delta: 0 repeated / 2 new / 0 likely resolved

The Agent will push a new commit to this PR branch. OCR will recheck after the commit is pushed.

Replace the adapter-local waitForRetryBackoff with the shared
core/util/retry-after.js waitForRetry so the abort-listener contract has a
single implementation, and fail fast when HOST_LLM_MAX_ATTEMPTS is
misconfigured to a non-positive value instead of throwing `undefined`.

Refs MemTensor#2494
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai:generated Generated or modified by AI | 由 AI 生成或修改 area:plugin OpenClaw & Hermes status:in-progress Someone or AI is working on it | 人工或 AI 正在处理

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants