jarvis-ai-assistant 0.3.18__py3-none-any.whl → 0.3.20__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 +30 -12
- jarvis/jarvis_agent/config_editor.py +1 -1
- jarvis/jarvis_agent/edit_file_handler.py +8 -13
- jarvis/jarvis_agent/memory_manager.py +4 -4
- jarvis/jarvis_agent/shell_input_handler.py +17 -2
- jarvis/jarvis_agent/task_analyzer.py +4 -3
- jarvis/jarvis_agent/task_manager.py +6 -6
- jarvis/jarvis_agent/tool_executor.py +2 -2
- jarvis/jarvis_code_agent/code_agent.py +21 -29
- jarvis/jarvis_code_analysis/code_review.py +2 -4
- jarvis/jarvis_git_utils/git_commiter.py +17 -18
- jarvis/jarvis_methodology/main.py +12 -12
- jarvis/jarvis_platform/ai8.py +0 -4
- jarvis/jarvis_platform/base.py +16 -15
- jarvis/jarvis_platform/kimi.py +13 -13
- jarvis/jarvis_platform/tongyi.py +17 -15
- jarvis/jarvis_platform/yuanbao.py +11 -11
- jarvis/jarvis_platform_manager/service.py +2 -2
- jarvis/jarvis_rag/cli.py +36 -32
- jarvis/jarvis_rag/embedding_manager.py +11 -6
- jarvis/jarvis_rag/llm_interface.py +6 -5
- jarvis/jarvis_rag/rag_pipeline.py +9 -8
- jarvis/jarvis_rag/reranker.py +3 -2
- jarvis/jarvis_rag/retriever.py +18 -8
- jarvis/jarvis_smart_shell/main.py +307 -47
- jarvis/jarvis_stats/cli.py +2 -1
- jarvis/jarvis_stats/stats.py +45 -5
- jarvis/jarvis_stats/storage.py +220 -9
- jarvis/jarvis_tools/clear_memory.py +0 -11
- jarvis/jarvis_tools/cli/main.py +18 -17
- jarvis/jarvis_tools/edit_file.py +4 -4
- jarvis/jarvis_tools/execute_script.py +5 -1
- jarvis/jarvis_tools/file_analyzer.py +6 -6
- jarvis/jarvis_tools/generate_new_tool.py +6 -17
- jarvis/jarvis_tools/read_code.py +3 -6
- jarvis/jarvis_tools/read_webpage.py +4 -4
- jarvis/jarvis_tools/registry.py +8 -28
- jarvis/jarvis_tools/retrieve_memory.py +5 -16
- jarvis/jarvis_tools/rewrite_file.py +0 -4
- jarvis/jarvis_tools/save_memory.py +2 -10
- jarvis/jarvis_tools/search_web.py +5 -8
- jarvis/jarvis_tools/virtual_tty.py +22 -40
- jarvis/jarvis_utils/clipboard.py +2 -2
- jarvis/jarvis_utils/input.py +316 -30
- jarvis/jarvis_utils/methodology.py +3 -3
- jarvis/jarvis_utils/output.py +215 -135
- jarvis/jarvis_utils/utils.py +35 -58
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/RECORD +54 -54
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.18.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/top_level.txt +0 -0
@@ -146,11 +146,11 @@ def extract_methodology(
|
|
146
146
|
"""
|
147
147
|
|
148
148
|
# 调用大模型平台提取方法论
|
149
|
-
print("
|
149
|
+
PrettyOutput.print("正在提取方法论...", OutputType.INFO)
|
150
150
|
try:
|
151
151
|
response = platform.chat_until_success(prompt)
|
152
152
|
except Exception as e:
|
153
|
-
print("
|
153
|
+
PrettyOutput.print("提取失败", OutputType.ERROR)
|
154
154
|
PrettyOutput.print(f"提取方法论失败: {str(e)}", OutputType.ERROR)
|
155
155
|
raise typer.Exit(code=1)
|
156
156
|
|
@@ -158,7 +158,7 @@ def extract_methodology(
|
|
158
158
|
methodologies_start = response.find("<methodologies>") + len("<methodologies>")
|
159
159
|
methodologies_end = response.find("</methodologies>")
|
160
160
|
if methodologies_start == -1 or methodologies_end == -1:
|
161
|
-
print("
|
161
|
+
PrettyOutput.print("响应格式无效", OutputType.ERROR)
|
162
162
|
PrettyOutput.print(
|
163
163
|
"大模型未返回有效的<methodologies>格式", OutputType.ERROR
|
164
164
|
)
|
@@ -172,14 +172,14 @@ def extract_methodology(
|
|
172
172
|
item["problem_type"]: item["content"] for item in data
|
173
173
|
}
|
174
174
|
except (yaml.YAMLError, KeyError, TypeError) as e:
|
175
|
-
print("
|
175
|
+
PrettyOutput.print("YAML解析失败", OutputType.ERROR)
|
176
176
|
PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
|
177
177
|
raise typer.Exit(code=1)
|
178
178
|
|
179
179
|
if not extracted_methodologies:
|
180
|
-
print("
|
180
|
+
PrettyOutput.print("未提取到有效方法论", OutputType.WARNING)
|
181
181
|
return
|
182
|
-
print("
|
182
|
+
PrettyOutput.print("提取到有效方法论", OutputType.SUCCESS)
|
183
183
|
|
184
184
|
# 加载现有方法论
|
185
185
|
existing_methodologies = _load_all_methodologies()
|
@@ -247,11 +247,11 @@ def extract_methodology_from_url(url: str = typer.Argument(..., help="要提取
|
|
247
247
|
6. 内容字段使用|保留多行格式
|
248
248
|
"""
|
249
249
|
# 调用大模型平台提取方法论
|
250
|
-
print("
|
250
|
+
PrettyOutput.print("正在从URL提取方法论...", OutputType.INFO)
|
251
251
|
try:
|
252
252
|
response = platform.chat_until_success(prompt)
|
253
253
|
except Exception as e:
|
254
|
-
print("
|
254
|
+
PrettyOutput.print("提取失败", OutputType.ERROR)
|
255
255
|
PrettyOutput.print(f"提取方法论失败: {str(e)}", OutputType.ERROR)
|
256
256
|
raise typer.Exit(code=1)
|
257
257
|
|
@@ -259,7 +259,7 @@ def extract_methodology_from_url(url: str = typer.Argument(..., help="要提取
|
|
259
259
|
methodologies_start = response.find("<methodologies>") + len("<methodologies>")
|
260
260
|
methodologies_end = response.find("</methodologies>")
|
261
261
|
if methodologies_start == -1 or methodologies_end == -1:
|
262
|
-
print("
|
262
|
+
PrettyOutput.print("响应格式无效", OutputType.ERROR)
|
263
263
|
PrettyOutput.print(
|
264
264
|
"大模型未返回有效的<methodologies>格式", OutputType.ERROR
|
265
265
|
)
|
@@ -273,14 +273,14 @@ def extract_methodology_from_url(url: str = typer.Argument(..., help="要提取
|
|
273
273
|
item["problem_type"]: item["content"] for item in data
|
274
274
|
}
|
275
275
|
except (yaml.YAMLError, KeyError, TypeError) as e:
|
276
|
-
print("
|
276
|
+
PrettyOutput.print("YAML解析失败", OutputType.ERROR)
|
277
277
|
PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
|
278
278
|
raise typer.Exit(code=1)
|
279
279
|
|
280
280
|
if not extracted_methodologies:
|
281
|
-
print("
|
281
|
+
PrettyOutput.print("未提取到有效方法论", OutputType.WARNING)
|
282
282
|
return
|
283
|
-
print("
|
283
|
+
PrettyOutput.print("提取到有效方法论", OutputType.SUCCESS)
|
284
284
|
|
285
285
|
# 加载现有方法论
|
286
286
|
existing_methodologies = _load_all_methodologies()
|
jarvis/jarvis_platform/ai8.py
CHANGED
@@ -50,10 +50,6 @@ class AI8Model(BasePlatform):
|
|
50
50
|
}
|
51
51
|
|
52
52
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
53
|
-
if self.model_name not in self.get_available_models():
|
54
|
-
PrettyOutput.print(
|
55
|
-
f"警告: 选择的模型 {self.model_name} 不在可用列表中", OutputType.WARNING
|
56
|
-
)
|
57
53
|
|
58
54
|
def set_model_name(self, model_name: str):
|
59
55
|
"""Set model name"""
|
jarvis/jarvis_platform/base.py
CHANGED
@@ -18,7 +18,7 @@ from jarvis.jarvis_utils.config import (
|
|
18
18
|
is_immediate_abort,
|
19
19
|
)
|
20
20
|
from jarvis.jarvis_utils.embedding import split_text_into_chunks
|
21
|
-
from jarvis.jarvis_utils.globals import set_in_chat, get_interrupt
|
21
|
+
from jarvis.jarvis_utils.globals import set_in_chat, get_interrupt, console
|
22
22
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
23
23
|
from jarvis.jarvis_utils.tag import ct, ot
|
24
24
|
from jarvis.jarvis_utils.utils import get_context_token_count, while_success, while_true
|
@@ -84,7 +84,7 @@ class BasePlatform(ABC):
|
|
84
84
|
) # 留出一些余量
|
85
85
|
min_chunk_size = get_max_input_token_count(self.model_group) - 2048
|
86
86
|
inputs = split_text_into_chunks(message, max_chunk_size, min_chunk_size)
|
87
|
-
print("
|
87
|
+
PrettyOutput.print(f"长上下文,分批提交,共{len(inputs)}部分...", OutputType.INFO)
|
88
88
|
prefix_prompt = f"""
|
89
89
|
我将分多次提供大量内容,在我明确告诉你内容已经全部提供完毕之前,每次仅需要输出"已收到",明白请输出"开始接收输入"。
|
90
90
|
"""
|
@@ -95,9 +95,7 @@ class BasePlatform(ABC):
|
|
95
95
|
for input in inputs:
|
96
96
|
submit_count += 1
|
97
97
|
length += len(input)
|
98
|
-
|
99
|
-
f"📤 正在提交第{submit_count}部分(共{len(inputs)}部分({length}/{len(message)}))"
|
100
|
-
)
|
98
|
+
|
101
99
|
|
102
100
|
response += "\n"
|
103
101
|
for trunk in while_true(
|
@@ -111,10 +109,8 @@ class BasePlatform(ABC):
|
|
111
109
|
):
|
112
110
|
response += trunk
|
113
111
|
|
114
|
-
|
115
|
-
|
116
|
-
)
|
117
|
-
print("✅ 提交完成")
|
112
|
+
|
113
|
+
PrettyOutput.print("提交完成", OutputType.SUCCESS)
|
118
114
|
response += "\n" + while_true(
|
119
115
|
lambda: while_success(
|
120
116
|
lambda: self._chat("内容已经全部提供完毕,请根据内容继续"), 5
|
@@ -128,7 +124,7 @@ class BasePlatform(ABC):
|
|
128
124
|
panel = Panel(
|
129
125
|
text_content,
|
130
126
|
title=f"[bold cyan]{self.name()}[/bold cyan]",
|
131
|
-
subtitle="[dim]思考中...[/dim]",
|
127
|
+
subtitle="[dim]思考中... (按 Ctrl+C 中断)[/dim]",
|
132
128
|
border_style="bright_blue",
|
133
129
|
box=box.ROUNDED,
|
134
130
|
)
|
@@ -138,11 +134,11 @@ class BasePlatform(ABC):
|
|
138
134
|
with Live(panel, refresh_per_second=10, transient=False) as live:
|
139
135
|
for s in self.chat(message):
|
140
136
|
response += s
|
141
|
-
if is_immediate_abort() and get_interrupt():
|
142
|
-
return response
|
143
137
|
text_content.append(s, style="bright_white")
|
144
|
-
panel.subtitle = "[yellow]正在回答...[/yellow]"
|
138
|
+
panel.subtitle = "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
|
145
139
|
live.update(panel)
|
140
|
+
if is_immediate_abort() and get_interrupt():
|
141
|
+
return response
|
146
142
|
end_time = time.time()
|
147
143
|
duration = end_time - start_time
|
148
144
|
panel.subtitle = (
|
@@ -150,12 +146,17 @@ class BasePlatform(ABC):
|
|
150
146
|
)
|
151
147
|
live.update(panel)
|
152
148
|
else:
|
149
|
+
# Print a clear prefix line before streaming model output (non-pretty mode)
|
150
|
+
console.print(f"🤖 模型输出 - {self.name()} (按 Ctrl+C 中断)", soft_wrap=False)
|
153
151
|
for s in self.chat(message):
|
154
|
-
print(s, end=""
|
152
|
+
console.print(s, end="")
|
155
153
|
response += s
|
156
154
|
if is_immediate_abort() and get_interrupt():
|
157
155
|
return response
|
158
|
-
print()
|
156
|
+
console.print()
|
157
|
+
end_time = time.time()
|
158
|
+
duration = end_time - start_time
|
159
|
+
console.print(f"✓ 对话完成耗时: {duration:.2f}秒")
|
159
160
|
else:
|
160
161
|
for s in self.chat(message):
|
161
162
|
response += s
|
jarvis/jarvis_platform/kimi.py
CHANGED
@@ -185,16 +185,16 @@ class KimiModel(BasePlatform):
|
|
185
185
|
return True
|
186
186
|
|
187
187
|
if not self.chat_id:
|
188
|
-
print("
|
188
|
+
PrettyOutput.print("正在创建聊天会话...", OutputType.INFO)
|
189
189
|
if not self._create_chat():
|
190
|
-
print("
|
190
|
+
PrettyOutput.print("创建聊天会话失败", OutputType.ERROR)
|
191
191
|
return False
|
192
|
-
print("
|
192
|
+
PrettyOutput.print("创建聊天会话成功", OutputType.SUCCESS)
|
193
193
|
|
194
194
|
uploaded_files = []
|
195
195
|
for index, file_path in enumerate(file_list, 1):
|
196
196
|
file_name = os.path.basename(file_path)
|
197
|
-
print(f"
|
197
|
+
PrettyOutput.print(f"处理文件 [{index}/{len(file_list)}]: {file_name}", OutputType.INFO)
|
198
198
|
try:
|
199
199
|
mime_type, _ = mimetypes.guess_type(file_path)
|
200
200
|
action = (
|
@@ -202,34 +202,34 @@ class KimiModel(BasePlatform):
|
|
202
202
|
)
|
203
203
|
|
204
204
|
# 获取预签名URL
|
205
|
-
print(f"
|
205
|
+
PrettyOutput.print(f"获取上传URL: {file_name}", OutputType.INFO)
|
206
206
|
presigned_data = self._get_presigned_url(file_path, action)
|
207
207
|
|
208
208
|
# 上传文件
|
209
|
-
print(f"
|
209
|
+
PrettyOutput.print(f"上传文件: {file_name}", OutputType.INFO)
|
210
210
|
if self._upload_file(file_path, presigned_data["url"]):
|
211
211
|
# 获取文件信息
|
212
|
-
print(f"
|
212
|
+
PrettyOutput.print(f"获取文件信息: {file_name}", OutputType.INFO)
|
213
213
|
file_info = self._get_file_info(presigned_data, file_name, action)
|
214
214
|
|
215
215
|
# 只有文件需要解析
|
216
216
|
if action == "file":
|
217
|
-
print(f"
|
217
|
+
PrettyOutput.print(f"等待文件解析: {file_name}", OutputType.INFO)
|
218
218
|
if self._wait_for_parse(file_info["id"]):
|
219
219
|
uploaded_files.append(file_info)
|
220
|
-
print(f"
|
220
|
+
PrettyOutput.print(f"文件处理完成: {file_name}", OutputType.SUCCESS)
|
221
221
|
else:
|
222
|
-
print(f"
|
222
|
+
PrettyOutput.print(f"文件解析失败: {file_name}", OutputType.ERROR)
|
223
223
|
return False
|
224
224
|
else:
|
225
225
|
uploaded_files.append(file_info)
|
226
|
-
print(f"
|
226
|
+
PrettyOutput.print(f"图片处理完成: {file_name}", OutputType.SUCCESS)
|
227
227
|
else:
|
228
|
-
print(f"
|
228
|
+
PrettyOutput.print(f"文件上传失败: {file_name}", OutputType.ERROR)
|
229
229
|
return False
|
230
230
|
|
231
231
|
except Exception as e:
|
232
|
-
print(f"
|
232
|
+
PrettyOutput.print(f"处理文件出错 {file_path}: {str(e)}", OutputType.ERROR)
|
233
233
|
return False
|
234
234
|
|
235
235
|
self.uploaded_files = uploaded_files
|
jarvis/jarvis_platform/tongyi.py
CHANGED
@@ -276,16 +276,16 @@ class TongyiPlatform(BasePlatform):
|
|
276
276
|
|
277
277
|
for file_path in file_list:
|
278
278
|
file_name = os.path.basename(file_path)
|
279
|
-
print(f"
|
279
|
+
PrettyOutput.print(f"上传文件 {file_name}", OutputType.INFO)
|
280
280
|
try:
|
281
281
|
if not os.path.exists(file_path):
|
282
|
-
print(f"
|
282
|
+
PrettyOutput.print(f"文件不存在: {file_path}", OutputType.ERROR)
|
283
283
|
return False
|
284
284
|
|
285
285
|
# Get file name and content type
|
286
286
|
content_type = self._get_content_type(file_path)
|
287
287
|
|
288
|
-
print(f"
|
288
|
+
PrettyOutput.print(f"准备上传文件: {file_name}", OutputType.INFO)
|
289
289
|
|
290
290
|
# Prepare form data
|
291
291
|
form_data = {
|
@@ -300,7 +300,7 @@ class TongyiPlatform(BasePlatform):
|
|
300
300
|
# Prepare files
|
301
301
|
files = {"file": (file_name, open(file_path, "rb"), content_type)}
|
302
302
|
|
303
|
-
print(f"
|
303
|
+
PrettyOutput.print(f"正在上传文件: {file_name}", OutputType.INFO)
|
304
304
|
|
305
305
|
# Upload file
|
306
306
|
response = http.post(
|
@@ -308,7 +308,7 @@ class TongyiPlatform(BasePlatform):
|
|
308
308
|
)
|
309
309
|
|
310
310
|
if response.status_code != 200:
|
311
|
-
print(f"
|
311
|
+
PrettyOutput.print(f"上传失败 {file_name}: HTTP {response.status_code}", OutputType.ERROR)
|
312
312
|
return False
|
313
313
|
|
314
314
|
# Determine file type based on extension
|
@@ -323,7 +323,7 @@ class TongyiPlatform(BasePlatform):
|
|
323
323
|
}
|
324
324
|
)
|
325
325
|
|
326
|
-
print(f"
|
326
|
+
PrettyOutput.print(f"获取下载链接: {file_name}", OutputType.INFO)
|
327
327
|
|
328
328
|
# Get download links for uploaded files
|
329
329
|
url = "https://api.tongyi.com/dialog/downloadLink/batch"
|
@@ -340,18 +340,18 @@ class TongyiPlatform(BasePlatform):
|
|
340
340
|
|
341
341
|
response = http.post(url, headers=headers, json=payload)
|
342
342
|
if response.status_code != 200:
|
343
|
-
print(f"
|
343
|
+
PrettyOutput.print(f"获取下载链接失败: HTTP {response.status_code}", OutputType.ERROR)
|
344
344
|
return False
|
345
345
|
|
346
346
|
result = response.json()
|
347
347
|
if not result.get("success"):
|
348
|
-
print(f"
|
348
|
+
PrettyOutput.print(f"获取下载链接失败: {result.get('errorMsg')}", OutputType.ERROR)
|
349
349
|
return False
|
350
350
|
|
351
351
|
# Add files to chat
|
352
352
|
self.uploaded_file_info = result.get("data", {}).get("results", [])
|
353
353
|
for file_info in self.uploaded_file_info:
|
354
|
-
print(f"
|
354
|
+
PrettyOutput.print(f"添加文件到对话: {file_name}", OutputType.INFO)
|
355
355
|
add_url = "https://api.tongyi.com/assistant/api/chat/file/add"
|
356
356
|
add_payload = {
|
357
357
|
"workSource": "chat",
|
@@ -374,25 +374,27 @@ class TongyiPlatform(BasePlatform):
|
|
374
374
|
add_url, headers=headers, json=add_payload
|
375
375
|
)
|
376
376
|
if add_response.status_code != 200:
|
377
|
-
print(
|
378
|
-
f"
|
377
|
+
PrettyOutput.print(
|
378
|
+
f"添加文件到对话失败: HTTP {add_response.status_code}",
|
379
|
+
OutputType.ERROR,
|
379
380
|
)
|
380
381
|
continue
|
381
382
|
|
382
383
|
add_result = add_response.json()
|
383
384
|
if not add_result.get("success"):
|
384
|
-
print(
|
385
|
-
f"
|
385
|
+
PrettyOutput.print(
|
386
|
+
f"添加文件到对话失败: {add_result.get('errorMsg')}",
|
387
|
+
OutputType.ERROR,
|
386
388
|
)
|
387
389
|
continue
|
388
390
|
|
389
391
|
file_info.update(add_result.get("data", {}))
|
390
392
|
|
391
|
-
print(f"
|
393
|
+
PrettyOutput.print(f"文件 {file_name} 上传成功", OutputType.SUCCESS)
|
392
394
|
time.sleep(1) # 短暂暂停以便用户看到成功状态
|
393
395
|
|
394
396
|
except Exception as e:
|
395
|
-
print(f"
|
397
|
+
PrettyOutput.print(f"上传文件 {file_name} 时出错: {str(e)}", OutputType.ERROR)
|
396
398
|
return False
|
397
399
|
return True
|
398
400
|
|
@@ -134,10 +134,10 @@ class YuanbaoPlatform(BasePlatform):
|
|
134
134
|
|
135
135
|
for file_path in file_list:
|
136
136
|
file_name = os.path.basename(file_path)
|
137
|
-
print(f"
|
137
|
+
PrettyOutput.print(f"上传文件 {file_name}", OutputType.INFO)
|
138
138
|
try:
|
139
139
|
# 1. Prepare the file information
|
140
|
-
print(f"
|
140
|
+
PrettyOutput.print(f"准备文件信息: {file_name}", OutputType.INFO)
|
141
141
|
file_size = os.path.getsize(file_path)
|
142
142
|
file_extension = os.path.splitext(file_path)[1].lower().lstrip(".")
|
143
143
|
|
@@ -192,21 +192,21 @@ class YuanbaoPlatform(BasePlatform):
|
|
192
192
|
file_type = "code"
|
193
193
|
|
194
194
|
# 2. Generate upload information
|
195
|
-
print(f"
|
195
|
+
PrettyOutput.print(f"获取上传信息: {file_name}", OutputType.INFO)
|
196
196
|
upload_info = self._generate_upload_info(file_name)
|
197
197
|
if not upload_info:
|
198
|
-
print(f"
|
198
|
+
PrettyOutput.print(f"无法获取文件 {file_name} 的上传信息", OutputType.ERROR)
|
199
199
|
return False
|
200
200
|
|
201
201
|
# 3. Upload the file to COS
|
202
|
-
print(f"
|
202
|
+
PrettyOutput.print(f"上传文件到云存储: {file_name}", OutputType.INFO)
|
203
203
|
upload_success = self._upload_file_to_cos(file_path, upload_info)
|
204
204
|
if not upload_success:
|
205
|
-
print(f"
|
205
|
+
PrettyOutput.print(f"上传文件 {file_name} 失败", OutputType.ERROR)
|
206
206
|
return False
|
207
207
|
|
208
208
|
# 4. Create file metadata for chat
|
209
|
-
print(f"
|
209
|
+
PrettyOutput.print(f"生成文件元数据: {file_name}", OutputType.INFO)
|
210
210
|
file_metadata = {
|
211
211
|
"type": file_type,
|
212
212
|
"docType": file_extension if file_extension else file_type,
|
@@ -224,14 +224,14 @@ class YuanbaoPlatform(BasePlatform):
|
|
224
224
|
file_metadata["width"] = img.width
|
225
225
|
file_metadata["height"] = img.height
|
226
226
|
except Exception as e:
|
227
|
-
print(f"
|
227
|
+
PrettyOutput.print(f"无法获取图片 {file_name} 的尺寸: {str(e)}", OutputType.WARNING)
|
228
228
|
|
229
229
|
uploaded_files.append(file_metadata)
|
230
|
-
print(f"
|
230
|
+
PrettyOutput.print(f"文件 {file_name} 上传成功", OutputType.SUCCESS)
|
231
231
|
time.sleep(3) # 上传成功后等待3秒
|
232
232
|
|
233
233
|
except Exception as e:
|
234
|
-
print(f"
|
234
|
+
PrettyOutput.print(f"上传文件 {file_path} 时出错: {str(e)}", OutputType.ERROR)
|
235
235
|
return False
|
236
236
|
|
237
237
|
self.multimedia = uploaded_files
|
@@ -302,7 +302,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
302
302
|
with open(file_path, "rb") as file:
|
303
303
|
file_content = file.read()
|
304
304
|
|
305
|
-
print(f"
|
305
|
+
PrettyOutput.print(f"上传文件大小: {len(file_content)}", OutputType.INFO)
|
306
306
|
|
307
307
|
# Prepare headers for PUT request
|
308
308
|
host = f"{upload_info['bucketName']}.{upload_info.get('accelerateDomain', 'cos.accelerate.myqcloud.com')}"
|
@@ -108,7 +108,7 @@ def start_service(
|
|
108
108
|
OutputType.INFO,
|
109
109
|
)
|
110
110
|
|
111
|
-
|
111
|
+
|
112
112
|
|
113
113
|
# Platform and model cache
|
114
114
|
platform_instances: Dict[str, Any] = {}
|
@@ -178,7 +178,7 @@ def start_service(
|
|
178
178
|
}
|
179
179
|
)
|
180
180
|
except Exception as exc:
|
181
|
-
print(f"Error getting models for {default_platform}: {str(exc)}")
|
181
|
+
PrettyOutput.print(f"Error getting models for {default_platform}: {str(exc)}", OutputType.ERROR)
|
182
182
|
|
183
183
|
# Return model list
|
184
184
|
return {"object": "list", "data": model_list}
|
jarvis/jarvis_rag/cli.py
CHANGED
@@ -20,6 +20,7 @@ from jarvis.jarvis_utils.config import (
|
|
20
20
|
get_rag_use_bm25,
|
21
21
|
get_rag_use_rerank,
|
22
22
|
)
|
23
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
23
24
|
|
24
25
|
|
25
26
|
def is_likely_text_file(file_path: Path) -> bool:
|
@@ -70,7 +71,10 @@ class _CustomPlatformLLM(LLMInterface):
|
|
70
71
|
|
71
72
|
def __init__(self, platform: BasePlatform):
|
72
73
|
self.platform = platform
|
73
|
-
print(
|
74
|
+
PrettyOutput.print(
|
75
|
+
f"使用自定义LLM: 平台='{platform.platform_name()}', 模型='{platform.name()}'",
|
76
|
+
OutputType.INFO,
|
77
|
+
)
|
74
78
|
|
75
79
|
def generate(self, prompt: str, **kwargs) -> str:
|
76
80
|
return self.platform.chat_until_success(prompt)
|
@@ -84,13 +88,13 @@ def _create_custom_llm(platform_name: str, model_name: str) -> Optional[LLMInter
|
|
84
88
|
registry = PlatformRegistry.get_global_platform_registry()
|
85
89
|
platform_instance = registry.create_platform(platform_name)
|
86
90
|
if not platform_instance:
|
87
|
-
print(f"
|
91
|
+
PrettyOutput.print(f"错误: 平台 '{platform_name}' 未找到。", OutputType.ERROR)
|
88
92
|
return None
|
89
93
|
platform_instance.set_model_name(model_name)
|
90
94
|
platform_instance.set_suppress_output(True)
|
91
95
|
return _CustomPlatformLLM(platform_instance)
|
92
96
|
except Exception as e:
|
93
|
-
print(f"
|
97
|
+
PrettyOutput.print(f"创建自定义LLM时出错: {e}", OutputType.ERROR)
|
94
98
|
return None
|
95
99
|
|
96
100
|
|
@@ -114,10 +118,10 @@ def _load_ragignore_spec() -> Tuple[Optional[pathspec.PathSpec], Optional[Path]]
|
|
114
118
|
with open(ignore_file_to_use, "r", encoding="utf-8") as f:
|
115
119
|
patterns = f.read().splitlines()
|
116
120
|
spec = pathspec.PathSpec.from_lines("gitwildmatch", patterns)
|
117
|
-
print(f"
|
121
|
+
PrettyOutput.print(f"加载忽略规则: {ignore_file_to_use}", OutputType.SUCCESS)
|
118
122
|
return spec, project_root_path
|
119
123
|
except Exception as e:
|
120
|
-
print(f"
|
124
|
+
PrettyOutput.print(f"加载 {ignore_file_to_use.name} 文件失败: {e}", OutputType.WARNING)
|
121
125
|
|
122
126
|
return None, None
|
123
127
|
|
@@ -166,7 +170,7 @@ def add_documents(
|
|
166
170
|
continue
|
167
171
|
|
168
172
|
if path.is_dir():
|
169
|
-
print(f"
|
173
|
+
PrettyOutput.print(f"正在扫描目录: {path}", OutputType.INFO)
|
170
174
|
for item in path.rglob("*"):
|
171
175
|
if item.is_file() and is_likely_text_file(item):
|
172
176
|
files_to_process.add(item)
|
@@ -174,10 +178,10 @@ def add_documents(
|
|
174
178
|
if is_likely_text_file(path):
|
175
179
|
files_to_process.add(path)
|
176
180
|
else:
|
177
|
-
print(f"
|
181
|
+
PrettyOutput.print(f"跳过可能的二进制文件: {path}", OutputType.WARNING)
|
178
182
|
|
179
183
|
if not files_to_process:
|
180
|
-
print("
|
184
|
+
PrettyOutput.print("在指定路径中未找到任何文本文件。", OutputType.WARNING)
|
181
185
|
return
|
182
186
|
|
183
187
|
# 使用 .ragignore 过滤文件
|
@@ -198,14 +202,14 @@ def add_documents(
|
|
198
202
|
|
199
203
|
ignored_count = initial_count - len(retained_files)
|
200
204
|
if ignored_count > 0:
|
201
|
-
print(f"
|
205
|
+
PrettyOutput.print(f"根据 .ragignore 规则过滤掉 {ignored_count} 个文件。", OutputType.INFO)
|
202
206
|
files_to_process = retained_files
|
203
207
|
|
204
208
|
if not files_to_process:
|
205
|
-
print("
|
209
|
+
PrettyOutput.print("所有找到的文本文件都被忽略规则过滤掉了。", OutputType.WARNING)
|
206
210
|
return
|
207
211
|
|
208
|
-
print(f"
|
212
|
+
PrettyOutput.print(f"发现 {len(files_to_process)} 个独立文件待处理。", OutputType.INFO)
|
209
213
|
|
210
214
|
try:
|
211
215
|
pipeline = JarvisRAGPipeline(
|
@@ -229,26 +233,26 @@ def add_documents(
|
|
229
233
|
loader = TextLoader(str(file_path), encoding="utf-8")
|
230
234
|
|
231
235
|
docs_batch.extend(loader.load())
|
232
|
-
print(f"
|
236
|
+
PrettyOutput.print(f"已加载: {file_path} (文件 {i + 1}/{total_files})", OutputType.INFO)
|
233
237
|
except Exception as e:
|
234
|
-
print(f"
|
238
|
+
PrettyOutput.print(f"加载失败 {file_path}: {e}", OutputType.WARNING)
|
235
239
|
|
236
240
|
# 当批处理已满或是最后一个文件时处理批处理
|
237
241
|
if docs_batch and (len(docs_batch) >= batch_size or (i + 1) == total_files):
|
238
|
-
print(f"
|
242
|
+
PrettyOutput.print(f"正在处理批次,包含 {len(docs_batch)} 个文档...", OutputType.INFO)
|
239
243
|
pipeline.add_documents(docs_batch)
|
240
244
|
total_docs_added += len(docs_batch)
|
241
|
-
print(f"
|
245
|
+
PrettyOutput.print(f"成功添加 {len(docs_batch)} 个文档。", OutputType.SUCCESS)
|
242
246
|
docs_batch = [] # 清空批处理
|
243
247
|
|
244
248
|
if total_docs_added == 0:
|
245
|
-
print("
|
249
|
+
PrettyOutput.print("未能成功加载任何文档。", OutputType.ERROR)
|
246
250
|
raise typer.Exit(code=1)
|
247
251
|
|
248
|
-
print(f"
|
252
|
+
PrettyOutput.print(f"成功将 {total_docs_added} 个文档的内容添加至集合 '{collection_name}'。", OutputType.SUCCESS)
|
249
253
|
|
250
254
|
except Exception as e:
|
251
|
-
print(f"
|
255
|
+
PrettyOutput.print(f"发生严重错误: {e}", OutputType.ERROR)
|
252
256
|
raise typer.Exit(code=1)
|
253
257
|
|
254
258
|
|
@@ -273,7 +277,7 @@ def list_documents(
|
|
273
277
|
results = collection.get() # 获取集合中的所有项目
|
274
278
|
|
275
279
|
if not results or not results["metadatas"]:
|
276
|
-
print("
|
280
|
+
PrettyOutput.print("知识库中没有找到任何文档。", OutputType.INFO)
|
277
281
|
return
|
278
282
|
|
279
283
|
# 从元数据中提取唯一的源文件路径
|
@@ -285,15 +289,15 @@ def list_documents(
|
|
285
289
|
sources.add(source)
|
286
290
|
|
287
291
|
if not sources:
|
288
|
-
print("
|
292
|
+
PrettyOutput.print("知识库中没有找到任何带有源信息的文档。", OutputType.INFO)
|
289
293
|
return
|
290
294
|
|
291
|
-
print(f"
|
295
|
+
PrettyOutput.print(f"知识库 '{collection_name}' 中共有 {len(sources)} 个独立文档:", OutputType.INFO)
|
292
296
|
for i, source in enumerate(sorted(list(sources)), 1):
|
293
|
-
print(f" {i}. {source}")
|
297
|
+
PrettyOutput.print(f" {i}. {source}", OutputType.INFO)
|
294
298
|
|
295
299
|
except Exception as e:
|
296
|
-
print(f"
|
300
|
+
PrettyOutput.print(f"发生错误: {e}", OutputType.ERROR)
|
297
301
|
raise typer.Exit(code=1)
|
298
302
|
|
299
303
|
|
@@ -330,14 +334,14 @@ def retrieve(
|
|
330
334
|
use_rerank=use_rerank,
|
331
335
|
)
|
332
336
|
|
333
|
-
print(f"
|
337
|
+
PrettyOutput.print(f"正在为问题检索文档: '{question}'", OutputType.INFO)
|
334
338
|
retrieved_docs = pipeline.retrieve_only(question, n_results=n_results)
|
335
339
|
|
336
340
|
if not retrieved_docs:
|
337
|
-
print("
|
341
|
+
PrettyOutput.print("未找到相关文档。", OutputType.INFO)
|
338
342
|
return
|
339
343
|
|
340
|
-
print(f"
|
344
|
+
PrettyOutput.print(f"成功检索到 {len(retrieved_docs)} 个文档:", OutputType.SUCCESS)
|
341
345
|
from jarvis.jarvis_utils.globals import console
|
342
346
|
|
343
347
|
for i, doc in enumerate(retrieved_docs, 1):
|
@@ -350,7 +354,7 @@ def retrieve(
|
|
350
354
|
console.print(Markdown(f"```\n{content}\n```"))
|
351
355
|
|
352
356
|
except Exception as e:
|
353
|
-
print(f"
|
357
|
+
PrettyOutput.print(f"发生错误: {e}", OutputType.ERROR)
|
354
358
|
raise typer.Exit(code=1)
|
355
359
|
|
356
360
|
|
@@ -385,7 +389,7 @@ def query(
|
|
385
389
|
):
|
386
390
|
"""查询RAG知识库并打印答案。"""
|
387
391
|
if model and not platform:
|
388
|
-
print("
|
392
|
+
PrettyOutput.print("错误: --model 需要指定 --platform。", OutputType.ERROR)
|
389
393
|
raise typer.Exit(code=1)
|
390
394
|
|
391
395
|
try:
|
@@ -407,17 +411,17 @@ def query(
|
|
407
411
|
use_rerank=use_rerank,
|
408
412
|
)
|
409
413
|
|
410
|
-
print(f"
|
414
|
+
PrettyOutput.print(f"正在查询: '{question}'", OutputType.INFO)
|
411
415
|
answer = pipeline.query(question)
|
412
416
|
|
413
|
-
print("
|
417
|
+
PrettyOutput.print("答案:", OutputType.INFO)
|
414
418
|
# 我们仍然可以使用 rich.markdown.Markdown,因为 PrettyOutput 底层使用了 rich
|
415
419
|
from jarvis.jarvis_utils.globals import console
|
416
420
|
|
417
421
|
console.print(Markdown(answer))
|
418
422
|
|
419
423
|
except Exception as e:
|
420
|
-
print(f"
|
424
|
+
PrettyOutput.print(f"发生错误: {e}", OutputType.ERROR)
|
421
425
|
raise typer.Exit(code=1)
|
422
426
|
|
423
427
|
|
@@ -432,7 +436,7 @@ except ImportError:
|
|
432
436
|
|
433
437
|
def _check_rag_dependencies():
|
434
438
|
if not _RAG_INSTALLED:
|
435
|
-
print("
|
439
|
+
PrettyOutput.print("RAG依赖项未安装。请运行 'pip install \"jarvis-ai-assistant[rag]\"' 来使用此命令。", OutputType.ERROR)
|
436
440
|
raise typer.Exit(code=1)
|
437
441
|
|
438
442
|
|