zer0lint 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zer0lint-0.1.0/PKG-INFO +185 -0
- zer0lint-0.1.0/README.md +169 -0
- zer0lint-0.1.0/pyproject.toml +37 -0
- zer0lint-0.1.0/tests/__init__.py +1 -0
- zer0lint-0.1.0/tests/test_fixer.py +138 -0
- zer0lint-0.1.0/tests/test_sampler.py +92 -0
- zer0lint-0.1.0/tests/test_tester.py +137 -0
- zer0lint-0.1.0/zer0lint/__init__.py +8 -0
- zer0lint-0.1.0/zer0lint/__main__.py +6 -0
- zer0lint-0.1.0/zer0lint/analyzer.py +73 -0
- zer0lint-0.1.0/zer0lint/cli.py +182 -0
- zer0lint-0.1.0/zer0lint/fixer.py +116 -0
- zer0lint-0.1.0/zer0lint/orchestrator.py +240 -0
- zer0lint-0.1.0/zer0lint/sampler.py +165 -0
- zer0lint-0.1.0/zer0lint/scanner.py +135 -0
- zer0lint-0.1.0/zer0lint/tester.py +316 -0
zer0lint-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zer0lint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: mem0 extraction optimizer: diagnose failures and generate custom prompts for your system
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Requires-Dist: httpx>=0.24.0
|
|
7
|
+
Requires-Dist: mem0ai>=0.1.0
|
|
8
|
+
Requires-Dist: pydantic>=2.0.0
|
|
9
|
+
Requires-Dist: rich>=13.0.0
|
|
10
|
+
Requires-Dist: typer[all]>=0.9.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# zer0lint
|
|
18
|
+
|
|
19
|
+
**AI memory extraction diagnostics.** Find out why your AI agent forgets what matters — and fix it.
|
|
20
|
+
|
|
21
|
+
[](https://pypi.org/project/zer0lint/)
|
|
22
|
+
[](LICENSE)
|
|
23
|
+
[](https://hermes-labs.ai)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## The Problem
|
|
28
|
+
|
|
29
|
+
You set up mem0 (or Zep, or LangMem) and your AI agent still seems forgetful. Technical decisions, version numbers, experiment results — it stores them, but can't recall them.
|
|
30
|
+
|
|
31
|
+
**Why?** mem0's default extraction prompt is designed for personal assistants: favorite coffee shops, dietary preferences, weekend plans. When your agent stores "Redis upgraded to v7.2.4" or "security audit scored 7.2/10", it silently drops the specifics.
|
|
32
|
+
|
|
33
|
+
**Proven gap (our testing):**
|
|
34
|
+
- Default mem0 prompt → **70% recall** on technical facts
|
|
35
|
+
- Wrong domain prompt (personal) → **40% recall**
|
|
36
|
+
- zer0lint technical prompt → **90% recall**
|
|
37
|
+
|
|
38
|
+
That's a **+20pp improvement** from getting the extraction prompt right.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install zer0lint # coming soon
|
|
46
|
+
|
|
47
|
+
# Check your current extraction health
|
|
48
|
+
zer0lint check --config ~/.mem0/config.json
|
|
49
|
+
|
|
50
|
+
# Diagnose and fix
|
|
51
|
+
zer0lint generate --config ~/.mem0/config.json
|
|
52
|
+
|
|
53
|
+
# Dry run (see what would change without applying)
|
|
54
|
+
zer0lint generate --config ~/.mem0/config.json --dry-run
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## What It Does
|
|
60
|
+
|
|
61
|
+
### `zer0lint check`
|
|
62
|
+
Tests your current mem0 config against domain-relevant synthetic facts. Returns a score and status.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
zer0lint v0.2.0 — extraction health check
|
|
66
|
+
Config : ~/.mem0/config.json
|
|
67
|
+
Model : qwen3.5:4b
|
|
68
|
+
Prompt : default (mem0 built-in)
|
|
69
|
+
|
|
70
|
+
Score : 4/5 (80%) — HEALTHY
|
|
71
|
+
|
|
72
|
+
✅ Version update
|
|
73
|
+
✅ CI status
|
|
74
|
+
✅ Model upgrade
|
|
75
|
+
✅ Configuration
|
|
76
|
+
⚠ API endpoint
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Statuses: **HEALTHY** (≥80%) · **ACCEPTABLE** (60–79%) · **DEGRADED** (40–59%) · **CRITICAL** (<40%)
|
|
80
|
+
|
|
81
|
+
### `zer0lint generate`
|
|
82
|
+
3-phase diagnostic + fix:
|
|
83
|
+
|
|
84
|
+
1. **Baseline** — test your config as-is
|
|
85
|
+
2. **Re-test** — inject zer0lint's domain-aware extraction prompt at config level
|
|
86
|
+
3. **Apply** — if improved, write the validated prompt to your config
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
zer0lint v0.2.0 — extraction optimizer
|
|
90
|
+
|
|
91
|
+
[1/3] Baseline — testing current config as-is...
|
|
92
|
+
Baseline score: 4/5 (80%)
|
|
93
|
+
|
|
94
|
+
[2/3] Re-testing with zer0lint technical extraction prompt...
|
|
95
|
+
Improved score: 5/5 (100%)
|
|
96
|
+
Improvement: +20pp
|
|
97
|
+
|
|
98
|
+
Results:
|
|
99
|
+
Before : 4/5 (80%)
|
|
100
|
+
After : 5/5 (100%)
|
|
101
|
+
Δ : +20pp
|
|
102
|
+
|
|
103
|
+
✅ Fix applied to config.
|
|
104
|
+
Backup: ~/.mem0/config.backup.2026-03-23T14:20:00
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Why Config-Level Injection Matters
|
|
110
|
+
|
|
111
|
+
**Important finding:** In mem0 v1.x, passing a prompt via `memory.add(..., prompt=X)` has **no measurable effect** on retrieval quality. The extraction prompt must live in the config (`custom_fact_extraction_prompt` field) to actually work.
|
|
112
|
+
|
|
113
|
+
zer0lint writes the validated prompt directly to your config — this is the correct fix.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Supported Systems
|
|
118
|
+
|
|
119
|
+
| System | Status | Notes |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| mem0 (v1.x) | ✅ Supported | Full check + generate |
|
|
122
|
+
| Zep / Graphiti | 🔜 Planned | v0.3 |
|
|
123
|
+
| LangMem | 🔜 Planned | v0.4 |
|
|
124
|
+
| Generic adapter | 🔜 Planned | BYOC callables |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Test Results (2026-03-23)
|
|
129
|
+
|
|
130
|
+
Horse race across models (5 technical + research facts):
|
|
131
|
+
|
|
132
|
+
| Model | Default prompt | zer0lint prompt | Δ |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| qwen3.5:4b | 80% | **100%** | +20pp |
|
|
135
|
+
| mistral:7b | 40% | 40% | 0pp |
|
|
136
|
+
|
|
137
|
+
**mistral:7b** produces malformed JSON regardless of prompt — model quality is the bottleneck. zer0lint detects this and recommends switching models.
|
|
138
|
+
|
|
139
|
+
Scale test (10 facts, 5 domains):
|
|
140
|
+
|
|
141
|
+
| | Score | % |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| Default | 7/10 | 70% |
|
|
144
|
+
| zer0lint | 9/10 | **90%** |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Installation
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# From source (current)
|
|
152
|
+
git clone https://github.com/roli-lpci/zer0lint
|
|
153
|
+
cd zer0lint
|
|
154
|
+
pip install -e .
|
|
155
|
+
|
|
156
|
+
# PyPI (coming soon)
|
|
157
|
+
pip install zer0lint
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Requirements:** Python 3.9+, mem0 v1.x, an Ollama or cloud LLM configured in your mem0 config.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## How It Works
|
|
165
|
+
|
|
166
|
+
zer0lint reads your existing mem0 config, borrows whatever LLM you already have configured, and runs a controlled recall test. No new API keys, no new models, no cloud calls beyond what you already have.
|
|
167
|
+
|
|
168
|
+
The extraction prompt it generates is **domain-aware** — tuned for technical/research work rather than personal assistant use cases. It's validated against synthetic facts before being applied, and your original config is always backed up.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Built by Hermes Labs
|
|
173
|
+
|
|
174
|
+
zer0lint is part of the [Hermes Labs](https://hermes-labs.ai) AI agent tooling suite:
|
|
175
|
+
|
|
176
|
+
- **[lintlang](https://github.com/roli-lpci/lintlang)** — Static linter for AI agent tool descriptions and prompts
|
|
177
|
+
- **[Little Canary](https://github.com/roli-lpci/little-canary)** — Prompt injection detection
|
|
178
|
+
- **[Suy Sideguy](https://github.com/roli-lpci/suy-sideguy)** — Runtime policy enforcement for agents
|
|
179
|
+
- **zer0lint** — Memory extraction diagnostics ← you are here
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
Apache 2.0
|
zer0lint-0.1.0/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# zer0lint
|
|
2
|
+
|
|
3
|
+
**AI memory extraction diagnostics.** Find out why your AI agent forgets what matters — and fix it.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/zer0lint/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://hermes-labs.ai)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The Problem
|
|
12
|
+
|
|
13
|
+
You set up mem0 (or Zep, or LangMem) and your AI agent still seems forgetful. Technical decisions, version numbers, experiment results — it stores them, but can't recall them.
|
|
14
|
+
|
|
15
|
+
**Why?** mem0's default extraction prompt is designed for personal assistants: favorite coffee shops, dietary preferences, weekend plans. When your agent stores "Redis upgraded to v7.2.4" or "security audit scored 7.2/10", it silently drops the specifics.
|
|
16
|
+
|
|
17
|
+
**Proven gap (our testing):**
|
|
18
|
+
- Default mem0 prompt → **70% recall** on technical facts
|
|
19
|
+
- Wrong domain prompt (personal) → **40% recall**
|
|
20
|
+
- zer0lint technical prompt → **90% recall**
|
|
21
|
+
|
|
22
|
+
That's a **+20pp improvement** from getting the extraction prompt right.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install zer0lint # coming soon
|
|
30
|
+
|
|
31
|
+
# Check your current extraction health
|
|
32
|
+
zer0lint check --config ~/.mem0/config.json
|
|
33
|
+
|
|
34
|
+
# Diagnose and fix
|
|
35
|
+
zer0lint generate --config ~/.mem0/config.json
|
|
36
|
+
|
|
37
|
+
# Dry run (see what would change without applying)
|
|
38
|
+
zer0lint generate --config ~/.mem0/config.json --dry-run
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## What It Does
|
|
44
|
+
|
|
45
|
+
### `zer0lint check`
|
|
46
|
+
Tests your current mem0 config against domain-relevant synthetic facts. Returns a score and status.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
zer0lint v0.2.0 — extraction health check
|
|
50
|
+
Config : ~/.mem0/config.json
|
|
51
|
+
Model : qwen3.5:4b
|
|
52
|
+
Prompt : default (mem0 built-in)
|
|
53
|
+
|
|
54
|
+
Score : 4/5 (80%) — HEALTHY
|
|
55
|
+
|
|
56
|
+
✅ Version update
|
|
57
|
+
✅ CI status
|
|
58
|
+
✅ Model upgrade
|
|
59
|
+
✅ Configuration
|
|
60
|
+
⚠ API endpoint
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Statuses: **HEALTHY** (≥80%) · **ACCEPTABLE** (60–79%) · **DEGRADED** (40–59%) · **CRITICAL** (<40%)
|
|
64
|
+
|
|
65
|
+
### `zer0lint generate`
|
|
66
|
+
3-phase diagnostic + fix:
|
|
67
|
+
|
|
68
|
+
1. **Baseline** — test your config as-is
|
|
69
|
+
2. **Re-test** — inject zer0lint's domain-aware extraction prompt at config level
|
|
70
|
+
3. **Apply** — if improved, write the validated prompt to your config
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
zer0lint v0.2.0 — extraction optimizer
|
|
74
|
+
|
|
75
|
+
[1/3] Baseline — testing current config as-is...
|
|
76
|
+
Baseline score: 4/5 (80%)
|
|
77
|
+
|
|
78
|
+
[2/3] Re-testing with zer0lint technical extraction prompt...
|
|
79
|
+
Improved score: 5/5 (100%)
|
|
80
|
+
Improvement: +20pp
|
|
81
|
+
|
|
82
|
+
Results:
|
|
83
|
+
Before : 4/5 (80%)
|
|
84
|
+
After : 5/5 (100%)
|
|
85
|
+
Δ : +20pp
|
|
86
|
+
|
|
87
|
+
✅ Fix applied to config.
|
|
88
|
+
Backup: ~/.mem0/config.backup.2026-03-23T14:20:00
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Why Config-Level Injection Matters
|
|
94
|
+
|
|
95
|
+
**Important finding:** In mem0 v1.x, passing a prompt via `memory.add(..., prompt=X)` has **no measurable effect** on retrieval quality. The extraction prompt must live in the config (`custom_fact_extraction_prompt` field) to actually work.
|
|
96
|
+
|
|
97
|
+
zer0lint writes the validated prompt directly to your config — this is the correct fix.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Supported Systems
|
|
102
|
+
|
|
103
|
+
| System | Status | Notes |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| mem0 (v1.x) | ✅ Supported | Full check + generate |
|
|
106
|
+
| Zep / Graphiti | 🔜 Planned | v0.3 |
|
|
107
|
+
| LangMem | 🔜 Planned | v0.4 |
|
|
108
|
+
| Generic adapter | 🔜 Planned | BYOC callables |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Test Results (2026-03-23)
|
|
113
|
+
|
|
114
|
+
Horse race across models (5 technical + research facts):
|
|
115
|
+
|
|
116
|
+
| Model | Default prompt | zer0lint prompt | Δ |
|
|
117
|
+
|---|---|---|---|
|
|
118
|
+
| qwen3.5:4b | 80% | **100%** | +20pp |
|
|
119
|
+
| mistral:7b | 40% | 40% | 0pp |
|
|
120
|
+
|
|
121
|
+
**mistral:7b** produces malformed JSON regardless of prompt — model quality is the bottleneck. zer0lint detects this and recommends switching models.
|
|
122
|
+
|
|
123
|
+
Scale test (10 facts, 5 domains):
|
|
124
|
+
|
|
125
|
+
| | Score | % |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| Default | 7/10 | 70% |
|
|
128
|
+
| zer0lint | 9/10 | **90%** |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Installation
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# From source (current)
|
|
136
|
+
git clone https://github.com/roli-lpci/zer0lint
|
|
137
|
+
cd zer0lint
|
|
138
|
+
pip install -e .
|
|
139
|
+
|
|
140
|
+
# PyPI (coming soon)
|
|
141
|
+
pip install zer0lint
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Requirements:** Python 3.9+, mem0 v1.x, an Ollama or cloud LLM configured in your mem0 config.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## How It Works
|
|
149
|
+
|
|
150
|
+
zer0lint reads your existing mem0 config, borrows whatever LLM you already have configured, and runs a controlled recall test. No new API keys, no new models, no cloud calls beyond what you already have.
|
|
151
|
+
|
|
152
|
+
The extraction prompt it generates is **domain-aware** — tuned for technical/research work rather than personal assistant use cases. It's validated against synthetic facts before being applied, and your original config is always backed up.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Built by Hermes Labs
|
|
157
|
+
|
|
158
|
+
zer0lint is part of the [Hermes Labs](https://hermes-labs.ai) AI agent tooling suite:
|
|
159
|
+
|
|
160
|
+
- **[lintlang](https://github.com/roli-lpci/lintlang)** — Static linter for AI agent tool descriptions and prompts
|
|
161
|
+
- **[Little Canary](https://github.com/roli-lpci/little-canary)** — Prompt injection detection
|
|
162
|
+
- **[Suy Sideguy](https://github.com/roli-lpci/suy-sideguy)** — Runtime policy enforcement for agents
|
|
163
|
+
- **zer0lint** — Memory extraction diagnostics ← you are here
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
Apache 2.0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zer0lint"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "mem0 extraction optimizer: diagnose failures and generate custom prompts for your system"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"mem0ai>=0.1.0",
|
|
13
|
+
"typer[all]>=0.9.0",
|
|
14
|
+
"rich>=13.0.0",
|
|
15
|
+
"httpx>=0.24.0",
|
|
16
|
+
"pydantic>=2.0.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=7.0",
|
|
22
|
+
"pytest-cov>=4.0",
|
|
23
|
+
"ruff>=0.1.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
zer0lint = "zer0lint.cli:app"
|
|
28
|
+
|
|
29
|
+
[tool.ruff]
|
|
30
|
+
line-length = 100
|
|
31
|
+
target-version = "py39"
|
|
32
|
+
|
|
33
|
+
[tool.ruff.lint]
|
|
34
|
+
select = ["E", "F", "W", "I"]
|
|
35
|
+
|
|
36
|
+
[tool.pytest.ini_options]
|
|
37
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Tests for zer0lint."""
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Tests for config application and backup."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import tempfile
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from zer0lint.fixer import (
|
|
10
|
+
apply_prompt,
|
|
11
|
+
backup_config,
|
|
12
|
+
detect_extraction_model,
|
|
13
|
+
detect_vector_store,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_backup_config():
|
|
18
|
+
"""Test config backup creation."""
|
|
19
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
20
|
+
config_path = Path(tmpdir) / "config.json"
|
|
21
|
+
config_path.write_text('{"test": "data"}')
|
|
22
|
+
|
|
23
|
+
backup_path = backup_config(config_path)
|
|
24
|
+
|
|
25
|
+
assert Path(backup_path).exists()
|
|
26
|
+
assert "backup" in backup_path
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_apply_prompt_new():
|
|
30
|
+
"""Test applying a new prompt to config."""
|
|
31
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
32
|
+
config_path = Path(tmpdir) / "config.json"
|
|
33
|
+
config = {
|
|
34
|
+
"llm": {"provider": "ollama"},
|
|
35
|
+
"custom_fact_extraction_prompt": "old prompt",
|
|
36
|
+
}
|
|
37
|
+
config_path.write_text(json.dumps(config))
|
|
38
|
+
|
|
39
|
+
result = apply_prompt(config_path, "new prompt", backup=True)
|
|
40
|
+
|
|
41
|
+
assert result["success"] is True
|
|
42
|
+
assert result["backup_path"] is not None
|
|
43
|
+
|
|
44
|
+
# Verify new prompt was written
|
|
45
|
+
updated = json.loads(config_path.read_text())
|
|
46
|
+
assert updated["custom_fact_extraction_prompt"] == "new prompt"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_apply_prompt_no_backup():
|
|
50
|
+
"""Test applying prompt without backup."""
|
|
51
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
52
|
+
config_path = Path(tmpdir) / "config.json"
|
|
53
|
+
config_path.write_text('{"custom_fact_extraction_prompt": "old"}')
|
|
54
|
+
|
|
55
|
+
result = apply_prompt(config_path, "new", backup=False)
|
|
56
|
+
|
|
57
|
+
assert result["success"] is True
|
|
58
|
+
assert result["backup_path"] is None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_apply_prompt_file_not_found():
|
|
62
|
+
"""Test error handling for missing config."""
|
|
63
|
+
with pytest.raises(FileNotFoundError):
|
|
64
|
+
apply_prompt("/nonexistent/path/config.json", "new prompt")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_detect_extraction_model_ollama():
|
|
68
|
+
"""Test model detection for Ollama config."""
|
|
69
|
+
config = {
|
|
70
|
+
"llm": {
|
|
71
|
+
"provider": "ollama",
|
|
72
|
+
"config": {"model": "mistral:7b", "ollama_base_url": "http://localhost:11434"},
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
model = detect_extraction_model(config)
|
|
77
|
+
|
|
78
|
+
assert model == "mistral:7b"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_detect_extraction_model_openai():
|
|
82
|
+
"""Test model detection for OpenAI config."""
|
|
83
|
+
config = {
|
|
84
|
+
"llm": {
|
|
85
|
+
"provider": "openai",
|
|
86
|
+
"model": "gpt-4o",
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
model = detect_extraction_model(config)
|
|
91
|
+
|
|
92
|
+
assert model == "gpt-4o"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_detect_extraction_model_unknown():
|
|
96
|
+
"""Test fallback for unknown config structure."""
|
|
97
|
+
config = {"llm": {}}
|
|
98
|
+
|
|
99
|
+
model = detect_extraction_model(config)
|
|
100
|
+
|
|
101
|
+
assert model == "unknown"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_detect_vector_store_chroma():
|
|
105
|
+
"""Test vector store detection for Chroma."""
|
|
106
|
+
config = {
|
|
107
|
+
"vector_store": {
|
|
108
|
+
"provider": "chroma",
|
|
109
|
+
"config": {"collection_name": "mem0", "path": ".mem0_chroma"},
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
store = detect_vector_store(config)
|
|
114
|
+
|
|
115
|
+
assert store == "chroma"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_detect_vector_store_qdrant():
|
|
119
|
+
"""Test vector store detection for Qdrant."""
|
|
120
|
+
config = {
|
|
121
|
+
"vector_store": {
|
|
122
|
+
"provider": "qdrant",
|
|
123
|
+
"config": {"collection_name": "mem0", "url": "http://localhost:6333"},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
store = detect_vector_store(config)
|
|
128
|
+
|
|
129
|
+
assert store == "qdrant"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_detect_vector_store_unknown():
|
|
133
|
+
"""Test fallback for unknown vector store."""
|
|
134
|
+
config = {"vector_store": {}}
|
|
135
|
+
|
|
136
|
+
store = detect_vector_store(config)
|
|
137
|
+
|
|
138
|
+
assert store == "unknown"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Tests for memory sampling."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from zer0lint.sampler import MemorySample, analyze_sample_content, sample_memories
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_memory_sample_model():
|
|
11
|
+
"""Test MemorySample dataclass."""
|
|
12
|
+
sample = MemorySample(id="1", content="API runs on port 8421", metadata={"tag": "tech"})
|
|
13
|
+
assert sample.id == "1"
|
|
14
|
+
assert sample.content == "API runs on port 8421"
|
|
15
|
+
assert sample.metadata["tag"] == "tech"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_sample_memories_with_search():
|
|
19
|
+
"""Test sampling memories via search method."""
|
|
20
|
+
mock_memory = MagicMock()
|
|
21
|
+
mock_memory.search.return_value = [
|
|
22
|
+
{"id": "1", "memory": "API runs on port 8421", "metadata": None},
|
|
23
|
+
{"id": "2", "memory": "Using gpt-4o-mini", "metadata": None},
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
samples = sample_memories(mock_memory, limit=30)
|
|
27
|
+
|
|
28
|
+
assert len(samples) == 2
|
|
29
|
+
assert samples[0].id == "1"
|
|
30
|
+
assert "port 8421" in samples[0].content
|
|
31
|
+
mock_memory.search.assert_called_once()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_sample_memories_fallback_to_direct():
|
|
35
|
+
"""Test fallback to direct store access when search fails."""
|
|
36
|
+
mock_memory = MagicMock()
|
|
37
|
+
mock_memory.search.side_effect = Exception("Search failed")
|
|
38
|
+
mock_memory.vector_store.get_all.return_value = [
|
|
39
|
+
{"id": "1", "content": "Memory 1", "text": None},
|
|
40
|
+
{"id": "2", "content": "Memory 2", "text": None},
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
samples = sample_memories(mock_memory, limit=30)
|
|
44
|
+
|
|
45
|
+
assert len(samples) >= 1
|
|
46
|
+
assert samples[0].id == "1"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_sample_memories_empty():
|
|
50
|
+
"""Test handling empty memories."""
|
|
51
|
+
mock_memory = MagicMock()
|
|
52
|
+
mock_memory.search.return_value = []
|
|
53
|
+
mock_memory.vector_store.get_all.side_effect = Exception("No store")
|
|
54
|
+
|
|
55
|
+
samples = sample_memories(mock_memory)
|
|
56
|
+
|
|
57
|
+
assert samples == []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_analyze_sample_content_technical():
|
|
61
|
+
"""Test pattern analysis for technical content."""
|
|
62
|
+
samples = [
|
|
63
|
+
MemorySample(id="1", content="API server runs on port 8421"),
|
|
64
|
+
MemorySample(id="2", content="Updated to gpt-4o-mini model"),
|
|
65
|
+
MemorySample(id="3", content="Redis version 7.2.4 deployed"),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
patterns = analyze_sample_content(samples)
|
|
69
|
+
|
|
70
|
+
assert "technical" in patterns
|
|
71
|
+
assert any(kw in patterns["technical"] for kw in ["port", "version", "model"])
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_analyze_sample_content_mixed():
|
|
75
|
+
"""Test pattern analysis with mixed content."""
|
|
76
|
+
samples = [
|
|
77
|
+
MemorySample(id="1", content="API on port 8421"),
|
|
78
|
+
MemorySample(id="2", content="Diagnosis: acute bronchitis"),
|
|
79
|
+
MemorySample(id="3", content="Contract clause specifies indemnification"),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
patterns = analyze_sample_content(samples)
|
|
83
|
+
|
|
84
|
+
# Should detect multiple domains
|
|
85
|
+
detected_domains = list(patterns.keys())
|
|
86
|
+
assert len(detected_domains) > 0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_analyze_sample_content_empty():
|
|
90
|
+
"""Test pattern analysis with no samples."""
|
|
91
|
+
patterns = analyze_sample_content([])
|
|
92
|
+
assert patterns == {}
|