deepline 0.1.12 → 0.1.20
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 +14 -6
- package/dist/cli/index.js +1346 -717
- package/dist/cli/index.mjs +1342 -713
- package/dist/index.d.mts +199 -23
- package/dist/index.d.ts +199 -23
- package/dist/index.js +221 -14
- package/dist/index.mjs +221 -14
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +214 -77
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +85 -60
- package/dist/repo/apps/play-runner-workers/src/entry.ts +385 -66
- package/dist/repo/sdk/src/client.ts +237 -0
- package/dist/repo/sdk/src/config.ts +125 -8
- package/dist/repo/sdk/src/http.ts +29 -5
- package/dist/repo/sdk/src/play.ts +19 -36
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
- package/dist/repo/sdk/src/types.ts +25 -0
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
- package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
- package/dist/repo/shared_libs/plays/dataset.ts +28 -0
- package/dist/repo/shared_libs/plays/row-identity.ts +59 -4
- package/package.json +5 -4
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
- package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
- package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
- package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
- package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
- package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
- package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
- package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
- package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
- package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
- package/dist/repo/sdk/src/cli/index.ts +0 -148
- package/dist/repo/sdk/src/cli/progress.ts +0 -149
- package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
- package/dist/repo/sdk/src/cli/trace.ts +0 -61
- package/dist/repo/sdk/src/cli/utils.ts +0 -145
- package/dist/repo/sdk/src/compat.ts +0 -77
- package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
- package/dist/repo/shared_libs/observability/tracing.ts +0 -98
- package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
- package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
- package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
- package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
- package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
- package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
- package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
- package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
- package/dist/repo/shared_libs/plays/definition.ts +0 -264
- package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
- package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
- package/dist/repo/shared_libs/temporal/constants.ts +0 -39
- package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
} from '../../../shared_libs/plays/row-identity';
|
|
64
64
|
import {
|
|
65
65
|
getCompiledPipelineSubsteps,
|
|
66
|
+
flattenStaticPipeline,
|
|
66
67
|
resolveSheetContractForTableNamespace,
|
|
67
68
|
sqlSafePlayColumnName,
|
|
68
69
|
type PlayStaticPipeline,
|
|
@@ -204,11 +205,18 @@ type WorkerEnv = {
|
|
|
204
205
|
* loud error. Loud failures > silent fallbacks.
|
|
205
206
|
*/
|
|
206
207
|
HARNESS?: import('../../play-harness-worker/src/rpc-types').PlayHarnessRpc;
|
|
208
|
+
VERCEL_PROTECTION_BYPASS_TOKEN?: string;
|
|
207
209
|
};
|
|
208
210
|
|
|
209
211
|
let cachedRuntimeApiBinding: WorkerEnv['RUNTIME_API'] | null = null;
|
|
212
|
+
let cachedRuntimeApiVercelBypassToken: string | null = null;
|
|
210
213
|
function captureRuntimeApiBinding(env: WorkerEnv): void {
|
|
211
214
|
cachedRuntimeApiBinding = env.RUNTIME_API ?? null;
|
|
215
|
+
cachedRuntimeApiVercelBypassToken =
|
|
216
|
+
typeof env.VERCEL_PROTECTION_BYPASS_TOKEN === 'string' &&
|
|
217
|
+
env.VERCEL_PROTECTION_BYPASS_TOKEN.trim()
|
|
218
|
+
? env.VERCEL_PROTECTION_BYPASS_TOKEN.trim()
|
|
219
|
+
: null;
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
let cachedCoordinatorBinding: WorkerEnv['COORDINATOR'] | null = null;
|
|
@@ -288,22 +296,20 @@ async function probeHarnessOnce(
|
|
|
288
296
|
}
|
|
289
297
|
}
|
|
290
298
|
/**
|
|
291
|
-
* Routes runtime API requests through the in-process RUNTIME_API binding
|
|
292
|
-
*
|
|
299
|
+
* Routes runtime API requests through the in-process RUNTIME_API binding when
|
|
300
|
+
* Cloudflare exposes the coordinator WorkerEntrypoint export. Some workflow
|
|
301
|
+
* execution paths do not expose those exports; there we keep the older public
|
|
302
|
+
* fetch transport so the play still reaches the same authenticated handler.
|
|
293
303
|
*/
|
|
294
304
|
const RUNTIME_API_TIMEOUT_MS = 30_000;
|
|
295
305
|
const RUNTIME_API_PLAY_RUN_TIMEOUT_MS = 75_000;
|
|
306
|
+
let loggedMissingRuntimeApiBinding = false;
|
|
296
307
|
|
|
297
308
|
async function fetchRuntimeApi(
|
|
298
309
|
baseUrl: string,
|
|
299
310
|
path: string,
|
|
300
311
|
init: RequestInit,
|
|
301
312
|
): Promise<Response> {
|
|
302
|
-
if (!cachedRuntimeApiBinding) {
|
|
303
|
-
throw new Error(
|
|
304
|
-
`[play-harness] RUNTIME_API binding missing — coordinator did not wire it before invoking the play. path=${path}`,
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
313
|
const timeoutMs =
|
|
308
314
|
path === '/api/v2/plays/run'
|
|
309
315
|
? RUNTIME_API_PLAY_RUN_TIMEOUT_MS
|
|
@@ -313,16 +319,25 @@ async function fetchRuntimeApi(
|
|
|
313
319
|
try {
|
|
314
320
|
const mergedInit: RequestInit = {
|
|
315
321
|
...init,
|
|
322
|
+
headers: runtimeApiHeaders(init.headers, cachedRuntimeApiBinding == null),
|
|
316
323
|
signal: controller.signal,
|
|
317
324
|
};
|
|
318
|
-
|
|
319
|
-
|
|
325
|
+
if (!cachedRuntimeApiBinding) {
|
|
326
|
+
if (!loggedMissingRuntimeApiBinding) {
|
|
327
|
+
loggedMissingRuntimeApiBinding = true;
|
|
328
|
+
console.warn(
|
|
329
|
+
`[play-harness] RUNTIME_API binding missing; using public runtime API transport. path=${path}`,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
return await fetch(`${baseUrl.replace(/\/$/, '')}${path}`, mergedInit);
|
|
333
|
+
}
|
|
334
|
+
return await cachedRuntimeApiBinding.fetch(
|
|
335
|
+
new Request(`${baseUrl.replace(/\/$/, '')}${path}`, mergedInit),
|
|
320
336
|
);
|
|
321
|
-
return res;
|
|
322
337
|
} catch (err) {
|
|
323
338
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
324
339
|
throw new Error(
|
|
325
|
-
`[play-harness]
|
|
340
|
+
`[play-harness] runtime API call timed out after ${timeoutMs}ms. path=${path} baseUrl=${baseUrl}`,
|
|
326
341
|
);
|
|
327
342
|
}
|
|
328
343
|
throw err;
|
|
@@ -331,10 +346,29 @@ async function fetchRuntimeApi(
|
|
|
331
346
|
}
|
|
332
347
|
}
|
|
333
348
|
|
|
349
|
+
function runtimeApiHeaders(
|
|
350
|
+
headers: HeadersInit | undefined,
|
|
351
|
+
includeVercelBypass: boolean,
|
|
352
|
+
): Headers {
|
|
353
|
+
const next = new Headers(headers);
|
|
354
|
+
if (includeVercelBypass) {
|
|
355
|
+
const bypassToken = cachedVercelProtectionBypassToken();
|
|
356
|
+
if (bypassToken) {
|
|
357
|
+
next.set('x-vercel-protection-bypass', bypassToken);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return next;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function cachedVercelProtectionBypassToken(): string | null {
|
|
364
|
+
return cachedRuntimeApiVercelBypassToken;
|
|
365
|
+
}
|
|
366
|
+
|
|
334
367
|
const WORKER_PLAY_CALL_LIMITS = {
|
|
335
368
|
maxPlayCallDepth: 6,
|
|
336
|
-
maxPlayCallCount:
|
|
337
|
-
maxChildPlayCallsPerParent:
|
|
369
|
+
maxPlayCallCount: 1_000,
|
|
370
|
+
maxChildPlayCallsPerParent: 1_000,
|
|
371
|
+
maxConcurrentPlayCalls: 16,
|
|
338
372
|
};
|
|
339
373
|
|
|
340
374
|
/**
|
|
@@ -350,6 +384,20 @@ function makeWorkerDataset<T extends Record<string, unknown>>(
|
|
|
350
384
|
count?: number;
|
|
351
385
|
datasetKind?: 'csv' | 'map';
|
|
352
386
|
cacheSummary?: string | null;
|
|
387
|
+
workProgress?: {
|
|
388
|
+
total: number;
|
|
389
|
+
executed: number;
|
|
390
|
+
reused: number;
|
|
391
|
+
skipped: number;
|
|
392
|
+
pending: number;
|
|
393
|
+
failed: number;
|
|
394
|
+
degraded?: boolean;
|
|
395
|
+
duplicates?: {
|
|
396
|
+
exact?: number;
|
|
397
|
+
semantic?: number;
|
|
398
|
+
rejected?: number;
|
|
399
|
+
};
|
|
400
|
+
};
|
|
353
401
|
},
|
|
354
402
|
): T[] & {
|
|
355
403
|
count(): Promise<number>;
|
|
@@ -363,6 +411,7 @@ function makeWorkerDataset<T extends Record<string, unknown>>(
|
|
|
363
411
|
const count = Math.max(0, Math.floor(options?.count ?? rows.length));
|
|
364
412
|
const datasetKind = options?.datasetKind ?? 'map';
|
|
365
413
|
const cacheSummary = options?.cacheSummary ?? null;
|
|
414
|
+
const workProgress = options?.workProgress;
|
|
366
415
|
// Build the array result. JSON.stringify on arrays calls toJSON only if
|
|
367
416
|
// present on the array itself — we attach below. The dataset metadata is
|
|
368
417
|
// also exposed via own properties so plays can `enriched.count()` etc.
|
|
@@ -415,6 +464,10 @@ function makeWorkerDataset<T extends Record<string, unknown>>(
|
|
|
415
464
|
value: cacheSummary,
|
|
416
465
|
enumerable: false,
|
|
417
466
|
});
|
|
467
|
+
Object.defineProperty(arr, '__deeplineWorkProgress', {
|
|
468
|
+
value: workProgress,
|
|
469
|
+
enumerable: false,
|
|
470
|
+
});
|
|
418
471
|
// Plays often `return { rows: dataset, count: N }`. JSON.stringify on the
|
|
419
472
|
// array would normally produce `[row, row, ...]` — we want the dataset
|
|
420
473
|
// envelope shape instead so assertions seeing `result.rows.columns` pass.
|
|
@@ -435,6 +488,9 @@ function makeWorkerDataset<T extends Record<string, unknown>>(
|
|
|
435
488
|
preview: plainRows,
|
|
436
489
|
tableNamespace: name,
|
|
437
490
|
...(cacheSummary ? { cacheSummary } : {}),
|
|
491
|
+
...(workProgress
|
|
492
|
+
? { _metadata: { workProgress } }
|
|
493
|
+
: {}),
|
|
438
494
|
};
|
|
439
495
|
},
|
|
440
496
|
enumerable: false,
|
|
@@ -454,6 +510,7 @@ type RunnerEvent =
|
|
|
454
510
|
| { type: 'error'; message: string; stack?: string; ts: number };
|
|
455
511
|
|
|
456
512
|
type WorkflowRunOutput = {
|
|
513
|
+
playName: string;
|
|
457
514
|
result: unknown;
|
|
458
515
|
outputRows: number;
|
|
459
516
|
durationMs: number;
|
|
@@ -469,7 +526,12 @@ function makeRequestId(): string {
|
|
|
469
526
|
}
|
|
470
527
|
|
|
471
528
|
function publicCsvInputRow<T extends Record<string, unknown>>(row: T): T {
|
|
472
|
-
|
|
529
|
+
const stripped = stripCsvProjectedFields(row) as Record<string, unknown>;
|
|
530
|
+
return Object.fromEntries(
|
|
531
|
+
Object.entries(stripped).filter(
|
|
532
|
+
([fieldName]) => !fieldName.startsWith('__deepline'),
|
|
533
|
+
),
|
|
534
|
+
) as T;
|
|
473
535
|
}
|
|
474
536
|
|
|
475
537
|
/**
|
|
@@ -577,6 +639,7 @@ async function postRuntimeApiBestEffort(
|
|
|
577
639
|
async function submitChildPlayThroughCoordinator(input: {
|
|
578
640
|
req: RunRequest;
|
|
579
641
|
body: unknown;
|
|
642
|
+
allowInline?: boolean;
|
|
580
643
|
}): Promise<{
|
|
581
644
|
workflowId?: string;
|
|
582
645
|
runId?: string;
|
|
@@ -588,7 +651,7 @@ async function submitChildPlayThroughCoordinator(input: {
|
|
|
588
651
|
logs?: string[];
|
|
589
652
|
timings?: Array<{ phase: string; ms: number }>;
|
|
590
653
|
}> {
|
|
591
|
-
if (cachedCoordinatorBinding) {
|
|
654
|
+
if (cachedCoordinatorBinding && input.allowInline !== false) {
|
|
592
655
|
if (!isRecord(input.body)) {
|
|
593
656
|
throw new Error('ctx.runPlay child submit requires an object body.');
|
|
594
657
|
}
|
|
@@ -714,6 +777,11 @@ function childPlayEventKey(input: { key: string; workflowId: string }): string {
|
|
|
714
777
|
return `child_play_${hashChildPlayEventKey(`${input.key}:${input.workflowId}`)}_${readableKey}`;
|
|
715
778
|
}
|
|
716
779
|
|
|
780
|
+
function workflowTimeoutFromMs(timeoutMs: number): string {
|
|
781
|
+
const seconds = Math.max(1, Math.ceil(timeoutMs / 1000));
|
|
782
|
+
return `${seconds} second${seconds === 1 ? '' : 's'}`;
|
|
783
|
+
}
|
|
784
|
+
|
|
717
785
|
async function waitForChildPlayTerminalEvent(input: {
|
|
718
786
|
req: RunRequest;
|
|
719
787
|
workflowStep?: WorkflowStep;
|
|
@@ -734,11 +802,11 @@ async function waitForChildPlayTerminalEvent(input: {
|
|
|
734
802
|
const event = (await (
|
|
735
803
|
input.workflowStep.waitForEvent as unknown as (
|
|
736
804
|
name: string,
|
|
737
|
-
options: { type: string; timeout:
|
|
805
|
+
options: { type: string; timeout: string },
|
|
738
806
|
) => Promise<{ payload: unknown }>
|
|
739
807
|
)(`child_play_terminal:${eventKey}`, {
|
|
740
808
|
type: integrationEventType(eventKey),
|
|
741
|
-
timeout: input.timeoutMs,
|
|
809
|
+
timeout: workflowTimeoutFromMs(input.timeoutMs),
|
|
742
810
|
})) as { payload: unknown };
|
|
743
811
|
const rawPayload = isRecord(event.payload) ? event.payload : {};
|
|
744
812
|
const payload = isRecord(rawPayload.data) ? rawPayload.data : rawPayload;
|
|
@@ -909,15 +977,25 @@ async function waitForSyntheticIntegrationEvent(
|
|
|
909
977
|
typeof input.timeout_ms === 'number' && Number.isFinite(input.timeout_ms)
|
|
910
978
|
? Math.max(1, Math.round(input.timeout_ms))
|
|
911
979
|
: 30_000;
|
|
980
|
+
await postRuntimeApiBestEffort(req.baseUrl, req.executorToken, {
|
|
981
|
+
action: 'update_run_status',
|
|
982
|
+
playId: req.runId,
|
|
983
|
+
status: 'running',
|
|
984
|
+
runtimeBackend: 'cf_workflows_dynamic_worker',
|
|
985
|
+
waitKind: 'integration_event_batch',
|
|
986
|
+
waitUntil: nowMs() + timeoutMs,
|
|
987
|
+
activeBoundaryId: `integration_event:${eventKey}`,
|
|
988
|
+
lastCheckpointAt: nowMs(),
|
|
989
|
+
});
|
|
912
990
|
try {
|
|
913
991
|
const event = (await (
|
|
914
992
|
workflowStep.waitForEvent as unknown as (
|
|
915
993
|
name: string,
|
|
916
|
-
options: { type: string; timeout:
|
|
994
|
+
options: { type: string; timeout: string },
|
|
917
995
|
) => Promise<{ payload: unknown }>
|
|
918
996
|
)(`integration_event:${eventKey}`, {
|
|
919
997
|
type: integrationEventType(eventKey),
|
|
920
|
-
timeout: timeoutMs,
|
|
998
|
+
timeout: workflowTimeoutFromMs(timeoutMs),
|
|
921
999
|
})) as { payload: unknown };
|
|
922
1000
|
const payload =
|
|
923
1001
|
event.payload &&
|
|
@@ -983,7 +1061,7 @@ async function callToolDirect(
|
|
|
983
1061
|
headers: {
|
|
984
1062
|
'content-type': 'application/json',
|
|
985
1063
|
authorization: `Bearer ${req.executorToken}`,
|
|
986
|
-
'x-deepline-request-id': `${req.runId}:${toolId}:${id}`,
|
|
1064
|
+
'x-deepline-request-id': `${req.runId}:${toolId}:${id}:attempt:${attempt}`,
|
|
987
1065
|
[EXECUTE_TOOL_METADATA_HEADER]: 'true',
|
|
988
1066
|
},
|
|
989
1067
|
body: JSON.stringify({ payload: input }),
|
|
@@ -1455,7 +1533,9 @@ async function executeBatchedWorkerToolGroup(input: {
|
|
|
1455
1533
|
) => {
|
|
1456
1534
|
for (const entry of chunkResults) {
|
|
1457
1535
|
const batchResult = isToolExecuteResult(entry.result)
|
|
1458
|
-
? entry.result.result
|
|
1536
|
+
? isRecordLike(entry.result.result)
|
|
1537
|
+
? entry.result.result.data
|
|
1538
|
+
: undefined
|
|
1459
1539
|
: entry.result;
|
|
1460
1540
|
const splitResults =
|
|
1461
1541
|
batchResult != null
|
|
@@ -1516,6 +1596,7 @@ type WorkerMapChunkSummary<T extends Record<string, unknown>> = {
|
|
|
1516
1596
|
rowsWritten: number;
|
|
1517
1597
|
rowsExecuted: number;
|
|
1518
1598
|
rowsCached: number;
|
|
1599
|
+
rowsDuplicateReused: number;
|
|
1519
1600
|
rowsInserted: number;
|
|
1520
1601
|
rowsSkipped: number;
|
|
1521
1602
|
outputDatasetId: string;
|
|
@@ -1641,22 +1722,32 @@ async function executeWorkerStepProgram(
|
|
|
1641
1722
|
path: string[];
|
|
1642
1723
|
outputs: RecordedStepProgramOutput[];
|
|
1643
1724
|
},
|
|
1725
|
+
workflowStep?: WorkflowStep,
|
|
1644
1726
|
): Promise<unknown> {
|
|
1645
1727
|
let currentRow: Record<string, unknown> = cloneCsvAliasedRow(inputRow);
|
|
1646
1728
|
for (const step of program.steps) {
|
|
1647
1729
|
const stepPath = [...(recorder?.path ?? []), step.name];
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1730
|
+
const runStep = async () =>
|
|
1731
|
+
await executeWorkerStepResolver(
|
|
1732
|
+
step.resolver,
|
|
1733
|
+
currentRow,
|
|
1734
|
+
ctx,
|
|
1735
|
+
index,
|
|
1736
|
+
recorder
|
|
1737
|
+
? {
|
|
1738
|
+
...recorder,
|
|
1739
|
+
path: stepPath,
|
|
1740
|
+
}
|
|
1741
|
+
: undefined,
|
|
1742
|
+
);
|
|
1743
|
+
const resolution = workflowStep
|
|
1744
|
+
? await (
|
|
1745
|
+
workflowStep.do as unknown as (
|
|
1746
|
+
name: string,
|
|
1747
|
+
callback: () => Promise<WorkerStepResolution>,
|
|
1748
|
+
) => Promise<WorkerStepResolution>
|
|
1749
|
+
)(stepPath.join('.'), runStep)
|
|
1750
|
+
: await runStep();
|
|
1660
1751
|
const value = resolution.value;
|
|
1661
1752
|
currentRow = cloneCsvAliasedRow(currentRow, { [step.name]: value });
|
|
1662
1753
|
if (recorder) {
|
|
@@ -2373,6 +2464,18 @@ function childPipelineUsesCtxMap(
|
|
|
2373
2464
|
);
|
|
2374
2465
|
}
|
|
2375
2466
|
|
|
2467
|
+
function childPipelineNeedsWorkflowScheduler(
|
|
2468
|
+
pipeline: PlayStaticPipeline | null | undefined,
|
|
2469
|
+
): boolean {
|
|
2470
|
+
if (!pipeline) return false;
|
|
2471
|
+
return flattenStaticPipeline(pipeline).some(
|
|
2472
|
+
(substep) =>
|
|
2473
|
+
substep.type === 'tool' &&
|
|
2474
|
+
(substep.isEventWait === true ||
|
|
2475
|
+
substep.toolId === 'test_wait_for_event'),
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2376
2479
|
function releaseChildPlayConcurrency(
|
|
2377
2480
|
inFlightByPlayName: Record<string, number>,
|
|
2378
2481
|
playName: string,
|
|
@@ -2395,6 +2498,41 @@ function createMinimalWorkerCtx(
|
|
|
2395
2498
|
let playCallCount = 0;
|
|
2396
2499
|
const parentChildCalls: Record<string, number> = {};
|
|
2397
2500
|
const inFlightChildCallsByPlayName: Record<string, number> = {};
|
|
2501
|
+
let inFlightChildPlayCalls = 0;
|
|
2502
|
+
const childPlaySlotWaiters: Array<() => void> = [];
|
|
2503
|
+
|
|
2504
|
+
const acquireChildPlaySlot = async (): Promise<() => void> => {
|
|
2505
|
+
while (
|
|
2506
|
+
inFlightChildPlayCalls >= WORKER_PLAY_CALL_LIMITS.maxConcurrentPlayCalls
|
|
2507
|
+
) {
|
|
2508
|
+
await new Promise<void>((resolve, reject) => {
|
|
2509
|
+
const waiter = () => {
|
|
2510
|
+
abortSignal?.removeEventListener('abort', onAbort);
|
|
2511
|
+
resolve();
|
|
2512
|
+
};
|
|
2513
|
+
const onAbort = () => {
|
|
2514
|
+
const index = childPlaySlotWaiters.indexOf(waiter);
|
|
2515
|
+
if (index >= 0) childPlaySlotWaiters.splice(index, 1);
|
|
2516
|
+
reject(
|
|
2517
|
+
abortSignal?.reason instanceof Error
|
|
2518
|
+
? abortSignal.reason
|
|
2519
|
+
: new WorkflowAbortError(),
|
|
2520
|
+
);
|
|
2521
|
+
};
|
|
2522
|
+
childPlaySlotWaiters.push(waiter);
|
|
2523
|
+
abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
2524
|
+
});
|
|
2525
|
+
assertNotAborted(abortSignal);
|
|
2526
|
+
}
|
|
2527
|
+
inFlightChildPlayCalls += 1;
|
|
2528
|
+
let released = false;
|
|
2529
|
+
return () => {
|
|
2530
|
+
if (released) return;
|
|
2531
|
+
released = true;
|
|
2532
|
+
inFlightChildPlayCalls = Math.max(0, inFlightChildPlayCalls - 1);
|
|
2533
|
+
childPlaySlotWaiters.shift()?.();
|
|
2534
|
+
};
|
|
2535
|
+
};
|
|
2398
2536
|
const rootGovernance = req.playCallGovernance;
|
|
2399
2537
|
const rootRunId = rootGovernance?.rootRunId ?? req.runId;
|
|
2400
2538
|
// Local ancestry chain that always ENDS with the currently-executing play
|
|
@@ -2491,6 +2629,7 @@ function createMinimalWorkerCtx(
|
|
|
2491
2629
|
: JSON.stringify(normalizedParts);
|
|
2492
2630
|
return keyValue;
|
|
2493
2631
|
};
|
|
2632
|
+
const mapLogicFingerprint = req.graphHash ?? null;
|
|
2494
2633
|
const resolveRowKey = (
|
|
2495
2634
|
row: Record<string, unknown>,
|
|
2496
2635
|
index: number,
|
|
@@ -2498,8 +2637,12 @@ function createMinimalWorkerCtx(
|
|
|
2498
2637
|
const inputRow = publicCsvInputRow(row);
|
|
2499
2638
|
const explicitKeyValue = resolveExplicitKeyValue(row, index);
|
|
2500
2639
|
return explicitKeyValue == null
|
|
2501
|
-
? derivePlayRowIdentity(inputRow, name)
|
|
2502
|
-
: derivePlayRowIdentityFromKey(
|
|
2640
|
+
? derivePlayRowIdentity(inputRow, name, mapLogicFingerprint)
|
|
2641
|
+
: derivePlayRowIdentityFromKey(
|
|
2642
|
+
explicitKeyValue,
|
|
2643
|
+
name,
|
|
2644
|
+
mapLogicFingerprint,
|
|
2645
|
+
);
|
|
2503
2646
|
};
|
|
2504
2647
|
const assertUniqueExplicitRowKeys = (
|
|
2505
2648
|
chunkRows: readonly Record<string, unknown>[],
|
|
@@ -2547,7 +2690,11 @@ function createMinimalWorkerCtx(
|
|
|
2547
2690
|
const key =
|
|
2548
2691
|
typeof row.__deeplineRowKey === 'string'
|
|
2549
2692
|
? row.__deeplineRowKey
|
|
2550
|
-
: derivePlayRowIdentity(
|
|
2693
|
+
: derivePlayRowIdentity(
|
|
2694
|
+
publicCsvInputRow(row),
|
|
2695
|
+
name,
|
|
2696
|
+
mapLogicFingerprint,
|
|
2697
|
+
);
|
|
2551
2698
|
if (key) {
|
|
2552
2699
|
pendingKeys.add(key);
|
|
2553
2700
|
preparedKeys.add(key);
|
|
@@ -2557,7 +2704,11 @@ function createMinimalWorkerCtx(
|
|
|
2557
2704
|
const key =
|
|
2558
2705
|
typeof row.__deeplineRowKey === 'string'
|
|
2559
2706
|
? row.__deeplineRowKey
|
|
2560
|
-
: derivePlayRowIdentity(
|
|
2707
|
+
: derivePlayRowIdentity(
|
|
2708
|
+
publicCsvInputRow(row),
|
|
2709
|
+
name,
|
|
2710
|
+
mapLogicFingerprint,
|
|
2711
|
+
);
|
|
2561
2712
|
if (key) {
|
|
2562
2713
|
completedKeys.add(key);
|
|
2563
2714
|
preparedKeys.add(key);
|
|
@@ -2569,7 +2720,17 @@ function createMinimalWorkerCtx(
|
|
|
2569
2720
|
const rowsToExecuteEntries = chunkEntries.filter(
|
|
2570
2721
|
({ rowKey }) => pendingKeys.has(rowKey) || !completedKeys.has(rowKey),
|
|
2571
2722
|
);
|
|
2572
|
-
const
|
|
2723
|
+
const uniqueRowsToExecuteEntries = [
|
|
2724
|
+
...new Map(
|
|
2725
|
+
rowsToExecuteEntries.map((entry) => [entry.rowKey, entry]),
|
|
2726
|
+
).values(),
|
|
2727
|
+
];
|
|
2728
|
+
const duplicateInputReuseCount = Math.max(
|
|
2729
|
+
0,
|
|
2730
|
+
chunkEntries.length -
|
|
2731
|
+
new Set(chunkEntries.map((entry) => entry.rowKey)).size,
|
|
2732
|
+
);
|
|
2733
|
+
const rowsToExecute = uniqueRowsToExecuteEntries.map(({ row }) => row);
|
|
2573
2734
|
const rowsInserted = prepared.inserted + missingPreparedRows.length;
|
|
2574
2735
|
const rowsSkipped = Math.max(
|
|
2575
2736
|
0,
|
|
@@ -2593,7 +2754,7 @@ function createMinimalWorkerCtx(
|
|
|
2593
2754
|
if (abortSignal?.aborted) return;
|
|
2594
2755
|
const myIndex = idx++;
|
|
2595
2756
|
if (myIndex >= rowsToExecute.length) return;
|
|
2596
|
-
const entry =
|
|
2757
|
+
const entry = uniqueRowsToExecuteEntries[myIndex]!;
|
|
2597
2758
|
const row = entry.row;
|
|
2598
2759
|
const absoluteIndex = entry.absoluteIndex;
|
|
2599
2760
|
const enriched: Record<string, unknown> = cloneCsvAliasedRow(row);
|
|
@@ -2701,14 +2862,33 @@ function createMinimalWorkerCtx(
|
|
|
2701
2862
|
})(),
|
|
2702
2863
|
);
|
|
2703
2864
|
}
|
|
2704
|
-
|
|
2705
|
-
|
|
2865
|
+
const persistExecutedRows = async () => {
|
|
2866
|
+
const rowsToPersist = executedRows
|
|
2867
|
+
.map((row, executedIndex) =>
|
|
2868
|
+
row
|
|
2869
|
+
? {
|
|
2870
|
+
row,
|
|
2871
|
+
executedIndex,
|
|
2872
|
+
}
|
|
2873
|
+
: null,
|
|
2874
|
+
)
|
|
2875
|
+
.filter(
|
|
2876
|
+
(
|
|
2877
|
+
entry,
|
|
2878
|
+
): entry is {
|
|
2879
|
+
row: T & Record<string, unknown>;
|
|
2880
|
+
executedIndex: number;
|
|
2881
|
+
} => entry !== null,
|
|
2882
|
+
);
|
|
2883
|
+
if (rowsToPersist.length === 0) {
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2706
2886
|
await persistCompletedMapRows({
|
|
2707
2887
|
req,
|
|
2708
2888
|
tableNamespace: name,
|
|
2709
2889
|
outputFields,
|
|
2710
2890
|
extraOutputFields: Array.from(generatedOutputFields),
|
|
2711
|
-
rows:
|
|
2891
|
+
rows: rowsToPersist.map(({ row, executedIndex }) => ({
|
|
2712
2892
|
...row,
|
|
2713
2893
|
...(executedCellMetaPatches[executedIndex]
|
|
2714
2894
|
? {
|
|
@@ -2716,16 +2896,30 @@ function createMinimalWorkerCtx(
|
|
|
2716
2896
|
executedCellMetaPatches[executedIndex],
|
|
2717
2897
|
}
|
|
2718
2898
|
: {}),
|
|
2719
|
-
__deeplineRowKey:
|
|
2899
|
+
__deeplineRowKey:
|
|
2900
|
+
uniqueRowsToExecuteEntries[executedIndex]!.rowKey,
|
|
2720
2901
|
})),
|
|
2721
2902
|
});
|
|
2903
|
+
};
|
|
2904
|
+
const workerResults = await Promise.allSettled(workers);
|
|
2905
|
+
await persistExecutedRows();
|
|
2906
|
+
const rejectedWorker = workerResults.find(
|
|
2907
|
+
(result): result is PromiseRejectedResult =>
|
|
2908
|
+
result.status === 'rejected',
|
|
2909
|
+
);
|
|
2910
|
+
if (rejectedWorker) {
|
|
2911
|
+
throw rejectedWorker.reason;
|
|
2722
2912
|
}
|
|
2723
2913
|
const resultByKey = new Map<string, T & Record<string, unknown>>();
|
|
2724
2914
|
for (const completedRow of prepared.completedRows) {
|
|
2725
2915
|
const key =
|
|
2726
2916
|
typeof completedRow.__deeplineRowKey === 'string'
|
|
2727
2917
|
? completedRow.__deeplineRowKey
|
|
2728
|
-
: derivePlayRowIdentity(
|
|
2918
|
+
: derivePlayRowIdentity(
|
|
2919
|
+
publicCsvInputRow(completedRow),
|
|
2920
|
+
name,
|
|
2921
|
+
mapLogicFingerprint,
|
|
2922
|
+
);
|
|
2729
2923
|
if (key) {
|
|
2730
2924
|
const { __deeplineRowKey: _rowKey, ...cleanedRow } =
|
|
2731
2925
|
publicCsvInputRow(completedRow);
|
|
@@ -2739,7 +2933,7 @@ function createMinimalWorkerCtx(
|
|
|
2739
2933
|
executedIndex += 1
|
|
2740
2934
|
) {
|
|
2741
2935
|
const executedRow = executedRows[executedIndex]!;
|
|
2742
|
-
const key =
|
|
2936
|
+
const key = uniqueRowsToExecuteEntries[executedIndex]!.rowKey;
|
|
2743
2937
|
if (key) resultByKey.set(key, executedRow);
|
|
2744
2938
|
}
|
|
2745
2939
|
const out = chunkRows
|
|
@@ -2755,7 +2949,8 @@ function createMinimalWorkerCtx(
|
|
|
2755
2949
|
rowsRead: chunkRows.length,
|
|
2756
2950
|
rowsWritten: out.length,
|
|
2757
2951
|
rowsExecuted: executedRows.length,
|
|
2758
|
-
rowsCached:
|
|
2952
|
+
rowsCached: Math.max(0, out.length - executedRows.length),
|
|
2953
|
+
rowsDuplicateReused: duplicateInputReuseCount,
|
|
2759
2954
|
rowsInserted,
|
|
2760
2955
|
rowsSkipped,
|
|
2761
2956
|
outputDatasetId: `map:${name}`,
|
|
@@ -2767,6 +2962,7 @@ function createMinimalWorkerCtx(
|
|
|
2767
2962
|
const out: Array<T & Record<string, unknown>> = [];
|
|
2768
2963
|
let totalRowsExecuted = 0;
|
|
2769
2964
|
let totalRowsCached = 0;
|
|
2965
|
+
let totalRowsDuplicateReused = 0;
|
|
2770
2966
|
let totalRowsInserted = 0;
|
|
2771
2967
|
let totalRowsSkipped = 0;
|
|
2772
2968
|
|
|
@@ -2809,6 +3005,17 @@ function createMinimalWorkerCtx(
|
|
|
2809
3005
|
return makeWorkerDataset(name, out, {
|
|
2810
3006
|
count: totalRowsWritten,
|
|
2811
3007
|
cacheSummary,
|
|
3008
|
+
workProgress: {
|
|
3009
|
+
total: totalRowsWritten,
|
|
3010
|
+
executed: totalRowsExecuted,
|
|
3011
|
+
reused: totalRowsCached,
|
|
3012
|
+
skipped: totalRowsCached,
|
|
3013
|
+
pending: 0,
|
|
3014
|
+
failed: 0,
|
|
3015
|
+
...(totalRowsDuplicateReused > 0
|
|
3016
|
+
? { duplicates: { exact: totalRowsDuplicateReused } }
|
|
3017
|
+
: {}),
|
|
3018
|
+
},
|
|
2812
3019
|
});
|
|
2813
3020
|
};
|
|
2814
3021
|
|
|
@@ -2829,6 +3036,7 @@ function createMinimalWorkerCtx(
|
|
|
2829
3036
|
totalRowsWritten += chunkResult.rowsWritten;
|
|
2830
3037
|
totalRowsExecuted += chunkResult.rowsExecuted;
|
|
2831
3038
|
totalRowsCached += chunkResult.rowsCached;
|
|
3039
|
+
totalRowsDuplicateReused += chunkResult.rowsDuplicateReused;
|
|
2832
3040
|
totalRowsInserted += chunkResult.rowsInserted;
|
|
2833
3041
|
totalRowsSkipped += chunkResult.rowsSkipped;
|
|
2834
3042
|
if (out.length < 10) {
|
|
@@ -2852,6 +3060,7 @@ function createMinimalWorkerCtx(
|
|
|
2852
3060
|
totalRowsWritten += chunkResult.rowsWritten;
|
|
2853
3061
|
totalRowsExecuted += chunkResult.rowsExecuted;
|
|
2854
3062
|
totalRowsCached += chunkResult.rowsCached;
|
|
3063
|
+
totalRowsDuplicateReused += chunkResult.rowsDuplicateReused;
|
|
2855
3064
|
totalRowsInserted += chunkResult.rowsInserted;
|
|
2856
3065
|
totalRowsSkipped += chunkResult.rowsSkipped;
|
|
2857
3066
|
if (out.length < 10) {
|
|
@@ -2865,6 +3074,7 @@ function createMinimalWorkerCtx(
|
|
|
2865
3074
|
const chunkResult = await runChunkStep(sliced, 0, 0);
|
|
2866
3075
|
totalRowsExecuted = chunkResult.rowsExecuted;
|
|
2867
3076
|
totalRowsCached = chunkResult.rowsCached;
|
|
3077
|
+
totalRowsDuplicateReused = chunkResult.rowsDuplicateReused;
|
|
2868
3078
|
totalRowsInserted = chunkResult.rowsInserted;
|
|
2869
3079
|
totalRowsSkipped = chunkResult.rowsSkipped;
|
|
2870
3080
|
out.push(...chunkResult.preview);
|
|
@@ -2917,18 +3127,39 @@ function createMinimalWorkerCtx(
|
|
|
2917
3127
|
},
|
|
2918
3128
|
async step<T>(name: string, callback: () => Promise<T> | T): Promise<T> {
|
|
2919
3129
|
assertNotAborted(abortSignal);
|
|
2920
|
-
if (!
|
|
2921
|
-
|
|
3130
|
+
if (!name.trim()) {
|
|
3131
|
+
throw new Error('ctx.step(name, callback) requires a name.');
|
|
2922
3132
|
}
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
}
|
|
3133
|
+
// Static pipeline JS blocks are already Workflow steps in the Workers
|
|
3134
|
+
// backend. Nesting another `step.do` here can leave preview runs parked
|
|
3135
|
+
// inside the JS stage before they reach subsequent event waits.
|
|
3136
|
+
return await callback();
|
|
3137
|
+
},
|
|
3138
|
+
async runSteps<T>(
|
|
3139
|
+
program: WorkerStepProgram,
|
|
3140
|
+
input: Record<string, unknown>,
|
|
3141
|
+
opts?: { description?: string },
|
|
3142
|
+
): Promise<T> {
|
|
3143
|
+
assertNotAborted(abortSignal);
|
|
3144
|
+
if (!isWorkerStepProgram(program)) {
|
|
3145
|
+
throw new Error('ctx.runSteps(program, input) requires steps().');
|
|
3146
|
+
}
|
|
3147
|
+
if (opts?.description) {
|
|
3148
|
+
emitEvent({
|
|
3149
|
+
type: 'log',
|
|
3150
|
+
level: 'info',
|
|
3151
|
+
message: String(opts.description),
|
|
3152
|
+
ts: nowMs(),
|
|
3153
|
+
});
|
|
3154
|
+
}
|
|
3155
|
+
return (await executeWorkerStepProgram(
|
|
3156
|
+
program,
|
|
3157
|
+
input,
|
|
3158
|
+
ctx,
|
|
3159
|
+
0,
|
|
3160
|
+
undefined,
|
|
3161
|
+
workflowStep,
|
|
3162
|
+
)) as T;
|
|
2932
3163
|
},
|
|
2933
3164
|
async csv<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
2934
3165
|
arg: unknown,
|
|
@@ -3063,7 +3294,11 @@ function createMinimalWorkerCtx(
|
|
|
3063
3294
|
const completedKeys = new Set<string>();
|
|
3064
3295
|
const preparedKeys = new Set<string>();
|
|
3065
3296
|
for (const row of prepared.pendingRows) {
|
|
3066
|
-
const key = derivePlayRowIdentity(
|
|
3297
|
+
const key = derivePlayRowIdentity(
|
|
3298
|
+
publicCsvInputRow(row),
|
|
3299
|
+
name,
|
|
3300
|
+
mapLogicFingerprint,
|
|
3301
|
+
);
|
|
3067
3302
|
if (key) {
|
|
3068
3303
|
pendingKeys.add(key);
|
|
3069
3304
|
preparedKeys.add(key);
|
|
@@ -3073,18 +3308,30 @@ function createMinimalWorkerCtx(
|
|
|
3073
3308
|
const key =
|
|
3074
3309
|
typeof row.__deeplineRowKey === 'string'
|
|
3075
3310
|
? row.__deeplineRowKey
|
|
3076
|
-
: derivePlayRowIdentity(
|
|
3311
|
+
: derivePlayRowIdentity(
|
|
3312
|
+
publicCsvInputRow(row),
|
|
3313
|
+
name,
|
|
3314
|
+
mapLogicFingerprint,
|
|
3315
|
+
);
|
|
3077
3316
|
if (key) {
|
|
3078
3317
|
completedKeys.add(key);
|
|
3079
3318
|
preparedKeys.add(key);
|
|
3080
3319
|
}
|
|
3081
3320
|
}
|
|
3082
3321
|
const missingPreparedRows = chunkRows.filter((row) => {
|
|
3083
|
-
const key = derivePlayRowIdentity(
|
|
3322
|
+
const key = derivePlayRowIdentity(
|
|
3323
|
+
publicCsvInputRow(row),
|
|
3324
|
+
name,
|
|
3325
|
+
mapLogicFingerprint,
|
|
3326
|
+
);
|
|
3084
3327
|
return !key || !preparedKeys.has(key);
|
|
3085
3328
|
});
|
|
3086
3329
|
const rowsToExecute = chunkRows.filter((row) => {
|
|
3087
|
-
const key = derivePlayRowIdentity(
|
|
3330
|
+
const key = derivePlayRowIdentity(
|
|
3331
|
+
publicCsvInputRow(row),
|
|
3332
|
+
name,
|
|
3333
|
+
mapLogicFingerprint,
|
|
3334
|
+
);
|
|
3088
3335
|
return !key || pendingKeys.has(key) || !completedKeys.has(key);
|
|
3089
3336
|
});
|
|
3090
3337
|
const rowsInserted = prepared.inserted + missingPreparedRows.length;
|
|
@@ -3167,6 +3414,7 @@ function createMinimalWorkerCtx(
|
|
|
3167
3414
|
__deeplineRowKey: derivePlayRowIdentity(
|
|
3168
3415
|
publicCsvInputRow(rowsToExecute[executedIndex]!),
|
|
3169
3416
|
name,
|
|
3417
|
+
mapLogicFingerprint,
|
|
3170
3418
|
),
|
|
3171
3419
|
})),
|
|
3172
3420
|
});
|
|
@@ -3176,7 +3424,11 @@ function createMinimalWorkerCtx(
|
|
|
3176
3424
|
const key =
|
|
3177
3425
|
typeof completedRow.__deeplineRowKey === 'string'
|
|
3178
3426
|
? completedRow.__deeplineRowKey
|
|
3179
|
-
: derivePlayRowIdentity(
|
|
3427
|
+
: derivePlayRowIdentity(
|
|
3428
|
+
publicCsvInputRow(completedRow),
|
|
3429
|
+
name,
|
|
3430
|
+
mapLogicFingerprint,
|
|
3431
|
+
);
|
|
3180
3432
|
if (key) {
|
|
3181
3433
|
const { __deeplineRowKey: _rowKey, ...cleanedRow } =
|
|
3182
3434
|
publicCsvInputRow(completedRow);
|
|
@@ -3193,12 +3445,17 @@ function createMinimalWorkerCtx(
|
|
|
3193
3445
|
const key = derivePlayRowIdentity(
|
|
3194
3446
|
publicCsvInputRow(rowsToExecute[executedIndex]!),
|
|
3195
3447
|
name,
|
|
3448
|
+
mapLogicFingerprint,
|
|
3196
3449
|
);
|
|
3197
3450
|
if (key) resultByKey.set(key, executedRow);
|
|
3198
3451
|
}
|
|
3199
3452
|
const out = chunkRows
|
|
3200
3453
|
.map((row) => {
|
|
3201
|
-
const key = derivePlayRowIdentity(
|
|
3454
|
+
const key = derivePlayRowIdentity(
|
|
3455
|
+
publicCsvInputRow(row),
|
|
3456
|
+
name,
|
|
3457
|
+
mapLogicFingerprint,
|
|
3458
|
+
);
|
|
3202
3459
|
return key ? resultByKey.get(key) : undefined;
|
|
3203
3460
|
})
|
|
3204
3461
|
.filter((row): row is T & Record<string, unknown> => Boolean(row));
|
|
@@ -3263,6 +3520,14 @@ function createMinimalWorkerCtx(
|
|
|
3263
3520
|
return makeWorkerDataset(name, out, {
|
|
3264
3521
|
count: totalRowsWritten,
|
|
3265
3522
|
cacheSummary,
|
|
3523
|
+
workProgress: {
|
|
3524
|
+
total: totalRowsWritten,
|
|
3525
|
+
executed: totalRowsExecuted,
|
|
3526
|
+
reused: totalRowsCached,
|
|
3527
|
+
skipped: totalRowsCached,
|
|
3528
|
+
pending: 0,
|
|
3529
|
+
failed: 0,
|
|
3530
|
+
},
|
|
3266
3531
|
});
|
|
3267
3532
|
};
|
|
3268
3533
|
|
|
@@ -3448,7 +3713,11 @@ function createMinimalWorkerCtx(
|
|
|
3448
3713
|
const childIsMapBacked = childPipelineUsesCtxMap(
|
|
3449
3714
|
childManifest.staticPipeline,
|
|
3450
3715
|
);
|
|
3716
|
+
const childNeedsWorkflowScheduler = childPipelineNeedsWorkflowScheduler(
|
|
3717
|
+
childManifest.staticPipeline,
|
|
3718
|
+
);
|
|
3451
3719
|
let childConcurrencyAcquired = false;
|
|
3720
|
+
let releaseChildPlaySlot: (() => void) | null = null;
|
|
3452
3721
|
if (childIsMapBacked) {
|
|
3453
3722
|
const nextInFlight =
|
|
3454
3723
|
(inFlightChildCallsByPlayName[resolvedName] ?? 0) + 1;
|
|
@@ -3463,11 +3732,21 @@ function createMinimalWorkerCtx(
|
|
|
3463
3732
|
childConcurrencyAcquired = true;
|
|
3464
3733
|
}
|
|
3465
3734
|
try {
|
|
3735
|
+
releaseChildPlaySlot = await acquireChildPlaySlot();
|
|
3466
3736
|
const childSubmitStartedAt = nowMs();
|
|
3467
|
-
let started: {
|
|
3737
|
+
let started: {
|
|
3738
|
+
workflowId?: string;
|
|
3739
|
+
runId?: string;
|
|
3740
|
+
status?: string;
|
|
3741
|
+
output?: unknown;
|
|
3742
|
+
result?: unknown;
|
|
3743
|
+
error?: unknown;
|
|
3744
|
+
};
|
|
3468
3745
|
try {
|
|
3469
3746
|
started = await submitChildPlayThroughCoordinator({
|
|
3470
3747
|
req,
|
|
3748
|
+
allowInline:
|
|
3749
|
+
options?.timeoutMs == null && !childNeedsWorkflowScheduler,
|
|
3471
3750
|
body: {
|
|
3472
3751
|
name: resolvedName,
|
|
3473
3752
|
input: isRecord(input) ? input : {},
|
|
@@ -3537,6 +3816,27 @@ function createMinimalWorkerCtx(
|
|
|
3537
3816
|
ms: nowMs() - childSubmitStartedAt,
|
|
3538
3817
|
status: 'ok',
|
|
3539
3818
|
});
|
|
3819
|
+
const startedStatus = String(started.status ?? '').toLowerCase();
|
|
3820
|
+
if (startedStatus === 'completed') {
|
|
3821
|
+
emitEvent({
|
|
3822
|
+
type: 'log',
|
|
3823
|
+
level: 'info',
|
|
3824
|
+
message: `Completed child play ${resolvedName} (${normalizedKey})`,
|
|
3825
|
+
ts: nowMs(),
|
|
3826
|
+
});
|
|
3827
|
+
return started.output ?? extractChildPlayOutput(started);
|
|
3828
|
+
}
|
|
3829
|
+
if (startedStatus === 'failed') {
|
|
3830
|
+
const startedError = isRecord(started.error)
|
|
3831
|
+
? started.error
|
|
3832
|
+
: { message: started.error };
|
|
3833
|
+
const startedErrorMessage =
|
|
3834
|
+
typeof startedError.message === 'string' &&
|
|
3835
|
+
startedError.message.trim()
|
|
3836
|
+
? startedError.message.trim()
|
|
3837
|
+
: `Child play ${resolvedName} (${workflowId}) failed.`;
|
|
3838
|
+
throw new Error(startedErrorMessage);
|
|
3839
|
+
}
|
|
3540
3840
|
const childWaitStartedAt = nowMs();
|
|
3541
3841
|
let result: unknown;
|
|
3542
3842
|
try {
|
|
@@ -3589,6 +3889,7 @@ function createMinimalWorkerCtx(
|
|
|
3589
3889
|
});
|
|
3590
3890
|
return result;
|
|
3591
3891
|
} finally {
|
|
3892
|
+
releaseChildPlaySlot?.();
|
|
3592
3893
|
if (childConcurrencyAcquired) {
|
|
3593
3894
|
releaseChildPlayConcurrency(
|
|
3594
3895
|
inFlightChildCallsByPlayName,
|
|
@@ -3619,11 +3920,11 @@ function createMinimalWorkerCtx(
|
|
|
3619
3920
|
const event = (await (
|
|
3620
3921
|
workflowStep.waitForEvent as unknown as (
|
|
3621
3922
|
name: string,
|
|
3622
|
-
options: { type: string; timeout:
|
|
3923
|
+
options: { type: string; timeout: string },
|
|
3623
3924
|
) => Promise<{ payload: unknown }>
|
|
3624
3925
|
)(`wait_for_event:${workflowEventType(eventType)}`, {
|
|
3625
3926
|
type: workflowEventType(eventType),
|
|
3626
|
-
timeout: timeoutMs,
|
|
3927
|
+
timeout: workflowTimeoutFromMs(timeoutMs),
|
|
3627
3928
|
})) as { payload: unknown };
|
|
3628
3929
|
return event.payload ?? null;
|
|
3629
3930
|
},
|
|
@@ -3804,6 +4105,9 @@ async function executeRunRequest(
|
|
|
3804
4105
|
status: 'completed',
|
|
3805
4106
|
result: terminalResult,
|
|
3806
4107
|
runtimeBackend: 'cf_workflows_dynamic_worker',
|
|
4108
|
+
waitKind: null,
|
|
4109
|
+
waitUntil: null,
|
|
4110
|
+
activeBoundaryId: null,
|
|
3807
4111
|
liveLogs,
|
|
3808
4112
|
lastCheckpointAt: nowMs(),
|
|
3809
4113
|
});
|
|
@@ -3825,6 +4129,7 @@ async function executeRunRequest(
|
|
|
3825
4129
|
);
|
|
3826
4130
|
});
|
|
3827
4131
|
return {
|
|
4132
|
+
playName: req.playName,
|
|
3828
4133
|
result: serializedResult,
|
|
3829
4134
|
outputRows: inferOutputRows(serializedResult),
|
|
3830
4135
|
durationMs: nowMs() - startedAt,
|
|
@@ -3847,6 +4152,9 @@ async function executeRunRequest(
|
|
|
3847
4152
|
status: aborted ? 'cancelled' : 'failed',
|
|
3848
4153
|
error: message,
|
|
3849
4154
|
runtimeBackend: 'cf_workflows_dynamic_worker',
|
|
4155
|
+
waitKind: null,
|
|
4156
|
+
waitUntil: null,
|
|
4157
|
+
activeBoundaryId: null,
|
|
3850
4158
|
liveLogs,
|
|
3851
4159
|
lastCheckpointAt: nowMs(),
|
|
3852
4160
|
});
|
|
@@ -4121,6 +4429,14 @@ function serializeValue(value: unknown, depth: number): unknown {
|
|
|
4121
4429
|
? (value as unknown as { __deeplineCacheSummary: string })
|
|
4122
4430
|
.__deeplineCacheSummary
|
|
4123
4431
|
: null;
|
|
4432
|
+
const workProgress =
|
|
4433
|
+
isRecord(
|
|
4434
|
+
(value as unknown as { __deeplineWorkProgress?: unknown })
|
|
4435
|
+
.__deeplineWorkProgress,
|
|
4436
|
+
)
|
|
4437
|
+
? (value as unknown as { __deeplineWorkProgress: Record<string, unknown> })
|
|
4438
|
+
.__deeplineWorkProgress
|
|
4439
|
+
: null;
|
|
4124
4440
|
const previewRows = value
|
|
4125
4441
|
.slice(0, 5)
|
|
4126
4442
|
.map((row) => serializeValue(row, depth + 1))
|
|
@@ -4138,6 +4454,9 @@ function serializeValue(value: unknown, depth: number): unknown {
|
|
|
4138
4454
|
preview: previewRows,
|
|
4139
4455
|
tableNamespace,
|
|
4140
4456
|
...(cacheSummary ? { cacheSummary } : {}),
|
|
4457
|
+
...(workProgress
|
|
4458
|
+
? { _metadata: { workProgress } }
|
|
4459
|
+
: {}),
|
|
4141
4460
|
};
|
|
4142
4461
|
}
|
|
4143
4462
|
return value.map((entry) => serializeValue(entry, depth + 1));
|