deepline 0.1.163 → 0.1.165

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.
@@ -3559,6 +3559,18 @@ function resolveSheetContractFromReq(
3559
3559
  }
3560
3560
  }
3561
3561
 
3562
+ function staticPipelineFromReq(req: RunRequest): PlayStaticPipeline | null {
3563
+ const snapshot = req.contractSnapshot as
3564
+ | { staticPipeline?: unknown }
3565
+ | undefined
3566
+ | null;
3567
+ const pipeline = snapshot?.staticPipeline;
3568
+ if (!pipeline || typeof pipeline !== 'object' || Array.isArray(pipeline)) {
3569
+ return null;
3570
+ }
3571
+ return pipeline as PlayStaticPipeline;
3572
+ }
3573
+
3562
3574
  /**
3563
3575
  * Direct-Neon writes from the shared harness Worker. Resolves the org runtime
3564
3576
  * Postgres URL via `create_db_session` inside that long-lived Worker, then
@@ -4290,6 +4302,7 @@ function createMinimalWorkerCtx(
4290
4302
  mapName: name,
4291
4303
  rowCountHint,
4292
4304
  executionPlan: plan,
4305
+ staticPipeline: staticPipelineFromReq(req),
4293
4306
  });
4294
4307
  const outputFields = fieldEntries.map(([field]) => field);
4295
4308
  const updateMapProgress = (
@@ -1,83 +1,319 @@
1
1
  import {
2
2
  chooseMapChunkSize,
3
- EXECUTION_PLAN_DEFAULTS,
4
3
  type ExecutionPlan,
5
4
  } from '../../../../shared_libs/play-runtime/execution-plan';
5
+ import { getPlayRuntimeBatchStrategy } from '../../../../shared_libs/play-runtime/play-runtime-batching-registry';
6
6
  import { TOOL_CALLING_MAP_CHUNK_SIZE } from '../../../../shared_libs/play-runtime/map-chunk-limits';
7
+ import {
8
+ flattenStaticSubsteps,
9
+ getTopLevelPipelineSubsteps,
10
+ type PlayStaticColumnProducer,
11
+ type PlayStaticPipeline,
12
+ type PlayStaticSubstep,
13
+ } from '../../../../shared_libs/plays/static-pipeline';
7
14
 
8
15
  export const CACHE_ENABLED_SIMPLE_MAP_CHUNK_SIZE = 10_000;
9
16
  export { TOOL_CALLING_MAP_CHUNK_SIZE };
10
- export const WORKER_SUBREQUEST_SAFE_TOOL_CALLS_PER_CHUNK =
11
- TOOL_CALLING_MAP_CHUNK_SIZE;
17
+ // Cloudflare preview Workers enforce the 50-subrequest invocation limit. Leave
18
+ // headroom for coordinator/storage calls around row-level unbatched tool RPCs.
19
+ export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET = 40;
20
+ // Fresh unbatched tool calls use one RUNTIME_API integration execute RPC and
21
+ // one HARNESS durable-receipt completion RPC. Batch-cap rows by both.
22
+ export const SUBREQUESTS_PER_UNBATCHED_TOOL_CALL = 2;
12
23
 
13
24
  export type WorkerMapChunkPlanInput = {
14
25
  mapName: string;
15
26
  rowCountHint: number | null;
16
27
  executionPlan?: ExecutionPlan | null;
28
+ staticPipeline?: PlayStaticPipeline | null;
29
+ };
30
+
31
+ type PlanMap = ExecutionPlan['maps'][number];
32
+ type ChunkSizingMap = Pick<
33
+ PlanMap,
34
+ | 'mapName'
35
+ | 'tableNamespace'
36
+ | 'outputFields'
37
+ | 'defaultChunkSize'
38
+ | 'stepsPerChunk'
39
+ > &
40
+ Partial<Pick<PlanMap, 'externalStepFields'>>;
41
+ type ChunkSizingPlan = {
42
+ maps: ChunkSizingMap[];
43
+ toolDeclarations?: ExecutionPlan['toolDeclarations'];
44
+ softWorkflowStepBudget?: number | null;
17
45
  };
18
46
 
47
+ type MapToolStats = [totalToolCount: number, unbatchedToolCount: number];
48
+
49
+ function declarationBelongsToMapOutput(
50
+ declaration: { field?: string | null },
51
+ outputFields: readonly string[],
52
+ externalStepFields: readonly string[] = [],
53
+ ): boolean {
54
+ const fieldName = declaration.field;
55
+ if (fieldName == null) return true;
56
+ if (
57
+ outputFields.some(
58
+ (field) => fieldName === field || fieldName.startsWith(`${field}.`),
59
+ )
60
+ ) {
61
+ return true;
62
+ }
63
+ return externalStepFields.some(
64
+ (field) => fieldName === field || fieldName.endsWith(`.${field}`),
65
+ );
66
+ }
67
+
68
+ function isUnbatchedTool(toolId: string): boolean {
69
+ return getPlayRuntimeBatchStrategy(toolId) === null;
70
+ }
71
+
72
+ function countToolSubsteps(
73
+ substeps: readonly PlayStaticSubstep[],
74
+ ): MapToolStats {
75
+ let totalToolCount = 0;
76
+ let unbatchedToolCount = 0;
77
+
78
+ for (const substep of substeps) {
79
+ if (substep.type === 'tool') {
80
+ totalToolCount += 1;
81
+ if (isUnbatchedTool(substep.toolId)) {
82
+ unbatchedToolCount += 1;
83
+ }
84
+ continue;
85
+ }
86
+
87
+ if (substep.type === 'waterfall') {
88
+ for (const step of substep.steps ?? []) {
89
+ if (!step.toolId) continue;
90
+ totalToolCount += 1;
91
+ if (isUnbatchedTool(step.toolId)) {
92
+ unbatchedToolCount += 1;
93
+ }
94
+ }
95
+ continue;
96
+ }
97
+
98
+ if (
99
+ substep.type === 'step_suite' ||
100
+ substep.type === 'control_flow' ||
101
+ substep.type === 'dataset'
102
+ ) {
103
+ const [nestedTotal, nestedUnbatched] = countToolSubsteps(
104
+ substep.steps ?? [],
105
+ );
106
+ totalToolCount += nestedTotal;
107
+ unbatchedToolCount += nestedUnbatched;
108
+ }
109
+ }
110
+
111
+ return [totalToolCount, unbatchedToolCount];
112
+ }
113
+
114
+ function countProducerTools(
115
+ producers: readonly PlayStaticColumnProducer[],
116
+ ): MapToolStats {
117
+ let totalToolCount = 0;
118
+ let unbatchedToolCount = 0;
119
+
120
+ for (const producer of producers) {
121
+ if (producer.kind === 'tool' && producer.toolId) {
122
+ totalToolCount += 1;
123
+ if (isUnbatchedTool(producer.toolId)) {
124
+ unbatchedToolCount += 1;
125
+ }
126
+ }
127
+ if (producer.substep.type === 'waterfall') {
128
+ for (const step of producer.substep.steps ?? []) {
129
+ if (!step.toolId) continue;
130
+ totalToolCount += 1;
131
+ if (isUnbatchedTool(step.toolId)) {
132
+ unbatchedToolCount += 1;
133
+ }
134
+ }
135
+ }
136
+ if (producer.steps?.length) {
137
+ const [nestedTotal, nestedUnbatched] = countProducerTools(producer.steps);
138
+ totalToolCount += nestedTotal;
139
+ unbatchedToolCount += nestedUnbatched;
140
+ }
141
+ }
142
+
143
+ return [totalToolCount, unbatchedToolCount];
144
+ }
145
+
146
+ function datasetBelongsToPlanMap(
147
+ dataset: Extract<PlayStaticSubstep, { type: 'dataset' }>,
148
+ planMap: ChunkSizingMap,
149
+ mapName: string,
150
+ ): boolean {
151
+ return [dataset.name, dataset.tableNamespace, dataset.field].some(
152
+ (datasetName) =>
153
+ typeof datasetName === 'string' &&
154
+ datasetName.length > 0 &&
155
+ (datasetName === planMap.mapName ||
156
+ datasetName === planMap.tableNamespace ||
157
+ datasetName === mapName),
158
+ );
159
+ }
160
+
161
+ function countMapLocalTools(input: {
162
+ staticPipeline: PlayStaticPipeline | null | undefined;
163
+ planMap: ChunkSizingMap | null | undefined;
164
+ mapName: string;
165
+ }): MapToolStats | null {
166
+ if (!input.staticPipeline || !input.planMap) return null;
167
+ const planMap = input.planMap;
168
+ const dataset = flattenStaticSubsteps(
169
+ getTopLevelPipelineSubsteps(input.staticPipeline),
170
+ ).find(
171
+ (substep): substep is Extract<PlayStaticSubstep, { type: 'dataset' }> =>
172
+ substep.type === 'dataset' &&
173
+ datasetBelongsToPlanMap(substep, planMap, input.mapName),
174
+ );
175
+ if (!dataset) return null;
176
+
177
+ if (dataset.steps?.length) {
178
+ return countToolSubsteps(dataset.steps);
179
+ }
180
+
181
+ if (dataset.columns?.length) {
182
+ return countProducerTools(
183
+ dataset.columns.flatMap((column) => column.producers),
184
+ );
185
+ }
186
+
187
+ return [0, 0];
188
+ }
189
+
190
+ function countFallbackDeclarationTools(input: {
191
+ declarations: readonly { toolId: string; field?: string | null }[];
192
+ outputFields: readonly string[];
193
+ externalStepFields?: readonly string[];
194
+ }): MapToolStats {
195
+ let totalToolCount = 0;
196
+ let unbatchedToolCount = 0;
197
+
198
+ for (const declaration of input.declarations) {
199
+ if (
200
+ !declarationBelongsToMapOutput(
201
+ declaration,
202
+ input.outputFields,
203
+ input.externalStepFields ?? [],
204
+ )
205
+ ) {
206
+ continue;
207
+ }
208
+ totalToolCount += 1;
209
+ if (isUnbatchedTool(declaration.toolId)) {
210
+ unbatchedToolCount += 1;
211
+ }
212
+ }
213
+
214
+ return [totalToolCount, unbatchedToolCount];
215
+ }
216
+
217
+ function countDeclarationTools(
218
+ declarations: readonly { toolId: string; field?: string | null }[],
219
+ ): MapToolStats {
220
+ return [
221
+ declarations.length,
222
+ declarations.filter((declaration) => isUnbatchedTool(declaration.toolId))
223
+ .length,
224
+ ];
225
+ }
226
+
227
+ function countNestedExecutionSteps(
228
+ substeps: readonly PlayStaticSubstep[] | undefined,
229
+ ): number {
230
+ let stepsPerChunk = 1;
231
+ for (const substep of substeps ?? []) {
232
+ if (substep.type === 'waterfall') {
233
+ stepsPerChunk = Math.max(stepsPerChunk, substep.steps?.length ?? 1);
234
+ continue;
235
+ }
236
+ if (substep.type === 'step_suite') {
237
+ stepsPerChunk = Math.max(stepsPerChunk, substep.steps.length);
238
+ continue;
239
+ }
240
+ if (substep.type === 'control_flow' || substep.type === 'dataset') {
241
+ stepsPerChunk = Math.max(
242
+ stepsPerChunk,
243
+ countNestedExecutionSteps(substep.steps),
244
+ );
245
+ }
246
+ }
247
+ return stepsPerChunk;
248
+ }
249
+
250
+ function buildStaticChunkSizingPlan(
251
+ staticPipeline: PlayStaticPipeline | null,
252
+ ): ChunkSizingPlan | null {
253
+ if (!staticPipeline) return null;
254
+ const datasets = flattenStaticSubsteps(
255
+ getTopLevelPipelineSubsteps(staticPipeline),
256
+ ).filter(
257
+ (substep): substep is Extract<PlayStaticSubstep, { type: 'dataset' }> =>
258
+ substep.type === 'dataset',
259
+ );
260
+ if (datasets.length === 0) return null;
261
+ return {
262
+ maps: datasets.map((dataset) => {
263
+ const stepsPerChunk = countNestedExecutionSteps(dataset.steps);
264
+ return {
265
+ mapName: dataset.name ?? dataset.field,
266
+ tableNamespace: dataset.tableNamespace ?? dataset.field,
267
+ outputFields: dataset.outputFields ?? [],
268
+ defaultChunkSize: stepsPerChunk > 1 ? 1_000 : 5_000,
269
+ stepsPerChunk,
270
+ };
271
+ }),
272
+ };
273
+ }
274
+
19
275
  export function chooseWorkerMapRowsPerChunk(
20
276
  input: WorkerMapChunkPlanInput,
21
277
  ): number {
22
- const plan = input.executionPlan ?? null;
278
+ const plan: ChunkSizingPlan | null = input.executionPlan
279
+ ? {
280
+ maps: input.executionPlan.maps,
281
+ toolDeclarations: input.executionPlan.toolDeclarations,
282
+ softWorkflowStepBudget:
283
+ input.executionPlan.chunkPlan.softWorkflowStepBudget,
284
+ }
285
+ : buildStaticChunkSizingPlan(input.staticPipeline ?? null);
23
286
  const planMap = plan?.maps.find(
24
287
  (candidate) =>
25
288
  candidate.mapName === input.mapName ||
26
289
  candidate.tableNamespace === input.mapName,
27
290
  );
291
+ const staticMapToolStats = countMapLocalTools({
292
+ staticPipeline: input.staticPipeline,
293
+ planMap,
294
+ mapName: input.mapName,
295
+ });
296
+ const mapToolStats =
297
+ staticMapToolStats ??
298
+ (plan && planMap
299
+ ? countFallbackDeclarationTools({
300
+ declarations: plan.toolDeclarations ?? [],
301
+ outputFields: planMap.outputFields,
302
+ externalStepFields: planMap.externalStepFields ?? [],
303
+ })
304
+ : plan
305
+ ? countDeclarationTools(plan.toolDeclarations ?? [])
306
+ : [0, 0]);
28
307
  const rowsPerChunk = chooseMapChunkSize({
29
308
  totalRows: input.rowCountHint,
30
309
  mapCount: Math.max(1, plan?.maps.length ?? 1),
31
310
  stepsPerChunk: planMap?.stepsPerChunk ?? 1,
32
311
  preferredChunkSize: planMap?.defaultChunkSize,
33
- softWorkflowStepBudget: plan?.chunkPlan.softWorkflowStepBudget,
312
+ softWorkflowStepBudget: plan?.softWorkflowStepBudget,
34
313
  });
35
- const estimatedSubrequestProducingSteps =
36
- estimateSubrequestProducingStepsForMap(plan, planMap);
37
- if (estimatedSubrequestProducingSteps > 0) {
38
- const subrequestSafeRows = Math.max(
39
- 1,
40
- Math.floor(
41
- WORKER_SUBREQUEST_SAFE_TOOL_CALLS_PER_CHUNK /
42
- estimatedSubrequestProducingSteps,
43
- ),
44
- );
45
- if (subrequestSafeRows < rowsPerChunk) {
46
- if (input.rowCountHint === null || !Number.isFinite(input.rowCountHint)) {
47
- return subrequestSafeRows;
48
- }
49
- const totalRows = Math.max(0, Math.floor(input.rowCountHint));
50
- const mapCount = Math.max(1, plan?.maps.length ?? 1);
51
- const softBudget =
52
- plan?.chunkPlan.softWorkflowStepBudget ??
53
- EXECUTION_PLAN_DEFAULTS.workflowSoftStepBudget;
54
- const maxChunksPerMap = Math.max(
55
- 1,
56
- Math.floor(
57
- Math.max(
58
- mapCount,
59
- Math.floor(
60
- (softBudget -
61
- EXECUTION_PLAN_DEFAULTS.ingestStepCount -
62
- EXECUTION_PLAN_DEFAULTS.finalizationStepCount) /
63
- estimatedSubrequestProducingSteps,
64
- ),
65
- ) / mapCount,
66
- ),
67
- );
68
- const requiredChunks = Math.ceil(totalRows / subrequestSafeRows);
69
- if (requiredChunks <= maxChunksPerMap) return subrequestSafeRows;
70
- throw new Error(
71
- `Worker budget exceeded for map "${input.mapName}": ${requiredChunks}/${maxChunksPerMap} chunks.`,
72
- );
73
- }
74
- return rowsPerChunk;
75
- }
76
314
 
77
315
  const toolFreeSimpleMap =
78
- !!planMap &&
79
- planMap.stepsPerChunk === 1 &&
80
- estimateSubrequestProducingStepsForMap(plan, planMap) === 0;
316
+ !!planMap && planMap.stepsPerChunk === 1 && mapToolStats[0] === 0;
81
317
  if (
82
318
  toolFreeSimpleMap &&
83
319
  (input.rowCountHint === null ||
@@ -86,75 +322,22 @@ export function chooseWorkerMapRowsPerChunk(
86
322
  return Math.max(rowsPerChunk, CACHE_ENABLED_SIMPLE_MAP_CHUNK_SIZE);
87
323
  }
88
324
 
89
- return rowsPerChunk;
90
- }
91
-
92
- function estimateSubrequestProducingStepsForMap(
93
- plan: ExecutionPlan | null,
94
- planMap: ExecutionPlan['maps'][number] | undefined,
95
- ): number {
96
- const toolDeclarations = plan?.toolDeclarations ?? [];
97
- if (!planMap) {
98
- return toolDeclarations.length;
99
- }
100
- const explicitExternalSteps = Array.isArray(planMap.externalStepFields)
101
- ? planMap.externalStepFields.length
102
- : 0;
103
- const mapStepsPerChunk =
104
- explicitExternalSteps > 0
105
- ? 0
106
- : (planMap.stepsPerChunk ?? 1) > 1
107
- ? (planMap.stepsPerChunk ?? 1)
108
- : 0;
109
- if (toolDeclarations.length === 0) {
110
- return Math.max(mapStepsPerChunk, explicitExternalSteps);
111
- }
112
- const outputFields = new Set(
113
- planMap.outputFields.map((field) => field.trim()).filter(Boolean),
114
- );
115
- const explicitExternalStepFields = new Set(
116
- (planMap.externalStepFields ?? [])
117
- .map((field) => field.trim())
118
- .filter(Boolean),
119
- );
120
- const hasModernStepMetadata =
121
- Array.isArray(planMap.stepFields) ||
122
- Array.isArray(planMap.externalStepFields);
123
- const mapFields = new Set([
124
- planMap.mapName,
125
- planMap.tableNamespace,
126
- ...planMap.outputFields,
127
- ...(planMap.externalStepFields ?? []),
128
- ...(hasModernStepMetadata ? [] : (planMap.stepFields ?? [])),
129
- ...planMap.waterfallStages.flatMap((stage) => [
130
- stage.waterfallId,
131
- ...stage.stageIds,
132
- ]),
133
- ]);
134
- let scopedToolDeclarations = 0;
135
- for (const declaration of toolDeclarations) {
136
- const field = declaration.field?.trim();
137
- if (!field) continue;
138
- if (mapFields.has(field)) {
139
- scopedToolDeclarations += 1;
140
- continue;
141
- }
142
- if (!field.includes('.')) {
143
- continue;
144
- }
145
- const firstSegment = field.slice(0, field.indexOf('.'));
146
- const lastSegment = field.slice(field.lastIndexOf('.') + 1);
147
- if (
148
- outputFields.has(firstSegment) ||
149
- explicitExternalStepFields.has(lastSegment) ||
150
- (!hasModernStepMetadata && outputFields.has(lastSegment))
151
- ) {
152
- scopedToolDeclarations += 1;
153
- }
325
+ if (mapToolStats[1] > 0) {
326
+ const unbatchedToolCallsPerRow =
327
+ staticMapToolStats !== null
328
+ ? mapToolStats[1]
329
+ : Math.max(planMap?.stepsPerChunk ?? 1, mapToolStats[1]);
330
+ const unbatchedSubrequestsPerRow =
331
+ unbatchedToolCallsPerRow * SUBREQUESTS_PER_UNBATCHED_TOOL_CALL;
332
+ const unbatchedToolRowsPerChunk = Math.max(
333
+ 1,
334
+ Math.floor(
335
+ UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET /
336
+ unbatchedSubrequestsPerRow,
337
+ ),
338
+ );
339
+ return Math.min(rowsPerChunk, unbatchedToolRowsPerChunk);
154
340
  }
155
- return Math.max(
156
- mapStepsPerChunk,
157
- explicitExternalSteps,
158
- scopedToolDeclarations,
159
- );
341
+
342
+ return rowsPerChunk;
160
343
  }
@@ -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.163',
107
+ version: '0.1.165',
108
108
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
109
109
  supportPolicy: {
110
- latest: '0.1.163',
110
+ latest: '0.1.165',
111
111
  minimumSupported: '0.1.53',
112
112
  deprecatedBelow: '0.1.53',
113
113
  commandMinimumSupported: [
@@ -319,6 +319,15 @@ export interface ToolSearchResult {
319
319
  search_mode?: 'v1' | 'v2';
320
320
  /** Whether search fell back to category matching. */
321
321
  search_fallback_to_category?: boolean;
322
+ /** Explanation and next commands when filters/search succeed but match zero tools. */
323
+ emptyResult?: {
324
+ reason: string;
325
+ message: string;
326
+ suggestions: Array<{
327
+ label: string;
328
+ command: string;
329
+ }>;
330
+ };
322
331
  /** Hint explaining omitted play results when searching tools only. */
323
332
  omitted_plays_hint?: string;
324
333
  /** Copyable CLI command templates for follow-up discovery/execution. */