
    i'%                         d dl mZ d dlmZ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mZ d dlmZmZ d dlZd dlZd d	lmZ d d
lmZ d dlmZ  G d de          ZdefdZ G d d          Zd ZdS )    )Document)
ChatOpenAIOpenAIEmbeddings)ChatPromptTemplateHumanMessagePromptTemplateSystemMessagePromptTemplate)settings)pinecone_service)	BaseModelField)AsyncGeneratorOptionalN)BaseCallbackHandler)parse_partial_json)process_sourcesc                       e Zd ZU  ed          Zeed<    eed          Zee	         ed<    edd	          Z
ee         ed
<    edd	          Zee         ed<   dS )	RAGOutputzA clear, accurate, and self-contained answer to the user's question, based only on the provided context. Do not include information not present in the context.)descriptionanswerzList of source IDs from the provided context that directly support the answer. Only include IDs that were explicitly used. Do not fabricate or infer IDs.)default_factoryr   sourcesNzTimestamp in the format 'MM:SS' or 'HH:MM:SS' indicating where the answer appears in the video. Return null if no video reference is available.)defaultr   video_timestampzbDirect URL of the video source containing the answer. Return null if no video source is available.	video_url)__name__
__module____qualname__r   r   str__annotations__listr   intr   r   r        9/var/www/html/ls-pms-bot-apis/app/services/rag_service.pyr   r      s         %i  FC    Y  GT#Y    &+U>& & &OXc]     %u;     Ix}     r#   r   returnc                  ~    d} t          j        |           t          j        d          g}t          j        |          S )Nu  You are a helpful AI assistant for Lodging System Admin .

Your primary role is to guide users step-by-step using the provided documentation context.

Instructions:
- Answer ONLY using the provided context.
- If the answer exists, explain it clearly as a sequence of steps when applicable.
- Keep responses structured, concise, and easy to follow.
- Use bullet points or numbered steps for procedures.
- If relevant, include important options, fields, or actions the user must take.
- Do NOT make up information or assumptions.
- If the answer is not found in the context:
  - Say you couldn't find it in the documentation. 
  - Ask the user to rephrase or provide more details.

Language:
- Respond in the same language as the user’s question.
- If unclear, respond in English.

Context:
{context}

Response format:
- Use markdown
- Prefer step-by-step instructions for "how-to" questions
- Highlight important actions in **bold**
z{query})r   from_templater   r   from_messages)system_templatemessagess     r$   
get_promptr+   4   sA    O: 	$1/BB"0;;H
 +H555r#   c                       e Zd Zd ZdefdZddededee	         fdZ
ddee         dedee	         fd	Zd
ee	         defdZdedeedf         fdZdedeedf         fdZdS )
RAGServicec                 "    d| _         d| _        d S )N )tokensr   selfs    r$   __init__zRAGService.__init__Y   s    r#   r%   c                     t           j        st          d          t          t           j        t           j        d          S )Nz!OpenAI API key is not configured.i   )modelopenai_api_key
dimensions)r	   OPENAI_API_KEY
ValueErrorr   EMBEDDING_MODELr1   s    r$   _get_embeddingszRAGService._get_embeddings]   sC    & 	B@AAA*#2
 
 
 	
r#      querykc                   K   t          j                    }|                                 }|                    |           d{V }|                    ||dd          }t          |dg           pg }g }t          |          D ]\  }}	t          |	dd          pi }
|
                    d          p|
                    d          }|
                    dd           |s[|	                    t          ||
	                     |S )
zFQuery Pinecone directly and tolerate vectors missing metadata/content.NTFvectortop_kinclude_metadatainclude_valuesmatchesmetadatacontenttextpage_contentrF   )r
   	get_indexr;   aembed_queryr=   getattr	enumerategetpopappendr   )r2   r=   r>   index
embeddingsquery_vectorresultsrE   	documentsmatchrF   rG   s               r$   _safe_retrievezRAGService._safe_retrieveg   s)      *,,))++
'44U;;;;;;;;++! 	  
 
 '9b117R$&	%g.. 	P 	PLE5uj$77=2Hll9--Ef1E1EGLLD))) X7XNNNOOOOr#   queriesc                   K   d |D             }|sg S t          j                    |                                 }|                    |           d{V }dt          t
                   ffdt          j        fd|D               d{V }g }t                      }|D ]}	t          |	dg           pg }
|
D ]}t          |dd          pi }|
                    d          p|
                    d	          }|                    dd           |sX||
                    d
          p|
                    d          pdf}||v r|                    |           |                    t          ||                     Ō|S )zHBatch-embed queries, then run Pinecone similarity searches concurrently.c                     g | ]?}t          |t                    |                                +|                                @S r"   )
isinstancer   strip).0qs     r$   
<listcomp>z3RAGService._safe_retrieve_batch.<locals>.<listcomp>   sA    ZZZAs9K9KZPQPWPWPYPYZ17799ZZZr#   NrA   c                 P   K   t          j        j        | dd           d {V S )NTFr@   )asyncio	to_threadr=   )rA   rR   r>   s    r$   _query_indexz5RAGService._safe_retrieve_batch.<locals>._query_index   sP       *!%$         r#   c              3   .   K   | ]} |          V  d S Nr"   )r^   rA   rd   s     r$   	<genexpr>z2RAGService._safe_retrieve_batch.<locals>.<genexpr>   s-      (Z(Z&f)=)=(Z(Z(Z(Z(Z(Zr#   rE   rF   rG   rH   sourceurlr/   rI   )r
   rK   r;   aembed_documentsr    floatrb   gathersetrM   rO   rP   addrQ   r   )r2   rY   r>   cleaned_queriesrS   query_vectorsrU   rV   	seen_keysresultrE   rW   rF   rG   keyrd   rR   s     `            @@r$   _safe_retrieve_batchzRAGService._safe_retrieve_batch   s     ZZgZZZ 	I *,,))++
(99/JJJJJJJJ	tE{ 	 	 	 	 	 	 	  (Z(Z(Z(ZM(Z(Z(Z[[[[[[[$&	*-%%	 	T 	TFfi44:G  T T"5*d;;Ar",,y11IX\\&5I5IY--- X 6 6 S(,,u:M:M SQST)##c"""  w!R!R!RSSSST r#   docsc                 *   d}|D ]}|j         pi }|                    dd          }|j        pd                                }|                    dd           p|                    dd           }d}d| d}	d	| d
}
d| d}|||	z   |
z   |z   dz   z  }|S )Nr/   titlezN/Arh   ri   z

<source>
z<title>z	</title>
z	<content>z</content>
z<url>z</url>
z
</source>
)rF   rO   rJ   r]   )r2   ru   multiline_stringdocrF   rw   rG   ri   source_stringtitle_stringcontent_string
url_strings               r$   format_documentszRAGService.format_documents   s     	 	C \/RHLL%00E'-24466G,,x..K(,,ud2K2KC*M6U666L>>>>N....J !   	   r#   tokenNc                   K   ||dk    st          |          dk    rd S | xj        |z  c_        d }| j        rt          | j                  }|;d|v r7|d         }|t          | j                  d          }|| _        |dk    s|d S |S d S )Nr/   r   r   )lenr0   r   r   )r2   r   escaped_text
raw_answer	new_tokens        r$   _process_chunkzRAGService._process_chunk   s      =E2IIUQFu; 	;-dk::L#L(@(@%h/J"3t{#3#3#5#56I$DKB)"3tr#   c                "  K   |                      |d           d{V }d|                     |          z   dz   }||d}t                      g}t          t          j        dt          j        d|	          }t                      }||                    t                    z  }d
}		 |
                    |          2 3 d{V }
|
                    dd
          }|dk    rdt          j        ddd           dW V  |dk    rV|
d         d         }
|
j        r@|                     |
j                   d{V }|dt          j        d|d           dW V  |dk    r"|
d         d         }	|	                                }6 |d         t#          ||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)!z<True token streaming over SSE with final structured payload.   )r>   Nz	<context>z
</context>)contextr=   g      ?T)r5   temperaturer6   	streaming	callbacksr/   eventon_chain_startzdata: startzThinking...)r   r   z

on_chat_model_streamdatachunkr   on_chain_endoutputr   r   r   r   )r   r   r   r   end)r   final_responseerror)r   r   )rX   r~   r   r   r	   	LLM_MODELr8   r+   with_structured_outputr   astream_eventsrO   jsondumpsrG   r   
model_dumpr   	Exceptionr   )r2   r=   ru   context_textinputscallback_handlerllmstream_prompttoken_chaincollected_answerr   
event_typer   final_payloadr   es                   r$   stream_answerzRAGService.stream_answer   s      ((!(44444444"T%:%:4%@%@@<O)E::/112$#2&
 
 
 ##c&@&@&K&KK	Q*99&AA A A A A A A Ae"YYw33
!111`4:=.Y.Y#Z#Z```````!777!&M'2E} e*.*=*=em*L*L$L$L$L$L$L$L	$0"d4:S\6]6]+^+^"d"d"ddddd>11&+FmH&=##3#>#>#@#@=  B (1*4y1IJJ#01B#C*;7	 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%   %G :F  DG 
H+H		H)r<   )r   r   r   r3   r   r;   r   r!   r    r   rX   rt   r~   dictr   r   r   r"   r#   r$   r-   r-   X   s-         
!1 
 
 
 
 # # d8n    2( ($s) ( (DQYN ( ( ( (T T(^          4$ >#t)3L    $(Q (QT	1J (Q (Q (Q (Q (Q (Qr#   r-   c                      t                      S rf   )r-   r"   r#   r$   get_rag_servicer     s    <<r#   )langchain_core.documentsr   langchain_openair   r   langchain_core.promptsr   r   r   app.core.configr	   app.services.pinecone_servicer
   pydanticr   r   typingr   r   r   rb   langchain_core.callbacks.baser   langchain_core.utils.jsonr   app.utils.extrar   r   r+   r-   r   r"   r#   r$   <module>r      s   - - - - - - 9 9 9 9 9 9 9 9         
 % $ $ $ $ $ : : : : : : % % % % % % % % + + + + + + + +   = = = = = = 8 8 8 8 8 8 + + + + + +    	   @"6& "6 "6 "6 "6HfQ fQ fQ fQ fQ fQ fQ fQR    r#   