fix: correct task scheduling and simplify agent internals
Fix queue cancellation, concurrency limits and worker status updates. Use direct Explorer imports, split the Diagnoser loop into helpers and remove unused tool registration and prompt-builder code. Correct model caching, endpoint selection and retry handling.
This commit is contained in:
1 parent
67248c887e
commit
12d7902a82
92 files changed
+1085
-1763
No files matched your search
@@ -261,6 +261,20 @@ class DeviceExecutionLock:
|
||||
payload["lock_scope"] = str(lock_scope)
|
||||
return payload
|
||||
|
||||
@classmethod
|
||||
def _atomic_write_json(cls, path: Path, payload: dict) -> bool:
|
||||
"""Replace a ticket atomically so queue scans cannot read partial JSON."""
|
||||
temp_path = path.with_name(f"{path.name}.{uuid.uuid4().hex}.tmp")
|
||||
try:
|
||||
temp_path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")
|
||||
if not cls._safe_replace(temp_path, path):
|
||||
return False
|
||||
except OSError:
|
||||
return False
|
||||
finally:
|
||||
cls._safe_unlink(temp_path)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _ticket_targets_device(
|
||||
cls,
|
||||
@@ -454,11 +468,7 @@ class DeviceExecutionLock:
|
||||
effective_scope = lock_scope or os.getenv(cls.LOCK_SCOPE_ENV) or None
|
||||
if effective_scope:
|
||||
payload["lock_scope"] = str(effective_scope)
|
||||
try:
|
||||
path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")
|
||||
except OSError:
|
||||
return False
|
||||
return True
|
||||
return cls._atomic_write_json(path, payload)
|
||||
|
||||
def _find_reserved_ticket(self) -> Path | None:
|
||||
if not self.queue_ticket:
|
||||
@@ -482,7 +492,10 @@ class DeviceExecutionLock:
|
||||
ingress=self.ingress,
|
||||
lock_scope=self.lock_scope,
|
||||
)
|
||||
path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")
|
||||
if not self._atomic_write_json(path, payload):
|
||||
raise DeviceBusyError(
|
||||
"The Artemis queue reservation could not be claimed (ticket rewrite failed)."
|
||||
)
|
||||
return path
|
||||
|
||||
self.queue_ticket = self.reserve(
|
||||
@@ -567,6 +580,15 @@ class DeviceExecutionLock:
|
||||
return 0
|
||||
if self.max_concurrency is not None:
|
||||
return self.max_concurrency
|
||||
return self.resolve_env_concurrency()
|
||||
|
||||
@classmethod
|
||||
def resolve_env_concurrency(cls) -> int:
|
||||
"""Resolve the concurrency limit from the environment.
|
||||
|
||||
Shared with the admin scheduler. Returns 0 for per-device concurrency,
|
||||
or a positive global limit.
|
||||
"""
|
||||
env_mode = os.environ.get("ARTEMIS_CONCURRENCY_MODE", "").strip().lower()
|
||||
if env_mode in ("global", "serial", "1"):
|
||||
return 1
|
||||
@@ -690,9 +712,14 @@ class DeviceExecutionLock:
|
||||
return
|
||||
try:
|
||||
owner = self._read_owner(self.path)
|
||||
if owner is not None and (
|
||||
owner.token == self.token
|
||||
or (self.session_id and owner.session_id == self.session_id)
|
||||
# A matching session id cannot release another process's lease.
|
||||
if (
|
||||
owner is not None
|
||||
and owner.pid == os.getpid()
|
||||
and (
|
||||
owner.token == self.token
|
||||
or (self.session_id and owner.session_id == self.session_id)
|
||||
)
|
||||
):
|
||||
self._safe_unlink(self.path)
|
||||
finally:
|
||||
@@ -933,16 +960,7 @@ class DeviceExecutionLock:
|
||||
"session_id": str(session_id),
|
||||
"ingress": ingress or owner.ingress or "sdk",
|
||||
}
|
||||
temp_path = path.with_name(f"{path.name}.{uuid.uuid4().hex}.tmp")
|
||||
try:
|
||||
temp_path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")
|
||||
if not cls._safe_replace(temp_path, path):
|
||||
return False
|
||||
except OSError:
|
||||
return False
|
||||
finally:
|
||||
cls._safe_unlink(temp_path)
|
||||
return True
|
||||
return cls._atomic_write_json(path, payload)
|
||||
|
||||
@classmethod
|
||||
def cleanup_stale_locks(cls, device_id: str | None = None) -> int:
|
||||
|
||||
Reference in new issue
Block a user