
    iZ                        d dl 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mZ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mZmZ d d	lmZ d d
lmZmZmZ 	 d dlmZ d dlm Z   e         ejL                  e'      Z( ejL                  d      jS                  ejT                         d ejL                  d      _+         ejL                  d      jS                  ejT                          ed       G d d             Z, G d d      Z- ed       G d d             Z.y# e!$ r  	 d dl"mZ d dl#m$Z  n# e!$ r dZe%Z Y nw xY wY w xY w)    N)	dataclass)AnyListOptional)	BaseModel)load_dotenv)BaseMessage)MessageManager)PlannerPromptPlannerPreplanPromptPlannerPlanMessageBuilder)
Controller)SkillMetadataload_skill_contentsformat_skill_context)DDGS)DDGSException)DuckDuckGoSearchExceptionprimpFddgsT)frozenc                   F    e Zd ZU eed<   ee   ed<   ee   ed<   dZeed<   y)PreplanDecision
use_searchqueriesselected_skills raw_textN)__name__
__module____qualname__bool__annotations__r   strr        K/Users/mibo/.openclaw/workspace/data/TuriX-CUA/src/agent/planner_service.pyr   r   '   s&    #Y#YHcr&   r   c                      e Zd Z	 	 	 	 	 	 	 	 	 	 d.dededededee   dee   ded	eee      d
efdZ	dedefdZ
dedeee   ef   fdZdeddfdZdee   dededdfdZdee   fdZdedee   fdZdedefdZdee   dee   fdZdedee   fdZdee   dee   fdZdedefd Zdefd!Zd"edeeeee   f      fd#Zd/d"ed$edee   fd%Zd&ee   defd'Zd(edefd)Zdefd*Z defd+Z!d0d,Z"d0d-Z#y)1PlannerNtaskmax_input_tokensr   skill_catalogsave_planner_conversation_path'save_planner_conversation_path_encoding
use_skillsavailable_skillsskills_max_charsc                    || _         t               | _        || _        || _        g | _        d | _        |xs || _        || _        || _	        |	| _
        |
rt        |
      ng | _        t        d|xs d      | _        d | _        d | _        || _        |xs d| _        t'        | j                   | j                  | j                  j(                  j+                         t,        | j                        | _        y )Nr   utf-8)llmr*   action_descriptionssystem_prompt_classr+   )planner_llmr   
controllerr*   r+   	plan_list_search_contextpreplan_llmr   r,   r/   listr0   maxr1   _preplan_decision_skill_contextr-   r.   r
   registryget_prompt_descriptionr   message_manager)selfr7   r*   r+   
search_llmr   r,   r-   r.   r/   r0   r1   r;   s                r'   __init__zPlanner.__init__0   s     '$,	 0.2&4*$*$:J%5 6PR #A'7'<1 =<@-1.L+7^7ibi4-   $ 8 8 O O Q -!22 
r&   textreturnc                    |xs dj                         }|s|S t        j                  dd|t        j                        j                         }t        j                  dd|      j                         }|j	                  d      r|j                  d      r|S |j                  d      }|j                  d      }|dk7  r|dk7  r||kD  r|||dz    S |S )	Nr   z^```(?:json)?)flagsz```${}   )stripresub
IGNORECASE
startswithendswithfindrfind)rC   rF   cleanedstartends        r'   _coerce_json_textzPlanner._coerce_json_textW   s    :2$$&N&&)2wbmmLRRT&&"g.446c"w'7'7'<NS!mmC B;3"9u5q))r&   c                    | j                  |      }|sd |fS 	 t        j                  |      }t	        |t
              r[d|v rWt	        |d   t              rD|d   }| j                  |      }	 t        j                  |      }t	        |t
              r||xs |fS t	        |t
              r||fS d |fS # t        $ r d |fcY S w xY w# t        $ r ||fcY S w xY w)Ncontent)rY   jsonloads	Exception
isinstancedictr$   )rC   rF   rV   payload
inner_textinner_cleanedinner_payloads          r'   _parse_json_payloadzPlanner._parse_json_payloade   s    ((.:	jj)G gt$g)=*WU^M_adBe +J 22:>M( $

= 9 -.$m&AzAAgt$G##W}  	:	  (''(s#   B1 ,C 1C CCCresponsePlannerResultc                    t        |t              r]t        |dd      }t        |t              r|}n	 t	        j
                  |d      }| j                  |      \  }}t        |xs ||      S t        |t              r6|j                  d      }t	        j
                  |d      }t        ||      S t        |dd      }t        |t              r|}n	 t	        j
                  |d      }| j                  |      \  }}t        |xs ||      S # t        $ r t        |      }Y w xY w# t        $ r t        |      }Y Uw xY w)Nr[   r   F)ensure_ascii)r   ra   T)exclude_none)r_   r	   getattrr$   r\   dumpsr^   re   rg   r   
model_dump)rC   rf   raw_contentr   ra   normalized_texts         r'   _extract_planner_payloadz Planner._extract_planner_payloadz   s1   h,!(Ir:K+s+&0#zz+EJH (,'?'?'I$G_ /*EXwWWh	*))t)<Gzz'>H (GDDh	26k3'"H,::kF $(#;#;H#E o&A7SS) ! 0";/H0   ,{+,s#   D D+ D('D(+EEmessagesresponse_textlabelc           	         | j                   sy | j                    d| d}t        j                  j                  |      r4t        j                  t        j                  j                  |      d      nd  t        |d| j                        5 }|D ]$  }|j                  d|j                  j                   dd d       |j                  }t        |t              r|D ]  }t        |t              s|j                  d	      d
k(  rI|j                  d      xs |j                  d
d      }	|j                  d|	j                          d       q|j                  d	      dk(  s|d   d   }
|j                  d|
d d  d        n|j                  t!        |       d       |j                  d       ' |j                  d       |j                  t!        |      dz          |j                  d       d d d        y # 1 sw Y   y xY w)N	_planner_z.txtT)exist_okw)encoding
z(----------------------------------------typerF   r[   r   z[Text Content]
z

	image_urlurlz[Image URL]
d   z...

z>
============================================================
z	RESPONSE
)r-   ospathdirnamemakedirsopenr.   write	__class__r   r[   r_   r<   r`   getrN   r$   )rC   rq   rr   rs   	file_namefmessager[   itemtxtr{   s              r'   _save_planner_conversationz"Planner._save_planner_conversation   s    22::;9UG4P	BD''//R[B\BGGOOI.>bf)S4+W+WX\]#"W..7786("EF!//gt, '%dD1#xx/69&*hhy&9&QTXXfb=Q !*:399;-t(L M!%&!1[!@,0,=e,D	 !-	$37H(P Q !( GGs7|nD12./ $ GGL!GGC&-.GG*+% YXXs   A$H 2A1H $BH  H	c                    K   | j                   sg S | j                          d{   }|j                   sg S |j                  S 7 w)zN
        Use the cached preplan decision to determine search queries.
        N)r   _ensure_preplan_decisionr   )rC   decisions     r'   _decide_search_querieszPlanner._decide_search_queries   sD      I6688""I 9s   "AAAc                 H   g }|xs dj                         D ]J  }t        j                  dd|      j                         j                  d      }|s:|j	                  |       L |sg S t               }g }|D ])  }||vs|j	                  |       |j                  |       + |S )Nr   z^[\\s\\-\\*\\d\\)\\.]+")
splitlinesrO   rP   rN   appendsetadd)rC   rF   linesrawrV   seendedupedqs           r'   _parse_query_lineszPlanner._parse_query_lines   s    JB**,Cff6C@FFHNNsSGW% - IuA}q!  r&   namec                 h    t        j                  dd|j                         j                               S )Nz\s+-)rO   rP   rN   lower)rC   r   s     r'   _normalize_skill_namezPlanner._normalize_skill_name   s$    vvfc4::<#5#5#788r&   itemsc                 z    t               }g }|D ])  }||vs|j                  |       |j                  |       + |S N)r   r   r   )rC   r   r   r   r   s        r'   _dedupe_listzPlanner._dedupe_list   s>    uD4t$  r&   c                 t    | j                  |      }|sy 	 t        j                  |      S # t        $ r Y y w xY wr   )rY   r\   r]   r^   )rC   rF   rV   s      r'   _safe_json_loadszPlanner._safe_json_loads   s>    ((.	::g&& 		s   + 	77namesc                 T   |r| j                   sg S | j                   D ci c](  }| j                  |j                        |j                  * }}g }|D ]N  }t        |t              s| j                  |      }|j                  |      }|s9||vs>|j                  |       P |S c c}w r   )r0   r   r   r_   r$   r   r   )rC   r   skilllookupselectedr   
normalized	canonicals           r'   _canonicalize_selected_skillsz%Planner._canonicalize_selected_skills   s    D11IRVRgRghRg$,,UZZ8%**DRghCc3'33C8J

:.IYh6	*   is   -B%c                    | j                  |      }d }g }g }t        |t              r|j                  d      }|j                  d      xs |j                  d      xs g }t        |t              r|g}n0t        |t
              r |D cg c]  }t        |t              s| }}|j                  d      xs |j                  d      xs g }t        |t              r|g}nst        |t
              rc|D 	cg c]  }	t        |	t              s|	 }}	nBt        |t
              r!|D cg c]  }t        |t              s| }}n| j                  |      }|D cg c]4  }t        |t              s|j                         s%|j                         6 }
}| j                  |
      }
d}| j                  rt        |t              r|}n|
rd}|
sd}|sg }
g }| j                  rR|rP|D 	cg c]4  }	t        |	t              s|	j                         s%|	j                         6 }}	| j                  |      }t        ||
||xs d	      S c c}w c c}	w c c}w c c}w c c}	w )
Nr   r   search_queriesr   skillsFTr   r   r   r   r   )r   r_   r`   r   r$   r<   r   rN   r   r   r"   r/   r   r   )rC   rF   dataraw_use_searchraw_queriesraw_selectedqueries_valuer   skills_valuesr   r   r   rV   s                 r'   _parse_preplan_responsezPlanner._parse_preplan_response   s   $$T*!#"$dD!!XXl3N HHY/S488<L3MSQSM--,oM40*7N-Q:a;Mq-N88$56R$((8:LRPRL,, ,~L$/+7N<a:a;M<Nd#&*AdjC.@1dKA11$7K&1VkZ35GAGGI1779kV##G,
??.$/+
!
JG%'??|*6[,Q*Q:LQRQXQXQZqwwy,G["@@IO!+ZR	
 	
? O  OA W  \sH   	II5I	I	&I<II.I?I%I;IIc                   K   | j                   | j                   S t        dg g d      }| j                  s| j                  s|| _         | j                   S | j                  s(t
        j                  d       || _         | j                   S 	 t        | j                  | j                  | j                  | j                        }|j                         }| j                  j                  |       d {   }t        |dd      xs d}| j                  t        |t              r|n
t        |            }| j!                  |t        |t              r|n
t        |      d       |j                  r-|j&                  r!t
        j                  d|j&                         n!| j                  rt
        j                  d       | j                  rQ|j(                  r0t
        j                  ddj+                  |j(                               nt
        j                  d       || _         |S 7 0# t"        $ r$}t
        j%                  d|d	
       |}Y d }~d }~ww xY ww)NFr   r   zDPlanner preplan LLM unavailable; skipping search/skill preselection.)r*   r   r/   r,   r[   preplanz3Preplan decision failed; skipping search/skills: %sTexc_infozPlanner preplan queries: %sz/Planner preplan: search disabled or no queries.z#Planner preplan selected skills: %sz, z#Planner preplan selected no skills.)r>   r   r   r/   r;   loggerinfor   r*   r,   get_messagesainvokerk   r   r_   r$   r   r^   debugr   r   join)rC   defaultprompt_builderrq   resprF   r   excs           r'   r   z Planner._ensure_preplan_decision&  s    !!-)))!UBPR]_`4??%,D")))KK^_%,D")))	1YY????"00	N &224H))11(;;D4B/52D33JtS<QDWZ[_W`aH++Hjs>SdY\]aYbdmn
 8#3#3KK5x7G7GH__KKIJ??''A499XMeMeCfgAB!)) <  	LLNPS^bLcH	sE   BI
A%H" /H0A/H" C IH" "	I+I
I
IIqueryc                    |sg S |j                         }g }|j                  |df       t        |      dkD  r|j                  |dd df       |j                  |df       t        |      dkD  r|j                  |dd df       |S )zd
        Build a small set of query/backend combinations to increase the chance of results.
        
duckduckgo   Nauto)rN   r   len)rC   r   clean_queryvariantss       r'   _build_query_variantszPlanner._build_query_variantsR  s     Ikkm46 	l34 {c!OO[#.=> 	f-.{c!OO[#.78r&   max_resultsc                 $   K   sg S t         t        j                  d       g S t        j                         } fd}	 |j                  d|       d{   S 7 # t        $ r%}t        j                  d|d       g cY d}~S d}~ww xY ww)zj
        Fetch DuckDuckGo search results in a background thread to avoid blocking the event loop.
        NzAduckduckgo_search not installed; skipping planner search context.c            
         	 t               5 } j                        D ]j  \  }}	 t        | j                  ||            }|r.t        j                  d||t        |             |c cd d d        S t        j                  d||       l g cd d d        S # t        $ r!}t        j                  d||       Y d }~d }~wt        $ r#}t        j                  d||d       Y d }~d }~ww xY w# 1 sw Y   y xY w# t        $ r"}t        j                  d|       g cY d }~S d }~wt        $ r$}t        j                  d	|d       g cY d }~S d }~ww xY w)
N)backendr   z9Planner search success (backend=%s, query=%r): %d resultsz+Planner search empty (backend=%s, query=%r)z6DuckDuckGo search (%s backend) returned no results: %sz(DuckDuckGo search (%s backend) error: %sTr   z)DuckDuckGo search returned no results: %sz&DuckDuckGo search unexpected error: %s)
r   r   r<   rF   r   r   r   r   r^   r   )r   r   r   resultsr   r   r   rC   s        r'   _searchz.Planner._fetch_search_results.<locals>._searchv  s>   Vt&*&@&@&G
7	r&*499QU`9+a&bG& &,giprsux  zA  vB  !C'. V #KK(UW^`ab 'H  V  - p"KK(`biknoo( r"LL)SU\^alpLqqr V ! H#N	 EsUYZ	s   
C: C.AB'C.)	C: 3B
C.	C: 	C+ B<7C.<C+C&!C.&C++C..C73C: 7C: :	ED E E,EEEz)DuckDuckGo search failed for query %s: %sTr   )r   r   r   asyncioget_running_looprun_in_executorr^   warning)rC   r   r   loopr   r   s   ```   r'   _fetch_search_resultszPlanner._fetch_search_resultsj  s      I<LL\]I'')	,	--dG<<<< 	NNFs]aNbI	sG   =BA AA BA 	B(BBBBBr   c                    g }t        |d      D ]  \  }}|j                  d      xs dj                         }|j                  d      xs dj                         j                  dd      }|j                  d	      xs dj                         }t	        |      d
kD  r|dd dz   }| d| }|r|d| z  }|r	|d| dz  }|j                  |        dj                  |      S )zM
        Convert search results into a compact, readable text block.
        rM   )rW   titlezNo titlebodyr   ry    href   N   z...z. u    — z
 (source: ))	enumerater   rN   replacer   r   r   )	rC   r   r   idxr   r   snippetr   readables	            r'   _format_search_resultszPlanner._format_search_results  s     "7!4ICXXg&4*;;=Exx'-2446>>tSIGHHV$*113D7|c!!$3-%/b(HeG9--ja00LL" 5 yyr&   linec                 r    d|v r#|j                  dd      d   j                         }|j                  d      S )z?
        Remove source links/URLs from a summary line.
        z(source:rM   r   u    -—·.)splitrstrip)rC   r   s     r'   _strip_sourcezPlanner._strip_source  s9     ::j!,Q/668D{{:&&r&   c                   K   | j                   | j                   S | j                  sd| _         | j                   S d| _         | j                          d{   }|s!t        j	                  d       | j                   S t        j	                  d|       g }d}d}|D ]  }| j                  |d       d{   }|r| j                  |      }t        j	                  d||       t        |j                               D ]F  \  }}	|d	k\  r n<| j                  |	      }
|j                  |dd
  d|
        t        |      |k\  sF n t        |      |k\  st        |      |d	z  k\  s nt        j	                  d|        |rDddj                  |      z   | _         t        j	                  dt        |             | j                   S t        j	                  d       | j                   S 7 7 5w)z_
        Run DuckDuckGo search once per planner instance and cache a readable summary.
        Nr   z,Planner search skipped; no queries provided.z#Planner will try search queries: %s      )r   z'Planner search results for query=%r:
%s   2   z... -> z/Planner search produced no results for query=%rz(Concise search summary (links removed):
ry   z8Planner aggregated concise search summary from %d lines.zOPlanner search produced no usable results; proceeding without external context.)r:   r   r   r   r   r   r   r   r   r   r   r   r   )rC   r   summary_linesmax_queries_to_usemax_summary_linesr   r   	formattedr   r   
clean_lines              r'   _get_search_contextzPlanner._get_search_context  s     +'''#%D '''!3355KKFG'''97C#%A 66qa6HHG 77@	F9U!*9+?+?+A!BICax!%!3!3D!9J!((AcrF87:,)GH=)->> "C }%)::c->PTfijTj>jMqQ! $ #NQUQZQZ[hQi#iD KKRTWXeTfg ### KKij###E 6 Is3   AG+G%AG+3G(4BG+"G+$BG+(G+c                   K   | j                   | j                   S d| _         | j                  s| j                   S | j                          d{   }|j                  s| j                   S | j                  s!t
        j                  d       | j                   S t        | j                  |j                  | j                  xs d      }|s| j                   S t        |      | _         | j                   S 7 w)zm
        Load selected skill contents once per planner instance and cache a formatted context block.
        Nr   z/Skills enabled but no skills available to load.)	max_chars)
r?   r/   r   r   r0   r   r   r   r1   r   )rC   r   skill_contentss      r'   _get_skill_contextzPlanner._get_skill_context  s      *&&& &&&6688''&&&$$KKIJ&&&,!!$$++3t

 &&&2>B"""# 9s   AC3C1B$C3c                 L  K   | j                   sy t               }t        |j                  j	                         | j
                  | j                        }| j                          d {   }| j                          d {   }| j                          d {   }|r|j                  ng }|j                  | j                  |||      }| j                   j                  |       d {   }| j                  |      }	t        |	j                   t"              r||	j                   d<   |	j$                  xs dj'                         }
|
j)                         }d|v r t+        j,                  d       t/        d      | j1                  ||	j$                  d       |	S 7 27 7 7 w)	Nr,   r/   )r*   search_contextr   skill_contextr   r   REFUSE TO MAKE PLANPlanner refused. Aborting.rM   initial)r7   r   r   r@   rA   r,   r/   r   r   r   r   build_initial_messagesr*   r   rp   r_   ra   r`   r   rN   upperloggingerror
SystemExitr   )rC   r8   r   r   r  r  r   rq   rf   result
reply_text
reply_norms               r'   	edit_taskzPlanner.edit_task  sh    \
2668,,

 5577#7799"55775<'11"!88)+'	 9 
 ))11(;;..x8fnnd+0?FNN,-oo+224
%%'
 J.MM67Q-''&//9M) 897 <sJ   A%F$'F(F$?F F$FAF$)F"*B0F$F$F$"F$c           	      f  K   | j                   sy t               }t        |j                  j	                         | j
                  | j                        }| j                          d {   }| j                          d {   }| j                          d {   }|r|j                  ng }|j                  | j                  ||| j                  |||      }	| j                   j                  |	       d {   }
| j                  |
      }t!        |j"                  t$              r||j"                  d<   |j&                  xs dj)                         }|j+                         }d|v r t-        j.                  d       t1        d      | j3                  |	|j&                  d       |S 7 ?7 *7 7 w)	Nr  )r*   info_memorytask_summaryr9   r  r   r  r   r   r  r  rM   continue)r7   r   r   r@   rA   r,   r/   r   r   r   r   build_continue_messagesr*   r9   r   rp   r_   ra   r`   r   rN   r  r	  r
  r  r   )rC   r  r  r8   r   r   r  r  r   rq   rf   r  r  r  s                 r'   continue_edit_taskzPlanner.continue_edit_task  su    \
2668,,

 5577#7799"55775<'11"!99#%nn)+' : 
 ))11(;;..x8fnnd+0?FNN,-oo+224
%%'
 J.MM67Q-''&//:N/ 897 <sJ   A%F1'F&(F1?F) F1F,AF16F/7B0F1)F1,F1/F1)
i }  NTr   Nr3   FNi  N)r   )rG   rg   )$r   r    r!   r$   intr"   r   r   r   rE   rY   tupler`   re   r   rp   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)   /   sH    */ $(&(AEJQ$)CG)-!%
%
 $'%

 "%
 !$%
 2:#%
 ;C3-%
 "%
 $,D,?#@%
 $'%
Nc c  htnc6I0J *T T T<,{#, , 	,
 
,<	 d3i 	 s tCy  9# 9# 9$s) S	 S Xc] 49 c 0
C 0
O 0
d* *X3 4c8C=>P8Q3R 0& &3 &tTXz &P d4j  S  *'# '# '-$3 -$^## #<> r&   r)   c                   (    e Zd ZU eed<   ee   ed<   y)rg   r   ra   N)r   r    r!   r$   r#   r   r`   r%   r&   r'   rg   rg   >  s    Md^r&   rg   )/r   dataclassesr   r\   r	  r~   rO   typingr   r   r   pydanticr   dotenvr   langchain_core.messagesr	   !src.agent.message_manager.servicer
   src.agent.promptsr   r   r   src.controller.servicer   src.utils.skillsr   r   r   r   r   ddgs.exceptionsr   ImportErrorduckduckgo_searchduckduckgo_search.exceptionsr   r^   	getLoggerr   r   setLevelWARNING	propagater   r)   rg   r%   r&   r'   <module>r*     s/    !   	 	 & &   / < \ \ - U U
"- 			8	$   '  # #GOO 4',   '  $   &  " "7?? 3 $  L L^ $  Q  ""*[ "!"	"s6   D! !E'D43E4	E =E?E  EE