adaptive-memory-engine 0.1.6__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.
- adaptive_memory_engine-0.1.6.dist-info/METADATA +228 -0
- adaptive_memory_engine-0.1.6.dist-info/RECORD +72 -0
- adaptive_memory_engine-0.1.6.dist-info/WHEEL +4 -0
- adaptive_memory_engine-0.1.6.dist-info/entry_points.txt +3 -0
- adaptive_memory_engine-0.1.6.dist-info/licenses/LICENSE +21 -0
- ame/__init__.py +1 -0
- ame/agent/__init__.py +1 -0
- ame/agent/mcp.py +474 -0
- ame/agent/memory_api.py +141 -0
- ame/agent/results.py +30 -0
- ame/bronze/schema.py +17 -0
- ame/bronze/store.py +38 -0
- ame/cli/__init__.py +1 -0
- ame/cli/main.py +903 -0
- ame/connectors/base.py +30 -0
- ame/connectors/contract.py +199 -0
- ame/connectors/github.py +66 -0
- ame/connectors/google.py +464 -0
- ame/connectors/google_oauth.py +156 -0
- ame/connectors/jira.py +66 -0
- ame/connectors/json_helpers.py +43 -0
- ame/connectors/markdown.py +116 -0
- ame/connectors/notion.py +59 -0
- ame/connectors/oauth_callback.py +102 -0
- ame/connectors/oauth_provider.py +250 -0
- ame/connectors/obsidian.py +19 -0
- ame/connectors/router.py +155 -0
- ame/connectors/slack.py +66 -0
- ame/connectors/slack_oauth.py +417 -0
- ame/connectors/sync_history.py +73 -0
- ame/context_budget.py +106 -0
- ame/core/config.py +77 -0
- ame/core/corpus.py +17 -0
- ame/core/errors.py +18 -0
- ame/core/paths.py +111 -0
- ame/core/state.py +57 -0
- ame/export/obsidian.py +123 -0
- ame/gold/builder.py +300 -0
- ame/gold/ontology.py +80 -0
- ame/gold/resolver.py +91 -0
- ame/gold/schema.py +40 -0
- ame/gold/store.py +45 -0
- ame/hardware/profiler.py +85 -0
- ame/hardware/tier.py +27 -0
- ame/hermes/__init__.py +3 -0
- ame/hermes/memory.py +209 -0
- ame/models/download.py +243 -0
- ame/models/ollama.py +60 -0
- ame/models/registry.py +101 -0
- ame/models/router.py +22 -0
- ame/pipeline.py +155 -0
- ame/query/diff.py +40 -0
- ame/query/engine.py +919 -0
- ame/query/memory_os.py +313 -0
- ame/query/mql.py +84 -0
- ame/query/multihop.py +264 -0
- ame/query/result.py +20 -0
- ame/sdk.py +52 -0
- ame/security.py +145 -0
- ame/silver/extractor.py +414 -0
- ame/silver/llm_extractor.py +181 -0
- ame/silver/prompts.py +56 -0
- ame/silver/rationale.py +140 -0
- ame/silver/schema.py +51 -0
- ame/silver/store.py +59 -0
- ame/storage/custom_kg.py +33 -0
- ame/storage/lightrag_adapter.py +362 -0
- ame/validation/confidence.py +5 -0
- ame/validation/grounding.py +10 -0
- ame/validation/type_gate.py +22 -0
- ame/writeback.py +173 -0
- memory/__init__.py +3 -0
ame/connectors/base.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Protocol
|
|
6
|
+
|
|
7
|
+
from ame.bronze.schema import BronzeDocument
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class SourceRef:
|
|
12
|
+
path: Path
|
|
13
|
+
source_id: str
|
|
14
|
+
content: str | None = None
|
|
15
|
+
section_title: str | None = None
|
|
16
|
+
section_path: tuple[str, ...] = ()
|
|
17
|
+
section_level: int | None = None
|
|
18
|
+
section_index: int | None = None
|
|
19
|
+
root_source_id: str | None = None
|
|
20
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Connector(Protocol):
|
|
24
|
+
source_type: str
|
|
25
|
+
|
|
26
|
+
def scan(self, path: Path) -> list[SourceRef]:
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def load(self, corpus_id: str, ref: SourceRef) -> BronzeDocument:
|
|
30
|
+
...
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Literal, Protocol
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from ame.bronze.schema import BronzeDocument
|
|
10
|
+
from ame.bronze.store import BronzeStore
|
|
11
|
+
from ame.connectors.base import Connector, SourceRef
|
|
12
|
+
from ame.connectors.sync_history import ConnectorSyncStore
|
|
13
|
+
from ame.core.corpus import require_corpus
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConnectorProfile(BaseModel):
|
|
17
|
+
name: str
|
|
18
|
+
source_type: str
|
|
19
|
+
mode: Literal["export", "live"] = "export"
|
|
20
|
+
version: str = "1"
|
|
21
|
+
features: list[str] = Field(default_factory=list)
|
|
22
|
+
contract: list[str] = Field(
|
|
23
|
+
default_factory=lambda: ["connect", "fetch", "normalize_to_bronze", "diff", "sync"]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ConnectorConnection(BaseModel):
|
|
28
|
+
profile: ConnectorProfile
|
|
29
|
+
path: Path
|
|
30
|
+
connected_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ConnectorDiff(BaseModel):
|
|
34
|
+
connector: str
|
|
35
|
+
source_type: str
|
|
36
|
+
source_path: Path
|
|
37
|
+
fetched: int
|
|
38
|
+
new: list[str] = Field(default_factory=list)
|
|
39
|
+
changed: list[str] = Field(default_factory=list)
|
|
40
|
+
unchanged: list[str] = Field(default_factory=list)
|
|
41
|
+
removed: list[str] = Field(default_factory=list)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def counts(self) -> dict[str, int]:
|
|
45
|
+
return {
|
|
46
|
+
"fetched": self.fetched,
|
|
47
|
+
"new": len(self.new),
|
|
48
|
+
"changed": len(self.changed),
|
|
49
|
+
"unchanged": len(self.unchanged),
|
|
50
|
+
"removed": len(self.removed),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ConnectorContractSyncReport(BaseModel):
|
|
55
|
+
corpus_id: str
|
|
56
|
+
connector: str
|
|
57
|
+
source_type: str
|
|
58
|
+
source_path: Path
|
|
59
|
+
fetched: int
|
|
60
|
+
new: int
|
|
61
|
+
changed: int
|
|
62
|
+
unchanged: int
|
|
63
|
+
removed: int
|
|
64
|
+
ingested_documents: int
|
|
65
|
+
sync_run_id: str | None = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ContractConnector(Protocol):
|
|
69
|
+
profile: ConnectorProfile
|
|
70
|
+
|
|
71
|
+
def connect(self, path: Path) -> ConnectorConnection:
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
def fetch(self, path: Path) -> list[SourceRef]:
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
def normalize_to_bronze(self, corpus_id: str, ref: SourceRef) -> BronzeDocument:
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
def diff(self, corpus_id: str, path: Path) -> ConnectorDiff:
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
def sync(self, corpus_id: str, path: Path) -> ConnectorContractSyncReport:
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ExportConnectorRuntime:
|
|
88
|
+
def __init__(self, connector: Connector, profile: ConnectorProfile):
|
|
89
|
+
self.connector = connector
|
|
90
|
+
self.profile = profile
|
|
91
|
+
|
|
92
|
+
def connect(self, path: Path) -> ConnectorConnection:
|
|
93
|
+
return ConnectorConnection(profile=self.profile, path=path.expanduser().resolve())
|
|
94
|
+
|
|
95
|
+
def fetch(self, path: Path) -> list[SourceRef]:
|
|
96
|
+
return self.connector.scan(path)
|
|
97
|
+
|
|
98
|
+
def normalize_to_bronze(self, corpus_id: str, ref: SourceRef) -> BronzeDocument:
|
|
99
|
+
return self.connector.load(corpus_id, ref)
|
|
100
|
+
|
|
101
|
+
def diff(self, corpus_id: str, path: Path) -> ConnectorDiff:
|
|
102
|
+
corpus_root = require_corpus(corpus_id)
|
|
103
|
+
refs = self.fetch(path)
|
|
104
|
+
fetched_docs = [self.normalize_to_bronze(corpus_id, ref) for ref in refs]
|
|
105
|
+
existing_docs = [doc for doc in BronzeStore(corpus_root).list() if doc.source_type == self.connector.source_type]
|
|
106
|
+
existing_by_source = {doc.source_id: doc for doc in existing_docs}
|
|
107
|
+
fetched_source_ids = {doc.source_id for doc in fetched_docs}
|
|
108
|
+
|
|
109
|
+
new: list[str] = []
|
|
110
|
+
changed: list[str] = []
|
|
111
|
+
unchanged: list[str] = []
|
|
112
|
+
for doc in fetched_docs:
|
|
113
|
+
existing = existing_by_source.get(doc.source_id)
|
|
114
|
+
if existing is None:
|
|
115
|
+
new.append(doc.source_id)
|
|
116
|
+
elif existing.content_hash == doc.content_hash:
|
|
117
|
+
unchanged.append(doc.source_id)
|
|
118
|
+
else:
|
|
119
|
+
changed.append(doc.source_id)
|
|
120
|
+
|
|
121
|
+
removed = sorted(doc.source_id for doc in existing_docs if doc.source_id not in fetched_source_ids)
|
|
122
|
+
return ConnectorDiff(
|
|
123
|
+
connector=self.profile.name,
|
|
124
|
+
source_type=self.connector.source_type,
|
|
125
|
+
source_path=path,
|
|
126
|
+
fetched=len(refs),
|
|
127
|
+
new=sorted(new),
|
|
128
|
+
changed=sorted(changed),
|
|
129
|
+
unchanged=sorted(unchanged),
|
|
130
|
+
removed=removed,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def sync(self, corpus_id: str, path: Path) -> ConnectorContractSyncReport:
|
|
134
|
+
corpus_root = require_corpus(corpus_id)
|
|
135
|
+
started_at = datetime.now(timezone.utc)
|
|
136
|
+
sync_store = ConnectorSyncStore(corpus_root)
|
|
137
|
+
diff = self.diff(corpus_id, path)
|
|
138
|
+
ingested = 0
|
|
139
|
+
try:
|
|
140
|
+
from ame.pipeline import MemoryPipeline
|
|
141
|
+
|
|
142
|
+
ingest_report = MemoryPipeline().ingest(corpus_id, path, profile=self.profile.name)
|
|
143
|
+
ingested = ingest_report.documents
|
|
144
|
+
run = sync_store.record(
|
|
145
|
+
connector=self.profile.name,
|
|
146
|
+
status="success",
|
|
147
|
+
started_at=started_at,
|
|
148
|
+
source=str(path),
|
|
149
|
+
counts={
|
|
150
|
+
"fetched": diff.fetched,
|
|
151
|
+
"new": len(diff.new),
|
|
152
|
+
"changed": len(diff.changed),
|
|
153
|
+
"unchanged": len(diff.unchanged),
|
|
154
|
+
"removed": len(diff.removed),
|
|
155
|
+
"ingested_documents": ingested,
|
|
156
|
+
},
|
|
157
|
+
metadata={"source_type": self.connector.source_type, "profile": self.profile.name},
|
|
158
|
+
)
|
|
159
|
+
return self._report(corpus_id, path, diff, ingested, run.id)
|
|
160
|
+
except Exception as exc:
|
|
161
|
+
sync_store.record(
|
|
162
|
+
connector=self.profile.name,
|
|
163
|
+
status="failed",
|
|
164
|
+
started_at=started_at,
|
|
165
|
+
source=str(path),
|
|
166
|
+
counts={
|
|
167
|
+
"fetched": diff.fetched,
|
|
168
|
+
"new": len(diff.new),
|
|
169
|
+
"changed": len(diff.changed),
|
|
170
|
+
"unchanged": len(diff.unchanged),
|
|
171
|
+
"removed": len(diff.removed),
|
|
172
|
+
"ingested_documents": ingested,
|
|
173
|
+
},
|
|
174
|
+
metadata={"source_type": self.connector.source_type, "profile": self.profile.name},
|
|
175
|
+
error=str(exc),
|
|
176
|
+
)
|
|
177
|
+
raise
|
|
178
|
+
|
|
179
|
+
def _report(
|
|
180
|
+
self,
|
|
181
|
+
corpus_id: str,
|
|
182
|
+
path: Path,
|
|
183
|
+
diff: ConnectorDiff,
|
|
184
|
+
ingested_documents: int,
|
|
185
|
+
sync_run_id: str,
|
|
186
|
+
) -> ConnectorContractSyncReport:
|
|
187
|
+
return ConnectorContractSyncReport(
|
|
188
|
+
corpus_id=corpus_id,
|
|
189
|
+
connector=self.profile.name,
|
|
190
|
+
source_type=self.connector.source_type,
|
|
191
|
+
source_path=path,
|
|
192
|
+
fetched=diff.fetched,
|
|
193
|
+
new=len(diff.new),
|
|
194
|
+
changed=len(diff.changed),
|
|
195
|
+
unchanged=len(diff.unchanged),
|
|
196
|
+
removed=len(diff.removed),
|
|
197
|
+
ingested_documents=ingested_documents,
|
|
198
|
+
sync_run_id=sync_run_id,
|
|
199
|
+
)
|
ame/connectors/github.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ame.bronze.schema import BronzeDocument
|
|
8
|
+
from ame.connectors.base import SourceRef
|
|
9
|
+
from ame.connectors.json_helpers import as_list, first_present, read_json
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GitHubConnector:
|
|
13
|
+
source_type = "github"
|
|
14
|
+
|
|
15
|
+
def scan(self, path: Path) -> list[SourceRef]:
|
|
16
|
+
root = path.expanduser().resolve()
|
|
17
|
+
files = [root] if root.is_file() else sorted(root.rglob("*.json"))
|
|
18
|
+
refs: list[SourceRef] = []
|
|
19
|
+
for file in files:
|
|
20
|
+
for row in as_list(read_json(file)):
|
|
21
|
+
if not isinstance(row, dict):
|
|
22
|
+
continue
|
|
23
|
+
number = str(first_present(row, "number", "id") or file.stem)
|
|
24
|
+
kind = str(first_present(row, "kind", "type") or ("pull_request" if row.get("pull_request") else "issue"))
|
|
25
|
+
refs.append(SourceRef(path=file, source_id=f"github:{kind}:{number}", content=self._item_content(row, kind, number)))
|
|
26
|
+
return refs
|
|
27
|
+
|
|
28
|
+
def load(self, corpus_id: str, ref: SourceRef) -> BronzeDocument:
|
|
29
|
+
content = ref.content or ref.path.read_text(encoding="utf-8")
|
|
30
|
+
digest = hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
31
|
+
return BronzeDocument(
|
|
32
|
+
id=f"bronze_{digest[:16]}",
|
|
33
|
+
corpus_id=corpus_id,
|
|
34
|
+
source_type=self.source_type,
|
|
35
|
+
source_id=ref.source_id,
|
|
36
|
+
content=content,
|
|
37
|
+
metadata={"path": str(ref.path), "title": ref.source_id, "connector": "github"},
|
|
38
|
+
content_hash=f"sha256:{digest}",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def _item_content(self, row: dict[str, Any], kind: str, number: str) -> str:
|
|
42
|
+
title = str(first_present(row, "title", "summary", "name") or f"{kind} {number}")
|
|
43
|
+
body = str(first_present(row, "body", "description", "content") or "")
|
|
44
|
+
state = first_present(row, "state", "status")
|
|
45
|
+
comments = first_present(row, "comments", "discussion") or []
|
|
46
|
+
if isinstance(comments, dict):
|
|
47
|
+
comments = as_list(comments)
|
|
48
|
+
comment_text = "\n".join(f"- {first_present(comment, 'body', 'text', 'content')}" for comment in comments if isinstance(comment, dict))
|
|
49
|
+
return "\n".join(
|
|
50
|
+
[
|
|
51
|
+
"---",
|
|
52
|
+
f"title: {title}",
|
|
53
|
+
f"github_kind: {kind}",
|
|
54
|
+
f"github_number: {number}",
|
|
55
|
+
f"state: {state or ''}",
|
|
56
|
+
"---",
|
|
57
|
+
"",
|
|
58
|
+
f"# {title}",
|
|
59
|
+
"",
|
|
60
|
+
body,
|
|
61
|
+
"",
|
|
62
|
+
"## Comments",
|
|
63
|
+
comment_text,
|
|
64
|
+
"",
|
|
65
|
+
]
|
|
66
|
+
)
|