14
14
from codemodder .codetf import CodeTF
15
15
from codemodder .context import CodemodExecutionContext
16
16
from codemodder .dependency import Dependency
17
- from codemodder .llm import MisconfiguredAIClient
17
+ from codemodder .llm import MisconfiguredAIClient , TokenUsage , log_token_usage
18
18
from codemodder .logging import configure_logger , log_list , log_section , logger
19
19
from codemodder .project_analysis .file_parsers .package_store import PackageStore
20
20
from codemodder .project_analysis .python_repo_manager import PythonRepoManager
@@ -46,7 +46,7 @@ def find_semgrep_results(
46
46
return run_semgrep (context , yaml_files , files_to_analyze )
47
47
48
48
49
- def log_report (context , output , elapsed_ms , files_to_analyze ):
49
+ def log_report (context , output , elapsed_ms , files_to_analyze , token_usage ):
50
50
log_section ("report" )
51
51
logger .info ("scanned: %s files" , len (files_to_analyze ))
52
52
all_failures = context .get_failed_files ()
@@ -62,6 +62,7 @@ def log_report(context, output, elapsed_ms, files_to_analyze):
62
62
len (set (all_changes )),
63
63
)
64
64
logger .info ("report file: %s" , output )
65
+ log_token_usage ("All" , token_usage )
65
66
logger .info ("total elapsed: %s ms" , elapsed_ms )
66
67
logger .info (" semgrep: %s ms" , context .timer .get_time_ms ("semgrep" ))
67
68
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):
72
73
def apply_codemods (
73
74
context : CodemodExecutionContext ,
74
75
codemods_to_run : Sequence [BaseCodemod ],
75
- ):
76
+ ) -> TokenUsage :
76
77
log_section ("scanning" )
78
+ token_usage = TokenUsage ()
77
79
78
80
if not context .files_to_analyze :
79
81
logger .info ("no files to scan" )
80
- return
82
+ return token_usage
81
83
82
84
if not codemods_to_run :
83
85
logger .info ("no codemods to run" )
84
- return
86
+ return token_usage
85
87
86
88
# run codemods one at a time making sure to respect the given sequence
87
89
for codemod in codemods_to_run :
88
90
# NOTE: this may be used as a progress indicator by upstream tools
89
91
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
+
91
97
record_dependency_update (context .process_dependencies (codemod .id ))
92
98
context .log_changes (codemod .id )
99
+ return token_usage
93
100
94
101
95
102
def record_dependency_update (dependency_results : dict [Dependency , PackageStore | None ]):
@@ -128,7 +135,7 @@ def run(
128
135
codemod_registry : registry .CodemodRegistry | None = None ,
129
136
sast_only : bool = False ,
130
137
ai_client : bool = True ,
131
- ) -> tuple [CodeTF | None , int ]:
138
+ ) -> tuple [CodeTF | None , int , TokenUsage ]:
132
139
start = datetime .datetime .now ()
133
140
134
141
codemod_registry = codemod_registry or registry .load_registered_codemods ()
@@ -139,6 +146,7 @@ def run(
139
146
codemod_exclude = codemod_exclude or []
140
147
141
148
provider_registry = providers .load_providers ()
149
+ token_usage = TokenUsage ()
142
150
143
151
log_section ("startup" )
144
152
logger .info ("codemodder: python/%s" , __version__ )
@@ -148,7 +156,7 @@ def run(
148
156
logger .error (
149
157
f"FileNotFoundError: [Errno 2] No such file or directory: '{ file_name } '"
150
158
)
151
- return None , 1
159
+ return None , 1 , token_usage
152
160
153
161
repo_manager = PythonRepoManager (Path (directory ))
154
162
@@ -168,7 +176,8 @@ def run(
168
176
)
169
177
except MisconfiguredAIClient as e :
170
178
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
172
181
173
182
context .repo_manager .parse_project ()
174
183
@@ -194,10 +203,7 @@ def run(
194
203
context .find_and_fix_paths ,
195
204
)
196
205
197
- apply_codemods (
198
- context ,
199
- codemods_to_run ,
200
- )
206
+ token_usage = apply_codemods (context , codemods_to_run )
201
207
202
208
elapsed = datetime .datetime .now () - start
203
209
elapsed_ms = int (elapsed .total_seconds () * 1000 )
@@ -217,8 +223,9 @@ def run(
217
223
output ,
218
224
elapsed_ms ,
219
225
[] if not codemods_to_run else context .files_to_analyze ,
226
+ token_usage ,
220
227
)
221
- return codetf , 0
228
+ return codetf , 0 , token_usage
222
229
223
230
224
231
def _run_cli (original_args ) -> int :
@@ -258,7 +265,7 @@ def _run_cli(original_args) -> int:
258
265
logger .info ("command: %s %s" , Path (sys .argv [0 ]).name , " " .join (original_args ))
259
266
configure_logger (argv .verbose , argv .log_format , argv .project_name )
260
267
261
- _ , status = run (
268
+ _ , status , _ = run (
262
269
argv .directory ,
263
270
argv .dry_run ,
264
271
argv .output ,
0 commit comments