Skip to content

GitLab

  • Menu
Projects Groups Snippets
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • E Excitor KB
  • Project information
    • Project information
    • Activity
    • Labels
    • Planning hierarchy
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Metrics
    • Incidents
  • Packages & Registries
    • Packages & Registries
    • Package Registry
    • Container Registry
    • Infrastructure Registry
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Satyam Raj
  • Excitor KB
  • Merge requests
  • !1

Merged
Created Aug 31, 2026 by Satyam Raj@satyamrajMaintainer

Add a checkpointed LangGraph path for the analyzer, behind ANALYZE_GRAPH

  • Overview 0
  • Commits 2
  • Changes 11

Analysis was the last pipeline that could not resume. sync and embed have been resumable since they were written, but a two-hundred-requirement analysis is two hundred model calls, and losing it to a restart meant starting over.

analyzeBrd now dispatches to services/analyze/graph.js when ANALYZE_GRAPH is set: extract -> classify (one Send task per requirement) -> reduce, the shape it already had. Every verdict is written to a checkpoint as it lands, so a run that dies at requirement 180 of 200 resumes with twenty left. The thread id is content-derived and project-scoped, since the project scope changes which records are retrievable and therefore changes every verdict.

Off by default, and the import is dynamic and cached, so with the flag unset LangGraph is never loaded and this service's startup is untouched. classifyAll is unchanged and stays the covered path; the point of the flag is that the two can be compared.

No judgement moved into the graph. classifyRequirement is called unchanged, so the evidence gate stays where it is and stays covered by classifier.test.js. This module owns fan-out, ordering and persistence only.

Checkpoints go to SQLite via builtin node:sqlite, not @langchain/langgraph-checkpoint-sqlite: that depends on native better-sqlite3, which needs a compiler or a matching prebuilt at image build time, and deploy/DEPLOY.md already calls npm ci the most likely step to fail here. Same reasoning as the hand-rolled readers in services/parsers/. The trade is a version floor — node:sqlite is unflagged from Node 23.4 and needs --experimental-sqlite on the 22.x line the Dockerfile pins — so where the module is missing this degrades to LangGraph's in-memory saver and reports durable: false. That still resumes within one process, which is exactly today's behaviour, so the fallback is never a failure to start.

Two LangGraph 1.4.13 behaviours worked around:

  • A node may not share a name with a state channel, so the reduce node is reduce and matrix is the channel its output lands in.
  • maxConcurrency: 1 silently truncates a run. The task loop in pregel/runner.js continues only while a task is still executing, so with no overlap it exits after the first task and invoke resolves with partial state and no error — one classification out of two hundred, reported as success. The bound is applied by a local semaphore inside the classify node instead, where it cannot cut the run short. Raising the floor to 2 would have quietly violated a limit that exists to keep from queueing every call at Ollama.

23 tests in tests/analyzeGraph.test.js: graph-vs-classifyAll parity on rows, coverage, sections, attention and results; a bound of one still classifying a whole document; crash-and-resume; durability across two separate connections to a real SQLite file; and state hygiene, asserting the 25 MB upload buffer, the injected functions and the progress callback stay in the closure and never reach the checkpoint.

Assignee
Assign to
Reviewer
Request review from
Time tracking
Source branch: analyzer-langgraph