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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.392
3
+ Version: 0.1.393
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Programming Language :: Python :: 3.10
@@ -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=9PeW7m1bdZx9Q4yw6ULeBKuJQ2EZyO2MepTofl9RxA4,41376
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=ZTsw_XcFB4V267Rc6_tT3BTknX9GxJaf4Wj3R2S4z0U,24
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=UL7eQz7Im4BhpglIxUDmEBZ9xwM_hhk1J9iY228uvPU,3691
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=j44Wqco8-5sA2Yz05ZEidA1HwTbVGnbevbALCoasTtM,14760
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=f9GxvaOvgirxyPCEiGxFi7u8X4ZB3do72PGldxCzpAI,115291
179
- autocoder/common/v2/agent/agentic_edit_types.py,sha256=nEcZc2MOZ_fQLaJX-YDha_x9Iim22ao4tykYM2iIy4k,4908
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=RbPZZcZg_VnGssL577GxSyFrYrxQ_LopJ4G_-mY3z_Q,1337
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.392.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
345
- auto_coder-0.1.392.dist-info/METADATA,sha256=CI2kK7M_kEPPdkrzIufAAYs5PaKCS4AM4FhrzMDePcY,2796
346
- auto_coder-0.1.392.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
347
- auto_coder-0.1.392.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
348
- auto_coder-0.1.392.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
349
- auto_coder-0.1.392.dist-info/RECORD,,
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:
@@ -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
- # Handle lite/pro flags
569
- if args.lite:
570
- args.product_mode = "lite"
571
- elif args.pro:
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
- server_args = ServerArgs(
578
- **{
579
- arg: getattr(args, arg)
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,4 @@
1
+ # flake8: noqa
2
+ from .rag_manager import RAGManager
3
+
4
+ __all__ = ["RAGManager"]
@@ -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, libraries: List[str], docs_content: str) -> Dict[str, Any]:
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(libraries),
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
- libraries=added_libraries,
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.392"
1
+ __version__ = "0.1.393"