Skip to main content

Agentcore Metering

中文

Unified module for AI service invocation and statistics in Django projects.

  • Uses LiteLLM for completion and cost metering.
  • Stores cost as amount + currency (e.g. cost, cost_currency).
  • Uses USD as default cost currency from LiteLLM.

Install

  • Not on PyPI, install only from GitHub.

From GitHub (editable after clone):

pip install -e git+https://github.com/cloud2ai/agentcore-metering.git

Or, when the host project uses it as a submodule, from repo root:

pip install -e path/to/agentcore-metering
  • The host project Dockerfile should iterate over agentcore/ submodules and run pip install -e.
  • See the host project README for details.

Testing

Install with dev extras, then run pytest from the package root:

pip install -e ".[dev]"
pytest tests -v
  • Unit tests cover:
    • llm_usage_stats (parse, summary, by_model, series)
    • llm_usage (paginated list and query parsing)
  • API tests cover:
    • GET .../token-stats/
    • GET .../llm-usage/
    • llm-config endpoints (auth and response shape)

Backend: usage

  1. Register
    • Add 'agentcore_metering.adapters.django' to INSTALLED_APPS
    • Add path('api/v1/admin/', include('agentcore_metering.adapters.django.urls')) to root URLconf
  2. Call LLM and persist usage:
    from agentcore_metering.adapters.django import LLMTracker
    content, usage = LLMTracker.call_and_track(
        messages=[{"role": "user", "content": "..."}],
        node_name="my_node",
        state=request_state,
    )
    
    • state is optional (task/user context).
    • If state contains user_id, per-user LLM config (if set) is used. Pass model_uuid to use a specific config; otherwise the earliest enabled model is used.
    • For json_mode=True (non-stream), tracker does JSON repair + validation and retries by default (json_attempts=3; configurable).
    • For stream=True, JSON repair is skipped (stream output is unchanged).
  3. Config
    • All config can be managed by admin APIs (global defaults + optional per-user overrides).
    • When model_uuid is not provided, resolution uses the earliest enabled config (by is_default then created_at): user scope -> global scope; no settings fallback, raises if no DB config.
    • LLM calls go through LiteLLM.
    • Cost (USD) is estimated from LiteLLM reference pricing and stored per call and in stats.

Supported providers

  • Each provider uses a smallest/cheapest default model to reduce misconfiguration cost.
  • You can override model and optionally api_base/deployment in config.
  • All providers support api_base (URL):
    • official endpoint when omitted
    • proxy/forwarding URL when needed
  • If you are using an official model endpoint, choose the native provider for that vendor, for example minimax.
  • If you are using a third-party forwarding platform through an OpenAI Compatible endpoint, choose openai_compatible.
  • Do not rely on model strings to infer provider automatically; choose the provider based on the actual integration path.
Provider Default model / notes
openai gpt-4o-mini (official default URL)
azure_openai gpt-4o-mini (requires api_base and deployment)
gemini gemini-2.0-flash
anthropic claude-3-5-haiku
mistral mistral-tiny
dashscope qwen-turbo (Alibaba Qwen)
deepseek deepseek-chat
xai grok-3-mini-beta (Grok)
meta_llama Llama-3.3-8B-Instruct
amazon_nova nova-micro-v1
nvidia_nim meta/llama3-8b (Nemotron / NIM)
minimax MiniMax-M2.1
openai_compatible Depends on the gateway vendor (OpenAI Compatible endpoint)
moonshot moonshot-v1-8k (Kimi)
zai glm-4.5-flash (Z.AI GLM)
volcengine doubao-pro-32k (ByteDance Doubao)
openrouter google/gemma-2-9b-it:free
  • Config is from DB only (admin API); no Django settings fallback. At least one enabled global or user config is required for calls.

API reference

  • Mount under an admin prefix (e.g. api/v1/admin/).
  • Auth: IsAdminUser (staff or superuser), otherwise 403.
  • If the main project uses drf-spectacular, endpoints appear in Swagger UI (e.g. /swagger) under llm-metering.

LLM configuration (global and per-user)

