
    7j+              	      >   d Z ddlmZ ddlZddlmZ ddlZddlmZ ddl	m
Z
mZ ddlmZ d	Zd
ZdZddddZddddZdZdddidddg dddg ddddiddidg ddddddidddiddg dddZ G d  d!e      Zd*d"Zd+d#Zd,d$Zd-d%Zd.d&Zd/d'Zd0d(Zd1d)Zy)2u  Claude reviews of a draft forecast, informed by the current guidance and by
similar past forecasts. Claude advises; it never sets or dictates a rating.

Request layout (prompt-cache friendly — stable content first):
  system[0]  frozen reviewer instructions            (never changes)
  system[1]  guidance + center config for this run   (stable per config version) <- cache breakpoint
  messages   the draft, analogs, and the task         (varies every call)
    )annotationsN)Decimal   )ASPECTS)AFP_PROBLEM_NAMESLIKELIHOOD_NAMES)get_settingsz2026-09-14.1zserver-side-fallback-2026-07-01i }  )g      @g      9@g      @g      ?)g      $@g      I@g      )@g      ?)claude-opus-5zclaude-fable-5-1zclaude-opus-4-8zReview this draft for clarity and internal consistency: do the bottom line, danger ratings, avalanche problems and travel advice agree with each other and with the guidance? Flag jargon, passive voice, and missing what/where/what-to-do.a	  Write a suggested Bottom Line for this draft (three sentences or fewer; what is it, where is it, what can readers do) from the structured problems and ratings, following the writing guidance. Also note anything in the draft that makes the bottom line hard to write.u&  Sanity-check the draft's danger ratings against its problems (likelihood, size, location), the CMAH hazard-chart suggestion, the on-the-line guidance, and the similar past forecasts. Where a rating looks high or low, explain why and what to double-check — never state which rating is correct.)review_draftdraft_bottom_linesanity_checku  You are an experienced avalanche forecaster and editor reviewing a public avalanche forecast before it is published.

Ground rules:
- The forecaster decides. You advise. Never state what a danger rating must be, and never present your view as the correct rating.
- The current guidance supplied below (USDA FS Avalanche Forecast Guidance, the Conceptual Model of Avalanche Hazard, and the center's problem descriptions) is the standard. Past forecasts are shown only as context: many were written under older conventions (for example "Normal Caution" or different problem choices) and may not follow current guidance — do not treat them as correct just because they were published.
- Be concise and practical. Cite the guidance section when it supports a point (e.g. "FG §3.3", "FG Table 1").
- Do not rewrite the whole forecast.
- Respond only with the JSON object described by the output schema.objecttypestringarray)info
suggestion	important)r   enum)bottom_linedangerproblemstravel_advice
discussiongeneral)severitysectiontextguidance_refF)r   
propertiesrequiredadditionalProperties)r   items)summaryissuessuggested_bottom_linequestions_for_forecasterc                      e Zd Zy)AIUnavailableN)__name__
__module____qualname__     0/var/www/avy-guidance/api/app/services/claude.pyr)   r)   I   s    r.   r)   c                 <    t        t               j                        S )N)boolr	   anthropic_api_keyr-   r.   r/   enabledr3   M   s    0011r.   c                   d| ddg}| d   D ]  }|j                  d|d           |j                  |d          dD ]@  \  }}|j                  |      s|j                  | d	d
j                  ||         z          B |j                  d      s|j                  d|d            |ddgz  }| d   D ]Q  }|d   s	|j                  d|d    d
|d    d	|d    d|j                  dd       d|j                  dd       
       S |ddgz  }|| d   D cg c]  }d|d    d	|d     c}z  }|ddgz  }| d   j                         D ]+  \  }}	|j                  | d	d
j                  |	      z          - |dddt	        j
                  | d   d       d!t	        j
                  | j                  d"i       d        gz  }d#j                  |      S c c}w )$z;Stable per config version: the guidance the review applies.z# Guidance for  z<## Avalanche problem types (FG Table 1; center descriptions)problemTypesz### namedesc))
Initiation
initiation)zTermination/transitiontermination)Considerationsconsiderations:  saczCenter notes: u#   ## Danger levels (NAPADS, FG §2.1)dangerLevelslevelz- advicez Likelihood: 
likelihoodz Size/distribution: sizeDistu0   ## On-the-line and special situations (FG §2.2)	onTheLinetitler   u   ## Writing guidance (FG §3)writingzM## CMAH hazard-chart danger table used by the tool (a suggestion, not a rule)zcRows likelihood 1-5 (Unlikely..Certain); columns D1, D2, D3, D4-5; values 0-5 (No Rating..Extreme):dangerTableT)	sort_keyszBand labels: 
bandLabels
)appendgetjoinr#   jsondumps)
configcenter_namelinesplabelkeydtr   r#   s
             r/   guidance_contextrZ   Q   s[   {m,b2pqEN# 6tAfI;'(QvY J 	>JE3uuSzwb\CHHQsV,<<=	> 55<LL>!E(456 
b788EN# XW:LL2aj\1V9+R(}MRSRWRWXdfhRiQjj~  @A  @E  @E  FP  RT  @U  V  W  XX 
bDEEE	f[6IJ1W:,b6,JJE	b011E +113 7y^chhuo567	basjj.$?djjL")EQUVWXZ ZE 99U Ks   Gc                    i }| D ]6  }|j                         \  }}|j                  |g       j                  |       8 t        t              D ci c]  \  }}||
 c}}dj                  fd|j                         D              S c c}}w )Nz; c           	   3  z   K   | ]2  \  }}| d dj                  t        |j                                4 yw)r>   z, )rW   N)rO   sortedrN   ).0bvorders      r/   	<genexpr>z_loc_summary.<locals>.<genexpr>r   s5     _tq!s"TYYvaUYY'?@AB_s   8;)split
setdefaultrM   	enumerater   rO   r#   )	locationsby_bandlocaspectbandiara   s          @r/   _loc_summaryrm   l   s    $&G 4yy{4$++F34 (01daQT1E99_w}}___ 2s   B
c                   |d   D ci c]  }|d   |d    }}g }t        | d   d         D ]p  \  }}| d   d   |   }|j                  |dz   t        j                  |d         t	        j                  |d	         |d
   d   |d
   d   gt        |d         d       r d }| j                  d      %dD 	ci c]  }	|	|j                  | d|	           }}	t        | d         | j                  d      | d   j                  dg       ||xs ddD 	ci c]  }	|	|j                  | d   d   |	          c}	| j                  d      xs i | d   j                  d| d   j                  dg             D cg c]  }|d   	 c}| j                  d      xs d| j                  d      xs dd
S c c}w c c}	w c c}	w c c}w )NrA   rB   r7   inputsr   computed   r   rD   sizeminmaxrf   )rankr   rD   
size_rangewherefinal_upperuppermiddlelowerfinal_
valid_date	zone_name
conditionsznot yet setsuggestionsjustificationsfinalCheckschecksr   r   r5   notes)
r~   zoneobserved_conditionsr   forecaster_dangertool_suggestionpairing_justificationsguidance_checksr   r   )re   rM   r   rN   r   rm   str)
runrR   rX   namesr   rk   rU   cfinalr_   s
             r/   draft_payloadr   u   s   ,2>,BCqQwZ6"CECH#h-
34 1
OJ'*E%))!F)4*..q?V9U+QvYu-=>!!K.1
 	 E
ww}):VWQEIIcF1#,/00WW#l+,$"8}00rB"3mUqrPQAuyyZ)G)JKKr"%''*:";"Ar/2:/B/B=RUV`RaReRefnprRs/tu!AfIuww}-3!'R  D X sus   F.7F3"F85F=c                   |d   D ci c]  }|d   |d    }}| D cg c]  }t        |d         |d   dD ci c]  }||j                  |d|           c}|d   D cg c]7  }t        j                  |d	         t        j                  |d
         |d   d9 c}|j                  d      xs dd d d c}}}S c c}w c c}w c c}w c c}}}w )NrA   rB   r7   r~   guidance_erary   danger_r   r   rD   size_max)r   rD   r   r   r5   i  )dateerar   r   r   )r   rN   r   r   )analogsrR   rX   r   rl   r_   rU   s          r/   analog_payloadr      s   ,2>,BCqQwZ6"CEC    ,(^$?[\!q%))Asm$455\ :  +..qy9IYI]I]^_`l^mInz],
 EE-06B=
	
  D
 ]s'   CCC	C"<CC
