Ë
    KµCj*  ã            
       ó¬   — d Z ddlZddlZddlmZ ddlmZ  e«       Z ee«      Z	dZ
dededed	ed
ef
d„Zdededed	ed
ef
d„Z G d„ d«      Z e«       Zy)u	  
LLM reasoning layer.

Production concerns specific to calling an external LLM API:
1. Timeouts â€” never let one slow LLM call hold a worker indefinitely.
2. Retries â€” transient network/5xx errors should retry with backoff,
   but only a bounded number of times (LLM_MAX_RETRIES), not forever.
3. Graceful degradation â€” if the LLM is unreachable after retries, the
   user should still get the raw model numbers with a template-based
   explanation, not a 502 for the entire request. The ML predictions
   are the product; the LLM is a presentation layer on top of them.
4. No hallucination â€” the system prompt explicitly forbids inventing
   numbers, and the user prompt only contains values that came from the
   aggregator, never free text the model could "fill in."
é    N)Úget_settings)Ú
get_loggeruü  You are an agricultural decision assistant helping Indian farmers and agricultural officers understand machine learning model output about crop suitability.

Strict rules you must follow:
- Use ONLY the numbers and labels provided to you. Never invent or estimate a number that was not given.
- Do not override or contradict the model outputs. Your job is to explain them, not to second-guess them.
- If a model output is marked "degraded" or has null values, say so plainly rather than guessing a substitute number.
- If conflicts are listed, mention them honestly â€” do not smooth over genuine uncertainty.
- Use simple, direct language suitable for someone without a technical background. Avoid jargon like "anomaly score" without explaining what it means.
- Structure your response in exactly six sections, in this order:
  1. Crop suitability summary
  2. Climate assessment
  3. Expected yield
  4. Market insight
  5. Risk explanation
  6. Alternative crop suggestions
- Keep the whole response under 350 words.
ÚdistrictÚcropÚmonthÚmodel_outputsÚreturnc                 ó”   — d|› d| › d|› d|d   › d|d   › d|d	   › d
|d   › d|d   › d|d   › d|d   › d|j                  dg «      › d�S )NzCrop: z
District: z
Month: z

Model outputs:
- Climate: Úclimatez
- Feasibility: Úfeasibilityz

- Yield: Úyield_predictionz

- Trend: Útrendz
- Market: Úmarketz
- Alternative crops: Úrecommendationz
- Conflicts detected: Ú	conflictsz$
- Models that had issues this run: Údegraded_modelszL

Write the six-section explanation now, following the system rules exactly.)Úget)r   r   r   r   s       úEC:\Crop_Prediction\Backend\crop-ai-system\app\services\llm_service.pyÚ_build_user_promptr   5   s½   € Ø�d�Vð Øˆ*ð Ø€wð ð ˜)Ñ$Ð%ð &Ø˜mÑ,Ð-ð .
Ø
Ð*Ñ
+Ð	,ð -
Ø
˜Ñ
 Ð	!ð "Ø˜Ñ"Ð
#ð $Ø#Ð$4Ñ5Ð6ð 7Ø$ [Ñ1Ð2ð 3$Ø$1×$5Ñ$5Ð6GÈÓ$LÐ#Mð NKðNð Nó    c                 óø  — |d   }|d   }|d   }|d   }|d   j                  dg «      }|rdj                  d„ |D «       «      nd	}	d
|› d| › d|› d|j                  dd«      › d|j                  dd«      d›d|j                  d«      › d|j                  d«      › d|j                  d«      › d|j                  d«      › d|j                  d«      › ddj                  |j                  d g «      «      xs d!› d"|	› d#�S )$uÃ   
    Used only if the LLM is unreachable after all retries. A plain
    template ensures the user still receives the actual model results â€”
    degraded presentation, not a failed request.
    r   r   r   r   r   Úalternativesz, c              3   ó&   K  — | ]	  }|d    –— Œ y­w)r   N© )Ú.0Úas     r   ú	<genexpr>z(_fallback_explanation.<locals>.<genexpr>R   s   è ø€ Ò2¨˜!˜F�)Ñ2ùs   ‚znone availablezlAI explanation service is temporarily unavailable, showing raw model results.

