o
    ưic                    @   s  d Z ddlZddlZddlZddlmZmZ ddlmZmZm	Z	m
Z
mZmZmZ ddlZddlmZmZmZ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%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<m=Z=m>Z>m?Z?m@Z@mAZAmBZB ddlCmDZDmEZEmFZFmGZGmHZH ddlImJZJ ddlKmLZLmMZMmNZNmOZOmPZPmQZQmRZR ddlSmTZT ddlUmVZVmWZW ddlXmYZY ddlZm[Z[m\Z\ ddl]m^Z^m_Z_ ddl`maZa ddlbmcZc ddldmeZemfZfmgZgmhZhmiZimjZj e ZkG dd dZldemdeBdenfddZo	dde^de	em de
em de	e) fd d!Zpd"e	e* d#ee1eAf d$e
eq d%e
eq d&eemeqf d'eemeqf d(emddfd)d*Zrd"e	e* d#ee1eAf d$e
eq d%e
eq d(emddfd+d,Zsd"e	e* d-e'd#ee1eAf ddfd.d/Ztd"e	e* d-e'd#ee1eAf ddfd0d1Zud-e'd#ee1eAf de^ddfd2d3Zvd#ee1eAf deBde^d4eddf
d5d6Zwekjxd7d8geeJge*d9e\eeJedd:d;fd#e1d<edeBd=e
em fd>d?Zyd@e*dAezdemd=e
em deBdBemddfdCdDZ{d#eAdEe
em de^deBdBemde
em fdFdGZ|dHemd@edIe
ea dedef
dJdKZ}dLe*dMe(dIeadenfdNdOZ~ekjxdPd8geeJgdQe\eeJedd:d;fd#eAd<edeBd=e
em fdRdSZd#eAdAezddfdTdUZdVezd@e*dezfdWdXZdYee0e	e0 f dZenfd[d\Zde
e^ d#e;dZenfd]d^Zd#e;d@e*fd_d`ZdeBdae*ddfdbdcZd#e;dae*de^deBdBemdee	e, e	e) f fdddeZd#e;dae*dfe	e, ddfdgdhZd#e;dae*de^deBdBemdee*e	e, e	e) f fdidjZdYe0de^de0fdkdlZekjxdmd8geeJge7d9e\eeJfd#e;deBfdndoZd@e*d#e<deene	e0 f fdpdqZekjxdrd8geeJgdQe\eeJfd#e<deBfdsdtZekjxdud8geeJge>d9e\eeJfd#e=d<edeBfdvdwZdxe	e0 dye7de	ei fdzd{Zekjxd|d8geeJgefd9e\eeJfd#eedeBfd}d~Zekjxdd8geeJgdQe\eeJedd:d;fd#e!d<edeBd=e
em fddZ	dd"e	e* deBd=e
em de	eemef  fddZde	eemef  de^ddfddZ	dd"e	e* de^deBd=e
em ddf
ddZdeBde*fddZdemde^de9de9fddZekjdd8geeJgdQe\ejdddeeJfd<edemdeBfddZekjxdd8geeJgdQe\eeJfd#ed<edeBfddZekjxdd8geeJgdQe\eeJfd#ed<edeBfddZekdeeJe	e* fd<edeBfddZde^de
em de
em dHe
em de
em dendeemef fddZd"e	e dende	ee*e#f  fddZekjdd8geheeJgde\ejdddejdddejdddejdddejddddejdddddejdddejdddejdddeeJf
d<ede
em dHe
em de
em de
em deqdeqde
em demde
em deBfddZdeBde
em ded4ededefddZekjdd8geeJgdQe\ejddddeeJfd<ede
em dHe
em deBfddZ		dde^deqdeqdee	e* eqf fddÄZekjdd8geeJgddde	e* iidȍejdddejdddejddddejdddddeeJfde
em de
em deqdeqdeBf
dd̈́Zde*de	em de	em fddфZekjxdd8geeJgdQe\eeJfd#e?d<edeBfddԄZekjxdd8geeJgdQe\eeJfd#e@d<edeBfddׄZekjdd8geeJgdQe\ejdddeeJfdemdeBdegfddڄZekjxdd8geeJgdQeeJfd#ejd<edeBde*fdd݄Zekjdecd8gdߍddddddddeeJf	de
em de
em de
em de
em de
em deqdeqde
em deBfddZdS )za
TEAM MANAGEMENT

All /team management endpoints

/team/new
/team/info
/team/update
/team/delete
    N)datetimetimezone)AnyDictListOptionalTupleUnioncast)	APIRouterDependsHeaderHTTPExceptionRequeststatus)	BaseModel)verbose_proxy_logger)uuid)
safe_dumps)$BlockTeamRequestCommonProxyErrorsDeleteTeamRequestLiteLLM_AuditLogsLiteLLM_DeletedTeamTable)LiteLLM_ManagementEndpoint_MetadataFields1LiteLLM_ManagementEndpoint_MetadataFields_PremiumLiteLLM_ModelTableLiteLLM_OrganizationTable$LiteLLM_OrganizationTableWithMembersLiteLLM_TeamMembershipLiteLLM_TeamTableLiteLLM_TeamTableCachedObjLiteLLM_UserTableLiteLLM_VerificationTokenLitellmTableNamesLitellmUserRolesMemberNewTeamRequestProxyErrorTypesProxyExceptionSpecialManagementEndpointEnumsSpecialModelNamesSpecialProxyStringsTeamAddMemberResponseTeamInfoResponseObjectTeamInfoResponseObjectTeamTableTeamListResponseObjectTeamMemberAddRequestTeamMemberDeleteRequestTeamMemberUpdateRequestTeamMemberUpdateResponseTeamModelAddRequestTeamModelDeleteRequestUpdateTeamRequestUserAPIKeyAuth) allowed_route_check_inside_routecan_org_access_modelget_org_objectget_team_objectget_user_object)user_api_key_auth)_is_user_org_admin_for_team_is_user_team_admin_set_object_metadata_field_team_member_has_permission_update_metadata_fields_upsert_budget_and_membership_user_has_admin_view)get_daily_activity)_set_object_permission&handle_update_object_permission_common)TeamMemberPermissionChecks)add_new_membermanagement_endpoint_wrapper)PrismaClienthandle_exception_on_proxy)Router)SpendAnalyticsPaginatedResponse)BulkTeamMemberAddRequestBulkTeamMemberAddResponse GetTeamMemberPermissionsResponseTeamListResponseTeamMemberAddResult"UpdateTeamMemberPermissionsRequestc                   @   s   e Zd ZdZe				ddee dee dee dee de	f
dd	Z
e				dd
eeef dededee dee dee dee defddZe				ddedededee dee dee dee defddZededdfddZdS )TeamMemberBudgetHandlerzHHelper class to handle team member budget, RPM, and TPM limit operationsNteam_member_budgetteam_member_rpm_limitteam_member_tpm_limitteam_member_budget_durationreturnc                 C   s    t | du|du|du|dugS )z,Check if any team member limits are providedN)anyrW   rX   rY   rZ    r^   h/home/app/Keep/.python/lib/python3.10/site-packages/litellm/proxy/management_endpoints/team_endpoints.pyshould_create_budgets   s   z,TeamMemberBudgetHandler.should_create_budgetdatanew_team_data_jsonuser_api_key_dictc                    s   ddl m} ddlm} | jdur#d| jdd dt j }	nd	t j }	||	| j	p1|d
}
|dur;||
_
|durB||
_|durI||
_|durP||
_	||
|dI dH }|ddu rdi |d< |j|d d< t| |S )z4Create team member budget table with provided limitsr   BudgetNewRequest)
new_budgetNzteam- -z-budget-zteam-budget-)	budget_idbudget_durationZ
budget_objrc   metadatateam_member_budget_id)litellm.proxy._typesre   >litellm.proxy.management_endpoints.budget_management_endpointsrf   
team_aliasreplacer   uuid4hexrj   
max_budget	rpm_limit	tpm_limitgetri   rV   _clean_team_member_fields)ra   rb   rc   rW   rX   rY   rZ   re   rf   ri   budget_requestteam_member_budget_tabler^   r^   r_   create_team_member_budget_table   s>   

z7TeamMemberBudgetHandler.create_team_member_budget_table
team_table
updated_kvc              
      s  ddl m} ddlm} | jdu ri | _| jd}	|	durst|	trs||	d}
|dur0||
_|dur7||
_	|dur>||
_
|durE||
_||
|dI dH }td|j d	| d
| d|  |ddu rki |d< |j|d d< ntj| ||||||dI dH }t| |S )z4Upsert team member budget table with provided limitsr   rd   )update_budgetNrm   )ri   rk   z"Updated team member budget table: z, with team_member_budget=z, team_member_rpm_limit=z, team_member_tpm_limit=rl   )ra   rb   rc   rW   rX   rY   rZ   )rn   re   ro   r~   rl   rw   
isinstancestrrt   ru   rv   rj   r   infori   rV   r{   rx   )r|   rc   r}   rW   rX   rY   rZ   re   r~   rm   ry   Z
budget_rowr^   r^   r_   upsert_team_member_budget_table   sJ   


z7TeamMemberBudgetHandler.upsert_team_member_budget_table	data_dictc                 C   s4   |  dd |  dd |  dd |  dd dS )z.Remove team member fields from data dictionaryrW   NrZ   rX   rY   )pop)r   r^   r^   r_   rx      s   z1TeamMemberBudgetHandler._clean_team_member_fields)NNNN)__name__
__module____qualname____doc__staticmethodr   floatintr   boolr`   r	   r'   r    dictr8   r{   r   rx   r^   r^   r^   r_   rV   p   s    
6:rV   team_idrc   r[   c                 C   s*   t jd u rdS dt jv r| t jd v S dS )NFavailable_teams)litellmdefault_internal_user_paramsr   rc   r^   r^   r_   _is_available_team   s
   

r   prisma_clientteam_idsuser_idc                    sj   dd|ii}|durd|gi|d< | j jj|ddidI dH }g }|D ]}|tdi |  q$|S )	z)Get all team memberships for a given userr   inNr   litellm_budget_tableTwhereincluder^   )dblitellm_teammembership	find_manyappendr   
model_dump)r   r   r   Z	where_objteam_membershipsreturned_tmtmr^   r^   r_   get_all_team_memberships  s   r   teamsra   entity_rpm_limitentity_tpm_limitentity_model_rpm_limit_dictentity_model_tpm_limit_dictentity_typec                 C   sz  t |ddp|jr|jddnd}t |ddp#|jr"|jddnd}|du r.|du r.dS i }	i }
| D ]J}|jrZ|jdddurZ|jdi  D ]\}}|	|d| |	|< qK|jr~|jdddur~|jdi  D ]\}}|
|d| |
|< qoq4|dur| D ]Q\}}|dur|	|d| |krtdd|	|d d| d| d	| d
|r||}|r|	|d| |krtdd|	|d d| d| d	| d
q|dur9| D ]X\}}|dur|
|d| |krtdd|
|d d| d| d| d
|r8||}|r8|
|d| |kr8tdd|
|d d| d| d| d
qdS dS )z
    Generic function to check if a team is allocating model specific limits.
    Raises an error if we're overallocating.
    model_rpm_limitNmodel_tpm_limitr     Allocated RPM limit= + Team RPM limit= is greater than  RPM limit=status_codedetailAllocated TPM limit= + Team TPM limit= TPM limit=)getattrrl   rw   itemsr   )r   ra   r   r   r   r   r   r   r   Zmodel_specific_rpm_limitZmodel_specific_tpm_limitteammodelru   rv   Zentity_model_specific_rpm_limitZentity_model_specific_tpm_limitr^   r^   r_   !_check_team_model_specific_limits   s   "
"

"
"r   c              
   C   s   | durt | dkrtdd | D }tdd | D }nd}d}|jdurD|durD|j| |krDtdd| d|j d	| d
| d|jdurg|duri|j| |krktdd| d|j d	| d| ddS dS dS )zx
    Generic function to check if a team is allocating rpm/tpm limits.
    Raises an error if we're overallocating.
    Nr   c                 s        | ]}|j d ur|j V  qd S N)rv   .0r   r^   r^   r_   	<genexpr>      z-_check_team_rpm_tpm_limits.<locals>.<genexpr>c                 s   r   r   )ru   r   r^   r^   r_   r     r   r   r   r   r   r   r   r   r   r   )lensumrv   r   ru   )r   ra   r   r   r   Zallocated_tpmZallocated_rpmr^   r^   r_   _check_team_rpm_tpm_limitsu  s4   


r   	org_tablec              	   C   sf   d}d}i }i }|j dur|j j}|j j}|jr&|jdi }|jdi }t| |||||dd dS )z|
    Check if the organization team is allocating model specific limits. If so, raise an error if we're overallocating.
    Nr   r   organization)r   ra   r   r   r   r   r   )r   ru   rv   rl   rw   r   )r   r   ra   r   r   r   r   r^   r^   r_   $check_org_team_model_specific_limits  s&   


r   c                 C   s8   d}d}|j dur|j j}|j j}t| |||dd dS )zu
    Check if the organization team is allocating rpm/tpm limits. If so, raise an error if we're overallocating.
    Nr   )r   ra   r   r   r   )r   ru   rv   r   )r   r   ra   r   r   r^   r^   r_   check_org_team_rpm_tpm_limits  s   	

r   c           	   
      s,  |j dur-| jdur-| jj dur-|j | jj kr-tddd|j  d| jj  d| j id|jdur_t| jdkr_tjj| jv rAn|jD ]}|| jvr^tddd	| d
| j d| j idqD|j	dur| jdur| jj	dur|j	| jj	krtddd|j	 d| jj	 d| j id|j
dur| jdur| jj
dur|j
| jj
krtddd|j
 d| jj
 d| j idt|ddp|jr|jddnd}t|ddp|jr|jddnd}|dkr|dkrdS |jjjd| jidI dH }g }|D ]}|tdi |  qt|| |d t|| |d dS )z
    Check organization team limits including:
    - Team budget vs organization's max_budget
    - Team models vs organization's allowed models
    - Guaranteed throughput limits (tpm/rpm) if applicable
    Nr   errorzTeam max_budget (z%) exceeds organization's max_budget (z). Organization: r   r   zModel 'zD' not in organization's allowed models. Organization allowed models=z. Organization: zTeam tpm_limit (z$) exceeds organization's tpm_limit (zTeam rpm_limit (z$) exceeds organization's rpm_limit (rpm_limit_typetpm_limit_typeZguaranteed_throughputorganization_idr   )r   r   ra   r^   )rt   r   r   r   modelsr   r+   all_proxy_modelsvaluerv   ru   r   rl   rw   r   litellm_teamtabler   r   r    r   r   r   )	r   ra   r   mr   r   r   Z	team_objsr   r^   r^   r_   _check_org_team_limits  s   









r   user_api_key_cachec                    sF  | j dur6|jdur6t|j||ddI dH }|dur6|j dur6| j |j kr6tddd|j  d|j id| jdur]t|jd	kr]| jD ]}||jvr\tddd
|j d|j idqE| jdur}|jdur}| j|jkr}tddd|j d|j id| jdur|jdur| j|jkrtddd|j d|j iddS dS dS )a>  
    Check user team limits for standalone teams (not org-scoped).

    This validates:
    - Team budget vs user's max_budget
    - Team models vs user's allowed models

    Should only be called for standalone teams (when organization_id is None).
    For org-scoped teams, use _check_org_team_limits() instead.
    NF)r   r   r   user_id_upsertr   r   z1max budget higher than user max. User max budget=z. User role=r   r   z6Model not in allowed user models. User allowed models=z
. User id=z/tpm limit higher than user max. User tpm limit=z/rpm limit higher than user max. User rpm limit=)	rt   r   r=   r   	user_roler   r   rv   ru   )ra   rc   r   r   Zuser_objr   r^   r^   r_   _check_user_team_limitsD  sX   







r   z	/team/newzteam management)tagsdependenciesresponse_modelzThe litellm-changed-by header enables tracking of actions performed by authorized users on behalf of other users, providing an audit trail for accountability)descriptionhttp_requestlitellm_changed_byc                    s  zddl m}m}m}m}m} |du rtdddid| jdur3| jdk r3tddd	| j id| jdurI| jdk rItddd
| j id| j	dur_| j	dk r_tddd| j	 id| j	dur| jdur| j	| jkrtddd| j	 d| j did|j
j I dH }	|	r|j|	drtddd| jdu rtt | _n|j| jdddI dH }
|
durtddd| j did| jdur|durt| j||dI dH }|du rtdd| j dt|| |dI dH  | jdu rttjtrttjdkrttjd trtjd }|d}|dur|| _|jdu s*|jtjkr;| jdu r;t | |||dI dH  |j!durcd}| j"D ]}|j!|j!krQd}qF|du rc| j"#t$d|j!d d}| j%durt| j%trt&t'(| j%|j!p}||j!p|d }|j
j)*i |j'dd!I dH }|j+}| ' }t,||d"I dH }t-j.| j| j/| j0d#rt-j1| ||| j| j/| j0d$I dH }t2d3i |d%|i}t3D ]}t4| |ddurt5||t4| |d& qt6D ]}t4| |ddurt5||t4| |d& q|j7durdd'l8m9} ||j7d(|_:g }|j"dur |j"}g |_"|j;dd!}t4| d)d}|dur5t<|nt<i }||d)< |j=|d*}|j
jj*|d+did,I dH }t>| j|d-}t?|||||d.I dH  tj@du r|j'dd!}t'j(|td/}tAB|tCtt tDEtFjG|p|j!p||jHtIjJ| jd0|dd1	d2 z|; W W S  tKy   |  Y W S w  tKy } ztL|d}~ww )4u  
    Allow users to create a new team. Apply user permissions to their team.

    👉 [Detailed Doc on setting team budgets](https://docs.litellm.ai/docs/proxy/team_budgets)


    Parameters:
    - team_alias: Optional[str] - User defined team alias
    - team_id: Optional[str] - The team id of the user. If none passed, we'll generate it.
    - members_with_roles: List[{"role": "admin" or "user", "user_id": "<user-id>"}] - A list of users and their roles in the team. Get user_id when making a new user via `/user/new`.
    - team_member_permissions: Optional[List[str]] - A list of routes that non-admin team members can access. example: ["/key/generate", "/key/update", "/key/delete"]
    - metadata: Optional[dict] - Metadata for team, store information for team. Example metadata = {"extra_info": "some info"}
    - model_rpm_limit: Optional[Dict[str, int]] - The RPM (Requests Per Minute) limit for this team - applied across all keys for this team. 
    - model_tpm_limit: Optional[Dict[str, int]] - The TPM (Tokens Per Minute) limit for this team - applied across all keys for this team.
    - tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for this team - all keys with this team_id will have at max this TPM limit
    - rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for this team - all keys associated with this team_id will have at max this RPM limit
    - rpm_limit_type: Optional[Literal["guaranteed_throughput", "best_effort_throughput"]] - The type of RPM limit enforcement. Use "guaranteed_throughput" to raise an error if overallocating RPM, or "best_effort_throughput" for best effort enforcement.
    - tpm_limit_type: Optional[Literal["guaranteed_throughput", "best_effort_throughput"]] - The type of TPM limit enforcement. Use "guaranteed_throughput" to raise an error if overallocating TPM, or "best_effort_throughput" for best effort enforcement.
    - max_budget: Optional[float] - The maximum budget allocated to the team - all keys for this team_id will have at max this max_budget
    - soft_budget: Optional[float] - The soft budget threshold for the team. If max_budget is set, soft_budget must be strictly lower than max_budget. Can be set independently if max_budget is not set.
    - budget_duration: Optional[str] - The duration of the budget for the team. Doc [here](https://docs.litellm.ai/docs/proxy/team_budgets)
    - models: Optional[list] - A list of models associated with the team - all keys for this team_id will have at most, these models. If empty, assumes all models are allowed.
    - blocked: bool - Flag indicating if the team is blocked or not - will stop all calls from keys with this team_id.
    - members: Optional[List] - Control team members via `/team/member/add` and `/team/member/delete`.
    - tags: Optional[List[str]] - Tags for [tracking spend](https://litellm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing [tag-based routing](https://litellm.vercel.app/docs/proxy/tag_routing).
    - prompts: Optional[List[str]] - List of prompts that the team is allowed to use.
    - organization_id: Optional[str] - The organization id of the team. Default is None. Create via `/organization/new`.
    - model_aliases: Optional[dict] - Model aliases for the team. [Docs](https://docs.litellm.ai/docs/proxy/team_based_routing#create-team-with-model-alias)
    - guardrails: Optional[List[str]] - Guardrails for the team. [Docs](https://docs.litellm.ai/docs/proxy/guardrails)
    - policies: Optional[List[str]] - Policies for the team. [Docs](https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies)
    - disable_global_guardrails: Optional[bool] - Whether to disable global guardrails for the key.
    - object_permission: Optional[LiteLLM_ObjectPermissionBase] - team-specific object permission. Example - {"vector_stores": ["vector_store_1", "vector_store_2"], "agents": ["agent_1", "agent_2"], "agent_access_groups": ["dev_group"]}. IF null or {} then no object permission.
    - team_member_budget: Optional[float] - The maximum budget allocated to an individual team member.
    - team_member_rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for individual team members.
    - team_member_tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for individual team members.
    - team_member_key_duration: Optional[str] - The duration for a team member's key. e.g. "1d", "1w", "1mo"
    - allowed_passthrough_routes: Optional[List[str]] - List of allowed pass through routes for the team.
    - allowed_vector_store_indexes: Optional[List[dict]] - List of allowed vector store indexes for the key. Example - [{"index_name": "my-index", "index_permissions": ["write", "read"]}]. If specified, the key will only be able to use these specific vector store indexes. Create index, using `/v1/indexes` endpoint.
    - secret_manager_settings: Optional[dict] - Secret manager settings for the team. [Docs](https://docs.litellm.ai/docs/secret_managers/overview)
    - router_settings: Optional[UpdateRouterConfig] - team-specific router settings. Example - {"model_group_retry_policy": {"max_retries": 5}}. IF null or {} then no router settings.
    - access_group_ids: Optional[List[str]] - List of access group IDs to associate with the team. Access groups define which models the team can access. Example - ["access_group_1", "access_group_2"].
    - enforced_file_expires_after: Optional[dict] - Enforced file expiration policy for the team. Keys created under this team will inherit this policy for file uploads. Example - {"anchor": "created_at", "days": 30}.
    - enforced_batch_output_expires_after: Optional[dict] - Enforced batch output file expiration policy for the team. Keys created under this team will inherit this policy for batch output files. Example - {"anchor": "created_at", "days": 30}.

    Returns:
    - team_id: (str) Unique team id - used for tracking spend across multiple keys for same team id.

    _deprecated_params:
    - admins: list - A list of user_id's for the admin role
    - users: list - A list of user_id's for the user role

    Example Request:
    ```
    curl --location 'http://0.0.0.0:4000/team/new'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data '{
      "team_alias": "my-new-team_2",
      "members_with_roles": [{"role": "admin", "user_id": "user-1234"},
        {"role": "user", "user_id": "user-2434"}]
    }'

    ```

     ```
    curl --location 'http://0.0.0.0:4000/team/new'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data '{
                "team_alias": "QA Prod Bot",
                "max_budget": 0.000000001,
                "budget_duration": "1d"
            }'
    ```
    r   )_license_checkcreate_audit_log_for_updatelitellm_proxy_admin_namer   r   N  r   No db connectedr   r   )max_budget cannot be negative. Received: 1team_member_budget cannot be negative. Received: *soft_budget cannot be negative. Received: soft_budget (*) must be strictly lower than max_budget ())Z
team_count  zOLicense is over limit. Please contact support@berri.ai to upgrade your license.r   find_uniquer   
table_name
query_typez
Team id = z0 already exists. Please use a different team id.Zorg_idr   r   z+Organization not found for organization_id=r   ra   r   rt   ra   rc   r   r   FTadmin)roler   model_aliasesZ
created_byZ
updated_byZexclude_none)	data_jsonr   )rW   rX   rY   )ra   rb   rc   rW   rX   rY   model_id)Zobject_data
field_namer   get_budget_reset_timerj   router_settingsZdb_datalitellm_model_table)ra   r   )r   memberra   complete_team_datar   rc   r   defaultcreated	idZ
updated_atZ
changed_byZchanged_by_api_keyr   Z	object_idactionZupdated_valuesZbefore_valueZrequest_datar^   )Mlitellm.proxy.proxy_serverr   r   r   r   r   r   rt   rW   soft_budgetr   r   countZis_team_count_over_limitr   r   r   rr   get_datar   r;   r   r   r   Zdefault_team_settingslistr   r   rw   r   r%   PROXY_ADMINr   r   members_with_rolesr   r&   r   r   jsondumpslitellm_modeltablecreater  rG   rV   r`   rX   rY   r{   r    r   r   rA   r   rj   )litellm.proxy.common_utils.timezone_utilsr   budget_reset_atr   r   jsonify_team_objectr1   _add_team_members_to_teamstore_audit_logsasynciocreate_taskr   r   nowr   utcapi_keyr$   TEAM_TABLE_NAME	ExceptionrM   )ra   r   rc   r   r   r   r   r   r   Ztotal_teamsZ_existing_team_idr   Zdefault_settingsZdefault_budgetZcreating_user_in_listr   	_model_idr  
model_dictr   r  fieldr   r  Zcomplete_team_data_dictZrouter_settings_valueZrouter_settings_jsonteam_rowZteam_member_add_requestZ_updated_valueser^   r^   r_   new_team  s  [














	

r&  existing_team_rowr}   r   c           	         s~   ddl m} | jdd}tj|td}tj|td}t|ttt	 t
tj|p/|jp/||jtj|d||d	d d	S )
a  
    Create an audit log entry for team update operations.

    Args:
        existing_team_row: The team row before the update
        updated_kv: Dictionary of updated key-value pairs
        team_id: The ID of the team being updated
        litellm_changed_by: Optional header indicating who made the change
        user_api_key_dict: User API key authentication details
        litellm_proxy_admin_name: Name of the proxy admin
    r   )r   Tr   r  updatedr  r	  N)Z+litellm.proxy.management_helpers.audit_logsr   r  r  r   r  r  r   r   rr   r   r  r   r  r   r  r$   r  )	r'  r}   r   r   rc   r   r   Z_before_valueZ_after_valuer^   r^   r_   _create_team_update_audit_log  s.   

