feat(FN-2726): add bulk node override support in dashboard workflows

- Extend batch task update APIs and route handlers to accept nodeId overrides alongside model settings
- Update ListView bulk edit UX to load nodes and apply node overrides through batch updates
- Add node routing visibility improvements in task detail/settings UI and align related labels/styles
- Expand dashboard/API test coverage for node override batch updates and bulk edit behavior
This commit is contained in:
Fusion
2026-04-28 13:37:37 -07:00
committed by gsxdsm
parent 5414b8a0c7
commit 35694005ef
12 changed files with 445 additions and 163 deletions

View File

@@ -415,7 +415,16 @@ export function registerTaskWorkflowRoutes(ctx: ApiRoutesContext, deps: TaskWork
router.post("/tasks/batch-update-models", async (req, res) => {
try {
const { store: scopedStore } = await getProjectContext(req);
const { taskIds, modelProvider, modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId } = req.body;
const {
taskIds,
modelProvider,
modelId,
validatorModelProvider,
validatorModelId,
planningModelProvider,
planningModelId,
nodeId,
} = req.body;
// Validate taskIds
if (!Array.isArray(taskIds)) {
@@ -428,12 +437,17 @@ export function registerTaskWorkflowRoutes(ctx: ApiRoutesContext, deps: TaskWork
throw badRequest("taskIds must contain non-empty strings");
}
// Validate that at least one model field is being updated
// Validate that at least one model field or node override is being updated
const hasExecutorModel = modelProvider !== undefined || modelId !== undefined;
const hasValidatorModel = validatorModelProvider !== undefined || validatorModelId !== undefined;
const hasPlanningModel = planningModelProvider !== undefined || planningModelId !== undefined;
if (!hasExecutorModel && !hasValidatorModel && !hasPlanningModel) {
throw badRequest("At least one model field must be provided");
const hasNodeId = nodeId !== undefined;
if (!hasExecutorModel && !hasValidatorModel && !hasPlanningModel && !hasNodeId) {
throw badRequest("At least one model field or nodeId must be provided");
}
if (nodeId !== undefined && nodeId !== null && typeof nodeId !== "string") {
throw badRequest("nodeId must be a string, null, or undefined");
}
// Validate model field pairs (both provider and modelId must be provided together or neither)
@@ -486,7 +500,15 @@ export function registerTaskWorkflowRoutes(ctx: ApiRoutesContext, deps: TaskWork
}
// Build update payload (only include fields that were explicitly provided)
const updates: { modelProvider?: string | null; modelId?: string | null; validatorModelProvider?: string | null; validatorModelId?: string | null; planningModelProvider?: string | null; planningModelId?: string | null } = {};
const updates: {
modelProvider?: string | null;
modelId?: string | null;
validatorModelProvider?: string | null;
validatorModelId?: string | null;
planningModelProvider?: string | null;
planningModelId?: string | null;
nodeId?: string | null;
} = {};
if (validatedExecutor.provider !== undefined) {
updates.modelProvider = validatedExecutor.provider;
}
@@ -505,6 +527,9 @@ export function registerTaskWorkflowRoutes(ctx: ApiRoutesContext, deps: TaskWork
if (validatedPlanning.modelId !== undefined) {
updates.planningModelId = validatedPlanning.modelId;
}
if (nodeId !== undefined) {
updates.nodeId = nodeId;
}
// Update all tasks in parallel
const updatePromises = taskIds.map(async (taskId) => {