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
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique plan identifier |
name | string | yes | Human-readable name |
free_allowance | int | yes | Monthly free credits (0 = none) |
rate_overrides | object | no | Per-model expression overrides for this plan |
features | object | no | Arbitrary feature flags (not enforced by ducto) |
How plans work
- Assign a plan:
store.set_user_plan(user_id, "pro") - Deduct flow:
CreditManager.deduct()checks plan allowance first. If allowance covers the cost, no balance deduction. Partial coverage deducts remainder from balance. - No plan: Existing balance-only deduct flow unchanged.
Sections
version (required)
1— flat pricing, no plans2— supports optionalplansobject
models (required)
Per-model pricing expressions. Non-empty dict.
- Keys are model names (e.g.
gpt-4,claude-3-opus) _defaultused 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:
modelsis non-empty- All expression strings parse and are safe
- Only allowed functions used
- Plan names are unique
- Plan
rate_overridesexpressions are valid - No unknown sections