mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-05 17:27:42 +02:00
[eric] agents: session_store convention-clean (5 funcs off leading-_) + FIX auth/signout _save_session import bug
This commit is contained in:
@@ -32,8 +32,8 @@ from backend.apps.agents.core.error_classify import (
|
||||
# call time (dodging a circular import), and the disk-resilience test monkeypatches it here.
|
||||
from backend.config.paths import SESSIONS_DIR
|
||||
from backend.apps.agents.manager.session.session_store import (
|
||||
_save_session as save_session,
|
||||
_load_session_data as load_session_data,
|
||||
save_session,
|
||||
load_session_data,
|
||||
)
|
||||
from backend.apps.agents.manager.streaming.state import ThinkingState, TurnState
|
||||
from backend.apps.agents.manager.streaming.hook_context import HookContext
|
||||
|
||||
@@ -18,7 +18,7 @@ from backend.apps.agents.core.models import (
|
||||
)
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.settings.settings import load_settings
|
||||
from backend.apps.agents.manager.session.session_store import _load_session_data as load_session_data
|
||||
from backend.apps.agents.manager.session.session_store import load_session_data
|
||||
from backend.apps.agents.manager.session.apply_context_window import apply_context_window
|
||||
from backend.apps.agents.manager.session.workspace_git import (
|
||||
detect_git_identity,
|
||||
|
||||
@@ -15,8 +15,8 @@ from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.settings.settings import load_settings
|
||||
from backend.apps.agents.manager import browser_dispatch
|
||||
from backend.apps.agents.manager.session.session_store import (
|
||||
_load_session_data as load_session_data,
|
||||
_save_session as save_session,
|
||||
load_session_data,
|
||||
save_session,
|
||||
)
|
||||
from backend.apps.agents.manager.session.apply_context_window import apply_context_window
|
||||
from backend.apps.agents.manager.prompt.tool_catalog import get_all_tool_names
|
||||
|
||||
@@ -12,7 +12,7 @@ from typeguard import typechecked
|
||||
|
||||
from backend.apps.agents.core.models import AgentSession, Message
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.agents.manager.session.session_store import _save_session as save_session
|
||||
from backend.apps.agents.manager.session.session_store import save_session
|
||||
from backend.apps.settings.settings import load_settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -12,9 +12,9 @@ from typeguard import typechecked
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.agents.manager.session.session_store import (
|
||||
_delete_session_file as delete_session_file,
|
||||
_load_all_session_data as load_all_session_data,
|
||||
_save_session as save_session,
|
||||
delete_session_file,
|
||||
load_all_session_data,
|
||||
save_session,
|
||||
build_search_text,
|
||||
)
|
||||
from backend.apps.agents.manager.session.cloud_sync import sync_session_close
|
||||
|
||||
@@ -11,7 +11,7 @@ from typeguard import typechecked
|
||||
|
||||
from backend.apps.agents.core.models import AgentSession, Message, MessageBranch
|
||||
from backend.apps.agents.manager.session.apply_context_window import apply_context_window
|
||||
from backend.apps.agents.manager.session.session_store import _load_session_data as load_session_data
|
||||
from backend.apps.agents.manager.session.session_store import load_session_data
|
||||
|
||||
|
||||
@typechecked
|
||||
|
||||
@@ -1,45 +1,53 @@
|
||||
import os
|
||||
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from typeguard import typechecked
|
||||
from backend.config.json_store import read_json_or_none, atomic_write_json
|
||||
|
||||
|
||||
def _sessions_dir() -> str:
|
||||
@typechecked
|
||||
def sessions_dir() -> str:
|
||||
# Resolve live so test patches on either the paths module or the
|
||||
# agent_manager facade re-export land on the same directory.
|
||||
from backend.apps.agents import agent_manager
|
||||
return agent_manager.SESSIONS_DIR
|
||||
|
||||
|
||||
def _save_session(session_id: str, doc_data: dict):
|
||||
sessions_dir = _sessions_dir()
|
||||
os.makedirs(sessions_dir, exist_ok=True)
|
||||
atomic_write_json(os.path.join(sessions_dir, f"{session_id}.json"), doc_data)
|
||||
@typechecked
|
||||
def save_session(session_id: str, doc_data: Dict) -> None:
|
||||
dir_path = sessions_dir()
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
atomic_write_json(os.path.join(dir_path, f"{session_id}.json"), doc_data)
|
||||
|
||||
|
||||
def _load_session_data(session_id: str) -> dict | None:
|
||||
return read_json_or_none(os.path.join(_sessions_dir(), f"{session_id}.json"))
|
||||
@typechecked
|
||||
def load_session_data(session_id: str) -> Optional[Dict]:
|
||||
return read_json_or_none(os.path.join(sessions_dir(), f"{session_id}.json"))
|
||||
|
||||
|
||||
def _delete_session_file(session_id: str):
|
||||
path = os.path.join(_sessions_dir(), f"{session_id}.json")
|
||||
@typechecked
|
||||
def delete_session_file(session_id: str) -> None:
|
||||
path = os.path.join(sessions_dir(), f"{session_id}.json")
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def _load_all_session_data() -> list[tuple[str, dict]]:
|
||||
@typechecked
|
||||
def load_all_session_data() -> List[Tuple[str, Dict]]:
|
||||
results = []
|
||||
sessions_dir = _sessions_dir()
|
||||
if not os.path.exists(sessions_dir):
|
||||
dir_path = sessions_dir()
|
||||
if not os.path.exists(dir_path):
|
||||
return results
|
||||
for fname in os.listdir(sessions_dir):
|
||||
for fname in os.listdir(dir_path):
|
||||
if fname.endswith(".json"):
|
||||
data = read_json_or_none(os.path.join(sessions_dir, fname))
|
||||
data = read_json_or_none(os.path.join(dir_path, fname))
|
||||
if data is not None:
|
||||
results.append((fname[:-5], data))
|
||||
return results
|
||||
|
||||
|
||||
@typechecked
|
||||
def build_search_text(session: AgentSession, max_len: int = 5000) -> str:
|
||||
"""Build a search-indexing string from the session name and message content."""
|
||||
parts = [session.name or ""]
|
||||
|
||||
@@ -211,7 +211,7 @@ async def signout():
|
||||
# Best-effort: failures here shouldn't block the sign-out itself.
|
||||
try:
|
||||
from backend.apps.agents.agent_manager import agent_manager
|
||||
from backend.apps.agents.agent_manager import _save_session
|
||||
from backend.apps.agents.manager.session.session_store import save_session
|
||||
|
||||
running = list(agent_manager.tasks.keys())
|
||||
for session_id in running:
|
||||
@@ -227,7 +227,7 @@ async def signout():
|
||||
if sess.sdk_session_id:
|
||||
sess.sdk_session_id = None
|
||||
try:
|
||||
_save_session(sess.id, sess.model_dump(mode="json"))
|
||||
save_session(sess.id, sess.model_dump(mode="json"))
|
||||
except Exception as e:
|
||||
logger.warning("signout: save_session(%s) failed: %s", sess.id, e)
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ def _strip_orphan_session_cards(data: dict) -> None:
|
||||
non-destructive: a wrong check can only hide a card for one response, not
|
||||
delete it. Drafts have no backend session yet, so they're always kept."""
|
||||
from backend.apps.agents.agent_manager import agent_manager
|
||||
from backend.apps.agents.manager.session.session_store import _load_session_data
|
||||
from backend.apps.agents.manager.session.session_store import load_session_data
|
||||
layout = data.get("layout")
|
||||
if not isinstance(layout, dict):
|
||||
return
|
||||
@@ -385,7 +385,7 @@ def _strip_orphan_session_cards(data: dict) -> None:
|
||||
def gone(sid: str) -> bool:
|
||||
if sid.startswith("draft-") or sid in agent_manager.sessions:
|
||||
return False
|
||||
return _load_session_data(sid) is None
|
||||
return load_session_data(sid) is None
|
||||
|
||||
orphans = [sid for sid in cards if gone(sid)]
|
||||
for sid in orphans:
|
||||
@@ -463,7 +463,7 @@ async def duplicate_dashboard(dashboard_id: str):
|
||||
now = datetime.now().isoformat()
|
||||
|
||||
from backend.apps.agents.agent_manager import agent_manager
|
||||
from backend.apps.agents.manager.session.session_store import _save_session
|
||||
from backend.apps.agents.manager.session.session_store import save_session
|
||||
|
||||
source_layout = source_data.get("layout", {}) or {}
|
||||
source_browser_cards = source_layout.get("browser_cards", {}) or {}
|
||||
@@ -513,7 +513,7 @@ async def duplicate_dashboard(dashboard_id: str):
|
||||
new_sess.browser_id = browser_id_remap[old_browser_id]
|
||||
if old_parent_sid and old_parent_sid in session_id_remap:
|
||||
new_sess.parent_session_id = session_id_remap[old_parent_sid]
|
||||
_save_session(new_sess.id, new_sess.model_dump(mode="json"))
|
||||
save_session(new_sess.id, new_sess.model_dump(mode="json"))
|
||||
|
||||
new_cards: dict[str, dict] = {}
|
||||
for old_sid, card in source_cards.items():
|
||||
|
||||
@@ -150,12 +150,12 @@ def _write(did: str, doc: dict) -> None:
|
||||
|
||||
def _retag_sessions(session_ids, dashboard_id: str) -> None:
|
||||
# Best-effort: a hiccup here must not orphan the just-written dashboard.
|
||||
from backend.apps.agents.manager.session.session_store import _load_session_data, _save_session
|
||||
from backend.apps.agents.manager.session.session_store import load_session_data, save_session
|
||||
for sid in session_ids:
|
||||
try:
|
||||
d = _load_session_data(sid)
|
||||
d = load_session_data(sid)
|
||||
if d is not None:
|
||||
d["dashboard_id"] = dashboard_id
|
||||
_save_session(sid, d)
|
||||
save_session(sid, d)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -45,8 +45,8 @@ class SessionExportable:
|
||||
if sess is not None:
|
||||
d = sess.model_dump(mode="json")
|
||||
else:
|
||||
from backend.apps.agents.manager.session.session_store import _load_session_data
|
||||
d = _load_session_data(local_id)
|
||||
from backend.apps.agents.manager.session.session_store import load_session_data
|
||||
d = load_session_data(local_id)
|
||||
if d is None:
|
||||
return None
|
||||
return cls(local_id, d.get("name") or "Agent", d)
|
||||
@@ -85,7 +85,7 @@ class SessionExportable:
|
||||
|
||||
@classmethod
|
||||
def import_(cls, payload: dict, files: dict[str, bytes], remap: RemapTable) -> str:
|
||||
from backend.apps.agents.manager.session.session_store import _save_session
|
||||
from backend.apps.agents.manager.session.session_store import save_session
|
||||
sid = uuid4().hex
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
# Older bundles (made before transcripts were carried) have no messages;
|
||||
@@ -118,10 +118,10 @@ class SessionExportable:
|
||||
"created_at": now,
|
||||
"closed_at": now,
|
||||
}
|
||||
_save_session(sid, doc)
|
||||
save_session(sid, doc)
|
||||
return sid
|
||||
|
||||
@classmethod
|
||||
def rollback(cls, local_id: str) -> None:
|
||||
from backend.apps.agents.manager.session.session_store import _delete_session_file
|
||||
_delete_session_file(local_id)
|
||||
from backend.apps.agents.manager.session.session_store import delete_session_file
|
||||
delete_session_file(local_id)
|
||||
|
||||
@@ -77,11 +77,11 @@ def test_read_empty_file_returns_none(tmp_path):
|
||||
|
||||
def test_sessions_skip_corrupt_and_roundtrip(tmp_path, monkeypatch):
|
||||
from backend.apps.agents import agent_manager as am
|
||||
from backend.apps.agents.manager.session.session_store import _load_all_session_data
|
||||
from backend.apps.agents.manager.session.session_store import load_all_session_data
|
||||
monkeypatch.setattr(am, "SESSIONS_DIR", str(tmp_path))
|
||||
am.save_session("good", {"id": "good", "v": 1})
|
||||
(tmp_path / "bad.json").write_text("{ truncated session ,,,")
|
||||
loaded = dict(_load_all_session_data())
|
||||
loaded = dict(load_all_session_data())
|
||||
assert loaded == {"good": {"id": "good", "v": 1}}
|
||||
assert (tmp_path / "bad.json").exists() # corrupt file preserved, not deleted
|
||||
assert am.load_session_data("good") == {"id": "good", "v": 1}
|
||||
|
||||
@@ -204,7 +204,7 @@ def test_session_import_restores_transcript_without_granting_mcp(monkeypatch):
|
||||
from backend.apps.swarm.exportable import RemapTable
|
||||
from backend.apps.agents.manager.session import session_store
|
||||
saved: dict = {}
|
||||
monkeypatch.setattr(session_store, "_save_session", lambda sid, doc: saved.update({sid: doc}))
|
||||
monkeypatch.setattr(session_store, "save_session", lambda sid, doc: saved.update({sid: doc}))
|
||||
payload = {
|
||||
"name": "A", "model": "sonnet", "mode": "agent",
|
||||
"messages": [{"id": "m1", "role": "user", "content": "hi", "branch_id": "main"}],
|
||||
@@ -230,7 +230,7 @@ def test_session_import_old_bundle_without_transcript(monkeypatch):
|
||||
from backend.apps.swarm.exportable import RemapTable
|
||||
from backend.apps.agents.manager.session import session_store
|
||||
saved: dict = {}
|
||||
monkeypatch.setattr(session_store, "_save_session", lambda sid, doc: saved.update({sid: doc}))
|
||||
monkeypatch.setattr(session_store, "save_session", lambda sid, doc: saved.update({sid: doc}))
|
||||
sid = SessionExportable.import_({"name": "Old", "model": "sonnet"}, {}, RemapTable())
|
||||
doc = saved[sid]
|
||||
assert doc["messages"] == []
|
||||
|
||||
@@ -685,7 +685,7 @@ def test_dashboard_get_strips_only_orphan_session_cards():
|
||||
fake_mgr = SimpleNamespace(sessions={"live": object()})
|
||||
on_disk = {"ondisk": {"id": "ondisk"}}
|
||||
with patch("backend.apps.agents.agent_manager.agent_manager", fake_mgr), \
|
||||
patch("backend.apps.agents.manager.session.session_store._load_session_data",
|
||||
patch("backend.apps.agents.manager.session.session_store.load_session_data",
|
||||
side_effect=lambda sid: on_disk.get(sid)):
|
||||
D._strip_orphan_session_cards(data)
|
||||
assert set(data["layout"]["cards"].keys()) == {"live", "ondisk", "draft-1"}, "only the ghost should be dropped"
|
||||
|
||||
Reference in New Issue
Block a user