mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-11 06:28:58 +00:00
* feat(subagents): check and persist durable batch acceptance Carry optional per-item criteria into native subagents, reuse the deterministic checker, and expose separate verdicts through item queries and exports. Preserve execution and retry semantics, renew leases during checks, and migrate existing batch rows with nullable acceptance fields. * fix(subagents): align batch acceptance normalization and sandbox admission * test(auth): include project permissions in the full-stack contract
59 lines
2.9 KiB
Python
59 lines
2.9 KiB
Python
"""Run the existing checklist for a durable item without a parent tool runtime."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
from typing import Any
|
|
|
|
from deerflow.config.app_config import AppConfig
|
|
from deerflow.subagents.acceptance_checks import AcceptanceVerdict, check_acceptance_criteria, parse_file_criterion
|
|
from deerflow.subagents.report_contract import normalize_acceptance_criteria
|
|
|
|
|
|
def _thread_data(thread_id: str, user_id: str) -> dict[str, str]:
|
|
from deerflow.config.paths import get_paths
|
|
|
|
paths = get_paths()
|
|
return {
|
|
"workspace_path": str(paths.sandbox_work_dir(thread_id, user_id=user_id)),
|
|
"uploads_path": str(paths.sandbox_uploads_dir(thread_id, user_id=user_id)),
|
|
"outputs_path": str(paths.sandbox_outputs_dir(thread_id, user_id=user_id)),
|
|
}
|
|
|
|
|
|
async def check_batch_acceptance(
|
|
criteria: list[str],
|
|
*,
|
|
batch: dict[str, Any],
|
|
app_config: AppConfig,
|
|
bash_executions: list[dict[str, Any]] | None,
|
|
) -> AcceptanceVerdict | None:
|
|
from deerflow.authz.sandbox_authz import authorize_sandbox_execution_async
|
|
from deerflow.sandbox.lease import SANDBOX_LEASE_OWNER_CONTEXT_KEY, acquire_sandbox_client_lease, run_sync_lifecycle_operation
|
|
from deerflow.sandbox.sandbox_provider import get_sandbox_provider
|
|
|
|
criteria = await run_sync_lifecycle_operation(normalize_acceptance_criteria, criteria)
|
|
if not criteria:
|
|
return None
|
|
thread_id, user_id = batch["thread_id"], batch["user_id"]
|
|
spec = batch["execution_spec"]
|
|
context = {key: spec.get(key) for key in ("user_role", "oauth_provider", "oauth_id", "channel_user_id", "is_internal", "authz_attributes")}
|
|
context.update(thread_id=thread_id, user_id=user_id)
|
|
thread_data = await run_sync_lifecycle_operation(_thread_data, thread_id, user_id)
|
|
runtime = SimpleNamespace(state={"thread_data": thread_data}, context=context, config={"configurable": {"thread_id": thread_id}})
|
|
lease = None
|
|
try:
|
|
# Evidence-only / unsupported conditions need no sandbox. File checks
|
|
# use an authorized, owner-scoped holder of the shared thread sandbox.
|
|
if any(parse_file_criterion(criterion) is not None for criterion in criteria):
|
|
await authorize_sandbox_execution_async(context=context, app_config=app_config)
|
|
provider = await run_sync_lifecycle_operation(get_sandbox_provider)
|
|
lease = await acquire_sandbox_client_lease(provider, thread_id, user_id=user_id, owner_prefix="batch-acceptance")
|
|
runtime.state["sandbox"] = {"sandbox_id": lease.sandbox_id}
|
|
context[SANDBOX_LEASE_OWNER_CONTEXT_KEY] = lease.owner_id
|
|
# Drain blocking reads before releasing the holder, including shutdown.
|
|
return await run_sync_lifecycle_operation(check_acceptance_criteria, criteria, runtime=runtime, thread_data=thread_data, bash_executions=bash_executions)
|
|
finally:
|
|
if lease is not None:
|
|
await lease.release()
|