{"slug": "which-ai-agent-spent-the-money-on-your-openai-anthropic-bill", "title": "Which AI agent spent the money on your OpenAI/Anthropic bill", "summary": "Spaturzu released open-source SDKs for TypeScript and Python that enable per-agent LLM cost attribution, budget enforcement, and cross-provider fallback by wrapping existing provider clients like OpenAI and Anthropic with a single import change.", "body_md": "Open-source client SDKs for [ spaturzu](https://spaturzu.superchiu.org) —\nper-agent LLM cost attribution, budget enforcement, and cross-provider\nfallback. Wrap your existing provider client (OpenAI, Anthropic, Bedrock,\nGemini, Mistral) and every call is metered, attributed to the agent that made\nit, and optionally budget-capped — without changing how you call the model.\n\nAlready calling OpenAI, Anthropic, Bedrock, Gemini, or Mistral? Change a single\nimport — from `import OpenAI from \"openai\"`\n\nto\n`import OpenAI from \"@spaturzu/sdk/openai\"`\n\n. Construction and call sites stay\nexactly the same, and you can attribute each call to the agent that made it:\n\n``` python\nimport OpenAI from \"@spaturzu/sdk/openai\";\n\n// Swap one import — reads SPATURZU_API_KEY + OPENAI_API_KEY from env.\nconst openai = new OpenAI();\n\n// Tag any call with the agent that made it — one line, no closure.\nawait openai.withAgent(\"support-triage\").chat.completions.create({ /* … */ });\n```\n\nThat's the whole migration — no new objects, no wrappers around your call\nsites. **Python is identical:** `from spaturzu.openai import OpenAI`\n\n, then\n`client.with_agent(\"support-triage\").chat.completions.create(...)`\n\n. The same\none-import swap works for every provider — `@spaturzu/sdk/anthropic`\n\n,\n`/bedrock`\n\n, `/google`\n\n, `/mistral`\n\n(Python: `spaturzu.anthropic`\n\n, and so on).\n\nRunning multi-step workflows? Group several calls under one agent with\n\n`run()`\n\ninstead — see[What you can do]below.\n\n| SDK | Package | Docs |\n|---|---|---|\nTypeScript / Node |\n`@spaturzu/sdk` |\n\n[README](/Nu11P01nt3r3xc3pt10n/spaturzu-sdks/blob/main/typescript/README.md)**Python**`spaturzu`\n\n[README](/Nu11P01nt3r3xc3pt10n/spaturzu-sdks/blob/main/python/README.md)**OpenClaw plugin*** experimental*`@spaturzu/openclaw`\n\n[README](/Nu11P01nt3r3xc3pt10n/spaturzu-sdks/blob/main/openclaw/README.md)\n\n⚠️ and not yet ready for production use. APIs may change without notice.`@spaturzu/openclaw`\n\nis a work in progress\n\nBoth the TypeScript and Python SDKs treat the underlying provider clients as\n**optional dependencies** — install only the ones you actually call.\n\n**Python** — published on PyPI as\n[ spaturzu](https://pypi.org/project/spaturzu/) (Python 3.10+):\n\n```\npip install spaturzu                   # core\npip install \"spaturzu[openai]\"         # with the OpenAI integration\npip install \"spaturzu[all]\"            # every provider integration\n```\n\n**TypeScript / Node** — published on npm as\n[ @spaturzu/sdk](https://www.npmjs.com/package/@spaturzu/sdk) (ESM-only):\n\n```\nnpm install @spaturzu/sdk\n# …then whichever provider clients you call (these are normal npm packages):\nnpm install openai @anthropic-ai/sdk @aws-sdk/client-bedrock-runtime @google/genai @mistralai/mistralai\n```\n\nOnce installed, every import in this README works as written. `pnpm`\n\n/ `yarn`\n\naccept the same package name (`pnpm add @spaturzu/sdk`\n\n).\n\n**Full docs & guides:**[https://spaturzu.superchiu.org/docs](https://spaturzu.superchiu.org/docs)** TypeScript / Node API:**`typescript/README.md`\n\n**Python API:**`python/README.md`\n\n**OpenClaw plugin**(experimental):`openclaw/README.md`\n\n**For AI tools / LLMs:**[https://spaturzu.superchiu.org/llms.txt](https://spaturzu.superchiu.org/llms.txt)\n\nThe examples below all build on this one-time setup. Every later snippet\nreuses `spaturzu`\n\n/`sp`\n\n, `openai`\n\n, and `messages`\n\nfrom here.\n\n``` python\n// TypeScript\nimport { Spaturzu } from \"@spaturzu/sdk\";\nimport OpenAI from \"openai\";\n\nconst spaturzu = new Spaturzu({ apiKey: process.env.SPATURZU_API_KEY });\nconst openai = spaturzu.wrapOpenAI(new OpenAI());\n\nconst messages = [{ role: \"user\", content: \"Summarize the latest sales report.\" }];\npython\n# Python\nimport os\nfrom spaturzu import spaturzu\nfrom openai import OpenAI\n\nsp = spaturzu(api_key=os.environ[\"SPATURZU_API_KEY\"])\nopenai = sp.wrap_openai(OpenAI())\n\nmessages = [{\"role\": \"user\", \"content\": \"Summarize the latest sales report.\"}]\n```\n\nWrapping is transparent: the wrapped client has the\n\nsame methods and return typesas the original. You keep calling the provider exactly as before — spaturzu just meters each call in the background.\n\nWrap a block of work in `run(\"name\", …)`\n\nand every model call inside it is\nbilled to that agent — so the dashboard shows cost *per agent*, not one\nundifferentiated total.\n\n``` js\n// TypeScript\nawait spaturzu.run(\"researcher\", async () => {\n  await openai.chat.completions.create({ model: \"gpt-4o\", messages });\n});\n# Python\nwith sp.run(\"researcher\"):\n    openai.chat.completions.create(model=\"gpt-4o\", messages=messages)\n```\n\nNested `run()`\n\ncalls share one run id and extend the agent path, so a\nmulti-step workflow shows up as a tree (`research › synthesize`\n\n).\n\n``` js\n// TypeScript\nawait spaturzu.run(\"research\", async () => {\n  await openai.chat.completions.create({ model: \"gpt-4o\", messages }); // path: research\n\n  await spaturzu.run(\"synthesize\", async () => {\n    await openai.chat.completions.create({ model: \"gpt-4o\", messages }); // path: research › synthesize\n  });\n});\n# Python\nwith sp.run(\"research\"):\n    openai.chat.completions.create(model=\"gpt-4o\", messages=messages)        # path: research\n    with sp.run(\"synthesize\"):\n        openai.chat.completions.create(model=\"gpt-4o\", messages=messages)    # path: research › synthesize\n```\n\nSet tags globally on the client, or per-frame on a `run()`\n\n. Frame tags merge\nwith the global ones (inner wins on conflict), so you can break spend down by\nany dimension you like.\n\n``` js\n// TypeScript\nconst spaturzu = new Spaturzu({\n  apiKey: process.env.SPATURZU_API_KEY,\n  tags: { env: \"prod\", team: \"growth\" },        // on every call\n});\n\nawait spaturzu.run(\"billing-agent\", { tags: { customer: \"acme\" } }, async () => {\n  await openai.chat.completions.create({ model: \"gpt-4o\", messages });\n});\n# Python\nsp = spaturzu(\n    api_key=os.environ[\"SPATURZU_API_KEY\"],\n    tags={\"env\": \"prod\", \"team\": \"growth\"},       # on every call\n)\n\nwith sp.run(\"billing-agent\", tags={\"customer\": \"acme\"}):\n    openai.chat.completions.create(model=\"gpt-4o\", messages=messages)\n```\n\nWhen an agent's budget is exhausted, the wrapped call raises\n`BudgetExceededError`\n\n**before** it reaches the provider — so a refused call\ncosts nothing. (Use `onBreach: \"warn\"`\n\n/ `\"on_breach\": \"warn\"`\n\nto log and\nproceed instead of throwing.)\n\n``` js\n// TypeScript\nimport { BudgetExceededError } from \"@spaturzu/sdk\";\n\nconst capped = spaturzu.wrapOpenAI(new OpenAI(), {\n  budget: { hardCap: true, onBreach: \"throw\" },\n});\n\ntry {\n  await capped.chat.completions.create({ model: \"gpt-4o\", messages });\n} catch (err) {\n  if (err instanceof BudgetExceededError) {\n    // budget hit — the request never left your process, so no tokens were spent\n  }\n}\npython\n# Python\nfrom spaturzu import BudgetExceededError\n\ncapped = sp.wrap_openai(OpenAI(), budget={\"hard_cap\": True, \"on_breach\": \"throw\"})\n\ntry:\n    capped.chat.completions.create(model=\"gpt-4o\", messages=messages)\nexcept BudgetExceededError:\n    pass  # call never reached OpenAI — no spend\n```\n\nGive a wrap a fallback chain. On a retryable error (429 / 5xx / connection),\nspaturzu transparently retries the next provider — and **translates the\nresponse back to your primary provider's shape**, so your code is unchanged.\n\n``` python\n// TypeScript\nimport Anthropic from \"@anthropic-ai/sdk\";\n\nconst resilient = spaturzu.wrapOpenAI(new OpenAI(), {\n  fallback: [\n    { provider: \"anthropic\", client: new Anthropic(), model: \"claude-3-5-haiku-20241022\" },\n  ],\n});\n\n// If OpenAI is down, this is served by Anthropic — still returns an OpenAI-shaped response.\nconst r = await resilient.chat.completions.create({ model: \"gpt-4o\", messages });\npython\n# Python\nfrom anthropic import Anthropic\n\nresilient = sp.wrap_openai(OpenAI(), fallback=[\n    {\"provider\": \"anthropic\", \"client\": Anthropic(), \"model\": \"claude-3-5-haiku-20241022\"},\n])\n\nr = resilient.chat.completions.create(model=\"gpt-4o\", messages=messages)\n```\n\nAll 20 directional provider pairs are supported. v1 fallback is non-streaming, text-only (no tools /\n\n`response_format`\n\n).\n\nThe same five providers, the same shape, in both languages. Streaming and sync/async calls are metered automatically — no extra configuration.\n\n| Provider | TypeScript | Python |\n|---|---|---|\n| OpenAI (+ OpenAI-compatible) | `wrapOpenAI` |\n`wrap_openai` |\n| Anthropic | `wrapAnthropic` |\n`wrap_anthropic` |\n| Amazon Bedrock | `wrapBedrock` |\n`wrap_bedrock` |\n| Google Gemini | `wrapGemini` |\n`wrap_gemini` |\n| Mistral | `wrapMistral` |\n`wrap_mistral` |\n\nShort-lived processes(CLIs, serverless): call`spaturzu.flush()`\n\n/`sp.flush()`\n\nbefore exit so queued metering rows are sent.\n\nFor the complete API — every option, streaming details, and per-provider\nnotes — see the [TypeScript](/Nu11P01nt3r3xc3pt10n/spaturzu-sdks/blob/main/typescript/README.md) and\n[Python](/Nu11P01nt3r3xc3pt10n/spaturzu-sdks/blob/main/python/README.md) READMEs, or the full docs at\n** https://spaturzu.superchiu.org/docs**.\n\n```\nsdks/\n├── typescript/   @spaturzu/sdk        — Node/TS SDK (5 providers, 20 fallback pairs)\n├── python/       spaturzu             — Python SDK (parity with the TS surface)\n└── openclaw/     @spaturzu/openclaw   — OpenClaw metering/budget plugin (WIP)\n```\n\n**TypeScript packages** (managed as a pnpm workspace):\n\n```\npnpm install\npnpm build       # build all packages\npnpm test        # run all test suites\npnpm typecheck\n```\n\n**Python SDK:**\n\n```\ncd python\npython3 -m venv .venv\n.venv/bin/pip install -e \".[dev,all]\"\n.venv/bin/pytest\n```\n\n[MIT](/Nu11P01nt3r3xc3pt10n/spaturzu-sdks/blob/main/LICENSE) © Superchiu Ltd\n\nspaturzu is a product of **Superchiu Ltd**.", "url": "https://wpnews.pro/news/which-ai-agent-spent-the-money-on-your-openai-anthropic-bill", "canonical_source": "https://github.com/Nu11P01nt3r3xc3pt10n/spaturzu-sdks", "published_at": "2026-06-17 09:30:40+00:00", "updated_at": "2026-06-17 09:52:38.198251+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-infrastructure"], "entities": ["Spaturzu", "OpenAI", "Anthropic", "Bedrock", "Gemini", "Mistral", "TypeScript", "Python"], "alternates": {"html": "https://wpnews.pro/news/which-ai-agent-spent-the-money-on-your-openai-anthropic-bill", "markdown": "https://wpnews.pro/news/which-ai-agent-spent-the-money-on-your-openai-anthropic-bill.md", "text": "https://wpnews.pro/news/which-ai-agent-spent-the-money-on-your-openai-anthropic-bill.txt", "jsonld": "https://wpnews.pro/news/which-ai-agent-spent-the-money-on-your-openai-anthropic-bill.jsonld"}}