auto-coder 0.1.398__py3-none-any.whl → 0.1.400__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 (86) hide show
  1. auto_coder-0.1.400.dist-info/METADATA +396 -0
  2. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/RECORD +82 -29
  3. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/WHEEL +1 -1
  4. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/entry_points.txt +2 -0
  5. autocoder/agent/base_agentic/base_agent.py +2 -2
  6. autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
  7. autocoder/agent/entry_command_agent/__init__.py +29 -0
  8. autocoder/agent/entry_command_agent/auto_tool.py +61 -0
  9. autocoder/agent/entry_command_agent/chat.py +475 -0
  10. autocoder/agent/entry_command_agent/designer.py +53 -0
  11. autocoder/agent/entry_command_agent/generate_command.py +50 -0
  12. autocoder/agent/entry_command_agent/project_reader.py +58 -0
  13. autocoder/agent/entry_command_agent/voice2text.py +71 -0
  14. autocoder/auto_coder.py +23 -548
  15. autocoder/auto_coder_runner.py +511 -8
  16. autocoder/chat/rules_command.py +1 -1
  17. autocoder/chat_auto_coder.py +6 -1
  18. autocoder/common/ac_style_command_parser/__init__.py +15 -0
  19. autocoder/common/ac_style_command_parser/example.py +7 -0
  20. autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +28 -45
  21. autocoder/common/ac_style_command_parser/test_parser.py +516 -0
  22. autocoder/common/auto_coder_lang.py +78 -0
  23. autocoder/common/command_completer_v2.py +1 -1
  24. autocoder/common/command_file_manager/examples.py +22 -8
  25. autocoder/common/command_file_manager/manager.py +37 -6
  26. autocoder/common/conversations/get_conversation_manager.py +143 -0
  27. autocoder/common/conversations/manager.py +122 -11
  28. autocoder/common/conversations/storage/index_manager.py +89 -0
  29. autocoder/common/pull_requests/__init__.py +256 -0
  30. autocoder/common/pull_requests/base_provider.py +191 -0
  31. autocoder/common/pull_requests/config.py +66 -0
  32. autocoder/common/pull_requests/example.py +1 -0
  33. autocoder/common/pull_requests/exceptions.py +46 -0
  34. autocoder/common/pull_requests/manager.py +201 -0
  35. autocoder/common/pull_requests/models.py +164 -0
  36. autocoder/common/pull_requests/providers/__init__.py +23 -0
  37. autocoder/common/pull_requests/providers/gitcode_provider.py +19 -0
  38. autocoder/common/pull_requests/providers/gitee_provider.py +20 -0
  39. autocoder/common/pull_requests/providers/github_provider.py +214 -0
  40. autocoder/common/pull_requests/providers/gitlab_provider.py +29 -0
  41. autocoder/common/pull_requests/test_module.py +1 -0
  42. autocoder/common/pull_requests/utils.py +344 -0
  43. autocoder/common/tokens/__init__.py +62 -0
  44. autocoder/common/tokens/counter.py +211 -0
  45. autocoder/common/tokens/file_detector.py +105 -0
  46. autocoder/common/tokens/filters.py +111 -0
  47. autocoder/common/tokens/models.py +28 -0
  48. autocoder/common/v2/agent/agentic_edit.py +312 -85
  49. autocoder/common/v2/agent/agentic_edit_types.py +11 -0
  50. autocoder/common/v2/code_auto_generate_editblock.py +10 -2
  51. autocoder/dispacher/__init__.py +10 -0
  52. autocoder/rags.py +0 -27
  53. autocoder/run_context.py +1 -0
  54. autocoder/sdk/__init__.py +188 -0
  55. autocoder/sdk/cli/__init__.py +15 -0
  56. autocoder/sdk/cli/__main__.py +26 -0
  57. autocoder/sdk/cli/completion_wrapper.py +38 -0
  58. autocoder/sdk/cli/formatters.py +211 -0
  59. autocoder/sdk/cli/handlers.py +175 -0
  60. autocoder/sdk/cli/install_completion.py +301 -0
  61. autocoder/sdk/cli/main.py +286 -0
  62. autocoder/sdk/cli/options.py +73 -0
  63. autocoder/sdk/constants.py +102 -0
  64. autocoder/sdk/core/__init__.py +20 -0
  65. autocoder/sdk/core/auto_coder_core.py +880 -0
  66. autocoder/sdk/core/bridge.py +500 -0
  67. autocoder/sdk/example.py +0 -0
  68. autocoder/sdk/exceptions.py +72 -0
  69. autocoder/sdk/models/__init__.py +19 -0
  70. autocoder/sdk/models/messages.py +209 -0
  71. autocoder/sdk/models/options.py +196 -0
  72. autocoder/sdk/models/responses.py +311 -0
  73. autocoder/sdk/session/__init__.py +32 -0
  74. autocoder/sdk/session/session.py +106 -0
  75. autocoder/sdk/session/session_manager.py +56 -0
  76. autocoder/sdk/utils/__init__.py +24 -0
  77. autocoder/sdk/utils/formatters.py +216 -0
  78. autocoder/sdk/utils/io_utils.py +302 -0
  79. autocoder/sdk/utils/validators.py +287 -0
  80. autocoder/version.py +2 -1
  81. auto_coder-0.1.398.dist-info/METADATA +0 -111
  82. autocoder/common/conversations/compatibility.py +0 -303
  83. autocoder/common/conversations/conversation_manager.py +0 -502
  84. autocoder/common/conversations/example.py +0 -152
  85. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info/licenses}/LICENSE +0 -0
  86. {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/top_level.txt +0 -0
@@ -79,10 +79,12 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
79
79
  LLMOutputEvent, LLMThinkingEvent, ToolCallEvent,
80
80
  ToolResultEvent, CompletionEvent, PlanModeRespondEvent, ErrorEvent, TokenUsageEvent,
81
81
  WindowLengthChangeEvent,
82
+ ConversationIdEvent,
82
83
  # Import specific tool types for display mapping
83
84
  ReadFileTool, WriteToFileTool, ReplaceInFileTool, ExecuteCommandTool,
84
85
  ListFilesTool, SearchFilesTool, ListCodeDefinitionNamesTool,
85
- AskFollowupQuestionTool, UseMcpTool, AttemptCompletionTool
86
+ AskFollowupQuestionTool, UseMcpTool, AttemptCompletionTool,
87
+ AgenticEditConversationConfig
86
88
  )
