genome-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 (56) hide show
  1. genome/__init__.py +75 -0
  2. genome/adapters/__init__.py +10 -0
  3. genome/adapters/langchain.py +188 -0
  4. genome/adapters/llamaindex.py +133 -0
  5. genome/agent/__init__.py +41 -0
  6. genome/agent/memory.py +281 -0
  7. genome/agent/tools.py +173 -0
  8. genome/benchmark.py +235 -0
  9. genome/cli.py +59 -0
  10. genome/corpus.py +136 -0
  11. genome/dataset.py +35 -0
  12. genome/embeddings.py +152 -0
  13. genome/errors.py +84 -0
  14. genome/evals/__init__.py +30 -0
  15. genome/evals/baselines.py +409 -0
  16. genome/evals/llm_judge.py +346 -0
  17. genome/evals/locomo.py +1538 -0
  18. genome/evals/tempbelief.py +355 -0
  19. genome/evaluate.py +90 -0
  20. genome/mcp/__init__.py +4 -0
  21. genome/mcp/server.py +125 -0
  22. genome/memory/__init__.py +45 -0
  23. genome/memory/async_facade.py +348 -0
  24. genome/memory/belief.py +372 -0
  25. genome/memory/cache.py +157 -0
  26. genome/memory/conflict.py +179 -0
  27. genome/memory/consolidation.py +162 -0
  28. genome/memory/entities.py +337 -0
  29. genome/memory/extraction.py +197 -0
  30. genome/memory/facade.py +1388 -0
  31. genome/memory/graph.py +84 -0
  32. genome/memory/hybrid.py +89 -0
  33. genome/memory/postgres_store.py +537 -0
  34. genome/memory/raptor.py +261 -0
  35. genome/memory/rerank.py +68 -0
  36. genome/memory/schema.py +149 -0
  37. genome/memory/sqlite_store.py +402 -0
  38. genome/memory/store.py +117 -0
  39. genome/memory/temporal.py +351 -0
  40. genome/memory_benchmark.py +478 -0
  41. genome/metrics.py +114 -0
  42. genome/observability.py +411 -0
  43. genome/operators.py +188 -0
  44. genome/server/__init__.py +13 -0
  45. genome/server/__main__.py +25 -0
  46. genome/server/app.py +517 -0
  47. genome/server/models.py +176 -0
  48. genome/synthesis.py +183 -0
  49. genome/viz.py +67 -0
  50. genome_memory-1.0.0.dist-info/METADATA +307 -0
  51. genome_memory-1.0.0.dist-info/RECORD +56 -0
  52. genome_memory-1.0.0.dist-info/WHEEL +5 -0
  53. genome_memory-1.0.0.dist-info/entry_points.txt +3 -0
  54. genome_memory-1.0.0.dist-info/licenses/LICENSE +201 -0
  55. genome_memory-1.0.0.dist-info/licenses/NOTICE +7 -0
  56. genome_memory-1.0.0.dist-info/top_level.txt +1 -0
