Setting the file. One moment.
Render Report · LLM To Bedrock · aws/agent-toolkit-for-aws · Skills Docs
ContentsBack to the top of the page 81
Creating Amazon Aurora Db Cluster With Instances
104
Routing Traffic With Route53 And CloudFront
Resilience Program Design
Creating API Gateway Stage
scripts/ render_report.py
Python · 142 lines · 6 KB
17
18 summarize(payload) -> str is pure (testable).
19 """
20 import json, re, sys, shutil, pathlib, argparse
21
22
23 def summarize (payload: dict ) -> str :
24 rw = payload.get( "rewrite" ) or {}
25 ev = payload.get( "evalRes" ) or {}
26 rw = rw if isinstance (rw, dict ) else {}
27 ev = ev if isinstance (ev, dict ) else {}
28
29 files = rw.get( "files_changed" ) or []
30 notes = ev.get( "notes" ) or ""
31
32 # The evaluator emits pass_rate 1.0 with a `no_golden_cases: true` notes
33 # prefix when the golden dataset was empty — rendering that as "100%"
34 # would misrepresent a run with zero quality scoring.
35 if "no_golden_cases: true" in notes:
36 pass_line = "- Prompt eval pass rate: N/A (no golden cases — quality scoring skipped)"
37 else :
38 pass_rate = ev.get( "pass_rate" )
39 if isinstance (pass_rate, ( int , float )):
40 pass_line = f "- Prompt eval pass rate: { round (pass_rate * 100 ) } % ( { ev.get( 'total_cases' , 0 ) } cases, { ev.get( 'failures' , 0 ) } failures)"
41 m = re.search( r "partial_coverage: (\S + ) " , notes)
42 if m:
43 pass_line += f " — partial coverage { m.group( 1 ) } (throttled)"
44 else :
45 pass_line = "- Prompt eval pass rate: (unavailable)"
46
47 # Test counts live only in the rewriter's free-text notes (e.g. "5 tests
48 # generated, 5/5 passing"); show them when parseable, omit otherwise.
49 m = re.search( r " (\d + )\s * / \s * (\d + )\s + passing" , rw.get( "notes" ) or "" )
50 test_line = f "- Tests: { m.group( 1 ) } / { m.group( 2 ) } passing" if m else None
51
52 deltas = payload.get( "deltaDecisions" )
53 delta_line = ( f "- Behavior-delta decisions applied: { len (deltas) } "
54 if isinstance (deltas, list ) and deltas else None )
55
56 lines = [
57 "AI Migration Complete!" ,
58 f "- Branch: { rw.get( 'branch_name' , '(none)' ) } " ,
59 pass_line,
60 f "- Files modified: { len (files) } files" ,
61 test_line,
62 delta_line,
63 f "- Report: { find_report_path(payload) or '(see repository root: MIGRATION_REPORT_*.md)' } " ,
64 ]
65 return " \n " .join(l for l in lines if l)
66
67
68 def find_report_path (payload: dict ) -> str | None :
69 """Locate MIGRATION_REPORT_<suffix>.md from the payload's repo path."""
70 repo = payload.get( "repo" )
71 suffix = payload.get( "reportDateSuffix" )
72 if not repo:
73 return None
74 if suffix:
75 p = pathlib.Path(repo) / f "MIGRATION_REPORT_ { suffix } .md"
76 if p.exists():
77 return str (p)
78 candidates = sorted (pathlib.Path(repo).glob( "MIGRATION_REPORT_*.md" ))
79 return str (candidates[ - 1 ]) if candidates else None
80
81
82 def load_phase_results (results_dir: str , repo: str , date_suffix: str | None ) -> dict :
83 """Assemble the summarize() payload from phase-result files. Missing or
84 control-state files degrade to empty dicts (summarize handles absence)."""
85 d = pathlib.Path(results_dir)
86
87 def read (name):
88 try :
89 data = json.loads((d / name).read_text())
90 except ( OSError , json.JSONDecodeError):
91 return None
92 # A blocked/partial control-state file is not a payload.
93 if isinstance (data, dict ) and ( "blocked" in data or "partial" in data):
94 return None
95 return data
96
97 return {
98 "rewrite" : read( "rewrite.json" ) or {},
99 "evalRes" : read( "eval.json" ) or {},
100 "deltaDecisions" : read( "delta-decisions.json" ),
101 "repo" : repo,
102 "reportDateSuffix" : date_suffix,
103 }
104
105
106 def main (argv = None ) -> int :
107 ap = argparse.ArgumentParser()
108 ap.add_argument( "payload_json" , nargs = "?" ,
109 help = "legacy single-JSON payload path (mutually exclusive with --phase-results)" )
110 ap.add_argument( "--phase-results" , metavar = "DIR" ,
111 help = "phase-results directory (reads rewrite.json/eval.json/delta-decisions.json)" )
112 ap.add_argument( "--repo" , help = "repository root (required with --phase-results)" )
113 ap.add_argument( "--date-suffix" , help = "report date suffix YYYY-MM-DD (with --phase-results)" )
114 ap.add_argument( "--results-dir" , default = str (pathlib.Path.home() / "saws-migrate-results" ),
115 help = "directory the report file is copied to (both modes)" )
116 args = ap.parse_args(argv)
117
118 if bool (args.payload_json) == bool (args.phase_results):
119 ap.error( "provide exactly one of: payload_json, --phase-results" )
120
121 if args.phase_results:
122 if not args.repo:
123 ap.error( "--phase-results requires --repo" )
124 payload = load_phase_results(args.phase_results, args.repo, args.date_suffix)
125 else :
126 try :
127 payload = json.loads(pathlib.Path(args.payload_json).read_text())
128 except ( OSError , json.JSONDecodeError) as e:
129 print ( f "render_report: cannot read payload JSON: { e } " , file = sys.stderr)
130 return 1
131
132 report_path = find_report_path(payload)
133 if report_path:
134 dest_dir = pathlib.Path(args.results_dir)
135 dest_dir.mkdir( parents = True , exist_ok = True )
136 shutil.copy2(report_path, dest_dir / pathlib.Path(report_path).name)
137 print (summarize(payload))
138 return 0
139
140
141 if __name__ == "__main__" :
142 sys.exit(main())