fix(ui): gate guardrails and credential name on isFieldTouched too

Greptile flagged that guardrails and litellm_credential_name were added to
the PATCH on every save regardless of whether the user changed them (an empty
guardrails array is truthy, and the credential name was sent whenever
non-empty), which both contradicts the send-only-what-changed design and lets
an unrelated save re-assert a credential name that was changed elsewhere since
the form was opened. Both are now gated on isFieldTouched like the other
scalars; a touched-but-empty guardrails still sends [] to clear, and the
credential name typed into the LiteLLM Params JSON is still stripped so the
selector stays the source of truth
This commit is contained in:
ryan-crabbe-berri 2026-07-01 22:17:06 -07:00
parent 30acb09600
commit 807dde81d6
2 changed files with 7 additions and 6 deletions

View file

@ -544,7 +544,7 @@ describe("ModelInfoView", () => {
});
});
it("should keep selector credential and ignore litellm_credential_name from LiteLLM Params json", async () => {
it("ignores litellm_credential_name typed into the LiteLLM Params json and never re-sends the untouched selector credential", async () => {
const user = userEvent.setup();
render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} />, { wrapper });
@ -575,8 +575,9 @@ describe("ModelInfoView", () => {
});
const updatePayload = mockModelPatchUpdateCall.mock.calls[0][1];
expect(updatePayload.litellm_params.litellm_credential_name).toBe("selected-credential");
expect(updatePayload.litellm_params.timeout).toBe(42);
expect(updatePayload.litellm_params.litellm_credential_name).not.toBe("from-json");
expect(updatePayload.litellm_params).not.toHaveProperty("litellm_credential_name");
});
it("should not include vector_store_ids in update payload when model has none", async () => {
@ -790,6 +791,8 @@ describe("ModelInfoView", () => {
expect(updatePayload.litellm_params).not.toHaveProperty("organization");
expect(updatePayload.litellm_params).not.toHaveProperty("custom_llm_provider");
expect(updatePayload.litellm_params).not.toHaveProperty("stream_timeout");
expect(updatePayload.litellm_params).not.toHaveProperty("guardrails");
expect(updatePayload.litellm_params).not.toHaveProperty("litellm_credential_name");
});
it("never seeds or re-sends a masked secret nested inside an object (regression: nested-secret corruption)", async () => {

View file

@ -471,12 +471,10 @@ export default function ModelInfoView({
}
}
if (values.litellm_credential_name) {
if (form.isFieldTouched("litellm_credential_name") && values.litellm_credential_name) {
updatedLitellmParams.litellm_credential_name = values.litellm_credential_name;
} else {
delete updatedLitellmParams.litellm_credential_name;
}
if (values.guardrails) {
if (form.isFieldTouched("guardrails")) {
updatedLitellmParams.guardrails = values.guardrails;
}
if (values.vector_store_ids?.length > 0) {