stellar-memory 1.0.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 (66) hide show
  1. stellar_memory/__init__.py +164 -0
  2. stellar_memory/adapters/__init__.py +6 -0
  3. stellar_memory/adapters/langchain.py +67 -0
  4. stellar_memory/adapters/openai_plugin.py +125 -0
  5. stellar_memory/adaptive_decay.py +57 -0
  6. stellar_memory/benchmark.py +229 -0
  7. stellar_memory/cli.py +460 -0
  8. stellar_memory/config.py +341 -0
  9. stellar_memory/connectors/__init__.py +46 -0
  10. stellar_memory/connectors/api_connector.py +64 -0
  11. stellar_memory/connectors/file_connector.py +88 -0
  12. stellar_memory/connectors/web_connector.py +66 -0
  13. stellar_memory/consolidator.py +72 -0
  14. stellar_memory/dashboard/__init__.py +18 -0
  15. stellar_memory/dashboard/app.py +260 -0
  16. stellar_memory/decay_manager.py +73 -0
  17. stellar_memory/embedder.py +79 -0
  18. stellar_memory/emotion.py +111 -0
  19. stellar_memory/event_bus.py +57 -0
  20. stellar_memory/event_logger.py +68 -0
  21. stellar_memory/graph_analyzer.py +225 -0
  22. stellar_memory/importance_evaluator.py +148 -0
  23. stellar_memory/llm_adapter.py +95 -0
  24. stellar_memory/mcp_server.py +376 -0
  25. stellar_memory/memory_function.py +79 -0
  26. stellar_memory/memory_graph.py +62 -0
  27. stellar_memory/metacognition.py +153 -0
  28. stellar_memory/models.py +395 -0
  29. stellar_memory/multimodal.py +167 -0
  30. stellar_memory/namespace.py +47 -0
  31. stellar_memory/orbit_manager.py +138 -0
  32. stellar_memory/persistent_graph.py +118 -0
  33. stellar_memory/providers/__init__.py +140 -0
  34. stellar_memory/providers/ollama_provider.py +64 -0
  35. stellar_memory/providers/openai_provider.py +71 -0
  36. stellar_memory/reasoning.py +293 -0
  37. stellar_memory/scheduler.py +63 -0
  38. stellar_memory/security/__init__.py +9 -0
  39. stellar_memory/security/access_control.py +59 -0
  40. stellar_memory/security/audit.py +77 -0
  41. stellar_memory/security/encryption.py +84 -0
  42. stellar_memory/self_learning.py +341 -0
  43. stellar_memory/serializer.py +78 -0
  44. stellar_memory/server.py +605 -0
  45. stellar_memory/session.py +44 -0
  46. stellar_memory/stellar.py +946 -0
  47. stellar_memory/storage/__init__.py +94 -0
  48. stellar_memory/storage/in_memory.py +61 -0
  49. stellar_memory/storage/postgres_storage.py +300 -0
  50. stellar_memory/storage/redis_cache.py +138 -0
  51. stellar_memory/storage/sqlite_storage.py +192 -0
  52. stellar_memory/stream.py +113 -0
  53. stellar_memory/summarizer.py +49 -0
  54. stellar_memory/sync/__init__.py +7 -0
  55. stellar_memory/sync/sync_manager.py +104 -0
  56. stellar_memory/sync/ws_client.py +91 -0
  57. stellar_memory/sync/ws_server.py +90 -0
  58. stellar_memory/utils.py +28 -0
  59. stellar_memory/vector_index.py +233 -0
  60. stellar_memory/weight_tuner.py +163 -0
  61. stellar_memory-1.0.0.dist-info/LICENSE +21 -0
  62. stellar_memory-1.0.0.dist-info/METADATA +241 -0
  63. stellar_memory-1.0.0.dist-info/RECORD +66 -0
  64. stellar_memory-1.0.0.dist-info/WHEEL +5 -0
  65. stellar_memory-1.0.0.dist-info/entry_points.txt +2 -0
  66. stellar_memory-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,164 @@
