Ë
    ¥©	jÍ/  ã                   óº   — U d Z ddlZddlZddlmZ ddlmZ ddlmZ ddl	m
Z
 ddlmZmZ  e
e«      Zdaeed	<   daeed
<    G d„ d«      Zdaedz  ed<   defd„Zy)u¼   
Granite-based analysis client for PR review.
Implements the Mythos two-pass pattern: Searcher â†’ Validator.

Uses IBM watsonx.ai Granite model instead of Bob Shell for simplified setup.
é    N)ÚAny)Úget_watsonx_client)Úsettings)Ú
get_logger)Ú
FindingRawÚFindingValidatedÚ Ú_last_searcher_responseÚ_last_searcher_errorc                   ó¶   — e Zd ZdZd„ Z	 	 ddededz  dedefd„Zg d	¢Zd
edededede	ee
e   f   f
d„Zdededeeef   de
e   fd„Zdede
e   de
e   fd„Zy)Ú	BobClientu`  
    Analysis client using Granite (watsonx.ai).
    
    Implements the Mythos pattern with Granite:
    1. Searcher pass: Analyzes diff â†’ produces findings_raw.json
    2. Validator pass: Receives diff + findings_raw â†’ filters noise â†’ findings_validated.json
    
    Note: Named "BobClient" for compatibility, but uses Granite internally.
    c                 óž   — t        «       | _        t        j                  | _        d| _        t        j                  dd| j                  ¬«       y )Ng     Àr@Úanalysis_client_initializedÚgranite)ÚmodeÚmodel)r   Úwatsonxr   Úgranite_model_idÚmodel_idÚtimeoutÚloggerÚinfo)Úselfs    ú7/var/www/html/prparty/backend/app/clients/bob_client.pyÚ__init__zBobClient.__init__"   s7   € Ü)Ó+ˆŒÜ ×1Ñ1ˆŒØˆŒä�‰Ð1¸	ÈÏÉˆÕWó    NÚpromptÚsystem_promptÚ
max_tokensÚreturnc           
   ƒ   ój  K  — t         j                  dt        |«      |rt        |«      nd|¬«       t        j                  «       }	 |}|r|› d|› �}| j                  j                  | j                  ||ddd¬«      ƒ d	{  –—† }t        j                  «       |z
  }t         j                  d
t        |«      t        |d«      ¬«       |S 7 ŒH# t        $ r\}t        j                  «       |z
  }t         j                  dt        |«      t        |«      j                  t        |d«      ¬«       ‚ d	}~ww xY w­w)a  
        Call Granite model for analysis.
        
        Args:
            prompt: User prompt
            system_prompt: Optional system prompt
            max_tokens: Maximum tokens to generate
        
        Returns:
            Granite's response text
        Úgranite_requestr   )Úprompt_lengthÚsystem_prompt_lengthr   ú

g333333Ó?gÍÌÌÌÌÌì?é2   )r   r   Úmax_new_tokensÚtemperatureÚtop_pÚtop_kNÚgranite_successé   )Úresponse_lengthÚelapsed_secondsÚgranite_error)ÚerrorÚ
error_typer.   )r   r   ÚlenÚtimer   Úgenerate_textr   ÚroundÚ	Exceptionr0   ÚstrÚtypeÚ__name__)	r   r   r   r   Ú
start_timeÚfull_promptÚresponseÚelapsedÚes	            r   Ú_call_granitezBobClient._call_granite)   s-  è ø€ ô" 	�‰ØÜ˜f›+Ù7D¤ ]Ô!3È!Ø!ð	 	ô 	
ô —Y‘Y“[ˆ
ð"	à ˆKÙØ!. ¨t°F°8Ð<�ð "Ÿ\™\×7Ñ7ØŸ™Ø"Ø)ØØØð 8ó ÷ ˆHô —i‘i“k JÑ.ˆGä�K‰KØ!Ü # H£Ü % g¨qÓ 1ð ô ð ˆOð#ùô& ò 	Ü—i‘i“k JÑ.ˆGÜ�L‰LØÜ˜!“fÜ ›7×+Ñ+Ü % g¨qÓ 1ð	 ô ð ûð	üs>   ‚AD3Á9C Â C	ÂAC ÃD3Ã	C Ã	D0ÃAD+Ä+D0Ä0D3))zcharacter_aegis.mdÚAÚsecurity)zcharacter_schema.mdÚSCÚdatabase)zcharacter_pixel.mdÚPÚux)zcharacter_atlas.mdÚATÚarchitecture)zcharacter_echo.mdÚEÚtests)zcharacter_codex.mdÚCÚdocumentationÚprompt_fileÚdiffÚ	id_prefixÚforced_categoryc              ƒ   óV  K  — ddl }ddlm}  |t        «      j                  j                  j                  j                  }t        |dz  |z  dd¬«      5 }|j                  «       }	ddd«       	d|› d	�z   }
| j                  |
d
¬«      ƒ d{  –—† }g }	 |j                  «       }d|v r:|j                  d«      dz   }|j                  d|«      }||| j                  «       }n=d|v r9|j                  d«      dz   }|j                  d|«      }||| j                  «       }|j                  d«      s'|j                  d|«      }|r||j                  «       d }|j                  d«      s'|j                  d|«      }|r|d|j                  «        }t        j                   |«      }|j#                  dg «      }t%        |d«      D ]  \  }}|› |d›�|d<   ||d<   Œ 	 ||fS # 1 sw Y   �Œ{xY w7 �Œ_# t&        $ r.}t(        j+                  d|t-        |«      ¬«       Y d}~||fS d}~ww xY w­w)zNRun one character-specific searcher and return (raw_response, findings_dicts).r   N©ÚPathÚpromptsÚrúutf-8©Úencodingr%   z

Return JSON now:i   ©r   ú```jsoné   ú```é   ú{z\{ú}z\}(?=[^}]*$)Úfindingsé   Ú03dÚidÚcategoryÚcharacter_searcher_parse_failed)rL   r0   )ÚreÚpathlibrR   Ú__file__ÚparentÚopenÚreadr?   ÚstripÚfindÚ
startswithÚsearchÚstartÚendswithÚendÚjsonÚloadsÚgetÚ	enumerater6   r   Úwarningr7   )r   rL   rM   rN   rO   Ú_rerR   Úproject_rootÚfÚprompt_templater   r<   Úraw_findingsÚjson_strÚsr>   ÚmÚdataÚis                      r   Ú_run_character_searcherz!BobClient._run_character_searcherq   s@  è ø€ ó 	Ý áœH“~×,Ñ,×3Ñ3×:Ñ:×AÑAˆÜ�, Ñ*¨[Ñ8¸#ÈÔPð 	'ÐTUØŸf™f›hˆO÷	'ð ! T¨$¨Ð/CÐ#DÑDˆØ×+Ñ+¨F¸tÐ+ÓD×Dˆà#%ˆð	BØ—~‘~Ó'ˆHØ˜HÑ$Ø—M‘M )Ó,¨qÑ0�Ø—M‘M %¨Ó+�Ø# A a˜=×.Ñ.Ó0‘Ø˜(Ñ"Ø—M‘M %Ó(¨1Ñ,�Ø—M‘M %¨Ó+�Ø# A a˜=×.Ñ.Ó0�à×&Ñ& sÔ+Ø—J‘J˜u hÓ/�ÙØ'¨¯©«	¨
Ð3�HØ×$Ñ$ SÔ)Ø—J‘J˜°Ó9�ÙØ'¨¨¯©«Ð1�Hä—:‘:˜hÓ'ˆDØŸ8™8 J°Ó3ˆLô " ,°Ó2ò 0‘��1Ø&˜K¨¨# wÐ/��$‘Ø /��*’ñ0ð ˜Ð%Ð%÷O	'ñ 	'úð Eúô> ò 	BÜ�N‰NÐ<Ø'2¼#¸a»&ð ÷ Bð Bð ˜Ð%Ð%ûð		BüsU   ‚AH)ÁGÁ&'H)ÂG,ÂH)ÂEG/ ÇH)ÇG)Ç$	H)Ç/	H&Ç8!H!ÈH)È!H&È&H)Úpackage_jsonÚcontext_filesc              ƒ   ó€  ‡‡‡K  — ddl }t        j                  dt        |«      t        |«      ¬«        |j                  | j
                  D ���cg c]  \  }}}| j                  ||||«      ‘Œ c}}}Ž ƒ d{  –—† }dj                  d„ t        | j
                  |«      D «       «      a	da
|D �	�
�cg c]  \  }	}
|
D ]  }|‘Œ Œ }}
}	}dd	d
ddœŠdddddœŠdt        dt        fˆˆfd„}g }
|D ]#  }	 |
j                  t        di  ||«      ¤Ž«       Œ% | j
                  D �	�‡ci c]  \  }	}	Š‰t#        ˆfd„|
D «       «      “Œ }}	}t        j                  ddt        |
«      i|¤Ž |
S c c}}}w 7 Œöc c}}
}	w # t        $ r4}t        j                  dt!        |«      t!        |«      ¬«       Y d}~ŒÃd}~ww xY wc c}}	w ­w)u¥   
        Searcher Pass: run one specialised prompt per character in parallel.
        Findings arrive pre-classified â€” no separate classifier step needed.
        r   NÚsearcher_pass_start)Údiff_lengthÚcontext_files_countr%   c              3   óœ   K  — | ]D  \  \  }}}\  }}d |j                  dd«      j                  dd«      j                  «       › d|› �–— ŒF y­w)z=== Ú
character_r	   z.mdz ===
N)ÚreplaceÚupper)Ú.0ÚpfÚ_Úresps       r   ú	<genexpr>z*BobClient.searcher_pass.<locals>.<genexpr>À   sW   è ø€ ò .
á%‘
��Q˜™I˜T 1ð �2—:‘:˜l¨BÓ/×7Ñ7¸¸rÓB×HÑHÓJÐKÈ6ÐRVÐQWÔXñ.
ùs   ‚A
Ar	   éZ   é<   é   é
   )ÚcriticalÚhighÚmediumÚlowÚcrit_hitÚhitÚgrazeÚwhisperry   r    c                 ó  •— | j                  dd«      }| j                  dd«      | j                  dd«      | j                  d«      xs | j                  dd«      || j                  d	«      xs ‰j                  |d
«      | j                  d«      xs ‰j                  |d«      | j                  d«      xs | j                  dd«      | j                  dd«      | j                  dd«      | j                  dd«      | j                  dd«      dœS )NÚseverityr˜   rb   ÚF000Útitler	   ÚexplanationÚdescriptionÚdamager”   Údamage_typerœ   Ú	file_pathÚfileÚunknownÚ
line_startr`   Úline_endÚcode_snippetrc   Úgeneral)rb   r    r¢   rž   r£   r¤   r¥   r¨   r©   rª   rc   )rt   )ry   rž   Ú
damage_mapÚdamage_type_maps     €€r   Ú	normalizez*BobClient.searcher_pass.<locals>.normalizeË   sæ   ø€ Ø—u‘u˜Z¨Ó/ˆHà—e‘e˜D &Ó)ØŸ™˜w¨Ó+Ø Ÿu™u ]Ó3ÒO°q·u±u¸]ÈBÓ7OØ$ØŸ%™% ›/ÒI¨Z¯^©^¸HÀbÓ-IØ Ÿu™u ]Ó3Ò_°×7JÑ7JÈ8ÐU^Ó7_ØŸU™U ;Ó/ÒK°1·5±5¸ÀÓ3KØŸe™e L°!Ó4ØŸE™E *¨aÓ0Ø !§¡ n°bÓ 9ØŸE™E *¨iÓ8ñð r   Únormalize_failed)r0   Úfindingc              3   óB   •K  — | ]  }|j                   ‰k(  sŒd –— Œ y­w)r`   N)rc   )rŒ   ry   Úcats     €r   r�   z*BobClient.searcher_pass.<locals>.<genexpr>ã   s   øè ø€ Ò>˜1¨A¯J©J¸#Ó,=”QÑ>ùs   ƒ˜Úfindings_count© )Úsearcher_pass_complete)Úasyncior   r   r2   ÚgatherÚ_CHARACTER_PROMPTSr�   ÚjoinÚzipr
   r   ÚdictÚappendr   r6   rv   r7   Úsum)r   rM   r‚   rƒ   Ú_asynciorL   rN   rc   ÚresultsrŽ   r_   r°   Úall_rawr®   ry   r>   r²   Úcountsr¬   r­   s                   ` @@r   Úsearcher_passzBobClient.searcher_pass¦   sã  úè ø€ ó 	#ä�‰Ø!Ü˜D›	Ü # MÓ 2ð 	ô 	
ð (˜Ÿ™à48×4KÑ4K÷*
ð *
á0�˜Y¨ð ×(Ñ(¨°d¸IÀxÕPô*
ð ÷ ˆð #)§+¡+ñ .
ä),¨T×-DÑ-DÀgÓ)Nô.
ó #
Ðð  "Ðà.5×NÐN™{˜q (ÀXÒN¸'’7ÐN�7ÐNˆÒNà"$¨b¸BÀrÑJˆ
Ø'1¸5ÈGÐ\eÑfˆð	œð 	¤$ö 	ð  &(ˆØò 	QˆAðQØ—‘¤
Ñ :©Y°q«\Ñ :Õ;ð	Qð "×4Ñ4÷
ð 
á��1�cð ”Ó> Ô>Ó>Ñ>ð
ˆñ 
ô 	�‰ÑU¼SÀ»]ÐUÈfÒUàˆùô_*
ð úô Oøô2 ò QÜ—‘Ð1¼¸Q»ÌÈQË�×PÑPûðQüó
ùsf   …AF>ÁE(Á/	F>Á8E/Á98F>Â1E1Ã,F>Ã1 E8ÄF>Ä# F8Å-F>Å1F>Å8	F5Æ*F0Æ+F>Æ0F5Æ5	F>Úfindings_rawc           
   ƒ   ó  K  — t         j                  dt        |«      ¬«       ddlm}  |t
        «      j                  j                  j                  j                  }|dz  dz  }t        |dd¬	«      5 }|j                  «       }d
