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.
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
"""Safety-critical field definitions for v3 promotion gates.
|
||||
|
||||
Fields marked as safety-critical MUST pass their quality gates before pipeline
|
||||
outputs are allowed to influence production aggregation or trading decisions.
|
||||
A single safety-critical failure blocks promotion for the affected document type.
|
||||
|
||||
Schema version: 1.0.0
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class SafetyCriticalField(str, Enum):
|
||||
"""Fields whose incorrect extraction can materially affect trading decisions."""
|
||||
|
||||
# Company identity — wrong ticker attribution can cause trades on the wrong security
|
||||
COMPANY_IDENTITY = "company_identity"
|
||||
|
||||
# Event classification — misclassifying earnings_beat as earnings_miss inverts signals
|
||||
EVENT_CLASS = "event_class"
|
||||
|
||||
# Sentiment direction — wrong sentiment directly affects position direction
|
||||
SENTIMENT_DIRECTION = "sentiment_direction"
|
||||
|
||||
# Numeric fact accuracy — wrong EPS or revenue magnitude affects impact estimation
|
||||
NUMERIC_FACT_VALUE = "numeric_fact_value"
|
||||
|
||||
# Direct effect attribution — attributing an event to the wrong company creates false signals
|
||||
DIRECT_EFFECT_ATTRIBUTION = "direct_effect_attribution"
|
||||
|
||||
# Evidence support — claims without valid evidence spans are unverifiable
|
||||
EVIDENCE_SUPPORT = "evidence_support"
|
||||
|
||||
# Confidence calibration — overconfident scores bypass appropriate review thresholds
|
||||
CONFIDENCE_CALIBRATION = "confidence_calibration"
|
||||
|
||||
|
||||
# Map each safety-critical field to its minimum required quality metric for promotion
|
||||
SAFETY_CRITICAL_FIELDS: dict[SafetyCriticalField, dict[str, float]] = {
|
||||
SafetyCriticalField.COMPANY_IDENTITY: {
|
||||
"precision": 0.95,
|
||||
"recall": 0.90,
|
||||
"f1": 0.92,
|
||||
},
|
||||
SafetyCriticalField.EVENT_CLASS: {
|
||||
"macro_f1": 0.85,
|
||||
"per_class_min_f1": 0.70,
|
||||
},
|
||||
SafetyCriticalField.SENTIMENT_DIRECTION: {
|
||||
"macro_f1": 0.85,
|
||||
"direction_accuracy": 0.90,
|
||||
},
|
||||
SafetyCriticalField.NUMERIC_FACT_VALUE: {
|
||||
"exact_match": 0.80,
|
||||
"tolerance_match_5pct": 0.92,
|
||||
},
|
||||
SafetyCriticalField.DIRECT_EFFECT_ATTRIBUTION: {
|
||||
"precision": 0.93,
|
||||
"recall": 0.88,
|
||||
},
|
||||
SafetyCriticalField.EVIDENCE_SUPPORT: {
|
||||
"support_rate": 0.95,
|
||||
"offset_validity": 0.98,
|
||||
},
|
||||
SafetyCriticalField.CONFIDENCE_CALIBRATION: {
|
||||
"ece": 0.05, # Expected Calibration Error — lower is better
|
||||
"brier_score": 0.15, # Lower is better
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class SafetyGateResult:
|
||||
"""Result of checking a single safety-critical field against its gate."""
|
||||
|
||||
field: SafetyCriticalField
|
||||
passed: bool
|
||||
metric_name: str
|
||||
required_value: float
|
||||
actual_value: float
|
||||
is_lower_better: bool = False
|
||||
|
||||
@property
|
||||
def margin(self) -> float:
|
||||
"""How far above (or below for lower-is-better) the threshold."""
|
||||
if self.is_lower_better:
|
||||
return self.required_value - self.actual_value
|
||||
return self.actual_value - self.required_value
|
||||
|
||||
|
||||
def check_safety_gates(
|
||||
metrics: dict[SafetyCriticalField, dict[str, float]],
|
||||
) -> list[SafetyGateResult]:
|
||||
"""Check all safety-critical fields against their promotion thresholds.
|
||||
|
||||
Args:
|
||||
metrics: Measured quality metrics per safety-critical field.
|
||||
Keys match SafetyCriticalField, values are metric_name -> value dicts.
|
||||
|
||||
Returns:
|
||||
List of SafetyGateResult for each check performed.
|
||||
Any result with passed=False blocks promotion.
|
||||
"""
|
||||
lower_is_better = {"ece", "brier_score"}
|
||||
results: list[SafetyGateResult] = []
|
||||
|
||||
for field, thresholds in SAFETY_CRITICAL_FIELDS.items():
|
||||
measured = metrics.get(field, {})
|
||||
for metric_name, required in thresholds.items():
|
||||
actual = measured.get(metric_name)
|
||||
if actual is None:
|
||||
# Missing metric fails the gate
|
||||
results.append(
|
||||
SafetyGateResult(
|
||||
field=field,
|
||||
passed=False,
|
||||
metric_name=metric_name,
|
||||
required_value=required,
|
||||
actual_value=float("nan"),
|
||||
is_lower_better=metric_name in lower_is_better,
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
is_lower = metric_name in lower_is_better
|
||||
if is_lower:
|
||||
passed = actual <= required
|
||||
else:
|
||||
passed = actual >= required
|
||||
|
||||
results.append(
|
||||
SafetyGateResult(
|
||||
field=field,
|
||||
passed=passed,
|
||||
metric_name=metric_name,
|
||||
required_value=required,
|
||||
actual_value=actual,
|
||||
is_lower_better=is_lower,
|
||||
)
|
||||
)
|
||||
|
||||
return results
|
||||
Reference in New Issue
Block a user