Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ RUN useradd --create-home --uid 10001 appuser
COPY --from=builder /wheels /wheels
RUN python -m pip install --no-cache-dir /wheels/* && rm -rf /wheels

USER appuser
WORKDIR /app
COPY config ./config
RUN chown -R appuser:appuser /app

USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=2)"
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,29 @@ Successful PR CI runs are merged automatically only for trusted same-repository
and Dependabot. Forks, drafts, and untrusted author associations are deliberately skipped;
repository branch-protection and review requirements continue to apply.

## Model registry

[`config/registry.yaml`](config/registry.yaml) is the governed source of truth for what may
be served. A request can never introduce a model path, revision, or adapter.

- Model cards record license, tokenizer, revision, context limit, quantization, hardware
requirement, intended tasks, limitations, and evaluation evidence. Promotion to
`production` is rejected without evaluation references.
- Adapters bind to one immutable base revision, declare their dataset version and measured
quality delta, and cannot be promoted with unresolved regressions.
- Deployment revisions record container digest, model and adapter checksums, Ray and vLLM
configuration, GPU pool, and the previous revision used for rollback.

| Endpoint | Purpose |
|---|---|
| `GET /v1/models` | OpenAI-compatible catalog enriched with tier, stage, license, and quantization. |
| `GET /v1/registry/models/{id}` | Full model card with its benchmark evidence. |
| `GET /v1/registry/adapters` | Promoted LoRA and QLoRA adapters. |
| `GET /v1/registry/deployments` | Deployment revisions and rollback targets. |

Send `routing.domain` to request a domain adapter; the router applies the promoted adapter
with the largest measured quality gain for that base revision and task, or none at all.

## Observability

`GET /metrics` returns Prometheus exposition text and is intentionally unauthenticated so
Expand Down Expand Up @@ -104,6 +127,7 @@ All settings use the `ROUTER_` prefix.
| `ROUTER_ADMISSION_TIMEOUT_SECONDS` | `0.25` | Time allowed to wait for capacity. |
| `ROUTER_QUOTA_REQUESTS_PER_MINUTE` | `120` | Per-token sliding-window quota. |
| `ROUTER_EXTERNAL_FALLBACK_ENABLED` | `false` | Operator gate for external fallback. |
| `ROUTER_REGISTRY_PATH` | `config/registry.yaml` | Governed model catalog; built-in profiles are used if absent. |
| `ROUTER_ROUTING_POLICY_VERSION` | `v1` | Invalidates router and response caches when changed. |
| `ROUTER_CACHE_ENABLED` | `true` | Master switch for all cache tiers. |
| `ROUTER_CACHE_TTL_SECONDS` | `300` | Exact-response entry lifetime. |
Expand Down
181 changes: 181 additions & 0 deletions config/registry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Governed model catalog. Requests may never introduce a model path or revision;
# only records in this document are servable.
policy:
version: v1
restricted_privacy_is_local_only: true
quality_floor: 0.0
resource_ceiling: 2.0
fallback_order:
- small-specialist
- general-local
- high-capability
- approved-external-fallback

models:
- id: small-specialist
revision: mock-small@sha256:dev
tier: small-specialist
license: apache-2.0
tokenizer: mock-small-tokenizer
context_limit: 8192
quantization: awq
hardware:
accelerator: nvidia-l4
count: 1
minimum_memory_gb: 24
supported_tasks: [extraction, classification]
quality: 0.82
estimated_queue_ms: 12
cost_weight: 0.1
stage: production
intended_tasks: Field extraction and single-label classification.
limitations: Not evaluated for open-ended reasoning or multi-turn dialogue.
evaluation_references:
- benchmark:extraction-v3
- benchmark:classification-v2

- id: general-local
revision: mock-general@sha256:dev
tier: general-local
license: apache-2.0
tokenizer: mock-general-tokenizer
context_limit: 32768
quantization: none
hardware:
accelerator: nvidia-a10g
count: 1
minimum_memory_gb: 48
supported_tasks: [extraction, classification, rag, summarization, general]
quality: 0.89
estimated_queue_ms: 35
cost_weight: 0.35
stage: production
intended_tasks: Summarization, retrieval-augmented answers, and moderate reasoning.
limitations: Weaker than the high-capability tier on multi-step reasoning.
evaluation_references:
- benchmark:rag-v4

- id: high-capability
revision: mock-high@sha256:dev
tier: high-capability
license: apache-2.0
tokenizer: mock-high-tokenizer
context_limit: 65536
quantization: none
hardware:
accelerator: nvidia-a100
count: 2
minimum_memory_gb: 160
tensor_parallel_size: 2
supported_tasks: [extraction, classification, rag, summarization, reasoning, critique, general]
quality: 0.96
estimated_queue_ms: 90
cost_weight: 0.9
stage: production
intended_tasks: Complex reasoning, critique, and difficult fallback.
limitations: Highest GPU cost per request; reserve for genuinely hard work.
evaluation_references:
- benchmark:reasoning-v5

- id: approved-external-fallback
revision: external-policy-v1
tier: external-fallback
local: false
license: provider-terms
tokenizer: provider-managed
context_limit: 128000
supported_tasks: [extraction, classification, rag, summarization, reasoning, critique, general]
quality: 0.98
estimated_queue_ms: 45
cost_weight: 1.5
hardware:
accelerator: provider-managed
minimum_memory_gb: 1
stage: production
intended_tasks: Unsupported capability or temporary local saturation only.
limitations: Never eligible for private or restricted data.
evaluation_references:
- benchmark:external-parity-v1

adapters:
- id: claims-extraction-lora
base_model_id: small-specialist
base_revision: mock-small@sha256:dev
adapter_revision: claims-lora@sha256:dev
domain: claims
intended_tasks: [extraction]
dataset_version: claims-2026-05
quantized: true
stage: production
benchmark:
quality_delta: 0.06
regressions: []

- id: support-classification-lora
base_model_id: small-specialist
base_revision: mock-small@sha256:dev
adapter_revision: support-lora@sha256:dev
domain: support
intended_tasks: [classification]
dataset_version: support-2026-04
stage: production
benchmark:
quality_delta: 0.04
regressions: []

- id: claims-extraction-lora-next
base_model_id: small-specialist
base_revision: mock-small@sha256:dev
adapter_revision: claims-lora@sha256:next
domain: claims
intended_tasks: [extraction]
dataset_version: claims-2026-06
stage: staging
benchmark:
quality_delta: 0.02
regressions: []

benchmarks:
- id: extraction-v3
dataset_version: extraction-2026-05
workload_version: steady-32
hardware: nvidia-l4
driver: "570.86"
container_digest: sha256:mock-container
engine_revision: mock-engine-0.1.0
model_revision: mock-small@sha256:dev
concurrency: 32
prompt_tokens_p50: 420
prompt_tokens_p95: 1100
engine_settings:
max_num_seqs: 64
enable_prefix_caching: true
quality_score: 0.82
latency_p95_ms: 480
throughput_rps: 41.5
gpu_seconds_per_request: 0.11

deployments:
- id: deploy-0002
container_digest: sha256:mock-container
gpu_pool: l4-pool
model_checksums:
small-specialist: sha256:mock-small
general-local: sha256:mock-general
adapter_checksums:
claims-extraction-lora: sha256:mock-claims
ray_config:
num_replicas: 2
max_ongoing_requests: 16
vllm_config:
enable_prefix_caching: true
max_model_len: 8192
stage: production
previous_revision_id: deploy-0001

- id: deploy-0001
container_digest: sha256:mock-container-previous
gpu_pool: l4-pool
model_checksums:
small-specialist: sha256:mock-small-previous
stage: deprecated
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ requires-python = ">=3.11"
dependencies = [
"fastapi>=0.141.1,<1",
"prometheus-client>=0.26.0,<1",
"pyyaml>=6.0.2,<7",
"pydantic-settings>=2.15.0,<3",
"uvicorn[standard]>=0.52.4,<1",
]
Expand All @@ -23,6 +24,7 @@ dev = [
"pytest-asyncio>=1.4.0,<2",
"pytest-cov>=7.1.0,<8",
"ruff>=0.16.4,<1",
"types-PyYAML>=6.0.12,<7",
]

[tool.hatch.build.targets.wheel]
Expand Down
Loading
Loading