mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 21:26:39 +00:00
* refactor(gateway): hoist the persistent-write drain into one shared helper Each persistent-write surface forked its own per-router drain wrapper: _drained_write in routers/agents.py (action label, expected_errors pass-through, lost-failure type-only logging), _run_store_mutation in routers/subagents.py (bare drain), and the inline await_drained call in routers/managed_models.py. Hoist the superset into app.gateway.persistent_writes.run_drained_write and point all three routers at it, per the review suggestion on #6087 (its family predecessors #6078/#6092/#6093 have since landed). The subagents and managed-models call sites now also emit the type-only lost-failure log line on a drained-then-failed write (previously silent), matching the tradeoff acknowledged in the #6087 review. artifacts.py and personal_mcp.py keep their direct await_drained forms: the former drains a coroutine with its own rollback flow, the latter needs no label or expected-error handling. Router tests move their monkeypatch targets to the shared name; the lost-failure assertion pins the helper's logger. AGENTS.md points the next persistent-write surface at the canonical implementation. Signed-off-by: yetuge <2219677952@qq.com> * fix(gateway): quiet expected domain errors and scope the AGENTS.md drain claim Review follow-ups on #6151: - The subagents create/update call sites pass (ManagedSubagentExistsError,) and (FileNotFoundError,) so the shared helper stays silent on routine 4xx outcomes (duplicate-name 409, stale-store 404 race), matching create_agent's (AgentExistsError,). - The managed-models save passes (HTTPException,), so the helper stops double-logging failures that _save_with_failure_logging already reports and stays silent on its routine 4xx outcomes. - The AGENTS.md sentence now names the three converted routers instead of implying every persistent-write surface uses the helper; the shortened form also keeps the file under the guidance byte budget that agent-guidance enforces. Signed-off-by: yetuge <2219677952@qq.com> --------- Signed-off-by: yetuge <2219677952@qq.com>
48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
"""Shared helper for persistent router writes drained across cancellation.
|
|
|
|
The persistent-write surfaces used to fork one drain wrapper each
|
|
(``_drained_write`` in ``routers/agents.py``, ``_run_store_mutation`` in
|
|
``routers/subagents.py``, the inline managed-models save drain). Route
|
|
modules copy this one canonical implementation instead.
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
from collections.abc import Callable
|
|
|
|
from deerflow.utils.file_io import await_drained
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def run_drained_write[**P, T](
|
|
action: str,
|
|
func: Callable[P, T],
|
|
expected_errors: tuple[type[Exception], ...] = (),
|
|
/,
|
|
*args: P.args,
|
|
**kwargs: P.kwargs,
|
|
) -> T:
|
|
"""Run a persistent write off the event loop and drain it across cancellation.
|
|
|
|
A client that disconnects mid-request cancels the handler task: a bare
|
|
``asyncio.to_thread`` either cancels a still-queued worker — the write
|
|
silently never happens — or detaches from a running one, dropping its
|
|
failure because the handler's ``except`` never runs. ``await_drained``
|
|
lets the worker finish first; expected domain errors re-raise unlogged
|
|
(the caller maps them to a 4xx while still connected), anything else is
|
|
logged with the exception type only — the text can carry user content.
|
|
"""
|
|
|
|
def _logged() -> T:
|
|
try:
|
|
return func(*args, **kwargs)
|
|
except expected_errors:
|
|
raise
|
|
except Exception as exc:
|
|
# Non-cancelled failures are logged again by the outer route handler.
|
|
logger.error("%s failed (%s)", action, type(exc).__name__)
|
|
raise
|
|
|
|
return await await_drained(asyncio.to_thread(_logged))
|