
    %^j4              	           S r SSKrSSKrSSKJrJrJrJr  SSKJ	r	J
r
  SSKJr  SSKJr  \R                  " \5      rSrS	rS
rSr " S S5      r\" 5       rS\S\S\\\\   4   4S jrSS\4S jjrS rg)u   
Router für LLM-Enrichment — ausschließlich OpenAI.
3-Level Routing: gpt-5-nano → gpt-5-mini → gpt-5.2

Kein Anthropic. Kein externer Fallback.
    N)DictAnyOptionalTuple   )validateget_estimated_fields)enrichment_logger)OpenAIProvider2   g333333?g      ?gffffff?c                       \ rS rSrSrS rS\S\\   4S jr	S\
4S jrS\4S jrS	 r SS\S\S\\   S\\\\   4   4S jjrSS\4S jjrS\4S jrSrg
)EnrichmentRouter   u5  
Router mit 3 OpenAI-Leveln und 4-Phasen Cost Guard.

HARTE REGELN:
1. Immer mit Level 1 (günstig) starten
2. Jeder Provider genau 1 Versuch
3. Validierungsfehler → Eskalation (außer bei Throttle)
4. Alle Level fail → None (kein save)
5. Cost Guard: 4 Phasen (normal → warning → throttled → stop)
c                     SSS S.SSS S.SSS S./U l         SU l        SU l        SU l        SU l        [        5       U l        [        R                  S	5        g )
Nr   OPENAI_MODEL_LOW)level	model_envprovider   OPENAI_MODEL_MID   OPENAI_MODEL_HIGHr   uE   EnrichmentRouter (OpenAI-only) initialized: nano → mini → gpt-5.2)	levelsconsecutive_failsmax_consecutive_failstotal_counterlevel3_countersetdownprioritizedloggerinfoselfs    /app/llm/router.py__init__EnrichmentRouter.__init__&   si    &8tL&8tL&9tL
 "#%&"    #u[\    level_configreturnc                 `   US   co  [         R                  " US   5      nU(       d  [        R                  SUS    S35        g [	        U5      US'   [        R                  SUS    SU 35        US   $ US   $ ! [         a(  n[        R                  SUS    S	U 35         SnAgSnAff = f)
zLazy-load OpenAI Provider.r   Nr   zEnv var z nicht gesetztz
Provider Lr   z
 geladen: z	 Fehler: )osgetenvr    errorr   debug	Exception)r#   r(   modeles       r$   _get_providerEnrichmentRouter._get_provider:   s    
#+IIl;78Ex[(A'B.QR+9%+@Z(z,w*?)@
5'RS J''|J''  z,w*?)@	!MNs   ,A; ;
B-B((B-c                 V    U R                   S:X  a  gU R                  U R                   -  $ )z#Berechnet aktuellen Level-3 Anteil.r   g        )r   r   r"   s    r$   _get_level3_ratio"EnrichmentRouter._get_level3_ratioI   s*    """T%7%777r'   c                     U R                   [        :  a  gU R                  5       nU[        :  a  gU[        :  a  gU[
        :  a  gg)zf
Bestimmt den aktuellen Cost-Guard-Zustand.

Returns:
    'normal' | 'warning' | 'throttled' | 'stop'
