Files
stonks-oracle/services/intelligence_pipeline_v3/routing/rules.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

81 lines
3.2 KiB
Python

"""Hard ambiguity and conflict rules for routing decisions.
These rules check structural markers in extraction output that indicate
the document *requires* semantic reasoning by the 9B adjudicator. Any
triggered rule forces ADJUDICATION regardless of confidence scores.
"""
from __future__ import annotations
from typing import Any
from services.intelligence_pipeline_v3.routing.reasons import RoutingReason
def evaluate_hard_rules(
confidence_features: dict[str, Any],
ambiguity_markers: dict[str, Any],
) -> list[RoutingReason]:
"""Evaluate hard ambiguity/conflict rules against extraction output.
Parameters
----------
confidence_features:
Field-level confidence features from the confidence pipeline.
Expected keys include:
- ``evidence_coverage``: float 0-1
- ``material_fields_present``: bool
- ``cross_chunk_relations``: bool (relations span multiple chunks)
ambiguity_markers:
Structural ambiguity markers from candidate generation and resolution.
Expected keys include:
- ``unresolved_aliases``: int (count of unresolved entity aliases)
- ``primary_company_count``: int (number of primary companies detected)
- ``contradictory_numeric_facts``: bool
- ``conflicting_sentiment``: bool
- ``implied_causal_impact``: bool
- ``guidance_vs_consensus``: bool
- ``long_document_cross_chunk``: bool
Returns
-------
list[RoutingReason]
List of triggered reasons. Empty list means no hard rules triggered.
"""
triggered: list[RoutingReason] = []
# Unresolved entity aliases require contextual disambiguation
if ambiguity_markers.get("unresolved_aliases", 0) > 0:
triggered.append(RoutingReason.UNRESOLVED_ALIAS)
# Multiple primary companies need reasoning about which is the subject
if ambiguity_markers.get("primary_company_count", 0) > 1:
triggered.append(RoutingReason.MULTIPLE_PRIMARY_COMPANIES)
# Contradictory numeric facts (e.g., conflicting revenue figures)
if ambiguity_markers.get("contradictory_numeric_facts", False):
triggered.append(RoutingReason.CONTRADICTORY_NUMERIC_FACTS)
# Conflicting sentiment across evidence groups for the same company
if ambiguity_markers.get("conflicting_sentiment", False):
triggered.append(RoutingReason.CONFLICTING_SENTIMENT)
# Implied causal impact requiring reasoning (not explicit statement)
if ambiguity_markers.get("implied_causal_impact", False):
triggered.append(RoutingReason.IMPLIED_CAUSAL_IMPACT)
# Guidance vs consensus comparison requires model reasoning
if ambiguity_markers.get("guidance_vs_consensus", False):
triggered.append(RoutingReason.GUIDANCE_VS_CONSENSUS_REQUIRES_REASONING)
# Material fields missing from extraction output
if not confidence_features.get("material_fields_present", True):
triggered.append(RoutingReason.MATERIAL_FIELD_MISSING)
# Cross-chunk relations in long documents need broader context
if ambiguity_markers.get("long_document_cross_chunk", False):
triggered.append(RoutingReason.LONG_DOCUMENT_CROSS_CHUNK_RELATION)
return triggered