jarvis-ai-assistant 0.1.32__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.
Files changed (55) hide show
  1. jarvis/__init__.py +3 -0
  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__/tools.cpython-313.pyc +0 -0
  7. jarvis/__pycache__/utils.cpython-313.pyc +0 -0
  8. jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
  9. jarvis/agent.py +289 -0
  10. jarvis/main.py +148 -0
  11. jarvis/models/__init__.py +3 -0
  12. jarvis/models/__pycache__/__init__.cpython-313.pyc +0 -0
  13. jarvis/models/__pycache__/base.cpython-313.pyc +0 -0
  14. jarvis/models/__pycache__/kimi.cpython-313.pyc +0 -0
  15. jarvis/models/__pycache__/openai.cpython-313.pyc +0 -0
  16. jarvis/models/__pycache__/oyi.cpython-313.pyc +0 -0
  17. jarvis/models/__pycache__/registry.cpython-313.pyc +0 -0
  18. jarvis/models/base.py +39 -0
  19. jarvis/models/kimi.py +389 -0
  20. jarvis/models/openai.py +96 -0
  21. jarvis/models/oyi.py +271 -0
  22. jarvis/models/registry.py +199 -0
  23. jarvis/tools/__init__.py +5 -0
  24. jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  25. jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
  26. jarvis/tools/__pycache__/bing_search.cpython-313.pyc +0 -0
  27. jarvis/tools/__pycache__/calculator.cpython-313.pyc +0 -0
  28. jarvis/tools/__pycache__/calculator_tool.cpython-313.pyc +0 -0
  29. jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
  30. jarvis/tools/__pycache__/generator.cpython-313.pyc +0 -0
  31. jarvis/tools/__pycache__/methodology.cpython-313.pyc +0 -0
  32. jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
  33. jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
  34. jarvis/tools/__pycache__/registry.cpython-313.pyc +0 -0
  35. jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
  36. jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
  37. jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
  38. jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
  39. jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
  40. jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
  41. jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
  42. jarvis/tools/base.py +23 -0
  43. jarvis/tools/file_ops.py +110 -0
  44. jarvis/tools/generator.py +172 -0
  45. jarvis/tools/methodology.py +145 -0
  46. jarvis/tools/registry.py +183 -0
  47. jarvis/tools/shell.py +78 -0
  48. jarvis/tools/sub_agent.py +82 -0
  49. jarvis/utils.py +202 -0
  50. jarvis_ai_assistant-0.1.32.dist-info/LICENSE +21 -0
  51. jarvis_ai_assistant-0.1.32.dist-info/METADATA +345 -0
  52. jarvis_ai_assistant-0.1.32.dist-info/RECORD +55 -0
  53. jarvis_ai_assistant-0.1.32.dist-info/WHEEL +5 -0
  54. jarvis_ai_assistant-0.1.32.dist-info/entry_points.txt +2 -0
  55. jarvis_ai_assistant-0.1.32.dist-info/top_level.txt +1 -0