genome/__init__.py ADDED
@@ -0,0 +1,75 @@
1
+ """GENOME: DNA-inspired memory primitives for AI."""
2
+
3
+ # Copyright 2026 Northtek (FrostByte Digital LLC)
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ __version__ = "1.0.0"
7
+
8
+
9
+ _RELATION_CONSTANTS = {"SUPERSEDES", "CONTRADICTS", "DERIVED_FROM", "RELATES_TO", "CAUSES"}
10
+
11
+
12
+ _OBSERVABILITY_EXPORTS = {
13
+ "ErrorCapture", "CapturedError", "get_error_capture",
14
+ "MetricsRegistry", "get_metrics", "configure_logging", "get_logger",
15
+ }
16
+
17
+ _CONFLICT_EXPORTS = {"ConflictResolver", "ConflictDecision"}
18
+
19
+
20
+ def __getattr__(name: str):
21
+ # Lazy imports to avoid loading heavy deps (torch, sentence-transformers) on
22
+ # every `import genome`. The user pays for what they use.
23
+ if name == "Memory":
24
+ from genome.memory import Memory
25
+ return Memory
26
+ if name == "AsyncMemory":
27
+ from genome.memory import AsyncMemory
28
+ return AsyncMemory
29
+ if name == "MemoryRecord":
30
+ from genome.memory import MemoryRecord
31
+ return MemoryRecord
32
+ if name == "SearchResult":
33
+ from genome.memory import SearchResult
34
+ return SearchResult
35
+ if name == "MemoryEdge":
36
+ from genome.memory import MemoryEdge
37
+ return MemoryEdge
38
+ if name == "EmbeddingProvider":
39
+ from genome.embeddings import EmbeddingProvider
40
+ return EmbeddingProvider
41
+ if name in _RELATION_CONSTANTS:
42
+ from genome.memory import graph as _graph
43
+ return getattr(_graph, name)
44
+ if name in _OBSERVABILITY_EXPORTS:
45
+ from genome import observability as _obs
46
+ return getattr(_obs, name)
47
+ if name in _CONFLICT_EXPORTS:
48
+ from genome.memory import conflict as _conflict
49
+ return getattr(_conflict, name)
50
+ raise AttributeError(f"module 'genome' has no attribute {name!r}")
51
+
52
+
53
+ __all__ = [
54
+ "Memory",
55
+ "AsyncMemory",
56
+ "MemoryRecord",
57
+ "SearchResult",
58
+ "MemoryEdge",
59
+ "EmbeddingProvider",
60
+ "SUPERSEDES",
61
+ "CONTRADICTS",
62
+ "DERIVED_FROM",
63
+ "RELATES_TO",
64
+ "CAUSES",
65
+ "ErrorCapture",
66
+ "CapturedError",
67
+ "get_error_capture",
68
+ "MetricsRegistry",
69
+ "get_metrics",
70
+ "configure_logging",
71
+ "get_logger",
72
+ "ConflictResolver",
73
+ "ConflictDecision",
74
+ "__version__",
75
+ ]
@@ -0,0 +1,10 @@
1
+ """Framework adapters for genome.
2
+
3
+ Each adapter is soft-optional: importing `genome.adapters.langchain` only fails
4
+ if the user tries to use it without langchain installed. Core genome has zero
5
+ framework dependencies.
6
+ """
7
+
8
+ # Copyright 2026 Northtek (FrostByte Digital LLC)
9
+ # SPDX-License-Identifier: Apache-2.0
10
+
@@ -0,0 +1,188 @@
1
+ """LangChain adapter.
2
+
3
+ Two classes exposed:
4
+
5
+ 1. `GenomeChatMessageHistory` -- implements `BaseChatMessageHistory`. Persists
6
+ conversation turns as memories in a genome store, scoped by `session_id`.
7
+ Drop-in replacement for `ChatMessageHistory` / `FileChatMessageHistory`.
8
+
9
+ 2. `GenomeRetrieverMemory` -- implements `BaseMemory` for retrieval-augmented
10
+ chat. Calls `Memory.search` to surface relevant historical memories for the
11
+ current prompt. Use this in LangChain chains that want semantic recall.
12
+
13
+ Install: `pip install langchain-core` (genome does not depend on it).
14
+ """
15
+
16
+ # Copyright 2026 Northtek (FrostByte Digital LLC)
17
+ # SPDX-License-Identifier: Apache-2.0
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import TYPE_CHECKING, Any
22
+
23
+ from genome.memory.facade import Memory
24
+
25
+ if TYPE_CHECKING:
26
+ # Only imported for type checking; runtime import happens lazily below.
27
+ from langchain_core.messages import BaseMessage
28
+
29
+
30
+ def _require_langchain():
31
+ try:
32
+ from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
33
+ except ImportError as e:
34
+ raise ImportError(
35
+ "langchain adapter requires langchain-core. "
36
+ "Install with: pip install langchain-core"
37
+ ) from e
38
+ return AIMessage, HumanMessage, BaseMessage
39
+
40
+
41
+ class GenomeChatMessageHistory:
42
+ """A BaseChatMessageHistory backed by a genome Memory store.
43
+
44
+ Each message (human or AI) becomes a memory. The session_id scopes them via
45
+ user_id, so multiple concurrent conversations stay isolated.
46
+
47
+ Usage::
48
+
49
+ from genome import Memory
50
+ from genome.adapters.langchain import GenomeChatMessageHistory
51
+
52
+ mem = Memory(storage="chat.db")
53
+ history = GenomeChatMessageHistory(memory=mem, session_id="session_123")
54
+ # Use as a normal LangChain BaseChatMessageHistory
55
+ """
56
+
57
+ def __init__(self, memory: Memory, session_id: str) -> None:
58
+ _require_langchain() # will raise if not installed
59
+ self.memory = memory
60
+ self.session_id = session_id
61
+
62
+ @property
63
+ def messages(self) -> list[BaseMessage]:
64
+ from langchain_core.messages import AIMessage, HumanMessage
65
+ records = self.memory.list_all(user_id=self.session_id)
66
+ records.sort(key=lambda r: r.created_at)
67
+ out: list[BaseMessage] = []
68
+ for r in records:
69
+ role = r.metadata.get("role", "human")
70
+ if role == "ai":
71
+ out.append(AIMessage(content=r.content))
72
+ else:
73
+ out.append(HumanMessage(content=r.content))
74
+ return out
75
+
76
+ def add_message(self, message: BaseMessage) -> None:
77
+ role = "ai" if message.__class__.__name__ == "AIMessage" else "human"
78
+ self.memory.add(
79
+ str(message.content),
80
+ user_id=self.session_id,
81
+ metadata={"role": role, "source": "chat"},
82
+ )
83
+
84
+ def add_messages(self, messages) -> None:
85
+ """Bulk add. Part of the current BaseChatMessageHistory contract --
86
+ RunnableWithMessageHistory calls this rather than add_message."""
87
+ for m in messages:
88
+ self.add_message(m)
89
+
90
+ def add_user_message(self, message: str) -> None:
91
+ self.memory.add(
92
+ message,
93
+ user_id=self.session_id,
94
+ metadata={"role": "human", "source": "chat"},
95
+ )
96
+
97
+ def add_ai_message(self, message: str) -> None:
98
+ self.memory.add(
99
+ message,
100
+ user_id=self.session_id,
101
+ metadata={"role": "ai", "source": "chat"},
102
+ )
103
+
104
+ def clear(self) -> None:
105
+ self.memory.reset(user_id=self.session_id)
106
+
107
+
108
+ class GenomeRetrieverMemory:
109
+ """A BaseMemory-compatible class that injects relevant historical memories
110
+ into the chain's variables on each call.
111
+
112
+ Usage::
113
+
114
+ from genome import Memory
115
+ from genome.adapters.langchain import GenomeRetrieverMemory
116
+ from langchain_core.prompts import ChatPromptTemplate
117
+
118
+ mem = Memory(storage="chat.db")
119
+ retriever = GenomeRetrieverMemory(
120
+ memory=mem, user_id="alice", memory_key="history", top_k=5,
121
+ )
122
+ prompt = ChatPromptTemplate.from_messages([
123
+ ("system", "Relevant memories:\\n{history}"),
124
+ ("user", "{input}"),
125
+ ])
126
+ chain = prompt | llm
127
+ chain.invoke({"input": "what do I like?", **retriever.load_memory_variables({"input": "what do I like?"})})
128
+ """
129
+
130
+ def __init__(
131
+ self,
132
+ memory: Memory,
133
+ *,
134
+ user_id: str,
135
+ memory_key: str = "history",
136
+ input_key: str = "input",
137
+ top_k: int = 5,
138
+ agent_id: str | None = None,
139
+ ) -> None:
140
+ _require_langchain()
141
+ self.memory = memory
142
+ self.user_id = user_id
143
+ self.memory_key = memory_key
144
+ self.input_key = input_key
145
+ self.top_k = top_k
146
+ self.agent_id = agent_id
147
+
148
+ @property
149
+ def memory_variables(self) -> list[str]:
150
+ return [self.memory_key]
151
+
152
+ def load_memory_variables(self, inputs: dict[str, Any]) -> dict[str, str]:
153
+ query = inputs.get(self.input_key) or inputs.get("query") or ""
154
+ if not query:
155
+ return {self.memory_key: ""}
156
+ results = self.memory.search(
157
+ str(query),
158
+ user_id=self.user_id,
159
+ agent_id=self.agent_id,
160
+ limit=self.top_k,
161
+ )
162
+ lines = [f"- {r.content}" for r in results]
163
+ return {self.memory_key: "\n".join(lines) if lines else "(no relevant memories)"}
164
+
165
+ def save_context(self, inputs: dict[str, Any], outputs: dict[str, Any]) -> None:
166
+ """Persist the turn. Both the user input and AI output become memories."""
167
+ user_text = str(inputs.get(self.input_key, "") or "")
168
+ ai_text = str(outputs.get("output", "") or outputs.get("response", "") or "")
169
+ if user_text:
170
+ self.memory.add(
171
+ user_text,
172
+ user_id=self.user_id,
173
+ agent_id=self.agent_id,
174
+ metadata={"role": "human", "source": "chat"},
175
+ )
176
+ if ai_text:
177
+ self.memory.add(
178
+ ai_text,
179
+ user_id=self.user_id,
180
+ agent_id=self.agent_id,
181
+ metadata={"role": "ai", "source": "chat"},
182
+ )
183
+
184
+ def clear(self) -> None:
185
+ self.memory.reset(user_id=self.user_id, agent_id=self.agent_id)
186
+
187
+
188
+ __all__ = ["GenomeChatMessageHistory", "GenomeRetrieverMemory"]
@@ -0,0 +1,133 @@
1
+ """LlamaIndex adapter.
2
+
3
+ Exposes `GenomeChatMemory`, a BaseMemory-compatible class for LlamaIndex
4
+ agents/engines that delegates storage + retrieval to genome.
5
+
6
+ Install: `pip install llama-index-core` (genome does not depend on it).
7
+ """
8
+
9
+ # Copyright 2026 Northtek (FrostByte Digital LLC)
10
+ # SPDX-License-Identifier: Apache-2.0
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ from genome.memory.facade import Memory
17
+
18
+
19
+ def _require_llamaindex():
20
+ try:
21
+ from llama_index.core.llms import ChatMessage
22
+ except ImportError as e:
23
+ raise ImportError(
24
+ "llamaindex adapter requires llama-index-core. "
25
+ "Install with: pip install llama-index-core"
26
+ ) from e
27
+ return ChatMessage
28
+
29
+
30
+ class GenomeChatMemory:
31
+ """A minimal LlamaIndex-compatible ChatMemory backed by genome.
32
+
33
+ Stores each turn as a memory with role metadata. On retrieval, returns the
34
+ ordered sequence via `get()`, or the top-k semantically relevant via
35
+ `get_relevant(query)`.
36
+
37
+ Usage::
38
+
39
+ from genome import Memory
40
+ from genome.adapters.llamaindex import GenomeChatMemory
41
+ from llama_index.core.llms import ChatMessage, MessageRole
42
+
43
+ mem = Memory(storage="agent.db")
44
+ chat_memory = GenomeChatMemory(memory=mem, session_id="session_1")
45
+ chat_memory.put(ChatMessage(role=MessageRole.USER, content="hello"))
46
+ messages = chat_memory.get() # ordered history
47
+ relevant = chat_memory.get_relevant("what did I say about coffee?", top_k=3)
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ memory: Memory,
53
+ *,
54
+ session_id: str,
55
+ token_limit: int | None = None,
56
+ ) -> None:
57
+ _require_llamaindex()
58
+ self.memory = memory
59
+ self.session_id = session_id
60
+ self.token_limit = token_limit
61
+
62
+ def put(self, message: Any) -> None:
63
+ """Add a ChatMessage to memory."""
64
+ role = str(getattr(getattr(message, "role", None), "value", message.role)).lower()
65
+ content = str(message.content)
66
+ self.memory.add(
67
+ content,
68
+ user_id=self.session_id,
69
+ metadata={"role": role, "source": "chat"},
70
+ )
71
+
72
+ def set(self, messages: list[Any]) -> None:
73
+ """Replace all messages in this session."""
74
+ self.memory.reset(user_id=self.session_id)
75
+ for m in messages:
76
+ self.put(m)
77
+
78
+ def get(self, input: str | None = None, **kwargs: Any) -> list[Any]:
79
+ """Return the full chronological message history for this session.
80
+
81
+ Accepts LlamaIndex's `get(input=..., **kwargs)` call signature (the
82
+ agent runtime passes the current user message as `input`) so this
83
+ object is drop-in usable there. `input` is accepted for compatibility;
84
+ chronological history is returned regardless -- use `get_relevant()`
85
+ for retrieval-aware recall.
86
+ """
87
+ from llama_index.core.llms import ChatMessage, MessageRole
88
+ records = self.memory.list_all(user_id=self.session_id)
89
+ records.sort(key=lambda r: r.created_at)
90
+ out: list[ChatMessage] = []
91
+ for r in records:
92
+ role_str = r.metadata.get("role", "user").lower()
93
+ role = {
94
+ "user": MessageRole.USER,
95
+ "human": MessageRole.USER,
96
+ "ai": MessageRole.ASSISTANT,
97
+ "assistant": MessageRole.ASSISTANT,
98
+ "system": MessageRole.SYSTEM,
99
+ }.get(role_str, MessageRole.USER)
100
+ out.append(ChatMessage(role=role, content=r.content))
101
+ return out
102
+
103
+ def get_all(self, **kwargs: Any) -> list[Any]:
104
+ """Alias for get() -- matches LlamaIndex BaseMemory naming."""
105
+ return self.get()
106
+
107
+ def get_relevant(self, query: str, top_k: int = 5) -> list[Any]:
108
+ """Return the top-k semantically relevant historical messages for the query.
109
+
110
+ genome-specific: this is the recall-style retrieval that matters for
111
+ long-running agents whose full history exceeds the context window.
112
+ """
113
+ from llama_index.core.llms import ChatMessage, MessageRole
114
+ results = self.memory.search(query, user_id=self.session_id, limit=top_k)
115
+ out: list[ChatMessage] = []
116
+ for r in results:
117
+ role_str = r.record.metadata.get("role", "user").lower()
118
+ role = {
119
+ "user": MessageRole.USER,
120
+ "human": MessageRole.USER,
121
+ "ai": MessageRole.ASSISTANT,
122
+ "assistant": MessageRole.ASSISTANT,
123
+ "system": MessageRole.SYSTEM,
124
+ }.get(role_str, MessageRole.USER)
125
+ out.append(ChatMessage(role=role, content=r.content))
126
+ return out
127
+
128
+ def reset(self) -> None:
129
+ """Clear this session's history."""
130
+ self.memory.reset(user_id=self.session_id)
131
+
132
+
133
+ __all__ = ["GenomeChatMemory"]
@@ -0,0 +1,41 @@
1
+ """Agent runtime for genome.
2
+
3
+ Letta-style hierarchical memory (core + archival) with tool-callable
4
+ interfaces. Works with any LLM SDK (Anthropic, OpenAI, any tool-calling
5
+ model) by exposing tool schemas the agent can invoke.
6
+
7
+ Usage::
8
+
9
+ from genome import Memory
10
+ from genome.agent import AgentMemory, tool_schemas
11
+
12
+ mem = Memory(storage="agent.db")
13
+ agent_mem = AgentMemory(memory=mem, user_id="alice", session_id="s1")
14
+
15
+ # Render the core memory block into your system prompt
16
+ system_prompt = f"You are a helpful assistant.\\n\\n{agent_mem.render_core()}"
17
+
18
+ # Advertise the memory tools to your LLM
19
+ tools = tool_schemas() # Anthropic format
20
+
21
+ # When the LLM calls a tool:
22
+ result = agent_mem.handle_tool_call(tool_name, tool_args)
23
+ """
24
+
25
+ # Copyright 2026 Northtek (FrostByte Digital LLC)
26
+ # SPDX-License-Identifier: Apache-2.0
27
+
28
+ from genome.agent.memory import AgentMemory, CoreBlock
29
+ from genome.agent.tools import (
30
+ ANTHROPIC_TOOL_SCHEMAS,
31
+ OPENAI_TOOL_SCHEMAS,
32
+ tool_schemas,
33
+ )
34
+
35
+ __all__ = [
36
+ "AgentMemory",
37
+ "CoreBlock",
38
+ "tool_schemas",
39
+ "ANTHROPIC_TOOL_SCHEMAS",
40
+ "OPENAI_TOOL_SCHEMAS",
41
+ ]