auto-coder 0.1.363__py3-none-any.whl → 0.1.364__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

Files changed (33) hide show
  1. {auto_coder-0.1.363.dist-info → auto_coder-0.1.364.dist-info}/METADATA +2 -2
  2. {auto_coder-0.1.363.dist-info → auto_coder-0.1.364.dist-info}/RECORD +33 -18
  3. autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +1 -1
  4. autocoder/auto_coder_runner.py +2 -0
  5. autocoder/common/__init__.py +2 -0
  6. autocoder/common/file_checkpoint/__init__.py +21 -0
  7. autocoder/common/file_checkpoint/backup.py +264 -0
  8. autocoder/common/file_checkpoint/examples.py +217 -0
  9. autocoder/common/file_checkpoint/manager.py +404 -0
  10. autocoder/common/file_checkpoint/models.py +156 -0
  11. autocoder/common/file_checkpoint/store.py +383 -0
  12. autocoder/common/file_checkpoint/test_backup.py +242 -0
  13. autocoder/common/file_checkpoint/test_manager.py +570 -0
  14. autocoder/common/file_checkpoint/test_models.py +360 -0
  15. autocoder/common/file_checkpoint/test_store.py +327 -0
  16. autocoder/common/file_checkpoint/test_utils.py +297 -0
  17. autocoder/common/file_checkpoint/utils.py +119 -0
  18. autocoder/common/rulefiles/autocoderrules_utils.py +114 -55
  19. autocoder/common/save_formatted_log.py +76 -5
  20. autocoder/common/v2/agent/agentic_edit.py +318 -197
  21. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +2 -2
  22. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +27 -4
  23. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +322 -0
  24. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +83 -61
  25. autocoder/compilers/normal_compiler.py +64 -0
  26. autocoder/events/event_manager_singleton.py +133 -4
  27. autocoder/linters/normal_linter.py +373 -0
  28. autocoder/linters/python_linter.py +4 -2
  29. autocoder/version.py +1 -1
  30. {auto_coder-0.1.363.dist-info → auto_coder-0.1.364.dist-info}/LICENSE +0 -0
  31. {auto_coder-0.1.363.dist-info → auto_coder-0.1.364.dist-info}/WHEEL +0 -0
  32. {auto_coder-0.1.363.dist-info → auto_coder-0.1.364.dist-info}/entry_points.txt +0 -0
  33. {auto_coder-0.1.363.dist-info → auto_coder-0.1.364.dist-info}/top_level.txt +0 -0
@@ -40,9 +40,15 @@ from autocoder.linters.shadow_linter import ShadowLinter
40
40
  from autocoder.compilers.shadow_compiler import ShadowCompiler
41
41
  from autocoder.common.action_yml_file_manager import ActionYmlFileManager
42
42
  from autocoder.common.auto_coder_lang import get_message
43
+ from autocoder.common.save_formatted_log import save_formatted_log
43
44
  # Import the new display function
44
45
  from autocoder.common.v2.agent.agentic_tool_display import get_tool_display_message
45
46
  from autocoder.common.v2.agent.agentic_edit_types import FileChangeEntry
47
+
48
+ from autocoder.common.file_checkpoint.models import FileChange as CheckpointFileChange
49
+ from autocoder.common.file_checkpoint.manager import FileChangeManager as CheckpointFileChangeManager
50
+ from autocoder.linters.normal_linter import NormalLinter
51
+ from autocoder.compilers.normal_compiler import NormalCompiler
46
52
  from autocoder.common.v2.agent.agentic_edit_tools import ( # Import specific resolvers
47
53
  BaseToolResolver,
48
54
  ExecuteCommandToolResolver, ReadFileToolResolver, WriteToFileToolResolver,
@@ -51,7 +57,7 @@ from autocoder.common.v2.agent.agentic_edit_tools import ( # Import specific re
51
57
  AttemptCompletionToolResolver, PlanModeRespondToolResolver, UseMcpToolResolver,
52
58
  ListPackageInfoToolResolver
53
59
  )
54
- from autocoder.common.rulefiles.autocoderrules_utils import get_rules
60
+ from autocoder.common.rulefiles.autocoderrules_utils import get_rules,auto_select_rules,get_required_and_index_rules
55
61
  from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, ToolResult,
56
62
  MemoryConfig, CommandConfig, BaseTool,
57
63
  ExecuteCommandTool, ReadFileTool,
@@ -101,7 +107,7 @@ class AgenticEdit:
101
107
  args: AutoCoderArgs,
102
108
  memory_config: MemoryConfig,
103
109
  command_config: Optional[CommandConfig] = None,
104
- conversation_name: str = "current"
110
+ conversation_name:Optional[str] = "current"
105
111
  ):
