@broberg/ai-sdk

中文文档
0.69.4发布于 2026/11/01FSL-1.1-Apache-2.0TypeScriptES Module构建来源已验证

统一的AI/LLM SDK -一个门面,所有提供商,所有功能,每次调用的一流成本控制。

中文使用文档
文档目录9 节
  1. 数据到哪里去了?- - - - - - usage.region
  2. 快速启动
  3. 功能
  4. 大声朗读:实际上所说的不是你的 text
  5. 供应商和层级
  6. 这将走向何方?- - - - - - wouldRouteTo (预测,不是事实)
  7. 成本,预算和水槽
  8. 成本跟踪是默认开启的(v0.24+)
  9. 许可证

@broberg / ai-sdk

一个AI/LLM SDK -一个门面,所有的提供者,所有的功能 一流的 对每个电话进行成本控制.

与提供者无关的外观:您的代码调用 ai.chat(), ai.vision(), ai.image() -永远不要直接提供SDK。通过改变一个层来交换提供商; 不是你的电话亭。每次调用都会返回 Usage (令牌,成本,延迟, 运输, data-residency地区),并将其扩展到任何成本池。

bun add @broberg/ai-sdk   # or: npm i @broberg/ai-sdk

数据到哪里去了?- - - - - - usage.region

每个回应都带有 实际应答的端点的数据驻留:

const { text, usage } = await ai.chat({ prompt, tier: "smart" });
if (usage.region !== "eu") throw new Error(`personal data would have left the EU (${usage.region})`);

只有 "eu" 是一个积极的主张。 "unknown" 意思是我们不能这么说 region !== "us" 是 不 欧盟检查——它通过了每一个OpenRouter调用。

region 是由 主人 使用的请求,而不是来自提供程序的名称- vertex, azure 和 bfl 默认是EU,但每个都需要重写,所以a 基于名称的表将报告 eu 因为有人指着电话 us-central1.

三件事决定你的防御是否有效:

电话结束后usage.region - - - - - - "eu" | "us" | "cn" | "unknown"
打电话之前regionOfHost(urlOrHostname) ——这是拒绝的唯一方式 之前 字节离开
从来没有regionOfProvider(name) -见下文
import { regionOfHost } from "@broberg/ai-sdk";
if (regionOfHost("https://api.mistral.ai/v1") !== "eu") throw new Error("not EU");

不建立常驻警卫吗 regionOfProvider. 一个名字不能回答 托管时,提供商采取 baseUrl,所以 regionOfProvider("mistral") 是 "unknown" ——还有一个守卫,写着 regionOfProvider(p) === "eu" 因此拒绝了 米斯特拉尔,欧盟唯一的航线。 故障关闭和无用:它过滤掉 这是它存在的目的。

容许清单 (provider, model) 对的另一边有相同的孔:a 米斯特拉尔门前的大门还没有动 "mistral"/"mistral-large-latest",两者都匹配 田野和传球。 regionOfHost 答案 "unknown" 这是最诚实的回答。

快速启动

import { createAI } from "@broberg/ai-sdk";

const ai = createAI(); // real adapters, keys from env (ANTHROPIC_API_KEY, …)

const { text, usage } = await ai.chat({ prompt: "Say hi in Danish" });
console.log(text, usage.costUsd);

const v = await ai.vision({ image: "https://…/photo.png", prompt: "Describe" });
const img = await ai.image({ prompt: "a sunlit beach in Blokhus" });
const da = await ai.translate({ text: "hello", to: "Danish" });
const emb = await ai.embedding({ text: ["a", "b"] });

功能