1
+ """Stellar Memory - A celestial-structure-based AI memory management system.
2
+
3
+ Give any AI human-like memory, built on a celestial structure.
4
+ """
5
+
6
+ from stellar_memory.stellar import StellarMemory
7
+ from stellar_memory.config import (
8
+ StellarConfig, MemoryFunctionConfig, ZoneConfig,
9
+ EmbedderConfig, LLMConfig, TunerConfig,
10
+ ConsolidationConfig, SessionConfig,
11
+ EventConfig, NamespaceConfig, GraphConfig,
12
+ DecayConfig, EventLoggerConfig, RecallConfig,
13
+ VectorIndexConfig, SummarizationConfig,
14
+ AdaptiveDecayConfig, GraphAnalyticsConfig,
15
+ StorageConfig, SyncConfig, SecurityConfig,
16
+ ConnectorConfig, DashboardConfig,
17
+ EmotionConfig, ServerConfig,
18
+ MetacognitionConfig, SelfLearningConfig,
19
+ MultimodalConfig, ReasoningConfig, BenchmarkConfig,
20
+ )
21
+ from stellar_memory.models import (
22
+ MemoryItem, ScoreBreakdown, ReorbitResult, MemoryStats,
23
+ EvaluationResult, FeedbackRecord,
24
+ ConsolidationResult, SessionInfo, MemorySnapshot,
25
+ MemoryEdge, DecayResult, HealthStatus, SummarizationResult,
26
+ ChangeEvent, AccessRole, IngestResult, ZoneDistribution,
27
+ EmotionVector, TimelineEntry,
28
+ IntrospectionResult, ConfidentRecall, RecallLog,
29
+ OptimizationReport, ReasoningResult, Contradiction,
30
+ BenchmarkReport,
31
+ )
32
+ from stellar_memory.event_bus import EventBus
33
+ from stellar_memory.memory_graph import MemoryGraph
34
+ from stellar_memory.persistent_graph import PersistentMemoryGraph
35
+ from stellar_memory.namespace import NamespaceManager
36
+ from stellar_memory.decay_manager import DecayManager
37
+ from stellar_memory.event_logger import EventLogger
38
+ from stellar_memory.llm_adapter import MemoryMiddleware, AnthropicAdapter
39
+ from stellar_memory.vector_index import VectorIndex, BruteForceIndex, BallTreeIndex
40
+ from stellar_memory.summarizer import MemorySummarizer
41
+ from stellar_memory.adaptive_decay import AdaptiveDecayManager
42
+ from stellar_memory.graph_analyzer import GraphAnalyzer, GraphStats, CentralityResult
43
+ from stellar_memory.providers import ProviderRegistry
44
+ from stellar_memory.storage import StorageBackend
45
+ from stellar_memory.security.encryption import EncryptionManager
46
+ from stellar_memory.security.access_control import AccessControl
47
+ from stellar_memory.security.audit import SecurityAudit
48
+ from stellar_memory.sync import MemorySyncManager
49
+ from stellar_memory.emotion import EmotionAnalyzer
50
+ from stellar_memory.stream import MemoryStream
51
+ from stellar_memory.metacognition import Introspector, ConfidenceScorer
52
+ from stellar_memory.multimodal import (
53
+ ContentTypeHandler, TextHandler, CodeHandler, JsonHandler,
54
+ get_handler, detect_content_type,
55
+ )
56
+ from stellar_memory.self_learning import PatternCollector, WeightOptimizer
57
+ from stellar_memory.reasoning import MemoryReasoner, ContradictionDetector
58
+ from stellar_memory.benchmark import StandardDataset, MemoryBenchmark
59
+
60
+ try:
61
+ from importlib.metadata import version as _pkg_version
62
+ __version__ = _pkg_version("stellar-memory")
63
+ except Exception:
64
+ __version__ = "1.0.0"
65
+
66
+ __all__ = [
67
+ "StellarMemory",
68
+ "StellarConfig",
69
+ "MemoryFunctionConfig",
70
+ "ZoneConfig",
71
+ "EmbedderConfig",
72
+ "LLMConfig",
73
+ "TunerConfig",
74
+ "ConsolidationConfig",
75
+ "SessionConfig",
76
+ "EventConfig",
77
+ "NamespaceConfig",
78
+ "GraphConfig",
79
+ "DecayConfig",
80
+ "EventLoggerConfig",
81
+ "RecallConfig",
82
+ "VectorIndexConfig",
83
+ "SummarizationConfig",
84
+ "AdaptiveDecayConfig",
85
+ "GraphAnalyticsConfig",
86
+ "MemoryItem",
87
+ "ScoreBreakdown",
88
+ "ReorbitResult",
89
+ "MemoryStats",
90
+ "EvaluationResult",
91
+ "FeedbackRecord",
92
+ "ConsolidationResult",
93
+ "SessionInfo",
94
+ "MemorySnapshot",
95
+ "MemoryEdge",
96
+ "DecayResult",
97
+ "HealthStatus",
98
+ "SummarizationResult",
99
+ "EventBus",
100
+ "MemoryGraph",
101
+ "PersistentMemoryGraph",
102
+ "NamespaceManager",
103
+ "DecayManager",
104
+ "EventLogger",
105
+ "MemoryMiddleware",
106
+ "AnthropicAdapter",
107
+ "VectorIndex",
108
+ "BruteForceIndex",
109
+ "BallTreeIndex",
110
+ "MemorySummarizer",
111
+ "AdaptiveDecayManager",
112
+ "GraphAnalyzer",
113
+ "GraphStats",
114
+ "CentralityResult",
115
+ "ProviderRegistry",
116
+ "StorageConfig",
117
+ "SyncConfig",
118
+ "SecurityConfig",
119
+ "ConnectorConfig",
120
+ "DashboardConfig",
121
+ "ChangeEvent",
122
+ "AccessRole",
123
+ "IngestResult",
124
+ "ZoneDistribution",
125
+ "StorageBackend",
126
+ "EncryptionManager",
127
+ "AccessControl",
128
+ "SecurityAudit",
129
+ "MemorySyncManager",
130
+ # P7
131
+ "EmotionConfig",
132
+ "ServerConfig",
133
+ "EmotionVector",
134
+ "TimelineEntry",
135
+ "EmotionAnalyzer",
136
+ "MemoryStream",
137
+ # P9
138
+ "MetacognitionConfig",
139
+ "SelfLearningConfig",
140
+ "MultimodalConfig",
141
+ "ReasoningConfig",
142
+ "BenchmarkConfig",
143
+ "IntrospectionResult",
144
+ "ConfidentRecall",
145
+ "RecallLog",
146
+ "OptimizationReport",
147
+ "ReasoningResult",
148
+ "Contradiction",
149
+ "BenchmarkReport",
150
+ "Introspector",
151
+ "ConfidenceScorer",
152
+ "ContentTypeHandler",
153
+ "TextHandler",
154
+ "CodeHandler",
155
+ "JsonHandler",
156
+ "get_handler",
157
+ "detect_content_type",
158
+ "PatternCollector",
159
+ "WeightOptimizer",
160
+ "MemoryReasoner",
161
+ "ContradictionDetector",
162
+ "StandardDataset",
163
+ "MemoryBenchmark",
164
+ ]
@@ -0,0 +1,6 @@
1
+ """Stellar Memory adapters for AI frameworks."""
2
+
3
+ from stellar_memory.adapters.langchain import StellarLangChainMemory
4
+ from stellar_memory.adapters.openai_plugin import OpenAIMemoryPlugin, STELLAR_TOOLS
5
+
6
+ __all__ = ["StellarLangChainMemory", "OpenAIMemoryPlugin", "STELLAR_TOOLS"]
@@ -0,0 +1,67 @@
1
+ """LangChain Memory interface adapter for Stellar Memory."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from stellar_memory.stellar import StellarMemory
9
+
10
+
11
+ class StellarLangChainMemory:
12
+ """LangChain BaseMemory compatible adapter.
13
+
14
+ Usage::
15
+
16
+ from stellar_memory import StellarMemory
17
+ from stellar_memory.adapters.langchain import StellarLangChainMemory
18
+
19
+ memory = StellarMemory()
20
+ lc_memory = StellarLangChainMemory(memory)
21
+ # Use with LangChain ConversationChain
22
+ """
23
+
24
+ memory_key: str = "history"
25
+ input_key: str = "input"
26
+ output_key: str = "output"
27
+
28
+ def __init__(self, stellar_memory: StellarMemory,
29
+ recall_limit: int = 5,
30
+ memory_key: str = "history"):
31
+ self._memory = stellar_memory
32
+ self._recall_limit = recall_limit
33
+ self.memory_key = memory_key
34
+
35
+ @property
36
+ def memory_variables(self) -> list[str]:
37
+ return [self.memory_key]
38
+
39
+ def load_memory_variables(self, inputs: dict[str, Any]) -> dict[str, str]:
40
+ """Recall relevant memories based on input."""
41
+ query = inputs.get(self.input_key, "")
42
+ if not query:
43
+ return {self.memory_key: ""}
44
+
45
+ results = self._memory.recall(str(query), limit=self._recall_limit)
46
+ if not results:
47
+ return {self.memory_key: ""}
48
+
49
+ context = "\n".join(f"- {item.content}" for item in results)
50
+ return {self.memory_key: context}
51
+
52
+ def save_context(self, inputs: dict[str, Any],
53
+ outputs: dict[str, str]) -> None:
54
+ """Store the conversation exchange as a memory."""
55
+ user_input = inputs.get(self.input_key, "")
56
+ assistant_output = outputs.get(self.output_key, "")
57
+ if user_input or assistant_output:
58
+ content = f"User: {user_input}\nAssistant: {assistant_output}"
59
+ self._memory.store(
60
+ content=content,
61
+ auto_evaluate=True,
62
+ metadata={"source": "langchain"},
63
+ )
64
+
65
+ def clear(self) -> None:
66
+ """No-op for persistent memory."""
67
+ pass
@@ -0,0 +1,125 @@
1
+ """OpenAI function calling schema for Stellar Memory."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from stellar_memory.stellar import StellarMemory
10
+
11
+
12
+ STELLAR_TOOLS = [
13
+ {
14
+ "type": "function",
15
+ "function": {
16
+ "name": "memory_store",
17
+ "description": "Store a new memory for future recall",
18
+ "parameters": {
19
+ "type": "object",
20
+ "properties": {
21
+ "content": {
22
+ "type": "string",
23
+ "description": "Memory content to store",
24
+ },
25
+ "importance": {
26
+ "type": "number",
27
+ "minimum": 0,
28
+ "maximum": 1,
29
+ "description": "Importance score 0.0-1.0",
30
+ },
31
+ },
32
+ "required": ["content"],
33
+ },
34
+ },
35
+ },
36
+ {
37
+ "type": "function",
38
+ "function": {
39
+ "name": "memory_recall",
40
+ "description": "Search memories by query",
41
+ "parameters": {
42
+ "type": "object",
43
+ "properties": {
44
+ "query": {
45
+ "type": "string",
46
+ "description": "Search query",
47
+ },
48
+ "limit": {
49
+ "type": "integer",
50
+ "minimum": 1,
51
+ "maximum": 20,
52
+ "description": "Max results",
53
+ },
54
+ },
55
+ "required": ["query"],
56
+ },
57
+ },
58
+ },
59
+ {
60
+ "type": "function",
61
+ "function": {
62
+ "name": "memory_forget",
63
+ "description": "Delete a specific memory by ID",
64
+ "parameters": {
65
+ "type": "object",
66
+ "properties": {
67
+ "memory_id": {
68
+ "type": "string",
69
+ "description": "Memory UUID",
70
+ },
71
+ },
72
+ "required": ["memory_id"],
73
+ },
74
+ },
75
+ },
76
+ ]
77
+
78
+
79
+ class OpenAIMemoryPlugin:
80
+ """Plugin that handles OpenAI function call dispatch.
81
+
82
+ Usage::
83
+
84
+ from stellar_memory import StellarMemory
85
+ from stellar_memory.adapters.openai_plugin import OpenAIMemoryPlugin, STELLAR_TOOLS
86
+
87
+ memory = StellarMemory()
88
+ plugin = OpenAIMemoryPlugin(memory)
89
+ result = plugin.handle_call("memory_store", '{"content": "hello"}')
90
+ """
91
+
92
+ def __init__(self, stellar_memory: StellarMemory):
93
+ self._memory = stellar_memory
94
+
95
+ def get_tools(self) -> list[dict]:
96
+ """Return OpenAI tools schema."""
97
+ return STELLAR_TOOLS
98
+
99
+ def handle_call(self, function_name: str, arguments: str) -> str:
100
+ """Dispatch function call and return JSON result."""
101
+ args = json.loads(arguments)
102
+
103
+ if function_name == "memory_store":
104
+ item = self._memory.store(
105
+ content=args["content"],
106
+ importance=args.get("importance", 0.5),
107
+ auto_evaluate=True,
108
+ )
109
+ return json.dumps({"id": item.id, "zone": item.zone})
110
+
111
+ elif function_name == "memory_recall":
112
+ results = self._memory.recall(
113
+ args["query"], limit=args.get("limit", 5)
114
+ )
115
+ return json.dumps([{
116
+ "id": item.id, "content": item.content,
117
+ "zone": item.zone, "importance": item.arbitrary_importance,
118
+ } for item in results])
119
+
120
+ elif function_name == "memory_forget":
121
+ removed = self._memory.forget(args["memory_id"])
122
+ return json.dumps({"removed": removed})
123
+
124
+ else:
125
+ return json.dumps({"error": f"Unknown function: {function_name}"})
@@ -0,0 +1,57 @@
1
+ """Adaptive decay - importance-based differential forgetting rates."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+
7
+ from stellar_memory.config import DecayConfig
8
+ from stellar_memory.models import MemoryItem
9
+
10
+ SECONDS_PER_DAY = 86400
11
+
12
+
13
+ class AdaptiveDecayManager:
14
+ """Manages importance-based differential decay rates."""
15
+
16
+ def __init__(self, decay_config: DecayConfig):
17
+ self._config = decay_config
18
+ self._adaptive = decay_config.adaptive
19
+
20
+ def effective_decay_days(self, item: MemoryItem) -> float:
21
+ """Calculate effective decay days based on importance.
22
+
23
+ Formula: base_days * (0.5 + importance * weight)
24
+ - importance=0.9, weight=1.0 → 30 * 1.4 = 42 days
25
+ - importance=0.3, weight=1.0 → 30 * 0.8 = 24 days
26
+ - importance=0.0, weight=1.0 → 30 * 0.5 = 15 days
27
+ """
28
+ base = self._config.decay_days
29
+ importance = item.arbitrary_importance
30
+ weight = self._adaptive.importance_weight
31
+ factor = 0.5 + importance * weight
32
+
33
+ if self._adaptive.zone_factor and item.zone > 0:
34
+ zone_multiplier = max(0.5, 2.0 - item.zone * 0.5)
35
+ factor *= zone_multiplier
36
+
37
+ return base * factor
38
+
39
+ def effective_forget_days(self, item: MemoryItem) -> float:
40
+ """Calculate effective auto-forget days."""
41
+ base = self._config.auto_forget_days
42
+ importance = item.arbitrary_importance
43
+ return base * (0.5 + importance * self._adaptive.importance_weight)
44
+
45
+ def apply_curve(self, elapsed_days: float, threshold_days: float) -> float:
46
+ """Apply decay curve. Returns 0.0 (no decay) to 1.0 (full decay)."""
47
+ if elapsed_days < threshold_days:
48
+ return 0.0
49
+ ratio = elapsed_days / threshold_days
50
+
51
+ if self._adaptive.decay_curve == "exponential":
52
+ return min(1.0, 1.0 - math.exp(-(ratio - 1.0)))
53
+ elif self._adaptive.decay_curve == "sigmoid":
54
+ x = (ratio - 1.0) * 5
55
+ return 1.0 / (1.0 + math.exp(-x))
56
+ else: # linear
57
+ return min(1.0, ratio - 1.0)
@@ -0,0 +1,229 @@
1
+ """P9-F5: Memory Benchmark - quantitative performance measurement."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import random
6
+ import time
7
+ from dataclasses import dataclass, field
8
+
9
+ from stellar_memory.models import BenchmarkReport
10
+
11
+
12
+ # Standard dataset categories with template content
13
+ _CATEGORIES = ["daily", "work", "tech", "emotion", "code"]
14
+
15
+ _TEMPLATES = {
16
+ "daily": [
17
+ "I had coffee with {name} at the cafe this morning",
18
+ "The weather today was {adj} and {adj2}",
19
+ "Went grocery shopping and bought {item} and {item2}",
20
+ "Watched a movie called {title} last night",
21
+ "Took a walk in the park near {place}",
22
+ ],
23
+ "work": [
24
+ "Meeting with {name} about the {topic} project",
25
+ "Deadline for {topic} report is next {day}",
26
+ "New team member {name} joined the {topic} team",
27
+ "Completed the {topic} feature implementation",
28
+ "Code review feedback on {topic} module was positive",
29
+ ],
30
+ "tech": [
31
+ "Learned about {tech} framework for building {topic}",
32
+ "{tech} version {ver} was released with new features",
33
+ "The {tech} documentation recommends using {pattern}",
34
+ "Benchmark shows {tech} is {num}x faster than alternatives",
35
+ "Migrated from {tech} to {tech2} for better performance",
36
+ ],
37
+ "emotion": [
38
+ "Felt really happy when {name} surprised me with {item}",
39
+ "Frustrated with the {topic} bug that took hours to fix",
40
+ "Excited about the upcoming {topic} conference",
41
+ "Anxious about the {topic} presentation tomorrow",
42
+ "Relieved that the {topic} deployment went smoothly",
43
+ ],
44
+ "code": [
45
+ "def calculate_{func}(data): return sum(data) / len(data)",
46
+ "class {cls}Manager: def __init__(self): self.items = []",
47
+ "async def fetch_{func}(url): return await client.get(url)",
48
+ "SELECT * FROM {table} WHERE status = 'active' ORDER BY created_at",
49
+ "const {func} = ({param}) => {{ return {param}.filter(x => x > 0) }}",
50
+ ],
51
+ }
52
+
53
+ _NAMES = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry"]
54
+ _TOPICS = ["memory", "search", "auth", "deploy", "cache", "sync", "graph", "api"]
55
+ _TECHS = ["React", "Python", "Rust", "Go", "Docker", "Redis", "PostgreSQL", "FastAPI"]
56
+ _ITEMS = ["milk", "bread", "coffee", "laptop", "book", "headphones", "cake", "flowers"]
57
+ _ADJS = ["sunny", "rainy", "cold", "warm", "windy", "cloudy", "beautiful", "foggy"]
58
+
59
+
60
+ class StandardDataset:
61
+ """Reproducible benchmark dataset generator."""
62
+
63
+ SIZES = {
64
+ "small": (100, 20),
65
+ "standard": (1000, 100),
66
+ "large": (10000, 500),
67
+ }
68
+
69
+ def __init__(self, name: str = "standard", seed: int = 42):
70
+ self._name = name
71
+ self._seed = seed
72
+ self._rng = random.Random(seed)
73
+ count, query_count = self.SIZES.get(name, (1000, 100))
74
+ self._memory_count = count
75
+ self._query_count = query_count
76
+
77
+ @property
78
+ def name(self) -> str:
79
+ return self._name
80
+
81
+ def generate_memories(self) -> list[dict]:
82
+ """Generate reproducible memory items."""
83
+ memories = []
84
+ for i in range(self._memory_count):
85
+ cat = _CATEGORIES[i % len(_CATEGORIES)]
86
+ template = self._rng.choice(_TEMPLATES[cat])
87
+ content = self._fill_template(template)
88
+ importance = self._rng.uniform(0.1, 0.9)
89
+ tags = [cat]
90
+ if cat == "code":
91
+ tags.append("code")
92
+ memories.append({
93
+ "content": content,
94
+ "importance": round(importance, 2),
95
+ "tags": tags,
96
+ "category": cat,
97
+ "id_hint": f"bench_{i}",
98
+ })
99
+ return memories
100
+
101
+ def generate_queries(self) -> list[dict]:
102
+ """Generate queries with expected category matches."""
103
+ queries = []
104
+ for i in range(self._query_count):
105
+ cat = _CATEGORIES[i % len(_CATEGORIES)]
106
+ keywords = {
107
+ "daily": ["coffee", "weather", "morning", "walk", "movie"],
108
+ "work": ["meeting", "deadline", "team", "project", "review"],
109
+ "tech": ["framework", "version", "documentation", "benchmark"],
110
+ "emotion": ["happy", "frustrated", "excited", "anxious", "relieved"],
111
+ "code": ["function", "class", "async", "SELECT", "const"],
112
+ }
113
+ query = self._rng.choice(keywords.get(cat, ["memory"]))
114
+ queries.append({
115
+ "query": query,
116
+ "expected_category": cat,
117
+ })
118
+ return queries
119
+
120
+ def _fill_template(self, template: str) -> str:
121
+ replacements = {
122
+ "{name}": self._rng.choice(_NAMES),
123
+ "{topic}": self._rng.choice(_TOPICS),
124
+ "{tech}": self._rng.choice(_TECHS),
125
+ "{tech2}": self._rng.choice(_TECHS),
126
+ "{item}": self._rng.choice(_ITEMS),
127
+ "{item2}": self._rng.choice(_ITEMS),
128
+ "{adj}": self._rng.choice(_ADJS),
129
+ "{adj2}": self._rng.choice(_ADJS),
130
+ "{place}": self._rng.choice(["river", "hill", "garden", "lake"]),
131
+ "{title}": self._rng.choice(["Inception", "Matrix", "Arrival", "Dune"]),
132
+ "{day}": self._rng.choice(["Monday", "Friday", "Wednesday"]),
133
+ "{ver}": f"{self._rng.randint(1,5)}.{self._rng.randint(0,9)}",
134
+ "{pattern}": self._rng.choice(["hooks", "middleware", "plugins"]),
135
+ "{num}": str(self._rng.randint(2, 10)),
136
+ "{func}": self._rng.choice(["average", "total", "count", "max"]),
137
+ "{cls}": self._rng.choice(["Data", "Task", "User", "Event"]),
138
+ "{table}": self._rng.choice(["users", "orders", "events", "logs"]),
139
+ "{param}": self._rng.choice(["items", "data", "values", "records"]),
140
+ }
141
+ result = template
142
+ for key, val in replacements.items():
143
+ result = result.replace(key, val, 1)
144
+ return result
145
+
146
+
147
+ class MemoryBenchmark:
148
+ """Comprehensive memory system benchmark."""
149
+
150
+ def __init__(self, stellar):
151
+ self._stellar = stellar
152
+
153
+ def run(self, queries: int = 100, dataset: str = "standard",
154
+ seed: int = 42) -> BenchmarkReport:
155
+ ds = StandardDataset(dataset, seed)
156
+ memories = ds.generate_memories()
157
+ query_list = ds.generate_queries()[:queries]
158
+
159
+ # Measure store latency
160
+ store_times = []
161
+ stored_ids = []
162
+ for mem in memories:
163
+ t0 = time.perf_counter()
164
+ mid = self._stellar.store(
165
+ content=mem["content"],
166
+ importance=mem["importance"],
167
+ metadata={"category": mem["category"], "bench_id": mem["id_hint"]},
168
+ )
169
+ t1 = time.perf_counter()
170
+ store_times.append((t1 - t0) * 1000)
171
+ stored_ids.append(mid.id if hasattr(mid, 'id') else mid)
172
+
173
+ # Measure recall latency + accuracy
174
+ recall_times = []
175
+ hits_at_5 = 0
176
+ hits_at_10 = 0
177
+ precision_hits_5 = 0
178
+ for q in query_list:
179
+ t0 = time.perf_counter()
180
+ results = self._stellar.recall(q["query"], limit=10)
181
+ t1 = time.perf_counter()
182
+ recall_times.append((t1 - t0) * 1000)
183
+
184
+ # Check if any result matches expected category
185
+ top5 = results[:5]
186
+ top10 = results[:10]
187
+ cat = q["expected_category"]
188
+ if any(r.metadata.get("category") == cat for r in top5):
189
+ hits_at_5 += 1
190
+ if any(r.metadata.get("category") == cat for r in top10):
191
+ hits_at_10 += 1
192
+ precision_hits_5 += sum(
193
+ 1 for r in top5 if r.metadata.get("category") == cat
194
+ )
195
+
196
+ # Measure reorbit latency
197
+ t0 = time.perf_counter()
198
+ self._stellar.reorbit()
199
+ t1 = time.perf_counter()
200
+ reorbit_ms = (t1 - t0) * 1000
201
+
202
+ # Stats
203
+ stats = self._stellar.stats()
204
+ import os
205
+ db_size = 0.0
206
+ if hasattr(self._stellar, 'config') and self._stellar.config.db_path != ":memory:":
207
+ try:
208
+ db_size = os.path.getsize(self._stellar.config.db_path) / (1024 * 1024)
209
+ except OSError:
210
+ pass
211
+
212
+ import sys
213
+ mem_usage = sys.getsizeof(self._stellar) / (1024 * 1024)
214
+
215
+ total_q = len(query_list) or 1
216
+ return BenchmarkReport(
217
+ recall_at_5=hits_at_5 / total_q,
218
+ recall_at_10=hits_at_10 / total_q,
219
+ precision_at_5=(precision_hits_5 / (total_q * 5)) if total_q else 0.0,
220
+ avg_store_latency_ms=sum(store_times) / len(store_times) if store_times else 0.0,
221
+ avg_recall_latency_ms=sum(recall_times) / len(recall_times) if recall_times else 0.0,
222
+ avg_reorbit_latency_ms=reorbit_ms,
223
+ memory_usage_mb=round(mem_usage, 2),
224
+ db_size_mb=round(db_size, 2),
225
+ total_memories=stats.total_memories,
226
+ zone_distribution=dict(stats.zone_counts),
227
+ dataset_name=dataset,
228
+ queries_run=total_q,
229
+ )