jarvis-ai-assistant 0.2.2__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 (39) 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_agent/prompts.py +26 -4
  6. jarvis/jarvis_code_agent/code_agent.py +279 -11
  7. jarvis/jarvis_code_analysis/code_review.py +21 -19
  8. jarvis/jarvis_data/config_schema.json +86 -18
  9. jarvis/jarvis_git_squash/main.py +3 -3
  10. jarvis/jarvis_git_utils/git_commiter.py +32 -11
  11. jarvis/jarvis_mcp/sse_mcp_client.py +4 -6
  12. jarvis/jarvis_mcp/streamable_mcp_client.py +5 -9
  13. jarvis/jarvis_platform/tongyi.py +9 -9
  14. jarvis/jarvis_rag/cli.py +79 -23
  15. jarvis/jarvis_rag/query_rewriter.py +61 -12
  16. jarvis/jarvis_rag/rag_pipeline.py +143 -34
  17. jarvis/jarvis_rag/retriever.py +6 -6
  18. jarvis/jarvis_smart_shell/main.py +2 -2
  19. jarvis/jarvis_stats/__init__.py +13 -0
  20. jarvis/jarvis_stats/cli.py +337 -0
  21. jarvis/jarvis_stats/stats.py +433 -0
  22. jarvis/jarvis_stats/storage.py +329 -0
  23. jarvis/jarvis_stats/visualizer.py +443 -0
  24. jarvis/jarvis_tools/cli/main.py +84 -15
  25. jarvis/jarvis_tools/generate_new_tool.py +22 -1
  26. jarvis/jarvis_tools/registry.py +35 -16
  27. jarvis/jarvis_tools/search_web.py +3 -3
  28. jarvis/jarvis_tools/virtual_tty.py +315 -26
  29. jarvis/jarvis_utils/config.py +98 -11
  30. jarvis/jarvis_utils/git_utils.py +8 -16
  31. jarvis/jarvis_utils/globals.py +29 -8
  32. jarvis/jarvis_utils/input.py +114 -121
  33. jarvis/jarvis_utils/utils.py +213 -37
  34. {jarvis_ai_assistant-0.2.2.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/METADATA +99 -9
  35. {jarvis_ai_assistant-0.2.2.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/RECORD +39 -34
  36. {jarvis_ai_assistant-0.2.2.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/entry_points.txt +2 -0
  37. {jarvis_ai_assistant-0.2.2.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/WHEEL +0 -0
  38. {jarvis_ai_assistant-0.2.2.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/licenses/LICENSE +0 -0
  39. {jarvis_ai_assistant-0.2.2.dist-info → jarvis_ai_assistant-0.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,433 @@
1
+ """
2
+ 统计管理模块
3
+
4
+ 提供统计数据的增加、查看、分析等功能的主接口
5
+ """
6
+
7
+ from datetime import datetime, timedelta
8
+ from typing import Dict, List, Optional, Union, Any
9
+
10
+ from jarvis.jarvis_stats.storage import StatsStorage
11
+ from jarvis.jarvis_stats.visualizer import StatsVisualizer
12
+
13
+
14
+ class StatsManager:
15
+ """统计管理器"""
16
+
17
+ def __init__(self, storage_dir: Optional[str] = None):
18
+ """
19
+ 初始化统计管理器
20
+
21
+ Args:
22
+ storage_dir: 存储目录路径
23
+ """
24
+ self.storage = StatsStorage(storage_dir)
25
+ self.visualizer = StatsVisualizer()
26
+
27
+ def increment(
28
+ self,
29
+ metric_name: str,
30
+ amount: Union[int, float] = 1,
31
+ tags: Optional[Dict[str, str]] = None,
32
+ group: Optional[str] = None,
33
+ unit: str = "count",
34
+ ):
35
+ """
36
+ 增加计数型指标
37
+
38
+ Args:
39
+ metric_name: 指标名称
40
+ amount: 增加的数量,默认为1
41
+ tags: 标签字典
42
+ group: 指标分组,会自动添加到 tags 中
43
+ unit: 计量单位,默认为 "count"
44
+
45
+ Examples:
46
+ >>> stats = StatsManager()
47
+ >>> stats.increment("page_views")
48
+ >>> stats.increment("downloads", 5)
49
+ >>> stats.increment("response_time", 0.123, unit="seconds")
50
+ >>> stats.increment("execute_script", 1, group="tool")
51
+ """
52
+ # 如果指定了分组,自动添加到 tags 中
53
+ if group:
54
+ if tags is None:
55
+ tags = {}
56
+ tags["group"] = group
57
+
58
+ self.storage.add_metric(
59
+ metric_name=metric_name,
60
+ value=float(amount),
61
+ unit=unit,
62
+ timestamp=datetime.now(),
63
+ tags=tags,
64
+ )
65
+
66
+ def list_metrics(self) -> List[str]:
67
+ """
68
+ 列出所有指标
69
+
70
+ Returns:
71
+ 指标名称列表
72
+ """
73
+ return self.storage.list_metrics()
74
+
75
+ def show(
76
+ self,
77
+ metric_name: Optional[str] = None,
78
+ last_hours: Optional[int] = None,
79
+ last_days: Optional[int] = None,
80
+ start_time: Optional[datetime] = None,
81
+ end_time: Optional[datetime] = None,
82
+ format: str = "table",
83
+ aggregation: str = "hourly",
84
+ tags: Optional[Dict[str, str]] = None,
85
+ ):
86
+ """
87
+ 显示统计数据
88
+
89
+ Args:
90
+ metric_name: 指标名称,如果不指定则显示所有指标摘要
91
+ last_hours: 最近N小时
92
+ last_days: 最近N天
93
+ start_time: 开始时间
94
+ end_time: 结束时间
95
+ format: 显示格式 (table, chart, summary)
96
+ aggregation: 聚合方式 (hourly, daily)
97
+ tags: 过滤标签
98
+
99
+ Examples:
100
+ >>> stats = StatsManager()
101
+ >>> stats.show() # 显示所有指标摘要
102
+ >>> stats.show("api_calls", last_hours=24) # 显示最近24小时
103
+ >>> stats.show("response_time", last_days=7, format="chart") # 图表显示
104
+ """
105
+ # 处理时间范围
106
+ if end_time is None:
107
+ end_time = datetime.now()
108
+
109
+ if start_time is None:
110
+ if last_hours:
111
+ start_time = end_time - timedelta(hours=last_hours)
112
+ elif last_days:
113
+ start_time = end_time - timedelta(days=last_days)
114
+ else:
115
+ start_time = end_time - timedelta(days=7) # 默认7天
116
+
117
+ if metric_name is None:
118
+ # 显示所有指标摘要
119
+ self._show_metrics_summary()
120
+ else:
121
+ # 根据格式显示数据
122
+ if format == "chart":
123
+ self._show_chart(metric_name, start_time, end_time, aggregation, tags)
124
+ elif format == "summary":
125
+ self._show_summary(metric_name, start_time, end_time, aggregation, tags)
126
+ else:
127
+ self._show_table(metric_name, start_time, end_time, tags)
128
+
129
+ def plot(
130
+ self,
131
+ metric_name: Optional[str] = None,
132
+ last_hours: Optional[int] = None,
133
+ last_days: Optional[int] = None,
134
+ start_time: Optional[datetime] = None,
135
+ end_time: Optional[datetime] = None,
136
+ aggregation: str = "hourly",
137
+ tags: Optional[Dict[str, str]] = None,
138
+ width: Optional[int] = None,
139
+ height: Optional[int] = None,
140
+ ):
141
+ """
142
+ 绘制指标的折线图
143
+
144
+ Args:
145
+ metric_name: 指标名称(可选,不指定则根据标签过滤所有匹配的指标)
146
+ last_hours: 最近N小时
147
+ last_days: 最近N天
148
+ start_time: 开始时间
149
+ end_time: 结束时间
150
+ aggregation: 聚合方式
151
+ tags: 过滤标签
152
+ width: 图表宽度
153
+ height: 图表高度
154
+
155
+ Examples:
156
+ >>> stats = StatsManager()
157
+ >>> stats.plot("response_time", last_hours=24)
158
+ >>> stats.plot(tags={"service": "api"}, last_days=7)
159
+ """
160
+ # 处理时间范围
161
+ if end_time is None:
162
+ end_time = datetime.now()
163
+
164
+ if start_time is None:
165
+ if last_hours:
166
+ start_time = end_time - timedelta(hours=last_hours)
167
+ elif last_days:
168
+ start_time = end_time - timedelta(days=last_days)
169
+ else:
170
+ start_time = end_time - timedelta(days=7)
171
+
172
+ # 如果指定了metric_name,显示单个图表
173
+ if metric_name:
174
+ self._show_chart(
175
+ metric_name, start_time, end_time, aggregation, tags, width, height
176
+ )
177
+ else:
178
+ # 如果没有指定metric_name,根据标签过滤获取所有匹配的指标
179
+ self._show_multiple_charts(
180
+ start_time, end_time, aggregation, tags, width, height
181
+ )
182
+
183
+ def get_stats(
184
+ self,
185
+ metric_name: str,
186
+ last_hours: Optional[int] = None,
187
+ last_days: Optional[int] = None,
188
+ start_time: Optional[datetime] = None,
189
+ end_time: Optional[datetime] = None,
190
+ aggregation: Optional[str] = None,
191
+ tags: Optional[Dict[str, str]] = None,
192
+ ) -> Dict[str, Any]:
193
+ """
194
+ 获取统计数据
195
+
196
+ Args:
197
+ metric_name: 指标名称
198
+ last_hours: 最近N小时
199
+ last_days: 最近N天
200
+ start_time: 开始时间
201
+ end_time: 结束时间
202
+ aggregation: 聚合方式,如果指定则返回聚合数据
203
+ tags: 过滤标签
204
+
205
+ Returns:
206
+ 统计数据字典
207
+ """
208
+ # 处理时间范围
209
+ if end_time is None:
210
+ end_time = datetime.now()
211
+
212
+ if start_time is None:
213
+ if last_hours:
214
+ start_time = end_time - timedelta(hours=last_hours)
215
+ elif last_days:
216
+ start_time = end_time - timedelta(days=last_days)
217
+ else:
218
+ start_time = end_time - timedelta(days=7)
219
+
220
+ if aggregation:
221
+ # 返回聚合数据
222
+ return self.storage.aggregate_metrics(
223
+ metric_name, start_time, end_time, aggregation, tags
224
+ )
225
+ else:
226
+ # 返回原始数据
227
+ records = self.storage.get_metrics(metric_name, start_time, end_time, tags)
228
+ return {
229
+ "metric": metric_name,
230
+ "records": records,
231
+ "count": len(records),
232
+ "start_time": start_time.isoformat(),
233
+ "end_time": end_time.isoformat(),
234
+ }
235
+
236
+ def clean_old_data(self, days_to_keep: int = 30):
237
+ """
238
+ 清理旧数据
239
+
240
+ Args:
241
+ days_to_keep: 保留最近N天的数据
242
+ """
243
+ self.storage.delete_old_data(days_to_keep)
244
+ print(f"已清理 {days_to_keep} 天前的数据")
245
+
246
+ def _show_metrics_summary(self):
247
+ """显示所有指标摘要"""
248
+ from rich.console import Console
249
+ from rich.table import Table
250
+
251
+ console = Console()
252
+ metrics = self.storage.list_metrics()
253
+
254
+ if not metrics:
255
+ console.print("[yellow]没有找到任何统计指标[/yellow]")
256
+ return
257
+
258
+ # 创建表格
259
+ table = Table(title="统计指标摘要")
260
+ table.add_column("指标名称", style="cyan")
261
+ table.add_column("单位", style="green")
262
+ table.add_column("最后更新", style="yellow")
263
+ table.add_column("7天数据点", style="magenta")
264
+
265
+ # 获取每个指标的信息
266
+ end_time = datetime.now()
267
+ start_time = end_time - timedelta(days=7)
268
+
269
+ for metric in metrics:
270
+ info = self.storage.get_metric_info(metric)
271
+ if info:
272
+ unit = info.get("unit", "-")
273
+ last_updated = info.get("last_updated", "-")
274
+
275
+ # 格式化时间
276
+ if last_updated != "-":
277
+ try:
278
+ dt = datetime.fromisoformat(last_updated)
279
+ last_updated = dt.strftime("%Y-%m-%d %H:%M")
280
+ except:
281
+ pass
282
+
283
+ # 获取数据点数
284
+ records = self.storage.get_metrics(metric, start_time, end_time)
285
+ count = len(records)
286
+
287
+ table.add_row(metric, unit, last_updated, str(count))
288
+
289
+ console.print(table)
290
+ console.print(f"\n[green]总计: {len(metrics)} 个指标[/green]")
291
+
292
+ def _show_table(
293
+ self,
294
+ metric_name: str,
295
+ start_time: datetime,
296
+ end_time: datetime,
297
+ tags: Optional[Dict[str, str]],
298
+ ):
299
+ """以表格形式显示数据"""
300
+ records = self.storage.get_metrics(metric_name, start_time, end_time, tags)
301
+
302
+ # 获取指标信息
303
+ info = self.storage.get_metric_info(metric_name)
304
+ unit = info.get("unit", "") if info else ""
305
+
306
+ # 使用visualizer显示表格
307
+ self.visualizer.show_table(
308
+ records=records,
309
+ metric_name=metric_name,
310
+ unit=unit,
311
+ start_time=start_time.strftime("%Y-%m-%d %H:%M"),
312
+ end_time=end_time.strftime("%Y-%m-%d %H:%M"),
313
+ tags_filter=tags,
314
+ )
315
+
316
+ def _show_chart(
317
+ self,
318
+ metric_name: str,
319
+ start_time: datetime,
320
+ end_time: datetime,
321
+ aggregation: str,
322
+ tags: Optional[Dict[str, str]],
323
+ width: Optional[int] = None,
324
+ height: Optional[int] = None,
325
+ ):
326
+ """显示图表"""
327
+ # 获取聚合数据
328
+ aggregated = self.storage.aggregate_metrics(
329
+ metric_name, start_time, end_time, aggregation, tags
330
+ )
331
+
332
+ if not aggregated:
333
+ print(f"没有找到指标 '{metric_name}' 的数据")
334
+ return
335
+
336
+ # 获取指标信息
337
+ info = self.storage.get_metric_info(metric_name)
338
+ unit = info.get("unit", "") if info else ""
339
+
340
+ # 准备数据
341
+ data = {k: v["avg"] for k, v in aggregated.items()}
342
+
343
+ # 设置可视化器尺寸
344
+ if width or height:
345
+ self.visualizer.width = width or self.visualizer.width
346
+ self.visualizer.height = height or self.visualizer.height
347
+
348
+ # 绘制图表
349
+ chart = self.visualizer.plot_line_chart(
350
+ data=data,
351
+ title=f"{metric_name} - {aggregation}聚合",
352
+ unit=unit,
353
+ show_values=True,
354
+ )
355
+
356
+ print(chart)
357
+
358
+ # 显示时间范围
359
+ print(
360
+ f"\n时间范围: {start_time.strftime('%Y-%m-%d %H:%M')} ~ {end_time.strftime('%Y-%m-%d %H:%M')}"
361
+ )
362
+
363
+ def _show_multiple_charts(
364
+ self,
365
+ start_time: datetime,
366
+ end_time: datetime,
367
+ aggregation: str,
368
+ tags: Optional[Dict[str, str]],
369
+ width: Optional[int] = None,
370
+ height: Optional[int] = None,
371
+ ):
372
+ """根据标签过滤显示多个指标的图表"""
373
+ from rich.console import Console
374
+
375
+ console = Console()
376
+
377
+ # 获取所有指标
378
+ all_metrics = self.list_metrics()
379
+
380
+ # 根据标签过滤指标
381
+ matched_metrics = []
382
+ for metric in all_metrics:
383
+ # 获取该指标在时间范围内的数据
384
+ records = self.storage.get_metrics(metric, start_time, end_time, tags)
385
+ if records: # 如果有匹配标签的数据
386
+ matched_metrics.append(metric)
387
+
388
+ if not matched_metrics:
389
+ console.print("[yellow]没有找到匹配标签的指标数据[/yellow]")
390
+ return
391
+
392
+ console.print(f"[green]找到 {len(matched_metrics)} 个匹配的指标[/green]")
393
+
394
+ # 为每个匹配的指标绘制图表
395
+ for i, metric in enumerate(matched_metrics):
396
+ if i > 0:
397
+ console.print("\n" + "=" * 80 + "\n") # 分隔符
398
+
399
+ self._show_chart(
400
+ metric, start_time, end_time, aggregation, tags, width, height
401
+ )
402
+
403
+ def _show_summary(
404
+ self,
405
+ metric_name: str,
406
+ start_time: datetime,
407
+ end_time: datetime,
408
+ aggregation: str,
409
+ tags: Optional[Dict[str, str]],
410
+ ):
411
+ """显示汇总信息"""
412
+ # 获取聚合数据
413
+ aggregated = self.storage.aggregate_metrics(
414
+ metric_name, start_time, end_time, aggregation, tags
415
+ )
416
+
417
+ if not aggregated:
418
+ print(f"没有找到指标 '{metric_name}' 的数据")
419
+ return
420
+
421
+ # 获取指标信息
422
+ info = self.storage.get_metric_info(metric_name)
423
+ unit = info.get("unit", "") if info else ""
424
+
425
+ # 显示汇总
426
+ summary = self.visualizer.show_summary(aggregated, metric_name, unit, tags)
427
+ if summary: # 如果返回了内容才打印(兼容性)
428
+ print(summary)
429
+
430
+ # 显示时间范围
431
+ print(
432
+ f"\n时间范围: {start_time.strftime('%Y-%m-%d %H:%M')} ~ {end_time.strftime('%Y-%m-%d %H:%M')}"
433
+ )