§
    œŠtjŒ  ã                  óF  — d Z ddlmZ ddlmZ ddlmZmZmZm	Z	 ddl
mZmZmZmZ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 dd	lm Z m!Z!m"Z"m#Z#m$Z$ erdd
l%m&Z& ddl'm(Z( ddl)m*Z* dZ+	  G d„ de¦  «        Z, G d„ de e!e#         e"e#f         ¦  «        Z-g d¢Z.dS )z1PII detection and handling middleware for agents.é    )Úannotations)Úpartial)ÚTYPE_CHECKINGÚAnyÚClassVarÚLiteral)Ú	AIMessageÚ
AnyMessageÚBaseMessageÚHumanMessageÚToolMessage)ÚStreamTransformer)Úoverride)
ÚPIIDetectionErrorÚPIIMatchÚRedactionRuleÚResolvedRedactionRuleÚapply_strategyÚdetect_credit_cardÚdetect_emailÚ	detect_ipÚdetect_mac_addressÚ
detect_url)ÚAgentMiddlewareÚ
AgentStateÚContextTÚ	ResponseTÚhook_config)ÚCallable)ÚRuntime)ÚProtocolEventé€   c                  óÔ   ‡ — e Zd ZU dZdZded<   dZded<   	 d9ed	œd:ˆ fd„Zd;d„Z	d<d„Z
d<d„Zd<d„Zd<d„Zd=d„Zd>d#„Zd?d&„Zd@d(„ZdAd+„ZdBd.„ZdCd0„ZdAd1„ZdDd3„ZdEd4„ZdFd5„ZdGd8„Zˆ xZS )HÚ_PIIStreamTransformeruÏ  Mutates `content-block-delta` text on `messages` events in flight.

    Runs before built-in stream transformers so the redacted text is what
    every downstream consumer sees â€” both the main protocol event log and
    the `run.messages` projection that `MessagesTransformer` snapshots into.

    Holds a sliding buffer of the most recent text per (run_id, content
    block index) so PII patterns that straddle delta boundaries are caught.
    Anything older than `lookback` characters is redacted with the resolved
    rule's strategy and emitted as the new delta text; the trailing tail
    stays in the buffer until a later delta extends it past the cap or the
    block's finish event flushes the snapshot.
    TzClassVar[bool]Úbefore_builtins)ÚmessagesÚtoolsÚvalueszClassVar[tuple[str, ...]]Úrequired_stream_modes© )ÚlookbackÚscopeútuple[str, ...]Úruler   r+   ÚintÚreturnÚNonec               ó‚   •— t          ¦   «                              |¦  «         || _        || _        i | _        i | _        d S ©N)ÚsuperÚ__init__Ú_ruleÚ	_lookbackÚ_buffersÚ_tool_buffers)Úselfr,   r.   r+   Ú	__class__s       €ú]/var/www/html/CA-Chatbot/venv/lib/python3.11/site-packages/langchain/agents/middleware/pii.pyr5   z_PIIStreamTransformer.__init__D   sB   ø€ õ 	‰Œ×Ò˜ÑÔÐØˆŒ
Ø!ˆŒà46ˆŒð .0ˆÔÐÐó    údict[str, Any]c                ó   — i S r3   r*   ©r:   s    r<   Úinitz_PIIStreamTransformer.initU   s	   € ð ˆ	r=   Úeventr!   Úboolc                ó¸   — |d         }|dk    r|                       |¦  «        S |dk    r|                      |¦  «        S |dk    r|                      |¦  «        S dS )NÚmethodr&   r'   r(   T)Ú_process_messages_eventÚ_process_tools_eventÚ_process_values_event)r:   rB   rE   s      r<   Úprocessz_PIIStreamTransformer.processZ   sm   € Ø�x”ˆØ�ZÒÐØ×/Ò/°Ñ6Ô6Ð6Ø�WÒÐØ×,Ò,¨UÑ3Ô3Ð3Ø�XÒÐØ×-Ò-¨eÑ4Ô4Ð4Øˆtr=   c                ó€   — |d                               d¦  «        }|€dS |                      |¦  «        |d         d<   dS )u  Redact the state snapshot on the `values` channel.

        State snapshots emitted between nodes carry the full state dict,
        which typically includes the messages list. Walking the snapshot
        with `_redact_value` returns a fresh structure where every
        message has a redacted copy of its content â€” the original
        objects in graph state remain intact for the state-level
        enforcer (`apply_to_tool_results` via `before_model`) to act on
        independently when the agent loops back.
        ÚparamsÚdataNT)ÚgetÚ_redact_value)r:   rB   rL   s      r<   rH   z+_PIIStreamTransformer._process_values_eventd   sF   € ð �XŒ×"Ò" 6Ñ*Ô*ˆØˆ<Ø�4Ø"&×"4Ò"4°TÑ":Ô":ˆˆhŒ˜ÑØˆtr=   c                óZ  — |d         }|                      d¦  «        }t          |t          ¦  «        rt          |¦  «        dk    rdS |\  }}t          |t          ¦  «        r"|                      |¦  «        }||ur||f|d<   dS t          |t          ¦  «        sdS |                      d¦  «        }|r$t          |                      d¦  «        pd¦  «        nd}|dk    r|                      ||¦  «         n6|d	k    r|  	                    ||¦  «         n|d
v r|  
                    |¦  «         dS )NrK   rL   é   TrB   Úrun_idÚ zcontent-block-deltazcontent-block-finish>   úmessage-finishÚerror)rM   Ú
isinstanceÚtupleÚlenr   Ú_redact_base_messageÚdictÚstrÚ_mutate_deltaÚ_finalize_blockÚ	_drop_run)	r:   rB   rK   rL   ÚpayloadÚmetadataÚredactedÚkindrQ   s	            r<   rF   z-_PIIStreamTransformer._process_messages_eventu   sM  € Ø�x”ˆØ�zŠz˜&Ñ!Ô!ˆÝ˜$¥Ñ&Ô&ð 	­#¨d©)¬)°qª.¨.Ø�4Ø Ñˆ�õ �g�{Ñ+Ô+ð 	Ø×0Ò0°Ñ9Ô9ˆHØ˜wÐ&Ð&Ø"*¨HÐ!5��v‘Ø�4å˜'¥4Ñ(Ô(ð 	Ø�4Ø�{Š{˜7Ñ#Ô#ˆØ6>ÐF•�X—\’\ (Ñ+Ô+Ð1¨rÑ2Ô2Ð2ÀBˆàÐ(Ò(Ð(Ø×Ò˜w¨Ñ/Ô/Ð/Ð/ØÐ+Ò+Ð+Ø× Ò  ¨&Ñ1Ô1Ð1Ð1ØÐ0Ð0Ð0Ø�NŠN˜6Ñ"Ô"Ð"Øˆtr=   c                ód  — |d                               d¦  «        }t          |t          ¦  «        sdS |                      d¦  «        }|                      d¦  «        }|dk    r$d|v r|                      |d         ¦  «        |d<   �n)|dk    r.|                      |t          |t
          ¦  «        r|nd	¦  «         nõ|d
k    rSd|v r|                      |d         ¦  «        |d<   t          |t
          ¦  «        r| j                             |d ¦  «         nœ|dk    r–|                      d¦  «        }t          |t
          ¦  «        r<|r:| j         	                    |¦  «        }|rt          ||| j        j        ¦  «        |d<   t          |t
          ¦  «        r| j                             |d ¦  «         dS )NrK   rL   TrB   Útool_call_idztool-startedÚinputztool-output-deltarR   ztool-finishedÚoutputz
tool-errorÚmessage)rM   rU   rY   rN   Ú_mutate_tool_output_deltarZ   r9   Úpopr6   Údetectorr   Ústrategy)r:   rB   rL   ra   rc   ÚmsgÚmatchess          r<   rG   z*_PIIStreamTransformer._process_tools_event˜   sÅ  € Ø�XŒ×"Ò" 6Ñ*Ô*ˆÝ˜$¥Ñ%Ô%ð 	Ø�4Ø�xŠx˜Ñ Ô ˆØ—x’x Ñ/Ô/ˆà�>Ò!Ð!ð
 ˜$ˆˆØ $× 2Ò 2°4¸´=Ñ AÔ A��W‘ùØÐ(Ò(Ð(ð ×*Ò*Ø¥j°½sÑ&CÔ&CÐK�l�lÈñô ð ð ð �_Ò$Ð$Ø˜4ÐÐØ!%×!3Ò!3°D¸´NÑ!CÔ!C��X‘Ý˜,­Ñ,Ô,ð ;ØÔ"×&Ò& |°TÑ:Ô:Ð:øØ�\Ò!Ð!Ø—(’(˜9Ñ%Ô%ˆCÝ˜#�sÑ#Ô#ð X¨ð XØœ*×-Ò-¨cÑ2Ô2�Øð XÝ&4°S¸'À4Ä:ÔCVÑ&WÔ&W�D˜‘OÝ˜,­Ñ,Ô,ð ;ØÔ"×&Ò& |°TÑ:Ô:Ð:àˆtr=   rL   rc   rZ   c                ó   — |                      d¦  «        }t          |t          ¦  «        r�| j                              |d¦  «        }||z   }| j                             |¦  «        }|rt          ||| j        j        ¦  «        }t          dt          |¦  «        | j
        z
  ¦  «        }||d…         | j        |<   |d|…         |d<   dS t          |t          t          f¦  «        r|                      |¦  «        |d<   dS dS )uÅ  Redact a `tool-output-delta` payload.

        String deltas go through the same lookback machinery as
        text-deltas, keyed by `tool_call_id` in the disjoint
        `_tool_buffers` dict so `_drop_run` on the messages channel
        can't sweep active tool-output state.

        Structured deltas (dict/list) walk recursively without
        buffering â€” they don't have a position-stable shape across
        deltas to buffer against.
        ÚdeltarR   r   N)rM   rU   rZ   r9   r6   ri   r   rj   ÚmaxrW   r7   rY   ÚlistrN   )r:   rL   rc   rn   ÚheldÚcombinedrl   Úemit_ends           r<   rg   z/_PIIStreamTransformer._mutate_tool_output_delta½   s  € ð —’˜Ñ!Ô!ˆÝ�e�SÑ!Ô!ð 	6ØÔ%×)Ò)¨,¸Ñ;Ô;ˆDØ˜e‘|ˆHà”j×)Ò)¨(Ñ3Ô3ˆGØð Rõ
 *¨(°G¸T¼ZÔ=PÑQÔQ�å˜1�c (™mœm¨d¬nÑ<Ñ=Ô=ˆHØ/7¸¸	¸	Ô/BˆDÔ˜|Ñ,Ø$ Y h YÔ/ˆD�‰MˆMˆMÝ˜¥¥d˜|Ñ,Ô,ð 	6Ø ×.Ò.¨uÑ5Ô5ˆD�‰MˆMˆMð	6ð 	6r=   Úcallsúlist[Any] | Noneútuple[list[Any], bool]c                ó>  — |s|pg dfS g }d}|D ]‹}t          |t          ¦  «        r_d|v r[|d         �S|                      |d         ¦  «        }||d         k    r,t          |¦  «        }||d<   |                     |¦  «         d}Œv|                     |¦  «         ŒŒ||fS )u•  Walk a list of tool-call (or invalid-tool-call) dicts.

        Returns `(new_list, changed)`. Each element's `args` is run
        through `_redact_value` regardless of its type â€” `tool_call.args`
        is a dict, `invalid_tool_call.args` is a raw JSON string, and
        `_redact_value` handles both shapes uniformly. If nothing
        changed, returns the input list and `changed=False`.
        FÚargsNT)rU   rY   rN   Úappend)r:   rt   Ú	new_callsÚchangedÚtcr`   Únew_tcs          r<   Ú_redact_tool_call_listz,_PIIStreamTransformer._redact_tool_call_listÜ   sÏ   € ð ð 	&Ø�;˜B Ð%Ð%Ø!ˆ	ØˆØð 		!ð 		!ˆBÝ˜"�dÑ#Ô#ð ¨°"¨¨¸¸F¼Ð9OØ×-Ò-¨b°¬jÑ9Ô9�Ø˜r &œzÒ)Ð)Ý! "™XœX�FØ%-�F˜6‘NØ×$Ò$ VÑ,Ô,Ð,Ø"�GØØ×Ò˜RÑ Ô Ð Ð Ø˜'Ð!Ð!r=   Úvaluer   c                ó  ‡ — t          |t          ¦  «        r=|s|S ‰ j                             |¦  «        }|s|S t	          ||‰ j        j        ¦  «        S t          |t          ¦  «        r‰                      |¦  «        S t          |t          ¦  «        r ˆ fd„| 	                    ¦   «         D ¦   «         S t          |t          ¦  «        rˆ fd„|D ¦   «         S t          |t          ¦  «        rt          ˆ fd„|D ¦   «         ¦  «        S |S )uz  Recursively redact PII in string leaves of a nested structure.

        Returns a new value where every `str` leaf that contains PII has
        been replaced (or emptied under `block`). Non-string leaves and
        the structure itself are preserved.

        `BaseMessage` payloads (typically `ToolMessage` from
        `tool-finished.output`, or any message reached via the `values`
        channel) return a fresh copy with `.content` redacted plus
        `AIMessage.tool_calls[*].args` / `invalid_tool_calls[*].args`
        walked. The original object stays intact for state-level
        enforcers (`after_model`, `before_model` with
        `apply_to_tool_results`) to act on independently.

        Scope mirrors the pre-streaming state-level surfaces:
        `.content` (string or list-of-content-blocks) and `tool_calls`
        args. Other message attributes (`additional_kwargs`,
        `response_metadata`, `ToolMessage.artifact`) are intentionally
        not walked here â€” they aren't scrubbed in graph state by the
        existing hooks, so scrubbing them on the wire would create
        a wire/state divergence.
        c                óB   •— i | ]\  }}|‰                      |¦  «        “ŒS r*   ©rN   )Ú.0ÚkÚvr:   s      €r<   ú
<dictcomp>z7_PIIStreamTransformer._redact_value.<locals>.<dictcomp>  s-   ø€ ÐGÐGÐG±°°A�A�t×)Ò)¨!Ñ,Ô,ÐGÐGÐGr=   c                ó:   •— g | ]}‰                      |¦  «        ‘ŒS r*   r‚   ©rƒ   r…   r:   s     €r<   ú
<listcomp>z7_PIIStreamTransformer._redact_value.<locals>.<listcomp>  s'   ø€ Ð9Ð9Ð9¨a�D×&Ò& qÑ)Ô)Ð9Ð9Ð9r=   c              3  óB   •K  — | ]}‰                      |¦  «        V — Œd S r3   r‚   rˆ   s     €r<   ú	<genexpr>z6_PIIStreamTransformer._redact_value.<locals>.<genexpr>  s1   øè è € Ð>Ð>°1˜×+Ò+¨AÑ.Ô.Ð>Ð>Ð>Ð>Ð>Ð>r=   )rU   rZ   r6   ri   r   rj   r   rX   rY   Úitemsrp   rV   )r:   r   rl   s   `  r<   rN   z#_PIIStreamTransformer._redact_valueõ   s!  ø€ õ. �e�SÑ!Ô!ð 		GØð Ø�Ø”j×)Ò)¨%Ñ0Ô0ˆGØð Ø�õ " %¨°$´*Ô2EÑFÔFÐFÝ�e�[Ñ)Ô)ð 	4Ø×,Ò,¨UÑ3Ô3Ð3Ý�e�TÑ"Ô"ð 	HØGÐGÐGÐG¸¿º¹¼ÐGÑGÔGÐGÝ�e�TÑ"Ô"ð 	:Ø9Ð9Ð9Ð9°5Ð9Ñ9Ô9Ð9Ý�e�UÑ#Ô#ð 	?ÝÐ>Ð>Ð>Ð>¸Ð>Ñ>Ô>Ñ>Ô>Ð>Øˆr=   r   c                ó  — i }|j         }t          |t          ¦  «        r=|r;| j                             |¦  «        }|rt          ||| j        j        ¦  «        |d<   n7t          |t          ¦  «        r"|r |                      |¦  «        }||k    r||d<   t          |t          ¦  «        rH|  
                    |j        ¦  «        \  }}|r||d<   |  
                    |j        ¦  «        \  }}	|	r||d<   |s|S |                     |¬¦  «        S )zCReturn a fresh copy of `value` with PII-carrying surfaces redacted.ÚcontentÚ
tool_callsÚinvalid_tool_calls)Úupdate)rŽ   rU   rZ   r6   ri   r   rj   rp   rN   r	   r~   r�   r�   Ú
model_copy)
r:   r   r‘   rŽ   rl   Úredacted_contentÚnew_tc_listÚ
tc_changedÚnew_inv_listÚinv_changeds
             r<   rX   z*_PIIStreamTransformer._redact_base_message   s8  € à!#ˆà”-ˆÝ�g�sÑ#Ô#ð 		5¨ð 		5Ø”j×)Ò)¨'Ñ2Ô2ˆGØð ZÝ$2°7¸GÀTÄZÔEXÑ$YÔ$Y��yÑ!øÝ˜¥Ñ&Ô&ð 	5¨7ð 	5ð  $×1Ò1°'Ñ:Ô:ÐØ 7Ò*Ð*Ø$4��yÑ!õ �e�YÑ'Ô'ð 	<Ø&*×&AÒ&AÀ%ÔBRÑ&SÔ&SÑ#ˆK˜Øð 3Ø'2��|Ñ$Ø(,×(CÒ(CÀEÔD\Ñ(]Ô(]Ñ%ˆL˜+Øð <Ø/;�Ð+Ñ,àð 	ØˆLØ×Ò vÐÑ.Ô.Ð.r=   r^   rQ   c                óÌ  — |                      d¦  «        }t          |t          ¦  «        sd S |                      d¦  «        }|dk    r|                      |||d¦  «         d S |dk    r|                      |||d¦  «         d S |dk    rX|                      d¦  «        }t          |t          ¦  «        r0|                      d¦  «        d	v r|                      |¦  «         d S d S d S d S )
Nrn   Útypez
text-deltaÚtextzreasoning-deltaÚ	reasoningzblock-deltaÚfields>   Útool_call_chunkÚserver_tool_call_chunk)rM   rU   rY   Ú_mutate_string_field_deltaÚ_mutate_tool_call_chunk_delta)r:   r^   rQ   rn   Ú
delta_typerœ   s         r<   r[   z#_PIIStreamTransformer._mutate_delta@  s  € Ø—’˜GÑ$Ô$ˆÝ˜%¥Ñ&Ô&ð 	ØˆFØ—Y’Y˜vÑ&Ô&ˆ
Ø˜Ò%Ð%Ø×+Ò+¨E°7¸FÀFÑKÔKÐKØˆFØÐ*Ò*Ð*ð ×+Ò+¨E°7¸FÀKÑPÔPÐPØˆFØ˜Ò&Ð&Ø—Y’Y˜xÑ(Ô(ˆFÝ˜&¥$Ñ'Ô'ð ;¨F¯JªJ°vÑ,>Ô,>ð Cð -ð -ð ×2Ò2°6Ñ:Ô:Ð:Ð:Ð:ð 'Ð&ð;ð ;ð -ð -r=   rn   Úfieldc                óø  — |                      |¦  «        }t          |t          ¦  «        r|sdS |                      d¦  «        }t          |t          ¦  «        sdS ||f}| j                              |d¦  «        }||z   }	| j                             |	¦  «        }
|
rt          |	|
| j        j        ¦  «        }	t          dt          |	¦  «        | j        z
  ¦  «        }|	|d…         | j        |<   |	d|…         ||<   dS )a[  Apply the lookback-buffer redaction to a string field on a delta.

        Shared by `text-delta` (`field="text"`) and `reasoning-delta`
        (`field="reasoning"`). Buffer is keyed by `(run_id, block_index)`;
        block indices are unique within a message so different block
        types share the same key space without collision.
        NÚindexrR   r   )rM   rU   rZ   r/   r8   r6   ri   r   rj   ro   rW   r7   )r:   rn   r^   rQ   r¢   rš   r¤   Úkeyrq   rr   rl   rs   s               r<   rŸ   z0_PIIStreamTransformer._mutate_string_field_delta^  s  € ð �yŠy˜ÑÔˆÝ˜$¥Ñ$Ô$ð 	¨Dð 	ØˆFØ—’˜GÑ$Ô$ˆÝ˜%¥Ñ%Ô%ð 	ØˆFà�uˆoˆØŒ}× Ò   bÑ)Ô)ˆØ˜$‘;ˆð ”*×%Ò% hÑ/Ô/ˆØð 	NÝ% h°¸¼Ô9LÑMÔMˆHå�q�#˜h™-œ-¨$¬.Ñ8Ñ9Ô9ˆØ% h i iÔ0ˆŒ�cÑØ 	  	Ô*ˆˆe‰ˆˆr=   rœ   c                ó4  — |                      d¦  «        }t          |t          ¦  «        r|sdS | j                             |¦  «        }|rt          ||| j        j        ¦  «        }t          dt          |¦  «        | j	        z
  ¦  «        }|d|…         |d<   dS )u  Redact cumulative tool-call args with lookback withholding.

        Each `tool_call_chunk` `block-delta` event carries the full
        accumulated args string (verified against `_compat_bridge.py`
        â€” `delta_source = current` for these block types â€” and against
        the consumer-side `_merge_block_delta_into_store`, which
        replaces wholesale rather than appends).

        Detection runs on the full cumulative args so any complete PII
        anywhere in the string is redacted before emission. Lookback
        withholding then trims the trailing the lookback window characters
        from what reaches the consumer â€” those characters might be the
        start of a partial PII match that completes in a future
        cumulative delta. The trimmed tail surfaces at `content-block-
        finish` where `_finalize_block` redacts the parsed args dict.

        For args that fit within the lookback window (the typical case),
        this withholds the entire args string during streaming â€” the
        redacted args dict appears only at finalize. For args that
        exceed the lookback window, the safe prefix streams incrementally
        as the cumulative state grows. PII that appears more than
        the lookback window characters from the cumulative tail in a
        delta where it hasn't yet completed can still surface in the
        emit prefix â€” same residual exposure as PII longer than
        the lookback window on the text path. The `content-block-finish`
        snapshot redaction is the backstop.
        rx   Nr   )
rM   rU   rZ   r6   ri   r   rj   ro   rW   r7   )r:   rœ   rx   rl   rs   s        r<   r    z3_PIIStreamTransformer._mutate_tool_call_chunk_delta‡  sš   € ð8 �zŠz˜&Ñ!Ô!ˆÝ˜$¥Ñ$Ô$ð 	¨Dð 	ØˆFà”*×%Ò% dÑ+Ô+ˆØð 	Fõ " $¨°´Ô1DÑEÔEˆDå�q�#˜d™)œ) d¤nÑ4Ñ5Ô5ˆØ˜i˜x˜iœˆˆv‰ˆˆr=   c                óê  — |                      d¦  «        }t          |t          ¦  «        sd S ||f}|                      d¦  «        }t          |t          ¦  «        r}|                      d¦  «        }|dk    r|                      |d¦  «         nK|dk    r|                      |d¦  «         n.|dv r*d|v r&|d         �|                      |d         ¦  «        |d<   | j                             |d ¦  «         d S )Nr¤   rŽ   r™   rš   r›   >   Ú	tool_callÚserver_tool_callÚinvalid_tool_callrx   )rM   rU   r/   rY   Ú_finalize_string_fieldrN   r8   rh   )r:   r^   rQ   r¤   r¥   rŽ   Úctypes          r<   r\   z%_PIIStreamTransformer._finalize_block±  s  € Ø—’˜GÑ$Ô$ˆÝ˜%¥Ñ%Ô%ð 	ØˆFØ�uˆoˆð —+’+˜iÑ(Ô(ˆÝ�g�tÑ$Ô$ð 	FØ—K’K Ñ'Ô'ˆEØ˜ŠˆØ×+Ò+¨G°VÑ<Ô<Ð<Ð<Ø˜+Ò%Ð%Ø×+Ò+¨G°[ÑAÔAÐAÐAàÐOÐOÐOØ˜gÐ%Ð%Ø˜F”OÐ/ð
 #'×"4Ò"4°W¸V´_Ñ"EÔ"E�˜‘ØŒ×Ò˜#˜tÑ$Ô$Ð$Ð$Ð$r=   rŽ   c                óÚ   — |                      |¦  «        }t          |t          ¦  «        r|sdS | j                             |¦  «        }|sdS t          ||| j        j        ¦  «        ||<   dS )zöRe-redact a string content-block field on `content-block-finish`.

        Used for `text` and `reasoning` content blocks. Under
        `strategy="block"` `apply_strategy` raises `PIIDetectionError`,
        failing the run immediately.
        N)rM   rU   rZ   r6   ri   r   rj   )r:   rŽ   r¢   rš   rl   s        r<   r«   z,_PIIStreamTransformer._finalize_string_fieldË  ss   € ð �{Š{˜5Ñ!Ô!ˆÝ˜$¥Ñ$Ô$ð 	¨Dð 	ØˆFØ”*×%Ò% dÑ+Ô+ˆØð 	ØˆFÝ'¨¨g°t´zÔ7JÑKÔKˆ�‰ˆˆr=   c                óH   ‡— ˆfd„| j         D ¦   «         }|D ]
}| j         |= Œd S )Nc                ó,   •— g | ]}|d          ‰k    ¯|‘ŒS )r   r*   )rƒ   r¥   rQ   s     €r<   r‰   z3_PIIStreamTransformer._drop_run.<locals>.<listcomp>ß  s'   ø€ ÐBÐBÐB˜°°Q´¸6Ò1AÐ1A�Ð1AÐ1AÐ1Ar=   )r8   )r:   rQ   Ústaler¥   s    `  r<   r]   z_PIIStreamTransformer._drop_runÚ  sG   ø€ ð
 CÐBÐBÐB ¤ÐBÑBÔBˆØð 	#ð 	#ˆCØ”˜cÐ"Ð"ð	#ð 	#r=   c                ój   — | j                              ¦   «          | j                             ¦   «          d S r3   ©r8   Úclearr9   r@   s    r<   Úfinalizez_PIIStreamTransformer.finalizeã  ó1   € ØŒ×ÒÑÔÐØÔ× Ò Ñ"Ô"Ð"Ð"Ð"r=   ÚerrÚBaseExceptionc                ój   — | j                              ¦   «          | j                             ¦   «          d S r3   r²   )r:   r¶   s     r<   Úfailz_PIIStreamTransformer.failç  rµ   r=   )r*   )r,   r-   r.   r   r+   r/   r0   r1   )r0   r>   )rB   r!   r0   rC   )rL   r>   rc   rZ   r0   r1   )rt   ru   r0   rv   )r   r   r0   r   )r   r   r0   r   )r^   r>   rQ   rZ   r0   r1   )
rn   r>   r^   r>   rQ   rZ   r¢   rZ   r0   r1   )rœ   r>   r0   r1   )rŽ   r>   r¢   rZ   r0   r1   )rQ   rZ   r0   r1   )r0   r1   )r¶   r·   r0   r1   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r%   Ú__annotations__r)   Ú_DEFAULT_STREAM_LOOKBACKr5   rA   rI   rH   rF   rG   rg   r~   rN   rX   r[   rŸ   r    r\   r«   r]   r´   r¹   Ú__classcell__©r;   s   @r<   r$   r$   2   så  ø€ € € € € € ðð ð '+€OÐ*Ð*Ð*Ñ*Ø7VÐÐVÐVÐVÑVð "$ð0ð
 1ð0ð 0ð 0ð 0ð 0ð 0ð 0ð 0ð"ð ð ð ð
ð ð ð ðð ð ð ð"!ð !ð !ð !ðF#ð #ð #ð #ðJ6ð 6ð 6ð 6ð>"ð "ð "ð "ð2)ð )ð )ð )ðV/ð /ð /ð /ð@;ð ;ð ;ð ;ð<'+ð '+ð '+ð '+ðR()ð ()ð ()ð ()ðT%ð %ð %ð %ð4Lð Lð Lð Lð#ð #ð #ð #ð#ð #ð #ð #ð#ð #ð #ð #ð #ð #ð #ð #r=   r$   c                  óÞ   ‡ — e Zd ZdZddddddœd$ˆ fd„Zed%d„¦   «         Zd&d„Z edg¬¦  «        e	d'd „¦   «         ¦   «         Z
 edg¬¦  «        d'd!„¦   «         Ze	d'd"„¦   «         Zd'd#„Zˆ xZS )(ÚPIIMiddlewarea	  Detect and handle Personally Identifiable Information (PII) in conversations.

    This middleware detects common PII types and applies configurable strategies
    to handle them. It can detect emails, credit cards, IP addresses, MAC addresses, and
    URLs in both user input and agent output.

    Built-in PII types:

    - `email`: Email addresses
    - `credit_card`: Credit card numbers (validated with Luhn algorithm)
    - `ip`: IP addresses (validated with stdlib)
    - `mac_address`: MAC addresses
    - `url`: URLs (both `http`/`https` and bare URLs)

    Strategies:

    - `block`: Raise an exception when PII is detected
    - `redact`: Replace PII with `[REDACTED_TYPE]` placeholders
    - `mask`: Partially mask PII (e.g., `****-****-****-1234` for credit card)
    - `hash`: Replace PII with deterministic hash (e.g., `<email_hash:a1b2c3d4>`)

    Strategy Selection Guide:

    | Strategy | Preserves Identity? | Best For                                |
    | -------- | ------------------- | --------------------------------------- |
    | `block`  | N/A                 | Avoid PII completely                    |
    | `redact` | No                  | General compliance, log sanitization    |
    | `mask`   | No                  | Human readability, customer service UIs |
    | `hash`   | Yes (pseudonymous)  | Analytics, debugging                    |

    Example:
        ```python
        from langchain.agents.middleware import PIIMiddleware
        from langchain.agents import create_agent

        # Redact all emails in user input
        agent = create_agent(
            "openai:gpt-5.5",
            middleware=[
                PIIMiddleware("email", strategy="redact"),
            ],
        )

        # Use different strategies for different PII types
        agent = create_agent(
            "openai:gpt-5.5",
            middleware=[
                PIIMiddleware("credit_card", strategy="mask"),
                PIIMiddleware("url", strategy="redact"),
                PIIMiddleware("ip", strategy="hash"),
            ],
        )

        # Custom PII type with regex
        agent = create_agent(
            "openai:gpt-5.5",
            middleware=[
                PIIMiddleware("api_key", detector=r"sk-[a-zA-Z0-9]{32}", strategy="block"),
            ],
        )
        ```
    ÚredactNTF)rj   ri   Úapply_to_inputÚapply_to_outputÚapply_to_tool_resultsÚpii_typeúALiteral['email', 'credit_card', 'ip', 'mac_address', 'url'] | strrj   ú*Literal['block', 'redact', 'mask', 'hash']ri   ú,Callable[[str], list[PIIMatch]] | str | NonerÅ   rC   rÆ   rÇ   r0   r1   c               óŒ  •— t          ¦   «                              ¦   «          || _        || _        || _        t          |||¬¦  «                             ¦   «         | _        | j        j        | _        | j        j	        | _	        | j        j
        | _
        | j        s| j        r#t          t          | j        ¬¦  «        f| _        dS dS )u‡
  Initialize the PII detection middleware.

        Args:
            pii_type: Type of PII to detect.

                Can be a built-in type (`email`, `credit_card`, `ip`, `mac_address`,
                `url`) or a custom type name.
            strategy: How to handle detected PII.

                Options:

                * `block`: Raise `PIIDetectionError` when PII is detected
                * `redact`: Replace with `[REDACTED_TYPE]` placeholders
                * `mask`: Partially mask PII (show last few characters)
                * `hash`: Replace with deterministic hash (format: `<type_hash:digest>`)

            detector: Custom detector function or regex pattern.

                * If `Callable`: Function that takes content string and returns
                    list of `PIIMatch` objects
                * If `str`: Regex pattern to match PII
                * If `None`: Uses built-in detector for the `pii_type`
            apply_to_input: Whether to check user messages before model call.
            apply_to_output: Whether to check AI messages after model call.

                When `True`, a stream transformer is also installed so
                that every wire surface of an agent run is redacted in
                flight:

                * Streamed AI text deltas (`content-block-delta` of type
                  `text-delta`)
                * Streamed tool-call arguments (`content-block-delta`
                  with `tool_call_chunk` / `server_tool_call_chunk`
                  fields, plus the finalized `tool_call` content block
                  on `content-block-finish`)
                * Tool execution events on the `tools` channel
                  (`tool-started.input`, `tool-output-delta`,
                  `tool-finished.output`, `tool-error.message`)
                * State snapshots on the `values` channel â€” message
                  lists are walked and each message's `.content` is
                  redacted on a fresh copy (state itself stays intact
                  for `before_model` / `after_model` to act on
                  independently)

                State-level redaction via `after_model` (and
                `before_model` with `apply_to_tool_results`) remains the
                canonical enforcer; the streaming transformer ensures
                consumers reading `astream_events(version="v3")` or
                `run.messages` / `run.tool_calls` / `run.values` never
                see PII on the wire.
            apply_to_tool_results: Whether to check tool result messages after tool execution.

        Raises:
            ValueError: If `pii_type` is not built-in and no detector is provided.
        )rÈ   rj   ri   )r.   N)r4   r5   rÅ   rÆ   rÇ   r   ÚresolveÚ_resolved_rulerÈ   rj   ri   r   r$   Útransformers)r:   rÈ   rj   ri   rÅ   rÆ   rÇ   r;   s          €r<   r5   zPIIMiddleware.__init__,  s×   ø€ õF 	‰Œ×ÒÑÔÐà,ˆÔØ.ˆÔØ%:ˆÔ"å5BØØØð6
ñ 6
ô 6
÷ Š'‰)Œ)ð	 	Ôð
 Ô+Ô4ˆŒØÔ+Ô4ˆŒØÔ+Ô4ˆŒð Ôð 	 4Ô#=ð 	åÝ)ØÔ,ðñ ô ð!ˆDÔÐÐð	ð 	r=   rZ   c                ó0   — | j         j        › d| j        › d�S )zName of the middleware.ú[ú])r;   rº   rÈ   r@   s    r<   ÚnamezPIIMiddleware.name’  s"   € ð ”.Ô)Ð<Ð<¨D¬MÐ<Ð<Ð<Ð<r=   rŽ   útuple[str, list[PIIMatch]]c                ól   — |                       |¦  «        }|s|g fS t          ||| j        ¦  «        }||fS )z<Apply the configured redaction rule to the provided content.)ri   r   rj   )r:   rŽ   rl   Ú	sanitizeds       r<   Ú_process_contentzPIIMiddleware._process_content—  sC   € à—-’- Ñ(Ô(ˆØð 	Ø˜B�;ÐÝ" 7¨G°T´]ÑCÔCˆ	Ø˜'Ð!Ð!r=   Úend)Úcan_jump_toÚstateúAgentState[Any]ÚruntimeúRuntime[ContextT]údict[str, Any] | Nonec                ó°  — | j         s	| j        sdS |d         }|sdS t          |¦  «        }d}| j         r«d}d}t          t	          |¦  «        dz
  dd¦  «        D ])}t          ||         t          ¦  «        r||         }|} nŒ*|�Z|rX|j        rQt          |j        ¦  «        }	|  	                    |	¦  «        \  }
}|r#t          |
|j
        |j        ¬¦  «        }|||<   d}| j        réd}t          t	          |¦  «        dz
  dd¦  «        D ]!}t          ||         t          ¦  «        r|} nŒ"|�¢t          |dz   t	          |¦  «        ¦  «        D ]�}||         }t          |t          ¦  «        rb|}|j        sŒ)t          |j        ¦  «        }	|  	                    |	¦  «        \  }
}|sŒXt          |
|j
        |j        |j        ¬¦  «        }|||<   d}Œ‚|rd|iS dS )	a•  Check user messages and tool results for PII before model invocation.

        Args:
            state: The current agent state.
            runtime: The langgraph runtime.

        Returns:
            Updated state with PII handled according to strategy, or `None` if no PII
                detected.

        Raises:
            PIIDetectionError: If PII is detected and strategy is `'block'`.
        Nr&   Fé   éÿÿÿÿ)rŽ   ÚidrÓ   T)rŽ   râ   rÓ   rc   )rÅ   rÇ   rp   ÚrangerW   rU   r   rŽ   rZ   r×   râ   rÓ   r	   r   rc   )r:   rÚ   rÜ   r&   Únew_messagesÚany_modifiedÚlast_user_msgÚlast_user_idxÚirŽ   Únew_contentrl   Úupdated_messageÚlast_ai_idxrk   Útool_msgs                   r<   Úbefore_modelzPIIMiddleware.before_modelŸ  se  € ð( Ô"ð 	¨4Ô+Eð 	Ø�4à˜Ô$ˆØð 	Ø�4å˜H‘~”~ˆØˆð Ôð 	(à ˆMØ ˆMÝ�3˜x™=œ=¨1Ñ,¨b°"Ñ5Ô5ð ð �Ý˜h qœk­<Ñ8Ô8ð Ø$,¨Q¤K�MØ$%�MØ�Eðð
 Ð(¨]Ð(¸}Ô?TÐ(å˜mÔ3Ñ4Ô4�Ø'+×'<Ò'<¸WÑ'EÔ'EÑ$�˜Wàð (Ý2>Ø +Ø(Ô+Ø*Ô/ð3ñ 3ô 3�Oð 3B�L Ñ/Ø#'�Lð Ô%ð  	,àˆKÝ�3˜x™=œ=¨1Ñ,¨b°"Ñ5Ô5ð ð �Ý˜h qœk­9Ñ5Ô5ð Ø"#�KØ�Eðð Ð&å˜{¨Q™µ°H±´Ñ>Ô>ð ,ð ,�AØ" 1œ+�CÝ! #¥{Ñ3Ô3ð ,Ø#&˜Ø'Ô/ð %Ø$å"% hÔ&6Ñ"7Ô"7˜Ø/3×/DÒ/DÀWÑ/MÔ/MÑ,˜ Wà&ð %Ø$õ +6Ø$/Ø'œ{Ø!)¤Ø)1Ô)>ð	+ñ +ô +˜ð +:˜ Q™Ø'+˜øàð 	.Ø Ð-Ð-àˆtr=   c              ƒ  ó2   K  — |                       ||¦  «        S )a›  Async check user messages and tool results for PII before model invocation.

        Args:
            state: The current agent state.
            runtime: The langgraph runtime.

        Returns:
            Updated state with PII handled according to strategy, or `None` if no PII
                detected.

        Raises:
            PIIDetectionError: If PII is detected and strategy is `'block'`.
        )rí   ©r:   rÚ   rÜ   s      r<   Úabefore_modelzPIIMiddleware.abefore_modelÿ  s   è è € ð& × Ò  ¨Ñ0Ô0Ð0r=   c                ó°  — | j         sdS |d         }|sdS d}d}t          t          |¦  «        dz
  dd¦  «        D ]%}||         }t          |t          ¦  «        r|}|} nŒ&|�	|r|j        sdS t          |j        ¦  «        }|                      |¦  «        \  }	}
|
sdS t	          |	|j        |j	        |j
        ¬¦  «        }t          |¦  «        }|||<   d|iS )a  Check AI messages for PII after model invocation.

        Args:
            state: The current agent state.
            runtime: The langgraph runtime.

        Returns:
            Updated state with PII handled according to strategy, or None if no PII
                detected.

        Raises:
            PIIDetectionError: If PII is detected and strategy is `'block'`.
        Nr&   rà   rá   )rŽ   râ   rÓ   r�   )rÆ   rã   rW   rU   r	   rŽ   rZ   r×   râ   rÓ   r�   rp   )r:   rÚ   rÜ   r&   Úlast_ai_msgrë   rè   rk   rŽ   ré   rl   rê   rä   s                r<   Úafter_modelzPIIMiddleware.after_model  s)  € ð& Ô#ð 	Ø�4à˜Ô$ˆØð 	Ø�4ð ˆØˆÝ•s˜8‘}”} qÑ(¨"¨bÑ1Ô1ð 	ð 	ˆAØ˜1”+ˆCÝ˜#�yÑ)Ô)ð Ø!�Ø�Ø�ðð
 Ð kÐ¸Ô9LÐØ�4õ �kÔ)Ñ*Ô*ˆØ#×4Ò4°WÑ=Ô=Ñˆ�Wàð 	Ø�4õ $ØØŒ~ØÔ!Ø"Ô-ð	
ñ 
ô 
ˆõ ˜H‘~”~ˆØ$3ˆ�[Ñ!à˜LÐ)Ð)r=   c              ƒ  ó2   K  — |                       ||¦  «        S )a…  Async check AI messages for PII after model invocation.

        Args:
            state: The current agent state.
            runtime: The langgraph runtime.

        Returns:
            Updated state with PII handled according to strategy, or None if no PII
                detected.

        Raises:
            PIIDetectionError: If PII is detected and strategy is `'block'`.
        )ró   rï   s      r<   Úaafter_modelzPIIMiddleware.aafter_modelP  s   è è € ð$ ×Ò  wÑ/Ô/Ð/r=   )rÈ   rÉ   rj   rÊ   ri   rË   rÅ   rC   rÆ   rC   rÇ   rC   r0   r1   )r0   rZ   )rŽ   rZ   r0   rÔ   )rÚ   rÛ   rÜ   rÝ   r0   rÞ   )rº   r»   r¼   r½   r5   ÚpropertyrÓ   r×   r   r   rí   rð   ró   rõ   rÀ   rÁ   s   @r<   rÃ   rÃ   ì  sF  ø€ € € € € ð=ð =ðJ @HØAEØ#Ø %Ø&+ðdð dð dð dð dð dð dð dðL ð=ð =ð =ñ „Xð=ð"ð "ð "ð "ð €[˜e˜WÐ%Ñ%Ô%Øð\ð \ð \ñ „Xñ &Ô%ð\ð| €[˜e˜WÐ%Ñ%Ô%ð1ð 1ð 1ñ &Ô%ð1ð( ð9*ð 9*ð 9*ñ „Xð9*ðv0ð 0ð 0ð 0ð 0ð 0ð 0ð 0r=   rÃ   )r   r   rÃ   r   r   r   r   r   N)/r½   Ú
__future__r   Ú	functoolsr   Útypingr   r   r   r   Úlangchain_core.messagesr	   r
   r   r   r   Úlanggraph.streamr   Útyping_extensionsr   Ú&langchain.agents.middleware._redactionr   r   r   r   r   r   r   r   r   r   Ú!langchain.agents.middleware.typesr   r   r   r   r   Úcollections.abcr   Úlanggraph.runtimer    Úlanggraph.stream._typesr!   r¿   r$   rÃ   Ú__all__r*   r=   r<   ú<module>r     s9  ðØ 7Ð 7à "Ð "Ð "Ð "Ð "Ð "à Ð Ð Ð Ð Ð Ø 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8Ð 8à aÐ aÐ aÐ aÐ aÐ aÐ aÐ aÐ aÐ aÐ aÐ aÐ aÐ aØ .Ð .Ð .Ð .Ð .Ð .Ø &Ð &Ð &Ð &Ð &Ð &ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ð 6Ø(Ð(Ð(Ð(Ð(Ð(à)Ð)Ð)Ð)Ð)Ð)Ø5Ð5Ð5Ð5Ð5Ð5ð Ð ððw#ð w#ð w#ð w#ð w#Ð-ñ w#ô w#ð w#ðtv0ð v0ð v0ð v0ð v0�O J¨yÔ$9¸8ÀYÐ$NÔOñ v0ô v0ð v0ðr	ð 	ð 	€€€r=   