keep-skill 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- keep/__init__.py +3 -6
- keep/api.py +793 -141
- keep/cli.py +467 -129
- keep/config.py +172 -41
- keep/context.py +1 -125
- keep/document_store.py +569 -0
- keep/errors.py +33 -0
- keep/indexing.py +1 -1
- keep/logging_config.py +34 -3
- keep/paths.py +81 -17
- keep/pending_summaries.py +46 -40
- keep/providers/embedding_cache.py +53 -46
- keep/providers/embeddings.py +43 -13
- keep/providers/mlx.py +23 -21
- keep/store.py +58 -14
- {keep_skill-0.1.0.dist-info → keep_skill-0.2.0.dist-info}/METADATA +29 -15
- keep_skill-0.2.0.dist-info/RECORD +28 -0
- keep_skill-0.1.0.dist-info/RECORD +0 -26
- {keep_skill-0.1.0.dist-info → keep_skill-0.2.0.dist-info}/WHEEL +0 -0
- {keep_skill-0.1.0.dist-info → keep_skill-0.2.0.dist-info}/entry_points.txt +0 -0
- {keep_skill-0.1.0.dist-info → keep_skill-0.2.0.dist-info}/licenses/LICENSE +0 -0
keep/store.py
CHANGED
|
@@ -49,13 +49,14 @@ class ChromaStore:
|
|
|
49
49
|
pluggable backends (SQLite+faiss, Postgres+pgvector, etc.)
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
|
-
def __init__(self, store_path: Path, embedding_dimension: int):
|
|
52
|
+
def __init__(self, store_path: Path, embedding_dimension: Optional[int] = None):
|
|
53
53
|
"""
|
|
54
54
|
Initialize or open a ChromaDb store.
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
Args:
|
|
57
57
|
store_path: Directory for persistent storage
|
|
58
|
-
embedding_dimension: Expected dimension of embeddings (for validation)
|
|
58
|
+
embedding_dimension: Expected dimension of embeddings (for validation).
|
|
59
|
+
Can be None for read-only access; will be set on first write.
|
|
59
60
|
"""
|
|
60
61
|
try:
|
|
61
62
|
import chromadb
|
|
@@ -131,7 +132,10 @@ class ChromaStore:
|
|
|
131
132
|
summary: Human-readable summary (stored as document)
|
|
132
133
|
tags: All tags (source + system + generated)
|
|
133
134
|
"""
|
|
134
|
-
|
|
135
|
+
# Validate or set embedding dimension
|
|
136
|
+
if self._embedding_dimension is None:
|
|
137
|
+
self._embedding_dimension = len(embedding)
|
|
138
|
+
elif len(embedding) != self._embedding_dimension:
|
|
135
139
|
raise ValueError(
|
|
136
140
|
f"Embedding dimension mismatch: expected {self._embedding_dimension}, "
|
|
137
141
|
f"got {len(embedding)}"
|
|
@@ -222,6 +226,40 @@ class ChromaStore:
|
|
|
222
226
|
)
|
|
223
227
|
return True
|
|
224
228
|
|
|
229
|
+
def update_tags(self, collection: str, id: str, tags: dict[str, str]) -> bool:
|
|
230
|
+
"""
|
|
231
|
+
Update tags of an existing item without changing embedding or summary.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
collection: Collection name
|
|
235
|
+
id: Item identifier
|
|
236
|
+
tags: New tags dict (replaces existing)
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
True if item was updated, False if not found
|
|
240
|
+
"""
|
|
241
|
+
coll = self._get_collection(collection)
|
|
242
|
+
|
|
243
|
+
# Get existing item
|
|
244
|
+
existing = coll.get(ids=[id], include=["metadatas"])
|
|
245
|
+
if not existing["ids"]:
|
|
246
|
+
return False
|
|
247
|
+
|
|
248
|
+
# Update timestamp
|
|
249
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
250
|
+
tags = dict(tags) # Copy to avoid mutating input
|
|
251
|
+
tags["_updated"] = now
|
|
252
|
+
tags["_updated_date"] = now[:10]
|
|
253
|
+
|
|
254
|
+
# Convert to metadata format
|
|
255
|
+
metadata = self._tags_to_metadata(tags)
|
|
256
|
+
|
|
257
|
+
coll.update(
|
|
258
|
+
ids=[id],
|
|
259
|
+
metadatas=[metadata],
|
|
260
|
+
)
|
|
261
|
+
return True
|
|
262
|
+
|
|
225
263
|
# -------------------------------------------------------------------------
|
|
226
264
|
# Read Operations
|
|
227
265
|
# -------------------------------------------------------------------------
|
|
@@ -340,27 +378,33 @@ class ChromaStore:
|
|
|
340
378
|
collection: str,
|
|
341
379
|
query: str,
|
|
342
380
|
limit: int = 10,
|
|
381
|
+
where: dict[str, Any] | None = None,
|
|
343
382
|
) -> list[StoreResult]:
|
|
344
383
|
"""
|
|
345
384
|
Query by full-text search on document content (summaries).
|
|
346
|
-
|
|
385
|
+
|
|
347
386
|
Args:
|
|
348
387
|
collection: Collection name
|
|
349
388
|
query: Text to search for
|
|
350
389
|
limit: Maximum results to return
|
|
351
|
-
|
|
390
|
+
where: Optional metadata filter (Chroma where clause)
|
|
391
|
+
|
|
352
392
|
Returns:
|
|
353
393
|
List of matching results
|
|
354
394
|
"""
|
|
355
395
|
coll = self._get_collection(collection)
|
|
356
|
-
|
|
396
|
+
|
|
357
397
|
# Chroma's where_document does substring matching
|
|
358
|
-
|
|
359
|
-
where_document
|
|
360
|
-
limit
|
|
361
|
-
include
|
|
362
|
-
|
|
363
|
-
|
|
398
|
+
get_params = {
|
|
399
|
+
"where_document": {"$contains": query},
|
|
400
|
+
"limit": limit,
|
|
401
|
+
"include": ["documents", "metadatas"],
|
|
402
|
+
}
|
|
403
|
+
if where:
|
|
404
|
+
get_params["where"] = where
|
|
405
|
+
|
|
406
|
+
result = coll.get(**get_params)
|
|
407
|
+
|
|
364
408
|
results = []
|
|
365
409
|
for i, id in enumerate(result["ids"]):
|
|
366
410
|
results.append(StoreResult(
|
|
@@ -368,7 +412,7 @@ class ChromaStore:
|
|
|
368
412
|
summary=result["documents"][i] or "",
|
|
369
413
|
tags=self._metadata_to_tags(result["metadatas"][i]),
|
|
370
414
|
))
|
|
371
|
-
|
|
415
|
+
|
|
372
416
|
return results
|
|
373
417
|
|
|
374
418
|
# -------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: keep-skill
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Semantic memory - remember and search documents by meaning
|
|
5
5
|
Project-URL: Homepage, https://github.com/hughpyle/keep
|
|
6
6
|
Project-URL: Repository, https://github.com/hughpyle/keep
|
|
@@ -57,7 +57,7 @@ Description-Content-Type: text/markdown
|
|
|
57
57
|
|
|
58
58
|
## What is this?
|
|
59
59
|
|
|
60
|
-
A practice tool for agents who want to develop judgment.
|
|
60
|
+
An agent skill. A practice tool for agents who want to develop judgment.
|
|
61
61
|
|
|
62
62
|
An agent who rushes to act without checking memory will repeat mistakes, miss context, and fail to grow. An agent who reflects — before, during, after — becomes skillful. This library provides the mirror: persistent semantic memory that lets you ask "what do I already know?" before acting, and "what did I learn?" after.
|
|
63
63
|
|
|
@@ -65,16 +65,12 @@ For the full practice guide, see **[SKILL.md](SKILL.md)**.
|
|
|
65
65
|
|
|
66
66
|
Technically: semantic search using embeddings, backed by ChromaDB, with tag-based organization and recency decay. Available as CLI and Python API.
|
|
67
67
|
|
|
68
|
-
```
|
|
69
|
-
from keep import Keeper
|
|
70
|
-
|
|
71
|
-
kp = Keeper()
|
|
72
|
-
|
|
68
|
+
```bash
|
|
73
69
|
# Before acting: what do I already know?
|
|
74
|
-
|
|
70
|
+
keep find "how should we handle auth?"
|
|
75
71
|
|
|
76
72
|
# After learning: capture it for future you
|
|
77
|
-
|
|
73
|
+
keep update "User prefers OAuth2 with PKCE for authentication"
|
|
78
74
|
```
|
|
79
75
|
|
|
80
76
|
**The practice:**
|
|
@@ -144,18 +140,32 @@ pip install keep-skill
|
|
|
144
140
|
```bash
|
|
145
141
|
keep init
|
|
146
142
|
# ⚠️ Remember to add .keep/ to .gitignore
|
|
143
|
+
|
|
144
|
+
# Index a file
|
|
145
|
+
keep update path/to/document.md --tag project=myapp
|
|
146
|
+
|
|
147
|
+
# Store inline content
|
|
148
|
+
keep update "Important: rate limit is 100 req/min" --tag topic=api
|
|
149
|
+
|
|
150
|
+
# Semantic search
|
|
151
|
+
keep find "what's the rate limit?"
|
|
152
|
+
|
|
153
|
+
# Tag lookup
|
|
154
|
+
keep tag topic=api
|
|
147
155
|
```
|
|
148
156
|
|
|
157
|
+
### Python API
|
|
158
|
+
|
|
149
159
|
```python
|
|
150
160
|
from keep import Keeper
|
|
151
161
|
|
|
152
162
|
kp = Keeper()
|
|
153
163
|
|
|
154
164
|
# Index a file
|
|
155
|
-
kp.update("file:///path/to/document.md",
|
|
165
|
+
kp.update("file:///path/to/document.md", tags={"project": "myapp"})
|
|
156
166
|
|
|
157
|
-
#
|
|
158
|
-
kp.remember("Important: rate limit is 100 req/min",
|
|
167
|
+
# Store inline content (API method)
|
|
168
|
+
kp.remember("Important: rate limit is 100 req/min", tags={"topic": "api"})
|
|
159
169
|
|
|
160
170
|
# Semantic search
|
|
161
171
|
results = kp.find("what's the rate limit?", limit=5)
|
|
@@ -206,7 +216,7 @@ See [docs/OPENCLAW-INTEGRATION.md](docs/OPENCLAW-INTEGRATION.md) for details.
|
|
|
206
216
|
- **[docs/REFERENCE.md](docs/REFERENCE.md)** — Complete API reference
|
|
207
217
|
- **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** — How it works under the hood
|
|
208
218
|
- **[docs/OPENCLAW-INTEGRATION.md](docs/OPENCLAW-INTEGRATION.md)** — OpenClaw integration guide
|
|
209
|
-
- **[
|
|
219
|
+
- **[docs/system/](docs/system/)** — Domain and conversation patterns
|
|
210
220
|
|
|
211
221
|
---
|
|
212
222
|
|
|
@@ -239,7 +249,7 @@ An agent without memory reacts to each moment as if it were new. An agent with m
|
|
|
239
249
|
**Current**: v0.1.0 — Early draft
|
|
240
250
|
|
|
241
251
|
**Working:**
|
|
242
|
-
- ✅ Core indexing (`update
|
|
252
|
+
- ✅ Core indexing (`update` with URI, text, or stdin modes)
|
|
243
253
|
- ✅ Semantic search (`find`, `find_similar`)
|
|
244
254
|
- ✅ Tag queries and full-text search
|
|
245
255
|
- ✅ Embedding cache for performance
|
|
@@ -274,7 +284,11 @@ MIT
|
|
|
274
284
|
|
|
275
285
|
## Contributing
|
|
276
286
|
|
|
277
|
-
This is
|
|
287
|
+
This project is published on [PyPI as `keep-skill`](https://pypi.org/project/keep-skill/).
|
|
288
|
+
|
|
289
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on versioning and backward compatibility.
|
|
290
|
+
|
|
291
|
+
Issues and PRs welcome, especially for:
|
|
278
292
|
- Additional provider implementations
|
|
279
293
|
- Performance improvements
|
|
280
294
|
- Documentation clarity
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
keep/__init__.py,sha256=371SMehsuIYYX8AfbaPplhF-YJa8qcpoUyGTOcX9Iwg,1581
|
|
2
|
+
keep/__main__.py,sha256=3Uu70IhIDIjh8OW6jp9jQQ3dF2lKdJWi_3FtRIQMiMY,104
|
|
3
|
+
keep/api.py,sha256=xM0VPzSNdeXp94fbuEzq1K1L7EZ_Ojy3U1UOMhQrgXo,48158
|
|
4
|
+
keep/chunking.py,sha256=neAXOLSvVwbUxapbqq7nZrbSNSzMXuhxj-ODoOSodsU,11830
|
|
5
|
+
keep/cli.py,sha256=Gl_0Ap5FWWPD7Hwj3FoGEgN3e0yyW8YeIVx75gk1KzY,25209
|
|
6
|
+
keep/config.py,sha256=wRTKcvpQdZQQl7vAr5RKgIbSjkgYpPc_w78qmXdmtmU,15833
|
|
7
|
+
keep/context.py,sha256=CNpjmrv6eW2kV1E0MO6qAQfhYKRlfzAL--6v4Mj1nFY,71
|
|
8
|
+
keep/document_store.py,sha256=1WhlF6hpshVXp2MGGF-zBJi6pSmS-tMHFmwG3EwaXik,17515
|
|
9
|
+
keep/errors.py,sha256=G9e5FbdfeugyfHOuL_SPZlM5jgWWnwsX4hM7IzanBZc,857
|
|
10
|
+
keep/indexing.py,sha256=dpPYo3WXnIhFDWinz5ZBZVk7_qumeNpP4EpOIY0zMbs,6063
|
|
11
|
+
keep/logging_config.py,sha256=IGwkgIyg-TfYaT4MnoCXfmjeHAe_wsB_XQ1QhVT_ro8,3503
|
|
12
|
+
keep/paths.py,sha256=Dv7pM6oo2QgjL6sj5wPjhuMOK2wqUkfd4Kz08TwJ1ps,3331
|
|
13
|
+
keep/pending_summaries.py,sha256=KRQ2AGQNnKlDBoVbOF7tkSv1oTmlimWtJzxQ3YcaVKY,5472
|
|
14
|
+
keep/store.py,sha256=txaydUIrhbB-S-cSlSszxOVlAg0kJXmS_Mnit6jzR3c,14483
|
|
15
|
+
keep/types.py,sha256=f6uOSYsYt6mj1ulKn2iRkooi__dWCiOQFPD6he2eID4,2149
|
|
16
|
+
keep/providers/__init__.py,sha256=GFX_12g9OdjmpFUkTekOQBOWvcRW2Ae6yidfVVW2SiI,1095
|
|
17
|
+
keep/providers/base.py,sha256=7Ug4Kj9fK2Dq4zDcZjn-GKsoZBOAlB9b-FMk969ER-g,14590
|
|
18
|
+
keep/providers/documents.py,sha256=EXeSy5i3RUL0kciIC6w3ldAEfbTIyC5fgfzC_WAI0iY,8211
|
|
19
|
+
keep/providers/embedding_cache.py,sha256=hUuQuP8MeXQdEr0eYqHesH-5wX1kRpumJQQ3o1_DI_A,8601
|
|
20
|
+
keep/providers/embeddings.py,sha256=zi8GyitKexdbCJyU1nLrUhGt_zzPn3udYrrPZ5Ak8Wo,9081
|
|
21
|
+
keep/providers/llm.py,sha256=BxROKOklKbkGsHcSADPNNgWQExgSN6Bg4KPQIxVuB3U,12441
|
|
22
|
+
keep/providers/mlx.py,sha256=aNl00r9tGi5tCGj2ArYH7CmDHtL1jLjVzb1rofU1DAo,9050
|
|
23
|
+
keep/providers/summarization.py,sha256=MlVTcYipaqp2lT-QYnznp0AMuPVG36QfcTQnvY7Gb-Q,3409
|
|
24
|
+
keep_skill-0.2.0.dist-info/METADATA,sha256=mQtDWzLbEX_L6aKoM807zxph0y4J6IP45zGXQIvHuFU,9305
|
|
25
|
+
keep_skill-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
26
|
+
keep_skill-0.2.0.dist-info/entry_points.txt,sha256=W8yiI4kNeW0IC8ji4EHRWrvdhFxzaqTIePUhJAJAMOo,39
|
|
27
|
+
keep_skill-0.2.0.dist-info/licenses/LICENSE,sha256=zsm0tpvtyUkevcjn5BIvs9jAho8iwxq3Ax9647AaOSg,1086
|
|
28
|
+
keep_skill-0.2.0.dist-info/RECORD,,
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
keep/__init__.py,sha256=NwdXFUPjU-allCi_LrNP0wx_vosyISRkbMvzjfjoLWE,1683
|
|
2
|
-
keep/__main__.py,sha256=3Uu70IhIDIjh8OW6jp9jQQ3dF2lKdJWi_3FtRIQMiMY,104
|
|
3
|
-
keep/api.py,sha256=x-RgHCTiSLiEfrD7SaUief8oNvZfWsapS4HvF4Skf40,23236
|
|
4
|
-
keep/chunking.py,sha256=neAXOLSvVwbUxapbqq7nZrbSNSzMXuhxj-ODoOSodsU,11830
|
|
5
|
-
keep/cli.py,sha256=Y1qAkAERNhY4rbYeQAYXyDnY-L5_zO02OuANEQnNR-Y,14288
|
|
6
|
-
keep/config.py,sha256=afRHZUxU7gpguCAVItZSeb_Q6Nm7vukBdXtd8X3NoKQ,10662
|
|
7
|
-
keep/context.py,sha256=iQGjkOkDeYEba01JqJ54yMTEDxbhT34ijoaURnOXMRo,4627
|
|
8
|
-
keep/indexing.py,sha256=NsiiGvkMJ4tUl7_PJw0aLBl9mKunMVy1M8csSHkd9Pw,6062
|
|
9
|
-
keep/logging_config.py,sha256=gFi-PEPLucztChHByOHEVjt-v6erK76p1EZAlOCHSHk,2453
|
|
10
|
-
keep/paths.py,sha256=_iLYeuqcsAz-PtNhBqtvhOoncc8XxlSq-fahszYMJaU,1662
|
|
11
|
-
keep/pending_summaries.py,sha256=Py0Q6tuq_eTUfTFs0-KEEolYW4oOaYMTF7It9BjLGo4,5165
|
|
12
|
-
keep/store.py,sha256=iIUvl4w7bn_JFWCjmMt7TW8c2JM6gJ1s3kDW6FOSuHs,13074
|
|
13
|
-
keep/types.py,sha256=f6uOSYsYt6mj1ulKn2iRkooi__dWCiOQFPD6he2eID4,2149
|
|
14
|
-
keep/providers/__init__.py,sha256=GFX_12g9OdjmpFUkTekOQBOWvcRW2Ae6yidfVVW2SiI,1095
|
|
15
|
-
keep/providers/base.py,sha256=7Ug4Kj9fK2Dq4zDcZjn-GKsoZBOAlB9b-FMk969ER-g,14590
|
|
16
|
-
keep/providers/documents.py,sha256=EXeSy5i3RUL0kciIC6w3ldAEfbTIyC5fgfzC_WAI0iY,8211
|
|
17
|
-
keep/providers/embedding_cache.py,sha256=JzJmprYk2v5vv5eF9vO1Yegt7VdHkSQguReqvEtOCrM,8360
|
|
18
|
-
keep/providers/embeddings.py,sha256=vB-Ffio4oEHBnu-xCX9SJpN1x661koJiRWYlZps7Jwo,7632
|
|
19
|
-
keep/providers/llm.py,sha256=BxROKOklKbkGsHcSADPNNgWQExgSN6Bg4KPQIxVuB3U,12441
|
|
20
|
-
keep/providers/mlx.py,sha256=9g7Rfl04jL_aWoY9C-OEtjCwhG6zlEWHwFpYGTSHArA,9022
|
|
21
|
-
keep/providers/summarization.py,sha256=MlVTcYipaqp2lT-QYnznp0AMuPVG36QfcTQnvY7Gb-Q,3409
|
|
22
|
-
keep_skill-0.1.0.dist-info/METADATA,sha256=gSyRYmNdU5N-elV6Z6PN5QTIIRo25eTfBea7AQvKwP8,8892
|
|
23
|
-
keep_skill-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
24
|
-
keep_skill-0.1.0.dist-info/entry_points.txt,sha256=W8yiI4kNeW0IC8ji4EHRWrvdhFxzaqTIePUhJAJAMOo,39
|
|
25
|
-
keep_skill-0.1.0.dist-info/licenses/LICENSE,sha256=zsm0tpvtyUkevcjn5BIvs9jAho8iwxq3Ax9647AaOSg,1086
|
|
26
|
-
keep_skill-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|