jarvis-ai-assistant 0.1.113__py3-none-any.whl → 0.1.114__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 +130 -74
- jarvis/jarvis_code_agent/code_agent.py +1 -1
- jarvis/jarvis_code_agent/patch.py +2 -4
- jarvis/jarvis_codebase/main.py +2 -0
- jarvis/jarvis_dev/main.py +664 -0
- jarvis/jarvis_platform/ai8.py +1 -1
- jarvis/jarvis_platform/openai.py +9 -1
- jarvis/jarvis_tools/file_operation.py +89 -57
- jarvis/jarvis_tools/read_code.py +92 -41
- jarvis/jarvis_tools/registry.py +9 -16
- jarvis/multi_agent.py +76 -0
- jarvis/utils.py +24 -9
- {jarvis_ai_assistant-0.1.113.dist-info → jarvis_ai_assistant-0.1.114.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.1.113.dist-info → jarvis_ai_assistant-0.1.114.dist-info}/RECORD +19 -19
- {jarvis_ai_assistant-0.1.113.dist-info → jarvis_ai_assistant-0.1.114.dist-info}/entry_points.txt +1 -0
- jarvis/jarvis_tools/deep_thinking.py +0 -160
- jarvis/jarvis_tools/deep_thinking_agent.py +0 -146
- {jarvis_ai_assistant-0.1.113.dist-info → jarvis_ai_assistant-0.1.114.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.113.dist-info → jarvis_ai_assistant-0.1.114.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.113.dist-info → jarvis_ai_assistant-0.1.114.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/agent.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
|
|
2
|
+
import re
|
|
3
3
|
import time
|
|
4
4
|
from typing import Callable, Dict, List, Optional
|
|
5
5
|
|
|
@@ -8,8 +8,8 @@ import yaml
|
|
|
8
8
|
|
|
9
9
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
10
10
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
11
|
-
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
12
|
-
from jarvis.utils import PrettyOutput, OutputType, get_context_token_count, is_auto_complete, is_execute_tool_confirm, is_need_summary, is_record_methodology, load_methodology,
|
|
11
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
12
|
+
from 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
13
|
import os
|
|
14
14
|
|
|
15
15
|
class Agent:
|
|
@@ -22,6 +22,9 @@ class Agent:
|
|
|
22
22
|
"""
|
|
23
23
|
self.summary_prompt = summary_prompt
|
|
24
24
|
|
|
25
|
+
def __del__(self):
|
|
26
|
+
delete_agent(self.name)
|
|
27
|
+
|
|
25
28
|
def set_output_handler_before_tool(self, handler: List[Callable]):
|
|
26
29
|
"""Set handlers to process output before tool execution.
|
|
27
30
|
|
|
@@ -33,6 +36,7 @@ class Agent:
|
|
|
33
36
|
def __init__(self,
|
|
34
37
|
system_prompt: str,
|
|
35
38
|
name: str = "Jarvis",
|
|
39
|
+
description: str = "",
|
|
36
40
|
is_sub_agent: bool = False,
|
|
37
41
|
tool_registry: Optional[ToolRegistry|List[str]] = None,
|
|
38
42
|
platform: Optional[BasePlatform]|Optional[str] = None,
|
|
@@ -46,12 +50,14 @@ class Agent:
|
|
|
46
50
|
record_methodology: Optional[bool] = None,
|
|
47
51
|
need_summary: Optional[bool] = None,
|
|
48
52
|
max_context_length: Optional[int] = None,
|
|
53
|
+
support_send_msg: bool = False,
|
|
49
54
|
execute_tool_confirm: Optional[bool] = None):
|
|
50
55
|
"""Initialize an Agent instance.
|
|
51
56
|
|
|
52
57
|
Args:
|
|
53
58
|
system_prompt: The system prompt defining agent behavior
|
|
54
59
|
name: Agent name, defaults to "Jarvis"
|
|
60
|
+
description: Agent description, defaults to ""
|
|
55
61
|
is_sub_agent: Whether this is a sub-agent
|
|
56
62
|
tool_registry: Registry of available tools
|
|
57
63
|
platform: AI platform to use
|
|
@@ -63,9 +69,12 @@ class Agent:
|
|
|
63
69
|
record_methodology: Whether to record methodology
|
|
64
70
|
need_summary: Whether to generate summaries
|
|
65
71
|
max_context_length: Maximum context length
|
|
72
|
+
support_send_msg: Whether to support sending messages
|
|
73
|
+
execute_tool_confirm: Whether to confirm tool execution
|
|
66
74
|
"""
|
|
67
|
-
PrettyOutput.print(f"欢迎使用Jarvis,你的AI助手,正在初始化...", OutputType.SYSTEM)
|
|
68
75
|
|
|
76
|
+
self.name = make_agent_name(name)
|
|
77
|
+
self.description = description
|
|
69
78
|
# 初始化平台和模型
|
|
70
79
|
if platform is not None:
|
|
71
80
|
if isinstance(platform, str):
|
|
@@ -95,7 +104,6 @@ class Agent:
|
|
|
95
104
|
|
|
96
105
|
self.record_methodology = record_methodology if record_methodology is not None else is_record_methodology()
|
|
97
106
|
self.use_methodology = use_methodology if use_methodology is not None else is_use_methodology()
|
|
98
|
-
self.name = name
|
|
99
107
|
self.is_sub_agent = is_sub_agent
|
|
100
108
|
self.prompt = ""
|
|
101
109
|
self.conversation_length = 0 # Use length counter instead
|
|
@@ -106,6 +114,8 @@ class Agent:
|
|
|
106
114
|
self.output_handler_before_tool = output_handler_before_tool if output_handler_before_tool else []
|
|
107
115
|
self.output_handler_after_tool = output_handler_after_tool if output_handler_after_tool else []
|
|
108
116
|
|
|
117
|
+
self.support_send_msg = support_send_msg
|
|
118
|
+
|
|
109
119
|
self.execute_tool_confirm = execute_tool_confirm if execute_tool_confirm is not None else is_execute_tool_confirm()
|
|
110
120
|
|
|
111
121
|
self.summary_prompt = summary_prompt if summary_prompt else f"""Please generate a concise summary report of the task execution, including:
|
|
@@ -120,18 +130,52 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
120
130
|
"""
|
|
121
131
|
|
|
122
132
|
self.max_token_count = max_context_length if max_context_length is not None else get_max_token_count()
|
|
123
|
-
|
|
124
133
|
self.auto_complete = auto_complete if auto_complete is not None else is_auto_complete()
|
|
125
|
-
|
|
126
|
-
PrettyOutput.section(f"Jarvis 初始化完成 - 使用 {self.model.name()} 模型", OutputType.SYSTEM)
|
|
127
|
-
|
|
134
|
+
welcome_message = f"{name} 初始化完成 - 使用 {self.model.name()} 模型"
|
|
128
135
|
tools = self.tool_registry.get_all_tools()
|
|
129
136
|
if tools:
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
welcome_message += f"\n可用工具: \n{','.join([f'{tool['name']}' for tool in tools])}"
|
|
138
|
+
PrettyOutput.print(welcome_message, OutputType.SYSTEM)
|
|
132
139
|
|
|
133
140
|
tools_prompt = self.tool_registry.load_tools()
|
|
134
|
-
|
|
141
|
+
|
|
142
|
+
send_msg_prompt = ""
|
|
143
|
+
if self.support_send_msg:
|
|
144
|
+
send_msg_prompt = """
|
|
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
|
+
"""
|
|
177
|
+
|
|
178
|
+
complete_prompt = ""
|
|
135
179
|
if self.auto_complete:
|
|
136
180
|
complete_prompt = """
|
|
137
181
|
## Task Completion
|
|
@@ -139,15 +183,45 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
139
183
|
<!!!COMPLETE!!!>
|
|
140
184
|
"""
|
|
141
185
|
|
|
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
|
+
|
|
142
194
|
self.model.set_system_message(f"""
|
|
143
195
|
{self.system_prompt}
|
|
144
196
|
|
|
145
197
|
{tools_prompt}
|
|
146
198
|
|
|
199
|
+
{send_msg_prompt}
|
|
200
|
+
|
|
147
201
|
{complete_prompt}
|
|
202
|
+
|
|
203
|
+
{additional_prompt}
|
|
148
204
|
""")
|
|
149
205
|
self.first = True
|
|
150
206
|
|
|
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
|
+
|
|
151
225
|
@staticmethod
|
|
152
226
|
def _extract_tool_calls(content: str) -> List[Dict]:
|
|
153
227
|
"""Extract tool calls from content.
|
|
@@ -162,39 +236,17 @@ Please describe in concise bullet points, highlighting important information.
|
|
|
162
236
|
Exception: If tool call is missing necessary fields
|
|
163
237
|
"""
|
|
164
238
|
# Split content into lines
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
in_tool_call = True
|
|
174
|
-
continue
|
|
175
|
-
elif '</TOOL_CALL>' in line:
|
|
176
|
-
if in_tool_call and tool_call_lines:
|
|
177
|
-
# Parse YAML directly
|
|
178
|
-
tool_call_text = '\n'.join(tool_call_lines)
|
|
179
|
-
tool_call_data = yaml.safe_load(tool_call_text)
|
|
180
|
-
|
|
181
|
-
# Validate necessary fields
|
|
182
|
-
if "name" in tool_call_data and "arguments" in tool_call_data:
|
|
183
|
-
# Return content before tool call and tool call
|
|
184
|
-
return [{
|
|
185
|
-
"name": tool_call_data["name"],
|
|
186
|
-
"arguments": tool_call_data["arguments"]
|
|
187
|
-
}]
|
|
188
|
-
else:
|
|
189
|
-
raise Exception("Tool call missing necessary fields")
|
|
190
|
-
in_tool_call = False
|
|
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:
|
|
191
247
|
continue
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
tool_call_lines.append(line)
|
|
195
|
-
|
|
196
|
-
return []
|
|
197
|
-
|
|
248
|
+
return ret
|
|
249
|
+
|
|
198
250
|
def _call_model(self, message: str) -> str:
|
|
199
251
|
"""Call the AI model with retry logic.
|
|
200
252
|
|
|
@@ -301,7 +353,7 @@ Please continue the task based on the above information.
|
|
|
301
353
|
try:
|
|
302
354
|
tool_calls = Agent._extract_tool_calls(response)
|
|
303
355
|
if tool_calls:
|
|
304
|
-
self.tool_registry.handle_tool_calls(tool_calls)
|
|
356
|
+
self.tool_registry.handle_tool_calls(tool_calls[0])
|
|
305
357
|
except Exception as e:
|
|
306
358
|
PrettyOutput.print(f"处理方法论生成失败: {str(e)}", OutputType.ERROR)
|
|
307
359
|
|
|
@@ -317,7 +369,7 @@ Please continue the task based on the above information.
|
|
|
317
369
|
return "任务完成"
|
|
318
370
|
|
|
319
371
|
|
|
320
|
-
def run(self, user_input: str, file_list: Optional[List[str]] = None) -> str:
|
|
372
|
+
def run(self, user_input: str, file_list: Optional[List[str]] = None) -> str|Dict:
|
|
321
373
|
"""Process user input and execute the task.
|
|
322
374
|
|
|
323
375
|
Args:
|
|
@@ -325,18 +377,10 @@ Please continue the task based on the above information.
|
|
|
325
377
|
file_list: Optional list of files to process
|
|
326
378
|
|
|
327
379
|
Returns:
|
|
328
|
-
str: Task summary report
|
|
329
|
-
|
|
330
|
-
Note:
|
|
331
|
-
- Handles context management
|
|
332
|
-
- Processes tool calls
|
|
333
|
-
- Manages conversation flow
|
|
334
|
-
- Supports interactive mode
|
|
380
|
+
str|Dict: Task summary report or message to send
|
|
335
381
|
"""
|
|
336
|
-
|
|
337
|
-
add_agent(self.name)
|
|
338
|
-
|
|
339
382
|
try:
|
|
383
|
+
set_agent(self.name, self)
|
|
340
384
|
PrettyOutput.section("准备环境", OutputType.PLANNING)
|
|
341
385
|
if file_list:
|
|
342
386
|
self.model.upload_files(file_list) # type: ignore
|
|
@@ -344,11 +388,12 @@ Please continue the task based on the above information.
|
|
|
344
388
|
# 显示任务开始
|
|
345
389
|
PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
|
|
346
390
|
|
|
347
|
-
|
|
348
|
-
|
|
391
|
+
self.prompt = f"{user_input}"
|
|
392
|
+
|
|
393
|
+
if self.first:
|
|
394
|
+
if self.use_methodology:
|
|
395
|
+
self.prompt = f"{user_input}\n\n{load_methodology(user_input)}"
|
|
349
396
|
self.first = False
|
|
350
|
-
else:
|
|
351
|
-
self.prompt = f"{user_input}"
|
|
352
397
|
|
|
353
398
|
while True:
|
|
354
399
|
try:
|
|
@@ -367,22 +412,36 @@ Please continue the task based on the above information.
|
|
|
367
412
|
self.prompt = ""
|
|
368
413
|
self.conversation_length += get_context_token_count(current_response)
|
|
369
414
|
|
|
370
|
-
for
|
|
371
|
-
|
|
415
|
+
# Check for message first
|
|
416
|
+
send_msg = Agent._extract_send_msg(current_response)
|
|
417
|
+
tool_calls = Agent._extract_tool_calls(current_response)
|
|
372
418
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
419
|
+
if len(send_msg) > 1:
|
|
420
|
+
PrettyOutput.print(f"收到多个消息,违反规则,重试...", OutputType.WARNING)
|
|
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."
|
|
378
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)
|
|
379
438
|
|
|
380
|
-
if
|
|
381
|
-
if not self.execute_tool_confirm or user_confirm(f"执行工具调用: {
|
|
439
|
+
if tool_calls:
|
|
440
|
+
if not self.execute_tool_confirm or user_confirm(f"执行工具调用: {tool_calls[0]['name']}?"):
|
|
382
441
|
PrettyOutput.print("正在执行工具调用...", OutputType.PROGRESS)
|
|
383
|
-
tool_result = self.tool_registry.handle_tool_calls(
|
|
442
|
+
tool_result = self.tool_registry.handle_tool_calls(tool_calls[0])
|
|
384
443
|
self.prompt += tool_result
|
|
385
|
-
|
|
444
|
+
|
|
386
445
|
for handler in self.output_handler_after_tool:
|
|
387
446
|
self.prompt += handler(current_response)
|
|
388
447
|
|
|
@@ -409,9 +468,6 @@ Please continue the task based on the above information.
|
|
|
409
468
|
except Exception as e:
|
|
410
469
|
PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
|
|
411
470
|
return f"Task failed: {str(e)}"
|
|
412
|
-
|
|
413
|
-
finally:
|
|
414
|
-
delete_current_agent()
|
|
415
471
|
|
|
416
472
|
def _clear_history(self):
|
|
417
473
|
"""Clear conversation history while preserving system prompt.
|
|
@@ -8,7 +8,7 @@ from jarvis.utils import OutputType, PrettyOutput, get_multiline_input, has_unco
|
|
|
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
|
...
|
|
@@ -24,11 +24,9 @@ def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
|
|
|
24
24
|
|
|
25
25
|
# Parse file path and line range
|
|
26
26
|
file_info = lines[0].strip()
|
|
27
|
-
if not file_info.startswith('>'):
|
|
28
|
-
continue
|
|
29
27
|
|
|
30
28
|
# Extract file path and line range
|
|
31
|
-
match = re.match(r'
|
|
29
|
+
match = re.match(r'([^\s]+)\s+(\d+),(\d+)', file_info)
|
|
32
30
|
if not match:
|
|
33
31
|
continue
|
|
34
32
|
|
jarvis/jarvis_codebase/main.py
CHANGED
|
@@ -686,6 +686,8 @@ Please provide 10 search-optimized expressions in the specified format.
|
|
|
686
686
|
if question_match:
|
|
687
687
|
try:
|
|
688
688
|
variants = yaml.safe_load(question_match.group(1))
|
|
689
|
+
if not isinstance(variants, list):
|
|
690
|
+
variants = [str(variants)]
|
|
689
691
|
except Exception as e:
|
|
690
692
|
PrettyOutput.print(f"解析变体失败: {str(e)}", OutputType.ERROR)
|
|
691
693
|
|