Files
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

121 lines
4.1 KiB
Python

"""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),
}