Skip to content

feat(engine): pluggable SaaS pricing shapes — flat_subscription, per_unit_flat, free_tier, transactional (#241) - #242

Merged
elecnix merged 1 commit into
mainfrom
feat/241-saas-pricing-shapes
Jul 29, 2026
Merged

feat(engine): pluggable SaaS pricing shapes — flat_subscription, per_unit_flat, free_tier, transactional (#241)#242
elecnix merged 1 commit into
mainfrom
feat/241-saas-pricing-shapes

Conversation

@elecnix

@elecnix elecnix commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #241 by promoting the external-node extension point from a single transactional formula into a pluggable SaaS pricing-handler registry. Four built-in shapes cover the most common SaaS billing models; third-party packages add more via infra_cost_model.saas_handlers entry-points.

Design

Each shape is a (quantity, params) -> monthly_cost_usd callable. The model declares which shape a metric uses:

saas_vendor:
  provider: workos
  usageMetrics:
    MAU:             { unit: Users,  value: 0, shape: free_tier,       free: 1000000, overage: 0.01 }
    SSO-Connection:  { unit: Conns,  value: 0, shape: per_unit_flat,   rate: 125.0 }
    AuditLog-Org:    { unit: Orgs,   value: 0, shape: per_unit_flat,   rate: 5.0 }
    CustomDomain:    { unit: Months, value: 0, shape: flat_subscription, rate: 99.0 }

so free-tier boundaries and per-unit rates live in the model, not in Python.

Built-in shapes

Shape Signature Example
flat_subscription quantity × rate ($/unit-mo), 0 = off Custom domain, $99/mo
per_unit_flat quantity × rate ($/unit-mo) SSO connections, $125/conn-mo
free_tier First free units at $0, then overage $/unit, optional stepped tiers AuthKit, 1M MAU free, $0.01 above
transactional volume × percentage + quantity × (fixed + per_call) The existing Stripe/Twilio pattern

Engine integration

Both _compute_flat_cost and _compute_tiered_cost dispatch to SaaSPricingRegistry.compute() when a metric declares a shape field — before the catalog / embedded pricingRates fallback path. An unknown shape returns None instead of raising: backward-compatible by construction.

Entry-point discovery

discover_entry_point_handlers() scans infra_cost_model.saas_handlers at import time. The four built-in shapes themselves are registered as entry-points.

What this does NOT do

  • Does not change the catalog or embedded pricingRates path (Principle 13).
  • Does not register new catalog rows or AWS pricing data.
  • Does not change the ExternalServiceRegistry API.
  • Does not break the existing external tests — all 16 pass unchanged.

Tests

tests/test_saas_pricing_shapes.py — 32 new tests covering all shapes, registry, engine integration, backward compatibility, and entry-point discovery. All 51 external tests pass, zero regressions.

— silent-orca-64

…unit_flat, free_tier, transactional (#241)

Add a pluggable SaaS pricing-handler registry (SaaSPricingRegistry) with four
built-in shapes:

- flat_subscription — fixed monthly fee, charged when enabled (e.g. 9/mo
  per custom domain).
- per_unit_flat — $X × count (e.g. 25/mo per SSO connection, /mo per org).
- free_tier — first N units free, then /unit overage (e.g. 1M MAU free,
  bash.01/MAU above). Supports optional stepped tiers for graduated overage.
- transactional — the existing percentage + per-transaction + per-call shape,
  preserved for vocabulary completeness.

A cost-model node declares the shape per metric via a 'shape' field:

    workos:
      provider: workos
      usageMetrics:
        SSO-Connection: { unit: Conn, value: 2, shape: per_unit_flat, rate: 125.0 }

The engine dispatches to the shape handler before the catalog / embedded
pricingRates path. An unknown shape falls back to the existing path (opt-in,
backward-compatible). Third-party packages register additional shapes via the
infra_cost_model.saas_handlers entry-point group.

Design follows existing patterns — registry class (like ResourceRegistry),
shape computed per-metric in _compute_flat_cost and _compute_tiered_cost,
quantity = derived invocation count × value (usage-driven) or direct value
(fixed/flatOverride).

Co-authored-by: silent-orca-64 <silent-orca-64@pi-agent.local>
@elecnix
elecnix merged commit 40a8418 into main Jul 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

external: pluggable SaaS pricing handlers (subscription / per-unit / free-tier), not just transactional percentage

1 participant