@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
package/dist-lib/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { closePythonWorker } from './lib/tools/exec/adapters/python/worker/clien
|
|
|
8
8
|
import { completion, imageSettingsFrom } from './lib/capabilities/completion.js';
|
|
9
9
|
import { detectLanguage } from './lib/capabilities/detect-language.js';
|
|
10
10
|
import { invoke } from './lib/capabilities/invoke.js';
|
|
11
|
+
import { previewModelSource } from './lib/capabilities/preview-model-source.js';
|
|
11
12
|
import { expandImagePrompt } from './lib/capabilities/enhance.js';
|
|
12
13
|
import { cleanAnswer } from './lib/helpers/clean-answer.js';
|
|
13
14
|
import { isTranslationModel } from './lib/models/nmt-catalog.js';
|
|
@@ -21,10 +22,11 @@ import { createVoiceLane, packSamples, speakOnce, speakStreamOnce, transcribeOnc
|
|
|
21
22
|
import { pushQueue } from './lib/queue.js';
|
|
22
23
|
import { AbortedError, raceAbort } from './lib/helpers/race-abort.js';
|
|
23
24
|
import { audioGenDownloadMetadata, customProjectionMap, defaultProjectionMap, defaultTtsFor, defaultVoiceModel, diffusionDownloadMetadata, infraModelsFor, modelNameToCapabilityProfile, musicModelSet, setCustomModels } from './lib/models/model-catalog.js';
|
|
24
|
-
import { createModelStatusCache, invalidateModelStatusCache, listModels } from './lib/models/model-list.js';
|
|
25
|
+
import { createModelStatusCache, customModelRows, invalidateModelStatusCache, mergeCustomRows, listModels } from './lib/models/model-list.js';
|
|
25
26
|
import { deleteModelFiles, summarizeDelete } from './lib/models/model-delete.js';
|
|
26
27
|
import { createEmbedPool, createLoadTracker, createModelPool, createVoicePool, awaitModelCapacity, evictIdle, evictModelWithin, isResident, leaseResidentModel, leaseCount, loadedModels, modelKey, releaseModel, resolveLoadOptions, progressState } from './lib/models/model-pool.js';
|
|
27
28
|
import { resolveMainGpu } from './lib/models/gpu-selection.js';
|
|
29
|
+
import { createIdleReaper } from './lib/models/idle-unload.js';
|
|
28
30
|
import { openclCacheDir } from './lib/models/opencl-cache.js';
|
|
29
31
|
import { assertEmbedDevice, createEmbedConfig } from './lib/models/embed-config.js';
|
|
30
32
|
import { createGpuFaultState, isSdkGpuDeviceLostError, isSdkModelWedgedError } from './lib/models/sdk-faults.js';
|
|
@@ -38,6 +40,7 @@ import { ensureBundledSkillsDir } from './lib/tools/skills/bundled-dir.js';
|
|
|
38
40
|
import { ensureSkillsOverlayDir } from './lib/tools/skills/overlay-dir.js';
|
|
39
41
|
import { copyBaseForEdit, deleteOverlaySkill, listManagedSkills, readSkillFile, writeSkillFile } from './lib/tools/skills/overlay-crud.js';
|
|
40
42
|
import { watchSkillsOverlay } from './lib/tools/skills/overlay-watch.js';
|
|
43
|
+
import { errorMessage } from './lib/guards.js';
|
|
41
44
|
import { hlog } from './lib/log.js';
|
|
42
45
|
import { harness } from './spec/index.js';
|
|
43
46
|
import { installResolutionDetector, resolutionDetail } from './lib/resolution-detector.js';
|
|
@@ -104,6 +107,7 @@ export class Harness extends ReadyResource {
|
|
|
104
107
|
_voiceLane = createVoiceLane();
|
|
105
108
|
_state;
|
|
106
109
|
_skillsDir;
|
|
110
|
+
_modelsDir;
|
|
107
111
|
_overlayWatcher = null;
|
|
108
112
|
_supportedSkills;
|
|
109
113
|
_projectionModel;
|
|
@@ -131,7 +135,8 @@ export class Harness extends ReadyResource {
|
|
|
131
135
|
_capability = null;
|
|
132
136
|
_capabilityInflight = null;
|
|
133
137
|
_modelFaults = new Map();
|
|
134
|
-
|
|
138
|
+
_idle;
|
|
139
|
+
constructor({ tools = [], sdkLoader, skillsDir, modelsDir, projectionModel, imageModel, imageClamps, musicModel, musicClamps, llmDevice, llmGpuLayers, contextSize, kvCacheK, kvCacheV, embedDevice, embedGpuLayers, embedBatchSize, transcriptionDevice, completionModelCap, credentialStore, platform, gpuStats } = {}) {
|
|
135
140
|
super();
|
|
136
141
|
this._sdk = sdkLoader ?? createSdkLoader();
|
|
137
142
|
this._llmDevice = llmDevice;
|
|
@@ -175,8 +180,9 @@ export class Harness extends ReadyResource {
|
|
|
175
180
|
},
|
|
176
181
|
onLoaded: (model) => {
|
|
177
182
|
const transfer = imageTransfers.get(model);
|
|
178
|
-
if (!transfer)
|
|
183
|
+
if (!transfer) {
|
|
179
184
|
return;
|
|
185
|
+
}
|
|
180
186
|
imageTransfers.delete(model);
|
|
181
187
|
invalidateModelStatusCache(this._modelStatus);
|
|
182
188
|
this.emit('model', { ...transfer, status: 'downloaded' });
|
|
@@ -184,9 +190,14 @@ export class Harness extends ReadyResource {
|
|
|
184
190
|
onBeforeLoad: async (signal) => {
|
|
185
191
|
if (this._models.destroyed)
|
|
186
192
|
return;
|
|
187
|
-
const evicted = await awaitModelCapacity(this._models,
|
|
188
|
-
|
|
193
|
+
const evicted = await awaitModelCapacity(this._models, {
|
|
194
|
+
cap: 1,
|
|
195
|
+
deadlineMs: DIFFUSION_EVICT_WAIT_MS,
|
|
196
|
+
signal
|
|
197
|
+
});
|
|
198
|
+
if (signal?.aborted) {
|
|
189
199
|
throw new AbortedError();
|
|
200
|
+
}
|
|
190
201
|
if (!evicted) {
|
|
191
202
|
throw new Error('the chat model is busy; try the image again once the current reply finishes');
|
|
192
203
|
}
|
|
@@ -203,9 +214,14 @@ export class Harness extends ReadyResource {
|
|
|
203
214
|
onBeforeLoad: async (signal) => {
|
|
204
215
|
if (this._models.destroyed)
|
|
205
216
|
return;
|
|
206
|
-
const evicted = await awaitModelCapacity(this._models,
|
|
207
|
-
|
|
217
|
+
const evicted = await awaitModelCapacity(this._models, {
|
|
218
|
+
cap: 1,
|
|
219
|
+
deadlineMs: MUSIC_EVICT_WAIT_MS,
|
|
220
|
+
signal
|
|
221
|
+
});
|
|
222
|
+
if (signal?.aborted) {
|
|
208
223
|
throw new AbortedError();
|
|
224
|
+
}
|
|
209
225
|
if (!evicted) {
|
|
210
226
|
throw new Error('the chat model is busy; try the music again once the current reply finishes');
|
|
211
227
|
}
|
|
@@ -235,6 +251,7 @@ export class Harness extends ReadyResource {
|
|
|
235
251
|
this._projectionModel = projectionModel ?? defaultProjectionMap();
|
|
236
252
|
this._state = HarnessState.RESUME;
|
|
237
253
|
this._skillsDir = skillsDir;
|
|
254
|
+
this._modelsDir = modelsDir;
|
|
238
255
|
this._supportedSkills = [];
|
|
239
256
|
this._allSkills = [];
|
|
240
257
|
this._credentialStore = credentialStore;
|
|
@@ -242,13 +259,18 @@ export class Harness extends ReadyResource {
|
|
|
242
259
|
this._diffusionSdk = null;
|
|
243
260
|
this._musicSdk = null;
|
|
244
261
|
this._translationSdk = null;
|
|
245
|
-
}
|
|
246
|
-
async _open() {
|
|
247
|
-
installResolutionDetector(import.meta);
|
|
248
262
|
this._models = this._createPool();
|
|
249
263
|
this._embedModels = this._createEmbedPool();
|
|
250
264
|
this._ttsModels = this._createTtsPool();
|
|
251
265
|
this._transcriptionModels = this._createTranscriptionPool();
|
|
266
|
+
this._idle = createIdleReaper({
|
|
267
|
+
sweep: () => this._unloadIdleModels(),
|
|
268
|
+
waitMs: WEDGED_EVICT_WAIT_MS,
|
|
269
|
+
onError: (err) => hlog('idle unload failed', { err: errorMessage(err) })
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
async _open() {
|
|
273
|
+
installResolutionDetector(import.meta);
|
|
252
274
|
await this._reloadSkills();
|
|
253
275
|
await this._startOverlayWatch();
|
|
254
276
|
hlog('open', { skills: this._supportedSkills.length, tools: this._tools.length });
|
|
@@ -266,10 +288,7 @@ export class Harness extends ReadyResource {
|
|
|
266
288
|
if (!token)
|
|
267
289
|
continue;
|
|
268
290
|
const storeKey = scopeKey({ agentId, skill, key });
|
|
269
|
-
const record = {
|
|
270
|
-
token,
|
|
271
|
-
...(typeof expiresAt === 'number' && expiresAt > 0 ? { expiresAt } : {})
|
|
272
|
-
};
|
|
291
|
+
const record = typeof expiresAt === 'number' && expiresAt > 0 ? { token, expiresAt } : { token };
|
|
273
292
|
const current = previous.get(storeKey);
|
|
274
293
|
if (!current || current.token !== record.token || current.expiresAt !== record.expiresAt) {
|
|
275
294
|
resetCredentialHealthAfterSnapshot(key, agentId, skill);
|
|
@@ -307,48 +326,57 @@ export class Harness extends ReadyResource {
|
|
|
307
326
|
}
|
|
308
327
|
async writeSkillFile(name, content) {
|
|
309
328
|
const overlay = await ensureSkillsOverlayDir();
|
|
310
|
-
if (!overlay)
|
|
329
|
+
if (!overlay) {
|
|
311
330
|
throw new Error('no writable skills overlay is configured');
|
|
331
|
+
}
|
|
312
332
|
await writeSkillFile(overlay, name, content);
|
|
313
333
|
await this._reloadSkills();
|
|
314
334
|
return { ok: true };
|
|
315
335
|
}
|
|
316
336
|
async copyBaseForEdit(name) {
|
|
317
337
|
const overlay = await ensureSkillsOverlayDir();
|
|
318
|
-
if (!overlay)
|
|
338
|
+
if (!overlay) {
|
|
319
339
|
throw new Error('no writable skills overlay is configured');
|
|
340
|
+
}
|
|
320
341
|
const result = await copyBaseForEdit(await this._baseSkillsDir(), overlay, name);
|
|
321
|
-
if (result.copied)
|
|
342
|
+
if (result.copied) {
|
|
322
343
|
await this._reloadSkills();
|
|
344
|
+
}
|
|
323
345
|
return result;
|
|
324
346
|
}
|
|
325
347
|
async deleteOverlaySkill(name) {
|
|
326
348
|
const overlay = await ensureSkillsOverlayDir();
|
|
327
|
-
if (!overlay)
|
|
349
|
+
if (!overlay) {
|
|
328
350
|
throw new Error('no writable skills overlay is configured');
|
|
351
|
+
}
|
|
329
352
|
const result = await deleteOverlaySkill(overlay, name);
|
|
330
|
-
if (result.removed)
|
|
353
|
+
if (result.removed) {
|
|
331
354
|
await this._reloadSkills();
|
|
355
|
+
}
|
|
332
356
|
return result;
|
|
333
357
|
}
|
|
334
358
|
resolveCredential(key, ctx) {
|
|
335
|
-
if (this._credentialStore)
|
|
359
|
+
if (this._credentialStore) {
|
|
336
360
|
return this._credentialStore(key, ctx);
|
|
361
|
+
}
|
|
337
362
|
const agentId = ctx?.agentId ?? null;
|
|
338
363
|
const deviceShared = isDeviceSharedCredentialKey(key);
|
|
339
364
|
for (const skill of ctx?.skills ?? []) {
|
|
340
365
|
const owned = this._credentials.get(scopeKey({ agentId, skill, key })) ??
|
|
341
366
|
(agentId && deviceShared ? this._credentials.get(scopeKey({ skill, key })) : undefined);
|
|
342
|
-
if (owned)
|
|
367
|
+
if (owned) {
|
|
343
368
|
return owned;
|
|
369
|
+
}
|
|
344
370
|
}
|
|
345
371
|
if (agentId) {
|
|
346
372
|
const scoped = this._credentials.get(scopeKey({ agentId, key }));
|
|
347
|
-
if (scoped)
|
|
373
|
+
if (scoped) {
|
|
348
374
|
return scoped;
|
|
375
|
+
}
|
|
349
376
|
}
|
|
350
|
-
if (!agentId || deviceShared)
|
|
377
|
+
if (!agentId || deviceShared) {
|
|
351
378
|
return this._credentials.get(scopeKey({ key })) ?? null;
|
|
379
|
+
}
|
|
352
380
|
return null;
|
|
353
381
|
}
|
|
354
382
|
async _completionSkills(agentId, model) {
|
|
@@ -359,7 +387,8 @@ export class Harness extends ReadyResource {
|
|
|
359
387
|
}));
|
|
360
388
|
}
|
|
361
389
|
async skills(agentId, modelName, options) {
|
|
362
|
-
|
|
390
|
+
if (!this.opened)
|
|
391
|
+
await this.ready();
|
|
363
392
|
const platform = this._platform ?? detectPlatform();
|
|
364
393
|
const refresh = options?.refresh === true;
|
|
365
394
|
return resolveSkillStatuses(this._allSkills, {
|
|
@@ -373,7 +402,7 @@ export class Harness extends ReadyResource {
|
|
|
373
402
|
: await this._probeBinVersions(platform, refresh),
|
|
374
403
|
refresh,
|
|
375
404
|
skipCredentialProbe: options?.skipCredentialProbe === true,
|
|
376
|
-
|
|
405
|
+
activeProfile: modelName ? modelNameToCapabilityProfile(modelName) : undefined
|
|
377
406
|
});
|
|
378
407
|
}
|
|
379
408
|
async _reloadSkills() {
|
|
@@ -388,14 +417,15 @@ export class Harness extends ReadyResource {
|
|
|
388
417
|
return;
|
|
389
418
|
try {
|
|
390
419
|
const overlay = await ensureSkillsOverlayDir();
|
|
391
|
-
if (!overlay)
|
|
420
|
+
if (!overlay) {
|
|
392
421
|
return;
|
|
422
|
+
}
|
|
393
423
|
this._overlayWatcher = watchSkillsOverlay(overlay, () => {
|
|
394
|
-
this._reloadSkills().catch((err) => hlog('overlay reload failed', { err:
|
|
424
|
+
this._reloadSkills().catch((err) => hlog('overlay reload failed', { err: errorMessage(err) }));
|
|
395
425
|
});
|
|
396
426
|
}
|
|
397
427
|
catch (err) {
|
|
398
|
-
hlog('overlay watch unavailable', { err:
|
|
428
|
+
hlog('overlay watch unavailable', { err: errorMessage(err) });
|
|
399
429
|
}
|
|
400
430
|
}
|
|
401
431
|
async tools() {
|
|
@@ -408,8 +438,9 @@ export class Harness extends ReadyResource {
|
|
|
408
438
|
if (!refresh && this._binVersionCache && this._binVersionCache.expiresAt > now) {
|
|
409
439
|
return this._binVersionCache.bySkill;
|
|
410
440
|
}
|
|
411
|
-
if (this._binVersionInflight)
|
|
441
|
+
if (this._binVersionInflight) {
|
|
412
442
|
return this._binVersionInflight;
|
|
443
|
+
}
|
|
413
444
|
this._binVersionInflight = this._runBinVersionProbes(platform)
|
|
414
445
|
.then((bySkill) => {
|
|
415
446
|
this._binVersionCache = {
|
|
@@ -424,12 +455,14 @@ export class Harness extends ReadyResource {
|
|
|
424
455
|
return this._binVersionInflight;
|
|
425
456
|
}
|
|
426
457
|
async _runBinVersionProbes(platform) {
|
|
427
|
-
if (process.env.QVAC_NO_SKILL_BIN_PROBE)
|
|
458
|
+
if (process.env.QVAC_NO_SKILL_BIN_PROBE) {
|
|
428
459
|
return new Map();
|
|
460
|
+
}
|
|
429
461
|
const installed = new Set(this._tools.map((tool) => tool.name));
|
|
430
462
|
const candidates = versionProbeSkills(this._allSkills, installed, platform);
|
|
431
|
-
if (candidates.length === 0)
|
|
463
|
+
if (candidates.length === 0) {
|
|
432
464
|
return new Map();
|
|
465
|
+
}
|
|
433
466
|
let runtime;
|
|
434
467
|
try {
|
|
435
468
|
runtime = await bareRuntime();
|
|
@@ -449,8 +482,9 @@ export class Harness extends ReadyResource {
|
|
|
449
482
|
const bySkill = new Map();
|
|
450
483
|
for (const skill of candidates) {
|
|
451
484
|
const states = await probeBinVersions(skill.requires, { platform, run });
|
|
452
|
-
if (states.size > 0)
|
|
485
|
+
if (states.size > 0) {
|
|
453
486
|
bySkill.set(skill.name, states);
|
|
487
|
+
}
|
|
454
488
|
}
|
|
455
489
|
return bySkill;
|
|
456
490
|
}
|
|
@@ -469,17 +503,37 @@ export class Harness extends ReadyResource {
|
|
|
469
503
|
}
|
|
470
504
|
_projectionFor(model) {
|
|
471
505
|
const custom = this._customProjection[model];
|
|
472
|
-
if (custom)
|
|
506
|
+
if (custom) {
|
|
473
507
|
return custom;
|
|
508
|
+
}
|
|
474
509
|
const projection = this._projectionModel;
|
|
475
|
-
if (!projection)
|
|
510
|
+
if (!projection) {
|
|
476
511
|
return undefined;
|
|
512
|
+
}
|
|
477
513
|
return typeof projection === 'string' ? projection : projection[model];
|
|
478
514
|
}
|
|
479
515
|
setCustomModels(entries) {
|
|
480
516
|
setCustomModels(entries);
|
|
481
517
|
this._customProjection = customProjectionMap();
|
|
482
518
|
}
|
|
519
|
+
setModelIdleTimeout(ms) {
|
|
520
|
+
this._idle.configure(ms);
|
|
521
|
+
}
|
|
522
|
+
previewModelSource(context) {
|
|
523
|
+
return previewModelSource(context, {
|
|
524
|
+
assessFit: async ({ sha256Checksum, name, contextTokens }) => {
|
|
525
|
+
const sdk = await this._sdk.load();
|
|
526
|
+
const result = await sdk.assessModelFit({
|
|
527
|
+
models: [{ model: { sha256Checksum, name }, workload: { kind: 'llm', contextTokens } }]
|
|
528
|
+
});
|
|
529
|
+
const model = result.models[0];
|
|
530
|
+
return {
|
|
531
|
+
fit: model?.verdict ?? result.verdict,
|
|
532
|
+
fitReasons: [...(model?.reasons ?? result.reasons)]
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
}
|
|
483
537
|
async describe() {
|
|
484
538
|
if (!this.opened)
|
|
485
539
|
await this.ready();
|
|
@@ -501,11 +555,12 @@ export class Harness extends ReadyResource {
|
|
|
501
555
|
};
|
|
502
556
|
}
|
|
503
557
|
_loadedModels() {
|
|
504
|
-
const pools = [
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
558
|
+
const pools = [
|
|
559
|
+
this._embedModels,
|
|
560
|
+
...this._retiredEmbedModels,
|
|
561
|
+
this._ttsModels,
|
|
562
|
+
this._transcriptionModels
|
|
563
|
+
];
|
|
509
564
|
return [...loadedModels(this._models), ...pools.flatMap((pool) => loadedModels(pool))];
|
|
510
565
|
}
|
|
511
566
|
_loadPluginSdk(current, store, importPlugin) {
|
|
@@ -539,8 +594,9 @@ export class Harness extends ReadyResource {
|
|
|
539
594
|
const loadOpts = resolveLoadOptions({}, this._deviceConfig());
|
|
540
595
|
const key = modelKey(model, loadOpts);
|
|
541
596
|
const pool = this._models;
|
|
542
|
-
if (!isResident(pool, key))
|
|
597
|
+
if (!isResident(pool, key)) {
|
|
543
598
|
await this._makeRoomForCompletion(signal);
|
|
599
|
+
}
|
|
544
600
|
try {
|
|
545
601
|
const { modelId } = await raceAbort(pool.open(key, model, loadOpts).load(), signal);
|
|
546
602
|
const sdk = await this._sdk.load();
|
|
@@ -574,21 +630,40 @@ export class Harness extends ReadyResource {
|
|
|
574
630
|
}
|
|
575
631
|
_createPool() {
|
|
576
632
|
const loadSdk = this._sdk.load;
|
|
577
|
-
return createModelPool(loadSdk, (state) => this._notifyModel(state, true
|
|
633
|
+
return createModelPool(loadSdk, (state) => this._notifyModel(state, { languageModel: true }), {
|
|
634
|
+
deviceConfig: this._deviceConfig(),
|
|
635
|
+
loads: this._loads
|
|
636
|
+
});
|
|
578
637
|
}
|
|
579
638
|
_createEmbedPool() {
|
|
580
639
|
const loadSdk = () => this._sdk.loadEmbed();
|
|
581
|
-
return createEmbedPool(loadSdk, (state) => this._notifyModel(state
|
|
640
|
+
return createEmbedPool(loadSdk, (state) => this._notifyModel(state), {
|
|
641
|
+
faults: this._embedFaults,
|
|
642
|
+
batchSize: this._embedBatchSize,
|
|
643
|
+
openclCacheDir,
|
|
644
|
+
loads: this._loads
|
|
645
|
+
});
|
|
582
646
|
}
|
|
583
647
|
_createTtsPool() {
|
|
584
648
|
const loadSdk = () => this._sdk.loadVoice();
|
|
585
|
-
return createVoicePool(loadSdk, (state) => this._notifyModel(state
|
|
649
|
+
return createVoicePool(loadSdk, (state) => this._notifyModel(state), {
|
|
650
|
+
modelType: 'tts-ggml',
|
|
651
|
+
addon: 'tts',
|
|
652
|
+
modelConfig: TTS_MODEL_CONFIG,
|
|
653
|
+
loads: this._loads
|
|
654
|
+
});
|
|
586
655
|
}
|
|
587
656
|
_createTranscriptionPool() {
|
|
588
657
|
const loadSdk = () => this._sdk.loadVoice();
|
|
589
|
-
return createVoicePool(loadSdk, (state) => this._notifyModel({ ...state, model: TRANSCRIPTION_MODEL_LABEL }
|
|
658
|
+
return createVoicePool(loadSdk, (state) => this._notifyModel({ ...state, model: TRANSCRIPTION_MODEL_LABEL }), {
|
|
659
|
+
modelType: 'parakeet-transcription',
|
|
660
|
+
addon: 'parakeet',
|
|
661
|
+
modelConfig: { useGPU: this._transcriptionDevice !== 'cpu' },
|
|
662
|
+
openclCacheDir,
|
|
663
|
+
loads: this._loads
|
|
664
|
+
});
|
|
590
665
|
}
|
|
591
|
-
_notifyModel(state, languageModel) {
|
|
666
|
+
_notifyModel(state, { languageModel = false } = {}) {
|
|
592
667
|
const catalogChanged = languageModel && (state.status === 'ready' || state.status === 'unloaded');
|
|
593
668
|
if (catalogChanged) {
|
|
594
669
|
invalidateModelStatusCache(this._modelStatus);
|
|
@@ -628,14 +703,17 @@ export class Harness extends ReadyResource {
|
|
|
628
703
|
return this._enqueueEmbedControl(() => this._setEmbeddingDevice(device));
|
|
629
704
|
}
|
|
630
705
|
async _setEmbeddingDevice(device) {
|
|
631
|
-
|
|
706
|
+
if (!this.opened)
|
|
707
|
+
await this.ready();
|
|
632
708
|
if (this.closing)
|
|
633
709
|
throw new Error('harness is closing');
|
|
634
|
-
if (this._embedFaults.device === device)
|
|
710
|
+
if (this._embedFaults.device === device) {
|
|
635
711
|
return;
|
|
712
|
+
}
|
|
636
713
|
this._embedFaults = createGpuFaultState({ device, gpuLayers: this._embedGpuLayers });
|
|
637
|
-
if (this.suspended)
|
|
714
|
+
if (this.suspended) {
|
|
638
715
|
return;
|
|
716
|
+
}
|
|
639
717
|
const retired = this._embedModels;
|
|
640
718
|
this._retiredEmbedModels.add(retired);
|
|
641
719
|
this._embedModels = this._createEmbedPool();
|
|
@@ -649,15 +727,18 @@ export class Harness extends ReadyResource {
|
|
|
649
727
|
}
|
|
650
728
|
async suspend() {
|
|
651
729
|
hlog('suspend called');
|
|
652
|
-
if (this._state === HarnessState.SUSPEND)
|
|
730
|
+
if (this._state === HarnessState.SUSPEND) {
|
|
653
731
|
return;
|
|
654
|
-
|
|
732
|
+
}
|
|
733
|
+
if (this.closing) {
|
|
655
734
|
return;
|
|
735
|
+
}
|
|
656
736
|
this._state = HarnessState.SUSPEND;
|
|
657
737
|
hlog('suspending', { models: loadedModels(this._models).map((entry) => entry.model) });
|
|
658
738
|
const pending = this._sdk.peek();
|
|
659
|
-
if (pending)
|
|
739
|
+
if (pending) {
|
|
660
740
|
this._loads.drain(await pending);
|
|
741
|
+
}
|
|
661
742
|
await this._releaseRuntimes();
|
|
662
743
|
await this._sdkLifecycle('suspend');
|
|
663
744
|
invalidateModelStatusCache(this._modelStatus);
|
|
@@ -665,18 +746,20 @@ export class Harness extends ReadyResource {
|
|
|
665
746
|
}
|
|
666
747
|
async _sdkLifecycle(op) {
|
|
667
748
|
const sdk = await this._sdk.peek()?.catch(noop);
|
|
668
|
-
if (!sdk)
|
|
749
|
+
if (!sdk) {
|
|
669
750
|
return;
|
|
751
|
+
}
|
|
670
752
|
try {
|
|
671
753
|
await sdk[op]();
|
|
672
754
|
}
|
|
673
755
|
catch (err) {
|
|
674
|
-
hlog(`sdk ${op} failed`, { err:
|
|
675
|
-
if (op === 'suspend')
|
|
756
|
+
hlog(`sdk ${op} failed`, { err: errorMessage(err) });
|
|
757
|
+
if (op === 'suspend') {
|
|
676
758
|
return;
|
|
759
|
+
}
|
|
677
760
|
await sdk.resume().catch((retryErr) => {
|
|
678
761
|
hlog('sdk resume retry failed', {
|
|
679
|
-
err:
|
|
762
|
+
err: errorMessage(retryErr)
|
|
680
763
|
});
|
|
681
764
|
});
|
|
682
765
|
}
|
|
@@ -695,15 +778,38 @@ export class Harness extends ReadyResource {
|
|
|
695
778
|
if (outcome.status !== 'rejected')
|
|
696
779
|
continue;
|
|
697
780
|
const err = outcome.reason;
|
|
698
|
-
hlog('unload failed', { err:
|
|
781
|
+
hlog('unload failed', { err: errorMessage(err) });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
async _unloadIdleModels() {
|
|
785
|
+
if (!this.opened || this.closing || this.suspended)
|
|
786
|
+
return;
|
|
787
|
+
hlog('idle unload', { models: this._loadedModels().map((entry) => entry.model) });
|
|
788
|
+
const released = await Promise.allSettled([
|
|
789
|
+
evictIdle(this._models),
|
|
790
|
+
evictIdle(this._embedModels),
|
|
791
|
+
...[...this._retiredEmbedModels].map((pool) => this._collectRetiredEmbedPool(pool)),
|
|
792
|
+
evictIdle(this._ttsModels),
|
|
793
|
+
evictIdle(this._transcriptionModels),
|
|
794
|
+
this._imageRuntime?.unloadIdle(),
|
|
795
|
+
this._musicRuntime?.unloadIdle(),
|
|
796
|
+
this._translationRuntime.unload()
|
|
797
|
+
]);
|
|
798
|
+
for (const outcome of released) {
|
|
799
|
+
if (outcome.status !== 'rejected')
|
|
800
|
+
continue;
|
|
801
|
+
hlog('idle unload failed', { err: errorMessage(outcome.reason) });
|
|
699
802
|
}
|
|
803
|
+
invalidateModelStatusCache(this._modelStatus);
|
|
700
804
|
}
|
|
701
805
|
async resume() {
|
|
702
806
|
hlog('resume called');
|
|
703
|
-
if (this._state === HarnessState.RESUME)
|
|
807
|
+
if (this._state === HarnessState.RESUME) {
|
|
704
808
|
return;
|
|
705
|
-
|
|
809
|
+
}
|
|
810
|
+
if (this.closing) {
|
|
706
811
|
return;
|
|
812
|
+
}
|
|
707
813
|
if (this._state === HarnessState.PRESUSPEND) {
|
|
708
814
|
this._state = HarnessState.RESUME;
|
|
709
815
|
return;
|
|
@@ -726,22 +832,26 @@ export class Harness extends ReadyResource {
|
|
|
726
832
|
}
|
|
727
833
|
async _close() {
|
|
728
834
|
hlog('closing');
|
|
835
|
+
this._idle.close();
|
|
729
836
|
this._overlayWatcher?.stop();
|
|
730
837
|
this._overlayWatcher = null;
|
|
731
838
|
const pending = this._sdk.peek();
|
|
732
|
-
if (pending)
|
|
839
|
+
if (pending) {
|
|
733
840
|
this._loads.drain(await pending);
|
|
841
|
+
}
|
|
734
842
|
await Promise.all([this._releaseRuntimes(), closePythonWorker()]);
|
|
735
843
|
const sdk = await this._sdk.peek()?.catch(noop);
|
|
736
|
-
if (sdk)
|
|
844
|
+
if (sdk) {
|
|
737
845
|
await sdk.close();
|
|
846
|
+
}
|
|
738
847
|
}
|
|
739
848
|
download(args) {
|
|
740
849
|
const projection = (args.addon ?? 'llm') === 'llm' ? this._projectionFor(args.model) : undefined;
|
|
741
850
|
const multiFile = diffusionDownloadMetadata(args.model, args.addon) ??
|
|
742
851
|
audioGenDownloadMetadata(args.model, args.addon);
|
|
743
852
|
return download(async () => {
|
|
744
|
-
|
|
853
|
+
if (!this.opened)
|
|
854
|
+
await this.ready();
|
|
745
855
|
return this._sdk.load();
|
|
746
856
|
}, {
|
|
747
857
|
...args,
|
|
@@ -750,12 +860,17 @@ export class Harness extends ReadyResource {
|
|
|
750
860
|
assetSizes: multiFile?.assetSizes
|
|
751
861
|
}, () => invalidateModelStatusCache(this._modelStatus));
|
|
752
862
|
}
|
|
753
|
-
|
|
754
|
-
|
|
863
|
+
embed(context) {
|
|
864
|
+
return this._busy(() => this._embed(context));
|
|
865
|
+
}
|
|
866
|
+
async _embed({ model, texts, signal }) {
|
|
867
|
+
if (!this.opened)
|
|
868
|
+
await this.ready();
|
|
755
869
|
if (this.suspended)
|
|
756
870
|
throw new Error('harness is suspended');
|
|
757
|
-
if (texts.length === 0)
|
|
871
|
+
if (texts.length === 0) {
|
|
758
872
|
return [];
|
|
873
|
+
}
|
|
759
874
|
const pool = this._embedModels;
|
|
760
875
|
const faults = this._embedFaults;
|
|
761
876
|
const generation = faults.generation;
|
|
@@ -766,7 +881,7 @@ export class Harness extends ReadyResource {
|
|
|
766
881
|
let releaseAfter = null;
|
|
767
882
|
try {
|
|
768
883
|
loaded = await raceAbort(handle.load(), signal);
|
|
769
|
-
const sdk =
|
|
884
|
+
const sdk = await this._sdk.loadEmbed();
|
|
770
885
|
job = this._embedLane(sdk, { modelId: loaded.modelId, texts });
|
|
771
886
|
const vectors = await raceAbort(job, signal);
|
|
772
887
|
faults.reset();
|
|
@@ -789,15 +904,18 @@ export class Harness extends ReadyResource {
|
|
|
789
904
|
throw err;
|
|
790
905
|
}
|
|
791
906
|
finally {
|
|
792
|
-
if (releaseAfter)
|
|
907
|
+
if (releaseAfter) {
|
|
793
908
|
this._releaseEmbedResourceAfter(releaseAfter, pool, key);
|
|
794
|
-
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
795
911
|
await this._releaseEmbedResource(pool, key);
|
|
912
|
+
}
|
|
796
913
|
}
|
|
797
914
|
}
|
|
798
915
|
async _defaultVoiceSrc(category) {
|
|
799
|
-
if (category !== 'tts')
|
|
916
|
+
if (category !== 'tts') {
|
|
800
917
|
return defaultVoiceModel(category);
|
|
918
|
+
}
|
|
801
919
|
try {
|
|
802
920
|
const { capabilityProfile } = await this.capability();
|
|
803
921
|
return defaultTtsFor(capabilityProfile).asset.src;
|
|
@@ -806,8 +924,9 @@ export class Harness extends ReadyResource {
|
|
|
806
924
|
return defaultVoiceModel('tts');
|
|
807
925
|
}
|
|
808
926
|
}
|
|
809
|
-
async _voiceJob(pool, category, model, signal, run) {
|
|
810
|
-
|
|
927
|
+
async _voiceJob(pool, { category, model, signal }, run) {
|
|
928
|
+
if (!this.opened)
|
|
929
|
+
await this.ready();
|
|
811
930
|
if (this.suspended)
|
|
812
931
|
throw new Error('harness is suspended');
|
|
813
932
|
const name = model || (await this._defaultVoiceSrc(category));
|
|
@@ -826,8 +945,9 @@ export class Harness extends ReadyResource {
|
|
|
826
945
|
}
|
|
827
946
|
catch (err) {
|
|
828
947
|
if (err instanceof AbortedError) {
|
|
829
|
-
if (sdk && modelId)
|
|
948
|
+
if (sdk && modelId) {
|
|
830
949
|
void sdk.cancel({ modelId, operation: 'inference' }).catch(noop);
|
|
950
|
+
}
|
|
831
951
|
releaseAfter = job;
|
|
832
952
|
}
|
|
833
953
|
throw err;
|
|
@@ -836,19 +956,25 @@ export class Harness extends ReadyResource {
|
|
|
836
956
|
const release = () => {
|
|
837
957
|
releaseModel(pool, name);
|
|
838
958
|
};
|
|
839
|
-
if (releaseAfter)
|
|
959
|
+
if (releaseAfter) {
|
|
840
960
|
void releaseAfter.then(release, release);
|
|
841
|
-
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
842
963
|
release();
|
|
964
|
+
}
|
|
843
965
|
}
|
|
844
966
|
}
|
|
845
|
-
|
|
846
|
-
return this.
|
|
967
|
+
transcribe(context) {
|
|
968
|
+
return this._busy(() => this._transcribe(context));
|
|
969
|
+
}
|
|
970
|
+
async _transcribe({ model, audio, signal }) {
|
|
971
|
+
return this._voiceJob(this._transcriptionModels, { category: 'transcription', model, signal }, async (sdk, id) => {
|
|
847
972
|
const pcm = await pcmForTranscription(audio);
|
|
848
|
-
if (signal?.aborted)
|
|
973
|
+
if (signal?.aborted) {
|
|
849
974
|
throw new AbortedError();
|
|
975
|
+
}
|
|
850
976
|
if (pcm && pcm.length >= TRANSCRIBE_STREAM_MIN_BYTES) {
|
|
851
|
-
return transcribeStreaming(sdk,
|
|
977
|
+
return transcribeStreaming(sdk, pcm, { modelId: id, signal });
|
|
852
978
|
}
|
|
853
979
|
return transcribeOnce(sdk, id, pcm ? buildWavBuffer(pcm) : audio);
|
|
854
980
|
});
|
|
@@ -856,12 +982,19 @@ export class Harness extends ReadyResource {
|
|
|
856
982
|
detectLanguage(context) {
|
|
857
983
|
return detectLanguage(context);
|
|
858
984
|
}
|
|
859
|
-
|
|
860
|
-
|
|
985
|
+
speak(context) {
|
|
986
|
+
return this._busy(() => this._speak(context));
|
|
987
|
+
}
|
|
988
|
+
async _speak({ model, text, signal }) {
|
|
989
|
+
const samples = await this._voiceJob(this._ttsModels, { category: 'tts', model, signal }, (sdk, id) => speakOnce(sdk, id, text));
|
|
861
990
|
return { audio: packSamples(samples), sampleRate: SUPERTONIC_SAMPLE_RATE };
|
|
862
991
|
}
|
|
863
|
-
|
|
864
|
-
|
|
992
|
+
speakStream(context) {
|
|
993
|
+
return this._busyStream(() => this._speakStream(context));
|
|
994
|
+
}
|
|
995
|
+
async *_speakStream({ model, text, signal }) {
|
|
996
|
+
if (!this.opened)
|
|
997
|
+
await this.ready();
|
|
865
998
|
if (this.suspended)
|
|
866
999
|
throw new Error('harness is suspended');
|
|
867
1000
|
const name = model || (await this._defaultVoiceSrc('tts'));
|
|
@@ -871,18 +1004,21 @@ export class Harness extends ReadyResource {
|
|
|
871
1004
|
let sdk = null;
|
|
872
1005
|
let modelId = null;
|
|
873
1006
|
const cancel = () => {
|
|
874
|
-
if (sdk && modelId)
|
|
875
|
-
void sdk.cancel({ modelId, operation: 'inference' }).catch(
|
|
1007
|
+
if (sdk && modelId) {
|
|
1008
|
+
void sdk.cancel({ modelId, operation: 'inference' }).catch(noop);
|
|
1009
|
+
}
|
|
876
1010
|
};
|
|
877
1011
|
signal?.addEventListener('abort', cancel, { once: true });
|
|
878
1012
|
const job = this._voiceLane(async () => {
|
|
879
1013
|
try {
|
|
880
1014
|
modelId = (await handle.load()).modelId;
|
|
881
1015
|
sdk = (await this._sdk.loadVoice());
|
|
882
|
-
if (signal?.aborted)
|
|
1016
|
+
if (signal?.aborted) {
|
|
883
1017
|
return;
|
|
884
|
-
|
|
1018
|
+
}
|
|
1019
|
+
for await (const chunk of speakStreamOnce(sdk, modelId, text)) {
|
|
885
1020
|
chunks.push(chunk);
|
|
1021
|
+
}
|
|
886
1022
|
}
|
|
887
1023
|
finally {
|
|
888
1024
|
chunks.end();
|
|
@@ -896,31 +1032,46 @@ export class Harness extends ReadyResource {
|
|
|
896
1032
|
}
|
|
897
1033
|
finally {
|
|
898
1034
|
signal?.removeEventListener('abort', cancel);
|
|
899
|
-
if (!settled)
|
|
1035
|
+
if (!settled) {
|
|
900
1036
|
cancel();
|
|
1037
|
+
}
|
|
901
1038
|
void job.catch(noop).finally(() => {
|
|
902
1039
|
releaseModel(pool, name);
|
|
903
1040
|
});
|
|
904
1041
|
}
|
|
905
1042
|
}
|
|
906
1043
|
async list(options = {}) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1044
|
+
if (!this.opened)
|
|
1045
|
+
await this.ready();
|
|
1046
|
+
const sdk = await this._sdk.load();
|
|
1047
|
+
const [rows, custom] = await Promise.all([
|
|
1048
|
+
listModels(sdk, options, {
|
|
1049
|
+
cache: this._modelStatus,
|
|
1050
|
+
projectionFor: (name) => this._projectionFor(name)
|
|
1051
|
+
}),
|
|
1052
|
+
customModelRows(options, {
|
|
1053
|
+
modelsDir: this._modelsDir,
|
|
1054
|
+
loaded: new Set(this._loadedModels().map((entry) => entry.model))
|
|
1055
|
+
})
|
|
1056
|
+
]);
|
|
1057
|
+
return mergeCustomRows(rows, custom);
|
|
910
1058
|
}
|
|
911
1059
|
async deleteModel(name) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const
|
|
1060
|
+
if (!this.opened)
|
|
1061
|
+
await this.ready();
|
|
1062
|
+
const sdk = await this._sdk.load();
|
|
1063
|
+
const result = await deleteModelFiles(sdk, name, { modelsDir: this._modelsDir });
|
|
915
1064
|
invalidateModelStatusCache(this._modelStatus);
|
|
916
1065
|
return summarizeDelete(result);
|
|
917
1066
|
}
|
|
918
1067
|
async clearKvCache() {
|
|
919
|
-
|
|
1068
|
+
if (!this.opened)
|
|
1069
|
+
await this.ready();
|
|
920
1070
|
return deleteAllKvCaches(() => this._sdk.load());
|
|
921
1071
|
}
|
|
922
1072
|
async deleteKvCache(kvCacheKey) {
|
|
923
|
-
|
|
1073
|
+
if (!this.opened)
|
|
1074
|
+
await this.ready();
|
|
924
1075
|
return deleteKvCacheKey(() => this._sdk.load(), kvCacheKey);
|
|
925
1076
|
}
|
|
926
1077
|
async infraModels(profile) {
|
|
@@ -964,8 +1115,12 @@ export class Harness extends ReadyResource {
|
|
|
964
1115
|
});
|
|
965
1116
|
return this._capabilityInflight;
|
|
966
1117
|
}
|
|
967
|
-
|
|
968
|
-
|
|
1118
|
+
warmModel(context) {
|
|
1119
|
+
return this._busy(() => this._warmModel(context));
|
|
1120
|
+
}
|
|
1121
|
+
async _warmModel({ model, agent, signal }) {
|
|
1122
|
+
if (!this.opened)
|
|
1123
|
+
await this.ready();
|
|
969
1124
|
if (this.suspended)
|
|
970
1125
|
return;
|
|
971
1126
|
const projectionModel = this._projectionFor(model);
|
|
@@ -979,8 +1134,9 @@ export class Harness extends ReadyResource {
|
|
|
979
1134
|
const pool = this._models;
|
|
980
1135
|
hlog('warm model', { model, resident: isResident(pool, key) });
|
|
981
1136
|
if (!isResident(pool, key)) {
|
|
982
|
-
if (!(await this._makeRoomForCompletion(signal, WARM_ROOM_WAIT_MS)))
|
|
1137
|
+
if (!(await this._makeRoomForCompletion(signal, WARM_ROOM_WAIT_MS))) {
|
|
983
1138
|
return;
|
|
1139
|
+
}
|
|
984
1140
|
await this._imageRuntime?.unloadIdle().catch(noop);
|
|
985
1141
|
}
|
|
986
1142
|
try {
|
|
@@ -990,11 +1146,15 @@ export class Harness extends ReadyResource {
|
|
|
990
1146
|
releaseModel(pool, key);
|
|
991
1147
|
}
|
|
992
1148
|
}
|
|
993
|
-
|
|
1149
|
+
invoke(context) {
|
|
1150
|
+
return this._busyStream(() => this._invoke(context));
|
|
1151
|
+
}
|
|
1152
|
+
async *_invoke({ capability, args, enhance, agent, attach, fetchAttachment, progress, signal }) {
|
|
994
1153
|
if (!this.opened)
|
|
995
1154
|
await this.ready();
|
|
996
|
-
if (this.suspended)
|
|
1155
|
+
if (this.suspended) {
|
|
997
1156
|
return yield { type: 'aborted' };
|
|
1157
|
+
}
|
|
998
1158
|
hlog('invoke start', { capability, enhance });
|
|
999
1159
|
try {
|
|
1000
1160
|
yield* invoke({
|
|
@@ -1016,8 +1176,12 @@ export class Harness extends ReadyResource {
|
|
|
1016
1176
|
hlog('invoke end', { capability });
|
|
1017
1177
|
}
|
|
1018
1178
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1179
|
+
completion(context) {
|
|
1180
|
+
return this._busyStream(() => this._completion(context));
|
|
1181
|
+
}
|
|
1182
|
+
async *_completion({ model, agent, messages, cacheKey, thinkingDefault, toolConfigs, generationParams, responseFormat, captureInferenceCalls, grantedDirs, grantedDrives, driveOp, grantedWriteDirs, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal }) {
|
|
1183
|
+
if (!this.opened)
|
|
1184
|
+
await this.ready();
|
|
1021
1185
|
if (this.suspended)
|
|
1022
1186
|
throw new Error('harness is suspended');
|
|
1023
1187
|
const projectionModel = this._projectionFor(model);
|
|
@@ -1112,16 +1276,12 @@ export class Harness extends ReadyResource {
|
|
|
1112
1276
|
yield { type: 'aborted' };
|
|
1113
1277
|
}
|
|
1114
1278
|
else {
|
|
1115
|
-
const message =
|
|
1279
|
+
const message = errorMessage(err);
|
|
1116
1280
|
hlog('completion failed', { model, ...resolutionDetail(err), err: message });
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
process.exit(1);
|
|
1122
|
-
}
|
|
1123
|
-
else {
|
|
1124
|
-
yield { type: 'error', message };
|
|
1281
|
+
const wedged = isSdkModelWedgedError(err) ? await this._evictWedgedModel(model, key) : null;
|
|
1282
|
+
yield { type: 'error', message: wedged ? wedged.message : message };
|
|
1283
|
+
if (wedged?.fatal) {
|
|
1284
|
+
process.exit(1);
|
|
1125
1285
|
}
|
|
1126
1286
|
}
|
|
1127
1287
|
}
|
|
@@ -1135,14 +1295,19 @@ export class Harness extends ReadyResource {
|
|
|
1135
1295
|
_residentEnhancer() {
|
|
1136
1296
|
return async (prompt, signal) => {
|
|
1137
1297
|
const lease = await leaseResidentModel(this._models, (model) => !isTranslationModel(model));
|
|
1138
|
-
if (!lease)
|
|
1298
|
+
if (!lease) {
|
|
1139
1299
|
return null;
|
|
1300
|
+
}
|
|
1140
1301
|
try {
|
|
1141
1302
|
const sdk = await this._sdk.load();
|
|
1142
|
-
return await expandImagePrompt(sdk,
|
|
1303
|
+
return await expandImagePrompt(sdk, prompt, {
|
|
1304
|
+
modelId: lease.modelId,
|
|
1305
|
+
model: lease.model,
|
|
1306
|
+
signal
|
|
1307
|
+
});
|
|
1143
1308
|
}
|
|
1144
1309
|
catch (err) {
|
|
1145
|
-
hlog('enhance failed', { err:
|
|
1310
|
+
hlog('enhance failed', { err: errorMessage(err) });
|
|
1146
1311
|
return null;
|
|
1147
1312
|
}
|
|
1148
1313
|
finally {
|
|
@@ -1151,7 +1316,11 @@ export class Harness extends ReadyResource {
|
|
|
1151
1316
|
};
|
|
1152
1317
|
}
|
|
1153
1318
|
async _makeRoomForCompletion(signal, deadlineMs = MODEL_ROOM_WAIT_MS) {
|
|
1154
|
-
const roomed = await awaitModelCapacity(this._models,
|
|
1319
|
+
const roomed = await awaitModelCapacity(this._models, {
|
|
1320
|
+
cap: this._completionModelCap,
|
|
1321
|
+
deadlineMs,
|
|
1322
|
+
signal
|
|
1323
|
+
});
|
|
1155
1324
|
if (!roomed) {
|
|
1156
1325
|
hlog('model room wait gave up', {
|
|
1157
1326
|
cap: this._completionModelCap,
|
|
@@ -1164,10 +1333,13 @@ export class Harness extends ReadyResource {
|
|
|
1164
1333
|
const faults = (this._modelFaults.get(model) ?? 0) + 1;
|
|
1165
1334
|
this._modelFaults.set(model, faults);
|
|
1166
1335
|
const leased = leaseCount(this._models, key) > 0;
|
|
1167
|
-
const evicted = leased
|
|
1336
|
+
const evicted = leased
|
|
1337
|
+
? false
|
|
1338
|
+
: await evictModelWithin(this._models, key, { deadlineMs: WEDGED_EVICT_WAIT_MS });
|
|
1168
1339
|
hlog('wedged model evicted', { model, faults, evicted, leased });
|
|
1169
|
-
if (leased)
|
|
1340
|
+
if (leased) {
|
|
1170
1341
|
this._evictAfterLeases(model, key);
|
|
1342
|
+
}
|
|
1171
1343
|
if (!evicted && !leased) {
|
|
1172
1344
|
return {
|
|
1173
1345
|
message: `${model} is stuck in device memory — restarting the model runtime`,
|
|
@@ -1182,9 +1354,27 @@ export class Harness extends ReadyResource {
|
|
|
1182
1354
|
};
|
|
1183
1355
|
}
|
|
1184
1356
|
_evictAfterLeases(model, key) {
|
|
1185
|
-
void evictModelWithin(this._models, key, WEDGED_EVICT_LEASED_MS)
|
|
1357
|
+
void evictModelWithin(this._models, key, { deadlineMs: WEDGED_EVICT_LEASED_MS })
|
|
1186
1358
|
.then((evicted) => hlog('wedged model evicted after leases', { model, evicted }))
|
|
1187
1359
|
.catch(noop);
|
|
1188
1360
|
}
|
|
1361
|
+
async _busy(run) {
|
|
1362
|
+
await this._idle.enter();
|
|
1363
|
+
try {
|
|
1364
|
+
return await run();
|
|
1365
|
+
}
|
|
1366
|
+
finally {
|
|
1367
|
+
this._idle.exit();
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
async *_busyStream(run) {
|
|
1371
|
+
await this._idle.enter();
|
|
1372
|
+
try {
|
|
1373
|
+
yield* run();
|
|
1374
|
+
}
|
|
1375
|
+
finally {
|
|
1376
|
+
this._idle.exit();
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1189
1379
|
}
|
|
1190
1380
|
function noop() { }
|