AI News HubLIVE
站内改写4 min read

Which AI agent spent the money on your OpenAI/Anthropic bill

Spaturzu is an open-source SDK that wraps existing LLM clients (OpenAI, Anthropic, etc.) with a single import change to enable per-agent cost attribution, budget enforcement, and cross-provider fallback, without altering call sites.

SourceHacker News AIAuthor: nu11P01nt3r

Notifications You must be signed in to change notification settings

Fork 0

Star 3

BranchesTags

Open more actions menu

Folders and files

NameName

Last commit message

Last commit date

Latest commit

History

17 Commits

17 Commits

.github/workflows

.github/workflows

openclaw

openclaw

python

python

typescript

typescript

.gitignore

.gitignore

.npmrc

.npmrc

LICENSE

LICENSE

README.md

README.md

package-lock.json

package-lock.json

package.json

package.json

pnpm-workspace.yaml

pnpm-workspace.yaml

tsconfig.base.json

tsconfig.base.json

Repository files navigation

Open-source client SDKs for spaturzu — per-agent LLM cost attribution, budget enforcement, and cross-provider fallback. Wrap your existing provider client (OpenAI, Anthropic, Bedrock, Gemini, Mistral) and every call is metered, attributed to the agent that made it, and optionally budget-capped — without changing how you call the model.

Migrate an existing agent in one line

Already calling OpenAI, Anthropic, Bedrock, Gemini, or Mistral? Change a single import — from import OpenAI from "openai" to import OpenAI from "@spaturzu/sdk/openai". Construction and call sites stay exactly the same, and you can attribute each call to the agent that made it:

import OpenAI from "@spaturzu/sdk/openai";

// Swap one import — reads SPATURZU_API_KEY + OPENAI_API_KEY from env. const openai = new OpenAI();

// Tag any call with the agent that made it — one line, no closure. await openai.withAgent("support-triage").chat.completions.create({ /* … */ });

That's the whole migration — no new objects, no wrappers around your call sites. Python is identical: from spaturzu.openai import OpenAI, then client.with_agent("support-triage").chat.completions.create(...). The same one-import swap works for every provider — @spaturzu/sdk/anthropic, /bedrock, /google, /mistral (Python: spaturzu.anthropic, and so on).

Running multi-step workflows? Group several calls under one agent with run() instead — see What you can do below.

SDKs

SDK Package Docs

TypeScript / Node @spaturzu/sdk README

Python spaturzu README

OpenClaw plugin ⚠️ experimental @spaturzu/openclaw README

⚠️ @spaturzu/openclaw is a work in progress and not yet ready for production use. APIs may change without notice.

Both the TypeScript and Python SDKs treat the underlying provider clients as optional dependencies — install only the ones you actually call.

Installation

Python — published on PyPI as spaturzu (Python 3.10+):

pip install spaturzu # core pip install "spaturzu[openai]" # with the OpenAI integration pip install "spaturzu[all]" # every provider integration

TypeScript / Node — published on npm as @spaturzu/sdk (ESM-only):

npm install @spaturzu/sdk

…then whichever provider clients you call (these are normal npm packages):

npm install openai @anthropic-ai/sdk @aws-sdk/client-bedrock-runtime @google/genai @mistralai/mistralai

Once installed, every import in this README works as written. pnpm / yarn accept the same package name (pnpm add @spaturzu/sdk).

Documentation

Full docs & guides: https://spaturzu.superchiu.org/docs

TypeScript / Node API: typescript/README.md

Python API: python/README.md

OpenClaw plugin (experimental): openclaw/README.md

For AI tools / LLMs: https://spaturzu.superchiu.org/llms.txt

What you can do

The examples below all build on this one-time setup. Every later snippet reuses spaturzu/sp, openai, and messages from here.

// TypeScript import { Spaturzu } from "@spaturzu/sdk"; import OpenAI from "openai";

const spaturzu = new Spaturzu({ apiKey: process.env.SPATURZU_API_KEY }); const openai = spaturzu.wrapOpenAI(new OpenAI());

const messages = [{ role: "user", content: "Summarize the latest sales report." }];

Python

import os from spaturzu import spaturzu from openai import OpenAI

sp = spaturzu(api_key=os.environ["SPATURZU_API_KEY"]) openai = sp.wrap_openai(OpenAI())

messages = [{"role": "user", "content": "Summarize the latest sales report."}]

Wrapping is transparent: the wrapped client has the same methods and return types as the original. You keep calling the provider exactly as before — spaturzu just meters each call in the background.

  1. Attribute cost to a named agent

Wrap a block of work in run("name", …) and every model call inside it is billed to that agent — so the dashboard shows cost per agent, not one undifferentiated total.

// TypeScript await spaturzu.run("researcher", async () => { await openai.chat.completions.create({ model: "gpt-4o", messages }); });

Python

with sp.run("researcher"): openai.chat.completions.create(model="gpt-4o", messages=messages)

  1. Nest sub-agents into a cost tree

Nested run() calls share one run id and extend the agent path, so a multi-step workflow shows up as a tree (research › synthesize).

// TypeScript await spaturzu.run("research", async () => { await openai.chat.completions.create({ model: "gpt-4o", messages }); // path: research

