
    i].                     T   d dl Z d dlZd dlmZmZmZmZmZmZ d dl	m
Z
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 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$ d dl%m&Z&m'Z'  ee"e&          de(de(fd            Z) G d de          Z* G d d          Z+d Z,dS )    N)AsyncGenerator	Annotated	TypedDictSequenceLiteralAny)BaseMessageHumanMessageSystemMessage	AIMessage)add_messages)
StateGraphSTARTEND)ToolNode)tool)
ChatOpenAI)parse_partial_json)settings)AGENT_SYSTEM_PROMPT)MAX_TOKEN_LIMIT"RETRIEVE_DOCUMENTATION_DESCRIPTION)
RAGService)AgentRetrieverResponseClassifyResponse)descriptionargs_schemaqueryquestionc                    K   | |g}t                      }|                    |d           d {V }|                    |          S )N   )k)r   _safe_retrieve_batchformat_documents)r   r   all_queriesrag_service	documentss        E/var/www/html/ls-pms-bot-apis/app/services/langgraph_agent_service.pyretrieve_documentationr)      sY      
 (#K,,K!66{a6HHHHHHHHI''	222    c                   6    e Zd ZU eee         ef         ed<   dS )
AgentStatemessagesN)__name__
__module____qualname__r   r   r	   r   __annotations__ r*   r(   r,   r,      s-         -|;<<<<<<r*   r,   c            	       b   e Zd ZeZddedz  fdZdee         defdZddee	         d	edee         fd
Z
dee         fdZdefdZdededef         fdZd ZdefdZdedefdZdedefdZdedefdZdedeee	f         fdZdededefdZddedee	         dz  deedf         fdZdS )LangGraphAgentServiceNtoolsc                 .   d| _         d| _        i | _        t          t          j        dt          j        d          | _        t          g| _	        | j        
                    | j	        ddt                    | _        |                                 | _        d S )N g      ?T)modeltemperatureopenai_api_key	streaming)r5   parallel_tool_callsstrictresponse_format)tokensansweragent_structured_outputr   r   	LLM_MODELOPENAI_API_KEYllmr)   r5   
bind_toolsr   llm_with_tools_build_graphgraph)selfr5   s     r(   __init__zLangGraphAgentService.__init__$   s    ')$$#2	
 
 
 --
"h11* $,	 2 
 
 &&((


r*   r-   returnc                    	 | j                             |          S # t          $ r d}|D ]}|j        }t	          |t
                    r|t          |          z  }1t	          |t                    rV|D ]S}t	          |t                    r<|	                    d          }t	          |t
                    r|t          |          z  }Tt          d|dz            cY S w xY w)z-Approximate token count for history trimming.r   text      )rD   get_num_tokens_from_messages	Exceptioncontent
isinstancestrlenlistdictgetmax)rI   r-   total_charsmsgrR   itemrM   s          r(   _num_tokens_from_messagesz/LangGraphAgentService._num_tokens_from_messages<   s    	,888BBB 	, 	, 	,K 	9 	9+gs++ 93w<</KK.. 9 ' 9 9%dD11 9#'88F#3#3D)$44 9 +s4yy 8q+*+++++	,s    B>CCr   historyquestion_tokenc                    |sg S g }|D ]}t          |t                    st          |                    dd                                                    }|                    dd          }t          |t                    r|                                s|dk    r$|                    t          |                     |dv r#|                    t          |                     |sg S | 	                    |          }t          d| j        |z
            }|r8||k    r2|                    d           | 	                    |          }|r||k    2|S )Ntyper7   rM   humanrR   >   aibot	assistantr   )rS   rW   rT   rX   lowerstripappendr
   r   r]   rY   r   pop)	rI   r^   r_   
normalizedr\   rolerM   token_countmax_alloweds	            r(   _history_to_base_messagesz/LangGraphAgentService._history_to_base_messagesO   sy    	I(*
 	; 	;DdD)) txx++,,2244D88FB''DdC(( 

 w!!,t"<"<"<====333!!)D"9"9"9::: 	I44Z@@!T1NBCC 	E[;66NN188DDK  	E[;66 r*   c                 2    t           }t          |          gS )z&Creates the base prompt for the agent.rc   )r   r   )rI   system_templates     r(   get_agent_promptz&LangGraphAgentService.get_agent_promptl   s    -o66677r*   statec                    K   |                                  }|                    |d                    | j                            |ddi           d{V }d|giS )zThe agent node execution logic.r-   run_nameAgent)configN)rr   extendrF   ainvoke)rI   rs   r-   responses       r(   
call_agentz LangGraphAgentService.call_agentq   s       ((**j)*** ,44( 5 
 
 
 
 
 
 
 
 XJ''r*   c                 B    |d         }|d         }|j         rdS t          S )zOConditional routing: continue to tools if the LLM called a tool, otherwise END.r-   r5   )
tool_callsr   )rI   rs   r-   last_messages       r(   should_continuez%LangGraphAgentService.should_continue   s,    $| " 	7
r*   c                 p   t          t                    }|                    d| j                   |                    dt	          | j                             |                    t          d           |                    d| j	                   |                    dd           |
                                S )zConstruct the state graph.agentr5   )r   r,   add_noder{   r   r5   add_edger   add_conditional_edgesr   compile)rI   workflows     r(   rG   z"LangGraphAgentService._build_graph   s    j)) 	'4?333'8DJ#7#7888 	%)))&&w0DEEE'7+++ !!!r*   c                 "   |dS t          |t                    r|S t          |t                    rg }|D ]}t          |t                    r|                    |           -t          |t                    r?|                    d          }t          |t                    r|                    |           d                    |          S t          |t                    r.|                    d          }t          |t                    r|ndS dS )z0Normalize LangChain chunk content to plain text.Nr7   rM   )rS   rT   rV   ri   rW   rX   join)rI   rR   partsr\   rM   s        r(   _normalize_chunk_contentz.LangGraphAgentService._normalize_chunk_content   s	   ?2gs## 	Ngt$$ 		"E + +dC(( +LL&&&&d++ +88F++D!$,, +T***775>>!gt$$ 	9;;v&&D%dC00844b8rr*   rM   c                     |sdS t          j        dd|t           j                  }|                    dd          }|                    dd          }|S )z5Remove source tags/markers from streamed answer text.r7   z</?source\b[^>]*>)flagsz\n<source id=0>\nz\nz
<source id=0>

)resub
IGNORECASEreplace)rI   rM   cleaneds      r(   _strip_source_markupz*LangGraphAgentService._strip_source_markup   sW     	2&-r4r}MMM//"7??//"5t<<r*   c                 B    |sdS |                                 }d|v pd|v S )z7Check whether token text contains source marker syntax.Fz<sourcez	\n<source)rg   )rI   rM   lowereds      r(   _has_source_markerz(LangGraphAgentService._has_source_marker   s1     	5**,,G#>|w'>>r*   tokenc                 B  K   |sd S |                                 }| j        s |                    d          r	|| _        d S |S | xj        |z  c_        	 t          | j                  }n# t          $ r Y d S w xY wt          |t                    sd S || _        |                    d          }|qt          |t                    sd S | 
                    |          }|                    | j                  r|t          | j                  d          n|}|| _        |sd S |S d S )N{r@   )lstripr?   
startswithr   rQ   rS   rW   rA   rX   rT   r   r@   rU   )rI   r   strippedescaped_text
raw_answerclean_answer	new_tokens          r(   _process_chunkz$LangGraphAgentService._process_chunk   sU      	4 <<>>{ 	!""3'' #tLKK5 KK	-dk::LL 	 	 	44	 ,-- 	4'3$!%%h//
!j#.. t44Z@@L;G;R;RSWS^;_;_qS%5%5%6%677eqI&DK tts   A( (
A65A6eventc                 X   t          |t                    rt          |          dk    rdi fS |\  }}|dk    rdi fS t          |t                    rt          |          dk    rdi fS |\  }}|                     t	          |dd                    }t          |t
                    r|nd}|S )z4Convert LangGraph stream events into token payloads.   r7   r-   rR   )rS   tuplerU   r   getattrrT   )rI   r   modepayloadchunkmetachunk_content
token_texts           r(   _parse_stream_eventz)LangGraphAgentService._parse_stream_event   s    %'' 	3u::??r6Mg:r6M'5)) 	S\\Q->->r6Mt55geYPR6S6STT&0&D&DL]]"
r*   	video_urllanguagec                     |sd S |pd                                 }|dvrd}|dk    rd}|                    dd| d          S )Nen>   r   guhir   r   z/en//)rg   r   )rI   r   r   langs       r(   _get_video_url_for_languagez1LangGraphAgentService._get_video_url_for_language   sd     	4 D'')))))D4<<D   T555r*   r   c           	       K   d| _         d| _        i | _        |                     t	          |          g          }|                     |pg |          }d|i}dt          j        ddd           d	W V  	 | j        	                    |dg
          2 3 d {V }| 
                    |          }|rh|                     |           d {V }|rK|                     |          rQ|                     |          }	|	rdt          j        d|	d           d	W V  6 | j        | j                            dd           | j                            dd           | j                            dd          | j                            dg           | j                            dd           d}
|                     | j                            dd           |
d                   |
d<   dt          j        d|
d           d	W V  d S # t           $ r6}dt          j        dt#          |          d           d	W V  Y d }~d S d }~ww xY w)Nr7   rc   )r_   r-   zdata: startzThinking...)r   r@   z

)stream_moder   video_start_timestampvideo_end_timestampr   r   next_possible_questionsr   )r@   r   r   r   r   r   end)r   final_responseerror)r   r   )r?   r@   rA   r]   r
   ro   jsondumpsrH   astreamr   r   r   r   rX   r   rQ   rT   )rI   r   r^   r_   history_messagesinputsr   r   	processedclean_tokenr   es               r(   stream_answerz#LangGraphAgentService.stream_answer  s     ')$77e9T9T9T8UVV99'-RXf9gg./TtzG}"M"MNNTTTTTTT	Q#z11&zl1SS 
f 
f 
f 
f 
f 
f 
fe!55e<<
 f&*&9&9*&E&E E E E E E EI  f229== %$&*&?&?	&J&J& f"e4:R]6^6^+_+_"e"e"eeeee  T +)-)E)I)IJacg)h)h'+'C'G'GH]_c'd'd 8<<ZNN+/+G+K+KLegi+j+j!9==k4PP N +/*J*JDLhLlLlmxz~LL  BP  Q[  B\  +]  +]N;'_4:&X&XYY_________ 	Q 	Q 	QP4:#a&&&I&IJJPPPPPPPPPPPPP	Qs%   8H  DE#H   
I 
+H;;I N)r   )r.   r/   r0   r   rV   rJ   r	   intr]   rW   ro   rr   r,   r{   r   r   r   rG   rT   r   r   boolr   r   r   r   r   r   r   r   r2   r*   r(   r4   r4   !   s6       %O) )dTk ) ) ) )0,${2C , , , , ,& d S Y]^iYj    :8${"3 8 8 8 8
(j ( ( ( (	Z 	GGSL4I 	 	 	 	" " " 3    *	 	 	 	 	 	?s ?t ? ? ? ?## ## # # # #J sDy1A     6S 6C 6C 6 6 6 6$Q $Q $QtDzD7H $QTbcfhlclTm $Q $Q $Q $Q $Q $Qr*   r4   c                      t                      S r   )r4   r2   r*   r(   get_langgraph_servicer   ,  s     """r*   )-r   r   typingr   r   r   r   r   r   langchain_core.messagesr	   r
   r   r   langgraph.graph.messager   langgraph.graphr   r   r   langgraph.prebuiltr   langchain_core.toolsr   langchain_openair   langchain_core.utils.jsonr   app.core.configr   app.core.promptsr   app.core.constantsr   r   app.services.rag_servicer   app.models.chatr   r   rT   r)   r,   r4   r   r2   r*   r(   <module>r      s    				 O O O O O O O O O O O O O O O O W W W W W W W W W W W W 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 ' ' ' ' ' ' % % % % % % ' ' ' ' ' ' 8 8 8 8 8 8 $ $ $ $ $ $ 0 0 0 0 0 0 R R R R R R R R / / / / / / D D D D D D D D 2&  3 3s 3 3 3	 3= = = = = = = =HQ HQ HQ HQ HQ HQ HQ HQV# # # # #r*   