Skip to content

feat(models): manifest-driven model capabilities and curated labels - #3603

Open
wpfleger96 wants to merge 1 commit into
mainfrom
duncan/databricks-model-label-registry
Open

feat(models): manifest-driven model capabilities and curated labels#3603
wpfleger96 wants to merge 1 commit into
mainfrom
duncan/databricks-model-label-registry

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

This PR establishes a single hand-curated model-capability manifest (scripts/model-capabilities.json) as the source of truth for model metadata — display labels, supported reasoning-effort sets, default efforts, thinking modes, and Databricks v2 wire routes. A generator emits equivalent Rust and TypeScript interpreters from it, production resolution in both languages runs on the generated interpreters, and the superseded hand-maintained tables are deleted. It also ships the curated Databricks label registry and the frontend resolveModelLabel() contract.

Closes #3586. Landed as three sub-PRs into this branch: #3821 (manifest + generator), #3958 (production integration), #4589 (hand-table retirement).

Where the capability data comes from

scripts/model-capabilities.json is hand-curated in this repo — nothing is fetched at build time. Every fact in it is reconciled against a cited external authority, and the citations live in the manifest itself (the top-level _sources block plus a per-record _reconciliation_doc field with retrieval date):

Source What it authorizes
models.dev API (payload retrieved 2026-07-31, SHA-256 pinned in the manifest) Databricks endpoint reasoning-effort sets and verbatim display labels
Anthropic extended-thinking and effort docs (July 2025) Anthropic thinking modes and effort semantics
OpenAI reasoning guide (July 2025) OpenAI reasoning-effort semantics
goose DATABRICKS_V2_KNOWN_MODELS (revision 6789d4af) The Databricks v2 known-model ID list (opt-in drift check: --check-goose)

When a provider ships a new model or effort level, the workflow is: edit the manifest record with a fresh citation, regenerate, commit. The CI gates make artifact drift impossible; keeping the manifest current with provider reality is deliberately a curation step — auditable, cited, and reviewed like any other change.

Capability manifest and generated interpreters

scripts/generate-model-capabilities.mjs reads the manifest and emits two committed artifacts from one source of truth:

Artifact Consumer
crates/buzz-agent/src/generated_model_capabilities.rs Rust interpreter
desktop/src/features/agents/ui/modelCapabilities.ts TS interpreter

The generator validates every interpolated manifest string through a shared requireSafeString() boundary (rejects quotes, backslashes, control chars), enforces the manifest schema (42 negative tests in scripts/test-manifest-validator.mjs, covering effort canonicality, post-inheritance record consistency, integer match priorities, and normalized duplicate keys), and supports --check for byte-clean verification.

Production integration

Rust (buzz-agent): reasoning-effort normalization for OpenAI and legacy Databricks flows through normalize_effort_for_provider(), backed by the generated interpreter; anthropic_body() is provider-aware and Adaptive thinking clamps against the generated supported_efforts; Databricks v2 request routing reads the manifest's databricks_v2_wire_route instead of a hand-maintained segment classifier. The superseded effort/thinking hand tables and model-family classifiers in config.rs and llm.rs are deleted — the generated interpreter is the only authority.

Desktop: getProviderEffortConfig() resolves through the generated modelCapabilities.ts; canonicalizeProvider() (trim/lowercase plus the databricks-v2 and openai-compat aliases) is applied before every provider-scoped lookup, and the provider is threaded through all label/effort surfaces so a supplied provider can never fall through to unscoped registry lookups. The old hand-table effort path and its fixture are deleted.

Drift protection

The model-capabilities job in .github/workflows/ci.yml gates every PR:

  • regenerates both artifacts and fails on any byte difference, so the committed interpreters cannot drift from the manifest;
  • runs the shared 81-vector normative corpus (scripts/normative-corpus.json) against both interpreters — scripts/run-corpus.mjs for TS, generated Rust tests for Rust — every vector pins all six capability axes (label, thinking mode, effort set, default effort, wire route, normalization policy), and both runners reject sparse vectors;
  • runs the schema-negative validator suite and cargo test -p buzz-agent --lib (the only CI gate exercising the buzz-agent unit suite; just test-unit excludes that crate).

Model display labels

The manifest's databricks_v2 exact records carry a registry_label axis with models.dev-verbatim display names for the ~30 managed Databricks endpoints. The generator derives a flat DATABRICKS_MODEL_NAMES registry from those records and emits it into both artifacts (Rust static slice, TS Map), so the two languages cannot drift.

Display rule: a model's display name comes from an exact/curated record only — family rules carry no labels, so a family-matched ID with no curated record resolves registryLabel: null and displays its raw ID (or its upstream discovery name). Endpoint IDs like databricks-gpt-5-5 resolve to human-readable labels (GPT-5.5); unknown or custom workspace endpoints pass through as their raw ID unchanged, so a custom endpoint like databricks-team-2025-01 can never be given an invented name.

Rust label resolution

databricks_model_name(id) in crates/buzz-agent/src/catalog.rs reads the generated registry and is applied at every ModelEntry construction path — v1 and v2 discovery parsing, the authenticated-empty-catalog slate, and the configured-model fallback — so AgentModelInfo.name reaches the frontend already curated. Mirrors the existing openai_model_display_name precedent; no new IPC. DATABRICKS_V2_KNOWN_MODELS is likewise a re-export of the generated constant.

Frontend label resolution

resolveModelLabel(id, discoveredName?, provider?) in desktop/src/features/agents/lib/formatAgentModelLabel.ts owns the label contract with three-tier precedence: nonblank discovered/API name, then registry lookup by ID (provider-qualified exact record when a provider is supplied; unscoped registry only on the providerless path), then raw ID. Every model-label surface routes through it — ModelPicker, usePersonaModelDiscovery, agentCardModelLabel, AgentConfigFields, ManagedAgentRow, UserProfilePopover. formatAgentModelLabel() wraps the resolver so a null or empty ID renders Auto.

Tests

  • Shared corpus 81/81 in both interpreters (full six-axis expectations per vector); schema-negative suite 42/42; cargo test -p buzz-agent --lib covers the corpus harness and generated-interpreter tests as a CI merge gate.
  • Compact seam tests guard the live production boundaries: startup validation of pure-Anthropic effort values through Config::validate(), Anthropic-route effort normalization (none/minimal omitted from the wire), and the unsupported-effort clamp through anthropic_body() (a no-xhigh model record clamps xhigh to high).
  • Corpus vectors pin the label contract: provider-qualified curated IDs resolve their exact-record labels (including the family-masquerade defect shape, databricks-gpt-5-miniGPT-5 Mini), and a family-matched ID with no curated record pins registry_label: null; provider-alias tests pin openai-compat/databricks-v2 canonicalization in the desktop surfaces.
  • usePersonaModelDiscovery.test.mjs pins discovery-row label precedence; Rust unit tests in catalog.rs cover known IDs, custom endpoints, and the known-models fallback slate.

Docs and build infra

desktop/src/features/agents/AGENTS.md documents label precedence and the regeneration workflow (edit scripts/model-capabilities.json, run node scripts/generate-model-capabilities.mjs); the generator and CI job emit the regeneration command on any stale-artifact failure. desktop/scripts/check-file-sizes.mjs gains a fileOverrides mechanism because the generated modelCapabilities.ts exceeds the 1000-line ceiling intended for hand-authored files.

