Skip to content

feat(ui): add block/unblock key action to key info page - #34116

Merged
ryan-crabbe-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_ui_key_block_actions
Jul 21, 2026
Merged

feat(ui): add block/unblock key action to key info page#34116
ryan-crabbe-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_ui_key_block_actions

Conversation

@ryan-crabbe-berri

Copy link
Copy Markdown
Contributor

Relevant issues

Linear ticket

Pre-Submission checklist

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

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

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

Screenshots / Proof of Fix

Verified end to end at commit 75d708c against a live proxy on localhost:4000 with the dashboard dev server on localhost:3000. UI flow, with screenshots to follow in a comment:

  1. Create a throwaway key: curl -X POST http://localhost:4000/key/generate -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' -d '{"key_alias": "block-ui-test-key", "models": []}'
  2. Open http://localhost:3000/?login=success&page=api-keys, click the key to open its info page
  3. Click the "..." button next to Regenerate Key; the dropdown shows Block Key, Reset Spend, and Delete Key
  4. Click Block Key, confirm in the modal; a "Key blocked" toast appears and a red Blocked tag shows next to the key alias
  5. Prove the key is dead against the live proxy:
$ curl -s http://localhost:4000/v1/chat/completions \
    -H 'Authorization: Bearer sk-iPeheIaL8WT7KToTjKT7pA' -H 'Content-Type: application/json' \
    -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'
{"error":{"message":"Authentication Error, Key is blocked. Update via `/key/unblock` if you're an admin.","type":"auth_error","param":"None","code":"401"}}
  1. Reopen the dropdown; the item now reads Unblock Key. Click it and confirm; a "Key unblocked" toast appears, the Blocked tag disappears, and the same curl returns 200 again
  2. Delete Key from the same dropdown still walks through the existing type-the-alias confirmation modal and removes the key

Type

🆕 New Feature

Changes

The proxy has had /key/block and /key/unblock endpoints for a long time, but the Admin UI never exposed them; the keys table can render a Blocked status (e.g., for SCIM-deactivated users) yet there was no way to set it. This PR adds the action to the key info page

The Reset Spend and Delete Key buttons in the key info header are replaced by a "..." overflow dropdown next to Regenerate Key holding the three destructive actions: Block Key (or Unblock Key when the key is already blocked), Reset Spend, and Delete Key. Blocking asks for confirmation and explains that requests will be rejected with a 401 until unblocked; unblocking asks for a lighter confirmation. While blocked, a red Blocked tag renders next to the key alias

The new useSetKeyBlockedState hook posts to /key/block or /key/unblock through the shared apiClient (raw fetch is lint-banned outside src/lib/http/) and invalidates the keys query cache on success so the table status badge stays in sync. Blocking is offered to proxy admins and team admins, matching the reset spend gating and the admin-only check the backend enforces on these routes. apiClient in networking.tsx is now exported so hooks can use it

Of note, I first wired the hook to the typed openapi-fetch fetchClient, but its rebase middleware clones the outgoing Request, which turns the JSON body into a streaming upload that Chrome only allows over HTTP/2; against the plain HTTP/1.1 dev proxy the POST dies with ERR_ALPN_NEGOTIATION_FAILED. The plain apiClient sends a string body and works everywhere

Tests: KeyInfoHeader.test.tsx covers the dropdown (all three items render and fire their handlers, Block flips to Unblock based on blocked state, items hide when the caller lacks the corresponding permission callback, Blocked tag rendering) and useSetKeyBlockedState.test.ts covers endpoint selection per direction, the request payload, cache invalidation, error surfacing, and the missing-token guard

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Adds a Block Key / Unblock Key action to the key info page, wired to the
existing /key/block and /key/unblock endpoints which previously had no UI.
The Reset Spend and Delete Key buttons move together with it into a new
overflow dropdown next to Regenerate Key, and a red Blocked tag shows next
to the key alias while the key is blocked.
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds block and unblock actions to the key information page. The main changes are:

  • A shared mutation hook for the existing block and unblock endpoints
  • An overflow menu for block, reset-spend, and delete actions
  • Confirmation dialogs and blocked-state feedback
  • Tests for request handling, cache invalidation, permissions, and UI behavior

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • The shared HTTP client and existing backend authorization checks are preserved.
  • Tests and the supplied live flow cover the main behavior.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/app/(dashboard)/hooks/keys/useSetKeyBlockedState.ts Adds an authenticated mutation using the shared API client and invalidates key queries after success.
