Skip to content

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Azure AI Cost Calculator

License: MIT .NET .NET Aspire Deploy status PRs Welcome

🌐 Live app: https://aicostcalculator.livethoughts.net (also reachable at the default Azure Container Apps hostname: https://ca-aicost-web.grayisland-f9592069.swedencentral.azurecontainerapps.io)

Azure AI Cost Calculator — solution gallery

A .NET Aspire application that estimates monthly Azure cost for enterprise-grade AI reference architectures — from business metrics, not SKUs.

Users pick a use case template (e.g. Enterprise Chatbot with RAG, Document Intelligence Pipeline), configure business inputs like "daily active users" or "documents per month", and receive a monthly cost estimate broken down by Azure resource, using live prices from the Azure Retail Prices API.

Each use case template is a single YAML file describing:

  1. The business metrics the user configures (units, min/max/step).
  2. The advanced control assumptions that translate business metrics into Azure consumption (tokens per interaction, replicas per user cohort, ingestion ratios, ...).
  3. The Azure resources in the architecture, each with an Azure Retail Prices API filter and an NCalc expression that turns the metrics + variables into a monthly billable quantity.

Add or edit templates in templates/*.yaml, restart the API, and new architectures appear in the gallery. See Authoring a use case template for the full walkthrough or CONTRIBUTING.md for the PR workflow.

Shipped templates

Template Category Primary services
Enterprise Chatbot with RAG Conversational AI Azure OpenAI, AI Search
Document Intelligence Pipeline Document AI Document Intelligence, OpenAI
Call Center Analytics Speech / Analytics Speech, OpenAI, Storage
AI Code Assistant Developer Productivity Azure OpenAI, APIM
Multimodal Image Analysis Vision AI Vision, OpenAI

Missing an architecture? Open an issue or send a PR following the template authoring guide.

Screens

  • Gallery (/): filter by industry, category, and tags; free-text search; each card links to the calculator.
  • Template detail (/templates/{id}): configure business inputs, toggle between Basic (business only) and Advanced (business + assumption multipliers), pick an Azure region, see monthly cost + per-resource breakdown.

Solution layout

AICostCalculator/
├── AGENTS.md                     # spec2cloud orchestrator (unrelated to app runtime)
├── README.md
├── templates/                    # YAML use case templates (edit here to add architectures)
│   ├── enterprise-chatbot-rag.yaml
│   ├── document-intelligence-pipeline.yaml
│   ├── call-center-analytics.yaml
│   ├── ai-code-assistant.yaml
│   └── multimodal-image-analysis.yaml
└── src/
    ├── AICostCalculator.AppHost/          # Aspire orchestrator
    ├── AICostCalculator.ServiceDefaults/  # OpenTelemetry, health checks, service discovery
    ├── AICostCalculator.Shared/           # DTOs shared by API + Web
    ├── AICostCalculator.ApiService/       # Minimal API: templates, prices, calculator
    │   └── Services/
    │       ├── TemplateLoader.cs
    │       ├── AzureRetailPricesClient.cs
    │       └── CostCalculator.cs
    └── AICostCalculator.Web/              # Blazor Server frontend
        └── Components/Pages/
            ├── Home.razor                 # Gallery + filters
            └── TemplateDetail.razor       # Basic/Advanced calculator

Prerequisites

  • .NET SDK 10.0 (also works with .NET 9 — retarget the TargetFramework in every .csproj if you need to).
  • Aspire CLI 13.5.3 or newer (dotnet tool install -g aspire.cli) OR just dotnet run on the AppHost.
  • Internet access from the API service to https://prices.azure.com/api/retail/prices.

The repository ships a src/Directory.Build.props that disables the MSBuild workload resolver. This project does not use any .NET workloads (Android, iOS, MAUI, WASM), and disabling the resolver avoids build hangs/failures on machines with a broken or partial workload set. Leave it in place unless you add workload-based projects.

Run locally

cd src\AICostCalculator.AppHost
dotnet run

The Aspire dashboard prints a URL you can open in your browser (https://localhost:<port>/login?t=...). From the dashboard you can open both the webfrontend (the gallery) and apiservice (Swagger/OpenAPI) directly.

Or, with the Aspire CLI:

aspire run

Troubleshooting aspire run

  • aspire run hangs at "Building AppHost" — your Aspire CLI is older than the AppHost SDK. Update it: dotnet tool update -g aspire.cli.
  • SDK Resolver Failure … workload set … has missing manifests — a broken .NET workload set. This repo bypasses the workload resolver via src/Directory.Build.props; if you removed that file, either put it back or run dotnet workload repair (may require admin).
  • Dashboard opens but a resource is unhealthy — check the resource's Logs tab in the Aspire dashboard for the actual error; the /health endpoint is wired on both services.

API endpoints

Method Route Purpose
GET /api/templates Summaries of all loaded templates
GET /api/templates/{id} Full template incl. business metrics, advanced variables, resources, formulas
GET /api/filters Distinct industries, categories, tags for the gallery filter
GET /api/regions Supported Azure regions
POST /api/calculate Compute cost — body: { templateId, region, currency, businessMetrics, advancedOverrides? }

Authoring a use case template

Every architecture in the gallery is a single YAML file in templates/. The API loads every *.yaml file at startup (see AICostCalculator.ApiService/Services/TemplateLoader.cs) — add a file, restart the API, and it appears in the gallery. There is no code, no migration, and no database.

This section is the end-to-end recipe for building a new template. Follow it in order.

1. Pick a reference architecture

Start from an official Microsoft reference or a well-understood pattern. You need to know, at minimum:

  • Which Azure services participate.
  • What "unit of demand" drives cost (users, requests, documents, minutes of audio, tokens, images, …).
  • Any fixed-cost components (APIM tier, reserved instances, dedicated throughput units).

Good sources:

2. Copy an existing template as a starting point

Copy-Item templates\enterprise-chatbot-rag.yaml templates\my-new-arch.yaml

Every field is documented inline in the existing files. Prefer editing a real template over writing from scratch — it is far easier to keep formulas and price_query filters correct.

3. Fill in the metadata (gallery card + detail header)

id: my-new-arch                    # stable identifier — becomes the URL (/templates/my-new-arch)
name: My New Architecture          # gallery card title
category: Conversational AI        # groups templates in the gallery filter
short_description: One-liner shown on the gallery card.
long_description: |
  Multi-paragraph rich description shown on the detail page.
  Explain the pattern, when to use it, and what "cost driver" the user configures.
industries: [Financial Services, Retail]
tags: [RAG, GPT-4o]
architecture_center_url: https://learn.microsoft.com/azure/architecture/...
icon_url: null                     # optional, absolute URL to an SVG/PNG

Rules:

  • id must be unique and URL-safe (lowercase, hyphens).
  • category, industries, and tags values drive the gallery filters — reuse existing labels when possible so filters stay tidy.

4. Model the business inputs (business_metrics)

These are what an end user tunes on the Basic view. Keep them close to how the customer thinks about their workload — never expose SKU-level knobs here.

business_metrics:
  - key: daily_active_users        # identifier used in formulas
    label: Daily active users      # shown in the UI
    description: Number of unique users interacting with the assistant per day.
    unit: users/day
    default_value: 500
    min: 1
    max: 1000000
    step: 10

Every key used in monthly_quantity_formula must be defined here (or in advanced_variables).

5. Model the advanced assumptions (advanced_variables)

These translate business metrics into Azure consumption. They show up only in the Advanced view.

advanced_variables:
  - key: input_tokens_per_interaction
    label: Input tokens per interaction
    unit: tokens
    default_value: 1500
  - key: output_tokens_per_interaction
    label: Output tokens per interaction
    unit: tokens
    default_value: 500

Keep defaults defensible — link a source in description when the assumption is non-obvious (e.g. "based on average 3-page RAG context + 400-word answer").

6. List Azure resources and price them (azure_resources)

For each resource in the architecture, add one entry.

azure_resources:
  - key: openai_input
    name: Azure OpenAI GPT-4o (input tokens)
    service: Azure OpenAI
    price_query:
      product_name: Azure OpenAI
      meter_name: gpt 4o 1120 Inp regnl
    monthly_quantity_formula: >
      (daily_active_users * avg_interactions_per_user *
       input_tokens_per_interaction * 22) / 1000
    notes: Meter is priced per 1K tokens; formula divides by 1000.
  • Use one resource entry per meter you want to bill against. Input tokens and output tokens for the same model are usually two entries.
  • Multiply by working days (22) or calendar days (30) as appropriate for the workload.

price_query reference

Non-empty fields are AND-ed into the OData $filter sent to https://prices.azure.com/api/retail/prices.

Field Match Example
service_name equals Azure Container Apps
arm_sku_name equals (rarely needed)
sku_name equals Standard S1
meter_name contains gpt 4o 1120 Inp regnl
product_name contains Azure OpenAI
arm_region_name equals — usually left blank; region comes from the request
price_type equals — defaults to Consumption
currency_code equals — defaults to USD from the request

meter_name and product_name use contains(...) because Azure often versions meters (gpt 4o 1120 Inp regnl Tokens). Supply a stable substring; the client picks the first non-zero result.

If a price cannot be resolved, the resource shows unit price 0 and a warning is added to the response — the calculator degrades gracefully so a stale meter name never breaks the whole gallery.

monthly_quantity_formula reference

An NCalc expression producing the monthly billable quantity in the unit the Azure meter is quoted in. Every identifier must be a declared business_metrics[].key or advanced_variables[].key.

Supported operators: + - * / % and functions like Ceiling, Floor, Max, Min, Round, Pow, Abs.

Examples:

# Tokens → 1K-token units:
monthly_quantity_formula: (users * interactions * tokens * 22) / 1000

# Users → whole-number replica count:
monthly_quantity_formula: Ceiling(daily_active_users / 250)

# Total requests → per-1M units:
monthly_quantity_formula: (requests_per_day * 30) / 1000000

Flat-cost resources

For services with a fixed monthly cost (e.g. an APIM tier), omit price_query and set flat_monthly_cost:

- key: apim
  name: API Management (Standard tier)
  service: Azure API Management
  flat_monthly_cost: 672.31
  monthly_quantity_formula: "1"
  notes: Flat monthly rate — no per-unit lookup.

7. Find the right meter names

The meter names in price_query are the trickiest part. Two reliable ways to discover them:

  1. Query the retail prices API directly. Example: all Azure OpenAI GPT-4o input meters in East US:
    curl "https://prices.azure.com/api/retail/prices?`$filter=serviceName eq 'Azure OpenAI' and armRegionName eq 'eastus' and contains(meterName, 'gpt 4o') and contains(meterName, 'Inp')"
    Pick the shortest substring of meterName that uniquely identifies the meter you want and paste it into price_query.meter_name.
  2. Run the API service locally and hit GET /api/calculate for your template. The response includes any warnings about resources whose meter did not resolve — iterate on the substrings until every resource has a non-zero unit price.

8. Test your template locally

# Terminal 1 — run the app
cd src
aspire run

Then in the Aspire dashboard:

  1. Open apiservice → Swagger UI.
  2. GET /api/templates → confirm your new template is listed.
  3. POST /api/calculate with:
    {
      "templateId": "my-new-arch",
      "region": "eastus",
      "currency": "USD",
      "businessMetrics": { "daily_active_users": 1000 }
    }
  4. Verify totalMonthlyCost > 0, no warnings, and every resource has a sensible unitPrice and monthlyQuantity.
  5. Open webfrontend → your template shows up on the gallery, filters work, and the detail page renders both Basic and Advanced views.

9. Sanity-check the numbers

Even if the formulas evaluate, the result must be credible.

  • Sweep the sliders across their range and confirm the cost curves make sense (linear vs. step for replicas, no negative costs, no NaN).
  • Compare a single mid-range configuration against the Azure pricing calculator for the same architecture. Aim for the same order of magnitude; if it is off by 10x, a formula unit is wrong (per-1K vs per-1M is the usual culprit).
  • Toggle between Basic and Advanced — the total should not jump when you simply reveal the advanced view without changing values.

10. Submit it

Open a pull request with:

  • The new templates/*.yaml file.
  • A short PR description explaining the architecture, cost drivers, and reference link.
  • A screenshot of the detail page and one sample calculation for reproducibility.

See CONTRIBUTING.md for the full checklist.

Field reference (quick lookup)

Section Field Notes
root id, name, category, short_description, long_description Required (except long_description)
root industries, tags Lists — drive gallery filters
root architecture_center_url, icon_url Optional
business_metrics[] key, label, default_value, min, max, step, unit, description key MUST match variables used in formulas
advanced_variables[] key, label, default_value, unit, description Shown only in Advanced view
azure_resources[] key, name, service, description, price_query, monthly_quantity_formula, flat_monthly_cost, notes See above

How the calculation works

  1. The user submits businessMetrics (and optionally advancedOverrides) for a template.
  2. The CostCalculator builds an NCalc parameter dictionary from those values, falling back to each metric/variable's default_value when not supplied.
  3. For every resource:
    • MonthlyQuantityFormula is evaluated → monthly quantity.
    • If flat_monthly_cost is set, that flat value is used as unit price and monthly cost.
    • Otherwise AzureRetailPricesClient is queried, results are cached for 6 hours in-memory, and the first non-zero match becomes the unit price.
    • monthly_cost = monthly_quantity × unit_price.
  4. All resource costs are summed into TotalMonthlyCost and returned with a per-resource breakdown.

Extending

  • Add a template → drop a new templates/*.yaml file, restart the API.
  • Add a region → edit the region list in src/AICostCalculator.ApiService/Program.cs.
  • Change theme / brand → edit src/AICostCalculator.Web/wwwroot/app.css.
  • Add a shared type → put it in src/AICostCalculator.Shared/Models.cs; both API and Web pick it up automatically.

Notes on Azure Retail Prices

The Azure Retail Prices catalog changes over time. Meter names include model versions (e.g. gpt 4o 1120), and some services have been reorganized under a Foundry Tools service umbrella. The contains(meterName, ...) behaviour tolerates version drift for a while, but for long-lived deployments you may need to periodically update meter substrings in the templates.

Deployment

The app is deployed to Azure Container Apps in the shared cae-azinfrawork-shared environment (subscription 1f79b416-e33a-4be5-bcff-e1fa7e300776, resource group azureinfrawork, Sweden Central):

  • Two Container Apps: ca-aicost-web (external ingress) and ca-aicost-api (internal-only ingress).
  • Images live in the shared registry crazinfraworkml76cbhfv5.azurecr.io under aicostcalculator/web and aicostcalculator/apiservice.
  • Infrastructure as code in infra/main.bicep.
  • CI/CD in .github/workflows/deploy.yml — every push to main builds new images with az acr build and rolls the Container Apps via az deployment group create, using OIDC login through the shared id-azinfrawork-github managed identity.
  • Custom domain aicostcalculator.livethoughts.net bound with an Azure-managed certificate (auto-renewed).

Full runbook, DNS records, and rollback steps: docs/DEPLOYMENT.md.

Contributing

Contributions are very welcome — especially new use case templates for the gallery. Please read CONTRIBUTING.md for the full workflow, coding conventions, and the checklist for submitting a new template.

All participation in this project is governed by the Code of Conduct.

License

This project is licensed under the MIT License.

Disclaimer

Cost estimates are informational and use live public prices from the Azure Retail Prices API. They do not include Enterprise Agreement discounts, reservations, savings plans, or negotiated pricing, and they may lag Azure changes by hours or days. Always confirm final pricing with the Azure pricing calculator or your Microsoft account team before making commitments.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages