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,670 @@
|
||||
"""Tests for the inference registry API.
|
||||
|
||||
Covers:
|
||||
- CRUD operations for endpoints, deployments, bindings
|
||||
- auth_secret_ref NEVER appears in any response body
|
||||
- Probe action returns structured results
|
||||
- Enable/disable toggles
|
||||
- External egress requires confirmation
|
||||
- Protocol validation rejects unknown protocols
|
||||
- Endpoint creation validates URL format
|
||||
|
||||
Requirements: 3.6, 3.7
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from fastapi import FastAPI
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from services.inference_registry.router import (
|
||||
InferenceRegistryDB,
|
||||
router,
|
||||
set_db,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mock DB implementation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class MockInferenceDB(InferenceRegistryDB):
|
||||
"""In-memory mock implementation of the registry database."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.endpoints: dict[uuid.UUID, dict[str, Any]] = {}
|
||||
self.deployments: dict[uuid.UUID, dict[str, Any]] = {}
|
||||
self.bindings: dict[uuid.UUID, dict[str, Any]] = {}
|
||||
self.probes: dict[uuid.UUID, dict[str, Any]] = {}
|
||||
self.egress_confirmations: set[uuid.UUID] = set()
|
||||
|
||||
async def list_endpoints(self) -> list[dict[str, Any]]:
|
||||
return list(self.endpoints.values())
|
||||
|
||||
async def get_endpoint(self, endpoint_id: uuid.UUID) -> dict[str, Any] | None:
|
||||
return self.endpoints.get(endpoint_id)
|
||||
|
||||
async def create_endpoint(self, data: dict[str, Any]) -> dict[str, Any]:
|
||||
self.endpoints[data["id"]] = data
|
||||
return data
|
||||
|
||||
async def update_endpoint(
|
||||
self, endpoint_id: uuid.UUID, data: dict[str, Any]
|
||||
) -> dict[str, Any] | None:
|
||||
if endpoint_id not in self.endpoints:
|
||||
return None
|
||||
self.endpoints[endpoint_id].update(data)
|
||||
return self.endpoints[endpoint_id]
|
||||
|
||||
async def disable_endpoint(self, endpoint_id: uuid.UUID) -> dict[str, Any] | None:
|
||||
if endpoint_id not in self.endpoints:
|
||||
return None
|
||||
self.endpoints[endpoint_id]["enabled"] = False
|
||||
self.endpoints[endpoint_id]["updated_at"] = datetime.now(timezone.utc)
|
||||
return self.endpoints[endpoint_id]
|
||||
|
||||
async def list_deployments(
|
||||
self, endpoint_id: uuid.UUID | None = None
|
||||
) -> list[dict[str, Any]]:
|
||||
if endpoint_id:
|
||||
return [
|
||||
d for d in self.deployments.values()
|
||||
if d["endpoint_id"] == endpoint_id
|
||||
]
|
||||
return list(self.deployments.values())
|
||||
|
||||
async def get_deployment(self, deployment_id: uuid.UUID) -> dict[str, Any] | None:
|
||||
return self.deployments.get(deployment_id)
|
||||
|
||||
async def create_deployment(self, data: dict[str, Any]) -> dict[str, Any]:
|
||||
self.deployments[data["id"]] = data
|
||||
return data
|
||||
|
||||
async def list_bindings(
|
||||
self,
|
||||
agent_id: uuid.UUID | None = None,
|
||||
endpoint_id: uuid.UUID | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
result = list(self.bindings.values())
|
||||
if agent_id:
|
||||
result = [b for b in result if b["agent_id"] == agent_id]
|
||||
return result
|
||||
|
||||
async def create_binding(self, data: dict[str, Any]) -> dict[str, Any]:
|
||||
self.bindings[data["id"]] = data
|
||||
return data
|
||||
|
||||
async def get_bindings_for_endpoint(
|
||||
self, endpoint_id: uuid.UUID
|
||||
) -> list[dict[str, Any]]:
|
||||
# Find bindings whose deployment is on this endpoint
|
||||
dep_ids = {
|
||||
d["id"] for d in self.deployments.values()
|
||||
if d["endpoint_id"] == endpoint_id
|
||||
}
|
||||
return [
|
||||
b for b in self.bindings.values()
|
||||
if b.get("model_deployment_id") in dep_ids
|
||||
]
|
||||
|
||||
async def get_last_probe(
|
||||
self, endpoint_id: uuid.UUID
|
||||
) -> dict[str, Any] | None:
|
||||
return self.probes.get(endpoint_id)
|
||||
|
||||
async def store_probe_result(
|
||||
self, endpoint_id: uuid.UUID, result: dict[str, Any]
|
||||
) -> None:
|
||||
self.probes[endpoint_id] = result
|
||||
|
||||
async def get_egress_confirmation(self, endpoint_id: uuid.UUID) -> bool:
|
||||
return endpoint_id in self.egress_confirmations
|
||||
|
||||
async def store_egress_confirmation(self, endpoint_id: uuid.UUID) -> None:
|
||||
self.egress_confirmations.add(endpoint_id)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db() -> MockInferenceDB:
|
||||
return MockInferenceDB()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app(mock_db: MockInferenceDB) -> FastAPI:
|
||||
test_app = FastAPI()
|
||||
test_app.include_router(router)
|
||||
set_db(mock_db)
|
||||
return test_app
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def client(app: FastAPI) -> AsyncClient:
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
yield c
|
||||
|
||||
|
||||
def _make_endpoint_payload(
|
||||
name: str = "test-endpoint",
|
||||
protocol: str = "openai_chat",
|
||||
base_url: str = "http://localhost:8000",
|
||||
auth_secret_ref: str | None = "VLLM_API_KEY",
|
||||
) -> dict[str, Any]:
|
||||
"""Helper to build a valid endpoint creation payload."""
|
||||
return {
|
||||
"name": name,
|
||||
"protocol": protocol,
|
||||
"base_url": base_url,
|
||||
"auth_secret_ref": auth_secret_ref,
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: CRUD endpoints (19.1)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_endpoint(client: AsyncClient):
|
||||
"""Creating an endpoint returns 201 with redacted secrets."""
|
||||
payload = _make_endpoint_payload()
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
assert data["name"] == "test-endpoint"
|
||||
assert data["protocol"] == "openai_chat"
|
||||
assert data["base_url"] == "http://localhost:8000"
|
||||
assert data["auth_secret_status"] == "configured"
|
||||
# CRITICAL: auth_secret_ref must NEVER appear in response
|
||||
assert "auth_secret_ref" not in data
|
||||
assert "VLLM_API_KEY" not in str(data)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_endpoint_no_secret(client: AsyncClient):
|
||||
"""Creating an endpoint without a secret shows not_configured."""
|
||||
payload = _make_endpoint_payload(auth_secret_ref=None)
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
assert data["auth_secret_status"] == "not_configured"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_endpoints(client: AsyncClient):
|
||||
"""Listing endpoints returns all with redacted secrets."""
|
||||
await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(name="ep-1"),
|
||||
)
|
||||
await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(name="ep-2", auth_secret_ref="SECRET_KEY"),
|
||||
)
|
||||
resp = await client.get("/api/inference/endpoints")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert len(data) == 2
|
||||
for ep in data:
|
||||
assert "auth_secret_ref" not in ep
|
||||
assert "SECRET_KEY" not in str(ep)
|
||||
assert "VLLM_API_KEY" not in str(ep)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_endpoint_detail(client: AsyncClient):
|
||||
"""Getting an endpoint by ID returns detail with redacted secrets."""
|
||||
create_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(auth_secret_ref="MY_SECRET"),
|
||||
)
|
||||
ep_id = create_resp.json()["id"]
|
||||
resp = await client.get(f"/api/inference/endpoints/{ep_id}")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["auth_secret_status"] == "configured"
|
||||
assert "MY_SECRET" not in str(data)
|
||||
assert "auth_secret_ref" not in data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_endpoint_not_found(client: AsyncClient):
|
||||
"""Getting a nonexistent endpoint returns 404."""
|
||||
fake_id = str(uuid.uuid4())
|
||||
resp = await client.get(f"/api/inference/endpoints/{fake_id}")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_endpoint(client: AsyncClient):
|
||||
"""Updating an endpoint works and still redacts secrets."""
|
||||
create_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(),
|
||||
)
|
||||
ep_id = create_resp.json()["id"]
|
||||
resp = await client.put(
|
||||
f"/api/inference/endpoints/{ep_id}",
|
||||
json={"name": "updated-endpoint", "base_url": "http://new-host:9000"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["name"] == "updated-endpoint"
|
||||
assert data["base_url"] == "http://new-host:9000"
|
||||
assert data["revision"] == 2
|
||||
assert "auth_secret_ref" not in data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_endpoint_soft_disables(client: AsyncClient):
|
||||
"""Deleting an endpoint soft-disables it."""
|
||||
create_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(),
|
||||
)
|
||||
ep_id = create_resp.json()["id"]
|
||||
resp = await client.delete(f"/api/inference/endpoints/{ep_id}")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["enabled"] is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: Probe, enable, disable actions (19.2)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enable_endpoint(client: AsyncClient, mock_db: MockInferenceDB):
|
||||
"""Enable action sets enabled=True for local endpoints."""
|
||||
create_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(base_url="http://localhost:8000"),
|
||||
)
|
||||
ep_id = create_resp.json()["id"]
|
||||
# First disable it
|
||||
await client.post(f"/api/inference/endpoints/{ep_id}/disable")
|
||||
# Then enable
|
||||
resp = await client.post(f"/api/inference/endpoints/{ep_id}/enable")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["enabled"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disable_endpoint_action(client: AsyncClient):
|
||||
"""Disable action sets enabled=False."""
|
||||
create_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(),
|
||||
)
|
||||
ep_id = create_resp.json()["id"]
|
||||
resp = await client.post(f"/api/inference/endpoints/{ep_id}/disable")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["enabled"] is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: External egress requires confirmation (19.5)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_external_endpoint_disabled_without_egress(client: AsyncClient):
|
||||
"""External endpoint is created disabled until egress confirmed."""
|
||||
payload = _make_endpoint_payload(
|
||||
base_url="https://api.openai.com",
|
||||
name="openai-prod",
|
||||
)
|
||||
payload["enabled"] = True # Request enabled, but external
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
# External endpoints are forced disabled until egress is confirmed
|
||||
assert data["enabled"] is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enable_external_requires_confirmation(client: AsyncClient):
|
||||
"""Enabling an external endpoint without confirmation returns 403."""
|
||||
payload = _make_endpoint_payload(
|
||||
base_url="https://api.openai.com",
|
||||
name="openai-prod",
|
||||
)
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
ep_id = resp.json()["id"]
|
||||
# Try to enable without confirming egress
|
||||
resp = await client.post(f"/api/inference/endpoints/{ep_id}/enable")
|
||||
assert resp.status_code == 403
|
||||
assert "egress confirmation" in resp.json()["detail"].lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_confirm_egress_enables_external(client: AsyncClient):
|
||||
"""Confirming egress enables the external endpoint."""
|
||||
payload = _make_endpoint_payload(
|
||||
base_url="https://api.openai.com",
|
||||
name="openai-prod",
|
||||
)
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
ep_id = resp.json()["id"]
|
||||
# Confirm egress
|
||||
resp = await client.post(
|
||||
f"/api/inference/endpoints/{ep_id}/confirm-egress",
|
||||
json={"confirmed": True},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["enabled"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_confirm_egress_rejects_false(client: AsyncClient):
|
||||
"""Egress confirmation with confirmed=false is rejected."""
|
||||
payload = _make_endpoint_payload(
|
||||
base_url="https://api.openai.com",
|
||||
name="openai-prod",
|
||||
)
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
ep_id = resp.json()["id"]
|
||||
resp = await client.post(
|
||||
f"/api/inference/endpoints/{ep_id}/confirm-egress",
|
||||
json={"confirmed": False},
|
||||
)
|
||||
assert resp.status_code == 422 # Pydantic validation error
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_confirm_egress_local_endpoint_rejected(client: AsyncClient):
|
||||
"""Confirming egress on a local endpoint returns 400."""
|
||||
payload = _make_endpoint_payload(base_url="http://localhost:8000")
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
ep_id = resp.json()["id"]
|
||||
resp = await client.post(
|
||||
f"/api/inference/endpoints/{ep_id}/confirm-egress",
|
||||
json={"confirmed": True},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: Protocol validation (19.1, 19.3)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_protocol_rejected(client: AsyncClient):
|
||||
"""Unknown protocol values are rejected during creation."""
|
||||
payload = _make_endpoint_payload(protocol="unknown_provider")
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
assert resp.status_code == 422
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_url_rejected(client: AsyncClient):
|
||||
"""URLs not starting with http:// or https:// are rejected."""
|
||||
payload = _make_endpoint_payload(base_url="ftp://bad-url.com")
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
assert resp.status_code == 422
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_url_rejected(client: AsyncClient):
|
||||
"""Empty base_url is rejected."""
|
||||
payload = _make_endpoint_payload(base_url="")
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
assert resp.status_code == 422
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: Deployments and bindings (19.3, 19.4)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_and_list_deployments(client: AsyncClient):
|
||||
"""Create a deployment and list it."""
|
||||
# First create an endpoint
|
||||
ep_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(),
|
||||
)
|
||||
ep_id = ep_resp.json()["id"]
|
||||
|
||||
dep_payload = {
|
||||
"endpoint_id": ep_id,
|
||||
"served_model_name": "stonks-adjudicator-9b",
|
||||
"display_name": "Qwen 9B Adjudicator",
|
||||
"capabilities": {"json_schema": True, "usage": True},
|
||||
"context_window": 8192,
|
||||
"max_output_tokens": 4096,
|
||||
}
|
||||
resp = await client.post("/api/inference/deployments", json=dep_payload)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
assert data["served_model_name"] == "stonks-adjudicator-9b"
|
||||
assert data["context_window"] == 8192
|
||||
|
||||
# List
|
||||
resp = await client.get("/api/inference/deployments")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.json()) == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_deployment_invalid_endpoint(client: AsyncClient):
|
||||
"""Creating a deployment with non-existent endpoint returns 404."""
|
||||
dep_payload = {
|
||||
"endpoint_id": str(uuid.uuid4()),
|
||||
"served_model_name": "model",
|
||||
"display_name": "Model",
|
||||
}
|
||||
resp = await client.post("/api/inference/deployments", json=dep_payload)
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_deployment_detail(client: AsyncClient):
|
||||
"""Get a deployment by ID with capabilities and limits."""
|
||||
ep_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(),
|
||||
)
|
||||
ep_id = ep_resp.json()["id"]
|
||||
|
||||
dep_payload = {
|
||||
"endpoint_id": ep_id,
|
||||
"served_model_name": "test-model",
|
||||
"display_name": "Test Model",
|
||||
"capabilities": {"json_schema": True, "seed": True},
|
||||
"context_window": 16384,
|
||||
"max_output_tokens": 8192,
|
||||
"quantization": "NVFP4",
|
||||
}
|
||||
create_resp = await client.post("/api/inference/deployments", json=dep_payload)
|
||||
dep_id = create_resp.json()["id"]
|
||||
|
||||
resp = await client.get(f"/api/inference/deployments/{dep_id}")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["capabilities"] == {"json_schema": True, "seed": True}
|
||||
assert data["context_window"] == 16384
|
||||
assert data["max_output_tokens"] == 8192
|
||||
assert data["quantization"] == "NVFP4"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_and_list_bindings(client: AsyncClient):
|
||||
"""Create a binding and list it."""
|
||||
ep_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(),
|
||||
)
|
||||
ep_id = ep_resp.json()["id"]
|
||||
|
||||
dep_payload = {
|
||||
"endpoint_id": ep_id,
|
||||
"served_model_name": "test-model",
|
||||
"display_name": "Test Model",
|
||||
}
|
||||
dep_resp = await client.post("/api/inference/deployments", json=dep_payload)
|
||||
dep_id = dep_resp.json()["id"]
|
||||
|
||||
agent_id = str(uuid.uuid4())
|
||||
binding_payload = {
|
||||
"agent_id": agent_id,
|
||||
"stage": "extraction",
|
||||
"model_deployment_id": dep_id,
|
||||
"route_order": 0,
|
||||
}
|
||||
resp = await client.post("/api/inference/bindings", json=binding_payload)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
assert data["stage"] == "extraction"
|
||||
assert data["agent_id"] == agent_id
|
||||
|
||||
# List
|
||||
resp = await client.get("/api/inference/bindings")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.json()) == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: Secrets NEVER leak in any response (comprehensive)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_secret_never_in_any_response(client: AsyncClient):
|
||||
"""Verify auth_secret_ref NEVER appears in any endpoint response."""
|
||||
secret_ref = "super-secret-api-key-ref-12345"
|
||||
payload = _make_endpoint_payload(auth_secret_ref=secret_ref)
|
||||
|
||||
# Create
|
||||
resp = await client.post("/api/inference/endpoints", json=payload)
|
||||
assert secret_ref not in resp.text
|
||||
assert "auth_secret_ref" not in resp.text
|
||||
ep_id = resp.json()["id"]
|
||||
|
||||
# List
|
||||
resp = await client.get("/api/inference/endpoints")
|
||||
assert secret_ref not in resp.text
|
||||
assert "auth_secret_ref" not in resp.text
|
||||
|
||||
# Get detail
|
||||
resp = await client.get(f"/api/inference/endpoints/{ep_id}")
|
||||
assert secret_ref not in resp.text
|
||||
assert "auth_secret_ref" not in resp.text
|
||||
|
||||
# Update
|
||||
resp = await client.put(
|
||||
f"/api/inference/endpoints/{ep_id}",
|
||||
json={"name": "renamed"},
|
||||
)
|
||||
assert secret_ref not in resp.text
|
||||
assert "auth_secret_ref" not in resp.text
|
||||
|
||||
# Disable
|
||||
resp = await client.post(f"/api/inference/endpoints/{ep_id}/disable")
|
||||
assert secret_ref not in resp.text
|
||||
assert "auth_secret_ref" not in resp.text
|
||||
|
||||
# Enable
|
||||
resp = await client.post(f"/api/inference/endpoints/{ep_id}/enable")
|
||||
assert secret_ref not in resp.text
|
||||
assert "auth_secret_ref" not in resp.text
|
||||
|
||||
# Delete
|
||||
resp = await client.delete(f"/api/inference/endpoints/{ep_id}")
|
||||
assert secret_ref not in resp.text
|
||||
assert "auth_secret_ref" not in resp.text
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: Protocol selectors (19.3)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_protocols(client: AsyncClient):
|
||||
"""Protocol selector returns valid options."""
|
||||
resp = await client.get("/api/inference/protocols")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
values = [p["value"] for p in data["protocols"]]
|
||||
assert "ollama_native" in values
|
||||
assert "openai_chat" in values
|
||||
assert "specialist_http" in values
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: Endpoint detail with bindings and capabilities (19.4)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_endpoint_detail_includes_bindings(client: AsyncClient):
|
||||
"""GET endpoint detail includes active stage bindings."""
|
||||
# Create endpoint + deployment + binding
|
||||
ep_resp = await client.post(
|
||||
"/api/inference/endpoints",
|
||||
json=_make_endpoint_payload(),
|
||||
)
|
||||
ep_id = ep_resp.json()["id"]
|
||||
|
||||
dep_payload = {
|
||||
"endpoint_id": ep_id,
|
||||
"served_model_name": "model-a",
|
||||
"display_name": "Model A",
|
||||
"capabilities": {"json_schema": True},
|
||||
}
|
||||
dep_resp = await client.post("/api/inference/deployments", json=dep_payload)
|
||||
dep_id = dep_resp.json()["id"]
|
||||
|
||||
binding_payload = {
|
||||
"agent_id": str(uuid.uuid4()),
|
||||
"stage": "adjudication",
|
||||
"model_deployment_id": dep_id,
|
||||
}
|
||||
await client.post("/api/inference/bindings", json=binding_payload)
|
||||
|
||||
# Get endpoint detail
|
||||
resp = await client.get(f"/api/inference/endpoints/{ep_id}")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["capabilities"] == {"json_schema": True}
|
||||
assert data["active_bindings"] is not None
|
||||
assert len(data["active_bindings"]) == 1
|
||||
assert data["active_bindings"][0]["stage"] == "adjudication"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test: is_external_endpoint helper
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_is_external_detection():
|
||||
"""Verify external endpoint detection logic."""
|
||||
from services.inference_registry.security import is_external_endpoint
|
||||
|
||||
# Local/cluster endpoints
|
||||
assert not is_external_endpoint("http://localhost:8000")
|
||||
assert not is_external_endpoint("http://127.0.0.1:11434")
|
||||
assert not is_external_endpoint("http://ollama.ollama-service.svc.cluster.local:11434")
|
||||
assert not is_external_endpoint("http://10.1.1.12:2701")
|
||||
assert not is_external_endpoint("http://192.168.1.100:8080")
|
||||
assert not is_external_endpoint("http://172.16.0.1:9000")
|
||||
|
||||
# External endpoints
|
||||
assert is_external_endpoint("https://api.openai.com")
|
||||
assert is_external_endpoint("https://generativelanguage.googleapis.com")
|
||||
assert is_external_endpoint("https://api.anthropic.com")
|
||||
assert is_external_endpoint("https://some-cloud-provider.example.com")
|
||||
Reference in New Issue
Block a user