jarvis/models/oyi.py ADDED
@@ -0,0 +1,271 @@
1
+ import mimetypes
2
+ import os
3
+ from typing import Dict, List
4
+ from jarvis.models.base import BaseModel
5
+ from jarvis.utils import PrettyOutput, OutputType
6
+ import requests
7
+ import json
8
+
9
+ class OyiModel(BaseModel):
10
+ """Oyi model implementation"""
11
+
12
+ model_name = "oyi"
13
+ BASE_URL = "https://api-10086.rcouyi.com"
14
+
15
+ def __init__(self):
16
+ """Initialize model"""
17
+ self.messages = []
18
+ self.system_message = ""
19
+ self.conversation = None
20
+ self.upload_files = []
21
+ self.first_chat = True
22
+ self.model = os.getenv("OYI_MODEL") or "deepseek-chat"
23
+ self.token = os.getenv("OYI_API_KEY")
24
+ if not all([self.model, self.token]):
25
+ raise Exception("OYI_MODEL or OYI_API_KEY is not set")
26
+
27
+
28
+ def create_conversation(self) -> bool:
29
+ """Create a new conversation"""
30
+ try:
31
+ headers = {
32
+ 'Authorization': f'Bearer {self.token}',
33
+ 'Content-Type': 'application/json',
34
+ 'Accept': 'application/json',
35
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
36
+ }
37
+
38
+ payload = {
39
+ "id": 0,
40
+ "roleId": 0,
41
+ "title": "新对话",
42
+ "isLock": False,
43
+ "systemMessage": "",
44
+ "params": json.dumps({
45
+ "model": "gpt-4o-mini",
46
+ "is_webSearch": True,
47
+ "message": [],
48
+ "systemMessage": None,
49
+ "requestMsgCount": 1000,
50
+ "temperature": 0.8,
51
+ "speechVoice": "Alloy",
52
+ "max_tokens": 8192,
53
+ "chatPluginIds": []
54
+ })
55
+ }
56
+
57
+ response = requests.post(
58
+ f"{self.BASE_URL}/chatapi/chat/save",
59
+ headers=headers,
60
+ json=payload
61
+ )
62
+
63
+ if response.status_code == 200:
64
+ data = response.json()
65
+ if data['code'] == 200 and data['type'] == 'success':
66
+ self.conversation = data
67
+ PrettyOutput.print(f"创建会话成功: {data['result']['id']}", OutputType.SUCCESS)
68
+ return True
69
+ else:
70
+ PrettyOutput.print(f"创建会话失败: {data['message']}", OutputType.ERROR)
71
+ return False
72
+ else:
73
+ PrettyOutput.print(f"创建会话失败: {response.status_code}", OutputType.ERROR)
74
+ return False
75
+
76
+ except Exception as e:
77
+ PrettyOutput.print(f"创建会话异常: {str(e)}", OutputType.ERROR)
78
+ return False
79
+
80
+ def set_system_message(self, message: str):
81
+ """Set system message"""
82
+ self.system_message = message
83
+
84
+ def chat(self, message: str) -> str:
85
+ """Execute chat with the model
86
+
87
+ Args:
88
+ message: User input message
89
+
90
+ Returns:
91
+ str: Model response
92
+ """
93
+ try:
94
+ # 确保有会话ID
95
+ if not self.conversation:
96
+ if not self.create_conversation():
97
+ raise Exception("Failed to create conversation")
98
+
99
+ # 1. 发送消息
100
+ headers = {
101
+ 'Authorization': f'Bearer {self.token}',
102
+ 'Content-Type': 'application/json',
103
+ 'Accept': 'application/json, text/plain, */*',
104
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
105
+ 'Origin': 'https://ai.rcouyi.com',
106
+ 'Referer': 'https://ai.rcouyi.com/'
107
+ }
108
+
109
+ payload = {
110
+ "topicId": self.conversation['result']['id'],
111
+ "messages": self.messages,
112
+ "content": message,
113
+ "contentFiles": []
114
+ }
115
+
116
+ # 如果有上传的文件,添加到请求中
117
+ if self.first_chat:
118
+ if self.upload_files:
119
+ for file_data in self.upload_files:
120
+ file_info = {
121
+ "contentType": 1, # 1 表示图片
122
+ "fileUrl": file_data['result']['url'],
123
+ "fileId": file_data['result']['id'],
124
+ "fileName": file_data['result']['fileName']
125
+ }
126
+ payload["contentFiles"].append(file_info)
127
+ # 清空已使用的文件列表
128
+ self.upload_files = []
129
+ message = self.system_message + "\n" + message
130
+ payload["content"] = message
131
+ self.first_chat = False
132
+
133
+ self.messages.append({"role": "user", "content": message})
134
+
135
+ # 发送消息
136
+ response = requests.post(
137
+ f"{self.BASE_URL}/chatapi/chat/message",
138
+ headers=headers,
139
+ json=payload
140
+ )
141
+
142
+ if response.status_code != 200:
143
+ error_msg = f"聊天请求失败: {response.status_code}"
144
+ PrettyOutput.print(error_msg, OutputType.ERROR)
145
+ raise Exception(error_msg)
146
+
147
+ data = response.json()
148
+ if data['code'] != 200 or data['type'] != 'success':
149
+ error_msg = f"聊天失败: {data.get('message', '未知错误')}"
150
+ PrettyOutput.print(error_msg, OutputType.ERROR)
151
+ raise Exception(error_msg)
152
+
153
+ message_id = data['result'][-1]
154
+
155
+ # 获取响应内容
156
+ response = requests.post(
157
+ f"{self.BASE_URL}/chatapi/chat/message/{message_id}",
158
+ headers=headers
159
+ )
160
+
161
+ if response.status_code == 200:
162
+ PrettyOutput.print(response.text, OutputType.SYSTEM)
163
+ self.messages.append({"role": "assistant", "content": response.text})
164
+ return response.text
165
+ else:
166
+ error_msg = f"获取响应失败: {response.status_code}"
167
+ PrettyOutput.print(error_msg, OutputType.ERROR)
168
+ raise Exception(error_msg)
169
+
170
+ except Exception as e:
171
+ PrettyOutput.print(f"聊天异常: {str(e)}", OutputType.ERROR)
172
+ raise e
173
+
174
+ def name(self) -> str:
175
+ """Return model name"""
176
+ return self.model_name
177
+
178
+ def reset(self):
179
+ """Reset model state"""
180
+ self.messages = []
181
+ self.conversation = None
182
+ self.upload_files = []
183
+ self.first_chat = True
184
+
185
+ def delete_chat(self) -> bool:
186
+ """Delete current chat session"""
187
+ try:
188
+ if not self.conversation:
189
+ return True
190
+
191
+ headers = {
192
+ 'Authorization': f'Bearer {self.token}',
193
+ 'Content-Type': 'application/json',
194
+ 'Accept': 'application/json, text/plain, */*',
195
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
196
+ 'Origin': 'https://ai.rcouyi.com',
197
+ 'Referer': 'https://ai.rcouyi.com/'
198
+ }
199
+
200
+ response = requests.post(
201
+ f"{self.BASE_URL}/chatapi/chat/{self.conversation['result']['id']}",
202
+ headers=headers,
203
+ json={}
204
+ )
205
+
206
+ if response.status_code == 200:
207
+ data = response.json()
208
+ if data['code'] == 200 and data['type'] == 'success':
209
+ PrettyOutput.print("会话删除成功", OutputType.SUCCESS)
210
+ self.reset()
211
+ return True
212
+ else:
213
+ error_msg = f"删除会话失败: {data.get('message', '未知错误')}"
214
+ PrettyOutput.print(error_msg, OutputType.ERROR)
215
+ return False
216
+ else:
217
+ error_msg = f"删除会话请求失败: {response.status_code}"
218
+ PrettyOutput.print(error_msg, OutputType.ERROR)
219
+ return False
220
+
221
+ except Exception as e:
222
+ PrettyOutput.print(f"删除会话异常: {str(e)}", OutputType.ERROR)
223
+ return False
224
+
225
+ def upload_file(self, file_path: str) -> Dict:
226
+ """Upload a file to OYI API
227
+
228
+ Args:
229
+ file_path: Path to the file to upload
230
+
231
+ Returns:
232
+ Dict: Upload response data
233
+ """
234
+ try:
235
+ headers = {
236
+ 'Authorization': f'Bearer {self.token}',
237
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
238
+ 'Accept': '*/*',
239
+ 'DNT': '1',
240
+ 'Origin': 'https://ai.rcouyi.com',
241
+ 'Referer': 'https://ai.rcouyi.com/'
242
+ }
243
+
244
+ with open(file_path, 'rb') as f:
245
+ files = {
246
+ 'file': (os.path.basename(file_path), f, mimetypes.guess_type(file_path)[0]) # Adjust content-type based on file type
247
+ }
248
+
249
+ response = requests.post(
250
+ f"{self.BASE_URL}/chatapi/m_file/uploadfile",
251
+ headers=headers,
252
+ files=files
253
+ )
254
+
255
+ if response.status_code == 200:
256
+ data = response.json()
257
+ if data.get('code') == 200:
258
+ PrettyOutput.print("文件上传成功", OutputType.SUCCESS)
259
+ print(data)
260
+ self.upload_files.append(data)
261
+ return data
262
+ else:
263
+ PrettyOutput.print(f"文件上传失败: {data.get('message')}", OutputType.ERROR)
264
+ return None
265
+ else:
266
+ PrettyOutput.print(f"文件上传失败: {response.status_code}", OutputType.ERROR)
267
+ return None
268
+
269
+ except Exception as e:
270
+ PrettyOutput.print(f"文件上传异常: {str(e)}", OutputType.ERROR)
271
+ return None
@@ -0,0 +1,199 @@
1
+ import importlib
2
+ import inspect
3
+ import os
4
+ import sys
5
+ from typing import Dict, Type, Optional, List
6
+ from .base import BaseModel
7
+ from ..utils import PrettyOutput, OutputType
8
+
9
+ REQUIRED_METHODS = [
10
+ ('chat', ['message']), # 方法名和参数列表
11
+ ('name', []),
12
+ ('delete_chat', []),
13
+ ('reset', []),
14
+ ('set_system_message', ['message'])
15
+ ]
16
+
17
+ class ModelRegistry:
18
+ """模型注册器"""
19
+
20
+ global_model_name = "kimi"
21
+ global_model_registry = None
22
+
23
+ @staticmethod
24
+ def get_models_dir() -> str:
25
+ user_models_dir = os.path.expanduser("~/.jarvis_models")
26
+ if not os.path.exists(user_models_dir):
27
+ try:
28
+ os.makedirs(user_models_dir)
29
+ # 创建 __init__.py 使其成为 Python 包
30
+ with open(os.path.join(user_models_dir, "__init__.py"), "w") as f:
31
+ pass
32
+ PrettyOutput.print(f"已创建模型目录: {user_models_dir}", OutputType.INFO)
33
+ except Exception as e:
34
+ PrettyOutput.print(f"创建模型目录失败: {str(e)}", OutputType.ERROR)
35
+ return ""
36
+ return user_models_dir
37
+
38
+ @staticmethod
39
+ def check_model_implementation(model_class: Type[BaseModel]) -> bool:
40
+ """检查模型类是否实现了所有必要的方法
41
+
42
+ Args:
43
+ model_class: 要检查的模型类
44
+
45
+ Returns:
46
+ bool: 是否实现了所有必要的方法
47
+ """
48
+ missing_methods = []
49
+
50
+ for method_name, params in REQUIRED_METHODS:
51
+ if not hasattr(model_class, method_name):
52
+ missing_methods.append(method_name)
53
+ continue
54
+
55
+ method = getattr(model_class, method_name)
56
+ if not callable(method):
57
+ missing_methods.append(method_name)
58
+ continue
59
+
60
+ # 检查方法参数
61
+ import inspect
62
+ sig = inspect.signature(method)
63
+ method_params = [p for p in sig.parameters if p != 'self']
64
+ if len(method_params) != len(params):
65
+ missing_methods.append(f"{method_name}(参数不匹配)")
66
+
67
+ if missing_methods:
68
+ PrettyOutput.print(
69
+ f"模型 {model_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
70
+ OutputType.ERROR
71
+ )
72
+ return False
73
+
74
+ return True
75
+
76
+ @staticmethod
77
+ def load_models_from_dir(directory: str) -> Dict[str, Type[BaseModel]]:
78
+ """从指定目录加载模型
79
+
80
+ Args:
81
+ directory: 模型目录路径
82
+
83
+ Returns:
84
+ Dict[str, Type[BaseModel]]: 模型名称到模型类的映射
85
+ """
86
+ models = {}
87
+
88
+ # 确保目录存在
89
+ if not os.path.exists(directory):
90
+ PrettyOutput.print(f"模型目录不存在: {directory}", OutputType.ERROR)
91
+ return models
92
+
93
+ # 获取目录的包名
94
+ package_name = None
95
+ if directory == os.path.dirname(__file__):
96
+ package_name = "jarvis.models"
97
+
98
+ # 添加目录到Python路径
99
+ if directory not in sys.path:
100
+ sys.path.append(directory)
101
+
102
+ # 遍历目录下的所有.py文件
103
+ for filename in os.listdir(directory):
104
+ if filename.endswith('.py') and not filename.startswith('__'):
105
+ module_name = filename[:-3] # 移除.py后缀
106
+ try:
107
+ # 导入模块
108
+ if package_name:
109
+ module = importlib.import_module(f"{package_name}.{module_name}")
110
+ else:
111
+ module = importlib.import_module(module_name)
112
+
113
+ # 遍历模块中的所有类
114
+ for name, obj in inspect.getmembers(module):
115
+ # 检查是否是BaseModel的子类,但不是BaseModel本身
116
+ if (inspect.isclass(obj) and
117
+ issubclass(obj, BaseModel) and
118
+ obj != BaseModel and
119
+ hasattr(obj, 'model_name')):
120
+ # 检查模型实现
121
+ if not ModelRegistry.check_model_implementation(obj):
122
+ continue
123
+ models[obj.model_name] = obj
124
+ PrettyOutput.print(f"从 {directory} 加载模型: {obj.model_name}", OutputType.INFO)
125
+ break
126
+ except Exception as e:
127
+ PrettyOutput.print(f"加载模型 {module_name} 失败: {str(e)}", OutputType.ERROR)
128
+
129
+ return models
130
+
131
+
132
+ @staticmethod
133
+ def get_model_registry():
134
+ """获取全局模型注册器"""
135
+ if ModelRegistry.global_model_registry is None:
136
+ ModelRegistry.global_model_registry = ModelRegistry()
137
+
138
+ # 从用户模型目录加载额外模型
139
+ models_dir = ModelRegistry.get_models_dir()
140
+ if models_dir and os.path.exists(models_dir):
141
+ for model_name, model_class in ModelRegistry.load_models_from_dir(models_dir).items():
142
+ ModelRegistry.global_model_registry.register_model(model_name, model_class)
143
+ models_dir = os.path.dirname(__file__)
144
+ if models_dir and os.path.exists(models_dir):
145
+ for model_name, model_class in ModelRegistry.load_models_from_dir(models_dir).items():
146
+ ModelRegistry.global_model_registry.register_model(model_name, model_class)
147
+ return ModelRegistry.global_model_registry
148
+
149
+ def __init__(self):
150
+ """初始化模型注册器
151
+ """
152
+ self.models: Dict[str, Type[BaseModel]] = {}
153
+
154
+ @staticmethod
155
+ def get_global_model() -> BaseModel:
156
+ """获取全局模型实例"""
157
+ model = ModelRegistry.get_model_registry().create_model(ModelRegistry.global_model_name)
158
+ if not model:
159
+ raise Exception(f"Failed to create model: {ModelRegistry.global_model_name}")
160
+ return model
161
+
162
+ def register_model(self, name: str, model_class: Type[BaseModel]):
163
+ """注册模型类
164
+
165
+ Args:
166
+ name: 模型名称
167
+ model_class: 模型类
168
+ """
169
+ self.models[name] = model_class
170
+ PrettyOutput.print(f"已注册模型: {name}", OutputType.INFO)
171
+
172
+ def create_model(self, name: str) -> Optional[BaseModel]:
173
+ """创建模型实例
174
+
175
+ Args:
176
+ name: 模型名称
177
+
178
+ Returns:
179
+ BaseModel: 模型实例
180
+ """
181
+ if name not in self.models:
182
+ PrettyOutput.print(f"未找到模型: {name}", OutputType.ERROR)
183
+ return None
184
+
185
+ try:
186
+ model = self.models[name]()
187
+ PrettyOutput.print(f"已创建模型实例: {name}", OutputType.INFO)
188
+ return model
189
+ except Exception as e:
190
+ PrettyOutput.print(f"创建模型失败: {str(e)}", OutputType.ERROR)
191
+ return None
192
+
193
+ def get_available_models(self) -> List[str]:
194
+ """获取可用模型列表"""
195
+ return list(self.models.keys())
196
+
197
+ def set_global_model(self, model_name: str):
198
+ """设置全局模型"""
199
+ ModelRegistry.global_model_name = model_name
@@ -0,0 +1,5 @@
1
+ from .registry import ToolRegistry
2
+
3
+ __all__ = [
4
+ 'ToolRegistry',
5
+ ]
jarvis/tools/base.py ADDED
@@ -0,0 +1,23 @@
1
+ from typing import Dict, Any, Callable
2
+ import json
3
+
4
+
5
+
6
+ class Tool:
7
+ def __init__(self, name: str, description: str, parameters: Dict, func: Callable):
8
+ self.name = name
9
+ self.description = description
10
+ self.parameters = parameters
11
+ self.func = func
12
+
13
+ def to_dict(self) -> Dict:
14
+ """转换为工具格式"""
15
+ return {
16
+ "name": self.name,
17
+ "description": self.description,
18
+ "parameters": json.dumps(self.parameters, ensure_ascii=False)
19
+ }
20
+
21
+ def execute(self, arguments: Dict) -> Dict[str, Any]:
22
+ """执行工具函数"""
23
+ return self.func(arguments)
@@ -0,0 +1,110 @@
1
+ from typing import Dict, Any, Protocol
2
+ import os
3
+ from enum import Enum
4
+
5
+ from jarvis.utils import OutputType, PrettyOutput
6
+
7
+
8
+ class FileOperationTool:
9
+ name = "file_operation"
10
+ description = "文件操作 (read/write/append/exists)"
11
+ parameters = {
12
+ "type": "object",
13
+ "properties": {
14
+ "operation": {
15
+ "type": "string",
16
+ "enum": ["read", "write", "append", "exists"],
17
+ "description": "Type of file operation to perform"
18
+ },
19
+ "filepath": {
20
+ "type": "string",
21
+ "description": "Absolute or relative path to the file"
22
+ },
23
+ "content": {
24
+ "type": "string",
25
+ "description": "Content to write (required for write/append operations)",
26
+ "default": ""
27
+ },
28
+ "encoding": {
29
+ "type": "string",
30
+ "description": "File encoding (default: utf-8)",
31
+ "default": "utf-8"
32
+ }
33
+ },
34
+ "required": ["operation", "filepath"]
35
+ }
36
+
37
+
38
+ def execute(self, args: Dict) -> Dict[str, Any]:
39
+ """执行文件操作"""
40
+ try:
41
+ operation = args["operation"]
42
+ filepath = args["filepath"]
43
+ encoding = args.get("encoding", "utf-8")
44
+
45
+ # 记录操作和完整路径
46
+ abs_path = os.path.abspath(filepath)
47
+ PrettyOutput.print(f"文件操作: {operation} - {abs_path}", OutputType.INFO)
48
+
49
+ if operation == "exists":
50
+ exists = os.path.exists(filepath)
51
+ return {
52
+ "success": True,
53
+ "stdout": str(exists),
54
+ "stderr": ""
55
+ }
56
+
57
+ elif operation == "read":
58
+ if not os.path.exists(filepath):
59
+ return {
60
+ "success": False,
61
+ "error": f"文件不存在: {filepath}"
62
+ }
63
+
64
+ # 检查文件大小
65
+ if os.path.getsize(filepath) > 10 * 1024 * 1024: # 10MB
66
+ return {
67
+ "success": False,
68
+ "error": "文件过大 (>10MB)"
69
+ }
70
+
71
+ with open(filepath, 'r', encoding=encoding) as f:
72
+ content = f.read()
73
+ return {
74
+ "success": True,
75
+ "stdout": content,
76
+ "stderr": ""
77
+ }
78
+
79
+ elif operation in ["write", "append"]:
80
+ if not args.get("content"):
81
+ return {
82
+ "success": False,
83
+ "error": "写入/追加操作需要提供content参数"
84
+ }
85
+
86
+ # 创建目录(如果不存在)
87
+ os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
88
+
89
+ mode = 'a' if operation == "append" else 'w'
90
+ with open(filepath, mode, encoding=encoding) as f:
91
+ f.write(args["content"])
92
+
93
+ return {
94
+ "success": True,
95
+ "stdout": f"成功{operation}内容到 {filepath}",
96
+ "stderr": ""
97
+ }
98
+
99
+ else:
100
+ return {
101
+ "success": False,
102
+ "error": f"未知操作: {operation}"
103
+ }
104
+
105
+ except Exception as e:
106
+ PrettyOutput.print(str(e), OutputType.ERROR)
107
+ return {
108
+ "success": False,
109
+ "error": f"文件操作失败: {str(e)}"
110
+ }