jarvis-ai-assistant 0.1.134__py3-none-any.whl → 0.1.138__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 jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +201 -79
- jarvis/jarvis_agent/builtin_input_handler.py +16 -6
- jarvis/jarvis_agent/file_input_handler.py +9 -9
- jarvis/jarvis_agent/jarvis.py +10 -10
- jarvis/jarvis_agent/main.py +12 -11
- jarvis/jarvis_agent/output_handler.py +3 -3
- jarvis/jarvis_agent/patch.py +86 -62
- jarvis/jarvis_agent/shell_input_handler.py +5 -3
- jarvis/jarvis_code_agent/code_agent.py +134 -99
- jarvis/jarvis_code_agent/file_select.py +24 -24
- jarvis/jarvis_dev/main.py +45 -51
- jarvis/jarvis_git_details/__init__.py +0 -0
- jarvis/jarvis_git_details/main.py +179 -0
- jarvis/jarvis_git_squash/main.py +7 -7
- jarvis/jarvis_lsp/base.py +11 -11
- jarvis/jarvis_lsp/cpp.py +14 -14
- jarvis/jarvis_lsp/go.py +13 -13
- jarvis/jarvis_lsp/python.py +8 -8
- jarvis/jarvis_lsp/registry.py +21 -21
- jarvis/jarvis_lsp/rust.py +15 -15
- jarvis/jarvis_methodology/main.py +101 -0
- jarvis/jarvis_multi_agent/__init__.py +11 -11
- jarvis/jarvis_multi_agent/main.py +6 -6
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/ai8.py +67 -89
- jarvis/jarvis_platform/base.py +14 -13
- jarvis/jarvis_platform/kimi.py +25 -28
- jarvis/jarvis_platform/ollama.py +24 -26
- jarvis/jarvis_platform/openai.py +15 -19
- jarvis/jarvis_platform/oyi.py +48 -50
- jarvis/jarvis_platform/registry.py +27 -28
- jarvis/jarvis_platform/yuanbao.py +38 -42
- jarvis/jarvis_platform_manager/main.py +81 -81
- jarvis/jarvis_platform_manager/openai_test.py +21 -21
- jarvis/jarvis_rag/file_processors.py +18 -18
- jarvis/jarvis_rag/main.py +261 -277
- jarvis/jarvis_smart_shell/main.py +12 -12
- jarvis/jarvis_tools/ask_codebase.py +28 -28
- jarvis/jarvis_tools/ask_user.py +8 -8
- jarvis/jarvis_tools/base.py +4 -4
- jarvis/jarvis_tools/chdir.py +9 -9
- jarvis/jarvis_tools/code_review.py +19 -19
- jarvis/jarvis_tools/create_code_agent.py +15 -15
- jarvis/jarvis_tools/execute_python_script.py +3 -3
- jarvis/jarvis_tools/execute_shell.py +11 -11
- jarvis/jarvis_tools/execute_shell_script.py +3 -3
- jarvis/jarvis_tools/file_analyzer.py +29 -29
- jarvis/jarvis_tools/file_operation.py +22 -20
- jarvis/jarvis_tools/find_caller.py +25 -25
- jarvis/jarvis_tools/find_methodolopy.py +65 -0
- jarvis/jarvis_tools/find_symbol.py +24 -24
- jarvis/jarvis_tools/function_analyzer.py +27 -27
- jarvis/jarvis_tools/git_commiter.py +9 -9
- jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
- jarvis/jarvis_tools/methodology.py +23 -62
- jarvis/jarvis_tools/project_analyzer.py +29 -33
- jarvis/jarvis_tools/rag.py +15 -15
- jarvis/jarvis_tools/read_code.py +24 -22
- jarvis/jarvis_tools/read_webpage.py +31 -31
- jarvis/jarvis_tools/registry.py +72 -52
- jarvis/jarvis_tools/tool_generator.py +18 -18
- jarvis/jarvis_utils/config.py +23 -23
- jarvis/jarvis_utils/embedding.py +83 -83
- jarvis/jarvis_utils/git_utils.py +20 -20
- jarvis/jarvis_utils/globals.py +18 -6
- jarvis/jarvis_utils/input.py +10 -9
- jarvis/jarvis_utils/methodology.py +140 -136
- jarvis/jarvis_utils/output.py +11 -11
- jarvis/jarvis_utils/utils.py +22 -70
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +2 -0
- jarvis/jarvis_tools/select_code_files.py +0 -62
- jarvis_ai_assistant-0.1.134.dist-info/RECORD +0 -82
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
10
10
|
|
|
11
11
|
class MethodologyTool:
|
|
12
12
|
"""经验管理工具"""
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
name = "methodology"
|
|
15
15
|
description = "管理问题解决方法论,支持添加、更新和删除操作"
|
|
16
16
|
parameters = {
|
|
@@ -33,12 +33,12 @@ class MethodologyTool:
|
|
|
33
33
|
},
|
|
34
34
|
"required": ["operation", "problem_type"]
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
def __init__(self):
|
|
38
38
|
"""初始化经验管理工具"""
|
|
39
39
|
self.methodology_dir = os.path.expanduser("~/.jarvis/methodologies")
|
|
40
40
|
self._ensure_dir_exists()
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
def _ensure_dir_exists(self):
|
|
43
43
|
"""确保方法论目录存在"""
|
|
44
44
|
if not os.path.exists(self.methodology_dir):
|
|
@@ -46,70 +46,50 @@ class MethodologyTool:
|
|
|
46
46
|
os.makedirs(self.methodology_dir, exist_ok=True)
|
|
47
47
|
except Exception as e:
|
|
48
48
|
PrettyOutput.print(f"创建方法论目录失败:{str(e)}", OutputType.ERROR)
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
def _get_methodology_file_path(self, problem_type: str) -> str:
|
|
51
51
|
"""
|
|
52
52
|
根据问题类型获取对应的方法论文件路径
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
参数:
|
|
55
55
|
problem_type: 问题类型
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
返回:
|
|
58
58
|
str: 方法论文件路径
|
|
59
59
|
"""
|
|
60
60
|
# 使用MD5哈希作为文件名,避免文件名中的特殊字符
|
|
61
61
|
safe_filename = hashlib.md5(problem_type.encode('utf-8')).hexdigest()
|
|
62
62
|
return os.path.join(self.methodology_dir, f"{safe_filename}.json")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"""加载所有方法论"""
|
|
66
|
-
all_methodologies = {}
|
|
67
|
-
|
|
68
|
-
if not os.path.exists(self.methodology_dir):
|
|
69
|
-
return all_methodologies
|
|
70
|
-
|
|
71
|
-
for filepath in glob.glob(os.path.join(self.methodology_dir, "*.json")):
|
|
72
|
-
try:
|
|
73
|
-
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
74
|
-
methodology = json.load(f)
|
|
75
|
-
problem_type = methodology.get("problem_type", "")
|
|
76
|
-
content = methodology.get("content", "")
|
|
77
|
-
if problem_type and content:
|
|
78
|
-
all_methodologies[problem_type] = content
|
|
79
|
-
except Exception as e:
|
|
80
|
-
filename = os.path.basename(filepath)
|
|
81
|
-
PrettyOutput.print(f"加载方法论文件 {filename} 失败: {str(e)}", OutputType.WARNING)
|
|
82
|
-
|
|
83
|
-
return all_methodologies
|
|
84
|
-
|
|
63
|
+
|
|
64
|
+
|
|
85
65
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
86
66
|
"""执行管理方法论的操作
|
|
87
|
-
|
|
67
|
+
|
|
88
68
|
Args:
|
|
89
69
|
args: 包含操作参数的字典
|
|
90
70
|
- operation: 操作类型 (delete/update/add)
|
|
91
71
|
- problem_type: 问题类型
|
|
92
72
|
- content: 方法论内容 (更新和添加时必填)
|
|
93
|
-
|
|
73
|
+
|
|
94
74
|
Returns:
|
|
95
75
|
Dict[str, Any]: 包含执行结果的字典
|
|
96
76
|
"""
|
|
97
77
|
operation = args.get("operation", "").strip()
|
|
98
78
|
problem_type = args.get("problem_type", "").strip()
|
|
99
79
|
content = args.get("content", "").strip()
|
|
100
|
-
|
|
80
|
+
|
|
101
81
|
if not operation or not problem_type:
|
|
102
82
|
return {
|
|
103
83
|
"success": False,
|
|
104
84
|
"stdout": "",
|
|
105
85
|
"stderr": "Missing required parameters: operation and problem_type"
|
|
106
86
|
}
|
|
107
|
-
|
|
87
|
+
|
|
108
88
|
try:
|
|
109
89
|
if operation == "delete":
|
|
110
90
|
# 获取方法论文件路径
|
|
111
91
|
file_path = self._get_methodology_file_path(problem_type)
|
|
112
|
-
|
|
92
|
+
|
|
113
93
|
# 检查文件是否存在
|
|
114
94
|
if os.path.exists(file_path):
|
|
115
95
|
os.remove(file_path)
|
|
@@ -124,7 +104,7 @@ class MethodologyTool:
|
|
|
124
104
|
"stdout": "",
|
|
125
105
|
"stderr": f"Methodology for problem type '{problem_type}' not found"
|
|
126
106
|
}
|
|
127
|
-
|
|
107
|
+
|
|
128
108
|
elif operation in ["update", "add"]:
|
|
129
109
|
if not content:
|
|
130
110
|
return {
|
|
@@ -132,59 +112,40 @@ class MethodologyTool:
|
|
|
132
112
|
"stdout": "",
|
|
133
113
|
"stderr": "Need to provide methodology content"
|
|
134
114
|
}
|
|
135
|
-
|
|
115
|
+
|
|
136
116
|
# 确保目录存在
|
|
137
117
|
self._ensure_dir_exists()
|
|
138
|
-
|
|
118
|
+
|
|
139
119
|
# 获取方法论文件路径
|
|
140
120
|
file_path = self._get_methodology_file_path(problem_type)
|
|
141
|
-
|
|
121
|
+
|
|
142
122
|
# 保存方法论到单独的文件
|
|
143
123
|
with open(file_path, "w", encoding="utf-8", errors="ignore") as f:
|
|
144
124
|
json.dump({
|
|
145
125
|
"problem_type": problem_type,
|
|
146
126
|
"content": content
|
|
147
127
|
}, f, ensure_ascii=False, indent=2)
|
|
148
|
-
|
|
128
|
+
|
|
129
|
+
PrettyOutput.print(f"方法论已保存到 {file_path}", OutputType.INFO)
|
|
130
|
+
|
|
149
131
|
action = "Updated" if os.path.exists(file_path) else "Added"
|
|
150
132
|
return {
|
|
151
133
|
"success": True,
|
|
152
134
|
"stdout": f"{action} methodology for problem type '{problem_type}'",
|
|
153
135
|
"stderr": ""
|
|
154
136
|
}
|
|
155
|
-
|
|
137
|
+
|
|
156
138
|
else:
|
|
157
139
|
return {
|
|
158
140
|
"success": False,
|
|
159
141
|
"stdout": "",
|
|
160
142
|
"stderr": f"Unsupported operation type: {operation}"
|
|
161
143
|
}
|
|
162
|
-
|
|
144
|
+
|
|
163
145
|
except Exception as e:
|
|
164
146
|
return {
|
|
165
147
|
"success": False,
|
|
166
148
|
"stdout": "",
|
|
167
149
|
"stderr": f"Execution failed: {str(e)}"
|
|
168
150
|
}
|
|
169
|
-
|
|
170
|
-
def get_methodology(self, problem_type: str) -> Optional[str]:
|
|
171
|
-
"""获取特定问题类型的方法论
|
|
172
|
-
|
|
173
|
-
Args:
|
|
174
|
-
problem_type: 问题类型
|
|
175
|
-
|
|
176
|
-
Returns:
|
|
177
|
-
Optional[str]: 方法论内容,如果不存在则返回 None
|
|
178
|
-
"""
|
|
179
|
-
file_path = self._get_methodology_file_path(problem_type)
|
|
180
|
-
|
|
181
|
-
if not os.path.exists(file_path):
|
|
182
|
-
return None
|
|
183
|
-
|
|
184
|
-
try:
|
|
185
|
-
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
186
|
-
methodology = json.load(f)
|
|
187
|
-
return methodology.get("content")
|
|
188
|
-
except Exception as e:
|
|
189
|
-
PrettyOutput.print(f"读取方法论失败: {str(e)}", OutputType.ERROR)
|
|
190
|
-
return None
|
|
151
|
+
|
|
@@ -11,7 +11,7 @@ class ProjectAnalyzerTool:
|
|
|
11
11
|
项目分析工具
|
|
12
12
|
使用agent分析项目结构、入口点、模块划分等信息(支持所有文件类型)
|
|
13
13
|
"""
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
name = "project_analyzer"
|
|
16
16
|
description = "分析项目结构、入口点、模块划分等信息,提供项目概览(支持所有文件类型)"
|
|
17
17
|
parameters = {
|
|
@@ -46,51 +46,51 @@ class ProjectAnalyzerTool:
|
|
|
46
46
|
},
|
|
47
47
|
"required": []
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
51
51
|
"""
|
|
52
52
|
执行项目分析工具
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
Args:
|
|
55
55
|
args: 包含参数的字典
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
Returns:
|
|
58
58
|
包含执行结果的字典
|
|
59
59
|
"""
|
|
60
60
|
# 存储原始目录
|
|
61
61
|
original_dir = os.getcwd()
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
try:
|
|
64
64
|
# 解析参数
|
|
65
65
|
root_dir = args.get("root_dir", ".")
|
|
66
66
|
focus_dirs = args.get("focus_dirs", [])
|
|
67
67
|
exclude_dirs = args.get("exclude_dirs", [])
|
|
68
68
|
objective = args.get("objective", "")
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
# 创建agent的system prompt
|
|
71
71
|
system_prompt = self._create_system_prompt(
|
|
72
72
|
root_dir, focus_dirs, exclude_dirs, objective
|
|
73
73
|
)
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
# 创建agent的summary prompt
|
|
76
76
|
summary_prompt = self._create_summary_prompt(root_dir, objective)
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
# 切换到根目录
|
|
79
79
|
os.chdir(root_dir)
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
# 构建使用的工具
|
|
82
82
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
83
83
|
tool_registry = ToolRegistry()
|
|
84
84
|
tool_registry.use_tools([
|
|
85
|
-
"execute_shell",
|
|
86
|
-
"read_code",
|
|
85
|
+
"execute_shell",
|
|
86
|
+
"read_code",
|
|
87
87
|
"find_symbol",
|
|
88
88
|
"function_analyzer",
|
|
89
89
|
"find_caller",
|
|
90
90
|
"file_analyzer",
|
|
91
91
|
"ask_codebase"
|
|
92
92
|
])
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
# 创建并运行agent
|
|
95
95
|
analyzer_agent = Agent(
|
|
96
96
|
system_prompt=system_prompt,
|
|
@@ -102,17 +102,17 @@ class ProjectAnalyzerTool:
|
|
|
102
102
|
execute_tool_confirm=False,
|
|
103
103
|
auto_complete=True
|
|
104
104
|
)
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
# 运行agent并获取结果
|
|
107
107
|
task_input = f"分析项目结构、入口点、模块划分等信息,提供项目概览"
|
|
108
108
|
result = analyzer_agent.run(task_input)
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
return {
|
|
111
111
|
"success": True,
|
|
112
112
|
"stdout": result,
|
|
113
113
|
"stderr": ""
|
|
114
114
|
}
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
except Exception as e:
|
|
117
117
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
118
118
|
return {
|
|
@@ -123,47 +123,46 @@ class ProjectAnalyzerTool:
|
|
|
123
123
|
finally:
|
|
124
124
|
# 恢复原始目录
|
|
125
125
|
os.chdir(original_dir)
|
|
126
|
-
|
|
127
|
-
def _create_system_prompt(self, root_dir: str, focus_dirs: List[str],
|
|
126
|
+
|
|
127
|
+
def _create_system_prompt(self, root_dir: str, focus_dirs: List[str],
|
|
128
128
|
exclude_dirs: List[str], objective: str) -> str:
|
|
129
129
|
"""
|
|
130
130
|
创建Agent的system prompt
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
Args:
|
|
133
133
|
root_dir: 项目根目录
|
|
134
134
|
focus_dirs: 重点分析的目录列表
|
|
135
135
|
exclude_dirs: 排除的目录列表
|
|
136
136
|
objective: 分析目标
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
Returns:
|
|
139
139
|
系统提示文本
|
|
140
140
|
"""
|
|
141
141
|
focus_dirs_str = ", ".join(focus_dirs) if focus_dirs else "整个项目"
|
|
142
142
|
exclude_dirs_str = ", ".join(exclude_dirs) if exclude_dirs else "无"
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
objective_text = f"\n\n## 分析目标\n{objective}" if objective else "\n\n## 分析目标\n全面了解项目结构、模块划分和关键组件"
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
return f"""# 项目架构分析专家
|
|
147
147
|
|
|
148
148
|
## 任务描述
|
|
149
149
|
对项目 `{root_dir}` 进行针对性分析,专注于分析目标所需的内容,生成有针对性、深入且有洞察力的项目分析报告。{objective_text}
|
|
150
150
|
|
|
151
151
|
## 工具使用优先级
|
|
152
|
-
1. **优先使用 execute_shell 执行 fd 命令**:
|
|
152
|
+
1. **优先使用 execute_shell 执行 fd 命令**:
|
|
153
153
|
- `fd -t f -e py` 查找所有Python文件
|
|
154
154
|
- `fd -t d` 列出所有目录
|
|
155
155
|
- `fd README.md` 查找所有README文件
|
|
156
156
|
|
|
157
|
-
2. **优先使用 execute_shell 执行 rg 命令**:
|
|
157
|
+
2. **优先使用 execute_shell 执行 rg 命令**:
|
|
158
158
|
- `rg "import" --type py` 搜索导入语句
|
|
159
159
|
- `rg "class|def" --type py` 搜索类和函数定义
|
|
160
160
|
- `rg "TODO|FIXME" --type py` 搜索代码注释
|
|
161
161
|
|
|
162
|
-
3. **优先使用 execute_shell 执行 loc 命令**:
|
|
162
|
+
3. **优先使用 execute_shell 执行 loc 命令**:
|
|
163
163
|
- `loc` 统计所有代码行数
|
|
164
|
-
- `loc --include="*.py"` 统计Python代码行数
|
|
165
164
|
|
|
166
|
-
4. **辅以 read_code 读取关键文件**:
|
|
165
|
+
4. **辅以 read_code 读取关键文件**:
|
|
167
166
|
- 读取README.md、配置文件、主要模块
|
|
168
167
|
- 对于较大的文件,可读取关键部分
|
|
169
168
|
|
|
@@ -232,9 +231,6 @@ class ProjectAnalyzerTool:
|
|
|
232
231
|
|
|
233
232
|
### 代码统计分析
|
|
234
233
|
- `loc` 获取项目总体代码统计
|
|
235
|
-
- `loc --include="*.py"` 统计Python代码量
|
|
236
|
-
- `loc --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"` 统计JavaScript/TypeScript代码量
|
|
237
|
-
- `loc --exclude="test"` 排除测试代码后的统计
|
|
238
234
|
|
|
239
235
|
### 依赖分析
|
|
240
236
|
- `read_code requirements.txt` 读取Python依赖
|
|
@@ -280,16 +276,16 @@ class ProjectAnalyzerTool:
|
|
|
280
276
|
def _create_summary_prompt(self, root_dir: str, objective: str) -> str:
|
|
281
277
|
"""
|
|
282
278
|
创建Agent的summary prompt
|
|
283
|
-
|
|
279
|
+
|
|
284
280
|
Args:
|
|
285
281
|
root_dir: 项目根目录
|
|
286
282
|
objective: 分析目标
|
|
287
|
-
|
|
283
|
+
|
|
288
284
|
Returns:
|
|
289
285
|
总结提示文本
|
|
290
286
|
"""
|
|
291
287
|
objective_text = f"\n\n## 具体分析目标\n{objective}" if objective else ""
|
|
292
|
-
|
|
288
|
+
|
|
293
289
|
return f"""# 项目分析报告: `{root_dir}`{objective_text}
|
|
294
290
|
|
|
295
291
|
## 报告要求
|
|
@@ -305,4 +301,4 @@ class ProjectAnalyzerTool:
|
|
|
305
301
|
- 根据分析目标灵活组织报告结构,不必包含所有传统的项目分析章节
|
|
306
302
|
- 以清晰的Markdown格式呈现,简洁明了
|
|
307
303
|
|
|
308
|
-
在分析中保持灵活性,避免固定思维模式。你的任务不是提供全面的项目概览,而是直接解决分析目标中提出的具体问题。"""
|
|
304
|
+
在分析中保持灵活性,避免固定思维模式。你的任务不是提供全面的项目概览,而是直接解决分析目标中提出的具体问题。"""
|
jarvis/jarvis_tools/rag.py
CHANGED
|
@@ -32,10 +32,10 @@ class RAGTool:
|
|
|
32
32
|
|
|
33
33
|
def _get_rag_instance(self, dir_path: str) -> RAGCore:
|
|
34
34
|
"""Get or create RAG instance
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
Args:
|
|
37
37
|
dir_path: The absolute path of the document directory
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
Returns:
|
|
40
40
|
RAGCore: RAG instance
|
|
41
41
|
"""
|
|
@@ -45,13 +45,13 @@ class RAGTool:
|
|
|
45
45
|
|
|
46
46
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
47
47
|
"""执行文档问答
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
Args:
|
|
50
50
|
args: 包含参数的字典
|
|
51
51
|
- dir: 文档目录路径
|
|
52
52
|
- question: 要询问的问题
|
|
53
53
|
- rebuild_index: 是否重建索引
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
Returns:
|
|
56
56
|
Dict[str, Any]: 执行结果,包含以下字段:
|
|
57
57
|
- success: 布尔值,表示操作是否成功
|
|
@@ -64,7 +64,7 @@ class RAGTool:
|
|
|
64
64
|
dir_path = os.path.abspath(dir_path)
|
|
65
65
|
question = args["question"]
|
|
66
66
|
rebuild_index = args.get("rebuild_index", False)
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
# 检查目录是否存在
|
|
69
69
|
if not os.path.exists(dir_path):
|
|
70
70
|
return {
|
|
@@ -72,7 +72,7 @@ class RAGTool:
|
|
|
72
72
|
"stdout": "",
|
|
73
73
|
"stderr": f"Directory does not exist: {dir_path}"
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
# 检查路径是否为目录
|
|
77
77
|
if not os.path.isdir(dir_path):
|
|
78
78
|
return {
|
|
@@ -80,19 +80,19 @@ class RAGTool:
|
|
|
80
80
|
"stdout": "",
|
|
81
81
|
"stderr": f"The path is not a directory: {dir_path}"
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
# 获取RAG实例
|
|
85
85
|
rag = self._get_rag_instance(dir_path)
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
# 如果需要重建索引或索引不存在
|
|
88
88
|
if rebuild_index or not rag.is_index_built():
|
|
89
89
|
PrettyOutput.print("正在构建文档索引...", OutputType.INFO)
|
|
90
90
|
rag.build_index(dir_path)
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
# 执行问答
|
|
93
93
|
PrettyOutput.print(f"问题: {question}", OutputType.INFO)
|
|
94
94
|
response = rag.ask(question)
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
# 处理未找到相关文档的情况
|
|
97
97
|
if response is None:
|
|
98
98
|
return {
|
|
@@ -100,14 +100,14 @@ class RAGTool:
|
|
|
100
100
|
"stdout": "",
|
|
101
101
|
"stderr": "Failed to get answer, possibly no relevant documents found"
|
|
102
102
|
}
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
# 返回成功响应
|
|
105
105
|
return {
|
|
106
106
|
"success": True,
|
|
107
107
|
"stdout": response,
|
|
108
108
|
"stderr": ""
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
except Exception as e:
|
|
112
112
|
# 处理任何意外错误
|
|
113
113
|
PrettyOutput.print(f"文档问答失败:{str(e)}", OutputType.ERROR)
|
|
@@ -120,20 +120,20 @@ class RAGTool:
|
|
|
120
120
|
def main():
|
|
121
121
|
"""Run the tool directly from the command line"""
|
|
122
122
|
import argparse
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
parser = argparse.ArgumentParser(description='Document question and answer tool')
|
|
125
125
|
parser.add_argument('--dir', required=True, help='Document directory path')
|
|
126
126
|
parser.add_argument('--question', required=True, help='The question to ask')
|
|
127
127
|
parser.add_argument('--rebuild', action='store_true', help='Rebuild index')
|
|
128
128
|
args = parser.parse_args()
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
tool = RAGTool()
|
|
131
131
|
result = tool.execute({
|
|
132
132
|
"dir": args.dir,
|
|
133
133
|
"question": args.question,
|
|
134
134
|
"rebuild_index": args.rebuild
|
|
135
135
|
})
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
if result["success"]:
|
|
138
138
|
PrettyOutput.print(f"{result['stdout']}", OutputType.INFO, lang="markdown")
|
|
139
139
|
else:
|
jarvis/jarvis_tools/read_code.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
+
from pkg_resources import add_activation_listener
|
|
4
5
|
from yaspin import yaspin
|
|
5
6
|
|
|
7
|
+
from jarvis.jarvis_utils.globals import add_read_file_record
|
|
6
8
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
7
9
|
|
|
8
10
|
class ReadCodeTool:
|
|
@@ -30,17 +32,18 @@ class ReadCodeTool:
|
|
|
30
32
|
|
|
31
33
|
def _handle_single_file(self, filepath: str, start_line: int = 1, end_line: int = -1) -> Dict[str, Any]:
|
|
32
34
|
"""处理单个文件的读取操作
|
|
33
|
-
|
|
35
|
+
|
|
34
36
|
Args:
|
|
35
37
|
filepath (str): 文件路径
|
|
36
38
|
start_line (int): 起始行号,默认为1
|
|
37
39
|
end_line (int): 结束行号,默认为-1表示文件末尾
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
Returns:
|
|
40
42
|
Dict[str, Any]: 包含成功状态、输出内容和错误信息的字典
|
|
41
43
|
"""
|
|
42
44
|
try:
|
|
43
45
|
abs_path = os.path.abspath(filepath)
|
|
46
|
+
add_read_file_record(abs_path)
|
|
44
47
|
with yaspin(text=f"正在读取文件: {abs_path}...", color="cyan") as spinner:
|
|
45
48
|
# 文件存在性检查
|
|
46
49
|
if not os.path.exists(abs_path):
|
|
@@ -49,7 +52,7 @@ class ReadCodeTool:
|
|
|
49
52
|
"stdout": "",
|
|
50
53
|
"stderr": f"文件不存在: {abs_path}"
|
|
51
54
|
}
|
|
52
|
-
|
|
55
|
+
|
|
53
56
|
# 文件大小限制检查(10MB)
|
|
54
57
|
if os.path.getsize(abs_path) > 10 * 1024 * 1024:
|
|
55
58
|
return {
|
|
@@ -57,21 +60,21 @@ class ReadCodeTool:
|
|
|
57
60
|
"stdout": "",
|
|
58
61
|
"stderr": "文件过大 (>10MB)"
|
|
59
62
|
}
|
|
60
|
-
|
|
63
|
+
|
|
61
64
|
# 读取文件内容
|
|
62
65
|
with open(abs_path, 'r', encoding='utf-8', errors="ignore") as f:
|
|
63
66
|
lines = f.readlines()
|
|
64
|
-
|
|
67
|
+
|
|
65
68
|
total_lines = len(lines)
|
|
66
|
-
|
|
69
|
+
|
|
67
70
|
# 处理特殊值-1表示文件末尾
|
|
68
71
|
if end_line == -1:
|
|
69
72
|
end_line = total_lines
|
|
70
73
|
else:
|
|
71
74
|
end_line = max(1, min(end_line, total_lines)) if end_line >= 0 else total_lines + end_line + 1
|
|
72
|
-
|
|
75
|
+
|
|
73
76
|
start_line = max(1, min(start_line, total_lines)) if start_line >= 0 else total_lines + start_line + 1
|
|
74
|
-
|
|
77
|
+
|
|
75
78
|
if start_line > end_line:
|
|
76
79
|
spinner.fail("❌")
|
|
77
80
|
return {
|
|
@@ -79,20 +82,19 @@ class ReadCodeTool:
|
|
|
79
82
|
"stdout": "",
|
|
80
83
|
"stderr": f"无效的行范围 [{start_line}-{end_line}] (总行数: {total_lines})"
|
|
81
84
|
}
|
|
82
|
-
|
|
85
|
+
|
|
83
86
|
# 添加行号并构建输出内容
|
|
84
87
|
selected_lines = lines[start_line-1:end_line]
|
|
85
88
|
numbered_content = "".join(
|
|
86
|
-
[f"{i:4d}:{line}"
|
|
89
|
+
[f"{i:4d}:{line}"
|
|
87
90
|
for i, line in enumerate(selected_lines, start=start_line)]
|
|
88
91
|
)
|
|
89
|
-
|
|
92
|
+
|
|
90
93
|
# 构建输出格式
|
|
91
94
|
output = (
|
|
92
95
|
f"\n🔍 文件: {abs_path}\n"
|
|
93
96
|
f"📄 原始行号: {start_line}-{end_line} (共{total_lines}行) \n\n"
|
|
94
|
-
f"{numbered_content}\n"
|
|
95
|
-
f"{'='*80}\n"
|
|
97
|
+
f"{numbered_content}\n\n"
|
|
96
98
|
)
|
|
97
99
|
spinner.text = f"文件读取完成: {abs_path}"
|
|
98
100
|
spinner.ok("✅")
|
|
@@ -104,7 +106,7 @@ class ReadCodeTool:
|
|
|
104
106
|
"stdout": output,
|
|
105
107
|
"stderr": ""
|
|
106
108
|
}
|
|
107
|
-
|
|
109
|
+
|
|
108
110
|
except Exception as e:
|
|
109
111
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
110
112
|
return {
|
|
@@ -115,10 +117,10 @@ class ReadCodeTool:
|
|
|
115
117
|
|
|
116
118
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
117
119
|
"""执行代码读取操作
|
|
118
|
-
|
|
120
|
+
|
|
119
121
|
Args:
|
|
120
122
|
args (Dict): 包含文件列表的参数字典
|
|
121
|
-
|
|
123
|
+
|
|
122
124
|
Returns:
|
|
123
125
|
Dict[str, Any]: 包含成功状态、输出内容和错误信息的字典
|
|
124
126
|
"""
|
|
@@ -129,32 +131,32 @@ class ReadCodeTool:
|
|
|
129
131
|
"stdout": "",
|
|
130
132
|
"stderr": "参数中必须包含文件列表"
|
|
131
133
|
}
|
|
132
|
-
|
|
134
|
+
|
|
133
135
|
all_outputs = []
|
|
134
136
|
overall_success = True
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
for file_info in args["files"]:
|
|
137
139
|
if not isinstance(file_info, dict) or "path" not in file_info:
|
|
138
140
|
continue
|
|
139
|
-
|
|
141
|
+
|
|
140
142
|
result = self._handle_single_file(
|
|
141
143
|
file_info["path"].strip(),
|
|
142
144
|
file_info.get("start_line", 1),
|
|
143
145
|
file_info.get("end_line", -1)
|
|
144
146
|
)
|
|
145
|
-
|
|
147
|
+
|
|
146
148
|
if result["success"]:
|
|
147
149
|
all_outputs.append(result["stdout"])
|
|
148
150
|
else:
|
|
149
151
|
all_outputs.append(f"❌ {file_info['path']}: {result['stderr']}")
|
|
150
152
|
overall_success = False
|
|
151
|
-
|
|
153
|
+
|
|
152
154
|
return {
|
|
153
155
|
"success": overall_success,
|
|
154
156
|
"stdout": "\n".join(all_outputs),
|
|
155
157
|
"stderr": ""
|
|
156
158
|
}
|
|
157
|
-
|
|
159
|
+
|
|
158
160
|
except Exception as e:
|
|
159
161
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
160
162
|
return {
|