mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-23 18:14:53 +02:00
* [aidan] bug: fix schedule button * [aidan] fix/agent-errors: surface provider rate limits * [aidan] ux/cards: click-to-rename for chat and workflow titles Single-click a card's title to enter edit mode inline. Commit on Enter/blur, cancel on Escape. Rename persists via PATCH for workflows and sessions. * [aidan] feat/workflows: seed build prompt for zero-step workflows When a new workflow has no steps, seed the agent with a prompt asking the user to describe what the workflow should do, rather than starting blank. * [aidan] feat/workflows: add-to-schedule popover for unscheduled workflows Clicking the "+" on an unscheduled workflow row opens a popover with two options: - Keep this schedule: enables the workflow's existing cadence and moves it to Scheduled - Change schedule: opens the scheduling editor to pick a different time * [aidan] ux/workflows: wire add-to-schedule popover and simplify New button - Made the "+" icon on unscheduled workflow rows clickable, opening a popover to keep or change the schedule - Removed AddIcon from toolbar "New" button (now reads "New" instead of "+ New") * [aidan] fix/scheduled-tasks: open schedule calendar when Schedule pill clicked Fixed the Schedule pill click being swallowed by the toolbar's dismiss handler. Exempted the toolbar pills via data-toolbar-pills so their click handlers fire. * [aidan] ux/workflows: open New workflow in agent build chat instead of empty card When creating a new workflow from the hub, open it in edit_agent view (with the agent builder chat) instead of a preview card. The workflow is created on the backend first so the embedded session has a real ID.
69 lines
2.7 KiB
Python
69 lines
2.7 KiB
Python
"""Free-trial dispatch injection: the pure-logic pieces that decide routing."""
|
|
|
|
import backend # noqa: F401 (path sanity asserted below)
|
|
|
|
from backend.apps.settings.models import AppSettings
|
|
from backend.apps.settings.credentials import proxy_auth
|
|
from backend.apps.agents.core.error_classify import (
|
|
_is_free_trial_exhausted,
|
|
_is_transient_capacity_error,
|
|
)
|
|
from backend.apps.agents.providers.registry import resolve_model_id_for_sdk
|
|
from backend.apps.subscription.free_trial import _has_own_model
|
|
|
|
|
|
def test_proxy_auth_for_each_mode():
|
|
assert proxy_auth(AppSettings()) == (None, None)
|
|
|
|
pro = AppSettings(
|
|
connection_mode="openswarm-pro",
|
|
openswarm_bearer_token="bear",
|
|
openswarm_proxy_url="https://api.openswarm.com",
|
|
)
|
|
assert proxy_auth(pro) == ("bear", "https://api.openswarm.com")
|
|
|
|
free = AppSettings(
|
|
connection_mode="free-trial",
|
|
free_trial_token="ftk",
|
|
openswarm_proxy_url="https://api.openswarm.com",
|
|
)
|
|
# Free-trial carries the /free segment so the same SDK lands on the metered route.
|
|
assert proxy_auth(free) == ("ftk", "https://api.openswarm.com/free")
|
|
|
|
|
|
def test_free_trial_resolves_to_a_bare_anthropic_id():
|
|
s = AppSettings(connection_mode="free-trial", free_trial_token="ftk")
|
|
mid = resolve_model_id_for_sdk("sonnet", s)
|
|
# The bug this fixes: without the free-trial branch this returns a cc/-prefixed
|
|
# id that 401s when no Claude subscription is connected.
|
|
assert "cc/" not in mid
|
|
assert mid.startswith("claude-")
|
|
|
|
|
|
def test_exhaustion_is_classified_and_not_retried():
|
|
assert _is_free_trial_exhausted(Exception("error type free_trial_exhausted"))
|
|
assert _is_free_trial_exhausted(Exception("You've used your free OpenSwarm runs"))
|
|
assert not _is_free_trial_exhausted(Exception("overloaded, try again"))
|
|
# Must NOT look transient, or the agent loop would retry a spent trial forever.
|
|
assert not _is_transient_capacity_error(Exception("free_trial_exhausted"))
|
|
|
|
|
|
def test_generic_cli_failure_uses_sdk_system_events_for_rate_limits():
|
|
system_event_tail = (
|
|
'{"subtype":"api_retry","data":{"error_status":429,'
|
|
'"error":"rate_limit","max_retries":10}}'
|
|
)
|
|
assert _is_transient_capacity_error(
|
|
Exception("Command failed with exit code 1"),
|
|
extra_text=system_event_tail,
|
|
)
|
|
|
|
|
|
def test_has_own_model_never_shadows_a_real_provider():
|
|
assert not _has_own_model(AppSettings(connection_mode="free-trial", free_trial_token="x"))
|
|
assert not _has_own_model(AppSettings())
|
|
assert _has_own_model(AppSettings(anthropic_api_key="sk-ant-x"))
|
|
assert _has_own_model(
|
|
AppSettings(connection_mode="openswarm-pro", openswarm_bearer_token="b")
|
|
)
|