jarvis-ai-assistant 0.1.149__py3-none-any.whl → 0.1.151__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.

@@ -0,0 +1,590 @@
1
+ from typing import Any, Dict, List, Optional, Iterator, Callable
2
+ import requests
3
+ import json
4
+ import threading
5
+ import time
6
+ import uuid
7
+ from urllib.parse import urljoin, urlencode, parse_qs
8
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
+ from . import McpClient
10
+
11
+
12
+ class RemoteMcpClient(McpClient):
13
+ """远程MCP客户端实现
14
+
15
+ 参数:
16
+ config: 配置字典,包含以下字段:
17
+ - base_url: str - MCP服务器的基础URL
18
+ - auth_token: str - 认证令牌(可选)
19
+ - headers: Dict[str, str] - 额外的HTTP头(可选)
20
+ """
21
+ def __init__(self, config: Dict[str, Any]):
22
+ self.config = config
23
+ self.base_url = config.get('base_url', '')
24
+ if not self.base_url:
25
+ raise ValueError('No base_url specified in config')
26
+
27
+ # 设置HTTP客户端
28
+ self.session = requests.Session()
29
+ self.session.headers.update({
30
+ 'Content-Type': 'application/json',
31
+ 'Accept': 'application/json',
32
+ })
33
+
34
+ # 添加认证令牌(如果提供)
35
+ auth_token = config.get('auth_token')
36
+ if auth_token:
37
+ self.session.headers['Authorization'] = f'Bearer {auth_token}'
38
+
39
+ # 添加额外的HTTP头
40
+ extra_headers = config.get('headers', {})
41
+ self.session.headers.update(extra_headers)
42
+
43
+ # SSE相关属性
44
+ self.sse_response = None
45
+ self.sse_thread = None
46
+ self.messages_endpoint = None
47
+ self.session_id = None # 从SSE连接获取的会话ID
48
+ self.pending_requests = {} # 存储等待响应的请求 {id: Event}
49
+ self.request_results = {} # 存储请求结果 {id: result}
50
+ self.notification_handlers = {}
51
+ self.event_lock = threading.Lock()
52
+ self.request_id_counter = 0
53
+
54
+ # 初始化连接
55
+ self._initialize()
56
+
57
+ def _initialize(self) -> None:
58
+ """初始化MCP连接"""
59
+ try:
60
+ # 启动SSE连接
61
+ self._start_sse_connection()
62
+
63
+ # 等待获取消息端点和会话ID
64
+ start_time = time.time()
65
+ while (not self.messages_endpoint or not self.session_id) and time.time() - start_time < 5:
66
+ time.sleep(0.1)
67
+
68
+ if not self.messages_endpoint:
69
+ self.messages_endpoint = "/messages" # 默认端点
70
+ PrettyOutput.print(f"未获取到消息端点,使用默认值: {self.messages_endpoint}", OutputType.WARNING)
71
+
72
+ if not self.session_id:
73
+ PrettyOutput.print("未获取到会话ID", OutputType.WARNING)
74
+
75
+ # 发送初始化请求
76
+ response = self._send_request('initialize', {
77
+ 'processId': None, # 远程客户端不需要进程ID
78
+ 'clientInfo': {
79
+ 'name': 'jarvis',
80
+ 'version': '1.0.0'
81
+ },
82
+ 'capabilities': {},
83
+ 'protocolVersion': "2025-03-26"
84
+ })
85
+
86
+ # 验证服务器响应
87
+ if 'result' not in response:
88
+ raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
89
+
90
+ # 发送initialized通知
91
+ self._send_notification('notifications/initialized', {})
92
+
93
+ except Exception as e:
94
+ PrettyOutput.print(f"MCP初始化失败: {str(e)}", OutputType.ERROR)
95
+ raise
96
+
97
+ def _start_sse_connection(self) -> None:
98
+ """建立SSE连接并启动处理线程"""
99
+ try:
100
+ # 设置SSE请求头
101
+ sse_headers = dict(self.session.headers)
102
+ sse_headers.update({
103
+ 'Accept': 'text/event-stream',
104
+ 'Cache-Control': 'no-cache',
105
+ })
106
+
107
+ # 建立SSE连接
108
+ sse_url = urljoin(self.base_url, 'sse')
109
+ self.sse_response = self.session.get(
110
+ sse_url,
111
+ stream=True,
112
+ headers=sse_headers,
113
+ timeout=30
114
+ )
115
+ self.sse_response.raise_for_status()
116
+
117
+ # 启动事件处理线程
118
+ self.sse_thread = threading.Thread(target=self._process_sse_events, daemon=True)
119
+ self.sse_thread.start()
120
+
121
+ except Exception as e:
122
+ PrettyOutput.print(f"SSE连接失败: {str(e)}", OutputType.ERROR)
123
+ raise
124
+
125
+ def _process_sse_events(self) -> None:
126
+ """处理SSE事件流"""
127
+ if not self.sse_response:
128
+ return
129
+
130
+ buffer = ""
131
+ for line in self.sse_response.iter_lines(decode_unicode=True):
132
+ if line:
133
+ if line.startswith("data:"):
134
+ data = line[5:].strip()
135
+ # 检查是否包含消息端点信息
136
+ if data.startswith('/'):
137
+ # 这是消息端点信息,例如 "/messages/?session_id=xyz"
138
+ try:
139
+ # 提取消息端点路径和会话ID
140
+ url_parts = data.split('?')
141
+ self.messages_endpoint = url_parts[0]
142
+
143
+ # 如果有查询参数,尝试提取session_id
144
+ if len(url_parts) > 1:
145
+ query_string = url_parts[1]
146
+ query_params = parse_qs(query_string)
147
+ if 'session_id' in query_params:
148
+ self.session_id = query_params['session_id'][0]
149
+ except Exception as e:
150
+ PrettyOutput.print(f"解析消息端点或会话ID失败: {e}", OutputType.WARNING)
151
+ else:
152
+ buffer += data
153
+ elif line.startswith(":"): # 忽略注释行
154
+ continue
155
+ elif line.startswith("event:"): # 事件类型
156
+ continue # 我们不使用事件类型
157
+ elif line.startswith("id:"): # 事件ID
158
+ continue # 我们不使用事件ID
159
+ elif line.startswith("retry:"): # 重连时间
160
+ continue # 我们自己管理重连
161
+ else: # 空行表示事件结束
162
+ if buffer:
163
+ try:
164
+ self._handle_sse_event(buffer)
165
+ except Exception as e:
166
+ PrettyOutput.print(f"处理SSE事件出错: {e}", OutputType.ERROR)
167
+ buffer = ""
168
+
169
+ PrettyOutput.print("SSE连接已关闭", OutputType.WARNING)
170
+
171
+ def _handle_sse_event(self, data: str) -> None:
172
+ """处理单个SSE事件数据"""
173
+ try:
174
+ event_data = json.loads(data)
175
+
176
+ # 检查是请求响应还是通知
177
+ if 'id' in event_data:
178
+ # 这是一个请求的响应
179
+ req_id = event_data['id']
180
+ with self.event_lock:
181
+ self.request_results[req_id] = event_data
182
+ if req_id in self.pending_requests:
183
+ # 通知等待线程响应已到达
184
+ self.pending_requests[req_id].set()
185
+ elif 'method' in event_data:
186
+ # 这是一个通知
187
+ method = event_data.get('method', '')
188
+ params = event_data.get('params', {})
189
+
190
+ # 调用已注册的处理器
191
+ if method in self.notification_handlers:
192
+ for handler in self.notification_handlers[method]:
193
+ try:
194
+ handler(params)
195
+ except Exception as e:
196
+ PrettyOutput.print(
197
+ f"处理通知时出错 ({method}): {e}",
198
+ OutputType.ERROR
199
+ )
200
+ except json.JSONDecodeError:
201
+ PrettyOutput.print(f"无法解析SSE事件: {data}", OutputType.WARNING)
202
+ except Exception as e:
203
+ PrettyOutput.print(f"处理SSE事件时出错: {e}", OutputType.ERROR)
204
+
205
+ def register_notification_handler(self, method: str, handler: Callable) -> None:
206
+ """注册通知处理器
207
+
208
+ 参数:
209
+ method: 通知方法名
210
+ handler: 处理通知的回调函数,接收params参数
211
+ """
212
+ with self.event_lock:
213
+ if method not in self.notification_handlers:
214
+ self.notification_handlers[method] = []
215
+ self.notification_handlers[method].append(handler)
216
+
217
+ def unregister_notification_handler(self, method: str, handler: Callable) -> None:
218
+ """注销通知处理器
219
+
220
+ 参数:
221
+ method: 通知方法名
222
+ handler: 要注销的处理器函数
223
+ """
224
+ with self.event_lock:
225
+ if method in self.notification_handlers:
226
+ if handler in self.notification_handlers[method]:
227
+ self.notification_handlers[method].remove(handler)
228
+ if not self.notification_handlers[method]:
229
+ del self.notification_handlers[method]
230
+
231
+ def _get_next_request_id(self) -> str:
232
+ """获取下一个请求ID"""
233
+ with self.event_lock:
234
+ self.request_id_counter += 1
235
+ return str(self.request_id_counter)
236
+
237
+ def _send_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
238
+ """发送请求到MCP服务器
239
+
240
+ 参数:
241
+ method: 请求方法
242
+ params: 请求参数
243
+
244
+ 返回:
245
+ Dict[str, Any]: 响应结果
246
+ """
247
+ # 生成唯一请求ID
248
+ req_id = self._get_next_request_id()
249
+
250
+ # 创建事件标志,用于等待响应
251
+ event = threading.Event()
252
+
253
+ with self.event_lock:
254
+ self.pending_requests[req_id] = event
255
+
256
+ try:
257
+ # 构建请求
258
+ request = {
259
+ 'jsonrpc': '2.0',
260
+ 'method': method,
261
+ 'params': params,
262
+ 'id': req_id
263
+ }
264
+
265
+ # 尝试不同的请求发送方式
266
+ if self.session_id:
267
+ # 方法1: 使用查询参数中的session_id
268
+ query_params = {'session_id': self.session_id}
269
+ messages_url = urljoin(self.base_url, self.messages_endpoint)
270
+
271
+ # 尝试直接使用原始URL(不追加查询参数)
272
+ try:
273
+ post_response = self.session.post(
274
+ messages_url,
275
+ json=request
276
+ )
277
+ post_response.raise_for_status()
278
+ except requests.HTTPError:
279
+ # 如果失败,尝试添加会话ID到查询参数
280
+ messages_url_with_session = f"{messages_url}?{urlencode(query_params)}"
281
+ post_response = self.session.post(
282
+ messages_url_with_session,
283
+ json=request
284
+ )
285
+ post_response.raise_for_status()
286
+ else:
287
+ # 方法2: 不使用session_id
288
+ if not self.messages_endpoint:
289
+ self.messages_endpoint = "/messages"
290
+
291
+ messages_url = urljoin(self.base_url, self.messages_endpoint)
292
+
293
+ # 尝试直接使用messages端点而不带任何查询参数
294
+ try:
295
+ # 尝试1: 标准JSON-RPC格式
296
+ post_response = self.session.post(
297
+ messages_url,
298
+ json=request
299
+ )
300
+ post_response.raise_for_status()
301
+ except requests.HTTPError:
302
+ # 尝试2: JSON字符串作为请求参数
303
+ post_response = self.session.post(
304
+ messages_url,
305
+ params={'request': json.dumps(request)}
306
+ )
307
+ post_response.raise_for_status()
308
+
309
+ # 等待SSE通道返回响应(最多30秒)
310
+ if not event.wait(timeout=30):
311
+ raise TimeoutError(f"等待响应超时: {method}")
312
+
313
+ # 获取响应结果
314
+ with self.event_lock:
315
+ result = self.request_results.pop(req_id, None)
316
+ self.pending_requests.pop(req_id, None)
317
+
318
+ if result is None:
319
+ raise RuntimeError(f"未收到响应: {method}")
320
+
321
+ return result
322
+
323
+ except Exception as e:
324
+ # 清理请求状态
325
+ with self.event_lock:
326
+ self.pending_requests.pop(req_id, None)
327
+ self.request_results.pop(req_id, None)
328
+
329
+ PrettyOutput.print(f"发送请求失败: {str(e)}", OutputType.ERROR)
330
+ raise
331
+
332
+ def _send_notification(self, method: str, params: Dict[str, Any]) -> None:
333
+ """发送通知到MCP服务器(不需要响应)
334
+
335
+ 参数:
336
+ method: 通知方法
337
+ params: 通知参数
338
+ """
339
+ try:
340
+ # 构建通知
341
+ notification = {
342
+ 'jsonrpc': '2.0',
343
+ 'method': method,
344
+ 'params': params
345
+ }
346
+
347
+ # 尝试不同的请求发送方式,与_send_request保持一致
348
+ if self.session_id:
349
+ # 方法1: 使用查询参数中的session_id
350
+ query_params = {'session_id': self.session_id}
351
+ messages_url = urljoin(self.base_url, self.messages_endpoint or '/messages')
352
+
353
+ # 尝试直接使用原始URL(不追加查询参数)
354
+ try:
355
+ post_response = self.session.post(
356
+ messages_url,
357
+ json=notification
358
+ )
359
+ post_response.raise_for_status()
360
+ except requests.HTTPError:
361
+ # 如果失败,尝试添加会话ID到查询参数
362
+ messages_url_with_session = f"{messages_url}?{urlencode(query_params)}"
363
+ post_response = self.session.post(
364
+ messages_url_with_session,
365
+ json=notification
366
+ )
367
+ post_response.raise_for_status()
368
+ else:
369
+ # 方法2: 不使用session_id
370
+ if not self.messages_endpoint:
371
+ self.messages_endpoint = "/messages"
372
+
373
+ messages_url = urljoin(self.base_url, self.messages_endpoint)
374
+
375
+ # 尝试直接使用messages端点而不带任何查询参数
376
+ try:
377
+ # 尝试1: 标准JSON-RPC格式
378
+ post_response = self.session.post(
379
+ messages_url,
380
+ json=notification
381
+ )
382
+ post_response.raise_for_status()
383
+ except requests.HTTPError:
384
+ # 尝试2: JSON字符串作为请求参数
385
+ post_response = self.session.post(
386
+ messages_url,
387
+ params={'request': json.dumps(notification)}
388
+ )
389
+ post_response.raise_for_status()
390
+
391
+ except Exception as e:
392
+ PrettyOutput.print(f"发送通知失败: {str(e)}", OutputType.ERROR)
393
+ raise
394
+
395
+ def get_tool_list(self) -> List[Dict[str, Any]]:
396
+ """获取工具列表
397
+
398
+ 返回:
399
+ List[Dict[str, Any]]: 工具列表,每个工具包含以下字段:
400
+ - name: str - 工具名称
401
+ - description: str - 工具描述
402
+ - parameters: Dict - 工具参数
403
+ """
404
+ try:
405
+ response = self._send_request('tools/list', {})
406
+ if 'result' in response and 'tools' in response['result']:
407
+ # 注意这里: 响应结构是 response['result']['tools']
408
+ tools = response['result']['tools']
409
+ # 将MCP协议字段转换为内部格式
410
+ formatted_tools = []
411
+ for tool in tools:
412
+ # 从inputSchema中提取参数定义
413
+ input_schema = tool.get('inputSchema', {})
414
+ parameters = {}
415
+ if 'properties' in input_schema:
416
+ parameters = input_schema['properties']
417
+
418
+ formatted_tools.append({
419
+ 'name': tool.get('name', ''),
420
+ 'description': tool.get('description', ''),
421
+ 'parameters': parameters
422
+ })
423
+ return formatted_tools
424
+ else:
425
+ error_msg = "获取工具列表失败"
426
+ if 'error' in response:
427
+ error_msg += f": {response['error']}"
428
+ elif 'result' in response:
429
+ error_msg += f": 响应格式不正确 - {response['result']}"
430
+ else:
431
+ error_msg += ": 未知错误"
432
+
433
+ PrettyOutput.print(error_msg, OutputType.ERROR)
434
+ return []
435
+ except Exception as e:
436
+ PrettyOutput.print(f"获取工具列表失败: {str(e)}", OutputType.ERROR)
437
+ return []
438
+
439
+ def execute(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
440
+ """执行工具
441
+
442
+ 参数:
443
+ tool_name: 工具名称
444
+ arguments: 参数字典,包含工具执行所需的参数
445
+
446
+ 返回:
447
+ Dict[str, Any]: 执行结果,包含以下字段:
448
+ - success: bool - 是否执行成功
449
+ - stdout: str - 标准输出
450
+ - stderr: str - 标准错误
451
+ """
452
+ try:
453
+ response = self._send_request('tools/call', {
454
+ 'name': tool_name,
455
+ 'arguments': arguments
456
+ })
457
+ if 'result' in response:
458
+ result = response['result']
459
+ # 从content中提取输出信息
460
+ stdout = ''
461
+ stderr = ''
462
+ for content in result.get('content', []):
463
+ if content.get('type') == 'text':
464
+ stdout += content.get('text', '')
465
+ elif content.get('type') == 'error':
466
+ stderr += content.get('text', '')
467
+
468
+ return {
469
+ 'success': True,
470
+ 'stdout': stdout,
471
+ 'stderr': stderr
472
+ }
473
+ else:
474
+ return {
475
+ 'success': False,
476
+ 'stdout': '',
477
+ 'stderr': response.get('error', 'Unknown error')
478
+ }
479
+ except Exception as e:
480
+ PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
481
+ return {
482
+ 'success': False,
483
+ 'stdout': '',
484
+ 'stderr': str(e)
485
+ }
486
+
487
+ def get_resource_list(self) -> List[Dict[str, Any]]:
488
+ """获取资源列表
489
+
490
+ 返回:
491
+ List[Dict[str, Any]]: 资源列表,每个资源包含以下字段:
492
+ - uri: str - 资源的唯一标识符
493
+ - name: str - 资源的名称
494
+ - description: str - 资源的描述(可选)
495
+ - mimeType: str - 资源的MIME类型(可选)
496
+ """
497
+ try:
498
+ response = self._send_request('resources/list', {})
499
+ if 'result' in response and 'resources' in response['result']:
500
+ return response['result']['resources']
501
+ else:
502
+ error_msg = "获取资源列表失败"
503
+ if 'error' in response:
504
+ error_msg += f": {response['error']}"
505
+ else:
506
+ error_msg += ": 未知错误"
507
+ PrettyOutput.print(error_msg, OutputType.ERROR)
508
+ return []
509
+ except Exception as e:
510
+ PrettyOutput.print(f"获取资源列表失败: {str(e)}", OutputType.ERROR)
511
+ return []
512
+
513
+ def get_resource(self, uri: str) -> Dict[str, Any]:
514
+ """获取指定资源的内容
515
+
516
+ 参数:
517
+ uri: str - 资源的URI标识符
518
+
519
+ 返回:
520
+ Dict[str, Any]: 执行结果,包含以下字段:
521
+ - success: bool - 是否执行成功
522
+ - stdout: str - 资源内容(文本或base64编码的二进制内容)
523
+ - stderr: str - 错误信息
524
+ """
525
+ try:
526
+ response = self._send_request('resources/read', {
527
+ 'uri': uri
528
+ })
529
+ if 'result' in response and 'contents' in response['result']:
530
+ contents = response['result']['contents']
531
+ if contents:
532
+ content = contents[0] # 获取第一个资源内容
533
+ # 根据资源类型返回内容
534
+ if 'text' in content:
535
+ return {
536
+ 'success': True,
537
+ 'stdout': content['text'],
538
+ 'stderr': ''
539
+ }
540
+ elif 'blob' in content:
541
+ return {
542
+ 'success': True,
543
+ 'stdout': content['blob'],
544
+ 'stderr': ''
545
+ }
546
+ return {
547
+ 'success': False,
548
+ 'stdout': '',
549
+ 'stderr': '资源内容为空'
550
+ }
551
+ else:
552
+ error_msg = "获取资源内容失败"
553
+ if 'error' in response:
554
+ error_msg += f": {response['error']}"
555
+ else:
556
+ error_msg += ": 未知错误"
557
+ PrettyOutput.print(error_msg, OutputType.ERROR)
558
+ return {
559
+ 'success': False,
560
+ 'stdout': '',
561
+ 'stderr': error_msg
562
+ }
563
+ except Exception as e:
564
+ error_msg = f"获取资源内容失败: {str(e)}"
565
+ PrettyOutput.print(error_msg, OutputType.ERROR)
566
+ return {
567
+ 'success': False,
568
+ 'stdout': '',
569
+ 'stderr': error_msg
570
+ }
571
+
572
+ def __del__(self):
573
+ """清理资源"""
574
+ # 清理请求状态
575
+ with self.event_lock:
576
+ for event in self.pending_requests.values():
577
+ event.set() # 释放所有等待的请求
578
+ self.pending_requests.clear()
579
+ self.request_results.clear()
580
+
581
+ # 关闭SSE响应
582
+ if self.sse_response:
583
+ try:
584
+ self.sse_response.close()
585
+ except:
586
+ pass
587
+
588
+ # 关闭HTTP会话
589
+ if self.session:
590
+ self.session.close()
@@ -7,6 +7,7 @@ import time
7
7
  from jarvis.jarvis_platform.base import BasePlatform
8
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
9
  from jarvis.jarvis_utils.utils import while_success
10
+ from jarvis.jarvis_utils.config import get_data_dir
10
11
 
11
12
  class KimiModel(BasePlatform):
12
13
  """Kimi model implementation"""
@@ -39,8 +40,8 @@ class KimiModel(BasePlatform):
39
40
  " • 在请求中找到 Authorization 头\n"
40
41
  " • 复制 token 值(去掉 'Bearer ' 前缀)\n"
41
42
  "2. 设置环境变量:\n"
42
- " • 方法 1: 创建或编辑 ~/.jarvis/env 文件:\n"
43
- " echo 'KIMI_API_KEY=your_key_here' > ~/.jarvis/env\n"
43
+ " • 方法 1: 创建或编辑配置文件:\n"
44
+ f" echo 'KIMI_API_KEY=your_key_here' > {get_data_dir()}/env\n"
44
45
  " • 方法 2: 直接设置环境变量:\n"
45
46
  " export KIMI_API_KEY=your_key_here\n"
46
47
  "设置后,重新运行 Jarvis。"
@@ -4,7 +4,7 @@ import os
4
4
  import sys
5
5
  from typing import Dict, Type, Optional, List
6
6
  from jarvis.jarvis_platform.base import BasePlatform
7
- from jarvis.jarvis_utils.config import get_normal_model_name, get_normal_platform_name, get_thinking_model_name, get_thinking_platform_name
7
+ from jarvis.jarvis_utils.config import get_normal_model_name, get_normal_platform_name, get_thinking_model_name, get_thinking_platform_name, get_data_dir
8
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
9
 
10
10
  REQUIRED_METHODS = [
@@ -26,15 +26,13 @@ class PlatformRegistry:
26
26
 
27
27
  @staticmethod
28
28
  def get_platform_dir() -> str:
29
- user_platform_dir = os.path.expanduser("~/.jarvis/models")
29
+ user_platform_dir = os.path.join(get_data_dir(), "models")
30
30
  if not os.path.exists(user_platform_dir):
31
31
  try:
32
32
  os.makedirs(user_platform_dir)
33
33
  # 创建 __init__.py 使其成为 Python 包
34
34
  with open(os.path.join(user_platform_dir, "__init__.py"), "w", errors="ignore") as f:
35
35
  pass
36
-
37
- pass
38
36
  except Exception as e:
39
37
  PrettyOutput.print(f"创建平台目录失败: {str(e)}", OutputType.ERROR)
40
38
  return ""
@@ -6,7 +6,6 @@ import hmac
6
6
  import hashlib
7
7
  import time
8
8
  import urllib.parse
9
- from pathlib import Path
10
9
  from PIL import Image
11
10
  from yaspin import yaspin
12
11
  from yaspin.spinners import Spinners
@@ -14,6 +13,7 @@ from yaspin.api import Yaspin
14
13
  from jarvis.jarvis_platform.base import BasePlatform
15
14
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
16
15
  from jarvis.jarvis_utils.utils import while_success
16
+ from jarvis.jarvis_utils.config import get_data_dir
17
17
 
18
18
  class YuanbaoPlatform(BasePlatform):
19
19
  """Hunyuan模型实现"""
@@ -46,9 +46,9 @@ class YuanbaoPlatform(BasePlatform):
46
46
  " • 发送任意消息\n"
47
47
  " • 在请求中找到 X-Uskey 和 T-UserID 头部值\n"
48
48
  "2. 设置环境变量:\n"
49
- " • 方法 1: 创建或编辑 ~/.jarvis/env 文件:\n"
50
- " echo 'YUANBAO_COOKIES=your_cookies_here' >> ~/.jarvis/env\n"
51
- " echo 'YUANBAO_AGENT_ID=your_agent_id_here' >> ~/.jarvis/env\n"
49
+ " • 方法 1: 创建或编辑配置文件:\n"
50
+ f" echo 'YUANBAO_COOKIES=your_cookies_here' >> {get_data_dir()}/env\n"
51
+ f" echo 'YUANBAO_AGENT_ID=your_agent_id_here' >> {get_data_dir()}/env\n"
52
52
  " • 方法 2: 直接设置环境变量:\n"
53
53
  " export YUANBAO_COOKIES=your_cookies_here\n"
54
54
  " export YUANBAO_AGENT_ID=your_agent_id_here\n"