jarvis-ai-assistant 0.1.3__py3-none-any.whl → 0.1.6__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.

Files changed (33) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
  3. jarvis/__pycache__/agent.cpython-313.pyc +0 -0
  4. jarvis/__pycache__/main.cpython-313.pyc +0 -0
  5. jarvis/__pycache__/models.cpython-313.pyc +0 -0
  6. jarvis/__pycache__/utils.cpython-313.pyc +0 -0
  7. jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
  8. jarvis/agent.py +131 -36
  9. jarvis/main.py +44 -24
  10. jarvis/models.py +54 -36
  11. jarvis/tools/__init__.py +3 -9
  12. jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  13. jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
  14. jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
  15. jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
  16. jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
  17. jarvis/tools/base.py +57 -138
  18. jarvis/tools/shell.py +62 -48
  19. jarvis/tools/sub_agent.py +141 -0
  20. jarvis/tools/user_input.py +74 -0
  21. jarvis/utils.py +123 -64
  22. jarvis/zte_llm.py +136 -0
  23. {jarvis_ai_assistant-0.1.3.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/METADATA +6 -4
  24. jarvis_ai_assistant-0.1.6.dist-info/RECORD +38 -0
  25. {jarvis_ai_assistant-0.1.3.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/WHEEL +1 -1
  26. jarvis/.jarvis +0 -1
  27. jarvis/tools/python_script.py +0 -150
  28. jarvis/tools/rag.py +0 -154
  29. jarvis/tools/user_confirmation.py +0 -58
  30. jarvis/tools/user_interaction.py +0 -86
  31. jarvis_ai_assistant-0.1.3.dist-info/RECORD +0 -36
  32. {jarvis_ai_assistant-0.1.3.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/entry_points.txt +0 -0
  33. {jarvis_ai_assistant-0.1.3.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/top_level.txt +0 -0
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,30 +26,26 @@ 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
- from .python_script import PythonScriptTool
39
38
  from .file_ops import FileOperationTool
40
- from .rag import RAGTool
41
39
  from .webpage import WebpageTool
40
+ from .sub_agent import SubAgentTool
41
+ from .user_input import UserInputTool
42
42
 
43
43
  tools = [
44
44
  SearchTool(),
45
45
  ShellTool(),
46
- UserInteractionTool(),
47
- UserConfirmationTool(),
48
- PythonScriptTool(),
49
46
  FileOperationTool(),
50
- RAGTool(),
51
47
  WebpageTool(),
48
+ UserInputTool(),
52
49
  ]
53
50
 
54
51
  for tool in tools:
@@ -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)
@@ -79,133 +84,47 @@ class ToolRegistry:
79
84
  return tool.execute(arguments)
80
85
 
81
86
  def handle_tool_calls(self, tool_calls: List[Dict]) -> str:
82
- """处理工具调用"""
83
- results = []
84
- for tool_call in tool_calls:
85
- name = tool_call["function"]["name"]
86
- args = tool_call["function"]["arguments"]
87
- if isinstance(args, str):
88
- try:
89
- args = json.loads(args)
90
- except json.JSONDecodeError:
91
- return f"Invalid JSON in arguments for tool {name}"
92
-
93
- PrettyOutput.print(f"Calling tool: {name}", OutputType.INFO)
94
- if isinstance(args, dict):
95
- for key, value in args.items():
96
- PrettyOutput.print(f" - {key}: {value}", OutputType.INFO)
97
- else:
98
- PrettyOutput.print(f" Arguments: {args}", OutputType.INFO)
99
- PrettyOutput.print("", OutputType.INFO)
87
+ """处理工具调用,只处理第一个工具"""
88
+ if not tool_calls:
89
+ return ""
100
90
 
101
- result = self.execute_tool(name, args)
102
- if result["success"]:
103
- stdout = result["stdout"]
104
- stderr = result.get("stderr", "")
105
- output_parts = []
106
- output_parts.append(f"Result:\n{stdout}")
107
- if stderr:
108
- output_parts.append(f"Errors:\n{stderr}")
109
- output = "\n\n".join(output_parts)
110
- else:
111
- error_msg = result["error"]
112
- output = f"Execution failed: {error_msg}"
113
-
114
- results.append(output)
115
- return "\n".join(results)
116
-
117
- def tool_help_text(self) -> str:
118
- """返回所有工具的帮助文本"""
119
- return """Available Tools:
120
-
121
- 1. search: Search for information using DuckDuckGo
122
- 2. read_webpage: Extract content from webpages
123
- 3. execute_python: Run Python code with dependency management
124
- 4. execute_shell: Execute shell commands
125
- 5. ask_user: Get input from user with options support
126
- 6. ask_user_confirmation: Get yes/no confirmation from user
127
- 7. file_operation: Read/write files in workspace directory
128
- 8. rag_query: Query documents using RAG
129
-
130
- Output Guidelines:
131
- 1. Focus on Essential Information
132
- - Filter out irrelevant or redundant information
133
- - Only output the most relevant search results
134
- - Summarize long content when possible
135
-
136
- 2. Context Management
137
- - Avoid repeating information that's already known
138
- - Don't echo back the entire content of files
139
- - Use snippets instead of full documents
140
-
141
- 3. Error Handling
142
- - Only show relevant parts of error messages
143
- - Include troubleshooting steps when appropriate
144
- - Omit stack traces unless specifically requested
145
-
146
- 4. Search Results
147
- - Focus on the most relevant 2-3 results
148
- - Extract key information instead of full articles
149
- - Skip duplicate or similar content
150
-
151
- 5. RAG Queries
152
- - Return only the most relevant passages
153
- - Combine similar information
154
- - Skip redundant context
155
-
156
- Tool Call Format Requirements:
157
- 1. MUST use exact format:
158
- <tool_call>
159
- {
160
- "name": "tool_name",
161
- "arguments": {
162
- "param1": "value1"
163
- }
164
- }
165
- </tool_call>
166
-
167
- 2. Format Rules:
168
- - NO comments or explanations inside tool_call blocks
169
- - NO additional text or formatting within JSON
170
- - NO markdown or other markup inside tool_call
171
- - ONLY valid JSON allowed in arguments
172
- - ALL arguments must match tool parameters exactly
173
-
174
- 3. Invalid Examples (DO NOT USE):
175
- ❌ <tool_call>
176
- {
177
- "name": "search", # Search tool
178
- "arguments": {
179
- "query": "Python GIL" // Search query
180
- }
181
- }
182
- </tool_call>
183
-
184
- ❌ <tool_call>
185
- ```json
186
- {
187
- "name": "search",
188
- "arguments": {
189
- "query": "Python GIL"
190
- }
191
- }
192
- ```
193
- </tool_call>
194
-
195
- 4. Valid Example:
196
- ✅ <tool_call>
197
- {
198
- "name": "search",
199
- "arguments": {
200
- "query": "Python GIL",
201
- "max_results": 2
202
- }
203
- }
204
- </tool_call>
205
-
206
- Remember:
207
- 1. Quality over quantity
208
- 2. Relevance over completeness
209
- 3. Conciseness over verbosity
210
- 4. Context efficiency is crucial
211
- 5. STRICT adherence to tool call format"""
91
+ # 只处理第一个工具调用
92
+ tool_call = tool_calls[0]
93
+ name = tool_call["function"]["name"]
94
+ args = tool_call["function"]["arguments"]
95
+
96
+ if isinstance(args, str):
97
+ try:
98
+ args = json.loads(args)
99
+ except json.JSONDecodeError:
100
+ PrettyOutput.print(f"工具参数格式无效: {name}", OutputType.ERROR)
101
+ return ""
102
+
103
+ # 显示工具调用信息
104
+ PrettyOutput.section(f"执行工具: {name}", OutputType.TOOL)
105
+ if isinstance(args, dict):
106
+ for key, value in args.items():
107
+ PrettyOutput.print(f"参数: {key} = {value}", OutputType.DEBUG)
108
+ else:
109
+ PrettyOutput.print(f"参数: {args}", OutputType.DEBUG)
110
+
111
+ # 执行工具调用
112
+ result = self.execute_tool(name, args)
113
+
114
+ # 处理结果
115
+ if result["success"]:
116
+ stdout = result["stdout"]
117
+ stderr = result.get("stderr", "")
118
+ output_parts = []
119
+ if stdout:
120
+ output_parts.append(f"输出:\n{stdout}")
121
+ if stderr:
122
+ output_parts.append(f"错误:\n{stderr}")
123
+ output = "\n\n".join(output_parts)
124
+ PrettyOutput.section("执行成功", OutputType.SUCCESS)
125
+ else:
126
+ error_msg = result["error"]
127
+ output = f"执行失败: {error_msg}"
128
+ PrettyOutput.section("执行失败", OutputType.ERROR)
129
+
130
+ return output
jarvis/tools/shell.py CHANGED
@@ -1,80 +1,94 @@
1
1
  from typing import Dict, Any
2
- import subprocess
2
+ import os
3
+ import tempfile
4
+ import shlex
5
+ from pathlib import Path
3
6
  from ..utils import PrettyOutput, OutputType
4
7
 
5
8
  class ShellTool:
6
9
  name = "execute_shell"
7
10
  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
- """
11
+
12
+ Guidelines:
13
+ 1. Filter outputs
14
+ - Use grep/sed for specific data
15
+ - Use head/tail to limit lines
16
+ - Use -q for status checks
17
+
18
+ Examples:
19
+ <START_TOOL_CALL>
20
+ name: execute_shell
21
+ arguments:
22
+ command: ls -l file.txt # specific file
23
+ <END_TOOL_CALL>
24
+
25
+ ✗ <START_TOOL_CALL>
26
+ name: execute_shell
27
+ arguments:
28
+ command: ps aux # too much output
29
+ <END_TOOL_CALL>"""
30
+
22
31
  parameters = {
23
32
  "type": "object",
24
33
  "properties": {
25
34
  "command": {
26
35
  "type": "string",
27
- "description": "Shell command to execute (use filters/limits for large outputs)"
28
- },
29
- "timeout": {
30
- "type": "integer",
31
- "description": "Command execution timeout in seconds",
32
- "default": 30
36
+ "description": "Shell command to execute (filter output when possible)"
33
37
  }
34
38
  },
35
39
  "required": ["command"]
36
40
  }
37
41
 
42
+ def _escape_command(self, cmd: str) -> str:
43
+ """转义命令中的特殊字符"""
44
+ # 将命令中的单引号替换为'"'"'
45
+ return cmd.replace("'", "'\"'\"'")
46
+
38
47
  def execute(self, args: Dict) -> Dict[str, Any]:
39
48
  """执行shell命令"""
40
49
  try:
41
- # 获取参数
42
50
  command = args["command"]
43
- timeout = args.get("timeout", 30)
44
51
 
45
- # 执行命令
46
- result = subprocess.run(
47
- command,
48
- shell=True,
49
- capture_output=True,
50
- text=True,
51
- timeout=timeout
52
- )
52
+ # 生成临时文件名
53
+ output_file = os.path.join(tempfile.gettempdir(), f"jarvis_shell_{os.getpid()}.log")
53
54
 
54
- # 构建输出
55
- output = []
55
+ # 转义命令中的特殊字符
56
+ escaped_command = self._escape_command(command)
57
+
58
+ # 修改命令以使用script
59
+ tee_command = f"script -q -c '{escaped_command}' {output_file}"
56
60
 
57
- # 添加命令信息
58
61
  PrettyOutput.print(f"执行命令: {command}", OutputType.INFO)
59
- output.append(f"命令: {command}")
60
- output.append("")
61
62
 
62
- # 添加输出
63
- if result.stdout:
64
- output.append(result.stdout)
63
+ # 执行命令
64
+ return_code = os.system(tee_command)
65
+
66
+ # 读取输出文件
67
+ try:
68
+ with open(output_file, 'r', encoding='utf-8', errors='replace') as f:
69
+ output = f.read()
70
+ # 移除script命令添加的头尾
71
+ if output:
72
+ lines = output.splitlines()
73
+ if len(lines) > 2:
74
+ output = "\n".join(lines[1:-1])
75
+ except Exception as e:
76
+ output = f"读取输出文件失败: {str(e)}"
77
+ finally:
78
+ # 清理临时文件
79
+ Path(output_file).unlink(missing_ok=True)
65
80
 
66
81
  return {
67
- "success": True,
68
- "stdout": "\n".join(output),
69
- "stderr": result.stderr,
70
- "return_code": result.returncode
71
- }
72
- except subprocess.TimeoutExpired:
73
- return {
74
- "success": False,
75
- "error": f"命令执行超时 (>{timeout}秒)"
82
+ "success": return_code == 0,
83
+ "stdout": output,
84
+ "stderr": "",
85
+ "return_code": return_code
76
86
  }
87
+
77
88
  except Exception as e:
89
+ # 确保清理临时文件
90
+ if 'output_file' in locals():
91
+ Path(output_file).unlink(missing_ok=True)
78
92
  return {
79
93
  "success": False,
80
94
  "error": str(e)
@@ -0,0 +1,141 @@
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
+ IMPORTANT: Sub-agents start with NO context!
12
+ Must provide complete steps and context.
13
+
14
+ Required:
15
+ 1. Context
16
+ - Project background
17
+ - File locations
18
+ - Current standards
19
+ - Requirements
20
+
21
+ 2. Steps
22
+ - Clear actions
23
+ - Success criteria
24
+ - Expected output
25
+
26
+ Example (Good):
27
+ <START_TOOL_CALL>
28
+ name: create_sub_agent
29
+ arguments:
30
+ name: CodeAnalyzer
31
+ task: Analyze error handling
32
+ context: |
33
+ Project: Python 3.8+
34
+ File: src/utils.py
35
+
36
+ Steps:
37
+ 1. Read file content
38
+ 2. Find error patterns
39
+ 3. Check consistency
40
+ 4. Verify logging
41
+
42
+ Expected:
43
+ - Pattern list
44
+ - Issues found
45
+ - Suggestions
46
+
47
+ Standards:
48
+ - Dict[str, Any] returns
49
+ - PrettyOutput logging
50
+ <END_TOOL_CALL>
51
+
52
+ Example (Bad):
53
+ <START_TOOL_CALL>
54
+ name: create_sub_agent
55
+ arguments:
56
+ name: Analyzer
57
+ task: Check code
58
+ context: Look at utils.py
59
+ <END_TOOL_CALL>
60
+
61
+ Use for:
62
+ ✓ Clear, independent tasks
63
+ ✓ Well-defined goals
64
+ ✗ Vague tasks
65
+ ✗ Simple operations"""
66
+
67
+ parameters = {
68
+ "type": "object",
69
+ "properties": {
70
+ "name": {
71
+ "type": "string",
72
+ "description": "Sub-agent name (e.g., 'FileAnalyzer')"
73
+ },
74
+ "task": {
75
+ "type": "string",
76
+ "description": "Task with clear steps and goals"
77
+ },
78
+ "context": {
79
+ "type": "string",
80
+ "description": "REQUIRED: Background, steps, and expected results",
81
+ "default": ""
82
+ }
83
+ },
84
+ "required": ["name", "task", "context"]
85
+ }
86
+
87
+ def __init__(self, model: BaseModel):
88
+ """Initialize with the same model as parent agent"""
89
+ self.model = model
90
+
91
+ def execute(self, args: Dict) -> Dict[str, Any]:
92
+ """Create and run a sub-agent for the specified task"""
93
+ try:
94
+ name = args["name"]
95
+ task = args["task"]
96
+ context = args.get("context")
97
+
98
+ if not context:
99
+ return {
100
+ "success": False,
101
+ "error": "Context is required. Please provide complete background and steps."
102
+ }
103
+
104
+ PrettyOutput.print(f"Creating sub-agent '{name}'...", OutputType.INFO)
105
+
106
+ # Create a new tool registry for the sub-agent
107
+ tool_registry = ToolRegistry(self.model)
108
+
109
+ # Create the sub-agent with the specified name
110
+ sub_agent = Agent(self.model, tool_registry, name=name)
111
+
112
+ # Prepare the task with context
113
+ full_task = f"""Background and Steps:
114
+ {context}
115
+
116
+ Primary Task:
117
+ {task}
118
+
119
+ Requirements:
120
+ 1. Follow the provided steps exactly
121
+ 2. Report progress after each step
122
+ 3. Highlight any issues or unclear points
123
+ 4. Provide detailed results matching expected output"""
124
+
125
+ PrettyOutput.print(f"Sub-agent '{name}' executing task...", OutputType.INFO)
126
+
127
+
128
+ # Execute the task and get the summary
129
+ summary = sub_agent.run(full_task)
130
+ return {
131
+ "success": True,
132
+ "stdout": f"Sub-agent '{name}' completed.\n\nResults:\n{summary}",
133
+ "stderr": ""
134
+ }
135
+
136
+
137
+ except Exception as e:
138
+ return {
139
+ "success": False,
140
+ "error": f"Sub-agent failed: {str(e)}"
141
+ }
@@ -0,0 +1,74 @@
1
+ from typing import Dict, Any
2
+ from ..utils import PrettyOutput, OutputType, get_multiline_input
3
+
4
+ class UserInputTool:
5
+ name = "ask_user"
6
+ description = """Ask user for information or confirmation.
7
+
8
+ Use this tool when you need:
9
+ 1. Additional information
10
+ 2. Confirmation before critical actions
11
+ 3. User preferences or choices"""
12
+
13
+ parameters = {
14
+ "type": "object",
15
+ "properties": {
16
+ "question": {
17
+ "type": "string",
18
+ "description": "Clear question for the user"
19
+ },
20
+ "options": {
21
+ "type": "string",
22
+ "description": "Optional: Numbered list of choices",
23
+ "default": ""
24
+ },
25
+ "context": {
26
+ "type": "string",
27
+ "description": "Optional: Additional context to help user understand",
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
+ context = args.get("context", "")
40
+
41
+ # 显示问题
42
+ PrettyOutput.section("用户确认", OutputType.USER)
43
+
44
+ # 显示上下文(如果有)
45
+ if context:
46
+ PrettyOutput.print(f"背景: {context}", OutputType.INFO)
47
+
48
+ # 显示问题
49
+ PrettyOutput.print(f"问题: {question}", OutputType.USER)
50
+
51
+ # 显示选项(如果有)
52
+ if options:
53
+ PrettyOutput.print("\n选项:\n" + options, OutputType.INFO)
54
+
55
+ # 获取用户输入
56
+ response = get_multiline_input("请输入您的回答:")
57
+
58
+ if response == "__interrupt__":
59
+ return {
60
+ "success": False,
61
+ "error": "User cancelled the input"
62
+ }
63
+
64
+ return {
65
+ "success": True,
66
+ "stdout": response,
67
+ "stderr": ""
68
+ }
69
+
70
+ except Exception as e:
71
+ return {
72
+ "success": False,
73
+ "error": f"Failed to get user input: {str(e)}"
74
+ }