o
    ưi                    @   s  d Z ddlZddlZddlZddlmZmZmZmZm	Z	m
Z
mZmZ ddlmZmZmZ ddlmZ ddlZddlmZ ddlmZ ddlmZ dd	lmZmZmZmZmZm Z m!Z! dd
l"m#Z# ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z< ddl=m>Z> ddl?m@Z@ ddlAmBZBmCZC ddlDmEZE ddlFmGZGmHZHmIZI ddlJmKZK ddlLmMZM ddlNmOZO ddlPmQZQ erddlRmSZT eeTef ZSneZSeddZUeZVe5jWjXe5jYjX ZZde[de\ddfddZ]de
ee[ee[ f  de
eK de^fd d!Z_d"e
e/ d#e
ee[ee[ f  de
eK d$e^d%e
e< d&eHddfd'd(Z`d)ead*ed+ead,e[ddf
d-d.Zbd)ead+ead,e[ddfd/d0Zcd1e
ed d$e^d,e[ddfd2d3Zed+ead4e
e2 ddfd5d6Zfd+ead%e
e< d4e
e2 d,e[ddf
d7d8Zg	9	dd+ead4e
e2 d:e
e4 d;e
e) d1e
ed d)ead,e[de
eK d&eHd%e
e< d*ed$e^d"e
e/ de^fd<d=Zh	dd,e[d>e
e4 de^fd?d@Zid>e
e4 de
e6 fdAdBZj	dd,e[d*edCead%e
e< d>e
e4 de^fdDdEZkd>e
e4 fdFdGZl	dd,e[dHe	dI d*edCead%e
e< d>e
e4 de^fdJdKZmdLe[dMende^fdNdOZodPe6dLe[dQe+de^fdRdSZpdTe<dUe
e[ de^fdVdWZqdMendenfdXdYZr	ddZe
eG d[ed\e
eS de
e( fd]d^Zs	dd_e)dZeGd[ed\e
eS de)f
d`daZtd_e)d,e[ddfdbdcZueI		ddde
e[ dZe
eG d[ed,e[d\e
eS d&e
eH de
e) fdedfZveI		ddgee[ dZe
eG d[ed\e
eS d&e
eH dee[e0f fdhdiZweI		ddje
e[ dZe
eG d[ed\e
eS d&e
eH de
e0 fdkdlZxeI		ddme[dne[dZe
eG d[ed\e
eS d&e
eH de
do fdpdqZyde[dre
ee[  de
eK de^fdsdtZzdue[dvedwe{de^fdxdyZ|due[dze
e dvefd{d|Z}d}e%d)eadue	d~ de
ee[  fddZ~d}e%d)eade
ee[  fddZd}e%d)eade
ee[  fddZ		ddZeGde
e[ de
e[ de
e4 fddZeI					ddme
e[ dZe
eG d[ede^d\e
eS d&e
eH de
e[ de
e[ de
e^ de
e4 fddZdue[dzed[ed&e
eH fddZdne[de3d[ed&e
eH fddZde[de<d[ed&e
eH fddZde[d[ed&e
eH fddZeI	ddne[dZeGde
e^ fddZdne[dZeGfddZ	ddne[dZeGd[edvedwe{d&e
eH due[de
e^ de3fddZdue[d&e
eH d[ed\e
eS de
e3 f
ddZ					ddne[dZe
eG d[ed\e
eS d&e
eH de
e^ de
e^ de
e^ de3fddZ	dde[de'd[ed&e
eH fddZ	dde[d[ed&e
eH fddZeI	dde[dZe
eG d[ed&e
eH de'f
ddZeI		dde[dZe
eG d[ed\e
d d&e
eH de3fddZeI		dde[dZe
eG d[ed\e
d d&e
eH de
e. fddZG dd dZde[dZeGd\e
eS d&e
eH de
e f
ddZeIde[de[dZeGde
e[ fddZeI			dde[dZe
eG d[ed\e
eS d&e
eH de
e^ de<fddZeI		dde[dZe
eG d[ed\e
eS d&e
eH de
e, fddZeI			9dde[dZe
eG d[ed\e
eS d&e
eH de^de
e. fddÄZ			ddee[ de	d dZe
eG d[e
e d&e
eH dee[ fddȄZ			ddee[ dZe
eG d[e
e d&e
eH dee[ f
ddʄZ			ddee[ dZe
eG d[e
e d&e
eH dee[ f
dd̄Z			ddee[ dZe
eG d[e
e d&e
eH dee[ f
dd΄Z		dde[de
eK dee[ de
ee[e[f  dne
e[ de^fdd҄Z				ddee[ee[ f de
eK dee[ de
ee[e[f  dne
e[ de	d de{de	d fddلZ	dde[de
ee[e[f  de^fddۄZdee[ee[ f de
en d%e<de
ejK de	d f
ddބZ	dde[de
e. de
eK de
ee[e[f  de	d f
ddZ	ddee[ee[ f d4e
e2 de
eK de
ee[e[f  de	d f
ddZdee[ee[ f d"e/de
eK de	d fddZdee[ee[ f de
eK d:e
e4 de	d fddZde[de
eK de
e[ de	d fddZ	dd%e<d&eHd>e
e4 fddZ	dd%e<d&eHd>e
e4 fddZ	dd%e<d&eHd>e
e4 fddZd4e
e2 d:e
e4 d%e
e< dZe
eG d[ed&eHfddZd4e
e2 d%e
e< d&eHfddZd4e
e2 d%e
e< d&eHfddZd"e
e/ d%e
e< d&eHfddZd"e
e/ d%e
e< d&eHfddZ	dde[dZe
eG d[ed&e
eH de
e/ f
ddZd%e
e< d4e
e2 dZe
eG d[ed&eHf
ddZd+eadZe
eG d[ed&eHd%e
e< f
d dZde[de[de^fddZde[dende^fddZde[de[de^fdd	Zde[de^fd
dZd+ead4e
e2 d%e
e< fddZde	d dee[ de
e, fddZdS (  z
Got Valid Token from Cache, DB
Run checks for:

1. If user can call model
2. If user is in budget
3. If end_user ('user' passed to /chat/completions, /embeddings endpoint) is in budget
    N)TYPE_CHECKINGAnyDictListLiteralOptionalUnioncast)HTTPExceptionRequeststatus)	BaseModel)verbose_proxy_logger)	DualCache)LimitedSizeOrderedDict)CLI_JWT_EXPIRATION_HOURSCLI_JWT_TOKEN_NAMEDEFAULT_ACCESS_GROUP_CACHE_TTLDEFAULT_IN_MEMORY_TTL-DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTLDEFAULT_MAX_RECURSE_DEPTH-EMAIL_BUDGET_ALERT_MAX_SPEND_ALERT_PERCENTAGE)get_llm_provider)
RBAC_ROLESCallInfoLiteLLM_AccessGroupTableLiteLLM_BudgetTableLiteLLM_EndUserTableLitellm_EntityTypeLiteLLM_JWTAuthLiteLLM_ObjectPermissionTable#LiteLLM_OrganizationMembershipTableLiteLLM_OrganizationTableLiteLLM_ProjectTableCachedObjLiteLLM_TagTableLiteLLM_TeamMembershipLiteLLM_TeamTableLiteLLM_TeamTableCachedObjLiteLLM_UserTableLiteLLMRoutesLitellmUserRolesNewTeamRequestProxyErrorTypesProxyExceptionRoleBasedPermissionsSpecialModelNamesUserAPIKeyAuth)RouteChecks)PrismaDBExceptionHandler)TOOL_CAPABLE_CALL_TYPESextract_request_tool_namesroute_request)PrismaClientProxyLogginglog_db_metrics)Router)get_utc_datetime   )$organization_role_based_access_check)get_model_from_request)Spand   )max_sizeentityerrorreturnc                    sh   t |dkrt|jdkrdS t |  d}t fdddD r$d}td|  d	| d
|  dS )a  
    Log a warning when budget lookup fails; cache will not be populated.

    Skips logging for expected "user not found" cases (bare Exception from
    get_user_object when user_id_upsert=False). Adds a schema migration hint
    when the error appears schema-related.
     	ExceptionNc                 3   s    | ]}| v V  qd S N ).0xZerr_strrH   U/home/app/Keep/.python/lib/python3.10/site-packages/litellm/proxy/auth/auth_checks.py	<genexpr>V   s
    
z-_log_budget_lookup_failure.<locals>.<genexpr>)columnZschemazdoes not existZprismaZmigratezJ Run `prisma db push` or `prisma migrate deploy` to fix schema mismatches.zBudget lookup failed for zJ; cache will not be populated. Each request will hit the database. Error: .)strtype__name__loweranyr   rC   )rB   rC   hintrH   rK   rL   _log_budget_lookup_failureI   s    	rV   model
llm_routerc                 C   sB  | du s|du r
dS t | tr| gn| }|D ]}zd|j|d}|du r0td| d W  dS |j}|j}|du s>|du rQtd| d| d| d	 W  dS |d
ksY|d
krltd| d| d| d W  dS td| d| d| d W q ty } ztd| dt| d W Y d}~ dS d}~ww dS )aV  
    Check if a model has zero cost (no configured pricing).

    Uses the router's get_model_group_info method to get pricing information.

    Args:
        model: The model name or list of model names
        llm_router: The LiteLLM router instance

    Returns:
        bool: True if all costs for the model are zero, False otherwise
    NF)Zmodel_groupzNo model group info found for z, assuming it has costzModel z has undefined cost (input: z
, output: z), assuming it has costr   z has non-zero cost (input: )z- has zero cost explicitly configured (input: zError checking cost for model : T)
isinstancerP   Zget_model_group_infor   debugZinput_cost_per_tokenZoutput_cost_per_tokenrF   )rW   rX   Z
model_list
model_nameZmodel_group_infoZ
input_costZoutput_costerH   rH   rL   _is_model_cost_zeroc   sD   
r_   project_object_modelskip_budget_checksvalid_tokenproxy_logging_objc                    s~   | du rdS | j du rtd| j d|r%t| jdkr%t|| |d |s=t| ||dI dH  t| ||dI dH  dS dS )z
    Run all project-level checks: blocked, model access, budget, soft budget.
    Extracted from common_checks() to keep statement count manageable.
    NTzProject=z= is blocked. Update via `/project/update` if you're an admin.r   rW   r`   rX   )r`   rc   rd   )blockedrF   
project_idlenmodelscan_project_access_model_project_max_budget_check_project_soft_budget_checkr`   ra   rX   rb   rc   rd   rH   rH   rL   _run_project_checks   s2   
rn   general_settingsrequestrequest_bodyroutec                 C   s   |  ddsd S t|dr|jnd }|o| dk}tj|d}|tjjv p.tj	|tjjd}|rB|rD|sFd|vrHt
d| d  d S d S d S d S )	NZenforce_user_paramFmethodPOSTrr   )rr   allowed_routesuserz1'user' param not passed in. 'enforce_user_param'=)gethasattrrs   upperr1   is_llm_api_router)   Z
mcp_routesvalueZcheck_route_accessrF   )ro   rp   rq   rr   Zhttp_methodZis_post_methodZis_openai_routeZis_mcp_routerH   rH   rL   _enforce_user_param_check   s,   r}   c                 C   sn   |  ddsd S tj|dr/d|v r1t|d tr3d|d v r5td| d  dtjdtj	d	d S d S d S d S )
NZreject_clientside_metadata_tagsFru   metadatatagszVClient-side 'metadata.tags' not allowed in request. 'reject_clientside_metadata_tags'=z,. Tags can only be set via API key metadata.zmetadata.tagsmessagerQ   paramcode)
rx   r1   r{   r[   dictr-   r,   bad_request_errorr   HTTP_400_BAD_REQUEST)ro   rq   rr   rH   rH   rL   &_reject_clientside_metadata_tags_check   s"   
r   global_proxy_spendc                 C   sh   t jdkr&|s(| d ur*tj|dr,|dkr.|dkr0| t jkr2t j| t jdd S d S d S d S d S d S d S )Nr   ru   z
/v1/modelsz/modelscurrent_cost
max_budget)litellmr   r1   r{   BudgetExceededError)r   rb   rr   rH   rH   rL   _global_proxy_budget_check  s"   


r   team_objectc                 C   sF   |  di pi }| dsd S ddlm} ||s!tdddidd S )	Nr~   Z
guardrailsr   )can_modify_guardrailsi  rC   z8Your team does not have permission to modify guardrails.status_codedetail)rx   Z*litellm.proxy.guardrails.guardrail_helpersr   r
   )rq   r   Z_request_metadatar   rH   rH   rL   _guardrail_modification_check  s   
r   c                    s  ddl m} |du rdS ||}|rtdd |D sdS t|| }|s'dS t|jtr2|jp1i ni }t|jtr?|jp>i ni }|d}	|d}
t|	t	rXt
|	dkrX|	n|
}t|t	ret
|dkrgdS dd |D   fd	d
|D }|rtd| dtjdtjddS )u   
    Enforce key/team tool allowlist (metadata.allowed_tools). No DB in hot path —
    effective allowlist is read from valid_token.metadata and valid_token.team_metadata.
    Raises ProxyException with tool_access_denied if a tool is not allowed.
    r   )get_call_types_for_routeNc                 s   s    | ]}|j tv V  qd S rG   )r|   r3   )rI   ctrH   rH   rL   rM   9  s    

z(check_tools_allowlist.<locals>.<genexpr>Zallowed_toolsc                 S   s   h | ]}t |qS rH   )rP   )rI   trH   rH   rL   	<setcomp>Q      z(check_tools_allowlist.<locals>.<setcomp>c                       g | ]}| vr|qS rH   rH   )rI   nZallowed_setrH   rL   
<listcomp>R      z)check_tools_allowlist.<locals>.<listcomp>zTool(s) z5 are not in the allowed tools list for this key/team.toolsr   )Z2litellm.litellm_core_utils.api_route_to_call_typesr   rT   r4   r[   r~   r   Zteam_metadatarx   listrh   r-   r,   Ztool_access_deniedr   ZHTTP_403_FORBIDDEN)rq   rc   r   rr   r   Z
call_typesZ
tool_namesZkey_metaZ	team_metaZkey_allowedZteam_allowedZ	effectiveZ
disallowedrH   r   rL   check_tools_allowlist(  sR   
 




r   Fuser_objectend_user_objectc              	      s.  ddl m}m} t| |}|dur |jdu r td|j d|rI|rIt||||	r-|	jnddI dH sIt	d|j d	| d
|j
 tjdtjd|	dur|	jrddlm} ddlm} |j|	jd}|dur|jpki d}|rt|
j}||}|st	dtjdtjd|r|du r|durt|||dI dH  t|||||	|dI dH  |sCt|||	dI dH  t|||	dI dH  t|	||||dI dH  t | ||||	dI dH  |du s|jdu r|dur|j!dur|j!}||j"k rt#j$|j"|d|j% d|j" d| dt&|||	|||dI dH  |durC|j'durC|j'j!}|durC|j"|krCt#j$|j"|d|j% d|j" d| dt(||
| | t)|| | t*||| t+| | t,||| d t-|	dd}|durt|dkrtd nd!}t.||||
| |	d"}t/| ||	d#I dH  t0| |	||d$I dH  dS )%a  
    Common checks across jwt + key-based auth.

    1. If team is blocked
    1.1. If project is blocked
    2. If team can call model
    2.2 If project can call model
    3. If team is in budget
    3.0.2. If project is in budget
    3.0.3. If project is over soft budget (alert only)
    4. If user passed in (JWT or key.user_id) - is in budget
    5. If end_user (either via JWT or 'user' passed to /chat/completions, /embeddings endpoint) is in budget
    6. [OPTIONAL] If 'enforce_end_user' enabled - did developer pass in 'user' param for openai endpoints
    7. [OPTIONAL] If 'litellm.max_budget' is set (>0), is proxy under budget
    8. [OPTIONAL] If guardrails modified - is request allowed to change this
    9. Check if request body is safe
    10. [OPTIONAL] Organization checks - is user_object.organization_id is set, run these checks
    11. [OPTIONAL] Vector store checks - is the object allowed to access the vector store
    r   )prisma_clientuser_api_key_cacheNTzTeam=z6 is blocked. Update via `/team/unblock` if your admin.)rW   r   rX   team_model_aliasesz'Team not allowed to access model. Team=z, Model=z. Allowed team models = rW   r   )global_agent_registry)get_chain_id_from_headers)agent_idZ"require_trace_id_on_calls_by_agentzORequests made with this agent's key must include the x-litellm-trace-id header.rW   rX   r   rm   )r   rd   rc   )rc   r   r   r   rd   )rq   r   r   rd   rc   zExceededBudget: User= over budget. Spend=	, Budget=r   r   r   )r   r   rc   r   r   rd   ExceededBudget: End User=)r   rr   rq   team_idlitellm-dashboarduiapi)rr   
