Spaces:
Running
Running
Update services/secondary_brain.py
Browse files- services/secondary_brain.py +38 -12
services/secondary_brain.py
CHANGED
|
@@ -2,8 +2,32 @@
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import datetime
|
|
|
|
| 5 |
from pathlib import Path
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
# Tags that indicate procedural/how-to content worth extracting separately
|
| 8 |
PROCEDURAL_TAGS = {
|
| 9 |
"algorithm", "method", "formula", "process", "technique",
|
|
@@ -148,21 +172,23 @@ class DomainLayer:
|
|
| 148 |
condensed = json.loads(cleaned)
|
| 149 |
|
| 150 |
if isinstance(condensed, list) and len(condensed) > 0:
|
| 151 |
-
#
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
print(f"[SecondaryBrain] '{self.domain_name}' condensed from {count} → {len(condensed)} entries.", flush=True)
|
| 159 |
|
| 160 |
-
# Also update condensed_ontology.txt with a summary
|
| 161 |
-
ontology_lines = [f"Domain: {self.domain_name}", f"Condensed at: {
|
| 162 |
for item in condensed:
|
| 163 |
ontology_lines.append(f" [{item.get('sqt','')}] {item.get('summary','')}")
|
| 164 |
-
|
| 165 |
-
f.write("\n".join(ontology_lines))
|
| 166 |
|
| 167 |
return True
|
| 168 |
|
|
@@ -310,8 +336,8 @@ class SecondaryBrain:
|
|
| 310 |
"concept_count": concept_count,
|
| 311 |
"last_active": last_active
|
| 312 |
}
|
| 313 |
-
|
| 314 |
-
|
| 315 |
|
| 316 |
def _get_or_create_domain(self, domain_name: str) -> DomainLayer:
|
| 317 |
"""
|
|
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import datetime
|
| 5 |
+
import tempfile
|
| 6 |
from pathlib import Path
|
| 7 |
|
| 8 |
+
|
| 9 |
+
def _safe_write(filepath: str, content: str):
|
| 10 |
+
"""
|
| 11 |
+
Bucket-safe atomic write. Writes to a temp file in the SAME directory,
|
| 12 |
+
then renames over the target. Within one directory on a FUSE-mounted
|
| 13 |
+
bucket, rename is atomic. Direct open('w') is NOT safe — a crash
|
| 14 |
+
mid-write silently zeroes the file on object storage.
|
| 15 |
+
"""
|
| 16 |
+
dirpath = os.path.dirname(os.path.abspath(filepath))
|
| 17 |
+
os.makedirs(dirpath, exist_ok=True)
|
| 18 |
+
fd, tmp_path = tempfile.mkstemp(prefix=".tmp_sb_", dir=dirpath)
|
| 19 |
+
try:
|
| 20 |
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
| 21 |
+
f.write(content)
|
| 22 |
+
f.flush()
|
| 23 |
+
os.replace(tmp_path, filepath)
|
| 24 |
+
except Exception:
|
| 25 |
+
try:
|
| 26 |
+
os.remove(tmp_path)
|
| 27 |
+
except FileNotFoundError:
|
| 28 |
+
pass
|
| 29 |
+
raise
|
| 30 |
+
|
| 31 |
# Tags that indicate procedural/how-to content worth extracting separately
|
| 32 |
PROCEDURAL_TAGS = {
|
| 33 |
"algorithm", "method", "formula", "process", "technique",
|
|
|
|
| 172 |
condensed = json.loads(cleaned)
|
| 173 |
|
| 174 |
if isinstance(condensed, list) and len(condensed) > 0:
|
| 175 |
+
# Build condensed legend content
|
| 176 |
+
now_iso = datetime.datetime.now().isoformat()
|
| 177 |
+
legend_lines = []
|
| 178 |
+
for item in condensed:
|
| 179 |
+
item["domain"] = self.domain_name
|
| 180 |
+
item["timestamp"] = now_iso
|
| 181 |
+
legend_lines.append(json.dumps(item, ensure_ascii=False))
|
| 182 |
+
# Atomic write — safe on bucket/FUSE storage
|
| 183 |
+
_safe_write(self.legend_path, "\n".join(legend_lines) + "\n")
|
| 184 |
|
| 185 |
print(f"[SecondaryBrain] '{self.domain_name}' condensed from {count} → {len(condensed)} entries.", flush=True)
|
| 186 |
|
| 187 |
+
# Also update condensed_ontology.txt with a summary (atomic)
|
| 188 |
+
ontology_lines = [f"Domain: {self.domain_name}", f"Condensed at: {now_iso}", ""]
|
| 189 |
for item in condensed:
|
| 190 |
ontology_lines.append(f" [{item.get('sqt','')}] {item.get('summary','')}")
|
| 191 |
+
_safe_write(self.ontology_path, "\n".join(ontology_lines))
|
|
|
|
| 192 |
|
| 193 |
return True
|
| 194 |
|
|
|
|
| 336 |
"concept_count": concept_count,
|
| 337 |
"last_active": last_active
|
| 338 |
}
|
| 339 |
+
# Atomic write — safe on bucket/FUSE storage
|
| 340 |
+
_safe_write(self.index_path, json.dumps(index, indent=2, ensure_ascii=False))
|
| 341 |
|
| 342 |
def _get_or_create_domain(self, domain_name: str) -> DomainLayer:
|
| 343 |
"""
|