@intentic/iq-engine 1.16.0 → 1.17.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentic/iq-engine",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Search engine behind the iq CLI — disk index (node:sqlite), lexical/structural/semantic/git engines, rank fusion, budget renderer",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,11 +47,12 @@
47
47
  "@ast-grep/lang-python": "0.0.6",
48
48
  "@ast-grep/lang-rust": "0.0.7",
49
49
  "@ast-grep/napi": "0.44.1",
50
+ "@huggingface/hub": "2.13.3",
50
51
  "@huggingface/transformers": "4.2.0",
51
52
  "tslib": "2.8.1",
52
53
  "zod": "4.4.3",
53
- "@intentic/sandbox-contract": "1.16.0",
54
- "@intentic/workspace-ignore": "1.16.0"
54
+ "@intentic/sandbox-contract": "1.17.0",
55
+ "@intentic/workspace-ignore": "1.17.0"
55
56
  },
56
57
  "devDependencies": {
57
58
  "@types/node": "24.13.2",
@@ -1,7 +1,15 @@
1
1
  #!/usr/bin/env node
2
- // Downloads the iq models into <dest> at Docker build time, so `iq ask` runs fully offline: the bge embedder
3
- // (semantic retrieval) and the ms-marco cross-encoder (rerank stage).
4
- // Usage: node scripts/fetch-model.mjs /opt/iq-models
2
+ // Bakes the iq models into <dest> at Docker build time, so `iq ask` runs fully offline: the bge embedder
3
+ // (semantic retrieval) and the ms-marco cross-encoder (rerank stage). Downloads go through Hugging Face's Xet
4
+ // protocol (@huggingface/hub) — the CAS bridge denies anonymous plain-HTTP fetches of these artifacts — then
5
+ // Transformers.js re-instantiates everything to validate the exact offline layout the runtime consumes.
6
+ // Usage: node scripts/fetch-model.mjs <dest-dir>
7
+ import { createWriteStream } from "node:fs";
8
+ import { mkdir } from "node:fs/promises";
9
+ import { dirname, join } from "node:path";
10
+ import { Readable } from "node:stream";
11
+ import { pipeline as streamToFile } from "node:stream/promises";
12
+ import { downloadFile } from "@huggingface/hub";
5
13
  import { AutoModelForSequenceClassification, AutoTokenizer, env, pipeline } from "@huggingface/transformers";
6
14
 
7
15
  const dest = process.argv[2];
@@ -9,9 +17,32 @@ if (dest === undefined) {
9
17
  console.error("usage: fetch-model.mjs <dest-dir>");
10
18
  process.exit(2);
11
19
  }
20
+
21
+ // Same model ids + dtypes as src/embed/{embedder,reranker}.ts, pinned to exact HF revisions.
22
+ const MODELS = {
23
+ "Xenova/bge-small-en-v1.5": "ea104dacec62c0de699686887e3f920caeb4f3e3",
24
+ "Xenova/ms-marco-MiniLM-L-6-v2": "a09144355adeed5f58c8ed011d209bf8ee5a1fec",
25
+ };
26
+ // Everything a q8 pipeline reads: tokenizer + config + the int8 onnx graph.
27
+ const FILES = ["config.json", "tokenizer.json", "tokenizer_config.json", "onnx/model_quantized.onnx"];
28
+
29
+ for (const [repo, revision] of Object.entries(MODELS)) {
30
+ for (const file of FILES) {
31
+ const blob = await downloadFile({ repo, path: file, revision });
32
+ if (blob === null) {
33
+ throw new Error(`${repo}@${revision} has no ${file}`);
34
+ }
35
+ const path = join(dest, repo, file);
36
+ await mkdir(dirname(path), { recursive: true });
37
+ await streamToFile(Readable.fromWeb(blob.stream()), createWriteStream(path));
38
+ }
39
+ }
40
+
41
+ // Validate strictly offline — instantiating pulls every artifact the runtime needs, so a gap in FILES fails
42
+ // here with a clear local-file error instead of at first boot.
12
43
  env.cacheDir = dest;
13
44
  env.localModelPath = dest;
14
- // Same model ids + dtypes as src/embed/{embedder,reranker}.ts — instantiating pulls every needed artifact.
45
+ env.allowRemoteModels = false;
15
46
  await pipeline("feature-extraction", "Xenova/bge-small-en-v1.5", { dtype: "q8" });
16
47
  await AutoTokenizer.from_pretrained("Xenova/ms-marco-MiniLM-L-6-v2");
17
48
  await AutoModelForSequenceClassification.from_pretrained("Xenova/ms-marco-MiniLM-L-6-v2", { dtype: "q8" });