mesa-core 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mesa_core/__init__.py +76 -0
- mesa_core/backends/__init__.py +29 -0
- mesa_core/backends/jsonfile.py +43 -0
- mesa_core/backends/memory.py +29 -0
- mesa_core/backends/sqlite.py +57 -0
- mesa_core/conflict.py +595 -0
- mesa_core/enforcer.py +431 -0
- mesa_core/exceptions.py +34 -0
- mesa_core/inheritance.py +202 -0
- mesa_core/integration_import.py +51 -0
- mesa_core/mcp/__init__.py +5 -0
- mesa_core/mcp/adapters/__init__.py +39 -0
- mesa_core/mcp/adapters/fastmcp.py +47 -0
- mesa_core/mcp/adapters/raw_sdk.py +63 -0
- mesa_core/mcp/schemas.py +83 -0
- mesa_core/mcp/tools.py +244 -0
- mesa_core/migration.py +57 -0
- mesa_core/privacy.py +135 -0
- mesa_core/profile.py +444 -0
- mesa_core/py.typed +0 -0
- mesa_core/schemas/mesa_profile.schema.json +485 -0
- mesa_core/schemas/mesa_tools.schema.json +117 -0
- mesa_core/store.py +426 -0
- mesa_core/temporal.py +147 -0
- mesa_core/trigger_validator.py +142 -0
- mesa_core/validation.py +238 -0
- mesa_core/vocabulary.py +170 -0
- mesa_core-1.0.0.dist-info/METADATA +143 -0
- mesa_core-1.0.0.dist-info/RECORD +31 -0
- mesa_core-1.0.0.dist-info/WHEEL +4 -0
- mesa_core-1.0.0.dist-info/licenses/LICENSE +196 -0
mesa_core/__init__.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""mesa-core: reference implementation of the MESA specification."""
|
|
2
|
+
|
|
3
|
+
from mesa_core.conflict import ConflictResolver
|
|
4
|
+
from mesa_core.enforcer import ConfirmationManager, EnforcementResult, MesaEnforcer
|
|
5
|
+
from mesa_core.exceptions import (
|
|
6
|
+
InvalidCursorError,
|
|
7
|
+
MesaEnforcementError,
|
|
8
|
+
MesaError,
|
|
9
|
+
MesaValidationError,
|
|
10
|
+
)
|
|
11
|
+
from mesa_core.inheritance import InheritanceResolver, ProfileExplanation
|
|
12
|
+
from mesa_core.integration_import import import_from_integration
|
|
13
|
+
from mesa_core.migration import migrate_profile
|
|
14
|
+
from mesa_core.privacy import AccessDecision, CallerContext, PrivacyEnforcer
|
|
15
|
+
from mesa_core.profile import (
|
|
16
|
+
DOMAIN_SAFETY_BASELINE,
|
|
17
|
+
HELPER_DOMAINS,
|
|
18
|
+
ControlMode,
|
|
19
|
+
MetadataOrigin,
|
|
20
|
+
OperationalBoundaries,
|
|
21
|
+
PrivacyClassification,
|
|
22
|
+
PrivacyLevel,
|
|
23
|
+
ProfileMetadata,
|
|
24
|
+
SemanticProfile,
|
|
25
|
+
TriggersAutomations,
|
|
26
|
+
)
|
|
27
|
+
from mesa_core.store import DeploymentDefaults, ProfileQueryResult, ProfileStore, QueryRow
|
|
28
|
+
from mesa_core.temporal import TemporalEvaluator, TemporalResult
|
|
29
|
+
from mesa_core.trigger_validator import (
|
|
30
|
+
TriggerValidator,
|
|
31
|
+
ValidationIssue,
|
|
32
|
+
entities_by_role,
|
|
33
|
+
)
|
|
34
|
+
from mesa_core.validation import ValidationReport, validate_document, validate_or_raise
|
|
35
|
+
|
|
36
|
+
__version__ = "1.0.0"
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"DOMAIN_SAFETY_BASELINE",
|
|
40
|
+
"HELPER_DOMAINS",
|
|
41
|
+
"AccessDecision",
|
|
42
|
+
"CallerContext",
|
|
43
|
+
"ConfirmationManager",
|
|
44
|
+
"ConflictResolver",
|
|
45
|
+
"ControlMode",
|
|
46
|
+
"DeploymentDefaults",
|
|
47
|
+
"EnforcementResult",
|
|
48
|
+
"InheritanceResolver",
|
|
49
|
+
"InvalidCursorError",
|
|
50
|
+
"MesaEnforcementError",
|
|
51
|
+
"MesaEnforcer",
|
|
52
|
+
"MesaError",
|
|
53
|
+
"MesaValidationError",
|
|
54
|
+
"MetadataOrigin",
|
|
55
|
+
"OperationalBoundaries",
|
|
56
|
+
"PrivacyClassification",
|
|
57
|
+
"PrivacyEnforcer",
|
|
58
|
+
"PrivacyLevel",
|
|
59
|
+
"ProfileExplanation",
|
|
60
|
+
"ProfileMetadata",
|
|
61
|
+
"ProfileQueryResult",
|
|
62
|
+
"ProfileStore",
|
|
63
|
+
"QueryRow",
|
|
64
|
+
"SemanticProfile",
|
|
65
|
+
"TemporalEvaluator",
|
|
66
|
+
"TemporalResult",
|
|
67
|
+
"TriggerValidator",
|
|
68
|
+
"TriggersAutomations",
|
|
69
|
+
"ValidationIssue",
|
|
70
|
+
"ValidationReport",
|
|
71
|
+
"entities_by_role",
|
|
72
|
+
"import_from_integration",
|
|
73
|
+
"migrate_profile",
|
|
74
|
+
"validate_document",
|
|
75
|
+
"validate_or_raise",
|
|
76
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Storage backends for ProfileStore (Module Proposal 4.3)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StorageBackend(ABC):
|
|
10
|
+
"""Abstract storage backend. Host servers may implement their own."""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def read(self, key: str) -> dict[str, Any] | None: ...
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def write(self, key: str, data: dict[str, Any]) -> None: ...
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def delete(self, key: str) -> None: ...
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def list_keys(self, prefix: str | None = None) -> list[str]: ...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
from mesa_core.backends.jsonfile import JsonFileBackend # noqa: E402
|
|
26
|
+
from mesa_core.backends.memory import MemoryBackend # noqa: E402
|
|
27
|
+
from mesa_core.backends.sqlite import SqliteBackend # noqa: E402
|
|
28
|
+
|
|
29
|
+
__all__ = ["JsonFileBackend", "MemoryBackend", "SqliteBackend", "StorageBackend"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""JSON file storage backend: one file per profile, keyed by URL-quoted key.
|
|
2
|
+
|
|
3
|
+
Quoting (rather than lossy character replacement) keeps the filename-to-key
|
|
4
|
+
mapping reversible, so ``list_keys`` can reconstruct keys exactly.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
from urllib.parse import quote, unquote
|
|
13
|
+
|
|
14
|
+
from mesa_core import backends
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class JsonFileBackend(backends.StorageBackend):
|
|
18
|
+
def __init__(self, base_path: str | Path, create_if_missing: bool = True) -> None:
|
|
19
|
+
self.base_path = Path(base_path)
|
|
20
|
+
if create_if_missing:
|
|
21
|
+
self.base_path.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
|
|
23
|
+
def _path(self, key: str) -> Path:
|
|
24
|
+
return self.base_path / f"{quote(key, safe='')}.json"
|
|
25
|
+
|
|
26
|
+
def read(self, key: str) -> dict[str, Any] | None:
|
|
27
|
+
path = self._path(key)
|
|
28
|
+
if not path.exists():
|
|
29
|
+
return None
|
|
30
|
+
data: dict[str, Any] = json.loads(path.read_text())
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
def write(self, key: str, data: dict[str, Any]) -> None:
|
|
34
|
+
self._path(key).write_text(json.dumps(data, indent=2) + "\n")
|
|
35
|
+
|
|
36
|
+
def delete(self, key: str) -> None:
|
|
37
|
+
self._path(key).unlink(missing_ok=True)
|
|
38
|
+
|
|
39
|
+
def list_keys(self, prefix: str | None = None) -> list[str]:
|
|
40
|
+
keys = sorted(unquote(p.stem) for p in self.base_path.glob("*.json"))
|
|
41
|
+
if prefix is not None:
|
|
42
|
+
keys = [k for k in keys if k.startswith(prefix)]
|
|
43
|
+
return keys
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""In-memory storage backend. Not persistent; for testing and development."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from mesa_core import backends
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MemoryBackend(backends.StorageBackend):
|
|
12
|
+
def __init__(self, initial_data: dict[str, dict[str, Any]] | None = None) -> None:
|
|
13
|
+
self._data: dict[str, dict[str, Any]] = copy.deepcopy(initial_data or {})
|
|
14
|
+
|
|
15
|
+
def read(self, key: str) -> dict[str, Any] | None:
|
|
16
|
+
value = self._data.get(key)
|
|
17
|
+
return copy.deepcopy(value) if value is not None else None
|
|
18
|
+
|
|
19
|
+
def write(self, key: str, data: dict[str, Any]) -> None:
|
|
20
|
+
self._data[key] = copy.deepcopy(data)
|
|
21
|
+
|
|
22
|
+
def delete(self, key: str) -> None:
|
|
23
|
+
self._data.pop(key, None)
|
|
24
|
+
|
|
25
|
+
def list_keys(self, prefix: str | None = None) -> list[str]:
|
|
26
|
+
keys = sorted(self._data)
|
|
27
|
+
if prefix is not None:
|
|
28
|
+
keys = [k for k in keys if k.startswith(prefix)]
|
|
29
|
+
return keys
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""SQLite storage backend using the standard library.
|
|
2
|
+
|
|
3
|
+
Suitable for large deployments. Async access goes through the ProfileStore's
|
|
4
|
+
``a``-prefixed methods, which offload to a thread.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import sqlite3
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from mesa_core import backends
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SqliteBackend(backends.StorageBackend):
|
|
18
|
+
def __init__(self, db_path: str | Path) -> None:
|
|
19
|
+
self.db_path = str(db_path)
|
|
20
|
+
with self._connect() as conn:
|
|
21
|
+
conn.execute(
|
|
22
|
+
"CREATE TABLE IF NOT EXISTS profiles (key TEXT PRIMARY KEY, data TEXT NOT NULL)"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def _connect(self) -> sqlite3.Connection:
|
|
26
|
+
return sqlite3.connect(self.db_path)
|
|
27
|
+
|
|
28
|
+
def read(self, key: str) -> dict[str, Any] | None:
|
|
29
|
+
with self._connect() as conn:
|
|
30
|
+
row = conn.execute("SELECT data FROM profiles WHERE key = ?", (key,)).fetchone()
|
|
31
|
+
if row is None:
|
|
32
|
+
return None
|
|
33
|
+
data: dict[str, Any] = json.loads(row[0])
|
|
34
|
+
return data
|
|
35
|
+
|
|
36
|
+
def write(self, key: str, data: dict[str, Any]) -> None:
|
|
37
|
+
with self._connect() as conn:
|
|
38
|
+
conn.execute(
|
|
39
|
+
"INSERT INTO profiles (key, data) VALUES (?, ?) "
|
|
40
|
+
"ON CONFLICT(key) DO UPDATE SET data = excluded.data",
|
|
41
|
+
(key, json.dumps(data)),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def delete(self, key: str) -> None:
|
|
45
|
+
with self._connect() as conn:
|
|
46
|
+
conn.execute("DELETE FROM profiles WHERE key = ?", (key,))
|
|
47
|
+
|
|
48
|
+
def list_keys(self, prefix: str | None = None) -> list[str]:
|
|
49
|
+
query = "SELECT key FROM profiles"
|
|
50
|
+
params: tuple[Any, ...] = ()
|
|
51
|
+
if prefix is not None:
|
|
52
|
+
query += " WHERE key LIKE ?"
|
|
53
|
+
params = (prefix.replace("%", r"\%").replace("_", r"\_") + "%",)
|
|
54
|
+
query += r" ESCAPE '\'"
|
|
55
|
+
query += " ORDER BY key"
|
|
56
|
+
with self._connect() as conn:
|
|
57
|
+
return [row[0] for row in conn.execute(query, params)]
|