auto-coder 0.1.392__py3-none-any.whl → 0.1.393__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.392.dist-info → auto_coder-0.1.393.dist-info}/METADATA +1 -1
- {auto_coder-0.1.392.dist-info → auto_coder-0.1.393.dist-info}/RECORD +17 -13
- autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +1 -9
- autocoder/auto_coder_rag.py +67 -20
- autocoder/common/llm_friendly_package.py +36 -3
- autocoder/common/rag_manager/__init__.py +4 -0
- autocoder/common/rag_manager/rag_manager.py +144 -0
- autocoder/common/v2/agent/agentic_edit.py +60 -8
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +2 -0
- autocoder/common/v2/agent/agentic_edit_tools/use_rag_tool_resolver.py +91 -0
- autocoder/common/v2/agent/agentic_edit_types.py +5 -0
- autocoder/rags.py +348 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.392.dist-info → auto_coder-0.1.393.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.392.dist-info → auto_coder-0.1.393.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.392.dist-info → auto_coder-0.1.393.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.392.dist-info → auto_coder-0.1.393.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
autocoder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
autocoder/auto_coder.py,sha256=7602L3tG0JErNxh8vkLAmGUgv2c-DGPzPCkmWIQt9bs,69757
|
|
3
|
-
autocoder/auto_coder_rag.py,sha256=
|
|
3
|
+
autocoder/auto_coder_rag.py,sha256=r018fdFWn857cVgCHD1poNCCe70-UU-m9CpzMjSH-YY,43650
|
|
4
4
|
autocoder/auto_coder_runner.py,sha256=3kjlxvCgnOd-_P7VqPVcBYU_xqTdW515c-XFwuXUgdI,115789
|
|
5
5
|
autocoder/auto_coder_server.py,sha256=bLORGEclcVdbBVfM140JCI8WtdrU0jbgqdJIVVupiEU,20578
|
|
6
6
|
autocoder/benchmark.py,sha256=Ypomkdzd1T3GE6dRICY3Hj547dZ6_inqJbBJIp5QMco,4423
|
|
@@ -10,8 +10,9 @@ autocoder/command_args.py,sha256=HxflngkYtTrV17Vfgk6lyUyiG68jP2ftSc7FYr9AXwY,305
|
|
|
10
10
|
autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,10013
|
|
11
11
|
autocoder/lang.py,sha256=PFtATuOhHRnfpqHQkXr6p4C893JvpsgwTMif3l-GEi0,14321
|
|
12
12
|
autocoder/models.py,sha256=pD5u6gcMKRwWaLxeVin18g25k-ERyeHOFsRpOgO_Ae0,13788
|
|
13
|
+
autocoder/rags.py,sha256=1iVKH6GpgDYpE1LawehXoZNEupiHXlX5x6HmWJg83Vg,10555
|
|
13
14
|
autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
|
|
14
|
-
autocoder/version.py,sha256=
|
|
15
|
+
autocoder/version.py,sha256=2Pb_2wR0Aks--Yj5dIu-nevmw4UoRlXrBbRum03NOa8,24
|
|
15
16
|
autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
17
|
autocoder/agent/agentic_filter.py,sha256=zlInIRhawKIYTJjCiJBWqPCOV5UtMbh5VnvszfTy2vo,39824
|
|
17
18
|
autocoder/agent/auto_demand_organizer.py,sha256=URAq0gSEiHeV_W4zwhOI_83kHz0Ryfj1gcfh5jwCv_w,6501
|
|
@@ -40,7 +41,7 @@ autocoder/agent/base_agentic/tools/ask_followup_question_tool_resolver.py,sha256
|
|
|
40
41
|
autocoder/agent/base_agentic/tools/attempt_completion_tool_resolver.py,sha256=9a_qPihfm45Q22kmxmFwYrZrtNsgtEyBePL8D4SXfes,1487
|
|
41
42
|
autocoder/agent/base_agentic/tools/base_tool_resolver.py,sha256=Y0PmRPhBLLdZpr2DNV0W5goGfAqylM5m1bHxa3T6vAY,1009
|
|
42
43
|
autocoder/agent/base_agentic/tools/example_tool_resolver.py,sha256=8WBIqEH_76S0iBHBM3RhLkk14UpJ28lRAw6dNUT-0no,1388
|
|
43
|
-
autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py,sha256=
|
|
44
|
+
autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py,sha256=w-_8NCtT2TRlBi8ZcU4gKksbbQFicZ4vAakCC0HOziQ,3223
|
|
44
45
|
autocoder/agent/base_agentic/tools/list_files_tool_resolver.py,sha256=SCAYAiqmgwuCLMwRwroUQFfVS_hbcuAoG9-jIJxiB1c,8061
|
|
45
46
|
autocoder/agent/base_agentic/tools/plan_mode_respond_tool_resolver.py,sha256=hiLdMRknR_Aljd7Ic2bOWZKs11aFHdhkRTKTlP3IIgw,1461
|
|
46
47
|
autocoder/agent/base_agentic/tools/read_file_tool_resolver.py,sha256=egcxrXv32stvhB3QawE2k62-9W5U3TNuY3qoTyKKhs0,5076
|
|
@@ -97,7 +98,7 @@ autocoder/common/global_cancel.py,sha256=EYMIzdIJHQjoYP4grxhBxSIT3tCJOy3ESULNd-c
|
|
|
97
98
|
autocoder/common/image_to_page.py,sha256=yWiTJQ49Lm3j0FngiJhQ9u7qayqE_bOGb8Rk0TmSWx0,14123
|
|
98
99
|
autocoder/common/index_import_export.py,sha256=h758AYY1df6JMTKUXYmMkSgxItfymDt82XT7O-ygEuw,4565
|
|
99
100
|
autocoder/common/interpreter.py,sha256=62-dIakOunYB4yjmX8SHC0Gdy2h8NtxdgbpdqRZJ5vk,2833
|
|
100
|
-
autocoder/common/llm_friendly_package.py,sha256=
|
|
101
|
+
autocoder/common/llm_friendly_package.py,sha256=hrRoGxkHH91by-rQKJqNzDeUVvmG5HrS2eESCo3rLvI,16121
|
|
101
102
|
autocoder/common/llm_friendly_package_example.py,sha256=NXUiz6j7gpfzExC-2jmppnZaZaTuu4oGMCDneV7FC_k,4647
|
|
102
103
|
autocoder/common/llm_friendly_package_test.py,sha256=FhbzdNPEceuEBIxNvek2OYDFVlJlYJpkskZ7_PB4YSg,1930
|
|
103
104
|
autocoder/common/mcp_hub.py,sha256=grf51bZbZDXQIqlruIxXZjfat8MoVwK7NvHTHMaxKrg,23614
|
|
@@ -158,6 +159,8 @@ autocoder/common/ignorefiles/test_ignore_file_utils.py,sha256=EydHG6E2iPsnbt-Jt8
|
|
|
158
159
|
autocoder/common/mcp_servers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
159
160
|
autocoder/common/mcp_servers/mcp_server_gpt4o_mini_search.py,sha256=TApShxgoozLluobXHOK1-oAE1zm0-9jdRoPLQB1qwMI,5688
|
|
160
161
|
autocoder/common/mcp_servers/mcp_server_perplexity.py,sha256=CIC26UkfH1lYoVCjfyY5xGGYVx8h0oz0Uj1c7YJ3OPw,5560
|
|
162
|
+
autocoder/common/rag_manager/__init__.py,sha256=PR2LIdyFdF3XOFewd2VYuEFNp5ZUMrwAstYn8OOJX-Y,77
|
|
163
|
+
autocoder/common/rag_manager/rag_manager.py,sha256=l4tVztPOYTyHKWCsWOXyxzf5oxjEBx8rTDk6Q49dc6Y,6022
|
|
161
164
|
autocoder/common/rulefiles/__init__.py,sha256=babSbPdFaXk1NvdHtH2zrJLb_tWd7d2ELIyS8NApY_Y,221
|
|
162
165
|
autocoder/common/rulefiles/autocoderrules_utils.py,sha256=TvzKt41wa1uPnYXHkUaJRCuxCJL9IB-Zx6FsEwdSExg,19454
|
|
163
166
|
autocoder/common/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -175,10 +178,10 @@ autocoder/common/v2/code_editblock_manager.py,sha256=DMwJw-FAM6VyaBQV3p4xespHpgZ
|
|
|
175
178
|
autocoder/common/v2/code_manager.py,sha256=C403bS-f6urixwitlKHcml-J03hci-UyNwHJOqBiY6Q,9182
|
|
176
179
|
autocoder/common/v2/code_strict_diff_manager.py,sha256=Bys7tFAq4G03R1zUZuxrszBTvP4QB96jIw2y5BDLyRM,9424
|
|
177
180
|
autocoder/common/v2/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
178
|
-
autocoder/common/v2/agent/agentic_edit.py,sha256=
|
|
179
|
-
autocoder/common/v2/agent/agentic_edit_types.py,sha256=
|
|
181
|
+
autocoder/common/v2/agent/agentic_edit.py,sha256=_FEvwxlrdP5oLJTnE_P7Mdg4Hx43iwzsBaPvm8F7S-0,117662
|
|
182
|
+
autocoder/common/v2/agent/agentic_edit_types.py,sha256=rN3MuPES2Gwp8b5vrpp9v4eUkAlLA4AY7PEZ7LoUgvQ,5005
|
|
180
183
|
autocoder/common/v2/agent/agentic_tool_display.py,sha256=-a-JTQLc4q03E_rdIILKMI0B6DHN-5gcGlrqq-mBYK4,7239
|
|
181
|
-
autocoder/common/v2/agent/agentic_edit_tools/__init__.py,sha256=
|
|
184
|
+
autocoder/common/v2/agent/agentic_edit_tools/__init__.py,sha256=5I_N3xeekdGQJtOI6TxZ4LkwwtP0CQ7stu6SBaF9UeQ,1417
|
|
182
185
|
autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py,sha256=-HFXo3RR6CH8xXjDaE2mYV4XasTLAmvXe6WutL7qbwA,3208
|
|
183
186
|
autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py,sha256=82ZGKeRBSDKeead_XVBW4FxpiE-5dS7tBOk_3RZ6B5s,1511
|
|
184
187
|
autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py,sha256=Zid2m1uZd-2wVFGc_n_KAViXZyNjbdLSpI5n7ut1RUQ,1036
|
|
@@ -195,6 +198,7 @@ autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.
|
|
|
195
198
|
autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py,sha256=9eBo3WLkrr77iNotwIwVmH1ZL3UY0JQgLpdAIc9wTTM,6127
|
|
196
199
|
autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py,sha256=ZWRPsJny_My4UMzovrB8J2_x5N0rEW-xx3DVI-kDRFI,15870
|
|
197
200
|
autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py,sha256=wM2Xy4bcnD0TSLEmcM8rvvyyWenN5_KQnJMO6hJ8lTE,1716
|
|
201
|
+
autocoder/common/v2/agent/agentic_edit_tools/use_rag_tool_resolver.py,sha256=frVkXws__fDO2kXETi4NI7eKR_lz2i0C26MPsoYftp8,3901
|
|
198
202
|
autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=lPESPLoe9P_bEXXNqqHAkVlqMrIH0XziJ6XeILIejUo,8084
|
|
199
203
|
autocoder/compilers/__init__.py,sha256=C0HOms70QA747XD0uZEMmGtRFcIPenohyqECNStv0Bw,1647
|
|
200
204
|
autocoder/compilers/base_compiler.py,sha256=dsTzMO4H_RoqWfE-SntIk2B52hWuvSlWVLtkdCbHgGs,3244
|
|
@@ -341,9 +345,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
341
345
|
autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
342
346
|
autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=t902pKxQ5xM7zgIHiAOsTPLwxhE6VuvXAqPy751S7fg,14096
|
|
343
347
|
autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
344
|
-
auto_coder-0.1.
|
|
345
|
-
auto_coder-0.1.
|
|
346
|
-
auto_coder-0.1.
|
|
347
|
-
auto_coder-0.1.
|
|
348
|
-
auto_coder-0.1.
|
|
349
|
-
auto_coder-0.1.
|
|
348
|
+
auto_coder-0.1.393.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
349
|
+
auto_coder-0.1.393.dist-info/METADATA,sha256=sfXUErhySszB2h43fhNGRUPJSjbmDlMOrSqzlvw63pw,2796
|
|
350
|
+
auto_coder-0.1.393.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
351
|
+
auto_coder-0.1.393.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
|
|
352
|
+
auto_coder-0.1.393.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
|
|
353
|
+
auto_coder-0.1.393.dist-info/RECORD,,
|
|
@@ -21,15 +21,7 @@ class ExecuteCommandToolResolver(BaseToolResolver):
|
|
|
21
21
|
def resolve(self) -> ToolResult:
|
|
22
22
|
command = self.tool.command
|
|
23
23
|
requires_approval = self.tool.requires_approval
|
|
24
|
-
source_dir = self.args.source_dir or "."
|
|
25
|
-
|
|
26
|
-
# Basic security check (can be expanded)
|
|
27
|
-
if ";" in command or "&&" in command or "|" in command or "`" in command:
|
|
28
|
-
# Allow && for cd chaining, but be cautious
|
|
29
|
-
if not command.strip().startswith("cd ") and " && " in command:
|
|
30
|
-
pass # Allow cd chaining like 'cd subdir && command'
|
|
31
|
-
else:
|
|
32
|
-
return ToolResult(success=False, message=f"Command '{command}' contains potentially unsafe characters.")
|
|
24
|
+
source_dir = self.args.source_dir or "."
|
|
33
25
|
|
|
34
26
|
# Approval mechanism (simplified)
|
|
35
27
|
if requires_approval:
|
autocoder/auto_coder_rag.py
CHANGED
|
@@ -35,6 +35,7 @@ import pkg_resources
|
|
|
35
35
|
from autocoder.rag.token_counter import TokenCounter
|
|
36
36
|
from autocoder.rag.types import RAGServiceInfo
|
|
37
37
|
from autocoder.version import __version__
|
|
38
|
+
from autocoder.rags import get_rag_config
|
|
38
39
|
|
|
39
40
|
if platform.system() == "Windows":
|
|
40
41
|
from colorama import init
|
|
@@ -169,6 +170,40 @@ def initialize_system(args):
|
|
|
169
170
|
print_status(get_message("init_complete_final"), "success")
|
|
170
171
|
|
|
171
172
|
|
|
173
|
+
def merge_args_with_config(args, config, arg_class, parser):
|
|
174
|
+
"""
|
|
175
|
+
合并命令行参数和配置文件参数,优先级如下:
|
|
176
|
+
1. 命令行参数非默认值,以命令行为准
|
|
177
|
+
2. 命令行参数为默认值,且配置文件有值,以配置文件为准
|
|
178
|
+
3. 否则用命令行参数
|
|
179
|
+
"""
|
|
180
|
+
merged = {}
|
|
181
|
+
for arg in vars(arg_class()):
|
|
182
|
+
# 获取默认值
|
|
183
|
+
try:
|
|
184
|
+
default = parser.get_default(arg)
|
|
185
|
+
except Exception:
|
|
186
|
+
default = None
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if not hasattr(args,arg) and arg not in config:
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
cli_value = getattr(args, arg, None)
|
|
193
|
+
config_value = config.get(arg, None)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# 判断优先级
|
|
197
|
+
if cli_value != default:
|
|
198
|
+
merged[arg] = cli_value
|
|
199
|
+
elif config_value is not None:
|
|
200
|
+
merged[arg] = config_value
|
|
201
|
+
else:
|
|
202
|
+
merged[arg] = cli_value
|
|
203
|
+
|
|
204
|
+
return arg_class(**merged)
|
|
205
|
+
|
|
206
|
+
|
|
172
207
|
def main(input_args: Optional[List[str]] = None):
|
|
173
208
|
print(
|
|
174
209
|
f"""
|
|
@@ -297,6 +332,7 @@ def main(input_args: Optional[List[str]] = None):
|
|
|
297
332
|
serve_parser.add_argument("--source_dir", default=".", help="")
|
|
298
333
|
serve_parser.add_argument("--host", default="", help="")
|
|
299
334
|
serve_parser.add_argument("--port", type=int, default=8000, help="")
|
|
335
|
+
serve_parser.add_argument("--name", default="", help="RAG服务的名称(可选)")
|
|
300
336
|
serve_parser.add_argument("--workers", type=int, default=4, help="")
|
|
301
337
|
serve_parser.add_argument("--uvicorn_log_level", default="info", help="")
|
|
302
338
|
serve_parser.add_argument("--allow_credentials", action="store_true", help="")
|
|
@@ -565,29 +601,40 @@ def main(input_args: Optional[List[str]] = None):
|
|
|
565
601
|
benchmark_byzerllm(args.model, args.parallel, args.rounds, args.query)
|
|
566
602
|
|
|
567
603
|
elif args.command == "serve":
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
604
|
+
# 如果用户传递了 --name 参数,则加载已保存的配置并与命令行参数合并
|
|
605
|
+
server_args_config = {}
|
|
606
|
+
auto_coder_args_config = {}
|
|
607
|
+
if args.name:
|
|
608
|
+
saved_config = get_rag_config(args.name)
|
|
609
|
+
if saved_config:
|
|
610
|
+
logger.info(f"加载已保存的RAG配置: {args.name}")
|
|
611
|
+
# 将保存的配置合并到 args 中(命令行参数优先)
|
|
612
|
+
for key, value in saved_config.items():
|
|
613
|
+
# 跳过一些不应该被合并的字段
|
|
614
|
+
skip_fields = {'name', 'status', 'created_at', 'updated_at', 'process_id', 'stdout_fd', 'stderr_fd', 'cache_build_task_id'}
|
|
615
|
+
if key in skip_fields:
|
|
616
|
+
continue
|
|
617
|
+
server_args_config[key] = value
|
|
618
|
+
# 特殊处理 infer_params 字段
|
|
619
|
+
if 'infer_params' in saved_config and saved_config['infer_params']:
|
|
620
|
+
for infer_key, infer_value in saved_config['infer_params'].items():
|
|
621
|
+
auto_coder_args_config[infer_key] = infer_value
|
|
622
|
+
logger.info(f"配置合并完成,使用文档目录: {getattr(args, 'doc_dir', 'N/A')}")
|
|
623
|
+
else:
|
|
624
|
+
logger.warning(f"未找到名为 '{args.name}' 的RAG配置")
|
|
625
|
+
|
|
626
|
+
# Handle lite/pro flags
|
|
627
|
+
if args.pro:
|
|
572
628
|
args.product_mode = "pro"
|
|
573
|
-
|
|
629
|
+
else:
|
|
630
|
+
args.product_mode = "lite"
|
|
631
|
+
|
|
574
632
|
if not args.quick:
|
|
575
633
|
initialize_system(args)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
for arg in vars(ServerArgs())
|
|
581
|
-
if hasattr(args, arg)
|
|
582
|
-
}
|
|
583
|
-
)
|
|
584
|
-
auto_coder_args = AutoCoderArgs(
|
|
585
|
-
**{
|
|
586
|
-
arg: getattr(args, arg)
|
|
587
|
-
for arg in vars(AutoCoderArgs())
|
|
588
|
-
if hasattr(args, arg)
|
|
589
|
-
}
|
|
590
|
-
)
|
|
634
|
+
|
|
635
|
+
# 使用新的合并逻辑
|
|
636
|
+
server_args = merge_args_with_config(args, server_args_config, ServerArgs, serve_parser)
|
|
637
|
+
auto_coder_args = merge_args_with_config(args, server_args_config, AutoCoderArgs, serve_parser)
|
|
591
638
|
# 设置本地图床的地址
|
|
592
639
|
if args.enable_local_image_host:
|
|
593
640
|
host = server_args.host or "127.0.0.1"
|
|
@@ -178,6 +178,41 @@ class LLMFriendlyPackageManager:
|
|
|
178
178
|
|
|
179
179
|
return docs
|
|
180
180
|
|
|
181
|
+
def get_package_path(self, package_name: str) -> Optional[str]:
|
|
182
|
+
"""
|
|
183
|
+
Get the full path of a package by its name
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
package_name: Package name (can be just lib_name or username/lib_name)
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Full path to the package directory, or None if not found
|
|
190
|
+
"""
|
|
191
|
+
if not os.path.exists(self.llm_friendly_packages_dir):
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
for domain in os.listdir(self.llm_friendly_packages_dir):
|
|
195
|
+
domain_path = os.path.join(self.llm_friendly_packages_dir, domain)
|
|
196
|
+
if not os.path.isdir(domain_path):
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
for username in os.listdir(domain_path):
|
|
200
|
+
username_path = os.path.join(domain_path, username)
|
|
201
|
+
if not os.path.isdir(username_path):
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
for lib_name in os.listdir(username_path):
|
|
205
|
+
lib_path = os.path.join(username_path, lib_name)
|
|
206
|
+
if not os.path.isdir(lib_path):
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
# Check if this matches the requested package
|
|
210
|
+
if (lib_name == package_name or
|
|
211
|
+
package_name == os.path.join(username, lib_name)):
|
|
212
|
+
return lib_path
|
|
213
|
+
|
|
214
|
+
return None
|
|
215
|
+
|
|
181
216
|
def list_added_libraries(self) -> List[str]:
|
|
182
217
|
"""List all added libraries"""
|
|
183
218
|
memory = self._load_memory()
|
|
@@ -405,6 +440,4 @@ class LLMFriendlyPackageManager:
|
|
|
405
440
|
table.add_column("File Path", style="cyan")
|
|
406
441
|
|
|
407
442
|
for doc in docs:
|
|
408
|
-
table.add_row(doc)
|
|
409
|
-
|
|
410
|
-
self.console.print(table)
|
|
443
|
+
table.add_row(doc)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from typing import List, Dict, Any, Optional
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from autocoder.common import AutoCoderArgs
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RAGConfig(BaseModel):
|
|
10
|
+
"""RAG 配置项模型"""
|
|
11
|
+
name: str
|
|
12
|
+
server_name: str # 实际的服务器地址,如 http://127.0.0.1:8107/v1
|
|
13
|
+
api_key: Optional[str] = None
|
|
14
|
+
description: Optional[str] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RAGManager:
|
|
18
|
+
"""RAG 管理器,用于读取和管理 RAG 服务器配置"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, args: AutoCoderArgs):
|
|
21
|
+
self.args = args
|
|
22
|
+
self.configs: List[RAGConfig] = []
|
|
23
|
+
self._load_configs()
|
|
24
|
+
|
|
25
|
+
def _load_configs(self):
|
|
26
|
+
"""加载 RAG 配置,优先从项目配置,然后从全局配置"""
|
|
27
|
+
# 优先读取项目级别配置
|
|
28
|
+
project_config_path = os.path.join(
|
|
29
|
+
self.args.source_dir,
|
|
30
|
+
".auto-coder",
|
|
31
|
+
"auto-coder.web",
|
|
32
|
+
"rags",
|
|
33
|
+
"rags.json"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if os.path.exists(project_config_path):
|
|
37
|
+
logger.info(f"正在加载项目级别 RAG 配置: {project_config_path}")
|
|
38
|
+
self._load_project_config(project_config_path)
|
|
39
|
+
else:
|
|
40
|
+
logger.info("未找到项目级别 RAG 配置,尝试加载全局配置")
|
|
41
|
+
# 读取全局配置
|
|
42
|
+
global_config_path = os.path.expanduser("~/.auto-coder/keys/rags.json")
|
|
43
|
+
if os.path.exists(global_config_path):
|
|
44
|
+
logger.info(f"正在加载全局 RAG 配置: {global_config_path}")
|
|
45
|
+
self._load_global_config(global_config_path)
|
|
46
|
+
else:
|
|
47
|
+
logger.warning("未找到任何 RAG 配置文件")
|
|
48
|
+
|
|
49
|
+
def _load_project_config(self, config_path: str):
|
|
50
|
+
"""加载项目级别的 RAG 配置"""
|
|
51
|
+
try:
|
|
52
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
53
|
+
config_data = json.load(f)
|
|
54
|
+
|
|
55
|
+
if "data" in config_data and isinstance(config_data["data"], list):
|
|
56
|
+
for item in config_data["data"]:
|
|
57
|
+
try:
|
|
58
|
+
rag_config = RAGConfig(
|
|
59
|
+
name=item.get("name", ""),
|
|
60
|
+
server_name=item.get("base_url", ""),
|
|
61
|
+
api_key=item.get("api_key"),
|
|
62
|
+
description=item.get("description")
|
|
63
|
+
)
|
|
64
|
+
self.configs.append(rag_config)
|
|
65
|
+
logger.info(f"已加载 RAG 配置: {rag_config.name} -> {rag_config.server_name}")
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"解析项目级别 RAG 配置项时出错: {e}, 配置项: {item}")
|
|
68
|
+
else:
|
|
69
|
+
logger.error(f"项目级别 RAG 配置格式错误,缺少 'data' 字段或 'data' 不是列表")
|
|
70
|
+
|
|
71
|
+
except json.JSONDecodeError as e:
|
|
72
|
+
logger.error(f"项目级别 RAG 配置文件 JSON 格式错误: {e}")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"读取项目级别 RAG 配置文件时出错: {e}")
|
|
75
|
+
|
|
76
|
+
def _load_global_config(self, config_path: str):
|
|
77
|
+
"""加载全局级别的 RAG 配置"""
|
|
78
|
+
try:
|
|
79
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
80
|
+
config_data = json.load(f)
|
|
81
|
+
|
|
82
|
+
for key, item in config_data.items():
|
|
83
|
+
try:
|
|
84
|
+
# 构造 server_name: http://host:port/v1
|
|
85
|
+
host = item.get("host", "127.0.0.1")
|
|
86
|
+
port = item.get("port", 8080)
|
|
87
|
+
server_name = f"http://{host}:{port}/v1"
|
|
88
|
+
|
|
89
|
+
rag_config = RAGConfig(
|
|
90
|
+
name=item.get("name", key),
|
|
91
|
+
server_name=server_name,
|
|
92
|
+
api_key=None, # 全局配置中没有 api_key
|
|
93
|
+
description=item.get("description", f"{key} RAG 服务")
|
|
94
|
+
)
|
|
95
|
+
self.configs.append(rag_config)
|
|
96
|
+
logger.info(f"已加载 RAG 配置: {rag_config.name} -> {rag_config.server_name}")
|
|
97
|
+
except Exception as e:
|
|
98
|
+
logger.error(f"解析全局 RAG 配置项时出错: {e}, 配置项: {item}")
|
|
99
|
+
|
|
100
|
+
except json.JSONDecodeError as e:
|
|
101
|
+
logger.error(f"全局 RAG 配置文件 JSON 格式错误: {e}")
|
|
102
|
+
except Exception as e:
|
|
103
|
+
logger.error(f"读取全局 RAG 配置文件时出错: {e}")
|
|
104
|
+
|
|
105
|
+
def get_all_configs(self) -> List[RAGConfig]:
|
|
106
|
+
"""获取所有 RAG 配置"""
|
|
107
|
+
return self.configs
|
|
108
|
+
|
|
109
|
+
def get_config_by_name(self, name: str) -> Optional[RAGConfig]:
|
|
110
|
+
"""根据名称获取特定的 RAG 配置"""
|
|
111
|
+
for config in self.configs:
|
|
112
|
+
if config.name == name:
|
|
113
|
+
return config
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
def get_server_names(self) -> List[str]:
|
|
117
|
+
"""获取所有服务器名称列表"""
|
|
118
|
+
return [config.server_name for config in self.configs]
|
|
119
|
+
|
|
120
|
+
def get_config_info(self) -> str:
|
|
121
|
+
"""获取格式化的配置信息,用于显示"""
|
|
122
|
+
if not self.configs:
|
|
123
|
+
return "未找到可用的 RAG 服务器配置"
|
|
124
|
+
|
|
125
|
+
info_lines = []
|
|
126
|
+
info_lines.append("可用的 RAG 服务器配置")
|
|
127
|
+
|
|
128
|
+
for i, config in enumerate(self.configs, 1):
|
|
129
|
+
info_lines.append(f"\n{i}. 配置名称: {config.name}")
|
|
130
|
+
info_lines.append(f" 服务器地址: {config.server_name}")
|
|
131
|
+
|
|
132
|
+
if config.description:
|
|
133
|
+
info_lines.append(f" 描述信息: {config.description}")
|
|
134
|
+
else:
|
|
135
|
+
info_lines.append(f" 描述信息: 无")
|
|
136
|
+
|
|
137
|
+
if i < len(self.configs):
|
|
138
|
+
info_lines.append("-" * 30)
|
|
139
|
+
|
|
140
|
+
return "\n".join(info_lines)
|
|
141
|
+
|
|
142
|
+
def has_configs(self) -> bool:
|
|
143
|
+
"""检查是否有可用的配置"""
|
|
144
|
+
return len(self.configs) > 0
|
|
@@ -10,6 +10,10 @@ from rich.panel import Panel
|
|
|
10
10
|
from pydantic import SkipValidation
|
|
11
11
|
from byzerllm.utils.types import SingleOutputMeta
|
|
12
12
|
|
|
13
|
+
from byzerllm.utils import (
|
|
14
|
+
format_str_jinja2
|
|
15
|
+
)
|
|
16
|
+
|
|
13
17
|
from autocoder.common import AutoCoderArgs, git_utils, SourceCodeList, SourceCode
|
|
14
18
|
from autocoder.common.global_cancel import global_cancel
|
|
15
19
|
from autocoder.common import detect_env
|
|
@@ -56,7 +60,7 @@ from autocoder.common.v2.agent.agentic_edit_tools import ( # Import specific re
|
|
|
56
60
|
ReplaceInFileToolResolver, SearchFilesToolResolver, ListFilesToolResolver,
|
|
57
61
|
ListCodeDefinitionNamesToolResolver, AskFollowupQuestionToolResolver,
|
|
58
62
|
AttemptCompletionToolResolver, PlanModeRespondToolResolver, UseMcpToolResolver,
|
|
59
|
-
ListPackageInfoToolResolver
|
|
63
|
+
UseRAGToolResolver, ListPackageInfoToolResolver
|
|
60
64
|
)
|
|
61
65
|
from autocoder.common.llm_friendly_package import LLMFriendlyPackageManager
|
|
62
66
|
from autocoder.common.rulefiles.autocoderrules_utils import get_rules,auto_select_rules,get_required_and_index_rules
|
|
@@ -69,7 +73,7 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
|
|
|
69
73
|
ListFilesTool,
|
|
70
74
|
ListCodeDefinitionNamesTool, AskFollowupQuestionTool,
|
|
71
75
|
AttemptCompletionTool, PlanModeRespondTool, UseMcpTool,
|
|
72
|
-
ListPackageInfoTool,
|
|
76
|
+
UseRAGTool, ListPackageInfoTool,
|
|
73
77
|
TOOL_MODEL_MAP,
|
|
74
78
|
# Event Types
|
|
75
79
|
LLMOutputEvent, LLMThinkingEvent, ToolCallEvent,
|
|
@@ -80,7 +84,7 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
|
|
|
80
84
|
ListFilesTool, SearchFilesTool, ListCodeDefinitionNamesTool,
|
|
81
85
|
AskFollowupQuestionTool, UseMcpTool, AttemptCompletionTool
|
|
82
86
|
)
|
|
83
|
-
|
|
87
|
+
from autocoder.common.rag_manager import RAGManager
|
|
84
88
|
from autocoder.rag.token_counter import count_tokens
|
|
85
89
|
|
|
86
90
|
# Map Pydantic Tool Models to their Resolver Classes
|
|
@@ -96,7 +100,8 @@ TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
|
96
100
|
AskFollowupQuestionTool: AskFollowupQuestionToolResolver,
|
|
97
101
|
AttemptCompletionTool: AttemptCompletionToolResolver, # Will stop the loop anyway
|
|
98
102
|
PlanModeRespondTool: PlanModeRespondToolResolver,
|
|
99
|
-
UseMcpTool: UseMcpToolResolver
|
|
103
|
+
UseMcpTool: UseMcpToolResolver,
|
|
104
|
+
UseRAGTool: UseRAGToolResolver
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
|
|
@@ -161,12 +166,25 @@ class AgenticEdit:
|
|
|
161
166
|
except Exception as e:
|
|
162
167
|
logger.error(f"Error getting MCP server info: {str(e)}")
|
|
163
168
|
|
|
169
|
+
# 初始化 RAG 管理器并获取服务器信息
|
|
170
|
+
self.rag_server_info = ""
|
|
171
|
+
try:
|
|
172
|
+
self.rag_manager = RAGManager(args)
|
|
173
|
+
if self.rag_manager.has_configs():
|
|
174
|
+
self.rag_server_info = self.rag_manager.get_config_info()
|
|
175
|
+
logger.info(f"RAG manager initialized with {len(self.rag_manager.get_all_configs())} configurations")
|
|
176
|
+
else:
|
|
177
|
+
logger.info("No RAG configurations found")
|
|
178
|
+
except Exception as e:
|
|
179
|
+
logger.error(f"Error initializing RAG manager: {str(e)}")
|
|
180
|
+
self.rag_manager = None
|
|
181
|
+
|
|
164
182
|
# 变更跟踪信息
|
|
165
183
|
# 格式: { file_path: FileChangeEntry(...) }
|
|
166
184
|
self.file_changes: Dict[str, FileChangeEntry] = {}
|
|
167
185
|
|
|
168
186
|
@byzerllm.prompt()
|
|
169
|
-
def generate_library_docs_prompt(self,
|
|
187
|
+
def generate_library_docs_prompt(self, libraries_with_paths: List[Dict[str, str]], docs_content: str) -> Dict[str, Any]:
|
|
170
188
|
"""
|
|
171
189
|
====
|
|
172
190
|
|
|
@@ -187,8 +205,15 @@ class AgenticEdit:
|
|
|
187
205
|
4. You need to understand the API or usage patterns of these libraries
|
|
188
206
|
====
|
|
189
207
|
"""
|
|
208
|
+
# 格式化库列表,包含名称和路径
|
|
209
|
+
libraries_list = []
|
|
210
|
+
for lib_info in libraries_with_paths:
|
|
211
|
+
name = lib_info.get('name', '')
|
|
212
|
+
path = lib_info.get('path', 'Path not found')
|
|
213
|
+
libraries_list.append(f"{name} (路径: {path})")
|
|
214
|
+
|
|
190
215
|
return {
|
|
191
|
-
"libraries_list": ", ".join(
|
|
216
|
+
"libraries_list": ", ".join(libraries_list),
|
|
192
217
|
"combined_docs": docs_content
|
|
193
218
|
}
|
|
194
219
|
|
|
@@ -442,6 +467,22 @@ class AgenticEdit:
|
|
|
442
467
|
{{mcp_server_info}}
|
|
443
468
|
{%endif%}
|
|
444
469
|
|
|
470
|
+
## rag_tool
|
|
471
|
+
Description: Request to query the RAG server for information. Use this when you need to query the RAG server for information.
|
|
472
|
+
Parameters:
|
|
473
|
+
- server_name: (required) The url of the RAG server to use.
|
|
474
|
+
- query: (required) The query to pass to the tool.
|
|
475
|
+
Usage:
|
|
476
|
+
<use_rag_tool>
|
|
477
|
+
<server_name>xxx</server_name>
|
|
478
|
+
<query>Your query here</query>
|
|
479
|
+
</use_rag_tool>
|
|
480
|
+
|
|
481
|
+
{%if rag_server_info %}
|
|
482
|
+
### RAG_SERVER_LIST
|
|
483
|
+
{{rag_server_info}}
|
|
484
|
+
{%endif%}
|
|
485
|
+
|
|
445
486
|
# Tool Use Examples
|
|
446
487
|
|
|
447
488
|
## Example 1: Requesting to execute a command
|
|
@@ -688,6 +729,7 @@ class AgenticEdit:
|
|
|
688
729
|
- It is critical you wait for the user's response after each tool use, in order to confirm the success of the tool use. For example, if asked to make a todo app, you would create a file, wait for the user's response it was created successfully, then create another file if needed, wait for the user's response it was created successfully, etc.
|
|
689
730
|
- To display LaTeX formulas, use a single dollar sign to wrap inline formulas, like `$E=mc^2$`, and double dollar signs to wrap block-level formulas, like `$$\frac{d}{dx}e^x = e^x$$`.
|
|
690
731
|
- To include flowcharts or diagrams, you can use Mermaid syntax.
|
|
732
|
+
- If you come across some unknown or unfamiliar concepts or terms, or if the user is asking a question, you can try using appropriate MCP or RAG services to obtain the information.
|
|
691
733
|
|
|
692
734
|
|
|
693
735
|
{% if extra_docs %}
|
|
@@ -769,6 +811,7 @@ class AgenticEdit:
|
|
|
769
811
|
"home_dir": os.path.expanduser("~"),
|
|
770
812
|
"files": self.files.to_str(),
|
|
771
813
|
"mcp_server_info": self.mcp_server_info,
|
|
814
|
+
"rag_server_info": self.rag_server_info,
|
|
772
815
|
"enable_active_context_in_generate": self.args.enable_active_context_in_generate,
|
|
773
816
|
"extra_docs": extra_docs,
|
|
774
817
|
"file_paths_str": file_paths_str,
|
|
@@ -842,6 +885,15 @@ class AgenticEdit:
|
|
|
842
885
|
added_libraries = package_manager.list_added_libraries()
|
|
843
886
|
|
|
844
887
|
if added_libraries:
|
|
888
|
+
# Build libraries with paths information
|
|
889
|
+
libraries_with_paths = []
|
|
890
|
+
for lib_name in added_libraries:
|
|
891
|
+
lib_path = package_manager.get_package_path(lib_name)
|
|
892
|
+
libraries_with_paths.append({
|
|
893
|
+
'name': lib_name,
|
|
894
|
+
'path': lib_path if lib_path else 'Path not found'
|
|
895
|
+
})
|
|
896
|
+
|
|
845
897
|
# Get documentation content for all added libraries
|
|
846
898
|
docs_content = package_manager.get_docs(return_paths=False)
|
|
847
899
|
|
|
@@ -851,9 +903,9 @@ class AgenticEdit:
|
|
|
851
903
|
|
|
852
904
|
# Generate library documentation prompt using decorator
|
|
853
905
|
library_docs_prompt = self.generate_library_docs_prompt.prompt(
|
|
854
|
-
|
|
906
|
+
libraries_with_paths=libraries_with_paths,
|
|
855
907
|
docs_content=combined_docs
|
|
856
|
-
)
|
|
908
|
+
)
|
|
857
909
|
|
|
858
910
|
conversations.append({
|
|
859
911
|
"role": "user",
|
|
@@ -11,6 +11,7 @@ from .ask_followup_question_tool_resolver import AskFollowupQuestionToolResolver
|
|
|
11
11
|
from .attempt_completion_tool_resolver import AttemptCompletionToolResolver
|
|
12
12
|
from .plan_mode_respond_tool_resolver import PlanModeRespondToolResolver
|
|
13
13
|
from .use_mcp_tool_resolver import UseMcpToolResolver
|
|
14
|
+
from .use_rag_tool_resolver import UseRAGToolResolver
|
|
14
15
|
from .list_package_info_tool_resolver import ListPackageInfoToolResolver
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
@@ -26,5 +27,6 @@ __all__ = [
|
|
|
26
27
|
"AttemptCompletionToolResolver",
|
|
27
28
|
"PlanModeRespondToolResolver",
|
|
28
29
|
"UseMcpToolResolver",
|
|
30
|
+
"UseRAGToolResolver",
|
|
29
31
|
"ListPackageInfoToolResolver",
|
|
30
32
|
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
import typing
|
|
3
|
+
import openai
|
|
4
|
+
from autocoder.common import AutoCoderArgs
|
|
5
|
+
from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
|
|
6
|
+
from autocoder.common.v2.agent.agentic_edit_types import UseRAGTool, ToolResult
|
|
7
|
+
from autocoder.common.rag_manager import RAGManager
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from autocoder.common.v2.agent.agentic_edit import AgenticEdit
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UseRAGToolResolver(BaseToolResolver):
|
|
15
|
+
def __init__(self, agent: Optional['AgenticEdit'], tool: UseRAGTool, args: AutoCoderArgs):
|
|
16
|
+
super().__init__(agent, tool, args)
|
|
17
|
+
self.tool: UseRAGTool = tool # For type hinting
|
|
18
|
+
self.rag_manager = RAGManager(args)
|
|
19
|
+
|
|
20
|
+
def resolve(self) -> ToolResult:
|
|
21
|
+
"""
|
|
22
|
+
通过 OpenAI SDK 访问 RAG server 来执行查询。
|
|
23
|
+
"""
|
|
24
|
+
server_name = self.tool.server_name
|
|
25
|
+
query = self.tool.query
|
|
26
|
+
|
|
27
|
+
logger.info(f"正在解析 UseRAGTool: Server='{server_name}', Query='{query}'")
|
|
28
|
+
|
|
29
|
+
# 检查是否有可用的 RAG 配置
|
|
30
|
+
if not self.rag_manager.has_configs():
|
|
31
|
+
error_msg = "未找到可用的 RAG 服务器配置,请确保配置文件存在并格式正确"
|
|
32
|
+
logger.error(error_msg)
|
|
33
|
+
return ToolResult(success=False, message=error_msg)
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
# 查找指定的 RAG 配置
|
|
37
|
+
rag_config = None
|
|
38
|
+
if server_name:
|
|
39
|
+
# 尝试通过名称查找
|
|
40
|
+
rag_config = self.rag_manager.get_config_by_name(server_name)
|
|
41
|
+
# 如果按名称找不到,尝试直接使用 server_name 作为 URL
|
|
42
|
+
if not rag_config:
|
|
43
|
+
# 检查是否是直接的 URL
|
|
44
|
+
if server_name.startswith('http'):
|
|
45
|
+
# 使用提供的 server_name 作为 base_url
|
|
46
|
+
base_url = server_name
|
|
47
|
+
api_key = "dummy-key"
|
|
48
|
+
else:
|
|
49
|
+
error_msg = f"未找到名为 '{server_name}' 的 RAG 服务器配置\n\n{self.rag_manager.get_config_info()}"
|
|
50
|
+
logger.error(error_msg)
|
|
51
|
+
return ToolResult(success=False, message=error_msg)
|
|
52
|
+
else:
|
|
53
|
+
base_url = rag_config.server_name
|
|
54
|
+
api_key = rag_config.api_key or "dummy-key"
|
|
55
|
+
else:
|
|
56
|
+
# 如果没有指定 server_name,使用第一个可用的配置
|
|
57
|
+
rag_config = self.rag_manager.get_all_configs()[0]
|
|
58
|
+
base_url = rag_config.server_name
|
|
59
|
+
api_key = rag_config.api_key or "dummy-key"
|
|
60
|
+
logger.info(f"未指定服务器名称,使用默认配置: {rag_config.name}")
|
|
61
|
+
|
|
62
|
+
logger.info(f"使用 RAG 服务器: {base_url}")
|
|
63
|
+
|
|
64
|
+
# 使用 OpenAI SDK 访问 RAG server
|
|
65
|
+
client = openai.OpenAI(
|
|
66
|
+
base_url=base_url,
|
|
67
|
+
api_key=api_key
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
response = client.chat.completions.create(
|
|
71
|
+
model="default",
|
|
72
|
+
messages=[
|
|
73
|
+
{
|
|
74
|
+
"role": "user",
|
|
75
|
+
"content": query
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
max_tokens=8024
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
result_content = response.choices[0].message.content
|
|
82
|
+
logger.info(f"RAG server 响应成功,内容长度: {len(result_content)}")
|
|
83
|
+
|
|
84
|
+
return ToolResult(success=True, message=result_content)
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
error_msg = f"访问 RAG server 时出错: {str(e)}"
|
|
88
|
+
if not self.rag_manager.has_configs():
|
|
89
|
+
error_msg += f"\n\n{self.rag_manager.get_config_info()}"
|
|
90
|
+
logger.error(error_msg)
|
|
91
|
+
return ToolResult(success=False, message=error_msg)
|
|
@@ -56,6 +56,10 @@ class UseMcpTool(BaseTool):
|
|
|
56
56
|
tool_name: str
|
|
57
57
|
query:str
|
|
58
58
|
|
|
59
|
+
class UseRAGTool(BaseTool):
|
|
60
|
+
server_name: str
|
|
61
|
+
query: str
|
|
62
|
+
|
|
59
63
|
class ListPackageInfoTool(BaseTool):
|
|
60
64
|
path: str # 源码包目录,相对路径或绝对路径
|
|
61
65
|
|
|
@@ -118,6 +122,7 @@ TOOL_MODEL_MAP: Dict[str, Type[BaseTool]] = {
|
|
|
118
122
|
"attempt_completion": AttemptCompletionTool,
|
|
119
123
|
"plan_mode_respond": PlanModeRespondTool,
|
|
120
124
|
"use_mcp_tool": UseMcpTool,
|
|
125
|
+
"use_rag_tool": UseRAGTool,
|
|
121
126
|
"list_package_info": ListPackageInfoTool,
|
|
122
127
|
}
|
|
123
128
|
|
autocoder/rags.py
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import fcntl
|
|
4
|
+
import platform
|
|
5
|
+
import time
|
|
6
|
+
from typing import Dict, Optional, Any, List
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
RAGS_JSON = os.path.expanduser("~/.auto-coder/keys/rags.json")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RAGConfigManager:
|
|
16
|
+
"""RAG配置管理器,支持并发安全的增删改查操作"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, config_path: str = RAGS_JSON):
|
|
19
|
+
self.config_path = config_path
|
|
20
|
+
self._ensure_config_dir()
|
|
21
|
+
|
|
22
|
+
def _ensure_config_dir(self):
|
|
23
|
+
"""确保配置目录存在"""
|
|
24
|
+
config_dir = os.path.dirname(self.config_path)
|
|
25
|
+
if not os.path.exists(config_dir):
|
|
26
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
27
|
+
|
|
28
|
+
@contextmanager
|
|
29
|
+
def _file_lock(self, mode='r'):
|
|
30
|
+
"""
|
|
31
|
+
文件锁上下文管理器,确保并发安全
|
|
32
|
+
"""
|
|
33
|
+
# 确保文件存在
|
|
34
|
+
if not os.path.exists(self.config_path):
|
|
35
|
+
with open(self.config_path, 'w') as f:
|
|
36
|
+
json.dump({}, f)
|
|
37
|
+
|
|
38
|
+
file_handle = open(self.config_path, mode)
|
|
39
|
+
try:
|
|
40
|
+
if platform.system() != "Windows":
|
|
41
|
+
# Unix系统使用fcntl
|
|
42
|
+
if 'w' in mode or 'a' in mode or '+' in mode:
|
|
43
|
+
fcntl.flock(file_handle.fileno(), fcntl.LOCK_EX) # 独占锁
|
|
44
|
+
else:
|
|
45
|
+
fcntl.flock(file_handle.fileno(), fcntl.LOCK_SH) # 共享锁
|
|
46
|
+
else:
|
|
47
|
+
# Windows系统的文件锁实现
|
|
48
|
+
try:
|
|
49
|
+
import msvcrt
|
|
50
|
+
if 'w' in mode or 'a' in mode or '+' in mode:
|
|
51
|
+
msvcrt.locking(file_handle.fileno(), msvcrt.LK_LOCK, 1)
|
|
52
|
+
else:
|
|
53
|
+
msvcrt.locking(file_handle.fileno(), msvcrt.LK_LOCK, 1)
|
|
54
|
+
except ImportError:
|
|
55
|
+
# 如果msvcrt不可用,退化到无锁模式(不推荐)
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
yield file_handle
|
|
59
|
+
finally:
|
|
60
|
+
if platform.system() != "Windows":
|
|
61
|
+
fcntl.flock(file_handle.fileno(), fcntl.LOCK_UN) # 释放锁
|
|
62
|
+
else:
|
|
63
|
+
try:
|
|
64
|
+
import msvcrt
|
|
65
|
+
msvcrt.locking(file_handle.fileno(), msvcrt.LK_UNLCK, 1)
|
|
66
|
+
except ImportError:
|
|
67
|
+
pass
|
|
68
|
+
file_handle.close()
|
|
69
|
+
|
|
70
|
+
def _load_config(self) -> Dict[str, Any]:
|
|
71
|
+
"""
|
|
72
|
+
加载配置文件
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
with self._file_lock('r') as f:
|
|
76
|
+
content = f.read().strip()
|
|
77
|
+
if not content:
|
|
78
|
+
return {}
|
|
79
|
+
return json.loads(content)
|
|
80
|
+
except (FileNotFoundError, json.JSONDecodeError):
|
|
81
|
+
return {}
|
|
82
|
+
|
|
83
|
+
def _save_config(self, config: Dict[str, Any]):
|
|
84
|
+
"""
|
|
85
|
+
保存配置文件
|
|
86
|
+
"""
|
|
87
|
+
with self._file_lock('w') as f:
|
|
88
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
89
|
+
|
|
90
|
+
def create(self, name: str, config: Dict[str, Any]) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
创建新的RAG服务配置
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
name: RAG服务名称
|
|
96
|
+
config: 配置字典
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
bool: 创建成功返回True,如果名称已存在返回False
|
|
100
|
+
"""
|
|
101
|
+
all_configs = self._load_config()
|
|
102
|
+
|
|
103
|
+
if name in all_configs:
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
# 确保配置包含必要的字段
|
|
107
|
+
config['name'] = name
|
|
108
|
+
if 'status' not in config:
|
|
109
|
+
config['status'] = 'stopped'
|
|
110
|
+
if 'created_at' not in config:
|
|
111
|
+
config['created_at'] = datetime.now().isoformat()
|
|
112
|
+
config['updated_at'] = datetime.now().isoformat()
|
|
113
|
+
|
|
114
|
+
all_configs[name] = config
|
|
115
|
+
self._save_config(all_configs)
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
def read(self, name: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
|
119
|
+
"""
|
|
120
|
+
读取RAG服务配置
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
name: RAG服务名称,如果为None则返回所有配置
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Dict: 配置字典,如果name不存在或为None且无配置则返回None
|
|
127
|
+
"""
|
|
128
|
+
all_configs = self._load_config()
|
|
129
|
+
|
|
130
|
+
if name is None:
|
|
131
|
+
return all_configs if all_configs else None
|
|
132
|
+
|
|
133
|
+
return all_configs.get(name)
|
|
134
|
+
|
|
135
|
+
def update(self, name: str, config: Dict[str, Any]) -> bool:
|
|
136
|
+
"""
|
|
137
|
+
更新RAG服务配置
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
name: RAG服务名称
|
|
141
|
+
config: 新的配置字典(会与现有配置合并)
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
bool: 更新成功返回True,如果名称不存在返回False
|
|
145
|
+
"""
|
|
146
|
+
all_configs = self._load_config()
|
|
147
|
+
|
|
148
|
+
if name not in all_configs:
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
# 合并配置
|
|
152
|
+
existing_config = all_configs[name]
|
|
153
|
+
existing_config.update(config)
|
|
154
|
+
existing_config['name'] = name # 确保名称不被覆盖
|
|
155
|
+
existing_config['updated_at'] = datetime.now().isoformat()
|
|
156
|
+
|
|
157
|
+
all_configs[name] = existing_config
|
|
158
|
+
self._save_config(all_configs)
|
|
159
|
+
return True
|
|
160
|
+
|
|
161
|
+
def delete(self, name: str) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
删除RAG服务配置
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
name: RAG服务名称
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
bool: 删除成功返回True,如果名称不存在返回False
|
|
170
|
+
"""
|
|
171
|
+
all_configs = self._load_config()
|
|
172
|
+
|
|
173
|
+
if name not in all_configs:
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
del all_configs[name]
|
|
177
|
+
self._save_config(all_configs)
|
|
178
|
+
return True
|
|
179
|
+
|
|
180
|
+
def list_names(self) -> List[str]:
|
|
181
|
+
"""
|
|
182
|
+
获取所有RAG服务名称列表
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
List[str]: 服务名称列表
|
|
186
|
+
"""
|
|
187
|
+
all_configs = self._load_config()
|
|
188
|
+
return list(all_configs.keys())
|
|
189
|
+
|
|
190
|
+
def exists(self, name: str) -> bool:
|
|
191
|
+
"""
|
|
192
|
+
检查RAG服务是否存在
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
name: RAG服务名称
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
bool: 存在返回True,否则返回False
|
|
199
|
+
"""
|
|
200
|
+
all_configs = self._load_config()
|
|
201
|
+
return name in all_configs
|
|
202
|
+
|
|
203
|
+
def get_by_port(self, port: int) -> Optional[Dict[str, Any]]:
|
|
204
|
+
"""
|
|
205
|
+
根据端口号查找RAG服务配置
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
port: 端口号
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Dict: 配置字典,如果未找到返回None
|
|
212
|
+
"""
|
|
213
|
+
all_configs = self._load_config()
|
|
214
|
+
|
|
215
|
+
for name, config in all_configs.items():
|
|
216
|
+
if config.get('port') == port:
|
|
217
|
+
return config
|
|
218
|
+
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
def get_running_services(self) -> Dict[str, Any]:
|
|
222
|
+
"""
|
|
223
|
+
获取所有运行中的RAG服务
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Dict: 运行中的服务配置字典
|
|
227
|
+
"""
|
|
228
|
+
all_configs = self._load_config()
|
|
229
|
+
running_services = {}
|
|
230
|
+
|
|
231
|
+
for name, config in all_configs.items():
|
|
232
|
+
if config.get('status') == 'running':
|
|
233
|
+
running_services[name] = config
|
|
234
|
+
|
|
235
|
+
return running_services
|
|
236
|
+
|
|
237
|
+
def update_status(self, name: str, status: str, **kwargs) -> bool:
|
|
238
|
+
"""
|
|
239
|
+
更新RAG服务状态
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
name: RAG服务名称
|
|
243
|
+
status: 新状态 ('running', 'stopped', 'error', etc.)
|
|
244
|
+
**kwargs: 其他要更新的状态信息(如process_id, stdout_fd等)
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
bool: 更新成功返回True,如果名称不存在返回False
|
|
248
|
+
"""
|
|
249
|
+
update_data = {'status': status, **kwargs}
|
|
250
|
+
return self.update(name, update_data)
|
|
251
|
+
|
|
252
|
+
def cleanup_stopped_services(self):
|
|
253
|
+
"""
|
|
254
|
+
清理已停止服务的临时状态信息(如process_id, stdout_fd等)
|
|
255
|
+
"""
|
|
256
|
+
all_configs = self._load_config()
|
|
257
|
+
modified = False
|
|
258
|
+
|
|
259
|
+
for name, config in all_configs.items():
|
|
260
|
+
if config.get('status') == 'stopped':
|
|
261
|
+
# 清理临时状态字段
|
|
262
|
+
temp_fields = ['process_id', 'stdout_fd', 'stderr_fd', 'cache_build_task_id']
|
|
263
|
+
for field in temp_fields:
|
|
264
|
+
if field in config:
|
|
265
|
+
del config[field]
|
|
266
|
+
modified = True
|
|
267
|
+
|
|
268
|
+
if modified:
|
|
269
|
+
self._save_config(all_configs)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# 创建全局实例
|
|
273
|
+
rag_manager = RAGConfigManager()
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# 便捷函数
|
|
277
|
+
def create_rag_config(name: str, config: Dict[str, Any]) -> bool:
|
|
278
|
+
"""创建RAG配置的便捷函数"""
|
|
279
|
+
return rag_manager.create(name, config)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_rag_config(name: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
|
283
|
+
"""获取RAG配置的便捷函数"""
|
|
284
|
+
return rag_manager.read(name)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def update_rag_config(name: str, config: Dict[str, Any]) -> bool:
|
|
288
|
+
"""更新RAG配置的便捷函数"""
|
|
289
|
+
return rag_manager.update(name, config)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def delete_rag_config(name: str) -> bool:
|
|
293
|
+
"""删除RAG配置的便捷函数"""
|
|
294
|
+
return rag_manager.delete(name)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def list_rag_names() -> List[str]:
|
|
298
|
+
"""获取所有RAG服务名称的便捷函数"""
|
|
299
|
+
return rag_manager.list_names()
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def rag_exists(name: str) -> bool:
|
|
303
|
+
"""检查RAG服务是否存在的便捷函数"""
|
|
304
|
+
return rag_manager.exists(name)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def get_rag_by_port(port: int) -> Optional[Dict[str, Any]]:
|
|
308
|
+
"""根据端口获取RAG配置的便捷函数"""
|
|
309
|
+
return rag_manager.get_by_port(port)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def get_running_rags() -> Dict[str, Any]:
|
|
313
|
+
"""获取运行中RAG服务的便捷函数"""
|
|
314
|
+
return rag_manager.get_running_services()
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def update_rag_status(name: str, status: str, **kwargs) -> bool:
|
|
318
|
+
"""更新RAG服务状态的便捷函数"""
|
|
319
|
+
return rag_manager.update_status(name, status, **kwargs)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# 使用示例
|
|
323
|
+
if __name__ == "__main__":
|
|
324
|
+
# 创建配置
|
|
325
|
+
config = {
|
|
326
|
+
"model": "deepseek_chat",
|
|
327
|
+
"tokenizer_path": "/Users/allwefantasy/Downloads/tokenizer.json",
|
|
328
|
+
"doc_dir": "/path/to/docs",
|
|
329
|
+
"rag_doc_filter_relevance": 2.0,
|
|
330
|
+
"host": "0.0.0.0",
|
|
331
|
+
"port": 8000,
|
|
332
|
+
"enable_hybrid_index": False,
|
|
333
|
+
"required_exts": ".md,.rst",
|
|
334
|
+
"disable_inference_enhance": True
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
# 创建
|
|
338
|
+
create_rag_config("test_rag", config)
|
|
339
|
+
|
|
340
|
+
# 读取
|
|
341
|
+
all_configs = get_rag_config()
|
|
342
|
+
specific_config = get_rag_config("test_rag")
|
|
343
|
+
|
|
344
|
+
# 更新
|
|
345
|
+
update_rag_config("test_rag", {"status": "running", "port": 8001})
|
|
346
|
+
|
|
347
|
+
# 删除
|
|
348
|
+
delete_rag_config("test_rag")
|
autocoder/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.393"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|