feat: add guided exploration and share user-started MCP backend

This commit is contained in:
chenmingxuan committed 2026-10-16 18:32:46 +08:00
1 parent f86db1ced6
commit ee256ad7ce
144 files changed
+8625 -2075

No files matched your search

@@ -25,16 +25,10 @@ import time
from typing import Any
import uuid
try:
from admin_console.core.state import state
from admin_console.database.repositories.session_repository import session_repo
from admin_console.services import worker_process_io
from admin_console.services.media_service import media_service
except ImportError:
from apps.admin_console.core.state import state
from apps.admin_console.database.repositories.session_repository import session_repo
from apps.admin_console.services import worker_process_io
from apps.admin_console.services.media_service import media_service
from apps.admin_console.core.state import state
from apps.admin_console.database.repositories.session_repository import session_repo
from apps.admin_console.services import worker_process_io
from apps.admin_console.services.media_service import media_service
from artemis.config import (
PAUSE_FILE,
@@ -585,6 +579,12 @@ class TaskQueueService:
env["ARTEMIS_SESSION_ID"] = str(sess_id)
env["ARTEMIS_TASK_INGRESS"] = str(task_item.get("ingress", "frontend"))
env["ARTEMIS_TASK_WORKER"] = "1"
from artemis.exploration.store import explorations
exploration_run = explorations.run(str(sess_id)) if sess_id else None
env.pop("ARTEMIS_EXPLORATION_ID", None)
if exploration_run and exploration_run["kind"] == "exploration":
env["ARTEMIS_EXPLORATION_ID"] = exploration_run["exploration_id"]
overrides = task_item.get("runtime_overrides") or {}
if overrides.get("recording") is not None:
env["ARTEMIS_TASK_RECORDING_CONFIG"] = json.dumps(overrides["recording"])
@@ -669,6 +669,11 @@ class TaskQueueService:
batch = store.get("batch", job["batch_id"]) if job and job.get("batch_id") else None
limit = task_timeout(task_item, job, batch)
budget = ExecutionBudget(limit)
from artemis.exploration.store import explorations
exploration_run = explorations.run(sid)
is_exploration = bool(exploration_run and exploration_run["kind"] == "exploration")
exploration_finalized = False
cleanup_limit = (task_item.get("runtime_overrides") or {}).get(
"worker_cleanup_timeout_seconds"
) or settings.ARTEMIS_WORKER_CLEANUP_TIMEOUT_SECONDS
@@ -681,6 +686,9 @@ class TaskQueueService:
return waiter.result()
outcome = read_worker_outcome(sid)
if outcome:
if exploration_run and not exploration_finalized:
explorations.finish_run(sid, {**outcome, "status": outcome["task_status"]})
exploration_finalized = True
finalized_at = finalized_at if finalized_at is not None else time.monotonic()
if time.monotonic() - finalized_at >= cleanup_limit:
logger.warning(
@@ -692,6 +700,9 @@ class TaskQueueService:
return await waiter
continue # Final result is authoritative; only bound resource cleanup now.
cls._sync_control_states()
if is_exploration:
# The worker owns its pausable budget so it survives a UI server restart.
continue
run = state.active_runs.get(run_key, {})
remaining = budget.update(
excluded=bool(run.get("parked")) or read_state(sid).get("status") == "queued"
@@ -1077,9 +1088,35 @@ class TaskQueueService:
f"[QueueWorker] Could not persist failure status for [{sess_id}]"
)
finally:
await cls._finish_output_forwarder(output_task)
# 5. Clean up the finished task and release this run's scheduling slot
cls._release_run_slot(sess_id, run_key, proc)
try:
await cls._finish_output_forwarder(output_task)
if sess_id:
from artemis.exploration.store import explorations
from artemis.runtime.task_result import read_worker_outcome
exploration_run = explorations.run(str(sess_id))
if exploration_run and exploration_run.get("outcome") is None:
from artemis.runtime.case_result import read_case_result
outcome = read_worker_outcome(str(sess_id)) or {}
session = session_repo.get_session_by_id(str(sess_id)) or {}
status = (
outcome.get("task_status") or session.get("status") or "interrupted"
)
explorations.finish_run(
str(sess_id),
{
**outcome,
"status": status,
"test_result": outcome.get("test_result")
or read_case_result(str(sess_id), status),
},
)
except Exception:
logger.exception("Cannot finalize exploration metadata for %s", sess_id)
finally:
# Metadata/storage failure must never leak a scheduling slot.
cls._release_run_slot(sess_id, run_key, proc)
@classmethod
def _find_duplicate_submission(