token_typeuser_objrp   request_datarc   )rq   r   rc   )rq   rc   r   rr   )1litellm.proxy.proxy_serverr   r   r>   rf   rF   r   can_team_access_modelr   r-   ri   r,   Zteam_model_access_deniedr   HTTP_401_UNAUTHORIZEDr   Z,litellm.proxy.agent_endpoints.agent_registryr   Z$litellm.proxy.litellm_pre_call_utilsr   Zget_agent_by_idZlitellm_paramsrx   r   headersr   r   can_user_call_modelrn   _team_max_budget_check_team_soft_budget_check_organization_max_budget_check_tag_max_budget_checkr   spendr   r   user_id_check_team_member_budgetlitellm_budget_tabler}   r   r   r   r=   getattr_is_allowed_routevector_store_access_checkr   )rq   r   r   r   r   ro   rr   rX   rd   rc   rp   rb   r`   r   r   ra   r   r   ZagentZrequire_trace_idZheaders_dictZtrace_idZuser_budgetend_user_budgetZ
token_teamr   Z_is_route_allowedrH   rH   rL   common_checks\  s  "






r   r   c                    sR   t jj} durt trt fdd|D rdS t fdd|D r'dS dS )z1
    - Check if the route is a UI used route
    Nc                 3   s    | ]}  |V  qd S rG   )
startswithrI   allowed_routeru   rH   rL   rM   ?  s    z_is_ui_route.<locals>.<genexpr>Tc                 3   s    | ]
}t j |d V  qdS ))rr   patternN)r1   Z_route_matches_patternr   ru   rH   rL   rM   C  s
    
F)r)   Z	ui_routesr|   r[   rP   rT   )rr   r   rv   rH   ru   rL   _is_ui_route2  s   r   c                 C   s>   | d u rd S | }|j }zt|}W |S  ty   tj Y S w rG   )	user_roler*   
ValueErrorZINTERNAL_USER)r   _user
_user_rolerolerH   rH   rL   _get_user_roleK  s   

r   r   c                 C   s>   t |d}|du rtdt|dstj||| |||d dS )z<
    - Route b/w api token check and normal token check
    r   Nz4Invalid proxy server token passed. valid_token=None.)r   r   rr   rp   r   rc   T)r   rF   _is_user_proxy_adminr1   Z$non_proxy_admin_allowed_routes_check)rr   rp   r   rc   r   r   rH   rH   rL   _is_api_route_allowed\  s   


r   c                 C   sH   | d u rdS | j d ur| j tjjkrdS | j d ur"| j tjjkr"dS dS )NFT)r   r*   PROXY_ADMINr|   r   rH   rH   rL   r   w  s   

r   r   )r   r   c                 C   s*   |dkrt | |drdS t| ||||dS )z;
    - Route b/w ui token check and normal token check
    r   )rr   r   T)rr   rp   r   rc   r   )r   r   )rr   r   rp   r   rc   r   rH   rH   rL   r     s   r   
user_routerv   c                 C   sd   ddl m} |D ]'}|tjv r(t| jD ]}||\}}}|| r&  dS qq|| kr/ dS qdS )a	  
    Return if a user is allowed to access route. Helper function for `allowed_routes_check`.

    Parameters:
    - user_route: str - the route the user is trying to call
    - allowed_routes: List[str|LiteLLMRoutes] - the list of allowed routes for the user.
    r   )compile_pathTF)Zstarlette.routingr   r)   __members__r|   match)r   rv   r   r   templateregex_rH   rH   rL   _allowed_routes_check  s   

r   r   litellm_proxy_rolesc                 C   sf   | t jkrt||jd}|S | t jkr1|jdu r#	 t|ddgd}|S |jdur1t||jd}|S dS )zE
    Check if user -> not admin - allowed to access these routes
    )r   rv   Nopenai_routesZinfo_routesF)r*   r   r   Zadmin_allowed_routesTEAMZteam_allowed_routes)r   r   r   Z
is_allowedrH   rH   rL   allowed_routes_check  s(   
	


r   user_api_key_dictrequested_user_idc                 C   sD   d}| j tjkr| j tjkrd}|d ur | jd ur | j|kr d}|S )NTF)r   r*   r   ZPROXY_ADMIN_VIEW_ONLYr   )r   r   Zret_valrH   rH   rL    allowed_route_check_inside_route  s   
r   c              	   C   s`   g }| D ])}zt | j}t|tr|t| n|| W q ty-   || Y qw |S rG   )r)   r|   r[   setextendr   KeyErrorappend)rv   Zactual_routesZ
route_nameZroute_valuerH   rH   rL   get_actual_routes  s   


r   r   r   parent_otel_spanc              
      s   | du s
t jdu rdS dt j }|j|dI dH }|dur&td	i |S z5| jjjdt jidI dH }|du rEtdt j  W dS |j	||
 tdI dH  td	i |
 W S  tyx } ztdt|  W Y d}~dS d}~ww )
a  
    Fetches the default end user budget from the database if litellm.max_end_user_budget_id is configured.

    This budget is applied to end users who don't have an explicit budget_id set.
    Results are cached for performance.

    Args:
        prisma_client: Database client instance
        user_api_key_cache: Cache for storing/retrieving budget data
        parent_otel_span: Optional OpenTelemetry span for tracing

    Returns:
        LiteLLM_BudgetTable if configured and found, None otherwise
    Nzdefault_end_user_budget:keyZ	budget_idwherez/Default end user budget not found in database: r   r|   ttlz(Error fetching default end user budget: rH   )r   max_end_user_budget_idasync_get_cacher   dbZlitellm_budgettablefind_uniquer   warningasync_set_cacher   r   rF   rC   rP   )r   r   r   	cache_keyZcached_budgetZbudget_recordr^   rH   rH   rL   get_default_end_user_budget  s6   
r   end_user_objc                    s^   | j dur| S tjdu r| S t|||dI dH }|dur-|| _ tdtj d| j  | S )a  
    Helper function to apply default budget to end user if they don't have a budget assigned.

    Args:
        end_user_obj: The end user object to potentially apply default budget to
        prisma_client: Database client instance
        user_api_key_cache: Cache for storing/retrieving data
        parent_otel_span: Optional OpenTelemetry span for tracing

    Returns:
        Updated end user object with default budget applied if applicable
    N)r   r   r   zApplied default budget z to end user )r   r   r   r   r   r\   r   )r   r   r   r   Zdefault_budgetrH   rH   rL   !_apply_default_budget_to_end_user/  s    

r   c              	   C   sf   t |rdS | jdu rdS | jj}|dur/| j|kr1tj| j|d| j d| j d| ddS dS )z
    Check if end user is within their budget limit.

    Args:
        end_user_obj: The end user object to check
        route: The request route

    Raises:
        litellm.BudgetExceededError: If end user has exceeded their budget
    Nr   r   r   r   )r1   Zis_info_router   r   r   r   r   r   )r   rr   r   rH   rH   rL   _check_end_user_budgetZ  s   

r   end_user_idc              
      s4  |du r	t d| du rdS d| }|j|dI dH }|dur;tdi |}t||||dI dH }t||d |S zC|jjjd| iddd	d
I dH }	|	du rSt tdi |		 }
t|
|||dI dH }
|j
d| |
	 dI dH  t|
|d |
W S  t y } zt|tjr|W Y d}~dS d}~ww )a?  
    Returns end user object from database or cache.

    If end user exists but has no budget_id, applies the default budget
    (if configured via litellm.max_end_user_budget_id).

    Args:
        end_user_id: The ID of the end user
        prisma_client: Database client instance
        user_api_key_cache: Cache for storing/retrieving data
        route: The request route
        parent_otel_span: Optional OpenTelemetry span for tracing
        proxy_logging_obj: Optional proxy logging object

    Returns:
        LiteLLM_EndUserTable if found, None otherwise
    NNo db connectedzend_user_id:{}r   )r   r   r   r   )r   rr   r   T)r   object_permissionr   includer   r|   rH   )rF   formatr   r   r   r   r   Zlitellm_endusertabler   r   r   r[   r   r   )r   r   r   rr   r   rd   _keycached_user_objZ
return_objresponse	_responser^   rH   rH   rL   get_end_user_objectw  sT   
r  	tag_namesc              
      s0  |du ri S | si S i }g }| D ]-}d| }|j |dI dH }	|	dur9t|	tr4tdi |	||< q|	||< q|| q|rz9|jjjdd|iiddidI dH }
|
D ]!}|j}d| }|j	|| d	I dH  tdi | ||< qVW |S  t
y } ztd
|  W Y d}~|S d}~ww |S )a%  
    Batch fetch multiple tag objects from cache and db.

    Optimizes for latency by:
    1. Fetching all cached tags in parallel
    2. Batch fetching uncached tags in one DB query

    Args:
        tag_names: List of tag names to fetch
        prisma_client: Prisma database client
        user_api_key_cache: Cache for storing tag objects
        parent_otel_span: Optional OpenTelemetry span for tracing
        proxy_logging_obj: Optional proxy logging object

    Returns:
        Dictionary mapping tag_name to LiteLLM_TagTable object
    Nztag:r   tag_nameinr   Tr   r  z)Error batch fetching tags from database: rH   )r   r[   r   r$   r   r   Zlitellm_tagtable	find_manyr
  r   rF   r   r\   )r	  r   r   r   rd   tag_objectsZuncached_tagsr
  r   Z
cached_tagZdb_tagsZdb_tagr^   rH   rH   rL   get_tag_objects_batch  sH   




r  r
  c                    s:   |du s	| du rdS t | g||||dI dH }|| S )a  
    Returns tag object from cache or db.

    Uses default cache TTL (same as end_user objects) to avoid drift.

    Args:
        tag_name: Name of the tag to fetch
        prisma_client: Prisma database client
        user_api_key_cache: Cache for storing tag objects
        parent_otel_span: Optional OpenTelemetry span for tracing
        proxy_logging_obj: Optional proxy logging object

    Returns:
        LiteLLM_TagTable object if found, None otherwise
    N)r	  r   r   r   rd   )r  rx   )r
  r   r   r   rd   r  rH   rH   rL   get_tag_object  s   
r  r   r   r%   c                    s   ddl m} |du rtd| du s|du rdS d| |}|j|dI dH }|dur3|di |S z2|jjjd| |did	d
idI dH }	|	du rNW dS |j||		 dI dH  |di |		 }
|
W S  tyv   t
d| | Y dS w )a  
    Returns team membership object if user is member of team.

    Do a isolated check for team membership vs. doing a combined key + team + user + team-membership check, as key might come in frequently for different users/teams. Larger call will slowdown query time. This way we get to cache the constant (key/team/user info) and only update based on the changing value (team membership).
    r   )r%   Nr   zteam_membership:{}:{}r   Zuser_id_team_id)r   r   r   Tr   r  z:Error getting team membership for user_id: %s, team_id: %srH   )litellm.proxy._typesr%   rF   r  r   r   Zlitellm_teammembershipr   r   r   r   	exception)r   r   r   r   r   rd   r%   r  Zcached_membership_objr  r  rH   rH   rL   get_team_membership;  s8   r  team_modelsc                    s   ddl m} |d u rdS | |v rdS |t |r|j| d t dkr4t|D ]\}}| v r3 dS q( fdd|D }| |v rCdS dS )Nr   defaultdictT)r]   c                    r   rH   rH   rI   mZaccess_groupsrH   rL   r     r   z)model_in_access_group.<locals>.<listcomp>F)collectionsr  r   get_model_access_groupsrh   	enumerate)rW   r  rX   r  idxr  filtered_modelsrH   r  rL   model_in_access_groupq  s&   r  r   last_db_access_timedb_cache_expiryc                 C   sP   t   }| |vr
dS ||  d durdS ||  d du r&|||   |kr&dS dS )zM
    Prevent calling db repeatedly for items that don't exist in the db.
    Tr   NFtime)r   r  r   current_timerH   rH   rL   _should_check_db  s   r$  r|   c                 C   s   |t   f|| < d S rG   r!  r   r|   r  rH   rH   rL   _update_last_db_access_time  s   r&  	rbac_role)ri   routesc                 C   sL   t ttt  |dg }|du rdS |D ]}|j| kr#t||  S qdS )zC
    Get the role based permissions from the general settings.
    Zrole_permissionsN)r	   r   r   r.   rx   r   r   )r'  ro   r   Zrole_based_permissionsZrole_based_permissionrH   rH   rL   _get_role_based_permissions  s   


r)  c                 C      t | |ddS )zH
    Get the models allowed for a user role.

    Used by JWT Auth.
    ri   r'  ro   r   r)  r'  ro   rH   rH   rL   get_role_based_models  s
   
r.  c                 C   r*  )z1
    Get the routes allowed for a user role.
    r(  r+  r,  r-  rH   rH   rL   get_role_based_routes  s
   r/  sso_user_id
user_emailc                    s   d}|dur| j jjd|iddidI dH }|du rK|durK| j jjd|ddiddidI dH }|durK|durKt| j jjd	|jid|id
 |S )a<  
    Checks if sso user is in db.

    Called when user id match is not found in db.

    - Check if sso_user_id is user_id in db
    - Check if sso_user_id is sso_user_id in db
    - Check if user_email is user_email in db
    - If not, create new user with user_email and sso_user_id and user_id = sso_user_id
    Nr0  organization_membershipsTr   r1  Zinsensitive)equalsmoder   )r   data)r   litellm_usertabler   
find_firstasynciocreate_taskupdater   )r   r0  r1  r  rH   rH   rL   _get_fuzzy_user_object  s(   r;  user_id_upsertcheck_db_onlyc	              
      s  | du rdS |s)|j | dI dH }	|	dur)t|	tr"tdi |	S t|	tr)|	S |du r1tdzd| }
t|
ttd}|r_|j	j
jd| iddid	I dH }|du r^t|||d
I dH }nd}|du r|rd| i}|durs||d< tjdur~|tj |j	j
j|ddidI dH }nt|jdurt|jdkrdd |jD }||_tdi t|}| }|j| |tdI dH  t|
|td |W S  ty } ztd| td|  d| d}~ww )z
    - Check if user id in proxy User Table
    - if valid, return LiteLLM_UserTable object with defined limits
    - if not, then raise an error
    Nr   r   z
user_id:{}r   r  r   r   r2  Tr   )r   r0  r1  r1  )r5  r  r   c                 S   s&   g | ]}|d urt di | qS )NrH   )r!   
model_dump)rI   Z
membershiprH   rH   rL   r   V  s
    z#get_user_object.<locals>.<listcomp>r   r%  rw   z$User doesn't exist in db. 'user_id'=z0. Create user via `/user/new` call. Got error - rH   )r   r[   r   r(   rF   r  r$  r  r   r   r6  r   r;  r   Zdefault_internal_user_paramsr:  creater2  rh   r?  r   r   r&  rV   r   )r   r   r   r<  r   rd   r0  r1  r=  r  db_access_time_keyshould_check_dbr  Znew_user_paramsZ_dumped_membershipsr  Zresponse_dictr^   rH   rH   rL   get_user_object	  s   





rC  c                    s   |j | |tdI d H  d S )Nr   )r   r   r   r|   r   rd   rH   rH   rL   _cache_management_objectv  s   rE  
team_tablec                    s0   d | }t |_t||||dI d H  d S )N
team_id:{}rD  )r  r"  last_refreshed_atrE  )r   rF  r   rd   r   rH   rH   rL   _cache_team_object  s   

rI  hashed_tokenuser_api_key_objc                    s*   | }t   |_t||||dI d H  d S )NrD  )r"  rH  rE  )rJ  rK  r   rd   r   rH   rH   rL   _cache_key_object  s   
rL  c                    s8   | }|j |d |d ur|jjj|dI d H  d S d S )Nr   )delete_cacheinternal_usage_cache
dual_cacheasync_delete_cache)rJ  r   rd   r   rH   rH   rL   _delete_cache_key_object  s   rQ  team_id_upsertc           	         s~   |j jjd| idI d H }|d u r=|r=ddlm} t| d}tddid}ttj	d	}||||d
I d H }t
di |}|S )Nr   r   r   )new_team)r   rQ   http)scope)r   )r5  http_requestr   rH   )r   litellm_teamtabler   Z1litellm.proxy.management_endpoints.team_endpointsrS  r+   r   r0   r*   r   r&   )	r   r   rR  r  rS  Znew_team_dataZmock_requestZsystem_admin_userZcreated_team_dictrH   rH   rL   _get_team_db_check  s    
rX  c                    s   |j jjd| idI d H S )Nr   r   )r   rW  r   )r   r   rH   rH   rL   _get_team_object_from_db  s   rY  c                    s   |}t |||d}	|	rt| ||dI d H }
nd }
|
d u rttd	i |
 }|jr`|js`zt|j||d |dI d H |_W n! ty_ } zt	d|  d|j d|  W Y d }~nd }~ww t
| |||dI d H  t|||d |S )
Nr>  )r   r   rR  object_permission_idr   r   r   rd   *Failed to load object_permission for team  with object_permission_id=rZ   )r   rF  r   rd   r%  rH   )r$  rX  rF   r'   r   r[  r   get_object_permissionr   r\   rI  r&  )r   r   r   r  r   rd   r   rR  rA  rB  r  r  r^   rH   rH   rL   (_get_team_object_from_user_api_key_cache  sT   
r_  c                    sz   d }|d ur|j jr|j jj| |dI d H }|d u r$|j| dI d H }|d ur;t|tr4tdi |S t|tr;|S d S )N)r   r   r   rH   )rN  rO  r   r[   r   r'   )r   rd   r   r   cached_team_objrH   rH   rL   _get_team_object_from_cache  s"   


ra  check_cache_onlyc           
   
      s   |du r	t dd| }|s/t||||dI dH }	|	dur!|	S |r/tddd|  did	zt| |||tt||d
I dH W S  t yR   tddd|  did	w )z
    - Check if team id in proxy Team Table
    - if valid, return LiteLLM_TeamTable object with defined limits
    - if not, then raise an error

    Raises:
        - HTTPException: If team doesn't exist in db or cache (status_code=404)
    NFNo DB Connected. See - https://docs.litellm.ai/docs/proxy/virtual_keysrG  r   rd   r   r     rC   z:Team doesn't exist in cache + check_cache_only=True. Team=rO   r   )r   r   r   rd   r  r   r   rR  zTeam doesn't exist in db. Team=z#. Create team via `/team/new` call.)rF   r  ra  r
   r_  r  r   )
r   r   r   r   rd   rb  r=  rR  r   r`  rH   rH   rL   get_team_object4  sN   

rf  access_group_idaccess_group_tablec                    s&   d | }|j||tdI d H  d S )Naccess_group_id:{}r   )r  r   r   )rg  rh  r   rd   r   rH   rH   rL   _cache_access_objectv  s   
rj  c                    s>   d | }|j|d |d ur|jjj|dI d H  d S d S )Nri  r   )r  rM  rN  rO  rP  )rg  r   rd   r   rH   rH   rL   _delete_cache_access_object  s   
rk  c           	   
      s  |du r	t dd| }|j|dI dH }|dur.t|tr'tdi |S t|tr.|S z4|jjjd| idI dH }|du rLt	ddd	|  d
idtdi | }t
| |||dI dH  |W S  t	yj     t y } ztd|  t	ddd	|  d| idd}~ww )a  
    - Check if access_group_id in proxy AccessGroupTable
    - Always checks cache first, then DB only when not found in cache
    - if valid, return LiteLLM_AccessGroupTable object
    - if not, then raise an error

    Unlike get_team_object, this has no check_cache_only or check_db_only flags;
    it always follows cache-first-then-db semantics.

    Raises:
        - HTTPException: If access group doesn't exist in db or cache (status_code=404)
    Nrc  ri  r   rg  r   re  rC   z/Access group doesn't exist in db. Access group=rO   r   )rg  rh  r   rd   z2Error getting access group for access_group_id: %s	. Error: rH   )rF   r  r   r[   r   r   r   Zlitellm_accessgrouptabler   r
   rj  r   r  )	rg  r   r   rd   r   Zcached_access_objr  r  r^   rH   rH   rL   get_access_object  sX   


rm  
team_aliasr?   c                    s  |du r	t dd| }t||||dI dH }|dur|S z|jjjd| idI dH }|s;tddd	|  d
idt|dkrMtddd|  did|d }tdi |	 }	|	j
r|	jszt|	j
||||dI dH |	_W n" t y }
 ztd|	j d|	j
 d|
  W Y d}
~
nd}
~
ww |j||	tdI dH  d|	j}|j||	tdI dH  |	W S  ty     t y }
 ztd|  tddd|  dt|
 idd}
~
ww )a  
    Look up a team by its team_alias (name) in the database.

    Args:
        team_alias: The team name/alias to look up
        prisma_client: Database client
        user_api_key_cache: Cache for storing results
        parent_otel_span: Optional OpenTelemetry span
        proxy_logging_obj: Optional proxy logging object

    Returns:
        LiteLLM_TeamTableCachedObj: The team object if found

    Raises:
        HTTPException: If team doesn't exist or multiple teams have the same alias
    Nrc  zteam_alias:{}rd  rn  r   re  rC   zTeam with alias 'z8' doesn't exist in db. Create team via `/team/new` call.r   r<     z!Multiple teams found with alias 'zJ'. Please use team_id_jwt_field instead or ensure team aliases are unique.r   rZ  r\  r]  rZ   r   rG  z"Error looking up team by alias: %s  z Error looking up team by alias '': rH   )rF   r  ra  r   rW  r  r
   rh   r'   r?  r[  r   r^  r   r\   r   r   r   r  rP   )rn  r   r   r   rd   r   r`  teamsteamZteam_objr^   Zteam_id_cache_keyrH   rH   rL   get_team_object_by_alias  s   
rt  	org_aliasc                    sf  |du r	t dd| }|j|dI dH }|dur.t|tr'tdi |S t|tr.|S z[|jjjd| idI dH }|sJt	ddd	|  d
idt
|dkr\t	ddd|  did|d }tdi | }	|j||	 tdI dH  |jd|	j|	 tdI dH  |	W S  t	y     t y }
 ztd|  t	ddd|  dt|
 idd}
~
ww )a	  
    Look up an organization by its organization_alias in the database.

    Args:
        org_alias: The organization name/alias to look up
        prisma_client: Database client
        user_api_key_cache: Cache for storing results
        parent_otel_span: Optional OpenTelemetry span
        proxy_logging_obj: Optional proxy logging object

    Returns:
        LiteLLM_OrganizationTable if found, None otherwise

    Raises:
        HTTPException: If organization not found or multiple orgs have the same alias
    Nrc  zorg_alias:{}r   Zorganization_aliasr   re  rC   zOrganization with alias 'zH' doesn't exist in db. Create organization via `/organization/new` call.r   r<   ro  z)Multiple organizations found with alias 'zQ'. Please use org_id_jwt_field instead or ensure organization aliases are unique.r   r   	org_id:{}z*Error looking up organization by alias: %srp  z(Error looking up organization by alias 'rq  rH   )rF   r  r   r[   r   r"   r   litellm_organizationtabler  r
   rh   r?  r   r   organization_idr   r  rP   )ru  r   r   r   rd   r   cached_org_objZorgsorgZorg_objr^   rH   rH   rL   get_org_object_by_aliasL  sl   



r{  c                   @   s^   e Zd ZededefddZe	ddedee defddZed	edee	 fd
dZ
dS )ExperimentalUIJWTToken	user_inforD   c                 C   s   ddl m} ddlm} | jd u rtdt |dd }|dd d d	 }td
d
d
t	j
d|| jd| jd t| jd}||jddS )Nr   	timedeltaencrypt_value_helperz/User role is required for experimental UI login
   )minutes%Y-%m-%dT%H:%M:%S.%f+00:00zui-tokenr@   r   )tokenkey_name	key_aliasr   Z	rpm_limitexpiresr   r   ri   max_parallel_requestsr   TZexclude_none)datetimer  0litellm.proxy.common_utils.encrypt_decrypt_utilsr  r   rF   r;   strftimer0   r   max_ui_session_budgetr   ri   r*   model_dump_json)r}  r  r  expiration_timer  rc   rH   rH   rL   (get_experimental_ui_login_jwt_auth_token  s(   
z?ExperimentalUIJWTToken.get_experimental_ui_login_jwt_auth_tokenNr   c                 C   s   ddl m} ddlm} | jdu rtdt |td }|ddd d	 }|}|du rDt	| d
rD| j
rDt| j
dkrB| j
d nd}tttttj|| j|| jdt| jd
}||jddS )a  
        Generate a JWT token for CLI authentication with configurable expiration.

        The expiration time can be controlled via the LITELLM_CLI_JWT_EXPIRATION_HOURS
        environment variable (defaults to 24 hours).

        Args:
            user_info: User information from the database
            team_id: Team ID for the user (optional, uses user's team if available)

        Returns:
            Encrypted JWT token string
        r   r~  r  Nz'User role is required for CLI JWT login)hoursr  r  r  rr  )
r  r  r  r   r  r   r   ri   r  r   Tr  )r  r  r  r  r   rF   r;   r   r  ry   rr  rh   r0   r   r   r  r   ri   r*   r  )r}  r   r  r  r  r  Z_team_idrc   rH   rH   rL   get_cli_jwt_auth_token  s,   
z-ExperimentalUIJWTToken.get_cli_jwt_auth_tokenrJ  c              
   C   s   dd l }ddlm} ddlm} || ddd}|d u rd S z|d
i ||W S  tyA } ztd|  d| d	| d }~ww )Nr   )r0   )decrypt_value_helperZui_hash_keyr\   )r   Zexception_typezInvalid hash key. Hash key=z. Decrypted token=rl  rH   )jsonZ$litellm.proxy.auth.user_api_key_authr0   r  r  loadsrF   )rJ  r  r0   r  Zdecrypted_tokenr^   rH   rH   rL   get_key_object_from_ui_hash_key  s    z6ExperimentalUIJWTToken.get_key_object_from_ui_hash_keyrG   )rR   
__module____qualname__staticmethodr(   rP   r  r   r  r0   r  rH   rH   rH   rL   r|    s$    3r|  c              
      s   z|j | d||dI dH W S  tyd } zJt|r_d}t|drKt|dd}t|ttfs1d}t|dd	}t|ttfs@d	}|j	d
||dI dH }|r_|j | d||dI dH W  Y d}~S  d}~ww )zY
    Fetch key object from DB and retry once if a DB connection error can be healed.
    Zcombined_view)r  Z
table_namer   rd   NFattempt_db_reconnectZ"_db_auth_reconnect_timeout_secondsg       @Z'_db_auth_reconnect_lock_timeout_secondsg?Z"auth_get_key_object_lookup_failure)reasonZtimeout_secondsZlock_timeout_seconds)
get_datarF   r2   Zis_database_transport_errorry   r   r[   intfloatr  )rJ  r   r   rd   r^   Zdid_reconnectZauth_reconnect_timeoutZauth_reconnect_lock_timeoutrH   rH   rL   (_fetch_key_object_from_db_with_reconnect  sJ   	

r  jwt_claim_namejwt_claim_valuec                    s2   |j jj| |dddI dH }|dur|jS dS )z
    Lookup a JWT-to-virtual-key mapping from the database.

    Returns the hashed token (str) if a matching active mapping is found, else None.
    T)r  r  	is_activer   N)r   Zlitellm_jwtkeymappingr7  r  )r  r  r   mappingrH   rH   rL   get_jwt_key_mapping_objectA  s   r  c              
      s<  |du r	t d| }|j|dI dH }|dur+t|tr$tdi |S t|tr+|S |r5t d| dt| |||dI dH }|du rQtd| tj	dt
jd	tdi |jd
d}	|	jr|	jszt|	j||||dI dH |	_W n t y }
 ztd|	j d|
  W Y d}
~
nd}
~
ww t| |	||dI dH  |	S )z
    - Check if team id in proxy Team Table
    - if valid, return LiteLLM_TeamTable object with defined limits
    - if not, then raise an error
    Nrc  r   z8Key doesn't exist in cache + check_cache_only=True. key=rO   )rJ  r   r   rd   zvAuthentication Error, Invalid proxy server token passed. key={}, not found in db. Create key via `/key/generate` call.r   r   Tr  rZ  zCFailed to load object_permission for key with object_permission_id=rZ   )rJ  rK  r   rd   rH   )rF   r   r[   r   r0   r  r-   r  r,   Ztoken_not_found_in_dbr   r   r?  r[  r   r^  r   r\   rL  )rJ  r   r   r   rd   rb  r   Zcached_key_objZ_valid_tokenr  r^   rH   rH   rL   get_key_objectX  sp   


	r  r[  c                    s   |du r	t dd| }|j|dI dH }|dur.t|tr'tdi |S t|tr.|S z+|jjjd| idI dH }|du rCW dS |j	||
 tdI dH  tdi | W S  t yc   Y dS w )	z
    - Check if object permission id in proxy ObjectPermissionTable
    - if valid, return LiteLLM_ObjectPermissionTable object
    - if not, then raise an error
    Nrc  zobject_permission_id:{}r   r[  r   r   rH   )rF   r  r   r[   r   r    r   litellm_objectpermissiontabler   r   r?  r   )r[  r   r   r   rd   r   Zcached_obj_permissionr  rH   rH   rL   r^    s6   


r^  org_idinclude_budget_tablec           
         s  |du r	t dt| tsdS d| }|rd| }|j|d}|dur9t|tr2tdi |S t|tr9|S z8dd| ii}|rHdd	i|d
< |jjj	di |I dH }	|	du r[t |j
|t|	drg|	 n|	tdI dH  |	W S  t y   t d|  dw )a  
    - Check if org id in proxy Org Table
    - if valid, return LiteLLM_OrganizationTable object
    - if not, then raise an error

    Args:
        org_id: Organization ID to look up
        prisma_client: Database client
        user_api_key_cache: Cache for storing results
        parent_otel_span: Optional OpenTelemetry span
        proxy_logging_obj: Optional proxy logging object
        include_budget_table: If True, includes litellm_budget_table in the query
    Nrc  rv  zorg_id:{}:with_budgetr   r   rx  r   Tr  r?  r   z/Organization doesn't exist in db. Organization=z3. Create organization via `/organization/new` call.rH   )rF   r[   rP   r  r   r   r"   r   rw  r   r   ry   r?  r   )
r  r   r   r   rd   r  r   ry  Zquery_kwargsr  rH   rH   rL   get_org_object  sH   





