deepline 0.1.170 → 0.1.172
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/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +87 -61
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +47 -62
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +58 -23
- package/dist/cli/index.js +151 -28
- package/dist/cli/index.mjs +151 -28
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -195,7 +195,7 @@ Full sequence from zero to hitting + surviving rate limits via v2 plays.
|
|
|
195
195
|
|
|
196
196
|
```bash
|
|
197
197
|
# Production
|
|
198
|
-
bash <(curl -
|
|
198
|
+
bash <(curl -fsSL https://deepline.com/api/v2/sdk/install)
|
|
199
199
|
|
|
200
200
|
# Local dev (no install needed)
|
|
201
201
|
bun run dev # start server
|
|
@@ -1719,7 +1719,10 @@ class RuntimeReceiptPersistenceError extends Error {
|
|
|
1719
1719
|
function isRuntimeReceiptPersistenceError(error: unknown): boolean {
|
|
1720
1720
|
if (!error || typeof error !== 'object') return false;
|
|
1721
1721
|
if (error instanceof RuntimeReceiptPersistenceError) return true;
|
|
1722
|
-
if (
|
|
1722
|
+
if (
|
|
1723
|
+
error instanceof Error &&
|
|
1724
|
+
error.name === 'RuntimeReceiptPersistenceError'
|
|
1725
|
+
) {
|
|
1723
1726
|
return true;
|
|
1724
1727
|
}
|
|
1725
1728
|
const nestedErrors = (error as { errors?: unknown }).errors;
|
|
@@ -2559,8 +2562,7 @@ class WorkerToolBatchScheduler {
|
|
|
2559
2562
|
{ id: request.id, toolId, input: request.input },
|
|
2560
2563
|
request.workflowStep,
|
|
2561
2564
|
this.callbacks,
|
|
2562
|
-
(retryAfterMs) =>
|
|
2563
|
-
this.reportBackpressure(toolId, retryAfterMs),
|
|
2565
|
+
(retryAfterMs) => this.reportBackpressure(toolId, retryAfterMs),
|
|
2564
2566
|
() => this.governor.chargeBudget('retry'),
|
|
2565
2567
|
toolContract?.retrySafeTransientHttp === true,
|
|
2566
2568
|
this.abortSignal,
|
|
@@ -4529,9 +4531,12 @@ function createMinimalWorkerCtx(
|
|
|
4529
4531
|
const executeWithRuntimeReceipt = async <T>(
|
|
4530
4532
|
key: string,
|
|
4531
4533
|
execute: () => Promise<T> | T,
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4534
|
+
options: {
|
|
4535
|
+
repairRunningReceiptForSameRun?: boolean;
|
|
4536
|
+
reclaimRunning?: boolean;
|
|
4537
|
+
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
4538
|
+
runningReceiptWaitMaxAttempts?: number;
|
|
4539
|
+
} = {},
|
|
4535
4540
|
): Promise<T> => {
|
|
4536
4541
|
const serialized = await runWorkerRuntimeReceiptBoundary<unknown>({
|
|
4537
4542
|
orgId: req.orgId,
|
|
@@ -4540,9 +4545,12 @@ function createMinimalWorkerCtx(
|
|
|
4540
4545
|
key,
|
|
4541
4546
|
receiptStore,
|
|
4542
4547
|
execute: async () => serializeDurableStepValue(await execute()),
|
|
4543
|
-
repairRunningReceiptForSameRun
|
|
4544
|
-
|
|
4545
|
-
|
|
4548
|
+
repairRunningReceiptForSameRun:
|
|
4549
|
+
options.repairRunningReceiptForSameRun ?? false,
|
|
4550
|
+
reclaimRunning: options.reclaimRunning ?? false,
|
|
4551
|
+
repairRunningReceiptForSameRunAfterWaitTimeout:
|
|
4552
|
+
options.repairRunningReceiptForSameRunAfterWaitTimeout ?? false,
|
|
4553
|
+
runningReceiptWaitMaxAttempts: options.runningReceiptWaitMaxAttempts,
|
|
4546
4554
|
});
|
|
4547
4555
|
return deserializeDurableStepValue(serialized) as T;
|
|
4548
4556
|
};
|
|
@@ -4553,19 +4561,15 @@ function createMinimalWorkerCtx(
|
|
|
4553
4561
|
if (!workflowStep) {
|
|
4554
4562
|
return await executeWithRuntimeReceipt(name, execute);
|
|
4555
4563
|
}
|
|
4556
|
-
return await executeWithRuntimeReceipt(
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
return deserializeDurableStepValue(serialized) as T;
|
|
4566
|
-
},
|
|
4567
|
-
false,
|
|
4568
|
-
);
|
|
4564
|
+
return await executeWithRuntimeReceipt(name, async () => {
|
|
4565
|
+
const serialized = await (
|
|
4566
|
+
workflowStep.do as unknown as (
|
|
4567
|
+
name: string,
|
|
4568
|
+
callback: () => Promise<unknown>,
|
|
4569
|
+
) => Promise<unknown>
|
|
4570
|
+
)(name, async () => serializeDurableStepValue(await execute()));
|
|
4571
|
+
return deserializeDurableStepValue(serialized) as T;
|
|
4572
|
+
});
|
|
4569
4573
|
};
|
|
4570
4574
|
const nextCtxStepReceiptKey = (
|
|
4571
4575
|
name: string,
|
|
@@ -4648,7 +4652,6 @@ function createMinimalWorkerCtx(
|
|
|
4648
4652
|
nodeId: mapNodeId,
|
|
4649
4653
|
progress: {
|
|
4650
4654
|
artifactTableNamespace: name,
|
|
4651
|
-
failed: 0,
|
|
4652
4655
|
...progress,
|
|
4653
4656
|
updatedAt: progress.updatedAt ?? nowMs(),
|
|
4654
4657
|
},
|
|
@@ -5032,6 +5035,7 @@ function createMinimalWorkerCtx(
|
|
|
5032
5035
|
{
|
|
5033
5036
|
completed,
|
|
5034
5037
|
total: progressTotalRows,
|
|
5038
|
+
failed: failedExecutedRows,
|
|
5035
5039
|
startedAt: mapStartedAt,
|
|
5036
5040
|
message: formatMapProgressMessage(completed, progressTotalRows),
|
|
5037
5041
|
},
|
|
@@ -5076,7 +5080,7 @@ function createMinimalWorkerCtx(
|
|
|
5076
5080
|
let stepCellsCompleted = 0;
|
|
5077
5081
|
let stepCellsSkipped = 0;
|
|
5078
5082
|
const generatedOutputFields = new Set<string>();
|
|
5079
|
-
const
|
|
5083
|
+
const pendingLiveUpdates: Array<
|
|
5080
5084
|
Omit<PlayRowUpdate, 'rowId'> & { runId?: string }
|
|
5081
5085
|
> = [];
|
|
5082
5086
|
const persistedExecutedIndexes = new Set<number>();
|
|
@@ -5086,9 +5090,9 @@ function createMinimalWorkerCtx(
|
|
|
5086
5090
|
let scheduledPersistTimer: ReturnType<typeof setTimeout> | null = null;
|
|
5087
5091
|
let persistFlushChain: Promise<void> = Promise.resolve();
|
|
5088
5092
|
let persistFailure: unknown = null;
|
|
5089
|
-
let
|
|
5090
|
-
let
|
|
5091
|
-
let
|
|
5093
|
+
let liveUpdateTimer: ReturnType<typeof setTimeout> | null = null;
|
|
5094
|
+
let liveFlushChain: Promise<void> = Promise.resolve();
|
|
5095
|
+
let liveUpdateFailures = 0;
|
|
5092
5096
|
|
|
5093
5097
|
const clearScheduledPersistTimer = () => {
|
|
5094
5098
|
if (scheduledPersistTimer) {
|
|
@@ -5097,10 +5101,10 @@ function createMinimalWorkerCtx(
|
|
|
5097
5101
|
}
|
|
5098
5102
|
};
|
|
5099
5103
|
|
|
5100
|
-
const
|
|
5101
|
-
if (
|
|
5102
|
-
clearTimeout(
|
|
5103
|
-
|
|
5104
|
+
const clearLiveUpdateTimer = () => {
|
|
5105
|
+
if (liveUpdateTimer) {
|
|
5106
|
+
clearTimeout(liveUpdateTimer);
|
|
5107
|
+
liveUpdateTimer = null;
|
|
5104
5108
|
}
|
|
5105
5109
|
};
|
|
5106
5110
|
|
|
@@ -5218,14 +5222,36 @@ function createMinimalWorkerCtx(
|
|
|
5218
5222
|
return task;
|
|
5219
5223
|
};
|
|
5220
5224
|
|
|
5225
|
+
const coalesceLiveUpdates = (
|
|
5226
|
+
updates: Array<Omit<PlayRowUpdate, 'rowId'> & { runId?: string }>,
|
|
5227
|
+
): Array<Omit<PlayRowUpdate, 'rowId'> & { runId?: string }> => {
|
|
5228
|
+
const mergedByKey = new Map<
|
|
5229
|
+
string,
|
|
5230
|
+
Omit<PlayRowUpdate, 'rowId'> & { runId?: string }
|
|
5231
|
+
>();
|
|
5232
|
+
for (const update of updates) {
|
|
5233
|
+
const existing = mergedByKey.get(update.key);
|
|
5234
|
+
mergedByKey.set(update.key, {
|
|
5235
|
+
...existing,
|
|
5236
|
+
...update,
|
|
5237
|
+
dataPatch: { ...existing?.dataPatch, ...update.dataPatch },
|
|
5238
|
+
cellMetaPatch: {
|
|
5239
|
+
...existing?.cellMetaPatch,
|
|
5240
|
+
...update.cellMetaPatch,
|
|
5241
|
+
},
|
|
5242
|
+
});
|
|
5243
|
+
}
|
|
5244
|
+
return [...mergedByKey.values()];
|
|
5245
|
+
};
|
|
5246
|
+
|
|
5221
5247
|
const flushLiveRowUpdates = (): Promise<void> => {
|
|
5222
|
-
|
|
5223
|
-
if (
|
|
5224
|
-
return
|
|
5248
|
+
clearLiveUpdateTimer();
|
|
5249
|
+
if (pendingLiveUpdates.length === 0) {
|
|
5250
|
+
return liveFlushChain;
|
|
5225
5251
|
}
|
|
5226
|
-
const updates =
|
|
5252
|
+
const updates = coalesceLiveUpdates(pendingLiveUpdates.splice(0));
|
|
5227
5253
|
const extraOutputFields = Array.from(generatedOutputFields);
|
|
5228
|
-
const task =
|
|
5254
|
+
const task = liveFlushChain.then(async () => {
|
|
5229
5255
|
try {
|
|
5230
5256
|
await applyLiveMapRowUpdates({
|
|
5231
5257
|
req,
|
|
@@ -5235,33 +5261,29 @@ function createMinimalWorkerCtx(
|
|
|
5235
5261
|
updates,
|
|
5236
5262
|
});
|
|
5237
5263
|
} catch (error) {
|
|
5238
|
-
|
|
5239
|
-
if (
|
|
5264
|
+
liveUpdateFailures += 1;
|
|
5265
|
+
if (liveUpdateFailures <= MAP_ROW_FAILURE_SAMPLE_LIMIT) {
|
|
5240
5266
|
emitEvent({
|
|
5241
5267
|
type: 'log',
|
|
5242
5268
|
level: 'warn',
|
|
5243
|
-
message:
|
|
5244
|
-
`Live row update flush failed for ctx.dataset("${name}") ` +
|
|
5245
|
-
`(non-fatal; terminal rows still persist): ${formatWorkerRowFailureMessage(error)}`,
|
|
5269
|
+
message: formatWorkerRowFailureMessage(error),
|
|
5246
5270
|
ts: nowMs(),
|
|
5247
5271
|
});
|
|
5248
5272
|
}
|
|
5249
5273
|
}
|
|
5250
5274
|
});
|
|
5251
|
-
|
|
5275
|
+
liveFlushChain = task.catch(() => undefined);
|
|
5252
5276
|
return task;
|
|
5253
5277
|
};
|
|
5254
5278
|
|
|
5255
5279
|
const scheduleLiveRowUpdates = () => {
|
|
5256
|
-
if (
|
|
5257
|
-
pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
|
|
5258
|
-
) {
|
|
5280
|
+
if (pendingLiveUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS) {
|
|
5259
5281
|
void flushLiveRowUpdates().catch(() => undefined);
|
|
5260
5282
|
return;
|
|
5261
5283
|
}
|
|
5262
|
-
if (
|
|
5263
|
-
|
|
5264
|
-
|
|
5284
|
+
if (liveUpdateTimer) return;
|
|
5285
|
+
liveUpdateTimer = setTimeout(() => {
|
|
5286
|
+
liveUpdateTimer = null;
|
|
5265
5287
|
void flushLiveRowUpdates().catch(() => undefined);
|
|
5266
5288
|
}, MAP_INCREMENTAL_PERSIST_INTERVAL_MS);
|
|
5267
5289
|
};
|
|
@@ -5269,10 +5291,8 @@ function createMinimalWorkerCtx(
|
|
|
5269
5291
|
const enqueueLiveRowUpdate = (
|
|
5270
5292
|
update: Omit<PlayRowUpdate, 'rowId'> & { runId?: string },
|
|
5271
5293
|
): Promise<void> => {
|
|
5272
|
-
|
|
5273
|
-
if (
|
|
5274
|
-
pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
|
|
5275
|
-
) {
|
|
5294
|
+
pendingLiveUpdates.push(update);
|
|
5295
|
+
if (pendingLiveUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS) {
|
|
5276
5296
|
void flushLiveRowUpdates().catch(() => undefined);
|
|
5277
5297
|
return Promise.resolve();
|
|
5278
5298
|
}
|
|
@@ -5397,10 +5417,6 @@ function createMinimalWorkerCtx(
|
|
|
5397
5417
|
else stepCellsCompleted += 1;
|
|
5398
5418
|
await enqueueLiveRowUpdate({
|
|
5399
5419
|
key: entry.rowKey,
|
|
5400
|
-
tableNamespace: name,
|
|
5401
|
-
runId: req.runId,
|
|
5402
|
-
status: 'running',
|
|
5403
|
-
stage: stepOutput.stepId,
|
|
5404
5420
|
dataPatch: {
|
|
5405
5421
|
[stepOutput.columnName]: stepOutput.value,
|
|
5406
5422
|
},
|
|
@@ -5431,6 +5447,12 @@ function createMinimalWorkerCtx(
|
|
|
5431
5447
|
completedAt: nowMs(),
|
|
5432
5448
|
};
|
|
5433
5449
|
}
|
|
5450
|
+
if (fieldEntries.length > 1) {
|
|
5451
|
+
await enqueueLiveRowUpdate({
|
|
5452
|
+
key: entry.rowKey,
|
|
5453
|
+
dataPatch: { [key]: resolved.value },
|
|
5454
|
+
});
|
|
5455
|
+
}
|
|
5434
5456
|
activeField = null;
|
|
5435
5457
|
}
|
|
5436
5458
|
for (const stepOutput of stepProgramOutputs) {
|
|
@@ -5607,7 +5629,7 @@ function createMinimalWorkerCtx(
|
|
|
5607
5629
|
});
|
|
5608
5630
|
try {
|
|
5609
5631
|
await flushLiveRowUpdates();
|
|
5610
|
-
await
|
|
5632
|
+
await liveFlushChain;
|
|
5611
5633
|
await enqueuePersistExecutedRows();
|
|
5612
5634
|
await persistFlushChain;
|
|
5613
5635
|
if (persistFailure) throw persistFailure;
|
|
@@ -6733,9 +6755,11 @@ function createMinimalWorkerCtx(
|
|
|
6733
6755
|
childPlaySlot?.release();
|
|
6734
6756
|
}
|
|
6735
6757
|
},
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6758
|
+
{
|
|
6759
|
+
repairRunningReceiptForSameRunAfterWaitTimeout: true,
|
|
6760
|
+
runningReceiptWaitMaxAttempts:
|
|
6761
|
+
resolveRuntimeToolReceiptWaitMaxAttempts(input),
|
|
6762
|
+
},
|
|
6739
6763
|
);
|
|
6740
6764
|
},
|
|
6741
6765
|
async fetch(
|
|
@@ -7857,7 +7881,9 @@ function extractMaxCreditsPerRun(contractSnapshot: unknown): number | null {
|
|
|
7857
7881
|
}
|
|
7858
7882
|
|
|
7859
7883
|
function shouldSkipWorkerComputeBilling(req: RunRequest): boolean {
|
|
7860
|
-
return
|
|
7884
|
+
return (
|
|
7885
|
+
req.integrationMode === 'fixture' || req.integrationMode === 'eval_stub'
|
|
7886
|
+
);
|
|
7861
7887
|
}
|
|
7862
7888
|
|
|
7863
7889
|
async function finalizeWorkerComputeBilling(input: {
|
|
@@ -17,11 +17,10 @@ export { TOOL_CALLING_MAP_CHUNK_SIZE };
|
|
|
17
17
|
// Paid Cloudflare Workers support a much higher configured subrequest limit.
|
|
18
18
|
// Keep a large buffer for coordinator/storage calls around row-level unbatched
|
|
19
19
|
// tool RPCs, but avoid pathological one-row chunks for provider waterfalls.
|
|
20
|
-
export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET =
|
|
20
|
+
export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET = 200;
|
|
21
21
|
// Fresh unbatched tool calls use one RUNTIME_API integration execute RPC and
|
|
22
22
|
// one HARNESS durable-receipt completion RPC. Batch-cap rows by both.
|
|
23
23
|
export const SUBREQUESTS_PER_UNBATCHED_TOOL_CALL = 2;
|
|
24
|
-
|
|
25
24
|
export type WorkerMapChunkPlanInput = {
|
|
26
25
|
mapName: string;
|
|
27
26
|
rowCountHint: number | null;
|
|
@@ -45,7 +44,7 @@ type ChunkSizingPlan = {
|
|
|
45
44
|
softWorkflowStepBudget?: number | null;
|
|
46
45
|
};
|
|
47
46
|
|
|
48
|
-
type MapToolStats = [totalToolCount: number,
|
|
47
|
+
type MapToolStats = [totalToolCount: number, unbatchedSubrequestCount: number];
|
|
49
48
|
|
|
50
49
|
function fieldRoot(field: string | null | undefined): string {
|
|
51
50
|
return String(field ?? '').split('.', 1)[0] ?? '';
|
|
@@ -70,41 +69,42 @@ function declarationBelongsToMapOutput(
|
|
|
70
69
|
);
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
function
|
|
74
|
-
|
|
72
|
+
function countTool(toolId: string): MapToolStats {
|
|
73
|
+
if (getPlayRuntimeBatchStrategy(toolId) !== null) return [1, 0];
|
|
74
|
+
return [1, toolId === 'test_high_single' ? 5 : 2];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function addStats(
|
|
78
|
+
[total, subrequests]: MapToolStats,
|
|
79
|
+
stats: MapToolStats,
|
|
80
|
+
): MapToolStats {
|
|
81
|
+
return [total + stats[0], subrequests + stats[1]];
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
function countToolSubsteps(
|
|
78
85
|
substeps: readonly PlayStaticSubstep[],
|
|
79
86
|
): MapToolStats {
|
|
80
|
-
let
|
|
81
|
-
let unbatchedToolCount = 0;
|
|
87
|
+
let stats: MapToolStats = [0, 0];
|
|
82
88
|
|
|
83
89
|
for (const substep of substeps) {
|
|
84
|
-
|
|
85
|
-
totalToolCount += nestedTotal;
|
|
86
|
-
unbatchedToolCount += nestedUnbatched;
|
|
90
|
+
stats = addStats(stats, countToolSubstep(substep));
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
return
|
|
93
|
+
return stats;
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
function countToolSubstep(substep: PlayStaticSubstep): MapToolStats {
|
|
93
97
|
if (substep.type === 'tool') {
|
|
94
|
-
return
|
|
98
|
+
return countTool(substep.toolId);
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
if (substep.type === 'waterfall') {
|
|
98
|
-
let
|
|
99
|
-
let unbatchedToolCount = 0;
|
|
102
|
+
let stats: MapToolStats = [0, 0];
|
|
100
103
|
for (const step of substep.steps ?? []) {
|
|
101
104
|
if (!step.toolId) continue;
|
|
102
|
-
|
|
103
|
-
if (isUnbatchedTool(step.toolId)) {
|
|
104
|
-
unbatchedToolCount += 1;
|
|
105
|
-
}
|
|
105
|
+
stats = addStats(stats, countTool(step.toolId));
|
|
106
106
|
}
|
|
107
|
-
return
|
|
107
|
+
return stats;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
if (substep.type === 'step_suite') {
|
|
@@ -117,9 +117,9 @@ function countToolSubstep(substep: PlayStaticSubstep): MapToolStats {
|
|
|
117
117
|
countToolSubsteps(branch.steps),
|
|
118
118
|
);
|
|
119
119
|
return branchStats.reduce<MapToolStats>(
|
|
120
|
-
([totalMax,
|
|
120
|
+
([totalMax, subrequestsMax], [total, subrequests]) => [
|
|
121
121
|
Math.max(totalMax, total),
|
|
122
|
-
Math.max(
|
|
122
|
+
Math.max(subrequestsMax, subrequests),
|
|
123
123
|
],
|
|
124
124
|
[0, 0],
|
|
125
125
|
);
|
|
@@ -151,9 +151,9 @@ function countStepSuiteTools(
|
|
|
151
151
|
conditionalChildren.length < childStats.length / 2
|
|
152
152
|
) {
|
|
153
153
|
return childStats.reduce<MapToolStats>(
|
|
154
|
-
([totalSum,
|
|
154
|
+
([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
|
|
155
155
|
totalSum + total,
|
|
156
|
-
|
|
156
|
+
subrequestsSum + subrequests,
|
|
157
157
|
],
|
|
158
158
|
[0, 0],
|
|
159
159
|
);
|
|
@@ -166,9 +166,9 @@ function countStepSuiteTools(
|
|
|
166
166
|
});
|
|
167
167
|
if (!sameRoot) {
|
|
168
168
|
return childStats.reduce<MapToolStats>(
|
|
169
|
-
([totalSum,
|
|
169
|
+
([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
|
|
170
170
|
totalSum + total,
|
|
171
|
-
|
|
171
|
+
subrequestsSum + subrequests,
|
|
172
172
|
],
|
|
173
173
|
[0, 0],
|
|
174
174
|
);
|
|
@@ -181,52 +181,41 @@ function countStepSuiteTools(
|
|
|
181
181
|
!(step.type === 'control_flow' && step.kind === 'conditional'),
|
|
182
182
|
)
|
|
183
183
|
.reduce<MapToolStats>(
|
|
184
|
-
([totalSum,
|
|
184
|
+
([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
|
|
185
185
|
totalSum + total,
|
|
186
|
-
|
|
186
|
+
subrequestsSum + subrequests,
|
|
187
187
|
],
|
|
188
188
|
[0, 0],
|
|
189
189
|
);
|
|
190
190
|
const conditionalStats = conditionalChildren.reduce<MapToolStats>(
|
|
191
|
-
([totalSum,
|
|
191
|
+
([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
|
|
192
192
|
totalSum + total,
|
|
193
|
-
|
|
193
|
+
subrequestsSum + subrequests,
|
|
194
194
|
],
|
|
195
195
|
[0, 0],
|
|
196
196
|
);
|
|
197
197
|
|
|
198
|
-
return
|
|
199
|
-
unconditionalStats[0] + conditionalStats[0],
|
|
200
|
-
unconditionalStats[1] + conditionalStats[1],
|
|
201
|
-
];
|
|
198
|
+
return addStats(unconditionalStats, conditionalStats);
|
|
202
199
|
}
|
|
203
200
|
|
|
204
201
|
function countProducerTools(
|
|
205
202
|
producers: readonly PlayStaticColumnProducer[],
|
|
206
203
|
): MapToolStats {
|
|
207
|
-
let
|
|
208
|
-
let unbatchedToolCount = 0;
|
|
204
|
+
let stats: MapToolStats = [0, 0];
|
|
209
205
|
|
|
210
206
|
for (const producer of producers) {
|
|
211
207
|
if (producer.kind === 'tool' && producer.toolId) {
|
|
212
|
-
|
|
213
|
-
if (isUnbatchedTool(producer.toolId)) {
|
|
214
|
-
unbatchedToolCount += 1;
|
|
215
|
-
}
|
|
208
|
+
stats = addStats(stats, countTool(producer.toolId));
|
|
216
209
|
}
|
|
217
210
|
if (producer.substep.type === 'waterfall') {
|
|
218
|
-
|
|
219
|
-
totalToolCount += nestedTotal;
|
|
220
|
-
unbatchedToolCount += nestedUnbatched;
|
|
211
|
+
stats = addStats(stats, countToolSubstep(producer.substep));
|
|
221
212
|
}
|
|
222
213
|
if (producer.steps?.length) {
|
|
223
|
-
|
|
224
|
-
totalToolCount += nestedTotal;
|
|
225
|
-
unbatchedToolCount += nestedUnbatched;
|
|
214
|
+
stats = addStats(stats, countProducerTools(producer.steps));
|
|
226
215
|
}
|
|
227
216
|
}
|
|
228
217
|
|
|
229
|
-
return
|
|
218
|
+
return stats;
|
|
230
219
|
}
|
|
231
220
|
|
|
232
221
|
function datasetBelongsToPlanMap(
|
|
@@ -278,8 +267,7 @@ function countFallbackDeclarationTools(input: {
|
|
|
278
267
|
outputFields: readonly string[];
|
|
279
268
|
externalStepFields?: readonly string[];
|
|
280
269
|
}): MapToolStats {
|
|
281
|
-
let
|
|
282
|
-
let unbatchedToolCount = 0;
|
|
270
|
+
let stats: MapToolStats = [0, 0];
|
|
283
271
|
|
|
284
272
|
for (const declaration of input.declarations) {
|
|
285
273
|
if (
|
|
@@ -291,23 +279,19 @@ function countFallbackDeclarationTools(input: {
|
|
|
291
279
|
) {
|
|
292
280
|
continue;
|
|
293
281
|
}
|
|
294
|
-
|
|
295
|
-
if (isUnbatchedTool(declaration.toolId)) {
|
|
296
|
-
unbatchedToolCount += 1;
|
|
297
|
-
}
|
|
282
|
+
stats = addStats(stats, countTool(declaration.toolId));
|
|
298
283
|
}
|
|
299
284
|
|
|
300
|
-
return
|
|
285
|
+
return stats;
|
|
301
286
|
}
|
|
302
287
|
|
|
303
288
|
function countDeclarationTools(
|
|
304
289
|
declarations: readonly { toolId: string; field?: string | null }[],
|
|
305
290
|
): MapToolStats {
|
|
306
|
-
return
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
];
|
|
291
|
+
return declarations.reduce<MapToolStats>(
|
|
292
|
+
(stats, declaration) => addStats(stats, countTool(declaration.toolId)),
|
|
293
|
+
[0, 0],
|
|
294
|
+
);
|
|
311
295
|
}
|
|
312
296
|
|
|
313
297
|
function countNestedExecutionSteps(
|
|
@@ -409,12 +393,13 @@ export function chooseWorkerMapRowsPerChunk(
|
|
|
409
393
|
}
|
|
410
394
|
|
|
411
395
|
if (mapToolStats[1] > 0) {
|
|
412
|
-
const
|
|
396
|
+
const unbatchedSubrequestsPerRow =
|
|
413
397
|
staticMapToolStats !== null
|
|
414
398
|
? mapToolStats[1]
|
|
415
|
-
: Math.max(
|
|
416
|
-
|
|
417
|
-
|
|
399
|
+
: Math.max(
|
|
400
|
+
(planMap?.stepsPerChunk ?? 1) * SUBREQUESTS_PER_UNBATCHED_TOOL_CALL,
|
|
401
|
+
mapToolStats[1],
|
|
402
|
+
);
|
|
418
403
|
const unbatchedToolRowsPerChunk = Math.max(
|
|
419
404
|
1,
|
|
420
405
|
Math.floor(
|
|
@@ -104,10 +104,10 @@ export const SDK_RELEASE = {
|
|
|
104
104
|
// 0.1.111 ships dataset-native tool list getters and result row datasets.
|
|
105
105
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
106
106
|
// fields shipped in 0.1.153.
|
|
107
|
-
version: '0.1.
|
|
107
|
+
version: '0.1.172',
|
|
108
108
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
109
109
|
supportPolicy: {
|
|
110
|
-
latest: '0.1.
|
|
110
|
+
latest: '0.1.172',
|
|
111
111
|
minimumSupported: '0.1.53',
|
|
112
112
|
deprecatedBelow: '0.1.53',
|
|
113
113
|
commandMinimumSupported: [
|
|
@@ -1315,6 +1315,7 @@ export class PlayContextImpl {
|
|
|
1315
1315
|
repairRunningReceiptForSameRun?: boolean;
|
|
1316
1316
|
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
1317
1317
|
runningReceiptWaitMaxAttempts?: number;
|
|
1318
|
+
runningReceiptWaitDelayMs?: number;
|
|
1318
1319
|
reclaimRunning?: boolean;
|
|
1319
1320
|
markSkipped?: (output: T) => Promise<void> | void;
|
|
1320
1321
|
onRecovered?: (
|
|
@@ -1347,6 +1348,7 @@ export class PlayContextImpl {
|
|
|
1347
1348
|
repairRunningReceiptForSameRunAfterWaitTimeout:
|
|
1348
1349
|
opts.repairRunningReceiptForSameRunAfterWaitTimeout,
|
|
1349
1350
|
runningReceiptWaitMaxAttempts: opts.runningReceiptWaitMaxAttempts,
|
|
1351
|
+
runningReceiptWaitDelayMs: opts.runningReceiptWaitDelayMs,
|
|
1350
1352
|
reclaimRunning: opts.reclaimRunning,
|
|
1351
1353
|
markSkipped: opts.markSkipped,
|
|
1352
1354
|
onRecovered: opts.onRecovered,
|
|
@@ -3881,6 +3883,8 @@ export class PlayContextImpl {
|
|
|
3881
3883
|
);
|
|
3882
3884
|
},
|
|
3883
3885
|
repairRunningReceiptForSameRunAfterWaitTimeout: true,
|
|
3886
|
+
runningReceiptWaitMaxAttempts:
|
|
3887
|
+
resolveRuntimeToolReceiptWaitMaxAttempts(input),
|
|
3884
3888
|
execute: executePlayCall,
|
|
3885
3889
|
},
|
|
3886
3890
|
);
|
|
@@ -114,6 +114,9 @@ const RUNTIME_DB_SESSION_ROW_LIMIT_FLOOR = DIRECT_POSTGRES_BATCH_SIZE;
|
|
|
114
114
|
const APPEND_KEY_SUFFIX_LENGTH = 12;
|
|
115
115
|
const RUNTIME_DB_SESSION_TTL_SECONDS = 10 * 60;
|
|
116
116
|
const RUNTIME_SHEET_ENSURE_CACHE_TTL_MS = 10 * 60_000;
|
|
117
|
+
const RUNTIME_SHEET_READY_AFTER_ENSURE_RETRY_DELAYS_MS = [
|
|
118
|
+
100, 250, 500, 1_000, 2_000,
|
|
119
|
+
] as const;
|
|
117
120
|
const RUNTIME_DB_SESSION_RENEWAL_WINDOW_MS = 60_000;
|
|
118
121
|
const RUNTIME_API_RETRY_DELAYS_MS = [
|
|
119
122
|
250, 500, 1_000, 2_000, 4_000, 8_000, 8_000,
|
|
@@ -529,6 +532,41 @@ async function isRuntimeSheetSchemaReady(
|
|
|
529
532
|
}
|
|
530
533
|
}
|
|
531
534
|
|
|
535
|
+
async function waitForRuntimeSheetSchemaReadyAfterEnsure(
|
|
536
|
+
session: RuntimePostgresSession,
|
|
537
|
+
input: {
|
|
538
|
+
sheetContract: PlaySheetContract;
|
|
539
|
+
timings?: RuntimeSheetTiming[];
|
|
540
|
+
},
|
|
541
|
+
): Promise<boolean> {
|
|
542
|
+
for (
|
|
543
|
+
let attempt = 0;
|
|
544
|
+
attempt <= RUNTIME_SHEET_READY_AFTER_ENSURE_RETRY_DELAYS_MS.length;
|
|
545
|
+
attempt += 1
|
|
546
|
+
) {
|
|
547
|
+
const recheckStartedAt = Date.now();
|
|
548
|
+
const ready = await isRuntimeSheetSchemaReady(session, {
|
|
549
|
+
sheetContract: input.sheetContract,
|
|
550
|
+
});
|
|
551
|
+
input.timings?.push({
|
|
552
|
+
phase: 'schema_check_after_preloaded_session_ensure',
|
|
553
|
+
ms: Date.now() - recheckStartedAt,
|
|
554
|
+
ready,
|
|
555
|
+
retried: attempt > 0,
|
|
556
|
+
});
|
|
557
|
+
if (ready) {
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
const delay =
|
|
561
|
+
RUNTIME_SHEET_READY_AFTER_ENSURE_RETRY_DELAYS_MS[attempt] ?? null;
|
|
562
|
+
if (delay === null) {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
await sleep(delay);
|
|
566
|
+
}
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
|
|
532
570
|
async function ensureRuntimeSheetForPreloadedSession(
|
|
533
571
|
context: RuntimeApiContext & { playName: string },
|
|
534
572
|
input: {
|
|
@@ -597,38 +635,35 @@ async function ensureRuntimeSheetForPreloadedSession(
|
|
|
597
635
|
return;
|
|
598
636
|
}
|
|
599
637
|
const ensureStartedAt = Date.now();
|
|
600
|
-
const promise =
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
});
|
|
605
|
-
runtimeSheetEnsureCache.set(cacheKey, {
|
|
606
|
-
expiresAt: now + RUNTIME_SHEET_ENSURE_CACHE_TTL_MS,
|
|
607
|
-
promise,
|
|
608
|
-
});
|
|
609
|
-
try {
|
|
610
|
-
await promise;
|
|
611
|
-
input.timings?.push({
|
|
612
|
-
phase: 'ensure_sheet_for_preloaded_session',
|
|
613
|
-
ms: Date.now() - ensureStartedAt,
|
|
614
|
-
});
|
|
615
|
-
const recheckStartedAt = Date.now();
|
|
616
|
-
const readyAfterEnsure = await isRuntimeSheetSchemaReady(input.session, {
|
|
638
|
+
const promise = (async () => {
|
|
639
|
+
await ensureRuntimeSheet(context, {
|
|
640
|
+
playName: context.playName,
|
|
641
|
+
tableNamespace: input.tableNamespace,
|
|
617
642
|
sheetContract: input.sheetContract,
|
|
618
643
|
});
|
|
619
644
|
input.timings?.push({
|
|
620
|
-
phase: '
|
|
621
|
-
ms: Date.now() -
|
|
622
|
-
ready: readyAfterEnsure,
|
|
645
|
+
phase: 'ensure_sheet_for_preloaded_session',
|
|
646
|
+
ms: Date.now() - ensureStartedAt,
|
|
623
647
|
});
|
|
648
|
+
const readyAfterEnsure = await waitForRuntimeSheetSchemaReadyAfterEnsure(
|
|
649
|
+
input.session,
|
|
650
|
+
{
|
|
651
|
+
sheetContract: input.sheetContract,
|
|
652
|
+
timings: input.timings,
|
|
653
|
+
},
|
|
654
|
+
);
|
|
624
655
|
if (!readyAfterEnsure) {
|
|
625
|
-
if (runtimeSheetEnsureCache.get(cacheKey)?.promise === promise) {
|
|
626
|
-
runtimeSheetEnsureCache.delete(cacheKey);
|
|
627
|
-
}
|
|
628
656
|
throw new Error(
|
|
629
657
|
`Runtime sheet schema for ctx.dataset("${input.tableNamespace}") is still not ready after ensure_sheet.`,
|
|
630
658
|
);
|
|
631
659
|
}
|
|
660
|
+
})();
|
|
661
|
+
runtimeSheetEnsureCache.set(cacheKey, {
|
|
662
|
+
expiresAt: now + RUNTIME_SHEET_ENSURE_CACHE_TTL_MS,
|
|
663
|
+
promise,
|
|
664
|
+
});
|
|
665
|
+
try {
|
|
666
|
+
await promise;
|
|
632
667
|
} catch (error) {
|
|
633
668
|
if (runtimeSheetEnsureCache.get(cacheKey)?.promise === promise) {
|
|
634
669
|
runtimeSheetEnsureCache.delete(cacheKey);
|