@qvac/sdk 0.10.0 → 0.10.1

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.
Files changed (27) hide show
  1. package/NOTICE +28 -28
  2. package/dist/examples/{llamacpp-native-tools.js → tools/llamacpp-native-tools.js} +4 -47
  3. package/dist/examples/tools/llamacpp-tools-harmony-multiturn.d.ts +2 -0
  4. package/dist/examples/tools/llamacpp-tools-harmony-multiturn.js +81 -0
  5. package/dist/examples/{llamacpp-tools-pythonic.js → tools/llamacpp-tools-pythonic.js} +1 -20
  6. package/dist/examples/tools/shared.d.ts +21 -0
  7. package/dist/examples/tools/shared.js +42 -0
  8. package/dist/models/registry/models.d.ts +312 -1231
  9. package/dist/models/registry/models.js +2081 -3025
  10. package/dist/models/update-models/registry.d.ts +1 -0
  11. package/dist/models/update-models/registry.js +19 -1
  12. package/dist/schemas/common.d.ts +1 -0
  13. package/dist/schemas/completion-stream.d.ts +4 -0
  14. package/dist/schemas/completion-stream.js +2 -1
  15. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +1 -0
  16. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +3 -3
  17. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +1 -82
  18. package/dist/server/bare/plugins/nmtcpp-translation/resolve-vocab.d.ts +32 -0
  19. package/dist/server/bare/plugins/nmtcpp-translation/resolve-vocab.js +117 -0
  20. package/dist/server/utils/completion-normalizer.js +168 -56
  21. package/dist/server/utils/tools/dialect.js +2 -0
  22. package/dist/server/utils/tools/parser.js +6 -0
  23. package/dist/server/utils/tools/parsers/harmony.d.ts +4 -0
  24. package/dist/server/utils/tools/parsers/harmony.js +59 -0
  25. package/package.json +1 -1
  26. /package/dist/examples/{llamacpp-native-tools.d.ts → tools/llamacpp-native-tools.d.ts} +0 -0
  27. /package/dist/examples/{llamacpp-tools-pythonic.d.ts → tools/llamacpp-tools-pythonic.d.ts} +0 -0
@@ -1,4 +1,5 @@
1
1
  import type { CollectOptions, ProcessedModel } from "./types";
2
2
  export { processRegistryModel, extractModelName, toHexString, } from "./processing";
3
3
  export declare function collectModels(options?: CollectOptions): Promise<ProcessedModel[]>;
4
+ export declare function deduplicateModels(models: ProcessedModel[], showDuplicates: boolean): ProcessedModel[];
4
5
  //# sourceMappingURL=registry.d.ts.map
@@ -32,7 +32,20 @@ export async function collectModels(options = {}) {
32
32
  }
33
33
  return deduplicateModels(withCompanions, showDuplicates);
34
34
  }
