Skip to content

Commit 6b06a3f

Browse files
committed
feat(workflow): enhance Loop node with dynamic iteration control
- Add iterations-override input handle for external loop control - Implement executeLoopStartNode for improved execution logic - Add getLoopIterations helper for dynamic iteration counting - Update node sorting to handle iteration override inputs - Refactor loop initialization and state management - Clean up IfElse node display formatting - Add overrideIterations flag to Loop initialization The changes enable dynamic adjustment of loop iterations through external inputs, improving workflow flexibility and control flow management.
1 parent e67c356 commit 6b06a3f

File tree

6 files changed

+93
-31
lines changed

6 files changed

+93
-31
lines changed

vscode/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "cody-ai",
44
"private": true,
55
"displayName": "Cody: AI Code Assistant",
6-
"version": "1.67.0+3",
6+
"version": "1.67.0+4",
77
"publisher": "sourcegraph",
88
"license": "Apache-2.0",
99
"icon": "resources/sourcegraph.png",

vscode/src/workflow/node-sorting.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,7 @@ function processLoopWithCycles(
572572
const preLoopNodeIds = new Set(preLoopNodes.map(node => node.id))
573573
const nodesInsideLoop = findLoopNodes(loopStart, nodes, edges, preLoopNodeIds)
574574

575-
// Process loop iterations
576-
const iterations = shouldIterateLoops ? (loopStart as LoopStartNode).data.iterations : 1
575+
const iterations = getLoopIterations(loopStart, nodes, edges, shouldIterateLoops)
577576

578577
for (let i = 0; i < iterations; i++) {
579578
processedNodes.push({ ...loopStart })
@@ -597,3 +596,27 @@ function processLoopWithCycles(
597596

598597
return processedNodes
599598
}
599+
600+
function getLoopIterations(
601+
loopStart: WorkflowNodes,
602+
nodes: WorkflowNodes[],
603+
edges: Edge[],
604+
shouldIterateLoops: boolean
605+
): number {
606+
if (!shouldIterateLoops) {
607+
return 1
608+
}
609+
610+
const iterationOverrideEdges = edges.filter(
611+
edge => edge.target === loopStart.id && edge.targetHandle === 'iterations-override'
612+
)
613+
614+
if (iterationOverrideEdges.length === 0) {
615+
return (loopStart as LoopStartNode).data.iterations
616+
}
617+
618+
const sourceNode = nodes.find(n => n.id === iterationOverrideEdges[0].source)
619+
const overrideValue = Number.parseInt(sourceNode?.data.content || '', 10)
620+
621+
return !Number.isNaN(overrideValue) ? overrideValue : (loopStart as LoopStartNode).data.iterations
622+
}

vscode/src/workflow/workflow-executor.ts

+49-25
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,6 @@ export async function executeWorkflow(
100100
type: 'execution_started',
101101
} as ExtensionToWorkflow)
102102

103-
for (const node of sortedNodes) {
104-
if (node.type === NodeType.LOOP_START) {
105-
context.loopStates.set(node.id, {
106-
currentIteration: -1,
107-
maxIterations: (node as LoopStartNode).data.iterations,
108-
variable: (node as LoopStartNode).data.loopVariable,
109-
})
110-
}
111-
}
112103
for (const node of sortedNodes) {
113104
if (skipNodes.has(node.id)) {
114105
continue
@@ -261,22 +252,7 @@ export async function executeWorkflow(
261252
break
262253
}
263254
case NodeType.LOOP_START: {
264-
const inputs = combineParentOutputsByConnectionOrder(node.id, context)
265-
266-
const loopState = context.loopStates.get(node.id) || {
267-
currentIteration: -1,
268-
maxIterations: (node as LoopStartNode).data.iterations,
269-
variable: (node as LoopStartNode).data.loopVariable,
270-
}
271-
272-
// Only increment for next iteration if not at max
273-
if (loopState.currentIteration < loopState.maxIterations) {
274-
context.loopStates.set(node.id, {
275-
...loopState,
276-
currentIteration: loopState.currentIteration + 1,
277-
})
278-
}
279-
result = await executePreviewNode(inputs.join('\n'), node.id, webview, context)
255+
result = await executeLoopStartNode(node as LoopStartNode, context)
280256
break
281257
}
282258
case NodeType.LOOP_END: {
@@ -913,3 +889,51 @@ const commandsNotAllowed = [
913889
'lsusb',
914890
'lspci',
915891
]
892+
893+
async function executeLoopStartNode(
894+
node: LoopStartNode,
895+
context: IndexedExecutionContext
896+
): Promise<string> {
897+
// Helper function to filter edges by handle type
898+
const getInputsByHandle = (handleType: string) =>
899+
combineParentOutputsByConnectionOrder(node.id, {
900+
...context,
901+
edgeIndex: {
902+
...context.edgeIndex,
903+
byTarget: new Map([
904+
[
905+
node.id,
906+
(context.edgeIndex.byTarget.get(node.id) || []).filter(
907+
edge => edge.targetHandle === handleType
908+
),
909+
],
910+
]),
911+
},
912+
})
913+
914+
const mainInputs = getInputsByHandle('main')
915+
const iterationOverrides = getInputsByHandle('iterations-override')
916+
917+
let loopState = context.loopStates.get(node.id)
918+
919+
if (!loopState) {
920+
const maxIterations =
921+
iterationOverrides.length > 0
922+
? Number.parseInt(iterationOverrides[0], 10) || node.data.iterations
923+
: node.data.iterations
924+
925+
loopState = {
926+
currentIteration: 0, // Start at 0 for clearer iteration counting
927+
maxIterations,
928+
variable: node.data.loopVariable,
929+
}
930+
context.loopStates.set(node.id, loopState)
931+
} else if (loopState.currentIteration < loopState.maxIterations - 1) {
932+
context.loopStates.set(node.id, {
933+
...loopState,
934+
currentIteration: loopState.currentIteration + 1,
935+
})
936+
}
937+
938+
return mainInputs.join('\n')
939+
}

vscode/webviews/workflow/components/nodes/IfElse_Node.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,12 @@ export const IfElseNode: React.FC<BaseNodeProps> = ({ data, selected }) => (
4949
>
5050
IF...ELSE
5151
</div>
52-
<div className="tw-flex tw-items-center tw-justify-center tw-p-2 tw-border tw-border-[var(--vscode-input-border)] tw-rounded">
53-
<span>{data.condition || 'Condition'}</span>
52+
<div className="tw-flex tw-items-center tw-justify-center">
53+
<span>{data.title}</span>
5454
</div>
55+
{/*<div className="tw-flex tw-items-center tw-justify-center tw-p-2 tw-border tw-border-[var(--vscode-input-border)] tw-rounded">
56+
<span>{data.condition || 'Condition'}</span>
57+
</div>*/}
5558
<div className="tw-flex tw-justify-between tw-mt-2">
5659
<div className="tw-text-sm tw-opacity-75">True</div>
5760
<div className="tw-text-sm tw-opacity-75">False</div>

vscode/webviews/workflow/components/nodes/LoopStart_Node.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type LoopStartNode = Omit<WorkflowNode, 'data'> & {
1414
data: BaseNodeData & {
1515
iterations: number
1616
loopVariable: string
17+
overrideIterations?: boolean
1718
}
1819
}
1920

@@ -33,6 +34,7 @@ export const LoopStartNode: React.FC<BaseNodeProps> = ({ data, selected }) => (
3334
}}
3435
>
3536
<Handle type="target" position={Position.Top} />
37+
<Handle type="target" position={Position.Left} id="iterations-override" style={{ top: '83%' }} />
3638
<div className="tw-flex tw-flex-col tw-gap-2">
3739
<div
3840
className="tw-text-center tw-py-1 tw-mb-2 tw-rounded-t-sm tw-font-bold"
@@ -44,7 +46,7 @@ export const LoopStartNode: React.FC<BaseNodeProps> = ({ data, selected }) => (
4446
selected,
4547
interrupted: data.interrupted,
4648
active: data.active,
47-
})}`,
49+
})})`,
4850
color: ' #1e1e1e',
4951
marginLeft: '-0.5rem',
5052
marginRight: '-0.5rem',
@@ -56,6 +58,7 @@ export const LoopStartNode: React.FC<BaseNodeProps> = ({ data, selected }) => (
5658
<div className="tw-flex tw-flex-col tw-justify-center">
5759
<span>{data.title}</span>
5860
<span className="tw-text-sm tw-opacity-70">Iterations: {data.iterations || 1}</span>
61+
<span className="tw-text-xs tw-opacity-50">← Iterations Override</span>
5962
</div>
6063
</div>
6164
<Handle type="source" position={Position.Bottom} />

vscode/webviews/workflow/components/nodes/Nodes.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ export const createNode = (node: Omit<WorkflowNodes, 'id'>): WorkflowNodes => {
141141
falsePathActive: false,
142142
},
143143
} as IfElseNode
144+
case NodeType.LOOP_START:
145+
return {
146+
...node,
147+
id,
148+
data: {
149+
...node.data,
150+
overrideIterations: false,
151+
},
152+
} as LoopStartNode
144153
default:
145154
return {
146155
...node,

0 commit comments

Comments
 (0)