Commit graph

33557 commits

Author SHA1 Message Date
yuneng-jiang
0e7bb5773c bump: version 0.4.47 → 0.4.48 2026-03-03 18:15:17 +05:30
Krish Dholakia
835ac240d2 Agents - assign tools (#22064)
* feat(proxy): add max_iterations limiter for agent session loops (#22058)

Adds a new proxy hook that enforces a per-session cap on the number of
LLM calls an agentic loop can make. Callers send a session_id with each
request, and the hook counts calls per session, returning 429 when the
configured max_iterations limit is exceeded.

- Uses Redis Lua script for atomic increment (multi-instance safe)
- Falls back to in-memory cache when Redis unavailable
- Follows parallel_request_limiter_v3 pattern
- Configurable via key metadata: {"max_iterations": 25}
- Session counters auto-expire via TTL (default 1hr)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add new code execution dataset

* feat(agent_endpoints/): allow giving agents keys

* fix: ui fixes

* feat: allow assigning mcp servers to agents

* fix: eliminate duplicate DB queries in MCP agent auth and N+1 in agent listing (#22110)

- Extract _get_agent_object_permission helper so _get_allowed_mcp_servers_for_agent
  and _get_agent_tool_permissions_for_server share a single DB fetch instead of
  each independently querying the same agent row (was 1+N queries per MCP request)
- Use include={"object_permission": True} on find_many in get_all_agents_from_db
  to eagerly load permissions in one query instead of N+1
- Use include={"object_permission": True} on create/update/find_unique in all
  agent CRUD operations, removing attach_object_permission_to_dict follow-up calls

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:15:17 +05:30
yuneng-jiang
0d9e6f8011 fixing build and tests 2026-03-03 18:15:17 +05:30
yuneng-jiang
f795faa640 fixing build 2026-03-03 18:15:17 +05:30
yuneng-jiang
f4dc7c665f [Docs] Add Credential Usage Tracking documentation
Add new document explaining automatic credential usage tracking and tagging. When models use reusable credentials, LiteLLM automatically injects a Credential: <name> tag on requests, enabling credential-level spend tracking on the Usage page with no additional configuration.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 18:15:17 +05:30
yuneng-jiang
14b785c6e6 [Test] UI - Pricing Calculator: Add comprehensive unit tests
Added unit tests for all pricing calculator components with 64 passing tests across 5 test files:
- multi_export_utils.test.ts (16 tests for PDF/CSV export functions)
- use_multi_cost_estimate.test.ts (15 tests for cost estimation hook)
- multi_export_dropdown.test.tsx (8 tests for export dropdown component)
- multi_cost_results.test.tsx (15 tests for results display and UI states)
- index.test.tsx (10 tests for main calculator component)

Also fixed missing page description for tool-policies page in page_metadata.ts.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 18:15:17 +05:30
Krish Dholakia
10f0f7b8c3 feat(ui): add user filtering to usage page (#22059)
* feat(ui): add user filtering to usage page

Adds "User Usage" as a new view option in the usage page dropdown,
allowing admins to view and filter usage data by individual users
via the existing /user/daily/activity backend endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(ui/): working usage filtering

* fix(ui): use single-select for user filter and add tests

The user entity type's backend endpoint only accepts a single user_id,
so the filter now uses single-select mode instead of multi-select.
Added tests for the new user entity type in EntityUsage and
UsageViewSelect. Updated CLAUDE.md and AGENTS.md with guidance on
UI/backend contract consistency and test coverage for new entity types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: remove unintended package-lock.json changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: restore package-lock.json to merge base state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:15:17 +05:30
Shin
81377f6679 fix(ui): remove duplicate antd import in ToolPolicies
Duplicate import was causing UI build to fail:
- Line 4: import { Select, Switch, Tooltip } from 'antd'
- Line 5: import { Select, Tooltip } from 'antd' (duplicate)

Removed the duplicate line 5.
2026-03-03 18:15:17 +05:30
Sameer Kankute
5281232a40 remove comment 2026-03-03 18:15:17 +05:30
Sameer Kankute
022d87b1dd Bump openai package 2026-03-03 18:15:17 +05:30
Sameer Kankute
612fd3d808 Fix: Test connect failing for bedrock batches mode 2026-03-03 18:15:17 +05:30
Sameer Kankute
f823e37344 Fix None (TypeError: 'NoneType' object is not a mapping) 2026-03-03 18:15:17 +05:30
Sameer Kankute
889bd1d440 Fix clean header logger 2026-03-03 18:15:17 +05:30
Sameer Kankute
68993c510c Fix docs 2026-03-03 18:15:16 +05:30
Sameer Kankute
ef9414cdc9 Fix docs 2026-03-03 18:15:16 +05:30
Sameer Kankute
0ab766ed75 add docs for Authentication Headers forwarding 2026-03-03 18:15:16 +05:30
Sameer Kankute
ccc4504445 Add support for forwarding provider's auth headers 2026-03-03 18:15:16 +05:30
Harshit28j
9b89c2e8cc Address CR feedback: fix auth dependency duplication, correct logger wording, clean imports 2026-03-03 18:15:16 +05:30
Harshit28j
d7adecd996 fix Unauthenticated RCE and Sandbox Escape in Custom Code Guardrail 2026-03-03 18:15:16 +05:30
Harshit28j
893eeef1ad fix(guardrails): prevent presidio crash on non-json responses 2026-03-03 18:15:16 +05:30
Harshit Jain
0880e1160e fix: Metadata / Trace ID Missing in S3 Streaming Callbacks 2026-03-03 18:15:16 +05:30
Harshit Jain
0e229a7b75 fix: Metadata / Trace ID Missing in S3 Streaming Callbacks 2026-03-03 18:15:16 +05:30
Krish Dholakia
f60f464753 feat: add new code execution dataset (#22065) 2026-03-03 18:15:16 +05:30
yuneng-jiang
02134a07bb Allow DB fallback for failure metadata enrichment lookups
Accurate logging is more important than avoiding the rare DB lookup
on the async failure path. Remove check_cache_only=True so lookups
fall back to DB when the key/team is not in cache.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:15:16 +05:30
yuneng-jiang
64d88f7d53 address greptile review feedback (greploop iteration 2)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:15:16 +05:30
yuneng-jiang
5a96f5a832 address greptile review feedback (greploop iteration 1)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:15:16 +05:30
yuneng-jiang
138654e0f8 [Fix] Enrich failure spend logs with key/team metadata
Failure spend logs were missing key metadata (key alias, user ID, team ID,
team alias) in two scenarios:

1. Auth errors (401 ProxyException): auth_exception_handler creates a
   minimal UserAPIKeyAuth with only api_key and request_route set — all
   other fields are null. The failure hook now looks up the full key object
   from cache/DB using the key hash to populate the missing fields.

2. Post-auth failures (provider errors, rate limits): key fields are
   present but team_alias is always null because LiteLLM_VerificationTokenView
   SQL view does not include team_alias. The failure hook now looks up the
   team object from cache to populate team_alias.

Both lookups are non-fatal and wrapped in try/except.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 18:15:16 +05:30
shin-bot-litellm
d16190baac fix(test): Update status enum values to match Google Interactions OpenAPI spec (#22061)
Google's Interactions API spec changed the status enum:
- Values are now lowercase (was uppercase)
- 'UNSPECIFIED' value was removed

Updated test to match the current spec from:
https://ai.google.dev/static/api/interactions.openapi.json
2026-03-03 18:15:16 +05:30
yuneng-jiang
7c0f0ff4b8 [Feature] Add request_duration_ms to SpendLogs
Add a `request_duration_ms` column to `LiteLLM_SpendLogs` to track request
duration. New rows are computed at write time. Legacy rows use a COALESCE
fallback in the `/spend/logs/ui` query to compute duration on the fly from
`endTime - startTime`. The field is also sortable in the UI endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 18:15:16 +05:30
Alejandro Tapia
1a656c0795 removed extra comma typo 2026-03-03 18:15:15 +05:30
Ishaan Jaff
2b276fabbc feat(agents): assign virtual keys to agents (#22045)
* feat(agents): assign virtual keys to agents

- Add agent_id field to LiteLLM_VerificationToken (schema.prisma + _types.py)
- Pass agent_id through key generation endpoint so keys can be scoped to an agent
- Refactor Add Agent wizard to 3-step flow (Configure → Assign Key → Ready)
  - Configure: all agent fields, custom/other type with just name+description
  - Assign Key: create new key or reassign existing key to agent
  - URL is now optional for easy discovery
- Add "Agent" ownership option to Create Key modal on Virtual Keys page
  with agent selector dropdown
- Extract CreatedKeyDisplay into shared component, reused in both flows
- Add keyCreateForAgentCall networking helper
- Add test for agent_id key generation

* fix(agents): code quality fixes from self-review

- Fix test_generate_key_helper_fn_agent_id: remove bare except clause,
  use explicit assert mock_insert.called, use .kwargs for clean arg access
- Remove no-op conditional in handleNext (both branches were identical)
- Validate selectedExistingKey before calling keyUpdateCall
- Validate selectedAgentId before setting on formValues in create_key_button

* fix(ui): replace deprecated Tremor Button with Ant Design Button in CreatedKeyDisplay
2026-03-03 18:15:10 +05:30
Ishaan Jaff
9b4a2352bf fix(ui): show real tool names in logs for Anthropic format tools (#22048) 2026-03-03 18:15:10 +05:30
Alejandro Tapia
62a61f3c43 differentiate solely by model_Id 2026-03-03 18:15:10 +05:30
Alejandro Tapia
73eaa11372 healthcheck-model_id-fix: There was quite a bit of code that needed to be changed since health checks were entirely keyed by model name. This includes, proxy logic, the dashboard, and even networking, because model name was the identifier everywhere. All changed files had tests added to them, which are passing with no regressions. 2026-03-03 18:15:08 +05:30
yuneng-jiang
fd05c49787 [Feature] UI - Virtual Keys: Add KeyInfoHeader component with metadata display
Add a reusable KeyInfoHeader component to replace the inline header in KeyInfoView.
Extract LabeledField as a common component for labeled metadata display with copyable
support, default_user_id handling, and empty value placeholders.

- Migrate all icons from lucide-react/heroicons to @ant-design/icons
- Use antd native copyable with descriptive tooltips (Copy Key Alias, Copy Key ID, etc.)
- Show DefaultProxyAdminTag for default_user_id values
- Add canModifyKey, regenerateDisabled, regenerateTooltip props for permission gating
- Add tests for KeyInfoHeader (19 tests) and LabeledField (8 tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 18:15:05 +05:30
Ryan Crabbe
28a225d8bd fix: add prompt_cache_key and prompt_cache_retention support for OpenAI
These params were silently dropped for Chat Completions because they
were missing from the supported params whitelist. Also adds
prompt_cache_retention to the Responses API TypedDict and fixes
misleading cache_control comments in OpenAI prompt caching docs.
2026-03-03 18:15:05 +05:30
Ishaan Jaff
4b5c47f7e7 [Feat] UI - Allow using AI to understand Usage patterns (#22042)
* Add Ask AI chat component to Usage page

- Create UsageAIChatModal component with streaming chat interface
- Integrate with existing model hub for model selection
- Pass usage data context (spend, models, providers, keys) to AI
- Add Ask AI button next to Export Data button in global view
- Add tests for the new component and integration

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Convert Ask AI from modal to right-side sliding panel

- Replace UsageAIChatModal with UsageAIChatPanel
- Panel slides in from right side, usage page stays visible
- Full-height panel with header, model selector, chat area, and input
- Smooth CSS transition for open/close animation
- Update tests for new panel component (34 tests passing)

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Remove build output directory from tracking

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Add backend AI usage chat endpoint with tool calling

Backend:
- New /usage/ai/chat SSE streaming endpoint
- AI agent has get_usage_data tool that queries /user/daily/activity/aggregated
- Follows same architecture as policy AI suggest (litellm.acompletion + tools)
- Non-admin users are restricted to their own data
- 12 backend unit tests

Frontend:
- Panel now calls /usage/ai/chat backend endpoint via SSE
- Removed direct OpenAI client calls from frontend
- Added usageAiChatStream networking function following enrichPolicyTemplateStream pattern

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Make model selection optional, default to gpt-4o-mini on backend

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Add team/tag tools, status indicators, and improved AI agent

- AI agent now has 3 tools: get_usage_data, get_team_usage_data, get_tag_usage_data
- Stream status events (Thinking... Fetching... Analyzing...) to UI
- Frontend shows spinner + status text during tool execution
- Better system prompt guiding tool selection
- Entity summariser for team/tag data with ranked breakdowns
- 13 backend tests, 34 frontend tests passing

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Fix: inject today's date into system prompt so AI resolves relative dates correctly

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Show tool calls as distinct steps + render markdown in responses

- Backend emits tool_call events with tool_name, label, args, and status
- Frontend shows each tool call as a step with ✓/spinner/✗ indicator
- Tool call steps show icon, label, date range, and filters
- AI responses rendered with ReactMarkdown (bold, lists, tables, code)
- Cursor-like UX: Thinking → tool calls → Analyzing → streamed answer

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Refactor backend for code quality: proper types, constants, all functions ≤50 LOC

- TypedDict for SSE events (SSEStatusEvent, SSEToolCallEvent, etc.) and ToolHandler
- Constants for table names, entity fields, temperature, page sizes, top-N limits
- Shared _query_activity() eliminates duplicated fetch logic
- _accumulate_breakdown() + _ranked_lines() replace inline aggregation loops
- Extracted _process_tool_call() and _stream_final_response() from main stream fn
- Black + Ruff clean, all 15 functions verified ≤50 LOC
- Replaced Tremor Button with Antd Button in panel (Tremor deprecated per AGENTS.md)

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Address greptile review: security fixes and input validation

- Restrict team/tag tools to admin-only users (non-admins only get get_usage_data)
- Constrain ChatMessage.role to Literal['user', 'assistant'] to prevent system prompt injection
- Add test for base tools restriction (non-admin gets 1 tool, admin gets 3)
- Issues 3 (unused imports) and 4 (inline datetime) were already fixed in prior commit

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Address greptile round 2: sanitize errors, defense-in-depth allowlist, revert tsconfig

- Sanitize error messages: generic 'An internal error occurred' sent to client,
  full exception logged server-side via verbose_proxy_logger
- Defense-in-depth: _process_tool_call validates fn_name against role-based
  allowlist before dispatch (even though LLM only receives allowed tools)
- Revert tsconfig.json jsx back to 'preserve' (Next.js recommended default)

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Role-scoped system prompt + additional test coverage

- System prompt is now role-aware: admin sees all 3 tool descriptions,
  non-admin only sees get_usage_data (consistent with tool filtering)
- Added tests: non-admin prompt excludes team/tag tools, date injection
- 15 backend tests, 34 frontend tests passing

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

* Fix LLM arg validation + cap conversation size at 20 messages

- _resolve_fetch_kwargs uses .get() with ValueError for missing dates
  (handles malformed LLM tool arguments gracefully)
- MAX_CHAT_MESSAGES = 20 constant; backend truncates to last 20
- Frontend also sends only last 20 messages per request
- Prevents excessive token usage and context-length errors

Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
2026-03-03 18:15:05 +05:30
Ryan Crabbe
6cda26fc75 docs: add trailing slash to /mcp endpoint URLs
The /mcp endpoint requires a trailing slash because the MCP server
is mounted as a sub-application using app.mount(). Starlette's mount
behavior causes a 307 redirect from /mcp to /mcp/, which many MCP
clients fail to handle.

Updates documentation examples to use /mcp/ consistently.
2026-03-03 18:15:05 +05:30
Ryan Crabbe
f763876b07 fix: add cache invalidation for _cached_get_model_group_info on deployment changes
_cached_get_model_group_info uses @lru_cache but had no invalidation,
causing stale model group info (TPM/RPM limits) after dynamic deployment
changes. Add cache_clear() at all 5 model_list mutation sites.
2026-03-03 18:15:03 +05:30
Ishaan Jaff
fef13a161b feat(proxy): tool policies - auto-discover tools + policy enforcement guardrail (#22041)
* feat(proxy): tool policies - auto-discover tools, manage policies, guardrail enforcement

- New LiteLLM_ToolTable in schema.prisma to store discovered tools
- Auto-discovery: tools seen in LLM responses get upserted via ToolDiscoveryQueue
  (hooks into DBSpendUpdateWriter, same pipeline as spend tracking)
- Management endpoints: GET /v1/tool/list, GET /v1/tool/{name}, POST /v1/tool/policy
- ToolPolicyGuardrail: blocks tool_calls in responses based on policy setting
- UI: Tool Policies page under Guardrails section with policy selector,
  filters by policy/team/key, live tail, sortable table
- Unit tests for queue, writer, endpoints, guardrail

* feat(tool-policies): track call_count + discover tools from request body and /messages API

- Add call_count column to LiteLLM_ToolTable; incremented on every flush
- Extract tools from request body too (not just response tool_calls):
  - OpenAI /chat/completions: tools[].function.name
  - Anthropic /messages pass-through: request_body.tools[].name
- Show call_count column in UI table (sortable)
- UI: drop dual_llm option, keep only trusted/blocked

* fix: address greptile review feedback

- Remove redundant @@index([tool_name]) from schema.prisma (tool_name has @unique which already creates an index)
- Replace gen_random_uuid()::text with str(uuid.uuid4()) for portability
- Rewrite test_tool_registry_writer.py to mock execute_raw/query_raw (actual implementation) instead of Prisma model methods
- Fix test patches in test_tool_management_endpoints.py to target source modules since imports are inside function bodies
- Add "Tool Policies" page title to ToolPolicies.tsx

* fix: address greptile review round 2

- Replace NOW() with Python datetime parameter in tool_registry_writer (SQLite portability)
- Fix cache key collision in tool_policy_guardrail: use null-byte separator instead of colon
- Remove type==function filter from request-side tool extraction to match response-side behavior
- Clear seen_tool_names on flush so call_count increments per batch cycle not per pod lifetime

* fix: address greptile review round 3

- Fix test_seen_names_persist_across_flushes to match actual per-flush-cycle behavior
- Update module docstring in tool_discovery_queue.py to accurately describe flush behavior
- Add created_at/updated_at to raw SQL INSERT in batch_upsert_tools and update_tool_policy

* fix: cache tool policies per tool name not per combination

Previously the cache key was built from the full set of tool names in a
request, so each unique combination of tools got its own cold cache entry
and triggered a separate DB query. With N distinct tools across requests
this was effectively a DB hit on every request.

Now each tool name is cached individually. Cache hits are checked per
tool, only missing tools are fetched from DB in a single batch query,
and each result is cached separately. Once a tool's policy is warm,
any subsequent request using that tool benefits from the cache regardless
of what other tools are in the request.

* Update ui/litellm-dashboard/src/components/ToolPolicies.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-03 18:14:57 +05:30
yuneng-jiang
f13a8b6003 address greptile review feedback (greploop iteration 2)
- Wait for strategy select (API data loaded) before clicking Save
- Assert specific payload content in setCallbacksCall
- Move NotificationsManager import to top of file
2026-03-03 18:14:54 +05:30
yuneng-jiang
1fb5c496c8 address greptile review feedback (greploop iteration 1)
- Replace document.querySelector/querySelectorAll with screen.getByRole
- Replace raw dispatchEvent with userEvent.selectOptions
2026-03-03 18:14:54 +05:30
yuneng-jiang
0e8b5616d6 [Test] Add unit tests for router_settings components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 18:14:54 +05:30
Ryan Crabbe
f778afc14b fix: convert remaining dict(request.headers) to _safe_get_request_headers
Missed conversions in user_api_key_auth.py and litellm_pre_call_utils.py.
Both call sites are read-only so no .copy() needed.
2026-03-03 18:14:54 +05:30
Ryan Crabbe
071a7bbad1 fix: add .copy() to create_request_copy and tests for header caching
Protect cached headers from mutation in create_request_copy sites and
add unit tests for _safe_get_request_headers caching behavior.
2026-03-03 18:14:54 +05:30
Ryan Crabbe
97d3942c78 perf: add request.state caching to _safe_get_request_headers
Cache the dict(request.headers) result on request.state._cached_headers
so subsequent calls within the same request return the cached dict
instead of re-creating it each time.
2026-03-03 18:14:54 +05:30
Ryan Crabbe
92c0fee05b perf: use cached _safe_get_request_headers instead of dict(request.headers)
Replace 15 call sites across 9 files that called dict(request.headers)
with _safe_get_request_headers(request) which caches the result on
request.state. Mutation sites use .copy() to protect the shared cache.
2026-03-03 18:14:52 +05:30
Ryan Crabbe
f902f7eaeb perf: short-circuit is_model_o_series_model with startswith before set lookup
Reorder the check to use str.startswith(tuple) first, which immediately
returns False for non-o-series models (the common case), avoiding the
genexpr + 198-element set lookup. Line profiling shows 4.75x speedup
(13.6µs → 2.9µs per call, 2.45s → 0.52s across 180k calls).
2026-03-03 18:14:48 +05:30
Ryan Crabbe
b07078ae75 perf: skip throwaway Usage() construction in ModelResponse.__init__
Avoid constructing a default Usage() object that gets immediately
overwritten by convert_to_model_response_object. Set usage=None
instead; the real Usage is assigned via setattr later.

Also fix Bedrock Qwen2/Qwen3 transform_response to assign a new
Usage object instead of mutating a potentially missing one.
2026-03-03 18:14:47 +05:30
yuneng-jiang
bacfa9653a address greptile review feedback (greploop iteration 3)
- Add missing traceback to team member spend enqueue error log
2026-03-03 18:14:41 +05:30