alma-memory 0.5.0__tar.gz → 0.5.1__tar.gz

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 (99) hide show
  1. {alma_memory-0.5.0 → alma_memory-0.5.1}/PKG-INFO +42 -8
  2. {alma_memory-0.5.0 → alma_memory-0.5.1}/README.md +36 -6
  3. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/__init__.py +33 -1
  4. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/core.py +124 -16
  5. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/extraction/auto_learner.py +4 -3
  6. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/graph/__init__.py +26 -1
  7. alma_memory-0.5.1/alma/graph/backends/__init__.py +32 -0
  8. alma_memory-0.5.1/alma/graph/backends/kuzu.py +624 -0
  9. alma_memory-0.5.1/alma/graph/backends/memgraph.py +432 -0
  10. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/integration/claude_agents.py +22 -10
  11. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/learning/protocols.py +3 -3
  12. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/mcp/tools.py +9 -11
  13. alma_memory-0.5.1/alma/observability/__init__.py +84 -0
  14. alma_memory-0.5.1/alma/observability/config.py +302 -0
  15. alma_memory-0.5.1/alma/observability/logging.py +424 -0
  16. alma_memory-0.5.1/alma/observability/metrics.py +583 -0
  17. alma_memory-0.5.1/alma/observability/tracing.py +440 -0
  18. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/retrieval/engine.py +65 -4
  19. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/__init__.py +29 -0
  20. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/azure_cosmos.py +343 -132
  21. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/base.py +58 -0
  22. alma_memory-0.5.1/alma/storage/constants.py +103 -0
  23. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/file_based.py +3 -8
  24. alma_memory-0.5.1/alma/storage/migrations/__init__.py +21 -0
  25. alma_memory-0.5.1/alma/storage/migrations/base.py +321 -0
  26. alma_memory-0.5.1/alma/storage/migrations/runner.py +323 -0
  27. alma_memory-0.5.1/alma/storage/migrations/version_stores.py +337 -0
  28. alma_memory-0.5.1/alma/storage/migrations/versions/__init__.py +11 -0
  29. alma_memory-0.5.1/alma/storage/migrations/versions/v1_0_0.py +373 -0
  30. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/postgresql.py +185 -78
  31. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/sqlite_local.py +149 -50
  32. alma_memory-0.5.1/alma/testing/__init__.py +46 -0
  33. alma_memory-0.5.1/alma/testing/factories.py +301 -0
  34. alma_memory-0.5.1/alma/testing/mocks.py +389 -0
  35. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma_memory.egg-info/PKG-INFO +42 -8
  36. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma_memory.egg-info/SOURCES.txt +17 -0
  37. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma_memory.egg-info/requires.txt +6 -1
  38. {alma_memory-0.5.0 → alma_memory-0.5.1}/pyproject.toml +9 -2
  39. alma_memory-0.5.0/alma/graph/backends/__init__.py +0 -18
  40. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/confidence/__init__.py +0 -0
  41. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/confidence/engine.py +0 -0
  42. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/confidence/types.py +0 -0
  43. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/config/__init__.py +0 -0
  44. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/config/loader.py +0 -0
  45. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/consolidation/__init__.py +0 -0
  46. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/consolidation/engine.py +0 -0
  47. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/consolidation/prompts.py +0 -0
  48. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/domains/__init__.py +0 -0
  49. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/domains/factory.py +0 -0
  50. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/domains/schemas.py +0 -0
  51. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/domains/types.py +0 -0
  52. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/events/__init__.py +0 -0
  53. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/events/emitter.py +0 -0
  54. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/events/storage_mixin.py +0 -0
  55. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/events/types.py +0 -0
  56. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/events/webhook.py +0 -0
  57. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/exceptions.py +0 -0
  58. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/extraction/__init__.py +0 -0
  59. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/extraction/extractor.py +0 -0
  60. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/graph/backends/memory.py +0 -0
  61. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/graph/backends/neo4j.py +0 -0
  62. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/graph/base.py +0 -0
  63. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/graph/extraction.py +0 -0
  64. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/graph/store.py +0 -0
  65. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/harness/__init__.py +0 -0
  66. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/harness/base.py +0 -0
  67. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/harness/domains.py +0 -0
  68. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/initializer/__init__.py +0 -0
  69. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/initializer/initializer.py +0 -0
  70. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/initializer/types.py +0 -0
  71. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/integration/__init__.py +0 -0
  72. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/integration/helena.py +0 -0
  73. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/integration/victor.py +0 -0
  74. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/learning/__init__.py +0 -0
  75. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/learning/forgetting.py +0 -0
  76. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/learning/heuristic_extractor.py +0 -0
  77. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/learning/validation.py +0 -0
  78. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/mcp/__init__.py +0 -0
  79. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/mcp/__main__.py +0 -0
  80. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/mcp/resources.py +0 -0
  81. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/mcp/server.py +0 -0
  82. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/progress/__init__.py +0 -0
  83. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/progress/tracker.py +0 -0
  84. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/progress/types.py +0 -0
  85. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/py.typed +0 -0
  86. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/retrieval/__init__.py +0 -0
  87. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/retrieval/cache.py +0 -0
  88. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/retrieval/embeddings.py +0 -0
  89. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/retrieval/scoring.py +0 -0
  90. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/session/__init__.py +0 -0
  91. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/session/manager.py +0 -0
  92. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/session/types.py +0 -0
  93. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/chroma.py +0 -0
  94. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/pinecone.py +0 -0
  95. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/storage/qdrant.py +0 -0
  96. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma/types.py +0 -0
  97. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma_memory.egg-info/dependency_links.txt +0 -0
  98. {alma_memory-0.5.0 → alma_memory-0.5.1}/alma_memory.egg-info/top_level.txt +0 -0
  99. {alma_memory-0.5.0 → alma_memory-0.5.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alma-memory
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Agent Learning Memory Architecture - Persistent memory for AI agents
5
5
  Author-email: RBKunnela <aiagentsprompt@gmail.com>
6
6
  License: MIT
@@ -41,6 +41,10 @@ Requires-Dist: pinecone>=3.0.0; extra == "pinecone"
41
41
  Provides-Extra: mcp
42
42
  Requires-Dist: pydantic>=2.0.0; extra == "mcp"
43
43
  Requires-Dist: aiohttp>=3.9.0; extra == "mcp"
44
+ Provides-Extra: observability
45
+ Requires-Dist: opentelemetry-api>=1.20.0; extra == "observability"
46
+ Requires-Dist: opentelemetry-sdk>=1.20.0; extra == "observability"
47
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.20.0; extra == "observability"
44
48
  Provides-Extra: dev
45
49
  Requires-Dist: pytest>=7.0.0; extra == "dev"
46
50
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -52,7 +56,7 @@ Requires-Dist: black>=24.0.0; extra == "dev"
52
56
  Requires-Dist: bandit[toml]>=1.7.0; extra == "dev"
53
57
  Requires-Dist: numpy>=1.24.0; extra == "dev"
54
58
  Provides-Extra: all
55
- Requires-Dist: alma-memory[azure,chroma,dev,local,mcp,pinecone,postgres,qdrant]; extra == "all"
59
+ Requires-Dist: alma-memory[azure,chroma,dev,local,mcp,observability,pinecone,postgres,qdrant]; extra == "all"
56
60
 
57
61
  # ALMA - Agent Learning Memory Architecture
58
62
 
@@ -79,7 +83,7 @@ ALMA isn't just another memory framework. Here's what sets it apart from alterna
79
83
  | **Event System** | Webhooks + in-process callbacks | None | React to memory changes in real-time |
80
84
  | **TypeScript SDK** | Full-featured client library | None | First-class JavaScript/TypeScript support |
81
85
  | **Vector DB Support** | 6 backends (PostgreSQL, Qdrant, Pinecone, Chroma, SQLite, Azure) | Limited | Deploy anywhere |
82
- | **Graph Memory** | Pluggable backends (Neo4j, In-memory) | Limited | Entity relationship tracking |
86
+ | **Graph Memory** | Pluggable backends (Neo4j, Memgraph, Kuzu, In-memory) | Limited | Entity relationship tracking |
83
87
  | **Harness Pattern** | Decouples agent from domain memory | None | Reusable agent architecture |
84
88
  | **MCP Integration** | Native stdio/HTTP server | None | Direct Claude Code integration |
85
89
  | **Domain Memory Factory** | 6 pre-built schemas | None | Instant setup for any domain |
@@ -111,9 +115,16 @@ ALMA isn't just another memory framework. Here's what sets it apart from alterna
111
115
  - **Graph Database Abstraction** (`alma/graph/`)
112
116
  - Pluggable `GraphBackend` interface
113
117
  - Neo4j backend for production
118
+ - Memgraph backend for high-performance in-memory graphs
119
+ - Kuzu backend for embedded graph storage (no server required)
114
120
  - In-memory backend for testing
115
121
  - Factory function `create_graph_backend()` for easy setup
116
122
 
123
+ - **Testing Module** (`alma/testing/`)
124
+ - `MockStorage` - In-memory storage backend for isolated testing
125
+ - `MockEmbedder` - Deterministic fake embedding provider
126
+ - Factory functions for creating test data with sensible defaults
127
+
117
128
  ### Phase 1: Core Features
118
129
 
119
130
  - **Memory Consolidation Engine** (`alma/consolidation/`)
@@ -434,14 +445,23 @@ Capture entity relationships for complex reasoning:
434
445
  ```python
435
446
  from alma.graph import create_graph_backend, BackendGraphStore, EntityExtractor
436
447
 
437
- # Create graph backend (Neo4j for production, memory for testing)
448
+ # Create graph backend - multiple options available:
449
+
450
+ # Neo4j (production, hosted)
438
451
  backend = create_graph_backend(
439
452
  "neo4j",
440
453
  uri="neo4j+s://xxx.databases.neo4j.io",
441
454
  username="neo4j",
442
455
  password="your-password"
443
456
  )
444
- # Or for testing:
457
+
458
+ # Memgraph (high-performance, in-memory)
459
+ # backend = create_graph_backend("memgraph", uri="bolt://localhost:7687")
460
+
461
+ # Kuzu (embedded, no server required)
462
+ # backend = create_graph_backend("kuzu", database_path="./my_graph_db")
463
+
464
+ # In-memory (testing)
445
465
  # backend = create_graph_backend("memory")
446
466
 
447
467
  # Create store with backend
@@ -693,9 +713,12 @@ print(f"Recommendation: {signal.recommendation}")
693
713
  | +---------------+ +------------------+ +---------------+ |
694
714
  +-------------------------------------------------------------------------+
695
715
  | GRAPH LAYER |
696
- | +---------------+ +------------------+ |
697
- | | Neo4j | | In-Memory | |
698
- | +---------------+ +------------------+ |
716
+ | +---------------+ +------------------+ +---------------+ |
717
+ | | Neo4j | | Memgraph | | Kuzu | |
718
+ | +---------------+ +------------------+ +---------------+ |
719
+ | +---------------+ |
720
+ | | In-Memory | |
721
+ | +---------------+ |
699
722
  +-------------------------------------------------------------------------+
700
723
  | INTEGRATION LAYER |
701
724
  | +-------------------------------------------------------------------+ |
@@ -819,6 +842,7 @@ chroma:
819
842
  | Pinecone Backend | Serverless vector DB | Done |
820
843
  | Chroma Backend | Lightweight vector DB | Done |
821
844
  | Graph Abstraction | Pluggable graph backends | Done |
845
+ | Testing Module | Mocks and factories for testing | Done |
822
846
 
823
847
  ---
824
848
 
@@ -850,6 +874,16 @@ docker run -p 6333:6333 qdrant/qdrant
850
874
  - Ensure `embedding_dim` in config matches your embedding provider
851
875
  - Local: 384, Azure text-embedding-3-small: 1536
852
876
 
877
+ **Memgraph connection refused**
878
+ ```bash
879
+ # Start Memgraph with Docker
880
+ docker run -p 7687:7687 memgraph/memgraph-mage
881
+ ```
882
+
883
+ **Kuzu database locked**
884
+ - Ensure only one process accesses the database at a time
885
+ - Use `read_only=True` for concurrent read access
886
+
853
887
  ### Debug Logging
854
888
 
855
889
  ```python
@@ -23,7 +23,7 @@ ALMA isn't just another memory framework. Here's what sets it apart from alterna
23
23
  | **Event System** | Webhooks + in-process callbacks | None | React to memory changes in real-time |
24
24
  | **TypeScript SDK** | Full-featured client library | None | First-class JavaScript/TypeScript support |
25
25
  | **Vector DB Support** | 6 backends (PostgreSQL, Qdrant, Pinecone, Chroma, SQLite, Azure) | Limited | Deploy anywhere |
26
- | **Graph Memory** | Pluggable backends (Neo4j, In-memory) | Limited | Entity relationship tracking |
26
+ | **Graph Memory** | Pluggable backends (Neo4j, Memgraph, Kuzu, In-memory) | Limited | Entity relationship tracking |
27
27
  | **Harness Pattern** | Decouples agent from domain memory | None | Reusable agent architecture |
28
28
  | **MCP Integration** | Native stdio/HTTP server | None | Direct Claude Code integration |
29
29
  | **Domain Memory Factory** | 6 pre-built schemas | None | Instant setup for any domain |
@@ -55,9 +55,16 @@ ALMA isn't just another memory framework. Here's what sets it apart from alterna
55
55
  - **Graph Database Abstraction** (`alma/graph/`)
56
56
  - Pluggable `GraphBackend` interface
57
57
  - Neo4j backend for production
58
+ - Memgraph backend for high-performance in-memory graphs
59
+ - Kuzu backend for embedded graph storage (no server required)
58
60
  - In-memory backend for testing
59
61
  - Factory function `create_graph_backend()` for easy setup
60
62
 
63
+ - **Testing Module** (`alma/testing/`)
64
+ - `MockStorage` - In-memory storage backend for isolated testing
65
+ - `MockEmbedder` - Deterministic fake embedding provider
66
+ - Factory functions for creating test data with sensible defaults
67
+
61
68
  ### Phase 1: Core Features
62
69
 
63
70
  - **Memory Consolidation Engine** (`alma/consolidation/`)
@@ -378,14 +385,23 @@ Capture entity relationships for complex reasoning:
378
385
  ```python
379
386
  from alma.graph import create_graph_backend, BackendGraphStore, EntityExtractor
380
387
 
381
- # Create graph backend (Neo4j for production, memory for testing)
388
+ # Create graph backend - multiple options available:
389
+
390
+ # Neo4j (production, hosted)
382
391
  backend = create_graph_backend(
383
392
  "neo4j",
384
393
  uri="neo4j+s://xxx.databases.neo4j.io",
385
394
  username="neo4j",
386
395
  password="your-password"
387
396
  )
388
- # Or for testing:
397
+
398
+ # Memgraph (high-performance, in-memory)
399
+ # backend = create_graph_backend("memgraph", uri="bolt://localhost:7687")
400
+
401
+ # Kuzu (embedded, no server required)
402
+ # backend = create_graph_backend("kuzu", database_path="./my_graph_db")
403
+
404
+ # In-memory (testing)
389
405
  # backend = create_graph_backend("memory")
390
406
 
391
407
  # Create store with backend
@@ -637,9 +653,12 @@ print(f"Recommendation: {signal.recommendation}")
637
653
  | +---------------+ +------------------+ +---------------+ |
638
654
  +-------------------------------------------------------------------------+
639
655
  | GRAPH LAYER |
640
- | +---------------+ +------------------+ |
641
- | | Neo4j | | In-Memory | |
642
- | +---------------+ +------------------+ |
656
+ | +---------------+ +------------------+ +---------------+ |
657
+ | | Neo4j | | Memgraph | | Kuzu | |
658
+ | +---------------+ +------------------+ +---------------+ |
659
+ | +---------------+ |
660
+ | | In-Memory | |
661
+ | +---------------+ |
643
662
  +-------------------------------------------------------------------------+
644
663
  | INTEGRATION LAYER |
645
664
  | +-------------------------------------------------------------------+ |
@@ -763,6 +782,7 @@ chroma:
763
782
  | Pinecone Backend | Serverless vector DB | Done |
764
783
  | Chroma Backend | Lightweight vector DB | Done |
765
784
  | Graph Abstraction | Pluggable graph backends | Done |
785
+ | Testing Module | Mocks and factories for testing | Done |
766
786
 
767
787
  ---
768
788
 
@@ -794,6 +814,16 @@ docker run -p 6333:6333 qdrant/qdrant
794
814
  - Ensure `embedding_dim` in config matches your embedding provider
795
815
  - Local: 384, Azure text-embedding-3-small: 1536
796
816
 
817
+ **Memgraph connection refused**
818
+ ```bash
819
+ # Start Memgraph with Docker
820
+ docker run -p 7687:7687 memgraph/memgraph-mage
821
+ ```
822
+
823
+ **Kuzu database locked**
824
+ - Ensure only one process accesses the database at a time
825
+ - Use `read_only=True` for concurrent read access
826
+
797
827
  ### Debug Logging
798
828
 
799
829
  ```python
@@ -12,9 +12,24 @@ The Harness Pattern:
12
12
 
13
13
  This makes any tool-using agent appear to "learn" by injecting relevant
14
14
  memory slices before each run and updating memory after.
15
+
16
+ Testing Support:
17
+ For testing ALMA integrations, use the `alma.testing` module:
18
+
19
+ from alma.testing import MockStorage, create_test_heuristic
20
+
21
+ def test_my_integration():
22
+ storage = MockStorage()
23
+ heuristic = create_test_heuristic(agent="test-agent")
24
+ storage.save_heuristic(heuristic)
25
+
26
+ Available utilities:
27
+ - MockStorage: In-memory storage backend
28
+ - MockEmbedder: Deterministic fake embeddings
29
+ - create_test_heuristic(), create_test_outcome(), etc.
15
30
  """
16
31
 
17
- __version__ = "0.4.0"
32
+ __version__ = "0.5.1"
18
33
 
19
34
  # Core
20
35
  # Confidence Engine (Phase 12)
@@ -92,6 +107,16 @@ from alma.initializer import (
92
107
  SessionInitializer,
93
108
  )
94
109
 
110
+ # Observability (Phase 20)
111
+ from alma.observability import (
112
+ ALMAMetrics,
113
+ configure_observability,
114
+ get_logger,
115
+ get_metrics,
116
+ get_tracer,
117
+ trace_method,
118
+ )
119
+
95
120
  # Progress Tracking (Phase 10)
96
121
  from alma.progress import (
97
122
  ProgressLog,
@@ -191,4 +216,11 @@ __all__ = [
191
216
  "EmbeddingError",
192
217
  "RetrievalError",
193
218
  "ExtractionError",
219
+ # Observability
220
+ "configure_observability",
221
+ "get_tracer",
222
+ "get_logger",
223
+ "get_metrics",
224
+ "ALMAMetrics",
225
+ "trace_method",
194
226
  ]
@@ -1,22 +1,38 @@
1
1
  """
2
2
  ALMA Core - Main interface for the Agent Learning Memory Architecture.
3
+
4
+ API Return Type Conventions:
5
+ - Create operations: Return created object or raise exception
6
+ - Update operations: Return updated object or raise exception
7
+ - Delete operations: Return bool (success) or int (count), raise on failure
8
+ - Query operations: Return list (empty if none) or object
9
+
10
+ All scope violations raise ScopeViolationError for consistent error handling.
3
11
  """
4
12
 
5
13
  import logging
14
+ import time
6
15
  from typing import Any, Dict, Optional
7
16
 
8
17
  from alma.config.loader import ConfigLoader
18
+ from alma.exceptions import ScopeViolationError
9
19
  from alma.learning.protocols import LearningProtocol
20
+ from alma.observability.logging import get_logger
21
+ from alma.observability.metrics import get_metrics
22
+ from alma.observability.tracing import SpanKind, get_tracer, trace_method
10
23
  from alma.retrieval.engine import RetrievalEngine
11
24
  from alma.storage.base import StorageBackend
12
25
  from alma.types import (
13
26
  DomainKnowledge,
14
27
  MemoryScope,
15
28
  MemorySlice,
29
+ Outcome,
16
30
  UserPreference,
17
31
  )
18
32
 
19
33
  logger = logging.getLogger(__name__)
34
+ structured_logger = get_logger(__name__)
35
+ tracer = get_tracer(__name__)
20
36
 
21
37
 
22
38
  class ALMA:
@@ -124,6 +140,7 @@ class ALMA:
124
140
 
125
141
  return FileBasedStorage.from_config(config)
126
142
 
143
+ @trace_method(name="ALMA.retrieve", kind=SpanKind.INTERNAL)
127
144
  def retrieve(
128
145
  self,
129
146
  task: str,
@@ -143,11 +160,19 @@ class ALMA:
143
160
  Returns:
144
161
  MemorySlice with relevant memories for context injection
145
162
  """
163
+ start_time = time.time()
164
+ metrics = get_metrics()
165
+
146
166
  # Validate agent has a defined scope
147
167
  if agent not in self.scopes:
148
168
  logger.warning(f"Agent '{agent}' has no defined scope, using defaults")
169
+ structured_logger.warning(
170
+ "Agent has no defined scope, using defaults",
171
+ agent=agent,
172
+ project_id=self.project_id,
173
+ )
149
174
 
150
- return self.retrieval.retrieve(
175
+ result = self.retrieval.retrieve(
151
176
  query=task,
152
177
  agent=agent,
153
178
  project_id=self.project_id,
@@ -156,6 +181,30 @@ class ALMA:
156
181
  scope=self.scopes.get(agent),
157
182
  )
158
183
 
184
+ # Record metrics
185
+ duration_ms = (time.time() - start_time) * 1000
186
+ cache_hit = result.retrieval_time_ms < 10 # Approximate cache hit detection
187
+ metrics.record_retrieve_latency(
188
+ duration_ms=duration_ms,
189
+ agent=agent,
190
+ project_id=self.project_id,
191
+ cache_hit=cache_hit,
192
+ items_returned=result.total_items,
193
+ )
194
+
195
+ structured_logger.info(
196
+ "Memory retrieval completed",
197
+ agent=agent,
198
+ project_id=self.project_id,
199
+ task_preview=task[:50] if task else "",
200
+ items_returned=result.total_items,
201
+ duration_ms=duration_ms,
202
+ cache_hit=cache_hit,
203
+ )
204
+
205
+ return result
206
+
207
+ @trace_method(name="ALMA.learn", kind=SpanKind.INTERNAL)
159
208
  def learn(
160
209
  self,
161
210
  agent: str,
@@ -166,7 +215,7 @@ class ALMA:
166
215
  duration_ms: Optional[int] = None,
167
216
  error_message: Optional[str] = None,
168
217
  feedback: Optional[str] = None,
169
- ) -> bool:
218
+ ) -> Outcome:
170
219
  """
171
220
  Learn from a task outcome.
172
221
 
@@ -184,9 +233,15 @@ class ALMA:
184
233
  feedback: User feedback if provided
185
234
 
186
235
  Returns:
187
- True if learning was accepted, False if rejected (scope violation)
236
+ The created Outcome record
237
+
238
+ Raises:
239
+ ScopeViolationError: If learning is outside agent's scope
188
240
  """
189
- result = self.learning.learn(
241
+ start_time = time.time()
242
+ metrics = get_metrics()
243
+
244
+ outcome_record = self.learning.learn(
190
245
  agent=agent,
191
246
  project_id=self.project_id,
192
247
  task=task,
@@ -199,10 +254,28 @@ class ALMA:
199
254
  )
200
255
 
201
256
  # Invalidate cache for this agent/project after learning
202
- if result:
203
- self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
257
+ self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
204
258
 
205
- return result
259
+ # Record metrics
260
+ learn_duration_ms = (time.time() - start_time) * 1000
261
+ metrics.record_learn_operation(
262
+ duration_ms=learn_duration_ms,
263
+ agent=agent,
264
+ project_id=self.project_id,
265
+ memory_type="outcome",
266
+ success=True,
267
+ )
268
+
269
+ structured_logger.info(
270
+ "Learning operation completed",
271
+ agent=agent,
272
+ project_id=self.project_id,
273
+ task_type=task_type,
274
+ outcome=outcome,
275
+ duration_ms=learn_duration_ms,
276
+ )
277
+
278
+ return outcome_record
206
279
 
207
280
  def add_user_preference(
208
281
  self,
@@ -241,7 +314,7 @@ class ALMA:
241
314
  domain: str,
242
315
  fact: str,
243
316
  source: str = "user_stated",
244
- ) -> Optional[DomainKnowledge]:
317
+ ) -> DomainKnowledge:
245
318
  """
246
319
  Add domain knowledge within agent's scope.
247
320
 
@@ -252,13 +325,17 @@ class ALMA:
252
325
  source: How this was learned
253
326
 
254
327
  Returns:
255
- The created DomainKnowledge or None if scope violation
328
+ The created DomainKnowledge
329
+
330
+ Raises:
331
+ ScopeViolationError: If agent is not allowed to learn in this domain
256
332
  """
257
333
  # Check scope
258
334
  scope = self.scopes.get(agent)
259
335
  if scope and not scope.is_allowed(domain):
260
- logger.warning(f"Agent '{agent}' not allowed to learn in domain '{domain}'")
261
- return None
336
+ raise ScopeViolationError(
337
+ f"Agent '{agent}' is not allowed to learn in domain '{domain}'"
338
+ )
262
339
 
263
340
  result = self.learning.add_domain_knowledge(
264
341
  agent=agent,
@@ -269,11 +346,11 @@ class ALMA:
269
346
  )
270
347
 
271
348
  # Invalidate cache for this agent/project after adding knowledge
272
- if result:
273
- self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
349
+ self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
274
350
 
275
351
  return result
276
352
 
353
+ @trace_method(name="ALMA.forget", kind=SpanKind.INTERNAL)
277
354
  def forget(
278
355
  self,
279
356
  agent: Optional[str] = None,
@@ -283,7 +360,8 @@ class ALMA:
283
360
  """
284
361
  Prune stale or low-confidence memories.
285
362
 
286
- Invalidates cache after pruning to ensure fresh retrieval results.
363
+ This is a delete operation that invalidates cache after pruning
364
+ to ensure fresh retrieval results.
287
365
 
288
366
  Args:
289
367
  agent: Specific agent to prune, or None for all
@@ -291,8 +369,14 @@ class ALMA:
291
369
  below_confidence: Remove heuristics below this confidence
292
370
 
293
371
  Returns:
294
- Number of items pruned
372
+ Number of items pruned (0 if nothing was pruned)
373
+
374
+ Raises:
375
+ StorageError: If the delete operation fails
295
376
  """
377
+ start_time = time.time()
378
+ metrics = get_metrics()
379
+
296
380
  count = self.learning.forget(
297
381
  project_id=self.project_id,
298
382
  agent=agent,
@@ -304,17 +388,41 @@ class ALMA:
304
388
  if count > 0:
305
389
  self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
306
390
 
391
+ # Record metrics
392
+ duration_ms = (time.time() - start_time) * 1000
393
+ metrics.record_forget_operation(
394
+ duration_ms=duration_ms,
395
+ agent=agent,
396
+ project_id=self.project_id,
397
+ items_removed=count,
398
+ )
399
+
400
+ structured_logger.info(
401
+ "Forget operation completed",
402
+ agent=agent or "all",
403
+ project_id=self.project_id,
404
+ items_removed=count,
405
+ older_than_days=older_than_days,
406
+ below_confidence=below_confidence,
407
+ duration_ms=duration_ms,
408
+ )
409
+
307
410
  return count
308
411
 
309
412
  def get_stats(self, agent: Optional[str] = None) -> Dict[str, Any]:
310
413
  """
311
414
  Get memory statistics.
312
415
 
416
+ This is a query operation that returns statistics about stored memories.
417
+
313
418
  Args:
314
419
  agent: Specific agent or None for all
315
420
 
316
421
  Returns:
317
- Dict with counts and metadata
422
+ Dict with counts and metadata (always returns a dict, may be empty)
423
+
424
+ Raises:
425
+ StorageError: If the query operation fails
318
426
  """
319
427
  return self.storage.get_stats(
320
428
  project_id=self.project_id,
@@ -208,16 +208,17 @@ class AutoLearner:
208
208
  preference=fact.content,
209
209
  source="auto_extraction",
210
210
  )
211
- return pref.id if pref else None
211
+ return pref.id
212
212
 
213
213
  elif fact.fact_type == FactType.DOMAIN_KNOWLEDGE:
214
+ # add_domain_knowledge now raises ScopeViolationError instead of returning None
214
215
  knowledge = self.alma.add_domain_knowledge(
215
216
  agent=agent,
216
217
  domain=fact.domain or "general",
217
218
  fact=fact.content,
218
219
  source="auto_extraction",
219
220
  )
220
- return knowledge.id if knowledge else None
221
+ return knowledge.id
221
222
 
222
223
  elif fact.fact_type == FactType.OUTCOME:
223
224
  # Outcomes need success/failure info we don't have
@@ -228,7 +229,7 @@ class AutoLearner:
228
229
  fact=fact.content,
229
230
  source="auto_extraction",
230
231
  )
231
- return knowledge.id if knowledge else None
232
+ return knowledge.id
232
233
 
233
234
  return None
234
235
 
@@ -48,7 +48,7 @@ def create_graph_backend(backend: str = "neo4j", **config) -> GraphBackend:
48
48
  Factory function to create a graph backend.
49
49
 
50
50
  Args:
51
- backend: Backend type ("neo4j" or "memory")
51
+ backend: Backend type ("neo4j", "memgraph", "kuzu", or "memory")
52
52
  **config: Backend-specific configuration options
53
53
 
54
54
  Returns:
@@ -66,6 +66,23 @@ def create_graph_backend(backend: str = "neo4j", **config) -> GraphBackend:
66
66
  password="password"
67
67
  )
68
68
 
69
+ # Create Memgraph backend
70
+ backend = create_graph_backend(
71
+ backend="memgraph",
72
+ uri="bolt://localhost:7687",
73
+ username="",
74
+ password=""
75
+ )
76
+
77
+ # Create Kuzu embedded backend (persistent)
78
+ backend = create_graph_backend(
79
+ backend="kuzu",
80
+ database_path="./my_graph_db"
81
+ )
82
+
83
+ # Create Kuzu embedded backend (in-memory)
84
+ backend = create_graph_backend(backend="kuzu")
85
+
69
86
  # Create in-memory backend for testing
70
87
  backend = create_graph_backend(backend="memory")
71
88
  """
@@ -73,6 +90,14 @@ def create_graph_backend(backend: str = "neo4j", **config) -> GraphBackend:
73
90
  from alma.graph.backends.neo4j import Neo4jBackend
74
91
 
75
92
  return Neo4jBackend(**config)
93
+ elif backend == "memgraph":
94
+ from alma.graph.backends.memgraph import MemgraphBackend
95
+
96
+ return MemgraphBackend(**config)
97
+ elif backend == "kuzu":
98
+ from alma.graph.backends.kuzu import KuzuBackend
99
+
100
+ return KuzuBackend(**config)
76
101
  elif backend == "memory":
77
102
  from alma.graph.backends.memory import InMemoryBackend
78
103
 
@@ -0,0 +1,32 @@
1
+ """
2
+ ALMA Graph Memory Backends.
3
+
4
+ This package contains implementations of the GraphBackend interface
5
+ for various graph database systems.
6
+
7
+ Available backends:
8
+ - neo4j: Neo4j graph database backend
9
+ - memgraph: Memgraph graph database backend (Neo4j Bolt protocol compatible)
10
+ - kuzu: Kuzu embedded graph database backend (no server required)
11
+ - memory: In-memory backend for testing and development
12
+ """
13
+
14
+ from alma.graph.backends.memgraph import MemgraphBackend
15
+ from alma.graph.backends.memory import InMemoryBackend
16
+ from alma.graph.backends.neo4j import Neo4jBackend
17
+
18
+ # Kuzu is an optional dependency
19
+ try:
20
+ from alma.graph.backends.kuzu import KuzuBackend
21
+
22
+ _KUZU_AVAILABLE = True
23
+ except ImportError:
24
+ KuzuBackend = None # type: ignore
25
+ _KUZU_AVAILABLE = False
26
+
27
+ __all__ = [
28
+ "Neo4jBackend",
29
+ "MemgraphBackend",
30
+ "KuzuBackend",
31
+ "InMemoryBackend",
32
+ ]