open-swe/agent/utils/deferred_model.py
seahaven-openswe[bot] 0546085672
Some checks are pending
CI / Lint (push) Waiting to run
CI / Format check (push) Waiting to run
CI / Unit tests (push) Waiting to run
CI / Playwright E2E (push) Waiting to run
CI / Docker build smoke (push) Waiting to run
CI / Triage ledger up to date (push) Waiting to run
CI / ui bun.lock in sync (push) Waiting to run
feat: port durable dispatch hardening and startup latency improvements (#160)
* feat: port plan-review & workflow-approval UX (#135)

Port six upstream commits onto dev:

- c03a6be7 (already ported): keep plan guidance high-level
- 546042a4: add workflow approval UI with diff preview, approval URLs,
  web review links, and polling for approval status during active runs
- 216cf181: remove workflow token elevation; approved pushes pass
  through directly without proxy token rewriting
- 3dbc0282: preserve plan redirects after login by accepting relative
  same-origin redirect_to values and rejecting blocked paths
- bb104d93: submit plan comments with cmd+enter
- 90cb6caa: terse Slack replies, shared content via save_plan outside
  plan mode (PLAN_STATUS_SHARED), reject shared-content mutations

Refs: #135

* feat: port durable dispatch hardening and startup latency improvements

Port five upstream PRs onto dev:

- #1621 / #1658: durable dispatch with loopback webhook defense,
  create_durable_run helper, _config_with_prepare_run_id, degradation
  to None for relative/loopback completion webhook URLs
- #1696: run-level completion webhook deduplication (replace
  claim-then-post with post-then-flag per run_id), DeferredErrorModel
  for graph-factory resilience, ToolRetryMiddleware for task subagents,
  TimeoutWrapupMiddleware for all three graphs
- #1697: lazy-load __init__.py for agent.middleware, agent.tools,
  agent.dashboard (PEP 562); defer heavy imports (exa_py in web_search,
  agent.webapp in request_pr_review, deepagents in sandbox.py); add
  ttl_cache.py with stale-while-revalidate for tool loaders

Refs: #137

* fix: restore login page render and clear CI lint/format

The plan-review port removed the authRedirectUrl import from login.tsx
but left its call site, crashing the login page at runtime (blank page,
no 'Sign in to open-swe'). Pass the relative path straight to loginUrl,
matching the plan route and the backend relative-redirect handling.

Also drop an unused os import in the guard test and reformat
workflow_push_guard.py to satisfy ruff.

* fix: restore RepairOrphaned middleware export and repoint model fake to deferred_model boundary

* fix: restore RepairOrphanedToolCallsMiddleware, fix E2E model-fake patch, drop dead ttl_cache

- Re-add RepairOrphanedToolCallsMiddleware to the lazy middleware __init__
  (_MIDDLEWARE_MODULES, __all__, TYPE_CHECKING) so agent.reviewer can import it.
- Reroute E2E model patching to deferred_model.make_model so make_model_or_defer
  (used by all three graph factories) returns the scripted fake instead of
  building a real model with fake credentials.
- Drop unused agent/utils/ttl_cache.py — no agent module imports it.
- Fix import ordering in agent/reviewer.py and agent/analyzer.py (ruff I001).
- Format tests/test_dispatch.py.

* fix: claim-then-post run-level failure dedup; stop permanent suppression

---------

Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com>
Co-authored-by: Adam Moussa <adam@seahavenind.com>
2026-07-09 17:11:25 -04:00

57 lines
1.9 KiB
Python

from __future__ import annotations
import logging
from typing import Any
from langchain_core.language_models import BaseChatModel
from .model import make_model
logger = logging.getLogger(__name__)
class DeferredErrorModel(BaseChatModel):
"""Model placeholder that raises a stored setup error on first invocation."""
error_message: str
model_id: str | None = None
@property
def _llm_type(self) -> str:
return "deferred-error"
def _get_ls_params(self, stop: Any = None, **kwargs: Any) -> dict[str, Any]:
params = super()._get_ls_params(stop=stop, **kwargs)
if self.model_id:
params["ls_model_name"] = self.model_id
if ":" in self.model_id:
params["ls_provider"] = self.model_id.split(":", 1)[0]
return params
def bind_tools(self, tools: Any, **kwargs: Any) -> DeferredErrorModel:
return self
def _generate(self, messages: Any, stop: Any = None, run_manager: Any = None, **kwargs: Any):
raise ValueError(self.error_message)
def make_deferred_error_model(
error: BaseException, *, model_id: str | None = None
) -> BaseChatModel:
return DeferredErrorModel(error_message=f"{type(error).__name__}: {error}", model_id=model_id)
def make_model_or_defer(model_id: str, **kwargs: Any) -> BaseChatModel:
"""Call ``make_model`` and wrap any setup error in a ``DeferredErrorModel``.
This lets graph factories pass a model placeholder into the agent graph on
startup without crashing the process. The error is raised later inside the
agent run when the model is first invoked, so the caller can recover
gracefully (e.g. via fallback middleware or by surfacing the error to the
user).
"""
try:
return make_model(model_id, **kwargs)
except Exception as e: # noqa: BLE001
logger.warning("Deferring model setup failure for %s", model_id, exc_info=True)
return make_deferred_error_model(e, model_id=model_id)