35
- function deduplicateModels(models, showDuplicates) {
35
+ export function deduplicateModels(models, showDuplicates) {
36
+ // Paths referenced as companion files in any companion set. Dropping one of
37
+ // these on sha256 collision would leave its companion set without a
38
+ // standalone RegistryItem that `getModelByPath()` can resolve, causing
39
+ // recursive registry resolutions (e.g. the Bergamot shared vocab) to fall
40
+ // into the metadata-less fallback where `expectedSize` is `0`.
41
+ const companionReferencedPaths = new Set();
42
+ for (const m of models) {
43
+ if (!m.companionSet)
44
+ continue;
45
+ for (const f of m.companionSet.files) {
46
+ companionReferencedPaths.add(`${f.registrySource}:${f.registryPath}`);
47
+ }
48
+ }
36
49
  const seenChecksums = new Map();
37
50
  const dedupedModels = [];
38
51
  const skipped = [];
@@ -42,6 +55,11 @@ function deduplicateModels(models, showDuplicates) {
42
55
  continue;
43
56
  }
44
57
  if (seenChecksums.has(model.sha256Checksum)) {
58
+ const pathKey = `${model.registrySource}:${model.registryPath}`;
59
+ if (companionReferencedPaths.has(pathKey)) {
60
+ dedupedModels.push(model);
61
+ continue;
62
+ }
45
63
  skipped.push({
46
64
  name: model.registryPath,
47
65
  checksum: model.sha256Checksum,
@@ -3384,6 +3384,7 @@ export declare const requestSchema: z.ZodUnion<readonly [z.ZodObject<{
3384
3384
  hermes: "hermes";
3385
3385
  pythonic: "pythonic";
3386
3386
  json: "json";
3387
+ harmony: "harmony";
3387
3388
  }>>;
3388
3389
  responseFormat: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
3389
3390
  type: z.ZodLiteral<"text">;
@@ -8,11 +8,13 @@ export { completionStatsSchema, type CompletionStats } from "./completion-event"
8
8
  * - `"hermes"`: `<tool_call>{"name":"get_weather","arguments":{"city":"Tokyo"}}</tool_call>`
9
9
  * - `"pythonic"`: `[get_weather(city="Tokyo")]` (optionally `<|tool_call_start|>...<|tool_call_end|>`-wrapped)
10
10
  * - `"json"`: `{"name":"get_weather","arguments":{"city":"Tokyo"}}` or `{"tool_calls":[{"name":"...","arguments":{...}}]}`
11
+ * - `"harmony"`: `<|channel|>commentary to=functions.get_weather <|constrain|>json<|message|>{"city":"Tokyo"}<|call|>`
11
12
  */
12
13
  export declare const toolDialectSchema: z.ZodEnum<{
13
14
  hermes: "hermes";
14
15
  pythonic: "pythonic";
15
16
  json: "json";
17
+ harmony: "harmony";
16
18
  }>;
17
19
  export declare const attachmentSchema: z.ZodObject<{
18
20
  path: z.ZodString;
@@ -99,6 +101,7 @@ export declare const completionClientParamsSchema: z.ZodObject<{
99
101
  hermes: "hermes";
100
102
  pythonic: "pythonic";
101
103
  json: "json";
104
+ harmony: "harmony";
102
105
  }>>;
103
106
  responseFormat: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
104
107
  type: z.ZodLiteral<"text">;
@@ -162,6 +165,7 @@ export declare const completionStreamRequestSchema: z.ZodObject<{
162
165
  hermes: "hermes";
163
166
  pythonic: "pythonic";
164
167
  json: "json";
168
+ harmony: "harmony";
165
169
  }>>;
166
170
  responseFormat: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
167
171
  type: z.ZodLiteral<"text">;
@@ -10,8 +10,9 @@ export { completionStatsSchema } from "./completion-event.js";
10
10
  * - `"hermes"`: `<tool_call>{"name":"get_weather","arguments":{"city":"Tokyo"}}</tool_call>`
11
11
  * - `"pythonic"`: `[get_weather(city="Tokyo")]` (optionally `<|tool_call_start|>...<|tool_call_end|>`-wrapped)
12
12
  * - `"json"`: `{"name":"get_weather","arguments":{"city":"Tokyo"}}` or `{"tool_calls":[{"name":"...","arguments":{...}}]}`
13
+ * - `"harmony"`: `<|channel|>commentary to=functions.get_weather <|constrain|>json<|message|>{"city":"Tokyo"}<|call|>`
13
14
  */
14
- export const toolDialectSchema = z.enum(["hermes", "pythonic", "json"]);
15
+ export const toolDialectSchema = z.enum(["hermes", "pythonic", "json", "harmony"]);
15
16
  export const attachmentSchema = z.object({
16
17
  path: z
17
18
  .string()
@@ -165,6 +165,7 @@ export declare const llmPlugin: {
165
165
  hermes: "hermes";
166
166
  pythonic: "pythonic";
167
167
  json: "json";
168
+ harmony: "harmony";
168
169
  }>>;
169
170
  responseFormat: import("zod").ZodOptional<import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
170
171
  type: import("zod").ZodLiteral<"text">;
@@ -91,9 +91,9 @@ export const llmPlugin = definePlugin({
91
91
  toolCalling: toolsActive ? "textParse" : "none",
92
92
  thinkingFraming: request.captureThinking ? "thinkTags" : "none",
93
93
  };
94
- const dialect = toolsActive
95
- ? (request.toolDialect ?? detectToolDialect(request.modelId))
96
- : "hermes";
94
+ // Dialect runs regardless of tool availability — thinking/content
95
+ // stripping is needed even on plain completions.
96
+ const dialect = request.toolDialect ?? detectToolDialect(request.modelId);
97
97
  const normalizer = createCompletionNormalizer({
98
98
  capabilities,
99
99
  tools: request.tools ?? [],
@@ -3,35 +3,9 @@ import TranslationNmtcpp, {} from "@qvac/translation-nmtcpp";
3
3
  import { definePlugin, defineHandler, translateRequestSchema, translateResponseSchema, ModelType, nmtConfigBaseSchema, ADDON_NMT, BERGAMOT_CJK_LANG_PAIRS, } from "../../../../schemas/index.js";
4
4
  import { createStreamLogger, registerAddonLogger } from "../../../../logging/index.js";
5
5
  import path from "bare-path";
6
- import { ModelLoadFailedError } from "../../../../utils/errors-server.js";
7
6
  import { translate } from "../../ops/translate.js";
8
7
  import { attachModelExecutionMs } from "../../../../profiling/model-execution.js";
9
- function buildBergamotVocabSources(basePath, langPair) {
10
- if (BERGAMOT_CJK_LANG_PAIRS.includes(langPair)) {
11
- return {
12
- srcVocabSrc: `${basePath}srcvocab.${langPair}.spm`,
13
- dstVocabSrc: `${basePath}trgvocab.${langPair}.spm`,
14
- };
15
- }
16
- const sharedVocab = `${basePath}vocab.${langPair}.spm`;
17
- return { srcVocabSrc: sharedVocab, dstVocabSrc: sharedVocab };
18
- }
19
- function deriveBergamotVocabSources(modelSrc) {
20
- const match = modelSrc.match(/^pear:\/\/([a-f0-9]+)\/model\.([a-z]+)\.intgemm\.alphas\.bin$/);
21
- if (!match?.[1] || !match[2])
22
- return null;
23
- const basePath = `pear://${match[1]}/`;
24
- const langPair = match[2];
25
- return buildBergamotVocabSources(basePath, langPair);
26
- }
27
- function deriveBergamotRegistryVocabSources(modelSrc) {
28
- const match = modelSrc.match(/^(registry:\/\/.+\/)model\.([a-z]+)\.intgemm\.alphas\.bin$/);
29
- if (!match?.[1] || !match[2])
30
- return null;
31
- const basePath = match[1];
32
- const langPair = match[2];
33
- return buildBergamotVocabSources(basePath, langPair);
34
- }
8
+ import { resolveBergamotVocab, } from "./resolve-vocab.js";
35
9
  /**
36
10
  * Derive absolute vocab paths from a resolved Bergamot model path.
37
11
  * Works for both companion-set layout (colocated files) and any layout where
@@ -101,61 +75,6 @@ function createNmtModel(modelId, modelPath, nmtConfig, srcVocabPath, dstVocabPat
101
75
  });
102
76
  return { model };
103
77
  }
104
- async function resolveBergamotVocab(nmtConfig, ctx, srcVocabSrc, dstVocabSrc, pivotModel) {
105
- let srcSrc = srcVocabSrc;
106
- let dstSrc = dstVocabSrc;
107
- if (!srcSrc || !dstSrc) {
108
- const derived = ctx.modelSrc.startsWith("pear://")
109
- ? deriveBergamotVocabSources(ctx.modelSrc)
110
- : ctx.modelSrc.startsWith("registry://")
111
- ? deriveBergamotRegistryVocabSources(ctx.modelSrc)
112
- : null;
113
- if (derived) {
114
- srcSrc = srcSrc ?? derived.srcVocabSrc;
115
- dstSrc = dstSrc ?? derived.dstVocabSrc;
116
- }
117
- }
118
- if (!srcSrc || !dstSrc) {
119
- throw new ModelLoadFailedError("Bergamot requires srcVocabSrc and dstVocabSrc. Provide them in modelConfig or use a pear:// or registry:// model source for auto-derivation.");
120
- }
121
- if (!pivotModel) {
122
- const [srcVocabPath, dstVocabPath] = await Promise.all([
123
- ctx.resolveModelPath(srcSrc),
124
- ctx.resolveModelPath(dstSrc),
125
- ]);
126
- return {
127
- config: nmtConfig,
128
- artifacts: { srcVocabPath, dstVocabPath },
129
- };
130
- }
131
- let pivotSrcSrc = pivotModel.srcVocabSrc;
132
- let pivotDstSrc = pivotModel.dstVocabSrc;
133
- if (!pivotSrcSrc || !pivotDstSrc) {
134
- const pivotDerived = pivotModel.modelSrc.startsWith("pear://")
135
- ? deriveBergamotVocabSources(pivotModel.modelSrc)
136
- : pivotModel.modelSrc.startsWith("registry://")
137
- ? deriveBergamotRegistryVocabSources(pivotModel.modelSrc)
138
- : null;
139
- if (pivotDerived) {
140
- pivotSrcSrc = pivotSrcSrc ?? pivotDerived.srcVocabSrc;
141
- pivotDstSrc = pivotDstSrc ?? pivotDerived.dstVocabSrc;
142
- }
143
- }
144
- if (!pivotSrcSrc || !pivotDstSrc) {
145
- throw new ModelLoadFailedError("Bergamot pivot model requires srcVocabSrc and dstVocabSrc. Provide them in modelConfig or use a pear:// or registry:// model source for auto-derivation.");
146
- }
147
- const [srcVocabPath, dstVocabPath, pivotSrcVocabPath, pivotDstVocabPath, pivotModelPath] = await Promise.all([
148
- ctx.resolveModelPath(srcSrc),
149
- ctx.resolveModelPath(dstSrc),
150
- ctx.resolveModelPath(pivotSrcSrc),
151
- ctx.resolveModelPath(pivotDstSrc),
152
- ctx.resolveModelPath(pivotModel.modelSrc),
153
- ]);
154
- return {
155
- config: nmtConfig,
156
- artifacts: { srcVocabPath, dstVocabPath, pivotSrcVocabPath, pivotDstVocabPath, pivotModelPath },
157
- };
158
- }
159
78
  export const nmtPlugin = definePlugin({
160
79
  modelType: ModelType.nmtcppTranslation,
161
80
  displayName: "NMT (nmtcpp)",
@@ -0,0 +1,32 @@
1
+ import { type ModelSrcInput, type NmtConfig, type ResolveContext, type ResolveResult } from "../../../../schemas/index";
2
+ export interface PivotModelConfig {
3
+ modelSrc: string;
4
+ srcVocabSrc?: ModelSrcInput;
5
+ dstVocabSrc?: ModelSrcInput;
6
+ }
7
+ export declare function buildBergamotVocabSources(basePath: string, langPair: string): {
8
+ srcVocabSrc: string;
9
+ dstVocabSrc: string;
10
+ };
11
+ export declare function deriveBergamotVocabSources(modelSrc: string): {
12
+ srcVocabSrc: string;
13
+ dstVocabSrc: string;
14
+ } | null;
15
+ export declare function deriveBergamotRegistryVocabSources(modelSrc: string): {
16
+ srcVocabSrc: string;
17
+ dstVocabSrc: string;
18
+ } | null;
19
+ /**
20
+ * Resolves Bergamot vocab sources to absolute filesystem paths via the SDK
21
+ * resolver context, optionally including a pivot model.
22
+ *
23
+ * For `registry://` sources with auto-derived vocabs, this skips per-vocab
24
+ * resolution because the companion-set download already colocates the vocabs
25
+ * next to the primary model under `sets/<setKey>/`. `createModel` then derives
26
+ * the colocated paths via `deriveColocatedBergamotVocabPaths`. This avoids a
27
+ * redundant flat-cache copy and sidesteps the dedup edge case in QVAC-18420
28
+ * where two directions of a bidirectional pair ship the same vocab blob under
29
+ * distinct registry paths and only one survives sha256 dedup.
30
+ */
31
+ export declare function resolveBergamotVocab(nmtConfig: NmtConfig, ctx: ResolveContext, srcVocabSrc: ModelSrcInput | undefined, dstVocabSrc: ModelSrcInput | undefined, pivotModel?: PivotModelConfig): Promise<ResolveResult<Record<string, unknown>>>;
32
+ //# sourceMappingURL=resolve-vocab.d.ts.map
@@ -0,0 +1,117 @@
1
+ import { BERGAMOT_CJK_LANG_PAIRS, } from "../../../../schemas/index.js";
2
+ import { ModelLoadFailedError } from "../../../../utils/errors-server.js";
3
+ export function buildBergamotVocabSources(basePath, langPair) {
4
+ if (BERGAMOT_CJK_LANG_PAIRS.includes(langPair)) {
5
+ return {
6
+ srcVocabSrc: `${basePath}srcvocab.${langPair}.spm`,
7
+ dstVocabSrc: `${basePath}trgvocab.${langPair}.spm`,
8
+ };
9
+ }
10
+ const sharedVocab = `${basePath}vocab.${langPair}.spm`;
11
+ return { srcVocabSrc: sharedVocab, dstVocabSrc: sharedVocab };
12
+ }
13
+ export function deriveBergamotVocabSources(modelSrc) {
14
+ const match = modelSrc.match(/^pear:\/\/([a-f0-9]+)\/model\.([a-z]+)\.intgemm\.alphas\.bin$/);
15
+ if (!match?.[1] || !match[2])
16
+ return null;
17
+ const basePath = `pear://${match[1]}/`;
18
+ const langPair = match[2];
19
+ return buildBergamotVocabSources(basePath, langPair);
20
+ }
21
+ export function deriveBergamotRegistryVocabSources(modelSrc) {
22
+ const match = modelSrc.match(/^(registry:\/\/.+\/)model\.([a-z]+)\.intgemm\.alphas\.bin$/);
23
+ if (!match?.[1] || !match[2])
24
+ return null;
25
+ const basePath = match[1];
26
+ const langPair = match[2];
27
+ return buildBergamotVocabSources(basePath, langPair);
28
+ }
29
+ /**
30
+ * Resolves Bergamot vocab sources to absolute filesystem paths via the SDK
31
+ * resolver context, optionally including a pivot model.
32
+ *
33
+ * For `registry://` sources with auto-derived vocabs, this skips per-vocab
34
+ * resolution because the companion-set download already colocates the vocabs
35
+ * next to the primary model under `sets/<setKey>/`. `createModel` then derives
36
+ * the colocated paths via `deriveColocatedBergamotVocabPaths`. This avoids a
37
+ * redundant flat-cache copy and sidesteps the dedup edge case in QVAC-18420
38
+ * where two directions of a bidirectional pair ship the same vocab blob under
39
+ * distinct registry paths and only one survives sha256 dedup.
40
+ */
41
+ export async function resolveBergamotVocab(nmtConfig, ctx, srcVocabSrc, dstVocabSrc, pivotModel) {
42
+ let srcSrc = srcVocabSrc;
43
+ let dstSrc = dstVocabSrc;
44
+ if (!srcSrc || !dstSrc) {
45
+ const derived = ctx.modelSrc.startsWith("pear://")
46
+ ? deriveBergamotVocabSources(ctx.modelSrc)
47
+ : ctx.modelSrc.startsWith("registry://")
48
+ ? deriveBergamotRegistryVocabSources(ctx.modelSrc)
49
+ : null;
50
+ if (derived) {
51
+ srcSrc = srcSrc ?? derived.srcVocabSrc;
52
+ dstSrc = dstSrc ?? derived.dstVocabSrc;
53
+ }
54
+ }
55
+ if (!srcSrc || !dstSrc) {
56
+ throw new ModelLoadFailedError("Bergamot requires srcVocabSrc and dstVocabSrc. Provide them in modelConfig or use a pear:// or registry:// model source for auto-derivation.");
57
+ }
58
+ if (!pivotModel) {
59
+ const modelIsRegistry = ctx.modelSrc.startsWith("registry://");
60
+ const vocabsAreDerived = !srcVocabSrc && !dstVocabSrc;
61
+ if (modelIsRegistry && vocabsAreDerived) {
62
+ return { config: nmtConfig };
63
+ }
64
+ const [srcVocabPath, dstVocabPath] = await Promise.all([
65
+ ctx.resolveModelPath(srcSrc),
66
+ ctx.resolveModelPath(dstSrc),
67
+ ]);
68
+ return {
69
+ config: nmtConfig,
70
+ artifacts: { srcVocabPath, dstVocabPath },
71
+ };
72
+ }
73
+ let pivotSrcSrc = pivotModel.srcVocabSrc;
74
+ let pivotDstSrc = pivotModel.dstVocabSrc;
75
+ if (!pivotSrcSrc || !pivotDstSrc) {
76
+ const pivotDerived = pivotModel.modelSrc.startsWith("pear://")
77
+ ? deriveBergamotVocabSources(pivotModel.modelSrc)
78
+ : pivotModel.modelSrc.startsWith("registry://")
79
+ ? deriveBergamotRegistryVocabSources(pivotModel.modelSrc)
80
+ : null;
81
+ if (pivotDerived) {
82
+ pivotSrcSrc = pivotSrcSrc ?? pivotDerived.srcVocabSrc;
83
+ pivotDstSrc = pivotDstSrc ?? pivotDerived.dstVocabSrc;
84
+ }
85
+ }
86
+ if (!pivotSrcSrc || !pivotDstSrc) {
87
+ throw new ModelLoadFailedError("Bergamot pivot model requires srcVocabSrc and dstVocabSrc. Provide them in modelConfig or use a pear:// or registry:// model source for auto-derivation.");
88
+ }
89
+ const modelIsRegistry = ctx.modelSrc.startsWith("registry://");
90
+ const pivotIsRegistry = pivotModel.modelSrc.startsWith("registry://");
91
+ const primaryVocabsAreDerived = !srcVocabSrc && !dstVocabSrc;
92
+ const pivotVocabsAreDerived = !pivotModel.srcVocabSrc && !pivotModel.dstVocabSrc;
93
+ if (modelIsRegistry &&
94
+ pivotIsRegistry &&
95
+ primaryVocabsAreDerived &&
96
+ pivotVocabsAreDerived) {
97
+ const pivotModelPath = await ctx.resolveModelPath(pivotModel.modelSrc);
98
+ return { config: nmtConfig, artifacts: { pivotModelPath } };
99
+ }
100
+ const [srcVocabPath, dstVocabPath, pivotSrcVocabPath, pivotDstVocabPath, pivotModelPath,] = await Promise.all([
101
+ ctx.resolveModelPath(srcSrc),
102
+ ctx.resolveModelPath(dstSrc),
103
+ ctx.resolveModelPath(pivotSrcSrc),
104
+ ctx.resolveModelPath(pivotDstSrc),
105
+ ctx.resolveModelPath(pivotModel.modelSrc),
106
+ ]);
107
+ return {
108
+ config: nmtConfig,
109
+ artifacts: {
110
+ srcVocabPath,
111
+ dstVocabPath,
112
+ pivotSrcVocabPath,
113
+ pivotDstVocabPath,
114
+ pivotModelPath,
115
+ },
116
+ };
117
+ }