jarvis-ai-assistant 0.1.132__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 +330 -347
- jarvis/jarvis_agent/builtin_input_handler.py +16 -6
- jarvis/jarvis_agent/file_input_handler.py +9 -9
- jarvis/jarvis_agent/jarvis.py +143 -0
- jarvis/jarvis_agent/main.py +12 -13
- jarvis/jarvis_agent/output_handler.py +3 -3
- jarvis/jarvis_agent/patch.py +92 -64
- jarvis/jarvis_agent/shell_input_handler.py +5 -3
- jarvis/jarvis_code_agent/code_agent.py +263 -177
- jarvis/jarvis_code_agent/file_select.py +24 -24
- jarvis/jarvis_dev/main.py +45 -59
- 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 -53
- jarvis/jarvis_lsp/cpp.py +13 -28
- jarvis/jarvis_lsp/go.py +13 -28
- jarvis/jarvis_lsp/python.py +8 -27
- jarvis/jarvis_lsp/registry.py +21 -83
- jarvis/jarvis_lsp/rust.py +15 -30
- jarvis/jarvis_methodology/main.py +101 -0
- jarvis/jarvis_multi_agent/__init__.py +10 -51
- jarvis/jarvis_multi_agent/main.py +43 -0
- 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 +29 -44
- jarvis/jarvis_platform/yuanbao.py +39 -43
- 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 +262 -278
- jarvis/jarvis_smart_shell/main.py +12 -12
- jarvis/jarvis_tools/ask_codebase.py +85 -78
- 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 +40 -21
- jarvis/jarvis_tools/create_code_agent.py +15 -15
- jarvis/jarvis_tools/create_sub_agent.py +0 -1
- 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 +116 -105
- jarvis/jarvis_tools/file_operation.py +22 -20
- jarvis/jarvis_tools/find_caller.py +105 -40
- jarvis/jarvis_tools/find_methodolopy.py +65 -0
- jarvis/jarvis_tools/find_symbol.py +123 -39
- jarvis/jarvis_tools/function_analyzer.py +140 -57
- jarvis/jarvis_tools/git_commiter.py +10 -10
- jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
- jarvis/jarvis_tools/methodology.py +22 -67
- jarvis/jarvis_tools/project_analyzer.py +137 -53
- jarvis/jarvis_tools/rag.py +15 -20
- jarvis/jarvis_tools/read_code.py +25 -23
- jarvis/jarvis_tools/read_webpage.py +31 -31
- jarvis/jarvis_tools/registry.py +72 -52
- jarvis/jarvis_tools/search_web.py +23 -353
- jarvis/jarvis_tools/tool_generator.py +19 -19
- jarvis/jarvis_utils/config.py +36 -96
- 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 +141 -140
- jarvis/jarvis_utils/output.py +13 -13
- jarvis/jarvis_utils/utils.py +23 -71
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +6 -15
- jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +4 -3
- jarvis/jarvis_tools/lsp_find_definition.py +0 -150
- jarvis/jarvis_tools/lsp_find_references.py +0 -127
- jarvis/jarvis_tools/select_code_files.py +0 -62
- jarvis_ai_assistant-0.1.132.dist-info/RECORD +0 -82
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
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
|
-
f"📄 原始行号: {start_line}-{end_line} (共{
|
|
94
|
-
f"{numbered_content}\n"
|
|
95
|
-
f"{'='*80}\n"
|
|
96
|
+
f"📄 原始行号: {start_line}-{end_line} (共{total_lines}行) \n\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 {
|
|
@@ -24,38 +24,38 @@ class WebpageTool:
|
|
|
24
24
|
"""Read webpage content using Playwright to handle JavaScript-rendered pages"""
|
|
25
25
|
try:
|
|
26
26
|
url = args["url"].strip()
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
with sync_playwright() as p:
|
|
29
29
|
# Launch browser
|
|
30
30
|
browser = p.chromium.launch(
|
|
31
31
|
headless=True,
|
|
32
32
|
args=['--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage']
|
|
33
33
|
)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Create a new page with appropriate settings
|
|
36
36
|
page = browser.new_page(
|
|
37
37
|
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
|
38
38
|
viewport={'width': 1920, 'height': 1080}
|
|
39
39
|
)
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
# Set timeout to avoid long waits
|
|
42
42
|
page.set_default_timeout(30000) # 30 seconds
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
try:
|
|
45
45
|
# Navigate to URL and wait for page to load
|
|
46
46
|
response = page.goto(url, wait_until="domcontentloaded")
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
# Additional wait for network to be idle (with a timeout)
|
|
49
49
|
try:
|
|
50
50
|
page.wait_for_load_state("networkidle", timeout=10000)
|
|
51
51
|
except PlaywrightTimeoutError:
|
|
52
52
|
# Continue even if network doesn't become completely idle
|
|
53
53
|
pass
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
# Make sure we got a valid response
|
|
56
56
|
if not response or response.status >= 400:
|
|
57
57
|
raise Exception(f"Failed to load page: HTTP {response.status if response else 'No response'}")
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
# Get page title safely
|
|
60
60
|
title = "No title"
|
|
61
61
|
try:
|
|
@@ -68,26 +68,26 @@ class WebpageTool:
|
|
|
68
68
|
title = title_element.text_content() or "No title"
|
|
69
69
|
except Exception:
|
|
70
70
|
pass
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
# Get the HTML content after JavaScript execution
|
|
73
73
|
html_content = page.content()
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
except Exception as e:
|
|
76
76
|
raise Exception(f"Error navigating to page: {str(e)}")
|
|
77
77
|
finally:
|
|
78
78
|
# Always close browser
|
|
79
79
|
browser.close()
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
# Parse with BeautifulSoup and convert to markdown
|
|
82
82
|
markdown_content = self._html_to_markdown(html_content, url)
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
# Build output in markdown format
|
|
85
85
|
output = [
|
|
86
86
|
f"# {title}",
|
|
87
87
|
f"Url: {url}",
|
|
88
88
|
markdown_content
|
|
89
89
|
]
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
return {
|
|
92
92
|
"success": True,
|
|
93
93
|
"stdout": "\n".join(output),
|
|
@@ -101,7 +101,7 @@ class WebpageTool:
|
|
|
101
101
|
"stdout": "",
|
|
102
102
|
"stderr": f"Failed to parse webpage: {str(e)}"
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
def _create_soup_element(self, content):
|
|
106
106
|
"""Safely create a BeautifulSoup element, ensuring it's treated as markup"""
|
|
107
107
|
if isinstance(content, str):
|
|
@@ -112,15 +112,15 @@ class WebpageTool:
|
|
|
112
112
|
# Return an empty list if the div is None
|
|
113
113
|
return []
|
|
114
114
|
return content
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
def _html_to_markdown(self, html_content: str, base_url: str) -> str:
|
|
117
117
|
"""Convert HTML to Markdown format preserving the content structure"""
|
|
118
118
|
soup = BeautifulSoup(html_content, 'html.parser')
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
# Remove unwanted elements
|
|
121
121
|
for element in soup(['script', 'style', 'meta', 'noscript', 'head']):
|
|
122
122
|
element.decompose()
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
# Process headings
|
|
125
125
|
for level in range(1, 7):
|
|
126
126
|
for heading in soup.find_all(f'h{level}'):
|
|
@@ -128,14 +128,14 @@ class WebpageTool:
|
|
|
128
128
|
heading_md = "\n\n" + "#" * level + " " + text + "\n\n"
|
|
129
129
|
new_element = self._create_soup_element(heading_md)
|
|
130
130
|
heading.replace_with(*new_element)
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
# Process paragraphs
|
|
133
133
|
for p in soup.find_all('p'):
|
|
134
134
|
text = p.get_text().strip()
|
|
135
135
|
if text:
|
|
136
136
|
new_element = self._create_soup_element("\n\n" + text + "\n\n")
|
|
137
137
|
p.replace_with(*new_element)
|
|
138
|
-
|
|
138
|
+
|
|
139
139
|
# Process unordered lists
|
|
140
140
|
for ul in soup.find_all('ul'):
|
|
141
141
|
items = []
|
|
@@ -143,7 +143,7 @@ class WebpageTool:
|
|
|
143
143
|
items.append("* " + li.get_text().strip())
|
|
144
144
|
new_element = self._create_soup_element("\n\n" + "\n".join(items) + "\n\n")
|
|
145
145
|
ul.replace_with(*new_element)
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
# Process ordered lists
|
|
148
148
|
for ol in soup.find_all('ol'):
|
|
149
149
|
items = []
|
|
@@ -151,7 +151,7 @@ class WebpageTool:
|
|
|
151
151
|
items.append(str(i) + ". " + li.get_text().strip())
|
|
152
152
|
new_element = self._create_soup_element("\n\n" + "\n".join(items) + "\n\n")
|
|
153
153
|
ol.replace_with(*new_element)
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
# Process links (first pass)
|
|
156
156
|
for a in soup.find_all('a', href=True):
|
|
157
157
|
try:
|
|
@@ -166,7 +166,7 @@ class WebpageTool:
|
|
|
166
166
|
a.replace_with(*new_element)
|
|
167
167
|
except (KeyError, AttributeError):
|
|
168
168
|
continue
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
# Process images
|
|
171
171
|
for img in soup.find_all('img', src=True):
|
|
172
172
|
try:
|
|
@@ -180,37 +180,37 @@ class WebpageTool:
|
|
|
180
180
|
img.replace_with(*new_element)
|
|
181
181
|
except (KeyError, AttributeError, UnboundLocalError):
|
|
182
182
|
continue
|
|
183
|
-
|
|
183
|
+
|
|
184
184
|
# Process code blocks
|
|
185
185
|
for pre in soup.find_all('pre'):
|
|
186
186
|
code = pre.get_text().strip()
|
|
187
187
|
pre_md = "\n\n```\n" + code + "\n```\n\n"
|
|
188
188
|
new_element = self._create_soup_element(pre_md)
|
|
189
189
|
pre.replace_with(*new_element)
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
# Process inline code
|
|
192
192
|
for code in soup.find_all('code'):
|
|
193
193
|
text = code.get_text().strip()
|
|
194
194
|
code_md = "`" + text + "`"
|
|
195
195
|
new_element = self._create_soup_element(code_md)
|
|
196
196
|
code.replace_with(*new_element)
|
|
197
|
-
|
|
197
|
+
|
|
198
198
|
# Process line breaks
|
|
199
199
|
for br in soup.find_all('br'):
|
|
200
200
|
new_element = self._create_soup_element('\n')
|
|
201
201
|
br.replace_with(*new_element)
|
|
202
|
-
|
|
202
|
+
|
|
203
203
|
# Get the full text
|
|
204
204
|
markdown_text = soup.get_text()
|
|
205
|
-
|
|
205
|
+
|
|
206
206
|
# Clean up extra whitespace and line breaks
|
|
207
207
|
markdown_text = re.sub(r'\n{3,}', '\n\n', markdown_text)
|
|
208
208
|
markdown_text = re.sub(r'\s{2,}', ' ', markdown_text)
|
|
209
|
-
|
|
209
|
+
|
|
210
210
|
# Process links again (for any that might have been missed)
|
|
211
211
|
link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
|
|
212
212
|
all_links = re.findall(link_pattern, markdown_text)
|
|
213
|
-
|
|
213
|
+
|
|
214
214
|
# Add a section with all links at the end
|
|
215
215
|
if all_links:
|
|
216
216
|
link_section = ["", "## Links", ""]
|
|
@@ -220,7 +220,7 @@ class WebpageTool:
|
|
|
220
220
|
if link_entry not in seen_links:
|
|
221
221
|
link_section.append(link_entry)
|
|
222
222
|
seen_links.add(link_entry)
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
markdown_text += "\n\n" + "\n".join(link_section)
|
|
225
|
-
|
|
226
|
-
return markdown_text.strip()
|
|
225
|
+
|
|
226
|
+
return markdown_text.strip()
|