Method Path Description
GET .../llm-config/ List global LLM configs (ordered by created_at, id)
POST .../llm-config/ Create one config. Body: provider, config (optional scope, user_id, is_active)
GET .../llm-config/all/ List all LLM configs (global + user). Optional `scope=all
GET .../llm-config/<pk>/ Get one config by id
PUT .../llm-config/<pk>/ Update one config by id
DELETE .../llm-config/<pk>/ Delete one config by id
GET .../llm-config/providers/ Per-provider param schema (required/optional/editable keys, default model and api_base) for building provider-specific forms
GET .../llm-config/models/ Provider list and model list with capability tags (text-to-text / vision / code / reasoning, etc.)
POST .../llm-config/test/ Validate credentials without saving. Body: provider, config
POST .../llm-config/test-call/ Run one completion and persist usage. Body: config_uuid (or legacy config_id), prompt, optional max_tokens, optional stream (see below)
GET .../llm-config/users/ List per-user configs (optional ?user_id= filter)
GET .../llm-config/users/<user_id>/ Get one user's config (404 if not set)
PUT .../llm-config/users/<user_id>/ Create or update that user's config
DELETE .../llm-config/users/<user_id>/ Remove user config (they fall back to global default)
  • POST/PUT body

    • provider (default openai)
    • config (single JSON object, e.g. api_key, model, api_base, deployment, max_tokens, temperature, top_p, request_timeout_seconds, num_retries)
    • num_retries defaults to 3, accepts 0 to disable retries, and is applied within the single request_timeout_seconds budget
    • for create/list workflows: optional scope, user_id, is_active, optional model_type
    • required/optional keys differ by provider (e.g. Azure needs api_base and deployment)
    • use GET .../llm-config/providers/ to fetch schema
    • on GET, config.api_key and config.key are masked (e.g. sk-**xxxx); is_default is true when this config is the current default (earliest enabled global config used when model_uuid is not set), so the frontend can highlight the default model.
  • GET .../llm-config/providers/

    • returns { "providers": { "<provider>": { "required": [...], "optional": [...], "editable_params": [...], "default_model": "...", "default_api_base": "..." } } }
    • use it to render provider-specific forms and placeholders
  • POST .../llm-config/test/

    • body: provider, config (same as PUT)
    • runs a minimal completion to verify key and endpoint
    • success: 200 + { "ok": true }
    • validation/completion failure: 200 + { "ok": false, "detail": "..." }
    • invalid payload: 400
  • POST .../llm-config/test-call/

    • body: config_uuid (preferred) or legacy config_id, prompt, optional max_tokens (default 512, max 4096), optional stream (default false)
    • when stream is false (default): JSON response. success: { "ok": true, "content": "...", "usage": { ... } }; failure: { "ok": false, "detail": "..." }
    • when stream is true: response is SSE (Content-Type: text/event-stream). Events: data: {"type":"chunk","content":"<fragment>"} for each content fragment; final event data: {"type":"done","ok":true,"usage":{...}} or data: {"type":"done","ok":false,"detail":"..."}
    • call is persisted to usage records; streaming calls are recorded with is_streaming=true and optional TTFT (first_chunk_at)

GET .../token-stats/

  • Returns aggregates and optional time series.
  • Query params are all optional:
Param Type Description
start_date string Start time, ISO or date-only (e.g. 2025-01-01)
end_date string End time; date-only is end of that day
user_id int Filter by user id
granularity string Time bucket: day (hour), month (day), year (month); omit for no series
use_series string 1 / true / yes: when set with granularity and date range, also return series_by_model from pre-aggregated LLMUsageSeries (for per-model trend charts)

200 (JSON):

{
  "summary": { ... },
  "by_model": [ ... ],
  "series": null,
  "series_by_model": null
}
  • series is non-null only when granularity is set: { "granularity": "day", "items": [ { "bucket": "...", "total_tokens": 0, ... } ] }
  • series_by_model is non-null only when use_series=1 (or true/yes) and granularity and start_date/end_date are set. It is a list of { "bucket", "model", "call_count", "success_count", "avg_e2e_latency_sec", "avg_ttft_sec", "avg_output_tps", "total_prompt_tokens", "total_completion_tokens", "total_tokens", "total_cached_tokens", "total_reasoning_tokens", "total_cost", "cost_currency" } from the pre-aggregated table (global scope; not filtered by user_id). Use it for “Token trend by model” and “Cost trend by model” charts.
  • invalid granularity returns 400 + { "detail": "..." }

GET / PATCH .../metering-config/

  • GET: Returns effective metering config (retention, cleanup and aggregation schedules).
  • PATCH: Update config. Body fields are optional: retention_days (1–3650), cleanup_enabled, cleanup_crontab, aggregation_crontab (five-field cron expressions).

GET 200 (JSON):

{
  "retention_days": 365,
  "cleanup_enabled": true,
  "cleanup_crontab": "0 2 * * *",
  "aggregation_crontab": "5 * * * *"
}
  • Used by the admin UI “Data settings” / “Scheduled tasks” page to configure how long to keep data and when cleanup/aggregation run.

GET .../llm-usage/

  • Returns a paginated usage list.
  • Query params:
Param Type Description
page int Page number, default 1
page_size int Page size, default 20, max 100
user_id int Filter by user id
model string Model name (icontains)
success string true / false
start_date string Start time
end_date string End time

200 (JSON):

{
  "results": [
    {
      "id": "uuid",
      "user_id": "uuid-or-null",
      "username": "string-or-null",
      "model": "gpt-4",
      "prompt_tokens": 100,
      "completion_tokens": 50,
      "total_tokens": 150,
      "cost": 0.0012,
      "cost_currency": "USD",
      "success": true,
      "error": null,
      "created_at": "2025-01-01T12:00:00+00:00",
      "metadata": {}
    }
  ],
  "total": 100,
  "page": 1,
  "page_size": 20
}

  • UX
    • use token-stats for dashboards and trend charts
    • use llm-usage for filterable, paginated call logs

Pre-aggregated series (LLMUsageSeries)

  • Table: llm_usage_series stores pre-aggregated usage per (granularity, bucket, model). Granularities: hour (for “day” view), day (for “month” view), month (for “year” view).
  • Fields per row: bucket, model, call_count, success_count, avg_e2e_latency_sec, avg_ttft_sec, avg_output_tps, token totals (total_prompt_tokens, total_completion_tokens, total_tokens, total_cached_tokens, total_reasoning_tokens), total_cost, cost_currency.
  • Population: Celery task aggregate_llm_usage_series_task (hour + day + month in one run when invoked from beat) aggregates from llm_tracker_usage into llm_usage_series. Use it for fast “by model” trend charts without querying raw usage on each request.
  • API: GET .../token-stats/?use_series=1&granularity=...&start_date=...&end_date=... returns series_by_model (list of the above rows) for charting.

Metering config and scheduled tasks

  • Config model: MeteringConfig (single row) holds retention_days (default 365), cleanup_enabled, cleanup_crontab, aggregation_crontab. API: GET / PATCH .../metering-config/ (see above).
  • Celery tasks (require Celery and agentcore-task; registered with django-celery-beat):
    • Cleanup: cleanup_old_llm_usage_task — deletes llm_tracker_usage and llm_usage_series older than retention_days. No-op if cleanup_enabled is false.
    • Aggregation: aggregate_llm_usage_series_task — runs hour, day, and month aggregation in one go (when called from beat without args); populates llm_usage_series.
  • Task tracking: Both tasks register with agentcore-task TaskTracker (module agentcore_metering). Executions appear in the unified task list and stats (status, result, error).

Data and layout

  • Tables: llm_tracker_usage (usage records), llm_usage_series (pre-aggregated series), agentcore_metering_llm_config (LLM provider config), MeteringConfig (retention and cron settings).
  • Package: agentcore_metering.adapters.django is a full Django app (models, views, urls, admin, migrations). The public API for LLM metering lives under trackers/ (e.g. trackers/llm.py); import LLMTracker from agentcore_metering.adapters.django or agentcore_metering.adapters.django.trackers.llm. Additional tracker types may be added under trackers/ (e.g. trackers/other.py).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agentcore_metering-0.2.0.tar.gz (112.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agentcore_metering-0.2.0-py3-none-any.whl (113.1 kB view details)

Uploaded Python 3

File details

Details for the file agentcore_metering-0.2.0.tar.gz.

File metadata

  • Download URL: agentcore_metering-0.2.0.tar.gz
  • Upload date:
  • Size: 112.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agentcore_metering-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1b71a6f47f5a5f2f81244f6b17be615b281ab5c34b5fc91e86263841905ab586
MD5 171647ed7e70eada9d17210364e74f2b
BLAKE2b-256 f48dd56c423b0bb7df39cd9a98d6ce51000af2b9b7989cc4f8b8c3f00b34c37c

See more details on using hashes here.

Provenance

The following attestation bundles were made for agentcore_metering-0.2.0.tar.gz:

Publisher: release.yml on cloud2ai/agentcore-metering

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agentcore_metering-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agentcore_metering-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3b66d7a0ff6fc9b9a8a5478de15f1729d7e382e1d50d2964b3204df6a4bbffc4
MD5 4f421bff89c48af60a5f1536897f9862
BLAKE2b-256 bbe1469723242e9026ae219ac0eb9fefa67fe1a832c8e6495564c202464f7c82

See more details on using hashes here.

Provenance

The following attestation bundles were made for agentcore_metering-0.2.0-py3-none-any.whl:

Publisher: release.yml on cloud2ai/agentcore-metering

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page