@qvac/sdk 0.15.0 → 0.16.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/NOTICE +31 -45
- package/dist/client/api/ocr.d.ts +9 -0
- package/dist/client/api/ocr.js +9 -0
- package/dist/client/api/translate.js +6 -17
- package/dist/client/api/video.d.ts +7 -4
- package/dist/client/api/video.js +7 -4
- package/dist/client/api/vla.d.ts +14 -10
- package/dist/client/api/vla.js +14 -10
- package/dist/client/rpc/rpc-error.js +7 -1
- package/dist/examples/diffusion-ideogram.d.ts +2 -0
- package/dist/examples/diffusion-ideogram.js +108 -0
- package/dist/examples/diffusion-img2vid-ltx.d.ts +2 -0
- package/dist/examples/diffusion-img2vid-ltx.js +98 -0
- package/dist/examples/diffusion-txt2vid-ltx.d.ts +2 -0
- package/dist/examples/diffusion-txt2vid-ltx.js +92 -0
- package/dist/examples/logging-streaming.js +5 -1
- package/dist/examples/transcription/parakeet-tdt-filesystem-chunked.d.ts +2 -0
- package/dist/examples/transcription/parakeet-tdt-filesystem-chunked.js +203 -0
- package/dist/examples/vla-groot.d.ts +2 -0
- package/dist/examples/vla-groot.js +118 -0
- package/dist/models/registry/models.d.ts +5711 -9637
- package/dist/models/registry/models.js +2747 -6886
- package/dist/models/update-models/naming.js +5 -1
- package/dist/schemas/batch-completion-stream.d.ts +4 -4
- package/dist/schemas/common.d.ts +385 -4
- package/dist/schemas/common.js +3 -1
- package/dist/schemas/completion-stream.d.ts +186 -2
- package/dist/schemas/completion-stream.js +67 -0
- package/dist/schemas/constants-registry.d.ts +67 -0
- package/dist/schemas/constants-registry.js +51 -0
- package/dist/schemas/delete-cache.js +7 -3
- package/dist/schemas/engine-addon-map.d.ts +1 -0
- package/dist/schemas/engine-addon-map.js +1 -1
- package/dist/schemas/finetune.js +6 -3
- package/dist/schemas/get-loaded-model-info.d.ts +16 -0
- package/dist/schemas/get-loaded-model-info.js +14 -6
- package/dist/schemas/llamacpp-config.d.ts +24 -4
- package/dist/schemas/llamacpp-config.js +1 -6
- package/dist/schemas/load-model.d.ts +1380 -60
- package/dist/schemas/load-model.js +6 -3
- package/dist/schemas/plugin.d.ts +4 -4
- package/dist/schemas/plugin.js +4 -4
- package/dist/schemas/reload-config.js +3 -1
- package/dist/schemas/sdcpp-config.d.ts +239 -0
- package/dist/schemas/sdcpp-config.js +116 -10
- package/dist/schemas/sdk-config.d.ts +790 -40
- package/dist/schemas/sdk-errors-server.js +3 -1
- package/dist/schemas/text-to-speech.d.ts +14 -0
- package/dist/schemas/text-to-speech.js +10 -4
- package/dist/schemas/tools.d.ts +1 -1
- package/dist/schemas/tools.js +2 -1
- package/dist/schemas/translate.js +12 -6
- package/dist/schemas/vla.d.ts +10 -0
- package/dist/schemas/vla.js +31 -11
- package/dist/scripts/contract/build-contract.d.ts +38 -0
- package/dist/scripts/contract/build-contract.js +620 -0
- package/dist/scripts/contract/build-error-codes.d.ts +17 -0
- package/dist/scripts/contract/build-error-codes.js +17 -0
- package/dist/scripts/contract/build-model-type-maps.d.ts +22 -0
- package/dist/scripts/contract/build-model-type-maps.js +16 -0
- package/dist/scripts/contract/build-models-registry.d.ts +17 -0
- package/dist/scripts/contract/build-models-registry.js +31 -0
- package/dist/scripts/contract/plugin-contract.d.ts +23 -0
- package/dist/scripts/contract/plugin-contract.js +28 -0
- package/dist/scripts/export-contract.d.ts +2 -0
- package/dist/scripts/export-contract.js +27 -0
- package/dist/scripts/export-plugin-contract.d.ts +2 -0
- package/dist/scripts/export-plugin-contract.js +18 -0
- package/dist/server/bare/ops/translate.js +16 -3
- package/dist/server/bare/plugins/bci-whispercpp-transcription/plugin.d.ts +2 -1
- package/dist/server/bare/plugins/ggml-ocr/plugin.js +2 -23
- package/dist/server/bare/plugins/ggml-ocr/resolve-config.d.ts +6 -0
- package/dist/server/bare/plugins/ggml-ocr/resolve-config.js +73 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +5 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.js +1 -1
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +11 -22
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +8 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +8 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +8 -3
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +6 -1
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.d.ts +3 -1
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.js +31 -2
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +129 -1
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +94 -22
- package/dist/server/bare/plugins/tts-ggml/ops/text-to-speech.js +2 -16
- package/dist/server/bare/plugins/tts-ggml/plugin.d.ts +6 -0
- package/dist/server/bare/plugins/tts-ggml/plugin.js +3 -1
- package/dist/server/bare/types/addon-responses.d.ts +2 -0
- package/dist/server/bare/utils/tts-stats.d.ts +8 -4
- package/dist/server/bare/utils/tts-stats.js +6 -0
- package/dist/server/rpc/create-server.js +23 -1
- package/dist/server/rpc/handler-registry.js +5 -0
- package/dist/server/rpc/handlers/completion-orchestrate.d.ts +42 -0
- package/dist/server/rpc/handlers/completion-orchestrate.js +190 -0
- package/dist/server/rpc/handlers/get-loaded-model-info.js +8 -2
- package/dist/server/rpc/handlers/index.d.ts +2 -0
- package/dist/server/rpc/handlers/index.js +2 -0
- package/dist/server/rpc/method-shapes.d.ts +51 -0
- package/dist/server/rpc/method-shapes.js +48 -0
- package/dist/server/rpc/profiling/operation-metrics.js +20 -0
- package/dist/server/utils/cache/auto-history.js +13 -9
- package/package.json +16 -13
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ModelTypeAliases } from '../../schemas/model-types.js';
|
|
2
|
+
import { ENGINE_TO_ADDON, LEGACY_ENGINE_TO_CANONICAL } from '../../schemas/engine-addon-map.js';
|
|
3
|
+
function sortedEntries(record) {
|
|
4
|
+
const entries = Object.entries(record);
|
|
5
|
+
entries.sort(function (a, b) {
|
|
6
|
+
return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
|
|
7
|
+
});
|
|
8
|
+
return Object.fromEntries(entries);
|
|
9
|
+
}
|
|
10
|
+
export function buildModelTypeMaps() {
|
|
11
|
+
return {
|
|
12
|
+
aliasToCanonical: sortedEntries(ModelTypeAliases),
|
|
13
|
+
engineToAddon: sortedEntries(ENGINE_TO_ADDON),
|
|
14
|
+
legacyEngineToCanonical: sortedEntries(LEGACY_ENGINE_TO_CANONICAL)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ModelConstant } from '../../models/registry/index';
|
|
2
|
+
/**
|
|
3
|
+
* JSON export of every named model constant in `@/models/registry`
|
|
4
|
+
* (`QWEN3_600M_INST_Q4`, `BCI_EMBEDDER`, ...) — the same constants JS
|
|
5
|
+
* consumers import directly and pass as `modelSrc`
|
|
6
|
+
* (`loadModel({ modelSrc: QWEN3_600M_INST_Q4, ... })`). Only the ~440
|
|
7
|
+
* promoted-to-a-named-export entries are included, not the full ~780-entry
|
|
8
|
+
* `models` array — entries without a name aren't meant for direct import in
|
|
9
|
+
* JS either.
|
|
10
|
+
*
|
|
11
|
+
* This is a separate artifact from schema.json/manifest.json: it's a static
|
|
12
|
+
* data catalog, not part of the RPC wire contract, but it lives alongside
|
|
13
|
+
* them in contract/ for the same reason — a language-neutral build artifact
|
|
14
|
+
* downstream client generators (starting with Python) consume.
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildModelsRegistry(): Record<string, ModelConstant>;
|
|
17
|
+
//# sourceMappingURL=build-models-registry.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as registry from '../../models/registry/index.js';
|
|
2
|
+
function isModelConstant(value) {
|
|
3
|
+
return (value !== null &&
|
|
4
|
+
typeof value === 'object' &&
|
|
5
|
+
!Array.isArray(value) &&
|
|
6
|
+
typeof value.src === 'string');
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* JSON export of every named model constant in `@/models/registry`
|
|
10
|
+
* (`QWEN3_600M_INST_Q4`, `BCI_EMBEDDER`, ...) — the same constants JS
|
|
11
|
+
* consumers import directly and pass as `modelSrc`
|
|
12
|
+
* (`loadModel({ modelSrc: QWEN3_600M_INST_Q4, ... })`). Only the ~440
|
|
13
|
+
* promoted-to-a-named-export entries are included, not the full ~780-entry
|
|
14
|
+
* `models` array — entries without a name aren't meant for direct import in
|
|
15
|
+
* JS either.
|
|
16
|
+
*
|
|
17
|
+
* This is a separate artifact from schema.json/manifest.json: it's a static
|
|
18
|
+
* data catalog, not part of the RPC wire contract, but it lives alongside
|
|
19
|
+
* them in contract/ for the same reason — a language-neutral build artifact
|
|
20
|
+
* downstream client generators (starting with Python) consume.
|
|
21
|
+
*/
|
|
22
|
+
export function buildModelsRegistry() {
|
|
23
|
+
const namespace = registry;
|
|
24
|
+
const entries = Object.entries(namespace).filter(function (entry) {
|
|
25
|
+
return isModelConstant(entry[1]);
|
|
26
|
+
});
|
|
27
|
+
entries.sort(function (a, b) {
|
|
28
|
+
return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
|
|
29
|
+
});
|
|
30
|
+
return Object.fromEntries(entries);
|
|
31
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { QvacPlugin } from '../../schemas/index';
|
|
2
|
+
export interface PluginHandlerContract {
|
|
3
|
+
name: string;
|
|
4
|
+
streaming: boolean;
|
|
5
|
+
requestSchema: unknown;
|
|
6
|
+
responseSchema: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginContract {
|
|
9
|
+
modelType: string;
|
|
10
|
+
displayName?: string;
|
|
11
|
+
handlers: PluginHandlerContract[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Wire contract of one registered plugin: JSON Schema per handler, in the
|
|
15
|
+
* same wire shape the built-in contract export uses (requests as input
|
|
16
|
+
* shape, responses as output shape). Custom plugins register at runtime, so
|
|
17
|
+
* unlike the built-in contract this isn't emitted by `contract:export` —
|
|
18
|
+
* plugin authors (or app builds) run `scripts/export-plugin-contract.ts` on
|
|
19
|
+
* their plugin module and feed the result to the Python generator's
|
|
20
|
+
* `--plugin` input to get typed clients for their handlers.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildPluginContract(plugin: QvacPlugin): PluginContract;
|
|
23
|
+
//# sourceMappingURL=plugin-contract.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { toWireJsonSchema } from './build-contract.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wire contract of one registered plugin: JSON Schema per handler, in the
|
|
4
|
+
* same wire shape the built-in contract export uses (requests as input
|
|
5
|
+
* shape, responses as output shape). Custom plugins register at runtime, so
|
|
6
|
+
* unlike the built-in contract this isn't emitted by `contract:export` —
|
|
7
|
+
* plugin authors (or app builds) run `scripts/export-plugin-contract.ts` on
|
|
8
|
+
* their plugin module and feed the result to the Python generator's
|
|
9
|
+
* `--plugin` input to get typed clients for their handlers.
|
|
10
|
+
*/
|
|
11
|
+
export function buildPluginContract(plugin) {
|
|
12
|
+
const handlers = Object.entries(plugin.handlers).map(function ([name, handler]) {
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
streaming: handler.streaming === true,
|
|
16
|
+
requestSchema: toWireJsonSchema(handler.requestSchema, 'input', `${plugin.modelType}.${name}.request`),
|
|
17
|
+
responseSchema: toWireJsonSchema(handler.responseSchema, 'output', `${plugin.modelType}.${name}.response`)
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
handlers.sort(function (a, b) {
|
|
21
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
modelType: plugin.modelType,
|
|
25
|
+
...(plugin.displayName && { displayName: plugin.displayName }),
|
|
26
|
+
handlers
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { contractDir, renderContractFiles } from './contract/build-contract.js';
|
|
4
|
+
const checkOnly = process.argv.includes('--check');
|
|
5
|
+
const files = await renderContractFiles();
|
|
6
|
+
await mkdir(contractDir, { recursive: true });
|
|
7
|
+
let stale = false;
|
|
8
|
+
for (const [name, content] of Object.entries(files)) {
|
|
9
|
+
const filePath = fileURLToPath(new URL(name, contractDir));
|
|
10
|
+
if (checkOnly) {
|
|
11
|
+
const existing = await readFile(filePath, 'utf8').catch(function () {
|
|
12
|
+
return null;
|
|
13
|
+
});
|
|
14
|
+
if (existing !== content) {
|
|
15
|
+
console.error(`contract/${name} is ${existing === null ? 'missing' : 'out of date'}`);
|
|
16
|
+
stale = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await writeFile(filePath, content);
|
|
21
|
+
console.log(`wrote contract/${name}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (stale) {
|
|
25
|
+
console.error("Contract artifacts are stale. Run 'bun run contract:export' and commit the result.");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { buildPluginContract } from './contract/plugin-contract.js';
|
|
4
|
+
// Usage: tsx scripts/export-plugin-contract.ts <plugin-module> <out.json>
|
|
5
|
+
// The plugin module's default export must be a definePlugin(...) value.
|
|
6
|
+
const [modulePath, outPath] = process.argv.slice(2);
|
|
7
|
+
if (!modulePath || !outPath) {
|
|
8
|
+
console.error('usage: export-plugin-contract <plugin-module> <out.json>');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const imported = (await import(pathToFileURL(modulePath).href));
|
|
12
|
+
if (!imported.default) {
|
|
13
|
+
console.error(`${modulePath} has no default export`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const contract = buildPluginContract(imported.default);
|
|
17
|
+
await writeFile(outPath, JSON.stringify(contract, null, 2) + '\n');
|
|
18
|
+
console.log(`wrote ${outPath} (${contract.handlers.length} handlers)`);
|
|
@@ -52,10 +52,10 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
52
52
|
});
|
|
53
53
|
import { getModelEntry } from '../registry/model-registry.js';
|
|
54
54
|
import { translateServerParamsSchema, normalizeModelType, ModelType, AFRICAN_LANGUAGES_MAP } from '../../../schemas/index.js';
|
|
55
|
-
import { getLangName } from '@qvac/langdetect-text';
|
|
55
|
+
import { getLangName, detectOne } from '@qvac/langdetect-text';
|
|
56
56
|
import { nowMs } from '../../../profiling/index.js';
|
|
57
57
|
import { buildStreamResult } from '../../../profiling/model-execution.js';
|
|
58
|
-
import { ModelIsDelegatedError, ModelNotFoundError, ModelTypeMismatchError } from '../../../utils/errors-server.js';
|
|
58
|
+
import { ModelIsDelegatedError, ModelNotFoundError, ModelTypeMismatchError, TranslationFailedError } from '../../../utils/errors-server.js';
|
|
59
59
|
import { getRequestRegistry, withRequestContext } from '../runtime/index.js';
|
|
60
60
|
import { generateServerRequestId } from '../runtime/request-id.js';
|
|
61
61
|
import { getServerLogger } from '../../../logging/index.js';
|
|
@@ -101,9 +101,22 @@ export async function* translate(params, requestId) {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
const isLlm = canonicalModelType === ModelType.llamacppCompletion;
|
|
104
|
-
|
|
104
|
+
let from = isLlm ? params.from : undefined;
|
|
105
105
|
const to = isLlm ? params.to : undefined;
|
|
106
106
|
const context = isLlm ? params.context : undefined;
|
|
107
|
+
// Auto-detect the source language when the caller didn't pass `from` (LLM
|
|
108
|
+
// only). This used to run in each client; moving it here gives every
|
|
109
|
+
// language binding one detector instead of each shipping its own (lingua in
|
|
110
|
+
// Python vs langdetect-text in JS drifted on the same input). Store the
|
|
111
|
+
// detected code — not the language name — so the explicit-`from` and
|
|
112
|
+
// detected paths feed getLanguage() identically.
|
|
113
|
+
if (isLlm && !from) {
|
|
114
|
+
const detected = detectOne(text);
|
|
115
|
+
if (detected.code === 'und' || detected.language === 'Undetermined') {
|
|
116
|
+
throw new TranslationFailedError("Could not detect the source language. Please specify the 'from' parameter explicitly.");
|
|
117
|
+
}
|
|
118
|
+
from = detected.code;
|
|
119
|
+
}
|
|
107
120
|
const afriquePrompt = isLlm && (isAfrican(from) || isAfrican(to));
|
|
108
121
|
translateServerParamsSchema.parse(params);
|
|
109
122
|
const fromLanguage = getLanguage(from);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import bciAddonLogging from '@qvac/bci-whispercpp/addonLogging';
|
|
1
2
|
import { type CreateModelParams, type PluginModelResult } from '../../../../schemas/index';
|
|
2
3
|
import { resolveBciConfig } from './resolve-config';
|
|
3
4
|
export declare const bciPlugin: {
|
|
@@ -164,7 +165,7 @@ export declare const bciPlugin: {
|
|
|
164
165
|
}, import("zod/v4/core").$strip>>;
|
|
165
166
|
};
|
|
166
167
|
logging: {
|
|
167
|
-
module:
|
|
168
|
+
module: typeof bciAddonLogging;
|
|
168
169
|
namespace: "bci-whispercpp-transcription";
|
|
169
170
|
};
|
|
170
171
|
};
|
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
import { definePlugin, defineHandler, ocrStreamRequestSchema, ocrStreamResponseSchema, ModelType, ocrConfigSchema, ADDON_OCR } from '../../../../schemas/index.js';
|
|
2
2
|
import { ModelLoadFailedError } from '../../../../utils/errors-server.js';
|
|
3
|
-
import { hyperdriveUrlSchema } from '../../../../schemas/load-model.js';
|
|
4
3
|
import { createStreamLogger, registerAddonLogger } from '../../../../logging/index.js';
|
|
5
4
|
import ocrAddonLogging from '@qvac/ocr-ggml/addonLogging.js';
|
|
6
5
|
import { OcrGgml } from '@qvac/ocr-ggml';
|
|
7
6
|
import { ocr } from './ops/ocr-stream.js';
|
|
8
7
|
import { attachModelExecutionMs } from '../../../../profiling/model-execution.js';
|
|
9
|
-
import {
|
|
10
|
-
const OCR_DETECTOR_FILENAME = 'craft_mlt_25k.gguf';
|
|
11
|
-
function deriveDetectorSource(modelSrc) {
|
|
12
|
-
if (modelSrc.startsWith('pear://')) {
|
|
13
|
-
const { key } = hyperdriveUrlSchema.parse(modelSrc);
|
|
14
|
-
return `pear://${key}/${OCR_DETECTOR_FILENAME}`;
|
|
15
|
-
}
|
|
16
|
-
if (modelSrc.startsWith('registry://')) {
|
|
17
|
-
return OCR_CRAFT.src;
|
|
18
|
-
}
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
8
|
+
import { resolveOcrConfig } from './resolve-config.js';
|
|
21
9
|
function createOCRModel(modelId, detectorPath, recognizerPath, ocrConfig) {
|
|
22
10
|
const logger = createStreamLogger(modelId, ModelType.ggmlOcr);
|
|
23
11
|
registerAddonLogger(modelId, ModelType.ggmlOcr, logger);
|
|
@@ -66,16 +54,7 @@ export const ocrPlugin = definePlugin({
|
|
|
66
54
|
addonPackage: ADDON_OCR,
|
|
67
55
|
loadConfigSchema: ocrConfigSchema,
|
|
68
56
|
async resolveConfig(cfg, ctx) {
|
|
69
|
-
|
|
70
|
-
const detectorSrc = detectorModelSrc ?? deriveDetectorSource(ctx.modelSrc);
|
|
71
|
-
if (!detectorSrc) {
|
|
72
|
-
throw new ModelLoadFailedError('Detector model required for OCR. Use a hyperdrive source or provide detectorModelSrc');
|
|
73
|
-
}
|
|
74
|
-
const detectorModelPath = await ctx.resolveModelPath(detectorSrc);
|
|
75
|
-
return {
|
|
76
|
-
config: ocrConfig,
|
|
77
|
-
artifacts: { detectorModelPath }
|
|
78
|
-
};
|
|
57
|
+
return resolveOcrConfig(cfg, ctx);
|
|
79
58
|
},
|
|
80
59
|
createModel(params) {
|
|
81
60
|
const ocrConfig = (params.modelConfig ?? {});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type OCRConfig, type ResolveContext, type ResolveResult } from '../../../../schemas/index';
|
|
2
|
+
type OcrPipelineType = NonNullable<OCRConfig['pipelineType']>;
|
|
3
|
+
export declare const DETECTOR_FILENAMES: Record<OcrPipelineType, string>;
|
|
4
|
+
export declare function resolveOcrConfig(cfg: OCRConfig, ctx: ResolveContext): Promise<ResolveResult<Record<string, unknown>, 'detectorModelPath'>>;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=resolve-config.d.ts.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {} from '../../../../schemas/index.js';
|
|
2
|
+
import { ModelLoadFailedError } from '../../../../utils/errors-server.js';
|
|
3
|
+
import { hyperdriveUrlSchema } from '../../../../schemas/load-model.js';
|
|
4
|
+
import { OCR_CRAFT, OCR_DOCTR_1 } from '../../../../models/registry/index.js';
|
|
5
|
+
// Detector weights paired with each pipeline. EasyOCR runs CRAFT
|
|
6
|
+
// detection, DocTR runs DBNet detection — the addon cannot load one
|
|
7
|
+
// pipeline's detector into the other.
|
|
8
|
+
export const DETECTOR_FILENAMES = {
|
|
9
|
+
easyocr: 'craft_mlt_25k.gguf',
|
|
10
|
+
doctr: 'db_mobilenet_v3_large.gguf'
|
|
11
|
+
};
|
|
12
|
+
const DETECTOR_REGISTRY_SOURCES = {
|
|
13
|
+
easyocr: OCR_CRAFT.src,
|
|
14
|
+
doctr: OCR_DOCTR_1.src
|
|
15
|
+
};
|
|
16
|
+
// The addon defaults to the EasyOCR pipeline, so a DocTR recognizer
|
|
17
|
+
// loaded without an explicit `pipelineType` dies deep in the EasyOCR
|
|
18
|
+
// CRNN weight loader with a raw missing-tensor error (QVAC-22514).
|
|
19
|
+
// Recognize the known DocTR registry artifacts by name and opt in to
|
|
20
|
+
// the DocTR pipeline automatically.
|
|
21
|
+
function inferPipelineType(recognizerSrc) {
|
|
22
|
+
const path = srcPathname(recognizerSrc);
|
|
23
|
+
const filename = path.split('/').pop() ?? '';
|
|
24
|
+
if (filename === 'crnn_mobilenet_v3_small.gguf' || path.includes('/gguf/doctr')) {
|
|
25
|
+
return 'doctr';
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
function srcPathname(src) {
|
|
30
|
+
return (src.split(/[?#]/)[0] ?? '').toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
function srcToString(src) {
|
|
33
|
+
return typeof src === 'string' ? src : src.src;
|
|
34
|
+
}
|
|
35
|
+
// The registry still lists legacy `.onnx` OCR artifacts under the
|
|
36
|
+
// "@qvac/ocr-onnx" engine; the ggml-ocr addon can only open GGUF, so a
|
|
37
|
+
// raw "failed to open GGUF" surfaces mid-load. Fail fast with the
|
|
38
|
+
// supported configurations instead (QVAC-22514).
|
|
39
|
+
function assertGgufSource(src, role) {
|
|
40
|
+
if (!srcPathname(src).endsWith('.onnx'))
|
|
41
|
+
return;
|
|
42
|
+
throw new ModelLoadFailedError(`ggml-ocr only loads GGUF models, but the ${role} source points to an ONNX file: ${src}. ` +
|
|
43
|
+
'The .onnx OCR registry entries are legacy artifacts of the removed "onnx-ocr" engine. ' +
|
|
44
|
+
'Supported registry configurations: EasyOCR (default) — modelSrc: OCR_LATIN.src (detector OCR_CRAFT auto-derived); ' +
|
|
45
|
+
'DocTR — modelSrc: OCR_DOCTR.src (pipelineType and detector OCR_DOCTR_1 auto-derived).');
|
|
46
|
+
}
|
|
47
|
+
function deriveDetectorSource(modelSrc, pipelineType) {
|
|
48
|
+
if (modelSrc.startsWith('pear://')) {
|
|
49
|
+
const { key } = hyperdriveUrlSchema.parse(modelSrc);
|
|
50
|
+
return `pear://${key}/${DETECTOR_FILENAMES[pipelineType]}`;
|
|
51
|
+
}
|
|
52
|
+
if (modelSrc.startsWith('registry://')) {
|
|
53
|
+
return DETECTOR_REGISTRY_SOURCES[pipelineType];
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
export async function resolveOcrConfig(cfg, ctx) {
|
|
58
|
+
const { detectorModelSrc, ...ocrConfig } = cfg;
|
|
59
|
+
assertGgufSource(ctx.modelSrc, 'recognizer');
|
|
60
|
+
const pipelineType = ocrConfig.pipelineType ?? inferPipelineType(ctx.modelSrc) ?? 'easyocr';
|
|
61
|
+
const detectorSrc = detectorModelSrc ?? deriveDetectorSource(ctx.modelSrc, pipelineType);
|
|
62
|
+
if (!detectorSrc) {
|
|
63
|
+
throw new ModelLoadFailedError(`Detector model required for OCR (${pipelineType} pipeline expects ` +
|
|
64
|
+
`${DETECTOR_FILENAMES[pipelineType]}). Use a hyperdrive or registry ` +
|
|
65
|
+
'source, or provide detectorModelSrc');
|
|
66
|
+
}
|
|
67
|
+
assertGgufSource(srcToString(detectorSrc), 'detector');
|
|
68
|
+
const detectorModelPath = await ctx.resolveModelPath(detectorSrc);
|
|
69
|
+
return {
|
|
70
|
+
config: { ...ocrConfig, pipelineType },
|
|
71
|
+
artifacts: { detectorModelPath }
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -53,6 +53,11 @@ export declare const vlaPlugin: {
|
|
|
53
53
|
discrete: "discrete";
|
|
54
54
|
continuous: "continuous";
|
|
55
55
|
}>>;
|
|
56
|
+
imageInputMode: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
57
|
+
pixels: "pixels";
|
|
58
|
+
patches: "patches";
|
|
59
|
+
}>>;
|
|
60
|
+
imagePatchElems: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
56
61
|
}, import("zod/v4/core").$strip>;
|
|
57
62
|
backendName: import("zod").ZodNullable<import("zod").ZodString>;
|
|
58
63
|
}, import("zod/v4/core").$strip>>;
|
|
@@ -16,7 +16,7 @@ function wrapVlaModel(inner, loadOpts) {
|
|
|
16
16
|
}
|
|
17
17
|
export const vlaPlugin = definePlugin({
|
|
18
18
|
modelType: ModelType.ggmlVla,
|
|
19
|
-
displayName: 'VLA (SmolVLA / π₀.₅ ggml)',
|
|
19
|
+
displayName: 'VLA (SmolVLA / π₀.₅ / GR00T ggml)',
|
|
20
20
|
addonPackage: ADDON_VLA,
|
|
21
21
|
loadConfigSchema: vlaConfigSchema,
|
|
22
22
|
createModel(params) {
|
|
@@ -2,7 +2,7 @@ import { TOOLS_MODE } from '../../../../../schemas/tools.js';
|
|
|
2
2
|
import { logCacheDisabled, logCacheInit, logCacheSave, logMessagesToAddon } from './cache-logger.js';
|
|
3
3
|
import { extractSystemPrompt, getCurrentCacheInfo } from '../../../ops/kv-cache-utils.js';
|
|
4
4
|
import { getModel, getModelConfig } from '../../../registry/model-registry.js';
|
|
5
|
-
import { decideCachedHistorySlice } from './kv-cache-state.js';
|
|
5
|
+
import { decideCachedHistorySlice, shouldCommitCachedTurn } from './kv-cache-state.js';
|
|
6
6
|
import { createKvCacheSession, generateConfigHash } from './kv-cache-session.js';
|
|
7
7
|
import { appendToolsToHistory, detectToolDialect, prependToolsToHistory } from '../../../../utils/tool-integration.js';
|
|
8
8
|
import { parseToolCalls } from '../../../../utils/tools/index.js';
|
|
@@ -15,22 +15,6 @@ import { buildStreamResult } from '../../../../../profiling/model-execution.js';
|
|
|
15
15
|
import { normalizeCompletionStats } from './completion-stats.js';
|
|
16
16
|
import fs from 'bare-fs';
|
|
17
17
|
const logger = getServerLogger();
|
|
18
|
-
/**
|
|
19
|
-
* Decide whether a completed turn earned the right to record its kv-cache
|
|
20
|
-
* boundary. A `savedCount` is only safe to write when the turn ran to
|
|
21
|
-
* completion AND produced at least one token — anything else (cancelled
|
|
22
|
-
* mid-decode, zero-token reply, early EOS) leaves the on-disk cache file
|
|
23
|
-
* in an unknown state relative to `history.length + 1`, and a stale entry
|
|
24
|
-
* would slice the next turn's history down to an empty payload.
|
|
25
|
-
*
|
|
26
|
-
* Replaces the pre-0.11.0 `shouldRecordSavedCount(wasCancelled, ...)` with
|
|
27
|
-
* a signal-driven check that reads directly from the request's
|
|
28
|
-
* `AbortSignal`. The local helper keeps the call sites in
|
|
29
|
-
* `completion-stream.ts` honest without importing the registry every time.
|
|
30
|
-
*/
|
|
31
|
-
function shouldCommitTurn(signal, producedTokens) {
|
|
32
|
-
return !signal.aborted && producedTokens;
|
|
33
|
-
}
|
|
34
18
|
function transformMessage(message) {
|
|
35
19
|
const transformed = [];
|
|
36
20
|
// Check if it's a tool definition (has type: "function")
|
|
@@ -338,11 +322,17 @@ export async function* completion(params, opts) {
|
|
|
338
322
|
/* cacheExists */ true, history, dynamicTools ? tools : undefined);
|
|
339
323
|
logMessagesToAddon(messagesToSend, 'PROMPT_SEND');
|
|
340
324
|
const result = yield* processModelResponse(model, messagesToSend, tools, mergedGenerationParams, { cacheKey: turn.cachePath, saveCacheToDisk: true }, dialect);
|
|
325
|
+
const shouldCommitTurn = shouldCommitCachedTurn({
|
|
326
|
+
aborted: signal.aborted,
|
|
327
|
+
producedTokens: result.producedTokens,
|
|
328
|
+
generatedTokens: result.stats?.generatedTokens,
|
|
329
|
+
predict: mergedGenerationParams?.predict ?? modelConfig.predict
|
|
330
|
+
});
|
|
341
331
|
if (typeof kvCache === 'string') {
|
|
342
332
|
// Custom-key path: the addon wrote the new cache state inline at
|
|
343
333
|
// the same path. Either commit (records the boundary, suppresses
|
|
344
334
|
// rollback) or fall through to the deferred rollback.
|
|
345
|
-
if (shouldCommitTurn
|
|
335
|
+
if (shouldCommitTurn) {
|
|
346
336
|
await session.commitTurn(turn, {
|
|
347
337
|
kind: 'static',
|
|
348
338
|
messageCount: history.length + 1
|
|
@@ -362,10 +352,9 @@ export async function* completion(params, opts) {
|
|
|
362
352
|
logger.warn(`[kv-cache] Auto cache tool-call turn; rolling back to avoid disk leak. path=${turn.cachePath}`);
|
|
363
353
|
return result;
|
|
364
354
|
}
|
|
365
|
-
if (!shouldCommitTurn
|
|
366
|
-
// Cancelled
|
|
367
|
-
//
|
|
368
|
-
// deferred rollback unlink it.
|
|
355
|
+
if (!shouldCommitTurn) {
|
|
356
|
+
// Cancelled, zero-token, or budget-exhausted turns do not establish
|
|
357
|
+
// a trustworthy message boundary.
|
|
369
358
|
return result;
|
|
370
359
|
}
|
|
371
360
|
const savedHistory = buildAutoCacheSaveHistory(history.map((msg) => ({
|
|
@@ -32,6 +32,13 @@ export interface HistorySliceDecision {
|
|
|
32
32
|
*/
|
|
33
33
|
clearStaleCount: boolean;
|
|
34
34
|
}
|
|
35
|
+
interface CacheCommitContext {
|
|
36
|
+
aborted: boolean;
|
|
37
|
+
producedTokens: boolean;
|
|
38
|
+
generatedTokens?: number | undefined;
|
|
39
|
+
predict?: number | undefined;
|
|
40
|
+
}
|
|
41
|
+
export declare function shouldCommitCachedTurn(context: CacheCommitContext): boolean;
|
|
35
42
|
/**
|
|
36
43
|
* Pure slice decision for `prepareMessagesForCache`.
|
|
37
44
|
*
|
|
@@ -46,4 +53,5 @@ export interface HistorySliceDecision {
|
|
|
46
53
|
* than handing the model an empty payload.
|
|
47
54
|
*/
|
|
48
55
|
export declare function decideCachedHistorySlice(savedCount: number, cacheExists: boolean, history: HistoryMessage[]): HistorySliceDecision;
|
|
56
|
+
export {};
|
|
49
57
|
//# sourceMappingURL=kv-cache-state.d.ts.map
|
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
* layers (saved counts, init flags, on-disk files). Only the pure
|
|
15
15
|
* slice-decision helper remains here.
|
|
16
16
|
*/
|
|
17
|
+
export function shouldCommitCachedTurn(context) {
|
|
18
|
+
const { aborted, producedTokens, generatedTokens, predict } = context;
|
|
19
|
+
const stoppedByBudget = predict !== undefined &&
|
|
20
|
+
predict > 0 &&
|
|
21
|
+
generatedTokens !== undefined &&
|
|
22
|
+
generatedTokens >= predict;
|
|
23
|
+
return !aborted && producedTokens && !stoppedByBudget;
|
|
24
|
+
}
|
|
17
25
|
/**
|
|
18
26
|
* Pure slice decision for `prepareMessagesForCache`.
|
|
19
27
|
*
|
|
@@ -15,7 +15,12 @@ export declare const llmPlugin: {
|
|
|
15
15
|
predict: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodLiteral<-1>, import("zod").ZodLiteral<-2>, import("zod").ZodNumber]>>;
|
|
16
16
|
system_prompt: import("zod").ZodOptional<import("zod").ZodString>;
|
|
17
17
|
no_mmap: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
18
|
-
verbosity: import("zod").ZodOptional<import("zod").
|
|
18
|
+
verbosity: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
19
|
+
readonly ERROR: 0;
|
|
20
|
+
readonly WARN: 1;
|
|
21
|
+
readonly INFO: 2;
|
|
22
|
+
readonly DEBUG: 3;
|
|
23
|
+
}>>;
|
|
19
24
|
presence_penalty: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
20
25
|
frequency_penalty: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
21
26
|
repeat_penalty: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
@@ -220,7 +225,7 @@ export declare const llmPlugin: {
|
|
|
220
225
|
integer: "integer";
|
|
221
226
|
}>;
|
|
222
227
|
description: import("zod").ZodOptional<import("zod").ZodString>;
|
|
223
|
-
enum: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString
|
|
228
|
+
enum: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodNumber, import("zod").ZodBoolean, import("zod").ZodNull]>>>;
|
|
224
229
|
}, import("zod/v4/core").$strip>>;
|
|
225
230
|
required: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
226
231
|
}, import("zod/v4/core").$strip>;
|
|
@@ -343,7 +348,7 @@ export declare const llmPlugin: {
|
|
|
343
348
|
integer: "integer";
|
|
344
349
|
}>;
|
|
345
350
|
description: import("zod").ZodOptional<import("zod").ZodString>;
|
|
346
|
-
enum: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString
|
|
351
|
+
enum: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodNumber, import("zod").ZodBoolean, import("zod").ZodNull]>>>;
|
|
347
352
|
}, import("zod/v4/core").$strip>>;
|
|
348
353
|
required: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
349
354
|
}, import("zod/v4/core").$strip>;
|
|
@@ -37,7 +37,12 @@ export declare const embeddingsPlugin: {
|
|
|
37
37
|
row: "row";
|
|
38
38
|
}>>;
|
|
39
39
|
tensorSplit: import("zod").ZodOptional<import("zod").ZodString>;
|
|
40
|
-
verbosity: import("zod").ZodOptional<import("zod").
|
|
40
|
+
verbosity: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
41
|
+
readonly ERROR: 0;
|
|
42
|
+
readonly WARN: 1;
|
|
43
|
+
readonly INFO: 2;
|
|
44
|
+
readonly DEBUG: 3;
|
|
45
|
+
}>>;
|
|
41
46
|
openclCacheDir: import("zod").ZodOptional<import("zod").ZodString>;
|
|
42
47
|
}, import("zod/v4/core").$strip>;
|
|
43
48
|
createModel(params: CreateModelParams): PluginModelResult;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { VideoStableDiffusion } from '@qvac/diffusion-cpp';
|
|
2
|
+
import { type VideoRequest, type VideoStreamResponse } from '../../../../../schemas/sdcpp-config';
|
|
3
|
+
export declare function markLtxVideoModel(model: VideoStableDiffusion): void;
|
|
2
4
|
export declare function video(request: VideoRequest): AsyncGenerator<VideoStreamResponse>;
|
|
3
5
|
//# sourceMappingURL=video.d.ts.map
|
|
@@ -55,8 +55,32 @@ import { getServerLogger } from '../../../../../logging/index.js';
|
|
|
55
55
|
import { getModel, getModelEntry } from '../../../registry/model-registry.js';
|
|
56
56
|
import { getRequestRegistry, withRequestContext } from '../../../runtime/index.js';
|
|
57
57
|
import { generateServerRequestId } from '../../../runtime/request-id.js';
|
|
58
|
-
import { ModelOperationNotSupportedError } from '../../../../../utils/errors-server.js';
|
|
58
|
+
import { ModelOperationNotSupportedError, PluginRequestValidationFailedError } from '../../../../../utils/errors-server.js';
|
|
59
|
+
import { formatZodError } from '../../../../../utils/zod-error.js';
|
|
59
60
|
import { ModelType } from '../../../../../schemas/index.js';
|
|
61
|
+
import { ltxVideoRequestSchema } from '../../../../../schemas/sdcpp-config.js';
|
|
62
|
+
const ltxVideoModels = new WeakSet();
|
|
63
|
+
export function markLtxVideoModel(model) {
|
|
64
|
+
ltxVideoModels.add(model);
|
|
65
|
+
}
|
|
66
|
+
function parseLtxVideoRequest(request) {
|
|
67
|
+
const result = ltxVideoRequestSchema.safeParse(request);
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
throw new PluginRequestValidationFailedError('videoStream', formatZodError(result.error));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// The addon reports `hasAudio` as a numeric flag (1/0); the SDK surfaces it as
|
|
73
|
+
// a boolean so consumers get a readable `stats.hasAudio` instead of a magic
|
|
74
|
+
// number. Everything else passes through and is validated client-side.
|
|
75
|
+
function toVideoStats(stats) {
|
|
76
|
+
if (!stats)
|
|
77
|
+
return undefined;
|
|
78
|
+
const { hasAudio, ...rest } = stats;
|
|
79
|
+
return {
|
|
80
|
+
...rest,
|
|
81
|
+
...(hasAudio !== undefined && { hasAudio: hasAudio !== 0 })
|
|
82
|
+
};
|
|
83
|
+
}
|
|
60
84
|
// The diffusion plugin instantiates `VideoStableDiffusion` when the model is
|
|
61
85
|
// loaded with `modelConfig.mode === "video"` and `ImgStableDiffusion` (or
|
|
62
86
|
// `EsrganUpscaler`) otherwise. Those have no video `run()` shape, so we refuse
|
|
@@ -80,6 +104,8 @@ export async function* video(request) {
|
|
|
80
104
|
}), true);
|
|
81
105
|
const requestLogger = withRequestContext(getServerLogger(), ctx);
|
|
82
106
|
const model = asVideoModel(getModel(request.modelId), request.modelId);
|
|
107
|
+
if (ltxVideoModels.has(model))
|
|
108
|
+
parseLtxVideoRequest(request);
|
|
83
109
|
const onAbort = () => {
|
|
84
110
|
model.cancel().catch((err) => {
|
|
85
111
|
requestLogger.warn(`[cancel] model.cancel() rejected during abort for modelId=${request.modelId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -148,6 +174,9 @@ export async function* video(request) {
|
|
|
148
174
|
...(request.vae_tile_overlap !== undefined && {
|
|
149
175
|
vae_tile_overlap: request.vae_tile_overlap
|
|
150
176
|
}),
|
|
177
|
+
...(request.temporal_tiling !== undefined && {
|
|
178
|
+
temporal_tiling: request.temporal_tiling
|
|
179
|
+
}),
|
|
151
180
|
...(request.cache_mode !== undefined && { cache_mode: request.cache_mode }),
|
|
152
181
|
...(request.cache_preset !== undefined && {
|
|
153
182
|
cache_preset: request.cache_preset
|
|
@@ -188,7 +217,7 @@ export async function* video(request) {
|
|
|
188
217
|
yield {
|
|
189
218
|
type: 'videoStream',
|
|
190
219
|
done: true,
|
|
191
|
-
stats: responseWithStats.stats
|
|
220
|
+
stats: toVideoStats(responseWithStats.stats)
|
|
192
221
|
};
|
|
193
222
|
}
|
|
194
223
|
catch (e_1) {
|