jarvis-ai-assistant 0.1.126__py3-none-any.whl → 0.1.129__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +108 -95
- jarvis/jarvis_agent/main.py +77 -0
- jarvis/jarvis_code_agent/builtin_input_handler.py +43 -0
- jarvis/jarvis_code_agent/code_agent.py +17 -81
- jarvis/jarvis_code_agent/file_input_handler.py +88 -0
- jarvis/jarvis_code_agent/patch.py +142 -114
- jarvis/jarvis_code_agent/shell_input_handler.py +8 -2
- jarvis/jarvis_codebase/main.py +240 -213
- jarvis/jarvis_dev/main.py +4 -3
- jarvis/jarvis_multi_agent/__init__.py +51 -40
- jarvis/jarvis_platform/base.py +6 -5
- jarvis/jarvis_platform_manager/main.py +1 -1
- jarvis/jarvis_rag/main.py +250 -186
- jarvis/jarvis_smart_shell/main.py +0 -1
- jarvis/jarvis_tools/ask_codebase.py +4 -3
- jarvis/jarvis_tools/chdir.py +22 -22
- jarvis/jarvis_tools/code_review.py +38 -33
- jarvis/jarvis_tools/execute_shell.py +0 -3
- jarvis/jarvis_tools/file_operation.py +56 -55
- jarvis/jarvis_tools/git_commiter.py +60 -50
- jarvis/jarvis_tools/read_code.py +143 -0
- jarvis/jarvis_tools/read_webpage.py +50 -30
- jarvis/jarvis_tools/registry.py +4 -21
- jarvis/jarvis_tools/search_web.py +61 -36
- jarvis/jarvis_tools/tool_generator.py +78 -36
- jarvis/jarvis_utils/__init__.py +17 -17
- jarvis/jarvis_utils/config.py +87 -51
- jarvis/jarvis_utils/embedding.py +49 -48
- jarvis/jarvis_utils/git_utils.py +34 -34
- jarvis/jarvis_utils/globals.py +26 -26
- jarvis/jarvis_utils/input.py +61 -45
- jarvis/jarvis_utils/methodology.py +94 -76
- jarvis/jarvis_utils/output.py +63 -62
- jarvis/jarvis_utils/utils.py +2 -2
- {jarvis_ai_assistant-0.1.126.dist-info → jarvis_ai_assistant-0.1.129.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.129.dist-info/RECORD +78 -0
- {jarvis_ai_assistant-0.1.126.dist-info → jarvis_ai_assistant-0.1.129.dist-info}/entry_points.txt +2 -0
- jarvis_ai_assistant-0.1.126.dist-info/RECORD +0 -74
- {jarvis_ai_assistant-0.1.126.dist-info → jarvis_ai_assistant-0.1.129.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.126.dist-info → jarvis_ai_assistant-0.1.129.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.126.dist-info → jarvis_ai_assistant-0.1.129.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/input.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
2
|
+
输入处理模块
|
|
3
|
+
该模块提供了处理Jarvis系统中用户输入的实用工具。
|
|
4
|
+
包含:
|
|
5
|
+
- 支持历史记录的单行输入
|
|
6
|
+
- 增强补全功能的多行输入
|
|
7
|
+
- 带有模糊匹配的文件路径补全
|
|
8
|
+
- 用于输入控制的自定义键绑定
|
|
9
9
|
"""
|
|
10
10
|
from prompt_toolkit import PromptSession
|
|
11
11
|
from prompt_toolkit.styles import Style as PromptStyle
|
|
@@ -18,13 +18,13 @@ from colorama import Fore, Style as ColoramaStyle
|
|
|
18
18
|
from ..jarvis_utils.output import PrettyOutput, OutputType
|
|
19
19
|
def get_single_line_input(tip: str) -> str:
|
|
20
20
|
"""
|
|
21
|
-
|
|
21
|
+
获取支持历史记录的单行输入。
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
tip:
|
|
23
|
+
参数:
|
|
24
|
+
tip: 要显示的提示信息
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
str:
|
|
26
|
+
返回:
|
|
27
|
+
str: 用户的输入
|
|
28
28
|
"""
|
|
29
29
|
session = PromptSession(history=None)
|
|
30
30
|
style = PromptStyle.from_dict({
|
|
@@ -33,49 +33,65 @@ def get_single_line_input(tip: str) -> str:
|
|
|
33
33
|
return session.prompt(f"{tip}", style=style)
|
|
34
34
|
class FileCompleter(Completer):
|
|
35
35
|
"""
|
|
36
|
-
|
|
36
|
+
带有模糊匹配的文件路径自定义补全器。
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
path_completer:
|
|
40
|
-
max_suggestions:
|
|
41
|
-
min_score:
|
|
38
|
+
属性:
|
|
39
|
+
path_completer: 基础路径补全器
|
|
40
|
+
max_suggestions: 显示的最大建议数量
|
|
41
|
+
min_score: 建议的最小匹配分数
|
|
42
42
|
"""
|
|
43
43
|
def __init__(self):
|
|
44
|
-
"""
|
|
44
|
+
"""使用默认设置初始化文件补全器。"""
|
|
45
45
|
self.path_completer = PathCompleter()
|
|
46
46
|
self.max_suggestions = 10
|
|
47
47
|
self.min_score = 10
|
|
48
48
|
def get_completions(self, document: Document, complete_event) -> Completion: # type: ignore
|
|
49
49
|
"""
|
|
50
|
-
|
|
50
|
+
生成带有模糊匹配的文件路径补全建议。
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
document:
|
|
54
|
-
complete_event:
|
|
52
|
+
参数:
|
|
53
|
+
document: 当前正在编辑的文档
|
|
54
|
+
complete_event: 补全事件
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
Completion:
|
|
56
|
+
生成:
|
|
57
|
+
Completion: 建议的补全项
|
|
58
58
|
"""
|
|
59
59
|
text = document.text_before_cursor
|
|
60
60
|
cursor_pos = document.cursor_position
|
|
61
|
-
#
|
|
61
|
+
# 查找文本中的所有@位置
|
|
62
62
|
at_positions = [i for i, char in enumerate(text) if char == '@']
|
|
63
63
|
if not at_positions:
|
|
64
64
|
return
|
|
65
|
-
#
|
|
65
|
+
# 获取最后一个@位置
|
|
66
66
|
current_at_pos = at_positions[-1]
|
|
67
|
-
#
|
|
67
|
+
# 如果光标不在最后一个@之后,则不补全
|
|
68
68
|
if cursor_pos <= current_at_pos:
|
|
69
69
|
return
|
|
70
|
-
#
|
|
70
|
+
# 检查@之后是否有空格
|
|
71
71
|
text_after_at = text[current_at_pos + 1:cursor_pos]
|
|
72
72
|
if ' ' in text_after_at:
|
|
73
73
|
return
|
|
74
|
-
#
|
|
74
|
+
# 添加默认建议
|
|
75
|
+
if not text_after_at.strip():
|
|
76
|
+
# 默认建议列表
|
|
77
|
+
default_suggestions = [
|
|
78
|
+
('<CodeBase>', '查询代码库'),
|
|
79
|
+
('<Web>', '网页搜索'),
|
|
80
|
+
('<RAG>', '知识库检索')
|
|
81
|
+
]
|
|
82
|
+
for name, desc in default_suggestions:
|
|
83
|
+
yield Completion(
|
|
84
|
+
text=f"'{name}'",
|
|
85
|
+
start_position=-1,
|
|
86
|
+
display=name,
|
|
87
|
+
display_meta=desc
|
|
88
|
+
) # type: ignore
|
|
89
|
+
return
|
|
90
|
+
# 获取当前@之后的文本
|
|
75
91
|
file_path = text_after_at.strip()
|
|
76
|
-
#
|
|
92
|
+
# 计算替换长度
|
|
77
93
|
replace_length = len(text_after_at) + 1
|
|
78
|
-
#
|
|
94
|
+
# 使用git ls-files获取所有可能的文件
|
|
79
95
|
all_files = []
|
|
80
96
|
try:
|
|
81
97
|
import subprocess
|
|
@@ -87,7 +103,7 @@ class FileCompleter(Completer):
|
|
|
87
103
|
all_files = [line.strip() for line in result.stdout.splitlines() if line.strip()]
|
|
88
104
|
except Exception:
|
|
89
105
|
pass
|
|
90
|
-
#
|
|
106
|
+
# 生成补全建议
|
|
91
107
|
if not file_path:
|
|
92
108
|
scored_files = [(path, 100) for path in all_files[:self.max_suggestions]]
|
|
93
109
|
else:
|
|
@@ -95,7 +111,7 @@ class FileCompleter(Completer):
|
|
|
95
111
|
scored_files = [(m[0], m[1]) for m in scored_files_data]
|
|
96
112
|
scored_files.sort(key=lambda x: x[1], reverse=True)
|
|
97
113
|
scored_files = scored_files[:self.max_suggestions]
|
|
98
|
-
#
|
|
114
|
+
# 生成补全项
|
|
99
115
|
for path, score in scored_files:
|
|
100
116
|
if not file_path or score > self.min_score:
|
|
101
117
|
display_text = path
|
|
@@ -109,31 +125,31 @@ class FileCompleter(Completer):
|
|
|
109
125
|
) # type: ignore
|
|
110
126
|
def get_multiline_input(tip: str) -> str:
|
|
111
127
|
"""
|
|
112
|
-
|
|
128
|
+
获取带有增强补全和确认功能的多行输入。
|
|
113
129
|
|
|
114
|
-
|
|
115
|
-
tip:
|
|
130
|
+
参数:
|
|
131
|
+
tip: 要显示的提示信息
|
|
116
132
|
|
|
117
|
-
|
|
118
|
-
str:
|
|
133
|
+
返回:
|
|
134
|
+
str: 用户的输入,如果取消则返回空字符串
|
|
119
135
|
"""
|
|
120
|
-
#
|
|
136
|
+
# 显示输入说明
|
|
121
137
|
PrettyOutput.section("用户输入 - 使用 @ 触发文件补全,Tab 选择补全项,Ctrl+J 提交,按 Ctrl+C 取消输入", OutputType.USER)
|
|
122
138
|
print(f"{Fore.GREEN}{tip}{ColoramaStyle.RESET_ALL}")
|
|
123
|
-
#
|
|
139
|
+
# 配置键绑定
|
|
124
140
|
bindings = KeyBindings()
|
|
125
141
|
@bindings.add('enter')
|
|
126
142
|
def _(event):
|
|
127
|
-
"""
|
|
143
|
+
"""处理回车键以进行补全或换行。"""
|
|
128
144
|
if event.current_buffer.complete_state:
|
|
129
145
|
event.current_buffer.apply_completion(event.current_buffer.complete_state.current_completion)
|
|
130
146
|
else:
|
|
131
147
|
event.current_buffer.insert_text('\n')
|
|
132
148
|
@bindings.add('c-j')
|
|
133
149
|
def _(event):
|
|
134
|
-
"""
|
|
150
|
+
"""处理Ctrl+J以提交输入。"""
|
|
135
151
|
event.current_buffer.validate_and_handle()
|
|
136
|
-
#
|
|
152
|
+
# 配置提示会话
|
|
137
153
|
style = PromptStyle.from_dict({
|
|
138
154
|
'prompt': 'ansicyan',
|
|
139
155
|
})
|
|
@@ -150,7 +166,7 @@ def get_multiline_input(tip: str) -> str:
|
|
|
150
166
|
prompt = FormattedText([
|
|
151
167
|
('class:prompt', '>>> ')
|
|
152
168
|
])
|
|
153
|
-
#
|
|
169
|
+
# 获取输入
|
|
154
170
|
text = session.prompt(
|
|
155
171
|
prompt,
|
|
156
172
|
style=style,
|
|
@@ -158,4 +174,4 @@ def get_multiline_input(tip: str) -> str:
|
|
|
158
174
|
return text
|
|
159
175
|
except KeyboardInterrupt:
|
|
160
176
|
PrettyOutput.print("输入已取消", OutputType.INFO)
|
|
161
|
-
return ""
|
|
177
|
+
return ""
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
2
|
+
方法论管理模块
|
|
3
|
+
该模块提供了加载和搜索方法论的实用工具。
|
|
4
|
+
包含以下功能:
|
|
5
|
+
- 创建方法论嵌入向量
|
|
6
|
+
- 加载和处理方法论数据
|
|
7
|
+
- 构建和搜索方法论索引
|
|
8
|
+
- 生成方法论提示
|
|
9
9
|
"""
|
|
10
10
|
import os
|
|
11
11
|
import yaml
|
|
@@ -17,17 +17,17 @@ from jarvis.jarvis_utils.embedding import load_embedding_model
|
|
|
17
17
|
from jarvis.jarvis_utils.config import dont_use_local_model
|
|
18
18
|
def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -> np.ndarray:
|
|
19
19
|
"""
|
|
20
|
-
|
|
20
|
+
为方法论文本创建嵌入向量。
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
embedding_model:
|
|
24
|
-
methodology_text:
|
|
22
|
+
参数:
|
|
23
|
+
embedding_model: 使用的嵌入模型
|
|
24
|
+
methodology_text: 要创建嵌入的文本
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
np.ndarray:
|
|
26
|
+
返回:
|
|
27
|
+
np.ndarray: 嵌入向量
|
|
28
28
|
"""
|
|
29
29
|
try:
|
|
30
|
-
#
|
|
30
|
+
# 截断长文本
|
|
31
31
|
max_length = 512
|
|
32
32
|
text = ' '.join(methodology_text.split()[:max_length])
|
|
33
33
|
|
|
@@ -36,7 +36,7 @@ def _create_methodology_embedding(embedding_model: Any, methodology_text: str) -
|
|
|
36
36
|
convert_to_tensor=True,
|
|
37
37
|
normalize_embeddings=True)
|
|
38
38
|
vector = np.array(embedding.cpu().numpy(), dtype=np.float32)
|
|
39
|
-
return vector[0] #
|
|
39
|
+
return vector[0] # 返回第一个向量,因为我们只编码了一个文本
|
|
40
40
|
except Exception as e:
|
|
41
41
|
PrettyOutput.print(f"创建方法论嵌入向量失败: {str(e)}", OutputType.ERROR)
|
|
42
42
|
return np.zeros(1536, dtype=np.float32)
|
|
@@ -44,85 +44,103 @@ def make_methodology_prompt(data: Dict[str, str]) -> str:
|
|
|
44
44
|
"""
|
|
45
45
|
从方法论数据生成格式化提示
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
参数:
|
|
48
48
|
data: 方法论数据字典
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
返回:
|
|
51
51
|
str: 格式化后的提示字符串
|
|
52
52
|
"""
|
|
53
53
|
ret = """这是处理以往问题的标准方法论,如果当前任务类似,可以参考使用,如果不相关,请忽略:\n"""
|
|
54
54
|
for key, value in data.items():
|
|
55
55
|
ret += f"问题: {key}\n方法论: {value}\n"
|
|
56
56
|
return ret
|
|
57
|
+
|
|
57
58
|
def load_methodology(user_input: str) -> str:
|
|
58
59
|
"""
|
|
59
|
-
|
|
60
|
+
加载方法论并构建向量索引以进行相似性搜索。
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
user_input:
|
|
62
|
+
参数:
|
|
63
|
+
user_input: 要搜索方法论的输入文本
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
str:
|
|
65
|
+
返回:
|
|
66
|
+
str: 相关的方法论提示,如果未找到方法论则返回空字符串
|
|
66
67
|
"""
|
|
67
|
-
|
|
68
|
+
from yaspin import yaspin
|
|
68
69
|
user_jarvis_methodology = os.path.expanduser("~/.jarvis/methodology")
|
|
69
70
|
if not os.path.exists(user_jarvis_methodology):
|
|
70
71
|
return ""
|
|
71
72
|
|
|
72
73
|
try:
|
|
73
|
-
with
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ids: List[int] = []
|
|
81
|
-
# Get embedding model
|
|
82
|
-
embedding_model = load_embedding_model()
|
|
74
|
+
with yaspin(text="加载方法论文件...", color="yellow") as spinner:
|
|
75
|
+
with open(user_jarvis_methodology, "r", encoding="utf-8") as f:
|
|
76
|
+
data = yaml.safe_load(f)
|
|
77
|
+
if dont_use_local_model():
|
|
78
|
+
spinner.text = "加载方法论文件完成"
|
|
79
|
+
spinner.ok("✅")
|
|
80
|
+
return make_methodology_prompt(data)
|
|
83
81
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
methodology_index.add_with_ids(vectors_array, np.array(ids)) # type: ignore
|
|
102
|
-
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
103
|
-
k = min(3, len(methodology_data))
|
|
104
|
-
PrettyOutput.print(f"检索方法论...", OutputType.INFO)
|
|
105
|
-
distances, indices = methodology_index.search(
|
|
106
|
-
query_embedding.reshape(1, -1), k
|
|
107
|
-
) # type: ignore
|
|
108
|
-
relevant_methodologies = {}
|
|
109
|
-
output_lines = []
|
|
110
|
-
for dist, idx in zip(distances[0], indices[0]):
|
|
111
|
-
if idx >= 0:
|
|
112
|
-
similarity = 1.0 / (1.0 + float(dist))
|
|
113
|
-
methodology = methodology_data[idx]
|
|
114
|
-
output_lines.append(
|
|
115
|
-
f"Methodology '{methodology['key']}' similarity: {similarity:.3f}"
|
|
116
|
-
)
|
|
117
|
-
if similarity >= 0.5:
|
|
118
|
-
relevant_methodologies[methodology["key"]] = methodology["value"]
|
|
82
|
+
with yaspin(text="初始化数据结构...", color="yellow") as spinner:
|
|
83
|
+
methodology_data: List[Dict[str, str]] = []
|
|
84
|
+
vectors: List[np.ndarray] = []
|
|
85
|
+
ids: List[int] = []
|
|
86
|
+
spinner.text = "初始化数据结构完成"
|
|
87
|
+
spinner.ok("✅")
|
|
88
|
+
|
|
89
|
+
with yaspin(text="加载嵌入模型...", color="yellow") as spinner:
|
|
90
|
+
embedding_model = load_embedding_model()
|
|
91
|
+
spinner.text = "加载嵌入模型完成"
|
|
92
|
+
spinner.ok("✅")
|
|
93
|
+
|
|
94
|
+
with yaspin(text="创建测试嵌入...", color="yellow") as spinner:
|
|
95
|
+
test_embedding = _create_methodology_embedding(embedding_model, "test")
|
|
96
|
+
embedding_dimension = len(test_embedding)
|
|
97
|
+
spinner.text = "创建测试嵌入完成"
|
|
98
|
+
spinner.ok("✅")
|
|
119
99
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
100
|
+
with yaspin(text="处理方法论数据...", color="yellow") as spinner:
|
|
101
|
+
for i, (key, value) in enumerate(data.items()):
|
|
102
|
+
methodology_text = f"{key}\n{value}"
|
|
103
|
+
embedding = _create_methodology_embedding(embedding_model, methodology_text)
|
|
104
|
+
vectors.append(embedding)
|
|
105
|
+
ids.append(i)
|
|
106
|
+
methodology_data.append({"key": key, "value": value})
|
|
107
|
+
spinner.text = "处理方法论数据完成"
|
|
108
|
+
spinner.ok("✅")
|
|
109
|
+
|
|
110
|
+
if vectors:
|
|
111
|
+
with yaspin(text="构建索引...", color="yellow") as spinner:
|
|
112
|
+
vectors_array = np.vstack(vectors)
|
|
113
|
+
hnsw_index = faiss.IndexHNSWFlat(embedding_dimension, 16)
|
|
114
|
+
hnsw_index.hnsw.efConstruction = 40
|
|
115
|
+
hnsw_index.hnsw.efSearch = 16
|
|
116
|
+
methodology_index = faiss.IndexIDMap(hnsw_index)
|
|
117
|
+
methodology_index.add_with_ids(vectors_array, np.array(ids)) # type: ignore
|
|
118
|
+
spinner.text = "构建索引完成"
|
|
119
|
+
spinner.ok("✅")
|
|
120
|
+
|
|
121
|
+
with yaspin(text="执行搜索...", color="yellow") as spinner:
|
|
122
|
+
query_embedding = _create_methodology_embedding(embedding_model, user_input)
|
|
123
|
+
k = min(3, len(methodology_data))
|
|
124
|
+
distances, indices = methodology_index.search(
|
|
125
|
+
query_embedding.reshape(1, -1), k
|
|
126
|
+
) # type: ignore
|
|
127
|
+
spinner.text = "执行搜索完成"
|
|
128
|
+
spinner.ok("✅")
|
|
129
|
+
|
|
130
|
+
with yaspin(text="处理搜索结果...", color="yellow") as spinner:
|
|
131
|
+
relevant_methodologies = {}
|
|
132
|
+
for dist, idx in zip(distances[0], indices[0]):
|
|
133
|
+
if idx >= 0:
|
|
134
|
+
similarity = 1.0 / (1.0 + float(dist))
|
|
135
|
+
methodology = methodology_data[idx]
|
|
136
|
+
if similarity >= 0.5:
|
|
137
|
+
relevant_methodologies[methodology["key"]] = methodology["value"]
|
|
138
|
+
spinner.text = "处理搜索结果完成"
|
|
139
|
+
spinner.ok("✅")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if relevant_methodologies:
|
|
143
|
+
return make_methodology_prompt(relevant_methodologies)
|
|
144
|
+
return make_methodology_prompt(data)
|
|
126
145
|
except Exception as e:
|
|
127
|
-
|
|
128
|
-
return ""
|
|
146
|
+
return ""
|
jarvis/jarvis_utils/output.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- OutputType
|
|
6
|
-
- PrettyOutput
|
|
7
|
-
-
|
|
8
|
-
-
|
|
2
|
+
输出格式化模块
|
|
3
|
+
该模块为Jarvis系统提供了丰富的文本格式化和显示工具。
|
|
4
|
+
包含:
|
|
5
|
+
- 用于分类不同输出类型的OutputType枚举
|
|
6
|
+
- 用于格式化和显示样式化输出的PrettyOutput类
|
|
7
|
+
- 多种编程语言的语法高亮支持
|
|
8
|
+
- 结构化输出的面板显示
|
|
9
9
|
"""
|
|
10
10
|
from enum import Enum
|
|
11
11
|
from datetime import datetime
|
|
@@ -20,21 +20,21 @@ from pygments.util import ClassNotFound
|
|
|
20
20
|
from .globals import console, get_agent_list
|
|
21
21
|
class OutputType(Enum):
|
|
22
22
|
"""
|
|
23
|
-
|
|
23
|
+
输出类型枚举,用于分类和样式化不同类型的消息。
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
SYSTEM: AI
|
|
27
|
-
CODE:
|
|
28
|
-
RESULT:
|
|
29
|
-
ERROR:
|
|
30
|
-
INFO:
|
|
31
|
-
PLANNING:
|
|
32
|
-
PROGRESS:
|
|
33
|
-
SUCCESS:
|
|
34
|
-
WARNING:
|
|
35
|
-
DEBUG:
|
|
36
|
-
USER:
|
|
37
|
-
TOOL:
|
|
25
|
+
属性:
|
|
26
|
+
SYSTEM: AI助手消息
|
|
27
|
+
CODE: 代码相关输出
|
|
28
|
+
RESULT: 工具执行结果
|
|
29
|
+
ERROR: 错误信息
|
|
30
|
+
INFO: 系统提示
|
|
31
|
+
PLANNING: 任务规划
|
|
32
|
+
PROGRESS: 执行进度
|
|
33
|
+
SUCCESS: 成功信息
|
|
34
|
+
WARNING: 警告信息
|
|
35
|
+
DEBUG: 调试信息
|
|
36
|
+
USER: 用户输入
|
|
37
|
+
TOOL: 工具调用
|
|
38
38
|
"""
|
|
39
39
|
SYSTEM = "SYSTEM"
|
|
40
40
|
CODE = "CODE"
|
|
@@ -50,15 +50,15 @@ class OutputType(Enum):
|
|
|
50
50
|
TOOL = "TOOL"
|
|
51
51
|
class PrettyOutput:
|
|
52
52
|
"""
|
|
53
|
-
|
|
53
|
+
使用rich库格式化和显示富文本输出的类。
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
55
|
+
提供以下方法:
|
|
56
|
+
- 使用适当的样式格式化不同类型的输出
|
|
57
|
+
- 代码块的语法高亮
|
|
58
|
+
- 结构化内容的面板显示
|
|
59
|
+
- 渐进显示的流式输出
|
|
60
60
|
"""
|
|
61
|
-
#
|
|
61
|
+
# 不同输出类型的图标
|
|
62
62
|
_ICONS = {
|
|
63
63
|
OutputType.SYSTEM: "🤖",
|
|
64
64
|
OutputType.CODE: "📝",
|
|
@@ -73,7 +73,7 @@ class PrettyOutput:
|
|
|
73
73
|
OutputType.USER: "👤",
|
|
74
74
|
OutputType.TOOL: "🔧",
|
|
75
75
|
}
|
|
76
|
-
#
|
|
76
|
+
# 语法高亮的语言映射
|
|
77
77
|
_lang_map = {
|
|
78
78
|
'Python': 'python',
|
|
79
79
|
'JavaScript': 'javascript',
|
|
@@ -109,14 +109,14 @@ class PrettyOutput:
|
|
|
109
109
|
@staticmethod
|
|
110
110
|
def _detect_language(text: str, default_lang: str = 'markdown') -> str:
|
|
111
111
|
"""
|
|
112
|
-
|
|
112
|
+
检测给定文本的编程语言。
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
text:
|
|
116
|
-
default_lang:
|
|
114
|
+
参数:
|
|
115
|
+
text: 要分析的文本
|
|
116
|
+
default_lang: 如果检测失败,默认返回的语言
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
str:
|
|
118
|
+
返回:
|
|
119
|
+
str: 检测到的语言名称
|
|
120
120
|
"""
|
|
121
121
|
try:
|
|
122
122
|
lexer = guess_lexer(text)
|
|
@@ -127,14 +127,14 @@ class PrettyOutput:
|
|
|
127
127
|
@staticmethod
|
|
128
128
|
def _format(output_type: OutputType, timestamp: bool = True) -> Text:
|
|
129
129
|
"""
|
|
130
|
-
|
|
130
|
+
使用时间戳和图标格式化输出头。
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
output_type:
|
|
134
|
-
timestamp:
|
|
132
|
+
参数:
|
|
133
|
+
output_type: 输出类型
|
|
134
|
+
timestamp: 是否包含时间戳
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
Text:
|
|
136
|
+
返回:
|
|
137
|
+
Text: 格式化后的rich Text对象
|
|
138
138
|
"""
|
|
139
139
|
formatted = Text()
|
|
140
140
|
if timestamp:
|
|
@@ -148,14 +148,14 @@ class PrettyOutput:
|
|
|
148
148
|
@staticmethod
|
|
149
149
|
def print(text: str, output_type: OutputType, timestamp: bool = True, lang: Optional[str] = None, traceback: bool = False):
|
|
150
150
|
"""
|
|
151
|
-
|
|
151
|
+
使用样式和语法高亮打印格式化输出。
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
text:
|
|
155
|
-
output_type:
|
|
156
|
-
timestamp:
|
|
157
|
-
lang:
|
|
158
|
-
traceback:
|
|
153
|
+
参数:
|
|
154
|
+
text: 要打印的文本内容
|
|
155
|
+
output_type: 输出类型(影响样式)
|
|
156
|
+
timestamp: 是否显示时间戳
|
|
157
|
+
lang: 语法高亮的语言
|
|
158
|
+
traceback: 是否显示错误的回溯信息
|
|
159
159
|
"""
|
|
160
160
|
styles = {
|
|
161
161
|
OutputType.SYSTEM: RichStyle(color="bright_cyan", bgcolor="#1a1a1a", frame=True, meta={"icon": "🤖"}),
|
|
@@ -184,17 +184,18 @@ class PrettyOutput:
|
|
|
184
184
|
highlight=True,
|
|
185
185
|
box=HEAVY,
|
|
186
186
|
)
|
|
187
|
+
console.print()
|
|
187
188
|
console.print(panel)
|
|
188
189
|
if traceback or output_type == OutputType.ERROR:
|
|
189
190
|
console.print_exception()
|
|
190
191
|
@staticmethod
|
|
191
192
|
def section(title: str, output_type: OutputType = OutputType.INFO):
|
|
192
193
|
"""
|
|
193
|
-
|
|
194
|
+
在样式化面板中打印章节标题。
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
title:
|
|
197
|
-
output_type:
|
|
196
|
+
参数:
|
|
197
|
+
title: 章节标题文本
|
|
198
|
+
output_type: 输出类型(影响样式)
|
|
198
199
|
"""
|
|
199
200
|
panel = Panel(
|
|
200
201
|
Text(title, style=output_type.value, justify="center"),
|
|
@@ -206,17 +207,17 @@ class PrettyOutput:
|
|
|
206
207
|
@staticmethod
|
|
207
208
|
def print_stream(text: str):
|
|
208
209
|
"""
|
|
209
|
-
|
|
210
|
+
打印流式输出,不带换行符。
|
|
210
211
|
|
|
211
|
-
|
|
212
|
-
text:
|
|
212
|
+
参数:
|
|
213
|
+
text: 要打印的文本
|
|
213
214
|
"""
|
|
214
215
|
style = PrettyOutput._get_style(OutputType.SYSTEM)
|
|
215
216
|
console.print(text, style=style, end="")
|
|
216
217
|
@staticmethod
|
|
217
218
|
def print_stream_end():
|
|
218
219
|
"""
|
|
219
|
-
|
|
220
|
+
结束流式输出,带换行符。
|
|
220
221
|
"""
|
|
221
222
|
end_style = PrettyOutput._get_style(OutputType.SUCCESS)
|
|
222
223
|
console.print("\n", style=end_style)
|
|
@@ -224,12 +225,12 @@ class PrettyOutput:
|
|
|
224
225
|
@staticmethod
|
|
225
226
|
def _get_style(output_type: OutputType) -> RichStyle:
|
|
226
227
|
"""
|
|
227
|
-
|
|
228
|
+
获取预定义的RichStyle用于输出类型。
|
|
228
229
|
|
|
229
|
-
|
|
230
|
-
output_type:
|
|
230
|
+
参数:
|
|
231
|
+
output_type: 要获取样式的输出类型
|
|
231
232
|
|
|
232
|
-
|
|
233
|
-
RichStyle:
|
|
233
|
+
返回:
|
|
234
|
+
RichStyle: 对应的样式
|
|
234
235
|
"""
|
|
235
|
-
return console.get_style(output_type.value)
|
|
236
|
+
return console.get_style(output_type.value)
|
jarvis/jarvis_utils/utils.py
CHANGED
|
@@ -130,7 +130,7 @@ def init_gpu_config() -> Dict:
|
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
def is_long_context(files: list) -> bool:
|
|
133
|
-
"""
|
|
133
|
+
"""检查文件列表是否属于长上下文(总字符数超过最大上下文长度的80%)"""
|
|
134
134
|
max_token_count = get_max_token_count()
|
|
135
135
|
threshold = max_token_count * 0.8
|
|
136
136
|
total_tokens = 0
|
|
@@ -147,4 +147,4 @@ def is_long_context(files: list) -> bool:
|
|
|
147
147
|
PrettyOutput.print(f"读取文件 {file_path} 失败: {e}", OutputType.WARNING)
|
|
148
148
|
continue
|
|
149
149
|
|
|
150
|
-
return total_tokens > threshold
|
|
150
|
+
return total_tokens > threshold
|