feat(graph-rag): report gleaning as its own extraction stage - #145
Conversation
Gleaning is the second extraction round: a per-chunk model call that recovers entities the first round missed, and the part of extraction a profile can disable or a token guard can decline. Folded into EXTRACT it was indistinguishable from the round that always runs, so a deployment could not tell gleaning working from gleaning silently not happening. The extractor was already recording exactly that. ExtractionDiagnostics carries a per-round metric and a gleaning outcome, every ExtractedChunk carries it into the worker, and nothing published it. The stage counts eligible chunks against chunks that completed a round, so a declined round shows as the gap between the two. Its duration is the second round's model time alone, aggregated across chunks that glean concurrently — it is what gleaning cost rather than how long it took, and it is nested inside EXTRACT's wall clock, so stage durations within one job are not additive. A profile with gleaning disabled emits nothing. A zero-valued series would claim a round that was never configured to run, which is the same lie as an empty series that looks like silence. GENERATE and time to first token are not here. Generation happens in the application shell rather than the GraphRAG runtime, above an engine-neutral retrieval interface with a non-GraphRAG implementation, so wiring it to a GraphRAG stage is a boundary decision rather than a wiring one. Recorded with its strongest counterargument in challenge-generation-telemetry.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (4)
📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Continues the Phase 2 stage gap.
Stagedeclared fourteen values and production emitted ten; this makes it eleven.What was invisible
Gleaning is the second extraction round — a per-chunk model call that recovers entities the first round missed. It is also the part of extraction that a profile can disable and a token guard can decline at runtime. Folded into
EXTRACT, all three cases looked identical: gleaning working, gleaning configured off, and gleaning being declined chunk after chunk because the prompt grew too large.The extractor was already recording which.
ExtractionDiagnosticscarries a per-round metric and aGleaningOutcome, and everyExtractedChunkcarries it into the worker. Nothing published it — the same pattern as the token usage in #142.Shape
Emitted once per indexing job, when the profile enables gleaning:
inputCount— chunks eligibleoutputCount— chunks that completed a gleaning roundA declined round is the gap between them, which is what makes the token guard visible.
Duration is the second round's model time alone, aggregated across chunks that glean concurrently. That is what gleaning cost, not how long it took, and it is nested inside
EXTRACT's wall clock rather than sequential with it — so stage durations within one job are not additive. Said explicitly in the code and the spec, because a dashboard that sums them would be wrong and nothing else would tell it so.Nothing is emitted when the profile disables gleaning. A zero-valued series would claim a round that was never configured to run.
GENERATEand TTFT are not hereThey were the next items on the plan and I stopped short of implementing them, because they are not a wiring task:
QueryOutputMode.ANSWERexists, butGraphRagRetrievalPolicypinsCONTEXT— generation was deliberately moved into the application shell so the shell can re-verify the evidence closure before delivery.AssistantServicesits abovePermissionAwareKnowledgeSearch, which has a second, non-GraphRAG implementation (CanonicalHybridKnowledgeSearch) selected by configuration.GraphRagEventrequires a non-nulloperationIdthat only the GraphRAG service mints and that never leaves it.So emitting
Stage.GENERATEfrom the assistant either labels canonical-engine turns as GraphRAG stages, or threads a GraphRAG identifier through an engine-neutral interface.CLAUDE.mdrequires an independent architecture challenge before that class of decision.challenge-generation-telemetry.mdstates the proposal, the repository evidence, and the strongest counterargument against my own proposal — thatGraphRagEventSinkis the enforcement point of the payload boundary, and generation is the one stage where prompts and completions actually exist, so a second telemetry surface for exactly that stage is how a structural guarantee decays into a conventional one.Also found, not fixed
ExtractionRoundMetricsalready carriesproviderInputTokensandproviderOutputTokensper round, unpublished. Ingestion spend is invisible while retrieval spend is not. TheTokenUsagerecord from #142 does not fit — its channels are retrieval's — so it needs its own shape rather than a forced reuse. Recorded on the plan.Verification
:apps:worker:test,:components:graph-rag-core:test. Green.🤖 Generated with Claude Code