mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-14 05:37:40 +02:00
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
"""On-disk JSON persistence for agent sessions.
|
|
|
|
Each session is stored as {session_id}.json in SESSIONS_DIR.
|
|
The agents subapp calls these functions during close/resume/startup/shutdown.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from typing import List, Tuple, Optional
|
|
|
|
from backend.config.paths import SESSIONS_DIR
|
|
|
|
def _path(session_id: str) -> str:
|
|
return os.path.join(SESSIONS_DIR, f"{session_id}.json")
|
|
|
|
|
|
def save(session_id: str, data: dict) -> None:
|
|
os.makedirs(SESSIONS_DIR, exist_ok=True)
|
|
with open(_path(session_id), "w") as f:
|
|
json.dump(data, f, indent=2)
|
|
|
|
|
|
def load(session_id: str) -> Optional[dict]:
|
|
path = _path(session_id)
|
|
if not os.path.exists(path):
|
|
return None
|
|
with open(path) as f:
|
|
return json.load(f)
|
|
|
|
|
|
def delete(session_id: str) -> None:
|
|
path = _path(session_id)
|
|
if os.path.exists(path):
|
|
os.remove(path)
|
|
|
|
|
|
def load_all() -> List[Tuple[str, dict]]:
|
|
results: List[Tuple[str, dict]] = []
|
|
if not os.path.exists(SESSIONS_DIR):
|
|
return results
|
|
for fname in os.listdir(SESSIONS_DIR):
|
|
if fname.endswith(".json"):
|
|
try:
|
|
with open(os.path.join(SESSIONS_DIR, fname)) as f:
|
|
results.append((fname[:-5], json.load(f)))
|
|
except (json.JSONDecodeError, OSError) as e:
|
|
print(f"[session_store.load_all] Skipping corrupt session file {fname}: {e}")
|
|
return results
|
|
|
|
|
|
def build_search_text(agent_data: dict, max_len: int = 5000) -> str:
|
|
parts = [agent_data.get("name", "")]
|
|
for msg in agent_data.get("messages", {}).get("messages", []):
|
|
role = msg.get("role")
|
|
content = msg.get("content")
|
|
if role in ("user", "assistant") and isinstance(content, str):
|
|
parts.append(content)
|
|
return " ".join(parts)[:max_len]
|
|
|
|
|
|
def get_history(
|
|
q: str = "",
|
|
limit: int = 20,
|
|
offset: int = 0,
|
|
dashboard_id: Optional[str] = None,
|
|
) -> dict:
|
|
all_data = load_all()
|
|
all_data.sort(key=lambda pair: pair[1].get("closed_at") or "", reverse=True)
|
|
|
|
q_lower = q.strip().lower()
|
|
history: List[dict] = []
|
|
for sid, data in all_data:
|
|
if dashboard_id and data.get("dashboard_id") != dashboard_id:
|
|
continue
|
|
if q_lower:
|
|
name = (data.get("name") or "").lower()
|
|
search_text = (data.get("search_text") or "").lower()
|
|
if q_lower not in name and q_lower not in search_text:
|
|
continue
|
|
history.append({
|
|
"id": data.get("session_id", sid),
|
|
"name": data.get("name", "Untitled"),
|
|
"status": data.get("status", "stopped"),
|
|
"model": data.get("model", "sonnet"),
|
|
"mode": data.get("mode", "agent"),
|
|
"created_at": data.get("created_at"),
|
|
"closed_at": data.get("closed_at"),
|
|
"cost_usd": data.get("cost_usd", 0),
|
|
"dashboard_id": data.get("dashboard_id"),
|
|
})
|
|
|
|
total = len(history)
|
|
page = history[offset : offset + limit]
|
|
return {"sessions": page, "total": total, "has_more": offset + limit < total}
|
|
|
|
|
|
async def reconcile_on_startup() -> None:
|
|
for sid, data in load_all():
|
|
if data.get("status") in ("running", "waiting_approval"):
|
|
data["status"] = "stopped"
|
|
save(sid, data)
|
|
print(f"[session_store.reconcile_on_startup] Marked stale session {sid} as stopped") |