d
d
«       t        j                  |D �cg c]  }|j                  «       ‘Œ c}d¬«      }j                  d|«      }	|	j                  d|«      }	| j                  |	d¬«      ƒ d
{  –—† }
	 d|
v r:|
j                  d«      dz   }|
j                  d|«      }|
|| j                  «       }n|
j                  «       }t        j                   |«      }|j#                  dg «      D �ci c]  }t%        |t&        «      r
d|v r|d   |“Œ }}g }|D ]Ÿ  }|j#                  |j(                  «      }|�|j#                  dd«      sŒ3|xs i j#                  d«      xs |j*                  }|xs i j#                  dd«      }|j                  «       }||d<   ||d<   |j-                  t/        d!i |¤Ž«       Œ¡ t         j                  dt        |«      t        |«      t        |«      z
  ¬«       |S # 1 sw Y   �ŒõxY wc c}w 7 �Œ�c c}w # t0        $ re}t         j3                  dt5        |«      t        |«      ¬«       |D �cg c]  }t/        d!i |j                  «       ¤dd i¤Ž‘Œ! nc c}w c}cY d
}~S d
}~ww xY w­w)"a  
        Validator Pass: Filter noise from raw findings using Granite.
        
        Args:
            diff: Original git diff
            findings_raw: Raw findings from searcher pass
        
        Returns:
            List of validated findings (noise filtered out)
        Úvalidator_pass_start)Úfindings_raw_countr   rQ   rS   zbob_validator.mdrT   rU   rV   Nr,   )Úindentz{{DIFF}}z{{FINDINGS_RAW}}i    rX   rY   rZ   r[   r_   rb   Ú	validatedTÚseverity_adjustmentÚvalidation_notesr	   rž   Úvalidator_pass_complete)Úfindings_validated_countÚfiltered_countÚ)validator_parse_failed_using_raw_findings)r0   Ú	raw_countzvalidator skippedr´   )r   r   r2   rf   rR   rg   rh   ri   rj   rr   ÚdumpsÚ
model_dumprŠ   r?   rl   rk   rs   rt   Ú
isinstancer»   rb   rž   r¼   r   r6   rv   r7   )r   rM   rÃ   rR   rx   Úvalidator_pathry   rz   Úfindings_jsonr   r<   Ú
json_startÚjson_endr|   Úvalidated_dataÚvÚvalidation_by_idr_   ÚrawÚdecisionrž   Únotesr   r>   s                           r   Úvalidator_passzBobClient.validator_passê   sé  è ø€ ô 	�‰Ø"Ü" <Ó0ð 	ô 	
õ 	!ÙœH“~×,Ñ,×3Ñ3×:Ñ:×AÑAˆØ%¨	Ñ1Ð4FÑFˆä�. #°Ô8ð 	'¸AØŸf™f›hˆO÷	'ô Ÿ
™
Ø%1Ö2 ˆQ�\‰\�^Ò2Øô
ˆð !×(Ñ(¨°TÓ:ˆØ—‘Ð 2°MÓBˆð ×+Ñ+¨F¸tÐ+ÓD×Dˆð-	Ø˜HÑ$Ø%Ÿ]™]¨9Ó5¸Ñ9�
Ø#Ÿ=™=¨°
Ó;�Ø# J¨xÐ8×>Ñ>Ó@‘à#Ÿ>™>Ó+�ä!ŸZ™Z¨Ó1ˆNð (×+Ñ+¨J¸Ó;ö àÜ˜a¤Ô&¨4°1©9ð �$‘˜‘
ð Ðð  ð 02ˆHØ#ò >�Ø+×/Ñ/°·±Ó7�ØÐ# x§|¡|°KÀÕ'FØ (¢¨B×3Ñ3Ð4IÓJÒZÈcÏlÉl�HØ%š^¨×0Ñ0Ð1CÀRÓH�EØŸ>™>Ó+�DØ'/�D˜Ñ$Ø/4�DÐ+Ñ,Ø—O‘OÔ$4Ñ$<°tÑ$<Õ=ð>ô �K‰KØ)Ü),¨X«Ü" <Ó0´3°x³=Ñ@ð ô ð ˆO÷g	'ñ 	'üò
 3ð Eûò øô4 ò 
	Ü�N‰NØ;Ü˜!“fÜ˜lÓ+ð ô ð &öàô !ÑX 1§<¡<£>ÑXÐDWÔXñùô õ ûð
	üs�   ‚A1LÁ3I7ÂLÂJÂ6ALÃ9J	Ã:LÃ?A7J Å6!JÆ8J ÇB&J É6LÉ7JÉ<LÊJ Ê	K?Ê.K:Ë$K-Ë,K:Ë4K?Ë5LË:K?Ë?L)Ni   )r9   Ú
__module__Ú__qualname__Ú__doc__r   r7   Úintr?   r¸   ÚtupleÚlistr»   r�   r   rÂ   r   rÝ   r´   r   r   r   r      sþ   „ ñòXð %)Øñ	<àð<ð ˜T‘zð<ð ð	<ð
 
ó<ò~Ðð3&àð3&ð ð3&ð ð	3&ð
 ð3&ð 
ˆs�D˜‘JˆÑ	ó3&ðjBàðBð ðBð ˜C ˜H‘~ð	Bð
 
ˆjÑ	óBðHXàðXð ˜:Ñ&ðXð 
ÐÑ	ô	Xr   r   Ú_bob_clientr    c                  ó.   — t         €
t        «       a t         S )z=Get or create global analysis client instance (uses Granite).)rä   r   r´   r   r   Úget_bob_clientræ   I  s   € ô ÐÜ“kˆÜÐr   )rà   rr   r3   Útypingr   Úapp.clients.watsonx_clientr   Ú
app.configr   Úapp.logging_configr   Ú
app.modelsr   r   r9   r   r
   r7   Ú__annotations__r   r   rä   ræ   r´   r   r   ú<module>rí      sm   ðòó Û Ý å 9Ý Ý )ß 3á	�HÓ	€ð  "Ð ˜Ó !ØÐ �cÓ ÷kñ kð^	 !%€ˆY˜ÑÓ $ð˜	ô r   