jarvis-ai-assistant 0.3.19__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 +9 -2
- jarvis/jarvis_agent/config_editor.py +1 -1
- jarvis/jarvis_agent/shell_input_handler.py +17 -2
- jarvis/jarvis_platform/ai8.py +0 -4
- jarvis/jarvis_platform/base.py +2 -2
- jarvis/jarvis_platform_manager/service.py +2 -2
- jarvis/jarvis_smart_shell/main.py +1 -1
- jarvis/jarvis_stats/cli.py +2 -1
- jarvis/jarvis_stats/stats.py +5 -5
- jarvis/jarvis_utils/clipboard.py +1 -1
- jarvis/jarvis_utils/input.py +253 -7
- jarvis/jarvis_utils/output.py +215 -129
- {jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/RECORD +19 -19
- {jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
@@ -223,6 +223,7 @@ class Agent:
|
|
223
223
|
use_analysis: Optional[bool] = None,
|
224
224
|
force_save_memory: Optional[bool] = None,
|
225
225
|
files: Optional[List[str]] = None,
|
226
|
+
confirm_callback: Optional[Callable[[str, bool], bool]] = None,
|
226
227
|
):
|
227
228
|
"""初始化Jarvis Agent实例
|
228
229
|
|
@@ -241,6 +242,7 @@ class Agent:
|
|
241
242
|
use_methodology: 是否使用方法论
|
242
243
|
use_analysis: 是否使用任务分析
|
243
244
|
force_save_memory: 是否强制保存记忆
|
245
|
+
confirm_callback: 用户确认回调函数,签名为 (tip: str, default: bool) -> bool;默认使用CLI的user_confirm
|
244
246
|
"""
|
245
247
|
# 基础属性初始化
|
246
248
|
self.files = files or []
|
@@ -254,6 +256,11 @@ class Agent:
|
|
254
256
|
self.user_data: Dict[str, Any] = {}
|
255
257
|
self.after_tool_call_cb: Optional[Callable[[Agent], None]] = None
|
256
258
|
|
259
|
+
# 用户确认回调:默认使用 CLI 的 user_confirm,可由外部注入以支持 TUI/GUI
|
260
|
+
self.user_confirm: Callable[[str, bool], bool] = (
|
261
|
+
confirm_callback or user_confirm # type: ignore[assignment]
|
262
|
+
)
|
263
|
+
|
257
264
|
# 初始化模型和会话
|
258
265
|
self._init_model(llm_type, model_group)
|
259
266
|
self._init_session()
|
@@ -811,7 +818,7 @@ class Agent:
|
|
811
818
|
return self._complete_task(auto_completed=False)
|
812
819
|
|
813
820
|
if any(handler.can_handle(current_response) for handler in self.output_handler):
|
814
|
-
if user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
|
821
|
+
if self.user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
|
815
822
|
self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n用户同意继续工具调用。"
|
816
823
|
return None # 继续执行工具调用
|
817
824
|
else:
|
@@ -905,7 +912,7 @@ class Agent:
|
|
905
912
|
f"并且存在3个以上标签重叠的记忆。\n"
|
906
913
|
f"是否立即整理记忆库以优化性能和相关性?"
|
907
914
|
)
|
908
|
-
if user_confirm(prompt,
|
915
|
+
if self.user_confirm(prompt, True):
|
909
916
|
PrettyOutput.print(
|
910
917
|
f"正在开始整理 '{scope_name}' ({memory_type}) 记忆库...",
|
911
918
|
OutputType.INFO,
|
@@ -40,7 +40,7 @@ class ConfigEditor:
|
|
40
40
|
|
41
41
|
if editor:
|
42
42
|
try:
|
43
|
-
subprocess.run([editor, str(config_file_path)], check=True)
|
43
|
+
subprocess.run([editor, str(config_file_path)], check=True, shell=(platform.system() == "Windows"))
|
44
44
|
raise typer.Exit(code=0)
|
45
45
|
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
46
46
|
PrettyOutput.print(f"Failed to open editor: {e}", OutputType.ERROR)
|
@@ -11,9 +11,24 @@ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
|
11
11
|
if len(cmdline) == 0:
|
12
12
|
return user_input, False
|
13
13
|
else:
|
14
|
-
|
14
|
+
marker = "# JARVIS-NOCONFIRM"
|
15
|
+
|
16
|
+
def _clean(line: str) -> str:
|
17
|
+
s = line[1:] # remove leading '!'
|
18
|
+
# strip no-confirm marker if present
|
19
|
+
idx = s.find(marker)
|
20
|
+
if idx != -1:
|
21
|
+
s = s[:idx]
|
22
|
+
return s.rstrip()
|
23
|
+
|
24
|
+
# Build script while stripping the no-confirm marker from each line
|
25
|
+
script = "\n".join([_clean(c) for c in cmdline])
|
15
26
|
PrettyOutput.print(script, OutputType.CODE, lang="bash")
|
16
|
-
|
27
|
+
|
28
|
+
# If any line contains the no-confirm marker, skip the pre-execution confirmation
|
29
|
+
no_confirm = any(marker in c for c in cmdline)
|
30
|
+
|
31
|
+
if no_confirm or user_confirm(f"是否要执行以上shell脚本?", default=True):
|
17
32
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
18
33
|
|
19
34
|
output = ToolRegistry().handle_tool_calls(
|
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
@@ -134,11 +134,11 @@ class BasePlatform(ABC):
|
|
134
134
|
with Live(panel, refresh_per_second=10, transient=False) as live:
|
135
135
|
for s in self.chat(message):
|
136
136
|
response += s
|
137
|
-
if is_immediate_abort() and get_interrupt():
|
138
|
-
return response
|
139
137
|
text_content.append(s, style="bright_white")
|
140
138
|
panel.subtitle = "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
|
141
139
|
live.update(panel)
|
140
|
+
if is_immediate_abort() and get_interrupt():
|
141
|
+
return response
|
142
142
|
end_time = time.time()
|
143
143
|
duration = end_time - start_time
|
144
144
|
panel.subtitle = (
|
@@ -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_stats/cli.py
CHANGED
@@ -16,6 +16,7 @@ from pathlib import Path
|
|
16
16
|
from .stats import StatsManager
|
17
17
|
from jarvis.jarvis_utils.utils import init_env
|
18
18
|
from jarvis.jarvis_utils.config import get_data_dir
|
19
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
19
20
|
|
20
21
|
app = typer.Typer(help="Jarvis 统计模块命令行工具")
|
21
22
|
console = Console()
|
@@ -291,7 +292,7 @@ def export(
|
|
291
292
|
|
292
293
|
if output == "json":
|
293
294
|
# JSON格式输出
|
294
|
-
print(json.dumps(data, indent=2, ensure_ascii=False))
|
295
|
+
PrettyOutput.print(json.dumps(data, indent=2, ensure_ascii=False), OutputType.CODE, lang="json")
|
295
296
|
else:
|
296
297
|
# CSV格式输出
|
297
298
|
records = data.get("records", [])
|
jarvis/jarvis_stats/stats.py
CHANGED
@@ -9,6 +9,7 @@ from typing import Dict, List, Optional, Union, Any
|
|
9
9
|
|
10
10
|
from jarvis.jarvis_stats.storage import StatsStorage
|
11
11
|
from jarvis.jarvis_stats.visualizer import StatsVisualizer
|
12
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
12
13
|
|
13
14
|
|
14
15
|
class StatsManager:
|
@@ -307,7 +308,6 @@ class StatsManager:
|
|
307
308
|
"""
|
308
309
|
storage = StatsManager._get_storage()
|
309
310
|
storage.delete_old_data(days_to_keep)
|
310
|
-
print(f"已清理 {days_to_keep} 天前的数据")
|
311
311
|
|
312
312
|
@staticmethod
|
313
313
|
def remove_metric(metric_name: str) -> bool:
|
@@ -440,7 +440,7 @@ class StatsManager:
|
|
440
440
|
)
|
441
441
|
|
442
442
|
if not aggregated:
|
443
|
-
print(f"没有找到指标 '{metric_name}' 的数据")
|
443
|
+
PrettyOutput.print(f"没有找到指标 '{metric_name}' 的数据", OutputType.WARNING)
|
444
444
|
return
|
445
445
|
|
446
446
|
# 获取指标信息
|
@@ -474,7 +474,7 @@ class StatsManager:
|
|
474
474
|
show_values=True,
|
475
475
|
)
|
476
476
|
|
477
|
-
print(chart)
|
477
|
+
PrettyOutput.print(chart, OutputType.CODE, lang="text")
|
478
478
|
|
479
479
|
# 显示时间范围
|
480
480
|
from rich.panel import Panel
|
@@ -550,7 +550,7 @@ class StatsManager:
|
|
550
550
|
)
|
551
551
|
|
552
552
|
if not aggregated:
|
553
|
-
print(f"没有找到指标 '{metric_name}' 的数据")
|
553
|
+
PrettyOutput.print(f"没有找到指标 '{metric_name}' 的数据", OutputType.WARNING)
|
554
554
|
return
|
555
555
|
|
556
556
|
# 获取指标信息
|
@@ -560,7 +560,7 @@ class StatsManager:
|
|
560
560
|
# 显示汇总
|
561
561
|
summary = visualizer.show_summary(aggregated, metric_name, unit, tags)
|
562
562
|
if summary: # 如果返回了内容才打印(兼容性)
|
563
|
-
print(summary)
|
563
|
+
PrettyOutput.print(summary, OutputType.INFO)
|
564
564
|
|
565
565
|
# 显示时间范围
|
566
566
|
from rich.panel import Panel
|
jarvis/jarvis_utils/clipboard.py
CHANGED
@@ -12,7 +12,7 @@ def copy_to_clipboard(text: str) -> None:
|
|
12
12
|
text: 要复制的文本
|
13
13
|
"""
|
14
14
|
PrettyOutput.print("--- 剪贴板内容开始 ---", OutputType.INFO)
|
15
|
-
|
15
|
+
print(text)
|
16
16
|
PrettyOutput.print("--- 剪贴板内容结束 ---", OutputType.INFO)
|
17
17
|
|
18
18
|
system = platform.system()
|
jarvis/jarvis_utils/input.py
CHANGED
@@ -9,7 +9,10 @@
|
|
9
9
|
- 用于输入控制的自定义键绑定
|
10
10
|
"""
|
11
11
|
import os
|
12
|
+
import sys
|
13
|
+
import base64
|
12
14
|
from typing import Iterable, List
|
15
|
+
import wcwidth
|
13
16
|
|
14
17
|
from colorama import Fore
|
15
18
|
from colorama import Style as ColoramaStyle
|
@@ -41,10 +44,63 @@ from jarvis.jarvis_utils.tag import ot
|
|
41
44
|
|
42
45
|
# Sentinel value to indicate that Ctrl+O was pressed
|
43
46
|
CTRL_O_SENTINEL = "__CTRL_O_PRESSED__"
|
47
|
+
# Sentinel prefix to indicate that Ctrl+F (fzf) inserted content should prefill next prompt
|
48
|
+
FZF_INSERT_SENTINEL_PREFIX = "__FZF_INSERT__::"
|
49
|
+
# Sentinel to request running fzf outside the prompt and then prefill next prompt
|
50
|
+
FZF_REQUEST_SENTINEL_PREFIX = "__FZF_REQUEST__::"
|
44
51
|
|
45
52
|
# Persistent hint marker for multiline input (shown only once across runs)
|
46
53
|
_MULTILINE_HINT_MARK_FILE = os.path.join(get_data_dir(), "multiline_enter_hint_shown")
|
47
54
|
|
55
|
+
def _display_width(s: str) -> int:
|
56
|
+
"""Calculate printable width of a string in terminal columns (handles wide chars)."""
|
57
|
+
try:
|
58
|
+
w = 0
|
59
|
+
for ch in s:
|
60
|
+
cw = wcwidth.wcwidth(ch)
|
61
|
+
if cw is None or cw < 0:
|
62
|
+
# Fallback for unknown width chars (e.g. emoji on some terminals)
|
63
|
+
cw = 1
|
64
|
+
w += cw
|
65
|
+
return w
|
66
|
+
except Exception:
|
67
|
+
return len(s)
|
68
|
+
|
69
|
+
def _calc_prompt_rows(prev_text: str) -> int:
|
70
|
+
"""
|
71
|
+
Estimate how many terminal rows the previous prompt occupied.
|
72
|
+
Considers prompt prefix and soft-wrapping across terminal columns.
|
73
|
+
"""
|
74
|
+
try:
|
75
|
+
cols = os.get_terminal_size().columns
|
76
|
+
except Exception:
|
77
|
+
cols = 80
|
78
|
+
prefix = "👤 > "
|
79
|
+
prefix_w = _display_width(prefix)
|
80
|
+
|
81
|
+
if prev_text is None:
|
82
|
+
return 1
|
83
|
+
|
84
|
+
lines = prev_text.splitlines()
|
85
|
+
if not lines:
|
86
|
+
lines = [""]
|
87
|
+
# If the text ends with a newline, there is a visible empty line at the end.
|
88
|
+
if prev_text.endswith("\n"):
|
89
|
+
lines.append("")
|
90
|
+
total_rows = 0
|
91
|
+
for i, line in enumerate(lines):
|
92
|
+
lw = _display_width(line)
|
93
|
+
if i == 0:
|
94
|
+
width = prefix_w + lw
|
95
|
+
else:
|
96
|
+
width = lw
|
97
|
+
rows = max(1, (width + cols - 1) // cols)
|
98
|
+
total_rows += rows
|
99
|
+
return max(1, total_rows)
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
48
104
|
|
49
105
|
def _multiline_hint_already_shown() -> bool:
|
50
106
|
"""Check if the multiline Enter hint has been shown before (persisted)."""
|
@@ -71,7 +127,7 @@ def get_single_line_input(tip: str, default: str = "") -> str:
|
|
71
127
|
"""
|
72
128
|
session: PromptSession = PromptSession(history=None)
|
73
129
|
style = PromptStyle.from_dict({"prompt": "ansicyan", "bottom-toolbar": "fg:#888888"})
|
74
|
-
prompt = FormattedText([("class:prompt", f"👤
|
130
|
+
prompt = FormattedText([("class:prompt", f"👤 > {tip}")])
|
75
131
|
return session.prompt(prompt, default=default, style=style)
|
76
132
|
|
77
133
|
|
@@ -333,7 +389,7 @@ def _show_history_and_copy():
|
|
333
389
|
break
|
334
390
|
|
335
391
|
|
336
|
-
def _get_multiline_input_internal(tip: str) -> str:
|
392
|
+
def _get_multiline_input_internal(tip: str, preset: str | None = None, preset_cursor: int | None = None) -> str:
|
337
393
|
"""
|
338
394
|
Internal function to get multiline input using prompt_toolkit.
|
339
395
|
Returns a sentinel value if Ctrl+O is pressed.
|
@@ -385,6 +441,64 @@ def _get_multiline_input_internal(tip: str) -> str:
|
|
385
441
|
"""Handle Ctrl+O by exiting the prompt and returning the sentinel value."""
|
386
442
|
event.app.exit(result=CTRL_O_SENTINEL)
|
387
443
|
|
444
|
+
@bindings.add("c-t", filter=has_focus(DEFAULT_BUFFER))
|
445
|
+
def _(event):
|
446
|
+
"""Return a shell command like '!bash' for upper input_handler to execute."""
|
447
|
+
def _gen_shell_cmd() -> str: # type: ignore
|
448
|
+
try:
|
449
|
+
import os
|
450
|
+
import shutil
|
451
|
+
|
452
|
+
if os.name == "nt":
|
453
|
+
# Prefer PowerShell if available, otherwise fallback to cmd
|
454
|
+
for name in ("pwsh", "powershell", "cmd"):
|
455
|
+
if name == "cmd" or shutil.which(name):
|
456
|
+
return f"!{name}"
|
457
|
+
else:
|
458
|
+
shell_path = os.environ.get("SHELL", "")
|
459
|
+
if shell_path:
|
460
|
+
base = os.path.basename(shell_path)
|
461
|
+
if base:
|
462
|
+
return f"!{base}"
|
463
|
+
for name in ("fish", "zsh", "bash", "sh"):
|
464
|
+
if shutil.which(name):
|
465
|
+
return f"!{name}"
|
466
|
+
return "!bash"
|
467
|
+
except Exception:
|
468
|
+
return "!bash"
|
469
|
+
|
470
|
+
# Append a special marker to indicate no-confirm execution in shell_input_handler
|
471
|
+
event.app.exit(result=_gen_shell_cmd() + " # JARVIS-NOCONFIRM")
|
472
|
+
|
473
|
+
|
474
|
+
@bindings.add("@", filter=has_focus(DEFAULT_BUFFER), eager=True)
|
475
|
+
def _(event):
|
476
|
+
"""
|
477
|
+
使用 @ 触发 fzf(当 fzf 存在);否则仅插入 @ 以启用内置补全
|
478
|
+
逻辑:
|
479
|
+
- 若检测到系统存在 fzf,则先插入 '@',随后请求外层运行 fzf 并在返回后进行替换/插入
|
480
|
+
- 若不存在 fzf 或发生异常,则直接插入 '@'
|
481
|
+
"""
|
482
|
+
try:
|
483
|
+
import shutil
|
484
|
+
buf = event.current_buffer
|
485
|
+
if shutil.which("fzf") is None:
|
486
|
+
buf.insert_text("@")
|
487
|
+
return
|
488
|
+
# 先插入 '@',以便外层根据最后一个 '@' 进行片段替换
|
489
|
+
buf.insert_text("@")
|
490
|
+
doc = buf.document
|
491
|
+
text = doc.text
|
492
|
+
cursor = doc.cursor_position
|
493
|
+
payload = f"{cursor}:{base64.b64encode(text.encode('utf-8')).decode('ascii')}"
|
494
|
+
event.app.exit(result=FZF_REQUEST_SENTINEL_PREFIX + payload)
|
495
|
+
return
|
496
|
+
except Exception:
|
497
|
+
try:
|
498
|
+
event.current_buffer.insert_text("@")
|
499
|
+
except Exception:
|
500
|
+
pass
|
501
|
+
|
388
502
|
style = PromptStyle.from_dict(
|
389
503
|
{
|
390
504
|
"prompt": "ansibrightmagenta bold",
|
@@ -414,11 +528,17 @@ def _get_multiline_input_internal(tip: str) -> str:
|
|
414
528
|
("class:bt.key", "Ctrl+O"),
|
415
529
|
("class:bt.label", " 历史复制 "),
|
416
530
|
("class:bt.sep", " • "),
|
531
|
+
("class:bt.key", "@"),
|
532
|
+
("class:bt.label", " FZF文件 "),
|
533
|
+
("class:bt.sep", " • "),
|
534
|
+
("class:bt.key", "Ctrl+T"),
|
535
|
+
("class:bt.label", " 终端(!SHELL) "),
|
536
|
+
("class:bt.sep", " • "),
|
417
537
|
("class:bt.key", "Ctrl+C/D"),
|
418
538
|
("class:bt.label", " 取消 "),
|
419
539
|
]
|
420
540
|
)
|
421
|
-
|
541
|
+
|
422
542
|
history_dir = get_data_dir()
|
423
543
|
session: PromptSession = PromptSession(
|
424
544
|
history=FileHistory(os.path.join(history_dir, "multiline_input_history")),
|
@@ -431,14 +551,26 @@ def _get_multiline_input_internal(tip: str) -> str:
|
|
431
551
|
)
|
432
552
|
|
433
553
|
# Tip is shown in bottom toolbar; avoid extra print
|
434
|
-
prompt = FormattedText([("class:prompt", "👤
|
554
|
+
prompt = FormattedText([("class:prompt", "👤 > ")])
|
555
|
+
|
556
|
+
def _pre_run():
|
557
|
+
try:
|
558
|
+
from prompt_toolkit.application.current import get_app as _ga
|
559
|
+
app = _ga()
|
560
|
+
buf = app.current_buffer
|
561
|
+
if preset is not None and preset_cursor is not None:
|
562
|
+
cp = max(0, min(len(buf.text), preset_cursor))
|
563
|
+
buf.cursor_position = cp
|
564
|
+
except Exception:
|
565
|
+
pass
|
435
566
|
|
436
567
|
try:
|
437
568
|
return session.prompt(
|
438
569
|
prompt,
|
439
570
|
style=style,
|
440
|
-
pre_run=
|
571
|
+
pre_run=_pre_run,
|
441
572
|
bottom_toolbar=_bottom_toolbar,
|
573
|
+
default=(preset or ""),
|
442
574
|
).strip()
|
443
575
|
except (KeyboardInterrupt, EOFError):
|
444
576
|
return ""
|
@@ -453,14 +585,128 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
|
|
453
585
|
tip: 提示文本,将显示在底部工具栏中
|
454
586
|
print_on_empty: 当输入为空字符串时,是否打印“输入已取消”提示。默认打印。
|
455
587
|
"""
|
588
|
+
preset: str | None = None
|
589
|
+
preset_cursor: int | None = None
|
456
590
|
while True:
|
457
|
-
user_input = _get_multiline_input_internal(tip)
|
591
|
+
user_input = _get_multiline_input_internal(tip, preset=preset, preset_cursor=preset_cursor)
|
458
592
|
|
459
593
|
if user_input == CTRL_O_SENTINEL:
|
460
594
|
_show_history_and_copy()
|
461
595
|
tip = "请继续输入(或按Ctrl+J确认):"
|
462
596
|
continue
|
597
|
+
elif isinstance(user_input, str) and user_input.startswith(FZF_REQUEST_SENTINEL_PREFIX):
|
598
|
+
# Handle fzf request outside the prompt, then prefill new text.
|
599
|
+
try:
|
600
|
+
payload = user_input[len(FZF_REQUEST_SENTINEL_PREFIX) :]
|
601
|
+
sep_index = payload.find(":")
|
602
|
+
cursor = int(payload[:sep_index])
|
603
|
+
text = base64.b64decode(payload[sep_index + 1 :].encode("ascii")).decode("utf-8")
|
604
|
+
except Exception:
|
605
|
+
# Malformed payload; just continue without change.
|
606
|
+
preset = None
|
607
|
+
tip = "FZF 预填失败,继续输入:"
|
608
|
+
continue
|
609
|
+
|
610
|
+
# Run fzf to get a file selection synchronously (outside prompt)
|
611
|
+
selected_path = ""
|
612
|
+
try:
|
613
|
+
import shutil
|
614
|
+
import subprocess
|
615
|
+
|
616
|
+
if shutil.which("fzf") is None:
|
617
|
+
PrettyOutput.print("未检测到 fzf,无法打开文件选择器。", OutputType.WARNING)
|
618
|
+
else:
|
619
|
+
files: list[str] = []
|
620
|
+
try:
|
621
|
+
r = subprocess.run(
|
622
|
+
["git", "ls-files"],
|
623
|
+
stdout=subprocess.PIPE,
|
624
|
+
stderr=subprocess.PIPE,
|
625
|
+
text=True,
|
626
|
+
)
|
627
|
+
if r.returncode == 0:
|
628
|
+
files = [line for line in r.stdout.splitlines() if line.strip()]
|
629
|
+
except Exception:
|
630
|
+
files = []
|
631
|
+
|
632
|
+
if not files:
|
633
|
+
import os as _os
|
634
|
+
for root, _, fnames in _os.walk(".", followlinks=False):
|
635
|
+
for name in fnames:
|
636
|
+
files.append(_os.path.relpath(_os.path.join(root, name), "."))
|
637
|
+
if len(files) > 10000:
|
638
|
+
break
|
639
|
+
|
640
|
+
if not files:
|
641
|
+
PrettyOutput.print("未找到可选择的文件。", OutputType.INFO)
|
642
|
+
else:
|
643
|
+
try:
|
644
|
+
specials = [ot("Summary"), ot("Clear"), ot("ToolUsage"), ot("ReloadConfig"), ot("SaveSession")]
|
645
|
+
except Exception:
|
646
|
+
specials = []
|
647
|
+
items = [s for s in specials if isinstance(s, str) and s.strip()] + files
|
648
|
+
proc = subprocess.run(
|
649
|
+
["fzf", "--prompt", "Files> ", "--height", "40%", "--border"],
|
650
|
+
input="\n".join(items),
|
651
|
+
stdout=subprocess.PIPE,
|
652
|
+
stderr=subprocess.PIPE,
|
653
|
+
text=True,
|
654
|
+
)
|
655
|
+
sel = proc.stdout.strip()
|
656
|
+
if sel:
|
657
|
+
selected_path = sel
|
658
|
+
except Exception as e:
|
659
|
+
PrettyOutput.print(f"FZF 执行失败: {e}", OutputType.ERROR)
|
660
|
+
|
661
|
+
# Compute new text based on selection (or keep original if none)
|
662
|
+
if selected_path:
|
663
|
+
text_before = text[:cursor]
|
664
|
+
last_at = text_before.rfind("@")
|
665
|
+
if last_at != -1 and " " not in text_before[last_at + 1 :]:
|
666
|
+
# Replace @... segment
|
667
|
+
inserted = f"'{selected_path}'"
|
668
|
+
new_text = text[:last_at] + inserted + text[cursor:]
|
669
|
+
new_cursor = last_at + len(inserted)
|
670
|
+
else:
|
671
|
+
# Plain insert
|
672
|
+
inserted = f"'{selected_path}'"
|
673
|
+
new_text = text[:cursor] + inserted + text[cursor:]
|
674
|
+
new_cursor = cursor + len(inserted)
|
675
|
+
preset = new_text
|
676
|
+
preset_cursor = new_cursor
|
677
|
+
tip = "已插入文件,继续编辑或按Ctrl+J确认:"
|
678
|
+
else:
|
679
|
+
# No selection; keep original text and cursor
|
680
|
+
preset = text
|
681
|
+
preset_cursor = cursor
|
682
|
+
tip = "未选择文件或已取消,继续编辑:"
|
683
|
+
# 清除上一条输入行(多行安全),避免多清,保守仅按提示行估算
|
684
|
+
try:
|
685
|
+
rows_total = _calc_prompt_rows(text)
|
686
|
+
for _ in range(rows_total):
|
687
|
+
sys.stdout.write("\x1b[1A") # 光标上移一行
|
688
|
+
sys.stdout.write("\x1b[2K\r") # 清除整行
|
689
|
+
sys.stdout.flush()
|
690
|
+
except Exception:
|
691
|
+
pass
|
692
|
+
continue
|
693
|
+
elif isinstance(user_input, str) and user_input.startswith(FZF_INSERT_SENTINEL_PREFIX):
|
694
|
+
# 从哨兵载荷中提取新文本,作为下次进入提示的预填内容
|
695
|
+
preset = user_input[len(FZF_INSERT_SENTINEL_PREFIX) :]
|
696
|
+
preset_cursor = len(preset)
|
697
|
+
|
698
|
+
# 清除上一条输入行(多行安全),避免多清,保守仅按提示行估算
|
699
|
+
try:
|
700
|
+
rows_total = _calc_prompt_rows(preset)
|
701
|
+
for _ in range(rows_total):
|
702
|
+
sys.stdout.write("\x1b[1A")
|
703
|
+
sys.stdout.write("\x1b[2K\r")
|
704
|
+
sys.stdout.flush()
|
705
|
+
except Exception:
|
706
|
+
pass
|
707
|
+
tip = "已插入文件,继续编辑或按Ctrl+J确认:"
|
708
|
+
continue
|
463
709
|
else:
|
464
710
|
if not user_input and print_on_empty:
|
465
|
-
PrettyOutput.print("
|
711
|
+
PrettyOutput.print("输入已取消", OutputType.INFO)
|
466
712
|
return user_input
|
jarvis/jarvis_utils/output.py
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
"""
|
11
11
|
from datetime import datetime
|
12
12
|
from enum import Enum
|
13
|
-
from typing import Dict, Optional, Tuple, Any
|
13
|
+
from typing import Dict, Optional, Tuple, Any, List
|
14
14
|
|
15
15
|
from pygments.lexers import guess_lexer
|
16
16
|
from pygments.util import ClassNotFound
|
@@ -22,6 +22,8 @@ from rich.text import Text
|
|
22
22
|
|
23
23
|
from jarvis.jarvis_utils.config import get_pretty_output
|
24
24
|
from jarvis.jarvis_utils.globals import console, get_agent_list
|
25
|
+
from dataclasses import dataclass
|
26
|
+
from abc import ABC, abstractmethod
|
25
27
|
|
26
28
|
|
27
29
|
class OutputType(Enum):
|
@@ -57,124 +59,59 @@ class OutputType(Enum):
|
|
57
59
|
TOOL = "TOOL"
|
58
60
|
|
59
61
|
|
60
|
-
|
62
|
+
@dataclass
|
63
|
+
class OutputEvent:
|
61
64
|
"""
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
-
|
66
|
-
-
|
67
|
-
-
|
68
|
-
-
|
65
|
+
输出事件的通用结构,供不同输出后端(Sink)消费。
|
66
|
+
- text: 文本内容
|
67
|
+
- output_type: 输出类型
|
68
|
+
- timestamp: 是否显示时间戳
|
69
|
+
- lang: 语法高亮语言(可选,不提供则自动检测)
|
70
|
+
- traceback: 是否显示异常堆栈
|
71
|
+
- section: 若为章节标题输出,填入标题文本;否则为None
|
72
|
+
- context: 额外上下文(预留给TUI/日志等)
|
69
73
|
"""
|
74
|
+
text: str
|
75
|
+
output_type: OutputType
|
76
|
+
timestamp: bool = True
|
77
|
+
lang: Optional[str] = None
|
78
|
+
traceback: bool = False
|
79
|
+
section: Optional[str] = None
|
80
|
+
context: Optional[Dict[str, Any]] = None
|
70
81
|
|
71
|
-
# 不同输出类型的图标
|
72
|
-
_ICONS = {
|
73
|
-
OutputType.SYSTEM: "🤖",
|
74
|
-
OutputType.CODE: "📝",
|
75
|
-
OutputType.RESULT: "✨",
|
76
|
-
OutputType.ERROR: "❌",
|
77
|
-
OutputType.INFO: "ℹ️",
|
78
|
-
OutputType.PLANNING: "📋",
|
79
|
-
OutputType.PROGRESS: "⏳",
|
80
|
-
OutputType.SUCCESS: "✅",
|
81
|
-
OutputType.WARNING: "⚠️",
|
82
|
-
OutputType.DEBUG: "🔍",
|
83
|
-
OutputType.USER: "👤",
|
84
|
-
OutputType.TOOL: "🔧",
|
85
|
-
}
|
86
|
-
# 语法高亮的语言映射
|
87
|
-
_lang_map = {
|
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",
|
118
|
-
}
|
119
|
-
|
120
|
-
@staticmethod
|
121
|
-
def _detect_language(text: str, default_lang: str = "markdown") -> str:
|
122
|
-
"""
|
123
|
-
检测给定文本的编程语言。
|
124
82
|
|
125
|
-
|
126
|
-
|
127
|
-
default_lang: 如果检测失败,默认返回的语言
|
83
|
+
class OutputSink(ABC):
|
84
|
+
"""输出后端抽象接口,不同前端(控制台/TUI/SSE/日志)实现该接口以消费输出事件。"""
|
128
85
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
try:
|
133
|
-
lexer = guess_lexer(text)
|
134
|
-
detected_lang = lexer.name # type: ignore[attr-defined]
|
135
|
-
return PrettyOutput._lang_map.get(detected_lang, default_lang)
|
136
|
-
except (ClassNotFound, Exception):
|
137
|
-
return default_lang
|
86
|
+
@abstractmethod
|
87
|
+
def emit(self, event: OutputEvent) -> None: # pragma: no cover - 抽象方法
|
88
|
+
raise NotImplementedError
|
138
89
|
|
139
|
-
@staticmethod
|
140
|
-
def _format(output_type: OutputType, timestamp: bool = True) -> str:
|
141
|
-
"""
|
142
|
-
使用时间戳和图标格式化输出头。
|
143
90
|
|
144
|
-
|
145
|
-
|
146
|
-
|
91
|
+
class ConsoleOutputSink(OutputSink):
|
92
|
+
"""
|
93
|
+
默认控制台输出实现,保持与原 PrettyOutput 行为一致。
|
94
|
+
"""
|
147
95
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
return formatted
|
96
|
+
def emit(self, event: OutputEvent) -> None:
|
97
|
+
# 章节输出
|
98
|
+
if event.section is not None:
|
99
|
+
text = Text(event.section, style=event.output_type.value, justify="center")
|
100
|
+
panel = Panel(text, border_style=event.output_type.value)
|
101
|
+
if get_pretty_output():
|
102
|
+
console.print(panel)
|
103
|
+
else:
|
104
|
+
console.print(text)
|
105
|
+
return
|
159
106
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
traceback: bool = False,
|
167
|
-
):
|
168
|
-
"""
|
169
|
-
使用样式和语法高亮打印格式化输出。
|
107
|
+
# 普通内容输出
|
108
|
+
lang = (
|
109
|
+
event.lang
|
110
|
+
if event.lang is not None
|
111
|
+
else PrettyOutput._detect_language(event.text, default_lang="markdown")
|
112
|
+
)
|
170
113
|
|
171
|
-
|
172
|
-
text: 要打印的文本内容
|
173
|
-
output_type: 输出类型(影响样式)
|
174
|
-
timestamp: 是否显示时间戳
|
175
|
-
lang: 语法高亮的语言
|
176
|
-
traceback: 是否显示错误的回溯信息
|
177
|
-
"""
|
114
|
+
# 与原实现保持一致的样式定义
|
178
115
|
styles: Dict[OutputType, Dict[str, Any]] = {
|
179
116
|
OutputType.SYSTEM: dict(bgcolor="#1e2b3c"),
|
180
117
|
OutputType.CODE: dict(bgcolor="#1c2b1c"),
|
@@ -255,25 +192,20 @@ class PrettyOutput:
|
|
255
192
|
),
|
256
193
|
}
|
257
194
|
|
258
|
-
lang = (
|
259
|
-
lang
|
260
|
-
if lang is not None
|
261
|
-
else PrettyOutput._detect_language(text, default_lang="markdown")
|
262
|
-
)
|
263
195
|
header = Text(
|
264
|
-
PrettyOutput._format(output_type, timestamp),
|
265
|
-
style=header_styles[output_type],
|
196
|
+
PrettyOutput._format(event.output_type, event.timestamp),
|
197
|
+
style=header_styles[event.output_type],
|
266
198
|
)
|
267
199
|
content = Syntax(
|
268
|
-
text,
|
200
|
+
event.text,
|
269
201
|
lang,
|
270
202
|
theme="monokai",
|
271
203
|
word_wrap=True,
|
272
|
-
background_color=styles[output_type]["bgcolor"],
|
204
|
+
background_color=styles[event.output_type]["bgcolor"],
|
273
205
|
)
|
274
206
|
panel = Panel(
|
275
207
|
content,
|
276
|
-
border_style=header_styles[output_type],
|
208
|
+
border_style=header_styles[event.output_type],
|
277
209
|
padding=(0, 0),
|
278
210
|
highlight=True,
|
279
211
|
)
|
@@ -281,27 +213,181 @@ class PrettyOutput:
|
|
281
213
|
console.print(panel)
|
282
214
|
else:
|
283
215
|
console.print(content)
|
284
|
-
if traceback or output_type == OutputType.ERROR:
|
216
|
+
if event.traceback or event.output_type == OutputType.ERROR:
|
285
217
|
try:
|
286
218
|
console.print_exception()
|
287
219
|
except Exception as e:
|
288
220
|
console.print(f"Error: {e}")
|
289
221
|
|
222
|
+
|
223
|
+
# 模块级输出分发器(默认注册控制台后端)
|
224
|
+
_output_sinks: List[OutputSink] = [ConsoleOutputSink()]
|
225
|
+
|
226
|
+
|
227
|
+
def emit_output(event: OutputEvent) -> None:
|
228
|
+
"""向所有已注册的输出后端广播事件。"""
|
229
|
+
for sink in list(_output_sinks):
|
230
|
+
try:
|
231
|
+
sink.emit(event)
|
232
|
+
except Exception as e:
|
233
|
+
# 后端故障不影响其他后端
|
234
|
+
console.print(f"[输出后端错误] {sink.__class__.__name__}: {e}")
|
235
|
+
|
236
|
+
|
237
|
+
class PrettyOutput:
|
238
|
+
"""
|
239
|
+
使用rich库格式化和显示富文本输出的类。
|
240
|
+
|
241
|
+
提供以下方法:
|
242
|
+
- 使用适当的样式格式化不同类型的输出
|
243
|
+
- 代码块的语法高亮
|
244
|
+
- 结构化内容的面板显示
|
245
|
+
- 渐进显示的流式输出
|
246
|
+
"""
|
247
|
+
|
248
|
+
# 不同输出类型的图标
|
249
|
+
_ICONS = {
|
250
|
+
OutputType.SYSTEM: "🤖",
|
251
|
+
OutputType.CODE: "📝",
|
252
|
+
OutputType.RESULT: "✨",
|
253
|
+
OutputType.ERROR: "❌",
|
254
|
+
OutputType.INFO: "ℹ️",
|
255
|
+
OutputType.PLANNING: "📋",
|
256
|
+
OutputType.PROGRESS: "⏳",
|
257
|
+
OutputType.SUCCESS: "✅",
|
258
|
+
OutputType.WARNING: "⚠️",
|
259
|
+
OutputType.DEBUG: "🔍",
|
260
|
+
OutputType.USER: "👤",
|
261
|
+
OutputType.TOOL: "🔧",
|
262
|
+
}
|
263
|
+
# 语法高亮的语言映射
|
264
|
+
_lang_map = {
|
265
|
+
"Python": "python",
|
266
|
+
"JavaScript": "javascript",
|
267
|
+
"TypeScript": "typescript",
|
268
|
+
"Java": "java",
|
269
|
+
"C++": "cpp",
|
270
|
+
"C#": "csharp",
|
271
|
+
"Ruby": "ruby",
|
272
|
+
"PHP": "php",
|
273
|
+
"Go": "go",
|
274
|
+
"Rust": "rust",
|
275
|
+
"Bash": "bash",
|
276
|
+
"HTML": "html",
|
277
|
+
"CSS": "css",
|
278
|
+
"SQL": "sql",
|
279
|
+
"R": "r",
|
280
|
+
"Kotlin": "kotlin",
|
281
|
+
"Swift": "swift",
|
282
|
+
"Scala": "scala",
|
283
|
+
"Perl": "perl",
|
284
|
+
"Lua": "lua",
|
285
|
+
"YAML": "yaml",
|
286
|
+
"JSON": "json",
|
287
|
+
"XML": "xml",
|
288
|
+
"Markdown": "markdown",
|
289
|
+
"Text": "text",
|
290
|
+
"Shell": "bash",
|
291
|
+
"Dockerfile": "dockerfile",
|
292
|
+
"Makefile": "makefile",
|
293
|
+
"INI": "ini",
|
294
|
+
"TOML": "toml",
|
295
|
+
}
|
296
|
+
|
290
297
|
@staticmethod
|
291
|
-
def
|
298
|
+
def _detect_language(text: str, default_lang: str = "markdown") -> str:
|
299
|
+
"""
|
300
|
+
检测给定文本的编程语言。
|
301
|
+
|
302
|
+
参数:
|
303
|
+
text: 要分析的文本
|
304
|
+
default_lang: 如果检测失败,默认返回的语言
|
305
|
+
|
306
|
+
返回:
|
307
|
+
str: 检测到的语言名称
|
308
|
+
"""
|
309
|
+
try:
|
310
|
+
lexer = guess_lexer(text)
|
311
|
+
detected_lang = lexer.name # type: ignore[attr-defined]
|
312
|
+
return PrettyOutput._lang_map.get(detected_lang, default_lang)
|
313
|
+
except (ClassNotFound, Exception):
|
314
|
+
return default_lang
|
315
|
+
|
316
|
+
@staticmethod
|
317
|
+
def _format(output_type: OutputType, timestamp: bool = True) -> str:
|
292
318
|
"""
|
293
|
-
|
319
|
+
使用时间戳和图标格式化输出头。
|
294
320
|
|
295
321
|
参数:
|
296
|
-
|
297
|
-
|
322
|
+
output_type: 输出类型
|
323
|
+
timestamp: 是否包含时间戳
|
324
|
+
|
325
|
+
返回:
|
326
|
+
Text: 格式化后的rich Text对象
|
298
327
|
"""
|
299
|
-
|
300
|
-
|
301
|
-
if
|
302
|
-
|
328
|
+
icon = PrettyOutput._ICONS.get(output_type, "")
|
329
|
+
formatted = f"{icon} "
|
330
|
+
if timestamp:
|
331
|
+
formatted += f"[{datetime.now().strftime('%H:%M:%S')}][{output_type.value}]"
|
332
|
+
agent_info = get_agent_list()
|
333
|
+
if agent_info:
|
334
|
+
formatted += f"[{agent_info}]"
|
335
|
+
return formatted
|
336
|
+
|
337
|
+
@staticmethod
|
338
|
+
def print(
|
339
|
+
text: str,
|
340
|
+
output_type: OutputType,
|
341
|
+
timestamp: bool = True,
|
342
|
+
lang: Optional[str] = None,
|
343
|
+
traceback: bool = False,
|
344
|
+
):
|
345
|
+
"""
|
346
|
+
使用样式和语法高亮打印格式化输出(已抽象为事件 + Sink 机制)。
|
347
|
+
保持对现有调用方的向后兼容,同时为TUI/日志等前端预留扩展点。
|
348
|
+
"""
|
349
|
+
event = OutputEvent(
|
350
|
+
text=text,
|
351
|
+
output_type=output_type,
|
352
|
+
timestamp=timestamp,
|
353
|
+
lang=lang,
|
354
|
+
traceback=traceback,
|
355
|
+
)
|
356
|
+
emit_output(event)
|
357
|
+
|
358
|
+
@staticmethod
|
359
|
+
def section(title: str, output_type: OutputType = OutputType.INFO):
|
360
|
+
"""
|
361
|
+
在样式化面板中打印章节标题(通过事件 + Sink 机制分发)。
|
362
|
+
"""
|
363
|
+
event = OutputEvent(
|
364
|
+
text="",
|
365
|
+
output_type=output_type,
|
366
|
+
section=title,
|
367
|
+
)
|
368
|
+
emit_output(event)
|
369
|
+
|
370
|
+
@staticmethod
|
371
|
+
# Sink管理(为外部注册自定义后端预留)
|
372
|
+
@staticmethod
|
373
|
+
def add_sink(sink: OutputSink) -> None:
|
374
|
+
"""注册一个新的输出后端。"""
|
375
|
+
_output_sinks.append(sink)
|
376
|
+
|
377
|
+
@staticmethod
|
378
|
+
def clear_sinks(keep_default: bool = True) -> None:
|
379
|
+
"""清空已注册的输出后端;可选择保留默认控制台后端。"""
|
380
|
+
if keep_default:
|
381
|
+
globals()["_output_sinks"] = [
|
382
|
+
s for s in _output_sinks if isinstance(s, ConsoleOutputSink)
|
383
|
+
]
|
303
384
|
else:
|
304
|
-
|
385
|
+
_output_sinks.clear()
|
386
|
+
|
387
|
+
@staticmethod
|
388
|
+
def get_sinks() -> List[OutputSink]:
|
389
|
+
"""获取当前已注册的输出后端列表(副本)。"""
|
390
|
+
return list(_output_sinks)
|
305
391
|
|
306
392
|
@staticmethod
|
307
393
|
def print_gradient_text(
|
@@ -1,8 +1,8 @@
|
|
1
|
-
jarvis/__init__.py,sha256=
|
2
|
-
jarvis/jarvis_agent/__init__.py,sha256=
|
1
|
+
jarvis/__init__.py,sha256=N7QVf6q_SvBscQmf9AFdoKn3bxAilYPXJ-mx-Vjn6aQ,74
|
2
|
+
jarvis/jarvis_agent/__init__.py,sha256=3_CKwWoN-eq9JMviyDrhgeSlt3o9oMBP1DTYFOT-g_U,33655
|
3
3
|
jarvis/jarvis_agent/agent_manager.py,sha256=YzpMiF0H2-eyk2kn2o24Bkj3bXsQx7Pv2vfD4gWepo0,2893
|
4
4
|
jarvis/jarvis_agent/builtin_input_handler.py,sha256=wS-FqpT3pIXwHn1dfL3SpXonUKWgVThbQueUIeyRc2U,2917
|
5
|
-
jarvis/jarvis_agent/config_editor.py,sha256=
|
5
|
+
jarvis/jarvis_agent/config_editor.py,sha256=q4t_LNqiPPVYKcbE7N1cXSqObCK6wBNemLvAYc1y05Y,1849
|
6
6
|
jarvis/jarvis_agent/edit_file_handler.py,sha256=bkvCghB_X-CQKuIG7dJfos8F1KNnHSLNhldraVgqVk8,11209
|
7
7
|
jarvis/jarvis_agent/file_methodology_manager.py,sha256=PwDUQwq7HVIyPInsN8fgWyMXLwi8heIXPrqfBZJhVHs,4260
|
8
8
|
jarvis/jarvis_agent/jarvis.py,sha256=J_R27hZUTGattEAKppkeCoklMJoEFHjAycsCMRjYA7o,19340
|
@@ -15,7 +15,7 @@ jarvis/jarvis_agent/prompts.py,sha256=X6cXa-n0xqBQ8LDTgLsD0kqziAh1s0cNp89i4mxcvH
|
|
15
15
|
jarvis/jarvis_agent/protocols.py,sha256=JWnJDikFEuwvFUv7uzXu0ggJ4O9K2FkMnfVCwIJ5REw,873
|
16
16
|
jarvis/jarvis_agent/session_manager.py,sha256=5wVcaZGwJ9cEKTQglSbqyxUDJ2fI5KxYN8C8L16UWLw,3024
|
17
17
|
jarvis/jarvis_agent/share_manager.py,sha256=wFcdULSog1mMxDyB94ofbqitFL8DCX8i1u6qVzSEuAk,8704
|
18
|
-
jarvis/jarvis_agent/shell_input_handler.py,sha256=
|
18
|
+
jarvis/jarvis_agent/shell_input_handler.py,sha256=YjzmtcBUqJDYeD1i1cA1betUKaGOcvKNo8biayYEfc8,1860
|
19
19
|
jarvis/jarvis_agent/task_analyzer.py,sha256=W9Pm2AB0kNhbFos3Qh6tpe5gA-x8e566IhIKvJkQJmg,4667
|
20
20
|
jarvis/jarvis_agent/task_manager.py,sha256=hP2PF_mgmmATD3h5HHDdkU_m3_LBOK1eZGZ3gh-9Kh8,4851
|
21
21
|
jarvis/jarvis_agent/tool_executor.py,sha256=gyVahM_d4hzYxiYJD209tVSbXO8SpKi1pohEDmyAmnc,1768
|
@@ -59,8 +59,8 @@ jarvis/jarvis_methodology/main.py,sha256=DtfgvuFdJl7IoccoyTSdZdnwOd2ghBmN55Gu7eZ
|
|
59
59
|
jarvis/jarvis_multi_agent/__init__.py,sha256=kCgtAX7VvliyEOQxIj2DvNjRAuh6bpNaOtDn60nzph4,6089
|
60
60
|
jarvis/jarvis_multi_agent/main.py,sha256=b9IThFMeUZCYSlgT-VT8r7xeBdrEE_zNT11awEc8IdY,1853
|
61
61
|
jarvis/jarvis_platform/__init__.py,sha256=WLQHSiE87PPket2M50_hHzjdMIgPIBx2VF8JfB_NNRk,105
|
62
|
-
jarvis/jarvis_platform/ai8.py,sha256=
|
63
|
-
jarvis/jarvis_platform/base.py,sha256=
|
62
|
+
jarvis/jarvis_platform/ai8.py,sha256=g8JkqPGs9SEbqstNMCc5rCHO0QcPHX9LNvb7HMWwB-Q,11471
|
63
|
+
jarvis/jarvis_platform/base.py,sha256=x-DAfYz-ZxdcqrNmCU-Awfe8R3p6CUP28vUH3GVmLXQ,9959
|
64
64
|
jarvis/jarvis_platform/human.py,sha256=jWjW8prEag79e6ddqTPV4nz_Gz6zFBfO4a1EbvP8QWA,4908
|
65
65
|
jarvis/jarvis_platform/kimi.py,sha256=k7CVJlTPDTPTBqWwbxZa3HJXGhQtDNf3zv-Lu9sONiw,15520
|
66
66
|
jarvis/jarvis_platform/openai.py,sha256=0YSeDGHRSPQP2haEzFARx_aZH_d_UZ-HSCsJLh2hW5k,8037
|
@@ -69,7 +69,7 @@ jarvis/jarvis_platform/tongyi.py,sha256=fw7PvB3FKGbztF29RrY4-YKSXp1-5TMy8RYw-g56
|
|
69
69
|
jarvis/jarvis_platform/yuanbao.py,sha256=mn7jxrab2CeVRtcwgQxGUhz90RRb9qK1iKz4rmFskOI,23858
|
70
70
|
jarvis/jarvis_platform_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
71
|
jarvis/jarvis_platform_manager/main.py,sha256=_G64sTDNyCcag82UhPZzrlS9t1__Qe_GBR8d16dhQV0,20909
|
72
|
-
jarvis/jarvis_platform_manager/service.py,sha256=
|
72
|
+
jarvis/jarvis_platform_manager/service.py,sha256=lxC9G6o_psIUjvxXVMAmhL-TGe3A6cJ5zCat6sB9NXo,14842
|
73
73
|
jarvis/jarvis_rag/__init__.py,sha256=HRTXgnQxDuaE9x-e3r6SYqhJ5d4DSI_rrIxy2IGY6qk,320
|
74
74
|
jarvis/jarvis_rag/cache.py,sha256=Tqx_Oe-AhuWlMXHGHUaIuG6OEHoHBVZq7mL3kldtFFU,2723
|
75
75
|
jarvis/jarvis_rag/cli.py,sha256=w79ZVVtNKbmNoYuDDzhprbxLZsDGv5hGe7uwk0ktB6k,16553
|
@@ -80,10 +80,10 @@ jarvis/jarvis_rag/rag_pipeline.py,sha256=f0ktQIfNyBY4kanfttyGz_sZWB9DMhXPzeognEe
|
|
80
80
|
jarvis/jarvis_rag/reranker.py,sha256=Uzn4n1bNj4kWyQu9-z-jK_5dAU6drn5jEugML-kFHg8,1885
|
81
81
|
jarvis/jarvis_rag/retriever.py,sha256=1nbw5m15drvYGbocLfGHO2FgXPSDKmT1k2unNk1IrXw,8067
|
82
82
|
jarvis/jarvis_smart_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
83
|
-
jarvis/jarvis_smart_shell/main.py,sha256=
|
83
|
+
jarvis/jarvis_smart_shell/main.py,sha256=XFiqB7GdVn2-mr9G7GLLrHeo3iAUyDGVD1w5tIB9xJQ,14644
|
84
84
|
jarvis/jarvis_stats/__init__.py,sha256=jJzgP43nxzLbNGs8Do4Jfta1PNCJMf1Oq9YTPd6EnFM,342
|
85
|
-
jarvis/jarvis_stats/cli.py,sha256=
|
86
|
-
jarvis/jarvis_stats/stats.py,sha256=
|
85
|
+
jarvis/jarvis_stats/cli.py,sha256=pPwS_v3jW_xbRzaegiwIYngzDJp7iE9mS_bOmIybHd0,12756
|
86
|
+
jarvis/jarvis_stats/stats.py,sha256=IpBdVaTFg4DowrVVUIv1OnN26IwTUE5yHzpMLUDaju0,19337
|
87
87
|
jarvis/jarvis_stats/storage.py,sha256=WvABIbYZLOSHDQZkM4X-cZyFMi7rlbMskFMXqbhFxQk,21697
|
88
88
|
jarvis/jarvis_stats/visualizer.py,sha256=ZIBmGELzs6c7qM01tQql1HF6eFKn6HDGVQfKXRUUIY0,8529
|
89
89
|
jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -107,21 +107,21 @@ jarvis/jarvis_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
107
107
|
jarvis/jarvis_tools/cli/main.py,sha256=H_Rdp7WMiPDxuUha_lsWds6PAAwy-2j0IhLlgmjP2Ro,8435
|
108
108
|
jarvis/jarvis_utils/__init__.py,sha256=67h0ldisGlh3oK4DAeNEL2Bl_VsI3tSmfclasyVlueM,850
|
109
109
|
jarvis/jarvis_utils/builtin_replace_map.py,sha256=4BurljGuiG_I93EBs7mlFlPm9wYC_4CmdTG5tQWpF6g,1712
|
110
|
-
jarvis/jarvis_utils/clipboard.py,sha256=
|
110
|
+
jarvis/jarvis_utils/clipboard.py,sha256=D3wzQeqg_yiH7Axs4d6MRxyNa9XxdnenH-ND2uj2WVQ,2967
|
111
111
|
jarvis/jarvis_utils/config.py,sha256=my9u8QL-PhByAumthP4oJq2NtH_Wc6wd0DnUguAQUWk,17580
|
112
112
|
jarvis/jarvis_utils/embedding.py,sha256=oEOEM2qf16DMYwPsQe6srET9BknyjOdY2ef0jsp3Or8,2714
|
113
113
|
jarvis/jarvis_utils/file_processors.py,sha256=XiM248SHS7lLgQDCbORVFWqinbVDUawYxWDOsLXDxP8,3043
|
114
114
|
jarvis/jarvis_utils/git_utils.py,sha256=AkczUiRcGcOnPfz2v3mdLwV1S41IopiAYD2tjeMTDrE,23586
|
115
115
|
jarvis/jarvis_utils/globals.py,sha256=aTrOHcCgPAeZFLFIWMAMiJCYlmr4XhdFZf5gZ745hnE,8900
|
116
116
|
jarvis/jarvis_utils/http.py,sha256=eRhV3-GYuWmQ0ogq9di9WMlQkFcVb1zGCrySnOgT1x0,4392
|
117
|
-
jarvis/jarvis_utils/input.py,sha256=
|
117
|
+
jarvis/jarvis_utils/input.py,sha256=9pqaCrZBzmU542msJ41xc69AGydw8goT7oqkrPLH0Mg,26608
|
118
118
|
jarvis/jarvis_utils/methodology.py,sha256=ypd2hraZWhzOfQ-bjfQprtcB27HBbpdcPW-NAfSAzd0,12640
|
119
|
-
jarvis/jarvis_utils/output.py,sha256=
|
119
|
+
jarvis/jarvis_utils/output.py,sha256=rJ_uOSUNjwUTj9SKrwQLb6JOb9Wb8ODoGMiHvrzvUfQ,13550
|
120
120
|
jarvis/jarvis_utils/tag.py,sha256=f211opbbbTcSyzCDwuIK_oCnKhXPNK-RknYyGzY1yD0,431
|
121
121
|
jarvis/jarvis_utils/utils.py,sha256=iU1DdQvSCjedOgnExLMxSAjcZkSqmV5MAdu2t2RRSjw,51341
|
122
|
-
jarvis_ai_assistant-0.3.
|
123
|
-
jarvis_ai_assistant-0.3.
|
124
|
-
jarvis_ai_assistant-0.3.
|
125
|
-
jarvis_ai_assistant-0.3.
|
126
|
-
jarvis_ai_assistant-0.3.
|
127
|
-
jarvis_ai_assistant-0.3.
|
122
|
+
jarvis_ai_assistant-0.3.20.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
|
123
|
+
jarvis_ai_assistant-0.3.20.dist-info/METADATA,sha256=Gboa0F2vb0jbopnQ-k224VQHmBVOjyooqx6aFCM_DW8,18216
|
124
|
+
jarvis_ai_assistant-0.3.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
125
|
+
jarvis_ai_assistant-0.3.20.dist-info/entry_points.txt,sha256=4GcWKFxRJD-QU14gw_3ZaW4KuEVxOcZK9i270rwPdjA,1395
|
126
|
+
jarvis_ai_assistant-0.3.20.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
|
127
|
+
jarvis_ai_assistant-0.3.20.dist-info/RECORD,,
|
File without changes
|
{jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/entry_points.txt
RENAMED
File without changes
|
{jarvis_ai_assistant-0.3.19.dist-info → jarvis_ai_assistant-0.3.20.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
File without changes
|