Skip to content

Commit 3d0807b

Browse files
committed
feat(controller): resolve engine and model upfront for immediate UI display
Move engine and model resolution before controller execution to ensure UI shows this information immediately rather than waiting for the first turn. This matches the behavior of step agents and provides better user experience.
1 parent c2a82f4 commit 3d0807b

1 file changed

Lines changed: 34 additions & 14 deletions

File tree

src/workflows/controller/view.ts

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,38 @@ export async function runControllerView(
128128
return { ran: false };
129129
}
130130

131-
// Initialize controller agent
132-
// Engine/model resolution happens inside executeAgent (single source of truth)
133-
// Only pass overrides from workflow definition.options
131+
// Resolve engine and model upfront (before execution) like step agents do
132+
// This ensures UI shows engine/model immediately, not after first turn
133+
debug('[ControllerView] Resolving engine and model upfront');
134+
const { selectEngine } = await import('../step/engine.js');
135+
const { registry } = await import('../../infra/engines/index.js');
136+
137+
// Create a step-like object for selectEngine
138+
const stepLike = {
139+
engine: definition.options?.engine,
140+
agentId: controller.id,
141+
agentName: (controller.name as string | undefined) ?? controller.id,
142+
};
143+
144+
const resolvedEngine = await selectEngine(stepLike, emitter, controller.id);
145+
debug('[ControllerView] Resolved engine: %s', resolvedEngine);
146+
147+
// Resolve model from definition override or engine default
148+
const engineModule = registry.get(resolvedEngine);
149+
const resolvedModel = definition.options?.model ?? engineModule?.metadata.defaultModel;
150+
debug('[ControllerView] Resolved model: %s', resolvedModel);
151+
152+
// Emit controller info BEFORE execution so UI shows it immediately
153+
const controllerName = (controller.name as string | undefined) ?? controller.id;
154+
emitter.setControllerInfo(
155+
controller.id,
156+
controllerName,
157+
resolvedEngine,
158+
resolvedModel
159+
);
160+
debug('[ControllerView] Emitted controller info upfront: engine=%s, model=%s', resolvedEngine, resolvedModel);
161+
162+
// Initialize controller agent with resolved engine/model
134163
debug('[ControllerView] Initializing controller agent');
135164
let controllerMonitoringId: number | undefined;
136165

@@ -146,23 +175,14 @@ export async function runControllerView(
146175
controllerMonitoringId = monitoringId;
147176
emitter.registerControllerMonitoring(monitoringId);
148177
},
149-
engineOverride: definition.options?.engine,
150-
modelOverride: definition.options?.model,
178+
engineOverride: resolvedEngine,
179+
modelOverride: resolvedModel,
151180
}
152181
);
153182

154183
debug('[ControllerView] Controller initialized: sessionId=%s, monitoringId=%d, engine=%s, model=%s',
155184
config.sessionId, controllerMonitoringId, config.engine, config.model);
156185

157-
// Emit controller info for UI (use resolved values from initControllerAgent)
158-
const controllerName = (controller.name as string | undefined) ?? controller.id;
159-
emitter.setControllerInfo(
160-
controller.id,
161-
controllerName,
162-
config.engine,
163-
config.model
164-
);
165-
166186
// Set up conversational loop
167187
// 1. Set input state active so UI shows input box
168188
// 2. Wait for user input

0 commit comments

Comments
 (0)