87
89
  from autocoder.common.rag_manager import RAGManager
88
90
  from autocoder.rag.token_counter import count_tokens
@@ -103,6 +105,14 @@ TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
103
105
  UseMcpTool: UseMcpToolResolver,
104
106
  UseRAGTool: UseRAGToolResolver
105
107
  }
108
+ from autocoder.common.conversations.get_conversation_manager import (
109
+ get_conversation_manager,
110
+ get_conversation_manager_config,
111
+ reset_conversation_manager
112
+ )
113
+ from autocoder.common.conversations import ConversationManagerConfig
114
+ from autocoder.common.pull_requests import create_pull_request, detect_platform_from_repo
115
+ from autocoder.common.auto_coder_lang import get_message, get_message_with_format
106
116
 
107
117
 
108
118
  # --- Tool Display Customization is now handled by agentic_tool_display.py ---
@@ -117,7 +127,8 @@ class AgenticEdit:
117
127
  args: AutoCoderArgs,
118
128
  memory_config: MemoryConfig,
119
129
  command_config: Optional[CommandConfig] = None,
120
- conversation_name:Optional[str] = "current"
130
+ conversation_name:Optional[str] = "current",
131
+ conversation_config:Optional[AgenticEditConversationConfig] = None
121
132
  ):
122
133
  self.llm = llm
123
134
  self.context_prune_llm = get_single_llm(args.context_prune_model or args.model,product_mode=args.product_mode)
@@ -127,7 +138,7 @@ class AgenticEdit:
127
138
  self.files = files
128
139
  # Removed self.max_iterations
129
140
  # Note: This might need updating based on the new flow
130
- self.conversation_history = conversation_history
141
+ self.conversation_history = conversation_history
131
142
 
132
143
  self.current_conversations = []
133
144
  self.memory_config = memory_config
@@ -183,6 +194,19 @@ class AgenticEdit:
183
194
  # 格式: { file_path: FileChangeEntry(...) }
184
195
  self.file_changes: Dict[str, FileChangeEntry] = {}
185
196
 
197
+ # 对话管理器
198
+ self.conversation_config =conversation_config
199
+ self.conversation_manager = get_conversation_manager()
200
+
201
+ if self.conversation_config.action == "new":
202
+ conversation_id = self.conversation_manager.create_conversation(name=self.conversation_config.query or "New Conversation",
203
+ description=self.conversation_config.query or "New Conversation")
204
+ self.conversation_manager.set_current_conversation(conversation_id)
205
+
206
+ if self.conversation_config.action == "resume" and self.conversation_config.conversation_id:
207
+ self.conversation_manager.set_current_conversation(self.conversation_config.conversation_id)
208
+
209
+
186
210
  @byzerllm.prompt()
