dirsql-plugin-embeddings 0.1.0__tar.gz → 0.1.2__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.
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/.gitignore +3 -0
- dirsql_plugin_embeddings-0.1.2/PKG-INFO +68 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/README.md +9 -6
- dirsql_plugin_embeddings-0.1.0/e2e-attestations/claude-open-issues-review-le8hm5-531.json → dirsql_plugin_embeddings-0.1.2/e2e-attestations/claude-on-file-integration-e2e-lpeicb.json +3 -3
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/pyproject.toml +2 -1
- dirsql_plugin_embeddings-0.1.2/src/dirsql_plugin_embeddings/on_file/__init__.py +11 -0
- dirsql_plugin_embeddings-0.1.2/src/dirsql_plugin_embeddings/on_file/build_rows.py +13 -0
- dirsql_plugin_embeddings-0.1.2/src/dirsql_plugin_embeddings/on_file/on_file.py +25 -0
- dirsql_plugin_embeddings-0.1.2/src/dirsql_plugin_embeddings/on_file/read_text.py +11 -0
- dirsql_plugin_embeddings-0.1.0/PKG-INFO +0 -5
- dirsql_plugin_embeddings-0.1.0/src/dirsql_plugin_embeddings/on_file.py +0 -33
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/src/dirsql_plugin_embeddings/__init__.py +0 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/src/dirsql_plugin_embeddings/dirsql.toml +0 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/src/dirsql_plugin_embeddings/embedder.py +0 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/src/dirsql_plugin_embeddings/pre_query.py +0 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/testing-conventions.toml +0 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/tests/conftest.py +0 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/tests/e2e/__init__.py +0 -0
- {dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/tests/integration/__init__.py +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dirsql-plugin-embeddings
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: First-party dirsql plugin: semantic search via an OpenAI-compatible embeddings endpoint.
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# dirsql-plugin-embeddings
|
|
9
|
+
|
|
10
|
+
A first-party [`dirsql`](https://github.com/thekevinscott/dirsql) plugin that
|
|
11
|
+
adds **semantic search** over a directory of Markdown files. It is the worked
|
|
12
|
+
implementation behind the [Search documents by
|
|
13
|
+
meaning](https://thekevinscott.github.io/dirsql/howto/search-by-meaning) how-to,
|
|
14
|
+
swapping that guide's local `model2vec` model for any OpenAI-compatible
|
|
15
|
+
`/v1/embeddings` endpoint.
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
uvx --with dirsql-plugin-embeddings dirsql
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Deliberately minimal (v0.1): one embedding provider shape, one table, no
|
|
22
|
+
chunking, no config surface beyond three environment variables.
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
|
|
26
|
+
The plugin ships a `dirsql.toml` fragment that dirsql discovers when the package
|
|
27
|
+
is installed alongside it. The fragment declares:
|
|
28
|
+
|
|
29
|
+
- the [`sqlite-vec`](https://github.com/asg017/sqlite-vec) extension, for
|
|
30
|
+
`vec_distance_cosine()`;
|
|
31
|
+
- a `documents` table whose `on-file` hook embeds each `**/*.md` file into a
|
|
32
|
+
TEXT `embedding` column;
|
|
33
|
+
- a `pre-query` hook that embeds the incoming question and emits the
|
|
34
|
+
nearest-neighbor SQL.
|
|
35
|
+
|
|
36
|
+
Both hooks are console scripts that call the same embedder.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
The embedder reads three environment variables (point them at any hosted or
|
|
41
|
+
self-managed OpenAI-compatible inference server):
|
|
42
|
+
|
|
43
|
+
| Variable | Meaning |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `DIRSQL_EMBEDDINGS_BASE_URL` | Base URL; `/v1/embeddings` is appended. |
|
|
46
|
+
| `DIRSQL_EMBEDDINGS_MODEL` | Model name sent in the request. |
|
|
47
|
+
| `DIRSQL_EMBEDDINGS_API_KEY` | Bearer token for `Authorization`. |
|
|
48
|
+
|
|
49
|
+
## Console scripts
|
|
50
|
+
|
|
51
|
+
| Script | Hook | Input | Output |
|
|
52
|
+
|---|---|---|---|
|
|
53
|
+
| `dirsql-embeddings-on-file` | `on-file` | a file's absolute path (`argv[1]`) | one-line JSON row array with `path`, `text`, `embedding` |
|
|
54
|
+
| `dirsql-embeddings-pre-query` | `pre-query` | a raw request body (`argv[1]`) | nearest-neighbor SQL over `documents` |
|
|
55
|
+
|
|
56
|
+
`pre-query` accepts both a verbatim server body (`{"q": ...}`) and the CLI
|
|
57
|
+
`query` subcommand's `{"sql": <arg>}` wrapper, so `dirsql query '{"q": ...}'` and
|
|
58
|
+
a real `POST /query` both work.
|
|
59
|
+
|
|
60
|
+
## Tests
|
|
61
|
+
|
|
62
|
+
Three tiers, per the dirsql testing conventions:
|
|
63
|
+
|
|
64
|
+
- **unit** (colocated, mocked seams) — `src/dirsql_plugin_embeddings/*_test.py`
|
|
65
|
+
- **integration** (`tests/integration/`) — each console script as a real
|
|
66
|
+
subprocess against a local stub `/v1/embeddings` server
|
|
67
|
+
- **e2e** (`tests/e2e/`) — the full loop through the real launcher + `dirsql`
|
|
68
|
+
binary + `sqlite-vec`, nothing mocked but the embedding endpoint
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
A first-party [`dirsql`](https://github.com/thekevinscott/dirsql) plugin that
|
|
4
4
|
adds **semantic search** over a directory of Markdown files. It is the worked
|
|
5
5
|
implementation behind the [Search documents by
|
|
6
|
-
meaning](
|
|
7
|
-
local `model2vec` model for any OpenAI-compatible
|
|
6
|
+
meaning](https://thekevinscott.github.io/dirsql/howto/search-by-meaning) how-to,
|
|
7
|
+
swapping that guide's local `model2vec` model for any OpenAI-compatible
|
|
8
|
+
`/v1/embeddings` endpoint.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
```sh
|
|
11
|
+
uvx --with dirsql-plugin-embeddings dirsql
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Deliberately minimal (v0.1): one embedding provider shape, one table, no
|
|
15
|
+
chunking, no config surface beyond three environment variables.
|
|
13
16
|
|
|
14
17
|
## How it works
|
|
15
18
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"command": "uv run --with sqlite-vec --with-editable . --with-editable ../../packages/python python -m pytest tests/e2e -x -q",
|
|
3
|
-
"ran_at":
|
|
3
|
+
"ran_at": 1785323892,
|
|
4
4
|
"exit_code": 0,
|
|
5
|
-
"commit": "
|
|
6
|
-
"branch": "claude/
|
|
5
|
+
"commit": "35c1c6929c4a8b517032cc51e0cc6720f43b58f4",
|
|
6
|
+
"branch": "claude/on-file-integration-e2e-lpeicb"
|
|
7
7
|
}
|
|
@@ -10,6 +10,7 @@ build-backend = "hatchling.build"
|
|
|
10
10
|
name = "dirsql-plugin-embeddings"
|
|
11
11
|
dynamic = ["version"]
|
|
12
12
|
description = "First-party dirsql plugin: semantic search via an OpenAI-compatible embeddings endpoint."
|
|
13
|
+
readme = "README.md"
|
|
13
14
|
requires-python = ">=3.11"
|
|
14
15
|
|
|
15
16
|
[tool.hatch.version]
|
|
@@ -38,7 +39,7 @@ git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--mat
|
|
|
38
39
|
[project.scripts]
|
|
39
40
|
# The two command hooks the fragment invokes; installing the package puts them
|
|
40
41
|
# on PATH so `dirsql` resolves them when it spawns the hooks.
|
|
41
|
-
dirsql-embeddings-on-file = "dirsql_plugin_embeddings.on_file:
|
|
42
|
+
dirsql-embeddings-on-file = "dirsql_plugin_embeddings.on_file:on_file"
|
|
42
43
|
dirsql-embeddings-pre-query = "dirsql_plugin_embeddings.pre_query:main"
|
|
43
44
|
|
|
44
45
|
[project.entry-points.dirsql]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""``on-file`` console script: embed one matched file into a dirsql row.
|
|
2
|
+
|
|
3
|
+
The package barrel: ``pyproject.toml`` points the ``dirsql-embeddings-on-file``
|
|
4
|
+
script at ``dirsql_plugin_embeddings.on_file:on_file``, which resolves here, so
|
|
5
|
+
this re-export is the shipped command's public surface -- moving the callable
|
|
6
|
+
between modules is free, dropping it from ``__all__`` breaks the install.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .on_file import on_file
|
|
10
|
+
|
|
11
|
+
__all__ = ["on_file"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Shape one embedded file into the command hook's row array.
|
|
2
|
+
|
|
3
|
+
Annotations are evaluated at runtime (no ``from __future__ import
|
|
4
|
+
annotations``) so a mutated ``X | None`` union in a signature fails at import
|
|
5
|
+
rather than surviving as an inert string.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_rows(path: str, text: str, vector: list[float]) -> list[dict]:
|
|
12
|
+
# The embedding is stored as JSON text, which `sqlite-vec` accepts directly.
|
|
13
|
+
return [{"path": path, "text": text, "embedding": json.dumps(vector)}]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""The ``on-file`` hook entry point.
|
|
2
|
+
|
|
3
|
+
Reads the file at ``argv[1]``, embeds its text, and prints a one-line JSON row
|
|
4
|
+
array (``path``, ``text``, ``embedding``).
|
|
5
|
+
|
|
6
|
+
Annotations are evaluated at runtime (no ``from __future__ import
|
|
7
|
+
annotations``) so a mutated ``X | None`` union in a signature fails at import
|
|
8
|
+
rather than surviving as an inert string.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from ..embedder import embed
|
|
15
|
+
from .build_rows import build_rows
|
|
16
|
+
from .read_text import read_text
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def on_file(argv: list[str] | None = None) -> int:
|
|
20
|
+
if argv is None:
|
|
21
|
+
argv = sys.argv
|
|
22
|
+
path = argv[1]
|
|
23
|
+
text = read_text(path)
|
|
24
|
+
print(json.dumps(build_rows(path, text, embed(text))))
|
|
25
|
+
return 0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Read the matched file's text off disk.
|
|
2
|
+
|
|
3
|
+
Annotations are evaluated at runtime (no ``from __future__ import
|
|
4
|
+
annotations``) so a mutated ``X | None`` union in a signature fails at import
|
|
5
|
+
rather than surviving as an inert string.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def read_text(path: str) -> str:
|
|
10
|
+
with open(path, encoding="utf-8") as handle:
|
|
11
|
+
return handle.read()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"""``on-file`` console script: embed one matched file into a dirsql row.
|
|
2
|
-
|
|
3
|
-
Reads the file at ``argv[1]``, embeds its text, and prints a one-line JSON row
|
|
4
|
-
array (``path``, ``text``, ``embedding``) -- the embedding stored as JSON text,
|
|
5
|
-
which ``sqlite-vec`` accepts directly.
|
|
6
|
-
|
|
7
|
-
Annotations are evaluated at runtime (no ``from __future__ import annotations``)
|
|
8
|
-
so a mutated ``X | None`` union in a signature fails at import rather than
|
|
9
|
-
surviving as an inert string.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import json
|
|
13
|
-
import sys
|
|
14
|
-
|
|
15
|
-
from .embedder import embed
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _read_text(path: str) -> str:
|
|
19
|
-
with open(path, encoding="utf-8") as handle:
|
|
20
|
-
return handle.read()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def build_rows(path: str, text: str, vector: list[float]) -> list[dict]:
|
|
24
|
-
return [{"path": path, "text": text, "embedding": json.dumps(vector)}]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def main(argv: list[str] | None = None) -> int:
|
|
28
|
-
if argv is None:
|
|
29
|
-
argv = sys.argv
|
|
30
|
-
path = argv[1]
|
|
31
|
-
text = _read_text(path)
|
|
32
|
-
print(json.dumps(build_rows(path, text, embed(text))))
|
|
33
|
-
return 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dirsql_plugin_embeddings-0.1.0 → dirsql_plugin_embeddings-0.1.2}/tests/integration/__init__.py
RENAMED
|
File without changes
|