deepline 0.1.226 → 0.1.227
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/bundling-sources/apps/play-runner-workers/src/entry.ts +25 -15
- package/dist/bundling-sources/sdk/src/play.ts +5 -0
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +65 -4
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +12 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +150 -18
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
|
@@ -2539,6 +2539,7 @@ type WorkerStepProgram = {
|
|
|
2539
2539
|
|
|
2540
2540
|
type WorkerMapOptions = {
|
|
2541
2541
|
description?: string;
|
|
2542
|
+
mode?: 'upsert' | 'net_new';
|
|
2542
2543
|
concurrency?: number;
|
|
2543
2544
|
key?:
|
|
2544
2545
|
| string
|
|
@@ -4870,9 +4871,18 @@ function createMinimalWorkerCtx(
|
|
|
4870
4871
|
runId: req.runId,
|
|
4871
4872
|
inputOffset: 0,
|
|
4872
4873
|
attemptLeaseTtlMs: runtimeSheetAttemptLeaseTtlMs(req),
|
|
4874
|
+
...(opts?.mode ? { mode: opts.mode } : {}),
|
|
4873
4875
|
...mapWriteAttempt,
|
|
4874
4876
|
});
|
|
4875
4877
|
const mapWriteVersion = mapReservation?.writeVersion ?? null;
|
|
4878
|
+
const netNewRowKeys =
|
|
4879
|
+
opts?.mode === 'net_new'
|
|
4880
|
+
? new Set(
|
|
4881
|
+
(mapReservation?.pendingRows ?? [])
|
|
4882
|
+
.map((row) => resolveMapRowOutcomeKey(row))
|
|
4883
|
+
.filter((key): key is string => Boolean(key)),
|
|
4884
|
+
)
|
|
4885
|
+
: null;
|
|
4876
4886
|
|
|
4877
4887
|
let totalRowsWritten = 0;
|
|
4878
4888
|
let datasetLifecycleRegistered = false;
|
|
@@ -4896,7 +4906,7 @@ function createMinimalWorkerCtx(
|
|
|
4896
4906
|
chunkIndex,
|
|
4897
4907
|
});
|
|
4898
4908
|
const keyStartedAt = nowMs();
|
|
4899
|
-
|
|
4909
|
+
let chunkEntries = chunkRows.map((row, localIndex) => {
|
|
4900
4910
|
const absoluteIndex = baseOffset + chunkStart + localIndex;
|
|
4901
4911
|
const rowKey = resolveRowKey(
|
|
4902
4912
|
row,
|
|
@@ -4906,6 +4916,11 @@ function createMinimalWorkerCtx(
|
|
|
4906
4916
|
);
|
|
4907
4917
|
return { row, absoluteIndex, rowKey };
|
|
4908
4918
|
});
|
|
4919
|
+
if (netNewRowKeys) {
|
|
4920
|
+
chunkEntries = chunkEntries.filter(({ rowKey }) =>
|
|
4921
|
+
netNewRowKeys.has(rowKey),
|
|
4922
|
+
);
|
|
4923
|
+
}
|
|
4909
4924
|
recordRunnerPerfTrace({
|
|
4910
4925
|
req,
|
|
4911
4926
|
phase: 'runner.map_chunk.keys',
|
|
@@ -4968,7 +4983,10 @@ function createMinimalWorkerCtx(
|
|
|
4968
4983
|
},
|
|
4969
4984
|
});
|
|
4970
4985
|
mapLatencyProfile.recordPhase('prepare_rows', nowMs() - prepareStartedAt);
|
|
4971
|
-
|
|
4986
|
+
// net_new reserves the entire source before chunk execution, so its
|
|
4987
|
+
// progress describes the admitted subset rather than the source CSV.
|
|
4988
|
+
const progressTotalRows =
|
|
4989
|
+
netNewRowKeys?.size ?? rowCountHint ?? chunkRows.length;
|
|
4972
4990
|
const preparedCompletedRows = Math.min(
|
|
4973
4991
|
progressTotalRows,
|
|
4974
4992
|
totalRowsWritten,
|
|
@@ -5023,15 +5041,8 @@ function createMinimalWorkerCtx(
|
|
|
5023
5041
|
({ rowKey }) => !preparedKeys.has(rowKey),
|
|
5024
5042
|
);
|
|
5025
5043
|
if (missingPreparedRows.length > 0) {
|
|
5026
|
-
const rowKeySamples = missingPreparedRows
|
|
5027
|
-
.map(({ rowKey }) => rowKey)
|
|
5028
|
-
.slice(0, MAP_ROW_FAILURE_SAMPLE_LIMIT);
|
|
5029
5044
|
throw new Error(
|
|
5030
|
-
`ctx.dataset("${name}")
|
|
5031
|
-
`Every input row must be claimed, completed, or blocked before execution starts.` +
|
|
5032
|
-
(rowKeySamples.length > 0
|
|
5033
|
-
? ` Missing row keys: ${rowKeySamples.join(', ')}`
|
|
5034
|
-
: ''),
|
|
5045
|
+
`ctx.dataset("${name}") preparation omitted ${missingPreparedRows.length} row disposition(s).`,
|
|
5035
5046
|
);
|
|
5036
5047
|
}
|
|
5037
5048
|
const rowsToExecuteEntries = chunkEntries.filter(({ rowKey }) =>
|
|
@@ -5986,9 +5997,8 @@ function createMinimalWorkerCtx(
|
|
|
5986
5997
|
// dataset (their recoverable state lives in the runtime sheet).
|
|
5987
5998
|
if (key && executedRow) resultByKey.set(key, executedRow);
|
|
5988
5999
|
}
|
|
5989
|
-
const outEntries =
|
|
5990
|
-
.map((
|
|
5991
|
-
const entry = chunkEntries[index]!;
|
|
6000
|
+
const outEntries = chunkEntries
|
|
6001
|
+
.map((entry) => {
|
|
5992
6002
|
const row = resultByKey.get(entry.rowKey);
|
|
5993
6003
|
return row
|
|
5994
6004
|
? {
|
|
@@ -6531,11 +6541,11 @@ function createMinimalWorkerCtx(
|
|
|
6531
6541
|
}
|
|
6532
6542
|
enqueueMapProgressUpdate({
|
|
6533
6543
|
completed: totalRowsWritten,
|
|
6534
|
-
total: rowCountHint ?? undefined,
|
|
6544
|
+
total: netNewRowKeys?.size ?? rowCountHint ?? undefined,
|
|
6535
6545
|
...(totalRowsFailed > 0 ? { failed: totalRowsFailed } : {}),
|
|
6536
6546
|
message: formatMapProgressMessage(
|
|
6537
6547
|
totalRowsWritten,
|
|
6538
|
-
rowCountHint ?? undefined,
|
|
6548
|
+
netNewRowKeys?.size ?? rowCountHint ?? undefined,
|
|
6539
6549
|
),
|
|
6540
6550
|
});
|
|
6541
6551
|
if (previewRows.length < WORKER_DATASET_PREVIEW_ROWS) {
|
|
@@ -560,6 +560,11 @@ export type DatasetBuilder<
|
|
|
560
560
|
*/
|
|
561
561
|
run(options?: {
|
|
562
562
|
description?: string;
|
|
563
|
+
/**
|
|
564
|
+
* `upsert` (default) returns every input row, reusing persisted work for
|
|
565
|
+
* existing keys. `net_new` atomically inserts and returns only unseen keys.
|
|
566
|
+
*/
|
|
567
|
+
mode?: 'upsert' | 'net_new';
|
|
563
568
|
key?:
|
|
564
569
|
| (keyof InputRow & string)
|
|
565
570
|
| readonly (keyof InputRow & string)[]
|
|
@@ -108,10 +108,10 @@ export const SDK_RELEASE = {
|
|
|
108
108
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
109
109
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
110
110
|
// automatically without blocking their current command.
|
|
111
|
-
version: '0.1.
|
|
111
|
+
version: '0.1.227',
|
|
112
112
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
113
113
|
supportPolicy: {
|
|
114
|
-
latest: '0.1.
|
|
114
|
+
latest: '0.1.227',
|
|
115
115
|
minimumSupported: '0.1.53',
|
|
116
116
|
deprecatedBelow: '0.1.219',
|
|
117
117
|
commandMinimumSupported: [
|
|
@@ -3943,7 +3943,10 @@ export class PlayContextImpl {
|
|
|
3943
3943
|
this.#options.runId,
|
|
3944
3944
|
);
|
|
3945
3945
|
if (shouldStreamRuntimeBackedDataset) {
|
|
3946
|
-
|
|
3946
|
+
// Net-new admission happens one source page at a time. Its progress and
|
|
3947
|
+
// returned dataset describe admitted rows, not every provider/CSV row
|
|
3948
|
+
// supplied to this run, so accumulate the admitted count below.
|
|
3949
|
+
totalInputCount = options?.mode === 'net_new' ? 0 : await items.count();
|
|
3947
3950
|
const mapScope = this.createMapExecutionScope({
|
|
3948
3951
|
logicalNamespace: normalizedMapNamespace,
|
|
3949
3952
|
artifactTableNamespace: resolvedTableNamespace,
|
|
@@ -4097,6 +4100,7 @@ export class PlayContextImpl {
|
|
|
4097
4100
|
staticPipeline: this.currentStaticPipeline,
|
|
4098
4101
|
forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
|
|
4099
4102
|
inputOffset: page.offset,
|
|
4103
|
+
mode: options?.mode,
|
|
4100
4104
|
},
|
|
4101
4105
|
);
|
|
4102
4106
|
resolvedTableNamespace = normalizeTableNamespace(
|
|
@@ -4140,12 +4144,21 @@ export class PlayContextImpl {
|
|
|
4140
4144
|
index: originalIndex,
|
|
4141
4145
|
};
|
|
4142
4146
|
});
|
|
4147
|
+
const admittedSeededItems =
|
|
4148
|
+
options?.mode === 'net_new'
|
|
4149
|
+
? seededItems.filter(({ row, index }) =>
|
|
4150
|
+
pendingRowsByKey.has(seedRowIdentity(row, index)),
|
|
4151
|
+
)
|
|
4152
|
+
: seededItems;
|
|
4153
|
+
if (options?.mode === 'net_new') {
|
|
4154
|
+
totalInputCount += admittedSeededItems.length;
|
|
4155
|
+
}
|
|
4143
4156
|
|
|
4144
4157
|
const rowsToExecuteByKey = new Map<
|
|
4145
4158
|
string,
|
|
4146
4159
|
{ row: Record<string, unknown>; originalIndex: number }
|
|
4147
4160
|
>();
|
|
4148
|
-
for (const { row, index } of
|
|
4161
|
+
for (const { row, index } of admittedSeededItems) {
|
|
4149
4162
|
if (explicitKeyResolver) {
|
|
4150
4163
|
const explicitKey = explicitKeyResolver(row, index);
|
|
4151
4164
|
if (seenExplicitKeys.has(explicitKey)) {
|
|
@@ -4280,6 +4293,45 @@ export class PlayContextImpl {
|
|
|
4280
4293
|
page.rows.length = 0;
|
|
4281
4294
|
}
|
|
4282
4295
|
|
|
4296
|
+
// Isolated failures are a partial result only when at least one admitted
|
|
4297
|
+
// row completed. A fully failed admission run is systemic: preserve the
|
|
4298
|
+
// failed sheet rows for recovery, but fail the enclosing play loudly.
|
|
4299
|
+
if (totalInputCount > 0 && failedCount === totalInputCount) {
|
|
4300
|
+
const completedRows = 0;
|
|
4301
|
+
this.setMapFrame({
|
|
4302
|
+
mapInvocationId: mapScope.mapInvocationId,
|
|
4303
|
+
mapNodeId: mapScope.mapNodeId ?? null,
|
|
4304
|
+
logicalNamespace: mapScope.logicalNamespace,
|
|
4305
|
+
artifactTableNamespace: resolvedTableNamespace,
|
|
4306
|
+
status: 'failed',
|
|
4307
|
+
totalRows: totalInputCount,
|
|
4308
|
+
completedRowKeys: [],
|
|
4309
|
+
pendingRowKeys: [],
|
|
4310
|
+
completedRowsCount: completedRows,
|
|
4311
|
+
pendingRowsCount: 0,
|
|
4312
|
+
failedRowsCount: failedCount,
|
|
4313
|
+
startedAt:
|
|
4314
|
+
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]?.startedAt ??
|
|
4315
|
+
Date.now(),
|
|
4316
|
+
updatedAt: Date.now(),
|
|
4317
|
+
});
|
|
4318
|
+
this.emitExecutionEvent({
|
|
4319
|
+
type: 'map.failed',
|
|
4320
|
+
mapInvocationId: mapScope.mapInvocationId,
|
|
4321
|
+
mapNodeId: mapScope.mapNodeId ?? null,
|
|
4322
|
+
logicalNamespace: mapScope.logicalNamespace,
|
|
4323
|
+
artifactTableNamespace: resolvedTableNamespace,
|
|
4324
|
+
completedRows,
|
|
4325
|
+
failedRows: failedCount,
|
|
4326
|
+
totalRows: totalInputCount,
|
|
4327
|
+
at: Date.now(),
|
|
4328
|
+
});
|
|
4329
|
+
throw new Error(
|
|
4330
|
+
`ctx.dataset("${normalizedMapNamespace}") failed for every admitted row (${failedCount}/${totalInputCount}). ` +
|
|
4331
|
+
'Failed rows were persisted and will be retried on the next run.',
|
|
4332
|
+
);
|
|
4333
|
+
}
|
|
4334
|
+
|
|
4283
4335
|
this.setMapFrame({
|
|
4284
4336
|
mapInvocationId: mapScope.mapInvocationId,
|
|
4285
4337
|
mapNodeId: mapScope.mapNodeId ?? null,
|
|
@@ -4531,6 +4583,7 @@ export class PlayContextImpl {
|
|
|
4531
4583
|
executorToken: this.#options.executorToken,
|
|
4532
4584
|
staticPipeline: this.currentStaticPipeline,
|
|
4533
4585
|
forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
|
|
4586
|
+
mode: options?.mode,
|
|
4534
4587
|
},
|
|
4535
4588
|
);
|
|
4536
4589
|
resolvedTableNamespace = normalizeTableNamespace(
|
|
@@ -4587,8 +4640,16 @@ export class PlayContextImpl {
|
|
|
4587
4640
|
index,
|
|
4588
4641
|
};
|
|
4589
4642
|
});
|
|
4590
|
-
|
|
4591
|
-
|
|
4643
|
+
const admittedItems =
|
|
4644
|
+
options?.mode === 'net_new'
|
|
4645
|
+
? seededItems.filter(({ row, index }) =>
|
|
4646
|
+
pendingRowsByKey.has(seedRowIdentity(row, index)),
|
|
4647
|
+
)
|
|
4648
|
+
: seededItems;
|
|
4649
|
+
rawItems = admittedItems.map((item) => item.row);
|
|
4650
|
+
itemsToProcess = admittedItems.map((item) => item.row);
|
|
4651
|
+
itemOriginalIndexes = admittedItems.map((item) => item.index);
|
|
4652
|
+
totalInputCount = admittedItems.length;
|
|
4592
4653
|
}
|
|
4593
4654
|
|
|
4594
4655
|
const mapScope = this.createMapExecutionScope({
|
|
@@ -248,6 +248,16 @@ export interface DatasetOptions<TItem = Record<string, unknown>> {
|
|
|
248
248
|
* and are reused on re-run.
|
|
249
249
|
*/
|
|
250
250
|
onRowError?: 'isolate' | 'fail';
|
|
251
|
+
/**
|
|
252
|
+
* Controls how a persisted dataset admits rows whose key already exists.
|
|
253
|
+
*
|
|
254
|
+
* `upsert` (the default) preserves the row-preserving enrichment contract:
|
|
255
|
+
* existing rows are reused and returned with the current input. `net_new`
|
|
256
|
+
* atomically inserts only previously unseen keys and returns/processes only
|
|
257
|
+
* those inserted rows. Use `net_new` for a durable sourcing table, not for
|
|
258
|
+
* ordinary CSV enrichment reruns.
|
|
259
|
+
*/
|
|
260
|
+
mode?: 'upsert' | 'net_new';
|
|
251
261
|
}
|
|
252
262
|
|
|
253
263
|
export type DatasetDefinitionOptions<TItem = Record<string, unknown>> = Omit<
|
|
@@ -257,7 +267,7 @@ export type DatasetDefinitionOptions<TItem = Record<string, unknown>> = Omit<
|
|
|
257
267
|
|
|
258
268
|
export type DatasetRunOptions<TItem = Record<string, unknown>> = Pick<
|
|
259
269
|
DatasetOptions<TItem>,
|
|
260
|
-
'description' | 'key' | 'onRowError'
|
|
270
|
+
'description' | 'key' | 'onRowError' | 'mode'
|
|
261
271
|
>;
|
|
262
272
|
|
|
263
273
|
export interface ToolCallOptions {
|
|
@@ -597,6 +607,7 @@ export interface ContextOptions {
|
|
|
597
607
|
staticPipeline?: PlayStaticPipeline | null;
|
|
598
608
|
forceRefresh?: boolean;
|
|
599
609
|
inputOffset?: number;
|
|
610
|
+
mode?: DatasetRunOptions['mode'];
|
|
600
611
|
},
|
|
601
612
|
) => Promise<MapStartResult>;
|
|
602
613
|
/**
|
|
@@ -273,6 +273,8 @@ type RuntimeSheetPreparedRowDisposition = {
|
|
|
273
273
|
key: string;
|
|
274
274
|
disposition: RuntimeSheetPrepareDisposition;
|
|
275
275
|
};
|
|
276
|
+
|
|
277
|
+
type RuntimeSheetDatasetMode = 'upsert' | 'net_new';
|
|
276
278
|
const dbSessionCache = new Map<string, DbSessionCacheEntry>();
|
|
277
279
|
const dbSessionInFlight = new Map<string, Promise<CreateDbSessionResponse>>();
|
|
278
280
|
const postgresPools = new Map<string, RuntimePool>();
|
|
@@ -2976,6 +2978,7 @@ async function prepareRuntimeSheetDatasetRows(
|
|
|
2976
2978
|
physicalUpsertSetSql: string;
|
|
2977
2979
|
outputPhysicalColumns: PhysicalSheetColumnProjection[];
|
|
2978
2980
|
force?: boolean;
|
|
2981
|
+
mode?: RuntimeSheetDatasetMode;
|
|
2979
2982
|
},
|
|
2980
2983
|
): Promise<{
|
|
2981
2984
|
inserted: number;
|
|
@@ -3378,6 +3381,121 @@ async function prepareRuntimeSheetDatasetRows(
|
|
|
3378
3381
|
return { inserted, rowDispositions };
|
|
3379
3382
|
}
|
|
3380
3383
|
|
|
3384
|
+
/**
|
|
3385
|
+
* Atomically admit source-table keys that have not completed. A completed row
|
|
3386
|
+
* is consumed forever; failed rows and rows owned by this logical run remain
|
|
3387
|
+
* recoverable. This is deliberately a separate insert-only path: deriving
|
|
3388
|
+
* "new" from a read before the normal insert would race a concurrent sourcing
|
|
3389
|
+
* run.
|
|
3390
|
+
*/
|
|
3391
|
+
async function prepareNetNewRuntimeSheetDatasetRows(
|
|
3392
|
+
client: RuntimeQueryClient,
|
|
3393
|
+
session: RuntimePostgresSession,
|
|
3394
|
+
input: {
|
|
3395
|
+
chunks: RuntimeDatasetRowEntry[][];
|
|
3396
|
+
runId: string;
|
|
3397
|
+
attemptId: string;
|
|
3398
|
+
attemptOwnerRunId: string;
|
|
3399
|
+
attemptExpiresAt: string;
|
|
3400
|
+
attemptSeq: number;
|
|
3401
|
+
writeVersion: number;
|
|
3402
|
+
physicalInsertColumnsSql: string;
|
|
3403
|
+
physicalInsertValuesSql: string;
|
|
3404
|
+
},
|
|
3405
|
+
): Promise<{
|
|
3406
|
+
inserted: number;
|
|
3407
|
+
rowDispositions: RuntimeSheetPreparedRowDisposition[];
|
|
3408
|
+
}> {
|
|
3409
|
+
let inserted = 0;
|
|
3410
|
+
const rowDispositions: RuntimeSheetPreparedRowDisposition[] = [];
|
|
3411
|
+
for (const chunk of input.chunks) {
|
|
3412
|
+
const { rows } = await client.query<{
|
|
3413
|
+
inserted_count: number;
|
|
3414
|
+
row_dispositions: RuntimeSheetPreparedRowDisposition[];
|
|
3415
|
+
}>(
|
|
3416
|
+
`WITH input_rows AS (
|
|
3417
|
+
SELECT DISTINCT ON (key_values._key)
|
|
3418
|
+
key_values._key, payload_values.payload, index_values._input_index
|
|
3419
|
+
FROM unnest($1::text[]) WITH ORDINALITY AS key_values(_key, ord)
|
|
3420
|
+
JOIN unnest($2::jsonb[]) WITH ORDINALITY AS payload_values(payload, ord)
|
|
3421
|
+
ON payload_values.ord = key_values.ord
|
|
3422
|
+
JOIN unnest($3::bigint[]) WITH ORDINALITY AS index_values(_input_index, ord)
|
|
3423
|
+
ON index_values.ord = key_values.ord
|
|
3424
|
+
ORDER BY key_values._key, key_values.ord
|
|
3425
|
+
),
|
|
3426
|
+
reclaimed_rows AS (
|
|
3427
|
+
UPDATE ${sheetTable(session)} AS target
|
|
3428
|
+
SET _status = 'pending',
|
|
3429
|
+
_run_id = $4::text,
|
|
3430
|
+
_input_index = input_rows._input_index,
|
|
3431
|
+
_attempt_id = $5::text,
|
|
3432
|
+
_attempt_owner_run_id = $6::text,
|
|
3433
|
+
_attempt_expires_at = $7::timestamptz,
|
|
3434
|
+
_attempt_seq = $8::integer,
|
|
3435
|
+
_write_version = $9::bigint,
|
|
3436
|
+
_writer_run_id = $4::text,
|
|
3437
|
+
_error = NULL,
|
|
3438
|
+
_updated_at = now(),
|
|
3439
|
+
_version = ${nextRuntimeSheetVersionExpression(session)}
|
|
3440
|
+
FROM input_rows
|
|
3441
|
+
WHERE target._key = input_rows._key
|
|
3442
|
+
AND (
|
|
3443
|
+
target._status = 'failed'
|
|
3444
|
+
OR (
|
|
3445
|
+
target._status IN ('pending', 'running')
|
|
3446
|
+
AND coalesce(target._attempt_owner_run_id, target._run_id) = $6::text
|
|
3447
|
+
)
|
|
3448
|
+
)
|
|
3449
|
+
RETURNING target._key
|
|
3450
|
+
),
|
|
3451
|
+
inserted_rows AS (
|
|
3452
|
+
INSERT INTO ${sheetTable(session)}
|
|
3453
|
+
(_key, _status, _run_id, _input_index, _attempt_id, _attempt_owner_run_id,
|
|
3454
|
+
_attempt_expires_at, _attempt_seq, _write_version, _writer_run_id${input.physicalInsertColumnsSql})
|
|
3455
|
+
SELECT _key, 'pending', $4::text, _input_index, $5::text, $6::text,
|
|
3456
|
+
$7::timestamptz, $8::integer, $9::bigint, $4::text${input.physicalInsertValuesSql}
|
|
3457
|
+
FROM input_rows
|
|
3458
|
+
ON CONFLICT (_key) DO NOTHING
|
|
3459
|
+
RETURNING _key
|
|
3460
|
+
),
|
|
3461
|
+
pending_rows AS (
|
|
3462
|
+
SELECT _key FROM inserted_rows
|
|
3463
|
+
UNION
|
|
3464
|
+
SELECT _key FROM reclaimed_rows
|
|
3465
|
+
)
|
|
3466
|
+
SELECT
|
|
3467
|
+
(SELECT count(*)::int FROM inserted_rows) AS inserted_count,
|
|
3468
|
+
coalesce(
|
|
3469
|
+
(SELECT jsonb_agg(jsonb_build_object('key', _key, 'disposition', 'pending')) FROM pending_rows),
|
|
3470
|
+
'[]'::jsonb
|
|
3471
|
+
) AS row_dispositions`,
|
|
3472
|
+
[
|
|
3473
|
+
chunk.map((entry) => entry.key),
|
|
3474
|
+
chunk.map((entry) => stringifyPostgresJson(entry.row)),
|
|
3475
|
+
chunk.map((entry) => entry.inputIndex),
|
|
3476
|
+
input.runId,
|
|
3477
|
+
input.attemptId,
|
|
3478
|
+
input.attemptOwnerRunId,
|
|
3479
|
+
input.attemptExpiresAt,
|
|
3480
|
+
input.attemptSeq,
|
|
3481
|
+
input.writeVersion,
|
|
3482
|
+
],
|
|
3483
|
+
);
|
|
3484
|
+
inserted += Number(rows[0]?.inserted_count ?? 0);
|
|
3485
|
+
if (Array.isArray(rows[0]?.row_dispositions)) {
|
|
3486
|
+
rowDispositions.push(
|
|
3487
|
+
...rows[0].row_dispositions.filter(
|
|
3488
|
+
(row): row is RuntimeSheetPreparedRowDisposition =>
|
|
3489
|
+
row != null &&
|
|
3490
|
+
typeof row.key === 'string' &&
|
|
3491
|
+
row.disposition === 'pending',
|
|
3492
|
+
),
|
|
3493
|
+
);
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
return { inserted, rowDispositions };
|
|
3497
|
+
}
|
|
3498
|
+
|
|
3381
3499
|
async function buildRuntimeSheetDatasetStartResult(
|
|
3382
3500
|
client: RuntimeQueryClient,
|
|
3383
3501
|
session: RuntimePostgresSession,
|
|
@@ -5561,6 +5679,7 @@ export async function startRuntimeSheetDataset(
|
|
|
5561
5679
|
writeVersion?: number | null;
|
|
5562
5680
|
inputOffset?: number;
|
|
5563
5681
|
force?: boolean;
|
|
5682
|
+
mode?: RuntimeSheetDatasetMode;
|
|
5564
5683
|
},
|
|
5565
5684
|
): Promise<PrepareRuntimeSheetResult> {
|
|
5566
5685
|
if (context.dbSessionStrategy === 'gateway_only') {
|
|
@@ -5737,24 +5856,37 @@ export async function startRuntimeSheetDataset(
|
|
|
5737
5856
|
rows: rowEntries.length,
|
|
5738
5857
|
});
|
|
5739
5858
|
const prepareStartedAt = Date.now();
|
|
5740
|
-
const prepared =
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5859
|
+
const prepared =
|
|
5860
|
+
input.mode === 'net_new'
|
|
5861
|
+
? await prepareNetNewRuntimeSheetDatasetRows(client, session, {
|
|
5862
|
+
chunks,
|
|
5863
|
+
runId: input.runId,
|
|
5864
|
+
attemptId,
|
|
5865
|
+
attemptOwnerRunId,
|
|
5866
|
+
attemptExpiresAt,
|
|
5867
|
+
attemptSeq,
|
|
5868
|
+
writeVersion,
|
|
5869
|
+
physicalInsertColumnsSql,
|
|
5870
|
+
physicalInsertValuesSql,
|
|
5871
|
+
})
|
|
5872
|
+
: await prepareRuntimeSheetDatasetRows(client, session, {
|
|
5873
|
+
chunks,
|
|
5874
|
+
runId: input.runId,
|
|
5875
|
+
normalizedPlayName,
|
|
5876
|
+
normalizedTableNamespace,
|
|
5877
|
+
attemptId,
|
|
5878
|
+
attemptOwnerRunId,
|
|
5879
|
+
attemptExpiresAt,
|
|
5880
|
+
attemptSeq,
|
|
5881
|
+
writeVersion,
|
|
5882
|
+
physicalInsertColumnsSql,
|
|
5883
|
+
physicalInsertValuesSql,
|
|
5884
|
+
physicalRefreshSetSql:
|
|
5885
|
+
input.force === true ? physicalRefreshSetSql : '',
|
|
5886
|
+
physicalUpsertSetSql,
|
|
5887
|
+
outputPhysicalColumns,
|
|
5888
|
+
force: input.force === true,
|
|
5889
|
+
});
|
|
5758
5890
|
timings.push({
|
|
5759
5891
|
phase: 'prepare_rows_sql',
|
|
5760
5892
|
ms: Date.now() - prepareStartedAt,
|
package/dist/cli/index.js
CHANGED
|
@@ -625,10 +625,10 @@ var SDK_RELEASE = {
|
|
|
625
625
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
626
626
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
627
627
|
// automatically without blocking their current command.
|
|
628
|
-
version: "0.1.
|
|
628
|
+
version: "0.1.227",
|
|
629
629
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
630
630
|
supportPolicy: {
|
|
631
|
-
latest: "0.1.
|
|
631
|
+
latest: "0.1.227",
|
|
632
632
|
minimumSupported: "0.1.53",
|
|
633
633
|
deprecatedBelow: "0.1.219",
|
|
634
634
|
commandMinimumSupported: [
|
package/dist/cli/index.mjs
CHANGED
|
@@ -610,10 +610,10 @@ var SDK_RELEASE = {
|
|
|
610
610
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
611
611
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
612
612
|
// automatically without blocking their current command.
|
|
613
|
-
version: "0.1.
|
|
613
|
+
version: "0.1.227",
|
|
614
614
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
615
615
|
supportPolicy: {
|
|
616
|
-
latest: "0.1.
|
|
616
|
+
latest: "0.1.227",
|
|
617
617
|
minimumSupported: "0.1.53",
|
|
618
618
|
deprecatedBelow: "0.1.219",
|
|
619
619
|
commandMinimumSupported: [
|
package/dist/index.d.mts
CHANGED
|
@@ -3426,6 +3426,11 @@ type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
|
|
|
3426
3426
|
*/
|
|
3427
3427
|
run(options?: {
|
|
3428
3428
|
description?: string;
|
|
3429
|
+
/**
|
|
3430
|
+
* `upsert` (default) returns every input row, reusing persisted work for
|
|
3431
|
+
* existing keys. `net_new` atomically inserts and returns only unseen keys.
|
|
3432
|
+
*/
|
|
3433
|
+
mode?: 'upsert' | 'net_new';
|
|
3429
3434
|
key?: (keyof InputRow & string) | readonly (keyof InputRow & string)[] | ((row: InputRow, index: number) => string | number | readonly unknown[]);
|
|
3430
3435
|
}): Promise<PlayDataset<OutputRow>>;
|
|
3431
3436
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -3426,6 +3426,11 @@ type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
|
|
|
3426
3426
|
*/
|
|
3427
3427
|
run(options?: {
|
|
3428
3428
|
description?: string;
|
|
3429
|
+
/**
|
|
3430
|
+
* `upsert` (default) returns every input row, reusing persisted work for
|
|
3431
|
+
* existing keys. `net_new` atomically inserts and returns only unseen keys.
|
|
3432
|
+
*/
|
|
3433
|
+
mode?: 'upsert' | 'net_new';
|
|
3429
3434
|
key?: (keyof InputRow & string) | readonly (keyof InputRow & string)[] | ((row: InputRow, index: number) => string | number | readonly unknown[]);
|
|
3430
3435
|
}): Promise<PlayDataset<OutputRow>>;
|
|
3431
3436
|
};
|
package/dist/index.js
CHANGED
|
@@ -424,10 +424,10 @@ var SDK_RELEASE = {
|
|
|
424
424
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
425
425
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
426
426
|
// automatically without blocking their current command.
|
|
427
|
-
version: "0.1.
|
|
427
|
+
version: "0.1.227",
|
|
428
428
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
429
429
|
supportPolicy: {
|
|
430
|
-
latest: "0.1.
|
|
430
|
+
latest: "0.1.227",
|
|
431
431
|
minimumSupported: "0.1.53",
|
|
432
432
|
deprecatedBelow: "0.1.219",
|
|
433
433
|
commandMinimumSupported: [
|
package/dist/index.mjs
CHANGED
|
@@ -354,10 +354,10 @@ var SDK_RELEASE = {
|
|
|
354
354
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
355
355
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
356
356
|
// automatically without blocking their current command.
|
|
357
|
-
version: "0.1.
|
|
357
|
+
version: "0.1.227",
|
|
358
358
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
359
359
|
supportPolicy: {
|
|
360
|
-
latest: "0.1.
|
|
360
|
+
latest: "0.1.227",
|
|
361
361
|
minimumSupported: "0.1.53",
|
|
362
362
|
deprecatedBelow: "0.1.219",
|
|
363
363
|
commandMinimumSupported: [
|