jarvis-ai-assistant 0.1.110__py3-none-any.whl → 0.1.112__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/agent.py +51 -39
- jarvis/jarvis_code_agent/code_agent.py +89 -53
- jarvis/jarvis_code_agent/file_select.py +20 -20
- jarvis/jarvis_code_agent/patch.py +20 -11
- jarvis/jarvis_code_agent/relevant_files.py +68 -16
- jarvis/jarvis_codebase/main.py +82 -88
- jarvis/jarvis_lsp/cpp.py +1 -1
- jarvis/jarvis_lsp/go.py +1 -1
- jarvis/jarvis_lsp/python.py +0 -2
- jarvis/jarvis_lsp/registry.py +13 -13
- jarvis/jarvis_lsp/rust.py +1 -1
- jarvis/jarvis_platform/ai8.py +14 -14
- jarvis/jarvis_platform/base.py +1 -1
- jarvis/jarvis_platform/kimi.py +17 -17
- jarvis/jarvis_platform/ollama.py +14 -14
- jarvis/jarvis_platform/openai.py +8 -8
- jarvis/jarvis_platform/oyi.py +19 -19
- jarvis/jarvis_platform/registry.py +6 -6
- jarvis/jarvis_platform_manager/main.py +17 -17
- jarvis/jarvis_rag/main.py +25 -25
- jarvis/jarvis_smart_shell/main.py +6 -6
- jarvis/jarvis_tools/ask_codebase.py +4 -4
- jarvis/jarvis_tools/ask_user.py +2 -2
- jarvis/jarvis_tools/create_code_agent.py +8 -8
- jarvis/jarvis_tools/create_sub_agent.py +2 -2
- jarvis/jarvis_tools/execute_shell.py +2 -2
- jarvis/jarvis_tools/file_operation.py +1 -1
- jarvis/jarvis_tools/git_commiter.py +4 -6
- jarvis/jarvis_tools/methodology.py +3 -3
- jarvis/jarvis_tools/rag.py +3 -3
- jarvis/jarvis_tools/read_code.py +4 -3
- jarvis/jarvis_tools/read_webpage.py +19 -6
- jarvis/jarvis_tools/registry.py +11 -11
- jarvis/jarvis_tools/search.py +88 -27
- jarvis/jarvis_tools/select_code_files.py +1 -1
- jarvis/jarvis_tools/tool_generator.py +182 -0
- jarvis/utils/date_utils.py +19 -0
- jarvis/utils.py +31 -25
- jarvis_ai_assistant-0.1.112.dist-info/METADATA +460 -0
- jarvis_ai_assistant-0.1.112.dist-info/RECORD +64 -0
- jarvis_ai_assistant-0.1.110.dist-info/METADATA +0 -462
- jarvis_ai_assistant-0.1.110.dist-info/RECORD +0 -62
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.110.dist-info → jarvis_ai_assistant-0.1.112.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import re
|
|
3
3
|
from typing import Dict, List
|
|
4
4
|
from prompt_toolkit import PromptSession
|
|
5
|
-
from prompt_toolkit.completion import
|
|
5
|
+
from prompt_toolkit.completion import Completer, Completion
|
|
6
6
|
from jarvis.utils import OutputType, PrettyOutput, get_single_line_input, user_confirm
|
|
7
7
|
|
|
8
8
|
|
|
@@ -42,7 +42,7 @@ def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
|
|
|
42
42
|
if start <= end:
|
|
43
43
|
selected.update(range(start, end + 1))
|
|
44
44
|
except ValueError:
|
|
45
|
-
PrettyOutput.print(f"
|
|
45
|
+
PrettyOutput.print(f"忽略无效的范围表达式: {part}", OutputType.WARNING)
|
|
46
46
|
# Process single number
|
|
47
47
|
else:
|
|
48
48
|
try:
|
|
@@ -50,9 +50,9 @@ def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
|
|
|
50
50
|
if 0 <= index < max_index:
|
|
51
51
|
selected.add(index)
|
|
52
52
|
else:
|
|
53
|
-
PrettyOutput.print(f"
|
|
53
|
+
PrettyOutput.print(f"忽略超出范围的索引: {part}", OutputType.WARNING)
|
|
54
54
|
except ValueError:
|
|
55
|
-
PrettyOutput.print(f"
|
|
55
|
+
PrettyOutput.print(f"忽略无效的数字: {part}", OutputType.WARNING)
|
|
56
56
|
|
|
57
57
|
return sorted(list(selected))
|
|
58
58
|
|
|
@@ -125,32 +125,32 @@ def _fuzzy_match_files(root_dir: str, pattern: str) -> List[str]:
|
|
|
125
125
|
|
|
126
126
|
return sorted(matches)
|
|
127
127
|
|
|
128
|
-
def select_files(related_files: List[str], root_dir: str) -> List[str]:
|
|
128
|
+
def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dict[str, str]]:
|
|
129
129
|
"""Let the user select and supplement related files"""
|
|
130
|
-
PrettyOutput.section("
|
|
131
|
-
|
|
130
|
+
PrettyOutput.section("相关文件", OutputType.INFO)
|
|
131
|
+
|
|
132
132
|
output = ""
|
|
133
133
|
# Display found files
|
|
134
134
|
selected_files = list(related_files) # Default select all
|
|
135
135
|
for i, file in enumerate(related_files, 1):
|
|
136
|
-
output += f"[{i}] {file}\n"
|
|
136
|
+
output += f"[{i}] {file['file']} ({file['reason']})\n"
|
|
137
137
|
|
|
138
138
|
PrettyOutput.print(output, OutputType.INFO, lang="markdown")
|
|
139
139
|
|
|
140
140
|
if len(related_files) > 0:
|
|
141
141
|
# Ask the user if they need to adjust the file list
|
|
142
|
-
if user_confirm("
|
|
142
|
+
if user_confirm("是否需要调整文件列表?", False):
|
|
143
143
|
# Let the user select files
|
|
144
|
-
numbers = get_single_line_input("
|
|
144
|
+
numbers = get_single_line_input("请输入要包含的文件编号(支持: 1,3-6格式, 按回车保持当前选择)").strip()
|
|
145
145
|
if numbers:
|
|
146
146
|
selected_indices = _parse_file_selection(numbers, len(related_files))
|
|
147
147
|
if selected_indices:
|
|
148
148
|
selected_files = [related_files[i] for i in selected_indices]
|
|
149
149
|
else:
|
|
150
|
-
PrettyOutput.print("
|
|
150
|
+
PrettyOutput.print("没有有效的文件被选择, 保持当前选择", OutputType.WARNING)
|
|
151
151
|
|
|
152
152
|
# Ask if they need to supplement files
|
|
153
|
-
if user_confirm("
|
|
153
|
+
if user_confirm("是否需要补充其他文件?", False):
|
|
154
154
|
# Create file completion session
|
|
155
155
|
session = PromptSession(
|
|
156
156
|
completer=_get_file_completer(root_dir),
|
|
@@ -158,7 +158,7 @@ def select_files(related_files: List[str], root_dir: str) -> List[str]:
|
|
|
158
158
|
)
|
|
159
159
|
|
|
160
160
|
while True:
|
|
161
|
-
PrettyOutput.print("
|
|
161
|
+
PrettyOutput.print("请输入要补充的文件路径(支持Tab补全和*?通配符, 输入空行结束)", OutputType.INFO)
|
|
162
162
|
try:
|
|
163
163
|
file_path = session.prompt(">>> ").strip()
|
|
164
164
|
except KeyboardInterrupt:
|
|
@@ -171,16 +171,16 @@ def select_files(related_files: List[str], root_dir: str) -> List[str]:
|
|
|
171
171
|
if '*' in file_path or '?' in file_path:
|
|
172
172
|
matches = _fuzzy_match_files(root_dir, file_path)
|
|
173
173
|
if not matches:
|
|
174
|
-
PrettyOutput.print("
|
|
174
|
+
PrettyOutput.print("没有找到匹配的文件", OutputType.WARNING)
|
|
175
175
|
continue
|
|
176
176
|
|
|
177
177
|
# Display matching files
|
|
178
|
-
PrettyOutput.print("
|
|
178
|
+
PrettyOutput.print("找到以下匹配的文件:", OutputType.INFO)
|
|
179
179
|
for i, path in enumerate(matches, 1):
|
|
180
180
|
PrettyOutput.print(f"[{i}] {path}", OutputType.INFO)
|
|
181
181
|
|
|
182
182
|
# Let the user select
|
|
183
|
-
numbers = get_single_line_input("
|
|
183
|
+
numbers = get_single_line_input("请选择要添加的文件编号(支持: 1,3-6格式, 按回车选择所有)").strip()
|
|
184
184
|
if numbers:
|
|
185
185
|
indices = _parse_file_selection(numbers, len(matches))
|
|
186
186
|
if not indices:
|
|
@@ -195,13 +195,13 @@ def select_files(related_files: List[str], root_dir: str) -> List[str]:
|
|
|
195
195
|
for path in paths_to_add:
|
|
196
196
|
full_path = os.path.join(root_dir, path)
|
|
197
197
|
if not os.path.isfile(full_path):
|
|
198
|
-
PrettyOutput.print(f"
|
|
198
|
+
PrettyOutput.print(f"文件不存在: {path}", OutputType.ERROR)
|
|
199
199
|
continue
|
|
200
200
|
|
|
201
201
|
try:
|
|
202
|
-
selected_files.append(path)
|
|
203
|
-
PrettyOutput.print(f"
|
|
202
|
+
selected_files.append({"file": path, "reason": "User Added"})
|
|
203
|
+
PrettyOutput.print(f"文件已添加: {path}", OutputType.SUCCESS)
|
|
204
204
|
except Exception as e:
|
|
205
|
-
PrettyOutput.print(f"
|
|
205
|
+
PrettyOutput.print(f"读取文件失败: {str(e)}", OutputType.ERROR)
|
|
206
206
|
|
|
207
207
|
return selected_files
|
|
@@ -2,13 +2,13 @@ import re
|
|
|
2
2
|
from typing import Dict, Any, List
|
|
3
3
|
import os
|
|
4
4
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
5
|
-
from jarvis.utils import OutputType, PrettyOutput, has_uncommitted_changes, user_confirm
|
|
5
|
+
from jarvis.utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, user_confirm
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
|
|
9
9
|
"""Parse patches from string with format:
|
|
10
10
|
<PATCH>
|
|
11
|
-
>
|
|
11
|
+
> path/to/file start_line,end_line
|
|
12
12
|
content_line1
|
|
13
13
|
content_line2
|
|
14
14
|
...
|
|
@@ -77,12 +77,12 @@ def apply_patch(output_str: str)->str:
|
|
|
77
77
|
# Write new file
|
|
78
78
|
with open(filepath, 'w', encoding='utf-8') as f:
|
|
79
79
|
f.writelines(new_content)
|
|
80
|
-
PrettyOutput.print(f"
|
|
80
|
+
PrettyOutput.print(f"成功创建新文件 {filepath}", OutputType.SUCCESS)
|
|
81
81
|
continue
|
|
82
82
|
|
|
83
83
|
# Regular patch logic for existing files
|
|
84
84
|
if not os.path.exists(filepath):
|
|
85
|
-
PrettyOutput.print(f"
|
|
85
|
+
PrettyOutput.print(f"文件不存在: {filepath}", OutputType.WARNING)
|
|
86
86
|
continue
|
|
87
87
|
|
|
88
88
|
# Read original file content
|
|
@@ -90,7 +90,7 @@ def apply_patch(output_str: str)->str:
|
|
|
90
90
|
|
|
91
91
|
# Validate line numbers
|
|
92
92
|
if start_line < 0 or end_line > len(lines) + 1 or start_line > end_line:
|
|
93
|
-
PrettyOutput.print(f"
|
|
93
|
+
PrettyOutput.print(f"无效的行范围 [{start_line}, {end_line}) 对于文件: {filepath}", OutputType.WARNING)
|
|
94
94
|
continue
|
|
95
95
|
|
|
96
96
|
# Create new content
|
|
@@ -99,15 +99,21 @@ def apply_patch(output_str: str)->str:
|
|
|
99
99
|
# Write back to file
|
|
100
100
|
open(filepath, 'w', encoding='utf-8').writelines(lines)
|
|
101
101
|
|
|
102
|
-
PrettyOutput.print(f"
|
|
102
|
+
PrettyOutput.print(f"成功应用补丁到 {filepath}", OutputType.SUCCESS)
|
|
103
103
|
|
|
104
104
|
except Exception as e:
|
|
105
|
-
PrettyOutput.print(f"
|
|
105
|
+
PrettyOutput.print(f"应用补丁到 {filepath} 失败: {str(e)}", OutputType.ERROR)
|
|
106
106
|
continue
|
|
107
|
-
|
|
107
|
+
ret = ""
|
|
108
108
|
if has_uncommitted_changes():
|
|
109
|
-
handle_commit_workflow()
|
|
110
|
-
|
|
109
|
+
if handle_commit_workflow():
|
|
110
|
+
ret += "Successfully applied the patch"
|
|
111
|
+
else:
|
|
112
|
+
ret += "User rejected the patch"
|
|
113
|
+
user_input = get_multiline_input("你可以继续输入: ")
|
|
114
|
+
if user_input:
|
|
115
|
+
ret += user_input
|
|
116
|
+
return ret
|
|
111
117
|
|
|
112
118
|
def handle_commit_workflow()->bool:
|
|
113
119
|
"""Handle the git commit workflow and return the commit details.
|
|
@@ -115,11 +121,14 @@ def handle_commit_workflow()->bool:
|
|
|
115
121
|
Returns:
|
|
116
122
|
tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
|
|
117
123
|
"""
|
|
124
|
+
os.system("git add .")
|
|
118
125
|
diff = os.popen("git diff HEAD").read()
|
|
126
|
+
os.system("git reset HEAD")
|
|
119
127
|
PrettyOutput.print(diff, OutputType.CODE, lang="diff")
|
|
120
|
-
if not user_confirm("
|
|
128
|
+
if not user_confirm("是否要提交代码?", default=True):
|
|
121
129
|
os.system("git reset HEAD")
|
|
122
130
|
os.system("git checkout -- .")
|
|
131
|
+
os.system("git clean -fd")
|
|
123
132
|
return False
|
|
124
133
|
|
|
125
134
|
git_commiter = GitCommitTool()
|
|
@@ -1,27 +1,79 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
|
-
from typing import List
|
|
3
|
+
from typing import Dict, List, Optional, Tuple
|
|
4
4
|
|
|
5
|
-
import yaml
|
|
6
|
-
from jarvis.agent import Agent
|
|
7
5
|
from jarvis.jarvis_code_agent.file_select import select_files
|
|
8
6
|
from jarvis.jarvis_codebase.main import CodeBase
|
|
9
7
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
10
|
-
from jarvis.
|
|
11
|
-
from jarvis.utils import OutputType, PrettyOutput, is_disable_codebase
|
|
8
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
12
9
|
|
|
10
|
+
def make_question(requirement: str) -> Optional[str]:
|
|
11
|
+
"""Generate structured questions to gather necessary information for the requirement."""
|
|
12
|
+
prompt = f"""You are a code analysis expert who helps developers understand existing system implementations. Generate specific questions to investigate:
|
|
13
|
+
- Current system implementations
|
|
14
|
+
- Existing interfaces and integration points
|
|
15
|
+
- Extension mechanisms and patterns
|
|
16
|
+
- Related components and their interactions
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Key Instructions:
|
|
19
|
+
1. Focus on understanding the EXISTING system
|
|
20
|
+
2. Ask about interfaces, hooks, and extension points
|
|
21
|
+
3. Investigate integration patterns and dependencies
|
|
22
|
+
4. Explore current implementation details
|
|
23
|
+
|
|
24
|
+
For example:
|
|
25
|
+
BAD: "How should we implement the new feature?"
|
|
26
|
+
GOOD: "What are the existing extension points in the authentication system that we can use to add the new OAuth provider? Specifically, how does AuthProvider interface work and where is it currently used?"
|
|
27
|
+
|
|
28
|
+
Consider these investigation aspects:
|
|
29
|
+
|
|
30
|
+
1. System Architecture:
|
|
31
|
+
- "What are the existing interfaces/classes that handle [functionality]?"
|
|
32
|
+
- "How is [feature] currently integrated with other components?"
|
|
33
|
+
- "Where are the extension points for [system component]?"
|
|
34
|
+
|
|
35
|
+
2. Implementation Details:
|
|
36
|
+
- "What is the current workflow for [operation] in the system?"
|
|
37
|
+
- "How does the system expose hooks/callbacks for [functionality]?"
|
|
38
|
+
- "Which interfaces/abstract classes are used for [feature] extensibility?"
|
|
39
|
+
|
|
40
|
+
3. Integration Patterns:
|
|
41
|
+
- "How do existing components integrate with [system part]?"
|
|
42
|
+
- "What are the current integration points for [feature]?"
|
|
43
|
+
- "How does the system handle extensions to [component]?"
|
|
21
44
|
|
|
22
|
-
|
|
45
|
+
4. Extension Mechanisms:
|
|
46
|
+
- "What patterns are used for extending [functionality]?"
|
|
47
|
+
- "How do existing plugins/extensions connect to [system]?"
|
|
48
|
+
- "Where are the configuration points for [feature] customization?"
|
|
49
|
+
|
|
50
|
+
User Requirement:
|
|
51
|
+
{requirement}
|
|
52
|
+
|
|
53
|
+
Output Format:
|
|
54
|
+
<QUESTION>
|
|
55
|
+
[Write 3-5 specific questions focusing on existing implementations and extension points. Each question should help understand how to integrate with or extend the current system]
|
|
56
|
+
</QUESTION>
|
|
57
|
+
"""
|
|
58
|
+
model = PlatformRegistry().get_thinking_platform()
|
|
59
|
+
response = model.chat_until_success(prompt)
|
|
60
|
+
response = re.search(r'<QUESTION>(.*?)</QUESTION>', response, re.DOTALL)
|
|
61
|
+
if response is None:
|
|
62
|
+
return None
|
|
63
|
+
return response.group(1)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def find_relevant_information(user_input: str, root_dir: str) -> Tuple[List[Dict[str, str]], str]:
|
|
67
|
+
try:
|
|
68
|
+
PrettyOutput.print("从代码库中查找文件...", OutputType.INFO)
|
|
69
|
+
codebase = CodeBase(root_dir)
|
|
70
|
+
question = make_question(user_input)
|
|
71
|
+
if question is None:
|
|
72
|
+
return [], ""
|
|
73
|
+
files_from_codebase, infomation = codebase.ask_codebase(question)
|
|
23
74
|
|
|
24
75
|
selected_files = select_files(files_from_codebase, os.getcwd())
|
|
25
|
-
return selected_files
|
|
26
|
-
except Exception
|
|
27
|
-
|
|
76
|
+
return selected_files, infomation
|
|
77
|
+
except Exception:
|
|
78
|
+
PrettyOutput.print("查找相关文件失败", OutputType.ERROR)
|
|
79
|
+
return [], ""
|