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.
Files changed (72) hide show
  1. adaptive_memory_engine-0.1.6.dist-info/METADATA +228 -0
  2. adaptive_memory_engine-0.1.6.dist-info/RECORD +72 -0
  3. adaptive_memory_engine-0.1.6.dist-info/WHEEL +4 -0
  4. adaptive_memory_engine-0.1.6.dist-info/entry_points.txt +3 -0
  5. adaptive_memory_engine-0.1.6.dist-info/licenses/LICENSE +21 -0
  6. ame/__init__.py +1 -0
  7. ame/agent/__init__.py +1 -0
  8. ame/agent/mcp.py +474 -0
  9. ame/agent/memory_api.py +141 -0
  10. ame/agent/results.py +30 -0
  11. ame/bronze/schema.py +17 -0
  12. ame/bronze/store.py +38 -0
  13. ame/cli/__init__.py +1 -0
  14. ame/cli/main.py +903 -0
  15. ame/connectors/base.py +30 -0
  16. ame/connectors/contract.py +199 -0
  17. ame/connectors/github.py +66 -0
  18. ame/connectors/google.py +464 -0
  19. ame/connectors/google_oauth.py +156 -0
  20. ame/connectors/jira.py +66 -0
  21. ame/connectors/json_helpers.py +43 -0
  22. ame/connectors/markdown.py +116 -0
  23. ame/connectors/notion.py +59 -0
  24. ame/connectors/oauth_callback.py +102 -0
  25. ame/connectors/oauth_provider.py +250 -0
  26. ame/connectors/obsidian.py +19 -0
  27. ame/connectors/router.py +155 -0
  28. ame/connectors/slack.py +66 -0
  29. ame/connectors/slack_oauth.py +417 -0
  30. ame/connectors/sync_history.py +73 -0
  31. ame/context_budget.py +106 -0
  32. ame/core/config.py +77 -0
  33. ame/core/corpus.py +17 -0
  34. ame/core/errors.py +18 -0
  35. ame/core/paths.py +111 -0
  36. ame/core/state.py +57 -0
  37. ame/export/obsidian.py +123 -0
  38. ame/gold/builder.py +300 -0
  39. ame/gold/ontology.py +80 -0
  40. ame/gold/resolver.py +91 -0
  41. ame/gold/schema.py +40 -0
  42. ame/gold/store.py +45 -0
  43. ame/hardware/profiler.py +85 -0
  44. ame/hardware/tier.py +27 -0
  45. ame/hermes/__init__.py +3 -0
  46. ame/hermes/memory.py +209 -0
  47. ame/models/download.py +243 -0
  48. ame/models/ollama.py +60 -0
  49. ame/models/registry.py +101 -0
  50. ame/models/router.py +22 -0
  51. ame/pipeline.py +155 -0
  52. ame/query/diff.py +40 -0
  53. ame/query/engine.py +919 -0
  54. ame/query/memory_os.py +313 -0
  55. ame/query/mql.py +84 -0
  56. ame/query/multihop.py +264 -0
  57. ame/query/result.py +20 -0
  58. ame/sdk.py +52 -0
  59. ame/security.py +145 -0
  60. ame/silver/extractor.py +414 -0
  61. ame/silver/llm_extractor.py +181 -0
  62. ame/silver/prompts.py +56 -0
  63. ame/silver/rationale.py +140 -0
  64. ame/silver/schema.py +51 -0
  65. ame/silver/store.py +59 -0
  66. ame/storage/custom_kg.py +33 -0
  67. ame/storage/lightrag_adapter.py +362 -0
  68. ame/validation/confidence.py +5 -0
  69. ame/validation/grounding.py +10 -0
  70. ame/validation/type_gate.py +22 -0
  71. ame/writeback.py +173 -0
  72. memory/__init__.py +3 -0