106
112
  self.llm = llm
107
113
  self.args = args
@@ -116,11 +122,22 @@ class AgenticEdit:
116
122
  self.project_type_analyzer = ProjectTypeAnalyzer(
117
123
  args=args, llm=self.llm)
118
124
 
119
- self.shadow_manager = ShadowManager(
120
- args.source_dir, args.event_file, args.ignore_clean_shadows)
121
- self.shadow_linter = ShadowLinter(self.shadow_manager, verbose=False)
122
- self.shadow_compiler = ShadowCompiler(
123
- self.shadow_manager, verbose=False)
125
+ # self.shadow_manager = ShadowManager(
126
+ # args.source_dir, args.event_file, args.ignore_clean_shadows)
127
+ self.shadow_manager = None
128
+ # self.shadow_linter = ShadowLinter(self.shadow_manager, verbose=False)
129
+ self.shadow_compiler = None
130
+ # self.shadow_compiler = ShadowCompiler(self.shadow_manager, verbose=False)
131
+ self.shadow_linter = None
132
+
133
+ self.checkpoint_manager = CheckpointFileChangeManager(
134
+ project_dir=args.source_dir,
135
+ backup_dir=os.path.join(args.source_dir,".auto-coder","checkpoint"),
136
+ store_dir=os.path.join(args.source_dir,".auto-coder","checkpoint_store"),
137
+ max_history=50)
138
+ self.linter = NormalLinter(args.source_dir,verbose=False)
139
+ self.compiler = NormalCompiler(args.source_dir,verbose=False)
140
+
124
141
 
125
142
  self.mcp_server_info = ""
126
143
  try:
@@ -663,12 +680,14 @@ class AgenticEdit:
663
680
  The following rules are provided by the user, and you must follow them strictly.
664
681
 
665
682
  {% for key, value in extra_docs.items() %}
666
- <user_rule>
683
+ <user_rule_file>
667
684
  ##File: {{ key }}
668
685
  {{ value }}
669
- </user_rule>
670
- {% endfor %}
686
+ </user_rule_file>
687
+ {% endfor %}
688
+ Make sure you always start your task by using the read_file tool to get the relevant RULE files listed in index.md based on the user's specific requirements.
671
689
  {% endif %}
690
+
672
691
 
673
692
  ====
674
693
 
@@ -694,14 +713,15 @@ class AgenticEdit:
694
713
  {% if file_paths_str %}
695
714
  ====
696
715
  The following are files that the user is currently focusing on.
697
- Make sure you always start your analysis by using the read_file tool to get the content of the files.
716
+ Make sure you always start your task by using the read_file tool to get the content of the files.
698
717
  <files>
699
718
  {{file_paths_str}}
700
719
  </files>
701
720
  {% endif %}
702
- """
703
- import os
704
- extra_docs = get_rules()
721
+ """
722
+ ## auto_select_rules(context=request.user_input, llm=self.llm,args=self.args) rules =
723
+ # extra_docs = get_rules()
724
+ extra_docs = get_required_and_index_rules()
705
725
 
706
726
  env_info = detect_env()
707
727
  shell_type = "bash"
@@ -790,9 +810,7 @@ class AgenticEdit:
790
810
 
791
811
  logger.info(
792
812
  f"Initial conversation history size: {len(conversations)}")
793
-
794
- logger.info(f"Conversation history: {json.dumps(conversations, indent=2,ensure_ascii=False)}")
795
-
813
+
796
814
  iteration_count = 0
797
815
  tool_executed = False
798
816
  while True:
@@ -828,7 +846,7 @@ class AgenticEdit:
828
846
  event_count = 0
829
847
  for event in parsed_events:
830
848
  event_count += 1
831
- logger.info(f"Processing event #{event_count}: {type(event).__name__}")
849
+ # logger.info(f"Processing event #{event_count}: {type(event).__name__}")
832
850
  global_cancel.check_and_raise(token=self.args.event_file)
833
851
  if isinstance(event, (LLMOutputEvent, LLMThinkingEvent)):
834
852
  assistant_buffer += event.text
@@ -861,6 +879,7 @@ class AgenticEdit:
861
879
  yield CompletionEvent(completion=tool_obj, completion_xml=tool_xml)
862
880
  logger.info(
863
881
  "AgenticEdit analyze loop finished due to AttemptCompletion.")
882
+ save_formatted_log(self.args.source_dir, json.dumps(conversations, ensure_ascii=False), "agentic_conversation")
864
883
  return
865
884
 
866
885
  if isinstance(tool_obj, PlanModeRespondTool):
@@ -870,6 +889,7 @@ class AgenticEdit:
870
889
  yield PlanModeRespondEvent(completion=tool_obj, completion_xml=tool_xml)
871
890
  logger.info(
872
891
  "AgenticEdit analyze loop finished due to PlanModeRespond.")
892
+ save_formatted_log(self.args.source_dir, json.dumps(conversations, ensure_ascii=False), "agentic_conversation")
873
893
  return
874
894
 
875
895
  # Resolve the tool
@@ -972,6 +992,7 @@ class AgenticEdit:
972
992
  continue
973
993
 
974
994
  logger.info(f"AgenticEdit analyze loop finished after {iteration_count} iterations.")
995
+ save_formatted_log(self.args.source_dir, json.dumps(conversations, ensure_ascii=False), "agentic_conversation")
975
996
 
976
997
  def stream_and_parse_llm_response(
977
998
  self, generator: Generator[Tuple[str, Any], None, None]
@@ -1187,174 +1208,28 @@ class AgenticEdit:
1187
1208
  elif buffer:
1188
1209
  # Yield remaining plain text
1189
1210
  yield LLMOutputEvent(text=buffer)
1190
-
1191
- def run_with_events(self, request: AgenticEditRequest):
1192
- """
1193
- Runs the agentic edit process, converting internal events to the
1194
- standard event system format and writing them using the event manager.
1195
- """
1196
- event_manager = get_event_manager(self.args.event_file)
1197
- self.apply_pre_changes()
1198
-
1199
- try:
1200
- event_stream = self.analyze(request)
1201
- for agent_event in event_stream:
1202
- content = None
1203
- metadata = EventMetadata(
1204
- action_file=self.args.file,
1205
- is_streaming=False,
1206
- stream_out_type="/agent/edit")
1207
-
1208
- if isinstance(agent_event, LLMThinkingEvent):
1209
- content = EventContentCreator.create_stream_thinking(
1210
- content=agent_event.text)
1211
- metadata.is_streaming = True
1212
- metadata.path = "/agent/edit/thinking"
1213
- event_manager.write_stream(
1214
- content=content.to_dict(), metadata=metadata.to_dict())
1215
- elif isinstance(agent_event, LLMOutputEvent):
1216
- content = EventContentCreator.create_stream_content(
1217
- content=agent_event.text)
1218
- metadata.is_streaming = True
1219
- metadata.path = "/agent/edit/output"
1220
- event_manager.write_stream(content=content.to_dict(),
1221
- metadata=metadata.to_dict())
1222
- elif isinstance(agent_event, ToolCallEvent):
1223
- tool_name = type(agent_event.tool).__name__
1224
- metadata.path = "/agent/edit/tool/call"
1225
- content = EventContentCreator.create_result(
1226
- content={
1227
- "tool_name": tool_name,
1228
- **agent_event.tool.model_dump()
1229
- },
1230
- metadata={}
1231
- )
1232
- event_manager.write_result(
1233
- content=content.to_dict(), metadata=metadata.to_dict())
1234
- elif isinstance(agent_event, ToolResultEvent):
1235
- metadata.path = "/agent/edit/tool/result"
1236
- content = EventContentCreator.create_result(
1237
- content={
1238
- "tool_name": agent_event.tool_name,
1239
- **agent_event.result.model_dump()
1240
- },
1241
- metadata={}
1242
- )
1243
- event_manager.write_result(
1244
- content=content.to_dict(), metadata=metadata.to_dict())
1245
- elif isinstance(agent_event, PlanModeRespondEvent):
1246
- metadata.path = "/agent/edit/plan_mode_respond"
1247
- content = EventContentCreator.create_markdown_result(
1248
- content=agent_event.completion.response,
1249
- metadata={}
1250
- )
1251
- event_manager.write_result(
1252
- content=content.to_dict(), metadata=metadata.to_dict())
1253
-
1254
- elif isinstance(agent_event, TokenUsageEvent):
1255
- last_meta: SingleOutputMeta = agent_event.usage
1256
- # Get model info for pricing
1257
- from autocoder.utils import llms as llm_utils
1258
- model_name = ",".join(llm_utils.get_llm_names(self.llm))
1259
- model_info = llm_utils.get_model_info(
1260
- model_name, self.args.product_mode) or {}
1261
- input_price = model_info.get(
1262
- "input_price", 0.0) if model_info else 0.0
1263
- output_price = model_info.get(
1264
- "output_price", 0.0) if model_info else 0.0
1265
-
1266
- # Calculate costs
1267
- input_cost = (last_meta.input_tokens_count *
1268
- input_price) / 1000000 # Convert to millions
1269
- # Convert to millions
1270
- output_cost = (
1271
- last_meta.generated_tokens_count * output_price) / 1000000
1272
-
1273
- # 添加日志记录
1274
- logger.info(f"Token Usage Details: Model={model_name}, Input Tokens={last_meta.input_tokens_count}, Output Tokens={last_meta.generated_tokens_count}, Input Cost=${input_cost:.6f}, Output Cost=${output_cost:.6f}")
1275
-
1276
- get_event_manager(self.args.event_file).write_result(
1277
- EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
1278
- model_name=model_name,
1279
- elapsed_time=0.0,
1280
- first_token_time=last_meta.first_token_time,
1281
- input_tokens=last_meta.input_tokens_count,
1282
- output_tokens=last_meta.generated_tokens_count,
1283
- input_cost=input_cost,
1284
- output_cost=output_cost
1285
- ).to_dict()), metadata=metadata.to_dict())
1286
-
1287
- elif isinstance(agent_event, CompletionEvent):
1288
- # 在这里完成实际合并
1289
- try:
1290
- self.apply_changes()
1291
- except Exception as e:
1292
- logger.exception(
1293
- f"Error merging shadow changes to project: {e}")
1294
-
1295
- metadata.path = "/agent/edit/completion"
1296
- content = EventContentCreator.create_completion(
1297
- success_code="AGENT_COMPLETE",
1298
- success_message="Agent attempted task completion.",
1299
- result={
1300
- "response": agent_event.completion.result
1301
- }
1302
- )
1303
- event_manager.write_completion(
1304
- content=content.to_dict(), metadata=metadata.to_dict())
1305
- elif isinstance(agent_event, ErrorEvent):
1306
- metadata.path = "/agent/edit/error"
1307
- content = EventContentCreator.create_error(
1308
- error_code="AGENT_ERROR",
1309
- error_message=agent_event.message,
1310
- details={"agent_event_type": "ErrorEvent"}
1311
- )
1312
- event_manager.write_error(
1313
- content=content.to_dict(), metadata=metadata.to_dict())
1314
- else:
1315
- metadata.path = "/agent/edit/error"
1316
- logger.warning(
1317
- f"Unhandled agent event type: {type(agent_event)}")
1318
- content = EventContentCreator.create_error(
1319
- error_code="AGENT_ERROR",
1320
- error_message=f"Unhandled agent event type: {type(agent_event)}",
1321
- details={"agent_event_type": type(
1322
- agent_event).__name__}
1323
- )
1324
- event_manager.write_error(
1325
- content=content.to_dict(), metadata=metadata.to_dict())
1326
-
1327
- except Exception as e:
1328
- logger.exception(
1329
- "An unexpected error occurred during agent execution:")
1330
- metadata = EventMetadata(
1331
- action_file=self.args.file,
1332
- is_streaming=False,
1333
- stream_out_type="/agent/edit/error")
1334
- error_content = EventContentCreator.create_error(
1335
- error_code="AGENT_FATAL_ERROR",
1336
- error_message=f"An unexpected error occurred: {str(e)}",
1337
- details={"exception_type": type(e).__name__}
1338
- )
1339
- event_manager.write_error(
1340
- content=error_content.to_dict(), metadata=metadata.to_dict())
1341
- # Re-raise the exception if needed, or handle appropriately
1342
- raise e
1211
+
1343
1212
 
