alma-memory 0.3.0__py3-none-any.whl → 0.5.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.
Files changed (77) hide show
  1. alma/__init__.py +99 -29
  2. alma/confidence/__init__.py +47 -0
  3. alma/confidence/engine.py +540 -0
  4. alma/confidence/types.py +351 -0
  5. alma/config/loader.py +3 -2
  6. alma/consolidation/__init__.py +23 -0
  7. alma/consolidation/engine.py +678 -0
  8. alma/consolidation/prompts.py +84 -0
  9. alma/core.py +15 -15
  10. alma/domains/__init__.py +6 -6
  11. alma/domains/factory.py +12 -9
  12. alma/domains/schemas.py +17 -3
  13. alma/domains/types.py +8 -4
  14. alma/events/__init__.py +75 -0
  15. alma/events/emitter.py +284 -0
  16. alma/events/storage_mixin.py +246 -0
  17. alma/events/types.py +126 -0
  18. alma/events/webhook.py +425 -0
  19. alma/exceptions.py +49 -0
  20. alma/extraction/__init__.py +31 -0
  21. alma/extraction/auto_learner.py +264 -0
  22. alma/extraction/extractor.py +420 -0
  23. alma/graph/__init__.py +81 -0
  24. alma/graph/backends/__init__.py +18 -0
  25. alma/graph/backends/memory.py +236 -0
  26. alma/graph/backends/neo4j.py +417 -0
  27. alma/graph/base.py +159 -0
  28. alma/graph/extraction.py +198 -0
  29. alma/graph/store.py +860 -0
  30. alma/harness/__init__.py +4 -4
  31. alma/harness/base.py +18 -9
  32. alma/harness/domains.py +27 -11
  33. alma/initializer/__init__.py +37 -0
  34. alma/initializer/initializer.py +418 -0
  35. alma/initializer/types.py +250 -0
  36. alma/integration/__init__.py +9 -9
  37. alma/integration/claude_agents.py +10 -10
  38. alma/integration/helena.py +32 -22
  39. alma/integration/victor.py +57 -33
  40. alma/learning/__init__.py +27 -27
  41. alma/learning/forgetting.py +198 -148
  42. alma/learning/heuristic_extractor.py +40 -24
  43. alma/learning/protocols.py +62 -14
  44. alma/learning/validation.py +7 -2
  45. alma/mcp/__init__.py +4 -4
  46. alma/mcp/__main__.py +2 -1
  47. alma/mcp/resources.py +17 -16
  48. alma/mcp/server.py +102 -44
  49. alma/mcp/tools.py +174 -37
  50. alma/progress/__init__.py +3 -3
  51. alma/progress/tracker.py +26 -20
  52. alma/progress/types.py +8 -12
  53. alma/py.typed +0 -0
  54. alma/retrieval/__init__.py +11 -11
  55. alma/retrieval/cache.py +20 -21
  56. alma/retrieval/embeddings.py +4 -4
  57. alma/retrieval/engine.py +114 -35
  58. alma/retrieval/scoring.py +73 -63
  59. alma/session/__init__.py +2 -2
  60. alma/session/manager.py +5 -5
  61. alma/session/types.py +5 -4
  62. alma/storage/__init__.py +41 -0
  63. alma/storage/azure_cosmos.py +107 -31
  64. alma/storage/base.py +157 -4
  65. alma/storage/chroma.py +1443 -0
  66. alma/storage/file_based.py +56 -20
  67. alma/storage/pinecone.py +1080 -0
  68. alma/storage/postgresql.py +1452 -0
  69. alma/storage/qdrant.py +1306 -0
  70. alma/storage/sqlite_local.py +376 -31
  71. alma/types.py +62 -14
  72. alma_memory-0.5.0.dist-info/METADATA +905 -0
  73. alma_memory-0.5.0.dist-info/RECORD +76 -0
  74. {alma_memory-0.3.0.dist-info → alma_memory-0.5.0.dist-info}/WHEEL +1 -1
  75. alma_memory-0.3.0.dist-info/METADATA +0 -438
  76. alma_memory-0.3.0.dist-info/RECORD +0 -46
  77. {alma_memory-0.3.0.dist-info → alma_memory-0.5.0.dist-info}/top_level.txt +0 -0
