auto-coder 0.1.398__py3-none-any.whl → 0.1.399__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.399.dist-info/METADATA +396 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/RECORD +62 -28
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/WHEEL +1 -1
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/entry_points.txt +2 -0
- autocoder/agent/base_agentic/base_agent.py +2 -2
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
- autocoder/agent/entry_command_agent/__init__.py +29 -0
- autocoder/agent/entry_command_agent/auto_tool.py +61 -0
- autocoder/agent/entry_command_agent/chat.py +475 -0
- autocoder/agent/entry_command_agent/designer.py +53 -0
- autocoder/agent/entry_command_agent/generate_command.py +50 -0
- autocoder/agent/entry_command_agent/project_reader.py +58 -0
- autocoder/agent/entry_command_agent/voice2text.py +71 -0
- autocoder/auto_coder.py +23 -548
- autocoder/auto_coder_runner.py +510 -8
- autocoder/chat/rules_command.py +1 -1
- autocoder/chat_auto_coder.py +6 -1
- autocoder/common/ac_style_command_parser/__init__.py +15 -0
- autocoder/common/ac_style_command_parser/example.py +7 -0
- autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +1 -33
- autocoder/common/ac_style_command_parser/test_parser.py +516 -0
- autocoder/common/command_completer_v2.py +1 -1
- autocoder/common/command_file_manager/examples.py +22 -8
- autocoder/common/command_file_manager/manager.py +37 -6
- autocoder/common/conversations/get_conversation_manager.py +143 -0
- autocoder/common/conversations/manager.py +122 -11
- autocoder/common/conversations/storage/index_manager.py +89 -0
- autocoder/common/v2/agent/agentic_edit.py +131 -18
- autocoder/common/v2/agent/agentic_edit_types.py +10 -0
- autocoder/common/v2/code_auto_generate_editblock.py +10 -2
- autocoder/dispacher/__init__.py +10 -0
- autocoder/rags.py +0 -27
- autocoder/run_context.py +1 -0
- autocoder/sdk/__init__.py +188 -0
- autocoder/sdk/cli/__init__.py +15 -0
- autocoder/sdk/cli/__main__.py +26 -0
- autocoder/sdk/cli/completion_wrapper.py +38 -0
- autocoder/sdk/cli/formatters.py +211 -0
- autocoder/sdk/cli/handlers.py +174 -0
- autocoder/sdk/cli/install_completion.py +301 -0
- autocoder/sdk/cli/main.py +284 -0
- autocoder/sdk/cli/options.py +72 -0
- autocoder/sdk/constants.py +102 -0
- autocoder/sdk/core/__init__.py +20 -0
- autocoder/sdk/core/auto_coder_core.py +867 -0
- autocoder/sdk/core/bridge.py +497 -0
- autocoder/sdk/example.py +0 -0
- autocoder/sdk/exceptions.py +72 -0
- autocoder/sdk/models/__init__.py +19 -0
- autocoder/sdk/models/messages.py +209 -0
- autocoder/sdk/models/options.py +194 -0
- autocoder/sdk/models/responses.py +311 -0
- autocoder/sdk/session/__init__.py +32 -0
- autocoder/sdk/session/session.py +106 -0
- autocoder/sdk/session/session_manager.py +56 -0
- autocoder/sdk/utils/__init__.py +24 -0
- autocoder/sdk/utils/formatters.py +216 -0
- autocoder/sdk/utils/io_utils.py +302 -0
- autocoder/sdk/utils/validators.py +287 -0
- autocoder/version.py +2 -1
- auto_coder-0.1.398.dist-info/METADATA +0 -111
- autocoder/common/conversations/compatibility.py +0 -303
- autocoder/common/conversations/conversation_manager.py +0 -502
- autocoder/common/conversations/example.py +0 -152
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info/licenses}/LICENSE +0 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/top_level.txt +0 -0
autocoder/auto_coder_runner.py
CHANGED
|
@@ -12,6 +12,7 @@ import io
|
|
|
12
12
|
import uuid
|
|
13
13
|
import glob
|
|
14
14
|
import time
|
|
15
|
+
import datetime
|
|
15
16
|
import hashlib
|
|
16
17
|
from contextlib import contextmanager
|
|
17
18
|
from typing import List, Dict, Any, Optional
|
|
@@ -24,6 +25,7 @@ from autocoder.auto_coder import main as auto_coder_main
|
|
|
24
25
|
from autocoder.utils import get_last_yaml_file
|
|
25
26
|
from autocoder.commands.auto_command import CommandAutoTuner, AutoCommandRequest, CommandConfig, MemoryConfig
|
|
26
27
|
from autocoder.common.v2.agent.agentic_edit import AgenticEdit,AgenticEditRequest
|
|
28
|
+
from autocoder.common.v2.agent.agentic_edit_types import AgenticEditConversationConfig
|
|
27
29
|
from autocoder.index.symbols_utils import (
|
|
28
30
|
extract_symbols,
|
|
29
31
|
SymbolType,
|
|
@@ -60,12 +62,13 @@ from autocoder.utils.thread_utils import run_in_raw_thread
|
|
|
60
62
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
61
63
|
from autocoder.common.command_completer import CommandCompleter,FileSystemModel as CCFileSystemModel,MemoryConfig as CCMemoryModel
|
|
62
64
|
from autocoder.common.conf_validator import ConfigValidator
|
|
63
|
-
from autocoder import
|
|
65
|
+
from autocoder.common.ac_style_command_parser import parse_query
|
|
64
66
|
from loguru import logger as global_logger
|
|
65
67
|
from autocoder.utils.project_structure import EnhancedFileAnalyzer
|
|
66
68
|
from autocoder.common import SourceCodeList,SourceCode
|
|
67
69
|
from autocoder.common.file_monitor import FileMonitor
|
|
68
70
|
from filelock import FileLock
|
|
71
|
+
from autocoder.common.command_file_manager import CommandManager
|
|
69
72
|
|
|
70
73
|
|
|
71
74
|
## 对外API,用于第三方集成 auto-coder 使用。
|
|
@@ -193,7 +196,7 @@ def configure_project_type():
|
|
|
193
196
|
|
|
194
197
|
if project_type:
|
|
195
198
|
configure(f"project_type:{project_type}", skip_print=True)
|
|
196
|
-
configure("skip_build_index:
|
|
199
|
+
configure("skip_build_index:true", skip_print=True)
|
|
197
200
|
print_info(f"\n{get_message('project_type_set')} {project_type}")
|
|
198
201
|
else:
|
|
199
202
|
print_info(f"\n{get_message('using_default_type')}")
|
|
@@ -281,6 +284,16 @@ def start():
|
|
|
281
284
|
configure_logger()
|
|
282
285
|
init_singleton_instances()
|
|
283
286
|
|
|
287
|
+
# conversation_manager = get_conversation_manager()
|
|
288
|
+
# if not conversation_manager.get_current_conversation():
|
|
289
|
+
# # Format: yyyyMMdd-MM-ss-uuid
|
|
290
|
+
# current_time = datetime.datetime.now()
|
|
291
|
+
# time_str = current_time.strftime("%Y%m%d-%H-%M-%S")
|
|
292
|
+
# name = f"{time_str}-{str(uuid.uuid4())}"
|
|
293
|
+
# conversation_id = conversation_manager.create_new_conversation(name=name,description="")
|
|
294
|
+
# conversation_manager.set_current_conversation(conversation_id)
|
|
295
|
+
|
|
296
|
+
|
|
284
297
|
def stop():
|
|
285
298
|
try:
|
|
286
299
|
FileMonitor(project_root).stop()
|
|
@@ -737,6 +750,266 @@ def revert():
|
|
|
737
750
|
}})
|
|
738
751
|
|
|
739
752
|
|
|
753
|
+
def add_files(args: List[str]):
|
|
754
|
+
|
|
755
|
+
result_manager = ResultManager()
|
|
756
|
+
if "groups" not in memory["current_files"]:
|
|
757
|
+
memory["current_files"]["groups"] = {}
|
|
758
|
+
if "groups_info" not in memory["current_files"]:
|
|
759
|
+
memory["current_files"]["groups_info"] = {}
|
|
760
|
+
if "current_groups" not in memory["current_files"]:
|
|
761
|
+
memory["current_files"]["current_groups"] = []
|
|
762
|
+
groups = memory["current_files"]["groups"]
|
|
763
|
+
groups_info = memory["current_files"]["groups_info"]
|
|
764
|
+
|
|
765
|
+
console = Console()
|
|
766
|
+
printer = Printer()
|
|
767
|
+
|
|
768
|
+
if not args:
|
|
769
|
+
printer.print_in_terminal("add_files_no_args", style="red")
|
|
770
|
+
result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
|
|
771
|
+
meta={"action": "add_files","success":False, "input":{ "args": args}})
|
|
772
|
+
return
|
|
773
|
+
|
|
774
|
+
if args[0] == "/refresh":
|
|
775
|
+
completer.refresh_files()
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, llm):
|
|
779
|
+
"""
|
|
780
|
+
处理 post_commit 和 PR 功能
|
|
781
|
+
|
|
782
|
+
Args:
|
|
783
|
+
post_commit: 是否执行 post_commit
|
|
784
|
+
pr: 是否创建 PR
|
|
785
|
+
query: 原始查询
|
|
786
|
+
args: 配置参数
|
|
787
|
+
llm: LLM 实例
|
|
788
|
+
"""
|
|
789
|
+
printer = Printer()
|
|
790
|
+
console = Console()
|
|
791
|
+
|
|
792
|
+
try:
|
|
793
|
+
if post_commit:
|
|
794
|
+
# 执行 post_commit 操作
|
|
795
|
+
printer.print_in_terminal("post_commit_executing", style="blue")
|
|
796
|
+
|
|
797
|
+
# 检查是否有未提交的更改
|
|
798
|
+
uncommitted_changes = git_utils.get_uncommitted_changes(".")
|
|
799
|
+
if uncommitted_changes:
|
|
800
|
+
# 生成提交消息
|
|
801
|
+
commit_message = git_utils.generate_commit_message.with_llm(llm).run(
|
|
802
|
+
uncommitted_changes
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
# 执行提交
|
|
806
|
+
commit_result = git_utils.commit_changes(".", commit_message)
|
|
807
|
+
git_utils.print_commit_info(commit_result=commit_result)
|
|
808
|
+
printer.print_in_terminal("post_commit_success", style="green", message=commit_message)
|
|
809
|
+
|
|
810
|
+
# 如果需要创建 PR,则继续处理
|
|
811
|
+
if pr:
|
|
812
|
+
_create_pull_request(commit_result, query, llm)
|
|
813
|
+
else:
|
|
814
|
+
printer.print_in_terminal("post_commit_no_changes", style="yellow")
|
|
815
|
+
|
|
816
|
+
elif pr:
|
|
817
|
+
# 只创建 PR,不执行 post_commit
|
|
818
|
+
# 获取最后一个 commit
|
|
819
|
+
try:
|
|
820
|
+
repo = git.Repo(".")
|
|
821
|
+
last_commit = repo.head.commit
|
|
822
|
+
|
|
823
|
+
# 创建一个模拟的 commit_result 对象
|
|
824
|
+
class MockCommitResult:
|
|
825
|
+
def __init__(self, commit):
|
|
826
|
+
self.commit_hash = commit.hexsha
|
|
827
|
+
self.commit_message = commit.message.strip()
|
|
828
|
+
self.changed_files = []
|
|
829
|
+
|
|
830
|
+
mock_commit_result = MockCommitResult(last_commit)
|
|
831
|
+
_create_pull_request(mock_commit_result, query, llm)
|
|
832
|
+
|
|
833
|
+
except Exception as e:
|
|
834
|
+
printer.print_in_terminal("pr_get_last_commit_failed", style="red", error=str(e))
|
|
835
|
+
|
|
836
|
+
except Exception as e:
|
|
837
|
+
printer.print_in_terminal("post_commit_pr_failed", style="red", error=str(e))
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
def _create_pull_request(commit_result, original_query: str, llm):
|
|
841
|
+
"""
|
|
842
|
+
创建 Pull Request
|
|
843
|
+
|
|
844
|
+
Args:
|
|
845
|
+
commit_result: 提交结果对象
|
|
846
|
+
original_query: 原始查询
|
|
847
|
+
llm: LLM 实例
|
|
848
|
+
"""
|
|
849
|
+
printer = Printer()
|
|
850
|
+
console = Console()
|
|
851
|
+
|
|
852
|
+
try:
|
|
853
|
+
# 检查是否安装了 gh CLI
|
|
854
|
+
gh_check = subprocess.run(["gh", "--version"], capture_output=True, text=True)
|
|
855
|
+
if gh_check.returncode != 0:
|
|
856
|
+
printer.print_in_terminal("pr_gh_not_installed", style="red")
|
|
857
|
+
return
|
|
858
|
+
|
|
859
|
+
# 检查是否已经登录 GitHub
|
|
860
|
+
auth_check = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True)
|
|
861
|
+
if auth_check.returncode != 0:
|
|
862
|
+
printer.print_in_terminal("pr_gh_not_authenticated", style="red")
|
|
863
|
+
return
|
|
864
|
+
|
|
865
|
+
# 获取当前分支名
|
|
866
|
+
repo = git.Repo(".")
|
|
867
|
+
current_branch = repo.active_branch.name
|
|
868
|
+
|
|
869
|
+
# 如果在 main/master 分支,创建新分支
|
|
870
|
+
if current_branch in ["main", "master"]:
|
|
871
|
+
# 生成新分支名
|
|
872
|
+
import re
|
|
873
|
+
branch_name = re.sub(r'[^a-zA-Z0-9\-_]', '-', original_query.lower())
|
|
874
|
+
branch_name = f"auto-coder-{branch_name[:30]}-{int(time.time())}"
|
|
875
|
+
|
|
876
|
+
# 创建并切换到新分支
|
|
877
|
+
new_branch = repo.create_head(branch_name)
|
|
878
|
+
new_branch.checkout()
|
|
879
|
+
current_branch = branch_name
|
|
880
|
+
|
|
881
|
+
printer.print_in_terminal("pr_created_branch", style="blue", branch=branch_name)
|
|
882
|
+
|
|
883
|
+
# 推送当前分支到远程
|
|
884
|
+
try:
|
|
885
|
+
origin = repo.remotes.origin
|
|
886
|
+
origin.push(current_branch)
|
|
887
|
+
printer.print_in_terminal("pr_pushed_branch", style="blue", branch=current_branch)
|
|
888
|
+
except Exception as e:
|
|
889
|
+
printer.print_in_terminal("pr_push_failed", style="red", error=str(e))
|
|
890
|
+
return
|
|
891
|
+
|
|
892
|
+
# 生成 PR 标题和描述
|
|
893
|
+
pr_title, pr_body = _generate_pr_content(commit_result, original_query, llm)
|
|
894
|
+
|
|
895
|
+
# 创建 PR
|
|
896
|
+
pr_cmd = [
|
|
897
|
+
"gh", "pr", "create",
|
|
898
|
+
"--title", pr_title,
|
|
899
|
+
"--body", pr_body,
|
|
900
|
+
"--head", current_branch
|
|
901
|
+
]
|
|
902
|
+
|
|
903
|
+
pr_result = subprocess.run(pr_cmd, capture_output=True, text=True)
|
|
904
|
+
|
|
905
|
+
if pr_result.returncode == 0:
|
|
906
|
+
pr_url = pr_result.stdout.strip()
|
|
907
|
+
printer.print_in_terminal("pr_created_success", style="green", url=pr_url)
|
|
908
|
+
|
|
909
|
+
# 显示 PR 信息
|
|
910
|
+
console.print(Panel(
|
|
911
|
+
f"[bold green]Pull Request Created Successfully![/bold green]\n\n"
|
|
912
|
+
f"[bold]Title:[/bold] {pr_title}\n"
|
|
913
|
+
f"[bold]URL:[/bold] {pr_url}\n"
|
|
914
|
+
f"[bold]Branch:[/bold] {current_branch}",
|
|
915
|
+
title="🎉 Pull Request",
|
|
916
|
+
border_style="green"
|
|
917
|
+
))
|
|
918
|
+
else:
|
|
919
|
+
printer.print_in_terminal("pr_creation_failed", style="red", error=pr_result.stderr)
|
|
920
|
+
|
|
921
|
+
except Exception as e:
|
|
922
|
+
printer.print_in_terminal("pr_creation_error", style="red", error=str(e))
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
@byzerllm.prompt()
|
|
926
|
+
def _generate_pr_content(commit_result, original_query: str, llm) -> tuple:
|
|
927
|
+
"""
|
|
928
|
+
生成 PR 标题和描述
|
|
929
|
+
|
|
930
|
+
根据提交信息和原始查询生成合适的 PR 标题和描述。
|
|
931
|
+
|
|
932
|
+
Args:
|
|
933
|
+
commit_result: 提交结果,包含 commit_message 和 changed_files
|
|
934
|
+
original_query: 用户的原始查询请求
|
|
935
|
+
|
|
936
|
+
Returns:
|
|
937
|
+
tuple: (pr_title, pr_body) PR标题和描述内容
|
|
938
|
+
|
|
939
|
+
请生成简洁明了的 PR 标题(不超过72字符)和详细的描述内容。
|
|
940
|
+
标题应该概括主要变更,描述应该包含:
|
|
941
|
+
1. 变更的背景和目的
|
|
942
|
+
2. 主要修改内容
|
|
943
|
+
3. 影响的文件(如果有的话)
|
|
944
|
+
|
|
945
|
+
提交信息:{{ commit_result.commit_message }}
|
|
946
|
+
原始需求:{{ original_query }}
|
|
947
|
+
{% if commit_result.changed_files %}
|
|
948
|
+
修改的文件:
|
|
949
|
+
{% for file in commit_result.changed_files %}
|
|
950
|
+
- {{ file }}
|
|
951
|
+
{% endfor %}
|
|
952
|
+
{% endif %}
|
|
953
|
+
"""
|
|
954
|
+
|
|
955
|
+
# 这个函数会被 byzerllm 装饰器处理,返回 LLM 生成的内容
|
|
956
|
+
# 实际实现会在运行时由装饰器处理
|
|
957
|
+
pass
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
# 实际的 PR 内容生成函数
|
|
961
|
+
def _generate_pr_content(commit_result, original_query: str, llm):
|
|
962
|
+
"""
|
|
963
|
+
生成 PR 标题和描述的实际实现
|
|
964
|
+
"""
|
|
965
|
+
try:
|
|
966
|
+
# 使用 LLM 生成 PR 内容
|
|
967
|
+
prompt = f"""
|
|
968
|
+
根据以下信息生成 Pull Request 的标题和描述:
|
|
969
|
+
|
|
970
|
+
提交信息:{getattr(commit_result, 'commit_message', 'Auto-generated commit')}
|
|
971
|
+
原始需求:{original_query}
|
|
972
|
+
修改的文件:{getattr(commit_result, 'changed_files', [])}
|
|
973
|
+
|
|
974
|
+
请生成:
|
|
975
|
+
1. 简洁的 PR 标题(不超过72字符)
|
|
976
|
+
2. 详细的 PR 描述,包含变更背景、主要修改内容等
|
|
977
|
+
|
|
978
|
+
格式要求:
|
|
979
|
+
TITLE: [标题内容]
|
|
980
|
+
BODY: [描述内容]
|
|
981
|
+
"""
|
|
982
|
+
|
|
983
|
+
response = llm.chat([{"role": "user", "content": prompt}])
|
|
984
|
+
|
|
985
|
+
# 解析响应
|
|
986
|
+
lines = response.split('\n')
|
|
987
|
+
title = ""
|
|
988
|
+
body = ""
|
|
989
|
+
|
|
990
|
+
for line in lines:
|
|
991
|
+
if line.startswith("TITLE:"):
|
|
992
|
+
title = line.replace("TITLE:", "").strip()
|
|
993
|
+
elif line.startswith("BODY:"):
|
|
994
|
+
body = line.replace("BODY:", "").strip()
|
|
995
|
+
elif body: # 如果已经开始收集 body,继续添加后续行
|
|
996
|
+
body += "\n" + line
|
|
997
|
+
|
|
998
|
+
# 如果解析失败,使用默认值
|
|
999
|
+
if not title:
|
|
1000
|
+
title = f"Auto-coder: {original_query[:50]}..."
|
|
1001
|
+
if not body:
|
|
1002
|
+
body = f"This PR was automatically generated by Auto-coder.\n\nOriginal request: {original_query}"
|
|
1003
|
+
|
|
1004
|
+
return title, body
|
|
1005
|
+
|
|
1006
|
+
except Exception as e:
|
|
1007
|
+
# 如果 LLM 生成失败,使用默认值
|
|
1008
|
+
title = f"Auto-coder: {original_query[:50]}..."
|
|
1009
|
+
body = f"This PR was automatically generated by Auto-coder.\n\nOriginal request: {original_query}\n\nCommit: {getattr(commit_result, 'commit_message', 'Auto-generated commit')}"
|
|
1010
|
+
return title, body
|
|
1011
|
+
|
|
1012
|
+
|
|
740
1013
|
def add_files(args: List[str]):
|
|
741
1014
|
|
|
742
1015
|
result_manager = ResultManager()
|
|
@@ -1704,7 +1977,7 @@ def chat(query: str):
|
|
|
1704
1977
|
yaml_config["emb_model"] = conf["emb_model"]
|
|
1705
1978
|
|
|
1706
1979
|
# 解析命令
|
|
1707
|
-
commands_infos =
|
|
1980
|
+
commands_infos = parse_query(query)
|
|
1708
1981
|
if len(commands_infos) > 0:
|
|
1709
1982
|
if "query" in commands_infos:
|
|
1710
1983
|
query = " ".join(commands_infos["query"]["args"])
|
|
@@ -1866,7 +2139,7 @@ def active_context(query: str):
|
|
|
1866
2139
|
query: 命令参数,例如 "list" 列出所有任务
|
|
1867
2140
|
"""
|
|
1868
2141
|
# 解析命令
|
|
1869
|
-
commands_infos =
|
|
2142
|
+
commands_infos = parse_query(query)
|
|
1870
2143
|
command = "list" # 默认命令是列出所有任务
|
|
1871
2144
|
|
|
1872
2145
|
if len(commands_infos) > 0:
|
|
@@ -2918,16 +3191,55 @@ def auto_command(query: str,extra_args: Dict[str,Any]={}):
|
|
|
2918
3191
|
|
|
2919
3192
|
llm = get_single_llm(args.code_model or args.model,product_mode=args.product_mode)
|
|
2920
3193
|
conversation_history = extra_args.get("conversations",[])
|
|
3194
|
+
|
|
3195
|
+
command_infos = parse_query(query)
|
|
3196
|
+
|
|
3197
|
+
# terminal 的总是接着上次对话, 所以这里总是设置为 resume
|
|
3198
|
+
conversation_config = AgenticEditConversationConfig(
|
|
3199
|
+
action="resume"
|
|
3200
|
+
)
|
|
3201
|
+
|
|
3202
|
+
## web 模式会自己管理对话,所以这里总是设置为新对话
|
|
3203
|
+
if get_run_context().mode == RunMode.WEB:
|
|
3204
|
+
command_infos = {
|
|
3205
|
+
"new":{
|
|
3206
|
+
"args":[query]
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
task_query = query
|
|
3211
|
+
|
|
3212
|
+
if "new" in command_infos:
|
|
3213
|
+
conversation_config.action = "new"
|
|
3214
|
+
task_query = " ".join(command_infos["new"]["args"])
|
|
3215
|
+
|
|
3216
|
+
if "resume" in command_infos:
|
|
3217
|
+
conversation_config.action = "resume"
|
|
3218
|
+
conversation_config.conversation_id = command_infos["resume"]["args"][0]
|
|
3219
|
+
task_query = " ".join(command_infos["resume"]["args"][1:])
|
|
3220
|
+
|
|
3221
|
+
if "list" in command_infos:
|
|
3222
|
+
conversation_config.action = "list"
|
|
3223
|
+
|
|
3224
|
+
if "command" in command_infos:
|
|
3225
|
+
conversation_config.action = "command"
|
|
3226
|
+
task_query = render_command_file_with_variables(command_infos)
|
|
3227
|
+
|
|
3228
|
+
|
|
3229
|
+
conversation_config.query = task_query
|
|
3230
|
+
|
|
2921
3231
|
agent = AgenticEdit(llm=llm,args=args,files=SourceCodeList(sources=sources),
|
|
2922
3232
|
conversation_history=conversation_history,
|
|
2923
3233
|
memory_config=MemoryConfig(memory=memory,
|
|
2924
3234
|
save_memory_func=save_memory), command_config=CommandConfig,
|
|
2925
|
-
conversation_name="current"
|
|
3235
|
+
conversation_name="current",
|
|
3236
|
+
conversation_config=conversation_config
|
|
2926
3237
|
)
|
|
2927
3238
|
if get_run_context().mode == RunMode.WEB:
|
|
2928
|
-
agent.run_with_events(AgenticEditRequest(user_input=
|
|
2929
|
-
|
|
2930
|
-
|
|
3239
|
+
agent.run_with_events(AgenticEditRequest(user_input=task_query))
|
|
3240
|
+
|
|
3241
|
+
if get_run_context().mode == RunMode.TERMINAL:
|
|
3242
|
+
agent.run_in_terminal(AgenticEditRequest(user_input=task_query))
|
|
2931
3243
|
|
|
2932
3244
|
completer.refresh_files()
|
|
2933
3245
|
return
|
|
@@ -2983,3 +3295,193 @@ def auto_command(query: str,extra_args: Dict[str,Any]={}):
|
|
|
2983
3295
|
padding=(1, 2)
|
|
2984
3296
|
))
|
|
2985
3297
|
completer.refresh_files()
|
|
3298
|
+
|
|
3299
|
+
|
|
3300
|
+
|
|
3301
|
+
def run_auto_command(query: str,
|
|
3302
|
+
pre_commit:bool=False,
|
|
3303
|
+
post_commit:bool=False,
|
|
3304
|
+
pr:bool=False,
|
|
3305
|
+
extra_args: Dict[str,Any]={}
|
|
3306
|
+
):
|
|
3307
|
+
"""处理/auto指令"""
|
|
3308
|
+
args = get_final_config()
|
|
3309
|
+
memory = get_memory()
|
|
3310
|
+
if args.enable_agentic_edit:
|
|
3311
|
+
from autocoder.run_context import get_run_context,RunMode
|
|
3312
|
+
execute_file,args = generate_new_yaml(query)
|
|
3313
|
+
args.file = execute_file
|
|
3314
|
+
current_files = memory.get("current_files",{}).get("files",[])
|
|
3315
|
+
sources = []
|
|
3316
|
+
for file in current_files:
|
|
3317
|
+
try:
|
|
3318
|
+
with open(file,"r",encoding="utf-8") as f:
|
|
3319
|
+
sources.append(SourceCode(module_name=file,source_code=f.read()))
|
|
3320
|
+
except Exception as e:
|
|
3321
|
+
global_logger.error(f"Failed to read file {file}: {e}")
|
|
3322
|
+
|
|
3323
|
+
llm = get_single_llm(args.code_model or args.model,product_mode=args.product_mode)
|
|
3324
|
+
conversation_history = extra_args.get("conversations",[])
|
|
3325
|
+
|
|
3326
|
+
command_infos = parse_query(query)
|
|
3327
|
+
|
|
3328
|
+
# terminal 的总是接着上次对话, 所以这里总是设置为 resume
|
|
3329
|
+
conversation_config = AgenticEditConversationConfig(
|
|
3330
|
+
action="resume"
|
|
3331
|
+
)
|
|
3332
|
+
|
|
3333
|
+
## web 模式会自己管理对话,所以这里总是设置为新对话
|
|
3334
|
+
if get_run_context().mode == RunMode.WEB:
|
|
3335
|
+
command_infos = {
|
|
3336
|
+
"new":{
|
|
3337
|
+
"args":[query]
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
task_query = query
|
|
3342
|
+
|
|
3343
|
+
if "new" in command_infos:
|
|
3344
|
+
conversation_config.action = "new"
|
|
3345
|
+
task_query = " ".join(command_infos["new"]["args"])
|
|
3346
|
+
|
|
3347
|
+
if "resume" in command_infos:
|
|
3348
|
+
conversation_config.action = "resume"
|
|
3349
|
+
conversation_config.conversation_id = command_infos["resume"]["args"][0]
|
|
3350
|
+
task_query = " ".join(command_infos["resume"]["args"][1:])
|
|
3351
|
+
|
|
3352
|
+
if "list" in command_infos:
|
|
3353
|
+
conversation_config.action = "list"
|
|
3354
|
+
|
|
3355
|
+
|
|
3356
|
+
if "command" in command_infos:
|
|
3357
|
+
conversation_config.action = "command"
|
|
3358
|
+
task_query = render_command_file_with_variables(command_infos)
|
|
3359
|
+
|
|
3360
|
+
conversation_config.query = task_query
|
|
3361
|
+
|
|
3362
|
+
agent = AgenticEdit(llm=llm,args=args,files=SourceCodeList(sources=sources),
|
|
3363
|
+
conversation_history=conversation_history,
|
|
3364
|
+
memory_config=MemoryConfig(memory=memory,
|
|
3365
|
+
save_memory_func=save_memory), command_config=CommandConfig,
|
|
3366
|
+
conversation_name="current",
|
|
3367
|
+
conversation_config=conversation_config
|
|
3368
|
+
)
|
|
3369
|
+
if pre_commit:
|
|
3370
|
+
agent.apply_pre_changes()
|
|
3371
|
+
|
|
3372
|
+
events = agent.run(AgenticEditRequest(user_input=task_query))
|
|
3373
|
+
|
|
3374
|
+
for event in events:
|
|
3375
|
+
yield event
|
|
3376
|
+
|
|
3377
|
+
# 处理 post_commit 和 PR 功能
|
|
3378
|
+
if post_commit or pr:
|
|
3379
|
+
_handle_post_commit_and_pr(post_commit, pr, query, args, llm)
|
|
3380
|
+
|
|
3381
|
+
completer.refresh_files()
|
|
3382
|
+
return
|
|
3383
|
+
|
|
3384
|
+
args = get_final_config()
|
|
3385
|
+
# 准备请求参数
|
|
3386
|
+
request = AutoCommandRequest(
|
|
3387
|
+
user_input=query
|
|
3388
|
+
)
|
|
3389
|
+
|
|
3390
|
+
# 初始化调优器
|
|
3391
|
+
llm = get_single_llm(args.chat_model or args.model,product_mode=args.product_mode)
|
|
3392
|
+
tuner = CommandAutoTuner(llm,
|
|
3393
|
+
args=args,
|
|
3394
|
+
memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
|
|
3395
|
+
command_config=CommandConfig(
|
|
3396
|
+
add_files=add_files,
|
|
3397
|
+
remove_files=remove_files,
|
|
3398
|
+
list_files=list_files,
|
|
3399
|
+
conf=configure,
|
|
3400
|
+
revert=revert,
|
|
3401
|
+
commit=commit,
|
|
3402
|
+
help=help,
|
|
3403
|
+
exclude_dirs=exclude_dirs,
|
|
3404
|
+
exclude_files=exclude_files,
|
|
3405
|
+
ask=ask,
|
|
3406
|
+
chat=chat,
|
|
3407
|
+
coding=coding,
|
|
3408
|
+
design=design,
|
|
3409
|
+
summon=summon,
|
|
3410
|
+
lib=lib_command,
|
|
3411
|
+
mcp=mcp,
|
|
3412
|
+
models=manage_models,
|
|
3413
|
+
index_build=index_build,
|
|
3414
|
+
index_query=index_query,
|
|
3415
|
+
execute_shell_command=execute_shell_command,
|
|
3416
|
+
generate_shell_command=generate_shell_command,
|
|
3417
|
+
conf_export=conf_export,
|
|
3418
|
+
conf_import=conf_import,
|
|
3419
|
+
index_export=index_export,
|
|
3420
|
+
index_import=index_import
|
|
3421
|
+
))
|
|
3422
|
+
|
|
3423
|
+
# 生成建议
|
|
3424
|
+
response = tuner.analyze(request)
|
|
3425
|
+
printer = Printer()
|
|
3426
|
+
# 显示建议
|
|
3427
|
+
console = Console()
|
|
3428
|
+
console.print(Panel(
|
|
3429
|
+
Markdown(response.reasoning or ""),
|
|
3430
|
+
title=printer.get_message_from_key_with_format("auto_command_reasoning_title"),
|
|
3431
|
+
border_style="blue",
|
|
3432
|
+
padding=(1, 2)
|
|
3433
|
+
))
|
|
3434
|
+
|
|
3435
|
+
# 处理 post_commit 和 PR 功能
|
|
3436
|
+
if post_commit or pr:
|
|
3437
|
+
_handle_post_commit_and_pr(post_commit, pr, query, args, llm)
|
|
3438
|
+
|
|
3439
|
+
completer.refresh_files()
|
|
3440
|
+
|
|
3441
|
+
|
|
3442
|
+
def render_command_file_with_variables(command_infos: Dict[str, Any]) -> str:
|
|
3443
|
+
"""
|
|
3444
|
+
使用 CommandManager 加载并渲染命令文件
|
|
3445
|
+
|
|
3446
|
+
Args:
|
|
3447
|
+
command_infos: parse_query(query) 的返回结果,包含命令和参数信息
|
|
3448
|
+
|
|
3449
|
+
Returns:
|
|
3450
|
+
str: 渲染后的文件内容
|
|
3451
|
+
|
|
3452
|
+
Raises:
|
|
3453
|
+
ValueError: 当参数不足或文件不存在时
|
|
3454
|
+
Exception: 当渲染过程出现错误时
|
|
3455
|
+
"""
|
|
3456
|
+
try:
|
|
3457
|
+
# 获取第一个命令的信息
|
|
3458
|
+
if not command_infos:
|
|
3459
|
+
raise ValueError("command_infos 为空,无法获取命令信息")
|
|
3460
|
+
|
|
3461
|
+
# command 的位置参数作为路径
|
|
3462
|
+
first_command = command_infos["command"]
|
|
3463
|
+
|
|
3464
|
+
# 获取位置参数(文件路径)
|
|
3465
|
+
args = first_command.get("args", [])
|
|
3466
|
+
if not args:
|
|
3467
|
+
raise ValueError("未提供文件路径参数")
|
|
3468
|
+
|
|
3469
|
+
file_path = args[0] # 第一个位置参数作为文件路径
|
|
3470
|
+
|
|
3471
|
+
# 获取关键字参数作为渲染参数
|
|
3472
|
+
kwargs = first_command.get("kwargs", {})
|
|
3473
|
+
|
|
3474
|
+
# 初始化 CommandManager
|
|
3475
|
+
command_manager = CommandManager()
|
|
3476
|
+
|
|
3477
|
+
# 使用 read_command_file_with_render 直接读取并渲染命令文件
|
|
3478
|
+
rendered_content = command_manager.read_command_file_with_render(file_path, kwargs)
|
|
3479
|
+
if rendered_content is None:
|
|
3480
|
+
raise ValueError(f"无法读取或渲染命令文件: {file_path}")
|
|
3481
|
+
|
|
3482
|
+
global_logger.info(f"成功渲染命令文件: {file_path}, 使用参数: {kwargs}")
|
|
3483
|
+
return rendered_content
|
|
3484
|
+
|
|
3485
|
+
except Exception as e:
|
|
3486
|
+
global_logger.error(f"render_command_file_with_variables 执行失败: {str(e)}")
|
|
3487
|
+
raise
|
autocoder/chat/rules_command.py
CHANGED
|
@@ -17,7 +17,7 @@ from autocoder.auto_coder_runner import get_final_config, get_single_llm
|
|
|
17
17
|
from autocoder.chat_auto_coder_lang import get_message, get_message_with_format
|
|
18
18
|
from autocoder.rag.token_counter import count_tokens
|
|
19
19
|
from autocoder.common.printer import Printer
|
|
20
|
-
from autocoder.
|
|
20
|
+
from autocoder.common.ac_style_command_parser import CommandParser
|
|
21
21
|
from loguru import logger
|
|
22
22
|
|
|
23
23
|
printer = Printer()
|
autocoder/chat_auto_coder.py
CHANGED
|
@@ -18,6 +18,11 @@ from autocoder.plugins import PluginManager
|
|
|
18
18
|
from autocoder.events.event_manager_singleton import gengerate_event_file_path
|
|
19
19
|
from autocoder.common.global_cancel import global_cancel
|
|
20
20
|
from autocoder.chat.models_command import handle_models_command
|
|
21
|
+
from autocoder.common.conversations.get_conversation_manager import (
|
|
22
|
+
get_conversation_manager,
|
|
23
|
+
get_conversation_manager_config,
|
|
24
|
+
reset_conversation_manager
|
|
25
|
+
)
|
|
21
26
|
from autocoder.auto_coder_runner import (
|
|
22
27
|
auto_command,
|
|
23
28
|
configure, # Keep configure if it's used elsewhere or by handle_conf_command internally (though we adapted handle_conf_command not to)
|
|
@@ -666,7 +671,7 @@ def main():
|
|
|
666
671
|
else:
|
|
667
672
|
execute_shell_command(command)
|
|
668
673
|
else:
|
|
669
|
-
if user_input:
|
|
674
|
+
if user_input and user_input.strip():
|
|
670
675
|
auto_command(user_input)
|
|
671
676
|
|
|
672
677
|
except KeyboardInterrupt:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from typing import Dict, List, Tuple, Any, Optional
|
|
2
1
|
import re
|
|
2
|
+
from typing import Dict, List, Tuple, Any, Optional
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class CommandParser:
|
|
@@ -246,35 +246,3 @@ def get_command_kwargs(query: str, command: str) -> Dict[str, str]:
|
|
|
246
246
|
if command_info:
|
|
247
247
|
return command_info['kwargs']
|
|
248
248
|
return {}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
# 示例用法
|
|
252
|
-
if __name__ == "__main__":
|
|
253
|
-
# 测试各种格式的查询
|
|
254
|
-
test_queries = [
|
|
255
|
-
"/learn hello world /commit 123456",
|
|
256
|
-
"/learn /commit 123456",
|
|
257
|
-
"/learn /commit commit_id=123456",
|
|
258
|
-
"/learn msg=hello /commit commit_id=123456",
|
|
259
|
-
"/learn hello key=value /commit",
|
|
260
|
-
# 带引号的值
|
|
261
|
-
'/learn msg="hello world" /commit message="Fix bug #123"',
|
|
262
|
-
"/learn 'quoted arg' key='value with spaces' /commit",
|
|
263
|
-
# 路径参数测试
|
|
264
|
-
"/learn /path/to/file.txt",
|
|
265
|
-
"/commit message='Added /path/to/file.txt'",
|
|
266
|
-
"Check /path/to/file.txt and also /another/path/file.md",
|
|
267
|
-
"/clone /path/to/repo /checkout branch",
|
|
268
|
-
"Use the file at /usr/local/bin/python with /learn"
|
|
269
|
-
]
|
|
270
|
-
|
|
271
|
-
for query in test_queries:
|
|
272
|
-
print(f"\nQuery: {query}")
|
|
273
|
-
result = parse_query(query)
|
|
274
|
-
print(f"Parsed: {result}")
|
|
275
|
-
|
|
276
|
-
if has_command(query, "commit"):
|
|
277
|
-
args = get_command_args(query, "commit")
|
|
278
|
-
kwargs = get_command_kwargs(query, "commit")
|
|
279
|
-
print(f"Commit args: {args}")
|
|
280
|
-
print(f"Commit kwargs: {kwargs}")
|