jarvis-ai-assistant 0.1.6__tar.gz → 0.1.8__tar.gz

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 (60) hide show
  1. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/PKG-INFO +2 -1
  2. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/pyproject.toml +2 -1
  3. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/setup.py +2 -1
  4. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/__init__.py +1 -1
  5. jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
  6. jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/agent.cpython-313.pyc +0 -0
  7. jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/main.cpython-313.pyc +0 -0
  8. jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/models.cpython-313.pyc +0 -0
  9. jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
  10. jarvis_ai_assistant-0.1.8/src/jarvis/agent.py +298 -0
  11. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/main.py +22 -6
  12. jarvis_ai_assistant-0.1.8/src/jarvis/models.py +122 -0
  13. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__init__.py +0 -2
  14. jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  15. jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
  16. jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/bing_search.cpython-313.pyc +0 -0
  17. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
  18. jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
  19. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
  20. jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
  21. jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
  22. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/base.py +8 -19
  23. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/file_ops.py +1 -1
  24. jarvis_ai_assistant-0.1.8/src/jarvis/tools/search.py +144 -0
  25. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/shell.py +1 -20
  26. jarvis_ai_assistant-0.1.8/src/jarvis/tools/sub_agent.py +83 -0
  27. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/webpage.py +12 -26
  28. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/zte_llm.py +26 -27
  29. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/PKG-INFO +2 -1
  30. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +1 -1
  31. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/requires.txt +1 -0
  32. jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
  33. jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/agent.cpython-313.pyc +0 -0
  34. jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/main.cpython-313.pyc +0 -0
  35. jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/models.cpython-313.pyc +0 -0
  36. jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
  37. jarvis_ai_assistant-0.1.6/src/jarvis/agent.py +0 -195
  38. jarvis_ai_assistant-0.1.6/src/jarvis/models.py +0 -130
  39. jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  40. jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
  41. jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
  42. jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
  43. jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
  44. jarvis_ai_assistant-0.1.6/src/jarvis/tools/search.py +0 -48
  45. jarvis_ai_assistant-0.1.6/src/jarvis/tools/sub_agent.py +0 -141
  46. jarvis_ai_assistant-0.1.6/src/jarvis/tools/user_input.py +0 -74
  47. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/MANIFEST.in +0 -0
  48. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/README.md +0 -0
  49. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/setup.cfg +0 -0
  50. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/__pycache__/tools.cpython-313.pyc +0 -0
  51. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/__pycache__/utils.cpython-313.pyc +0 -0
  52. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
  53. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
  54. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
  55. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
  56. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
  57. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/utils.py +0 -0
  58. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
  59. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
  60. {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.6
3
+ Version: 0.1.8
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
@@ -22,6 +22,7 @@ Requires-Dist: duckduckgo-search>=3.0.0
22
22
  Requires-Dist: pyyaml>=5.1
23
23
  Requires-Dist: ollama>=0.1.6
24
24
  Requires-Dist: colorama>=0.4.6
25
+ Requires-Dist: openai>=1.2.0
25
26
  Provides-Extra: dev
26
27
  Requires-Dist: pytest; extra == "dev"
27
28
  Requires-Dist: black; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jarvis-ai-assistant"
7
- version = "0.1.6"
7
+ version = "0.1.8"
8
8
  description = "Jarvis: An AI assistant that uses tools to interact with the system"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Your Name", email = "your.email@example.com" }]
@@ -26,6 +26,7 @@ dependencies = [
26
26
  "pyyaml>=5.1",
27
27
  "ollama>=0.1.6",
28
28
  "colorama>=0.4.6",
29
+ "openai>=1.2.0",
29
30
  ]
30
31
  requires-python = ">=3.8"
31
32
 
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="jarvis-ai-assistant",
5
- version="0.1.6",
5
+ version="0.1.8",
6
6
  author="skyfire",
7
7
  author_email="skyfireitdiy@hotmail.com",
8
8
  description="An AI assistant that uses various tools to interact with the system",
@@ -19,6 +19,7 @@ setup(
19
19
  "pyyaml>=5.1",
20
20
  "ollama>=0.1.6",
21
21
  "colorama>=0.4.6",
22
+ "openai>=1.2.0",
22
23
  ],
23
24
  entry_points={
24
25
  "console_scripts": [
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.6"
3
+ __version__ = "0.1.8"
@@ -0,0 +1,298 @@
1
+ import json
2
+ import subprocess
3
+ from typing import Dict, Any, List, Optional, Tuple
4
+
5
+ import yaml
6
+ from .tools import ToolRegistry
7
+ from .utils import PrettyOutput, OutputType, get_multiline_input
8
+ from .models import BaseModel
9
+ import re
10
+ import os
11
+ from datetime import datetime
12
+
13
+ class Agent:
14
+ def __init__(self, model: BaseModel, tool_registry: ToolRegistry, name: str = "Jarvis", is_sub_agent: bool = False):
15
+ """Initialize Agent with a model, optional tool registry and name
16
+
17
+ Args:
18
+ model: 语言模型实例
19
+ tool_registry: 工具注册表实例
20
+ name: Agent名称,默认为"Jarvis"
21
+ is_sub_agent: 是否为子Agent,默认为False
22
+ """
23
+ self.model = model
24
+ self.tool_registry = tool_registry or ToolRegistry(model)
25
+ self.name = name
26
+ self.is_sub_agent = is_sub_agent
27
+
28
+ # 构建工具说明
29
+ tools_prompt = "可用工具:\n"
30
+ for tool in self.tool_registry.get_all_tools():
31
+ tools_prompt += f"- 名称: {tool['name']}\n"
32
+ tools_prompt += f" 描述: {tool['description']}\n"
33
+ tools_prompt += f" 参数: {tool['parameters']}\n"
34
+
35
+ self.messages = [
36
+ {
37
+ "role": "system",
38
+ "content": f"""你是 {name},一个严格遵循 ReAct 框架进行逐步推理和行动的 AI 助手。
39
+
40
+ {tools_prompt}
41
+
42
+ 关键规则:
43
+ ‼️ 禁止创建虚假对话
44
+ ‼️ 禁止假设用户回应
45
+ ‼️ 禁止在没有实际用户输入时继续
46
+ ‼️ 只回应用户实际说的内容
47
+ ‼️ 每个动作后停止并等待
48
+
49
+ ReAct 框架:
50
+ 1. 思考
51
+ - 分析当前情况
52
+ - 考虑可用工具
53
+ - 规划下一步行动
54
+ - 仅基于事实
55
+ - 不做用户回应的假设
56
+ - 不想象对话内容
57
+
58
+ 2. 行动(可选)
59
+ - 不是每次回应都需要调用工具
60
+ - 如果需要更多信息,直接询问用户
61
+ - 使用工具时:
62
+ - 只使用下面列出的工具
63
+ - 每次只执行一个工具
64
+ - 工具由用户手动执行
65
+ - 必须使用有效的YAML格式:
66
+ <START_TOOL_CALL>
67
+ name: tool_name
68
+ arguments:
69
+ param1: value1 # 所有参数必须正确缩进
70
+ param2: | # 使用YAML块样式表示多行字符串
71
+ line1
72
+ line2
73
+ <END_TOOL_CALL>
74
+
75
+ 3. 观察
76
+ - 等待工具执行结果或用户回应
77
+ - 工具执行结果由用户提供
78
+ - 不要假设或想象回应
79
+ - 不要创建虚假对话
80
+ - 停止并等待实际输入
81
+
82
+ 回应格式:
83
+ 思考:我分析当前情况[具体情况]... 基于[事实],我需要[目标]...
84
+
85
+ [如果需要使用工具:]
86
+ 行动:我将使用[工具]来[具体目的]...
87
+ <START_TOOL_CALL>
88
+ name: tool_name
89
+ arguments:
90
+ param1: value1
91
+ param2: |
92
+ multiline
93
+ value
94
+ <END_TOOL_CALL>
95
+
96
+ [如果需要更多信息:]
97
+ 我需要了解更多关于[具体细节]的信息。请提供[需要的信息]。
98
+
99
+ 严格规则:
100
+ ‼️ 只使用下面列出的工具
101
+ ‼️ 工具调用是可选的 - 需要时询问用户
102
+ ‼️ 每次只能调用一个工具
103
+ ‼️ 工具调用必须是有效的YAML格式
104
+ ‼️ 参数必须正确缩进
105
+ ‼️ 使用YAML块样式(|)表示多行值
106
+ ‼️ <END_TOOL_CALL>后的内容将被丢弃
107
+ ‼️ 工具由用户手动执行
108
+ ‼️ 等待用户提供工具执行结果
109
+ ‼️ 不要假设或想象用户回应
110
+ ‼️ 没有用户输入时不要继续对话
111
+ ‼️ 不要创建虚假对话
112
+ ‼️ 每个动作后停止
113
+ ‼️ 不要假设结果
114
+ ‼️ 不要假设行动
115
+
116
+ 注意事项:
117
+ - 先思考再行动
118
+ - 需要时询问用户
119
+ - 只使用列出的工具
120
+ - 一次一个工具
121
+ - 严格遵循YAML格式
122
+ - 等待用户回应
123
+ - 工具结果来自用户
124
+ - 不要假设回应
125
+ - 不要虚构对话
126
+ - 每个动作后停止
127
+ - 只在有实际用户输入时继续
128
+ """
129
+ }
130
+ ]
131
+
132
+ @staticmethod
133
+ def extract_tool_calls(content: str) -> Tuple[str, List[Dict]]:
134
+ """从内容中提取工具调用,如果检测到多个工具调用则抛出异常,并返回工具调用之前的内容和工具调用"""
135
+ # 分割内容为行
136
+ lines = content.split('\n')
137
+ tool_call_lines = []
138
+ content_lines = [] # 存储工具调用之前的内容
139
+ in_tool_call = False
140
+
141
+ # 逐行处理
142
+ for line in lines:
143
+ content_lines.append(line) # 所有内容都添加到 content_lines
144
+
145
+ if '<START_TOOL_CALL>' in line:
146
+ in_tool_call = True
147
+ continue
148
+ elif '<END_TOOL_CALL>' in line:
149
+ if in_tool_call and tool_call_lines:
150
+ try:
151
+ # 直接解析YAML
152
+ tool_call_text = '\n'.join(tool_call_lines)
153
+ tool_call_data = yaml.safe_load(tool_call_text)
154
+
155
+ # 验证必要的字段
156
+ if "name" in tool_call_data and "arguments" in tool_call_data:
157
+ # 返回工具调用之前的内容和工具调用
158
+ return '\n'.join(content_lines), [{
159
+ "name": tool_call_data["name"],
160
+ "arguments": tool_call_data["arguments"]
161
+ }]
162
+ else:
163
+ PrettyOutput.print("工具调用缺少必要字段", OutputType.ERROR)
164
+ raise '工具调用缺少必要字段'
165
+ except yaml.YAMLError as e:
166
+ PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
167
+ raise 'YAML解析错误'
168
+ except Exception as e:
169
+ PrettyOutput.print(f"处理工具调用时发生错误: {str(e)}", OutputType.ERROR)
170
+ raise '处理工具调用时发生错误'
171
+ in_tool_call = False
172
+ continue
173
+
174
+ if in_tool_call:
175
+ tool_call_lines.append(line)
176
+
177
+ # 如果没有找到有效的工具调用,返回原始内容
178
+ return '\n'.join(content_lines), []
179
+
180
+ def _call_model(self, messages: List[Dict]) -> Dict:
181
+ """调用模型获取响应"""
182
+ try:
183
+ return self.model.chat(
184
+ messages=messages,
185
+ )
186
+ except Exception as e:
187
+ raise Exception(f"{self.name}: 模型调用失败: {str(e)}")
188
+
189
+ def run(self, user_input: str) -> str:
190
+ """处理用户输入并返回响应,返回任务总结报告"""
191
+ self.clear_history()
192
+
193
+ # 显示任务开始
194
+ PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
195
+
196
+ self.messages.append({
197
+ "role": "user",
198
+ "content": user_input
199
+ })
200
+
201
+ while True:
202
+ try:
203
+ # 显示思考状态
204
+ PrettyOutput.print("分析任务...", OutputType.PROGRESS)
205
+
206
+ current_response = self._call_model(self.messages)
207
+
208
+ try:
209
+ result = Agent.extract_tool_calls(current_response)
210
+ except Exception as e:
211
+ PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
212
+ self.messages.append({
213
+ "role": "user",
214
+ "content": f"工具调用错误: {str(e)}"
215
+ })
216
+ continue
217
+
218
+
219
+ self.messages.append({
220
+ "role": "assistant",
221
+ "content": result[0]
222
+ })
223
+
224
+
225
+ if len(result[1]) > 0:
226
+ try:
227
+ # 显示工具调用
228
+ PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
229
+ tool_result = self.tool_registry.handle_tool_calls(result[1])
230
+ PrettyOutput.print(tool_result, OutputType.RESULT)
231
+ except Exception as e:
232
+ PrettyOutput.print(str(e), OutputType.ERROR)
233
+ tool_result = f"Tool call failed: {str(e)}"
234
+
235
+ self.messages.append({
236
+ "role": "user",
237
+ "content": tool_result
238
+ })
239
+ continue
240
+
241
+ # 获取用户输入
242
+ user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
243
+ if not user_input:
244
+ # 只有子Agent才需要生成任务总结
245
+ if self.is_sub_agent:
246
+ PrettyOutput.print("生成任务总结...", OutputType.PROGRESS)
247
+
248
+ # 生成任务总结
249
+ summary_prompt = {
250
+ "role": "user",
251
+ "content": """任务已完成。请根据之前的分析和执行结果,提供一个简明的任务总结,包括:
252
+
253
+ 1. 关键发现:
254
+ - 分析过程中的重要发现
255
+ - 工具执行的关键结果
256
+ - 发现的重要数据
257
+
258
+ 2. 执行成果:
259
+ - 任务完成情况
260
+ - 具体实现结果
261
+ - 达成的目标
262
+
263
+ 请直接描述事实和实际结果,保持简洁明了。"""
264
+ }
265
+
266
+ while True:
267
+ try:
268
+ summary = self._call_model(self.messages + [summary_prompt])
269
+
270
+ # 显示任务总结
271
+ PrettyOutput.section("任务总结", OutputType.SUCCESS)
272
+ PrettyOutput.print(summary, OutputType.SYSTEM)
273
+ PrettyOutput.section("任务完成", OutputType.SUCCESS)
274
+
275
+ return summary
276
+
277
+ except Exception as e:
278
+ PrettyOutput.print(str(e), OutputType.ERROR)
279
+ else:
280
+ # 顶层Agent直接返回空字符串
281
+ PrettyOutput.section("任务完成", OutputType.SUCCESS)
282
+ return ""
283
+
284
+ if user_input == "__interrupt__":
285
+ PrettyOutput.print("任务已取消", OutputType.WARNING)
286
+ return "Task cancelled by user"
287
+
288
+ self.messages.append({
289
+ "role": "user",
290
+ "content": user_input
291
+ })
292
+
293
+ except Exception as e:
294
+ PrettyOutput.print(str(e), OutputType.ERROR)
295
+
296
+ def clear_history(self):
297
+ """清除对话历史,只保留系统提示"""
298
+ self.messages = [self.messages[0]]
@@ -12,7 +12,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
12
12
 
13
13
  from jarvis.agent import Agent
14
14
  from jarvis.tools import ToolRegistry
15
- from jarvis.models import DDGSModel, OllamaModel
15
+ from jarvis.models import DDGSModel, OllamaModel, OpenAIModel
16
16
  from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, load_env_from_file
17
17
  from jarvis.zte_llm import create_zte_llm
18
18
 
@@ -20,15 +20,23 @@ from jarvis.zte_llm import create_zte_llm
20
20
  SUPPORTED_PLATFORMS = {
21
21
  "ollama": {
22
22
  "models": ["qwen2.5:14b", "qwq"],
23
- "default": "qwen2.5:14b"
23
+ "default": "qwen2.5:14b",
24
+ "allow_custom": True
24
25
  },
25
26
  "ddgs": {
26
27
  "models": ["gpt-4o-mini", "claude-3-haiku", "llama-3.1-70b", "mixtral-8x7b"],
27
- "default": "gpt-4o-mini"
28
+ "default": "gpt-4o-mini",
29
+ "allow_custom": False
28
30
  },
29
31
  "zte": {
30
32
  "models": ["NebulaBiz", "nebulacoder", "NTele-72B"],
31
- "default": "NebulaBiz"
33
+ "default": "NebulaBiz",
34
+ "allow_custom": False
35
+ },
36
+ "openai": {
37
+ "models": ["deepseek-chat"],
38
+ "default": "deepseek-chat",
39
+ "allow_custom": True
32
40
  }
33
41
  }
34
42
 
@@ -118,9 +126,10 @@ def main():
118
126
 
119
127
  args.model = args.model or os.getenv("JARVIS_MODEL")
120
128
 
121
- # 验证并设置默认模型
129
+ # 修改模型验证逻辑
122
130
  if args.model:
123
- if args.model not in SUPPORTED_PLATFORMS[args.platform]["models"]:
131
+ if (args.model not in SUPPORTED_PLATFORMS[args.platform]["models"] and
132
+ not SUPPORTED_PLATFORMS[args.platform]["allow_custom"]):
124
133
  supported_models = ", ".join(SUPPORTED_PLATFORMS[args.platform]["models"])
125
134
  PrettyOutput.print(
126
135
  f"错误: 平台 {args.platform} 不支持模型 {args.model}\n"
@@ -145,6 +154,13 @@ def main():
145
154
  elif args.platform == "zte": # zte
146
155
  model = create_zte_llm(model_name=args.model)
147
156
  platform_name = f"ZTE ({args.model})"
157
+ elif args.platform == "openai":
158
+ model = OpenAIModel(
159
+ model_name=args.model,
160
+ api_key=os.getenv("OPENAI_API_KEY"),
161
+ api_base=os.getenv("OPENAI_API_BASE")
162
+ )
163
+ platform_name = f"OpenAI ({args.model})"
148
164
 
149
165
  tool_registry = ToolRegistry(model)
150
166
  agent = Agent(model, tool_registry)
@@ -0,0 +1,122 @@
1
+ import re
2
+ import time
3
+ from typing import Dict, List, Optional, Tuple
4
+ from duckduckgo_search import DDGS
5
+ import ollama
6
+ from abc import ABC, abstractmethod
7
+ import yaml
8
+ import openai
9
+
10
+ from .utils import OutputType, PrettyOutput
11
+
12
+ class BaseModel(ABC):
13
+ """大语言模型基类"""
14
+
15
+ @abstractmethod
16
+ def chat(self, messages: List[Dict]) -> str:
17
+ """执行对话"""
18
+ pass
19
+
20
+
21
+ class DDGSModel(BaseModel):
22
+ def __init__(self, model_name: str = "gpt-4o-mini"):
23
+ """
24
+ [1]: gpt-4o-mini
25
+ [2]: claude-3-haiku
26
+ [3]: llama-3.1-70b
27
+ [4]: mixtral-8x7b
28
+ """
29
+ self.model_name = model_name
30
+
31
+ def __make_prompt(self, messages: List[Dict]) -> str:
32
+ prompt = ""
33
+ for message in messages:
34
+ prompt += f"[{message['role']}]: {message['content']}\n"
35
+ return prompt
36
+
37
+ def chat(self, messages: List[Dict]) -> str:
38
+ ddgs = DDGS()
39
+ prompt = self.__make_prompt(messages)
40
+ content = ddgs.chat(prompt)
41
+ PrettyOutput.print(content, OutputType.SYSTEM)
42
+ return content
43
+
44
+
45
+ class OllamaModel(BaseModel):
46
+ """Ollama模型实现"""
47
+
48
+ def __init__(self, model_name: str = "qwen2.5:14b", api_base: str = "http://localhost:11434"):
49
+ self.model_name = model_name
50
+ self.api_base = api_base
51
+ self.client = ollama.Client(host=api_base)
52
+
53
+ def chat(self, messages: List[Dict]) -> str:
54
+ """调用Ollama API获取响应"""
55
+ try:
56
+ # 使用流式调用
57
+ stream = self.client.chat(
58
+ model=self.model_name,
59
+ messages=messages,
60
+ stream=True
61
+ )
62
+
63
+ # 收集完整响应
64
+ content_parts = []
65
+ for chunk in stream:
66
+ if chunk.message.content:
67
+ content_parts.append(chunk.message.content)
68
+ # 实时打印内容
69
+ PrettyOutput.print_stream(chunk.message.content, OutputType.SYSTEM)
70
+
71
+ PrettyOutput.print_stream_end()
72
+
73
+ # 合并完整内容
74
+ return "".join(content_parts)
75
+
76
+ except Exception as e:
77
+ raise Exception(f"Ollama API调用失败: {str(e)}")
78
+
79
+
80
+ class OpenAIModel(BaseModel):
81
+ """OpenAI模型实现"""
82
+
83
+ def __init__(self, model_name: str = "deepseek-chat", api_key: Optional[str] = None, api_base: Optional[str] = None):
84
+ """
85
+ 初始化OpenAI模型
86
+ Args:
87
+ model_name: 模型名称,默认为 deepseek-chat
88
+ api_key: OpenAI API密钥
89
+ api_base: 可选的API基础URL,用于自定义端点
90
+ """
91
+ self.model_name = model_name
92
+ if api_key:
93
+ openai.api_key = api_key
94
+ if api_base:
95
+ openai.base_url = api_base
96
+
97
+ def chat(self, messages: List[Dict]) -> str:
98
+ """调用OpenAI API获取响应"""
99
+ try:
100
+ # 使用流式调用
101
+ stream = openai.chat.completions.create(
102
+ model=self.model_name,
103
+ messages=messages,
104
+ stream=True
105
+ )
106
+
107
+ # 收集完整响应
108
+ content_parts = []
109
+ for chunk in stream:
110
+ if chunk.choices[0].delta.content:
111
+ content = chunk.choices[0].delta.content
112
+ content_parts.append(content)
113
+ # 实时打印内容
114
+ PrettyOutput.print_stream(content, OutputType.SYSTEM)
115
+
116
+ PrettyOutput.print_stream_end()
117
+
118
+ # 合并完整内容
119
+ return "".join(content_parts)
120
+
121
+ except Exception as e:
122
+ raise Exception(f"OpenAI API调用失败: {str(e)}")
@@ -3,7 +3,6 @@ from .file_ops import FileOperationTool
3
3
  from .search import SearchTool
4
4
  from .shell import ShellTool
5
5
  from .webpage import WebpageTool
6
- from .user_input import UserInputTool
7
6
 
8
7
  __all__ = [
9
8
  'Tool',
@@ -12,5 +11,4 @@ __all__ = [
12
11
  'SearchTool',
13
12
  'ShellTool',
14
13
  'WebpageTool',
15
- 'UserInputTool',
16
14
  ]
@@ -13,12 +13,9 @@ class Tool:
13
13
  def to_dict(self) -> Dict:
14
14
  """转换为Ollama工具格式"""
15
15
  return {
16
- "type": "function",
17
- "function": {
18
- "name": self.name,
19
- "description": self.description,
20
- "parameters": self.parameters
21
- }
16
+ "name": self.name,
17
+ "description": self.description,
18
+ "parameters": json.dumps(self.parameters)
22
19
  }
23
20
 
24
21
  def execute(self, arguments: Dict) -> Dict[str, Any]:
@@ -38,14 +35,13 @@ class ToolRegistry:
38
35
  from .file_ops import FileOperationTool
39
36
  from .webpage import WebpageTool
40
37
  from .sub_agent import SubAgentTool
41
- from .user_input import UserInputTool
42
38
 
43
39
  tools = [
44
- SearchTool(),
40
+ SearchTool(self.model),
45
41
  ShellTool(),
46
42
  FileOperationTool(),
47
43
  WebpageTool(),
48
- UserInputTool(),
44
+ SubAgentTool(self.model)
49
45
  ]
50
46
 
51
47
  for tool in tools:
@@ -56,14 +52,6 @@ class ToolRegistry:
56
52
  func=tool.execute
57
53
  )
58
54
 
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
-
67
55
  def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
68
56
  """注册新工具"""
69
57
  self.tools[name] = Tool(name, description, parameters, func)
@@ -90,8 +78,8 @@ class ToolRegistry:
90
78
 
91
79
  # 只处理第一个工具调用
92
80
  tool_call = tool_calls[0]
93
- name = tool_call["function"]["name"]
94
- args = tool_call["function"]["arguments"]
81
+ name = tool_call["name"]
82
+ args = tool_call["arguments"]
95
83
 
96
84
  if isinstance(args, str):
97
85
  try:
@@ -121,6 +109,7 @@ class ToolRegistry:
121
109
  if stderr:
122
110
  output_parts.append(f"错误:\n{stderr}")
123
111
  output = "\n\n".join(output_parts)
112
+ output = "没有输出和错误" if not output else output
124
113
  PrettyOutput.section("执行成功", OutputType.SUCCESS)
125
114
  else:
126
115
  error_msg = result["error"]
@@ -5,7 +5,7 @@ from ..utils import PrettyOutput, OutputType
5
5
 
6
6
  class FileOperationTool:
7
7
  name = "file_operation"
8
- description = "Execute file operations (read/write/append/exists)"
8
+ description = "文件操作 (read/write/append/exists)"
9
9
  parameters = {
10
10
  "type": "object",
11
11
  "properties": {