Skip to content

refactor(adapters): decouple KNOWN_CAPABILITIES from catalog entry names (#1186)#1266

Merged
planetf1 merged 6 commits into
generative-computing:mainfrom
planetf1:worktree-issue-1186
Jun 16, 2026
Merged

refactor(adapters): decouple KNOWN_CAPABILITIES from catalog entry names (#1186)#1266
planetf1 merged 6 commits into
generative-computing:mainfrom
planetf1:worktree-issue-1186

Conversation

@planetf1

@planetf1 planetf1 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #1186. Part of Epic #929 Phase 0 (adapter lifecycle refactor).

Background

Epic #929 is a multi-phase refactor of how Mellea loads and identifies adapters (LoRA/aLoRA fine-tuned modules). Phase 0 (#1134, #1135, this PR) lays the scaffolding; later phases wire it into the live loading paths.

This PR fixes a coupling introduced in #1134: KNOWN_CAPABILITIES was built directly from catalog entry name fields, so the capability vocabulary was tied to the names HuggingFace uses for their adapters. When an upstream adapter gets renamed, Mellea capability strings break too. The requirement-check / requirement_check mismatch (hyphens vs underscores) had already forced several edits.

Change

Before — capability vocabulary = upstream adapter names:

# capabilities.py
KNOWN_CAPABILITIES = frozenset(e.name for e in _INTRINSICS_CATALOG_ENTRIES)
# → {"requirement-check", "context-attribution", "policy-guardrails", ...}

# catalog.py
IntriniscsCatalogEntry(name="requirement-check", repo_id=..., revision=...)

After — capability vocabulary is declared independently:

# capabilities.py
KNOWN_CAPABILITIES = frozenset(e.effective_capability for e in _INTRINSICS_CATALOG_ENTRIES)
# → {"requirement_check", "context_attribution", "policy_guardrails", ...}

# catalog.py
IntriniscsCatalogEntry(
    name="requirement-check",       # upstream HF adapter name — may change
    capability="requirement_check", # stable Mellea token — must not change without deprecation
    repo_id=...,
    revision=...,
)

effective_capability returns capability when set, falling back to name — so entries whose name is already a clean identifier (answerability, citations, etc.) need no explicit capability field.

The six hyphenated catalog entries (Core + Guardian groups) get explicit underscore capabilities. The seven underscore-named RAG entries are unchanged.

The issue also called out updating call sites that hardcode adapter names as capabilities. There are none: Identity is Phase 0 scaffolding not yet wired into production adapter loading — nothing in production code constructs Identity objects today. That migration is Phase 1/2 work.

Validation hardening

As part of ensuring effective_capability genuinely guarantees a non-empty, clean token, this PR extends validation consistently across all three string token fields on IntriniscsCatalogEntry:

  • name, capability, and revision all now reject empty strings, whitespace-only strings, and strings with leading or trailing whitespace
  • Validators raise; they do not silently strip — consistent with the existing codebase convention throughout mellea/
  • validate_revision (pre-existing, exported) updated to match

Relationship to #1256

Independent of #1256 (terminology sweep) — can merge in either order. Both touch catalog.py and capabilities.py but in different lines. Whoever merges second needs a one-line docstring rebase.

Test plan

uv run pytest test/backends/test_adapters/test_catalog.py \
               test/backends/test_adapters/test_catalog_revision.py \
               test/backends/test_adapters/test_core_types.py -v

60 tests pass. Key assertions:

  • "requirement_check" in KNOWN_CAPABILITIES, "requirement-check" not in KNOWN_CAPABILITIES
  • All six hyphenated entries carry explicit underscore capabilities
  • No hyphenated string can leak into KNOWN_CAPABILITIES (regression guard)
  • len(KNOWN_CAPABILITIES) == len(_INTRINSICS_CATALOG_ENTRIES) (no duplicates)
  • name, capability, revision all reject empty, whitespace-only, and leading/trailing whitespace

@github-actions github-actions Bot added the enhancement New feature or request label Jun 15, 2026
planetf1 added 2 commits June 15, 2026 14:31
…mes (generative-computing#1186)

Add an optional `capability` field to `IntriniscsCatalogEntry` and an
`effective_capability` property (returns `capability` or falls back to
`name`).  Derive `KNOWN_CAPABILITIES` from `effective_capability` instead
of `name`, so the capability vocabulary survives upstream adapter renames.

Set explicit underscore capabilities on the six hyphenated catalog entries
(context-attribution, requirement-check, policy-guardrails, guardian-core,
factuality-detection, factuality-correction).  All other entries default to
their name unchanged.

Closes generative-computing#1186.  Part of Epic generative-computing#929 Phase 0.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Add three extra assertions to catch future regressions:
- no hyphenated names can leak into KNOWN_CAPABILITIES
- count of capabilities must equal count of catalog entries (no duplicates)
- every catalog entry's effective_capability is non-empty

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
@planetf1
planetf1 force-pushed the worktree-issue-1186 branch from f488e62 to a83e2fe Compare June 15, 2026 13:32
@planetf1
planetf1 marked this pull request as ready for review June 15, 2026 13:49
@planetf1
planetf1 requested a review from a team as a code owner June 15, 2026 13:49
@psschwei

Copy link
Copy Markdown
Member

Will actually review later, but there were a few non-blocking optional things that an AI review put forward (so treat them as such):

  1. effective_capability non-emptiness isn't enforced. The property docstring says "guaranteed non-empty," and test_all_catalog_effective_capabilities_are_nonempty checks current entries — but nothing prevents a future entry with capability="" (empty string is not None, so it'd be returned as-is). If you want the guarantee to be real rather than conventional, a @field_validator("capability") rejecting empty/whitespace strings would enforce it. Reasonable to defer to a later phase given this is scaffolding.

  2. No uniqueness validator on capability. Two entries resolving to the same token would silently collapse in the frozenset. The test catches it for the catalog as it stands, but it's a test-time guard, not a structural one. Again, fine to defer.

  3. Pre-existing (not introduced here): the class name IntriniscsCatalogEntry is misspelled ("Intriniscs"). Out of scope for this PR.

Address two structural gaps raised in PR review:

- Add field_validator on `capability` rejecting empty/whitespace strings,
  making the effective_capability "guaranteed non-empty" docstring true
  rather than conventional.
- Add module-level uniqueness check so duplicate effective_capability
  values raise ValueError at import time, not just at test time.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
@planetf1

Copy link
Copy Markdown
Contributor Author

Thanks @psschwei.

1 and 2 are fixed in the latest commit: now rejects empty/whitespace strings via a field validator, and the catalog asserts uniqueness of values at import time rather than relying on the test alone.

3 (the class name misspelling) is tracked in #1268. Deferred to a standalone PR after #1256 and this one merge — both have open diffs against the same files and adding a rename now would create unnecessary conflict for one of them.

Comment thread mellea/backends/adapters/catalog.py
Comment thread mellea/backends/adapters/catalog.py
Comment thread test/backends/test_adapters/test_catalog.py Outdated

@psschwei psschwei left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm outside of the things @ajbozarth already mentioned

planetf1 added 3 commits June 16, 2026 09:24
…uarantee true

Add _check_name validator rejecting empty/whitespace strings on
IntriniscsCatalogEntry.name. The effective_capability fallback path returns
name unchanged, so without this validator the "guaranteed non-empty"
docstring was only honoured when capability was explicitly set. Now both
branches are guarded and the guarantee holds for any constructed entry.

Consistent with the existing validators on capability and revision.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
…, revision

Extend all three string token validators to be consistent: empty/whitespace-
only strings were already rejected; now leading or trailing whitespace is
rejected too. Silent normalisation (strip-and-return) is deliberately not
used — the codebase convention is validators raise, they do not mutate.

Covers _check_name, _check_capability (both new in this PR), and the
pre-existing validate_revision. Update docstrings and Raises entries
accordingly.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Move the inline `from mellea.backends.adapters.catalog import
_INTRINSICS_CATALOG_ENTRIES` from inside two test functions to the
module-level import block in each file. No logic change.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

@akihikokuroda akihikokuroda left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@planetf1
planetf1 added this pull request to the merge queue Jun 16, 2026
Merged via the queue into generative-computing:main with commit e1fce6d Jun 16, 2026
9 checks passed
@planetf1
planetf1 deleted the worktree-issue-1186 branch June 16, 2026 16:34
planetf1 added a commit to planetf1/mellea that referenced this pull request Jun 17, 2026
…mputing#1266 content)

Two HuggingFace references in docstrings were introduced by the generative-computing#1186
capability-decoupling work (generative-computing#1266) after the original sweep's branch
point — not caught until rebase. Brought in line with the rest of the
brand name corrections.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
psschwei pushed a commit to psschwei/mellea that referenced this pull request Jun 17, 2026
…nerative-computing#1192) (generative-computing#1256)

* chore(terminology): adopt agreed Granite Switch / Mellea glossary (generative-computing#1192)

Sweeps the five Phase 0 adapter files, AGENTS.md, and the reference glossary
to use the canonical terms agreed under Epic generative-computing#929:
- "adapter function" replaces "intrinsic" / "intrinsic function" in prose
- "aLoRA" replaces "Activated LoRA" / "activated-LoRA"
- "Granite Switch" replaces "Project Granite Switch"
- "cache hit/miss" framing in the aLoRA glossary entry (never "cache rewrite")

Python symbol names (IntrinsicAdapter, fetch_intrinsic_metadata, etc.) and
import paths are untouched — those renames are Phase 1 (generative-computing#1136) / Phase 4 (generative-computing#1144).

Also adds the full agreed glossary set to docs/docs/reference/glossary.md:
Adapter function, Adapter, Capability, Checkpoint, Granite Libraries,
Granite Switch, Granite Switch composer, Mellea, and a References section.
Fixes the duplicate ## 13 heading in AGENTS.md (→ ## 14).

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* fixup! chore(terminology): adopt agreed Granite Switch / Mellea glossary (generative-computing#1192)

Address code review findings:
- adapter.py: fix four missed prose descriptions in EmbeddedIntrinsicAdapter
  and CustomIntrinsicAdapter (intrinsic_name param docs, __init__ docstring)
- adapter.py:202: lands → is merged (second TODO comment missed by replace-all)
- adapter.py:335: fix clunky doubled noun in EmbeddedIntrinsicAdapter init doc
- glossary.md aLoRA entry: rewrite to centre base-model KV-cache reuse /
  single-token activation (the fundamental mechanism), keep multi-adapter
  Granite Switch throughput benefit as secondary paragraph
- glossary.md: move ## Mellea above ## MelleaSession (alphabetical order)

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* chore(terminology): extend glossary sweep to docstrings, backends, and integration docs

- Fix Adapter / Adapter function glossary entry ordering (alphabetical)
- Replace "Guardian Intrinsic function" with "Guardian adapter function" in
  all four Guardian glossary entries and the GuardianCheck deprecation note
- Replace "Intrinsic function(s)" with "Adapter function(s)" in module and
  function docstrings: core.py, rag.py, guardian.py
- Replace "activated LoRAs" / "ALoras" with "aLoRA adapters" in backend
  error messages and docstrings: huggingface.py, litellm.py, watsonx.py,
  ollama.py; fix double-"support" typo in watsonx.py and ollama.py error
  messages
- Replace "ALora" with "aLoRA" in requirement.py comment
- Replace "intrinsic functions" / "intrinsic wrappers" / "intrinsics" prose
  in integration docs: openai.md, huggingface.md, lora-and-alora-adapters.md

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* chore(terminology): sweep Guardian Intrinsics → Guardian adapter functions

Replaces all remaining "Guardian Intrinsics" / "Guardian intrinsics" prose
with "Guardian adapter functions" across 13 doc pages and one deprecation
warning in guardian.py.  Not covered by Phase 1 (generative-computing#1136, symbol-only) or
Phase 4 (generative-computing#1144, deferred page rewrite) — safe to fix in this sweep.

Also fixes 7 bare [Intrinsics](path) cross-doc links (missing .md suffix,
violating CONTRIBUTING_DOCS.md) by adding the extension and updating link
text to "Adapter functions".  No page slugs are renamed; the intrinsics.md
page stays in place until Phase 4.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* chore(terminology): address review findings from generative-computing#1256

Four user-facing error strings in adapter.py still said "intrinsic" as prose,
directly violating issue generative-computing#1192 criterion generative-computing#4 ("Done when: no 'intrinsic' prose
remains in the five Phase 0 files"). Sweep the four f-strings:
  - "Intrinsic '{name}' not available as an adapter of type …"
  - "io.yaml for intrinsic '{name}' …"
  - "No adapter found for intrinsic '{name}' in {path}" (×2)

Sweep remaining user-visible prose in two other files:
  - openai.py:582 — "has no adapter for processing intrinsic: …"
  - rag.py:206   — "Citation generation intrinsic failed: …"

Fix openai.md section heading ("## Intrinsics with Granite Switch" →
"## Adapter functions with Granite Switch") — body was swept in prior
commit but heading was missed, leaving the section self-contradictory.

Sweep intrinsics.md page title and body prose (title, opening paragraph,
backend-note callout, reuse-note, Direct-usage heading, catalog reference)
to use "adapter functions" throughout. File slug rename is deferred to
Phase 4 (generative-computing#1144); the page content is swept here to keep it consistent
with the cross-references that now point to it as "Adapter functions".

Fix three remaining safety-guardrails links without .md suffix in
common-errors.md and the lora-and-alora-adapters link in
prefix-caching-and-kv-blocks.md.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* test: update error message assertion after terminology sweep

test_filter_nonexistent_intrinsic matched the old "No adapter found
for intrinsic" string; update to match the swept form "No adapter
found for adapter function".

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* docs: fix broken tutorial link in safety-guardrails See also

The .md suffix added in the terminology sweep made Docusaurus validate
the link, revealing it pointed to the wrong filename. The tutorial file
is 04-making-agents-reliable.md, not making-agents-reliable.md.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* docs: correct HuggingFace → Hugging Face brand name in prose

'Hugging Face' is the correct two-word brand name. URLs (huggingface.co),
Python symbols (huggingface_hub, LocalHFBackend), and file slugs are
intentionally unchanged — only prose references are corrected.

14 files, 25 occurrences. 9 of these files were already in the PR diff;
the remaining 5 (context-and-sessions, installation, quickstart,
smolagents, faq) are included since the work was already underway.

No page slugs change and no redirects are needed — Docusaurus derives
URL slugs from file paths, not page titles.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* docs: correct HuggingFace → Hugging Face in catalog.py (generative-computing#1266 content)

Two HuggingFace references in docstrings were introduced by the generative-computing#1186
capability-decoupling work (generative-computing#1266) after the original sweep's branch
point — not caught until rebase. Brought in line with the rest of the
brand name corrections.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

* chore(terminology): thorough sweep of remaining intrinsic prose and HuggingFace brand

catalog.py: effective_capability docstring said "for this intrinsic"; corrected
to "adapter function" (Phase 0 file, missed in original sweep).

rag.py: three docstrings and two internal comments still referenced
"find_citations intrinsic" / "passed to intrinsics"; swept to "adapter function".

adapter.py, huggingface.py, openai.py, guardian.py: HuggingFace brand name
corrected to "Hugging Face" in docstrings and comments across all four
Python files that were already in the PR diff.

Note: docstrings that describe _generate_from_intrinsic() by name (openai.py,
huggingface.py) are deliberately left — they are tied to that Python symbol
and will be swept naturally in Phase 1 (generative-computing#1136).

Out of scope for this PR (noted for follow-up): docs/examples/ READMEs not
in this diff, and Python files outside the sweep perimeter (backend.py,
tools.py, model_ids.py, etc.).

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>

---------

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(intrinsics): decouple capability registry from catalog entry names (Epic #929 Phase 0)

4 participants