Skip to main content

Pricing Configuration

The pricing config is stored in the credit_pricing_config table or loaded from a dict.

Flat Pricing

{
"version": 1,
"models": {
"gpt-4": "input_tokens * 0.01 + output_tokens * 0.03",
"_default": "input_tokens * 0.001 + output_tokens * 0.003"
},
"tools": {
"_default": "tool_calls * 5 / 1000",
"code_exec": "tool_calls * 10 / 1000"
},
"search": { "costs": "search_queries * 0.5 + search_results * 0.05" },
"cache": { "discount": "-cache_read_tokens * 0.0045" },
"fixed": { "batch_train": 100, "daily_report": 10 },
"min_balance": 5
}

Plan-Based Pricing

Extends flat pricing with subscription plan definitions. Plans give users free monthly allowances, rate overrides, and feature gating metadata.

{
"version": 1,
"models": {
"_default": "input_tokens * (0.01 / 1000) + output_tokens * (0.03 / 1000)"
},
"plans": {
"free": {
"id": "free",
"name": "Free Tier",
"free_allowance": 50000,
"rate_overrides": {
"_default": "input_tokens * (0.02 / 1000) + output_tokens * (0.06 / 1000)"
},
"features": { "max_concurrency": 1, "max_context": 4096 }
},
"pro": {
"id": "pro",
"name": "Pro Plan",
"free_allowance": 500000,
"rate_overrides": {
"gpt-4": "input_tokens * (0.005 / 1000) + output_tokens * (0.015 / 1000)"
}
}
}
}

Plan fields

FieldTypeRequiredDescription
idstringyesUnique plan identifier
namestringyesHuman-readable name
free_allowanceintyesMonthly free credits (0 = none)
rate_overridesobjectnoPer-model expression overrides for this plan
featuresobjectnoArbitrary feature flags (not enforced by ducto)

How plans work

  1. Assign a plan: store.set_user_plan(user_id, "pro")
  2. Deduct flow: CreditManager.deduct() checks plan allowance first. If allowance covers the cost, no balance deduction. Partial coverage deducts remainder from balance.
  3. No plan: Existing balance-only deduct flow unchanged.

Sections

version (required)

  • 1 — flat pricing, no plans
  • 2 — supports optional plans object

models (required)

Per-model pricing expressions. Non-empty dict.

  • Keys are model names (e.g. gpt-4, claude-3-opus)
  • _default used when no specific model matches
  • Each value is an expression string

tools (optional)

Per-tool pricing overrides. _default applies to tool calls not individually configured.

search (optional)

Search/RAG query pricing.

cache (optional)

Cache read discounts (typically negative — savings rebate).

fixed (optional)

Fixed-cost jobs. Keys match UsageMetrics.fixed_job. Values are non-negative integers.

min_balance (optional)

Minimum balance floor (default 5).

Loading

from ducto import PricingEngine

# From dict
engine = PricingEngine.from_dict({"models": {"_default": "input_tokens * 1"}})

# From file (YAML/JSON)
import yaml
with open("pricing.yaml") as f:
engine = PricingEngine.from_dict(yaml.safe_load(f))
import { PricingEngine, loadPricingFile } from "@apoorwv/ducto";

const engine = PricingEngine.fromDict({ models: { _default: "input_tokens * 1" } });

const data = await loadPricingFile("./pricing.yaml");
const engine2 = PricingEngine.fromDict(data);

Live Updates

Pricing stored in credit_pricing_config table. Update via CLI or RPC — no redeploy needed.

ducto pricing set config.json

CreditManager.load_pricing_from_store() fetches latest config.

Validation

At load time the engine validates:

  1. models is non-empty
  2. All expression strings parse and are safe
  3. Only allowed functions used
  4. Plan names are unique
  5. Plan rate_overrides expressions are valid
  6. No unknown sections