§
    éƒtj;S  ã                   óÈ  — 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 d dl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 d dlmZmZ d d	lmZ d d
lmZmZmZ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+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1 d dl2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8 d dl9m:Z: d dl;m<Z< d dl=m>Z>  e"¦   «         Z?e!dz  dz  Z@ e
ddd¬¦  «        Zd„ e?jA         B                    d¦  «        D ¦   «         ZCe D                    eeCdgk    reCndgddgdg¬¦  «         e E                    e<¦  «          eeF¦  «         G                    ¦   «         jH        jH        dz  ZIe J                    d¦  «        d‚d„¦   «         ZKe L                    d e-d!g¬"¦  «        de-fd#„¦   «         ZMe N                    d$e&d%g¬"¦  «        d&e%de&fd'„¦   «         ZOe N                    d(e&d%g¬"¦  «         ed)¦  «         ed¬*¦  «         ed¬*¦  «        fd+ed,ePdz  d-ePdz  de&fd.„¦   «         ZQe R                    d/d%g¬0¦  «         ed)¦  «        fd,ePdeSfd1„¦   «         ZTe L                    d2eUe)         d%g¬"¦  «         ed3d4d5¬6¦  «        fd7eVdeUe)         fd8„¦   «         ZWe L                    d9e'd%g¬"¦  «        d,ePde'fd:„¦   «         ZXe R                    d9d%g¬0¦  «        d,ePdeSfd;„¦   «         ZYd<ePdePfd=„ZZe N                    d>e,d%g¬"¦  «        d?e+de,fd@„¦   «         Z[ G dA„ dBe¦  «        Z\e ]                    dCd%g¬0¦  «        dDeVd?e\deSfdE„¦   «         Z^ G dF„ dGe¦  «        Z_ G dH„ dIe¦  «        Z`e N                    dJe`dKg¬"¦  «        d?e_de`fdL„¦   «         Zae L                    dMeUe*         dKg¬"¦  «         eddN¬O¦  «         edd dP¬6¦  «         ed¬*¦  «         ed¬*¦  «         edQd4dR¬6¦  «         ed d ¬S¦  «        fdTeeP e	e¦  «        f         dUePdz  dVeVdz  dWePdz  dXePdz  d7eVdYeVdeUe*         fdZ„¦   «         Zbe L                    d[dKg¬0¦  «         ed¬*¦  «         ed¬*¦  «         ed\d4d]¬6¦  «        fdTeeP e	e¦  «        f         dWePdz  dXePdz  d7eVdeSf
d^„¦   «         Zce ]                    d_dKg¬0¦  «        dDeVd?e1dTeeP e	e¦  «        f         deSfd`„¦   «         Zde L                    daeUe$         dKg¬"¦  «         edQd4dR¬6¦  «         ed d ¬S¦  «        fdTeeP e	e¦  «        f         d7eVdYeVdeUe$         fdb„¦   «         ZedƒddeSd-ePde/fde„Zfe N                    dfe/dgg¬"¦  «         ed)¦  «        fd+ede/fdh„¦   «         Zge L                    die/dgg¬"¦  «        djePde/fdk„¦   «         Zhe N                    dle/dgg¬"¦  «        djePd?e.de/fdm„¦   «         Zie N                    dne/dgg¬"¦  «        djePde/fdo„¦   «         ZjeIdpz  Zkdefdq„Zle L                    drdsg¬0¦  «        dt„ ¦   «         Zme L                    dudsg¬0¦  «        dv„ ¦   «         Zne L                    dwdsg¬0¦  «        dx„ ¦   «         Zoe L                    dydsg¬0¦  «        dz„ ¦   «         Zpe L                    d{dsg¬0¦  «        d|„ ¦   «         Zqe L                    d}dsg¬0¦  «        d~„ ¦   «         Zrek s                    ¦   «         r.e t                    d e ePek¦  «        d¬€¦  «        dp¬�¦  «         dS dS )„é    N)ÚPath)Ú	Annotated)ÚDependsÚFastAPIÚFileÚFormÚHTTPExceptionÚQueryÚ
UploadFile)ÚCORSMiddleware)ÚFileResponse)ÚStaticFiles)Ú	BaseModelÚField)Údatabase)ÚADMIN_PASSWORDÚADMIN_USERNAMEÚcreate_access_tokenÚverify_admin_token)ÚBASE_DIRÚget_settings)Ú	AuditItemÚChatRequestÚChatResponseÚConversationDetailÚConversationMessageÚConversationSummaryÚFeedbackItemÚFeedbackRequestÚFeedbackResponseÚHealthResponseÚPdfConfirmRequestÚPdfJobResponseÚPdfRowÚReviewRequest)Úcalculate_jobÚclear_session_jobÚconfirm_jobÚcreate_job_from_uploadÚget_jobÚingest_pdf_for_chat)Úget_rag_service)Úsaas_router)Úinit_saas_tablesÚdataÚpdf_uploadszAccountants SaaS ChatbotzMMulti-tenant RAG chatbot agent for accounting and finance (Ollama + ChromaDB)z2.0.0)ÚtitleÚdescriptionÚversionc                 ó^   — g | ]*}|                      ¦   «         ¯|                      ¦   «         ‘Œ+S © )Ústrip)Ú.0Úos     ú$/var/www/html/CA-Chatbot/app/main.pyú
<listcomp>r:   6   s-   € Ð
LÐ
LÐ
L˜À!Ç'Â'Á)Ä)Ð
Lˆ1�7Š7‰9Œ9Ð
LÐ
LÐ
Ló    ú,Ú*T)Úallow_originsÚallow_credentialsÚallow_methodsÚallow_headersÚstaticÚstartupÚreturnc               ƒ   óì  K  — t          j        t          j        rt           j        nt           j        d¬¦  «         t          j        d¦  «                             t          j        rt           j        nt           j        ¦  «         t          j        r't          j        d¦  «                             d¦  «         t          j
        ¦   «          t          ¦   «          	 ddlm}   | ¦   «          n># t          $ r1 t          j        t          ¦  «                             dd¬	¦  «         Y nw xY w	 t          j        ¦   «          n># t          $ r1 t          j        t          ¦  «                             d
d¬	¦  «         Y nw xY wt          j        sŠt&                               ¦   «         rqt&                               ¦   «         D ]W}|                     ¦   «         rA|j                             ¦   «         dv r&	 |                     ¦   «          ŒG# t4          $ r Y ŒSw xY wŒXt7          ¦   «         }t9          j        t9          j        |j        ¦  «        ¦  «         dd„}t9          j        t9          j        |¦  «        ¦  «         d S )Nz0%(asctime)s %(levelname)s [%(name)s] %(message)s)ÚlevelÚformatz
app.rag.kbzRAG_DEBUG enabledr   )Úensure_admin_accountzFailed to ensure admin accountT©Úexc_infozPostgres pool warm-up failed>   ú.jsonú.pdfrD   c                  ó   — 	 ddl m}   | ¦   «         }t          j        t          ¦  «                             d|¦  «         d S # t          $ r2 t          j        t          ¦  «                             dd¬¦  «         Y d S w xY w)Nr   )Úwarm_local_statement_cachesz Warmed %s statement PDF cache(s)zStatement cache warm-up failedTrI   )Úapp.saas.kb_answerrN   ÚloggingÚ	getLoggerÚ__name__ÚinfoÚ	ExceptionÚwarning)rN   Úns     r9   Ú_warm_statementsz!startup.<locals>._warm_statementsg   sŸ   € ð	aØFÐFÐFÐFÐFÐFà+Ð+Ñ-Ô-ˆAÝÔ�hÑ'Ô'×,Ò,Ð-OÐQRÑSÔSÐSÐSÐSøÝð 	að 	að 	aÝÔ�hÑ'Ô'×/Ò/Ð0PÐ[_Ð/Ñ`Ô`Ð`Ð`Ð`Ð`ð	aøøøs   ‚=A Á8A=Á<A=©rD   N) rP   ÚbasicConfigÚsettingsÚ	rag_debugÚDEBUGÚINFOrQ   ÚsetLevelrS   r   Úinit_dbr.   Úapp.saas.dbrH   rT   rR   rU   Úwarm_pg_poolÚpersist_user_dataÚ
UPLOAD_DIRÚexistsÚiterdirÚis_fileÚsuffixÚlowerÚunlinkÚOSErrorr,   ÚasyncioÚcreate_taskÚ	to_threadÚwarmup)rH   ÚpathÚservicerW   s       r9   rC   rC   D   sŽ  è è € åÔÝ'Ô1ÐC�gŒmˆmµw´|ØAðñ ô ð õ Ô�lÑ#Ô#×,Ò,Ý!Ô+Ð=�Œˆµ´ñô ð õ Ôð BÝÔ˜,Ñ'Ô'×,Ò,Ð-@ÑAÔAÐAåÔÑÔÐÝÑÔÐð]Ø4Ð4Ð4Ð4Ð4Ð4àÐÑÔÐÐøÝð ]ð ]ð ]ÝÔ�(Ñ#Ô#×+Ò+Ð,LÐW[Ð+Ñ\Ô\Ð\Ð\Ð\ð]øøøð[ÝÔÑÔÐÐøÝð [ð [ð [ÝÔ�(Ñ#Ô#×+Ò+Ð,JÐUYÐ+ÑZÔZÐZÐZÐZð[øøøõ Ô%ð ­*×*;Ò*;Ñ*=Ô*=ð Ý×&Ò&Ñ(Ô(ð 	ð 	ˆDØ�|Š|‰~Œ~ð  $¤+×"3Ò"3Ñ"5Ô"5Ð9JÐ"JÐ"JðØ—K’K‘M”M�M�MøÝð ð ð Ø�DðøøøøåÑÔ€GÝÔ�Ô)¨'¬.Ñ9Ô9Ñ:Ô:Ð:ðað að að aõ Ô�Ô)Ð*:Ñ;Ô;Ñ<Ô<Ð<Ð<Ð<s6   ÃC* Ã*8D%Ä$D%Ä)D= Ä=8E8Å7E8Ç+H È 
HÈHz/healthÚsystem)Úresponse_modelÚtagsc               ƒ   óÖ   K  — t          ¦   «         } t          d|                      ¦   «         ƒ d {V —†t          j        |                      ¦   «         |                      ¦   «         ¬¦  «        S )NÚok)ÚstatusÚollama_reachableÚollama_modelÚvector_store_readyÚdocument_count)r,   r!   rw   rZ   rx   ry   rz   )rp   s    r9   Úhealthr{   u   sp   è è € åÑÔ€GÝØØ&×7Ò7Ñ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9ÝÔ*Ø"×5Ò5Ñ7Ô7Ø×-Ò-Ñ/Ô/ðñ ô ð r;   z/chatÚchatÚrequestc              ƒ   ó²  K  — t          ¦   «         }|                     ¦   «         st          dd¬¦  «        ‚|                     ¦   «         ƒ d {V —†s,t          ddt          j        › dt          j        › d�¬¦  «        ‚	 t          j        |j	        | j
        | j        ¦  «        ƒ d {V —†S # t          $ r$}t          dt          |¦  «        ¬¦  «        |‚d }~ww xY w)Né÷  z4Vector store is empty. Run: python scripts/ingest.py©Ústatus_codeÚdetailzOllama is not reachable at z. Start Ollama and pull ú.)r,   ry   r	   rw   rZ   Úollama_base_urlrx   rk   rm   r|   ÚquestionÚ
session_idÚRuntimeErrorÚstr)r}   rp   Úexcs      r9   r|   r|   ƒ   s&  è è € åÑÔ€Gà×%Ò%Ñ'Ô'ð 
ÝØØIð
ñ 
ô 
ð 	
ð
 ×)Ò)Ñ+Ô+Ð+Ð+Ð+Ð+Ð+Ð+ð 
ÝØðEµÔ1Ið Eð EÝ,4Ô,AðEð Eð Eð
ñ 
ô 
ð 	
ðGÝÔ& w¤|°WÔ5EÀwÔGYÑZÔZÐZÐZÐZÐZÐZÐZÐZøÝð Gð Gð GÝ¨µC¸±H´HÐ=Ñ=Ô=À3ÐFøøøøðGøøøs   Á=*B( Â(
CÂ2CÃCz/chat/upload-pdf.)ÚdefaultÚfiler†   Úmessagec           
   ƒ   ó"  K  — | j         r,| j                              ¦   «                              d¦  «        st          dd¬¦  «        ‚|                      ¦   «         ƒ d{V —†}|st          dd¬¦  «        ‚t          |¦  «        dk    rt          dd¬¦  «        ‚|pt          t          j        ¦   «         ¦  «        }d}	 t          j
        rWt                               d	d	¬
¦  «         t          t          j        ¦   «         › d| j         › �z  }|                     |¦  «         n‚t          j        dd¬¦  «        }	 |                     |¦  «         |                     ¦   «          t%          |j        ¦  «        }|                     ¦   «          n# |                     ¦   «          w xY wt+          j        t.          || j         |¦  «        ƒ d{V —†}n'# t0          $ r}t          dd|› �¬¦  «        |‚d}~ww xY w	 |�4t          j
        s(	 |                     d	¬¦  «         nM# t4          $ r Y nAw xY wn<# |�4t          j
        s)	 |                     d	¬¦  «         w # t4          $ r Y w w xY ww w xY w|pd                     ¦   «         }	|                     d¦  «        pi                      d¦  «        }
t;          |                     d¦  «        p>|                     d¦  «        p)|                     d¦  «        p|                     d¦  «        ¦  «        }|sl|                     d¦  «        p
d| j         › d�}t=          j        d| j         › �|dd|¬¦  «        }tA          |g ||pdd|                     d¦  «        |
¬ ¦  «        S |	pd!}tC          ¦   «         }	 t+          j        |j"        ||¦  «        ƒ d{V —†}tA          |j#        |j$        ||j%        d|                     d¦  «        |
¬ ¦  «        S # tL          $ r$}t          d"t          |¦  «        ¬¦  «        |‚d}~ww xY w)#a  Upload a PDF into the chat session: extract, calculate, and answer-ready.

    Optional ``message`` is answered in the same turn using the uploaded statement.
    By default the PDF is processed from a temp file and deleted immediately (no disk retention).
    rL   é�  úPlease upload a .pdf filer€   Nú
Empty fileé  @úPDF too large (max 20 MB)T©ÚparentsÚexist_okÚ_F©rg   Údeleteéô  zPDF ingest failed: ©Ú
missing_okÚ ÚcalculationÚsummaryÚrowsÚdocument_textÚtext_previewÚerrorzCould not read content from rƒ   z[uploaded PDF] z[]r   )r…   ÚanswerÚcitations_jsonÚduration_msr†   ÚpdfÚjob_id)r£   Ú	citationsr†   Úlog_idÚmodeÚ
pdf_job_idÚpdf_summaryz=What is this PDF about? Summarize what the document contains.r   )'Úfilenamerh   Úendswithr	   ÚreadÚlenrˆ   ÚuuidÚuuid4rZ   rb   rc   ÚmkdirÚwrite_bytesÚtempfileÚNamedTemporaryFileÚwriteÚflushr   ÚnameÚcloserk   rm   r+   rT   ri   rj   r6   ÚgetÚboolr   Ú	log_queryr   r,   r|   r£   r¨   r©   r‡   )r‹   r†   rŒ   ÚrawÚsidÚsavedÚtmpÚjobr‰   Ú	user_noterž   Úhas_contentr£   r©   r…   rp   Úresps                    r9   Úchat_upload_pdfrÆ   š   sœ  è è € ð Œ=ð Q ¤× 3Ò 3Ñ 5Ô 5× >Ò >¸vÑ FÔ Fð QÝ¨Ð4OÐPÑPÔPÐPà—	’	‘”Ð
Ð
Ð
Ð
Ð
Ð
€CØð BÝ¨°LÐAÑAÔAÐAÝ
ˆ3�x„xÐ"Ò"Ð"Ý¨Ð4OÐPÑPÔPÐPà
Ð
)��DœJ™LœLÑ)Ô)€CØ€EðÝÔ%ð 	Ý×Ò T°DÐÑ9Ô9Ð9Ý¥D¤J¡L¤LÐ!BÐ!B°4´=Ð!BÐ!BÑBˆEØ×Ò˜cÑ"Ô"Ð"Ð"åÔ-°VÀEÐJÑJÔJˆCðØ—	’	˜#‘”�Ø—	’	‘”�Ý˜SœX™œ�à—	’	‘”��ø�—	’	‘”��øøøåÔ%Õ&9¸5À$Ä-ÐQTÑUÔUÐUÐUÐUÐUÐUÐUˆˆøÝð Zð Zð ZÝ¨Ð4OÈ#Ð4OÐ4OÐPÑPÔPÐVYÐYøøøøðZøøøð 	ð
 Ð¥XÔ%?ÐðØ—’¨�Ñ-Ô-Ð-Ð-øÝð ð ð Ø�ðøøøøøð Ð¥XÔ%?ÐðØ—’¨�Ñ-Ô-Ð-Ð-øÝð ð ð Ø�ðøøøð Ðøøøð �˜B×%Ò%Ñ'Ô'€IØ�wŠw�}Ñ%Ô%Ð+¨×0Ò0°Ñ;Ô;€GÝØ�Š�‰Œð 	#Ø�7Š7�=Ñ!Ô!ð	#à�7Š7�?Ñ#Ô#ð	#ð �7Š7�>Ñ"Ô"ñ	ô €Kð ð 
Ø—’˜Ñ!Ô!ÐTÐ%TÀDÄMÐ%TÐ%TÐ%TˆÝÔ#Ø6 t¤}Ð6Ð6ØØØØð
ñ 
ô 
ˆõ ØØØØ�>˜TØØ—w’w˜xÑ(Ô(Øð
ñ 
ô 
ð 	
ð Ð[Ð[€HÝÑÔ€GðGÝÔ& w¤|°X¸sÑCÔCÐCÐCÐCÐCÐCÐCˆÝØ”;Ø”nØØ”;ØØ—w’w˜xÑ(Ô(Øð
ñ 
ô 
ð 	
øõ ð Gð Gð GÝ¨µC¸±H´HÐ=Ñ=Ô=À3ÐFøøøøðGøøøs’   Â=A9G
 Ä7=F	 Å4G
 Æ	FÆ*G
 Ç	H) Ç

G.ÇG)Ç)G.Ç.H) ÈH È
H%È$H%È)I"È9IÉI"É
IÉI"ÉIÉI"ÎAO  Ï 
PÏ*P	Ð	Pz	/chat/pdf)rs   c              ƒ   ó.   K  — t          | ¦  «         d| dœS )zIDetach the active uploaded PDF from a chat session so normal RAG resumes.T©ru   r†   )r'   ©r†   s    r9   Úchat_clear_pdfrÊ   ÷   s$   è è € õ �jÑ!Ô!Ð!Ø jÐ1Ð1Ð1r;   z/conversationsé(   é   éd   )rŠ   ÚgeÚleÚlimitc              ƒ   óH   K  — t          j        | ¬¦  «        }d„ |D ¦   «         S )N©rÐ   c                 ó&   — g | ]}t          d i |¤Ž‘ŒS ©r5   )r   ©r7   Úrs     r9   r:   z!conversations.<locals>.<listcomp>  s'   € Ð3Ð3Ð3¨ÕÐ$Ð$ !Ð$Ð$Ð3Ð3Ð3r;   )r   Úlist_conversations)rÐ   rŸ   s     r9   ÚconversationsrØ      s-   è è € åÔ&¨UÐ3Ñ3Ô3€DØ3Ð3¨dÐ3Ñ3Ô3Ð3r;   z/conversations/{session_id}c              ƒ   óD  K  — t          j        | ¦  «        }|st          dd¬¦  «        ‚d}t          j        d¬¦  «        }|D ]'}|d         | k    r|                     d¦  «        p|} nŒ(t          |d	         d
         ¦  «        }t          | |d„ |D ¦   «         ¬¦  «        S )Né”  zConversation not foundr€   ÚChatéÈ   rÒ   Úidr1   r   r…   c                 ó&   — g | ]}t          d i |¤Ž‘ŒS rÔ   )r   )r7   Úms     r9   r:   z'conversation_detail.<locals>.<listcomp>  s'   € Ð=Ð=Ð=¨qÕ%Ð*Ð*¨Ð*Ð*Ð=Ð=Ð=r;   )rÝ   r1   Úmessages)r   Úget_session_messagesr	   r×   r»   Ú_title_fallbackr   )r†   rà   r1   ÚconvosÚcs        r9   Úconversation_detailrå     sÍ   è è € åÔ,¨ZÑ8Ô8€HØð NÝ¨Ð4LÐMÑMÔMÐMØ€EÝÔ(¨sÐ3Ñ3Ô3€FØð 9ð 9ˆØˆTŒ7�jÒ Ð Ø—E’E˜'‘N”NÐ+ eˆEØˆEð !õ   ¨¤¨JÔ 7Ñ8Ô8ˆÝØØØ=Ð=°HÐ=Ñ=Ô=ðñ ô ð r;   c              ƒ   ó8   K  — t          j        | ¦  «         d| dœS )NTrÈ   )r   Údelete_conversationrÉ   s    r9   Úconversation_deleterè     s%   è è € åÔ  Ñ,Ô,Ð,Ø jÐ1Ð1Ð1r;   r…   c                 óB  — | pd                      ¦   «         }|                     d¦  «        r*|                     dd¦  «                              ¦   «         pd}d                     |                     ¦   «         ¦  «        }t          |¦  «        dk    r|d d…         dz   n|pdS )	Nrœ   z[uploaded PDF]z
PDF uploadú é0   é-   u   â€¦rÛ   )r6   Ú
startswithÚreplaceÚjoinÚsplitr°   )r…   Útexts     r9   râ   râ      s•   € ØˆN˜×!Ò!Ñ#Ô#€DØ‡‚Ð'Ñ(Ô(ð JØ�|Š|Ð,¨bÑ1Ô1×7Ò7Ñ9Ô9ÐI¸\ˆØ�8Š8�D—J’J‘L”LÑ!Ô!€DÝ"% d¡)¤)¨b¢. .ˆD��"�ŒI˜ÑÐ°t°~¸vÐFr;   z	/feedbackÚbodyc           
   ƒ   ó¼   K  — t          j        | j        | j        | j        | j        | j        | j        | j        | j	        ¬¦  «        }t          |d| j        ¬¦  «        S )zMSave thumbs up/down into SQLite for later model training / preference tuning.)r©   r†   r…   r£   ÚratingÚ
correctionÚ
message_idrª   zThank you for your feedback!)Úfeedback_idrŒ   rô   )r   Úsave_feedbackr©   r†   r…   r£   rô   rõ   rö   rª   r    )rò   r÷   s     r9   Úsubmit_feedbackrù   *  sk   è è € õ Ô(ØŒ{Ø”?Ø”ØŒ{ØŒ{Ø”?Ø”?ØŒYð	ñ 	ô 	€Kõ ØØ.ØŒ{ðñ ô ð r;   c                   ó:   — e Zd ZU  edd¬¦  «        Zedz  ed<   dS )ÚFeedbackCorrectionRequestNi   )rŠ   Ú
max_lengthrõ   )rR   Ú
__module__Ú__qualname__r   rõ   rˆ   Ú__annotations__r5   r;   r9   rû   rû   =  s5   € € € € € € Ø"˜U¨4¸DÐAÑAÔA€J��d‘
ÐAÐAÑAÐAÐAr;   rû   z/feedback/{feedback_id}r÷   c              ƒ   óD   K  — t          j        | |j        ¦  «         d| dœS )NT)ru   r÷   )r   Úupdate_feedback_correctionrõ   )r÷   rò   s     r9   Úpatch_feedback_correctionr  A  s+   è è € õ
 Ô'¨°T´_ÑEÔEÐEØ {Ð3Ð3Ð3r;   c                   ó$   — e Zd ZU eed<   eed<   dS )ÚLoginRequestÚusernameÚpasswordN)rR   rý   rþ   rˆ   rÿ   r5   r;   r9   r  r  K  s"   € € € € € € Ø€M€M�MØ€M€M�M€M€Mr;   r  c                   ó(   — e Zd ZU eed<   dZeed<   dS )ÚTokenResponseÚaccess_tokenÚbearerÚ
token_typeN)rR   rý   rþ   rˆ   rÿ   r  r5   r;   r9   r  r  P  s.   € € € € € € ØÐÐÑØ€J�ÐÐÑÐÐr;   r  z/admin/loginÚadminc              ƒ   ó´   K  — | j         t          k    s| j        t          k    rt	          dd¬¦  «        ‚t          d| j         i¦  «        }t          |¬¦  «        S )Ni‘  zInvalid credentialsr€   Úsub)r	  )r  r   r  r   r	   r   r  )rò   Útokens     r9   Úadmin_loginr  U  sX   è è € à„}�Ò&Ð&¨$¬-½>Ò*IÐ*IÝ¨Ð4IÐJÑJÔJÐJÝ ¨¬Ð 6Ñ7Ô7€EÝ eÐ,Ñ,Ô,Ð,r;   z/admin/feedbackz^(up|down)$)rŠ   Úpatterné   é2   r™   )rŠ   rÎ   Ú_adminrô   ÚreviewedÚorg_idÚagent_idÚoffsetc              ƒ   óR   K  — t          j        ||||||¬¦  «        }d„ |D ¦   «         S )N)rÐ   r  rô   r  r  r  c                 ó&   — g | ]}t          d i |¤Ž‘ŒS rÔ   )r   rÕ   s     r9   r:   z!list_feedback.<locals>.<listcomp>q  s&   € Ð,Ð,Ð, !�LÐÐ˜1ÐÐÐ,Ð,Ð,r;   )r   Úget_feedback)r  rô   r  r  r  rÐ   r  rŸ   s           r9   Úlist_feedbackr  _  sG   è è € õ Ô ØØØØØØðñ ô €Dð -Ð, tÐ,Ñ,Ô,Ð,r;   z/admin/feedback/trainingiˆ  i N  c              ƒ   óÐ   K  — t          j        |||¬¦  «        }d„ |D ¦   «         }t          |¦  «        t          d„ |D ¦   «         ¦  «        t          d„ |D ¦   «         ¦  «        |dœS )z;Export rated Q/A pairs for preference tuning / fine-tuning.)r  r  rÐ   c                 ó<  — g | ]™}|d          |d         |d         |d         |                      d¦  «        |                      d¦  «        |                      d¦  «        |                      d¦  «        |                      d¦  «        |                      d	¦  «        d
œ
‘ŒšS )rÝ   r…   r£   rô   rõ   rª   Ú
model_namer  r  Ú
created_at)
rÝ   r…   r£   rô   rõ   rª   r  r  r  r   )r»   rÕ   s     r9   r:   z,export_training_feedback.<locals>.<listcomp>}  s£   € ð ð ð ð ð �D”'Ø˜*œØ˜”kØ˜”kØŸ%š% Ñ-Ô-Ø—E’E˜&‘M”MØŸ%š% Ñ-Ô-Ø—e’e˜H‘o”oØŸš˜jÑ)Ô)ØŸ%š% Ñ-Ô-ð	
ð 	
ðð ð r;   c              3   ó2   K  — | ]}|d          dk    ¯dV — ŒdS )rô   ÚuprÌ   Nr5   ©r7   Úps     r9   ú	<genexpr>z+export_training_feedback.<locals>.<genexpr>Ž  s0   è è € Ð:Ð:˜ a¨¤k°TÒ&9Ð&9�!Ð&9Ð&9Ð&9Ð&9Ð:Ð:r;   c              3   ó2   K  — | ]}|d          dk    ¯dV — ŒdS )rô   ÚdownrÌ   Nr5   r#  s     r9   r%  z+export_training_feedback.<locals>.<genexpr>�  s0   è è € Ð>Ð>˜!¨¨(¬°vÒ(=Ð(=�AÐ(=Ð(=Ð(=Ð(=Ð>Ð>r;   )Úcountr"  r'  Úpairs)r   Úget_training_pairsr°   Úsum)r  r  r  rÐ   rŸ   r)  s         r9   Úexport_training_feedbackr,  t  s�   è è € õ Ô&¨f¸xÈuÐUÑUÔU€Dðð ð ðñ ô €Eõ  �U‘”ÝÐ:Ð:˜UÐ:Ñ:Ô:Ñ:Ô:ÝÐ>Ð>˜uÐ>Ñ>Ô>Ñ>Ô>Øð	ð ð r;   z/admin/feedback/{feedback_id}c              ƒ   óf   K  — t          j        | |j        ¦  «         ddddœ}| ||j                 dœS )NÚpendingÚapprovedÚ	dismissed)r   rÌ   r  )r÷   r  )r   Úupdate_feedback_reviewr  )r÷   rò   r  Úlabelss       r9   Úreview_feedbackr3  “  sA   è è € õ Ô# K°´Ñ?Ô?Ð?Ø˜z¨kÐ:Ð:€FØ&°F¸4¼=Ô4IÐJÐJÐJr;   z/admin/auditc              ƒ   óJ   K  — t          j        ||¬¦  «        }d„ |D ¦   «         S )N)rÐ   r  c                 ó&   — g | ]}t          d i |¤Ž‘ŒS rÔ   )r   rÕ   s     r9   r:   zaudit_log.<locals>.<listcomp>§  s"   € Ð)Ð)Ð)˜q�IˆNˆN˜ˆNˆNÐ)Ð)Ð)r;   )r   Úget_audit_log)r  rÐ   r  rŸ   s       r9   Ú	audit_logr7     s1   è è € õ Ô!¨°fÐ=Ñ=Ô=€DØ)Ð) DÐ)Ñ)Ô)Ð)r;   rœ   rÂ   c                 ó¶  — t          | d         | d         | d         | d         | d         |                      dd¦  «        | d         d	„ |                      d
¦  «        pg D ¦   «         |                      d¦  «        pd|                      d¦  «        pg |                      d¦  «        pi |                      dd¦  «        |                      d¦  «        |¬¦  «        S )Nr§   r­   Úsource_typeÚtrustedÚneeds_confirmÚ	confirmedFrv   c                 ó&   — g | ]}t          d i |¤Ž‘ŒS rÔ   )r$   rÕ   s     r9   r:   z!_job_response.<locals>.<listcomp>µ  s"   € Ð9Ð9Ð9˜a�fˆkˆk�qˆkˆkÐ9Ð9Ð9r;   rŸ   r¡   rœ   ÚwarningsÚ
validationÚocr_usedr�   )r§   r­   r9  r:  r;  r<  rv   rŸ   r¡   r>  r?  r@  r�   rŒ   )r#   r»   )rÂ   rŒ   s     r9   Ú_job_responserA  ¬  sÕ   € ÝØ�8Œ}Ø�Z”Ø˜Ô&Ø�I”Ø˜/Ô*Ø—'’'˜+ uÑ-Ô-Ø�8Œ}Ø9Ð9 3§7¢7¨6¡?¤?Ð#8°bÐ9Ñ9Ô9Ø—W’W˜^Ñ,Ô,Ð2°Ø—’˜Ñ$Ô$Ð*¨Ø—7’7˜<Ñ(Ô(Ð.¨BØ—’˜ UÑ+Ô+Ø—G’G˜MÑ*Ô*Øðñ ô ð r;   z/pdf/extractr¦   c              ƒ   óÎ  K  — | j         r,| j                              ¦   «                              d¦  «        st          dd¬¦  «        ‚|                      ¦   «         ƒ d {V —†}|st          dd¬¦  «        ‚t          |¦  «        dk    rt          dd¬¦  «        ‚d }	 t          j        rWt           	                    dd¬	¦  «         t          t          j        ¦   «         › d
| j         › �z  }|                     |¦  «         n‚t          j        dd¬¦  «        }	 |                     |¦  «         |                     ¦   «          t#          |j        ¦  «        }|                     ¦   «          n# |                     ¦   «          w xY wt)          j        t,          || j         ¦  «        ƒ d {V —†}n'# t.          $ r}t          dd|› �¬¦  «        |‚d }~ww xY w	 |�4t          j        s(	 |                     d¬¦  «         nM# t2          $ r Y nAw xY wn<# |�4t          j        s)	 |                     d¬¦  «         w # t2          $ r Y w w xY ww w xY w|d         r
|d         sdnd}t5          ||¬¦  «        S )NrL   rŽ   r�   r€   r�   r‘   r’   Tr“   r–   Fr—   r™   zExtraction failed: rš   r:  r;  u;   Digital PDF extracted and validated â€” ready to calculate.zSReview and confirm amounts before calculating (required for OCR / unverified data).©rŒ   )r­   rh   r®   r	   r¯   r°   rZ   rb   rc   r³   r±   r²   r´   rµ   r¶   r·   r¸   r   r¹   rº   rk   rm   r)   rT   ri   rj   rA  )r‹   r¾   rÀ   rÁ   rÂ   r‰   Úmsgs          r9   Úpdf_extractrE  ¿  sÊ  è è € àŒ=ð Q ¤× 3Ò 3Ñ 5Ô 5× >Ò >¸vÑ FÔ Fð QÝ¨Ð4OÐPÑPÔPÐPà—	’	‘”Ð
Ð
Ð
Ð
Ð
Ð
€CØð BÝ¨°LÐAÑAÔAÐAÝ
ˆ3�x„xÐ"Ò"Ð"Ý¨Ð4OÐPÑPÔPÐPà€EðÝÔ%ð 	Ý×Ò T°DÐÑ9Ô9Ð9Ý¥D¤J¡L¤LÐ!BÐ!B°4´=Ð!BÐ!BÑBˆEØ×Ò˜cÑ"Ô"Ð"Ð"åÔ-°VÀEÐJÑJÔJˆCðØ—	’	˜#‘”�Ø—	’	‘”�Ý˜SœX™œ�à—	’	‘”��ø�—	’	‘”��øøøåÔ%Õ&<¸eÀTÄ]ÑSÔSÐSÐSÐSÐSÐSÐSˆˆøÝð Zð Zð ZÝ¨Ð4OÈ#Ð4OÐ4OÐPÑPÔPÐVYÐYøøøøðZøøøð 	ð Ð¥XÔ%?ÐðØ—’¨�Ñ-Ô-Ð-Ð-øÝð ð ð Ø�ðøøøøøð Ð¥XÔ%?ÐðØ—’¨�Ñ-Ô-Ð-Ð-øÝð ð ð Ø�ðøøøð Ðøøøð ˆyŒ>ð	cØ"% oÔ"6ð	cÐEÐEàbð õ
 ˜ cÐ*Ñ*Ô*Ð*sy   ÂA9F' Ä=E' ÅF' Å'E=Å=)F' Æ&H Æ'
GÆ1GÇGÇH ÇG5 Ç5
HÈHÈH?ÈH-È,H?È-
H:È7H?È9H:È:H?z/pdf/{job_id}r§   c              ƒ   óh   K  — t          | ¦  «        }|st          dd¬¦  «        ‚t          |¦  «        S )NrÚ   úJob not foundr€   )r*   r	   rA  )r§   rÂ   s     r9   Úpdf_getrH  ë  s;   è è € å
�&‰/Œ/€CØð EÝ¨°OÐDÑDÔDÐDÝ˜ÑÔÐr;   z/pdf/{job_id}/confirmc              ƒ   óÜ   K  — 	 t          | d„ |j        D ¦   «         ¦  «        }n # t          $ r t          dd¬¦  «        d ‚w xY w|                     d¦  «        rdnd}t          ||¬¦  «        S )	Nc                 ó6   — g | ]}|                      ¦   «         ‘ŒS r5   )Ú
model_dumprÕ   s     r9   r:   zpdf_confirm.<locals>.<listcomp>ö  s    € Ð"EÐ"EÐ"E°a 1§<¢<¡>¤>Ð"EÐ"EÐ"Er;   rÚ   rG  r€   r:  z)Amounts confirmed. You can calculate now.uD   Confirmation saved but validation failed â€” fix highlighted issues.rC  )r(   rŸ   ÚKeyErrorr	   r»   rA  )r§   rò   rÂ   rD  s       r9   Úpdf_confirmrM  ó  sœ   è è € ðOÝ˜&Ð"EÐ"E¸4¼9Ð"EÑ"EÔ"EÑFÔFˆˆøÝð Oð Oð OÝ¨°OÐDÑDÔDÈ$ÐNðOøøøð �7Š7�9ÑÔð	TÐ3Ð3àSð õ
 ˜ cÐ*Ñ*Ô*Ð*s	   „$ ¤Az/pdf/{job_id}/calculatec              ƒ   ó\  K  — 	 t          j        t          | ¦  «        ƒ d {V —†}nx# t          $ r t	          dd¬¦  «        d ‚t
          $ r$}t	          dt          |¦  «        ¬¦  «        |‚d }~wt          $ r$}t	          dt          |¦  «        ¬¦  «        |‚d }~ww xY wt          |d¬¦  «        S )NrÚ   rG  r€   i“  rŽ   z,Calculation complete (deterministic totals).rC  )	rk   rm   r&   rL  r	   ÚPermissionErrorrˆ   Ú
ValueErrorrA  )r§   rÂ   r‰   s      r9   Úpdf_calculaterQ    sâ   è è € ðGÝÔ%¥m°VÑ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<ˆˆøÝð Oð Oð OÝ¨°OÐDÑDÔDÈ$ÐNÝð Gð Gð GÝ¨µC¸±H´HÐ=Ñ=Ô=À3ÐFøøøøÝð Gð Gð GÝ¨µC¸±H´HÐ=Ñ=Ô=À3ÐFøøøøðGøøøå˜Ð&TÐUÑUÔUÐUs!   „ % ¥%BÁ
A)Á)BÁ6BÂBÚstudioc                  ó~   — t           dz  } |                      ¦   «         st          dd¬¦  «        ‚t          | ¦  «        S )Nz
index.htmlrÚ   zEStudio UI not built. Run: cd frontend && npm install && npm run buildr€   )Ú
STUDIO_DIRrd   r	   r   ©Úindexs    r9   Ú_studio_indexrW    sI   € Ý˜Ñ%€EØ�<Š<‰>Œ>ð 
ÝØØZð
ñ 
ô 
ð 	
õ ˜ÑÔÐr;   z/adminÚuic               ƒ   ó‚   K  — t           dz  } |                      ¦   «         st          dd¬¦  «        ‚t          | ¦  «        S )Nz
admin.htmlrÚ   zAdmin UI not foundr€   ©Ú
STATIC_DIRrd   r	   r   ©Úpages    r9   Úadmin_uir^    sC   è è € å˜Ñ$€DØ�;Š;‰=Œ=ð JÝ¨Ð4HÐIÑIÔIÐIÝ˜ÑÔÐr;   z/pdfc               ƒ   ó‚   K  — t           dz  } |                      ¦   «         st          dd¬¦  «        ‚t          | ¦  «        S )Nzpdf.htmlrÚ   zPDF UI not foundr€   rZ  r\  s    r9   Úpdf_uir`  %  sC   è è € å˜
Ñ"€DØ�;Š;‰=Œ=ð HÝ¨Ð4FÐGÑGÔGÐGÝ˜ÑÔÐr;   ú/c               ƒ   ó"   K  — t          ¦   «         S ©N©rW  r5   r;   r9   Úchat_uire  -  ó   è è € å‰?Œ?Ðr;   z/appc               ƒ   ó"   K  — t          ¦   «         S rc  rd  r5   r;   r9   Úapp_uirh  2  rf  r;   z/chat-classicc               ƒ   ó‚   K  — t           dz  } |                      ¦   «         st          dd¬¦  «        ‚t          | ¦  «        S )Nz	chat.htmlrÚ   zChat UI not foundr€   rZ  rU  s    r9   Úclassic_chat_uirj  7  sC   è è € å˜Ñ$€EØ�<Š<‰>Œ>ð IÝ¨Ð4GÐHÑHÔHÐHÝ˜ÑÔÐr;   z/legacy-studioc               ƒ   ó‚   K  — t           dz  } |                      ¦   «         st          dd¬¦  «        ‚t          | ¦  «        S )Nz	saas.htmlrÚ   zLegacy SaaS UI not foundr€   rZ  r\  s    r9   Úlegacy_saas_uirl  ?  sC   è è € å˜Ñ#€DØ�;Š;‰=Œ=ð PÝ¨Ð4NÐOÑOÔOÐOÝ˜ÑÔÐr;   z/studio)Ú	directoryÚhtml)r¹   rX   )rœ   )urk   rP   rµ   r±   Úpathlibr   Útypingr   Úfastapir   r   r   r   r	   r
   r   Úfastapi.middleware.corsr   Úfastapi.responsesr   Úfastapi.staticfilesr   Úpydanticr   r   Úappr   Úapp.authr   r   r   r   Ú
app.configr   r   Úapp.models.schemasr   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   Úapp.pdfr&   r'   r(   r)   r*   r+   Úapp.rag.chainr,   Úapp.saasr-   r`   r.   rZ   rc   Úcors_originsrð   ÚoriginsÚadd_middlewareÚinclude_routerÚ__file__ÚresolveÚparentr[  Úon_eventrC   r»   r{   Úpostr|   rˆ   rÆ   r˜   ÚdictrÊ   ÚlistÚintrØ   rå   rè   râ   rù   rû   Úpatchr  r  r  r  r  r,  r3  r7  rA  rE  rH  rM  rQ  rT  rW  r^  r`  re  rh  rj  rl  rd   Úmountr5   r;   r9   ú<module>r‹     s5  ðØ €€€Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð à RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RØ 2Ð 2Ð 2Ð 2Ð 2Ð 2Ø *Ð *Ð *Ð *Ð *Ð *Ø +Ð +Ð +Ð +Ð +Ð +Ø %Ð %Ð %Ð %Ð %Ð %Ð %Ð %à Ð Ð Ð Ð Ð Ø \Ð \Ð \Ð \Ð \Ð \Ð \Ð \Ð \Ð \Ð \Ð \Ø -Ð -Ð -Ð -Ð -Ð -Ð -Ð -ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð *Ð )Ð )Ð )Ð )Ð )Ø  Ð  Ð  Ð  Ð  Ð  Ø (Ð (Ð (Ð (Ð (Ð (àˆ<‰>Œ>€Ø˜Ñ Ñ.€
à€gØ
$Ø_Øðñ ô €ð MÐ
L˜hÔ3×9Ò9¸#Ñ>Ô>Ð
LÑ
LÔ
L€Ø × Ò ØØ$¨¨Ò-Ð-�'�'°C°5ØØ�%Ø�%ð ñ ô ð ð × Ò �;Ñ Ô Ð àˆT�(‰^Œ^×#Ò#Ñ%Ô%Ô,Ô3°hÑ>€
ð ‡‚ˆiÑÔð+=ð +=ð +=ñ Ôð+=ð` ‡‚ˆ >¸¸
€ÑCÔCð�nð ð ð ñ DÔCðð ‡‚ˆ' ,°f°X€Ñ>Ô>ðG˜ð G¨ð Gð Gð Gñ ?Ô>ðGð, ‡‚Ð
¨\ÀÀ€ÑIÔIà�t˜C‘y”yØ!˜T¨$Ð/Ñ/Ô/Ø˜$ tÐ,Ñ,Ô,ðYGð YGØ
ðYGà�d‘
ðYGð �4‰ZðYGð ð	YGð YGð YGñ JÔIðYGðx ‡‚ˆK˜v˜h€Ñ'Ô'Ø+0¨5°©:¬:ð 2ð 2 Sð 2¸$ð 2ð 2ð 2ñ (Ô'ð2ð ‡‚Ð	¨$Ð/BÔ*CÈ6È(€ÑSÔSØ%* U°2¸!ÀÐ%DÑ%DÔ%Dð 4ð 4˜sð 4ÈÐNaÔIbð 4ð 4ð 4ñ TÔSð4ð
 ‡‚Ð	&Ð7IÐQWÐPX€ÑYÔYð¨#ð Ð2Dð ð ð ñ ZÔYðð& ‡‚Ð)°°€Ñ9Ô9ð2¨#ð 2°$ð 2ð 2ð 2ñ :Ô9ð2ð
G˜cð G cð Gð Gð Gð Gð ‡‚ˆ+Ð&6¸f¸X€ÑFÔFð ð Ð4Dð ð ð ñ GÔFðð$Bð Bð Bð Bð B 	ñ Bô Bð Bð ‡‚Ð$¨F¨8€Ñ4Ô4ð4Øð4à
#ð4ð 
ð4ð 4ð 4ñ 5Ô4ð4ðð ð ð ð �9ñ ô ð ð
ð ð ð ð �Iñ ô ð ð
 ‡‚ˆ.¨¸g¸Y€ÑGÔGð-˜Lð -¨]ð -ð -ð -ñ HÔGð-ð ‡‚Ð	¨4°Ô+=ÀWÀI€ÑNÔNð ˜ t°]ÐCÑCÔCØ ˜5¨°!¸Ð:Ñ:Ô:Ø˜ tÐ,Ñ,Ô,Ø ˜5¨Ð.Ñ.Ô.Ø�˜r a¨CÐ0Ñ0Ô0Ø�%  aÐ(Ñ(Ô(ð-ð -Ø�c˜7˜7Ð#5Ñ6Ô6Ð6Ô7ð-à�$‰Jð-ð �D‰jð-ð �$‰Jð	-ð
 �D‰jð-ð ð-ð ð-ð 
ˆ,Ôð-ð -ð -ñ OÔNð-ð( ‡‚Ð	#¨7¨)€Ñ4Ô4ð ˜ tÐ,Ñ,Ô,Ø ˜5¨Ð.Ñ.Ô.Ø�˜t¨¨eÐ4Ñ4Ô4ð	ð Ø�c˜7˜7Ð#5Ñ6Ô6Ð6Ô7ðà�$‰Jðð �D‰jðð ð	ð
 
ðð ð ñ 5Ô4ðð< ‡‚Ð*°'°€Ñ;Ô;ðKØðKà
ðKð �c˜7˜7Ð#5Ñ6Ô6Ð6Ô7ðKð 
ð	Kð Kð Kñ <Ô;ðKð ‡‚ˆ¨¨Y¬¸w¸i€ÑHÔHð �˜r a¨CÐ0Ñ0Ô0Ø�%  aÐ(Ñ(Ô(ð*ð *Ø�c˜7˜7Ð#5Ñ6Ô6Ð6Ô7ð*àð*ð ð*ð 
ˆ)„_ð	*ð *ð *ñ IÔHð*ðð �tð  cð °>ð ð ð ð ð& ‡‚ˆ.¨¸u¸g€ÑFÔFØ)-¨¨c©¬ð (+ð (+˜Jð (+°~ð (+ð (+ð (+ñ GÔFð(+ðV ‡‚ˆ¨¸u¸g€ÑFÔFð˜#ð  .ð ð ð ñ GÔFðð ‡‚Ð
!°.ÈÀw€ÑOÔOð
+˜cð 
+Ð):ð 
+¸~ð 
+ð 
+ð 
+ñ PÔOð
+ð ‡‚Ð
#°NÈ%È€ÑQÔQð	V ð 	V¨ð 	Vð 	Vð 	Vñ RÔQð	Vð ˜(Ñ"€
ð�|ð ð ð ð ð ‡‚ˆ˜˜€ÑÔðð ñ  Ôðð ‡‚ˆ�t�f€ÑÔðð ñ Ôðð ‡‚ˆ�D�6€ÑÔðð ñ Ôðð ‡‚ˆ�t�f€ÑÔðð ñ Ôðð ‡‚ˆ ˜v€Ñ&Ô&ðð ñ 'Ô&ðð ‡‚Ð	  €Ñ'Ô'ðð ñ (Ô'ðð ×ÒÑÔð [Ø‡I‚Iˆi˜˜¨s¨s°:©¬ÀTÐJÑJÔJÐQY€IÑZÔZÐZÐZÐZð[ð [r;   