feat: add Mantle override for AWS Bedrock Anthropic endpoint
Introduce `model_capabilities.mantle` flag that installs a provider-specific override in fast-agent's `ModelDatabase._PROVIDER_MODEL_OVERRIDES` to strip features the AWS Bedrock Mantle endpoint rejects (beta headers, extended thinking, task budgets, web tools, prompt caching). Without this override, fast-agent sends default beta headers and `thinking` parameters for modern Claude models that Mantle rejects with a misleading 404 "model does not exist" error.
This commit is contained in:
@@ -123,33 +123,85 @@ def _preflight_mcp_servers(agent_name: str, servers: dict[str, dict]) -> None:
|
||||
# ── Model registration ────────────────────────────────────────────────────────
|
||||
|
||||
def _register_one_model(model_spec: str, capabilities: dict) -> None:
|
||||
"""Register a single model with fast-agent's ModelDatabase if unknown."""
|
||||
"""Register a single model with fast-agent's ModelDatabase.
|
||||
|
||||
Two cases:
|
||||
|
||||
1. **Unknown model** — if fast-agent has no built-in entry for this model,
|
||||
register a minimal ``ModelParameters`` with the declared capabilities.
|
||||
|
||||
2. **Mantle-hosted model** (``capabilities.mantle: true``) — regardless of
|
||||
whether the model has a built-in entry, install a provider-specific
|
||||
override for ``(Provider.ANTHROPIC, model_name)`` in
|
||||
``_PROVIDER_MODEL_OVERRIDES`` that strips the features the AWS Bedrock
|
||||
Mantle endpoint rejects:
|
||||
|
||||
- ``anthropic_required_betas`` (no ``anthropic-beta`` header)
|
||||
- ``reasoning`` / ``reasoning_effort_spec`` (no extended-thinking request)
|
||||
- ``anthropic_task_budget_supported``
|
||||
- ``anthropic_web_fetch_version`` / ``anthropic_web_search_version``
|
||||
- ``cache_ttl`` (prompt caching is not advertised as supported on
|
||||
Mantle for every model; disable the cache planner by default)
|
||||
|
||||
Without this override fast-agent sends beta headers and ``thinking``
|
||||
parameters that Mantle rejects with a misleading ``"model does not
|
||||
exist"`` 404.
|
||||
"""
|
||||
from fast_agent.llm.model_database import ModelDatabase, ModelParameters
|
||||
from fast_agent.llm.provider_types import Provider
|
||||
|
||||
model_name = model_spec.split(".", 1)[-1] if "." in model_spec else model_spec
|
||||
|
||||
if ModelDatabase.get_model_params(model_name) is not None:
|
||||
return
|
||||
|
||||
is_vision = capabilities.get("vision", False)
|
||||
context_window = capabilities.get("context_window", 131072)
|
||||
max_output_tokens = capabilities.get("max_output_tokens", 16384)
|
||||
is_mantle = capabilities.get("mantle", False)
|
||||
|
||||
if is_vision:
|
||||
tokenizes = list(ModelDatabase.QWEN_MULTIMODAL)
|
||||
logger.info("Registered model '%s' with vision capabilities", model_name)
|
||||
existing = ModelDatabase.get_model_params(model_name)
|
||||
|
||||
if existing is None:
|
||||
# Unknown model — register a fresh runtime entry.
|
||||
if is_vision:
|
||||
tokenizes = list(ModelDatabase.QWEN_MULTIMODAL)
|
||||
logger.info("Registered model '%s' with vision capabilities", model_name)
|
||||
else:
|
||||
tokenizes = list(ModelDatabase.TEXT_ONLY)
|
||||
logger.info("Registered model '%s' as text-only", model_name)
|
||||
|
||||
ModelDatabase.register_runtime_model_params(
|
||||
model_name,
|
||||
ModelParameters(
|
||||
context_window=context_window,
|
||||
max_output_tokens=max_output_tokens,
|
||||
tokenizes=tokenizes,
|
||||
),
|
||||
)
|
||||
base_params = ModelDatabase.get_model_params(model_name)
|
||||
else:
|
||||
tokenizes = list(ModelDatabase.TEXT_ONLY)
|
||||
logger.info("Registered model '%s' as text-only", model_name)
|
||||
base_params = existing
|
||||
|
||||
if is_mantle and base_params is not None:
|
||||
# Clone the base params and strip Mantle-incompatible features.
|
||||
override = base_params.model_copy(
|
||||
update={
|
||||
"context_window": context_window,
|
||||
"max_output_tokens": max_output_tokens,
|
||||
"anthropic_required_betas": None,
|
||||
"reasoning": None,
|
||||
"reasoning_effort_spec": None,
|
||||
"anthropic_task_budget_supported": False,
|
||||
"anthropic_web_fetch_version": None,
|
||||
"anthropic_web_search_version": None,
|
||||
"cache_ttl": None,
|
||||
}
|
||||
)
|
||||
normalized = ModelDatabase.normalize_model_name(model_name)
|
||||
ModelDatabase._PROVIDER_MODEL_OVERRIDES[(Provider.ANTHROPIC, normalized)] = override
|
||||
logger.info(
|
||||
"Registered Mantle override for anthropic/'%s' (strips beta headers, thinking, web tools, caching)",
|
||||
model_name,
|
||||
)
|
||||
|
||||
ModelDatabase.register_runtime_model_params(
|
||||
model_name,
|
||||
ModelParameters(
|
||||
context_window=context_window,
|
||||
max_output_tokens=max_output_tokens,
|
||||
tokenizes=tokenizes,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _register_unknown_models(deployment_config: dict) -> None:
|
||||
|
||||
Reference in New Issue
Block a user