🛟 fix: Add Section-Level Reset to Escape Stuck Partial Overrides#48
🛟 fix: Add Section-Level Reset to Escape Stuck Partial Overrides#48dustinhealy wants to merge 1 commit into
Conversation
Some sections, like modelSpecs, have cross-field invariants enforced by zod refinements (e.g. enforce: true requires list.length > 0). When a user toggles enforce on, then later deletes the last list entry, the admin panel cannot save: the path-granular override stream lacks an atomic operation to drop both the toggle and the (now-empty) list at once, so the partial state is rejected by the schema and the section gets stuck. Adds a "Reset section" affordance to every ConfigSection header that clears all overrides under the section path in a single save. On dispatch the handler drops any pending sub-edits under the prefix and writes a single undefined at the section path; on save this routes through resetBaseConfigFieldFn to a $unset overrides.<sectionPath> on the LibreChat side, leaving the section back at its librechat.yaml defaults. The button only appears when the user has edit permission and the section has at least one persisted DB override or pending edit, so it stays out of the way during normal editing. Fixes AI-896.
|
Closing in favor of an upstream fix that addresses the root cause rather than working around it. The stuck-state happens because Upstream PR: danny-avila/LibreChat#13036 -- relaxes the schema to The "Reset section" affordance this PR introduced is no longer load-bearing for the bug. Keeping it would just add a destructive footgun visible during normal editing for a workflow that the upstream fix removes entirely. |
Summary
Some config sections have cross-field invariants enforced by zod refinements (e.g.
modelSpecsrequireslist.length > 0whenenforce: true). The admin panel's path-granular override stream has no atomic op to drop two fields at once, so a user who togglesenforceon and later deletes the last list entry ends up with a save the schema rejects and no per-field reset can unstick it.This PR adds a "Reset section" affordance on every
ConfigSectionheader that atomically clears every override under the section path in a single save. The handler drops pending sub-edits under the prefix and writes a singleundefinedat the section path; on save this routes throughresetBaseConfigFieldFn→$unset overrides.<sectionPath>server-side, leaving the section at itslibrechat.yamldefaults.The button only renders when the user has edit permission and the section has at least one persisted DB override or pending edit, so it stays out of the way during normal editing.
Changes
ConfigPage.tsx--handleResetSectioncallback that strips sub-edits fromeditedValuesand queuesundefinedat the section pathConfigTabContent.tsx-- wiresonResetSectionthrough to inline sections; for accordion sections, renders anAccordionSectionHeadercarrying the reset button alongside the titleConfigSection.tsx-- addsResetSectionButtonrendered in both the inline and collapsible header variantstypes/config-ui.ts--sectionPath,onResetSection,hasOverridesprops onConfigSectionProps;onResetSectiononConfigTabContentPropslocales/en/translation.json--com_config_reset_section,com_config_section_reset_confirm,com_a11y_reset_sectionChange Type
Testing
bun run devagainst a LibreChat backend.modelSpecssection, toggleenforce: true, add one entry, save. Then delete that entry and try to save, the schema rejects the save and there's no way out via per-field reset.modelSpecsheader, confirm the dialog, then save. The section should revert to itslibrechat.yamldefaults and the form unblocks.previewMode/fieldsDisabled.Checklist