Обновить сервис агента-кодера

This commit is contained in:
AidarKC
2026-05-24 09:21:50 +03:00
parent 4b371e142d
commit a83ec2c971
6 changed files with 174 additions and 24 deletions
+63 -9
View File
@@ -181,6 +181,7 @@ class ShinePyBotService:
self.stop_current_job = False
self.lock_fd = None
self.last_heartbeat_at: float = 0.0
self.restart_requested = False
def run(self) -> None:
self._ensure_dirs()
@@ -449,6 +450,19 @@ class ShinePyBotService:
archived = self._rotate_history("command_new", username)
self._safe_send(chat_id, f"История очищена. Новый диалог начат.\nАрхив: {archived.name}", reply_to=message_id)
return
if lower in ("/restart_service", "/restart"):
self._append_history_event("restart_service_requested", {
"chatId": chat_id,
"messageId": message_id,
"username": username,
})
self._safe_send(
chat_id,
"Перезапускаю сервис. Если задача была активна, после старта она вернётся в очередь и продолжится.",
reply_to=message_id,
)
self._schedule_self_restart()
return
if lower == "/stop":
stopped = self._cancel_active_job("stopped_by_user")
if stopped:
@@ -483,6 +497,7 @@ class ShinePyBotService:
"/stop — остановить текущую задачу\n"
"/cancel <id|all> — удалить задачу по id (префикс) или все\n"
"/new — архивировать историю и начать новую\n"
"/restart_service — перезапустить сервис через systemd\n"
"/help — эта справка"
)
@@ -654,9 +669,11 @@ class ShinePyBotService:
self.last_heartbeat_at = 0.0
last_user_note = ""
last_user_note_at = 0.0
codex_started_at = time.time()
last_job_message_at = codex_started_at
def on_line(line: str) -> None:
nonlocal last_user_note, last_user_note_at
nonlocal last_user_note, last_user_note_at, last_job_message_at
output_lines.append(line)
note = self._extract_codex_user_note(line)
now = time.time()
@@ -664,9 +681,7 @@ class ShinePyBotService:
self._safe_send(chat_id, f"#{job_num}: {note}", reply_to=message_id)
last_user_note = note
last_user_note_at = now
if now - self.last_heartbeat_at > 60:
self._safe_send(chat_id, f"#{job_num}: всё ещё выполняется...", reply_to=message_id)
self.last_heartbeat_at = now
last_job_message_at = now
reader_done = threading.Event()
@@ -682,11 +697,27 @@ class ShinePyBotService:
t.start()
try:
return_code = process.wait(timeout=self.cfg.codex_timeout_seconds)
except subprocess.TimeoutExpired:
process.kill()
t.join(timeout=2)
raise RuntimeError(f"Codex timeout after {self.cfg.codex_timeout_seconds}s")
deadline = time.time() + self.cfg.codex_timeout_seconds
return_code = None
while return_code is None:
return_code = process.poll()
now = time.time()
if return_code is not None:
break
if now >= deadline:
process.kill()
t.join(timeout=2)
raise RuntimeError(f"Codex timeout after {self.cfg.codex_timeout_seconds}s")
if now - codex_started_at >= 120 and now - last_job_message_at >= 120:
elapsed = self._format_duration(int(now - codex_started_at))
self._safe_send(
chat_id,
f"#{job_num}: задача ещё выполняется, работает уже {elapsed}. От Codex давно нет сообщений.",
reply_to=message_id,
)
last_job_message_at = now
self.last_heartbeat_at = now
time.sleep(1)
finally:
with self.active_process_lock:
self.active_process = None
@@ -777,6 +808,18 @@ class ShinePyBotService:
except Exception as e:
print(f"[py-bot] sendMessage error: {e}", flush=True)
def _schedule_self_restart(self) -> None:
if self.restart_requested:
return
self.restart_requested = True
def restart() -> None:
time.sleep(1.5)
print("[py-bot] restart requested by Telegram command", flush=True)
os._exit(0)
threading.Thread(target=restart, name="shine-py-bot-self-restart", daemon=True).start()
def _transcribe_voice_job(self, job: dict[str, Any]) -> str:
if not self.cfg.openai_api_key:
raise RuntimeError("Не задан OPENAI_API_KEY для распознавания voice")
@@ -878,6 +921,17 @@ class ShinePyBotService:
return line
return ""
@staticmethod
def _format_duration(seconds: int) -> str:
seconds = max(0, seconds)
minutes, sec = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
if hours:
return f"{hours}ч {minutes}м {sec}с"
if minutes:
return f"{minutes}м {sec}с"
return f"{sec}с"
def run_selftest(config: BotConfig, prompt: str) -> int:
cmd = [