fix(ui): leave unset callback select params out of the save payload (#44213)

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot] 2026-10-02 11:17:51 -07:00 • committed by GitHub
parent e5873adbc4
commit 4c648f181a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 2 deletions

View file

@ -439,6 +439,40 @@ describe("Settings", () => {
});
});
it("should post the saved s3_v2 folder partitioning when unchanged", async () => {
mockS3Callback({ S3_LOG_PROMPTS_ONLY: null, S3_PARTITION_GRANULARITY: "hour" }, "s3_v2");
const user = await openS3EditModal("s3_v2");
const dialog = screen.getByRole("dialog");
const partitioning = await within(dialog).findByRole("combobox", { name: "Folder Partitioning" });
expect(partitioning).toHaveTextContent("hour");
await user.click(await within(dialog).findByRole("button", { name: "Save Changes" }));
await waitFor(() => {
expect(vi.mocked(setCallbacksCall)).toHaveBeenCalledTimes(1);
});
const [, payload] = vi.mocked(setCallbacksCall).mock.calls[0];
expect(payload.environment_variables.s3_partition_granularity).toBe("hour");
});
it("should leave an unset s3_v2 folder partitioning out of the save payload", async () => {
mockS3Callback({ S3_LOG_PROMPTS_ONLY: null, S3_PARTITION_GRANULARITY: null }, "s3_v2");
const user = await openS3EditModal("s3_v2");
const dialog = screen.getByRole("dialog");
const partitioning = await within(dialog).findByRole("combobox", { name: "Folder Partitioning" });
expect(partitioning).toHaveTextContent("Select folder partitioning");
expect(partitioning).not.toHaveTextContent(/day|hour/i);
await user.click(await within(dialog).findByRole("button", { name: "Save Changes" }));
await waitFor(() => {
expect(vi.mocked(setCallbacksCall)).toHaveBeenCalledTimes(1);
});
const [, payload] = vi.mocked(setCallbacksCall).mock.calls[0];
expect(Object.keys(payload.environment_variables)).not.toContain("s3_partition_granularity");
expect(payload.environment_variables.callback).toBe("s3_v2");
});
it("should not offer folder partitioning for the legacy s3 callback, which cannot honour it", async () => {
mockS3Callback({ S3_LOG_PROMPTS_ONLY: null, S3_PARTITION_GRANULARITY: null });
const user = await openS3EditModal();

View file

@ -285,7 +285,7 @@ const getDynamicParamsForCallback = (
// Shared helper function to build callback payload
const buildCallbackPayload = (formValues: Record<string, any>, callbackName: string) => {
return {
environment_variables: formValues,
environment_variables: Object.fromEntries(Object.entries(formValues).filter(([, value]) => value !== undefined)),
litellm_settings: {
success_callback: [callbackName],
},
@ -348,8 +348,15 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
);
const fieldNameFor = (variable: string) =>
params.find((param) => param.toUpperCase() === variable.toUpperCase()) ?? variable;
const callbackConfig = findCallbackConfig(callbackConfigs, selectedEditCallback.name);
const normalized = Object.fromEntries(
Object.entries(selectedEditCallback.variables || {}).map(([k, v]) => [fieldNameFor(k), v ?? ""]),
Object.entries(selectedEditCallback.variables || {}).flatMap(([key, value]) => {
const fieldName = fieldNameFor(key);
if (value == null && callbackConfig?.dynamic_params?.[fieldName]?.type === "select") {
return [];
}
return [[fieldName, value ?? ""]];
}),
);
editForm.reset({
...normalized,