+
    8j-                        R t ^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIt^ RIt^ RI	H
t
HtHtHtHt ]! ]4      P                   t]R,          R,          t]P'                  RRR7       ]R,          R	,          P)                  4       t]P,                  R
,          t]P,                  R,          t]P,                  P3                  RR4      tRtRt]P:                  ! ]R7      t]
! ]RRR7      t R%R R llt!R R lt"R R lt#] PI                  R4      R 4       t%] PM                  R4      R 4       t'] PM                  R4      R 4       t(] P3                  R4      R  4       t)]R!8X  d   ] PU                  R"R#RR$7       R# R# )&u  Persian Tutor — Flask backend.

Endpoints:
  GET  /persian              → chat UI
  POST /api/ask              → {"query": "..."} → {"persian", "translit", "english", "notes", "audio_url"}
  POST /api/breakdown        → word-by-word table for a Persian phrase
  GET  /static/audio/<file>  → cached TTS mp3

Setup:
  1. cp .env.template .env  (fill in keys)
  2. pip install -r requirements.txt
  3. python app.py  (or bash run.sh)
N)Path)Flaskjsonifyrender_templaterequestsend_from_directorystaticaudioT)parentsexist_okpromptsztutor_system.mdANTHROPIC_API_KEYELEVENLABS_API_KEYELEVENLABS_VOICE_ID eleven_multilingual_v2zclaude-sonnet-4-6)api_keyz/static)static_url_pathstatic_folderc                <    V ^8  d   QhR\         R\        R\         /# )   
user_queryhistoryreturn)strlist)formats   "*/data/cameron/life/projects/persian/app.py__annotate__r   +   s!     	  	 3 	  	  	     c                    T;'       g    . RRRV /.,           p\         P                  P                  \        R\        VR7      pVP
                  ^ ,          P                  # )zASend query to Claude tutor with system prompt + optional history.roleusercontenti   )model
max_tokenssystemmessages)clientr'   createCLAUDE_MODELSYSTEM_PROMPTr#   text)r   r   r'   resps   &&  r   
claude_askr.   +   sY    2669j"I!JJH??!!	 " D <<?r   c                0    V ^8  d   QhR\         R\        /# r   r,   r   )r   dict)r   s   "r   r   r   7   s      s t r   c                    RRRRRRRR/pR	 FZ  w  r#\         P                  ! RV R2V \         P                  4      pV'       g   K9  VP                  ^4      P	                  4       W&   K\  	  WR&   V# )
z9Extract structured fields from tutor's markdown response.persianr   translitenglishnotesz\*\*z:\*\*\s*(.+?)(?=\n\*\*|\Z)raw))r3   Persian)r4   Transliteration)r5   English)r6   Notes)researchDOTALLgroupstrip)r,   outkeylabelms   &    r   parse_tutor_responserE   7   ss    b*b)R"
EC
 IIeW$>?ryyQ1wwqz'')CH JJr   c                0    V ^8  d   QhR\         R\         /# r0   )r   )r   s   "r   r   r   G   s     & & & &r   c                   \         P                  ! V P                  R4      4      P                  4       R,          p\        V R2,          pVP                  4       '       d   RV R2# \        '       g   \        R4      h\        P                  ! R\         2R\        RR	R
R/RV R\        RRRRR//^<R7      pVP                  4        VP                  VP                  4       RV R2# )zRGenerate Persian (+ optional bilingual) audio. Returns relative URL to audio file.zutf-8:N   Nz.mp3z/static/audio/u[   ELEVENLABS_VOICE_ID not set — pick a voice at elevenlabs.io/voice-library and set in .envz,https://api.elevenlabs.io/v1/text-to-speech/z
xi-api-keyzContent-Typezapplication/jsonAcceptz
audio/mpegr,   model_idvoice_settings	stabilityg      ?similarity_boostg      ?)headersjsontimeout)hashlibsha256encode	hexdigest	AUDIO_DIRexistsr   RuntimeErrorrequestspostELEVENLABS_KEYELEVENLABS_MODELraise_for_statuswrite_bytesr#   )r,   rB   out_pathrs   &   r   elevenlabs_ttsr`   G   s     ..W-
.
8
8
:3
?CcU$<'HuD))xyy
67J6KL..l
 D({C1CTJ

 	A #C5%%r   z/persianc                      \        R 4      # )z	chat.html)r    r   r   chat_uirc   g   s    ;''r   z/api/askc                     \         P                  ! 4       p V P                  R R4      P                  4       pV P                  R. 4      pV'       g   \	        RR/4      R3# \        W4      p\        V4      pVR,          pVR,          '       d   RVR,           2p VR,          '       d   \        V4      MR	VR
&   \	        V4      #   \         d   pR	TR
&   \        T4      TR&    R	p?L.R	p?ii ; i)queryr   r   errorzempty query  r3   r4   zIn Persian, you say: N	audio_urlaudio_error)
r   get_jsongetr@   r   r.   rE   r`   	Exceptionr   )datare   r   r7   parsed
audio_textes          r   api_askrq   l   s    DHHWb!'')Ehhy"%G/0#55
U
$C!#&F 	"Jj -VI->,?@
'<B9<M<MnZ8SW{
 6?	  '"{ #A}'s   C )C C,C''C,z/api/breakdownc                     \         P                  ! 4       p V P                  RR4      P                  4       pV'       g   \	        RR/4      R3# \        RV 24      p\	        RV/4      # )z,Word-by-word breakdown for a Persian phrase.phraser   rf   zempty phraserg   zABreak down this Persian phrase word-by-word in a markdown table: r7   )r   rj   rk   r@   r   r.   )rm   rs   r7   s      r   api_breakdownrt      sf     DXXh#))+F01366
XY_X`a
bCE3<  r   z/healthc                  (    R RR\        \        4      /# )okT	voice_set)boolr   rb   r   r   healthry      s    $T*=%>??r   __main__z0.0.0.0i  )hostportdebug)N)+__doc__rQ   rO   osr<   pathlibr   	anthropicrX   flaskr   r   r   r   r   __file__parentROOTrU   mkdir	read_textr+   environANTHROPIC_KEYrZ   rk   r   r[   r*   	Anthropicr(   __name__appr.   rE   r`   routerc   rY   rq   rt   ry   runrb   r   r   <module>r      st     	 	    O O H~8Og%	 	t ,	!$55@@B

./01jjnn%:B? + "			]	3HixH
	  &@ :( ( * 4 
! ! @ @ zGGTG2 r   