@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
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { type AbortSignal } from "bare-abort-controller";
|
|
2
|
+
import type { RequestContext, RequestKind } from "./request-context";
|
|
3
|
+
import type { Logger } from "../../../logging/types";
|
|
4
|
+
/**
|
|
5
|
+
* Outcome the caller declares when terminating a request through
|
|
6
|
+
* `registry.end(...)`. The registry maps it to a terminal `RequestState`
|
|
7
|
+
* before disposing the scope so observers see a coherent final state.
|
|
8
|
+
*/
|
|
9
|
+
export type RequestOutcome = "completed" | "failed" | "cancelled";
|
|
10
|
+
export interface BeginOpts {
|
|
11
|
+
/** Stable identity. Caller-provided so the client and server agree. */
|
|
12
|
+
requestId: string;
|
|
13
|
+
kind: RequestKind;
|
|
14
|
+
modelId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional parent abort signal — typically the worker-level "shutdown"
|
|
17
|
+
* signal. When the parent aborts, the request's own signal aborts too.
|
|
18
|
+
* Composes through a `addEventListener("abort", ...)` hook so cancelling
|
|
19
|
+
* the parent does not require iterating the registry.
|
|
20
|
+
*/
|
|
21
|
+
parentSignal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
export interface CancelByRequestId {
|
|
24
|
+
requestId: string;
|
|
25
|
+
reason?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CancelByModelId {
|
|
28
|
+
modelId: string;
|
|
29
|
+
kind?: RequestKind;
|
|
30
|
+
reason?: string;
|
|
31
|
+
}
|
|
32
|
+
export type CancelTarget = CancelByRequestId | CancelByModelId;
|
|
33
|
+
/**
|
|
34
|
+
* Per-kind admission rule. Kinds without a registered policy have no
|
|
35
|
+
* admission control (every `begin(...)` is accepted as long as the
|
|
36
|
+
* request id is unique).
|
|
37
|
+
*/
|
|
38
|
+
export interface ConcurrencyPolicy {
|
|
39
|
+
kind: RequestKind;
|
|
40
|
+
/**
|
|
41
|
+
* When true, at most one in-flight request per `(kind, modelId)`
|
|
42
|
+
* tuple — a second `begin(...)` rejects with
|
|
43
|
+
* `RequestRejectedByPolicyError`. Requests without a `modelId` are
|
|
44
|
+
* unaffected.
|
|
45
|
+
*/
|
|
46
|
+
oneAtATimePerModel: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `ManagedRequestContext` is the value `begin(...)` returns. It extends
|
|
50
|
+
* `RequestContext` with an async-dispose method so handlers can write:
|
|
51
|
+
*
|
|
52
|
+
* await using ctx = registry.begin({ ... });
|
|
53
|
+
*
|
|
54
|
+
* On dispose the scope unwinds (LIFO cleanup) and the registry slot is
|
|
55
|
+
* freed. If the handler doesn't override `ctx.state` before unwinding,
|
|
56
|
+
* the registry derives the terminal state from `signal.aborted` —
|
|
57
|
+
* `"cancelled"` when an abort was recorded, `"completed"` otherwise.
|
|
58
|
+
*/
|
|
59
|
+
export interface ManagedRequestContext extends RequestContext {
|
|
60
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
export interface RequestRegistry {
|
|
63
|
+
/**
|
|
64
|
+
* Open a new request. Throws:
|
|
65
|
+
* - `RequestIdConflictError` if `requestId` is already present
|
|
66
|
+
* (UUIDv4 collision is astronomically unlikely; the guard exists
|
|
67
|
+
* so a buggy client retry sending the same id can't silently
|
|
68
|
+
* overwrite an in-flight request).
|
|
69
|
+
* - `RequestRejectedByPolicyError` if a concurrency policy was
|
|
70
|
+
* registered for `opts.kind` and the new request would violate
|
|
71
|
+
* it (e.g. `oneAtATimePerModel` rejects when another request
|
|
72
|
+
* with the same `(kind, modelId)` pair is already in flight).
|
|
73
|
+
*/
|
|
74
|
+
begin(opts: BeginOpts): ManagedRequestContext;
|
|
75
|
+
/**
|
|
76
|
+
* Register or replace the concurrency policy for a `RequestKind`.
|
|
77
|
+
* Subsequent `begin(...)` calls for that kind run the policy before
|
|
78
|
+
* allocating a controller / scope. One policy per kind — calling
|
|
79
|
+
* twice replaces the previous declaration.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* r.policy({ kind: "completion", oneAtATimePerModel: true });
|
|
83
|
+
* await using a = r.begin({ requestId: "r-1", kind: "completion", modelId: "m1" });
|
|
84
|
+
* r.begin({ requestId: "r-2", kind: "completion", modelId: "m1" });
|
|
85
|
+
* // → throws RequestRejectedByPolicyError (code 52420)
|
|
86
|
+
*/
|
|
87
|
+
policy(opts: ConcurrencyPolicy): void;
|
|
88
|
+
/** Look up an in-flight request by id. */
|
|
89
|
+
get(requestId: string): RequestContext | null;
|
|
90
|
+
/**
|
|
91
|
+
* Snapshot of currently-tracked requests. Useful for diagnostics /
|
|
92
|
+
* structured logs ("which requests are in flight right now?"). Returns
|
|
93
|
+
* a fresh array; mutations on it are not observed by the registry.
|
|
94
|
+
*/
|
|
95
|
+
list(): RequestContext[];
|
|
96
|
+
/**
|
|
97
|
+
* Cancel matching requests. Returns the number of contexts whose abort
|
|
98
|
+
* was triggered by *this* call (already-cancelled contexts are skipped
|
|
99
|
+
* so callers can rely on the count to log "n requests cancelled" once).
|
|
100
|
+
*
|
|
101
|
+
* For `{ modelId }` and an optional `kind`, cancels every active
|
|
102
|
+
* request that matches the predicate. This is the broad-cancel path
|
|
103
|
+
* the pre-registry `cancel({ modelId })` API maps to.
|
|
104
|
+
*/
|
|
105
|
+
cancel(target: CancelTarget): number;
|
|
106
|
+
/**
|
|
107
|
+
* Cancel every active request — the worker-shutdown / model-unload
|
|
108
|
+
* sweep. The reason is forwarded to each request as the abort reason
|
|
109
|
+
* so handler logs can distinguish a normal cancel from a sweep.
|
|
110
|
+
* Resolves once all targeted contexts have flipped to `"cancelling"`;
|
|
111
|
+
* scope unwinding still happens on each handler's own dispose path.
|
|
112
|
+
*/
|
|
113
|
+
cancelAll(reason: "shutdown" | "modelUnload"): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Mark a request finished and dispose its scope. Equivalent to
|
|
116
|
+
* `await ctx[Symbol.asyncDispose]()` with an explicit outcome.
|
|
117
|
+
* Idempotent — calling `end` after a scope dispose is a no-op.
|
|
118
|
+
*/
|
|
119
|
+
end(requestId: string, outcome: RequestOutcome): Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
export declare function createRequestRegistry(options?: {
|
|
122
|
+
/** Defaults to `getServerLogger()`. Tests inject a stub. */
|
|
123
|
+
logger?: Logger;
|
|
124
|
+
}): RequestRegistry;
|
|
125
|
+
/**
|
|
126
|
+
* Test-only knobs exported for `request-registry.test.ts` so the bound
|
|
127
|
+
* assertions can pin the documented limits without re-reading them via
|
|
128
|
+
* fragile string comparison. **Not part of the public SDK surface.**
|
|
129
|
+
*
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
export declare const __requestRegistryTestHooks: {
|
|
133
|
+
cancelBeforeBeginMaxEntries: number;
|
|
134
|
+
cancelBeforeBeginTtlMs: number;
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=request-registry.d.ts.map
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { AbortController } from "bare-abort-controller";
|
|
2
|
+
import { createDisposableScope, } from "./disposable-scope.js";
|
|
3
|
+
import { RequestIdConflictError, RequestRejectedByPolicyError, } from "../../../utils/errors-server.js";
|
|
4
|
+
import { getServerLogger } from "../../../logging/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* Tuning knobs for the "cancelled-before-begin" bookkeeping set.
|
|
7
|
+
*
|
|
8
|
+
* The race window is bounded by the client-to-server round-trip: a
|
|
9
|
+
* `cancel({ requestId })` issued by the client at the same time as the
|
|
10
|
+
* matching `completion(...)` either lands first (and we need to remember
|
|
11
|
+
* it long enough for the server's `begin(...)` to follow) or lands
|
|
12
|
+
* second (and we never touch this set). 30 seconds is overkill for a
|
|
13
|
+
* 500ms round-trip but gives slow networks / pause-the-debugger
|
|
14
|
+
* scenarios enough slack while still bounding worst-case retention.
|
|
15
|
+
*
|
|
16
|
+
* The size cap protects against a buggy or malicious client firing a
|
|
17
|
+
* stream of cancels for ids that never get a `begin(...)` follow-up —
|
|
18
|
+
* each `cancel({ requestId })` that doesn't match an in-flight context
|
|
19
|
+
* inserts one entry, so without a cap the worker would grow the map
|
|
20
|
+
* unbounded. At the cap, the oldest entry is evicted.
|
|
21
|
+
*
|
|
22
|
+
* Tweak with care: both bounds appear in the registry race test
|
|
23
|
+
* (`bounded cancel-before-begin set does not grow past its cap`).
|
|
24
|
+
*/
|
|
25
|
+
const CANCEL_BEFORE_BEGIN_MAX_ENTRIES = 128;
|
|
26
|
+
const CANCEL_BEFORE_BEGIN_TTL_MS = 30_000;
|
|
27
|
+
export function createRequestRegistry(options) {
|
|
28
|
+
const entries = new Map();
|
|
29
|
+
const policies = new Map();
|
|
30
|
+
const logger = options?.logger ?? getServerLogger();
|
|
31
|
+
function logLifecycle(event, ctx, durationMs) {
|
|
32
|
+
const modelId = ctx.modelId !== undefined ? ctx.modelId : "-";
|
|
33
|
+
const base = `[request-lifecycle] ${event} requestId=${ctx.requestId} kind=${ctx.kind} modelId=${modelId} state=${ctx.state}`;
|
|
34
|
+
const line = durationMs !== undefined ? `${base} durationMs=${durationMs}` : base;
|
|
35
|
+
// `failed` end emits at `warn` so log shippers can alert on
|
|
36
|
+
// `level>=warn` for this prefix without parsing `state=failed`
|
|
37
|
+
// out of the message body. Everything else stays at `info`.
|
|
38
|
+
if (event === "end" && ctx.state === "failed") {
|
|
39
|
+
logger.warn(line);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
logger.info(line);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* "Cancelled-before-begin" tripwire. A
|
|
47
|
+
* `cancel({ requestId })` whose target isn't yet in `entries` records
|
|
48
|
+
* the id here; the subsequent `begin({ requestId: <same id> })` then
|
|
49
|
+
* aborts the new controller before returning. Map order is insertion
|
|
50
|
+
* order — the iterator's first key is the oldest entry, which makes
|
|
51
|
+
* the size-cap eviction free.
|
|
52
|
+
*
|
|
53
|
+
* Invariants:
|
|
54
|
+
* - Every read path (`begin`, `cancel`-by-id) calls
|
|
55
|
+
* `pruneCancelBeforeBeginExpired()` first so a 30s+ stale entry
|
|
56
|
+
* never decides a fresh `begin(...)`.
|
|
57
|
+
* - Insertion enforces the size cap by evicting the oldest entry
|
|
58
|
+
* when at capacity — a malicious client cannot grow this map
|
|
59
|
+
* unbounded.
|
|
60
|
+
* - On a successful `begin(...)` match, the entry is consumed
|
|
61
|
+
* (removed) so a second `begin(...)` with the same id (which
|
|
62
|
+
* would itself throw `RequestIdConflictError`) doesn't see a
|
|
63
|
+
* phantom pre-cancel.
|
|
64
|
+
*/
|
|
65
|
+
const cancelledBeforeBegin = new Map();
|
|
66
|
+
function pruneCancelBeforeBeginExpired(now = Date.now()) {
|
|
67
|
+
if (cancelledBeforeBegin.size === 0)
|
|
68
|
+
return;
|
|
69
|
+
const cutoff = now - CANCEL_BEFORE_BEGIN_TTL_MS;
|
|
70
|
+
for (const [id, entry] of cancelledBeforeBegin) {
|
|
71
|
+
if (entry.at > cutoff)
|
|
72
|
+
break; // Insertion order ⇒ rest are newer.
|
|
73
|
+
cancelledBeforeBegin.delete(id);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function recordCancelBeforeBegin(requestId, reason) {
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
pruneCancelBeforeBeginExpired(now);
|
|
79
|
+
// Re-canceling an id that is already tracked refreshes its TTL but
|
|
80
|
+
// keeps the original reason — the first cancel "won" the race.
|
|
81
|
+
if (cancelledBeforeBegin.has(requestId)) {
|
|
82
|
+
const existing = cancelledBeforeBegin.get(requestId);
|
|
83
|
+
cancelledBeforeBegin.delete(requestId);
|
|
84
|
+
cancelledBeforeBegin.set(requestId, { ...existing, at: now });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (cancelledBeforeBegin.size >= CANCEL_BEFORE_BEGIN_MAX_ENTRIES) {
|
|
88
|
+
const oldest = cancelledBeforeBegin.keys().next().value;
|
|
89
|
+
if (oldest !== undefined)
|
|
90
|
+
cancelledBeforeBegin.delete(oldest);
|
|
91
|
+
}
|
|
92
|
+
cancelledBeforeBegin.set(requestId, reason !== undefined ? { at: now, reason } : { at: now });
|
|
93
|
+
}
|
|
94
|
+
function consumeCancelBeforeBegin(requestId) {
|
|
95
|
+
pruneCancelBeforeBeginExpired();
|
|
96
|
+
const entry = cancelledBeforeBegin.get(requestId);
|
|
97
|
+
if (!entry)
|
|
98
|
+
return undefined;
|
|
99
|
+
cancelledBeforeBegin.delete(requestId);
|
|
100
|
+
return entry;
|
|
101
|
+
}
|
|
102
|
+
function cancelEntry(entry, reason) {
|
|
103
|
+
if (entry.controller.signal.aborted)
|
|
104
|
+
return false;
|
|
105
|
+
entry.ctx.state = "cancelling";
|
|
106
|
+
entry.controller.abort(reason);
|
|
107
|
+
logLifecycle("cancel", entry.ctx);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
async function disposeEntry(entry, outcome) {
|
|
111
|
+
if (entry.scope.disposed)
|
|
112
|
+
return;
|
|
113
|
+
entry.ctx.state = outcome;
|
|
114
|
+
entry.detachParent();
|
|
115
|
+
logLifecycle("end", entry.ctx, Date.now() - entry.startedAt);
|
|
116
|
+
// Pull the entry out before unwinding so observers (e.g. a `cancel(...)`
|
|
117
|
+
// racing with dispose) don't see a half-disposed context.
|
|
118
|
+
entries.delete(entry.ctx.requestId);
|
|
119
|
+
await entry.scope[Symbol.asyncDispose]();
|
|
120
|
+
}
|
|
121
|
+
function begin(opts) {
|
|
122
|
+
if (entries.has(opts.requestId)) {
|
|
123
|
+
throw new RequestIdConflictError(opts.requestId);
|
|
124
|
+
}
|
|
125
|
+
// Admission control runs before allocation so a rejected begin
|
|
126
|
+
// leaves no controller / scope behind.
|
|
127
|
+
const policy = policies.get(opts.kind);
|
|
128
|
+
if (policy && policy.oneAtATimePerModel && opts.modelId !== undefined) {
|
|
129
|
+
for (const existing of entries.values()) {
|
|
130
|
+
if (existing.ctx.kind !== opts.kind)
|
|
131
|
+
continue;
|
|
132
|
+
if (existing.ctx.modelId !== opts.modelId)
|
|
133
|
+
continue;
|
|
134
|
+
throw new RequestRejectedByPolicyError(opts.requestId, opts.kind, opts.modelId, `another ${opts.kind} request (${existing.ctx.requestId}) is already running on this model`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const controller = new AbortController();
|
|
138
|
+
const scope = createDisposableScope();
|
|
139
|
+
// Stop-button race close. If a
|
|
140
|
+
// `cancel({ requestId })` already arrived for this id, abort the
|
|
141
|
+
// new controller before observers can subscribe to it. The
|
|
142
|
+
// tripwire entry is consumed so a later, separate `begin(...)`
|
|
143
|
+
// with the same id is unaffected (in practice ids are UUIDv4 and
|
|
144
|
+
// never reused; this guard just keeps the contract self-
|
|
145
|
+
// consistent under retries).
|
|
146
|
+
const preCancel = consumeCancelBeforeBegin(opts.requestId);
|
|
147
|
+
if (preCancel) {
|
|
148
|
+
controller.abort(preCancel.reason);
|
|
149
|
+
}
|
|
150
|
+
let detachParent = () => { };
|
|
151
|
+
if (opts.parentSignal) {
|
|
152
|
+
const parent = opts.parentSignal;
|
|
153
|
+
if (parent.aborted) {
|
|
154
|
+
controller.abort(parent.reason);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
const onParentAbort = () => controller.abort(parent.reason);
|
|
158
|
+
parent.addEventListener("abort", onParentAbort, { once: true });
|
|
159
|
+
detachParent = () => parent.removeEventListener("abort", onParentAbort);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const ctx = {
|
|
163
|
+
requestId: opts.requestId,
|
|
164
|
+
kind: opts.kind,
|
|
165
|
+
modelId: opts.modelId,
|
|
166
|
+
signal: controller.signal,
|
|
167
|
+
scope,
|
|
168
|
+
// Land the context in `cancelling` from the outset whenever the
|
|
169
|
+
// controller was already aborted by `begin(...)` itself — either
|
|
170
|
+
// the Stop-button race (`preCancel`) or a `parentSignal` that was
|
|
171
|
+
// already aborted at begin time. Both branches abort the
|
|
172
|
+
// controller above, so without this guard observers would see a
|
|
173
|
+
// momentarily-`running` context with an already-aborted signal.
|
|
174
|
+
state: preCancel || opts.parentSignal?.aborted ? "cancelling" : "running",
|
|
175
|
+
};
|
|
176
|
+
const entry = {
|
|
177
|
+
ctx,
|
|
178
|
+
controller,
|
|
179
|
+
scope,
|
|
180
|
+
detachParent,
|
|
181
|
+
startedAt: Date.now(),
|
|
182
|
+
};
|
|
183
|
+
entries.set(opts.requestId, entry);
|
|
184
|
+
logLifecycle("begin", ctx);
|
|
185
|
+
return {
|
|
186
|
+
get requestId() {
|
|
187
|
+
return ctx.requestId;
|
|
188
|
+
},
|
|
189
|
+
get kind() {
|
|
190
|
+
return ctx.kind;
|
|
191
|
+
},
|
|
192
|
+
get modelId() {
|
|
193
|
+
return ctx.modelId;
|
|
194
|
+
},
|
|
195
|
+
get signal() {
|
|
196
|
+
return ctx.signal;
|
|
197
|
+
},
|
|
198
|
+
get scope() {
|
|
199
|
+
return ctx.scope;
|
|
200
|
+
},
|
|
201
|
+
get state() {
|
|
202
|
+
return ctx.state;
|
|
203
|
+
},
|
|
204
|
+
set state(next) {
|
|
205
|
+
ctx.state = next;
|
|
206
|
+
},
|
|
207
|
+
[Symbol.asyncDispose]: async () => {
|
|
208
|
+
await disposeEntry(entry, derivedTerminalState(ctx));
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function get(requestId) {
|
|
213
|
+
return entries.get(requestId)?.ctx ?? null;
|
|
214
|
+
}
|
|
215
|
+
function list() {
|
|
216
|
+
return Array.from(entries.values(), (e) => e.ctx);
|
|
217
|
+
}
|
|
218
|
+
function cancel(target) {
|
|
219
|
+
let cancelled = 0;
|
|
220
|
+
if ("requestId" in target) {
|
|
221
|
+
const entry = entries.get(target.requestId);
|
|
222
|
+
if (entry) {
|
|
223
|
+
if (cancelEntry(entry, target.reason))
|
|
224
|
+
cancelled++;
|
|
225
|
+
return cancelled;
|
|
226
|
+
}
|
|
227
|
+
// Stop-button race: the client beat its own
|
|
228
|
+
// `begin(...)`. Record the cancel so the next matching `begin`
|
|
229
|
+
// aborts immediately. The return value stays 0 — no in-flight
|
|
230
|
+
// request was matched, which is still the truth — but the
|
|
231
|
+
// *effective* cancel will land when the begin arrives.
|
|
232
|
+
recordCancelBeforeBegin(target.requestId, target.reason);
|
|
233
|
+
return cancelled;
|
|
234
|
+
}
|
|
235
|
+
for (const entry of entries.values()) {
|
|
236
|
+
if (entry.ctx.modelId !== target.modelId)
|
|
237
|
+
continue;
|
|
238
|
+
if (target.kind && entry.ctx.kind !== target.kind)
|
|
239
|
+
continue;
|
|
240
|
+
if (cancelEntry(entry, target.reason))
|
|
241
|
+
cancelled++;
|
|
242
|
+
}
|
|
243
|
+
return cancelled;
|
|
244
|
+
}
|
|
245
|
+
function cancelAll(reason) {
|
|
246
|
+
for (const entry of entries.values()) {
|
|
247
|
+
cancelEntry(entry, reason);
|
|
248
|
+
}
|
|
249
|
+
// The interface returns Promise<void> so we can later make this an
|
|
250
|
+
// async sweep that awaits per-handler scope unwinding (e.g. join on
|
|
251
|
+
// the disposers). Today every handler unwinds on its own dispose
|
|
252
|
+
// path, so the function only needs to fire-and-forget the abort.
|
|
253
|
+
return Promise.resolve();
|
|
254
|
+
}
|
|
255
|
+
async function end(requestId, outcome) {
|
|
256
|
+
const entry = entries.get(requestId);
|
|
257
|
+
if (!entry)
|
|
258
|
+
return;
|
|
259
|
+
await disposeEntry(entry, outcome);
|
|
260
|
+
}
|
|
261
|
+
function policy(opts) {
|
|
262
|
+
policies.set(opts.kind, opts);
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
begin,
|
|
266
|
+
get,
|
|
267
|
+
list,
|
|
268
|
+
cancel,
|
|
269
|
+
cancelAll,
|
|
270
|
+
end,
|
|
271
|
+
policy,
|
|
272
|
+
// Test-only: lets the registry race tests assert the bound
|
|
273
|
+
// invariants on the internal "cancelled-before-begin" set without
|
|
274
|
+
// exposing it as a public surface. Kept off the `RequestRegistry`
|
|
275
|
+
// interface (typed via the augmented return type below) so handler
|
|
276
|
+
// code can't depend on it accidentally.
|
|
277
|
+
__cancelBeforeBeginSize: () => cancelledBeforeBegin.size,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Test-only knobs exported for `request-registry.test.ts` so the bound
|
|
282
|
+
* assertions can pin the documented limits without re-reading them via
|
|
283
|
+
* fragile string comparison. **Not part of the public SDK surface.**
|
|
284
|
+
*
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
export const __requestRegistryTestHooks = {
|
|
288
|
+
cancelBeforeBeginMaxEntries: CANCEL_BEFORE_BEGIN_MAX_ENTRIES,
|
|
289
|
+
cancelBeforeBeginTtlMs: CANCEL_BEFORE_BEGIN_TTL_MS,
|
|
290
|
+
};
|
|
291
|
+
function derivedTerminalState(ctx) {
|
|
292
|
+
if (ctx.state === "failed")
|
|
293
|
+
return "failed";
|
|
294
|
+
if (ctx.signal.aborted || ctx.state === "cancelled")
|
|
295
|
+
return "cancelled";
|
|
296
|
+
return "completed";
|
|
297
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Logger } from "../../../logging/types";
|
|
2
|
+
export interface RequestLogContext {
|
|
3
|
+
requestId: string;
|
|
4
|
+
kind: string;
|
|
5
|
+
modelId: string | undefined;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Wraps `logger` so every emit is prefixed with
|
|
9
|
+
* `[request-lifecycle <kind> requestId=<id> modelId=<modelId>] `
|
|
10
|
+
* (the `modelId=...` segment is dropped when absent). The wrapper is a
|
|
11
|
+
* thin shim: `setLevel` / `getLevel` / `addTransport` / `setConsoleOutput`
|
|
12
|
+
* pass through to the underlying logger, and transport callbacks receive
|
|
13
|
+
* the prefixed message.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* await using ctx = registry.begin({ requestId, kind: "completion", modelId });
|
|
17
|
+
* const log = withRequestContext(getServerLogger(), ctx);
|
|
18
|
+
* log.info("decoding token 7");
|
|
19
|
+
* // → "[request-lifecycle completion requestId=<id> modelId=<id>] decoding token 7"
|
|
20
|
+
*/
|
|
21
|
+
export declare function withRequestContext(logger: Logger, ctx: RequestLogContext): Logger;
|
|
22
|
+
//# sourceMappingURL=with-request-context.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps `logger` so every emit is prefixed with
|
|
3
|
+
* `[request-lifecycle <kind> requestId=<id> modelId=<modelId>] `
|
|
4
|
+
* (the `modelId=...` segment is dropped when absent). The wrapper is a
|
|
5
|
+
* thin shim: `setLevel` / `getLevel` / `addTransport` / `setConsoleOutput`
|
|
6
|
+
* pass through to the underlying logger, and transport callbacks receive
|
|
7
|
+
* the prefixed message.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* await using ctx = registry.begin({ requestId, kind: "completion", modelId });
|
|
11
|
+
* const log = withRequestContext(getServerLogger(), ctx);
|
|
12
|
+
* log.info("decoding token 7");
|
|
13
|
+
* // → "[request-lifecycle completion requestId=<id> modelId=<id>] decoding token 7"
|
|
14
|
+
*/
|
|
15
|
+
export function withRequestContext(logger, ctx) {
|
|
16
|
+
const prefix = ctx.modelId !== undefined
|
|
17
|
+
? `[request-lifecycle ${ctx.kind} requestId=${ctx.requestId} modelId=${ctx.modelId}] `
|
|
18
|
+
: `[request-lifecycle ${ctx.kind} requestId=${ctx.requestId}] `;
|
|
19
|
+
function pick(method) {
|
|
20
|
+
switch (method) {
|
|
21
|
+
case "error":
|
|
22
|
+
return logger.error;
|
|
23
|
+
case "warn":
|
|
24
|
+
return logger.warn;
|
|
25
|
+
case "info":
|
|
26
|
+
return logger.info;
|
|
27
|
+
case "debug":
|
|
28
|
+
return logger.debug;
|
|
29
|
+
case "trace":
|
|
30
|
+
return logger.trace;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function emit(method, args) {
|
|
34
|
+
const sink = pick(method);
|
|
35
|
+
if (args.length === 0) {
|
|
36
|
+
sink(prefix);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const [first, ...rest] = args;
|
|
40
|
+
sink(prefix + String(first), ...rest);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
error: (...args) => emit("error", args),
|
|
44
|
+
warn: (...args) => emit("warn", args),
|
|
45
|
+
info: (...args) => emit("info", args),
|
|
46
|
+
debug: (...args) => emit("debug", args),
|
|
47
|
+
trace: (...args) => emit("trace", args),
|
|
48
|
+
setLevel: (level) => logger.setLevel(level),
|
|
49
|
+
getLevel: () => logger.getLevel(),
|
|
50
|
+
addTransport: (transport) => logger.addTransport(transport),
|
|
51
|
+
setConsoleOutput: (enabled) => logger.setConsoleOutput(enabled),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -60,8 +60,18 @@ export interface TranscribeAddonSegment {
|
|
|
60
60
|
toAppend?: boolean;
|
|
61
61
|
id?: number;
|
|
62
62
|
}
|
|
63
|
+
export interface TranscribeAddonVadEvent {
|
|
64
|
+
type: "vad";
|
|
65
|
+
speaking: boolean;
|
|
66
|
+
probability: number;
|
|
67
|
+
}
|
|
68
|
+
export interface TranscribeAddonEndOfTurnEvent {
|
|
69
|
+
type: "endOfTurn";
|
|
70
|
+
silenceDurationMs: number;
|
|
71
|
+
}
|
|
72
|
+
export type TranscribeAddonOutput = Array<TranscribeAddonSegment> | TranscribeAddonVadEvent | TranscribeAddonEndOfTurnEvent;
|
|
63
73
|
export interface TranscribeResponse {
|
|
64
74
|
stats?: TranscribeStats;
|
|
65
|
-
iterate(): AsyncIterable<
|
|
75
|
+
iterate(): AsyncIterable<TranscribeAddonOutput>;
|
|
66
76
|
}
|
|
67
77
|
//# sourceMappingURL=addon-responses.d.ts.map
|
|
@@ -27,6 +27,7 @@ import { handleFinetune } from "./handlers/finetune.js";
|
|
|
27
27
|
import { handleHeartbeatDelegated } from "./handlers/heartbeat-delegated.js";
|
|
28
28
|
import { handleCancelDelegated } from "./handlers/cancel-delegated.js";
|
|
29
29
|
import { handleDiffusionStream } from "./handlers/diffusion-stream.js";
|
|
30
|
+
import { handleUpscaleStream } from "./handlers/upscale-stream.js";
|
|
30
31
|
import { handlePluginInvoke, handlePluginInvokeStream, } from "./handlers/plugin-invoke.js";
|
|
31
32
|
import { handleModelRegistryList, handleModelRegistrySearch, handleModelRegistryGetModel, } from "./handlers/registry.js";
|
|
32
33
|
import { handleSuspend } from "./handlers/suspend.js";
|
|
@@ -48,16 +49,33 @@ function isModelDelegated(request) {
|
|
|
48
49
|
const entry = getModelEntry(request.modelId);
|
|
49
50
|
return entry?.isDelegated ?? false;
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Should the cancel be forwarded to a delegated provider?
|
|
54
|
+
*
|
|
55
|
+
* After the 0.11.0 wire-schema collapse the cancel envelope has two
|
|
56
|
+
* operations:
|
|
57
|
+
*
|
|
58
|
+
* - `request` — targeted cancel by `requestId`. Always handled
|
|
59
|
+
* locally: the worker-singleton `RequestRegistry` is the source of
|
|
60
|
+
* truth for active requests (delegated handlers register their own
|
|
61
|
+
* requests on it the same way local handlers do), so a `requestId`
|
|
62
|
+
* cancel always lands on the right worker without needing a hop
|
|
63
|
+
* through the provider. Returning `false` here keeps the cancel on
|
|
64
|
+
* the local cancel handler, where it routes through the registry
|
|
65
|
+
* and (for downloads) the `markClearCacheForRequest` helper.
|
|
66
|
+
*
|
|
67
|
+
* - `broad` — abort every in-flight request on a model. Forwarded to
|
|
68
|
+
* the delegated provider iff the targeted model itself is
|
|
69
|
+
* delegated; the provider then runs the same broad-cancel sweep
|
|
70
|
+
* server-side. Local broad cancels for non-delegated models stay
|
|
71
|
+
* on this worker.
|
|
72
|
+
*/
|
|
51
73
|
function isCancelDelegated(request) {
|
|
52
74
|
if (request.type !== "cancel")
|
|
53
75
|
return false;
|
|
54
|
-
if (request.operation
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
if (request.operation === "downloadAsset") {
|
|
58
|
-
return !!request.delegate;
|
|
59
|
-
}
|
|
60
|
-
return false;
|
|
76
|
+
if (request.operation !== "broad")
|
|
77
|
+
return false;
|
|
78
|
+
return isModelDelegated(request);
|
|
61
79
|
}
|
|
62
80
|
export const registry = {
|
|
63
81
|
// Simple Reply handlers
|
|
@@ -104,6 +122,7 @@ export const registry = {
|
|
|
104
122
|
textToSpeechStream: { type: "duplex", handler: handleTextToSpeechStream },
|
|
105
123
|
ocrStream: { type: "stream", handler: handleOCRStream },
|
|
106
124
|
diffusionStream: { type: "stream", handler: handleDiffusionStream },
|
|
125
|
+
upscaleStream: { type: "stream", handler: handleUpscaleStream },
|
|
107
126
|
pluginInvokeStream: { type: "stream", handler: handlePluginInvokeStream },
|
|
108
127
|
// Handlers with delegation support
|
|
109
128
|
loadModel: {
|
|
@@ -3,38 +3,35 @@ import { getModelEntry } from "../../bare/registry/model-registry.js";
|
|
|
3
3
|
import { getRPC } from "../../bare/delegate-rpc-client.js";
|
|
4
4
|
import { send } from "../delegate-transport.js";
|
|
5
5
|
const logger = getServerLogger();
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the delegated provider for a cancel request, if any.
|
|
8
|
+
*
|
|
9
|
+
* After the 0.11.0 wire-schema collapse the cancel envelope has only
|
|
10
|
+
* two operations. Only `broad` cancels delegate at the cancel layer —
|
|
11
|
+
* see `isCancelDelegated` in `handler-registry.ts` for the policy and
|
|
12
|
+
* the rationale.
|
|
13
|
+
*
|
|
14
|
+
* The targeted `request` arm is handled locally because the registry
|
|
15
|
+
* is worker-singleton and already holds the entry for delegated
|
|
16
|
+
* requests (the delegated handler registers its own context on the
|
|
17
|
+
* provider-facing side). For pre-0.11.0 behaviour where a `requestId`
|
|
18
|
+
* cancel against a delegated model needed to round-trip to the
|
|
19
|
+
* provider, hold onto the delegated `loadModel(...).requestId` and
|
|
20
|
+
* fire a broad cancel against the model id instead.
|
|
21
|
+
*/
|
|
6
22
|
function resolveDelegationTarget(request) {
|
|
7
|
-
if (request.operation
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
return target;
|
|
19
|
-
}
|
|
20
|
-
if (request.operation === "downloadAsset" && request.delegate) {
|
|
21
|
-
const target = {
|
|
22
|
-
providerPublicKey: request.delegate.providerPublicKey,
|
|
23
|
-
};
|
|
24
|
-
if (request.delegate.timeout !== undefined) {
|
|
25
|
-
target.timeout = request.delegate.timeout;
|
|
26
|
-
}
|
|
27
|
-
return target;
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
function toProviderCancelRequest(request) {
|
|
32
|
-
if (request.operation !== "downloadAsset") {
|
|
33
|
-
return request;
|
|
23
|
+
if (request.operation !== "broad")
|
|
24
|
+
return null;
|
|
25
|
+
const entry = getModelEntry(request.modelId);
|
|
26
|
+
if (!entry?.isDelegated)
|
|
27
|
+
return null;
|
|
28
|
+
const target = {
|
|
29
|
+
providerPublicKey: entry.delegated.providerPublicKey,
|
|
30
|
+
};
|
|
31
|
+
if (entry.delegated.timeout !== undefined) {
|
|
32
|
+
target.timeout = entry.delegated.timeout;
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
const { delegate: _delegate, ...providerRequest } = request;
|
|
37
|
-
return providerRequest;
|
|
34
|
+
return target;
|
|
38
35
|
}
|
|
39
36
|
export async function handleCancelDelegated(request, options) {
|
|
40
37
|
const target = resolveDelegationTarget(request);
|
|
@@ -55,7 +52,7 @@ export async function handleCancelDelegated(request, options) {
|
|
|
55
52
|
if (options?.profilingMeta) {
|
|
56
53
|
delegateOpts.profilingMeta = options.profilingMeta;
|
|
57
54
|
}
|
|
58
|
-
await send(
|
|
55
|
+
await send(request, rpc, delegateOpts);
|
|
59
56
|
return { type: "cancel", success: true };
|
|
60
57
|
}
|
|
61
58
|
catch (error) {
|