jarvis-ai-assistant 0.1.115__py3-none-any.whl → 0.1.116__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.
- jarvis/__init__.py +1 -1
- jarvis/{agent.py → jarvis_agent/__init__.py} +35 -159
- jarvis/jarvis_agent/output_handler.py +23 -0
- jarvis/jarvis_code_agent/code_agent.py +11 -11
- jarvis/jarvis_code_agent/file_select.py +28 -7
- jarvis/jarvis_code_agent/patch.py +25 -2
- jarvis/jarvis_code_agent/relevant_files.py +1 -1
- jarvis/jarvis_codebase/main.py +2 -2
- jarvis/jarvis_lsp/cpp.py +1 -1
- jarvis/jarvis_lsp/go.py +1 -1
- jarvis/jarvis_lsp/registry.py +1 -1
- jarvis/jarvis_lsp/rust.py +1 -1
- jarvis/jarvis_multi_agent/__init__.py +147 -0
- jarvis/jarvis_platform/ai8.py +2 -2
- jarvis/jarvis_platform/base.py +14 -4
- jarvis/jarvis_platform/kimi.py +2 -2
- jarvis/jarvis_platform/ollama.py +1 -1
- jarvis/jarvis_platform/openai.py +1 -1
- jarvis/jarvis_platform/oyi.py +1 -1
- jarvis/jarvis_platform/registry.py +1 -1
- jarvis/jarvis_platform_manager/main.py +422 -6
- jarvis/jarvis_platform_manager/openai_test.py +139 -0
- jarvis/jarvis_rag/main.py +2 -2
- jarvis/jarvis_smart_shell/main.py +17 -16
- jarvis/jarvis_tools/ask_codebase.py +1 -1
- jarvis/jarvis_tools/ask_user.py +1 -1
- jarvis/jarvis_tools/chdir.py +1 -1
- jarvis/jarvis_tools/code_review.py +3 -3
- jarvis/jarvis_tools/create_code_agent.py +1 -1
- jarvis/jarvis_tools/create_sub_agent.py +2 -2
- jarvis/jarvis_tools/execute_shell.py +1 -1
- jarvis/jarvis_tools/file_operation.py +16 -14
- jarvis/jarvis_tools/git_commiter.py +2 -2
- jarvis/jarvis_tools/methodology.py +1 -1
- jarvis/jarvis_tools/rag.py +1 -1
- jarvis/jarvis_tools/read_code.py +19 -8
- jarvis/jarvis_tools/read_webpage.py +1 -1
- jarvis/jarvis_tools/registry.py +53 -6
- jarvis/jarvis_tools/search.py +1 -1
- jarvis/jarvis_tools/select_code_files.py +1 -1
- jarvis/{utils.py → jarvis_utils/__init__.py} +69 -53
- {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.116.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.116.dist-info/RECORD +64 -0
- {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.116.dist-info}/WHEEL +1 -1
- {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.116.dist-info}/entry_points.txt +1 -2
- jarvis/jarvis_dev/main.py +0 -664
- jarvis/multi_agent.py +0 -76
- jarvis/utils/date_utils.py +0 -19
- jarvis_ai_assistant-0.1.115.dist-info/RECORD +0 -64
- {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.116.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.116.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import re
|
|
3
3
|
import time
|
|
4
|
-
from typing import Callable, Dict, List, Optional
|
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
5
5
|
|
|
6
6
|
from prompt_toolkit import prompt
|
|
7
7
|
import yaml
|
|
8
8
|
|
|
9
|
+
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
9
10
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
10
11
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
11
12
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
12
|
-
from jarvis.
|
|
13
|
+
from jarvis.jarvis_utils import PrettyOutput, OutputType, get_context_token_count, is_auto_complete, is_execute_tool_confirm, is_need_summary, is_record_methodology, load_methodology, set_agent, delete_agent, get_max_token_count, get_multiline_input, init_env, is_use_methodology, make_agent_name, user_confirm
|
|
13
14
|
import os
|
|
14
15
|
|
|
15
16
|
class Agent:
|
|
@@ -25,32 +26,22 @@ class Agent:
|
|
|
25
26
|
def __del__(self):
|
|
26
27
|
delete_agent(self.name)
|
|
27
28
|
|
|
28
|
-
def set_output_handler_before_tool(self, handler: List[Callable]):
|
|
29
|
-
"""Set handlers to process output before tool execution.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
handler: List of callable functions to process output
|
|
33
|
-
"""
|
|
34
|
-
self.output_handler_before_tool = handler
|
|
35
29
|
|
|
36
30
|
def __init__(self,
|
|
37
31
|
system_prompt: str,
|
|
38
32
|
name: str = "Jarvis",
|
|
39
33
|
description: str = "",
|
|
40
34
|
is_sub_agent: bool = False,
|
|
41
|
-
|
|
42
|
-
platform: Optional[BasePlatform]|Optional[str] = None,
|
|
35
|
+
platform: Union[Optional[BasePlatform], Optional[str]] = None,
|
|
43
36
|
model_name: Optional[str] = None,
|
|
44
37
|
summary_prompt: Optional[str] = None,
|
|
45
38
|
auto_complete: Optional[bool] = None,
|
|
46
|
-
|
|
47
|
-
output_handler_after_tool: Optional[List[Callable]] = None,
|
|
39
|
+
output_handler: List[OutputHandler] = [],
|
|
48
40
|
input_handler: Optional[List[Callable]] = None,
|
|
49
41
|
use_methodology: Optional[bool] = None,
|
|
50
42
|
record_methodology: Optional[bool] = None,
|
|
51
43
|
need_summary: Optional[bool] = None,
|
|
52
44
|
max_context_length: Optional[int] = None,
|
|
53
|
-
support_send_msg: bool = False,
|
|
54
45
|
execute_tool_confirm: Optional[bool] = None):
|
|
55
46
|
"""Initialize an Agent instance.
|
|
56
47
|
|
|
@@ -91,15 +82,7 @@ class Agent:
|
|
|
91
82
|
self.model.set_model_name(model_name)
|
|
92
83
|
|
|
93
84
|
|
|
94
|
-
|
|
95
|
-
if tool_registry is not None:
|
|
96
|
-
if isinstance(tool_registry, ToolRegistry):
|
|
97
|
-
self.tool_registry = tool_registry
|
|
98
|
-
elif isinstance(tool_registry, List):
|
|
99
|
-
self.tool_registry = ToolRegistry()
|
|
100
|
-
self.tool_registry.use_tools(tool_registry)
|
|
101
|
-
else:
|
|
102
|
-
self.tool_registry = ToolRegistry()
|
|
85
|
+
self.output_handler = output_handler
|
|
103
86
|
|
|
104
87
|
|
|
105
88
|
self.record_methodology = record_methodology if record_methodology is not None else is_record_methodology()
|
|
@@ -111,10 +94,7 @@ class Agent:
|
|
|
111
94
|
self.need_summary = need_summary if need_summary is not None else is_need_summary()
|
|
112
95
|
self.input_handler = input_handler if input_handler is not None else []
|
|
113
96
|
# Load configuration from environment variables
|
|
114
|
-
self.output_handler_before_tool = output_handler_before_tool if output_handler_before_tool else []
|
|
115
|
-
self.output_handler_after_tool = output_handler_after_tool if output_handler_after_tool else []
|
|
116
97
|
|
|
117
|
-
self.support_send_msg = support_send_msg
|
|
118
98
|
|
|
119
99
|
self.execute_tool_confirm = execute_tool_confirm if execute_tool_confirm is not None else is_execute_tool_confirm()
|
|
120
100
|
|
|
@@ -132,48 +112,14 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
132
112
|
self.max_token_count = max_context_length if max_context_length is not None else get_max_token_count()
|
|
133
113
|
self.auto_complete = auto_complete if auto_complete is not None else is_auto_complete()
|
|
134
114
|
welcome_message = f"{name} 初始化完成 - 使用 {self.model.name()} 模型"
|
|
135
|
-
|
|
136
|
-
if tools:
|
|
137
|
-
welcome_message += f"\n可用工具: \n{','.join([f'{tool['name']}' for tool in tools])}"
|
|
115
|
+
|
|
138
116
|
PrettyOutput.print(welcome_message, OutputType.SYSTEM)
|
|
139
117
|
|
|
140
|
-
|
|
118
|
+
tool_prompt = f"""You can do the following actions: {','.join([handler.name() for handler in self.output_handler])}"""
|
|
141
119
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
## Send Message Rules
|
|
146
|
-
|
|
147
|
-
!!! CRITICAL ACTION RULES !!!
|
|
148
|
-
You can ONLY perform ONE action per turn:
|
|
149
|
-
- ENSURE USE ONLY ONE TOOL EVERY TURN (file_operation, ask_user, etc.)
|
|
150
|
-
- OR SEND ONE MESSAGE TO ANOTHER AGENT
|
|
151
|
-
- NEVER DO BOTH IN THE SAME TURN
|
|
152
|
-
|
|
153
|
-
2. Message Format:
|
|
154
|
-
<SEND_MESSAGE>
|
|
155
|
-
to: agent_name # Target agent name
|
|
156
|
-
content: |
|
|
157
|
-
message_content # Message content, multi-line must be separated by newlines
|
|
158
|
-
</SEND_MESSAGE>
|
|
159
|
-
|
|
160
|
-
3. Message Handling:
|
|
161
|
-
- After sending a message, WAIT for response
|
|
162
|
-
- Process response before next action
|
|
163
|
-
- Never send multiple messages at once
|
|
164
|
-
- Never combine message with tool calls
|
|
165
|
-
|
|
166
|
-
4. If Multiple Actions Needed:
|
|
167
|
-
a. Choose most important action first
|
|
168
|
-
b. Wait for response/result
|
|
169
|
-
c. Plan next action based on response
|
|
170
|
-
d. Execute next action in new turn
|
|
171
|
-
|
|
172
|
-
Remember:
|
|
173
|
-
- First action will be executed
|
|
174
|
-
- Additional actions will be IGNORED
|
|
175
|
-
- Always process responses before new actions
|
|
176
|
-
"""
|
|
120
|
+
for handler in self.output_handler:
|
|
121
|
+
tool_prompt += f"\n## {handler.name()}:\n"
|
|
122
|
+
tool_prompt += handler.prompt()
|
|
177
123
|
|
|
178
124
|
complete_prompt = ""
|
|
179
125
|
if self.auto_complete:
|
|
@@ -183,69 +129,16 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
183
129
|
<!!!COMPLETE!!!>
|
|
184
130
|
"""
|
|
185
131
|
|
|
186
|
-
additional_prompt = ""
|
|
187
|
-
if not tools_prompt and send_msg_prompt:
|
|
188
|
-
additional_prompt = """YOU MUST CALL ONE TOOL EVERY TURN, UNLESS YOU THINK THE TASK IS COMPLETED!!!"""
|
|
189
|
-
if not send_msg_prompt and tools_prompt:
|
|
190
|
-
additional_prompt = """YOU MUST SEND ONE MESSAGE EVERY TURN, UNLESS YOU THINK THE TASK IS COMPLETED!!!"""
|
|
191
|
-
if send_msg_prompt and tools_prompt:
|
|
192
|
-
additional_prompt = """YOU MUST CALL ONE TOOL OR SEND ONE MESSAGE EVERY TURN, UNLESS YOU THINK THE TASK IS COMPLETED!!!"""
|
|
193
|
-
|
|
194
132
|
self.model.set_system_message(f"""
|
|
195
133
|
{self.system_prompt}
|
|
196
134
|
|
|
197
|
-
{
|
|
198
|
-
|
|
199
|
-
{send_msg_prompt}
|
|
135
|
+
{tool_prompt}
|
|
200
136
|
|
|
201
137
|
{complete_prompt}
|
|
202
|
-
|
|
203
|
-
{additional_prompt}
|
|
204
138
|
""")
|
|
205
139
|
self.first = True
|
|
206
140
|
|
|
207
|
-
@staticmethod
|
|
208
|
-
def _extract_send_msg(content: str) -> List[Dict]:
|
|
209
|
-
"""Extract send message from content.
|
|
210
|
-
|
|
211
|
-
Args:
|
|
212
|
-
content: The content containing send message
|
|
213
|
-
"""
|
|
214
|
-
data = re.findall(r'<SEND_MESSAGE>(.*?)</SEND_MESSAGE>', content, re.DOTALL)
|
|
215
|
-
ret = []
|
|
216
|
-
for item in data:
|
|
217
|
-
try:
|
|
218
|
-
msg = yaml.safe_load(item)
|
|
219
|
-
if 'to' in msg and 'content' in msg:
|
|
220
|
-
ret.append(msg)
|
|
221
|
-
except Exception as e:
|
|
222
|
-
continue
|
|
223
|
-
return ret
|
|
224
141
|
|
|
225
|
-
@staticmethod
|
|
226
|
-
def _extract_tool_calls(content: str) -> List[Dict]:
|
|
227
|
-
"""Extract tool calls from content.
|
|
228
|
-
|
|
229
|
-
Args:
|
|
230
|
-
content: The content containing tool calls
|
|
231
|
-
|
|
232
|
-
Returns:
|
|
233
|
-
List[Dict]: List of extracted tool calls with name and arguments
|
|
234
|
-
|
|
235
|
-
Raises:
|
|
236
|
-
Exception: If tool call is missing necessary fields
|
|
237
|
-
"""
|
|
238
|
-
# Split content into lines
|
|
239
|
-
data = re.findall(r'<TOOL_CALL>(.*?)</TOOL_CALL>', content, re.DOTALL)
|
|
240
|
-
ret = []
|
|
241
|
-
for item in data:
|
|
242
|
-
try:
|
|
243
|
-
msg = yaml.safe_load(item)
|
|
244
|
-
if 'name' in msg and 'arguments' in msg:
|
|
245
|
-
ret.append(msg)
|
|
246
|
-
except Exception as e:
|
|
247
|
-
continue
|
|
248
|
-
return ret
|
|
249
142
|
|
|
250
143
|
def _call_model(self, message: str) -> str:
|
|
251
144
|
"""Call the AI model with retry logic.
|
|
@@ -322,6 +215,21 @@ Please continue the task based on the above information.
|
|
|
322
215
|
except Exception as e:
|
|
323
216
|
PrettyOutput.print(f"总结对话历史失败: {str(e)}", OutputType.ERROR)
|
|
324
217
|
|
|
218
|
+
def _call_tools(self, response: str) -> Tuple[bool, Any]:
|
|
219
|
+
tool_list = []
|
|
220
|
+
for handler in self.output_handler:
|
|
221
|
+
if handler.can_handle(response):
|
|
222
|
+
tool_list.append(handler)
|
|
223
|
+
if len(tool_list) > 1:
|
|
224
|
+
PrettyOutput.print(f"操作失败:检测到多个操作。一次只能执行一个操作。尝试执行的操作:{', '.join([handler.name() for handler in tool_list])}", OutputType.ERROR)
|
|
225
|
+
return False, f"Action failed: Multiple actions detected. Please only perform one action at a time. Actions attempted: {', '.join([handler.name() for handler in tool_list])}"
|
|
226
|
+
if len(tool_list) == 0:
|
|
227
|
+
return False, ""
|
|
228
|
+
if not self.execute_tool_confirm or user_confirm(f"需要执行{tool_list[0].name()}确认执行?", True):
|
|
229
|
+
return tool_list[0].handle(response)
|
|
230
|
+
return False, ""
|
|
231
|
+
|
|
232
|
+
|
|
325
233
|
def _complete_task(self) -> str:
|
|
326
234
|
"""Complete the current task and generate summary if needed.
|
|
327
235
|
|
|
@@ -349,13 +257,7 @@ Please continue the task based on the above information.
|
|
|
349
257
|
self.prompt = analysis_prompt
|
|
350
258
|
response = self._call_model(self.prompt)
|
|
351
259
|
|
|
352
|
-
|
|
353
|
-
try:
|
|
354
|
-
tool_calls = Agent._extract_tool_calls(response)
|
|
355
|
-
if tool_calls:
|
|
356
|
-
self.tool_registry.handle_tool_calls(tool_calls[0])
|
|
357
|
-
except Exception as e:
|
|
358
|
-
PrettyOutput.print(f"处理方法论生成失败: {str(e)}", OutputType.ERROR)
|
|
260
|
+
self._call_tools(response)
|
|
359
261
|
|
|
360
262
|
except Exception as e:
|
|
361
263
|
PrettyOutput.print(f"生成方法论失败: {str(e)}", OutputType.ERROR)
|
|
@@ -369,7 +271,7 @@ Please continue the task based on the above information.
|
|
|
369
271
|
return "任务完成"
|
|
370
272
|
|
|
371
273
|
|
|
372
|
-
def run(self, user_input: str, file_list: Optional[List[str]] = None) ->
|
|
274
|
+
def run(self, user_input: str, file_list: Optional[List[str]] = None) -> Any:
|
|
373
275
|
"""Process user input and execute the task.
|
|
374
276
|
|
|
375
277
|
Args:
|
|
@@ -412,38 +314,10 @@ Please continue the task based on the above information.
|
|
|
412
314
|
self.prompt = ""
|
|
413
315
|
self.conversation_length += get_context_token_count(current_response)
|
|
414
316
|
|
|
415
|
-
|
|
416
|
-
send_msg = Agent._extract_send_msg(current_response)
|
|
417
|
-
tool_calls = Agent._extract_tool_calls(current_response)
|
|
317
|
+
need_return, self.prompt = self._call_tools(current_response)
|
|
418
318
|
|
|
419
|
-
if
|
|
420
|
-
|
|
421
|
-
self.prompt += f"Only one message can be sent, but {len(send_msg)} messages were received. Please retry."
|
|
422
|
-
continue
|
|
423
|
-
if len(tool_calls) > 1:
|
|
424
|
-
PrettyOutput.print(f"收到多个工具调用,违反规则,重试...", OutputType.WARNING)
|
|
425
|
-
self.prompt += f"Only one tool call can be executed, but {len(tool_calls)} tool calls were received. Please retry."
|
|
426
|
-
continue
|
|
427
|
-
if send_msg and tool_calls:
|
|
428
|
-
PrettyOutput.print(f"收到消息和工具调用,违反规则,重试...", OutputType.WARNING)
|
|
429
|
-
self.prompt += f"Only one action can be performed, but both a message and a tool call were received. Please retry."
|
|
430
|
-
continue
|
|
431
|
-
|
|
432
|
-
if send_msg:
|
|
433
|
-
return send_msg[0] # Return the first message
|
|
434
|
-
|
|
435
|
-
# If no message, process tool calls
|
|
436
|
-
for handler in self.output_handler_before_tool:
|
|
437
|
-
self.prompt += handler(current_response)
|
|
438
|
-
|
|
439
|
-
if tool_calls:
|
|
440
|
-
if not self.execute_tool_confirm or user_confirm(f"执行工具调用: {tool_calls[0]['name']}?"):
|
|
441
|
-
PrettyOutput.print("正在执行工具调用...", OutputType.PROGRESS)
|
|
442
|
-
tool_result = self.tool_registry.handle_tool_calls(tool_calls[0])
|
|
443
|
-
self.prompt += tool_result
|
|
444
|
-
|
|
445
|
-
for handler in self.output_handler_after_tool:
|
|
446
|
-
self.prompt += handler(current_response)
|
|
319
|
+
if need_return:
|
|
320
|
+
return self.prompt
|
|
447
321
|
|
|
448
322
|
if self.prompt:
|
|
449
323
|
continue
|
|
@@ -605,11 +479,13 @@ def main():
|
|
|
605
479
|
init_env()
|
|
606
480
|
parser = argparse.ArgumentParser(description='Jarvis AI assistant')
|
|
607
481
|
parser.add_argument('-f', '--files', nargs='*', help='List of files to process')
|
|
482
|
+
parser.add_argument('-p', '--platform', type=str, help='Platform to use')
|
|
483
|
+
parser.add_argument('-m', '--model', type=str, help='Model to use')
|
|
608
484
|
args = parser.parse_args()
|
|
609
485
|
|
|
610
486
|
try:
|
|
611
487
|
# 获取全局模型实例
|
|
612
|
-
agent = Agent(system_prompt=origin_agent_system_prompt)
|
|
488
|
+
agent = Agent(system_prompt=origin_agent_system_prompt, platform=args.platform, model_name=args.model, output_handler=[ToolRegistry()])
|
|
613
489
|
|
|
614
490
|
# 加载预定义任务
|
|
615
491
|
tasks = _load_tasks()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any, Tuple
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OutputHandler(ABC):
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def handle(self, response: str) -> Tuple[bool, Any]:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def can_handle(self, response: str) -> bool:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def prompt(self) -> str:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def name(self) -> str:
|
|
23
|
+
pass
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Dict, List
|
|
3
3
|
|
|
4
|
-
from jarvis.
|
|
5
|
-
from jarvis.jarvis_code_agent.
|
|
4
|
+
from jarvis.jarvis_agent import Agent
|
|
5
|
+
from jarvis.jarvis_code_agent.file_select import file_input_handler
|
|
6
|
+
from jarvis.jarvis_code_agent.patch import PatchOutputHandler
|
|
6
7
|
from jarvis.jarvis_code_agent.relevant_files import find_relevant_information
|
|
7
8
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
8
9
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
9
10
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
10
|
-
from jarvis.
|
|
11
|
+
from jarvis.jarvis_tools.read_code import ReadCodeTool
|
|
12
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, init_env, find_git_root
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class CodeAgent:
|
|
@@ -49,12 +51,6 @@ class CodeAgent:
|
|
|
49
51
|
• Exceptions must be documented
|
|
50
52
|
• Complex logic must be explained
|
|
51
53
|
|
|
52
|
-
# Patch Format
|
|
53
|
-
<PATCH>
|
|
54
|
-
path/file start,end
|
|
55
|
-
new_content
|
|
56
|
-
</PATCH>
|
|
57
|
-
|
|
58
54
|
Key Rules:
|
|
59
55
|
• One modification per patch block
|
|
60
56
|
• Line numbers are based on original file
|
|
@@ -129,10 +125,10 @@ Every Change Must:
|
|
|
129
125
|
auto_complete=False,
|
|
130
126
|
is_sub_agent=False,
|
|
131
127
|
use_methodology=False,
|
|
132
|
-
|
|
128
|
+
output_handler=[tool_registry, PatchOutputHandler()],
|
|
133
129
|
platform=PlatformRegistry().get_codegen_platform(),
|
|
134
130
|
record_methodology=False,
|
|
135
|
-
|
|
131
|
+
input_handler=[file_input_handler],
|
|
136
132
|
need_summary=False)
|
|
137
133
|
|
|
138
134
|
|
|
@@ -160,6 +156,10 @@ Every Change Must:
|
|
|
160
156
|
# Then try to add file contents
|
|
161
157
|
for file in files:
|
|
162
158
|
prompt_parts.append(f'''- {file['file']} ({file['reason']})''')
|
|
159
|
+
|
|
160
|
+
result = ReadCodeTool().execute({"files": [{"file": file["file"], "reason": file["reason"]} for file in files]})
|
|
161
|
+
if result["success"]:
|
|
162
|
+
prompt_parts.append(result["stdout"])
|
|
163
163
|
|
|
164
164
|
return "\n".join(prompt_parts)
|
|
165
165
|
|
|
@@ -3,7 +3,8 @@ import re
|
|
|
3
3
|
from typing import Dict, List
|
|
4
4
|
from prompt_toolkit import PromptSession
|
|
5
5
|
from prompt_toolkit.completion import Completer, Completion
|
|
6
|
-
from jarvis.
|
|
6
|
+
from jarvis.jarvis_tools.read_code import ReadCodeTool
|
|
7
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput, get_single_line_input, user_confirm
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
|
|
@@ -135,6 +136,9 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
135
136
|
for i, file in enumerate(related_files, 1):
|
|
136
137
|
output += f"[{i}] {file['file']} ({file['reason']})\n"
|
|
137
138
|
|
|
139
|
+
# Filter out files that do not exist
|
|
140
|
+
related_files = [f for f in related_files if os.path.isfile(os.path.join(root_dir, f["file"]))]
|
|
141
|
+
|
|
138
142
|
PrettyOutput.print(output, OutputType.INFO, lang="markdown")
|
|
139
143
|
|
|
140
144
|
if len(related_files) > 0:
|
|
@@ -175,9 +179,10 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
175
179
|
continue
|
|
176
180
|
|
|
177
181
|
# Display matching files
|
|
178
|
-
|
|
182
|
+
tips = "找到以下匹配的文件:"
|
|
179
183
|
for i, path in enumerate(matches, 1):
|
|
180
|
-
|
|
184
|
+
tips += f"\n[{i}] {path}"
|
|
185
|
+
PrettyOutput.print(tips, OutputType.INFO)
|
|
181
186
|
|
|
182
187
|
# Let the user select
|
|
183
188
|
numbers = get_single_line_input("请选择要添加的文件编号(支持: 1,3-6格式, 按回车选择所有)").strip()
|
|
@@ -192,16 +197,32 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
192
197
|
paths_to_add = [file_path]
|
|
193
198
|
|
|
194
199
|
# Add selected files
|
|
200
|
+
tips = "添加以下文件:"
|
|
195
201
|
for path in paths_to_add:
|
|
196
202
|
full_path = os.path.join(root_dir, path)
|
|
197
203
|
if not os.path.isfile(full_path):
|
|
198
|
-
|
|
204
|
+
tips += f"\n文件不存在: {path}"
|
|
199
205
|
continue
|
|
200
206
|
|
|
201
207
|
try:
|
|
202
208
|
selected_files.append({"file": path, "reason": "User Added"})
|
|
203
|
-
|
|
209
|
+
tips += f"\n文件已添加: {path}"
|
|
204
210
|
except Exception as e:
|
|
205
|
-
|
|
211
|
+
tips += f"\n读取文件失败: {str(e)}"
|
|
212
|
+
selected_files = [f for f in selected_files if os.path.isfile(os.path.join(root_dir, f["file"]))]
|
|
213
|
+
PrettyOutput.print(tips, OutputType.INFO)
|
|
214
|
+
return selected_files
|
|
215
|
+
|
|
216
|
+
def file_input_handler(user_input: str) -> str:
|
|
217
|
+
prompt = user_input
|
|
218
|
+
files = []
|
|
219
|
+
sm = re.findall(r'`(.+?)`', user_input)
|
|
220
|
+
if sm:
|
|
221
|
+
for s in sm:
|
|
222
|
+
if os.path.isfile(s[0]):
|
|
223
|
+
files.append(s[0])
|
|
224
|
+
result = ReadCodeTool().execute({"files": files})
|
|
225
|
+
if result["success"]:
|
|
226
|
+
return result["stdout"] + "\n" + prompt
|
|
206
227
|
|
|
207
|
-
return
|
|
228
|
+
return prompt
|
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
import re
|
|
2
|
-
from typing import Dict, Any, List
|
|
2
|
+
from typing import Dict, Any, List, Tuple
|
|
3
3
|
import os
|
|
4
|
+
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
4
5
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
5
|
-
from jarvis.
|
|
6
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, user_confirm
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PatchOutputHandler(OutputHandler):
|
|
10
|
+
def name(self) -> str:
|
|
11
|
+
return "PATCH"
|
|
12
|
+
|
|
13
|
+
def handle(self, response: str) -> Tuple[bool, Any]:
|
|
14
|
+
return False, apply_patch(response)
|
|
15
|
+
|
|
16
|
+
def can_handle(self, response: str) -> bool:
|
|
17
|
+
if _parse_patch(response):
|
|
18
|
+
return True
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
def prompt(self) -> str:
|
|
22
|
+
return """
|
|
23
|
+
<PATCH>
|
|
24
|
+
path/to/file start,end
|
|
25
|
+
new_content
|
|
26
|
+
</PATCH>
|
|
27
|
+
|
|
28
|
+
"""
|
|
6
29
|
|
|
7
30
|
|
|
8
31
|
def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
|
|
@@ -5,7 +5,7 @@ from typing import Dict, List, Optional, Tuple
|
|
|
5
5
|
from jarvis.jarvis_code_agent.file_select import select_files
|
|
6
6
|
from jarvis.jarvis_codebase.main import CodeBase
|
|
7
7
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
8
|
-
from jarvis.
|
|
8
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput
|
|
9
9
|
|
|
10
10
|
def make_question(requirement: str) -> Optional[str]:
|
|
11
11
|
"""Generate structured questions to gather necessary information for the requirement."""
|
jarvis/jarvis_codebase/main.py
CHANGED
|
@@ -7,8 +7,8 @@ from typing import List, Tuple, Optional, Dict
|
|
|
7
7
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
8
8
|
import concurrent.futures
|
|
9
9
|
from concurrent.futures import ThreadPoolExecutor
|
|
10
|
-
from jarvis.
|
|
11
|
-
from jarvis.
|
|
10
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput, find_git_root, get_context_token_count, get_embedding, get_file_md5, get_max_token_count, get_thread_count, load_embedding_model, user_confirm
|
|
11
|
+
from jarvis.jarvis_utils import init_env
|
|
12
12
|
import argparse
|
|
13
13
|
import pickle
|
|
14
14
|
import lzma # 添加 lzma 导入
|
jarvis/jarvis_lsp/cpp.py
CHANGED
|
@@ -4,7 +4,7 @@ import subprocess
|
|
|
4
4
|
from typing import List, Dict, Optional, Tuple, Any
|
|
5
5
|
import json
|
|
6
6
|
from jarvis.jarvis_lsp.base import BaseLSP
|
|
7
|
-
from jarvis.
|
|
7
|
+
from jarvis.jarvis_utils import PrettyOutput, OutputType
|
|
8
8
|
|
|
9
9
|
class CPPLSP(BaseLSP):
|
|
10
10
|
"""C++ LSP implementation using clangd."""
|
jarvis/jarvis_lsp/go.py
CHANGED
|
@@ -4,7 +4,7 @@ import subprocess
|
|
|
4
4
|
from typing import List, Dict, Optional, Tuple, Any
|
|
5
5
|
import json
|
|
6
6
|
from jarvis.jarvis_lsp.base import BaseLSP
|
|
7
|
-
from jarvis.
|
|
7
|
+
from jarvis.jarvis_utils import PrettyOutput, OutputType
|
|
8
8
|
|
|
9
9
|
class GoLSP(BaseLSP):
|
|
10
10
|
"""Go LSP implementation using gopls."""
|
jarvis/jarvis_lsp/registry.py
CHANGED
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
import sys
|
|
6
6
|
from typing import Dict, Type, Optional, List
|
|
7
7
|
from jarvis.jarvis_lsp.base import BaseLSP
|
|
8
|
-
from jarvis.
|
|
8
|
+
from jarvis.jarvis_utils import PrettyOutput, OutputType
|
|
9
9
|
|
|
10
10
|
REQUIRED_METHODS = [
|
|
11
11
|
('initialize', ['workspace_path']),
|
jarvis/jarvis_lsp/rust.py
CHANGED
|
@@ -4,7 +4,7 @@ import subprocess
|
|
|
4
4
|
from typing import List, Dict, Optional, Tuple, Any
|
|
5
5
|
import json
|
|
6
6
|
from jarvis.jarvis_lsp.base import BaseLSP
|
|
7
|
-
from jarvis.
|
|
7
|
+
from jarvis.jarvis_utils import PrettyOutput, OutputType
|
|
8
8
|
|
|
9
9
|
class RustLSP(BaseLSP):
|
|
10
10
|
"""Rust LSP implementation using rust-analyzer."""
|