ko-qmd 2.8.3-ko.3 → 2.8.3-ko.5
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.
- package/CHANGELOG.md +168 -0
- package/README.md +171 -1262
- package/dist/cli/build-info.json +2 -2
- package/dist/cli/qmd.d.ts +10 -0
- package/dist/cli/qmd.js +262 -25
- package/dist/collections.d.ts +2 -0
- package/dist/collections.js +9 -4
- package/dist/db.js +1 -0
- package/dist/filters.d.ts +46 -0
- package/dist/filters.js +120 -0
- package/dist/grep.d.ts +82 -0
- package/dist/grep.js +159 -0
- package/dist/hangul.d.ts +32 -3
- package/dist/hangul.js +142 -6
- package/dist/index.d.ts +27 -0
- package/dist/index.js +21 -2
- package/dist/llm.d.ts +12 -1
- package/dist/llm.js +39 -6
- package/dist/mcp/server.js +277 -12
- package/dist/metadata-filter.d.ts +74 -0
- package/dist/metadata-filter.js +279 -0
- package/dist/metadata-store.d.ts +45 -0
- package/dist/metadata-store.js +170 -0
- package/dist/metadata.d.ts +61 -0
- package/dist/metadata.js +215 -0
- package/dist/query-log.d.ts +30 -9
- package/dist/query-log.js +22 -3
- package/dist/refresh.d.ts +58 -0
- package/dist/refresh.js +139 -0
- package/dist/store.d.ts +58 -16
- package/dist/store.js +374 -168
- package/package.json +4 -2
- package/skills/qmd/SKILL.md +14 -0
- package/README.ko.md +0 -104
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,174 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.8.3-ko.5] - 2026-09-24
|
|
6
|
+
|
|
7
|
+
Rerank gets an input cap. Truncating each document to 128 tokens keeps KB
|
|
8
|
+
goldset quality (Hit@5 0.97 / Recall@5 0.77) while cutting cold rerank p90 from
|
|
9
|
+
3.6s to 0.9s on an M3 Max. The cap can be set daemon-wide
|
|
10
|
+
(`QMD_RERANK_MAX_DOC_TOKENS`) or per request (`rerankMaxDocTokens` on REST
|
|
11
|
+
`/query` and SDK `search()`). The README is now a Korean guide to ko-qmd itself,
|
|
12
|
+
and the English reference lives in `docs/REFERENCE.md`.
|
|
13
|
+
|
|
14
|
+
### Changes
|
|
15
|
+
|
|
16
|
+
- README.md is rewritten in Korean around ko-qmd itself: what differs from
|
|
17
|
+
upstream, install, quick start, MCP/REST/SDK, environment variables and the
|
|
18
|
+
maintenance guide that used to live in `README.ko.md` (now removed). The
|
|
19
|
+
upstream-derived English reference (full CLI, MCP tool parameters, SDK API,
|
|
20
|
+
score fusion) moved to `docs/REFERENCE.md`, with install and import names
|
|
21
|
+
switched to `ko-qmd`.
|
|
22
|
+
- REST `POST /query` and SDK `search()` take `rerankMaxDocTokens`, a
|
|
23
|
+
per-request rerank doc token cap. For that request it overrides the daemon's
|
|
24
|
+
`QMD_RERANK_MAX_DOC_TOKENS`, and it never raises the context budget. A caller
|
|
25
|
+
like the KB seam can then turn a capped rerank on for one machine without
|
|
26
|
+
changing the daemon's env. The rerank cache key follows the effective cap
|
|
27
|
+
(request, else env). Invalid values (non-integer, ≤ 0, strings) are ignored.
|
|
28
|
+
- `QMD_RERANK_MAX_DOC_TOKENS` caps the tokens of each document sent to the
|
|
29
|
+
reranker, below the context budget. Unset keeps today's behaviour. On the KB
|
|
30
|
+
goldset (40 queries, candidateLimit 15), a cap of 128 keeps quality at
|
|
31
|
+
Hit@5 0.97 / Recall@5 0.77 against 0.95 / 0.76 uncapped. It cuts cold p90 from
|
|
32
|
+
3623ms to 897ms on an M3 Max and from 4720ms to 2192ms on another Apple Silicon
|
|
33
|
+
Mac. The cap is part of the rerank cache key, so capped and uncapped scores
|
|
34
|
+
never mix. Listed in `qmd doctor`.
|
|
35
|
+
- `scripts/dogfood.sh` warms the embedding model with one vec query after
|
|
36
|
+
every daemon restart (deploy and `--restore`). A fresh daemon's first vec
|
|
37
|
+
query took 15.5s, so the first KB seam search after a restart hit its 20s
|
|
38
|
+
limit and fell back to rg. A failed warm-up is a warning, not a failed restart.
|
|
39
|
+
- `scripts/dogfood.sh` waits for the *restarted* daemon. `launchctl kickstart -k`
|
|
40
|
+
returns while the old process is still shutting down and answering `/health`,
|
|
41
|
+
so the restart looked done ~9s before the new process listened. A `/health`
|
|
42
|
+
whose `uptime` predates the kickstart is now treated as the old process.
|
|
43
|
+
|
|
44
|
+
## [2.8.3-ko.4] - 2026-09-24
|
|
45
|
+
|
|
46
|
+
Korean documents are now chunked at their real token budget, and Hangul
|
|
47
|
+
spellings of technical loanwords (`서치`, `웹 코어`) reach notes written in Latin
|
|
48
|
+
script. This release also syncs tobi/qmd up to `04e4dbd` — metadata filtering,
|
|
49
|
+
the oxlint fence and the REST JSON body guard — and adds `qmd grep`, `path`/
|
|
50
|
+
`since`/`until` scoping on MCP and REST, and a daemon that keeps the text index
|
|
51
|
+
fresh on its own. Existing indexes need one `qmd embed` run to pick up the new
|
|
52
|
+
chunking, and SDK callers that passed `filter` for path/time scoping must rename
|
|
53
|
+
it to `scope`.
|
|
54
|
+
|
|
55
|
+
### Breaking
|
|
56
|
+
|
|
57
|
+
- SDK: ko-qmd's path/time narrowing option is now `scope` (was `filter`) on
|
|
58
|
+
`search()`, `searchLex()` and `searchVector()`, and the trailing `scope`
|
|
59
|
+
parameter of `searchFTS`/`searchVec`. `filter` is upstream's metadata filter.
|
|
60
|
+
CLI `--path/--since/--until` and REST/MCP `path/since/until` are unchanged,
|
|
61
|
+
and a request may pass both — a result has to satisfy each.
|
|
62
|
+
|
|
63
|
+
### Upstream sync
|
|
64
|
+
|
|
65
|
+
- Merged tobi/qmd `main` up to `04e4dbd` (24 commits past v2.8.3): oxlint
|
|
66
|
+
fence (`bun run lint` is oxlint; the tsc check is `bun run test:types`),
|
|
67
|
+
metadata filtering, separator-aware FTS terms (quoted `"PIO-1384"` now
|
|
68
|
+
matches), REST JSON body guard, Windows index names, unreadable-gguf
|
|
69
|
+
diagnostics and the store-local embedding tokenizer. ko-qmd behaviour is
|
|
70
|
+
kept: the Hangul query branch runs ahead of the separator split, a metadata
|
|
71
|
+
filter gets the same anti-starvation scan as `--path`, and bm25 on the
|
|
72
|
+
ko-vault bench is identical per query.
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- Added Oxlint lint fence.
|
|
77
|
+
- Document metadata and metadata filtering. Markdown documents can opt into typed metadata through a namespaced frontmatter block (`qmd.metadata` with strings, numbers, booleans, or flat homogeneous arrays), and every search surface — CLI `search`/`vsearch`/`query` via `--filter <json>`, the SDK's `filter` option on `search()`/`searchLex()`/`searchVector()`, the MCP `query` tool, and HTTP `POST /query` and `/search` — accepts one shared recursive filter AST discriminated by `operator`: `and`/`or`/`not` logical groups, `eq`/`ne`/`gt`/`gte`/`lt`/`lte` comparisons, `in`/`nin`/`all` membership, and `exists` presence. Every returned result satisfies the filter (applied before RRF fusion and reranking); like collection filtering, highly selective filters remain best-effort for top-K completeness. Frontmatter stays ordinary searchable content — no chunking, embedding, snippet, or line-number changes — and documents without `qmd.metadata` behave exactly as before. JSON/SDK/MCP/HTTP results now include each document's indexed metadata, and `qmd status` reports how many documents still need metadata extraction (a normal `qmd update` backfills existing indexes).
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- Embedding generation and legacy fingerprint adoption now tokenize documents
|
|
82
|
+
with the store-selected embedding model instead of the global default. This
|
|
83
|
+
keeps chunk boundaries aligned with the model that creates and verifies the
|
|
84
|
+
stored vectors without initializing an unrelated provider.
|
|
85
|
+
|
|
86
|
+
### Changes
|
|
87
|
+
|
|
88
|
+
- Plain Hangul lex terms that spell a common technical loanword (`서치`, `웹`,
|
|
89
|
+
`코어`, `마이그레이션`, about 80 entries in a built-in table) also match the
|
|
90
|
+
Latin spelling, so `하이브리드 서치` finds a note that only says
|
|
91
|
+
`hybrid-search` and `레몬 웹 코어` reaches `lemon-web-core`. The lookup runs
|
|
92
|
+
on particle-stripped stems too (`서치를`), single-syllable entries (`웹`, `훅`)
|
|
93
|
+
are bridged when they stand alone (not `웹을`), and terms with no entry produce the same FTS5 query as before.
|
|
94
|
+
Query-side only; no re-index. On the ko-vault bench the eleven new loanword
|
|
95
|
+
queries go from bm25_r5 0.0000 to 1.0000 while the original 52 hold at
|
|
96
|
+
0.9519 per query.
|
|
97
|
+
- REST `POST /query` and the MCP `query` tool attach `vec_score` (cosine
|
|
98
|
+
similarity) and `fts_score` (normalized BM25) to each result on the
|
|
99
|
+
`rerank:false` path, where `score` is the 1/rank fusion position and cannot
|
|
100
|
+
be thresholded. Each is the max over the sub-queries that returned the hit
|
|
101
|
+
and is omitted, never 0, when that backend did not return it. The daemon
|
|
102
|
+
query log (`QMD_QUERY_LOG`) records the same two fields per result; the row
|
|
103
|
+
schema stays `v: 1` and `rerank:true` responses are unchanged.
|
|
104
|
+
- Korean documents are chunked at their real token budget instead of the
|
|
105
|
+
English-tuned 3.0 chars/token: `estimateCharsPerToken` blends CJK and other
|
|
106
|
+
scripts as a harmonic mean (measured ko 1.64 / en 6.08 chars per token on
|
|
107
|
+
Qwen3-Embedding-0.6B-Q8_0, 2026-09-21), and indexing plus both search-path
|
|
108
|
+
best-chunk selections size chunks with it. A 20KB Korean document now
|
|
109
|
+
first-passes into ~900-token chunks instead of being split ~855 and Korean
|
|
110
|
+
rerank chunks shrink from ~3600 to ~1440 chars. Non-CJK text keeps the ratio
|
|
111
|
+
each call site already used — 3.0 when indexing, 4.0 on the search paths — so
|
|
112
|
+
English chunk boundaries do not move, and the ko-vault bench holds
|
|
113
|
+
(bm25_r5=0.9519).
|
|
114
|
+
- Re-embedding a document whose chunk count shrank now prunes its stale
|
|
115
|
+
higher-`seq` vectors before inserting, so one `qmd embed` run converges
|
|
116
|
+
instead of waiting for a second run. The embedding fingerprint gains a
|
|
117
|
+
`chunk_chars_estimator` term, so `qmd status` reports re-embedding as needed
|
|
118
|
+
for existing indexes; run `qmd embed` once to pick up the new chunking.
|
|
119
|
+
|
|
120
|
+
- `search`, `vsearch` and `query` take `--path`, `--since` and `--until`, so a
|
|
121
|
+
search can be narrowed to part of a collection or to what changed recently.
|
|
122
|
+
`--path` matches globs against `collection/path` and is repeatable, with a
|
|
123
|
+
`!` prefix to exclude; `--since` / `--until` take a span (`7d`) or a date.
|
|
124
|
+
The filter narrows the corpus rather than the result list — excluded
|
|
125
|
+
documents are gone before ranking, on the vector path as well as BM25, so a
|
|
126
|
+
narrow filter returns its own best matches instead of whatever survived the
|
|
127
|
+
global top-K. A span that does not parse is an error, and a filter that
|
|
128
|
+
leaves nothing to search says so instead of looking like "no match".
|
|
129
|
+
|
|
130
|
+
- `qmd query --explain` now lists every expansion sub-query that placed the
|
|
131
|
+
document, with that sub-query's own rank, contribution, backend score and the
|
|
132
|
+
text it ran. It previously showed the top three contributions collapsed onto
|
|
133
|
+
one line without the query text, which could not distinguish a document that
|
|
134
|
+
ranked first in one sub-query and nowhere else from one that ranked middling
|
|
135
|
+
everywhere — two situations that call for different fixes.
|
|
136
|
+
|
|
137
|
+
- `qmd grep <pattern>` matches document bodies exactly, by string or regular
|
|
138
|
+
expression, with no ranking and no LLM: every matching line, grouped by file,
|
|
139
|
+
with line numbers that `qmd get <file>:<n>:<count>` accepts. It is the escape
|
|
140
|
+
hatch for a query that finds nothing because tokenization never produced the
|
|
141
|
+
term the document contains — the failure this fork keeps hitting with Korean
|
|
142
|
+
text — and for finding an identifier or error string verbatim. It searches
|
|
143
|
+
exactly what the index holds and honours collection scoping, `--path`,
|
|
144
|
+
`--since` and `--until`. Smart case: an uppercase letter in the pattern makes
|
|
145
|
+
it case-sensitive, and Hangul has no case so Korean patterns are unaffected.
|
|
146
|
+
`-F` searches for a literal string. An MCP `grep` tool exposes the same thing
|
|
147
|
+
to agents, which is the reason it exists: a client whose only tools are
|
|
148
|
+
`query`, `get` and `multi_get` has no other way to match a string exactly.
|
|
149
|
+
|
|
150
|
+
- The MCP `query` tool and the REST `/query` endpoint take the same `path`,
|
|
151
|
+
`since` and `until` parameters, so an agent can scope a search to a folder or
|
|
152
|
+
to recent changes without shelling out. A span that does not parse comes back
|
|
153
|
+
as an error rather than an unfiltered search, and an empty result under a
|
|
154
|
+
filter reports how many documents the filter left — an agent reading a bare
|
|
155
|
+
"No results" would otherwise conclude the corpus has nothing on the topic.
|
|
156
|
+
|
|
157
|
+
- The HTTP and stdio MCP daemon keeps the text index current on its own. Before
|
|
158
|
+
each search it checks the collections that search covers — at most once every
|
|
159
|
+
30 seconds each — and re-indexes the ones whose files changed, so a note
|
|
160
|
+
written while the daemon runs is findable on the next query without `qmd
|
|
161
|
+
update`. It updates keyword search only; vectors still wait for `qmd embed`.
|
|
162
|
+
It does not run a collection's `update:` hook, does not clear the LLM cache
|
|
163
|
+
the way `qmd update` does, and does not touch a collection whose folder has
|
|
164
|
+
vanished or emptied, since reindexing would deactivate every document in it.
|
|
165
|
+
|
|
166
|
+
- The daemon's query log now covers the MCP `query` tool, not just the REST
|
|
167
|
+
endpoints. A search an agent runs through MCP over HTTP lands in the same
|
|
168
|
+
`queries-YYYY-MM.jsonl` with `via: "mcp"`, the same result path spelling and
|
|
169
|
+
the same `X-QMD-*` header handling, so a dogfooding session's real traffic is
|
|
170
|
+
in one file. A plain `query` argument is recorded as one search of type
|
|
171
|
+
`auto`. stdio MCP calls stay unlogged — they carry no request to annotate.
|
|
172
|
+
|
|
5
173
|
## [2.8.3-ko.3] - 2026-09-18
|
|
6
174
|
|
|
7
175
|
The reranker now decides which document ranks first. The blend's retrieval-position
|