1344
1213
  def apply_pre_changes(self):
1345
1214
  # get the file name
1346
1215
  file_name = os.path.basename(self.args.file)
1347
1216
  if not self.args.skip_commit:
1348
1217
  try:
1218
+ commit_result = git_utils.commit_changes(
1219
+ self.args.source_dir, f"auto_coder_pre_{file_name}")
1349
1220
  get_event_manager(self.args.event_file).write_result(
1350
1221
  EventContentCreator.create_result(
1351
- content=self.printer.get_message_from_key("/agent/edit/apply_pre_changes")), metadata=EventMetadata(
1222
+ content={
1223
+ "have_commit":commit_result.success,
1224
+ "commit_hash":commit_result.commit_hash,
1225
+ "diff_file_num":len(commit_result.changed_files),
1226
+ "event_file":self.args.event_file
1227
+ }), metadata=EventMetadata(
1352
1228
  action_file=self.args.file,
1353
1229
  is_streaming=False,
1354
1230
  path="/agent/edit/apply_pre_changes",
1355
1231
  stream_out_type="/agent/edit").to_dict())
1356
- git_utils.commit_changes(
1357
- self.args.source_dir, f"auto_coder_pre_{file_name}")
1232
+
1358
1233
  except Exception as e:
1359
1234
  self.printer.print_in_terminal("git_init_required",
1360
1235
  source_dir=self.args.source_dir, error=str(e))
@@ -1364,16 +1239,22 @@ class AgenticEdit:
1364
1239
  """
1365
1240
  Apply all tracked file changes to the original project directory.
1366
1241
  """
1367
- for (file_path, change) in self.get_all_file_changes().items():
1368
- # Ensure the directory exists before writing the file
1369
- dir_path = os.path.dirname(file_path)
1370
- if dir_path: # Ensure dir_path is not empty (for files in root)
1371
- os.makedirs(dir_path, exist_ok=True)
1372
-
1373
- with open(file_path, 'w', encoding='utf-8') as f:
1374
- f.write(change.content)
1375
-
1376
- if len(self.get_all_file_changes()) > 0:
1242
+ diff_file_num = 0
1243
+ if self.shadow_manager:
1244
+ for (file_path, change) in self.get_all_file_changes().items():
1245
+ # Ensure the directory exists before writing the file
1246
+ dir_path = os.path.dirname(file_path)
1247
+ if dir_path: # Ensure dir_path is not empty (for files in root)
1248
+ os.makedirs(dir_path, exist_ok=True)
1249
+
1250
+ with open(file_path, 'w', encoding='utf-8') as f:
1251
+ f.write(change.content)
1252
+ diff_file_num = len(self.get_all_file_changes())
1253
+ else:
1254
+ changes = self.checkpoint_manager.get_changes_by_group(self.args.event_file)
1255
+ diff_file_num = len(changes)
1256
+
1257
+ if diff_file_num > 0:
1377
1258
  if not self.args.skip_commit:
1378
1259
  try:
1379
1260
  file_name = os.path.basename(self.args.file)
@@ -1381,13 +1262,20 @@ class AgenticEdit:
1381
1262
  self.args.source_dir,
1382
1263
  f"{self.args.query}\nauto_coder_{file_name}",
1383
1264
  )
1384
-
1265
+
1385
1266
  get_event_manager(self.args.event_file).write_result(
1386
1267
  EventContentCreator.create_result(
1387
- content=self.printer.get_message_from_key("/agent/edit/apply_changes")), metadata=EventMetadata(
1268
+ content={
1269
+ "have_commit":commit_result.success,
1270
+ "commit_hash":commit_result.commit_hash,
1271
+ "diff_file_num":diff_file_num,
1272
+ "event_file":self.args.event_file
1273
+ }), metadata=EventMetadata(
1388
1274
  action_file=self.args.file,
1389
1275
  is_streaming=False,
1276
+ path="/agent/edit/apply_changes",
1390
1277
  stream_out_type="/agent/edit").to_dict())
1278
+
1391
1279
  action_yml_file_manager = ActionYmlFileManager(
1392
1280
  self.args.source_dir)
1393
1281
  action_file_name = os.path.basename(self.args.file)
@@ -1433,6 +1321,15 @@ class AgenticEdit:
1433
1321
  console.print(Panel(
1434
1322
  f"[bold]{get_message('/agent/edit/user_query')}:[/bold]\n{request.user_input}", title=get_message("/agent/edit/objective"), border_style="blue"))
1435
1323
 
1324
+ # 用于累计TokenUsageEvent数据
1325
+ accumulated_token_usage = {
1326
+ "model_name": "",
1327
+ "input_tokens": 0,
1328
+ "output_tokens": 0,
1329
+ "input_cost": 0.0,
1330
+ "output_cost": 0.0
1331
+ }
1332
+
1436
1333
  try:
1437
1334
  self.apply_pre_changes()
1438
1335
  event_stream = self.analyze(request)
@@ -1459,17 +1356,12 @@ class AgenticEdit:
1459
1356
  # 添加日志记录
1460
1357
  logger.info(f"Token Usage: Model={model_name}, Input Tokens={last_meta.input_tokens_count}, Output Tokens={last_meta.generated_tokens_count}, Input Cost=${input_cost:.6f}, Output Cost=${output_cost:.6f}")
1461
1358
 
1462
- self.printer.print_in_terminal(
1463
- "code_generation_complete",
1464
- duration=0.0,
1465
- input_tokens=last_meta.input_tokens_count,
1466
- output_tokens=last_meta.generated_tokens_count,
1467
- input_cost=input_cost,
1468
- output_cost=output_cost,
1469
- speed=0.0,
1470
- model_names=model_name,
1471
- sampling_count=1
1472
- )
1359
+ # 累计token使用情况
1360
+ accumulated_token_usage["model_name"] = model_name
1361
+ accumulated_token_usage["input_tokens"] += last_meta.input_tokens_count
1362
+ accumulated_token_usage["output_tokens"] += last_meta.generated_tokens_count
1363
+ accumulated_token_usage["input_cost"] += input_cost
1364
+ accumulated_token_usage["output_cost"] += output_cost
1473
1365
 
1474
1366
  if isinstance(event, LLMThinkingEvent):
1475
1367
  # Render thinking within a less prominent style, maybe grey?
@@ -1592,7 +1484,35 @@ class AgenticEdit:
1592
1484
 
1593
1485
  time.sleep(0.1) # Small delay for better visual flow
1594
1486
 
1487
+ # 在处理完所有事件后打印累计的token使用情况
1488
+ if accumulated_token_usage["input_tokens"] > 0:
1489
+ self.printer.print_in_terminal(
1490
+ "code_generation_complete",
1491
+ duration=0.0,
1492
+ input_tokens=accumulated_token_usage["input_tokens"],
1493
+ output_tokens=accumulated_token_usage["output_tokens"],
1494
+ input_cost=accumulated_token_usage["input_cost"],
1495
+ output_cost=accumulated_token_usage["output_cost"],
1496
+ speed=0.0,
1497
+ model_names=accumulated_token_usage["model_name"],
1498
+ sampling_count=1
1499
+ )
1500
+
1595
1501
  except Exception as e:
1502
+ # 在处理异常时也打印累计的token使用情况
1503
+ if accumulated_token_usage["input_tokens"] > 0:
1504
+ self.printer.print_in_terminal(
1505
+ "code_generation_complete",
1506
+ duration=0.0,
1507
+ input_tokens=accumulated_token_usage["input_tokens"],
1508
+ output_tokens=accumulated_token_usage["output_tokens"],
1509
+ input_cost=accumulated_token_usage["input_cost"],
1510
+ output_cost=accumulated_token_usage["output_cost"],
1511
+ speed=0.0,
1512
+ model_names=accumulated_token_usage["model_name"],
1513
+ sampling_count=1
1514
+ )
1515
+
1596
1516
  logger.exception(
1597
1517
  "An unexpected error occurred during agent execution:")
1598
1518
  console.print(Panel(
@@ -1600,3 +1520,204 @@ class AgenticEdit:
1600
1520
  raise e
1601
1521
  finally:
1602
1522
  console.rule("[bold cyan]Agentic Edit Finished[/]")
1523
+
1524
+ def run_with_events(self, request: AgenticEditRequest):
1525
+ """
1526
+ Runs the agentic edit process, converting internal events to the
1527
+ standard event system format and writing them using the event manager.
1528
+ """
1529
+ event_manager = get_event_manager(self.args.event_file)
1530
+ self.apply_pre_changes()
1531
+
1532
+ # 用于累计TokenUsageEvent数据
1533
+ accumulated_token_usage = {
1534
+ "model_name": "",
1535
+ "elapsed_time": 0.0,
1536
+ "first_token_time": 0.0,
1537
+ "input_tokens": 0,
1538
+ "output_tokens": 0,
1539
+ "input_cost": 0.0,
1540
+ "output_cost": 0.0
1541
+ }
1542
+
1543
+ try:
1544
+ event_stream = self.analyze(request)
1545
+ for agent_event in event_stream:
1546
+ content = None
1547
+ metadata = EventMetadata(
1548
+ action_file=self.args.file,
1549
+ is_streaming=False,
1550
+ stream_out_type="/agent/edit")
1551
+
1552
+ if isinstance(agent_event, LLMThinkingEvent):
1553
+ content = EventContentCreator.create_stream_thinking(
1554
+ content=agent_event.text)
1555
+ metadata.is_streaming = True
1556
+ metadata.path = "/agent/edit/thinking"
1557
+ event_manager.write_stream(
1558
+ content=content.to_dict(), metadata=metadata.to_dict())
1559
+ elif isinstance(agent_event, LLMOutputEvent):
1560
+ content = EventContentCreator.create_stream_content(
1561
+ content=agent_event.text)
1562
+ metadata.is_streaming = True
1563
+ metadata.path = "/agent/edit/output"
1564
+ event_manager.write_stream(content=content.to_dict(),
1565
+ metadata=metadata.to_dict())
1566
+ elif isinstance(agent_event, ToolCallEvent):
1567
+ tool_name = type(agent_event.tool).__name__
1568
+ metadata.path = "/agent/edit/tool/call"
1569
+ content = EventContentCreator.create_result(
1570
+ content={
1571
+ "tool_name": tool_name,
1572
+ **agent_event.tool.model_dump()
1573
+ },
1574
+ metadata={}
1575
+ )
1576
+ event_manager.write_result(
1577
+ content=content.to_dict(), metadata=metadata.to_dict())
1578
+ elif isinstance(agent_event, ToolResultEvent):
1579
+ metadata.path = "/agent/edit/tool/result"
1580
+ content = EventContentCreator.create_result(
1581
+ content={
1582
+ "tool_name": agent_event.tool_name,
1583
+ **agent_event.result.model_dump()
1584
+ },
1585
+ metadata={}
1586
+ )
1587
+ event_manager.write_result(
1588
+ content=content.to_dict(), metadata=metadata.to_dict())
1589
+ elif isinstance(agent_event, PlanModeRespondEvent):
1590
+ metadata.path = "/agent/edit/plan_mode_respond"
1591
+ content = EventContentCreator.create_markdown_result(
1592
+ content=agent_event.completion.response,
1593
+ metadata={}
1594
+ )
1595
+ event_manager.write_result(
1596
+ content=content.to_dict(), metadata=metadata.to_dict())
1597
+
1598
+ elif isinstance(agent_event, TokenUsageEvent):
1599
+ last_meta: SingleOutputMeta = agent_event.usage
1600
+ # Get model info for pricing
1601
+ from autocoder.utils import llms as llm_utils
1602
+ model_name = ",".join(llm_utils.get_llm_names(self.llm))
1603
+ model_info = llm_utils.get_model_info(
1604
+ model_name, self.args.product_mode) or {}
1605
+ input_price = model_info.get(
1606
+ "input_price", 0.0) if model_info else 0.0
1607
+ output_price = model_info.get(
1608
+ "output_price", 0.0) if model_info else 0.0
1609
+
1610
+ # Calculate costs
1611
+ input_cost = (last_meta.input_tokens_count *
1612
+ input_price) / 1000000 # Convert to millions
1613
+ # Convert to millions
1614
+ output_cost = (
1615
+ last_meta.generated_tokens_count * output_price) / 1000000
1616
+
1617
+ # 添加日志记录
1618
+ logger.info(f"Token Usage Details: Model={model_name}, Input Tokens={last_meta.input_tokens_count}, Output Tokens={last_meta.generated_tokens_count}, Input Cost=${input_cost:.6f}, Output Cost=${output_cost:.6f}")
1619
+
1620
+ # 累计TokenUsageEvent数据而不是立即发送
1621
+ accumulated_token_usage["model_name"] = model_name
1622
+ if accumulated_token_usage["first_token_time"] == 0.0:
1623
+ accumulated_token_usage["first_token_time"] = last_meta.first_token_time
1624
+ accumulated_token_usage["input_tokens"] += last_meta.input_tokens_count
1625
+ accumulated_token_usage["output_tokens"] += last_meta.generated_tokens_count
1626
+ accumulated_token_usage["input_cost"] += input_cost
1627
+ accumulated_token_usage["output_cost"] += output_cost
1628
+
1629
+ elif isinstance(agent_event, CompletionEvent):
1630
+ # 在这里完成实际合并
1631
+ try:
1632
+ self.apply_changes()
1633
+ except Exception as e:
1634
+ logger.exception(
1635
+ f"Error merging shadow changes to project: {e}")
1636
+
1637
+ # 发送累计的TokenUsageEvent数据
1638
+ get_event_manager(self.args.event_file).write_result(
1639
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
1640
+ model_name=accumulated_token_usage["model_name"],
1641
+ elapsed_time=0.0,
1642
+ first_token_time=accumulated_token_usage["first_token_time"],
1643
+ input_tokens=accumulated_token_usage["input_tokens"],
1644
+ output_tokens=accumulated_token_usage["output_tokens"],
1645
+ input_cost=accumulated_token_usage["input_cost"],
1646
+ output_cost=accumulated_token_usage["output_cost"]
1647
+ ).to_dict()), metadata=metadata.to_dict())
1648
+
1649
+ metadata.path = "/agent/edit/completion"
1650
+ content = EventContentCreator.create_completion(
1651
+ success_code="AGENT_COMPLETE",
1652
+ success_message="Agent attempted task completion.",
1653
+ result={
1654
+ "response": agent_event.completion.result
1655
+ }
1656
+ )
1657
+ event_manager.write_completion(
1658
+ content=content.to_dict(), metadata=metadata.to_dict())
1659
+ elif isinstance(agent_event, ErrorEvent):
1660
+ # 发送累计的TokenUsageEvent数据(在错误情况下也需要发送)
1661
+ if accumulated_token_usage["input_tokens"] > 0:
1662
+ get_event_manager(self.args.event_file).write_result(
1663
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
1664
+ model_name=accumulated_token_usage["model_name"],
1665
+ elapsed_time=0.0,
1666
+ first_token_time=accumulated_token_usage["first_token_time"],
1667
+ input_tokens=accumulated_token_usage["input_tokens"],
1668
+ output_tokens=accumulated_token_usage["output_tokens"],
1669
+ input_cost=accumulated_token_usage["input_cost"],
1670
+ output_cost=accumulated_token_usage["output_cost"]
1671
+ ).to_dict()), metadata=metadata.to_dict())
1672
+
1673
+ metadata.path = "/agent/edit/error"
1674
+ content = EventContentCreator.create_error(
1675
+ error_code="AGENT_ERROR",
1676
+ error_message=agent_event.message,
1677
+ details={"agent_event_type": "ErrorEvent"}
1678
+ )
1679
+ event_manager.write_error(
1680
+ content=content.to_dict(), metadata=metadata.to_dict())
1681
+ else:
1682
+ metadata.path = "/agent/edit/error"
1683
+ logger.warning(
1684
+ f"Unhandled agent event type: {type(agent_event)}")
1685
+ content = EventContentCreator.create_error(
1686
+ error_code="AGENT_ERROR",
1687
+ error_message=f"Unhandled agent event type: {type(agent_event)}",
1688
+ details={"agent_event_type": type(
1689
+ agent_event).__name__}
1690
+ )
1691
+ event_manager.write_error(
1692
+ content=content.to_dict(), metadata=metadata.to_dict())
1693
+
1694
+ except Exception as e:
1695
+ logger.exception(
1696
+ "An unexpected error occurred during agent execution:")
1697
+ metadata = EventMetadata(
1698
+ action_file=self.args.file,
1699
+ is_streaming=False,
1700
+ stream_out_type="/agent/edit/error")
1701
+
1702
+ # 发送累计的TokenUsageEvent数据(在错误情况下也需要发送)
1703
+ if accumulated_token_usage["input_tokens"] > 0:
1704
+ get_event_manager(self.args.event_file).write_result(
1705
+ EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
1706
+ model_name=accumulated_token_usage["model_name"],
1707
+ elapsed_time=0.0,
1708
+ first_token_time=accumulated_token_usage["first_token_time"],
1709
+ input_tokens=accumulated_token_usage["input_tokens"],
1710
+ output_tokens=accumulated_token_usage["output_tokens"],
1711
+ input_cost=accumulated_token_usage["input_cost"],
1712
+ output_cost=accumulated_token_usage["output_cost"]
1713
+ ).to_dict()), metadata=metadata.to_dict())
1714
+
1715
+ error_content = EventContentCreator.create_error(
1716
+ error_code="AGENT_FATAL_ERROR",
1717
+ error_message=f"An unexpected error occurred: {str(e)}",
1718
+ details={"exception_type": type(e).__name__}
1719
+ )
1720
+ event_manager.write_error(
1721
+ content=error_content.to_dict(), metadata=metadata.to_dict())
1722
+ # Re-raise the exception if needed, or handle appropriately
1723
+ raise e