187
211
  def generate_library_docs_prompt(self, libraries_with_paths: List[Dict[str, str]], docs_content: str) -> Dict[str, Any]:
188
212
  """
@@ -419,7 +443,7 @@ class AgenticEdit:
419
443
  <list_code_definition_names>
420
444
  <path>Directory path here</path>
421
445
  </list_code_definition_names>
422
-
446
+
423
447
  ## ask_followup_question
424
448
  Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.
425
449
  Parameters:
@@ -431,7 +455,7 @@ class AgenticEdit:
431
455
  <options>
432
456
  Array of options here (optional), e.g. ["Option 1", "Option 2", "Option 3"]
433
457
  </options>
434
- </ask_followup_question>
458
+ </ask_followup_question>
435
459
 
436
460
  ## attempt_completion
437
461
  Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. Optionally you may provide a CLI command to showcase the result of your work. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
@@ -1028,7 +1052,7 @@ class AgenticEdit:
1028
1052
  {% endfor %}
1029
1053
  </user_rule_or_document_files>
1030
1054
 
1031
- 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.
1055
+ You should decide based on the user's requirements whether to use the read_file tool to get the relevant files listed in index.md.
1032
1056
  {% endif %}
1033
1057
 
1034
1058
 
@@ -1096,6 +1120,7 @@ class AgenticEdit:
1096
1120
  "enable_active_context_in_generate": self.args.enable_active_context_in_generate,
1097
1121
  "extra_docs": extra_docs,
1098
1122
  "file_paths_str": file_paths_str,
1123
+ "agentic_auto_approve": self.args.enable_agentic_auto_approve,
1099
1124
  }
1100
1125
 
1101
1126
  # Removed _execute_command_result and execute_auto_command methods
@@ -1144,7 +1169,7 @@ class AgenticEdit:
1144
1169
  """
1145
1170
  Analyzes the user request, interacts with the LLM, parses responses,
1146
1171
  executes tools, and yields structured events for visualization until completion or error.
1147
- """
1172
+ """
1148
1173
  logger.info(f"Starting analyze method with user input: {request.user_input[:50]}...")
1149
1174
  system_prompt = self._analyze.prompt(request)
1150
1175
  logger.info(f"Generated system prompt with length: {len(system_prompt)}")
@@ -1154,7 +1179,7 @@ class AgenticEdit:
1154
1179
  conversations = [
1155
1180
  {"role": "system", "content": system_prompt},
1156
1181
  ]
1157
-
1182
+
1158
1183
  # Add third-party library documentation information
1159
1184
  try:
1160
1185
  package_manager = LLMFriendlyPackageManager(
@@ -1199,12 +1224,36 @@ class AgenticEdit:
1199
1224
  })
1200
1225
 
1201
1226
  except Exception as e:
1202
- logger.warning(f"Failed to load library documentation: {str(e)}")
1203
-
1227
+ logger.warning(f"Failed to load library documentation: {str(e)}")
1228
+
1229
+ if self.conversation_config.action == "resume":
1230
+ current_conversation = self.conversation_manager.get_current_conversation()
1231
+ # 如果继续的是当前的对话,将其消息加入到 conversations 中
1232
+ if current_conversation and current_conversation.get('messages'):
1233
+ for message in current_conversation['messages']:
1234
+ # 确保消息格式正确(包含 role 和 content 字段)
1235
+ if isinstance(message, dict) and 'role' in message and 'content' in message:
1236
+ conversations.append({
1237
+ "role": message['role'],
1238
+ "content": message['content']
1239
+ })
1240
+ logger.info(f"Resumed conversation with {len(current_conversation['messages'])} existing messages")
1241
+
1242
+ if self.conversation_manager.get_current_conversation_id() is None:
1243
+ conv_id = self.conversation_manager.create_conversation(name=self.conversation_config.query,description=self.conversation_config.query)
1244
+ self.conversation_manager.set_current_conversation(conv_id)
1245
+
1246
+ self.conversation_manager.set_current_conversation(self.conversation_manager.get_current_conversation_id())
1247
+ yield ConversationIdEvent(conversation_id=self.conversation_manager.get_current_conversation_id())
1248
+
1204
1249
  conversations.append({
1205
1250
  "role": "user", "content": request.user_input
1206
- })
1251
+ })
1207
1252
 
1253
+ self.conversation_manager.append_message_to_current(
1254
+ role="user",
1255
+ content=request.user_input,
1256
+ metadata={})
1208
1257
 
1209
1258
  self.current_conversations = conversations
1210
1259
 