Cc                r   |t         vrt        d|       dt         |    dt        j                  t	        | |      dt
               dt        j                  t        ||      dt
               d}|t        t        gdd	t        d
d	t        ||      ddidgd|dgddt        did}|dk(  rddi|d<   |S )Nzunknown mode zTask: z

<draft_forecast>
rq   )indentdefaultzw
</draft_forecast>

<similar_past_forecasts note="context only; legacy-era forecasts may not follow current guidance">
z
</similar_past_forecasts>r   r   r   r   r   	ephemeral)r   r   cache_controluser)rolecontentformatjson_schema)r   schema)model
max_tokensbetas	fallbackssystemmessagesoutput_configr
   adaptivethinking)MODES
ValueErrorrP   rQ   r   r   r   
MAX_TOKENSFALLBACK_BETASYSTEMrZ   OUTPUT_SCHEMA)r   rR   rS   r   moder   r   paramss           r/   build_requestr      s    5=/00
t !ZZc6(B1VYZ[ \q::nWf5aM
NNi	k 	  V,%5fk%J^dfq]rs
 %67"]m$TUF $j1zMr.   c                .   t         j                  | t         d         \  }}}}t        |dd      xs d|z  t        |dd      xs d|z  z   t        |dd      xs d|z  z   t        |dd      xs d|z  z   dz  }t        t	        t        |d                  S )	Nr
   input_tokensr   output_tokenscache_creation_input_tokenscache_read_input_tokensi@B    )PRICESrN   getattrr   r   round)r   usagep_inp_outp_cwp_crtotals          r/   cost_usdr      s    $jj0GHD%t		*	/a475/1-2e
;	<57;@qD
H	I 53Q7<1
D	E 	E 3uUA'((r.   c           
        t               }|j                  st        d      t        j                  |j                  dd      } |j
                  j                  j                  di | 5 }|j                         }ddd       j                  }|| d   k7  r|nd}|j                  }|j                  ||j                  |j                  t        |dd      t        |dd      t        ||      ddd		}|j                  d
k(  rd|d<   |S t!        d |j"                  D        d      }		 t%        j&                  |	      |d<   |S # 1 sw Y   xY w# t$        j(                  $ r! d|j                   d|d<   d|	dd i|d<   Y |S w xY w)zHCall Claude (streamed server-side) and return a result dict for storage.z3AI review is not configured (no ANTHROPIC_API_KEY).g     @r   )api_keytimeoutmax_retriesNr   r   r   )	stop_reasonfallback_modelr   r   cache_read_tokenscache_write_tokensr   outputerrorrefusalz The model declined this request.r   c              3  T   K   | ]   }|j                   d k(  s|j                   " yw)r   Nr   )r^   r_   s     r/   rb   zrun_review.<locals>.<genexpr>   s     EAAFFf4DEs   ((r5   r   z!Unreadable response (stop_reason=z).rawi  r-   )r	   r2   r)   	anthropic	Anthropicbetar   streamget_final_messager   r   r   r   r   r   r   nextr   rP   loadsJSONDecodeError)
r   settingsclientr   message	served_byr   r   resultr   s
             r/   
run_reviewr      s   ~H%%QRR  )C)CU`abF	$			$	$	.v	. -&**,- I"+vg">YDNMME**(**,,$U,EtL%e-JDQY.
F i'<wEErJD0::d+x M5- -.  0=g>Q>Q=RRTUw!4;/xM0s   *D?%E ?E0E?>E?)returnr1   )rR   dictrS   r   r   r   )rf   z	list[str]r   r   )r   r   rR   r   r   r   )r   
list[dict]rR   r   r   r   )r   r   rR   r   rS   r   r   r   r   r   r   r   r   r   )r   r   r   r   )r   r   r   r   )__doc__
__future__r   rP   decimalr   r   enginer   mappingr   r   r   r	   PROMPT_VERSIONr   r   r   r   r   r   	Exceptionr)   r3   rZ   rm   r   r   r   r   r   r-   r.   r/   <module>r      s   #     9 #1
 -0.
 C e }	
G H% )1;^ _(0  ;J   K#X.%+X$6	 L(-

 #)(!3-4?Q$R#& [!-4	I 	26`:$8) r.   