نسخهٔ وب مستقل — پلتفرم نیروی کار هوش مصنوعی
مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت. تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""AI runtime worker — drives queued tasks through the run engine.
|
||||
|
||||
The HTTP API can start a run inline (that is what the "اجرا" button does), but
|
||||
a platform that only ever runs work on the request thread has two problems: a
|
||||
slow model call holds a connection open, and a task created by anything other
|
||||
than a button press never runs at all. This worker is the second driver.
|
||||
|
||||
It deliberately owns **no** orchestration logic. It finds runnable tasks and
|
||||
hands each one to ``WorkforceService.start_run`` — the same call the router
|
||||
makes. Model routing, usage accounting, the timeline and the artifact are all
|
||||
the service's business.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from app.application.container import build_workforce_service
|
||||
from app.core.errors import AppError
|
||||
from app.domain.audit.models import ErrorSeverity, ErrorSource
|
||||
from app.domain.workforce.models import TaskStatus
|
||||
from app.infrastructure.error_log import record_error
|
||||
from app.infrastructure.repositories.workforce import TaskRepository
|
||||
from app.workers.base import WorkerOutcome, worker_session
|
||||
|
||||
|
||||
class AiRuntimeWorker:
|
||||
"""Drains the queue of tasks that nobody has run yet."""
|
||||
|
||||
name = "ai-runtime"
|
||||
|
||||
async def drain(self, *, limit: int = 10) -> WorkerOutcome:
|
||||
"""Run up to ``limit`` queued tasks, oldest first.
|
||||
|
||||
A task that fails is recorded and the pass continues: one poisoned task
|
||||
must not block the queue behind it.
|
||||
"""
|
||||
outcome = WorkerOutcome(name=self.name)
|
||||
|
||||
async with worker_session() as session:
|
||||
tasks = TaskRepository(session)
|
||||
workforce = build_workforce_service(session)
|
||||
|
||||
for task in await tasks.list_runnable(limit=limit):
|
||||
# Re-read through the service so the tenant check and the
|
||||
# "already closed" guard both apply. The worker gets no
|
||||
# privileged bypass.
|
||||
try:
|
||||
current = await workforce.get_task_or_404(
|
||||
organization_id=task.organization_id, task_id=task.id
|
||||
)
|
||||
except AppError:
|
||||
outcome.skipped += 1
|
||||
outcome.note(f"task {task.id}: vanished before the run")
|
||||
continue
|
||||
|
||||
if current.status not in (TaskStatus.NEW, TaskStatus.TRIAGED):
|
||||
outcome.skipped += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
run = await workforce.start_run(
|
||||
organization_id=task.organization_id,
|
||||
task_id=task.id,
|
||||
user_id=task.created_by_user_id or "",
|
||||
employee_id=task.assigned_employee_id,
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001 - keep draining
|
||||
outcome.failed += 1
|
||||
outcome.note(f"task {task.id}: {type(exc).__name__}: {exc}")
|
||||
# Persist with the tenant attached: a task that fails to run
|
||||
# is a tenant-visible problem, and the console must be able
|
||||
# to show that organization what happened.
|
||||
await record_error(
|
||||
kind="task_run_failed",
|
||||
message=str(exc) or type(exc).__name__,
|
||||
exc=exc,
|
||||
source=ErrorSource.WORKER,
|
||||
severity=ErrorSeverity.ERROR,
|
||||
organization_id=task.organization_id,
|
||||
user_id=task.created_by_user_id,
|
||||
metadata={
|
||||
"worker": self.name,
|
||||
"task_id": task.id,
|
||||
"employee_id": task.assigned_employee_id,
|
||||
},
|
||||
)
|
||||
continue
|
||||
|
||||
outcome.processed += 1
|
||||
outcome.note(f"task {task.id} -> run {run.id} ({run.status})")
|
||||
|
||||
return outcome
|
||||
|
||||
|
||||
__all__ = ["AiRuntimeWorker"]
|
||||
Reference in New Issue
Block a user