npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@polycode-projects/bedrock-meter-cdk

v1.10.1

Published

AWS CDK construct that provisions the store, flags, budget, alarms, dashboard and correct Bedrock IAM for @polycode-projects/bedrock-meter.

Downloads

2,142

Readme

@polycode-projects/bedrock-meter-cdk

The cost plane for @polycode-projects/bedrock-meter, as one AWS CDK construct.

new BedrockMeter(...) provisions the DynamoDB ledger table, the SSM control flags, an AWS monthly Budget, the daily-cap CloudWatch alarms and dashboard, and an optional Guardrail. grantMeteredBedrock(role) attaches the correct 3-way Bedrock IAM split — the Converse-vs-InvokeModel GuardrailIdentifier condition-key trap, pre-solved.

Why

bedrock-meter meters and caps Bedrock spend in-process, before each call returns — far ahead of AWS's own actuals (CloudWatch token metrics trail by ~13–40s; Cost Explorer / CUR dollars settle next-day). Because cost is known pre-flight, you can throttle, degrade, and alarm — enforcement, not just observation. This construct stands up the AWS-side resources the runtime reads and writes, and gets the notoriously fiddly Bedrock IAM right the first time.

Install

npm install @polycode-projects/bedrock-meter-cdk

A plain aws-cdk-lib TypeScript construct (built with tsc), published to npm under the @polycode-projects scope on the GitLab project registry. Point the scope at the registry with a project-local .npmrc:

@polycode-projects:registry=https://gitlab.com/api/v4/projects/<project-id>/packages/npm/

Peer deps: aws-cdk-lib ^2.150.0 and constructs ^10.3.0. Node >=20.

Quickstart

import { BedrockMeter } from "@polycode-projects/bedrock-meter-cdk";

// In a Stack:
const meter = new BedrockMeter(this, "Meter", {
  envName: "prod",
  capDailyGbp: 7.9,
  capMonthlyGbp: 237,
  guardrail: true,            // provision a PII/content Guardrail and wire its IAM
});

// Grant a Lambda (or any IGrantable) the correct, least-privilege Bedrock IAM:
meter.grantMeteredBedrock(myLambda.role);

// Point the runtime at the provisioned table:
myLambda.addEnvironment("DDB_TABLE_NAME", meter.table.tableName);

Props

| Prop | Type | Default | Meaning | | --- | --- | --- | --- | | envName | string | "ci" | Label woven into resource names + SSM paths. | | capDailyGbp | number | 7.9 | Daily spend cap in GBP (≈ $10 at fx 0.79). | | capMonthlyGbp | number | 237 | Monthly spend cap in GBP. | | fxGbpUsd | number | 0.79 | GBP→USD rate used to derive the USD AWS Budget. | | guardrail | boolean | false | Provision a PII/content Guardrail and wire it into the IAM grant. | | namespace | string | "BedrockMeter/Budget" | CloudWatch EMF namespace the runtime emits into. | | modelIds | string[] | Nova + Titan set | Foundation-model ids InvokeModel* is restricted to. | | budgetNotifyEmails | string[] | [] | Emails to subscribe to the AWS Budget at 50/80/100%. | | taggedSpendModelIds | string[] | [] | On-demand foundation-model ids to provision a cost-allocation-tagged Application Inference Profile for — see Cost-allocation-tagged spend below. | | costAllocationTagKey | string | "bedrock-meter-metered" | Tag key stamped on every provisioned profile. | | costAllocationTagValue | string | "true" | Tag value stamped on every provisioned profile. |

Exposed resources

After construction these are public fields for further wiring:

| Field | Type | What | | --- | --- | --- | | table | dynamodb.Table | The single-table ledger (pk/sk, TTL, PITR). | | capParam | ssm.StringParameter | /bedrock-meter/<env>/cap — JSON daily/monthly caps. | | enabledParam / degradedParam | ssm.StringParameter | Kill-switch + degraded-mode flags. | | budget | budgets.CfnBudget | Monthly AWS Budget in USD, filtered to Amazon Bedrock. | | alarms | cloudwatch.Alarm[] | Daily-cap alarms at 80 / 95 / 100%. | | dashboard | cloudwatch.Dashboard | Daily-spend graph + single-value widget. | | guardrail? | bedrock.CfnGuardrail | The optional Guardrail (when guardrail: true). | | taggedProfileArns | Record<string, string> | modelId -> Application Inference Profile ARN, one entry per id in taggedSpendModelIds. |

grantMeteredBedrock(grantee, opts?)

Bedrock IAM is split three ways because the GuardrailIdentifier condition key behaves differently across actions. This method emits all three, correctly:

  1. Converse / ConverseStreamResource: "*" (plus a GuardrailIdentifier condition when a guardrail is configured).
  2. InvokeModel / InvokeModelWithResponseStream → restricted to the foundation-model ARNs (and EU inference-profile ARNs), with no condition.
  3. ApplyGuardrail → the guardrail ARN (only when a guardrail exists).

Plus SSM read on the control flags, DynamoDB read/write on the ledger table, and pricing:GetProducts (us-east-1) for the live price tier.

Pass opts.guardrailIdentifiers to grant a guardrail this construct didn't provision — one you deployed elsewhere. Each entry is a guardrail id or a full ARN; a bare id is qualified into the stack's own account and region for ApplyGuardrail. It unions with any guardrail the construct did provision, so the GuardrailIdentifier condition and the ApplyGuardrail grant cover both:

meter.grantMeteredBedrock(myLambda.role, {
  guardrailIdentifiers: ["my-existing-guardrail-id"],
});

Without this option, a grantee whose guardrail lives outside this construct gets a Converse grant with no GuardrailIdentifier condition and no ApplyGuardrail at all.

Cost-allocation-tagged spend (taggedSpendModelIds)

Bedrock Converse/InvokeModel calls aren't taggable directly — there is no per-call resource to attach a cost-allocation tag to. The mechanism this construct uses is an AWS::Bedrock::ApplicationInferenceProfile: a taggable resource whose ARN you pass as modelId on your Bedrock call instead of the raw model id. Every call routed through the profile ARN is then attributed to that profile's tags in Cost Explorer / CUR.

const meter = new BedrockMeter(this, "Meter", {
  envName: "prod",
  taggedSpendModelIds: ["amazon.nova-lite-v1:0", "amazon.nova-micro-v1:0"],
});

// meter.taggedProfileArns["amazon.nova-lite-v1:0"] -> the profile ARN.
// Pass that ARN as `modelId` on your ConverseCommand/InvokeModelCommand
// instead of "amazon.nova-lite-v1:0" for that call's spend to be tagged.

The exact convention, documented in full because a non-TypeScript consumer (a Java/Maven CDK app, say — bedrock-meter-cdk is a plain TS construct, not jsii, so it can't be imported from another language) needs to reproduce this by hand rather than call the construct directly:

  • Profile name: bedrock-meter-<envName>-<slug>, where <slug> is the model id lowercased with every run of non-[a-z0-9] characters collapsed to a single hyphen (no leading/trailing hyphen) — e.g. amazon.nova-lite-v1:0amazon-nova-lite-v1-0. The exact algorithm is slugifyModelId(), also exported by this package if you're calling it from TypeScript/JavaScript.
  • modelSource.copyFrom: arn:aws:bedrock:<region>::foundation-model/<modelId> — same-region, on-demand foundation models only.
  • Tags: { Key: costAllocationTagKey, Value: costAllocationTagValue } (default bedrock-meter-metered / true) plus { Key: "bedrock-meter-env", Value: envName }.

Constraints, not bugs: embedding models (e.g. Titan-embed) don't support Application Inference Profiles — AWS rejects those at deploy time, this construct doesn't pre-filter the list. Cross-region (eu.-prefixed) profile ids aren't supported by taggedSpendModelIds either — only same-region, on-demand foundation models.

The tag key still needs manual activation. Provisioning the tag is not the same as it appearing in Cost Explorer/CUR — activate costAllocationTagKey once, per account, under AWS Billing → Cost allocation tags, before it's queryable (and expect a real settling delay after that: tags typically become visible in Cost Explorer/CUR ~24h after first tagged spend — see reports/EVIDENCE_LOG.md for what that looks like in practice for this repo's own evidence pipeline, which uses this exact mechanism with its own tag key).

Links

Licence

Apache-2.0 © 2026 Polycode Limited.