jarvis-ai-assistant 0.1.32__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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
+ }