@qvac/harness 0.8.5 → 0.8.6

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 (53) hide show
  1. package/CHANGELOG.md +55 -1
  2. package/README.md +49 -0
  3. package/dist-lib/index.d.ts +31 -2
  4. package/dist-lib/index.js +365 -53
  5. package/dist-lib/lib/audio-transcode-bare.d.ts +1 -1
  6. package/dist-lib/lib/audio-transcode-bare.js +22 -25
  7. package/dist-lib/lib/audio-transcode-node.d.ts +1 -1
  8. package/dist-lib/lib/audio-transcode-node.js +1 -3
  9. package/dist-lib/lib/capabilities/audio-resample.d.ts +1 -1
  10. package/dist-lib/lib/capabilities/audio-resample.js +7 -8
  11. package/dist-lib/lib/capabilities/completion.d.ts +5 -2
  12. package/dist-lib/lib/capabilities/completion.js +22 -4
  13. package/dist-lib/lib/capabilities/download.d.ts +3 -1
  14. package/dist-lib/lib/capabilities/download.js +1 -1
  15. package/dist-lib/lib/capabilities/preview-model-source.d.ts +1 -0
  16. package/dist-lib/lib/capabilities/preview-model-source.js +1 -0
  17. package/dist-lib/lib/capabilities/usage-breakdown.js +4 -1
  18. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  19. package/dist-lib/lib/capabilities/voice.js +94 -26
  20. package/dist-lib/lib/connect.d.ts +4 -1
  21. package/dist-lib/lib/connect.js +14 -1
  22. package/dist-lib/lib/contract-types.d.ts +10 -0
  23. package/dist-lib/lib/models/download-gate.d.ts +20 -0
  24. package/dist-lib/lib/models/download-gate.js +73 -0
  25. package/dist-lib/lib/models/memory-cap.d.ts +13 -0
  26. package/dist-lib/lib/models/memory-cap.js +21 -0
  27. package/dist-lib/lib/models/model-catalog-constants.d.ts +3 -1
  28. package/dist-lib/lib/models/model-catalog-constants.js +9 -1
  29. package/dist-lib/lib/models/model-catalog.d.ts +6 -2
  30. package/dist-lib/lib/models/model-catalog.js +35 -3
  31. package/dist-lib/lib/models/model-handle.d.ts +8 -1
  32. package/dist-lib/lib/models/model-handle.js +20 -2
  33. package/dist-lib/lib/models/model-list.d.ts +8 -1
  34. package/dist-lib/lib/models/model-list.js +23 -0
  35. package/dist-lib/lib/models/model-pool.d.ts +33 -5
  36. package/dist-lib/lib/models/model-pool.js +97 -29
  37. package/dist-lib/lib/serve.js +18 -2
  38. package/dist-lib/lib/tools/generate-image.d.ts +4 -2
  39. package/dist-lib/lib/tools/generate-image.js +5 -2
  40. package/dist-lib/lib/tools/generate-music.d.ts +4 -2
  41. package/dist-lib/lib/tools/generate-music.js +5 -2
  42. package/dist-lib/lib/tools/translate.d.ts +3 -1
  43. package/dist-lib/lib/tools/translate.js +6 -1
  44. package/dist-lib/schema/harness.js +41 -3
  45. package/dist-lib/spec/bind.js +9 -0
  46. package/dist-lib/spec/calls.js +3 -0
  47. package/dist-lib/spec/hrpc/hrpc.json +39 -0
  48. package/dist-lib/spec/hrpc/index.js +39 -3
  49. package/dist-lib/spec/hrpc/messages.js +93 -10
  50. package/dist-lib/spec/hyperschema/index.js +93 -10
  51. package/dist-lib/spec/hyperschema/schema.json +57 -1
  52. package/dist-lib/spec/hyperschema/types.d.ts +19 -2
  53. package/package.json +4 -4
