o
    ưiՐ                     @   s   U d 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
 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 erEddlmZ dZd	ed
efddZG dd dZdae	e ed< d
efddZdS )z
Policy Registry - In-memory storage for policies.

Handles storing, retrieving, and managing policies.

Policies define WHAT guardrails to apply. WHERE they apply is defined
by policy_attachments (see AttachmentRegistry).
    N)datetimetimezone)TYPE_CHECKINGAnyDictListOptionalTuple)verbose_proxy_logger)
GuardrailPipelinePipelineStepPolicyPolicyConditionPolicyCreateRequestPolicyDBResponsePolicyGuardrailsPolicyUpdateRequestPolicyVersionCompareResponsePolicyVersionListResponse)PrismaClientZpolicy_rowreturnc                 C   s   t di d| jd| jdt| dddt| dddt| ddd	t| d	d
dt| dddt| ddd| jd| jd| jpBg d| jpcg d| jd| j	d| j
d| jd| jd| jS d| jd| j	d| j
d| jd| jd| jS )z=Build PolicyDBResponse from a Prisma LiteLLM_PolicyTable row.	policy_idpolicy_nameversion_number   version_status
productionparent_version_idN	is_latestTpublished_atproduction_atinheritdescriptionguardrails_addguardrails_remove	conditionpipeline
created_at
updated_at
created_by
updated_by )r   r   r   getattrr"   r#   r$   r%   r&   r'   r(   r)   r*   r+   )r   r,   r,   b/home/app/Keep/.python/lib/python3.10/site-packages/litellm/proxy/policy_engine/policy_registry.py_row_to_policy_db_response   sh   	

r/   c                   @   s  e Zd ZdZdd Zdeeef ddfddZd	ed
eeef de	fddZ
edeeeef  dee fddZd	edee	 fddZdeee	f fddZdee fddZd	edefddZdefddZdFddZd	ede	ddfddZd	edefdd Z	dGd!ed"d#d$ee defd%d&Z	dGd'ed!ed"d#d(ee def
d)d*Zd'ed"d#deeef fd+d,Zd'ed"d#dee fd-d.Zd'edee ee	f  fd/d0Z!	dGd"d#d1ee dee fd2d3Z"	"	#		dHd4d5Z#d	ed"d#dee fd6d7Z$d	ed"d#de%fd8d9Z&		dId	ed"d#d:ee d$ee def
d;d<Z'	dGd'ed=ed"d#d(ee def
d>d?Z(d@edAed"d#de)fdBdCZ*d	ed"d#deeef fdDdEZ+dS )JPolicyRegistryaT  
    In-memory registry for storing and managing policies.

    This is a singleton that holds all loaded policies and provides
    methods to access them.

    Policies define WHAT guardrails to apply:
    - Base guardrails via guardrails.add/remove
    - Inheritance via inherit field
    - Conditional guardrails via condition.model
    c                 C   s   i | _ i | _d| _d S )NF)	_policies_policies_by_id_initializedselfr,   r,   r.   __init__D   s   
zPolicyRegistry.__init__policies_configr   Nc                 C   s   i | _ i | _| D ]?\}}z| ||}|| j |< td|  W q
 tyI } ztd| dt|  t	d| dt| |d}~ww d| _
tdt| j  d dS )	z
        Load policies from a configuration dictionary.

        Args:
            policies_config: Dictionary mapping policy names to policy definitions.
                            This is the raw config from the YAML file.
        zLoaded policy: zError loading policy 'z': zInvalid policy 'NTzLoaded z	 policies)r1   r2   items_parse_policyr
   debug	Exceptionerrorstr
ValueErrorr3   infolen)r5   r7   r   policy_datapolicyer,   r,   r.   load_policiesI   s    
zPolicyRegistry.load_policiesr   rA   c                 C   s   | di }t|trt| d| dd}n	t|r|ndd}d}| d}|r2t| dd	}t| d
}t| d| d|||dS )z
        Parse a policy from raw configuration data.

        Args:
            policy_name: Name of the policy
            policy_data: Raw policy configuration

        Returns:
            Parsed Policy object
        
guardrailsaddremoverF   rG   N)rF   r&   model)rI   r'   r"   r#   r"   r#   rE   r&   r'   )get
isinstancedictr   r   r0   _parse_pipeliner   )r5   r   rA   Zguardrails_datarE   r&   Zcondition_datar'   r,   r,   r.   r9   b   s*   


zPolicyRegistry._parse_policypipeline_datac                 C   s:   | du rdS |  dg }dd |D }t|  dd|dS )z-Parse a pipeline configuration from raw data.Nstepsc                 S   s(   g | ]}t |trtd i |n|qS )r,   )rL   rM   r   ).0Z	step_datar,   r,   r.   
<listcomp>   s    z2PolicyRegistry._parse_pipeline.<locals>.<listcomp>modeZpre_call)rS   rP   )rK   r   )rO   Z
steps_datarP   r,   r,   r.   rN      s   
zPolicyRegistry._parse_pipelinec                 C      | j |S )z
        Get a policy by name.

        Args:
            policy_name: Name of the policy to retrieve

        Returns:
            Policy object if found, None otherwise
        )r1   rK   r5   r   r,   r,   r.   
get_policy   s   
zPolicyRegistry.get_policyc                 C   s
   | j  S )zz
        Get all loaded policies.

        Returns:
            Dictionary mapping policy names to Policy objects
        )r1   copyr4   r,   r,   r.   get_all_policies   s   
zPolicyRegistry.get_all_policiesc                 C   s   t | j S )zb
        Get list of all policy names.

        Returns:
            List of policy names
        )listr1   keysr4   r,   r,   r.   get_policy_names   s   zPolicyRegistry.get_policy_namesc                 C   s
   || j v S )z
        Check if a policy exists.

        Args:
            policy_name: Name of the policy to check

        Returns:
            True if policy exists, False otherwise
        )r1   rU   r,   r,   r.   
has_policy   s   

zPolicyRegistry.has_policyc                 C   s   | j S )z
        Check if the registry has been initialized with policies.

        Returns:
            True if policies have been loaded, False otherwise
        )r3   r4   r,   r,   r.   is_initialized   s   zPolicyRegistry.is_initializedc                 C   s   i | _ d| _dS )z7
        Clear all policies from the registry.
        FN)r1   r3   r4   r,   r,   r.   clear   s   
zPolicyRegistry.clearrB   c                 C   s   || j |< td|  dS )z
        Add or update a single policy.

        Args:
            policy_name: Name of the policy
            policy: Policy object to add
        zAdded/updated policy: Nr1   r
   r:   )r5   r   rB   r,   r,   r.   
add_policy   s   
zPolicyRegistry.add_policyc                 C   s*   || j v r| j |= td|  dS dS )z
        Remove a policy by name.

        Args:
            policy_name: Name of the policy to remove

        Returns:
            True if policy was removed, False if it didn't exist
        zRemoved policy: TFr_   rU   r,   r,   r.   remove_policy   s
   

zPolicyRegistry.remove_policypolicy_requestprisma_clientr   r*   c           
   
      sb  zt tj}|jddd||jpg |jpg ||d	}|jdur%|j|d< |jdur/|j|d< |dur;||d< ||d	< |j	durJt
|j	 |d
< |jdur`tdi |j}t
| |d< |jjj|dI dH }| |j|j|j|j|jd|j	r|j	 nd|jd}| |j| t|W S  ty }	 ztd|	  tdt|	 d}	~	ww )a'  
        Add a policy to the database.

        Args:
            policy_request: The policy creation request
            prisma_client: The Prisma client instance
            created_by: User who created the policy

        Returns:
            PolicyDBResponse with the created policy
        r   r   T)	r   r   r   r   r!   r$   r%   r(   r)   Nr"   r#   r*   r+   r&   r'   datarH   rJ   zError adding policy to DB: r,   )r   nowr   utcr   r$   r%   r"   r#   r&   jsondumps
model_dumpr'   r   dblitellm_policytablecreater9   r`   r/   r;   r
   	exceptionr=   )
r5   rb   rc   r*   rf   re   validated_pipelineZcreated_policyrB   rC   r,   r,   r.   add_policy_to_db   s`   






zPolicyRegistry.add_policy_to_dbr   r+   c              
      sz  z|j jjd|idI dH }|du rtd| dt|dd}|dkr-td	| d
ttj|d}|j	dur@|j	|d< |j
durJ|j
|d< |jdurT|j|d< |jdur^|j|d< |jdurh|j|d< |jdurwt|j |d< |jdurtdi |j}t| |d< |j jjd|i|dI dH }	t|	W S  ty }
 ztd|
  tdt|
 d}
~
ww )a  
        Update a policy in the database. Only draft versions can be updated.

        Args:
            policy_id: The ID of the policy to update
            policy_request: The policy update request
            prisma_client: The Prisma client instance
            updated_by: User who updated the policy

        Returns:
            PolicyDBResponse with the updated policy

        Raises:
            Exception: If policy is not in draft status (only drafts are editable).
        r   whereNPolicy with ID 
 not foundr   r   draftz<Only draft versions can be updated. This policy has status ''.)r)   r+   r   r"   r#   r$   r%   r&   r'   rr   re   zError updating policy in DB: r,   )rk   rl   find_uniquer;   r-   r   rf   r   rg   r   r"   r#   r$   r%   r&   rh   ri   rj   r'   r   updater/   r
   rn   r=   )r5   r   rb   rc   r+   existingr   Zupdate_dataro   Zupdated_policyrC   r,   r,   r.   update_policy_in_dbB  sT   














z"PolicyRegistry.update_policy_in_dbc              
      s   zG|j jjd|idI dH }|du rtd| dt|dd}|j}|j jjd|idI dH  dd	| d
i}|dkrF| | d|d< |W S  tye } zt	d|  tdt
| d}~ww )a  
        Delete a policy version from the database.

        If the deleted version was production, it is removed from the in-memory
        registry. No other version is auto-promoted; admin must explicitly promote.

        Args:
            policy_id: The ID of the policy version to delete
            prisma_client: The Prisma client instance

        Returns:
            Dict with "message" and optional "warning" if production was deleted.
        r   rq   Nrs   rt   r   r   messagePolicy z deleted successfullyzProduction version was deleted. No other version was promoted. Promote another version to production if this policy should remain active.warningzError deleting policy from DB: )rk   rl   rx   r;   r-   r   deletera   r
   rn   r=   )r5   r   rc   rB   r   r   resultrC   r,   r,   r.   delete_policy_from_db  s0   
z$PolicyRegistry.delete_policy_from_dbc              
      sp   z|j jjd|idI dH }|du rW dS t|W S  ty7 } ztd|  tdt| d}~ww )z
        Get a policy by ID from the database.

        Args:
            policy_id: The ID of the policy to retrieve
            prisma_client: The Prisma client instance

        Returns:
            PolicyDBResponse if found, None otherwise
        r   rq   NzError getting policy from DB: )rk   rl   rx   r/   r;   r
   rn   r=   )r5   r   rc   rB   rC   r,   r,   r.   get_policy_by_id_from_db  s   
z'PolicyRegistry.get_policy_by_id_from_dbc                 C   rT   )a  
        Return a policy version by ID from in-memory cache (no DB access).

        Used when the request body specifies policy_<uuid> to execute a specific version
        (e.g. published or draft). The cache is populated by sync_policies_from_db,
        which loads draft and published versions keyed by policy_id.

        Args:
            policy_id: The policy version ID (raw UUID, no prefix)

        Returns:
            (policy_name, Policy) if found, None otherwise
        )r2   rK   )r5   r   r,   r,   r.   get_policy_by_id_for_request  s   z+PolicyRegistry.get_policy_by_id_for_requestr   c              
      s   z$i }|dur||d< |j jj|r|ndddidI dH }dd |D W S  tyB } ztd|  tdt| d}~ww )	ak  
        Get all policies from the database, optionally filtered by version_status.

        Args:
            prisma_client: The Prisma client instance
            version_status: If set, only return policies with this status
                           ("draft", "published", "production").

        Returns:
            List of PolicyDBResponse objects
        Nr   r(   descrr   orderc                 S      g | ]}t |qS r,   r/   )rQ   pr,   r,   r.   rR         z;PolicyRegistry.get_all_policies_from_db.<locals>.<listcomp>z Error getting policies from DB: )rk   rl   	find_manyr;   r
   rn   r=   )r5   rc   r   rr   policiesrC   r,   r,   r.   get_all_policies_from_db  s   
z'PolicyRegistry.get_all_policies_from_dbc              
      sF  zi | _ | j|ddI dH }|D ]}| |j|j|j|j|jd|j|j	d}| 
|j| qi | _|jjjdddd	giid
didI dH }|D ]%}| |j|j|j|jpXg |jp\g d|j|j	d}|j|f| j|j< qJd| _tdt| dt| d W dS  ty } ztd|  tdt| d}~ww )aM  
        Sync policies from the database to in-memory registry.
        - Production versions are loaded into _policies (by policy name) for resolution.
        - Draft and published versions are loaded into _policies_by_id so request-body
          policy_<uuid> overrides can be resolved without DB access in the hot path.
        r   r   NrH   rJ   r   inru   	publishedr(   r   r   TzSynced z production policies and z6 draft/published (by ID) from DB to in-memory registryz Error syncing policies from DB: )r1   r   r9   r   r"   r#   r$   r%   r&   r'   r`   r2   rk   rl   r   r   r3   r
   r?   r@   r;   rn   r=   )r5   rc   r   policy_responserB   Znon_productionr   rC   r,   r,   r.   sync_policies_from_db  s\   

z$PolicyRegistry.sync_policies_from_dbc           
   
      s   ddl m} z:| j|ddI dH }i }|D ]}| |j|j|j|j|jd|j	|j
d}|||j< q|j||dd}t|jW S  ty^ }	 ztd	|	  td	t|	 d}	~	ww )
aU  
        Resolve all guardrails for a policy from the database.

        Uses the existing PolicyResolver to handle inheritance chain resolution.

        Args:
            policy_name: Name of the policy to resolve
            prisma_client: The Prisma client instance

        Returns:
            List of resolved guardrail names
        r   )PolicyResolverr   r   NrH   rJ   )r   r   contextz$Error resolving guardrails from DB: )Z+litellm.proxy.policy_engine.policy_resolverr   r   r9   r   r"   r#   r$   r%   r&   r'   Zresolve_policy_guardrailssortedrE   r;   r
   rn   r=   )
r5   r   rc   r   r   Ztemp_policiesr   rB   Zresolved_policyrC   r,   r,   r.   resolve_guardrails_from_dbC  s>   z)PolicyRegistry.resolve_guardrails_from_dbc              
      s   z!|j jjd|iddidI dH }dd |D }t||t|dW S  ty? } ztd	|  td	t| d}~ww )
a,  
        Get all versions of a policy by name, ordered by version_number descending.

        Args:
            policy_name: Name of the policy
            prisma_client: The Prisma client instance

        Returns:
            PolicyVersionListResponse with policy_name and list of versions
        r   r   r   r   Nc                 S   r   r,   r   )rQ   rr,   r,   r.   rR     r   z>PolicyRegistry.get_versions_by_policy_name.<locals>.<listcomp>)r   versionsZtotal_countzError getting versions: )	rk   rl   r   r   r@   r;   r
   rn   r=   )r5   r   rc   rowsr   rC   r,   r,   r.   get_versions_by_policy_namez  s"   z*PolicyRegistry.get_versions_by_policy_namesource_policy_idc                    s  z|dur1|j jjd|idI dH }|du rtd| d|j|kr0td|j d| dn|j jj|d	d
dI dH }|du rKtd| d|}|j jjd|iddidI dH }|rd|jd nd}tt	j
}	|j jjd|iddidI dH  ||d|jddd|j|j|jpg |jpg |	|	||d}
|jdurt|jtrt|jn|j|
d< |jdurt|jtrt|jn|j|
d< |j jj|
dI dH }t|W S  ty } ztd|  tdt| d}~ww )a  
        Create a new draft version of a policy. Copies all fields from the source.
        Source is current production if source_policy_id is None.

        Args:
            policy_name: Name of the policy
            prisma_client: The Prisma client instance
            source_policy_id: Policy ID to clone from; if None, use current production
            created_by: User who created the version

        Returns:
            PolicyDBResponse for the new draft version
        Nr   rq   zSource policy rt   zSource policy name 'z' does not match ''r   r   r   z(No production version found for policy 'r   r   r   r   r   r   Frw   ru   T)r   r   r   r   r   r    r!   r"   r#   r$   r%   r(   r)   r*   r+   r&   r'   rd   zError creating new version: )rk   rl   rx   r;   r   Z
find_firstr   r   rf   r   rg   update_manyr   r"   r#   r$   r%   r&   rL   rM   rh   ri   r'   rm   r/   r
   rn   r=   )r5   r   rc   r   r*   sourceprodZlatestZnext_numrf   re   createdrC   r,   r,   r.   create_new_version  s   






z!PolicyRegistry.create_new_version
new_statusc              
      s  z|dvrt d| d|jjjd|idI dH }|du r't d| dt|d	d
}|j}ttj	}|dkr^|dkrFt d| d|jjj
d|id|||ddI dH }	t|	W S |dvrjt d| d|dkrrt d|jjj|d
dd||ddI dH  |jjj
d|id
|||ddI dH }	| | | ||	j|	j|	jpg |	jpg d|	j|	jd}
| ||
 t|	W S  t y } ztd|  t dt| d}~ww )a  
        Update a policy version's status. Valid transitions:
        - draft -> published (sets published_at)
        - published -> production (sets production_at, demotes current production to published, updates in-memory)
        - production -> published (demotes, removes from in-memory)
        - draft -> production: NOT allowed (must publish first)
        - published -> draft: NOT allowed

        Args:
            policy_id: The policy version ID
            new_status: "published" or "production"
            prisma_client: The Prisma client instance
            updated_by: User who updated

        Returns:
            PolicyDBResponse for the updated version
        )r   r   zInvalid status 'z#'. Use 'published' or 'production'.r   rq   Nrs   rt   r   r   r   ru   z7Only draft versions can be published. Current status: 'rv   )r   r    r)   r+   rw   )ru   r   zJOnly draft or published versions can be promoted to production. Current: 'zGCannot promote draft directly to production. Publish the version first.r   )r   r)   r+   )r   r!   r)   r+   rH   rJ   zError updating version status: )r;   rk   rl   rx   r-   r   r   rf   r   rg   ry   r/   r   ra   r9   r"   r#   r$   r%   r&   r'   r`   r
   rn   r=   )r5   r   r   rc   r+   r   currentr   rf   updatedrB   rC   r,   r,   r.   update_version_status  s   


	


z$PolicyRegistry.update_version_statuspolicy_id_apolicy_id_bc              
      s  zb|j jjd|idI dH }|j jjd|idI dH }|du r(td| d|du r4td| dt|}t|}g d}i }	|D ]}
t||
}t||
}||kr[||d|	|
< qDt|||	dW S  ty } ztd	|  td	t	| d}~ww )
aT  
        Compare two policy versions and return field-by-field diffs.

        Args:
            policy_id_a: First policy version ID
            policy_id_b: Second policy version ID
            prisma_client: The Prisma client instance

        Returns:
            PolicyVersionCompareResponse with both versions and field_diffs
        r   rq   Nr}   rt   )r"   r#   r$   r%   r&   r'   )	version_a	version_b)r   r   field_diffszError comparing versions: )
rk   rl   rx   r;   r/   r-   r   r
   rn   r=   )r5   r   r   rc   abZresp_aZresp_bZcompare_fieldsr   fieldZval_aZval_brC   r,   r,   r.   compare_versionsi  s@   

zPolicyRegistry.compare_versionsc              
      st   z|j jjd|idI dH  | | dd| diW S  ty9 } ztd|  tdt| d}~ww )a  
        Delete all versions of a policy. Also removes from in-memory registry.

        Args:
            policy_name: Name of the policy
            prisma_client: The Prisma client instance

        Returns:
            Dict with success message
        r   rq   Nr|   zAll versions of policy 'z' deleted successfullyzError deleting all versions: )rk   rl   Zdelete_manyra   r;   r
   rn   r=   )r5   r   rc   rC   r,   r,   r.   delete_all_versions  s   
z"PolicyRegistry.delete_all_versions)r   N)N)rc   r   r   N)NN),__name__
__module____qualname____doc__r6   r   r=   r   rD   r   r9   staticmethodr   r   rN   rV   rX   r   r[   boolr\   r]   r^   r`   ra   r   r   rp   r   r{   r   r   r	   r   r   r   r   r   r   r   r   r   r   r   r,   r,   r,   r.   r0   7   s    )		
	
P
F

3


=
7
"
e
q
9
r0   _policy_registryc                   C   s   t du rt a t S )zk
    Get the global PolicyRegistry singleton.

    Returns:
        The global PolicyRegistry instance
    N)r   r0   r,   r,   r,   r.   get_policy_registry  s   r   ) r   rh   r   r   typingr   r   r   r   r   r	   Zlitellm._loggingr
   Z!litellm.types.proxy.policy_enginer   r   r   r   r   r   r   r   r   r   Zlitellm.proxy.utilsr   ZPOLICY_VERSION_ID_PREFIXr/   r0   r   __annotations__r   r,   r,   r,   r.   <module>   s(    	 0	       