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
3 changes: 3 additions & 0 deletions src/hal0/normalize/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
# Canonical virtual names -> ordered chain of roles to try against loaded slots.
DEFAULT_CHAINS: dict[str, tuple[str, ...]] = {
"hal0/chat": ("chat",),
# hal0/agent → the slot named/tagged "agent" (a first-class chat role,
# e.g. the GPU MoE agent slot). Falls back to chat when agent is unloaded.
"hal0/agent": ("agent", "chat"),
"hal0/npu": ("npu", "utility", "chat"),
"hal0/utility": ("utility", "npu", "chat"),
}
Expand Down
11 changes: 11 additions & 0 deletions tests/normalize/test_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,14 @@ async def test_live_resolver_hal0_primary_alias():
res = await resolver.resolve("hal0/primary")
assert res is not None
assert res.model_id == "big"


def test_agent_virtual_name_resolves_to_agent_slot():
"""hal0/agent must resolve to the slot named 'agent' (cutover #662: the
GPU MoE agent slot). Without a DEFAULT_CHAINS entry the virtual name is
unknown → passes through unnormalized → lemonade 404."""
r = resolve_chain("hal0/agent", _slots(), loaded={"qwen3-4b-FLM"})
assert r is not None
assert r.matched_role == "agent"
assert r.model_id == "qwen3-4b-FLM" # the 'agent'-named slot in the fixture
assert r.fallback is False
Loading