F0: establish task backend input and native job spine

This commit is contained in:
Xin Wang
2026-10-23 23:33:43 +08:00
parent b9c9d141b9
commit e958491201
8 changed files with 259 additions and 5 deletions
+15 -5
View File
@@ -44,7 +44,7 @@ class TaskService:
self._listeners.clear()
self._jobs.clear()
async def submit(self, *, assistant_id, conversation_id, tool, instruction, invocation_id):
async def submit(self, *, assistant_id, conversation_id, instruction, invocation_id, backend=None, tool=None):
if not self.runtime:
raise ValueError("后台任务服务尚未启动")
if not assistant_id or not conversation_id:
@@ -52,15 +52,25 @@ class TaskService:
text = instruction.strip()
if not text or len(text) > 12000:
raise ValueError("任务指令必须为 1-12000 个字符")
if tool.type != "task":
raise ValueError("请选择后台任务工具")
if backend is not None:
if backend.type != "opencode":
raise ValueError("该任务后端尚未接通")
backend_id = backend.id
connection = deepcopy(backend.config)
connection["_backend_type"] = backend.type
secrets = deepcopy(backend.secrets)
else:
# F0 transition: retired when saved Tool bindings migrate in F1.
if tool is None or tool.type != "task":
raise ValueError("请选择任务后端")
backend_id, connection, secrets = tool.id, deepcopy(tool.definition["config"]), deepcopy(tool.secrets)
async with self._submit_lock:
record = await self.repository.create(TaskRecord(
id="task_" + uuid4().hex,
assistant_id=assistant_id, conversation_id=conversation_id,
tool_id=tool.id, instruction=text,
tool_id=backend_id, instruction=text,
origin_key=f"{conversation_id}:{invocation_id}"[:256],
connection=deepcopy(tool.definition["config"]), secrets=deepcopy(tool.secrets),
connection=connection, secrets=secrets,
))
if not record.job_id and record.status == "queued":
await self._dispatch(record)
+31
View File
@@ -0,0 +1,31 @@
"""F0: explicit simulated backend, real application-owned Pipecat Jobs."""
import asyncio
import json
from pathlib import Path
from types import SimpleNamespace
import unittest
from services.background_tasks.service import TaskService
from tests.test_background_tasks import MemoryRepository, SimulatedExecutor
class TaskBackendSpineTest(unittest.IsolatedAsyncioTestCase):
async def test_backend_resource_reaches_native_job_without_tool_definition(self):
SimulatedExecutor.release = asyncio.Event()
SimulatedExecutor.confirms_stop = True
service = TaskService(MemoryRepository(), SimulatedExecutor)
backend = SimpleNamespace(id="tb_smoke", type="opencode", config={"url": "https://simulated.test", "directory": "/demo", "timeout_seconds": 30}, secrets={})
await service.start()
try:
task = await service.submit(assistant_id="asst_smoke", conversation_id="conv_smoke", backend=backend, instruction="SIMULATED task", invocation_id="smoke")
SimulatedExecutor.release.set()
async with asyncio.timeout(3):
while (await service.get(task["id"])).status != "succeeded":
await asyncio.sleep(.005)
record = await service.get(task["id"])
self.assertEqual(record.connection["_backend_type"], "opencode")
self.assertTrue(record.job_id)
artifact = Path(__file__).resolve().parents[2] / "implement-stage/task-backends/backend-spine-smoke.json"
artifact.write_text(json.dumps({"simulated_remote": True, "native_job": "completed", "backend_id": backend.id, "tool_definition_required": False}) + "\n")
finally:
await service.close()