chat · vision · translate · image (歧视。ai default / OpenRouter embedding · transcribe (耳语)优先 提示合同 具有结构化输出:

import { z } from "zod";
const { data } = await ai.contracts.extract({
  text: "Sanne is 40 and lives in Blokhus",
  schema: z.object({ name: z.string(), age: z.number(), city: z.string() }),
});
// also: ai.contracts.{ mockup, design, classify, rerank }

ai.judge - Jev (TypeSafe), F066 不是聊天模式:输入是/否,选择和 对一段内容的问题进行评分,每个答案都有一个校准的概率 和一个 confidence 你可以登机了。每百万输入令牌$0.042,输出免费。 美国托管-不用于个人数据。 看到文档/ API。md§4。

大声朗读:实际上所说的不是你的 text

ai.tts({ pronunciations }) 重写您的文本之前,提供者看到它- broberg.ai 变成四个口语单词。所以 text 是音频的复述吗,和 文字荧光笔建立在 text 孤独会漂向每一个重写的字。

有两个领域弥补了这一差距:

const { audio, wordTimings, ssml } = await ai.tts({
  text, voice, wordTimings: true, pronunciations,
  override: { provider: "azure" },
});
wordTimings.words      // [{ text, startMs, endMs, sourceStart, sourceEnd }]
wordTimings.unaligned  // spoken words we could NOT place — never a guessed offset
ssml                   // the EXACT markup we sent, verbatim — your ground truth

sourceStart/sourceEnd 指数 你的原文,因为Azure会报告音频时间 只有和不携带任何文本偏移量;手稿的链接在这里。 ssml 存在,因此您可以检查派生而不是信任它-它是字符串 我们发送了,而不是重建(由无法验证所说内容的消费者提交,F055.4)。 它是 undefined 在没有建立标记的路由上;空字符串则是另一种说法。

两个让人惊讶的匹配规则,两者均以 pronunciations:

  • 不区分大小写的。 ai, Ai 和 AI 所有匹配规则为 AI.
  • 碰到连字符的单词保持其拼写。 broberg.ai-drevet 不会被重写 除非这个条目 matchInCompounds: true -保持的规则 mail 的 e-mail. 一个消费者得出了他们自己的期望单词列表,得到了这两个 字段存在之前出现错误。

Word计时需要Azure的BATCH路由,不同的呼叫形式(提交→投票→ZIP), 该路由需要资源的自定义子域-区域主机回答401 使用有效的密钥,责备密钥。集 AZURE_SPEECH_RESOURCE;没有它 wordTimings 在调用之前失败,命名修复。

供应商和层级

适配器: 人为 (HTTP + claude -p 子流程), OpenAI, 谷歌 双子座, DeepInfra, OpenRouter (包括极大极小), fal.ai (图片)。

通过命名调用路由 层 - - - - - - fast · smart · powerful · cheap · vision · embedding -每个解析为a (provider, model, transport) 三, 每次调用可重写:

await ai.chat({ prompt: "…", tier: "powerful" });
await ai.chat({ prompt: "…", override: { provider: "openrouter", model: "minimax/minimax-m2.7" } });

图像-光栅vs.矢量。 ai.image() 默认为false。ai(栅格PNG)。为 向量/ SVG 输出(标识),覆盖到OpenRouter Recraft - slug是一个 OpenRouter 模型,而不是一个失败的应用程序id:

await ai.image({ prompt: "…", override: { provider: "openrouter", model: "recraft/recraft-v4.1-vector" } });
// → data:image/svg+xml;base64,… with ground-truth cost

recraft/recraft-v4.1-pro-vector 为复杂向量提供了更高的细节(~$0.30) 每张图片vs 0.08美元); recraft/recraft-v4.1-pro 光栅等效(0.21美元)。

cheap 默认为最便宜的云模型 据悉,小 (欧盟/巴黎托管,gdp安全,~ 0.10美元/ 0.30美元)-所以成本层呼叫是安全的 默认的个人资料;覆盖每个呼叫,以获得更便宜的非个人路线。 ( claude -p 子进程传输仍然可以通过显式传输 override: { transport: "subprocess" },但不再是默认路由。)

这将走向何方?- - - - - - wouldRouteTo (预测,不是事实)

usage.region 答案 电话去了哪里. 它无法回答 我可以寄这个吗?,因为 它只在字节剩余时存在。 wouldRouteTo 回答那一半:

import { wouldRouteTo } from "@broberg/ai-sdk";

const f = wouldRouteTo("smart");
// { kind: "forecast", provider: "mistral", host: "https://api.mistral.ai/v1",
//   wouldRouteTo: "eu", onlyIf: [ …the assumptions… ] }

它的形状故意不像 usage.region. 没有所谓的领域 region 在 这是由一个考验来执行的:一个可能是错误的预测不能穿衣服 事实不能。 onlyIf 回来 与 而不是生活在这些 文档-只有当你推翻“否”时,答案才有效 baseUrl 没有退路,因为 退路是一条路线,而这条路线决定了居住权.

"depends-on-config" 是一个答案,而不是一个失败。 azure, vertex, deepl, requesty 和 fal 根据您提供的值构建它们的主机。 requesty 是这个国家存在的原因吗 有 两个 欧盟和非欧盟的东道主,所以我们在那里命名的任何地区都会有居住权 由表而不是由路由决定的。

它什么都不拒绝。 它永远不会抛出和阻止调用-即使是无意义的 提供者。居住权政策属于每个消费者,而不属于这个包裹;这是 使决策成为可能的信息,这是门的反义词。

为什么它会说 "eu" 哪里 regionOfProvider 说 "unknown": 它要求 主人 这个 SDK将实际使用,而不是提供商的 名字. regionOfProvider("mistral") 是 "unknown" 根据设计,适配器需要一个 baseUrl. 由一位消费者提出的 手动复制我们的层表到他们自己的仓库来回答这个问题;现在可以删除该副本 而不是维护。

成本,预算和水槽

import { createAI, upmetricsSink, sqliteSink, multiSink } from "@broberg/ai-sdk";

const ai = createAI({
  budget: { perCallUsd: 0.05, rollingUsd: 5 }, // pre-flight guard (throws BudgetExceededError)
  costSink: multiSink([
    upmetricsSink({ baseUrl: "https://upmetrics.org", apiKey: process.env.UPMETRICS_API_KEY!, agentName: "my-app" }),
    sqliteSink({ dbPath: "./ai-cost.db" }), // Bun only — throws on Node, see below
  ]),
});

