@qvac/harness 0.3.2 → 0.4.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 +56 -1
- package/README.md +134 -0
- package/dist/harness.bundle +304 -175
- package/dist-lib/index.d.ts +6 -1
- package/dist-lib/index.js +29 -4
- package/dist-lib/lib/capabilities/completion.js +2 -5
- package/dist-lib/lib/capabilities/preview-model-source.d.ts +67 -0
- package/dist-lib/lib/capabilities/preview-model-source.js +387 -0
- package/dist-lib/lib/connect.d.ts +4 -5
- package/dist-lib/lib/connect.js +9 -0
- package/dist-lib/lib/contract-types.d.ts +4 -5
- package/dist-lib/lib/models/custom-model-cache.d.ts +4 -0
- package/dist-lib/lib/models/custom-model-cache.js +42 -0
- package/dist-lib/lib/models/gguf-header.d.ts +30 -0
- package/dist-lib/lib/models/gguf-header.js +208 -0
- package/dist-lib/lib/models/model-catalog.d.ts +6 -1
- package/dist-lib/lib/models/model-catalog.js +7 -2
- package/dist-lib/lib/models/model-delete.d.ts +4 -1
- package/dist-lib/lib/models/model-delete.js +19 -14
- package/dist-lib/lib/models/model-list.d.ts +6 -0
- package/dist-lib/lib/models/model-list.js +23 -1
- package/dist-lib/lib/models/model-source.js +1 -1
- package/dist-lib/lib/serve.js +21 -5
- package/dist-lib/schema/harness.js +42 -1
- 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 +173 -5
- package/dist-lib/spec/hyperschema/index.js +173 -5
- package/dist-lib/spec/hyperschema/schema.json +136 -1
- package/dist-lib/spec/hyperschema/types.d.ts +34 -1
- package/package.json +6 -7
- package/worker/harness-sidecar.mjs +1 -0
- package/dist-lib/lib/capabilities/vision-history.d.ts +0 -3
- package/dist-lib/lib/capabilities/vision-history.js +0 -21
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,59 @@ While the major version is `0`, a breaking change bumps the minor version.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.4.0] - 2026-09-24
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `Harness#previewModelSource({ src, mmprojSrc })`: checks a Hugging Face link,
|
|
16
|
+
a direct `.gguf` link or a local path without downloading it. Returns the
|
|
17
|
+
download link, size, context length, architecture, license, task type and a
|
|
18
|
+
fit verdict, lists the files of a repo that holds several, or refuses with a
|
|
19
|
+
reason. (#121)
|
|
20
|
+
- `setCustomModels` takes `taskType` and `expectedSize`. Only `llm` custom
|
|
21
|
+
models are registered, and a custom name never resolves for another task.
|
|
22
|
+
(#121)
|
|
23
|
+
- `list()` returns custom models with their task, size, and whether they are
|
|
24
|
+
loaded and on disk; the new `modelsDir` option tells the harness where
|
|
25
|
+
downloads live. (#121)
|
|
26
|
+
- `Harness#deleteModel(name)` removes what a custom model downloaded, never a
|
|
27
|
+
local path the user pointed at. (#121)
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- A turn that preloads a skill with `@skill` no longer caches the inlined
|
|
32
|
+
SKILL.md body under the chat's KV key. Each such turn left about 7k tokens
|
|
33
|
+
stranded in the cache, until a later turn overflowed the context. (#120)
|
|
34
|
+
|
|
35
|
+
## [0.3.3] - 2026-09-24
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- `@qvac/sdk` 0.20.0. Some addons now ship their prebuilds as per-platform
|
|
40
|
+
packages (`@qvac/<addon>-<os>-<cpu>`, e.g. `audiogen-ggml` 0.4, `tts-ggml`
|
|
41
|
+
0.9), and `@qvac/model-fit` and `@qvac/fabric` join the native graph. A host
|
|
42
|
+
that bundles the harness resolves prebuilds through each addon's
|
|
43
|
+
`#host-addon` map, defers the new addons, and exempts the platform packages
|
|
44
|
+
from any install age gate. (#85)
|
|
45
|
+
- `@qvac/skills` 0.1.8: the diagrams skill carries every recipe inline, so a
|
|
46
|
+
2B no longer loops on a second `skill` load for a reference file it cannot
|
|
47
|
+
name (QVAC-24436, QVAC-24502); the excel skill routes a chat image to create,
|
|
48
|
+
not edit; the Google OAuth routes drop the loopback port. (#115)
|
|
49
|
+
- A user message with several images reaches the SDK as one turn again. The
|
|
50
|
+
per-image split added for QVAC-24156 is gone: SDK 0.20 carries the upstream
|
|
51
|
+
mtmd fix (llama.cpp #27348), so equal-size still images no longer merge as
|
|
52
|
+
video frames and abort the encode. `<key>-vision-history-v1` cache
|
|
53
|
+
directories left by the split are no longer read; clearing all caches
|
|
54
|
+
removes them. (#116)
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- Retrying or regenerating in a chat that held a multi-image message could
|
|
59
|
+
reuse a stale cached prefix, because the split kept that chat's cache under a
|
|
60
|
+
key the host never deletes. (#116)
|
|
61
|
+
- `@qvac/skills` 0.1.9: the Asana skill's first setup step names the Asana MCP
|
|
62
|
+
app to connect. (#118)
|
|
63
|
+
|
|
11
64
|
## [0.3.2] - 2026-09-23
|
|
12
65
|
|
|
13
66
|
### Changed
|
|
@@ -240,7 +293,9 @@ Tagged but never published to npm; these changes first shipped in 0.2.12.
|
|
|
240
293
|
and the hash-pinned Python wheels), and `prebuilds-manifest.json` naming the
|
|
241
294
|
native addons it defers (#13, #15)
|
|
242
295
|
|
|
243
|
-
[Unreleased]: https://github.com/tetherto/qvac-harness/compare/v0.
|
|
296
|
+
[Unreleased]: https://github.com/tetherto/qvac-harness/compare/v0.4.0...HEAD
|
|
297
|
+
[0.4.0]: https://github.com/tetherto/qvac-harness/compare/v0.3.3...v0.4.0
|
|
298
|
+
[0.3.3]: https://github.com/tetherto/qvac-harness/compare/v0.3.2...v0.3.3
|
|
244
299
|
[0.3.2]: https://github.com/tetherto/qvac-harness/compare/v0.3.1...v0.3.2
|
|
245
300
|
[0.3.1]: https://github.com/tetherto/qvac-harness/compare/v0.3.0...v0.3.1
|
|
246
301
|
[0.3.0]: https://github.com/tetherto/qvac-harness/compare/v0.2.12...v0.3.0
|
package/README.md
CHANGED
|
@@ -48,6 +48,140 @@ dir, ...) are the ones `worker/harness-sidecar.mjs` documents in this repo. The
|
|
|
48
48
|
in-house hosts don't call this directly — they use the repo's library surface
|
|
49
49
|
(`harnessChild` under bare-supervisor), which owns that argv contract.
|
|
50
50
|
|
|
51
|
+
## Custom models
|
|
52
|
+
|
|
53
|
+
A host can let its users add a GGUF the catalog does not carry: a Hugging Face
|
|
54
|
+
link, a direct link to a `.gguf` file, or a local path. The harness checks the
|
|
55
|
+
source, runs it by the name the host registers it under, lists it next to the
|
|
56
|
+
catalog and deletes what it downloaded. Which custom models a device has is the
|
|
57
|
+
host's state; the harness keeps only the set it was last given.
|
|
58
|
+
|
|
59
|
+
The calls below are on the client `spawnHarness` returns (a `RemoteHarness`) and
|
|
60
|
+
on an in-process `Harness` alike.
|
|
61
|
+
|
|
62
|
+
### Preview a source before adding it
|
|
63
|
+
|
|
64
|
+
`previewModelSource` reads the source's Hugging Face metadata and the first
|
|
65
|
+
megabyte of the file. It never downloads the model.
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
const preview = await harness.previewModelSource({
|
|
69
|
+
src: 'https://huggingface.co/bartowski/Nanbeige_Nanbeige4.2-3B-GGUF/resolve/main/Nanbeige_Nanbeige4.2-3B-IQ2_M.gguf',
|
|
70
|
+
mmprojSrc: undefined // a vision model's projection, checked with it
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// {
|
|
74
|
+
// ok: true,
|
|
75
|
+
// src: 'https://huggingface.co/.../Nanbeige_Nanbeige4.2-3B-IQ2_M.gguf',
|
|
76
|
+
// sizeBytes: 1868506272,
|
|
77
|
+
// contextLength: 262144,
|
|
78
|
+
// architecture: 'nanbeige',
|
|
79
|
+
// sizeLabel: '3B',
|
|
80
|
+
// taskType: 'llm',
|
|
81
|
+
// license: 'apache-2.0',
|
|
82
|
+
// fit: 'unknown',
|
|
83
|
+
// fitReasons: ['...why the fit is unknown']
|
|
84
|
+
// }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A preview is one of three answers:
|
|
88
|
+
|
|
89
|
+
- **A file**: `ok: true` with `src`, the download link to register. A Hugging
|
|
90
|
+
Face page link is resolved to its `resolve/` url.
|
|
91
|
+
- **A choice**: `ok: true` with `files: [{ src, label, sizeBytes }]` and no
|
|
92
|
+
`src`, for a repo that holds several weights. Split shards are one entry and
|
|
93
|
+
projections are left out. Preview the chosen `files[i].src` again.
|
|
94
|
+
- **A refusal**: `ok: false` with `refusal` and a `reason` ready to show. The
|
|
95
|
+
refusals are `invalid-source`, `not-found`, `private`, `gated`, `web-page`,
|
|
96
|
+
`unreachable`, `not-gguf`, `not-a-model` (a projection or LoRA adapter),
|
|
97
|
+
`no-model-files` and `invalid-projection`.
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
if (!preview.ok) return showError(preview.reason)
|
|
101
|
+
if (!preview.src) return pickOneOf(preview.files)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`taskType` says what the model does: `llm`, `embeddings`, `speech-to-text`,
|
|
105
|
+
`text-to-speech` or `image-generation`. It comes from the Hugging Face pipeline
|
|
106
|
+
tag, then the card's and the file's tags, then the architecture. The preview
|
|
107
|
+
reports it for any model and refuses none for its task, so the host decides
|
|
108
|
+
what may be added. Only an `llm` runs as a custom model today:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
if (preview.taskType !== 'llm') {
|
|
112
|
+
return showError(`This is a ${preview.taskType} model. Custom models run as the text model only.`)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`fit` is `likely-fits`, `likely-too-large` or `unknown`, and `fitReasons` says
|
|
117
|
+
why. It comes only from the SDK, for a file whose sha256 its catalog knows, so
|
|
118
|
+
most custom files are `unknown`. It is never a reason to refuse.
|
|
119
|
+
|
|
120
|
+
`sizeLabel` is the file's own `general.size_label` as written (`0.6B`, `3B`,
|
|
121
|
+
`30B-A3B`), and is absent when the file records none.
|
|
122
|
+
|
|
123
|
+
### Register the set
|
|
124
|
+
|
|
125
|
+
`setCustomModels` replaces the whole set. Send it on start and whenever the
|
|
126
|
+
host's store changes; the client sends the last set again after a reconnect.
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
await harness.setCustomModels([
|
|
130
|
+
{
|
|
131
|
+
name: 'nanbeige-3b',
|
|
132
|
+
src: preview.src,
|
|
133
|
+
projectionSrc: preview.mmprojSrc,
|
|
134
|
+
taskType: preview.taskType, // 'llm'; an entry for another task is not registered
|
|
135
|
+
expectedSize: preview.sizeBytes
|
|
136
|
+
}
|
|
137
|
+
])
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
A registered name loads like a catalog model wherever an `llm` name is taken,
|
|
141
|
+
and never resolves for another task. A missing `taskType` means `llm`.
|
|
142
|
+
`expectedSize` is what `isCached` compares the downloaded files against.
|
|
143
|
+
|
|
144
|
+
### List them
|
|
145
|
+
|
|
146
|
+
`list` returns custom models after the catalog rows, under the same `type` and
|
|
147
|
+
`cachedOnly` filters:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const models = await harness.list({ type: 'llm' })
|
|
151
|
+
const custom = models.filter((model) => model.registrySource === 'custom')
|
|
152
|
+
|
|
153
|
+
// {
|
|
154
|
+
// name: 'nanbeige-3b',
|
|
155
|
+
// type: 'llm',
|
|
156
|
+
// registryPath: 'https://huggingface.co/.../Nanbeige_Nanbeige4.2-3B-IQ2_M.gguf',
|
|
157
|
+
// registrySource: 'custom',
|
|
158
|
+
// isCached: false,
|
|
159
|
+
// isLoaded: false,
|
|
160
|
+
// supportsImages: false,
|
|
161
|
+
// expectedSize: 1868506272
|
|
162
|
+
// }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`registryPath` is the model's `src`, and `supportsImages` is true when it has a
|
|
166
|
+
projection. `isCached` means every file is on disk and, when `expectedSize` is
|
|
167
|
+
known, their sizes add up to it, so a partial download is not cached. A local
|
|
168
|
+
path is checked where it is. A downloaded file is looked for at the path the SDK
|
|
169
|
+
gives a single-file http download under the models cache dir, which the spawned
|
|
170
|
+
sidecar knows from `sdkStorage.models`; an in-process `Harness` needs
|
|
171
|
+
`new Harness({ modelsDir })` for it. A sharded or archive url always reads as
|
|
172
|
+
not cached.
|
|
173
|
+
|
|
174
|
+
### Delete a download
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
const { removed, freedBytes } = await harness.deleteModel('nanbeige-3b')
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
For a custom model this removes the weight and projection it downloaded, and
|
|
181
|
+
never a local path: those are the user's files. The model stays registered and
|
|
182
|
+
lists as not cached, ready to download again. Like a catalog delete, it does not
|
|
183
|
+
unload a model that is running.
|
|
184
|
+
|
|
51
185
|
## Office extraction
|
|
52
186
|
|
|
53
187
|
Hosts can supply the bundled Python extractor to their document pipeline:
|