@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
package/src/sdk/client.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
1
2
|
/**
|
|
2
3
|
* GNO SDK client.
|
|
3
4
|
*
|
|
4
5
|
* @module src/sdk/client
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
import { mkdir } from "node:fs/promises";
|
|
8
7
|
import { dirname } from "node:path";
|
|
9
8
|
|
|
10
9
|
import type { Config } from "../config/types";
|
|
@@ -160,6 +159,10 @@ import {
|
|
|
160
159
|
withContentTypeRules,
|
|
161
160
|
} from "../ingestion";
|
|
162
161
|
import { updateFrontmatterTags } from "../ingestion/frontmatter";
|
|
162
|
+
import {
|
|
163
|
+
withInferenceScope,
|
|
164
|
+
finishInferenceCleanup,
|
|
165
|
+
} from "../llm/inference-scope";
|
|
163
166
|
import { LlmAdapter } from "../llm/nodeLlamaCpp/adapter";
|
|
164
167
|
import { resolveDownloadPolicy } from "../llm/policy";
|
|
165
168
|
import { resolveModelUri } from "../llm/registry";
|
|
@@ -170,6 +173,7 @@ import {
|
|
|
170
173
|
} from "../pipeline/answer";
|
|
171
174
|
import { formatQueryForEmbedding } from "../pipeline/contextual";
|
|
172
175
|
import { searchHybrid } from "../pipeline/hybrid";
|
|
176
|
+
import { RequestHydration } from "../pipeline/hydration";
|
|
173
177
|
import { searchBm25 } from "../pipeline/search";
|
|
174
178
|
import { searchVectorWithEmbedding } from "../pipeline/vsearch";
|
|
175
179
|
import { SqliteAdapter } from "../store/sqlite/adapter";
|
|
@@ -591,6 +595,10 @@ class GnoClientImpl implements GnoClient {
|
|
|
591
595
|
}
|
|
592
596
|
|
|
593
597
|
private async disposeRuntimePorts(ports: RuntimePorts): Promise<void> {
|
|
598
|
+
return finishInferenceCleanup(() => this.disposeRuntimePortsOwned(ports));
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
private async disposeRuntimePortsOwned(ports: RuntimePorts): Promise<void> {
|
|
594
602
|
if (ports.embedPort) {
|
|
595
603
|
await ports.embedPort.dispose();
|
|
596
604
|
}
|
|
@@ -618,6 +626,15 @@ class GnoClientImpl implements GnoClient {
|
|
|
618
626
|
async search(
|
|
619
627
|
query: string,
|
|
620
628
|
options: GnoSearchOptions = {}
|
|
629
|
+
): Promise<SearchResults> {
|
|
630
|
+
return withInferenceScope(options, () =>
|
|
631
|
+
this.searchRequest(query, options)
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
private async searchRequest(
|
|
636
|
+
query: string,
|
|
637
|
+
options: GnoSearchOptions
|
|
621
638
|
): Promise<SearchResults> {
|
|
622
639
|
this.assertOpen();
|
|
623
640
|
let traceSession: RetrievalTraceSession | null = null;
|
|
@@ -661,6 +678,15 @@ class GnoClientImpl implements GnoClient {
|
|
|
661
678
|
async vsearch(
|
|
662
679
|
query: string,
|
|
663
680
|
options: GnoVectorSearchOptions = {}
|
|
681
|
+
): Promise<SearchResults> {
|
|
682
|
+
return withInferenceScope(options, () =>
|
|
683
|
+
this.vsearchRequest(query, options)
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
private async vsearchRequest(
|
|
688
|
+
query: string,
|
|
689
|
+
options: GnoVectorSearchOptions
|
|
664
690
|
): Promise<SearchResults> {
|
|
665
691
|
this.assertOpen();
|
|
666
692
|
|
|
@@ -745,6 +771,13 @@ class GnoClientImpl implements GnoClient {
|
|
|
745
771
|
async query(
|
|
746
772
|
query: string,
|
|
747
773
|
options: GnoQueryOptions = {}
|
|
774
|
+
): Promise<SearchResults> {
|
|
775
|
+
return withInferenceScope(options, () => this.queryRequest(query, options));
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
private async queryRequest(
|
|
779
|
+
query: string,
|
|
780
|
+
options: GnoQueryOptions
|
|
748
781
|
): Promise<SearchResults> {
|
|
749
782
|
this.assertOpen();
|
|
750
783
|
|
|
@@ -852,6 +885,13 @@ class GnoClientImpl implements GnoClient {
|
|
|
852
885
|
}
|
|
853
886
|
|
|
854
887
|
async ask(query: string, options: GnoAskOptions = {}): Promise<AskResult> {
|
|
888
|
+
return withInferenceScope(options, () => this.askRequest(query, options));
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
private async askRequest(
|
|
892
|
+
query: string,
|
|
893
|
+
options: GnoAskOptions
|
|
894
|
+
): Promise<AskResult> {
|
|
855
895
|
this.assertOpen();
|
|
856
896
|
|
|
857
897
|
const normalizedInput = normalizeStructuredQueryInput(
|
|
@@ -911,6 +951,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
911
951
|
let ports: RuntimePorts | null = null;
|
|
912
952
|
let traceSession: RetrievalTraceSession | null = null;
|
|
913
953
|
|
|
954
|
+
const hydration = new RequestHydration(this.store);
|
|
914
955
|
try {
|
|
915
956
|
const { projectHints, ...askOptions } = options;
|
|
916
957
|
const projectAffinity = await resolveSdkProjectAffinity(
|
|
@@ -960,6 +1001,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
960
1001
|
{ ...askOptions, projectAffinity },
|
|
961
1002
|
{
|
|
962
1003
|
store: this.store,
|
|
1004
|
+
hydration,
|
|
963
1005
|
config: this.config,
|
|
964
1006
|
indexName: this.indexName,
|
|
965
1007
|
vectorIndex: ports.vectorIndex,
|
|
@@ -987,6 +1029,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
987
1029
|
await searchHybrid(
|
|
988
1030
|
{
|
|
989
1031
|
store: this.store,
|
|
1032
|
+
hydration,
|
|
990
1033
|
config: this.config,
|
|
991
1034
|
vectorIndex: ports.vectorIndex,
|
|
992
1035
|
embedPort: ports.embedPort,
|
|
@@ -1033,7 +1076,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
1033
1076
|
) {
|
|
1034
1077
|
await traceSession?.recordCapability("answer_generation", "attempted");
|
|
1035
1078
|
const rawAnswer = await generateGroundedAnswer(
|
|
1036
|
-
{ genPort: ports.answerPort, store: this.store },
|
|
1079
|
+
{ genPort: ports.answerPort, store: this.store, hydration },
|
|
1037
1080
|
query,
|
|
1038
1081
|
searchResult.results,
|
|
1039
1082
|
options.maxAnswerTokens ?? 512
|
|
@@ -1100,6 +1143,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
1100
1143
|
await finishRetrievalTraceAfterError(traceSession, cause);
|
|
1101
1144
|
throw cause;
|
|
1102
1145
|
} finally {
|
|
1146
|
+
hydration.release();
|
|
1103
1147
|
if (ports) await this.disposeRuntimePorts(ports);
|
|
1104
1148
|
}
|
|
1105
1149
|
}
|
package/src/sdk/embed.ts
CHANGED
|
@@ -18,13 +18,14 @@ import type {
|
|
|
18
18
|
} from "../store/vector";
|
|
19
19
|
import type { GnoEmbedOptions, GnoEmbedResult } from "./types";
|
|
20
20
|
|
|
21
|
-
import { embedBacklog } from "../embed";
|
|
21
|
+
import { embedBacklog, prepareEmbeddingBacklog } from "../embed/backlog";
|
|
22
22
|
import { getEmbeddingFingerprint } from "../embed/fingerprint";
|
|
23
23
|
import {
|
|
24
24
|
chunkRetryKey,
|
|
25
25
|
embedAndStoreBatch,
|
|
26
26
|
MAX_EMBED_CHUNK_ATTEMPTS,
|
|
27
27
|
} from "../embed/retry";
|
|
28
|
+
import { countVariantBacklog } from "../embed/variant-plan";
|
|
28
29
|
import { resolveModelUri } from "../llm/registry";
|
|
29
30
|
import { err, ok } from "../store/types";
|
|
30
31
|
import { createVectorIndexPort, createVectorStatsPort } from "../store/vector";
|
|
@@ -71,7 +72,7 @@ function getActiveChunks(
|
|
|
71
72
|
const sql = after
|
|
72
73
|
? `
|
|
73
74
|
SELECT c.mirror_hash as mirrorHash, c.seq, c.text,
|
|
74
|
-
(SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 LIMIT 1) as title,
|
|
75
|
+
(SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 ORDER BY d.id LIMIT 1) as title,
|
|
75
76
|
'force' as reason
|
|
76
77
|
FROM content_chunks c
|
|
77
78
|
WHERE EXISTS (
|
|
@@ -84,7 +85,7 @@ function getActiveChunks(
|
|
|
84
85
|
`
|
|
85
86
|
: `
|
|
86
87
|
SELECT c.mirror_hash as mirrorHash, c.seq, c.text,
|
|
87
|
-
(SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 LIMIT 1) as title,
|
|
88
|
+
(SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 ORDER BY d.id LIMIT 1) as title,
|
|
88
89
|
'force' as reason
|
|
89
90
|
FROM content_chunks c
|
|
90
91
|
WHERE EXISTS (
|
|
@@ -238,16 +239,6 @@ async function forceEmbedAll(
|
|
|
238
239
|
return { embedded, errors, contentionErrors };
|
|
239
240
|
}
|
|
240
241
|
|
|
241
|
-
async function checkVecAvailable(db: Database): Promise<boolean> {
|
|
242
|
-
try {
|
|
243
|
-
const sqliteVec = await import("sqlite-vec");
|
|
244
|
-
sqliteVec.load(db);
|
|
245
|
-
return true;
|
|
246
|
-
} catch {
|
|
247
|
-
return false;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
242
|
export async function runEmbed(
|
|
252
243
|
runtime: EmbedRuntimeOptions,
|
|
253
244
|
options: GnoEmbedOptions = {}
|
|
@@ -265,26 +256,6 @@ export async function runEmbed(
|
|
|
265
256
|
const stats: VectorStatsPort = createVectorStatsPort(db);
|
|
266
257
|
|
|
267
258
|
let totalToEmbed = 0;
|
|
268
|
-
if (force) {
|
|
269
|
-
const forceCount = await getActiveChunkCount(db);
|
|
270
|
-
if (!forceCount.ok) {
|
|
271
|
-
throw sdkError("STORE", forceCount.error.message, {
|
|
272
|
-
cause: forceCount.error.cause,
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
totalToEmbed = forceCount.value;
|
|
277
|
-
if (totalToEmbed === 0 || dryRun) {
|
|
278
|
-
return {
|
|
279
|
-
embedded: totalToEmbed,
|
|
280
|
-
errors: 0,
|
|
281
|
-
duration: 0,
|
|
282
|
-
model: modelUri,
|
|
283
|
-
searchAvailable: await checkVecAvailable(db),
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
259
|
const embedResult = await runtime.llm.createEmbeddingPort(modelUri, {
|
|
289
260
|
egressCollections: options.collection ? [options.collection] : "all",
|
|
290
261
|
policy: runtime.downloadPolicy,
|
|
@@ -297,16 +268,22 @@ export async function runEmbed(
|
|
|
297
268
|
|
|
298
269
|
const embedPort = embedResult.value;
|
|
299
270
|
try {
|
|
300
|
-
const
|
|
301
|
-
if (!
|
|
302
|
-
throw sdkError("MODEL",
|
|
303
|
-
|
|
304
|
-
|
|
271
|
+
const initializedPort = await embedPort.init();
|
|
272
|
+
if (!initializedPort.ok)
|
|
273
|
+
throw sdkError("MODEL", initializedPort.error.message);
|
|
274
|
+
let dimensions = embedPort.dimensions();
|
|
275
|
+
if (!embedPort.getIdentity?.()) {
|
|
276
|
+
const probeResult = await embedPort.embed("dimension probe");
|
|
277
|
+
if (!probeResult.ok)
|
|
278
|
+
throw sdkError("MODEL", probeResult.error.message, {
|
|
279
|
+
cause: probeResult.error.cause,
|
|
280
|
+
});
|
|
281
|
+
dimensions = probeResult.value.length;
|
|
305
282
|
}
|
|
306
283
|
|
|
307
284
|
const vectorResult = await createVectorIndexPort(db, {
|
|
308
285
|
model: modelUri,
|
|
309
|
-
dimensions
|
|
286
|
+
dimensions,
|
|
310
287
|
});
|
|
311
288
|
if (!vectorResult.ok) {
|
|
312
289
|
throw sdkError("STORE", vectorResult.error.message, {
|
|
@@ -315,6 +292,53 @@ export async function runEmbed(
|
|
|
315
292
|
}
|
|
316
293
|
|
|
317
294
|
const vectorIndex = vectorResult.value;
|
|
295
|
+
const prepared = await prepareEmbeddingBacklog({
|
|
296
|
+
statsPort: stats,
|
|
297
|
+
embedPort,
|
|
298
|
+
vectorIndex,
|
|
299
|
+
modelUri,
|
|
300
|
+
collection: options.collection,
|
|
301
|
+
batchSize,
|
|
302
|
+
force,
|
|
303
|
+
});
|
|
304
|
+
if (!prepared.ok) throw sdkError("STORE", prepared.error.message);
|
|
305
|
+
if (prepared.value.variantStore) {
|
|
306
|
+
const count = countVariantBacklog(prepared.value);
|
|
307
|
+
const startedAt = Date.now();
|
|
308
|
+
if (dryRun)
|
|
309
|
+
return {
|
|
310
|
+
embedded: count,
|
|
311
|
+
errors: 0,
|
|
312
|
+
duration: 0,
|
|
313
|
+
model: modelUri,
|
|
314
|
+
searchAvailable: prepared.value.variantStore.searchAvailable,
|
|
315
|
+
};
|
|
316
|
+
const processed = await embedBacklog(prepared.value);
|
|
317
|
+
if (!processed.ok) throw sdkError("STORE", processed.error.message);
|
|
318
|
+
if (processed.value.syncError)
|
|
319
|
+
throw sdkError("STORE", processed.value.syncError);
|
|
320
|
+
return {
|
|
321
|
+
embedded: processed.value.embedded,
|
|
322
|
+
errors: processed.value.errors,
|
|
323
|
+
contentionErrors: processed.value.contentionErrors ?? 0,
|
|
324
|
+
duration: (Date.now() - startedAt) / 1000,
|
|
325
|
+
model: modelUri,
|
|
326
|
+
searchAvailable: prepared.value.variantStore.searchAvailable,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (force) {
|
|
330
|
+
const count = await getActiveChunkCount(db);
|
|
331
|
+
if (!count.ok) throw sdkError("STORE", count.error.message);
|
|
332
|
+
totalToEmbed = count.value;
|
|
333
|
+
if (dryRun || !totalToEmbed)
|
|
334
|
+
return {
|
|
335
|
+
embedded: totalToEmbed,
|
|
336
|
+
errors: 0,
|
|
337
|
+
duration: 0,
|
|
338
|
+
model: modelUri,
|
|
339
|
+
searchAvailable: vectorIndex.searchAvailable,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
318
342
|
if (!force) {
|
|
319
343
|
const embedFingerprint = getEmbeddingFingerprint({
|
|
320
344
|
modelUri,
|
|
@@ -29,7 +29,7 @@ export interface BackgroundRuntime {
|
|
|
29
29
|
runUpdateCmd?: boolean;
|
|
30
30
|
triggerEmbed?: boolean;
|
|
31
31
|
}): Promise<{ syncResult: SyncResult; embedResult: EmbedResult | null }>;
|
|
32
|
-
dispose(): Promise<void>;
|
|
32
|
+
dispose(closeSurface?: () => Promise<unknown>): Promise<void>;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export type BackgroundRuntimeResult =
|
package/src/serve/context.ts
CHANGED
|
@@ -15,16 +15,22 @@ import type {
|
|
|
15
15
|
RerankPort,
|
|
16
16
|
} from "../llm/types";
|
|
17
17
|
import type { SqliteAdapter } from "../store/sqlite/adapter";
|
|
18
|
+
import type { VectorIndexPort } from "../store/vector";
|
|
18
19
|
import type { DocumentEventBus } from "./doc-events";
|
|
19
20
|
import type { EmbedScheduler } from "./embed-scheduler";
|
|
20
21
|
import type { CollectionWatchService } from "./watch-service";
|
|
21
22
|
|
|
22
23
|
import { DEFAULT_INDEX_NAME } from "../app/constants";
|
|
23
24
|
import { canonicalizeIndexName } from "../app/index-name";
|
|
25
|
+
import {
|
|
26
|
+
lazyEmbeddingPort,
|
|
27
|
+
lazyGenerationPort,
|
|
28
|
+
lazyRerankPort,
|
|
29
|
+
} from "../llm/lazy-ports";
|
|
24
30
|
import { LlmAdapter } from "../llm/nodeLlamaCpp/adapter";
|
|
25
31
|
import { resolveDownloadPolicy } from "../llm/policy";
|
|
26
32
|
import { getActivePreset } from "../llm/registry";
|
|
27
|
-
import {
|
|
33
|
+
import { createLazyVectorIndex } from "../store/vector/lazy";
|
|
28
34
|
|
|
29
35
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
36
|
// Download State (in-memory, single user)
|
|
@@ -64,6 +70,7 @@ export function resetDownloadState(): void {
|
|
|
64
70
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
65
71
|
|
|
66
72
|
export interface ServerContext {
|
|
73
|
+
llm?: LlmAdapter;
|
|
67
74
|
store: SqliteAdapter;
|
|
68
75
|
config: Config;
|
|
69
76
|
/** Canonical identity of the already-open resident store. */
|
|
@@ -91,7 +98,7 @@ export interface CreateServerContextOptions {
|
|
|
91
98
|
|
|
92
99
|
/**
|
|
93
100
|
* Initialize server context with LLM ports.
|
|
94
|
-
*
|
|
101
|
+
* Model resolution, downloads and loading wait until actual inference.
|
|
95
102
|
*/
|
|
96
103
|
export async function createServerContext(
|
|
97
104
|
store: SqliteAdapter,
|
|
@@ -104,9 +111,9 @@ export async function createServerContext(
|
|
|
104
111
|
let rerankPort: RerankPort | null = null;
|
|
105
112
|
let vectorIndex: VectorIndexPort | null = null;
|
|
106
113
|
|
|
114
|
+
const llm = new LlmAdapter(config);
|
|
107
115
|
try {
|
|
108
116
|
const preset = getActivePreset(config);
|
|
109
|
-
const llm = new LlmAdapter(config);
|
|
110
117
|
|
|
111
118
|
// Resolve download policy from env (serve has no CLI flags)
|
|
112
119
|
const policy = resolveDownloadPolicy(process.env, {
|
|
@@ -127,64 +134,40 @@ export async function createServerContext(
|
|
|
127
134
|
},
|
|
128
135
|
});
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
const resolvePort = async <T>(operation: () => Promise<T>): Promise<T> => {
|
|
138
|
+
try {
|
|
139
|
+
return await operation();
|
|
140
|
+
} finally {
|
|
141
|
+
downloadState.active = false;
|
|
142
|
+
downloadState.currentType = null;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
embedPort = lazyEmbeddingPort(preset.embed, () =>
|
|
146
|
+
resolvePort(() =>
|
|
147
|
+
llm.createEmbeddingPort(preset.embed, createPortOptions("embed"))
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
vectorIndex = await createLazyVectorIndex(
|
|
151
|
+
store.getRawDb(),
|
|
132
152
|
preset.embed,
|
|
133
|
-
|
|
153
|
+
embedPort
|
|
134
154
|
);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const dimensions = embedPort.dimensions();
|
|
141
|
-
const db = store.getRawDb();
|
|
142
|
-
const vectorResult = await createVectorIndexPort(db, {
|
|
143
|
-
model: preset.embed,
|
|
144
|
-
dimensions,
|
|
145
|
-
});
|
|
146
|
-
if (vectorResult.ok) {
|
|
147
|
-
vectorIndex = vectorResult.value;
|
|
148
|
-
console.log("Vector search enabled");
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Try to create expansion port
|
|
154
|
-
const expandResult = await llm.createExpansionPort(
|
|
155
|
-
preset.expand ?? preset.gen,
|
|
156
|
-
createPortOptions("expand")
|
|
155
|
+
const expandUri = preset.expand ?? preset.gen;
|
|
156
|
+
expandPort = lazyGenerationPort(expandUri, () =>
|
|
157
|
+
resolvePort(() =>
|
|
158
|
+
llm.createExpansionPort(expandUri, createPortOptions("expand"))
|
|
159
|
+
)
|
|
157
160
|
);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// Try to create answer generation port
|
|
164
|
-
const answerResult = await llm.createGenerationPort(
|
|
165
|
-
preset.gen,
|
|
166
|
-
createPortOptions("gen")
|
|
161
|
+
answerPort = lazyGenerationPort(preset.gen, () =>
|
|
162
|
+
resolvePort(() =>
|
|
163
|
+
llm.createGenerationPort(preset.gen, createPortOptions("gen"))
|
|
164
|
+
)
|
|
167
165
|
);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Try to create rerank port
|
|
174
|
-
const rerankResult = await llm.createRerankPort(
|
|
175
|
-
preset.rerank,
|
|
176
|
-
createPortOptions("rerank")
|
|
166
|
+
rerankPort = lazyRerankPort(preset.rerank, () =>
|
|
167
|
+
resolvePort(() =>
|
|
168
|
+
llm.createRerankPort(preset.rerank, createPortOptions("rerank"))
|
|
169
|
+
)
|
|
177
170
|
);
|
|
178
|
-
if (rerankResult.ok) {
|
|
179
|
-
rerankPort = rerankResult.value;
|
|
180
|
-
console.log("Reranking enabled");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Reset download state after initialization
|
|
184
|
-
if (downloadState.active) {
|
|
185
|
-
downloadState.active = false;
|
|
186
|
-
downloadState.currentType = null;
|
|
187
|
-
}
|
|
188
171
|
} catch (e) {
|
|
189
172
|
// Log but don't fail - models are optional
|
|
190
173
|
console.log(
|
|
@@ -201,6 +184,7 @@ export async function createServerContext(
|
|
|
201
184
|
};
|
|
202
185
|
|
|
203
186
|
return {
|
|
187
|
+
llm,
|
|
204
188
|
store,
|
|
205
189
|
config,
|
|
206
190
|
indexName: canonicalizeIndexName(options.indexName ?? DEFAULT_INDEX_NAME),
|
|
@@ -237,6 +221,7 @@ export async function disposeServerContext(ctx: ServerContext): Promise<void> {
|
|
|
237
221
|
}
|
|
238
222
|
}
|
|
239
223
|
}
|
|
224
|
+
await ctx.llm?.dispose();
|
|
240
225
|
}
|
|
241
226
|
|
|
242
227
|
/**
|
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Database } from "bun:sqlite";
|
|
9
9
|
|
|
10
|
-
import type { ModelLease } from "../llm/nodeLlamaCpp/lifecycle";
|
|
11
10
|
import type { EmbeddingPort } from "../llm/types";
|
|
12
11
|
import type { VectorIndexPort } from "../store/vector";
|
|
13
12
|
|
|
14
13
|
import { embedBacklog } from "../embed";
|
|
14
|
+
import {
|
|
15
|
+
withBackgroundInference,
|
|
16
|
+
withOwnedInferenceScope,
|
|
17
|
+
} from "../llm/inference-scope";
|
|
15
18
|
import { createVectorStatsPort } from "../store/vector";
|
|
16
19
|
|
|
17
20
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -47,7 +50,6 @@ export interface EmbedSchedulerDeps {
|
|
|
47
50
|
getVectorIndex: () => VectorIndexPort | null;
|
|
48
51
|
/** Getter for current model URI (survives preset changes) */
|
|
49
52
|
getModelUri: () => string;
|
|
50
|
-
acquireModelLease?: () => ModelLease;
|
|
51
53
|
onEmbedded?: (result: EmbedResult) => void;
|
|
52
54
|
embedBacklogFn?: typeof embedBacklog;
|
|
53
55
|
}
|
|
@@ -68,6 +70,8 @@ export interface EmbedScheduler {
|
|
|
68
70
|
|
|
69
71
|
/** Cleanup on server shutdown */
|
|
70
72
|
dispose(): Promise<void>;
|
|
73
|
+
/** Stop new turns while allowing the current pass to settle before cancellation. */
|
|
74
|
+
stop?(): Promise<void>;
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
/**
|
|
@@ -80,7 +84,6 @@ export function createEmbedScheduler(deps: EmbedSchedulerDeps): EmbedScheduler {
|
|
|
80
84
|
getEmbedPort,
|
|
81
85
|
getVectorIndex,
|
|
82
86
|
getModelUri,
|
|
83
|
-
acquireModelLease,
|
|
84
87
|
onEmbedded,
|
|
85
88
|
embedBacklogFn = embedBacklog,
|
|
86
89
|
} = deps;
|
|
@@ -96,6 +99,7 @@ export function createEmbedScheduler(deps: EmbedSchedulerDeps): EmbedScheduler {
|
|
|
96
99
|
let currentRun: Promise<EmbedResult | null> | null = null;
|
|
97
100
|
let lastRunAt: number | null = null;
|
|
98
101
|
let lastResult: EmbedResult | null = null;
|
|
102
|
+
const controller = new AbortController();
|
|
99
103
|
|
|
100
104
|
const stats = createVectorStatsPort(db);
|
|
101
105
|
|
|
@@ -116,33 +120,48 @@ export function createEmbedScheduler(deps: EmbedSchedulerDeps): EmbedScheduler {
|
|
|
116
120
|
return { embedded: 0, errors: 0 };
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
|
|
123
|
+
let result: Awaited<ReturnType<typeof embedBacklogFn>>;
|
|
120
124
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
125
|
+
result = await withBackgroundInference(() =>
|
|
126
|
+
withOwnedInferenceScope({ signal: controller.signal }, () =>
|
|
127
|
+
embedBacklogFn({
|
|
128
|
+
statsPort: stats,
|
|
129
|
+
embedPort,
|
|
130
|
+
vectorIndex,
|
|
131
|
+
modelUri,
|
|
132
|
+
batchSize: BATCH_SIZE,
|
|
133
|
+
identityStillCurrent: () =>
|
|
134
|
+
getEmbedPort() === embedPort &&
|
|
135
|
+
getVectorIndex() === vectorIndex &&
|
|
136
|
+
getModelUri() === modelUri,
|
|
137
|
+
})
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
} catch (cause) {
|
|
141
|
+
if (controller.signal.aborted) return { embedded: 0, errors: 0 };
|
|
142
|
+
throw cause;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!result.ok) {
|
|
146
|
+
needsRerun = true;
|
|
147
|
+
console.error("[embed-scheduler] Embed failed:", result.error.message);
|
|
148
|
+
return { embedded: 0, errors: 0 };
|
|
149
|
+
}
|
|
150
|
+
if (
|
|
151
|
+
getEmbedPort() !== embedPort ||
|
|
152
|
+
getVectorIndex() !== vectorIndex ||
|
|
153
|
+
getModelUri() !== modelUri
|
|
154
|
+
)
|
|
155
|
+
needsRerun = true;
|
|
156
|
+
if ((result.value.contentionErrors ?? 0) > 0 || result.value.errors > 0) {
|
|
157
|
+
// Provider failures and contended checkpoints remain durably pending.
|
|
158
|
+
console.error(
|
|
159
|
+
`[embed-scheduler] ${result.value.errors} embedding errors, ${result.value.contentionErrors ?? 0} contended writes; rescheduling`
|
|
160
|
+
);
|
|
161
|
+
needsRerun = true;
|
|
145
162
|
}
|
|
163
|
+
if (result.value.embedded > 0) onEmbedded?.(result.value);
|
|
164
|
+
return result.value;
|
|
146
165
|
}
|
|
147
166
|
|
|
148
167
|
/**
|
|
@@ -241,7 +260,16 @@ export function createEmbedScheduler(deps: EmbedSchedulerDeps): EmbedScheduler {
|
|
|
241
260
|
return operation;
|
|
242
261
|
}
|
|
243
262
|
|
|
263
|
+
const stop = async (): Promise<void> => {
|
|
264
|
+
disposed = true;
|
|
265
|
+
if (timer) clearTimeout(timer);
|
|
266
|
+
timer = null;
|
|
267
|
+
nextRunAt = null;
|
|
268
|
+
await currentRun;
|
|
269
|
+
};
|
|
270
|
+
|
|
244
271
|
return {
|
|
272
|
+
stop,
|
|
245
273
|
notifySyncComplete(docIds: string[]): void {
|
|
246
274
|
// Resolve embedPort at call time to check availability
|
|
247
275
|
if (disposed || !getEmbedPort()) {
|
|
@@ -302,13 +330,8 @@ export function createEmbedScheduler(deps: EmbedSchedulerDeps): EmbedScheduler {
|
|
|
302
330
|
},
|
|
303
331
|
|
|
304
332
|
async dispose(): Promise<void> {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
clearTimeout(timer);
|
|
308
|
-
timer = null;
|
|
309
|
-
nextRunAt = null;
|
|
310
|
-
}
|
|
311
|
-
await currentRun;
|
|
333
|
+
controller.abort();
|
|
334
|
+
await stop();
|
|
312
335
|
},
|
|
313
336
|
};
|
|
314
337
|
}
|