jarvis-ai-assistant 0.1.208__py3-none-any.whl → 0.1.209__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.
@@ -3,7 +3,7 @@ import os
3
3
  from functools import lru_cache
4
4
  from typing import Any, Dict, List
5
5
 
6
- import yaml
6
+ import yaml # type: ignore
7
7
 
8
8
  from jarvis.jarvis_utils.builtin_replace_map import BUILTIN_REPLACE_MAP
9
9
 
@@ -96,16 +96,6 @@ def get_max_input_token_count() -> int:
96
96
  return int(GLOBAL_CONFIG_DATA.get("JARVIS_MAX_INPUT_TOKEN_COUNT", "32000"))
97
97
 
98
98
 
99
- def is_auto_complete() -> bool:
100
- """
101
- 检查是否启用了自动补全功能。
102
-
103
- 返回:
104
- bool: 如果启用了自动补全则返回True,默认为False
105
- """
106
- return GLOBAL_CONFIG_DATA.get("JARVIS_AUTO_COMPLETE", False) == True
107
-
108
-
109
99
  def get_shell_name() -> str:
110
100
  """
111
101
  获取系统shell名称。
@@ -119,10 +109,6 @@ def get_shell_name() -> str:
119
109
  3. 最后从环境变量SHELL获取
120
110
  4. 如果都未配置,则默认返回bash
121
111
  """
122
- shell_name = GLOBAL_CONFIG_DATA.get("JARVIS_SHELL")
123
- if shell_name:
124
- return shell_name.lower()
125
-
126
112
  shell_path = GLOBAL_CONFIG_DATA.get("SHELL", os.getenv("SHELL", "/bin/bash"))
127
113
  return os.path.basename(shell_path).lower()
128
114
 
@@ -191,16 +177,6 @@ def is_confirm_before_apply_patch() -> bool:
191
177
  return GLOBAL_CONFIG_DATA.get("JARVIS_CONFIRM_BEFORE_APPLY_PATCH", False) == True
192
178
 
193
179
 
194
- def get_max_tool_call_count() -> int:
195
- """
196
- 获取最大工具调用次数。
197
-
198
- 返回:
199
- int: 最大连续工具调用次数,默认为20
200
- """
201
- return int(GLOBAL_CONFIG_DATA.get("JARVIS_MAX_TOOL_CALL_COUNT", "20"))
202
-
203
-
204
180
  def get_data_dir() -> str:
205
181
  """
206
182
  获取Jarvis数据存储目录路径。
@@ -209,20 +185,9 @@ def get_data_dir() -> str:
209
185
  str: 数据目录路径,优先从JARVIS_DATA_PATH环境变量获取,
210
186
  如果未设置或为空,则使用~/.jarvis作为默认值
211
187
  """
212
- data_path = GLOBAL_CONFIG_DATA.get("JARVIS_DATA_PATH", "").strip()
213
- if not data_path:
214
- return os.path.expanduser("~/.jarvis")
215
- return data_path
216
-
217
-
218
- def get_auto_update() -> bool:
219
- """
220
- 获取是否自动更新git仓库。
221
-
222
- 返回:
223
- bool: 如果需要自动更新则返回True,默认为True
224
- """
225
- return GLOBAL_CONFIG_DATA.get("JARVIS_AUTO_UPDATE", True) == True
188
+ return os.path.expanduser(
189
+ GLOBAL_CONFIG_DATA.get("JARVIS_DATA_PATH", "~/.jarvis").strip()
190
+ )
226
191
 
227
192
 
228
193
  def get_max_big_content_size() -> int:
@@ -275,16 +240,6 @@ def is_print_prompt() -> bool:
275
240
  return GLOBAL_CONFIG_DATA.get("JARVIS_PRINT_PROMPT", False) == True
276
241
 
277
242
 
278
- def get_history_count() -> int:
279
- """
280
- 获取是否启用历史记录功能。
281
-
282
- 返回:
283
- bool: 如果启用历史记录则返回True,默认为False
284
- """
285
- return GLOBAL_CONFIG_DATA.get("JARVIS_USE_HISTORY_COUNT", 0)
286
-
287
-
288
243
  def get_mcp_config() -> List[Dict[str, Any]]:
289
244
  """
290
245
  获取MCP配置列表。
@@ -1,17 +1,11 @@
1
1
  # -*- coding: utf-8 -*-
2
- import functools
3
- import os
4
- from typing import Any, List
2
+ from typing import List
5
3
 
6
- from jarvis.jarvis_utils.config import get_data_dir
7
4
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
8
5
 
9
- # 全局缓存,避免重复加载模型
10
- _global_tokenizers = {}
11
-
12
6
 
13
7
  def get_context_token_count(text: str) -> int:
14
- """使用分词器获取文本的token数量。
8
+ """使用tiktoken获取文本的token数量。
15
9
 
16
10
  参数:
17
11
  text: 要计算token的输入文本
@@ -20,16 +14,10 @@ def get_context_token_count(text: str) -> int:
20
14
  int: 文本中的token数量
21
15
  """
22
16
  try:
23
- from transformers import AutoTokenizer # type: ignore
24
- tokenizer : AutoTokenizer = load_tokenizer()
25
- # 分批处理长文本,确保不超过模型最大长度
26
- total_tokens = 0
27
- chunk_size = 100 # 每次处理100个字符,避免超过模型最大长度(考虑到中文字符可能被编码成多个token)
28
- for i in range(0, len(text), chunk_size):
29
- chunk = text[i : i + chunk_size]
30
- tokens = tokenizer.encode(chunk) # type: ignore
31
- total_tokens += len(tokens)
32
- return total_tokens
17
+ import tiktoken
18
+
19
+ encoding = tiktoken.get_encoding("cl100k_base")
20
+ return len(encoding.encode(text))
33
21
  except Exception as e:
34
22
  PrettyOutput.print(f"计算token失败: {str(e)}", OutputType.WARNING)
35
23
  return len(text) // 4 # 每个token大约4个字符的粗略估计
