jarvis-ai-assistant 0.3.26__py3-none-any.whl → 0.3.28__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 +303 -177
- jarvis/jarvis_agent/agent_manager.py +6 -0
- jarvis/jarvis_agent/config.py +92 -0
- jarvis/jarvis_agent/config_editor.py +1 -1
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/file_methodology_manager.py +1 -3
- jarvis/jarvis_agent/jarvis.py +77 -36
- jarvis/jarvis_agent/memory_manager.py +70 -3
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/run_loop.py +130 -0
- jarvis/jarvis_agent/shell_input_handler.py +1 -1
- jarvis/jarvis_agent/task_analyzer.py +89 -11
- 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_code_analysis/code_review.py +0 -1
- jarvis/jarvis_data/config_schema.json +7 -6
- jarvis/jarvis_git_squash/main.py +6 -1
- jarvis/jarvis_git_utils/git_commiter.py +51 -16
- jarvis/jarvis_mcp/stdio_mcp_client.py +1 -1
- jarvis/jarvis_memory_organizer/memory_organizer.py +2 -5
- jarvis/jarvis_methodology/main.py +0 -2
- jarvis/jarvis_multi_agent/__init__.py +3 -3
- jarvis/jarvis_platform/base.py +5 -6
- jarvis/jarvis_platform/registry.py +1 -1
- jarvis/jarvis_platform/yuanbao.py +0 -1
- jarvis/jarvis_platform_manager/main.py +28 -11
- jarvis/jarvis_platform_manager/service.py +1 -1
- jarvis/jarvis_rag/cli.py +1 -1
- jarvis/jarvis_rag/embedding_manager.py +0 -1
- jarvis/jarvis_rag/llm_interface.py +0 -3
- jarvis/jarvis_smart_shell/main.py +0 -1
- jarvis/jarvis_stats/cli.py +15 -35
- jarvis/jarvis_stats/stats.py +178 -51
- jarvis/jarvis_tools/clear_memory.py +1 -3
- jarvis/jarvis_tools/cli/main.py +0 -1
- jarvis/jarvis_tools/edit_file.py +0 -1
- jarvis/jarvis_tools/generate_new_tool.py +3 -5
- jarvis/jarvis_tools/registry.py +17 -3
- jarvis/jarvis_tools/retrieve_memory.py +2 -3
- jarvis/jarvis_tools/save_memory.py +3 -3
- jarvis/jarvis_tools/search_web.py +2 -2
- jarvis/jarvis_tools/sub_agent.py +114 -85
- jarvis/jarvis_tools/sub_code_agent.py +29 -7
- jarvis/jarvis_tools/virtual_tty.py +3 -14
- jarvis/jarvis_utils/builtin_replace_map.py +4 -4
- jarvis/jarvis_utils/config.py +44 -15
- jarvis/jarvis_utils/fzf.py +56 -0
- jarvis/jarvis_utils/git_utils.py +1 -1
- jarvis/jarvis_utils/globals.py +1 -2
- jarvis/jarvis_utils/input.py +0 -3
- jarvis/jarvis_utils/methodology.py +3 -5
- jarvis/jarvis_utils/output.py +1 -1
- jarvis/jarvis_utils/utils.py +117 -27
- {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/METADATA +2 -3
- {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/RECORD +62 -56
- {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.28.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,7 @@ from jarvis.jarvis_utils.input import get_multiline_input, get_single_line_input
|
|
18
18
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
19
19
|
from jarvis.jarvis_utils.utils import init_env
|
20
20
|
from jarvis.jarvis_platform_manager.service import start_service
|
21
|
+
from jarvis.jarvis_utils.fzf import fzf_select
|
21
22
|
|
22
23
|
app = typer.Typer(help="Jarvis AI 平台")
|
23
24
|
|
@@ -451,17 +452,33 @@ def role_command(
|
|
451
452
|
)
|
452
453
|
PrettyOutput.print(output_str, OutputType.INFO)
|
453
454
|
|
454
|
-
#
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
455
|
+
# 让用户选择角色(优先 fzf,回退编号输入)
|
456
|
+
selected_role = None # type: ignore[var-annotated]
|
457
|
+
fzf_options = [
|
458
|
+
f"{i:>3} | {role['name']} - {role.get('description', '')}"
|
459
|
+
for i, role in enumerate(config["roles"], 1)
|
460
|
+
]
|
461
|
+
selected_str = fzf_select(fzf_options, prompt="选择角色编号 (Enter退出) > ")
|
462
|
+
if selected_str:
|
463
|
+
try:
|
464
|
+
num_part = selected_str.split("|", 1)[0].strip()
|
465
|
+
idx = int(num_part)
|
466
|
+
if 1 <= idx <= len(config["roles"]):
|
467
|
+
selected_role = config["roles"][idx - 1]
|
468
|
+
except Exception:
|
469
|
+
selected_role = None
|
470
|
+
|
471
|
+
if selected_role is None:
|
472
|
+
raw_choice = get_single_line_input("请选择角色(输入编号,直接回车退出): ")
|
473
|
+
if not raw_choice.strip():
|
474
|
+
PrettyOutput.print("已取消,退出程序", OutputType.INFO)
|
475
|
+
raise typer.Exit(code=0)
|
476
|
+
try:
|
477
|
+
choice = int(raw_choice)
|
478
|
+
selected_role = config["roles"][choice - 1]
|
479
|
+
except (ValueError, IndexError):
|
480
|
+
PrettyOutput.print("无效的选择", OutputType.ERROR)
|
481
|
+
return
|
465
482
|
|
466
483
|
|
467
484
|
|
jarvis/jarvis_rag/cli.py
CHANGED
jarvis/jarvis_stats/cli.py
CHANGED
@@ -16,7 +16,6 @@ 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
|
20
19
|
|
21
20
|
app = typer.Typer(help="Jarvis 统计模块命令行工具")
|
22
21
|
console = Console()
|
@@ -98,11 +97,6 @@ def inc(
|
|
98
97
|
@app.command()
|
99
98
|
def show(
|
100
99
|
metric: Optional[str] = typer.Argument(None, help="指标名称,不指定则显示所有"),
|
101
|
-
last_hours: Optional[int] = typer.Option(None, "--hours", "-h", help="最近N小时"),
|
102
|
-
last_days: Optional[int] = typer.Option(None, "--days", "-d", help="最近N天"),
|
103
|
-
format: str = typer.Option(
|
104
|
-
"table", "--format", "-f", help="显示格式: table/chart/summary"
|
105
|
-
),
|
106
100
|
aggregation: str = typer.Option(
|
107
101
|
"hourly", "--agg", "-a", help="聚合方式: hourly/daily"
|
108
102
|
),
|
@@ -123,9 +117,7 @@ def show(
|
|
123
117
|
|
124
118
|
stats.show(
|
125
119
|
metric_name=metric,
|
126
|
-
|
127
|
-
last_days=last_days,
|
128
|
-
format=format,
|
120
|
+
|
129
121
|
aggregation=aggregation,
|
130
122
|
tags=tag_dict if tag_dict else None,
|
131
123
|
)
|
@@ -136,8 +128,6 @@ def plot(
|
|
136
128
|
metric: Optional[str] = typer.Argument(
|
137
129
|
None, help="指标名称(可选,不指定则根据标签过滤所有匹配的指标)"
|
138
130
|
),
|
139
|
-
last_hours: Optional[int] = typer.Option(None, "--hours", "-h", help="最近N小时"),
|
140
|
-
last_days: Optional[int] = typer.Option(None, "--days", "-d", help="最近N天"),
|
141
131
|
aggregation: str = typer.Option(
|
142
132
|
"hourly", "--agg", "-a", help="聚合方式: hourly/daily"
|
143
133
|
),
|
@@ -160,8 +150,6 @@ def plot(
|
|
160
150
|
|
161
151
|
stats.plot(
|
162
152
|
metric_name=metric,
|
163
|
-
last_hours=last_hours,
|
164
|
-
last_days=last_days,
|
165
153
|
aggregation=aggregation,
|
166
154
|
width=width,
|
167
155
|
height=height,
|
@@ -260,9 +248,9 @@ def clean(
|
|
260
248
|
@app.command()
|
261
249
|
def export(
|
262
250
|
metric: str = typer.Argument(..., help="指标名称"),
|
263
|
-
|
264
|
-
|
265
|
-
|
251
|
+
|
252
|
+
|
253
|
+
|
266
254
|
tags: Optional[List[str]] = typer.Option(
|
267
255
|
None, "--tag", "-t", help="标签过滤,格式: key=value"
|
268
256
|
),
|
@@ -285,27 +273,19 @@ def export(
|
|
285
273
|
# 获取数据
|
286
274
|
data = stats.get_stats(
|
287
275
|
metric_name=metric,
|
288
|
-
last_hours=last_hours,
|
289
|
-
last_days=last_days,
|
290
276
|
tags=tag_dict if tag_dict else None,
|
291
277
|
)
|
292
278
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
)
|
279
|
+
# CSV格式输出(默认)
|
280
|
+
records = data.get("records", [])
|
281
|
+
if records:
|
282
|
+
writer = csv.writer(sys.stdout)
|
283
|
+
writer.writerow(["timestamp", "value", "tags"])
|
284
|
+
for record in records:
|
285
|
+
tags_str = json.dumps(record.get("tags", {}))
|
286
|
+
writer.writerow([record["timestamp"], record["value"], tags_str])
|
298
287
|
else:
|
299
|
-
|
300
|
-
records = data.get("records", [])
|
301
|
-
if records:
|
302
|
-
writer = csv.writer(sys.stdout)
|
303
|
-
writer.writerow(["timestamp", "value", "tags"])
|
304
|
-
for record in records:
|
305
|
-
tags_str = json.dumps(record.get("tags", {}))
|
306
|
-
writer.writerow([record["timestamp"], record["value"], tags_str])
|
307
|
-
else:
|
308
|
-
rprint("[yellow]没有找到数据[/yellow]", file=sys.stderr)
|
288
|
+
rprint("[yellow]没有找到数据[/yellow]", file=sys.stderr)
|
309
289
|
|
310
290
|
|
311
291
|
@app.command()
|
@@ -329,7 +309,7 @@ def remove(
|
|
329
309
|
unit = info.get("unit", "-")
|
330
310
|
last_updated = info.get("last_updated", "-")
|
331
311
|
|
332
|
-
rprint(
|
312
|
+
rprint("\n[yellow]准备删除指标:[/yellow]")
|
333
313
|
rprint(f" 名称: {metric}")
|
334
314
|
rprint(f" 单位: {unit}")
|
335
315
|
rprint(f" 最后更新: {last_updated}")
|
@@ -359,7 +339,7 @@ def demo():
|
|
359
339
|
stats = StatsManager(_get_stats_dir())
|
360
340
|
|
361
341
|
# 添加演示数据
|
362
|
-
with console.status("[bold green]正在生成演示数据...")
|
342
|
+
with console.status("[bold green]正在生成演示数据..."):
|
363
343
|
# API响应时间
|
364
344
|
for i in range(20):
|
365
345
|
response_time = random.uniform(0.1, 2.0)
|
jarvis/jarvis_stats/stats.py
CHANGED
@@ -166,32 +166,57 @@ class StatsManager:
|
|
166
166
|
>>> StatsManager.show("response_time", last_days=7, format="chart") # 图表显示
|
167
167
|
"""
|
168
168
|
# 处理时间范围
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
if
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
169
|
+
# 当未提供时间过滤参数时,默认显示全历史数据
|
170
|
+
if last_hours or last_days:
|
171
|
+
if end_time is None:
|
172
|
+
end_time = datetime.now()
|
173
|
+
if start_time is None:
|
174
|
+
if last_hours:
|
175
|
+
start_time = end_time - timedelta(hours=last_hours)
|
176
|
+
elif last_days:
|
177
|
+
start_time = end_time - timedelta(days=last_days)
|
178
|
+
else:
|
179
|
+
pass
|
179
180
|
|
180
181
|
if metric_name is None:
|
181
182
|
# 显示所有指标摘要
|
182
183
|
StatsManager._show_metrics_summary(start_time, end_time, tags)
|
183
184
|
else:
|
185
|
+
# 基于元数据的 created_at / last_updated 推断全历史时间范围
|
186
|
+
storage = StatsManager._get_storage()
|
187
|
+
if start_time is None or end_time is None:
|
188
|
+
info = storage.get_metric_info(metric_name)
|
189
|
+
if info:
|
190
|
+
try:
|
191
|
+
created_at = info.get("created_at")
|
192
|
+
last_updated = info.get("last_updated")
|
193
|
+
start_dt = start_time or (
|
194
|
+
datetime.fromisoformat(created_at) if created_at else datetime.now()
|
195
|
+
)
|
196
|
+
end_dt = end_time or (
|
197
|
+
datetime.fromisoformat(last_updated) if last_updated else datetime.now()
|
198
|
+
)
|
199
|
+
except Exception:
|
200
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
201
|
+
end_dt = end_time or datetime.now()
|
202
|
+
else:
|
203
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
204
|
+
end_dt = end_time or datetime.now()
|
205
|
+
else:
|
206
|
+
start_dt = start_time
|
207
|
+
end_dt = end_time
|
208
|
+
|
184
209
|
# 根据格式显示数据
|
185
210
|
if format == "chart":
|
186
211
|
StatsManager._show_chart(
|
187
|
-
metric_name,
|
212
|
+
metric_name, start_dt, end_dt, aggregation, tags
|
188
213
|
)
|
189
214
|
elif format == "summary":
|
190
215
|
StatsManager._show_summary(
|
191
|
-
metric_name,
|
216
|
+
metric_name, start_dt, end_dt, aggregation, tags
|
192
217
|
)
|
193
218
|
else:
|
194
|
-
StatsManager._show_table(metric_name,
|
219
|
+
StatsManager._show_table(metric_name, start_dt, end_dt, tags)
|
195
220
|
|
196
221
|
@staticmethod
|
197
222
|
def plot(
|
@@ -224,21 +249,44 @@ class StatsManager:
|
|
224
249
|
>>> StatsManager.plot(tags={"service": "api"}, last_days=7)
|
225
250
|
"""
|
226
251
|
# 处理时间范围
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
if
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
start_time = end_time - timedelta(days=7)
|
252
|
+
# 当未提供时间过滤参数时,默认使用全历史数据
|
253
|
+
if last_hours or last_days:
|
254
|
+
if end_time is None:
|
255
|
+
end_time = datetime.now()
|
256
|
+
if start_time is None:
|
257
|
+
if last_hours:
|
258
|
+
start_time = end_time - timedelta(hours=last_hours)
|
259
|
+
elif last_days:
|
260
|
+
start_time = end_time - timedelta(days=last_days)
|
237
261
|
|
238
262
|
# 如果指定了metric_name,显示单个图表
|
239
263
|
if metric_name:
|
264
|
+
# 基于元数据的 created_at / last_updated 推断全历史时间范围
|
265
|
+
storage = StatsManager._get_storage()
|
266
|
+
if start_time is None or end_time is None:
|
267
|
+
info = storage.get_metric_info(metric_name)
|
268
|
+
if info:
|
269
|
+
try:
|
270
|
+
created_at = info.get("created_at")
|
271
|
+
last_updated = info.get("last_updated")
|
272
|
+
start_dt = start_time or (
|
273
|
+
datetime.fromisoformat(created_at) if created_at else datetime.now()
|
274
|
+
)
|
275
|
+
end_dt = end_time or (
|
276
|
+
datetime.fromisoformat(last_updated) if last_updated else datetime.now()
|
277
|
+
)
|
278
|
+
except Exception:
|
279
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
280
|
+
end_dt = end_time or datetime.now()
|
281
|
+
else:
|
282
|
+
start_dt = start_time or (datetime.now() - timedelta(days=7))
|
283
|
+
end_dt = end_time or datetime.now()
|
284
|
+
else:
|
285
|
+
start_dt = start_time
|
286
|
+
end_dt = end_time
|
287
|
+
|
240
288
|
StatsManager._show_chart(
|
241
|
-
metric_name,
|
289
|
+
metric_name, start_dt, end_dt, aggregation, tags, width, height
|
242
290
|
)
|
243
291
|
else:
|
244
292
|
# 如果没有指定metric_name,根据标签过滤获取所有匹配的指标
|
@@ -272,32 +320,53 @@ class StatsManager:
|
|
272
320
|
统计数据字典
|
273
321
|
"""
|
274
322
|
# 处理时间范围
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
if
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
start_time = end_time - timedelta(days=7)
|
323
|
+
# 当未提供时间过滤参数时,返回全历史数据
|
324
|
+
if last_hours or last_days or start_time is not None:
|
325
|
+
if end_time is None:
|
326
|
+
end_time = datetime.now()
|
327
|
+
if start_time is None:
|
328
|
+
if last_hours:
|
329
|
+
start_time = end_time - timedelta(hours=last_hours)
|
330
|
+
elif last_days:
|
331
|
+
start_time = end_time - timedelta(days=last_days)
|
285
332
|
|
286
333
|
storage = StatsManager._get_storage()
|
334
|
+
|
335
|
+
# 基于元数据推断全历史范围(当未提供时间过滤参数时)
|
336
|
+
start_dt = start_time
|
337
|
+
end_dt = end_time
|
338
|
+
if start_dt is None or end_dt is None:
|
339
|
+
info = storage.get_metric_info(metric_name)
|
340
|
+
if info:
|
341
|
+
try:
|
342
|
+
created_at = info.get("created_at")
|
343
|
+
last_updated = info.get("last_updated")
|
344
|
+
if start_dt is None and created_at:
|
345
|
+
start_dt = datetime.fromisoformat(created_at)
|
346
|
+
if end_dt is None and last_updated:
|
347
|
+
end_dt = datetime.fromisoformat(last_updated)
|
348
|
+
except Exception:
|
349
|
+
pass
|
350
|
+
if start_dt is None:
|
351
|
+
start_dt = datetime.now() - timedelta(days=7)
|
352
|
+
if end_dt is None:
|
353
|
+
end_dt = datetime.now()
|
354
|
+
|
287
355
|
if aggregation:
|
288
|
-
#
|
356
|
+
# 返回聚合数据(按推断的全历史时间范围)
|
289
357
|
return storage.aggregate_metrics(
|
290
|
-
metric_name,
|
358
|
+
metric_name, start_dt, end_dt, aggregation, tags
|
291
359
|
)
|
292
360
|
else:
|
293
|
-
#
|
294
|
-
records = storage.get_metrics(metric_name,
|
361
|
+
# 返回原始数据(全历史或指定范围)
|
362
|
+
records = storage.get_metrics(metric_name, start_dt, end_dt, tags)
|
363
|
+
|
295
364
|
return {
|
296
365
|
"metric": metric_name,
|
297
366
|
"records": records,
|
298
367
|
"count": len(records),
|
299
|
-
"start_time":
|
300
|
-
"end_time":
|
368
|
+
"start_time": start_dt.isoformat(),
|
369
|
+
"end_time": end_dt.isoformat(),
|
301
370
|
}
|
302
371
|
|
303
372
|
@staticmethod
|
@@ -343,24 +412,56 @@ class StatsManager:
|
|
343
412
|
console.print("[yellow]没有找到任何统计指标[/yellow]")
|
344
413
|
return
|
345
414
|
|
346
|
-
#
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
415
|
+
# 如果未指定时间范围且两者皆为 None,则表示使用全历史数据
|
416
|
+
all_time = False
|
417
|
+
if start_time is None and end_time is None:
|
418
|
+
all_time = True
|
419
|
+
else:
|
420
|
+
if end_time is None:
|
421
|
+
end_time = datetime.now()
|
422
|
+
if start_time is None:
|
423
|
+
start_time = end_time - timedelta(days=7)
|
424
|
+
|
425
|
+
|
426
|
+
|
427
|
+
# 计算时间范围列标题
|
428
|
+
if start_time is None or end_time is None:
|
429
|
+
period_label = "值"
|
430
|
+
else:
|
431
|
+
delta = end_time - start_time
|
432
|
+
if delta.days >= 1:
|
433
|
+
period_label = f"{delta.days}天数据点"
|
434
|
+
else:
|
435
|
+
hours = max(1, int(delta.total_seconds() // 3600))
|
436
|
+
period_label = f"{hours}小时数据点"
|
351
437
|
|
352
438
|
# 创建表格
|
353
439
|
table = Table(title="统计指标摘要")
|
354
440
|
table.add_column("指标名称", style="cyan")
|
355
441
|
table.add_column("单位", style="green")
|
356
442
|
table.add_column("最后更新", style="yellow")
|
357
|
-
table.add_column(
|
443
|
+
table.add_column(period_label, style="magenta")
|
358
444
|
|
359
445
|
# 过滤满足标签条件的指标
|
360
446
|
displayed_count = 0
|
361
447
|
for metric in metrics:
|
362
|
-
#
|
363
|
-
|
448
|
+
# 获取该指标的记录(全历史或指定范围)
|
449
|
+
if 'all_time' in locals() and all_time:
|
450
|
+
_start = None
|
451
|
+
_end = None
|
452
|
+
try:
|
453
|
+
info = storage.get_metric_info(metric)
|
454
|
+
if info:
|
455
|
+
ca = info.get("created_at")
|
456
|
+
lu = info.get("last_updated")
|
457
|
+
_start = datetime.fromisoformat(ca) if ca else None
|
458
|
+
_end = datetime.fromisoformat(lu) if lu else None
|
459
|
+
except Exception:
|
460
|
+
_start = None
|
461
|
+
_end = None
|
462
|
+
records = storage.get_metrics(metric, _start, _end, tags)
|
463
|
+
else:
|
464
|
+
records = storage.get_metrics(metric, start_time, end_time, tags)
|
364
465
|
|
365
466
|
# 如果指定了标签过滤,但没有匹配的记录,跳过该指标
|
366
467
|
if tags and len(records) == 0:
|
@@ -382,8 +483,14 @@ class StatsManager:
|
|
382
483
|
except:
|
383
484
|
pass
|
384
485
|
|
385
|
-
|
386
|
-
|
486
|
+
total_value = sum(r.get("value", 0) for r in records)
|
487
|
+
# 智能格式化,如果是整数则不显示小数点
|
488
|
+
if total_value == int(total_value):
|
489
|
+
value_to_display = str(int(total_value))
|
490
|
+
else:
|
491
|
+
value_to_display = f"{total_value:.2f}"
|
492
|
+
|
493
|
+
table.add_row(metric, unit, last_updated, value_to_display)
|
387
494
|
displayed_count += 1
|
388
495
|
|
389
496
|
if displayed_count == 0:
|
@@ -532,8 +639,28 @@ class StatsManager:
|
|
532
639
|
if i > 0:
|
533
640
|
console.print("\n" + "=" * 80 + "\n") # 分隔符
|
534
641
|
|
642
|
+
# 计算该指标的有效时间范围(基于元数据推断全历史)
|
643
|
+
_start = start_time
|
644
|
+
_end = end_time
|
645
|
+
if _start is None or _end is None:
|
646
|
+
info = storage.get_metric_info(metric)
|
647
|
+
if info:
|
648
|
+
try:
|
649
|
+
ca = info.get("created_at")
|
650
|
+
lu = info.get("last_updated")
|
651
|
+
if _start is None and ca:
|
652
|
+
_start = datetime.fromisoformat(ca)
|
653
|
+
if _end is None and lu:
|
654
|
+
_end = datetime.fromisoformat(lu)
|
655
|
+
except Exception:
|
656
|
+
pass
|
657
|
+
if _start is None:
|
658
|
+
_start = datetime.now() - timedelta(days=7)
|
659
|
+
if _end is None:
|
660
|
+
_end = datetime.now()
|
661
|
+
|
535
662
|
StatsManager._show_chart(
|
536
|
-
metric,
|
663
|
+
metric, _start, _end, aggregation, tags, width, height
|
537
664
|
)
|
538
665
|
|
539
666
|
@staticmethod
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
import json
|
3
|
-
import shutil
|
4
3
|
from pathlib import Path
|
5
4
|
from typing import Any, Dict, List, Optional
|
6
5
|
|
@@ -8,7 +7,6 @@ from jarvis.jarvis_utils.config import get_data_dir
|
|
8
7
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
9
8
|
from jarvis.jarvis_utils.globals import (
|
10
9
|
clear_short_term_memories,
|
11
|
-
get_short_term_memories,
|
12
10
|
short_term_memories,
|
13
11
|
)
|
14
12
|
|
@@ -212,7 +210,7 @@ class ClearMemoryTool:
|
|
212
210
|
# 生成结果报告
|
213
211
|
|
214
212
|
# 详细报告
|
215
|
-
report =
|
213
|
+
report = "# 记忆清除报告\n\n"
|
216
214
|
report += f"**总计清除**: {total_removed} 条记忆\n\n"
|
217
215
|
|
218
216
|
if tags:
|
jarvis/jarvis_tools/cli/main.py
CHANGED
jarvis/jarvis_tools/edit_file.py
CHANGED
@@ -120,15 +120,13 @@ class generate_new_tool:
|
|
120
120
|
# 注册新工具到当前的工具注册表
|
121
121
|
success_message = f"工具 '{tool_name}' 已成功生成在 {tool_file_path}"
|
122
122
|
|
123
|
-
registration_successful = False
|
124
123
|
if agent:
|
125
124
|
tool_registry = agent.get_tool_registry()
|
126
125
|
if tool_registry:
|
127
126
|
# 尝试加载并注册新工具
|
128
127
|
|
129
128
|
if tool_registry.register_tool_by_file(str(tool_file_path)):
|
130
|
-
success_message +=
|
131
|
-
registration_successful = True
|
129
|
+
success_message += "\n已成功注册到当前会话的工具注册表中"
|
132
130
|
else:
|
133
131
|
# 注册失败,删除已创建的文件
|
134
132
|
PrettyOutput.print(
|
@@ -140,13 +138,13 @@ class generate_new_tool:
|
|
140
138
|
return {
|
141
139
|
"success": False,
|
142
140
|
"stdout": "",
|
143
|
-
"stderr":
|
141
|
+
"stderr": "工具文件已生成,但注册失败。文件已被删除。",
|
144
142
|
}
|
145
143
|
else:
|
146
144
|
PrettyOutput.print(
|
147
145
|
"未找到工具注册表,无法自动注册工具", OutputType.WARNING
|
148
146
|
)
|
149
|
-
success_message +=
|
147
|
+
success_message += "\n注册到当前会话失败,可能需要重新启动Jarvis"
|
150
148
|
|
151
149
|
# 检查并安装缺失的依赖
|
152
150
|
try:
|
jarvis/jarvis_tools/registry.py
CHANGED
@@ -15,7 +15,6 @@ from jarvis.jarvis_mcp.stdio_mcp_client import StdioMcpClient
|
|
15
15
|
from jarvis.jarvis_mcp.streamable_mcp_client import StreamableMcpClient
|
16
16
|
from jarvis.jarvis_tools.base import Tool
|
17
17
|
from jarvis.jarvis_utils.config import get_data_dir, get_tool_load_dirs
|
18
|
-
from jarvis.jarvis_utils.input import user_confirm
|
19
18
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
20
19
|
from jarvis.jarvis_utils.tag import ct, ot
|
21
20
|
from jarvis.jarvis_utils.utils import is_context_overflow, daily_check_git_updates
|
@@ -128,7 +127,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
128
127
|
"""加载工具"""
|
129
128
|
tools = self.get_all_tools()
|
130
129
|
if tools:
|
131
|
-
tools_prompt =
|
130
|
+
tools_prompt = "<tools_section>\n"
|
132
131
|
tools_prompt += " <header>## 可用工具:</header>\n"
|
133
132
|
tools_prompt += " <tools_list>\n"
|
134
133
|
for tool in tools:
|
@@ -202,7 +201,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
202
201
|
def _get_tool_stats(self) -> Dict[str, int]:
|
203
202
|
"""从数据目录获取工具调用统计"""
|
204
203
|
from jarvis.jarvis_stats.stats import StatsManager
|
205
|
-
from datetime import datetime
|
204
|
+
from datetime import datetime
|
206
205
|
|
207
206
|
# 获取所有工具的统计数据
|
208
207
|
tool_stats = {}
|
@@ -839,6 +838,21 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
839
838
|
# 执行工具调用(根据工具实现的协议版本,由系统在内部决定agent的传递方式)
|
840
839
|
result = self.execute_tool(name, args, agent)
|
841
840
|
|
841
|
+
# 记录本轮实际执行的工具,供上层逻辑(如记忆保存判定)使用
|
842
|
+
try:
|
843
|
+
from jarvis.jarvis_agent import Agent # 延迟导入避免循环依赖
|
844
|
+
agent_instance_for_record: Agent = agent_instance
|
845
|
+
# 记录最后一次执行的工具
|
846
|
+
agent_instance_for_record.set_user_data("__last_executed_tool__", name) # type: ignore
|
847
|
+
# 记录本轮累计执行的工具列表
|
848
|
+
executed_list = agent_instance_for_record.get_user_data("__executed_tools__") # type: ignore
|
849
|
+
if not isinstance(executed_list, list):
|
850
|
+
executed_list = []
|
851
|
+
executed_list.append(name)
|
852
|
+
agent_instance_for_record.set_user_data("__executed_tools__", executed_list) # type: ignore
|
853
|
+
except Exception:
|
854
|
+
pass
|
855
|
+
|
842
856
|
# 格式化输出
|
843
857
|
output = self._format_tool_output(
|
844
858
|
result["stdout"], result.get("stderr", "")
|