mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-07 16:19:09 +00:00
refactor(models): convert re-added Fable 5 to Bedrock model IDs
This commit is contained in:
parent
396780285d
commit
17a9a25160
8 changed files with 54 additions and 37 deletions
|
|
@ -29,7 +29,7 @@ SUPPORTED_MODELS: list[ModelOption] = [
|
|||
"supports_images": True,
|
||||
},
|
||||
{
|
||||
"id": "anthropic:claude-fable-5",
|
||||
"id": "bedrock_converse:us.anthropic.claude-fable-5",
|
||||
"label": "Fable 5",
|
||||
"efforts": ["low", "medium", "high", "xhigh", "max"],
|
||||
"default_effort": "high",
|
||||
|
|
@ -81,17 +81,19 @@ SUPPORTED_MODELS: list[ModelOption] = [
|
|||
|
||||
SUPPORTED_MODEL_IDS: frozenset[str] = frozenset(m["id"] for m in SUPPORTED_MODELS)
|
||||
|
||||
_FABLE_ID_PREFIX = "bedrock_converse:us.anthropic.claude-fable"
|
||||
|
||||
FABLE_MODEL_IDS: frozenset[str] = frozenset(
|
||||
m["id"] for m in SUPPORTED_MODELS if m["id"].startswith("anthropic:claude-fable")
|
||||
m["id"] for m in SUPPORTED_MODELS if m["id"].startswith(_FABLE_ID_PREFIX)
|
||||
)
|
||||
|
||||
|
||||
def fable_disabled_fallback(effort: object = None) -> tuple[str, str]:
|
||||
"""Newest supported non-Fable Anthropic model (keeps the Claude family),
|
||||
"""Newest supported non-Fable Claude model (keeps the Claude family),
|
||||
else the global default. Substitutes a Fable selection when Fable is
|
||||
disabled workspace-wide, preserving ``effort`` when the fallback supports it."""
|
||||
for m in SUPPORTED_MODELS:
|
||||
if m["id"].startswith("anthropic:") and m["id"] not in FABLE_MODEL_IDS:
|
||||
if m["id"].startswith("bedrock_converse:us.anthropic.") and m["id"] not in FABLE_MODEL_IDS:
|
||||
return m["id"], _fallback_effort_for(m, effort) or m["default_effort"]
|
||||
return default_model_pair()
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
{"sha": "216cf181", "pr": 1699, "subject": "fix: keep workflow HITL without token downscoping (#1699)", "disposition": "landed", "reason": "DIVERGES-FROM-UPSTREAM: fork deliberately does NOT adopt #1699's standing-token workflows:write broadening. Security review (#159) BLOCKed it — the standing ALWAYS-ON proxy token carrying workflows:write turns the HITL guard's git-push-parser gaps (obfuscated-expansion push, `gh api` REST contents PUT, cross-branch refspecs) into live unapproved-workflow-push exploits. Fork keeps BASE without workflows:write and restores the transient per-approval elevation (_run_with_workflow_token mints WORKFLOW_RUNTIME_PROXY_TOKEN_PERMISSIONS around the approved, guard-normalized fixed_command, then downscopes to RUNTIME then BASE): the token scope is the backstop the parser relies on, so a bypass hits GitHub 403. HITL diff-preview/approval-URL/Slack-card additions from #159 retained; token-model divergence only.", "branch": "plan-approval", "local_sha": null, "updated": "2026-07-09T20:00:00Z"}
|
||||
{"sha": "67abf5b0", "pr": 1659, "subject": "fix: surface attributed PR creation failures (#1659)", "disposition": "deferred", "reason": "PR-attribution-failure guard (new mw, safe imports); heavy conflict on diverged open_pull_request.py", "branch": "pr-attribution", "local_sha": null, "updated": "2026-07-08T20:14:42Z"}
|
||||
{"sha": "3dbc0282", "pr": 1676, "subject": "fix: preserve plan redirects after login (#1676)", "disposition": "landed", "reason": "FLAG-HUMAN: follow-on to landed #1668 refining sanitize_redirect_to (open-redirect auth surface); not a dup", "branch": "plan-approval", "local_sha": null, "updated": "2026-07-09T17:10:22Z"}
|
||||
{"sha": "c75cbb1f", "pr": 1677, "subject": "feat: re-add Fable 5 with an admin toggle to disable it (#1677)", "disposition": "deferred", "reason": "FLAG-HUMAN: re-adds Fable 5 via anthropic: — contradicts dev's deliberate hide (#1483) + Bedrock migration (#62); wont-merge candidate", "branch": "fable-admin-toggle", "local_sha": null, "updated": "2026-07-08T20:14:42Z"}
|
||||
{"sha": "c75cbb1f", "pr": 1677, "subject": "feat: re-add Fable 5 with an admin toggle to disable it (#1677)", "disposition": "landed", "reason": "Ported + Bedrock-converted onto dev via feat/readd-fable5-bedrock; anthropic: Fable ID mapped to bedrock_converse:us.anthropic.claude-fable-5.", "branch": "fable-admin-toggle", "local_sha": null, "updated": "2026-07-10T17:07:19Z"}
|
||||
{"sha": "bb104d93", "pr": 1679, "subject": "fix: submit plan comments with cmd enter (#1679)", "disposition": "landed", "reason": "applies clean but edits fork-diverged PlanReview.tsx (#130); needs UI/e2e validation — separate PR", "branch": "plan-approval", "local_sha": null, "updated": "2026-07-09T17:10:22Z"}
|
||||
{"sha": "304032fa", "pr": 1680, "subject": "chore: clarify question answering prompt (#1680)", "disposition": "deferred", "reason": "reword Slack info-only answer guidance; conflicts w/ fork's customized Slack prompt", "branch": "prompt-tweaks", "local_sha": null, "updated": "2026-07-08T20:14:42Z"}
|
||||
{"sha": "5003c953", "pr": 1683, "subject": "feat: open Linear-triggered PRs as the triggering user (#1683)", "disposition": "deferred", "reason": "FLAG-HUMAN: adds linear to resolve_github_token per-user OAuth branch (auth surface); depends on #1626 linear.py", "branch": "linear-pr-as-user", "local_sha": null, "updated": "2026-07-08T20:14:42Z"}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ Rows key on the **upstream SHA** (stable across local cherry-picks). Deferred ro
|
|||
| `5f7f5fbd` | #1697 | fix: Reduce graph import and loader startup latency (#1697) | Landed | import-hygiene refactor; cross-cutting, references many deferred upstream-only modules | durable-dispatch |
|
||||
| `216cf181` | #1699 | fix: keep workflow HITL without token downscoping (#1699) | Landed | DIVERGES-FROM-UPSTREAM: fork deliberately does NOT adopt #1699's standing-token workflows:write broadening. Security review (#159) BLOCKed it — the standing ALWAYS-ON proxy token carrying workflows:write turns the HITL guard's git-push-parser gaps (obfuscated-expansion push, `gh api` REST contents PUT, cross-branch refspecs) into live unapproved-workflow-push exploits. Fork keeps BASE without workflows:write and restores the transient per-approval elevation (_run_with_workflow_token mints WORKFLOW_RUNTIME_PROXY_TOKEN_PERMISSIONS around the approved, guard-normalized fixed_command, then downscopes to RUNTIME then BASE): the token scope is the backstop the parser relies on, so a bypass hits GitHub 403. HITL diff-preview/approval-URL/Slack-card additions from #159 retained; token-model divergence only. | plan-approval |
|
||||
| `3dbc0282` | #1676 | fix: preserve plan redirects after login (#1676) | Landed | FLAG-HUMAN: follow-on to landed #1668 refining sanitize_redirect_to (open-redirect auth surface); not a dup | plan-approval |
|
||||
| `c75cbb1f` | #1677 | feat: re-add Fable 5 with an admin toggle to disable it (#1677) | Landed | Ported + Bedrock-converted onto dev via feat/readd-fable5-bedrock; anthropic: Fable ID mapped to bedrock_converse:us.anthropic.claude-fable-5. | fable-admin-toggle |
|
||||
| `bb104d93` | #1679 | fix: submit plan comments with cmd enter (#1679) | Landed | applies clean but edits fork-diverged PlanReview.tsx (#130); needs UI/e2e validation — separate PR | plan-approval |
|
||||
| `7f7af715` | #1684 | feat: auto-load scoped AGENTS on reads (#1684) | Landed | ported in #129 (SubdirAgentsReadMiddleware) | subdir-agents |
|
||||
| `88b62322` | #1685 | feat: add platform issue reporting tool (#1685) | Landed | ported in #129 (report_platform_issue tool) | small-tools |
|
||||
|
|
@ -88,7 +89,6 @@ Rows key on the **upstream SHA** (stable across local cherry-picks). Deferred ro
|
|||
| `48217b68` | #1489 | feat(open-swe): add E2B sandbox provider (#1489) | Deferred | additive E2B provider; separable but ships on the async sandbox.py base | sandbox-refactor |
|
||||
| `fbc6de85` | #1667 | chore(deps): bump fireworks-ai from 1.2.0a75 to 1.2.0a86 (#1667) | Deferred | conflicts on pick — dev diverged to fireworks-ai a85; a86 still wanted, needs manual bump + uv lock | deps |
|
||||
| `67abf5b0` | #1659 | fix: surface attributed PR creation failures (#1659) | Deferred | PR-attribution-failure guard (new mw, safe imports); heavy conflict on diverged open_pull_request.py | pr-attribution |
|
||||
| `c75cbb1f` | #1677 | feat: re-add Fable 5 with an admin toggle to disable it (#1677) | Deferred | FLAG-HUMAN: re-adds Fable 5 via anthropic: — contradicts dev's deliberate hide (#1483) + Bedrock migration (#62); wont-merge candidate | fable-admin-toggle |
|
||||
| `304032fa` | #1680 | chore: clarify question answering prompt (#1680) | Deferred | reword Slack info-only answer guidance; conflicts w/ fork's customized Slack prompt | prompt-tweaks |
|
||||
| `5003c953` | #1683 | feat: open Linear-triggered PRs as the triggering user (#1683) | Deferred | FLAG-HUMAN: adds linear to resolve_github_token per-user OAuth branch (auth surface); depends on #1626 linear.py | linear-pr-as-user |
|
||||
| `feb7ac98` | #1689 | feat(web): surface thread sandbox ID with touch-friendly menu (#1689) | Deferred | applies clean but frontend<->backend contract (thread_api->queries->types->sidebar); needs UI build + e2e validation — separate PR | dashboard-ui |
|
||||
|
|
|
|||
|
|
@ -199,26 +199,33 @@ async def test_agent_gate_swaps_disabled_fable_profile_to_opus() -> None:
|
|||
patch(
|
||||
"agent.server.get_team_default_model_pair",
|
||||
new_callable=AsyncMock,
|
||||
return_value=(("openai:gpt-5.5", "medium"), ("openai:gpt-5.5", "low")),
|
||||
return_value=(
|
||||
("bedrock_converse:us.anthropic.claude-opus-4-8", "medium"),
|
||||
("bedrock_converse:us.anthropic.claude-opus-4-8", "low"),
|
||||
),
|
||||
),
|
||||
# Profile selected Fable back when it was allowed; it's now disabled.
|
||||
patch(
|
||||
"agent.server.load_profile",
|
||||
new_callable=AsyncMock,
|
||||
return_value={
|
||||
"default_model": "anthropic:claude-fable-5",
|
||||
"default_model": "bedrock_converse:us.anthropic.claude-fable-5",
|
||||
"reasoning_effort": "high",
|
||||
},
|
||||
),
|
||||
patch("agent.server.get_team_fable_enabled", new_callable=AsyncMock, return_value=False),
|
||||
patch("agent.server.fallback_model_id_for", return_value=None),
|
||||
patch("agent.server.make_model", side_effect=[main_model, subagent_model]) as make_model,
|
||||
patch(
|
||||
"agent.utils.deferred_model.make_model", side_effect=[main_model, subagent_model]
|
||||
) as make_model,
|
||||
patch("agent.server.construct_system_prompt", return_value="prompt"),
|
||||
patch("agent.server.create_deep_agent", side_effect=fake_create_deep_agent),
|
||||
):
|
||||
await get_agent(config)
|
||||
|
||||
# Fable was scrubbed to Opus for both main and subagent; effort preserved.
|
||||
assert make_model.call_args_list[0].args == ("anthropic:claude-opus-4-8",)
|
||||
assert make_model.call_args_list[0].kwargs["effort"] == "high"
|
||||
assert make_model.call_args_list[1].args == ("anthropic:claude-opus-4-8",)
|
||||
assert make_model.call_args_list[0].args == ("bedrock_converse:us.anthropic.claude-opus-4-8",)
|
||||
assert make_model.call_args_list[0].kwargs["additional_model_request_fields"][
|
||||
"output_config"
|
||||
] == {"effort": "high"}
|
||||
assert make_model.call_args_list[1].args == ("bedrock_converse:us.anthropic.claude-opus-4-8",)
|
||||
|
|
|
|||
|
|
@ -3,36 +3,37 @@ import pytest
|
|||
from agent.dashboard.options import SUPPORTED_MODELS, provider_fallback_pair
|
||||
from agent.utils.model import provider_model_kwargs
|
||||
|
||||
SONNET_5_ID = "anthropic:claude-sonnet-5"
|
||||
FABLE_5_ID = "anthropic:claude-fable-5"
|
||||
SONNET_5_ID = "bedrock_converse:us.anthropic.claude-sonnet-5"
|
||||
FABLE_5_ID = "bedrock_converse:us.anthropic.claude-fable-5"
|
||||
|
||||
|
||||
def test_sonnet_5_is_supported_with_documented_efforts() -> None:
|
||||
sonnet = next(m for m in SUPPORTED_MODELS if m["id"] == SONNET_5_ID)
|
||||
assert sonnet["label"] == "Sonnet 5"
|
||||
assert sonnet["label"] == "Sonnet 5 (Bedrock)"
|
||||
assert sonnet["efforts"] == ["low", "medium", "high", "xhigh", "max"]
|
||||
assert sonnet["default_effort"] == "high"
|
||||
assert sonnet["supports_images"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("effort", ["low", "medium", "high", "xhigh", "max"])
|
||||
def test_sonnet_5_efforts_map_to_anthropic_kwargs(effort: str) -> None:
|
||||
def test_sonnet_5_efforts_map_to_bedrock_kwargs(effort: str) -> None:
|
||||
kwargs = provider_model_kwargs(SONNET_5_ID, effort, max_tokens=16_000)
|
||||
assert kwargs["max_tokens"] == 16_000
|
||||
assert kwargs["effort"] == effort
|
||||
assert kwargs["thinking"] == {"type": "adaptive", "display": "summarized"}
|
||||
fields = kwargs["additional_model_request_fields"]
|
||||
assert fields["output_config"] == {"effort": effort}
|
||||
assert fields["thinking"] == {"type": "adaptive", "display": "summarized"}
|
||||
|
||||
|
||||
def test_sonnet_46_fallback_uses_sonnet_5() -> None:
|
||||
assert provider_fallback_pair("anthropic:claude-sonnet-4-6", "xhigh") == (
|
||||
assert provider_fallback_pair("bedrock_converse:us.anthropic.claude-sonnet-4-6", "xhigh") == (
|
||||
SONNET_5_ID,
|
||||
"xhigh",
|
||||
)
|
||||
|
||||
|
||||
def test_opus_fallback_stays_on_opus_family() -> None:
|
||||
assert provider_fallback_pair("anthropic:claude-opus-4-7", "xhigh") == (
|
||||
"anthropic:claude-opus-4-8",
|
||||
assert provider_fallback_pair("bedrock_converse:us.anthropic.claude-opus-4-7", "xhigh") == (
|
||||
"bedrock_converse:us.anthropic.claude-opus-4-8",
|
||||
"xhigh",
|
||||
)
|
||||
|
||||
|
|
@ -46,8 +47,9 @@ def test_fable_5_is_supported_with_documented_efforts() -> None:
|
|||
|
||||
|
||||
@pytest.mark.parametrize("effort", ["low", "medium", "high", "xhigh", "max"])
|
||||
def test_fable_5_efforts_map_to_anthropic_kwargs(effort: str) -> None:
|
||||
def test_fable_5_efforts_map_to_bedrock_kwargs(effort: str) -> None:
|
||||
kwargs = provider_model_kwargs(FABLE_5_ID, effort, max_tokens=16_000)
|
||||
assert kwargs["max_tokens"] == 16_000
|
||||
assert kwargs["effort"] == effort
|
||||
assert kwargs["thinking"] == {"type": "adaptive", "display": "summarized"}
|
||||
fields = kwargs["additional_model_request_fields"]
|
||||
assert fields["output_config"] == {"effort": effort}
|
||||
assert fields["thinking"] == {"type": "adaptive", "display": "summarized"}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ from agent.dashboard.options import model_supports_images
|
|||
|
||||
_TEXT_ONLY_MODEL = "fireworks:accounts/fireworks/models/deepseek-v4-pro"
|
||||
_VISION_MODEL = "bedrock_converse:us.anthropic.claude-opus-4-8"
|
||||
_FABLE = "anthropic:claude-fable-5"
|
||||
_PAIR = ("openai:gpt-5.5", "medium")
|
||||
_FABLE = "bedrock_converse:us.anthropic.claude-fable-5"
|
||||
_PAIR = ("bedrock_converse:us.anthropic.claude-opus-4-8", "medium")
|
||||
|
||||
|
||||
def _image() -> thread_api.DashboardImageBody:
|
||||
|
|
|
|||
|
|
@ -94,28 +94,34 @@ def test_global_default_is_supported() -> None:
|
|||
|
||||
|
||||
def test_gate_fable_passthrough_when_enabled() -> None:
|
||||
assert gate_fable_model("anthropic:claude-fable-5", "high", fable_enabled=True) == (
|
||||
"anthropic:claude-fable-5",
|
||||
assert gate_fable_model(
|
||||
"bedrock_converse:us.anthropic.claude-fable-5", "high", fable_enabled=True
|
||||
) == (
|
||||
"bedrock_converse:us.anthropic.claude-fable-5",
|
||||
"high",
|
||||
)
|
||||
|
||||
|
||||
def test_gate_fable_swaps_to_opus_when_disabled() -> None:
|
||||
assert gate_fable_model("anthropic:claude-fable-5", "high", fable_enabled=False) == (
|
||||
"anthropic:claude-opus-4-8",
|
||||
assert gate_fable_model(
|
||||
"bedrock_converse:us.anthropic.claude-fable-5", "high", fable_enabled=False
|
||||
) == (
|
||||
"bedrock_converse:us.anthropic.claude-opus-4-8",
|
||||
"high",
|
||||
)
|
||||
|
||||
|
||||
def test_gate_fable_leaves_non_fable_ids_alone() -> None:
|
||||
assert gate_fable_model("openai:gpt-5.5", "high", fable_enabled=False) == (
|
||||
"openai:gpt-5.5",
|
||||
assert gate_fable_model(
|
||||
"fireworks:accounts/fireworks/models/deepseek-v4-pro", "high", fable_enabled=False
|
||||
) == (
|
||||
"fireworks:accounts/fireworks/models/deepseek-v4-pro",
|
||||
"high",
|
||||
)
|
||||
|
||||
|
||||
def test_fable_disabled_fallback_is_non_fable_anthropic() -> None:
|
||||
def test_fable_disabled_fallback_is_non_fable_claude() -> None:
|
||||
model, effort = fable_disabled_fallback("high")
|
||||
assert model == "anthropic:claude-opus-4-8"
|
||||
assert model == "bedrock_converse:us.anthropic.claude-opus-4-8"
|
||||
assert model not in FABLE_MODEL_IDS
|
||||
assert effort == "high"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import pytest
|
|||
from agent.dashboard.options import FABLE_MODEL_IDS
|
||||
from agent.dashboard.team_settings import TeamSettingsUpdate, get_team_fable_enabled
|
||||
|
||||
_FABLE = "anthropic:claude-fable-5"
|
||||
_FABLE = "bedrock_converse:us.anthropic.claude-fable-5"
|
||||
|
||||
# --- accessor: get_team_fable_enabled (async, patched store) ---
|
||||
|
||||
|
|
@ -105,10 +105,10 @@ def test_disable_transition_coerces_all_fable_defaults() -> None:
|
|||
def test_update_leaves_non_fable_defaults_untouched_when_disabled() -> None:
|
||||
# Coercion only rewrites Fable ids; other selections pass through unchanged.
|
||||
update = TeamSettingsUpdate(
|
||||
default_agent_model="openai:gpt-5.5",
|
||||
default_agent_model="bedrock_converse:us.anthropic.claude-sonnet-5",
|
||||
default_agent_reasoning_effort="medium",
|
||||
)
|
||||
assert update.default_agent_model == "openai:gpt-5.5"
|
||||
assert update.default_agent_model == "bedrock_converse:us.anthropic.claude-sonnet-5"
|
||||
assert update.default_agent_reasoning_effort == "medium"
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue