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

Files changed (41) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/jarvis.py +7 -5
  3. jarvis/jarvis_agent/main.py +0 -1
  4. jarvis/jarvis_agent/patch.py +42 -7
  5. jarvis/jarvis_code_analysis/code_review.py +0 -1
  6. jarvis/jarvis_git_utils/git_commiter.py +0 -1
  7. jarvis/jarvis_lsp/base.py +1 -1
  8. jarvis/jarvis_lsp/cpp.py +1 -1
  9. jarvis/jarvis_lsp/go.py +1 -1
  10. jarvis/jarvis_lsp/python.py +1 -1
  11. jarvis/jarvis_lsp/registry.py +2 -1
  12. jarvis/jarvis_lsp/rust.py +1 -1
  13. jarvis/jarvis_mcp/__init__.py +36 -0
  14. jarvis/jarvis_mcp/local_mcp_client.py +241 -0
  15. jarvis/jarvis_mcp/remote_mcp_client.py +230 -0
  16. jarvis/jarvis_platform/base.py +1 -1
  17. jarvis/jarvis_platform/kimi.py +3 -3
  18. jarvis/jarvis_platform/registry.py +2 -4
  19. jarvis/jarvis_platform/yuanbao.py +4 -4
  20. jarvis/jarvis_tools/code_plan.py +0 -1
  21. jarvis/jarvis_tools/file_analyzer.py +1 -1
  22. jarvis/jarvis_tools/file_operation.py +7 -26
  23. jarvis/jarvis_tools/methodology.py +2 -1
  24. jarvis/jarvis_tools/read_code.py +0 -1
  25. jarvis/jarvis_tools/registry.py +108 -3
  26. jarvis/jarvis_tools/search_web.py +0 -1
  27. jarvis/jarvis_utils/config.py +14 -8
  28. jarvis/jarvis_utils/embedding.py +4 -8
  29. jarvis/jarvis_utils/file_processors.py +0 -262
  30. jarvis/jarvis_utils/git_utils.py +19 -8
  31. jarvis/jarvis_utils/input.py +7 -1
  32. jarvis/jarvis_utils/methodology.py +2 -2
  33. jarvis/jarvis_utils/output.py +0 -1
  34. jarvis/jarvis_utils/utils.py +23 -221
  35. {jarvis_ai_assistant-0.1.148.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/METADATA +31 -38
  36. {jarvis_ai_assistant-0.1.148.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/RECORD +40 -38
  37. jarvis/jarvis_platform_manager/openai_test.py +0 -138
  38. {jarvis_ai_assistant-0.1.148.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/LICENSE +0 -0
  39. {jarvis_ai_assistant-0.1.148.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/WHEEL +0 -0
  40. {jarvis_ai_assistant-0.1.148.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/entry_points.txt +0 -0
  41. {jarvis_ai_assistant-0.1.148.dist-info → jarvis_ai_assistant-0.1.150.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,230 @@
1
+ from typing import Any, Dict, List
2
+ import requests
3
+ import sseclient
4
+ from urllib.parse import urljoin
5
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
6
+ from . import McpClient
7
+
8
+
9
+ class RemoteMcpClient(McpClient):
10
+ """远程MCP客户端实现
11
+
12
+ 参数:
13
+ config: 配置字典,包含以下字段:
14
+ - base_url: str - MCP服务器的基础URL
15
+ - auth_token: str - 认证令牌(可选)
16
+ - headers: Dict[str, str] - 额外的HTTP头(可选)
17
+ """
18
+ def __init__(self, config: Dict[str, Any]):
19
+ self.config = config
20
+ self.base_url = config.get('base_url', '')
21
+ if not self.base_url:
22
+ raise ValueError('No base_url specified in config')
23
+
24
+ # 设置HTTP客户端
25
+ self.session = requests.Session()
26
+ self.session.headers.update({
27
+ 'Content-Type': 'application/json',
28
+ 'Accept': 'application/json',
29
+ })
30
+
31
+ # 添加认证令牌(如果提供)
32
+ auth_token = config.get('auth_token')
33
+ if auth_token:
34
+ self.session.headers['Authorization'] = f'Bearer {auth_token}'
35
+
36
+ # 添加额外的HTTP头
37
+ extra_headers = config.get('headers', {})
38
+ self.session.headers.update(extra_headers)
39
+
40
+ # 初始化SSE连接
41
+ self.sse_client = None
42
+ self._initialize()
43
+
44
+ def _initialize(self) -> None:
45
+ """初始化MCP连接"""
46
+ try:
47
+ # 发送初始化请求
48
+ response = self._send_request('initialize', {
49
+ 'processId': None, # 远程客户端不需要进程ID
50
+ 'clientInfo': {
51
+ 'name': 'jarvis',
52
+ 'version': '1.0.0'
53
+ },
54
+ 'capabilities': {},
55
+ 'protocolVersion': "2025-03-26"
56
+ })
57
+
58
+ # 验证服务器响应
59
+ if 'result' not in response:
60
+ raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
61
+
62
+ result = response['result']
63
+
64
+ # 发送initialized通知 - 使用正确的方法名格式
65
+ self._send_notification('notifications/initialized', {})
66
+
67
+ # 建立SSE连接
68
+ sse_url = urljoin(self.base_url, 'events')
69
+ response = self.session.get(sse_url, stream=True)
70
+ self.sse_client = sseclient.SSEClient(response)
71
+
72
+ except Exception as e:
73
+ PrettyOutput.print(f"MCP初始化失败: {str(e)}", OutputType.ERROR)
74
+ raise
75
+
76
+ def _send_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
77
+ """发送请求到MCP服务器
78
+
79
+ 参数:
80
+ method: 请求方法
81
+ params: 请求参数
82
+
83
+ 返回:
84
+ Dict[str, Any]: 响应结果
85
+ """
86
+ try:
87
+ # 构建请求
88
+ request = {
89
+ 'jsonrpc': '2.0',
90
+ 'method': method,
91
+ 'params': params,
92
+ 'id': 1
93
+ }
94
+
95
+ # 发送请求
96
+ response = self.session.post(
97
+ urljoin(self.base_url, 'rpc'),
98
+ json=request
99
+ )
100
+ response.raise_for_status()
101
+ return response.json()
102
+
103
+ except Exception as e:
104
+ PrettyOutput.print(f"发送请求失败: {str(e)}", OutputType.ERROR)
105
+ raise
106
+
107
+ def _send_notification(self, method: str, params: Dict[str, Any]) -> None:
108
+ """发送通知到MCP服务器(不需要响应)
109
+
110
+ 参数:
111
+ method: 通知方法
112
+ params: 通知参数
113
+ """
114
+ try:
115
+ # 构建通知
116
+ notification = {
117
+ 'jsonrpc': '2.0',
118
+ 'method': method,
119
+ 'params': params
120
+ }
121
+
122
+ # 发送通知
123
+ response = self.session.post(
124
+ urljoin(self.base_url, 'rpc'),
125
+ json=notification
126
+ )
127
+ response.raise_for_status()
128
+
129
+ except Exception as e:
130
+ PrettyOutput.print(f"发送通知失败: {str(e)}", OutputType.ERROR)
131
+ raise
132
+
133
+ def get_tool_list(self) -> List[Dict[str, Any]]:
134
+ """获取工具列表
135
+
136
+ 返回:
137
+ List[Dict[str, Any]]: 工具列表,每个工具包含以下字段:
138
+ - name: str - 工具名称
139
+ - description: str - 工具描述
140
+ - parameters: Dict - 工具参数
141
+ """
142
+ try:
143
+ response = self._send_request('tools/list', {})
144
+ if 'result' in response and 'tools' in response['result']:
145
+ # 注意这里: 响应结构是 response['result']['tools']
146
+ tools = response['result']['tools']
147
+ # 将MCP协议字段转换为内部格式
148
+ formatted_tools = []
149
+ for tool in tools:
150
+ # 从inputSchema中提取参数定义
151
+ input_schema = tool.get('inputSchema', {})
152
+ parameters = {}
153
+ if 'properties' in input_schema:
154
+ parameters = input_schema['properties']
155
+
156
+ formatted_tools.append({
157
+ 'name': tool.get('name', ''),
158
+ 'description': tool.get('description', ''),
159
+ 'parameters': parameters
160
+ })
161
+ return formatted_tools
162
+ else:
163
+ error_msg = "获取工具列表失败"
164
+ if 'error' in response:
165
+ error_msg += f": {response['error']}"
166
+ elif 'result' in response:
167
+ error_msg += f": 响应格式不正确 - {response['result']}"
168
+ else:
169
+ error_msg += ": 未知错误"
170
+
171
+ PrettyOutput.print(error_msg, OutputType.ERROR)
172
+ return []
173
+ except Exception as e:
174
+ PrettyOutput.print(f"获取工具列表失败: {str(e)}", OutputType.ERROR)
175
+ return []
176
+
177
+ def execute(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
178
+ """执行工具
179
+
180
+ 参数:
181
+ tool_name: 工具名称
182
+ arguments: 参数字典,包含工具执行所需的参数
183
+
184
+ 返回:
185
+ Dict[str, Any]: 执行结果,包含以下字段:
186
+ - success: bool - 是否执行成功
187
+ - stdout: str - 标准输出
188
+ - stderr: str - 标准错误
189
+ """
190
+ try:
191
+ response = self._send_request('tools/call', {
192
+ 'name': tool_name,
193
+ 'arguments': arguments
194
+ })
195
+ if 'result' in response:
196
+ result = response['result']
197
+ # 从content中提取输出信息
198
+ stdout = ''
199
+ stderr = ''
200
+ for content in result.get('content', []):
201
+ if content.get('type') == 'text':
202
+ stdout += content.get('text', '')
203
+ elif content.get('type') == 'error':
204
+ stderr += content.get('text', '')
205
+
206
+ return {
207
+ 'success': True,
208
+ 'stdout': stdout,
209
+ 'stderr': stderr
210
+ }
211
+ else:
212
+ return {
213
+ 'success': False,
214
+ 'stdout': '',
215
+ 'stderr': response.get('error', 'Unknown error')
216
+ }
217
+ except Exception as e:
218
+ PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
219
+ return {
220
+ 'success': False,
221
+ 'stdout': '',
222
+ 'stderr': str(e)
223
+ }
224
+
225
+ def __del__(self):
226
+ """清理资源"""
227
+ if self.sse_client and hasattr(self.sse_client, 'resp'):
228
+ self.sse_client.resp.close()
229
+ if self.session:
230
+ self.session.close()
@@ -1,6 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
  import re
3
- from typing import Dict, List, Tuple
3
+ from typing import List, Tuple
4
4
  from jarvis.jarvis_utils.globals import clear_read_file_record
5
5
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
6
6
  from jarvis.jarvis_utils.utils import ct, ot, get_context_token_count, while_success, while_true
@@ -7,7 +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 yaspin import yaspin
10
+ from jarvis.jarvis_utils.config import get_data_dir
11
11
 
12
12
  class KimiModel(BasePlatform):
13
13
  """Kimi model implementation"""
@@ -40,8 +40,8 @@ class KimiModel(BasePlatform):
40
40
  " • 在请求中找到 Authorization 头\n"
41
41
  " • 复制 token 值(去掉 'Bearer ' 前缀)\n"
42
42
  "2. 设置环境变量:\n"
43
- " • 方法 1: 创建或编辑 ~/.jarvis/env 文件:\n"
44
- " 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"
45
45
  " • 方法 2: 直接设置环境变量:\n"
46
46
  " export KIMI_API_KEY=your_key_here\n"
47
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"
@@ -15,7 +15,6 @@ from jarvis.jarvis_agent import Agent
15
15
  from jarvis.jarvis_platform.registry import PlatformRegistry
16
16
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
17
17
  from jarvis.jarvis_utils.git_utils import find_git_root
18
- from jarvis.jarvis_utils.utils import init_env
19
18
 
20
19
  class CodePlanTool:
21
20
  """用于代码修改规划和需求分析的工具
@@ -1,4 +1,4 @@
1
- from typing import Dict, Any, List
1
+ from typing import Dict, Any
2
2
  import os
3
3
 
4
4
  from jarvis.jarvis_platform.registry import PlatformRegistry
@@ -8,15 +8,14 @@ from jarvis.jarvis_utils.globals import add_read_file_record
8
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
9
  # 导入文件处理器
10
10
  from jarvis.jarvis_utils.file_processors import (
11
- TextFileProcessor, PDFProcessor, DocxProcessor,
12
- PPTProcessor, ExcelProcessor
11
+ TextFileProcessor
13
12
  )
14
13
 
15
14
 
16
15
 
17
16
  class FileOperationTool:
18
17
  name = "file_operation"
19
- description = "文件批量操作工具,可批量读写多个文件,支持文本、PDF、Word、Excel、PPT等格式,适用于需要同时处理多个文件的场景(读取配置文件、保存生成内容等)"
18
+ description = "文件批量操作工具,可批量读写多个文件,仅支持文本文件,适用于需要同时处理多个文件的场景(读取配置文件、保存生成内容等)"
20
19
  parameters = {
21
20
  "type": "object",
22
21
  "properties": {
@@ -44,10 +43,6 @@ class FileOperationTool:
44
43
  def _get_file_processor(self, file_path: str):
45
44
  """获取适合处理指定文件的处理器"""
46
45
  processors = [
47
- PDFProcessor, # PDF文件处理器
48
- DocxProcessor, # Word文档处理器
49
- PPTProcessor, # PowerPoint演示文稿处理器
50
- ExcelProcessor, # Excel表格处理器
51
46
  TextFileProcessor # 文本文件处理器(放在最后作为兜底)
52
47
  ]
53
48
 
@@ -126,25 +121,11 @@ class FileOperationTool:
126
121
  "stderr": f"读取文本文件失败: {str(e)}"
127
122
  }
128
123
  else:
129
- # 使用专用处理器来提取非文本文件的内容
130
- try:
131
- spinner.text = f"使用 {processor.__name__} 提取 {abs_path} 的内容..."
132
- content = processor.extract_text(abs_path)
133
- # 获取文件类型友好名称
134
- file_type_names = {
135
- PDFProcessor: "PDF文档",
136
- DocxProcessor: "Word文档",
137
- PPTProcessor: "PowerPoint演示文稿",
138
- ExcelProcessor: "Excel表格"
139
- }
140
- file_type = file_type_names.get(processor, file_extension)
141
- file_info = f"\n文件: {abs_path} ({file_type})"
142
- except Exception as e:
143
- return {
144
- "success": False,
145
- "stdout": "",
146
- "stderr": f"提取 {file_extension} 文件内容失败: {str(e)}"
147
- }
124
+ return {
125
+ "success": False,
126
+ "stdout": "",
127
+ "stderr": f"不支持的文件类型: {file_extension}"
128
+ }
148
129
 
149
130
  # 构建输出信息
150
131
  output = f"{file_info}\n{content}" + "\n\n"
@@ -3,6 +3,7 @@ import json
3
3
  import hashlib
4
4
  from typing import Dict, Any
5
5
 
6
+ from jarvis.jarvis_utils.config import get_data_dir
6
7
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
7
8
 
8
9
 
@@ -35,7 +36,7 @@ class MethodologyTool:
35
36
 
36
37
  def __init__(self):
37
38
  """初始化经验管理工具"""
38
- self.methodology_dir = os.path.expanduser("~/.jarvis/methodologies")
39
+ self.methodology_dir = os.path.join(get_data_dir(), "methodologies")
39
40
  self._ensure_dir_exists()
40
41
 
41
42
  def _ensure_dir_exists(self):
@@ -1,7 +1,6 @@
1
1
  from typing import Dict, Any
2
2
  import os
3
3
 
4
- from pkg_resources import add_activation_listener
5
4
  from yaspin import yaspin
6
5
 
7
6
  from jarvis.jarvis_utils.globals import add_read_file_record
@@ -10,10 +10,12 @@ import yaml
10
10
  from jarvis.jarvis_agent.output_handler import OutputHandler
11
11
  from jarvis.jarvis_platform.registry import PlatformRegistry
12
12
  from jarvis.jarvis_tools.base import Tool
13
- from jarvis.jarvis_utils.config import INPUT_WINDOW_REVERSE_SIZE, get_max_input_token_count
13
+ from jarvis.jarvis_utils.config import INPUT_WINDOW_REVERSE_SIZE, get_max_input_token_count, get_data_dir
14
14
  from jarvis.jarvis_utils.embedding import get_context_token_count
15
15
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
16
16
  from jarvis.jarvis_utils.utils import ct, ot, init_env
17
+ from jarvis.jarvis_mcp.local_mcp_client import LocalMcpClient
18
+ from jarvis.jarvis_mcp.remote_mcp_client import RemoteMcpClient
17
19
 
18
20
 
19
21
 
@@ -131,6 +133,7 @@ class ToolRegistry(OutputHandler):
131
133
  # 加载内置工具和外部工具
132
134
  self._load_builtin_tools()
133
135
  self._load_external_tools()
136
+ self._load_mcp_tools()
134
137
  self.max_input_token_count = get_max_input_token_count() - INPUT_WINDOW_REVERSE_SIZE
135
138
 
136
139
  def use_tools(self, name: List[str]) -> None:
@@ -152,6 +155,16 @@ class ToolRegistry(OutputHandler):
152
155
  """
153
156
  self.tools = {name: tool for name, tool in self.tools.items() if name not in names}
154
157
 
158
+ def _load_mcp_tools(self) -> None:
159
+ """从jarvis_data/tools/mcp加载工具"""
160
+ mcp_tools_dir = Path(get_data_dir()) / 'mcp'
161
+ if not mcp_tools_dir.exists():
162
+ return
163
+
164
+ # 遍历目录中的所有.yaml文件
165
+ for file_path in mcp_tools_dir.glob("*.yaml"):
166
+ self.register_mcp_tool_by_file(str(file_path))
167
+
155
168
  def _load_builtin_tools(self) -> None:
156
169
  """从内置工具目录加载工具"""
157
170
  tools_dir = Path(__file__).parent
@@ -165,8 +178,8 @@ class ToolRegistry(OutputHandler):
165
178
  self.register_tool_by_file(str(file_path))
166
179
 
167
180
  def _load_external_tools(self) -> None:
168
- """从~/.jarvis/tools加载外部工具"""
169
- external_tools_dir = Path.home() / '.jarvis/tools'
181
+ """从jarvis_data/tools加载外部工具"""
182
+ external_tools_dir = Path(get_data_dir()) / 'tools'
170
183
  if not external_tools_dir.exists():
171
184
  return
172
185
 
@@ -178,6 +191,98 @@ class ToolRegistry(OutputHandler):
178
191
 
179
192
  self.register_tool_by_file(str(file_path))
180
193
 
194
+ def register_mcp_tool_by_file(self, file_path: str) -> bool:
195
+ """从指定文件加载并注册工具
196
+
197
+ 参数:
198
+ file_path: 工具文件的路径
199
+
200
+ 返回:
201
+ bool: 工具是否加载成功
202
+ """
203
+ try:
204
+ config = yaml.safe_load(open(file_path, 'r', encoding='utf-8'))
205
+ if 'type' not in config:
206
+ PrettyOutput.print(f"文件 {file_path} 缺少type字段", OutputType.WARNING)
207
+ return False
208
+ if config['type'] == 'local':
209
+ if 'command' not in config:
210
+ PrettyOutput.print(f"文件 {file_path} 缺少command字段", OutputType.WARNING)
211
+ return False
212
+
213
+ # 创建本地MCP客户端
214
+ mcp_client = LocalMcpClient(config)
215
+
216
+ # 获取工具信息
217
+ tools = mcp_client.get_tool_list()
218
+ if not tools:
219
+ PrettyOutput.print(f"从 {file_path} 获取工具列表失败", OutputType.WARNING)
220
+ return False
221
+
222
+ # 注册每个工具
223
+ for tool in tools:
224
+ def create_local_execute_func(tool_name: str, client: LocalMcpClient):
225
+ def execute(arguments: Dict[str, Any]) -> Dict[str, Any]:
226
+ args = arguments.copy()
227
+ args.pop('agent', None)
228
+ args.pop('want', None)
229
+ ret = client.execute(tool_name, args)
230
+ PrettyOutput.print(f"MCP {tool_name} 执行结果:\n{yaml.safe_dump(ret)}", OutputType.TOOL)
231
+ return ret
232
+ return execute
233
+
234
+ # 注册工具
235
+ self.register_tool(
236
+ name=tool['name'],
237
+ description=tool['description'],
238
+ parameters=tool['parameters'],
239
+ func=create_local_execute_func(tool['name'], mcp_client)
240
+ )
241
+
242
+ return True
243
+
244
+ elif config['type'] == 'remote':
245
+ if 'base_url' not in config:
246
+ PrettyOutput.print(f"文件 {file_path} 缺少base_url字段", OutputType.WARNING)
247
+ return False
248
+
249
+ # 创建远程MCP客户端
250
+ mcp_client = RemoteMcpClient(config)
251
+
252
+ # 获取工具信息
253
+ tools = mcp_client.get_tool_list()
254
+ if not tools:
255
+ PrettyOutput.print(f"从 {file_path} 获取工具列表失败", OutputType.WARNING)
256
+ return False
257
+
258
+ # 注册每个工具
259
+ for tool in tools:
260
+ def create_remote_execute_func(tool_name: str, client: RemoteMcpClient):
261
+ def execute(arguments: Dict[str, Any]) -> Dict[str, Any]:
262
+ args = arguments.copy()
263
+ args.pop('agent', None)
264
+ args.pop('want', None)
265
+ ret = client.execute(tool_name, args)
266
+ PrettyOutput.print(f"MCP {tool_name} 执行结果:\n{yaml.safe_dump(ret)}", OutputType.TOOL)
267
+ return ret
268
+ return execute
269
+
270
+ # 注册工具
271
+ self.register_tool(
272
+ name=tool['name'],
273
+ description=tool['description'],
274
+ parameters=tool['parameters'],
275
+ func=create_remote_execute_func(tool['name'], mcp_client)
276
+ )
277
+
278
+ return True
279
+ else:
280
+ PrettyOutput.print(f"文件 {file_path} 类型错误: {config['type']}", OutputType.WARNING)
281
+ return False
282
+ except Exception as e:
283
+ PrettyOutput.print(f"文件 {file_path} 加载失败: {str(e)}", OutputType.WARNING)
284
+ return False
285
+
181
286
  def register_tool_by_file(self, file_path: str) -> bool:
182
287
  """从指定文件加载并注册工具
183
288
 
@@ -1,5 +1,4 @@
1
1
  import os
2
- import statistics
3
2
  from typing import Any, Dict
4
3
  from jarvis.jarvis_platform.registry import PlatformRegistry
5
4
 
@@ -31,14 +31,6 @@ def get_max_input_token_count() -> int:
31
31
  """
32
32
  return int(os.getenv('JARVIS_MAX_INPUT_TOKEN_COUNT', '32000'))
33
33
 
34
- def get_thread_count() -> int:
35
- """
36
- 获取用于并行处理的线程数。
37
-
38
- 返回:
39
- int: 线程数,默认为1
40
- """
41
- return int(os.getenv('JARVIS_THREAD_COUNT', '1'))
42
34
 
43
35
  def is_auto_complete() -> bool:
44
36
  """
@@ -118,3 +110,17 @@ def get_max_tool_call_count() -> int:
118
110
  int: 最大连续工具调用次数,默认为20
119
111
  """
120
112
  return int(os.getenv('JARVIS_MAX_TOOL_CALL_COUNT', '20'))
113
+
114
+
115
+ def get_data_dir() -> str:
116
+ """
117
+ 获取Jarvis数据存储目录路径。
118
+
119
+ 返回:
120
+ str: 数据目录路径,优先从JARVIS_DATA_PATH环境变量获取,
121
+ 如果未设置或为空,则使用~/.jarvis作为默认值
122
+ """
123
+ data_path = os.getenv('JARVIS_DATA_PATH', '').strip()
124
+ if not data_path:
125
+ return os.path.expanduser('~/.jarvis')
126
+ return data_path
@@ -1,16 +1,12 @@
1
1
  import os
2
- import numpy as np
3
- import torch
4
- from sentence_transformers import SentenceTransformer
5
- from transformers import AutoTokenizer, AutoModelForSequenceClassification
6
- from typing import List, Any, Optional, Tuple
2
+ from transformers import AutoTokenizer
3
+ from typing import List
7
4
  import functools
8
5
 
9
- from yaspin.api import Yaspin
10
6
  from jarvis.jarvis_utils.output import PrettyOutput, OutputType
7
+ from jarvis.jarvis_utils.config import get_data_dir
11
8
 
12
9
  # 全局缓存,避免重复加载模型
13
- _global_models = {}
14
10
  _global_tokenizers = {}
15
11
 
16
12
  def get_context_token_count(text: str) -> int:
@@ -159,7 +155,7 @@ def load_tokenizer() -> AutoTokenizer:
159
155
  AutoTokenizer: 加载的分词器
160
156
  """
161
157
  model_name = "gpt2"
162
- cache_dir = os.path.expanduser("~/.cache/huggingface/hub")
158
+ cache_dir = os.path.join(get_data_dir(), "huggingface", "hub")
163
159
 
164
160
  # 检查全局缓存
165
161
  if model_name in _global_tokenizers: