@wrenautomation/models

中文文档
0.30.6发布于 2026/11/03PolyForm-Strict-1.0.0TypeScriptES Module构建来源已验证

One door to every model: a Model takes text, image, video, audio and PDF parts, answers with text, tokens and USD, and parses an answer to a zod shape. Adapters for Anthropic, Gemini, Cohere, any OpenAI-compatible server and a shell command (claude -p); e

原始使用文档
文档目录5 节
  1. Use
  2. Errors
  3. Prices
  4. Why the AI SDK
  5. Develop

models

One door to every model call in Wren's repos. A Model takes text, image, video, audio and PDF parts. It answers with text, tokens, USD and why it stopped. ask parses the answer to a zod shape and asks again once when the answer does not fit.

The rule: code that calls a model calls it through this package. A new vendor, a new part kind or a new price goes here, never in the caller. No guard enforces this; this file and wren's designs/2026-10-26-research-engine.md record it.

ImportDoesRuns in
@wrenautomation/modelsModel, Part, Usage; anthropic, gemini, cohere, compatible (Groq, OpenRouter, Mistral, Cerebras, OpenAI), gateway; rotating key rings, fallback model order, geminiLiveKeys, geminiFile (Files API upload); fakeModel, scriptedModel; the errorsNode, Workers
@wrenautomation/models/hearspeech to text: groqEar, cohereEar, compatibleEar (any /audio/transcriptions server); fakeEarNode, Workers
@wrenautomation/models/pricesone price table: MODEL_PRICES, SPEECH_PRICES, priceUsd, priced, speechUsd, addUsage, pricesTextNode, Workers
@wrenautomation/models/commanda shell command as a model (commandModel), Claude Code on the subscription (claudeCode)Node
@wrenautomation/models/speca model from a setting string: modelFromSpec("anthropic:sonnet", env)Node
@wrenautomation/models/judgea judge: typed questions (noul yes or no, choice, score) answered with each label's probability, no text; jevJudge (TypeSafe's Jev, TYPESAFE_API_KEY; Laya with TYPESAFE_BASE_URL), judgeFromSpec("jev", env), fakeJudge, judgeFailure, top. A hint or a router, never the final callNode, Workers

Use

import { anthropic, fallback, gemini } from "@wrenautomation/models";
import { z } from "zod";

const claude = anthropic({ key: [key1, key2], model: "sonnet" });
const { value, usage } = await claude.ask("Name three colours.", z.object({ colours: z.array(z.string()) }));
// usage: { input, output, cacheRead?, cacheWrite?, usd }

const eyes = gemini({ key: geminiKeys });
const reply = await eyes.complete({
  prompt: [
    { type: "text", text: "What is on this slide?" },
    { type: "image", data: pngBytes, mediaType: "image/png" },
  ],
  effort: "low",
});

// A stretch of a YouTube video, sampled at one frame per 2 s, at low resolution.
const clip = await eyes.complete({
  prompt: [
    { type: "video", data: new URL(youtubeUrl), mediaType: "video/mp4", clip: { startS: 60, endS: 90, fps: 0.5 } },
    { type: "text", text: "What is said and shown?" },
  ],
  resolution: "low",
});

// Newest model first; each call starts at the top and moves down past a busy or refused one.
const reader = fallback([gemini({ key, model: "gemini-3.8-flash" }), gemini({ key })], { passes: 4 });

Gemini reads a clipped video on generateContent: withClips adds the videoMetadata field the SDK does not send. The Interactions API also reads a stretch, but on free keys its flash quota is spent at once (seen 2026-10-27; flash-lite answered).

A file too large to send inline goes up once with geminiFile. It lives in one key's project, so read its part with a Gemini model on file.key alone, then file.drop().

Spec strings: fake, claude-code[:model], anthropic[:haiku|sonnet|opus|id], gateway[:alias], gemini[:id], cohere[:id], groq|openrouter|mistral|cerebras|openai[:id], command:<shell command>. Keys come from <VENDOR>_API_KEY, or a ring NUM_<VENDOR> + <VENDOR>_API_KEY_1..n.

Errors

Each error says what the caller does next. Each check (isTransient, isCut...) also matches by name, so a host that holds two copies of this package still sorts them.

ErrorWhenNext
Transient408, 429, 5xx, a dropped connection, a timeoutwait retryAfter, try again
Refused401, 402, 403the key ring moves to the next key
InputRejected400, 413, 422change the request
Unsupporteda part kind the model does not takepick another model; parts are never dropped
Cutthe output cap stopped the answerpartial holds the text so far, usage its cost
Filteredthe vendor stopped the answer itself (Gemini's RECITATION, SAFETY, a failed run)stop says which; ask in other words
NoAnswerask found no object of the shape after its repairsissues says why

A key ring moves past a refused or busy key at once, and passes over a refused key on later calls. When every key is busy, it waits 2 s, then doubles up to 30 s, for passes passes (default 1). Retries inside the SDK are off. An error never carries the key: a vendor that echoes it (Gemini does, on a suspended key) has it put out as [key].

Prices

USD per million tokens, as of PRICES_AS_OF. A call on a free key ring (Cohere, Gemini, Groq, OpenRouter :free, the gateway's free, Claude Code on the subscription) records usd: 0, free: true, and listUsd (what it would have cost). Anthropic cache writes are 1.25x input (5 minutes) or 2x (1 hour); reads are 0.1x (0.05x on Opus 5.5). Haiku 5.5 has two tiers: a prompt over 100K tokens costs 5x. Jev costs input only: its answers are free. A model with no row keeps its tokens and has no usd.

Sources: Anthropic's pricing page; Cohere from activepieces.com and pricepertoken.com; Groq from groq.com/pricing; Gemini from anotherwrapper.com and benchlm.ai; Jev from typesafe.ai.

Why the AI SDK

The adapters sit on the AI SDK 7 (Apache 2.0): one call shape for Anthropic, Google, Cohere and every OpenAI-compatible server, with media parts and token details per vendor. The other choices were each vendor's own SDK (four call shapes and four usage formats to keep) and plain fetch (every wire format kept by hand). This package adds only what the SDK leaves out: errors sorted by what to do next, key rings, prices, the zod parse with repair, and speech.

Develop

pnpm install
pnpm lint && pnpm typecheck && pnpm test

A v* tag publishes to npm (.github/workflows/release.yml, trusted publishing: no stored token).

License: PolyForm Strict 1.0.0 (LICENSE.md).

版本与发布记录

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

版本发布日期距今
0.30.6latest2026/11/03今天
0.30.52026/11/03今天
0.30.42026/11/03今天
0.30.32026/11/03今天
0.30.22026/11/02今天
0.30.12026/11/02今天

关于 @wrenautomation/models 的常见问题

@wrenautomation/models 是什么?

每个模型都有一扇门:模型接受文本,图像,视频,音频和PDF部分,回答文本,令牌和美元,并将答案解析为zod形状。适配器Anthropic, Gemini, Cohere,任何openai兼容的服务器和shell命令(claude -p);e

如何安装 @wrenautomation/models?

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

@wrenautomation/models 支持 TypeScript 吗?

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

@wrenautomation/models 支持 ESM 还是 CommonJS?

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

@wrenautomation/models 需要什么版本的 Node.js?

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

@wrenautomation/models 可以商用吗?

@wrenautomation/models 使用 PolyForm-Strict-1.0.0 许可。声明的许可为 PolyForm-Strict-1.0.0,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@wrenautomation/models 还在维护吗?

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