Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Ai Headroom Ledger Laravel Package

phoenix1331/laravel-ai-headroom-ledger

Laravel AI Headroom Ledger provides an auditable record of AI token usage and remaining headroom, helping you track consumption, enforce budgets, and monitor provider limits through simple Laravel-friendly APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install the package, publish its config and migrations, and run php artisan migrate. The ledger tables and daily rollups must exist before any query returns data; if entries() comes back empty on a fresh install, check migrations ran before debugging the query.
  • Look first at: the Ledger facade, LedgerQuery, GroupedQuery, and the enums EntrySort / CountMethod. The README's warning block about Ledger::find() / Ledger::run() being unscoped is the single most important paragraph to read before writing lookups.
  • First use case: a request log screen. Ledger::query()->orderBy(EntrySort::CountedAt)->paginate(25) gives you newest-first-by-default paging with no extra work, then layer filters as the UI grows.
  • Second use case, usually same week: a cost/token dashboard. Ledger::query()->orderBy(EntrySort::Cost) for the top-spend list, and Ledger::grouped()->byModel()->accuracy() (or per day) for the "how good are our estimates" panel.

Implementation Patterns

  • Sorting is an explicit opt-in. The default order stays newest first; orderBy(EntrySort $sort, bool $descending = true) replaces it. Treat sort as part of the query state object you pass around your controller/service, not something you sprinkle per-call.
  • Pagination for API endpoints. paginate($perPage, $page) returns a readonly LedgerPage with entries, total, page, perPage, and the helpers lastPage(), hasMorePages(), isEmpty(). The readonly value object maps cleanly onto a JSON resource: emit entries plus a meta block built from the rest, don't try to hand it to Laravel's paginate() view helpers.
  • Multi-tenant lookups. The pattern to standardise on: Ledger::query()->forTenant($id)->find($requestId) and ...->run($invocationId). Both return null unless the record matches every filter, so a wrong-tenant ID 404s naturally instead of leaking existence. Never call the unscoped Ledger::find() / Ledger::run() from a request-scoped controller.
  • Filter composition. Filters (forCountMethod(CountMethod ...$methods), date ranges, tenant/agent/tag, and whatever else your version exposes) are answered from daily rollups, so they're cheap and stack with orderBy() and paginate(). Build a single LedgerQuery in a form request → service → query pipeline rather than branching per filter in the controller.
  • Accuracy reporting. GroupedQuery::accuracy() returns a Breakdown of AccuracyStats, which is Arrayable — so it drops straight into a Blade table loop or ->toArray() for a chart endpoint. Combine with byDay() and a date range for a trend line; days without samples are still present and empty, so the chart has no gaps and you don't need to backfill client-side.
  • Run drill-down. A run lookup returns steps; a date filter narrows that run to only the steps inside the range. That's ideal for "what did this agent do last Tuesday" — treat the returned steps as the authoritative slice, and don't recompute run totals from them when a date filter is active.

Gotchas and Tips

  • Missing figures sort last in both directions. orderBy(EntrySort::PercentUsed, false) is not the reverse of the ascending order — unsampled rows float to the end either way. If you build a "click to flip direction" table, don't assume symmetry, and don't use array_reverse() on the entries.
  • Ties break on request ID, which is a stable but arbitrary secondary order. Fine for pagination correctness; not something to surface in UI copy.
  • Cost and InputTokens fall back to the estimate when no actual figure exists. A cost-sorted list mixes measured and estimated spend without flagging it — join against the estimate-accuracy view (or filter to actuals) if the distinction matters.
  • Currencies are not converted. Amounts sort by stored value. If you support multiple currencies, scope the query by currency before sorting by Cost, otherwise a cheap-in-JPY request can outrank an expensive-in-USD one on raw numbers.
  • Unscoped find()/run() are unchanged — only the docblocks now warn. Any existing code calling Ledger::find() in a tenant context is a latent IDOR; grep for it after upgrading.
  • A page past the end is empty but keeps the right total. So isEmpty() on a deep page does not mean "no results" — check total before showing a "nothing here" empty state.
  • Rollup-backed vs raw reads. Filters and accuracy come from daily rollups where grouping allows; some groupings force a raw scan. If a dashboard is slow, the grouping key is the first suspect — test with byDay() + range (rollup-friendly) versus a high-cardinality grouping.
  • Extension points: the EntrySort and CountMethod enums are the natural place to hang your own label/icon mapping (a small match in a presenter) rather than in Blade; LedgerPage and AccuracyStats being readonly/Arrayable makes them safe to pass into resources and jobs.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox