From 1d73c5e4396dd91be46e2966b5bae0b355693796 Mon Sep 17 00:00:00 2001 From: gleb Date: Tue, 26 May 2026 22:57:24 +0300 Subject: [PATCH] feat: pipeline guarantees task submission regardless of LLM tool calling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added _force_submit() and _is_submitted() helpers. After the agent finishes writing code, the pipeline checks if the task was actually submitted. If not (LLM failed to call tools), the pipeline submits directly via MCP — no LLM involved in the critical submit step. Co-Authored-By: Claude Sonnet 4.6 --- src/agent/graph/pipeline.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/agent/graph/pipeline.py b/src/agent/graph/pipeline.py index 9355c8a..7569393 100644 --- a/src/agent/graph/pipeline.py +++ b/src/agent/graph/pipeline.py @@ -103,6 +103,40 @@ def _is_coding(task: TaskInfo) -> bool: return True +async def _force_submit(task_id: str) -> bool: + """Гарантированно сабмитит задание через MCP напрямую, минуя LLM. + + Вызывается пайплайном после того, как агент написал код в репо. + Возвращает True если сабмит прошёл успешно. + """ + repo_url = f"https://git.brojs.ru/{GITEA_OWNER}/task-{task_id}" + update_tool = _get_journal_tool("task_update_answer") + submit_tool = _get_journal_tool("task_submit") + if not update_tool or not submit_tool: + print(f"[pipeline] force_submit: инструменты не найдены") + return False + try: + await update_tool.ainvoke({ + "taskId": task_id, + "answerType": "link", + "content": repo_url, + "commit": {"repoUrl": repo_url, "branch": "main"}, + }) + await submit_tool.ainvoke({"taskId": task_id, "confirmSubmit": True}) + print(f"[pipeline] Задание {task_id[:8]} — сабмит выполнен пайплайном ✓") + return True + except Exception as e: + print(f"[pipeline] force_submit ошибка: {e}") + return False + + +async def _is_submitted(task_id: str) -> bool: + """Проверяет, сменился ли статус задания на ready_for_review / done.""" + data = await _task_json(task_id) + status = data.get("status", "") + return status in ("ready_for_review", "done") + + async def _task_text(task_id: str) -> str: tool = _get_journal_tool("task_text") if not tool: @@ -298,6 +332,12 @@ async def process_one_task(state: PipelineState) -> dict: ) verification = await _verify_repo(repo_name) + # Гарантированный сабмит пайплайном — не полагаемся на LLM + submitted = await _is_submitted(task_id) + if not submitted: + print(f"[pipeline] Задание {task_id[:8]} — агент не сабмитил, делаем сами...") + await _force_submit(task_id) + print(f"[pipeline] Задание {task_id[:8]} — OK (retries={retries})") results.append({ "task_id": task_id,