Spaces:
Sleeping
Sleeping
fix: use upload_folder for backup (prevents 429 rate limits) and auto-create second-brain dir
Browse files- src/app.py +1 -2
- sync.py +46 -39
src/app.py
CHANGED
|
@@ -1312,8 +1312,7 @@ def api_get_second_brain():
|
|
| 1312 |
return auth_err
|
| 1313 |
|
| 1314 |
brain_dir = os.getenv("SECOND_BRAIN_DIR", os.path.join(os.path.expanduser("~"), ".agentmemory", "second-brain"))
|
| 1315 |
-
|
| 1316 |
-
return jsonify({"error": "Second brain directory not found", "path": brain_dir}), 404
|
| 1317 |
|
| 1318 |
file_param = request.args.get("file")
|
| 1319 |
if file_param:
|
|
|
|
| 1312 |
return auth_err
|
| 1313 |
|
| 1314 |
brain_dir = os.getenv("SECOND_BRAIN_DIR", os.path.join(os.path.expanduser("~"), ".agentmemory", "second-brain"))
|
| 1315 |
+
os.makedirs(brain_dir, exist_ok=True)
|
|
|
|
| 1316 |
|
| 1317 |
file_param = request.args.get("file")
|
| 1318 |
if file_param:
|
sync.py
CHANGED
|
@@ -7,8 +7,8 @@ Usage:
|
|
| 7 |
"""
|
| 8 |
import os
|
| 9 |
import sys
|
| 10 |
-
import glob
|
| 11 |
import shutil
|
|
|
|
| 12 |
|
| 13 |
try:
|
| 14 |
from huggingface_hub import HfApi, hf_hub_download, list_repo_files
|
|
@@ -17,11 +17,12 @@ except ImportError:
|
|
| 17 |
print("[sync] huggingface_hub not installed, skipping sync")
|
| 18 |
sys.exit(0)
|
| 19 |
|
| 20 |
-
HF_TOKEN
|
| 21 |
-
REPO_ID
|
| 22 |
-
DATA_DIR
|
| 23 |
-
SKIP_FILES
|
| 24 |
-
ALLOW_HIDDEN = {".hmac"}
|
|
|
|
| 25 |
|
| 26 |
def get_api():
|
| 27 |
return HfApi(token=HF_TOKEN)
|
|
@@ -49,7 +50,7 @@ def restore():
|
|
| 49 |
try:
|
| 50 |
local_path = os.path.join(DATA_DIR, fname)
|
| 51 |
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
| 52 |
-
|
| 53 |
repo_id=REPO_ID,
|
| 54 |
filename=fname,
|
| 55 |
repo_type="dataset",
|
|
@@ -77,40 +78,46 @@ def backup():
|
|
| 77 |
print(f"[sync] repo_info error: {e}")
|
| 78 |
return
|
| 79 |
|
| 80 |
-
#
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
path_or_fileobj=full_path,
|
| 104 |
-
path_in_repo=rel_path.replace('\\', '/'),
|
| 105 |
-
repo_id=REPO_ID,
|
| 106 |
-
repo_type="dataset",
|
| 107 |
-
token=HF_TOKEN,
|
| 108 |
-
)
|
| 109 |
-
print(f"[sync] backed up {rel_path}")
|
| 110 |
-
except Exception as e:
|
| 111 |
-
print(f"[sync] backup {rel_path} error: {e}")
|
| 112 |
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
if __name__ == "__main__":
|
| 116 |
cmd = sys.argv[1] if len(sys.argv) > 1 else "backup"
|
|
|
|
| 7 |
"""
|
| 8 |
import os
|
| 9 |
import sys
|
|
|
|
| 10 |
import shutil
|
| 11 |
+
import tempfile
|
| 12 |
|
| 13 |
try:
|
| 14 |
from huggingface_hub import HfApi, hf_hub_download, list_repo_files
|
|
|
|
| 17 |
print("[sync] huggingface_hub not installed, skipping sync")
|
| 18 |
sys.exit(0)
|
| 19 |
|
| 20 |
+
HF_TOKEN = os.environ.get("HF_TOKEN", "")
|
| 21 |
+
REPO_ID = os.environ.get("AGENTMEMORY_DATASET_REPO", "Yash030/agentmemory-python-data")
|
| 22 |
+
DATA_DIR = os.path.expanduser("~/.agentmemory")
|
| 23 |
+
SKIP_FILES = {".env"}
|
| 24 |
+
ALLOW_HIDDEN = {".hmac"}
|
| 25 |
+
SKIP_NAMES = {"LOCK"} # held open by Dolt — always skip
|
| 26 |
|
| 27 |
def get_api():
|
| 28 |
return HfApi(token=HF_TOKEN)
|
|
|
|
| 50 |
try:
|
| 51 |
local_path = os.path.join(DATA_DIR, fname)
|
| 52 |
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
| 53 |
+
hf_hub_download(
|
| 54 |
repo_id=REPO_ID,
|
| 55 |
filename=fname,
|
| 56 |
repo_type="dataset",
|
|
|
|
| 78 |
print(f"[sync] repo_info error: {e}")
|
| 79 |
return
|
| 80 |
|
| 81 |
+
# Stage files into a temp dir then upload in one commit
|
| 82 |
+
staging = tempfile.mkdtemp(prefix="agentmemory_sync_")
|
| 83 |
+
try:
|
| 84 |
+
copied = 0
|
| 85 |
+
for root, dirs, files in os.walk(DATA_DIR):
|
| 86 |
+
is_inside_dolt = ".dolt" in root.replace("\\", "/").split("/")
|
| 87 |
+
if not is_inside_dolt:
|
| 88 |
+
dirs[:] = [d for d in dirs if not d.startswith(".") or d == ".dolt"]
|
| 89 |
+
for f in files:
|
| 90 |
+
if f in SKIP_FILES or f in SKIP_NAMES:
|
| 91 |
+
continue
|
| 92 |
+
if f.startswith(".") and f not in ALLOW_HIDDEN:
|
| 93 |
+
continue
|
| 94 |
+
full = os.path.join(root, f)
|
| 95 |
+
rel = os.path.relpath(full, DATA_DIR).replace("\\", "/")
|
| 96 |
+
dest = os.path.join(staging, rel.replace("/", os.sep))
|
| 97 |
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
| 98 |
+
try:
|
| 99 |
+
shutil.copy2(full, dest)
|
| 100 |
+
copied += 1
|
| 101 |
+
except Exception as e:
|
| 102 |
+
print(f"[sync] stage {rel} error: {e}")
|
| 103 |
|
| 104 |
+
if copied == 0:
|
| 105 |
+
print("[sync] nothing to backup")
|
| 106 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
+
print(f"[sync] uploading {copied} files to {REPO_ID}...")
|
| 109 |
+
api.upload_folder(
|
| 110 |
+
folder_path=staging,
|
| 111 |
+
repo_id=REPO_ID,
|
| 112 |
+
repo_type="dataset",
|
| 113 |
+
token=HF_TOKEN,
|
| 114 |
+
commit_message="sync: periodic backup",
|
| 115 |
+
)
|
| 116 |
+
print("[sync] backup complete")
|
| 117 |
+
except Exception as e:
|
| 118 |
+
print(f"[sync] backup error: {e}")
|
| 119 |
+
finally:
|
| 120 |
+
shutil.rmtree(staging, ignore_errors=True)
|
| 121 |
|
| 122 |
if __name__ == "__main__":
|
| 123 |
cmd = sys.argv[1] if len(sys.argv) > 1 else "backup"
|