@nodetool-ai/runtime 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.nodetool-build-stamp +1 -1
- package/dist/context.d.ts +31 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +366 -158
- package/dist/context.js.map +1 -1
- package/dist/generation-registry.d.ts +8 -0
- package/dist/generation-registry.d.ts.map +1 -1
- package/dist/generation-registry.js +18 -0
- package/dist/generation-registry.js.map +1 -1
- package/dist/host-binaries.d.ts.map +1 -1
- package/dist/host-binaries.js +18 -10
- package/dist/host-binaries.js.map +1 -1
- package/dist/image-codec.d.ts +2 -0
- package/dist/image-codec.d.ts.map +1 -1
- package/dist/image-codec.js +23 -3
- package/dist/image-codec.js.map +1 -1
- package/dist/image-model-tasks.d.ts +15 -0
- package/dist/image-model-tasks.d.ts.map +1 -0
- package/dist/image-model-tasks.js +24 -0
- package/dist/image-model-tasks.js.map +1 -0
- package/dist/index.d.ts +14 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/media-ref-bytes.d.ts +18 -2
- package/dist/media-ref-bytes.d.ts.map +1 -1
- package/dist/media-ref-bytes.js +26 -16
- package/dist/media-ref-bytes.js.map +1 -1
- package/dist/node-executor.d.ts +6 -0
- package/dist/node-executor.d.ts.map +1 -1
- package/dist/providers/aki-provider.d.ts +2 -0
- package/dist/providers/aki-provider.d.ts.map +1 -1
- package/dist/providers/aki-provider.js +6 -0
- package/dist/providers/aki-provider.js.map +1 -1
- package/dist/providers/atlascloud-provider.d.ts +2 -0
- package/dist/providers/atlascloud-provider.d.ts.map +1 -1
- package/dist/providers/atlascloud-provider.js +22 -0
- package/dist/providers/atlascloud-provider.js.map +1 -1
- package/dist/providers/base-provider.d.ts +36 -24
- package/dist/providers/base-provider.d.ts.map +1 -1
- package/dist/providers/base-provider.js +124 -148
- package/dist/providers/base-provider.js.map +1 -1
- package/dist/providers/claude-agent-provider.d.ts +6 -36
- package/dist/providers/claude-agent-provider.d.ts.map +1 -1
- package/dist/providers/claude-agent-provider.js +208 -26
- package/dist/providers/claude-agent-provider.js.map +1 -1
- package/dist/providers/codex-provider.d.ts +2 -0
- package/dist/providers/codex-provider.d.ts.map +1 -1
- package/dist/providers/codex-provider.js +15 -2
- package/dist/providers/codex-provider.js.map +1 -1
- package/dist/providers/cohere-provider.d.ts +2 -1
- package/dist/providers/cohere-provider.d.ts.map +1 -1
- package/dist/providers/cohere-provider.js +5 -0
- package/dist/providers/cohere-provider.js.map +1 -1
- package/dist/providers/core-tools.d.ts +2 -1
- package/dist/providers/core-tools.d.ts.map +1 -1
- package/dist/providers/core-tools.js +2 -1
- package/dist/providers/core-tools.js.map +1 -1
- package/dist/providers/credential-check.d.ts.map +1 -1
- package/dist/providers/credential-check.js +5 -0
- package/dist/providers/credential-check.js.map +1 -1
- package/dist/providers/custom-openai-provider.d.ts +2 -0
- package/dist/providers/custom-openai-provider.d.ts.map +1 -1
- package/dist/providers/custom-openai-provider.js +8 -0
- package/dist/providers/custom-openai-provider.js.map +1 -1
- package/dist/providers/dreamina-models.d.ts +5 -0
- package/dist/providers/dreamina-models.d.ts.map +1 -0
- package/dist/providers/dreamina-models.js +57 -0
- package/dist/providers/dreamina-models.js.map +1 -0
- package/dist/providers/dreamina-music.d.ts +52 -0
- package/dist/providers/dreamina-music.d.ts.map +1 -0
- package/dist/providers/dreamina-music.js +115 -0
- package/dist/providers/dreamina-music.js.map +1 -0
- package/dist/providers/dreamina-provider.d.ts +62 -0
- package/dist/providers/dreamina-provider.d.ts.map +1 -0
- package/dist/providers/dreamina-provider.js +790 -0
- package/dist/providers/dreamina-provider.js.map +1 -0
- package/dist/providers/elevenlabs-provider.d.ts +2 -1
- package/dist/providers/elevenlabs-provider.d.ts.map +1 -1
- package/dist/providers/elevenlabs-provider.js +5 -0
- package/dist/providers/elevenlabs-provider.js.map +1 -1
- package/dist/providers/evolink-provider.d.ts +2 -0
- package/dist/providers/evolink-provider.d.ts.map +1 -1
- package/dist/providers/evolink-provider.js +8 -0
- package/dist/providers/evolink-provider.js.map +1 -1
- package/dist/providers/fake-provider.d.ts +2 -1
- package/dist/providers/fake-provider.d.ts.map +1 -1
- package/dist/providers/fake-provider.js +10 -0
- package/dist/providers/fake-provider.js.map +1 -1
- package/dist/providers/fal-provider.d.ts +2 -1
- package/dist/providers/fal-provider.d.ts.map +1 -1
- package/dist/providers/fal-provider.js +27 -0
- package/dist/providers/fal-provider.js.map +1 -1
- package/dist/providers/gemini-provider.d.ts +2 -1
- package/dist/providers/gemini-provider.d.ts.map +1 -1
- package/dist/providers/gemini-provider.js +15 -0
- package/dist/providers/gemini-provider.js.map +1 -1
- package/dist/providers/higgsfield-provider.d.ts +2 -1
- package/dist/providers/higgsfield-provider.d.ts.map +1 -1
- package/dist/providers/higgsfield-provider.js +12 -0
- package/dist/providers/higgsfield-provider.js.map +1 -1
- package/dist/providers/huggingface-provider.d.ts +2 -1
- package/dist/providers/huggingface-provider.d.ts.map +1 -1
- package/dist/providers/huggingface-provider.js +10 -0
- package/dist/providers/huggingface-provider.js.map +1 -1
- package/dist/providers/index.d.ts +5 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +13 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/jina-provider.d.ts +2 -1
- package/dist/providers/jina-provider.d.ts.map +1 -1
- package/dist/providers/jina-provider.js +5 -0
- package/dist/providers/jina-provider.js.map +1 -1
- package/dist/providers/kie-provider.d.ts +2 -1
- package/dist/providers/kie-provider.d.ts.map +1 -1
- package/dist/providers/kie-provider.js +14 -0
- package/dist/providers/kie-provider.js.map +1 -1
- package/dist/providers/llama-provider.d.ts +4 -1
- package/dist/providers/llama-provider.d.ts.map +1 -1
- package/dist/providers/llama-provider.js +9 -3
- package/dist/providers/llama-provider.js.map +1 -1
- package/dist/providers/loop-tracing.d.ts +17 -0
- package/dist/providers/loop-tracing.d.ts.map +1 -0
- package/dist/providers/loop-tracing.js +132 -0
- package/dist/providers/loop-tracing.js.map +1 -0
- package/dist/providers/meshy-provider.d.ts +2 -1
- package/dist/providers/meshy-provider.d.ts.map +1 -1
- package/dist/providers/meshy-provider.js +6 -0
- package/dist/providers/meshy-provider.js.map +1 -1
- package/dist/providers/minimax-provider.d.ts +2 -0
- package/dist/providers/minimax-provider.d.ts.map +1 -1
- package/dist/providers/minimax-provider.js +11 -0
- package/dist/providers/minimax-provider.js.map +1 -1
- package/dist/providers/mistral-provider.d.ts +2 -0
- package/dist/providers/mistral-provider.d.ts.map +1 -1
- package/dist/providers/mistral-provider.js +5 -0
- package/dist/providers/mistral-provider.js.map +1 -1
- package/dist/providers/node-llama-cpp-provider.d.ts +2 -1
- package/dist/providers/node-llama-cpp-provider.d.ts.map +1 -1
- package/dist/providers/node-llama-cpp-provider.js +8 -3
- package/dist/providers/node-llama-cpp-provider.js.map +1 -1
- package/dist/providers/nodetool-provider.d.ts +1 -1
- package/dist/providers/nodetool-provider.d.ts.map +1 -1
- package/dist/providers/nodetool-provider.js +0 -2
- package/dist/providers/nodetool-provider.js.map +1 -1
- package/dist/providers/ollama-provider.d.ts +2 -1
- package/dist/providers/ollama-provider.d.ts.map +1 -1
- package/dist/providers/ollama-provider.js +5 -0
- package/dist/providers/ollama-provider.js.map +1 -1
- package/dist/providers/openai-provider.d.ts +10 -2
- package/dist/providers/openai-provider.d.ts.map +1 -1
- package/dist/providers/openai-provider.js +63 -16
- package/dist/providers/openai-provider.js.map +1 -1
- package/dist/providers/openrouter-provider.d.ts +52 -1
- package/dist/providers/openrouter-provider.d.ts.map +1 -1
- package/dist/providers/openrouter-provider.js +372 -11
- package/dist/providers/openrouter-provider.js.map +1 -1
- package/dist/providers/provider-error.d.ts.map +1 -1
- package/dist/providers/provider-error.js +7 -1
- package/dist/providers/provider-error.js.map +1 -1
- package/dist/providers/provider-generations.d.ts +2 -2
- package/dist/providers/provider-generations.js +2 -2
- package/dist/providers/provider-request-log.d.ts.map +1 -1
- package/dist/providers/provider-request-log.js +18 -7
- package/dist/providers/provider-request-log.js.map +1 -1
- package/dist/providers/python-provider.d.ts +6 -1
- package/dist/providers/python-provider.d.ts.map +1 -1
- package/dist/providers/python-provider.js +30 -11
- package/dist/providers/python-provider.js.map +1 -1
- package/dist/providers/replicate-provider.d.ts +2 -1
- package/dist/providers/replicate-provider.d.ts.map +1 -1
- package/dist/providers/replicate-provider.js +22 -0
- package/dist/providers/replicate-provider.js.map +1 -1
- package/dist/providers/requesty-provider.d.ts +30 -0
- package/dist/providers/requesty-provider.d.ts.map +1 -0
- package/dist/providers/requesty-provider.js +87 -0
- package/dist/providers/requesty-provider.js.map +1 -0
- package/dist/providers/reve-provider.d.ts +2 -1
- package/dist/providers/reve-provider.d.ts.map +1 -1
- package/dist/providers/reve-provider.js +6 -0
- package/dist/providers/reve-provider.js.map +1 -1
- package/dist/providers/rodin-provider.d.ts +2 -1
- package/dist/providers/rodin-provider.d.ts.map +1 -1
- package/dist/providers/rodin-provider.js +6 -0
- package/dist/providers/rodin-provider.js.map +1 -1
- package/dist/providers/together-operations.d.ts +27 -0
- package/dist/providers/together-operations.d.ts.map +1 -0
- package/dist/providers/together-operations.js +95 -0
- package/dist/providers/together-operations.js.map +1 -0
- package/dist/providers/together-provider.d.ts +5 -8
- package/dist/providers/together-provider.d.ts.map +1 -1
- package/dist/providers/together-provider.js +54 -159
- package/dist/providers/together-provider.js.map +1 -1
- package/dist/providers/topaz-provider.d.ts +2 -1
- package/dist/providers/topaz-provider.d.ts.map +1 -1
- package/dist/providers/topaz-provider.js +5 -0
- package/dist/providers/topaz-provider.js.map +1 -1
- package/dist/providers/types.d.ts +38 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/useapi-provider.d.ts +2 -1
- package/dist/providers/useapi-provider.d.ts.map +1 -1
- package/dist/providers/useapi-provider.js +8 -0
- package/dist/providers/useapi-provider.js.map +1 -1
- package/dist/providers/vllm-provider.d.ts +2 -4
- package/dist/providers/vllm-provider.d.ts.map +1 -1
- package/dist/providers/vllm-provider.js +1 -5
- package/dist/providers/vllm-provider.js.map +1 -1
- package/dist/providers/voyage-provider.d.ts +2 -1
- package/dist/providers/voyage-provider.d.ts.map +1 -1
- package/dist/providers/voyage-provider.js +5 -0
- package/dist/providers/voyage-provider.js.map +1 -1
- package/dist/providers/xai-provider.d.ts +2 -0
- package/dist/providers/xai-provider.d.ts.map +1 -1
- package/dist/providers/xai-provider.js +8 -0
- package/dist/providers/xai-provider.js.map +1 -1
- package/dist/recommended-models.d.ts.map +1 -1
- package/dist/recommended-models.js +20 -0
- package/dist/recommended-models.js.map +1 -1
- package/dist/run-context.d.ts +20 -0
- package/dist/run-context.d.ts.map +1 -0
- package/dist/run-context.js +18 -0
- package/dist/run-context.js.map +1 -0
- package/dist/run-trace-context.d.ts +30 -0
- package/dist/run-trace-context.d.ts.map +1 -0
- package/dist/run-trace-context.js +80 -0
- package/dist/run-trace-context.js.map +1 -0
- package/dist/run-trace-processor.d.ts +65 -0
- package/dist/run-trace-processor.d.ts.map +1 -0
- package/dist/run-trace-processor.js +417 -0
- package/dist/run-trace-processor.js.map +1 -0
- package/dist/run-trace-serialization.d.ts +5 -0
- package/dist/run-trace-serialization.d.ts.map +1 -0
- package/dist/run-trace-serialization.js +113 -0
- package/dist/run-trace-serialization.js.map +1 -0
- package/dist/storage-workspace.d.ts +3 -0
- package/dist/storage-workspace.d.ts.map +1 -1
- package/dist/storage-workspace.js +32 -17
- package/dist/storage-workspace.js.map +1 -1
- package/dist/telemetry.d.ts +9 -3
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +53 -20
- package/dist/telemetry.js.map +1 -1
- package/dist/trace-exporters.d.ts +2 -29
- package/dist/trace-exporters.d.ts.map +1 -1
- package/dist/trace-exporters.js +10 -2
- package/dist/trace-exporters.js.map +1 -1
- package/dist/tracing-helpers.d.ts +15 -22
- package/dist/tracing-helpers.d.ts.map +1 -1
- package/dist/tracing-helpers.js +38 -6
- package/dist/tracing-helpers.js.map +1 -1
- package/dist/trim-video-window.d.ts +8 -0
- package/dist/trim-video-window.d.ts.map +1 -0
- package/dist/trim-video-window.js +50 -0
- package/dist/trim-video-window.js.map +1 -0
- package/package.json +19 -5
package/dist/context.js
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Port of src/nodetool/workflows/processing_context.py.
|
|
5
|
-
*
|
|
6
|
-
* Provides:
|
|
7
|
-
* - Message queue for emitting ProcessingMessages.
|
|
8
|
-
* - Cache get/set interface.
|
|
9
|
-
* - Output normalization (sanitize memory URIs, etc.).
|
|
10
|
-
* - Asset handling with pluggable storage adapters.
|
|
11
|
-
*/
|
|
12
|
-
import { buildAssetGenerationMetadata, isShortResourceId, packageAssetHttpPath, parsePackageAssetUri } from "@nodetool-ai/protocol";
|
|
1
|
+
import { getRunTraceScope } from "./run-trace-context.js";
|
|
2
|
+
import { withSpan } from "./tracing-helpers.js";
|
|
3
|
+
import { assetFileNameCandidates, buildAssetGenerationMetadata, isShortResourceId, packageAssetHttpPath, parsePackageAssetUri } from "@nodetool-ai/protocol";
|
|
13
4
|
import { AgentMemory } from "./agent-memory.js";
|
|
14
5
|
import { recordInvocationAsset, recordInvocationCost } from "./invocation-account.js";
|
|
15
6
|
import { VariableChannel } from "./variable-channel.js";
|
|
16
7
|
import { loadMediaRefBytes } from "./media-ref-bytes.js";
|
|
17
8
|
import { objectTrackingRequestSchema, parseObjectTrackingResult } from "./providers/object-tracking.js";
|
|
18
9
|
import { requestVideoToAudio, VIDEO_TO_AUDIO_TASK, VideoToAudioRequest } from "./providers/video-to-audio.js";
|
|
19
|
-
import { encodeRawImageRef } from "./image-codec.js";
|
|
10
|
+
import { encodeRawImageRef, validateImageEvidence } from "./image-codec.js";
|
|
20
11
|
import { extForImageMime, sniffImageMime } from "./providers/image-mime.js";
|
|
21
12
|
import { GenerationScopeError, isAuthoritativeProviderTerminalError, runWithGenerationReceipt } from "./generation-receipt.js";
|
|
22
13
|
import { generationRegistry } from "./generation-registry.js";
|
|
@@ -28,11 +19,6 @@ import { getNodeBuiltinSync } from "@nodetool-ai/config";
|
|
|
28
19
|
// lazily so this module loads in browser / Edge runtimes. `resolveWorkspacePath`
|
|
29
20
|
// and the `randomUUID` fallback both degrade gracefully when these are
|
|
30
21
|
// unavailable.
|
|
31
|
-
/**
|
|
32
|
-
* Secret values shorter than this are not recorded for masking: redacting a
|
|
33
|
-
* two-character value would blank unrelated text wherever it happens to occur.
|
|
34
|
-
*/
|
|
35
|
-
const MIN_MASKABLE_SECRET_LENGTH = 8;
|
|
36
22
|
/**
|
|
37
23
|
* ProcessingContext variable key carrying the provider/model the currently
|
|
38
24
|
* running agent loop is itself talking to. Every loop that executes tools
|
|
@@ -481,6 +467,11 @@ export class ProcessingContext {
|
|
|
481
467
|
_secretResolver = null;
|
|
482
468
|
/** Every secret value the resolver handed out this run (see {@link ProcessingContext.getResolvedSecretValues}). */
|
|
483
469
|
_resolvedSecrets = new Set();
|
|
470
|
+
/** Identity shared by all work beneath an app operation. */
|
|
471
|
+
appRunContext;
|
|
472
|
+
runTraceContext;
|
|
473
|
+
appRunCostAccount;
|
|
474
|
+
_appRunDocuments = new Map();
|
|
484
475
|
/** Fetch function used by HTTP helpers. */
|
|
485
476
|
_fetch;
|
|
486
477
|
/** Optional temporary URL resolver for stored assets. */
|
|
@@ -526,6 +517,12 @@ export class ProcessingContext {
|
|
|
526
517
|
this.assetOutputMode = opts.assetOutputMode ?? "native";
|
|
527
518
|
this.persistOutputAssets = opts.persistOutputAssets ?? true;
|
|
528
519
|
this._generationLifecycle = opts.generationLifecycle ?? null;
|
|
520
|
+
this.appRunContext = opts.appRunContext ? Object.freeze({ ...opts.appRunContext }) : null;
|
|
521
|
+
this.runTraceContext = opts.runTraceContext ?? getRunTraceScope() ?? null;
|
|
522
|
+
if (this.runTraceContext?.secretValues instanceof Set) {
|
|
523
|
+
this._resolvedSecrets = this.runTraceContext.secretValues;
|
|
524
|
+
}
|
|
525
|
+
this.appRunCostAccount = opts.appRunCostAccount ?? (this.appRunContext ? { llmCostUsd: 0, unpriced: false } : null);
|
|
529
526
|
this.cache = opts.cache ?? new MemoryCache();
|
|
530
527
|
this.storage = opts.storage ?? null;
|
|
531
528
|
this.assetStorage = opts.assetStorage ?? null;
|
|
@@ -575,12 +572,12 @@ export class ProcessingContext {
|
|
|
575
572
|
*/
|
|
576
573
|
copy(opts) {
|
|
577
574
|
const next = new ProcessingContext({
|
|
578
|
-
jobId: this.jobId,
|
|
575
|
+
jobId: opts?.jobId ?? this.jobId,
|
|
579
576
|
projectId: this.projectId,
|
|
580
|
-
workflowId: this.workflowId,
|
|
577
|
+
workflowId: opts?.workflowId !== undefined ? opts.workflowId : this.workflowId,
|
|
581
578
|
threadId: this.threadId,
|
|
582
579
|
userId: this.userId,
|
|
583
|
-
|
|
580
|
+
workspace: opts?.workspace !== undefined ? opts.workspace : this.workspace,
|
|
584
581
|
assetOutputMode: this.assetOutputMode,
|
|
585
582
|
persistOutputAssets: this.persistOutputAssets,
|
|
586
583
|
cache: this.cache,
|
|
@@ -596,7 +593,11 @@ export class ProcessingContext {
|
|
|
596
593
|
tempUrlResolver: this._tempUrlResolver ?? undefined,
|
|
597
594
|
modelInterfaces: this._modelInterfaces ?? undefined,
|
|
598
595
|
retainMessageQueue: this._retainMessageQueue,
|
|
599
|
-
triggerEvent: this.triggerEvent
|
|
596
|
+
triggerEvent: this.triggerEvent,
|
|
597
|
+
generationLifecycle: opts?.generationLifecycle ?? this._generationLifecycle ?? undefined,
|
|
598
|
+
appRunContext: opts?.appRunContext ?? this.appRunContext ?? undefined,
|
|
599
|
+
runTraceContext: opts?.runTraceContext ?? this.runTraceContext ?? undefined,
|
|
600
|
+
appRunCostAccount: !opts?.appRunContext || opts.appRunContext.appRunId === this.appRunContext?.appRunId ? this.appRunCostAccount ?? undefined : undefined
|
|
600
601
|
});
|
|
601
602
|
if (opts?.inheritMessageListeners !== false) {
|
|
602
603
|
for (const listener of this._messageListeners) {
|
|
@@ -606,6 +607,12 @@ export class ProcessingContext {
|
|
|
606
607
|
if (opts?.shareMemory) {
|
|
607
608
|
next.memory = this.memory;
|
|
608
609
|
}
|
|
610
|
+
if (!opts?.runTraceContext) {
|
|
611
|
+
next._resolvedSecrets = this._resolvedSecrets;
|
|
612
|
+
}
|
|
613
|
+
if (next.appRunContext?.appRunId === this.appRunContext?.appRunId) {
|
|
614
|
+
next._appRunDocuments = this._appRunDocuments;
|
|
615
|
+
}
|
|
609
616
|
next.signal = this.signal;
|
|
610
617
|
next._providerResolver = this._providerResolver;
|
|
611
618
|
next._modelInterfaces = this._modelInterfaces;
|
|
@@ -813,7 +820,7 @@ export class ProcessingContext {
|
|
|
813
820
|
if (!this._secretResolver)
|
|
814
821
|
return null;
|
|
815
822
|
const value = await this._secretResolver(key, this.userId);
|
|
816
|
-
if (value != null && value.length
|
|
823
|
+
if (value != null && value.length > 0) {
|
|
817
824
|
this._resolvedSecrets.add(value);
|
|
818
825
|
}
|
|
819
826
|
return value ?? null;
|
|
@@ -1041,6 +1048,26 @@ export class ProcessingContext {
|
|
|
1041
1048
|
getTotalCost() {
|
|
1042
1049
|
return this._totalCost;
|
|
1043
1050
|
}
|
|
1051
|
+
/** Null means at least one LLM call could not be priced. */
|
|
1052
|
+
getAppRunLlmCost() {
|
|
1053
|
+
return this.appRunCostAccount && !this.appRunCostAccount.unpriced
|
|
1054
|
+
? this.appRunCostAccount.llmCostUsd
|
|
1055
|
+
: null;
|
|
1056
|
+
}
|
|
1057
|
+
getAppRunDocuments() {
|
|
1058
|
+
return [...this._appRunDocuments.values()].map((reference) => ({
|
|
1059
|
+
...reference
|
|
1060
|
+
}));
|
|
1061
|
+
}
|
|
1062
|
+
recordAppRunDocument(kind, document) {
|
|
1063
|
+
if (this.appRunContext && document) {
|
|
1064
|
+
this._appRunDocuments.set(`${kind}:${document.id}`, {
|
|
1065
|
+
kind,
|
|
1066
|
+
id: document.id
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
return document;
|
|
1070
|
+
}
|
|
1044
1071
|
getOperationCosts() {
|
|
1045
1072
|
return this._operationCosts;
|
|
1046
1073
|
}
|
|
@@ -1199,7 +1226,8 @@ export class ProcessingContext {
|
|
|
1199
1226
|
/** Create a persisted sketch (image document) owned by the current user. */
|
|
1200
1227
|
async createImageDocument(args) {
|
|
1201
1228
|
const fn = this.requireModelInterface("createImageDocument");
|
|
1202
|
-
|
|
1229
|
+
const document = await fn({ userId: this.userId, ...args });
|
|
1230
|
+
return this.recordAppRunDocument("sketch", document);
|
|
1203
1231
|
}
|
|
1204
1232
|
/** Load a persisted timeline sequence owned by the current user. */
|
|
1205
1233
|
async getTimelineSequence(id) {
|
|
@@ -1209,12 +1237,14 @@ export class ProcessingContext {
|
|
|
1209
1237
|
/** Create a persisted timeline sequence owned by the current user. */
|
|
1210
1238
|
async createTimelineSequence(sequence) {
|
|
1211
1239
|
const fn = this.requireModelInterface("createTimelineSequence");
|
|
1212
|
-
|
|
1240
|
+
const document = await fn({ userId: this.userId, sequence });
|
|
1241
|
+
return this.recordAppRunDocument("timeline", document);
|
|
1213
1242
|
}
|
|
1214
1243
|
/** Replace a persisted timeline sequence's document. */
|
|
1215
1244
|
async updateTimelineSequence(id, sequence) {
|
|
1216
1245
|
const fn = this.requireModelInterface("updateTimelineSequence");
|
|
1217
|
-
|
|
1246
|
+
const document = await fn({ userId: this.userId, id, sequence });
|
|
1247
|
+
return this.recordAppRunDocument("timeline", document);
|
|
1218
1248
|
}
|
|
1219
1249
|
/** Load a persisted script owned by the current user. */
|
|
1220
1250
|
async getScript(id) {
|
|
@@ -1224,12 +1254,14 @@ export class ProcessingContext {
|
|
|
1224
1254
|
/** Create a persisted script owned by the current user. */
|
|
1225
1255
|
async createScript(args) {
|
|
1226
1256
|
const fn = this.requireModelInterface("createScript");
|
|
1227
|
-
|
|
1257
|
+
const document = await fn({ userId: this.userId, ...args });
|
|
1258
|
+
return this.recordAppRunDocument("script", document);
|
|
1228
1259
|
}
|
|
1229
1260
|
/** Replace a persisted script's document (and optional timeline link). */
|
|
1230
1261
|
async updateScript(id, args) {
|
|
1231
1262
|
const fn = this.requireModelInterface("updateScript");
|
|
1232
|
-
|
|
1263
|
+
const document = await fn({ userId: this.userId, id, ...args });
|
|
1264
|
+
return this.recordAppRunDocument("script", document);
|
|
1233
1265
|
}
|
|
1234
1266
|
/** Load a persisted storyboard owned by the current user. */
|
|
1235
1267
|
async getStoryboard(id) {
|
|
@@ -1249,12 +1281,14 @@ export class ProcessingContext {
|
|
|
1249
1281
|
/** Create a persisted storyboard owned by the current user. */
|
|
1250
1282
|
async createStoryboard(args) {
|
|
1251
1283
|
const fn = this.requireModelInterface("createStoryboard");
|
|
1252
|
-
|
|
1284
|
+
const document = await fn({ userId: this.userId, ...args });
|
|
1285
|
+
return this.recordAppRunDocument("storyboard", document);
|
|
1253
1286
|
}
|
|
1254
1287
|
/** Replace a persisted storyboard's document (and optional timeline link). */
|
|
1255
1288
|
async updateStoryboard(id, args) {
|
|
1256
1289
|
const fn = this.requireModelInterface("updateStoryboard");
|
|
1257
|
-
|
|
1290
|
+
const document = await fn({ userId: this.userId, id, ...args });
|
|
1291
|
+
return this.recordAppRunDocument("storyboard", document);
|
|
1258
1292
|
}
|
|
1259
1293
|
/** The current user's entity library, narrowed by the given filters. */
|
|
1260
1294
|
async listEntities(args = {}) {
|
|
@@ -1289,7 +1323,7 @@ export class ProcessingContext {
|
|
|
1289
1323
|
* `null` when that interface is not configured.
|
|
1290
1324
|
*/
|
|
1291
1325
|
async getAssetInfo(assetId) {
|
|
1292
|
-
const fn = this.
|
|
1326
|
+
const fn = this.modelInterfaces()?.getAssetInfo;
|
|
1293
1327
|
if (!fn) {
|
|
1294
1328
|
return null;
|
|
1295
1329
|
}
|
|
@@ -1507,8 +1541,8 @@ export class ProcessingContext {
|
|
|
1507
1541
|
* from the server's `/api/storage` route. Returns the bytes plus an `attempts`
|
|
1508
1542
|
* trail for callers that build detailed errors; `null` bytes means unresolved.
|
|
1509
1543
|
*/
|
|
1510
|
-
async resolveAssetBytes(assetId) {
|
|
1511
|
-
|
|
1544
|
+
async resolveAssetBytes(assetId, options) {
|
|
1545
|
+
let idCandidates = this.parseAssetIdCandidates(assetId);
|
|
1512
1546
|
const trimmed = assetId.trim();
|
|
1513
1547
|
const attempts = [];
|
|
1514
1548
|
const adapters = this.assetAdapters();
|
|
@@ -1595,6 +1629,33 @@ export class ProcessingContext {
|
|
|
1595
1629
|
}
|
|
1596
1630
|
}
|
|
1597
1631
|
}
|
|
1632
|
+
// An asset row names the stored file exactly (`<owner>/<id>.<ext>`), so a
|
|
1633
|
+
// reference that resolves to one is read by key and never by listing.
|
|
1634
|
+
let assetRowFound = false;
|
|
1635
|
+
if (this.hasModelInterface("getAssetInfo")) {
|
|
1636
|
+
let asset = null;
|
|
1637
|
+
const ownerPrefix = `${this.userId}/`;
|
|
1638
|
+
const rowIdCandidates = new Set(idCandidates);
|
|
1639
|
+
for (const candidate of idCandidates) {
|
|
1640
|
+
if (candidate.startsWith(ownerPrefix)) {
|
|
1641
|
+
rowIdCandidates.add(candidate.slice(ownerPrefix.length));
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
for (const candidate of rowIdCandidates) {
|
|
1645
|
+
asset = await this.getAssetInfo(candidate);
|
|
1646
|
+
if (asset) {
|
|
1647
|
+
break;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
if (asset) {
|
|
1651
|
+
assetRowFound = true;
|
|
1652
|
+
idCandidates = assetFileNameCandidates(asset.id, asset.content_type);
|
|
1653
|
+
}
|
|
1654
|
+
else if (options?.requireOwnedAsset) {
|
|
1655
|
+
attempts.push(`asset unavailable or not owned: ${trimmed}`);
|
|
1656
|
+
return { bytes: null, attempts };
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1598
1659
|
for (const adapter of adapters) {
|
|
1599
1660
|
for (const candidate of idCandidates) {
|
|
1600
1661
|
for (const key of this.assetKeysFor(candidate)) {
|
|
@@ -1606,9 +1667,10 @@ export class ProcessingContext {
|
|
|
1606
1667
|
}
|
|
1607
1668
|
// Extension-tolerant fallback: locate the stored file whose name starts
|
|
1608
1669
|
// with the id, since the URN extension may differ from the one on disk
|
|
1609
|
-
// (e.g. jpeg vs jpg). Only reached when the exact-key lookups all miss
|
|
1670
|
+
// (e.g. jpeg vs jpg). Only reached when the exact-key lookups all miss
|
|
1671
|
+
// and no asset row named the file.
|
|
1610
1672
|
const bareId = idCandidates[idCandidates.length - 1];
|
|
1611
|
-
if (bareId) {
|
|
1673
|
+
if (bareId && !assetRowFound) {
|
|
1612
1674
|
for (const prefix of this.assetListingPrefixes(bareId)) {
|
|
1613
1675
|
try {
|
|
1614
1676
|
const listing = await adapter.list(prefix);
|
|
@@ -1876,8 +1938,9 @@ export class ProcessingContext {
|
|
|
1876
1938
|
// Strict, anchored sanitizer for the URIs that may flow into the asset /
|
|
1877
1939
|
// storage retrieval path. Accept asset references, the browser-facing
|
|
1878
1940
|
// storage route, and URIs from configured storage adapters. External URLs
|
|
1879
|
-
// and unrecognized schemes stay out of the storage adapter.
|
|
1880
|
-
|
|
1941
|
+
// and unrecognized schemes stay out of the storage adapter. `:` is allowed
|
|
1942
|
+
// for named owner ids such as `projects/personal:<user_id>/assets/…`.
|
|
1943
|
+
const RESOLVABLE_URI_RE = /^(?:asset:\/\/|\/?api\/storage\/|(?:memory|file|s3|supabase):\/\/)[A-Za-z0-9._~/%:-]+$/;
|
|
1881
1944
|
const isResolvableUri = (uri) => RESOLVABLE_URI_RE.test(uri);
|
|
1882
1945
|
// An `asset://` reference that cannot be dereferenced must not reach the
|
|
1883
1946
|
// provider: providers `fetch` a non-data URI, and the SSRF guard then
|
|
@@ -2003,12 +2066,82 @@ export class ProcessingContext {
|
|
|
2003
2066
|
}
|
|
2004
2067
|
return resolved;
|
|
2005
2068
|
}
|
|
2069
|
+
async resolveRequiredImageEvidence(messages, required = []) {
|
|
2070
|
+
const evidence = new Map();
|
|
2071
|
+
const resolve = async (ref) => {
|
|
2072
|
+
const identity = ref.uri?.startsWith("data:")
|
|
2073
|
+
? "inline image"
|
|
2074
|
+
: ref.uri ?? ref.asset_id ?? "inline image";
|
|
2075
|
+
const cached = ref.uri ? evidence.get(ref.uri) : undefined;
|
|
2076
|
+
if (cached) {
|
|
2077
|
+
return cached;
|
|
2078
|
+
}
|
|
2079
|
+
try {
|
|
2080
|
+
const bytes = await loadMediaRefBytes(ref, this);
|
|
2081
|
+
if (!bytes?.length) {
|
|
2082
|
+
throw new Error("no image bytes");
|
|
2083
|
+
}
|
|
2084
|
+
const mimeType = sniffImageMime(bytes);
|
|
2085
|
+
if (!mimeType ||
|
|
2086
|
+
!["image/png", "image/jpeg", "image/webp", "image/gif"].includes(mimeType)) {
|
|
2087
|
+
throw new Error("unsupported image format");
|
|
2088
|
+
}
|
|
2089
|
+
await validateImageEvidence(bytes);
|
|
2090
|
+
const block = {
|
|
2091
|
+
type: "image_url",
|
|
2092
|
+
image: {
|
|
2093
|
+
uri: `data:${mimeType};base64,${Buffer.from(bytes).toString("base64")}`,
|
|
2094
|
+
mimeType
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
if (ref.uri) {
|
|
2098
|
+
evidence.set(ref.uri, block);
|
|
2099
|
+
}
|
|
2100
|
+
return block;
|
|
2101
|
+
}
|
|
2102
|
+
catch (error) {
|
|
2103
|
+
throw new Error(`Required image evidence unavailable: ${identity} (${error instanceof Error ? error.message : String(error)})`);
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
for (const uri of required) {
|
|
2107
|
+
await resolve({ type: "image", uri });
|
|
2108
|
+
}
|
|
2109
|
+
const result = [];
|
|
2110
|
+
for (const message of messages) {
|
|
2111
|
+
if (!Array.isArray(message.content)) {
|
|
2112
|
+
result.push(message);
|
|
2113
|
+
continue;
|
|
2114
|
+
}
|
|
2115
|
+
const content = [];
|
|
2116
|
+
let imageIndex = 0;
|
|
2117
|
+
for (const part of message.content) {
|
|
2118
|
+
if (part.type === "image_url") {
|
|
2119
|
+
imageIndex++;
|
|
2120
|
+
content.push(await resolve(part.image), {
|
|
2121
|
+
type: "text",
|
|
2122
|
+
text: `[required image ${part.image.uri && !part.image.uri.startsWith("data:")
|
|
2123
|
+
? part.image.uri
|
|
2124
|
+
: `inline image ${imageIndex}`}]`
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
else {
|
|
2128
|
+
content.push(part);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
result.push({ ...message, content });
|
|
2132
|
+
}
|
|
2133
|
+
return result;
|
|
2134
|
+
}
|
|
2006
2135
|
async dispatchCapability(provider, req) {
|
|
2007
2136
|
const params = req.params ?? {};
|
|
2008
2137
|
switch (req.capability) {
|
|
2009
2138
|
case "generate_message":
|
|
2010
2139
|
return provider.generateMessageTraced({
|
|
2011
|
-
messages:
|
|
2140
|
+
messages: params.strict_image_evidence === true
|
|
2141
|
+
? await this.resolveRequiredImageEvidence(params.messages ?? [], Array.isArray(params.required_images)
|
|
2142
|
+
? params.required_images.filter(isString)
|
|
2143
|
+
: [])
|
|
2144
|
+
: await this.resolveMessageMediaUris(params.messages ?? []),
|
|
2012
2145
|
model: req.model,
|
|
2013
2146
|
tools: params.tools,
|
|
2014
2147
|
toolChoice: params.tool_choice,
|
|
@@ -2163,6 +2296,22 @@ export class ProcessingContext {
|
|
|
2163
2296
|
return provider.vectorizeImage(params.image, {
|
|
2164
2297
|
model: { id: req.model, name: req.model, provider: req.provider }
|
|
2165
2298
|
});
|
|
2299
|
+
case "estimate_depth":
|
|
2300
|
+
return provider.estimateDepth(params.image, {
|
|
2301
|
+
model: { id: req.model, name: req.model, provider: req.provider },
|
|
2302
|
+
signal: isAbortSignal(params.signal) ? params.signal : this.signal
|
|
2303
|
+
});
|
|
2304
|
+
case "rerank":
|
|
2305
|
+
return provider.rerank({
|
|
2306
|
+
model: req.model,
|
|
2307
|
+
query: isString(params.query) ? params.query : "",
|
|
2308
|
+
// Every entry stays, so result indices point into the caller's list.
|
|
2309
|
+
documents: Array.isArray(params.documents)
|
|
2310
|
+
? params.documents.map((doc) => (isString(doc) ? doc : String(doc)))
|
|
2311
|
+
: [],
|
|
2312
|
+
topK: isNumber(params.top_k) ? params.top_k : undefined,
|
|
2313
|
+
signal: isAbortSignal(params.signal) ? params.signal : this.signal
|
|
2314
|
+
});
|
|
2166
2315
|
case "track_object": {
|
|
2167
2316
|
const request = objectTrackingRequestSchema.parse(params);
|
|
2168
2317
|
if (!provider.getCapabilities().includes("track_object")) {
|
|
@@ -2175,12 +2324,18 @@ export class ProcessingContext {
|
|
|
2175
2324
|
throw new Error("The tracking source video could not be loaded.");
|
|
2176
2325
|
}
|
|
2177
2326
|
signal.throwIfAborted();
|
|
2178
|
-
const output = await provider.trackObject(video, {
|
|
2327
|
+
const output = await provider.trackObject(video, {
|
|
2328
|
+
...request,
|
|
2329
|
+
model: req.model,
|
|
2330
|
+
signal
|
|
2331
|
+
});
|
|
2179
2332
|
signal.throwIfAborted();
|
|
2180
2333
|
return parseObjectTrackingResult(output, request);
|
|
2181
2334
|
}
|
|
2182
2335
|
case "video_to_video": {
|
|
2183
|
-
const signal = isAbortSignal(params.signal)
|
|
2336
|
+
const signal = isAbortSignal(params.signal)
|
|
2337
|
+
? params.signal
|
|
2338
|
+
: this.signal;
|
|
2184
2339
|
const referenceImages = coerceByteList(params.reference_images ?? params.referenceImages);
|
|
2185
2340
|
const referenceAssetIds = coerceStringList(params.reference_asset_ids ?? params.referenceAssetIds);
|
|
2186
2341
|
signal.throwIfAborted();
|
|
@@ -2378,137 +2533,174 @@ export class ProcessingContext {
|
|
|
2378
2533
|
*/
|
|
2379
2534
|
async runGenerationWith(req, call, opts) {
|
|
2380
2535
|
const id = req.id ?? randomUUID();
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
const accepted = await accept?.({ generationId: id, request: req });
|
|
2396
|
-
if (accepted?.skipProvider) {
|
|
2397
|
-
throw new GenerationAlreadyAcceptedError(accepted.existingGenerationId ?? id);
|
|
2398
|
-
}
|
|
2399
|
-
durableAccepted = accepted?.durable === true;
|
|
2400
|
-
generationRegistry.register(id, {
|
|
2401
|
-
userId: this.userId,
|
|
2402
|
-
abort: () => controller.abort(new Error("Generation cancelled"))
|
|
2403
|
-
});
|
|
2404
|
-
this.emitPrediction("running", req, id, null, undefined, startedAt, {
|
|
2405
|
-
origin
|
|
2406
|
-
});
|
|
2407
|
-
const provider = opts?.withoutProvider
|
|
2408
|
-
? null
|
|
2409
|
-
: await this.getProvider(req.provider);
|
|
2410
|
-
const { value: output, receipt: recorded, providerResult } = await runWithGenerationReceipt(() => call(provider, signal), {
|
|
2411
|
-
providerRequestOptions: accepted ?? undefined,
|
|
2412
|
-
onProviderRequestAccepted: opts?.onProviderRequestAccepted ??
|
|
2413
|
-
this._generationLifecycle?.onProviderRequestAccepted,
|
|
2414
|
-
onProviderRequestBound: opts?.onProviderRequestBound ??
|
|
2415
|
-
this._generationLifecycle?.onProviderRequestBound
|
|
2416
|
-
});
|
|
2417
|
-
const encoded = encodedAudioOf(output);
|
|
2418
|
-
const persistedAssets = req.persist
|
|
2419
|
-
? await this.persistGenerationBytes(id, req, encoded ? [encoded.data] : mediaBuffers(output), encoded?.mimeType)
|
|
2420
|
-
: [];
|
|
2421
|
-
const assets = persistedAssets.filter((asset) => asset !== null);
|
|
2422
|
-
const late = opts?.receiptAfterPersist?.() ?? null;
|
|
2423
|
-
const receipt = late || recorded ? { ...(recorded ?? {}), ...(late ?? {}) } : null;
|
|
2424
|
-
const assetIds = assets
|
|
2425
|
-
.map((asset) => asset.asset_id)
|
|
2426
|
-
.filter((assetId) => typeof assetId === "string");
|
|
2427
|
-
const durableAssetIds = persistedAssets.map((asset) => asset?.asset_id ?? null);
|
|
2536
|
+
return withSpan("generation", { "generation.id": id, "generation.provider": req.provider, "generation.kind": req.capability }, async () => {
|
|
2537
|
+
const startedAt = Date.now();
|
|
2538
|
+
const origin = this.generationOrigin(req, req.origin);
|
|
2539
|
+
const controller = new AbortController();
|
|
2540
|
+
const signals = [controller.signal, this.signal];
|
|
2541
|
+
if (req.signal)
|
|
2542
|
+
signals.push(req.signal);
|
|
2543
|
+
const signal = AbortSignal.any(signals);
|
|
2544
|
+
const accept = opts?.onGenerationAccepted ??
|
|
2545
|
+
this._generationLifecycle?.onGenerationAccepted;
|
|
2546
|
+
const terminal = opts?.onGenerationTerminal ??
|
|
2547
|
+
this._generationLifecycle?.onGenerationTerminal;
|
|
2548
|
+
let durableAccepted = false;
|
|
2549
|
+
let terminalAttempted = false;
|
|
2428
2550
|
try {
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2551
|
+
const accepted = await accept?.({ generationId: id, request: req });
|
|
2552
|
+
if (accepted?.skipProvider) {
|
|
2553
|
+
throw new GenerationAlreadyAcceptedError(accepted.existingGenerationId ?? id);
|
|
2554
|
+
}
|
|
2555
|
+
durableAccepted = accepted?.durable === true;
|
|
2556
|
+
generationRegistry.register(id, {
|
|
2557
|
+
userId: this.userId,
|
|
2558
|
+
abort: () => controller.abort(new Error("Generation cancelled"))
|
|
2437
2559
|
});
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
this.emit({
|
|
2441
|
-
type: "log_update",
|
|
2442
|
-
node_id: req.nodeId ?? "",
|
|
2443
|
-
node_name: req.nodeId ?? "",
|
|
2444
|
-
workflow_id: req.workflowId ?? this.workflowId ?? null,
|
|
2445
|
-
severity: "warning",
|
|
2446
|
-
content: `Generation ${id} completed but durable finalization will be recovered: ${terminalError instanceof Error ? terminalError.message : String(terminalError)}`
|
|
2560
|
+
this.emitPrediction("running", req, id, null, undefined, startedAt, {
|
|
2561
|
+
origin
|
|
2447
2562
|
});
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2563
|
+
const provider = opts?.withoutProvider
|
|
2564
|
+
? null
|
|
2565
|
+
: await this.getProvider(req.provider);
|
|
2566
|
+
const { value: output, receipt: recorded, providerResult } = await runWithGenerationReceipt(() => call(provider, signal), {
|
|
2567
|
+
providerRequestOptions: accepted ?? undefined,
|
|
2568
|
+
onProviderRequestAccepted: opts?.onProviderRequestAccepted ??
|
|
2569
|
+
this._generationLifecycle?.onProviderRequestAccepted,
|
|
2570
|
+
onProviderRequestBound: opts?.onProviderRequestBound ??
|
|
2571
|
+
this._generationLifecycle?.onProviderRequestBound
|
|
2572
|
+
});
|
|
2573
|
+
const encoded = encodedAudioOf(output);
|
|
2574
|
+
const persistedAssets = req.persist
|
|
2575
|
+
? await this.persistGenerationBytes(id, req, encoded ? [encoded.data] : mediaBuffers(output), encoded?.mimeType)
|
|
2576
|
+
: [];
|
|
2577
|
+
const assets = persistedAssets.filter((asset) => asset !== null);
|
|
2578
|
+
let delivery;
|
|
2579
|
+
if (opts?.finalizeOutput) {
|
|
2580
|
+
try {
|
|
2581
|
+
delivery = await opts.finalizeOutput(output, assets);
|
|
2582
|
+
}
|
|
2583
|
+
catch (error) {
|
|
2584
|
+
delivery = {
|
|
2585
|
+
status: "failed",
|
|
2586
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
const deliveryFields = {};
|
|
2591
|
+
if (delivery) {
|
|
2592
|
+
deliveryFields.delivery = delivery;
|
|
2593
|
+
}
|
|
2594
|
+
const late = opts?.receiptAfterPersist?.() ?? null;
|
|
2595
|
+
const receipt = late || recorded ? { ...(recorded ?? {}), ...(late ?? {}) } : null;
|
|
2596
|
+
const assetIds = assets
|
|
2597
|
+
.map((asset) => asset.asset_id)
|
|
2598
|
+
.filter((assetId) => typeof assetId === "string");
|
|
2599
|
+
const durableAssetIds = persistedAssets.map((asset) => asset?.asset_id ?? null);
|
|
2481
2600
|
try {
|
|
2482
2601
|
terminalAttempted = true;
|
|
2483
2602
|
await terminal?.({
|
|
2484
2603
|
generationId: id,
|
|
2485
2604
|
request: req,
|
|
2486
|
-
status:
|
|
2487
|
-
|
|
2488
|
-
: "recovering",
|
|
2489
|
-
output: providerResult,
|
|
2490
|
-
error: message,
|
|
2605
|
+
status: "completed",
|
|
2606
|
+
output: providerResult ?? output,
|
|
2491
2607
|
receipt,
|
|
2492
|
-
assetIds:
|
|
2608
|
+
assetIds: durableAssetIds,
|
|
2609
|
+
...deliveryFields
|
|
2493
2610
|
});
|
|
2494
2611
|
}
|
|
2495
|
-
catch {
|
|
2496
|
-
|
|
2497
|
-
|
|
2612
|
+
catch (terminalError) {
|
|
2613
|
+
this.emit({
|
|
2614
|
+
type: "log_update",
|
|
2615
|
+
node_id: req.nodeId ?? "",
|
|
2616
|
+
node_name: req.nodeId ?? "",
|
|
2617
|
+
workflow_id: req.workflowId ?? this.workflowId ?? null,
|
|
2618
|
+
severity: "warning",
|
|
2619
|
+
content: `Generation ${id} completed but durable finalization will be recovered: ${terminalError instanceof Error ? terminalError.message : String(terminalError)}`
|
|
2620
|
+
});
|
|
2498
2621
|
}
|
|
2622
|
+
this.emitPrediction("completed", req, id, delivery ? { delivery } : generationResultData(req.capability, output), undefined, startedAt, { origin, asset_ids: assetIds, receipt });
|
|
2623
|
+
generationRegistry.settle(id, {
|
|
2624
|
+
status: "completed",
|
|
2625
|
+
asset_ids: assetIds,
|
|
2626
|
+
receipt,
|
|
2627
|
+
...deliveryFields
|
|
2628
|
+
});
|
|
2629
|
+
return {
|
|
2630
|
+
id,
|
|
2631
|
+
output,
|
|
2632
|
+
assets,
|
|
2633
|
+
receipt,
|
|
2634
|
+
duration_ms: Date.now() - startedAt,
|
|
2635
|
+
...deliveryFields
|
|
2636
|
+
};
|
|
2499
2637
|
}
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
receipt
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2638
|
+
catch (error) {
|
|
2639
|
+
const cause = error instanceof GenerationScopeError ? error.cause : error;
|
|
2640
|
+
const receipt = error instanceof GenerationScopeError ? error.receipt : null;
|
|
2641
|
+
const providerResult = error instanceof GenerationScopeError
|
|
2642
|
+
? error.providerResult
|
|
2643
|
+
: undefined;
|
|
2644
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
2645
|
+
if (cause instanceof GenerationAlreadyAcceptedError) {
|
|
2646
|
+
throw cause;
|
|
2647
|
+
}
|
|
2648
|
+
const status = signal.aborted
|
|
2649
|
+
? durableAccepted
|
|
2650
|
+
? "running"
|
|
2651
|
+
: "cancelled"
|
|
2652
|
+
: durableAccepted && !isAuthoritativeProviderTerminalError(cause)
|
|
2653
|
+
? "running"
|
|
2654
|
+
: "failed";
|
|
2655
|
+
if (durableAccepted && !terminalAttempted) {
|
|
2656
|
+
try {
|
|
2657
|
+
terminalAttempted = true;
|
|
2658
|
+
await terminal?.({
|
|
2659
|
+
generationId: id,
|
|
2660
|
+
request: req,
|
|
2661
|
+
status: isAuthoritativeProviderTerminalError(cause)
|
|
2662
|
+
? "failed"
|
|
2663
|
+
: "recovering",
|
|
2664
|
+
output: providerResult,
|
|
2665
|
+
error: message,
|
|
2666
|
+
receipt,
|
|
2667
|
+
assetIds: []
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
catch {
|
|
2671
|
+
// The provider observation remains authoritative. The durable row
|
|
2672
|
+
// can be finalized by the recovery worker.
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
if (this.appRunContext && !durableAccepted && !terminalAttempted) {
|
|
2676
|
+
try {
|
|
2677
|
+
terminalAttempted = true;
|
|
2678
|
+
await terminal?.({
|
|
2679
|
+
generationId: id,
|
|
2680
|
+
request: req,
|
|
2681
|
+
status: signal.aborted ? "cancelled" : "failed",
|
|
2682
|
+
error: message,
|
|
2683
|
+
receipt,
|
|
2684
|
+
assetIds: []
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
catch {
|
|
2688
|
+
// Preserve the provider failure; the live ledger can retry its write.
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
this.emitPrediction(status, req, id, null, message, startedAt, {
|
|
2692
|
+
origin,
|
|
2693
|
+
receipt
|
|
2694
|
+
});
|
|
2695
|
+
generationRegistry.settle(id, {
|
|
2696
|
+
status,
|
|
2697
|
+
error: message,
|
|
2698
|
+
asset_ids: [],
|
|
2699
|
+
receipt
|
|
2700
|
+
});
|
|
2701
|
+
throw cause;
|
|
2702
|
+
}
|
|
2703
|
+
});
|
|
2512
2704
|
}
|
|
2513
2705
|
/**
|
|
2514
2706
|
* Save a generation's media output as assets, stamping the generation id
|
|
@@ -2763,6 +2955,22 @@ export class ProcessingContext {
|
|
|
2763
2955
|
// retried by the recovery worker.
|
|
2764
2956
|
}
|
|
2765
2957
|
}
|
|
2958
|
+
if (this.appRunContext && !durableAccepted && !terminalAttempted) {
|
|
2959
|
+
try {
|
|
2960
|
+
terminalAttempted = true;
|
|
2961
|
+
await terminal?.({
|
|
2962
|
+
generationId: id,
|
|
2963
|
+
request: req,
|
|
2964
|
+
status: signal.aborted ? "cancelled" : "failed",
|
|
2965
|
+
error: message,
|
|
2966
|
+
receipt,
|
|
2967
|
+
assetIds: []
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
catch {
|
|
2971
|
+
// Preserve the provider failure; the live ledger can retry its write.
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2766
2974
|
this.emitPrediction(status, req, id, null, message, startedAt, {
|
|
2767
2975
|
origin,
|
|
2768
2976
|
receipt
|