ame/pipeline.py ADDED
@@ -0,0 +1,155 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Literal
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from ame.bronze.store import BronzeStore
9
+ from ame.connectors.router import ConnectorRouter
10
+ from ame.core.config import load_config
11
+ from ame.core.corpus import require_corpus
12
+ from ame.core.state import CorpusStateStore, IngestedDocumentState
13
+ from ame.gold.builder import GoldBuilder
14
+ from ame.gold.store import GoldStore
15
+ from ame.hardware.profiler import HardwareProfiler
16
+ from ame.models.ollama import OllamaClient
17
+ from ame.models.registry import load_default_registry
18
+ from ame.models.router import ModelRouter
19
+ from ame.silver.extractor import DeterministicExtractor
20
+ from ame.silver.llm_extractor import LlmClient, LlmExtractor
21
+ from ame.silver.rationale import RationaleExtractor
22
+ from ame.silver.schema import SilverEntity
23
+ from ame.silver.store import SilverStore
24
+ from ame.storage.lightrag_adapter import LightRagAdapter
25
+ from ame.validation.confidence import passes_confidence
26
+ from ame.validation.grounding import is_grounded
27
+ from ame.validation.type_gate import relation_type_valid
28
+
29
+
30
+ class IngestReport(BaseModel):
31
+ mode: str
32
+ documents: int
33
+ silver_entities: int
34
+ silver_relations: int
35
+ silver_decisions: int
36
+ silver_rationales: int
37
+ rejected: int
38
+ gold_nodes: int
39
+ gold_edges: int
40
+ custom_kg_path: Path
41
+
42
+
43
+ class MemoryPipeline:
44
+ def ingest(
45
+ self,
46
+ corpus_id: str,
47
+ source_path: Path,
48
+ mode: Literal["deterministic", "llm"] = "deterministic",
49
+ llm_client: LlmClient | None = None,
50
+ profile: str | None = None,
51
+ ) -> IngestReport:
52
+ root = require_corpus(corpus_id)
53
+ config = load_config()
54
+ connector = ConnectorRouter().resolve(source_path, profile)
55
+ bronze = BronzeStore(root)
56
+ extractor = DeterministicExtractor() if mode == "deterministic" else LlmExtractor(llm_client or self._default_llm_client(config))
57
+
58
+ docs_by_id = {}
59
+ for ref in connector.scan(source_path):
60
+ doc = bronze.put(connector.load(corpus_id, ref))
61
+ docs_by_id[doc.id] = doc
62
+ docs = list(bronze.list())
63
+ doc_map = {doc.id: doc for doc in docs}
64
+ entities = []
65
+ relations = []
66
+ decisions = []
67
+ rejected: list[dict] = []
68
+
69
+ for doc in docs:
70
+ extracted_entities, extracted_relations, extracted_decisions = extractor.extract(doc)
71
+ valid_entities = []
72
+ for entity in extracted_entities:
73
+ if not is_grounded(entity, doc_map) or not passes_confidence(entity.confidence, config.engine.confidence_threshold):
74
+ rejected.append({"kind": "entity", "id": entity.id, "reason": "validation_failed"})
75
+ continue
76
+ valid_entities.append(entity)
77
+ valid_decisions = []
78
+ for decision in extracted_decisions:
79
+ if not passes_confidence(decision.confidence, config.engine.confidence_threshold):
80
+ rejected.append({"kind": "decision", "id": decision.id, "reason": "validation_failed"})
81
+ continue
82
+ valid_decisions.append(decision)
83
+ decision_entities = [
84
+ SilverEntity(
85
+ id=f"entity_{decision.id}",
86
+ corpus_id=decision.corpus_id,
87
+ type="Decision",
88
+ name=decision.title,
89
+ span=decision.title if decision.title in doc.content else None,
90
+ source_ids=decision.source_ids,
91
+ confidence=decision.confidence,
92
+ )
93
+ for decision in valid_decisions
94
+ ]
95
+ validation_entities = valid_entities + decision_entities
96
+ for relation in extracted_relations:
97
+ if not passes_confidence(relation.confidence, config.engine.confidence_threshold) or not relation_type_valid(relation, validation_entities):
98
+ rejected.append(
99
+ {
100
+ "kind": "relation",
101
+ "id": relation.id,
102
+ "reason": "validation_failed",
103
+ "subject": relation.subject,
104
+ "predicate": relation.predicate,
105
+ "object": relation.object,
106
+ }
107
+ )
108
+ continue
109
+ relations.append(relation)
110
+ entities.extend(valid_entities)
111
+ decisions.extend(valid_decisions)
112
+
113
+ rationales = RationaleExtractor().extract(docs, decisions)
114
+ SilverStore(root).replace(entities, relations, decisions, rejected, rationales)
115
+ nodes, edges, timeline = GoldBuilder().build(entities, relations, decisions, rationales)
116
+ GoldStore(root).replace(nodes, edges, timeline)
117
+ kg_path = LightRagAdapter(root).sync(nodes, edges, docs)
118
+ counts = {
119
+ "documents": len(docs),
120
+ "silver_entities": len(entities),
121
+ "silver_relations": len(relations),
122
+ "silver_decisions": len(decisions),
123
+ "silver_rationales": len(rationales),
124
+ "rejected": len(rejected),
125
+ "gold_nodes": len(nodes),
126
+ "gold_edges": len(edges),
127
+ }
128
+ CorpusStateStore(root).record_ingest(
129
+ source_path=source_path,
130
+ mode=mode,
131
+ documents=[
132
+ IngestedDocumentState(id=doc.id, source_id=doc.source_id, content_hash=doc.content_hash)
133
+ for doc in docs
134
+ ],
135
+ counts=counts,
136
+ )
137
+
138
+ return IngestReport(
139
+ mode=mode,
140
+ documents=len(docs),
141
+ silver_entities=len(entities),
142
+ silver_relations=len(relations),
143
+ silver_decisions=len(decisions),
144
+ silver_rationales=len(rationales),
145
+ rejected=len(rejected),
146
+ gold_nodes=len(nodes),
147
+ gold_edges=len(edges),
148
+ custom_kg_path=kg_path,
149
+ )
150
+
151
+ def _default_llm_client(self, config) -> OllamaClient:
152
+ registry = load_default_registry()
153
+ profile = HardwareProfiler().profile(Path.home())
154
+ plan = ModelRouter(registry).plan(profile)
155
+ return OllamaClient(model=plan.models.extract.model, base_url=config.lightrag.ollama_host)
ame/query/diff.py ADDED
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import date, timedelta
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+ from ame.gold.schema import GoldTimelineEvent
8
+
9
+
10
+ class MemoryDiff(BaseModel):
11
+ window_days: int
12
+ since: str | None = None
13
+ accepted_decisions: list[str] = Field(default_factory=list)
14
+ superseded_decisions: list[str] = Field(default_factory=list)
15
+ current_false_decisions: list[str] = Field(default_factory=list)
16
+
17
+
18
+ class MemoryDiffEngine:
19
+ def diff_last_days(self, timeline: list[GoldTimelineEvent], days: int = 7) -> MemoryDiff:
20
+ dated = [(event, self._parse_date(event.valid_from)) for event in timeline if event.valid_from]
21
+ if not dated:
22
+ return MemoryDiff(window_days=days)
23
+ newest = max(day for _, day in dated if day is not None)
24
+ since = newest - timedelta(days=days)
25
+ in_window = [event for event, day in dated if day is not None and day >= since]
26
+ return MemoryDiff(
27
+ window_days=days,
28
+ since=since.isoformat(),
29
+ accepted_decisions=[event.title for event in in_window if event.status == "accepted"],
30
+ superseded_decisions=[event.title for event in in_window if event.supersedes],
31
+ current_false_decisions=[event.title for event in timeline if event.current is False and event.superseded_by],
32
+ )
33
+
34
+ def _parse_date(self, value: str | None) -> date | None:
35
+ if not value:
36
+ return None
37
+ try:
38
+ return date.fromisoformat(value)
39
+ except ValueError:
40
+ return None