@@ -1292,7 +1341,12 @@ class AgenticEdit:
1292
1341
  conversations.append({
1293
1342
  "role": "assistant",
1294
1343
  "content": assistant_buffer + tool_xml
1295
- })
1344
+ })
1345
+ self.conversation_manager.append_message_to_current(
1346
+ role="assistant",
1347
+ content=assistant_buffer + tool_xml,
1348
+ metadata={})
1349
+
1296
1350
  assistant_buffer = "" # Reset buffer after tool call
1297
1351
 
1298
1352
  # 计算当前对话的总 token 数量并触发事件
@@ -1311,7 +1365,7 @@ class AgenticEdit:
1311
1365
  completion_event = CompletionEvent(completion=tool_obj, completion_xml=tool_xml)
1312
1366
  logger.info(
1313
1367
  "AgenticEdit analyze loop finished due to AttemptCompletion.")
1314
- save_formatted_log(self.args.source_dir, json.dumps(conversations, ensure_ascii=False), "agentic_conversation")
1368
+ save_formatted_log(self.args.source_dir, json.dumps(conversations, ensure_ascii=False), "agentic_conversation")
1315
1369
  mark_event_should_finish = True
1316
1370
  should_yield_completion_event = True
1317
1371
  continue
@@ -1387,6 +1441,10 @@ class AgenticEdit:
1387
1441
  "role": "user", # Simulating the user providing the tool result
1388
1442
  "content": error_xml
1389
1443
  })
1444
+ self.conversation_manager.append_message_to_current(
1445
+ role="user",
1446
+ content=error_xml,
1447
+ metadata={})
1390
1448
 
1391
1449
  # 计算当前对话的总 token 数量并触发事件
1392
1450
  current_conversation_str = json.dumps(conversations, ensure_ascii=False)
@@ -1425,6 +1483,9 @@ class AgenticEdit:
1425
1483
  logger.info("Adding new assistant message")
1426
1484
  conversations.append(
1427
1485
  {"role": "assistant", "content": assistant_buffer})
1486
+ self.conversation_manager.append_message_to_current(
1487
+ role="assistant", content=assistant_buffer,metadata={})
1488
+
1428
1489
  elif last_message["role"] == "assistant":
1429
1490
  logger.info("Appending to existing assistant message")
1430
1491
  last_message["content"] += assistant_buffer
@@ -1441,6 +1502,11 @@ class AgenticEdit:
1441
1502
  "role": "user",
1442
1503
  "content": "NOTE: You must use an appropriate tool (such as read_file, write_to_file, execute_command, etc.) or explicitly complete the task (using attempt_completion). Do not provide text responses without taking concrete actions. Please select a suitable tool to continue based on the user's task."
1443
1504
  })
1505
+
1506
+ self.conversation_manager.append_message_to_current(
1507
+ role="user",
1508
+ content="NOTE: You must use an appropriate tool (such as read_file, write_to_file, execute_command, etc.) or explicitly complete the task (using attempt_completion). Do not provide text responses without taking concrete actions. Please select a suitable tool to continue based on the user's task.",
1509
+ metadata={})
1444
1510
 
1445
1511
  # 计算当前对话的总 token 数量并触发事件
1446
1512
  current_conversation_str = json.dumps(conversations, ensure_ascii=False)
@@ -1580,9 +1646,9 @@ class AgenticEdit:
1580
1646
  else:
1581
1647
  yield ToolCallEvent(tool=tool_obj, tool_xml=reconstructed_xml)
1582
1648
  else:
1583
- yield ErrorEvent(message=f"Failed to parse tool: <{current_tool_tag}>")
1649
+ # yield ErrorEvent(message=f"Failed to parse tool: <{current_tool_tag}>")
1584
1650
  # Optionally yield the raw XML as plain text?
1585
- # yield LLMOutputEvent(text=tool_xml)
1651
+ yield LLMOutputEvent(text=f"Failed to parse tool: <{current_tool_tag}> {tool_xml}")
1586
1652
 
1587
1653
  buffer = buffer[tool_block_end_index:]
1588
1654
  in_tool_block = False
@@ -1856,85 +1922,219 @@ class AgenticEdit:
1856
1922
  def apply_changes(self):
