@qvac/harness 0.4.0 → 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 +689 -141
- package/README.md +29 -7
- package/dist/harness.bundle +71 -50
- package/dist-lib/index.d.ts +36 -39
- package/dist-lib/index.js +279 -114
- 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 +193 -132
- 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 +9 -7
- package/dist-lib/lib/capabilities/preview-model-source.js +80 -28
- 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 +11 -32
- package/dist-lib/lib/connect.js +152 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +22 -15
- 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 +10 -1
- package/dist-lib/lib/models/custom-model-cache.js +43 -15
- package/dist-lib/lib/models/gguf-header.d.ts +3 -2
- package/dist-lib/lib/models/gguf-header.js +44 -22
- 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 +13 -20
- package/dist-lib/lib/models/model-catalog.js +59 -31
- package/dist-lib/lib/models/model-delete.js +18 -8
- package/dist-lib/lib/models/model-handle.js +2 -1
- package/dist-lib/lib/models/model-list.d.ts +8 -3
- package/dist-lib/lib/models/model-list.js +11 -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 +10 -5
- 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 +110 -66
- 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 +14 -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 +3 -0
- package/dist-lib/spec/calls.js +1 -0
- package/dist-lib/spec/hrpc/hrpc.json +13 -0
- package/dist-lib/spec/hrpc/index.js +15 -3
- package/dist-lib/spec/hrpc/messages.js +39 -8
- package/dist-lib/spec/hyperschema/index.js +39 -8
- package/dist-lib/spec/hyperschema/schema.json +30 -1
- package/dist-lib/spec/hyperschema/types.d.ts +9 -1
- package/package.json +13 -14
- package/worker/harness-sidecar.mjs +15 -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
|
@@ -26,6 +26,7 @@ import { createModelStatusCache, customModelRows, invalidateModelStatusCache, me
|
|
|
26
26
|
import { deleteModelFiles, summarizeDelete } from './lib/models/model-delete.js';
|
|
27
27
|
import { createEmbedPool, createLoadTracker, createModelPool, createVoicePool, awaitModelCapacity, evictIdle, evictModelWithin, isResident, leaseResidentModel, leaseCount, loadedModels, modelKey, releaseModel, resolveLoadOptions, progressState } from './lib/models/model-pool.js';
|
|
28
28
|
import { resolveMainGpu } from './lib/models/gpu-selection.js';
|
|
29
|
+
import { createIdleReaper } from './lib/models/idle-unload.js';
|
|
29
30
|
import { openclCacheDir } from './lib/models/opencl-cache.js';
|
|
30
31
|
import { assertEmbedDevice, createEmbedConfig } from './lib/models/embed-config.js';
|
|
31
32
|
import { createGpuFaultState, isSdkGpuDeviceLostError, isSdkModelWedgedError } from './lib/models/sdk-faults.js';
|
|
@@ -39,6 +40,7 @@ import { ensureBundledSkillsDir } from './lib/tools/skills/bundled-dir.js';
|
|
|
39
40
|
import { ensureSkillsOverlayDir } from './lib/tools/skills/overlay-dir.js';
|
|
40
41
|
import { copyBaseForEdit, deleteOverlaySkill, listManagedSkills, readSkillFile, writeSkillFile } from './lib/tools/skills/overlay-crud.js';
|
|
41
42
|
import { watchSkillsOverlay } from './lib/tools/skills/overlay-watch.js';
|
|
43
|
+
import { errorMessage } from './lib/guards.js';
|
|
42
44
|
import { hlog } from './lib/log.js';
|
|
43
45
|
import { harness } from './spec/index.js';
|
|
44
46
|
import { installResolutionDetector, resolutionDetail } from './lib/resolution-detector.js';
|
|
@@ -133,6 +135,7 @@ export class Harness extends ReadyResource {
|
|
|
133
135
|
_capability = null;
|
|
134
136
|
_capabilityInflight = null;
|
|
135
137
|
_modelFaults = new Map();
|
|
138
|
+
_idle;
|
|
136
139
|
constructor({ tools = [], sdkLoader, skillsDir, modelsDir, projectionModel, imageModel, imageClamps, musicModel, musicClamps, llmDevice, llmGpuLayers, contextSize, kvCacheK, kvCacheV, embedDevice, embedGpuLayers, embedBatchSize, transcriptionDevice, completionModelCap, credentialStore, platform, gpuStats } = {}) {
|
|
137
140
|
super();
|
|
138
141
|
this._sdk = sdkLoader ?? createSdkLoader();
|
|
@@ -177,8 +180,9 @@ export class Harness extends ReadyResource {
|
|
|
177
180
|
},
|
|
178
181
|
onLoaded: (model) => {
|
|
179
182
|
const transfer = imageTransfers.get(model);
|
|
180
|
-
if (!transfer)
|
|
183
|
+
if (!transfer) {
|
|
181
184
|
return;
|
|
185
|
+
}
|
|
182
186
|
imageTransfers.delete(model);
|
|
183
187
|
invalidateModelStatusCache(this._modelStatus);
|
|
184
188
|
this.emit('model', { ...transfer, status: 'downloaded' });
|
|
@@ -186,9 +190,14 @@ export class Harness extends ReadyResource {
|
|
|
186
190
|
onBeforeLoad: async (signal) => {
|
|
187
191
|
if (this._models.destroyed)
|
|
188
192
|
return;
|
|
189
|
-
const evicted = await awaitModelCapacity(this._models,
|
|
190
|
-
|
|
193
|
+
const evicted = await awaitModelCapacity(this._models, {
|
|
194
|
+
cap: 1,
|
|
195
|
+
deadlineMs: DIFFUSION_EVICT_WAIT_MS,
|
|
196
|
+
signal
|
|
197
|
+
});
|
|
198
|
+
if (signal?.aborted) {
|
|
191
199
|
throw new AbortedError();
|
|
200
|
+
}
|
|
192
201
|
if (!evicted) {
|
|
193
202
|
throw new Error('the chat model is busy; try the image again once the current reply finishes');
|
|
194
203
|
}
|
|
@@ -205,9 +214,14 @@ export class Harness extends ReadyResource {
|
|
|
205
214
|
onBeforeLoad: async (signal) => {
|
|
206
215
|
if (this._models.destroyed)
|
|
207
216
|
return;
|
|
208
|
-
const evicted = await awaitModelCapacity(this._models,
|
|
209
|
-
|
|
217
|
+
const evicted = await awaitModelCapacity(this._models, {
|
|
218
|
+
cap: 1,
|
|
219
|
+
deadlineMs: MUSIC_EVICT_WAIT_MS,
|
|
220
|
+
signal
|
|
221
|
+
});
|
|
222
|
+
if (signal?.aborted) {
|
|
210
223
|
throw new AbortedError();
|
|
224
|
+
}
|
|
211
225
|
if (!evicted) {
|
|
212
226
|
throw new Error('the chat model is busy; try the music again once the current reply finishes');
|
|
213
227
|
}
|
|
@@ -245,13 +259,18 @@ export class Harness extends ReadyResource {
|
|
|
245
259
|
this._diffusionSdk = null;
|
|
246
260
|
this._musicSdk = null;
|
|
247
261
|
this._translationSdk = null;
|
|
248
|
-
}
|
|
249
|
-
async _open() {
|
|
250
|
-
installResolutionDetector(import.meta);
|
|
251
262
|
this._models = this._createPool();
|
|
252
263
|
this._embedModels = this._createEmbedPool();
|
|
253
264
|
this._ttsModels = this._createTtsPool();
|
|
254
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);
|
|
255
274
|
await this._reloadSkills();
|
|
256
275
|
await this._startOverlayWatch();
|
|
257
276
|
hlog('open', { skills: this._supportedSkills.length, tools: this._tools.length });
|
|
@@ -269,10 +288,7 @@ export class Harness extends ReadyResource {
|
|
|
269
288
|
if (!token)
|
|
270
289
|
continue;
|
|
271
290
|
const storeKey = scopeKey({ agentId, skill, key });
|
|
272
|
-
const record = {
|
|
273
|
-
token,
|
|
274
|
-
...(typeof expiresAt === 'number' && expiresAt > 0 ? { expiresAt } : {})
|
|
275
|
-
};
|
|
291
|
+
const record = typeof expiresAt === 'number' && expiresAt > 0 ? { token, expiresAt } : { token };
|
|
276
292
|
const current = previous.get(storeKey);
|
|
277
293
|
if (!current || current.token !== record.token || current.expiresAt !== record.expiresAt) {
|
|
278
294
|
resetCredentialHealthAfterSnapshot(key, agentId, skill);
|
|
@@ -310,48 +326,57 @@ export class Harness extends ReadyResource {
|
|
|
310
326
|
}
|
|
311
327
|
async writeSkillFile(name, content) {
|
|
312
328
|
const overlay = await ensureSkillsOverlayDir();
|
|
313
|
-
if (!overlay)
|
|
329
|
+
if (!overlay) {
|
|
314
330
|
throw new Error('no writable skills overlay is configured');
|
|
331
|
+
}
|
|
315
332
|
await writeSkillFile(overlay, name, content);
|
|
316
333
|
await this._reloadSkills();
|
|
317
334
|
return { ok: true };
|
|
318
335
|
}
|
|
319
336
|
async copyBaseForEdit(name) {
|
|
320
337
|
const overlay = await ensureSkillsOverlayDir();
|
|
321
|
-
if (!overlay)
|
|
338
|
+
if (!overlay) {
|
|
322
339
|
throw new Error('no writable skills overlay is configured');
|
|
340
|
+
}
|
|
323
341
|
const result = await copyBaseForEdit(await this._baseSkillsDir(), overlay, name);
|
|
324
|
-
if (result.copied)
|
|
342
|
+
if (result.copied) {
|
|
325
343
|
await this._reloadSkills();
|
|
344
|
+
}
|
|
326
345
|
return result;
|
|
327
346
|
}
|
|
328
347
|
async deleteOverlaySkill(name) {
|
|
329
348
|
const overlay = await ensureSkillsOverlayDir();
|
|
330
|
-
if (!overlay)
|
|
349
|
+
if (!overlay) {
|
|
331
350
|
throw new Error('no writable skills overlay is configured');
|
|
351
|
+
}
|
|
332
352
|
const result = await deleteOverlaySkill(overlay, name);
|
|
333
|
-
if (result.removed)
|
|
353
|
+
if (result.removed) {
|
|
334
354
|
await this._reloadSkills();
|
|
355
|
+
}
|
|
335
356
|
return result;
|
|
336
357
|
}
|
|
337
358
|
resolveCredential(key, ctx) {
|
|
338
|
-
if (this._credentialStore)
|
|
359
|
+
if (this._credentialStore) {
|
|
339
360
|
return this._credentialStore(key, ctx);
|
|
361
|
+
}
|
|
340
362
|
const agentId = ctx?.agentId ?? null;
|
|
341
363
|
const deviceShared = isDeviceSharedCredentialKey(key);
|
|
342
364
|
for (const skill of ctx?.skills ?? []) {
|
|
343
365
|
const owned = this._credentials.get(scopeKey({ agentId, skill, key })) ??
|
|
344
366
|
(agentId && deviceShared ? this._credentials.get(scopeKey({ skill, key })) : undefined);
|
|
345
|
-
if (owned)
|
|
367
|
+
if (owned) {
|
|
346
368
|
return owned;
|
|
369
|
+
}
|
|
347
370
|
}
|
|
348
371
|
if (agentId) {
|
|
349
372
|
const scoped = this._credentials.get(scopeKey({ agentId, key }));
|
|
350
|
-
if (scoped)
|
|
373
|
+
if (scoped) {
|
|
351
374
|
return scoped;
|
|
375
|
+
}
|
|
352
376
|
}
|
|
353
|
-
if (!agentId || deviceShared)
|
|
377
|
+
if (!agentId || deviceShared) {
|
|
354
378
|
return this._credentials.get(scopeKey({ key })) ?? null;
|
|
379
|
+
}
|
|
355
380
|
return null;
|
|
356
381
|
}
|
|
357
382
|
async _completionSkills(agentId, model) {
|
|
@@ -362,7 +387,8 @@ export class Harness extends ReadyResource {
|
|
|
362
387
|
}));
|
|
363
388
|
}
|
|
364
389
|
async skills(agentId, modelName, options) {
|
|
365
|
-
|
|
390
|
+
if (!this.opened)
|
|
391
|
+
await this.ready();
|
|
366
392
|
const platform = this._platform ?? detectPlatform();
|
|
367
393
|
const refresh = options?.refresh === true;
|
|
368
394
|
return resolveSkillStatuses(this._allSkills, {
|
|
@@ -376,7 +402,7 @@ export class Harness extends ReadyResource {
|
|
|
376
402
|
: await this._probeBinVersions(platform, refresh),
|
|
377
403
|
refresh,
|
|
378
404
|
skipCredentialProbe: options?.skipCredentialProbe === true,
|
|
379
|
-
|
|
405
|
+
activeProfile: modelName ? modelNameToCapabilityProfile(modelName) : undefined
|
|
380
406
|
});
|
|
381
407
|
}
|
|
382
408
|
async _reloadSkills() {
|
|
@@ -391,14 +417,15 @@ export class Harness extends ReadyResource {
|
|
|
391
417
|
return;
|
|
392
418
|
try {
|
|
393
419
|
const overlay = await ensureSkillsOverlayDir();
|
|
394
|
-
if (!overlay)
|
|
420
|
+
if (!overlay) {
|
|
395
421
|
return;
|
|
422
|
+
}
|
|
396
423
|
this._overlayWatcher = watchSkillsOverlay(overlay, () => {
|
|
397
|
-
this._reloadSkills().catch((err) => hlog('overlay reload failed', { err:
|
|
424
|
+
this._reloadSkills().catch((err) => hlog('overlay reload failed', { err: errorMessage(err) }));
|
|
398
425
|
});
|
|
399
426
|
}
|
|
400
427
|
catch (err) {
|
|
401
|
-
hlog('overlay watch unavailable', { err:
|
|
428
|
+
hlog('overlay watch unavailable', { err: errorMessage(err) });
|
|
402
429
|
}
|
|
403
430
|
}
|
|
404
431
|
async tools() {
|
|
@@ -411,8 +438,9 @@ export class Harness extends ReadyResource {
|
|
|
411
438
|
if (!refresh && this._binVersionCache && this._binVersionCache.expiresAt > now) {
|
|
412
439
|
return this._binVersionCache.bySkill;
|
|
413
440
|
}
|
|
414
|
-
if (this._binVersionInflight)
|
|
441
|
+
if (this._binVersionInflight) {
|
|
415
442
|
return this._binVersionInflight;
|
|
443
|
+
}
|
|
416
444
|
this._binVersionInflight = this._runBinVersionProbes(platform)
|
|
417
445
|
.then((bySkill) => {
|
|
418
446
|
this._binVersionCache = {
|
|
@@ -427,12 +455,14 @@ export class Harness extends ReadyResource {
|
|
|
427
455
|
return this._binVersionInflight;
|
|
428
456
|
}
|
|
429
457
|
async _runBinVersionProbes(platform) {
|
|
430
|
-
if (process.env.QVAC_NO_SKILL_BIN_PROBE)
|
|
458
|
+
if (process.env.QVAC_NO_SKILL_BIN_PROBE) {
|
|
431
459
|
return new Map();
|
|
460
|
+
}
|
|
432
461
|
const installed = new Set(this._tools.map((tool) => tool.name));
|
|
433
462
|
const candidates = versionProbeSkills(this._allSkills, installed, platform);
|
|
434
|
-
if (candidates.length === 0)
|
|
463
|
+
if (candidates.length === 0) {
|
|
435
464
|
return new Map();
|
|
465
|
+
}
|
|
436
466
|
let runtime;
|
|
437
467
|
try {
|
|
438
468
|
runtime = await bareRuntime();
|
|
@@ -452,8 +482,9 @@ export class Harness extends ReadyResource {
|
|
|
452
482
|
const bySkill = new Map();
|
|
453
483
|
for (const skill of candidates) {
|
|
454
484
|
const states = await probeBinVersions(skill.requires, { platform, run });
|
|
455
|
-
if (states.size > 0)
|
|
485
|
+
if (states.size > 0) {
|
|
456
486
|
bySkill.set(skill.name, states);
|
|
487
|
+
}
|
|
457
488
|
}
|
|
458
489
|
return bySkill;
|
|
459
490
|
}
|
|
@@ -472,17 +503,22 @@ export class Harness extends ReadyResource {
|
|
|
472
503
|
}
|
|
473
504
|
_projectionFor(model) {
|
|
474
505
|
const custom = this._customProjection[model];
|
|
475
|
-
if (custom)
|
|
506
|
+
if (custom) {
|
|
476
507
|
return custom;
|
|
508
|
+
}
|
|
477
509
|
const projection = this._projectionModel;
|
|
478
|
-
if (!projection)
|
|
510
|
+
if (!projection) {
|
|
479
511
|
return undefined;
|
|
512
|
+
}
|
|
480
513
|
return typeof projection === 'string' ? projection : projection[model];
|
|
481
514
|
}
|
|
482
515
|
setCustomModels(entries) {
|
|
483
516
|
setCustomModels(entries);
|
|
484
517
|
this._customProjection = customProjectionMap();
|
|
485
518
|
}
|
|
519
|
+
setModelIdleTimeout(ms) {
|
|
520
|
+
this._idle.configure(ms);
|
|
521
|
+
}
|
|
486
522
|
previewModelSource(context) {
|
|
487
523
|
return previewModelSource(context, {
|
|
488
524
|
assessFit: async ({ sha256Checksum, name, contextTokens }) => {
|
|
@@ -519,11 +555,12 @@ export class Harness extends ReadyResource {
|
|
|
519
555
|
};
|
|
520
556
|
}
|
|
521
557
|
_loadedModels() {
|
|
522
|
-
const pools = [
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
558
|
+
const pools = [
|
|
559
|
+
this._embedModels,
|
|
560
|
+
...this._retiredEmbedModels,
|
|
561
|
+
this._ttsModels,
|
|
562
|
+
this._transcriptionModels
|
|
563
|
+
];
|
|
527
564
|
return [...loadedModels(this._models), ...pools.flatMap((pool) => loadedModels(pool))];
|
|
528
565
|
}
|
|
529
566
|
_loadPluginSdk(current, store, importPlugin) {
|
|
@@ -557,8 +594,9 @@ export class Harness extends ReadyResource {
|
|
|
557
594
|
const loadOpts = resolveLoadOptions({}, this._deviceConfig());
|
|
558
595
|
const key = modelKey(model, loadOpts);
|
|
559
596
|
const pool = this._models;
|
|
560
|
-
if (!isResident(pool, key))
|
|
597
|
+
if (!isResident(pool, key)) {
|
|
561
598
|
await this._makeRoomForCompletion(signal);
|
|
599
|
+
}
|
|
562
600
|
try {
|
|
563
601
|
const { modelId } = await raceAbort(pool.open(key, model, loadOpts).load(), signal);
|
|
564
602
|
const sdk = await this._sdk.load();
|
|
@@ -592,21 +630,40 @@ export class Harness extends ReadyResource {
|
|
|
592
630
|
}
|
|
593
631
|
_createPool() {
|
|
594
632
|
const loadSdk = this._sdk.load;
|
|
595
|
-
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
|
+
});
|
|
596
637
|
}
|
|
597
638
|
_createEmbedPool() {
|
|
598
639
|
const loadSdk = () => this._sdk.loadEmbed();
|
|
599
|
-
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
|
+
});
|
|
600
646
|
}
|
|
601
647
|
_createTtsPool() {
|
|
602
648
|
const loadSdk = () => this._sdk.loadVoice();
|
|
603
|
-
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
|
+
});
|
|
604
655
|
}
|
|
605
656
|
_createTranscriptionPool() {
|
|
606
657
|
const loadSdk = () => this._sdk.loadVoice();
|
|
607
|
-
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
|
+
});
|
|
608
665
|
}
|
|
609
|
-
_notifyModel(state, languageModel) {
|
|
666
|
+
_notifyModel(state, { languageModel = false } = {}) {
|
|
610
667
|
const catalogChanged = languageModel && (state.status === 'ready' || state.status === 'unloaded');
|
|
611
668
|
if (catalogChanged) {
|
|
612
669
|
invalidateModelStatusCache(this._modelStatus);
|
|
@@ -646,14 +703,17 @@ export class Harness extends ReadyResource {
|
|
|
646
703
|
return this._enqueueEmbedControl(() => this._setEmbeddingDevice(device));
|
|
647
704
|
}
|
|
648
705
|
async _setEmbeddingDevice(device) {
|
|
649
|
-
|
|
706
|
+
if (!this.opened)
|
|
707
|
+
await this.ready();
|
|
650
708
|
if (this.closing)
|
|
651
709
|
throw new Error('harness is closing');
|
|
652
|
-
if (this._embedFaults.device === device)
|
|
710
|
+
if (this._embedFaults.device === device) {
|
|
653
711
|
return;
|
|
712
|
+
}
|
|
654
713
|
this._embedFaults = createGpuFaultState({ device, gpuLayers: this._embedGpuLayers });
|
|
655
|
-
if (this.suspended)
|
|
714
|
+
if (this.suspended) {
|
|
656
715
|
return;
|
|
716
|
+
}
|
|
657
717
|
const retired = this._embedModels;
|
|
658
718
|
this._retiredEmbedModels.add(retired);
|
|
659
719
|
this._embedModels = this._createEmbedPool();
|
|
@@ -667,15 +727,18 @@ export class Harness extends ReadyResource {
|
|
|
667
727
|
}
|
|
668
728
|
async suspend() {
|
|
669
729
|
hlog('suspend called');
|
|
670
|
-
if (this._state === HarnessState.SUSPEND)
|
|
730
|
+
if (this._state === HarnessState.SUSPEND) {
|
|
671
731
|
return;
|
|
672
|
-
|
|
732
|
+
}
|
|
733
|
+
if (this.closing) {
|
|
673
734
|
return;
|
|
735
|
+
}
|
|
674
736
|
this._state = HarnessState.SUSPEND;
|
|
675
737
|
hlog('suspending', { models: loadedModels(this._models).map((entry) => entry.model) });
|
|
676
738
|
const pending = this._sdk.peek();
|
|
677
|
-
if (pending)
|
|
739
|
+
if (pending) {
|
|
678
740
|
this._loads.drain(await pending);
|
|
741
|
+
}
|
|
679
742
|
await this._releaseRuntimes();
|
|
680
743
|
await this._sdkLifecycle('suspend');
|
|
681
744
|
invalidateModelStatusCache(this._modelStatus);
|
|
@@ -683,18 +746,20 @@ export class Harness extends ReadyResource {
|
|
|
683
746
|
}
|
|
684
747
|
async _sdkLifecycle(op) {
|
|
685
748
|
const sdk = await this._sdk.peek()?.catch(noop);
|
|
686
|
-
if (!sdk)
|
|
749
|
+
if (!sdk) {
|
|
687
750
|
return;
|
|
751
|
+
}
|
|
688
752
|
try {
|
|
689
753
|
await sdk[op]();
|
|
690
754
|
}
|
|
691
755
|
catch (err) {
|
|
692
|
-
hlog(`sdk ${op} failed`, { err:
|
|
693
|
-
if (op === 'suspend')
|
|
756
|
+
hlog(`sdk ${op} failed`, { err: errorMessage(err) });
|
|
757
|
+
if (op === 'suspend') {
|
|
694
758
|
return;
|
|
759
|
+
}
|
|
695
760
|
await sdk.resume().catch((retryErr) => {
|
|
696
761
|
hlog('sdk resume retry failed', {
|
|
697
|
-
err:
|
|
762
|
+
err: errorMessage(retryErr)
|
|
698
763
|
});
|
|
699
764
|
});
|
|
700
765
|
}
|
|
@@ -713,15 +778,38 @@ export class Harness extends ReadyResource {
|
|
|
713
778
|
if (outcome.status !== 'rejected')
|
|
714
779
|
continue;
|
|
715
780
|
const err = outcome.reason;
|
|
716
|
-
hlog('unload failed', { err:
|
|
781
|
+
hlog('unload failed', { err: errorMessage(err) });
|
|
717
782
|
}
|
|
718
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) });
|
|
802
|
+
}
|
|
803
|
+
invalidateModelStatusCache(this._modelStatus);
|
|
804
|
+
}
|
|
719
805
|
async resume() {
|
|
720
806
|
hlog('resume called');
|
|
721
|
-
if (this._state === HarnessState.RESUME)
|
|
807
|
+
if (this._state === HarnessState.RESUME) {
|
|
722
808
|
return;
|
|
723
|
-
|
|
809
|
+
}
|
|
810
|
+
if (this.closing) {
|
|
724
811
|
return;
|
|
812
|
+
}
|
|
725
813
|
if (this._state === HarnessState.PRESUSPEND) {
|
|
726
814
|
this._state = HarnessState.RESUME;
|
|
727
815
|
return;
|
|
@@ -744,22 +832,26 @@ export class Harness extends ReadyResource {
|
|
|
744
832
|
}
|
|
745
833
|
async _close() {
|
|
746
834
|
hlog('closing');
|
|
835
|
+
this._idle.close();
|
|
747
836
|
this._overlayWatcher?.stop();
|
|
748
837
|
this._overlayWatcher = null;
|
|
749
838
|
const pending = this._sdk.peek();
|
|
750
|
-
if (pending)
|
|
839
|
+
if (pending) {
|
|
751
840
|
this._loads.drain(await pending);
|
|
841
|
+
}
|
|
752
842
|
await Promise.all([this._releaseRuntimes(), closePythonWorker()]);
|
|
753
843
|
const sdk = await this._sdk.peek()?.catch(noop);
|
|
754
|
-
if (sdk)
|
|
844
|
+
if (sdk) {
|
|
755
845
|
await sdk.close();
|
|
846
|
+
}
|
|
756
847
|
}
|
|
757
848
|
download(args) {
|
|
758
849
|
const projection = (args.addon ?? 'llm') === 'llm' ? this._projectionFor(args.model) : undefined;
|
|
759
850
|
const multiFile = diffusionDownloadMetadata(args.model, args.addon) ??
|
|
760
851
|
audioGenDownloadMetadata(args.model, args.addon);
|
|
761
852
|
return download(async () => {
|
|
762
|
-
|
|
853
|
+
if (!this.opened)
|
|
854
|
+
await this.ready();
|
|
763
855
|
return this._sdk.load();
|
|
764
856
|
}, {
|
|
765
857
|
...args,
|
|
@@ -768,12 +860,17 @@ export class Harness extends ReadyResource {
|
|
|
768
860
|
assetSizes: multiFile?.assetSizes
|
|
769
861
|
}, () => invalidateModelStatusCache(this._modelStatus));
|
|
770
862
|
}
|
|
771
|
-
|
|
772
|
-
|
|
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();
|
|
773
869
|
if (this.suspended)
|
|
774
870
|
throw new Error('harness is suspended');
|
|
775
|
-
if (texts.length === 0)
|
|
871
|
+
if (texts.length === 0) {
|
|
776
872
|
return [];
|
|
873
|
+
}
|
|
777
874
|
const pool = this._embedModels;
|
|
778
875
|
const faults = this._embedFaults;
|
|
779
876
|
const generation = faults.generation;
|
|
@@ -784,7 +881,7 @@ export class Harness extends ReadyResource {
|
|
|
784
881
|
let releaseAfter = null;
|
|
785
882
|
try {
|
|
786
883
|
loaded = await raceAbort(handle.load(), signal);
|
|
787
|
-
const sdk =
|
|
884
|
+
const sdk = await this._sdk.loadEmbed();
|
|
788
885
|
job = this._embedLane(sdk, { modelId: loaded.modelId, texts });
|
|
789
886
|
const vectors = await raceAbort(job, signal);
|
|
790
887
|
faults.reset();
|
|
@@ -807,15 +904,18 @@ export class Harness extends ReadyResource {
|
|
|
807
904
|
throw err;
|
|
808
905
|
}
|
|
809
906
|
finally {
|
|
810
|
-
if (releaseAfter)
|
|
907
|
+
if (releaseAfter) {
|
|
811
908
|
this._releaseEmbedResourceAfter(releaseAfter, pool, key);
|
|
812
|
-
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
813
911
|
await this._releaseEmbedResource(pool, key);
|
|
912
|
+
}
|
|
814
913
|
}
|
|
815
914
|
}
|
|
816
915
|
async _defaultVoiceSrc(category) {
|
|
817
|
-
if (category !== 'tts')
|
|
916
|
+
if (category !== 'tts') {
|
|
818
917
|
return defaultVoiceModel(category);
|
|
918
|
+
}
|
|
819
919
|
try {
|
|
820
920
|
const { capabilityProfile } = await this.capability();
|
|
821
921
|
return defaultTtsFor(capabilityProfile).asset.src;
|
|
@@ -824,8 +924,9 @@ export class Harness extends ReadyResource {
|
|
|
824
924
|
return defaultVoiceModel('tts');
|
|
825
925
|
}
|
|
826
926
|
}
|
|
827
|
-
async _voiceJob(pool, category, model, signal, run) {
|
|
828
|
-
|
|
927
|
+
async _voiceJob(pool, { category, model, signal }, run) {
|
|
928
|
+
if (!this.opened)
|
|
929
|
+
await this.ready();
|
|
829
930
|
if (this.suspended)
|
|
830
931
|
throw new Error('harness is suspended');
|
|
831
932
|
const name = model || (await this._defaultVoiceSrc(category));
|
|
@@ -844,8 +945,9 @@ export class Harness extends ReadyResource {
|
|
|
844
945
|
}
|
|
845
946
|
catch (err) {
|
|
846
947
|
if (err instanceof AbortedError) {
|
|
847
|
-
if (sdk && modelId)
|
|
948
|
+
if (sdk && modelId) {
|
|
848
949
|
void sdk.cancel({ modelId, operation: 'inference' }).catch(noop);
|
|
950
|
+
}
|
|
849
951
|
releaseAfter = job;
|
|
850
952
|
}
|
|
851
953
|
throw err;
|
|
@@ -854,19 +956,25 @@ export class Harness extends ReadyResource {
|
|
|
854
956
|
const release = () => {
|
|
855
957
|
releaseModel(pool, name);
|
|
856
958
|
};
|
|
857
|
-
if (releaseAfter)
|
|
959
|
+
if (releaseAfter) {
|
|
858
960
|
void releaseAfter.then(release, release);
|
|
859
|
-
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
860
963
|
release();
|
|
964
|
+
}
|
|
861
965
|
}
|
|
862
966
|
}
|
|
863
|
-
|
|
864
|
-
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) => {
|
|
865
972
|
const pcm = await pcmForTranscription(audio);
|
|
866
|
-
if (signal?.aborted)
|
|
973
|
+
if (signal?.aborted) {
|
|
867
974
|
throw new AbortedError();
|
|
975
|
+
}
|
|
868
976
|
if (pcm && pcm.length >= TRANSCRIBE_STREAM_MIN_BYTES) {
|
|
869
|
-
return transcribeStreaming(sdk,
|
|
977
|
+
return transcribeStreaming(sdk, pcm, { modelId: id, signal });
|
|
870
978
|
}
|
|
871
979
|
return transcribeOnce(sdk, id, pcm ? buildWavBuffer(pcm) : audio);
|
|
872
980
|
});
|
|
@@ -874,12 +982,19 @@ export class Harness extends ReadyResource {
|
|
|
874
982
|
detectLanguage(context) {
|
|
875
983
|
return detectLanguage(context);
|
|
876
984
|
}
|
|
877
|
-
|
|
878
|
-
|
|
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));
|
|
879
990
|
return { audio: packSamples(samples), sampleRate: SUPERTONIC_SAMPLE_RATE };
|
|
880
991
|
}
|
|
881
|
-
|
|
882
|
-
|
|
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();
|
|
883
998
|
if (this.suspended)
|
|
884
999
|
throw new Error('harness is suspended');
|
|
885
1000
|
const name = model || (await this._defaultVoiceSrc('tts'));
|
|
@@ -889,18 +1004,21 @@ export class Harness extends ReadyResource {
|
|
|
889
1004
|
let sdk = null;
|
|
890
1005
|
let modelId = null;
|
|
891
1006
|
const cancel = () => {
|
|
892
|
-
if (sdk && modelId)
|
|
893
|
-
void sdk.cancel({ modelId, operation: 'inference' }).catch(
|
|
1007
|
+
if (sdk && modelId) {
|
|
1008
|
+
void sdk.cancel({ modelId, operation: 'inference' }).catch(noop);
|
|
1009
|
+
}
|
|
894
1010
|
};
|
|
895
1011
|
signal?.addEventListener('abort', cancel, { once: true });
|
|
896
1012
|
const job = this._voiceLane(async () => {
|
|
897
1013
|
try {
|
|
898
1014
|
modelId = (await handle.load()).modelId;
|
|
899
1015
|
sdk = (await this._sdk.loadVoice());
|
|
900
|
-
if (signal?.aborted)
|
|
1016
|
+
if (signal?.aborted) {
|
|
901
1017
|
return;
|
|
902
|
-
|
|
1018
|
+
}
|
|
1019
|
+
for await (const chunk of speakStreamOnce(sdk, modelId, text)) {
|
|
903
1020
|
chunks.push(chunk);
|
|
1021
|
+
}
|
|
904
1022
|
}
|
|
905
1023
|
finally {
|
|
906
1024
|
chunks.end();
|
|
@@ -914,18 +1032,23 @@ export class Harness extends ReadyResource {
|
|
|
914
1032
|
}
|
|
915
1033
|
finally {
|
|
916
1034
|
signal?.removeEventListener('abort', cancel);
|
|
917
|
-
if (!settled)
|
|
1035
|
+
if (!settled) {
|
|
918
1036
|
cancel();
|
|
1037
|
+
}
|
|
919
1038
|
void job.catch(noop).finally(() => {
|
|
920
1039
|
releaseModel(pool, name);
|
|
921
1040
|
});
|
|
922
1041
|
}
|
|
923
1042
|
}
|
|
924
1043
|
async list(options = {}) {
|
|
925
|
-
|
|
926
|
-
|
|
1044
|
+
if (!this.opened)
|
|
1045
|
+
await this.ready();
|
|
1046
|
+
const sdk = await this._sdk.load();
|
|
927
1047
|
const [rows, custom] = await Promise.all([
|
|
928
|
-
listModels(sdk, options,
|
|
1048
|
+
listModels(sdk, options, {
|
|
1049
|
+
cache: this._modelStatus,
|
|
1050
|
+
projectionFor: (name) => this._projectionFor(name)
|
|
1051
|
+
}),
|
|
929
1052
|
customModelRows(options, {
|
|
930
1053
|
modelsDir: this._modelsDir,
|
|
931
1054
|
loaded: new Set(this._loadedModels().map((entry) => entry.model))
|
|
@@ -934,18 +1057,21 @@ export class Harness extends ReadyResource {
|
|
|
934
1057
|
return mergeCustomRows(rows, custom);
|
|
935
1058
|
}
|
|
936
1059
|
async deleteModel(name) {
|
|
937
|
-
|
|
938
|
-
|
|
1060
|
+
if (!this.opened)
|
|
1061
|
+
await this.ready();
|
|
1062
|
+
const sdk = await this._sdk.load();
|
|
939
1063
|
const result = await deleteModelFiles(sdk, name, { modelsDir: this._modelsDir });
|
|
940
1064
|
invalidateModelStatusCache(this._modelStatus);
|
|
941
1065
|
return summarizeDelete(result);
|
|
942
1066
|
}
|
|
943
1067
|
async clearKvCache() {
|
|
944
|
-
|
|
1068
|
+
if (!this.opened)
|
|
1069
|
+
await this.ready();
|
|
945
1070
|
return deleteAllKvCaches(() => this._sdk.load());
|
|
946
1071
|
}
|
|
947
1072
|
async deleteKvCache(kvCacheKey) {
|
|
948
|
-
|
|
1073
|
+
if (!this.opened)
|
|
1074
|
+
await this.ready();
|
|
949
1075
|
return deleteKvCacheKey(() => this._sdk.load(), kvCacheKey);
|
|
950
1076
|
}
|
|
951
1077
|
async infraModels(profile) {
|
|
@@ -989,8 +1115,12 @@ export class Harness extends ReadyResource {
|
|
|
989
1115
|
});
|
|
990
1116
|
return this._capabilityInflight;
|
|
991
1117
|
}
|
|
992
|
-
|
|
993
|
-
|
|
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();
|
|
994
1124
|
if (this.suspended)
|
|
995
1125
|
return;
|
|
996
1126
|
const projectionModel = this._projectionFor(model);
|
|
@@ -1004,8 +1134,9 @@ export class Harness extends ReadyResource {
|
|
|
1004
1134
|
const pool = this._models;
|
|
1005
1135
|
hlog('warm model', { model, resident: isResident(pool, key) });
|
|
1006
1136
|
if (!isResident(pool, key)) {
|
|
1007
|
-
if (!(await this._makeRoomForCompletion(signal, WARM_ROOM_WAIT_MS)))
|
|
1137
|
+
if (!(await this._makeRoomForCompletion(signal, WARM_ROOM_WAIT_MS))) {
|
|
1008
1138
|
return;
|
|
1139
|
+
}
|
|
1009
1140
|
await this._imageRuntime?.unloadIdle().catch(noop);
|
|
1010
1141
|
}
|
|
1011
1142
|
try {
|
|
@@ -1015,11 +1146,15 @@ export class Harness extends ReadyResource {
|
|
|
1015
1146
|
releaseModel(pool, key);
|
|
1016
1147
|
}
|
|
1017
1148
|
}
|
|
1018
|
-
|
|
1149
|
+
invoke(context) {
|
|
1150
|
+
return this._busyStream(() => this._invoke(context));
|
|
1151
|
+
}
|
|
1152
|
+
async *_invoke({ capability, args, enhance, agent, attach, fetchAttachment, progress, signal }) {
|
|
1019
1153
|
if (!this.opened)
|
|
1020
1154
|
await this.ready();
|
|
1021
|
-
if (this.suspended)
|
|
1155
|
+
if (this.suspended) {
|
|
1022
1156
|
return yield { type: 'aborted' };
|
|
1157
|
+
}
|
|
1023
1158
|
hlog('invoke start', { capability, enhance });
|
|
1024
1159
|
try {
|
|
1025
1160
|
yield* invoke({
|
|
@@ -1041,8 +1176,12 @@ export class Harness extends ReadyResource {
|
|
|
1041
1176
|
hlog('invoke end', { capability });
|
|
1042
1177
|
}
|
|
1043
1178
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
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();
|
|
1046
1185
|
if (this.suspended)
|
|
1047
1186
|
throw new Error('harness is suspended');
|
|
1048
1187
|
const projectionModel = this._projectionFor(model);
|
|
@@ -1137,16 +1276,12 @@ export class Harness extends ReadyResource {
|
|
|
1137
1276
|
yield { type: 'aborted' };
|
|
1138
1277
|
}
|
|
1139
1278
|
else {
|
|
1140
|
-
const message =
|
|
1279
|
+
const message = errorMessage(err);
|
|
1141
1280
|
hlog('completion failed', { model, ...resolutionDetail(err), err: message });
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
process.exit(1);
|
|
1147
|
-
}
|
|
1148
|
-
else {
|
|
1149
|
-
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);
|
|
1150
1285
|
}
|
|
1151
1286
|
}
|
|
1152
1287
|
}
|
|
@@ -1160,14 +1295,19 @@ export class Harness extends ReadyResource {
|
|
|
1160
1295
|
_residentEnhancer() {
|
|
1161
1296
|
return async (prompt, signal) => {
|
|
1162
1297
|
const lease = await leaseResidentModel(this._models, (model) => !isTranslationModel(model));
|
|
1163
|
-
if (!lease)
|
|
1298
|
+
if (!lease) {
|
|
1164
1299
|
return null;
|
|
1300
|
+
}
|
|
1165
1301
|
try {
|
|
1166
1302
|
const sdk = await this._sdk.load();
|
|
1167
|
-
return await expandImagePrompt(sdk,
|
|
1303
|
+
return await expandImagePrompt(sdk, prompt, {
|
|
1304
|
+
modelId: lease.modelId,
|
|
1305
|
+
model: lease.model,
|
|
1306
|
+
signal
|
|
1307
|
+
});
|
|
1168
1308
|
}
|
|
1169
1309
|
catch (err) {
|
|
1170
|
-
hlog('enhance failed', { err:
|
|
1310
|
+
hlog('enhance failed', { err: errorMessage(err) });
|
|
1171
1311
|
return null;
|
|
1172
1312
|
}
|
|
1173
1313
|
finally {
|
|
@@ -1176,7 +1316,11 @@ export class Harness extends ReadyResource {
|
|
|
1176
1316
|
};
|
|
1177
1317
|
}
|
|
1178
1318
|
async _makeRoomForCompletion(signal, deadlineMs = MODEL_ROOM_WAIT_MS) {
|
|
1179
|
-
const roomed = await awaitModelCapacity(this._models,
|
|
1319
|
+
const roomed = await awaitModelCapacity(this._models, {
|
|
1320
|
+
cap: this._completionModelCap,
|
|
1321
|
+
deadlineMs,
|
|
1322
|
+
signal
|
|
1323
|
+
});
|
|
1180
1324
|
if (!roomed) {
|
|
1181
1325
|
hlog('model room wait gave up', {
|
|
1182
1326
|
cap: this._completionModelCap,
|
|
@@ -1189,10 +1333,13 @@ export class Harness extends ReadyResource {
|
|
|
1189
1333
|
const faults = (this._modelFaults.get(model) ?? 0) + 1;
|
|
1190
1334
|
this._modelFaults.set(model, faults);
|
|
1191
1335
|
const leased = leaseCount(this._models, key) > 0;
|
|
1192
|
-
const evicted = leased
|
|
1336
|
+
const evicted = leased
|
|
1337
|
+
? false
|
|
1338
|
+
: await evictModelWithin(this._models, key, { deadlineMs: WEDGED_EVICT_WAIT_MS });
|
|
1193
1339
|
hlog('wedged model evicted', { model, faults, evicted, leased });
|
|
1194
|
-
if (leased)
|
|
1340
|
+
if (leased) {
|
|
1195
1341
|
this._evictAfterLeases(model, key);
|
|
1342
|
+
}
|
|
1196
1343
|
if (!evicted && !leased) {
|
|
1197
1344
|
return {
|
|
1198
1345
|
message: `${model} is stuck in device memory — restarting the model runtime`,
|
|
@@ -1207,9 +1354,27 @@ export class Harness extends ReadyResource {
|
|
|
1207
1354
|
};
|
|
1208
1355
|
}
|
|
1209
1356
|
_evictAfterLeases(model, key) {
|
|
1210
|
-
void evictModelWithin(this._models, key, WEDGED_EVICT_LEASED_MS)
|
|
1357
|
+
void evictModelWithin(this._models, key, { deadlineMs: WEDGED_EVICT_LEASED_MS })
|
|
1211
1358
|
.then((evicted) => hlog('wedged model evicted after leases', { model, evicted }))
|
|
1212
1359
|
.catch(noop);
|
|
1213
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
|
+
}
|
|
1214
1379
|
}
|
|
1215
1380
|
function noop() { }
|