@functional-systems/srcx 0.1.0-dev.18 → 0.1.0-dev.20
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 +6 -0
- package/README.md +21 -0
- package/VALIDATION.md +49 -0
- package/dist/rerank.js +24 -5
- package/dist/rerank.js.map +1 -1
- package/package.json +2 -2
- package/runtime/qwen.py +50 -15
- package/src/rerank.ts +24 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Distinguish Qwen setup, cache, device, input-size and timeout failures with
|
|
6
|
+
actionable messages while keeping raw worker diagnostics private.
|
|
7
|
+
|
|
8
|
+
- Record three developer search/read investigations with concrete change maps,
|
|
9
|
+
bounded live usage and source verification, separate from ranking benchmarks.
|
|
10
|
+
|
|
5
11
|
- Refresh implemented/remaining scope and verify the published dev.17 package
|
|
6
12
|
through a fresh npm consumer and bounded live first-use workflow.
|
|
7
13
|
|
package/README.md
CHANGED
|
@@ -183,6 +183,27 @@ or fallback. Empty candidate pools skip model execution. Existing search default
|
|
|
183
183
|
and managed embedding usage rules remain unchanged; wider pools increase source
|
|
184
184
|
verification work. See the [integration validation](VALIDATION.md#local-reranker-integration).
|
|
185
185
|
|
|
186
|
+
Failures include a fixed remediation message where the cause is known:
|
|
187
|
+
|
|
188
|
+
| Failure | Action |
|
|
189
|
+
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
190
|
+
| Python executable missing or not executable | Check the path and execute permissions in `SRCX_RERANK_PYTHON`. |
|
|
191
|
+
| Missing or incompatible Python imports | Install `runtime/requirements.txt` using that Python environment. |
|
|
192
|
+
| Missing or incomplete model cache | Download the pinned revision and all files listed above; check `HF_HUB_CACHE`. |
|
|
193
|
+
| Invalid or unavailable device | Use `SRCX_RERANK_DEVICE=auto` or `cpu`, or an available `mps`/`cuda` device. |
|
|
194
|
+
| Request exceeds 4 MiB | Reduce `--candidates` or shorten the query. |
|
|
195
|
+
| A query/chunk pair exceeds 8,192 tokens | Shorten the query or omit `--rerank`; reducing candidate count does not shorten an individual chunk. |
|
|
196
|
+
| Worker exceeds 120 seconds | Reduce `--candidates` or select an available faster device. |
|
|
197
|
+
|
|
198
|
+
Unknown worker failures remain generic. Raw subprocess output and library
|
|
199
|
+
exceptions are never included in these messages. Failed reranking does not
|
|
200
|
+
silently return the original ranking; rerun without `--rerank` to opt out.
|
|
201
|
+
|
|
202
|
+
For practical search → read → change-planning examples, see the
|
|
203
|
+
[developer workflow pilot](https://github.com/lambdadb/srcx/blob/develop/eval/DEVELOPER-WORKFLOW-PILOT.md).
|
|
204
|
+
It follows three maintenance investigations through related source and tests,
|
|
205
|
+
including the limitations of previews and optional reranking.
|
|
206
|
+
|
|
186
207
|
## Evaluate retrieval quality
|
|
187
208
|
|
|
188
209
|
The internal [lexical chunking pilot](https://github.com/lambdadb/srcx/blob/develop/eval/README.md) compares syntax-aware and
|
package/VALIDATION.md
CHANGED
|
@@ -2,6 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
Date: 2026-09-26 (Asia/Seoul).
|
|
4
4
|
|
|
5
|
+
## Qwen failure diagnostics
|
|
6
|
+
|
|
7
|
+
Known worker failures now produce distinct setup, cache, device and input-limit
|
|
8
|
+
messages; host launch and deadline failures have separate remediation. Unknown
|
|
9
|
+
failures remain generic, and raw worker output is not forwarded. Search defaults,
|
|
10
|
+
scores, source verification and the no-fallback policy are unchanged.
|
|
11
|
+
|
|
12
|
+
Local validation passed 97 Node tests, nine Python checks (six worker and three
|
|
13
|
+
local-reranker protocol checks), three installed-package CLI checks, typecheck,
|
|
14
|
+
formatting and version consistency. Reserved worker exits and unexpected exits
|
|
15
|
+
were exercised with real synthetic subprocesses. Deadline, permission and
|
|
16
|
+
unexpected-signal mapping used a mocked process callback; the test asserted the
|
|
17
|
+
unchanged 120-second/SIGKILL policy rather than waiting for a real timeout.
|
|
18
|
+
|
|
19
|
+
Six additional offline checks used the existing Python 3.12 environment and
|
|
20
|
+
cached pinned model: missing executable, missing Python imports, empty cache,
|
|
21
|
+
invalid device, unavailable CUDA, and successful MPS inference. All produced the
|
|
22
|
+
expected outcome; the successful two-candidate query returned finite scores and
|
|
23
|
+
ranked the sorting function above an unrelated reader. No LambdaDB calls, document
|
|
24
|
+
embeddings or model downloads were needed. This is an error-handling and inference
|
|
25
|
+
smoke check, not a retrieval-quality comparison.
|
|
26
|
+
|
|
27
|
+
The retained report is `.srcx/diagnostics/report.json` in the
|
|
28
|
+
`srcx-reranker-diagnostics` worktree, SHA-256
|
|
29
|
+
`7725a96f6c5bf5b4a6a4f01369e2dbea6a8ce16d70692f6e1039774f42564e67`.
|
|
30
|
+
The local runner and Node/package logs are retained alongside it.
|
|
31
|
+
|
|
32
|
+
## Developer workflow pilot
|
|
33
|
+
|
|
34
|
+
The [workflow pilot](https://github.com/lambdadb/srcx/blob/develop/eval/DEVELOPER-WORKFLOW-PILOT.md)
|
|
35
|
+
used installed dev.17 against srcx `7521974a331471b264a695e6f9795bba3e4a7dfd`
|
|
36
|
+
for three assistant-selected maintenance investigations. Five searches and 16
|
|
37
|
+
source-verified reads produced change maps for Qwen diagnostics, Go chunking and
|
|
38
|
+
automatic Git sync. No feature implementation or comparative quality claim is
|
|
39
|
+
part of this record.
|
|
40
|
+
|
|
41
|
+
One import added 127 files / 699 chunks to the existing managed-small Collection,
|
|
42
|
+
with 283,104 estimated document embedding input tokens. Two query embedding
|
|
43
|
+
requests and one cached Qwen invocation stayed within predeclared bounds. All
|
|
44
|
+
live commands completed without replay. Two separate local failure probes
|
|
45
|
+
confirmed indistinguishable public errors for missing Python and an invalid
|
|
46
|
+
device; raw exceptions remained masked.
|
|
47
|
+
|
|
48
|
+
Evidence is retained in the workflow-pilot worktree under `.srcx/workflow-pilot/`:
|
|
49
|
+
|
|
50
|
+
- `plan.json`: SHA-256 `86d929c19ff69faaed6a264198e0b9c15bf32d3b721a31c503a08c4d2e12da39`.
|
|
51
|
+
- `run.json`: SHA-256 `f0ca1a5e6ac12d48e4929959b85b08e846fb239011125cb153aaa6d0babb1359`.
|
|
52
|
+
- `error-probes.json`: SHA-256 `a5fb5c44dc089466cee8a6bba1c4d374c46bba01f9eef24ac739cfeac72b8612`.
|
|
53
|
+
|
|
5
54
|
## Installed dev package first use
|
|
6
55
|
|
|
7
56
|
On 2026-09-26, the public `@functional-systems/srcx@0.1.0-dev.17` tarball from
|
package/dist/rerank.js
CHANGED
|
@@ -3,6 +3,14 @@ import { fileURLToPath } from "node:url";
|
|
|
3
3
|
import { invariant } from "./common.js";
|
|
4
4
|
export const QWEN_MODEL = "Qwen/Qwen3-Reranker-0.6B";
|
|
5
5
|
export const QWEN_REVISION = "e61197ed45024b0ed8a2d74b80b4d909f1255473";
|
|
6
|
+
// Reserved worker exit codes; never interpret or forward library stderr.
|
|
7
|
+
const WORKER_FAILURES = {
|
|
8
|
+
20: "Qwen Python dependencies are missing or incompatible. Install runtime/requirements.txt with the Python selected by SRCX_RERANK_PYTHON.",
|
|
9
|
+
21: "Qwen model cache is missing or incomplete. Download the pinned model revision from README and check HF_HUB_CACHE.",
|
|
10
|
+
22: "Qwen device is invalid or unavailable. Set SRCX_RERANK_DEVICE to auto or cpu, or a supported mps/cuda device.",
|
|
11
|
+
23: "Qwen query/chunk pair exceeds 8,192 tokens. Shorten the query or use search without --rerank; source is not truncated.",
|
|
12
|
+
24: "Qwen request exceeds 4 MiB. Reduce --candidates or shorten the query.",
|
|
13
|
+
};
|
|
6
14
|
export function candidateLimit(limit, options) {
|
|
7
15
|
invariant(Number.isInteger(limit) && limit >= 1 && limit <= 100, "Limit must be an integer from 1 to 100.");
|
|
8
16
|
invariant(options.rerank === undefined || options.rerank === "qwen", "Unknown reranker.");
|
|
@@ -20,7 +28,7 @@ export async function qwenScores(query, candidates) {
|
|
|
20
28
|
candidates.length <= 100 &&
|
|
21
29
|
new Set(candidates.map((c) => c.id)).size === candidates.length, "Invalid reranker candidates.");
|
|
22
30
|
const input = JSON.stringify({ query, candidates });
|
|
23
|
-
invariant(Buffer.byteLength(input) <= 4 * 1024 * 1024, "Reranker input exceeds 4 MiB.");
|
|
31
|
+
invariant(Buffer.byteLength(input) <= 4 * 1024 * 1024, "Reranker input exceeds 4 MiB. Reduce --candidates or shorten the query; no fallback was applied.");
|
|
24
32
|
const python = process.env.SRCX_RERANK_PYTHON;
|
|
25
33
|
invariant(python, "Set SRCX_RERANK_PYTHON to the prepared Python executable.");
|
|
26
34
|
const start = performance.now();
|
|
@@ -37,10 +45,21 @@ export async function qwenScores(query, candidates) {
|
|
|
37
45
|
TOKENIZERS_PARALLELISM: "false",
|
|
38
46
|
},
|
|
39
47
|
}, (error, output) => {
|
|
40
|
-
if (error)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
if (!error)
|
|
49
|
+
return resolve(output);
|
|
50
|
+
let message = "Qwen reranking failed. Check Python dependencies, cached model and SRCX_RERANK_DEVICE.";
|
|
51
|
+
if (error.code === "ENOENT")
|
|
52
|
+
message =
|
|
53
|
+
"Qwen Python executable was not found. Set SRCX_RERANK_PYTHON to an existing Python executable.";
|
|
54
|
+
else if (error.code === "EACCES")
|
|
55
|
+
message =
|
|
56
|
+
"Qwen Python executable cannot be started. Check execute permissions for SRCX_RERANK_PYTHON.";
|
|
57
|
+
else if (error.killed && error.signal === "SIGKILL" && !error.code)
|
|
58
|
+
message =
|
|
59
|
+
"Qwen reranking exceeded 120 seconds. Reduce --candidates or use an available faster device via SRCX_RERANK_DEVICE.";
|
|
60
|
+
else if (typeof error.code === "number")
|
|
61
|
+
message = WORKER_FAILURES[error.code] ?? message;
|
|
62
|
+
reject(new Error(`${message} No fallback was applied.`));
|
|
44
63
|
});
|
|
45
64
|
// An early worker exit can close stdin before Node finishes writing.
|
|
46
65
|
child.stdin?.on("error", () => { });
|
package/dist/rerank.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rerank.js","sourceRoot":"","sources":["../src/rerank.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,CAAC,MAAM,UAAU,GAAG,0BAA0B,CAAC;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"rerank.js","sourceRoot":"","sources":["../src/rerank.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@functional-systems/srcx",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.20",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Version-aware code search on LambdaDB",
|
|
6
6
|
"type": "module",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"url": "https://github.com/lambdadb/srcx/issues"
|
|
65
65
|
},
|
|
66
66
|
"license": "Apache-2.0",
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "d1cdcfd08f3ca8becb5d6a5cd8c42552429509c1"
|
|
68
68
|
}
|
package/runtime/qwen.py
CHANGED
|
@@ -4,6 +4,7 @@ import json
|
|
|
4
4
|
import math
|
|
5
5
|
import os
|
|
6
6
|
import sys
|
|
7
|
+
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
MODEL = "Qwen/Qwen3-Reranker-0.6B"
|
|
9
10
|
REVISION = "e61197ed45024b0ed8a2d74b80b4d909f1255473"
|
|
@@ -14,6 +15,19 @@ PREFIX = (
|
|
|
14
15
|
)
|
|
15
16
|
SUFFIX = "<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n"
|
|
16
17
|
INSTRUCTION = "Given a code search query, retrieve code passages that answer the query."
|
|
18
|
+
CACHE_FILES = ["config.json", "generation_config.json", "model.safetensors",
|
|
19
|
+
"tokenizer.json", "tokenizer_config.json", "vocab.json", "merges.txt"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorkerFailure(ValueError):
|
|
23
|
+
"""Exit codes: 20 imports, 21 cache, 22 device, 23 pair tokens, 24 request bytes.
|
|
24
|
+
|
|
25
|
+
The Node caller maps codes to messages; exception text never crosses the boundary.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, code):
|
|
29
|
+
super().__init__("Qwen worker failure")
|
|
30
|
+
self.code = code
|
|
17
31
|
|
|
18
32
|
|
|
19
33
|
def require(condition, message):
|
|
@@ -25,7 +39,8 @@ def encode(tokenizer, query, text):
|
|
|
25
39
|
body = f"<Instruct>: {INSTRUCTION}\n<Query>: {query}\n<Document>: {text}"
|
|
26
40
|
ids = sum((tokenizer.encode(p, add_special_tokens=False)
|
|
27
41
|
for p in (PREFIX, body, SUFFIX)), [])
|
|
28
|
-
|
|
42
|
+
if len(ids) > 8192:
|
|
43
|
+
raise WorkerFailure(23)
|
|
29
44
|
return ids
|
|
30
45
|
|
|
31
46
|
|
|
@@ -45,22 +60,33 @@ def validate(body):
|
|
|
45
60
|
|
|
46
61
|
def score(body):
|
|
47
62
|
os.environ["HF_HUB_OFFLINE"] = "1"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
device = os.environ.get("SRCX_RERANK_DEVICE", "auto")
|
|
64
|
+
if device not in ("auto", "cpu", "mps", "cuda"):
|
|
65
|
+
raise WorkerFailure(22)
|
|
66
|
+
try:
|
|
67
|
+
from huggingface_hub import snapshot_download
|
|
68
|
+
from huggingface_hub.errors import LocalEntryNotFoundError
|
|
69
|
+
from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
70
|
+
import torch
|
|
71
|
+
except ImportError:
|
|
72
|
+
raise WorkerFailure(20) from None
|
|
73
|
+
if device == "auto":
|
|
74
|
+
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
|
|
75
|
+
elif ((device == "cuda" and not torch.cuda.is_available()) or
|
|
76
|
+
(device == "mps" and not torch.backends.mps.is_available())):
|
|
77
|
+
raise WorkerFailure(22)
|
|
78
|
+
try:
|
|
79
|
+
path = snapshot_download(MODEL, revision=REVISION, local_files_only=True,
|
|
80
|
+
allow_patterns=CACHE_FILES)
|
|
81
|
+
except LocalEntryNotFoundError:
|
|
82
|
+
raise WorkerFailure(21) from None
|
|
83
|
+
if not all((Path(path) / name).is_file() for name in CACHE_FILES):
|
|
84
|
+
raise WorkerFailure(21)
|
|
55
85
|
tokenizer = AutoTokenizer.from_pretrained(path, local_files_only=True, trust_remote_code=False)
|
|
56
86
|
# Reuse exact duplicate code within this query, retaining every candidate ID.
|
|
57
87
|
encoded = {c["text"]: None for c in body["candidates"]}
|
|
58
88
|
for text in encoded:
|
|
59
89
|
encoded[text] = encode(tokenizer, body["query"], text)
|
|
60
|
-
device = os.environ.get("SRCX_RERANK_DEVICE", "auto")
|
|
61
|
-
require(device in ("auto", "cpu", "mps", "cuda"), "Invalid device")
|
|
62
|
-
if device == "auto":
|
|
63
|
-
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
|
|
64
90
|
model = AutoModelForCausalLM.from_pretrained(
|
|
65
91
|
path, local_files_only=True, trust_remote_code=False,
|
|
66
92
|
dtype=torch.float32, attn_implementation="sdpa",
|
|
@@ -83,15 +109,24 @@ def score(body):
|
|
|
83
109
|
|
|
84
110
|
def main():
|
|
85
111
|
raw = sys.stdin.buffer.read(4 * 1024 * 1024 + 1)
|
|
86
|
-
|
|
112
|
+
if len(raw) > 4 * 1024 * 1024:
|
|
113
|
+
raise WorkerFailure(24)
|
|
87
114
|
body = validate(json.loads(raw))
|
|
88
115
|
print(json.dumps(score(body), allow_nan=False))
|
|
89
116
|
|
|
90
117
|
|
|
91
|
-
|
|
118
|
+
def run():
|
|
92
119
|
try:
|
|
93
120
|
main()
|
|
121
|
+
except WorkerFailure as error:
|
|
122
|
+
# The Node caller maps these codes to fixed, actionable messages.
|
|
123
|
+
return error.code
|
|
94
124
|
except Exception:
|
|
95
125
|
# Libraries may include source text or environment values in exceptions.
|
|
96
126
|
print("Qwen worker failed; verify setup, device and input size.", file=sys.stderr)
|
|
97
|
-
|
|
127
|
+
return 1
|
|
128
|
+
return 0
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
sys.exit(run())
|
package/src/rerank.ts
CHANGED
|
@@ -4,6 +4,14 @@ import { invariant } from "./common.js";
|
|
|
4
4
|
|
|
5
5
|
export const QWEN_MODEL = "Qwen/Qwen3-Reranker-0.6B";
|
|
6
6
|
export const QWEN_REVISION = "e61197ed45024b0ed8a2d74b80b4d909f1255473";
|
|
7
|
+
// Reserved worker exit codes; never interpret or forward library stderr.
|
|
8
|
+
const WORKER_FAILURES: Record<number, string> = {
|
|
9
|
+
20: "Qwen Python dependencies are missing or incompatible. Install runtime/requirements.txt with the Python selected by SRCX_RERANK_PYTHON.",
|
|
10
|
+
21: "Qwen model cache is missing or incomplete. Download the pinned model revision from README and check HF_HUB_CACHE.",
|
|
11
|
+
22: "Qwen device is invalid or unavailable. Set SRCX_RERANK_DEVICE to auto or cpu, or a supported mps/cuda device.",
|
|
12
|
+
23: "Qwen query/chunk pair exceeds 8,192 tokens. Shorten the query or use search without --rerank; source is not truncated.",
|
|
13
|
+
24: "Qwen request exceeds 4 MiB. Reduce --candidates or shorten the query.",
|
|
14
|
+
};
|
|
7
15
|
export type Candidate = { id: string; text: string };
|
|
8
16
|
export type RerankOptions = {
|
|
9
17
|
rerank?: "qwen";
|
|
@@ -49,7 +57,7 @@ export async function qwenScores(query: string, candidates: Candidate[]) {
|
|
|
49
57
|
const input = JSON.stringify({ query, candidates });
|
|
50
58
|
invariant(
|
|
51
59
|
Buffer.byteLength(input) <= 4 * 1024 * 1024,
|
|
52
|
-
"Reranker input exceeds 4 MiB.",
|
|
60
|
+
"Reranker input exceeds 4 MiB. Reduce --candidates or shorten the query; no fallback was applied.",
|
|
53
61
|
);
|
|
54
62
|
const python = process.env.SRCX_RERANK_PYTHON;
|
|
55
63
|
invariant(
|
|
@@ -74,13 +82,21 @@ export async function qwenScores(query: string, candidates: Candidate[]) {
|
|
|
74
82
|
},
|
|
75
83
|
},
|
|
76
84
|
(error, output) => {
|
|
77
|
-
if (error)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
else
|
|
85
|
+
if (!error) return resolve(output);
|
|
86
|
+
let message =
|
|
87
|
+
"Qwen reranking failed. Check Python dependencies, cached model and SRCX_RERANK_DEVICE.";
|
|
88
|
+
if (error.code === "ENOENT")
|
|
89
|
+
message =
|
|
90
|
+
"Qwen Python executable was not found. Set SRCX_RERANK_PYTHON to an existing Python executable.";
|
|
91
|
+
else if (error.code === "EACCES")
|
|
92
|
+
message =
|
|
93
|
+
"Qwen Python executable cannot be started. Check execute permissions for SRCX_RERANK_PYTHON.";
|
|
94
|
+
else if (error.killed && error.signal === "SIGKILL" && !error.code)
|
|
95
|
+
message =
|
|
96
|
+
"Qwen reranking exceeded 120 seconds. Reduce --candidates or use an available faster device via SRCX_RERANK_DEVICE.";
|
|
97
|
+
else if (typeof error.code === "number")
|
|
98
|
+
message = WORKER_FAILURES[error.code] ?? message;
|
|
99
|
+
reject(new Error(`${message} No fallback was applied.`));
|
|
84
100
|
},
|
|
85
101
|
);
|
|
86
102
|
// An early worker exit can close stdin before Node finishes writing.
|