jarvis-ai-assistant 0.3.28__py3-none-any.whl → 0.3.30__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 +154 -32
- jarvis/jarvis_agent/event_bus.py +2 -2
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_methodology_manager.py +17 -4
- jarvis/jarvis_agent/jarvis.py +3 -3
- jarvis/jarvis_agent/memory_manager.py +4 -3
- jarvis/jarvis_agent/prompts.py +2 -2
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/run_loop.py +10 -27
- jarvis/jarvis_agent/shell_input_handler.py +6 -1
- jarvis/jarvis_agent/task_analyzer.py +18 -13
- jarvis/jarvis_agent/task_manager.py +6 -4
- jarvis/jarvis_agent/utils.py +50 -0
- jarvis/jarvis_code_agent/code_agent.py +3 -2
- jarvis/jarvis_data/config_schema.json +8 -0
- jarvis/jarvis_mcp/sse_mcp_client.py +1 -1
- jarvis/jarvis_memory_organizer/memory_organizer.py +4 -4
- jarvis/jarvis_platform/kimi.py +1 -1
- jarvis/jarvis_platform/tongyi.py +1 -1
- jarvis/jarvis_platform/yuanbao.py +1 -1
- jarvis/jarvis_rag/retriever.py +3 -3
- jarvis/jarvis_stats/stats.py +2 -2
- jarvis/jarvis_stats/storage.py +3 -3
- jarvis/jarvis_tools/edit_file.py +3 -3
- jarvis/jarvis_tools/execute_script.py +2 -2
- jarvis/jarvis_tools/generate_new_tool.py +13 -2
- jarvis/jarvis_tools/sub_agent.py +2 -2
- jarvis/jarvis_tools/sub_code_agent.py +2 -2
- jarvis/jarvis_utils/config.py +17 -3
- jarvis/jarvis_utils/fzf.py +4 -3
- jarvis/jarvis_utils/git_utils.py +20 -16
- jarvis/jarvis_utils/input.py +5 -5
- jarvis/jarvis_utils/utils.py +109 -20
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.30.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.30.dist-info}/RECORD +40 -38
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.30.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.30.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.30.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.30.dist-info}/top_level.txt +0 -0
jarvis/jarvis_agent/protocols.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
from typing import Any, Protocol, Tuple
|
2
|
+
from typing import Any, Protocol, Tuple, runtime_checkable
|
3
3
|
|
4
4
|
|
5
|
+
@runtime_checkable
|
5
6
|
class OutputHandlerProtocol(Protocol):
|
6
7
|
"""
|
7
8
|
Defines the interface for an output handler, which is responsible for
|
@@ -28,3 +29,5 @@ class OutputHandlerProtocol(Protocol):
|
|
28
29
|
A tuple containing a boolean (whether to return) and the result.
|
29
30
|
"""
|
30
31
|
...
|
32
|
+
|
33
|
+
__all__ = ["OutputHandlerProtocol"]
|
jarvis/jarvis_agent/run_loop.py
CHANGED
@@ -11,7 +11,8 @@ from enum import Enum
|
|
11
11
|
from typing import Any, TYPE_CHECKING
|
12
12
|
|
13
13
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
14
|
-
from jarvis.
|
14
|
+
from jarvis.jarvis_agent.events import BEFORE_TOOL_CALL, AFTER_TOOL_CALL
|
15
|
+
from jarvis.jarvis_agent.utils import join_prompts, is_auto_complete, normalize_next_action
|
15
16
|
|
16
17
|
if TYPE_CHECKING:
|
17
18
|
# 仅用于类型标注,避免运行时循环依赖
|
@@ -63,7 +64,7 @@ class AgentRunLoop:
|
|
63
64
|
# 广播工具调用前事件(不影响主流程)
|
64
65
|
try:
|
65
66
|
ag.event_bus.emit(
|
66
|
-
|
67
|
+
BEFORE_TOOL_CALL,
|
67
68
|
agent=ag,
|
68
69
|
current_response=current_response,
|
69
70
|
)
|
@@ -72,23 +73,16 @@ class AgentRunLoop:
|
|
72
73
|
need_return, tool_prompt = ag._call_tools(current_response)
|
73
74
|
|
74
75
|
# 将上一个提示和工具提示安全地拼接起来
|
75
|
-
|
76
|
-
if ag.session.prompt:
|
77
|
-
prompt_parts.append(ag.session.prompt)
|
78
|
-
if tool_prompt:
|
79
|
-
prompt_parts.append(tool_prompt)
|
80
|
-
ag.session.prompt = "\n\n".join(prompt_parts)
|
76
|
+
ag.session.prompt = join_prompts([ag.session.prompt, tool_prompt])
|
81
77
|
|
82
78
|
if need_return:
|
83
79
|
return ag.session.prompt
|
84
80
|
|
85
|
-
|
86
|
-
if ag.after_tool_call_cb:
|
87
|
-
ag.after_tool_call_cb(ag)
|
81
|
+
|
88
82
|
# 广播工具调用后的事件(不影响主流程)
|
89
83
|
try:
|
90
84
|
ag.event_bus.emit(
|
91
|
-
|
85
|
+
AFTER_TOOL_CALL,
|
92
86
|
agent=ag,
|
93
87
|
current_response=current_response,
|
94
88
|
need_return=need_return,
|
@@ -102,27 +96,16 @@ class AgentRunLoop:
|
|
102
96
|
continue
|
103
97
|
|
104
98
|
# 检查自动完成
|
105
|
-
if ag.auto_complete and
|
99
|
+
if ag.auto_complete and is_auto_complete(current_response):
|
106
100
|
return ag._complete_task(auto_completed=True)
|
107
101
|
|
108
102
|
# 获取下一步用户输入
|
109
103
|
next_action = ag._get_next_user_action()
|
110
|
-
|
111
|
-
|
112
|
-
or (
|
113
|
-
isinstance(next_action, Enum)
|
114
|
-
and getattr(next_action, "value", None) == "continue"
|
115
|
-
)
|
116
|
-
):
|
104
|
+
action = normalize_next_action(next_action)
|
105
|
+
if action == "continue":
|
117
106
|
run_input_handlers = True
|
118
107
|
continue
|
119
|
-
elif
|
120
|
-
next_action == "complete"
|
121
|
-
or (
|
122
|
-
isinstance(next_action, Enum)
|
123
|
-
and getattr(next_action, "value", None) == "complete"
|
124
|
-
)
|
125
|
-
):
|
108
|
+
elif action == "complete":
|
126
109
|
return ag._complete_task(auto_completed=False)
|
127
110
|
|
128
111
|
except Exception as e:
|
@@ -3,6 +3,7 @@ from typing import Any, Tuple
|
|
3
3
|
|
4
4
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
5
5
|
from jarvis.jarvis_utils.input import user_confirm
|
6
|
+
from jarvis.jarvis_agent.utils import join_prompts
|
6
7
|
|
7
8
|
|
8
9
|
def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
@@ -41,7 +42,11 @@ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
|
41
42
|
)
|
42
43
|
if user_confirm("是否将执行结果反馈给Agent?", default=True):
|
43
44
|
return (
|
44
|
-
|
45
|
+
join_prompts([
|
46
|
+
user_input,
|
47
|
+
f"用户执行以下脚本:\n{script}",
|
48
|
+
f"执行结果:\n{output}",
|
49
|
+
]),
|
45
50
|
False,
|
46
51
|
)
|
47
52
|
return "", True
|
@@ -8,6 +8,8 @@ from jarvis.jarvis_utils.globals import get_interrupt, set_interrupt
|
|
8
8
|
|
9
9
|
from jarvis.jarvis_agent.prompts import TASK_ANALYSIS_PROMPT
|
10
10
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
11
|
+
from jarvis.jarvis_agent.utils import join_prompts
|
12
|
+
from jarvis.jarvis_agent.events import BEFORE_TOOL_CALL, AFTER_TOOL_CALL, BEFORE_SUMMARY, TASK_COMPLETED
|
11
13
|
|
12
14
|
|
13
15
|
class TaskAnalyzer:
|
@@ -55,10 +57,7 @@ class TaskAnalyzer:
|
|
55
57
|
|
56
58
|
def _prepare_analysis_prompt(self, satisfaction_feedback: str) -> str:
|
57
59
|
"""准备分析提示"""
|
58
|
-
|
59
|
-
if satisfaction_feedback:
|
60
|
-
analysis_prompt += satisfaction_feedback
|
61
|
-
return analysis_prompt
|
60
|
+
return join_prompts([TASK_ANALYSIS_PROMPT, satisfaction_feedback])
|
62
61
|
|
63
62
|
def _process_analysis_loop(self):
|
64
63
|
"""处理分析循环"""
|
@@ -74,7 +73,7 @@ class TaskAnalyzer:
|
|
74
73
|
# 执行工具调用(补充事件:before_tool_call/after_tool_call)
|
75
74
|
try:
|
76
75
|
self.agent.event_bus.emit(
|
77
|
-
|
76
|
+
BEFORE_TOOL_CALL,
|
78
77
|
agent=self.agent,
|
79
78
|
current_response=response,
|
80
79
|
)
|
@@ -84,7 +83,7 @@ class TaskAnalyzer:
|
|
84
83
|
self.agent.session.prompt = tool_prompt
|
85
84
|
try:
|
86
85
|
self.agent.event_bus.emit(
|
87
|
-
|
86
|
+
AFTER_TOOL_CALL,
|
88
87
|
agent=self.agent,
|
89
88
|
current_response=response,
|
90
89
|
need_return=need_return,
|
@@ -130,9 +129,15 @@ class TaskAnalyzer:
|
|
130
129
|
def _handle_interrupt_with_tool_calls(self, user_input: str) -> str:
|
131
130
|
"""处理有工具调用时的中断"""
|
132
131
|
if self.agent.user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
|
133
|
-
return
|
132
|
+
return join_prompts([
|
133
|
+
f"被用户中断,用户补充信息为:{user_input}",
|
134
|
+
"用户同意继续工具调用。"
|
135
|
+
])
|
134
136
|
else:
|
135
|
-
return
|
137
|
+
return join_prompts([
|
138
|
+
f"被用户中断,用户补充信息为:{user_input}",
|
139
|
+
"检测到有工具调用,但被用户拒绝执行。请根据用户的补充信息重新考虑下一步操作。"
|
140
|
+
])
|
136
141
|
|
137
142
|
def collect_satisfaction_feedback(self, auto_completed: bool) -> str:
|
138
143
|
"""收集满意度反馈"""
|
@@ -140,18 +145,18 @@ class TaskAnalyzer:
|
|
140
145
|
|
141
146
|
if not auto_completed and self.agent.use_analysis:
|
142
147
|
if self.agent.user_confirm("您对本次任务的完成是否满意?", True):
|
143
|
-
satisfaction_feedback = "
|
148
|
+
satisfaction_feedback = "用户对本次任务的完成表示满意。"
|
144
149
|
else:
|
145
150
|
feedback = self.agent._multiline_input(
|
146
151
|
"请提供您的反馈意见(可留空直接回车):", False
|
147
152
|
)
|
148
153
|
if feedback:
|
149
154
|
satisfaction_feedback = (
|
150
|
-
f"
|
155
|
+
f"用户对本次任务的完成不满意,反馈意见如下:\n{feedback}"
|
151
156
|
)
|
152
157
|
else:
|
153
158
|
satisfaction_feedback = (
|
154
|
-
"
|
159
|
+
"用户对本次任务的完成不满意,未提供具体反馈意见。"
|
155
160
|
)
|
156
161
|
|
157
162
|
return satisfaction_feedback
|
@@ -162,9 +167,9 @@ class TaskAnalyzer:
|
|
162
167
|
def _subscribe_events(self) -> None:
|
163
168
|
bus = self.agent.get_event_bus() # type: ignore[attr-defined]
|
164
169
|
# 在生成总结前触发(保持与原顺序一致)
|
165
|
-
bus.subscribe(
|
170
|
+
bus.subscribe(BEFORE_SUMMARY, self._on_before_summary)
|
166
171
|
# 当无需总结时,作为兜底触发分析
|
167
|
-
bus.subscribe(
|
172
|
+
bus.subscribe(TASK_COMPLETED, self._on_task_completed)
|
168
173
|
|
169
174
|
def _on_before_summary(self, **payload) -> None:
|
170
175
|
if self._analysis_done:
|
@@ -14,6 +14,7 @@ from jarvis.jarvis_agent import (
|
|
14
14
|
get_multiline_input,
|
15
15
|
user_confirm,
|
16
16
|
)
|
17
|
+
from jarvis.jarvis_agent.utils import join_prompts
|
17
18
|
from jarvis.jarvis_utils.config import get_data_dir
|
18
19
|
from jarvis.jarvis_utils.fzf import fzf_select
|
19
20
|
|
@@ -109,7 +110,7 @@ class TaskManager:
|
|
109
110
|
if need_additional:
|
110
111
|
additional_input = get_multiline_input("请输入补充信息:")
|
111
112
|
if additional_input:
|
112
|
-
selected_task = f"
|
113
|
+
selected_task = join_prompts([selected_task, f"补充信息:\n{additional_input}"])
|
113
114
|
return selected_task
|
114
115
|
except Exception:
|
115
116
|
# 如果解析失败,则回退到手动输入
|
@@ -138,9 +139,10 @@ class TaskManager:
|
|
138
139
|
if need_additional:
|
139
140
|
additional_input = get_multiline_input("请输入补充信息:")
|
140
141
|
if additional_input:
|
141
|
-
selected_task = (
|
142
|
-
|
143
|
-
|
142
|
+
selected_task = join_prompts([
|
143
|
+
selected_task,
|
144
|
+
f"补充信息:\n{additional_input}"
|
145
|
+
])
|
144
146
|
return selected_task
|
145
147
|
PrettyOutput.print(
|
146
148
|
"无效的选择。请选择列表中的一个号码。", OutputType.WARNING
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
工具函数(jarvis_agent.utils)
|
4
|
+
|
5
|
+
- join_prompts: 统一的提示拼接策略(仅拼接非空段落,使用双换行)
|
6
|
+
- is_auto_complete: 统一的自动完成标记检测
|
7
|
+
"""
|
8
|
+
from typing import Iterable, List, Any
|
9
|
+
from enum import Enum
|
10
|
+
from jarvis.jarvis_utils.tag import ot
|
11
|
+
|
12
|
+
def join_prompts(parts: Iterable[str]) -> str:
|
13
|
+
"""
|
14
|
+
将多个提示片段按统一规则拼接:
|
15
|
+
- 过滤掉空字符串
|
16
|
+
- 使用两个换行分隔
|
17
|
+
- 不进行额外 strip,保持调用方原样语义
|
18
|
+
"""
|
19
|
+
non_empty: List[str] = [p for p in parts if p]
|
20
|
+
return "\n\n".join(non_empty)
|
21
|
+
|
22
|
+
def is_auto_complete(response: str) -> bool:
|
23
|
+
"""
|
24
|
+
检测是否包含自动完成标记。
|
25
|
+
当前实现:包含 ot('!!!COMPLETE!!!') 即视为自动完成。
|
26
|
+
"""
|
27
|
+
try:
|
28
|
+
return ot("!!!COMPLETE!!!") in response
|
29
|
+
except Exception:
|
30
|
+
# 防御性处理:即使 ot 出现异常,也不阻塞主流程
|
31
|
+
return "!!!COMPLETE!!!" in response
|
32
|
+
|
33
|
+
def normalize_next_action(next_action: Any) -> str:
|
34
|
+
"""
|
35
|
+
规范化下一步动作为字符串:
|
36
|
+
- 如果是 Enum, 返回其 value(若为字符串)
|
37
|
+
- 如果是 str, 原样返回
|
38
|
+
- 其他情况返回空字符串
|
39
|
+
"""
|
40
|
+
try:
|
41
|
+
if isinstance(next_action, Enum):
|
42
|
+
value = getattr(next_action, "value", None)
|
43
|
+
return value if isinstance(value, str) else ""
|
44
|
+
if isinstance(next_action, str):
|
45
|
+
return next_action
|
46
|
+
return ""
|
47
|
+
except Exception:
|
48
|
+
return ""
|
49
|
+
|
50
|
+
__all__ = ["join_prompts", "is_auto_complete", "normalize_next_action"]
|
@@ -12,6 +12,7 @@ from typing import List, Optional, Tuple
|
|
12
12
|
import typer
|
13
13
|
|
14
14
|
from jarvis.jarvis_agent import Agent
|
15
|
+
from jarvis.jarvis_agent.events import AFTER_TOOL_CALL
|
15
16
|
from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
|
16
17
|
from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
|
17
18
|
from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
|
@@ -94,7 +95,7 @@ class CodeAgent:
|
|
94
95
|
use_analysis=False, # 禁用分析
|
95
96
|
)
|
96
97
|
|
97
|
-
self.agent.
|
98
|
+
self.agent.event_bus.subscribe(AFTER_TOOL_CALL, self._on_after_tool_call)
|
98
99
|
|
99
100
|
def _get_system_prompt(self) -> str:
|
100
101
|
"""获取代码工程师的系统提示词"""
|
@@ -598,7 +599,7 @@ class CodeAgent:
|
|
598
599
|
except RuntimeError as e:
|
599
600
|
return f"Error during execution: {str(e)}"
|
600
601
|
|
601
|
-
def
|
602
|
+
def _on_after_tool_call(self, agent: Agent, current_response=None, need_return=None, tool_prompt=None, **kwargs) -> None:
|
602
603
|
"""工具调用后回调函数。"""
|
603
604
|
final_ret = ""
|
604
605
|
diff = get_diff()
|
@@ -247,6 +247,14 @@
|
|
247
247
|
},
|
248
248
|
"default": []
|
249
249
|
},
|
250
|
+
"JARVIS_AFTER_TOOL_CALL_CB_DIRS": {
|
251
|
+
"type": "array",
|
252
|
+
"description": "工具调用后回调函数实现目录",
|
253
|
+
"items": {
|
254
|
+
"type": "string"
|
255
|
+
},
|
256
|
+
"default": []
|
257
|
+
},
|
250
258
|
"JARVIS_CENTRAL_METHODOLOGY_REPO": {
|
251
259
|
"type": "string",
|
252
260
|
"description": "中心方法论Git仓库地址,该仓库会自动添加到方法论加载路径中",
|
@@ -63,7 +63,7 @@ class MemoryOrganizer:
|
|
63
63
|
"""加载指定类型的所有记忆"""
|
64
64
|
memories = []
|
65
65
|
memory_files = self._get_memory_files(memory_type)
|
66
|
-
error_lines:
|
66
|
+
error_lines: List[str] = []
|
67
67
|
|
68
68
|
for memory_file in memory_files:
|
69
69
|
try:
|
@@ -388,8 +388,8 @@ tags:
|
|
388
388
|
)
|
389
389
|
|
390
390
|
# 删除原始记忆文件(先汇总日志,最后统一打印)
|
391
|
-
info_lines:
|
392
|
-
warn_lines:
|
391
|
+
info_lines: List[str] = []
|
392
|
+
warn_lines: List[str] = []
|
393
393
|
for orig_memory in original_memories:
|
394
394
|
if "file_path" in orig_memory:
|
395
395
|
try:
|
@@ -428,7 +428,7 @@ tags:
|
|
428
428
|
导出的记忆数量
|
429
429
|
"""
|
430
430
|
all_memories = []
|
431
|
-
progress_lines:
|
431
|
+
progress_lines: List[str] = []
|
432
432
|
|
433
433
|
for memory_type in memory_types:
|
434
434
|
progress_lines.append(f"正在导出 {memory_type} 类型的记忆...")
|
jarvis/jarvis_platform/kimi.py
CHANGED
@@ -194,7 +194,7 @@ class KimiModel(BasePlatform):
|
|
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
|
-
log_lines:
|
197
|
+
log_lines: List[str] = [f"处理文件 [{index}/{len(file_list)}]: {file_name}"]
|
198
198
|
try:
|
199
199
|
mime_type, _ = mimetypes.guess_type(file_path)
|
200
200
|
action = (
|
jarvis/jarvis_platform/tongyi.py
CHANGED
@@ -276,7 +276,7 @@ class TongyiPlatform(BasePlatform):
|
|
276
276
|
|
277
277
|
for file_path in file_list:
|
278
278
|
file_name = os.path.basename(file_path)
|
279
|
-
log_lines:
|
279
|
+
log_lines: List[str] = []
|
280
280
|
log_lines.append(f"上传文件 {file_name}")
|
281
281
|
try:
|
282
282
|
if not os.path.exists(file_path):
|
@@ -133,7 +133,7 @@ class YuanbaoPlatform(BasePlatform):
|
|
133
133
|
|
134
134
|
for file_path in file_list:
|
135
135
|
file_name = os.path.basename(file_path)
|
136
|
-
log_lines:
|
136
|
+
log_lines: List[str] = []
|
137
137
|
log_lines.append(f"上传文件 {file_name}")
|
138
138
|
try:
|
139
139
|
# 1. Prepare the file information
|
jarvis/jarvis_rag/retriever.py
CHANGED
@@ -185,7 +185,7 @@ class ChromaRetriever:
|
|
185
185
|
if not changed and not deleted:
|
186
186
|
return
|
187
187
|
# 为避免在循环中逐条打印,先拼接后统一打印
|
188
|
-
lines:
|
188
|
+
lines: List[str] = []
|
189
189
|
if changed:
|
190
190
|
lines.append(
|
191
191
|
f"检测到 {len(changed)} 个已索引文件发生变化,建议重新索引以保证检索准确性。"
|
@@ -247,7 +247,7 @@ class ChromaRetriever:
|
|
247
247
|
return
|
248
248
|
|
249
249
|
# 先处理删除
|
250
|
-
delete_errors:
|
250
|
+
delete_errors: List[str] = []
|
251
251
|
for src in deleted:
|
252
252
|
try:
|
253
253
|
self.collection.delete(where={"source": src}) # type: ignore[arg-type]
|
@@ -258,7 +258,7 @@ class ChromaRetriever:
|
|
258
258
|
|
259
259
|
# 再处理变更(重建)
|
260
260
|
docs_to_add: List[Document] = []
|
261
|
-
rebuild_errors:
|
261
|
+
rebuild_errors: List[str] = []
|
262
262
|
for src in changed:
|
263
263
|
try:
|
264
264
|
# 删除旧条目
|
jarvis/jarvis_stats/stats.py
CHANGED
@@ -604,8 +604,8 @@ class StatsManager:
|
|
604
604
|
|
605
605
|
@staticmethod
|
606
606
|
def _show_multiple_charts(
|
607
|
-
start_time: datetime,
|
608
|
-
end_time: datetime,
|
607
|
+
start_time: Optional[datetime],
|
608
|
+
end_time: Optional[datetime],
|
609
609
|
aggregation: str,
|
610
610
|
tags: Optional[Dict[str, str]],
|
611
611
|
width: Optional[int] = None,
|
jarvis/jarvis_stats/storage.py
CHANGED
@@ -8,7 +8,7 @@ import json
|
|
8
8
|
import os
|
9
9
|
from datetime import datetime, timedelta
|
10
10
|
from pathlib import Path
|
11
|
-
from typing import Dict, List, Optional, Any
|
11
|
+
from typing import Dict, List, Optional, Any, Set
|
12
12
|
from collections import defaultdict
|
13
13
|
import sys
|
14
14
|
import time
|
@@ -457,7 +457,7 @@ class StatsStorage:
|
|
457
457
|
metrics_from_meta = set(meta.get("metrics", {}).keys())
|
458
458
|
|
459
459
|
# 扫描所有数据文件获取实际存在的指标
|
460
|
-
metrics_from_data:
|
460
|
+
metrics_from_data: Set[str] = set()
|
461
461
|
for data_file in self.data_dir.glob("stats_*.json"):
|
462
462
|
try:
|
463
463
|
data = self._load_json(data_file)
|
@@ -467,7 +467,7 @@ class StatsStorage:
|
|
467
467
|
continue
|
468
468
|
|
469
469
|
# 扫描总量缓存目录中已有的指标文件
|
470
|
-
metrics_from_totals:
|
470
|
+
metrics_from_totals: Set[str] = set()
|
471
471
|
try:
|
472
472
|
for f in self.totals_dir.glob("*"):
|
473
473
|
if f.is_file():
|
jarvis/jarvis_tools/edit_file.py
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
- 完善的错误处理和回滚机制
|
15
15
|
- 严格的格式保持要求
|
16
16
|
"""
|
17
|
-
from typing import Any, Dict
|
17
|
+
from typing import Any, Dict, List
|
18
18
|
|
19
19
|
from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
|
20
20
|
|
@@ -122,7 +122,7 @@ class FileSearchReplaceTool:
|
|
122
122
|
|
123
123
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
124
124
|
|
125
|
-
stdout_messages:
|
125
|
+
stdout_messages: List[str] = []
|
126
126
|
overall_success = False
|
127
127
|
file_results = []
|
128
128
|
|
@@ -167,7 +167,7 @@ class FileSearchReplaceTool:
|
|
167
167
|
)
|
168
168
|
|
169
169
|
# 整合所有错误信息到stderr
|
170
|
-
all_stderr:
|
170
|
+
all_stderr: List[str] = []
|
171
171
|
for file_result in file_results:
|
172
172
|
if not file_result["success"]:
|
173
173
|
all_stderr.append(f"文件 {file_result['file']} 处理失败: {file_result['stderr']}")
|
@@ -2,7 +2,7 @@
|
|
2
2
|
import os
|
3
3
|
import tempfile
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import Any, Dict
|
5
|
+
from typing import Any, Dict, List
|
6
6
|
|
7
7
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
8
8
|
|
@@ -74,7 +74,7 @@ class ScriptTool:
|
|
74
74
|
stream.feed(data)
|
75
75
|
|
76
76
|
# 清理每行右侧空格,并过滤空行
|
77
|
-
cleaned:
|
77
|
+
cleaned: List[str] = []
|
78
78
|
for y in range(screen.lines):
|
79
79
|
line = screen.buffer[y]
|
80
80
|
stripped = "".join(char.data for char in line.values()).rstrip()
|
@@ -173,9 +173,20 @@ class generate_new_tool:
|
|
173
173
|
__import__(pkg)
|
174
174
|
except ImportError:
|
175
175
|
|
176
|
-
import subprocess
|
176
|
+
import subprocess, sys, os
|
177
|
+
from shutil import which as _which
|
178
|
+
# 优先使用 uv 安装(先查 venv 内 uv,再查 PATH 中 uv),否则回退到 python -m pip
|
179
|
+
if sys.platform == "win32":
|
180
|
+
venv_uv = os.path.join(sys.prefix, "Scripts", "uv.exe")
|
181
|
+
else:
|
182
|
+
venv_uv = os.path.join(sys.prefix, "bin", "uv")
|
183
|
+
uv_executable = venv_uv if os.path.exists(venv_uv) else (_which("uv") or None)
|
184
|
+
if uv_executable:
|
185
|
+
install_cmd = [uv_executable, "pip", "install", pkg]
|
186
|
+
else:
|
187
|
+
install_cmd = [sys.executable, "-m", "pip", "install", pkg]
|
188
|
+
subprocess.run(install_cmd, check=True)
|
177
189
|
|
178
|
-
subprocess.run(["pip", "install", pkg], check=True)
|
179
190
|
|
180
191
|
except Exception as e:
|
181
192
|
PrettyOutput.print(f"依赖检查/安装失败: {str(e)}", OutputType.WARNING)
|
jarvis/jarvis_tools/sub_agent.py
CHANGED
@@ -8,7 +8,7 @@ sub_agent 工具
|
|
8
8
|
- 继承父 Agent 的部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer;其他参数需显式提供
|
9
9
|
- 子Agent必须自动完成(auto_complete=True)且需要summary(need_summary=True)
|
10
10
|
"""
|
11
|
-
from typing import Any, Dict
|
11
|
+
from typing import Any, Dict, List
|
12
12
|
import json
|
13
13
|
|
14
14
|
from jarvis.jarvis_agent import Agent
|
@@ -107,7 +107,7 @@ class SubAgentTool:
|
|
107
107
|
|
108
108
|
# 解析可用工具列表(支持数组或以逗号分隔的字符串)
|
109
109
|
_use_tools = args.get("use_tools", None)
|
110
|
-
use_tools:
|
110
|
+
use_tools: List[str] = []
|
111
111
|
if isinstance(_use_tools, list):
|
112
112
|
use_tools = [str(x).strip() for x in _use_tools if str(x).strip()]
|
113
113
|
elif isinstance(_use_tools, str):
|
@@ -8,7 +8,7 @@ sub_code_agent 工具
|
|
8
8
|
- 不依赖父 Agent,所有配置使用系统默认与全局变量
|
9
9
|
- 子Agent必须自动完成(auto_complete=True)且需要summary(need_summary=True)
|
10
10
|
"""
|
11
|
-
from typing import Any, Dict
|
11
|
+
from typing import Any, Dict, List
|
12
12
|
import json
|
13
13
|
|
14
14
|
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
@@ -85,7 +85,7 @@ class SubCodeAgentTool:
|
|
85
85
|
except Exception:
|
86
86
|
parent_agent = None
|
87
87
|
model_group = None
|
88
|
-
use_tools:
|
88
|
+
use_tools: List[str] = []
|
89
89
|
try:
|
90
90
|
if parent_agent is not None:
|
91
91
|
if getattr(parent_agent, "model", None):
|
jarvis/jarvis_utils/config.py
CHANGED
@@ -285,7 +285,7 @@ def get_pretty_output() -> bool:
|
|
285
285
|
if platform.system() == "Windows":
|
286
286
|
return False
|
287
287
|
|
288
|
-
return GLOBAL_CONFIG_DATA.get("JARVIS_PRETTY_OUTPUT",
|
288
|
+
return GLOBAL_CONFIG_DATA.get("JARVIS_PRETTY_OUTPUT", True)
|
289
289
|
|
290
290
|
|
291
291
|
def is_use_methodology() -> bool:
|
@@ -378,6 +378,20 @@ def get_roles_dirs() -> List[str]:
|
|
378
378
|
]
|
379
379
|
|
380
380
|
|
381
|
+
def get_after_tool_call_cb_dirs() -> List[str]:
|
382
|
+
"""
|
383
|
+
获取工具调用后回调函数实现目录。
|
384
|
+
|
385
|
+
返回:
|
386
|
+
List[str]: 工具调用后回调函数实现目录列表
|
387
|
+
"""
|
388
|
+
return [
|
389
|
+
os.path.expanduser(os.path.expandvars(str(p)))
|
390
|
+
for p in GLOBAL_CONFIG_DATA.get("JARVIS_AFTER_TOOL_CALL_CB_DIRS", [])
|
391
|
+
if p
|
392
|
+
]
|
393
|
+
|
394
|
+
|
381
395
|
def get_central_methodology_repo() -> str:
|
382
396
|
"""
|
383
397
|
获取中心方法论Git仓库地址。
|
@@ -423,9 +437,9 @@ def is_force_save_memory() -> bool:
|
|
423
437
|
获取是否强制保存记忆。
|
424
438
|
|
425
439
|
返回:
|
426
|
-
bool: 如果强制保存记忆则返回True,默认为
|
440
|
+
bool: 如果强制保存记忆则返回True,默认为False
|
427
441
|
"""
|
428
|
-
return GLOBAL_CONFIG_DATA.get("JARVIS_FORCE_SAVE_MEMORY",
|
442
|
+
return GLOBAL_CONFIG_DATA.get("JARVIS_FORCE_SAVE_MEMORY", False) is True
|
429
443
|
|
430
444
|
|
431
445
|
def is_enable_static_analysis() -> bool:
|
jarvis/jarvis_utils/fzf.py
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
"""FZF selector utility."""
|
3
3
|
import shutil
|
4
4
|
import subprocess
|
5
|
-
from typing import List, Optional, Union
|
5
|
+
from typing import List, Optional, Union, Dict, Any, cast
|
6
6
|
|
7
7
|
def fzf_select(
|
8
|
-
options: Union[List[str], List[
|
8
|
+
options: Union[List[str], List[Dict[str, Any]]],
|
9
9
|
prompt: str = "SELECT> ",
|
10
10
|
key: Optional[str] = None,
|
11
11
|
) -> Optional[str]:
|
@@ -29,7 +29,8 @@ def fzf_select(
|
|
29
29
|
if isinstance(options[0], dict):
|
30
30
|
if key is None:
|
31
31
|
raise ValueError("A key must be provided for a list of dicts.")
|
32
|
-
|
32
|
+
options_dict = cast(List[Dict[str, Any]], options)
|
33
|
+
input_lines = [str(item.get(key, "")) for item in options_dict]
|
33
34
|
else:
|
34
35
|
input_lines = [str(item) for item in options]
|
35
36
|
|