mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-28 08:56:13 +00:00
Queue comments received while a run is active, then submit one deduplicated follow-up after it finishes. Failed drains are requeued and watcher tasks stop cleanly with the channel manager.
119 lines
6.2 KiB
Python
119 lines
6.2 KiB
Python
"""Per-channel run policy registry.
|
|
|
|
Holds the global ``CHANNEL_RUN_POLICY`` map and its :class:`ChannelRunPolicy`
|
|
descriptor. Split into its own module so channels can register their own
|
|
policy entries (typically as a side-effect of importing their package)
|
|
without creating a circular dependency on :mod:`app.channels.manager`.
|
|
|
|
The dispatch path in :class:`app.channels.manager.ChannelManager` looks
|
|
up policy entries by ``msg.channel_name`` and applies them after
|
|
``_resolve_run_params``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Awaitable, Callable
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
if TYPE_CHECKING:
|
|
from app.channels.message_bus import InboundMessage
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ChannelRunPolicy:
|
|
"""Per-channel knobs applied by :meth:`ChannelManager._apply_channel_policy`.
|
|
|
|
Webhook-driven channels (GitHub today; others later) need four
|
|
things the generic interactive-chat path does not: a higher
|
|
``recursion_limit`` for autonomous long runs, suppression of
|
|
``ask_clarification`` (no human is synchronously present), a
|
|
credentials provider that mints platform tokens for the agent, and
|
|
an opt-out from the per-sender bound-identity gate (authenticity is
|
|
enforced at the webhook route by HMAC, and there is no equivalent
|
|
of a per-user ``/connect`` handshake to perform).
|
|
|
|
Declaring all four on one dataclass keeps the channel's run
|
|
behavior in a single discoverable place and turns "add a new
|
|
webhook channel" into a one-row registration instead of touching
|
|
multiple separate methods on the manager.
|
|
|
|
Attributes:
|
|
is_interactive: When False, the manager sets
|
|
``run_context["disable_clarification"] = True`` so
|
|
``ClarificationMiddleware`` returns a "proceed with best
|
|
judgment" ToolMessage instead of interrupting via
|
|
``Command(goto=END)``. Defaults to True (the safe default
|
|
for an IM channel).
|
|
default_recursion_limit: When set, the manager raises
|
|
``run_config["recursion_limit"]`` to ``max(existing,
|
|
limit)``. None leaves the global default (100) untouched —
|
|
interactive chat turns don't need 250 super-steps.
|
|
credentials_provider: Optional async hook that mutates
|
|
``run_context`` with platform-specific credentials. Called
|
|
after ``_resolve_run_params``. Exceptions are caught and
|
|
logged so a credential failure degrades gracefully (agent
|
|
runs read-only) instead of dropping the delivery.
|
|
requires_bound_identity: When False, the manager skips the
|
|
per-sender bound-identity gate (``_get_bound_identity_rejection``)
|
|
for this channel even when ``channel_connections.enabled`` is
|
|
on. Webhook-authenticated channels (GitHub) have no
|
|
per-sender ``/connect`` handshake — authenticity is enforced
|
|
by HMAC at the webhook route, and the binding from "sender"
|
|
to DeerFlow user is encoded in the agent's ``config.yaml``
|
|
ownership, not in the channel-connections table. Defaults to
|
|
True (the safe default for an interactive IM channel).
|
|
fire_and_forget: When True, the manager schedules the run with
|
|
``runs.create`` (returns immediately once the run is
|
|
``pending``) instead of ``runs.wait`` (which keeps an HTTP
|
|
stream open for the entire run lifetime). Channels that do
|
|
their own outbound during the run — e.g. GitHub, where the
|
|
agent posts to the issue/PR via the ``gh`` CLI in its
|
|
sandbox — don't need the manager to ferry a final state
|
|
back. Eliminates the SDK's 300s ``httpx.ReadTimeout`` on
|
|
runs that legitimately take more than 5 minutes, and the
|
|
false "internal error" outbound that follows when it
|
|
fires. Defaults to False (the safe default for an
|
|
interactive IM channel that depends on the manager to
|
|
publish the agent's reply).
|
|
serialize_thread_runs: When True, the manager serializes
|
|
same-thread inbound turns for this channel instead of
|
|
surfacing the runtime's generic busy-thread error. This is
|
|
useful for chat surfaces like Feishu topics where rapid
|
|
follow-up messages should queue behind the active turn while
|
|
unrelated DeerFlow threads continue concurrently. Defaults
|
|
to False so existing channels keep the runtime's native
|
|
multitask behavior unless they opt in explicitly.
|
|
buffer_followups_on_busy: When True, a ``ConflictError`` on the
|
|
``fire_and_forget`` dispatch path (see
|
|
:meth:`ChannelManager._handle_chat_on_thread`) does more than
|
|
log + reply with the generic busy message: the triggering
|
|
message is appended to a per-thread follow-up buffer, and a
|
|
background watcher subscribes to the active run's
|
|
``StreamBridge`` stream so it can coalesce the buffer into a
|
|
follow-up run as soon as that run ends. This targets
|
|
``fire_and_forget`` channels whose ``send`` is otherwise the
|
|
only feedback a busy sender gets (e.g. GitHub, where ``send``
|
|
is log-only) — without it, a concurrent comment is silently
|
|
dropped from the sender's point of view. Defaults to False so
|
|
channels that have not opted in keep the exact old
|
|
silent-drop-with-log behavior; see
|
|
``app.gateway.github.run_policy`` for GitHub's opt-in.
|
|
"""
|
|
|
|
is_interactive: bool = True
|
|
default_recursion_limit: int | None = None
|
|
credentials_provider: Callable[[InboundMessage, dict[str, Any]], Awaitable[None]] | None = None
|
|
requires_bound_identity: bool = True
|
|
fire_and_forget: bool = False
|
|
serialize_thread_runs: bool = False
|
|
buffer_followups_on_busy: bool = False
|
|
|
|
|
|
# Channel name → policy. Channels absent from this map fall through to
|
|
# the policy default (an interactive IM channel with no credential
|
|
# plumbing) — which is what every IM channel had before GitHub. Webhook
|
|
# channels register their entry at package-import time (see
|
|
# ``app.gateway.github.run_policy``).
|
|
CHANNEL_RUN_POLICY: dict[str, ChannelRunPolicy] = {}
|