jarvis-ai-assistant 0.1.10__tar.gz → 0.1.11__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 (51) hide show
  1. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/PKG-INFO +1 -1
  2. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/pyproject.toml +1 -1
  3. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/setup.py +1 -1
  4. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/__init__.py +1 -1
  5. jarvis_ai_assistant-0.1.11/src/jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
  6. jarvis_ai_assistant-0.1.11/src/jarvis/__pycache__/agent.cpython-313.pyc +0 -0
  7. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/__pycache__/main.cpython-313.pyc +0 -0
  8. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/agent.py +33 -86
  9. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/main.py +6 -2
  10. jarvis_ai_assistant-0.1.11/src/jarvis/models/__pycache__/base.cpython-313.pyc +0 -0
  11. jarvis_ai_assistant-0.1.11/src/jarvis/models/__pycache__/kimi.cpython-313.pyc +0 -0
  12. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/models/base.py +5 -1
  13. jarvis_ai_assistant-0.1.11/src/jarvis/models/kimi.py +258 -0
  14. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis_ai_assistant.egg-info/PKG-INFO +1 -1
  15. jarvis_ai_assistant-0.1.10/src/jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
  16. jarvis_ai_assistant-0.1.10/src/jarvis/__pycache__/agent.cpython-313.pyc +0 -0
  17. jarvis_ai_assistant-0.1.10/src/jarvis/models/__pycache__/base.cpython-313.pyc +0 -0
  18. jarvis_ai_assistant-0.1.10/src/jarvis/models/__pycache__/kimi.cpython-313.pyc +0 -0
  19. jarvis_ai_assistant-0.1.10/src/jarvis/models/kimi.py +0 -135
  20. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/MANIFEST.in +0 -0
  21. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/README.md +0 -0
  22. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/setup.cfg +0 -0
  23. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/__pycache__/models.cpython-313.pyc +0 -0
  24. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/__pycache__/tools.cpython-313.pyc +0 -0
  25. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/__pycache__/utils.cpython-313.pyc +0 -0
  26. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
  27. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/models/__init__.py +0 -0
  28. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/models/__pycache__/__init__.cpython-313.pyc +0 -0
  29. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__init__.py +0 -0
  30. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  31. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
  32. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/bing_search.cpython-313.pyc +0 -0
  33. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
  34. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
  35. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
  36. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
  37. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
  38. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
  39. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
  40. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
  41. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
  42. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
  43. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/base.py +0 -0
  44. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/file_ops.py +0 -0
  45. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/tools/shell.py +0 -0
  46. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis/utils.py +0 -0
  47. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +0 -0
  48. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
  49. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
  50. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/src/jarvis_ai_assistant.egg-info/requires.txt +0 -0
  51. {jarvis_ai_assistant-0.1.10 → jarvis_ai_assistant-0.1.11}/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.10
3
+ Version: 0.1.11
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jarvis-ai-assistant"
7
- version = "0.1.10"
7
+ version = "0.1.11"
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" }]
@@ -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.10",
5
+ version="0.1.11",
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",
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.10"
3
+ __version__ = "0.1.11"
@@ -11,7 +11,7 @@ import os
11
11
  from datetime import datetime
12
12
 
13
13
  class Agent:
14
- def __init__(self, model: BaseModel, tool_registry: ToolRegistry, name: str = "Jarvis", is_sub_agent: bool = False):
14
+ def __init__(self, model: BaseModel, tool_registry: ToolRegistry, name: str = "Jarvis"):
15
15
  """Initialize Agent with a model, optional tool registry and name
16
16
 
17
17
  Args:
@@ -23,8 +23,7 @@ class Agent:
23
23
  self.model = model
24
24
  self.tool_registry = tool_registry or ToolRegistry(model)
25
25
  self.name = name
26
- self.is_sub_agent = is_sub_agent
27
- self.messages = []
26
+ self.prompt = ""
28
27
 
29
28
 
30
29
  @staticmethod
@@ -71,19 +70,28 @@ class Agent:
71
70
 
72
71
  return []
73
72
 
74
- def _call_model(self, messages: List[Dict]) -> Dict:
73
+ def _call_model(self, message: str) -> str:
75
74
  """调用模型获取响应"""
76
75
  try:
77
- return self.model.chat(
78
- messages=messages,
79
- )
76
+ return self.model.chat(message)
80
77
  except Exception as e:
81
78
  raise Exception(f"{self.name}: 模型调用失败: {str(e)}")
82
79
 
83
- def run(self, user_input: str) -> str:
84
- """处理用户输入并返回响应,返回任务总结报告"""
80
+ def run(self, user_input: str, file_list: Optional[List[str]] = None):
81
+ """处理用户输入并返回响应,返回任务总结报告
82
+
83
+ Args:
84
+ user_input: 用户输入的任务描述
85
+ file_list: 可选的文件列表,默认为None
86
+
87
+ Returns:
88
+ str: 任务总结报告
89
+ """
85
90
  self.clear_history()
86
91
 
92
+ if file_list:
93
+ self.model.upload_files(file_list)
94
+
87
95
  # 显示任务开始
88
96
  PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
89
97
 
@@ -93,20 +101,10 @@ class Agent:
93
101
  tools_prompt += f" 描述: {tool['description']}\n"
94
102
  tools_prompt += f" 参数: {tool['parameters']}\n"
95
103
 
96
- self.messages = [
97
- {
98
- "role": "user",
99
- "content": f"""你是 {self.name},一个严格遵循 ReAct 框架进行逐步推理和行动的 AI 助手。
104
+ self.prompt =f"""你是 {self.name},一个严格遵循 ReAct 框架进行逐步推理和行动的 AI 助手。
100
105
 
101
106
  {tools_prompt}
102
107
 
103
- 关键规则:
104
- ‼️ 禁止创建虚假对话
105
- ‼️ 禁止假设用户回应
106
- ‼️ 禁止在没有实际用户输入时继续
107
- ‼️ 只回应用户实际说的内容
108
- ‼️ 每个动作后停止并等待
109
-
110
108
  ReAct 框架:
111
109
  1. 思考
112
110
  - 分析当前情况
@@ -123,7 +121,7 @@ ReAct 框架:
123
121
  - 只使用下面列出的工具
124
122
  - 每次只执行一个工具
125
123
  - 工具由用户手动执行
126
- - 必须使用有效的YAML格式:
124
+ - 必须使用有效合法的YAML格式:
127
125
  <START_TOOL_CALL>
128
126
  name: tool_name
129
127
  arguments:
@@ -164,7 +162,6 @@ arguments:
164
162
  ‼️ 工具调用必须是有效的YAML格式
165
163
  ‼️ 参数必须正确缩进
166
164
  ‼️ 使用YAML块样式(|)表示多行值
167
- ‼️ <END_TOOL_CALL>后的内容将被丢弃
168
165
  ‼️ 工具由用户手动执行
169
166
  ‼️ 等待用户提供工具执行结果
170
167
  ‼️ 不要假设或想象用户回应
@@ -190,32 +187,21 @@ arguments:
190
187
  任务:
191
188
  {user_input}
192
189
  """
193
- }
194
- ]
190
+
195
191
 
196
192
  while True:
197
193
  try:
198
194
  # 显示思考状态
199
195
  PrettyOutput.print("分析任务...", OutputType.PROGRESS)
200
196
 
201
- current_response = self._call_model(self.messages)
197
+ current_response = self._call_model(self.prompt)
202
198
 
203
199
  try:
204
200
  result = Agent.extract_tool_calls(current_response)
205
201
  except Exception as e:
206
202
  PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
207
- self.messages.append({
208
- "role": "user",
209
- "content": f"工具调用错误: {str(e)}"
210
- })
203
+ self.prompt = f"工具调用错误: {str(e)}"
211
204
  continue
