Skip to main content

paygo-ptu-cost-analyzer

Headless Azure OpenAI PAYGO-vs-PTU cost analysis and sizing report. Wraps the analysis library of aiappsgbb/ptu-paygo-mix (NOT its Streamlit UI). Supports CSV token-usage input and Log Analytics queries via vendored KQL against `AzureMetrics`. Produces a markdown report, JSON data, and charts. Bundles a synthetic-data generator for offline demos. USE FOR: ptu sizing, paygo vs ptu, provisioned throughput unit recommendation, AOAI capacity planning, TPM percentile report, spillover cost estimate, log analytics token query, AzureMetrics PTU, CSV-based PTU sizing, headless cost report, paygo-ptu-cost-analyzer. DO NOT USE FOR: capacity reservation purchase workflow, deploying the upstream Streamlit UI (use the upstream repo), real-time TPM monitoring (use azure-monitor-query directly), pricing for non-Azure-OpenAI services.

Jump to install

Source facts

Repository
aiappsgbb/awesome-gbb
Last source activity
September 16, 2026 at 10:12
Detected SKILL.md language
English
Stars
6
Forks
3

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
12 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
paygo-ptu-cost-analyzer
description
Headless Azure OpenAI PAYGO-vs-PTU cost analysis and sizing report. Wraps the analysis library of aiappsgbb/ptu-paygo-mix (NOT its Streamlit UI). Supports CSV token-usage input and Log Analytics queries via vendored KQL against `AzureMetrics`. Produces a markdown report, JSON data, and charts. Bundles a synthetic-data generator for offline demos. USE FOR: ptu sizing, paygo vs ptu, provisioned throughput unit recommendation, AOAI capacity planning, TPM percentile report, spillover cost estimate, log analytics token query, AzureMetrics PTU, CSV-based PTU sizing, headless cost report, paygo-ptu-cost-analyzer. DO NOT USE FOR: capacity reservation purchase workflow, deploying the upstream Streamlit UI (use the upstream repo), real-time TPM monitoring (use azure-monitor-query directly), pricing for non-Azure-OpenAI services.
metadata
{"version":"1.1.1"}
# PAYGO vs PTU Cost Analyzer Headless cost-analysis skill for **Azure OpenAI**. Given a window of per-minute token usage, it computes the percentile TPM envelope, prices both **PAYGO** and **PTU** scenarios per percentile, estimates spillover costs, and emits a markdown + JSON + PNG report. This is the analysis core of **aiappsgbb/ptu-paygo-mix** wrapped in a CLI; the upstream Streamlit UI is **excluded by design** (this skill runs in a terminal, in CI, or inside an agent — never as a web app). > **Sister capability.** If you actually want the interactive UI, use > the upstream repo directly (`uv run streamlit run app/streamlit_app.py`). > This skill is for the cases where the seller / analyst / agent needs > a **report file** — over chat, in a PR, or as part of an automated > capacity-planning rollup. > **Continuous monitoring companion.** [`foundry-cost-monitoring`](../foundry-cost-monitoring/SKILL.md) > consumes live OTel `gen_ai.usage.*` spans for per-agent / per-project > cost projection — use it when the FinOps question is "what's my > spend right now?" rather than "how should I size PTU?" --- ## Two data paths | Path | When to use | Auth needed | |------|-------------|-------------| | `--csv <path>` | Customer already exported their token usage (e.g. via the upstream `docs/sample_kql_queries.md`, Excel, Cost Mgmt export). | None. | | `--workspace <id>` | You have direct read access on the Log Analytics workspace ingesting Cognitive Services metrics. | `DefaultAzureCredential` (az login / managed identity / env vars / VS Code) with `Log Analytics Reader` on the workspace. | | `--synthetic` | Offline demo, regression test, or you need to show the report shape before real data lands. | None. | All three paths converge on the same `run_analysis()` and `render_report()` so the output contract is identical. ### Required CSV schema | Column | Type | Notes | |--------|------|-------| | `minute_bin` | datetime (parseable) | Per-minute bucket. | | `input_tokens_sum` | int | Total input tokens in the bucket (includes cached). | | `cached_tokens_sum` | int | Subset of `input_tokens_sum` that hit the prompt cache. | | `output_tokens_sum` | int | Generated tokens. | `normalize_usage_dataframe()` validates the schema, parses datetimes, strips thousands separators, and sorts by `minute_bin`. Bad input fails loudly with a descriptive `ValueError`. --- ## Quickstart ```bash # 0. One-time deps (matches references/upstream-pin.md) python3 -m venv .venv && source .venv/bin/activate pip install "pandas~=3.0.3" "numpy~=2.4.6" "matplotlib~=3.11.0" \ "azure-identity~=1.25.3" "azure-monitor-query~=2.0.0" # 1. Offline demo — uses the bundled synthetic generator python references/run_report.py \ --synthetic --days 3 \ --model gpt-5.4 --tier global --ptu-term monthly \ --out-dir ./paygo-ptu-report # 2. Customer CSV python references/run_report.py \ --csv ./customer_tpm_april.csv \ --model gpt-5.2 --tier data_zone --ptu-term yearly \ --percentiles 75,90,95,99 \ --out-dir ./customer-report # 3. Live Log Analytics (requires az login + workspace Reader role) python references/run_report.py \ --workspace 11111111-2222-3333-4444-555555555555 \ --time-range 14d \ --deployment my-gpt-5-4-prod \ --model gpt-5.4 \ --out-dir ./live-report # A bundled tiny CSV is also available for smoke tests: python references/run_report.py \ --csv references/sample_input.csv --model gpt-5.4 --out-dir /tmp/smoke ``` Every run prints the 4 absolute output paths to stdout and exits 0 on success, non-zero on validation errors. --- ## Output contract Every run produces exactly these four files under `--out-dir`: | File | Contents | |------|----------| | `report.md` | Narrative report: input window summary, percentile TPM table, PAYGO baseline breakdown, per-percentile PTU sizing scenarios (PTUs, capacity, covered-token %, base cost, spillover cost, total, ∆ vs PAYGO), recommendation paragraph, pricing snapshot. | | `report.json` | Full `run_analysis()` dict + run metadata (`model`, `tier`, `ptu_term`, `source`, `percentiles`, `pricing_snapshot`, `generated_at`). Reconsumable by downstream agents. | | `tpm_over_time.png` | Line chart of `total_tpm` over `minute_bin` with horizontal dashed lines for each percentile's PTU capacity (so you can eyeball spillover). | | `ptu_sizing_scenarios.png` | Grouped bar chart per percentile: PTU base cost (blue) stacked with spillover cost (orange), plus a green dashed line at the PAYGO baseline. Each bar labelled with the total. | The markdown report always contains the substrings `"PTU Sizing"`, `"PAYGO Baseline"`, and `"p95"` (the freshness validator asserts these). --- ## CLI reference (`run_report.py`) | Flag | Default | Notes | |------|---------|-------| | `--csv <path>` | — | CSV matching the required schema. Mutually exclusive with `--workspace` / `--synthetic`. | | `--workspace <id>` | — | Log Analytics workspace GUID. Mutually exclusive. | | `--synthetic` | — | Use the bundled synthetic generator. Mutually exclusive. | | `--kql <path>` | `references/queries/default.kql` | KQL template; placeholders `__TIME_RANGE__`, `__DEPLOYMENT_FILTER__`. | | `--time-range` | `7d` | `Nd` or `Nh`. Applied to KQL + Log Analytics timespan. | | `--deployment` | _empty_ | Filter to a specific Cognitive Services deployment. Optional. | | `--days` | `7` | Synthetic data duration. | | `--model` | `gpt-5.4` | Slug from `references/analyzer/models.json`. | | `--tier` | `global` | `global` or `data_zone` (per the catalog). | | `--ptu-term` | `monthly` | `monthly` or `yearly`. Switches the cost columns + recommendation. | | `--percentiles` | `50,75,90,95,99` | Comma-separated, in `(0,100)`. | | `--ptu-output-weight` | model's `ptu.output_weight` | Multiplier on output-token TPM when sizing PTUs. Defaults to the selected model's catalog `ptu.output_weight` (e.g. `6` for gpt-5.4); pass a value to override (e.g. `1.0` for the old flat behaviour). Falls back to `1` if the model has no `output_weight`. | | `--out-dir` | `./paygo-ptu-report` | Created if absent. | --- ## Pricing catalog (`references/analyzer/models.json`) Vendored from the upstream repo. Each model entry carries: - `paygo.global` + optional `paygo.data_zone` → `input_per_m`, `cached_input_per_m`, `output_per_m`, plus optional `priority_processing` rates - `ptu.capacity_tpm`, `ptu.min_deployment`, `ptu.increment`, `ptu.output_weight` - `ptu.global` + optional `ptu.data_zone` → `monthly_price`, `yearly_price` `ptu.output_weight` (typically 4–8) scales output-token TPM when sizing PTUs; `--ptu-output-weight` defaults to it (see the CLI table). As of the `2636464` re-vendor the catalog also ships **gpt-5.5** and **gpt-5.4-mini**, and **gpt-5.4** now carries `data_zone` pricing (so `--tier data_zone` resolves for it). To use a custom catalog: edit `models.json` in place (it's a vendored file — not auto-refreshed). The upstream-pin tracks the SHA of the upstream repo so the catalog can be re-vendored when prices drift. > **Pricing drift is real.** Microsoft updates Azure OpenAI pricing > several times a year. Treat the bundled `models.json` as a **starting > point** for a customer conversation, not a quote — always confirm > against the current pricing page for the customer's region and the > region of their reservation. --- ## Vendored layout ``` references/ ├── analyzer/ # Analysis core vendored from aiappsgbb/ptu-paygo-mix @ 2636464 │ ├── __init__.py # Re-exports the public API │ ├── analysis.py # run_analysis() — pure pandas/numpy │ ├── data.py # load/normalize/KQL helpers (frozen @ e1786f8, Streamlit stripped) │ ├── formatting.py # fmt_num / fmt_cost │ └── models.json # PTU + PAYGO pricing catalog ├── queries/ │ ├── default.kql # AzureMetrics → required schema (InputTokens path) │ └── active_tokens.kql # Alternative: derives cached from ActiveTokens ├── render_report.py # md + json + 2× png (matplotlib, Agg backend) ├── run_report.py # CLI entry point ├── sample_input.csv # ~10 KB demo CSV (240 rows, reproducible seed) └── upstream-pin.md # Tier-B freshness contract ``` `analysis.py`, `formatting.py`, and `models.json` are byte-identical to upstream `@ 2636464`. Three files **intentionally diverge**: - `data.py` — **frozen at the `e1786f8` shape** (Streamlit stripped: the `import streamlit as st` line and both `@st.cache_data` / `@st.cache_data(ttl=300)` decorators removed). Upstream **deleted** its live Log-Analytics / KQL path at commit `14a5bec`; this skill keeps it to power `--workspace` mode, so `data.py` is **not** re-vendored from newer SHAs. See `upstream-pin.md` KI-002. - `queries/*.kql` — **retained**; upstream deleted the `kql/` directory at `14a5bec`. These drive the `--workspace` path. - `__init__.py` — exported names match the `e1786f8` upstream plus `REQUIRED_COLUMNS`, `MODELS_CONFIG_PATH`, `time_range_to_timedelta`. Re-vendoring the analysis core is a manual chore signalled by SHA drift in `upstream-pin.md` (the upstream repo is private — a token with read access is needed to clone it). --- ## KQL path notes The bundled query targets `AzureMetrics` for the Cognitive Services resource provider. Prereqs on the customer side: 1. **Diagnostic settings** on the Azure OpenAI resource → route metrics to a Log Analytics workspace. 2. Enable the metric categories `InputTokens`, `ProcessedPromptTokens`, `GeneratedTokens` (and optionally `ActiveTokens`). 3. The caller needs `Log Analytics Reader` on the workspace (or `Reader` at the subscription scope). 4. Allow at least 15 minutes for fresh metrics to land in `AzureMetrics`. ### Two bundled queries — pick by metric availability | File | Cached-token derivation | Use when | |------|-------------------------|----------| | `references/queries/default.kql` | `InputTokens − ProcessedPromptTokens` | The standard path. Works on any Cognitive Services deployment with `InputTokens` enabled. | | `references/queries/active_tokens.kql` | `ProcessedPromptTokens − ActiveTokens` | Fallback when `InputTokens` is missing or zero — uses the `ActiveTokens` metric (non-cached tokens) as the subtractor. Some older diagnostic-setting configurations only expose this path. | Pass either with `--kql`: ```bash python references/run_report.py \ --workspace <id> --kql references/queries/active_tokens.kql \ --model gpt-5.4 --out-dir ./report ``` If your workspace uses `AzureDiagnostics` instead of `AzureMetrics`, write a custom KQL that emits the same 4-column schema and pass it with `--kql`. See `docs/sample_kql_queries.md` in the upstream repo for several alternative shapes (request-level breakdown, PTU utilisation, error analysis). --- ## Known caveats - **Pricing drift:** as above — re-vendor `models.json` when upstream bumps. `upstream-pin.md` tracks the SHA. - **`AzureMetrics.Dimensions` shape varies** by API version — the bundled default KQL coalesces `DimensionJson`, `Dimensions`, and `Tags`, which covers everything we've seen. If you see empty `ModelDeploymentName` after a query, dump the raw row and adjust. - **Synthetic generator's anchor date is `2026-01-19`** (Monday). The synth produces realistic business-hours + weekend + burst patterns starting from that fixed timestamp — useful for reproducible demos, but obvious if you ship a customer report without disclosing it. - **`data.py` + `queries/*.kql` are frozen at `e1786f8`** — upstream removed the live Log-Analytics / KQL path (commit `14a5bec`); this skill keeps it for `--workspace` mode, so those files are **not** re-vendored from newer SHAs (only `analysis.py` / `formatting.py` / `models.json` are). The `data.py` Streamlit strip is part of that frozen baseline; the validation script catches any reintroduction because the import would fail outside a Streamlit context. See `upstream-pin.md` KI-002. --- ## References - Upstream repo: <https://github.com/aiappsgbb/ptu-paygo-mix> (SHA pinned in `references/upstream-pin.md`) - Azure OpenAI PTU docs: <https://learn.microsoft.com/azure/ai-services/openai/how-to/provisioned-throughput-onboarding> - AzureMetrics schema: <https://learn.microsoft.com/azure/azure-monitor/reference/tables/azuremetrics> - Related awesome-gbb skills: `azure-tenant-isolation` (for `--workspace` runs), `foundry-observability` (downstream consumers of the JSON report).
View on GitHub