@qvac/sdk 1.0.0 → 1.1.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/dist/client/api/completion-stream.js +7 -5
- package/dist/client/api/transcribe.d.ts +2 -0
- package/dist/client/api/transcribe.js +3 -0
- package/dist/examples/whispercpp-filesystem.js +32 -1
- package/dist/examples/whispercpp-microphone-record.js +9 -5
- package/dist/examples/whispercpp-prompt.d.ts +2 -0
- package/dist/examples/whispercpp-prompt.js +71 -0
- package/dist/models/hyperdrive/models.d.ts +181 -5
- package/dist/models/hyperdrive/models.js +237 -39
- package/dist/schemas/common.d.ts +169 -27
- package/dist/schemas/load-model.d.ts +558 -86
- package/dist/schemas/transcription.d.ts +3 -0
- package/dist/schemas/transcription.js +1 -0
- package/dist/schemas/whispercpp-config.d.ts +58 -9
- package/dist/schemas/whispercpp-config.js +65 -11
- package/dist/server/bare/addons/llamacpp/handlers/completion-stream.js +15 -45
- package/dist/server/bare/addons/whispercpp/create-model.js +8 -7
- package/dist/server/bare/addons/whispercpp/handlers/transcribe-stream.js +15 -1
- package/dist/server/bare/registry/model-registry.d.ts +2 -0
- package/dist/server/bare/registry/model-registry.js +7 -0
- package/dist/server/rpc/handlers/completion-stream.js +4 -2
- package/dist/server/rpc/handlers/load-model/http.js +10 -4
- package/dist/server/rpc/handlers/transcribe-stream.js +6 -2
- package/dist/server/utils/tool-integration.d.ts +13 -0
- package/dist/server/utils/tool-integration.js +43 -0
- package/dist/server/utils/tool-parser.js +28 -14
- package/package.json +2 -2
|
@@ -15,6 +15,7 @@ export declare const transcribeParamsSchema: z.ZodObject<{
|
|
|
15
15
|
type: z.ZodLiteral<"filePath">;
|
|
16
16
|
value: z.ZodString;
|
|
17
17
|
}, z.core.$strip>], "type">;
|
|
18
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
18
19
|
}, z.core.$strip>;
|
|
19
20
|
export declare const transcribeStreamRequestSchema: z.ZodObject<{
|
|
20
21
|
modelId: z.ZodString;
|
|
@@ -25,6 +26,7 @@ export declare const transcribeStreamRequestSchema: z.ZodObject<{
|
|
|
25
26
|
type: z.ZodLiteral<"filePath">;
|
|
26
27
|
value: z.ZodString;
|
|
27
28
|
}, z.core.$strip>], "type">;
|
|
29
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
28
30
|
type: z.ZodLiteral<"transcribeStream">;
|
|
29
31
|
}, z.core.$strip>;
|
|
30
32
|
export declare const transcribeStreamResponseSchema: z.ZodObject<{
|
|
@@ -38,6 +40,7 @@ export type TranscribeParams = z.infer<typeof transcribeParamsSchema>;
|
|
|
38
40
|
export type TranscribeClientParams = {
|
|
39
41
|
modelId: string;
|
|
40
42
|
audioChunk: string | Buffer;
|
|
43
|
+
prompt?: string;
|
|
41
44
|
};
|
|
42
45
|
export type TranscribeStreamRequest = z.infer<typeof transcribeStreamRequestSchema>;
|
|
43
46
|
export type TranscribeStreamResponse = z.infer<typeof transcribeStreamResponseSchema>;
|
|
@@ -12,6 +12,7 @@ export const audioInputSchema = z.discriminatedUnion("type", [
|
|
|
12
12
|
export const transcribeParamsSchema = z.object({
|
|
13
13
|
modelId: z.string(),
|
|
14
14
|
audioChunk: audioInputSchema,
|
|
15
|
+
prompt: z.string().optional(),
|
|
15
16
|
});
|
|
16
17
|
// Streaming Transcribe Schema (for real-time audio streaming)
|
|
17
18
|
export const transcribeStreamRequestSchema = transcribeParamsSchema.extend({
|
|
@@ -9,16 +9,65 @@ export declare const whisperConfigSchema: z.ZodObject<{
|
|
|
9
9
|
json: "json";
|
|
10
10
|
}>>;
|
|
11
11
|
min_seconds: z.ZodDefault<z.ZodNumber>;
|
|
12
|
-
max_seconds: z.ZodDefault<z.ZodNumber>;
|
|
13
|
-
audio_format: z.ZodDefault<z.ZodString>;
|
|
14
12
|
update_frequency: z.ZodDefault<z.ZodString>;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
strategy: z.ZodOptional<z.ZodEnum<{
|
|
14
|
+
greedy: "greedy";
|
|
15
|
+
beam_search: "beam_search";
|
|
16
|
+
}>>;
|
|
17
|
+
n_threads: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
n_max_text_ctx: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
offset_ms: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
duration_ms: z.ZodOptional<z.ZodNumber>;
|
|
21
|
+
audio_ctx: z.ZodOptional<z.ZodNumber>;
|
|
22
|
+
translate: z.ZodOptional<z.ZodBoolean>;
|
|
23
|
+
no_context: z.ZodOptional<z.ZodBoolean>;
|
|
24
|
+
no_timestamps: z.ZodOptional<z.ZodBoolean>;
|
|
25
|
+
single_segment: z.ZodOptional<z.ZodBoolean>;
|
|
26
|
+
print_special: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
+
print_progress: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
+
print_realtime: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
print_timestamps: z.ZodOptional<z.ZodBoolean>;
|
|
30
|
+
token_timestamps: z.ZodOptional<z.ZodBoolean>;
|
|
31
|
+
thold_pt: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
thold_ptsum: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
max_len: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
split_on_word: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
+
max_tokens: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
debug_mode: z.ZodOptional<z.ZodBoolean>;
|
|
37
|
+
tdrz_enable: z.ZodOptional<z.ZodBoolean>;
|
|
38
|
+
suppress_regex: z.ZodOptional<z.ZodString>;
|
|
39
|
+
initial_prompt: z.ZodOptional<z.ZodString>;
|
|
40
|
+
language: z.ZodOptional<z.ZodString>;
|
|
41
|
+
detect_language: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
+
suppress_blank: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
+
suppress_nst: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
+
temperature: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
length_penalty: z.ZodOptional<z.ZodNumber>;
|
|
46
|
+
temperature_inc: z.ZodOptional<z.ZodNumber>;
|
|
47
|
+
entropy_thold: z.ZodOptional<z.ZodNumber>;
|
|
48
|
+
logprob_thold: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
greedy_best_of: z.ZodOptional<z.ZodNumber>;
|
|
50
|
+
beam_search_beam_size: z.ZodOptional<z.ZodNumber>;
|
|
51
|
+
vadModelPath: z.ZodOptional<z.ZodString>;
|
|
52
|
+
vad_params: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
threshold: z.ZodOptional<z.ZodNumber>;
|
|
54
|
+
min_speech_duration_ms: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
min_silence_duration_ms: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
max_speech_duration_s: z.ZodOptional<z.ZodNumber>;
|
|
57
|
+
speech_pad_ms: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
samples_overlap: z.ZodOptional<z.ZodNumber>;
|
|
59
|
+
}, z.core.$strip>>;
|
|
60
|
+
max_seconds: z.ZodOptional<z.ZodNumber>;
|
|
61
|
+
chunk_length_s: z.ZodOptional<z.ZodNumber>;
|
|
62
|
+
audio_format: z.ZodOptional<z.ZodString>;
|
|
63
|
+
contextParams: z.ZodOptional<z.ZodObject<{
|
|
64
|
+
model: z.ZodOptional<z.ZodString>;
|
|
65
|
+
use_gpu: z.ZodOptional<z.ZodBoolean>;
|
|
66
|
+
flash_attn: z.ZodOptional<z.ZodBoolean>;
|
|
67
|
+
gpu_device: z.ZodOptional<z.ZodNumber>;
|
|
68
|
+
}, z.core.$strip>>;
|
|
69
|
+
miscConfig: z.ZodOptional<z.ZodObject<{
|
|
70
|
+
caption_enabled: z.ZodOptional<z.ZodBoolean>;
|
|
22
71
|
}, z.core.$strip>>;
|
|
23
72
|
}, z.core.$strip>;
|
|
24
73
|
export type WhisperConfig = z.infer<typeof whisperConfigSchema>;
|
|
@@ -1,18 +1,72 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
const vadParamsSchema = z
|
|
3
|
+
.object({
|
|
4
|
+
threshold: z.number().optional(),
|
|
5
|
+
min_speech_duration_ms: z.number().optional(),
|
|
6
|
+
min_silence_duration_ms: z.number().optional(),
|
|
7
|
+
max_speech_duration_s: z.number().optional(),
|
|
8
|
+
speech_pad_ms: z.number().optional(),
|
|
9
|
+
samples_overlap: z.number().optional(),
|
|
10
|
+
})
|
|
11
|
+
.optional();
|
|
12
|
+
const contextParamsSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
model: z.string().optional(),
|
|
15
|
+
use_gpu: z.boolean().optional(),
|
|
16
|
+
flash_attn: z.boolean().optional(),
|
|
17
|
+
gpu_device: z.number().optional(),
|
|
18
|
+
})
|
|
19
|
+
.optional();
|
|
20
|
+
const miscConfigSchema = z
|
|
21
|
+
.object({
|
|
22
|
+
caption_enabled: z.boolean().optional(),
|
|
23
|
+
})
|
|
24
|
+
.optional();
|
|
10
25
|
export const whisperConfigSchema = z.object({
|
|
11
26
|
mode: z.enum(["caption", "batch"]).default("caption"),
|
|
12
27
|
output_format: z.enum(["plaintext", "json"]).default("plaintext"),
|
|
13
28
|
min_seconds: z.number().default(1),
|
|
14
|
-
max_seconds: z.number().default(30),
|
|
15
|
-
audio_format: z.string().default("wav"),
|
|
16
29
|
update_frequency: z.string().default("on_end"),
|
|
17
|
-
|
|
30
|
+
strategy: z.enum(["greedy", "beam_search"]).optional(),
|
|
31
|
+
n_threads: z.number().int().optional(),
|
|
32
|
+
n_max_text_ctx: z.number().int().optional(),
|
|
33
|
+
offset_ms: z.number().int().optional(),
|
|
34
|
+
duration_ms: z.number().int().optional(),
|
|
35
|
+
audio_ctx: z.number().int().optional(),
|
|
36
|
+
translate: z.boolean().optional(),
|
|
37
|
+
no_context: z.boolean().optional(),
|
|
38
|
+
no_timestamps: z.boolean().optional(),
|
|
39
|
+
single_segment: z.boolean().optional(),
|
|
40
|
+
print_special: z.boolean().optional(),
|
|
41
|
+
print_progress: z.boolean().optional(),
|
|
42
|
+
print_realtime: z.boolean().optional(),
|
|
43
|
+
print_timestamps: z.boolean().optional(),
|
|
44
|
+
token_timestamps: z.boolean().optional(),
|
|
45
|
+
thold_pt: z.number().optional(),
|
|
46
|
+
thold_ptsum: z.number().optional(),
|
|
47
|
+
max_len: z.number().int().optional(),
|
|
48
|
+
split_on_word: z.boolean().optional(),
|
|
49
|
+
max_tokens: z.number().int().optional(),
|
|
50
|
+
debug_mode: z.boolean().optional(),
|
|
51
|
+
tdrz_enable: z.boolean().optional(),
|
|
52
|
+
suppress_regex: z.string().optional(),
|
|
53
|
+
initial_prompt: z.string().optional(),
|
|
54
|
+
language: z.string().optional(),
|
|
55
|
+
detect_language: z.boolean().optional(),
|
|
56
|
+
suppress_blank: z.boolean().optional(),
|
|
57
|
+
suppress_nst: z.boolean().optional(),
|
|
58
|
+
temperature: z.number().optional(),
|
|
59
|
+
length_penalty: z.number().optional(),
|
|
60
|
+
temperature_inc: z.number().optional(),
|
|
61
|
+
entropy_thold: z.number().optional(),
|
|
62
|
+
logprob_thold: z.number().optional(),
|
|
63
|
+
greedy_best_of: z.number().int().optional(),
|
|
64
|
+
beam_search_beam_size: z.number().int().optional(),
|
|
65
|
+
vadModelPath: z.string().optional(),
|
|
66
|
+
vad_params: vadParamsSchema,
|
|
67
|
+
max_seconds: z.number().optional(),
|
|
68
|
+
chunk_length_s: z.number().optional(),
|
|
69
|
+
audio_format: z.string().optional(),
|
|
70
|
+
contextParams: contextParamsSchema,
|
|
71
|
+
miscConfig: miscConfigSchema,
|
|
18
72
|
});
|
|
@@ -2,7 +2,8 @@ import {} from "../../../../../schemas/index.js";
|
|
|
2
2
|
import { getModel, getModelConfig, } from "../../../registry/model-registry.js";
|
|
3
3
|
import { findMatchingCache, getCurrentCacheInfo, renameCacheFile, getCacheFilePath, extractSystemPrompt, generateConfigHash, customCacheExists, } from "../kv-cache-utils";
|
|
4
4
|
import fs from "bare-fs";
|
|
5
|
-
import {
|
|
5
|
+
import { insertToolsIntoHistory, setupToolGrammar, checkForToolEvents, } from "../../../../utils/tool-integration.js";
|
|
6
|
+
import { parseToolCalls } from "../../../../utils/tool-parser.js";
|
|
6
7
|
function transformMessage(message) {
|
|
7
8
|
const transformed = [];
|
|
8
9
|
// Check if it's a tool definition (has type: "function")
|
|
@@ -74,7 +75,6 @@ function prepareMessagesForCache(cachePathToUse, cacheExists, history) {
|
|
|
74
75
|
}
|
|
75
76
|
async function* processModelResponse(model, messagesToSend, shouldSaveCache, tools) {
|
|
76
77
|
const response = await model.run(messagesToSend);
|
|
77
|
-
// Create a promise that rejects when onError is triggered
|
|
78
78
|
let rejectOnError = null;
|
|
79
79
|
const errorPromise = new Promise((_, reject) => {
|
|
80
80
|
rejectOnError = reject;
|
|
@@ -86,43 +86,23 @@ async function* processModelResponse(model, messagesToSend, shouldSaveCache, too
|
|
|
86
86
|
});
|
|
87
87
|
const iterator = response.iterate();
|
|
88
88
|
let accumulatedText = "";
|
|
89
|
-
const
|
|
89
|
+
const emittedToolCallPositions = new Set();
|
|
90
|
+
let toolCallsResult = [];
|
|
90
91
|
try {
|
|
91
92
|
while (true) {
|
|
92
|
-
// Race between getting the next token and an error occurring
|
|
93
93
|
const result = await Promise.race([iterator.next(), errorPromise]);
|
|
94
94
|
if (result.done) {
|
|
95
95
|
break;
|
|
96
96
|
}
|
|
97
97
|
const token = result.value;
|
|
98
98
|
accumulatedText += token;
|
|
99
|
+
yield { token };
|
|
99
100
|
if (tools && tools.length > 0) {
|
|
100
|
-
const
|
|
101
|
-
for (const
|
|
102
|
-
|
|
103
|
-
emittedToolCalls.add(call.id);
|
|
104
|
-
const toolCallEvent = {
|
|
105
|
-
type: "toolCall",
|
|
106
|
-
call,
|
|
107
|
-
};
|
|
108
|
-
yield { token, toolCallEvent };
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
for (const error of errors) {
|
|
113
|
-
const errorKey = `error:${error.code}:${error.message}`;
|
|
114
|
-
if (!emittedToolCalls.has(errorKey)) {
|
|
115
|
-
emittedToolCalls.add(errorKey);
|
|
116
|
-
const toolCallEvent = {
|
|
117
|
-
type: "toolCallError",
|
|
118
|
-
error,
|
|
119
|
-
};
|
|
120
|
-
yield { token, toolCallEvent };
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
101
|
+
const toolEvents = checkForToolEvents(accumulatedText, token, tools, emittedToolCallPositions);
|
|
102
|
+
for (const toolEvent of toolEvents) {
|
|
103
|
+
yield { token: "", toolCallEvent: toolEvent };
|
|
123
104
|
}
|
|
124
105
|
}
|
|
125
|
-
yield { token };
|
|
126
106
|
}
|
|
127
107
|
}
|
|
128
108
|
catch (error) {
|
|
@@ -133,6 +113,10 @@ async function* processModelResponse(model, messagesToSend, shouldSaveCache, too
|
|
|
133
113
|
throw new Error(String(error));
|
|
134
114
|
}
|
|
135
115
|
}
|
|
116
|
+
if (tools && tools.length > 0) {
|
|
117
|
+
const { toolCalls } = parseToolCalls(accumulatedText, tools);
|
|
118
|
+
toolCallsResult = toolCalls;
|
|
119
|
+
}
|
|
136
120
|
if (shouldSaveCache) {
|
|
137
121
|
await model.run([{ role: "session", content: "save" }]);
|
|
138
122
|
}
|
|
@@ -142,30 +126,16 @@ async function* processModelResponse(model, messagesToSend, shouldSaveCache, too
|
|
|
142
126
|
tokensPerSecond: responseWithStats.stats?.TPS ?? 0,
|
|
143
127
|
cacheTokens: responseWithStats.stats?.CacheTokens ?? 0,
|
|
144
128
|
};
|
|
145
|
-
|
|
146
|
-
return { stats, toolCalls };
|
|
129
|
+
return { stats, toolCalls: toolCallsResult };
|
|
147
130
|
}
|
|
148
131
|
export async function* completion(params) {
|
|
149
132
|
const { history, modelId, kvCache, tools } = params;
|
|
150
|
-
// Check if model was configured with tools support
|
|
151
133
|
const modelConfig = getModelConfig(modelId);
|
|
152
134
|
const toolsEnabled = modelConfig.tools === true;
|
|
153
135
|
let historyWithTools = history;
|
|
154
136
|
if (tools && tools.length > 0 && toolsEnabled) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (systemMsgIndex >= 0) {
|
|
158
|
-
historyWithTools = [
|
|
159
|
-
...history.slice(0, systemMsgIndex + 1),
|
|
160
|
-
...tools,
|
|
161
|
-
...history.slice(systemMsgIndex + 1),
|
|
162
|
-
];
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
historyWithTools = [...tools, ...history];
|
|
166
|
-
}
|
|
167
|
-
const grammar = convertToolsToGrammar(tools);
|
|
168
|
-
modelConfig.grammar = grammar;
|
|
137
|
+
historyWithTools = insertToolsIntoHistory(history, tools);
|
|
138
|
+
setupToolGrammar(modelConfig, tools);
|
|
169
139
|
}
|
|
170
140
|
const transformedHistory = transformMessages(historyWithTools);
|
|
171
141
|
const model = getModel(modelId);
|
|
@@ -23,11 +23,12 @@ function createFilesystemDLMimic(options) {
|
|
|
23
23
|
}
|
|
24
24
|
export function createWhisperModel(modelId, modelPath, whisperConfig, vadModelPath) {
|
|
25
25
|
const { dirPath, basePath } = parseModelPath(modelPath);
|
|
26
|
-
// Handle VAD model path
|
|
26
|
+
// Handle VAD model path
|
|
27
27
|
let vadModelName = "";
|
|
28
28
|
let vadDirPath = "./";
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const effectiveVadPath = vadModelPath || whisperConfig.vadModelPath;
|
|
30
|
+
if (effectiveVadPath) {
|
|
31
|
+
const vadParsed = parseModelPath(effectiveVadPath);
|
|
31
32
|
vadDirPath = vadParsed.dirPath;
|
|
32
33
|
vadModelName = vadParsed.basePath;
|
|
33
34
|
}
|
|
@@ -45,11 +46,11 @@ export function createWhisperModel(modelId, modelPath, whisperConfig, vadModelPa
|
|
|
45
46
|
stats: false,
|
|
46
47
|
},
|
|
47
48
|
};
|
|
49
|
+
const { contextParams, miscConfig, ...whisperParams } = whisperConfig;
|
|
48
50
|
const config = {
|
|
49
|
-
whisperConfig:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
51
|
+
whisperConfig: whisperParams,
|
|
52
|
+
...(contextParams && { contextParams }),
|
|
53
|
+
...(miscConfig && { miscConfig }),
|
|
53
54
|
};
|
|
54
55
|
return new TranscriptionWhispercpp(args, config);
|
|
55
56
|
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
import { getModel } from "../../../registry/model-registry.js";
|
|
1
|
+
import { getModel, getModelConfig, updateModelConfig, } from "../../../registry/model-registry.js";
|
|
2
2
|
import { Readable } from "bare-stream";
|
|
3
3
|
import fs from "bare-fs";
|
|
4
4
|
import { needsDecoding, decodeAudioToStream } from "../../../../utils/index.js";
|
|
5
5
|
import {} from "../../../../../schemas/index.js";
|
|
6
6
|
export async function* transcribe(params) {
|
|
7
7
|
const model = getModel(params.modelId);
|
|
8
|
+
if (params.prompt && typeof model.reload === "function") {
|
|
9
|
+
const existingConfig = getModelConfig(params.modelId);
|
|
10
|
+
const updatedConfig = {
|
|
11
|
+
...existingConfig,
|
|
12
|
+
initial_prompt: params.prompt,
|
|
13
|
+
};
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15
|
+
const { contextParams: _, miscConfig, ...whisperParams } = updatedConfig;
|
|
16
|
+
await model.reload({
|
|
17
|
+
whisperConfig: whisperParams,
|
|
18
|
+
...(miscConfig && { miscConfig }),
|
|
19
|
+
});
|
|
20
|
+
updateModelConfig(params.modelId, updatedConfig);
|
|
21
|
+
}
|
|
8
22
|
let audioStream;
|
|
9
23
|
const audioChunk = params.audioChunk;
|
|
10
24
|
switch (audioChunk.type) {
|
|
@@ -25,6 +25,7 @@ export interface AnyModel {
|
|
|
25
25
|
}>>;
|
|
26
26
|
await(): Promise<void>;
|
|
27
27
|
}>;
|
|
28
|
+
reload?(config: unknown): Promise<void>;
|
|
28
29
|
unload?(): Promise<void>;
|
|
29
30
|
addon?: AddonInterface;
|
|
30
31
|
}
|
|
@@ -66,6 +67,7 @@ export declare function getModelInfo(id: string): {
|
|
|
66
67
|
config: unknown;
|
|
67
68
|
} | null;
|
|
68
69
|
export declare function getModelConfig(id: string): unknown;
|
|
70
|
+
export declare function updateModelConfig(id: string, config: unknown): void;
|
|
69
71
|
export declare function clearRegistry(): void;
|
|
70
72
|
export declare function getRegistryStats(): {
|
|
71
73
|
totalModels: number;
|
|
@@ -80,6 +80,13 @@ export function getModelConfig(id) {
|
|
|
80
80
|
}
|
|
81
81
|
return entry.local.config;
|
|
82
82
|
}
|
|
83
|
+
export function updateModelConfig(id, config) {
|
|
84
|
+
const entry = modelRegistry.get(id);
|
|
85
|
+
if (!entry || !entry.local) {
|
|
86
|
+
throw new Error(`Model with ID "${id}" not found or is delegated`);
|
|
87
|
+
}
|
|
88
|
+
entry.local.config = config;
|
|
89
|
+
}
|
|
83
90
|
export function clearRegistry() {
|
|
84
91
|
modelRegistry.clear();
|
|
85
92
|
console.log("Model registry cleared");
|
|
@@ -23,7 +23,6 @@ export async function* handleCompletionStream(request) {
|
|
|
23
23
|
done = true;
|
|
24
24
|
}
|
|
25
25
|
else {
|
|
26
|
-
buffer += result.value.token;
|
|
27
26
|
if (request.stream) {
|
|
28
27
|
yield {
|
|
29
28
|
type: "completionStream",
|
|
@@ -31,11 +30,14 @@ export async function* handleCompletionStream(request) {
|
|
|
31
30
|
toolCallEvent: result.value.toolCallEvent,
|
|
32
31
|
};
|
|
33
32
|
}
|
|
33
|
+
else {
|
|
34
|
+
buffer += result.value.token;
|
|
35
|
+
}
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
yield {
|
|
37
39
|
type: "completionStream",
|
|
38
|
-
token: buffer,
|
|
40
|
+
token: request.stream ? "" : buffer,
|
|
39
41
|
done: true,
|
|
40
42
|
stats,
|
|
41
43
|
toolCalls,
|
|
@@ -27,11 +27,10 @@ async function validateCachedFile(modelPath, url, signal) {
|
|
|
27
27
|
expectedSize = parseInt(response.headers.get("content-length") || "0");
|
|
28
28
|
}
|
|
29
29
|
catch (error) {
|
|
30
|
-
console.log(`⚠️ Could not get expected file size from HEAD request: ${String(error)}`);
|
|
31
|
-
|
|
32
|
-
return modelPath;
|
|
30
|
+
console.log(`⚠️ Could not get expected file size from HEAD request, will re-download: ${String(error)}`);
|
|
31
|
+
return null;
|
|
33
32
|
}
|
|
34
|
-
if (
|
|
33
|
+
if (localSize !== expectedSize) {
|
|
35
34
|
console.log(`📥 Cached file size mismatch. Expected: ${expectedSize}, Found: ${localSize}. Re-downloading...`);
|
|
36
35
|
return null;
|
|
37
36
|
}
|
|
@@ -64,6 +63,13 @@ async function performHttpDownload(url, modelPath, downloadKey, progressCallback
|
|
|
64
63
|
const headers = {
|
|
65
64
|
"User-Agent": "qvac-sdk",
|
|
66
65
|
};
|
|
66
|
+
const isPresignedUrl = url.includes("X-Amz-") || // AWS S3
|
|
67
|
+
url.includes("X-Goog-") || // Google Cloud Storage
|
|
68
|
+
(url.includes("sig=") && url.includes("se=")); // Azure Blob Storage SAS
|
|
69
|
+
if (isPresignedUrl) {
|
|
70
|
+
const urlObj = new URL(url);
|
|
71
|
+
headers["host"] = urlObj.hostname; // Override to prevent :443 from being appended
|
|
72
|
+
}
|
|
67
73
|
if (startOffset > 0) {
|
|
68
74
|
headers["Range"] = `bytes=${startOffset}-`;
|
|
69
75
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { transcribeStream } from "../../bare/addons/whispercpp/index.js";
|
|
2
2
|
export async function* handleTranscribeStream(request) {
|
|
3
|
-
const { modelId, audioChunk } = request;
|
|
3
|
+
const { modelId, audioChunk, prompt } = request;
|
|
4
4
|
try {
|
|
5
5
|
// Stream transcription results in real-time
|
|
6
|
-
for await (const textChunk of transcribeStream({
|
|
6
|
+
for await (const textChunk of transcribeStream({
|
|
7
|
+
audioChunk,
|
|
8
|
+
modelId,
|
|
9
|
+
prompt,
|
|
10
|
+
})) {
|
|
7
11
|
yield {
|
|
8
12
|
type: "transcribeStream",
|
|
9
13
|
text: textChunk,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Tool, ToolCallEvent } from "../../schemas/index";
|
|
2
|
+
interface HistoryMessage {
|
|
3
|
+
role: string;
|
|
4
|
+
content: string;
|
|
5
|
+
attachments?: {
|
|
6
|
+
path: string;
|
|
7
|
+
}[] | undefined;
|
|
8
|
+
}
|
|
9
|
+
export declare function insertToolsIntoHistory(history: HistoryMessage[], tools: Tool[]): Array<HistoryMessage | Tool>;
|
|
10
|
+
export declare function setupToolGrammar(modelConfig: Record<string, unknown>, tools: Tool[]): void;
|
|
11
|
+
export declare function checkForToolEvents(accumulatedText: string, currentToken: string, tools: Tool[], emittedToolCallPositions: Set<number>): ToolCallEvent[];
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=tool-integration.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { parseToolCalls, convertToolsToGrammar, } from "./tool-parser.js";
|
|
2
|
+
export function insertToolsIntoHistory(history, tools) {
|
|
3
|
+
const systemMsgIndex = history.findIndex((msg) => msg.role === "system");
|
|
4
|
+
if (systemMsgIndex >= 0) {
|
|
5
|
+
return [
|
|
6
|
+
...history.slice(0, systemMsgIndex + 1),
|
|
7
|
+
...tools,
|
|
8
|
+
...history.slice(systemMsgIndex + 1),
|
|
9
|
+
];
|
|
10
|
+
}
|
|
11
|
+
return [...tools, ...history];
|
|
12
|
+
}
|
|
13
|
+
export function setupToolGrammar(modelConfig, tools) {
|
|
14
|
+
const grammar = convertToolsToGrammar(tools);
|
|
15
|
+
modelConfig["grammar"] = grammar;
|
|
16
|
+
}
|
|
17
|
+
export function checkForToolEvents(accumulatedText, currentToken, tools, emittedToolCallPositions) {
|
|
18
|
+
const events = [];
|
|
19
|
+
if (currentToken.includes("</tool_call>") || currentToken.includes("}")) {
|
|
20
|
+
const { toolCalls, errors } = parseToolCalls(accumulatedText, tools);
|
|
21
|
+
for (const call of toolCalls) {
|
|
22
|
+
const callPosition = accumulatedText.indexOf(call.raw || "");
|
|
23
|
+
if (callPosition >= 0 && !emittedToolCallPositions.has(callPosition)) {
|
|
24
|
+
emittedToolCallPositions.add(callPosition);
|
|
25
|
+
events.push({
|
|
26
|
+
type: "toolCall",
|
|
27
|
+
call,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const error of errors) {
|
|
32
|
+
const errorPosition = accumulatedText.indexOf(error.raw || "");
|
|
33
|
+
if (errorPosition >= 0 && !emittedToolCallPositions.has(errorPosition)) {
|
|
34
|
+
emittedToolCallPositions.add(errorPosition);
|
|
35
|
+
events.push({
|
|
36
|
+
type: "toolCallError",
|
|
37
|
+
error,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return events;
|
|
43
|
+
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let toolCallSequence = 0;
|
|
2
|
+
function generateStableToolCallId(name, args) {
|
|
3
|
+
const content = `${name}:${JSON.stringify(args)}`;
|
|
4
|
+
let hash = 0;
|
|
5
|
+
for (let i = 0; i < content.length; i++) {
|
|
6
|
+
const char = content.charCodeAt(i);
|
|
7
|
+
hash = (hash << 5) - hash + char;
|
|
8
|
+
hash = hash & hash;
|
|
9
|
+
}
|
|
10
|
+
const sequence = toolCallSequence++;
|
|
11
|
+
return `call_${Math.abs(hash).toString(36)}_${sequence}`;
|
|
3
12
|
}
|
|
4
13
|
function isValidToolCall(obj) {
|
|
5
14
|
if (!obj || typeof obj !== "object") {
|
|
@@ -63,7 +72,7 @@ function parseGemmaFormat(text, tools) {
|
|
|
63
72
|
continue;
|
|
64
73
|
}
|
|
65
74
|
toolCalls.push({
|
|
66
|
-
id: call.id ||
|
|
75
|
+
id: call.id || generateStableToolCallId(call.name, call.arguments),
|
|
67
76
|
name: call.name,
|
|
68
77
|
arguments: call.arguments,
|
|
69
78
|
raw: JSON.stringify(call),
|
|
@@ -80,13 +89,14 @@ function parseQwenFormat(text, tools) {
|
|
|
80
89
|
const toolCalls = [];
|
|
81
90
|
const errors = [];
|
|
82
91
|
const toolCallRegex = /<tool_call>\s*({[\s\S]*?})\s*<\/tool_call>/g;
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
const matches = Array.from(text.matchAll(toolCallRegex));
|
|
93
|
+
for (const match of matches) {
|
|
85
94
|
try {
|
|
86
95
|
const callJson = match[1];
|
|
87
96
|
if (!callJson)
|
|
88
97
|
continue;
|
|
89
|
-
const
|
|
98
|
+
const trimmedJson = callJson.trim();
|
|
99
|
+
const callItem = JSON.parse(trimmedJson);
|
|
90
100
|
if (!isValidToolCall(callItem)) {
|
|
91
101
|
continue;
|
|
92
102
|
}
|
|
@@ -95,15 +105,15 @@ function parseQwenFormat(text, tools) {
|
|
|
95
105
|
if (!validation.isValid && validation.error) {
|
|
96
106
|
errors.push({
|
|
97
107
|
...validation.error,
|
|
98
|
-
raw:
|
|
108
|
+
raw: trimmedJson,
|
|
99
109
|
});
|
|
100
110
|
continue;
|
|
101
111
|
}
|
|
102
112
|
toolCalls.push({
|
|
103
|
-
id: call.id ||
|
|
113
|
+
id: call.id || generateStableToolCallId(call.name, call.arguments),
|
|
104
114
|
name: call.name,
|
|
105
115
|
arguments: call.arguments,
|
|
106
|
-
raw:
|
|
116
|
+
raw: trimmedJson,
|
|
107
117
|
});
|
|
108
118
|
}
|
|
109
119
|
catch (error) {
|
|
@@ -132,7 +142,8 @@ function parseLlamacppFormat(text, tools) {
|
|
|
132
142
|
}
|
|
133
143
|
else {
|
|
134
144
|
toolCalls.push({
|
|
135
|
-
id: parsed.id ||
|
|
145
|
+
id: parsed.id ||
|
|
146
|
+
generateStableToolCallId(parsed.name, parsed.arguments),
|
|
136
147
|
name: parsed.name,
|
|
137
148
|
arguments: parsed.arguments,
|
|
138
149
|
raw: text,
|
|
@@ -164,7 +175,7 @@ function parseGenericFormat(text, tools) {
|
|
|
164
175
|
continue;
|
|
165
176
|
}
|
|
166
177
|
toolCalls.push({
|
|
167
|
-
id: obj.id ||
|
|
178
|
+
id: obj.id || generateStableToolCallId(obj.name, obj.arguments),
|
|
168
179
|
name: obj.name,
|
|
169
180
|
arguments: obj.arguments,
|
|
170
181
|
raw: match[0],
|
|
@@ -186,17 +197,20 @@ export function parseToolCalls(text, tools) {
|
|
|
186
197
|
return { toolCalls: [], errors: [] };
|
|
187
198
|
}
|
|
188
199
|
let result = parseGemmaFormat(text, tools);
|
|
189
|
-
if (result.toolCalls.length > 0
|
|
200
|
+
if (result.toolCalls.length > 0) {
|
|
190
201
|
return result;
|
|
191
202
|
}
|
|
192
203
|
result = parseQwenFormat(text, tools);
|
|
193
|
-
if (result.toolCalls.length > 0
|
|
204
|
+
if (result.toolCalls.length > 0) {
|
|
194
205
|
return result;
|
|
195
206
|
}
|
|
196
207
|
result = parseLlamacppFormat(text, tools);
|
|
197
|
-
if (result.toolCalls.length > 0
|
|
208
|
+
if (result.toolCalls.length > 0) {
|
|
198
209
|
return result;
|
|
199
210
|
}
|
|
211
|
+
if (text.includes("<tool_call>")) {
|
|
212
|
+
return { toolCalls: [], errors: [] };
|
|
213
|
+
}
|
|
200
214
|
result = parseGenericFormat(text, tools);
|
|
201
215
|
return result;
|
|
202
216
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qvac/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@qvac/langdetect-text": "^0.1.0",
|
|
61
61
|
"@qvac/logging": "^0.1.0",
|
|
62
62
|
"@qvac/rag": "^0.2.1",
|
|
63
|
-
"@qvac/transcription-whispercpp": "^0.1.
|
|
63
|
+
"@qvac/transcription-whispercpp": "^0.1.14",
|
|
64
64
|
"@qvac/translation-nmtcpp": "^0.1.2",
|
|
65
65
|
"@qvac/tts-onnx": "^0.2.1",
|
|
66
66
|
"fast-safe-stringify": "2.1.1",
|