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.
121 lines
4.1 KiB
Python
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),
|
|
}
|