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.
- 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__/models.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 +131 -36
- jarvis/main.py +44 -24
- jarvis/models.py +54 -36
- jarvis/tools/__init__.py +3 -9
- 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/__pycache__/user_input.cpython-313.pyc +0 -0
- jarvis/tools/base.py +57 -138
- jarvis/tools/shell.py +62 -48
- jarvis/tools/sub_agent.py +141 -0
- jarvis/tools/user_input.py +74 -0
- jarvis/utils.py +123 -64
- jarvis/zte_llm.py +136 -0
- {jarvis_ai_assistant-0.1.3.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/METADATA +6 -4
- jarvis_ai_assistant-0.1.6.dist-info/RECORD +38 -0
- {jarvis_ai_assistant-0.1.3.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/WHEEL +1 -1
- jarvis/.jarvis +0 -1
- jarvis/tools/python_script.py +0 -150
- 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.3.dist-info/RECORD +0 -36
- {jarvis_ai_assistant-0.1.3.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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":
|
|
68
|
-
"stdout":
|
|
69
|
-
"stderr":
|
|
70
|
-
"return_code":
|
|
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
|
+
}
|