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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +9 -59
- jarvis/jarvis_agent/edit_file_handler.py +1 -1
- jarvis/jarvis_code_agent/code_agent.py +53 -8
- jarvis/jarvis_code_agent/lint.py +1 -1
- jarvis/jarvis_data/config_schema.json +0 -25
- jarvis/jarvis_git_utils/git_commiter.py +2 -2
- jarvis/jarvis_platform/kimi.py +20 -11
- jarvis/jarvis_platform/tongyi.py +14 -9
- jarvis/jarvis_platform/yuanbao.py +11 -8
- jarvis/jarvis_tools/ask_user.py +0 -1
- jarvis/jarvis_tools/file_analyzer.py +0 -3
- jarvis/jarvis_utils/config.py +4 -49
- jarvis/jarvis_utils/embedding.py +6 -51
- jarvis/jarvis_utils/git_utils.py +60 -11
- jarvis/jarvis_utils/http.py +104 -0
- jarvis/jarvis_utils/utils.py +186 -63
- {jarvis_ai_assistant-0.1.208.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/METADATA +4 -10
- {jarvis_ai_assistant-0.1.208.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/RECORD +23 -24
- {jarvis_ai_assistant-0.1.208.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/entry_points.txt +1 -0
- jarvis/jarvis_data/huggingface.tar.gz +0 -0
- jarvis/jarvis_utils/jarvis_history.py +0 -98
- {jarvis_ai_assistant-0.1.208.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.208.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.208.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/config.py
CHANGED
@@ -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
|
-
|
213
|
-
|
214
|
-
|
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配置列表。
|
jarvis/jarvis_utils/embedding.py
CHANGED
@@ -1,17 +1,11 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
import
|
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
|
-
"""
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
jarvis/jarvis_utils/git_utils.py
CHANGED
@@ -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
|
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
|
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(
|
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 =
|
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
|
-
|
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(
|
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(
|
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(
|
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)
|