r  access_group_idsresource_field)access_model_namesaccess_mcp_server_idsaccess_agent_idsc              	      s   | sg S |du s|du r+ddl m} ddl m} ddl m} |p"|}|p&|}|p*|}|du r1g S g }| D ](}	zt|	|||dI dH }
|t|
|g  W q5 ty]   t	d|	| Y q5w t
t|S )a]  
    Fetch access groups by their IDs (from cache or DB) and collect
    the specified resource field across all of them.

    Args:
        access_group_ids: List of access group IDs to fetch
        resource_field: Which resource list to extract from each access group
            - "access_model_names": model names (for model access checks)
            - "access_mcp_server_ids": MCP server IDs (for MCP access checks)
            - "access_agent_ids": agent IDs (for agent access checks)
        prisma_client: Optional PrismaClient (lazy-imported from proxy_server if None)
        user_api_key_cache: Optional DualCache (lazy-imported from proxy_server if None)
        proxy_logging_obj: Optional ProxyLogging (lazy-imported from proxy_server if None)

    Returns:
        Deduplicated list of resource identifiers from all resolved access groups.
    Nr   r   )rd   )r   )rg  r   r   rd   z5Could not fetch access group %s for resource field %s)r   r   rd   r   rm  r   r   rF   r   r\   r   r   )r  r  r   r   rd   Z_prisma_clientZ_proxy_logging_objZ_user_api_key_cache	resourcesZag_idagrH   rH   rL   !_get_resources_from_access_groups!	  s<   r  c                       t | d|||dI dH S )z{
    Collect model names from unified access groups.
    Models are matched by model name for backwards compatibility.
    r  r  r  r   r   rd   Nr  r  r   r   rd   rH   rH   rL   _get_models_from_access_groups`	     
r  c                    r  )z_
    Collect MCP server IDs from unified access groups.
    MCPs are matched by server ID.
    r  r  Nr  r  rH   rH   rL   &_get_mcp_server_ids_from_access_groupss	  r  r  c                    r  )z[
    Collect agent IDs from unified access groups.
    Agents are matched by agent ID.
    r  r  Nr  r  rH   rH   rL   !_get_agent_ids_from_access_groups	  r  r  ri   r   c           
         s   ddl m} |t |r|j| |d t dkr-|d ur-t|D ]\}}| v r, dS q! fdd|D }t| |dr>dS t| |drFdS d	}	t|dkrTt|dksXd
|v rZd}	tj	j
|v rbd}	| d urp| |vrp|	d	u rpd	S dS )Nr   r  )r]   r   Tc                    r   rH   rH   r  r  rH   rL   r   	  r   z._check_model_access_helper.<locals>.<listcomp>rW   r   rW   allowed_model_listF*)r  r  r   r  rh   r  _model_in_team_aliases+_model_matches_any_wildcard_pattern_in_listr/   Zall_proxy_modelsr|   )
rW   rX   ri   r   r   r  r  r  r  Zall_model_accessrH   r  rL   _check_model_access_helper	  s8    r  rw   object_type)rw   rs  r   rz  projectfallback_depthTc           
   
   C   s   |t krtd| t| tr$| D ]}t|||||||d d qdS | g}| tjv r5|tj|   n|rH| |j	v rH|
| }	|	rH||	 |D ]}t|||||drX dS qJt| d| d| d|  tj|d	d
tjd)a  
    Checks if token can call a given model

    Args:
        - model: str
        - llm_router: Optional[Router]
        - models: List[str]
        - team_model_aliases: Optional[Dict[str, str]]
        - object_type: Literal["user", "team", "key", "org"]. We use the object type to raise the correct exception type

    Returns:
        - True: if token allowed to call model

    Raises:
        - Exception: If token not allowed to call model
    zGUnable to parse model, max fallback depth exceeded - received model: {}r<   )rW   rX   ri   r   r   r  r  T)rW   rX   ri   r   r   z# not allowed to access model. This z can only access models=z. Tried to access )r  rW   r   )r   rF   r  r[   r   _can_object_call_modelr   Zmodel_alias_mapr   Zmodel_group_aliasZ_get_model_from_aliasr  r-   r,   Z&get_model_access_error_type_for_objectr   r   )
rW   rX   ri   r   r   r  r  r  Zpotential_modelsra   rH   rH   rL   r  	  sV   
	


	r  c                 C   s   |r| |v rdS dS )a  
    Returns True if `model` being accessed is an alias of a team model

    - `model=gpt-4o`
    - `team_model_aliases={"gpt-4o": "gpt-4o-team-1"}`
        - returns True

    - `model=gp-4o`
    - `team_model_aliases={"o-3": "o3-preview"}`
        - returns False
    TFrH   r  rH   rH   rL   r  
  s   r  llm_model_listc                    sn   zt | ||j|j|jddW S  ty6   |jpg }|r5t|dI dH }|r5t | |||j|jdd Y S  w )aM  
    Checks if token can call a given model

    1. First checks native key-level model permissions (current implementation)
    2. If not allowed natively, falls back to access_group_ids on the key

    Returns:
        - True: if token allowed to call model

    Raises:
        - Exception: If token not allowed to call model
    r   rW   rX   ri   r   r   r  r  N)r  ri   r   r   r-   r  r  )rW   r  rc   rX   Zkey_access_group_idsmodels_from_groupsrH   rH   rL   can_key_call_model(
  s6   

r  
org_objectc                 C   s   t | ||r|jng |ddS )z@
    Returns True if the team can access a specific model.

    rz  )rW   rX   ri   r   r  r  ri   )rW   r  rX   r   rH   rH   rL   can_org_access_modelV
  s   
r  c                    s   zt | ||r
|jng ||r|jndddW S  tyD   |r$|jp#g ng }|rCt|dI dH }|rCt | ||||r<|jnddd Y S  w )z
    Returns True if the team can access a specific model.

    1. First checks native team-level model permissions (current implementation)
    2. If not allowed natively, falls back to access_group_ids on the team
    Nrs  r  r  )r  ri   r   r-   r  r  )rW   r   rX   r   Zteam_access_group_idsr  rH   rH   rL   r   i
  s<    
r   c                 C   s    t | ||r|jddS g ddS )zr
    Returns True if the project can access a specific model.

    Raises ProxyException if access is denied.
    r  rW   rX   ri   r  r  re   rH   rH   rL   rj   
  s   
rj   c                    sH   |d u rdS t jj|jv rtd|  tjdtjdt	| ||jddS )NTzeUser not allowed to access model. No default model access, only team models allowed. Tried to access rW   r   rw   r  )
r/   Zno_default_modelsr|   ri   r-   r,   Zkey_model_access_deniedr   r   r  r   rH   rH   rL   r   
  s    r   
user_modelc                    s*   t | dddgd||ddI dH  dS )	z
    Try to route the fallback model request.

    Validate if it can't be routed.

    Help catch invalid fallback models.
    rw   zWho was Alexander?)r   content)rW   messagesZacompletion)r5  rX   r  Z
route_typeNTr5   )rW   rX   r  rH   rH   rL   is_valid_fallback_model
  s   

r  c                    s   | j durD| jdurFd}|dur|j}t| j| j | j| j| j| j| j|| j	t
jd
}t|jd|d | j | jkrHtj| j | jddS dS dS )z
    Raises:
        BudgetExceededError if the token is over it's max budget.
        Triggers a budget alert if the token is over it's max budget.

    N)
r  r   r   soft_budgetr   r   rx  r1  r  event_groupZtoken_budgetrQ   r}  r   )r   r   r1  r   r  r  r   r   r  r  r   KEYr8  r9  budget_alertsr   r   )rc   rd   r   r1  	call_inforH   rH   rL   _virtual_key_max_budget_check
  s<   !r  c                    s   | j r?| j| j krAtd| j| j| j  t| j| j| j| j | j| j| j	| j
|r+|jnd| jtjd}t|jd|d dS dS dS )zI
    Triggers a budget alert if the token is over it's soft budget.

    z:Crossed Soft Budget for token %s, spend %s, soft_budget %sNr  r   r   r  r   r   rn  rx  r1  r  r  r  r  )r  r   r   r\   r  r   r   r   r   rn  r  r1  r  r   r  r8  r9  r  )rc   rd   r   r  rH   rH   rL   _virtual_key_soft_budget_check	  s8   
r  c                    s   | j durV| jdurX| jdkrZ| j t }| j|kr\| j| j k r^td| j| j| j | t| j| j| j | j| j| j	| j
| j|rB|jnd| jtjd}t|jd|d dS dS dS dS dS dS )z
    Triggers a budget alert if the token has reached EMAIL_BUDGET_ALERT_MAX_SPEND_ALERT_PERCENTAGE
    (default 80%) of its max budget.
    This is a warning alert before the token actually exceeds the max budget.

    Nr   z\Reached Max Budget Alert Threshold for token %s, spend %s, max_budget %s, alert_threshold %sr  Zmax_budget_alertr  )r   r   r   r   r\   r  r   r  r   r   rn  r  r1  r  r   r  r8  r9  r  )rc   rd   r   Zalert_thresholdr  rH   rH   rL   #_virtual_key_max_budget_alert_check0  sN   



r  c           	         s   | durW| j durY|dur[|dur]|jdur_t|j| j |||dI dH }|dura|jdurc|jjdure|jj}|jp<d}||krgtj||d|j d| j  d| d| ddS dS dS dS dS dS dS dS dS )	z>Check if team member is over their max budget within the team.N)r   r   r   r   rd   g        zBudget has been exceeded! User=z	 in Team= Current cost: , Max budget: r   )r   r   r  r   r   r   r   r   )	r   r   rc   r   r   rd   Zteam_membershipZteam_member_budgetZteam_member_spendrH   rH   rL   r   g  sB   


	

r   c              
      s   | durK| j durM| jdurO| j| j krQ|r5t|j| j| j |j|j|j|jtj	d}t
|jd|d tj| j| j d| j d| j d| j  ddS dS dS dS )	z
    Check if the team is over it's max budget.

    Raises:
        BudgetExceededError if the team is over it's max budget.
        Triggers a budget alert if the team is over it's max budget.
    Nr  r   r   r   r   rn  rx  r  Zteam_budgetr  zBudget has been exceeded! Team=r  r  r   )r   r   r   r  r   r   rn  r  r   r   r8  r9  r  r   r   )r   rc   rd   r  rH   rH   rL   r     s>   


r   c                    sH  | dur| j dur| jdur| j| j krtd| j| j| j  |rd}| jdurat| jtra| jd}|durat|t	rFdd |D }nt|t
rUdd |dD }|r_dd |D }nd}|du skt|d	krttd
| j dS t|j| j| j| j |j|j|j|jd|jtj|d}t|jd|d dS dS dS dS dS dS )zG
    Triggers a budget alert if the team is over it's soft budget.
    Nz9Crossed Soft Budget for team %s, spend %s, soft_budget %sZsoft_budget_alerting_emailsc                 S   s"   g | ]}t |tr| r|qS rH   )r[   rP   striprI   emailrH   rH   rL   r     s    z+_team_soft_budget_check.<locals>.<listcomp>c                 S   s   g | ]
}|  r|  qS rH   )r  r  rH   rH   rL   r     s    ,c                 S   s   g | ]}|r|qS rH   rH   r  rH   rH   rL   r     r   r   zoSkipping team soft budget alert for team %s: no alert_emails configured in metadata.soft_budget_alerting_emails)r  r   r   r  r   r   rn  rx  r1  r  r  alert_emailsr  r  )r  r   r   r\   r   r~   r[   r   rx   r   rP   splitrh   r   r  r   r   rn  r  r  r   r   r8  r9  r  )r   rc   rd   r  Zsoft_budget_alert_emailsr  rH   rH   rL   r     s|   	



r   c              
      s   | du rdS d}| j dur| j j}|durS| jdurU| j|krW|r?t|j| j||j|j|j|jt	j
d}t|jd|d tj| j|d| j d| j d| ddS dS dS )	z
    Check if the project is over its max budget.

    Raises:
        BudgetExceededError if the project is over its max budget.
        Triggers a budget alert if the project is over its max budget.
    Nr  Zproject_budgetr  z"Budget has been exceeded! Project=r  r  r   )r   r   r   r   r  r   r   rn  r  r   PROJECTr8  r9  r  r   r   rg   )r`   rc   rd   r   r  rH   rH   rL   rk     sD   



rk   c                    s   | du rdS d}| j dur| j j}|durL| jdurN| j|krPtd| j| j| |rRt|j| jd||j|j	|j
|jtjd	}t|jd|d dS dS dS dS dS )zy
    Triggers a budget alert if the project is over its soft budget.

    Mirrors _team_soft_budget_check() pattern.
    Nz<Crossed Soft Budget for project %s, spend %s, soft_budget %s)	r  r   r   r  r   r   rn  rx  r  r  r  )r   r  r   r   r\   rg   r   r  r   r   rn  r  r   r  r8  r9  r  )r`   rc   rd   r  r  rH   rH   rL   rl   <  sJ   



rl   rg   c                    s   |du rdS d | }|j|dI dH }|dur,t|tr%td	i |S t|tr,|S |jjjd| iddidI dH }|du rBdS td	i | }t		 |_
t||||dI dH  |S )
z
    Fetch project object from cache or DB.

    Follows get_team_object() caching pattern with TTL and last_refreshed_at.

    Returns LiteLLM_ProjectTableCachedObj or None if not found.
    Nzproject_id:{}r   rg   r   Tr   rD  rH   )r  r   r[   r   r#   r   Zlitellm_projecttabler   r?  r"  rH  rE  )rg   r   r   rd   r   Z
cached_objZproject_rowZproject_objrH   rH   rL   get_project_objectl  s2   



r  c           	   
      s$  | du s	|du rdS d}| j dur| j }n|dur"|jdur"|j}|du r(dS zt||||ddI dH }W n
 ty@   Y dS w |du rGdS d}|jdurR|jj}|du sZ|dkr\dS |j|krt| j|j|| j	| j
| j|tjd}t|jd|d tj|j|d| d	|j d
| ddS )a`  
    Check if the organization is over its max budget.

    This function checks the organization budget using:
    1. First, tries to use valid_token.org_id (if key has organization_id set)
    2. Falls back to team_object.organization_id (if key doesn't have org_id but team does)

    This ensures organization budget checks work even when keys don't have organization_id
    set directly, as long as their team belongs to an organization.

    Raises:
        BudgetExceededError if the organization is over its max budget.
        Triggers a budget alert if the organization is over its max budget.
    NT)r  r   r   rd   r  r   r  Zorganization_budgetr  z'Budget has been exceeded! Organization=r  r  r   )r  rx  r  rF   r   r   r   r   r  r   r   rn  r   ZORGANIZATIONr8  r9  r  r   r   )	rc   r   r   r   rd   r  Z	org_tableZorg_max_budgetr  rH   rH   rL   r     sf   