@@ -0,0 +1,13 @@
1
+ export declare const VictimRank: {
2
+ readonly generator: 0;
3
+ readonly companion: 1;
4
+ readonly requested: 2;
5
+ readonly pinned: 3;
6
+ readonly replying: 4;
7
+ };
8
+ export interface CapVictim {
9
+ readonly rank: number;
10
+ readonly lastUsed: number;
11
+ }
12
+ export declare function nextVictim<T extends CapVictim>(victims: ReadonlyArray<T>): T | undefined;
13
+ export declare function memoryCapBytes(percent: number, availableBytes: number): number;
@@ -0,0 +1,21 @@
1
+ export const VictimRank = {
2
+ generator: 0,
3
+ companion: 1,
4
+ requested: 2,
5
+ pinned: 3,
6
+ replying: 4
7
+ };
8
+ export function nextVictim(victims) {
9
+ let best;
10
+ for (const victim of victims) {
11
+ if (best === undefined ||
12
+ victim.rank < best.rank ||
13
+ (victim.rank === best.rank && victim.lastUsed < best.lastUsed)) {
14
+ best = victim;
15
+ }
16
+ }
17
+ return best;
18
+ }
19
+ export function memoryCapBytes(percent, availableBytes) {
20
+ return Math.round((Math.min(percent, 100) / 100) * availableBytes);
21
+ }
@@ -6,6 +6,8 @@ export declare const QWEN_35_9B = "Qwen3.5-9B-Q4_K_M";
6
6
  export declare const QWEN_36_27B = "Qwen3.6-27B-UD-Q4_K_XL";
7
7
  export declare const QWEN_36_35B_A3B = "Qwen3.6-35B-A3B-UD-Q4_K_M";
8
8
  export declare const QWEN_38_27B = "Qwen3.8-27B-UD-Q4_K_XL";
9
+ export declare const BONSAI_2_27B_PQ2 = "Ternary-Bonsai-2-27B-PQ2_0";
10
+ export declare const BONSAI_2_27B_PTQ1 = "Ternary-Bonsai-2-27B-PTQ1_0";
9
11
  export declare const MEDPSY_1_7B = "medpsy-1.7b-q4_k_m-imat";
10
12
  export declare const MEDPSY_4B = "medpsy-4b-q4_k_m-imat";
11
13
  export declare function isCapabilityProfile(value: string): value is CapabilityProfile;