212
-
213
-
214
- self.messages.append({
215
- "role": "assistant",
216
- "content": current_response
217
- })
218
-
219
205
 
220
206
  if len(result) > 0:
221
207
  try:
@@ -227,68 +213,29 @@ arguments:
227
213
  PrettyOutput.print(str(e), OutputType.ERROR)
228
214
  tool_result = f"Tool call failed: {str(e)}"
229
215
 
230
- self.messages.append({
231
- "role": "user",
232
- "content": tool_result
233
- })
216
+ self.prompt = tool_result
234
217
  continue
235
218
 
236
219
  # 获取用户输入
237
220
  user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
238
- if not user_input:
239
- # 只有子Agent才需要生成任务总结
240
- if self.is_sub_agent:
241
- PrettyOutput.print("生成任务总结...", OutputType.PROGRESS)
242
-
243
- # 生成任务总结
244
- summary_prompt = {
245
- "role": "user",
246
- "content": """任务已完成。请根据之前的分析和执行结果,提供一个简明的任务总结,包括:
247
-
248
- 1. 关键发现:
249
- - 分析过程中的重要发现
250
- - 工具执行的关键结果
251
- - 发现的重要数据
252
-
253
- 2. 执行成果:
254
- - 任务完成情况
255
- - 具体实现结果
256
- - 达成的目标
257
-
258
- 请直接描述事实和实际结果,保持简洁明了。"""
259
- }
260
-
261
- while True:
262
- try:
263
- summary = self._call_model(self.messages + [summary_prompt])
264
-
265
- # 显示任务总结
266
- PrettyOutput.section("任务总结", OutputType.SUCCESS)
267
- PrettyOutput.print(summary, OutputType.SYSTEM)
268
- PrettyOutput.section("任务完成", OutputType.SUCCESS)
269
-
270
- return summary
271
-
272
- except Exception as e:
273
- PrettyOutput.print(str(e), OutputType.ERROR)
274
- else:
275
- # 顶层Agent直接返回空字符串
276
- PrettyOutput.section("任务完成", OutputType.SUCCESS)
277
- return ""
278
-
279
221
  if user_input == "__interrupt__":
280
222
  PrettyOutput.print("任务已取消", OutputType.WARNING)
281
223
  return "Task cancelled by user"
224
+ if user_input:
225
+ self.prompt = user_input
226
+ continue
227
+
228
+ if not user_input:
229
+ PrettyOutput.section("任务完成", OutputType.SUCCESS)
230
+ return
231
+
232
+
282
233
 
283
- self.messages.append({
284
- "role": "user",
285
- "content": user_input
286
- })
287
234
 
288
235
  except Exception as e:
289
236
  PrettyOutput.print(str(e), OutputType.ERROR)
290
237
 
291
238
  def clear_history(self):
292
239
  """清除对话历史,只保留系统提示"""
293
- self.messages = []
240
+ self.prompt = ""
294
241
  self.model.reset()
@@ -72,6 +72,10 @@ def select_task(tasks: dict) -> str:
72
72
 
73
73
  def main():
74
74
  """Main entry point for Jarvis."""
75
+ # Add argument parser
76
+ parser = argparse.ArgumentParser(description='Jarvis AI Assistant')
77
+ parser.add_argument('-f', '--files', nargs='*', help='List of files to process')
78
+ args = parser.parse_args()
75
79
 
76
80
  load_env_from_file()
77
81
 
@@ -95,7 +99,7 @@ def main():
95
99
  selected_task = select_task(tasks)
96
100
  if selected_task:
97
101
  PrettyOutput.print(f"\n执行任务: {selected_task}", OutputType.INFO)
98
- agent.run(selected_task)
102
+ agent.run(selected_task, args.files)
99
103
  return 0
100
104
 
101
105
  # 如果没有选择预定义任务,进入交互模式
@@ -104,7 +108,7 @@ def main():
104
108
  user_input = get_multiline_input("请输入您的任务(输入空行退出):")