1857
1923
  """
1858
1924
  Apply all tracked file changes to the original project directory.
1925
+ """
1926
+ if not self.args.skip_commit:
1927
+ try:
1928
+ file_name = os.path.basename(self.args.file)
1929
+ commit_result = git_utils.commit_changes(
1930
+ self.args.source_dir,
1931
+ f"{self.args.query}\nauto_coder_{file_name}",
1932
+ )
1933
+
1934
+ get_event_manager(self.args.event_file).write_result(
1935
+ EventContentCreator.create_result(
1936
+ content={
1937
+ "have_commit":commit_result.success,
1938
+ "commit_hash":commit_result.commit_hash,
1939
+ "diff_file_num":len(commit_result.changed_files),
1940
+ "event_file":self.args.event_file
1941
+ }), metadata=EventMetadata(
1942
+ action_file=self.args.file,
1943
+ is_streaming=False,
1944
+ path="/agent/edit/apply_changes",
1945
+ stream_out_type="/agent/edit").to_dict())
1946
+
1947
+ action_yml_file_manager = ActionYmlFileManager(
1948
+ self.args.source_dir)
1949
+ action_file_name = os.path.basename(self.args.file)
1950
+ add_updated_urls = []
1951
+ commit_result.changed_files
1952
+ for file in commit_result.changed_files:
1953
+ add_updated_urls.append(
1954
+ os.path.join(self.args.source_dir, file))
1955
+
1956
+ self.args.add_updated_urls = add_updated_urls
1957
+ update_yaml_success = action_yml_file_manager.update_yaml_field(
1958
+ action_file_name, "add_updated_urls", add_updated_urls)
1959
+ if not update_yaml_success:
1960
+ self.printer.print_in_terminal(
1961
+ "yaml_save_error", style="red", yaml_file=action_file_name)
1962
+
1963
+ if self.args.enable_active_context:
1964
+ active_context_manager = ActiveContextManager(
1965
+ self.llm, self.args.source_dir)
1966
+ task_id = active_context_manager.process_changes(
1967
+ self.args)
1968
+ self.printer.print_in_terminal("active_context_background_task",
1969
+ style="blue",
1970
+ task_id=task_id)
1971
+ git_utils.print_commit_info(commit_result=commit_result)
1972
+
1973
+ # 检查是否需要创建 Pull Request
1974
+ if self.conversation_config and self.conversation_config.pull_request:
1975
+ self._create_pull_request(commit_result)
1976
+
1977
+ except Exception as e:
1978
+ self.printer.print_str_in_terminal(
1979
+ str(e),
1980
+ style="red"
1981
+ )
1982
+
1983
+ def _create_pull_request(self, commit_result):
1859
1984
  """
1860
- diff_file_num = 0
1861
- if self.shadow_manager:
1862
- for (file_path, change) in self.get_all_file_changes().items():
1863
- # Ensure the directory exists before writing the file
1864
- dir_path = os.path.dirname(file_path)
1865
- if dir_path: # Ensure dir_path is not empty (for files in root)
1866
- os.makedirs(dir_path, exist_ok=True)
1867
-
1868
- with open(file_path, 'w', encoding='utf-8') as f:
1869
- f.write(change.content)
1870
- diff_file_num = len(self.get_all_file_changes())
1871
- else:
1872
- changes = self.checkpoint_manager.get_changes_by_group(self.args.event_file)
1873
- diff_file_num = len(changes)
1985
+ 创建 Pull Request(如果配置启用)
1986
+
1987
+ Args:
1988
+ commit_result: Git commit 结果对象
1989
+ """
1990
+ try:
1991
+ # 获取当前分支名
1992
+ current_branch = git_utils.get_current_branch(self.args.source_dir)
1993
+ if not current_branch:
1994
+ logger.warning(get_message("/agent/edit/pull_request/branch_name_failed"))
1995
+ return
1874
1996
 
