local-persona-memory 0.2.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.
- local_persona_memory-0.2.0/.github/workflows/ci.yml +58 -0
- local_persona_memory-0.2.0/.gitignore +34 -0
- local_persona_memory-0.2.0/CHANGELOG.md +37 -0
- local_persona_memory-0.2.0/CONTRIBUTING.md +51 -0
- local_persona_memory-0.2.0/LICENSE +21 -0
- local_persona_memory-0.2.0/PKG-INFO +174 -0
- local_persona_memory-0.2.0/PUBLISH_GUIDE.md +72 -0
- local_persona_memory-0.2.0/README.md +118 -0
- local_persona_memory-0.2.0/examples/basic_usage.py +39 -0
- local_persona_memory-0.2.0/examples/multi_model_demo.py +38 -0
- local_persona_memory-0.2.0/examples/pre_publish_real_test.py +216 -0
- local_persona_memory-0.2.0/pyproject.toml +65 -0
- local_persona_memory-0.2.0/src/local_persona_memory/__init__.py +51 -0
- local_persona_memory-0.2.0/src/local_persona_memory/config.py +84 -0
- local_persona_memory-0.2.0/src/local_persona_memory/exceptions.py +84 -0
- local_persona_memory-0.2.0/src/local_persona_memory/ingestion.py +207 -0
- local_persona_memory-0.2.0/src/local_persona_memory/llm.py +193 -0
- local_persona_memory-0.2.0/src/local_persona_memory/manager.py +420 -0
- local_persona_memory-0.2.0/src/local_persona_memory/models.py +104 -0
- local_persona_memory-0.2.0/src/local_persona_memory/retrieval.py +122 -0
- local_persona_memory-0.2.0/src/local_persona_memory/store.py +190 -0
- local_persona_memory-0.2.0/src/local_persona_memory/utils.py +127 -0
- local_persona_memory-0.2.0/tests/__init__.py +0 -0
- local_persona_memory-0.2.0/tests/conftest.py +104 -0
- local_persona_memory-0.2.0/tests/test_config.py +50 -0
- local_persona_memory-0.2.0/tests/test_exceptions.py +86 -0
- local_persona_memory-0.2.0/tests/test_ingestion.py +111 -0
- local_persona_memory-0.2.0/tests/test_ingestion_v2.py +157 -0
- local_persona_memory-0.2.0/tests/test_manager.py +151 -0
- local_persona_memory-0.2.0/tests/test_manager_v2.py +332 -0
- local_persona_memory-0.2.0/tests/test_models.py +100 -0
- local_persona_memory-0.2.0/tests/test_real_ollama.py +395 -0
- local_persona_memory-0.2.0/tests/test_retrieval.py +96 -0
- local_persona_memory-0.2.0/tests/test_store.py +97 -0
- local_persona_memory-0.2.0/tests/test_utils.py +156 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, dev]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -e ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: ruff check src/
|
|
31
|
+
|
|
32
|
+
- name: Type check with mypy
|
|
33
|
+
run: mypy src/ --ignore-missing-imports
|
|
34
|
+
continue-on-error: true # Don't fail CI on mypy warnings during early dev
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: pytest tests/ -v --cov=src/local_persona_memory --cov-report=term-missing
|
|
38
|
+
|
|
39
|
+
- name: Build package (verify it builds cleanly)
|
|
40
|
+
run: python -m build
|
|
41
|
+
|
|
42
|
+
publish:
|
|
43
|
+
# Only publish to PyPI when you push a version tag like v0.1.0
|
|
44
|
+
needs: test
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
- uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.11"
|
|
52
|
+
- run: pip install build twine
|
|
53
|
+
- run: python -m build
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
env:
|
|
56
|
+
TWINE_USERNAME: __token__
|
|
57
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
58
|
+
run: twine upload dist/*
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
*.egg
|
|
10
|
+
|
|
11
|
+
# Testing
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
|
|
16
|
+
# Type checking
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
|
|
19
|
+
# IDE
|
|
20
|
+
.vscode/
|
|
21
|
+
.idea/
|
|
22
|
+
*.swp
|
|
23
|
+
|
|
24
|
+
# Local memory data — never commit user memories!
|
|
25
|
+
~/.local_persona_memory/
|
|
26
|
+
*.chroma/
|
|
27
|
+
|
|
28
|
+
# Environment
|
|
29
|
+
.env
|
|
30
|
+
.env.local
|
|
31
|
+
|
|
32
|
+
# OS
|
|
33
|
+
.DS_Store
|
|
34
|
+
Thumbs.db
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.0] - 2024-XX-XX
|
|
4
|
+
|
|
5
|
+
### Fixed (Breaking warnings resolved)
|
|
6
|
+
- Removed `langchain-community` dependency (was being sunset)
|
|
7
|
+
- PDF loading now uses `pypdf` directly — no deprecated wrapper
|
|
8
|
+
- `Document` type now from `langchain-core` (stable)
|
|
9
|
+
- `RecursiveCharacterTextSplitter` now from `langchain-text-splitters` (standalone)
|
|
10
|
+
- No more deprecation warnings on install or import
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `add_conversation()` — manually add conversation turns to history
|
|
14
|
+
- `get_conversation_history(last_n)` — retrieve recent chat turns
|
|
15
|
+
- `clear_conversation_history()` — reset in-session history
|
|
16
|
+
- `export_memories(path)` — back up all memories to JSON
|
|
17
|
+
- `import_memories(path, merge)` — restore from JSON backup
|
|
18
|
+
- `memory_summary()` — LLM-generated natural language profile
|
|
19
|
+
- `on_remember` callback hook — fires after every `remember()` call
|
|
20
|
+
- `on_recall` callback hook — fires after every `recall()` call
|
|
21
|
+
- `on_ingest_complete` callback on `DocumentIngester`
|
|
22
|
+
- `remember_conversation` flag on `chat()` to opt out of history tracking
|
|
23
|
+
- Real Ollama integration test suite (`test_real_ollama.py`)
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- `pyproject.toml` dependencies updated — `langchain-community` removed
|
|
27
|
+
- Version bumped to `0.2.0`
|
|
28
|
+
|
|
29
|
+
## [0.1.0] - 2024-XX-XX
|
|
30
|
+
### Added
|
|
31
|
+
- Initial release
|
|
32
|
+
- `PersonaMemoryManager` with `remember()`, `recall()`, `chat()`, `ingest_pdf()`, `forget()`
|
|
33
|
+
- ChromaDB persistent vector storage
|
|
34
|
+
- Any Ollama model support
|
|
35
|
+
- PDF, TXT, MD ingestion
|
|
36
|
+
- User isolation
|
|
37
|
+
- 97 tests, 83% coverage
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Contributing to local-persona-memory
|
|
2
|
+
|
|
3
|
+
Thank you for your interest! Here's how to get started.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/yourname/local-persona-memory
|
|
9
|
+
cd local-persona-memory
|
|
10
|
+
python -m venv .venv
|
|
11
|
+
source .venv/bin/activate
|
|
12
|
+
pip install -e ".[dev]"
|
|
13
|
+
pre-commit install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Running tests
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pytest tests/ -v --cov=src/local_persona_memory
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Code style
|
|
23
|
+
|
|
24
|
+
We use `ruff` for linting:
|
|
25
|
+
```bash
|
|
26
|
+
ruff check src/
|
|
27
|
+
ruff format src/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Adding a new feature
|
|
31
|
+
|
|
32
|
+
1. Open an issue first to discuss
|
|
33
|
+
2. Create a branch: `git checkout -b feat/your-feature`
|
|
34
|
+
3. Write tests BEFORE writing code (TDD)
|
|
35
|
+
4. Make tests pass
|
|
36
|
+
5. Open a pull request
|
|
37
|
+
|
|
38
|
+
## Adding support for a new document type
|
|
39
|
+
|
|
40
|
+
1. Add the file extension and loader to `SUPPORTED_EXTENSIONS` in `ingestion.py`
|
|
41
|
+
2. Add a test in `tests/test_ingestion.py`
|
|
42
|
+
3. Update the README supported formats table
|
|
43
|
+
|
|
44
|
+
## Reporting bugs
|
|
45
|
+
|
|
46
|
+
Include:
|
|
47
|
+
- Python version (`python --version`)
|
|
48
|
+
- Ollama version (`ollama --version`)
|
|
49
|
+
- Model being used
|
|
50
|
+
- Full traceback
|
|
51
|
+
- Minimal reproducible example
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Your Name
|
|
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,174 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: local-persona-memory
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Long-term personalized memory for ANY local LLM via Ollama + ChromaDB + LangChain
|
|
5
|
+
Project-URL: Homepage, https://github.com/AbhiLohar/local-persona-memory
|
|
6
|
+
Project-URL: Repository, https://github.com/AbhiLohar/local-persona-memory
|
|
7
|
+
Project-URL: Issues, https://github.com/AbhiLohar/local-persona-memory/issues
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2024 Your Name
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: ai,chromadb,langchain,llm,local-ai,memory,ollama,rag
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
37
|
+
Requires-Python: >=3.11
|
|
38
|
+
Requires-Dist: chromadb>=0.5.0
|
|
39
|
+
Requires-Dist: langchain-core>=0.3.0
|
|
40
|
+
Requires-Dist: langchain-ollama>=0.1.0
|
|
41
|
+
Requires-Dist: langchain-text-splitters>=0.3.0
|
|
42
|
+
Requires-Dist: ollama>=0.2.0
|
|
43
|
+
Requires-Dist: pydantic>=2.0.0
|
|
44
|
+
Requires-Dist: pypdf>=4.0.0
|
|
45
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
46
|
+
Requires-Dist: rich>=13.0.0
|
|
47
|
+
Requires-Dist: tenacity>=8.0.0
|
|
48
|
+
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
52
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
53
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
54
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
|
|
57
|
+
# local-persona-memory
|
|
58
|
+
|
|
59
|
+
> Long-term personalized memory for ANY local LLM running via Ollama.
|
|
60
|
+
|
|
61
|
+
Your AI assistant forgets everything when the session ends. This library fixes that — permanently, privately, and with zero cloud dependency.
|
|
62
|
+
|
|
63
|
+
## What it solves
|
|
64
|
+
|
|
65
|
+
Most RAG libraries are built for searching documents. This library is built for **remembering people**. It stores user preferences, facts, skills, and goals, then injects the most relevant memories into every LLM response automatically.
|
|
66
|
+
|
|
67
|
+
## Supported models
|
|
68
|
+
|
|
69
|
+
Works with **any model in the Ollama library**:
|
|
70
|
+
|
|
71
|
+
| Use case | Recommended model |
|
|
72
|
+
|---|---|
|
|
73
|
+
| General assistant | `llama3`, `llama3.1`, `mistral` |
|
|
74
|
+
| Fast / lightweight | `phi3`, `gemma2:2b`, `tinyllama` |
|
|
75
|
+
| Coding assistant | `codellama`, `deepseek-coder` |
|
|
76
|
+
| Multilingual | `qwen2`, `qwen2.5` |
|
|
77
|
+
| Reasoning | `deepseek-r1`, `qwen2.5:14b` |
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install local-persona-memory
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Prerequisites:**
|
|
86
|
+
1. [Install Ollama](https://ollama.com)
|
|
87
|
+
2. Pull a model and the embedding model:
|
|
88
|
+
```bash
|
|
89
|
+
ollama pull llama3
|
|
90
|
+
ollama pull nomic-embed-text
|
|
91
|
+
```
|
|
92
|
+
3. Run `ollama serve` (or open the Ollama app)
|
|
93
|
+
|
|
94
|
+
## Quick start
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from local_persona_memory import PersonaMemoryManager
|
|
98
|
+
|
|
99
|
+
# Works with any Ollama model
|
|
100
|
+
mem = PersonaMemoryManager(user_id="alice", model="llama3")
|
|
101
|
+
|
|
102
|
+
# Store memories
|
|
103
|
+
mem.remember("Alice is a Python developer building AI tools")
|
|
104
|
+
mem.remember("Alice prefers concise answers and code examples")
|
|
105
|
+
|
|
106
|
+
# Learn from documents
|
|
107
|
+
mem.ingest_pdf("notes.pdf")
|
|
108
|
+
|
|
109
|
+
# Chat with automatic memory context
|
|
110
|
+
response = mem.chat("What should I work on today?")
|
|
111
|
+
print(response)
|
|
112
|
+
|
|
113
|
+
# Inspect stored memories
|
|
114
|
+
print(f"Stored {mem.memory_count()} memories")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Advanced usage
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from local_persona_memory import PersonaMemoryManager, LLMConfig, MemoryConfig, MemoryCategory
|
|
121
|
+
|
|
122
|
+
mem = PersonaMemoryManager(
|
|
123
|
+
user_id="bob",
|
|
124
|
+
llm_config=LLMConfig(
|
|
125
|
+
model="mistral", # any Ollama model
|
|
126
|
+
temperature=0.4,
|
|
127
|
+
context_window=8192, # increase for larger models
|
|
128
|
+
),
|
|
129
|
+
memory_config=MemoryConfig(
|
|
130
|
+
top_k_memories=8, # retrieve more memories per query
|
|
131
|
+
chunk_size=512, # PDF chunk size in characters
|
|
132
|
+
similarity_threshold=0.3,
|
|
133
|
+
embedding_model="nomic-embed-text",
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Categorise memories for better filtering
|
|
138
|
+
mem.remember("Bob knows Rust and Go", category=MemoryCategory.SKILL)
|
|
139
|
+
mem.remember("Bob wants to build a CLI tool", category=MemoryCategory.GOAL)
|
|
140
|
+
|
|
141
|
+
# Recall with category filter
|
|
142
|
+
skill_memories = mem.recall("programming languages", category=MemoryCategory.SKILL)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## How it works
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
User message
|
|
149
|
+
│
|
|
150
|
+
▼
|
|
151
|
+
[Embed with nomic-embed-text]
|
|
152
|
+
│
|
|
153
|
+
▼
|
|
154
|
+
[ChromaDB similarity search] ──► Top-K relevant memories
|
|
155
|
+
│
|
|
156
|
+
▼
|
|
157
|
+
[Build prompt: system + memories + message]
|
|
158
|
+
│
|
|
159
|
+
▼
|
|
160
|
+
[Generate with Ollama model]
|
|
161
|
+
│
|
|
162
|
+
▼
|
|
163
|
+
Personalised response
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Memories persist to `~/.local_persona_memory/` between sessions. Each user gets an isolated ChromaDB collection.
|
|
167
|
+
|
|
168
|
+
## Contributing
|
|
169
|
+
|
|
170
|
+
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md). Open an issue first for large changes.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Step-by-Step: Publishing to PyPI
|
|
2
|
+
|
|
3
|
+
## Phase 5 — CI/CD (already set up in .github/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Push to GitHub and CI runs automatically on every push.
|
|
6
|
+
|
|
7
|
+
## Phase 6 — Publishing to PyPI
|
|
8
|
+
|
|
9
|
+
### Step 1: Create accounts
|
|
10
|
+
- PyPI (production): https://pypi.org/account/register/
|
|
11
|
+
- TestPyPI (for testing): https://test.pypi.org/account/register/
|
|
12
|
+
|
|
13
|
+
### Step 2: Create API tokens
|
|
14
|
+
- Go to PyPI → Account Settings → API Tokens
|
|
15
|
+
- Create token scoped to this project
|
|
16
|
+
- Save it somewhere safe — shown only once
|
|
17
|
+
|
|
18
|
+
### Step 3: Install build tools
|
|
19
|
+
```bash
|
|
20
|
+
pip install build twine
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Step 4: Build the package
|
|
24
|
+
```bash
|
|
25
|
+
cd local-persona-memory
|
|
26
|
+
python -m build
|
|
27
|
+
# Creates: dist/local_persona_memory-0.1.0.tar.gz
|
|
28
|
+
# dist/local_persona_memory-0.1.0-py3-none-any.whl
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Step 5: Test on TestPyPI first (always do this)
|
|
32
|
+
```bash
|
|
33
|
+
twine upload --repository testpypi dist/*
|
|
34
|
+
# Enter: __token__
|
|
35
|
+
# Enter: your-testpypi-token
|
|
36
|
+
|
|
37
|
+
# Test install from TestPyPI
|
|
38
|
+
pip install --index-url https://test.pypi.org/simple/ local-persona-memory
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Step 6: Publish to real PyPI
|
|
42
|
+
```bash
|
|
43
|
+
twine upload dist/*
|
|
44
|
+
# Enter: __token__
|
|
45
|
+
# Enter: your-pypi-token
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Step 7: Verify it works
|
|
49
|
+
```bash
|
|
50
|
+
pip install local-persona-memory
|
|
51
|
+
python -c "from local_persona_memory import PersonaMemoryManager; print('Success!')"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Releasing updates (v0.2.0)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# 1. Update version in pyproject.toml: version = "0.2.0"
|
|
58
|
+
# 2. Update CHANGELOG.md
|
|
59
|
+
# 3. Commit: git commit -m "chore: bump version to 0.2.0"
|
|
60
|
+
# 4. Tag: git tag v0.2.0
|
|
61
|
+
# 5. Push tag: git push origin v0.2.0
|
|
62
|
+
# CI/CD automatically builds and publishes!
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Automating publish via GitHub Actions (already in ci.yml)
|
|
66
|
+
|
|
67
|
+
Add your PyPI token to GitHub:
|
|
68
|
+
Repository → Settings → Secrets → Actions → New secret
|
|
69
|
+
Name: PYPI_API_TOKEN
|
|
70
|
+
Value: your-token
|
|
71
|
+
|
|
72
|
+
Now every `git push --tags` auto-publishes to PyPI!
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# local-persona-memory
|
|
2
|
+
|
|
3
|
+
> Long-term personalized memory for ANY local LLM running via Ollama.
|
|
4
|
+
|
|
5
|
+
Your AI assistant forgets everything when the session ends. This library fixes that — permanently, privately, and with zero cloud dependency.
|
|
6
|
+
|
|
7
|
+
## What it solves
|
|
8
|
+
|
|
9
|
+
Most RAG libraries are built for searching documents. This library is built for **remembering people**. It stores user preferences, facts, skills, and goals, then injects the most relevant memories into every LLM response automatically.
|
|
10
|
+
|
|
11
|
+
## Supported models
|
|
12
|
+
|
|
13
|
+
Works with **any model in the Ollama library**:
|
|
14
|
+
|
|
15
|
+
| Use case | Recommended model |
|
|
16
|
+
|---|---|
|
|
17
|
+
| General assistant | `llama3`, `llama3.1`, `mistral` |
|
|
18
|
+
| Fast / lightweight | `phi3`, `gemma2:2b`, `tinyllama` |
|
|
19
|
+
| Coding assistant | `codellama`, `deepseek-coder` |
|
|
20
|
+
| Multilingual | `qwen2`, `qwen2.5` |
|
|
21
|
+
| Reasoning | `deepseek-r1`, `qwen2.5:14b` |
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install local-persona-memory
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Prerequisites:**
|
|
30
|
+
1. [Install Ollama](https://ollama.com)
|
|
31
|
+
2. Pull a model and the embedding model:
|
|
32
|
+
```bash
|
|
33
|
+
ollama pull llama3
|
|
34
|
+
ollama pull nomic-embed-text
|
|
35
|
+
```
|
|
36
|
+
3. Run `ollama serve` (or open the Ollama app)
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from local_persona_memory import PersonaMemoryManager
|
|
42
|
+
|
|
43
|
+
# Works with any Ollama model
|
|
44
|
+
mem = PersonaMemoryManager(user_id="alice", model="llama3")
|
|
45
|
+
|
|
46
|
+
# Store memories
|
|
47
|
+
mem.remember("Alice is a Python developer building AI tools")
|
|
48
|
+
mem.remember("Alice prefers concise answers and code examples")
|
|
49
|
+
|
|
50
|
+
# Learn from documents
|
|
51
|
+
mem.ingest_pdf("notes.pdf")
|
|
52
|
+
|
|
53
|
+
# Chat with automatic memory context
|
|
54
|
+
response = mem.chat("What should I work on today?")
|
|
55
|
+
print(response)
|
|
56
|
+
|
|
57
|
+
# Inspect stored memories
|
|
58
|
+
print(f"Stored {mem.memory_count()} memories")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Advanced usage
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from local_persona_memory import PersonaMemoryManager, LLMConfig, MemoryConfig, MemoryCategory
|
|
65
|
+
|
|
66
|
+
mem = PersonaMemoryManager(
|
|
67
|
+
user_id="bob",
|
|
68
|
+
llm_config=LLMConfig(
|
|
69
|
+
model="mistral", # any Ollama model
|
|
70
|
+
temperature=0.4,
|
|
71
|
+
context_window=8192, # increase for larger models
|
|
72
|
+
),
|
|
73
|
+
memory_config=MemoryConfig(
|
|
74
|
+
top_k_memories=8, # retrieve more memories per query
|
|
75
|
+
chunk_size=512, # PDF chunk size in characters
|
|
76
|
+
similarity_threshold=0.3,
|
|
77
|
+
embedding_model="nomic-embed-text",
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Categorise memories for better filtering
|
|
82
|
+
mem.remember("Bob knows Rust and Go", category=MemoryCategory.SKILL)
|
|
83
|
+
mem.remember("Bob wants to build a CLI tool", category=MemoryCategory.GOAL)
|
|
84
|
+
|
|
85
|
+
# Recall with category filter
|
|
86
|
+
skill_memories = mem.recall("programming languages", category=MemoryCategory.SKILL)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## How it works
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
User message
|
|
93
|
+
│
|
|
94
|
+
▼
|
|
95
|
+
[Embed with nomic-embed-text]
|
|
96
|
+
│
|
|
97
|
+
▼
|
|
98
|
+
[ChromaDB similarity search] ──► Top-K relevant memories
|
|
99
|
+
│
|
|
100
|
+
▼
|
|
101
|
+
[Build prompt: system + memories + message]
|
|
102
|
+
│
|
|
103
|
+
▼
|
|
104
|
+
[Generate with Ollama model]
|
|
105
|
+
│
|
|
106
|
+
▼
|
|
107
|
+
Personalised response
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Memories persist to `~/.local_persona_memory/` between sessions. Each user gets an isolated ChromaDB collection.
|
|
111
|
+
|
|
112
|
+
## Contributing
|
|
113
|
+
|
|
114
|
+
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md). Open an issue first for large changes.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
examples/basic_usage.py
|
|
3
|
+
-----------------------
|
|
4
|
+
Run this AFTER:
|
|
5
|
+
1. Installing Ollama: https://ollama.com
|
|
6
|
+
2. Pulling models:
|
|
7
|
+
ollama pull llama3
|
|
8
|
+
ollama pull nomic-embed-text
|
|
9
|
+
3. pip install local-persona-memory
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from local_persona_memory import PersonaMemoryManager, MemoryCategory
|
|
13
|
+
|
|
14
|
+
# ── Works with ANY Ollama model ──────────────────────────────────────────────
|
|
15
|
+
# Replace "llama3" with: mistral, phi3, gemma2, qwen2, deepseek-r1, etc.
|
|
16
|
+
mem = PersonaMemoryManager(user_id="alice", model="llama3")
|
|
17
|
+
|
|
18
|
+
# ── Store memories ───────────────────────────────────────────────────────────
|
|
19
|
+
mem.remember("Alice is a software developer specialising in Python and AI")
|
|
20
|
+
mem.remember("Alice prefers dark mode in all her editors and tools")
|
|
21
|
+
mem.remember("Alice is building a RAG-based AI assistant as a side project")
|
|
22
|
+
mem.remember("Alice's goal is to publish an open source library on PyPI",
|
|
23
|
+
category=MemoryCategory.GOAL)
|
|
24
|
+
|
|
25
|
+
# ── Learn from a PDF ─────────────────────────────────────────────────────────
|
|
26
|
+
# mem.ingest_pdf("path/to/your_document.pdf")
|
|
27
|
+
|
|
28
|
+
# ── Chat with memory context ─────────────────────────────────────────────────
|
|
29
|
+
response = mem.chat("What should I focus on this week?")
|
|
30
|
+
print("Response:", response)
|
|
31
|
+
|
|
32
|
+
# ── Inspect memories ─────────────────────────────────────────────────────────
|
|
33
|
+
print(f"\nTotal memories: {mem.memory_count()}")
|
|
34
|
+
|
|
35
|
+
results = mem.recall("programming preferences")
|
|
36
|
+
for r in results:
|
|
37
|
+
print(f" [{r.similarity_score:.2f}] {r.memory.text}")
|
|
38
|
+
|
|
39
|
+
print(repr(mem))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
examples/multi_model_demo.py
|
|
3
|
+
-----------------------------
|
|
4
|
+
Shows how to use different Ollama models for the same user.
|
|
5
|
+
Demonstrates model-agnostic design.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from local_persona_memory import PersonaMemoryManager, LLMConfig, MemoryConfig
|
|
9
|
+
|
|
10
|
+
# Same user_id = same memories, different model for generation
|
|
11
|
+
phi3_assistant = PersonaMemoryManager(
|
|
12
|
+
user_id="bob",
|
|
13
|
+
llm_config=LLMConfig(model="phi3", temperature=0.3),
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
llama_assistant = PersonaMemoryManager(
|
|
17
|
+
user_id="bob",
|
|
18
|
+
llm_config=LLMConfig(model="llama3", temperature=0.5),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
mistral_assistant = PersonaMemoryManager(
|
|
22
|
+
user_id="bob",
|
|
23
|
+
llm_config=LLMConfig(model="mistral", temperature=0.4),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Store once — all assistants share the same ChromaDB
|
|
27
|
+
phi3_assistant.remember("Bob is learning Rust for systems programming")
|
|
28
|
+
|
|
29
|
+
# Ask the same question with different models
|
|
30
|
+
question = "What is Bob working on and what advice would you give?"
|
|
31
|
+
print("=== phi3 response ===")
|
|
32
|
+
print(phi3_assistant.chat(question))
|
|
33
|
+
|
|
34
|
+
print("\n=== llama3 response ===")
|
|
35
|
+
print(llama_assistant.chat(question))
|
|
36
|
+
|
|
37
|
+
print("\n=== mistral response ===")
|
|
38
|
+
print(mistral_assistant.chat(question))
|