@@ -19,7 +21,7 @@ export interface HostCapability {
19
21
  export declare function isMobilePlatform(platform: string): boolean;
20
22
  export declare function runnableModelName(requested: string, host: HostCapability | null): string;
21
23
  export declare function capabilityProfileToModelName(profile: CapabilityProfile): string;
22
- export declare const AGENT_MODEL_NAMES: readonly ["Qwen3.5-2B-Q4_K_M", "Qwen3.5-4B-Q4_K_M", "Qwen3.5-9B-Q4_K_M", "Qwen3.6-27B-UD-Q4_K_XL", "Qwen3.6-35B-A3B-UD-Q4_K_M", "Qwen3.8-27B-UD-Q4_K_XL", "medpsy-1.7b-q4_k_m-imat", "medpsy-4b-q4_k_m-imat"];
24
+ export declare const AGENT_MODEL_NAMES: readonly ["Qwen3.5-2B-Q4_K_M", "Qwen3.5-4B-Q4_K_M", "Qwen3.5-9B-Q4_K_M", "Qwen3.6-27B-UD-Q4_K_XL", "Qwen3.6-35B-A3B-UD-Q4_K_M", "Qwen3.8-27B-UD-Q4_K_XL", "Ternary-Bonsai-2-27B-PQ2_0", "Ternary-Bonsai-2-27B-PTQ1_0", "medpsy-1.7b-q4_k_m-imat", "medpsy-4b-q4_k_m-imat"];
23
25
  export declare function modelDisplayName(modelName: string): string;
24
26
  export declare function capabilityProfileModelLabel(profile: CapabilityProfile): string;
25
27
  export declare function isMultimodalLlm(modelName: string): boolean;
@@ -11,6 +11,8 @@ export const QWEN_35_9B = 'Qwen3.5-9B-Q4_K_M';
11
11
  export const QWEN_36_27B = 'Qwen3.6-27B-UD-Q4_K_XL';
12
12
  export const QWEN_36_35B_A3B = 'Qwen3.6-35B-A3B-UD-Q4_K_M';
13
13
  export const QWEN_38_27B = 'Qwen3.8-27B-UD-Q4_K_XL';
14
+ export const BONSAI_2_27B_PQ2 = 'Ternary-Bonsai-2-27B-PQ2_0';
15
+ export const BONSAI_2_27B_PTQ1 = 'Ternary-Bonsai-2-27B-PTQ1_0';
14
16
  export const MEDPSY_1_7B = 'medpsy-1.7b-q4_k_m-imat';
15
17
  export const MEDPSY_4B = 'medpsy-4b-q4_k_m-imat';
16
18
  export function isCapabilityProfile(value) {
@@ -26,6 +28,8 @@ const MODEL_NAME_TO_PROFILE = new Map([
26
28
  [QWEN_36_27B, 'xhigh'],
27
29
  [QWEN_36_35B_A3B, 'xhigh'],
28
30
  [QWEN_38_27B, 'xhigh'],
31
+ [BONSAI_2_27B_PQ2, 'high'],
32
+ [BONSAI_2_27B_PTQ1, 'high'],
29
33
  [MEDPSY_1_7B, 'low'],
30
34
  [MEDPSY_4B, 'mid']
31
35
  ]);
@@ -70,6 +74,8 @@ export const AGENT_MODEL_NAMES = [
70
74
  QWEN_36_27B,
71
75
  QWEN_36_35B_A3B,
72
76
  QWEN_38_27B,
77
+ BONSAI_2_27B_PQ2,
78
+ BONSAI_2_27B_PTQ1,
73
79
  MEDPSY_1_7B,
74
80
  MEDPSY_4B
75
81
  ];
@@ -87,7 +93,9 @@ const MULTIMODAL_LLMS = new Set([
87
93
  QWEN_35_9B,
88
94
  QWEN_36_27B,
89
95
  QWEN_36_35B_A3B,
90
- QWEN_38_27B
96
+ QWEN_38_27B,
97
+ BONSAI_2_27B_PQ2,
98
+ BONSAI_2_27B_PTQ1
91
99
  ]);
92
100
  export function isMultimodalLlm(modelName) {
93
101
  return MULTIMODAL_LLMS.has(modelName);
@@ -6,8 +6,8 @@ import type { ModelAddon } from './model-source.ts';
6
6
  export interface SdkModelAsset extends ModelRegistryEntry {
7
7
  readonly src: string;
8
8
  }
9
- import { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName, type CapabilityProfile, type HostCapability, type ImageModelChoice, type ImageChoiceEntry } from './model-catalog-constants.ts';
10
- export { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName };
9
+ import { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, BONSAI_2_27B_PQ2, BONSAI_2_27B_PTQ1, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName, type CapabilityProfile, type HostCapability, type ImageModelChoice, type ImageChoiceEntry } from './model-catalog-constants.ts';
10
+ export { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, BONSAI_2_27B_PQ2, BONSAI_2_27B_PTQ1, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName };
11
11
  export type { CapabilityProfile, HostCapability, ImageModelChoice, ImageChoiceEntry };
12
12
  export interface LlmCatalogEntry {
13
13
  readonly name: string;
@@ -15,6 +15,7 @@ export interface LlmCatalogEntry {
15
15
  readonly capabilityProfile: CapabilityProfile;
16
16
  readonly projection?: SdkModelAsset;
17
17
  readonly textOnly?: true;
18
+ readonly toolDialect?: ToolDialect;
18
19
  }
19
20
  export declare const LLM_CATALOG: ReadonlyArray<LlmCatalogEntry>;
20
21
  export declare function defaultProjectionMap(): Record<string, string>;
@@ -52,6 +53,7 @@ export interface DiffusionDownloadMetadata {
52
53
  }
53
54
  export declare function diffusionDownloadName(model: string): string | null;
54
55
  export declare function diffusionDownloadMetadata(model: string, addon: ModelAddon | undefined): DiffusionDownloadMetadata | undefined;
56
+ export declare function assetBytes(src: string): number | undefined;
55
57
  import { DEFAULT_MUSIC_MODEL_CHOICE, isMusicModelChoice, musicModelChoiceFrom, type MusicModelChoice } from './music-catalog.ts';
56
58
  export { DEFAULT_MUSIC_MODEL_CHOICE, isMusicModelChoice, musicModelChoiceFrom, type MusicModelChoice };
57
59
  export interface AudioGenCatalogEntry {
@@ -108,6 +110,7 @@ export declare function curatedListables(): CuratedListable[];
108
110
  export declare function curatedModelAsset(name: string, addon: ModelRegistryEntry['addon']): SdkModelAsset | undefined;
109
111
  export declare function modelAddonOf(model: string): ModelRegistryEntry['addon'];
110
112
  export declare function residentSrc(model: string, addon: ModelRegistryEntry['addon']): string;
113
+ export declare function catalogModelName(model: string, addon: ModelRegistryEntry['addon']): string;
111
114
  export declare const CUSTOM_TASK_TYPES: readonly ['llm'];
112
115
  export type CustomTaskType = (typeof CUSTOM_TASK_TYPES)[number];
113
116
  export interface CustomModelEntry {
@@ -123,6 +126,7 @@ export declare function setCustomModels(entries: ReadonlyArray<CustomModelEntry>
123
126
  export declare function customModels(): ReadonlyArray<CustomModelEntry>;
124
127
  export declare function customContextLength(name: string): number | undefined;
125
128
  export declare function customModelSrc(name: string, addon: ModelRegistryEntry['addon']): string | undefined;
129
+ export declare function toolDialectFor(name: string): ToolDialect | undefined;
126
130
  export declare function customToolDialect(name: string): ToolDialect | undefined;
127
131
  export declare function customProjectionMap(): Record<string, string>;
128
132
  export declare function companionRegistryPaths(): Map<string, string[]>;
@@ -1,8 +1,8 @@
1
- import { AUDIOGEN_ACESTEP_5HZ_LM_0_6B_Q8_0, AUDIOGEN_ACESTEP_V15_SFT_Q8_0, AUDIOGEN_ACESTEP_V15_TURBO_Q4_K_M, AUDIOGEN_ACESTEP_V15_TURBO_Q8_0, AUDIOGEN_QWEN3_EMBEDDING_0_6B_Q8_0, AUDIOGEN_VAE_BF16, EMBEDDINGGEMMA_300M_Q4_0, FLUX_2_KLEIN_4B_Q4_0, FLUX_2_KLEIN_4B_VAE, HEALTHCARE_1_7B_MEDICAL_Q4_K_M, HEALTHCARE_4B_MEDICAL_Q4_K_M, MMPROJ_QWEN3_5_2B_MULTIMODAL_Q8_0, MMPROJ_QWEN3_5_4B_MULTIMODAL_Q8_0, MMPROJ_QWEN3_5_9B_MULTIMODAL_F16, MMPROJ_QWEN3_6_27B_MULTIMODAL_F16, MMPROJ_QWEN3_6_35B_A3B_MULTIMODAL_F16, MMPROJ_QWEN3_8_27B_MULTIMODAL_F16, QWEN3_5_2B_MULTIMODAL_Q4_K_M, QWEN3_5_4B_MULTIMODAL_Q4_K_M, QWEN3_5_9B_MULTIMODAL_Q4_K_M, PARAKEET_TDT_0_6B_V3_Q8_0, QWEN3_4B_Q4_K_M, QWEN3_6_27B_MULTIMODAL_Q4_K_XL, QWEN3_6_35B_A3B_MULTIMODAL_Q4_K_M, QWEN3_8_27B_MULTIMODAL_UD_Q4_K_XL, SD_V2_1_1B_Q8_0, SDXL_BASE_1_0_3B_Q4_0, TTS_MULTILINGUAL_SUPERTONIC3_FP16, TTS_MULTILINGUAL_SUPERTONIC3_Q4_0, TTS_MULTILINGUAL_SUPERTONIC3_Q8_0 } from '@qvac/sdk/models';
1
+ import { AUDIOGEN_ACESTEP_5HZ_LM_0_6B_Q8_0, AUDIOGEN_ACESTEP_V15_SFT_Q8_0, AUDIOGEN_ACESTEP_V15_TURBO_Q4_K_M, AUDIOGEN_ACESTEP_V15_TURBO_Q8_0, AUDIOGEN_QWEN3_EMBEDDING_0_6B_Q8_0, AUDIOGEN_VAE_BF16, EMBEDDINGGEMMA_300M_Q4_0, FLUX_2_KLEIN_4B_Q4_0, FLUX_2_KLEIN_4B_VAE, HEALTHCARE_1_7B_MEDICAL_Q4_K_M, HEALTHCARE_4B_MEDICAL_Q4_K_M, MMPROJ_QWEN3_5_2B_MULTIMODAL_Q8_0, MMPROJ_QWEN3_5_4B_MULTIMODAL_Q8_0, MMPROJ_QWEN3_5_9B_MULTIMODAL_F16, MMPROJ_QWEN3_6_27B_MULTIMODAL_F16, MMPROJ_QWEN3_6_35B_A3B_MULTIMODAL_F16, MMPROJ_QWEN3_8_27B_MULTIMODAL_F16, MMPROJ_TERNARY_BONSAI_2_27B_MULTIMODAL_Q8_0, QWEN3_5_2B_MULTIMODAL_Q4_K_M, QWEN3_5_4B_MULTIMODAL_Q4_K_M, QWEN3_5_9B_MULTIMODAL_Q4_K_M, PARAKEET_TDT_0_6B_V3_Q8_0, QWEN3_4B_Q4_K_M, QWEN3_6_27B_MULTIMODAL_Q4_K_XL, QWEN3_6_35B_A3B_MULTIMODAL_Q4_K_M, QWEN3_8_27B_MULTIMODAL_UD_Q4_K_XL, SD_V2_1_1B_Q8_0, SDXL_BASE_1_0_3B_Q4_0, TERNARY_BONSAI_2_27B_MULTIMODAL_PQ2_0, TERNARY_BONSAI_2_27B_MULTIMODAL_PTQ1_0, TTS_MULTILINGUAL_SUPERTONIC3_FP16, TTS_MULTILINGUAL_SUPERTONIC3_Q4_0, TTS_MULTILINGUAL_SUPERTONIC3_Q8_0 } from '@qvac/sdk/models';
2
2
  import { detectToolDialect } from './tool-dialect.js';
3
3
  import { defaultTtsModelName } from './tts-model-default.js';
4
- import { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName } from './model-catalog-constants.js';
5
- export { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName };
4
+ import { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, BONSAI_2_27B_PQ2, BONSAI_2_27B_PTQ1, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName } from './model-catalog-constants.js';
5
+ export { CAPABILITY_PROFILE_ORDER, QWEN_35_2B, QWEN_35_4B, QWEN_35_9B, QWEN_36_27B, QWEN_36_35B_A3B, QWEN_38_27B, BONSAI_2_27B_PQ2, BONSAI_2_27B_PTQ1, MEDPSY_1_7B, MEDPSY_4B, isCapabilityProfile, meetsMinCapabilityProfile, modelNameToCapabilityProfile, capabilityProfileToModelName, isMultimodalLlm, AGENT_MODEL_NAMES, modelDisplayName, capabilityProfileModelLabel, DEFAULT_IMAGE_MODEL_CHOICE, IMAGE_CHOICE_CATALOG, isMobilePlatform, runnableModelName };
6
6
  export const LLM_CATALOG = [
7
7
  {
8
8
  name: QWEN_35_2B,
@@ -51,6 +51,20 @@ export const LLM_CATALOG = [
51
51
  weight: QWEN3_8_27B_MULTIMODAL_UD_Q4_K_XL,
52
52
  capabilityProfile: 'xhigh',
53
53
  projection: MMPROJ_QWEN3_8_27B_MULTIMODAL_F16
54
+ },
55
+ {
56
+ name: BONSAI_2_27B_PQ2,
57
+ weight: TERNARY_BONSAI_2_27B_MULTIMODAL_PQ2_0,
58
+ capabilityProfile: 'high',
59
+ projection: MMPROJ_TERNARY_BONSAI_2_27B_MULTIMODAL_Q8_0,
60
+ toolDialect: 'qwen35'
61
+ },
62
+ {
63
+ name: BONSAI_2_27B_PTQ1,
64
+ weight: TERNARY_BONSAI_2_27B_MULTIMODAL_PTQ1_0,
65
+ capabilityProfile: 'high',
66
+ projection: MMPROJ_TERNARY_BONSAI_2_27B_MULTIMODAL_Q8_0,
67
+ toolDialect: 'qwen35'
54
68
  }
55
69
  ];
56
70
  export function defaultProjectionMap() {
@@ -181,6 +195,16 @@ export function diffusionDownloadMetadata(model, addon) {
181
195
  assetSizes
182
196
  };
183
197
  }
198
+ export function assetBytes(src) {
199
+ const assets = [
200
+ ...curatedListables().map((entry) => entry.asset),
201
+ ...LLM_CATALOG.flatMap((entry) => (entry.projection ? [entry.projection] : [])),
202
+ ...DIFFUSION_CATALOG.flatMap(diffusionAssets),
203
+ ...AUDIOGEN_CATALOG.flatMap((entry) => [entry.dit, entry.textEnc, entry.lm, entry.vae])
204
+ ];
205
+ const custom = customModels().find((entry) => entry.src === src);
206
+ return assets.find((asset) => asset.src === src)?.expectedSize ?? custom?.expectedSize;
207
+ }
184
208
  import { DEFAULT_MUSIC_MODEL_CHOICE, isMusicModelChoice, musicModelChoiceFrom } from './music-catalog.js';
185
209
  export { DEFAULT_MUSIC_MODEL_CHOICE, isMusicModelChoice, musicModelChoiceFrom };
186
210
  const MUSIC_GENERATION_SKILL = 'music-generation';
@@ -363,6 +387,11 @@ export function modelAddonOf(model) {
363
387
  export function residentSrc(model, addon) {
364
388
  return curatedModelAsset(model, addon)?.src ?? model;
365
389
  }
390
+ export function catalogModelName(model, addon) {
391
+ const known = curatedListables().find((entry) => entry.asset.addon === addon &&
392
+ (entry.name === model || entry.asset.name === model || entry.asset.src === model));
393
+ return known?.name ?? model;
394
+ }
366
395
  export const CUSTOM_TASK_TYPES = ['llm'];
367
396
  let CUSTOM_MODELS = new Map();
368
397
  export function setCustomModels(entries) {
@@ -378,6 +407,9 @@ export function customModelSrc(name, addon) {
378
407
  const entry = CUSTOM_MODELS.get(name);
379
408
  return entry && (entry.taskType ?? 'llm') === addon ? entry.src : undefined;
380
409
  }
410
+ export function toolDialectFor(name) {
411
+ return LLM_CATALOG.find((entry) => entry.name === name)?.toolDialect ?? customToolDialect(name);
412
+ }
381
413
  export function customToolDialect(name) {
382
414
  const entry = CUSTOM_MODELS.get(name);
383
415
  return entry
@@ -1,10 +1,12 @@
1
+ import { type AbortSignal } from 'bare-abort-controller';
1
2
  export interface ModelResource {
2
3
  model: string;
3
4
  modelId: string;
4
5
  ctxSize?: number;
6
+ bytes?: number;
5
7
  }
6
8
  export interface ModelHandleOptions {
7
- load(): Promise<ModelResource>;
9
+ load(signal: AbortSignal): Promise<ModelResource>;
8
10
  onLoaded?(): void;
9
11
  onFailed?(err: unknown): void;
10
12
  }
@@ -15,8 +17,13 @@ export declare class ModelHandle {
15
17
  private readonly _onFailed;
16
18
  private _tracked;
17
19
  private _loading;
20
+ private _resource;
21
+ lastUsed: number;
22
+ private _attempt;
18
23
  constructor({ load, onLoaded, onFailed }: ModelHandleOptions);
19
24
  load(): Promise<ModelResource>;
20
25
  get loading(): boolean;
26
+ abandon(): void;
21
27
  peek(): Promise<ModelResource> | null;
28
+ get resource(): ModelResource | null;
22
29
  }
@@ -1,3 +1,5 @@
1
+ import { AbortController } from 'bare-abort-controller';
2
+ import { AbortedError } from '../helpers/race-abort.js';
1
3
  import { retryMemo } from '../helpers/retry-memo.js';
2
4
  export class ModelHandle {
3
5
  _load;
@@ -6,8 +8,15 @@ export class ModelHandle {
6
8
  _onFailed;
7
9
  _tracked = null;
8
10
  _loading = false;
11
+ _resource = null;
12
+ lastUsed = 0;
13
+ _attempt = null;
9
14
  constructor({ load, onLoaded, onFailed }) {
10
- const memo = retryMemo(load);
15
+ const memo = retryMemo(() => {
16
+ const attempt = new AbortController();
17
+ this._attempt = attempt;
18
+ return load(attempt.signal);
19
+ });
11
20
  this._load = memo;
12
21
  this._peek = memo.peek;
13
22
  this._onLoaded = onLoaded;
@@ -25,15 +34,24 @@ export class ModelHandle {
25
34
  report();
26
35
  }
27
36
  };
28
- attempt.then(() => settle(() => this._onLoaded?.()), (err) => settle(() => this._onFailed?.(err)));
37
+ attempt.then((resource) => settle(() => {
38
+ this._resource = resource;
39
+ this._onLoaded?.();
40
+ }), (err) => settle(() => this._onFailed?.(err)));
29
41
  }
30
42
  return attempt;
31
43
  }
32
44
  get loading() {
33
45
  return this._loading;
34
46
  }
47
+ abandon() {
48
+ this._attempt?.abort(new AbortedError());
49
+ }
35
50
  peek() {
36
51
  return this._peek();
37
52
  }
53
+ get resource() {
54
+ return this._loading ? null : this._resource;
55
+ }
38
56
  }
39
57
  function noop() { }
@@ -1,5 +1,5 @@
1
1
  import type * as QvacSdk from '@qvac/sdk';
2
- import type { ModelAddon, ModelRegistrySdk } from './model-source.ts';
2
+ import { type ModelAddon, type ModelRegistrySdk } from './model-source.ts';
3
3
  import { type ModelTaskType } from './model-task.ts';
4
4
  export type ModelType = ModelAddon;
5
5
  export interface StaticCatalogEntry {
@@ -22,6 +22,7 @@ export interface ModelInfo {
22
22
  actualSize?: number;
23
23
  label?: string;
24
24
  taskType: ModelTaskType;
25
+ memoryBytes?: number | undefined;
25
26
  }
26
27
  export type CatalogRow = Omit<ModelInfo, 'isLoading'>;
27
28
  export interface ListModelsOptions {
@@ -54,4 +55,10 @@ export interface CustomModelRowsContext {
54
55
  }
55
56
  export declare function customModelRows({ type, cachedOnly }: ListModelsOptions, { modelsDir, loaded }: CustomModelRowsContext): Promise<CatalogRow[]>;
56
57
  export declare function mergeCustomRows(catalog: ReadonlyArray<CatalogRow>, custom: ReadonlyArray<CatalogRow>): CatalogRow[];
58
+ export interface ModelSourceQuery {
59
+ readonly model: string;
60
+ readonly addon: ModelAddon;
61
+ readonly modelsDir: string | undefined;
62
+ }
63
+ export declare function isModelSourceCached(sdk: ModelListSdk, { model, addon, modelsDir }: ModelSourceQuery): Promise<boolean>;
57
64
  export {};
@@ -1,3 +1,4 @@
1
+ import { resolveModelSrc } from './model-source.js';
1
2
  import { addonTaskType } from './model-task.js';
2
3
  import { isCustomModelCached } from './custom-model-cache.js';
3
4
  import { companionRegistryPaths, curatedListables, customModels, musicListables } from './model-catalog.js';
@@ -153,3 +154,25 @@ export function mergeCustomRows(catalog, custom) {
153
154
  const shadowed = new Set(custom.map((row) => row.name));
154
155
  return [...catalog.filter((row) => !shadowed.has(row.name)), ...custom];
155
156
  }
157
+ export async function isModelSourceCached(sdk, { model, addon, modelsDir }) {
158
+ const src = await resolveModelSrc(sdk, model, addon).catch(() => null);
159
+ if (src === null) {
160
+ return false;
161
+ }
162
+ const name = registryCatalogName(sdk, src);
163
+ if (name === undefined) {
164
+ return typeof src === 'string' && isCustomModelCached({ name: model, src }, modelsDir);
165
+ }
166
+ const info = await sdk.getModelInfo({ name }).catch(() => null);
167
+ return info?.isCached ?? false;
168
+ }
169
+ function registryCatalogName(sdk, src) {
170
+ if (typeof src !== 'string') {
171
+ return sdk.models.find((entry) => entry.registryPath === src.registryPath)?.name ?? src.name;
172
+ }
173
+ const registryPath = /^registry:\/\/[^/]+\/(.+)$/.exec(src)?.[1];
174
+ if (registryPath === undefined) {
175
+ return undefined;
176
+ }
177
+ return sdk.models.find((entry) => entry.registryPath === registryPath)?.name;
178
+ }
@@ -3,14 +3,28 @@ import { ModelHandle } from './model-handle.ts';
3
3
  import { type ModelSrc } from './context.ts';
4
4
  import { type ModelAddon } from './model-source.ts';
5
5
  import type { GpuFaultState } from './sdk-faults.ts';
6
+ import type { HoldFn } from './download-gate.ts';
6
7
  type SdkModule = typeof import('@qvac/sdk');
8
+ type FitModelType = NonNullable<import('@qvac/sdk').AssessModelFitInput['models'][number]['modelType']>;
7
9
  export interface ModelState {
8
- status: 'loading' | 'downloading' | 'downloaded' | 'ready' | 'error' | 'unloading' | 'unloaded';
10
+ status: 'held' | 'loading' | 'downloading' | 'downloaded' | 'ready' | 'error' | 'unloading' | 'unloaded';
9
11
  model: string;
10
12
  catalogChanged?: boolean;
11
13
  [key: string]: unknown;
12
14
  }
13
15
  export type NotifyFn = (state: ModelState) => void;
16
+ export interface PendingLoad {
17
+ readonly key: string;
18
+ readonly model: string;
19
+ readonly modelSrc: ModelSrc;
20
+ readonly modelType: FitModelType;
21
+ readonly modelConfig?: Record<string, unknown> | undefined;
22
+ }
23
+ export interface Admission {
24
+ readonly bytes: number | undefined;
25
+ release(): void;
26
+ }
27
+ export type BeforeLoad = (load: PendingLoad) => Promise<Admission | undefined>;
14
28
  export interface LlmDeviceConfig {
15
29
  device?: 'cpu' | 'gpu';
16
30
  gpuLayers?: number;
@@ -80,6 +94,13 @@ export declare function modelOf(key: string): string;
80
94
  export declare function releaseModel(pool: PoolLike, key: string): void;
81
95
  export declare function loadedModels(pool: PoolLike): LoadedModel[];
82
96
  export declare function settledModels(pool: PoolLike): string[];
97
+ export interface SettledEntry {
98
+ readonly key: string;
99
+ readonly refs: number;
100
+ readonly bytes: number | undefined;
101
+ readonly lastUsed: number;
102
+ }
103
+ export declare function settledEntries(pool: PoolLike): SettledEntry[];
83
104
  export declare function loadingModels(pool: PoolLike): string[];
84
105
  export interface ResidentLease {
85
106
  model: string;
@@ -104,6 +125,7 @@ export interface ModelCapacityOptions extends DeadlineOptions {
104
125
  }
105
126
  export declare function awaitModelCapacity(pool: PoolLike, { cap, deadlineMs, tickMs, signal, evictable }: ModelCapacityOptions): Promise<boolean>;
106
127
  export declare function evictModelWithin(pool: PoolLike, key: string, options: DeadlineOptions): Promise<boolean>;
128
+ export declare function evictIdleWithin(pool: PoolLike, key: string, { deadlineMs }: DeadlineOptions): Promise<boolean>;
107
129
  export declare function evictModel(pool: PoolLike, model: string, options: DeadlineOptions): Promise<boolean>;
108
130
  export type LoadSdk = () => Promise<ModelLifecycleSdk>;
109
131
  export type EmbedLifecycleSdk = Pick<SdkModule, 'unloadModel' | 'modelRegistrySearch'> & Partial<CancelSdk> & {
@@ -134,15 +156,19 @@ export declare function loadClockGuard(model: string, sdk: Partial<CancelSdk>, {
134
156
  export interface ModelPoolOptions {
135
157
  deviceConfig?: LlmDeviceConfig;
136
158
  loads?: LoadTracker;
159
+ beforeLoad?: BeforeLoad;
160
+ hold?: HoldFn;
137
161
  }
138
- export declare function createModelPool(loadSdk: LoadSdk, notify: NotifyFn, { deviceConfig, loads }?: ModelPoolOptions): ModelPuddle;
162
+ export declare function createModelPool(loadSdk: LoadSdk, notify: NotifyFn, { deviceConfig, loads, beforeLoad, hold }?: ModelPoolOptions): ModelPuddle;
139
163
  export interface EmbedPoolOptions {
140
164
  faults: GpuFaultState;
141
165
  batchSize?: number;
142
166
  openclCacheDir?: (() => string | undefined) | undefined;
143
167
  loads?: LoadTracker;
168
+ beforeLoad?: BeforeLoad;
169
+ hold?: HoldFn;
144
170
  }
145
- export declare function createEmbedPool(loadSdk: LoadEmbedSdk, notify: NotifyFn, { faults, batchSize, openclCacheDir, loads }: EmbedPoolOptions): ModelPuddle;
171
+ export declare function createEmbedPool(loadSdk: LoadEmbedSdk, notify: NotifyFn, { faults, batchSize, openclCacheDir, loads, beforeLoad, hold }: EmbedPoolOptions): ModelPuddle;
146
172
  export type VoiceLifecycleSdk = Pick<SdkModule, 'unloadModel' | 'modelRegistrySearch'> & Partial<CancelSdk> & {
147
173
  loadModel(params: {
148
174
  modelSrc: ModelSrc;
@@ -153,12 +179,14 @@ export type VoiceLifecycleSdk = Pick<SdkModule, 'unloadModel' | 'modelRegistrySe
153
179
  };
154
180
  export type LoadVoiceSdk = () => Promise<VoiceLifecycleSdk>;
155
181
  export interface VoicePoolOptions {
156
- modelType: string;
182
+ modelType: FitModelType;
157
183
  addon: ModelAddon;
158
184
  modelConfig?: Record<string, unknown> | undefined;
159
185
  modelConfigFor?: (modelSrc: ModelSrc) => Readonly<Record<string, unknown>> | undefined;
160
186
  openclCacheDir?: (() => string | undefined) | undefined;
161
187
  loads?: LoadTracker;
188
+ beforeLoad?: BeforeLoad;
189
+ hold?: HoldFn;
162
190
  }
163
- export declare function createVoicePool(loadSdk: LoadVoiceSdk, notify: NotifyFn, { modelType, addon, modelConfig, modelConfigFor, openclCacheDir, loads }: VoicePoolOptions): ModelPuddle;
191
+ export declare function createVoicePool(loadSdk: LoadVoiceSdk, notify: NotifyFn, { modelType, addon, modelConfig, modelConfigFor, openclCacheDir, loads, beforeLoad, hold }: VoicePoolOptions): ModelPuddle;
164
192
  export {};