The home ADR 0050 left open for a usage reading. mesh-control is a CLI and
cannot consume events, so the store that keeps the current usage picture is a
MODULE — the audit-logger's sibling: it consumes `module.*.usage.*` and upserts
each reading into its own provisioned postgres store, latest per
(licence, consumer, period, metric), in the clear. One vendor-neutral table
holds BOTH grains; they differ only in `consumer` (the holding module for the
licence grain, the session for the finer one). The consumer creates its table
on startup and, as a restart-until-ready service, self-heals rather than
gating the apply on a run-once that must reach a provider over the overlay.
The vendor->row normalisation moves into the adapter, as ADR 0054 requires:
anthropic-manager (licence grain, utilization%) and anthropic-consumer (session
grain, token/cost) now emit already-normalised { rows: UsageRow[], raw } on
their existing keys, so the store stays vendor-blind.
Proven end to end by the mesh-lab model-usage bed (green): a usage event
emitted into the mesh is upserted at both grains, latest-per-key, in the clear.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
33 lines
1.2 KiB
TypeScript
33 lines
1.2 KiB
TypeScript
// model-usage's tools — its own read-only query surface over the usage store (novox/hq ADR 0039:
|
|
// the tools and the client they call live in the module). The store is opened from the environment;
|
|
// when it cannot be (no DATABASE_URL), the module contributes no tools rather than failing the whole
|
|
// tool runtime — the postgres precedent.
|
|
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { UsageStore } from "../store.js";
|
|
|
|
export function getUsageTools(store: UsageStore): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "model_usage_current",
|
|
description:
|
|
"The latest model-usage reading per (licence, consumer, period, metric), across both grains, optionally restricted to one licence.",
|
|
input: {
|
|
licence: { type: "string", description: "restrict to one licence (optional)" },
|
|
},
|
|
run: async (args) => {
|
|
const licence = args.licence ? String(args.licence) : undefined;
|
|
return { usage: await store.current(licence) };
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
registerModuleTools("model-usage", (env) => {
|
|
try {
|
|
return getUsageTools(UsageStore.fromEnv(env));
|
|
} catch {
|
|
return [];
|
|
}
|
|
});
|