From e3c7c03c92bf920855321d00c5c07f4a16c59172 Mon Sep 17 00:00:00 2001 From: "seahaven-openswe[bot]" <296972425+seahaven-openswe[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:28:21 -0400 Subject: [PATCH 1/4] feat: port model-fallback resilience from upstream (#1694, #1695) (#161) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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. * feat: port model-fallback resilience from upstream (#1694, #1695) - Add httpx.TransportError to the transient-exception set so incomplete chunked reads on streamed responses trigger a fallback instead of cancelling the run (#1694 / c9f6dd86). - Rewrite fallback to alternate primary/fallback with exponential backoff instead of a single failover, so the agent survives multi-minute gateway outages spanning both providers (#1695 / c9a9a7cd). - Default backoff schedule (0, 5, 15, 30, 45) reaches past the gateway's ~30s recovery window; jittered ±25%. - On exhaustion, surface a terminal AIMessage explaining the outage instead of crashing — progress is checkpointed so the user can retrigger to continue. - Preserve fork conventions: Bedrock ClientError retryability check, sync wrap_model_call (using time.sleep instead of asyncio.sleep), and existing access-error surfacing for Anthropic, OpenAI, and Bedrock (botocore) provider errors. - Update triage ledger (c9f6dd86, c9a9a7cd → landed) and re-render triage.md. Refs: #139 * fix: align workflow-push-guard tests with dev's transient-elevation impl The dev merge auto-combined dev's elevation tests with the stale passthrough tests inherited from the durable-dispatch branch; the passthrough tests contradict dev's restored _run_with_workflow_token impl. Take dev's test file. * fix: drop dead ttl_cache module; make fallback backoff jitter two-sided ttl_cache.py was re-introduced via the dev merge but dev/#160 deliberately removed it as dead code (no agent importer). Remove it to match dev. Also make _jittered_delay symmetric (±25%) to match its docstring. * chore(upstream-sync): triage 4 new upstream commits (#1708-#1713) Synced ledger to upstream/main (71e3b818). New rows all deferred: - #1708 add GPT-5.6 OpenAI models (FLAG-HUMAN: fork picker is Bedrock/Fireworks-only) - #1709 stale admin model defaults after upgrades - #1710 bump langchain-fireworks 1.4.4 - #1713 align reviewer eval with published findings --------- Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com> Co-authored-by: Adam Moussa --- agent/middleware/model_fallback.py | 198 +++++++++++++++++++----- docs/upstream-sync/triage.jsonl | 10 +- docs/upstream-sync/triage.md | 10 +- tests/test_model_fallback_middleware.py | 78 +++++++++- 4 files changed, 246 insertions(+), 50 deletions(-) diff --git a/agent/middleware/model_fallback.py b/agent/middleware/model_fallback.py index 41a2cb5c..0d3d059b 100644 --- a/agent/middleware/model_fallback.py +++ b/agent/middleware/model_fallback.py @@ -1,22 +1,40 @@ -"""Middleware that falls back to a secondary model when the primary fails transiently. +"""Middleware that retries model calls across a primary and fallback provider. -Wraps the model call. When the primary model raises a transient provider error -(5xx, 429, connection/timeout), the same request is retried once against the -configured fallback model. The fallback is bound to tools by the agent factory -on the second call, so swapping ``request.model`` is sufficient. +Wraps the model call. When a model raises a transient provider error (5xx, +429, connection/timeout), the request is retried, alternating between the +primary and the configured fallback model with exponential backoff between +attempts. The fallback is bound to tools by the agent factory on each call, +so swapping ``request.model`` is sufficient. + +Why alternate with backoff instead of failing over once: both providers can +be routed through the same LLM Gateway, so a gateway outage takes out the +"cross-provider" fallback too. A single immediate failover cannot ride out +even a short shared outage (the gateway's 502 page literally says "try again +in 30 seconds"), and an unprotected fallback call crashes the whole run. +Alternating with a backoff schedule that reaches past 30s lets a long-running +agent run survive multi-minute provider or gateway blips. Bidirectional: if the primary is Anthropic the fallback is typically OpenAI, and vice versa. The middleware itself is provider-agnostic — it inspects the -exception type/status code to decide whether to fall over. +exception type/status code to decide whether an attempt is retryable. + +If every attempt fails, the middleware either raises the last error or (by +default) returns a terminal ``AIMessage`` explaining the outage, so the run +ends with a visible message in Slack/GitHub instead of an abrupt crash. The +turn's progress is checkpointed, so the user can retrigger to continue. """ from __future__ import annotations +import asyncio import logging -from collections.abc import Awaitable, Callable +import random +import time +from collections.abc import Awaitable, Callable, Sequence from typing import Any import anthropic +import httpx import openai from botocore.exceptions import ClientError from langchain.agents.middleware import AgentMiddleware @@ -37,6 +55,7 @@ _TRANSIENT_EXCEPTIONS: tuple[type[BaseException], ...] = ( openai.APITimeoutError, openai.RateLimitError, openai.InternalServerError, + httpx.TransportError, ) @@ -48,6 +67,23 @@ _RETRYABLE_BEDROCK_ERROR_CODES = { } +# Seconds slept before each retry attempt (attempt 0 is the initial call). +# The first failover is immediate: a provider-specific outage should not delay +# the cross-provider retry. Later delays grow past the ~30s the gateway's 502 +# page asks for. Each attempt additionally benefits from the SDK's own +# ``max_retries`` backoff, so worst-case wall time before giving up is a few +# minutes — acceptable for a long-running agent, far better than crashing. +DEFAULT_BACKOFF_SCHEDULE: tuple[float, ...] = (0.0, 5.0, 15.0, 30.0, 45.0) + +MODEL_OUTAGE_MESSAGE = ( + "I wasn't able to reach the language model providers after several retries " + "(both the primary and fallback models returned transient errors, e.g. " + "502/503/overloaded). This is a temporary provider or gateway outage, not a " + "problem with your task. My progress so far has been saved — please retrigger " + "the run in a few minutes to continue." +) + + def _should_fallback(exc: BaseException) -> bool: if isinstance(exc, _TRANSIENT_EXCEPTIONS): return True @@ -117,52 +153,132 @@ def _provider_access_error_message(exc: BaseException) -> str | None: class ModelFallbackMiddleware(AgentMiddleware): - """Retry the model call against a fallback provider on transient errors.""" + """Retry the model call across primary and fallback providers on transient errors. - def __init__(self, fallback_model: BaseChatModel) -> None: + Args: + fallback_model: Cross-provider model used on odd-numbered attempts. + backoff_schedule: Seconds slept before each retry. ``len(schedule) + 1`` + is the total number of attempts. Delays get ±25% jitter. + surface_outage_message: When all attempts fail, return a terminal + ``AIMessage`` describing the outage instead of raising, so the run + ends gracefully with a user-visible message rather than a crash. + Set to ``False`` to re-raise the last error (e.g. if platform-level + alerting keys off failed runs). + """ + + def __init__( + self, + fallback_model: BaseChatModel, + *, + backoff_schedule: Sequence[float] = DEFAULT_BACKOFF_SCHEDULE, + surface_outage_message: bool = True, + ) -> None: super().__init__() self._fallback_model = fallback_model + self._backoff_schedule = tuple(backoff_schedule) + self._surface_outage_message = surface_outage_message + + def _fallback_name(self) -> str: + return ( + getattr(self._fallback_model, "model_name", None) + or getattr(self._fallback_model, "model", None) + or "fallback" + ) + + def _jittered_delay(self, delay: float) -> float: + return delay + random.uniform(-delay * 0.25, delay * 0.25) if delay > 0 else 0.0 + + def _log_retry( + self, exc_type: str, use_fallback: bool, attempt: int, total: int, delay: float + ) -> None: + logger.warning( + "Model call failed transiently (%s) on %s model " + "(attempt %d/%d); retrying %s model in %.1fs", + exc_type, + "fallback" if use_fallback else "primary", + attempt + 1, + total, + "primary" if use_fallback else f"fallback ({self._fallback_name()})", + delay, + ) + + def _resolve_request(self, request: ModelRequest, use_fallback: bool) -> ModelRequest: + return request.override(model=self._fallback_model) if use_fallback else request def wrap_model_call( self, request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse], ) -> ModelCallResult: - try: - return handler(request) - except Exception as exc: - access_error_message = _provider_access_error_message(exc) - if access_error_message is not None: - logger.warning("Model access error surfaced to user: %s", type(exc).__name__) - return AIMessage(content=access_error_message) - if not _should_fallback(exc): - raise - logger.warning( - "Primary model failed (%s); falling back to %s", - type(exc).__name__, - getattr(self._fallback_model, "model_name", None) - or getattr(self._fallback_model, "model", "fallback"), - ) - return handler(request.override(model=self._fallback_model)) + total_attempts = len(self._backoff_schedule) + 1 + last_exc: BaseException | None = None + + for attempt in range(total_attempts): + use_fallback = attempt % 2 == 1 + attempt_request = self._resolve_request(request, use_fallback) + try: + return handler(attempt_request) + except Exception as exc: + access_error_message = _provider_access_error_message(exc) + if access_error_message is not None: + logger.warning("Model access error surfaced to user: %s", type(exc).__name__) + return AIMessage(content=access_error_message) + if not _should_fallback(exc): + raise + last_exc = exc + if attempt + 1 >= total_attempts: + break + delay = self._jittered_delay(self._backoff_schedule[attempt]) + self._log_retry(type(exc).__name__, use_fallback, attempt, total_attempts, delay) + if delay > 0: + time.sleep(delay) + + assert last_exc is not None + logger.error( + "Model call failed after %d attempts across primary and fallback (%s): %s", + total_attempts, + self._fallback_name(), + last_exc, + ) + if self._surface_outage_message: + return AIMessage(content=MODEL_OUTAGE_MESSAGE) + raise last_exc async def awrap_model_call( self, request: ModelRequest, handler: Callable[[ModelRequest], Awaitable[ModelResponse]], ) -> Any: - try: - return await handler(request) - except Exception as exc: - access_error_message = _provider_access_error_message(exc) - if access_error_message is not None: - logger.warning("Model access error surfaced to user: %s", type(exc).__name__) - return AIMessage(content=access_error_message) - if not _should_fallback(exc): - raise - logger.warning( - "Primary model failed (%s); falling back to %s", - type(exc).__name__, - getattr(self._fallback_model, "model_name", None) - or getattr(self._fallback_model, "model", "fallback"), - ) - return await handler(request.override(model=self._fallback_model)) + total_attempts = len(self._backoff_schedule) + 1 + last_exc: BaseException | None = None + + for attempt in range(total_attempts): + use_fallback = attempt % 2 == 1 + attempt_request = self._resolve_request(request, use_fallback) + try: + return await handler(attempt_request) + except Exception as exc: + access_error_message = _provider_access_error_message(exc) + if access_error_message is not None: + logger.warning("Model access error surfaced to user: %s", type(exc).__name__) + return AIMessage(content=access_error_message) + if not _should_fallback(exc): + raise + last_exc = exc + if attempt + 1 >= total_attempts: + break + delay = self._jittered_delay(self._backoff_schedule[attempt]) + self._log_retry(type(exc).__name__, use_fallback, attempt, total_attempts, delay) + if delay > 0: + await asyncio.sleep(delay) + + assert last_exc is not None + logger.error( + "Model call failed after %d attempts across primary and fallback (%s): %s", + total_attempts, + self._fallback_name(), + last_exc, + ) + if self._surface_outage_message: + return AIMessage(content=MODEL_OUTAGE_MESSAGE) + raise last_exc diff --git a/docs/upstream-sync/triage.jsonl b/docs/upstream-sync/triage.jsonl index 03d6f5e8..a19f247a 100644 --- a/docs/upstream-sync/triage.jsonl +++ b/docs/upstream-sync/triage.jsonl @@ -1,4 +1,4 @@ -{"_meta": {"last_synced": "fd2541ce", "last_synced_date": "2026-07-09"}} +{"_meta": {"last_synced": "71e3b818", "last_synced_date": "2026-07-09"}} {"sha": "0b76afdc", "pr": 1653, "subject": "reviews block agenda, sticky headers, diff scroll", "disposition": "landed", "reason": "", "branch": "cherry-pick-upstream", "local_sha": null, "updated": "2026-07-02T00:00:00Z"} {"sha": "7530653b", "pr": 1655, "subject": "ResizeObserver settle for review scroll-to", "disposition": "landed", "reason": "", "branch": "cherry-pick-upstream", "local_sha": null, "updated": "2026-07-02T00:00:00Z"} {"sha": "23bd4a63", "pr": 1660, "subject": "top padding to sticky review block header", "disposition": "landed", "reason": "", "branch": "cherry-pick-upstream", "local_sha": null, "updated": "2026-07-02T00:00:00Z"} @@ -62,8 +62,8 @@ {"sha": "13b40113", "pr": 1666, "subject": "chore(deps): bump cryptography from 48.0.1 to 49.0.0 in the major group (#1666)", "disposition": "wont-merge", "reason": "already in dev — same 48->49 cryptography bump dev did via #105", "branch": "", "local_sha": null, "updated": "2026-07-08T20:14:42Z"} {"sha": "fbc6de85", "pr": 1667, "subject": "chore(deps): bump fireworks-ai from 1.2.0a75 to 1.2.0a86 (#1667)", "disposition": "deferred", "reason": "conflicts on pick — dev diverged to fireworks-ai a85; a86 still wanted, needs manual bump + uv lock", "branch": "deps", "local_sha": null, "updated": "2026-07-08T23:17:35Z"} {"sha": "290d0fee", "pr": 1669, "subject": "chore(deps): update langgraph-cli[inmem] requirement (#1669)", "disposition": "wont-merge", "reason": "already in dev — langgraph-cli[inmem] at 0.4.30", "branch": "", "local_sha": null, "updated": "2026-07-08T20:14:42Z"} -{"sha": "c9f6dd86", "pr": 1694, "subject": "fix: fall back on model stream transport errors (#1694)", "disposition": "deferred", "reason": "adds httpx.TransportError to transient set; small conflict w/ dev's diverged Bedrock fallback", "branch": "model-fallback", "local_sha": null, "updated": "2026-07-08T20:14:42Z"} -{"sha": "c9a9a7cd", "pr": 1695, "subject": "fix: retry model fallback exhaustion (#1695)", "disposition": "deferred", "reason": "alternating retry+backoff rewrite; reconcile by hand w/ dev's Bedrock + sync wrap path", "branch": "model-fallback", "local_sha": null, "updated": "2026-07-08T20:14:42Z"} +{"sha": "c9f6dd86", "pr": 1694, "subject": "fix: fall back on model stream transport errors (#1694)", "disposition": "landed", "reason": "adds httpx.TransportError to transient set; small conflict w/ dev's diverged Bedrock fallback", "branch": "model-fallback", "local_sha": null, "updated": "2026-07-09T18:59:20Z"} +{"sha": "c9a9a7cd", "pr": 1695, "subject": "fix: retry model fallback exhaustion (#1695)", "disposition": "landed", "reason": "alternating retry+backoff rewrite; reconcile by hand w/ dev's Bedrock + sync wrap path", "branch": "model-fallback", "local_sha": null, "updated": "2026-07-09T18:59:20Z"} {"sha": "e5dbc788", "pr": 1696, "subject": "fix: Harden durable agent runs (#1696)", "disposition": "landed", "reason": "large durable-run hardening; 3 new modules dev lacks; rewrites fork dispatch/completion", "branch": "durable-dispatch", "local_sha": null, "updated": "2026-07-09T17:22:53Z"} {"sha": "52fe2916", "pr": 1698, "subject": "feat: add PR review link route (#1698)", "disposition": "landed", "reason": "cherry-picked (-x) in #127 (PR review link route)", "branch": "reviewer-misc", "local_sha": null, "updated": "2026-07-08T22:58:21Z"} {"sha": "5f7f5fbd", "pr": 1697, "subject": "fix: Reduce graph import and loader startup latency (#1697)", "disposition": "landed", "reason": "import-hygiene refactor; cross-cutting, references many deferred upstream-only modules", "branch": "durable-dispatch", "local_sha": null, "updated": "2026-07-09T17:22:53Z"} @@ -83,3 +83,7 @@ {"sha": "9cd7e464", "pr": 1700, "subject": "Fix workflow approval visibility (#1700)", "disposition": "wont-merge", "reason": "superseded — dev's list_workflow_approvals_for_thread already enforces owner-only 403", "branch": "", "local_sha": null, "updated": "2026-07-08T20:14:42Z"} {"sha": "22e024cb", "pr": 1704, "subject": "fix: link issue PRs and prompt repo conventions (#1704)", "disposition": "deferred", "reason": "issue/PR linking + repo-convention prompt; clean but prompt-conflict risk vs #113", "branch": "webhook-issue-linking", "local_sha": null, "updated": "2026-07-08T20:14:42Z"} {"sha": "fd2541ce", "pr": 1705, "subject": "fix: drop orphaned function_call items with stale OpenAI reasoning (#1705)", "disposition": "wont-merge", "reason": "N/A — edits sanitize_openai_responses.py which dev deleted in the Bedrock/Fireworks migration (#62)", "branch": "", "local_sha": null, "updated": "2026-07-08T20:14:42Z"} +{"sha": "27b0ddeb", "pr": 1708, "subject": "feat: add GPT-5.6 OpenAI models (#1708)", "disposition": "deferred", "reason": "FLAG-HUMAN: adds OpenAI GPT-5.6 to the model picker; fork's picker is Bedrock/Fireworks-only — needs a product decision before adopting OpenAI models. Gateway (#155) can route OpenAI if adopted.", "branch": "model-picker", "local_sha": null, "updated": "2026-07-09T22:20:06Z"} +{"sha": "62e0ca2d", "pr": 1709, "subject": "fix: stale admin model defaults after model upgrades (#1709)", "disposition": "deferred", "reason": "stale admin model-default cleanup in team_settings after model upgrades; applies to fork's default-model resolution.", "branch": "model-picker", "local_sha": null, "updated": "2026-07-09T22:20:06Z"} +{"sha": "138ab9ec", "pr": 1710, "subject": "fix: bump langchain-fireworks to 1.4.4 (#1710)", "disposition": "deferred", "reason": "langchain-fireworks 1.4.4 bump; fork uses Fireworks as a primary provider — adopt with a lockfile refresh.", "branch": "deps", "local_sha": null, "updated": "2026-07-09T22:20:07Z"} +{"sha": "71e3b818", "pr": 1713, "subject": "fix: align reviewer eval with published findings (#1713)", "disposition": "deferred", "reason": "reviewer-eval/judge alignment; touches reviewer.py + add_finding/publish_review which are fork-diverged — reconcile against fork's reviewer before porting.", "branch": "reviewer-eval", "local_sha": null, "updated": "2026-07-09T22:20:07Z"} diff --git a/docs/upstream-sync/triage.md b/docs/upstream-sync/triage.md index 078f4c5f..8306dabb 100644 --- a/docs/upstream-sync/triage.md +++ b/docs/upstream-sync/triage.md @@ -6,7 +6,7 @@ Commits on `upstream/main` (langchain-ai/open-swe) not yet in `dev`, and the dec Rows key on the **upstream SHA** (stable across local cherry-picks). Deferred rows are provisional — re-inspect before picking. See the fork-maintenance runbook in `CLAUDE.md`. -**Last synced `upstream/main`:** `fd2541ce` (2026-07-09) +**Last synced `upstream/main`:** `71e3b818` (2026-07-09) | sha | pr | subject | decision | why | branch | |---|---|---|---|---|---| @@ -46,6 +46,8 @@ Rows key on the **upstream SHA** (stable across local cherry-picks). Deferred ro | `4f913198` | #1647 | widen split review diffs | Landed | already present in dev; empty pick confirmed in #127 | reviewer-misc | | `20f63e8c` | #1646 | install missing deps before verification | Landed | already in dev via #81 (upstream-sync); ledger was stale | prompt-tweaks | | `2f237b53` | #1626 | fall back to vision model for image threads | Landed | ported (adapted to Bedrock/Fireworks vision) in #128 | gateway-routing | +| `c9f6dd86` | #1694 | fix: fall back on model stream transport errors (#1694) | Landed | adds httpx.TransportError to transient set; small conflict w/ dev's diverged Bedrock fallback | model-fallback | +| `c9a9a7cd` | #1695 | fix: retry model fallback exhaustion (#1695) | Landed | alternating retry+backoff rewrite; reconcile by hand w/ dev's Bedrock + sync wrap path | model-fallback | | `e5dbc788` | #1696 | fix: Harden durable agent runs (#1696) | Landed | large durable-run hardening; 3 new modules dev lacks; rewrites fork dispatch/completion | durable-dispatch | | `52fe2916` | #1698 | feat: add PR review link route (#1698) | Landed | cherry-picked (-x) in #127 (PR review link route) | reviewer-misc | | `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 | @@ -85,8 +87,6 @@ Rows key on the **upstream SHA** (stable across local cherry-picks). Deferred ro | `4f8bc2dd` | #1692 | refactor: simplify open-swe agent sandbox lifecycle (#1692) | Deferred | FLAG-HUMAN: structural rewrite of ensure_sandbox_for_thread (drops __creating__ 4-case sentinel) | sandbox-refactor | | `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 | -| `c9f6dd86` | #1694 | fix: fall back on model stream transport errors (#1694) | Deferred | adds httpx.TransportError to transient set; small conflict w/ dev's diverged Bedrock fallback | model-fallback | -| `c9a9a7cd` | #1695 | fix: retry model fallback exhaustion (#1695) | Deferred | alternating retry+backoff rewrite; reconcile by hand w/ dev's Bedrock + sync wrap path | model-fallback | | `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 | @@ -94,5 +94,9 @@ Rows key on the **upstream SHA** (stable across local cherry-picks). Deferred ro | `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 | | `f53caff1` | #1701 | fix: fall back to core GitHub App scope when optional grants missing (#1701) | Deferred | FLAG-HUMAN: GitHub-App permission-ladder degrade (auth surface); heavy conflict on diverged github_app.py/_resolve_proxy_token | github-app-scope | | `22e024cb` | #1704 | fix: link issue PRs and prompt repo conventions (#1704) | Deferred | issue/PR linking + repo-convention prompt; clean but prompt-conflict risk vs #113 | webhook-issue-linking | +| `27b0ddeb` | #1708 | feat: add GPT-5.6 OpenAI models (#1708) | Deferred | FLAG-HUMAN: adds OpenAI GPT-5.6 to the model picker; fork's picker is Bedrock/Fireworks-only — needs a product decision before adopting OpenAI models. Gateway (#155) can route OpenAI if adopted. | model-picker | +| `62e0ca2d` | #1709 | fix: stale admin model defaults after model upgrades (#1709) | Deferred | stale admin model-default cleanup in team_settings after model upgrades; applies to fork's default-model resolution. | model-picker | +| `138ab9ec` | #1710 | fix: bump langchain-fireworks to 1.4.4 (#1710) | Deferred | langchain-fireworks 1.4.4 bump; fork uses Fireworks as a primary provider — adopt with a lockfile refresh. | deps | +| `71e3b818` | #1713 | fix: align reviewer eval with published findings (#1713) | Deferred | reviewer-eval/judge alignment; touches reviewer.py + add_finding/publish_review which are fork-diverged — reconcile against fork's reviewer before porting. | reviewer-eval | _Maintenance: after a `git sync`, add new `dev..upstream/main` SHAs as **Untriaged** (edit `triage.jsonl`) and bump "Last synced". A successful `git cherry-pick -x` auto-moves the row to **Landed** via the `post-commit` journal + `make triage-reconcile`._ diff --git a/tests/test_model_fallback_middleware.py b/tests/test_model_fallback_middleware.py index f60bf0f7..7017ee89 100644 --- a/tests/test_model_fallback_middleware.py +++ b/tests/test_model_fallback_middleware.py @@ -73,6 +73,12 @@ class TestShouldFallback: exc = anthropic.BadRequestError("bad", response=response, body={}) assert _should_fallback(exc) is False + def test_httpx_remote_protocol_error_falls_back(self) -> None: + exc = httpx.RemoteProtocolError( + "peer closed connection without sending complete message body (incomplete chunked read)" + ) + assert _should_fallback(exc) is True + def test_value_error_does_not_fall_back(self) -> None: assert _should_fallback(ValueError("nope")) is False @@ -100,6 +106,31 @@ class TestModelFallbackMiddleware: request.override.assert_called_once_with(model=fallback_model) assert calls[1] is request.override.return_value + @pytest.mark.asyncio + async def test_async_falls_over_on_stream_transport_error(self) -> None: + fallback_model = MagicMock(name="fallback_model") + middleware = ModelFallbackMiddleware(fallback_model) + + calls: list[object] = [] + good_response = MagicMock(result=[AIMessage(content="ok from fallback")]) + + async def handler(req: object) -> object: + calls.append(req) + if len(calls) == 1: + raise httpx.RemoteProtocolError( + "peer closed connection without sending complete message body " + "(incomplete chunked read)" + ) + return good_response + + request = _make_request() + result = await middleware.awrap_model_call(request, handler) + + assert result is good_response + assert len(calls) == 2 + request.override.assert_called_once_with(model=fallback_model) + assert calls[1] is request.override.return_value + @pytest.mark.asyncio async def test_async_propagates_non_transient_error(self) -> None: middleware = ModelFallbackMiddleware(MagicMock()) @@ -128,9 +159,50 @@ class TestModelFallbackMiddleware: assert "data retention enabled" in result.text @pytest.mark.asyncio - async def test_async_does_not_double_fall_back(self) -> None: - """If the fallback also fails transiently, the error propagates.""" - middleware = ModelFallbackMiddleware(MagicMock()) + async def test_async_retries_primary_after_fallback_failure(self) -> None: + """If the fallback also fails transiently, retry the primary instead of crashing.""" + fallback_model = MagicMock(name="fallback_model") + middleware = ModelFallbackMiddleware(fallback_model, backoff_schedule=(0.0, 0.0, 0.0)) + calls: list[object] = [] + good_response = MagicMock(result=[AIMessage(content="ok from primary retry")]) + + async def handler(req: object) -> object: + calls.append(req) + if len(calls) <= 2: # primary fails, then fallback fails + raise _openai_5xx() + return good_response + + request = _make_request() + result = await middleware.awrap_model_call(request, handler) + + assert result is good_response + assert len(calls) == 3 + # Attempts alternate primary -> fallback -> primary. + assert calls[0] is request + assert calls[1] is request.override.return_value + assert calls[2] is request + + @pytest.mark.asyncio + async def test_async_exhaustion_returns_outage_message(self) -> None: + """After exhausting all attempts, the run ends with a visible message, not a crash.""" + middleware = ModelFallbackMiddleware(MagicMock(), backoff_schedule=(0.0, 0.0)) + calls: list[object] = [] + + async def handler(req: object) -> object: + calls.append(req) + raise _openai_5xx() + + result = await middleware.awrap_model_call(_make_request(), handler) + + assert len(calls) == 3 + assert isinstance(result, AIMessage) + assert "retrigger" in result.text + + @pytest.mark.asyncio + async def test_async_exhaustion_raises_when_message_disabled(self) -> None: + middleware = ModelFallbackMiddleware( + MagicMock(), backoff_schedule=(0.0,), surface_outage_message=False + ) calls: list[object] = [] async def handler(req: object) -> object: From efeb6b1245a44bb3c36ef367648fde4083be9e60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:35:23 -0400 Subject: [PATCH 2/4] chore(deps): bump fireworks-ai from 1.2.0a85 to 1.2.0a88 (#158) Bumps [fireworks-ai](https://fireworks.ai) from 1.2.0a85 to 1.2.0a88. --- updated-dependencies: - dependency-name: fireworks-ai dependency-version: 1.2.0a88 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 22367e7a..bd4804a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "langchain-openai>=1.3.3", "langchain-fireworks>=1.4.3", # langchain-fireworks 1.4.2 pins a pre-release fireworks-ai; opt in explicitly so uv resolves it. - "fireworks-ai>=1.2.0a85", + "fireworks-ai>=1.2.0a88", "langchain-daytona>=0.0.7", "langchain-modal>=0.0.5", "langchain-runloop>=0.0.6", diff --git a/uv.lock b/uv.lock index 47f3c9f3..4d1468ee 100644 --- a/uv.lock +++ b/uv.lock @@ -781,7 +781,7 @@ wheels = [ [[package]] name = "fireworks-ai" -version = "1.2.0a85" +version = "1.2.0a88" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -793,9 +793,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/94/fb75cf9c769a09de31e550ba99a279e5f83a05cfcf49cd0cbb8ad586ab35/fireworks_ai-1.2.0a85.tar.gz", hash = "sha256:33abbeddd9a6bd9993bca6cb9e16afe9799b2cd674d6b7c04a601cd4e412f2a7", size = 448217, upload-time = "2026-06-26T08:06:57.286Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/17/f48738655a14d93c98ac34d966caeec7de69843b1e4492e748cc78622169/fireworks_ai-1.2.0a88.tar.gz", hash = "sha256:565db96c0804d9a7901eedad06ae0b656bbbc7514bdf94a1408afba46b40737c", size = 456358, upload-time = "2026-07-09T05:52:58.742Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/5b/0b10d0afb633b45f234f6728cc680851f136b9f75903b4387049537a6cd6/fireworks_ai-1.2.0a85-py3-none-any.whl", hash = "sha256:80a4b13bda8e28ca9064c8339d0afe47cbbdb7c61a88eeebc173ccb58129f19a", size = 528844, upload-time = "2026-06-26T08:06:55.927Z" }, + { url = "https://files.pythonhosted.org/packages/53/8d/ecbdaf71f79d5a29075837300609fc6d0a802025acd4c648e2b6e8f19fc8/fireworks_ai-1.2.0a88-py3-none-any.whl", hash = "sha256:b532ac0a04e9f5c112297732c3f1238d61641b34a6f5843c87f24c879e842d24", size = 537817, upload-time = "2026-07-09T05:52:57.357Z" }, ] [[package]] @@ -2221,7 +2221,7 @@ requires-dist = [ { name = "deepagents", specifier = "==0.6.12" }, { name = "exa-py", specifier = ">=2.16.0" }, { name = "fastapi", specifier = ">=0.139.0" }, - { name = "fireworks-ai", specifier = ">=1.2.0a85" }, + { name = "fireworks-ai", specifier = ">=1.2.0a88" }, { name = "httpx", specifier = ">=0.28.1" }, { name = "langchain", specifier = ">=1.3.9" }, { name = "langchain-anthropic", specifier = ">=1.4.6" }, From ddae41542c3f78188d9ca589e5545e42fc014069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 22:36:35 +0000 Subject: [PATCH 3/4] chore(deps): bump the minor-and-patch group with 7 updates (#157) Bumps the minor-and-patch group with 7 updates: | Package | From | To | | --- | --- | --- | | [uvicorn](https://github.com/Kludex/uvicorn) | `0.49.0` | `0.51.0` | | [langchain](https://github.com/langchain-ai/langchain) | `1.3.11` | `1.3.12` | | [langgraph](https://github.com/langchain-ai/langgraph) | `1.2.7` | `1.2.8` | | [langchain-aws](https://github.com/langchain-ai/langchain-aws) | `1.6.1` | `1.6.2` | | [langsmith](https://github.com/langchain-ai/langsmith-sdk) | `0.9.7` | `0.10.0` | | [langchain-openai](https://github.com/langchain-ai/langchain) | `1.3.3` | `1.3.4` | | [langchain-google-genai](https://github.com/langchain-ai/langchain-google) | `4.2.6` | `4.2.7` | Updates `uvicorn` from 0.49.0 to 0.51.0 - [Release notes](https://github.com/Kludex/uvicorn/releases) - [Changelog](https://github.com/Kludex/uvicorn/blob/main/docs/release-notes.md) - [Commits](https://github.com/Kludex/uvicorn/compare/0.49.0...0.51.0) Updates `langchain` from 1.3.11 to 1.3.12 - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](https://github.com/langchain-ai/langchain/compare/langchain==1.3.11...langchain==1.3.12) Updates `langgraph` from 1.2.7 to 1.2.8 - [Release notes](https://github.com/langchain-ai/langgraph/releases) - [Commits](https://github.com/langchain-ai/langgraph/compare/1.2.7...1.2.8) Updates `langchain-aws` from 1.6.1 to 1.6.2 - [Release notes](https://github.com/langchain-ai/langchain-aws/releases) - [Commits](https://github.com/langchain-ai/langchain-aws/compare/langchain-aws==1.6.1...langchain-aws==1.6.2) Updates `langsmith` from 0.9.7 to 0.10.0 - [Release notes](https://github.com/langchain-ai/langsmith-sdk/releases) - [Commits](https://github.com/langchain-ai/langsmith-sdk/compare/v0.9.7...v0.10.0) Updates `langchain-openai` from 1.3.3 to 1.3.4 - [Release notes](https://github.com/langchain-ai/langchain/releases) - [Commits](https://github.com/langchain-ai/langchain/compare/langchain-openai==1.3.3...langchain-openai==1.3.4) Updates `langchain-google-genai` from 4.2.6 to 4.2.7 - [Release notes](https://github.com/langchain-ai/langchain-google/releases) - [Commits](https://github.com/langchain-ai/langchain-google/compare/libs/genai/v4.2.6...libs/genai/v4.2.7) --- updated-dependencies: - dependency-name: uvicorn dependency-version: 0.51.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: langchain dependency-version: 1.3.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: langgraph dependency-version: 1.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: langchain-aws dependency-version: 1.6.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: langsmith dependency-version: 0.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: langchain-openai dependency-version: 1.3.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: langchain-google-genai dependency-version: 4.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> --- pyproject.toml | 14 ++++++------ uv.lock | 62 +++++++++++++++++++++++++------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bd4804a6..4129fa72 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,19 +8,19 @@ license = { text = "MIT" } dependencies = [ "deepagents==0.6.12", "fastapi>=0.139.0", - "uvicorn>=0.49.0", + "uvicorn>=0.51.0", "httpx>=0.28.1", "PyJWT>=2.13.0", "cryptography>=49.0.0", "langgraph-sdk>=0.4.2", - "langchain>=1.3.9", - "langgraph>=1.1.10", + "langchain>=1.3.12", + "langgraph>=1.2.8", "markdownify>=1.2.3", "langchain-anthropic>=1.4.6", - "langchain-aws>=0.2.0", + "langchain-aws>=1.6.2", "langgraph-cli[inmem]>=0.4.30", - "langsmith==0.9.7", - "langchain-openai>=1.3.3", + "langsmith==0.10.0", + "langchain-openai>=1.3.4", "langchain-fireworks>=1.4.3", # langchain-fireworks 1.4.2 pins a pre-release fireworks-ai; opt in explicitly so uv resolves it. "fireworks-ai>=1.2.0a88", @@ -28,7 +28,7 @@ dependencies = [ "langchain-modal>=0.0.5", "langchain-runloop>=0.0.6", "exa-py>=2.16.0", - "langchain-google-genai>=4.2.4", + "langchain-google-genai>=4.2.7", "langchain-mcp-adapters>=0.3.0", ] diff --git a/uv.lock b/uv.lock index 4d1468ee..e02cea4c 100644 --- a/uv.lock +++ b/uv.lock @@ -1431,16 +1431,16 @@ wheels = [ [[package]] name = "langchain" -version = "1.3.11" +version = "1.3.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "langgraph" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a2/91a7197c604a3ce1b774b3c10dd114c3c745c6186a304fc2573b3f94d400/langchain-1.3.11.tar.gz", hash = "sha256:f3cf9cd4d2329b1a03eb8fd92b9d73e4e58a4d52570d67725fc77fbe0f104b32", size = 633374, upload-time = "2026-06-22T23:00:33.44Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/c0/8481c93a3899d7e05ac27f21a4e984dab613b72631f39fe01fe0100e3be0/langchain-1.3.12.tar.gz", hash = "sha256:3321f86824a0c0720004d28797d729a58b9bb8a0ac73c6ea2b856cac38117879", size = 642308, upload-time = "2026-07-08T22:38:12.466Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/a4/3a181967294f8876362cc4ba36840d50b8286fa23bb3f5e602b69eb3cb1e/langchain-1.3.11-py3-none-any.whl", hash = "sha256:7ae011f95a09b22feea1e8ae4e43f0b6164aebf4c61b8ad845b45f72ff3a90a2", size = 133639, upload-time = "2026-06-22T23:00:31.619Z" }, + { url = "https://files.pythonhosted.org/packages/b1/31/33ba60f3aaa88dc7b97f9c7680f431237466804bf6826efb75400dfd2e6c/langchain-1.3.12-py3-none-any.whl", hash = "sha256:19685eb9dce01ebd71b11281d5489453e7ab3d2caf61eacb2a76d15574246535", size = 136863, upload-time = "2026-07-08T22:38:11.003Z" }, ] [[package]] @@ -1459,7 +1459,7 @@ wheels = [ [[package]] name = "langchain-aws" -version = "1.6.1" +version = "1.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, @@ -1468,14 +1468,14 @@ dependencies = [ { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/c6/4065908dc2f113324d1ac31ecf5c907a23863f7aeaab7b9361efd7109563/langchain_aws-1.6.1.tar.gz", hash = "sha256:b5b054f48e2697fa1b96733e9de2accfdd1a4948d9b4e3712e8180c4585cfd2f", size = 538265, upload-time = "2026-06-25T19:02:23.364Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/59/04f8572e65581d032b2df2516c0059fa064e680f5caa5cabcd03d525092a/langchain_aws-1.6.2.tar.gz", hash = "sha256:df2c15b463c068031af40460dbea6efb7999832263ff6eaec6b99c5f6b291acd", size = 537522, upload-time = "2026-07-07T01:24:58.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/ab/d85b915c12394385459bb536202227d0fd81377929d71b16f177559ba074/langchain_aws-1.6.1-py3-none-any.whl", hash = "sha256:a121f687b36678239dd96ee9d0503a0b7d5fc4570b3af6d19983ef6ebfaf115e", size = 206315, upload-time = "2026-06-25T19:02:21.848Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ed/bb5211637276616e7b176f3ff08e9d0bf1ce61d1a00c99d26348d6236a3d/langchain_aws-1.6.2-py3-none-any.whl", hash = "sha256:4755c60ce88fff2b829d96027068b7930f335cd85da41fda061769439c288251", size = 206507, upload-time = "2026-07-07T01:24:56.557Z" }, ] [[package]] name = "langchain-core" -version = "1.4.8" +version = "1.4.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1488,9 +1488,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/12/e3/bea6d0080acf183332f24dcd74c208aee5857cf8f783c3fb0bd86027d8fb/langchain_core-1.4.8.tar.gz", hash = "sha256:5bf1f8411077c904182ad8f975943d36adcbf579c4e017b3a118b719229ebf9a", size = 957974, upload-time = "2026-06-18T19:39:23.636Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/b9/e937d0a90b26540bff07e7a7c64349f3b29c2dcc36257cd1cd3fdce17f2a/langchain_core-1.4.9.tar.gz", hash = "sha256:f8078901145bed0466755277500a5a22822a7b628808c4c0a28d4fc88895fcf2", size = 967294, upload-time = "2026-07-08T20:06:54.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/d6/bdf6f0481cc57ef300d6b1eb48cf1400c0409be715d6eb3cabadd1142a09/langchain_core-1.4.8-py3-none-any.whl", hash = "sha256:d84c28b05e3ba8d4271d0827aad5b592ccdaaf986e76768c23503f0a2045e8aa", size = 557416, upload-time = "2026-06-18T19:39:21.902Z" }, + { url = "https://files.pythonhosted.org/packages/84/70/ade2fada52772798ef815b6352b59e71b116aa0c32c3aef5be3dc2cbed12/langchain_core-1.4.9-py3-none-any.whl", hash = "sha256:28e3909e2a10cc81504952d795ac0a9e014c0018121ef89d48dd396fa09ec624", size = 558293, upload-time = "2026-07-08T20:06:52.382Z" }, ] [[package]] @@ -1524,7 +1524,7 @@ wheels = [ [[package]] name = "langchain-google-genai" -version = "4.2.6" +version = "4.2.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filetype" }, @@ -1532,9 +1532,9 @@ dependencies = [ { name = "langchain-core" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/08/68e4f90b273d1d8dc3a7a948e6535e60baac726adbb2a8341dcf17662f54/langchain_google_genai-4.2.6.tar.gz", hash = "sha256:653dc331e691ddd79784d9ff6a4082749e0f873394c6dc782c414eb4409850eb", size = 280074, upload-time = "2026-06-26T06:18:58.807Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/0c/bc60dabc362ca7c6ffe8c4bcc2f724c7e566b43eb230cee51419f88f784c/langchain_google_genai-4.2.7.tar.gz", hash = "sha256:03b1463ffe4d42435f43c7870467f2215f684bb46400d2543435d10157c80ac7", size = 281605, upload-time = "2026-07-06T13:51:58.724Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/3c/9a6b43bced0238f95640555e14423fe4a8268e3f4f536f67e26e79e633d4/langchain_google_genai-4.2.6-py3-none-any.whl", hash = "sha256:c40db0c2d033a5fb6db8e2cc3fb6d49c5678b89b337a64da095fb73ec9f72021", size = 70457, upload-time = "2026-06-26T06:18:57.781Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f9/d73d1e712591723aaddb7a7b1e94978cd2320c29acfe0d26b6169a2f26f0/langchain_google_genai-4.2.7-py3-none-any.whl", hash = "sha256:0d9c388d0e6c629718fca6abb19c6fdca728a9a7873d0324c1ec821288b5b571", size = 70702, upload-time = "2026-07-06T13:51:57.499Z" }, ] [[package]] @@ -1566,16 +1566,16 @@ wheels = [ [[package]] name = "langchain-openai" -version = "1.3.3" +version = "1.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "openai" }, { name = "tiktoken" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/f3/b38e052f943a75ba0e6762c0a50b6f1d6bcd52a9ce63386d8803c2ff506e/langchain_openai-1.3.3.tar.gz", hash = "sha256:143769bf943820b80db769e47ca8fd0aac08ed18714519333b044c4431e9aa67", size = 3256559, upload-time = "2026-06-22T22:54:05.445Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/fe/cfd11b9ebc54f7667e3c8f847f64246fad169ad998b8a08d7c3c9d9bccaf/langchain_openai-1.3.4.tar.gz", hash = "sha256:d888d5f39c2a8c3d0d8aa88f5cf50e58a8e7d242f3f15e39422add520eec8e31", size = 3258207, upload-time = "2026-07-08T22:59:50.651Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/4b/7520114de1ce36bb17cbc98d0fcda048a9f755bedaeb73b92137aeaaf1db/langchain_openai-1.3.3-py3-none-any.whl", hash = "sha256:e469659862c8aabba4f6653df973206e7be54f98cf2275c86be7f06b7abe20d7", size = 120437, upload-time = "2026-06-22T22:54:03.8Z" }, + { url = "https://files.pythonhosted.org/packages/51/6e/f8f47f2c6976a4520b5416eaeeaee5e6aa7dd906b39685dc1ad4ef6d1ba2/langchain_openai-1.3.4-py3-none-any.whl", hash = "sha256:3241b8392b29c1af233b902b7d9a84bfc5fe26ccb210a7febdfc3972af7e5771", size = 120529, upload-time = "2026-07-08T22:59:49.451Z" }, ] [[package]] @@ -1605,7 +1605,7 @@ wheels = [ [[package]] name = "langgraph" -version = "1.2.7" +version = "1.2.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, @@ -1615,9 +1615,9 @@ dependencies = [ { name = "pydantic" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/11/c3961537ac7577125aabba68effc33ecc3dc0962e2d287d734dbd61caad7/langgraph-1.2.7.tar.gz", hash = "sha256:dcdf5b441bf8c7c7c154e603b302c9dbfbfd2d11e1b7ae7d93a5aba979dc87bd", size = 721385, upload-time = "2026-06-30T01:24:12.528Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/ad/583fda4c69501390b989770a465ccd0bdab1c1612eba582c012002ddf9b6/langgraph-1.2.8.tar.gz", hash = "sha256:f79d3575f45b404899358976e4fac0294eb75f8df1bfe8cd11286be7539c4548", size = 722464, upload-time = "2026-07-06T20:40:19.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/5d/19de0c674cf2d8bc95b25cdb4c13ba3f4fbe0f35bf2224ef19cc46bbb4cd/langgraph-1.2.7-py3-none-any.whl", hash = "sha256:249349a5d6a32cb0aa2304a8fb6529bd0cf56db8780d21db9e086e5383668f89", size = 246930, upload-time = "2026-06-30T01:24:11.15Z" }, + { url = "https://files.pythonhosted.org/packages/36/49/b958a9963606807e5a20cc75fced14aa77c5cbcc470d5bf8ae13277cd298/langgraph-1.2.8-py3-none-any.whl", hash = "sha256:aa8de1d4df44162353d117589ae0bf6930ca009b62d2d6e26cc32580794c5be6", size = 246983, upload-time = "2026-07-06T20:40:18.242Z" }, ] [[package]] @@ -1747,7 +1747,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.9.7" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1765,9 +1765,9 @@ dependencies = [ { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/8b/a56b97a3b8a94f850fe2ec42351d4c508cc6bd7ca96644906c311e32ec5b/langsmith-0.9.7.tar.gz", hash = "sha256:40f8a66a466a7abd991a9df1b50de0a9d30c48ee0d3da46ce00516b68e41c9d7", size = 4711142, upload-time = "2026-07-02T20:11:09.619Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/53/18bc9169517c274681402f3714d1975ff687e8e175a9fea2b8087db99747/langsmith-0.10.0.tar.gz", hash = "sha256:1bc97a28e4b7a0f2e2f5419668592b3bf460ba5d886991ad725fb2778aa2e236", size = 4708392, upload-time = "2026-07-08T13:28:28.081Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/95/0abb8647123c9ebbb31162bcdba3183f5f3d3bb2f752ef3d777ab715e88c/langsmith-0.9.7-py3-none-any.whl", hash = "sha256:a5ff9179b77eb0c3a0129294d30924eab13e51e2720f374372dbbc014c892911", size = 671052, upload-time = "2026-07-02T20:11:07.702Z" }, + { url = "https://files.pythonhosted.org/packages/64/cf/41d4077d82dce88fb8f6c02938dd5937b5a74cd67bfd7bdb5206277752ca/langsmith-0.10.0-py3-none-any.whl", hash = "sha256:63aec1105b776b8c65a32b002b29ada02469cd26c91beeb2fc2831b247f4da27", size = 652557, upload-time = "2026-07-08T13:28:26.366Z" }, ] [package.optional-dependencies] @@ -2223,27 +2223,27 @@ requires-dist = [ { name = "fastapi", specifier = ">=0.139.0" }, { name = "fireworks-ai", specifier = ">=1.2.0a88" }, { name = "httpx", specifier = ">=0.28.1" }, - { name = "langchain", specifier = ">=1.3.9" }, + { name = "langchain", specifier = ">=1.3.12" }, { name = "langchain-anthropic", specifier = ">=1.4.6" }, - { name = "langchain-aws", specifier = ">=0.2.0" }, + { name = "langchain-aws", specifier = ">=1.6.2" }, { name = "langchain-daytona", specifier = ">=0.0.7" }, { name = "langchain-fireworks", specifier = ">=1.4.3" }, - { name = "langchain-google-genai", specifier = ">=4.2.4" }, + { name = "langchain-google-genai", specifier = ">=4.2.7" }, { name = "langchain-mcp-adapters", specifier = ">=0.3.0" }, { name = "langchain-modal", specifier = ">=0.0.5" }, - { name = "langchain-openai", specifier = ">=1.3.3" }, + { name = "langchain-openai", specifier = ">=1.3.4" }, { name = "langchain-runloop", specifier = ">=0.0.6" }, - { name = "langgraph", specifier = ">=1.1.10" }, + { name = "langgraph", specifier = ">=1.2.8" }, { name = "langgraph-cli", extras = ["inmem"], specifier = ">=0.4.30" }, { name = "langgraph-sdk", specifier = ">=0.4.2" }, - { name = "langsmith", specifier = "==0.9.7" }, + { name = "langsmith", specifier = "==0.10.0" }, { name = "markdownify", specifier = ">=1.2.3" }, { name = "pygments", marker = "extra == 'dev'", specifier = ">=2.20.0" }, { name = "pyjwt", specifier = ">=2.13.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.1.1" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=1.4.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.20" }, - { name = "uvicorn", specifier = ">=0.49.0" }, + { name = "uvicorn", specifier = ">=0.51.0" }, ] provides-extras = ["dev"] @@ -3589,15 +3589,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.49.0" +version = "0.51.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/65/b7c6c443ccc58678c91e1e973bbe2a878591538655d6e1d47f24ba1c51f3/uvicorn-0.51.0.tar.gz", hash = "sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0", size = 94412, upload-time = "2026-07-08T10:59:05.962Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/fa/e1388bbcf24ef3274f45c0c1c7b501fd14971037c1b6ee23610553307497/uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f", size = 71376, upload-time = "2026-06-03T22:01:29.037Z" }, + { url = "https://files.pythonhosted.org/packages/45/ec/dbb7e5a6b91f86bfb9eb7d2988a2730907b6a729875b949c7f022e8b88fa/uvicorn-0.51.0-py3-none-any.whl", hash = "sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b", size = 73219, upload-time = "2026-07-08T10:59:04.44Z" }, ] [[package]] From eb1bf12d2d705622e2e3484e1e0101970ee4c3bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:47:02 -0400 Subject: [PATCH 4/4] chore(deps-dev): bump typescript from 6.0.3 to 7.0.2 in /ui (#156) * chore(deps-dev): bump typescript from 6.0.3 to 7.0.2 in /ui Bumps [typescript](https://github.com/microsoft/TypeScript) from 6.0.3 to 7.0.2. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/commits) --- updated-dependencies: - dependency-name: typescript dependency-version: 7.0.2 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: regenerate bun.lock to reflect typscript 7.0.2 bump --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Co-authored-by: Adam Moussa --- ui/bun.lock | 44 ++++++++++++++++++++++++++++++++++++++++++-- ui/package.json | 2 +- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/ui/bun.lock b/ui/bun.lock index 0adef0a4..52787374 100644 --- a/ui/bun.lock +++ b/ui/bun.lock @@ -51,7 +51,7 @@ "jsdom": "^29.1.1", "prettier": "^3.8.1", "prettier-plugin-tailwindcss": "^0.8.0", - "typescript": "^6.0.3", + "typescript": "^7.0.2", "vite": "^8.1.2", "vite-plugin-pwa": "^1.3.0", "vitest": "^4.1.0", @@ -866,6 +866,46 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.61.0", "", { "dependencies": { "@typescript-eslint/types": "8.61.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ=="], + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], + + "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], + + "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], + + "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], + + "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], + + "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], + + "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], + + "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], + + "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], + + "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], + + "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], + + "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], + + "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], + + "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], + + "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], + + "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], + + "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], + + "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], + + "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], + + "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.12.2", "", { "os": "android", "cpu": "arm" }, "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w=="], @@ -2218,7 +2258,7 @@ "typed-array-length": ["typed-array-length@1.0.8", "", { "dependencies": { "call-bind": "^1.0.9", "for-each": "^0.3.5", "gopd": "^1.2.0", "is-typed-array": "^1.1.15", "possible-typed-array-names": "^1.1.0", "reflect.getprototypeof": "^1.0.10" } }, "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g=="], - "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], "typescript-eslint": ["typescript-eslint@8.61.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.61.0", "@typescript-eslint/parser": "8.61.0", "@typescript-eslint/typescript-estree": "8.61.0", "@typescript-eslint/utils": "8.61.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw=="], diff --git a/ui/package.json b/ui/package.json index 51b13190..758009c1 100644 --- a/ui/package.json +++ b/ui/package.json @@ -58,7 +58,7 @@ "jsdom": "^29.1.1", "prettier": "^3.8.1", "prettier-plugin-tailwindcss": "^0.8.0", - "typescript": "^6.0.3", + "typescript": "^7.0.2", "vite": "^8.1.2", "vite-plugin-pwa": "^1.3.0", "vitest": "^4.1.0",