
    j&                        d Z ddlmZ ddlZddlZddlZddlmZ ddlZddl	m
Z
 ddlmZ ddlmZ d	Zd
Z G d de      Z G d de      ZddZddZd Zy)a  Quick-read v3: two-pass approach + Anthropic extended thinking.

Pass 1: forced per-lead ST/T enumeration with thinking enabled. The model
        builds an unbiased catalog of every visible finding before naming
        any diagnosis.
Pass 2: synthesis. Sees the original image + pass-1 output, produces the
        formal reading. Thinking enabled.

Total time ~60-150s. Designed to be invoked from a background worker, not
synchronously from an HTTP-edge-timeout-bounded request.
    )annotationsN)Path)HTTPException)	BaseModel   )configu  You are an expert cardiac electrophysiologist examining a 12-lead ECG.

DO NOT yet attempt to diagnose. Your only job in this pass is to systematically catalog every visible finding so a synthesis step later can interpret them.

For EACH of the 13 leads visible (I, II, III, aVR, aVL, aVF, V1, V2, V3, V4, V5, V6, and the rhythm strip — typically lead II at the bottom), describe what you see in plain language:

1. **P wave** — present? visible morphology? upright/inverted?
2. **PR interval** — short/normal/long?
3. **QRS** — narrow/wide? notching? deep S? tall R? Q wave presence + width + depth (Q ≥40 ms wide or ≥25% R height = pathologic)?
4. **ST segment** — elevation (mm), depression (mm), morphology (concave/convex/flat/downsloping/upsloping), or isoelectric?
5. **T wave** — upright, flat, inverted, biphasic, hyperacute (tall + symmetric)?
6. **Rhythm strip features** — regularity, P-QRS relationship, any ectopy?

Also note overall:
- Heart rate (count R-R intervals in the rhythm strip)
- QRS axis (estimate quadrant from leads I and aVF)
- Image quality issues (rotation, low contrast, partial obstruction)

Format as a structured per-lead table or bulleted list. Be quantitative whenever possible (mm for ST shifts, ms for intervals).

Do NOT name a diagnosis. Do NOT use the words "STEMI", "NSTEMI", "MI", "ischemia" yet — those are interpretive labels for the synthesis pass.

Output plain markdown, no JSON, no code fences. End with: "---END PASS 1---" on its own line.u	  You are an expert cardiac electrophysiologist. You previously examined a 12-lead ECG and produced this objective per-lead catalog:

═══════════════════════════════════════════════════════════════════
PASS 1 OUTPUT (your prior cataloging step)
═══════════════════════════════════════════════════════════════════

{pass1_text}

═══════════════════════════════════════════════════════════════════
END PASS 1 OUTPUT
═══════════════════════════════════════════════════════════════════

Now look at the ECG image again WITH the pass-1 catalog in mind and produce the formal reading.

Required structure (plain markdown, no JSON, no code fences):

**Diagnosis** — ONE sentence. If STEMI/OMI criteria are met anywhere, lead with that explicitly and name the territory. Otherwise state the dominant clinical pattern.

**Rate** — bpm.

**Rhythm** — primary rhythm + regularity.

**Axis** — quadrant + estimated degrees.

**Intervals** — PR / QRS / QT(c) in ms.

**ST/T summary table** — Group by territory (inferior, lateral, anteroseptal, anterior, lateral precordial, posterior). For each: what was elevated, depressed, or otherwise abnormal, with mm where measurable.

**Reciprocal changes** — Explicitly check every ST elevation against its anatomical opposite. State yes/no per pairing.

**Q waves** — Pathologic? Which leads? Significance?

**Final synthesis** — 2-3 sentences integrating the findings into a clinical picture.

**Why — three teaching pearls** — each pearl must reference specific leads or features from your pass-1 catalog and explain the reasoning chain.

CRITICAL:
- Use the pass-1 catalog as your evidence base. Do not invent findings not present in pass 1.
- If ST elevation in any territory was noted in pass 1, you MUST address whether it meets STEMI/OMI criteria (and check for reciprocal changes).
- If pass 1 noted "no ST elevation anywhere", confidently call this non-ischemic.
- Do NOT default to "nonspecific ST-T changes" — name a pattern.c                      e Zd ZU ded<   y)QuickReadRequeststr	image_urlN__name__
__module____qualname____annotations__     B/var/www/adampowell.pro/apps/ekg-tutor/interpret/src/quick_read.pyr
   r
   _   s    Nr   r
   c                  ,    e Zd ZU ded<   ded<   ded<   y)QuickReadResponser   markdownint
elapsed_msmodelNr   r   r   r   r   r   c   s    MOJr   r   c                f   | j                  d      rt        | dd        }n+| j                  d      rt        |       }nt        d|        |j                         st	        t        |            |j                  j                         }dddddj                  |d      }|j                         |fS )	Nzfile://   /zunsupported image_url scheme: z	image/pngz
image/jpegz
image/webp)z.pngz.jpgz.jpegz.webp)

startswithr   
ValueErrorexistsFileNotFoundErrorr   suffixlowerget
read_bytes)r   pathext
media_types       r   _read_image_bytesr)   i   s    I&IabM"			c	"I9)EFF;;=D	**
++


C	
 
c#{  ??j((r   c                    g }| j                   D ].  }t        |dd      dk(  s|j                  |j                         0 dj	                  |      j                         S )zMConcatenate text blocks from an Anthropic response, skipping thinking blocks.typeNtext )contentgetattrappendr,   joinstrip)responsepartsblocks      r   _extract_textr6   |   sQ    E!!5&$'61LL$ " 775>!!r   c                @    | j                  dt              dd       }y )Nz/v1/quick-read)response_modelc                   t         j                  st        dd      | j                  }|j	                  d      r:t
        j                  j                  t         j                  |t        d      d        }	 t        |      \  }}t        j                  |      j!                  d      }d	d
||dd}t#        j$                  t         j                  d      }t
        j&                  j)                  dd      }t+        t
        j&                  j)                  dd            }	d|	d}
t-        j.                         }	 |j0                  j3                  |dd|
d|dt4        dgdg      }t9        |      }|st        dd      t:        j=                  |      }	 |j0                  j3                  |d d|
d|d|dgdg      }t9        |      }|st        dd"      |d#z   d$z   |j?                  d%d&      jA                         z   d'z   }t+        t-        j.                         |z
  d(z        }tC        |||jD                  )      S # t        $ r}t        dd|       d }~wt        $ r}t        dt        |            d }~ww xY w# t"        j6                  $ r}t        dd|       d }~ww xY w# t"        j6                  $ r}t        dd!|       d }~ww xY w)*Ni  z ANTHROPIC_API_KEY not configured)status_codedetailz	/uploads/i  zimage not found: i  asciiimagebase64)r+   r(   data)r+   sourcei,  )api_keytimeoutEKG_TUTOR_INTERPRET_MODELzclaude-sonnet-4-6#EKG_TUTOR_INTERPRET_THINKING_BUDGET4000enabled)r+   budget_tokensi    g      ?userr,   )r+   r,   )roler.   )r   
max_tokenstemperaturethinkingmessagesi  zanthropic pass-1 error: zempty pass-1 response)
pass1_texti   zanthropic pass-2 error: zempty pass-2 responsez

---

zF<details><summary><em>Show per-lead analysis (pass 1)</em></summary>

z---END PASS 1---r-   z

</details>i  )r   r   r   )#r   anthropic_api_keyr   r   r   osr&   r1   
upload_dirlenr)   r!   r   r   r>   standard_b64encodedecode	anthropic	Anthropicenvironr$   r   time	monotonicrM   createPASS1_PROMPTAPIErrorr6   PASS2_PROMPT_TEMPLATEformatreplacer2   r   r   )reqpath_strr?   r(   eimg_b64image_contentclientr   thinking_budgetthinking_configstartpass1rN   pass2_promptpass2	synthesisr   r   s                      r   
quick_readz'register_quick_read.<locals>.quick_read   s   ''C8Z[[=={+ww||F$5$5xK@P@Q7RSH	@0:D* ++D188A'z7S

 $$,,
 

:<OP bjjnn-RTZ[\#,O 	XOO**( !'$1FL3Y#Z + E #5)
C8OPP -33z3J	XOO**( !'$1FL3Y#Z + E "%(	C8OPP XY   !3R8>>@A 	 	 $..*U2d:;
 !++
 	
[ ! 	QC:KA38OPP 	@CA??	@H !! 	XC:RSTRU8VWW	X, !! 	XC:RSTRU8VWW	XsT   6I 9-J )J/ 	J
IJ&I<<JJ,J''J,/KKK)r`   r
   )postr   )apprm   s     r   register_quick_readrp      s&    XX/@XAZ
 BZ
r   )r   r   returnztuple[bytes, str])rq   r   )__doc__
__future__r   r>   rP   rX   pathlibr   rU   fastapir   pydanticr   r   r[   r]   r
   r   r)   r6   rp   r   r   r   <module>rw      sa   
 #  	    !  a2(F Vy 	 )&"\
r   