episodicdb 0.1.0__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.
- episodicdb-0.1.0/.gitignore +36 -0
- episodicdb-0.1.0/LICENSE +21 -0
- episodicdb-0.1.0/PKG-INFO +260 -0
- episodicdb-0.1.0/README.md +233 -0
- episodicdb-0.1.0/episodicdb/__init__.py +8 -0
- episodicdb-0.1.0/episodicdb/analytics.py +220 -0
- episodicdb-0.1.0/episodicdb/db.py +80 -0
- episodicdb-0.1.0/episodicdb/mcp/__init__.py +0 -0
- episodicdb-0.1.0/episodicdb/mcp/__main__.py +17 -0
- episodicdb-0.1.0/episodicdb/mcp/server.py +235 -0
- episodicdb-0.1.0/episodicdb/schema/__init__.py +75 -0
- episodicdb-0.1.0/episodicdb/temporal.py +63 -0
- episodicdb-0.1.0/episodicdb/writer.py +150 -0
- episodicdb-0.1.0/pyproject.toml +46 -0
- episodicdb-0.1.0/tests/__init__.py +0 -0
- episodicdb-0.1.0/tests/conftest.py +45 -0
- episodicdb-0.1.0/tests/test_analytics.py +142 -0
- episodicdb-0.1.0/tests/test_mcp_server.py +252 -0
- episodicdb-0.1.0/tests/test_temporal.py +139 -0
- episodicdb-0.1.0/tests/test_writer.py +104 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
*.so
|
|
8
|
+
*.egg
|
|
9
|
+
*.egg-info/
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
.eggs/
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
env/
|
|
18
|
+
|
|
19
|
+
# Testing
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.coverage
|
|
22
|
+
htmlcov/
|
|
23
|
+
|
|
24
|
+
# EpisodicDB runtime
|
|
25
|
+
~/.episodicdb/
|
|
26
|
+
|
|
27
|
+
# Internal / agent config
|
|
28
|
+
docs/superpowers/
|
|
29
|
+
CLAUDE.md
|
|
30
|
+
|
|
31
|
+
# IDE
|
|
32
|
+
.idea/
|
|
33
|
+
.vscode/
|
|
34
|
+
*.swp
|
|
35
|
+
*.swo
|
|
36
|
+
.DS_Store
|
episodicdb-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KsPsD
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: episodicdb
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OLAP-based memory engine for AI agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/KsPsD/EpisodicDB
|
|
6
|
+
Project-URL: Repository, https://github.com/KsPsD/EpisodicDB
|
|
7
|
+
Project-URL: Issues, https://github.com/KsPsD/EpisodicDB/issues
|
|
8
|
+
Author: KsPsD
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,ai,duckdb,mcp,memory,olap,vector
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: duckdb>=1.2.0
|
|
23
|
+
Requires-Dist: mcp[cli]>=1.0.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# EpisodicDB
|
|
29
|
+
|
|
30
|
+
**An OLAP-based memory engine for AI agents.**
|
|
31
|
+
|
|
32
|
+
Existing agent memory systems (Mem0, Zep, Letta) are designed as *search systems*. EpisodicDB treats agent memory as an *analytics problem* — aggregation, time-series patterns, causal tracing, temporal facts, and vector similarity all in a single query engine.
|
|
33
|
+
|
|
34
|
+
```sql
|
|
35
|
+
-- "Which tools failed most this week?"
|
|
36
|
+
SELECT tool_name, COUNT(*) AS failures
|
|
37
|
+
FROM tool_calls
|
|
38
|
+
WHERE outcome = 'failure'
|
|
39
|
+
AND called_at >= NOW() - INTERVAL '7 days'
|
|
40
|
+
GROUP BY tool_name
|
|
41
|
+
ORDER BY failures DESC;
|
|
42
|
+
|
|
43
|
+
-- "Find past episodes similar to this context that succeeded"
|
|
44
|
+
SELECT *, array_cosine_distance(context_embedding, ?) AS dist
|
|
45
|
+
FROM episodes
|
|
46
|
+
WHERE status = 'success'
|
|
47
|
+
ORDER BY dist ASC
|
|
48
|
+
LIMIT 5;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## The Problem
|
|
52
|
+
|
|
53
|
+
| Query type | Example | Vector search? |
|
|
54
|
+
|------------|---------|----------------|
|
|
55
|
+
| Similarity | "Seen a similar error before?" | Yes |
|
|
56
|
+
| Aggregation | "How many tool failures this week?" | No |
|
|
57
|
+
| Time-series | "Do failures spike in the afternoon?" | No |
|
|
58
|
+
| Causal trace | "What tool ran right before failures?" | No |
|
|
59
|
+
| Comparison | "Worse than last week?" | No |
|
|
60
|
+
| Absence | "Tools that never succeeded?" | No |
|
|
61
|
+
| Temporal | "What was the user's timezone last Tuesday?" | No |
|
|
62
|
+
|
|
63
|
+
EpisodicDB answers all of them. Vector similarity is just another SQL operator.
|
|
64
|
+
|
|
65
|
+
## Architecture
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
EpisodicDB
|
|
69
|
+
├── WriterMixin record_episode / record_tool_call / record_decision / record_fact
|
|
70
|
+
├── AnalyticsMixin 6 analytics methods + vector similarity
|
|
71
|
+
└── TemporalMixin facts_as_of / fact_history
|
|
72
|
+
|
|
73
|
+
Engine: DuckDB (OLAP) + VSS extension (HNSW vector index)
|
|
74
|
+
Schema: episodes + tool_calls + decisions + facts
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install episodicdb
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Quick Start
|
|
84
|
+
|
|
85
|
+
### Python SDK
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from episodicdb import EpisodicDB
|
|
89
|
+
|
|
90
|
+
with EpisodicDB(agent_id="my-agent") as db:
|
|
91
|
+
# Record what happened
|
|
92
|
+
ep_id = db.record_episode(
|
|
93
|
+
status="failure",
|
|
94
|
+
task_type="file_edit",
|
|
95
|
+
context={"file": "auth.py", "error": "permission denied"},
|
|
96
|
+
)
|
|
97
|
+
db.record_tool_call(ep_id, "Edit", "failure",
|
|
98
|
+
duration_ms=120, error_message="permission denied")
|
|
99
|
+
db.record_tool_call(ep_id, "Bash", "success", duration_ms=50)
|
|
100
|
+
|
|
101
|
+
# Record temporal facts (auto-supersedes previous values)
|
|
102
|
+
db.record_fact("user_timezone", "Asia/Seoul", episode_id=ep_id)
|
|
103
|
+
db.record_fact("user_timezone", "America/New_York") # closes previous
|
|
104
|
+
|
|
105
|
+
# Analyze patterns
|
|
106
|
+
print(db.top_failing_tools(days=7))
|
|
107
|
+
# [{"tool_name": "Edit", "failures": 5}, ...]
|
|
108
|
+
|
|
109
|
+
print(db.before_failure_sequence("Edit"))
|
|
110
|
+
# [{"prev_tool": "Bash", "count": 4}, ...]
|
|
111
|
+
|
|
112
|
+
print(db.compare_periods("failure_rate", days=7))
|
|
113
|
+
# {"period_a": 0.32, "period_b": 0.18, "delta": 0.14}
|
|
114
|
+
|
|
115
|
+
# Time-travel query
|
|
116
|
+
from datetime import datetime
|
|
117
|
+
print(db.facts_as_of(datetime(2025, 3, 15)))
|
|
118
|
+
# [{"key": "user_timezone", "value": "Asia/Seoul", ...}]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### MCP Server (Claude, OpenAI Agents SDK)
|
|
122
|
+
|
|
123
|
+
EpisodicDB ships an MCP server with 12 tools over stdio.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
episodicdb-mcp --agent-id my-agent
|
|
127
|
+
episodicdb-mcp --agent-id my-agent --db ./memory.db
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Claude Desktop** (`claude_desktop_config.json`):
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"episodicdb": {
|
|
136
|
+
"command": "episodicdb-mcp",
|
|
137
|
+
"args": ["--agent-id", "my-agent"]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Claude Code** (`.mcp.json`):
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"mcpServers": {
|
|
148
|
+
"episodicdb": {
|
|
149
|
+
"command": "episodicdb-mcp",
|
|
150
|
+
"args": ["--agent-id", "my-agent"]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**OpenAI Agents SDK**:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from agents import Agent
|
|
160
|
+
from agents.mcp import MCPServerStdio
|
|
161
|
+
|
|
162
|
+
agent = Agent(
|
|
163
|
+
name="my-agent",
|
|
164
|
+
instructions="You have access to episodic memory.",
|
|
165
|
+
mcp_servers=[MCPServerStdio(
|
|
166
|
+
command="episodicdb-mcp",
|
|
167
|
+
args=["--agent-id", "my-agent"],
|
|
168
|
+
)],
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## API
|
|
173
|
+
|
|
174
|
+
### Writer
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
db.record_episode(status, task_type=None, context=None,
|
|
178
|
+
embedding=None, tags=None,
|
|
179
|
+
started_at=None, ended_at=None) -> str # episode UUID
|
|
180
|
+
|
|
181
|
+
db.record_tool_call(episode_id, tool_name, outcome,
|
|
182
|
+
parameters=None, result=None,
|
|
183
|
+
duration_ms=None, error_message=None) -> str
|
|
184
|
+
|
|
185
|
+
db.record_decision(episode_id, rationale,
|
|
186
|
+
decision_type=None, alternatives=None,
|
|
187
|
+
outcome=None) -> str
|
|
188
|
+
|
|
189
|
+
db.record_fact(key, value, episode_id=None,
|
|
190
|
+
valid_from=None) -> str # auto-supersedes previous
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Analytics
|
|
194
|
+
|
|
195
|
+
| Method | Description |
|
|
196
|
+
|--------|-------------|
|
|
197
|
+
| `top_failing_tools(days, limit)` | Most-failed tools in the last N days |
|
|
198
|
+
| `hourly_failure_rate(days)` | Failure count by hour of day |
|
|
199
|
+
| `before_failure_sequence(tool_name, lookback)` | Tools that precede failures |
|
|
200
|
+
| `compare_periods(metric, days)` | Period-over-period comparison |
|
|
201
|
+
| `never_succeeded_tools()` | Tools with zero successful calls |
|
|
202
|
+
| `similar_episodes(embedding, status, limit)` | Vector similarity + SQL filter |
|
|
203
|
+
|
|
204
|
+
### Temporal Facts
|
|
205
|
+
|
|
206
|
+
Facts are key-value pairs with automatic temporal validity. Recording a new value for the same key closes the previous one.
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
db.record_fact("preferred_model", "gpt-4o")
|
|
210
|
+
# later...
|
|
211
|
+
db.record_fact("preferred_model", "claude-sonnet") # supersedes gpt-4o
|
|
212
|
+
|
|
213
|
+
db.facts_as_of(some_datetime) # point-in-time snapshot
|
|
214
|
+
db.fact_history("preferred_model") # full change log
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Persistence
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
EpisodicDB(agent_id="my-agent") # ~/.episodicdb/my-agent.db
|
|
221
|
+
EpisodicDB(agent_id="my-agent", path="./x.db") # explicit path
|
|
222
|
+
EpisodicDB(agent_id="my-agent", path=":memory:") # in-memory (testing)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Embeddings
|
|
226
|
+
|
|
227
|
+
EpisodicDB does not generate embeddings. Pass them in:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
import openai
|
|
231
|
+
|
|
232
|
+
response = openai.embeddings.create(
|
|
233
|
+
model="text-embedding-3-small",
|
|
234
|
+
input="what the agent was doing"
|
|
235
|
+
)
|
|
236
|
+
embedding = response.data[0].embedding # 1536 dims
|
|
237
|
+
|
|
238
|
+
db.record_episode(status="success", embedding=embedding)
|
|
239
|
+
db.similar_episodes(embedding, status="failure", limit=5)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Development
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
git clone https://github.com/KsPsD/EpisodicDB
|
|
246
|
+
cd EpisodicDB
|
|
247
|
+
pip install -e ".[dev]"
|
|
248
|
+
pytest
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Stack
|
|
252
|
+
|
|
253
|
+
- [DuckDB](https://duckdb.org/) — embedded OLAP engine
|
|
254
|
+
- [DuckDB VSS](https://duckdb.org/docs/extensions/vss) — HNSW vector index
|
|
255
|
+
- [MCP](https://modelcontextprotocol.io/) — Model Context Protocol server
|
|
256
|
+
- Python 3.11+
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# EpisodicDB
|
|
2
|
+
|
|
3
|
+
**An OLAP-based memory engine for AI agents.**
|
|
4
|
+
|
|
5
|
+
Existing agent memory systems (Mem0, Zep, Letta) are designed as *search systems*. EpisodicDB treats agent memory as an *analytics problem* — aggregation, time-series patterns, causal tracing, temporal facts, and vector similarity all in a single query engine.
|
|
6
|
+
|
|
7
|
+
```sql
|
|
8
|
+
-- "Which tools failed most this week?"
|
|
9
|
+
SELECT tool_name, COUNT(*) AS failures
|
|
10
|
+
FROM tool_calls
|
|
11
|
+
WHERE outcome = 'failure'
|
|
12
|
+
AND called_at >= NOW() - INTERVAL '7 days'
|
|
13
|
+
GROUP BY tool_name
|
|
14
|
+
ORDER BY failures DESC;
|
|
15
|
+
|
|
16
|
+
-- "Find past episodes similar to this context that succeeded"
|
|
17
|
+
SELECT *, array_cosine_distance(context_embedding, ?) AS dist
|
|
18
|
+
FROM episodes
|
|
19
|
+
WHERE status = 'success'
|
|
20
|
+
ORDER BY dist ASC
|
|
21
|
+
LIMIT 5;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## The Problem
|
|
25
|
+
|
|
26
|
+
| Query type | Example | Vector search? |
|
|
27
|
+
|------------|---------|----------------|
|
|
28
|
+
| Similarity | "Seen a similar error before?" | Yes |
|
|
29
|
+
| Aggregation | "How many tool failures this week?" | No |
|
|
30
|
+
| Time-series | "Do failures spike in the afternoon?" | No |
|
|
31
|
+
| Causal trace | "What tool ran right before failures?" | No |
|
|
32
|
+
| Comparison | "Worse than last week?" | No |
|
|
33
|
+
| Absence | "Tools that never succeeded?" | No |
|
|
34
|
+
| Temporal | "What was the user's timezone last Tuesday?" | No |
|
|
35
|
+
|
|
36
|
+
EpisodicDB answers all of them. Vector similarity is just another SQL operator.
|
|
37
|
+
|
|
38
|
+
## Architecture
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
EpisodicDB
|
|
42
|
+
├── WriterMixin record_episode / record_tool_call / record_decision / record_fact
|
|
43
|
+
├── AnalyticsMixin 6 analytics methods + vector similarity
|
|
44
|
+
└── TemporalMixin facts_as_of / fact_history
|
|
45
|
+
|
|
46
|
+
Engine: DuckDB (OLAP) + VSS extension (HNSW vector index)
|
|
47
|
+
Schema: episodes + tool_calls + decisions + facts
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install episodicdb
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
### Python SDK
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from episodicdb import EpisodicDB
|
|
62
|
+
|
|
63
|
+
with EpisodicDB(agent_id="my-agent") as db:
|
|
64
|
+
# Record what happened
|
|
65
|
+
ep_id = db.record_episode(
|
|
66
|
+
status="failure",
|
|
67
|
+
task_type="file_edit",
|
|
68
|
+
context={"file": "auth.py", "error": "permission denied"},
|
|
69
|
+
)
|
|
70
|
+
db.record_tool_call(ep_id, "Edit", "failure",
|
|
71
|
+
duration_ms=120, error_message="permission denied")
|
|
72
|
+
db.record_tool_call(ep_id, "Bash", "success", duration_ms=50)
|
|
73
|
+
|
|
74
|
+
# Record temporal facts (auto-supersedes previous values)
|
|
75
|
+
db.record_fact("user_timezone", "Asia/Seoul", episode_id=ep_id)
|
|
76
|
+
db.record_fact("user_timezone", "America/New_York") # closes previous
|
|
77
|
+
|
|
78
|
+
# Analyze patterns
|
|
79
|
+
print(db.top_failing_tools(days=7))
|
|
80
|
+
# [{"tool_name": "Edit", "failures": 5}, ...]
|
|
81
|
+
|
|
82
|
+
print(db.before_failure_sequence("Edit"))
|
|
83
|
+
# [{"prev_tool": "Bash", "count": 4}, ...]
|
|
84
|
+
|
|
85
|
+
print(db.compare_periods("failure_rate", days=7))
|
|
86
|
+
# {"period_a": 0.32, "period_b": 0.18, "delta": 0.14}
|
|
87
|
+
|
|
88
|
+
# Time-travel query
|
|
89
|
+
from datetime import datetime
|
|
90
|
+
print(db.facts_as_of(datetime(2025, 3, 15)))
|
|
91
|
+
# [{"key": "user_timezone", "value": "Asia/Seoul", ...}]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### MCP Server (Claude, OpenAI Agents SDK)
|
|
95
|
+
|
|
96
|
+
EpisodicDB ships an MCP server with 12 tools over stdio.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
episodicdb-mcp --agent-id my-agent
|
|
100
|
+
episodicdb-mcp --agent-id my-agent --db ./memory.db
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Claude Desktop** (`claude_desktop_config.json`):
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"episodicdb": {
|
|
109
|
+
"command": "episodicdb-mcp",
|
|
110
|
+
"args": ["--agent-id", "my-agent"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Claude Code** (`.mcp.json`):
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"episodicdb": {
|
|
122
|
+
"command": "episodicdb-mcp",
|
|
123
|
+
"args": ["--agent-id", "my-agent"]
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**OpenAI Agents SDK**:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from agents import Agent
|
|
133
|
+
from agents.mcp import MCPServerStdio
|
|
134
|
+
|
|
135
|
+
agent = Agent(
|
|
136
|
+
name="my-agent",
|
|
137
|
+
instructions="You have access to episodic memory.",
|
|
138
|
+
mcp_servers=[MCPServerStdio(
|
|
139
|
+
command="episodicdb-mcp",
|
|
140
|
+
args=["--agent-id", "my-agent"],
|
|
141
|
+
)],
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## API
|
|
146
|
+
|
|
147
|
+
### Writer
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
db.record_episode(status, task_type=None, context=None,
|
|
151
|
+
embedding=None, tags=None,
|
|
152
|
+
started_at=None, ended_at=None) -> str # episode UUID
|
|
153
|
+
|
|
154
|
+
db.record_tool_call(episode_id, tool_name, outcome,
|
|
155
|
+
parameters=None, result=None,
|
|
156
|
+
duration_ms=None, error_message=None) -> str
|
|
157
|
+
|
|
158
|
+
db.record_decision(episode_id, rationale,
|
|
159
|
+
decision_type=None, alternatives=None,
|
|
160
|
+
outcome=None) -> str
|
|
161
|
+
|
|
162
|
+
db.record_fact(key, value, episode_id=None,
|
|
163
|
+
valid_from=None) -> str # auto-supersedes previous
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Analytics
|
|
167
|
+
|
|
168
|
+
| Method | Description |
|
|
169
|
+
|--------|-------------|
|
|
170
|
+
| `top_failing_tools(days, limit)` | Most-failed tools in the last N days |
|
|
171
|
+
| `hourly_failure_rate(days)` | Failure count by hour of day |
|
|
172
|
+
| `before_failure_sequence(tool_name, lookback)` | Tools that precede failures |
|
|
173
|
+
| `compare_periods(metric, days)` | Period-over-period comparison |
|
|
174
|
+
| `never_succeeded_tools()` | Tools with zero successful calls |
|
|
175
|
+
| `similar_episodes(embedding, status, limit)` | Vector similarity + SQL filter |
|
|
176
|
+
|
|
177
|
+
### Temporal Facts
|
|
178
|
+
|
|
179
|
+
Facts are key-value pairs with automatic temporal validity. Recording a new value for the same key closes the previous one.
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
db.record_fact("preferred_model", "gpt-4o")
|
|
183
|
+
# later...
|
|
184
|
+
db.record_fact("preferred_model", "claude-sonnet") # supersedes gpt-4o
|
|
185
|
+
|
|
186
|
+
db.facts_as_of(some_datetime) # point-in-time snapshot
|
|
187
|
+
db.fact_history("preferred_model") # full change log
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Persistence
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
EpisodicDB(agent_id="my-agent") # ~/.episodicdb/my-agent.db
|
|
194
|
+
EpisodicDB(agent_id="my-agent", path="./x.db") # explicit path
|
|
195
|
+
EpisodicDB(agent_id="my-agent", path=":memory:") # in-memory (testing)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Embeddings
|
|
199
|
+
|
|
200
|
+
EpisodicDB does not generate embeddings. Pass them in:
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
import openai
|
|
204
|
+
|
|
205
|
+
response = openai.embeddings.create(
|
|
206
|
+
model="text-embedding-3-small",
|
|
207
|
+
input="what the agent was doing"
|
|
208
|
+
)
|
|
209
|
+
embedding = response.data[0].embedding # 1536 dims
|
|
210
|
+
|
|
211
|
+
db.record_episode(status="success", embedding=embedding)
|
|
212
|
+
db.similar_episodes(embedding, status="failure", limit=5)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Development
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
git clone https://github.com/KsPsD/EpisodicDB
|
|
219
|
+
cd EpisodicDB
|
|
220
|
+
pip install -e ".[dev]"
|
|
221
|
+
pytest
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Stack
|
|
225
|
+
|
|
226
|
+
- [DuckDB](https://duckdb.org/) — embedded OLAP engine
|
|
227
|
+
- [DuckDB VSS](https://duckdb.org/docs/extensions/vss) — HNSW vector index
|
|
228
|
+
- [MCP](https://modelcontextprotocol.io/) — Model Context Protocol server
|
|
229
|
+
- Python 3.11+
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT
|