Configuration
All Config fields, environment variables, development mode, TaskOptions attribution fields, and the dexcost CLI.
Configuration options
Pass a Config struct to dexcost::init(). init is safe to call multiple times — only the first call takes effect and subsequent calls return DexcostError::AlreadyInitialized.
| Field | Type | Default | Description |
|---|---|---|---|
api_key | Option<String> | None — falls back to DEXCOST_API_KEY | API key for cloud push. Must start with dx_live_ (production) or dx_test_ (sandbox). Omit for local-only mode. |
batch_size | usize | 100 | Maximum number of events per sync batch sent to the Control Layer. |
flush_interval_secs | u64 | 5 | Seconds between background sync pushes. |
redact_fields | Vec<String> | vec![] (no redaction) | Field names removed from event details before cloud push (e.g. vec!["prompt".into(), "completion".into()]). |
hash_customer_id | bool | false | SHA-256 hash customer_id before cloud push so raw identifiers never leave the process. |
environment | Option<String> | None — falls back to DEXCOST_ENV | Deployment environment. Set to "development" to enable dev console output. |
auto_instrument | Vec<String> | vec![] | Names of providers to note for auto-instrumentation intent (e.g. vec!["openai".into()]). Wrapper clients must still be constructed explicitly. |
track_http | bool | true | When true, the global ServiceCatalog is initialised and made available via service_catalog(). Set to false to skip HTTP cost tracking entirely. |
service_catalog_url | Option<String> | None | URL to fetch an updated service catalog JSON at startup, merged in the background after init returns. |
buffer_path | Option<PathBuf> | None — falls back to DEXCOST_BUFFER_PATH, then ~/.dexcost/buffer.db | Explicit path for the on-disk SQLite event buffer. |
init returns Err(DexcostError::InvalidApiKey) if the key format is invalid and Err(DexcostError::AlreadyInitialized) on a second call.
use dexcost::{init, Config};
use std::path::PathBuf;
init(Config {
api_key: Some("dx_live_...".into()),
batch_size: 200,
flush_interval_secs: 10,
redact_fields: vec!["prompt".into(), "completion".into()],
hash_customer_id: true,
track_http: true,
service_catalog_url: Some("https://catalog.example.com/catalog.json".into()),
buffer_path: Some(PathBuf::from("/var/lib/myapp/dexcost.db")),
..Default::default()
}).unwrap();Environment variables
Environment variables are read inside Config::validate() when init is called. Explicit field values in Config take precedence over environment variables.
| Variable | Description |
|---|---|
DEXCOST_API_KEY | API key. Read when Config::api_key is None. |
DEXCOST_ENV | Deployment environment. Read when Config::environment is None. Set to development to enable dev console mode. |
DEXCOST_ENDPOINT | Override the Control Layer URL. Defaults to https://api.dexcost.io. Read at push time via Config::endpoint(). |
DEXCOST_BUFFER_PATH | Override the SQLite buffer file path. Read when Config::buffer_path is None and no home directory default applies. |
DEXCOST_RATES_PATH | Override the default rates YAML path used by RateRegistry::default_path(). Defaults to ~/.dexcost/rates.yaml. |
Development mode
Pass environment: Some("development".into()) in Config (or set DEXCOST_ENV=development) to enable dev mode:
dexcost::init(Config {
environment: Some("development".into()),
..Default::default()
}).unwrap();In dev mode:
- Every
llm_call,external_cost,compute_cost, andretry_markerevent is printed tostderras it is recorded, including provider, model, token counts, and cost. - Task completion is printed with aggregated cost totals and retry counts.
- An API key is not required — the background pusher is not started.
Dev mode is controlled by dexcost::dev_console::enable_dev_mode(), which is called automatically when environment == "development" during Config::validate().
Attribution & context
TaskOptions
Pass a TaskOptions struct to start_task to set attribution on an individual task:
use dexcost::{start_task, TaskOptions};
use std::collections::HashMap;
let mut task = start_task("resolve_ticket", TaskOptions {
customer_id: Some("acme-corp".into()),
project_id: Some("support".into()),
experiment_id: Some("exp-001".into()),
variant: Some("v2-prompt".into()),
metadata: Some(HashMap::from([
("tier".into(), serde_json::json!("enterprise")),
])),
..Default::default()
}).await?;| Field | Type | Description |
|---|---|---|
customer_id | Option<String> | Customer attribution. Stored on the task and included in every event within the task. |
project_id | Option<String> | Project attribution. |
experiment_id | Option<String> | Experiment identifier for A/B tracking. |
variant | Option<String> | Variant label for the experiment. |
metadata | Option<HashMap<String, serde_json::Value>> | Arbitrary key-value metadata attached to the task. |
parent_task_id | Option<String> | Explicit parent task link. When None, the parent is inferred from task-local context (via scope). |
heuristics | Option<HeuristicConfig> | Enable heuristic retry detection. See Instrumentation. |
DexcostContext
DexcostContext provides ambient attribution without an explicit task. Call set_context to store it in a process-wide RwLock; wrapper clients and auto-tasks read it via get_dexcost_context:
use dexcost::{set_context, DexcostContext};
set_context(DexcostContext {
customer_id: Some("acme-corp".into()),
project_id: Some("chatbot-v2".into()),
agent: Some("support_bot".into()),
..Default::default()
}).await;| Field | Type | Description |
|---|---|---|
customer_id | Option<String> | Customer attribution. |
project_id | Option<String> | Project attribution. |
agent | Option<String> | Agent name; overrides the task_type of auto-created session tasks. |
metadata | Option<HashMap<String, serde_json::Value>> | Arbitrary metadata propagated to auto-created tasks. |
Call get_dexcost_context().await to read the current context and clear_dexcost_context().await to remove it.
CLI
The dexcost CLI operates offline against the local SQLite buffer. No API key or network connection is required.
Build from source:
cargo install --path . --bin dexcostdexcost status
Print event and task counts from the local SQLite buffer:
dexcost status
dexcost status --db /path/to/buffer.dbOutput includes: database path, task count, event count, pending sync count, and total cost (USD).
dexcost rates
Manage the persistent rate registry stored at ~/.dexcost/rates.yaml:
# List all registered rates
dexcost rates --list
# Import rates from a YAML file and merge into the persistent store
dexcost rates --import rates.yaml
# Export the persistent store to a YAML file
dexcost rates --export rates.yamlYAML format expected by --import (canonical format shared with the SDK):
rates:
maps.googleapis.com:
per: request
cost_usd: "0.005"
stripe:
per: per_transaction
cost_usd: "0.029"A legacy flat JSON array of {service, per, cost_usd} objects is also accepted on import for backward compatibility. Files exported by the CLI re-import cleanly via RateRegistry::load_from_file and vice versa.
dexcost scan
Static-analysis scan of your codebase to identify LLM call sites and untracked cost points:
dexcost scan ./src
dexcost scan ./src --generate-stubsscan walks the target directory (default .), identifies auto-instrumented LLM call sites, and flags external API calls that likely incur cost but have no record_cost call nearby. --generate-stubs prints ready-to-paste record_cost snippets for each untracked cost point found.