
    POj(1                        S r SSKJr  SSKrSSKrSSKrSSKJr  SSKJ	r	  SSK
Jr  SSKJr  SSKJr  SSKrSS	KJr  SS
KJr  \" SS9 " S S5      5       r " S S\5      r " S S5      r\" 5       rg)z>
gemini.py

LeafDoctor AI
Google Gemini Vision Client

Part 1
    )annotationsN)	dataclass)Path)Any)genai)types)	BaseModel)settingsT)slotsc                  H    \ rS rSr% S\S'   S\S'   S\S'   S\S'   S\S'   S	rg
)DiagnosisResult   strcroplabelfloat
confidenceraw_responsemodel N__name__
__module____qualname____firstlineno____annotations____static_attributes__r       &/var/www/html/leaf-doctor-v1/gemini.pyr   r      s    
IJJr   r   c                  4    \ rS rSr% S\S'   S\S'   S\S'   Srg)	DiagnosisSchema"   r   r   r   r   r   r   Nr   r   r   r   r!   r!   "   s    	
r   r!   c                  B   \ rS rSrS rS r\  SS j5       rS r\    SS j5       r	\    SS j5       r
    SS jr    SS	 jr S     SS
 jjr\  SS j5       rS r    SS jrSS jr    SS jr    S S jr      S!S jrS rSr\S 5       rSrg)"GeminiClient'   c                    [         R                  " [        R                  S9U l        [        R
                  U l        U R                  5       U l        [        U R                  R                  5       5      U l        g )N)api_key)r   Clientr
   GEMINI_API_KEYclientGEMINI_MODELr   _load_crop_mappingcrop_mappingsortedkeysallowed_cropsselfs    r   __init__GeminiClient.__init__)   sZ    ll++
 **
 335#""$
