@alucard_24/pi-codeindex 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +345 -2
- package/config.schema.json +16 -0
- package/eval/queries.jsonl +8 -0
- package/package.json +33 -3
- package/scripts/setup-gpu.sh +65 -0
- package/scripts/setup-vulkan.mjs +140 -0
- package/scripts/vulkan-sources.json +29 -0
- package/src/backend.js +120 -0
- package/src/chunker.js +276 -0
- package/src/cli.js +196 -0
- package/src/config-ui.js +35 -0
- package/src/config.js +126 -0
- package/src/doctor.js +96 -0
- package/src/embed.js +88 -0
- package/src/eval.js +72 -0
- package/src/extension.js +485 -0
- package/src/graph.js +306 -0
- package/src/indexer.js +302 -0
- package/src/model.js +71 -0
- package/src/present.js +177 -0
- package/src/probe.js +20 -0
- package/src/search.js +189 -0
- package/src/store.js +142 -0
- package/src/sync.js +106 -0
- package/src/util.js +108 -0
- package/src/vulkan.js +151 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alucard24
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,346 @@
|
|
|
1
|
-
#
|
|
1
|
+
# pi-codeindex
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Indice semantico locale del codice per l'agente pi: embedding con
|
|
4
|
+
**EmbeddingGemma 2** (ONNX, in-process) e ricerca ibrida (coseno + lessico)
|
|
5
|
+
che restituisce `path:righe` + simbolo + anteprima. Nessun server, nessun
|
|
6
|
+
database: due file in `~/.cache/pi-codeindex/<sha1(repo)>/`.
|
|
7
|
+
|
|
8
|
+
## Requisiti
|
|
9
|
+
|
|
10
|
+
- Node 22+ (testato su 26.5.1).
|
|
11
|
+
- CPU sempre disponibile come profilo base nelle build native supportate.
|
|
12
|
+
- GPU opzionale: CUDA su Linux x64, WebGPU dove incluso nel binding,
|
|
13
|
+
DirectML su Windows, CoreML su macOS. Non tutte le combinazioni modello,
|
|
14
|
+
driver e hardware sono verificate: vedi la matrice sotto.
|
|
15
|
+
|
|
16
|
+
## Installazione
|
|
17
|
+
|
|
18
|
+
### Da npm, solo su richiesta
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pi -e npm:@alucard_24/pi-codeindex@1.0.0
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Carica il pacchetto per questa invocazione, senza aggiungerlo alle impostazioni
|
|
25
|
+
globali di pi. Dalla sessione puoi usare `/codeindex configure` e
|
|
26
|
+
`/codeindex doctor`. I pesi vengono scaricati solo al primo utilizzo del modello;
|
|
27
|
+
CUDA e Vulkan richiedono i setup opzionali descritti sotto.
|
|
28
|
+
|
|
29
|
+
Il pacchetto npm include codice, setup, schema, README, licenza e query di
|
|
30
|
+
esempio. Il repository GitHub resta privato; piano di lavoro, report completi,
|
|
31
|
+
harness sperimentale e test non sono inclusi nel tarball npm.
|
|
32
|
+
|
|
33
|
+
### Dal sorgente (sviluppo)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install
|
|
37
|
+
npm run setup-gpu # solo GPU NVIDIA: scarica il provider CUDA e lo configura
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`setup-gpu.sh` è **solo Linux x64** e richiede CUDA 13 + `patchelf`.
|
|
41
|
+
Va rieseguito se un aggiornamento/reinstallazione sostituisce il provider.
|
|
42
|
+
DirectML, CoreML e WebGPU usano i provider inclusi nel binding nativo;
|
|
43
|
+
non richiedono questo script CUDA.
|
|
44
|
+
|
|
45
|
+
Il pacchetto espone anche il binario `pi-codeindex`: gli esempi
|
|
46
|
+
`node src/cli.js ...` possono essere eseguiti come `pi-codeindex ...`
|
|
47
|
+
quando il binario è sul PATH. `pi-codeindex setup-gpu` avvia il setup CUDA.
|
|
48
|
+
Nessun postinstall scarica i pesi e nessuna configurazione globale di pi viene
|
|
49
|
+
modificata dal pacchetto.
|
|
50
|
+
|
|
51
|
+
## Embedding e backend
|
|
52
|
+
|
|
53
|
+
Il modello **EmbeddingGemma 2** gira localmente, in-process. I soli pesi testo
|
|
54
|
+
vengono scaricati al primo utilizzo (anche dall'indicizzazione automatica) e
|
|
55
|
+
riutilizzati fra repository. Nessun server o API di embedding; dopo il primo
|
|
56
|
+
download è possibile lavorare offline. Il codice da embeddare non viene
|
|
57
|
+
inviato al server dei pesi. Un processo pi mantiene una propria pipeline.
|
|
58
|
+
|
|
59
|
+
| `device` | Default `dtype: auto` | Stato |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `cpu` | `q8` | inferenza reale verificata su Linux x64 |
|
|
62
|
+
| `cuda` | `fp16` | inferenza reale verificata su NVIDIA/Linux x64; setup opzionale |
|
|
63
|
+
| `webgpu` | `fp16` | inferenza reale verificata su questa macchina Linux; supporto hardware/driver variabile |
|
|
64
|
+
| `dml` | `fp16` | DirectML/Windows: integrazione e policy testate, hardware non verificato |
|
|
65
|
+
| `coreml` | `fp16` | CoreML/macOS: integrazione e policy testate, hardware non verificato |
|
|
66
|
+
|
|
67
|
+
**Vulkan è selezionabile come backend nativo di WebGPU**, non come provider
|
|
68
|
+
ONNX autonomo: usa `device: "webgpu"` + `webgpuBackend: "vulkan"` dopo il setup
|
|
69
|
+
opzionale sotto. `device: "vulkan"` resta un errore esplicativo. Con
|
|
70
|
+
`webgpuBackend: "auto"` decide Dawn: non si garantisce Vulkan e non lo si
|
|
71
|
+
presenta come forzato. Stato e metadati distinguono `webgpu→vulkan/fp16` da
|
|
72
|
+
`webgpu→auto/fp16`.
|
|
73
|
+
Un provider presente nel binding non garantisce compatibilità, velocità,
|
|
74
|
+
utilizzo della GPU dedicata o copertura totale del grafo: ORT può eseguire
|
|
75
|
+
alcuni operatori su CPU. Gli smoke test non sostituiscono un benchmark di
|
|
76
|
+
retrieval/performance per piattaforma. `q4` è disponibile ma il drift non è
|
|
77
|
+
validato sull'eval.
|
|
78
|
+
|
|
79
|
+
**Auto:** Linux prova CUDA → WebGPU → CPU; Windows DirectML → WebGPU → CPU;
|
|
80
|
+
macOS CoreML → WebGPU → CPU. I provider assenti vengono saltati; una prova del
|
|
81
|
+
modello reale deve riuscire prima che il profilo venga fissato. Tentativi e
|
|
82
|
+
fallback sono visibili nei log e nello stato/avvisi dell'estensione.
|
|
83
|
+
Un device forzato fallisce per default; `fallback: "cpu"` autorizza il ripiego.
|
|
84
|
+
Il fallback avviene solo all'inizializzazione, mai durante l'embedding di un
|
|
85
|
+
indice già iniziato. La prima riconciliazione di un processo inizializza il
|
|
86
|
+
modello anche se l'indice era caldo; i vecchi tempi di re-index sotto indicati
|
|
87
|
+
precedono questo cambiamento.
|
|
88
|
+
|
|
89
|
+
### WebGPU con Vulkan forzato (opzionale, Linux x64)
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm run setup-vulkan
|
|
93
|
+
CODEINDEX_OFFLINE=true node src/cli.js doctor --probe --device webgpu --webgpu-backend vulkan
|
|
94
|
+
CODEINDEX_DEVICE=webgpu CODEINDEX_WEBGPU_BACKEND=vulkan pi -e ./src/extension.js
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Il setup richiede **compilatore C++17, header Node/N-API, `tar`, `patchelf` e
|
|
98
|
+
rete** per scaricare i piccoli sorgenti del binding. Non compila ORT/Dawn e
|
|
99
|
+
non richiede CUDA o il Vulkan SDK: riusa il runtime precompilato con WebGPU;
|
|
100
|
+
per eseguire serve il driver Vulkan. Header fuori dai percorsi standard:
|
|
101
|
+
`npm run setup-vulkan -- --node-headers /percorso/include/node`.
|
|
102
|
+
|
|
103
|
+
Il binding Node standard 1.30.0 non espone il selettore Dawn. Il setup
|
|
104
|
+
compila un binding opzionale da un commit ORT fissato e con hash dei sorgenti
|
|
105
|
+
verificati. Binding e copia del runtime (con SONAME distinto) restano in
|
|
106
|
+
`~/.cache/pi-codeindex/native/`: **nessuna modifica a `node_modules`**, nessun
|
|
107
|
+
provider standard sostituito. Il backend aggiunto ha nome unico e priorità
|
|
108
|
+
negativa: viene usato solo da una sessione che lo richiede esplicitamente.
|
|
109
|
+
Prima del caricamento si verificano gli hash; un controllo negativo obbligatorio
|
|
110
|
+
rifiuta i binding che ignorano il selettore. Manca il setup, il runtime è
|
|
111
|
+
cambiato o non esiste un adapter Vulkan? Errore, non WebGPU automatico nascosto.
|
|
112
|
+
Il fallback CPU resta solo quello già autorizzato da `fallback: "cpu"`.
|
|
113
|
+
|
|
114
|
+
Il setup è idempotente; dopo un cambio di runtime può essere necessario
|
|
115
|
+
rieseguirlo. `pi-codeindex setup-vulkan` è equivalente. Riavvia pi dopo setup
|
|
116
|
+
o cambi di profilo. Il backend WebGPU entra nell'identità dell'indice: cambiare
|
|
117
|
+
`auto` ↔ `vulkan` richiede re-index. Con `device: "auto"` il selettore riguarda
|
|
118
|
+
solo l'eventuale tentativo WebGPU: CUDA può ancora essere scelto prima.
|
|
119
|
+
|
|
120
|
+
Windows richiederebbe anche una build ORT/Dawn con Vulkan abilitato: **non
|
|
121
|
+
implementata/verificata** da questo setup. AMD e Intel richiedono prove sulle
|
|
122
|
+
rispettive GPU. Nessuna promessa di vantaggio prestazionale rispetto a CUDA.
|
|
123
|
+
Le prove con il modello completo richiedono VRAM libera; il processo `doctor`
|
|
124
|
+
riporta l'errore e non termina applicazioni GPU altrui per liberarla.
|
|
125
|
+
|
|
126
|
+
Test opzionali reali, dopo setup e download dei pesi fp16:
|
|
127
|
+
`CODEINDEX_TEST_VULKAN=1 node --test test/vulkan-integration.test.js`.
|
|
128
|
+
La suite normale non richiede GPU né compila addon. Verificati su questa
|
|
129
|
+
macchina Linux x64 / RTX 5070 Ti: EmbeddingGemma fp16, indice incrementale,
|
|
130
|
+
ricerca su fixture e coesistenza con i provider standard. Queste prove non
|
|
131
|
+
sostituiscono un benchmark di qualità o prestazioni su altri hardware.
|
|
132
|
+
|
|
133
|
+
## Configurazione
|
|
134
|
+
|
|
135
|
+
Dentro pi:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
/codeindex configure menu interattivo + conferma salvataggio
|
|
139
|
+
/codeindex config valori effettivi, provenienza e percorso
|
|
140
|
+
/codeindex config set device webgpu
|
|
141
|
+
/codeindex config set dtype fp16
|
|
142
|
+
/codeindex config set webgpuBackend vulkan solo WebGPU, setup-vulkan richiesto
|
|
143
|
+
/codeindex config set fallback cpu
|
|
144
|
+
/codeindex config unset device ripristina la scelta automatica
|
|
145
|
+
/codeindex doctor inventario, senza inferenza/download
|
|
146
|
+
/codeindex doctor --probe --device webgpu prova reale isolata (può scaricare i pesi)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
La configurazione è **della macchina**, non della sessione pi:
|
|
150
|
+
`$XDG_CONFIG_HOME/pi-codeindex/config.json` oppure
|
|
151
|
+
`~/.config/pi-codeindex/config.json` (`%APPDATA%/pi-codeindex/config.json`
|
|
152
|
+
su Windows). `CODEINDEX_CONFIG=/percorso/config.json` sceglie un file diverso,
|
|
153
|
+
anche per un singolo progetto; nessun file del repository viene letto
|
|
154
|
+
implicitamente. Non vengono modificate le impostazioni di pi.
|
|
155
|
+
Precedenza: **default < file < ambiente**. Il menu salva solo i campi cambiati,
|
|
156
|
+
non gli override d'ambiente. JSON malformato/chiavi sconosciute producono errori,
|
|
157
|
+
non default silenziosi. Schema per editor: [config.schema.json](config.schema.json).
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"device": "auto",
|
|
162
|
+
"dtype": "auto",
|
|
163
|
+
"fallback": "error",
|
|
164
|
+
"offline": false,
|
|
165
|
+
"cpuThreads": 0
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
| Campo | Valori | Override ambiente |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `device` | `auto`, `cpu`, `cuda`, `webgpu`, `dml`, `coreml` | `CODEINDEX_DEVICE` |
|
|
172
|
+
| `dtype` | `auto`, `q8`, `q4`, `fp16` | `CODEINDEX_DTYPE` |
|
|
173
|
+
| `webgpuBackend` | `auto` (default), `vulkan` forzato; riguarda solo WebGPU | `CODEINDEX_WEBGPU_BACKEND` |
|
|
174
|
+
| `fallback` | `error` (default), `cpu`; riguarda device forzati | `CODEINDEX_FALLBACK` |
|
|
175
|
+
| `offline` | booleano; niente download, errore se mancano pesi | `CODEINDEX_OFFLINE=true\|false\|1\|0` |
|
|
176
|
+
| `cacheDir` | cache pesi, default `~/.cache/pi-codeindex/text-model` | `CODEINDEX_MODEL_CACHE` |
|
|
177
|
+
| `cpuThreads` | 0 = default ORT, oppure 1–256; non limita la GPU | `CODEINDEX_CPU_THREADS` |
|
|
178
|
+
|
|
179
|
+
**Riavvia pi con un nuovo processo** dopo una modifica: non si cambia pipeline
|
|
180
|
+
nel mezzo di un run. Il menu non riavvia né reindicizza da solo. Device, dtype
|
|
181
|
+
e backend WebGPU effettivi sono salvati nell'indice; se diversi, la ricerca rifiuta di mescolare
|
|
182
|
+
i vettori e chiede un re-index. Gli indici precedenti senza `device` vengono
|
|
183
|
+
ricostruiti alla prossima indicizzazione.
|
|
184
|
+
|
|
185
|
+
Da shell, senza avviare pi:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
node src/cli.js config show
|
|
189
|
+
node src/cli.js config set device cpu
|
|
190
|
+
node src/cli.js config set cpuThreads 4
|
|
191
|
+
node src/cli.js doctor --probe --device cpu --json
|
|
192
|
+
node src/cli.js doctor --probe --all # prove sequenziali dei provider presenti
|
|
193
|
+
node src/cli.js doctor --probe --device webgpu --webgpu-backend vulkan
|
|
194
|
+
CODEINDEX_DEVICE=cuda CODEINDEX_DTYPE=fp16 pi -e ./src/extension.js
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`doctor` senza `--probe` non carica/scarica il modello. Le prove reali girano
|
|
198
|
+
in subprocessi isolati con timeout di 120 s per backend; `--all` può scaricare
|
|
199
|
+
più varianti. Ogni provider forzato viene provato senza fallback, per non
|
|
200
|
+
attribuire alla GPU un successo ottenuto in CPU. Usa `offline: true` per provarle solo dalla cache. Un crash nativo
|
|
201
|
+
di una prova non abbatte pi; questo isolamento riguarda `doctor`, non la normale
|
|
202
|
+
pipeline in-process. Smoke del menu e dei comandi via RPC, senza chiamate al
|
|
203
|
+
modello: `CODEINDEX_TEST_RPC=1 node --test test/config-rpc.test.js` (richiede
|
|
204
|
+
`pi` sul PATH; usa un file config temporaneo, non modifica quello utente).
|
|
205
|
+
|
|
206
|
+
## Comandi
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
node src/cli.js index [--root DIR] [--max-files 5000] [--max-bytes 200] [--force]
|
|
210
|
+
node src/cli.js status [--root DIR]
|
|
211
|
+
node src/cli.js search "query" [--k 8] [--path-prefix src/] [--stale] [--bundle] [--json]
|
|
212
|
+
node src/cli.js eval [--file eval/queries.jsonl] [--json]
|
|
213
|
+
node src/cli.js embed-test
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
- `index` è incrementale: confronta l'hash di ogni file e riusa i vettori dei
|
|
217
|
+
chunk invariati. Salvataggio atomico; una cache di ripresa evita di
|
|
218
|
+
ricalcolare tutto se il run viene interrotto.
|
|
219
|
+
- `search` legge il codice dal disco solo per i risultati finali e non esce
|
|
220
|
+
mai dal repo.
|
|
221
|
+
- `eval` misura recall@3/@8 e latenza sulle query della fixture.
|
|
222
|
+
|
|
223
|
+
## Uso dentro pi
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# pacchetto npm: solo questa invocazione, nessuna configurazione globale
|
|
227
|
+
pi -e npm:@alucard_24/pi-codeindex@1.0.0
|
|
228
|
+
|
|
229
|
+
# in sviluppo, dalla directory del progetto
|
|
230
|
+
pi -e ./src/extension.js
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Cosa aggiunge:
|
|
234
|
+
|
|
235
|
+
- **`search_code`** — ricerca semantica (`query`, `k` 1–30, `path_prefix`,
|
|
236
|
+
`context`) che restituisce `path:start-end`, simbolo e anteprima. Non
|
|
237
|
+
sostituisce `grep`.
|
|
238
|
+
- i risultati il cui file è cambiato dopo l'indicizzazione sono marcati
|
|
239
|
+
`STALE` (hash su disco, non fiducia nel watcher);
|
|
240
|
+
- `context: "bundle"` aggiunge import, importatori e fino a 5 chiamanti
|
|
241
|
+
**euristici** dei primi 3 risultati: un round-trip invece di una catena
|
|
242
|
+
`grep`+`read`.
|
|
243
|
+
- **outline su `read`** — se leggi un file > 300 righe con ≥ 5 simboli indicizzati
|
|
244
|
+
e senza `offset`/`limit` mirati, ricevi un blocco `[outline from code index]`
|
|
245
|
+
con i simboli e i loro range (max 40, ~3,5% di caratteri in più).
|
|
246
|
+
- **riga di stato** nel footer (`codeindex: 5.2k chunk`, `codeindex: 1.2k/4.8k`
|
|
247
|
+
durante l'indicizzazione).
|
|
248
|
+
- **`/codeindex status|reindex [--force]|enable|disable|config|configure|doctor`**. `disable` spegne
|
|
249
|
+
tutto: tool, outline, riga di stato, riga nel system prompt, watcher e
|
|
250
|
+
indicizzazione in corso. Lo stato è persistito nel branch della sessione;
|
|
251
|
+
una sessione nuova riparte attiva.
|
|
252
|
+
- **indicizzazione automatica** all'avvio della sessione, in background,
|
|
253
|
+
single-flight, interrompibile, con cap 5.000 file / 200 MB.
|
|
254
|
+
- **sincronizzazione**: watcher ricorsivo con debounce 800 ms, invalidazione
|
|
255
|
+
diretta da `write`/`edit` e sweep `git status` dopo `bash`. Una riconciliazione
|
|
256
|
+
riusa i vettori dei file intatti (misurato: 1 chunk re-embedded su 3 dopo una
|
|
257
|
+
modifica, 920 ms fino all'indice aggiornato).
|
|
258
|
+
|
|
259
|
+
Fuori da un repo git l'estensione si disattiva da sola; se il modello non è
|
|
260
|
+
scaricabile o il reindex fallisce, `search_code` risponde con lo stato reale e
|
|
261
|
+
la sessione continua.
|
|
262
|
+
|
|
263
|
+
## Cosa indicizza
|
|
264
|
+
|
|
265
|
+
JS/TS/TSX/JSX, Python, Go, Rust, C, C++, C#, PowerBuilder (.srw/.sru/…),
|
|
266
|
+
Markdown. Chunk declaration-aware, con fusione dei chunk piccoli (Markdown fino
|
|
267
|
+
a 80 righe; codice solo micro-chunk ≤6 righe). Esclusi: binari, file >1 MB,
|
|
268
|
+
minified, lockfile, `.pbl`/`.pbt`/`.pbw`.
|
|
269
|
+
|
|
270
|
+
## Numeri misurati (RTX 5070 Ti, fp16)
|
|
271
|
+
|
|
272
|
+
| Repo | File | Chunk | Primo indice | Re-index |
|
|
273
|
+
|---|---|---|---|---|
|
|
274
|
+
| `pi.dev-ext` | 373 | 5.230 | 44 s | 169 ms |
|
|
275
|
+
| `Strata` | 2.872 | 29.974 | ~2–5 min | 737 ms |
|
|
276
|
+
|
|
277
|
+
Su CPU q8 (`pi.dev-ext`): 18m28s il primo indice, 143 ms il secondo.
|
|
278
|
+
Ricerca a indice caldo: p50 32 ms. Recall@3 sulla fixture: 5/8.
|
|
279
|
+
|
|
280
|
+
Dentro pi: cache in-process dell'indice (stat dei due file come chiave), quindi
|
|
281
|
+
11,5 ms a caldo su `pi.dev-ext` e **40 ms su `Strata`** (30k chunk), contro ~1,2 s
|
|
282
|
+
di caricamento modello alla prima query di un processo nuovo.
|
|
283
|
+
Outline su un file da 3.713 righe: **+3,5% caratteri** sulla read.
|
|
284
|
+
|
|
285
|
+
## Misura (giro completo)
|
|
286
|
+
|
|
287
|
+
I file e i comandi di questa sezione si riferiscono al checkout di sviluppo:
|
|
288
|
+
piano, report completi e harness non vengono distribuiti nel pacchetto npm.
|
|
289
|
+
Il riepilogo e i limiti delle misure sono riportati qui senza estenderne lo scope.
|
|
290
|
+
|
|
291
|
+
`RESULTS.md` riporta l'esperimento A (pi nudo) vs C (pi + indice) su 10 task per
|
|
292
|
+
repo × 3 ripetizioni: mediana dei delta **−25,4% token su `Strata`** e **−9,7% su
|
|
293
|
+
`pi.dev-ext`**, ma costo totale **−12,3%** e **−12,9%**: sotto la soglia del 15%
|
|
294
|
+
del gate primario. Il verdetto è nel file: **gate non superato**, progetto fermo
|
|
295
|
+
per la regola scritta in `PLAN.md`. Il braccio B (OpenLore) non è stato eseguito:
|
|
296
|
+
nessun claim comparativo.
|
|
297
|
+
|
|
298
|
+
La Fase 6 ha ripetuto la misura con un budget di **80 tool call** per sessione
|
|
299
|
+
(`RESULTS-FASE6.md`): costo **−23,2%** su `Strata` e **−19,2%** su `pi.dev-ext`,
|
|
300
|
+
ma successo **27/30 vs 28/30** su `Strata`. Tutti i fallimenti sono run tagliati
|
|
301
|
+
dal cap; il criterio pre-registrato `C ≥ A` non è soddisfatto per una run, quindi
|
|
302
|
+
quel gate resta non superato.
|
|
303
|
+
|
|
304
|
+
La Fase 7 (`RESULTS-FASE7.md`, 80 coppie appaiate per repo, ipotesi più debole
|
|
305
|
+
pre-registrata prima dei run) supera il gate che chiede **solo di non spendere
|
|
306
|
+
più**: costo appaiato **−22,1%** su `Strata` (CI unilaterale [−29,3%, −14,1%]) e
|
|
307
|
+
−21,5% su `pi.dev-ext`, successo **74/80 vs 74/80** e 79/80 vs 80/80. La soglia
|
|
308
|
+
del **15%** resta non misurata: quello che è dimostrato è "in sessione con budget
|
|
309
|
+
di 80 tool call il costo non aumenta e il successo non peggiora oltre 10pp".
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
node scripts/experiment.mjs --repo ~/AI-Projects/Strata --repeat 8 \
|
|
313
|
+
--max-tool-calls 80 --out /tmp/exp5-strata
|
|
314
|
+
node scripts/analyze-experiment.mjs --runs /tmp/exp5-strata --runs /tmp/exp5-pidev
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Limiti noti
|
|
318
|
+
|
|
319
|
+
- Il chunker è euristico (regex per famiglia): nessun parser AST, nessun
|
|
320
|
+
call graph. Gli errori di confine sono possibili e non etichettati.
|
|
321
|
+
- I chiamanti del bundle sono euristici (scansione dell'identificatore, non un
|
|
322
|
+
call graph): su un campione di 61 casi la precisione misurata è 92%. Il livello
|
|
323
|
+
import è più stretto: solo specifiche che risolvono a un unico file, 29/30 edge
|
|
324
|
+
senza ambiguità su `Strata`.
|
|
325
|
+
- I documenti di piano Markdown competono con il codice nella ricerca: il cap
|
|
326
|
+
di 2 risultati per file e il peso maggiore di path/simbolo mitigano, non
|
|
327
|
+
eliminano il problema.
|
|
328
|
+
- Query in italiano su codice con identificatori inglesi funzionano meno bene
|
|
329
|
+
delle query in inglese.
|
|
330
|
+
- Device e `dtype` fanno parte dell'identità dei vettori: cambiarli richiede
|
|
331
|
+
un re-index. Lo storage v2 lega manifest e vettori con SHA-256: anche un
|
|
332
|
+
salvataggio concorrente/incompleto non viene scambiato per un indice valido.
|
|
333
|
+
Gli indici vecchi senza digest richiedono ricostruzione. La directory è
|
|
334
|
+
unica per repo: processi con profili diversi possono sostituirla o renderla
|
|
335
|
+
temporaneamente indisponibile, mai riutilizzare vettori incompatibili.
|
|
336
|
+
- DirectML e CoreML richiedono ancora smoke test su Windows/macOS; WebGPU è
|
|
337
|
+
verificato solo sulla macchina Linux disponibile, non su tutte le GPU.
|
|
338
|
+
- I pesi sono ancora scaricati dalla revisione `main` del modello; il cache non
|
|
339
|
+
viene aggiornato automaticamente. Prima di un rilascio riproducibile occorre
|
|
340
|
+
fissare uno snapshot dei pesi e verificarne l'integrità.
|
|
341
|
+
|
|
342
|
+
## Licenza
|
|
343
|
+
|
|
344
|
+
Il codice di pi-codeindex è distribuito sotto licenza [MIT](LICENSE).
|
|
345
|
+
Dipendenze, sorgenti upstream scaricati dal setup e pesi del modello mantengono
|
|
346
|
+
le rispettive licenze; non sono inclusi nel tarball del pacchetto.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "pi-codeindex — configurazione embedding locali",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"properties": {
|
|
7
|
+
"$schema": { "type": "string" },
|
|
8
|
+
"device": { "enum": ["auto", "cpu", "cuda", "webgpu", "dml", "coreml"], "default": "auto", "description": "Per Vulkan forzato usa webgpu con webgpuBackend: vulkan. auto prova i provider presenti per piattaforma, poi CPU." },
|
|
9
|
+
"dtype": { "enum": ["auto", "q8", "q4", "fp16"], "default": "auto", "description": "auto: q8 su CPU, fp16 sugli altri provider. Cambiarlo richiede reindex." },
|
|
10
|
+
"webgpuBackend": { "enum": ["auto", "vulkan"], "default": "auto", "description": "Backend Dawn quando il provider effettivo è webgpu. vulkan forza Vulkan e richiede setup-vulkan (inizialmente Linux x64). Non influenza CPU/CUDA/DirectML/CoreML." },
|
|
11
|
+
"fallback": { "enum": ["error", "cpu"], "default": "error", "description": "Fallback consentito per un device forzato; auto include sempre CPU." },
|
|
12
|
+
"offline": { "type": "boolean", "default": false },
|
|
13
|
+
"cacheDir": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Cache dei pesi. Supporta ~/; i percorsi relativi sono risolti dal cwd." },
|
|
14
|
+
"cpuThreads": { "type": "integer", "minimum": 0, "maximum": 256, "default": 0, "description": "0 usa il default di ONNX Runtime; non limita la GPU." }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{"query":"dove viene stimato il numero di token BPE di un messaggio","expects":["bpe-token-estimator"],"why":"estimatore token BPE dedicato"}
|
|
2
|
+
{"query":"come viene applicata una modifica ancorata a un file","expects":["anchored-edit"],"why":"motore delle modifiche ancorate"}
|
|
3
|
+
{"query":"ricerca nella history canonica delle sessioni","expects":["history-service","history-source-reader","context-history-tool"],"why":"lettura e ricerca della history"}
|
|
4
|
+
{"query":"coordinamento della compaction tra più sessioni","expects":["compaction-coordinator"],"why":"coordinatore della compaction"}
|
|
5
|
+
{"query":"lock per evitare rebase concorrenti di una sessione","expects":["rebase-lock"],"why":"lock di rebase"}
|
|
6
|
+
{"query":"quali artefatti vengono conservati e per quanto tempo","expects":["ARTIFACTS.md","artifacts"],"why":"documentazione sugli artefatti"}
|
|
7
|
+
{"query":"marker di omissione per non riesportare dati già visti","expects":["egress"],"why":"gestione egress e marker di omissione"}
|
|
8
|
+
{"query":"indicizzazione incrementale delle sessioni salvate","expects":["session-indexer"],"why":"indice incrementale delle sessioni"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alucard_24/pi-codeindex",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Indice semantico locale del codice per pi (EmbeddingGemma 2, ONNX)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Alucard24",
|
|
7
|
+
"repository": { "type": "git", "url": "git+https://github.com/Alucard24/pi-codeindex.git" },
|
|
8
|
+
"homepage": "https://www.npmjs.com/package/@alucard_24/pi-codeindex",
|
|
9
|
+
"publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
|
|
10
|
+
"main": "./src/extension.js",
|
|
11
|
+
"bin": { "pi-codeindex": "src/cli.js" },
|
|
12
|
+
"files": ["src", "scripts/setup-gpu.sh", "scripts/setup-vulkan.mjs", "scripts/vulkan-sources.json", "eval/queries.jsonl", "config.schema.json", "README.md", "LICENSE"],
|
|
13
|
+
"engines": { "node": ">=22" },
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node --test 'test/*.test.js'",
|
|
16
|
+
"embed-test": "node src/cli.js embed-test",
|
|
17
|
+
"setup-gpu": "bash scripts/setup-gpu.sh",
|
|
18
|
+
"setup-vulkan": "node scripts/setup-vulkan.mjs",
|
|
19
|
+
"doctor": "node src/cli.js doctor"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["pi-package", "embeddings", "code-search"],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"pi": {
|
|
24
|
+
"extensions": [
|
|
25
|
+
"./src/extension.js"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@huggingface/transformers": "4.3.2",
|
|
30
|
+
"onnxruntime-node": "1.30.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@earendil-works/pi-ai": "*",
|
|
34
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
35
|
+
}
|
|
6
36
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Abilita il provider CUDA di onnxruntime-node (Linux x64, GPU NVIDIA).
|
|
3
|
+
#
|
|
4
|
+
# 1. scarica i binari del provider CUDA se mancano (script ufficiale ORT);
|
|
5
|
+
# 2. trova le librerie CUDA 13 richieste dal provider 1.30 (libcublasLt.so.13,
|
|
6
|
+
# libcudart.so.13, libcurand.so.10, ...);
|
|
7
|
+
# 3. scrive un RUNPATH nel .so del provider: così la GPU funziona senza
|
|
8
|
+
# esportare LD_LIBRARY_PATH (necessario perché l'estensione pi gira
|
|
9
|
+
# dentro un processo già avviato).
|
|
10
|
+
#
|
|
11
|
+
# Idempotente. Da rieseguire dopo `npm install` (node_modules viene ricreato).
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
if ! node -e 'process.exit(process.platform === "linux" && process.arch === "x64" ? 0 : 1)'; then
|
|
16
|
+
echo "[gpu] setup CUDA disponibile solo su Linux x64; usa doctor per i backend della tua piattaforma." >&2
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
# Funziona anche con dipendenze npm hoisted, non solo con node_modules locale.
|
|
20
|
+
ORT="$(node --input-type=module - "$ROOT/package.json" <<'JS'
|
|
21
|
+
import { createRequire } from 'node:module';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
console.log(path.dirname(createRequire(process.argv[2]).resolve('onnxruntime-node/package.json')));
|
|
24
|
+
JS
|
|
25
|
+
)"
|
|
26
|
+
PROVIDER="$(find "$ORT/bin" -path '*/linux/x64/libonnxruntime_providers_cuda.so' | head -1 || true)"
|
|
27
|
+
BIN_DIR="$(dirname "${PROVIDER:-$ORT/bin/napi-v6/linux/x64/x}")"
|
|
28
|
+
|
|
29
|
+
if [[ ! -f "$PROVIDER" ]]; then
|
|
30
|
+
echo "[gpu] provider CUDA assente: lo scarico (≈270 MB)…"
|
|
31
|
+
node "$ORT/script/install.js" --onnxruntime-node-install=cuda12
|
|
32
|
+
PROVIDER="$BIN_DIR/libonnxruntime_providers_cuda.so"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
find_cuda_lib() {
|
|
36
|
+
local d
|
|
37
|
+
for d in \
|
|
38
|
+
"${CUDA13_LIB:-}" \
|
|
39
|
+
"$HOME"/.local/cuda-13*/targets/*/lib \
|
|
40
|
+
/usr/local/cuda-13*/targets/*/lib \
|
|
41
|
+
/usr/local/cuda/lib64 \
|
|
42
|
+
"$HOME"/.local/venv-cu13/lib/python*/site-packages/nvidia/cu13/lib \
|
|
43
|
+
/opt/cuda/lib64; do
|
|
44
|
+
[[ -n "$d" && -e "$d/libcublasLt.so.13" ]] && { echo "$d"; return 0; }
|
|
45
|
+
done
|
|
46
|
+
return 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
CUDA_LIB="$(find_cuda_lib || true)"
|
|
50
|
+
if [[ -z "$CUDA_LIB" ]]; then
|
|
51
|
+
echo "[gpu] ERRORE: non trovo libcublasLt.so.13 (serve il runtime CUDA 13)." >&2
|
|
52
|
+
echo " Installa CUDA 13 o imposta CUDA13_LIB=/percorso/lib." >&2
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
command -v patchelf >/dev/null || { echo "[gpu] ERRORE: patchelf non installato." >&2; exit 1; }
|
|
57
|
+
|
|
58
|
+
echo "[gpu] CUDA libs: $CUDA_LIB"
|
|
59
|
+
patchelf --set-rpath "$CUDA_LIB" "$PROVIDER"
|
|
60
|
+
if LD_LIBRARY_PATH= ldd "$PROVIDER" | grep -q "not found"; then
|
|
61
|
+
echo "[gpu] ERRORE: dipendenze non risolte:" >&2
|
|
62
|
+
LD_LIBRARY_PATH= ldd "$PROVIDER" | grep "not found" >&2
|
|
63
|
+
exit 1
|
|
64
|
+
fi
|
|
65
|
+
echo "[gpu] ok: provider CUDA pronto (nessun LD_LIBRARY_PATH richiesto)"
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Setup esplicito, nessun postinstall. Compila solo il binding Node, non ORT/Dawn.
|
|
3
|
+
// Sorgenti MIT upstream fissati; copia ORT privata con SONAME distinto, senza
|
|
4
|
+
// toccare node_modules o il contesto WebGPU di altre estensioni.
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
import { ORT_VERSION, ORT_SOURCE, VULKAN_REVISION, fileHash, ortLibrary, vulkanCacheDir,
|
|
13
|
+
vulkanStatus, verifyVulkanArtifacts } from '../src/vulkan.js';
|
|
14
|
+
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const ADDON_URL = 'https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz';
|
|
17
|
+
const ADDON_INTEGRITY = '+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==';
|
|
18
|
+
|
|
19
|
+
export function patchBindingSource(source) {
|
|
20
|
+
const anchor = 'if (name == "preferredLayout" ||';
|
|
21
|
+
if (source.split(anchor).length !== 2) throw new Error('patch Vulkan: sorgente upstream inatteso');
|
|
22
|
+
return source.replace(anchor, 'if (name == "dawnBackendType" ||\n name == "preferredLayout" ||');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function run(command, args, options = {}) {
|
|
26
|
+
const result = spawnSync(command, args, { stdio: 'inherit', timeout: 180_000, ...options });
|
|
27
|
+
if (result.error) throw result.error;
|
|
28
|
+
if (result.status !== 0) throw new Error(`${command} terminato con ${result.signal ?? result.status}`);
|
|
29
|
+
}
|
|
30
|
+
async function download(url) {
|
|
31
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(60_000) });
|
|
32
|
+
if (!response.ok) throw new Error(`download ${response.status}: ${url}`);
|
|
33
|
+
return Buffer.from(await response.arrayBuffer());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function setupVulkan(args = []) {
|
|
37
|
+
let headers, force = false;
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
if (args[i] === '--force') force = true;
|
|
40
|
+
else if (args[i] === '--node-headers' && args[i + 1] && !args[i + 1].startsWith('--')) headers = path.resolve(args[++i]);
|
|
41
|
+
else throw new Error('uso: setup-vulkan [--force] [--node-headers DIR]');
|
|
42
|
+
}
|
|
43
|
+
if (process.platform !== 'linux' || process.arch !== 'x64') throw new Error('setup-vulkan inizialmente disponibile solo su Linux x64; Windows/macOS non verificati');
|
|
44
|
+
if (require('onnxruntime-node/package.json').version !== ORT_VERSION) throw new Error(`setup-vulkan richiede onnxruntime-node ${ORT_VERSION}`);
|
|
45
|
+
if (!force) {
|
|
46
|
+
const status = vulkanStatus();
|
|
47
|
+
if (status.available) {
|
|
48
|
+
try { await verifyVulkanArtifacts(status); console.log(`Vulkan: binding già pronto in ${status.dir}`); return; }
|
|
49
|
+
catch (e) { console.log(e.message); }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const compiler = process.env.CXX || 'c++';
|
|
53
|
+
run(compiler, ['--version'], { stdio: 'ignore' });
|
|
54
|
+
run('patchelf', ['--version'], { stdio: 'ignore' });
|
|
55
|
+
run('tar', ['--version'], { stdio: 'ignore' });
|
|
56
|
+
headers ??= [path.resolve(path.dirname(process.execPath), '../include/node'), '/usr/include/node']
|
|
57
|
+
.find((dir) => existsSync(path.join(dir, 'node_api.h')));
|
|
58
|
+
if (!headers || !existsSync(path.join(headers, 'node_api.h'))) throw new Error('header Node mancanti: installa gli header di sviluppo o usa --node-headers DIR');
|
|
59
|
+
|
|
60
|
+
const dir = vulkanCacheDir();
|
|
61
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
62
|
+
const work = await fs.mkdtemp(path.join(dir, '.build-'));
|
|
63
|
+
try {
|
|
64
|
+
const sourceManifest = JSON.parse(await fs.readFile(new URL('./vulkan-sources.json', import.meta.url), 'utf8'));
|
|
65
|
+
if (sourceManifest.revision !== ORT_SOURCE) throw new Error('revision sorgenti Vulkan incoerente');
|
|
66
|
+
console.log(`Vulkan: sorgenti ORT ${ORT_VERSION} @ ${ORT_SOURCE.slice(0, 12)} (hash Git verificati)`);
|
|
67
|
+
const sourceDir = path.join(work, 'source');
|
|
68
|
+
const entries = Object.entries(sourceManifest.files);
|
|
69
|
+
// Download limitato: solo binding e header pubblici, niente clone del repository.
|
|
70
|
+
for (let i = 0; i < entries.length; i += 4) await Promise.all(entries.slice(i, i + 4).map(async ([file, expected]) => {
|
|
71
|
+
const bytes = await download(`https://raw.githubusercontent.com/microsoft/onnxruntime/${ORT_SOURCE}/${file}`);
|
|
72
|
+
const actual = createHash('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex');
|
|
73
|
+
if (actual !== expected) throw new Error(`hash sorgente inatteso: ${file}`);
|
|
74
|
+
const dest = path.join(sourceDir, file);
|
|
75
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
76
|
+
await fs.writeFile(dest, bytes);
|
|
77
|
+
}));
|
|
78
|
+
const helper = path.join(sourceDir, 'js/node/src/session_options_helper.cc');
|
|
79
|
+
await fs.writeFile(helper, patchBindingSource(await fs.readFile(helper, 'utf8')));
|
|
80
|
+
const tarball = await download(ADDON_URL);
|
|
81
|
+
if (createHash('sha512').update(tarball).digest('base64') !== ADDON_INTEGRITY) throw new Error('integrità node-addon-api non valida');
|
|
82
|
+
const archive = path.join(work, 'addon-api.tgz');
|
|
83
|
+
await fs.writeFile(archive, tarball);
|
|
84
|
+
const addonDir = path.join(work, 'addon-api');
|
|
85
|
+
await fs.mkdir(addonDir);
|
|
86
|
+
run('tar', ['-xzf', archive, '--strip-components=1', '-C', addonDir]);
|
|
87
|
+
|
|
88
|
+
const out = path.join(work, 'artifact');
|
|
89
|
+
await fs.mkdir(out);
|
|
90
|
+
const sourceLibraryHash = await fileHash(ortLibrary());
|
|
91
|
+
const library = `libpi-codeindex-ort-vulkan-${sourceLibraryHash.slice(0, 16)}.so.1`;
|
|
92
|
+
const libraryFile = path.join(out, library);
|
|
93
|
+
await fs.copyFile(ortLibrary(), libraryFile);
|
|
94
|
+
run('patchelf', ['--set-soname', library, libraryFile]);
|
|
95
|
+
const nodeDir = path.join(sourceDir, 'js/node/src');
|
|
96
|
+
const cpp = (await fs.readdir(nodeDir)).filter((f) => f.endsWith('.cc')).sort().map((f) => path.join(nodeDir, f));
|
|
97
|
+
console.log('Vulkan: compilazione binding isolato (C++/N-API 6), runtime precompilato riutilizzato');
|
|
98
|
+
const bindingFile = path.join(out, 'onnxruntime_binding.node');
|
|
99
|
+
run(compiler, ['-shared', '-fPIC', '-fvisibility=hidden', '-fvisibility-inlines-hidden', '-std=c++17', '-O2',
|
|
100
|
+
'-DNAPI_VERSION=6', '-DUSE_WEBGPU', '-DNAPI_CPP_EXCEPTIONS', '-DORT_API_MANUAL_INIT',
|
|
101
|
+
`-I${headers}`, `-I${addonDir}`, `-I${path.join(sourceDir, 'include/onnxruntime')}`,
|
|
102
|
+
`-I${path.join(sourceDir, 'include/onnxruntime/core/session')}`, ...cpp,
|
|
103
|
+
`-L${out}`, `-l:${library}`, '-Wl,-rpath,$ORIGIN', '-o', bindingFile]);
|
|
104
|
+
await fs.copyFile(path.join(sourceDir, 'LICENSE'), path.join(out, 'LICENSE-onnxruntime'));
|
|
105
|
+
await fs.copyFile(path.join(addonDir, 'LICENSE.md'), path.join(out, 'LICENSE-node-addon-api'));
|
|
106
|
+
// Il controllo negativo non richiede GPU o pesi. Nuovo processo, nessun
|
|
107
|
+
// addon tenuto aperto durante la pubblicazione degli artefatti.
|
|
108
|
+
const moduleUrl = new URL('../src/vulkan.js', import.meta.url).href;
|
|
109
|
+
run(process.execPath, ['--input-type=module', '-e', `
|
|
110
|
+
import { createRequire } from 'node:module';
|
|
111
|
+
import { assertVulkanSelector } from ${JSON.stringify(moduleUrl)};
|
|
112
|
+
process.env.ORT_DISABLE_TELEMETRY = '1';
|
|
113
|
+
const req = createRequire(${JSON.stringify(import.meta.url)});
|
|
114
|
+
const ort = req('onnxruntime-node');
|
|
115
|
+
const binding = req(${JSON.stringify(bindingFile)});
|
|
116
|
+
binding.initOrtOnce(2, ort.Tensor, true);
|
|
117
|
+
assertVulkanSelector(binding);
|
|
118
|
+
console.log('Vulkan: controllo negativo del selettore superato');
|
|
119
|
+
`]);
|
|
120
|
+
const bindingHash = await fileHash(bindingFile);
|
|
121
|
+
const libraryHash = await fileHash(libraryFile);
|
|
122
|
+
// Directory nuova ad ogni build: non riusare/sovrascrivere artefatti
|
|
123
|
+
// corrotti o ancora mappati da un altro processo. Il manifest si pubblica ultimo.
|
|
124
|
+
const artifact = createHash('sha256').update(bindingHash + libraryHash + randomUUID()).digest('hex');
|
|
125
|
+
const target = path.join(dir, artifact);
|
|
126
|
+
await fs.rename(out, target);
|
|
127
|
+
const manifest = { runtimeVersion: ORT_VERSION, sourceRevision: ORT_SOURCE, revision: VULKAN_REVISION,
|
|
128
|
+
platform: process.platform, arch: process.arch, artifact, library, sourceLibraryHash, bindingHash, libraryHash };
|
|
129
|
+
const tmp = path.join(dir, `manifest.${randomUUID()}.tmp`);
|
|
130
|
+
try {
|
|
131
|
+
await fs.writeFile(tmp, JSON.stringify(manifest, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
|
|
132
|
+
await fs.rename(tmp, path.join(dir, 'manifest.json'));
|
|
133
|
+
} finally { await fs.rm(tmp, { force: true }); }
|
|
134
|
+
console.log(`Vulkan: installato in ${target}\nNessun file in node_modules modificato. Riavvia pi se già aperto. Ora esegui doctor --probe --device webgpu --webgpu-backend vulkan.`);
|
|
135
|
+
} finally { await fs.rm(work, { recursive: true, force: true }); }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
139
|
+
setupVulkan(process.argv.slice(2)).catch((e) => { console.error(`setup-vulkan: ${e.message}`); process.exitCode = 1; });
|
|
140
|
+
}
|