汇: upmetricsSink (规范), discordSink, sqliteSink, multiSink, noopSink. 失败的水槽 在 一个调用永远不会使该调用崩溃。

sqliteSink 和 getCostSummary 是Bun-only (在v0.48.0更改,已发布 2026年9月22日)。他们的背后是 bun:sqlite, Node根本无法导入。在Node上 扔到哪里 构建他们 -故意的,这是一个行为改变:直到v0.47你 有个水槽砸在每个 record(),客户端吞下每个调用 由于设计上的失误,成本跟踪悄无声息地消失了。空成本数据集 和一个正常工作的在报告里看起来一模一样。 关于Node的使用 upmetricsSink. sqliteBudgetStore 也是出于同样的原因;它总是彻底失败 在那里,因为预算错误没有被吞下。

成本跟踪是默认开启的(v0.24+)

你不用给水槽装电线。如果 UPMETRICS_API_KEY 是在羡慕,一个赤裸 createAI() 自动附加upmetrics接收器-这存在是因为大多数 呼叫地点没有经过水槽,留下约91%的西北风花费不可见。没有钥匙 环境→没有沉没,没有坠毁(船暗)。

每个客户端都需要一个名称(0.63+,F034.4)。 createAI() 启动时抛出,除非 UPMETRICS_AGENT_NAME 准备好了还是你过去了 agentName -即使没有钥匙,所以 您的测试在生产之前捕获它。业主订单2026-10-15:«Ingen morv ære 匿名»-在30天内,8个回购者发送了他们的支出 unknown. 不需要 有一个明确的 costSink 或者 costSink: null.

createAI({ agentName: "my-app" }); // key in env → tracked; no key → nothing happens
createAI();                      // same, if UPMETRICS_AGENT_NAME is set — else throws
createAI({ costSink: mySink });  // explicit sink wins
createAI({ costSink: null });    // explicit OPT-OUT — see below
Env var效果
UPMETRICS_API_KEY唯一的开关。 Present→tracking on。
UPMETRICS_AGENT_NAME行标签, 要求 (或 agentName) -在生产环境和测试环境中设置它。到0.62时,回退为 npm_package_name,空在npm/bun脚本外面,所以 node dist/… 和Docker服务日志为 unknown.
UPMETRICS_BASE_URL摄取宿主。默认为 https://upmetrics.org.
UPMETRICS_COMPLIANCE1 设置合规模式。 Usage 两种方式都不携带提示/响应文本。

在repo中采用它-首先要做两件事,否则采用会破坏 它本来要透露的数字是:

  1. 在你已经申报了自己的费用的地方选择退出。 通过 costSink: null. 否则SDK会添加一个 第二个 上面的报告路径 你和每一通电话都算数 两次,在生产中,没有错误 任何地方。(发现的 buddy,谁聚集到一起 cli_usage 每小时推一次。)

  2. 不要让钥匙进入你的测试。 测试运行器自动加载 .env (包 做;Vitest与dotenv设置),所以任何套件达到a createAI() 将 制造用于生产遥测. 请注意 触发因素是 进口 构建客户端的模块——模块级 createAI() 在没有任何命令的情况下武装它。去掉整个前缀:

    // test-setup.ts  — bunfig.toml: [test] preload = ["./test-setup.ts"]
    for (const k of Object.keys(process.env)) {
      if (k.startsWith("UPMETRICS_")) delete process.env[k];
    }
    

    去掉 前缀不是名单,而是你必须记住的守卫 更新是一种无声腐烂的东西。并断言 效果 (没有 UPMETRICS_* 对测试可见)有一个控制,证明探针可以变红;一个测试 检查“密钥未定义”通过,同时不保护机器上的任何内容 他从来没有钥匙。

采用 每个回购,选择加入 -没有舰队范围的推进。打开它 回购值得关注,因为双方都有防范措施。

许可证

目前2.0 - 1.1 - apache -

版本与发布记录

@broberg/ai-sdk 共发布 124 个版本,首次发布于 2026/06/02;过去 12 个月发布了 124 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.69.4latest2026/11/012 天前
0.69.32026/11/012 天前
0.69.22026/10/313 天前
0.69.12026/10/313 天前
0.69.02026/10/277 天前
0.68.12026/10/277 天前

关于 @broberg/ai-sdk 的常见问题

@broberg/ai-sdk 是什么?

统一的AI/LLM SDK -一个门面,所有提供商,所有功能,每次调用的一流成本控制。

如何安装 @broberg/ai-sdk?

在项目目录运行 npm install @broberg/ai-sdk,或使用 pnpm add / yarn add / bun add。

@broberg/ai-sdk 支持 TypeScript 吗?

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

@broberg/ai-sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@broberg/ai-sdk 可以商用吗?

@broberg/ai-sdk 使用 FSL-1.1-Apache-2.0 许可。声明的许可为 FSL-1.1-Apache-2.0,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@broberg/ai-sdk 还在维护吗?

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