Skip to content

feat(ui): add guardrails support to project create/edit forms#25100

Merged
krrish-berri-2 merged 1 commit intoBerriAI:mainfrom
michelligabriele:fix/project-guardrails-ui
Apr 4, 2026
Merged

feat(ui): add guardrails support to project create/edit forms#25100
krrish-berri-2 merged 1 commit intoBerriAI:mainfrom
michelligabriele:fix/project-guardrails-ui

Conversation

@michelligabriele
Copy link
Copy Markdown
Collaborator

The backend already supports guardrails on projects (PR #25087), but the dashboard UI had no way to set them. This adds a guardrails multi-select field to the project form's Advanced Settings, following the same pattern used for team guardrails.

Relevant issues

Follows up on #25087 (backend support for project-level guardrails)

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature

Changes

  • ProjectBaseForm.tsx — Added guardrails?: string[] to ProjectFormValues interface, added getGuardrailsList fetch on mount, added Select mode="tags" field in Advanced Settings (between Block Project and Model-Specific Limits)
  • projectFormUtils.ts — Pass guardrails as a top-level field in API params (not nested in metadata), omit when empty
  • useCreateProject.ts / useUpdateProject.ts — Added guardrails?: string[] to param type interfaces
  • EditProjectModal.tsx — Extract guardrails from metadata.guardrails when editing, filter from user-facing KV metadata display, pre-populate form field
  • projectFormUtils.test.ts — 2 new tests: guardrails included when provided, omitted when empty
  • ProjectBaseForm.test.tsx — 1 new test: guardrails field renders in Advanced Settings + mock for getGuardrailsList

The backend already supports guardrails on projects (PR BerriAI#25087), but
the dashboard UI had no way to set them. This adds a guardrails
multi-select field to the project form's Advanced Settings, following
the same pattern used for team guardrails.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 3, 2026 7:52pm

Request Review

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented Apr 3, 2026

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing michelligabriele:fix/project-guardrails-ui (140a4a8) with main (d4a3a5e)

Open in CodSpeed

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 3, 2026

Greptile Summary

This PR adds a Guardrails multi-select field to the project create/edit forms, following the same UX pattern used for team guardrails. The UI wiring (form field, fetch on mount, type interfaces, filtering from KV metadata display) is generally well-structured, but there is a critical write-path mismatch that will silently prevent guardrails from ever being saved.

Key issues:

  • Guardrails silently dropped on save: projectFormUtils.ts sends guardrails as a top-level field in the API request body. The backend types NewProjectRequest and UpdateProjectRequest have no guardrails field, and LiteLLM_ProjectTable has no guardrails DB column (only a metadata JSON field). With Pydantic v2's default extra=\"ignore\", the field is silently discarded — no error, no data saved.
  • Read/write path inconsistency: EditProjectModal.tsx correctly reads guardrails from metadataObj.guardrails (consistent with how keys and teams store guardrails in the metadata JSON). The write path must match: guardrails should be nested inside the metadata object when calling the API, not sent as a separate top-level key.
  • ProjectResponse TypeScript type not updated: The ProjectResponse interface in useProjects.ts has no guardrails field.
  • Unit tests validate the broken write path: projectFormUtils.test.ts asserts result.guardrails — once the write path is corrected to nest guardrails inside metadata, these tests will need updating.

Confidence Score: 4/5

Not safe to merge as-is — the feature is silently broken: guardrails are never persisted due to a write-path mismatch with the backend.

There are two P1 issues: (1) the API field is silently dropped by the backend because NewProjectRequest/UpdateProjectRequest have no guardrails field and the DB schema has no guardrails column, and (2) a read/write path inconsistency means even if the backend is updated, the edit form pre-population would still break unless the write also uses metadata. The unit tests validate the broken behavior and would need to be updated alongside the fix. The UI rendering, type interfaces, and list-fetch logic are all correct.

projectFormUtils.ts (write path must nest guardrails in metadata) and projectFormUtils.test.ts (tests assert the current broken behavior and will need updating).

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/components/Projects/ProjectModals/projectFormUtils.ts Sends guardrails as a top-level field, but the backend ProjectRequest types have no guardrails field and the DB schema has no guardrails column — the field will be silently dropped. Write path must nest guardrails inside metadata to match both the read path and the backend's metadata-only storage.
ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx Read path correctly reads guardrails from metadataObj.guardrails and excludes them from the user-facing KV list, consistent with metadata storage. Pre-population will break if the backend stores guardrails at the top level instead of metadata.
ui/litellm-dashboard/src/components/Projects/ProjectModals/ProjectBaseForm.tsx Correctly fetches guardrails list on mount and renders a mode="tags" Select field in Advanced Settings, matching the team form pattern. The useEffect only runs when accessToken changes, which is correct.
ui/litellm-dashboard/src/components/Projects/ProjectModals/projectFormUtils.test.ts Two new tests for guardrails: one verifies inclusion when provided, one verifies omission when empty. Tests are correct against the current implementation but will need updating once the write path is fixed to nest guardrails in metadata.
ui/litellm-dashboard/src/components/Projects/ProjectModals/ProjectBaseForm.test.tsx Adds a test verifying the Guardrails field renders in Advanced Settings, and mocks getGuardrailsList. Mock returns { guardrails: [] } which matches the expected API shape.
ui/litellm-dashboard/src/app/(dashboard)/hooks/projects/useCreateProject.ts Adds guardrails?: string[] to ProjectCreateParams interface — correct interface update. The field will have no effect until the write path mismatch is fixed.
ui/litellm-dashboard/src/app/(dashboard)/hooks/projects/useUpdateProject.ts Adds guardrails?: string[] to ProjectUpdateParams interface — correct interface update matching the create hook.

Sequence Diagram

sequenceDiagram
    participant UI as ProjectBaseForm
    participant Utils as projectFormUtils
    participant API as Backend /project/new|update
    participant DB as LiteLLM_ProjectTable

    UI->>Utils: buildProjectApiParams(values)
    Note over Utils: guardrails spread at top-level ❌ should be nested in metadata ✅

    Utils-->>UI: { guardrails: [...], ...rest }

    UI->>API: POST /project/new { guardrails: [...] }
    Note over API: NewProjectRequest has no guardrails field Pydantic extra=ignore → silently drops it

    API->>DB: INSERT (metadata={}, no guardrails)
    DB-->>API: saved project row
    API-->>UI: ProjectResponse (no guardrails)

    UI->>UI: Edit modal opens
    Note over UI: Reads metadataObj.guardrails → always [] Form never pre-populated
Loading

Comments Outside Diff (1)

  1. ui/litellm-dashboard/src/app/(dashboard)/hooks/projects/useProjects.ts, line 25-44 (link)

    P2 ProjectResponse type missing guardrails field

    The ProjectResponse TypeScript interface is missing a guardrails field. If the backend ever returns guardrails at the project object's top level (e.g. after PR feat(proxy): add project-level guardrails support #25087 lands), TypeScript won't be aware of it and project.guardrails access would be typed as any / cause a compile error.

    Even if guardrails remain in metadata, adding it here as an explicit optional field documents the contract:

Reviews (1): Last reviewed commit: "feat(ui): add guardrails support to proj..." | Re-trigger Greptile

Comment on lines +28 to +30
...(values.guardrails && values.guardrails.length > 0 && {
guardrails: values.guardrails,
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Guardrails sent as top-level field but backend has no support for it

guardrails is sent as a top-level field in the API request body, but the backend request types (NewProjectRequest and UpdateProjectRequest in litellm/proxy/_types.py) have no guardrails field and LiteLLM_ProjectTable has no guardrails column in schema.prisma. Because LiteLLMPydanticObjectBase uses Pydantic v2's default extra="ignore", this field will be silently dropped — guardrails will never actually be saved.

The only storage location available for project-level guardrails is the metadata JSON column (same pattern as keys and teams, which store guardrails at metadata.guardrails). The read path in EditProjectModal.tsx already reads from metadataObj.guardrails, confirming the intended design. The write path needs to match:

// Instead of spreading guardrails at the top level, merge them into the metadata object
const metadata: Record<string, unknown> = {};
for (const entry of values.metadata ?? []) {
  if (entry.key) metadata[entry.key] = entry.value;
}
if (values.guardrails && values.guardrails.length > 0) {
  metadata.guardrails = values.guardrails;
}

return {
  // ...
  ...(Object.keys(metadata).length > 0 && { metadata }),
};

This aligns the write path with the read path in EditProjectModal.tsx (which reads metadataObj.guardrails) and matches the existing pattern in the codebase (e.g. key_edit_view.tsx sends guardrails inside metadata and reads them from keyData.metadata?.guardrails).

Comment on lines +36 to +38
const guardrails = (Array.isArray(metadataObj.guardrails)
? metadataObj.guardrails
: []) as string[];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Read/write mismatch will cause pre-population to fail when guardrails are stored top-level

The edit form reads guardrails from metadataObj.guardrails (inside the metadata JSON), but projectFormUtils.ts sends them as a top-level API field. If the backend is updated to store guardrails at the top-level column (not in metadata), the backend response would return project.guardrails — not inside project.metadata.guardrails — and this read will always return an empty array, breaking pre-population on edit.

To stay consistent: if the intended storage is metadata (which the existing read path already assumes), the write path in projectFormUtils.ts must also nest guardrails inside metadata. Alternatively, if guardrails will be a top-level project column, update the read to use project.guardrails and update the ProjectResponse TypeScript interface accordingly.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@krrish-berri-2 krrish-berri-2 self-requested a review April 4, 2026 03:40
@krrish-berri-2 krrish-berri-2 merged commit 4c1ef42 into BerriAI:main Apr 4, 2026
59 of 62 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants