
    `gjg                         d Z ddlZddlZddlmZmZmZmZ ddlm	Z	 ddl
mZmZ dedee   fdZd	ed
eeeeef         dee   fdZ G d de	      ZddlmZ  ede       y)u   OpenAI Responses API (Codex) transport.

Delegates to the existing adapter functions in agent/codex_responses_adapter.py.
This transport owns format conversion and normalization — NOT client lifecycle,
streaming, or the _run_codex_stream() call path.
    N)AnyDictListOptional)ProviderTransport)NormalizedResponseToolCallvaluereturnc                     | yt        |       j                         }|syt        |      dk  r|S t        j                  |j                  dd            j                         dd }d| S )zCReturn a provider-safe cache key without changing session identity.N@   utf-8replaceerrors   pck_)strstriplenhashlibsha256encode	hexdigest)r
   keydigests      I/root/.hermes/venv/lib/python3.12/site-packages/agent/transports/codex.py_bounded_prompt_cache_keyr      sj    }
e*


C
3x2~
^^CJJwyJABLLNsPRSF&?    instructionstoolsc                     | s|syd}|r.t        d |D        d       }t        j                  |ddd	      }| xs d d
| }t        j                  |j                  dd            j                         dd }d| S )u  Content-address the prompt cache key from the static request prefix.

    Returns ``pck_<sha256[:24]>`` of (instructions + sorted tool schemas), or
    None when there is nothing static to key on. The cache key is a routing
    hint only — never a correctness boundary — so two requests sharing a system
    prompt and tool set intentionally resolve to the same warm prefix bucket.

    The fix this exists for: recurring cron jobs build session_id as
    ``cron_<id>_<timestamp>``, so using session_id as the cache key made every
    fire cache-cold. The static prefix (identity + tools) is identical across
    fires, so hashing it gives a stable key that stays warm within the
    provider's cache TTL. Sorting tools by name keeps the hash insertion-order
    independent.
    N c              3   B   K   | ]  }t        |t              s|  y wN)
isinstancedict.0ts     r   	<genexpr>z%_content_cache_key.<locals>.<genexpr>2   s     51At!4Q5s   c                 d    t        | j                  d      xs | j                  d      xs d      S )Nnametyper#   )r   get)r*   s    r   <lambda>z$_content_cache_key.<locals>.<lambda>3   s%    #aeeFmBquuV}BC r   )r   TF),:)	sort_keysensure_ascii
separators r   r   r   r   r   )sortedjsondumpsr   r   r   r   )r    r!   
tools_partsorted_toolscontentr   s         r   _content_cache_keyr=      s     J55C
 ZZDu


 #$D5G^^GNN79NEFPPRSVTVWF&?r   c                   2   e Zd ZU dZdZee   ed<   edefd       Z	de
eef   defdZdee
eef      defd	Zd
ee
eef      defdZ	 ddedee
eef      d
eee
eef         de
eef   fdZdedefdZdedefdZddddedededefdZdedefdZy)ResponsesApiTransportzyTransport for api_mode='codex_responses'.

    Wraps the functions extracted into codex_responses_adapter.py (PR 1).
    N_last_issuer_kindr   c                      y)Ncodex_responses )selfs    r   api_modezResponsesApiTransport.api_modeL   s     r   paramsc                     ddl m}  ||j                  d      du |j                  d      du |j                  d      du |j                  d            S )	z>Classify the current Responses endpoint from transport params.r   )_classify_responses_issueris_xai_responsesTis_github_responsesis_codex_backendbase_url)rI   rJ   rK   rL   )agent.codex_responses_adapterrH   r/   )rD   rF   rH   s      r   _resolve_issuer_kindz*ResponsesApiTransport._resolve_issuer_kindP   sT    L)#ZZ(:;tC &

+@ AT I#ZZ(:;tCZZ
+	
 	
r   messagesc                     ddl m} | j                  |      }|| _         |||j	                  d      du |j	                  d      du t        |j	                  dd            |      S )z:Convert OpenAI chat messages to Responses API input items.r   )!_chat_messages_to_responses_inputrI   TrJ   replay_encrypted_reasoningrI   rJ   rR   current_issuer_kind)rM   rQ   rN   r@   r/   bool)rD   rO   kwargsrQ   issuers        r   convert_messagesz&ResponsesApiTransport.convert_messagesZ   sm    S**62!'0#ZZ(:;tC &

+@ AT I'+

7>( !'
 	
r   r!   c                     ddl m}  ||      S )zBConvert OpenAI tool schemas to Responses API function definitions.r   )_responses_tools)rM   rZ   )rD   r!   rZ   s      r   convert_toolsz#ResponsesApiTransport.convert_toolsi   s    B&&r   modelc           
      
   ddl m}m} ddlm} |j                  dd      }|}	|sM|rK|d   j                  d      dk(  r4t        |d   j                  d      xs d      j                         }|d	d
 }	|s|}|j                  d      du }
|j                  d      du }|j                  d      du }t        |j                  dd            }| j                  |      }|| _
        d}d}|j                  d      }|r<t        |t              r,|j                  d      du rd}n|j                  d      r|d   }ddi}d|xs dj                         v rd|d<   |j                  dd      r|j                  dddd       |j                  ||      } ||      }|r^|r\t        d |D              }|rH|D cg c](  }t        |t              r|j                  d      dk(  s|* }}|j!                  ddi       |}|| ||	||
||       dd!}|r||d"<   d#|d$<   d|d%<   |j                  d&      }t#        ||      xs |}|
s	|s|r||d'<   |r"|r dd(lm} |rd)gng |d*<    ||      rAd|i|d+<   n9|r.|
r|j                  d,      }|"||d+<   n|d#d-|d+<   |rd)gng |d*<   n	|
s|sg |d*<   |j                  d.      }|r|j                  |       d'|v r(t)        |d'         }|r||d'<   n|j+                  d'd
       |r|j+                  d/d
       |j                  d0|j                  d0            }t        |t,        t.        f      r?t        |t              s/dt/        |      cxk  rt/        d1      k  rn nt/        |      |d0<   n|j+                  d0d
       |rt)        |      }|r{|j                  d2      } i }!t        | t              rI|!j                  | j1                         D "#ci c]  \  }"}#|"r|#t        |"      t        |#        c}#}"       ||!d&<   ||!d3<   |!|d2<   |j                  d4      }$|$|s|$|d5<   |r|r|j                  d2      } i }!t        | t              rI|!j                  | j1                         D "#ci c]  \  }"}#|"r|#t        |"      t        |#        c}#}"       ||!d6<   |!|d2<   |j                  d7      }%i }&t        |%t              r|&j                  |%       |&j3                  d'|       |&|d7<   |j                  d7      }'t        |'t              r-d'|'v r)t)        |'d'         }|r||'d'<   |S |'j+                  d'd
       |S c c}w c c}#}"w c c}#}"w )8u  Build Responses API kwargs.

        Calls convert_messages and convert_tools internally.

        params:
            instructions: str — system prompt (extracted from messages[0] if not given)
            reasoning_config: dict | None — {effort, enabled}
            session_id: str | None — transcript/session id; drives the xAI
                x-grok-conv-id header and the Codex cache-scope headers, and is
                the fallback prompt_cache_key when there is no static prefix to
                content-address
            max_tokens: int | None — max_output_tokens
            timeout: float | None — per-request timeout forwarded to the SDK
            request_overrides: dict | None — extra kwargs merged in
            provider: str | None — provider name for backend-specific logic
            base_url: str | None — endpoint URL
            base_url_hostname: str | None — hostname for backend detection
            is_github_responses: bool — Copilot/GitHub models backend
            is_codex_backend: bool — chatgpt.com/backend-api/codex
            is_xai_responses: bool — xAI/Grok backend
            github_reasoning_extra: dict | None — Copilot reasoning params
        r   )rQ   rZ   )DEFAULT_AGENT_IDENTITYr    r#   rolesystemr<      NrJ   TrK   rI   rR   mediumreasoning_configenabledFeffortminimallowzgpt-5.6maxultrahigh)xhighrh   ri   c              3   h   K   | ]*  }t        |t              xr |j                  d       dk(   , yw)r-   
web_searchN)r&   r'   r/   r(   s     r   r+   z5ResponsesApiTransport.build_kwargs.<locals>.<genexpr>   s4      ( 1d#Ef(EE(s   02r-   rm   r.   rS   )r\   r    inputstorer!   autotool_choiceparallel_tool_calls
session_idprompt_cache_key)grok_supports_reasoning_effortzreasoning.encrypted_contentinclude	reasoninggithub_reasoning_extra)re   summaryrequest_overridesservice_tiertimeoutinfextra_headerszx-client-request-id
max_tokensmax_output_tokenszx-grok-conv-id
extra_body)rM   rQ   rZ   	run_agentr^   r/   r   r   rU   rN   r@   r&   r'   lowerupdateanyappendr=   agent.model_metadataru   r   popintfloatitems
setdefault)(rD   r\   rO   r!   rF   rQ   rZ   r^   r    payload_messagesrJ   rK   rI   rR   issuer_kindreasoning_effortreasoning_enabledrc   _effort_clampresponse_toolshas_client_web_searchr*   filteredrV   rs   	cache_keyru   github_reasoningrz   bounded_cache_keyr|   cache_scope_idexisting_extra_headersmerged_extra_headersr   r
   r   existing_extra_bodymerged_extra_bodyr   s(                                           r   build_kwargsz"ResponsesApiTransport.build_kwargsn   s   :	

 	5zz."5#HQKOOF3x?"8A;??9#=#CDJJL#+AB< 1L$jj)>?4G!::&89TA!::&89TA%)JJ3T:&
" //7!, $ !::&89
+;T B##I.%7$)!!%%h/#3H#= "E*"++--%*M'"::(%0  6&6!RS(,,-=?OP)%0^ $' ('( %! %-&q$/AEE&M\4Q    67!) (6 !1$7+E$/ 
 ,F7O$*F=!,0F()ZZ-
 '|^DR
	 #+;	)2F%&!1K 4N./SU 9 .e4'/1A&B{#"#)::.F#G #/*:F;'1Af&U{#7Q23WY y! %-= "F9"JJ':;MM+,' 9&AS:T U ->)*

-t4 JJ~t, **Y

9(=>we-w-E'N1U5\1 %gF9JJy$' 7zBN)/O)D&79$4d;(// /E.J.J.L *U"u'8  Hc%j0 6D$\2>L$%:;*>'ZZ-
!*:*4F&'
%+ZZ%@"35 0$7$++ +A*F*F*H&C5#4 C#e*, 6@ !12&:F?# #)**\":02-t4!(()<=(();YG#4F< ZZ-
j$',>*,L 9*EW:X Y 1B
-.  148Yz&s   1-U#U
6#U
responsec                 <   ddl m} |j                  d      xs | j                  } |||      \  }}d}|r(|j                  rg }|j                  D ]	  }i }	t        |d      r|j                  r|j                  |	d<   t        |d      r|j                  r|j                  |	d<   |j                  t        t        |d      r|j                  n#t        |d	      r|j                  j                  ndt        |d	      r|j                  j                  nt        |d
d      t        |d	      r|j                  j                  nt        |dd      |	xs d              i }	|r't        |d      r|j                  r|j                  |	d<   |r't        |d      r|j                   r|j                   |	d<   |r't        |d      r|j"                  r|j"                  |	d<   t%        |r|j&                  nd||xs d|rt        |d      r|j(                  ndd|	xs d      S )z=Normalize Codex Responses API response to NormalizedResponse.r   )_normalize_codex_responser   )r   Ncall_idresponse_item_ididfunctionr-   r#   	argumentsz{})r   r-   r   provider_datacodex_reasoning_itemscodex_message_itemsreasoning_detailsstoprw   )r<   
tool_callsfinish_reasonrw   usager   )rM   r   r/   r@   r   hasattrr   r   r   r	   r   r   r-   getattrr   r   r   r   r   r<   rw   )
rD   r   rV   r   r   msgr   r   tcr   s
             r   normalize_responsez(ResponsesApiTransport.normalize_response  s   	
 jj/I43I3I6x[Y]
3>>Jnn  "2y)bjj/1zzM),212r7J7J8:8K8KM"45!!( 'D 1ruuGTVXbLc8H8Him-4R-D))'RTV\^`Ja7>r:7Nbkk33T[\^`kmqTr"/"74	#  73 78S=V=V585N5NM1273 563;R;R363J3JM/073 349N9N141F1FM-.!#&CKKD!'16'*wsK/Hcmmd'/4
 	
r   c                    |yt        |dd      }t        |t              r|st        t        |dd      xs d      j	                         j                         }t        |dd      }t        |t              r;t        |j                  d      xs d      j	                         j                         }n6t        t        |dd      xs d      j	                         j                         }|dk(  xr |d	k(  S y
)u  Check Codex Responses API response has valid output structure.

        Returns True only if response.output is a non-empty list. Also treats
        terminal content-filter incomplete responses as valid: the Responses API
        may return status=incomplete with incomplete_details.reason='content_filter'
        and no output items. That is a provider refusal signal, not a malformed
        response, and must reach normalization so the agent loop can use the
        content-policy / fallback path instead of invalid-response retries.

        Does NOT check output_text fallback — the caller handles that with
        diagnostic logging for stream backfill recovery.
        NFoutputstatusr#   incomplete_detailsreason
incompletecontent_filterT)r   r&   listr   r   r   r'   r/   )rD   r   r   r   r   r   s         r   validate_responsez'ResponsesApiTransport.validate_response  s     8T2&$'v8R8>B?EEGMMOF!(3G!N,d3/33H=CDJJLRRTW%72FL"MSSU[[]\)Hf8H.HHr   Fallow_streamrJ   
api_kwargsr   rJ   c                   ddl m}  ||||      }d|v r(t        |d         }|r||d<   n|j                  dd       |j	                  d      }t        |t              r-d|v r)t        |d         }|r||d<   |S |j                  dd       |S )zValidate and sanitize Codex API kwargs before the call.

        Normalizes input items, strips unsupported fields, validates structure.
        r   )_preflight_codex_api_kwargsr   rt   Nr   )rM   r   r   r   r/   r&   r'   )rD   r   r   rJ   r   
normalizedboundedr   s           r   preflight_kwargsz&ResponsesApiTransport.preflight_kwargs  s     	N0% 3


 +/
;M0NOG18
-.148^^L1
j$',>*,L/
;M0NOG18
-.  148r   
raw_reasonc                 4    ddddd}|j                  |d      S )aK  Map Codex response.status to OpenAI finish_reason.

        Codex uses response.status ('completed', 'incomplete') +
        response.incomplete_details.reason for granular mapping.
        This method handles the simple status string; the caller
        should check incomplete_details separately for 'max_output_tokens'.
        r   length)	completedr   failed	cancelled)r/   )rD   r   _MAPs      r   map_finish_reasonz'ResponsesApiTransport.map_finish_reason  s*      "	
 xx
F++r   r%   )__name__
__module____qualname____doc__r@   r   r   __annotations__propertyrE   r   r   rN   r   rX   r[   r   r   r   rU   r   r'   r   r   rC   r   r   r?   r?   ?   sX    (,x}+!# ! !
4S> 
c 

d38n)= 
C 
'4S#X#7 'C ' 15	pp tCH~&p T#s(^,-	p 
c3hpd	.
3 .
=O .
`# $ < #$) 	
 " 
B,C ,C ,r   r?   )register_transportrB   )r   r   r8   typingr   r   r   r   agent.transports.baser   agent.transports.typesr   r	   r   r   r=   r?   agent.transportsr   rC   r   r   <module>r      s      , , 3 ?S Xc] S $tCH~:N1O T\]`Ta BZ,- Z,| 0 $&; <r   