r   c           
   
      s   ddl m} |du rdS || d}|sdS t||||dI dH }|D ]:}||}	|	du r/q#|	jdur]|	jjdur]|	jdur]|	j|	jjkr]tj|	j|	jjd| d|	j d|	jj d	q#dS )
z
    Check if any tags in the request are over their max budget.

    Raises:
        BudgetExceededError if any tag is over its max budget.
        Triggers a budget alert if any tag is over its max budget.
    r   )get_tags_from_request_bodyN)rq   )r	  r   r   rd   zBudget has been exceeded! Tag=r  r  r   )	Z-litellm.proxy.common_utils.http_parsing_utilsr  r  rx   r   r   r   r   r   )
rq   r   r   rd   rc   r  r   r  r
  Z
tag_objectrH   rH   rL   r     s8   



r   allowed_model_patternc                 C   s0   d|v rd| dd d}tt|| S dS )a~  
    Check if a model matches an allowed pattern.
    Handles exact matches and wildcard patterns.

    Args:
        model (str): The model to check (e.g., "bedrock/anthropic.claude-3-5-sonnet-20240620")
        allowed_model_pattern (str): The allowed pattern (e.g., "bedrock/*", "*", "openai/*")

    Returns:
        bool: True if model matches the pattern, False otherwise
    r  ^z.*$F)replaceboolrer   )rW   r  r   rH   rH   rL   is_model_allowed_by_pattern'  s   r  r  c                    s8   t  fdd|D rdS t  fdd|D rdS dS )ae  
    Returns True if a model matches any wildcard pattern in a list.

    eg.
    - model=`bedrock/us.amazon.nova-micro-v1:0`, allowed_models=`bedrock/*` returns True
    - model=`bedrock/us.amazon.nova-micro-v1:0`, allowed_models=`bedrock/us.*` returns True
    - model=`bedrockzzzz/us.amazon.nova-micro-v1:0`, allowed_models=`bedrock/*` returns False
    c                 3   $    | ]}t |ot |d V  qdS rW   r  N)_is_wildcard_patternr  rI   r  rW   rH   rL   rM   F      
z>_model_matches_any_wildcard_pattern_in_list.<locals>.<genexpr>Tc                 3   r  r  )r  3_model_custom_llm_provider_matches_wildcard_patternr  r  rH   rL   rM   O  r  F)rT   r  rH   r  rL   r  :  s   r  c                 C   sB   zt | d\} }}}W n
 ty   Y dS w t| d|  |dS )z
    Returns True for this scenario:
    - `model=gpt-4o`
    - `allowed_model_pattern=openai/*`

    or
    - `model=claude-3-5-sonnet-20240620`
    - `allowed_model_pattern=anthropic/*`
    r  F/r  )r   rF   r  )rW   r  Zcustom_llm_providerr   rH   rH   rL   r  [  s   r  c                 C   s   d| v S )zb
    Returns True if the pattern is a wildcard pattern.

    Checks if `*` is in the pattern.
    r  rH   )r  rH   rH   rL   r  r  s   r  c                    s   ddl m} |du rtd dS tjdu rtd dS tjj| | ddd}|du r5td	 dS |durW|jdurW|j	j
jd
|jidI dH }|durWtd||d |dury|jdury|j	j
jd
|jidI dH }|durytd||d dS )z
    Checks if the object (key, team, org) has access to the vector store.

    Raises ProxyException if the object (key, team, org) cannot access the specific vector store.
    r   r  Nz;Prisma client not found, skipping vector store access checkTzCVector store registry not found, skipping vector store access checkr   )Znon_default_paramsr   zAVector store to run not found, skipping vector store access checkr[  r   r   )r  vector_store_ids_to_runobject_permissionsrs  )r   r   r   r\   r   Zvector_store_registryZget_vector_store_ids_to_runrx   r[  r   r  r   _can_object_call_vector_stores)rq   r   rc   r   r  Zkey_object_permissionZteam_object_permissionrH   rH   rL   r   {  sV   



r   )r   rs  rz  r  r  c                 C   sj   |du rdS |j du rdS t|j dkrdS |D ]}||j vr2td| d|j  t| dtjdqdS )zg
    Raises ProxyException if the object (key, team, org) cannot access the specific vector store.
    NTr   z9User not allowed to access vector store. Tried to access z. Only allowed to access Zvector_storer   )Zvector_storesrh   r-   r,   Z-get_vector_store_access_error_type_for_objectr   r   )r  r  r  Zvector_store_idrH   rH   rL   r    s$   


r  )FNrG   )NN)NNNNN)NNN)NNF)NNrw   r   )__doc__r8  r  r"  typingr   r   r   r   r   r   r   r	   Zfastapir
   r   r   Zpydanticr   r   Zlitellm._loggingr   Zlitellm.caching.cachingr   Zlitellm.caching.dual_cacher   Zlitellm.constantsr   r   r   r   r   r   r   Z1litellm.litellm_core_utils.get_llm_provider_logicr   r  r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   Zlitellm.proxy.auth.route_checksr1   Z"litellm.proxy.db.exception_handlerr2   Z-litellm.proxy.guardrails.tool_name_extractionr3   r4   Zlitellm.proxy.route_llm_requestr6   Zlitellm.proxy.utilsr7   r8   r9   Zlitellm.routerr:   Zlitellm.utilsr;   Zauth_checks_organizationr=   Z
auth_utilsr>   Zopentelemetry.tracer?   _Spanr  r   r   r|   Zmanagement_routesZ
all_routesrP   rF   rV   r  r_   rn   r   r}   r   r  r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r  r$  r&  r)  r.  r/  r;  rC  rE  rI  rL  rQ  rX  rY  r_  ra  rf  rj  rk  rm  rt  r{  r|  r  r  r  r^  r  r  r  r  r  r  r  r  r  r  r   rj   r   r  r  r  r  r   r   r   rk   rl   r  r   r   r  r  r  r  r   r  rH   rH   rH   rL   <module>   s  ($h

E
-




@	

 Y




"

;
+
X
D%5










*	
l



	
=
#	
F

Im[l
-R/J
A



6
J

2

*



6
*
7
(
+
Q
1
4
/
W5!	E