Skip to content

Commit 2c6d0c3

Browse files
committed
progress
1 parent a1752e8 commit 2c6d0c3

44 files changed

Lines changed: 1416 additions & 218 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/docs/content/docs/en/blocks/function.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ plt.show()
189189

190190
Function blocks receive their code, parameters, resolved references, and previous block context in an internal execution request. Sim can safely reference oversized workflow outputs, such as large `loop.results` or `parallel.results`, when you select a smaller nested field like `<loop.results[0][0].id>`.
191191

192+
File outputs are metadata-first by default. Referencing `<file.name>`, `<file.url>`, or similar metadata does not hydrate file contents. Referencing `<file.base64>` explicitly hydrates that file's base64 content at resolver time and can fail if the file exceeds the configured inline limit.
193+
192194
Avoid passing a full large object into a Function block when you only need one field. For example, prefer `<api.data.customerId>` over `<api.data>` when the API response is large. If the complete function request body is still larger than the platform limit, execution can fail before your code starts.
193195

194196
For large generated data, write the result to a file or table with `outputPath`, `outputSandboxPath`, or `outputTable` instead of returning the entire payload inline.

apps/docs/content/docs/en/blocks/parallel.mdx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Choose between two types of parallel execution:
3434
</div>
3535

3636
Use this when you need to run the same operation multiple times concurrently.
37+
If the total count is larger than the batch size, Sim runs the work in serial batches while preserving the original result order.
3738

3839
```
3940
Example: Run 5 parallel instances
@@ -57,7 +58,7 @@ Choose between two types of parallel execution:
5758
/>
5859
</div>
5960

60-
Each instance processes one item from the collection simultaneously.
61+
Each instance processes one item from the collection. Large collections run in serial batches while preserving each item's original index.
6162

6263
```
6364
Example: Process ["task1", "task2", "task3"] in parallel
@@ -140,6 +141,12 @@ const allResults = <processtasks.results>;
140141
// Returns: [result1, result2, result3, ...]
141142
```
142143

144+
For large result sets, reference only the entry or field you need, such as `<processtasks.results[10][0].id>`. Sim keeps aggregate results indexable and hydrates stored entries when an indexed path is explicitly referenced.
145+
146+
### Batch Size
147+
148+
Parallel blocks run up to 20 branches at a time by default. Increase the total count or collection size to process more work; Sim will execute the next batch after the current batch finishes. You can lower the batch size to reduce concurrency for rate-limited APIs.
149+
143150
### Instance Isolation
144151

145152
Each parallel instance runs independently:
@@ -157,7 +164,7 @@ Each parallel instance runs independently:
157164
While parallel execution is faster, be mindful of:
158165
- API rate limits when making concurrent requests
159166
- Memory usage with large datasets
160-
- Maximum of 20 concurrent instances to prevent resource exhaustion
167+
- Maximum of 20 concurrent instances per batch to prevent resource exhaustion
161168
</Callout>
162169

163170
## Parallel vs Loop
@@ -186,6 +193,9 @@ Understanding when to use each:
186193
<li>
187194
<strong>Collection</strong>: Array or object to distribute (collection-based)
188195
</li>
196+
<li>
197+
<strong>Batch size</strong>: Number of branches to run concurrently, from 1 to 20
198+
</li>
189199
</ul>
190200
</Tab>
191201
<Tab>

apps/docs/content/docs/en/execution/api-deployment.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ Workflow execution responses are capped by platform request and response limits.
232232
}
233233
```
234234

235-
The `version` field is part of the external API contract. Treat the reference as an opaque placeholder for a value that could not be safely embedded in the response. `id`, `key`, and `executionId` are not fetch URLs; use `selectedOutputs` to request a smaller nested field, reduce the data passed between blocks, or return the data from a Response block when your workflow intentionally owns the HTTP response body.
235+
The `version` field is part of the external API contract. Treat the reference as an opaque placeholder for a value that could not be safely embedded in the response. `id`, `key`, and `executionId` are not fetch URLs; use `selectedOutputs` to request a smaller nested field, reduce the data passed between blocks, or return the data from a Response block when your workflow intentionally owns the HTTP response body. File outputs are metadata-first; request `.base64` only when you need inline file content.
236236

237237
### Asynchronous
238238

apps/realtime/src/database/operations.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ async function handleBlocksOperationTx(
742742
config: {
743743
parallelType: 'fixed',
744744
count: DEFAULT_PARALLEL_COUNT,
745+
batchSize: 20,
745746
nodes: [],
746747
},
747748
})
@@ -1700,6 +1701,10 @@ async function handleSubflowOperationTx(
17001701
blockData.parallelType = payload.config.parallelType
17011702
}
17021703

1704+
if (payload.config.batchSize !== undefined) {
1705+
blockData.batchSize = payload.config.batchSize
1706+
}
1707+
17031708
await tx
17041709
.update(workflowBlocks)
17051710
.set({

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ async function handleExecutePost(
775775
? ((await hydrateUserFilesWithBase64(result.output, {
776776
requestId,
777777
executionId,
778+
userId: actorUserId,
778779
maxBytes: base64MaxBytes,
779780
})) as NormalizedBlockOutput)
780781
: result.output
@@ -1297,6 +1298,7 @@ async function handleExecutePost(
12971298
? await hydrateUserFilesWithBase64(result.output, {
12981299
requestId,
12991300
executionId,
1301+
userId: actorUserId,
13001302
maxBytes: base64MaxBytes,
13011303
})
13021304
: result.output

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/subflow-editor/subflow-editor.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export function SubflowEditor({
5353
isCountMode,
5454
isConditionMode,
5555
inputValue,
56+
batchSizeValue,
5657
editorValue,
5758
typeOptions,
5859
showTagDropdown,
@@ -61,6 +62,8 @@ export function SubflowEditor({
6162
handleSubflowTypeChange,
6263
handleSubflowIterationsChange,
6364
handleSubflowIterationsSave,
65+
handleParallelBatchSizeChange,
66+
handleParallelBatchSizeSave,
6467
handleSubflowEditorChange,
6568
handleSubflowTagSelect,
6669
highlightWithReferences,
@@ -80,6 +83,7 @@ export function SubflowEditor({
8083
activeSearchTarget.canonicalSubBlockId === fieldId)
8184
const isTypeHighlighted = isSearchHighlighted(WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.type)
8285
const isConfigHighlighted = isSearchHighlighted(configSearchFieldId)
86+
const isBatchSizeHighlighted = isSearchHighlighted(WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.batchSize)
8387

8488
return (
8589
<div className='flex flex-1 flex-col overflow-hidden pt-[0px]'>
@@ -197,6 +201,34 @@ export function SubflowEditor({
197201
</div>
198202
)}
199203
</div>
204+
205+
{currentBlock.type === 'parallel' && (
206+
<div
207+
data-workflow-search-subblock-id={WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.batchSize}
208+
data-workflow-search-canonical-id={WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS.batchSize}
209+
className='mt-4 rounded-md'
210+
>
211+
<Label className='mb-[6.5px] block pl-0.5 font-medium text-[var(--text-primary)] text-small'>
212+
{isBatchSizeHighlighted ? (
213+
<mark className={WORKFLOW_SEARCH_HIGHLIGHT_CLASS}>Parallel Batch Size</mark>
214+
) : (
215+
'Parallel Batch Size'
216+
)}
217+
</Label>
218+
<Input
219+
type='text'
220+
value={batchSizeValue}
221+
onChange={handleParallelBatchSizeChange}
222+
onBlur={handleParallelBatchSizeSave}
223+
onKeyDown={(e) => e.key === 'Enter' && handleParallelBatchSizeSave()}
224+
disabled={!userCanEdit}
225+
className='mb-1'
226+
/>
227+
<div className='text-[var(--text-muted)] text-micro'>
228+
Run 1 to 20 parallel branches at a time.
229+
</div>
230+
</div>
231+
)}
200232
</div>
201233
</div>
202234

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/hooks/use-subflow-editor.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const SUBFLOW_CONFIG = {
4040
typeLabels: { count: 'Parallel Count', collection: 'Parallel Each' },
4141
typeKey: 'parallelType' as const,
4242
storeKey: 'parallels' as const,
43-
maxIterations: 20,
43+
maxIterations: 1000,
4444
configKeys: {
4545
iterations: 'count' as const,
4646
items: 'distribution' as const,
@@ -62,6 +62,7 @@ export function useSubflowEditor(currentBlock: BlockState | null, currentBlockId
6262
const textareaRef = useRef<HTMLTextAreaElement | null>(null)
6363
const editorContainerRef = useRef<HTMLDivElement>(null)
6464
const [tempInputValue, setTempInputValue] = useState<string | null>(null)
65+
const [tempBatchSizeValue, setTempBatchSizeValue] = useState<string | null>(null)
6566
const [showTagDropdown, setShowTagDropdown] = useState(false)
6667
const [cursorPosition, setCursorPosition] = useState(0)
6768

@@ -97,6 +98,7 @@ export function useSubflowEditor(currentBlock: BlockState | null, currentBlockId
9798
const {
9899
collaborativeUpdateLoopType,
99100
collaborativeUpdateParallelType,
101+
collaborativeUpdateParallelBatchSize,
100102
collaborativeUpdateIterationCount,
101103
collaborativeUpdateIterationCollection,
102104
} = useCollaborativeWorkflow()
@@ -260,6 +262,25 @@ export function useSubflowEditor(currentBlock: BlockState | null, currentBlockId
260262
collaborativeUpdateIterationCount,
261263
])
262264

265+
const handleParallelBatchSizeChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
266+
const sanitizedValue = e.target.value.replace(/[^0-9]/g, '')
267+
const numValue = Number.parseInt(sanitizedValue)
268+
if (!Number.isNaN(numValue)) {
269+
setTempBatchSizeValue(Math.min(20, numValue).toString())
270+
} else {
271+
setTempBatchSizeValue(sanitizedValue)
272+
}
273+
}, [])
274+
275+
const handleParallelBatchSizeSave = useCallback(() => {
276+
if (!currentBlockId || currentBlock?.type !== 'parallel') return
277+
const value = Number.parseInt(tempBatchSizeValue ?? '20')
278+
if (!Number.isNaN(value)) {
279+
collaborativeUpdateParallelBatchSize(currentBlockId, Math.min(20, Math.max(1, value)))
280+
}
281+
setTempBatchSizeValue(null)
282+
}, [tempBatchSizeValue, currentBlockId, currentBlock, collaborativeUpdateParallelBatchSize])
283+
263284
/**
264285
* Handle editor value change (collection/condition)
265286
*/
@@ -342,11 +363,16 @@ export function useSubflowEditor(currentBlock: BlockState | null, currentBlockId
342363
: ''
343364

344365
const iterations = configIterations
366+
const parallelBatchSize =
367+
isSubflow && currentBlock?.type === 'parallel'
368+
? ((nodeConfig as any)?.batchSize ?? (blockData as any)?.batchSize ?? 20)
369+
: 20
345370
const collectionString =
346371
typeof configCollection === 'string' ? configCollection : JSON.stringify(configCollection) || ''
347372
const conditionString = typeof configCondition === 'string' ? configCondition : ''
348373

349374
const inputValue = tempInputValue ?? iterations.toString()
375+
const batchSizeValue = tempBatchSizeValue ?? parallelBatchSize.toString()
350376
const editorValue = isConditionMode ? conditionString : collectionString
351377

352378
// Type options for combobox
@@ -366,6 +392,7 @@ export function useSubflowEditor(currentBlock: BlockState | null, currentBlockId
366392
isCountMode,
367393
isConditionMode,
368394
inputValue,
395+
batchSizeValue,
369396
editorValue,
370397
typeOptions,
371398
showTagDropdown,
@@ -377,6 +404,8 @@ export function useSubflowEditor(currentBlock: BlockState | null, currentBlockId
377404
handleSubflowTypeChange,
378405
handleSubflowIterationsChange,
379406
handleSubflowIterationsSave,
407+
handleParallelBatchSizeChange,
408+
handleParallelBatchSizeSave,
380409
handleSubflowEditorChange,
381410
handleSubflowTagSelect,
382411
highlightWithReferences,

apps/sim/executor/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export const EDGE = {
6767
LOOP_CONTINUE: 'loop_continue',
6868
LOOP_CONTINUE_ALT: 'loop-continue-source',
6969
LOOP_EXIT: 'loop_exit',
70+
PARALLEL_CONTINUE: 'parallel_continue',
7071
PARALLEL_EXIT: 'parallel_exit',
7172
ERROR: 'error',
7273
SOURCE: 'source',

apps/sim/executor/execution/block-executor.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,12 @@ export class BlockExecutor {
127127
resolvedInputs: fnInputs,
128128
displayInputs,
129129
contextVariables,
130-
} = this.resolver.resolveInputsForFunctionBlock(ctx, node.id, block.config.params, block)
130+
} = await this.resolver.resolveInputsForFunctionBlock(
131+
ctx,
132+
node.id,
133+
block.config.params,
134+
block
135+
)
131136
resolvedInputs = {
132137
...fnInputs,
133138
[FUNCTION_BLOCK_CONTEXT_VARS_KEY]: contextVariables,
@@ -137,7 +142,7 @@ export class BlockExecutor {
137142
}
138143
inputsForLog = displayInputs
139144
} else {
140-
resolvedInputs = this.resolver.resolveInputs(ctx, node.id, block.config.params, block)
145+
resolvedInputs = await this.resolver.resolveInputs(ctx, node.id, block.config.params, block)
141146
inputsForLog = resolvedInputs
142147
}
143148

@@ -194,6 +199,7 @@ export class BlockExecutor {
194199
normalizedOutput = (await hydrateUserFilesWithBase64(normalizedOutput, {
195200
requestId: ctx.metadata.requestId,
196201
executionId: ctx.executionId,
202+
userId: ctx.userId,
197203
maxBytes: ctx.base64MaxBytes,
198204
})) as NormalizedBlockOutput
199205
}

apps/sim/executor/execution/edge-manager.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ export class EdgeManager {
230230
return handle === EDGE.PARALLEL_EXIT
231231
}
232232

233+
if (output.selectedRoute === EDGE.PARALLEL_CONTINUE) {
234+
return false
235+
}
236+
233237
if (!handle) {
234238
return true
235239
}

0 commit comments

Comments
 (0)