@particle-academy/prism

中文文档
0.7.0发布于 2026/11/02MITTypeScriptES Module零依赖构建来源已验证

TypeScript port of Prism — a unified API layer over LLM providers.

原始使用文档
文档目录7 节
  1. Scope
  2. Install
  3. Usage
  4. Parity
  5. Probes
  6. Development
  7. License

@particle-academy/prism

A TypeScript port of Prism, the PHP/Laravel unified API layer over LLM providers.

Real TypeScript: promises, discriminated unions, strict null checking. What it is not is a transliteration — the idioms are this language's. The corpus records where provider behavior agrees and where it differs.

Working on this package? Read AGENTS.md first — the boundary this package has to hold, the gates that must be green, and the traps that have already caught someone. @link AGENTS.md

Scope

Stored assistant messages retain provider replay state in additionalContent: OpenAI Responses uses output_items; Anthropic uses ordered content_blocks, with thinking_blocks as a fallback. Keep that metadata when sending history back. Signatures, redacted thinking and encrypted reasoning remain opaque and unchanged; avoid logging these values. The legacy thinking/signature pair still works when full replay metadata is absent.

In 0.5.0, Anthropic EOF before message_stop emits non-recoverable incomplete_stream and a terminal Error event. Provider errors close unfinished text and still end with Error; later stop/usage events cannot erase the error. Terminal usage stays null unless a final usage delta arrived. Consumers should handle these newly observable terminal events. Native event vocabulary and payload differences are tracked separately in the stream comparison contract.

Every capability the reference has, and three of its eighteen providers. That is the shape of the remaining drift: one axis, not two.

  • Capabilities — all of them. text, structured, stream, embeddings, images, moderation, audio (textToSpeech and speechToText), files, batch, fim. A user message carries images and documents, and each provider spells them its own way.
  • Providers — anthropic, mistral, openai. The OpenAI provider speaks the Responses API; Mistral brings the chat-completions shape, which is a different wire format rather than a variant of it.

Not included, and not stubbed: the tool-execution loop — a response that finishes on tool calls is refused with tool_loop_not_supported rather than half-executed — and the fifteen providers the reference has and this port does not. Asking for one fails with the unsupported_provider_action code rather than returning something plausible.

Not every capability reaches every provider, because not every provider has one. images, moderation, textToSpeech, files and batch are OpenAI-only here, and fim is Mistral-only because Mistral is the only provider that implements it at all — in the reference as well. See the envelope's port gaps register for which of those are work someone could do and which are facts about the provider.

Every value object serialises and rebuilds — toObject() and a static fromObject(). The PHP package has only the first half, which left each consumer to invent the second; one of those inventions shipped a defect that corrupted stored assistant messages. Both halves ship here, and the rebuild direction is pinned by fixtures.

Failures carry a stable code (PrismError.code). The prose is not part of the contract and will change; the code will not.

Structured responses retain structured, steps, raw, and the tool carrier arrays when serialized with toObject(). Each exported StructuredStep also retains its completed messages and per-step parsed value. Preserve that conversation state when continuing a conversation. Anthropic uses native schema output by default: text is empty while the original JSON remains in the assistant message. Explicit structured tool mode is currently refused with unsupported_provider_action before HTTP; it does not run tools.

Install

npm install @particle-academy/prism

Node 22 or newer.

Usage

import { Prism } from '@particle-academy/prism';

const response = await Prism.text()
  .using('openai', 'gpt-4o')
  .withSystemPrompt('You are terse.')
  .withPrompt('Who are you?')
  .asText();

console.log(response.text);
console.log(response.usage.promptTokens, response.usage.completionTokens);

Configuration is explicit first, environment second — OPENAI_API_KEY, OPENAI_URL, OPENAI_ORGANIZATION, OPENAI_PROJECT:

Prism.text().using('openai', 'gpt-4o', { apiKey: 'sk-…', transport });

transport is the seam between the provider and the network. Pass one and no test ever touches a socket:

import type { HttpTransport } from '@particle-academy/prism';

const transport: HttpTransport = async (request) => ({
  status: 200,
  headers: {},
  body: recordedPayload,
  rawBody: JSON.stringify(recordedPayload),
});

Build a request without sending it, or parse a stored payload without HTTP:

import { buildRequestBody, canonicalJson, parseTextResponse } from '@particle-academy/prism';

const request = Prism.text().using('openai', 'gpt-4o').withPrompt('hi').toRequest();

canonicalJson(buildRequestBody(request)); // exactly the bytes that would be POSTed
parseTextResponse(request, recordedPayload); // the same parse a live call performs

Parity

This port's behaviour is not defined by its own tests. It is pinned by prism-parity — a language-independent conformance corpus generated by executing the PHP reference, which states the rules once so three implementations cannot each drift into their own reading of them.

The corpus and its loader install as @particle-academy/prism-conformance. The runner lives in conformance/runner.mjs and implements prism-parity's subprocess contract, so scripts/cross-check.mjs can run this port and the others side by side and require identical verdicts — a stronger claim than three suites that each went green alone.

git clone https://github.com/Particle-Academy/prism-parity .parity
npm install ./.parity/loaders/ts
npm run conformance                       # every suite
node conformance/runner.mjs --suite openai-text-request
node conformance/runner.mjs --probe omit-null-keys

Two rows diverge from the reference on purpose, and the corpus says which and why: trq-0021 and trq-0022, where the reference drops a prompt of "" or "0" and this port sends it. Read the corpus for the rest — restating its rules here would only give them somewhere to drift.

Probes

--probe runs the port with one deliberate defect injected, from the corpus's probes/probes.json. Each probe declares the exact set of case ids it must fail, and tests/probes.test.ts asserts the observed set equals it — not that it is non-empty. A table every plausible implementation passes measures nothing.

The mutants live in conformance/mutations.mjs, outside src/: a defect you can switch on in production is a defect.

Development

npm run build       # tsc → dist/
npm run typecheck
npm run lint
npm test
npm run conformance

License

MIT. See LICENSE.

版本与发布记录

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

版本发布日期距今
0.7.0latest2026/11/021 天前
0.6.02026/11/021 天前
0.5.12026/10/304 天前
0.5.02026/10/259 天前
0.4.02026/10/249 天前
0.3.02026/10/0826 天前

关于 @particle-academy/prism 的常见问题

@particle-academy/prism 是什么?

Prism的TypeScript端口——LLM提供商之上的统一API层。

如何安装 @particle-academy/prism?

在项目目录运行 npm install @particle-academy/prism,或使用 pnpm add / yarn add / bun add。

@particle-academy/prism 支持 TypeScript 吗?

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

@particle-academy/prism 支持 ESM 还是 CommonJS?

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

@particle-academy/prism 需要什么版本的 Node.js?

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

@particle-academy/prism 可以商用吗?

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

@particle-academy/prism 还在维护吗?

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