1875
- if diff_file_num > 0:
1876
- if not self.args.skip_commit:
1877
- try:
1878
- file_name = os.path.basename(self.args.file)
1879
- commit_result = git_utils.commit_changes(
1880
- self.args.source_dir,
1881
- f"{self.args.query}\nauto_coder_{file_name}",
1882
- )
1883
-
1884
- get_event_manager(self.args.event_file).write_result(
1885
- EventContentCreator.create_result(
1886
- content={
1887
- "have_commit":commit_result.success,
1888
- "commit_hash":commit_result.commit_hash,
1889
- "diff_file_num":diff_file_num,
1890
- "event_file":self.args.event_file
1891
- }), metadata=EventMetadata(
1892
- action_file=self.args.file,
1893
- is_streaming=False,
1894
- path="/agent/edit/apply_changes",
1895
- stream_out_type="/agent/edit").to_dict())
1896
-
1897
- action_yml_file_manager = ActionYmlFileManager(
1898
- self.args.source_dir)
1899
- action_file_name = os.path.basename(self.args.file)
1900
- add_updated_urls = []
1901
- commit_result.changed_files
1902
- for file in commit_result.changed_files:
1903
- add_updated_urls.append(
1904
- os.path.join(self.args.source_dir, file))
1905
-
1906
- self.args.add_updated_urls = add_updated_urls
1907
- update_yaml_success = action_yml_file_manager.update_yaml_field(
1908
- action_file_name, "add_updated_urls", add_updated_urls)
1909
- if not update_yaml_success:
1910
- self.printer.print_in_terminal(
1911
- "yaml_save_error", style="red", yaml_file=action_file_name)
1912
-
1913
- if self.args.enable_active_context:
1914
- active_context_manager = ActiveContextManager(
1915
- self.llm, self.args.source_dir)
1916
- task_id = active_context_manager.process_changes(
1917
- self.args)
1918
- self.printer.print_in_terminal("active_context_background_task",
1919
- style="blue",
1920
- task_id=task_id)
1921
- git_utils.print_commit_info(commit_result=commit_result)
1922
- except Exception as e:
1923
- self.printer.print_str_in_terminal(
1924
- self.git_require_msg(
1925
- source_dir=self.args.source_dir, error=str(e)),
1926
- style="red"
1927
- )
1928
- else:
1929
- self.printer.print_in_terminal("no_changes_made")
1997
+ # 准备 PR 标题和描述
1998
+ query = self.args.query or get_message("/agent/edit/pull_request/default_query")
1999
+ pr_title = get_message_with_format("/agent/edit/pull_request/title", query=query[0:40])
2000
+
2001
+ # 构建 PR 描述
2002
+ file_list = ""
2003
+ if commit_result.changed_files:
2004
+ for file_path in commit_result.changed_files:
2005
+ file_list += f"- `{file_path}`\n"
2006
+
2007
+ pr_description = get_message_with_format(
2008
+ "/agent/edit/pull_request/description",
2009
+ query=query,
2010
+ file_count=len(commit_result.changed_files or []),
2011
+ commit_hash=commit_result.commit_hash,
2012
+ file_list=file_list.strip(),
2013
+ source_branch=current_branch,
2014
+ target_branch="main",
2015
+ timestamp=time.strftime('%Y-%m-%d %H:%M:%S')
2016
+ )
2017
+
2018
+ # 创建 Pull Request
2019
+ logger.info(get_message_with_format("/agent/edit/pull_request/creating", title=pr_title))
2020
+
2021
+ result = create_pull_request(
2022
+ repo_path=self.args.source_dir,
2023
+ title=pr_title,
2024
+ description=pr_description,
2025
+ )
2026
+
2027
+ if result.success:
2028
+ logger.info(get_message("/agent/edit/pull_request/success"))
2029
+ logger.info(f"PR URL: {result.pr_url}")
2030
+ logger.info(f"PR 编号: {result.pr_number}")
2031
+
2032
+ # 打印成功信息到终端
2033
+ self.printer.print_str_in_terminal(
2034
+ get_message("/agent/edit/pull_request/success"),
2035
+ style="green"
2036
+ )
2037
+ self.printer.print_str_in_terminal(f"PR URL: {result.pr_url}")
2038
+ self.printer.print_str_in_terminal(f"PR 编号: {result.pr_number}")
2039
+
2040
+ # 写入事件日志
2041
+ get_event_manager(self.args.event_file).write_result(
2042
+ EventContentCreator.create_result(
2043
+ content={
2044
+ "success": True,
2045
+ "pr_url": result.pr_url,
2046
+ "pr_number": result.pr_number,
2047
+ "source_branch": current_branch,
2048
+ "target_branch": "main",
2049
+ "platform": result.platform.value if result.platform else "unknown"
2050
+ }),
2051
+ metadata=EventMetadata(
2052
+ action_file=self.args.file,
2053
+ is_streaming=False,
2054
+ path="/agent/edit/pull_request_created",
2055
+ stream_out_type="/agent/edit"
2056
+ ).to_dict()
2057
+ )
2058
+
2059
+ else:
2060
+ error_msg = get_message_with_format("/agent/edit/pull_request/failed", error=result.error_message)
2061
+ logger.error(error_msg)
2062
+
2063
+ # 打印错误信息到终端
2064
+ self.printer.print_str_in_terminal(error_msg, style="red")
2065
+
2066
+ # 写入错误事件日志
2067
+ get_event_manager(self.args.event_file).write_error(
2068
+ EventContentCreator.create_error(
2069
+ error_code="PR_CREATION_FAILED",
2070
+ error_message=result.error_message,
2071
+ details={
2072
+ "source_branch": current_branch,
2073
+ "target_branch": "main"
2074
+ }
2075
+ ).to_dict(),
2076
+ metadata=EventMetadata(
2077
+ action_file=self.args.file,
2078
+ is_streaming=False,
2079
+ path="/agent/edit/pull_request_error",
2080
+ stream_out_type="/agent/edit"
2081
+ ).to_dict()
2082
+ )
2083
+
2084
+ except Exception as e:
2085
+ error_msg = get_message_with_format("/agent/edit/pull_request/exception", error=str(e))
2086
+ logger.exception(error_msg)
2087
+
2088
+ # 打印异常信息到终端
2089
+ self.printer.print_str_in_terminal(error_msg, style="red")
2090
+
2091
+ # 写入异常事件日志
2092
+ get_event_manager(self.args.event_file).write_error(
2093
+ EventContentCreator.create_error(
2094
+ error_code="PR_CREATION_EXCEPTION",
2095
+ error_message=get_message_with_format("/agent/edit/pull_request/exception", error=str(e)),
2096
+ details={"exception_type": type(e).__name__}
2097
+ ).to_dict(),
2098
+ metadata=EventMetadata(
2099
+ action_file=self.args.file,
2100
+ is_streaming=False,
2101
+ path="/agent/edit/pull_request_exception",
2102
+ stream_out_type="/agent/edit"
2103
+ ).to_dict()
2104
+ )
1930
2105
 
1931
2106
  def run_in_terminal(self, request: AgenticEditRequest):
1932
2107
  """
1933
2108
  Runs the agentic edit process based on the request and displays
1934
2109
  the interaction streamingly in the terminal using Rich.
1935
2110
  """
2111
+ import json
1936
2112
  console = Console()
1937
2113
  project_name = os.path.basename(os.path.abspath(self.args.source_dir))
2114
+
2115
+ if self.conversation_config.action == "list":
2116
+ conversations = self.conversation_manager.list_conversations()
2117
+ # 只保留 conversation_id 和 name 字段
2118
+ filtered_conversations = []
2119
+ for conv in conversations:
2120
+ filtered_conv = {
2121
+ "conversation_id": conv.get("conversation_id"),
2122
+ "name": conv.get("name")
2123
+ }
2124
+ filtered_conversations.append(filtered_conv)
2125
+
2126
+ # 格式化 JSON 输出,使用 JSON 格式渲染而不是 Markdown
2127
+ json_str = json.dumps(filtered_conversations, ensure_ascii=False, indent=4)
2128
+ console.print(Panel(json_str,
2129
+ title="🏁 Task Completion", border_style="green", title_align="left"))
2130
+ return
2131
+
2132
+
2133
+ if self.conversation_config.action == "new" and not request.user_input.strip():
2134
+ console.print(Panel(Markdown(f"New conversation created: {self.conversation_manager.get_current_conversation_id()}"),
2135
+ title="🏁 Task Completion", border_style="green", title_align="left"))
2136
+ return
2137
+
1938
2138
  console.rule(f"[bold cyan]Starting Agentic Edit: {project_name}[/]")
1939
2139
  console.print(Panel(
1940
2140
  f"[bold]{get_message('/agent/edit/user_query')}:[/bold]\n{request.user_input}", title=get_message("/agent/edit/objective"), border_style="blue"))
@@ -1952,6 +2152,9 @@ class AgenticEdit:
1952
2152
  self.apply_pre_changes()
1953
2153
  event_stream = self.analyze(request)
1954
2154
  for event in event_stream:
2155
+ if isinstance(event, ConversationIdEvent):
2156
+ console.print(f"[dim]Conversation ID: {event.conversation_id}[/dim]")
2157
+ continue
1955
2158
  if isinstance(event, TokenUsageEvent):
1956
2159
  last_meta: SingleOutputMeta = event.usage
1957
2160
  # Get model info for pricing
@@ -2144,6 +2347,20 @@ class AgenticEdit:
2144
2347
  finally:
2145
2348
  console.rule("[bold cyan]Agentic Edit Finished[/]")
2146
2349
 
2350
+ def run(self, request: AgenticEditRequest):
2351
+ try:
2352
+ event_stream = self.analyze(request)
2353
+ for agent_event in event_stream:
2354
+ if isinstance(agent_event, CompletionEvent):
2355
+ self.apply_changes()
2356
+ yield agent_event
2357
+
2358
+ except Exception as e:
2359
+ logger.exception(
2360
+ "An unexpected error occurred during agent execution: {e}")
2361
+ raise e
2362
+
2363
+
2147
2364
  def run_with_events(self, request: AgenticEditRequest):
2148
2365
  """
2149
2366
  Runs the agentic edit process, converting internal events to the
@@ -2275,6 +2492,16 @@ class AgenticEdit:
2275
2492
 
2276
2493
  # 记录日志
2277
2494
  logger.info(f"当前会话总 tokens: {agent_event.tokens_used}")
2495
+
2496
+ elif isinstance(agent_event, ConversationIdEvent):
2497
+ metadata.path = "/agent/edit/conversation_id"
2498
+ content = EventContentCreator.create_result(
2499
+ content={
2500
+ "conversation_id": agent_event.conversation_id
2501
+ },
2502
+ metadata={}
2503
+ )
2504
+ event_manager.write_result(content=content.to_dict(), metadata=metadata.to_dict())
2278
2505
 
2279
2506
  elif isinstance(agent_event, ErrorEvent):
2280
2507
  metadata.path = "/agent/edit/error"
@@ -2284,7 +2511,7 @@ class AgenticEdit:
2284
2511
  details={"agent_event_type": "ErrorEvent"}
2285
2512
  )
2286
2513
  event_manager.write_error(
2287
- content=content.to_dict(), metadata=metadata.to_dict())
2514
+ content=content.to_dict(), metadata=metadata.to_dict())
2288
2515
  else:
2289
2516
  metadata.path = "/agent/edit/error"
2290
2517
  logger.warning(
@@ -86,6 +86,11 @@ class TokenUsageEvent(BaseModel):
86
86
  """Represents the result of executing a tool."""
87
87
  usage: Any
88
88
 
89
+
90
+ class ConversationIdEvent(BaseModel):
91
+ """Represents the conversation id."""
92
+ conversation_id: str
93
+
89
94
  class PlanModeRespondEvent(BaseModel):
90
95
  """Represents the LLM attempting to complete the task."""
91
96
  completion: SkipValidation[PlanModeRespondTool] # Skip validation
@@ -177,3 +182,9 @@ class CommandConfig(BaseModel):
177
182
  index_import: SkipValidation[Callable]
178
183
  exclude_files: SkipValidation[Callable]
179
184
 
185
+ class AgenticEditConversationConfig(BaseModel):
186
+ conversation_name: Optional[str] = "current"
187
+ conversation_id: Optional[str] = None
188
+ action: Optional[str] = None
189
+ query: Optional[str] = None
190
+ pull_request: bool = False
@@ -52,7 +52,7 @@ class CodeAutoGenerateEditBlock:
52
52
  package_context: str = ""
53
53
  ) -> str:
54
54
  """
55
- 如果你需要生成代码,对于每个需要更改的文件,你需要按 *SEARCH/REPLACE block* 的格式进行生成。
55
+ 如果你需要生成代码,对于每个需要更改的文件,你需要按 *SEARCH/REPLACE block* 的格式进行生成。
56
56
 
57
57
  # *SEARCH/REPLACE block* Rules:
58
58
 
@@ -196,7 +196,15 @@ class CodeAutoGenerateEditBlock:
196
196
  <extra_context>
197
197
  {{ context }}
198
198
  </extra_context>
199
- {%- endif %}
199
+ {%- endif %}
200
+
201
+ ====
202
+
203
+ RULES PROVIDED BY SYSTEM
204
+
205
+ Before outputting the *SEARCH/REPLACE block* format, you must carefully think through the user's question and make a complete design. These thoughts and designs should be placed within the <thinking></thinking> tags.
206
+
207
+ ====
200
208
 
201
209
  {%- if extra_docs %}
202
210
  ====
@@ -8,6 +8,11 @@ from autocoder.dispacher.actions.action import (
8
8
  from autocoder.dispacher.actions.plugins.action_regex_project import ActionRegexProject
9
9
  from typing import Optional
10
10
  import byzerllm
11
+ # from autocoder.common.conversations.get_conversation_manager import (
12
+ # get_conversation_manager,
13
+ # get_conversation_manager_config,
14
+ # reset_conversation_manager
15
+ # )
11
16
 
12
17
 
13
18
  class Dispacher:
@@ -16,6 +21,11 @@ class Dispacher:
16
21
  self.llm = llm
17
22
 
18
23
  def dispach(self):
24
+ # manager = get_conversation_manager()
25
+ # if not manager.get_current_conversation():
26
+ # manager.create_conversation(name="New Conversation", description="New Conversation")
27
+ # manager.set_current_conversation(manager.get_current_conversation_id())
28
+
19
29
  args = self.args
20
30
  actions = [
21
31
  ActionTSProject(args=args, llm=self.llm),