@@ -6,21 +6,19 @@ No vector search - uses basic text matching for retrieval.
6
6
  """
7
7
 
8
8
  import json
9
- import uuid
10
9
  import logging
11
- from pathlib import Path
12
10
  from datetime import datetime, timezone
13
- from typing import Optional, List, Dict, Any
14
- from dataclasses import asdict
11
+ from pathlib import Path
12
+ from typing import Any, Dict, List, Optional
15
13
 
14
+ from alma.storage.base import StorageBackend
16
15
  from alma.types import (
16
+ AntiPattern,
17
+ DomainKnowledge,
17
18
  Heuristic,
18
19
  Outcome,
19
20
  UserPreference,
20
- DomainKnowledge,
21
- AntiPattern,
22
21
  )
23
- from alma.storage.base import StorageBackend
24
22
 
25
23
  logger = logging.getLogger(__name__)
26
24
 
@@ -74,46 +72,86 @@ class FileBasedStorage(StorageBackend):
74
72
  # ==================== WRITE OPERATIONS ====================
75
73
 
76
74
  def save_heuristic(self, heuristic: Heuristic) -> str:
77
- """Save a heuristic."""
75
+ """Save a heuristic (UPSERT - update if exists, insert if new)."""
78
76
  data = self._read_json(self._files["heuristics"])
79
77
  record = self._to_dict(heuristic)
80
- data.append(record)
78
+ # Find and replace existing, or append new
79
+ found = False
80
+ for i, existing in enumerate(data):
81
+ if existing.get("id") == record["id"]:
82
+ data[i] = record
83
+ found = True
84
+ break
85
+ if not found:
86
+ data.append(record)
81
87
  self._write_json(self._files["heuristics"], data)
82
88
  logger.debug(f"Saved heuristic: {heuristic.id}")
83
89
  return heuristic.id
84
90
 
85
91
  def save_outcome(self, outcome: Outcome) -> str:
86
- """Save an outcome."""
92
+ """Save an outcome (UPSERT - update if exists, insert if new)."""
87
93
  data = self._read_json(self._files["outcomes"])
88
94
  record = self._to_dict(outcome)
89
- data.append(record)
95
+ # Find and replace existing, or append new
96
+ found = False
97
+ for i, existing in enumerate(data):
98
+ if existing.get("id") == record["id"]:
99
+ data[i] = record
100
+ found = True
101
+ break
102
+ if not found:
103
+ data.append(record)
90
104
  self._write_json(self._files["outcomes"], data)
91
105
  logger.debug(f"Saved outcome: {outcome.id}")
92
106
  return outcome.id
93
107
 
94
108
  def save_user_preference(self, preference: UserPreference) -> str:
95
- """Save a user preference."""
109
+ """Save a user preference (UPSERT - update if exists, insert if new)."""
96
110
  data = self._read_json(self._files["preferences"])
97
111
  record = self._to_dict(preference)
98
- data.append(record)
112
+ # Find and replace existing, or append new
113
+ found = False
114
+ for i, existing in enumerate(data):
115
+ if existing.get("id") == record["id"]:
116
+ data[i] = record
117
+ found = True
118
+ break
119
+ if not found:
120
+ data.append(record)
99
121
  self._write_json(self._files["preferences"], data)
100
122
  logger.debug(f"Saved preference: {preference.id}")
101
123
  return preference.id
102
124
 
103
125
  def save_domain_knowledge(self, knowledge: DomainKnowledge) -> str:
104
- """Save domain knowledge."""
126
+ """Save domain knowledge (UPSERT - update if exists, insert if new)."""
105
127
  data = self._read_json(self._files["domain_knowledge"])
106
128
  record = self._to_dict(knowledge)
107
- data.append(record)
129
+ # Find and replace existing, or append new
130
+ found = False
131
+ for i, existing in enumerate(data):
132
+ if existing.get("id") == record["id"]:
133
+ data[i] = record
134
+ found = True
135
+ break
136
+ if not found:
137
+ data.append(record)
108
138
  self._write_json(self._files["domain_knowledge"], data)
109
139
  logger.debug(f"Saved domain knowledge: {knowledge.id}")
110
140
  return knowledge.id
111
141
 
112
142
  def save_anti_pattern(self, anti_pattern: AntiPattern) -> str:
113
- """Save an anti-pattern."""
143
+ """Save an anti-pattern (UPSERT - update if exists, insert if new)."""
114
144
  data = self._read_json(self._files["anti_patterns"])
115
145
  record = self._to_dict(anti_pattern)
116
- data.append(record)
146
+ # Find and replace existing, or append new
147
+ found = False
148
+ for i, existing in enumerate(data):
149
+ if existing.get("id") == record["id"]:
150
+ data[i] = record
151
+ found = True
152
+ break
153
+ if not found:
154
+ data.append(record)
117
155
  self._write_json(self._files["anti_patterns"], data)
118
156
  logger.debug(f"Saved anti-pattern: {anti_pattern.id}")
119
157
  return anti_pattern.id
@@ -451,9 +489,7 @@ class FileBasedStorage(StorageBackend):
451
489
  count += 1
452
490
  stats[f"{name}_count"] = count
453
491
 
454
- stats["total_count"] = sum(
455
- stats[k] for k in stats if k.endswith("_count")
456
- )
492
+ stats["total_count"] = sum(stats[k] for k in stats if k.endswith("_count"))
457
493
 
458
494
  return stats
459
495