jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.0__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 +1143 -245
- jarvis/jarvis_agent/agent_manager.py +97 -0
- jarvis/jarvis_agent/builtin_input_handler.py +12 -10
- jarvis/jarvis_agent/config_editor.py +57 -0
- jarvis/jarvis_agent/edit_file_handler.py +392 -99
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/file_methodology_manager.py +117 -0
- jarvis/jarvis_agent/jarvis.py +1117 -147
- jarvis/jarvis_agent/main.py +78 -34
- jarvis/jarvis_agent/memory_manager.py +195 -0
- jarvis/jarvis_agent/methodology_share_manager.py +174 -0
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/prompts.py +46 -9
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +146 -0
- jarvis/jarvis_agent/session_manager.py +9 -9
- jarvis/jarvis_agent/share_manager.py +228 -0
- jarvis/jarvis_agent/shell_input_handler.py +23 -3
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +212 -0
- jarvis/jarvis_agent/task_manager.py +154 -0
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/tool_executor.py +8 -4
- jarvis/jarvis_agent/tool_share_manager.py +139 -0
- jarvis/jarvis_agent/user_interaction.py +42 -0
- jarvis/jarvis_agent/utils.py +54 -0
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +751 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +613 -0
- jarvis/jarvis_c2rust/collector.py +258 -0
- jarvis/jarvis_c2rust/library_replacer.py +1122 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
- jarvis/jarvis_c2rust/optimizer.py +960 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2325 -0
- jarvis/jarvis_code_agent/build_validation_config.py +133 -0
- jarvis/jarvis_code_agent/code_agent.py +1605 -178
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
- jarvis/jarvis_code_agent/lint.py +275 -13
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
- jarvis/jarvis_code_analysis/code_review.py +583 -548
- jarvis/jarvis_data/config_schema.json +339 -28
- jarvis/jarvis_git_squash/main.py +22 -13
- jarvis/jarvis_git_utils/git_commiter.py +171 -55
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
- jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
- jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
- jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
- jarvis/jarvis_methodology/main.py +48 -63
- jarvis/jarvis_multi_agent/__init__.py +302 -43
- jarvis/jarvis_multi_agent/main.py +70 -24
- jarvis/jarvis_platform/ai8.py +40 -23
- jarvis/jarvis_platform/base.py +210 -49
- jarvis/jarvis_platform/human.py +11 -1
- jarvis/jarvis_platform/kimi.py +82 -76
- jarvis/jarvis_platform/openai.py +73 -1
- jarvis/jarvis_platform/registry.py +8 -15
- jarvis/jarvis_platform/tongyi.py +115 -101
- jarvis/jarvis_platform/yuanbao.py +89 -63
- jarvis/jarvis_platform_manager/main.py +194 -132
- jarvis/jarvis_platform_manager/service.py +122 -86
- jarvis/jarvis_rag/cli.py +156 -53
- jarvis/jarvis_rag/embedding_manager.py +155 -12
- jarvis/jarvis_rag/llm_interface.py +10 -13
- jarvis/jarvis_rag/query_rewriter.py +63 -12
- jarvis/jarvis_rag/rag_pipeline.py +222 -40
- jarvis/jarvis_rag/reranker.py +26 -3
- jarvis/jarvis_rag/retriever.py +270 -14
- jarvis/jarvis_sec/__init__.py +3605 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +116 -0
- jarvis/jarvis_sec/report.py +257 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +219 -0
- jarvis/jarvis_smart_shell/main.py +405 -137
- jarvis/jarvis_stats/__init__.py +13 -0
- jarvis/jarvis_stats/cli.py +387 -0
- jarvis/jarvis_stats/stats.py +711 -0
- jarvis/jarvis_stats/storage.py +612 -0
- jarvis/jarvis_stats/visualizer.py +282 -0
- jarvis/jarvis_tools/ask_user.py +1 -0
- jarvis/jarvis_tools/base.py +18 -2
- jarvis/jarvis_tools/clear_memory.py +239 -0
- jarvis/jarvis_tools/cli/main.py +220 -144
- jarvis/jarvis_tools/execute_script.py +52 -12
- jarvis/jarvis_tools/file_analyzer.py +17 -12
- jarvis/jarvis_tools/generate_new_tool.py +46 -24
- jarvis/jarvis_tools/read_code.py +277 -18
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +86 -13
- jarvis/jarvis_tools/registry.py +294 -90
- jarvis/jarvis_tools/retrieve_memory.py +227 -0
- jarvis/jarvis_tools/save_memory.py +194 -0
- jarvis/jarvis_tools/search_web.py +62 -28
- jarvis/jarvis_tools/sub_agent.py +205 -0
- jarvis/jarvis_tools/sub_code_agent.py +217 -0
- jarvis/jarvis_tools/virtual_tty.py +330 -62
- jarvis/jarvis_utils/builtin_replace_map.py +4 -5
- jarvis/jarvis_utils/clipboard.py +90 -0
- jarvis/jarvis_utils/config.py +607 -50
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/fzf.py +57 -0
- jarvis/jarvis_utils/git_utils.py +251 -29
- jarvis/jarvis_utils/globals.py +174 -17
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/input.py +899 -153
- jarvis/jarvis_utils/methodology.py +210 -83
- jarvis/jarvis_utils/output.py +220 -137
- jarvis/jarvis_utils/utils.py +1906 -135
- jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis/jarvis_tools/edit_file.py +0 -255
- jarvis/jarvis_tools/rewrite_file.py +0 -195
- jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
- jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
- /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""
|
|
2
|
+
统计模块命令行接口
|
|
3
|
+
|
|
4
|
+
使用 typer 提供友好的命令行交互
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import builtins
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from typing import Optional, List
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
from rich import print as rprint
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .stats import StatsManager
|
|
17
|
+
from jarvis.jarvis_utils.utils import init_env
|
|
18
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
19
|
+
|
|
20
|
+
app = typer.Typer(help="Jarvis 统计模块命令行工具")
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
# 全局变量,存储是否已初始化
|
|
24
|
+
_initialized = False
|
|
25
|
+
_stats_dir = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _get_stats_dir():
|
|
29
|
+
"""获取统计数据目录"""
|
|
30
|
+
global _initialized, _stats_dir
|
|
31
|
+
if not _initialized:
|
|
32
|
+
_stats_dir = Path(get_data_dir()) / "stats"
|
|
33
|
+
_initialized = True
|
|
34
|
+
return str(_stats_dir)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@app.command()
|
|
38
|
+
def add(
|
|
39
|
+
metric: str = typer.Argument(..., help="指标名称"),
|
|
40
|
+
value: float = typer.Argument(..., help="指标值"),
|
|
41
|
+
unit: Optional[str] = typer.Option(None, "--unit", "-u", help="单位"),
|
|
42
|
+
tags: Optional[List[str]] = typer.Option(
|
|
43
|
+
None, "--tag", "-t", help="标签,格式: key=value"
|
|
44
|
+
),
|
|
45
|
+
):
|
|
46
|
+
"""添加统计数据"""
|
|
47
|
+
stats = StatsManager(_get_stats_dir())
|
|
48
|
+
|
|
49
|
+
# 解析标签
|
|
50
|
+
tag_dict = {}
|
|
51
|
+
if tags:
|
|
52
|
+
for tag in tags:
|
|
53
|
+
if "=" in tag:
|
|
54
|
+
key, val = tag.split("=", 1)
|
|
55
|
+
tag_dict[key] = val
|
|
56
|
+
|
|
57
|
+
stats.increment(
|
|
58
|
+
metric,
|
|
59
|
+
amount=value,
|
|
60
|
+
unit=unit if unit else "count",
|
|
61
|
+
tags=tag_dict if tag_dict else None,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
rprint(
|
|
65
|
+
f"[green]✓[/green] 已添加数据: {metric}={value}" + (f" {unit}" if unit else "")
|
|
66
|
+
)
|
|
67
|
+
if tag_dict:
|
|
68
|
+
rprint(f" 标签: {tag_dict}")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@app.command()
|
|
72
|
+
def inc(
|
|
73
|
+
metric: str = typer.Argument(..., help="指标名称"),
|
|
74
|
+
amount: int = typer.Option(1, "--amount", "-a", help="增加的数量"),
|
|
75
|
+
tags: Optional[List[str]] = typer.Option(
|
|
76
|
+
None, "--tag", "-t", help="标签,格式: key=value"
|
|
77
|
+
),
|
|
78
|
+
):
|
|
79
|
+
"""增加计数型指标"""
|
|
80
|
+
stats = StatsManager(_get_stats_dir())
|
|
81
|
+
|
|
82
|
+
# 解析标签
|
|
83
|
+
tag_dict = {}
|
|
84
|
+
if tags:
|
|
85
|
+
for tag in tags:
|
|
86
|
+
if "=" in tag:
|
|
87
|
+
key, val = tag.split("=", 1)
|
|
88
|
+
tag_dict[key] = val
|
|
89
|
+
|
|
90
|
+
stats.increment(metric, amount=amount, tags=tag_dict if tag_dict else None)
|
|
91
|
+
|
|
92
|
+
rprint(f"[green]✓[/green] 已增加计数: {metric} +{amount}")
|
|
93
|
+
if tag_dict:
|
|
94
|
+
rprint(f" 标签: {tag_dict}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@app.command()
|
|
98
|
+
def show(
|
|
99
|
+
metric: Optional[str] = typer.Argument(None, help="指标名称,不指定则显示所有"),
|
|
100
|
+
aggregation: str = typer.Option(
|
|
101
|
+
"hourly", "--agg", "-a", help="聚合方式: hourly/daily"
|
|
102
|
+
),
|
|
103
|
+
tags: Optional[List[str]] = typer.Option(
|
|
104
|
+
None, "--tag", "-t", help="标签过滤,格式: key=value"
|
|
105
|
+
),
|
|
106
|
+
):
|
|
107
|
+
"""显示统计数据"""
|
|
108
|
+
stats = StatsManager(_get_stats_dir())
|
|
109
|
+
|
|
110
|
+
# 解析标签
|
|
111
|
+
tag_dict = {}
|
|
112
|
+
if tags:
|
|
113
|
+
for tag in tags:
|
|
114
|
+
if "=" in tag:
|
|
115
|
+
key, val = tag.split("=", 1)
|
|
116
|
+
tag_dict[key] = val
|
|
117
|
+
|
|
118
|
+
stats.show(
|
|
119
|
+
metric_name=metric,
|
|
120
|
+
|
|
121
|
+
aggregation=aggregation,
|
|
122
|
+
tags=tag_dict if tag_dict else None,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.command()
|
|
127
|
+
def plot(
|
|
128
|
+
metric: Optional[str] = typer.Argument(
|
|
129
|
+
None, help="指标名称(可选,不指定则根据标签过滤所有匹配的指标)"
|
|
130
|
+
),
|
|
131
|
+
aggregation: str = typer.Option(
|
|
132
|
+
"hourly", "--agg", "-a", help="聚合方式: hourly/daily"
|
|
133
|
+
),
|
|
134
|
+
width: Optional[int] = typer.Option(None, "--width", "-w", help="图表宽度"),
|
|
135
|
+
height: Optional[int] = typer.Option(None, "--height", "-H", help="图表高度"),
|
|
136
|
+
tags: Optional[List[str]] = typer.Option(
|
|
137
|
+
None, "--tag", "-t", help="标签过滤,格式: key=value"
|
|
138
|
+
),
|
|
139
|
+
):
|
|
140
|
+
"""绘制指标折线图,支持根据标签过滤显示多个指标"""
|
|
141
|
+
stats = StatsManager(_get_stats_dir())
|
|
142
|
+
|
|
143
|
+
# 解析标签
|
|
144
|
+
tag_dict = {}
|
|
145
|
+
if tags:
|
|
146
|
+
for tag in tags:
|
|
147
|
+
if "=" in tag:
|
|
148
|
+
key, val = tag.split("=", 1)
|
|
149
|
+
tag_dict[key] = val
|
|
150
|
+
|
|
151
|
+
stats.plot(
|
|
152
|
+
metric_name=metric,
|
|
153
|
+
aggregation=aggregation,
|
|
154
|
+
width=width,
|
|
155
|
+
height=height,
|
|
156
|
+
tags=tag_dict if tag_dict else None,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@app.command()
|
|
161
|
+
def list():
|
|
162
|
+
"""列出所有指标"""
|
|
163
|
+
stats = StatsManager(_get_stats_dir())
|
|
164
|
+
metrics = stats.list_metrics()
|
|
165
|
+
|
|
166
|
+
if not metrics:
|
|
167
|
+
rprint("[yellow]没有找到任何指标[/yellow]")
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# 创建表格
|
|
171
|
+
table = Table(title="统计指标列表")
|
|
172
|
+
table.add_column("指标名称", style="cyan")
|
|
173
|
+
table.add_column("单位", style="green")
|
|
174
|
+
table.add_column("最后更新", style="yellow")
|
|
175
|
+
table.add_column("7天数据点", style="magenta")
|
|
176
|
+
table.add_column("标签", style="blue")
|
|
177
|
+
|
|
178
|
+
# 获取每个指标的信息
|
|
179
|
+
end_time = datetime.now()
|
|
180
|
+
start_time = end_time - timedelta(days=7)
|
|
181
|
+
|
|
182
|
+
for metric in metrics:
|
|
183
|
+
info = stats._get_storage().get_metric_info(metric)
|
|
184
|
+
if info:
|
|
185
|
+
unit = info.get("unit", "-")
|
|
186
|
+
last_updated = info.get("last_updated", "-")
|
|
187
|
+
|
|
188
|
+
# 格式化时间
|
|
189
|
+
if last_updated != "-":
|
|
190
|
+
try:
|
|
191
|
+
dt = datetime.fromisoformat(last_updated)
|
|
192
|
+
last_updated = dt.strftime("%Y-%m-%d %H:%M")
|
|
193
|
+
except Exception:
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
# 获取数据点数和标签
|
|
197
|
+
records = stats._get_storage().get_metrics(metric, start_time, end_time)
|
|
198
|
+
count = len(records)
|
|
199
|
+
|
|
200
|
+
# 收集所有唯一的标签
|
|
201
|
+
all_tags = {}
|
|
202
|
+
for record in records:
|
|
203
|
+
tags = record.get("tags", {})
|
|
204
|
+
for k, v in tags.items():
|
|
205
|
+
if k not in all_tags:
|
|
206
|
+
all_tags[k] = set()
|
|
207
|
+
all_tags[k].add(v)
|
|
208
|
+
|
|
209
|
+
# 格式化标签显示
|
|
210
|
+
tag_str = ""
|
|
211
|
+
if all_tags:
|
|
212
|
+
tag_parts = []
|
|
213
|
+
for k, values in sorted(all_tags.items()):
|
|
214
|
+
# 使用内置的list函数
|
|
215
|
+
values_list = sorted(builtins.list(values))
|
|
216
|
+
if len(values_list) == 1:
|
|
217
|
+
tag_parts.append(f"{k}={values_list[0]}")
|
|
218
|
+
else:
|
|
219
|
+
# 转义方括号以避免Rich markup错误
|
|
220
|
+
tag_parts.append(f"{k}=\\[{', '.join(values_list)}\\]")
|
|
221
|
+
tag_str = ", ".join(tag_parts)
|
|
222
|
+
else:
|
|
223
|
+
tag_str = "-"
|
|
224
|
+
|
|
225
|
+
table.add_row(metric, unit, last_updated, str(count), tag_str)
|
|
226
|
+
|
|
227
|
+
console.print(table)
|
|
228
|
+
rprint(f"\n[green]总计: {len(metrics)} 个指标[/green]")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@app.command()
|
|
232
|
+
def clean(
|
|
233
|
+
days: int = typer.Option(30, "--days", "-d", help="保留最近N天的数据"),
|
|
234
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="跳过确认"),
|
|
235
|
+
):
|
|
236
|
+
"""清理旧数据"""
|
|
237
|
+
if not yes:
|
|
238
|
+
confirm = typer.confirm(f"确定要删除 {days} 天前的数据吗?")
|
|
239
|
+
if not confirm:
|
|
240
|
+
rprint("[yellow]已取消操作[/yellow]")
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
stats = StatsManager(_get_stats_dir())
|
|
244
|
+
stats.clean_old_data(days_to_keep=days)
|
|
245
|
+
rprint(f"[green]✓[/green] 已清理 {days} 天前的数据")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@app.command()
|
|
249
|
+
def export(
|
|
250
|
+
metric: str = typer.Argument(..., help="指标名称"),
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
tags: Optional[List[str]] = typer.Option(
|
|
255
|
+
None, "--tag", "-t", help="标签过滤,格式: key=value"
|
|
256
|
+
),
|
|
257
|
+
):
|
|
258
|
+
"""导出统计数据"""
|
|
259
|
+
import json
|
|
260
|
+
import csv
|
|
261
|
+
import sys
|
|
262
|
+
|
|
263
|
+
stats = StatsManager(_get_stats_dir())
|
|
264
|
+
|
|
265
|
+
# 解析标签
|
|
266
|
+
tag_dict = {}
|
|
267
|
+
if tags:
|
|
268
|
+
for tag in tags:
|
|
269
|
+
if "=" in tag:
|
|
270
|
+
key, val = tag.split("=", 1)
|
|
271
|
+
tag_dict[key] = val
|
|
272
|
+
|
|
273
|
+
# 获取数据
|
|
274
|
+
data = stats.get_stats(
|
|
275
|
+
metric_name=metric,
|
|
276
|
+
tags=tag_dict if tag_dict else None,
|
|
277
|
+
)
|
|
278
|
+
|
|
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])
|
|
287
|
+
else:
|
|
288
|
+
rprint("[yellow]没有找到数据[/yellow]", file=sys.stderr)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@app.command()
|
|
292
|
+
def remove(
|
|
293
|
+
metric: str = typer.Argument(..., help="要删除的指标名称"),
|
|
294
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="跳过确认"),
|
|
295
|
+
):
|
|
296
|
+
"""删除指定的指标及其所有数据"""
|
|
297
|
+
if not yes:
|
|
298
|
+
# 显示指标信息供用户确认
|
|
299
|
+
stats = StatsManager(_get_stats_dir())
|
|
300
|
+
metrics = stats.list_metrics()
|
|
301
|
+
|
|
302
|
+
if metric not in metrics:
|
|
303
|
+
rprint(f"[red]错误:指标 '{metric}' 不存在[/red]")
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
# 获取指标的基本信息
|
|
307
|
+
info = stats._get_storage().get_metric_info(metric)
|
|
308
|
+
if info:
|
|
309
|
+
unit = info.get("unit", "-")
|
|
310
|
+
last_updated = info.get("last_updated", "-")
|
|
311
|
+
|
|
312
|
+
rprint("\n[yellow]准备删除指标:[/yellow]")
|
|
313
|
+
rprint(f" 名称: {metric}")
|
|
314
|
+
rprint(f" 单位: {unit}")
|
|
315
|
+
rprint(f" 最后更新: {last_updated}")
|
|
316
|
+
|
|
317
|
+
confirm = typer.confirm(f"\n确定要删除指标 '{metric}' 及其所有数据吗?")
|
|
318
|
+
if not confirm:
|
|
319
|
+
rprint("[yellow]已取消操作[/yellow]")
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
stats = StatsManager(_get_stats_dir())
|
|
323
|
+
success = stats.remove_metric(metric)
|
|
324
|
+
|
|
325
|
+
if success:
|
|
326
|
+
rprint(f"[green]✓[/green] 已成功删除指标: {metric}")
|
|
327
|
+
else:
|
|
328
|
+
rprint(f"[red]✗[/red] 删除失败:指标 '{metric}' 不存在")
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@app.command()
|
|
332
|
+
def demo():
|
|
333
|
+
"""运行演示,展示统计模块的功能"""
|
|
334
|
+
import random
|
|
335
|
+
import time
|
|
336
|
+
|
|
337
|
+
console.print("[bold cyan]Jarvis 统计模块演示[/bold cyan]\n")
|
|
338
|
+
|
|
339
|
+
stats = StatsManager(_get_stats_dir())
|
|
340
|
+
|
|
341
|
+
# 添加演示数据
|
|
342
|
+
with console.status("[bold green]正在生成演示数据..."):
|
|
343
|
+
# API响应时间
|
|
344
|
+
for i in range(20):
|
|
345
|
+
response_time = random.uniform(0.1, 2.0)
|
|
346
|
+
status_code = random.choice(["200", "404", "500"])
|
|
347
|
+
stats.increment(
|
|
348
|
+
"demo_response_time",
|
|
349
|
+
amount=response_time,
|
|
350
|
+
unit="seconds",
|
|
351
|
+
tags={"status": status_code},
|
|
352
|
+
)
|
|
353
|
+
time.sleep(0.05)
|
|
354
|
+
|
|
355
|
+
# 访问计数
|
|
356
|
+
for i in range(30):
|
|
357
|
+
endpoint = random.choice(["/api/users", "/api/posts", "/api/admin"])
|
|
358
|
+
stats.increment("demo_api_calls", tags={"endpoint": endpoint})
|
|
359
|
+
time.sleep(0.05)
|
|
360
|
+
|
|
361
|
+
rprint("[green]✓[/green] 演示数据生成完成\n")
|
|
362
|
+
|
|
363
|
+
# 显示数据
|
|
364
|
+
console.rule("[bold blue]指标列表")
|
|
365
|
+
stats.show()
|
|
366
|
+
|
|
367
|
+
console.rule("[bold blue]响应时间详情")
|
|
368
|
+
stats.show("demo_response_time", last_hours=1)
|
|
369
|
+
|
|
370
|
+
console.rule("[bold blue]API调用折线图")
|
|
371
|
+
stats.plot("demo_api_calls", last_hours=1, height=10)
|
|
372
|
+
|
|
373
|
+
console.rule("[bold blue]响应时间汇总")
|
|
374
|
+
stats.show("demo_response_time", last_hours=1, format="summary")
|
|
375
|
+
|
|
376
|
+
rprint("\n[green]✓[/green] 演示完成!")
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def main():
|
|
380
|
+
"""主入口函数"""
|
|
381
|
+
# 初始化环境,防止设置初始化太迟
|
|
382
|
+
init_env("欢迎使用 Jarvis-Stats,您的统计分析工具已准备就绪!", None)
|
|
383
|
+
app()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
if __name__ == "__main__":
|
|
387
|
+
main()
|