feat(code): support Fireworks /routers model ids (#4591)

Fireworks router model ids (`accounts/fireworks/routers/...`) now
display compactly in the status bar and support `/effort` reasoning
controls, matching individual Fireworks model ids.

---

Fireworks exposes two fully-qualified model-id forms: individual models
(`accounts/fireworks/models/...`) and routers that dispatch across
models (`accounts/fireworks/routers/...`, e.g.
`accounts/fireworks/routers/glm-5p1-fast`; see
[models.dev](https://models.dev/providers/fireworks-ai/)). Previously
only the `models/` form was recognized, so router ids rendered with the
full prefix in the status bar and never qualified for reasoning-effort
controls.

This teaches the two places that key off the qualified prefix to accept
both forms:

- `ModelLabel`'s `PROVIDER_PREFIX_STRIPS` now strips the `routers/`
prefix for compact status-bar display.
- `_classify_reasoning_provider` gates `/effort` on a shared
`_FIREWORKS_MODEL_ID_PREFIXES` tuple covering both forms, so router
models get the same family-based effort support as individual models.

The model-family logic (Kimi/GLM/DeepSeek) is substring-based and
already works for either form.

Made by [Open
SWE](https://openswe.vercel.app/agents/29503e81-c1ee-65e7-b10d-56661b75aa89)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
Mason Daugherty
2026-07-11 19:32:08 -04:00
committed by GitHub
parent 518c322e7d
commit 1c08d2705f
5 changed files with 34 additions and 4 deletions
+6
View File
@@ -13,6 +13,12 @@ from typing import Final
DEFAULT_AGENT_NAME: Final[str] = "agent"
"""Default agent / assistant identifier when no `-a` flag is given."""
FIREWORKS_MODEL_ID_PREFIXES: Final[tuple[str, ...]] = (
"accounts/fireworks/models/",
"accounts/fireworks/routers/",
)
"""Fully-qualified prefixes for Fireworks model and router IDs."""
SYSTEM_MESSAGE_PREFIX: Final[str] = "[SYSTEM]"
"""Prefix for synthetic human messages (e.g. interrupt cancellation notices).
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Literal, NamedTuple, TypeAlias, get_args
if TYPE_CHECKING:
from collections.abc import Callable
from deepagents_code._constants import FIREWORKS_MODEL_ID_PREFIXES
from deepagents_code.model_config import CODEX_PROVIDER, ModelSpec
logger = logging.getLogger(__name__)
@@ -456,7 +457,7 @@ def _classify_reasoning_provider(provider: str, model: str) -> ReasoningProvider
return "anthropic"
if provider == "google_genai" and model_lower.startswith("gemini-3"):
return "google_genai"
if provider == "fireworks" and model_lower.startswith("accounts/fireworks/models/"):
if provider == "fireworks" and model_lower.startswith(FIREWORKS_MODEL_ID_PREFIXES):
return "fireworks"
if provider == "xai" and _is_xai_grok_45(model_lower):
return "xai"
@@ -14,6 +14,7 @@ from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Static
from deepagents_code._constants import FIREWORKS_MODEL_ID_PREFIXES
from deepagents_code._env_vars import HIDE_CWD, HIDE_GIT_BRANCH, is_env_truthy
from deepagents_code.config import get_glyphs
from deepagents_code.tui.widgets.loading import Spinner
@@ -27,11 +28,11 @@ if TYPE_CHECKING:
from textual.timer import Timer
PROVIDER_PREFIX_STRIPS: dict[str, tuple[str, ...]] = {
"fireworks": ("accounts/fireworks/models/",),
"fireworks": FIREWORKS_MODEL_ID_PREFIXES,
}
"""Some providers (e.g. Fireworks) require fully-qualified IDs like
`accounts/fireworks/models/...` that crowd out the rest of the status bar;
strip the registered prefixes before display."""
`accounts/fireworks/models/...` or `accounts/fireworks/routers/...` that crowd
out the rest of the status bar; strip the registered prefixes before display."""
ConnectionState = Literal["", "connecting", "reconnecting", "resuming"]
"""Connection states the status bar can display (`''` means cleared)."""
@@ -94,6 +94,12 @@ def _restore_settings() -> Iterator[None]:
("low", "medium", "high"),
),
("fireworks:accounts/fireworks/models/glm-5p2", ("none", "high", "max")),
# Fireworks routers (`accounts/fireworks/routers/...`) are gated the same
# as individual models, so effort support keys off the model family.
(
"fireworks:accounts/fireworks/routers/glm-5p1-fast",
("none", "high", "max"),
),
# Recognized provider, wrong model family: the per-provider prefix
# guards in `_classify_reasoning_provider` (and the Fireworks family
# check) must reject these rather than fall through to an effort set.
@@ -103,6 +109,9 @@ def _restore_settings() -> Iterator[None]:
("google_genai:gemini-2.5-flash", ()),
("xai:grok-4", ()),
("fireworks:accounts/fireworks/models/llama-v3p1-70b-instruct", ()),
# Same guard for the router prefix: a recognized router whose id carries
# no known family token must also fall through to no efforts.
("fireworks:accounts/fireworks/routers/llama-v3p1-70b-instruct", ()),
],
)
def test_supported_efforts_for_model(model_spec: str, efforts: tuple[str, ...]) -> None:
@@ -133,6 +142,8 @@ def test_supported_efforts_for_model(model_spec: str, efforts: tuple[str, ...])
("fireworks:accounts/fireworks/models/deepseek-v4-pro", "high"),
("fireworks:accounts/fireworks/models/glm-5p2", "max"),
("fireworks:accounts/fireworks/models/kimi-k2p7-code", None),
# Routers reach the same family-based default lookup as models.
("fireworks:accounts/fireworks/routers/deepseek-v4-pro", "high"),
("ollama:llama3.1", None),
],
)
@@ -542,6 +542,17 @@ class TestModelLabelPrefixStripping:
assert "fireworks:kimi-k2p6" in rendered
assert "accounts/fireworks/models/" not in rendered
async def test_fireworks_routers_prefix_stripped(self) -> None:
"""The fireworks routers prefix is stripped before rendering."""
async with StatusBarApp().run_test() as pilot:
label = pilot.app.query_one("#model-display", ModelLabel)
label.provider = "fireworks"
label.model = "accounts/fireworks/routers/glm-5p1-fast"
await pilot.pause()
rendered = str(label.render())
assert "fireworks:glm-5p1-fast" in rendered
assert "accounts/fireworks/routers/" not in rendered
async def test_get_content_width_uses_stripped_name(self) -> None:
"""`get_content_width` sizes to the stripped name, not the raw model."""
async with StatusBarApp().run_test() as pilot: