
    *"hj`/             
         U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlmZ ddlmZmZ ddlmZmZ ddlZddlmZmZmZmZ ddlmZmZ d	d
lmZ d	dl m!Z! d	dl"m#Z$ d	dl"m%Z& d	dl'm(Z( d	dl)m*Z*  ejV                  e,      Z-dZ.dZ/dZ0dZ1dZ2h dZ3dZ4dZ5dxdZ6dydZ7 e
jp                  de
jr                        Z: e
jp                  d      Z; e
jp                  de
jr                        Z< e
jp                  de
jr                        Z=dZ>dZ?dZ@d ZAd ZBd!d"hZCh d#ZDh d$ZEd%hZFd&hZGh d'ZHd(hZIh d)ZJ e
jp                  d*e
j                        ZL e
jp                  d+      ZMdzd,ZNd{d-ZOd|d.ZPd!d/	 	 	 	 	 	 	 d}d0ZQ	 	 	 	 	 	 d~d1ZRdd2ZSdd3ZTdd4ZUdd5ZVdd6ZWdd7ZXdd8ZYdd9ZZdd:Z[dd;Z\dd<Z]dd=Z^	 	 	 	 	 	 dd>Z_	 d	 	 	 	 	 	 	 dd?Z`dd@ZaddAZb	 	 	 	 	 	 ddBZcddCZdddDZeddEZf e
jp                  dFe
j                        dGf e
jp                  dHe
j                        dIf e
jp                  dJe
j                        dKf e
jp                  dLe
j                        dMf e
jp                  dNe
j                        dOf e
jp                  dPe
j                        dQf e
jp                  dRe
j                        dSfgZhdTeidU<   ddVZjddWZkddXZl	 	 	 	 	 	 ddYZmddZZndd[Zo	 	 	 	 	 	 dd\Zp	 	 	 	 	 	 dd]Zq	 d	 	 	 	 	 	 	 dd^Zrdd_Zsdd`ZtddaZudbZvddcZwdddZxddeZyddfZzddgZ{ddhZ|ddiZ}ddjZ~ddkZddlZddmZddnZ	 	 	 	 	 	 ddoZddpZddqZddrZddsZddtZ	 d	 	 	 	 	 	 	 dduZ G dv dw      Zy)z
FastAPI proxy server for SkillClaw.

Intercepts LLM requests from Claw agents, injects skills into system
prompts, forwards to a real LLM API, and optionally collects PRM scores.
    )annotationsN)asynccontextmanager)datetimetimezone)AnyOptional)FastAPIHeaderHTTPExceptionRequest)JSONResponseStreamingResponse   )SkillClawConfig)	PRMScorer)anthropic_messages)openai_responses)SkillManager)run_llmz[32mz[33mz[31mz[36mz[0m>   	turn_type
session_idsession_done_skillclaw_protocolr   responses_compatc                "   t        | t              r| S t        | t              rZ| D cg c]:  }t        |t              s|j	                  d      dk(  s)|j	                  dd      < }}|rdj                  |      S dS | t        |       S dS c c}w )Ntypetext  )
isinstancestrlistdictgetjoin)contentitempartss      //root/.hermes/SkillClaw/skillclaw/api_server.py_flatten_message_contentr*   <   s    '3'4 29s$Zd=SX\X`X`agXhlrXr&"%ss"'sxx/R/".3w<6B6 ts   BBBc                Z   g }g }t        |       D ]  \  }}t        |t              s|j                  d      }|dk(  r7|j                  d      }t        |t              sO|sR|j                  |       d|dk(  sj|j                  d      }|j                  di       }t        |t              s	 t        j                  |d      }|j                  d	      xs d
| }	|j                  |	d|xs d|dd        dj                  |      j                         |fS # t        $ r d}Y dw xY w)zLExtract plain text and OpenAI-style tool_calls from assistant content parts.r   r   toolCallname	argumentsFensure_ascii{}idcall_functionunknown_toolr-   r.   r2   r   r4   r   )	enumerater    r#   r$   r!   appendjsondumps	Exceptionr%   strip)
r&   
text_parts
tool_callsir'   	item_typer   r-   argstc_ids
             r)   "_normalize_assistant_content_partsrD   E   s,   JJW% 4$%HHV$	88F#D$$!!$'*$88F#D88K,DdC( ::d?D HHTN1aSkE& $ 6%)!	#6 HHZ &&(*55 !  D s   .DD*)D*z<think>.*?</think>z^call_(?:kimi|xml)_\d+$zu<\|tool_call_begin\|>\s*([a-zA-Z0-9_.-]+)(?::\d+)?\s*<\|tool_call_argument_begin\|>\s*(\{.*?\})\s*<\|tool_call_end\|>z"<tool_call>\s*(.*?)\s*</tool_call>i        mainside>   1onyestrue>   readreadfile	file_read	read_file
skill_viewskill>   mveditmovepatchwriter9   renamereplace	edit_file	writefile
file_write
write_fileappend_fileapply_patchcreate_filereplace_in_fileskill_manage>   bashexecshellterminalz)^\*\*\* (?:Add|Update|Delete) File: (.+)$zy([~./A-Za-z0-9_\-][^\n\"'`]*?(?:SKILL\.md|references/[^\s\"'`]+|scripts/[^\s\"'`]+|assets/[^\s\"'`]+|history/[^\s\"'`]+))c                &   t               }| xs g D ]}  }t        |t              r8|j                  d      xs$ |j                  d      xs |j                  d      }n|}t	        |xs d      j                         }|sm|j                  |        |S )N
skill_namer-   rR   r   )setr    r#   r$   r!   r=   add)itemsnamesr'   rawr-   s        r)   _extract_skill_namesrn      s    eE dD!((<(QDHHV,<Q@QCC39"~##%IIdO L    c                    t               }| xs g D ]<  }t        |t              s|j                  t	        |j                  d                   > |S )Nmodified_skills)ri   r    r#   updatern   r$   )turnsrl   turns      r)   _extract_modified_skill_namesru      sK    eE LdD!LL-dhh7H.IJKL Lro   c                     t        t        j                  j                  dd            j	                         } 	 t        |       }|dkD  r|S dS # t        $ r Y yw xY w)NSKILLCLAW_LLM_REQUEST_TIMEOUT_S120g      ^@r   )r!   osenvironr$   r=   float
ValueError)rm   timeouts     r)   _llm_request_timeout_secondsr~      sZ    
bjjnn>F
G
M
M
OC* k7,u,  s   A 	AAdefaultc                   |t         vrd}| | n|}t        |xs d      j                         j                         }|s|S |t         v r|S t        j                  d||       |S )zResolve request turn_type safely.

    Defaults to ``main`` to avoid silently dropping record/PRM paths when
    clients include a session id but forget to provide turn_type.
    rG   r   z1[SessionDetect] invalid turn_type=%r; fallback=%s)_VALID_TURN_TYPESr!   r=   lowerloggerwarning)header_turn_typebody_turn_typer   	candidaterm   s        r)   _resolve_turn_typer      sk     ''$4$@ nI
io2

$
$
&
,
,
.C


NNFWUNro   c                    | | n|}t        |t              r|S |yt        |      j                         j	                         t
        v S )z)Resolve session_done from header or body.F)r    boolr!   r=   r   _TRUE_STRINGS)header_session_donebody_session_doner   s      r)   _resolve_session_doner      sL    
 (;'F#L]I)T"y>!'')]::ro   c                |   t        | xs d      j                         }|rt        |      dkD  ry	 t        j                  |      }t        |t              rt        |j                               dhz
  ry|j                  d      }t        |t               xr t        |j                               S # t
        $ r Y yw xY w)zGReturn True for Claude Code's internal generate_session_title response.r     Ftitle)r!   r=   lenr:   loadsr<   r    r#   ri   keysr$   r   )r&   r   parsedr   s       r)   "_looks_like_session_title_responser      s    w}"##%D3t9s?D! fd#s6;;='9WI'EJJwEeS!9d5;;=&99  s   B/ /	B;:B;c                4    |ry| t         k(  rt        |      ryy)zAClassify recorded raw/main turns for user-turn cadence decisions.tool_usesession_titlefinal)_PROTOCOL_ANTHROPIC_MESSAGESr   )protocolr&   r?   s      r)   _classify_raw_turn_kindr      s     //4VW^4_ro   c                    | dk(  S )zEOnly final assistant responses advance the user-visible turn counter.r    )raw_turn_kinds    r)   _is_user_turn_boundaryr      s    G##ro   c                   | xs dj                         }|j                  d      r|j                  dd      d   }t        j	                  |      s|xs dS 	 t        j                  |xs d      }t        |t              rbt        |j                  d      t              r|j                  d      ryt        |j                  d	      t              r|j                  d	      ry
y# t        $ r i }Y w xY w)z
    Normalize tool names from model output.
    Fixes common drift where a call handle (e.g. call_kimi_0) is emitted as
    function name instead of the actual tool name.
    r   
functions..r   r5   r1   commandrd   	sessionIdprocess)r=   
startswithsplit_TOOL_HANDLE_RE	fullmatchr:   r   r<   r    r#   r$   r!   )raw_nameargs_rawr-   args_objs       r)   _normalize_tool_namer      s     N!!#D|$zz#q!!$$$T*%~%::h.$/ (D!hll9-s3Y8Ohll;/5(,,{:S  s   C# #C10C1c                    t        | xs d      j                         }|j                  d      r|j                  dd      d   S |S )z3Strip transport-specific prefixes from a tool name.r   r   r   r   )r!   r=   r   r   )r   r-   s     r)   _normalize_tool_call_namer     s@    x~2$$&D|$zz#q!!$$Kro   c                    t               }g }| D ]H  }t        |xs d      j                         }|r||v r'|j                  |       |j	                  |       J |S )Nr   )ri   r!   r=   rj   r9   )pathsseenoutpathcleans        r)   _deduplicate_pathsr     s]    UDC DJB%%'

5 Jro   c                    t        | xs d      j                         }|r|dv ryd|v xs* d|v xs$ |j                  d      xs |j                  d      S )Nr   >   ..r   F/\~zSKILL.md)r!   r=   r   endswith)valuer   s     r)   _looks_like_pathr      sV    u{!!#D4;&$;[$$,[$//#*>[$--PZB[[ro   c                    t        t        j                  t        | xs d            D cg c]A  }|j	                  d      j                         s#|j	                  d      j                         C c}      S c c}w Nr   r   )r   _PATCH_PATH_REfinditerr!   groupr=   )raw_textmatchs     r)   _extract_skill_paths_from_patchr   '  s^    -;-D-DSUWEX-YtE]b]h]hij]k]q]q]sQ			t ts   $A7!A7c                    t        t        j                  t        | xs d            D cg c]@  }|j	                  d      j                         r|j	                  d      j                         B c}      S c c}w r   )r   _SHELL_SKILL_PATH_REr   r!   r   r=   )r   r   s     r)   _extract_skill_paths_from_shellr   -  se     .66s7=b7IJ	
{{1~##% KKN  "	
 	
s   AA6c                   g }dD ]O  }| j                  |      }t        |t              s%t        |      s1|j	                  |j                                Q | j                  d      }t        |t              rC|D ]>  }t        |t              st        |      s |j	                  |j                                @ t        |      S )N)r   file	file_pathtargetdestinationdesttosourcesrcold_pathnew_pathr   )r$   r    r!   r   r9   r=   r"   r   )rB   r   keyr   	raw_pathsr'   s         r)   #_extract_skill_paths_from_args_dictr   7  s    E ( eS!&6u&=LL'(" !I)T" 	+D$$)9$)?TZZ\*	+ e$$ro   c                `   t        | t              r| j                  di       ni }t        |j                  d      xs d      }|j                  dd      }t        |t              s	 t        j                  |d      }g }d }	 t        j                  |      }t        |t              r{|j                  t        |             |j                         t        v rKt	        |j                  d      xs |j                  d	      xs d      }|j                  t        |             |j                         d
v r|j                  t        |             n0|j                         t        v r|j                  t        |             |t        |      fS # t        $ r d}Y w xY w# t        $ r d }Y w xY w)Nr4   r-   r   r.   r1   Fr/   r   cmd>   rV   r_   )r    r#   r$   r   r!   r:   r;   r<   r   extendr   r   _SHELL_TOOL_NAMESr   r   r   )	tool_callfunc	tool_namer   r   r   r   s          r)   #_extract_skill_paths_from_tool_callr   R  si   ,6y$,G9==R(RD)$((6*:*@bAIxxT*Hh$	zz(?H EH::h' (D!8BC?? 11(,,y1NX\\%5HNBOGLL8AB444X>?		/	/4X>?(///+  	H	  s$   &F F FFF-,F-c                   t        | t              r| j                  di       ni }t        |j                  d      xs d      }|j                  dd      }t        |t              s	 t        j                  |d      }	 t        j                  |      }t        |t              s|ddfS d}dD ]F  }|j                  |      }t        |t              s%|j                         s6|j                         } n d	D ]I  }|j                  |      }t        |t              s%|j                         s6||j                         |fc S  |d|fS # t        $ r d}Y w xY w# t        $ r i }Y w xY w)
zGExtract Hermes-native skill name + relative file path from skill calls.r4   r-   r   r.   r1   Fr/   )r   r   )rh   r-   rR   )
r    r#   r$   r   r!   r:   r;   r<   r   r=   )r   r   r   r   r   rel_pathr   r   s           r)   )_extract_hermes_skill_name_from_tool_callr   q  sP   ,6y$,G9==R(RD)$((6*:*@bAIxxT*Hh$	zz(?H::h' h%"b  H$ S!eS!ekkm{{}H	
 / 6S!eS!ekkmekkmX556 b(""+  	H	
  s$   &E >E EEE'&E'c                \   t         j                  j                  t        | xs d      j	                               }|rt         j                  j                  |      nd}|j                  |      xs? |j                  |      xs, |j                  t        | xs d      j	                               }|r[t        |j                  dd      xs d      t        |j                  dd      xs d      t        | xs d      j	                         dS ddt        | xs d      j	                         dS )Nr   skill_idrh   r   rh   r   )ry   r   
expanduserr!   r=   realpathr$   )r   skill_path_mapexpanded	real_path
skill_infos        r)   _resolve_skill_referencer     s    ww!!#djb/"7"7"9:H.6  *BI9%t););H)EtI[I[\_`d`jhj\k\q\q\sIt  JNN:r:@bAjnn\2>D"E
O))+
 	
 DJB%%' ro   c                2   t        | xs d      j                         }|sddddS t        |xs d      j                         j                  dd      j                  d      }|rd| }|j	                         D ]  \  }}t        |j                  dd      xs d      j                         |k7  r7t        |xs d      j                  dd      }|j                  |      s||k(  smt        |j                  dd      xs d      |t        |xs d      dc S  |j	                         D ]g  \  }}t        |j                  dd      xs d      j                         |k(  s7t        |j                  dd      xs d      |t        |xs d      dc S  d|ddS )Nr   r   r   r   z./rh   r   )r!   r=   rY   lstriprk   r$   r   )	rh   r   r   
clean_namenormalized_relsuffixr   r   r   s	            r)    _resolve_skill_reference_by_namer     s   
 Z%2&,,.Jb"==R(..088sCJJ4PN^$% . 4 4 6 		D*:>>,39r:@@BjPDJB//c:I!!&)Y.-H #JNN:r$B$Hb I",
O 		 +002 jz~~lB/526<<>*L
z2 > D"E(DJB  *bAAro   c                   | sdg fS g }t        t        j                  |             D ]  \  }}|j                  d      xs dj	                         }|j                  d      xs dj	                         }t        ||      }	 t        j                  |      }t        j                  |d      }|j                  d| d|xs d	|d
d        t        t        j                  |       t        |            D ]  \  }}|j                  d      xs dj	                         }		 t        j                  |	      }
|
j                  d      xs8 |
j                  d      xs% |
j                  di       j                  d      xs d	}|
j                  d      xs% |
j                  di       j                  d      xs i }t        |t              s	 t        j                  |d      }t        t        |      |      }|j                  d| d||d
d        | }t         j#                  d|      }|j%                  dd      }t'        j"                  dd|t&        j(                        }t'        j"                  dd|t&        j(                        }t        j#                  d|      }|j	                         }||fS # t        $ r
 |r|nd}Y w xY w# t        $ r Y w xY w# t        $ r d}Y 	w xY w)z
    Parse tool-call tags embedded in assistant text into OpenAI-style tool_calls.
    Supports Kimi markers and Qwen <tool_call> wrappers.
    r   r      r1   Fr/   
call_kimi_r4   r5   r6   r7   )startr-   r   r.   	call_xml_z</think>z+<\|tool_call_begin\|>.*?<\|tool_call_end\|>)flagsz=<\|tool_calls_section_begin\|>.*?<\|tool_calls_section_end\|>)r8   _KIMI_TOOL_CALL_REr   r   r=   r   r:   r   r;   r<   r9   _QWEN_TOOL_CALL_REr   r$   r    r!   	_THINK_REsubrY   reDOTALL)r   r?   r@   mr   r   r   r   args_strpayload_rawpayloadr-   rB   r   s                 r)   _extract_tool_calls_from_textr    s   
 2vJ,55d;< 
1GGAJ$"++-GGAJ&$--/(8<		6zz(+Hzz(?H 	"1#&"%.%@.xX	

" ,55d;3z?S 
1wwqz'R..0	jj-G KKx7;;{#;xw{{:WY?Z?^?^_e?fxjx 	 {{;']7;;z2+F+J+J;+W][]$$zz$U; $CIt4!!o"%)=	
!
0 EMM"e$EMM*b)EFFA2uTVT]T]^EFFSUWY^fhfofopE""2u-EKKME*U  	6#+xH	6  		  s6    ,J6!K
K6K	K		KKK+*K+c                   | j                  d      }t        |t              r|ry| j                  d      }t        |t              rt        |      \  }}t	        |      S t        |t
              r|rt        |      \  }}t	        |      S y)Nr?   Tr&   F)r$   r    r"   rD   r   r!   r  )messageraw_tool_callsraw_content_part_tool_callstext_tool_callss         r)   !_assistant_message_has_tool_callsr    sz    [[.N.$'N++i(K+t$?L?O$$+s#:;G?O$$ro   c                   t        |       D cg c]6  \  }}t        |t              r!|j                  d      dk(  rt	        |      r|8 }}}|D cg c]'  }t        |t              s|j                  d      s&|) }}|r|syd}t        t        |      t        |            D ][  \  }}| |   }|j                  d      rt        |j                  d      xs d      j                         }	|	sLi |d|	i| |<   |dz  }] |S c c}}w c c}w )zBBackfill reasoning_content for prior assistant tool-call messages.role	assistantr?   r   reasoning_contentr   r   )	r8   r    r#   r$   r  zipreversedr!   r=   )
messagesprior_turnsidxmsgassistant_tool_indicesrt   prior_tool_turnsrestoredmsg_idx	reasonings
             r)   "_restore_missing_reasoning_contentr    s#    "(+Cc4 SWWV_%CHijmHn 	 
 *5j
48NSWS[S[\hSijj!)9HX&<=xHX?YZ w77&'!45;<BBD	CsC$7CA O'
 ks   ;DD
'D
9D
c                   g }t               }| D ]  }t        |t              st        |j	                  d      xs d      j                         }|j	                  d      xs i }t        |j	                  d      xs d      }t        |j	                  d      xs d      }|rd| nd| d| }||v r|j                  |       |j                  |        |S )	zDeduplicate tool calls while preserving order.

    Priority key is tool-call id. When id is missing, fallback to
    (function.name, function.arguments).
    r2   r   r4   r-   r.   zid:zfn:z|args:)ri   r    r#   r!   r$   r=   rj   r9   )	r?   dedupedr   tcrC   r   fn_namefn_argsr   s	            r)   _deduplicate_tool_callsr$  .  s     GUD "d#BFF4L&B'--/vvj!'Rdhhv&,"-dhh{+1r2$E7mCywi*H$;r Nro   c                d   g }| D ]'  }t        |      }|j                  d      }|dk(  rd|d<   d}|dk(  rdt        |j                  d            d}|j                  d      xs |j                  d	      }|r||d	<   |j                  d
      xs |j                  d      }|r||d<   |j                  |       |j                  d      }|dk(  r+t	        |t
              rt        |      \  }	}
|	|d<   |
r&|
|d<   n t	        |t              s|t        |      |d<   |j                  |       * |S )zGNormalize OpenClaw-style messages into chat-template-compatible format.r  	developersystem
toolResulttoolr&   r  r&   
toolCallIdtool_call_idtoolNamer-   r  r?   )r#   r$   r*   r9   r    r"   rD   r!   )r  r   r  r  r  tool_msgrC   r   rm   r   r?   s              r)    _normalize_messages_for_templater/  E  s>   
C !IuuV}; AfID <3AEE)4DE(H EE,'@155+@E+0(j):QUU6]I#, JJx  eeI;:c4#8A#FD*AiL",,C%#/3C8AiL

1C!D Jro   c                    t        |       D ]H  }t        |t              s|j                  d      dk(  s)t	        |j                  d            }|sF|c S  y)zGReturn the most recent user message text from the current turn context.r  userr&   r   )r  r    r#   r$   r*   )r  r  r   s      r)   _extract_last_user_instructionr2  m  sO    ! c4 SWWV_%>+CGGI,>?D	
 ro   zGexited with code (?!0\b)\d+|exit code (?!0\b)\d+|exit status (?!0\b)\d+	exit_codez4Traceback \(most recent call last\)|\.py\", line \d+	tracebackz(Permission denied|EACCES|PermissionError
permissionz/No such file|FileNotFoundError|ENOENT|not found	not_foundz5command not found|not recognized as|is not recognizedcommand_not_foundz#timed?\s*out|TimeoutError|ETIMEDOUTr}   z(?:^|\W)(?:Error|Exception):\sgeneric_errorzlist[tuple[re.Pattern, str]]_ERROR_PATTERNSc                N    t         D ]  \  }}|j                  |       sd|fc S  y)zJReturn (has_error, error_type) by matching content against known patterns.T)FN)r9  search)r&   pattern
error_types      r)   _classify_tool_errorr>    s2    . $>>'"##$ ro   c           	        g }t        |       D ]  }t        |t              s|j                  dd      }|dv rt	        |j                  d            }|j                  d      xs( |j                  d      xs |j                  d      xs d}t        |      \  }}|j                  ||j                  d	      xs |j                  d
      xs d|dt         ||d       |dk(  r n |j                          |S )a.  Extract tool results from the most recent tool-call round in messages.

    Scans backwards from the end of *messages*, collecting all consecutive
    tool / toolResult messages that appear after the last assistant message.
    Returns a list of summary dicts suitable for skill feedback tracking.
    r  r   r(  r)  r&   r-  r-   r   unknownr+  r,  N)r   r,  r&   	has_errorr=  r1  )	r  r    r#   r$   r*   r>  r9   _TOOL_RESULT_CONTENT_MAX_CHARSreverse)r  resultsr  r  r&   r   rB  r=  s           r)   _extract_recent_tool_resultsrF    s     G! #t$wwvr")).swwy/ABG
+cswwvc#''+BVcZcI$8$A!IzNN!*%(WW\%:%[cggn>U%[Y[&'F(FG!*", V^)* OONro   c           	     n   g }t        |       D ]k  }t        |t              s|j                  dd      }|dv r;	 |j	                  t        j                  t        j                  |d                   e|dk(  rk n |j                          |S # t        $ r |j	                  t        |             Y w xY w)a	  Extract raw tool result messages from the most recent tool round.

    This preserves the original payload shape so cloud sessions can retain a
    complete tool-execution snapshot for future analysis. No truncation or
    error classification is applied here.
    r  r   r@  Fr/   r1  )
r  r    r#   r$   r9   r:   r   r;   r<   rD  )r  rE  r  r  s       r)   $_extract_recent_tool_result_messagesrH    s     G! #t$wwvr"))*tzz$**Su*MNO V^ OON  *tCy)*s   9B#B43B4c                  ddl }i }d}|}t         |j                                }i }| D ]#  }t        |t              st	        |j                  d      xs |      }t	        |j                  d      xs |      }|j                  d      }	t        |	t              r|	}t        |j                  d      t              rt        |d         }|j                  dg       xs g D ]i  }
t        |
t              st        |
j                  d	d            }|j                  |d
g i dd      }|
j                  d      xs i }t        |j                  d      t              r|d   |d<   |j                  d      }t        |t              r|d   j                  |       n_t        |t              rO|D ]J  }t        |t              st        |j                  d      t              s4|d   j                  |d          L |j                  dg       xs g D ]  }t        |t              st        |j                  d	d            }|d   j                  ||j                  d      xs d| |j                  dd      dddd      }|j                  d      r|d   |d<   |j                  d      r|d   |d<   |j                  d      xs i }t        |j                  d      t              r|d   dxx   |d   z  cc<   t        |j                  d      t              s|d   dxx   |d   z  cc<   ! |
j                  d      }|e||d<   l & g }t        |      D ]g  }||   }|d   dj                  |d         d}|d   r%t        |d         D cg c]
  }|d   |    c}|d<   |j                  |||d   xs dd       i |xs d| d|||xs
 dd
ddddg|dS c c}w ) zBCollapse OpenAI-style SSE chat chunks into a single response dict.r   Nr   r2   modelcreatedusagechoicesindexr  )r  content_partsr?   finish_reasondeltar  r&   rO  r   r?   r3   r   r4   r6   r7   r-   r.   rP  r*  stoprN  r  rP  zchatcmpl-stream-chat.completion)r2   objectrK  rJ  rM  rL  )timeintr    r#   r!   r$   
setdefaultr9   r"   sortedr%   )eventsfallback_modelrV  buildersresponse_idresponse_modelresponse_createdrL  eventrK  choicerN  entryrQ  r&   r'   r!  tc_index
tool_entryfnrP  rM  r  r@   s                           r)   #_assemble_streaming_chat_completionrf    s	    *,HK#N9499;'E <7%&%))D/8[9UYYw/A>B))I&gs#&eii($/w(Eii	2.4" 1	7Ffd+

7A./E'''%'"$%)	E JJw'-2E%))F+S1 %ffii	*G'3'o&--g6GT*# DD!$-*TXXf=Ms2So.55d6lCD iib17R K!"d+rvvgq12"<0;; ffTl@hZ.@ "vz :-/b$A
 66$<')$xJt$66&>)+FJv&VVJ'-2bffVnc2z*62bj@2bff[137z*;72k?J7)K, #JJ7M()6o&c1	7<7| %'G! 
&Mwwu_56#
 EKER^L_E`$aU<%8%;$aGL!"!&!7!A6	

" B/0@/AB## 
 $/B?!'
   %bs   O#c                N  K   g }| j                         2 3 d{   }|s|j                  d      s|dd j                         }|r|dk(  r9	 t        j                  |      }t        |t              s`|j                  |       r7 m# t        j
                  $ r Y w xY w6 |S w)zARead SSE `data:` lines from a streaming chat completion response.Ndata:   [DONE])	aiter_linesr   r=   r:   r   JSONDecodeErrorr    r#   r9   )responserZ  liner  r`  s        r)   _collect_sse_chat_eventsro  8  s     #%F**, ! !dw'qr(.."'X-	JJw'E eT"MM% ! ## 		 - MsI   B%B"BB"1B%B	$B%5B%B"	BB%BB%c                F   g }| D ]t  }|j                  di       }t        |j                  dd            }|j                  dd      }t        |t              s	 t	        j
                  |d      }	 t	        j                  |      }t        |      \  }}|t        |j                  d      xs d	      |d
t         dd}	|j                         t        v r?t        |j                  d      xs |j                  d      xs d	      }
|
r|
d
t         |	d<   t        |j                  d      xs( |j                  d      xs |j                  d      xs d	      }|r||	d<   n
|r|d   |	d<   |j                  |	       w |S # t        $ r d}Y w xY w# t        $ r i }Y w xY w)a;  Build tool summary dicts from the model's tool_calls.

    Extracts the tool name and key arguments (``command`` for shell-like
    tools, ``path`` for file-based tools) into a compact format for the
    turn record.  ``has_error`` defaults to ``False`` and is merged later
    when actual tool results arrive.
    r4   r-   rA  r.   r1   Fr/   r2   r   N)r   r,  r.   rB  r   r   r   r   r   r   )r$   r   r    r!   r:   r;   r<   r   r   _TOOL_ARGS_MAX_CHARSr   r   r9   )r?   	summariesr!  r   r-   r   rB   r  skill_pathssummaryr   r   s               r)   _build_tool_summariesru  L  s    I !"vvj"%(&))DE88K.(C( ::hUC	::h'D =R@; t 23!"7#78	#
 ::<,,dhhy)BTXXe_BCC%()>*>%?	"488F#Vtxx'7V488K;PVTVW"GFO)!nGFO!C!"D 7      	D	s$   E?1F?FFF F c                r   g }t               }| D ]  }t        |      \  }}|j                         }|t        v rit	        |      \  }}	}
t        |	||
      }|j                  d      xs |j                  d      }|r&||vr"|j                  |       |j                  |       |t        v rjt	        |      \  }}	}
t        |	||
      }|j                  d      xs |j                  d      }|r&||vr"|j                  |       |j                  |       |t        vr|D ]  }t        ||      }|j                  d      s|j                  d      s2|j                  d      xs$ |j                  d      xs |j                  d      }|r||v rp|j                  |       |j                  |         |S )zIdentify which skill bundle files were read from the model's tool_calls.

    Returns a list of ``{"skill_id": ..., "skill_name": ...}`` dicts for
    each ``read`` tool call whose ``path`` argument points inside a skill.
    r   rh   r   )ri   r   r   _HERMES_SKILL_READ_TOOL_NAMESr   r   r$   r9   rj   _CLAUDE_CODE_SKILL_TOOL_NAMES_READ_TOOL_NAMESr   )r?   r   read_skillsseen_idsr!  r   rs  
normalizedr  rh   r   	skill_ref
dedupe_keyr   s                 r)   $_extract_read_skills_from_tool_callsr  z  s    !KH %!DR!H	;__&
66&OPR&S#Az88^U]^I"z2QimmL6QJj8""9-Z(66&OPR&S#Az88^U]^I"z2QimmL6QJj8""9-Z(-- 	%D0~FI==,Y]]<5P"z2jimmF6Kjy}}]iOjJx!7y)LL$	%+%> ro   c                   g }t               }| D ]X  }t        |      \  }}|j                         }|t        v r+|t        v rot        |      \  }}	}
t        |	||
      }|j                  d      xs |j                  d      }|r,||vr(|j                  i |d|i       |j                  |       |t        vr	|t        vr|D ]  }t        ||      }|j                  d      s|j                  d      s2|j                  d      xs$ |j                  d      xs |j                  d      }|r||v rp|j                  i |d|t        v rdn|i       |j                  |        [ |S )zCIdentify skill bundle files the model attempted to write or update.r   rh   actionr   re   )ri   r   r   ry  _HERMES_SKILL_WRITE_TOOL_NAMESr   r   r$   r9   rj   _SKILL_WRITE_TOOL_NAMESr   r   )r?   r   rq   r{  r!  r   rs  r|  r  rh   r   r}  r~  r   s                 r)   (_extract_modified_skills_from_tool_callsr    s   
 #%OH %!DR!H	;__&
))77&OPR&S#Az88^U]^I"z2QimmL6QJj8&&'J)'JXz'JKZ(44K\9\ 	%D0~FI==,Y]]<5P"z2jimmF6Kjy}}]iOjJx!7""7H)Hgj LL$	%%: ro   c           	        | j                  dg       }g }|Zg }|D ]M  }t        |t              s	 |j                  t	        j
                  t	        j                  |d                   O || d<   n| j                  dg        t        |      D ]  \  }}|j                  dd      |j                  dd	      t        |j                  d
d            d}	|j                  d      r|d   |	d<   |j                  dd	      }
|
rt        |
      dt         |	d<   |j                  |	       |t        |      k  rt        |j                  d
d            ||   d
<   |j                  d||   j                  dd            ||   d<   |j                  d      r|d   ||   d<   |j                  d      r|d   ||   d<   n||   j                  dd       |j                  dd	      }
|
rt        |
      dt         ||   d<   o||   j                  dd       |j                  dd      |j                  dd	      t        |j                  d
d            d}|j                  d      r|d   |d<   |j                  dd	      }
|
rt        |
      dt         |d<   |j                  |        || d<   |D cg c]u  }|j                  d
      rbd|j                  dd      i|j                  d      rd|d   ini |j                  d      rd|d   ini |j                  d      rd|d   ini w c}| d<   y# t        $ r |j                  t        |             Y >w xY wc c}w )ai  Merge error information from tool results into the turn record.

    Matches tool results to the ``tool_results`` summaries built from tool
    calls (by position).  Updates ``has_error``, ``error_type``, and
    ``content`` on matching entries, then rebuilds ``tool_errors``.
    ``raw_tool_results`` preserves the original tool payloads for cloud upload.
    tool_resultsNFr/   tool_results_rawr   rA  r,  r   rB  )r   r,  rB  r=  r&   tool_observationstool_errors)r$   r    r#   r9   r:   r   r;   r<   rX  r8   r   r!   rC  r   pop)turn_recordr  raw_tool_resultsrr  observationsraw_snapshotr'   r@   resultobsr&   rb  ss                r)   _merge_tool_error_infor    su    3I!L##%$ 	0DdD)0##DJJtzz$U/S$TU		0 +7&'126|, &$	6K;"JJ~r:fjje<=

 ::l# &| 4C**Y+ \*I+IJC	NC s9~(,VZZU-K(LIaL%(.

;	!@P@PQ\^g@h(iIaL%zz.)/5n/E	!^,zz,'-3L-A	!\*!  t4jjB/G*-g,7V8V*W	!Y'!  D1 $ZZY? &

>2 >!&**[%"@A%E
 zz,'&,\&:l#jjB/G#&w<0O1O#Pi U#M&$P (4K#$ 	" 55 {I6	
67eeN6K. 12QS	
 34%%2Eao.2	
 -.EE),<	1Y<("		
	"K_  0##DJ/0^	"s   9L32A:M3#MMc                h   d}g }| D ]  }t        |t              s|j                  |       %|j                  d      dk7  r|j                  |       Kt	        |j                  d            }|j                         }d|v r|j                  i |ddi       |dz  }|j                  |        ||fS )a  Rewrite OpenClaw /new bootstrap user prompt to a safer variant.

    Some upstream providers over-trigger policy filters on the stock bootstrap
    text ("A new session was started via /new or /reset ..."). This keeps
    behavior while avoiding brittle phrasing.
    r   r  r1  r&   z,a new session was started via /new or /resetzfA new chat session just started. Greet the user briefly in 1-3 sentences and ask what they want to do.r   )r    r#   r9   r$   r*   r   )r  	rewrittenr   r  r   lowereds         r)   %_rewrite_new_session_bootstrap_promptr    s     IC #t$JJsO776?f$JJsO'	(:;**,9WDJJ` NI

3+, 	>ro   c                ,    t        j                  |       S N)anthropic_protocolto_openai_body)bodys    r)   _anthropic_to_openai_bodyr  F  s    ,,T22ro   c                   t               }| j                  d      }t        |t              s|S |D ]S  }t        |t              st        |j                  d      xs d      j                         }|sC|j                  |       U |S )Ntoolsr-   r   )ri   r$   r    r"   r#   r!   r=   rj   )r  
tool_namesr  r'   r-   s        r)   _anthropic_request_tool_namesr  J  s{    5JHHWEeT" !$%488F#)r*002NN4 ! ro   i@  c                    | j                  d      rd| vry | j                  dd      \  }}d|vry 	 t        j                  |d      S # t        $ r Y y w xY w)Nrh  ,r   z;base64F)validate)r   r   base64	b64decoder<   )urlheaderdatas      r)   _data_url_bytesr  [  s`    >>'"cn99S!$LFDu55 s   A	 		AAc                   | j                  d      r:t        |       dk\  r,t        j                  d| dd       \  }}|dkD  r	|dkD  r||fS d S | j                  d      s| j                  d      r;t        |       dk\  r,t        j                  d	| d
d       \  }}|dkD  r	|dkD  r||fS d S y | j                  d      rt        |       dk\  r| dd dk(  r| dd dk(  rHt        j                  | dd d      dz   }t        j                  | dd d      dz   }|dkD  r	|dkD  r||fS d S | dd dk(  r\t        |       dk\  rNt        j                  d| dd       d   dz  }t        j                  d| dd       d   dz  }|dkD  r	|dkD  r||fS d S | j                  d      rd}|dz   t        |       k  r| |   dk7  r|dz  }| |dz      }|dz  }|dv r1|dz   t        |       kD  ry t        j                  d| ||dz          d   }|dk  s||z   t        |       kD  ry |dv r8|d k\  r2t        j                  d!| |d"z   |d z          \  }}|dkD  r	|dkD  r||fS d S y ||z  }|dz   t        |       k  ry )#Ns   PNG

   z>II   r   s   GIF87as   GIF89a
   z<HH   s   RIFF         s   WEBPs   VP8X   littler   s   VP8 z<H      i?  s   r   	      >         z>H>                                             z>HH   )r   r   structunpackrW  
from_bytes)r  widthheightrN  markersegment_lengths         r)   _image_dimensions_from_bytesr  g  s   +,TbeT"R[9v"'!)
vDDy!T__Y%?t9?"MM%a<ME6&+aiFQJE6?HDHwCIOQr
g8M2;'!NN42;9A=E^^DBK:Q>F&+aiFQJE6?HDH2;'!c$i2oMM$R4Q7&@E]]4b5a86AF&+aiFQJE6?HDH{#ai#d)#E{d"
%!)_FQJE%qy3t9$#]]4eeai1HI!LN!U^%;c$i%G   "Q&$*MM%eai%RS)9T$UMFE.3aiFQJE6?PDP^#EA ai#d)#B ro   c                    t        |       }|t        S t        |      }|t        S |\  }}t        t        ||z  dz   dz        S )Ni  i  )r  _IMAGE_TOKEN_ESTIMATEr  max)r  r  
dimensionsr  r  s        r)   _image_token_estimate_from_urlr    sR    3D|$$-d3J$$ME6$uv~';&CDDro   c                   | j                  d      }t        |t              r|j                  d      n|}t        |t              r|st        | j                  d      t              r| j                  d      ni }|j                  d      dk(  rIt        |j                  d      xs d      }t        |j                  d      xs d	      }|rd
| d| nd	}nt        | j                  d      xs d	      }|st        S t        |      S )N	image_urlr  r   r   r  
media_typez	image/pngr  r   rh  z;base64,)r$   r    r#   r!   r  r  )r&   r  r  r   r  r  s         r)   _image_token_estimate_from_partr    s    K(I",Y"=)--
9Cc3s*4W[[5JD*QX&WY::f)VZZ5DEJvzz&)/R0D8<E*XdV4"Cgkk%(.B/C$$)#..ro   c                    t        | t              rt        d | D              S t        | t              rE| j	                  d      }|dv rt        |       nd}d| v r|t        | j	                  d            z  }|S y)Nc              3  2   K   | ]  }t        |        y wr  )_estimate_image_content_tokens).0r'   s     r)   	<genexpr>z1_estimate_image_content_tokens.<locals>.<genexpr>  s     LD1$7Ls   r   >   imager  input_imager   r&   )r    r"   sumr#   r$   r  r  )r&   rA   counts      r)   r  r    st    '4 LGLLL'4 KK'	<EIn<n/8tu3GKK	4JKKEro   c                j   t        | t              r| S t        | t              rg }| D ]  }t        |t              s||j	                  t        |             0|j                  d      }|dv r4t        |j                  d      t              r|j	                  |d          y|dv r|j	                  d       d|v s|j	                  t        |j                  d                    dj                  d |D              S t        | t              rt        j                  | d	d
      S | t        |       S dS )Nr   >   r   
input_textoutput_textr   >   r  r  z[image]r&   r   c              3  &   K   | ]	  }|s|  y wr  r   r  parts     r)   r  z'_token_estimate_text.<locals>.<genexpr>  s     7$7   FTr0   	sort_keysr   )
r    r!   r"   r#   r9   r$   _token_estimate_textr%   r:   r;   )r&   r(   r'   rA   s       r)   r  r    s   '3'4  	HDdD)#LLT+(IAAjQUQYQYZ`QacfFgT&\*44Y'd"1$((92EFG	H xx7777'4 zz'FF".3w<6B6ro   c           
        t        | j                  d      xs g       }| j                  d      }t        d |D              }g }|D ]  }t        |t              s|j                  |j                  dd       dt        |j                  d                    |j                  d      sd|j                  t        j                  |j                  d      d	d
              |r'|j                  t        j                  |d	d
             dj                  d |D              }t        dt        |      dz   dz  |z         S )a  Return a provider-agnostic rough input token estimate.

    SkillClaw proxies external agents and does not own the upstream model's
    exact tokenization. Keep this estimate local and dependency-free so
    daemon readiness never depends on model-specific tokenization.
    r  r  c              3  r   K   | ]/  }t        |t              st        |j                  d              1 yw)r&   N)r    r#   r  r$   )r  r  s     r)   r  z5_estimate_openai_body_input_tokens.<locals>.<genexpr>  s+     wcakloquav5cggi6HIws   77r  r   : r&   r?   FTr  
c              3  &   K   | ]	  }|s|  y wr  r   r  s     r)   r  z5_estimate_openai_body_input_tokens.<locals>.<genexpr>  s     9dDT9r  r   r     )r"   r$   r  r    r#   r9   r  r:   r;   r%   r  r   )openai_bodyr  r  image_tokensr>   r  r   s          r)   "_estimate_openai_body_input_tokensr    s    KOOJ/526HOOG$EwU]wwLJ e#t$SWWVR014HQZI[4\3]^_77< djj)>U^bcde $**U$OP999j99Dq3t9q=Q&566ro   c                j    	 t        j                  | ddd      S # t        $ r t        |       cY S w xY w)NFT)r  :)r0   r  
separators)r:   r;   r<   r!   )r  s    r)   _message_identityr    s6    zz'R\]] 7|s    22c                    d}|t        |       k  r>| |   }t        |t              r|j                  d      dk7  rn|dz  }|t        |       k  r>| d | | |d  fS )Nr   r  r'  r   )r   r    r#   r$   )r  rN  r  s      r)   _split_leading_system_messagesr    sj    E
#h-
uo#t$8(C
	 #h-

 FUXef---ro   c                ^    t        |       }d|v rt        |j                  d            |d<   |S )Nr&   )r#   r*   r$   )r  r|  s     r)   _canonical_overlap_messager    s1    gJJ 8	9R S
9ro   c                   g }g }| D ]b  }t        |j                  d            }|r|j                  |       |j                  d      }t        |t              sR|j                  |       d ddj                  |      d}|r||d<   |S )Nr&   r?   r  r   r*  )r*   r$   r9   r    r"   r   r%   )r  rO  r?   r  r&   msg_tool_callsmergeds          r)   _merge_assistant_overlap_runr    s    !MJ .*3779+=>  ).nd+n-. '2chh}>UVF)|Mro   c                \   g }d}|t        |       k  r| |   }t        |t              s'|j                  t	        d|i      |dz   f       |dz  }K|j                  d      dk(  r
|g}|dz   }t        |j                  d      t              xr t        |j                  d            }|t        |       k  r| |   }t        |t              r|j                  d      dk7  rnd|j                  |       |xs; t        |j                  d      t              xr t        |j                  d            }|dz  }|t        |       k  r|r)|j                  t	        t        |            |f       |}j|j                  t	        t        |            |dz   f       |dz  }|t        |       k  r|S )Nr   r   r   r  r  r?   )
r   r    r#   r9   r  r$   r"   r   r  r  )r  entriesrN  r  run
next_indexhas_tool_callsnext_msgs           r)   _messages_for_overlapr    s   %'GE
#h-
uo#t$NN-wn=uqyIJQJE776?k)%CJ'(=tDdcggVbNcIdNs8},#J/!(D1X\\&5I[5X

8$!/ "x||L94@eT(,,WcJdEe  a
 s8},  12Ns2S TV`ab")*DS*IJETUIVW
5 #h-
6 Nro   c                   | s|S |s| S t        |      \  }}|rt        |       \  }}n| }t        |      }t        |      }|D cg c]  \  }}|	 }	}}|D cg c]  \  }}|	 }
}}|
d t        |	       |	k(  r||z   S t        t        |	      t        |
            }d}t	        |dd      D ]  }|	| d  |
d | k(  s|} n |r||dz
     d   nd}||z   ||d  z   S c c}}w c c}}w )Nr   r   )r  r  r   minrange)previous_messagescurrent_messagescurrent_system_messagescurrent_body_messagesr  previous_body_messagesprevious_entriescurrent_entriesr   previous_keyscurrent_keysmax_overlapoverlapsizecurrent_drop_indexs                  r)   !_merge_previous_response_messagesr  >  s9      5STd5e22$BCT$U!!!2,-CD+,ABO'78VS!S8M8&56FCC6L6(c-()]:&)>>>c-(#l*;<KGk1b) $ L$$77G =D15a8"%;;>STfTg>hhh 96s   CC c                ,    t        j                  |       S r  )responses_protocolnormalize_content_to_textr&   s    r)   _normalize_responses_contentr  ^  s    77@@ro   c                ,    t        j                  |       S r  )r  tools_to_openai_tools)r  s    r)    _responses_tools_to_openai_toolsr"  b  s    33E::ro   c                    	 t        j                  | |      S # t        $ r}t        dt	        |            |d }~ww xY w)N  status_codedetail)r  r  r|   r   r!   )r  default_modelexcs      r)   _responses_to_openai_bodyr*  f  s@    G!00}EE GCH=3FGs    	=8=c                .    t        j                  | |      S r  )r  function_item_id)call_idrN  s     r)   _responses_function_item_idr.  m  s    ..w>>ro   c                .    t        j                  | |      S r  )r  from_openai_chat_payload)r  rJ  s     r)   !_openai_chat_to_responses_payloadr1  q  s    66wFFro   c                0    t        j                  | ||      S r  )r  from_openai_response)openai_resprJ  r  s      r)   _openai_to_anthropic_responser5  u  s    
 22;zRRro   c                     e Zd ZdZ	 	 	 	 dB	 	 	 	 	 dCdZdDdZdEdZdFdZdGdZdHdZ	dId	Z
dJd
ZdKdZdLdZdMdNdZdFdZdFdZdOdZdFdZdFdZdPdZdFdZdQdRdZdSdZ	 	 	 	 	 	 	 	 	 	 	 	 dTdZdUdZd Z	 dV	 	 	 	 	 	 	 	 	 dWdZ	 	 	 	 	 	 	 	 dXdZdYdZdYdZdZdZd[dZ 	 	 	 	 	 	 	 	 	 	 d\d Z!d]d!Z"d^d"Z#	 	 	 	 	 	 d_d#Z$d`d$Z%dad%Z&	 	 	 	 	 	 	 	 	 	 	 	 	 	 dbd&Z'd]d'Z(dd(	 	 	 	 	 	 	 	 	 	 	 dcd)Z)ddd*Z*d]d+Z+d]d,Z,	 	 	 	 	 	 ded-Z-dfd.Z.dfd/Z/dgd0Z0dhd1Z1dFd2Z2dMdid3Z3	 	 	 	 	 	 djd4Z4dkd5Z5dSd6Z6	 	 	 	 	 	 	 	 dld7Z7dmd8Z8dnd9Z9	 dM	 	 	 	 	 dod:Z:d; Z;d< Z<d= Z=d> Z>dpdqd?Z?d@ Z@eAdrdA       ZBy)sSkillClawAPIServeraw  Proxy between client agents and the upstream model with SkillClaw hooks.

    OpenClaw sends ``X-Session-Id`` and ``X-Turn-Type`` headers with every
    request. The proxy injects skills, records conversation artifacts when
    enabled, and can attach PRM scoring when configured. Side tasks
    (``turn_type != "main"``) are forwarded but do not generate the main
    conversation artifact path.

    Parameters
    ----------
    config:
        SkillClawConfig instance.
    skill_manager:
        Optional SkillManager for injecting skills into system prompts.
    prm_scorer:
        Optional PRMScorer for turn feedback.
    Nc           
        || _         || _        || _        || _        || _        t        j
                         | _        |j                  | _        |j                  | _
        t        j                  |j                  d       |j                  dk(  | _        |j                   d|j                    }t        j"                  j%                  |j                  d| d      | _        i | _        i | _        i | _        i | _        i | _        i | _        i | _        i | _        t9               | _        t9               | _        i | _        d | _         d | _!        tE        dtG        tI        |dtJ                          | _&        tE        d	tG        tI        |d
tN                          | _(        tE        d	tG        tI        |dtR                          | _*        tE        dtG        tI        |dd      xs d            | _+        i | _,        d}|| _-        d| _.        d| _/        |j`                  rt        j                  |j                  d       t        j"                  j%                  |j                  d      | _.        t        j"                  j%                  |j                  d      | _/        tc        | j\                  d      5  	 d d d        tc        | j^                  d      5  	 d d d        | je                         | _3        d | _4        d | _5        tm        jn                         | _8        tm        jn                         | _9        y # 1 sw Y   xY w# 1 sw Y   kxY w)NT)exist_okopenclawr  system_prompt_cache_.jsonr   session_idle_close_secondsr   session_sweep_interval_secondsshutdown_drain_timeout_secondsri  %sharing_skill_reload_interval_secondsr  i,  r   zconversations.jsonlzprm_scores.jsonlw):config_sampling_clientskill_manager
prm_scorer_last_request_trackerrV  _last_request_atserved_model_name_served_modelproxy_api_key_expected_api_keyry   makedirs
record_dir	claw_type_compress_system_promptllm_providerr   r%   _system_prompt_cache_file_turn_counts_user_turn_counts_pending_turn_data
_prm_tasks_pending_records_session_scored_turns_session_turns_session_last_activeri   _closing_sessions_background_tasks_responses_store_session_sweeper_task_skill_reload_taskr  rW  getattr_SESSION_IDLE_CLOSE_SECONDS_session_idle_close_seconds_SESSION_SWEEP_INTERVAL_SECONDS_session_sweep_interval_seconds_SHUTDOWN_DRAIN_TIMEOUT_SECONDS_shutdown_drain_timeout_seconds_skill_reload_interval_seconds_tui_session_meta_tui_inactivity_timeout_record_file_prm_record_filerecord_enabledopen
_build_appapp_server_thread	threadingEvent_ready_event_server_stopped_event)selfrB  sampling_clientrD  rE  last_request_trackercache_suffix_INACTIVITY_TIMEOUTs           r)   __init__zSkillClawAPIServer.__init__  s     /*$%9" $		#55!'!5!5
F%%5
 (.'7'7:'E$ **+1V-@-@,AB)+f6G6GK_`l_mmrIs)t& -/13>@>@1357"/168!+.547E;==A":>+. <>YZ[,
( 03 @Babc0
, 03 @Babc0
, /2 GLRPRS/
+ 35!':$  "  KK))D9 "V->->@U VD$&GGLL1B1BDV$WD!d''- d++S1  ??$ 2637%OO-%.__%6"  s   0L0L<0L9<Mc           	     f   | t         dfd       }t        d|      }| |j                  _        |j	                  d      d        }|j                  d      t        d       f	 	 	 dd       }|j	                  d	      t        d       f	 	 	 dd
       }|j                  d      t        d       t        d       t        d       t        d       f	 	 	 	 	 	 	 	 	 dd       }|j                  d      t        d       t        d       t        d d      t        d       t        d       f	 	 	 	 	 	 	 	 	 	 	 dd       }|j	                  d      t        d       f	 	 	 	 	 dd       }|j                  d      t        d       f	 	 	 	 	 dd       }	|j                  d      t        d       t        d d      f	 	 	 	 	 dd       }
|j                  d      t        d       t        d d      t        d       t        d d      t        d       t        d       f	 	 	 	 	 	 	 	 	 	 	 	 	 d d       }|S )!Nc               f  K   j                   j                          j                          j                          	 d  j                   j	                          j                          d {    y 7 # j                   j	                          j                          d {  7   w xY wwr  )rs  ri   _start_session_idle_sweeper_start_skill_reload_pollingclear_shutdown_cleanup)_appowners    r)   lifespanz/SkillClawAPIServer._build_app.<locals>.lifespan  s     ""$--/--/0""((*--/// ""((*--///s4   ;B1A9 -B11A72B19.B.'B*(B..B1zSkillClaw Proxy)r   r  z/healthzc                    K   ddiS w)NokTr   r   ro   r)   healthzz.SkillClawAPIServer._build_app.<locals>.healthz  s     $<s   z/internal/reload-skillsr   c                   K   | j                   j                  j                  }|j                  |       d {    |j	                          d {    |j
                  r#t        |j
                  j                               nd}d|dS 7 R7 <w)Nr   T)r  skills)rn  stater  _check_auth_pull_skills_from_cloudrD  r   get_all_skills)requestauthorizationr  skill_counts       r)   reload_skillsz4SkillClawAPIServer._build_app.<locals>.reload_skills  s{     
 )0(9(9(?(?E##M222//111GLGZGZ#e11@@BC`aK+66 31s!   5BB
BB;BBz
/v1/modelsc                   K   | j                   j                  j                  }|j                  |       d {    |j                  }t        d|ddddgd      S 7 %w)Nr"   rJ  r   	skillclaw)r2   rU  rK  owned_by)rU  r  r  )rn  r  r  r  rI  r   )r  r  r  model_ids       r)   list_modelsz2SkillClawAPIServer._build_app.<locals>.list_models  sn     
 )0(9(9(?(?E##M222**H$ #+&-'((3	
  3s   5AA&Az/v1/chat/completionsc                  K   | j                   j                  j                  }|j                          |j	                  |       d {    | j                          d {   }|j                  dg       }t        |t              rt        |      \  }}	||d<   |xs |j                  d      xs d}
|
r |
}t        ||j                  d      d      }net        |j                  d      xs g       }|j                  |j                  dd      |       d {   }t        ||j                  d      d      }t        ||j                  d	            }t        |j                  d
d            }|j                  ||||       d {   }|rt!        |j#                  |      d      S t%        |d         S 7 r7 ]7 7 9w)Nr  r   r   r   rG   r   rJ  r   r   streamFr   r   r   text/event-streamr  rm  r  )rn  r  r  _mark_request_activityr  r:   r$   r    r"   r  r   r   _resolve_tui_sessionr   r   _handle_requestr   _stream_responser   )r  r  x_session_idx_turn_typex_session_doner  r  incoming_messagesrewritten_messagesr  _raw_sidr   r   	msg_countr   r  r  s                    r)   chat_completionsz7SkillClawAPIServer._build_app.<locals>.chat_completions"  s     )0(9(9(?(?E((*##M222 'D $R 8+T2(MN_(`%"A#5Z #Ctxx'=CH
 %
.{DHH[<Q[ab	 4 :;	#(#=#=HHWi0$ 
 /{DHH[<Q[ab	0.AYZL $((8U34F 00%#)	 1  F ()?)?)GTghhz(:;;G 3'sJ   AGF9GF< B7GF?A/GG2G<G?GGz/v1/responsesr   )r   aliasc           
     	  K   | j                   j                  j                  }|j                          |j	                  |       d {    | j                          d {   }|j                         rht        j                  |      }t        ||j                  d      d      }	|j                  ||	      }
|xs |xs |j                  d      xs d}|xsn |j                  |j                  d|j                        t        t        |j                  d      t               r|j                  dg       ng              d {   }t#        ||j                  d	            }t%        |j                  d
d            r"t'        |j)                  ||||	|
|      d      S |j+                  |       d {   }|j-                  ||||	|
|       t/        |      S t1        |j                  d      xs d      j3                         }t%        |j                  dd            }t5        ||j                        }t6        |d<   |rt|j8                  j                  |      }|t;        dd|       t=        t!        |j                  d      xs g       t!        |j                  d      xs g             |d<   |xs |xs |j                  d      xs d}|r |}t        ||j                  d      d      }	not        |j                  d      xs g       }|j                  |j                  d|j                        |       d {   }t        ||j                  d      d      }	t#        ||j                  d	            }|j?                  |||	|       d {   }tA        |d   |j                  d|j                              }t        |j                  d      tB              r6|j                  di       j                  di g      d   j                  di       ni }|rG|g t!        |j                  d      xs g       t        |tB              r|ni d|j8                  |d    <   t%        |j                  d
d            rt'        |jE                  |      d      S t/        |      S 7 e7 P7 n7  7 s7 !w)!Nr   rG   r   r   r   r   rJ  inputr   r  F)record_bodyr   r   injected_skillsr   r  r  r   r  r   r  previous_response_idstoreTr     z previous_response_id not found: r%  r  r  rm  )rJ  rM  r   r  )rm  r  r2   )#rn  r  r  r  r  r:   _responses_native_enabledcopydeepcopyr   r$   &_prepare_native_responses_body_inplacer  rI  r   r    r"   r   r   r   _stream_and_track_responses_forward_to_llm_responses_record_responses_turnr   r!   r=   r*  _PROTOCOL_RESPONSES_COMPATr\  r   r  r  r1  r#   _stream_responses_response)r  r  r  codex_session_idr  r  r  r  r  r   r  r  r   r   response_payloadr  store_responser  storedr  r  assistant_messages                         r)   	responsesz0SkillClawAPIServer._build_app.<locals>.responsesR  s     )0(9(9(?(?E((*##M222 'D..0"mmD1.{DHH[<Q[ab	"'"N"Nt_h"N"i'[+;[txx?U[Y[% u/I/IHHWe&9&9:DHHW<Mt1T"-Z\]0 *
  5^TXXnE]^512,99 (3'1&/,;)5 :  $7
 
 */)H)H)N#N ,,$'$3!- -  $,<==#&txx0F'G'M2#N#T#T#V !$((7D"9:N3D%:M:MNK1KK-.#//334HI>'$'!ABVAWX  +LJ/5264:;+J' $W'7W488L;QWUWH%
.{DHH[<Q[ab	
 ; ArB	#(#=#=OOGU-@-@A$ 
 /{DHH[<Q[ab	0.AYZL 00%#)	 1  F  Az"!oogu/B/BC  fjj4d; 

:r*..y2$?BFFyRTU 
  0!kooj9?R@!-78I4-P)VX!B&&'7'=> DHHXu-.(445EF2   (899y 3'*" $O@sq   AR>R,R>R/ C$R>R2A0R>5R56FR>R8AR>R;DR>/R>2R>5R>8R>;R>z/v1/responses/{response_id}c                   K   |j                   j                  j                  }|j                  |       d {    |j                  j                  |       }|t        dd      t        |d         S 7 =w)Nr  response not foundr%  rm  r  )rn  r  r  r  r\  r$   r   r   r]  r  r  r  r  s        r)   get_responsez3SkillClawAPIServer._build_app.<locals>.get_response  so      )0(9(9(?(?E##M222++//<F~#<PQQz(:;;	 3s   5A7A5>A7c                   K   |j                   j                  j                  }|j                  |       d {    |j                  j                  | d       }|t        dd      t        | ddd      S 7 ?w)Nr  r  r%  rm  T)r2   rU  deletedr  )rn  r  r  r  r\  r  r   r   r  s        r)   delete_responsez6SkillClawAPIServer._build_app.<locals>.delete_response  st      )0(9(9(?(?E##M222++//TBF~#<PQQ{j]a(bcc	 3s   5A9A7A A9z/v1/messages/count_tokensz	x-api-keyc                6  K   | j                   j                  j                  }|j                          |xs	 |rd| nd }|j	                  |       d {    | j                          d {   }t        |      }t        |      }t        d|i      S 7 @7 *w)NBearer input_tokensr  )	rn  r  r  r  r  r:   r  r  r   )r  r  	x_api_keyr  auth_headerraw_bodyr  r  s           r)   anthropic_count_tokensz=SkillClawAPIServer._build_app.<locals>.anthropic_count_tokens  s      )0(9(9(?(?E((*'YYgi[,ATXK##K000$\\^+H3H=K=kJL(FGG 1+s$   ABBB,B-)BBz/v1/messageszx-claude-code-session-idc                "  K   | j                   j                  j                  }|j                          |xs	 |rd| nd }|j	                  |       d {    | j                          d {   }	t        |	j                  dd            }
t        |	      }t        |	      }t        |d<   |	j                  d      xs |j                  }|j                  dg       }t        |t              rt        |      \  }}||d<   |xs |xs |	j                  d      xs d}|r |}t        ||	j                  d	      d
      }nUt!        |j                  d      xs g       }|j#                  ||       d {   }t        ||	j                  d	      d
      }t%        ||	j                  d            }|j'                  ||||       d {   }|
rt)        |j+                  |||      d      S t-        t/        |d   ||            S 7 7 7 7 Fw)Nr  r  Fr   rJ  r  r   r   r   rG   r   r   r  r  r  rm  r  )rn  r  r  r  r  r:   r   r$   r  r  r   rI  r    r"   r  r   r   r  r   r  r   _stream_anthropic_responser   r5  )r  r  r  r  x_claude_code_session_idr  r  r  r  r  r  r  r  rJ  r  r  r  r  r   r   r  r   r  s                          r)   r   z9SkillClawAPIServer._build_app.<locals>.anthropic_messages  s     )0(9(9(?(?E((*'YYgi[,ATXK##K000$\\^+H(,,x78F6x@J3H=K1MK-.LL)@U-@-@E +
B ?+T2(MN_(`%"A*<J'#c'?c8<<P\C]cacH%
.{HLL<U_ef	
 ; ArB	#(#=#=eY#OO
.{HLL<U_ef	0nA]^L 00%#)	 1  F (44VUJO2   (EfZFXZ_ak(lmmG 1+$ PsJ   AHHH,H-DH1H2AHH?HHHH)r  r	   )r  r   r  Optional[str])
r  r   r  r  r  r  r  r  r  r  )r  r   r  r  r  r  r  r  r  r  r  r  )r]  r!   r  r   r  r  )r  r   r  r  r  r  )r  r   r  r  r  r  r  r  r  r  r  r  r  r  )r   r	   r  r  r$   postr
   delete)ru  r  rn  r  r  r  r  r  r  r  r  r   r  s               @r)   rm  zSkillClawAPIServer._build_app  s8   		0 
	0 -A					  
	  
+	, ,2$+?	7	7(	7 
-	7 
	 ,2$+?		(	 
	* 
(	) ,2$+?*0*>)/)=,24,@-	<-	<(-	< (-	< '	-	<
 *-	< 
*-	<^ 
/	" ,2$+?*0*>.4T.V)/)=,24,@f	:f	:(f	: (f	: ,	f	:
 'f	: *f	: 
#f	:P 
.	/ ,2$+?
	<
	<
	< )
	< 
0
	< 
1	2 ,2$+?
	d
	d
	d )
	d 
3
	d" 
-	. ,2$+?'-d+'N	H	H(	H %	H 
/	H 
.	! ,2$+?'-d+'N*0*>6<TQk6l)/)=,24,@0	n0	n(0	n %0	n (	0	n
 '40	n '0	n *0	n 
"0	nd 
ro   c                   K   | j                   sy |r|j                  d      st        dd      |j                  dd      d   j	                         }|| j                   k7  rt        dd      y w)Nr  i  zmissing bearer tokenr%  r   r   zinvalid api key)rK  r   r   r   r=   )ru  r  tokens      r)   r  zSkillClawAPIServer._check_auth  sq     %%M$<$<Y$GC8NOO##C+A.446D***C8IJJ +s   A.A0c                    t        j                          | _        | j                  	 | j                  j                          y y # t        $ r Y y w xY wr  )rV  rG  rF  touchr<   ru  s    r)   r  z)SkillClawAPIServer._mark_request_activity(  sL     $		%%1**002 2  s   A 	AAc                v    t        | dd       }|y t        dt        j                         t        |      z
        S )NrG          )r_  r  rV  r{   )ru  lasts     r)   last_request_age_secondsz+SkillClawAPIServer.last_request_age_seconds0  s5    t/6<3		eDk122ro   c                4    t        | j                               S r  )r   _collect_active_session_idsr  s    r)   active_session_countz'SkillClawAPIServer.active_session_count6  s    433566ro   c                    | j                         }|y| j                         dkD  ry|t        dt        |            k\  S )NFr   )r  r  r  rW  )ru  idle_after_secondsages      r)   is_idle_for_validationz)SkillClawAPIServer.is_idle_for_validation9  sD    ++-;$$&*c!S!34555ro   c                  K   ddl }d| }t        j                         }| j                  j                  |      }|Nd| d|j	                         j
                  dd  }|||d| j                  |<   t        j                  d|       |S d}||d	   k  rd
}t        j                  d|d	   |       nI||d   z
  | j                  kD  r4d
}t        ||d   z
        }	t        j                  d|	| j                         |ro|d   }
| j                  |
d       d{    d| d|j	                         j
                  dd  }|||d| j                  |<   t        j                  d||
       |S ||d	<   ||d<   |d   S 7 bw)u  Return a session_id for agents that don't send X-Session-Id.

        Detects new-conversation boundaries by two heuristics:
          1. Message count dropped — the client started a fresh conversation.
          2. Inactivity timeout — the user was idle for >N seconds.

        When a boundary is detected the old session is flushed (session data
        uploaded, state dicts cleaned up) and a new unique id is assigned.
        r   Nztui--r  )r   last_msg_countlast_request_timez2[SessionDetect] new TUI session %s (first request)Fr  Tu;   [SessionDetect] msg count dropped %d → %d — new sessionr  u4   [SessionDetect] inactivity %ds > %ds — new sessionr   tui_boundaryreasonz1[SessionDetect] new TUI session %s (replacing %s))uuidrV  rg  r$   uuid4hexr   inforh  rW  _close_session)ru  rJ  r  r  tui_keynowmetasidnew_sessionidle_secold_sids              r)   r  z'SkillClawAPIServer._resolve_tui_sessionE  s     	.iik%%))'2<q!1!1"1!5 67C!"+%(/D""7+
 KKLcRJt,--KKKM%&
 D,--1M1MMK3&9!::;HKKF,, <(G%%gn%EEEq!1!1"1!5 67C!"+%(/D""7+
 KKKSRYZJ "+$' !L!! Fs   DE=E;A#E=c                L    |r"t        j                          | j                  |<   y y r  )rV  rY  ru  r   s     r)   _touch_sessionz!SkillClawAPIServer._touch_session  s     48IIKD%%j1 ro   c                ^    t         j                  j                               }|j                   j                  j                                |j                   j
                  j                                |j                   j                  j                                |j                   j                  j                                |j                   j                  j                                |j                   j                  j                                t         fd|D              S )Nc              3  F   K   | ]  }|s|j                   vs|  y wr  )rZ  )r  r  ru  s     r)   r  zASkillClawAPIServer._collect_active_session_ids.<locals>.<genexpr>  s      VAat?U?U6UaVs   !!!)ri   rY  r   rr   rV  rX  rT  rR  rW  rU  rY  )ru  session_idss   ` r)   r  z.SkillClawAPIServer._collect_active_session_ids  s    $3388:;40055784..3356422779:4,,1134455::<=4??//12VVVVro   c                      j                   dk  rg S t        j                         t         j                         t         fd j                  j                         D              S )Nr   c              3  j   K   | ]*  \  }}|r#|j                   vrt        |      z
  k\  r| , y wr  )rZ  r{   )r  r  tsr  ru  	thresholds      r)   r  z?SkillClawAPIServer._collect_idle_session_ids.<locals>.<genexpr>  s=      
Rs$"8"88cE"IoR[=[ 
s   03)ra  rV  r{   rY  rY  rk   )ru  r  r   s   ``@r)   _collect_idle_session_idsz,SkillClawAPIServer._collect_idle_session_ids  s_    ++q0I;))+C$::;	 
44::<
 
 	
ro   c                   | j                   dk  rt        j                  d       y | j                  | j                  j	                         sy t        j                  | j                               | _        | j                  j                  | j                         t        j                  d| j                   | j                         y )Nr   z4[SessionDetect] idle sweeper disabled (timeout <= 0)z?[SessionDetect] idle sweeper started (timeout=%ss interval=%ss))ra  r   r  r]  doneasynciocreate_task_session_idle_sweeper_loopadd_done_callback_task_done_cbrc  r  s    r)   r}  z.SkillClawAPIServer._start_session_idle_sweeper  s    ++q0KKNO%%1$:T:T:Y:Y:[%,%8%89X9X9Z%["""44T5G5GHM,,00	
ro   c                $  K   	 	 t        j                  | j                         d {    | j                         }|D ]  }| j	                  |d       d {     Z7 77 	# t         j
                  $ r t        j                  d        w xY ww)Nidle_timeoutr  z$[SessionDetect] idle sweeper stopped)r  sleeprc  r  r  CancelledErrorr   r  )ru  	stale_idsr  s      r)   r  z-SkillClawAPIServer._session_idle_sweeper_loop  s     	mmD$H$HIII ::<	$ JC--c.-IIIJ I J%% 	KK>?	s2   B#A# A/A# A!A# !A# #*BBc                  K   t        | j                        D cg c]  }|j                         r| }}|sy t        j                  ||       d {   \  }}|rTt
        j                  dt        |             |D ]  }|j                           t        j                  |ddi d {    y t
        j                  dt        |             y c c}w 7 7 ,w)Nr}   z=[OpenClaw] background drain timeout: %d task(s) still runningreturn_exceptionsTz1[OpenClaw] background drain complete (%d task(s)))r"   r[  r  r  waitr   r   r   cancelgatherr  )ru  timeout_secondstpendingr  still_pendingtasks          r)   _await_background_tasksz*SkillClawAPIServer._await_background_tasks  s     "4#9#9:K!&&(1KK$+LL/$RRmNNOM" & ..-H4HHHKKKSQUYW L S Is3   CCCCCAC+C,+CCc                   | j                   j                  sy t        t        | j                   dd      xs d      j	                         j                         }|dk7  ry | j                  | j                  j                         sy t        j                  | j                               | _        | j                  j                  | j                         t        j                  d| j                         y )Nsharing_skill_reload_moder   pollz4[SkillHub] skill reload polling enabled interval=%ds)rB  sharing_enabledr!   r_  r=   r   r^  r  r  r  _skill_reload_poll_loopr  r  r   r  rf  )ru  modes     r)   r~  z.SkillClawAPIServer._start_skill_reload_polling  s    {{**74;;(CRHRFSYY[aac6>"".t7N7N7S7S7U")"5"5d6R6R6T"U11$2D2DEB//	
ro   c                  K   d}d}	 	 |rd}n_t        j                  d| j                  dz        }t        |dz  d      }t	        j
                  | j                  |z   |z          d {    	 | j                          d {    d}7  7 	# t        $ r&}|dz  }t        j                  d||       Y d }~/d }~ww xY w# t        j                  $ r t        j                  d	        w xY ww)
Nr   TFg?g      @g      N@r   z3[SkillHub] skill reload poll failed (streak=%d): %sz'[SkillHub] skill reload polling stopped)randomuniformrf  r  r  r  r  r<   r   r   r  r  )ru  consecutive_failures
first_pulljitterbackoffr)  s         r)   r  z*SkillClawAPIServer._skill_reload_poll_loop  s      
	!&J#^^At/R/RUX/XYF!"6"<dCG!--(K(Kf(TW^(^___	66888+,(  `8  (A-(NNM,  %% 	KKAB	s_   C,A B? (B	)B? .B BB B? B 	B<B72B? 7B<<B? ?*C))C,c                   K   | j                         }|sy t        j                  dt        |      |       |D ]  }| j	                  ||       d {     y 7 w)Nz8[SessionDetect] draining %d active session(s): reason=%sr  )r  r   r  r   r  )ru  r  
active_idsr  s       r)   _drain_active_sessionsz)SkillClawAPIServer._drain_active_sessions  s^     557
NPST^P_agh 	:C%%c&%999	:9s   AAAAc                  K   | j                   J| j                   j                          t        j                  | j                   d       d {    d | _         | j                  J| j                  j                          t        j                  | j                  d       d {    d | _        | j                  d       d {    | j                  | j                         d {    y 7 7 K7 ,7 w)NT)r  server_shutdownr  )r^  r  r  r  r]  r)  r  re  r  s    r)   r  z$SkillClawAPIServer._shutdown_cleanup  s     "".##**,..!8!8DQQQ&*D#%%1&&--/..!;!;tTTT)-D&))1B)CCC**4+O+OPPP R UCPsI   AC5C-AC5#C/$ C5C1"C5'C3(C5/C51C53C5c           
     >
   K   |sy| j                   v ry j                   j                  |       	  j                  |d        j                  j	                  |i       } j
                  j                  |i       } j                  j                  r j                  rt        |j                               D ]  \  }}||v rt        j                   j                  j                  |j	                  dd      |j	                  dd      ||            }|j                   j                          |j                  ||f fd	       |||<    t        |j#                               }|r7	 t        j$                  t        j&                  |ddit(        	       d{    t1        t        |j3                                     D ]  }|j5                  |      }|j5                  dd      }	|j	                  |      }|	#|!|j7                         r	 |j9                         }	|j5                  |d        j?                  ||||	       d{      j@                  j5                  |d      }
 jB                  j5                  |d        jD                  j5                  |d        j                  j5                  |d        j
                  j5                  |i       }|j#                         D ]>  }tG        |t        jH                        s|j7                         r/|jK                          @ t,        jM                  d|||
        jN                  r jN                  jQ                           jR                  j5                  |g       }tU        |      }|r7 j                  jV                  r! jY                   j[                  ||              j                  jV                  r! jY                   j]                  |              j^                  j5                  |d       t         j`                  j                               D ]G  \  }}tG        |tb              s|j	                  d      |k(  s, j`                  j5                  |d       I 	  j                   je                  |       y7 # t        j*                  $ r t,        j/                  d
|       Y 5w xY w# t        j:                  t<        f$ r d}	Y w xY w7 #  j                   je                  |       w xY ww)zUFlush a session: finalize pending turn feedback, upload session data, clean up state.Nresponse_textr   prompt_textr   turn_numc                *    j                  |||       S r  _on_prm_done_record_only)_tr  tnumru  s      r)   <lambda>z3SkillClawAPIServer._close_session.<locals>.<lambda>  s    $B_B_`ceikmBn ro   r  Tr  z2[SessionDetect] PRM drain timed out for session=%s
prm_resultr   z=[SessionDetect] closed session=%s reason=%s (scored_turns=%d)
skip_namesr   )3rZ  rj   _flush_pending_recordrT  r$   rU  rX  rB  use_prmrE  r"   rk   r  r  evaluater  r  valueswait_forr  rd  TimeoutErrorr   r   rY  r   r  r  r  r  r<   _finalize_turn_feedbackrW  rR  rS  r    Taskr  r  rD  _save_statsrX  ru   r  _safe_create_task_upload_session_datar  rY  rg  r#   discard)ru  r   r  r  	prm_tasksr0  	turn_dataprm_taskactive_prm_tasksr7  effr  rs   modified_skill_namesr   r  s   `               r)   r  z!SkillClawAPIServer._close_session  sk    ///"":.I	7&&z48--11*bAG22:rBI{{""t+/+@ 3'Hi9, &2200%MM/2>%MM-<'1%-	 1  H ..t/A/AB..'1n +3Ih'3   $I$4$4$67e!**(8QDQ ?   #4#78 #KK1	&]]<>
$==2%(*>8==?*%-__%6
 h-22	    ,,00Q?C!!*d3""&&z48##''
D9++J;I!((* "dGLL1$))+KKM" KKO	 !!""..0''++J;E#@#G 44&&t'@'@U'ST{{**&&t'C'COc'C'de%%))*d;!$"8"8">">"@A :	TdD)dhh|.D
.R**..sD9: ""**:6c ++ eNN#WYcde $22I> *%)
*B ""**:6s   .TD6S= )1R( R%R( A-S= S)S= S:B?S= S= D<S= S= *S= 	T%R( ()SS= SS= S73S= 6S77S= =TTc                   | j                   j                  |d      }|y||d<   |r|j                  dd      }t        |j                  d            }t        j                  t         d| d|d    d	| d
t        |       d|dd  t                | j                  ||d   |d   |j                  dd      |       | j                  rL	 t        | j                  dd      5 }|j                  t        j                  |d      dz          ddd       yy# 1 sw Y   yxY w# t        $ r }t        j!                  d|       Y d}~yd}~ww xY w)z<Write out the buffered record for *session_id* and fire PRM.N
next_stater  ?r&   z[OpenClaw] session=z turn=rt   z next_state role=z len=r     r-  instruction_textr   autf-8encodingFr/   r  z%[OpenClaw] failed to write record: %s)rV  r  r$   r*   r   r  _GREENr   _RESET_fire_prm_scoringri  rl  rW   r:   r;   OSErrorr   )ru  r   rM  recns_role
ns_contentfes           r)   r:  z(SkillClawAPIServer._flush_pending_recordX  s^   ##''
D9;&L nnVS1G1*..2KLJKK(-j\F} M##*)5Z0Ads#$VH.
 ""FO$*B/ K$++S7C HqGGDJJs?$FGH H H H KFJJKs6   
D" "*DD" DD" D" "	E+EEc           	         | j                   sy t        |      }||t        j                  d      |||||xs d d| j                  |<   y )Nz%Y-%m-%d %H:%M:%S)r   rt   	timestampr  rP  r.  r-  r?   )ri  r2  rV  strftimerV  )ru  r   r0  r  r.  r-  r?   rP  s           r)   _buffer_recordz!SkillClawAPIServer._buffer_recordt  sT       9(C$':;  0&*$,	-
j)ro   c           
     .   | j                   sy 	 t        | j                   dd      5 }|j                  t        j                  ||||dd      dz          d d d        y # 1 sw Y   y xY w# t
        $ r }t        j                  d|       Y d }~y d }~ww xY w)	NrQ  rR  rS  )r   rt   scorevotesFr/   r  z)[OpenClaw] failed to write PRM record: %s)rj  rl  rW   r:   r;   rX  r   r   )ru  r   r0  rc  rd  r\  r]  s          r)   _append_prm_recordz%SkillClawAPIServer._append_prm_record  s    $$	Kd++S7C qJJ*4$,%*%*	 &+ 	    	KNNFJJ	Ks4   A+ /AA+ A($A+ (A+ +	B4BBc                   | j                   df| j                  dffD ]:  \  }}|s		 t        |d      j                          t        j                  d||       < y# t        $ r!}t        j                  d||       Y d}~cd}~ww xY w)zClear all record JSONL files.recordz
PRM recordrA  z[OpenClaw] %s file purged: %sz&[OpenClaw] failed to purge %s file: %sN)ri  rj  rl  closer   r  rX  r   )ru  r   labelr]  s       r)   purge_record_filesz%SkillClawAPIServer.purge_record_files  s     )""L1
 
	SKD% ST3%%';UDI
	S  SGPQRRSs   1A	B$B  Bc                     j                   r|sy |xs d}t        j                   j                   j                  ||            }|j	                   j
                         |r|j	                   fd       n|j	                   fd       | j                  j                  i       <    j                  j                  i       j                        }	|	d|	d<   y y )Nr   r/  c                *    j                  |       S r  )_on_prm_doner4  ru  r   r0  s    r)   r6  z6SkillClawAPIServer._fire_prm_scoring.<locals>.<lambda>  s    d.?.?
HVX.Y ro   c                *    j                  |       S r  r2  rn  s    r)   r6  z6SkillClawAPIServer._fire_prm_scoring.<locals>.<lambda>  s    d.K.KJX`bd.e ro   Thas_next_state)
rE  r  r  r<  r  r  rU  rX  rT  r$   )
ru  r   r0  r-  rP  rM  finalize_ready_turns	inst_textr  tds
   ```       r)   rW  z$SkillClawAPIServer._fire_prm_scoring  s     j$*	""OO$$]I*_g$h
 	t112""#YZ""#ef?C"":r28<$$((R8<<XF>#'B  ro   c                   |r|j                  dd      nd}| j                  j                  |g       }|dz
  }d|cxk  rt        |      k  rn n|||   d<   ||   j                  dg       }|r(| j                  r| j                  j	                  ||       ||   j                  dg       }|r_| j                  rS|D 	cg c]*  }	t        |	t              s|	j                  d      s&|	d   , }
}	|
r| j                  j	                  |
|       | j                  j                  |i       j                  |      }t        |t              r||d	<   y y c c}	w )
Nrc  r  r   r   	prm_scorer  rz  rh   r7  )r$   rX  r   rD  record_feedbackr    r#   rT  )ru  r   r0  r7  rc  rs   r  injectedrM   r
read_namespending_turns               r)   _apply_prm_resultz$SkillClawAPIServer._apply_prm_result  s7    1;
w,##''
B7l c%j &+E#J{#Sz~~&7<HD..""228UC:>>-4D**7;k!z!T?RWXW\W\]iWjaok
k&&66z5I..22:rBFFxPlD))3L& *	 ls   =EE%Ec                    |j                         ry	 |j                         }| j                  |||       || j                  v ry| j                  |       y# t        $ r Y yw xY w)uQ   Callback after PRM scoring completes — write score back and update skill stats.N)	cancelledr  r<   r{  rZ  _maybe_finalize_ready_turnsru  r   r0  r  r7  s        r)   rm  zSkillClawAPIServer._on_prm_done  sf    >>	J 	z8Z@///((4  		s   A 	A#"A#c                    |j                         ry	 |j                         }| j                  |||       y# t        $ r Y yw xY w)z>Callback used for close-session PRM tasks; records score only.N)r}  r  r<   r{  r  s        r)   r3  z+SkillClawAPIServer._on_prm_done_record_only  sG    >>	J 	z8Z@  		s   7 	AAc                    	 t        | j                  dd      5 }t        j                  |      }d d d        j	                  dd      }t        |t              r|S dS # 1 sw Y   /xY w# t        $ r Y yw xY w)Nrx  rR  rS  compressed_system_promptr   )rl  rQ  r:   loadr$   r    r!   r<   )ru  r\  r  cacheds       r)   _read_cached_system_promptz-SkillClawAPIServer._read_cached_system_prompt  sv    	d44cGL $PQyy|$XX8"=F'46<"<$ $  		s-   A* A+A* A* A'#A* *	A65A6c                    	 t        | j                  dd      5 }t        j                  d|i|d       d d d        y # 1 sw Y   y xY w# t        $ r }t
        j                  d|       Y d }~y d }~ww xY w)NrA  rR  rS  r  Fr/   z2[OpenClaw] failed to write system prompt cache: %s)rl  rQ  r:   dumpr<   r   r   )ru  promptr\  r]  s       r)   _write_cached_system_promptz.SkillClawAPIServer._write_cached_system_prompt 	  sq    	Td44cGL WPQ		5v>PUVW W W 	TNNOQRSS	Ts1   A
 >A
 AA
 A
 
	A3A..A3c                  K   t        j                  d      xs d      j                         }j                  d      }t        |t              r|st        dd      | j                  |       d}|D ]o  }t        |t              s|j                  d      d	k(  s)t        |j                  d
      t               sI|j                  d
d      j                  d      sk|dz  }q |rt        j                  d|       fd}	d}
| j                  r| j                         }
|
sd}|D ]C  }t        |t              s|j                  d      dk(  s)t        |j                  d
            } n |rW	 t        j                  t         d	|dg| j"                         d {   }
|
xs |j                         }
| j)                  |
       |
r2|D ]-  }t        |t              s|j                  d      dk(  s)|
|d
<   / t+        || j,                  j                  |g             }|rt        j                  d|       j                  d      }g }| j.                  r|dk(  r| j1                  |      \  }}| j                  r"|
r t        j                  d |	d|
dg             | j"                  j2                  t5        j                  d      xs d      z
  }|dkD  r| j7                  |||      }j9                         D ci c]  \  }}|t:        vs|| }}}d|d<   |j=                  dd        d|vr| j>                  |d<   ||d<   | jA                  |       d {   }|j                  d      xs | j>                  |d<   |j                  di g      d   }|j                  di       }t        |t              sdt        |      d}|j                  d      xs g }t        |t              rt	        |      ng }|j                  d
      }t        |t              r"tC        |      \  }}|}|jE                  |       nt        |      }tG        |      \  }}|r|}|jE                  |       tI        |      }||d
<   |r||d<   n|j=                  dd        ||d<   t        |j                  d      t              r|d   r	||d   d<   n|g|d<   |j                  d       xs d} t        j                  tJ         d!| d"| d#tM        |       tN                t        j                  tP         d!| d"| d$tM        |        d%| tN         
       |r.t        j                  d&tS        jT                  |d'      d d(        |dk(  rtW        |      }!| j,                  j                  |g       }"|!r|"rtY        |      }#t[        |"d)   |!|#       || j\                  v r|r| j_                  ||d)          t        |      }$|$j                  d
      d|$d
<   | j.                  r| j.                  ja                         ni }%tc        ||%      }&te        ||%      }'tg        |      }(|&r6t        j                  d*tM        |&      d+ji                  d, |&D                     |'r6t        j                  d-tM        |'      d+ji                  d. |'D                     tk        |      })| jl                  j                  |d      dz   | jl                  |<   | jl                  |   }*d/ji                  d0 |D              }+|xs |rtS        jT                  |d'      nd},| jo                  ||*||+|,|       tq        |||      }-|*|-|)|,| xs d ||&|'|(g g g |d d1}.| j,                  js                  |g       ju                  |.       tw        |-      r(| jy                  |      }/|/|.d2<   | j{                  ||/       |+|,d3| j|                  js                  |i       |*<   t        j                  d4||*|.j                  d2d5      |-t        ||d6      tM        |,             | j                  |       nt        j                  d7|       |r| j                  |       d {    ||d8<   d9|iS 7 # t$        $ r1}t        j'                  d|       |j                         }
Y d }~d }~ww xY wc c}}w 7 t7 Vw):Nr   r   r  r$  z!messages must be a non-empty listr%  r   r  r1  r&   z A new chat session just started.r   zG[OpenClaw] rewrote %d /new bootstrap user prompt(s) for provider safetyc                >    t        | j                  d      d      S )Nr  r  r  )r  r$   )msgsr  s    r)   _prompt_lenz7SkillClawAPIServer._handle_request.<locals>._prompt_len	  s     54RVRZRZ[bRc6deero   r'  r*  uK   [OpenClaw] system prompt compression failed: %s — using raw system promptzP[OpenClaw] restored reasoning_content on %d prior assistant tool-call message(s)r  rG   z&[OpenClaw] system prompt cached len=%d
max_tokensi   Fr  stream_optionsrJ  rM  r  r  r?   r  z[OpenClaw] [z
] session=z prompt_msgs=z
 thinking=z chars, response:
z[OpenClaw] tool_calls: %sr/   r   r
  z)[SkillManager] model read %d skill(s): %s, c              3  @   K   | ]  }|j                  d d        ywrh   rN  Nr$   r  rx  s     r)   r  z5SkillClawAPIServer._handle_request.<locals>.<genexpr>	  s     L1aeeL#6L   z-[SkillManager] model modified %d skill(s): %sc              3  @   K   | ]  }|j                  d d        ywr  r  r  s     r)   r  z5SkillClawAPIServer._handle_request.<locals>.<genexpr>	  s     P1aeeL#6Pr  r  c           	   3  |   K   | ]4  }|j                  d d       dt        |j                  dd              6 yw)r  rN  r  r&   r   N)r$   r*   )r  r  s     r)   r  z5SkillClawAPIServer._handle_request.<locals>.<genexpr>	  s?      $^_155%&b)A!%%	SUBV)W(XY$s   :<)r0  r   r.  r-  r  r?   rz  rq   r  r  r  r  r  ru  user_turn_num)r.  r-  z^[OpenClaw] MAIN session=%s turn=%d user_turn=%s kind=%s prompt_est_tokens=%d response_chars=%dr  r  z9[OpenClaw] SIDE session=%s -> skipped (side-channel turn)r   rm  )Br!   r$   r=   r    r"   r   r  r#   r   r   r  rO  r  r*   r  	to_threadr   rB  r<   r   r  r  rX  rD  _inject_skillsmax_context_tokensrW  _truncate_messagesrk   _NON_STANDARD_BODY_KEYSr  rI  _forward_to_llmrD   r   r  r$  _YELLOWr   rV  _REDr:   r;   rF  rH  r  rV  r:  get_skill_path_mapr  r  ru  r%   r2  rR  ra  r   rX  r9   r   _next_user_turn_num_maybe_upload_session_snapshotrT  r  r~  r  )0ru  r  r   r   r   r   r  r  r  r  cached_system
raw_systemr  r]  restored_reasoningr  r  
max_promptkvforward_bodyoutputra  assistant_msgr	  r?   r
  	part_textr  r&   clean_contentr  r  r  
prev_turnsr  response_msgr   rz  rq   tool_summariesuser_instructionr0  r.  r-  r   r  r  s0    `                                              r)   r  z"SkillClawAPIServer._handle_request	  s     txx 56<"=CCE88J'(D)C8[\\J'	 	C3%GGFOv-swwy137GGIr*556XYQ		 KKaclm	f '' ;;=M 
! A!!T*quuV}/H%=aeeI>N%O
 ;.5.?.?#&,DE KK/ )
 *7)D*(K(K(M 44]C! 5A!!T*quuV}/H'4)5 @##J3
 KKb"
 ! &()v"5(,(;(;H(E%Ho''MKK8h=IJK [[33c$((<:P:XTX6YY
>..x
KH)-ZABY9Y1ZZ!&X)40,&$($6$6L!#+Z ++L99&**73It7I7IwIt,Q/

9b1-.%0=UVc=deM&**<8>B-7-MT.)SU
#''	2k4()KK)X&IGo..{;G *Gw)O&#Go.,Z8
#*i *4M,'lD1)yfjj+T2vi7H#)F9a !'F9!%%&9:@b	wi|I;jMZ]^fZgYhiohpqrfL:j\ BI'':7)F8M	
 KK3TZZ
Y^5_`dad5ef7AL,,00R@J
#G#Q &z"~|EUVT222x**:x|D.L	*2*,Y'HLHZHZT//BBD`bN>K GO 3:>N?$IILLL
 C(IIPPP  >hG,0,=,=,A,A*a,PST,TDj)((4H)) $ck$ K $iV`

:E(RfhM
Hh]\fg3HgzRM$!./!.%.%6$(*#2 .$&%'!#2!K  **:r:AA+N%m4 $ 8 8 D/<O,33JN*!.LD##..z2>xH KKp52SX3YZM" ,,Z8KKSU_`%%j111)|F##{) % ;i )3(8(8(:;P [ :J 2s   Bc	cc>!c Ac<cc1,b b	b 6(cc4Dc?c	c	AccTc;c<c	b 	c&c;cc
ccc                   K   | j                   j                  dk(  r| j                  |       d{   S | j                  |       d{   S 7 7 w)uU  Forward to a real LLM API.

        Supports providers:
          - ``"openai"`` (default) — any OpenAI-compatible ``/v1/chat/completions`` endpoint.
          - ``"openrouter"`` — OpenRouter gateway (OpenAI-compatible + routing extensions).
          - ``"bedrock"`` — AWS Bedrock Converse API via :class:`BedrockChatClient`.
        bedrockN)rB  rP  _forward_to_llm_bedrock_forward_to_llm_openai)ru  r  s     r)   r  z"SkillClawAPIServer._forward_to_llm	  sK      ;;##y055d;;;00666 <6s!   .AAA	A
AAc                l    t        t        | j                  dd      xs d      j                         dk(  S )zBReturn whether /v1/responses should be forwarded as Responses API.llm_api_modechatr  )r!   r_  rB  r   r  s    r)   r  z,SkillClawAPIServer._responses_native_enabled	  s.    74;;?I6JPPRVaaaro   c                  | j                   j                  j                  d      }|st        dd      |j	                         D ci c]  \  }}|t
        vs|| }}}| j                   j                  xs |j                  dd      |d<   ||d<   i }| j                   j                  rd| j                   j                   |d	<   | d
||fS c c}}w )zBuild URL, body, and headers for native Responses forwarding.

        Native mode intentionally keeps Responses-only tools (custom, web_search,
        namespace, etc.) untouched instead of converting the request to chat.
        r     <llm_api_base is not configured. Run 'skillclaw setup' first.r%  rJ  r   r  r  Authorizationz
/responses)	rB  llm_api_baserstripr   rk   r  llm_model_idr$   llm_api_key)ru  r  r  api_baser  r  	send_bodyheaderss           r)   _prepare_responses_forwardz-SkillClawAPIServer._prepare_responses_forward
  s     ;;++2237U 
 '+jjlWdaa?V6VQTW	W![[55N'29N	'$	("$;;"")01H1H0I'JGO$:&	7:: Xs   CCc               B    t        |      }| j                  ||       |S )zIApply non-destructive SkillClaw hooks before native Responses forwarding.r  )r#   r  )ru  r  r   prepareds       r)   _prepare_native_responses_bodyz1SkillClawAPIServer._prepare_native_responses_body
  s#    :33H	3Rro   c                  | j                   r|dk7  rg S 	 | j                   j                          | j                   j                  t        | j                  dd            }|sg S | j                   j                         }|D cg c]%  }t        |t              s|j                  dd      ' }}t        j                  d	t        |      d
j                  |      dd        | j                   j                  |       t!        |j                  dd            }|r|dz   |z   j#                         n||d<   |S # t        $ r!}t        j	                  d|       Y d}~+d}~ww xY wc c}w )zKInject skills into a Responses body in-place. Returns injected skill names.rG   1[SkillManager] failed to refresh local skills: %sNmax_skills_prompt_chars0u  	max_charsr-   unknown_skillzD[SkillManager] listing %d skills in Codex Responses instructions: %sr  r$  instructionsr   

)rD  refresh_if_changedr<   r   r   build_injection_promptr_  rB  r  r    r#   r$   r  r   r%   record_injectionr  r=   )	ru  r  r   r]  
skill_text
all_skillsr  skill_namesexistings	            r)   r  z9SkillClawAPIServer._prepare_native_responses_body_inplace$
  sM   !!Y&%8I	S113 ''>>dkk+DfM ? 

 I''668
?Ia!ZXY[_M`quuV_5aaRIIk"4C(	

 	++K8/0LMKS6 1J >EEGYc^)  	SNNNPQRR	S bs#   D5 E"E"5	E>EEc                  |sy| j                  |       t        |j                  dd            }|j                  d      }t        |t              r|r|dz   |z   j                         n|}nt        |t              rg }	|D ]R  }
t        |
t              s|
j                  d      dk(  s)|	j                  t        |
j                  dd                   T |	r+d	j                  |	      }|r|dz   |z   j                         n|}g }|j                  d
g       D ]  }
t        |
t              s|
j                  d      dk(  r_|
j                  dg       D ]I  }t        |t              s|j                  d      dk(  s)|j                  |j                  dd             K |
j                  d      dk(  s|
j                  dd      }t	        |
j                  dd            dd }|j                  d| d|         dj                  |      }| j                  j                  |g       }t        |      dz   }||dk(  rdnd|dd |dd |dd}|j                  |       |dk(  r(| j                  |      }||d<   | j                  ||       t        j!                  d||||j                  dd      t        |      t        |      |rdj                  |      nd       |r#| j#                  | j%                  |d !             yy)"z=Record a Responses API turn into the session tracking system.Nr  r   r  r  r  r1  r&   r   r  r   r  r  r   function_callr-   r.   r   z[tool:z] r   rG   r   rH   i  )r0  r   r.  r-  r  ru  r  zV[Codex] %s session=%s turn=%d user_turn=%s prompt=%d chars response=%d chars skills=%sr  r  z(none)codex_session_doner  )r  r  r$   r    r!   r=   r"   r#   r9   r%   rX  rX  r   r  r  r   r  rC  r  )ru  r   request_bodyr  r   r  r   r.  inp
user_partsr'   joinedresponse_partsr  r-   rB   r-  rs   r0  r  r  s                        r)   r  z)SkillClawAPIServer._record_responses_turnA
  s    J'2<3C3CNTV3WXw'c3@K;-3::<QTKT"J ]dD)dhhv.>&.H%%&B488IWYCZ&[\] *-GR{T1F:AACX^$((26 
	?DdD)xx9, HHY3 DD!$-$((62Bm2S&--dhhvr.BCD &!_4xx+488K45ds;%%tfBtf&=>
	? 		.1##..z2>u:> (1V(;W&u-*5D1.
 	[! 44Z@M+8K(//
MJdOOOS1)8CHH_%h		
 ""4#6#6zJ^#6#_` ro   c           
       K   ddl }| j                  |d      \  }}}d}t        |      D ]t  }	 |j                  t	                     4 d{   }|j                  |||       d{   }	|	j                          |	j                         cddd      d{    c S  y7 U7 ;7 # 1 d{  7  sw Y   nxY w# |j                  $ r}
|
j                  j                  dd }||d	z
  k  r{t        d
|z  t        j                  dd	      z   d      }t        j                  d|d	z   |||
j                  j                   |       t#        j$                  |       d{  7   Y d}
~
?t        j'                  d|
j                  j                   |       t)        dd|
       |
d}
~
wt*        $ r}
||d	z
  k  rft        d
|z  t        j                  dd	      z   d      }t        j                  d|d	z   |||
       t#        j$                  |       d{  7   Y d}
~
t        j'                  d|
d       t)        dd|
       |
d}
~
ww xY ww)z?Forward a Codex Responses payload to an upstream Responses API.r   NFr  r  r  r:   r  rO  r   r   r  zM[OpenClaw] upstream Responses error (attempt %d/%d), retrying in %.1fs: %s %sz*[OpenClaw] upstream Responses error: %s %s  zUpstream Responses error: r%  zJ[OpenClaw] Responses forward failed (attempt %d/%d), retrying in %.1fs: %sz'[OpenClaw] Responses forward failed: %sTexc_infozResponses forward error: )httpxr  r  AsyncClientr~   r  raise_for_statusr:   HTTPStatusErrorrm  r   r  r!  r"  r   r   r&  r  r  errorr   r<   )ru  r  r  r  r  r  max_retriesattemptclientrespr]  r-  r  s                r)   r  z,SkillClawAPIServer._forward_to_llm_responses
  sB    "&"A"A$u"A"UY[) '	dG&d ,,5Q5S,T ' 'X^!'& ' "- " D
 ))+99;' ' ''	d'' ' ' ' ' (( e !

 5[1_,q'zFNN1a,@@"EDNNg!#

..% "-----I1::KaKacpq#>XYZX[<\]cdd d[1_,q'zFNN1a,@@"EDNNd!# "-----FTXY#>WXYWZ<[\bccds   ,IB?B#B?B))B%*#B)B?B'B?I#B?%B)'B?)B;	/B20B;	7B?>I?IBF&E!F&$I*<F&&I2A"I	HI	I )I		II)r  c          	    ^  K   d}d}i i dfddfddfd}	| j                  |      2 3 d{   }
|s	 t        |
t              r|
j                  dd	      n|
}||z  }d
|v r|j	                  d
d      \  }}|j                         }|j                  d      s;|dd }|dk(  rF	 t        j                  |      }t        |t              r |	|      nd}|| j                  ||xs |||||       d}nd
|v r|
 7 # t        $ r Y w xY w# t        $ r Y "w xY w6 yw)zNWrap _stream_llm_responses: passthrough SSE + parse response.completed inline.Fr   c                    j                  | dddg d      }|j                  dg       }t        |t              sg |d<   |S )Nr  r  	completed)r   r  statusr&   r&   )rX  r    r"   )output_indexr'   r&   output_itemss      r)   ensure_message_itemzKSkillClawAPIServer._stream_and_track_responses.<locals>.ensure_message_item
  sQ    **%')!	D ooi4Ggt,"$YKro   c                (    |       }|j                  dg       }t        |      |k  r$|j                  ddg d       t        |      |k  r$||   }t        |t              r0|j                  d      xs d|d<   ||d<   |j                  dg        y y )Nr&   r  r   )r   r   r   r   r   r   )rX  r   r9   r    r#   r$   )r  content_indexr   r'   r&   r  r  s         r)   apply_output_textzISkillClawAPIServer._stream_and_track_responses.<locals>.apply_output_text
  s    &|4Dooi4Gg,-/rRTUV g,-/=)D$%#xx/@=V#Vr2 &ro   c                   | j                  d      }t        | j                  dd      xs d      }t        | j                  dd      xs d      }|dk(  r'| j                  d      }t        |t              r||<   y |dk(  r'| j                  d      }t        |t              r||<   y |dk(  rF||f}j                  |d	      t	        | j                  d
      xs d	      z   |<    
|||          y |dk(  rBt	        | j                  d      xs j                  ||fd	            }|||f<    
|||       y |dk(  rw| j                  d      }t        |t              rU|j                  d      dk(  rAt	        |j                  d      xs j                  ||fd	            }|||f<    
|||       y |dk(  rt        | j                  d      t              r| j                  d      n
t        |       }rC|j                  d      s2i |dt        j                               D 	cg c]  \  }	}|	 c}}	i}|S y c c}}	w )Nr   r  r   r  zresponse.output_item.addedr'   zresponse.output_item.donezresponse.output_text.deltar   rQ  zresponse.output_text.doner   zresponse.content_part.doner  r  zresponse.completedrm  r  )r$   rW  r    r#   r!   rY  rk   )r  
event_typer  r  r'   r   r   r  r  r  r  r  output_text_partss             r)   parse_responses_stream_eventzTSkillClawAPIServer._stream_and_track_responses.<locals>.parse_responses_stream_event
  so   &)Jtxx:?a@L! < ABM99xx'dD)15L.6 5 ::xx'dD)15L.. - ;;#]3):)>)>sB)G#dhhW^N_NeceJf)f!#&!,?PQT?UV& % ::488F+g/@/D/DlTaEbdf/ghCG!<"?@!,tD  ;;xx'dD)dhhv.>-.Otxx/k3D3H3H,XeIfhj3klDGK%|]&CD%lM4H  33;EdhhzFZ\`;a488J#7gklpgq (8(<(<X(F(*( vl>P>P>R7S"TGAt4"T($ (' #Us   *H?
NrR  ignore)errorsr  r   data: r  rj  r  T)r  rW  returndict[str, Any])r  rW  r  rW  r   r!   r  None)r  r  r  dict[str, Any] | None)_stream_llm_responsesr    bytesdecoder   r=   r   r:   r   r<   r#   r  )ru  r  r  r   r   r  r   trackedbufr  chunkr   rn  strippedrm   r  r  r  r  r  r  s                    @@@@r)   r  z.SkillClawAPIServer._stream_and_track_responses
  sr     248:			3#	J  55d; 	 	%EOPUW\E]5<<<AchD4KC#+$'IIdA$6	c#'::<'228<$&qrl(?$%#'::c?D R\\`bfQg+G+Mmq(+7 77 * + 3t 0*30?-9 8  '+G!/ #+4 K?	  ) %$% ! ; <sh   .D-D+DD+D- A0D1D>DD-D+	DDDD	D(%D-'D((D-c                 K   ddl }| j                  |d      \  }}}	 |j                  t                     4 d{   }|j	                  d|||      4 d{   }|j                          |j                         2 3 d{   }|s| 7 T7 87 6 ddd      d{  7   n# 1 d{  7  sw Y   nxY wddd      d{  7   y# 1 d{  7  sw Y   yxY w# |j                  $ rZ}	|	j                  j                  dd }
t        j                  d	|	j                  j                  |
       t        d
d|	       |	d}	~	wt        $ r.}	t        j                  d|	d       t        d
d|	       |	d}	~	ww xY ww)zKPassthrough upstream Responses SSE without aggregating or rewriting events.r   NTr  r  POSTr  rO  z1[OpenClaw] upstream Responses stream error: %s %sr  z!Upstream Responses stream error: r%  z&[OpenClaw] Responses stream failed: %sr  zResponses stream error: )r  r  r  r~   r  r  aiter_bytesr  rm  r   r   r  r&  r   r<   )ru  r  r  r  r  r  r  r  r  r]  r-  s              r)   r  z(SkillClawAPIServer._stream_llm_responses$  s|    "&"A"A$t"A"TY	_((1M1O(P ( (TZ!==9g=V ( (Z^))+'+'7'7'9 ( (e "'K((('9( ( ( ( (( ( ( ( ( $$ 	hJJOODS1MLLLajjNdNdfstC:[\][^8_`fgg 	_LLA1tLTC:RSTRU8VW]^^	_s   FC' BC' CBC!B+ BB
BB+B+C' CBB+C$B'%C+B=	1B42B=	9C C' CC' FC$CC$ C' #F$C' 'F6AEF)F  FFc           
       K   ddl }| j                  j                  j                  d      }|st	        dd      |j                         D ci c]  \  }}|dvs|| }}}| j                  j                  xs |j                  dd	      |d<   d
|d<   i }| j                  j                  rd| j                  j                   |d<   | j                  j                  dk(  r%| j                  j                  r| j                  j                  |d<   | j                  j                  r| j                  j                  |d<   | j                  j                  }|r|dk7  rd|i|d<   | j                  j                  }	|	rX|	j                  d      D 
cg c]#  }
|
j                         s|
j                         % }}
|r|j                  dd	      g|z   |d<   | j                  j                   dk(  r|j#                  di        d|d   d<   d}t%        |      D ]w  }	 |j'                  t)                     4 d{   }|j+                  | d||       d{   }|j-                          |j/                         cddd      d{    c S  yc c}}w c c}
w 7 c7 F7 # 1 d{  7  sw Y   nxY w# |j0                  $ r}|j2                  j4                  dd }|j2                  j6                  dk(  rEd|j2                  j4                  v r-t8        j;                  d       |j=                  d d       Y d}~)|j2                  j6                  dk(  rd!|j2                  j4                  v rt8        j;                  d"       t?        |      }d#|d<   	 |j'                  t)                     4 d{  7  }|jA                  d$| d||      4 d{  7  }|j-                          tC        |       d{  7  }ddd      d{  7   n# 1 d{  7  sw Y   nxY wddd      d{  7   n# 1 d{  7  sw Y   nxY wtE        |j                  dd	      %      cY d}~c S # |j0                  $ rX}t8        jG                  d&|j2                  j6                  |j2                  j4                  dd        t	        d'd(|       |d}~wtH        $ r.}t8        jG                  d)|d#*       t	        d'd+|       |d}~ww xY w||d,z
  k  r{tK        d-|z  tM        jN                  dd,      z   d.      }t8        jQ                  d/|d,z   |||j2                  j6                  |       tS        jT                  |       d{  7   Y d}~t8        jG                  d0|j2                  j6                  |       t	        d'd1|       |d}~wtH        $ r}||d,z
  k  rftK        d-|z  tM        jN                  dd,      z   d.      }t8        jQ                  d2|d,z   |||       tS        jT                  |       d{  7   Y d}~6t8        jG                  d3|d#*       t	        d'd4|       |d}~ww xY ww)5z$Forward to an OpenAI-compatible API.r   Nr   r  r  r%  >   logprobstop_logprobsr  rJ  r   Fr  r  r  
openrouterzX-TitlezHTTP-Refererfallbacksortproviderr  modelsdenydata_collectionr  r  z/chat/completionsr  rO  r$  z'temperature' is not supportedzB[OpenClaw] upstream rejects temperature param, retrying without ittemperaturezStream must be set to truezF[OpenClaw] upstream requires stream=true, retrying with SSE collectionTr  )r[  z*[OpenClaw] upstream SSE retry error: %s %sr  zUpstream LLM SSE retry error: z([OpenClaw] upstream SSE retry failed: %sr  zUpstream LLM SSE retry failed: r   r   r  zG[OpenClaw] upstream LLM error (attempt %d/%d), retrying in %.1fs: %s %sz$[OpenClaw] upstream LLM error: %s %szUpstream LLM error: zD[OpenClaw] LLM forward failed (attempt %d/%d), retrying in %.1fs: %sz![OpenClaw] LLM forward failed: %szLLM forward error: )+r  rB  r  r  r   rk   r  r$   r  rP  openrouter_app_nameopenrouter_app_urlopenrouter_routeopenrouter_fallback_modelsr   r=   openrouter_data_policyrX  r  r  r~   r  r  r:   r  rm  r   r&  r   r  r  r#   r  ro  rf  r  r<   r  r!  r"  r   r  r  )ru  r  r  r  r  r  r  r  router  r  r  r  r  r  r  r]  r-  stream_bodystream_resprZ  stream_errorr  s                          r)   r  z)SkillClawAPIServer._forward_to_llm_openai8  s    ;;++2237U  '+jjlndaa?m6mQTn	n![[55N'29N	'#	("$;;"")01H1H0I'JGO$ ;;##|3{{..%)[[%D%D	"{{--*.++*H*H'KK00E*,)/	*%{{==H-5^^C-@NAGGI!'')NN+4=="+E*F*OIh'{{11V;$$Z4;A	*%&78[) N	^GM^ ,,5Q5S,T ' 'X^!'#*$56& ' "- " D
 ))+99;' ' 'N	^= o* O'' ' ' ' ' (( 6_ !

 5::))S05UYZYcYcYhYh5hKK deMM-6::))S05QUVU_U_UdUd5dKK hi"&y/K,0K),#(#4#4=Y=[#4#\ U U`f'-}} &#+*,= >%0(/	 (5 ( U U
 "- + < < >/G/T)T)TU U U U UU U U U U  C"+4=="+E   !00 	,H(11==(1166t<
 ,(+%CL>#R  ,, % ,%OQ]hlm+(+%D\N#S  ,,, [1_,q'zFNN1a,@@"EDNNa!#

..% "-----CQZZE[E[]jk#>RSTRU<VW]^^ ^[1_,q'zFNN1a,@@"EDNN^!# "-----@!dS#>QRSQT<UV\]]^s  AY<JJ D0Y<J&J8A Y<J77J8J7;J!J#J!:J7JJ7Y<J7J!J7!J3	'J*(J3	/J76Y<7Y9A1W8Y<>AWQ35N8
6Q3:P:OP:P	;O><P	P:P
P:P%PP%!P:(Q33P64Q3:Q QQ#Q3+Y9,Y<3TAST!)T

TA:W	V
WY<<WY9A"Y4?Y Y4Y<)Y44Y99Y<c                  K   ddl m} | j                  j                  }|st	        dd      |j                  dg       }|j                  dd      }|j                  d	      xs |j                  d
      xs d}	  ||| j                  j                        }t        j                  |j                  j                  j                  ||||       d{   }|j                  r|j                  d   nd}	dt        t        j                                d|d|	r|	j                  j                   nd|	r|	j                  j"                  ndd|	r|	j$                  nddg|j&                  j(                  |j&                  j*                  |j&                  j,                  ddS 7 # t.        $ r.}
t0        j3                  d|
d       t	        dd|
       |
d}
~
ww xY ww)z-Forward to AWS Bedrock via BedrockChatClient.r   )BedrockChatClientr  z;llm.model_id (Bedrock inference profile) is not configured.r%  r  r  g333333?max_completion_tokensr  i    )r  region)rJ  r  r  r&  Nr   zchatcmpl-bedrock-rT  r  r   r*  rR  rS  )prompt_tokenscompletion_tokenstotal_tokens)r2   rU  rJ  rM  rL  z%[OpenClaw] Bedrock forward failed: %sTr  r  zBedrock forward error: )bedrock_clientr%  rB  r  r   r$   bedrock_regionr  r  r  completionscreaterM  rW  rV  r  r  r&   rP  rL  r(  r)  r*  r<   r   r  )ru  r  r%  r  r  r  r  r  r  ra  r]  s              r)   r  z*SkillClawAPIServer._forward_to_llm_bedrock  s    5;;++T 
 88J+hh}c2XX56X$((<:PXTX
$	^&!{{11F !**''..!'&0 D )-T\\!_$F)#diik*:);<+! "#;AFNN$7$7{AGv~~'='=R$ BH)=)=V	 &*ZZ%=%=)-)E)E$(JJ$;$; :  	^LL@!dLSC:QRSQT8UV\]]	^s>   A8G;AF$ F"CF$ !G"F$ $	G-)GGGc                  K   	 ddl m} |j                  | j                        }|t        j                  d       y|t        j                  t        j                        j                  d      j                  dd	      | j                  j                  xs  t        j                  j                  d
d      t!        |      |d}t#        j$                  |d      }|j'                          d| d}|j(                  j+                  ||j-                  d             t        j                  d|t!        |      t!        |             y# t.        $ r }t        j1                  d|       Y d}~yd}~ww xY ww)aJ  Upload the complete session turn records to cloud storage.

        Session data and skill data live in *separate* cloud paths so they
        can be consumed independently:
          - sessions: ``{group_id}/sessions/{session_id}.jsonl``
          - skills:   ``{group_id}/skills/{name}/SKILL.md``  (handled by SkillHub)
        r   SkillHubNzq[SkillHub] session remote upload skipped: no local/OSS/S3 storage configured (skill registry may still use Nacos)Fseconds)timespecz+00:00ZUSER	anonymous)r   r_  
user_alias	num_turnsrs   r/   z	sessions/r<  rR  z4[SkillHub] session uploaded: %s (%d turns, %d bytes)Tz$[SkillHub] session upload failed: %s)	skill_hubr1  object_storage_from_configrB  r   r  r   r  r   utc	isoformatrY   sharing_user_aliasry   rz   r$   r   r:   r;   _prefix_bucket
put_objectencoder<   r   )	ru  r   rs   r1  hubsession_payloadr&   oss_keyr]  s	            r)   rD  z'SkillClawAPIServer._upload_session_data  s-    	+55dkkBC{; (%\\(,,7AA9AU]]^fhkl"kk<<c

vWb@c ZO jjuEGyEBGKK""7GNN7,CDKKFE
G	  	NNA1E	s9   E48E E4D
E E4	E1E,'E4,E11E4c                x    | j                   j                  |d      dz   | j                   |<   | j                   |   S )Nr   r   )rS  r$   r  s     r)   r  z&SkillClawAPIServer._next_user_turn_num  s=    -1-C-C-G-G
TU-VYZ-Zz*%%j11ro   c                L    | j                  |      }| j                  ||       |S r  )r  r  )ru  r   r  s      r)   #_advance_user_turn_and_maybe_uploadz6SkillClawAPIServer._advance_user_turn_and_maybe_upload  s(    00<++JFro   c           
     Z   t        dt        t        | j                  dd      xs d            }| j                  j                  r|dk  ry|dk  s||z  dk7  ryt        j                  | j                  j                  |g             }|sy| j                  | j                  ||             y)zGQueue a session snapshot when the user-visible turn cadence is reached.r   sharing_session_upload_intervalN)r  rW  r_  rB  r  r  r  rX  r$   rC  $_upload_session_snapshot_and_trigger)ru  r   r  intervalrs   s        r)   r  z1SkillClawAPIServer._maybe_upload_session_snapshot   s    q#gdkk3TVWX]\]^_{{**h!mA!9Q!>d1155j"EFtHHUZ[\ro   c                ~   K   | j                  ||       d {   }|r| j                          d {    y y 7  7 wr  )rD  _trigger_evolve)ru  r   rs   uploadeds       r)   rJ  z7SkillClawAPIServer._upload_session_snapshot_and_trigger,  s?     22:uEE&&(((  F(s   =9=;==c                4  K   t        t        | j                  dd      xs d      j                         j	                  d      }|sy dd l}t        d      D ]  }	 |j                  d      4 d {   }|j                  | d       d {   }|j                          |j                         }d d d       d {    t        j                  d	|       t        t              r9t        |j!                  d
      xs d      dkD  r| j#                          d {     y  y 7 7 7 k# 1 d {  7  sw Y   {xY w7 ## t$        $ rK}|dk  r%t'        j(                  d|dz   z         d {  7   nt        j+                  d|       Y d }~:d }~ww xY ww)Nevolve_server_urlr   r   r   r  g     r@r  z/triggerz&[SkillHub] triggered evolve server: %suploaded_skillsr   g      ?r   z5[SkillHub] evolve trigger failed after 3 attempts: %s)r!   r_  rB  r=   r  r  r  r  r  r  r:   r   r  r    r#   rW  r$   r  r<   r  r  r   )ru  r  r  r  r  r  r  r]  s           r)   rM  z"SkillClawAPIServer._trigger_evolve1  sh    '$++':B?E2FLLNUUVYZQx 	_G_ ,,U,; ) )v!'uH-=!>>D))+!YY[F) ) DcJfd+FJJ?P4Q4VUV0WZ[0[66888	_)>) ) ) ) 9 _Q;!--w{(;<<<NN#Z\]^	_s   AFE0D$1E4D*D&$D*1E<D(=AED?E F$E&D*(E*D<	0D31D<	8E	F
#F-E0.F
FFFc                  K   	 ddl m} |j                  | j                        }|j	                  | j                  j
                  |      }t        j                  d|d   |d   |j                  dd      |j                  d	d      |j                  d
d             |j                  d      r(t        j                  ddj                  |d                | j                  rX|j                  dd      dkD  s'|j                  d	d      dkD  s|j                  dd      r| j                  j                          yyy# t        $ r }t        j                  d|       Y d}~yd}~ww xY ww)u   Pull latest skills from cloud storage and reload the skill manager.

        This is a *read-only* operation — local skills are never pushed
        automatically.  Use ``skillclaw skills push`` for explicit uploads.
        r   r0  r8  zZ[SkillHub] skill pull: %d downloaded, %d unchanged, %d failed, %d deleted, %d total remote
downloadedskippedfailedr   r  total_remotefailed_namesz&[SkillHub] skill pull failed names: %sr  restored_from_backupFz [SkillHub] skill pull failed: %sN)r9  r1  from_configrB  pull_skills
skills_dirr   r  r$   r   r%   rD  reloadr<   )ru  r9  r1  rB  pull_resultr]  s         r)   r  z*SkillClawAPIServer._pull_skills_from_cloudK  s5    	B+&&t{{3C//$++*@*@Z/XKKKlL)I&!,	1-2 ~.GS^_mSnIop!!a014??9a014??#95A""))+ B "  	BNN=qAA	Bs/   E&D3D: 7E&:	E#EE&E##E&c                   fd} ||      |k  r|S |D cg c]  }|j                  d      dk(  s| }}|D cg c]  }|j                  d      dk7  s| }}d}t        |      dkD  r0|||dz   d z   }	 ||	      |k  r|dz  }n|dz  }t        |      dkD  r0|||d z   }
|r%t        j                  d| ||       ||
      |       |
S c c}w c c}w )zGDrop oldest non-system messages using a dependency-free token estimate.c                     t        | d      S )Nr  )r  )r  r  s    r)   r  z:SkillClawAPIServer._truncate_messages.<locals>._prompt_lenu  s    54RW6XYYro   r  r'  r   r   NzX[OpenClaw] context truncated: dropped %d oldest messages (%d -> %d est tokens, limit=%d))r$   r   r   r  )ru  r  r  max_prompt_tokensr  r  sys_msgsnon_sysdroppedr   r  s     `        r)   r  z%SkillClawAPIServer._truncate_messagesm  s   	Z x $55O  (E!155=H+DAEE&D!%%-8*C1DD'lQ 77Q;=#99I9%)::1qLG 'lQ GGH--KKjH%F#! ) FDs   CCCCc                   | j                   s|g fS 	 | j                   j                          | j                   j                  t        | j                  dd            }|s|g fS | j                   j                         }|D cg c]%  }t        |t              s|j                  dd      ' }}t        j                  dt        |      d	j                  |      dd
        | j                   j                  |       t!        |      }t#        |      D cg c]  \  }}|j                  d      dk(  s| }	}}|	r;|	d   }
t%        ||
   j                  dd            }i ||
   d|dz   |z   i||
<   ||fS |j'                  dd|d       ||fS # t        $ r!}t        j	                  d|       Y d}~d}~ww xY wc c}w c c}}w )a  Inject an OpenClaw-compatible skill catalog into the system message.

        Lists ALL eligible skills as an XML ``<available_skills>`` catalog
        with ``<name>``, ``<description>``, and ``<location>`` per entry.
        The model is instructed to ``read`` at most one SKILL.md when
        relevant (lazy loading), matching OpenClaw's injection behaviour.

        Returns (modified_messages, listed_skill_names).
        r  Nr  r  r  r-   r  z/[SkillManager] listing %d skills in catalog: %sr  r$  r  r'  r   r&   r   r  r*  )rD  r  r<   r   r   r  r_  rB  r  r    r#   r$   r  r   r%   r  r"   r8   r*   insert)ru  r  r]  r  r  r  r  r@   r  sys_indicesr  r  s               r)   r  z!SkillClawAPIServer._inject_skills  s    !!R<	S113 ''>>dkk+DfM ? 

 R<''668
?Ia!ZXY[_M`quuV_5aa=IIk"4C(	
 	++K8>%.x%8VTQAEE&MX<UqVVa.C/0A0A)R0PQHXx}XiF9JZ9WXHSM $$ OOAZHI$$9  	SNNNPQRR	S b Ws/   F F:F:F?0F?	F7F22F7c           	        | j                   j                  |i       }| j                  j                  |i       }t	        t        |j                                     D ]  }|j                  |      }| j                  j                  r | j                  r|9|j                         sJ|j                  |      }|j                  dd      }|5|3|j                         r#	 |j                         }|j                  |d       | j!                  | j#                  ||||              y# t        j                  t        f$ r Y Sw xY w)z2Finalize turns whose optional PRM scoring is done.Nr7  )rU  rX  rT  r$   rY  r"   r   rB  r;  rE  r  r  r  r  r  r<   rC  r@  )ru  r   rF  r  r0  rH  rG  r7  s           r)   r~  z.SkillClawAPIServer._maybe_finalize_ready_turns  s   OO..z2>	))--j"=tGLLN34 	H }}X.H{{""t#}}H-I"|T:J!h&:x}}!)!2J h-"",,	#	  ..	: s   D%%E Ec                @  K   |r|j                  dd      nd}|rP| j                  ||||j                  dg              | j                  j                  |d      dz   | j                  |<   t        j	                  d|||t        |j                  dd                   y	w)
zFinalize a turn after optional PRM scoring.

        SkillClaw acts as an external-agent proxy, so finalization keeps only
        feedback/record side effects that are consumed by the framework.
        rc  r  rd  r   r   zI[OpenClaw] finalized turn session=%s turn=%d score=%.1f response_chars=%dr-  r   N)r$   re  rW  r   r  r   )ru  r0  rG  r   r7  rc  s         r)   r@  z*SkillClawAPIServer._finalize_turn_feedback  s      1;
w,##J%PWY[A\]595O5O5S5ST^`a5bef5fD&&z2W	or23	
s   BBc           	    f  K   |d   }|j                  di g      d   }|j                  di       }d|j                  dd      xs dd}|j                  d	      r|d	   |d	<   |j                  d
d      d|j                  dt        t        j                                     |j                  dd      |j                  dd      d}i |dd|d dgi}i |ddi |j                  dd      dgi}dt        j                  |d       d dt        j                  |d       d d y w)Nrm  rM  r   r  r  r&   r   r*  r?   r2   zchat.completion.chunkrK  rJ  r   )r2   rU  rK  rJ  r   )rN  rQ  rP  rP  rR  r  Fr/   r  zdata: [DONE]

)r$   rW  rV  r:   r;   )	ru  r  r  ra  r  rQ  
chunk_basefirstr   s	            r)   r  z#SkillClawAPIServer._stream_response  sE    $Y-a0**Y+$Y1K1QrR;;|$"),"7E,++dB'-{{9c$))+.>?[["-!++lB7

 a:`yQY]+^*_`

!b6::o_eCfgh
 tzz%e<=TBBtzz%e<=TBB  s   D/D1c               Z   K   t        j                  |      2 3 d{   }| 7 
6 yw)z1Yield OpenAI Responses API-compatible SSE events.N)r  stream_response)ru  r  r  s      r)   r  z-SkillClawAPIServer._stream_responses_response  s/     -==>NO 	 	%K	Os   +)')+)+c               ^   K   t        j                  |||      2 3 d{   }| 7 
6 yw)z?Yield Anthropic-format SSE events from an internal result dict.N)r  stream_from_openai_result)ru  r  rJ  r  r  s        r)   r  z-SkillClawAPIServer._stream_anthropic_response  s6      .GGPUWab 	 	%K	bs   -+)+-+-c                b   | j                   r| j                   j                         ry | j                  j                          | j                  j                          t        j                  | j                  | j                  j                  | j                  j                  d      }t        j                  |      | _        t        j                  | j                  d      | _         | j                   j!                          t        j                  | j"                  d      j!                          y )Nr  )hostport	log_levelT)r   daemon)rp  is_alivers  r  rt  uvicornConfigrn  rB  
proxy_host
proxy_portServerro  rq  Thread_run_serverr   _print_ready_banner)ru  cfgs     r)   r   zSkillClawAPIServer.start(  s    <<DLL113!""((*nnHH''''	
 ~~c* ''t/?/?M 8 8FLLNro   c                   	 | j                   j                          | j                  j                          | j                  j                          y # | j                  j                          | j                  j                          w xY wr  )ro  r  rt  ri   rs  r  r  s    r)   r|  zSkillClawAPIServer._run_server8  sc    	&LL&&**,##% &&**,##%s   A 6Bc                j   | j                   j                  d      sy | j                  j                         ry d| j                  j
                  xs d d}dd d| j                  j                   d	| j                  j                   d
| dd d}t        j                  t         | t                y )Nr  r  zLLM (upstream)r  zF======================================================================z!
  SkillClaw proxy ready
  proxy r  u    → zC
  Claw agent has been configured to use this proxy automatically.
)rs  r  rt  is_setrB  r  rx  ry  r   r  rU  rV  )ru  backendbanners      r)   r}  z&SkillClawAPIServer._print_ready_banner?  s      %%b%1%%,,.$++22@jAC
 {{--.a0F0F/GuWI VRj	 	 	vhvhvh/0ro   c                *   | j                   d| j                   _        | j                  r6| j                  j                         r| j                  j	                  d       | j
                  j                          | j                  j                          y )NTri  r  )	ro  should_exitrp  ru  r%   rs  r  rt  ri   r  s    r)   rR  zSkillClawAPIServer.stopN  sg    <<#'+DLL$<<DLL113LLa(!""&&(ro   c                :    | j                   j                  |      S )Nr  )rs  r  )ru  	timeout_ss     r)   wait_until_readyz#SkillClawAPIServer.wait_until_readyV  s      %%i%88ro   c                     t        j                  |      } j                  j                  |       d fd}|j	                  |       |S )Nc                ^    j                   j                  |        j                  |        y r  )r[  rE  r  )r  ru  s    r)   _on_donez6SkillClawAPIServer._safe_create_task.<locals>._on_donea  s%    ""**1-q!ro   )r  asyncio.Task)r  r  r[  rj   r  )ru  coror  r  s   `   r)   rC  z$SkillClawAPIServer._safe_create_task]  sA    ""4(""4(	" 	x(ro   c                |    | j                         ry | j                         }|t        j                  d||       y y )Nz%[OpenClaw] background task failed: %sr  )r}  	exceptionr   r  )r  r)  s     r)   r  z SkillClawAPIServer._task_done_cbh  s9    >>nn?LL@#PSLT ro   )NNNN)rB  r   rD  zOptional[SkillManager]rE  zOptional[PRMScorer])r  r	   )r  r  )r  r  )r  Optional[float])r  rW  )r  rW  r  r   )rJ  r!   r  rW  r  r!   )r   r!   r  r  )r  	list[str]r  )r  r  r  r  )r  r{   r  r  )r  r!   r  r  )explicit)r   r!   r  r!   r  r  )r   r!   )r   r!   r0  rW  r  r"   r.  r!   r-  r!   r?   r"   )r   r!   r0  rW  rc  r{   rd  r"   )T)
r   r!   r0  rW  r-  r!   rP  r!   rq  r   )r   r!   r0  rW  r7  Optional[dict]r  r  )r   r!   r0  rW  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  z*tuple[str, dict[str, Any], dict[str, str]])r  r  r   r!   r  r  )r  r  r   r!   r  r  )r   r!   r  r  r  r  r   r!   r  r  r   r   r  r  )r  r  r  r  r   r!   r   r!   r  r  r   r   )r  r  )r   r!   rs   
list[dict]r  r   )r   r!   r  rW  )r   r!   r  rW  r  r  )r   r!   rs   r  r  r  )r9  zOptional[set[str]]r  r  )r  r  r`  rW  r  r  )r  r  r  ztuple[list[dict], list[str]])r0  rW  rG  r  r   r!   r7  r  )r  r  )r  r  )r  r  rJ  r!   r  set[str] | None)g      >@)r  r{   r  r   )r  r  )C__name__
__module____qualname____doc__rz  rm  r  r  r  r  r  r  r  r  r  r}  r  r  r~  r  r)  r  r  r:  ra  re  rj  rW  r{  rm  r3  r  r  r  r  r  r  r  r  r  r  r  r  r  r  rD  r  rG  r  rJ  rM  r  r  r  r~  r@  r  r  r  r   r|  r}  rR  r  rC  staticmethodr  r   ro   r)   r7  r7    s   * 04*.!Q7Q7 .	Q7
 (Q7nqf	K376<"|@W


	X 
2:
QP7lK8

),
8<
KN
_b
pt
"K(S2 &*(( ( 	(
 ( #(244 4 #	4
 
405ATf$f$ f$ 	f$
 f$ 
f$X
7b;; 	;
 
4;6:AaAa %Aa )	Aa Aa #Aa Aa 
AaF.dh .2	nn +	n
 n n #n n`_(x^t3^r** * 
	*X2

])
_4BD## 	#
 
#J+%b<

 "
 	

 #
:!. '+	  $	O &1)9	 U Uro   r7  r  )r&   r  r  tuple[str, list[dict]])rk   zlist[Any] | Noner  set[str])rs   list[dict] | Noner  r  )r  r{   )r   r  r   r   r   r!   r  r!   )r   r  r   r   r  r   )r&   r!   r  r   )r   r!   r&   r!   r?   r  r  r!   )r   r!   r  r   )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  )rB   r  r  r  )r   r#   r  ztuple[str, list[str]])r   r#   r  ztuple[str, str, str])r   r!   r   dict[str, dict[str, str]]r  dict[str, str])r   )rh   r!   r   r  r   r!   r  r  )r   r!   r  r  )r  r  r  r   )r  list[dict[str, Any]]r  r  r  rW  )r?   r  r  r  )r  r  r  r  )r  r  r  r!   )r&   r!   r  ztuple[bool, str | None])rZ  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  ztuple[list[dict], int]r  )r  r  r  r  )r  r!   r  zbytes | None)r  r  r  ztuple[int, int] | None)r  r!   r  rW  )r&   r  r  rW  )r&   r   r  rW  )r&   r   r  r!   )r  r  r  rW  )r  r  r  r!   )r  r  r  z1tuple[list[dict[str, Any]], list[dict[str, Any]]])r  r  r  r  )r  r  r  r  )r  r  r  zlist[tuple[str, int]])r  r  r  r  r  r  )r  r   r  r  )r  r  r(  r!   r  r  )r-  r!   rN  rW  r  r!   )r  r  rJ  r!   r  r  )r4  r  rJ  r!   r  r  r  r  )r  
__future__r   r  r  r  r:   loggingry   r!  r   r  rq  rV  
contextlibr   r   r   typingr   r   rv  fastapir	   r
   r   r   fastapi.responsesr   r   rB  r   rE  r   	protocolsr   r  r   r  rD  r   utilsr   	getLoggerr  r   rU  r  r  _CYANrV  r  r   r  r*   rD   compiler  r   r   r   r   rq  rC  r`  rb  rd  r   r   ry  rw  rx  r  r  r   	MULTILINEr   r   rn   ru   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r$  r/  r2  
IGNORECASEr9  __annotations__r>  rF  rH  rf  ro  ru  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r"  r*  r.  r1  r5  r7  r   ro   r)   <module>r     s   #      	  	    * '    ; ; = # ! ? = ' 			8	$	
	   4 / 76D BJJ,bii8	"**78RZZ II	   RZZ EryyQ  !& ! "$ "$ V$ *A !- !(	  $ #1!1 9 H",,W!rzzd 
- 	# 	
 	.
;&
;
; 

;:$
0	\%60>#@
- 2 BB-B B 	B>;|"% 	6.%P 	

VMM	
 	 RZZGWYdeRZZ;R]]K\ZRZZBBMMRT_`RZZH"--XZmnRZZ6F	RRZZ12<<@/R1- "B2k k k 	k\(+\**-* *Z$$-$ $T +/NNN (N 
	NbN3  	5pE/ 	7.7.."Bi+i*i i@A;G?G #'SSS  S 	SlU lUro   