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:
Celes Renata
2026-07-13 02:14:59 +00:00
parent 84634a365e
commit a72f336ad1
227 changed files with 50403 additions and 0 deletions
@@ -0,0 +1,165 @@
"""Replaceable compact embedding backend for novelty retrieval.
Provides:
- EmbeddingBackend protocol for pluggable embedding models
- MockEmbeddingBackend for deterministic testing
- SentenceTransformerBackend stub for production (all-MiniLM-L6-v2)
- cosine_similarity utility function
"""
from __future__ import annotations
import hashlib
import math
import struct
from typing import Protocol, runtime_checkable
@runtime_checkable
class EmbeddingBackend(Protocol):
"""Protocol for embedding backends.
Implementations must produce fixed-dimension vectors for a batch of texts.
The backend is designed to be replaceable: swap between mock, local model,
and remote API backends without changing scoring logic.
"""
@property
def dimension(self) -> int:
"""Return the embedding dimension produced by this backend."""
...
def embed(self, texts: list[str]) -> list[list[float]]:
"""Embed a batch of texts into dense vectors.
Args:
texts: List of text strings to embed.
Returns:
List of embedding vectors, one per input text.
Each vector has length == self.dimension.
"""
...
class MockEmbeddingBackend:
"""Deterministic hash-based embedding backend for testing.
Produces consistent embeddings using text hashing. Useful for
unit tests and integration tests that need repeatable results
without loading a real model.
"""
def __init__(self, dimension: int = 384) -> None:
self._dimension = dimension
@property
def dimension(self) -> int:
return self._dimension
def embed(self, texts: list[str]) -> list[list[float]]:
"""Generate deterministic embeddings from text hashes.
Uses SHA-256 expanded to fill the dimension. Normalizes to unit length
for compatibility with cosine similarity.
"""
results = []
for text in texts:
raw = self._hash_to_vector(text)
norm = math.sqrt(sum(x * x for x in raw))
if norm > 0:
normalized = [x / norm for x in raw]
else:
normalized = raw
results.append(normalized)
return results
def _hash_to_vector(self, text: str) -> list[float]:
"""Expand text hash into a vector of the target dimension."""
vector = []
# Generate enough hash bytes to fill dimension
chunk_idx = 0
while len(vector) < self._dimension:
data = f"{text}:{chunk_idx}".encode("utf-8")
digest = hashlib.sha256(data).digest()
# Convert 32 bytes to 8 floats (4 bytes each)
for i in range(0, 32, 4):
if len(vector) >= self._dimension:
break
# Unpack as float in [-1, 1] range
raw_int = struct.unpack("<I", digest[i : i + 4])[0]
value = (raw_int / (2**32 - 1)) * 2.0 - 1.0
vector.append(value)
chunk_idx += 1
return vector[: self._dimension]
class SentenceTransformerBackend:
"""Production embedding backend using sentence-transformers.
Wraps all-MiniLM-L6-v2 (384-dimensional) for compact, fast embeddings.
The model loads lazily on first call to avoid startup cost when not needed.
Note: Requires `sentence-transformers` package to be installed.
This is a stub—actual model loading is deferred to production deployment.
"""
MODEL_NAME = "all-MiniLM-L6-v2"
def __init__(self) -> None:
self._model = None
self._dimension = 384
@property
def dimension(self) -> int:
return self._dimension
def embed(self, texts: list[str]) -> list[list[float]]:
"""Embed texts using the sentence-transformers model.
Lazily loads the model on first invocation.
Raises:
ImportError: If sentence-transformers is not installed.
"""
if self._model is None:
self._load_model()
embeddings = self._model.encode(texts, normalize_embeddings=True)
return [emb.tolist() for emb in embeddings]
def _load_model(self) -> None:
"""Load the sentence-transformers model."""
try:
from sentence_transformers import SentenceTransformer
except ImportError as e:
raise ImportError(
"sentence-transformers package is required for SentenceTransformerBackend. "
"Install with: pip install sentence-transformers"
) from e
self._model = SentenceTransformer(self.MODEL_NAME)
def cosine_similarity(a: list[float], b: list[float]) -> float:
"""Compute cosine similarity between two embedding vectors.
Args:
a: First embedding vector.
b: Second embedding vector (must be same dimension as a).
Returns:
Cosine similarity in range [-1, 1]. Returns 0.0 for zero vectors.
Raises:
ValueError: If vectors have different dimensions.
"""
if len(a) != len(b):
raise ValueError(f"Vectors must have same dimension: {len(a)} != {len(b)}")
dot = sum(x * y for x, y in zip(a, b))
norm_a = math.sqrt(sum(x * x for x in a))
norm_b = math.sqrt(sum(x * x for x in b))
if norm_a == 0.0 or norm_b == 0.0:
return 0.0
return dot / (norm_a * norm_b)