ui/litellm-dashboard/src/components/networking.tsx Exports the existing shared API client for use by dashboard hooks.
ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx Moves destructive actions into an overflow menu and displays the blocked state.
ui/litellm-dashboard/src/components/templates/key_info_view.tsx Adds permission-gated block and unblock confirmation flows with local state updates and notifications.
ui/litellm-dashboard/src/app/(dashboard)/hooks/keys/useSetKeyBlockedState.test.ts Covers endpoint selection, payloads, errors, authentication checks, and cache invalidation.
ui/litellm-dashboard/src/components/templates/KeyInfoHeader.test.tsx Covers dropdown actions, conditional visibility, callbacks, disabled regeneration, and blocked-state rendering.

Reviews (1): Last reviewed commit: "feat(ui): add block/unblock key action t..." | Re-trigger Greptile

@devin-ai-integration

Copy link
Copy Markdown
Contributor

QA'd this end-to-end on litellm_ui_key_block_actions (75d708cf64) against a local proxy on :4000 with a live gpt-4o-mini, comparing to base litellm_internal_staging. The new block/unblock action works and is wired to the live proxy — all checks passed. Before/after screen recordings below (animated).

BEFORE (base litellm_internal_staging) — the key-info header only had Regenerate Key, Reset Spend, and Delete Key, with no overflow menu / block action:

before

AFTER (this PR) — full flow: ... overflow menu → Block Key → red "Blocked" tag + live proxy 401 → Unblock Key → tag removed + live proxy 200 restored:

after

Live proxy proof (same throwaway key, gpt-4o-mini):

# while blocked
POST /v1/chat/completions -> HTTP 401  "Key is blocked. Update via /key/unblock if you're an admin."
# after unblock
POST /v1/chat/completions -> HTTP 200  {"choices":[{"message":{"content":"Pong! How can I assist you today?"}}]}

The dropdown item correctly flips between "Block Key" and "Unblock Key" based on state, and the confirm modals show the expected copy (block warns about the 401; unblock is lighter).

@codspeed-hq

codspeed-hq Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_ui_key_block_actions (75d708c) with litellm_internal_staging (212a921)

Open in CodSpeed

@ryan-crabbe-berri
ryan-crabbe-berri enabled auto-merge (squash) July 21, 2026 20:35
@ryan-crabbe-berri
ryan-crabbe-berri merged commit d2819ba into litellm_internal_staging Jul 21, 2026
76 checks passed
@ryan-crabbe-berri
ryan-crabbe-berri deleted the litellm_ui_key_block_actions branch July 21, 2026 20:41
yuneng-berri added a commit that referenced this pull request Jul 22, 2026
…grations (#34206)

Delete Key moved into the key info page's overflow dropdown (#34116) and the
credentials table's row actions moved into a shared DataTable overflow menu, so
both specs were clicking a button that no longer exists. Point them at the menu
items instead.

Add a CredentialsPanel unit test asserting the update payload drops the masked
api key and keeps the edited api base, so that guard is not held up solely by an
e2e a table migration can silently disarm.
ryan-crabbe-berri added a commit that referenced this pull request Jul 22, 2026
…n UI

The key info header moved Delete Key into the More key actions menu
(#34116) and the credentials table moved row actions into a dropdown with
data-testids (shared DataTable migration), so both specs timed out
looking for buttons that no longer exist. Reproduced against a proxy
serving a fresh dashboard build; both pass after pointing them at the
menus.
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