Ë
    ¥©	jÎO  ã                   ó2  — U d Z ddlZddlmZ ddlmZmZ ddlmZ ddlm	Z	 ddl
mZmZmZmZmZ ddlmZ dd	lmZmZmZmZ dd
lmZ  e	e«      Zg d¢Zeeeeeef      ed<   dddddœZ dddddœZ!de"dededz  fd„Z# G d„ d«      Z$da%e$dz  ed<   de$fd„Z&y)uk  
PR analysis orchestrator - coordinates the entire Mythos pipeline.

Flow:
1. Load PR diff + context
2. Bob Searcher Pass â†’ findings_raw
3. Bob Validator Pass â†’ findings_validated
4. Classifier â†’ assign characters
5. Voice Rewriter (Granite) â†’ add character_dialogue
6. Calculate damage & verdict
7. Generate character dialogues
8. Return EncounterResult
é    N)Údatetime)ÚAnyÚAsyncGenerator)Úget_bob_client)Ú
get_logger)ÚCharacterDialogueÚEncounterResultÚFindingÚFindingValidatedÚVerdict)Úget_classifier)Ú	DiffIndexÚfind_snippet_lineÚis_groundedÚ
parse_diff)Úget_voice_rewriter))zcharacter_aegis.mdÚAÚsecurityÚaegis)zcharacter_schema.mdÚSCÚdatabaseÚschema)zcharacter_pixel.mdÚPÚuxÚpixel)zcharacter_atlas.mdÚATÚarchitectureÚatlas)zcharacter_echo.mdÚEÚtestsÚecho)zcharacter_codex.mdÚCÚdocumentationÚcodexÚCHARACTER_PIPELINEéZ   é<   é   é
   )ÚcriticalÚhighÚmediumÚlowÚcrit_hitÚhitÚgrazeÚwhisperÚfÚcategoryÚreturnc                 óž  — 	 | j                  dd«      }t        | j                  dd«      | j                  dd«      | j                  d«      xs | j                  dd«      || j                  d	«      xs t        j                  |d
«      | j                  d«      xs t        j                  |d«      | j                  d«      xs | j                  dd«      | j                  dd«      | j                  dd«      | j                  dd«      |d¬«      S # t        $ r4}t
        j                  dt        |«      t        | «      ¬«       Y d}~yd}~ww xY w)zOConvert a raw searcher dict into a FindingValidated, returning None on failure.Úseverityr-   ÚidÚF000ÚtitleÚ ÚexplanationÚdescriptionÚdamager)   Údamage_typer1   Ú	file_pathÚfileÚunknownÚ
line_starté   Úline_endÚcode_snippet)r7   r9   r<   r6   r=   r>   r?   rB   rD   rE   r3   Úvalidation_notesÚnormalize_validated_failed)ÚerrorÚfindingN)Úgetr   Ú_SEVERITY_DAMAGEÚ_SEVERITY_DAMAGE_TYPEÚ	ExceptionÚloggerÚwarningÚstr)r2   r3   r6   Úexcs       ú:/var/www/html/prparty/backend/app/services/orchestrator.pyÚ_normalize_raw_to_validatedrS   =   s  € ðØ—5‘5˜ UÓ+ˆÜØ�u‰u�T˜6Ó"Ø—%‘%˜ Ó$ØŸ™˜mÓ,ÒH°·±°mÀRÓ0HØØ—5‘5˜“?ÒHÔ&6×&:Ñ&:¸8ÀRÓ&Hà—‘�mÓ$ò BÜ(×,Ñ,¨X°yÓAà—e‘e˜KÓ(ÒD¨A¯E©E°&¸)Ó,DØ—u‘u˜\¨1Ó-Ø—U‘U˜: qÓ)ØŸ™˜~¨rÓ2ØØô
ð 	
øô" ò Ü�‰Ð3¼3¸s»8ÌSÐQRËVˆÔTÜûðús   ‚DD Ä	EÄ*EÅEc                   óä   — e Zd ZdZd„ Zdedefd„Zdee	   dee
   fd„Zded	ed
edededeeef   defd„Zdededededededeeee	   edz  f   fd„Zded	ed
edededeeef   deedf   fd„Zy)ÚPRAnalysisOrchestratorz/Orchestrates the complete PR analysis pipeline.c                 óˆ   — t        «       | _        t        «       | _        t	        «       | _        t        j                  d«       y )NÚorchestrator_initialized)r   Úbobr   Ú
classifierr   Úvoice_rewriterrN   Úinfo)Úselfs    rR   Ú__init__zPRAnalysisOrchestrator.__init__\   s.   € Ü!Ó#ˆŒÜ(Ó*ˆŒÜ0Ó2ˆÔÜ�‰Ð.Õ/ó    Útotal_damager4   c                 ó   — |dk\  ry|dk\  ryy)u½   
        Calculate PR verdict based on total damage.
        
        Rules:
        - â‰¥80 damage: blocked
        - 50-79 damage: changes_required
        - <50 damage: approved
        éP   Úblockedé2   Úchanges_requiredÚapproved© )r\   r_   s     rR   Ú_calculate_verdictz)PRAnalysisOrchestrator._calculate_verdictb   s   € ð ˜2ÒØØ˜RÒØ%àr^   Úfindingsc           	      óV  — g }i }|D ]=  }|j                   |vrg ||j                   <   ||j                      j                  |«       Œ? |j                  «       D ��cg c]  \  }}t        |«      dk\  sŒ|‘Œ }}}t        |«      dk\  r}|d   }|d   }	||   d   }
||	   d   }t	        |
j
                  |d|
j                  j                  «       › d�|	d|j                  j                  «       › d�¬«      }|j                  |«       t        j                  d	t        |«      ¬
«       |S c c}}w )a  
        Generate character dialogues based on finding patterns.
        
        For demo, we'll create dialogues when:
        - Multiple findings from same character (self-reflection)
        - Related findings from different characters (collaboration)
        é   r   rC   zI've discovered z$. This requires immediate attention!zIndeed! And I've found z+ as well. These issues compound each other.)Ú
finding_idÚcharacter_1Ú
dialogue_1Úcharacter_2Ú
dialogue_2Údialogues_generated)Údialogue_count)
Úcharacter_idÚappendÚitemsÚlenr   r7   r9   ÚlowerrN   r[   )r\   rh   Ú	dialoguesÚby_characterrI   ÚcharÚfindsÚcharacters_with_multipleÚchar1Úchar2Úfinding1Úfinding2Údialogues                rR   Ú_generate_dialoguesz*PRAnalysisOrchestrator._generate_dialoguesr   sQ  € ð .0ˆ	ð 24ˆØò 	?ˆGØ×#Ñ#¨<Ñ7Ø57�˜W×1Ñ1Ñ2Ø˜×-Ñ-Ñ.×5Ñ5°gÕ>ð	?ð %1×$6Ñ$6Ó$8÷$
Ù �T˜5¼CÀ»JÈ!»OŠDð$
Ð ñ $
ô Ð'Ó(¨AÒ-à,¨QÑ/ˆEØ,¨QÑ/ˆEØ# EÑ*¨1Ñ-ˆHØ# EÑ*¨1Ñ-ˆHä(Ø#Ÿ;™;Ø!Ø-¨h¯n©n×.BÑ.BÓ.DÐ-EÐEiÐjØ!Ø4°X·^±^×5IÑ5IÓ5KÐ4LÐLwÐxôˆHð ×Ñ˜XÔ&ä�‰Ø!Ü˜y›>ð 	ô 	
ð
 Ðùó3$
s   ÁD%Á1D%Ú	pr_numberÚpr_titleÚ	pr_authorÚdiffÚpackage_jsonÚcontext_filesc              ƒ   ó  K  — t         j                  d|t        |«      t        |«      ¬«       t         j                  d«       | j                  j	                  |||¬«      ƒ d{  –—† }|sIt         j                  d«       t        |||ddd	g g t        j                  «       j                  «       ¬
«	      S t         j                  dt        |«      ¬«       | j                  j                  ||¬«      ƒ d{  –—† }|sIt         j                  d«       t        |||ddd	g g t        j                  «       j                  «       ¬
«	      S t         j                  dt        |«      ¬«       | j                  j                  |«      }	t         j                  d«       | j                  j                  |	«      ƒ d{  –—† }
t        d„ |
D «       «      }t        dd	|z
  «      }| j!                  |«      }t         j                  d|||¬«       t         j                  d«       | j#                  |
«      }t        |||||||
|t        j                  «       j                  «       ¬
«	      }t         j                  d||t        |
«      |¬«       |S 7 �Œ7 �Œ�7 ŒÔ­w)a£  
        Analyze a PR through the complete pipeline.
        
        Args:
            pr_number: GitHub PR number
            pr_title: PR title
            pr_author: PR author username
            diff: Git diff content
            package_json: package.json content
            context_files: Dict of filename -> content
        
        Returns:
            Complete encounter result with voiced findings
        Úpr_analysis_start)r‚   Údiff_lengthÚcontext_files_countÚstep_1_searcher_pass)r…   r†   r‡   NÚno_findings_detectedre   r   éd   ©	r‚   rƒ   r„   Úverdictr_   Úremaining_hprh   rw   Úanalysis_timestampÚstep_2_validator_pass)Úfindings_raw_count)r…   Úfindings_rawÚall_findings_filtered_outÚstep_3_classify_findings)Úfindings_validated_countÚstep_4_voice_rewritec              3   ó4   K  — | ]  }|j                   –— Œ y ­w©N©r=   ©Ú.0r2   s     rR   ú	<genexpr>z4PRAnalysisOrchestrator.analyze_pr.<locals>.<genexpr>ò   s   è ø€ Ò=¨˜1Ÿ8�8Ñ=ùó   ‚Údamage_calculated)r_   r‘   r�   Ústep_5_generate_dialoguesÚpr_analysis_complete©r‚   r�   Úfindings_countr_   )rN   r[   ru   rX   Úsearcher_passr	   r   ÚutcnowÚ	isoformatÚvalidator_passrY   Úclassify_findingsrZ   Úrewrite_findingsÚsumÚmaxrg   r�   )r\   r‚   rƒ   r„   r…   r†   r‡   r•   Úfindings_validatedÚclassified_findingsÚvoiced_findingsr_   r‘   r�   rw   Úresults                   rR   Ú
analyze_prz!PRAnalysisOrchestrator.analyze_prŸ   st  è ø€ ô. 	�‰ØØÜ˜D›	Ü # MÓ 2ð	 	ô 	
ô 	�‰Ð*Ô+Ø!ŸX™X×3Ñ3ØØ%Ø'ð 4ó 
÷ 
ˆñ Ü�K‰KÐ.Ô/ä"Ø#Ø!Ø#Ø"ØØ ØØÜ#+§?¡?Ó#4×#>Ñ#>Ó#@ô
ð 
ô 	�‰Ð+ÄÀLÓ@QˆÔRØ#'§8¡8×#:Ñ#:ØØ%ð $;ó $
÷ 
Ðñ
 "Ü�K‰KÐ3Ô4Ü"Ø#Ø!Ø#Ø"ØØ ØØÜ#+§?¡?Ó#4×#>Ñ#>Ó#@ô
ð 
ô 	�‰Ð.ÌÐM_ÓI`ˆÔaØ"Ÿo™o×?Ñ?Ð@RÓSÐô 	�‰Ð*Ô+Ø $× 3Ñ 3× DÑ DÐEXÓ Y×Yˆô Ñ=¨_Ô=Ó=ˆÜ˜1˜c LÑ0Ó1ˆØ×)Ñ)¨,Ó7ˆä�‰ØØ%Ø%Øð	 	ô 	
ô 	�‰Ð/Ô0Ø×,Ñ,¨_Ó=ˆ	ô !ØØØØØ%Ø%Ø$ØÜ'Ÿ™Ó0×:Ñ:Ó<ô

ˆô 	�‰Ø"ØØÜ˜Ó/Ø%ð 	ô 	
ð ˆðo
ùð.
ùð2 Zús9   ‚A"J	Á$JÁ%BJ	Ã4JÃ5B=J	Æ2JÆ3CJ	ÊJ	ÊJ	Úprompt_fileÚ	id_prefixr3   rr   Ú
diff_indexNc              ƒ   óØ  ‡ ‡K  — 	 ‰ j                   j                  ||||«      ƒ d{  –—† \  }}g }	g }
|D ]ê  }t        ||«      }|€Œt        |j                  |j
                  |j                  |j                  |¬«      \  }}|s|
j                  |«       Œat        |j                  |j                  ||j
                  ¬«      }|�J||j
                  k7  r;t        d|j                  |j
                  z
  «      }|j                  |||z   dœ¬«      }|	j                  |«       Œì |
r/t        j                  d‰t        |	«      t        |
«      |
dd ¬	«       |	st        j                  d
‰¬«       ‰g dfS t        j                   ˆˆ fd„|	D «       ddiŽƒ d{  –—† }g }t#        |	|«      D ]½  \  }}t%        |t&        «      r|j)                  «       r|n|j*                  }|j                  t-        |j.                  |j0                  |j*                  |j2                  |j4                  |j6                  |j                  |j
                  |j                  |j                  ‰|¬«      «       Œ¿ t        j                  d‰t        |«      ¬«       ‰|dfS 7 �Œe7 Œû# t8        $ rM}t        j;                  d‰t'        |«      t=        |«      j>                  ¬«       ‰g t'        |«      fcY d}~S d}~ww xY w­w)aÚ  
        Run searcher + voice rewriter for ONE character end-to-end.

        Returns (character_id, finished_findings, error_message_or_None).
        Voice rewrites for that character's findings are awaited in parallel
        so the per-character pipeline finishes as fast as its slowest finding.
        Skips the LLM validator pass (which would force us to wait for ALL
        searchers before any character can finish, breaking the streaming UX)
        and replaces it with a deterministic grounding filter that drops any
        finding whose file/line/snippet does not exist in `diff_index`. That
        kills hallucinations like `frontend/UserDashboard.tsx:123` without
        adding a single extra LLM call.
        N)r?   rB   rD   rE   Úindex)Úsnippetr@   r·   Ú	hint_liner   )rB   rD   )ÚupdateÚcharacter_grounding_filterr)   )rr   ÚkeptÚdroppedÚreasonsÚcharacter_pipeline_no_findings)rr   c              3   óV   •K  — | ]   }‰j                   j                  ‰|«      –— Œ" y ­wr›   )rZ   Úrewrite_finding)rž   Úfvrr   r\   s     €€rR   rŸ   zHPRAnalysisOrchestrator._run_single_character_pipeline.<locals>.<genexpr>k  s,   øè ø€ ò àð ×'Ñ'×7Ñ7¸Àb×Iñùs   ƒ&)Úreturn_exceptionsT)r7   r9   r<   r6   r=   r>   r?   rB   rD   rE   rr   Úcharacter_dialogueÚcharacter_pipeline_complete)rr   r¥   Úcharacter_pipeline_failed)rr   rH   Ú
error_type) rX   Ú_run_character_searcherrS   r   r?   rB   rD   rE   rs   r   r­   Ú
model_copyrN   r[   ru   ÚasyncioÚgatherÚzipÚ
isinstancerP   Ústripr<   r
   r7   r9   r6   r=   r>   rM   rH   ÚtypeÚ__name__)r\   r³   r´   r3   rr   r…   rµ   Ú_Úraw_findingsÚ	validatedr½   ÚrawÚ
normalizedÚgroundedÚreasonÚ	real_lineÚwidthÚdialogue_resultsÚfinishedrÂ   r€   ÚvoicedrQ   s   `   `                  rR   Ú_run_single_character_pipelinez5PRAnalysisOrchestrator._run_single_character_pipeline  s  ùè ø€ ð,j	.Ø$(§H¡H×$DÑ$DØ˜T 9¨hó%÷ ‰OˆAˆ|ð 13ˆIØ!#ˆGØ#ò "-�Ü8¸¸hÓG�
ØÐ%ØÜ#.Ø(×2Ñ2Ø)×4Ñ4Ø'×0Ñ0Ø!+×!8Ñ!8Ø$ô$Ñ �˜&ñ  Ø—N‘N 6Ô*Øô .Ø&×3Ñ3Ø#×-Ñ-Ø$Ø(×3Ñ3ô	�	ð Ð(¨Y¸*×:OÑ:OÒ-OÜ  :×#6Ñ#6¸×9NÑ9NÑ#NÓO�EØ!+×!6Ñ!6à*3Ø(1°EÑ(9ñ ð "7ó "�Jð × Ñ  Õ,ðE"-ñH Ü—‘Ø0Ø!-Ü˜Y›Ü ›LØ# C R˜Lð ô ñ Ü—‘Ø4Ø!-ð ô ð $ R¨Ð-Ð-ô &-§^¡^ôà'ôð&ð
 #'ñ&÷  Ðð ')ˆHÜ # IÐ/?Ó @ò ‘��Hô " (¬CÔ0°X·^±^Ô5Eñ àŸ™ð ð
 —‘ÜØŸ5™5Ø Ÿh™hØ$&§N¡NØ!#§¡Ø!Ÿy™yØ$&§N¡NØ"$§,¡,Ø#%§=¡=Ø!#§¡Ø%'§_¡_Ø%1Ø+1ôõðô. �K‰KØ-Ø)Ü" 8›}ð ô ð
   ¨4Ð/Ð/ðAùðv ùôN ò 	.Ü�L‰LØ+Ø)Ü˜#“hÜ ›9×-Ñ-ð	 ô ð   ¤S¨£XÐ-Õ-ûð	.üse   „K*†!J §J¨EJ Å0K*Å1"J ÆJÆC7J ÊK*ÊJ ÊJ Ê	K'ÊAK"ËK'ËK*Ë"K'Ë'K*c                óÆ  K  — t         j                  d|t        |«      ¬«       t        |«      }t         j                  dt        |j                  «      t        d„ |j                  j                  «       D «       «      ¬«       t        D �cg c]  }|d   ‘Œ	 }	}d||||	dœd	œ­–— |	D ]  }
d
d|
id	œ­–— Œ t        D ����cg c]1  \  }}}}t        j                  | j                  ||||||¬«      «      ‘Œ3 }}}}}g }	 t        j                  |«      D ]V  }|ƒ d{  –—† \  }}}|�d||dœd	œ­–— Œ|j                  |«       d||D �cg c]  }|j                  «       ‘Œ c}dœd	œ­–— ŒX 	 t        d„ |D «       «      }t#        dd|z
  «      }| j%                  |«      }t'        |||||||| j)                  |«      t+        j,                  «       j/                  «       ¬«	      }t         j                  d||t        |«      |¬«       d|j                  «       d	œ­–— yc c}w c c}}}}w 7 �Œc c}w # t        j                  $ r |D ]  }|j!                  «        Œ ‚ w xY w­w)uÞ  
        Run the per-character pipeline and yield events as each character finishes.

        Event shape (each is a dict with `event` + `data`):
          - {"event": "started",            "data": {pr_number, pr_title, pr_author, characters: [...]}}
          - {"event": "character_started",  "data": {"character_id": id}}
          - {"event": "character_complete", "data": {"character_id": id, "findings": [...]}}
          - {"event": "character_error",    "data": {"character_id": id, "error": str}}
          - {"event": "complete",           "data": EncounterResult.model_dump()}

        Failures in one character do NOT abort the others â€” they emit
        `character_error` and the remaining characters continue.
        Ústream_analysis_start)r‚   rŠ   Údiff_indexedc              3   ó2   K  — | ]  }t        |«      –— Œ y ­wr›   )ru   )rž   Úvs     rR   rŸ   z>PRAnalysisOrchestrator.analyze_pr_streaming.<locals>.<genexpr>½  s   è ø€ ÒL qœC ŸFÑLùs   ‚)ÚfilesÚadded_linesé   Ústarted)r‚   rƒ   r„   Ú
characters)ÚeventÚdataÚcharacter_startedrr   )r³   r´   r3   rr   r…   rµ   NÚcharacter_error)rr   rH   Úcharacter_complete)rr   rh   c              3   ó4   K  — | ]  }|j                   –— Œ y ­wr›   rœ   r�   s     rR   rŸ   z>PRAnalysisOrchestrator.analyze_pr_streaming.<locals>.<genexpr>  s   è ø€ Ò:¨˜1Ÿ8�8Ñ:ùr    r   rŽ   r�   Ústream_analysis_completer¤   Úcomplete)rN   r[   ru   r   rã   r¬   rä   Úvaluesr%   rÊ   Úcreate_taskrÝ   Úas_completedÚextendÚ
model_dumpÚCancelledErrorÚcancelr­   rg   r	   r�   r   r§   r¨   )r\   r‚   rƒ   r„   r…   r†   r‡   rµ   ÚcÚall_character_idsÚchar_idr³   r´   r3   rr   ÚtasksÚall_findingsÚcororh   rH   r2   Útr_   r‘   r�   r±   s                             rR   Úanalyze_pr_streamingz+PRAnalysisOrchestrator.analyze_pr_streamingš  s¹  è ø€ ô, 	�‰Ø#ØÜ˜D›	ð 	ô 	
ô   Ó%ˆ
Ü�‰ØÜ�j×&Ñ&Ó'ÜÑL¨J×,BÑ,B×,IÑ,IÓ,KÔLÓLð 	ô 	
ô ,>Ö> a˜Q˜q›TÐ>ÐÐ>ð à&Ø$Ø&Ø/ñ	ñ
ó 	
ð )ò 	ˆGà,Ø'¨Ð1ñô ð	ô( EW÷
ñ 
ñ A�˜i¨°<ô ×ÑØ×3Ñ3Ø +Ø'Ø%Ø!-ØØ)ð 4ó õ	ð
ˆó 
ð ')ˆð	Ü×,Ñ,¨UÓ3ò �Ø6:·
Ñ-�˜h¨ØÐ$à!2à,8Ø%*ñ!ñó ð à×#Ñ# HÔ-à1à(4Ø=EÖ$F¸ Q§\¡\¥^Ò$Fññô ñô6 Ñ:¨\Ô:Ó:ˆÜ˜1˜c LÑ0Ó1ˆØ×)Ñ)¨,Ó7ˆä ØØØØØ%Ø%Ø!Ø×.Ñ.¨|Ó<Ü'Ÿ™Ó0×:Ñ:Ó<ô

ˆô 	�‰Ø&ØØÜ˜|Ó,Ø%ð 	ô 	
ð #¨F×,=Ñ,=Ó,?Ñ@Ô@ùòw ?ùõ.
ð$ 1;ûò  %Gøô ×%Ñ%ò 	ð ò �Ø—‘•
ðàð	üs[   ‚BI!Â
HÂ+I!Ã6H"
Ã7I!Ã?H2 ÄH*Ä-H2 Å
H-
Å!H2 Å-B=I!È*H2 È-H2 È2,IÉI!)rÐ   Ú
__module__Ú__qualname__Ú__doc__r]   Úintr   rg   Úlistr
   r   r�   rP   Údictr	   r²   r   ÚtuplerÝ   r   rþ   rf   r^   rR   rU   rU   Y   se  „ Ù9ò0ð¨sð °wó ð +¨D°©Mð +¸dÐCTÑ>Uó +ðZwàðwð ðwð ð	wð
 ðwð ðwð ˜C ˜H‘~ðwð 
ówðr@.àð@.ð ð@.ð ð	@.ð
 ð@.ð ð@.ð ð@.ð 
ˆs�D˜‘M 3¨¡:Ð-Ñ	.ó@.ðDAAàðAAð ðAAð ð	AAð
 ðAAð ðAAð ˜C ˜H‘~ðAAð 
˜˜d˜
Ñ	#ôAAr^   rU   Ú_orchestratorc                  ó.   — t         €
t        «       a t         S )z+Get or create global orchestrator instance.)r  rU   rf   r^   rR   Úget_orchestratorr  "  s   € ô ÐÜ.Ó0ˆÜÐr^   )'r  rÊ   r   Útypingr   r   Úapp.clients.bob_clientr   Úapp.logging_configr   Ú
app.modelsr   r	   r
   r   r   Úapp.services.classifierr   Úapp.services.diff_groundingr   r   r   r   Úapp.services.voice_rewriterr   rÐ   rN   r%   r  r  rP   Ú__annotations__rK   rL   r  rS   rU   r  r  rf   r^   rR   ú<module>r     sÚ   ðòó Ý ß &å 1Ý )÷õ õ 3÷ó õ ;á	�HÓ	€ò7Ð �D˜˜s C¨¨cÐ1Ñ2Ñ3ó ð !#¨B¸"ÀRÑHÐ àØØØñ	Ð ðØðØðà˜Ñó÷8BAñ BAðL 04€Ð%¨Ñ,Ó 3ðÐ0ô r^   