smolAmem 1.0.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.
- smolamem-1.0.0/.env.example +44 -0
- smolamem-1.0.0/.github/workflows/docs.yml +71 -0
- smolamem-1.0.0/.github/workflows/release.yml +150 -0
- smolamem-1.0.0/.gitignore +82 -0
- smolamem-1.0.0/CHANGELOG.md +133 -0
- smolamem-1.0.0/LICENSE +21 -0
- smolamem-1.0.0/PKG-INFO +122 -0
- smolamem-1.0.0/README.md +78 -0
- smolamem-1.0.0/docker-compose.yml +47 -0
- smolamem-1.0.0/docs/adapters.md +149 -0
- smolamem-1.0.0/docs/api.md +81 -0
- smolamem-1.0.0/docs/backends.md +181 -0
- smolamem-1.0.0/docs/concepts.md +200 -0
- smolamem-1.0.0/docs/embedders.md +98 -0
- smolamem-1.0.0/docs/eval.md +213 -0
- smolamem-1.0.0/docs/index.md +115 -0
- smolamem-1.0.0/docs/quickstart.md +180 -0
- smolamem-1.0.0/evals/README.md +162 -0
- smolamem-1.0.0/evals/__init__.py +19 -0
- smolamem-1.0.0/evals/__main__.py +217 -0
- smolamem-1.0.0/evals/baselines/__init__.py +19 -0
- smolamem-1.0.0/evals/baselines/base.py +64 -0
- smolamem-1.0.0/evals/baselines/full_history.py +43 -0
- smolamem-1.0.0/evals/baselines/no_memory.py +31 -0
- smolamem-1.0.0/evals/baselines/summary_buffer.py +93 -0
- smolamem-1.0.0/evals/corpus/001_typescript_preference.json +28 -0
- smolamem-1.0.0/evals/corpus/002_react_to_vue_switch.json +28 -0
- smolamem-1.0.0/evals/corpus/003_project_facts.json +37 -0
- smolamem-1.0.0/evals/corpus/004_time_bounded_request.json +26 -0
- smolamem-1.0.0/evals/corpus/005_multi_fact_recall.json +26 -0
- smolamem-1.0.0/evals/corpus/README.md +80 -0
- smolamem-1.0.0/evals/metrics/__init__.py +11 -0
- smolamem-1.0.0/evals/metrics/recall.py +59 -0
- smolamem-1.0.0/evals/metrics/tokens.py +67 -0
- smolamem-1.0.0/evals/runners/__init__.py +11 -0
- smolamem-1.0.0/evals/runners/base.py +123 -0
- smolamem-1.0.0/evals/runners/mneme.py +137 -0
- smolamem-1.0.0/evals/schema.py +93 -0
- smolamem-1.0.0/mkdocs.yml +104 -0
- smolamem-1.0.0/pyproject.toml +175 -0
- smolamem-1.0.0/scripts/check.ps1 +46 -0
- smolamem-1.0.0/scripts/smoke.py +229 -0
- smolamem-1.0.0/src/mneme/__init__.py +57 -0
- smolamem-1.0.0/src/mneme/adapters/__init__.py +58 -0
- smolamem-1.0.0/src/mneme/adapters/langchain.py +143 -0
- smolamem-1.0.0/src/mneme/adapters/llamaindex.py +151 -0
- smolamem-1.0.0/src/mneme/adapters/openai_helper.py +192 -0
- smolamem-1.0.0/src/mneme/backends/__init__.py +33 -0
- smolamem-1.0.0/src/mneme/backends/base.py +176 -0
- smolamem-1.0.0/src/mneme/backends/memory.py +170 -0
- smolamem-1.0.0/src/mneme/backends/pgvector.py +400 -0
- smolamem-1.0.0/src/mneme/backends/qdrant.py +448 -0
- smolamem-1.0.0/src/mneme/backends/sqlite.py +493 -0
- smolamem-1.0.0/src/mneme/embeddings.py +212 -0
- smolamem-1.0.0/src/mneme/judge.py +209 -0
- smolamem-1.0.0/src/mneme/manager.py +750 -0
- smolamem-1.0.0/src/mneme/py.typed +0 -0
- smolamem-1.0.0/src/mneme/tiers/__init__.py +17 -0
- smolamem-1.0.0/src/mneme/tiers/episodic.py +108 -0
- smolamem-1.0.0/src/mneme/tiers/semantic.py +119 -0
- smolamem-1.0.0/src/mneme/tiers/working.py +78 -0
- smolamem-1.0.0/src/mneme/types.py +156 -0
- smolamem-1.0.0/tests/__init__.py +0 -0
- smolamem-1.0.0/tests/conftest.py +39 -0
- smolamem-1.0.0/tests/test_adapter_langchain.py +161 -0
- smolamem-1.0.0/tests/test_adapter_llamaindex.py +140 -0
- smolamem-1.0.0/tests/test_adapter_openai.py +180 -0
- smolamem-1.0.0/tests/test_backends.py +571 -0
- smolamem-1.0.0/tests/test_consolidate.py +389 -0
- smolamem-1.0.0/tests/test_embeddings.py +173 -0
- smolamem-1.0.0/tests/test_eval_harness.py +229 -0
- smolamem-1.0.0/tests/test_forget.py +330 -0
- smolamem-1.0.0/tests/test_judge.py +165 -0
- smolamem-1.0.0/tests/test_manager.py +133 -0
- smolamem-1.0.0/tests/test_retrieve.py +356 -0
- smolamem-1.0.0/tests/test_scheduler.py +183 -0
- smolamem-1.0.0/tests/test_smoke.py +17 -0
- smolamem-1.0.0/tests/test_tier_episodic.py +151 -0
- smolamem-1.0.0/tests/test_tier_semantic.py +127 -0
- smolamem-1.0.0/tests/test_tier_working.py +77 -0
- smolamem-1.0.0/tests/test_types.py +81 -0
- smolamem-1.0.0/uv.lock +3555 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Example .env file. Copy to `.env` (gitignored) and fill in real values.
|
|
2
|
+
#
|
|
3
|
+
# `tests/conftest.py` loads this on test startup so any test using
|
|
4
|
+
# OpenAIEmbeddings (or any other env-dependent code) just works locally.
|
|
5
|
+
# The library itself never auto-loads dotenv.
|
|
6
|
+
#
|
|
7
|
+
# IMPORTANT — Windows / PowerShell users:
|
|
8
|
+
# Do NOT create .env with `echo ... > .env`. PowerShell's `>` redirection
|
|
9
|
+
# writes UTF-16 LE with a BOM, and python-dotenv reads UTF-8. The test
|
|
10
|
+
# suite will warn and skip key-dependent tests instead of crashing, but
|
|
11
|
+
# your tests still won't see the key.
|
|
12
|
+
#
|
|
13
|
+
# Correct ways to create `.env` on Windows:
|
|
14
|
+
# * Open `.env.example` in VS Code / Notepad, "Save As" -> `.env` with
|
|
15
|
+
# UTF-8 encoding.
|
|
16
|
+
# * Or PowerShell with explicit encoding:
|
|
17
|
+
# Set-Content -Encoding utf8 -Path .env -Value 'OPENAI_API_KEY=sk-...'
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# OpenAI (embeddings + LLM judge)
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
# Required for OpenAIEmbeddings + OpenAILLMJudge integration tests. Without
|
|
24
|
+
# it the OpenAI-specific tests skip cleanly.
|
|
25
|
+
# Get one at https://platform.openai.com/api-keys.
|
|
26
|
+
OPENAI_API_KEY=sk-...
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Backend integration tests (v0.4)
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Setting either of the next two env vars opts that backend into the
|
|
32
|
+
# conformance suite. With neither set, qdrant + pgvector tests skip and
|
|
33
|
+
# memory + sqlite still run normally.
|
|
34
|
+
#
|
|
35
|
+
# To spin both services up locally:
|
|
36
|
+
# docker compose up -d
|
|
37
|
+
#
|
|
38
|
+
# Then uncomment the lines below.
|
|
39
|
+
|
|
40
|
+
# Qdrant: default REST endpoint exposed by docker-compose.yml.
|
|
41
|
+
# MNEME_TEST_QDRANT_URL=http://localhost:6333
|
|
42
|
+
|
|
43
|
+
# pgvector: connection string matching docker-compose.yml's defaults.
|
|
44
|
+
# MNEME_TEST_PGVECTOR_DSN=postgresql://mneme:mneme@localhost:5433/mneme
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Build + deploy the mkdocs-material site to GitHub Pages.
|
|
2
|
+
#
|
|
3
|
+
# Runs on every push to main of the public mirror repo. The private dev repo
|
|
4
|
+
# uses the same workflow file (it's part of the published tree) but won't
|
|
5
|
+
# have Pages enabled, so the deploy step no-ops there if the GITHUB_PAGES
|
|
6
|
+
# environment isn't configured. To enable for a fresh fork:
|
|
7
|
+
#
|
|
8
|
+
# 1. Settings -> Pages -> Source: "GitHub Actions"
|
|
9
|
+
# 2. Push to main. The workflow takes over from there.
|
|
10
|
+
name: Build docs
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
branches: [main]
|
|
15
|
+
paths:
|
|
16
|
+
- "docs/**"
|
|
17
|
+
- "mkdocs.yml"
|
|
18
|
+
- "src/**"
|
|
19
|
+
- "pyproject.toml"
|
|
20
|
+
- ".github/workflows/docs.yml"
|
|
21
|
+
workflow_dispatch:
|
|
22
|
+
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
pages: write
|
|
26
|
+
id-token: write
|
|
27
|
+
|
|
28
|
+
# Only one in-flight docs build at a time; cancel any superseded run.
|
|
29
|
+
concurrency:
|
|
30
|
+
group: "pages"
|
|
31
|
+
cancel-in-progress: false
|
|
32
|
+
|
|
33
|
+
jobs:
|
|
34
|
+
build:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
|
|
39
|
+
- name: Set up Python
|
|
40
|
+
uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: "3.12"
|
|
43
|
+
|
|
44
|
+
- name: Install uv
|
|
45
|
+
uses: astral-sh/setup-uv@v3
|
|
46
|
+
with:
|
|
47
|
+
enable-cache: true
|
|
48
|
+
|
|
49
|
+
# Install the project with the [docs] extra. mkdocstrings autogen needs
|
|
50
|
+
# the package importable, so we install + the extra in one shot.
|
|
51
|
+
- name: Install project + docs deps
|
|
52
|
+
run: uv sync --extra docs
|
|
53
|
+
|
|
54
|
+
- name: Build site
|
|
55
|
+
run: uv run mkdocs build --strict
|
|
56
|
+
|
|
57
|
+
- name: Upload artifact
|
|
58
|
+
uses: actions/upload-pages-artifact@v3
|
|
59
|
+
with:
|
|
60
|
+
path: site
|
|
61
|
+
|
|
62
|
+
deploy:
|
|
63
|
+
needs: build
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
environment:
|
|
66
|
+
name: github-pages
|
|
67
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
68
|
+
steps:
|
|
69
|
+
- name: Deploy to GitHub Pages
|
|
70
|
+
id: deployment
|
|
71
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Build a wheel + sdist and publish to PyPI via trusted publishing (OIDC).
|
|
2
|
+
#
|
|
3
|
+
# Triggers on tags matching ``v*`` pushed to the repo. No long-lived API
|
|
4
|
+
# token is stored anywhere — PyPI verifies the OIDC token GitHub Actions
|
|
5
|
+
# issues and authorises the upload directly.
|
|
6
|
+
#
|
|
7
|
+
# One-time PyPI setup (done in the PyPI web UI, not here):
|
|
8
|
+
#
|
|
9
|
+
# 1. Create an account at https://pypi.org/account/register/ if you
|
|
10
|
+
# don't have one.
|
|
11
|
+
# 2. Go to https://pypi.org/manage/account/publishing/ and add a
|
|
12
|
+
# "pending publisher":
|
|
13
|
+
# - PyPI Project Name: smolAmem
|
|
14
|
+
# - Owner: AshwinUgale
|
|
15
|
+
# - Repository name: ashwinugale-mneme
|
|
16
|
+
# - Workflow name: release.yml
|
|
17
|
+
# - Environment name: pypi (must match the `environment:` below)
|
|
18
|
+
# 3. The first time this workflow runs successfully, PyPI creates the
|
|
19
|
+
# real project and removes the "pending" status. Subsequent releases
|
|
20
|
+
# use the same publisher config without any further setup.
|
|
21
|
+
#
|
|
22
|
+
# Cutting a release:
|
|
23
|
+
#
|
|
24
|
+
# git tag v1.0.0
|
|
25
|
+
# git push private --tags # (your private remote)
|
|
26
|
+
# ./.cowork/publish.sh "v1.0.0" # also push to the public mirror
|
|
27
|
+
# # then on the PUBLIC mirror repo, create a tag matching the version:
|
|
28
|
+
# # git tag v1.0.0
|
|
29
|
+
# # git push origin v1.0.0
|
|
30
|
+
# # That tag push fires this workflow.
|
|
31
|
+
|
|
32
|
+
name: Release
|
|
33
|
+
|
|
34
|
+
on:
|
|
35
|
+
push:
|
|
36
|
+
tags:
|
|
37
|
+
- "v*"
|
|
38
|
+
workflow_dispatch:
|
|
39
|
+
|
|
40
|
+
permissions:
|
|
41
|
+
contents: read
|
|
42
|
+
|
|
43
|
+
concurrency:
|
|
44
|
+
group: release-${{ github.ref }}
|
|
45
|
+
cancel-in-progress: false
|
|
46
|
+
|
|
47
|
+
jobs:
|
|
48
|
+
build:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
|
|
53
|
+
- name: Set up Python
|
|
54
|
+
uses: actions/setup-python@v5
|
|
55
|
+
with:
|
|
56
|
+
python-version: "3.12"
|
|
57
|
+
|
|
58
|
+
- name: Install uv
|
|
59
|
+
uses: astral-sh/setup-uv@v3
|
|
60
|
+
with:
|
|
61
|
+
enable-cache: true
|
|
62
|
+
|
|
63
|
+
- name: Verify tag matches package version
|
|
64
|
+
run: |
|
|
65
|
+
set -e
|
|
66
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
67
|
+
PKG_VERSION="$(uv run --no-project python -c '
|
|
68
|
+
import tomllib, pathlib
|
|
69
|
+
print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])
|
|
70
|
+
')"
|
|
71
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
72
|
+
echo "::error::tag $TAG_VERSION does not match pyproject version $PKG_VERSION"
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
echo "tag and pyproject agree on version $PKG_VERSION"
|
|
76
|
+
|
|
77
|
+
- name: Build sdist + wheel
|
|
78
|
+
run: uv build
|
|
79
|
+
|
|
80
|
+
- name: List build artifacts
|
|
81
|
+
run: ls -la dist/
|
|
82
|
+
|
|
83
|
+
- name: Upload artifacts
|
|
84
|
+
uses: actions/upload-artifact@v4
|
|
85
|
+
with:
|
|
86
|
+
name: dist
|
|
87
|
+
path: dist/
|
|
88
|
+
|
|
89
|
+
publish-pypi:
|
|
90
|
+
needs: build
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
# The environment name MUST match what's registered as the
|
|
93
|
+
# trusted-publisher environment in PyPI's "pending publisher"
|
|
94
|
+
# config. PyPI will reject the upload otherwise.
|
|
95
|
+
environment:
|
|
96
|
+
name: pypi
|
|
97
|
+
url: https://pypi.org/p/smolAmem
|
|
98
|
+
permissions:
|
|
99
|
+
# id-token: write is what lets GitHub mint the OIDC token PyPI
|
|
100
|
+
# uses to authenticate. contents: read is the default.
|
|
101
|
+
id-token: write
|
|
102
|
+
contents: read
|
|
103
|
+
steps:
|
|
104
|
+
- name: Download build artifacts
|
|
105
|
+
uses: actions/download-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
name: dist
|
|
108
|
+
path: dist/
|
|
109
|
+
|
|
110
|
+
- name: Publish to PyPI
|
|
111
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
112
|
+
with:
|
|
113
|
+
# No `password:` / `user:` — trusted publishing handles auth.
|
|
114
|
+
# `verbose: true` makes the upload log show why if it fails.
|
|
115
|
+
verbose: true
|
|
116
|
+
|
|
117
|
+
github-release:
|
|
118
|
+
needs: publish-pypi
|
|
119
|
+
runs-on: ubuntu-latest
|
|
120
|
+
permissions:
|
|
121
|
+
contents: write
|
|
122
|
+
steps:
|
|
123
|
+
- uses: actions/checkout@v4
|
|
124
|
+
with:
|
|
125
|
+
fetch-depth: 0
|
|
126
|
+
|
|
127
|
+
- name: Download build artifacts
|
|
128
|
+
uses: actions/download-artifact@v4
|
|
129
|
+
with:
|
|
130
|
+
name: dist
|
|
131
|
+
path: dist/
|
|
132
|
+
|
|
133
|
+
- name: Create GitHub Release
|
|
134
|
+
uses: softprops/action-gh-release@v2
|
|
135
|
+
with:
|
|
136
|
+
tag_name: ${{ github.ref_name }}
|
|
137
|
+
name: ${{ github.ref_name }}
|
|
138
|
+
body: |
|
|
139
|
+
See [CHANGELOG.md](./CHANGELOG.md) for what's in this release.
|
|
140
|
+
|
|
141
|
+
Install:
|
|
142
|
+
|
|
143
|
+
pip install smolAmem==${{ github.ref_name }}
|
|
144
|
+
|
|
145
|
+
(the leading `v` is dropped from the version in `pip` install lines)
|
|
146
|
+
generate_release_notes: true
|
|
147
|
+
fail_on_unmatched_files: true
|
|
148
|
+
files: |
|
|
149
|
+
dist/*.whl
|
|
150
|
+
dist/*.tar.gz
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
*.egg
|
|
21
|
+
MANIFEST
|
|
22
|
+
|
|
23
|
+
# uv
|
|
24
|
+
.venv/
|
|
25
|
+
.uv-cache/
|
|
26
|
+
|
|
27
|
+
# Testing / coverage
|
|
28
|
+
.pytest_cache/
|
|
29
|
+
.coverage
|
|
30
|
+
.coverage.*
|
|
31
|
+
htmlcov/
|
|
32
|
+
.tox/
|
|
33
|
+
.nox/
|
|
34
|
+
coverage.xml
|
|
35
|
+
*.cover
|
|
36
|
+
.cache/
|
|
37
|
+
|
|
38
|
+
# Mypy / ruff
|
|
39
|
+
.mypy_cache/
|
|
40
|
+
.dmypy.json
|
|
41
|
+
dmypy.json
|
|
42
|
+
.ruff_cache/
|
|
43
|
+
|
|
44
|
+
# Type checker
|
|
45
|
+
.pyre/
|
|
46
|
+
.pytype/
|
|
47
|
+
|
|
48
|
+
# IDEs
|
|
49
|
+
.idea/
|
|
50
|
+
.vscode/
|
|
51
|
+
*.swp
|
|
52
|
+
*.swo
|
|
53
|
+
*~
|
|
54
|
+
|
|
55
|
+
# Claude Code local config / memory (machine-local, never commit)
|
|
56
|
+
.claude/
|
|
57
|
+
|
|
58
|
+
# OS
|
|
59
|
+
.DS_Store
|
|
60
|
+
Thumbs.db
|
|
61
|
+
|
|
62
|
+
# Secrets / local env
|
|
63
|
+
.env
|
|
64
|
+
.env.*
|
|
65
|
+
!.env.example
|
|
66
|
+
*.local
|
|
67
|
+
|
|
68
|
+
# SQLite databases created during dev/eval
|
|
69
|
+
*.sqlite
|
|
70
|
+
*.sqlite3
|
|
71
|
+
*.db
|
|
72
|
+
|
|
73
|
+
# Eval artifacts (results get checked in selectively, not blanket)
|
|
74
|
+
.eval-cache/
|
|
75
|
+
out/
|
|
76
|
+
|
|
77
|
+
# mkdocs build output
|
|
78
|
+
site/
|
|
79
|
+
|
|
80
|
+
# NOTE: .cowork/ is intentionally NOT gitignored.
|
|
81
|
+
# It contains private dev context that we want versioned in the PRIVATE repo.
|
|
82
|
+
# It is excluded from the PUBLIC repo via .cowork/publish.sh.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) from `1.0.0` onward.
|
|
4
|
+
|
|
5
|
+
> **Naming note:** install with `pip install smolAmem`; import as `import mneme`. Two-name pattern (same as Pillow/PIL) because `mneme` was already taken on PyPI when this library was published.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
(empty)
|
|
10
|
+
|
|
11
|
+
## [1.0.0] — 2026-05-23
|
|
12
|
+
|
|
13
|
+
First stable release. Public APIs are now under semver; minor bumps add features without breaking, patch bumps fix bugs, major bumps will be rare and pre-announced.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- PyPI release as `smolAmem`. Install with `pip install smolAmem`; import name stays `mneme`.
|
|
17
|
+
- `Development Status :: 5 - Production/Stable` classifier.
|
|
18
|
+
- `CHANGELOG.md` (this file).
|
|
19
|
+
- GitHub Actions release workflow that builds + publishes to PyPI via OIDC trusted publishing (no long-lived API token).
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Dropped pre-alpha warnings from README and docs landing page.
|
|
23
|
+
|
|
24
|
+
## [0.8.0] — 2026-05-23
|
|
25
|
+
|
|
26
|
+
Integration validation. No new features — every previously env-skipped backend test is now actually executed against real Qdrant + pgvector containers, and an end-to-end smoke script proves the full lifecycle works with real OpenAI.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- `scripts/smoke.py` — end-to-end smoke test that wires `MemoryManager` + real `OpenAIEmbeddings` + real `OpenAILLMJudge` against the chosen backend and walks through write → retrieve → consolidate → retrieve → forget. `--backend {sqlite,qdrant,pgvector}`.
|
|
30
|
+
- `scripts/check.ps1` — Windows-native full check pipeline wrapper (ruff + format + mypy + pytest + `mkdocs build --strict`).
|
|
31
|
+
- `.cowork/publish.ps1` — Windows-native equivalent of `publish.sh` using `robocopy` instead of `rsync`.
|
|
32
|
+
- Qdrant payload indexes on `created_at` (DATETIME, required by `order_by`), `agent_id` and `tier` (KEYWORD, performance) created during `_ensure_collection`.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- `QdrantBackend.list_recent()` no longer crashes with `"No range index for order_by key: created_at"`. Caused by missing payload index; now created at construction time.
|
|
36
|
+
- `QdrantBackend.touch()` no longer crashes on IDs that aren't valid hex UUIDs. Now silently skips them, matching the existing `get()` / `delete()` "unknown id" contract.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- README + docs lead with the OpenAI-embedder benchmark (`recall@5 = 1.000` at 67.7 tokens/test point) and keep the HashEmbedder row as the no-API-key deterministic baseline.
|
|
40
|
+
|
|
41
|
+
## [0.7.0] — 2026-05-22
|
|
42
|
+
|
|
43
|
+
Public documentation site.
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- `[docs]` extra: mkdocs, mkdocs-material, mkdocstrings[python], pymdown-extensions.
|
|
47
|
+
- `mkdocs.yml` at repo root (Material theme, indigo palette, full feature set).
|
|
48
|
+
- Eight hand-written documentation pages under `docs/`: index, quickstart, concepts, backends, embedders, adapters, eval, api.
|
|
49
|
+
- `docs/api.md` autogenerated from docstrings via mkdocstrings (Google docstring style, source-order members, filtered to public symbols).
|
|
50
|
+
- `.github/workflows/docs.yml` — build + deploy to GitHub Pages on push to main via `actions/upload-pages-artifact@v3` + `actions/deploy-pages@v4`. `mkdocs build --strict` so any warning fails the build.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- README rewritten with badges, concise pitch, install/quickstart snippet, and links to the live docs site.
|
|
54
|
+
|
|
55
|
+
## [0.6.0] — 2026-05-22
|
|
56
|
+
|
|
57
|
+
Eval harness.
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
- `evals/` sibling directory at repo root (not shipped in the wheel).
|
|
61
|
+
- `evals/schema.py` — Pydantic models for the corpus shape (`Conversation`, `Turn`, `TestPoint`).
|
|
62
|
+
- `evals/corpus/` with five labelled starter conversations covering long-range preference recall, contradiction handling (newer-wins), multi-fact retrieval, in-flight tasks, and multi-fact recall queries.
|
|
63
|
+
- `evals/baselines/` — `MemoryStrategy` Protocol plus three reference strategies: `NoMemoryStrategy` (floor), `SummaryBufferStrategy` (LangChain-style rolling summary), `FullHistoryStrategy` (oracle ceiling).
|
|
64
|
+
- `evals/runners/base.py::EvalRunner` orchestrator + `evals/runners/mneme.py` adapter.
|
|
65
|
+
- `evals/metrics/recall.py::recall_at_k` and `evals/metrics/tokens.py::token_cost_summary` — both pure functions over `RunResult`.
|
|
66
|
+
- `python -m evals` CLI with flags `--corpus`, `--runner`, `--backend`, `--embedder`, `--output`, `--seed`, `--k`. `--with-answers` / `--judge` reserved for future LLM-judged accuracy.
|
|
67
|
+
- `tests/test_eval_harness.py` end-to-end coverage of all four strategies plus the CLI.
|
|
68
|
+
|
|
69
|
+
## [0.5.0] — 2026-05-21
|
|
70
|
+
|
|
71
|
+
Framework adapters.
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
- `mneme.adapters.MnemeChatMessageHistory` — subclass of LangChain's `BaseChatMessageHistory`. Dual-writes to working + episodic by default; `also_persist_episodic=False` for ephemeral chat.
|
|
75
|
+
- `mneme.adapters.MnemeLlamaIndexMemory` — subclass of LlamaIndex's `BaseMemory`. Same dual-write semantics.
|
|
76
|
+
- `mneme.adapters.context_for()` — raw-OpenAI helper returning `messages=[...]` with retrieved memory as a `system` block (with `[FACT id=...]` / `[EPISODE id=...]` citation markers) plus working-memory turns. Optional `token_budget` packs to a tiktoken (`cl100k_base`) count.
|
|
77
|
+
- Three optional extras: `[langchain]`, `[llamaindex]`, `[tokens]`.
|
|
78
|
+
- PEP 562 lazy submodule loading in `mneme.adapters.__init__` so users only pay for the framework deps they actually use.
|
|
79
|
+
|
|
80
|
+
## [0.4.0] — 2026-05-21
|
|
81
|
+
|
|
82
|
+
Production-grade backends.
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
- `mneme.QdrantBackend` — one Qdrant collection per backend instance; multi-tenancy via `agent_id` payload filter; cosine distance; UUID round-trip at the boundary.
|
|
86
|
+
- `mneme.PgVectorBackend` — psycopg3 sync, pgvector extension, `<=>` cosine operator. Schema mirrors `SQLiteBackend`.
|
|
87
|
+
- Conformance suite (`tests/test_backends.py`) now parametrises over `["memory", "sqlite", "qdrant", "pgvector"]`. Env-gated via `MNEME_TEST_QDRANT_URL` / `MNEME_TEST_PGVECTOR_DSN`.
|
|
88
|
+
- `docker-compose.yml` at repo root for local Qdrant + pgvector services.
|
|
89
|
+
- Two new extras: `[qdrant]`, `[pgvector]`.
|
|
90
|
+
|
|
91
|
+
## [0.3.0] — 2026-05-21
|
|
92
|
+
|
|
93
|
+
Forgetting + scheduled background work.
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
- `MemoryRecord.expires_at` — UTC datetime, default `None`. Round-trips through every backend.
|
|
97
|
+
- `MnemeBackend.touch(record_ids, *, now)` — persists access count + last-accessed timestamp.
|
|
98
|
+
- `retrieve()` calls `backend.touch()` on the returned top-k and defensively filters records with `expires_at <= now`.
|
|
99
|
+
- `MemoryManager.forget(*, now, ttl_only, access_floor, cold_age_days, max_per_tier)` — two-phase: TTL eviction, then access-frequency decay. Walks `(EPISODIC, SEMANTIC)`; working memory untouched.
|
|
100
|
+
- `MemoryManager.start_scheduler()` / `stop_scheduler()` — APScheduler `BackgroundScheduler` integration. `max_instances=1, coalesce=True` on both jobs.
|
|
101
|
+
- New `[scheduler]` extra: `apscheduler>=3.10`.
|
|
102
|
+
|
|
103
|
+
## [0.2.0] — 2026-05-21
|
|
104
|
+
|
|
105
|
+
Real consolidation. Episodic → semantic pipeline driven by an LLM judge.
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
- `mneme.LLMJudge` Protocol + `OpenAILLMJudge` (uses `response_format={"type":"json_schema","strict":true}`) + `MockLLMJudge` (handler callable + call log for tests).
|
|
109
|
+
- `MemoryManager.consolidate(*, since, batch_size, dedup_threshold, max_episodes)` — reads recent episodes, batches them, asks the judge to extract durable facts, dedups against existing semantic facts via cosine similarity (newer-wins-with-provenance merge).
|
|
110
|
+
- `MnemeBackend.list_recent(*, agent_id, tier, since, limit)` added to the protocol.
|
|
111
|
+
|
|
112
|
+
## [0.1.0] — 2026-05-21
|
|
113
|
+
|
|
114
|
+
Walking skeleton. The library does something end-to-end.
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
- `mneme.MemoryTier` enum + `MemoryRecord` base + `WorkingMemory` / `EpisodicMemory` / `SemanticFact` subclasses.
|
|
118
|
+
- `mneme.MnemeBackend` Protocol + reference `InMemoryBackend`.
|
|
119
|
+
- `mneme.SQLiteBackend` using sqlite-vec via a `vec0` virtual table joined by rowid.
|
|
120
|
+
- `mneme.EmbeddingProvider` Protocol + `OpenAIEmbeddings` (defaults to `text-embedding-3-small`) + `HashEmbedder` (deterministic, no-key test double).
|
|
121
|
+
- Three tier classes (`WorkingMemoryTier`, `EpisodicMemoryTier`, `SemanticMemoryTier`).
|
|
122
|
+
- `MemoryManager` facade exposing `.working`, `.episodic`, `.semantic` as attributes and a single `retrieve(query, *, k, tiers, authority_weights, half_life_days, use_confidence, now)` with multiplicative score fusion (similarity × authority × recency × confidence).
|
|
123
|
+
|
|
124
|
+
[unreleased]: https://github.com/ashwinugale/ashwinugale-mneme/compare/v1.0.0...HEAD
|
|
125
|
+
[1.0.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v1.0.0
|
|
126
|
+
[0.8.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.8.0
|
|
127
|
+
[0.7.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.7.0
|
|
128
|
+
[0.6.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.6.0
|
|
129
|
+
[0.5.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.5.0
|
|
130
|
+
[0.4.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.4.0
|
|
131
|
+
[0.3.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.3.0
|
|
132
|
+
[0.2.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.2.0
|
|
133
|
+
[0.1.0]: https://github.com/ashwinugale/ashwinugale-mneme/releases/tag/v0.1.0
|
smolamem-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ashwin Ugale
|
|
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.
|
smolamem-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smolAmem
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Multi-tier long-term memory for LLM agents. Install: pip install smolAmem. Import: import mneme.
|
|
5
|
+
Project-URL: Homepage, https://ashwinugale.github.io/ashwinugale-mneme/
|
|
6
|
+
Project-URL: Documentation, https://ashwinugale.github.io/ashwinugale-mneme/
|
|
7
|
+
Project-URL: Repository, https://github.com/ashwinugale/ashwinugale-mneme
|
|
8
|
+
Project-URL: Issues, https://github.com/ashwinugale/ashwinugale-mneme/issues
|
|
9
|
+
Author-email: Ashwin Ugale <ugaleashwin@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,llm,memory,rag,vector-search
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Provides-Extra: docs
|
|
22
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
23
|
+
Requires-Dist: mkdocs>=1.6; extra == 'docs'
|
|
24
|
+
Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
|
|
25
|
+
Requires-Dist: pymdown-extensions>=10.7; extra == 'docs'
|
|
26
|
+
Provides-Extra: langchain
|
|
27
|
+
Requires-Dist: langchain-core>=0.3; extra == 'langchain'
|
|
28
|
+
Provides-Extra: llamaindex
|
|
29
|
+
Requires-Dist: llama-index-core>=0.11; extra == 'llamaindex'
|
|
30
|
+
Provides-Extra: openai
|
|
31
|
+
Requires-Dist: openai>=1.40; extra == 'openai'
|
|
32
|
+
Provides-Extra: pgvector
|
|
33
|
+
Requires-Dist: pgvector>=0.3; extra == 'pgvector'
|
|
34
|
+
Requires-Dist: psycopg[binary]>=3.2; extra == 'pgvector'
|
|
35
|
+
Provides-Extra: qdrant
|
|
36
|
+
Requires-Dist: qdrant-client>=1.12; extra == 'qdrant'
|
|
37
|
+
Provides-Extra: scheduler
|
|
38
|
+
Requires-Dist: apscheduler>=3.10; extra == 'scheduler'
|
|
39
|
+
Provides-Extra: sqlite
|
|
40
|
+
Requires-Dist: sqlite-vec>=0.1.6; extra == 'sqlite'
|
|
41
|
+
Provides-Extra: tokens
|
|
42
|
+
Requires-Dist: tiktoken>=0.7; extra == 'tokens'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# Mneme
|
|
46
|
+
|
|
47
|
+
> Multi-tier long-term memory for LLM agents. Working / episodic / semantic tiers, pluggable storage backends, framework-agnostic adapters, and explicit forgetting + consolidation.
|
|
48
|
+
|
|
49
|
+
[](https://ashwinugale.github.io/ashwinugale-mneme/)
|
|
50
|
+
[](https://www.python.org/)
|
|
51
|
+
[](./LICENSE)
|
|
52
|
+
|
|
53
|
+
**Status:** 1.0 released. APIs are stable; semver from here. Bug reports welcome.
|
|
54
|
+
|
|
55
|
+
## Why
|
|
56
|
+
|
|
57
|
+
Every agent framework ships with toy memory: last-N messages, or an LLM-summarised buffer. Real agents need to remember user preferences across weeks, recall specific past interactions, and store semantic facts extracted from many interactions — and they need to forget things that stop being relevant.
|
|
58
|
+
|
|
59
|
+
Mneme is the library you wish existed.
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install smolAmem # core only
|
|
65
|
+
pip install "smolAmem[sqlite,openai]" # SQLite + OpenAI embeddings
|
|
66
|
+
pip install "smolAmem[qdrant,openai]" # production vector backend
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> **Heads-up on naming:** PyPI install name is `smolAmem` (the `mneme` slot was already taken). Python import name stays `mneme` — same dual-name pattern as `Pillow` / `PIL` or `pyyaml` / `yaml`. So you `pip install smolAmem` but write `import mneme` everywhere in your code.
|
|
70
|
+
|
|
71
|
+
## Quickstart
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from mneme import MemoryManager, SQLiteBackend, OpenAIEmbeddings
|
|
75
|
+
|
|
76
|
+
m = MemoryManager(
|
|
77
|
+
agent_id="alice",
|
|
78
|
+
backend=SQLiteBackend(path="mneme.db", dimensions=1536),
|
|
79
|
+
embedder=OpenAIEmbeddings(),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
m.episodic.add("user mostly works in TypeScript", metadata={"role": "user"})
|
|
83
|
+
|
|
84
|
+
for r in m.retrieve("what language does the user prefer?", k=3):
|
|
85
|
+
print(r.score, r.record.content)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Full walkthrough: [docs/quickstart](https://ashwinugale.github.io/ashwinugale-mneme/quickstart/).
|
|
89
|
+
|
|
90
|
+
## Documentation
|
|
91
|
+
|
|
92
|
+
The full site lives at **[ashwinugale.github.io/ashwinugale-mneme](https://ashwinugale.github.io/ashwinugale-mneme/)**.
|
|
93
|
+
|
|
94
|
+
- [Concepts](https://ashwinugale.github.io/ashwinugale-mneme/concepts/) — the three-tier model, retrieval, consolidation, forgetting.
|
|
95
|
+
- [Backends](https://ashwinugale.github.io/ashwinugale-mneme/backends/) — InMemory, SQLite, Qdrant, pgvector.
|
|
96
|
+
- [Adapters](https://ashwinugale.github.io/ashwinugale-mneme/adapters/) — LangChain, LlamaIndex, raw OpenAI.
|
|
97
|
+
- [Eval harness](https://ashwinugale.github.io/ashwinugale-mneme/eval/) — reproducible recall@k + token-cost benchmark.
|
|
98
|
+
- [API reference](https://ashwinugale.github.io/ashwinugale-mneme/api/) — autogen from docstrings.
|
|
99
|
+
|
|
100
|
+
## Benchmark snapshot
|
|
101
|
+
|
|
102
|
+
From the v0.6 [eval harness](https://ashwinugale.github.io/ashwinugale-mneme/eval/) on the 5-conversation starter corpus, k=5:
|
|
103
|
+
|
|
104
|
+
| Strategy | recall@5 | tokens / test point |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `no_memory` | 0.000 | 0.0 |
|
|
107
|
+
| `mneme` (hash, deterministic) | 0.833 | 68.0 |
|
|
108
|
+
| **`mneme` (OpenAI embeddings)** | **1.000** | **67.7** |
|
|
109
|
+
| `full_history` | 1.000 | 141.0 |
|
|
110
|
+
| `summary_buffer` | 1.000 | 165.3 |
|
|
111
|
+
|
|
112
|
+
Mneme matches the full-history oracle for accuracy at **less than half** the token cost. The HashEmbedder row is the cheap deterministic baseline that runs without an API key — useful in CI and for reviewers reproducing numbers.
|
|
113
|
+
|
|
114
|
+
Reproduce:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv run python -m evals --runner mneme --embedder openai --output out/mneme.json
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT. See [LICENSE](./LICENSE).
|