mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] apps: the scaffold ships a durable store and the skill forbids memory-only state, since the runtime dies on a timer
This commit is contained in:
@@ -274,6 +274,22 @@ and flips `BACKEND_PORT` in both `.env` and `.env.example`. Then run
|
|||||||
- Install your own venv or `pip install` manually.
|
- Install your own venv or `pip install` manually.
|
||||||
- Edit `backend/run.sh` or the SubApp framework.
|
- Edit `backend/run.sh` or the SubApp framework.
|
||||||
|
|
||||||
|
**Persist anything the user comes back to. Your process is disposable.**
|
||||||
|
OpenSwarm freezes this app's process when its card closes and fully kills
|
||||||
|
it after ~15 minutes idle, on quit, and on crash. A module-level list or
|
||||||
|
dict is therefore data loss on a timer. The scaffold ships a durable
|
||||||
|
store; use it (or your own files under `backend/data/`):
|
||||||
|
|
||||||
|
```python
|
||||||
|
from backend.apps.store.store import load_store, save_store
|
||||||
|
|
||||||
|
data = load_store() # {} on first run, never raises
|
||||||
|
data["items"] = [*data.get("items", []), new_item]
|
||||||
|
save_store(data) # atomic write; a kill mid-write keeps the old data
|
||||||
|
```
|
||||||
|
|
||||||
|
Holding state only in memory is a bug, not a style choice.
|
||||||
|
|
||||||
Adding a new endpoint is just adding a new SubApp:
|
Adding a new endpoint is just adding a new SubApp:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
"""Disk-backed app state. Use this instead of module-level variables for anything worth keeping.
|
||||||
|
|
||||||
|
The app's process is DISPOSABLE: OpenSwarm freezes it when its card closes, kills it after ~15
|
||||||
|
minutes idle, on quit, and on crash. A module-level list or dict therefore silently loses the
|
||||||
|
user's data on a schedule you don't control. This store survives all of that: one JSON file under
|
||||||
|
backend/data/, written atomically so a kill mid-write can never corrupt it.
|
||||||
|
|
||||||
|
from backend.apps.store.store import load_store, save_store
|
||||||
|
|
||||||
|
items = load_store().get("items", [])
|
||||||
|
items.append(new_item)
|
||||||
|
save_store({**load_store(), "items": items})
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from typeguard import typechecked
|
||||||
|
|
||||||
|
P_BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
DATA_DIR = os.path.join(P_BACKEND_DIR, "data")
|
||||||
|
STORE_PATH = os.path.join(DATA_DIR, "store.json")
|
||||||
|
|
||||||
|
|
||||||
|
@typechecked
|
||||||
|
def load_store() -> Dict[str, Any]:
|
||||||
|
"""The whole store as a dict; empty on first run or an unreadable file, never an exception."""
|
||||||
|
try:
|
||||||
|
with open(STORE_PATH, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return data if isinstance(data, dict) else {}
|
||||||
|
except (FileNotFoundError, json.JSONDecodeError, OSError):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@typechecked
|
||||||
|
def save_store(data: Dict[str, Any]) -> None:
|
||||||
|
"""Replace the store atomically: temp file then rename, so a kill mid-write leaves the old data."""
|
||||||
|
os.makedirs(DATA_DIR, exist_ok=True)
|
||||||
|
fd, tmp = tempfile.mkstemp(dir=DATA_DIR, suffix=".tmp")
|
||||||
|
try:
|
||||||
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=1)
|
||||||
|
os.replace(tmp, STORE_PATH)
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
os.unlink(tmp)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
Reference in New Issue
Block a user