await spaturzu.run("synthesize", async () => { await openai.chat.completions.create({ model: "gpt-4o", messages }); // path: research › synthesize }); });

Python

with sp.run("research"): openai.chat.completions.create(model="gpt-4o", messages=messages) # path: research with sp.run("synthesize"): openai.chat.completions.create(model="gpt-4o", messages=messages) # path: research › synthesize

  1. Slice cost by team, customer, or environment with tags

Set tags globally on the client, or per-frame on a run(). Frame tags merge with the global ones (inner wins on conflict), so you can break spend down by any dimension you like.

// TypeScript const spaturzu = new Spaturzu({ apiKey: process.env.SPATURZU_API_KEY, tags: { env: "prod", team: "growth" }, // on every call });

await spaturzu.run("billing-agent", { tags: { customer: "acme" } }, async () => { await openai.chat.completions.create({ model: "gpt-4o", messages }); });

Python

sp = spaturzu( api_key=os.environ["SPATURZU_API_KEY"], tags={"env": "prod", "team": "growth"}, # on every call )

with sp.run("billing-agent", tags={"customer": "acme"}): openai.chat.completions.create(model="gpt-4o", messages=messages)

  1. Stop runaway spend with a hard-cap budget

When an agent's budget is exhausted, the wrapped call raises BudgetExceededError before it reaches the provider — so a refused call costs nothing. (Use onBreach: "warn" / "on_breach": "warn" to log and proceed instead of throwing.)

// TypeScript import { BudgetExceededError } from "@spaturzu/sdk";

const capped = spaturzu.wrapOpenAI(new OpenAI(), { budget: { hardCap: true, onBreach: "throw" }, });

try { await capped.chat.completions.create({ model: "gpt-4o", messages }); } catch (err) { if (err instanceof BudgetExceededError) { // budget hit — the request never left your process, so no tokens were spent } }

Python

from spaturzu import BudgetExceededError

capped = sp.wrap_openai(OpenAI(), budget={"hard_cap": True, "on_breach": "throw"})

try: capped.chat.completions.create(model="gpt-4o", messages=messages) except BudgetExceededError: pass # call never reached OpenAI — no spend

  1. Survive a provider outage with cross-provider fallback

Give a wrap a fallback chain. On a retryable error (429 / 5xx / connection), spaturzu transparently retries the next provider — and translates the response back to your primary provider's shape, so your code is unchanged.

// TypeScript import Anthropic from "@anthropic-ai/sdk";

const resilient = spaturzu.wrapOpenAI(new OpenAI(), { fallback: [ { provider: "anthropic", client: new Anthropic(), model: "claude-3-5-haiku-20241022" }, ], });

// If OpenAI is down, this is served by Anthropic — still returns an OpenAI-shaped response. const r = await resilient.chat.completions.create({ model: "gpt-4o", messages });

Python

from anthropic import Anthropic

resilient = sp.wrap_openai(OpenAI(), fallback=[ {"provider": "anthropic", "client": Anthropic(), "model": "claude-3-5-haiku-20241022"}, ])

r = resilient.chat.completions.create(model="gpt-4o", messages=messages)

All 20 directional provider pairs are supported. v1 fallback is non-streaming, text-only (no tools / response_format).

One wrap method per provider

The same five providers, the same shape, in both languages. Streaming and sync/async calls are metered automatically — no extra configuration.

Provider TypeScript Python

OpenAI (+ OpenAI-compatible) wrapOpenAI wrap_openai

Anthropic wrapAnthropic wrap_anthropic

Amazon Bedrock wrapBedrock wrap_bedrock

Google Gemini wrapGemini wrap_gemini

Mistral wrapMistral wrap_mistral

Short-lived processes (CLIs, serverless): call spaturzu.flush() / sp.flush() before exit so queued metering rows are sent.

For the complete API — every option, streaming details, and per-provider notes — see the TypeScript and Python READMEs, or the full docs at https://spaturzu.superchiu.org/docs.

Repository layout

sdks/ ├── typescript/ @spaturzu/sdk — Node/TS SDK (5 providers, 20 fallback pairs) ├── python/ spaturzu — Python SDK (parity with the TS surface) └── openclaw/ @spaturzu/openclaw — OpenClaw metering/budget plugin (WIP)

Development

TypeScript packages (managed as a pnpm workspace):

pnpm install pnpm build # build all packages pnpm test # run all test suites pnpm typecheck

Python SDK:

cd python python3 -m venv .venv .venv/bin/pip install -e ".[dev,all]" .venv/bin/pytest

License

MIT © Superchiu Ltd

spaturzu is a product of Superchiu Ltd.

About

Know which AI agent spent the money.

spaturzu.superchiu.org

Topics

bedrock

openai

ai-agents

finops

llms

genai

llm-tools

mistralai

antrophic

Resources

Readme

License

MIT license

Uh oh!

There was an error while loading. Please reload this page.

Activity

Stars

3 stars

Watchers

0 watching

Forks

0 forks

Report repository

Releases 6

python-v0.1.7

Latest

Jun 11, 2026

+ 5 releases

Packages 0

Uh oh!

There was an error while loading. Please reload this page.

Contributors 1

Nu11P01nt3r3xc3pt10n Nu11P01nt3r3xc3pt10n

Languages

Python 53.7%

TypeScript 46.3%