normalstop	throttledwarning)r   
MIN_SAMPLEr5   STOP_THRESHOLDTHROTTLE_THRESHOLDWARN_THRESHOLDr#   ratios     r$   _get_cost_guard_state&EnrichmentRouter._get_cost_guard_stateO   sH     
*&&(>!%%>!r'   c                    U R                  5       nU R                  5       nUS:X  a  gUS:X  aC  [        R                  SU R                   SU R
                   SUS-  S S	[        S-  S
 S3	5        gUS:X  a)  [        R                  SUS-  S S[        S-  S
 S35        gUS:X  a  [        SUS-  S S[        S-  S
 S35      eg)u  
4-Phasen Cost Guard nach Dottores Spezifikation.

Phase 1 (< MIN_SAMPLE):   Kein Eingriff, nur Logging
Phase 2 (> WARN):         Warnung, kein Eingriff
Phase 3 (> THROTTLE):     Level 3 eingeschränkt (→ nur kritische Fehler)
Phase 4 (> STOP):         Hard Stop, raise Exception
r8   Nr;   zLEVEL3_HIGH_USAGE: / (d   .1fu   %) — Warnschwelle .0fu    % überschritten. Kein Eingriff.r:   z%COST_GUARD_THROTTLED: Level-3 Anteil z% > z+%. Level 3 nur noch bei kritischen Fehlern.r9   z COST_GUARD_STOP: Level-3 Anteil z%. Prozess gestoppt.)
rB   r5   r    r;   r   r   r?   r>   r/   r=   )r#   staterA   s      r$   _check_cost_guard"EnrichmentRouter._check_cost_guardc   s    **,&&(HINN%d&9&9%:!D<N<N;O P#Ic?"6~c7I#6N O!"
 KNN7c	#d%c)#..Y[ F?259S/!#%c**>@  r'   Nprompt
contact_id
max_budgetc                 F   Uc   [        [        R                  " SS5      5      nSn0 nU R                  U R                  :  a'  [
        R                  SU SU R                   S35        g U R                  5         U =R                  S
-  sl
        U R                  5       nSnU R                   GH  n	U	S   n
XC:  a?  [
        R                  SU SU SU SU
S
-
   S3	5        [        R                  " UUUUSS9    gU
S:X  a  US:X  a}  U(       a0  UR!                  S5      (       a  [
        R#                  SU S35        OFU(       a   SU;   a  [
        R#                  SU S35        O[
        R#                  SU SU S35          GOMU R%                  U	5      nUc  M  UR&                  nXR(                  ;   a$  U
S:  a  [
        R+                  SU SU 35        GM+   UR-                  U5      n[/        USS5      nXN-  nXSU
 3'   U
S:X  a  U =R0                  S
-  sl        [3        U5      u  nn[5        U5      nU(       a  SOUn[        R6                  " UUU
UUUUS9  U(       aJ  SU l        [        R                  " UUUUSS9  [
        R#                  SU SU S U
 S!U SU S"35        SU4s  $ [
        R+                  SU S#U S$U S%35        GM!     U =R                  S
-  sl        [        R                  " UUUUSS9  [
        R                  SU S*U R                   SU R                   35        U R                  U R                  :  a#  [
        R                  S+U R                   S,35        g! [         a%  n[
        R                  SU S	U 35         SnAgSnAff = f! [         aw  n[/        USS5      nXN-  nXSU
 3'   S&[9        U5      SS'  3n[        R6                  " UUU
SSU[9        U5      S(9  [
        R                  SU S)U S$U S%35         SnAGMn  SnAff = f)-u   
Führt Enrichment durch: Level 1 → 2 → 3.

Args:
    max_budget: Token-Budget pro Kontakt (Override für .env MAX_TOKENS_PER_CONTACT)

Returns:
    (True, data) bei Erfolg
    (False, None) wenn alle Level scheitern oder Budget überschritten
NMAX_TOKENS_PER_CONTACT25000r   [z] PROCESS_STOPPED: u    konsekutive Totalausfälle)FNu   ] Cost Guard ausgelöst: r   r   z] BUDGET_EXCEEDED: rE   z Tokens nach Lu    → needs_manualT)rN   budget_usedrO   tokens_per_levelexceededr   r:   MISSING_CRITICALu3   ] Throttled aber CRITICAL fehlt → Level 3 erlaubt	API_ERRORu/   ] Throttled aber API-Fehler → Level 3 erlaubtu*   ] THROTTLE: Level 3 übersprungen (Grund: )z] Skip downpriorisiert: tokens_usedL)rN   
model_usedr   successvalidation_resultrZ   estimated_fieldsFz] SUCCESS mit z (Lz) | Budget: z Tokensz!] Validierung fehlgeschlagen auf : u    → EskalationzAPI_ERROR: r   )rN   r\   r   r]   r^   rZ   	error_msgz] API-Fehler auf z)] ALLE LEVEL FEHLGESCHLAGEN. Konsekutiv: zProzess wird gestoppt nach u    konsekutiven Totalausfällen.)intr+   r,   r   r   r    r-   rK   r/   criticalr   rB   r   r;   r
   log_contact_budget
startswithr!   r2   r0   r   r.   extractgetattrr   r   r	   log_attemptstr)r#   rM   rN   rO   rT   rU   r1   cost_guard_statelast_fail_reasonr(   	level_numr   
model_namedatatokensvalidreason	estimateds                     r$   run_enrichmentEnrichmentRouter.run_enrichment   s    RYY'?IJJ !!T%?%??LLJ<2--..IK 	""$
 	a557   KKL$W-I (
|#6{m1ZL Q&]O+<> "44) +)%5! # A~"2k"A#(8(C(CDV(W(WKKJ<'Z[ &+9I*IKKJ<'VW KKJ< (##3"4A7 )),7H!J 111i!mq,DZLQRA''/ =!< %4:1YK1 >''1,' !)v06	+04f  "--))#!&, &%. -.D*%88#-$/#-)9!& KKJ<~j\YK P##.-qGE  :%
|#DZLPRho/u (d 	!#,,!#!-	
 	
| 112!D4N4N3OQ	

 !!T%?%??OO-d.H.H-I J0 1
 _  	OOa
|+DQCHI	R   =!<%4:1YK1%0Q#> !--))#!&1 &!!f 
|#4ZL1#_U s8   *M- CN,N-
N7NN
P )A+PP 	thresholdc           
      2   [         R                  R                  5        Hu  u  p#US   U:  d  M  US   US   -  nUS:  d  M#  X R                  ;  d  M4  U R                  R	                  U5        [
        R                  SU SUS-  S SUS    S	35        Mw     g
)u   
Nach N Requests: Modelle mit <70% Erfolg downpriorisieren.
Bleibt als Fallback aktiv — wird nur nicht mehr als Level 1 genutzt.
attempts	successesgffffff?zModel r`   rG   rH   z% < 70% nach u    Requests → downpriorisiertN)r
   statsitemsr   addr    r;   )r#   ru   r0   ry   rates        r$   check_model_performance(EnrichmentRouter.check_model_performance6  s    
 .3399;LEZ I-[)E*,==$;50D0D#D((,,U3NN r$s(3} ,--JL <r'   c                     U R                  5       nU R                  U R                  U R                  [	        US5      U R                  5       [        U R                  5      [        R                  S.$ )Nr   )r   total_requestslevel3_requestslevel3_ratiorj   r   model_stats)
r5   r   r   r   roundrB   listr   r
   ry   r@   s     r$   	get_statsEnrichmentRouter.get_statsE  sa    &&(!%!7!7"00#22!%O $ : : <#D$8$89,22
 	
r'   )r   r   r   r   r   r   Nr   )__name__
__module____qualname____firstlineno____doc__r%   r   r   r   r2   floatr5   ri   rB   rK   rb   r   boolrs   r}   r   __static_attributes__ r'   r$   r   r      s    	](($ (8N3K (85 8s ("J 48mS mc m#+C=m<A$QUBV<Wm^ 

4 

r'   r   rM   rN   r)   c                 ,    [         R                  X5      $ r   )routerrs   )rM   rN   s     r$   rs   rs   V  s      44r'   ru   c                 .    [         R                  U 5        g r   )r   r}   )ru   s    r$   check_performancer   Z  s    
""9-r'   c                     [         R                  " 5         [        R                  5       n U S   nSSSSS.n[	        S5        [	        SU S	    S
35        [	        SU S    35        [	        SU S    SU S    SU S   S-  S S35        [	        SUR                  X5       SUR                  5        S[        S-  S S[        S-  S S[        S-  S S35        [	        SU S    35        g )Nrj   z[OK]z[WARN]z
[THROTTLE]z[STOP])r8   r;   r:   r9   z
Router Status:z  Konsekutive Fehler:  r   z/3z  Total Requests:      r   z  Level-3 Anteil:      r   rE   rF   r   rG   rH   z%)z  Cost Guard:           z [Schwellen: rI   z% / z%]z  Downpriorisiert:     r   )
r
   print_statsr   r   printgetupperr?   r>   r=   )ry   rJ   state_iconss      r$   r   r   ^  s1   !!#E$%E!	K 
	#E*=$>#?r
BC	#E*:$;#<
=>	#E*;$<#=QuEU?V>W XN#C',B0 1	#KOOE$A#B!EKKM? S'+C05G5KC4PPTUcdgUghkTllnp q	#E*;$<#=
>?r'   r   )r   r+   loggingtypingr   r   r   r   	validatorr   r	   r    r
   openai_providerr   	getLoggerr   r<   r?   r>   r=   r   r   ri   rb   r   rs   r   r   r   r'   r$   <module>r      s    
  - - 5 % +			8	$ 
 u
 u
r	 
	53 5C 5E$:N4O 5. .@r'   