fix(ui): normalize persisted cache controls

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Krrish Dholakia 2026-07-14 13:25:54 +00:00
parent e1e233f88a
commit 6fc1718a4a
2 changed files with 44 additions and 11 deletions

View file

@ -26,6 +26,18 @@ describe("DefaultLitellmParamsSection", () => {
expect(screen.getByTestId("cache-control-location-select-0")).toBeInTheDocument();
});
it("should normalize nullable cache control fields from persisted settings", () => {
render(
<DefaultLitellmParamsSection
value={{ cache_control_injection_points: [{ location: "message", role: null, index: null }] }}
onChange={vi.fn()}
/>,
);
expect(screen.getByTestId("cache-control-location-select-0")).toBeInTheDocument();
expect(screen.getByTestId("cache-control-index-input-0")).toHaveValue("");
});
it("should preserve unsupported cache control points in the JSON editor", () => {
render(
<DefaultLitellmParamsSection

View file

@ -13,6 +13,7 @@ interface DefaultLitellmParamsSectionProps {
type ParsedDefaultParams = { status: "valid"; value: Record<string, unknown> } | { status: "invalid"; message: string };
const CACHE_CONTROL_ROLES = ["user", "system", "assistant"] as const;
type CacheControlRole = (typeof CACHE_CONTROL_ROLES)[number];
const parseDefaultParams = (text: string): ParsedDefaultParams => {
try {
@ -26,27 +27,47 @@ const parseDefaultParams = (text: string): ParsedDefaultParams => {
}
};
const isCacheControlInjectionPoint = (value: unknown): value is CacheControlInjectionPoint => {
const isCacheControlRole = (value: unknown): value is CacheControlRole =>
CACHE_CONTROL_ROLES.some((validRole) => validRole === value);
const parseCacheControlInjectionPoint = (value: unknown): CacheControlInjectionPoint | undefined => {
if (typeof value !== "object" || value === null) {
return false;
return undefined;
}
if (!("location" in value) || value.location !== "message") {
return false;
return undefined;
}
const role = "role" in value ? value.role : undefined;
const index = "index" in value ? value.index : undefined;
const hasValidRole = role === undefined || CACHE_CONTROL_ROLES.some((validRole) => validRole === role);
const hasValidIndex = index === undefined || (typeof index === "number" && Number.isInteger(index));
return hasValidRole && hasValidIndex;
const hasInvalidRole = role !== undefined && role !== null && !isCacheControlRole(role);
if (hasInvalidRole) {
return undefined;
}
const hasIndex = index !== undefined && index !== null;
const hasInvalidIndex = hasIndex && (typeof index !== "number" || !Number.isInteger(index));
if (hasInvalidIndex) {
return undefined;
}
return {
location: "message",
...(isCacheControlRole(role) ? { role } : {}),
...(typeof index === "number" ? { index } : {}),
};
};
const parseCacheControlInjectionPoints = (value: unknown): CacheControlInjectionPoint[] | undefined => {
if (!Array.isArray(value)) {
return undefined;
}
const points = value.map(parseCacheControlInjectionPoint);
return points.every((point) => point !== undefined)
? points.filter((point): point is CacheControlInjectionPoint => point !== undefined)
: undefined;
};
const DefaultLitellmParamsSection: React.FC<DefaultLitellmParamsSectionProps> = ({ value, onChange }) => {
const cacheControlInjectionPoints = React.useMemo(
() =>
Array.isArray(value.cache_control_injection_points) &&
value.cache_control_injection_points.every(isCacheControlInjectionPoint)
? value.cache_control_injection_points
: undefined,
() => parseCacheControlInjectionPoints(value.cache_control_injection_points),
[value.cache_control_injection_points],
);
const otherParams = React.useMemo(