1414from codemodder .codetf import CodeTF
1515from codemodder .context import CodemodExecutionContext
1616from codemodder .dependency import Dependency
17- from codemodder .llm import MisconfiguredAIClient
17+ from codemodder .llm import MisconfiguredAIClient , TokenUsage , log_token_usage
1818from codemodder .logging import configure_logger , log_list , log_section , logger
1919from codemodder .project_analysis .file_parsers .package_store import PackageStore
2020from codemodder .project_analysis .python_repo_manager import PythonRepoManager
@@ -46,7 +46,7 @@ def find_semgrep_results(
4646 return run_semgrep (context , yaml_files , files_to_analyze )
4747
4848
49- def log_report (context , output , elapsed_ms , files_to_analyze ):
49+ def log_report (context , output , elapsed_ms , files_to_analyze , token_usage ):
5050 log_section ("report" )
5151 logger .info ("scanned: %s files" , len (files_to_analyze ))
5252 all_failures = context .get_failed_files ()
@@ -62,6 +62,7 @@ def log_report(context, output, elapsed_ms, files_to_analyze):
6262 len (set (all_changes )),
6363 )
6464 logger .info ("report file: %s" , output )
65+ log_token_usage ("All" , token_usage )
6566 logger .info ("total elapsed: %s ms" , elapsed_ms )
6667 logger .info (" semgrep: %s ms" , context .timer .get_time_ms ("semgrep" ))
6768 logger .info (" parse: %s ms" , context .timer .get_time_ms ("parse" ))
@@ -72,24 +73,30 @@ def log_report(context, output, elapsed_ms, files_to_analyze):
7273def apply_codemods (
7374 context : CodemodExecutionContext ,
7475 codemods_to_run : Sequence [BaseCodemod ],
75- ):
76+ ) -> TokenUsage :
7677 log_section ("scanning" )
78+ token_usage = TokenUsage ()
7779
7880 if not context .files_to_analyze :
7981 logger .info ("no files to scan" )
80- return
82+ return token_usage
8183
8284 if not codemods_to_run :
8385 logger .info ("no codemods to run" )
84- return
86+ return token_usage
8587
8688 # run codemods one at a time making sure to respect the given sequence
8789 for codemod in codemods_to_run :
8890 # NOTE: this may be used as a progress indicator by upstream tools
8991 logger .info ("running codemod %s" , codemod .id )
90- codemod .apply (context )
92+ codemod_token_usage = codemod .apply (context )
93+ if codemod_token_usage :
94+ log_token_usage (f"Codemod { codemod .id } " , codemod_token_usage )
95+ token_usage += codemod_token_usage
96+
9197 record_dependency_update (context .process_dependencies (codemod .id ))
9298 context .log_changes (codemod .id )
99+ return token_usage
93100
94101
95102def record_dependency_update (dependency_results : dict [Dependency , PackageStore | None ]):
@@ -128,7 +135,7 @@ def run(
128135 codemod_registry : registry .CodemodRegistry | None = None ,
129136 sast_only : bool = False ,
130137 ai_client : bool = True ,
131- ) -> tuple [CodeTF | None , int ]:
138+ ) -> tuple [CodeTF | None , int , TokenUsage ]:
132139 start = datetime .datetime .now ()
133140
134141 codemod_registry = codemod_registry or registry .load_registered_codemods ()
@@ -139,6 +146,7 @@ def run(
139146 codemod_exclude = codemod_exclude or []
140147
141148 provider_registry = providers .load_providers ()
149+ token_usage = TokenUsage ()
142150
143151 log_section ("startup" )
144152 logger .info ("codemodder: python/%s" , __version__ )
@@ -148,7 +156,7 @@ def run(
148156 logger .error (
149157 f"FileNotFoundError: [Errno 2] No such file or directory: '{ file_name } '"
150158 )
151- return None , 1
159+ return None , 1 , token_usage
152160
153161 repo_manager = PythonRepoManager (Path (directory ))
154162
@@ -168,7 +176,8 @@ def run(
168176 )
169177 except MisconfiguredAIClient as e :
170178 logger .error (e )
171- return None , 3 # Codemodder instructions conflicted (according to spec)
179+ # Codemodder instructions conflicted (according to spec)
180+ return None , 3 , token_usage
172181
173182 context .repo_manager .parse_project ()
174183
@@ -194,10 +203,7 @@ def run(
194203 context .find_and_fix_paths ,
195204 )
196205
197- apply_codemods (
198- context ,
199- codemods_to_run ,
200- )
206+ token_usage = apply_codemods (context , codemods_to_run )
201207
202208 elapsed = datetime .datetime .now () - start
203209 elapsed_ms = int (elapsed .total_seconds () * 1000 )
@@ -217,8 +223,9 @@ def run(
217223 output ,
218224 elapsed_ms ,
219225 [] if not codemods_to_run else context .files_to_analyze ,
226+ token_usage ,
220227 )
221- return codetf , 0
228+ return codetf , 0 , token_usage
222229
223230
224231def _run_cli (original_args ) -> int :
@@ -258,7 +265,7 @@ def _run_cli(original_args) -> int:
258265 logger .info ("command: %s %s" , Path (sys .argv [0 ]).name , " " .join (original_args ))
259266 configure_logger (argv .verbose , argv .log_format , argv .project_name )
260267
261- _ , status = run (
268+ _ , status , _ = run (
262269 argv .directory ,
263270 argv .dry_run ,
264271 argv .output ,
0 commit comments