Initial SFERA platform baseline

This commit is contained in:
2026-05-16 19:03:49 +03:00
commit 3b845c8fce
282 changed files with 55045 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# sfera-storage-core
File-backed storage for SFERA semantic snapshots and local stand state.
+11
View File
@@ -0,0 +1,11 @@
[project]
name = "sfera-storage-core"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"pydantic>=2.0",
"sfera-sir",
]
[tool.uv]
package = true
@@ -0,0 +1,131 @@
from __future__ import annotations
from pathlib import Path
from typing import Any
import orjson
from pydantic import BaseModel
from sir import SirSnapshot, snapshot_from_json, snapshot_to_json
class StoredSnapshotInfo(BaseModel):
project_id: str
snapshot_id: str
snapshot_hash: str | None = None
path: str
class FileStorage:
def __init__(self, root: str | Path = ".sfera/storage") -> None:
self.root = Path(root)
self.snapshots_dir = self.root / "snapshots"
def initialize(self) -> None:
self.snapshots_dir.mkdir(parents=True, exist_ok=True)
def save_snapshot(self, snapshot: SirSnapshot) -> StoredSnapshotInfo:
self.initialize()
path = self._snapshot_path(snapshot.project_id)
path.write_bytes(snapshot_to_json(snapshot))
return StoredSnapshotInfo(
project_id=snapshot.project_id,
snapshot_id=snapshot.snapshot_id,
snapshot_hash=snapshot.snapshot_hash,
path=path.as_posix(),
)
def load_snapshot(self, project_id: str) -> SirSnapshot:
path = self._snapshot_path(project_id)
if not path.exists():
raise FileNotFoundError(project_id)
return snapshot_from_json(path.read_bytes())
def list_snapshots(self) -> list[StoredSnapshotInfo]:
self.initialize()
result: list[StoredSnapshotInfo] = []
for path in sorted(self.snapshots_dir.glob("*.json")):
snapshot = snapshot_from_json(path.read_bytes())
result.append(
StoredSnapshotInfo(
project_id=snapshot.project_id,
snapshot_id=snapshot.snapshot_id,
snapshot_hash=snapshot.snapshot_hash,
path=path.as_posix(),
)
)
return result
def has_snapshot(self, project_id: str) -> bool:
return self._snapshot_path(project_id).exists()
def write_document(self, collection: str, document_id: str, payload: dict[str, Any]) -> Path:
directory = self._collection_dir(collection)
directory.mkdir(parents=True, exist_ok=True)
path = directory / f"{self._safe_name(document_id)}.json"
path.write_bytes(orjson.dumps(payload, option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS))
return path
def read_document(self, collection: str, document_id: str) -> dict[str, Any]:
path = self._collection_dir(collection) / f"{self._safe_name(document_id)}.json"
if not path.exists():
raise FileNotFoundError(f"{collection}/{document_id}")
return orjson.loads(path.read_bytes())
def delete_document(self, collection: str, document_id: str) -> bool:
path = self._collection_dir(collection) / f"{self._safe_name(document_id)}.json"
if not path.exists():
return False
path.unlink()
return True
def delete_snapshot(self, project_id: str) -> bool:
path = self._snapshot_path(project_id)
if not path.exists():
return False
path.unlink()
return True
def delete_documents_matching(self, collection: str, predicate) -> int:
directory = self._collection_dir(collection)
if not directory.exists():
return 0
deleted = 0
for path in sorted(directory.glob("*.json")):
try:
payload = orjson.loads(path.read_bytes())
except (FileNotFoundError, orjson.JSONDecodeError):
continue
if predicate(payload):
path.unlink()
deleted += 1
return deleted
def list_documents(self, collection: str, *, limit: int | None = None) -> list[dict[str, Any]]:
directory = self._collection_dir(collection)
if not directory.exists():
return []
documents: list[dict[str, Any]] = []
for path in sorted(directory.glob("*.json")):
try:
documents.append(orjson.loads(path.read_bytes()))
except (FileNotFoundError, orjson.JSONDecodeError):
continue
if limit is not None and len(documents) >= limit:
break
return documents
def _snapshot_path(self, project_id: str) -> Path:
return self.snapshots_dir / f"{self._safe_name(project_id)}.json"
def _collection_dir(self, collection: str) -> Path:
return self.root / self._safe_name(collection)
def _safe_name(self, value: str) -> str:
return "".join(
character if character.isalnum() or character in {"-", "_", "."} else "_"
for character in value
)
__all__ = ["FileStorage", "StoredSnapshotInfo"]
@@ -0,0 +1,28 @@
from pathlib import Path
from semantic_kernel import index_project
from storage_core import FileStorage
def test_file_storage_saves_and_loads_snapshot(tmp_path: Path):
source_dir = tmp_path / "src"
source_dir.mkdir()
(source_dir / "module.bsl").write_text("Процедура Test()\nКонецПроцедуры\n", encoding="utf-8")
snapshot = index_project(source_dir, project_id="demo")
storage = FileStorage(tmp_path / "storage")
info = storage.save_snapshot(snapshot)
restored = storage.load_snapshot("demo")
assert info.project_id == "demo"
assert restored.snapshot_hash == snapshot.snapshot_hash
assert storage.list_snapshots()[0].snapshot_id == snapshot.snapshot_id
def test_file_storage_generic_documents(tmp_path: Path):
storage = FileStorage(tmp_path / "storage")
storage.write_document("knowledge", "record/1", {"record_id": "record/1", "title": "Demo"})
assert storage.read_document("knowledge", "record/1")["title"] == "Demo"
assert storage.list_documents("knowledge")[0]["record_id"] == "record/1"