auto-coder 0.1.374__py3-none-any.whl → 0.1.376__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.374.dist-info → auto_coder-0.1.376.dist-info}/METADATA +2 -2
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/RECORD +27 -57
- autocoder/agent/base_agentic/base_agent.py +202 -52
- autocoder/agent/base_agentic/default_tools.py +38 -6
- autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +83 -43
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +88 -25
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +171 -62
- autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +101 -56
- autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +5 -0
- autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +5 -0
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +145 -32
- autocoder/auto_coder_rag.py +80 -11
- autocoder/models.py +2 -2
- autocoder/rag/agentic_rag.py +217 -0
- autocoder/rag/cache/local_duckdb_storage_cache.py +63 -33
- autocoder/rag/conversation_to_queries.py +37 -5
- autocoder/rag/long_context_rag.py +161 -41
- autocoder/rag/tools/__init__.py +10 -0
- autocoder/rag/tools/recall_tool.py +163 -0
- autocoder/rag/tools/search_tool.py +126 -0
- autocoder/rag/types.py +36 -0
- autocoder/utils/_markitdown.py +59 -13
- autocoder/version.py +1 -1
- autocoder/agent/agentic_edit.py +0 -833
- autocoder/agent/agentic_edit_tools/__init__.py +0 -28
- autocoder/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +0 -32
- autocoder/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +0 -29
- autocoder/agent/agentic_edit_tools/base_tool_resolver.py +0 -29
- autocoder/agent/agentic_edit_tools/execute_command_tool_resolver.py +0 -84
- autocoder/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +0 -75
- autocoder/agent/agentic_edit_tools/list_files_tool_resolver.py +0 -62
- autocoder/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +0 -30
- autocoder/agent/agentic_edit_tools/read_file_tool_resolver.py +0 -36
- autocoder/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +0 -95
- autocoder/agent/agentic_edit_tools/search_files_tool_resolver.py +0 -70
- autocoder/agent/agentic_edit_tools/use_mcp_tool_resolver.py +0 -55
- autocoder/agent/agentic_edit_tools/write_to_file_tool_resolver.py +0 -98
- autocoder/agent/agentic_edit_types.py +0 -124
- autocoder/auto_coder_lang.py +0 -60
- autocoder/auto_coder_rag_client_mcp.py +0 -170
- autocoder/auto_coder_rag_mcp.py +0 -193
- autocoder/common/llm_rerank.py +0 -84
- autocoder/common/model_speed_test.py +0 -392
- autocoder/common/v2/agent/agentic_edit_conversation.py +0 -188
- autocoder/common/v2/agent/ignore_utils.py +0 -50
- autocoder/dispacher/actions/plugins/action_translate.py +0 -214
- autocoder/ignorefiles/__init__.py +0 -4
- autocoder/ignorefiles/ignore_file_utils.py +0 -63
- autocoder/ignorefiles/test_ignore_file_utils.py +0 -91
- autocoder/linters/code_linter.py +0 -588
- autocoder/rag/loaders/test_image_loader.py +0 -209
- autocoder/rag/raw_rag.py +0 -96
- autocoder/rag/simple_directory_reader.py +0 -646
- autocoder/rag/simple_rag.py +0 -404
- autocoder/regex_project/__init__.py +0 -162
- autocoder/utils/coder.py +0 -125
- autocoder/utils/tests.py +0 -37
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.374.dist-info → auto_coder-0.1.376.dist-info}/top_level.txt +0 -0
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import re
|
|
3
|
-
from typing import Dict, Any, Optional, List, Tuple
|
|
4
|
-
from .base_tool_resolver import BaseToolResolver
|
|
5
|
-
from autocoder.agent.agentic_edit_types import WriteToFileTool, ToolResult # Import ToolResult from types
|
|
6
|
-
from loguru import logger
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class WriteToFileToolResolver(BaseToolResolver):
|
|
10
|
-
def __init__(self, agent: Optional[Any], tool: WriteToFileTool, args: Dict[str, Any]):
|
|
11
|
-
super().__init__(agent, tool, args)
|
|
12
|
-
self.tool: WriteToFileTool = tool # For type hinting
|
|
13
|
-
|
|
14
|
-
def parse_diff(self, diff_content: str) -> List[Tuple[str, str]]:
|
|
15
|
-
"""
|
|
16
|
-
Parses the diff content into a list of (search_block, replace_block) tuples.
|
|
17
|
-
"""
|
|
18
|
-
blocks = []
|
|
19
|
-
# Regex to find SEARCH/REPLACE blocks, handling potential variations in line endings
|
|
20
|
-
pattern = re.compile(r"<<<<<<< SEARCH\r?\n(.*?)\r?\n=======\r?\n(.*?)\r?\n>>>>>>> REPLACE", re.DOTALL)
|
|
21
|
-
matches = pattern.findall(diff_content)
|
|
22
|
-
for search_block, replace_block in matches:
|
|
23
|
-
blocks.append((search_block, replace_block))
|
|
24
|
-
if not matches and diff_content.strip():
|
|
25
|
-
logger.warning(f"Could not parse any SEARCH/REPLACE blocks from diff: {diff_content}")
|
|
26
|
-
return blocks
|
|
27
|
-
|
|
28
|
-
def resolve(self) -> ToolResult:
|
|
29
|
-
file_path = self.tool.path
|
|
30
|
-
content = self.tool.content
|
|
31
|
-
source_dir = self.args.source_dir or "."
|
|
32
|
-
absolute_path = os.path.abspath(os.path.join(source_dir, file_path))
|
|
33
|
-
|
|
34
|
-
# Security check: ensure the path is within the source directory
|
|
35
|
-
if not absolute_path.startswith(os.path.abspath(source_dir)):
|
|
36
|
-
return ToolResult(success=False, message=f"Error: Access denied. Attempted to write file outside the project directory: {file_path}")
|
|
37
|
-
|
|
38
|
-
try:
|
|
39
|
-
# Create directories if they don't exist
|
|
40
|
-
os.makedirs(os.path.dirname(absolute_path), exist_ok=True)
|
|
41
|
-
|
|
42
|
-
# Check if the content contains SEARCH/REPLACE blocks
|
|
43
|
-
parsed_blocks = self.parse_diff(content)
|
|
44
|
-
if parsed_blocks:
|
|
45
|
-
# If file exists, read its current content
|
|
46
|
-
if os.path.exists(absolute_path):
|
|
47
|
-
try:
|
|
48
|
-
with open(absolute_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
49
|
-
original_content = f.read()
|
|
50
|
-
except Exception as e:
|
|
51
|
-
logger.error(f"Error reading existing file '{file_path}' for diff apply: {str(e)}")
|
|
52
|
-
return ToolResult(success=False, message=f"An error occurred while reading the existing file: {str(e)}")
|
|
53
|
-
else:
|
|
54
|
-
# If file does not exist, start with empty content
|
|
55
|
-
original_content = ""
|
|
56
|
-
|
|
57
|
-
current_content = original_content
|
|
58
|
-
applied_count = 0
|
|
59
|
-
errors = []
|
|
60
|
-
|
|
61
|
-
for i, (search_block, replace_block) in enumerate(parsed_blocks):
|
|
62
|
-
start_index = current_content.find(search_block)
|
|
63
|
-
if start_index != -1:
|
|
64
|
-
current_content = (
|
|
65
|
-
current_content[:start_index]
|
|
66
|
-
+ replace_block
|
|
67
|
-
+ current_content[start_index + len(search_block):]
|
|
68
|
-
)
|
|
69
|
-
applied_count += 1
|
|
70
|
-
logger.info(f"Applied SEARCH/REPLACE block {i+1} in file {file_path}")
|
|
71
|
-
else:
|
|
72
|
-
error_message = f"SEARCH block {i+1} not found in current content. Search block:\n---\n{search_block}\n---"
|
|
73
|
-
logger.warning(error_message)
|
|
74
|
-
errors.append(error_message)
|
|
75
|
-
# Continue with next block
|
|
76
|
-
|
|
77
|
-
try:
|
|
78
|
-
with open(absolute_path, 'w', encoding='utf-8') as f:
|
|
79
|
-
f.write(current_content)
|
|
80
|
-
message = f"Successfully applied {applied_count}/{len(parsed_blocks)} changes to file: {file_path}."
|
|
81
|
-
if errors:
|
|
82
|
-
message += "\nWarnings:\n" + "\n".join(errors)
|
|
83
|
-
logger.info(message)
|
|
84
|
-
return ToolResult(success=True, message=message, content=current_content)
|
|
85
|
-
except Exception as e:
|
|
86
|
-
logger.error(f"Error writing replaced content to file '{file_path}': {str(e)}")
|
|
87
|
-
return ToolResult(success=False, message=f"An error occurred while writing the modified file: {str(e)}")
|
|
88
|
-
else:
|
|
89
|
-
# No diff blocks detected, treat as full content overwrite
|
|
90
|
-
with open(absolute_path, 'w', encoding='utf-8') as f:
|
|
91
|
-
f.write(content)
|
|
92
|
-
|
|
93
|
-
logger.info(f"Successfully wrote to file: {file_path}")
|
|
94
|
-
return ToolResult(success=True, message=f"Successfully wrote to file: {file_path}", content=content)
|
|
95
|
-
|
|
96
|
-
except Exception as e:
|
|
97
|
-
logger.error(f"Error writing to file '{file_path}': {str(e)}")
|
|
98
|
-
return ToolResult(success=False, message=f"An error occurred while writing to the file: {str(e)}")
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
from pydantic import BaseModel
|
|
2
|
-
from typing import List, Dict, Any, Callable, Optional, Type
|
|
3
|
-
from pydantic import SkipValidation
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Result class used by Tool Resolvers
|
|
7
|
-
class ToolResult(BaseModel):
|
|
8
|
-
success: bool
|
|
9
|
-
message: str
|
|
10
|
-
content: Any = None # Can store file content, command output, etc.
|
|
11
|
-
|
|
12
|
-
# Pydantic Models for Tools
|
|
13
|
-
class BaseTool(BaseModel):
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
class ExecuteCommandTool(BaseTool):
|
|
17
|
-
command: str
|
|
18
|
-
requires_approval: bool
|
|
19
|
-
|
|
20
|
-
class ReadFileTool(BaseTool):
|
|
21
|
-
path: str
|
|
22
|
-
|
|
23
|
-
class WriteToFileTool(BaseTool):
|
|
24
|
-
path: str
|
|
25
|
-
content: str
|
|
26
|
-
|
|
27
|
-
class ReplaceInFileTool(BaseTool):
|
|
28
|
-
path: str
|
|
29
|
-
diff: str
|
|
30
|
-
|
|
31
|
-
class SearchFilesTool(BaseTool):
|
|
32
|
-
path: str
|
|
33
|
-
regex: str
|
|
34
|
-
file_pattern: Optional[str] = None
|
|
35
|
-
|
|
36
|
-
class ListFilesTool(BaseTool):
|
|
37
|
-
path: str
|
|
38
|
-
recursive: Optional[bool] = False
|
|
39
|
-
|
|
40
|
-
class ListCodeDefinitionNamesTool(BaseTool):
|
|
41
|
-
path: str
|
|
42
|
-
|
|
43
|
-
class AskFollowupQuestionTool(BaseTool):
|
|
44
|
-
question: str
|
|
45
|
-
options: Optional[List[str]] = None
|
|
46
|
-
|
|
47
|
-
class AttemptCompletionTool(BaseTool):
|
|
48
|
-
result: str
|
|
49
|
-
command: Optional[str] = None
|
|
50
|
-
|
|
51
|
-
class PlanModeRespondTool(BaseTool):
|
|
52
|
-
response: str
|
|
53
|
-
options: Optional[List[str]] = None
|
|
54
|
-
|
|
55
|
-
class UseMcpTool(BaseTool):
|
|
56
|
-
server_name: str
|
|
57
|
-
tool_name: str
|
|
58
|
-
arguments: Dict[str, Any]
|
|
59
|
-
|
|
60
|
-
class PlainTextOutput(BaseModel):
|
|
61
|
-
text: str
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# Mapping from tool tag names to Pydantic models
|
|
65
|
-
TOOL_MODEL_MAP: Dict[str, Type[BaseTool]] = {
|
|
66
|
-
"execute_command": ExecuteCommandTool,
|
|
67
|
-
"read_file": ReadFileTool,
|
|
68
|
-
"write_to_file": WriteToFileTool,
|
|
69
|
-
"replace_in_file": ReplaceInFileTool,
|
|
70
|
-
"search_files": SearchFilesTool,
|
|
71
|
-
"list_files": ListFilesTool,
|
|
72
|
-
"list_code_definition_names": ListCodeDefinitionNamesTool,
|
|
73
|
-
"ask_followup_question": AskFollowupQuestionTool,
|
|
74
|
-
"attempt_completion": AttemptCompletionTool,
|
|
75
|
-
"plan_mode_respond": PlanModeRespondTool,
|
|
76
|
-
"use_mcp_tool": UseMcpTool,
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class AgenticEditRequest(BaseModel):
|
|
81
|
-
user_input: str
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class FileOperation(BaseModel):
|
|
85
|
-
path: str
|
|
86
|
-
operation: str # e.g., "MODIFY", "REFERENCE", "ADD", "REMOVE"
|
|
87
|
-
class MemoryConfig(BaseModel):
|
|
88
|
-
"""
|
|
89
|
-
A model to encapsulate memory configuration and operations.
|
|
90
|
-
"""
|
|
91
|
-
|
|
92
|
-
memory: Dict[str, Any]
|
|
93
|
-
save_memory_func: SkipValidation[Callable]
|
|
94
|
-
|
|
95
|
-
class Config:
|
|
96
|
-
arbitrary_types_allowed = True
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class CommandConfig(BaseModel):
|
|
100
|
-
coding: SkipValidation[Callable]
|
|
101
|
-
chat: SkipValidation[Callable]
|
|
102
|
-
add_files: SkipValidation[Callable]
|
|
103
|
-
remove_files: SkipValidation[Callable]
|
|
104
|
-
index_build: SkipValidation[Callable]
|
|
105
|
-
index_query: SkipValidation[Callable]
|
|
106
|
-
list_files: SkipValidation[Callable]
|
|
107
|
-
ask: SkipValidation[Callable]
|
|
108
|
-
revert: SkipValidation[Callable]
|
|
109
|
-
commit: SkipValidation[Callable]
|
|
110
|
-
help: SkipValidation[Callable]
|
|
111
|
-
exclude_dirs: SkipValidation[Callable]
|
|
112
|
-
summon: SkipValidation[Callable]
|
|
113
|
-
design: SkipValidation[Callable]
|
|
114
|
-
mcp: SkipValidation[Callable]
|
|
115
|
-
models: SkipValidation[Callable]
|
|
116
|
-
lib: SkipValidation[Callable]
|
|
117
|
-
execute_shell_command: SkipValidation[Callable]
|
|
118
|
-
generate_shell_command: SkipValidation[Callable]
|
|
119
|
-
conf_export: SkipValidation[Callable]
|
|
120
|
-
conf_import: SkipValidation[Callable]
|
|
121
|
-
index_export: SkipValidation[Callable]
|
|
122
|
-
index_import: SkipValidation[Callable]
|
|
123
|
-
exclude_files: SkipValidation[Callable]
|
|
124
|
-
|
autocoder/auto_coder_lang.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import locale
|
|
2
|
-
|
|
3
|
-
MESSAGES = {
|
|
4
|
-
"en": {
|
|
5
|
-
"human_as_model_instructions": (
|
|
6
|
-
"You are now in Human as Model mode. The content has been copied to your clipboard.\n"
|
|
7
|
-
"The system is waiting for your input. When finished, enter 'EOF' on a new line to submit.\n"
|
|
8
|
-
"Use '/break' to exit this mode. If you have issues with copy-paste, use '/clear' to clean and paste again."
|
|
9
|
-
),
|
|
10
|
-
"clipboard_not_supported": (
|
|
11
|
-
"pyperclip not installed or clipboard is not supported, instruction will not be copied to clipboard."
|
|
12
|
-
),
|
|
13
|
-
"human_as_model_instructions_no_clipboard": (
|
|
14
|
-
"You are now in Human as Model mode. [bold red]The content could not be copied to your clipboard.[/bold red]\n"
|
|
15
|
-
"but you can copy prompt from output.txt file.\n"
|
|
16
|
-
"The system is waiting for your input. When finished, enter 'EOF' on a new line to submit.\n"
|
|
17
|
-
"Use '/break' to exit this mode. If you have issues with copy-paste, use '/clear' to clean and paste again."
|
|
18
|
-
),
|
|
19
|
-
"chat_human_as_model_instructions": (
|
|
20
|
-
"Chat is now in Human as Model mode.\n"
|
|
21
|
-
"The question has been copied to your clipboard.\n"
|
|
22
|
-
"Please use Web version model to get the answer.\n"
|
|
23
|
-
"Or use /conf human_as_model:false to close this mode and get the answer in terminal directly."
|
|
24
|
-
"Paste the answer to the input box below, use '/break' to exit, '/clear' to clear the screen, '/eof' to submit."
|
|
25
|
-
)
|
|
26
|
-
},
|
|
27
|
-
"zh": {
|
|
28
|
-
"human_as_model_instructions": (
|
|
29
|
-
"您现在处于人类作为模型模式。内容已复制到您的剪贴板。\n"
|
|
30
|
-
"系统正在等待您的输入。完成后,在新行输入'EOF'提交。\n"
|
|
31
|
-
"使用'/break'退出此模式。如果复制粘贴有问题,使用'/clear'清理并重新粘贴。"
|
|
32
|
-
),
|
|
33
|
-
"clipboard_not_supported": (
|
|
34
|
-
"未安装pyperclip或不支持剪贴板,指令将不会被复制到剪贴板。"
|
|
35
|
-
),
|
|
36
|
-
"human_as_model_instructions_no_clipboard": (
|
|
37
|
-
"您现在处于人类作为模型模式。[bold red]内容无法复制到您的剪贴板。[/bold red]\n"
|
|
38
|
-
"但您可以从output.txt文件复制提示。\n"
|
|
39
|
-
"系统正在等待您的输入。完成后,在新行输入'EOF'提交。\n"
|
|
40
|
-
"使用'/break'退出此模式。如果复制粘贴有问题,使用'/clear'清理并重新粘贴。"
|
|
41
|
-
),
|
|
42
|
-
"chat_human_as_model_instructions": (
|
|
43
|
-
"\n============= Chat 处于 Human as Model 模式 =============\n"
|
|
44
|
-
"问题已复制到剪贴板\n"
|
|
45
|
-
"请使用Web版本模型获取答案\n"
|
|
46
|
-
"或者使用 /conf human_as_model:false 关闭该模式直接在终端获得答案。"
|
|
47
|
-
"将获得答案黏贴到下面的输入框,换行后,使用 '/break' 退出,'/clear' 清屏,'/eof' 提交。"
|
|
48
|
-
),
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
def get_system_language():
|
|
53
|
-
try:
|
|
54
|
-
return locale.getdefaultlocale()[0][:2]
|
|
55
|
-
except:
|
|
56
|
-
return 'en'
|
|
57
|
-
|
|
58
|
-
def get_message(key):
|
|
59
|
-
lang = get_system_language()
|
|
60
|
-
return MESSAGES.get(lang, MESSAGES['en']).get(key, MESSAGES['en'][key])
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
from typing import Any, List, Dict, Generator, Optional
|
|
2
|
-
import asyncio
|
|
3
|
-
import httpx
|
|
4
|
-
import argparse
|
|
5
|
-
from mcp.server.models import InitializationOptions
|
|
6
|
-
import mcp.types as types
|
|
7
|
-
from mcp.server import NotificationOptions, Server
|
|
8
|
-
import mcp.server.stdio
|
|
9
|
-
from autocoder.common import AutoCoderArgs
|
|
10
|
-
from byzerllm import ByzerLLM
|
|
11
|
-
from autocoder.lang import lang_desc
|
|
12
|
-
import locale
|
|
13
|
-
import pkg_resources
|
|
14
|
-
from openai import OpenAI
|
|
15
|
-
|
|
16
|
-
class AutoCoderRAGClientMCP:
|
|
17
|
-
def __init__(self, llm: ByzerLLM, args: AutoCoderArgs):
|
|
18
|
-
self.llm = llm
|
|
19
|
-
self.args = args
|
|
20
|
-
|
|
21
|
-
if not args.rag_url:
|
|
22
|
-
raise ValueError("rag_url is required for RAG client mode")
|
|
23
|
-
|
|
24
|
-
if not args.rag_url.startswith("http://"):
|
|
25
|
-
args.rag_url = f"http://{args.rag_url}"
|
|
26
|
-
|
|
27
|
-
if not args.rag_url.endswith("/v1"):
|
|
28
|
-
args.rag_url = args.rag_url.rstrip("/") + "/v1"
|
|
29
|
-
|
|
30
|
-
if not args.rag_token:
|
|
31
|
-
raise ValueError("rag_token is required for RAG client mode")
|
|
32
|
-
|
|
33
|
-
self.client = OpenAI(api_key=args.rag_token, base_url=args.rag_url)
|
|
34
|
-
|
|
35
|
-
self.server = Server("auto_coder_rag_client")
|
|
36
|
-
|
|
37
|
-
async def setup_server(self):
|
|
38
|
-
@self.server.list_tools()
|
|
39
|
-
async def handle_list_tools() -> List[types.Tool]:
|
|
40
|
-
return [
|
|
41
|
-
types.Tool(
|
|
42
|
-
name="rag-search",
|
|
43
|
-
description="Search documents using RAG",
|
|
44
|
-
inputSchema={
|
|
45
|
-
"type": "object",
|
|
46
|
-
"properties": {
|
|
47
|
-
"query": {
|
|
48
|
-
"type": "string",
|
|
49
|
-
"description": "Search query",
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
"required": ["query"],
|
|
53
|
-
},
|
|
54
|
-
),
|
|
55
|
-
types.Tool(
|
|
56
|
-
name="rag-chat",
|
|
57
|
-
description="Chat with documents using RAG",
|
|
58
|
-
inputSchema={
|
|
59
|
-
"type": "object",
|
|
60
|
-
"properties": {
|
|
61
|
-
"query": {
|
|
62
|
-
"type": "string",
|
|
63
|
-
"description": "Chat query",
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
"required": ["query"],
|
|
67
|
-
},
|
|
68
|
-
),
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
@self.server.call_tool()
|
|
72
|
-
async def handle_call_tool(
|
|
73
|
-
name: str, arguments: Dict[str, Any] | None
|
|
74
|
-
) -> List[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
|
75
|
-
if not arguments:
|
|
76
|
-
raise ValueError("Missing arguments")
|
|
77
|
-
|
|
78
|
-
if name == "rag-search":
|
|
79
|
-
query = arguments.get("query")
|
|
80
|
-
if not query:
|
|
81
|
-
raise ValueError("Missing query parameter")
|
|
82
|
-
|
|
83
|
-
response = self.client.chat.completions.create(
|
|
84
|
-
messages=[{"role": "user", "content": json.dumps({
|
|
85
|
-
"query": query,
|
|
86
|
-
"only_contexts": True
|
|
87
|
-
})}],
|
|
88
|
-
model=self.args.model,
|
|
89
|
-
max_tokens=self.args.rag_params_max_tokens,
|
|
90
|
-
)
|
|
91
|
-
result = response.choices[0].message.content
|
|
92
|
-
|
|
93
|
-
return [
|
|
94
|
-
types.TextContent(
|
|
95
|
-
type="text",
|
|
96
|
-
text=f"Search results for '{query}':\n\n{result}"
|
|
97
|
-
)
|
|
98
|
-
]
|
|
99
|
-
|
|
100
|
-
elif name == "rag-chat":
|
|
101
|
-
query = arguments.get("query")
|
|
102
|
-
if not query:
|
|
103
|
-
raise ValueError("Missing query parameter")
|
|
104
|
-
|
|
105
|
-
response = self.client.chat.completions.create(
|
|
106
|
-
messages=[{"role": "user", "content": query}],
|
|
107
|
-
model=self.args.model,
|
|
108
|
-
stream=True,
|
|
109
|
-
max_tokens=self.args.rag_params_max_tokens
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
full_response = ""
|
|
113
|
-
for chunk in response:
|
|
114
|
-
if chunk.choices[0].delta.content is not None:
|
|
115
|
-
full_response += chunk.choices[0].delta.content
|
|
116
|
-
|
|
117
|
-
return [
|
|
118
|
-
types.TextContent(
|
|
119
|
-
type="text",
|
|
120
|
-
text=f"Chat response for '{query}':\n\n{full_response}"
|
|
121
|
-
)
|
|
122
|
-
]
|
|
123
|
-
|
|
124
|
-
else:
|
|
125
|
-
raise ValueError(f"Unknown tool: {name}")
|
|
126
|
-
|
|
127
|
-
async def run(self):
|
|
128
|
-
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
|
129
|
-
await self.server.run(
|
|
130
|
-
read_stream,
|
|
131
|
-
write_stream,
|
|
132
|
-
InitializationOptions(
|
|
133
|
-
server_name="auto_coder_rag_client",
|
|
134
|
-
server_version="0.1.0",
|
|
135
|
-
capabilities=self.server.get_capabilities(
|
|
136
|
-
notification_options=NotificationOptions(),
|
|
137
|
-
experimental_capabilities={},
|
|
138
|
-
),
|
|
139
|
-
),
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
def parse_args(input_args: Optional[List[str]] = None) -> AutoCoderArgs:
|
|
143
|
-
system_lang, _ = locale.getdefaultlocale()
|
|
144
|
-
lang = "zh" if system_lang and system_lang.startswith("zh") else "en"
|
|
145
|
-
desc = lang_desc[lang]
|
|
146
|
-
|
|
147
|
-
parser = argparse.ArgumentParser(description="Auto Coder RAG Client MCP Server")
|
|
148
|
-
parser.add_argument("--rag_url", required=True, help="RAG server URL")
|
|
149
|
-
parser.add_argument("--rag_token", required=True, help="RAG server token")
|
|
150
|
-
parser.add_argument("--model", default="v3_chat", help=desc["model"])
|
|
151
|
-
parser.add_argument("--rag_params_max_tokens", type=int, default=4096, help="Max tokens for RAG response")
|
|
152
|
-
|
|
153
|
-
args = parser.parse_args(input_args)
|
|
154
|
-
return AutoCoderArgs(**vars(args))
|
|
155
|
-
|
|
156
|
-
async def main():
|
|
157
|
-
# Parse command line arguments
|
|
158
|
-
args = parse_args()
|
|
159
|
-
|
|
160
|
-
# Initialize LLM
|
|
161
|
-
llm = ByzerLLM()
|
|
162
|
-
llm.setup_default_model_name(args.model)
|
|
163
|
-
|
|
164
|
-
# Initialize and run server
|
|
165
|
-
server = AutoCoderRAGClientMCP(llm=llm, args=args)
|
|
166
|
-
await server.setup_server()
|
|
167
|
-
await server.run()
|
|
168
|
-
|
|
169
|
-
if __name__ == "__main__":
|
|
170
|
-
asyncio.run(main())
|
autocoder/auto_coder_rag_mcp.py
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
from typing import Any, List, Dict, Generator, Optional
|
|
2
|
-
import asyncio
|
|
3
|
-
import httpx
|
|
4
|
-
import argparse
|
|
5
|
-
from mcp.server.models import InitializationOptions
|
|
6
|
-
import mcp.types as types
|
|
7
|
-
from mcp.server import NotificationOptions, Server
|
|
8
|
-
import mcp.server.stdio
|
|
9
|
-
from autocoder.rag.long_context_rag import LongContextRAG
|
|
10
|
-
from autocoder.common import AutoCoderArgs
|
|
11
|
-
from byzerllm import ByzerLLM
|
|
12
|
-
from autocoder.lang import lang_desc
|
|
13
|
-
import locale
|
|
14
|
-
import pkg_resources
|
|
15
|
-
|
|
16
|
-
class AutoCoderRAGMCP:
|
|
17
|
-
def __init__(self, llm: ByzerLLM, args: AutoCoderArgs):
|
|
18
|
-
self.llm = llm
|
|
19
|
-
self.args = args
|
|
20
|
-
self.rag = LongContextRAG(
|
|
21
|
-
llm=llm,
|
|
22
|
-
args=args,
|
|
23
|
-
path=args.source_dir,
|
|
24
|
-
tokenizer_path=args.tokenizer_path
|
|
25
|
-
)
|
|
26
|
-
self.server = Server("auto_coder_rag")
|
|
27
|
-
|
|
28
|
-
async def setup_server(self):
|
|
29
|
-
@self.server.list_tools()
|
|
30
|
-
async def handle_list_tools() -> List[types.Tool]:
|
|
31
|
-
return [
|
|
32
|
-
types.Tool(
|
|
33
|
-
name="rag-search",
|
|
34
|
-
description="Search documents using RAG",
|
|
35
|
-
inputSchema={
|
|
36
|
-
"type": "object",
|
|
37
|
-
"properties": {
|
|
38
|
-
"query": {
|
|
39
|
-
"type": "string",
|
|
40
|
-
"description": "Search query",
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
"required": ["query"],
|
|
44
|
-
},
|
|
45
|
-
),
|
|
46
|
-
types.Tool(
|
|
47
|
-
name="rag-chat",
|
|
48
|
-
description="Chat with documents using RAG",
|
|
49
|
-
inputSchema={
|
|
50
|
-
"type": "object",
|
|
51
|
-
"properties": {
|
|
52
|
-
"query": {
|
|
53
|
-
"type": "string",
|
|
54
|
-
"description": "Chat query",
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
"required": ["query"],
|
|
58
|
-
},
|
|
59
|
-
),
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
@self.server.call_tool()
|
|
63
|
-
async def handle_call_tool(
|
|
64
|
-
name: str, arguments: Dict[str, Any] | None
|
|
65
|
-
) -> List[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
|
66
|
-
if not arguments:
|
|
67
|
-
raise ValueError("Missing arguments")
|
|
68
|
-
|
|
69
|
-
if name == "rag-search":
|
|
70
|
-
query = arguments.get("query")
|
|
71
|
-
if not query:
|
|
72
|
-
raise ValueError("Missing query parameter")
|
|
73
|
-
|
|
74
|
-
results = self.rag.search(query)
|
|
75
|
-
return [
|
|
76
|
-
types.TextContent(
|
|
77
|
-
type="text",
|
|
78
|
-
text=f"Search results for '{query}':\n\n" +
|
|
79
|
-
"\n".join([f"- {result.module_name}: {result.source_code[:200]}..."
|
|
80
|
-
for result in results])
|
|
81
|
-
)
|
|
82
|
-
]
|
|
83
|
-
|
|
84
|
-
elif name == "rag-chat":
|
|
85
|
-
query = arguments.get("query")
|
|
86
|
-
if not query:
|
|
87
|
-
raise ValueError("Missing query parameter")
|
|
88
|
-
|
|
89
|
-
response, _ = self.rag.stream_chat_oai(
|
|
90
|
-
conversations=[{"role": "user", "content": query}]
|
|
91
|
-
)
|
|
92
|
-
full_response = "".join([chunk for chunk in response])
|
|
93
|
-
|
|
94
|
-
return [
|
|
95
|
-
types.TextContent(
|
|
96
|
-
type="text",
|
|
97
|
-
text=f"Chat response for '{query}':\n\n{full_response}"
|
|
98
|
-
)
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
else:
|
|
102
|
-
raise ValueError(f"Unknown tool: {name}")
|
|
103
|
-
|
|
104
|
-
async def run(self):
|
|
105
|
-
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
|
106
|
-
await self.server.run(
|
|
107
|
-
read_stream,
|
|
108
|
-
write_stream,
|
|
109
|
-
InitializationOptions(
|
|
110
|
-
server_name="auto_coder_rag",
|
|
111
|
-
server_version="0.1.0",
|
|
112
|
-
capabilities=self.server.get_capabilities(
|
|
113
|
-
notification_options=NotificationOptions(),
|
|
114
|
-
experimental_capabilities={},
|
|
115
|
-
),
|
|
116
|
-
),
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
def parse_args(input_args: Optional[List[str]] = None) -> AutoCoderArgs:
|
|
120
|
-
try:
|
|
121
|
-
tokenizer_path = pkg_resources.resource_filename(
|
|
122
|
-
"autocoder", "data/tokenizer.json"
|
|
123
|
-
)
|
|
124
|
-
except FileNotFoundError:
|
|
125
|
-
tokenizer_path = None
|
|
126
|
-
|
|
127
|
-
system_lang, _ = locale.getdefaultlocale()
|
|
128
|
-
lang = "zh" if system_lang and system_lang.startswith("zh") else "en"
|
|
129
|
-
desc = lang_desc[lang]
|
|
130
|
-
|
|
131
|
-
parser = argparse.ArgumentParser(description="Auto Coder RAG MCP Server")
|
|
132
|
-
parser.add_argument("--source_dir", default=".", help="Source directory path")
|
|
133
|
-
parser.add_argument("--tokenizer_path", default=tokenizer_path, help="Path to tokenizer file")
|
|
134
|
-
parser.add_argument("--model", default="deepseek_chat", help=desc["model"])
|
|
135
|
-
parser.add_argument("--index_model", default="", help=desc["index_model"])
|
|
136
|
-
parser.add_argument("--emb_model", default="", help=desc["emb_model"])
|
|
137
|
-
parser.add_argument("--ray_address", default="auto", help=desc["ray_address"])
|
|
138
|
-
parser.add_argument("--required_exts", default="", help=desc["doc_build_parse_required_exts"])
|
|
139
|
-
parser.add_argument("--rag_doc_filter_relevance", type=int, default=5, help="Relevance score threshold for document filtering")
|
|
140
|
-
parser.add_argument("--rag_context_window_limit", type=int, default=56000, help="Context window limit for RAG")
|
|
141
|
-
parser.add_argument("--full_text_ratio", type=float, default=0.7, help="Ratio of full text area in context window")
|
|
142
|
-
parser.add_argument("--segment_ratio", type=float, default=0.2, help="Ratio of segment area in context window")
|
|
143
|
-
parser.add_argument("--index_filter_workers", type=int, default=5, help="Number of workers for document filtering")
|
|
144
|
-
parser.add_argument("--index_filter_file_num", type=int, default=3, help="Maximum number of files to filter")
|
|
145
|
-
parser.add_argument("--host", default="", help="Server host address")
|
|
146
|
-
parser.add_argument("--port", type=int, default=8000, help="Server port")
|
|
147
|
-
parser.add_argument("--monitor_mode", action="store_true", help="Enable document monitoring mode")
|
|
148
|
-
parser.add_argument("--enable_hybrid_index", action="store_true", help="Enable hybrid index")
|
|
149
|
-
parser.add_argument("--disable_auto_window", action="store_true", help="Disable automatic window adaptation")
|
|
150
|
-
parser.add_argument("--disable_segment_reorder", action="store_true", help="Disable segment reordering")
|
|
151
|
-
parser.add_argument("--disable_inference_enhance", action="store_true", help="Disable inference enhancement")
|
|
152
|
-
parser.add_argument("--inference_deep_thought", action="store_true", help="Enable deep thought in inference")
|
|
153
|
-
parser.add_argument("--inference_slow_without_deep_thought", action="store_true", help="Enable slow inference without deep thought")
|
|
154
|
-
parser.add_argument("--inference_compute_precision", type=int, default=64, help="Inference compute precision")
|
|
155
|
-
parser.add_argument("--data_cells_max_num", type=int, default=2000, help="Maximum number of data cells to process")
|
|
156
|
-
parser.add_argument("--recall_model", default="", help="Model used for document recall")
|
|
157
|
-
parser.add_argument("--chunk_model", default="", help="Model used for document chunking")
|
|
158
|
-
parser.add_argument("--qa_model", default="", help="Model used for question answering")
|
|
159
|
-
|
|
160
|
-
args = parser.parse_args(input_args)
|
|
161
|
-
return AutoCoderArgs(**vars(args)),args
|
|
162
|
-
|
|
163
|
-
async def main():
|
|
164
|
-
# Parse command line arguments
|
|
165
|
-
args,raw_rags = parse_args()
|
|
166
|
-
|
|
167
|
-
# Initialize LLM
|
|
168
|
-
llm = ByzerLLM()
|
|
169
|
-
llm.setup_default_model_name(args.model)
|
|
170
|
-
|
|
171
|
-
# Setup sub models if specified
|
|
172
|
-
if raw_rags.recall_model:
|
|
173
|
-
recall_model = ByzerLLM()
|
|
174
|
-
recall_model.setup_default_model_name(args.recall_model)
|
|
175
|
-
llm.setup_sub_client("recall_model", recall_model)
|
|
176
|
-
|
|
177
|
-
if raw_rags.chunk_model:
|
|
178
|
-
chunk_model = ByzerLLM()
|
|
179
|
-
chunk_model.setup_default_model_name(args.chunk_model)
|
|
180
|
-
llm.setup_sub_client("chunk_model", chunk_model)
|
|
181
|
-
|
|
182
|
-
if raw_rags.qa_model:
|
|
183
|
-
qa_model = ByzerLLM()
|
|
184
|
-
qa_model.setup_default_model_name(args.qa_model)
|
|
185
|
-
llm.setup_sub_client("qa_model", qa_model)
|
|
186
|
-
|
|
187
|
-
# Initialize and run server
|
|
188
|
-
server = AutoCoderRAGMCP(llm=llm, args=args)
|
|
189
|
-
await server.setup_server()
|
|
190
|
-
await server.run()
|
|
191
|
-
|
|
192
|
-
if __name__ == "__main__":
|
|
193
|
-
asyncio.run(main())
|