1717from .findings import Finding
1818from .redaction import redact_text
1919
20- _FENCE_RE = re .compile (r"```(?:json)?\s*(\{.*? \})\s*```" , re .DOTALL )
20+ _FENCE_RE = re .compile (r"```(?:json)?\s*(\{.*\})\s*```" , re .DOTALL )
2121
2222
23- def parse_llm_output (text : str ):
23+ def parse_llm_output (text : str ) -> tuple [ list [ dict ], str ] :
2424 """Extract (findings_dicts, summary) from model text; ([], "") on failure."""
2525 candidates = [text .strip ()]
2626 fence = _FENCE_RE .search (text )
@@ -36,24 +36,27 @@ def parse_llm_output(text: str):
3636 return [], ""
3737
3838
39- async def run_llm_review (agent , diff_text : str , static_findings ):
39+ async def run_llm_review (agent , diff_text : str , static_findings ) -> tuple [ list [ Finding ], str , list [ str ]] :
4040 """Run one review turn. Returns (llm_findings, summary, warnings)."""
41- warnings = []
41+ warnings : list [ str ] = []
4242 runner = Runner (app_name = "code_review_agent" , agent = agent ,
4343 session_service = InMemorySessionService ())
4444 prompt = REVIEW_REQUEST_TEMPLATE .format (
4545 findings_json = json .dumps ([f .model_dump () for f in static_findings ]),
4646 diff = redact_text (diff_text ))
4747 message = Content (role = "user" , parts = [Part .from_text (text = prompt )])
4848 final_text = ""
49- async for event in runner .run_async (user_id = "cr_user" ,
50- session_id = uuid .uuid4 ().hex ,
51- new_message = message ):
52- if event .partial or not event .content or not event .content .parts :
53- continue
54- for part in event .content .parts :
55- if getattr (part , "text" , None ) and not getattr (part , "thought" , None ):
56- final_text += part .text
49+ try :
50+ async for event in runner .run_async (user_id = "cr_user" ,
51+ session_id = uuid .uuid4 ().hex ,
52+ new_message = message ):
53+ if event .partial or not event .content or not event .content .parts :
54+ continue
55+ for part in event .content .parts :
56+ if getattr (part , "text" , None ) and not getattr (part , "thought" , None ):
57+ final_text += part .text
58+ finally :
59+ await runner .close ()
5760 raw_findings , summary = parse_llm_output (final_text )
5861 if not summary and final_text :
5962 warnings .append ("llm output was not valid JSON; ignored" )
0 commit comments