@wpfleger96
wpfleger96 requested a review from a team as a code owner July 29, 2026 19:04
@wpfleger96 wpfleger96 changed the title feat(catalog): replace Databricks model-label tokenizer with models.dev registry lookup feat(catalog): resolve Databricks labels from models.dev Jul 30, 2026
Comment thread .github/workflows/model-capability-regen-diff.yml Fixed
@wpfleger96 wpfleger96 changed the title feat(catalog): resolve Databricks labels from models.dev feat(models): manifest-driven model capabilities with generated Rust/TS interpreters and curated Databricks labels Jul 31, 2026
@wpfleger96 wpfleger96 changed the title feat(models): manifest-driven model capabilities with generated Rust/TS interpreters and curated Databricks labels feat(models): manifest-driven model capabilities and curated labels Jul 31, 2026
Comment thread .github/workflows/model-capability-regen-diff.yml Fixed
@wpfleger96
wpfleger96 force-pushed the duncan/databricks-model-label-registry branch from cc00060 to 4d47f48 Compare August 3, 2026 20:04
wpfleger96 added a commit that referenced this pull request Aug 3, 2026
…t apparatus (#4589)

## Summary

Retires the old hand-table authorities and transitional verification
scaffolding from the model-capability manifest arc. All production
routes now run exclusively through the generated interpreters introduced
in Phase 1 ([#3821](#3821)) and wired
in Phase 2 ([#3958](#3958)).

Stack: [#3821](#3821) →
[#3958](#3958) → this PR
Base: [#3603](#3603)

## What the manifest system is now

**Source of truth:** `scripts/model-capabilities.json`
**Generator:** `scripts/generate-model-capabilities.mjs` — emits Rust
and TS interpreters only (coverage JSON output removed)
**Generated interpreters:**
`crates/buzz-agent/src/generated_model_capabilities.rs` (+ normative
tests), `desktop/src/features/agents/ui/modelCapabilities.ts`
**Label registry:** `generate-databricks-model-names.py` →
`databricks_model_names.rs` / `databricksModelNames.ts`
**Permanent gates:** `scripts/normative-corpus.json` +
`scripts/run-corpus.mjs` (both-interpreter equivalence, 51 vectors),
`scripts/test-manifest-validator.mjs` (schema, 24 cases), regen-diff job
inside `ci.yml`
**One doc:** `scripts/MODEL_CAPABILITIES.md`

## Deleted

**Old hand-table authorities (production):**
- `getProviderEffortConfig_oldHandTable()` and all supporting helpers
from `desktop/src/features/agents/ui/buzzAgentConfig.ts`
- `normalize_effort_for_openai_route()`,
`_old_anthropic_thinking_config_for_databricks_v2()`, test-only
re-export wrappers from `crates/buzz-agent/src/config.rs`
- `strip_catalog_prefix()`, `anthropic_thinking_config()`,
`anthropic_model_supports_xhigh()`, `clamp_adaptive_effort()`,
`anthropic_efforts_for_model()`, `is_manual_budget_model()`,
`is_adaptive_thinking_model()`, `gpt5_token_matches()`,
`gpt5_base_matches()`, `openai_efforts_for_model()` from
`crates/buzz-agent/src/config.rs` — all superseded by generated
interpreter
- Old DBv2 body-level tests, `_OLD_DATABRICKS_V2_*` constants,
`model_name_segments()`, `_old_databricks_v2_route_for_model()`, all
Phase-2 behavioral differential test functions from
`crates/buzz-agent/src/llm.rs`

**Transitional scaffolding:**
- `scripts/run-differential.mjs` — old-vs-new JS differential harness
- `scripts/run-mutation-evidence.mjs` — one-time mutation evidence
runner
- `desktop/src/features/agents/ui/effortTable.fixture.json` — Phase-2
TS/Rust sync fixture
- `desktop/src/features/agents/ui/effortTable.fixture.test.mjs` —
fixture sync guard
- `.github/workflows/model-capability-regen-diff.yml` — standalone
workflow (steps folded into `ci.yml`)

**One-time evidence and generated snapshots:**
- `scripts/MUTATION_EVIDENCE.md`,
`scripts/MODEL_CAPABILITIES_SCHEMA.md`,
`scripts/MODELS_DEV_RECONCILIATION.md` — consolidated into
`scripts/MODEL_CAPABILITIES.md`
- `scripts/generated-model-capabilities-coverage.json` — full-table
snapshot (generator no longer emits it)
- `scripts/catalog-sample-fixture.json` — models.dev snapshot used only
by the deleted differential harness

## Verification

- `cargo test -p buzz-agent --lib` with `RUSTFLAGS="-D warnings"`:
**337/337** (clean — no dead_code warnings)
- `node --experimental-strip-types scripts/run-corpus.mjs`: **51/51**
- `node scripts/generate-model-capabilities.mjs` + regen diff: **clean
(exit 0)**
- `node --test scripts/test-manifest-validator.mjs`: **24/24**
- `just clippy`: zero warnings, zero errors

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
@wpfleger96
wpfleger96 force-pushed the duncan/databricks-model-label-registry branch 2 times, most recently from 0189510 to 15aa135 Compare August 4, 2026 21:16
wpfleger96 added a commit that referenced this pull request Aug 4, 2026
…t apparatus (#4589)

## Summary

Retires the old hand-table authorities and transitional verification
scaffolding from the model-capability manifest arc. All production
routes now run exclusively through the generated interpreters introduced
in Phase 1 ([#3821](#3821)) and wired
in Phase 2 ([#3958](#3958)).

Stack: [#3821](#3821) →
[#3958](#3958) → this PR
Base: [#3603](#3603)

## What the manifest system is now

**Source of truth:** `scripts/model-capabilities.json`
**Generator:** `scripts/generate-model-capabilities.mjs` — emits Rust
and TS interpreters only (coverage JSON output removed)
**Generated interpreters:**
`crates/buzz-agent/src/generated_model_capabilities.rs` (+ normative
tests), `desktop/src/features/agents/ui/modelCapabilities.ts`
**Label registry:** `generate-databricks-model-names.py` →
`databricks_model_names.rs` / `databricksModelNames.ts`
**Permanent gates:** `scripts/normative-corpus.json` +
`scripts/run-corpus.mjs` (both-interpreter equivalence, 51 vectors),
`scripts/test-manifest-validator.mjs` (schema, 24 cases), regen-diff job
inside `ci.yml`
**One doc:** `scripts/MODEL_CAPABILITIES.md`

## Deleted

**Old hand-table authorities (production):**
- `getProviderEffortConfig_oldHandTable()` and all supporting helpers
from `desktop/src/features/agents/ui/buzzAgentConfig.ts`
- `normalize_effort_for_openai_route()`,
`_old_anthropic_thinking_config_for_databricks_v2()`, test-only
re-export wrappers from `crates/buzz-agent/src/config.rs`
- `strip_catalog_prefix()`, `anthropic_thinking_config()`,
`anthropic_model_supports_xhigh()`, `clamp_adaptive_effort()`,
`anthropic_efforts_for_model()`, `is_manual_budget_model()`,
`is_adaptive_thinking_model()`, `gpt5_token_matches()`,
`gpt5_base_matches()`, `openai_efforts_for_model()` from
`crates/buzz-agent/src/config.rs` — all superseded by generated
interpreter
- Old DBv2 body-level tests, `_OLD_DATABRICKS_V2_*` constants,
`model_name_segments()`, `_old_databricks_v2_route_for_model()`, all
Phase-2 behavioral differential test functions from
`crates/buzz-agent/src/llm.rs`

**Transitional scaffolding:**
- `scripts/run-differential.mjs` — old-vs-new JS differential harness
- `scripts/run-mutation-evidence.mjs` — one-time mutation evidence
runner
- `desktop/src/features/agents/ui/effortTable.fixture.json` — Phase-2
TS/Rust sync fixture
- `desktop/src/features/agents/ui/effortTable.fixture.test.mjs` —
fixture sync guard
- `.github/workflows/model-capability-regen-diff.yml` — standalone
workflow (steps folded into `ci.yml`)

**One-time evidence and generated snapshots:**
- `scripts/MUTATION_EVIDENCE.md`,
`scripts/MODEL_CAPABILITIES_SCHEMA.md`,
`scripts/MODELS_DEV_RECONCILIATION.md` — consolidated into
`scripts/MODEL_CAPABILITIES.md`
- `scripts/generated-model-capabilities-coverage.json` — full-table
snapshot (generator no longer emits it)
- `scripts/catalog-sample-fixture.json` — models.dev snapshot used only
by the deleted differential harness

## Verification

- `cargo test -p buzz-agent --lib` with `RUSTFLAGS="-D warnings"`:
**337/337** (clean — no dead_code warnings)
- `node --experimental-strip-types scripts/run-corpus.mjs`: **51/51**
- `node scripts/generate-model-capabilities.mjs` + regen diff: **clean
(exit 0)**
- `node --test scripts/test-manifest-validator.mjs`: **24/24**
- `just clippy`: zero warnings, zero errors

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
kalvinnchau
kalvinnchau previously approved these changes Aug 5, 2026

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the doubled-separator parity fix at exact head 62ebc94. The generated Rust matcher now collapses empty segments consistently with the TypeScript matcher, and the new six-axis corpus vector pins the previously divergent openai-responses route. Generator check and both corpus runners pass. No findings.

…r efforts, routes, labels

Rebased onto origin/main (6ca9641).

Conflict resolved in crates/buzz-agent/src/llm.rs: combined origin/main's
.and_then() refactor (error stamp coverage for Anthropic/OpenRouter) with our
branch's anthropic_body provider argument.

  62ebc94 fix(model-capabilities): collapse empty segments in Rust gpt-version-segment matcher
  817b887 test(file-size): fix fileOverrides integration test under GITHUB_ACTIONS
  1e9df55 fix(manifest): enforce registry_label exclusion on family rules; test fileOverrides boundary
  0e6aacf fix(buzz-agent): curate display name in configured_model_fallback
  77c24ef chore(manifest): remove dead registry_labels key

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/databricks-model-label-registry branch from 62ebc94 to 1b57e84 Compare August 6, 2026 15:31
Comment thread .github/workflows/ci.yml
- name: Regenerate artifacts
run: node scripts/generate-model-capabilities.mjs

- name: Diff check — fail if generated files are stale
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants