jarvis-ai-assistant 0.1.2__py3-none-any.whl → 0.1.5__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/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/__pycache__/agent.cpython-313.pyc +0 -0
- jarvis/__pycache__/main.cpython-313.pyc +0 -0
- jarvis/__pycache__/utils.cpython-313.pyc +0 -0
- jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
- jarvis/agent.py +60 -27
- jarvis/main.py +38 -18
- jarvis/models.py +15 -17
- jarvis/tools/__init__.py +0 -6
- jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
- jarvis/tools/base.py +67 -49
- jarvis/tools/shell.py +16 -2
- jarvis/tools/sub_agent.py +79 -0
- jarvis/utils.py +25 -2
- jarvis/zte_llm.py +132 -0
- {jarvis_ai_assistant-0.1.2.dist-info → jarvis_ai_assistant-0.1.5.dist-info}/METADATA +6 -4
- jarvis_ai_assistant-0.1.5.dist-info/RECORD +38 -0
- {jarvis_ai_assistant-0.1.2.dist-info → jarvis_ai_assistant-0.1.5.dist-info}/WHEEL +1 -1
- jarvis/tools/rag.py +0 -154
- jarvis/tools/user_confirmation.py +0 -58
- jarvis/tools/user_interaction.py +0 -86
- jarvis_ai_assistant-0.1.2.dist-info/RECORD +0 -36
- {jarvis_ai_assistant-0.1.2.dist-info → jarvis_ai_assistant-0.1.5.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.2.dist-info → jarvis_ai_assistant-0.1.5.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
jarvis/agent.py
CHANGED
|
@@ -3,21 +3,32 @@ import subprocess
|
|
|
3
3
|
from typing import Dict, Any, List, Optional
|
|
4
4
|
from .tools import ToolRegistry
|
|
5
5
|
from .utils import Spinner, PrettyOutput, OutputType, get_multiline_input
|
|
6
|
-
from .models import BaseModel
|
|
6
|
+
from .models import BaseModel
|
|
7
7
|
import re
|
|
8
8
|
import os
|
|
9
9
|
from datetime import datetime
|
|
10
10
|
|
|
11
11
|
class Agent:
|
|
12
|
-
def __init__(self, model: BaseModel, tool_registry: ToolRegistry):
|
|
12
|
+
def __init__(self, model: BaseModel, tool_registry: ToolRegistry, name: str = "Jarvis"):
|
|
13
|
+
"""Initialize Agent with a model, optional tool registry and name"""
|
|
13
14
|
self.model = model
|
|
14
|
-
self.tool_registry = tool_registry
|
|
15
|
+
self.tool_registry = tool_registry or ToolRegistry(model)
|
|
16
|
+
self.name = name
|
|
15
17
|
# 编译正则表达式
|
|
16
18
|
self.tool_call_pattern = re.compile(r'<tool_call>\s*({[^}]+})\s*</tool_call>')
|
|
17
19
|
self.messages = [
|
|
18
20
|
{
|
|
19
21
|
"role": "system",
|
|
20
|
-
"content": """You are a rigorous AI assistant
|
|
22
|
+
"content": f"""You are {name}, a rigorous AI assistant that executes tasks step by step.
|
|
23
|
+
|
|
24
|
+
Key Principles:
|
|
25
|
+
1. Execute ONE step at a time
|
|
26
|
+
2. Wait for each step's result before planning the next
|
|
27
|
+
3. Use tools to obtain all data, no fabrication
|
|
28
|
+
4. Create sub-agents for independent subtasks
|
|
29
|
+
5. Think carefully before each action
|
|
30
|
+
|
|
31
|
+
""" + self.tool_registry.tool_help_text()
|
|
21
32
|
}
|
|
22
33
|
]
|
|
23
34
|
self.spinner = Spinner()
|
|
@@ -28,44 +39,38 @@ class Agent:
|
|
|
28
39
|
try:
|
|
29
40
|
return self.model.chat(
|
|
30
41
|
messages=messages,
|
|
31
|
-
tools=self.tool_registry.get_all_tools() if use_tools else
|
|
42
|
+
tools=self.tool_registry.get_all_tools() if use_tools else []
|
|
32
43
|
)
|
|
33
44
|
except Exception as e:
|
|
34
|
-
raise Exception(f"模型调用失败: {str(e)}")
|
|
45
|
+
raise Exception(f"{self.name}: 模型调用失败: {str(e)}")
|
|
35
46
|
finally:
|
|
36
47
|
self.spinner.stop()
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"""处理用户输入并返回响应"""
|
|
41
|
-
# 检查是否是结束命令
|
|
49
|
+
def run(self, user_input: str) -> str:
|
|
50
|
+
"""处理用户输入并返回响应,返回任务总结报告"""
|
|
42
51
|
self.clear_history()
|
|
43
52
|
self.messages.append({
|
|
44
53
|
"role": "user",
|
|
45
54
|
"content": user_input
|
|
46
55
|
})
|
|
56
|
+
|
|
47
57
|
while True:
|
|
48
58
|
try:
|
|
49
|
-
# 获取初始响应
|
|
50
59
|
response = self._call_model(self.messages)
|
|
51
60
|
current_response = response
|
|
52
61
|
|
|
53
|
-
# 将工具执行结果添加到对话
|
|
54
62
|
self.messages.append({
|
|
55
63
|
"role": "assistant",
|
|
56
64
|
"content": response["message"].get("content", ""),
|
|
57
65
|
"tool_calls": current_response["message"]["tool_calls"]
|
|
58
66
|
})
|
|
59
67
|
|
|
60
|
-
# 处理可能的多轮工具调用
|
|
61
68
|
if len(current_response["message"]["tool_calls"]) > 0:
|
|
62
|
-
# 添加当前助手响应到输出(如果有内容)
|
|
63
69
|
if current_response["message"].get("content"):
|
|
64
|
-
PrettyOutput.print(current_response[
|
|
70
|
+
PrettyOutput.print(f"{self.name}: {current_response['message']['content']}", OutputType.SYSTEM)
|
|
65
71
|
|
|
66
|
-
# 使用 ToolRegistry 的 handle_tool_calls 方法处理工具调用
|
|
67
72
|
tool_result = self.tool_registry.handle_tool_calls(current_response["message"]["tool_calls"])
|
|
68
|
-
PrettyOutput.print(tool_result, OutputType.RESULT)
|
|
73
|
+
PrettyOutput.print(f"{self.name} Tool Result: {tool_result}", OutputType.RESULT)
|
|
69
74
|
|
|
70
75
|
self.messages.append({
|
|
71
76
|
"role": "tool",
|
|
@@ -73,18 +78,46 @@ class Agent:
|
|
|
73
78
|
})
|
|
74
79
|
continue
|
|
75
80
|
|
|
76
|
-
# 添加最终响应到对话历史和输出
|
|
77
81
|
final_content = current_response["message"].get("content", "")
|
|
78
|
-
|
|
79
82
|
if final_content:
|
|
80
|
-
PrettyOutput.print(final_content, OutputType.SYSTEM)
|
|
81
|
-
|
|
83
|
+
PrettyOutput.print(f"{self.name}: {final_content}", OutputType.SYSTEM)
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
|
|
86
|
+
if not user_input:
|
|
87
|
+
PrettyOutput.print(f"{self.name}: 正在生成任务总结...", OutputType.INFO)
|
|
88
|
+
|
|
89
|
+
# 优化后的任务总结提示语
|
|
90
|
+
summary_prompt = {
|
|
91
|
+
"role": "user",
|
|
92
|
+
"content": """The task has been completed. Based on the previous analysis and execution results, provide a task summary including:
|
|
93
|
+
|
|
94
|
+
1. Key Information:
|
|
95
|
+
- Essential findings from analysis
|
|
96
|
+
- Important results from tool executions
|
|
97
|
+
- Critical data discovered
|
|
98
|
+
|
|
99
|
+
2. Task Results:
|
|
100
|
+
- Final outcome
|
|
101
|
+
- Actual achievements
|
|
102
|
+
- Concrete results
|
|
103
|
+
|
|
104
|
+
Focus only on facts and actual results. Be direct and concise."""
|
|
105
|
+
}
|
|
106
|
+
while True:
|
|
107
|
+
try:
|
|
108
|
+
summary_response = self._call_model(self.messages + [summary_prompt], use_tools=False)
|
|
109
|
+
summary = summary_response["message"].get("content", "")
|
|
110
|
+
|
|
111
|
+
PrettyOutput.print(f"==============={self.name} 任务总结===============", OutputType.INFO)
|
|
112
|
+
PrettyOutput.print(summary, OutputType.SYSTEM)
|
|
113
|
+
PrettyOutput.print("=" * (len(self.name) + 16), OutputType.INFO)
|
|
114
|
+
|
|
115
|
+
return summary
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
error_msg = f"{self.name}: 生成任务总结时出错: {str(e)}"
|
|
119
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
120
|
+
|
|
88
121
|
|
|
89
122
|
self.messages.append({
|
|
90
123
|
"role": "user",
|
|
@@ -92,7 +125,7 @@ class Agent:
|
|
|
92
125
|
})
|
|
93
126
|
|
|
94
127
|
except Exception as e:
|
|
95
|
-
error_msg = f"处理响应时出错: {str(e)}"
|
|
128
|
+
error_msg = f"{self.name}: 处理响应时出错: {str(e)}"
|
|
96
129
|
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
97
130
|
|
|
98
131
|
def clear_history(self):
|
jarvis/main.py
CHANGED
|
@@ -13,7 +13,8 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
13
13
|
from jarvis.agent import Agent
|
|
14
14
|
from jarvis.tools import ToolRegistry
|
|
15
15
|
from jarvis.models import DDGSModel, OllamaModel
|
|
16
|
-
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input
|
|
16
|
+
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, load_env_from_file
|
|
17
|
+
from jarvis.zte_llm import create_zte_llm
|
|
17
18
|
|
|
18
19
|
# 定义支持的平台和模型
|
|
19
20
|
SUPPORTED_PLATFORMS = {
|
|
@@ -24,35 +25,48 @@ SUPPORTED_PLATFORMS = {
|
|
|
24
25
|
"ddgs": {
|
|
25
26
|
"models": ["gpt-4o-mini", "claude-3-haiku", "llama-3.1-70b", "mixtral-8x7b"],
|
|
26
27
|
"default": "gpt-4o-mini"
|
|
28
|
+
},
|
|
29
|
+
"zte": {
|
|
30
|
+
"models": ["NebulaBiz", "nebulacoder", "NTele-72B"],
|
|
31
|
+
"default": "NebulaBiz"
|
|
27
32
|
}
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
def load_tasks() ->
|
|
35
|
+
def load_tasks() -> dict:
|
|
31
36
|
"""Load tasks from .jarvis file if it exists."""
|
|
32
37
|
if not os.path.exists(".jarvis"):
|
|
33
|
-
return
|
|
38
|
+
return {}
|
|
34
39
|
|
|
35
40
|
try:
|
|
36
41
|
with open(".jarvis", "r", encoding="utf-8") as f:
|
|
37
42
|
tasks = yaml.safe_load(f)
|
|
38
43
|
|
|
39
|
-
if not isinstance(tasks,
|
|
40
|
-
PrettyOutput.print("Warning: .jarvis file should contain a
|
|
41
|
-
return
|
|
44
|
+
if not isinstance(tasks, dict):
|
|
45
|
+
PrettyOutput.print("Warning: .jarvis file should contain a dictionary of task_name: task_description", OutputType.ERROR)
|
|
46
|
+
return {}
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
# Validate format and convert all values to strings
|
|
49
|
+
validated_tasks = {}
|
|
50
|
+
for name, desc in tasks.items():
|
|
51
|
+
if desc: # Ensure description is not empty
|
|
52
|
+
validated_tasks[str(name)] = str(desc)
|
|
53
|
+
|
|
54
|
+
return validated_tasks
|
|
44
55
|
except Exception as e:
|
|
45
56
|
PrettyOutput.print(f"Error loading .jarvis file: {str(e)}", OutputType.ERROR)
|
|
46
|
-
return
|
|
57
|
+
return {}
|
|
47
58
|
|
|
48
|
-
def select_task(tasks:
|
|
49
|
-
"""Let user select a task from the list or skip."""
|
|
59
|
+
def select_task(tasks: dict) -> str:
|
|
60
|
+
"""Let user select a task from the list or skip. Returns task description if selected."""
|
|
50
61
|
if not tasks:
|
|
51
62
|
return ""
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
# Convert tasks to list for ordered display
|
|
65
|
+
task_names = list(tasks.keys())
|
|
66
|
+
|
|
67
|
+
PrettyOutput.print("\nAvailable tasks:", OutputType.INFO)
|
|
68
|
+
for i, name in enumerate(task_names, 1):
|
|
69
|
+
PrettyOutput.print(f"[{i}] {name}", OutputType.INFO)
|
|
56
70
|
PrettyOutput.print("[0] Skip predefined tasks", OutputType.INFO)
|
|
57
71
|
|
|
58
72
|
while True:
|
|
@@ -64,8 +78,9 @@ def select_task(tasks: list) -> str:
|
|
|
64
78
|
choice = int(choice)
|
|
65
79
|
if choice == 0:
|
|
66
80
|
return ""
|
|
67
|
-
elif 1 <= choice <= len(
|
|
68
|
-
|
|
81
|
+
elif 1 <= choice <= len(task_names):
|
|
82
|
+
selected_name = task_names[choice - 1]
|
|
83
|
+
return tasks[selected_name] # Return the task description
|
|
69
84
|
else:
|
|
70
85
|
PrettyOutput.print("Invalid choice. Please try again.", OutputType.ERROR)
|
|
71
86
|
except ValueError:
|
|
@@ -79,7 +94,7 @@ def main():
|
|
|
79
94
|
parser.add_argument(
|
|
80
95
|
"--platform",
|
|
81
96
|
choices=list(SUPPORTED_PLATFORMS.keys()),
|
|
82
|
-
default="
|
|
97
|
+
default="ddgs",
|
|
83
98
|
help="选择运行平台 (默认: ollama)"
|
|
84
99
|
)
|
|
85
100
|
|
|
@@ -97,6 +112,8 @@ def main():
|
|
|
97
112
|
)
|
|
98
113
|
|
|
99
114
|
args = parser.parse_args()
|
|
115
|
+
|
|
116
|
+
load_env_from_file()
|
|
100
117
|
|
|
101
118
|
# 验证并设置默认模型
|
|
102
119
|
if args.model:
|
|
@@ -119,11 +136,14 @@ def main():
|
|
|
119
136
|
api_base=args.api_base
|
|
120
137
|
)
|
|
121
138
|
platform_name = f"Ollama ({args.model})"
|
|
122
|
-
|
|
139
|
+
elif args.platform == "ddgs": # ddgs
|
|
123
140
|
model = DDGSModel(model_name=args.model)
|
|
124
141
|
platform_name = f"DuckDuckGo Search ({args.model})"
|
|
142
|
+
elif args.platform == "zte": # zte
|
|
143
|
+
model = create_zte_llm(model_name=args.model)
|
|
144
|
+
platform_name = f"ZTE ({args.model})"
|
|
125
145
|
|
|
126
|
-
tool_registry = ToolRegistry()
|
|
146
|
+
tool_registry = ToolRegistry(model)
|
|
127
147
|
agent = Agent(model, tool_registry)
|
|
128
148
|
|
|
129
149
|
# 欢迎信息
|
jarvis/models.py
CHANGED
|
@@ -20,31 +20,29 @@ class BaseModel(ABC):
|
|
|
20
20
|
def extract_tool_calls(content: str) -> List[Dict]:
|
|
21
21
|
"""从内容中提取工具调用"""
|
|
22
22
|
tool_calls = []
|
|
23
|
-
#
|
|
24
|
-
pattern = re.compile(
|
|
25
|
-
r'<tool_call>\s*({(?:[^{}]|(?:{[^{}]*})|(?:{(?:[^{}]|{[^{}]*})*}))*})\s*</tool_call>',
|
|
26
|
-
re.DOTALL # 添加DOTALL标志以匹配跨行内容
|
|
27
|
-
)
|
|
23
|
+
# 使用非贪婪匹配来获取标签之间的所有内容
|
|
24
|
+
pattern = re.compile(r'<tool_call>(.*?)</tool_call>', re.DOTALL)
|
|
28
25
|
|
|
29
26
|
matches = pattern.finditer(content)
|
|
30
27
|
for match in matches:
|
|
31
28
|
try:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
# 提取并解析 JSON
|
|
30
|
+
tool_call_text = match.group(1).strip()
|
|
31
|
+
tool_call_data = json.loads(tool_call_text)
|
|
32
|
+
|
|
33
|
+
# 验证必要的字段
|
|
34
|
+
if "name" in tool_call_data and "arguments" in tool_call_data:
|
|
35
35
|
tool_calls.append({
|
|
36
36
|
"function": {
|
|
37
|
-
"name":
|
|
38
|
-
"arguments":
|
|
37
|
+
"name": tool_call_data["name"],
|
|
38
|
+
"arguments": tool_call_data["arguments"]
|
|
39
39
|
}
|
|
40
40
|
})
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
except
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
continue
|
|
47
|
-
|
|
41
|
+
except json.JSONDecodeError:
|
|
42
|
+
continue # 跳过无效的 JSON
|
|
43
|
+
except Exception:
|
|
44
|
+
continue # 跳过其他错误
|
|
45
|
+
|
|
48
46
|
return tool_calls
|
|
49
47
|
|
|
50
48
|
|
jarvis/tools/__init__.py
CHANGED
|
@@ -3,9 +3,6 @@ from .python_script import PythonScript
|
|
|
3
3
|
from .file_ops import FileOperationTool
|
|
4
4
|
from .search import SearchTool
|
|
5
5
|
from .shell import ShellTool
|
|
6
|
-
from .user_interaction import UserInteractionTool
|
|
7
|
-
from .user_confirmation import UserConfirmationTool
|
|
8
|
-
from .rag import RAGTool
|
|
9
6
|
from .webpage import WebpageTool
|
|
10
7
|
|
|
11
8
|
__all__ = [
|
|
@@ -15,8 +12,5 @@ __all__ = [
|
|
|
15
12
|
'FileOperationTool',
|
|
16
13
|
'SearchTool',
|
|
17
14
|
'ShellTool',
|
|
18
|
-
'UserInteractionTool',
|
|
19
|
-
'UserConfirmationTool',
|
|
20
|
-
'RAGTool',
|
|
21
15
|
'WebpageTool',
|
|
22
16
|
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
jarvis/tools/base.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Dict, Any, List, Optional, Callable
|
|
2
2
|
import json
|
|
3
3
|
from ..utils import PrettyOutput, OutputType
|
|
4
|
+
from ..models import BaseModel
|
|
4
5
|
|
|
5
6
|
class Tool:
|
|
6
7
|
def __init__(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
@@ -25,29 +26,25 @@ class Tool:
|
|
|
25
26
|
return self.func(arguments)
|
|
26
27
|
|
|
27
28
|
class ToolRegistry:
|
|
28
|
-
def __init__(self):
|
|
29
|
+
def __init__(self, model: BaseModel):
|
|
29
30
|
self.tools: Dict[str, Tool] = {}
|
|
31
|
+
self.model = model
|
|
30
32
|
self._register_default_tools()
|
|
31
33
|
|
|
32
34
|
def _register_default_tools(self):
|
|
33
35
|
"""注册所有默认工具"""
|
|
34
36
|
from .search import SearchTool
|
|
35
37
|
from .shell import ShellTool
|
|
36
|
-
from .user_interaction import UserInteractionTool
|
|
37
|
-
from .user_confirmation import UserConfirmationTool
|
|
38
38
|
from .python_script import PythonScriptTool
|
|
39
39
|
from .file_ops import FileOperationTool
|
|
40
|
-
from .rag import RAGTool
|
|
41
40
|
from .webpage import WebpageTool
|
|
41
|
+
from .sub_agent import SubAgentTool
|
|
42
42
|
|
|
43
43
|
tools = [
|
|
44
44
|
SearchTool(),
|
|
45
45
|
ShellTool(),
|
|
46
|
-
UserInteractionTool(),
|
|
47
|
-
UserConfirmationTool(),
|
|
48
46
|
PythonScriptTool(),
|
|
49
47
|
FileOperationTool(),
|
|
50
|
-
RAGTool(),
|
|
51
48
|
WebpageTool(),
|
|
52
49
|
]
|
|
53
50
|
|
|
@@ -59,6 +56,14 @@ class ToolRegistry:
|
|
|
59
56
|
func=tool.execute
|
|
60
57
|
)
|
|
61
58
|
|
|
59
|
+
sub_agent_tool = SubAgentTool(self.model)
|
|
60
|
+
self.register_tool(
|
|
61
|
+
name=sub_agent_tool.name,
|
|
62
|
+
description=sub_agent_tool.description,
|
|
63
|
+
parameters=sub_agent_tool.parameters,
|
|
64
|
+
func=sub_agent_tool.execute
|
|
65
|
+
)
|
|
66
|
+
|
|
62
67
|
def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
63
68
|
"""注册新工具"""
|
|
64
69
|
self.tools[name] = Tool(name, description, parameters, func)
|
|
@@ -117,64 +122,77 @@ class ToolRegistry:
|
|
|
117
122
|
def tool_help_text(self) -> str:
|
|
118
123
|
"""返回所有工具的帮助文本"""
|
|
119
124
|
return """Available Tools:
|
|
125
|
+
|
|
120
126
|
1. search: Search for information using DuckDuckGo
|
|
121
127
|
2. read_webpage: Extract content from webpages
|
|
122
128
|
3. execute_python: Run Python code with dependency management
|
|
123
129
|
4. execute_shell: Execute shell commands
|
|
124
|
-
5.
|
|
125
|
-
6.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
130
|
+
5. file_operation: Read/write files in workspace directory
|
|
131
|
+
6. create_sub_agent: Create a sub-agent to handle subtasks (ONLY for independent subtasks)
|
|
132
|
+
|
|
133
|
+
Core Rules:
|
|
134
|
+
1. ONE Step at a Time
|
|
135
|
+
- Execute only one action per response
|
|
136
|
+
- Explain what you're going to do before doing it
|
|
137
|
+
- Wait for the result before planning next step
|
|
138
|
+
- No multiple actions or parallel execution
|
|
139
|
+
|
|
140
|
+
2. Sub-Agent Usage (ONLY for Subtasks)
|
|
141
|
+
- Create sub-agents ONLY to handle independent subtasks
|
|
142
|
+
- Main task should be broken down into clear subtasks first
|
|
143
|
+
- Examples of good subtask delegation:
|
|
144
|
+
* Analyzing each Python file in a directory
|
|
145
|
+
* Processing each data file in a batch
|
|
146
|
+
* Researching different aspects of a topic
|
|
147
|
+
* Reviewing multiple code components
|
|
148
|
+
- Do NOT use for:
|
|
149
|
+
* Main task execution
|
|
150
|
+
* Single file operations
|
|
151
|
+
* Simple sequential steps
|
|
152
|
+
* Tasks requiring continuous context
|
|
153
|
+
|
|
154
|
+
3. Output Guidelines
|
|
155
|
+
- Focus on essential information
|
|
156
|
+
- Clear step-by-step explanations
|
|
157
|
+
- Summarize results concisely
|
|
158
|
+
|
|
159
|
+
Tool Call Format:
|
|
160
|
+
<tool_call>
|
|
161
|
+
{
|
|
162
|
+
"name": "tool_name",
|
|
163
|
+
"arguments": {
|
|
164
|
+
"param1": "value1"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
</tool_call>
|
|
168
|
+
|
|
169
|
+
Example (Correct - Subtask Delegation):
|
|
170
|
+
Assistant: I'll create a sub-agent to handle the code analysis subtask.
|
|
156
171
|
<tool_call>
|
|
157
172
|
{
|
|
158
|
-
"name": "
|
|
173
|
+
"name": "create_sub_agent",
|
|
159
174
|
"arguments": {
|
|
160
|
-
"
|
|
161
|
-
"
|
|
175
|
+
"name": "CodeAnalyzer",
|
|
176
|
+
"task": "Analyze the utils.py file structure and generate documentation",
|
|
177
|
+
"context": "This is part of the larger documentation task"
|
|
162
178
|
}
|
|
163
179
|
}
|
|
164
180
|
</tool_call>
|
|
165
181
|
|
|
182
|
+
Example (Incorrect - Main Task):
|
|
183
|
+
❌ Assistant: I'll create a sub-agent to handle the entire project analysis.
|
|
166
184
|
<tool_call>
|
|
167
185
|
{
|
|
168
|
-
"name": "
|
|
186
|
+
"name": "create_sub_agent",
|
|
169
187
|
"arguments": {
|
|
170
|
-
"
|
|
171
|
-
"
|
|
188
|
+
"name": "ProjectAnalyzer",
|
|
189
|
+
"task": "Analyze and document the entire project"
|
|
172
190
|
}
|
|
173
191
|
}
|
|
174
192
|
</tool_call>
|
|
175
193
|
|
|
176
194
|
Remember:
|
|
177
|
-
1.
|
|
178
|
-
2.
|
|
179
|
-
3.
|
|
180
|
-
4.
|
|
195
|
+
1. ONE step at a time
|
|
196
|
+
2. Create sub-agents ONLY for subtasks
|
|
197
|
+
3. Explain before acting
|
|
198
|
+
4. Wait for results"""
|
jarvis/tools/shell.py
CHANGED
|
@@ -4,13 +4,27 @@ from ..utils import PrettyOutput, OutputType
|
|
|
4
4
|
|
|
5
5
|
class ShellTool:
|
|
6
6
|
name = "execute_shell"
|
|
7
|
-
description = "Execute shell commands and return the results
|
|
7
|
+
description = """Execute shell commands and return the results.
|
|
8
|
+
Guidelines for output optimization:
|
|
9
|
+
1. Use grep/awk/sed to filter output when possible
|
|
10
|
+
2. Avoid listing all files/dirs unless specifically needed
|
|
11
|
+
3. Prefer -q/--quiet flags when status is all that's needed
|
|
12
|
+
4. Use head/tail to limit long outputs
|
|
13
|
+
5. Redirect stderr to /dev/null for noisy commands
|
|
14
|
+
|
|
15
|
+
Examples of optimized commands:
|
|
16
|
+
- 'ls -l file.txt' instead of 'ls -l'
|
|
17
|
+
- 'grep -c pattern file' instead of 'grep pattern file'
|
|
18
|
+
- 'ps aux | grep process | head -n 5' instead of 'ps aux'
|
|
19
|
+
- 'command 2>/dev/null' to suppress error messages
|
|
20
|
+
- 'df -h . ' instead of 'df -h'
|
|
21
|
+
"""
|
|
8
22
|
parameters = {
|
|
9
23
|
"type": "object",
|
|
10
24
|
"properties": {
|
|
11
25
|
"command": {
|
|
12
26
|
"type": "string",
|
|
13
|
-
"description": "Shell command to execute"
|
|
27
|
+
"description": "Shell command to execute (use filters/limits for large outputs)"
|
|
14
28
|
},
|
|
15
29
|
"timeout": {
|
|
16
30
|
"type": "integer",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
from ..agent import Agent
|
|
3
|
+
from ..models import BaseModel
|
|
4
|
+
from ..utils import PrettyOutput, OutputType
|
|
5
|
+
from .base import ToolRegistry
|
|
6
|
+
|
|
7
|
+
class SubAgentTool:
|
|
8
|
+
name = "create_sub_agent"
|
|
9
|
+
description = """Create a sub-agent to handle independent tasks.
|
|
10
|
+
|
|
11
|
+
Use this tool when:
|
|
12
|
+
1. A subtask can be executed independently
|
|
13
|
+
2. The task requires separate context management
|
|
14
|
+
3. To optimize token usage of the main agent
|
|
15
|
+
4. For parallel task processing
|
|
16
|
+
|
|
17
|
+
The sub-agent will:
|
|
18
|
+
1. Inherit all tools from the parent agent
|
|
19
|
+
2. Maintain its own conversation history
|
|
20
|
+
3. Return a comprehensive task summary
|
|
21
|
+
"""
|
|
22
|
+
parameters = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"name": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Name for the sub-agent (e.g., 'FileAnalyzer', 'CodeReviewer')"
|
|
28
|
+
},
|
|
29
|
+
"task": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Task description with complete context"
|
|
32
|
+
},
|
|
33
|
+
"context": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Additional context or background information",
|
|
36
|
+
"default": ""
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": ["name", "task"]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def __init__(self, model: BaseModel):
|
|
43
|
+
"""Initialize with the same model as parent agent"""
|
|
44
|
+
self.model = model
|
|
45
|
+
|
|
46
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
47
|
+
"""Create and run a sub-agent for the specified task"""
|
|
48
|
+
try:
|
|
49
|
+
name = args["name"]
|
|
50
|
+
task = args["task"]
|
|
51
|
+
context = args.get("context", "")
|
|
52
|
+
|
|
53
|
+
PrettyOutput.print(f"Creating sub-agent '{name}'...", OutputType.INFO)
|
|
54
|
+
|
|
55
|
+
# Create a new tool registry for the sub-agent
|
|
56
|
+
tool_registry = ToolRegistry(self.model)
|
|
57
|
+
|
|
58
|
+
# Create the sub-agent with the specified name
|
|
59
|
+
sub_agent = Agent(self.model, tool_registry, name=name)
|
|
60
|
+
|
|
61
|
+
# Prepare the task with context if provided
|
|
62
|
+
full_task = f"{context}\n\nTask: {task}" if context else task
|
|
63
|
+
|
|
64
|
+
PrettyOutput.print(f"Sub-agent '{name}' executing task...", OutputType.INFO)
|
|
65
|
+
|
|
66
|
+
# Execute the task and get the summary
|
|
67
|
+
summary = sub_agent.run(full_task)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"success": True,
|
|
71
|
+
"stdout": f"Sub-agent '{name}' completed the task.\n\nSummary:\n{summary}",
|
|
72
|
+
"stderr": ""
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
return {
|
|
77
|
+
"success": False,
|
|
78
|
+
"error": f"Sub-agent execution failed: {str(e)}"
|
|
79
|
+
}
|
jarvis/utils.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
from pathlib import Path
|
|
1
2
|
import sys
|
|
2
3
|
import time
|
|
3
4
|
import threading
|
|
4
|
-
from typing import Optional
|
|
5
|
+
from typing import Dict, Optional
|
|
5
6
|
from enum import Enum
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
import colorama
|
|
8
9
|
from colorama import Fore, Style
|
|
10
|
+
import os
|
|
9
11
|
|
|
10
12
|
# 初始化colorama
|
|
11
13
|
colorama.init()
|
|
@@ -102,4 +104,25 @@ def get_multiline_input(tip: str) -> str:
|
|
|
102
104
|
PrettyOutput.print("\n输入已取消", OutputType.ERROR)
|
|
103
105
|
return ""
|
|
104
106
|
|
|
105
|
-
return "\n".join(lines).strip()
|
|
107
|
+
return "\n".join(lines).strip()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def load_env_from_file():
|
|
111
|
+
"""Load environment variables from ~/.jarvis_env file"""
|
|
112
|
+
env_file = Path.home() / ".jarvis_env"
|
|
113
|
+
|
|
114
|
+
if env_file.exists():
|
|
115
|
+
try:
|
|
116
|
+
with open(env_file, "r", encoding="utf-8") as f:
|
|
117
|
+
for line in f:
|
|
118
|
+
line = line.strip()
|
|
119
|
+
if line and not line.startswith("#"):
|
|
120
|
+
try:
|
|
121
|
+
key, value = line.split("=", 1)
|
|
122
|
+
os.environ[key.strip()] = value.strip().strip("'").strip('"')
|
|
123
|
+
except ValueError:
|
|
124
|
+
continue
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print(f"Warning: Failed to read ~/.jarvis_env: {e}")
|
|
127
|
+
|
|
128
|
+
|
jarvis/zte_llm.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, Any, List, Optional
|
|
6
|
+
from .models import BaseModel
|
|
7
|
+
|
|
8
|
+
class ZteLLM(BaseModel):
|
|
9
|
+
"""ZTE Nebula LLM implementation"""
|
|
10
|
+
|
|
11
|
+
def __init__(self,
|
|
12
|
+
app_id: str,
|
|
13
|
+
app_key: str,
|
|
14
|
+
emp_no: str,
|
|
15
|
+
auth_value: str,
|
|
16
|
+
model: str = "nebulacoder",
|
|
17
|
+
):
|
|
18
|
+
"""Initialize ZTE LLM with required credentials"""
|
|
19
|
+
self.app_id = str(app_id)
|
|
20
|
+
self.app_key = str(app_key)
|
|
21
|
+
self.emp_no = str(emp_no)
|
|
22
|
+
self.auth_value = str(auth_value)
|
|
23
|
+
self.model = model
|
|
24
|
+
self.base_url = "https://studio.zte.com.cn/zte-studio-ai-platform/openapi/v1"
|
|
25
|
+
|
|
26
|
+
def _make_request(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
27
|
+
"""Make request to ZTE API"""
|
|
28
|
+
headers = {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'Authorization': f'Bearer {self.app_id}-{self.app_key}',
|
|
31
|
+
'X-Emp-No': self.emp_no,
|
|
32
|
+
'X-Auth-Value': self.auth_value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
response = requests.post(
|
|
36
|
+
f"{self.base_url}/{endpoint}",
|
|
37
|
+
headers=headers,
|
|
38
|
+
json=data
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
response.raise_for_status()
|
|
42
|
+
result = response.json()
|
|
43
|
+
|
|
44
|
+
if result["code"]["code"] != "0000":
|
|
45
|
+
raise Exception(f"API Error: {result['code']['msg']}")
|
|
46
|
+
|
|
47
|
+
return result["bo"]
|
|
48
|
+
|
|
49
|
+
def chat(self, messages: List[Dict[str, Any]], tools: Optional[List[Dict]] = None) -> Dict[str, Any]:
|
|
50
|
+
"""Chat with ZTE LLM"""
|
|
51
|
+
# Convert messages to prompt
|
|
52
|
+
prompt = self._convert_messages_to_prompt(messages)
|
|
53
|
+
|
|
54
|
+
# Prepare data for API call
|
|
55
|
+
data = {
|
|
56
|
+
"chatUuid": "",
|
|
57
|
+
"chatName": "",
|
|
58
|
+
"stream": False,
|
|
59
|
+
"keep": False,
|
|
60
|
+
"text": prompt,
|
|
61
|
+
"model": self.model
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# If tools are provided, add them to the prompt
|
|
65
|
+
if tools:
|
|
66
|
+
tools_desc = "Available tools:\n\n" + json.dumps(tools, indent=2, ensure_ascii=False)
|
|
67
|
+
data["text"] = tools_desc + "\n\n" + data["text"]
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
result = self._make_request("chat", data)
|
|
71
|
+
|
|
72
|
+
# Parse the response to extract potential tool calls
|
|
73
|
+
response_text = result["result"]
|
|
74
|
+
tool_calls = BaseModel.extract_tool_calls(response_text)
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
"message": {
|
|
78
|
+
"content": response_text,
|
|
79
|
+
"tool_calls": tool_calls
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
except Exception as e:
|
|
84
|
+
raise Exception(f"ZTE LLM chat failed: {str(e)}")
|
|
85
|
+
|
|
86
|
+
def _convert_messages_to_prompt(self, messages: List[Dict[str, Any]]) -> str:
|
|
87
|
+
"""Convert message list to a single prompt string"""
|
|
88
|
+
prompt_parts = []
|
|
89
|
+
|
|
90
|
+
for message in messages:
|
|
91
|
+
role = message["role"]
|
|
92
|
+
content = message.get("content", "")
|
|
93
|
+
|
|
94
|
+
if role == "system":
|
|
95
|
+
prompt_parts.append(f"System: {content}")
|
|
96
|
+
elif role == "user":
|
|
97
|
+
prompt_parts.append(f"User: {content}")
|
|
98
|
+
elif role == "assistant":
|
|
99
|
+
prompt_parts.append(f"Assistant: {content}")
|
|
100
|
+
elif role == "tool":
|
|
101
|
+
prompt_parts.append(f"Tool Result: {content}")
|
|
102
|
+
|
|
103
|
+
return "\n\n".join(prompt_parts)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def create_zte_llm(model_name: str = "NebulaBiz") -> ZteLLM:
|
|
107
|
+
"""Create ZTE LLM instance with provided parameters"""
|
|
108
|
+
# Load environment variables from file
|
|
109
|
+
|
|
110
|
+
# Get credentials from parameters, env file, or system environment variables
|
|
111
|
+
app_id = os.getenv('ZTE_APP_ID')
|
|
112
|
+
app_key = os.getenv('ZTE_APP_KEY')
|
|
113
|
+
emp_no = os.getenv('ZTE_EMP_NO')
|
|
114
|
+
auth_value = os.getenv('ZTE_AUTH_VALUE')
|
|
115
|
+
|
|
116
|
+
# Validate required credentials
|
|
117
|
+
if not all([app_id, app_key, emp_no, auth_value]):
|
|
118
|
+
raise ValueError(
|
|
119
|
+
"Missing required credentials. Please provide through either:\n"
|
|
120
|
+
"1. Function parameters\n"
|
|
121
|
+
"2. ~/.jarvis_env file\n"
|
|
122
|
+
"3. System environment variables\n\n"
|
|
123
|
+
"Required variables: ZTE_APP_ID, ZTE_APP_KEY, ZTE_EMP_NO, ZTE_AUTH_VALUE"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return ZteLLM(
|
|
127
|
+
app_id=app_id,
|
|
128
|
+
app_key=app_key,
|
|
129
|
+
emp_no=emp_no,
|
|
130
|
+
auth_value=auth_value,
|
|
131
|
+
model=model_name
|
|
132
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Jarvis: An AI assistant that uses tools to interact with the system
|
|
5
5
|
Home-page: https://github.com/skyfireitdiy/Jarvis
|
|
6
6
|
Author: skyfire
|
|
@@ -21,13 +21,15 @@ Requires-Dist: beautifulsoup4>=4.9.3
|
|
|
21
21
|
Requires-Dist: duckduckgo-search>=3.0.0
|
|
22
22
|
Requires-Dist: pyyaml>=5.1
|
|
23
23
|
Requires-Dist: ollama>=0.1.6
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: chromadb>=0.4.24
|
|
24
|
+
Requires-Dist: colorama>=0.4.6
|
|
26
25
|
Provides-Extra: dev
|
|
27
26
|
Requires-Dist: pytest; extra == "dev"
|
|
28
27
|
Requires-Dist: black; extra == "dev"
|
|
29
28
|
Requires-Dist: isort; extra == "dev"
|
|
30
29
|
Requires-Dist: mypy; extra == "dev"
|
|
30
|
+
Dynamic: author
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: requires-python
|
|
31
33
|
|
|
32
34
|
<div align="center">
|
|
33
35
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
jarvis/.jarvis,sha256=S4ZMmLqlVLHAPmnwHFQ3vYt0gnDFp-KQRH4okBh8Hpw,209
|
|
2
|
+
jarvis/__init__.py,sha256=ArINt8uI8UveWy3H6iPntzn05IR-PEq6P7qb8dzmPjc,49
|
|
3
|
+
jarvis/agent.py,sha256=YdtSBqwGl8xKN9xWIqYcAgxsLmXKyXzLcJ1D-Oie9fE,5536
|
|
4
|
+
jarvis/main.py,sha256=egbfAhkHUGHI5J3j5ZMwb9EgOXE7ikJef-4lqFj0-mA,6063
|
|
5
|
+
jarvis/models.py,sha256=NWXMsgnw6AHkfk22tvmGOjcyVhC74Us-t3T6rUF4R20,4186
|
|
6
|
+
jarvis/utils.py,sha256=-5tugkGABZdi2QXVStVtCQfag9CRTfCsGUvReitmHUM,3816
|
|
7
|
+
jarvis/zte_llm.py,sha256=ygUeGd8WGm0qrnkt5TrCLnaf5ZFZ0tdfwrA7nrByt4g,4504
|
|
8
|
+
jarvis/__pycache__/__init__.cpython-313.pyc,sha256=viNe2ZOgBWfBtJpzh5J0oAhQphAwJi4m2ogxgBgCt6s,208
|
|
9
|
+
jarvis/__pycache__/agent.cpython-313.pyc,sha256=7Ae1sOwF6W2HCGjzDToWdCOYFiS47EUqDZVCz2OxH9Q,7125
|
|
10
|
+
jarvis/__pycache__/main.cpython-313.pyc,sha256=lNKrL5HZQahMGpv5cOVriWZoNOScMWRk_toYD7X-Xeo,7444
|
|
11
|
+
jarvis/__pycache__/models.cpython-313.pyc,sha256=fot2VOV0lgODg4b_WuD-kkq-g7uZGLuZlYXiljlW13U,6350
|
|
12
|
+
jarvis/__pycache__/tools.cpython-313.pyc,sha256=lAD4LrnnWzNZQmHXGfZ_2l7oskOpr2_2OC-gdFhxQY8,33933
|
|
13
|
+
jarvis/__pycache__/utils.cpython-313.pyc,sha256=m9l4R4U1vCG2WkXXNWCTbSASRiS6tWYEWK8pu29EtY4,7175
|
|
14
|
+
jarvis/__pycache__/zte_llm.cpython-313.pyc,sha256=PcS-P2H96EyelWcPleCIcu0lGYYoLrdi9VSZgg2cooA,5340
|
|
15
|
+
jarvis/tools/__init__.py,sha256=NZqKPfIbBxeM5FOrN1ssQQ4S9gVXNCSVXa3ov73AyEk,356
|
|
16
|
+
jarvis/tools/base.py,sha256=I_XIwxb6hCH82qCVPESbDNC93Dj0G3KVSZRXSh6mHIM,6516
|
|
17
|
+
jarvis/tools/file_ops.py,sha256=05Vc4u-NGMjLD15WI52eL_nt_RyYt-Z_cXJnnBepf-c,3781
|
|
18
|
+
jarvis/tools/python_script.py,sha256=_eK8LqNs-Mz50zdcgwbjdd8-qAeOl6kJ_qRDvWawGMw,5006
|
|
19
|
+
jarvis/tools/search.py,sha256=dyJmeP_s2tWv5KQejOl-TuVF12B6SXViiXEyTemD1Wo,1412
|
|
20
|
+
jarvis/tools/shell.py,sha256=VhEqEdoUGIRW6CT6F0YNMAlzJxpHcsKS9hSS0VC3ezw,2644
|
|
21
|
+
jarvis/tools/sub_agent.py,sha256=vC495JBAEMgHbAkHzGlYhIPA5scKekwhy1RViNcwqy8,2683
|
|
22
|
+
jarvis/tools/webpage.py,sha256=UTEomu5j7jbOw8c5az2jsjv5E7LeokWKj1QahvZO7xc,3077
|
|
23
|
+
jarvis/tools/__pycache__/__init__.cpython-313.pyc,sha256=Qng3CpxPy9R_1hTjSSr_qLXHtlI8GneyYX6JHwUtmHE,642
|
|
24
|
+
jarvis/tools/__pycache__/base.cpython-313.pyc,sha256=wXXQs94_MAEuHVhFx80q_IgWBRuo1I4Dc7wz3G9ZMo4,8544
|
|
25
|
+
jarvis/tools/__pycache__/file_ops.cpython-313.pyc,sha256=bawv11xhWSj5wCtW0QeJLI-InhUBUXaSkogq6rZzvTQ,3636
|
|
26
|
+
jarvis/tools/__pycache__/python_script.cpython-313.pyc,sha256=8JpryqTovEiTvBlWAK1KjZmPvHUuPc9GT9rTXBEQoJc,6693
|
|
27
|
+
jarvis/tools/__pycache__/rag.cpython-313.pyc,sha256=JH6-PSZRMKAvTZqCwlRXJGClxYXNMs-vetU0q7hBLz0,6064
|
|
28
|
+
jarvis/tools/__pycache__/search.cpython-313.pyc,sha256=VX9zztOwIsPCkYwev0A0XJGyu4Tr0PQcQkbbqx8vfB0,1870
|
|
29
|
+
jarvis/tools/__pycache__/shell.cpython-313.pyc,sha256=UUciwt3nW3mkHrMI7WvCASQ-YLwVx2N68RzQq97nL74,2972
|
|
30
|
+
jarvis/tools/__pycache__/sub_agent.cpython-313.pyc,sha256=p6vgjqdHW7ZAJYaiDh6F39CMndeA3tutbxXB9cwc6xY,3003
|
|
31
|
+
jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc,sha256=wK3Ev10lHSUSRvoYmi7A0GzxYkzU-C4Wfhs5qW_HBqs,2271
|
|
32
|
+
jarvis/tools/__pycache__/user_interaction.cpython-313.pyc,sha256=RuVZ-pmiPBDywY3efgXSfohMAciC1avMGPmBK5qlnew,3305
|
|
33
|
+
jarvis/tools/__pycache__/webpage.cpython-313.pyc,sha256=VcpkaV8IyOOtebedXjn8ybjr8AglU-3-Cs80yzE4FYo,3628
|
|
34
|
+
jarvis_ai_assistant-0.1.5.dist-info/METADATA,sha256=pZCtN2L3EmGQMvpbxA6vED4RHJIWdJqXC2JnaD0whEw,3690
|
|
35
|
+
jarvis_ai_assistant-0.1.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
36
|
+
jarvis_ai_assistant-0.1.5.dist-info/entry_points.txt,sha256=iKu7OMfew9dtfGhW71gIMTg4wvafuPqKb4wyQOnMAGU,44
|
|
37
|
+
jarvis_ai_assistant-0.1.5.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
|
|
38
|
+
jarvis_ai_assistant-0.1.5.dist-info/RECORD,,
|
jarvis/tools/rag.py
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
from typing import Dict, Any, List
|
|
2
|
-
import chromadb
|
|
3
|
-
from chromadb.utils import embedding_functions
|
|
4
|
-
import hashlib
|
|
5
|
-
import re
|
|
6
|
-
from ..utils import PrettyOutput, OutputType
|
|
7
|
-
|
|
8
|
-
class RAGTool:
|
|
9
|
-
name = "rag_query"
|
|
10
|
-
description = """Execute RAG queries on documents.
|
|
11
|
-
Features:
|
|
12
|
-
1. Auto-creates document embeddings
|
|
13
|
-
2. Returns relevant passages
|
|
14
|
-
3. Maintains embedding database
|
|
15
|
-
"""
|
|
16
|
-
parameters = {
|
|
17
|
-
"type": "object",
|
|
18
|
-
"properties": {
|
|
19
|
-
"files": {
|
|
20
|
-
"type": "array",
|
|
21
|
-
"items": {"type": "string"},
|
|
22
|
-
"description": "Files to process"
|
|
23
|
-
},
|
|
24
|
-
"query": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"description": "Query text"
|
|
27
|
-
},
|
|
28
|
-
"num_passages": {
|
|
29
|
-
"type": "integer",
|
|
30
|
-
"description": "Number of passages",
|
|
31
|
-
"default": 3
|
|
32
|
-
},
|
|
33
|
-
"chunk_size": {
|
|
34
|
-
"type": "integer",
|
|
35
|
-
"description": "Chunk size",
|
|
36
|
-
"default": 500
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"required": ["files", "query"]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
def _get_document_hash(self, content: str) -> str:
|
|
43
|
-
return hashlib.md5(content.encode()).hexdigest()
|
|
44
|
-
|
|
45
|
-
def _chunk_text(self, text: str, chunk_size: int = 500) -> List[str]:
|
|
46
|
-
"""将文本分割成适当大小的块"""
|
|
47
|
-
# 按句子分割
|
|
48
|
-
sentences = re.split(r'(?<=[.!?])\s+', text)
|
|
49
|
-
chunks = []
|
|
50
|
-
current_chunk = []
|
|
51
|
-
current_length = 0
|
|
52
|
-
|
|
53
|
-
for sentence in sentences:
|
|
54
|
-
sentence_length = len(sentence)
|
|
55
|
-
if current_length + sentence_length > chunk_size and current_chunk:
|
|
56
|
-
chunks.append(" ".join(current_chunk))
|
|
57
|
-
current_chunk = []
|
|
58
|
-
current_length = 0
|
|
59
|
-
current_chunk.append(sentence)
|
|
60
|
-
current_length += sentence_length
|
|
61
|
-
|
|
62
|
-
if current_chunk:
|
|
63
|
-
chunks.append(" ".join(current_chunk))
|
|
64
|
-
|
|
65
|
-
return chunks
|
|
66
|
-
|
|
67
|
-
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
68
|
-
"""执行RAG查询"""
|
|
69
|
-
try:
|
|
70
|
-
files = args["files"]
|
|
71
|
-
query = args["query"]
|
|
72
|
-
num_passages = args.get("num_passages", 3)
|
|
73
|
-
chunk_size = args.get("chunk_size", 500)
|
|
74
|
-
|
|
75
|
-
# 初始化ChromaDB
|
|
76
|
-
chroma_client = chromadb.PersistentClient(path="./data/chromadb")
|
|
77
|
-
|
|
78
|
-
# 使用sentence-transformers作为嵌入模型
|
|
79
|
-
embedding_function = embedding_functions.SentenceTransformerEmbeddingFunction(
|
|
80
|
-
model_name="all-MiniLM-L6-v2"
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# 获取或创建集合
|
|
84
|
-
collection = chroma_client.get_or_create_collection(
|
|
85
|
-
name="document_store",
|
|
86
|
-
embedding_function=embedding_function
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
# 处理每个文件
|
|
90
|
-
for file_path in files:
|
|
91
|
-
try:
|
|
92
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
93
|
-
content = f.read()
|
|
94
|
-
|
|
95
|
-
# 计算文档哈希值
|
|
96
|
-
doc_hash = self._get_document_hash(content)
|
|
97
|
-
|
|
98
|
-
# 检查文档是否已经存在且未更改
|
|
99
|
-
existing_ids = collection.get(
|
|
100
|
-
where={"doc_hash": doc_hash}
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
if not existing_ids["ids"]:
|
|
104
|
-
# 分块处理文档
|
|
105
|
-
chunks = self._chunk_text(content, chunk_size)
|
|
106
|
-
|
|
107
|
-
# 为每个块生成唯一ID
|
|
108
|
-
chunk_ids = [f"{doc_hash}_{i}" for i in range(len(chunks))]
|
|
109
|
-
|
|
110
|
-
# 添加到数据库
|
|
111
|
-
collection.add(
|
|
112
|
-
documents=chunks,
|
|
113
|
-
ids=chunk_ids,
|
|
114
|
-
metadatas=[{
|
|
115
|
-
"file_path": file_path,
|
|
116
|
-
"doc_hash": doc_hash,
|
|
117
|
-
"chunk_index": i
|
|
118
|
-
} for i in range(len(chunks))]
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
PrettyOutput.print(f"已添加文档: {file_path}", OutputType.INFO)
|
|
122
|
-
else:
|
|
123
|
-
PrettyOutput.print(f"文档已存在且未更改: {file_path}", OutputType.INFO)
|
|
124
|
-
|
|
125
|
-
except Exception as e:
|
|
126
|
-
PrettyOutput.print(f"处理文件 {file_path} 时出错: {str(e)}", OutputType.ERROR)
|
|
127
|
-
|
|
128
|
-
# 执行查询
|
|
129
|
-
results = collection.query(
|
|
130
|
-
query_texts=[query],
|
|
131
|
-
n_results=num_passages
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
# 格式化输出
|
|
135
|
-
output = [f"查询: {query}\n"]
|
|
136
|
-
output.append(f"找到 {len(results['documents'][0])} 个相关段落:\n")
|
|
137
|
-
|
|
138
|
-
for i, (doc, metadata) in enumerate(zip(results['documents'][0], results['metadatas'][0]), 1):
|
|
139
|
-
output.append(f"\n段落 {i}:")
|
|
140
|
-
output.append(f"来源: {metadata['file_path']}")
|
|
141
|
-
output.append(f"相关内容:\n{doc}\n")
|
|
142
|
-
output.append("-" * 50)
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
"success": True,
|
|
146
|
-
"stdout": "\n".join(output),
|
|
147
|
-
"stderr": ""
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
except Exception as e:
|
|
151
|
-
return {
|
|
152
|
-
"success": False,
|
|
153
|
-
"error": f"RAG查询失败: {str(e)}"
|
|
154
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
from typing import Dict, Any
|
|
2
|
-
from ..utils import PrettyOutput, OutputType
|
|
3
|
-
|
|
4
|
-
class UserConfirmationTool:
|
|
5
|
-
name = "ask_user_confirmation"
|
|
6
|
-
description = "Request confirmation from user, returns yes/no"
|
|
7
|
-
parameters = {
|
|
8
|
-
"type": "object",
|
|
9
|
-
"properties": {
|
|
10
|
-
"question": {
|
|
11
|
-
"type": "string",
|
|
12
|
-
"description": "Question to ask for confirmation"
|
|
13
|
-
},
|
|
14
|
-
"details": {
|
|
15
|
-
"type": "string",
|
|
16
|
-
"description": "Additional details or context",
|
|
17
|
-
"default": ""
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"required": ["question"]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
24
|
-
"""获取用户确认"""
|
|
25
|
-
try:
|
|
26
|
-
question = args["question"]
|
|
27
|
-
details = args.get("details", "")
|
|
28
|
-
|
|
29
|
-
# 打印详细信息
|
|
30
|
-
if details:
|
|
31
|
-
PrettyOutput.print(details, OutputType.INFO)
|
|
32
|
-
PrettyOutput.print("", OutputType.INFO) # 空行
|
|
33
|
-
|
|
34
|
-
# 打印问题
|
|
35
|
-
PrettyOutput.print(f"{question} (y/n)", OutputType.INFO)
|
|
36
|
-
|
|
37
|
-
while True:
|
|
38
|
-
response = input(">>> ").strip().lower()
|
|
39
|
-
if response in ['y', 'yes']:
|
|
40
|
-
result = True
|
|
41
|
-
break
|
|
42
|
-
elif response in ['n', 'no']:
|
|
43
|
-
result = False
|
|
44
|
-
break
|
|
45
|
-
else:
|
|
46
|
-
PrettyOutput.print("请输入 y/yes 或 n/no", OutputType.ERROR)
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
"success": True,
|
|
50
|
-
"stdout": str(result),
|
|
51
|
-
"stderr": ""
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
except Exception as e:
|
|
55
|
-
return {
|
|
56
|
-
"success": False,
|
|
57
|
-
"error": f"获取用户确认失败: {str(e)}"
|
|
58
|
-
}
|
jarvis/tools/user_interaction.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
from typing import Dict, Any, List
|
|
2
|
-
from ..utils import PrettyOutput, OutputType, get_multiline_input
|
|
3
|
-
|
|
4
|
-
class UserInteractionTool:
|
|
5
|
-
name = "ask_user"
|
|
6
|
-
description = "Ask user for information, supports option selection and multiline input"
|
|
7
|
-
parameters = {
|
|
8
|
-
"type": "object",
|
|
9
|
-
"properties": {
|
|
10
|
-
"question": {
|
|
11
|
-
"type": "string",
|
|
12
|
-
"description": "Question to ask the user"
|
|
13
|
-
},
|
|
14
|
-
"options": {
|
|
15
|
-
"type": "array",
|
|
16
|
-
"items": {"type": "string"},
|
|
17
|
-
"description": "List of options for user to choose from",
|
|
18
|
-
"default": []
|
|
19
|
-
},
|
|
20
|
-
"multiline": {
|
|
21
|
-
"type": "boolean",
|
|
22
|
-
"description": "Allow multiline input",
|
|
23
|
-
"default": False
|
|
24
|
-
},
|
|
25
|
-
"description": {
|
|
26
|
-
"type": "string",
|
|
27
|
-
"description": "Additional description or context",
|
|
28
|
-
"default": ""
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"required": ["question"]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
35
|
-
"""获取用户输入"""
|
|
36
|
-
try:
|
|
37
|
-
question = args["question"]
|
|
38
|
-
options = args.get("options", [])
|
|
39
|
-
multiline = args.get("multiline", False)
|
|
40
|
-
description = args.get("description", "")
|
|
41
|
-
|
|
42
|
-
# 打印问题描述
|
|
43
|
-
if description:
|
|
44
|
-
PrettyOutput.print(description, OutputType.INFO)
|
|
45
|
-
|
|
46
|
-
# 如果有选项,显示选项列表
|
|
47
|
-
if options:
|
|
48
|
-
PrettyOutput.print("\n可选项:", OutputType.INFO)
|
|
49
|
-
for i, option in enumerate(options, 1):
|
|
50
|
-
PrettyOutput.print(f"[{i}] {option}", OutputType.INFO)
|
|
51
|
-
|
|
52
|
-
while True:
|
|
53
|
-
try:
|
|
54
|
-
choice = input("\n请选择 (输入数字): ").strip()
|
|
55
|
-
if not choice:
|
|
56
|
-
continue
|
|
57
|
-
|
|
58
|
-
choice = int(choice)
|
|
59
|
-
if 1 <= choice <= len(options):
|
|
60
|
-
response = options[choice - 1]
|
|
61
|
-
break
|
|
62
|
-
else:
|
|
63
|
-
PrettyOutput.print("无效选择,请重试", OutputType.ERROR)
|
|
64
|
-
except ValueError:
|
|
65
|
-
PrettyOutput.print("请输入有效数字", OutputType.ERROR)
|
|
66
|
-
|
|
67
|
-
# 多行输入
|
|
68
|
-
elif multiline:
|
|
69
|
-
response = get_multiline_input(question)
|
|
70
|
-
|
|
71
|
-
# 单行输入
|
|
72
|
-
else:
|
|
73
|
-
PrettyOutput.print(question, OutputType.INFO)
|
|
74
|
-
response = input(">>> ").strip()
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
"success": True,
|
|
78
|
-
"stdout": response,
|
|
79
|
-
"stderr": ""
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
except Exception as e:
|
|
83
|
-
return {
|
|
84
|
-
"success": False,
|
|
85
|
-
"error": f"获取用户输入失败: {str(e)}"
|
|
86
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
jarvis/.jarvis,sha256=S4ZMmLqlVLHAPmnwHFQ3vYt0gnDFp-KQRH4okBh8Hpw,209
|
|
2
|
-
jarvis/__init__.py,sha256=V3Qwm3k_WFeGUNccLbLJuCyWQyXYnRUkY5-KlBUm4UU,49
|
|
3
|
-
jarvis/agent.py,sha256=hEatrIothG6C_RuOsygZz4ez1aF1EycnUPwY2Krwiwk,4092
|
|
4
|
-
jarvis/main.py,sha256=wAeeGoRq8fFiTWUCQ31MYVeU9o6SZ48ORUow0jlMJJE,5259
|
|
5
|
-
jarvis/models.py,sha256=ZQAyc39e_UsNmHkME6ATp6053safQ7Gog-oWwETsrMM,4415
|
|
6
|
-
jarvis/utils.py,sha256=-fOPAgiKVPC6DevHM7A7IJvvIzvvFlGM0pHBKyBXDcA,3044
|
|
7
|
-
jarvis/__pycache__/__init__.cpython-313.pyc,sha256=u64PLu4KyOPrirhIqNXZSkj9B6yB1L_ohFYhPX8p334,208
|
|
8
|
-
jarvis/__pycache__/agent.cpython-313.pyc,sha256=I1JYUKC-zedURwuxQQjd9saj_yQKUhpdbb1KNWlY6Ss,4940
|
|
9
|
-
jarvis/__pycache__/models.cpython-313.pyc,sha256=fot2VOV0lgODg4b_WuD-kkq-g7uZGLuZlYXiljlW13U,6350
|
|
10
|
-
jarvis/__pycache__/tools.cpython-313.pyc,sha256=lAD4LrnnWzNZQmHXGfZ_2l7oskOpr2_2OC-gdFhxQY8,33933
|
|
11
|
-
jarvis/__pycache__/utils.cpython-313.pyc,sha256=o8Fubq8Dcrvv3ATzQ9BnZoZjQmU3_FNPrfTt0ANdKdI,5804
|
|
12
|
-
jarvis/tools/__init__.py,sha256=FNF5X32UzKvlT08-hPz7-7vcIyqqMngQYj36LXyNbV0,553
|
|
13
|
-
jarvis/tools/base.py,sha256=xdZaoqxCOYVAwBmof_zqICg5ixb5-ksX-XbNXJw1O7k,5938
|
|
14
|
-
jarvis/tools/file_ops.py,sha256=05Vc4u-NGMjLD15WI52eL_nt_RyYt-Z_cXJnnBepf-c,3781
|
|
15
|
-
jarvis/tools/python_script.py,sha256=_eK8LqNs-Mz50zdcgwbjdd8-qAeOl6kJ_qRDvWawGMw,5006
|
|
16
|
-
jarvis/tools/rag.py,sha256=eBrn1fdqy-nd2nR0b4oH1EQpthTYVen-sYDhC5Ypl38,5647
|
|
17
|
-
jarvis/tools/search.py,sha256=dyJmeP_s2tWv5KQejOl-TuVF12B6SXViiXEyTemD1Wo,1412
|
|
18
|
-
jarvis/tools/shell.py,sha256=uGqwUGmqFg1cJkyTg7zyPk3jRwEr-woKTmqcTKdbbg8,1979
|
|
19
|
-
jarvis/tools/user_confirmation.py,sha256=p0JverifHJfnALeIhtKtaVBBVEGkgSpUzT-PSycG4NY,1850
|
|
20
|
-
jarvis/tools/user_interaction.py,sha256=xOaoYsCKTa9-K3mg5tsu1zegn1HKcO40k2aJ3VygZqM,3073
|
|
21
|
-
jarvis/tools/webpage.py,sha256=UTEomu5j7jbOw8c5az2jsjv5E7LeokWKj1QahvZO7xc,3077
|
|
22
|
-
jarvis/tools/__pycache__/__init__.cpython-313.pyc,sha256=wktmOMuGMwQjQ--eRzooUNsL5YjY0bS_CbcSFUWze54,674
|
|
23
|
-
jarvis/tools/__pycache__/base.cpython-313.pyc,sha256=hqhRtxwQwWLnBEzNqZ1FqcWYuyX87iu89SgHxu10Gho,8192
|
|
24
|
-
jarvis/tools/__pycache__/file_ops.cpython-313.pyc,sha256=bawv11xhWSj5wCtW0QeJLI-InhUBUXaSkogq6rZzvTQ,3636
|
|
25
|
-
jarvis/tools/__pycache__/python_script.cpython-313.pyc,sha256=8JpryqTovEiTvBlWAK1KjZmPvHUuPc9GT9rTXBEQoJc,6693
|
|
26
|
-
jarvis/tools/__pycache__/rag.cpython-313.pyc,sha256=JH6-PSZRMKAvTZqCwlRXJGClxYXNMs-vetU0q7hBLz0,6064
|
|
27
|
-
jarvis/tools/__pycache__/search.cpython-313.pyc,sha256=VX9zztOwIsPCkYwev0A0XJGyu4Tr0PQcQkbbqx8vfB0,1870
|
|
28
|
-
jarvis/tools/__pycache__/shell.cpython-313.pyc,sha256=Xtqt-LzMRfsCXeCE7QxFY8TR8XEeQG_jic73YLkhXQw,2302
|
|
29
|
-
jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc,sha256=wK3Ev10lHSUSRvoYmi7A0GzxYkzU-C4Wfhs5qW_HBqs,2271
|
|
30
|
-
jarvis/tools/__pycache__/user_interaction.cpython-313.pyc,sha256=RuVZ-pmiPBDywY3efgXSfohMAciC1avMGPmBK5qlnew,3305
|
|
31
|
-
jarvis/tools/__pycache__/webpage.cpython-313.pyc,sha256=VcpkaV8IyOOtebedXjn8ybjr8AglU-3-Cs80yzE4FYo,3628
|
|
32
|
-
jarvis_ai_assistant-0.1.2.dist-info/METADATA,sha256=0EX8k3LdrzeEWHqRTHlguT80fPxHLbVtpY65f1kMhOk,3675
|
|
33
|
-
jarvis_ai_assistant-0.1.2.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
|
|
34
|
-
jarvis_ai_assistant-0.1.2.dist-info/entry_points.txt,sha256=iKu7OMfew9dtfGhW71gIMTg4wvafuPqKb4wyQOnMAGU,44
|
|
35
|
-
jarvis_ai_assistant-0.1.2.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
|
|
36
|
-
jarvis_ai_assistant-0.1.2.dist-info/RECORD,,
|
{jarvis_ai_assistant-0.1.2.dist-info → jarvis_ai_assistant-0.1.5.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|