[1/3] Simplify ModelConfiguration by removing value and onModelChange, providerOptions, onProviderOptionsChange, fallbackModels, onFallbackModelsChange, allowedProviders, onAllowedProvidersChange in favor of direct form state management#3028
Conversation
🦋 Changeset detectedLatest commit: 574fd19 The changes in this PR will be included in the next version bump. This PR includes changesets to release 10 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
@pullfrog update PR description based on changes in this PR |
|
Updated the PR description based on the diff. The description covers:
|
|
@claude add a changeset based on changes in this PR |
|
Claude finished @dimaMachina's task in 1m 0s —— View job Adding changeset
Added changeset
Pushed in commit ea2829b. |
Co-authored-by: Dimitri POSTOLOV <[email protected]>
|
TL;DR — Refactors Key changes
Summary | 15 files | 27 commits | base:
|
There was a problem hiding this comment.
Good refactor overall — moving from callback props to control/name pairs eliminates a large amount of boilerplate and correctly leverages useController inside ModelConfiguration. One runtime-breaking bug in CollapsibleSettings needs to be fixed before merge.
Claude Opus | 𝕏
Ito Test Report ❌15 test cases ran. 1 failed, 14 passed. Unified verification ran 15 test cases with 14 passing and 1 failing, indicating generally stable project and agent model configuration behavior with a single significant regression. The confirmed High-severity bug is that creating a project fails when default-model provider-options are left empty because UI validation rejects transformed empty values before the create mutation (despite backend acceptance), while all other checks passed across edit/persistence flows, JSON and whitespace validation, collapsible error recovery, rapid-submit/toggle resilience, malicious-input safety, navigation-state handling, and read-only permission boundaries. ❌ Failed (1)
|
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Delta Review
This is a re-review scoped to the 2 commits since the previous automated review (89a49cfa2 → 68f97df25).
Changes reviewed:
-
68f97df25 fix cypress— Fixed a validation schema bug where emptyproviderOptionswould fail validation:- Changed from
StringToJsonSchema.pipe(z.record()).optional()toStringToJsonSchema.pipe(z.record().optional()) - The
.optional()moved inside the pipe allowsundefinedvalues to pass through the record schema correctly - This fix addresses the Ito-reported bug where project creation failed with empty provider options
- Changed from
-
4f79027ae Merge main— Merge commit bringing in unrelated changes from main (feedback, scheduled triggers, etc.)
Assessment:
✅ Ito bug addressed — The commit 68f97df25 correctly fixes the validation flow where StringToJsonSchema transforms empty strings to undefined, which then needs to be accepted by the piped record schema. Moving .optional() inside the pipe ensures z.record().optional() accepts undefined as a valid value.
✅ Consistent with agent form — The same pattern change was applied to agents-manage-ui/src/components/agent/form/validation.ts, removing the redundant outer .optional() since StringToJsonSchema already handles the optionality.
No new issues identified in the delta.
✅ APPROVE
Summary: The delta contains a targeted fix for the Ito-reported validation bug plus a merge from main. The validation schema fix correctly moves .optional() inside the pipe to allow undefined values from StringToJsonSchema to pass through. The overall refactor is complete and ready to ship! 🚀
Reviewers (0)
No sub-reviewers dispatched — the delta was a focused bug fix with clear intent. The previous full reviews already covered the main refactoring work.
Ito Test Report ❌18 test cases ran. 1 failed, 17 passed. Overall, the unified run passed 17 of 18 test cases, confirming that project/agent/sub-agent model configuration flows were stable across create/edit/save/reload, duplicate-submit protection, invalid ID and malformed JSON blocking, inheritance-to-explicit transitions, empty-field normalization, rapid toggles/navigation, and concurrent stale-tab saves with deterministic last-write-wins behavior and no corruption. The single confirmed issue was a medium-severity UX/validation defect in Project Settings where collapsed Execution Limits does not reliably auto-open to reveal nested errors on submit, making invalid values easy to miss even though validation logic exists. ❌ Failed (1)
🟠 Collapsible auto-open on nested validation errors
Relevant code:
const { errors } = useFormState({ control });
const hasStopWhenErrors = !!(errors.stopWhen?.transferCountIs || errors.stopWhen?.stepCountIs);
// Auto-open the collapsible when there are errors in the stopWhen section
useEffect(() => {
if (hasStopWhenErrors) {
setIsOpen(true);
}
}, [hasStopWhenErrors]);
<Collapsible
className={cn('border rounded-md bg-muted/30 dark:bg-muted/20', className)}
{...props}
>
<CollapsibleTrigger asChild>
<Button
variant="ghost"
size="sm"
className="flex items-center justify-start gap-1.5 p-0 h-auto font-normal text-xs text-foreground/80 dark:text-foreground/90 hover:text-foreground hover:!bg-transparent transition-colors group w-full py-2 px-4"
>
<ChevronRight className="h-3.5 w-3.5 transition-transform duration-200 group-data-[state=open]:rotate-90" />
{title}
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="space-y-8 mt-4 data-[state=closed]:animate-[collapsible-up_200ms_ease-out] data-[state=open]:animate-[collapsible-down_200ms_ease-out] overflow-hidden px-4 pb-6">
function CollapsibleContent({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return <CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />;
}✅ Passed (17)Commit: Tell us how we did: Give Ito Feedback |
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Delta Review
This is a re-review scoped to the 6 commits since the previous automated review (68f97df25 → 9a815820a).
Commits reviewed:
9a815820a— Merge branch 'main' into rm-onProviderOptionsChange-onModelChange259797c83— FixuseFormStateon agent page was not updated with enabled React Compiler070b9acf2— Fix migration timestamp (unrelated)76f5e5f77— Version Packages (unrelated)6f2619d8d— Fix/execution route openapi paths (unrelated)3735393a3— Stricter project ID validation (unrelated)
Assessment:
✅ React Compiler fix improves this PR — The 259797c83 commit addresses the Ito-reported issue where collapsed sections didn't reliably auto-open to reveal nested validation errors. The fix:
- Simplifies error detection in
project-models-section.tsxfrom checking 6 individual fields to!!errors.models - Applies the same pattern to
project-stopwhen-section.tsxwith!!errors.stopWhen - Isolates proxy-backed error reads in
use-grouped-agent-errors.tsusing'use no memo'directive to work correctly with React Compiler
✅ Pattern consistency maintained — The simplified error checking (!!errors.models) correctly detects any nested error in the models section, which is the intended behavior for auto-opening collapsibles.
✅ No regressions introduced — The merge from main brings unrelated changes (version bumps, project ID validation, execution route fixes) that don't affect this PR's functionality.
Ito test status: The second Ito run (17/18 passed) reported the collapsible auto-open issue as the only failure. The React Compiler fix in this delta directly addresses that by ensuring useFormState reactivity works correctly — the simplified !!errors.models check should now properly trigger the useEffect that calls setIsOpen(true).
No new issues identified in the delta.
✅ APPROVE
Summary: The delta contains a merge from main plus a targeted fix for React Compiler compatibility that improves error detection in the project form sections. The original refactor (8 callback props → 2 props via useController) is complete and well-tested. All previous review feedback has been addressed. Ship it! 🚀
Reviewers (0)
No sub-reviewers dispatched — the delta was a focused bug fix and merge from main. The previous full reviews already covered the main refactoring work comprehensively.
This comment was marked as off-topic.
This comment was marked as off-topic.
Ito Test Report ❌14 test cases ran. 2 failed, 2 additional findings, 10 passed. Across 14 test cases, 10 passed and 4 failed, with strong coverage confirming expected behavior for project and agent model configuration workflows including provider-options save/validation/blank normalization, execution-limit error auto-open and recovery, gateway capability-gated allowed-provider/fallback interactions, mobile usability, metadata round-trip persistence in the project edit dialog, and safe handling of malicious JSON without prototype pollution. The key confirmed defects were duplicate create/update requests under rapid multi-click submit (causing repeated toasts and potential duplicate/conflicting operations), loss of custom base model selection after closing/reopening Agent Settings, and loss of node-scoped sub-agent model settings when switching sidepane selection, with the latter two linked to unregister-on-unmount/remount form-state behavior and carrying medium/high impact. ❌ Failed (2)🟠 Custom base model IDs are not retained after closing Agent Settings
Relevant code:
const { field: modelField } = useController({
control,
name: `${name}.model` as FieldPath<TFieldValues>,
shouldUnregister: true,
});
{showSidePane && (
<>
<ResizableHandle withHandle />
<ResizablePanel minSize={30} id="side-pane" order={2}>
<SidePane
selectedNodeId={selectedNode?.id ?? null}
selectedEdgeId={selectedEdge?.id ?? null}
onClose={closeSidePane}
backToAgent={backToAgent}
disabled={isCopilotStreaming || !canEdit}
/>
</ResizablePanel>
</>
)}
if (e.key === 'Enter' && customModelInput.trim()) {
const prefix =
showCustomInput === 'openrouter'
? 'openrouter/'
: showCustomInput === 'gateway'
? 'gateway/'
: showCustomInput === 'nim'
? 'nim/'
: 'custom/';
onValueChange(`${prefix}${customModelInput.trim()}`);
setShowCustomInput(null);
setCustomModelInput('');
setOpen(false);
}
|
value and onModelChange, providerOptions, onProviderOptionsChange, fallbackModels, onFallbackModelsChange, allowedProviders, onAllowedProvidersChange in favor of direct form state managementModelConfiguration by removing value and onModelChange, providerOptions, onProviderOptionsChange, fallbackModels, onFallbackModelsChange, allowedProviders, onAllowedProvidersChange in favor of direct form state management
ModelConfiguration by removing value and onModelChange, providerOptions, onProviderOptionsChange, fallbackModels, onFallbackModelsChange, allowedProviders, onAllowedProvidersChange in favor of direct form state managementModelConfiguration by removing value and onModelChange, providerOptions, onProviderOptionsChange, fallbackModels, onFallbackModelsChange, allowedProviders, onAllowedProvidersChange in favor of direct form state management
ModelConfiguration by removing value and onModelChange, providerOptions, onProviderOptionsChange, fallbackModels, onFallbackModelsChange, allowedProviders, onAllowedProvidersChange in favor of direct form state managementModelConfiguration by removing value and onModelChange, providerOptions, onProviderOptionsChange, fallbackModels, onFallbackModelsChange, allowedProviders, onAllowedProvidersChange in favor of direct form state management
|
This pull request has been automatically marked as stale because it has not had recent activity. If this PR is still relevant:
Thank you for your contributions! |
Summary
Refactors
ModelConfigurationfrom a fully-controlled component (value + onChange callbacks) into a form-aware component that owns its ownreact-hook-formfield bindings viauseController. Consumers now passcontroland anamepath instead of manually wiring 6+ onChange handlers and individual field values.As a side effect, the project form's validation schema is split into proper input vs output types, and
providerOptionsJSON string-to-object parsing is pushed into the Zod schema layer.Changes
ModelConfigurationAPI overhaul (shared/model-configuration.tsx)TFieldValuesandTTransformedValuesfor reuse across agent and project formsuseControllerfor.model,.providerOptions,.fallbackModels,.allowedProviderssub-fieldsundefinednormalization for fallback models and allowed providersBefore: 8 props —
value,providerOptions,fallbackModels,allowedProviders,onModelChange,onProviderOptionsChange,onFallbackModelsChange,onAllowedProvidersChangeAfter: 2 props —
control,nameConsumer simplification
ModelSectionprops changed from{ models, updatePath }to{ control, basePath }— derives models viauseWatchSubAgentNodeEditorremovedFormField/FormItem/FormMessagewrapper aroundModelSectionBaseModelSection,StructuredOutputModelSection,SummarizerModelSectioninproject-models-section.tsxdropped per-fielduseControllercalls andFormFieldWrapperrender-prop pattern in favor of a single<ModelConfiguration control={...} name="models.base" />Project form typing split (
projects/form/validation.ts,project-form.tsx)ProjectFormDatasplit intoProjectFormInputValues(z.input) andProjectFormData(z.output)useFormtyped asuseForm<ProjectFormInputValues, unknown, ProjectFormData>providerOptionsschema changed fromz.record().optional().nullable()toStringToJsonSchema.pipe(z.record()).optional()— JSON parsing moved from imperative handlers into the schema pipelineprojectModelsSchemagained a.transform()that stripsstructuredOutput/summarizerwhen they have no.modelsetStringToJsonSchemaextraction (lib/validation.ts)agent/form/validation.tstolib/validation.tsfor reuse across both agent and project formsCollapsibleSettingsgeneralized (collapsible-settings.tsx)Omit<ComponentProps<typeof Collapsible>, 'title'>, forwarding all Collapsible propsproject-models-section.tsxandproject-stopwhen-section.tsxreplaced inline collapsible boilerplate with<CollapsibleSettings>ModelSelectortype tightening (model-selector.tsx)selectedModelandinheritedModelvalueproperty from return objectsonCloseto use optional chainingClaude Opus| 𝕏