1. Crop suitability summary: z in z for month z is classified as 'ÚlabelÚunknownz' (confidence Ú
confidencer   z.0%z,).
2. Climate assessment: forecast rainfall Úforecast_rainfall_mmz mm, anomaly level: Úanomaly_labelz.
3. Expected yield: Úexpected_yield_ton_haz3 tonnes/hectare.
4. Market insight: price forecast Úprice_forecast_per_quintalz per quintal, demand level: Údemand_levelz.
5. Risk explanation: z; r   zno conflicts detectedz#.
6. Alternative crop suggestions: ú.)r   Újoin)
r   r   r   r   ÚfeasÚyldr   r   ÚaltsÚ	alt_namess
             r   Ú_fallback_explanationr,   G   sL  € ð ˜Ñ'€DØ
Ð*Ñ
+€CØ˜IÑ&€GØ˜8Ñ$€FØÐ)Ñ*×.Ñ.¨~¸rÓB€DÙ6:�—	‘	Ñ2¨TÔ2Ô2Ð@P€Ið(Ø(, v¨T°(°¸;ÀuÀgð NØŸ(™( 7¨IÓ6Ð7ð 8Ø—x‘x ¨aÓ0°Ð5ð 64Ø4;·K±KÐ@VÓ4WÐ3Xð YØ!Ÿ+™+ oÓ6Ð7ð 8Ø!Ÿg™gÐ&=Ó>Ð?ð @-Ø-3¯Z©ZÐ8TÓ-UÐ,Vð W&Ø&,§j¡j°Ó&@Ð%Að B Ø $§	¡	¨-×*;Ñ*;¸KÈÓ*LÓ MÒ hÐQhÐið j,Ø,5¨;°að
	9ðr   c            
       ó8   — e Zd Zd
d„Zd
d„Zdededededef
d	„Zy)Ú
LLMServicer	   Nc                 ó   — d | _         y )N)Ú_client©Úselfs    r   Ú__init__zLLMService.__init__d   s	   € Ø37ˆ�r   c                 óÎ   — t         j                  st        j                  d«       d | _        y t        j                  t         j                  t         j                  ¬«      | _        y )Nuk   ANTHROPIC_API_KEY is not set â€” LLM explanations will use the fallback template until a key is configured.)Úapi_keyÚtimeout)ÚsettingsÚANTHROPIC_API_KEYÚloggerÚwarningr0   Ú	anthropicÚ	AnthropicÚLLM_TIMEOUT_SECONDSr1   s    r   ÚinitzLLMService.initg   sM   € Ü×)Ò)Ü�N‰NðCôð  ˆDŒLØÜ ×*Ñ*Ü×.Ñ.Ü×0Ñ0ô
ˆ�r   r   r   r   r   c           	      ó  — | j                   €t        ||||«      S t        ||||«      }d }t        dt        j
                  dz   «      D ]£  }	 | j                   j                  j                  t        j                  t        j                  t        d|dœg¬«      }|j                  D �	cg c]  }	|	j                  dk(  sŒ|	j                  ‘Œ  }
}	dj                  |
«      j                  «       c S  t&        j5                  d|› �«       t        ||||«      S c c}	w # t         j"                  $ rM}|}t%        d|z  d«      }t&        j)                  d	|› d
|› d�«       t+        j,                  |«       Y d }~�Œ,d }~wt         j.                  $ rM}|}t%        d|z  d«      }t&        j)                  d|› d
|› d�«       t+        j,                  |«       Y d }~�ŒŠd }~wt         j0                  $ rœ}|}d|j2                  cxk  rdk  rNn nKt%        d|z  d«      }t&        j)                  d|j2                  › d|› d�«       t+        j,                  |«       n,t&        j5                  d|j2                  › d|› �«       Y d }~ �Œ‹Y d }~�Œ7d }~ww xY w)Né   é   Úuser)ÚroleÚcontent)ÚmodelÚ
max_tokensÚsystemÚmessagesÚtextú
é
   zLLM rate limited, retrying in zs (attempt ú)z"LLM connection error, retrying in iô  iX  zLLM server error z, retrying in ÚszLLM client error z, not retrying: z;LLM call failed after retries, using fallback explanation: )r0   r,   r   Úranger7   ÚLLM_MAX_RETRIESrH   ÚcreateÚ	LLM_MODELÚLLM_MAX_TOKENSÚSYSTEM_PROMPTrD   ÚtyperI   r'   Ústripr;   ÚRateLimitErrorÚminr9   r:   ÚtimeÚsleepÚAPIConnectionErrorÚAPIStatusErrorÚstatus_codeÚerror)r2   r   r   r   r   Úuser_promptÚ
last_errorÚattemptÚresponseÚbÚtext_blocksÚexcÚwaits                r   Úget_explanationzLLMService.get_explanationt   sG  € Ø�<‰<ÐÜ(¨°4¸ÀÓNÐNä(¨°4¸ÀÓNˆØ'+ˆ
ä˜Q¤× 8Ñ 8¸1Ñ <Ó=ò !	ˆGð ØŸ<™<×0Ñ0×7Ñ7Ü"×,Ñ,Ü'×6Ñ6Ü(Ø'-¸+ÑFÐGð	 8ó �ð 08×/?Ñ/?ÖT¨!À1Ç6Á6ÈVÓCS˜qŸv›vÐT�ÐTØ—y‘y Ó-×3Ñ3Ó5Ò5ð!	ôF 	�‰ÐRÐS]ÐR^Ð_Ô`Ü$ X¨t°U¸MÓJÐJùò9 Uøô ×+Ñ+ò !Ø �
Ü˜1 ™<¨Ó,�Ü—‘Ð!?À¸vÀ[ÐQXÐPYÐYZÐ[Ô\Ü—
‘
˜4× Ò ûä×/Ñ/ò !Ø �
Ü˜1 ™<¨Ó,�Ü—‘Ð!CÀDÀ6ÈÐU\ÐT]Ð]^Ð_Ô`Ü—
‘
˜4× Ò ûä×+Ñ+ò 
Ø �
Ø˜#Ÿ/™/Ô/¨CÕ/Ü˜q G™|¨RÓ0�DÜ—N‘NÐ%6°s·±Ð6GÀ~ÐVZÐU[Ð[\Ð#]Ô^Ü—J‘J˜tÕ$ô —L‘LÐ#4°S·_±_Ð4EÐEUÐVYÐUZÐ![Ô\Þõ %ûð
úsL   ÁADÂ'DÂ<DÃ
 DÄDÄJÄ,AE4Å4JÆ
AGÇJÇ(B
I?É?J)r	   N)	Ú__name__Ú
__module__Ú__qualname__r3   r>   ÚstrÚintÚdictrf   r   r   r   r.   r.   c   s<   „ ó8ó
ð+K¨ð +K°3ð +K¸sð +KÐSWð +KÐ\_ô +Kr   r.   )Ú__doc__rX   r;   Úapp.core.configr   Úapp.core.logging_configr   r7   rg   r9   rS   rj   rk   rl   r   r,   r.   Úllm_servicer   r   r   ú<module>rq      sž   ðñó  ã å (Ý .á‹>€Ù	�HÓ	€ð€ð4N ð N¨Cð N¸ð NÈDð NÐUXó Nð$ Cð ¨sð ¸3ð Ètð ÐX[ó ÷8<Kñ <Kñ@ ‹l�r   