Files
stonks-oracle/services/intelligence_pipeline_v3/routing/router.py
T
Celes Renata a72f336ad1 feat: Intelligence Pipeline v3 — full implementation
Multi-stage evidence-grounded inference architecture replacing the
monolithic 9B model extraction pipeline. CPU-first specialist services
handle routine extraction while the 9B vLLM model is preserved for
semantic adjudication of ambiguous cases.

Key components:
- Capability-aware inference gateway (OpenAI-compatible + Ollama)
- Endpoint registry with DB migrations and REST API
- Sentence-aware document segmenter (property tests)
- Deterministic financial parsing with offset integrity
- Symbol resolution with ambiguity detection
- Specialist service (GLiNER2, dynamic batching, K8s deployment)
- Company-specific sentiment (FinBERT, calibration)
- Retrieval-based novelty and duplicate detection
- Confidence calibration pipeline
- Deterministic routing engine (property tests)
- 9B adjudication layer with VRAM gating
- Stock-specific impact model (features, labels, baseline, trained)
- Pipeline orchestrator (state machine, queues, leases, feature flags)
- Bounded parallelism (async workers, semaphore, load shedding)
- Observability (tracing, metrics, alerts)
- Compatibility adapter (v3→v2 golden mapping tests)
- Shadow/canary promotion framework
- Active learning and fine-tuning pipeline

Test results: 1,161 tests pass, ruff lint clean.
All 282 spec tasks completed.
2026-07-13 02:14:59 +00:00

184 lines
6.1 KiB
Python

"""Deterministic routing engine for Intelligence Pipeline v3.
The RoutingEngine combines hard ambiguity/conflict rules with calibrated
confidence thresholds to produce a deterministic route decision. The same
inputs always produce the same output — no randomness or side effects.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
from uuid import UUID, uuid4
from services.intelligence_pipeline_v3.routing.reasons import (
RouteDecision,
RoutingReason,
)
from services.intelligence_pipeline_v3.routing.rules import evaluate_hard_rules
from services.intelligence_pipeline_v3.routing.thresholds import (
FastPathThresholds,
evaluate_thresholds,
)
@dataclass(frozen=True)
class RoutingDecision:
"""Immutable record of a routing decision with full context.
Attributes
----------
id:
Unique identifier for this decision.
pipeline_run_id:
The pipeline run this decision belongs to.
document_id:
The document being routed.
route:
The binary routing outcome (fast_path or adjudication).
reasons:
List of routing reasons explaining the decision.
confidence_snapshot:
Full feature snapshot at decision time for audit and recalibration.
decided_at:
UTC timestamp of the decision.
"""
id: UUID
pipeline_run_id: UUID
document_id: UUID
route: RouteDecision
reasons: list[RoutingReason]
confidence_snapshot: dict[str, Any]
decided_at: datetime
@dataclass
class RoutingEngine:
"""Deterministic routing engine.
Evaluates hard rules first, then applies confidence thresholds.
Same inputs always produce the same route — no randomness, no external
state dependency beyond the provided arguments.
Parameters
----------
thresholds:
Fast-path threshold configuration. Defaults to conservative values.
"""
thresholds: FastPathThresholds = field(default_factory=FastPathThresholds)
def route(
self,
pipeline_run_id: UUID,
document_id: UUID,
confidence_features: dict[str, Any],
ambiguity_markers: dict[str, Any],
document_type: str,
event_type: str | None = None,
) -> RoutingDecision:
"""Produce a deterministic routing decision.
Parameters
----------
pipeline_run_id:
The pipeline run identifier.
document_id:
The document being routed.
confidence_features:
Field-level confidence features from the confidence pipeline.
Must include ``calibrated_confidence`` (float 0-1).
ambiguity_markers:
Structural ambiguity markers from candidate generation.
document_type:
The document type (article, filing, transcript, etc.).
event_type:
Optional event type detected in the document.
Returns
-------
RoutingDecision
Immutable decision record with route, reasons, and feature snapshot.
"""
# Step 1: Evaluate hard rules (any trigger = adjudication)
hard_reasons = evaluate_hard_rules(confidence_features, ambiguity_markers)
if hard_reasons:
return self._build_decision(
pipeline_run_id=pipeline_run_id,
document_id=document_id,
route=RouteDecision.ADJUDICATION,
reasons=hard_reasons,
confidence_features=confidence_features,
ambiguity_markers=ambiguity_markers,
)
# Step 2: Evaluate confidence thresholds
calibrated_confidence = confidence_features.get("calibrated_confidence", 0.0)
# Check evidence coverage threshold (hard threshold, not configurable per doc type)
evidence_coverage = confidence_features.get("evidence_coverage", 1.0)
if evidence_coverage < 0.5:
return self._build_decision(
pipeline_run_id=pipeline_run_id,
document_id=document_id,
route=RouteDecision.ADJUDICATION,
reasons=[RoutingReason.EVIDENCE_COVERAGE_BELOW_THRESHOLD],
confidence_features=confidence_features,
ambiguity_markers=ambiguity_markers,
)
# Apply calibrated confidence threshold
threshold_decision = evaluate_thresholds(
confidence=calibrated_confidence,
document_type=document_type,
event_type=event_type,
thresholds=self.thresholds,
)
if threshold_decision == RouteDecision.ADJUDICATION:
return self._build_decision(
pipeline_run_id=pipeline_run_id,
document_id=document_id,
route=RouteDecision.ADJUDICATION,
reasons=[RoutingReason.CALIBRATED_CONFIDENCE_BELOW_THRESHOLD],
confidence_features=confidence_features,
ambiguity_markers=ambiguity_markers,
)
# All checks passed — fast path accepted
return self._build_decision(
pipeline_run_id=pipeline_run_id,
document_id=document_id,
route=RouteDecision.FAST_PATH,
reasons=[RoutingReason.FAST_PATH_ACCEPTED],
confidence_features=confidence_features,
ambiguity_markers=ambiguity_markers,
)
def _build_decision(
self,
pipeline_run_id: UUID,
document_id: UUID,
route: RouteDecision,
reasons: list[RoutingReason],
confidence_features: dict[str, Any],
ambiguity_markers: dict[str, Any],
) -> RoutingDecision:
"""Build an immutable routing decision with full snapshot."""
return RoutingDecision(
id=uuid4(),
pipeline_run_id=pipeline_run_id,
document_id=document_id,
route=route,
reasons=reasons,
confidence_snapshot={
"confidence_features": confidence_features,
"ambiguity_markers": ambiguity_markers,
"thresholds_version": self.thresholds.version,
},
decided_at=datetime.now(timezone.utc),
)