feat(training): release V0.9.1 避障训练与基础策略迁移
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled

This commit is contained in:
2026-09-08 10:50:13 +08:00
parent fa5485049a
commit 438e56bcc8
113 changed files with 15027 additions and 539 deletions
+56 -29
View File
@@ -12,6 +12,8 @@ from datetime import UTC, datetime
from pathlib import Path
from typing import Any
from .schema import FLAT_TASK, RewardConfigError, validate_configuration
SCHEMA_VERSION = 2
@@ -169,16 +171,37 @@ class TuningStorage:
def recover_interrupted(self) -> None:
at = now_iso()
with self.transaction() as connection:
previous = connection.execute(
"SELECT id,state FROM sessions WHERE state IN "
"('queued','running','evaluating','paused','awaiting_approval')"
).fetchall()
for row in previous:
connection.execute(
"INSERT INTO audit_events(session_id,event_type,payload_json,created_at) "
"VALUES (?,?,?,?)",
(
row["id"],
"service_restart_interrupted",
_json(
{
"previousState": row["state"],
"reason": "service_restart",
"requiresExplicitResume": True,
}
),
at,
),
)
connection.execute(
"UPDATE trials SET state='interrupted', ended_at=?, "
"message='服务重启中断,等待显式恢复' "
"WHERE state IN ('training','evaluating')",
"WHERE state IN ('queued','training','evaluating')",
(at,),
)
connection.execute(
"UPDATE sessions SET state='interrupted', updated_at=?, "
"message='服务重启中断,可从完整 checkpoint 恢复' "
"WHERE state IN ('running','evaluating')",
"WHERE state IN ('queued','running','evaluating','paused','awaiting_approval')",
(at,),
)
@@ -643,47 +666,51 @@ class TuningStorage:
for row in rows
]
def _preset_task(self, session_id: str) -> str:
# Identity comes only from the persisted source session, never client/preset labels.
try:
config = self.get_session(session_id)["config"]
except (KeyError, ValueError, TypeError) as error:
raise RewardConfigError("奖励 preset 来源 session 丢失或损坏") from error
if not isinstance(config, dict):
raise RewardConfigError("奖励 preset 来源 config 必须是对象")
# Original Flat-only sessions did not require taskId. Validate their preset below.
return config.get("taskId", FLAT_TASK)
def _preset(self, row) -> dict:
task_id = self._preset_task(row["session_id"])
try:
reward_config = validate_configuration(_decode(row["reward_config_json"]), task_id)
except (ValueError, TypeError) as error:
raise RewardConfigError("奖励 preset 配置无效:" + str(error)) from error
return {
"id": row["id"],
"name": row["name"],
"sessionId": row["session_id"],
"trialId": row["trial_id"],
"taskId": task_id,
"rewardConfig": reward_config,
"createdAt": row["created_at"],
}
def save_preset(self, name: str, session_id: str, trial_id: str, reward_config: dict) -> dict:
reward_config = validate_configuration(reward_config, self._preset_task(session_id))
preset_id, at = uuid.uuid4().hex, now_iso()
self.connection().execute(
"INSERT INTO presets(id,name,session_id,trial_id,reward_config_json,created_at) "
"VALUES (?,?,?,?,?,?)",
(preset_id, name, session_id, trial_id, _json(reward_config), at),
)
return {
"id": preset_id,
"name": name,
"sessionId": session_id,
"trialId": trial_id,
"rewardConfig": reward_config,
"createdAt": at,
}
return self.get_preset(preset_id)
def get_preset(self, preset_id: str) -> dict:
row = self.connection().execute("SELECT * FROM presets WHERE id=?", (preset_id,)).fetchone()
if row is None:
raise KeyError(preset_id)
return {
"id": row["id"],
"name": row["name"],
"sessionId": row["session_id"],
"trialId": row["trial_id"],
"rewardConfig": _decode(row["reward_config_json"]),
"createdAt": row["created_at"],
}
return self._preset(row)
def list_presets(self) -> list[dict]:
rows = (
self.connection().execute("SELECT * FROM presets ORDER BY created_at DESC").fetchall()
)
return [
{
"id": row["id"],
"name": row["name"],
"sessionId": row["session_id"],
"trialId": row["trial_id"],
"rewardConfig": _decode(row["reward_config_json"]),
"createdAt": row["created_at"],
}
for row in rows
]
return [self._preset(row) for row in rows]