codex-stats 1.4.2__tar.gz → 1.4.3__tar.gz
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.
- {codex_stats-1.4.2/src/codex_stats.egg-info → codex_stats-1.4.3}/PKG-INFO +2 -2
- {codex_stats-1.4.2 → codex_stats-1.4.3}/README.md +1 -1
- {codex_stats-1.4.2 → codex_stats-1.4.3}/pyproject.toml +1 -1
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/__init__.py +1 -1
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/display.py +113 -41
- {codex_stats-1.4.2 → codex_stats-1.4.3/src/codex_stats.egg-info}/PKG-INFO +2 -2
- {codex_stats-1.4.2 → codex_stats-1.4.3}/tests/test_cli.py +1 -1
- {codex_stats-1.4.2 → codex_stats-1.4.3}/tests/test_metrics.py +4 -2
- {codex_stats-1.4.2 → codex_stats-1.4.3}/LICENSE +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/setup.cfg +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/__main__.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/cli.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/completions.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/config.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/ingest.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/metrics.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/models.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/otel.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/transfer.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats/watch_state.py +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats.egg-info/SOURCES.txt +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats.egg-info/dependency_links.txt +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats.egg-info/entry_points.txt +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats.egg-info/requires.txt +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/src/codex_stats.egg-info/top_level.txt +0 -0
- {codex_stats-1.4.2 → codex_stats-1.4.3}/tests/test_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-stats
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.3
|
|
4
4
|
Summary: Local AI usage observability CLI for Codex sessions.
|
|
5
5
|
Author: Vivek
|
|
6
6
|
License: MIT
|
|
@@ -110,7 +110,7 @@ python3 -m pip install codex-stats
|
|
|
110
110
|
- `codex-stats report weekly --format html`
|
|
111
111
|
Generate a standalone HTML report for sharing, including inline charts.
|
|
112
112
|
- `codex-stats report weekly --format svg`
|
|
113
|
-
Generate a bundle of smaller standalone SVG assets in the current working directory,
|
|
113
|
+
Generate a bundle of four smaller standalone SVG assets in the current working directory: summary, cost, focus, and project snapshots.
|
|
114
114
|
- `codex-stats report weekly --project backend-api`
|
|
115
115
|
Generate a weekly report for one project.
|
|
116
116
|
- `codex-stats report weekly --format markdown --output weekly-report.md`
|
|
@@ -83,7 +83,7 @@ python3 -m pip install codex-stats
|
|
|
83
83
|
- `codex-stats report weekly --format html`
|
|
84
84
|
Generate a standalone HTML report for sharing, including inline charts.
|
|
85
85
|
- `codex-stats report weekly --format svg`
|
|
86
|
-
Generate a bundle of smaller standalone SVG assets in the current working directory,
|
|
86
|
+
Generate a bundle of four smaller standalone SVG assets in the current working directory: summary, cost, focus, and project snapshots.
|
|
87
87
|
- `codex-stats report weekly --project backend-api`
|
|
88
88
|
Generate a weekly report for one project.
|
|
89
89
|
- `codex-stats report weekly --format markdown --output weekly-report.md`
|
|
@@ -792,49 +792,11 @@ def format_report_svg_assets(report: ReportData, daily_points: list[DailyPoint]
|
|
|
792
792
|
title = f"Codex Stats {report.period.title()} Report"
|
|
793
793
|
if report.project_name:
|
|
794
794
|
title = f"{title}: {report.project_name}"
|
|
795
|
-
daily_points = daily_points or []
|
|
796
795
|
assets = {
|
|
797
796
|
"summary-card": _format_summary_card_svg(report, title),
|
|
798
|
-
"
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
chart_svg=_svg_line_chart(
|
|
802
|
-
[(point.day[5:], float(point.total_tokens)) for point in daily_points],
|
|
803
|
-
stroke="#0f766e",
|
|
804
|
-
fill="rgba(15, 118, 110, 0.14)",
|
|
805
|
-
value_formatter=lambda value: f"{int(value):,}",
|
|
806
|
-
),
|
|
807
|
-
),
|
|
808
|
-
"daily-cost": _wrap_chart_svg(
|
|
809
|
-
title="Daily Cost Trend",
|
|
810
|
-
subtitle=title,
|
|
811
|
-
chart_svg=_svg_line_chart(
|
|
812
|
-
[(point.day[5:], float(point.estimated_cost_usd)) for point in daily_points],
|
|
813
|
-
stroke="#b45309",
|
|
814
|
-
fill="rgba(180, 83, 9, 0.14)",
|
|
815
|
-
value_formatter=lambda value: f"${value:.2f}",
|
|
816
|
-
),
|
|
817
|
-
),
|
|
818
|
-
"project-share": _wrap_chart_svg(
|
|
819
|
-
title="Project Share",
|
|
820
|
-
subtitle=title,
|
|
821
|
-
chart_svg=_svg_bar_chart(
|
|
822
|
-
[(entry.name, float(entry.total_tokens)) for entry in report.projects[:5]],
|
|
823
|
-
bar_color="#0f766e",
|
|
824
|
-
value_formatter=lambda value: f"{int(value):,}",
|
|
825
|
-
empty_label="No project breakdown for this scoped report.",
|
|
826
|
-
),
|
|
827
|
-
),
|
|
828
|
-
"top-sessions": _wrap_chart_svg(
|
|
829
|
-
title="Top Sessions",
|
|
830
|
-
subtitle=title,
|
|
831
|
-
chart_svg=_svg_bar_chart(
|
|
832
|
-
[(f"{entry.project_name} / {entry.model or 'unknown'}", float(entry.total_tokens)) for entry in report.top_sessions[:5]],
|
|
833
|
-
bar_color="#b45309",
|
|
834
|
-
value_formatter=lambda value: f"{int(value):,}",
|
|
835
|
-
empty_label="No top session data available.",
|
|
836
|
-
),
|
|
837
|
-
),
|
|
797
|
+
"cost-card": _format_cost_card_svg(report, title),
|
|
798
|
+
"focus-card": _format_focus_card_svg(report, title),
|
|
799
|
+
"projects-card": _format_projects_card_svg(report, title),
|
|
838
800
|
}
|
|
839
801
|
return assets
|
|
840
802
|
|
|
@@ -1004,6 +966,116 @@ def _format_summary_card_svg(report: ReportData, title: str) -> str:
|
|
|
1004
966
|
</svg>"""
|
|
1005
967
|
|
|
1006
968
|
|
|
969
|
+
def _format_cost_card_svg(report: ReportData, title: str) -> str:
|
|
970
|
+
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-label="{escape(title)} cost card">
|
|
971
|
+
<rect width="1200" height="630" fill="#f7efe2"/>
|
|
972
|
+
<rect x="40" y="36" width="1120" height="558" rx="32" fill="#fffaf2" stroke="rgba(66,48,31,0.10)"/>
|
|
973
|
+
<text x="76" y="92" font-size="18" letter-spacing="3" fill="#b45309">CODEX STATS</text>
|
|
974
|
+
<text x="76" y="150" font-size="56" font-weight="700" fill="#1f1a17">Cost Snapshot</text>
|
|
975
|
+
<text x="76" y="194" font-size="24" fill="#6d645d">{escape(title)}</text>
|
|
976
|
+
|
|
977
|
+
<rect x="76" y="244" width="254" height="154" rx="28" fill="#b45309"/>
|
|
978
|
+
<text x="104" y="286" font-size="16" letter-spacing="2" fill="#ffedd5">TODAY</text>
|
|
979
|
+
<text x="104" y="348" font-size="48" font-weight="700" fill="#ffffff">${report.costs.today_cost_usd:.2f}</text>
|
|
980
|
+
<text x="104" y="378" font-size="20" fill="#ffedd5">Current day estimate</text>
|
|
981
|
+
|
|
982
|
+
<rect x="348" y="244" width="254" height="154" rx="28" fill="#fffaf2" stroke="rgba(66,48,31,0.10)"/>
|
|
983
|
+
<text x="376" y="286" font-size="16" letter-spacing="2" fill="#6d645d">WEEK</text>
|
|
984
|
+
<text x="376" y="348" font-size="48" font-weight="700" fill="#1f1a17">${report.costs.week_cost_usd:.2f}</text>
|
|
985
|
+
<text x="376" y="378" font-size="20" fill="#6d645d">Rolling weekly total</text>
|
|
986
|
+
|
|
987
|
+
<rect x="620" y="244" width="254" height="154" rx="28" fill="#fffaf2" stroke="rgba(66,48,31,0.10)"/>
|
|
988
|
+
<text x="648" y="286" font-size="16" letter-spacing="2" fill="#6d645d">MONTH</text>
|
|
989
|
+
<text x="648" y="348" font-size="48" font-weight="700" fill="#1f1a17">${report.costs.month_cost_usd:.2f}</text>
|
|
990
|
+
<text x="648" y="378" font-size="20" fill="#6d645d">Rolling monthly total</text>
|
|
991
|
+
|
|
992
|
+
<rect x="892" y="244" width="236" height="154" rx="28" fill="#115e59"/>
|
|
993
|
+
<text x="920" y="286" font-size="16" letter-spacing="2" fill="#d7faf5">PROJECTED</text>
|
|
994
|
+
<text x="920" y="348" font-size="44" font-weight="700" fill="#ffffff">${report.costs.projected_monthly_cost_usd:.2f}</text>
|
|
995
|
+
<text x="920" y="378" font-size="20" fill="#d7faf5">Monthly run rate</text>
|
|
996
|
+
|
|
997
|
+
<rect x="76" y="436" width="1052" height="112" rx="28" fill="#fffaf2" stroke="rgba(66,48,31,0.10)"/>
|
|
998
|
+
<text x="104" y="474" font-size="18" letter-spacing="2" fill="#6d645d">HIGHEST SESSION</text>
|
|
999
|
+
<text x="104" y="522" font-size="38" font-weight="700" fill="#1f1a17">${report.costs.highest_session_cost_usd:.2f}</text>
|
|
1000
|
+
<text x="420" y="522" font-size="24" fill="#4b4036">Possible savings ${report.insights.possible_savings_usd:.2f} • Suggestion: {escape(report.insights.suggestion[:44])}</text>
|
|
1001
|
+
|
|
1002
|
+
<text x="1128" y="580" text-anchor="end" font-size="18" fill="#6d645d">Generated by codex-stats</text>
|
|
1003
|
+
</svg>"""
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
def _format_focus_card_svg(report: ReportData, title: str) -> str:
|
|
1007
|
+
anomalies = report.insights.anomalies[:3] or ["No active anomalies"]
|
|
1008
|
+
recommendations = report.insights.recommendations[:3] or ["Usage looks healthy."]
|
|
1009
|
+
anomaly_lines = "".join(
|
|
1010
|
+
f'<text x="90" y="{244 + index * 46}" font-size="24" fill="#8a4b0f">• {escape(item)}</text>'
|
|
1011
|
+
for index, item in enumerate(anomalies)
|
|
1012
|
+
)
|
|
1013
|
+
recommendation_lines = "".join(
|
|
1014
|
+
f'<text x="620" y="{244 + index * 46}" font-size="24" fill="#1f1a17">• {escape(item)}</text>'
|
|
1015
|
+
for index, item in enumerate(recommendations)
|
|
1016
|
+
)
|
|
1017
|
+
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-label="{escape(title)} focus card">
|
|
1018
|
+
<rect width="1200" height="630" fill="#f2ece3"/>
|
|
1019
|
+
<rect x="40" y="36" width="1120" height="558" rx="32" fill="#fffaf2" stroke="rgba(66,48,31,0.10)"/>
|
|
1020
|
+
<text x="76" y="92" font-size="18" letter-spacing="3" fill="#0f766e">CODEX STATS</text>
|
|
1021
|
+
<text x="76" y="150" font-size="56" font-weight="700" fill="#1f1a17">Focus Card</text>
|
|
1022
|
+
<text x="76" y="194" font-size="24" fill="#6d645d">{escape(title)}</text>
|
|
1023
|
+
|
|
1024
|
+
<rect x="76" y="216" width="492" height="306" rx="28" fill="rgba(180,83,9,0.08)" stroke="rgba(180,83,9,0.18)"/>
|
|
1025
|
+
<text x="90" y="252" font-size="20" letter-spacing="2" fill="#8a4b0f">WHAT LOOKS OFF</text>
|
|
1026
|
+
{anomaly_lines}
|
|
1027
|
+
|
|
1028
|
+
<rect x="604" y="216" width="524" height="306" rx="28" fill="rgba(15,118,110,0.08)" stroke="rgba(15,118,110,0.18)"/>
|
|
1029
|
+
<text x="620" y="252" font-size="20" letter-spacing="2" fill="#115e59">WHAT TO DO NEXT</text>
|
|
1030
|
+
{recommendation_lines}
|
|
1031
|
+
|
|
1032
|
+
<rect x="76" y="540" width="1052" height="40" rx="20" fill="#fffaf2"/>
|
|
1033
|
+
<text x="602" y="566" text-anchor="middle" font-size="20" fill="#4b4036">Avg/request {report.summary.average_tokens_per_request:,.0f} • Cache ratio {escape(_fmt_percent(report.summary.cache_ratio))} • Large sessions {report.insights.large_session_count}</text>
|
|
1034
|
+
</svg>"""
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
def _format_projects_card_svg(report: ReportData, title: str) -> str:
|
|
1038
|
+
project_rows = report.projects[:4]
|
|
1039
|
+
if not project_rows:
|
|
1040
|
+
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-label="{escape(title)} projects card">
|
|
1041
|
+
<rect width="1200" height="630" fill="#f5eee4"/>
|
|
1042
|
+
<rect x="40" y="36" width="1120" height="558" rx="32" fill="#fffaf2" stroke="rgba(66,48,31,0.10)"/>
|
|
1043
|
+
<text x="76" y="92" font-size="18" letter-spacing="3" fill="#0f766e">CODEX STATS</text>
|
|
1044
|
+
<text x="76" y="150" font-size="56" font-weight="700" fill="#1f1a17">Project Snapshot</text>
|
|
1045
|
+
<text x="76" y="194" font-size="24" fill="#6d645d">{escape(title)}</text>
|
|
1046
|
+
<text x="76" y="320" font-size="30" fill="#4b4036">No project breakdown is available for this scoped report.</text>
|
|
1047
|
+
</svg>"""
|
|
1048
|
+
max_tokens = max(entry.total_tokens for entry in project_rows) or 1
|
|
1049
|
+
rows = []
|
|
1050
|
+
for index, entry in enumerate(project_rows):
|
|
1051
|
+
y = 236 + index * 86
|
|
1052
|
+
width = 520 * (entry.total_tokens / max_tokens)
|
|
1053
|
+
rows.append(
|
|
1054
|
+
f'<text x="90" y="{y}" font-size="26" fill="#1f1a17">{escape(entry.name)}</text>'
|
|
1055
|
+
f'<rect x="90" y="{y + 18}" width="520" height="18" rx="9" fill="rgba(72,53,36,0.08)" />'
|
|
1056
|
+
f'<rect x="90" y="{y + 18}" width="{width:.1f}" height="18" rx="9" fill="#0f766e" />'
|
|
1057
|
+
f'<text x="636" y="{y + 34}" font-size="22" fill="#4b4036">{entry.total_tokens:,} tokens</text>'
|
|
1058
|
+
f'<text x="868" y="{y + 34}" font-size="22" fill="#4b4036">{entry.requests} req</text>'
|
|
1059
|
+
f'<text x="1014" y="{y + 34}" font-size="22" fill="#4b4036">${entry.estimated_cost_usd:.2f}</text>'
|
|
1060
|
+
)
|
|
1061
|
+
top_sessions = report.top_sessions[:2]
|
|
1062
|
+
session_lines = "".join(
|
|
1063
|
+
f'<text x="700" y="{260 + index * 42}" font-size="24" fill="#4b4036">• {escape(entry.project_name)} / {escape(entry.model or "unknown")} {entry.total_tokens:,}</text>'
|
|
1064
|
+
for index, entry in enumerate(top_sessions)
|
|
1065
|
+
) or '<text x="700" y="260" font-size="24" fill="#4b4036">No top sessions available.</text>'
|
|
1066
|
+
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-label="{escape(title)} projects card">
|
|
1067
|
+
<rect width="1200" height="630" fill="#f5eee4"/>
|
|
1068
|
+
<rect x="40" y="36" width="1120" height="558" rx="32" fill="#fffaf2" stroke="rgba(66,48,31,0.10)"/>
|
|
1069
|
+
<text x="76" y="92" font-size="18" letter-spacing="3" fill="#0f766e">CODEX STATS</text>
|
|
1070
|
+
<text x="76" y="150" font-size="56" font-weight="700" fill="#1f1a17">Project Snapshot</text>
|
|
1071
|
+
<text x="76" y="194" font-size="24" fill="#6d645d">{escape(title)}</text>
|
|
1072
|
+
{''.join(rows)}
|
|
1073
|
+
<text x="700" y="224" font-size="20" letter-spacing="2" fill="#8a4b0f">TOP SESSIONS</text>
|
|
1074
|
+
{session_lines}
|
|
1075
|
+
<text x="1128" y="580" text-anchor="end" font-size="18" fill="#6d645d">Generated by codex-stats</text>
|
|
1076
|
+
</svg>"""
|
|
1077
|
+
|
|
1078
|
+
|
|
1007
1079
|
def _wrap_chart_svg(title: str, subtitle: str, chart_svg: str) -> str:
|
|
1008
1080
|
chart_body = chart_svg.strip()
|
|
1009
1081
|
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="720" viewBox="0 0 1200 720" role="img" aria-label="{escape(title)}">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-stats
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.3
|
|
4
4
|
Summary: Local AI usage observability CLI for Codex sessions.
|
|
5
5
|
Author: Vivek
|
|
6
6
|
License: MIT
|
|
@@ -110,7 +110,7 @@ python3 -m pip install codex-stats
|
|
|
110
110
|
- `codex-stats report weekly --format html`
|
|
111
111
|
Generate a standalone HTML report for sharing, including inline charts.
|
|
112
112
|
- `codex-stats report weekly --format svg`
|
|
113
|
-
Generate a bundle of smaller standalone SVG assets in the current working directory,
|
|
113
|
+
Generate a bundle of four smaller standalone SVG assets in the current working directory: summary, cost, focus, and project snapshots.
|
|
114
114
|
- `codex-stats report weekly --project backend-api`
|
|
115
115
|
Generate a weekly report for one project.
|
|
116
116
|
- `codex-stats report weekly --format markdown --output weekly-report.md`
|
|
@@ -205,7 +205,7 @@ class CliTestCase(unittest.TestCase):
|
|
|
205
205
|
self.assertIn("report.html", str(output_path))
|
|
206
206
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
207
207
|
svg_dir = _write_report_svg_assets(
|
|
208
|
-
{"summary-card": "<svg></svg>", "
|
|
208
|
+
{"summary-card": "<svg></svg>", "cost-card": "<svg></svg>"},
|
|
209
209
|
tmpdir,
|
|
210
210
|
base_name="codex-stats-weekly",
|
|
211
211
|
)
|
|
@@ -465,8 +465,10 @@ class MetricsTestCase(unittest.TestCase):
|
|
|
465
465
|
self.assertIn("Compact share card for release notes, README embeds, and social posts.", svg)
|
|
466
466
|
self.assertIn("ANOMALIES", svg)
|
|
467
467
|
self.assertIn("Generated by codex-stats", svg)
|
|
468
|
-
self.assertEqual(set(assets), {"summary-card", "
|
|
469
|
-
self.assertIn("
|
|
468
|
+
self.assertEqual(set(assets), {"summary-card", "cost-card", "focus-card", "projects-card"})
|
|
469
|
+
self.assertIn("Cost Snapshot", assets["cost-card"])
|
|
470
|
+
self.assertIn("Focus Card", assets["focus-card"])
|
|
471
|
+
self.assertIn("Project Snapshot", assets["projects-card"])
|
|
470
472
|
|
|
471
473
|
|
|
472
474
|
if __name__ == "__main__":
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|