jarvis-ai-assistant 0.1.193__py3-none-any.whl → 0.1.195__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 +45 -41
- jarvis/jarvis_agent/builtin_input_handler.py +26 -4
- jarvis/jarvis_agent/jarvis.py +30 -19
- jarvis/jarvis_agent/main.py +20 -12
- jarvis/jarvis_agent/output_handler.py +7 -7
- jarvis/jarvis_agent/shell_input_handler.py +14 -11
- jarvis/jarvis_code_agent/code_agent.py +81 -79
- jarvis/jarvis_code_agent/lint.py +92 -105
- jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
- jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
- jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
- jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
- jarvis/jarvis_code_analysis/checklists/go.py +1 -1
- jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
- jarvis/jarvis_code_analysis/checklists/java.py +1 -1
- jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
- jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
- jarvis/jarvis_code_analysis/checklists/loader.py +31 -29
- jarvis/jarvis_code_analysis/checklists/php.py +1 -1
- jarvis/jarvis_code_analysis/checklists/python.py +1 -1
- jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
- jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
- jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
- jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
- jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
- jarvis/jarvis_code_analysis/checklists/web.py +1 -1
- jarvis/jarvis_code_analysis/code_review.py +292 -190
- jarvis/jarvis_dev/main.py +73 -56
- jarvis/jarvis_git_details/main.py +29 -33
- jarvis/jarvis_git_squash/main.py +13 -11
- jarvis/jarvis_git_utils/git_commiter.py +15 -5
- jarvis/jarvis_mcp/__init__.py +8 -10
- jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
- jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
- jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
- jarvis/jarvis_methodology/main.py +71 -39
- jarvis/jarvis_multi_agent/__init__.py +24 -16
- jarvis/jarvis_multi_agent/main.py +10 -4
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/base.py +44 -18
- jarvis/jarvis_platform/human.py +15 -3
- jarvis/jarvis_platform/kimi.py +117 -81
- jarvis/jarvis_platform/openai.py +23 -28
- jarvis/jarvis_platform/registry.py +43 -29
- jarvis/jarvis_platform/tongyi.py +16 -10
- jarvis/jarvis_platform/yuanbao.py +197 -144
- jarvis/jarvis_platform_manager/main.py +4 -2
- jarvis/jarvis_smart_shell/main.py +35 -30
- jarvis/jarvis_tools/ask_user.py +8 -16
- jarvis/jarvis_tools/base.py +3 -2
- jarvis/jarvis_tools/chdir.py +7 -19
- jarvis/jarvis_tools/cli/main.py +14 -10
- jarvis/jarvis_tools/code_plan.py +10 -31
- jarvis/jarvis_tools/create_code_agent.py +6 -11
- jarvis/jarvis_tools/create_sub_agent.py +10 -22
- jarvis/jarvis_tools/edit_file.py +98 -76
- jarvis/jarvis_tools/execute_script.py +46 -46
- jarvis/jarvis_tools/file_analyzer.py +22 -34
- jarvis/jarvis_tools/file_operation.py +69 -62
- jarvis/jarvis_tools/generate_new_tool.py +0 -2
- jarvis/jarvis_tools/methodology.py +19 -23
- jarvis/jarvis_tools/read_code.py +35 -35
- jarvis/jarvis_tools/read_webpage.py +7 -16
- jarvis/jarvis_tools/registry.py +63 -30
- jarvis/jarvis_tools/rewrite_file.py +26 -29
- jarvis/jarvis_tools/search_web.py +5 -8
- jarvis/jarvis_tools/virtual_tty.py +133 -122
- jarvis/jarvis_utils/__init__.py +0 -1
- jarvis/jarvis_utils/builtin_replace_map.py +9 -9
- jarvis/jarvis_utils/config.py +60 -37
- jarvis/jarvis_utils/embedding.py +24 -19
- jarvis/jarvis_utils/file_processors.py +16 -9
- jarvis/jarvis_utils/git_utils.py +157 -107
- jarvis/jarvis_utils/globals.py +1 -1
- jarvis/jarvis_utils/input.py +85 -52
- jarvis/jarvis_utils/jarvis_history.py +43 -0
- jarvis/jarvis_utils/methodology.py +31 -24
- jarvis/jarvis_utils/output.py +164 -80
- jarvis/jarvis_utils/tag.py +2 -1
- jarvis/jarvis_utils/utils.py +84 -52
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/METADATA +362 -230
- jarvis_ai_assistant-0.1.195.dist-info/RECORD +98 -0
- jarvis/jarvis_agent/file_input_handler.py +0 -112
- jarvis/jarvis_event/__init__.py +0 -0
- jarvis_ai_assistant-0.1.193.dist-info/RECORD +0 -99
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/top_level.txt +0 -0
jarvis/jarvis_utils/output.py
CHANGED
@@ -42,6 +42,7 @@ class OutputType(Enum):
|
|
42
42
|
USER: 用户输入
|
43
43
|
TOOL: 工具调用
|
44
44
|
"""
|
45
|
+
|
45
46
|
SYSTEM = "SYSTEM"
|
46
47
|
CODE = "CODE"
|
47
48
|
RESULT = "RESULT"
|
@@ -54,6 +55,8 @@ class OutputType(Enum):
|
|
54
55
|
DEBUG = "DEBUG"
|
55
56
|
USER = "USER"
|
56
57
|
TOOL = "TOOL"
|
58
|
+
|
59
|
+
|
57
60
|
class PrettyOutput:
|
58
61
|
"""
|
59
62
|
使用rich库格式化和显示富文本输出的类。
|
@@ -64,6 +67,7 @@ class PrettyOutput:
|
|
64
67
|
- 结构化内容的面板显示
|
65
68
|
- 渐进显示的流式输出
|
66
69
|
"""
|
70
|
+
|
67
71
|
# 不同输出类型的图标
|
68
72
|
_ICONS = {
|
69
73
|
OutputType.SYSTEM: "🤖",
|
@@ -81,39 +85,40 @@ class PrettyOutput:
|
|
81
85
|
}
|
82
86
|
# 语法高亮的语言映射
|
83
87
|
_lang_map = {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
88
|
+
"Python": "python",
|
89
|
+
"JavaScript": "javascript",
|
90
|
+
"TypeScript": "typescript",
|
91
|
+
"Java": "java",
|
92
|
+
"C++": "cpp",
|
93
|
+
"C#": "csharp",
|
94
|
+
"Ruby": "ruby",
|
95
|
+
"PHP": "php",
|
96
|
+
"Go": "go",
|
97
|
+
"Rust": "rust",
|
98
|
+
"Bash": "bash",
|
99
|
+
"HTML": "html",
|
100
|
+
"CSS": "css",
|
101
|
+
"SQL": "sql",
|
102
|
+
"R": "r",
|
103
|
+
"Kotlin": "kotlin",
|
104
|
+
"Swift": "swift",
|
105
|
+
"Scala": "scala",
|
106
|
+
"Perl": "perl",
|
107
|
+
"Lua": "lua",
|
108
|
+
"YAML": "yaml",
|
109
|
+
"JSON": "json",
|
110
|
+
"XML": "xml",
|
111
|
+
"Markdown": "markdown",
|
112
|
+
"Text": "text",
|
113
|
+
"Shell": "bash",
|
114
|
+
"Dockerfile": "dockerfile",
|
115
|
+
"Makefile": "makefile",
|
116
|
+
"INI": "ini",
|
117
|
+
"TOML": "toml",
|
114
118
|
}
|
119
|
+
|
115
120
|
@staticmethod
|
116
|
-
def _detect_language(text: str, default_lang: str =
|
121
|
+
def _detect_language(text: str, default_lang: str = "markdown") -> str:
|
117
122
|
"""
|
118
123
|
检测给定文本的编程语言。
|
119
124
|
|
@@ -130,6 +135,7 @@ class PrettyOutput:
|
|
130
135
|
return PrettyOutput._lang_map.get(detected_lang, default_lang)
|
131
136
|
except (ClassNotFound, Exception):
|
132
137
|
return default_lang
|
138
|
+
|
133
139
|
@staticmethod
|
134
140
|
def _format(output_type: OutputType, timestamp: bool = True) -> str:
|
135
141
|
"""
|
@@ -145,13 +151,20 @@ class PrettyOutput:
|
|
145
151
|
icon = PrettyOutput._ICONS.get(output_type, "")
|
146
152
|
formatted = f"{icon} "
|
147
153
|
if timestamp:
|
148
|
-
formatted+=f"[{datetime.now().strftime('%H:%M:%S')}][{output_type.value}]"
|
154
|
+
formatted += f"[{datetime.now().strftime('%H:%M:%S')}][{output_type.value}]"
|
149
155
|
agent_info = get_agent_list()
|
150
156
|
if agent_info:
|
151
|
-
formatted+=f"[{agent_info}]"
|
157
|
+
formatted += f"[{agent_info}]"
|
152
158
|
return formatted
|
159
|
+
|
153
160
|
@staticmethod
|
154
|
-
def print(
|
161
|
+
def print(
|
162
|
+
text: str,
|
163
|
+
output_type: OutputType,
|
164
|
+
timestamp: bool = True,
|
165
|
+
lang: Optional[str] = None,
|
166
|
+
traceback: bool = False,
|
167
|
+
):
|
155
168
|
"""
|
156
169
|
使用样式和语法高亮打印格式化输出。
|
157
170
|
|
@@ -163,38 +176,101 @@ class PrettyOutput:
|
|
163
176
|
traceback: 是否显示错误的回溯信息
|
164
177
|
"""
|
165
178
|
styles = {
|
166
|
-
OutputType.SYSTEM: dict(
|
167
|
-
OutputType.CODE: dict(
|
168
|
-
OutputType.RESULT: dict(
|
169
|
-
OutputType.ERROR: dict(
|
170
|
-
OutputType.INFO: dict(
|
171
|
-
OutputType.PLANNING: dict(
|
172
|
-
OutputType.PROGRESS: dict(
|
173
|
-
OutputType.SUCCESS: dict(
|
174
|
-
OutputType.WARNING: dict(
|
175
|
-
OutputType.DEBUG: dict(
|
176
|
-
OutputType.USER: dict(
|
177
|
-
OutputType.TOOL: dict(
|
179
|
+
OutputType.SYSTEM: dict(bgcolor="#1e2b3c"),
|
180
|
+
OutputType.CODE: dict(bgcolor="#1c2b1c"),
|
181
|
+
OutputType.RESULT: dict(bgcolor="#1c1c2b"),
|
182
|
+
OutputType.ERROR: dict(bgcolor="#2b1c1c"),
|
183
|
+
OutputType.INFO: dict(bgcolor="#2b2b1c", meta={"icon": "ℹ️"}),
|
184
|
+
OutputType.PLANNING: dict(bgcolor="#2b1c2b"),
|
185
|
+
OutputType.PROGRESS: dict(bgcolor="#1c1c1c"),
|
186
|
+
OutputType.SUCCESS: dict(bgcolor="#1c2b1c"),
|
187
|
+
OutputType.WARNING: dict(bgcolor="#2b2b1c"),
|
188
|
+
OutputType.DEBUG: dict(bgcolor="#1c1c1c"),
|
189
|
+
OutputType.USER: dict(bgcolor="#1c2b2b"),
|
190
|
+
OutputType.TOOL: dict(bgcolor="#1c2b2b"),
|
178
191
|
}
|
179
192
|
|
180
193
|
header_styles = {
|
181
|
-
OutputType.SYSTEM: RichStyle(
|
182
|
-
|
183
|
-
|
184
|
-
OutputType.
|
185
|
-
|
186
|
-
|
187
|
-
OutputType.
|
188
|
-
|
189
|
-
|
190
|
-
OutputType.
|
191
|
-
|
192
|
-
|
194
|
+
OutputType.SYSTEM: RichStyle(
|
195
|
+
color="bright_cyan", bgcolor="#1e2b3c", frame=True, meta={"icon": "🤖"}
|
196
|
+
),
|
197
|
+
OutputType.CODE: RichStyle(
|
198
|
+
color="green", bgcolor="#1c2b1c", frame=True, meta={"icon": "📝"}
|
199
|
+
),
|
200
|
+
OutputType.RESULT: RichStyle(
|
201
|
+
color="bright_blue", bgcolor="#1c1c2b", frame=True, meta={"icon": "✨"}
|
202
|
+
),
|
203
|
+
OutputType.ERROR: RichStyle(
|
204
|
+
color="red", frame=True, bgcolor="#2b1c1c", meta={"icon": "❌"}
|
205
|
+
),
|
206
|
+
OutputType.INFO: RichStyle(
|
207
|
+
color="gold1", frame=True, bgcolor="#2b2b1c", meta={"icon": "ℹ️"}
|
208
|
+
),
|
209
|
+
OutputType.PLANNING: RichStyle(
|
210
|
+
color="purple",
|
211
|
+
bold=True,
|
212
|
+
frame=True,
|
213
|
+
bgcolor="#2b1c2b",
|
214
|
+
meta={"icon": "📋"},
|
215
|
+
),
|
216
|
+
OutputType.PROGRESS: RichStyle(
|
217
|
+
color="white",
|
218
|
+
encircle=True,
|
219
|
+
frame=True,
|
220
|
+
bgcolor="#1c1c1c",
|
221
|
+
meta={"icon": "⏳"},
|
222
|
+
),
|
223
|
+
OutputType.SUCCESS: RichStyle(
|
224
|
+
color="bright_green",
|
225
|
+
bold=True,
|
226
|
+
strike=False,
|
227
|
+
bgcolor="#1c2b1c",
|
228
|
+
meta={"icon": "✅"},
|
229
|
+
),
|
230
|
+
OutputType.WARNING: RichStyle(
|
231
|
+
color="yellow",
|
232
|
+
bold=True,
|
233
|
+
blink2=True,
|
234
|
+
bgcolor="#2b2b1c",
|
235
|
+
meta={"icon": "⚠️"},
|
236
|
+
),
|
237
|
+
OutputType.DEBUG: RichStyle(
|
238
|
+
color="grey58",
|
239
|
+
dim=True,
|
240
|
+
conceal=True,
|
241
|
+
bgcolor="#1c1c1c",
|
242
|
+
meta={"icon": "🔍"},
|
243
|
+
),
|
244
|
+
OutputType.USER: RichStyle(
|
245
|
+
color="spring_green2",
|
246
|
+
frame=True,
|
247
|
+
bgcolor="#1c2b2b",
|
248
|
+
meta={"icon": "👤"},
|
249
|
+
),
|
250
|
+
OutputType.TOOL: RichStyle(
|
251
|
+
color="dark_sea_green4",
|
252
|
+
bgcolor="#1c2b2b",
|
253
|
+
frame=True,
|
254
|
+
meta={"icon": "🔧"},
|
255
|
+
),
|
193
256
|
}
|
194
257
|
|
195
|
-
lang =
|
196
|
-
|
197
|
-
|
258
|
+
lang = (
|
259
|
+
lang
|
260
|
+
if lang is not None
|
261
|
+
else PrettyOutput._detect_language(text, default_lang="markdown")
|
262
|
+
)
|
263
|
+
header = Text(
|
264
|
+
PrettyOutput._format(output_type, timestamp),
|
265
|
+
style=header_styles[output_type],
|
266
|
+
)
|
267
|
+
content = Syntax(
|
268
|
+
text,
|
269
|
+
lang,
|
270
|
+
theme="monokai",
|
271
|
+
word_wrap=True,
|
272
|
+
background_color=styles[output_type]["bgcolor"],
|
273
|
+
)
|
198
274
|
panel = Panel(
|
199
275
|
content,
|
200
276
|
border_style=header_styles[output_type],
|
@@ -211,8 +287,12 @@ class PrettyOutput:
|
|
211
287
|
console.print(content)
|
212
288
|
else:
|
213
289
|
console.print(header, content)
|
214
|
-
if traceback:
|
215
|
-
|
290
|
+
if traceback or output_type == OutputType.ERROR:
|
291
|
+
try:
|
292
|
+
console.print_exception()
|
293
|
+
except Exception as e:
|
294
|
+
console.print(f"Error: {e}")
|
295
|
+
|
216
296
|
@staticmethod
|
217
297
|
def section(title: str, output_type: OutputType = OutputType.INFO):
|
218
298
|
"""
|
@@ -223,38 +303,42 @@ class PrettyOutput:
|
|
223
303
|
output_type: 输出类型(影响样式)
|
224
304
|
"""
|
225
305
|
text = Text(title, style=output_type.value, justify="center")
|
226
|
-
panel = Panel(
|
227
|
-
text,
|
228
|
-
border_style=output_type.value
|
229
|
-
)
|
306
|
+
panel = Panel(text, border_style=output_type.value)
|
230
307
|
if get_pretty_output():
|
231
308
|
console.print(panel)
|
232
309
|
else:
|
233
310
|
console.print(text)
|
234
|
-
|
311
|
+
|
235
312
|
@staticmethod
|
236
|
-
def print_gradient_text(
|
313
|
+
def print_gradient_text(
|
314
|
+
text: str, start_color: Tuple[int, int, int], end_color: Tuple[int, int, int]
|
315
|
+
) -> None:
|
237
316
|
"""打印带有渐变色彩的文本。
|
238
|
-
|
317
|
+
|
239
318
|
Args:
|
240
319
|
text: 要打印的文本
|
241
320
|
start_color: 起始RGB颜色元组 (r, g, b)
|
242
321
|
end_color: 结束RGB颜色元组 (r, g, b)
|
243
322
|
"""
|
244
|
-
lines = text.strip(
|
323
|
+
lines = text.strip("\n").split("\n")
|
245
324
|
total_lines = len(lines)
|
246
325
|
colored_lines = []
|
247
326
|
for i, line in enumerate(lines):
|
248
327
|
# 计算当前行的渐变颜色
|
249
|
-
r = int(
|
250
|
-
|
251
|
-
|
252
|
-
|
328
|
+
r = int(
|
329
|
+
start_color[0] + (end_color[0] - start_color[0]) * i / (total_lines - 1)
|
330
|
+
)
|
331
|
+
g = int(
|
332
|
+
start_color[1] + (end_color[1] - start_color[1]) * i / (total_lines - 1)
|
333
|
+
)
|
334
|
+
b = int(
|
335
|
+
start_color[2] + (end_color[2] - start_color[2]) * i / (total_lines - 1)
|
336
|
+
)
|
337
|
+
|
253
338
|
# 使用ANSI转义序列设置颜色
|
254
339
|
colored_lines.append(f"\033[38;2;{r};{g};{b}m{line}\033[0m")
|
255
|
-
colored_text = Text(
|
256
|
-
|
257
|
-
colored_text,
|
258
|
-
box=SIMPLE
|
340
|
+
colored_text = Text(
|
341
|
+
"\n".join(colored_lines), style=OutputType.TOOL.value, justify="center"
|
259
342
|
)
|
260
|
-
|
343
|
+
panel = Panel(colored_text, box=SIMPLE)
|
344
|
+
console.print(panel)
|
jarvis/jarvis_utils/tag.py
CHANGED
@@ -10,6 +10,7 @@ def ot(tag_name: str) -> str:
|
|
10
10
|
"""
|
11
11
|
return f"<{tag_name}>"
|
12
12
|
|
13
|
+
|
13
14
|
def ct(tag_name: str) -> str:
|
14
15
|
"""生成HTML标签结束标记
|
15
16
|
|
@@ -19,4 +20,4 @@ def ct(tag_name: str) -> str:
|
|
19
20
|
返回:
|
20
21
|
格式化的结束标签字符串
|
21
22
|
"""
|
22
|
-
return f"</{tag_name}>"
|
23
|
+
return f"</{tag_name}>"
|
jarvis/jarvis_utils/utils.py
CHANGED
@@ -11,12 +11,15 @@ from typing import Any, Callable, Dict, Optional
|
|
11
11
|
import yaml
|
12
12
|
|
13
13
|
from jarvis import __version__
|
14
|
-
from jarvis.jarvis_utils.config import get_data_dir, get_max_big_content_size,
|
14
|
+
from jarvis.jarvis_utils.config import (get_data_dir, get_max_big_content_size,
|
15
|
+
set_global_env_data)
|
15
16
|
from jarvis.jarvis_utils.embedding import get_context_token_count
|
17
|
+
from jarvis.jarvis_utils.globals import (get_in_chat, get_interrupt,
|
18
|
+
set_interrupt)
|
16
19
|
from jarvis.jarvis_utils.input import get_single_line_input
|
17
20
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
18
|
-
from jarvis.jarvis_utils.globals import get_in_chat, get_interrupt, set_interrupt
|
19
21
|
|
22
|
+
g_config_file = None
|
20
23
|
|
21
24
|
|
22
25
|
def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
@@ -35,7 +38,7 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
35
38
|
"""
|
36
39
|
# 保存原始信号处理函数
|
37
40
|
original_sigint = signal.getsignal(signal.SIGINT)
|
38
|
-
|
41
|
+
|
39
42
|
def sigint_handler(signum, frame):
|
40
43
|
if get_in_chat():
|
41
44
|
set_interrupt(True)
|
@@ -44,7 +47,7 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
44
47
|
else:
|
45
48
|
if original_sigint and callable(original_sigint):
|
46
49
|
original_sigint(signum, frame)
|
47
|
-
|
50
|
+
|
48
51
|
signal.signal(signal.SIGINT, sigint_handler)
|
49
52
|
count_cmd_usage()
|
50
53
|
|
@@ -63,22 +66,16 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
63
66
|
if welcome_str:
|
64
67
|
PrettyOutput.print_gradient_text(jarvis_ascii_art, (0, 120, 255), (0, 255, 200))
|
65
68
|
|
66
|
-
|
69
|
+
global g_config_file
|
70
|
+
g_config_file = config_file
|
67
71
|
|
68
|
-
|
69
|
-
if not config_file_path.exists():
|
70
|
-
old_config_file = config_file_path.parent / "env"
|
71
|
-
if old_config_file.exists():# 旧的配置文件存在
|
72
|
-
_read_old_config_file(old_config_file)
|
73
|
-
else:
|
74
|
-
_read_config_file(config_file_path.parent, config_file_path)
|
72
|
+
load_config()
|
75
73
|
|
76
74
|
# 现在获取最终的数据目录(可能被配置文件修改)
|
77
75
|
data_dir = Path(get_data_dir())
|
78
76
|
script_dir = Path(os.path.dirname(os.path.dirname(__file__)))
|
79
77
|
hf_archive = script_dir / "jarvis_data" / "huggingface.tar.gz"
|
80
78
|
|
81
|
-
|
82
79
|
# 检查并解压huggingface模型
|
83
80
|
hf_dir = data_dir / "huggingface" / "hub"
|
84
81
|
if not hf_dir.exists() and hf_archive.exists():
|
@@ -88,22 +85,40 @@ def init_env(welcome_str: str, config_file: Optional[str] = None) -> None:
|
|
88
85
|
tar.extractall(path=data_dir)
|
89
86
|
PrettyOutput.print("HuggingFace模型解压完成", OutputType.SUCCESS)
|
90
87
|
except Exception as e:
|
91
|
-
PrettyOutput.print(f"解压HuggingFace模型失败: {e}", OutputType.ERROR)
|
88
|
+
PrettyOutput.print(f"解压HuggingFace模型失败: {e}", OutputType.ERROR)
|
92
89
|
|
93
90
|
# 检查是否是git仓库并更新
|
94
91
|
from jarvis.jarvis_utils.git_utils import check_and_update_git_repo
|
95
92
|
|
96
93
|
check_and_update_git_repo(str(script_dir))
|
97
94
|
|
95
|
+
|
96
|
+
def load_config():
|
97
|
+
config_file = g_config_file
|
98
|
+
config_file_path = (
|
99
|
+
Path(config_file)
|
100
|
+
if config_file is not None
|
101
|
+
else Path(os.path.expanduser("~/.jarvis/config.yaml"))
|
102
|
+
)
|
103
|
+
|
104
|
+
# 加载配置文件
|
105
|
+
if not config_file_path.exists():
|
106
|
+
old_config_file = config_file_path.parent / "env"
|
107
|
+
if old_config_file.exists(): # 旧的配置文件存在
|
108
|
+
_read_old_config_file(old_config_file)
|
109
|
+
else:
|
110
|
+
_read_config_file(config_file_path.parent, config_file_path)
|
111
|
+
|
112
|
+
|
98
113
|
def _read_config_file(jarvis_dir, config_file):
|
99
114
|
"""读取并解析YAML格式的配置文件
|
100
|
-
|
115
|
+
|
101
116
|
功能:
|
102
117
|
1. 读取配置文件内容
|
103
118
|
2. 检查并添加schema声明(如果缺失)
|
104
119
|
3. 将配置数据保存到全局变量
|
105
120
|
4. 设置环境变量(如果配置中有ENV字段)
|
106
|
-
|
121
|
+
|
107
122
|
参数:
|
108
123
|
jarvis_dir: Jarvis数据目录路径
|
109
124
|
config_file: 配置文件路径
|
@@ -112,31 +127,38 @@ def _read_config_file(jarvis_dir, config_file):
|
|
112
127
|
content = f.read()
|
113
128
|
config_data = yaml.safe_load(content) or {}
|
114
129
|
if isinstance(config_data, dict):
|
115
|
-
|
130
|
+
# 检查是否已有schema声明,没有则添加
|
116
131
|
if "# yaml-language-server: $schema=" not in content:
|
117
|
-
schema_path = Path(
|
118
|
-
|
119
|
-
|
120
|
-
|
132
|
+
schema_path = Path(
|
133
|
+
os.path.relpath(
|
134
|
+
Path(__file__).parent.parent
|
135
|
+
/ "jarvis_data"
|
136
|
+
/ "config_schema.json",
|
137
|
+
start=jarvis_dir,
|
138
|
+
)
|
139
|
+
)
|
121
140
|
with open(config_file, "w", encoding="utf-8") as f:
|
122
141
|
f.write(f"# yaml-language-server: $schema={schema_path}\n")
|
123
142
|
f.write(content)
|
124
143
|
# 保存到全局变量
|
125
144
|
set_global_env_data(config_data)
|
126
|
-
|
145
|
+
# 如果配置中有ENV键值对,则设置环境变量
|
127
146
|
if "ENV" in config_data and isinstance(config_data["ENV"], dict):
|
128
|
-
os.environ.update(
|
147
|
+
os.environ.update(
|
148
|
+
{str(k): str(v) for k, v in config_data["ENV"].items() if v is not None}
|
149
|
+
)
|
150
|
+
|
129
151
|
|
130
152
|
def _read_old_config_file(config_file):
|
131
153
|
"""读取并解析旧格式的env配置文件
|
132
|
-
|
154
|
+
|
133
155
|
功能:
|
134
156
|
1. 解析键值对格式的旧配置文件
|
135
157
|
2. 支持多行值的处理
|
136
158
|
3. 自动去除值的引号和空格
|
137
159
|
4. 将配置数据保存到全局变量
|
138
160
|
5. 设置环境变量并显示迁移警告
|
139
|
-
|
161
|
+
|
140
162
|
参数:
|
141
163
|
config_file: 旧格式配置文件路径
|
142
164
|
"""
|
@@ -149,7 +171,7 @@ def _read_old_config_file(config_file):
|
|
149
171
|
if not line or line.startswith(("#", ";")):
|
150
172
|
continue
|
151
173
|
if "=" in line and not line.startswith((" ", "\t")):
|
152
|
-
|
174
|
+
# 处理之前收集的多行值
|
153
175
|
if current_key is not None:
|
154
176
|
value = "\n".join(current_value).strip().strip("'").strip('"')
|
155
177
|
# 将字符串"true"/"false"转换为bool类型
|
@@ -159,12 +181,12 @@ def _read_old_config_file(config_file):
|
|
159
181
|
value = False
|
160
182
|
config_data[current_key] = value
|
161
183
|
current_value = []
|
162
|
-
|
184
|
+
# 解析新的键值对
|
163
185
|
key, value = line.split("=", 1)
|
164
186
|
current_key = key.strip()
|
165
187
|
current_value.append(value.strip())
|
166
188
|
elif current_key is not None:
|
167
|
-
|
189
|
+
# 多行值的后续行
|
168
190
|
current_value.append(line.strip())
|
169
191
|
# 处理最后一个键值对
|
170
192
|
if current_key is not None:
|
@@ -175,11 +197,16 @@ def _read_old_config_file(config_file):
|
|
175
197
|
elif value.lower() == "false":
|
176
198
|
value = False
|
177
199
|
config_data[current_key] = value
|
178
|
-
os.environ.update(
|
200
|
+
os.environ.update(
|
201
|
+
{str(k): str(v) for k, v in config_data.items() if v is not None}
|
202
|
+
)
|
179
203
|
set_global_env_data(config_data)
|
180
|
-
PrettyOutput.print(
|
204
|
+
PrettyOutput.print(
|
205
|
+
f"检测到旧格式配置文件,旧格式以后将不再支持,请尽快迁移到新格式",
|
206
|
+
OutputType.WARNING,
|
207
|
+
)
|
208
|
+
|
181
209
|
|
182
|
-
|
183
210
|
def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
184
211
|
"""循环执行函数直到成功
|
185
212
|
|
@@ -194,19 +221,23 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
|
|
194
221
|
try:
|
195
222
|
return func()
|
196
223
|
except Exception as e:
|
197
|
-
PrettyOutput.print(
|
224
|
+
PrettyOutput.print(
|
225
|
+
f"执行失败: {str(e)}, 等待 {sleep_time}s...", OutputType.WARNING
|
226
|
+
)
|
198
227
|
time.sleep(sleep_time)
|
199
228
|
continue
|
229
|
+
|
230
|
+
|
200
231
|
def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
|
201
232
|
"""循环执行函数直到返回True
|
202
|
-
|
233
|
+
|
203
234
|
参数:
|
204
235
|
func: 要执行的函数,必须返回布尔值
|
205
236
|
sleep_time: 每次失败后的等待时间(秒)
|
206
|
-
|
237
|
+
|
207
238
|
返回:
|
208
239
|
函数最终返回的True值
|
209
|
-
|
240
|
+
|
210
241
|
注意:
|
211
242
|
与while_success不同,此函数只检查返回是否为True,
|
212
243
|
不捕获异常,异常会直接抛出
|
@@ -218,7 +249,9 @@ def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
|
|
218
249
|
PrettyOutput.print(f"执行失败, 等待 {sleep_time}s...", OutputType.WARNING)
|
219
250
|
time.sleep(sleep_time)
|
220
251
|
return ret
|
221
|
-
|
252
|
+
|
253
|
+
|
254
|
+
def get_file_md5(filepath: str) -> str:
|
222
255
|
"""计算文件内容的MD5哈希值
|
223
256
|
|
224
257
|
参数:
|
@@ -227,7 +260,9 @@ def get_file_md5(filepath: str)->str:
|
|
227
260
|
返回:
|
228
261
|
str: 文件内容的MD5哈希值
|
229
262
|
"""
|
230
|
-
return hashlib.md5(open(filepath, "rb").read(100*1024*1024)).hexdigest()
|
263
|
+
return hashlib.md5(open(filepath, "rb").read(100 * 1024 * 1024)).hexdigest()
|
264
|
+
|
265
|
+
|
231
266
|
def user_confirm(tip: str, default: bool = True) -> bool:
|
232
267
|
"""提示用户确认是/否问题
|
233
268
|
|
@@ -242,6 +277,7 @@ def user_confirm(tip: str, default: bool = True) -> bool:
|
|
242
277
|
ret = get_single_line_input(f"{tip} {suffix}: ")
|
243
278
|
return default if ret == "" else ret.lower() == "y"
|
244
279
|
|
280
|
+
|
245
281
|
def get_file_line_count(filename: str) -> int:
|
246
282
|
"""计算文件中的行数
|
247
283
|
|
@@ -257,7 +293,6 @@ def get_file_line_count(filename: str) -> int:
|
|
257
293
|
return 0
|
258
294
|
|
259
295
|
|
260
|
-
|
261
296
|
def _get_cmd_stats() -> Dict[str, int]:
|
262
297
|
"""从数据目录获取命令调用统计"""
|
263
298
|
stats_file = Path(get_data_dir()) / "cmd_stat.yaml"
|
@@ -266,11 +301,10 @@ def _get_cmd_stats() -> Dict[str, int]:
|
|
266
301
|
with open(stats_file, "r", encoding="utf-8") as f:
|
267
302
|
return yaml.safe_load(f) or {}
|
268
303
|
except Exception as e:
|
269
|
-
PrettyOutput.print(
|
270
|
-
f"加载命令调用统计失败: {str(e)}", OutputType.WARNING
|
271
|
-
)
|
304
|
+
PrettyOutput.print(f"加载命令调用统计失败: {str(e)}", OutputType.WARNING)
|
272
305
|
return {}
|
273
306
|
|
307
|
+
|
274
308
|
def _update_cmd_stats(cmd_name: str) -> None:
|
275
309
|
"""更新命令调用统计"""
|
276
310
|
stats = _get_cmd_stats()
|
@@ -280,31 +314,29 @@ def _update_cmd_stats(cmd_name: str) -> None:
|
|
280
314
|
with open(stats_file, "w", encoding="utf-8") as f:
|
281
315
|
yaml.safe_dump(stats, f, allow_unicode=True)
|
282
316
|
except Exception as e:
|
283
|
-
PrettyOutput.print(
|
284
|
-
|
285
|
-
)
|
317
|
+
PrettyOutput.print(f"保存命令调用统计失败: {str(e)}", OutputType.WARNING)
|
318
|
+
|
286
319
|
|
287
320
|
def count_cmd_usage() -> None:
|
288
321
|
"""统计当前命令的使用次数"""
|
289
322
|
import sys
|
323
|
+
|
290
324
|
_update_cmd_stats(sys.argv[0])
|
291
325
|
|
326
|
+
|
292
327
|
def is_context_overflow(content: str) -> bool:
|
293
328
|
"""判断文件内容是否超出上下文限制"""
|
294
|
-
return get_context_token_count(content) > get_max_big_content_size()
|
329
|
+
return get_context_token_count(content) > get_max_big_content_size()
|
330
|
+
|
295
331
|
|
296
332
|
def get_loc_stats() -> str:
|
297
333
|
"""使用loc命令获取当前目录的代码统计信息
|
298
|
-
|
334
|
+
|
299
335
|
返回:
|
300
336
|
str: loc命令输出的原始字符串,失败时返回空字符串
|
301
337
|
"""
|
302
338
|
try:
|
303
|
-
result = subprocess.run(
|
304
|
-
['loc'],
|
305
|
-
capture_output=True,
|
306
|
-
text=True
|
307
|
-
)
|
339
|
+
result = subprocess.run(["loc"], capture_output=True, text=True)
|
308
340
|
return result.stdout if result.returncode == 0 else ""
|
309
341
|
except FileNotFoundError:
|
310
|
-
return ""
|
342
|
+
return ""
|