hermes-plugin-qdrant-memory 0.2.0__tar.gz → 0.2.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.
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/PKG-INFO +52 -31
- hermes_plugin_qdrant_memory-0.2.0/hermes_plugin_qdrant_memory.egg-info/PKG-INFO → hermes_plugin_qdrant_memory-0.2.1/README.md +49 -49
- hermes_plugin_qdrant_memory-0.2.0/README.md → hermes_plugin_qdrant_memory-0.2.1/hermes_plugin_qdrant_memory.egg-info/PKG-INFO +70 -30
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/SOURCES.txt +1 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/pyproject.toml +3 -1
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/dedupe.py +8 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/plugin.yaml +1 -1
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/runtime.py +3 -5
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/version.py +1 -1
- hermes_plugin_qdrant_memory-0.2.1/tests/test_explicit_dedupe.py +80 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_manifest_contract.py +7 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/LICENSE +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/MANIFEST.in +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/dependency_links.txt +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/entry_points.txt +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/requires.txt +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/hermes_plugin_qdrant_memory.egg-info/top_level.txt +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/__init__.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/cli.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/config.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/config_schema.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/embedding.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/extraction.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/inventory.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/ledger.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/mcp_server.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/migration.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/models.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/ownership.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/progress.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/provider.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/qdrant_store.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/reset.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/retry.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/standalone.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/tools.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/scripts/demo.sh +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/setup.cfg +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_backend_contracts.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_bot_scope.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_bounded_recovery.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_checkpoint_lifecycle.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_ci_gate.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_cli_output.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_config_embedding.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_deletion_ordering.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_discovery_cli.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_host_lifecycle.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_input_validation.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_inventory_export.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_live_metrics.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_maintenance.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_endpoints.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_progress.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_resume.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration_superseded.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_profile_scope.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_provider.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_recovery_benchmark.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_recovery_evaluation.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_remote_qdrant.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_reset_recovery.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_retention.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_review_regressions.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_session_provenance.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_setup_contract.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_shutdown_contract.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_store_runtime.py +0 -0
- {hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_writer_ownership.py +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hermes-plugin-qdrant-memory
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Durable Qdrant memory for Hermes, standalone CLI and local MCP
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/cnkang/hermes-plugin-qdrant-memory
|
|
7
7
|
Project-URL: Repository, https://github.com/cnkang/hermes-plugin-qdrant-memory
|
|
8
8
|
Project-URL: Issues, https://github.com/cnkang/hermes-plugin-qdrant-memory/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: MCP, https://github.com/cnkang/qdrant-memory-mcp
|
|
11
|
+
Project-URL: Skill, https://github.com/cnkang/skills/tree/main/qdrant-memory
|
|
10
12
|
Requires-Python: >=3.11
|
|
11
13
|
Description-Content-Type: text/markdown
|
|
12
14
|
License-File: LICENSE
|
|
@@ -26,12 +28,11 @@ Dynamic: license-file
|
|
|
26
28
|
[](https://www.python.org/downloads/)
|
|
27
29
|
[](LICENSE)
|
|
28
30
|
|
|
29
|
-
> **
|
|
30
|
-
>
|
|
31
|
-
>
|
|
32
|
-
>
|
|
33
|
-
>
|
|
34
|
-
> current certification.
|
|
31
|
+
> **Runtime 0.2.1** provides the standalone CLI/MCP and fixes explicit writes of
|
|
32
|
+
> distinct, highly similar texts. MCP distribution 0.1.1 pins Runtime 0.2.1.
|
|
33
|
+
> The Hermes Plugin Catalog has its own reviewed immutable pin. A PyPI release
|
|
34
|
+
> does not upgrade it. One writer per destination remains required.
|
|
35
|
+
> See [release notes](docs/releases/0.2.1.md) and [validation](docs/validation.md).
|
|
35
36
|
|
|
36
37
|
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
37
38
|
|
|
@@ -45,6 +46,16 @@ as a community-maintained plugin.
|
|
|
45
46
|
|
|
46
47
|
## How it works
|
|
47
48
|
|
|
49
|
+
For non-Hermes users, install in a private Python 3.11+
|
|
50
|
+
environment: `pip install 'hermes-plugin-qdrant-memory[mcp]==0.2.1'` (omit `[mcp]`
|
|
51
|
+
for CLI only). `qdrant-memory` is the CLI and `mcp` is its server subcommand.
|
|
52
|
+
Standalone does not require Hermes. See [setup and quick start](docs/standalone.md).
|
|
53
|
+
MCP-focused agents may instead use the [metadata-only distribution](https://github.com/cnkang/qdrant-memory-mcp).
|
|
54
|
+
Public releases: [Runtime on PyPI](https://pypi.org/project/hermes-plugin-qdrant-memory/)
|
|
55
|
+
and [MCP distribution on PyPI](https://pypi.org/project/qdrant-memory-mcp/).
|
|
56
|
+
For native Hermes use its official plugin installation mechanism below. Before
|
|
57
|
+
an authorized upgrade, verify the same profile, collection and embedding pipeline.
|
|
58
|
+
|
|
48
59
|
```mermaid
|
|
49
60
|
flowchart LR
|
|
50
61
|
turn["Hermes turn"] --> admit["Provider callback"]
|
|
@@ -66,7 +77,7 @@ flowchart LR
|
|
|
66
77
|
- A fully local setup works: embedded Qdrant and local Ollama keep the data
|
|
67
78
|
on your machine.
|
|
68
79
|
- Data locality follows the destination. Extraction uses the LLM configured
|
|
69
|
-
in Hermes, and embedding uses the configured embedding service
|
|
80
|
+
in Hermes, and embedding uses the configured embedding service. Either can
|
|
70
81
|
be remote. See [Data flow and privacy](#data-flow-and-privacy).
|
|
71
82
|
- Not supported in this preview: multi-writer Server/Cloud, hybrid search and
|
|
72
83
|
a web UI.
|
|
@@ -75,9 +86,9 @@ flowchart LR
|
|
|
75
86
|
|
|
76
87
|
The plugin primarily provides Hermes Agent's durable memory integration. As an
|
|
77
88
|
additional capability, its shared backend can serve other agents through the
|
|
78
|
-
Runtime
|
|
79
|
-
interface
|
|
80
|
-
memory operations and maintenance/migration
|
|
89
|
+
Runtime's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
|
|
90
|
+
interface. Standalone use does not require Hermes. These interfaces support explicit scoped
|
|
91
|
+
memory operations and maintenance/migration. Automatic extraction and prefetch
|
|
81
92
|
remain Hermes lifecycle features. See [standalone setup](docs/standalone.md) for
|
|
82
93
|
configuration and boundaries, and the agent-neutral
|
|
83
94
|
[qdrant-memory skill](https://github.com/cnkang/skills/tree/main/qdrant-memory)
|
|
@@ -85,19 +96,20 @@ for multi-agent installation and usage guidance.
|
|
|
85
96
|
|
|
86
97
|
The optional MCP server exposes four scoped tools: `qdrant_memory_search`,
|
|
87
98
|
`qdrant_memory_add`, `qdrant_memory_update` and `qdrant_memory_delete`. Compatible
|
|
88
|
-
clients launch it as a local stdio process and discover/call those tools
|
|
89
|
-
|
|
99
|
+
clients launch it as a local stdio process and discover/call those tools.
|
|
100
|
+
Maintenance and migration use the operator CLI. See the
|
|
90
101
|
[MCP capabilities and connection guide](docs/mcp.md) for setup, shared-memory
|
|
91
|
-
boundaries and the verified agent matrix.
|
|
92
|
-
|
|
102
|
+
boundaries and the verified agent matrix. Runtime 0.2.0 published these interfaces.
|
|
103
|
+
The published v0.1.0 plugin release does not include the MCP entrypoint.
|
|
93
104
|
|
|
94
105
|
## See it in action
|
|
95
106
|
|
|
96
107
|

|
|
97
108
|
|
|
98
|
-
The recording shows the full flow on a clean profile
|
|
99
|
-
official Hermes Plugin Catalog,
|
|
100
|
-
extraction
|
|
109
|
+
The recording shows the full flow on a clean profile. It installs from the
|
|
110
|
+
official Hermes Plugin Catalog, teaches a preference, and waits for the
|
|
111
|
+
background extraction. Then it recalls the preference in a new session and
|
|
112
|
+
cleans up the demo scope. All steps
|
|
101
113
|
run in embedded mode. `scripts/demo.sh` reproduces the same walkthrough with
|
|
102
114
|
the pinned release.
|
|
103
115
|
|
|
@@ -128,14 +140,17 @@ The install resolves the catalog's reviewed commit and prints the entry's declar
|
|
|
128
140
|
tools, requirements and known issues before it clones anything.
|
|
129
141
|
`hermes plugins enable` prepares the declared dependencies through Hermes PM.
|
|
130
142
|
|
|
131
|
-
**Version-locked: install the
|
|
143
|
+
**Version-locked: install the verified Runtime 0.2.1 source through the Hermes CLI.**
|
|
144
|
+
|
|
145
|
+
This immutable source revision includes the explicit-write fix. It does not
|
|
146
|
+
depend on a PyPI release or a catalog update.
|
|
132
147
|
|
|
133
148
|
```bash
|
|
134
149
|
export HERMES_HOME="/absolute/path/to/your/hermes-profile"
|
|
135
150
|
ollama pull qwen3-embedding:4b
|
|
136
151
|
# Start Ollama if it is not running. Keep its service available.
|
|
137
152
|
hermes plugins install https://github.com/cnkang/hermes-plugin-qdrant-memory \
|
|
138
|
-
--ref
|
|
153
|
+
--ref 8929693fcccd1efa91bfa062145e35d2369ca9cc # verified Runtime 0.2.1 source
|
|
139
154
|
hermes plugins enable qdrant-memory
|
|
140
155
|
hermes memory setup
|
|
141
156
|
hermes config set memory.provider qdrant-memory
|
|
@@ -144,12 +159,15 @@ hermes qdrant-memory init
|
|
|
144
159
|
|
|
145
160
|
For this version-locked form, `hermes plugins install` checks out the given ref, and
|
|
146
161
|
`hermes plugins enable` prepares the declared dependencies through Hermes PM.
|
|
147
|
-
`--ref` takes a full 40-character commit SHA
|
|
162
|
+
`--ref` takes a full 40-character commit SHA. The
|
|
148
163
|
[releases page](https://github.com/cnkang/hermes-plugin-qdrant-memory/releases)
|
|
149
164
|
lists the release commits. Do not pip-install into a managed Hermes
|
|
150
165
|
environment. Omit `--ref` to track `main`, the development line.
|
|
151
166
|
|
|
152
|
-
**
|
|
167
|
+
**Historical release: clone a published tag manually.**
|
|
168
|
+
|
|
169
|
+
The v0.1.0 tag below predates the standalone interfaces and the explicit-write
|
|
170
|
+
fix. Use the pinned source above for Runtime 0.2.1.
|
|
153
171
|
|
|
154
172
|
```bash
|
|
155
173
|
export HERMES_HOME="/absolute/path/to/your/hermes-profile"
|
|
@@ -178,7 +196,7 @@ import and registration. For development installs, see
|
|
|
178
196
|
[development setup](docs/development.md).
|
|
179
197
|
|
|
180
198
|
The release notes, supported Hermes/Python versions and the current
|
|
181
|
-
limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md)
|
|
199
|
+
limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md).
|
|
182
200
|
`main` stays the development line.
|
|
183
201
|
|
|
184
202
|
For local development, symlink the repository into a disposable profile:
|
|
@@ -223,7 +241,10 @@ mappings) stays in the local Hermes home. Back up that home together with your
|
|
|
223
241
|
Qdrant data. Migration from Mem0 is one-way, and version 0.1 has no vector
|
|
224
242
|
reuse or hybrid retrieval.
|
|
225
243
|
|
|
226
|
-
|
|
244
|
+
Explicit add deduplicates only canonical text in the same scope and never calls
|
|
245
|
+
an LLM. Different texts are saved regardless of similarity. Update replaces only
|
|
246
|
+
the authorized exact ID. For automatic Hermes extraction,
|
|
247
|
+
similarity is a candidate-selection signal, not proof that two facts are identical.
|
|
227
248
|
`SAME` skips, `SUPERSEDES` updates, `CONFLICT` creates a linked record, and
|
|
228
249
|
`UNRELATED` adds a record. Migration uses source IDs instead of semantic merging.
|
|
229
250
|
|
|
@@ -305,7 +326,7 @@ default profile scope uses an agent (`hermes`). Pass `--agent hermes` to
|
|
|
305
326
|
target it, or `--all-agents` to delete every agent scope of the user. Without
|
|
306
327
|
`--all-agents`, the result reports `other_agent_scopes` when other agent
|
|
307
328
|
scopes still hold memories. A literal `*` is an ordinary agent id, not a
|
|
308
|
-
wildcard. `--dry-run` previews the per-scope counts
|
|
329
|
+
wildcard. `--dry-run` previews the per-scope counts.
|
|
309
330
|
`--confirm` authorizes deletion. An interrupted deletion fails closed: other
|
|
310
331
|
commands and provider startup return `scope_delete_recovery_required` until
|
|
311
332
|
`delete-all --confirm` resumes it. Deletion intents are versioned and explicit.
|
|
@@ -313,8 +334,8 @@ A pre-versioned intent cannot show whether `*` is a single agent or all agents,
|
|
|
313
334
|
so the plugin refuses it (`scope_delete_legacy_intent`). Resolve it with
|
|
314
335
|
`--resolve-legacy single_agent`, or with `--resolve-legacy all_agents` when the
|
|
315
336
|
record's agent is `*` or absent. Turns admitted after the deletion can still
|
|
316
|
-
add new memories
|
|
317
|
-
writes and unprocessed turn events
|
|
337
|
+
add new memories. Everything admitted before it, including stored memories, prepared
|
|
338
|
+
writes and unprocessed turn events, is removed or invalidated. `list`,
|
|
318
339
|
`export` and `delete-all` do not require the embedding service.
|
|
319
340
|
|
|
320
341
|
## Data flow and privacy
|
|
@@ -325,10 +346,10 @@ carries and where it can go:
|
|
|
325
346
|
| Component | Carries | Destination |
|
|
326
347
|
| --- | --- | --- |
|
|
327
348
|
| Chat / agent turns | Your conversation | The model configured for the Hermes session |
|
|
328
|
-
| Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model
|
|
329
|
-
| Relation checks | The candidate memory and the similar stored memory | Same routing as extraction
|
|
349
|
+
| Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model. With a cloud model, turn text reaches that cloud |
|
|
350
|
+
| Relation checks | The candidate memory and the similar stored memory | Same routing as extraction. It runs only when similarity selects a review candidate |
|
|
330
351
|
| Embeddings (`embedding`) | Memory and search text | Ollama by default, or your OpenAI-compatible endpoint |
|
|
331
|
-
| Storage (`qdrant`) | Memory payloads and provenance
|
|
352
|
+
| Storage (`qdrant`) | Memory payloads and provenance. The ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
|
|
332
353
|
|
|
333
354
|
Three common deployments:
|
|
334
355
|
|
|
@@ -483,7 +504,7 @@ contains `memories` objects with `id`/`text`, and `queries` with `query`/`releva
|
|
|
483
504
|
The default fixture now contains 24 labeled queries, including Chinese, English,
|
|
484
505
|
mixed-language and hard-negative cases. The
|
|
485
506
|
[original 12-query recording](docs/retrieval-baseline.json) is a historical
|
|
486
|
-
synthetic regression baseline
|
|
507
|
+
synthetic regression baseline. Rerun the current fixture for current measurements.
|
|
487
508
|
These fixtures do not establish production recall quality. See the [recovery benchmark](docs/recovery-benchmark.md)
|
|
488
509
|
for a separate synthetic replay and retained-storage measurement.
|
|
489
510
|
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: hermes-plugin-qdrant-memory
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Durable Qdrant memory for Hermes, standalone CLI and local MCP
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
Project-URL: Homepage, https://github.com/cnkang/hermes-plugin-qdrant-memory
|
|
7
|
-
Project-URL: Repository, https://github.com/cnkang/hermes-plugin-qdrant-memory
|
|
8
|
-
Project-URL: Issues, https://github.com/cnkang/hermes-plugin-qdrant-memory/issues
|
|
9
|
-
Project-URL: Changelog, https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md
|
|
10
|
-
Requires-Python: >=3.11
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
|
-
Requires-Dist: qdrant-client<2,>=1.15
|
|
14
|
-
Requires-Dist: httpx<1,>=0.28
|
|
15
|
-
Requires-Dist: portalocker<4,>=3.2
|
|
16
|
-
Provides-Extra: mcp
|
|
17
|
-
Requires-Dist: mcp<2,>=1.30; extra == "mcp"
|
|
18
|
-
Dynamic: license-file
|
|
19
|
-
|
|
20
1
|
# Qdrant Memory for Hermes
|
|
21
2
|
|
|
22
3
|
[](https://github.com/cnkang/hermes-plugin-qdrant-memory/actions/workflows/ci.yml)
|
|
@@ -26,12 +7,11 @@ Dynamic: license-file
|
|
|
26
7
|
[](https://www.python.org/downloads/)
|
|
27
8
|
[](LICENSE)
|
|
28
9
|
|
|
29
|
-
> **
|
|
30
|
-
>
|
|
31
|
-
>
|
|
32
|
-
>
|
|
33
|
-
>
|
|
34
|
-
> current certification.
|
|
10
|
+
> **Runtime 0.2.1** provides the standalone CLI/MCP and fixes explicit writes of
|
|
11
|
+
> distinct, highly similar texts. MCP distribution 0.1.1 pins Runtime 0.2.1.
|
|
12
|
+
> The Hermes Plugin Catalog has its own reviewed immutable pin. A PyPI release
|
|
13
|
+
> does not upgrade it. One writer per destination remains required.
|
|
14
|
+
> See [release notes](docs/releases/0.2.1.md) and [validation](docs/validation.md).
|
|
35
15
|
|
|
36
16
|
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
37
17
|
|
|
@@ -45,6 +25,16 @@ as a community-maintained plugin.
|
|
|
45
25
|
|
|
46
26
|
## How it works
|
|
47
27
|
|
|
28
|
+
For non-Hermes users, install in a private Python 3.11+
|
|
29
|
+
environment: `pip install 'hermes-plugin-qdrant-memory[mcp]==0.2.1'` (omit `[mcp]`
|
|
30
|
+
for CLI only). `qdrant-memory` is the CLI and `mcp` is its server subcommand.
|
|
31
|
+
Standalone does not require Hermes. See [setup and quick start](docs/standalone.md).
|
|
32
|
+
MCP-focused agents may instead use the [metadata-only distribution](https://github.com/cnkang/qdrant-memory-mcp).
|
|
33
|
+
Public releases: [Runtime on PyPI](https://pypi.org/project/hermes-plugin-qdrant-memory/)
|
|
34
|
+
and [MCP distribution on PyPI](https://pypi.org/project/qdrant-memory-mcp/).
|
|
35
|
+
For native Hermes use its official plugin installation mechanism below. Before
|
|
36
|
+
an authorized upgrade, verify the same profile, collection and embedding pipeline.
|
|
37
|
+
|
|
48
38
|
```mermaid
|
|
49
39
|
flowchart LR
|
|
50
40
|
turn["Hermes turn"] --> admit["Provider callback"]
|
|
@@ -66,7 +56,7 @@ flowchart LR
|
|
|
66
56
|
- A fully local setup works: embedded Qdrant and local Ollama keep the data
|
|
67
57
|
on your machine.
|
|
68
58
|
- Data locality follows the destination. Extraction uses the LLM configured
|
|
69
|
-
in Hermes, and embedding uses the configured embedding service
|
|
59
|
+
in Hermes, and embedding uses the configured embedding service. Either can
|
|
70
60
|
be remote. See [Data flow and privacy](#data-flow-and-privacy).
|
|
71
61
|
- Not supported in this preview: multi-writer Server/Cloud, hybrid search and
|
|
72
62
|
a web UI.
|
|
@@ -75,9 +65,9 @@ flowchart LR
|
|
|
75
65
|
|
|
76
66
|
The plugin primarily provides Hermes Agent's durable memory integration. As an
|
|
77
67
|
additional capability, its shared backend can serve other agents through the
|
|
78
|
-
Runtime
|
|
79
|
-
interface
|
|
80
|
-
memory operations and maintenance/migration
|
|
68
|
+
Runtime's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
|
|
69
|
+
interface. Standalone use does not require Hermes. These interfaces support explicit scoped
|
|
70
|
+
memory operations and maintenance/migration. Automatic extraction and prefetch
|
|
81
71
|
remain Hermes lifecycle features. See [standalone setup](docs/standalone.md) for
|
|
82
72
|
configuration and boundaries, and the agent-neutral
|
|
83
73
|
[qdrant-memory skill](https://github.com/cnkang/skills/tree/main/qdrant-memory)
|
|
@@ -85,19 +75,20 @@ for multi-agent installation and usage guidance.
|
|
|
85
75
|
|
|
86
76
|
The optional MCP server exposes four scoped tools: `qdrant_memory_search`,
|
|
87
77
|
`qdrant_memory_add`, `qdrant_memory_update` and `qdrant_memory_delete`. Compatible
|
|
88
|
-
clients launch it as a local stdio process and discover/call those tools
|
|
89
|
-
|
|
78
|
+
clients launch it as a local stdio process and discover/call those tools.
|
|
79
|
+
Maintenance and migration use the operator CLI. See the
|
|
90
80
|
[MCP capabilities and connection guide](docs/mcp.md) for setup, shared-memory
|
|
91
|
-
boundaries and the verified agent matrix.
|
|
92
|
-
|
|
81
|
+
boundaries and the verified agent matrix. Runtime 0.2.0 published these interfaces.
|
|
82
|
+
The published v0.1.0 plugin release does not include the MCP entrypoint.
|
|
93
83
|
|
|
94
84
|
## See it in action
|
|
95
85
|
|
|
96
86
|

|
|
97
87
|
|
|
98
|
-
The recording shows the full flow on a clean profile
|
|
99
|
-
official Hermes Plugin Catalog,
|
|
100
|
-
extraction
|
|
88
|
+
The recording shows the full flow on a clean profile. It installs from the
|
|
89
|
+
official Hermes Plugin Catalog, teaches a preference, and waits for the
|
|
90
|
+
background extraction. Then it recalls the preference in a new session and
|
|
91
|
+
cleans up the demo scope. All steps
|
|
101
92
|
run in embedded mode. `scripts/demo.sh` reproduces the same walkthrough with
|
|
102
93
|
the pinned release.
|
|
103
94
|
|
|
@@ -128,14 +119,17 @@ The install resolves the catalog's reviewed commit and prints the entry's declar
|
|
|
128
119
|
tools, requirements and known issues before it clones anything.
|
|
129
120
|
`hermes plugins enable` prepares the declared dependencies through Hermes PM.
|
|
130
121
|
|
|
131
|
-
**Version-locked: install the
|
|
122
|
+
**Version-locked: install the verified Runtime 0.2.1 source through the Hermes CLI.**
|
|
123
|
+
|
|
124
|
+
This immutable source revision includes the explicit-write fix. It does not
|
|
125
|
+
depend on a PyPI release or a catalog update.
|
|
132
126
|
|
|
133
127
|
```bash
|
|
134
128
|
export HERMES_HOME="/absolute/path/to/your/hermes-profile"
|
|
135
129
|
ollama pull qwen3-embedding:4b
|
|
136
130
|
# Start Ollama if it is not running. Keep its service available.
|
|
137
131
|
hermes plugins install https://github.com/cnkang/hermes-plugin-qdrant-memory \
|
|
138
|
-
--ref
|
|
132
|
+
--ref 8929693fcccd1efa91bfa062145e35d2369ca9cc # verified Runtime 0.2.1 source
|
|
139
133
|
hermes plugins enable qdrant-memory
|
|
140
134
|
hermes memory setup
|
|
141
135
|
hermes config set memory.provider qdrant-memory
|
|
@@ -144,12 +138,15 @@ hermes qdrant-memory init
|
|
|
144
138
|
|
|
145
139
|
For this version-locked form, `hermes plugins install` checks out the given ref, and
|
|
146
140
|
`hermes plugins enable` prepares the declared dependencies through Hermes PM.
|
|
147
|
-
`--ref` takes a full 40-character commit SHA
|
|
141
|
+
`--ref` takes a full 40-character commit SHA. The
|
|
148
142
|
[releases page](https://github.com/cnkang/hermes-plugin-qdrant-memory/releases)
|
|
149
143
|
lists the release commits. Do not pip-install into a managed Hermes
|
|
150
144
|
environment. Omit `--ref` to track `main`, the development line.
|
|
151
145
|
|
|
152
|
-
**
|
|
146
|
+
**Historical release: clone a published tag manually.**
|
|
147
|
+
|
|
148
|
+
The v0.1.0 tag below predates the standalone interfaces and the explicit-write
|
|
149
|
+
fix. Use the pinned source above for Runtime 0.2.1.
|
|
153
150
|
|
|
154
151
|
```bash
|
|
155
152
|
export HERMES_HOME="/absolute/path/to/your/hermes-profile"
|
|
@@ -178,7 +175,7 @@ import and registration. For development installs, see
|
|
|
178
175
|
[development setup](docs/development.md).
|
|
179
176
|
|
|
180
177
|
The release notes, supported Hermes/Python versions and the current
|
|
181
|
-
limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md)
|
|
178
|
+
limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md).
|
|
182
179
|
`main` stays the development line.
|
|
183
180
|
|
|
184
181
|
For local development, symlink the repository into a disposable profile:
|
|
@@ -223,7 +220,10 @@ mappings) stays in the local Hermes home. Back up that home together with your
|
|
|
223
220
|
Qdrant data. Migration from Mem0 is one-way, and version 0.1 has no vector
|
|
224
221
|
reuse or hybrid retrieval.
|
|
225
222
|
|
|
226
|
-
|
|
223
|
+
Explicit add deduplicates only canonical text in the same scope and never calls
|
|
224
|
+
an LLM. Different texts are saved regardless of similarity. Update replaces only
|
|
225
|
+
the authorized exact ID. For automatic Hermes extraction,
|
|
226
|
+
similarity is a candidate-selection signal, not proof that two facts are identical.
|
|
227
227
|
`SAME` skips, `SUPERSEDES` updates, `CONFLICT` creates a linked record, and
|
|
228
228
|
`UNRELATED` adds a record. Migration uses source IDs instead of semantic merging.
|
|
229
229
|
|
|
@@ -305,7 +305,7 @@ default profile scope uses an agent (`hermes`). Pass `--agent hermes` to
|
|
|
305
305
|
target it, or `--all-agents` to delete every agent scope of the user. Without
|
|
306
306
|
`--all-agents`, the result reports `other_agent_scopes` when other agent
|
|
307
307
|
scopes still hold memories. A literal `*` is an ordinary agent id, not a
|
|
308
|
-
wildcard. `--dry-run` previews the per-scope counts
|
|
308
|
+
wildcard. `--dry-run` previews the per-scope counts.
|
|
309
309
|
`--confirm` authorizes deletion. An interrupted deletion fails closed: other
|
|
310
310
|
commands and provider startup return `scope_delete_recovery_required` until
|
|
311
311
|
`delete-all --confirm` resumes it. Deletion intents are versioned and explicit.
|
|
@@ -313,8 +313,8 @@ A pre-versioned intent cannot show whether `*` is a single agent or all agents,
|
|
|
313
313
|
so the plugin refuses it (`scope_delete_legacy_intent`). Resolve it with
|
|
314
314
|
`--resolve-legacy single_agent`, or with `--resolve-legacy all_agents` when the
|
|
315
315
|
record's agent is `*` or absent. Turns admitted after the deletion can still
|
|
316
|
-
add new memories
|
|
317
|
-
writes and unprocessed turn events
|
|
316
|
+
add new memories. Everything admitted before it, including stored memories, prepared
|
|
317
|
+
writes and unprocessed turn events, is removed or invalidated. `list`,
|
|
318
318
|
`export` and `delete-all` do not require the embedding service.
|
|
319
319
|
|
|
320
320
|
## Data flow and privacy
|
|
@@ -325,10 +325,10 @@ carries and where it can go:
|
|
|
325
325
|
| Component | Carries | Destination |
|
|
326
326
|
| --- | --- | --- |
|
|
327
327
|
| Chat / agent turns | Your conversation | The model configured for the Hermes session |
|
|
328
|
-
| Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model
|
|
329
|
-
| Relation checks | The candidate memory and the similar stored memory | Same routing as extraction
|
|
328
|
+
| Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model. With a cloud model, turn text reaches that cloud |
|
|
329
|
+
| Relation checks | The candidate memory and the similar stored memory | Same routing as extraction. It runs only when similarity selects a review candidate |
|
|
330
330
|
| Embeddings (`embedding`) | Memory and search text | Ollama by default, or your OpenAI-compatible endpoint |
|
|
331
|
-
| Storage (`qdrant`) | Memory payloads and provenance
|
|
331
|
+
| Storage (`qdrant`) | Memory payloads and provenance. The ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
|
|
332
332
|
|
|
333
333
|
Three common deployments:
|
|
334
334
|
|
|
@@ -483,7 +483,7 @@ contains `memories` objects with `id`/`text`, and `queries` with `query`/`releva
|
|
|
483
483
|
The default fixture now contains 24 labeled queries, including Chinese, English,
|
|
484
484
|
mixed-language and hard-negative cases. The
|
|
485
485
|
[original 12-query recording](docs/retrieval-baseline.json) is a historical
|
|
486
|
-
synthetic regression baseline
|
|
486
|
+
synthetic regression baseline. Rerun the current fixture for current measurements.
|
|
487
487
|
These fixtures do not establish production recall quality. See the [recovery benchmark](docs/recovery-benchmark.md)
|
|
488
488
|
for a separate synthetic replay and retained-storage measurement.
|
|
489
489
|
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hermes-plugin-qdrant-memory
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Durable Qdrant memory for Hermes, standalone CLI and local MCP
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/cnkang/hermes-plugin-qdrant-memory
|
|
7
|
+
Project-URL: Repository, https://github.com/cnkang/hermes-plugin-qdrant-memory
|
|
8
|
+
Project-URL: Issues, https://github.com/cnkang/hermes-plugin-qdrant-memory/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: MCP, https://github.com/cnkang/qdrant-memory-mcp
|
|
11
|
+
Project-URL: Skill, https://github.com/cnkang/skills/tree/main/qdrant-memory
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: qdrant-client<2,>=1.15
|
|
16
|
+
Requires-Dist: httpx<1,>=0.28
|
|
17
|
+
Requires-Dist: portalocker<4,>=3.2
|
|
18
|
+
Provides-Extra: mcp
|
|
19
|
+
Requires-Dist: mcp<2,>=1.30; extra == "mcp"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
1
22
|
# Qdrant Memory for Hermes
|
|
2
23
|
|
|
3
24
|
[](https://github.com/cnkang/hermes-plugin-qdrant-memory/actions/workflows/ci.yml)
|
|
@@ -7,12 +28,11 @@
|
|
|
7
28
|
[](https://www.python.org/downloads/)
|
|
8
29
|
[](LICENSE)
|
|
9
30
|
|
|
10
|
-
> **
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
> current certification.
|
|
31
|
+
> **Runtime 0.2.1** provides the standalone CLI/MCP and fixes explicit writes of
|
|
32
|
+
> distinct, highly similar texts. MCP distribution 0.1.1 pins Runtime 0.2.1.
|
|
33
|
+
> The Hermes Plugin Catalog has its own reviewed immutable pin. A PyPI release
|
|
34
|
+
> does not upgrade it. One writer per destination remains required.
|
|
35
|
+
> See [release notes](docs/releases/0.2.1.md) and [validation](docs/validation.md).
|
|
16
36
|
|
|
17
37
|
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
18
38
|
|
|
@@ -26,6 +46,16 @@ as a community-maintained plugin.
|
|
|
26
46
|
|
|
27
47
|
## How it works
|
|
28
48
|
|
|
49
|
+
For non-Hermes users, install in a private Python 3.11+
|
|
50
|
+
environment: `pip install 'hermes-plugin-qdrant-memory[mcp]==0.2.1'` (omit `[mcp]`
|
|
51
|
+
for CLI only). `qdrant-memory` is the CLI and `mcp` is its server subcommand.
|
|
52
|
+
Standalone does not require Hermes. See [setup and quick start](docs/standalone.md).
|
|
53
|
+
MCP-focused agents may instead use the [metadata-only distribution](https://github.com/cnkang/qdrant-memory-mcp).
|
|
54
|
+
Public releases: [Runtime on PyPI](https://pypi.org/project/hermes-plugin-qdrant-memory/)
|
|
55
|
+
and [MCP distribution on PyPI](https://pypi.org/project/qdrant-memory-mcp/).
|
|
56
|
+
For native Hermes use its official plugin installation mechanism below. Before
|
|
57
|
+
an authorized upgrade, verify the same profile, collection and embedding pipeline.
|
|
58
|
+
|
|
29
59
|
```mermaid
|
|
30
60
|
flowchart LR
|
|
31
61
|
turn["Hermes turn"] --> admit["Provider callback"]
|
|
@@ -47,7 +77,7 @@ flowchart LR
|
|
|
47
77
|
- A fully local setup works: embedded Qdrant and local Ollama keep the data
|
|
48
78
|
on your machine.
|
|
49
79
|
- Data locality follows the destination. Extraction uses the LLM configured
|
|
50
|
-
in Hermes, and embedding uses the configured embedding service
|
|
80
|
+
in Hermes, and embedding uses the configured embedding service. Either can
|
|
51
81
|
be remote. See [Data flow and privacy](#data-flow-and-privacy).
|
|
52
82
|
- Not supported in this preview: multi-writer Server/Cloud, hybrid search and
|
|
53
83
|
a web UI.
|
|
@@ -56,9 +86,9 @@ flowchart LR
|
|
|
56
86
|
|
|
57
87
|
The plugin primarily provides Hermes Agent's durable memory integration. As an
|
|
58
88
|
additional capability, its shared backend can serve other agents through the
|
|
59
|
-
Runtime
|
|
60
|
-
interface
|
|
61
|
-
memory operations and maintenance/migration
|
|
89
|
+
Runtime's `qdrant-memory --home PATH` JSON CLI and optional local stdio MCP
|
|
90
|
+
interface. Standalone use does not require Hermes. These interfaces support explicit scoped
|
|
91
|
+
memory operations and maintenance/migration. Automatic extraction and prefetch
|
|
62
92
|
remain Hermes lifecycle features. See [standalone setup](docs/standalone.md) for
|
|
63
93
|
configuration and boundaries, and the agent-neutral
|
|
64
94
|
[qdrant-memory skill](https://github.com/cnkang/skills/tree/main/qdrant-memory)
|
|
@@ -66,19 +96,20 @@ for multi-agent installation and usage guidance.
|
|
|
66
96
|
|
|
67
97
|
The optional MCP server exposes four scoped tools: `qdrant_memory_search`,
|
|
68
98
|
`qdrant_memory_add`, `qdrant_memory_update` and `qdrant_memory_delete`. Compatible
|
|
69
|
-
clients launch it as a local stdio process and discover/call those tools
|
|
70
|
-
|
|
99
|
+
clients launch it as a local stdio process and discover/call those tools.
|
|
100
|
+
Maintenance and migration use the operator CLI. See the
|
|
71
101
|
[MCP capabilities and connection guide](docs/mcp.md) for setup, shared-memory
|
|
72
|
-
boundaries and the verified agent matrix.
|
|
73
|
-
|
|
102
|
+
boundaries and the verified agent matrix. Runtime 0.2.0 published these interfaces.
|
|
103
|
+
The published v0.1.0 plugin release does not include the MCP entrypoint.
|
|
74
104
|
|
|
75
105
|
## See it in action
|
|
76
106
|
|
|
77
107
|

|
|
78
108
|
|
|
79
|
-
The recording shows the full flow on a clean profile
|
|
80
|
-
official Hermes Plugin Catalog,
|
|
81
|
-
extraction
|
|
109
|
+
The recording shows the full flow on a clean profile. It installs from the
|
|
110
|
+
official Hermes Plugin Catalog, teaches a preference, and waits for the
|
|
111
|
+
background extraction. Then it recalls the preference in a new session and
|
|
112
|
+
cleans up the demo scope. All steps
|
|
82
113
|
run in embedded mode. `scripts/demo.sh` reproduces the same walkthrough with
|
|
83
114
|
the pinned release.
|
|
84
115
|
|
|
@@ -109,14 +140,17 @@ The install resolves the catalog's reviewed commit and prints the entry's declar
|
|
|
109
140
|
tools, requirements and known issues before it clones anything.
|
|
110
141
|
`hermes plugins enable` prepares the declared dependencies through Hermes PM.
|
|
111
142
|
|
|
112
|
-
**Version-locked: install the
|
|
143
|
+
**Version-locked: install the verified Runtime 0.2.1 source through the Hermes CLI.**
|
|
144
|
+
|
|
145
|
+
This immutable source revision includes the explicit-write fix. It does not
|
|
146
|
+
depend on a PyPI release or a catalog update.
|
|
113
147
|
|
|
114
148
|
```bash
|
|
115
149
|
export HERMES_HOME="/absolute/path/to/your/hermes-profile"
|
|
116
150
|
ollama pull qwen3-embedding:4b
|
|
117
151
|
# Start Ollama if it is not running. Keep its service available.
|
|
118
152
|
hermes plugins install https://github.com/cnkang/hermes-plugin-qdrant-memory \
|
|
119
|
-
--ref
|
|
153
|
+
--ref 8929693fcccd1efa91bfa062145e35d2369ca9cc # verified Runtime 0.2.1 source
|
|
120
154
|
hermes plugins enable qdrant-memory
|
|
121
155
|
hermes memory setup
|
|
122
156
|
hermes config set memory.provider qdrant-memory
|
|
@@ -125,12 +159,15 @@ hermes qdrant-memory init
|
|
|
125
159
|
|
|
126
160
|
For this version-locked form, `hermes plugins install` checks out the given ref, and
|
|
127
161
|
`hermes plugins enable` prepares the declared dependencies through Hermes PM.
|
|
128
|
-
`--ref` takes a full 40-character commit SHA
|
|
162
|
+
`--ref` takes a full 40-character commit SHA. The
|
|
129
163
|
[releases page](https://github.com/cnkang/hermes-plugin-qdrant-memory/releases)
|
|
130
164
|
lists the release commits. Do not pip-install into a managed Hermes
|
|
131
165
|
environment. Omit `--ref` to track `main`, the development line.
|
|
132
166
|
|
|
133
|
-
**
|
|
167
|
+
**Historical release: clone a published tag manually.**
|
|
168
|
+
|
|
169
|
+
The v0.1.0 tag below predates the standalone interfaces and the explicit-write
|
|
170
|
+
fix. Use the pinned source above for Runtime 0.2.1.
|
|
134
171
|
|
|
135
172
|
```bash
|
|
136
173
|
export HERMES_HOME="/absolute/path/to/your/hermes-profile"
|
|
@@ -159,7 +196,7 @@ import and registration. For development installs, see
|
|
|
159
196
|
[development setup](docs/development.md).
|
|
160
197
|
|
|
161
198
|
The release notes, supported Hermes/Python versions and the current
|
|
162
|
-
limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md)
|
|
199
|
+
limitations live in [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md).
|
|
163
200
|
`main` stays the development line.
|
|
164
201
|
|
|
165
202
|
For local development, symlink the repository into a disposable profile:
|
|
@@ -204,7 +241,10 @@ mappings) stays in the local Hermes home. Back up that home together with your
|
|
|
204
241
|
Qdrant data. Migration from Mem0 is one-way, and version 0.1 has no vector
|
|
205
242
|
reuse or hybrid retrieval.
|
|
206
243
|
|
|
207
|
-
|
|
244
|
+
Explicit add deduplicates only canonical text in the same scope and never calls
|
|
245
|
+
an LLM. Different texts are saved regardless of similarity. Update replaces only
|
|
246
|
+
the authorized exact ID. For automatic Hermes extraction,
|
|
247
|
+
similarity is a candidate-selection signal, not proof that two facts are identical.
|
|
208
248
|
`SAME` skips, `SUPERSEDES` updates, `CONFLICT` creates a linked record, and
|
|
209
249
|
`UNRELATED` adds a record. Migration uses source IDs instead of semantic merging.
|
|
210
250
|
|
|
@@ -286,7 +326,7 @@ default profile scope uses an agent (`hermes`). Pass `--agent hermes` to
|
|
|
286
326
|
target it, or `--all-agents` to delete every agent scope of the user. Without
|
|
287
327
|
`--all-agents`, the result reports `other_agent_scopes` when other agent
|
|
288
328
|
scopes still hold memories. A literal `*` is an ordinary agent id, not a
|
|
289
|
-
wildcard. `--dry-run` previews the per-scope counts
|
|
329
|
+
wildcard. `--dry-run` previews the per-scope counts.
|
|
290
330
|
`--confirm` authorizes deletion. An interrupted deletion fails closed: other
|
|
291
331
|
commands and provider startup return `scope_delete_recovery_required` until
|
|
292
332
|
`delete-all --confirm` resumes it. Deletion intents are versioned and explicit.
|
|
@@ -294,8 +334,8 @@ A pre-versioned intent cannot show whether `*` is a single agent or all agents,
|
|
|
294
334
|
so the plugin refuses it (`scope_delete_legacy_intent`). Resolve it with
|
|
295
335
|
`--resolve-legacy single_agent`, or with `--resolve-legacy all_agents` when the
|
|
296
336
|
record's agent is `*` or absent. Turns admitted after the deletion can still
|
|
297
|
-
add new memories
|
|
298
|
-
writes and unprocessed turn events
|
|
337
|
+
add new memories. Everything admitted before it, including stored memories, prepared
|
|
338
|
+
writes and unprocessed turn events, is removed or invalidated. `list`,
|
|
299
339
|
`export` and `delete-all` do not require the embedding service.
|
|
300
340
|
|
|
301
341
|
## Data flow and privacy
|
|
@@ -306,10 +346,10 @@ carries and where it can go:
|
|
|
306
346
|
| Component | Carries | Destination |
|
|
307
347
|
| --- | --- | --- |
|
|
308
348
|
| Chat / agent turns | Your conversation | The model configured for the Hermes session |
|
|
309
|
-
| Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model
|
|
310
|
-
| Relation checks | The candidate memory and the similar stored memory | Same routing as extraction
|
|
349
|
+
| Memory extraction (`llm.mode: inherit`) | The turn's user and assistant text | The same session model. With a cloud model, turn text reaches that cloud |
|
|
350
|
+
| Relation checks | The candidate memory and the similar stored memory | Same routing as extraction. It runs only when similarity selects a review candidate |
|
|
311
351
|
| Embeddings (`embedding`) | Memory and search text | Ollama by default, or your OpenAI-compatible endpoint |
|
|
312
|
-
| Storage (`qdrant`) | Memory payloads and provenance
|
|
352
|
+
| Storage (`qdrant`) | Memory payloads and provenance. The ledger stays local | Embedded local path, self-hosted server, or Qdrant Cloud |
|
|
313
353
|
|
|
314
354
|
Three common deployments:
|
|
315
355
|
|
|
@@ -464,7 +504,7 @@ contains `memories` objects with `id`/`text`, and `queries` with `query`/`releva
|
|
|
464
504
|
The default fixture now contains 24 labeled queries, including Chinese, English,
|
|
465
505
|
mixed-language and hard-negative cases. The
|
|
466
506
|
[original 12-query recording](docs/retrieval-baseline.json) is a historical
|
|
467
|
-
synthetic regression baseline
|
|
507
|
+
synthetic regression baseline. Rerun the current fixture for current measurements.
|
|
468
508
|
These fixtures do not establish production recall quality. See the [recovery benchmark](docs/recovery-benchmark.md)
|
|
469
509
|
for a separate synthetic replay and retained-storage measurement.
|
|
470
510
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hermes-plugin-qdrant-memory"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Durable Qdrant memory for Hermes, standalone CLI and local MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -23,6 +23,8 @@ Homepage = "https://github.com/cnkang/hermes-plugin-qdrant-memory"
|
|
|
23
23
|
Repository = "https://github.com/cnkang/hermes-plugin-qdrant-memory"
|
|
24
24
|
Issues = "https://github.com/cnkang/hermes-plugin-qdrant-memory/issues"
|
|
25
25
|
Changelog = "https://github.com/cnkang/hermes-plugin-qdrant-memory/blob/main/CHANGELOG.md"
|
|
26
|
+
MCP = "https://github.com/cnkang/qdrant-memory-mcp"
|
|
27
|
+
Skill = "https://github.com/cnkang/skills/tree/main/qdrant-memory"
|
|
26
28
|
|
|
27
29
|
[project.entry-points."hermes_agent.memory_providers"]
|
|
28
30
|
qdrant-memory = "qdrant_memory:register"
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/dedupe.py
RENAMED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
from .models import content_hash
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
def decide_explicit(text, scope, store):
|
|
7
|
+
"""Deduplicate authorized explicit writes only by canonical hash in scope."""
|
|
8
|
+
exact = store.lookup_hash(scope, content_hash(text))
|
|
9
|
+
if exact:
|
|
10
|
+
return "SKIP", str(exact.id), {}
|
|
11
|
+
return "ADD", None, {}
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
def decide(text, scope, store, extractor, cfg):
|
|
7
15
|
"""Return ADD, UPDATE or SKIP after scoped hash and factual relation checks.
|
|
8
16
|
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/runtime.py
RENAMED
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
from itertools import islice
|
|
6
6
|
from threading import RLock
|
|
7
7
|
|
|
8
|
-
from .dedupe import decide
|
|
8
|
+
from .dedupe import decide, decide_explicit
|
|
9
9
|
from .models import Scope, content_hash, enforce_limits, now, payload, point_id
|
|
10
10
|
from .progress import report
|
|
11
11
|
from .retry import run_with_retry
|
|
@@ -163,11 +163,9 @@ class Runtime:
|
|
|
163
163
|
return failures
|
|
164
164
|
|
|
165
165
|
def add(self, text, scope, source="manual_tool", session_id="", **fields):
|
|
166
|
-
"""
|
|
166
|
+
"""Save an explicit scoped fact with deterministic exact-text deduplication."""
|
|
167
167
|
with self.lock:
|
|
168
|
-
action, identifier, relation =
|
|
169
|
-
text, scope, self.store, self.extractor, self.cfg["dedupe"]
|
|
170
|
-
)
|
|
168
|
+
action, identifier, relation = decide_explicit(text, scope, self.store)
|
|
171
169
|
self.decisions[action] += 1
|
|
172
170
|
self.ledger.increment(action)
|
|
173
171
|
if action == "SKIP":
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Explicit intent and automatic factual adjudication remain separate policies."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from qdrant_memory.models import Scope
|
|
8
|
+
from qdrant_memory.tools import dispatch
|
|
9
|
+
|
|
10
|
+
from .helpers import LLM, runtime
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.parametrize("score", [0.92, 0.97, 1.0])
|
|
14
|
+
@pytest.mark.parametrize("with_extractor", [False, True])
|
|
15
|
+
def test_explicit_add_keeps_distinct_similar_facts(tmp_path, score, with_extractor):
|
|
16
|
+
"""Similarity cannot discard explicit intent, even when an LLM reports SAME."""
|
|
17
|
+
llm = LLM("SAME")
|
|
18
|
+
rt = runtime(tmp_path, llm=llm)
|
|
19
|
+
if not with_extractor:
|
|
20
|
+
rt.extractor = None
|
|
21
|
+
scope = Scope("alice", "hermes")
|
|
22
|
+
rt.store.embedder.embed_query = lambda text: (
|
|
23
|
+
[1.0, 0.0, 0.0] if "preferred" in text else [score, math.sqrt(1 - score**2), 0.0]
|
|
24
|
+
)
|
|
25
|
+
try:
|
|
26
|
+
first = dispatch(rt, "qdrant_memory_add", {"text": "cats preferred"}, scope, "s")
|
|
27
|
+
assert rt.store.search("cats avoided", scope)[0].score == pytest.approx(score)
|
|
28
|
+
second = dispatch(rt, "qdrant_memory_add", {"text": "cats avoided"}, scope, "s")
|
|
29
|
+
assert first["action"] == second["action"] == "ADD"
|
|
30
|
+
assert first["id"] != second["id"]
|
|
31
|
+
duplicate = rt.add(" cats preferred ", scope)
|
|
32
|
+
assert duplicate == {"action": "SKIP", "id": first["id"]}
|
|
33
|
+
for other in (Scope("bob", "hermes"), Scope("alice", "other")):
|
|
34
|
+
assert rt.add("cats preferred", other)["id"] != first["id"]
|
|
35
|
+
assert rt.store.count(other) == 1
|
|
36
|
+
assert rt.store.count(scope) == 2
|
|
37
|
+
assert not llm.calls
|
|
38
|
+
finally:
|
|
39
|
+
rt.store.close()
|
|
40
|
+
rt.ledger.close()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize(
|
|
44
|
+
"relation,count,texts",
|
|
45
|
+
[
|
|
46
|
+
("SAME", 1, {"cats preferred"}),
|
|
47
|
+
("SUPERSEDES", 1, {"cats avoided"}),
|
|
48
|
+
("CONFLICT", 2, {"cats preferred", "cats avoided"}),
|
|
49
|
+
("UNRELATED", 2, {"cats preferred", "cats avoided"}),
|
|
50
|
+
],
|
|
51
|
+
)
|
|
52
|
+
def test_automatic_events_retain_factual_relations(tmp_path, relation, count, texts):
|
|
53
|
+
"""Real extraction and durable event processing still adjudicate similar facts."""
|
|
54
|
+
llm = LLM(relation)
|
|
55
|
+
rt = runtime(tmp_path, llm=llm)
|
|
56
|
+
scope = Scope("alice", "hermes")
|
|
57
|
+
try:
|
|
58
|
+
first = rt.add("cats preferred", scope)
|
|
59
|
+
key = rt.ledger.enqueue_event(
|
|
60
|
+
{
|
|
61
|
+
"kind": "turn",
|
|
62
|
+
"scope": scope.as_dict(),
|
|
63
|
+
"source": "conversation",
|
|
64
|
+
"session_id": "s",
|
|
65
|
+
"user": "cats avoided",
|
|
66
|
+
"assistant": "ack",
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
rt.process_event(key)
|
|
70
|
+
assert rt.store.count(scope) == count
|
|
71
|
+
hits = rt.store.search("cats", scope)
|
|
72
|
+
assert {h.payload["text"] for h in hits} == texts
|
|
73
|
+
if relation == "CONFLICT":
|
|
74
|
+
conflict = next(h for h in hits if h.payload["text"] == "cats avoided")
|
|
75
|
+
assert conflict.payload["metadata"]["conflicts_with"] == first["id"]
|
|
76
|
+
assert any(c["purpose"] == "qdrant-memory.relation" for c in llm.calls)
|
|
77
|
+
assert rt.ledger.row("events", key)["status"] == "COMMITTED"
|
|
78
|
+
finally:
|
|
79
|
+
rt.store.close()
|
|
80
|
+
rt.ledger.close()
|
|
@@ -17,6 +17,13 @@ def test_manifests_declare_v2_metadata_and_match_runtime_dependencies():
|
|
|
17
17
|
|
|
18
18
|
assert manifests[0] == manifests[1]
|
|
19
19
|
manifest = manifests[0]
|
|
20
|
+
from qdrant_memory.version import VERSION
|
|
21
|
+
|
|
22
|
+
assert str(manifest["version"]) == project["version"] == VERSION
|
|
23
|
+
for name in ("README.md", "README.zh-CN.md", "docs/standalone.md", "docs/mcp.md"):
|
|
24
|
+
text = (ROOT / name).read_text(encoding="utf-8")
|
|
25
|
+
assert f"hermes-plugin-qdrant-memory[mcp]=={VERSION}" in text
|
|
26
|
+
assert "hermes-plugin-qdrant-memory[mcp]==0.2.0" not in text
|
|
20
27
|
assert manifest["manifest_version"] == 2
|
|
21
28
|
assert manifest["author"] == "Kang Liu"
|
|
22
29
|
assert manifest["license"] == "MIT"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/__init__.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/cli.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/config.py
RENAMED
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/embedding.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/extraction.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/inventory.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/ledger.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/mcp_server.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/migration.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/models.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/ownership.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/progress.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/provider.py
RENAMED
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/reset.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/retry.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/standalone.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/qdrant_memory/tools.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_bot_scope.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_ci_gate.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_cli_output.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_discovery_cli.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_host_lifecycle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_live_metrics.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_maintenance.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_migration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_profile_scope.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_remote_qdrant.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_reset_recovery.py
RENAMED
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_retention.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_setup_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
{hermes_plugin_qdrant_memory-0.2.0 → hermes_plugin_qdrant_memory-0.2.1}/tests/test_store_runtime.py
RENAMED
|
File without changes
|
|
File without changes
|