fix(ui): add searchable filter to User Usage page#25751
fix(ui): add searchable filter to User Usage page#25751xykong wants to merge 55 commits intoBerriAI:mainfrom
Conversation
- 修改页面标题从 'LiteLLM Dashboard' 到 'Animal Gateway Dashboard' - 修改登录页面品牌图标从 🚅 到 🐾 - 修改所有用户可见的 'LiteLLM' 文本为 'Animal Gateway' - 更新 OAuth 回调页面品牌 - 更新 Onboarding 页面品牌
- 替换 favicon.ico 为自定义品牌图标 - 替换 logo.jpg 为自定义品牌 Logo - 移除 "Quick feedback" 对话框组件 - 修复 navbar.tsx 中未使用的 Button 导入导致的 ESLint 错误
- Replace next/font/google with system font - Prevents network timeout during Docker build - Uses Tailwind's font-sans class for consistent styling
Next.js static export mode doesn't support image optimization API. All <Image> components now use unoptimized prop to bypass optimization. Affected files: - LoginPage.tsx: login page logo (2 instances) - ChatImageRenderer.tsx: user uploaded images - create_search_tool.tsx: search provider logos Fixes: https://animal-gateway.kxxxl.com/_next/image returning 404
… API Fix model name lookup issue when using Google Gemini Provider native API format. When third-party tools use endpoints like: - /v1beta/models/gemini-3-pro-preview:streamGenerateContent - /v1beta/models/gemini-3-pro-preview:generateContent The router could not find the model deployment because the model name from the URL path (e.g., "gemini-3-pro-preview") did not match the registered model name (e.g., "gemini/gemini-3-pro-preview"). Solution: - Automatically add 'gemini/' prefix to model names from URL path - Modified google_generate_content() and google_stream_generate_content() - Skip prefix if model name already starts with 'gemini/' Also update branding: - Replace LiteLLM logo with Animal Gateway logo in login page - Update guardrail logo reference - Add Animal Gateway logo asset (50KB) Fixes 502 error when third-party tools use Google Gemini Provider instead of OpenAI Compatible format.
修复 Next.js 构建错误: - 修复 AdminPanel 和 SearchTools 组件的导入路径错误 - 移除 Navbar 组件中不存在的 props (isDarkMode, toggleDarkMode) - 为 useSearchParams() hook 添加 Suspense 边界 - 解决 layout.tsx 的 Git merge conflict 品牌定制功能: - 禁用 LiteLLM 用户反馈调查弹窗(移除 Claude Code Prompt 自动触发) - 替换管理界面左上角 Logo 为 Animal Gateway 自定义 Logo - ThemeContext 设置默认 logo 路径为 /assets/logos/animal_gateway_logo.jpg 修改文件: - ui/litellm-dashboard/src/app/page.tsx - ui/litellm-dashboard/src/app/(dashboard)/layout.tsx - ui/litellm-dashboard/src/app/(dashboard)/settings/admin-settings/page.tsx - ui/litellm-dashboard/src/app/layout.tsx - ui/litellm-dashboard/src/components/public_model_hub.tsx - ui/litellm-dashboard/src/contexts/ThemeContext.tsx
Add comprehensive support for Volcengine multimodal embeddings (text + image)
Changes:
- Extend VolcEngineEmbeddingConfig with multimodal capabilities
- Add _is_multimodal_input() to detect text vs image inputs
- Add _transform_multimodal_input() to support multiple input formats
- Add _transform_multimodal_response() for response standardization
- Add _multimodal_embedding() to call Volcengine SDK multimodal API
- Override aembedding() to route based on input type
- Maintain backward compatibility with text-only embeddings
- Add error handling for invalid inputs and API errors
Supported input formats:
- Single text string: "hello" → text-only embedding
- Single image URL: "https://..." → multimodal
- Mixed list: ["text", "url"] → multimodal
- Standard format: [{"type": "text", "text": "..."}]
Reference: https://www.volcengine.com/docs/82379/1409291
Implement Volcengine multimodal embedding functionality for generating embeddings from mixed text and image inputs. Changes: - Fix import: Embeddings -> Embedding (correct class name) - Add required 'object' parameter to Embedding constructor - Import VolcEngineError for consistent error handling - Add comprehensive test coverage (28 tests, all passing) Supported input formats: - Simplified: ["text", "https://image.jpg"] (auto-detection) - Standard: [{"type": "text", "text": "..."}, {"type": "image_url", ...}] - Image formats: HTTP/HTTPS URLs, Base64 encoded images Features: - Automatic input type detection (text vs image) - Input format transformation - Response transformation to EmbeddingResponse - SDK client initialization with API key support - Async multimodal embedding method - Full backward compatibility for text-only embeddings Test results: 28/28 tests passing
…tTable - Add migration file 20260210173037_add_tags_to_policy_attachment - This migration was missing from upstream LiteLLM commit f836201 - The schema.prisma was updated but no migration file was generated - Fixes: The column LiteLLM_PolicyAttachmentTable.tags does not exist
- Remove old migration 20260210173037 (past timestamp, won't be applied) - Add new migration 20260214000000 (future timestamp, will be applied) - This ensures Prisma migrate deploy will apply the migration - Fixes: PolicyAttachmentTable.tags column not existing error
…r TypeScript errors - Remove extra } on line 15 of onboarding/page.tsx (rebase artifact) - Add userName? optional prop to NavbarProps interface - Make isDarkMode/toggleDarkMode optional (not passed by callers) - Remove duplicate UserDropdown render and orphaned Docs/BlogDropdown refs - Remove unused localStorageUtils imports
…anner - Remove 'Join Slack' and 'Star us on GitHub' buttons from navbar - Remove 'Missing a provider?' banner from Models page - Clean up related test files and imports
The function used getattr() to extract user_id from SSO result, but getattr() doesn't work on dict types, causing user_id to be resolved as None when HappyElements SSO passes a dict. This led to user identity confusion where all users were incorrectly identified as the first user found by email lookup. Changes: - Add isinstance(result, dict) check before accessing attributes - Use result.get() for dict types, getattr() for objects - Apply fix to all attribute access points in the function Fixes: SSO login showing wrong user (C120588 instead of C223214)
[Animal Gateway] Bypass license check - is_premium() always returns True to enable all Enterprise features without requiring a valid license.
…ve tool_use ordering When opencode uses the Anthropic-native /v1/messages endpoint with modify_params=True, context compaction can produce assistant messages where text blocks appear after tool_use blocks (e.g. [text, tool_use, text, tool_use]). Anthropic rejects this with: 'messages.17: tool_use ids were found without tool_result blocks immediately after: toolu_014pWwdiQm6ear25PGFtxsuB' Root cause: the existing sanitize_messages_for_tool_calling() only handles OpenAI format (tool_calls field) and is not called on the /v1/messages pass-through path at all. Fix: - Add sanitize_anthropic_native_messages_for_tool_calling() which handles Anthropic-native format (content list with tool_use blocks): - Case A: inject dummy tool_result for orphaned tool_use blocks - Case C: reorder content so all text blocks precede tool_use blocks - Call it at the top of anthropic_messages_handler() before any API request is made - Add 18 unit tests including regression test from real production request (request_id: 8b51e753, 2026-03-05 17:48)
- Remove unused imports (Button, BlogDropdown, CommunityEngagementButtons) - Add type='button' attribute for accessibility - Fix React hooks dependency array (add setProxySettings) - Remove CommunityEngagementButtons component from navbar - Update test expectation from 'Docs' to 'Chat'
Anthropic API rejects vector_store_ids parameter with 400 error: 'vector_store_ids: Extra inputs are not permitted' This parameter is injected by LiteLLM proxy for internal RBAC checks but should not be forwarded to Anthropic's API endpoint.
Multimodal messages have content as list[dict]. Setting this directly as an OTEL span/log attribute violates the SDK type constraint (only primitives or sequences of primitives are allowed), causing: Invalid type dict in attribute 'gen_ai.prompt' value sequence. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or None Use safe_dumps() (already imported) to serialize non-string content before assigning it to the gen_ai.prompt attribute. Upstream issue: BerriAI#24057 Upstream PR (closed): BerriAI#24077
Attributes 'gen_ai.system', 'gen_ai.request.model', and 'finish_reason'
can be None in certain call paths, causing OTEL SDK to reject them:
Invalid type NoneType for attribute 'gen_ai.system' value.
Expected one of ['bool', 'str', 'bytes', 'int', 'float']
Root cause: dict.get(key, default) only uses default when key is absent;
if the key exists with value None, None is returned. Use 'or' fallback
to handle both cases.
Fixes: _record_metrics (gen_ai.system, gen_ai.request.model)
_emit_semantic_logs (gen_ai.system, finish_reason)
…ne and dict
Complete audit of opentelemetry.py for invalid OTEL attribute types.
Remaining issues fixed:
- attrs["id"] = str(msg["id"])
tool message id cast to str to guarantee primitive type
- attrs["message.content"]: apply same safe_dumps guard as gen_ai.prompt
multimodal completion content (list[dict]) serialized before assignment
- litellm_params.get("custom_llm_provider") or "Unknown"
guard against key-present-but-None in set_attributes span path
…ng attribute on ended span' warnings Move span.end() in _handle_success and _handle_failure to after all child spans (raw_request, guardrail, semantic_logs) are created. Previously the primary span was ended inside _start_primary_span before child spans were parented to it, causing the OTEL SDK to emit ~20 warnings per request.
…ing() to silence streaming span warnings
…s; add /prompts/list to self_managed_routes
- Add strip_null_bytes() to remove null bytes from strings before JSON encoding - Replace json.dumps() with safe_dumps() in spend tracking to prevent serialization errors - Add sanitize_anthropic_native_messages_for_tool_calling() for proper tool use block ordering - Add survey prompt modal to dashboard UI This prevents JSON serialization failures when request/response data contains null bytes.
- Move SSO button above the username/password form - Apply primary (blue) styling to SSO button for visual prominence - Apply default (white/outlined) styling to Sign in button - Users are now guided to prefer SSO login flow
…y creation When animal-mediakit auth login triggers /sso/happyelements/login?key=sk-xxx, the callback was flowing through get_redirect_response_from_openid() which calls generate_key_helper_fn() and creates a new virtual key in DB on every login. Fix: pass the CLI key through the callback URL (?cli_key=sk-xxx), then in happyelements_callback detect the cli_key param and route to cli_sso_callback() instead. This stores the session in cache and returns a JWT via /sso/cli/poll, consistent with the standard LiteLLM CLI SSO flow - no virtual key is created.
- happyelements_callback: when cli_key present, call _get_or_create_cli_virtual_key
which looks up key_alias=cli-sso-{user_id} in DB; deletes old row (hash-only,
cannot retrieve raw sk-xxx) and regenerates so caller always gets usable sk-xxx
- cli_poll_key: fast-path returns pre-resolved virtual key stored by happyelements_callback
without generating JWT
- Virtual key is stored in LiteLLM_VerificationToken so spend/RPM/TPM tracking works normally
…response generate_key_helper_fn returns the raw sk-xxx value in key_data['token'], not key_data['key']. The 'key' field does not exist in the return dict.
…m_id in CLI login - happyelements_callback now resolves team_id/team_alias and embeds them in session_data - cli_poll_key fast-path returns team_id, team_alias, teams, team_details to client - happyelements_login accepts preferred_team_id query param and threads it through callback URL - _get_or_create_cli_virtual_key uses preferred_team_id to select the correct team - Rewrite test file: remove stale cli_sso_callback tests, add coverage for new logic
…elected When a user belongs to multiple teams and no preferred_team_id is given, the callback now stores a pending_team_selection session instead of immediately generating a key bound to teams[0]. The poll endpoint then returns requires_team_selection=True so the CLI can prompt for a team, and generates the key only after the user picks one.
…ring rebase spend_counter_cache declaration and get_current_spend / increment_spend_counters / _init_and_increment_spend_counter were added by upstream commit d533b43 but lost when proxy_server.py conflicts were resolved with --theirs during the v1.82.5->v1.83.6 rebase. Restores: - spend_counter_cache DualCache module-level declaration - spend_counter_cache.redis_cache wiring in switch_on_caching - get_current_spend(): Redis-first cross-pod spend reader - increment_spend_counters(): atomic spend counter updater - _init_and_increment_spend_counter(): counter init + increment helper
Upstream commit ad43a35 added worker_registry to ProxyConfig for control plane support, but the attribute init, WorkerRegistryEntry import, and config loading logic were all lost when proxy_server.py conflicts were resolved with --theirs. Restores: - from litellm.types.proxy.control_plane_endpoints import WorkerRegistryEntry - self.worker_registry: List[WorkerRegistryEntry] = [] in __init__ - worker_registry config loading in load_config() Fixes: AttributeError: 'ProxyConfig' object has no attribute 'worker_registry' on GET /litellm/.well-known/litellm-ui-config (500)
…ver.py The feat(vod) commit (3276157) wrote proxy_server.py from an older baseline, silently dropping upstream additions that had already been rebased in. Restore all six function groups: - _run_pw_migration + migrate_passwords_to_scrypt_async import (password migration task on startup) - _get_endpoint_exception_status + _write_health_state_to_router_cache (health-check-driven routing, opt-in via enable_health_check_routing) - _add_access_group_models_to_team_models (resolve team models via access groups in UI listing) - login_v3 + login_v3_exchange (control-plane single-use-code login flow) - _stream_mcp_asgi_response + toolset_mcp_route (MCP toolset-namespaced endpoint, must precede dynamic_mcp_route)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ream sync Re-applies two fixes dropped when syncing 936 upstream commits (fa6d5ec): Path 1 - /chat/completions (factory.py): - convert_to_anthropic_image_obj() now detects actual image format from magic bytes via get_image_type() and overrides the declared media_type if they differ. Graceful fallback on detection failure. Path 2 - /v1/messages pass-through (handler.py): - Add _fix_image_media_types_in_messages() that walks all content blocks before forwarding, including nested tool_result image blocks. - Called from anthropic_messages_handler() before validation. Also adds 14 TDD unit tests (test_image_media_type_correction.py) as permanent regression / release smoke tests. Originally fixed in commits 420c8c7 and a7206b3 (Fixes BerriAI#24900). Lost in fa6d5ec upstream sync. Production failure: req_011CZz6xv2WrAh591jXehniT (user: xiangyi.kong) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… [local patch] Azure gpt-5.4 + tools + reasoning_effort combination triggered Azure content filter false positives (medium severity) causing completion blocks. Root cause: upstream commit cb15296 added 'azure' to responses_api_bridge_check's provider condition (in ('openai', 'azure')), forcing Azure gpt-5.4+tools+reasoning to Responses API. Azure Responses API reasoning tokens trigger content filter misclassification. Fix: - litellm/main.py: revert responses_api_bridge_check condition to == 'openai' only - gpt_5_transformation.py: restore drop of reasoning_effort when Azure gpt-5.4 + tools Tests: - Added regression guard test in test_main.py - Restored test_azure_gpt5_transformation.py assertion for drops_reasoning_effort - All 24 Azure gpt5 transformation tests pass Upstream commit reference: cb15296
…utput_to_choices [local patch] Azure gpt-5.4 (and newer Responses API models) may return a message with phase='commentary' before the actual tool_calls. This is intermediate model planning/reasoning, not the final response. Before this fix, litellm promoted the commentary message as choices[0] and placed the tool_calls in choices[1]. Clients like OpenCode only inspect choices[0], so they saw text content instead of tool_calls, breaking the agent loop (model kept reading files with no text output visible to the user). Fix: skip any ResponseOutputMessage with phase=='commentary' in _convert_response_output_to_choices so that tool_calls land in choices[0] with finish_reason='tool_calls' as expected.
… merge [local patch] Prompt management in _apply_prompt_management_to_responses_call filtered input items to only those with a 'role' key (chat messages) before passing to the template hook. After merging, the filtered result overwrote the original input—silently dropping all non-message items (function_call, function_call_output, reasoning, etc.). This caused multi-turn Responses API calls through the proxy to lose their tool-call history. The LLM saw only the user message on every turn and never progressed past the first step, breaking agent loops in OpenCode and other clients using /v1/responses with Azure gpt-5.4. Fix: collect non-message items before filtering, then re-append them to the merged result. Applied to both sync and async paths. Adds 3 regression tests (sync function_call, sync reasoning, async function_call) covering the exact failure mode.
User Usage filter only allowed selecting from a static preloaded list, unlike Global Usage which supports debounced text search via useInfiniteUsers. Add UserSingleSelect component (single-select with useInfiniteUsers + 300ms debounce + infinite scroll) and wire it into EntityUsage for entityType === 'user', mirroring the TeamMultiSelect fix in PR BerriAI#25107. - Add src/components/common_components/user_single_select.tsx - Add src/components/common_components/user_single_select.test.tsx (10 tests) - Update EntityUsage.tsx to render UserSingleSelect for user entity type - Update EntityUsage.test.tsx to mock user_single_select
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Too many files changed for review. ( |
|
dan.liang seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| - | - | Generic High Entropy Secret | da67018 | litellm/proxy/management_endpoints/sso/happyelements_endpoints.py | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
Description
Fixes #25750
The User Usage filter only showed a static preloaded list. Users could not type to search for a specific user, unlike the Global Usage page which supports debounced server-side search.
This PR introduces
UserSingleSelect— a dedicated single-select component withuseInfiniteUsers+ 300ms debounce + infinite scroll — and wires it intoEntityUsageforentityType === "user".Root Cause
EntityUsage.tsxdelegated user filtering toUsageExportHeader, which renders a plain<Select>withoutshowSearch/onSearch. The same issue was fixed for Team Usage in PR #25107 usingTeamMultiSelect.Changes
ui/litellm-dashboard/src/components/common_components/user_single_select.tsx— new componentui/litellm-dashboard/src/components/common_components/user_single_select.test.tsx— 10 testsui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx— wire upUserSingleSelectforentityType === "user"ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.test.tsx— add mock foruser_single_selectTesting
cd ui/litellm-dashboard npx vitest run src/components/UsagePage/components/EntityUsage/ --reporter=verbose npx vitest run src/components/common_components/user_single_select.test.tsx --reporter=verboseAll 92 existing tests pass + 10 new tests for
UserSingleSelect.Related