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,120 @@
|
||||
"""Credential and sensitive header redaction utilities.
|
||||
|
||||
Ensures that authentication values, API keys, bearer tokens, and other
|
||||
sensitive headers are never included in logs, traces, serialized request
|
||||
snapshots, or error messages.
|
||||
|
||||
Requirements: 2.8
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any, Mapping
|
||||
|
||||
from services.shared.inference.models import InferenceTarget
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sensitive header detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
#: Header names (lowercase) that are always redacted.
|
||||
SENSITIVE_HEADER_NAMES: frozenset[str] = frozenset(
|
||||
{
|
||||
"authorization",
|
||||
"x-api-key",
|
||||
"api-key",
|
||||
"x-auth-token",
|
||||
"proxy-authorization",
|
||||
"cookie",
|
||||
"set-cookie",
|
||||
"x-secret",
|
||||
"x-access-token",
|
||||
}
|
||||
)
|
||||
|
||||
_REDACTED = "***REDACTED***"
|
||||
|
||||
# Patterns that look like bearer tokens or API keys in free text
|
||||
_TOKEN_PATTERNS: list[re.Pattern[str]] = [
|
||||
# Bearer tokens
|
||||
re.compile(r"(Bearer\s+)\S+", re.IGNORECASE),
|
||||
# Common API key formats (sk-..., pk-..., key-..., token-..., api_key_...)
|
||||
re.compile(r"\b((?:sk|pk|key|token|api[_-]?key)[_-])\S{8,}", re.IGNORECASE),
|
||||
]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Header redaction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def redact_headers(headers: Mapping[str, str]) -> dict[str, str]:
|
||||
"""Return a copy of headers with sensitive values replaced by a placeholder.
|
||||
|
||||
Matches header names case-insensitively against ``SENSITIVE_HEADER_NAMES``.
|
||||
"""
|
||||
result: dict[str, str] = {}
|
||||
for name, value in headers.items():
|
||||
if name.lower() in SENSITIVE_HEADER_NAMES:
|
||||
result[name] = _REDACTED
|
||||
else:
|
||||
result[name] = value
|
||||
return result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Error message redaction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def redact_error_message(message: str) -> str:
|
||||
"""Remove bearer tokens and API key patterns from text.
|
||||
|
||||
Applied to error messages before they are logged or stored.
|
||||
"""
|
||||
result = message
|
||||
for pattern in _TOKEN_PATTERNS:
|
||||
result = pattern.sub(
|
||||
lambda m: m.group(1) + _REDACTED if m.lastindex else _REDACTED,
|
||||
result,
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Target redaction for logging / serialization
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def redact_target_for_logging(target: InferenceTarget) -> dict[str, Any]:
|
||||
"""Serialize an InferenceTarget for logging with credentials redacted.
|
||||
|
||||
- ``auth_secret_ref`` is included as the reference name (e.g.
|
||||
"vault://inference/openai-key") so operators can identify which
|
||||
secret is in use. The target never holds the resolved secret value.
|
||||
- Sensitive headers in ``extra_headers`` are redacted.
|
||||
- ``extra_body`` is included as-is (callers should not place secrets there).
|
||||
|
||||
Requirements: 2.8
|
||||
"""
|
||||
return {
|
||||
"endpoint_id": str(target.endpoint_id),
|
||||
"deployment_id": str(target.deployment_id),
|
||||
"protocol": target.protocol,
|
||||
"base_url": target.base_url,
|
||||
"model": target.model,
|
||||
"capabilities": {
|
||||
"chat_completions": target.capabilities.chat_completions,
|
||||
"responses_api": target.capabilities.responses_api,
|
||||
"json_schema": target.capabilities.json_schema,
|
||||
"json_object": target.capabilities.json_object,
|
||||
"seed": target.capabilities.seed,
|
||||
"usage": target.capabilities.usage,
|
||||
"max_completion_tokens": target.capabilities.max_completion_tokens,
|
||||
"reasoning_toggle": target.capabilities.reasoning_toggle,
|
||||
"model_listing": target.capabilities.model_listing,
|
||||
},
|
||||
"auth_secret_ref": target.auth_secret_ref,
|
||||
"extra_headers": redact_headers(dict(target.extra_headers)),
|
||||
"extra_body": dict(target.extra_body),
|
||||
}
|
||||
Reference in New Issue
Block a user