memuron 0.1.1__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.
- memuron/__init__.py +3 -0
- memuron/actions/__init__.py +12 -0
- memuron/actions/context.py +63 -0
- memuron/actions/helpers.py +88 -0
- memuron/actions/memory.py +340 -0
- memuron/actions/memory_write.py +290 -0
- memuron/actions/nodes.py +340 -0
- memuron/actions/registry.py +5 -0
- memuron/actions/runtime.py +37 -0
- memuron/actions/spaces_documents.py +720 -0
- memuron/actions/sync.py +155 -0
- memuron/application/__init__.py +1 -0
- memuron/application/api.py +206 -0
- memuron/application/app.py +103 -0
- memuron/application/capabilities.py +82 -0
- memuron/application/cli.py +35 -0
- memuron/application/config.py +176 -0
- memuron/application/mcp.py +44 -0
- memuron/application/mcp_oauth.py +290 -0
- memuron/application/registry.py +52 -0
- memuron/context.py +532 -0
- memuron/documents/__init__.py +1 -0
- memuron/documents/link_guardian.py +192 -0
- memuron/documents/linking.py +292 -0
- memuron/documents/parser.py +1152 -0
- memuron/documents/storage.py +151 -0
- memuron/documents/url_ingest.py +375 -0
- memuron/domain/__init__.py +1 -0
- memuron/domain/decoders.py +1 -0
- memuron/domain/encoders.py +185 -0
- memuron/domain/lifecycles.py +8 -0
- memuron/domain/limits.py +6 -0
- memuron/domain/representations.py +56 -0
- memuron/domain/schemas.py +581 -0
- memuron/domain/scope_filter.py +104 -0
- memuron/graphfs/__init__.py +1 -0
- memuron/graphfs/manual.py +635 -0
- memuron/graphfs/projection.py +578 -0
- memuron/graphfs/query.py +1782 -0
- memuron/graphfs/read_model.py +574 -0
- memuron/ingest/__init__.py +1 -0
- memuron/ingest/guardian.py +213 -0
- memuron/ingest/jobs.py +424 -0
- memuron/ingest/prompts.py +147 -0
- memuron/memory/__init__.py +1 -0
- memuron/memory/engine.py +35 -0
- memuron/memory/projections.py +452 -0
- memuron/memory/recipes.py +3247 -0
- memuron/persistence/__init__.py +1 -0
- memuron/persistence/db_pool.py +57 -0
- memuron/persistence/identity_store.py +918 -0
- memuron/persistence/store_helpers.py +16 -0
- memuron/search/__init__.py +1 -0
- memuron/search/fulltext.py +110 -0
- memuron/search/hybrid.py +284 -0
- memuron/search/pgvector.py +252 -0
- memuron/security/__init__.py +1 -0
- memuron/security/auth.py +143 -0
- memuron/security/auth_provider.py +119 -0
- memuron/security/authorization.py +53 -0
- memuron/security/clerk_scopes.py +94 -0
- memuron/security/clerk_webhooks.py +61 -0
- memuron/security/jwt_tokens.py +53 -0
- memuron/security/passwords.py +38 -0
- memuron/security/tenant.py +58 -0
- memuron/spaces/__init__.py +1 -0
- memuron/spaces/model.py +35 -0
- memuron/spaces/service.py +155 -0
- memuron/sync/__init__.py +25 -0
- memuron/sync/folder.py +828 -0
- memuron-0.1.1.dist-info/METADATA +242 -0
- memuron-0.1.1.dist-info/RECORD +74 -0
- memuron-0.1.1.dist-info/WHEEL +4 -0
- memuron-0.1.1.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Space resolution helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from fastapi import HTTPException
|
|
8
|
+
from artha_engine.runtime.auth import AuthContext
|
|
9
|
+
|
|
10
|
+
from memuron.persistence.identity_store import IdentityStore
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def resolve_space_reference(
|
|
14
|
+
identity: IdentityStore,
|
|
15
|
+
*,
|
|
16
|
+
org_id: str,
|
|
17
|
+
space_ref: str,
|
|
18
|
+
) -> dict[str, Any] | None:
|
|
19
|
+
"""Resolve a space UUID, slug, token, or filesystem path."""
|
|
20
|
+
|
|
21
|
+
value = space_ref.strip().rstrip("/")
|
|
22
|
+
if value.startswith("/spaces/"):
|
|
23
|
+
value = value.removeprefix("/spaces/")
|
|
24
|
+
spaces = identity.ensure_default_spaces(org_id)
|
|
25
|
+
for space in spaces:
|
|
26
|
+
if value in {
|
|
27
|
+
str(space["id"]),
|
|
28
|
+
str(space["slug"]),
|
|
29
|
+
str(space["token"]),
|
|
30
|
+
}:
|
|
31
|
+
return space
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def resolve_ingest_hint_space(
|
|
36
|
+
identity: IdentityStore,
|
|
37
|
+
*,
|
|
38
|
+
user_id: str,
|
|
39
|
+
org_id: str,
|
|
40
|
+
space_id: str | None,
|
|
41
|
+
) -> dict[str, Any]:
|
|
42
|
+
enabled = identity.get_enabled_org_spaces(user_id, org_id)
|
|
43
|
+
if not enabled:
|
|
44
|
+
raise HTTPException(
|
|
45
|
+
status_code=400,
|
|
46
|
+
detail="Enable at least one space before ingesting memories",
|
|
47
|
+
)
|
|
48
|
+
enabled_by_id = {space["id"]: space for space in enabled}
|
|
49
|
+
if space_id:
|
|
50
|
+
space = resolve_space_reference(
|
|
51
|
+
identity,
|
|
52
|
+
org_id=org_id,
|
|
53
|
+
space_ref=space_id,
|
|
54
|
+
)
|
|
55
|
+
if space is None:
|
|
56
|
+
raise HTTPException(status_code=404, detail="Space not found")
|
|
57
|
+
if space["id"] not in enabled_by_id:
|
|
58
|
+
raise HTTPException(status_code=400, detail="That space is disabled — enable it first")
|
|
59
|
+
return enabled_by_id[space["id"]]
|
|
60
|
+
for space in enabled:
|
|
61
|
+
if space.get("is_default"):
|
|
62
|
+
return space
|
|
63
|
+
return enabled[0]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _default_hint_space(enabled: list[dict[str, Any]]) -> dict[str, Any]:
|
|
67
|
+
for space in enabled:
|
|
68
|
+
if space.get("is_default"):
|
|
69
|
+
return space
|
|
70
|
+
return enabled[0]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def guardian_space_context(
|
|
74
|
+
identity: IdentityStore,
|
|
75
|
+
*,
|
|
76
|
+
user_id: str,
|
|
77
|
+
org_id: str,
|
|
78
|
+
hint_space: dict[str, Any] | None = None,
|
|
79
|
+
space_mode: str = "assist",
|
|
80
|
+
) -> dict[str, str]:
|
|
81
|
+
enabled = identity.get_enabled_org_spaces(user_id, org_id)
|
|
82
|
+
if not enabled:
|
|
83
|
+
raise HTTPException(
|
|
84
|
+
status_code=400,
|
|
85
|
+
detail="Enable at least one space before ingesting memories",
|
|
86
|
+
)
|
|
87
|
+
enabled_ids = {space["id"] for space in enabled}
|
|
88
|
+
if hint_space is None:
|
|
89
|
+
hint_space = _default_hint_space(enabled)
|
|
90
|
+
elif hint_space["id"] not in enabled_ids:
|
|
91
|
+
hint_space = _default_hint_space(enabled)
|
|
92
|
+
|
|
93
|
+
detail_lines: list[str] = []
|
|
94
|
+
registered_tokens: list[str] = []
|
|
95
|
+
for space in enabled:
|
|
96
|
+
registered_tokens.append(space["token"])
|
|
97
|
+
summary = space["description"] or space["name"]
|
|
98
|
+
prompt = space["guardian_prompt"] or summary
|
|
99
|
+
detail_lines.append(f"- {space['name']} ({space['token']}): {prompt}")
|
|
100
|
+
|
|
101
|
+
other_lines = [
|
|
102
|
+
f"- {space['name']} ({space['token']}): {space['description'] or space['name']}"
|
|
103
|
+
for space in enabled
|
|
104
|
+
if space["id"] != hint_space["id"]
|
|
105
|
+
]
|
|
106
|
+
return {
|
|
107
|
+
"active_space_name": hint_space["name"],
|
|
108
|
+
"active_space_token": hint_space["token"],
|
|
109
|
+
"active_space_prompt": hint_space["guardian_prompt"],
|
|
110
|
+
"other_spaces_summary": "\n".join(other_lines) if other_lines else "(none)",
|
|
111
|
+
"all_spaces_detail": "\n".join(detail_lines) if detail_lines else "(none)",
|
|
112
|
+
"registered_tokens": ", ".join(registered_tokens),
|
|
113
|
+
"space_mode": space_mode,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def apply_guardian_space_scope(
|
|
118
|
+
scope: list[str],
|
|
119
|
+
*,
|
|
120
|
+
space_context: dict[str, str],
|
|
121
|
+
) -> list[str]:
|
|
122
|
+
registered = [
|
|
123
|
+
token.strip()
|
|
124
|
+
for token in space_context.get("registered_tokens", "").split(",")
|
|
125
|
+
if token.strip()
|
|
126
|
+
]
|
|
127
|
+
registered_set = set(registered)
|
|
128
|
+
active = space_context.get("active_space_token")
|
|
129
|
+
mode = space_context.get("space_mode", "assist")
|
|
130
|
+
|
|
131
|
+
space_tokens = [token for token in scope if token in registered_set]
|
|
132
|
+
other_tokens = [token for token in scope if token not in registered_set]
|
|
133
|
+
|
|
134
|
+
if mode == "strict" and active:
|
|
135
|
+
if active not in space_tokens:
|
|
136
|
+
space_tokens.insert(0, active)
|
|
137
|
+
elif not space_tokens and active:
|
|
138
|
+
space_tokens = [active]
|
|
139
|
+
|
|
140
|
+
if not space_tokens and registered:
|
|
141
|
+
raise ValueError("Guardian must assign at least one enabled space token")
|
|
142
|
+
|
|
143
|
+
if len(space_tokens) > 1:
|
|
144
|
+
if mode == "strict" and active and active in space_tokens:
|
|
145
|
+
space_tokens = [active]
|
|
146
|
+
else:
|
|
147
|
+
space_tokens = [space_tokens[0]]
|
|
148
|
+
|
|
149
|
+
merged: list[str] = []
|
|
150
|
+
seen: set[str] = set()
|
|
151
|
+
for token in [*space_tokens, *other_tokens]:
|
|
152
|
+
if token not in seen:
|
|
153
|
+
seen.add(token)
|
|
154
|
+
merged.append(token)
|
|
155
|
+
return merged
|
memuron/sync/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Folder sync helpers."""
|
|
2
|
+
|
|
3
|
+
from memuron.sync.folder import (
|
|
4
|
+
DEFAULT_MANIFEST_RELATIVE_PATH,
|
|
5
|
+
SyncManifest,
|
|
6
|
+
SyncPlan,
|
|
7
|
+
init_manifest,
|
|
8
|
+
load_manifest,
|
|
9
|
+
manifest_path_for_root,
|
|
10
|
+
plan_folder_sync,
|
|
11
|
+
run_folder_sync,
|
|
12
|
+
scan_folder,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"DEFAULT_MANIFEST_RELATIVE_PATH",
|
|
17
|
+
"SyncManifest",
|
|
18
|
+
"SyncPlan",
|
|
19
|
+
"init_manifest",
|
|
20
|
+
"load_manifest",
|
|
21
|
+
"manifest_path_for_root",
|
|
22
|
+
"plan_folder_sync",
|
|
23
|
+
"run_folder_sync",
|
|
24
|
+
"scan_folder",
|
|
25
|
+
]
|