jarvis-ai-assistant 0.2.3__py3-none-any.whl → 0.2.4__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.
Files changed (31) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/edit_file_handler.py +5 -0
  3. jarvis/jarvis_agent/jarvis.py +22 -25
  4. jarvis/jarvis_agent/main.py +6 -6
  5. jarvis/jarvis_code_agent/code_agent.py +279 -11
  6. jarvis/jarvis_code_analysis/code_review.py +21 -19
  7. jarvis/jarvis_data/config_schema.json +23 -10
  8. jarvis/jarvis_git_squash/main.py +3 -3
  9. jarvis/jarvis_git_utils/git_commiter.py +32 -11
  10. jarvis/jarvis_mcp/sse_mcp_client.py +4 -6
  11. jarvis/jarvis_mcp/streamable_mcp_client.py +5 -9
  12. jarvis/jarvis_rag/retriever.py +1 -1
  13. jarvis/jarvis_smart_shell/main.py +2 -2
  14. jarvis/jarvis_stats/__init__.py +13 -0
  15. jarvis/jarvis_stats/cli.py +337 -0
  16. jarvis/jarvis_stats/stats.py +433 -0
  17. jarvis/jarvis_stats/storage.py +329 -0
  18. jarvis/jarvis_stats/visualizer.py +443 -0
  19. jarvis/jarvis_tools/cli/main.py +84 -15
  20. jarvis/jarvis_tools/registry.py +35 -16
  21. jarvis/jarvis_tools/search_web.py +3 -3
  22. jarvis/jarvis_tools/virtual_tty.py +315 -26
  23. jarvis/jarvis_utils/config.py +6 -0
  24. jarvis/jarvis_utils/git_utils.py +8 -16
  25. jarvis/jarvis_utils/utils.py +210 -37
  26. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/METADATA +19 -2
  27. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/RECORD +31 -26
  28. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/entry_points.txt +2 -0
  29. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/WHEEL +0 -0
  30. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/licenses/LICENSE +0 -0
  31. {jarvis_ai_assistant-0.2.3.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,443 @@
1
+ """
2
+ 统计数据可视化模块
3
+
4
+ 提供终端图形化展示功能
5
+ """
6
+
7
+ import os
8
+
9
+ from typing import Dict, List, Optional, Any
10
+ from collections import OrderedDict
11
+ from rich.console import Console
12
+ from rich.table import Table
13
+ from rich.panel import Panel
14
+ from rich import box
15
+
16
+
17
+ class StatsVisualizer:
18
+ """统计数据可视化类"""
19
+
20
+ def __init__(self, width: Optional[int] = None, height: Optional[int] = None):
21
+ """
22
+ 初始化可视化器
23
+
24
+ Args:
25
+ width: 图表宽度,默认为终端宽度-10
26
+ height: 图表高度,默认为20
27
+ """
28
+ self.width = width or self._get_terminal_width() - 10
29
+ self.height = height or 20
30
+
31
+ # 确保最小尺寸
32
+ self.width = max(self.width, 40)
33
+ self.height = max(self.height, 10)
34
+
35
+ # 初始化Rich Console
36
+ self.console = Console()
37
+
38
+ def _get_terminal_width(self) -> int:
39
+ """获取终端宽度"""
40
+ try:
41
+ columns = os.get_terminal_size().columns
42
+ return columns
43
+ except:
44
+ return 80
45
+
46
+ def plot_line_chart(
47
+ self,
48
+ data: Dict[str, float],
49
+ title: str = "",
50
+ unit: Optional[str] = None,
51
+ show_values: bool = True,
52
+ ) -> str:
53
+ """
54
+ 绘制折线图
55
+
56
+ Args:
57
+ data: 数据字典,键为时间标签,值为数值
58
+ title: 图表标题
59
+ unit: 单位
60
+ show_values: 是否显示数值
61
+
62
+ Returns:
63
+ 图表字符串
64
+ """
65
+ if not data:
66
+ return "无数据可显示"
67
+
68
+ # 排序数据
69
+ sorted_data = OrderedDict(sorted(data.items()))
70
+
71
+ # 提取值和标签
72
+ labels = list(sorted_data.keys())
73
+ values = list(sorted_data.values())
74
+
75
+ # 计算数值范围
76
+ min_val = min(values)
77
+ max_val = max(values)
78
+
79
+ # 如果所有值相同,调整范围
80
+ if min_val == max_val:
81
+ if min_val == 0:
82
+ min_val = -1
83
+ max_val = 1
84
+ else:
85
+ min_val = min_val * 0.9
86
+ max_val = max_val * 1.1
87
+
88
+ # 创建图表画布
89
+ chart_width = self.width - 10 # 留出y轴标签空间
90
+ chart_height = self.height - 5 # 留出x轴标签空间
91
+
92
+ # 初始化画布
93
+ canvas = [[" " for _ in range(chart_width)] for _ in range(chart_height)]
94
+
95
+ # 计算缩放因子
96
+ x_scale = (chart_width - 1) / max(len(values) - 1, 1)
97
+ y_scale = (chart_height - 1) / (max_val - min_val)
98
+
99
+ # 绘制坐标轴
100
+ for i in range(chart_height):
101
+ canvas[i][0] = "│"
102
+ for j in range(chart_width):
103
+ canvas[chart_height - 1][j] = "─"
104
+ canvas[chart_height - 1][0] = "└"
105
+
106
+ # 绘制数据点和连线
107
+ points = []
108
+ for i, value in enumerate(values):
109
+ x = int(i * x_scale) + 1
110
+ y = chart_height - 1 - int((value - min_val) * y_scale)
111
+ y = max(0, min(chart_height - 1, y)) # 确保在范围内
112
+
113
+ if 0 <= x < chart_width and 0 <= y < chart_height:
114
+ points.append((x, y, value))
115
+ canvas[y][x] = "●"
116
+
117
+ # 连接数据点
118
+ for i in range(len(points) - 1):
119
+ x1, y1, _ = points[i]
120
+ x2, y2, _ = points[i + 1]
121
+ self._draw_line(canvas, x1, y1, x2, y2)
122
+
123
+ # 构建输出
124
+ output = []
125
+
126
+ # 标题
127
+ if title:
128
+ output.append(f"\n{title.center(self.width)}")
129
+ output.append("=" * self.width)
130
+
131
+ # Y轴标签和图表
132
+ y_labels = self._generate_y_labels(min_val, max_val, chart_height)
133
+
134
+ for i, row in enumerate(canvas):
135
+ label = y_labels.get(i, "")
136
+ line = f"{label:>8} " + "".join(row)
137
+ output.append(line)
138
+
139
+ # X轴标签
140
+ x_labels = self._generate_x_labels(labels, chart_width)
141
+ output.append(" " * 9 + x_labels)
142
+
143
+ # 单位
144
+ if unit:
145
+ output.append(f"\n单位: {unit}")
146
+
147
+ # 统计信息
148
+ if show_values:
149
+ output.append(
150
+ f"\n最小值: {min_val:.2f}, 最大值: {max_val:.2f}, 平均值: {sum(values)/len(values):.2f}"
151
+ )
152
+
153
+ return "\n".join(output)
154
+
155
+ def plot_bar_chart(
156
+ self,
157
+ data: Dict[str, float],
158
+ title: str = "",
159
+ unit: Optional[str] = None,
160
+ horizontal: bool = False,
161
+ ) -> str:
162
+ """
163
+ 绘制柱状图
164
+
165
+ Args:
166
+ data: 数据字典
167
+ title: 图表标题
168
+ unit: 单位
169
+ horizontal: 是否横向
170
+
171
+ Returns:
172
+ 图表字符串
173
+ """
174
+ if not data:
175
+ return "无数据可显示"
176
+
177
+ output = []
178
+
179
+ # 标题
180
+ if title:
181
+ output.append(f"\n{title.center(self.width)}")
182
+ output.append("=" * self.width)
183
+
184
+ # 找出最大值用于缩放
185
+ max_value = max(data.values()) if data.values() else 1
186
+
187
+ if horizontal:
188
+ # 横向柱状图
189
+ max_label_len = max(len(str(k)) for k in data.keys())
190
+ bar_width = self.width - max_label_len - 15
191
+
192
+ for label, value in data.items():
193
+ bar_len = int((value / max_value) * bar_width)
194
+ bar = "█" * bar_len
195
+ output.append(f"{str(label):>{max_label_len}} │{bar} {value:.2f}")
196
+ else:
197
+ # 纵向柱状图
198
+ bar_height = self.height - 5
199
+ labels = list(data.keys())
200
+ values = list(data.values())
201
+
202
+ # 创建画布
203
+ canvas = [[" " for _ in range(len(labels) * 4)] for _ in range(bar_height)]
204
+
205
+ # 绘制柱子
206
+ for i, (label, value) in enumerate(zip(labels, values)):
207
+ height = int((value / max_value) * bar_height)
208
+ x = i * 4 + 1
209
+
210
+ for y in range(bar_height - height, bar_height):
211
+ if y >= 0 and x < len(canvas[0]):
212
+ canvas[y][x] = "█"
213
+ if x + 1 < len(canvas[0]):
214
+ canvas[y][x + 1] = "█"
215
+
216
+ # 输出画布
217
+ for row in canvas:
218
+ output.append("".join(row))
219
+
220
+ # X轴标签
221
+ label_line = ""
222
+ for i, label in enumerate(labels):
223
+ x = i * 4
224
+ label_line += f"{label[:3]:>4}"
225
+ output.append(label_line)
226
+
227
+ # 单位
228
+ if unit:
229
+ output.append(f"\n单位: {unit}")
230
+
231
+ return "\n".join(output)
232
+
233
+ def show_summary(
234
+ self,
235
+ aggregated_data: Dict[str, Dict[str, Any]],
236
+ metric_name: str,
237
+ unit: Optional[str] = None,
238
+ tags_filter: Optional[Dict[str, str]] = None,
239
+ ) -> str:
240
+ """
241
+ 显示数据摘要
242
+
243
+ Args:
244
+ aggregated_data: 聚合后的数据
245
+ metric_name: 指标名称
246
+ unit: 单位
247
+ tags_filter: 标签过滤条件
248
+
249
+ Returns:
250
+ 摘要字符串(用于兼容性,实际会直接打印)
251
+ """
252
+ if not aggregated_data:
253
+ self.console.print("[yellow]无数据可显示[/yellow]")
254
+ return "无数据可显示"
255
+
256
+ # 创建表格
257
+ table = Table(title=f"{metric_name} 统计摘要", box=box.ROUNDED)
258
+
259
+ # 添加列
260
+ table.add_column("时间", justify="center", style="cyan")
261
+ table.add_column("计数", justify="right", style="green")
262
+ table.add_column("总和", justify="right", style="yellow")
263
+ table.add_column("平均", justify="right", style="yellow")
264
+ table.add_column("最小", justify="right", style="blue")
265
+ table.add_column("最大", justify="right", style="red")
266
+
267
+ # 添加数据行
268
+ for time_key, stats in sorted(aggregated_data.items()):
269
+ table.add_row(
270
+ time_key,
271
+ str(stats["count"]),
272
+ f"{stats['sum']:.2f}",
273
+ f"{stats['avg']:.2f}",
274
+ f"{stats['min']:.2f}",
275
+ f"{stats['max']:.2f}",
276
+ )
277
+
278
+ # 显示表格
279
+ self.console.print(table)
280
+
281
+ # 显示单位信息
282
+ if unit:
283
+ self.console.print(f"\n[dim]单位: {unit}[/dim]")
284
+
285
+ # 显示过滤条件
286
+ if tags_filter:
287
+ filter_str = ", ".join([f"{k}={v}" for k, v in tags_filter.items()])
288
+ self.console.print(f"[dim]过滤条件: {filter_str}[/dim]")
289
+
290
+ return "" # 返回空字符串,实际输出已经通过console打印
291
+
292
+ def show_table(
293
+ self,
294
+ records: List[Dict[str, Any]],
295
+ metric_name: str,
296
+ unit: Optional[str] = None,
297
+ start_time: Optional[str] = None,
298
+ end_time: Optional[str] = None,
299
+ tags_filter: Optional[Dict[str, str]] = None,
300
+ ) -> str:
301
+ """
302
+ 使用Rich Table显示数据记录
303
+
304
+ Args:
305
+ records: 数据记录列表
306
+ metric_name: 指标名称
307
+ unit: 单位
308
+ start_time: 开始时间
309
+ end_time: 结束时间
310
+ tags_filter: 标签过滤条件
311
+
312
+ Returns:
313
+ 空字符串(实际通过console打印)
314
+ """
315
+ if not records:
316
+ self.console.print(f"[yellow]没有找到指标 '{metric_name}' 的数据[/yellow]")
317
+ return ""
318
+
319
+ # 创建表格
320
+ table = Table(title=f"指标: {metric_name}", box=box.ROUNDED)
321
+
322
+ # 添加列
323
+ table.add_column("时间", style="cyan", no_wrap=True)
324
+ table.add_column("值", justify="right", style="yellow")
325
+ table.add_column("标签", style="dim")
326
+
327
+ # 只显示最近的20条记录
328
+ display_records = records[-20:] if len(records) > 20 else records
329
+
330
+ # 添加数据行
331
+ from datetime import datetime
332
+
333
+ for record in display_records:
334
+ timestamp = datetime.fromisoformat(record["timestamp"])
335
+ time_str = timestamp.strftime("%Y-%m-%d %H:%M:%S")
336
+ value = f"{record['value']:.2f}"
337
+ tags_str = ", ".join(f"{k}={v}" for k, v in record.get("tags", {}).items())
338
+
339
+ table.add_row(time_str, value, tags_str)
340
+
341
+ # 显示表格
342
+ self.console.print(table)
343
+
344
+ # 显示元信息
345
+ info_items = []
346
+ if unit:
347
+ info_items.append(f"单位: {unit}")
348
+ if start_time and end_time:
349
+ info_items.append(f"时间范围: {start_time} ~ {end_time}")
350
+ if tags_filter:
351
+ filter_str = ", ".join([f"{k}={v}" for k, v in tags_filter.items()])
352
+ info_items.append(f"过滤条件: {filter_str}")
353
+
354
+ if info_items:
355
+ self.console.print(Panel(" | ".join(info_items), style="dim"))
356
+
357
+ # 统计信息
358
+ if len(records) > 0:
359
+ values = [r["value"] for r in records]
360
+ stats_info = (
361
+ f"总记录数: {len(records)} | "
362
+ f"显示: {len(display_records)} | "
363
+ f"最小值: {min(values):.2f} | "
364
+ f"最大值: {max(values):.2f} | "
365
+ f"平均值: {sum(values)/len(values):.2f}"
366
+ )
367
+ self.console.print(f"\n[dim]{stats_info}[/dim]")
368
+
369
+ return ""
370
+
371
+ def _draw_line(self, canvas: List[List[str]], x1: int, y1: int, x2: int, y2: int):
372
+ """在画布上绘制线条"""
373
+ # 使用Bresenham算法绘制线条
374
+ dx = abs(x2 - x1)
375
+ dy = abs(y2 - y1)
376
+ sx = 1 if x1 < x2 else -1
377
+ sy = 1 if y1 < y2 else -1
378
+ err = dx - dy
379
+
380
+ x, y = x1, y1
381
+
382
+ while True:
383
+ if 0 <= x < len(canvas[0]) and 0 <= y < len(canvas):
384
+ if canvas[y][x] == " ":
385
+ if dx > dy:
386
+ canvas[y][x] = "─"
387
+ else:
388
+ canvas[y][x] = "│"
389
+
390
+ if x == x2 and y == y2:
391
+ break
392
+
393
+ e2 = 2 * err
394
+ if e2 > -dy:
395
+ err -= dy
396
+ x += sx
397
+ if e2 < dx:
398
+ err += dx
399
+ y += sy
400
+
401
+ def _generate_y_labels(
402
+ self, min_val: float, max_val: float, height: int
403
+ ) -> Dict[int, str]:
404
+ """生成Y轴标签"""
405
+ labels = {}
406
+
407
+ # 在顶部、中部和底部放置标签
408
+ positions = [0, height // 2, height - 1]
409
+ values = [max_val, (max_val + min_val) / 2, min_val]
410
+
411
+ for pos, val in zip(positions, values):
412
+ labels[pos] = f"{val:.1f}"
413
+
414
+ return labels
415
+
416
+ def _generate_x_labels(self, labels: List[str], width: int) -> str:
417
+ """生成X轴标签"""
418
+ if not labels:
419
+ return ""
420
+
421
+ # 简化标签(如果是时间格式)
422
+ simplified_labels = []
423
+ for label in labels:
424
+ if len(label) > 10:
425
+ # 尝试提取时间部分
426
+ parts = label.split()
427
+ if len(parts) >= 2:
428
+ simplified_labels.append(parts[1][:5]) # 只取时间
429
+ else:
430
+ simplified_labels.append(label[:8])
431
+ else:
432
+ simplified_labels.append(label)
433
+
434
+ # 根据空间决定显示多少标签
435
+ max_labels = width // 10
436
+ step = max(1, len(simplified_labels) // max_labels)
437
+
438
+ label_line = ""
439
+ for i in range(0, len(simplified_labels), step):
440
+ if i < len(simplified_labels):
441
+ label_line += f"{simplified_labels[i]:>10}"
442
+
443
+ return label_line
@@ -40,25 +40,94 @@ def list_tools(
40
40
 
41
41
 
42
42
  @app.command("stat")
43
- def stat_tools(as_json: bool = typer.Option(False, "--json", help="以JSON格式输出")):
43
+ def stat_tools(
44
+ as_json: bool = typer.Option(False, "--json", help="以JSON格式输出"),
45
+ last_days: Optional[int] = typer.Option(None, "--days", help="显示最近N天的统计(默认显示所有历史数据)"),
46
+ format: str = typer.Option("table", "--format", help="显示格式: table, chart, summary")
47
+ ):
44
48
  """显示工具调用统计信息"""
45
- registry = ToolRegistry()
46
- stats = registry._get_tool_stats()
47
- tools = registry.get_all_tools()
48
-
49
- table_data = []
50
- for tool in tools:
51
- name = tool["name"]
52
- count = stats.get(name, 0)
53
- table_data.append([name, count])
49
+ from jarvis.jarvis_stats.stats import StatsManager
50
+
51
+ stats_manager = StatsManager()
52
+
53
+ if format == "table":
54
+ registry = ToolRegistry()
55
+ stats = registry._get_tool_stats()
56
+ tools = registry.get_all_tools()
57
+
58
+ table_data = []
59
+ for tool in tools:
60
+ name = tool["name"]
61
+ count = stats.get(name, 0)
62
+ if count > 0: # 只显示有调用记录的工具
63
+ table_data.append([name, count])
54
64
 
55
- table_data.sort(key=lambda x: x[1], reverse=True)
65
+ table_data.sort(key=lambda x: x[1], reverse=True)
56
66
 
57
- if as_json:
58
- print(json.dumps(dict(table_data), indent=2))
67
+ if as_json:
68
+ print(json.dumps(dict(table_data), indent=2))
69
+ else:
70
+ time_desc = f"最近{last_days}天" if last_days else "所有历史"
71
+ PrettyOutput.section(f"工具调用统计 ({time_desc})", OutputType.SYSTEM)
72
+ if table_data:
73
+ print(tabulate(table_data, headers=["工具名称", "调用次数"], tablefmt="grid"))
74
+ print(f"\n总计: {len(table_data)} 个工具被使用,共 {sum(x[1] for x in table_data)} 次调用")
75
+ else:
76
+ print("暂无工具调用记录")
59
77
  else:
60
- PrettyOutput.section("工具调用统计", OutputType.SYSTEM)
61
- print(tabulate(table_data, headers=["工具名称", "调用次数"], tablefmt="grid"))
78
+ # 使用 stats 系统的高级功能
79
+ PrettyOutput.section("工具组统计", OutputType.SYSTEM)
80
+ # 显示所有标记为 tool 组的指标
81
+ metrics = stats_manager.list_metrics()
82
+ tool_metrics = []
83
+
84
+ for metric in metrics:
85
+ # 检查是否是工具组的指标
86
+ if last_days:
87
+ stats_data = stats_manager.get_stats(
88
+ metric_name=metric,
89
+ last_days=last_days,
90
+ tags={"group": "tool"}
91
+ )
92
+ else:
93
+ # 获取所有历史数据
94
+ from datetime import datetime
95
+ stats_data = stats_manager.get_stats(
96
+ metric_name=metric,
97
+ start_time=datetime(2000, 1, 1),
98
+ end_time=datetime.now(),
99
+ tags={"group": "tool"}
100
+ )
101
+ if stats_data and stats_data.get("records"):
102
+ tool_metrics.append(metric)
103
+
104
+ if tool_metrics:
105
+ for metric in tool_metrics:
106
+ if format == "chart":
107
+ if last_days:
108
+ stats_manager.plot(metric, last_days=last_days, tags={"group": "tool"})
109
+ else:
110
+ from datetime import datetime
111
+ stats_manager.plot(
112
+ metric,
113
+ start_time=datetime(2000, 1, 1),
114
+ end_time=datetime.now(),
115
+ tags={"group": "tool"}
116
+ )
117
+ elif format == "summary":
118
+ if last_days:
119
+ stats_manager.show(metric, last_days=last_days, format="summary", tags={"group": "tool"})
120
+ else:
121
+ from datetime import datetime
122
+ stats_manager.show(
123
+ metric,
124
+ start_time=datetime(2000, 1, 1),
125
+ end_time=datetime.now(),
126
+ format="summary",
127
+ tags={"group": "tool"}
128
+ )
129
+ else:
130
+ print("暂无工具调用记录")
62
131
 
63
132
 
64
133
  @app.command("call")
@@ -200,25 +200,44 @@ class ToolRegistry(OutputHandlerProtocol):
200
200
 
201
201
  def _get_tool_stats(self) -> Dict[str, int]:
202
202
  """从数据目录获取工具调用统计"""
203
- stats_file = Path(get_data_dir()) / "tool_stat.yaml"
204
- if stats_file.exists():
205
- try:
206
- with open(stats_file, "r", encoding="utf-8") as f:
207
- return yaml.safe_load(f) or {}
208
- except Exception as e:
209
- PrettyOutput.print(f"加载工具调用统计失败: {str(e)}", OutputType.WARNING)
210
- return {}
203
+ from jarvis.jarvis_stats.stats import StatsManager
204
+ from datetime import datetime, timedelta
205
+
206
+ stats_manager = StatsManager()
207
+
208
+ # 获取所有工具的统计数据
209
+ tool_stats = {}
210
+ tools = self.get_all_tools()
211
+
212
+ # 获取所有历史数据(从很早的时间开始)
213
+ end_time = datetime.now()
214
+ start_time = datetime(2000, 1, 1) # 使用一个足够早的时间
215
+
216
+ for tool in tools:
217
+ tool_name = tool["name"]
218
+ # 获取该工具的统计数据
219
+ stats_data = stats_manager.get_stats(
220
+ metric_name=tool_name,
221
+ start_time=start_time,
222
+ end_time=end_time,
223
+ tags={"group": "tool"}
224
+ )
225
+
226
+ # 计算总调用次数
227
+ if stats_data and "records" in stats_data:
228
+ total_count = sum(record["value"] for record in stats_data["records"])
229
+ tool_stats[tool_name] = int(total_count)
230
+ else:
231
+ tool_stats[tool_name] = 0
232
+
233
+ return tool_stats
211
234
 
212
235
  def _update_tool_stats(self, name: str) -> None:
213
236
  """更新工具调用统计"""
214
- stats = self._get_tool_stats()
215
- stats[name] = stats.get(name, 0) + 1
216
- stats_file = Path(get_data_dir()) / "tool_stat.yaml"
217
- try:
218
- with open(stats_file, "w", encoding="utf-8") as f:
219
- yaml.safe_dump(stats, f, allow_unicode=True)
220
- except Exception as e:
221
- PrettyOutput.print(f"保存工具调用统计失败: {str(e)}", OutputType.WARNING)
237
+ from jarvis.jarvis_stats.stats import StatsManager
238
+
239
+ stats_manager = StatsManager()
240
+ stats_manager.increment(name, group="tool")
222
241
 
223
242
  def use_tools(self, name: List[str]) -> None:
224
243
  """使用指定工具
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
- """A tool for searching the web."""
2
+ """网络搜索工具。"""
3
3
  from typing import Any, Dict
4
4
 
5
5
  import requests
@@ -17,7 +17,7 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
17
17
 
18
18
 
19
19
  class SearchWebTool:
20
- """A class to handle web searches."""
20
+ """处理网络搜索的类。"""
21
21
 
22
22
  name = "search_web"
23
23
  description = "搜索互联网上的信息"
@@ -28,7 +28,7 @@ class SearchWebTool:
28
28
 
29
29
  def _search_with_ddgs(self, query: str, agent: Agent) -> Dict[str, Any]:
30
30
  # pylint: disable=too-many-locals, broad-except
31
- """Performs a web search, scrapes content, and summarizes the results."""
31
+ """执行网络搜索、抓取内容并总结结果。"""
32
32
  try:
33
33
  PrettyOutput.print("▶️ 使用 DuckDuckGo 开始网页搜索...", OutputType.INFO)
34
34
  results = list(DDGS().text(query, max_results=50))