105
109
  if not user_input or user_input == "__interrupt__":
106
110
  break
107
- agent.run(user_input)
111
+ agent.run(user_input, args.files)
108
112
  except Exception as e:
109
113
  PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
110
114
 
@@ -5,10 +5,14 @@ class BaseModel(ABC):
5
5
  """大语言模型基类"""
6
6
 
7
7
  @abstractmethod
8
- def chat(self, messages: List[Dict]) -> str:
8
+ def chat(self, message: str) -> str:
9
9
  """执行对话"""
10
10
  pass
11
11
 
12
+ def upload_files(self, file_list: List[str]) -> List[Dict]:
13
+ """上传文件"""
14
+ pass
15
+
12
16
  def reset(self):
13
17
  """重置模型"""
14
18
  pass
@@ -0,0 +1,258 @@
1
+ from typing import Dict, List, Optional
2
+ import requests
3
+ import json
4
+ import os
5
+ import mimetypes
6
+ from .base import BaseModel
7
+ from ..utils import PrettyOutput, OutputType
8
+ import time
9
+
10
+ class KimiModel(BaseModel):
11
+ """Kimi模型实现"""
12
+
13
+ def __init__(self, api_key: str = None):
14
+ """
15
+ 初始化Kimi模型
16
+ Args:
17
+ api_key: Kimi API密钥,如果不提供则从环境变量获取
18
+ """
19
+ self.api_key = api_key or os.getenv("KIMI_API_KEY")
20
+ if not self.api_key:
21
+ raise Exception("KIMI_API_KEY is not set")
22
+ self.auth_header = f"Bearer {self.api_key}"
23
+ self.chat_id = ""
24
+ self.uploaded_files = [] # 存储已上传文件的信息
25
+ self.first_chat = True # 添加标记,用于判断是否是第一次对话
26
+
27
+ def _create_chat(self) -> bool:
28
+ """创建新的对话会话"""
29
+ url = "https://kimi.moonshot.cn/api/chat"
30
+ payload = json.dumps({
31
+ "name": "未命名会话",
32
+ "is_example": False,
33
+ "kimiplus_id": "kimi"
34
+ })
35
+ headers = {
36
+ 'Authorization': self.auth_header,
37
+ 'Content-Type': 'application/json'
38
+ }
39
+ try:
40
+ response = requests.request("POST", url, headers=headers, data=payload)
41
+ self.chat_id = response.json()["id"]
42
+ return True
43
+ except Exception as e:
44
+ PrettyOutput.print(f"Failed to create chat: {e}", OutputType.ERROR)
45
+ return False
46
+
47
+ def _get_presigned_url(self, filename: str) -> Dict:
48
+ """获取预签名上传URL"""
49
+ url = "https://kimi.moonshot.cn/api/pre-sign-url"
50
+ mime_type, _ = mimetypes.guess_type(filename)
51
+ action = "image" if mime_type and mime_type.startswith('image/') else "file"
52
+
53
+ payload = json.dumps({
54
+ "action": action,
55
+ "name": os.path.basename(filename)
56
+ })
57
+
58
+ headers = {
59
+ 'Authorization': self.auth_header,
60
+ 'Content-Type': 'application/json'
61
+ }
62
+
63
+ response = requests.post(url, headers=headers, data=payload)
64
+ return response.json()
65
+
66
+ def _upload_file(self, file_path: str, presigned_url: str) -> bool:
67
+ """上传文件到预签名URL"""
68
+ try:
69
+ with open(file_path, 'rb') as f:
70
+ content = f.read()
71
+ response = requests.put(presigned_url, data=content)
72
+ return response.status_code == 200
73
+ except Exception as e:
74
+ PrettyOutput.print(f"Failed to upload file: {e}", OutputType.ERROR)
75
+ return False
76
+
77
+ def _get_file_info(self, file_data: Dict, name: str) -> Dict:
78
+ """获取文件信息"""
79
+ url = "https://kimi.moonshot.cn/api/file"
80
+ payload = json.dumps({
81
+ "type": "file",
82
+ "name": name,
83
+ "object_name": file_data["object_name"],
84
+ "chat_id": self.chat_id,
85
+ "file_id": file_data.get("file_id", "")
86
+ })
87
+
88
+ headers = {
89
+ 'Authorization': self.auth_header,
90
+ 'Content-Type': 'application/json'
91
+ }
92
+
93
+ response = requests.post(url, headers=headers, data=payload)
94
+ return response.json()
95
+
96
+ def _wait_for_parse(self, file_id: str) -> bool:
97
+ """等待文件解析完成"""
98
+ url = "https://kimi.moonshot.cn/api/file/parse_process"
99
+ headers = {
100
+ 'Authorization': self.auth_header,
101
+ 'Content-Type': 'application/json'
102
+ }
103
+
104
+ max_retries = 30
105
+ retry_count = 0
106
+
107
+ while retry_count < max_retries:
108
+ payload = json.dumps({"ids": [file_id]})
109
+ response = requests.post(url, headers=headers, data=payload, stream=True)
110
+
111
+ for line in response.iter_lines():
112
+ if not line:
113
+ continue
114
+
115
+ line = line.decode('utf-8')
116
+ if not line.startswith("data: "):
117
+ continue
118
+
119
+ try:
120
+ data = json.loads(line[6:])
121
+ if data.get("event") == "resp":
122
+ status = data.get("file_info", {}).get("status")
123
+ if status == "parsed":
124
+ return True
125
+ elif status == "failed":
126
+ return False
127
+ except json.JSONDecodeError:
128
+ continue
129
+
130
+ retry_count += 1
131
+ time.sleep(1)
132
+
133
+ return False
134
+
135
+ def upload_files(self, file_list: List[str]) -> List[Dict]:
136
+ """上传文件列表并返回文件信息"""
137
+ if not file_list:
138
+ return []
139
+
140
+ PrettyOutput.print("开始处理文件上传...", OutputType.PROGRESS)
141
+
142
+ if not self.chat_id:
143
+ PrettyOutput.print("创建新的对话会话...", OutputType.PROGRESS)
144
+ if not self._create_chat():
145
+ raise Exception("Failed to create chat session")
146
+
147
+ uploaded_files = []
148
+ for index, file_path in enumerate(file_list, 1):
149
+ try:
150
+ PrettyOutput.print(f"处理文件 [{index}/{len(file_list)}]: {file_path}", OutputType.PROGRESS)
151
+
152
+ # 获取预签名URL
153
+ PrettyOutput.print("获取上传URL...", OutputType.PROGRESS)
154
+ presigned_data = self._get_presigned_url(file_path)
155
+
156
+ # 上传文件
157
+ PrettyOutput.print("上传文件内容...", OutputType.PROGRESS)
158
+ if self._upload_file(file_path, presigned_data["url"]):
159
+ # 获取文件信息
160
+ PrettyOutput.print("获取文件信息...", OutputType.PROGRESS)
161
+ file_info = self._get_file_info(presigned_data, os.path.basename(file_path))
162
+
163
+ # 等待文件解析
164
+ PrettyOutput.print("等待文件解析完成...", OutputType.PROGRESS)
165
+ if self._wait_for_parse(file_info["id"]):
166
+ uploaded_files.append(file_info)
167
+ PrettyOutput.print(f"✓ 文件处理成功: {file_path}", OutputType.SUCCESS)
168
+ else:
169
+ PrettyOutput.print(f"✗ 文件解析失败: {file_path}", OutputType.ERROR)
170
+ else:
171
+ PrettyOutput.print(f"✗ 文件上传失败: {file_path}", OutputType.ERROR)
172
+
173
+ except Exception as e:
174
+ PrettyOutput.print(f"✗ 处理文件出错 {file_path}: {str(e)}", OutputType.ERROR)
175
+ continue
176
+
177
+ if uploaded_files:
178
+ PrettyOutput.print(f"成功处理 {len(uploaded_files)}/{len(file_list)} 个文件", OutputType.SUCCESS)
179
+ else:
180
+ PrettyOutput.print("没有文件成功处理", OutputType.WARNING)
181
+
182
+ self.uploaded_files = uploaded_files
183
+ return uploaded_files
184
+
185
+ def chat(self, message: str) -> str:
186
+ """发送消息并获取响应"""
187
+ if not self.chat_id:
188
+ PrettyOutput.print("创建新的对话会话...", OutputType.PROGRESS)
189
+ if not self._create_chat():
190
+ raise Exception("Failed to create chat session")
191
+
192
+ url = f"https://kimi.moonshot.cn/api/chat/{self.chat_id}/completion/stream"
193
+
194
+ # 只在第一次对话时带上文件引用
195
+ refs = []
196
+ refs_file = []
197
+ if self.first_chat and self.uploaded_files:
198
+ PrettyOutput.print(f"首次对话,引用 {len(self.uploaded_files)} 个文件...", OutputType.PROGRESS)
199
+ refs = [f["id"] for f in self.uploaded_files]
200
+ refs_file = self.uploaded_files
201
+ self.first_chat = False
202
+
203
+ PrettyOutput.print("发送请求...", OutputType.PROGRESS)
204
+ payload = {
205
+ "messages": [{"role": "user", "content": message}],
206
+ "use_search": True,
207
+ "extend": {"sidebar": True},
208
+ "kimiplus_id": "kimi",
209
+ "use_research": False,
210
+ "use_math": False,
211
+ "refs": refs,
212
+ "refs_file": refs_file
213
+ }
214
+
215
+ headers = {
216
+ 'Authorization': self.auth_header,
217
+ 'Content-Type': 'application/json'
218
+ }
219
+
220
+ try:
221
+ response = requests.post(url, headers=headers, json=payload, stream=True)
222
+ full_response = ""
223
+
224
+ PrettyOutput.print("接收响应...", OutputType.PROGRESS)
225
+ for line in response.iter_lines():
226
+ if not line:
227
+ continue
228
+
229
+ line = line.decode('utf-8')
230
+ if not line.startswith("data: "):
231
+ continue
232
+
233
+ try:
234
+ data = json.loads(line[6:])
235
+ if data.get("event") == "cmpl":
236
+ text = data.get("text", "")
237
+ if text:
238
+ PrettyOutput.print_stream(text, OutputType.SYSTEM)
239
+ full_response += text
240
+ except json.JSONDecodeError:
241
+ continue
242
+
243
+ PrettyOutput.print_stream_end()
244
+ return full_response
245
+
246
+ except Exception as e:
247
+ raise Exception(f"Chat failed: {str(e)}")
248
+
249
+ def reset(self):
250
+ """重置对话"""
251
+ self.chat_id = ""
252
+ self.uploaded_files = []
253
+ self.first_chat = True # 重置first_chat标记
254
+
255
+
256
+ if __name__ == "__main__":
257
+ kimi = KimiModel()
258
+ print(kimi.chat([{"role": "user", "content": "ollama如何部署"}]))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.10
3
+ Version: 0.1.11
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
@@ -1,135 +0,0 @@
1
- from typing import Dict, List
2
- import requests
3
- import json
4
- import os
5
- from .base import BaseModel
6
- from ..utils import PrettyOutput, OutputType
7
- import time
8
-
9
- class KimiModel(BaseModel):
10
- """Kimi模型实现"""
11
-
12
- def __init__(self, api_key: str = None):
13
- """
14
- 初始化Kimi模型
15
- Args:
16
- api_key: Kimi API密钥,如果不提供则从环境变量获取
17
- """
18
- self.api_key = api_key or os.getenv("KIMI_API_KEY")
19
- if not self.api_key:
20
- raise Exception("KIMI_API_KEY is not set")
21
- self.auth_header = f"Bearer {self.api_key}"
22
- self.chat_id = ""
23
-
24
- def _create_chat(self) -> bool:
25
- """创建新的对话会话"""
26
- url = "https://kimi.moonshot.cn/api/chat"
27
- payload = json.dumps({
28
- "name": "未命名会话",
29
- "is_example": False,
30
- "kimiplus_id": "kimi"
31
- })
32
- headers = {
33
- 'Authorization': self.auth_header,
34
- 'Content-Type': 'application/json'
35
- }
36
- try:
37
- response = requests.request("POST", url, headers=headers, data=payload)
38
- self.chat_id = response.json()["id"]
39
- return True
40
- except Exception as e:
41
- PrettyOutput.print(f"Failed to create chat: {e}", OutputType.ERROR)
42
- return False
43
-
44
- def chat(self, messages: List[Dict]) -> str:
45
- """
46
- 实现BaseModel的chat方法
47
- Args:
48
- messages: 对话消息列表
49
- Returns:
50
- str: 模型的回复内容
51
- """
52
- if not self.chat_id:
53
- if not self._create_chat():
54
- raise Exception("Failed to create chat")
55
-
56
- url = f"https://kimi.moonshot.cn/api/chat/{self.chat_id}/completion/stream"
57
- payload = json.dumps({
58
- "messages": [messages[-1]],
59
- "use_search": True,
60
- "extend": {
61
- "sidebar": True
62
- },
63
- "kimiplus_id": "kimi",
64
- "use_research": False,
65
- "use_math": False,
66
- "refs": [],
67
- "refs_file": []
68
- })
69
- headers = {
70
- 'Authorization': self.auth_header,
71
- 'Content-Type': 'application/json'
72
- }
73
-
74
- try:
75
- response = requests.request("POST", url, headers=headers, data=payload, stream=True)
76
- full_response = ""
77
- references = []
78
-
79
- for line in response.iter_lines():
80
- if not line:
81
- continue
82
-
83
- line = line.decode('utf-8')
84
- if not line.startswith("data: "):
85
- continue
86
-
87
- try:
88
- data = json.loads(line[6:]) # 去掉 "data: " 前缀
89
-
90
- # 处理不同类型的事件
91
- if data.get("event") == "cmpl":
92
- if "text" in data:
93
- text = data["text"]
94
- PrettyOutput.print_stream(text, OutputType.SYSTEM) # 使用统一的流式输出
95
- full_response += text
96
- elif data.get("event") == "ref_docs":
97
- if "ref_cards" in data:
98
- for ref in data["ref_cards"]:
99
- reference = {
100
- "title": ref.get("title", ""),
101
- "url": ref.get("url", ""),
102
- "abstract": ref.get("abstract", "")
103
- }
104
- references.append(reference)
105
- PrettyOutput.print(f"\n参考来源: {reference['title']} - {reference['url']}",
106
- OutputType.INFO)
107
- elif data.get("event") == "error":
108
- error_msg = data.get("error", "Unknown error")
109
- raise Exception(f"Chat error: {error_msg}")
110
- elif data.get("event") == "all_done":
111
- break
112
-
113
- except json.JSONDecodeError:
114
- continue
115
-
116
- # 如果有参考文献,在回答后面添加引用信息
117
- if references:
118
- full_response += "\n\n参考来源:\n"
119
- for i, ref in enumerate(references, 1):
120
- full_response += f"{i}. {ref['title']} - {ref['url']}\n"
121
-
122
- PrettyOutput.print_stream_end() # 结束流式输出
123
- return full_response
124
-
125
- except Exception as e:
126
- raise Exception(f"Kimi API调用失败: {str(e)}")
127
-
128
- def reset(self):
129
- """重置模型"""
130
- self.chat_id = ""
131
-
132
-
133
- if __name__ == "__main__":
134
- kimi = KimiModel()
135
- print(kimi.chat([{"role": "user", "content": "ollama如何部署"}]))