@creem_io/ingestion

中文文档
0.1.3发布于 2026/09/25MITTypeScriptCommonJS构建来源已验证近 30 天下载 3697

Usage ingestion toolkit for Creem — buffered, retry-safe, non-blocking event delivery with pluggable metering strategies

原始使用文档
文档目录7 节
  1. Why not just call the API?
  2. Buffered ingestion
  3. Direct ingestion
  4. Strategies
  5. Vercel AI SDK
  6. Timed execution
  7. Configuration

@creem_io/ingestion

Usage ingestion toolkit for Creem — buffered, retry-safe, non-blocking delivery of usage events, with a pluggable strategy layer for automatic metering (LLM calls, streams, timed executions).

npm install @creem_io/ingestion

Why not just call the API?

You can — creem.events.ingestEvents() works. This package exists for the delivery guarantees your request path needs:

  • Never blocks, never throws. enqueue() buffers in memory and returns immediately. Delivery failures surface via an onError hook, not as exceptions inside your user's request.
  • Retry-safe by construction. Every event gets an event_id at enqueue time; retries resend the same ids and Creem's (store, event_id) dedup collapses them — a retried batch can never double-bill.
  • Batched. Events flush when a batch fills (100, the server cap) or on an interval, not one HTTP call per event.
  • Poison-pill eviction. Creem validates batches all-or-nothing. When the server rejects one invalid event, the pipeline evicts exactly that event (reported via onError) and delivers the rest — one bad event can't take 99 good ones down with it.
  • Serverless-aware. await flush() before your handler returns (or hand it to waitUntil). The idle flush timer is unref'd, so it never holds a Node process open.

Buffered ingestion

import { Ingestion } from "@creem_io/ingestion";

const ingestion = Ingestion({
  apiKey: process.env.CREEM_API_KEY!,
  onError: (error) => console.error("[creem]", error.code, error.message),
  onWarnings: (warnings) => console.warn("[creem]", warnings),
});

// In your request handler — synchronous, cannot fail the request:
ingestion.enqueue({
  name: "tokens_used",
  externalCustomerId: userId, // or { customerId: "cust_..." }
  properties: { tokens: 512, model: "gpt-4o" },
});

// Serverless teardown / graceful shutdown:
await ingestion.flush();

Each event names its customer exactly one way: customerId (the Creem id) or externalCustomerId (your own id, registered as external_id on the customers API).

Direct ingestion

When you want the response (and are prepared to handle errors yourself):

const result = await ingestion.ingest([
  { name: "tokens_used", customerId: "cust_abc", properties: { tokens: 512 } },
]);
// { accepted: 1, eventIds: ["..."], warnings?: [...] }

Events without an eventId get one before sending, so retrying the call — yourself, or through a client configured with the SDK's backoff — replays the same ids and cannot bill twice.

warnings tells you when an accepted event will not produce billable usage — no meter listens to the event name, only archived meters match, or the timestamp falls in a finalized billing period.

Strategies

Strategies wrap a client you already use and meter it automatically. The chain binds a strategy to an event name; client(customer) returns a fresh wrapped client per customer (strategies are stateless — one instance serves concurrent customers safely):

const metered = ingestion
  .strategy(new SomeStrategy(/* ... */))
  .cost((emission) => Number(emission.properties.tokens) * 0.000002)
  .ingest("llm-usage");

const client = metered.client({ externalCustomerId: userId });
// use `client` normally — every call enqueues a usage event

Emitted events carry the strategy's raw measurements in properties, plus the reserved keys the SDK owns: strategy (the capture mechanism) and _cost (when a .cost() resolver is attached). Keys starting with _ are reserved for SDK-written values.

Resolvers run inside the metered call. If one throws, the event is dropped and reported via onError as resolver_failed — it never throws into the call you are metering.

Vercel AI SDK

Wrap a language model once and every generateText / streamText call is metered — token counts, model, and vendor, with no per-call code. Imported from the /ai-sdk subpath so its types only load when you use it (AI SDK 6+ / LanguageModelV3+V4):

import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
import { AiSdkStrategy } from "@creem_io/ingestion/ai-sdk";

const metered = ingestion
  .strategy(new AiSdkStrategy(openai("gpt-4o")))
  .ingest("llm-usage");

const model = metered.client({ externalCustomerId: userId });
const result = await generateText({ model, prompt });
// → event: { input_tokens, output_tokens, cached_input_tokens,
//            total_tokens, model, vendor, _llm, strategy }

Streaming is metered from the stream's finish part as it passes through — consuming the stream once is enough, nothing is buffered or double-read. A stream cancelled before its finish part bills nothing.

Timed execution

The cheapest strategy: time a unit of work, bill the duration. Only successful executions emit — a throwing execution propagates its error and bills nothing:

import { DeltaTimeStrategy } from "@creem_io/ingestion";

const metered = ingestion
  .strategy(new DeltaTimeStrategy())
  .ingest("compute-time");

const timed = metered.client({ customerId });
const output = await timed(() => renderVideo(input));
// → event: { delta_time_ms, strategy }

Further strategies (raw LLM provider clients, streams/bytes, OpenTelemetry) build on the same IngestionStrategy base class exported here.

Configuration

OptionDefaultMeaning
apiKey—Creem API key with the events:write scope
serverURLproductione.g. https://test-api.creem.io for test mode
client—bring your own configured Creem SDK instance
maxBatchSize100events per request (server cap)
flushIntervalMs5000idle time before a partial batch flushes
maxQueueSize10000buffer bound; overflow drops the newest event via onError
maxRetries5retries per batch after the first attempt (up to 6 deliveries; backoff capped at 30s)
onError—every delivery-side failure; never thrown, and a throwing hook is swallowed
onWarnings—advisory 202 warnings

版本与发布记录

@creem_io/ingestion 共发布 4 个版本,首次发布于 2026/09/16;过去 12 个月发布了 4 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.3latest2026/09/251 个月前
0.1.22026/09/181 个月前
0.1.12026/09/171 个月前
0.1.02026/09/161 个月前

关于 @creem_io/ingestion 的常见问题

@creem_io/ingestion 是什么?

Usage ingestion toolkit for Creem — buffered, retry-safe, non-blocking event delivery with pluggable metering strategies

如何安装 @creem_io/ingestion?

在项目目录运行 npm install @creem_io/ingestion,或使用 pnpm add / yarn add / bun add。

@creem_io/ingestion 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@creem_io/ingestion 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@creem_io/ingestion 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@creem_io/ingestion 可以商用吗?

@creem_io/ingestion 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@creem_io/ingestion 的下载量有多少?

根据 npm 官方下载统计,@creem_io/ingestion 在 2026/10/11 – 2026/11/09 这 30 天内被下载 3,697 次,日均约 123 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@creem_io/ingestion 还在维护吗?

最新版本 0.1.3 发布于 2026/09/25(1 个月前);至今共发布 4 个版本,其中过去 12 个月发布了 4 个;首次发布于 2026/09/16。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。