@alucard_24/pi-codeindex 0.0.0-stage → 1.1.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/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +442 -2
- package/config.schema.json +19 -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 +204 -0
- package/src/config-ui.js +37 -0
- package/src/config.js +131 -0
- package/src/doctor.js +101 -0
- package/src/embed.js +57 -0
- package/src/eval.js +72 -0
- package/src/extension.js +522 -0
- package/src/graph.js +306 -0
- package/src/indexer.js +302 -0
- package/src/local-embed.js +51 -0
- package/src/model.js +71 -0
- package/src/present.js +177 -0
- package/src/probe.js +23 -0
- package/src/search.js +189 -0
- package/src/service-client.js +126 -0
- package/src/service-policy.js +33 -0
- package/src/service-protocol.js +122 -0
- package/src/service-worker.js +3 -0
- package/src/service.js +172 -0
- package/src/store.js +142 -0
- package/src/sync.js +110 -0
- package/src/util.js +108 -0
- package/src/vulkan.js +151 -0
- package/src/workspace.js +18 -0
package/src/vulkan.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Backend opzionale: WebGPU/Dawn con selettore Vulkan effettivamente applicato.
|
|
2
|
+
// Binding e copia di ORT isolati nella cache; nessun provider standard sostituito.
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { isMainThread } from 'node:worker_threads';
|
|
10
|
+
|
|
11
|
+
export const ORT_VERSION = '1.30.0';
|
|
12
|
+
export const ORT_SOURCE = 'f2c39fe2f838cf35ce7da92824f5a5e3ee6e88a7';
|
|
13
|
+
export const VULKAN_REVISION = 1;
|
|
14
|
+
export const VULKAN_EP = 'pi-codeindex-vulkan-r1';
|
|
15
|
+
// Un solo Identity float32, anche per verificare il rifiuto di un backend invalido.
|
|
16
|
+
export const SMOKE_MODEL = Buffer.from('08083a370a100a015812015922084964656e746974791201675a0f0a0158120a0a08080112040a020801620f0a0159120a0a08080112040a0208014202100d', 'hex');
|
|
17
|
+
|
|
18
|
+
const require = createRequire(import.meta.url);
|
|
19
|
+
export function ortLibrary() {
|
|
20
|
+
const dir = path.dirname(require.resolve('onnxruntime-node/package.json'));
|
|
21
|
+
return path.join(dir, 'bin', 'napi-v6', process.platform, process.arch, 'libonnxruntime.so.1');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function vulkanCacheDir({ home = os.homedir(), platform = process.platform, arch = process.arch } = {}) {
|
|
25
|
+
return path.join(home, '.cache', 'pi-codeindex', 'native', `vulkan-ort-${ORT_VERSION}-r${VULKAN_REVISION}`, `${platform}-${arch}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function validateVulkanManifest(m, { platform = process.platform, arch = process.arch } = {}) {
|
|
29
|
+
if (!m || m.runtimeVersion !== ORT_VERSION || m.sourceRevision !== ORT_SOURCE ||
|
|
30
|
+
m.revision !== VULKAN_REVISION || m.platform !== platform || m.arch !== arch) {
|
|
31
|
+
throw new Error('manifest Vulkan incompatibile: riesegui npm run setup-vulkan');
|
|
32
|
+
}
|
|
33
|
+
for (const key of ['artifact', 'sourceLibraryHash', 'bindingHash', 'libraryHash']) {
|
|
34
|
+
if (typeof m[key] !== 'string' || !/^[a-f0-9]{64}$/.test(m[key])) throw new Error(`manifest Vulkan: ${key} invalido`);
|
|
35
|
+
}
|
|
36
|
+
if (m.library !== `libpi-codeindex-ort-vulkan-${m.sourceLibraryHash.slice(0, 16)}.so.1`) {
|
|
37
|
+
throw new Error('manifest Vulkan: nome libreria invalido');
|
|
38
|
+
}
|
|
39
|
+
return m;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Inventario leggero: non carica addon, driver o modelli. Gli hash vengono
|
|
43
|
+
// verificati prima del dlopen, non ad ogni richiesta di status/doctor.
|
|
44
|
+
export function vulkanStatus(options = {}) {
|
|
45
|
+
const platform = options.platform ?? process.platform;
|
|
46
|
+
const arch = options.arch ?? process.arch;
|
|
47
|
+
const dir = vulkanCacheDir(options);
|
|
48
|
+
if (platform !== 'linux' || arch !== 'x64') return { available: false, dir,
|
|
49
|
+
reason: 'setup Vulkan forzato disponibile inizialmente solo su Linux x64; questa piattaforma non è verificata' };
|
|
50
|
+
try {
|
|
51
|
+
const manifest = validateVulkanManifest(JSON.parse(readFileSync(path.join(dir, 'manifest.json'), 'utf8')), { platform, arch });
|
|
52
|
+
const artifactDir = path.join(dir, manifest.artifact);
|
|
53
|
+
const binding = path.join(artifactDir, 'onnxruntime_binding.node');
|
|
54
|
+
const library = path.join(artifactDir, manifest.library);
|
|
55
|
+
if (!existsSync(binding) || !existsSync(library)) throw new Error('artefatti Vulkan mancanti');
|
|
56
|
+
return { available: true, dir, binding, library, manifest, reason: 'binding isolato presente; integrità e inferenza da verificare' };
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return { available: false, dir, reason: e.code === 'ENOENT' ? 'binding opzionale assente: esegui npm run setup-vulkan' : e.message };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function fileHash(file) {
|
|
63
|
+
return createHash('sha256').update(await fs.readFile(file)).digest('hex');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function verifyVulkanArtifacts(status, sourceLibrary = ortLibrary()) {
|
|
67
|
+
if (!status.available) throw new Error(status.reason);
|
|
68
|
+
const version = require('onnxruntime-node/package.json').version;
|
|
69
|
+
if (version !== ORT_VERSION) throw new Error(`binding Vulkan richiede onnxruntime-node ${ORT_VERSION}, trovato ${version}`);
|
|
70
|
+
for (const [file, expected] of [[sourceLibrary, status.manifest.sourceLibraryHash],
|
|
71
|
+
[status.binding, status.manifest.bindingHash], [status.library, status.manifest.libraryHash]]) {
|
|
72
|
+
if (await fileHash(file) !== expected) throw new Error(`integrità/runtime Vulkan cambiato (${path.basename(file)}): riesegui npm run setup-vulkan`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Prova negativa obbligatoria: impedisce di dichiarare Vulkan quando il binding
|
|
77
|
+
// ignora il selettore. Deve fallire PRIMA di inizializzare un device Dawn.
|
|
78
|
+
export function assertVulkanSelector(binding) {
|
|
79
|
+
const session = new binding.InferenceSession();
|
|
80
|
+
let rejected = false;
|
|
81
|
+
try {
|
|
82
|
+
session.loadModel(SMOKE_MODEL.buffer, SMOKE_MODEL.byteOffset, SMOKE_MODEL.byteLength,
|
|
83
|
+
{ executionProviders: [{ name: 'webgpu', dawnBackendType: '__codeindex_invalid_backend__' }] });
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (!/Invalid Dawn backend type/.test(e.message)) throw new Error(`selettore Vulkan non verificato: ${e.message}`);
|
|
86
|
+
rejected = true;
|
|
87
|
+
} finally {
|
|
88
|
+
try { session.dispose(); } catch { /* loadModel può non avere creato la sessione */ }
|
|
89
|
+
}
|
|
90
|
+
if (!rejected) throw new Error('il binding ignora dawnBackendType: Vulkan non può essere garantito');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const TYPES = [null, 'float32', 'uint8', 'int8', 'uint16', 'int16', 'int32', 'int64', 'string', 'bool',
|
|
94
|
+
'float16', 'float64', 'uint32', 'uint64', null, null, null, null, null, null, null, 'uint4', 'int4'];
|
|
95
|
+
const defer = (fn) => new Promise((resolve, reject) => setImmediate(() => { try { resolve(fn()); } catch (e) { reject(e); } }));
|
|
96
|
+
function metadata(entries) {
|
|
97
|
+
return entries.map((m) => {
|
|
98
|
+
if (!m.isTensor) return { name: m.name, isTensor: false };
|
|
99
|
+
const type = TYPES[m.type];
|
|
100
|
+
if (!type) throw new Error(`tipo ONNX non supportato: ${m.type}`);
|
|
101
|
+
return { name: m.name, isTensor: true, type, shape: m.shape.map((dim, i) => {
|
|
102
|
+
if (dim < -1) throw new Error(`dimensione ONNX invalida: ${dim}`);
|
|
103
|
+
return dim === -1 ? m.symbolicDimensions[i] : dim;
|
|
104
|
+
}) };
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Adattatore dell'interfaccia pubblica Backend di onnxruntime-common. Registrato
|
|
109
|
+
// con nome unico e priorità negativa: non entra mai nella selezione automatica
|
|
110
|
+
// di altri utenti di ONNX Runtime nello stesso processo.
|
|
111
|
+
export function createVulkanBackend(binding) {
|
|
112
|
+
return {
|
|
113
|
+
init: async () => {},
|
|
114
|
+
createInferenceSessionHandler: (model, options) => defer(() => {
|
|
115
|
+
const native = new binding.InferenceSession();
|
|
116
|
+
try {
|
|
117
|
+
const opts = { ...options, executionProviders: [{ name: 'webgpu', dawnBackendType: 'Vulkan' }] };
|
|
118
|
+
if (typeof model === 'string') native.loadModel(model, opts);
|
|
119
|
+
else native.loadModel(model.buffer, model.byteOffset, model.byteLength, opts);
|
|
120
|
+
const inputMetadata = metadata(native.inputMetadata);
|
|
121
|
+
const outputMetadata = metadata(native.outputMetadata);
|
|
122
|
+
return { inputNames: inputMetadata.map((m) => m.name), outputNames: outputMetadata.map((m) => m.name),
|
|
123
|
+
inputMetadata, outputMetadata,
|
|
124
|
+
run: (feeds, fetches, runOptions) => defer(() => native.run(feeds, fetches, runOptions)),
|
|
125
|
+
dispose: async () => native.dispose(), startProfiling: () => {}, endProfiling: () => native.endProfiling() };
|
|
126
|
+
} catch (e) {
|
|
127
|
+
try { native.dispose(); } catch { /* conserva l'errore originale */ }
|
|
128
|
+
throw e;
|
|
129
|
+
}
|
|
130
|
+
}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let registration;
|
|
135
|
+
export async function registerVulkanBackend() {
|
|
136
|
+
if (!registration) registration = (async () => {
|
|
137
|
+
const status = vulkanStatus();
|
|
138
|
+
await verifyVulkanArtifacts(status);
|
|
139
|
+
// Stessa policy dell'adapter Transformers.js, prima di caricare la copia
|
|
140
|
+
// privata di ORT. Non modifica env/backends di Transformers.js.
|
|
141
|
+
process.env.ORT_DISABLE_TELEMETRY = '1';
|
|
142
|
+
const ort = require('onnxruntime-node');
|
|
143
|
+
const binding = require(status.binding);
|
|
144
|
+
const levels = { verbose: 0, info: 1, warning: 2, error: 3, fatal: 4 };
|
|
145
|
+
binding.initOrtOnce(levels[ort.env.logLevel] ?? 2, ort.Tensor, isMainThread);
|
|
146
|
+
assertVulkanSelector(binding);
|
|
147
|
+
ort.registerBackend(VULKAN_EP, createVulkanBackend(binding), -10);
|
|
148
|
+
return VULKAN_EP;
|
|
149
|
+
})();
|
|
150
|
+
try { return await registration; } catch (e) { registration = undefined; throw e; }
|
|
151
|
+
}
|
package/src/workspace.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Radice del progetto: Git quando disponibile, altrimenti la cartella corrente.
|
|
2
|
+
// Nessuna configurazione di progetto viene letta e nessun repository viene creato.
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { repoRoot } from './util.js';
|
|
6
|
+
|
|
7
|
+
export function resolveWorkspace(cwd = process.cwd(), {
|
|
8
|
+
findRepoRoot = repoRoot,
|
|
9
|
+
home = os.homedir(),
|
|
10
|
+
} = {}) {
|
|
11
|
+
const gitRoot = findRepoRoot(cwd);
|
|
12
|
+
const root = path.resolve(gitRoot ?? cwd);
|
|
13
|
+
const mode = gitRoot ? 'git' : 'folder';
|
|
14
|
+
// Un avvio dalla home o dalla radice del disco non deve scandire l'intera
|
|
15
|
+
// macchina per errore. /codeindex enable resta un consenso esplicito.
|
|
16
|
+
const broadFolder = root === path.parse(root).root || root === path.resolve(home);
|
|
17
|
+
return { root, mode, autoIndex: mode === 'git' || !broadFolder };
|
|
18
|
+
}
|