IBEX Harness
DocsBenchmarksBlogChangelogRoadmap
GitHub
IBEX Harness

Documentation

Architecture Decision RecordsADR-0002: Repository foundation bootstrapADR-0003: Branch protection and merge policyADR-0004: Protobuf and code generation policyADR-0005: Postgres migration strategyADR-0006: Auth protobuf contract (`ibex.auth.v1`)ADR-0007: Auth token validation implementationADR-0008: Security scanning and CI quality gatesADR-0009: Permission bitmap layoutADR-0010: Cryptography policyADR-0011: Proxy auth gRPC client and middlewareADR-0012: Proxy request normalization (OpenAI chat)ADR-0013: Proxy input validation and stable error envelopeADR-0014: Core domain migration sequencingADR-0015: Proxy rate limit skeleton (Phase 1)ADR-0016: Proxy agent identity verification (Phase 1)ADR-0017: Request ID and trace context strategy (Phase 1)ADR-0018: Graceful shutdown contract (Phase 1)ADR-0019: OpenTelemetry provider configuration (Phase 1)ADR-0020: Shared package boundaries — `packages/config` and `packages/apierror`ADR-0021: Prometheus Metric Catalog (Phase 1)ADR-0022: Health check contract (Phase 1)ADR-0023: Docs site architecture (Phase 1.5)ADR-0024: Benchmark data publishing modelADR-0025: LLM provider abstractionADR-0026: OpenAI client designADR-0027: Streaming dual-write strategyADR-0028: Auth cache designADR-0029: Token revocation propagation via Redis pub/subADR-0030: Directive versioning strategyADR-0031: System prompt injection strategyADR-0032: Session data model and retentionADR-0033: ClickHouse llm_traces schema and retentionADR-0034: Proxy overhead performance measurement methodologyADR-0035: Chat Idempotency-Key Redis dedupeADR-0038: Context assembly service design and gRPC contractADR-0039: Proxy Postgres ownership for session and directive storesADR-0040: Anthropic provider adapterADR-0041: Model capability registryADR-0042: Self-hosted OpenAI-compatible LLM adapterADR-0043: Tokenizer registry architectureADR-0044: Non-streaming response pipelineADR-0045: Streaming response transformationADR-0046: Embedder interface and profile registryADR-0047: Memory temporal validity foundationADR-0048: Memory multi-label categoriesADR-0049: Memory relationship graph readinessADR-0050: MCP server skeleton (transport, auth, audit)ADR-0051: Local LGTM observability stack (Phase 2.5 exit pull-forward)ADR-0052: Memory schema v2 expand (HNSW, quality columns)ADR-0053: Vector store abstraction and composite scoring v2
ADRs›ADR-0041: Model capability registry
ADRs

ADR-0041: Model capability registry

Architecture decision record 0041 — curated ModelCapability catalog beside Registry.For, fail-closed registration, and ExtraModels overlays.

ADR-0041: Model capability registry

  • Status: Accepted
  • Date: 2026-08-21
  • Authors: IBEX Harness team
  • Milestone: 2.5.G1.M2 Model capability registry

Context

ADR-0025 routes by model via Registry.For(model) — that answers which provider handles a request. After Anthropic (ADR-0040) and before self-hosted vLLM (2.5.G1.M3) and the tokenizer registry (2.5.G2.M1), the proxy and future context-assembly engine also need what a model can do: context window, max output tokens, tools/vision/streaming support, and tokenizer family.

A hardcoded allowlist of model IDs is not enough. Capability metadata must be queryable, fail-closed at startup, and reviewable in PRs.

Decision

1) Curated in-repo capability catalog

Maintain a small, hand-verified Go table in packages/provider (BuiltInCapabilityCatalog). Values are checked against vendor docs (OpenAI model cards; Anthropic models overview). Do not take a runtime dependency on LiteLLM or fetch capability JSON on the hot path.

An optional offline maintainer script may diff curated rows against a LiteLLM snapshot for drift detection; it is never imported by the proxy binary and is not a CI gate.

2) ModelCapability beside Registry.For

Go
type ModelCapability struct {
    ModelID           string
    Provider          string // vendor family: "openai" | "anthropic" (catalog truth)
    ContextWindow     int
    MaxOutputTokens   int
    SupportsTools     bool
    SupportsVision    bool
    SupportsStreaming bool
    TokenizerFamily   string // "o200k_base" | "cl100k_base" | "claude" | …
}
 
func (r *Registry) Capability(model string) (ModelCapability, bool)

Provider on a capability row is the vendor family for the model ID, not necessarily the runtime adapter name. The mock LLM reuses OpenAI model IDs and therefore reuses OpenAI capability rows.

3) Fail-closed NewRegistry

Go
func NewRegistry(catalog CapabilityCatalog, providers ...Provider) (*Registry, error)

Every model ID returned by SupportedModels() must resolve in catalog. Missing entries return ErrMissingCapability at startup — never a silent miss at request time. Duplicate model IDs still return ErrDuplicateModel. Invalid catalog rows (failed field validation, or a row whose ModelID does not match the catalog key) return ErrInvalidCapability.

4) ExtraModels require explicit overlays

IBEX_LLM_EXTRA_MODELS / ANTHROPIC_EXTRA_MODELS remain allowlist extensions. Operators must also supply capability overlays (IBEX_MODEL_CAPABILITY_OVERLAYS JSON) for every extra model ID. Overlay rules (fail-closed):

  • unknown JSON fields are rejected
  • feature flags (supports_tools / supports_vision / supports_streaming) must be present explicitly
  • tokenizer family must be one of the declared constants (o200k_base, cl100k_base, claude, unknown)
  • overlays cannot override built-in curated model IDs
  • overlay model_id values must appear in ExtraModels (no orphan overlays)

Overlays merge on top of the built-in catalog only after those checks pass. Extra models without overlay entries fail registry construction.

5) Capability reflects vendor model truth, not adapter completeness

SupportsTools / SupportsVision describe what the vendor model supports. Anthropic tool/image passthrough in the adapter may still be deferred; request-time enforcement of these flags is out of scope for this milestone. Document the gap so tokenizer/budget consumers are not confused with proxy feature completeness.

6) Tokenizer family keys for G2

Stable string keys (o200k_base, cl100k_base, claude, later llama3 / qwen2) are the join key for 2.5.G2.M1. Anthropic uses claude — not a fake tiktoken mapping.

Consequences

Positive:

  • Proxy and Phase 3 context assembly can size budgets and validate features without scraping vendor docs at runtime
  • Startup fails loudly when ExtraModels lack metadata
  • G2/M3 extend the same table rather than inventing parallel maps

Negative / follow-ups:

  • Curated table needs periodic refresh when vendors ship new model IDs
  • Hot-path rejection based on SupportsTools / SupportsVision is deferred until adapters expose those features
  • Self-hosted models (M3) must register capability rows (and overlays) explicitly

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0040: Anthropic provider adapterNextADR-0042: Self-hosted OpenAI-compatible LLM adapter

On this page

  • Context
  • Decision
  • 1) Curated in-repo capability catalog
  • 2) ModelCapability beside Registry.For
  • 3) Fail-closed NewRegistry
  • 4) ExtraModels require explicit overlays
  • 5) Capability reflects vendor model truth, not adapter completeness
  • 6) Tokenizer family keys for G2
  • Consequences
0%