@gmickel/gno 1.46.0 → 2.0.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/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +46 -0
- package/assets/skill/SKILL.md +7 -6
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +32 -33
- package/bunfig.toml +2 -0
- package/package.json +40 -26
- package/spec/cli.md +21 -4
- package/spec/db/schema.sql +146 -1
- package/spec/mcp.md +26 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +2 -0
- package/src/cli/commands/ask.ts +6 -1
- package/src/cli/commands/daemon.ts +21 -8
- package/src/cli/commands/embed.ts +77 -41
- package/src/cli/detach.ts +3 -2
- package/src/config/types.ts +3 -3
- package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
- package/src/converters/adapters/officeparser/adapter.ts +1 -2
- package/src/converters/versions.ts +6 -8
- package/src/core/context-evidence.ts +8 -4
- package/src/core/job-manager.ts +95 -13
- package/src/core/network-boundary-inventory.ts +10 -0
- package/src/core/shutdown-budget.ts +45 -0
- package/src/embed/backlog.ts +107 -4
- package/src/embed/batch.ts +42 -2
- package/src/embed/fingerprint.ts +16 -0
- package/src/embed/retry.ts +113 -5
- package/src/embed/variant-backlog.ts +105 -0
- package/src/embed/variant-plan.ts +62 -0
- package/src/embed/variant-retry.ts +113 -0
- package/src/ingestion/graph-reconciliation.ts +327 -0
- package/src/ingestion/sync.ts +9 -272
- package/src/llm/http-inference.ts +6 -0
- package/src/llm/httpEmbedding.ts +37 -6
- package/src/llm/httpGeneration.ts +18 -3
- package/src/llm/httpRerank.ts +23 -5
- package/src/llm/inference-cancellation.ts +168 -0
- package/src/llm/inference-scope.ts +202 -0
- package/src/llm/lazy-ports.ts +115 -0
- package/src/llm/native-worker/client.ts +541 -0
- package/src/llm/native-worker/dispatcher.ts +228 -0
- package/src/llm/native-worker/embedding-identity.ts +33 -0
- package/src/llm/native-worker/entry.ts +173 -0
- package/src/llm/native-worker/errors.ts +32 -0
- package/src/llm/native-worker/evaluation.ts +16 -0
- package/src/llm/native-worker/owned-exit.ts +108 -0
- package/src/llm/native-worker/owner.ts +141 -0
- package/src/llm/native-worker/ports.ts +317 -0
- package/src/llm/native-worker/protocol.ts +442 -0
- package/src/llm/native-worker/runtime-config.ts +92 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
- package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
- package/src/llm/nodeLlamaCpp/generation.ts +34 -5
- package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
- package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
- package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
- package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
- package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
- package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
- package/src/llm/types.ts +35 -5
- package/src/mcp/context.ts +27 -0
- package/src/mcp/http-transport.ts +12 -10
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-profile.ts +30 -8
- package/src/mcp/tools/context.ts +8 -11
- package/src/mcp/tools/embed.ts +1 -1
- package/src/mcp/tools/index-cmd.ts +1 -1
- package/src/mcp/tools/index.ts +10 -8
- package/src/mcp/tools/query.ts +14 -30
- package/src/mcp/tools/vsearch.ts +1 -1
- package/src/pipeline/answer.ts +23 -3
- package/src/pipeline/claim-verifier.ts +6 -0
- package/src/pipeline/expansion.ts +43 -40
- package/src/pipeline/explain.ts +6 -2
- package/src/pipeline/filters.ts +63 -0
- package/src/pipeline/fusion.ts +29 -9
- package/src/pipeline/graph-retrieval.ts +29 -9
- package/src/pipeline/hybrid.ts +198 -55
- package/src/pipeline/hydration.ts +161 -0
- package/src/pipeline/owner-fusion.ts +87 -0
- package/src/pipeline/rerank.ts +35 -11
- package/src/pipeline/search.ts +13 -2
- package/src/pipeline/types.ts +5 -3
- package/src/pipeline/vsearch.ts +87 -7
- package/src/sdk/client.ts +47 -3
- package/src/sdk/embed.ts +63 -39
- package/src/serve/background-runtime.ts +1 -1
- package/src/serve/context.ts +41 -56
- package/src/serve/embed-scheduler.ts +58 -35
- package/src/serve/public/components/IndexingProgress.tsx +46 -60
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/shiki-language-ids.ts +14 -0
- package/src/serve/resident-admission.ts +36 -36
- package/src/serve/resident-background-work.ts +20 -2
- package/src/serve/resident-request.ts +11 -5
- package/src/serve/resident-runtime.ts +97 -61
- package/src/serve/resident-shutdown.ts +153 -0
- package/src/serve/routes/api.ts +3 -1
- package/src/serve/server.ts +47 -26
- package/src/store/migrations/028-vector-variants.ts +54 -0
- package/src/store/migrations/029-graph-reference-state.ts +77 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +251 -183
- package/src/store/sqlite/eligibility.ts +174 -0
- package/src/store/sqlite/graph-edge-application.ts +66 -0
- package/src/store/sqlite/graph-reference-state.ts +194 -0
- package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
- package/src/store/types.ts +80 -12
- package/src/store/vector/eligibility.ts +36 -0
- package/src/store/vector/freshness.ts +33 -6
- package/src/store/vector/lazy.ts +81 -0
- package/src/store/vector/sqlite-vec.ts +106 -54
- package/src/store/vector/stats.ts +14 -3
- package/src/store/vector/types.ts +35 -2
- package/src/store/vector/variant-search.ts +192 -0
- package/src/store/vector/variants.ts +451 -0
- package/vendor/converters/markitdown-ts/LICENSE +21 -0
- package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
- package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
- package/vendor/converters/markitdown-ts/package.json +77 -0
- package/vendor/converters/officeparser/LICENSE +21 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
- package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
- package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
- package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
- package/vendor/converters/officeparser/dist/cli.js +381 -0
- package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
- package/vendor/converters/officeparser/dist/defaults.js +218 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
- package/vendor/converters/officeparser/dist/index.d.ts +60 -0
- package/vendor/converters/officeparser/dist/index.js +72 -0
- package/vendor/converters/officeparser/dist/index.mjs +18 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
- package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
- package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
- package/vendor/converters/officeparser/dist/types.js +107 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
- package/vendor/converters/officeparser/package.json +147 -0
- package/vendor/converters/upstream-manifest.json +124 -0
- package/vendor/dependency-fixes/README.md +77 -0
- package/vendor/dependency-fixes/vendor-converters.py +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-627emwpj.js +0 -75
- /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/** Internal lifetime contract shared by inference owners. Cancellation settles
|
|
2
|
+
* the caller once; only native settlement releases active ownership. Wiring the
|
|
3
|
+
* signal into a backend is the owner's responsibility, not proof of interruption.
|
|
4
|
+
*/
|
|
5
|
+
import type { LlmError } from "./errors";
|
|
6
|
+
import type { InferenceOptions, LlmResult } from "./types";
|
|
7
|
+
|
|
8
|
+
import { llmError } from "./errors";
|
|
9
|
+
|
|
10
|
+
const MAX_TIMER_MS = 2_147_483_647;
|
|
11
|
+
export type InferencePhase =
|
|
12
|
+
| "queued"
|
|
13
|
+
| "native-active"
|
|
14
|
+
| "response-pending"
|
|
15
|
+
| "settled";
|
|
16
|
+
|
|
17
|
+
/** Stable structural discriminator; do not inspect human-readable messages. */
|
|
18
|
+
export function isInferenceCancellation(error: LlmError): boolean {
|
|
19
|
+
return (
|
|
20
|
+
error.code === "INFERENCE_FAILED" &&
|
|
21
|
+
typeof error.cause === "object" &&
|
|
22
|
+
error.cause !== null &&
|
|
23
|
+
"name" in error.cause &&
|
|
24
|
+
error.cause.name === "AbortError"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class InferenceSettlement<T> {
|
|
29
|
+
private state: InferencePhase = "queued";
|
|
30
|
+
private delivered = false;
|
|
31
|
+
private executionStarted = false;
|
|
32
|
+
private executionDeadline?: number;
|
|
33
|
+
private result?: LlmResult<T>;
|
|
34
|
+
private deadlineTimer?: ReturnType<typeof setTimeout>;
|
|
35
|
+
private executionTimer?: ReturnType<typeof setTimeout>;
|
|
36
|
+
private readonly controller = new AbortController();
|
|
37
|
+
private readonly delivery = Promise.withResolvers<LlmResult<T>>();
|
|
38
|
+
readonly completion = this.delivery.promise;
|
|
39
|
+
readonly signal = this.controller.signal;
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
private readonly options: InferenceOptions = {},
|
|
43
|
+
private readonly inferenceTimeout = 30_000
|
|
44
|
+
) {
|
|
45
|
+
if (
|
|
46
|
+
!Number.isInteger(inferenceTimeout) ||
|
|
47
|
+
inferenceTimeout < 1 ||
|
|
48
|
+
inferenceTimeout > MAX_TIMER_MS
|
|
49
|
+
)
|
|
50
|
+
throw new RangeError("Invalid inference timeout");
|
|
51
|
+
if (
|
|
52
|
+
options.deadlineAt !== undefined &&
|
|
53
|
+
(!Number.isSafeInteger(options.deadlineAt) || options.deadlineAt < 0)
|
|
54
|
+
)
|
|
55
|
+
throw new RangeError("Invalid inference deadline");
|
|
56
|
+
options.signal?.addEventListener("abort", this.onAbort, { once: true });
|
|
57
|
+
if (options.signal?.aborted) this.cancel("abort");
|
|
58
|
+
this.checkDeadline();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get phase(): InferencePhase {
|
|
62
|
+
return this.state;
|
|
63
|
+
}
|
|
64
|
+
get ownsNative(): boolean {
|
|
65
|
+
return this.state === "native-active";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Claim before starting model/context load, including noncooperative creation. */
|
|
69
|
+
startNative(): boolean {
|
|
70
|
+
this.checkDeadline();
|
|
71
|
+
if (this.state !== "queued" || this.delivered) return false;
|
|
72
|
+
this.state = "native-active";
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Called immediately before evaluation, after loading. Never resets a timer. */
|
|
77
|
+
startExecution(): boolean {
|
|
78
|
+
this.checkDeadline();
|
|
79
|
+
if (!this.ownsNative || this.delivered || this.executionStarted)
|
|
80
|
+
return false;
|
|
81
|
+
this.executionStarted = true;
|
|
82
|
+
this.executionDeadline = performance.now() + this.inferenceTimeout;
|
|
83
|
+
this.executionTimer = setTimeout(
|
|
84
|
+
() => this.cancel("timeout"),
|
|
85
|
+
this.inferenceTimeout
|
|
86
|
+
);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Call only after the native promise settles or owned-child exit is confirmed.
|
|
91
|
+
* A cancellation acknowledgement alone is insufficient to release the lease.
|
|
92
|
+
*/
|
|
93
|
+
nativeSettled(result: LlmResult<T>): void {
|
|
94
|
+
if (!this.ownsNative) return;
|
|
95
|
+
this.checkDeadline();
|
|
96
|
+
clearTimeout(this.executionTimer);
|
|
97
|
+
this.executionDeadline = undefined;
|
|
98
|
+
this.result = result;
|
|
99
|
+
this.state = this.delivered ? "settled" : "response-pending";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Keep caller deadline/abort live through response validation and publication. */
|
|
103
|
+
publish(): boolean {
|
|
104
|
+
this.checkDeadline();
|
|
105
|
+
if (this.state !== "response-pending" || this.delivered || !this.result)
|
|
106
|
+
return false;
|
|
107
|
+
this.deliver(this.result);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Fail caller delivery while retaining native ownership until exit/settlement. */
|
|
112
|
+
fail(error: LlmError): void {
|
|
113
|
+
if (!this.delivered) this.deliver({ ok: false, error });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
cancel(reason: "abort" | "timeout"): void {
|
|
117
|
+
if (this.delivered) return;
|
|
118
|
+
const cause = new DOMException(
|
|
119
|
+
reason === "abort"
|
|
120
|
+
? "Inference cancelled"
|
|
121
|
+
: "Inference deadline exceeded",
|
|
122
|
+
reason === "abort" ? "AbortError" : "TimeoutError"
|
|
123
|
+
);
|
|
124
|
+
// Settle first: synchronous abort listeners cannot publish a late success.
|
|
125
|
+
this.deliver({
|
|
126
|
+
ok: false,
|
|
127
|
+
error: llmError(reason === "abort" ? "INFERENCE_FAILED" : "TIMEOUT", {
|
|
128
|
+
message: cause.message,
|
|
129
|
+
retryable: false,
|
|
130
|
+
cause,
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
this.controller.abort(cause);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private readonly onAbort = (): void => {
|
|
137
|
+
this.cancel("abort");
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
private checkDeadline(): void {
|
|
141
|
+
clearTimeout(this.deadlineTimer);
|
|
142
|
+
if (this.delivered) return;
|
|
143
|
+
if (
|
|
144
|
+
this.executionDeadline !== undefined &&
|
|
145
|
+
performance.now() >= this.executionDeadline
|
|
146
|
+
) {
|
|
147
|
+
this.cancel("timeout");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (this.options.deadlineAt === undefined) return;
|
|
151
|
+
const remaining = this.options.deadlineAt - Date.now();
|
|
152
|
+
if (remaining <= 0) this.cancel("timeout");
|
|
153
|
+
else
|
|
154
|
+
this.deadlineTimer = setTimeout(
|
|
155
|
+
() => this.checkDeadline(),
|
|
156
|
+
Math.min(remaining, MAX_TIMER_MS)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private deliver(result: LlmResult<T>): void {
|
|
161
|
+
this.delivered = true;
|
|
162
|
+
if (!this.ownsNative) this.state = "settled";
|
|
163
|
+
clearTimeout(this.deadlineTimer);
|
|
164
|
+
clearTimeout(this.executionTimer);
|
|
165
|
+
this.options.signal?.removeEventListener("abort", this.onAbort);
|
|
166
|
+
this.delivery.resolve(result);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Bun has no native async-context API; AsyncLocalStorage isolates concurrent callers.
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
+
|
|
4
|
+
import type { InferenceOptions, LlmResult } from "./types";
|
|
5
|
+
|
|
6
|
+
import { InferenceSettlement } from "./inference-cancellation";
|
|
7
|
+
|
|
8
|
+
interface RequestInferenceScope extends InferenceOptions {
|
|
9
|
+
parent?: RequestInferenceScope;
|
|
10
|
+
failure?: DOMException;
|
|
11
|
+
controller?: AbortController;
|
|
12
|
+
}
|
|
13
|
+
const scope = new AsyncLocalStorage<RequestInferenceScope>();
|
|
14
|
+
// Internal scheduling class, separate from public inference options/model inputs.
|
|
15
|
+
const background = new AsyncLocalStorage<boolean>();
|
|
16
|
+
|
|
17
|
+
export function isBackgroundInference(): boolean {
|
|
18
|
+
return background.getStore() === true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function withBackgroundInference<T>(
|
|
22
|
+
operation: () => Promise<T>
|
|
23
|
+
): Promise<T> {
|
|
24
|
+
return background.run(true, operation);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function recordInferenceTimeout(): void {
|
|
28
|
+
let current = scope.getStore();
|
|
29
|
+
while (current) {
|
|
30
|
+
current.failure = new DOMException(
|
|
31
|
+
"Inference deadline exceeded",
|
|
32
|
+
"TimeoutError"
|
|
33
|
+
);
|
|
34
|
+
current.controller?.abort(current.failure);
|
|
35
|
+
current = current.parent;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Operational metadata only: never serialized with model inputs. */
|
|
40
|
+
export function inferenceOptions(
|
|
41
|
+
options: InferenceOptions = {}
|
|
42
|
+
): InferenceOptions {
|
|
43
|
+
if (
|
|
44
|
+
options.deadlineAt !== undefined &&
|
|
45
|
+
(!Number.isSafeInteger(options.deadlineAt) || options.deadlineAt < 0)
|
|
46
|
+
)
|
|
47
|
+
throw new RangeError("Invalid inference deadline");
|
|
48
|
+
const parent = scope.getStore();
|
|
49
|
+
const signals = [
|
|
50
|
+
parent?.failure ? AbortSignal.abort(parent.failure) : parent?.signal,
|
|
51
|
+
options.signal,
|
|
52
|
+
].filter((signal): signal is AbortSignal => signal !== undefined);
|
|
53
|
+
const deadlines = [parent?.deadlineAt, options.deadlineAt].filter(
|
|
54
|
+
(deadline): deadline is number => deadline !== undefined
|
|
55
|
+
);
|
|
56
|
+
return {
|
|
57
|
+
signal: signals.length > 1 ? AbortSignal.any(signals) : signals[0],
|
|
58
|
+
deadlineAt: deadlines.length ? Math.min(...deadlines) : undefined,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function assertInferenceActive(options: InferenceOptions = {}): void {
|
|
63
|
+
if (scope.getStore()?.failure) throw scope.getStore()!.failure;
|
|
64
|
+
const current = inferenceOptions(options);
|
|
65
|
+
if (current.signal?.aborted)
|
|
66
|
+
throw new DOMException("Inference cancelled", "AbortError");
|
|
67
|
+
if (current.deadlineAt !== undefined && Date.now() >= current.deadlineAt)
|
|
68
|
+
throw new DOMException("Inference deadline exceeded", "TimeoutError");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function withInferenceScope<T>(
|
|
72
|
+
options: InferenceOptions,
|
|
73
|
+
operation: () => Promise<T>
|
|
74
|
+
): Promise<T> {
|
|
75
|
+
const inherited = inferenceOptions(options);
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
return scope.run(
|
|
78
|
+
{
|
|
79
|
+
...inherited,
|
|
80
|
+
signal: inherited.signal
|
|
81
|
+
? AbortSignal.any([inherited.signal, controller.signal])
|
|
82
|
+
: controller.signal,
|
|
83
|
+
controller,
|
|
84
|
+
parent: scope.getStore(),
|
|
85
|
+
},
|
|
86
|
+
async () => {
|
|
87
|
+
assertInferenceActive();
|
|
88
|
+
const result = await operation();
|
|
89
|
+
assertInferenceActive();
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** HTTP operations own their fetch/body until settlement; abort reaches fetch. */
|
|
96
|
+
export function runHttpInference<T>(
|
|
97
|
+
options: InferenceOptions | undefined,
|
|
98
|
+
operation: (options: InferenceOptions) => Promise<LlmResult<T>>,
|
|
99
|
+
inferenceTimeout = 30_000
|
|
100
|
+
): Promise<LlmResult<T>> {
|
|
101
|
+
const settlement = new InferenceSettlement<T>(
|
|
102
|
+
inferenceOptions(options),
|
|
103
|
+
inferenceTimeout
|
|
104
|
+
);
|
|
105
|
+
if (settlement.startNative()) {
|
|
106
|
+
settlement.startExecution();
|
|
107
|
+
void operation({ signal: settlement.signal }).then(
|
|
108
|
+
(result) => {
|
|
109
|
+
settlement.nativeSettled(result);
|
|
110
|
+
settlement.publish();
|
|
111
|
+
},
|
|
112
|
+
(cause: unknown) => {
|
|
113
|
+
settlement.nativeSettled({
|
|
114
|
+
ok: false,
|
|
115
|
+
error: {
|
|
116
|
+
code: "INFERENCE_FAILED",
|
|
117
|
+
message: "HTTP inference failed",
|
|
118
|
+
retryable: false,
|
|
119
|
+
cause,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
settlement.publish();
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return settlement.completion.then((result) => {
|
|
127
|
+
if (!result.ok && result.error.code === "TIMEOUT") recordInferenceTimeout();
|
|
128
|
+
return result;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Never convert cancellation or timeout into retrieval fallback. */
|
|
133
|
+
export function assertInferenceResult<T>(result: LlmResult<T>): void {
|
|
134
|
+
assertInferenceActive();
|
|
135
|
+
if (
|
|
136
|
+
!result.ok &&
|
|
137
|
+
(result.error.code === "TIMEOUT" ||
|
|
138
|
+
(typeof result.error.cause === "object" &&
|
|
139
|
+
result.error.cause !== null &&
|
|
140
|
+
"name" in result.error.cause &&
|
|
141
|
+
result.error.cause.name === "AbortError"))
|
|
142
|
+
)
|
|
143
|
+
throw new DOMException(
|
|
144
|
+
result.error.message,
|
|
145
|
+
result.error.code === "TIMEOUT" ? "TimeoutError" : "AbortError"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Owned cleanup must not inherit an expired caller, or block its delivery. */
|
|
150
|
+
export function finishInferenceCleanup(
|
|
151
|
+
operation: () => Promise<void>
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
const cancelled =
|
|
154
|
+
scope.getStore()?.failure ||
|
|
155
|
+
scope.getStore()?.signal?.aborted ||
|
|
156
|
+
(scope.getStore()?.deadlineAt !== undefined &&
|
|
157
|
+
Date.now() >= scope.getStore()!.deadlineAt!);
|
|
158
|
+
if (!cancelled) return operation();
|
|
159
|
+
void scope.run({}, operation).catch(() => {});
|
|
160
|
+
return Promise.resolve();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Accepted jobs own their lifetime independently of their initiating request. */
|
|
164
|
+
export function withOwnedInferenceScope<T>(
|
|
165
|
+
options: InferenceOptions,
|
|
166
|
+
operation: () => Promise<T>
|
|
167
|
+
): Promise<T> {
|
|
168
|
+
return scope.run({}, () => withInferenceScope(options, operation));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** A cancelled queued reader releases only its eventual grant, never another caller's. */
|
|
172
|
+
export async function acquireInferencePermit(
|
|
173
|
+
acquire: () => Promise<() => void>
|
|
174
|
+
): Promise<() => void> {
|
|
175
|
+
const lifetime = new InferenceSettlement<() => void>(inferenceOptions());
|
|
176
|
+
if (lifetime.startNative())
|
|
177
|
+
void acquire().then(
|
|
178
|
+
(release) => {
|
|
179
|
+
lifetime.nativeSettled({ ok: true, value: release });
|
|
180
|
+
if (!lifetime.publish()) release();
|
|
181
|
+
},
|
|
182
|
+
(cause: unknown) => {
|
|
183
|
+
lifetime.nativeSettled({
|
|
184
|
+
ok: false,
|
|
185
|
+
error: {
|
|
186
|
+
code: "INFERENCE_FAILED",
|
|
187
|
+
message: "Request admission failed",
|
|
188
|
+
retryable: false,
|
|
189
|
+
cause,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
lifetime.publish();
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
const result = await lifetime.completion;
|
|
196
|
+
if (!result.ok)
|
|
197
|
+
throw new DOMException(
|
|
198
|
+
result.error.message,
|
|
199
|
+
result.error.code === "TIMEOUT" ? "TimeoutError" : "AbortError"
|
|
200
|
+
);
|
|
201
|
+
return result.value;
|
|
202
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** Defer model resolution/download until an actual inference operation. */
|
|
2
|
+
import type {
|
|
3
|
+
EmbeddingPort,
|
|
4
|
+
GenerationPort,
|
|
5
|
+
LlmResult,
|
|
6
|
+
RerankPort,
|
|
7
|
+
} from "./types";
|
|
8
|
+
|
|
9
|
+
import { inferenceFailedError } from "./errors";
|
|
10
|
+
import { isHttpGenUri } from "./httpGeneration";
|
|
11
|
+
|
|
12
|
+
function lazyOwner<T extends { dispose(): Promise<void> }>(
|
|
13
|
+
modelUri: string,
|
|
14
|
+
create: () => Promise<LlmResult<T>>
|
|
15
|
+
) {
|
|
16
|
+
let current: T | undefined;
|
|
17
|
+
let pending: Promise<LlmResult<T>> | undefined;
|
|
18
|
+
let disposed = false;
|
|
19
|
+
const closed = (): LlmResult<T> => ({
|
|
20
|
+
ok: false,
|
|
21
|
+
error: inferenceFailedError(modelUri, new Error("Port is disposed")),
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
peek: () => current,
|
|
25
|
+
get(): Promise<LlmResult<T>> {
|
|
26
|
+
if (disposed) return Promise.resolve(closed());
|
|
27
|
+
if (current) return Promise.resolve({ ok: true, value: current });
|
|
28
|
+
pending ??= create()
|
|
29
|
+
.then(async (result) => {
|
|
30
|
+
if (disposed) {
|
|
31
|
+
if (result.ok) await result.value.dispose();
|
|
32
|
+
return closed();
|
|
33
|
+
}
|
|
34
|
+
if (result.ok) current = result.value;
|
|
35
|
+
return result;
|
|
36
|
+
})
|
|
37
|
+
.finally(() => {
|
|
38
|
+
pending = undefined;
|
|
39
|
+
});
|
|
40
|
+
return pending;
|
|
41
|
+
},
|
|
42
|
+
async dispose(): Promise<void> {
|
|
43
|
+
if (disposed) return;
|
|
44
|
+
disposed = true;
|
|
45
|
+
await pending;
|
|
46
|
+
const owned = current;
|
|
47
|
+
current = undefined;
|
|
48
|
+
await owned?.dispose();
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function lazyEmbeddingPort(
|
|
54
|
+
modelUri: string,
|
|
55
|
+
create: () => Promise<LlmResult<EmbeddingPort>>
|
|
56
|
+
): EmbeddingPort {
|
|
57
|
+
const owner = lazyOwner(modelUri, create);
|
|
58
|
+
return {
|
|
59
|
+
modelUri,
|
|
60
|
+
async init() {
|
|
61
|
+
const port = await owner.get();
|
|
62
|
+
return port.ok ? port.value.init() : port;
|
|
63
|
+
},
|
|
64
|
+
async embed(text) {
|
|
65
|
+
const port = await owner.get();
|
|
66
|
+
return port.ok ? port.value.embed(text) : port;
|
|
67
|
+
},
|
|
68
|
+
async embedBatch(texts) {
|
|
69
|
+
const port = await owner.get();
|
|
70
|
+
return port.ok ? port.value.embedBatch(texts) : port;
|
|
71
|
+
},
|
|
72
|
+
dimensions() {
|
|
73
|
+
const port = owner.peek();
|
|
74
|
+
if (!port)
|
|
75
|
+
throw new Error(
|
|
76
|
+
"Call init() or embed() first to initialize dimensions"
|
|
77
|
+
);
|
|
78
|
+
return port.dimensions();
|
|
79
|
+
},
|
|
80
|
+
getIdentity: () => owner.peek()?.getIdentity?.(),
|
|
81
|
+
dispose: () => owner.dispose(),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function lazyGenerationPort(
|
|
86
|
+
modelUri: string,
|
|
87
|
+
create: () => Promise<LlmResult<GenerationPort>>
|
|
88
|
+
): GenerationPort {
|
|
89
|
+
const owner = lazyOwner(modelUri, create);
|
|
90
|
+
return {
|
|
91
|
+
modelUri,
|
|
92
|
+
// These are the fixed native/HTTP adapter contracts; no probing required.
|
|
93
|
+
structuredOutput: isHttpGenUri(modelUri) ? "none" : "json_schema",
|
|
94
|
+
async generate(prompt, params) {
|
|
95
|
+
const port = await owner.get();
|
|
96
|
+
return port.ok ? port.value.generate(prompt, params) : port;
|
|
97
|
+
},
|
|
98
|
+
dispose: () => owner.dispose(),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function lazyRerankPort(
|
|
103
|
+
modelUri: string,
|
|
104
|
+
create: () => Promise<LlmResult<RerankPort>>
|
|
105
|
+
): RerankPort {
|
|
106
|
+
const owner = lazyOwner(modelUri, create);
|
|
107
|
+
return {
|
|
108
|
+
modelUri,
|
|
109
|
+
async rerank(query, documents) {
|
|
110
|
+
const port = await owner.get();
|
|
111
|
+
return port.ok ? port.value.rerank(query, documents) : port;
|
|
112
|
+
},
|
|
113
|
+
dispose: () => owner.dispose(),
|
|
114
|
+
};
|
|
115
|
+
}
|