feat: 任务间互发消息(API + agent 标记块,不中断任务)
同项目任务互发消息、对方做完可回消息:API POST /tasks/<目标>/messages (from_task_id,目标项目 create_task + 发送项目 read 鉴权)与 agent 回合输出标记块(worker 回合后解析投递,不用联网、不用凭证,本地 与 Docker 都通)。投递不中断任务:对方运行中/排队中排成后续消息 (轮次边界执行,direct 优先);对方终态则续跑复活(resume_message + queued),A→B→A 往返不受回合时序影响。拒绝项:自己、跨项目、goal 目标、已合并、复活位被占;每轮最多 5 块,仅 completed 轮次投递; 目标执行时配额门照常生效。控制台后续消息列显示发送方,新增给 其他任务发消息框(落已代理的 /tasks 下,不用改 nginx)。 测试:新增 test_peer_messages(15)、test_peer_messages_server(8)、 test_worker_peer(4);全量 cloud 654、host 64、deploy 19 全过(cloud 4 跳过)。
This commit is contained in:
1 parent
4901a1090b
commit
ef30558f7c
11 files changed
+1171
-23
No files matched your search
+55
-2
@@ -137,6 +137,9 @@ from cloud.cost import CostStore, estimate_tokens, price_usd_micros, sum_usage
|
||||
from cloud.db import connect as _db_connect
|
||||
from cloud.logging_config import get_logger, log_event, setup_logging
|
||||
from cloud.models import ModelRouter, is_fallbackable, record_fallback
|
||||
from cloud.peer_messages import deliver_peer_message as _deliver_peer_message
|
||||
from cloud.peer_messages import peer_message_prefix as _peer_prefix
|
||||
from cloud.peer_messages import row_get as _row_get
|
||||
from cloud.pr import PrSkipped, PrStepError
|
||||
from cloud.pr_exec import (claim_pr_job, execute_task_pr, finish_pr_job)
|
||||
from cloud.queue import budget_exceeded, ready_tasks
|
||||
@@ -1672,7 +1675,8 @@ class Worker:
|
||||
if cur.rowcount != 1:
|
||||
return None
|
||||
return {"id": row["id"], "body": row["body"],
|
||||
"mode": row["mode"]}
|
||||
"mode": row["mode"],
|
||||
"sender_task_id": _row_get(row, "sender_task_id")}
|
||||
except Exception:
|
||||
return None
|
||||
finally:
|
||||
@@ -1707,6 +1711,46 @@ class Worker:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _deliver_peer_messages(self, task, result):
|
||||
"""投递本轮 agent 输出里的互发消息标记块(见 peer_messages)。
|
||||
|
||||
只投递 completed 轮次的块:失败 / 超时 / 取消 / 配额中断
|
||||
的轮次不投递;任务执行期间被取消时也不投递。投递本身
|
||||
不中断任何任务:对方运行中/排队中排成后续消息(轮次边
|
||||
界执行),对方终态则续跑复活;拒绝(自己、跨项目、
|
||||
goal 目标、已合并、复活位被占等)只记审计,不炸 run_one。
|
||||
投递不查发送方配额(发送本身不花钱),目标任务执行时
|
||||
的配额门照常生效——互发消息不构成配额绕过。"""
|
||||
if not result or result.get("status") != "completed":
|
||||
return
|
||||
if self._was_cancelled(task["id"]):
|
||||
return
|
||||
messages = result.get("task_messages") or []
|
||||
if not messages:
|
||||
return
|
||||
delivered = 0
|
||||
rejected = []
|
||||
for msg in messages:
|
||||
try:
|
||||
conn = _db_connect(self.db_path)
|
||||
try:
|
||||
status, detail = _deliver_peer_message(
|
||||
conn, task["id"], msg["to_task_id"],
|
||||
msg["body"], msg["mode"])
|
||||
finally:
|
||||
conn.close()
|
||||
except Exception as exc:
|
||||
status, detail = "rejected", "delivery error: %s" % exc
|
||||
if status in ("delivered", "revived"):
|
||||
delivered += 1
|
||||
else:
|
||||
rejected.append("%s: %s" % (msg["to_task_id"], detail))
|
||||
detail = "%s: delivered=%d rejected=%d" % (
|
||||
task["id"], delivered, len(rejected))
|
||||
if rejected:
|
||||
detail += " (" + "; ".join(rejected) + ")"
|
||||
self._audit(task["project_id"], "worker_peer_messages", detail)
|
||||
|
||||
def _run_attempt(self, task, quota_check=None):
|
||||
"""单次执行尝试(不含认领、key 门、重试、成本与最终审计)。
|
||||
|
||||
@@ -2121,6 +2165,7 @@ class Worker:
|
||||
result, error = self._run_fork(task, quota_check=_quota_hit)
|
||||
usage_total = sum_usage(usage_total,
|
||||
(result or {}).get("usage"))
|
||||
self._deliver_peer_messages(task, result)
|
||||
if (result or {}).get("status") == "quota_exceeded":
|
||||
quota_error = self._quota_exceeded(task, usage_total) or \
|
||||
"quota_exceeded: 本月配额已用完"
|
||||
@@ -2137,6 +2182,7 @@ class Worker:
|
||||
break
|
||||
result, error = self._run_attempt(task, quota_check=_quota_hit)
|
||||
usage_total = sum_usage(usage_total, (result or {}).get("usage"))
|
||||
self._deliver_peer_messages(task, result)
|
||||
if (result or {}).get("status") == "quota_exceeded":
|
||||
# 轮询掐断:进程已被杀,配额原因以现查为准。
|
||||
quota_error = self._quota_exceeded(task, usage_total) or \
|
||||
@@ -2188,6 +2234,7 @@ class Worker:
|
||||
result, error = self._run_attempt(task, quota_check=_quota_hit)
|
||||
usage_total = sum_usage(usage_total,
|
||||
(result or {}).get("usage"))
|
||||
self._deliver_peer_messages(task, result)
|
||||
if (result or {}).get("status") == "quota_exceeded":
|
||||
quota_error = self._quota_exceeded(task, usage_total) or \
|
||||
"quota_exceeded: 本月配额已用完"
|
||||
@@ -2239,11 +2286,17 @@ class Worker:
|
||||
quota_stop = True
|
||||
break
|
||||
fu_task = dict(task)
|
||||
fu_task["prompt"] = fu["body"]
|
||||
# 任务间消息的接收方提示:带发送方 id,接收方
|
||||
# agent 凭此知道回给哪个任务;用户后续消息无前缀。
|
||||
fu_prompt = fu["body"]
|
||||
if fu.get("sender_task_id"):
|
||||
fu_prompt = _peer_prefix(fu["sender_task_id"]) + fu_prompt
|
||||
fu_task["prompt"] = fu_prompt
|
||||
fu_result, fu_error = self._run_attempt(
|
||||
fu_task, quota_check=_quota_hit)
|
||||
usage_total = sum_usage(
|
||||
usage_total, (fu_result or {}).get("usage"))
|
||||
self._deliver_peer_messages(task, fu_result)
|
||||
if (fu_result or {}).get("status") == "quota_exceeded":
|
||||
self._restore_followup(fu["id"])
|
||||
quota_error = self._quota_exceeded(task, usage_total) or \
|
||||
|
||||
Reference in new issue
Block a user