auto-coder 0.1.362__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.
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/METADATA +2 -2
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/RECORD +65 -22
- autocoder/agent/base_agentic/__init__.py +0 -0
- autocoder/agent/base_agentic/agent_hub.py +169 -0
- autocoder/agent/base_agentic/agentic_lang.py +112 -0
- autocoder/agent/base_agentic/agentic_tool_display.py +180 -0
- autocoder/agent/base_agentic/base_agent.py +1582 -0
- autocoder/agent/base_agentic/default_tools.py +683 -0
- autocoder/agent/base_agentic/test_base_agent.py +82 -0
- autocoder/agent/base_agentic/tool_registry.py +425 -0
- autocoder/agent/base_agentic/tools/__init__.py +12 -0
- autocoder/agent/base_agentic/tools/ask_followup_question_tool_resolver.py +72 -0
- autocoder/agent/base_agentic/tools/attempt_completion_tool_resolver.py +37 -0
- autocoder/agent/base_agentic/tools/base_tool_resolver.py +35 -0
- autocoder/agent/base_agentic/tools/example_tool_resolver.py +46 -0
- autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +72 -0
- autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +110 -0
- autocoder/agent/base_agentic/tools/plan_mode_respond_tool_resolver.py +35 -0
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +54 -0
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +156 -0
- autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +134 -0
- autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +96 -0
- autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +79 -0
- autocoder/agent/base_agentic/tools/use_mcp_tool_resolver.py +44 -0
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +58 -0
- autocoder/agent/base_agentic/types.py +189 -0
- autocoder/agent/base_agentic/utils.py +100 -0
- autocoder/auto_coder_runner.py +6 -4
- autocoder/chat/conf_command.py +11 -10
- autocoder/common/__init__.py +2 -0
- autocoder/common/file_checkpoint/__init__.py +21 -0
- autocoder/common/file_checkpoint/backup.py +264 -0
- autocoder/common/file_checkpoint/examples.py +217 -0
- autocoder/common/file_checkpoint/manager.py +404 -0
- autocoder/common/file_checkpoint/models.py +156 -0
- autocoder/common/file_checkpoint/store.py +383 -0
- autocoder/common/file_checkpoint/test_backup.py +242 -0
- autocoder/common/file_checkpoint/test_manager.py +570 -0
- autocoder/common/file_checkpoint/test_models.py +360 -0
- autocoder/common/file_checkpoint/test_store.py +327 -0
- autocoder/common/file_checkpoint/test_utils.py +297 -0
- autocoder/common/file_checkpoint/utils.py +119 -0
- autocoder/common/rulefiles/autocoderrules_utils.py +138 -55
- autocoder/common/save_formatted_log.py +76 -5
- autocoder/common/v2/agent/agentic_edit.py +339 -216
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +100 -5
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +322 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +160 -10
- autocoder/common/v2/agent/agentic_edit_types.py +1 -2
- autocoder/common/v2/agent/agentic_tool_display.py +2 -3
- autocoder/compilers/normal_compiler.py +64 -0
- autocoder/events/event_manager_singleton.py +133 -4
- autocoder/linters/normal_linter.py +373 -0
- autocoder/linters/python_linter.py +4 -2
- autocoder/rag/long_context_rag.py +424 -397
- autocoder/rag/test_doc_filter.py +393 -0
- autocoder/rag/test_long_context_rag.py +473 -0
- autocoder/rag/test_token_limiter.py +342 -0
- autocoder/shadows/shadow_manager.py +1 -3
- autocoder/version.py +1 -1
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.364.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.362.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,
|
|
@@ -72,7 +78,6 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
|
|
|
72
78
|
AskFollowupQuestionTool, UseMcpTool, AttemptCompletionTool
|
|
73
79
|
)
|
|
74
80
|
|
|
75
|
-
|
|
76
81
|
# Map Pydantic Tool Models to their Resolver Classes
|
|
77
82
|
TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
78
83
|
ExecuteCommandTool: ExecuteCommandToolResolver,
|
|
@@ -86,7 +91,7 @@ TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
|
86
91
|
AskFollowupQuestionTool: AskFollowupQuestionToolResolver,
|
|
87
92
|
AttemptCompletionTool: AttemptCompletionToolResolver, # Will stop the loop anyway
|
|
88
93
|
PlanModeRespondTool: PlanModeRespondToolResolver,
|
|
89
|
-
UseMcpTool: UseMcpToolResolver
|
|
94
|
+
UseMcpTool: UseMcpToolResolver
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
|
|
@@ -102,7 +107,7 @@ class AgenticEdit:
|
|
|
102
107
|
args: AutoCoderArgs,
|
|
103
108
|
memory_config: MemoryConfig,
|
|
104
109
|
command_config: Optional[CommandConfig] = None,
|
|
105
|
-
conversation_name:
|
|
110
|
+
conversation_name:Optional[str] = "current"
|
|
106
111
|
):
|
|
107
112
|
self.llm = llm
|
|
108
113
|
self.args = args
|
|
@@ -117,11 +122,22 @@ class AgenticEdit:
|
|
|
117
122
|
self.project_type_analyzer = ProjectTypeAnalyzer(
|
|
118
123
|
args=args, llm=self.llm)
|
|
119
124
|
|
|
120
|
-
self.shadow_manager = ShadowManager(
|
|
121
|
-
|
|
122
|
-
self.
|
|
123
|
-
self.
|
|
124
|
-
|
|
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
|
+
|
|
125
141
|
|
|
126
142
|
self.mcp_server_info = ""
|
|
127
143
|
try:
|
|
@@ -233,7 +249,7 @@ class AgenticEdit:
|
|
|
233
249
|
# Tools
|
|
234
250
|
|
|
235
251
|
## execute_command
|
|
236
|
-
Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory:
|
|
252
|
+
Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: {{current_project}}
|
|
237
253
|
Parameters:
|
|
238
254
|
- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
|
|
239
255
|
- requires_approval: (required) A boolean indicating whether this command requires explicit user approval before execution in case the user has auto-approve mode enabled. Set to 'true' for potentially impactful operations like installing/uninstalling packages, deleting/overwriting files, system configuration changes, network operations, or any commands that could have unintended side effects. Set to 'false' for safe operations like reading files/directories, running development servers, building projects, and other non-destructive operations.
|
|
@@ -255,7 +271,7 @@ class AgenticEdit:
|
|
|
255
271
|
## read_file
|
|
256
272
|
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.
|
|
257
273
|
Parameters:
|
|
258
|
-
- path: (required) The path of the file to read (relative to the current working directory
|
|
274
|
+
- path: (required) The path of the file to read (relative to the current working directory {{ current_project }})
|
|
259
275
|
Usage:
|
|
260
276
|
<read_file>
|
|
261
277
|
<path>File path here</path>
|
|
@@ -264,7 +280,7 @@ class AgenticEdit:
|
|
|
264
280
|
## write_to_file
|
|
265
281
|
Description: Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
|
|
266
282
|
Parameters:
|
|
267
|
-
- path: (required) The path of the file to write to (relative to the current working directory
|
|
283
|
+
- path: (required) The path of the file to write to (relative to the current working directory {{ current_project }})
|
|
268
284
|
- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified.
|
|
269
285
|
Usage:
|
|
270
286
|
<write_to_file>
|
|
@@ -277,15 +293,15 @@ class AgenticEdit:
|
|
|
277
293
|
## replace_in_file
|
|
278
294
|
Description: Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file. This tool should be used when you need to make targeted changes to specific parts of a file.
|
|
279
295
|
Parameters:
|
|
280
|
-
- path: (required) The path of the file to modify (relative to the current working directory
|
|
296
|
+
- path: (required) The path of the file to modify (relative to the current working directory {{ current_project }})
|
|
281
297
|
- diff: (required) One or more SEARCH/REPLACE blocks following this exact format:
|
|
282
|
-
|
|
298
|
+
```
|
|
283
299
|
<<<<<<< SEARCH
|
|
284
300
|
[exact content to find]
|
|
285
301
|
=======
|
|
286
302
|
[new content to replace with]
|
|
287
303
|
>>>>>>> REPLACE
|
|
288
|
-
|
|
304
|
+
```
|
|
289
305
|
Critical rules:
|
|
290
306
|
1. SEARCH content must match the associated file section to find EXACTLY:
|
|
291
307
|
* Match character-for-character including whitespace, indentation, line endings
|
|
@@ -313,7 +329,7 @@ class AgenticEdit:
|
|
|
313
329
|
## search_files
|
|
314
330
|
Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
|
|
315
331
|
Parameters:
|
|
316
|
-
- path: (required) The path of the directory to search in (relative to the current working directory
|
|
332
|
+
- path: (required) The path of the directory to search in (relative to the current working directory {{ current_project }}). This directory will be recursively searched.
|
|
317
333
|
- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
|
|
318
334
|
- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
|
|
319
335
|
Usage:
|
|
@@ -326,7 +342,7 @@ class AgenticEdit:
|
|
|
326
342
|
## list_files
|
|
327
343
|
Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
|
|
328
344
|
Parameters:
|
|
329
|
-
- path: (required) The path of the directory to list contents for (relative to the current working directory
|
|
345
|
+
- path: (required) The path of the directory to list contents for (relative to the current working directory {{ current_project }})
|
|
330
346
|
- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
|
|
331
347
|
Usage:
|
|
332
348
|
<list_files>
|
|
@@ -337,7 +353,7 @@ class AgenticEdit:
|
|
|
337
353
|
## list_code_definition_names
|
|
338
354
|
Description: Request to list definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.
|
|
339
355
|
Parameters:
|
|
340
|
-
- path: (required) The path of the directory (relative to the current working directory
|
|
356
|
+
- path: (required) The path of the directory (relative to the current working directory {{ current_project }}) to list top level source code definitions for.
|
|
341
357
|
Usage:
|
|
342
358
|
<list_code_definition_names>
|
|
343
359
|
<path>Directory path here</path>
|
|
@@ -636,7 +652,7 @@ class AgenticEdit:
|
|
|
636
652
|
- Your current working directory is: {{current_project}}
|
|
637
653
|
- You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '{{ current_project }}', so be sure to pass in the correct 'path' parameter when using tools that require a path.
|
|
638
654
|
- Do not use the ~ character or $HOME to refer to the home directory.
|
|
639
|
-
- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '
|
|
655
|
+
- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '{{ current_project }}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '{{ current_project }}'). For example, if you needed to run \`npm install\` in a project outside of '{{ current_project }}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`.
|
|
640
656
|
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using replace_in_file to make informed changes.
|
|
641
657
|
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when creating files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
|
642
658
|
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
|
@@ -664,12 +680,14 @@ class AgenticEdit:
|
|
|
664
680
|
The following rules are provided by the user, and you must follow them strictly.
|
|
665
681
|
|
|
666
682
|
{% for key, value in extra_docs.items() %}
|
|
667
|
-
<
|
|
683
|
+
<user_rule_file>
|
|
668
684
|
##File: {{ key }}
|
|
669
685
|
{{ value }}
|
|
670
|
-
</
|
|
671
|
-
{% 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.
|
|
672
689
|
{% endif %}
|
|
690
|
+
|
|
673
691
|
|
|
674
692
|
====
|
|
675
693
|
|
|
@@ -695,14 +713,15 @@ class AgenticEdit:
|
|
|
695
713
|
{% if file_paths_str %}
|
|
696
714
|
====
|
|
697
715
|
The following are files that the user is currently focusing on.
|
|
698
|
-
Make sure you always start your
|
|
716
|
+
Make sure you always start your task by using the read_file tool to get the content of the files.
|
|
699
717
|
<files>
|
|
700
718
|
{{file_paths_str}}
|
|
701
719
|
</files>
|
|
702
720
|
{% endif %}
|
|
703
|
-
"""
|
|
704
|
-
|
|
705
|
-
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()
|
|
706
725
|
|
|
707
726
|
env_info = detect_env()
|
|
708
727
|
shell_type = "bash"
|
|
@@ -791,9 +810,7 @@ class AgenticEdit:
|
|
|
791
810
|
|
|
792
811
|
logger.info(
|
|
793
812
|
f"Initial conversation history size: {len(conversations)}")
|
|
794
|
-
|
|
795
|
-
logger.info(f"Conversation history: {json.dumps(conversations, indent=2,ensure_ascii=False)}")
|
|
796
|
-
|
|
813
|
+
|
|
797
814
|
iteration_count = 0
|
|
798
815
|
tool_executed = False
|
|
799
816
|
while True:
|
|
@@ -813,22 +830,23 @@ class AgenticEdit:
|
|
|
813
830
|
|
|
814
831
|
assistant_buffer = ""
|
|
815
832
|
logger.info("Initializing stream chat with LLM")
|
|
833
|
+
|
|
834
|
+
## 实际请求大模型
|
|
816
835
|
llm_response_gen = stream_chat_with_continue(
|
|
817
836
|
llm=self.llm,
|
|
818
837
|
conversations=conversations,
|
|
819
838
|
llm_config={}, # Placeholder for future LLM configs
|
|
820
839
|
args=self.args
|
|
821
840
|
)
|
|
822
|
-
|
|
823
|
-
meta_holder = byzerllm.MetaHolder()
|
|
841
|
+
|
|
824
842
|
logger.info("Starting to parse LLM response stream")
|
|
825
843
|
parsed_events = self.stream_and_parse_llm_response(
|
|
826
|
-
llm_response_gen
|
|
844
|
+
llm_response_gen)
|
|
827
845
|
|
|
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
|
|
@@ -942,8 +962,9 @@ class AgenticEdit:
|
|
|
942
962
|
# logger.error("Stopping analyze loop due to parsing error.")
|
|
943
963
|
# return
|
|
944
964
|
|
|
945
|
-
|
|
946
|
-
|
|
965
|
+
elif isinstance(event, TokenUsageEvent):
|
|
966
|
+
logger.info("Yielding token usage event")
|
|
967
|
+
yield event
|
|
947
968
|
|
|
948
969
|
if not tool_executed:
|
|
949
970
|
# No tool executed in this LLM response cycle
|
|
@@ -971,9 +992,10 @@ class AgenticEdit:
|
|
|
971
992
|
continue
|
|
972
993
|
|
|
973
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")
|
|
974
996
|
|
|
975
997
|
def stream_and_parse_llm_response(
|
|
976
|
-
self, generator: Generator[Tuple[str, Any], None, None]
|
|
998
|
+
self, generator: Generator[Tuple[str, Any], None, None]
|
|
977
999
|
) -> Generator[Union[LLMOutputEvent, LLMThinkingEvent, ToolCallEvent, ErrorEvent], None, None]:
|
|
978
1000
|
"""
|
|
979
1001
|
Streamingly parses the LLM response generator, distinguishing between
|
|
@@ -1050,7 +1072,8 @@ class AgenticEdit:
|
|
|
1050
1072
|
logger.exception(
|
|
1051
1073
|
f"Failed to parse tool XML for <{tool_tag}>: {e}\nXML:\n{tool_xml}")
|
|
1052
1074
|
return None
|
|
1053
|
-
|
|
1075
|
+
|
|
1076
|
+
meta_holder = byzerllm.MetaHolder()
|
|
1054
1077
|
for content_chunk, metadata in generator:
|
|
1055
1078
|
global_cancel.check_and_raise(token=self.args.event_file)
|
|
1056
1079
|
meta_holder.meta = metadata
|
|
@@ -1185,174 +1208,28 @@ class AgenticEdit:
|
|
|
1185
1208
|
elif buffer:
|
|
1186
1209
|
# Yield remaining plain text
|
|
1187
1210
|
yield LLMOutputEvent(text=buffer)
|
|
1188
|
-
|
|
1189
|
-
def run_with_events(self, request: AgenticEditRequest):
|
|
1190
|
-
"""
|
|
1191
|
-
Runs the agentic edit process, converting internal events to the
|
|
1192
|
-
standard event system format and writing them using the event manager.
|
|
1193
|
-
"""
|
|
1194
|
-
event_manager = get_event_manager(self.args.event_file)
|
|
1195
|
-
self.apply_pre_changes()
|
|
1196
|
-
|
|
1197
|
-
try:
|
|
1198
|
-
event_stream = self.analyze(request)
|
|
1199
|
-
for agent_event in event_stream:
|
|
1200
|
-
content = None
|
|
1201
|
-
metadata = EventMetadata(
|
|
1202
|
-
action_file=self.args.file,
|
|
1203
|
-
is_streaming=False,
|
|
1204
|
-
stream_out_type="/agent/edit")
|
|
1205
|
-
|
|
1206
|
-
if isinstance(agent_event, LLMThinkingEvent):
|
|
1207
|
-
content = EventContentCreator.create_stream_thinking(
|
|
1208
|
-
content=agent_event.text)
|
|
1209
|
-
metadata.is_streaming = True
|
|
1210
|
-
metadata.path = "/agent/edit/thinking"
|
|
1211
|
-
event_manager.write_stream(
|
|
1212
|
-
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1213
|
-
elif isinstance(agent_event, LLMOutputEvent):
|
|
1214
|
-
content = EventContentCreator.create_stream_content(
|
|
1215
|
-
content=agent_event.text)
|
|
1216
|
-
metadata.is_streaming = True
|
|
1217
|
-
metadata.path = "/agent/edit/output"
|
|
1218
|
-
event_manager.write_stream(content=content.to_dict(),
|
|
1219
|
-
metadata=metadata.to_dict())
|
|
1220
|
-
elif isinstance(agent_event, ToolCallEvent):
|
|
1221
|
-
tool_name = type(agent_event.tool).__name__
|
|
1222
|
-
metadata.path = "/agent/edit/tool/call"
|
|
1223
|
-
content = EventContentCreator.create_result(
|
|
1224
|
-
content={
|
|
1225
|
-
"tool_name": tool_name,
|
|
1226
|
-
**agent_event.tool.model_dump()
|
|
1227
|
-
},
|
|
1228
|
-
metadata={}
|
|
1229
|
-
)
|
|
1230
|
-
event_manager.write_result(
|
|
1231
|
-
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1232
|
-
elif isinstance(agent_event, ToolResultEvent):
|
|
1233
|
-
metadata.path = "/agent/edit/tool/result"
|
|
1234
|
-
content = EventContentCreator.create_result(
|
|
1235
|
-
content={
|
|
1236
|
-
"tool_name": agent_event.tool_name,
|
|
1237
|
-
**agent_event.result.model_dump()
|
|
1238
|
-
},
|
|
1239
|
-
metadata={}
|
|
1240
|
-
)
|
|
1241
|
-
event_manager.write_result(
|
|
1242
|
-
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1243
|
-
elif isinstance(agent_event, PlanModeRespondEvent):
|
|
1244
|
-
metadata.path = "/agent/edit/plan_mode_respond"
|
|
1245
|
-
content = EventContentCreator.create_markdown_result(
|
|
1246
|
-
content=agent_event.completion.response,
|
|
1247
|
-
metadata={}
|
|
1248
|
-
)
|
|
1249
|
-
event_manager.write_result(
|
|
1250
|
-
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1251
|
-
|
|
1252
|
-
elif isinstance(agent_event, TokenUsageEvent):
|
|
1253
|
-
last_meta: SingleOutputMeta = agent_event.usage
|
|
1254
|
-
# Get model info for pricing
|
|
1255
|
-
from autocoder.utils import llms as llm_utils
|
|
1256
|
-
model_name = ",".join(llm_utils.get_llm_names(self.llm))
|
|
1257
|
-
model_info = llm_utils.get_model_info(
|
|
1258
|
-
model_name, self.args.product_mode) or {}
|
|
1259
|
-
input_price = model_info.get(
|
|
1260
|
-
"input_price", 0.0) if model_info else 0.0
|
|
1261
|
-
output_price = model_info.get(
|
|
1262
|
-
"output_price", 0.0) if model_info else 0.0
|
|
1263
|
-
|
|
1264
|
-
# Calculate costs
|
|
1265
|
-
input_cost = (last_meta.input_tokens_count *
|
|
1266
|
-
input_price) / 1000000 # Convert to millions
|
|
1267
|
-
# Convert to millions
|
|
1268
|
-
output_cost = (
|
|
1269
|
-
last_meta.generated_tokens_count * output_price) / 1000000
|
|
1270
|
-
|
|
1271
|
-
# 添加日志记录
|
|
1272
|
-
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}")
|
|
1273
|
-
|
|
1274
|
-
get_event_manager(self.args.event_file).write_result(
|
|
1275
|
-
EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
|
|
1276
|
-
model_name=model_name,
|
|
1277
|
-
elapsed_time=0.0,
|
|
1278
|
-
first_token_time=last_meta.first_token_time,
|
|
1279
|
-
input_tokens=last_meta.input_tokens_count,
|
|
1280
|
-
output_tokens=last_meta.generated_tokens_count,
|
|
1281
|
-
input_cost=input_cost,
|
|
1282
|
-
output_cost=output_cost
|
|
1283
|
-
).to_dict()), metadata=metadata.to_dict())
|
|
1284
|
-
|
|
1285
|
-
elif isinstance(agent_event, CompletionEvent):
|
|
1286
|
-
# 在这里完成实际合并
|
|
1287
|
-
try:
|
|
1288
|
-
self.apply_changes()
|
|
1289
|
-
except Exception as e:
|
|
1290
|
-
logger.exception(
|
|
1291
|
-
f"Error merging shadow changes to project: {e}")
|
|
1292
|
-
|
|
1293
|
-
metadata.path = "/agent/edit/completion"
|
|
1294
|
-
content = EventContentCreator.create_completion(
|
|
1295
|
-
success_code="AGENT_COMPLETE",
|
|
1296
|
-
success_message="Agent attempted task completion.",
|
|
1297
|
-
result={
|
|
1298
|
-
"response": agent_event.completion.result
|
|
1299
|
-
}
|
|
1300
|
-
)
|
|
1301
|
-
event_manager.write_completion(
|
|
1302
|
-
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1303
|
-
elif isinstance(agent_event, ErrorEvent):
|
|
1304
|
-
metadata.path = "/agent/edit/error"
|
|
1305
|
-
content = EventContentCreator.create_error(
|
|
1306
|
-
error_code="AGENT_ERROR",
|
|
1307
|
-
error_message=agent_event.message,
|
|
1308
|
-
details={"agent_event_type": "ErrorEvent"}
|
|
1309
|
-
)
|
|
1310
|
-
event_manager.write_error(
|
|
1311
|
-
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1312
|
-
else:
|
|
1313
|
-
metadata.path = "/agent/edit/error"
|
|
1314
|
-
logger.warning(
|
|
1315
|
-
f"Unhandled agent event type: {type(agent_event)}")
|
|
1316
|
-
content = EventContentCreator.create_error(
|
|
1317
|
-
error_code="AGENT_ERROR",
|
|
1318
|
-
error_message=f"Unhandled agent event type: {type(agent_event)}",
|
|
1319
|
-
details={"agent_event_type": type(
|
|
1320
|
-
agent_event).__name__}
|
|
1321
|
-
)
|
|
1322
|
-
event_manager.write_error(
|
|
1323
|
-
content=content.to_dict(), metadata=metadata.to_dict())
|
|
1324
|
-
|
|
1325
|
-
except Exception as e:
|
|
1326
|
-
logger.exception(
|
|
1327
|
-
"An unexpected error occurred during agent execution:")
|
|
1328
|
-
metadata = EventMetadata(
|
|
1329
|
-
action_file=self.args.file,
|
|
1330
|
-
is_streaming=False,
|
|
1331
|
-
stream_out_type="/agent/edit/error")
|
|
1332
|
-
error_content = EventContentCreator.create_error(
|
|
1333
|
-
error_code="AGENT_FATAL_ERROR",
|
|
1334
|
-
error_message=f"An unexpected error occurred: {str(e)}",
|
|
1335
|
-
details={"exception_type": type(e).__name__}
|
|
1336
|
-
)
|
|
1337
|
-
event_manager.write_error(
|
|
1338
|
-
content=error_content.to_dict(), metadata=metadata.to_dict())
|
|
1339
|
-
# Re-raise the exception if needed, or handle appropriately
|
|
1340
|
-
raise e
|
|
1211
|
+
|
|
1341
1212
|
|
|
1342
1213
|
def apply_pre_changes(self):
|
|
1343
1214
|
# get the file name
|
|
1344
1215
|
file_name = os.path.basename(self.args.file)
|
|
1345
1216
|
if not self.args.skip_commit:
|
|
1346
1217
|
try:
|
|
1218
|
+
commit_result = git_utils.commit_changes(
|
|
1219
|
+
self.args.source_dir, f"auto_coder_pre_{file_name}")
|
|
1347
1220
|
get_event_manager(self.args.event_file).write_result(
|
|
1348
1221
|
EventContentCreator.create_result(
|
|
1349
|
-
content=
|
|
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(
|
|
1350
1228
|
action_file=self.args.file,
|
|
1351
1229
|
is_streaming=False,
|
|
1352
1230
|
path="/agent/edit/apply_pre_changes",
|
|
1353
1231
|
stream_out_type="/agent/edit").to_dict())
|
|
1354
|
-
|
|
1355
|
-
self.args.source_dir, f"auto_coder_pre_{file_name}")
|
|
1232
|
+
|
|
1356
1233
|
except Exception as e:
|
|
1357
1234
|
self.printer.print_in_terminal("git_init_required",
|
|
1358
1235
|
source_dir=self.args.source_dir, error=str(e))
|
|
@@ -1362,16 +1239,22 @@ class AgenticEdit:
|
|
|
1362
1239
|
"""
|
|
1363
1240
|
Apply all tracked file changes to the original project directory.
|
|
1364
1241
|
"""
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
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:
|
|
1375
1258
|
if not self.args.skip_commit:
|
|
1376
1259
|
try:
|
|
1377
1260
|
file_name = os.path.basename(self.args.file)
|
|
@@ -1379,13 +1262,20 @@ class AgenticEdit:
|
|
|
1379
1262
|
self.args.source_dir,
|
|
1380
1263
|
f"{self.args.query}\nauto_coder_{file_name}",
|
|
1381
1264
|
)
|
|
1382
|
-
|
|
1265
|
+
|
|
1383
1266
|
get_event_manager(self.args.event_file).write_result(
|
|
1384
1267
|
EventContentCreator.create_result(
|
|
1385
|
-
content=
|
|
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(
|
|
1386
1274
|
action_file=self.args.file,
|
|
1387
1275
|
is_streaming=False,
|
|
1276
|
+
path="/agent/edit/apply_changes",
|
|
1388
1277
|
stream_out_type="/agent/edit").to_dict())
|
|
1278
|
+
|
|
1389
1279
|
action_yml_file_manager = ActionYmlFileManager(
|
|
1390
1280
|
self.args.source_dir)
|
|
1391
1281
|
action_file_name = os.path.basename(self.args.file)
|
|
@@ -1431,6 +1321,15 @@ class AgenticEdit:
|
|
|
1431
1321
|
console.print(Panel(
|
|
1432
1322
|
f"[bold]{get_message('/agent/edit/user_query')}:[/bold]\n{request.user_input}", title=get_message("/agent/edit/objective"), border_style="blue"))
|
|
1433
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
|
+
|
|
1434
1333
|
try:
|
|
1435
1334
|
self.apply_pre_changes()
|
|
1436
1335
|
event_stream = self.analyze(request)
|
|
@@ -1457,17 +1356,12 @@ class AgenticEdit:
|
|
|
1457
1356
|
# 添加日志记录
|
|
1458
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}")
|
|
1459
1358
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
output_cost=output_cost,
|
|
1467
|
-
speed=0.0,
|
|
1468
|
-
model_names=model_name,
|
|
1469
|
-
sampling_count=1
|
|
1470
|
-
)
|
|
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
|
|
1471
1365
|
|
|
1472
1366
|
if isinstance(event, LLMThinkingEvent):
|
|
1473
1367
|
# Render thinking within a less prominent style, maybe grey?
|
|
@@ -1590,7 +1484,35 @@ class AgenticEdit:
|
|
|
1590
1484
|
|
|
1591
1485
|
time.sleep(0.1) # Small delay for better visual flow
|
|
1592
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
|
+
|
|
1593
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
|
+
|
|
1594
1516
|
logger.exception(
|
|
1595
1517
|
"An unexpected error occurred during agent execution:")
|
|
1596
1518
|
console.print(Panel(
|
|
@@ -1598,3 +1520,204 @@ class AgenticEdit:
|
|
|
1598
1520
|
raise e
|
|
1599
1521
|
finally:
|
|
1600
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
|