Skip to content

fix(dsh): make extraction drain lossless and auditable - #97

Merged
adoresever merged 3 commits into
mainfrom
fix/extraction-drain-resilience
Aug 31, 2026
Merged

fix(dsh): make extraction drain lossless and auditable#97
adoresever merged 3 commits into
mainfrom
fix/extraction-drain-resilience

Conversation

@adoresever

Copy link
Copy Markdown
Owner

Integrates and supersedes #95 while preserving both original contributor commits and authorship. Thank you @evilh2019 and @penggaolai for identifying the fixed-size backlog stall and contributing the initial bounded batching, retry and bisect implementation.

Root-cause corrections added during maintainer validation:

  • durable pending / succeeded / quarantined extraction states; poison messages are never mislabeled as learned
  • exact message-id acknowledgements; quarantined sources are protected from retention GC
  • lossless semantic segmentation for a single oversized event; durable source text is unchanged
  • validated extractionDrain policy, deterministic existing-name hints, AbortSignal timeout and cleanup
  • explicit gm_retry_extraction recovery and startup pending-session recovery
  • optional agentPresets dependency so official DSH Headless boots without the UI preset service
  • awaited agent/turn-stopping drain, preventing provider disposal between long-message segments

Validation:

  • 149/149 Vitest tests
  • package verification: 79 files, no install-time scripts
  • official @deepseek-ai/dsh 0.1.1-rc.2 tarball install in Web and Headless profiles
  • real relay API: Deepseek-v4-flash and GLM-5.2 both produced succeeded messages and graph nodes
  • real 20,064-character source remained complete; extraction split into bounded calls, max observed request 16,121 bytes versus 28,125 before
  • injected first extraction HTTP 503 followed by a real Deepseek retry: succeeded with attempts=1 and cleared error

This prepares version 1.6.0-beta.11.

penggaolai and others added 3 commits August 30, 2026 20:52
The DSH adapter's extractPending loop pulled a fixed 50 unextracted
messages per batch with no length bound, built an unbounded
existingNames hint list, and on any error left the whole batch
unextracted while stopping the drain. A batch that stalled the LLM
stream (no finish/error chunk) was retried on every restart forever,
pinning the backlog.

- bound each extraction request by accumulated normalized characters
  (8K) then message count (15); a single long message always fits so
  the drain always progresses
- cap the existingNames hint list (150 entries / 3K chars)
- hard-bound the LLM stream with a 180s timeout (previously none)
- retry transient failures with backoff (5s/15s), then bisect the
  batch, and mark a lone failing message extracted as a last resort so
  the drain can never deadlock

Verified: 129 vitest tests pass; a backlog that previously stalled
forever now drains to zero on a real deployment.
…th capping)

Three new adapter tests drive a real in-memory-file store through the
backfill -> scheduleExtract -> drainBatch path:

- a transient LLM failure is retried and the backlog drains to zero
- a permanently failing batch is bisected and each singleton is marked
  extracted (no deadlock), with the drain still progressing
- an oversized message batch is split by accumulated normalized length
  so no single extraction request exceeds the size cap

Also expose extractionStreamTimeoutMs / extractionRetryDelaysMs as
adapter config so hosts can tune (and tests can shorten) the resilience
knobs.
@adoresever
adoresever merged commit 1dadb34 into main Aug 31, 2026
2 checks passed
@adoresever
adoresever deleted the fix/extraction-drain-resilience branch August 31, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants