
    iPl                     ~   d Z ddlZddlZddlmZ ddlmZmZmZm	Z	m
Z
 	 ddlmZ eeneZddlmZmZmZmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZmZ ddl m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z( ddl)m*Z* ddl+m,Z, ddl-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3  e*e4      Z5d2de6de7de6fdZ8de6de6de6fdZ9 G d d      Z: G d d      Z; G d d      Z< G d d      Z= G d  d!      Z>d"e6de
e	e6e6e6f      fd#Z?d$e6de@fd%ZAd&ZBd'ee   dee   fd(ZC G d) d*      ZD G d+ d,      ZE G d- d.      ZFd/ede6fd0ZGd/ede6fd1ZHy# e$ r dZY w xY w)3z~
Deep Analysis Agent nodes and routers.
Class-based nodes; executor logic split into QueryBatchExecutor for easier debugging.
    N)ThreadPoolExecutor)ListDictAnyTupleOptional)ContextThreadPoolExecutor)BaseMessageHumanMessage	AIMessageToolMessageSystemMessage   )DeepAnalysisState)REASONER_QUERIES_MAX)ReasonerQueriesBatch)execute_sql_query)serialize_sql_resultnormalize_markdown_output)INVESTIGATION_PROMPTREPORTING_PROMPTREASONER_SYSTEM_PROMPTREASONER_PROMPTQUERY_FIXER_PROMPTFINALIZER_PROMPT FINDING_FROM_QUERY_RESULT_PROMPT"DECIDE_CONTINUE_OR_FINALIZE_PROMPT)
get_logger)get_llm)cap_result_contentMAX_CHARS_PER_RESULTcount_tokensTARGET_TOKENSinvoke_with_retryrewrite_query_reduced_windowprompttemperaturereturnc                     t        |      j                  t        |       g      }|rt        |dd      nd}|xs dj	                         S )zGSingle HumanMessage prompt; returns content string. Never returns None.r'   contentr,   N )r   invoker   getattrstrip)r&   r'   responser,   s       B/var/www/html/userprofiledev.eatanceapp.com/deep_analysis/nodes.py_invoke_llmr3   4   sD    ;/66V8T7UVH4<gh	40$GMr  ""    
hypothesisqueryc                 H   	 t        j                  d|i      }t        |t              r#|j                  d      rd| xs d d| d|d    S t        |t              r@|r>t        |d   t              r+|d   j                  d	      r|d   d	   }d| xs d d| d
| S t        |t              r#|j                  d	      rd| xs d d| d|d	    S |rt        |      nd}t        |t        d      }d| xs d d| d| S # t        $ r}d| xs d d| d| cY d}~S d}~ww xY w)z
    Runs the given SQL query and returns full ToolMessage content string
    (hypothesis + query + answer). Used when retrying after context overflow
    with a reduced-date-window query.
    r6   hypothesis: r-   
query: 
answer: SYSTEM_ERROR: N
_sys_errorr   error
answer: SQL_ERROR: No rows.zretry reduced window	max_charsquery_label	
answer: )
r   r.   	Exception
isinstancedictgetlistr   r    r!   )r5   r6   resulteerr
raw_answeranswer_texts          r2   !_run_query_and_build_tool_contentrM   >   sg   \"))7E*:; &$FJJ|$<j.B/y?WX^_kXlWmnn&$Fz&)T/JvVWy}}]dOeQi j.B/y?TUXTYZZ&$FJJw$7j.B/y?WX^_fXgWhii17%f-ZJ$Z;O]stK**+9UG:k]SS  \j.B/y?WXYWZ[[\s   C? ?	D!DD!D!c                       e Zd ZdZdedeeeef      fdZdeeeef      dee	   fdZ
de	defdZd	ed
ede	deee	f   fdZdeeeee	f   deeee	eee   f   fdZd	ed
ede	dededeeef   fdZdedeee	f   fdZy)QueryBatchExecutorz
    Runs a batch of SQL queries in parallel and builds tool messages + findings.
    Each step is a separate method for easier debugging and testing.
    stater(   c                     |j                  d      xs g }|j                  d      W|j                  d      F|d   }t        |      }d|cxk  rt        |      k  r"n |S ||   j                  dd      |d   d||<   |S )z]Resolve batch from state; apply fixed_query when set (reserved for a future QueryFixer loop).last_queries_batchfixed_queryfailed_query_indexr   r5   r-   r5   r6   )rF   rG   len)selfrP   batchidxs       r2   resolve_batchz QueryBatchExecutor.resolve_batch^   s    		./5299]#/EII>R4S4_,-CKEC$#e*$
  #(*..r"B"=1c
 r4   rX   c                    |D cg c]_  }|j                  d      xs dj                         s'|j                  d      xs dj                         |j                  d      xs dda }}dt        dt        fd}t	        t        t        |      t                    5 }t        |j                  ||            cd	d	d	       S c c}w # 1 sw Y   y	xY w)
zRRun all queries in parallel; returns list of results in same order as valid items.r6   r-   r5   )r6   r5   payloadr(   c                 ~    	 t        j                  d| d   i      S # t        $ r}dt        |      icY d }~S d }~ww xY w)Nr6   r;   )r   r.   rC   str)r\   rI   s     r2   run_onez8QueryBatchExecutor.run_queries_parallel.<locals>.run_oneo   sA    .(//'':J0KLL .$c!f--.s    	<7<<max_workersN)
rF   r0   rE   r   	_ExecutorminrV   r   rG   map)rW   rX   itemvalidr_   exs         r2   run_queries_parallelz'QueryBatchExecutor.run_queries_parallelk   s    x}  dx}pt  CG  CK  CK  LS  CT  CZ  XZ  Ba  Ba  BcDHHW-3::<TXXVbMcMigikx}  d	.T 	.c 	. 3s5z3G#HIRw./ JI d JIs   (C;CCCrH   c                    t        |t              r|j                  d      ryt        |t              r6t	        |      dkD  r(t        |d   t              r|d   j                  d      ryt        |t              r|j                  d      ryy)z@One of: 'success', 'sys_exception', 'sql_error', 'system_error'.r;   sys_exceptionr   r<   	sql_errorsystem_errorsuccess)rD   rE   rF   rG   rV   )rW   rH   s     r2   classify_resultz"QueryBatchExecutor.classify_resultx   so    fd#

<(@"fd#FaJvayRV<W\bcd\e\i\ijq\rfd#

7(;!r4   r6   r5   c                     |rt        |t              s||fS t        |      }t        |      t        k  r||fS t
        j                  dt        |             t        |      }|j                         |j                         k(  r||fS 	 t        j                  d|i      }| j                  |      dk7  r||fS t
        j                  d       ||fS # t        $ r$}t
        j                  d|       ||fcY d}~S d}~ww xY w)a1  
        Backup path: if result is too large (would blow context), ask LLM for reduced-date-window
        query, re-run, and return smaller (query_to_use, result_to_use). Otherwise return (query, result).
        Rare in practice; keeps context safe when a query returns unexpectedly large data.
        zVExecutor: result size %s chars exceeds threshold; re-running with reduced date window.r6   zBExecutor: reduced-window re-run failed: %s; using original result.Nrm   z5Executor: using reduced-window result for this query.)rD   rG   r   rV   r!   loggerwarningr%   r0   r   r.   rC   rn   info)rW   r6   r5   rH   rK   	new_query
new_resultrI   s           r2   _maybe_reduce_result_on_sizez/QueryBatchExecutor._maybe_reduce_result_on_size   s     Z56?")&1
z?226?"d
O	
 17	??-6?"	#*117I2FGJ 
+y86?"KL:&&  	#NN_abc6?"	#s   C 	C=C82C=8C=re   c                 ^   |\  }}}}| j                  |      }|dk(  r|||||j                  d      xs dfS |dk(  r6|r+t        |d   t              r|d   j                  d      xs dnd}|||||fS |dk(  r|||||j                  d      xs dfS | j	                  |||      \  }}	|||	dd	fS )
uw   Run query → check size → maybe reduce. Returns (index, query_used, result_used, kind, error_msg). Used in parallel.rj   r;   r-   rk   r   r<   rl   rm   N)rn   rF   rD   rE   ru   )
rW   re   ir6   r5   rH   kindrJ   
query_usedresult_useds
             r2   _process_one_resultz&QueryBatchExecutor._process_one_result   s     (,$5*f##F+?"ufdVZZ-E-KMM;5;
6RS9VZ@[6!9==)/RbdCufdC00>!ufdVZZ-@-FBHH"&"C"CE:W]"^
K:{It<<r4   index	batch_lenc                    d| }| xs t        |      dk(  }|rt        |      nd}|sdnt        |t        d|dz    d|       }	d	|xs d
 d| d|	 }
t        |
      t        dz   kD  rt        |
t        d|dz    d|       }
t	        |
|      }|xs d|dz    dd }|	dd j                  dd      j                         }d| d| t        |	      dkD  rdnd
z   }||fS )zDBuild ToolMessage and short finding snippet for a successful result.batch_r   r>   zNo data rows.zquery r    of r?   r8   r-   r9   rB     r,   tool_call_idzQuery N2   P   
 [z] ...)rV   r   r    r!   r   replacer0   )rW   r6   r5   rH   r|   r}   r   is_emptyrK   rL   r,   msghypansfindings                  r2   build_success_messagez(QueryBatchExecutor.build_success_message   s;     w':1V!15;)&1
-3o9K* 4	{;:

 !!1r 2)E7*[MZw<.44(<P`fglopgpfqquv  vA  _B  CG'E/veAgY/"5#2&&tS1779cU"SE"s;/?"/De"MG|r4   c                    | j                  |      }|sddiS |d   dz   }|j                  dd      }t        j                  dt	        |       d| d	| d
       t        |      D cg c]a  \  }}|j                  d      xs dj                         r9||j                  d      xs dj                         |j                  d      xs dfc }}}|sddiS | j                  |      }t        t	        |            D 	cg c]  }	||	   d   ||	   d   ||	   d   ||	   f }
}	t        t        t	        |
      t                    5 }t        |j                  | j                  |
            }ddd       j                  d        g }g }g }d}d}|D ]3  \  }}}}}||   }|j                  d      xs d}d| }t        j                  d       t        j                  d|       t        j                  d|       t        j                  d       |dk(  rvt        j!                  d| d|        d| }|}|j#                  t%        d| d| d| |             |j#                  d |dz    d!|dd"         |j#                  d#       |d$k(  rt        j!                  d%|dz   ||r|dd& nd'|dd& t	        |      d&kD  rd(ndz          d)| d*| }|}|j#                  t%        d| d| d+| |             |j#                  d |dz    d!|dd"         |j#                  d#       |d,k(  r3|j#                  t%        d| |             |d| ddd|d   dz   d-c S | j'                  ||||t	        |            \  }}|j#                  |       |j#                  |       |j#                  | xs t	        |      dk(         6 t	        |      dkD  xr$ t	        |      t	        |      k(  xr t)        |      }|r"t        j                  d.t	        |       d/       |}|j                  d0      rt+        |d0   |z         nd}|t,        d1z  kD  }||dddddd|d   dz   ||d2S c c}}w c c}	w # 1 sw Y   	xY w)3zUFull executor step: resolve batch, run queries, process results, return state update.
last_errorz$SYSTEM_ERROR: No queries to execute.tool_call_countr   	max_turns   zExecutor running batch of z queries (turn r   )r6   r-   r5   r   r`   Nc                     | d   S )Nr    )xs    r2   <lambda>z(QueryBatchExecutor.run.<locals>.<lambda>   s    QqTr4   )keyr   z-----------------zHYPOTHESIS : %szLLM query : %sz-----------------------rj   z"Executor system failure for query z: zSYSTEM_ERROR: r8   r9   r:   r   z[Query z	 failed] d   Trk   zGSQL Error (query %s): %s | HYPOTHESIS: %s | QUERY (first 200 chars): %s   z(none)r   zSQL_ERROR: z
 | QUERY: r=   rl   )messagesr   rR   rT   rS   r   zAll zU queries in this turn returned no data; will ask LLM whether to continue or finalize.r   g?)r   findings
last_queryrR   r   rT   rS   retry_countr   context_near_limitbatch_all_empty)rZ   rF   rp   rr   rV   	enumerater0   rh   rangerb   rc   r   rG   rd   r{   sortr<   appendr   r   allr"   r#   )rW   rP   rX   turn_displayr   rw   re   valid_itemsresults_listjitems_for_processrg   	processedtool_messagesfindings_this_turnresults_emptylast_error_outfailed_query_index_outry   rz   rx   	error_msgr5   r   r   r   r   new_messagestotal_tokensr   s                                 r2   runzQueryBatchExecutor.run   s1   ""5) "HII./!3IIk1-	0UOL>Y]^g]hhijk %U+
+4!'R..0 '"(b//1DHH\4J4PbR+ 	 

  "HII007 3{+,
, ^AAq 1;q>!3DlSToV, 	 
 3s+<'=?S#TUY[RVVD$<$<>OPQI V>*+-(*$&(,04=F9Q
Ky8D((<06BJ#A3<LKK+,KK):6KK(*5KK12&A!BykRS#1)!=)*&$$*:,i
|Kcdmcno%1	 #))GAaC5	)DS/AR*ST$$T*{"]E(2Jt$t$Z31FBO $/ykJ<!P)*&$$".zl)J<Odendo p%1 #))GAaC5	)DS/AR*ST$$T*~%$$.(DS_` !.$29+">*.*.#'',->'?!'C   55JQE
LC   %%%g.  [!IC4D4IJ >GB k*Q.p3}3E[IY3Yp^abo^pKK$s5zl*  A  B$INS]I^|E*$5$DEde)MC,??
 %*"&"&$%67!;"4.
 	
E

 VUs   (A&Q'>"Q-&Q22Q<N)__name__
__module____qualname____doc__r   r   r   r^   rZ   r   rh   rn   r   ru   intr   r{   r
   r   r   r   r4   r2   rO   rO   X   s-   
#4 d38n9M 0$tCH~*> 049 0c c '# '3 'PS 'X]^acf^fXg '8=#sC,-=	sCc8C=0	1=   	
   
{C	 6w
* w
tCH~ w
r4   rO   c                   6    e Zd ZdZdefdZdedeeef   fdZ	y)ReasonerNodeu~   Generates 3–5 investigative SQL queries (single turn) using structured LLM output. Does not run queries; Executor does that.analytical_schemac                     || _         y Nr   rW   r   s     r2   __init__zReasonerNode.__init__H  
    !2r4   rP   r(   c           	      |   t         j                  dt         d|d           t        j                  |d   | j
                  |d   t              }t        j                  d      rdnd }t        d|	      j                  t              }t        |
      t        t        j                  |d         
      g}t        ||      }|j                   D cg c]  }|j"                  |j$                  d }}t         j                  dt'        |       d       t)        dt'        |       d
      gd |d d d dddS c c}w )Nu   Reasoner generating 3–z queries for User user_idprofile_json)r   r   r   investigation_promptOPENAI_API_KEYopenaig?)r'   providerr+   )r   rU   zReasoner produced z	 queries.z
Generated z investigative queries.r   F)r   r   rR   r   rT   rS   r   r   )rp   rr   r   r   formatr   r   osgetenvr   with_structured_outputr   r   r   r   r$   queriesr5   r6   rV   r   )	rW   rP   system_promptreasoner_providermodelr   rX   qqueries_lists	            r2   __call__zReasonerNode.__call__K  s>   ./C.DDVW\]fWgVhij.55)$"44~.!5	
 )+		2B(CHC2CD[[\pq-0!7!7i@P!QR
 '8x&HPUP]P]^P]1q||aggFP]^(\):(;9EF #ZL8I7JJa+bcd"."&"'	
 		
 _s    D9N
r   r   r   r   r^   r   r   r   r   r   r   r4   r2   r   r   E  s0     I3# 3
/ 
DcN 
r4   r   c                   0    e Zd ZdZd Zdedeeef   fdZ	y)ExecutorNodeuf   Runs the batch of SQL queries (from Reasoner, 3–5) in parallel and appends tool messages + findings.c                 "    t               | _        y r   )rO   _runner)rW   s    r2   r   zExecutorNode.__init__n  s    )+r4   rP   r(   c                 8    | j                   j                  |      S r   )r   r   )rW   rP   s     r2   r   zExecutorNode.__call__q  s    ||&&r4   N)
r   r   r   r   r   r   r   r^   r   r   r   r4   r2   r   r   k  s&    p,'/ 'DcN 'r4   r   c                   6    e Zd ZdZdefdZdedeeef   fdZ	y)QueryFixerNodez'Repairs one failed SQL query using LLM.r   c                     || _         y r   r   r   s     r2   r   zQueryFixerNode.__init__x  r   r4   rP   r(   c                    |d   }|j                  dd      }t        j                  d| d       t        j                  || j
                        }t        |d      }|j                         }t        j                  d|t        j                  	      }|r|j                  d
      j                         }||d   d
z   d dS )Nr   rT   r   z2Query Fixer attempting SQL repair for query index r   )	error_logr   r*   z```(?:sql)?\s*([\s\S]*?)```flagsr   r   )rS   r   r   )rF   rp   rr   r   r   r   r3   r0   research
IGNORECASEgroup)rW   rP   r   rY   r&   r,   rS   fence_matchs           r2   r   zQueryFixerNode.__call__{  s    ,'	ii,a0HSQR#**YRVRhRhif!4mmoii >SUS`S`a%++A.446K ' /!3
 	
r4   Nr   r   r4   r2   r   r   u  s-    13# 3
/ 
DcN 
r4   r   c                   *    e Zd ZdZdedeeef   fdZy)FinalizerNodez=Produces the final behavioral report from profile + findings.rP   r(   c                    t         j                  d       t         j                  d|d          |j                  d      xs g }|j                  d      xs g }|D cg c]  }t        |t              r
d|v rd|v r| }}|r+dj                  |      }|r)|d	d
j                  |      z   z  }ndj                  |      }|sd}t        j                  |d   |t              }t        |d      }t        |      }t        |t              r|xs dj                         nt	        |xs d      }|rd|j                         v sd|j                         v rt         j                  d       t        j                  dd|t        j                   t        j"                  z        }t        j                  dd|t        j                   t        j"                  z        }d|iS c c}w )NzEStarting finalize node (synthesizing report from profile + findings).z*Finalizing Behavioral Analysis for User %sr   llm_findingsr   z[Queryzfailed]z

zC

---
Queries that failed before analysis (no LLM interpretation):
r   zThe investigation concludes that the initial profile is consistent with raw event logs, with no major supplemental anomalies found.r   )r   combined_findingsreporting_prompt皙?r*   r-   z```sqlzselect zESQL leakage detected in Finalizer output. Stripping technical blocks.z```sql.*?```r   zSELECT\s+.*?\s+FROM\s+.*?;z[Technical Detail Redacted]final_analysis)rp   rr   rF   rD   r^   joinr   r   r   r3   r   r0   lowerrq   r   subDOTALLr   )	rW   rP   r   executor_findingsffailure_notesr   r&   r,   s	            r2   r   zFinalizerNode.__call__  s   [\@%	BRSyy06B!IIj17R '
&!S!h!m	Q & 	 

  &L 9!]ii./!
 !',= >  !f!((~./-

 f#6+G4-7-E7=b'')3w}Z\K]GMMO3yGMMO7SNNbcff_b'R]]AZ[Gff:<Y[bjljsjsvx  wD  wD  kD  EG '**?
s   !GN	r   r   r   r   r   r   r^   r   r   r   r4   r2   r   r     s!    G%+/ %+DcN %+r4   r   r,   c                    | rd| vry| j                  dd      }t        |      dk7  ry|d   j                         |d   j                         }}|dd t        |      dkD  rdndz   }d	|v r_d
|v r[|j                  d
d      d   j                  d	d      j                         }|j                  d
d      d   j                         }|||fS d
|v r(|j                  d
d      d   j                         }d||fS d||fS )zXParse ToolMessage content into (hypothesis, query, result). Returns None if parse fails.zanswer:Nr   r   r   r   r   r-   zhypothesis:zquery:)splitrV   r0   r   )r,   partsheadrH   h_partq_parts         r2   _parse_tool_message_for_findingr     s   iw.MM)Q'E
5zQ8>>#U1X^^%5&DDS\c&kC&7UR@FT!1Ha(+33M2FLLNHa(+113''4Ha(+113FF##fr4   answerc                     | xs dj                         }|j                         }|j                  d      xs |j                  d      S )uW   True when the executor/tool layer failed — do not call the findings LLM for this row.r-   z
SQL_ERROR:zSYSTEM_ERROR:)r0   upper
startswith)r  aus      r2   _answer_should_skip_finding_llmr    s<    	2A		A<<%Fo)FFr4   zinvestigative queries.r   c                 >   d}t        |       D ]7  \  }}t        |t              st        |dd      xs d}t        |v s1d|v s6|}9 |dk  r!| D cg c]  }t        |t
              s| c}S | |dz   d D cg c]  }t        |t
              s| c}S c c}w c c}w )z
    When the graph loops (e.g. reasoner_abort -> reasoner -> executor), ``messages`` accumulates
    ToolMessages from earlier turns. Only analyze tools after the most recent Reasoner batch marker.
    r,   r-   	Generatedr   r   N)r   rD   r   r/   _REASONER_BATCH_AIMSG_MARKERr   )r   last_reasoner_idxrw   mcs        r2   )_tool_messages_from_latest_reasoner_batchr    s    
 (#1a#9b)/RA+q0[A5E$%!	 $
 1#B8az!['A8BB 1A 5 78W8!Jq+<VA8WW CWs   B'B8BBc                   *    e Zd ZdZdedeeef   fdZy)FindingsNodeu   Runs parallel LLM calls only for successful tool rows: each (hypothesis, query, result) -> one finding.
    Skips SQL/system errors (syntax, invalid object, etc.) — no LLM spend on those rows.rP   r(   c                 6   |j                  d      xs g }t        |      }g }d}|D ]G  }t        t        |dd      xs d      }|s |\  }}	}
t	        |
      r|dz  }7|j                  |       I |rt        j                  d|       |st        j                  d       dg iS t        |      t        j                  d	       d
t        t        t        t        f   dt        fddt        t        t        t        t        t        f   f   dt        ffd}t        t        t                    5 }t!        |j#                  |t!        t%        |                        }d d d        t        j                  dt                     d|iS # 1 sw Y   -xY w)Nr   r   r,   r-   r   zSFindingsNode: skipping %s tool message(s) with SQL/SYSTEM errors (no findings LLM).z^FindingsNode: no successful (hypothesis, query, result) triples; returning empty llm_findings.r   zUFindingsNode: starting %s parallel LLM analyses (hypothesis+query+result -> finding).argsr(   c                     | \  }}}t        j                  |||      }	 t        |d      S # t        $ r4}t        j                  d|        dt        |      d d  dcY d }~S d }~ww xY w)N)r5   r6   rH   r   r*   zFindingsNode LLM call failed: z[Finding unavailable: r   ])r   r   r3   rC   rp   rq   r^   )r  r   r   resr&   rI   s         r2   one_findingz*FindingsNode.__call__.<locals>.one_finding  sx    KCC5<<F
@"6s;; @!?sCD/At~Q??@s   - 	A*)A%A*%A*re   c                 R    | \  }}t         j                  d|dz           |      S )NzAnalyzing finding %s/%s...r   )rp   rr   )re   rY   triplenr  s      r2   analyze_onez*FindingsNode.__call__.<locals>.analyze_one  s,    KCKK4cAgqAv&&r4   r`   z=FindingsNode: all %s findings complete; produced %s findings.)rF   r  r   r/   r  r   rp   rr   rq   rV   r   r^   r   rb   rc   r   rG   rd   r   )rW   rP   r   r   triplesskipped_failuresr  parsed_hyp_qr  r  rg   findings_listr  r  s                 @@r2   r   zFindingsNode.__call__  s~   99Z(.BA(K.0A4WQ	25N5TRTUF"MD"c.s3 A% NN6"  KKe  NN{|"B''Lkmno	@eCcM2 	@s 	@	'eCsC})=$=> 	'3 	'
 3q*>#?@B T)G:L5M!NOM ASUVX[\iXjk.. A@s   5.FFNr   r   r4   r2   r  r    s$    ^/// //DcN //r4   r  c                   *    e Zd ZdZdedeeef   fdZy)
DecideNodez;After each batch, asks LLM whether to continue or finalize.rP   r(   c                    |d   }|j                  d      xs dd d }|j                  d      xs g }|rdj                  |      nd}|d d t        |      dkD  rd	ndz   }t        d
|j                  dd      |d   z
        }|j                  d      du }|rdnd}t	        j
                  |||||      }		 t        |	d
      }
|
xs dj                         r3|
xs dj                         j                         j                         d
   nd}|dk(  rdnd}t        j                  d| d| d       d|dS # t        $ r%}t        j                  d| d       d}Y d }~Jd }~ww xY w)Nr   r   r-   i   r   r   zNo findings yet.i  r   r   r   r   r   r   Tz+ This batch returned no data for any query.)r   profile_snippetfindings_textturns_remaining
empty_noter*   FINALIZEfinalizecontinuezDecide node LLM failed: z; defaulting to finalize.zAfter-batch decision: z (turns_remaining=r   F)r   empty_turn_decision)rF   r   rV   maxr   r   r3   r0   r  r   rC   rp   rq   rr   )rW   rP   r   r%  raw_findingsr&  r'  r   r(  r&   r,   
first_worddecisionrI   s                 r2   r   zDecideNode.__call__   s   	" 99^4:DSAyy,23?		,/EW%et,]9Kd9RXZ[a;!:UCT=U!UV))$56$>FUB[]
3::+'+!
	"!&a8GHOSUG\G\G^'-R..0668>>@CdfJ%/:%=z:H
 	,XJ6HHYYZ[\#(JJ  	"NN5aS8QRS!H	"s   6AD6 6	E$?EE$Nr   r   r4   r2   r#  r#    s$    EK/ KDcN Kr4   r#  c                   *    e Zd ZdZdedeeef   fdZy)ReasonerAbortNodezMCleans up state after investigation abort (e.g. system error or retry limit).rP   r(   c           
      Z   |d   r|d   d   nd }d}|r*t        |d      r|j                  r|j                  d   d   }d|d    }t        ||	      }|j                  d
      xs+ |j                  d      xr d|j                  d       xs d}|g| d|d    gd d d d d d|d   dz   d	S )Nr   r	  abort
tool_callsr   idzINVESTIGATION ABORTED: r   r   r   rR   zBatch query index rT   Queryz
 | Error: r   r   )	r   failed_investigationsr   rR   r   rT   rS   r   r   )hasattrr5  r   rF   )rW   rP   last_ai_msgr   error_summarytool_msgfailed_descs          r2   r   zReasonerAbortNode.__call__@  s   /4Z/@eJ'+d7;=+BXBX&11!4T:L1%2E1FG}<Pii-  Y%))<P2Q  3MXjkpktkt  vJ  lK  kL  WM  Y  RY!
)4Zl@S?T&U%V"&"&$%67!;

 
	
r4   Nr   r   r4   r2   r2  r2  =  s!    W
/ 
DcN 
r4   r2  rP   c                 j   | j                  dd      }| j                  d      r*d| d   v s| j                  dd      dk\  ry| d   dz   |k\  ry	y| j                  dd      dkD  r| j                  d      sy
| d   |k\  ry	| j                  d      ry| j                  d      ry| j                  d      ryy)Nr   r   r   SYSTEM_ERRORr   r   reasoner_abortr   r*  r   rR   executerS   r   reasoner)rF   )rP   r   s     r2   should_investigaterC  W  s    		+q)IyyU<00EIImQ4OST4T#"#a'94yy"A&*599\3J9,yy%&yyyyr4   c                 n    | j                  d      xs dj                         j                         dk(  ryy)Nr,  r-   r)  r*  rB  )rF   r0   r  )rP   s    r2   should_continue_after_deciderE  l  s1    		'(.B557==?:Mr4   )r   )Ir   r   r   concurrent.futuresr   typingr   r   r   r   r   langsmith.utilsr	   ImportErrorrb   langchain_core.messagesr
   r   r   r   r   rP   r   	constantsr   modelsr   toolsr   helpersr   r   promptsr   r   r   r   r   r   r   r   core.logger_configr   core.llm_providerr   core.context_managerr    r!   r"   r#   r$   r%   r   rp   r^   floatr3   rM   rO   r   r   r   r   r   boolr  r  r  r  r#  r2  rC  rE  r   r4   r2   <module>rU     s  
 
 	 1 3 3%9
 *C)N%Tf	 d d $ + ( $ D	 	 	 * %  
H	
# #% # #T# Tc Tc T4h
 h
Z#
 #
L' '
 
2(+ (+VS XeCcM>R5S &GC GD G  8 X[8I XdS^N_ X 3/ 3/lK K@
 
4/ C *(9 c   % $%s   D1 1D<;D<