@qvac/harness 0.3.3 → 0.5.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 +700 -126
- package/README.md +156 -0
- package/dist/harness.bundle +71 -103
- package/dist/prebuilds-manifest.json +0 -8
- package/dist-lib/index.d.ts +42 -40
- package/dist-lib/index.js +307 -117
- package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
- package/dist-lib/lib/audio-transcode-bare.js +28 -18
- package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
- package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
- package/dist-lib/lib/capabilities/audio-resample.js +30 -18
- package/dist-lib/lib/capabilities/completion.d.ts +20 -19
- package/dist-lib/lib/capabilities/completion.js +194 -135
- package/dist-lib/lib/capabilities/download.d.ts +6 -6
- package/dist-lib/lib/capabilities/download.js +15 -8
- package/dist-lib/lib/capabilities/embed.js +2 -1
- package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
- package/dist-lib/lib/capabilities/enhance.js +7 -4
- package/dist-lib/lib/capabilities/invoke.js +7 -4
- package/dist-lib/lib/capabilities/kv-cache.js +2 -1
- package/dist-lib/lib/capabilities/preview-model-source.d.ts +69 -0
- package/dist-lib/lib/capabilities/preview-model-source.js +439 -0
- package/dist-lib/lib/capabilities/speech-text.js +5 -4
- package/dist-lib/lib/capabilities/tool-selection.js +10 -9
- package/dist-lib/lib/capabilities/tool-turns.js +30 -16
- package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
- package/dist-lib/lib/capabilities/voice.d.ts +5 -1
- package/dist-lib/lib/capabilities/voice.js +22 -12
- package/dist-lib/lib/connect.d.ts +15 -37
- package/dist-lib/lib/connect.js +161 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +26 -20
- package/dist-lib/lib/diagram-repair.d.ts +4 -4
- package/dist-lib/lib/diagram-repair.js +6 -3
- package/dist-lib/lib/docx/index.d.ts +3 -2
- package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
- package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
- package/dist-lib/lib/hardware/capability-probe.js +20 -10
- package/dist-lib/lib/hardware/gpu-classify.js +6 -3
- package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
- package/dist-lib/lib/hardware/gpu-probe.js +4 -2
- package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
- package/dist-lib/lib/hardware/parsers.js +8 -4
- package/dist-lib/lib/helpers/buffer-burst.js +4 -2
- package/dist-lib/lib/helpers/control-tokens.js +4 -2
- package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
- package/dist-lib/lib/helpers/join-guard.js +4 -2
- package/dist-lib/lib/helpers/load-stall.js +4 -2
- package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
- package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
- package/dist-lib/lib/helpers/race-abort.js +2 -1
- package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
- package/dist-lib/lib/helpers/race-timeout.js +14 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
- package/dist-lib/lib/helpers/run-outcome.js +36 -20
- package/dist-lib/lib/helpers/spawn-path.js +6 -3
- package/dist-lib/lib/helpers/text-coalescer.js +6 -3
- package/dist-lib/lib/helpers/thinking.js +38 -45
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
- package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
- package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
- package/dist-lib/lib/helpers/tool-intent.js +4 -2
- package/dist-lib/lib/helpers/tool-label.js +18 -9
- package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
- package/dist-lib/lib/hooks/built-in/egress.js +2 -1
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
- package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
- package/dist-lib/lib/hooks/pipeline.js +6 -3
- package/dist-lib/lib/hooks/run.js +12 -8
- package/dist-lib/lib/model-download-progress.js +10 -8
- package/dist-lib/lib/models/context.d.ts +1 -1
- package/dist-lib/lib/models/context.js +5 -1
- package/dist-lib/lib/models/custom-model-cache.d.ts +13 -0
- package/dist-lib/lib/models/custom-model-cache.js +70 -0
- package/dist-lib/lib/models/gguf-header.d.ts +31 -0
- package/dist-lib/lib/models/gguf-header.js +230 -0
- package/dist-lib/lib/models/idle-unload.d.ts +14 -0
- package/dist-lib/lib/models/idle-unload.js +51 -0
- package/dist-lib/lib/models/kv-cache.d.ts +3 -2
- package/dist-lib/lib/models/kv-cache.js +4 -3
- package/dist-lib/lib/models/llm-presets.d.ts +2 -2
- package/dist-lib/lib/models/llm-presets.js +4 -2
- package/dist-lib/lib/models/model-catalog-constants.js +3 -2
- package/dist-lib/lib/models/model-catalog.d.ts +19 -21
- package/dist-lib/lib/models/model-catalog.js +66 -33
- package/dist-lib/lib/models/model-delete.d.ts +4 -1
- package/dist-lib/lib/models/model-delete.js +30 -15
- package/dist-lib/lib/models/model-handle.js +2 -1
- package/dist-lib/lib/models/model-list.d.ts +14 -3
- package/dist-lib/lib/models/model-list.js +33 -6
- package/dist-lib/lib/models/model-pool.d.ts +44 -10
- package/dist-lib/lib/models/model-pool.js +47 -42
- package/dist-lib/lib/models/model-source.js +11 -6
- package/dist-lib/lib/models/music-catalog.d.ts +2 -2
- package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
- package/dist-lib/lib/models/nmt-catalog.js +10 -5
- package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
- package/dist-lib/lib/models/opencl-cache.js +5 -3
- package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
- package/dist-lib/lib/models/sdk-faults.js +9 -6
- package/dist-lib/lib/models/shards.d.ts +11 -0
- package/dist-lib/lib/models/shards.js +15 -0
- package/dist-lib/lib/models/tool-dialect.js +10 -5
- package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
- package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
- package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
- package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
- package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
- package/dist-lib/lib/office/index.d.ts +3 -2
- package/dist-lib/lib/office/python.d.ts +3 -2
- package/dist-lib/lib/office/python.js +2 -1
- package/dist-lib/lib/prompt.d.ts +5 -4
- package/dist-lib/lib/prompt.js +8 -4
- package/dist-lib/lib/queue.js +10 -5
- package/dist-lib/lib/resolution-detector.js +21 -11
- package/dist-lib/lib/rpc-session.js +2 -1
- package/dist-lib/lib/sdk-loader.js +2 -1
- package/dist-lib/lib/sdk-storage.js +4 -2
- package/dist-lib/lib/serve.js +128 -68
- package/dist-lib/lib/spawn.js +7 -4
- package/dist-lib/lib/tmp-path.d.ts +7 -6
- package/dist-lib/lib/tool-call-display.d.ts +2 -2
- package/dist-lib/lib/tools/create-documents.js +18 -10
- package/dist-lib/lib/tools/edit-documents.js +23 -13
- package/dist-lib/lib/tools/edit-image.d.ts +2 -2
- package/dist-lib/lib/tools/edit-image.js +60 -36
- package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
- package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
- package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
- package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
- package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
- package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
- package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
- package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
- package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
- package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
- package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
- package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
- package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
- package/dist-lib/lib/tools/exec/runner.js +5 -3
- package/dist-lib/lib/tools/exec/scope.js +52 -28
- package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
- package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
- package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
- package/dist-lib/lib/tools/exec/video-tools.js +2 -2
- package/dist-lib/lib/tools/exec.d.ts +6 -8
- package/dist-lib/lib/tools/exec.js +113 -54
- package/dist-lib/lib/tools/file-glob.d.ts +4 -5
- package/dist-lib/lib/tools/file-glob.js +16 -11
- package/dist-lib/lib/tools/fs/glob.js +13 -18
- package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
- package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
- package/dist-lib/lib/tools/fs/remote.js +13 -7
- package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
- package/dist-lib/lib/tools/fs/scope.js +20 -12
- package/dist-lib/lib/tools/fs/walk.js +6 -3
- package/dist-lib/lib/tools/fs/write.d.ts +3 -2
- package/dist-lib/lib/tools/generate-image.d.ts +11 -9
- package/dist-lib/lib/tools/generate-image.js +73 -37
- package/dist-lib/lib/tools/generate-music.d.ts +22 -11
- package/dist-lib/lib/tools/generate-music.js +79 -48
- package/dist-lib/lib/tools/grep.js +66 -41
- package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
- package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
- package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
- package/dist-lib/lib/tools/http-request.d.ts +1 -1
- package/dist-lib/lib/tools/http-request.js +33 -18
- package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
- package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
- package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
- package/dist-lib/lib/tools/mcp-call.js +37 -16
- package/dist-lib/lib/tools/operations/builders.js +10 -6
- package/dist-lib/lib/tools/operations/compile.js +20 -10
- package/dist-lib/lib/tools/operations/decoders.js +12 -6
- package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
- package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
- package/dist-lib/lib/tools/outbound-guard.js +14 -8
- package/dist-lib/lib/tools/private-ip.js +8 -7
- package/dist-lib/lib/tools/rag-search.js +14 -7
- package/dist-lib/lib/tools/read-files.js +23 -20
- package/dist-lib/lib/tools/skill.js +19 -10
- package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
- package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
- package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bin-check.js +10 -3
- package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
- package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
- package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
- package/dist-lib/lib/tools/skills/catalog.js +33 -28
- package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
- package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
- package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
- package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
- package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
- package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
- package/dist-lib/lib/tools/skills/credential-check.js +24 -12
- package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
- package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
- package/dist-lib/lib/tools/skills/credential-health.js +5 -3
- package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
- package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
- package/dist-lib/lib/tools/skills/mention.js +7 -6
- package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
- package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
- package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
- package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
- package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
- package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
- package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
- package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
- package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
- package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
- package/dist-lib/lib/tools/skills/skill-state.js +48 -29
- package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
- package/dist-lib/lib/tools/toolsets.js +2 -1
- package/dist-lib/lib/tools/translate.d.ts +4 -5
- package/dist-lib/lib/tools/translate.js +31 -14
- package/dist-lib/lib/tools/url-guard.js +2 -1
- package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
- package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
- package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
- package/dist-lib/lib/tools/weather-lookup.js +18 -12
- package/dist-lib/lib/tools/web-fetch.js +47 -24
- package/dist-lib/lib/tools/web-search.js +8 -3
- package/dist-lib/lib/validating-lookup.d.ts +4 -4
- package/dist-lib/lib/wav-encode.d.ts +1 -1
- package/dist-lib/lib/wav-encode.js +11 -10
- package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
- package/dist-lib/lib/web-fetch/extract.js +4 -2
- package/dist-lib/lib/web-fetch/images.js +8 -4
- package/dist-lib/lib/web-fetch/video.d.ts +2 -2
- package/dist-lib/lib/web-fetch/video.js +8 -4
- package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
- package/dist-lib/lib/web-search/engines/aol.js +1 -1
- package/dist-lib/lib/web-search/engines/bing.js +1 -1
- package/dist-lib/lib/web-search/engines/brave.js +4 -1
- package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
- package/dist-lib/lib/web-search/engines/google.js +7 -3
- package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
- package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
- package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
- package/dist-lib/lib/web-search/fetch-html.js +13 -7
- package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
- package/dist-lib/lib/web-search/orchestrator.js +3 -3
- package/dist-lib/lib/wire.d.ts +3 -2
- package/dist-lib/lib/wire.js +23 -15
- package/dist-lib/schema/baseline.js +10 -5
- package/dist-lib/schema/build.js +2 -1
- package/dist-lib/schema/harness.d.ts +15 -2
- package/dist-lib/schema/harness.js +55 -2
- package/dist-lib/schema/release-tags.d.ts +3 -2
- package/dist-lib/schema/release-tags.js +6 -3
- package/dist-lib/spec/bind.js +6 -0
- package/dist-lib/spec/calls.js +2 -0
- package/dist-lib/spec/hrpc/hrpc.json +26 -0
- package/dist-lib/spec/hrpc/index.js +27 -3
- package/dist-lib/spec/hrpc/messages.js +207 -8
- package/dist-lib/spec/hyperschema/index.js +207 -8
- package/dist-lib/spec/hyperschema/schema.json +165 -1
- package/dist-lib/spec/hyperschema/types.d.ts +42 -1
- package/package.json +13 -14
- package/worker/harness-sidecar.mjs +16 -22
- package/worker/stow-entry.mjs +7 -4
- package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
- /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsonrepair } from 'jsonrepair';
|
|
2
|
+
import { isRecord } from '../../guards.js';
|
|
2
3
|
import { hlog } from '../../log.js';
|
|
3
4
|
const STRUCTURED_TYPES = new Set(['object', 'array']);
|
|
4
5
|
export class ArgRepairHook {
|
|
@@ -9,11 +10,13 @@ export class ArgRepairHook {
|
|
|
9
10
|
let args = ctx.args;
|
|
10
11
|
for (const [key, schema] of Object.entries(ctx.parameters.properties)) {
|
|
11
12
|
const value = args[key];
|
|
12
|
-
if (typeof value !== 'string')
|
|
13
|
+
if (typeof value !== 'string') {
|
|
13
14
|
continue;
|
|
15
|
+
}
|
|
14
16
|
const types = structuredTypes(schema);
|
|
15
|
-
if (types.size === 0)
|
|
17
|
+
if (types.size === 0) {
|
|
16
18
|
continue;
|
|
19
|
+
}
|
|
17
20
|
const parsed = parseStructured(value, types);
|
|
18
21
|
if (parsed === null) {
|
|
19
22
|
hlog('arg repair declined', { tool: ctx.toolName, param: key, length: value.length });
|
|
@@ -21,8 +24,9 @@ export class ArgRepairHook {
|
|
|
21
24
|
}
|
|
22
25
|
args = { ...args, [key]: parsed };
|
|
23
26
|
}
|
|
24
|
-
if (args !== ctx.args)
|
|
27
|
+
if (args !== ctx.args) {
|
|
25
28
|
return { args };
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
function parseStructured(value, types) {
|
|
@@ -36,16 +40,18 @@ function parseStructured(value, types) {
|
|
|
36
40
|
return null;
|
|
37
41
|
}
|
|
38
42
|
const accepted = accept(parsed, types);
|
|
39
|
-
if (accepted !== null)
|
|
43
|
+
if (accepted !== null) {
|
|
40
44
|
return accepted;
|
|
45
|
+
}
|
|
41
46
|
if (Array.isArray(parsed) &&
|
|
42
47
|
types.has('object') &&
|
|
43
|
-
|
|
48
|
+
isPlainObject(parsed[0]) &&
|
|
44
49
|
typeof parsed.at(-1) === 'string') {
|
|
45
50
|
return parsed[0];
|
|
46
51
|
}
|
|
47
|
-
if (text[0] === '"' && types.has('object'))
|
|
52
|
+
if (text[0] === '"' && types.has('object')) {
|
|
48
53
|
return accept(read(`{${text}}`), types);
|
|
54
|
+
}
|
|
49
55
|
return null;
|
|
50
56
|
}
|
|
51
57
|
function read(text) {
|
|
@@ -57,7 +63,7 @@ function read(text) {
|
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
function accept(value, types) {
|
|
60
|
-
if (
|
|
66
|
+
if (isPlainObject(value) && types.has('object'))
|
|
61
67
|
return value;
|
|
62
68
|
if (Array.isArray(value) && types.has('array'))
|
|
63
69
|
return value;
|
|
@@ -65,24 +71,28 @@ function accept(value, types) {
|
|
|
65
71
|
}
|
|
66
72
|
function structuredTypes(schema) {
|
|
67
73
|
const found = new Set();
|
|
68
|
-
if (!
|
|
74
|
+
if (!isPlainObject(schema)) {
|
|
69
75
|
return found;
|
|
76
|
+
}
|
|
70
77
|
const declared = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
71
78
|
for (const type of declared) {
|
|
72
|
-
if (typeof type === 'string' && STRUCTURED_TYPES.has(type))
|
|
79
|
+
if (typeof type === 'string' && STRUCTURED_TYPES.has(type)) {
|
|
73
80
|
found.add(type);
|
|
81
|
+
}
|
|
74
82
|
}
|
|
75
83
|
for (const key of ['oneOf', 'anyOf']) {
|
|
76
84
|
const branches = schema[key];
|
|
77
|
-
if (!Array.isArray(branches))
|
|
85
|
+
if (!Array.isArray(branches)) {
|
|
78
86
|
continue;
|
|
87
|
+
}
|
|
79
88
|
for (const branch of branches) {
|
|
80
|
-
for (const type of structuredTypes(branch))
|
|
89
|
+
for (const type of structuredTypes(branch)) {
|
|
81
90
|
found.add(type);
|
|
91
|
+
}
|
|
82
92
|
}
|
|
83
93
|
}
|
|
84
94
|
return found;
|
|
85
95
|
}
|
|
86
|
-
function
|
|
87
|
-
return
|
|
96
|
+
function isPlainObject(value) {
|
|
97
|
+
return isRecord(value) && !Array.isArray(value);
|
|
88
98
|
}
|
|
@@ -11,8 +11,9 @@ export class SkillCatalogGuardHook {
|
|
|
11
11
|
const known = new Set(entries.map((entry) => (typeof entry === 'string' ? entry : entry.name)));
|
|
12
12
|
const raw = ctx.args.name ?? ctx.args.skill_name ?? ctx.args.skill;
|
|
13
13
|
const name = typeof raw === 'string' ? raw : '';
|
|
14
|
-
if (known.has(name))
|
|
14
|
+
if (known.has(name)) {
|
|
15
15
|
return;
|
|
16
|
+
}
|
|
16
17
|
const listed = [...known].join(', ');
|
|
17
18
|
const rest = listed
|
|
18
19
|
? `The only skills are: ${listed}. Nothing else needs a skill load — answer or act with the tools you already have.`
|
|
@@ -7,14 +7,15 @@ export type PreToolFold = {
|
|
|
7
7
|
ok: false;
|
|
8
8
|
rejection: HookRejection;
|
|
9
9
|
};
|
|
10
|
+
export interface PostToolFold {
|
|
11
|
+
result: unknown;
|
|
12
|
+
outcome?: ToolExecutionOutcome;
|
|
13
|
+
}
|
|
10
14
|
export declare class HookPipeline {
|
|
11
15
|
private readonly _behaviorHooks;
|
|
12
16
|
constructor(hooks?: readonly HarnessHook[]);
|
|
13
17
|
onRunStart(payload: Omit<RunStartPayload, 'phase'>): Promise<void>;
|
|
14
18
|
onRunEnd(payload: Omit<RunEndPayload, 'phase'>): Promise<void>;
|
|
15
19
|
runPreToolFold(payload: Omit<PreToolCallPayload, 'phase'>): Promise<PreToolFold>;
|
|
16
|
-
runPostToolFold(payload: Omit<PostToolCallPayload, 'phase'>): Promise<
|
|
17
|
-
result: unknown;
|
|
18
|
-
outcome?: ToolExecutionOutcome;
|
|
19
|
-
}>;
|
|
20
|
+
runPostToolFold(payload: Omit<PostToolCallPayload, 'phase'>): Promise<PostToolFold>;
|
|
20
21
|
}
|
|
@@ -9,13 +9,15 @@ export class HookPipeline {
|
|
|
9
9
|
}
|
|
10
10
|
async onRunStart(payload) {
|
|
11
11
|
const folded = await runHooks(this._behaviorHooks, { phase: 'runStart', ...payload });
|
|
12
|
-
if ('guidance' in folded)
|
|
12
|
+
if ('guidance' in folded) {
|
|
13
13
|
hlog('run hook failed', { stage: 'runStart', error: folded.error });
|
|
14
|
+
}
|
|
14
15
|
}
|
|
15
16
|
async onRunEnd(payload) {
|
|
16
17
|
const folded = await runHooks(this._behaviorHooks, { phase: 'runEnd', ...payload });
|
|
17
|
-
if ('guidance' in folded)
|
|
18
|
+
if ('guidance' in folded) {
|
|
18
19
|
hlog('run hook failed', { stage: 'runEnd', error: folded.error });
|
|
20
|
+
}
|
|
19
21
|
}
|
|
20
22
|
async runPreToolFold(payload) {
|
|
21
23
|
const folded = await runHooks(this._behaviorHooks, { phase: 'preToolCall', ...payload });
|
|
@@ -42,8 +44,9 @@ export class HookPipeline {
|
|
|
42
44
|
result = folded.result;
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
|
-
if (outcome !== undefined)
|
|
47
|
+
if (outcome !== undefined) {
|
|
46
48
|
outcome = normalizeToolExecutionOutcome(result, outcome);
|
|
49
|
+
}
|
|
47
50
|
return { result, outcome };
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorMessage, isRecord } from '../guards.js';
|
|
1
2
|
import { hlog } from '../log.js';
|
|
2
3
|
function cloneJson(value) {
|
|
3
4
|
try {
|
|
@@ -8,19 +9,20 @@ function cloneJson(value) {
|
|
|
8
9
|
return value;
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
|
-
function
|
|
12
|
-
return
|
|
12
|
+
function isPlainObject(value) {
|
|
13
|
+
return isRecord(value) && !Array.isArray(value);
|
|
13
14
|
}
|
|
14
15
|
function cloneArgs(args) {
|
|
15
16
|
const cloned = cloneJson(args);
|
|
16
|
-
return
|
|
17
|
+
return isPlainObject(cloned) ? cloned : args;
|
|
17
18
|
}
|
|
18
19
|
function isMessage(value) {
|
|
19
|
-
return
|
|
20
|
+
return isPlainObject(value) && typeof value.role === 'string' && typeof value.content === 'string';
|
|
20
21
|
}
|
|
21
22
|
function freshContext(ctx) {
|
|
22
|
-
if (ctx.phase === 'preToolCall')
|
|
23
|
+
if (ctx.phase === 'preToolCall') {
|
|
23
24
|
return { ...ctx, args: cloneArgs(ctx.args) };
|
|
25
|
+
}
|
|
24
26
|
if (ctx.phase === 'postToolCall') {
|
|
25
27
|
return { ...ctx, args: cloneArgs(ctx.args), result: cloneJson(ctx.result) };
|
|
26
28
|
}
|
|
@@ -52,7 +54,7 @@ function merge(current, returned) {
|
|
|
52
54
|
return current;
|
|
53
55
|
}
|
|
54
56
|
function rejectionOf(current, err) {
|
|
55
|
-
const message =
|
|
57
|
+
const message = errorMessage(err);
|
|
56
58
|
hlog('hook failed', { stage: current.phase, error: message });
|
|
57
59
|
if (current.phase === 'postToolCall') {
|
|
58
60
|
return {
|
|
@@ -69,10 +71,12 @@ export async function runHooks(hooks, ctx) {
|
|
|
69
71
|
continue;
|
|
70
72
|
try {
|
|
71
73
|
const returned = await hook.run(freshContext(current));
|
|
72
|
-
if (!returned || typeof returned !== 'object')
|
|
74
|
+
if (!returned || typeof returned !== 'object') {
|
|
73
75
|
continue;
|
|
74
|
-
|
|
76
|
+
}
|
|
77
|
+
if (isRejection(returned)) {
|
|
75
78
|
return rejection(returned);
|
|
79
|
+
}
|
|
76
80
|
current = merge(current, returned);
|
|
77
81
|
}
|
|
78
82
|
catch (err) {
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
+
import { isRecord } from './guards.js';
|
|
1
2
|
const EMA_WEIGHT = 0.25;
|
|
2
3
|
const STALL_MS = 2_000;
|
|
3
4
|
const NO_BYTES = { downloaded: 0, total: 0, aggregate: false };
|
|
4
5
|
export function transferProgress(event) {
|
|
5
6
|
const aggregate = aggregateTransfer(event.fileSetInfo) ?? aggregateTransfer(event.shardInfo);
|
|
6
|
-
if (aggregate)
|
|
7
|
+
if (aggregate) {
|
|
7
8
|
return aggregate;
|
|
9
|
+
}
|
|
8
10
|
const bytes = { downloaded: uint(event.downloaded), total: uint(event.total), aggregate: false };
|
|
9
11
|
const reported = reportedPercentage(event.percentage);
|
|
10
|
-
if (reported === null && bytes.downloaded === 0 && bytes.total === 0)
|
|
12
|
+
if (reported === null && bytes.downloaded === 0 && bytes.total === 0) {
|
|
11
13
|
return null;
|
|
14
|
+
}
|
|
12
15
|
return { ...bytes, percentage: resolvePercentage(reported, bytes) };
|
|
13
16
|
}
|
|
14
17
|
export function progressBytes(event) {
|
|
15
18
|
const transfer = transferProgress(event);
|
|
16
|
-
if (!transfer)
|
|
19
|
+
if (!transfer) {
|
|
17
20
|
return NO_BYTES;
|
|
21
|
+
}
|
|
18
22
|
return {
|
|
19
23
|
downloaded: transfer.downloaded,
|
|
20
24
|
total: transfer.total,
|
|
@@ -34,8 +38,9 @@ export class DownloadSpeedSampler {
|
|
|
34
38
|
return 0;
|
|
35
39
|
}
|
|
36
40
|
const elapsedMs = at - this._at;
|
|
37
|
-
if (elapsedMs <= 0)
|
|
41
|
+
if (elapsedMs <= 0) {
|
|
38
42
|
return Math.round(this._bytesPerSecond);
|
|
43
|
+
}
|
|
39
44
|
const delta = bytes.downloaded - this._downloaded;
|
|
40
45
|
if (delta === 0) {
|
|
41
46
|
if (elapsedMs >= STALL_MS) {
|
|
@@ -62,7 +67,7 @@ export class DownloadSpeedSampler {
|
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
function aggregateTransfer(value) {
|
|
65
|
-
if (!
|
|
70
|
+
if (!isRecord(value))
|
|
66
71
|
return null;
|
|
67
72
|
const bytes = {
|
|
68
73
|
downloaded: uint(value.overallDownloaded),
|
|
@@ -84,9 +89,6 @@ function reportedPercentage(value) {
|
|
|
84
89
|
function bytesPercentage(bytes) {
|
|
85
90
|
return bytes.total > 0 ? percentage((bytes.downloaded / bytes.total) * 100) : 0;
|
|
86
91
|
}
|
|
87
|
-
function record(value) {
|
|
88
|
-
return typeof value === 'object' && value !== null;
|
|
89
|
-
}
|
|
90
92
|
function uint(value) {
|
|
91
93
|
return typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
92
94
|
}
|
|
@@ -2,4 +2,4 @@ import type { ModelRegistryEntry } from '@qvac/sdk';
|
|
|
2
2
|
export type ModelSrc = string | (ModelRegistryEntry & {
|
|
3
3
|
src: string;
|
|
4
4
|
});
|
|
5
|
-
export declare function defaultContextSize(modelSrc: ModelSrc): number;
|
|
5
|
+
export declare function defaultContextSize(modelSrc: ModelSrc, trainedContext?: number): number;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
const TINY_MODEL_PARAMS_BILLIONS = 0.8;
|
|
2
2
|
const TINY_MODEL_CONTEXT_SIZE = 8_192;
|
|
3
3
|
const DESKTOP_DEFAULT_CONTEXT_SIZE = 51_200;
|
|
4
|
-
export function defaultContextSize(modelSrc) {
|
|
4
|
+
export function defaultContextSize(modelSrc, trainedContext) {
|
|
5
|
+
const size = tieredContextSize(modelSrc);
|
|
6
|
+
return trainedContext ? Math.min(size, trainedContext) : size;
|
|
7
|
+
}
|
|
8
|
+
function tieredContextSize(modelSrc) {
|
|
5
9
|
if (typeof modelSrc === 'string')
|
|
6
10
|
return DESKTOP_DEFAULT_CONTEXT_SIZE;
|
|
7
11
|
const params = Number.parseFloat(modelSrc.params);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CustomModelEntry } from './model-catalog.ts';
|
|
2
|
+
export interface CustomDownloads {
|
|
3
|
+
readonly files: ReadonlyArray<string>;
|
|
4
|
+
readonly dirs: ReadonlyArray<string>;
|
|
5
|
+
}
|
|
6
|
+
export declare function customFileCachePath(src: string, modelsDir: string | undefined): string | null;
|
|
7
|
+
export interface CustomShardCache {
|
|
8
|
+
readonly dir: string;
|
|
9
|
+
readonly files: ReadonlyArray<string>;
|
|
10
|
+
}
|
|
11
|
+
export declare function customShardCache(src: string, modelsDir: string | undefined): CustomShardCache | null;
|
|
12
|
+
export declare function customDownloads(entry: CustomModelEntry, modelsDir: string | undefined): Promise<CustomDownloads>;
|
|
13
|
+
export declare function isCustomModelCached(entry: CustomModelEntry, modelsDir: string | undefined): Promise<boolean>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { isRemoteSource, localSourcePath } from './gguf-header.js';
|
|
5
|
+
import { shardSet } from './shards.js';
|
|
6
|
+
const ARCHIVE = /\.(tar|tgz|zip)(\.\w+)?$/i;
|
|
7
|
+
export function customFileCachePath(src, modelsDir) {
|
|
8
|
+
if (!isRemoteSource(src)) {
|
|
9
|
+
return localSourcePath(src);
|
|
10
|
+
}
|
|
11
|
+
const filename = filenameOf(src);
|
|
12
|
+
if (!modelsDir || shardSet(src) || ARCHIVE.test(filename)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return path.join(modelsDir, `${shortHash(src)}_${filename}`);
|
|
16
|
+
}
|
|
17
|
+
export function customShardCache(src, modelsDir) {
|
|
18
|
+
const set = shardSet(src);
|
|
19
|
+
if (!modelsDir || !isRemoteSource(src) || !set) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const dir = path.join(modelsDir, 'sharded', shortHash(`${set.baseUrl}/${set.base}`));
|
|
23
|
+
return { dir, files: set.shards.map((shard) => path.join(dir, shard.filename)) };
|
|
24
|
+
}
|
|
25
|
+
export async function customDownloads(entry, modelsDir) {
|
|
26
|
+
const remote = sourcesOf(entry).filter(isRemoteSource);
|
|
27
|
+
const shardDirs = remote.flatMap((src) => customShardCache(src, modelsDir)?.dir ?? []);
|
|
28
|
+
const inShardDirs = await Promise.all(shardDirs.map(listFiles));
|
|
29
|
+
const singles = remote.flatMap((src) => customFileCachePath(src, modelsDir) ?? []);
|
|
30
|
+
return { files: [...singles, ...inShardDirs.flat()], dirs: shardDirs };
|
|
31
|
+
}
|
|
32
|
+
export async function isCustomModelCached(entry, modelsDir) {
|
|
33
|
+
const expected = sourcesOf(entry).flatMap((src) => customShardCache(src, modelsDir)?.files ?? [customFileCachePath(src, modelsDir)]);
|
|
34
|
+
const sizes = await Promise.all(expected.map(onDiskSize));
|
|
35
|
+
const present = sizes.filter((size) => size !== null);
|
|
36
|
+
if (present.length < sizes.length) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const total = present.reduce((sum, size) => sum + size, 0);
|
|
40
|
+
return entry.expectedSize ? total === entry.expectedSize : true;
|
|
41
|
+
}
|
|
42
|
+
function sourcesOf(entry) {
|
|
43
|
+
return entry.projectionSrc ? [entry.src, entry.projectionSrc] : [entry.src];
|
|
44
|
+
}
|
|
45
|
+
function filenameOf(src) {
|
|
46
|
+
return src.split('/').pop()?.split('?')[0] || 'model.gguf';
|
|
47
|
+
}
|
|
48
|
+
function shortHash(input) {
|
|
49
|
+
return crypto.createHash('sha256').update(input, 'utf8').digest('hex').slice(0, 16);
|
|
50
|
+
}
|
|
51
|
+
async function listFiles(dir) {
|
|
52
|
+
try {
|
|
53
|
+
const names = await fs.promises.readdir(dir);
|
|
54
|
+
return names.map((name) => path.join(dir, String(name)));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function onDiskSize(file) {
|
|
61
|
+
if (file === null)
|
|
62
|
+
return null;
|
|
63
|
+
try {
|
|
64
|
+
const stat = await fs.promises.stat(file);
|
|
65
|
+
return stat.isFile() ? stat.size : null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type AbortSignal } from 'bare-abort-controller';
|
|
2
|
+
export interface GgufHeader {
|
|
3
|
+
readonly architecture?: string;
|
|
4
|
+
readonly name?: string;
|
|
5
|
+
readonly type?: string;
|
|
6
|
+
readonly license?: string;
|
|
7
|
+
readonly contextLength?: number;
|
|
8
|
+
readonly sizeLabel?: string;
|
|
9
|
+
readonly tags?: ReadonlyArray<string>;
|
|
10
|
+
}
|
|
11
|
+
export type GgufRangeFetch = (url: string, init: {
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}) => Promise<{
|
|
15
|
+
ok: boolean;
|
|
16
|
+
status: number;
|
|
17
|
+
headers: {
|
|
18
|
+
get(name: string): string | null;
|
|
19
|
+
};
|
|
20
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
21
|
+
}>;
|
|
22
|
+
export declare const GGUF_HEADER_BYTES = 1048576;
|
|
23
|
+
export declare const GGUF_HEADER_TIMEOUT_MS = 30000;
|
|
24
|
+
export declare function isRemoteSource(src: string): boolean;
|
|
25
|
+
export declare function localSourcePath(src: string): string | null;
|
|
26
|
+
export interface ReadGgufHeaderOptions {
|
|
27
|
+
fetch?: GgufRangeFetch;
|
|
28
|
+
maxBytes?: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function readGgufHeaderBytes(src: string, opts?: ReadGgufHeaderOptions): Promise<Uint8Array | null>;
|
|
31
|
+
export declare function parseGgufHeader(bytes: Uint8Array): GgufHeader | null;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import { AbortController } from 'bare-abort-controller';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import defaultFetch from '#fetch';
|
|
8
|
+
const rangeFetch = defaultFetch;
|
|
9
|
+
export const GGUF_HEADER_BYTES = 1_048_576;
|
|
10
|
+
export const GGUF_HEADER_TIMEOUT_MS = 30_000;
|
|
11
|
+
const UINT8 = 0;
|
|
12
|
+
const INT8 = 1;
|
|
13
|
+
const UINT16 = 2;
|
|
14
|
+
const INT16 = 3;
|
|
15
|
+
const UINT32 = 4;
|
|
16
|
+
const INT32 = 5;
|
|
17
|
+
const FLOAT32 = 6;
|
|
18
|
+
const BOOL = 7;
|
|
19
|
+
const STRING = 8;
|
|
20
|
+
const ARRAY = 9;
|
|
21
|
+
const UINT64 = 10;
|
|
22
|
+
const INT64 = 11;
|
|
23
|
+
const FLOAT64 = 12;
|
|
24
|
+
export function isRemoteSource(src) {
|
|
25
|
+
return /^https?:\/\//i.test(src);
|
|
26
|
+
}
|
|
27
|
+
export function localSourcePath(src) {
|
|
28
|
+
if (/^file:/i.test(src)) {
|
|
29
|
+
return filePath(src);
|
|
30
|
+
}
|
|
31
|
+
if (/^[a-z][a-z0-9+.-]+:/i.test(src))
|
|
32
|
+
return null;
|
|
33
|
+
if (src === '~') {
|
|
34
|
+
return os.homedir();
|
|
35
|
+
}
|
|
36
|
+
if (src.startsWith('~/')) {
|
|
37
|
+
return path.join(os.homedir(), src.slice(2));
|
|
38
|
+
}
|
|
39
|
+
return src;
|
|
40
|
+
}
|
|
41
|
+
function filePath(src) {
|
|
42
|
+
try {
|
|
43
|
+
return fileURLToPath(src);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function readGgufHeaderBytes(src, opts = {}) {
|
|
50
|
+
const maxBytes = opts.maxBytes ?? GGUF_HEADER_BYTES;
|
|
51
|
+
return isRemoteSource(src)
|
|
52
|
+
? fetchHead(src, maxBytes, opts.fetch ?? rangeFetch)
|
|
53
|
+
: readLocalHead(src, maxBytes);
|
|
54
|
+
}
|
|
55
|
+
async function fetchHead(src, maxBytes, fetch) {
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timeout = setTimeout(() => controller.abort(undefined), GGUF_HEADER_TIMEOUT_MS);
|
|
58
|
+
try {
|
|
59
|
+
const response = await fetch(src, {
|
|
60
|
+
headers: { Range: `bytes=0-${maxBytes - 1}` },
|
|
61
|
+
signal: controller.signal
|
|
62
|
+
});
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const length = response.headers.get('content-length');
|
|
67
|
+
if (length === null || !(Number(length) <= maxBytes)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return new Uint8Array(await response.arrayBuffer()).subarray(0, maxBytes);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
clearTimeout(timeout);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function readLocalHead(src, maxBytes) {
|
|
80
|
+
const file = localSourcePath(src);
|
|
81
|
+
if (file === null) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
let handle;
|
|
85
|
+
try {
|
|
86
|
+
handle = await fs.promises.open(file, 'r');
|
|
87
|
+
const buffer = b4a.allocUnsafe(maxBytes);
|
|
88
|
+
const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
|
|
89
|
+
return buffer.subarray(0, bytesRead);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await handle?.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function parseGgufHeader(bytes) {
|
|
99
|
+
if (bytes.byteLength < 24)
|
|
100
|
+
return null;
|
|
101
|
+
if (b4a.toString(bytes.subarray(0, 4), 'latin1') !== 'GGUF')
|
|
102
|
+
return null;
|
|
103
|
+
const c = {
|
|
104
|
+
view: new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength),
|
|
105
|
+
bytes,
|
|
106
|
+
offset: 16
|
|
107
|
+
};
|
|
108
|
+
if (c.view.getUint32(4, true) < 2) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const count = readU64(c);
|
|
112
|
+
const header = {};
|
|
113
|
+
const contextLengths = new Map();
|
|
114
|
+
for (let i = 0; i < count; i += 1) {
|
|
115
|
+
const key = readString(c);
|
|
116
|
+
if (key === null || !has(c, 4)) {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
const value = readValue(c, readU32(c), key === 'general.tags');
|
|
120
|
+
if (!value.ok) {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
if (key === 'general.architecture') {
|
|
124
|
+
header.architecture = value.str;
|
|
125
|
+
}
|
|
126
|
+
else if (key === 'general.name') {
|
|
127
|
+
header.name = value.str;
|
|
128
|
+
}
|
|
129
|
+
else if (key === 'general.type') {
|
|
130
|
+
header.type = value.str;
|
|
131
|
+
}
|
|
132
|
+
else if (key === 'general.license') {
|
|
133
|
+
header.license = value.str;
|
|
134
|
+
}
|
|
135
|
+
else if (key === 'general.size_label') {
|
|
136
|
+
header.sizeLabel = value.str;
|
|
137
|
+
}
|
|
138
|
+
else if (key === 'general.tags') {
|
|
139
|
+
header.tags = value.strs;
|
|
140
|
+
}
|
|
141
|
+
else if (key.endsWith('.context_length')) {
|
|
142
|
+
contextLengths.set(key, value.num);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
header.contextLength =
|
|
146
|
+
contextLengths.get(`${header.architecture}.context_length`) ??
|
|
147
|
+
contextLengths.values().next().value;
|
|
148
|
+
return header;
|
|
149
|
+
}
|
|
150
|
+
function readValue(c, type, collect = false) {
|
|
151
|
+
switch (type) {
|
|
152
|
+
case UINT8:
|
|
153
|
+
case BOOL:
|
|
154
|
+
return fixed(c, 1, () => c.view.getUint8(c.offset));
|
|
155
|
+
case INT8:
|
|
156
|
+
return fixed(c, 1, () => c.view.getInt8(c.offset));
|
|
157
|
+
case UINT16:
|
|
158
|
+
return fixed(c, 2, () => c.view.getUint16(c.offset, true));
|
|
159
|
+
case INT16:
|
|
160
|
+
return fixed(c, 2, () => c.view.getInt16(c.offset, true));
|
|
161
|
+
case UINT32:
|
|
162
|
+
return fixed(c, 4, () => c.view.getUint32(c.offset, true));
|
|
163
|
+
case INT32:
|
|
164
|
+
return fixed(c, 4, () => c.view.getInt32(c.offset, true));
|
|
165
|
+
case FLOAT32:
|
|
166
|
+
return fixed(c, 4, () => c.view.getFloat32(c.offset, true));
|
|
167
|
+
case FLOAT64:
|
|
168
|
+
return fixed(c, 8, () => c.view.getFloat64(c.offset, true));
|
|
169
|
+
case UINT64:
|
|
170
|
+
case INT64:
|
|
171
|
+
return has(c, 8) ? { ok: true, num: readU64(c) } : { ok: false };
|
|
172
|
+
case STRING: {
|
|
173
|
+
const str = readString(c);
|
|
174
|
+
return str === null ? { ok: false } : { ok: true, str };
|
|
175
|
+
}
|
|
176
|
+
case ARRAY: {
|
|
177
|
+
if (!has(c, 12)) {
|
|
178
|
+
return { ok: false };
|
|
179
|
+
}
|
|
180
|
+
const elementType = readU32(c);
|
|
181
|
+
const length = readU64(c);
|
|
182
|
+
const strs = new Array();
|
|
183
|
+
for (let i = 0; i < length; i += 1) {
|
|
184
|
+
const element = readValue(c, elementType);
|
|
185
|
+
if (!element.ok) {
|
|
186
|
+
return { ok: false };
|
|
187
|
+
}
|
|
188
|
+
if (collect && element.str !== undefined) {
|
|
189
|
+
strs.push(element.str);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return collect ? { ok: true, strs } : { ok: true };
|
|
193
|
+
}
|
|
194
|
+
default:
|
|
195
|
+
return { ok: false };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function fixed(c, width, read) {
|
|
199
|
+
if (!has(c, width)) {
|
|
200
|
+
return { ok: false };
|
|
201
|
+
}
|
|
202
|
+
const num = read();
|
|
203
|
+
c.offset += width;
|
|
204
|
+
return { ok: true, num };
|
|
205
|
+
}
|
|
206
|
+
function has(c, n) {
|
|
207
|
+
return n >= 0 && c.offset + n <= c.bytes.byteLength;
|
|
208
|
+
}
|
|
209
|
+
function readU32(c) {
|
|
210
|
+
const value = c.view.getUint32(c.offset, true);
|
|
211
|
+
c.offset += 4;
|
|
212
|
+
return value;
|
|
213
|
+
}
|
|
214
|
+
function readU64(c) {
|
|
215
|
+
const lo = c.view.getUint32(c.offset, true);
|
|
216
|
+
const hi = c.view.getUint32(c.offset + 4, true);
|
|
217
|
+
c.offset += 8;
|
|
218
|
+
return hi * 2 ** 32 + lo;
|
|
219
|
+
}
|
|
220
|
+
function readString(c) {
|
|
221
|
+
if (!has(c, 8))
|
|
222
|
+
return null;
|
|
223
|
+
const length = readU64(c);
|
|
224
|
+
if (!has(c, length)) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const str = b4a.toString(c.bytes.subarray(c.offset, c.offset + length), 'utf8');
|
|
228
|
+
c.offset += length;
|
|
229
|
+
return str;
|
|
230
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface IdleReaperOptions {
|
|
2
|
+
sweep: () => Promise<void>;
|
|
3
|
+
waitMs: number;
|
|
4
|
+
onError: (err: unknown) => void;
|
|
5
|
+
now?: () => number;
|
|
6
|
+
tickMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface IdleReaper {
|
|
9
|
+
configure(timeoutMs: number): void;
|
|
10
|
+
enter(): Promise<void>;
|
|
11
|
+
exit(): void;
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function createIdleReaper({ sweep, waitMs, onError, now, tickMs }: IdleReaperOptions): IdleReaper;
|