r   c                    [         R                  S-  nUR                  5       (       d  [        U5      e[	        USSS9 n[
        R                  " U5      nS S S 5        U$ ! , (       d  f       W$ = f)Nzcrop_label_mapping.jsonrzutf-8)encoding)r
   	data_pathexistsFileNotFoundErroropenjsonload)r2   mapping_filefpmappings       r   r,   GeminiClient._load_crop_mapping;   s~     %& 	
 ""$$#  
 iimG
 
 
 s   A  
A/c                   [        U 5      n U R                  5       (       d  [        U 5      eU R                  5       (       d  [	        S5      eU R
                  R                  5       nUS;  a  [	        SU 35      eU R                  5       R                  S-  S-  nU[        R                  :  a  [	        S[        R                   S35      eg )NzInvalid image path.)z.jpgz.jpegz.pngz.webpzUnsupported image: i   zImage size exceeds z MB.)r   r9   r:   is_file
ValueErrorsuffixlowerstatst_sizer
   MAX_UPLOAD_SIZE_MB)
image_path	extensionsize_mbs      r   validate_imageGeminiClient.validate_imageU   s    
 *%
  ""#  !!##% 
 ##% 	  
 
 %i[1 
 OO%% 	 ''( %../t5 (r   c                R    SR                  S U R                   5       5      nSU S3$ )N
c              3  ,   #    U  H
  nS U 3v   M     g7f)z- Nr   ).0r   s     r   	<genexpr>-GeminiClient._build_prompt.<locals>.<genexpr>   s      
* K*s   z
You are an expert agricultural crop disease
diagnosis AI.

Your ONLY responsibility is diagnosis.

DO NOT recommend

- medicine
- treatment
- dosage
- fertilizer
- pesticide
- chemical

Those are handled separately.

Allowed Crops

a   

Instructions

1.
Identify crop.

2.
Identify disease/problem.

3.
Return confidence.

4.
Return ONLY JSON.

JSON Format

{
    "crop":"Rice",
    "label":"Blast",
    "confidence":0.98
}

Rules

Crop must be from allowed crops.

Return only JSON.

Do not use markdown.

Do not explain.
)joinr0   )r2   cropss     r   _build_promptGeminiClient._build_prompt   s>    		 
**
 

&  !'4 4	r   c                @    [         R                  " U 5      u  pUc  SnU$ )Nz
image/jpeg)	mimetypes
guess_type)rJ   mime_s      r   
_mime_typeGeminiClient._mime_type   s*    
 &&
 <Dr   c                   U R                  5       n U R                  S5      (       a]  U R                  5       nUS   R                  S5      (       a  USS  nUS   R                  S5      (       a  US S nSR                  U5      n U R	                  S5      nU R                  S5      nUS:X  d  US:X  a  [        S5      e[        R                  " XUS-    5      $ )	Nz```r      rP   {}zGemini returned invalid JSON.)	strip
startswith
splitlinesrU   findrfindrD   r<   loads)textlinesstartends       r   _extract_jsonGeminiClient._extract_json   s    
 zz|??5!!OO%EQx""5))ab	Ry##E**cr
99U#D		#jjoB;#)/  zzsQw
 	
r   c                   [        UR                  SS5      5      R                  5       n[        UR                  SS5      5      R                  5       n[        UR                  SS5      5      nX R                  ;  a  [        SU 35      eU R                  U   S   nX5;  a  [        SU S	U S
35      eUS:  d  US:  a  [        S5      e[        UUUSU R                  S9$ )Nr    r   r   r   zUnsupported crop: labelsInvalid label '' for crop ''.ra   #Confidence must be between 0 and 1.r   r   r   r   r   )r   getre   r   r-   rD   r   r   )r2   datar   r   r   alloweds         r   _validate_resultGeminiClient._validate_result   s    $ UW	 	 "% UW	 	 HH\1%

 ((($TF+ 
 d# 	
 !% )!F"&  NA~# 
 !**
 	
r   c                .   [        U5      nU R                  5       nU R                  U5      n[        US5       nUR	                  5       nS S S 5        U R
                  R                  R                  U R                  [        R                  R                  US9[        R                  R                  WUS9/[        R                  " [        R                  [        R                   [        R"                  " SS9S[$        S9S9nUR&                  c  [)        S	UR*                   S
U S35      eUR&                  R-                  5       nUS   R/                  5       nUS   R/                  5       n	[1        US   5      n
XR2                  ;  a  [5        SU S35      eU R2                  U   S   nX;  a  [5        SU	 SU S35      eSU
s=::  a  S::  d  O  [5        S5      e[7        UU	U
[8        R:                  " USSS9U R                  S9$ ! , (       d  f       GN= f)Nrb)rk   )rz   	mime_typer   )thinking_budgetzapplication/json)temperaturemax_output_tokensthinking_configresponse_mime_typeresponse_schema)r   contentsconfigzE
        Gemini returned an invalid response.

        TEXT:
        z

        RESPONSE:
        z	
        r   r   r   zUnsupported crop 'rv   rs   rt   ru   g        g      ?rw   F   )ensure_asciiindentrx   )r   rW   r^   r;   readr*   modelsgenerate_contentr   r   Part	from_text
from_bytesGenerateContentConfigr
   GEMINI_TEMPERATUREGEMINI_MAX_OUTPUT_TOKENSThinkingConfigr!   parsedRuntimeErrorrk   
model_dumpre   r   r-   rD   r   r<   dumps)r2   rJ   promptr   r?   image_bytesresponserz   r   r   r   allowed_labelss               r   _call_geminiGeminiClient._call_gemini3  s-   
 *%
##%OOJ/	*d#r'')K $ ;;%%66**

$$ %  

%%$' & 	 ..$77"*"C"C!&!5!5()" $6 / 7 
, ??"	 
 	 

 	
 
 ))+F|!!#W##%4-.
((($TF"-  **40:&!%TF"=  j'C'5  !"
 **

 
	
 $#s   H
Hc                    S n[        U5       H  n U R                  U5      s  $    [        SU 35      e! [         a9  nUnXBS-
  :  a$  US-   S-  n[        R                  " U5         S nAM_   S nAMe  S nAff = f)Nra   r   zGemini diagnosis failed: )ranger   	Exceptiontimesleepr   )r2   rJ   retrieslast_exceptionattemptexwaits          r   _call_with_retryGeminiClient._call_with_retry  s     W~G%(( 	 &( ' 
 	
  
%!#q[(  !D JJt$$ )	
%s   5
A8(A33A8c                F   [        5         [        S5        [        S5        [        S5        [        SU R                   35        [        SU R                   35        [        SU R                  S 35        [        SU R                   35        [        S5        [        5         g )Nz<============================================================zLeafDoctor AI DiagnosiszCrop       : zProblem    : zConfidence : z.2%zModel      : )printr   r   r   r   )results    r   _log_resultGeminiClient._log_result  s    
 	h'(hFKK=)	
 	FLL>*	
 	  %'	

 	||n	

 	hr   c                    U R                   R                  R                  U R                  SS9nUR                  (       d  [        S5      eg)NzReply with OK)r   r   zGemini connection failed.T)r*   r   r   r   rk   r   )r2   r   s     r   test_connectionGeminiClient.test_connection  sI    ;;%%66**$ 7 

 }}+  r   c                h    U R                  U5        U R                  US9nU R                  U5        U$ )zo
Complete diagnosis workflow.

1. Validate image
2. Call Gemini
3. Validate response
4. Return DiagnosisResult
)rJ   )rM   r   r   )r2   rJ   r   s      r   diagnoseGeminiClient.diagnose  s?     	J'&&! ' 
 	 r   c                6    U R                   R                  5       $ )z
Returns all supported crops.
)r0   copyr1   s    r   supported_cropsGeminiClient.supported_crops  s    
 !!&&((r   c                H    XR                   ;  a  / $ U R                   U   S   $ )z"
Returns valid labels for a crop.
rs   r-   r2   r   s     r   supported_labelsGeminiClient.supported_labels  s+     (((I  &x00r   c                    XR                   ;   $ Nr   r   s     r   is_valid_cropGeminiClient.is_valid_crop  s    
 ((((r   c                L    XR                   ;  a  gUU R                   U   S   ;   $ )NFrs   r   )r2   r   r   s      r   is_valid_labelGeminiClient.is_valid_label"  s5     (((   &x01	
r   c                ~    U R                  5       U l        [        U R                  R                  5       5      U l        g)z9
Reload crop mapping without
restarting the application.
N)r,   r-   r.   r/   r0   r1   s    r   reload_mappingGeminiClient.reload_mapping0  s9     ##% 	 $""$
r   Nc                L    U R                   c  U " 5       U l         U R                   $ r   )	_instance)clss    r   instanceGeminiClient.instanceD  s!     == ECM}}r   )r0   r*   r-   r   )rJ   
str | Path)rJ   r   returnr   )rk   r   r   zdict[str, Any])rz   dictr   r   )rJ   r   r   r   )   )rJ   r   r   intr   r   )r   r   )r   	list[str])r   r   r   r   )r   r   r   bool)r   r   r   r   r   r   )r   r   r   r   r3   r,   staticmethodrM   rW   r^   ro   r|   r   r   r   r   r   r   r   r   r   r   r   classmethodr   r   r   r   r   r$   r$   '   s|   
$4 ++ +b;B 	  

	
 
>8
8
 
8
zT
T
 
T
z 

 
 
	
J     L& 
6)11 
1)) 
)

 
 
	

$ I r   r$   )__doc__
__future__r   r<   rZ   r   dataclassesr   pathlibr   typingr   googler   google.genair   pydanticr	   r   r
   r   r!   r$   gemini_clientr   r   r   <module>r      sp    #    !          i 
d dN r   