Skip to content

feat(domain): add category descriptions - #225

Open
Amidwestnoob wants to merge 2 commits into
OpenAgentHQ:mainfrom
Amidwestnoob:feature/category-descriptions
Open

feat(domain): add category descriptions#225
Amidwestnoob wants to merge 2 commits into
OpenAgentHQ:mainfrom
Amidwestnoob:feature/category-descriptions

Conversation

@Amidwestnoob

Copy link
Copy Markdown

What changed

  • Added a human-readable description property to each Category.
  • Included the available category names and descriptions in install-category --help.
  • Added domain and CLI regression tests.

The existing enum values and serialization behavior are unchanged.

Tests

  • ruff check src tests
  • ruff format --check src tests
  • mypy src
  • bandit -q -r src
  • pytest -q --cov=modeldock --cov-report=term

Result: 589 passed, 2 skipped because Ollama was not running, 87% coverage.

pre-commit run --all-files still reports the same existing notebook Ruff errors and isolated-hook mypy errors found on clean main; this change adds no new failures.

Closes #103

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

Adds a description property to Category returning human-readable text for each enum member, and wires the install-category command's argument help to list every category with its description via _category_help() instead of the hardcoded "(e.g. coding)" hint.

No blocking issues surfaced.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 397 functions depend on the 99 functions this change touches.

Health — this change adds coupling hotspots:

  • new: load_settings() — 14 callers, 6 callees
  • new: _manager() — 21 callers, 3 callees
  • new: test_composite_versions_and_resolve_delegate() — 0 callers, 7 callees
  • new: test_huggingface_specs_carry_provenance() — 0 callers, 7 callees
  • new: test_cli_console_helpers() — 0 callers, 6 callees

Verification — 397 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 385 function(s) in the blast radius were not formally verified this run

· 5 more finding(s) on lines outside this diff (see the check run).

output = " ".join(result.output.replace("│", " ").split())

assert result.exit_code == 0
for category in Category:
@himanshu231204

Copy link
Copy Markdown
Member

Code Review Suggestions

Nice, focused change — the existing enum values and serialization are untouched, and the CLI test normalising Rich's box-drawing characters is a sensible workaround. One design issue to fix before merge, plus a few smaller points.


Important

1. description property raises KeyError for any future Category member not in the dict (domain/model.py)

The dict-lookup approach silently becomes a runtime crash the moment anyone adds a new Category without updating the property:

@property
def description(self) -> str:
    return {
        Category.CHAT: "...",
        ...
    }[self]   # ← KeyError if a new member is added

Since _category_help() iterates every member at import time, this would crash the entire CLI on install-category --help — not just on the new category.

The assert len(Category) == 6 test guard helps catch it in CI, but the better fix is to make it structurally impossible to add a category without a description by storing it in the enum definition:

class Category(str, Enum):
    def __new__(cls, value: str, description: str = "") -> "Category":
        obj = str.__new__(cls, value)
        obj._value_ = value
        obj._description = description
        return obj

    @property
    def description(self) -> str:
        return self._description

    CHAT      = ("chat",      "General-purpose conversational models")
    CODING    = ("coding",    "Models optimized for code generation and completion")
    EMBEDDING = ("embedding", "Models that convert text into vector representations")
    VISION    = ("vision",    "Models that understand images and text")
    REASONING = ("reasoning", "Models optimized for multi-step reasoning")
    INSTRUCT  = ("instruct",  "Models tuned to follow instructions")

.value still returns the plain lowercase string (the str subclass behaviour is preserved), and adding a new member without a description now produces a TypeError at class-definition time rather than a KeyError at runtime.

The assert len(Category) == 6 guard in test_domain.py can then be removed — the type system enforces the invariant instead.


2. Dict rebuilt on every .description call

The current implementation recreates the dict on each property access. For 6 categories iterated in _category_help() that's 6 × 6 = 36 dict allocations per import. Not a real performance problem, but it is unnecessary. The enum member data pattern above eliminates this entirely; if you keep the dict approach, at minimum hoist it to a class-level constant:

_DESCRIPTIONS: dict["Category", str] = {
    Category.CHAT: "General-purpose conversational models",
    ...
}

@property
def description(self) -> str:
    return self._DESCRIPTIONS[self]

Minor

3. _category_help() could be a module constant

The function is only ever called once, at import time (as a default-argument value). A named constant is clearer about its immutability:

_CATEGORY_HELP: str = "Category name. Available: " + "; ".join(
    f"{item.value} ({item.description})" for item in Category
)

def install_category_cmd(
    category: str = typer.Argument(..., help=_CATEGORY_HELP),
    ...

4. test_category_descriptions couples the test to exact wording

The test copies the description strings verbatim from the implementation. That's fine as a regression/pinning test, but a comment explaining the intent would help future contributors understand why the strings are duplicated rather than derived:

def test_category_descriptions() -> None:
    # Pin exact wording so accidental rewording is caught in CI.
    expected = { ... }

Strengths

  • Enum values and serialization unchanged — zero breaking-change risk.
  • _category_help() builds the string from the domain rather than hardcoding it in the CLI layer — correct separation.
  • CLI test covers both the value and the description appearing in help output.
  • assert len(Category) == 6 is a thoughtful guard even if the enum member data pattern makes it redundant.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

Adds a human-readable description to each Category member via a custom __new__ that carries (value, description) tuples, and derives the install-category argument help text from those descriptions instead of a hardcoded example. New tests pin the exact wording of each category description and assert the install-category --help output lists every category's value and description.

No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 398 functions depend on the 100 functions this change touches.

Health — this change adds coupling hotspots:

  • new: load_settings() — 14 callers, 6 callees
  • new: _manager() — 21 callers, 3 callees
  • new: test_composite_versions_and_resolve_delegate() — 0 callers, 7 callees
  • new: test_huggingface_specs_carry_provenance() — 0 callers, 7 callees
  • new: test_cli_console_helpers() — 0 callers, 6 callees

Verification — 398 functions in the blast radius were not formally verified this run (proofs are advisory here).

Health delta baseline: last indexed commit 12a87e2 (diverged from this PR's base — delta is approximate).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 386 function(s) in the blast radius were not formally verified this run

· 5 more finding(s) on lines outside this diff (see the check run).

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.

Domain: Category descriptions

3 participants