@@ -84,36 +72,3 @@ def split_text_into_chunks(
84
72
  PrettyOutput.print(f"文本分割失败: {str(e)}", OutputType.WARNING)
85
73
  # 发生错误时回退到简单的字符分割
86
74
  return [text[i : i + max_length] for i in range(0, len(text), max_length)]
87
-
88
-
89
- @functools.lru_cache(maxsize=1)
90
- def load_tokenizer() -> Any:
91
- """
92
- 加载用于文本处理的分词器,使用缓存避免重复加载。
93
-
94
- 返回:
95
- AutoTokenizer: 加载的分词器
96
- """
97
-
98
- from transformers import AutoTokenizer # type: ignore
99
-
100
- model_name = "gpt2"
101
- cache_dir = os.path.join(get_data_dir(), "huggingface", "hub")
102
-
103
- # 检查全局缓存
104
- if model_name in _global_tokenizers:
105
- return _global_tokenizers[model_name]
106
-
107
- try:
108
- tokenizer = AutoTokenizer.from_pretrained(
109
- model_name, cache_dir=cache_dir, local_files_only=True
110
- )
111
- except Exception:
112
- tokenizer = AutoTokenizer.from_pretrained(
113
- model_name, cache_dir=cache_dir, local_files_only=False
114
- )
115
-
116
- # 保存到全局缓存
117
- _global_tokenizers[model_name] = tokenizer
118
-
119
- return tokenizer # type: ignore
@@ -12,15 +12,15 @@ Git工具模块
12
12
  import os
13
13
  import re
14
14
  import subprocess
15
+ import sys
15
16
  from typing import Any, Dict, List, Set, Tuple
16
17
 
17
- from jarvis.jarvis_utils.config import (get_auto_update,
18
- is_confirm_before_apply_patch)
18
+ from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
19
19
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
20
20
  from jarvis.jarvis_utils.utils import user_confirm
21
21
 
22
22
 
23
- def find_git_root(start_dir: str = ".") -> str:
23
+ def find_git_root_and_cd(start_dir: str = ".") -> str:
24
24
  """
25
25
  切换到给定路径的Git根目录,如果不是Git仓库则初始化。
26
26
 
@@ -213,7 +213,9 @@ def handle_commit_workflow() -> bool:
213
213
  Returns:
214
214
  bool: 提交是否成功
215
215
  """
216
- if is_confirm_before_apply_patch() and not user_confirm("是否要提交代码?", default=True):
216
+ if is_confirm_before_apply_patch() and not user_confirm(
217
+ "是否要提交代码?", default=True
218
+ ):
217
219
  revert_change()
218
220
  return False
219
221
 
@@ -337,13 +339,11 @@ def check_and_update_git_repo(repo_path: str) -> bool:
337
339
  bool: 是否执行了更新
338
340
  """
339
341
  curr_dir = os.path.abspath(os.getcwd())
340
- git_root = find_git_root(repo_path)
342
+ git_root = find_git_root_and_cd(repo_path)
341
343
  if git_root is None:
342
344
  return False
343
345
 
344
346
  try:
345
- if not get_auto_update():
346
- return False
347
347
  # 检查是否有未提交的修改
348
348
  if has_uncommitted_changes():
349
349
  return False
@@ -394,7 +394,49 @@ def check_and_update_git_repo(repo_path: str) -> bool:
394
394
  f"Jarvis已更新到tag {remote_tag_result.stdout.strip()}",
395
395
  OutputType.SUCCESS,
396
396
  )
397
- return True
397
+
398
+ # 执行pip安装更新代码
399
+ try:
400
+ PrettyOutput.print("正在安装更新后的代码...", OutputType.INFO)
401
+
402
+ # 检查是否在虚拟环境中
403
+ in_venv = hasattr(sys, 'real_prefix') or (
404
+ hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix
405
+ )
406
+
407
+ # 尝试普通安装
408
+ install_cmd = [sys.executable, "-m", "pip", "install", "-e", "."]
409
+ result = subprocess.run(
410
+ install_cmd,
411
+ cwd=git_root,
412
+ capture_output=True,
413
+ text=True
414
+ )
415
+
416
+ if result.returncode == 0:
417
+ PrettyOutput.print("代码更新安装成功", OutputType.SUCCESS)
418
+ return True
419
+
420
+ # 处理权限错误
421
+ error_msg = result.stderr.strip()
422
+ if not in_venv and ("Permission denied" in error_msg or "not writeable" in error_msg):
423
+ if user_confirm("检测到权限问题,是否尝试用户级安装(--user)?", True):
424
+ user_result = subprocess.run(
425
+ install_cmd + ["--user"],
426
+ cwd=git_root,
427
+ capture_output=True,
428
+ text=True
429
+ )
430
+ if user_result.returncode == 0:
431
+ PrettyOutput.print("用户级代码安装成功", OutputType.SUCCESS)
432
+ return True
433
+ error_msg = user_result.stderr.strip()
434
+
435
+ PrettyOutput.print(f"代码安装失败: {error_msg}", OutputType.ERROR)
436
+ return False
437
+ except Exception as e:
438
+ PrettyOutput.print(f"安装过程中发生意外错误: {str(e)}", OutputType.ERROR)
439
+ return False
398
440
  return False
399
441
  except Exception as e:
400
442
  PrettyOutput.print(f"Git仓库更新检查失败: {e}", OutputType.WARNING)
@@ -424,7 +466,9 @@ def get_diff_file_list() -> List[str]:
424
466
  subprocess.run(["git", "reset"], check=True)
425
467
 
426
468
  if result.returncode != 0:
427
- PrettyOutput.print(f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR)
469
+ PrettyOutput.print(
470
+ f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR
471
+ )
428
472
  return []
429
473
 
430
474
  return [f for f in result.stdout.splitlines() if f]
@@ -559,7 +603,9 @@ def confirm_add_new_files() -> None:
559
603
  need_confirm = True
560
604
 
561
605
  if binary_files:
562
- output_lines.append(f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)")
606
+ output_lines.append(
607
+ f"检测到{len(binary_files)}个二进制文件(选择N将重新检测)"
608
+ )
563
609
  output_lines.append("二进制文件列表:")
564
610
  output_lines.extend(f" - {file}" for file in binary_files)
565
611
  need_confirm = True
@@ -580,7 +626,10 @@ def confirm_add_new_files() -> None:
580
626
  if not _check_conditions(new_files, added_lines, binary_files):
581
627
  break
582
628
 
583
- if not user_confirm("是否要添加这些变更(如果不需要请修改.gitignore文件以忽略不需要的文件)?", False):
629
+ if not user_confirm(
630
+ "是否要添加这些变更(如果不需要请修改.gitignore文件以忽略不需要的文件)?",
631
+ False,
632
+ ):
584
633
  continue
585
634
 
586
635
  break
@@ -0,0 +1,104 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import requests
4
+ from typing import Any, Dict, Optional, Union
5
+
6
+
7
+ def get_session() -> requests.Session:
8
+ """
9
+ 获取一个永不超时的 requests.Session 对象
10
+
11
+ 返回:
12
+ requests.Session 对象
13
+ """
14
+ session = requests.Session()
15
+
16
+ # 设置默认请求头以优化连接
17
+ session.headers.update(
18
+ {"Connection": "keep-alive"}
19
+ )
20
+
21
+ return session
22
+
23
+
24
+ # 增强版本的 HTTP 请求方法(带重试机制,解决连接中断问题)
25
+ def post(
26
+ url: str,
27
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
28
+ json: Optional[Dict[str, Any]] = None,
29
+ **kwargs,
30
+ ) -> requests.Response:
31
+ """
32
+ 发送增强版永不超时的 POST 请求,包含重试机制
33
+
34
+ 参数:
35
+ url: 请求的 URL
36
+ data: (可选) 请求体数据 (表单数据或原始数据)
37
+ json: (可选) JSON 数据,会自动设置 Content-Type
38
+ **kwargs: 其他传递给 requests.post 的参数
39
+
40
+ 返回:
41
+ requests.Response 对象
42
+
43
+ 注意:
44
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
45
+ """
46
+ session = get_session()
47
+ return session.post(url=url, data=data, json=json, **kwargs)
48
+
49
+
50
+ def get(url: str, **kwargs) -> requests.Response:
51
+ """
52
+ 发送增强版永不超时的 GET 请求,包含重试机制
53
+
54
+ 参数:
55
+ url: 请求的 URL
56
+ **kwargs: 其他传递给 requests.get 的参数
57
+
58
+ 返回:
59
+ requests.Response 对象
60
+
61
+ 注意:
62
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
63
+ """
64
+ session = get_session()
65
+ return session.get(url=url, **kwargs)
66
+
67
+
68
+ def put(
69
+ url: str, data: Optional[Union[Dict[str, Any], str, bytes]] = None, **kwargs
70
+ ) -> requests.Response:
71
+ """
72
+ 发送增强版永不超时的 PUT 请求,包含重试机制
73
+
74
+ 参数:
75
+ url: 请求的 URL
76
+ data: (可选) 请求体数据 (表单数据或原始数据)
77
+ **kwargs: 其他传递给 requests.put 的参数
78
+
79
+ 返回:
80
+ requests.Response 对象
81
+
82
+ 注意:
83
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
84
+ """
85
+ session = get_session()
86
+ return session.put(url=url, data=data, **kwargs)
87
+
88
+
89
+ def delete(url: str, **kwargs) -> requests.Response:
90
+ """
91
+ 发送增强版永不超时的 DELETE 请求,包含重试机制
92
+
93
+ 参数:
94
+ url: 请求的 URL
95
+ **kwargs: 其他传递给 requests.delete 的参数
96
+
97
+ 返回:
98
+ requests.Response 对象
99
+
100
+ 注意:
101
+ 此方法使用增强的永不超时设置,包含自动重试机制,适用于解决"Response ended prematurely"等连接问题
102
+ """
103
+ session = get_session()
104
+ return session.delete(url=url, **kwargs)