deepline 0.1.150 → 0.1.152
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 +170 -168
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/csv-rows.ts +2 -19
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/row-isolation.ts +5 -53
- package/dist/bundling-sources/sdk/src/config.ts +2 -2
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +101 -162
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +54 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-row-outcome.ts +167 -0
- package/dist/bundling-sources/shared_libs/play-runtime/pacing.ts +79 -0
- package/dist/bundling-sources/shared_libs/play-runtime/row-isolation.ts +39 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +36 -86
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-transition.ts +90 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-session.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +142 -11
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +3 -2
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +20 -23
- package/dist/cli/index.js +35 -3
- package/dist/cli/index.mjs +35 -3
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/dist/plays/bundle-play-file.mjs +22 -19
- package/package.json +1 -1
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { MapRowOutcome, MapRowOutcomeStatus } from './durability-store';
|
|
2
|
+
|
|
3
|
+
export const MAP_ROW_OUTCOME_RUNTIME_FIELDS = {
|
|
4
|
+
rowKey: '__deeplineRowKey',
|
|
5
|
+
cellMetaPatch: '__deeplineCellMetaPatch',
|
|
6
|
+
rowStatus: '__deeplineRowStatus',
|
|
7
|
+
rowError: '__deeplineRowError',
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
const MAP_ROW_OUTCOME_RUNTIME_FIELD_SET = new Set<string>(
|
|
11
|
+
Object.values(MAP_ROW_OUTCOME_RUNTIME_FIELDS),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export type MapRowOutcomeRuntimeRow = Record<string, unknown>;
|
|
15
|
+
|
|
16
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
17
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function resolveMapRowOutcomeKey(
|
|
21
|
+
row: MapRowOutcomeRuntimeRow,
|
|
22
|
+
): string | null {
|
|
23
|
+
const key = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowKey];
|
|
24
|
+
return typeof key === 'string' && key.length > 0 ? key : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function mapRowOutcomeRuntimeFields(input: {
|
|
28
|
+
key: string;
|
|
29
|
+
cellMetaPatch?: Record<string, unknown> | null;
|
|
30
|
+
status?: MapRowOutcomeStatus | null;
|
|
31
|
+
error?: string | null;
|
|
32
|
+
}): Record<string, unknown> {
|
|
33
|
+
return {
|
|
34
|
+
[MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowKey]: input.key,
|
|
35
|
+
...(input.cellMetaPatch
|
|
36
|
+
? { [MAP_ROW_OUTCOME_RUNTIME_FIELDS.cellMetaPatch]: input.cellMetaPatch }
|
|
37
|
+
: {}),
|
|
38
|
+
...(input.status
|
|
39
|
+
? { [MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowStatus]: input.status }
|
|
40
|
+
: {}),
|
|
41
|
+
...(input.error !== undefined
|
|
42
|
+
? { [MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowError]: input.error }
|
|
43
|
+
: {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function completedMapRowOutcome(input: {
|
|
48
|
+
key: string;
|
|
49
|
+
inputIndex?: number | null;
|
|
50
|
+
data: Record<string, unknown>;
|
|
51
|
+
cellMetaPatch?: Record<string, unknown> | null;
|
|
52
|
+
}): MapRowOutcome {
|
|
53
|
+
return {
|
|
54
|
+
key: input.key,
|
|
55
|
+
...(input.inputIndex !== undefined ? { inputIndex: input.inputIndex } : {}),
|
|
56
|
+
data: input.data,
|
|
57
|
+
...(input.cellMetaPatch ? { cellMetaPatch: input.cellMetaPatch } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function failedMapRowOutcome(input: {
|
|
62
|
+
key: string;
|
|
63
|
+
inputIndex?: number | null;
|
|
64
|
+
data: Record<string, unknown>;
|
|
65
|
+
cellMetaPatch?: Record<string, unknown> | null;
|
|
66
|
+
error?: string | null;
|
|
67
|
+
}): MapRowOutcome {
|
|
68
|
+
return {
|
|
69
|
+
...completedMapRowOutcome(input),
|
|
70
|
+
status: 'failed',
|
|
71
|
+
error: input.error ?? null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function mapRowOutcomeRuntimeRow(
|
|
76
|
+
outcome: MapRowOutcome,
|
|
77
|
+
): Record<string, unknown> {
|
|
78
|
+
return {
|
|
79
|
+
...outcome.data,
|
|
80
|
+
...mapRowOutcomeRuntimeFields({
|
|
81
|
+
key: outcome.key,
|
|
82
|
+
cellMetaPatch: outcome.cellMetaPatch,
|
|
83
|
+
status: outcome.status,
|
|
84
|
+
error: outcome.error,
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function copyMapRowOutcomeRuntimeFields<
|
|
90
|
+
TTarget extends MapRowOutcomeRuntimeRow,
|
|
91
|
+
>(target: TTarget, row: MapRowOutcomeRuntimeRow): TTarget {
|
|
92
|
+
const writableTarget = target as MapRowOutcomeRuntimeRow;
|
|
93
|
+
for (const runtimeField of MAP_ROW_OUTCOME_RUNTIME_FIELD_SET) {
|
|
94
|
+
if (runtimeField in row) {
|
|
95
|
+
writableTarget[runtimeField] = row[runtimeField];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return target;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function stripMapRowOutcomeRuntimeFields<
|
|
102
|
+
TRow extends MapRowOutcomeRuntimeRow,
|
|
103
|
+
>(row: TRow): TRow {
|
|
104
|
+
const data: MapRowOutcomeRuntimeRow = {};
|
|
105
|
+
for (const fieldName of Reflect.ownKeys(row)) {
|
|
106
|
+
if (
|
|
107
|
+
typeof fieldName === 'string' &&
|
|
108
|
+
MAP_ROW_OUTCOME_RUNTIME_FIELD_SET.has(fieldName)
|
|
109
|
+
) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const descriptor = Object.getOwnPropertyDescriptor(row, fieldName);
|
|
113
|
+
if (!descriptor) continue;
|
|
114
|
+
Object.defineProperty(data, fieldName, descriptor);
|
|
115
|
+
}
|
|
116
|
+
return data as TRow;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function mapRowOutcomeFromRuntimeRow(
|
|
120
|
+
row: MapRowOutcomeRuntimeRow,
|
|
121
|
+
): MapRowOutcome | null {
|
|
122
|
+
if (typeof row.key === 'string' && isRecord(row.data)) {
|
|
123
|
+
return {
|
|
124
|
+
key: row.key,
|
|
125
|
+
data: row.data,
|
|
126
|
+
...(isRecord(row.cellMetaPatch)
|
|
127
|
+
? { cellMetaPatch: row.cellMetaPatch }
|
|
128
|
+
: {}),
|
|
129
|
+
...(row.status === 'failed'
|
|
130
|
+
? {
|
|
131
|
+
status: 'failed',
|
|
132
|
+
error: typeof row.error === 'string' ? row.error : null,
|
|
133
|
+
}
|
|
134
|
+
: {}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const key = resolveMapRowOutcomeKey(row);
|
|
139
|
+
if (!key) return null;
|
|
140
|
+
const cellMetaPatch = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.cellMetaPatch];
|
|
141
|
+
const rowStatus = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowStatus];
|
|
142
|
+
const rowError = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowError];
|
|
143
|
+
return {
|
|
144
|
+
key,
|
|
145
|
+
data: stripMapRowOutcomeRuntimeFields(row),
|
|
146
|
+
...(isRecord(cellMetaPatch) ? { cellMetaPatch } : {}),
|
|
147
|
+
...(rowStatus === 'failed'
|
|
148
|
+
? {
|
|
149
|
+
status: 'failed',
|
|
150
|
+
error: typeof rowError === 'string' ? rowError : null,
|
|
151
|
+
}
|
|
152
|
+
: {}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function requireMapRowOutcomeFromRuntimeRow(
|
|
157
|
+
row: MapRowOutcomeRuntimeRow,
|
|
158
|
+
input: { tableNamespace: string },
|
|
159
|
+
): MapRowOutcome {
|
|
160
|
+
const outcome = mapRowOutcomeFromRuntimeRow(row);
|
|
161
|
+
if (!outcome) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`persistCompletedMapRows received a row without a key for tableNamespace=${input.tableNamespace}.`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return outcome;
|
|
167
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { resolveBuiltinPacing } from './builtin-pacing';
|
|
2
|
+
import type { PacingRule, PlayQueueHint } from './governor/rate-state-backend';
|
|
3
|
+
|
|
4
|
+
export type ResolvedPacingPolicy = {
|
|
5
|
+
provider: string;
|
|
6
|
+
rules: PacingRule[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type PacingPolicyQueueHint = Pick<
|
|
10
|
+
PlayQueueHint,
|
|
11
|
+
'provider' | 'ruleId' | 'requestsPerWindow' | 'windowMs' | 'maxConcurrency'
|
|
12
|
+
>;
|
|
13
|
+
|
|
14
|
+
function isFinitePositiveNumber(value: unknown): value is number {
|
|
15
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function pacingPolicyFromQueueHints(
|
|
19
|
+
hints: readonly PacingPolicyQueueHint[],
|
|
20
|
+
): ResolvedPacingPolicy | null {
|
|
21
|
+
if (hints.length === 0) return null;
|
|
22
|
+
const provider = hints[0]?.provider?.trim();
|
|
23
|
+
if (!provider) return null;
|
|
24
|
+
const rules = hints.flatMap((hint) => {
|
|
25
|
+
if (
|
|
26
|
+
typeof hint.ruleId !== 'string' ||
|
|
27
|
+
!hint.ruleId.trim() ||
|
|
28
|
+
!isFinitePositiveNumber(hint.requestsPerWindow) ||
|
|
29
|
+
!isFinitePositiveNumber(hint.windowMs)
|
|
30
|
+
) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
ruleId: hint.ruleId,
|
|
36
|
+
requestsPerWindow: hint.requestsPerWindow,
|
|
37
|
+
windowMs: hint.windowMs,
|
|
38
|
+
maxConcurrency:
|
|
39
|
+
typeof hint.maxConcurrency === 'number' &&
|
|
40
|
+
Number.isFinite(hint.maxConcurrency) &&
|
|
41
|
+
hint.maxConcurrency > 0
|
|
42
|
+
? hint.maxConcurrency
|
|
43
|
+
: null,
|
|
44
|
+
} satisfies PacingRule,
|
|
45
|
+
];
|
|
46
|
+
});
|
|
47
|
+
return rules.length > 0 ? { provider, rules } : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function pacingPolicyFromUnknownQueueHints(
|
|
51
|
+
value: unknown,
|
|
52
|
+
): ResolvedPacingPolicy | null {
|
|
53
|
+
if (!Array.isArray(value)) return null;
|
|
54
|
+
return pacingPolicyFromQueueHints(
|
|
55
|
+
value
|
|
56
|
+
.filter((hint): hint is Record<string, unknown> =>
|
|
57
|
+
Boolean(hint && typeof hint === 'object' && !Array.isArray(hint)),
|
|
58
|
+
)
|
|
59
|
+
.map((hint) => ({
|
|
60
|
+
provider: typeof hint.provider === 'string' ? hint.provider : '',
|
|
61
|
+
ruleId: typeof hint.ruleId === 'string' ? hint.ruleId : '',
|
|
62
|
+
requestsPerWindow:
|
|
63
|
+
typeof hint.requestsPerWindow === 'number'
|
|
64
|
+
? hint.requestsPerWindow
|
|
65
|
+
: Number.NaN,
|
|
66
|
+
windowMs:
|
|
67
|
+
typeof hint.windowMs === 'number' ? hint.windowMs : Number.NaN,
|
|
68
|
+
maxConcurrency:
|
|
69
|
+
typeof hint.maxConcurrency === 'number' ? hint.maxConcurrency : null,
|
|
70
|
+
})),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function pacingPolicyForTool(
|
|
75
|
+
toolId: string,
|
|
76
|
+
hints: readonly PlayQueueHint[],
|
|
77
|
+
): ResolvedPacingPolicy | null {
|
|
78
|
+
return resolveBuiltinPacing(toolId) ?? pacingPolicyFromQueueHints(hints);
|
|
79
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isHardBillingToolHttpError } from './tool-http-errors';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thrown by runner Adapters when a Play Run is externally cancelled. Row
|
|
5
|
+
* isolation must let this escape so in-flight user code stops cooperatively.
|
|
6
|
+
*/
|
|
7
|
+
export class WorkflowAbortError extends Error {
|
|
8
|
+
override readonly name = 'WorkflowAbort';
|
|
9
|
+
constructor(message = 'Play run cancelled.') {
|
|
10
|
+
super(message);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isAbortLikeError(error: unknown): boolean {
|
|
15
|
+
if (!error) return false;
|
|
16
|
+
if (error instanceof WorkflowAbortError) return true;
|
|
17
|
+
if (error instanceof Error) {
|
|
18
|
+
if (error.name === 'WorkflowAbort' || error.name === 'AbortError') {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return /\b(cancell?ed|aborted|terminate[d]?)\b/i.test(error.message);
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Errors that must stay run-fatal even under default map row failure isolation.
|
|
28
|
+
*
|
|
29
|
+
* Provider/tool HTTP failures, including exhausted 429/5xx retries, are row
|
|
30
|
+
* outcomes after the tool-call Adapter spends its local retry budget. Abort,
|
|
31
|
+
* Governor budget exhaustion, and hard billing failures escape row isolation.
|
|
32
|
+
*/
|
|
33
|
+
export function isRowIsolationExemptError(error: unknown): boolean {
|
|
34
|
+
if (isAbortLikeError(error)) return true;
|
|
35
|
+
if (error instanceof Error && error.name === 'GovernorBudgetError') {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return isHardBillingToolHttpError(error);
|
|
39
|
+
}
|
|
@@ -60,6 +60,15 @@ import {
|
|
|
60
60
|
import { stringifyPostgresJson } from './postgres-json';
|
|
61
61
|
import { RECEIPT_STATUS_CODE, receiptStatusFromCode } from './receipt-status';
|
|
62
62
|
import type { MapRowOutcome } from './durability-store';
|
|
63
|
+
import {
|
|
64
|
+
prepareRuntimeSheetRowTransitions,
|
|
65
|
+
type RuntimePreparedCompletedRow,
|
|
66
|
+
type RuntimePreparedFailedRow,
|
|
67
|
+
} from './runtime-sheet-row-transition';
|
|
68
|
+
import {
|
|
69
|
+
mapRowOutcomeRuntimeFields,
|
|
70
|
+
resolveMapRowOutcomeKey,
|
|
71
|
+
} from './map-row-outcome';
|
|
63
72
|
import {
|
|
64
73
|
DEEPLINE_CELL_META_FIELD,
|
|
65
74
|
cellPolicyFields,
|
|
@@ -92,18 +101,6 @@ type RuntimeDatasetRowEntry = {
|
|
|
92
101
|
row: Record<string, unknown>;
|
|
93
102
|
inputIndex: number;
|
|
94
103
|
};
|
|
95
|
-
type RuntimePreparedCompletedRow = {
|
|
96
|
-
key: string;
|
|
97
|
-
input_index: number | null;
|
|
98
|
-
data_patch: Record<string, unknown>;
|
|
99
|
-
cell_meta_patch: Record<string, unknown>;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
type RuntimePreparedFailedRow = RuntimePreparedCompletedRow & {
|
|
103
|
-
/** Row-level error persisted to `_error`; never empty. */
|
|
104
|
-
error: string;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
104
|
const dbSessionCache = new Map<string, DbSessionCacheEntry>();
|
|
108
105
|
const dbSessionInFlight = new Map<string, Promise<CreateDbSessionResponse>>();
|
|
109
106
|
const postgresPools = new Map<string, RuntimePool>();
|
|
@@ -1680,35 +1677,6 @@ function cachedRuntimeCellMetaPatch(runId: string): Record<string, unknown> {
|
|
|
1680
1677
|
};
|
|
1681
1678
|
}
|
|
1682
1679
|
|
|
1683
|
-
function completedRuntimeCellMetaPatch(input: {
|
|
1684
|
-
runId: string;
|
|
1685
|
-
outputFields: readonly string[];
|
|
1686
|
-
rowPatch?: Record<string, unknown>;
|
|
1687
|
-
}): Record<string, unknown> {
|
|
1688
|
-
const patch: Record<string, unknown> = {};
|
|
1689
|
-
const completedAt = Date.now();
|
|
1690
|
-
for (const field of input.outputFields) {
|
|
1691
|
-
const existing =
|
|
1692
|
-
input.rowPatch?.[field] &&
|
|
1693
|
-
typeof input.rowPatch[field] === 'object' &&
|
|
1694
|
-
!Array.isArray(input.rowPatch[field])
|
|
1695
|
-
? (input.rowPatch[field] as Record<string, unknown>)
|
|
1696
|
-
: {};
|
|
1697
|
-
patch[field] = {
|
|
1698
|
-
status: 'completed',
|
|
1699
|
-
runId: input.runId,
|
|
1700
|
-
completedAt,
|
|
1701
|
-
...existing,
|
|
1702
|
-
};
|
|
1703
|
-
}
|
|
1704
|
-
for (const [field, meta] of Object.entries(input.rowPatch ?? {})) {
|
|
1705
|
-
if (!Object.hasOwn(patch, field)) {
|
|
1706
|
-
patch[field] = meta;
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
return patch;
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
1680
|
function cachedRuntimeCellMetaUpdateSql(
|
|
1713
1681
|
tableAlias: string,
|
|
1714
1682
|
outputFields: readonly string[],
|
|
@@ -1981,11 +1949,26 @@ async function readRuntimeRows(
|
|
|
1981
1949
|
limit: number;
|
|
1982
1950
|
offset: number;
|
|
1983
1951
|
runId?: string | null;
|
|
1952
|
+
rowMode?: 'output' | 'all';
|
|
1984
1953
|
sheetContract?: PlaySheetContract | null;
|
|
1985
1954
|
},
|
|
1986
1955
|
): Promise<RuntimeApiRowRecord[]> {
|
|
1987
1956
|
return await withRuntimePostgres(session, async (client) => {
|
|
1988
1957
|
if (input.runId) {
|
|
1958
|
+
if (input.rowMode === 'all') {
|
|
1959
|
+
const { rows } = await client.query(
|
|
1960
|
+
`SELECT *
|
|
1961
|
+
FROM ${sheetTable(session)}
|
|
1962
|
+
WHERE _run_id = $1::text
|
|
1963
|
+
AND _status IN ('enriched', 'failed')
|
|
1964
|
+
ORDER BY _input_index ASC NULLS LAST, _created_at ASC, _key ASC
|
|
1965
|
+
LIMIT $2 OFFSET $3`,
|
|
1966
|
+
[input.runId, input.limit, input.offset],
|
|
1967
|
+
);
|
|
1968
|
+
return rows.map((raw) =>
|
|
1969
|
+
mapRuntimePostgresRow({ raw, sheetContract: input.sheetContract }),
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1989
1972
|
const { rows } = await client.query(
|
|
1990
1973
|
`WITH scoped AS (
|
|
1991
1974
|
SELECT *,
|
|
@@ -2554,7 +2537,7 @@ async function buildRuntimeSheetDatasetStartResult(
|
|
|
2554
2537
|
),
|
|
2555
2538
|
sheetContract: input.sheetContract,
|
|
2556
2539
|
}),
|
|
2557
|
-
|
|
2540
|
+
...mapRowOutcomeRuntimeFields({ key: entry.key }),
|
|
2558
2541
|
})),
|
|
2559
2542
|
tableNamespace: input.tableNamespace,
|
|
2560
2543
|
};
|
|
@@ -2671,7 +2654,7 @@ async function buildRuntimeSheetDatasetStartResult(
|
|
|
2671
2654
|
),
|
|
2672
2655
|
sheetContract: input.sheetContract,
|
|
2673
2656
|
}),
|
|
2674
|
-
|
|
2657
|
+
...mapRowOutcomeRuntimeFields({ key: entry.key }),
|
|
2675
2658
|
}));
|
|
2676
2659
|
return {
|
|
2677
2660
|
inserted: input.inserted,
|
|
@@ -2683,7 +2666,7 @@ async function buildRuntimeSheetDatasetStartResult(
|
|
|
2683
2666
|
completedData: existingPendingRowsByKey.get(entry.key) ?? {},
|
|
2684
2667
|
sheetContract: input.sheetContract,
|
|
2685
2668
|
}),
|
|
2686
|
-
|
|
2669
|
+
...mapRowOutcomeRuntimeFields({ key: entry.key }),
|
|
2687
2670
|
})),
|
|
2688
2671
|
completedRows,
|
|
2689
2672
|
tableNamespace: input.tableNamespace,
|
|
@@ -3061,9 +3044,8 @@ export async function startRuntimeSheetDataset(
|
|
|
3061
3044
|
// non-enumerable alias fields on the JSON payload boundary.
|
|
3062
3045
|
const cleanedRow = toSerializableCsvAliasedRow(row);
|
|
3063
3046
|
const key =
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
: derivePlayRowIdentity(cleanedRow, input.tableNamespace);
|
|
3047
|
+
resolveMapRowOutcomeKey(row) ??
|
|
3048
|
+
derivePlayRowIdentity(cleanedRow, input.tableNamespace);
|
|
3067
3049
|
if (key && !uniqueRows.has(key)) {
|
|
3068
3050
|
uniqueRows.set(key, cleanedRow);
|
|
3069
3051
|
}
|
|
@@ -3517,14 +3499,6 @@ async function failRuntimeMapRowChunks(
|
|
|
3517
3499
|
return { updated };
|
|
3518
3500
|
}
|
|
3519
3501
|
|
|
3520
|
-
function normalizeRuntimeMapInputIndex(value: unknown): number | null {
|
|
3521
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
3522
|
-
return null;
|
|
3523
|
-
}
|
|
3524
|
-
const normalized = Math.floor(value);
|
|
3525
|
-
return normalized >= 0 ? normalized : null;
|
|
3526
|
-
}
|
|
3527
|
-
|
|
3528
3502
|
/**
|
|
3529
3503
|
* Mark map rows terminal in the per-run scoped Postgres sheet table by key.
|
|
3530
3504
|
* Mirrors server-side `store.completeSheetRows` semantics: UPDATE-by-key with
|
|
@@ -3603,37 +3577,11 @@ export async function completeRuntimeMapRows(
|
|
|
3603
3577
|
.join(',\n ')}`
|
|
3604
3578
|
: '';
|
|
3605
3579
|
|
|
3606
|
-
const completedRows
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
// sibling cells + the failed cell). Defaulting every output field to
|
|
3612
|
-
// 'completed' here would lie about the failed cell and break re-run
|
|
3613
|
-
// recompute.
|
|
3614
|
-
failedRows.push({
|
|
3615
|
-
key,
|
|
3616
|
-
input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
|
|
3617
|
-
data_patch: row.data,
|
|
3618
|
-
cell_meta_patch: row.cellMetaPatch ?? {},
|
|
3619
|
-
error:
|
|
3620
|
-
typeof row.error === 'string' && row.error.trim()
|
|
3621
|
-
? row.error
|
|
3622
|
-
: 'Row execution failed.',
|
|
3623
|
-
});
|
|
3624
|
-
continue;
|
|
3625
|
-
}
|
|
3626
|
-
completedRows.push({
|
|
3627
|
-
key,
|
|
3628
|
-
input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
|
|
3629
|
-
data_patch: row.data,
|
|
3630
|
-
cell_meta_patch: completedRuntimeCellMetaPatch({
|
|
3631
|
-
runId: input.runId,
|
|
3632
|
-
outputFields: input.outputFields ?? [],
|
|
3633
|
-
rowPatch: row.cellMetaPatch,
|
|
3634
|
-
}),
|
|
3635
|
-
});
|
|
3636
|
-
}
|
|
3580
|
+
const { completedRows, failedRows } = prepareRuntimeSheetRowTransitions({
|
|
3581
|
+
rows: uniqueRows.values(),
|
|
3582
|
+
runId: input.runId,
|
|
3583
|
+
outputFields: input.outputFields ?? [],
|
|
3584
|
+
});
|
|
3637
3585
|
const chunks = chunkValues(completedRows, DIRECT_POSTGRES_BATCH_SIZE);
|
|
3638
3586
|
const failedChunks = chunkValues(failedRows, DIRECT_POSTGRES_BATCH_SIZE);
|
|
3639
3587
|
const needsTransaction = chunks.length + failedChunks.length > 1;
|
|
@@ -3685,6 +3633,7 @@ export async function readRuntimeSheetDatasetRows(
|
|
|
3685
3633
|
input: {
|
|
3686
3634
|
tableNamespace: string;
|
|
3687
3635
|
runId?: string | null;
|
|
3636
|
+
rowMode?: 'output' | 'all';
|
|
3688
3637
|
limit: number;
|
|
3689
3638
|
offset: number;
|
|
3690
3639
|
},
|
|
@@ -3713,6 +3662,7 @@ export async function readRuntimeSheetDatasetRows(
|
|
|
3713
3662
|
limit,
|
|
3714
3663
|
offset,
|
|
3715
3664
|
runId: input.runId ?? null,
|
|
3665
|
+
rowMode: input.rowMode,
|
|
3716
3666
|
});
|
|
3717
3667
|
return {
|
|
3718
3668
|
rows: rows.map((row) => row.data),
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { MapRowOutcome } from './durability-store';
|
|
2
|
+
|
|
3
|
+
export type RuntimePreparedCompletedRow = {
|
|
4
|
+
key: string;
|
|
5
|
+
input_index: number | null;
|
|
6
|
+
data_patch: Record<string, unknown>;
|
|
7
|
+
cell_meta_patch: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type RuntimePreparedFailedRow = RuntimePreparedCompletedRow & {
|
|
11
|
+
/** Row-level error persisted to `_error`; never empty. */
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function normalizeRuntimeMapInputIndex(value: unknown): number | null {
|
|
16
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const normalized = Math.floor(value);
|
|
20
|
+
return normalized >= 0 ? normalized : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function completedRuntimeCellMetaPatch(input: {
|
|
24
|
+
runId: string;
|
|
25
|
+
outputFields: readonly string[];
|
|
26
|
+
rowPatch?: Record<string, unknown>;
|
|
27
|
+
nowMs?: number;
|
|
28
|
+
}): Record<string, unknown> {
|
|
29
|
+
const patch: Record<string, unknown> = {};
|
|
30
|
+
const completedAt = input.nowMs ?? Date.now();
|
|
31
|
+
for (const field of input.outputFields) {
|
|
32
|
+
const existing =
|
|
33
|
+
input.rowPatch?.[field] &&
|
|
34
|
+
typeof input.rowPatch[field] === 'object' &&
|
|
35
|
+
!Array.isArray(input.rowPatch[field])
|
|
36
|
+
? (input.rowPatch[field] as Record<string, unknown>)
|
|
37
|
+
: {};
|
|
38
|
+
patch[field] = {
|
|
39
|
+
status: 'completed',
|
|
40
|
+
runId: input.runId,
|
|
41
|
+
completedAt,
|
|
42
|
+
...existing,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
for (const [field, meta] of Object.entries(input.rowPatch ?? {})) {
|
|
46
|
+
if (!Object.hasOwn(patch, field)) {
|
|
47
|
+
patch[field] = meta;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return patch;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function prepareRuntimeSheetRowTransitions(input: {
|
|
54
|
+
rows: Iterable<MapRowOutcome>;
|
|
55
|
+
runId: string;
|
|
56
|
+
outputFields: readonly string[];
|
|
57
|
+
}): {
|
|
58
|
+
completedRows: RuntimePreparedCompletedRow[];
|
|
59
|
+
failedRows: RuntimePreparedFailedRow[];
|
|
60
|
+
} {
|
|
61
|
+
const completedRows: RuntimePreparedCompletedRow[] = [];
|
|
62
|
+
const failedRows: RuntimePreparedFailedRow[] = [];
|
|
63
|
+
for (const row of input.rows) {
|
|
64
|
+
if (!row.key) continue;
|
|
65
|
+
if (row.status === 'failed') {
|
|
66
|
+
failedRows.push({
|
|
67
|
+
key: row.key,
|
|
68
|
+
input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
|
|
69
|
+
data_patch: row.data,
|
|
70
|
+
cell_meta_patch: row.cellMetaPatch ?? {},
|
|
71
|
+
error:
|
|
72
|
+
typeof row.error === 'string' && row.error.trim()
|
|
73
|
+
? row.error
|
|
74
|
+
: 'Row execution failed.',
|
|
75
|
+
});
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
completedRows.push({
|
|
79
|
+
key: row.key,
|
|
80
|
+
input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
|
|
81
|
+
data_patch: row.data,
|
|
82
|
+
cell_meta_patch: completedRuntimeCellMetaPatch({
|
|
83
|
+
runId: input.runId,
|
|
84
|
+
outputFields: input.outputFields,
|
|
85
|
+
rowPatch: row.cellMetaPatch,
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return { completedRows, failedRows };
|
|
90
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type RuntimeSheetSessionScope<TPreloadedSession = unknown> = {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
executorToken: string;
|
|
4
|
+
orgId: string;
|
|
5
|
+
preloadedDbSessions?: TPreloadedSession[] | null;
|
|
6
|
+
playName: string;
|
|
7
|
+
runId: string;
|
|
8
|
+
userEmail?: string | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function requireRuntimeSheetSessionField(
|
|
12
|
+
fieldName: string,
|
|
13
|
+
value: string,
|
|
14
|
+
): string {
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
if (!trimmed) {
|
|
17
|
+
throw new Error(`Runtime Sheet Session requires ${fieldName}.`);
|
|
18
|
+
}
|
|
19
|
+
return trimmed;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function runtimeSheetSessionScope<TPreloadedSession = unknown>(input: {
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
executorToken: string;
|
|
25
|
+
orgId: string;
|
|
26
|
+
preloadedDbSessions?: TPreloadedSession[] | null;
|
|
27
|
+
playName: string;
|
|
28
|
+
runId: string;
|
|
29
|
+
userEmail?: string | null;
|
|
30
|
+
}): RuntimeSheetSessionScope<TPreloadedSession> {
|
|
31
|
+
return {
|
|
32
|
+
baseUrl: requireRuntimeSheetSessionField('baseUrl', input.baseUrl),
|
|
33
|
+
executorToken: requireRuntimeSheetSessionField(
|
|
34
|
+
'executorToken',
|
|
35
|
+
input.executorToken,
|
|
36
|
+
),
|
|
37
|
+
orgId: requireRuntimeSheetSessionField('orgId', input.orgId),
|
|
38
|
+
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
39
|
+
playName: requireRuntimeSheetSessionField('playName', input.playName),
|
|
40
|
+
runId: requireRuntimeSheetSessionField('runId', input.runId),
|
|
41
|
+
userEmail: input.userEmail ?? null,
|
|
42
|
+
};
|
|
43
|
+
}
|