@qvac/sdk 0.10.1 → 0.11.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 +92 -54
- package/dist/client/api/cancel.d.ts +24 -32
- package/dist/client/api/cancel.js +58 -31
- package/dist/client/api/client-request-id.d.ts +17 -0
- package/dist/client/api/client-request-id.js +27 -0
- package/dist/client/api/completion-stream.d.ts +1 -1
- package/dist/client/api/completion-stream.js +34 -3
- package/dist/client/api/diffusion.d.ts +46 -0
- package/dist/client/api/diffusion.js +50 -1
- package/dist/client/api/download-asset.d.ts +12 -2
- package/dist/client/api/download-asset.js +21 -3
- package/dist/client/api/embed.d.ts +8 -4
- package/dist/client/api/embed.js +14 -1
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/index.d.ts +1 -0
- package/dist/client/api/index.js +1 -0
- package/dist/client/api/load-model.d.ts +12 -4
- package/dist/client/api/load-model.js +25 -1
- package/dist/client/api/rag.d.ts +9 -3
- package/dist/client/api/rag.js +23 -3
- package/dist/client/api/transcribe.d.ts +20 -8
- package/dist/client/api/transcribe.js +58 -4
- package/dist/client/api/unload-model.d.ts +5 -2
- package/dist/client/api/unload-model.js +9 -4
- package/dist/client/api/upscale.d.ts +45 -0
- package/dist/client/api/upscale.js +99 -0
- package/dist/client/rpc/expo-rpc-client.js +2 -1
- package/dist/client/rpc/node-rpc-client.js +18 -7
- package/dist/client/rpc/rpc-client.d.ts +2 -2
- package/dist/client/rpc/rpc-client.js +10 -4
- package/dist/client/rpc/rpc-error.d.ts +9 -0
- package/dist/client/rpc/rpc-error.js +92 -0
- package/dist/examples/cancel-by-request-id.d.ts +38 -0
- package/dist/examples/cancel-by-request-id.js +103 -0
- package/dist/examples/diffusion-esrgan-upscale.d.ts +2 -0
- package/dist/examples/diffusion-esrgan-upscale.js +78 -0
- package/dist/examples/download-with-cancel.js +10 -10
- package/dist/examples/embed-multi-gpu.d.ts +2 -0
- package/dist/examples/embed-multi-gpu.js +46 -0
- package/dist/examples/llamacpp-multi-gpu.d.ts +2 -0
- package/dist/examples/llamacpp-multi-gpu.js +57 -0
- package/dist/examples/rag/rag-hyperdb/cancellation.js +20 -17
- package/dist/examples/tools/llamacpp-tools-gemma4.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-gemma4.js +76 -0
- package/dist/examples/tools/llamacpp-tools-qwen35.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-qwen35.js +70 -0
- package/dist/examples/transcription/whispercpp-microphone-conversation.d.ts +2 -0
- package/dist/examples/transcription/whispercpp-microphone-conversation.js +102 -0
- package/dist/examples/tts/supertonic.js +1 -1
- package/dist/expo/plugins/withMobileBundle.d.ts +20 -4
- package/dist/expo/plugins/withMobileBundle.js +44 -6
- package/dist/index.d.ts +5 -2
- package/dist/index.js +21 -1
- package/dist/models/registry/models.d.ts +186 -318
- package/dist/models/registry/models.js +1522 -1667
- package/dist/pear/pre.d.ts +3 -0
- package/dist/pear/pre.js +3 -0
- package/dist/schemas/cancel.d.ts +167 -28
- package/dist/schemas/cancel.js +119 -19
- package/dist/schemas/common.d.ts +427 -208
- package/dist/schemas/common.js +3 -1
- package/dist/schemas/completion-event.d.ts +14 -0
- package/dist/schemas/completion-event.js +11 -1
- package/dist/schemas/completion-stream.d.ts +15 -1
- package/dist/schemas/completion-stream.js +25 -7
- package/dist/schemas/download-asset.d.ts +15 -9
- package/dist/schemas/download-asset.js +17 -6
- package/dist/schemas/embed.d.ts +1 -0
- package/dist/schemas/embed.js +5 -0
- package/dist/schemas/error.d.ts +31 -0
- package/dist/schemas/error.js +29 -1
- package/dist/schemas/finetune.d.ts +2 -0
- package/dist/schemas/finetune.js +5 -0
- package/dist/schemas/get-model-info.d.ts +6 -6
- package/dist/schemas/llamacpp-config.d.ts +54 -8
- package/dist/schemas/llamacpp-config.js +12 -0
- package/dist/schemas/load-model.d.ts +1911 -1260
- package/dist/schemas/load-model.js +15 -0
- package/dist/schemas/model-src-utils.d.ts +14 -14
- package/dist/schemas/model-types.d.ts +10 -10
- package/dist/schemas/ocr.d.ts +3 -3
- package/dist/schemas/plugin.d.ts +40 -1
- package/dist/schemas/plugin.js +10 -1
- package/dist/schemas/rag.d.ts +9 -0
- package/dist/schemas/rag.js +17 -9
- package/dist/schemas/registry.d.ts +30 -30
- package/dist/schemas/reload-config.d.ts +14 -14
- package/dist/schemas/sdcpp-config.d.ts +131 -17
- package/dist/schemas/sdcpp-config.js +163 -1
- package/dist/schemas/sdk-config.d.ts +649 -271
- package/dist/schemas/sdk-errors-client.d.ts +1 -0
- package/dist/schemas/sdk-errors-client.js +5 -0
- package/dist/schemas/sdk-errors-server.d.ts +5 -0
- package/dist/schemas/sdk-errors-server.js +25 -0
- package/dist/schemas/text-to-speech.d.ts +78 -78
- package/dist/schemas/transcription-config.d.ts +24 -24
- package/dist/schemas/transcription.d.ts +56 -2
- package/dist/schemas/transcription.js +27 -0
- package/dist/schemas/translate.d.ts +2 -0
- package/dist/schemas/translate.js +13 -2
- package/dist/schemas/translation-config.d.ts +40 -40
- package/dist/schemas/unload-model.d.ts +2 -1
- package/dist/schemas/unload-model.js +4 -1
- package/dist/scripts/run-unit-tests.js +15 -2
- package/dist/server/bare/delegate-rpc-client.js +10 -8
- package/dist/server/bare/ops/cancel.d.ts +27 -1
- package/dist/server/bare/ops/cancel.js +36 -10
- package/dist/server/bare/ops/embed.d.ts +3 -3
- package/dist/server/bare/ops/embed.js +144 -28
- package/dist/server/bare/ops/kv-cache-utils.d.ts +0 -7
- package/dist/server/bare/ops/kv-cache-utils.js +8 -47
- package/dist/server/bare/ops/transcribe.d.ts +10 -5
- package/dist/server/bare/ops/transcribe.js +196 -41
- package/dist/server/bare/ops/translate.d.ts +1 -1
- package/dist/server/bare/ops/translate.js +197 -88
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +12 -3
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +170 -172
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +125 -21
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +154 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +448 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +21 -73
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +18 -101
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +30 -3
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +157 -85
- package/dist/server/bare/plugins/llamacpp-completion/transform.d.ts +10 -0
- package/dist/server/bare/plugins/llamacpp-completion/transform.js +34 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +7 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -1
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +17 -15
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +5 -1
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +3 -3
- package/dist/server/bare/plugins/onnx-ocr/plugin.js +3 -0
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +39 -39
- package/dist/server/bare/plugins/onnx-tts/plugin.js +6 -0
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +29 -21
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +4 -1
- package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +8 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.d.ts +3 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.js +40 -0
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +91 -16
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +97 -7
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +22 -3
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +50 -14
- package/dist/server/bare/rag-hyperdb/index.d.ts +1 -1
- package/dist/server/bare/rag-hyperdb/index.js +1 -1
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +26 -4
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +65 -28
- package/dist/server/bare/runtime/disposable-scope.d.ts +36 -0
- package/dist/server/bare/runtime/disposable-scope.js +69 -0
- package/dist/server/bare/runtime/index.d.ts +8 -0
- package/dist/server/bare/runtime/index.js +3 -0
- package/dist/server/bare/runtime/request-context.d.ts +49 -0
- package/dist/server/bare/runtime/request-context.js +1 -0
- package/dist/server/bare/runtime/request-id.d.ts +12 -0
- package/dist/server/bare/runtime/request-id.js +14 -0
- package/dist/server/bare/runtime/request-registry-singleton.d.ts +4 -0
- package/dist/server/bare/runtime/request-registry-singleton.js +27 -0
- package/dist/server/bare/runtime/request-registry.d.ts +136 -0
- package/dist/server/bare/runtime/request-registry.js +297 -0
- package/dist/server/bare/runtime/with-request-context.d.ts +22 -0
- package/dist/server/bare/runtime/with-request-context.js +53 -0
- package/dist/server/bare/types/addon-responses.d.ts +11 -1
- package/dist/server/rpc/handler-registry.js +26 -7
- package/dist/server/rpc/handlers/cancel-delegated.js +28 -31
- package/dist/server/rpc/handlers/cancelHandler.d.ts +30 -1
- package/dist/server/rpc/handlers/cancelHandler.js +54 -22
- package/dist/server/rpc/handlers/delete-cache.d.ts +9 -0
- package/dist/server/rpc/handlers/delete-cache.js +13 -15
- package/dist/server/rpc/handlers/download-asset.js +133 -40
- package/dist/server/rpc/handlers/index.d.ts +2 -0
- package/dist/server/rpc/handlers/index.js +2 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +60 -2
- package/dist/server/rpc/handlers/load-model/download-manager.js +141 -8
- package/dist/server/rpc/handlers/load-model/handler.js +213 -98
- package/dist/server/rpc/handlers/load-model/http.js +3 -3
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -1
- package/dist/server/rpc/handlers/load-model/registry.js +1 -1
- package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +20 -1
- package/dist/server/rpc/handlers/load-model/resolve-session.js +6 -5
- package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -2
- package/dist/server/rpc/handlers/load-model/resolve.js +9 -6
- package/dist/server/rpc/handlers/load-model/types.d.ts +24 -0
- package/dist/server/rpc/handlers/rag.js +133 -15
- package/dist/server/rpc/handlers/upscale-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/upscale-stream.js +4 -0
- package/dist/server/utils/completion-normalizer.js +8 -0
- package/dist/server/utils/tools/dialect.js +4 -0
- package/dist/server/utils/tools/parser.js +16 -1
- package/dist/server/utils/tools/parsers/gemma4native.d.ts +4 -0
- package/dist/server/utils/tools/parsers/gemma4native.js +60 -0
- package/dist/server/utils/tools/parsers/qwen35.d.ts +4 -0
- package/dist/server/utils/tools/parsers/qwen35.js +120 -0
- package/dist/utils/aggregate-events.d.ts +9 -0
- package/dist/utils/aggregate-events.js +18 -3
- package/dist/utils/decorate-promise.d.ts +34 -0
- package/dist/utils/decorate-promise.js +35 -0
- package/dist/utils/errors-client.d.ts +3 -0
- package/dist/utils/errors-client.js +5 -0
- package/dist/utils/errors-server.d.ts +85 -0
- package/dist/utils/errors-server.js +97 -0
- package/package.json +20 -15
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ModelProgressUpdate } from "../../../../schemas/index";
|
|
2
2
|
import type { ResolveResult, DownloadHooks } from "./types";
|
|
3
|
-
|
|
4
|
-
export declare function
|
|
3
|
+
import type { AbortSignal } from "bare-abort-controller";
|
|
4
|
+
export declare function resolveModelPath(modelSrc: unknown, progressCallback?: (progress: ModelProgressUpdate) => void, seed?: boolean, signal?: AbortSignal, hooks?: DownloadHooks): Promise<string>;
|
|
5
|
+
export declare function resolveModelPathWithStats(modelSrc: unknown, progressCallback?: (progress: ModelProgressUpdate) => void, seed?: boolean, signal?: AbortSignal, hooks?: DownloadHooks): Promise<ResolveResult>;
|
|
5
6
|
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -7,7 +7,7 @@ import { downloadModelFromHttp } from "./http.js";
|
|
|
7
7
|
import { downloadModelFromHyperdrive } from "./hyperdrive.js";
|
|
8
8
|
import { downloadModelFromRegistry } from "./registry.js";
|
|
9
9
|
import { downloadModelFromHttpWithStats, downloadModelFromHyperdriveWithStats, downloadModelFromRegistryWithStats, } from "./download-stats.js";
|
|
10
|
-
import { ModelLoadFailedError, ModelNotFoundError, SeedingNotSupportedError, } from "../../../../utils/errors-server.js";
|
|
10
|
+
import { InferenceCancelledError, ModelLoadFailedError, ModelNotFoundError, SeedingNotSupportedError, } from "../../../../utils/errors-server.js";
|
|
11
11
|
import { validateAndJoinPath } from "../../../utils/path-security.js";
|
|
12
12
|
import { getServerLogger } from "../../../../logging/index.js";
|
|
13
13
|
import { modelInputToSrcSchema } from "../../../../schemas/index.js";
|
|
@@ -74,7 +74,10 @@ function buildResult(pathOrResult, sourceType) {
|
|
|
74
74
|
}
|
|
75
75
|
return { path: pathOrResult, sourceType };
|
|
76
76
|
}
|
|
77
|
-
async function resolveModelPathCore(modelSrc, progressCallback, seed, mode, hooks) {
|
|
77
|
+
async function resolveModelPathCore(modelSrc, progressCallback, seed, mode, signal, hooks) {
|
|
78
|
+
if (signal?.aborted) {
|
|
79
|
+
throw new InferenceCancelledError(hooks?.requestBinding?.requestId ?? "unknown");
|
|
80
|
+
}
|
|
78
81
|
const srcString = modelInputToSrcSchema.parse(modelSrc);
|
|
79
82
|
// Parse hyperdrive URLs if present
|
|
80
83
|
let hyperdriveKey;
|
|
@@ -137,10 +140,10 @@ async function resolveModelPathCore(modelSrc, progressCallback, seed, mode, hook
|
|
|
137
140
|
}
|
|
138
141
|
return { path: resolvedPath, sourceType: "filesystem" };
|
|
139
142
|
}
|
|
140
|
-
export async function resolveModelPath(modelSrc, progressCallback, seed, hooks) {
|
|
141
|
-
const result = await resolveModelPathCore(modelSrc, progressCallback, seed, "base", hooks);
|
|
143
|
+
export async function resolveModelPath(modelSrc, progressCallback, seed, signal, hooks) {
|
|
144
|
+
const result = await resolveModelPathCore(modelSrc, progressCallback, seed, "base", signal, hooks);
|
|
142
145
|
return result.path;
|
|
143
146
|
}
|
|
144
|
-
export async function resolveModelPathWithStats(modelSrc, progressCallback, seed, hooks) {
|
|
145
|
-
return resolveModelPathCore(modelSrc, progressCallback, seed, "stats", hooks);
|
|
147
|
+
export async function resolveModelPathWithStats(modelSrc, progressCallback, seed, signal, hooks) {
|
|
148
|
+
return resolveModelPathCore(modelSrc, progressCallback, seed, "stats", signal, hooks);
|
|
146
149
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { SourceType } from "../../../../schemas/index";
|
|
2
|
+
import type { AbortSignal } from "bare-abort-controller";
|
|
3
|
+
import type { DisposableScope } from "../../../bare/runtime/disposable-scope";
|
|
2
4
|
export interface DownloadStats {
|
|
3
5
|
downloadTimeMs?: number;
|
|
4
6
|
totalBytesDownloaded?: number;
|
|
@@ -16,12 +18,34 @@ export interface DownloadResult {
|
|
|
16
18
|
path: string;
|
|
17
19
|
stats?: DownloadStats;
|
|
18
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Internal request binding threaded through `DownloadHooks` so the
|
|
23
|
+
* download manager's `startOrJoinDownload` can register a per-subscriber
|
|
24
|
+
* cancel hook against the caller's `RequestContext`. The binding's
|
|
25
|
+
* fields mirror the registry context's surface area: `signal` is wired
|
|
26
|
+
* to a subscriber-removing listener, `scope` registers the idempotent
|
|
27
|
+
* cleanup, and `requestId` lets `cancelTransfer` (legacy entry point)
|
|
28
|
+
* route cancels through `registry.cancel(...)` rather than reaching
|
|
29
|
+
* into the transfer directly.
|
|
30
|
+
*/
|
|
31
|
+
export interface DownloadRequestBinding {
|
|
32
|
+
signal: AbortSignal;
|
|
33
|
+
scope: DisposableScope;
|
|
34
|
+
requestId: string;
|
|
35
|
+
}
|
|
19
36
|
export interface DownloadHooks {
|
|
20
37
|
onDownloadKey?: (key: string) => void;
|
|
21
38
|
markCacheHit?: () => void;
|
|
22
39
|
markCacheMiss?: () => void;
|
|
23
40
|
markSharedTransfer?: () => void;
|
|
24
41
|
addChecksumValidationTimeMs?: (durationMs: number) => void;
|
|
42
|
+
/**
|
|
43
|
+
* When set, `startOrJoinDownload` attaches a per-subscriber cancel
|
|
44
|
+
* listener bound to this request. `registry.cancel({ requestId })`
|
|
45
|
+
* aborts only this subscriber; the transfer keeps running for siblings
|
|
46
|
+
* joined on the same `downloadKey` until the last subscriber leaves.
|
|
47
|
+
*/
|
|
48
|
+
requestBinding?: DownloadRequestBinding;
|
|
25
49
|
}
|
|
26
50
|
export interface LoadModelProfilingMeta {
|
|
27
51
|
sourceType?: string;
|
|
@@ -1,4 +1,59 @@
|
|
|
1
|
-
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import { chunk, ingest, reindex, saveEmbeddings, search, deleteEmbeddings, listWorkspaces, closeWorkspace, deleteWorkspace, DEFAULT_WORKSPACE, getActiveRagRequest, setActiveRagRequest, clearActiveRagRequest, } from "../../bare/rag-hyperdb/index.js";
|
|
54
|
+
import { getRequestRegistry, withRequestContext, } from "../../bare/runtime/index.js";
|
|
55
|
+
import { generateServerRequestId } from "../../bare/runtime/request-id.js";
|
|
56
|
+
import { getServerLogger } from "../../../logging/index.js";
|
|
2
57
|
import { profileReplyHandler, registerOperationMetrics, } from "../profiling/index.js";
|
|
3
58
|
registerOperationMetrics({
|
|
4
59
|
op: "rag",
|
|
@@ -20,8 +75,7 @@ registerOperationMetrics({
|
|
|
20
75
|
return Object.keys(gauges).length > 0 ? gauges : undefined;
|
|
21
76
|
},
|
|
22
77
|
});
|
|
23
|
-
function createHandlerOptions(operation, workspace, onProgress) {
|
|
24
|
-
const signal = registerRagOperation(workspace, operation);
|
|
78
|
+
function createHandlerOptions(operation, workspace, signal, onProgress) {
|
|
25
79
|
const options = { signal };
|
|
26
80
|
if (onProgress) {
|
|
27
81
|
options.onProgress = (stage, current, total) => onProgress({
|
|
@@ -42,6 +96,37 @@ function omitOnProgress(obj) {
|
|
|
42
96
|
void withProgress;
|
|
43
97
|
return rest;
|
|
44
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Begin a registry-tracked RAG context with workspace-level pre-emption.
|
|
101
|
+
*
|
|
102
|
+
* Workspace-level admission lives in the dispatcher rather than as a
|
|
103
|
+
* registry policy primitive (it's a dispatch concern, not a registry
|
|
104
|
+
* `kind` admission rule). The sequence is **cancel-prior → begin-new**:
|
|
105
|
+
* if another RAG operation is already running on the same workspace,
|
|
106
|
+
* cancel it first, then begin the new context. Reversing the order
|
|
107
|
+
* would cancel the just-installed context.
|
|
108
|
+
*
|
|
109
|
+
* The workspace → requestId map is updated after `begin(...)` succeeds
|
|
110
|
+
* and cleared on scope unwind via `scope.defer(...)`, with a
|
|
111
|
+
* "still mine?" guard so an older op's deferred cleanup cannot stomp
|
|
112
|
+
* a newer op's mapping.
|
|
113
|
+
*/
|
|
114
|
+
function beginRagContext(workspace, requestId) {
|
|
115
|
+
const registry = getRequestRegistry();
|
|
116
|
+
const prev = getActiveRagRequest(workspace);
|
|
117
|
+
if (prev !== undefined && prev !== requestId) {
|
|
118
|
+
registry.cancel({ requestId: prev, reason: "rag-workspace-preempt" });
|
|
119
|
+
}
|
|
120
|
+
const ctx = registry.begin({
|
|
121
|
+
requestId,
|
|
122
|
+
kind: "rag",
|
|
123
|
+
});
|
|
124
|
+
setActiveRagRequest(workspace, requestId);
|
|
125
|
+
ctx.scope.defer(() => {
|
|
126
|
+
clearActiveRagRequest(workspace, requestId);
|
|
127
|
+
});
|
|
128
|
+
return ctx;
|
|
129
|
+
}
|
|
45
130
|
export async function handleRag(request, onProgress) {
|
|
46
131
|
return profileReplyHandler({ op: "rag", request }, async () => handleRagInternal(request, onProgress));
|
|
47
132
|
}
|
|
@@ -57,10 +142,15 @@ async function handleRagInternal(request, onProgress) {
|
|
|
57
142
|
};
|
|
58
143
|
}
|
|
59
144
|
case "ingest": {
|
|
60
|
-
const
|
|
61
|
-
const handlerOptions = createHandlerOptions("ingest", workspace, onProgress);
|
|
62
|
-
const params = omitOnProgress(request);
|
|
145
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
63
146
|
try {
|
|
147
|
+
const workspace = request.workspace ?? DEFAULT_WORKSPACE;
|
|
148
|
+
const requestId = request.requestId ?? generateServerRequestId();
|
|
149
|
+
const ctx = __addDisposableResource(env_1, beginRagContext(workspace, requestId), true);
|
|
150
|
+
const log = withRequestContext(getServerLogger(), ctx);
|
|
151
|
+
log.debug("ingest start");
|
|
152
|
+
const handlerOptions = createHandlerOptions("ingest", workspace, ctx.signal, onProgress);
|
|
153
|
+
const params = omitOnProgress(request);
|
|
64
154
|
const result = await ingest(params, handlerOptions);
|
|
65
155
|
return {
|
|
66
156
|
type: "rag",
|
|
@@ -70,15 +160,26 @@ async function handleRagInternal(request, onProgress) {
|
|
|
70
160
|
droppedIndices: result.droppedIndices,
|
|
71
161
|
};
|
|
72
162
|
}
|
|
163
|
+
catch (e_1) {
|
|
164
|
+
env_1.error = e_1;
|
|
165
|
+
env_1.hasError = true;
|
|
166
|
+
}
|
|
73
167
|
finally {
|
|
74
|
-
|
|
168
|
+
const result_1 = __disposeResources(env_1);
|
|
169
|
+
if (result_1)
|
|
170
|
+
await result_1;
|
|
75
171
|
}
|
|
76
172
|
}
|
|
77
173
|
case "saveEmbeddings": {
|
|
78
|
-
const
|
|
79
|
-
const handlerOptions = createHandlerOptions("saveEmbeddings", workspace, onProgress);
|
|
80
|
-
const params = omitOnProgress(request);
|
|
174
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
81
175
|
try {
|
|
176
|
+
const workspace = request.workspace ?? DEFAULT_WORKSPACE;
|
|
177
|
+
const requestId = request.requestId ?? generateServerRequestId();
|
|
178
|
+
const ctx = __addDisposableResource(env_2, beginRagContext(workspace, requestId), true);
|
|
179
|
+
const log = withRequestContext(getServerLogger(), ctx);
|
|
180
|
+
log.debug("saveEmbeddings start");
|
|
181
|
+
const handlerOptions = createHandlerOptions("saveEmbeddings", workspace, ctx.signal, onProgress);
|
|
182
|
+
const params = omitOnProgress(request);
|
|
82
183
|
const processed = await saveEmbeddings(params, handlerOptions);
|
|
83
184
|
return {
|
|
84
185
|
type: "rag",
|
|
@@ -87,8 +188,14 @@ async function handleRagInternal(request, onProgress) {
|
|
|
87
188
|
processed,
|
|
88
189
|
};
|
|
89
190
|
}
|
|
191
|
+
catch (e_2) {
|
|
192
|
+
env_2.error = e_2;
|
|
193
|
+
env_2.hasError = true;
|
|
194
|
+
}
|
|
90
195
|
finally {
|
|
91
|
-
|
|
196
|
+
const result_2 = __disposeResources(env_2);
|
|
197
|
+
if (result_2)
|
|
198
|
+
await result_2;
|
|
92
199
|
}
|
|
93
200
|
}
|
|
94
201
|
case "search": {
|
|
@@ -109,10 +216,15 @@ async function handleRagInternal(request, onProgress) {
|
|
|
109
216
|
};
|
|
110
217
|
}
|
|
111
218
|
case "reindex": {
|
|
112
|
-
const
|
|
113
|
-
const handlerOptions = createHandlerOptions("reindex", workspace, onProgress);
|
|
114
|
-
const params = omitOnProgress(request);
|
|
219
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
115
220
|
try {
|
|
221
|
+
const workspace = request.workspace ?? DEFAULT_WORKSPACE;
|
|
222
|
+
const requestId = request.requestId ?? generateServerRequestId();
|
|
223
|
+
const ctx = __addDisposableResource(env_3, beginRagContext(workspace, requestId), true);
|
|
224
|
+
const log = withRequestContext(getServerLogger(), ctx);
|
|
225
|
+
log.debug("reindex start");
|
|
226
|
+
const handlerOptions = createHandlerOptions("reindex", workspace, ctx.signal, onProgress);
|
|
227
|
+
const params = omitOnProgress(request);
|
|
116
228
|
const result = await reindex(params, handlerOptions);
|
|
117
229
|
return {
|
|
118
230
|
type: "rag",
|
|
@@ -121,8 +233,14 @@ async function handleRagInternal(request, onProgress) {
|
|
|
121
233
|
result,
|
|
122
234
|
};
|
|
123
235
|
}
|
|
236
|
+
catch (e_3) {
|
|
237
|
+
env_3.error = e_3;
|
|
238
|
+
env_3.hasError = true;
|
|
239
|
+
}
|
|
124
240
|
finally {
|
|
125
|
-
|
|
241
|
+
const result_3 = __disposeResources(env_3);
|
|
242
|
+
if (result_3)
|
|
243
|
+
await result_3;
|
|
126
244
|
}
|
|
127
245
|
}
|
|
128
246
|
case "listWorkspaces": {
|
|
@@ -34,6 +34,14 @@ const DIALECT_SPECS = {
|
|
|
34
34
|
"<|return|>",
|
|
35
35
|
],
|
|
36
36
|
},
|
|
37
|
+
qwen35: {
|
|
38
|
+
// Same <tool_call>…</tool_call> framing as hermes; inner content is XML.
|
|
39
|
+
toolFrames: [{ open: "<tool_call>", close: "</tool_call>" }],
|
|
40
|
+
},
|
|
41
|
+
gemma4: {
|
|
42
|
+
toolFrames: [{ open: "<|tool_call>", close: "<tool_call|>" }],
|
|
43
|
+
thinkingFrames: [{ open: "<|channel>thought", close: "<channel|>" }],
|
|
44
|
+
},
|
|
37
45
|
};
|
|
38
46
|
// Capture-gated reasoning marker — the generic `<think>...</think>`
|
|
39
47
|
// convention used by reasoning-tuned models. When the gate is off the
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
export function detectToolDialectFromName(name, path) {
|
|
7
7
|
const basename = path.toLowerCase().split(/[/\\]/).pop() ?? "";
|
|
8
8
|
const tag = `${(name ?? "").toLowerCase()}|${basename}`;
|
|
9
|
+
if (/qwen3[._-]?[56](?![a-z0-9])/.test(tag))
|
|
10
|
+
return "qwen35";
|
|
11
|
+
if (/gemma[-_]?4(?=[^a-z0-9]|$)/.test(tag))
|
|
12
|
+
return "gemma4";
|
|
9
13
|
if (/gpt[_-]?oss/.test(tag))
|
|
10
14
|
return "harmony";
|
|
11
15
|
if (/lfm[_-]?\d/.test(tag))
|
|
@@ -3,6 +3,8 @@ import { parseGemmaFormat, parseGenericFormat, parseLlamacppFormat, } from "./pa
|
|
|
3
3
|
import { parseHermesFormat } from "./parsers/hermes.js";
|
|
4
4
|
import { parsePythonicFormat } from "./parsers/pythonic.js";
|
|
5
5
|
import { parseHarmonyFormat } from "./parsers/harmony.js";
|
|
6
|
+
import { parseQwen35Format } from "./parsers/qwen35.js";
|
|
7
|
+
import { parseGemma4NativeFormat } from "./parsers/gemma4native.js";
|
|
6
8
|
function pickFormatParsers(dialect) {
|
|
7
9
|
switch (dialect) {
|
|
8
10
|
case "pythonic":
|
|
@@ -15,13 +17,26 @@ function pickFormatParsers(dialect) {
|
|
|
15
17
|
return [parseGemmaFormat, parseLlamacppFormat];
|
|
16
18
|
case "harmony":
|
|
17
19
|
return [parseHarmonyFormat];
|
|
20
|
+
case "qwen35":
|
|
21
|
+
// Hermes fallback: Qwen3.5 templates sometimes emit OpenAI-style JSON
|
|
22
|
+
// when the native XML format fails; Hermes chain recovers those.
|
|
23
|
+
return [parseQwen35Format, parseHermesFormat];
|
|
24
|
+
case "gemma4":
|
|
25
|
+
// No JSON fallback: Gemma4 emits only its native channel-thought dialect
|
|
26
|
+
// and never falls back to JSON-envelope formats.
|
|
27
|
+
return [parseGemma4NativeFormat];
|
|
18
28
|
default:
|
|
19
|
-
//
|
|
29
|
+
// Gemma4 first: `<|tool_call>` is uniquely distinctive and can't
|
|
20
30
|
// false-match other dialects.
|
|
31
|
+
// Harmony next: `to=functions.` is also uniquely Harmony.
|
|
32
|
+
// Qwen35 before Hermes: defers to Hermes when JSON is inside <tool_call>,
|
|
33
|
+
// so the XML path is recovered without breaking Hermes-JSON payloads.
|
|
21
34
|
// Pythonic last: its bare `[name(...)]` form can match payloads that
|
|
22
35
|
// look like other dialects.
|
|
23
36
|
return [
|
|
37
|
+
parseGemma4NativeFormat,
|
|
24
38
|
parseHarmonyFormat,
|
|
39
|
+
parseQwen35Format,
|
|
25
40
|
parseHermesFormat,
|
|
26
41
|
parseGemmaFormat,
|
|
27
42
|
parseLlamacppFormat,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { generateStableToolCallId, validateToolArguments, } from "../shared.js";
|
|
2
|
+
// Transliterates Gemma 4's JS-literal argument body to valid JSON so it can
|
|
3
|
+
// be parsed with JSON.parse. The body uses:
|
|
4
|
+
// - <|"|>...<|"|> instead of "..." for string values
|
|
5
|
+
// - bare (unquoted) object keys
|
|
6
|
+
// Strategy: split by <|"|> tokens so structural parts (even indices) and
|
|
7
|
+
// string value parts (odd indices) are processed separately, preventing
|
|
8
|
+
// the key-quoting regex from matching `, key:` patterns inside string values.
|
|
9
|
+
function gemmaArgsToJson(argsRaw) {
|
|
10
|
+
const parts = ("{" + argsRaw + "}").split(/<\|"\|>/);
|
|
11
|
+
return parts
|
|
12
|
+
.map((part, i) => i % 2 === 0
|
|
13
|
+
? part.replace(/([{,]\s*)([A-Za-z_][\w-]*)\s*:/g, '$1"$2":')
|
|
14
|
+
: '"' + part.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/[\x00-\x1f]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`) + '"')
|
|
15
|
+
.join("");
|
|
16
|
+
}
|
|
17
|
+
// Parses Gemma 4's native tool-call dialect:
|
|
18
|
+
// <|tool_call>call:NAME{key:<|"|>val<|"|>,key2:bareNum,...}<tool_call|>
|
|
19
|
+
// Keys are bare identifiers; string values are wrapped in <|"|>...<|"|>;
|
|
20
|
+
// numbers/booleans are bare literals; nested objects/arrays use JS-literal
|
|
21
|
+
// syntax throughout. Transliterates to JSON then parses.
|
|
22
|
+
export function parseGemma4NativeFormat(text, tools) {
|
|
23
|
+
const toolCalls = [];
|
|
24
|
+
const errors = [];
|
|
25
|
+
if (!text.includes("<|tool_call>")) {
|
|
26
|
+
return { matched: false, toolCalls, errors };
|
|
27
|
+
}
|
|
28
|
+
const callRegex = /<\|tool_call>call:([A-Za-z_][\w-]*)\{([\s\S]*?)\}<tool_call\|>/g;
|
|
29
|
+
const matches = Array.from(text.matchAll(callRegex));
|
|
30
|
+
if (matches.length === 0)
|
|
31
|
+
return { matched: false, toolCalls, errors };
|
|
32
|
+
for (const match of matches) {
|
|
33
|
+
const name = match[1];
|
|
34
|
+
const argsRaw = match[2];
|
|
35
|
+
let args;
|
|
36
|
+
try {
|
|
37
|
+
args = JSON.parse(gemmaArgsToJson(argsRaw));
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
errors.push({
|
|
41
|
+
code: "PARSE_ERROR",
|
|
42
|
+
message: `Failed to parse Gemma 4 tool call arguments: ${err instanceof Error ? err.message : String(err)}`,
|
|
43
|
+
raw: match[0],
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const validation = validateToolArguments(name, args, tools);
|
|
48
|
+
if (!validation.isValid && validation.error) {
|
|
49
|
+
errors.push({ ...validation.error, raw: match[0] });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
toolCalls.push({
|
|
53
|
+
id: generateStableToolCallId(name, args),
|
|
54
|
+
name,
|
|
55
|
+
arguments: args,
|
|
56
|
+
raw: match[0],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return { matched: true, toolCalls, errors };
|
|
60
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { generateStableToolCallId, validateToolArguments, } from "../shared.js";
|
|
2
|
+
// Coerce raw parameter text to the type declared in the tool's JSON schema.
|
|
3
|
+
// String values are raw (not JSON-quoted); arrays/objects are valid JSON.
|
|
4
|
+
function coerceParamValue(raw, schema) {
|
|
5
|
+
const trimmed = raw.trim();
|
|
6
|
+
if (!schema?.type)
|
|
7
|
+
return trimmed;
|
|
8
|
+
switch (schema.type) {
|
|
9
|
+
case "number": {
|
|
10
|
+
if (trimmed.length === 0)
|
|
11
|
+
throw new Error(`invalid numeric value: ""`);
|
|
12
|
+
const n = Number(trimmed);
|
|
13
|
+
if (Number.isNaN(n))
|
|
14
|
+
throw new Error(`invalid numeric value: "${trimmed}"`);
|
|
15
|
+
return n;
|
|
16
|
+
}
|
|
17
|
+
case "integer": {
|
|
18
|
+
if (trimmed.length === 0)
|
|
19
|
+
throw new Error(`invalid integer value: ""`);
|
|
20
|
+
const n = Number(trimmed);
|
|
21
|
+
if (Number.isNaN(n) || !Number.isInteger(n))
|
|
22
|
+
throw new Error(`invalid integer value: "${trimmed}"`);
|
|
23
|
+
return n;
|
|
24
|
+
}
|
|
25
|
+
case "boolean":
|
|
26
|
+
if (trimmed === "true")
|
|
27
|
+
return true;
|
|
28
|
+
if (trimmed === "false")
|
|
29
|
+
return false;
|
|
30
|
+
throw new Error(`invalid boolean value: "${trimmed}"`);
|
|
31
|
+
case "array":
|
|
32
|
+
case "object":
|
|
33
|
+
return JSON.parse(trimmed);
|
|
34
|
+
default:
|
|
35
|
+
return trimmed;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Parses Qwen3.5/3.6 Pythonic-XML tool-call format:
|
|
39
|
+
// <tool_call>
|
|
40
|
+
// <function=NAME>
|
|
41
|
+
// <parameter=KEY>VALUE</parameter>
|
|
42
|
+
// </function>
|
|
43
|
+
// </tool_call>
|
|
44
|
+
// String parameter values are raw text (not JSON-quoted); arrays/objects
|
|
45
|
+
// are JSON. Type coercion uses the tool schema; unknown params pass through.
|
|
46
|
+
export function parseQwen35Format(text, tools) {
|
|
47
|
+
const toolCalls = [];
|
|
48
|
+
const errors = [];
|
|
49
|
+
if (!text.includes("<tool_call>")) {
|
|
50
|
+
return { matched: false, toolCalls, errors };
|
|
51
|
+
}
|
|
52
|
+
const outerRegex = /<tool_call>([\s\S]*?)<\/tool_call>/g;
|
|
53
|
+
const outerMatches = Array.from(text.matchAll(outerRegex));
|
|
54
|
+
if (outerMatches.length === 0)
|
|
55
|
+
return { matched: false, toolCalls, errors };
|
|
56
|
+
// If no match contains XML function syntax, check if this is JSON format
|
|
57
|
+
// (defer to hermes) or just malformed content (surface as PARSE_ERROR).
|
|
58
|
+
if (!outerMatches.some((m) => m[1].includes("<function="))) {
|
|
59
|
+
const looksLikeJson = outerMatches.some((m) => {
|
|
60
|
+
const inner = m[1].trim();
|
|
61
|
+
return inner.startsWith("{") || inner.startsWith("[");
|
|
62
|
+
});
|
|
63
|
+
if (looksLikeJson)
|
|
64
|
+
return { matched: false, toolCalls, errors };
|
|
65
|
+
return {
|
|
66
|
+
matched: true,
|
|
67
|
+
toolCalls,
|
|
68
|
+
errors: outerMatches.map((m) => ({
|
|
69
|
+
code: "PARSE_ERROR",
|
|
70
|
+
message: "Qwen3.5 tool call missing <function=NAME>...</function>",
|
|
71
|
+
raw: m[1].trim(),
|
|
72
|
+
})),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
for (const outerMatch of outerMatches) {
|
|
76
|
+
const inner = outerMatch[1].trim();
|
|
77
|
+
const fnMatch = /<function=([^>\s]+)\s*>([\s\S]*?)<\/function>/i.exec(inner);
|
|
78
|
+
if (!fnMatch) {
|
|
79
|
+
errors.push({
|
|
80
|
+
code: "PARSE_ERROR",
|
|
81
|
+
message: "Qwen3.5 tool call missing <function=NAME>...</function>",
|
|
82
|
+
raw: inner,
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const name = fnMatch[1].trim();
|
|
87
|
+
const paramsBlock = fnMatch[2];
|
|
88
|
+
const tool = tools.find((t) => t.name === name);
|
|
89
|
+
const schemaProperties = tool?.parameters?.properties ?? {};
|
|
90
|
+
const args = {};
|
|
91
|
+
let parseError;
|
|
92
|
+
try {
|
|
93
|
+
const paramRegex = /<parameter=([^>\s]+)\s*>([\s\S]*?)<\/parameter>/gi;
|
|
94
|
+
let pm;
|
|
95
|
+
while ((pm = paramRegex.exec(paramsBlock)) !== null) {
|
|
96
|
+
const paramName = pm[1].trim();
|
|
97
|
+
args[paramName] = coerceParamValue(pm[2], schemaProperties[paramName]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
parseError = err instanceof Error ? err.message : String(err);
|
|
102
|
+
}
|
|
103
|
+
if (parseError !== undefined) {
|
|
104
|
+
errors.push({ code: "PARSE_ERROR", message: parseError, raw: inner });
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const validation = validateToolArguments(name, args, tools);
|
|
108
|
+
if (!validation.isValid && validation.error) {
|
|
109
|
+
errors.push({ ...validation.error, raw: inner });
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
toolCalls.push({
|
|
113
|
+
id: generateStableToolCallId(name, args),
|
|
114
|
+
name,
|
|
115
|
+
arguments: args,
|
|
116
|
+
raw: inner,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { matched: true, toolCalls, errors };
|
|
120
|
+
}
|
|
@@ -7,10 +7,19 @@ export type AggregatedEvents = {
|
|
|
7
7
|
toolCalls: ToolCall[];
|
|
8
8
|
rawFullText: string | undefined;
|
|
9
9
|
error: CompletionError | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* True when the terminal `completionDone` carried
|
|
12
|
+
* `stopReason: "cancelled"`. The client wrapper rejects the
|
|
13
|
+
* promise-aggregates (`final` / `text` / `toolCalls` / `stats`) with
|
|
14
|
+
* `InferenceCancelledError` carrying the partial state when this is
|
|
15
|
+
* set; the `events` stream itself still ends normally.
|
|
16
|
+
*/
|
|
17
|
+
cancelled: boolean;
|
|
10
18
|
};
|
|
11
19
|
export declare function aggregateEvents(events: CompletionEvent[]): AggregatedEvents;
|
|
12
20
|
export declare function buildFinalFromEvents(events: CompletionEvent[], handlers: ToolHandlerMap): {
|
|
13
21
|
final: CompletionFinal;
|
|
14
22
|
error: CompletionError | undefined;
|
|
23
|
+
cancelled: boolean;
|
|
15
24
|
};
|
|
16
25
|
//# sourceMappingURL=aggregate-events.d.ts.map
|
|
@@ -6,6 +6,7 @@ export function aggregateEvents(events) {
|
|
|
6
6
|
let stats;
|
|
7
7
|
let rawFullText;
|
|
8
8
|
let error;
|
|
9
|
+
let cancelled = false;
|
|
9
10
|
const toolCalls = [];
|
|
10
11
|
for (const event of events) {
|
|
11
12
|
if (event.type === "contentDelta") {
|
|
@@ -24,15 +25,29 @@ export function aggregateEvents(events) {
|
|
|
24
25
|
if ("raw" in event && event.raw) {
|
|
25
26
|
rawFullText = event.raw.fullText;
|
|
26
27
|
}
|
|
28
|
+
// Error wins over cancelled if a wire event ever carries both
|
|
29
|
+
// signals: a mid-stream addon failure makes the partial state
|
|
30
|
+
// unsafe to expose, regardless of why the loop exited.
|
|
27
31
|
if (event.stopReason === "error" && "error" in event) {
|
|
28
32
|
error = event.error;
|
|
29
33
|
}
|
|
34
|
+
else if (event.stopReason === "cancelled") {
|
|
35
|
+
cancelled = true;
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
|
-
return {
|
|
39
|
+
return {
|
|
40
|
+
contentText,
|
|
41
|
+
thinkingText,
|
|
42
|
+
stats,
|
|
43
|
+
toolCalls,
|
|
44
|
+
rawFullText,
|
|
45
|
+
error,
|
|
46
|
+
cancelled,
|
|
47
|
+
};
|
|
33
48
|
}
|
|
34
49
|
export function buildFinalFromEvents(events, handlers) {
|
|
35
|
-
const { contentText, thinkingText, stats, toolCalls, rawFullText, error } = aggregateEvents(events);
|
|
50
|
+
const { contentText, thinkingText, stats, toolCalls, rawFullText, error, cancelled, } = aggregateEvents(events);
|
|
36
51
|
const attachedToolCalls = attachHandlersToToolCalls(toolCalls, handlers);
|
|
37
52
|
const fullText = rawFullText ?? contentText;
|
|
38
53
|
const cacheableAssistantContent = attachedToolCalls.length === 0
|
|
@@ -48,5 +63,5 @@ export function buildFinalFromEvents(events, handlers) {
|
|
|
48
63
|
cacheableAssistantContent,
|
|
49
64
|
}),
|
|
50
65
|
};
|
|
51
|
-
return { final, error };
|
|
66
|
+
return { final, error, cancelled };
|
|
52
67
|
}
|