r)  r   c                    s   |}| j durUt| j trUtt| j |jp||jp|d}|du r5|jjj	i |jdddI dH }n|jjj
d|ii |jddi |jddddI dH }|j}|S )	z4
    Upsert model table and return the model id
    Nr   Tr   ra   r  )updater  r   ra   )r   r   r   r   r  r  r   r   r  r  Zupsertr  )ra   r   r   rc   r   r!  r  r"  r^   r^   r_   _update_model_table  s(   
r-  r   
llm_routerc                    s   |du rt ddtjjid|jjjd| idddddI dH }|du r0t d	dd
|  idttdi |	 t
di |	 |d |S )a  
    Fetch and validate an organization for team update operations.

    Args:
        organization_id: The organization ID to fetch
        existing_team_row: The existing team row being updated
        llm_router: The LLM router instance
        prisma_client: The Prisma database client

    Returns:
        The organization row from the database

    Raises:
        HTTPException: If llm_router is None, organization not found, or validation fails
    Nr   r   r   r   T)r   membersr   r     z/Organization not found, passed organization_id=)r   r   r.  r^   )r   r   Zno_llm_routerr   r   Zlitellm_organizationtabler   validate_team_org_changer    r   r   )r   r'  r.  r   Zorganization_rowr^   r^   r_   fetch_and_validate_organization;  s*   

r2  r   r   c                    s  | j |j krdS t|jdkr8tjj|jv rn!| jdu s#t| jdkr+tdddid| jD ]	}t|||d q.| jr[|j	r[|j	jr[| j|j	jkr[tddd	| j d
|j	j diddd | j
D }|jrndd |jD ng   fdd|D }t|dkrtddd| did| jr|j	r|j	jr| j|j	jkrtddd| j d|j	j did| jr|j	r|j	jr| j|j	jkrtddd| j d|j	j diddS )a2  
    Validate that a team can be moved to an organization.

    - The org must have access to the team's models
    - The team budget cannot be greater than the org max_budget
    - The team's user_id must be a member of the org
    - The team's tpm/rpm limit must be less than the org's tpm/rpm limit
    Tr   Nr   r   zeCannot move team to organization. Team has access to all proxy models, but the organization does not.r   )r   Z
org_objectr.  z6Cannot move team to organization. Team has max_budget z4 that is greater than the organization's max_budget .c                 S      g | ]}|j qS r^   r   r   r   r^   r^   r_   
<listcomp>      z,validate_team_org_change.<locals>.<listcomp>c                 S   r4  r^   r5  r6  r^   r^   r_   r7    r8  c                    s$   g | ]}| vr|t jjkr|qS r^   )r,   Zdefault_user_idr   r6  Zorg_membersr^   r_   r7    s
    z3Cannot move team to organization. Team has user_id z* that is not a member of the organization.z5Cannot move team to organization. Team has tpm_limit z3 that is greater than the organization's tpm_limit z5Cannot move team to organization. Team has rpm_limit z3 that is greater than the organization's rpm_limit )r   r   r   r+   r   r   r   r:   rt   r   r  r/  rv   ru   )r   r   r.  r   team_membersZ
not_in_orgr^   r9  r_   r1  k  s   

	r1  z/team/update)r   r   c              
      s  zNddl m} ddlm}m}m}m}m}	 |du r%tddt	j
jid| jdu r2tddd	idtd
|  | jdurN| jdk rNtddd| j id| jdurd| jdk rdtddd| j id| jdurz| jdk rztddd| j id|jjjd| jidI dH }
|
du rtddd| j id| jdur| jdur| jn|
j}|dur| j|krtddd| j d| did| jdurt|
dd}| jdur| jn|}|durt|ttfr| j|krtddd| j d| did| jdurt| jdkrt| j|
||dI dH  n| jdur"t| jdkr"d| _| jdur+| jn|
j}|durWt|trW|durWt||	|dI dH }|durWt|| |dI dH  |jdu sd|jt j!krt|du rtt"| |||	dI dH  | j#dd}t$| | t%j&| j| j'| j(| j)drt%j*|
||| j| j'| j(| j)dI dH }nt%+| | j,durt-||
d I dH }t.|d! d"|v r|/d" t0| |
j1|||d#I dH }|dur||d$< d%|v r|d% durt2|d% |d%< |j3|d&}|jjj4d| ji|d'did(I dH }|du s|jdu rtddd)5|idt6d*|j ||jt7d.i |8 |	|d+I dH  t9j:du rJt;|
|| j|||d,I dH  |j|d-W S  t<ya } zt=|d}~ww )/a  
    Use `/team/member_add` AND `/team/member/delete` to add/remove new team members

    You can now update team budget / rate limits via /team/update

    Parameters:
    - team_id: str - The team id of the user. Required param.
    - team_alias: Optional[str] - User defined team alias
    - team_member_permissions: Optional[List[str]] - A list of routes that non-admin team members can access. example: ["/key/generate", "/key/update", "/key/delete"]
    - metadata: Optional[dict] - Metadata for team, store information for team. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" }
    - tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for this team - all keys with this team_id will have at max this TPM limit
    - rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for this team - all keys associated with this team_id will have at max this RPM limit
    - max_budget: Optional[float] - The maximum budget allocated to the team - all keys for this team_id will have at max this max_budget
    - soft_budget: Optional[float] - The soft budget threshold for the team. If max_budget is set (either in the request or existing), soft_budget must be strictly lower than max_budget. Can be set independently if max_budget is not set.
    - budget_duration: Optional[str] - The duration of the budget for the team. Doc [here](https://docs.litellm.ai/docs/proxy/team_budgets)
    - models: Optional[list] - A list of models associated with the team - all keys for this team_id will have at most, these models. If empty, assumes all models are allowed.
    - prompts: Optional[List[str]] - List of prompts that the team is allowed to use.
    - blocked: bool - Flag indicating if the team is blocked or not - will stop all calls from keys with this team_id.
    - tags: Optional[List[str]] - Tags for [tracking spend](https://litellm.vercel.app/docs/proxy/enterprise#tracking-spend-for-custom-tags) and/or doing [tag-based routing](https://litellm.vercel.app/docs/proxy/tag_routing).
    - organization_id: Optional[str] - The organization id of the team. Default is None. Create via `/organization/new`.
    - model_aliases: Optional[dict] - Model aliases for the team. [Docs](https://docs.litellm.ai/docs/proxy/team_based_routing#create-team-with-model-alias)
    - guardrails: Optional[List[str]] - Guardrails for the team. [Docs](https://docs.litellm.ai/docs/proxy/guardrails)
    - policies: Optional[List[str]] - Policies for the team. [Docs](https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies)
    - disable_global_guardrails: Optional[bool] - Whether to disable global guardrails for the key.
    - object_permission: Optional[LiteLLM_ObjectPermissionBase] - team-specific object permission. Example - {"vector_stores": ["vector_store_1", "vector_store_2"], "agents": ["agent_1", "agent_2"], "agent_access_groups": ["dev_group"]}. IF null or {} then no object permission.
    - team_member_budget: Optional[float] - The maximum budget allocated to an individual team member.
    - team_member_budget_duration: Optional[str] - The duration of the budget for the team member. Doc [here](https://docs.litellm.ai/docs/proxy/team_budgets)
    - team_member_rpm_limit: Optional[int] - The RPM (Requests Per Minute) limit for individual team members.
    - team_member_tpm_limit: Optional[int] - The TPM (Tokens Per Minute) limit for individual team members.
    - team_member_key_duration: Optional[str] - The duration for a team member's key. e.g. "1d", "1w", "1mo"
    - allowed_passthrough_routes: Optional[List[str]] - List of allowed pass through routes for the team.
    - model_rpm_limit: Optional[Dict[str, int]] - The RPM (Requests Per Minute) limit per model for this team. Example: {"gpt-4": 100, "gpt-3.5-turbo": 200}
    - model_tpm_limit: Optional[Dict[str, int]] - The TPM (Tokens Per Minute) limit per model for this team. Example: {"gpt-4": 10000, "gpt-3.5-turbo": 20000}
    Example - update team TPM Limit
    - allowed_vector_store_indexes: Optional[List[dict]] - List of allowed vector store indexes for the key. Example - [{"index_name": "my-index", "index_permissions": ["write", "read"]}]. If specified, the key will only be able to use these specific vector store indexes. Create index, using `/v1/indexes` endpoint.
    - secret_manager_settings: Optional[dict] - Secret manager settings for the team. [Docs](https://docs.litellm.ai/docs/secret_managers/overview)
    - router_settings: Optional[UpdateRouterConfig] - team-specific router settings. Example - {"model_group_retry_policy": {"max_retries": 5}}. IF null or {} then no router settings.
    - access_group_ids: Optional[List[str]] - List of access group IDs to associate with the team. Access groups define which models the team can access. Example - ["access_group_1", "access_group_2"].
    - enforced_file_expires_after: Optional[dict] - Enforced file expiration policy for the team. Keys created under this team will inherit this policy for file uploads. Example - {"anchor": "created_at", "days": 30}.
    - enforced_batch_output_expires_after: Optional[dict] - Enforced batch output file expiration policy for the team. Keys created under this team will inherit this policy for batch output files. Example - {"anchor": "created_at", "days": 30}.

    ```
    curl --location 'http://0.0.0.0:4000/team/update'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data-raw '{
        "team_id": "8d916b1c-510d-4894-a334-1c16a93344f5",
        "tpm_limit": 100
    }'
    ```

    Example - Update Team `max_budget` budget
    ```
    curl --location 'http://0.0.0.0:4000/team/update'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data-raw '{
        "team_id": "8d916b1c-510d-4894-a334-1c16a93344f5",
        "max_budget": 10
    }'
    ```
    r   )_cache_team_object)r   r.  r   proxy_logging_objr   Nr   r   r   r   No team id passed inz/team/update - %sr   r   r   r   r   r0  Team not found, passed team_id=r   r   r   r  zmax_budget (z-) must be strictly greater than soft_budget ()r   r'  r.  r   r   r   r   T)Zexclude_unsetr]   )r|   rc   r}   rW   rX   rY   rZ   )r   r'  )r}   r   )ra   r   r   rc   r   r   r   r   r   )r   ra   r   zTeam doesn't exist. Got={}z$Successfully updated team - %s, info)r   r|   r   r<  )r'  r}   r   r   rc   r   )r   ra   r^   )>litellm.proxy.auth.auth_checksr;  r
  r   r.  r   r<  r   r   r   db_not_connected_errorr   r   r   debugrt   rW   r  r   r   r   r   r   r   r   r   r   r2  r   r;   r   r   r%   r  r   r  _set_budget_reset_atrV   r`   rX   rY   rZ   r   rx   object_permissionhandle_update_object_permissionrC   r   r-  r   r   r  r+  formatr   r!   r   r   r  r)  r   rM   )ra   r   rc   r   r;  r   r.  r   r<  r   r'  Zmax_budget_to_checkZexisting_soft_budgetZsoft_budget_to_checkZorg_id_to_checkr   r}   r!  r$  r%  r^   r^   r_   update_team  sB  K








	








	rF  c                 C   s2   | j durddlm} || j d}||d< dS dS )zASet budget_reset_at in updated_kv if budget_duration is provided.Nr   r   r   r  )rj   r  r   )ra   r}   r   Zreset_atr^   r^   r_   rB    s
   
rB  r   c                    sH   ddl m} t| |j|dI dH }|dur"|| d< td|  | S )a  
    Handle the update of object permission for a team.

    - IF there's no object_permission_id, then create a new entry in LiteLLM_ObjectPermissionTable
    - IF there's an object_permission_id, then update the entry in LiteLLM_ObjectPermissionTable
    r   r   )r   Zexisting_object_permission_idr   Nobject_permission_idzupdated object_permission_id: )r
  r   rH   rH  r   rA  )r   r'  r   rH  r^   r^   r_   rD    s   	rD  r   premium_userc                 C   sz   t | tr| jdkr|durtdtjj d S t | tr9| D ]}|jdkr8|dur8td| dtjj dq d S d S )Nr   Tz,Assigning team admins is a premium feature. z0Assigning team admins is a premium feature. Got=z. )r   r&   r   
ValueErrorr   Znot_premium_userr   r   )r   rI  r   r^   r^   r_   _check_team_member_admin_add  s    

rK  c              
   C   s   | d u rt dddid|jd u rt dddid|jd u r&t dddidz
t|j|d W d S  tyF } z
t ddt|idd }~ww )	Nr   r   r   r   r   r=  zNo member/members passed in)r   rI  )r   r   r   rK  r   r   )r   ra   rI  r%  r^   r^   r_   team_call_validation_checks  s"   

rL  c                    s   g dt f fdd}t| jt r|| j nt| jtr(| jD ]}|| q!t| jtrDtt| jkrDtd j tj	dddt| jt rgtdkrgtd| jj
 d	| jj d
 j tj	dddtdkr{td j d  dS dS )z
    Check if a member already exists in the team.
    This check is done BEFORE we create/fetch the user, so it only prevents
    obvious duplicates where both user_id and user_email match exactly.
    r   c                    s`   | j d ur jD ]}|j | j kr|  q| jd ur, jD ]}|j| jkr+|  qd S d S r   )r   r  r   
user_email)r   existing_memberr'  Zinvalid_team_membersr^   r_   _check_member_duplicationB  s   





zDteam_member_add_duplication_check.<locals>._check_member_duplicationz0All users are already in team. Existing members=Z400messagetypeparamcode   z&User already in team. Member: user_id=z, user_email=z. Existing members=r   z1Some users are already in team. Existing members=z. Duplicate members=N)r&   r   r   r   r  r   r)   r  r(   Zteam_member_already_in_teamr   rM  r   r   )ra   r'  rP  r   r^   rO  r_   !team_member_add_duplication_check6  s4   



rW  r  c                    st   t | dr0| jtjjkr2t| |ds4t| |dI dH s6t|j| ds8t	ddd
d|jid	dS dS dS dS dS )
z;Validate if user has permission to add members to the team.r   rc   team_objNr   r   r   JCall not allowed. User not proxy admin OR team admin. route={}, team_id={}/team/member_addr   )hasattrr   r%   r  r   r@   r?   r   r   r   rE  rc   r  r^   r^   r_   %_validate_team_member_add_permissionsj  s6   
r^  c                    sX  g }g }|j dur|j dnd}t| jtr]zt| j| j|||| j|dI dH \}}	W n tyJ }
 zt	ddd
| j| jt|
idd}
~
ww || |	durY||	 ||fS t| jtr| jD ]A}zt|| j|||| j|dI dH \}}	W n ty }
 zt	ddd
|| jt|
idd}
~
ww || |	dur||	 qf||fS )z!Process and add new team members.Nrm   )
new_membermax_budget_in_teamr   rc   r   r   default_team_budget_idr   r   z6Unable to add user - {}, to team - {}, for reason - {}r   )rl   rw   r   r   r&   rJ   r`  r   r   r   rE  r   r   r   )ra   r  r   rc   r   updated_usersupdated_team_membershipsra  updated_userZ
updated_tmr%  r   r^   r^   r_   _process_team_members  sv   
	
	

	
	
re  rb  c                    s`  t | jtrX| j }|jdu r*|jdur*|D ]}|jdur)|j|jkr)|j|_qd}|jD ]}|jdur<|j|jksG|jdurK|j|jkrKd} nq/|sV|j| dS dS t | jtr| jD ]L}|jdu r|jdur|D ]}|jdur|j|jkr|j|_qod}|jD ]}|jdur|j|jks|jdur|j|jkrd} nq|s|j| qadS dS )z*Update the team's members_with_roles list.NFT)	r   r   r&   Z
model_copyr   rM  r  r   r   )ra   r  rb  r_  userZmember_already_existsrN  nmr^   r^   r_   _update_team_members_list  sR   







rh  c           	         st   t | ||||dI dH \}}t| ||dI dH  dd |jD }|jjjd| jidt|idI dH }|||fS )	zAdd team members to the team.r  N)ra   r  rb  c                 S      g | ]}|  qS r^   r   r6  r^   r^   r_   r7        z-_add_team_members_to_team.<locals>.<listcomp>r   r  r,  )	re  rh  r  r   r   r+  r   r  r  )	ra   r  r   rc   r   rb  rc  _db_team_membersupdated_teamr^   r^   r_   r    s&   		
r  c                    s  | j du r| jdu rtdddid| j durq| jdurq|jd| j iddd	I dH }|du s:t|tr<t|d
kr<| S t|trTt|dkrTtddd| j  did|d
 }|j| jkrotddd| j  d| j did| S | j dur| jdu r|jjj	d| j ddidI dH }|du r| S |jd| j iddd	I dH }|rt|trt|dkrtddd| j  did|j| _| S | jdur| j du r|jjj
d| jidI dH }|du r| S |j | _ | S | S )a  
    Validate and populate user_email/user_id for a member.
    
    Logic:
    1. If both user_email and user_id are provided, verify they belong to the same user (use user_email as source of truth)
    2. If only user_email is provided, populate user_id from DB
    3. If only user_id is provided, populate user_email from DB (if user exists)
    4. If only user_id is provided and doesn't exist, allow it to pass with user_email as None (will be upserted later)
    5. If user_email and user_id mismatch, throw error
    
    Returns a Member with user_email and user_id populated (user_email may be None if only user_id provided and user doesn't exist).
    Nr   r   z-Either user_id or user_email must be providedr   rM  rf  find_all)key_valr   r   r   rV  z!Multiple users found with email 'z '. Please use 'user_id' instead.zuser_email 'z' and user_id 'z!' do not belong to the same user.insensitive)equalsmoder   r   )rM  r   r   r  r   r  r   r   litellm_usertableZ
find_firstr   )r   r   Zusers_by_emailZuser_by_emailZ
user_by_idr^   r^   r_   '_validate_and_populate_member_user_info'  sv   rt  r[  c              
      st  ddl m}m}m}m}m} z	t|| |d W n ty& } z|d}~ww tt	|}t
| j||d|dddI dH }|du rNtdd	d
t| dd idtdi | }	t| |	d t||	dI dH  t| jtrwt| j|dI dH  nt| jtr| jD ]}
t|
|dI dH  qt| |	|||dI dH \}}}|du rtdd	d| j didtdi | ||dS )a  
    Add new members (either via user_email or user_id) to a team

    If user doesn't exist, new user row will also be added to User Table

    Only proxy_admin or admin of team, allowed to access this endpoint.
    ```

    curl -X POST 'http://0.0.0.0:4000/team/member_add'     -H 'Authorization: Bearer sk-1234'     -H 'Content-Type: application/json'     -d '{"team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": {"role": "user", "user_id": "krrish247652@berri.ai"}}'

    ```
    r   )r   rI  r   r<  r   )r   ra   rI  NFTr   r   r   parent_otel_spanr<  Zcheck_cache_onlycheck_db_onlyr0  r   zTeam not found for team_id=r   r   )ra   r'  r]  )r   r   r  zTeam with id z
 not found)rb  rc  r^   )r
  r   rI  r   r<  r   rL  r   r
   rL   r<   r   r   r    r   rW  r^  r   r   r&   rt  r   r  r-   )ra   rc   r   rI  r   r<  r   r%  r'  r  r   rm  rb  rc  r^   r^   r_   team_member_add  s   

	


rx  c                 C   sr   d}g }| j D ]-}|jdur|jdur|j|jkrd}q|jdur/|jdur/|j|jkr/d}q|| q||fS )z+Cleanup members_with_roles list for a team.FNT)r  r   rM  r   )r'  ra   is_member_in_teamnew_team_membersr   r^   r^   r_   _cleanup_members_with_roles   s   




r{  /team/member_deletec                    s  ddl m} |du rtdddid| jdu r tddd	id| jdu r2| jdu r2tddd
id|jjjd| jidI dH }|du rPtddd	| jidt
d!i | }|jtjjkr|t||ds|t||dI dH s|tddd	d|jidt|| d\}}|stdddid||_dd |D }|jjjd| jidt|idI dH }i }	| jdur| j|	d< n
| jdur| j|	d< |jjj|	dI dH }
|
dur
t|
tr
t|
dkr
|
D ]'}g }| j|jv r	|j}|| j |jjjd|jidd|iidI dH  qt }| jdur|| j |
dur7t|
tr7|
D ]}t |ddr5||j q&|D ]}|jj!j"| j|ddI dH  q9|rddl#m$} |jj%jdt|i| jddI dH }|rv||||dd I dH  |jj%j"dt|i| jddI dH  |S )"a  
    [BETA]

    delete members (either via user_email or user_id) from a team

    If user doesn't exist, an exception will be raised
    ```
    curl -X POST 'http://0.0.0.0:8000/team/member_delete' 
    -H 'Authorization: Bearer sk-1234' 
    -H 'Content-Type: application/json' 
    -d '{
        "team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849",
        "user_id": "krrish247652@berri.ai"
    }'
    ```
    r   rG  Nr   r   r   r   r   r=  2Either user_id or user_email needs to be passed inr   r   Team id={} does not exist in dbrX  r   rZ  r|  )r'  ra   zUser not found in teamc                 S   ri  r^   rj  r6  r^   r^   r_   r7  o  rk  z&team_member_delete.<locals>.<listcomp>r  r,  r   rM  r   set)r   r   $_persist_deleted_verification_tokensr   )r   r   keysr   rc   r   r^   )&r
  r   r   r   r   rM  r   r   r   rE  r    r   r   r%   r  r   r@   r?   r{  r  r+  r  r  rs  r   r   r  r   r   remover  addr   r   Zdelete_many;litellm.proxy.management_endpoints.key_management_endpointsr  litellm_verificationtoken)ra   rc   r   _existing_team_rowr'  ry  rz  Z_db_new_team_members_ro  Zexisting_user_rowsZexisting_user	team_listZuser_ids_to_deleteZ_uidr  keys_to_deleter^   r^   r_   team_member_delete  s   



	







	
r  z/team/member_updatec                    s  ddl m}m} |du rtdddid| jdu r"tddd	id| jd
kr/|s/tddd| jdu rA| jdu rAtdddid|jj	j
d| jidI dH }|du r_tddd| jidtdi | }|jtjjkrt||dst||dI dH stdddd|jidt|| j|dI dH }|d }d}	| jdur| j}	n| jdur|d jD ]}
|
jdur|
j| jkr|
j}	 nq|	du rtddd| idd}|d D ]}|j|	kr|j} nq|j 4 I dH }t|| j|	| j||| j| jdI dH  W d  I dH  n1 I dH sw   Y  | jdurbg }|jD ]!}
|
j|	kr=|t|
j| j| jp7|
jd q"||
 q"||_dd |D }|jj	jd| jidt |idI dH  t!| j|	| j| j| j| jdS ) zE
    [BETA]

    Update team member budgets and team member role
    r   )rI  r   Nr   r   r   r   r   r=  r   a
  Assigning team admins is a premium feature. You must be a LiteLLM Enterprise user to use this feature. If you have a license please set `LITELLM_LICENSE` in your env. Get a 7 day trial key here: https://www.litellm.ai/#trial. Pricing: https://www.litellm.ai/#pricingr}  r   r   r~  rX  r   rZ  r|  )r   r   rc   	team_infoz,User id doesn't exist in team table. Data={}r   )txr   r   rt   Zexisting_budget_idrc   rv   ru   )r   r   rM  c                 S   ri  r^   rj  r6  r^   r^   r_   r7  D	  rk  z&team_member_update.<locals>.<listcomp>r  r,  )r   r   rM  r`  rv   ru   r^   )"r
  rI  r   r   r   r   r   rM  r   r   r   rE  r    r   r   r%   r  r   r@   r?   r  r  ri   r  rD   r`  rv   ru   r   r&   r+  r  r  r4   )ra   r   rc   rI  r   r  r'  Zreturned_team_infor|   Zreceived_user_idr   Zidentified_budget_idr   r  r:  rl  r^   r^   r_   team_member_update  s   

	



*
r  r/  responsec              
   C   s   g }| D ]G}d}|j D ]}|jr|j|jks|jr%|j|jkr%| } nqd}|jD ]}|jr<|j|jkr<| } nq+|t|j|jd||d q|S )zS
    Convert TeamAddMemberResponse into individual TeamMemberAddResult objects
    NT)r   rM  successrd  updated_team_membership)rb  r   rM  r   rc  r   rT   )r/  r  resultsr   rd  rf  r  r   r^   r^   r_   _create_results_from_responseT	  s6   


r  z/team/bulk_member_addc           	         sh  ddl m} ddlm} |du rtdd|jjid| jr3|jj	j
dd	id
I dH }dd |D | _| js>tdddidd}t| j|krStddd| didz*tt| j| j| jd|dI dH }t| j|}t| j|t| jt|d| dW S  ty } z*t| t|  fdd| jD }t| j|t| jdt| jddW  Y d}~S d}~ww )a"  
    Bulk add multiple members to a team at once.
    
    This endpoint reuses the same logic as /team/member_add but provides a bulk-friendly response format.
    
    Parameters:
    - team_id: str - The ID of the team to add members to
    - members: List[Member] - List of members to add to the team
    - all_users: Optional[bool] - Flag to add all users on Proxy to the team
    - max_budget_in_team: Optional[float] - Maximum budget allocated to each user within the team
    
    Returns:
    - results: List of individual member addition results
    - total_requested: Total number of members requested for addition
    - successful_additions: Number of successful additions  
    - failed_additions: Number of failed additions
    - updated_team: The updated team object
    
    Example request:
    ```bash
    curl --location 'http://0.0.0.0:4000/team/bulk_member_add'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data '{
        "team_id": "team-1234",
        "members": [
            {
                "user_id": "user1",
                "role": "user"
            },
            {
                "user_email": "user2@example.com",
                "role": "admin"
            }
        ],
        "max_budget_in_team": 100.0
    }'
    ```
    r   )r   rG  Nr   r   r   
created_atdesc)orderc                 S   s   g | ]}t |j|jd dqS )rf  )r   rM  r   )r&   r   rM  )r   rf  r^   r^   r_   r7  	  s    z(bulk_team_member_add.<locals>.<listcomp>r   zAt least one member is requiredzMaximum z members can be added at once)r   r   r`  ra   rc   )r   r  Ztotal_requestedZsuccessful_additionsZfailed_additionsrm  c                    s    g | ]}t |j|jd  dqS )F)r   rM  r  r   )rT   r   rM  )r   r   error_messager^   r_   r7  	  s    )rn   r   r
  r   r   r@  r   Z	all_usersr   rs  r   r/  r   rx  r1   r   r`  r  rQ   r   r   r   	exceptionr   )	ra   rc   r   r   Zall_users_in_dbZMAX_BATCH_SIZEr  r  r%  r^   r  r_   bulk_team_member_add{	  sx   2
	
	


r  z/team/deletec                    s&  ddl m}m}m} |du rtdddid| jdu r$tddd	idg }| jD ]8}z|jjjd
|idI dH }	|	du r?t	W n t	yR   tddd| idw t
di |	 }
||
 q)t||||dI dH  tjdu r| jD ]:}|j|dddI dH }|du rqu|jdd}t|ttt ttj|p|jp||jtj|dd|d	d quddl m!} |jj"j#d
d| jiidI dH }|r|||||dI dH  |j$| jddI dH  |D ]%}|j%}g }|D ]}|t&t'|j(|j|j)d|d qtj*| I dH  q|j$| jddI dH }|S ) a  
    delete team and associated team keys

    Parameters:
    - team_ids: List[str] - Required. List of team IDs to delete. Example: ["team-1234", "team-5678"]

    ```
    curl --location 'http://0.0.0.0:4000/team/delete'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data-raw '{
        "team_ids": ["8d916b1c-510d-4894-a334-1c16a93344f5"]
    }'
    ```
    r   )r   r   r   Nr   r   r   r   r   r=  r   r   r0  r>  )r   r   rc   r   Tr   r   r   r   deletedz{}r  r	  r  r   r  key)Zteam_id_listr   )r   r   rM  r  r^   )+r
  r   r   r   r   r   r   r   r   r   r    r   r   _persist_deleted_team_recordsr   r  r  r  r  r  r   r   r   rr   r   r  r   r  r   r  r$   r  r  r  r  r   Zdelete_datar  r  r2   r   rM  gather)ra   r   rc   r   r   r   r   Z	team_rowsr   Zteam_row_baseZteam_row_pydanticr$  Z	_team_rowr  r  r:  tasksZteam_memberZdeleted_teamsr^   r^   r_   delete_team
  s   



	




r  c              	   C   s   | sg S t tj}g }| D ]B}| }tdi |||j|j|d}| }dD ]}	|	|v r?||	 dur?t	||	 ||	< q*dD ]}
|
|
d qB|| q|S )z@Transform teams into deleted team records ready for persistence.)
deleted_atZ
deleted_byZdeleted_by_api_keyr   )r  rl   Zmodel_spendZmodel_max_budgetr   N)r   rC  r  r^   )r   r  r   r  r   r   r   r  r  r  r   r   )r   rc   r   r  recordsr   Zteam_payloadZdeleted_recordrecordZ
json_fieldZrel_keyr^   r^   r_   #_transform_teams_to_deleted_records
  s.   
r  r  c                    s$   | sdS |j jj| dI dH  dS )z*Save deleted team records to the database.Nr*  )r   litellm_deletedteamtableZcreate_manyr  r   r^   r^   r_   _save_deleted_team_records
  s   r  c                    s&   t | ||d}t||dI dH  dS )z=Persist deleted team records by transforming and saving them.)r   rc   r   r  N)r  r  )r   r   rc   r   r  r^   r^   r_   r  
  s   r  r|   c                    s   | j tjjks| j tjjkrd S | j|jkrd S | jd u r=| jd ur1tddd| j|jidtddd|jid| jdd |j	D v rJd S t
| |dI d H rUd S tddd	| j|jid)
Nr   r   z:Team key for team={} not authorized to access this team={}r   z^API key not authorized to access this team={}. No user_id or team_id associated with this key.c                 S   r4  r^   r5  r6  r^   r^   r_   r7  
  s    z'validate_membership.<locals>.<listcomp>rX  z-User={} not authorized to access this team={})r   r%   r  r   ZPROXY_ADMIN_VIEW_ONLYr   r   r   rE  r  r?   rc   r|   r^   r^   r_   validate_membership
  sJ   

	

r  rm   team_info_response_objectc                    sN   z|j jjd| idI d H }||_W |S  ty&   td|   Y |S w )Nri   r   zATeam member budget table not found, passed team_member_budget_id=)r   Zlitellm_budgettabler   rz   r   r   r   )rm   r   r  Zteam_budgetr^   r^   r_   _add_team_member_budget_table  s   r  z
/team/infoz!Team ID in the request parameters)r  r   c                    s  ddl m} ddlm} z|du rttjddid|du r(ttjdd	idz|jj	j
d
|iddidI dH }|du r?tW n tyT   ttjdd| didw t|td#i | dI dH  |j|ddt dI dH }|du ryg }|du rd}|D ]
}|t|dd7 }qd|i}|D ]}	z|	 }	W n ty   |	 }	Y nw |	dd qt||gddI dH }
t|tr|d#i |}nt|tr|d#i | }n| }|jdur|jdnd}|durt|||dI dH }t||||
d}|W S  tyR } zGtd|t !  t|tr4t"t|ddt#| dt$j%t|ddt|d tj&d!t|t"r<|t"d"t#| t$j%t|ddtj&d!d}~ww )$a#  
    get info on team + related keys

    Parameters:
    - team_id: str - Required. The unique identifier of the team to get info on.

    ```
    curl --location 'http://localhost:4000/team/info?team_id=your_team_id_here'     --header 'Authorization: Bearer your_api_key_here'
    ```
    r   )r/   rG  Nr   zDatabase not connected. Connect a database to your proxy - https://docs.litellm.ai/docs/simple_proxy#managing-auth---virtual-keysr   rR  z(Malformed request. No team id passed in.r   rC  Tr   z Team not found, passed team id: r3  r  r  rn  )r   r   r   expiresspendtokenr5  rm   )rm   r   r  )r   r  r  r   z\litellm.proxy.management_endpoints.team_endpoints.py::team_info - Exception occurred - {}
{}r   zAuthentication Error(r   rT  Noner   rQ  zAuthentication Error, r^   )'rn   r/   r
  r   r   r   ZHTTP_500_INTERNAL_SERVER_ERRORZHTTP_422_UNPROCESSABLE_ENTITYr   r   r   r   ZHTTP_404_NOT_FOUNDr  r    r   r  r   r  r   r   r   r   r   r   rl   rw   r  r.   r   r   rE  	traceback
format_excr)   r   r(   Z
auth_errorZHTTP_400_BAD_REQUEST)r   r   rc   r/   r   r  r  r  kr  r   Z
_team_inform   Zresponse_objectr%  r^   r^   r_   r  "  s   






r  z/team/blockc                    d   ddl m} |du rtd|jjjd| jiddidI dH }|du r0td	d
d| j id|S )a  
    Blocks all calls from keys with this team id.

    Parameters:
    - team_id: str - Required. The unique identifier of the team to block.

    Example:
    ```
    curl --location 'http://0.0.0.0:4000/team/block'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data '{
        "team_id": "team-1234"
    }'
    ```

    Returns:
    - The updated team record with blocked=True



    r   rG  NNo DB Connected.r   blockedTr,  r0  r   r>  r   r
  r   r   r   r   r+  r   r   ra   r   rc   r   r  r^   r^   r_   
block_team  s   r  z/team/unblockc                    r  )ay  
    Blocks all calls from keys with this team id.

    Parameters:
    - team_id: str - Required. The unique identifier of the team to unblock.

    Example:
    ```
    curl --location 'http://0.0.0.0:4000/team/unblock'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data '{
        "team_id": "team-1234"
    }'
    ```
    r   rG  Nr  r   r  Fr,  r0  r   r>  r   r  r  r^   r^   r_   unblock_team  s   r  z/team/availablec                    s   ddl m} |d u rtddtjjidtttt	  t
jd ur&t
jdnd }|d u r/g S |jjjd|jidI d H }|d u rItd	dd
idtdi |   fdd|D }|jjjdd|iidI d H }dd |D }|S )Nr   rG  r   r   r   r   r   r   r0  zUser not foundc                       g | ]	}| j vr|qS r^   )r   r   Zuser_info_correct_typer^   r_   r7  /      z(list_available_teams.<locals>.<listcomp>r   r   c                 S   s   g | ]}t d i | qS )r^   )r    r   r   r^   r^   r_   r7  7  s    r^   )r
  r   r   r   r@  r   r
   r   r   r   r   r   rw   r   rs  r   r   r"   r   r   r   )r   rc   r   r   r   	user_infoZavailable_teams_dbZavailable_teams_correct_typer^   r  r_   list_available_teams  sD   




r  rp   use_deleted_tablec           	         s  i }|r	||d< |r|dd|d< |r||d< |rz| j jjd|idI dH }W n ty;   td	d
d| idw |du rKtd	d
d| idtdi | }|r^d|i|d< |S |du rkd|ji|d< |S ||jv rv||d< |S td	d
d| id|S )z+Build where conditions for team list query.r   rp  containsrr  rp   r   r   r   Nr0  r   zUser not found, passed user_id=r   hasr/  r   z User is not a member of team_id=r^   )r   rs  r   r   r   r"   r   r   )	r   r   rp   r   r   r  where_conditionsZuser_objectZuser_object_correct_typer^   r^   r_   !_build_team_list_where_conditions>  sR   	
	
r  c              	   C   sl   g }| r4| D ]-}z|  }W n ty   | }Y nw |r)|tdi | q|tdi | q|S )z)Convert Prisma models to Pydantic models.Nr^   )r   r   r   r   r   r    )r   r  r  r   Z	team_dictr^   r^   r_   _convert_teams_to_responsev  s   r  z/v2/team/list)r   r   r   z1Only return teams which this 'user_id' belongs toz9Only return teams which this 'organization_id' belongs toz1Only return teams which this 'team_id' belongs tozPOnly return teams which this 'team_alias' belongs to. Supports partial matching.rV  zPage number for pagination)r  r   ge
   zNumber of teams per paged   )r  r   r  lez>Column to sort by (e.g. 'team_id', 'team_alias', 'created_at')asczSort order ('asc' or 'desc')z!Filter by status (e.g. 'deleted')page	page_sizesort_by
sort_orderr   c                    s  ddl m} |du rtddd| idt|
|ds(td	dd
|
jid|du r5|
jtjkr5|
j}|	durE|	dkrEtdddid|	dk}|d | }t	||||||dI dH }g d}d}|rv||v rv|
 dvrpd}||
 i}|r|jjj||||r|nddidI dH }|jjj|dI dH }n|jjj||||r|nddidI dH }|jjj|dI dH }| |  }t||}|||||dS )aU  
    Get a paginated list of teams with filtering and sorting options.

    Parameters:
        user_id: Optional[str]
            Only return teams which this user belongs to
        organization_id: Optional[str]
            Only return teams which belong to this organization
        team_id: Optional[str]
            Filter teams by exact team_id match
        team_alias: Optional[str]
            Filter teams by partial team_alias match
        page: int
            The page number to return
        page_size: int
            The number of items per page
        sort_by: Optional[str]
            Column to sort by (e.g. 'team_id', 'team_alias', 'created_at')
        sort_order: str
            Sort order ('asc' or 'desc')
        status: Optional[str]
            Filter by status. Currently supports "deleted" to query deleted teams.
    r   rG  Nr   r   zNo db connected. prisma client=r   )rc   Zrequested_user_id  COnly admin users can query all teams/other teams. Your user role={}r  r   z<Invalid status value. Currently only 'deleted' is supported.rV  )r   r   rp   r   r   r  )r   rp   r  )r  r  r  r  r  r   skiptaker  r   )r   totalr  r  total_pages)r
  r   r   r9   rE  r   r%   r  r   r  lowerr   r  r   r  r   r  )r   r   r   r   rp   r  r  r  r  r   rc   r   r  r  r  Zvalid_sort_columnsZorder_byr   total_countr  r  r^   r^   r_   list_team_v2  s   @	

r  r<  c                    s  t | }d}|sM duo| jduo| j k}| jdur;t| j||d|dI dH }|dur;dd |jp4g D }|s;d}|du rM|sMtddd| jid	|dur|jjj	d
d|iiddidI dH }	 sit
|	S dd |	D }
t
|	}|jjj	|
rddt
|
iini ddidI dH }|D ]}|jrt fdd|jD r|| q|S  r|jjj	ddidI dH } fdd|D S t
|jjj	ddidI dH S )a2  
    Authorize the /team/list request and return filtered teams.

    - Proxy admins: all teams (or filtered by user_id if provided).
    - Org admins: teams from their orgs + teams they are direct members of.
    - Own query (user_id matches caller): teams the user is a member of.
    - Others: 401.
    NF)r   r   r   r   r<  c                 S   s    g | ]}|j tjjkr|jqS r^   )r   r%   Z	ORG_ADMINr   r   r6  r^   r^   r_   r7  I  s
    z/_authorize_and_filter_teams.<locals>.<listcomp>r  r   r  r   r   r   r   Tr   c                 S   s   h | ]}|j qS r^   r   r   r^   r^   r_   	<setcomp>d  r8  z._authorize_and_filter_teams.<locals>.<setcomp>r   Znot_inc                 3       | ]
}| d  kV  qdS r   Nrw   r6  r5  r^   r_   r   l  s    
z._authorize_and_filter_teams.<locals>.<genexpr>)r   c                    s.   g | ]}|j rt fd d|j D r|qS )c                 3   r  r  r  r6  r5  r^   r_   r   z  s    z9_authorize_and_filter_teams.<locals>.<listcomp>.<genexpr>)r  r\   r   r5  r^   r_   r7  v  s    )rE   r   r=   Zorganization_membershipsr   rE  r   r   r   r   r  r  r\   r   )rc   r   r   r   r<  Zis_proxy_adminZallowed_org_idsZis_own_queryZcaller_userZ	org_teamsZseen_team_idsZ	all_teamsZmember_teamsr   r  r^   r5  r_   _authorize_and_filter_teams&  s~   
	



r  z
/team/listc                    sr  ddl m}m}m} |du rtddtjjidt|||||dI dH }dd	 |D }t	|||d
I dH }	g }
|D ]X}g }|	D ]}|j
|j
krM|| q@|jjjd|j
idI dH }z|
tdi | ||d W q: ty } zd|j
| t|}t| W Y d}~q:d}~ww |
jdd d  dur tjjkrdd	 |
D }
|
S  fdd	|
D }
|
S )a  
    ```
    curl --location --request GET 'http://0.0.0.0:4000/team/list'         --header 'Authorization: Bearer sk-1234'
    ```

    Parameters:
    - user_id: str - Optional. If passed will only return teams that the user_id is a member of.
    - organization_id: str - Optional. If passed will only return teams that belong to the organization_id. Pass 'default_organization' to get all teams without organization_id.
    r   r   r<  r   Nr   r   r   )rc   r   r   r   r<  c                 S   r4  r^   r  r   r^   r^   r_   r7    r8  zlist_team.<locals>.<listcomp>r5  r   r   )r   r  zWInvalid team object for team_id: {}. team_object={}.
            Error: {}
            c                 S   s   t | ddpdS )Nrp    )r   )xr^   r^   r_   <lambda>  s    zlist_team.<locals>.<lambda>)r  c                 S   s   g | ]	}|j d u r|qS r   r   r   r^   r^   r_   r7    r  c                    s   g | ]	}|j  kr|qS r^   r  r   r  r^   r_   r7    s
    
r^   )r
  r   r<  r   r   r   r@  r   r  r   r   r   r   r  r   r0   r   r   rE  r   r   r  sortr*   ZDEFAULT_ORGANIZATION)r   r   r   rc   r   r<  r   Zfiltered_responseZ	_team_idsr   Zreturned_responsesr   Z_team_membershipsr   r  r%  Zteam_exceptionr^   r  r_   	list_team  st   


	

r  c              
      s   z#|d | }| j j I dH }| j jj||ddidI dH }||fW S  tyB } ztd|  g dfW  Y d}~S d}~ww )a5  
    Get paginated list of teams from team table

    Parameters:
        prisma_client: PrismaClient - The database client
        page_size: int - Number of teams per page
        page: int - Page number (1-based)

    Returns:
        Tuple[List[LiteLLM_TeamTable], int] - (list of teams, total count)
    rV  Nrp   r  )r  r  r  z.[Non-Blocking] Error getting paginated teams: r   )r   r   r  r   r   r   r  )r   r  r  r  r  r   r%  r^   r^   r_   get_paginated_teams  s   

r  z/team/filter/uiF   r   )r   r   Zinclude_in_schema	responsesz$Team alias in the request parameters2   zNumber of items per pagec           
   
      s   ddl m} |du rtdddidz2|d | }i }| r%| d	d
|d< |r.|d	d
|d< |jjj|||ddidI dH }|sCg W S |W S  ty\ }	 ztddt|	 dd}	~	ww )a  
    [PROXY-ADMIN ONLY] Filter teams based on partial match of team_id or team_alias with pagination.

    Args:
        user_id (Optional[str]): Partial user ID to search for
        user_email (Optional[str]): Partial email to search for
        page (int): Page number for pagination (starts at 1)
        page_size (int): Number of items per page (max 100)
        user_api_key_dict (UserAPIKeyAuth): User authentication information

    Returns:
        List[LiteLLM_SpendLogs]: Paginated list of matching user records
    r   rG  Nr   r   r   r   rV  rp  r  r   rp   r  r  r  zError searching teams: )r
  r   r   r   r   r   r   r   )
r   rp   r  r  rc   r   r  r  r   r%  r^   r^   r_   ui_view_teams  s8   %

r  rY  
new_modelsc                 C   s@   | j }|durt|dkrtjjg}n| j }tt|| }|S )z8
    Add new models to a team's allowed model list.
    Nr   )r   r   r+   r   r   r  r  )rY  r  current_modelsupdated_modelsr^   r^   r_   add_new_models_to_teamR  s   r  z/team/model/addc                    s   ddl m} |du rtdddid|jjjd| jid	I dH }|du r1td
dd| j idtdi | }|j	t
jjkrXt||dsXt||dI dH sXtdddidt|| jd}|jjjd| jid|idI dH }|S )a
  
    Add models to a team's allowed model list. Only proxy admin or team admin can add models.

    Parameters:
    - team_id: str - Required. The team to add models to
    - models: List[str] - Required. List of models to add to the team

    Example Request:
    ```
    curl --location 'http://0.0.0.0:4000/team/model/add'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data '{
        "team_id": "team-1234",
        "models": ["gpt-4", "claude-2"]
    }'
    ```
    r   rG  Nr   r   r   r   r   r   r0  r>  rX  r   5Only proxy admin or team admin can modify team models)rY  r  r   r,  r^   )r
  r   r   r   r   r   r   r    r   r   r%   r  r   r@   r?   r  r   r+  )ra   r   rc   r   r$  rY  r  rm  r^   r^   r_   team_model_addc  s>   
r  z/team/model/deletec           	         s   ddl m} |du rtdddid|jjjd jid	I dH }|du r1td
dd j idtdi | }|j	t
jjkrXt||dsXt||dI dH sXtdddid|jp\g } fdd|D }|jjjd jid|idI dH }|S )a  
    Remove models from a team's allowed model list. Only proxy admin or team admin can remove models.

    Parameters:
    - team_id: str - Required. The team to remove models from
    - models: List[str] - Required. List of models to remove from the team

    Example Request:
    ```
    curl --location 'http://0.0.0.0:4000/team/model/delete'     --header 'Authorization: Bearer sk-1234'     --header 'Content-Type: application/json'     --data '{
        "team_id": "team-1234",
        "models": ["gpt-4"]
    }'
    ```
    r   rG  Nr   r   r   r   r   r   r0  r>  rX  r   r  c                    r  r^   )r   r6  r*  r^   r_   r7    s    z%team_model_delete.<locals>.<listcomp>r   r,  r^   )r
  r   r   r   r   r   r   r    r   r   r%   r  r   r@   r?   r   r+  )	ra   r   rc   r   r$  rY  r  r  rm  r^   r*  r_   team_model_delete  s@   

r  z/team/permissions_listc              	      s   ddl m}m}m} |du rtdddidt| ||d|dd	d
I dH }tdi | }t|dr]|j	t
jjkr]t||ds]t||dI dH s]t|j|ds]tdddd|jid|jdu rgt |_t| |jt dS )z4
    Get the team member permissions for a team
    r   r  Nr   r   r   r   FTru  r   rX  r   r   rZ  r[  )r   team_member_permissionsZall_available_permissionsr^   )r
  r   r<  r   r   r<   r    r   r\  r   r%   r  r   r@   r?   r   r   rE  r  rI   Zdefault_team_member_permissionsrR   Z)get_all_available_team_member_permissions)r   rc   r   r<  r   r'  r  r^   r^   r_   r    s\   



r  z/team/permissions_updatec           	   	      s   ddl m}m}m} |du rtdddidt| j||d|dd	d
I dH }tdi | }t	|dr^|j
tjjkr^t||ds^t||dI dH s^t|j|ds^tdddd|jid|jjjd| jid| jidI dH }|S )z7
    Update the team member permissions for a team
    r   r  Nr   r   r   r   FTru  r   rX  r   r   rZ  r[  r   r  r,  r^   )r
  r   r<  r   r   r<   r   r    r   r\  r   r%   r  r   r@   r?   r   rE  r   r   r+  r  )	ra   r   rc   r   r<  r   r'  r  rm  r^   r^   r_   update_team_member_permissions?  sV   


r  /team/daily/activity)r   r   
start_dateend_dater  exclude_team_idsc	                    s  ddl m}	m}
m} |	du rtddtjjid| r | dnd}d}|r/|r-|dnd}t	|spt
|j|	d||j|
d	d
I dH }|du rStddd|jid|du r[|j}n|D ]}||jvrotddd|idq]i }|r|dt|i|d< |	jjj|dI dH }dd |D }d}t	|s|r|rd}|D ] }tdi | }t||drd	} nt||ddrd	} nq|s|	jjjd|jidI dH }dd |D }|sdg}|}|du r|dur|}t|	dd|||||||||dI dH S )a  
    Get daily activity for specific teams or all teams.

    Args:
        team_ids (Optional[str]): Comma-separated list of team IDs to filter by. If not provided, returns data for all teams.
        start_date (Optional[str]): Start date for the activity period (YYYY-MM-DD).
        end_date (Optional[str]): End date for the activity period (YYYY-MM-DD).
        model (Optional[str]): Filter by model name.
        api_key (Optional[str]): Filter by API key.
        page (int): Page number for pagination.
        page_size (int): Number of items per page.
        exclude_team_ids (Optional[str]): Comma-separated list of team IDs to exclude.
    Returns:
        SpendAnalyticsPaginatedResponse: Paginated response containing daily activity data.
    r   r  Nr   r   r   ,FT)r   r   r   r   rv  r<  rw  r0  zUser= {} not foundzGUser does not belong to Team= {}. Call `/user/info` to see user's teamsr   r   r   c                 S   s   i | ]	}|j d |jiqS )rp   )r   rp   )r   tr^   r^   r_   
<dictcomp>  s    z+get_team_daily_activity.<locals>.<dictcomp>rX  r  )rc   rY  Z
permissionr   c                 S   s   g | ]}|j r|j qS r^   )r  )r   r  r^   r^   r_   r7    s    z+get_team_daily_activity.<locals>.<listcomp>r  Zlitellm_dailyteamspend)r   r   Zentity_id_fieldZ	entity_idZentity_metadata_fieldZexclude_entity_idsr  r  r   r  r  r  r^   )r
  r   r<  r   r   r   r@  r   splitrE   r=   r   rv  rE  r   r  r   r   r   r    r   r@   rB   r  rF   )r   r  r  r   r  r  r  r  rc   r   r<  r   Zteam_ids_listZexclude_team_ids_listr  r   Zwhere_conditionZteam_aliasesZteam_alias_metadataZuser_api_keysZhas_full_team_viewrp   rY  Z	user_keysZfinal_api_key_filterr^   r^   r_   get_team_daily_activity  s   
	
	r  r   )r  rV  )r   r  r  r  r   r   typingr   r   r   r   r   r	   r
   Zfastapir   r   r   r   r   r   Zpydanticr   r   Zlitellm._loggingr   Zlitellm._uuidr   Z*litellm.litellm_core_utils.safe_json_dumpsr   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/   r0   r1   r2   r3   r4   r5   r6   r7   r8   r?  r9   r:   r;   r<   r=   Z$litellm.proxy.auth.user_api_key_authr>   Z/litellm.proxy.management_endpoints.common_utilsr?   r@   rA   rB   rC   rD   rE   Z;litellm.proxy.management_endpoints.tag_management_endpointsrF   Z8litellm.proxy.management_helpers.object_permission_utilsrG   rH   Z>litellm.proxy.management_helpers.team_member_permission_checksrI   Z&litellm.proxy.management_helpers.utilsrJ   rK   Zlitellm.proxy.utilsrL   rM   Zlitellm.routerrN   Z>litellm.types.proxy.management_endpoints.common_daily_activityrO   Z7litellm.types.proxy.management_endpoints.team_endpointsrP   rQ   rR   rS   rT   rU   ZrouterrV   r   r   r   r   r   r   r   r   r   r   r   postr&  r   r)  r-  r2  r1  rF  rB  rD  rK  rL  rW  r^  re  rh  r  rt  rx  r{  r  r  r  r  r  r  r  r  r  r  rw   ZQueryr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r^   r^   r^   r_   <module>   s   $ &$	 	 	




U

*

"



j

K  W
,
$
0
^  	



4

D
9
"
kg
 # 
' 
"


;
 
-'2

8
	  
_[
"

E
AFC>	