seren-memory 1.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. seren_memory-1.9.1/PKG-INFO +21 -0
  2. seren_memory-1.9.1/SerenMemory.pyproj +86 -0
  3. seren_memory-1.9.1/pyproject.toml +71 -0
  4. seren_memory-1.9.1/seren-memory.service.sample +37 -0
  5. seren_memory-1.9.1/seren-memory.yaml.sample +89 -0
  6. seren_memory-1.9.1/seren_memory/__init__.py +25 -0
  7. seren_memory-1.9.1/seren_memory/__main__.py +42 -0
  8. seren_memory-1.9.1/seren_memory/_version.py +24 -0
  9. seren_memory-1.9.1/seren_memory/app.py +548 -0
  10. seren_memory-1.9.1/seren_memory/collections.py +739 -0
  11. seren_memory-1.9.1/seren_memory/config.py +161 -0
  12. seren_memory-1.9.1/seren_memory/consolidator/__init__.py +2 -0
  13. seren_memory-1.9.1/seren_memory/consolidator/service.py +791 -0
  14. seren_memory-1.9.1/seren_memory/mcp/__init__.py +13 -0
  15. seren_memory-1.9.1/seren_memory/mcp/server.py +190 -0
  16. seren_memory-1.9.1/seren_memory/mcp/tools.py +548 -0
  17. seren_memory-1.9.1/seren_memory/models/__init__.py +12 -0
  18. seren_memory-1.9.1/seren_memory/models/schemas.py +354 -0
  19. seren_memory-1.9.1/seren_memory/routes/__init__.py +1 -0
  20. seren_memory-1.9.1/seren_memory/routes/long.py +64 -0
  21. seren_memory-1.9.1/seren_memory/routes/near.py +56 -0
  22. seren_memory-1.9.1/seren_memory/routes/search.py +104 -0
  23. seren_memory-1.9.1/seren_memory/routes/short.py +36 -0
  24. seren_memory-1.9.1/seren_memory/viewer/halls.html +715 -0
  25. seren_memory-1.9.1/seren_memory.egg-info/PKG-INFO +21 -0
  26. seren_memory-1.9.1/seren_memory.egg-info/SOURCES.txt +42 -0
  27. seren_memory-1.9.1/seren_memory.egg-info/dependency_links.txt +1 -0
  28. seren_memory-1.9.1/seren_memory.egg-info/entry_points.txt +2 -0
  29. seren_memory-1.9.1/seren_memory.egg-info/requires.txt +14 -0
  30. seren_memory-1.9.1/seren_memory.egg-info/top_level.txt +1 -0
  31. seren_memory-1.9.1/setup.cfg +4 -0
  32. seren_memory-1.9.1/tests/__init__.py +0 -0
  33. seren_memory-1.9.1/tests/conftest.py +154 -0
  34. seren_memory-1.9.1/tests/test_auth.py +67 -0
  35. seren_memory-1.9.1/tests/test_brief.py +108 -0
  36. seren_memory-1.9.1/tests/test_drafts.py +446 -0
  37. seren_memory-1.9.1/tests/test_mcp_endpoint.py +110 -0
  38. seren_memory-1.9.1/tests/test_mcp_fallback.py +134 -0
  39. seren_memory-1.9.1/tests/test_mcp_mount.py +104 -0
  40. seren_memory-1.9.1/tests/test_mcp_tools.py +487 -0
  41. seren_memory-1.9.1/tests/test_near_listing.py +89 -0
  42. seren_memory-1.9.1/tests/test_search.py +126 -0
  43. seren_memory-1.9.1/tests/test_smoke.py +177 -0
  44. seren_memory-1.9.1/tests/test_validation.py +102 -0
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: seren-memory
3
+ Version: 1.9.1
4
+ Summary: Three-tier LLM memory with consolidation. The Halls of Memory.
5
+ Author: Chad Roesler
6
+ License: GPL-3.0-or-later
7
+ Keywords: llm,memory,vector-db,chromadb,consolidation,seren
8
+ Requires-Python: <3.13,>=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: fastapi>=0.115.0
11
+ Requires-Dist: uvicorn[standard]>=0.32.0
12
+ Requires-Dist: pydantic>=2.9.0
13
+ Requires-Dist: pyyaml>=6.0.2
14
+ Requires-Dist: httpx>=0.27.0
15
+ Requires-Dist: chromadb>=0.5.0
16
+ Provides-Extra: mcp
17
+ Requires-Dist: mcp>=1.0; extra == "mcp"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=8.0; extra == "dev"
20
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
21
+ Requires-Dist: httpx2>=2.3; extra == "dev"
@@ -0,0 +1,86 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
3
+ <PropertyGroup>
4
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5
+ <SchemaVersion>2.0</SchemaVersion>
6
+ <ProjectGuid>b1e7c0de-5e12-4a7b-9f3a-5e7e7a5e7e7a</ProjectGuid>
7
+ <ProjectHome>.</ProjectHome>
8
+ <!-- Run python -m seren_memory when you hit F5 -->
9
+ <StartupFile>seren_memory\__main__.py</StartupFile>
10
+ <SearchPath>.</SearchPath>
11
+ <WorkingDirectory>.</WorkingDirectory>
12
+ <OutputPath>.</OutputPath>
13
+ <Name>SerenMemory</Name>
14
+ <RootNamespace>seren_memory</RootNamespace>
15
+ <InterpreterId>Global|PythonCore|3.11</InterpreterId>
16
+ <IsWindowsApplication>False</IsWindowsApplication>
17
+ <Description>Three-tier LLM memory with consolidation. The Halls of Memory.</Description>
18
+ <TestFramework>Pytest</TestFramework>
19
+ </PropertyGroup>
20
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
21
+ <DebugSymbols>true</DebugSymbols>
22
+ <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
23
+ </PropertyGroup>
24
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
25
+ <DebugSymbols>true</DebugSymbols>
26
+ <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
27
+ </PropertyGroup>
28
+ <ItemGroup>
29
+ <Compile Include="seren_memory\mcp\server.py" />
30
+ <Compile Include="seren_memory\mcp\tools.py" />
31
+ <Compile Include="seren_memory\mcp\__init__.py" />
32
+ <Compile Include="seren_memory\__init__.py" />
33
+ <Compile Include="seren_memory\__main__.py" />
34
+ <Compile Include="seren_memory\app.py" />
35
+ <Compile Include="seren_memory\config.py" />
36
+ <Compile Include="seren_memory\collections.py" />
37
+ <Compile Include="seren_memory\models\__init__.py" />
38
+ <Compile Include="seren_memory\models\schemas.py" />
39
+ <Compile Include="seren_memory\routes\__init__.py" />
40
+ <Compile Include="seren_memory\routes\short.py" />
41
+ <Compile Include="seren_memory\routes\near.py" />
42
+ <Compile Include="seren_memory\routes\long.py" />
43
+ <Compile Include="seren_memory\routes\search.py" />
44
+ <Compile Include="seren_memory\consolidator\__init__.py" />
45
+ <Compile Include="seren_memory\consolidator\service.py" />
46
+ <Compile Include="tests\conftest.py" />
47
+ <Compile Include="tests\test_auth.py" />
48
+ <Compile Include="tests\test_brief.py" />
49
+ <Compile Include="tests\test_drafts.py" />
50
+ <Compile Include="tests\test_mcp_endpoint.py" />
51
+ <Compile Include="tests\test_mcp_fallback.py" />
52
+ <Compile Include="tests\test_mcp_mount.py" />
53
+ <Compile Include="tests\test_mcp_tools.py" />
54
+ <Compile Include="tests\test_near_listing.py" />
55
+ <Compile Include="tests\test_search.py" />
56
+ <Compile Include="tests\test_smoke.py" />
57
+ <Compile Include="tests\test_validation.py" />
58
+ <Compile Include="tests\__init__.py" />
59
+ </ItemGroup>
60
+ <ItemGroup>
61
+ <Folder Include="seren_memory\" />
62
+ <Folder Include="seren_memory\mcp\" />
63
+ <Folder Include="seren_memory\mcp\__pycache__\" />
64
+ <Folder Include="seren_memory\models\" />
65
+ <Folder Include="seren_memory\routes\" />
66
+ <Folder Include="seren_memory\consolidator\" />
67
+ <Folder Include="seren_memory\viewer\" />
68
+ <Folder Include="tests\" />
69
+ <Folder Include="tests\__pycache__\" />
70
+ </ItemGroup>
71
+ <ItemGroup>
72
+ <Content Include="pyproject.toml" />
73
+ <Content Include="seren-memory.yaml.sample" />
74
+ <Content Include="seren-memory.service.sample" />
75
+ <Content Include="seren_memory\mcp\__pycache__\server.cpython-311.pyc" />
76
+ <Content Include="seren_memory\mcp\__pycache__\tools.cpython-311.pyc" />
77
+ <Content Include="seren_memory\mcp\__pycache__\__init__.cpython-311.pyc" />
78
+ <Content Include="seren_memory\viewer\halls.html" />
79
+ <Content Include="tests\__pycache__\test_smoke.cpython-311-pytest-9.0.3.pyc" />
80
+ <Content Include="tests\__pycache__\__init__.cpython-311.pyc" />
81
+ </ItemGroup>
82
+ <ItemGroup>
83
+ <InterpreterReference Include="Global|PythonCore|3.11" />
84
+ </ItemGroup>
85
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
86
+ </Project>
@@ -0,0 +1,71 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "setuptools-scm>=8", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "seren-memory"
7
+ dynamic = ["version"]
8
+ description = "Three-tier LLM memory with consolidation. The Halls of Memory."
9
+ readme = "README.md"
10
+ # Upper bound is the chromadb wall, not ours: SerenMemory itself is fine on
11
+ # 3.13+, but chromadb's transitive deps don't build there yet (an lz4 wheel
12
+ # build failure). Capping here turns a cryptic transitive build explosion into
13
+ # a clean "needs a different Python" from pip. LIFT THE <3.13 the day chromadb
14
+ # ships 3.13 support (bump the CI matrix to match in the same commit).
15
+ requires-python = ">=3.10,<3.13"
16
+ license = { text = "GPL-3.0-or-later" }
17
+ authors = [{ name = "Chad Roesler" }]
18
+ keywords = ["llm", "memory", "vector-db", "chromadb", "consolidation", "seren"]
19
+
20
+ dependencies = [
21
+ "fastapi>=0.115.0",
22
+ "uvicorn[standard]>=0.32.0",
23
+ "pydantic>=2.9.0",
24
+ "pyyaml>=6.0.2",
25
+ "httpx>=0.27.0",
26
+ "chromadb>=0.5.0",
27
+ ]
28
+
29
+ [project.optional-dependencies]
30
+ mcp = [
31
+ # The Python MCP SDK from Anthropic. FastMCP (the high-level server
32
+ # API) was merged into the main SDK; this single dep gives us both.
33
+ # Pin loosely - the SDK is moving fast; our server.py is written to
34
+ # tolerate the streamable_http→sse transport-name drift across recent
35
+ # versions.
36
+ "mcp>=1.0",
37
+ ]
38
+ dev = [
39
+ "pytest>=8.0",
40
+ "pytest-asyncio>=0.23",
41
+ # starlette >= 0.43 emits StarletteDeprecationWarning when fastapi.testclient
42
+ # falls back to httpx; installing httpx2 silences it and future-proofs the
43
+ # test stack against the upcoming hard cutover.
44
+ "httpx2>=2.3",
45
+ ]
46
+
47
+ [project.scripts]
48
+ seren-memory = "seren_memory.__main__:main"
49
+
50
+ [tool.setuptools.packages.find]
51
+ where = ["."]
52
+ include = ["seren_memory*"]
53
+
54
+ # Ship the viewer HTML inside the wheel. Without this, pip strips non-.py
55
+ # files and GET /viewer 404s on an installed copy (works in dev checkout,
56
+ # silently breaks once installed - the classic package-data gotcha).
57
+ [tool.setuptools.package-data]
58
+ seren_memory = ["viewer/*.html"]
59
+
60
+ [tool.setuptools_scm]
61
+ # .git is one level above pyproject.toml (repo root, not the SerenMemory subdir)
62
+ root = ".."
63
+ version_scheme = "release-branch-semver"
64
+ # Write the tag-derived version into the package (shipped in the wheel, read
65
+ # by __init__ / importlib.metadata). Replaces the old sed-on-__init__ hack -
66
+ # version now flows from the git tag with no manual step. _version.py is
67
+ # gitignored; it's a build artifact, never committed.
68
+ version_file = "seren_memory/_version.py"
69
+
70
+ [tool.pytest.ini_options]
71
+ asyncio_mode = "auto"
@@ -0,0 +1,37 @@
1
+ # SerenMemory systemd unit (sample)
2
+ #
3
+ # Install:
4
+ # 1. Edit __TARGET_USER__ and paths below
5
+ # 2. sudo cp seren-memory.service /etc/systemd/system/
6
+ # 3. sudo systemctl daemon-reload
7
+ # 4. sudo systemctl enable --now seren-memory
8
+ #
9
+ # Logs: journalctl -u seren-memory -f
10
+
11
+ [Unit]
12
+ Description=SerenMemory - three-tier LLM memory with consolidation
13
+ After=network-online.target
14
+ Wants=network-online.target
15
+
16
+ [Service]
17
+ Type=simple
18
+ User=__TARGET_USER__
19
+ WorkingDirectory=/home/__TARGET_USER__/seren-memory
20
+
21
+ # Use the venv's python. Adjust the venv path to wherever you installed.
22
+ ExecStart=/home/__TARGET_USER__/seren-venvs/memory/bin/python -m seren_memory --config /home/__TARGET_USER__/seren-memory/seren-memory.yaml
23
+
24
+ # Config can also come from env - uncomment + set as needed:
25
+ # Environment=SEREN_MEMORY_PORT=7420
26
+ # Environment=SEREN_MEMORY_MODEL_URL=http://localhost:8090/v1
27
+ # Environment=SEREN_MEMORY_BEARER_TOKEN=
28
+
29
+ Restart=on-failure
30
+ RestartSec=5
31
+
32
+ # The consolidation loop and chroma keep some state in memory; give it
33
+ # room but cap it so a runaway can't OOM the box.
34
+ MemoryMax=2G
35
+
36
+ [Install]
37
+ WantedBy=multi-user.target
@@ -0,0 +1,89 @@
1
+ # ═══════════════════════════════════════════════════════════════════════
2
+ # seren-memory.yaml - config for SerenMemory
3
+ # ═══════════════════════════════════════════════════════════════════════
4
+ #
5
+ # Copy to seren-memory.yaml, edit, and run:
6
+ # python -m seren_memory --config seren-memory.yaml
7
+ #
8
+ # Or just run with no config at all - the defaults below are baked in, so
9
+ # `python -m seren_memory` works zero-config for a local dev spin.
10
+ #
11
+ # Env vars override file values (handy for Docker/systemd):
12
+ # SEREN_MEMORY_PORT, SEREN_MEMORY_HOST, SEREN_MEMORY_BEARER_TOKEN,
13
+ # SEREN_MEMORY_PERSIST_DIR, SEREN_MEMORY_MODEL_URL,
14
+ # SEREN_MEMORY_MODEL_NAME, SEREN_MEMORY_CONSOLIDATOR_ENABLED
15
+ #
16
+ # ═══════════════════════════════════════════════════════════════════════
17
+
18
+ server:
19
+ host: 0.0.0.0
20
+ port: 7420
21
+ # Leave empty for no auth (trusted LAN / dev). Set a token to require
22
+ # Authorization: Bearer <token>
23
+ # on every endpoint except / and /health.
24
+ bearer_token: ""
25
+
26
+ storage:
27
+ # Where chroma persists. ~ is expanded. Created if missing.
28
+ persist_dir: ~/.seren-memory/chroma
29
+ # Collection names - rarely need changing.
30
+ short_collection: seren_short
31
+ near_collection: seren_near
32
+ long_collection: seren_long
33
+ brief_collection: seren_briefs
34
+ # Embedding model. Leave null for chroma's default (all-MiniLM-L6-v2,
35
+ # ~80MB, CPU-friendly, downloaded on first use).
36
+ embedding_model: null
37
+
38
+ lifetimes:
39
+ # ShortTerm entries older than this become eligible to age out during
40
+ # consolidation (unless promoted or pinned). 8 days = a week plus drift.
41
+ # 691200 = 8 * 24 * 3600.
42
+ short_term_seconds: 691200
43
+ # NearTerm intents with no explicit expiry get REVIEWED (not auto-deleted)
44
+ # after this long unfulfilled. 30 days.
45
+ near_term_review_seconds: 2592000
46
+
47
+ consolidator:
48
+ enabled: true
49
+
50
+ # "thread" = runs as a background loop inside this process (simple).
51
+ # "external" = you drive it by POSTing /consolidate/run on a schedule
52
+ # (e.g. a separate systemd timer or Seren's scheduler).
53
+ mode: thread
54
+
55
+ # How often the consolidation window opens. ~20 hours, NOT 24, on
56
+ # purpose: the window drifts through the day over a week so it never
57
+ # aligns to a 'day boundary' that doesn't exist for the system. This
58
+ # number is load-bearing - resist the urge to 'fix' it to 24h.
59
+ # 72000 = 20 * 3600.
60
+ interval_seconds: 72000
61
+
62
+ # OpenAI-compatible inference endpoint for the consolidation model.
63
+ # The consolidator does classification + light summarization, so a
64
+ # 2B-4B model is plenty. Point this at Seren's llama-server, ollama,
65
+ # a remote API - anything that speaks /v1/chat/completions.
66
+ model_url: http://localhost:8090/v1
67
+ model_name: default
68
+ model_timeout_seconds: 120
69
+
70
+ # Never process more than this many short-term entries per window
71
+ # (prevents a runaway run hammering the model). Rest wait for next window.
72
+ max_entries_per_run: 500
73
+
74
+ # A topic cluster needs at least this many short-term entries to be
75
+ # promoted to long-term - unless a brief promote-hint or a pin overrides.
76
+ # This is the main "how eager is consolidation" knob.
77
+ promote_min_evidence: 3
78
+
79
+ # Before deleting aged-out short-term entries, copy them to a 'pruned'
80
+ # collection for this many days as insurance. 0 = no safety net.
81
+ # Recommended > 0 until you trust the heuristic.
82
+ pruned_safety_days: 14
83
+
84
+ # How many times the consolidator will re-synthesize a cluster after the
85
+ # main model rejects a draft with a critique. On the Nth rejection the
86
+ # chain flips to 'requires_selection' - the model must then compare all
87
+ # attempts via GET /drafts/{id}/chain and commit the best via POST
88
+ # /drafts/{id}/select.
89
+ max_redraft_attempts: 3
@@ -0,0 +1,25 @@
1
+ """
2
+ SerenMemory - three-tier LLM memory with consolidation.
3
+
4
+ The Halls of Memory: ShortTerm (working), NearTerm (open loops), LongTerm
5
+ (consolidated). A small "consolidator" model does the dream-work of
6
+ promoting what matters and letting the rest go.
7
+
8
+ Standalone. Bring your own LLM (any OpenAI-compatible endpoint). Configure
9
+ a couple of values and you've got a memory system that matters.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from importlib.metadata import PackageNotFoundError, version as _pkg_version
14
+
15
+ # Version flows from the git tag via setuptools-scm (written to _version.py
16
+ # at build time and recorded in the installed package's metadata). No manual
17
+ # bump, no sed step. The fallback only fires in a bare source checkout that
18
+ # was never installed; do `pip install -e .` to resolve it.
19
+ try:
20
+ __version__: str = _pkg_version("seren-memory")
21
+ except PackageNotFoundError:
22
+ __version__ = "0.0.0.dev"
23
+
24
+ from .app import create_app # noqa: F401,E402
25
+ from .config import load_config, MemoryConfig # noqa: F401,E402
@@ -0,0 +1,42 @@
1
+ """
2
+ Entry point: python -m seren_memory [--config path]
3
+
4
+ Boots the FastAPI app with uvicorn using the resolved config.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+
10
+ import uvicorn
11
+
12
+ from .app import create_app
13
+ from .config import load_config
14
+
15
+
16
+ def main() -> None:
17
+ parser = argparse.ArgumentParser(
18
+ prog="seren_memory",
19
+ description="SerenMemory - three-tier LLM memory with consolidation.")
20
+ parser.add_argument(
21
+ "--config", "-c", default=None,
22
+ help="Path to seren-memory.yaml (default: ./seren-memory.yaml or "
23
+ "$SEREN_MEMORY_CONFIG, falling back to built-in defaults).")
24
+ args = parser.parse_args()
25
+
26
+ cfg = load_config(args.config)
27
+ app = create_app(cfg)
28
+
29
+ print(f"[seren-memory] listening on {cfg.server.host}:{cfg.server.port}")
30
+ print(f"[seren-memory] auth: "
31
+ f"{'enabled' if cfg.server.bearer_token else 'DISABLED (no token)'}")
32
+
33
+ uvicorn.run(
34
+ app,
35
+ host=cfg.server.host,
36
+ port=cfg.server.port,
37
+ log_level="info",
38
+ )
39
+
40
+
41
+ if __name__ == "__main__":
42
+ main()
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '1.9.1'
22
+ __version_tuple__ = version_tuple = (1, 9, 1)
23
+
24
+ __commit_id__ = commit_id = 'g3e9f2859e'