jarvis-ai-assistant 0.3.25__py3-none-any.whl → 0.3.27__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 +290 -169
- jarvis/jarvis_agent/config.py +92 -0
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/jarvis.py +69 -35
- jarvis/jarvis_agent/memory_manager.py +70 -2
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/run_loop.py +130 -0
- jarvis/jarvis_agent/task_analyzer.py +88 -9
- jarvis/jarvis_agent/task_manager.py +26 -0
- jarvis/jarvis_agent/user_interaction.py +42 -0
- jarvis/jarvis_code_agent/code_agent.py +18 -3
- jarvis/jarvis_code_agent/lint.py +5 -5
- jarvis/jarvis_data/config_schema.json +7 -6
- jarvis/jarvis_git_squash/main.py +6 -1
- jarvis/jarvis_git_utils/git_commiter.py +38 -12
- jarvis/jarvis_platform/base.py +4 -5
- jarvis/jarvis_platform_manager/main.py +28 -11
- jarvis/jarvis_rag/cli.py +5 -9
- jarvis/jarvis_stats/cli.py +13 -32
- jarvis/jarvis_stats/stats.py +179 -51
- jarvis/jarvis_tools/registry.py +15 -0
- jarvis/jarvis_tools/sub_agent.py +94 -84
- jarvis/jarvis_tools/sub_code_agent.py +12 -6
- jarvis/jarvis_utils/config.py +14 -0
- jarvis/jarvis_utils/fzf.py +56 -0
- jarvis/jarvis_utils/git_utils.py +3 -8
- jarvis/jarvis_utils/input.py +0 -3
- jarvis/jarvis_utils/utils.py +96 -16
- {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/METADATA +2 -3
- {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/RECORD +35 -29
- {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/top_level.txt +0 -0
jarvis/jarvis_stats/stats.py
CHANGED
@@ -166,32 +166,58 @@ class StatsManager:
|
|
166
166
|
>>> StatsManager.show("response_time", last_days=7, format="chart") # 图表显示
|
167
167
|
"""
|
168
168
|
# 处理时间范围
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
169
|
+
# 当未提供时间过滤参数时,默认显示全历史数据
|
170
|
+
all_time = False
|
171
|
+
if last_hours or last_days:
|
172
|
+
if end_time is None:
|
173
|
+
end_time = datetime.now()
|
174
|
+
if start_time is None:
|
175
|
+
if last_hours:
|
176
|
+
start_time = end_time - timedelta(hours=last_hours)
|
177
|
+
elif last_days:
|
178
|
+
start_time = end_time - timedelta(days=last_days)
|
179
|
+
else:
|
180
|
+
all_time = True
|
179
181
|
|
180
182
|
if metric_name is None:
|
181
183
|
# 显示所有指标摘要
|
182
184
|
StatsManager._show_metrics_summary(start_time, end_time, tags)
|
183
185
|
else:
|
186
|
+
# 基于元数据的 created_at / last_updated 推断全历史时间范围
|
187
|
+
storage = StatsManager._get_storage()
|
188
|
+
if start_time is None or end_time is None:
|
189
|
+
info = storage.get_metric_info(metric_name)
|
190
|
+
if info:
|
191
|
+
try:
|
192
|
+
created_at = info.get("created_at")
|
193
|
+
last_updated = info.get("last_updated")
|
194
|
+
start_dt = start_time or (
|
195
|
+
datetime.fromisoformat(created_at) if created_at else datetime.now()
|
196
|
+
)
|
197
|
+
end_dt = end_time or (
|
198
|
+
datetime.fromisoformat(last_updated) if last_updated else datetime.now()
|
199
|
+
)
|
200
|
+
except Exception:
|
201
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
202
|
+
end_dt = end_time or datetime.now()
|
203
|
+
else:
|
204
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
205
|
+
end_dt = end_time or datetime.now()
|
206
|
+
else:
|
207
|
+
start_dt = start_time
|
208
|
+
end_dt = end_time
|
209
|
+
|
184
210
|
# 根据格式显示数据
|
185
211
|
if format == "chart":
|
186
212
|
StatsManager._show_chart(
|
187
|
-
metric_name,
|
213
|
+
metric_name, start_dt, end_dt, aggregation, tags
|
188
214
|
)
|
189
215
|
elif format == "summary":
|
190
216
|
StatsManager._show_summary(
|
191
|
-
metric_name,
|
217
|
+
metric_name, start_dt, end_dt, aggregation, tags
|
192
218
|
)
|
193
219
|
else:
|
194
|
-
StatsManager._show_table(metric_name,
|
220
|
+
StatsManager._show_table(metric_name, start_dt, end_dt, tags)
|
195
221
|
|
196
222
|
@staticmethod
|
197
223
|
def plot(
|
@@ -224,21 +250,44 @@ class StatsManager:
|
|
224
250
|
>>> StatsManager.plot(tags={"service": "api"}, last_days=7)
|
225
251
|
"""
|
226
252
|
# 处理时间范围
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
if
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
start_time = end_time - timedelta(days=7)
|
253
|
+
# 当未提供时间过滤参数时,默认使用全历史数据
|
254
|
+
if last_hours or last_days:
|
255
|
+
if end_time is None:
|
256
|
+
end_time = datetime.now()
|
257
|
+
if start_time is None:
|
258
|
+
if last_hours:
|
259
|
+
start_time = end_time - timedelta(hours=last_hours)
|
260
|
+
elif last_days:
|
261
|
+
start_time = end_time - timedelta(days=last_days)
|
237
262
|
|
238
263
|
# 如果指定了metric_name,显示单个图表
|
239
264
|
if metric_name:
|
265
|
+
# 基于元数据的 created_at / last_updated 推断全历史时间范围
|
266
|
+
storage = StatsManager._get_storage()
|
267
|
+
if start_time is None or end_time is None:
|
268
|
+
info = storage.get_metric_info(metric_name)
|
269
|
+
if info:
|
270
|
+
try:
|
271
|
+
created_at = info.get("created_at")
|
272
|
+
last_updated = info.get("last_updated")
|
273
|
+
start_dt = start_time or (
|
274
|
+
datetime.fromisoformat(created_at) if created_at else datetime.now()
|
275
|
+
)
|
276
|
+
end_dt = end_time or (
|
277
|
+
datetime.fromisoformat(last_updated) if last_updated else datetime.now()
|
278
|
+
)
|
279
|
+
except Exception:
|
280
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
281
|
+
end_dt = end_time or datetime.now()
|
282
|
+
else:
|
283
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
284
|
+
end_dt = end_time or datetime.now()
|
285
|
+
else:
|
286
|
+
start_dt = start_time
|
287
|
+
end_dt = end_time
|
288
|
+
|
240
289
|
StatsManager._show_chart(
|
241
|
-
metric_name,
|
290
|
+
metric_name, start_dt, end_dt, aggregation, tags, width, height
|
242
291
|
)
|
243
292
|
else:
|
244
293
|
# 如果没有指定metric_name,根据标签过滤获取所有匹配的指标
|
@@ -272,32 +321,53 @@ class StatsManager:
|
|
272
321
|
统计数据字典
|
273
322
|
"""
|
274
323
|
# 处理时间范围
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
if
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
start_time = end_time - timedelta(days=7)
|
324
|
+
# 当未提供时间过滤参数时,返回全历史数据
|
325
|
+
if last_hours or last_days or start_time is not None:
|
326
|
+
if end_time is None:
|
327
|
+
end_time = datetime.now()
|
328
|
+
if start_time is None:
|
329
|
+
if last_hours:
|
330
|
+
start_time = end_time - timedelta(hours=last_hours)
|
331
|
+
elif last_days:
|
332
|
+
start_time = end_time - timedelta(days=last_days)
|
285
333
|
|
286
334
|
storage = StatsManager._get_storage()
|
335
|
+
|
336
|
+
# 基于元数据推断全历史范围(当未提供时间过滤参数时)
|
337
|
+
start_dt = start_time
|
338
|
+
end_dt = end_time
|
339
|
+
if start_dt is None or end_dt is None:
|
340
|
+
info = storage.get_metric_info(metric_name)
|
341
|
+
if info:
|
342
|
+
try:
|
343
|
+
created_at = info.get("created_at")
|
344
|
+
last_updated = info.get("last_updated")
|
345
|
+
if start_dt is None and created_at:
|
346
|
+
start_dt = datetime.fromisoformat(created_at)
|
347
|
+
if end_dt is None and last_updated:
|
348
|
+
end_dt = datetime.fromisoformat(last_updated)
|
349
|
+
except Exception:
|
350
|
+
pass
|
351
|
+
if start_dt is None:
|
352
|
+
start_dt = datetime.now() - timedelta(days=7)
|
353
|
+
if end_dt is None:
|
354
|
+
end_dt = datetime.now()
|
355
|
+
|
287
356
|
if aggregation:
|
288
|
-
#
|
357
|
+
# 返回聚合数据(按推断的全历史时间范围)
|
289
358
|
return storage.aggregate_metrics(
|
290
|
-
metric_name,
|
359
|
+
metric_name, start_dt, end_dt, aggregation, tags
|
291
360
|
)
|
292
361
|
else:
|
293
|
-
#
|
294
|
-
records = storage.get_metrics(metric_name,
|
362
|
+
# 返回原始数据(全历史或指定范围)
|
363
|
+
records = storage.get_metrics(metric_name, start_dt, end_dt, tags)
|
364
|
+
|
295
365
|
return {
|
296
366
|
"metric": metric_name,
|
297
367
|
"records": records,
|
298
368
|
"count": len(records),
|
299
|
-
"start_time":
|
300
|
-
"end_time":
|
369
|
+
"start_time": start_dt.isoformat(),
|
370
|
+
"end_time": end_dt.isoformat(),
|
301
371
|
}
|
302
372
|
|
303
373
|
@staticmethod
|
@@ -343,24 +413,56 @@ class StatsManager:
|
|
343
413
|
console.print("[yellow]没有找到任何统计指标[/yellow]")
|
344
414
|
return
|
345
415
|
|
346
|
-
#
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
416
|
+
# 如果未指定时间范围且两者皆为 None,则表示使用全历史数据
|
417
|
+
all_time = False
|
418
|
+
if start_time is None and end_time is None:
|
419
|
+
all_time = True
|
420
|
+
else:
|
421
|
+
if end_time is None:
|
422
|
+
end_time = datetime.now()
|
423
|
+
if start_time is None:
|
424
|
+
start_time = end_time - timedelta(days=7)
|
425
|
+
|
426
|
+
|
427
|
+
|
428
|
+
# 计算时间范围列标题
|
429
|
+
if start_time is None or end_time is None:
|
430
|
+
period_label = "值"
|
431
|
+
else:
|
432
|
+
delta = end_time - start_time
|
433
|
+
if delta.days >= 1:
|
434
|
+
period_label = f"{delta.days}天数据点"
|
435
|
+
else:
|
436
|
+
hours = max(1, int(delta.total_seconds() // 3600))
|
437
|
+
period_label = f"{hours}小时数据点"
|
351
438
|
|
352
439
|
# 创建表格
|
353
440
|
table = Table(title="统计指标摘要")
|
354
441
|
table.add_column("指标名称", style="cyan")
|
355
442
|
table.add_column("单位", style="green")
|
356
443
|
table.add_column("最后更新", style="yellow")
|
357
|
-
table.add_column(
|
444
|
+
table.add_column(period_label, style="magenta")
|
358
445
|
|
359
446
|
# 过滤满足标签条件的指标
|
360
447
|
displayed_count = 0
|
361
448
|
for metric in metrics:
|
362
|
-
#
|
363
|
-
|
449
|
+
# 获取该指标的记录(全历史或指定范围)
|
450
|
+
if 'all_time' in locals() and all_time:
|
451
|
+
_start = None
|
452
|
+
_end = None
|
453
|
+
try:
|
454
|
+
info = storage.get_metric_info(metric)
|
455
|
+
if info:
|
456
|
+
ca = info.get("created_at")
|
457
|
+
lu = info.get("last_updated")
|
458
|
+
_start = datetime.fromisoformat(ca) if ca else None
|
459
|
+
_end = datetime.fromisoformat(lu) if lu else None
|
460
|
+
except Exception:
|
461
|
+
_start = None
|
462
|
+
_end = None
|
463
|
+
records = storage.get_metrics(metric, _start, _end, tags)
|
464
|
+
else:
|
465
|
+
records = storage.get_metrics(metric, start_time, end_time, tags)
|
364
466
|
|
365
467
|
# 如果指定了标签过滤,但没有匹配的记录,跳过该指标
|
366
468
|
if tags and len(records) == 0:
|
@@ -382,8 +484,14 @@ class StatsManager:
|
|
382
484
|
except:
|
383
485
|
pass
|
384
486
|
|
385
|
-
|
386
|
-
|
487
|
+
total_value = sum(r.get("value", 0) for r in records)
|
488
|
+
# 智能格式化,如果是整数则不显示小数点
|
489
|
+
if total_value == int(total_value):
|
490
|
+
value_to_display = str(int(total_value))
|
491
|
+
else:
|
492
|
+
value_to_display = f"{total_value:.2f}"
|
493
|
+
|
494
|
+
table.add_row(metric, unit, last_updated, value_to_display)
|
387
495
|
displayed_count += 1
|
388
496
|
|
389
497
|
if displayed_count == 0:
|
@@ -532,8 +640,28 @@ class StatsManager:
|
|
532
640
|
if i > 0:
|
533
641
|
console.print("\n" + "=" * 80 + "\n") # 分隔符
|
534
642
|
|
643
|
+
# 计算该指标的有效时间范围(基于元数据推断全历史)
|
644
|
+
_start = start_time
|
645
|
+
_end = end_time
|
646
|
+
if _start is None or _end is None:
|
647
|
+
info = storage.get_metric_info(metric)
|
648
|
+
if info:
|
649
|
+
try:
|
650
|
+
ca = info.get("created_at")
|
651
|
+
lu = info.get("last_updated")
|
652
|
+
if _start is None and ca:
|
653
|
+
_start = datetime.fromisoformat(ca)
|
654
|
+
if _end is None and lu:
|
655
|
+
_end = datetime.fromisoformat(lu)
|
656
|
+
except Exception:
|
657
|
+
pass
|
658
|
+
if _start is None:
|
659
|
+
_start = datetime.now() - timedelta(days=7)
|
660
|
+
if _end is None:
|
661
|
+
_end = datetime.now()
|
662
|
+
|
535
663
|
StatsManager._show_chart(
|
536
|
-
metric,
|
664
|
+
metric, _start, _end, aggregation, tags, width, height
|
537
665
|
)
|
538
666
|
|
539
667
|
@staticmethod
|
jarvis/jarvis_tools/registry.py
CHANGED
@@ -839,6 +839,21 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
839
839
|
# 执行工具调用(根据工具实现的协议版本,由系统在内部决定agent的传递方式)
|
840
840
|
result = self.execute_tool(name, args, agent)
|
841
841
|
|
842
|
+
# 记录本轮实际执行的工具,供上层逻辑(如记忆保存判定)使用
|
843
|
+
try:
|
844
|
+
from jarvis.jarvis_agent import Agent # 延迟导入避免循环依赖
|
845
|
+
agent_instance_for_record: Agent = agent_instance
|
846
|
+
# 记录最后一次执行的工具
|
847
|
+
agent_instance_for_record.set_user_data("__last_executed_tool__", name) # type: ignore
|
848
|
+
# 记录本轮累计执行的工具列表
|
849
|
+
executed_list = agent_instance_for_record.get_user_data("__executed_tools__") # type: ignore
|
850
|
+
if not isinstance(executed_list, list):
|
851
|
+
executed_list = []
|
852
|
+
executed_list.append(name)
|
853
|
+
agent_instance_for_record.set_user_data("__executed_tools__", executed_list) # type: ignore
|
854
|
+
except Exception:
|
855
|
+
pass
|
856
|
+
|
842
857
|
# 格式化输出
|
843
858
|
output = self._format_tool_output(
|
844
859
|
result["stdout"], result.get("stderr", "")
|
jarvis/jarvis_tools/sub_agent.py
CHANGED
@@ -4,15 +4,16 @@ sub_agent 工具
|
|
4
4
|
将子任务交给通用 Agent 执行,并返回执行结果。
|
5
5
|
|
6
6
|
约定:
|
7
|
-
-
|
8
|
-
-
|
7
|
+
- 必填参数:task, name, background, system_prompt, summary_prompt, use_tools
|
8
|
+
- 继承父 Agent 的部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer;其他参数需显式提供
|
9
9
|
- 子Agent必须自动完成(auto_complete=True)且需要summary(need_summary=True)
|
10
10
|
"""
|
11
11
|
from typing import Any, Dict, Optional
|
12
12
|
import json
|
13
13
|
|
14
|
-
from jarvis.jarvis_agent import Agent
|
14
|
+
from jarvis.jarvis_agent import Agent
|
15
15
|
from jarvis.jarvis_utils.globals import delete_agent
|
16
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
16
17
|
|
17
18
|
|
18
19
|
class SubAgentTool:
|
@@ -25,7 +26,7 @@ class SubAgentTool:
|
|
25
26
|
|
26
27
|
# 必须与文件名一致,供 ToolRegistry 自动注册
|
27
28
|
name = "sub_agent"
|
28
|
-
description = "将子任务交给通用 Agent
|
29
|
+
description = "将子任务交给通用 Agent 执行,并返回执行结果(继承父Agent部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer;其他参数需显式提供,自动完成并生成总结)。"
|
29
30
|
parameters = {
|
30
31
|
"type": "object",
|
31
32
|
"properties": {
|
@@ -33,12 +34,42 @@ class SubAgentTool:
|
|
33
34
|
"type": "string",
|
34
35
|
"description": "要执行的子任务内容(必填)",
|
35
36
|
},
|
37
|
+
"name": {
|
38
|
+
"type": "string",
|
39
|
+
"description": "子Agent名称(必填)",
|
40
|
+
},
|
36
41
|
"background": {
|
37
42
|
"type": "string",
|
38
|
-
"description": "
|
43
|
+
"description": "任务背景与已知信息(必填,将与任务一并提供给子Agent)",
|
44
|
+
},
|
45
|
+
"system_prompt": {
|
46
|
+
"type": "string",
|
47
|
+
"description": "覆盖子Agent的系统提示词(必填)",
|
48
|
+
},
|
49
|
+
"summary_prompt": {
|
50
|
+
"type": "string",
|
51
|
+
"description": "覆盖子Agent的总结提示词(必填)",
|
52
|
+
},
|
53
|
+
"use_tools": {
|
54
|
+
"type": "array",
|
55
|
+
"items": {"type": "string"},
|
56
|
+
"description": "限制子Agent可用的工具名称列表(必填)。兼容以逗号分隔的字符串输入。可用的工具列表:"
|
57
|
+
+ "\n".join(
|
58
|
+
[
|
59
|
+
t["name"] + ": " + t["description"]
|
60
|
+
for t in ToolRegistry().get_all_tools()
|
61
|
+
]
|
62
|
+
),
|
39
63
|
},
|
40
64
|
},
|
41
|
-
"required": [
|
65
|
+
"required": [
|
66
|
+
"task",
|
67
|
+
"name",
|
68
|
+
"background",
|
69
|
+
"system_prompt",
|
70
|
+
"summary_prompt",
|
71
|
+
"use_tools",
|
72
|
+
],
|
42
73
|
}
|
43
74
|
|
44
75
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
@@ -64,104 +95,83 @@ class SubAgentTool:
|
|
64
95
|
f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
|
65
96
|
)
|
66
97
|
|
67
|
-
#
|
68
|
-
parent_agent = args.get("agent")
|
69
|
-
# 如未注入父Agent,尝试从全局获取当前或任一已注册Agent
|
70
|
-
if parent_agent is None:
|
71
|
-
try:
|
72
|
-
from jarvis.jarvis_utils import globals as G # 延迟导入避免循环
|
73
|
-
|
74
|
-
curr = getattr(G, "current_agent_name", "")
|
75
|
-
if curr:
|
76
|
-
parent_agent = getattr(G, "global_agents", {}).get(curr)
|
77
|
-
if parent_agent is None and getattr(G, "global_agents", {}):
|
78
|
-
try:
|
79
|
-
parent_agent = next(iter(G.global_agents.values()))
|
80
|
-
except Exception:
|
81
|
-
parent_agent = None
|
82
|
-
except Exception:
|
83
|
-
parent_agent = None
|
84
|
-
# 默认/全局
|
85
|
-
system_prompt = origin_agent_system_prompt
|
98
|
+
# 不继承父Agent,所有关键参数必须由调用方显式提供
|
86
99
|
need_summary = True
|
87
100
|
auto_complete = True
|
88
101
|
|
89
|
-
#
|
90
|
-
|
91
|
-
summary_prompt =
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
102
|
+
# 读取并校验必填参数
|
103
|
+
system_prompt = str(args.get("system_prompt", "")).strip()
|
104
|
+
summary_prompt = str(args.get("summary_prompt", "")).strip()
|
105
|
+
agent_name = str(args.get("name", "")).strip()
|
106
|
+
|
107
|
+
# 解析可用工具列表(支持数组或以逗号分隔的字符串)
|
108
|
+
_use_tools = args.get("use_tools", None)
|
96
109
|
use_tools: list[str] = []
|
110
|
+
if isinstance(_use_tools, list):
|
111
|
+
use_tools = [str(x).strip() for x in _use_tools if str(x).strip()]
|
112
|
+
elif isinstance(_use_tools, str):
|
113
|
+
use_tools = [s.strip() for s in _use_tools.split(",") if s.strip()]
|
114
|
+
else:
|
115
|
+
use_tools = []
|
116
|
+
|
117
|
+
errors = []
|
118
|
+
if not system_prompt:
|
119
|
+
errors.append("system_prompt 不能为空")
|
120
|
+
if not summary_prompt:
|
121
|
+
errors.append("summary_prompt 不能为空")
|
122
|
+
if not agent_name:
|
123
|
+
errors.append("name 不能为空")
|
124
|
+
if not use_tools:
|
125
|
+
errors.append("use_tools 不能为空")
|
126
|
+
if not background:
|
127
|
+
errors.append("background 不能为空")
|
128
|
+
|
129
|
+
if errors:
|
130
|
+
return {
|
131
|
+
"success": False,
|
132
|
+
"stdout": "",
|
133
|
+
"stderr": "; ".join(errors),
|
134
|
+
}
|
97
135
|
|
136
|
+
# 基于父Agent(如有)继承部分配置后创建子Agent
|
137
|
+
parent_agent = args.get("agent", None)
|
138
|
+
parent_model_group = None
|
139
|
+
parent_input_handler = None
|
140
|
+
parent_execute_tool_confirm = None
|
141
|
+
parent_multiline_inputer = None
|
98
142
|
try:
|
99
143
|
if parent_agent is not None:
|
100
|
-
# 继承模型组
|
101
144
|
if getattr(parent_agent, "model", None):
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
parent_agent, "execute_tool_confirm", None
|
107
|
-
)
|
108
|
-
use_methodology = getattr(parent_agent, "use_methodology", None)
|
109
|
-
use_analysis = getattr(parent_agent, "use_analysis", None)
|
110
|
-
force_save_memory = getattr(parent_agent, "force_save_memory", None)
|
111
|
-
# 继承工具使用集(名称列表)
|
112
|
-
parent_registry = parent_agent.get_tool_registry()
|
113
|
-
if parent_registry:
|
114
|
-
for t in parent_registry.get_all_tools():
|
115
|
-
if isinstance(t, dict) and t.get("name"):
|
116
|
-
use_tools.append(str(t["name"]))
|
145
|
+
parent_model_group = getattr(parent_agent.model, "model_group", None)
|
146
|
+
parent_input_handler = getattr(parent_agent, "input_handler", None)
|
147
|
+
parent_execute_tool_confirm = getattr(parent_agent, "execute_tool_confirm", None)
|
148
|
+
parent_multiline_inputer = getattr(parent_agent, "multiline_inputer", None)
|
117
149
|
except Exception:
|
118
|
-
#
|
150
|
+
# 安全兜底:无法从父Agent获取配置则保持为None,使用系统默认
|
119
151
|
pass
|
120
152
|
|
121
|
-
# 为避免交互阻塞:提供自动确认与空输入处理器
|
122
|
-
def _auto_confirm(tip: str, default: bool = True) -> bool:
|
123
|
-
return default
|
124
|
-
|
125
|
-
# 创建子Agent(其余配置使用默认/全局)
|
126
153
|
agent = Agent(
|
127
154
|
system_prompt=system_prompt,
|
128
|
-
name=
|
155
|
+
name=agent_name,
|
129
156
|
description="Temporary sub agent for executing a subtask",
|
130
|
-
|
131
|
-
|
132
|
-
summary_prompt=summary_prompt, # 继承父Agent总结提示词(如可用)
|
157
|
+
model_group=parent_model_group,
|
158
|
+
summary_prompt=summary_prompt,
|
133
159
|
auto_complete=auto_complete,
|
134
|
-
output_handler=None,
|
135
|
-
use_tools=None,
|
136
|
-
input_handler=
|
137
|
-
execute_tool_confirm=
|
160
|
+
output_handler=None,
|
161
|
+
use_tools=None,
|
162
|
+
input_handler=parent_input_handler,
|
163
|
+
execute_tool_confirm=parent_execute_tool_confirm,
|
138
164
|
need_summary=need_summary,
|
139
|
-
multiline_inputer=
|
140
|
-
use_methodology=
|
141
|
-
use_analysis=
|
142
|
-
force_save_memory=
|
165
|
+
multiline_inputer=parent_multiline_inputer,
|
166
|
+
use_methodology=None,
|
167
|
+
use_analysis=None,
|
168
|
+
force_save_memory=None,
|
143
169
|
files=None,
|
144
|
-
confirm_callback=_auto_confirm, # 自动确认
|
145
170
|
)
|
146
171
|
|
147
|
-
#
|
172
|
+
# 设置可用工具列表
|
148
173
|
try:
|
149
|
-
|
150
|
-
parent_agent is not None
|
151
|
-
and getattr(parent_agent, "model", None)
|
152
|
-
and getattr(agent, "model", None)
|
153
|
-
):
|
154
|
-
try:
|
155
|
-
model_name = parent_agent.model.name() # type: ignore[attr-defined]
|
156
|
-
if model_name:
|
157
|
-
from typing import Any
|
158
|
-
model_obj: Any = getattr(agent, "model", None)
|
159
|
-
if model_obj is not None:
|
160
|
-
model_obj.set_model_name(model_name)
|
161
|
-
except Exception:
|
162
|
-
pass
|
163
|
-
if use_tools:
|
164
|
-
agent.set_use_tools(use_tools)
|
174
|
+
agent.set_use_tools(use_tools)
|
165
175
|
except Exception:
|
166
176
|
pass
|
167
177
|
|
@@ -13,7 +13,7 @@ import json
|
|
13
13
|
|
14
14
|
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
15
15
|
from jarvis.jarvis_utils.globals import delete_agent
|
16
|
-
from jarvis.
|
16
|
+
from jarvis.jarvis_utils.config import set_config, get_git_check_mode
|
17
17
|
|
18
18
|
|
19
19
|
class SubCodeAgentTool:
|
@@ -59,11 +59,6 @@ class SubCodeAgentTool:
|
|
59
59
|
"stderr": "task 不能为空",
|
60
60
|
}
|
61
61
|
|
62
|
-
# 在模块级别打补丁,仅自动确认交互(允许用户输入)
|
63
|
-
def _auto_confirm(tip: str, default: bool = True) -> bool:
|
64
|
-
return default
|
65
|
-
|
66
|
-
code_agent_module.user_confirm = _auto_confirm
|
67
62
|
|
68
63
|
# 读取背景信息并组合任务
|
69
64
|
background: str = str(args.get("background", "")).strip()
|
@@ -131,7 +126,12 @@ class SubCodeAgentTool:
|
|
131
126
|
except Exception:
|
132
127
|
append_tools = None
|
133
128
|
|
129
|
+
# 在子Agent中放宽 Git 配置校验,避免因严格校验导致进程退出
|
130
|
+
# 使用配置项将校验模式临时切换为 warn,构造完成后恢复原值
|
131
|
+
old_mode = None
|
134
132
|
try:
|
133
|
+
old_mode = get_git_check_mode()
|
134
|
+
set_config("JARVIS_GIT_CHECK_MODE", "warn")
|
135
135
|
code_agent = CodeAgent(
|
136
136
|
model_group=model_group,
|
137
137
|
need_summary=True,
|
@@ -145,6 +145,12 @@ class SubCodeAgentTool:
|
|
145
145
|
"stdout": "",
|
146
146
|
"stderr": f"初始化 CodeAgent 失败(可能未配置 git 或当前非 git 仓库): {se}",
|
147
147
|
}
|
148
|
+
finally:
|
149
|
+
if old_mode is not None:
|
150
|
+
try:
|
151
|
+
set_config("JARVIS_GIT_CHECK_MODE", old_mode)
|
152
|
+
except Exception:
|
153
|
+
pass
|
148
154
|
|
149
155
|
# 子Agent需要自动完成
|
150
156
|
try:
|
jarvis/jarvis_utils/config.py
CHANGED
@@ -423,6 +423,20 @@ def is_enable_static_analysis() -> bool:
|
|
423
423
|
return GLOBAL_CONFIG_DATA.get("JARVIS_ENABLE_STATIC_ANALYSIS", True) is True
|
424
424
|
|
425
425
|
|
426
|
+
def get_git_check_mode() -> str:
|
427
|
+
"""
|
428
|
+
获取Git校验模式。
|
429
|
+
|
430
|
+
返回:
|
431
|
+
str: "strict" 或 "warn",默认为 "strict"
|
432
|
+
"""
|
433
|
+
mode = GLOBAL_CONFIG_DATA.get("JARVIS_GIT_CHECK_MODE", "strict")
|
434
|
+
try:
|
435
|
+
return str(mode)
|
436
|
+
except Exception:
|
437
|
+
return "strict"
|
438
|
+
|
439
|
+
|
426
440
|
def get_mcp_config() -> List[Dict[str, Any]]:
|
427
441
|
"""
|
428
442
|
获取MCP配置列表。
|