@ekwo-ai/document-reading

中文文档
0.25.0发布于 2026/11/07MITTypeScriptES Module构建来源已验证近 30 天下载 1990

One reader for received invoices: CII, Factur-X and UBL read exactly, and a photograph or a plain PDF read by a vision model through a published schema and prompt — both into the EN 16931 ReceivedInvoice.

原始使用文档
文档目录6 节
  1. How a product chains it
  2. The exact path: readStructured(bytes, options?)
  3. The model path: readingSchema() , readingPrompt() , reading…
  4. Extending the schema
  5. The purchase draft: purchaseDraftOf(invoice)
  6. Licence

@ekwo-ai/document-reading

One reader for received invoices. A structured e-invoice — CII, UBL, a PDF carrying its Factur-X or ZUGFeRD XML — is read exactly. A photograph or a plain PDF is read by a vision model you call, with the JSON schema and the prompt this package publishes, and its answer is checked here. Both arrive as the same EN 16931 ReceivedInvoice, and become the same purchase draft.

MIT. No database, no network, no model call: the package reads bytes and JSON, and returns objects.

npm install @ekwo-ai/document-reading

How a product chains it

import {
  READING_VERSION,
  purchaseDraftOf,
  readStructured,
  readingOf,
  readingPrompt,
  readingSchema,
} from '@ekwo-ai/document-reading';

// 1. A structured e-invoice is read exactly, and no model is called.
const structured = await readStructured(bytes);
let invoice = structured?.invoice;

// 2. Anything else is read by a vision model: one call, one schema.
if (invoice === undefined) {
  const answer = await yourModel({
    system: readingPrompt(),
    file: bytes, // the image or the PDF, as your provider takes it
    format: { type: 'json_schema', schema: readingSchema() },
  });
  const reading = readingOf(JSON.parse(answer.text));
  // reading is undefined when the page is not a purchase document.
  invoice = reading?.invoice;
  log({ reading: READING_VERSION, missing: reading?.missing, rejected: reading?.rejected });
}

// 3. The draft of a purchase, in the columns of Ekwo OS.
if (invoice !== undefined) {
  const draft = purchaseDraftOf(invoice);
  // Choose the supplier, the account and the tax in your books, then create_document.
}

The model call, its key, its timeout and its metering stay in the product. This package names no provider and holds no secret; the schema is a plain JSON schema with every object closed and every property required, as the structured outputs of the main providers take it.

The exact path: readStructured(bytes, options?)

Tells a PDF from XML by the first bytes and CII from UBL by the root, and reads with readFacturX() / readCii() of @ekwo-ai/factur-x or readUbl() of @ekwo-ai/peppol-ubl. Returns their { invoice, violations }, or undefined for a file that holds no e-invoice — a photograph, a PDF without XML, an XML that is not an invoice, a file over the limits — so the caller reads it the other way. The readers are imported when a file needs them.

The model path: readingSchema(), readingPrompt(), readingOf()

The model is told to copy, never compute, and to leave empty rather than guess. It returns, as printed:

  • what the page is: invoice, credit_note, receipt (already paid), or other (read as nothing);
  • the number, the issue, due and tax-point dates, and the currency as printed — empty where the page prints none, never the currency of a country;
  • the supplier and the customer: legal and trade name, VAT identifier, registration number, address;
  • the references (customer, order, contract, the invoice a credit note corrects), the invoicing period, the IBAN, BIC, payment reference and terms;
  • each line: name, description, quantity, the unit as printed and its UN/ECE Recommendation 20 code, net price, net amount, VAT category (UNTDID 5305) and rate, and its own period;
  • each VAT group of the summary: category, rate, base, tax, the amount including tax where printed;
  • the totals: lines, without VAT, VAT, with VAT, prepaid, payable;
  • a confidence (high, medium, low) for each field of CONFIDENCE_FIELDS, and for each line.

readingOf(answer) checks it into a ReceivedInvoice with syntax: 'model', and returns beside it:

FieldWhat it holds
missingThe fields EN 16931 requires that the page did not print, by path (number, totals.payable, lines[2].net_amount). Each is '' in the invoice.
rejectedThe fields the model filled with something not of their shape — a date that is no day, a rate in words. Each is emptied.
violationsThe arithmetic of EN 16931 the printed figures break, checked only when every figure it reads was read.
confidenceBy field and by line (lines[n]), null where the model said nothing usable.
taxInclusiveByGroupPer VAT group, the amount including tax the page prints, which EN 16931 does not carry.
extensionThe caller's own fields, untouched.

Nothing is computed, defaulted or corrected: a total that is not printed stays empty, a lone comma is read as a decimal separator and anything else that is not a decimal is rejected.

Extending the schema

A caller that needs more than an invoice adds its own properties, read in the same call:

const extension = {
  properties: {
    meter: { type: 'string', description: 'The meter number, as printed.' },
    contract_type: { type: 'string', description: 'The contract type, as printed.' },
  },
  instructions: 'The meter number is printed near the address of supply.',
};
const schema = readingSchema(extension); // adds a closed `extension` object
const prompt = readingPrompt(extension); // adds the instructions after the package's words
const reading = readingOf<{ meter: string; contract_type: string }>(answer);
reading?.extension; // as the model answered it: check your own fields

The invoice is read once; the extension's fields come back beside it, and the PDF is never read a second time.

The purchase draft: purchaseDraftOf(invoice)

The fields of documents and document_lines of Ekwo OS, from either path:

  • doc_type (purchase_invoice or purchase_credit_note), the dates, the supplier's number as supplier_reference, currency_code (null where it was not read), the references, payment_reference, payee_iban;
  • the supplier as the invoice names it, to be found among the contacts or added;
  • one line per line, then one per allowance (negative) or charge of the whole document, each with its unit_code, its VAT category and rate.

A line keeps its quantity, unit code and price where they make its amount at the decimals of its currency, as amount_untaxed is computed — 1234 kWh × 0.1834 = 226.3156, which rounds to the 226.32 printed. Otherwise it is one unit (C62) at its amount, and the invoice's own figures go in attributes (invoiced_quantity, invoiced_unit_code, invoiced_net_price). The period of a line goes in attributes as period_start and period_end; the period of the invoice goes on each line without its own, and is returned as invoicing_period.

The supplier, the account and the tax are chosen by the caller, in its own books and country pack: this package knows no chart and no rate.

Licence

MIT. See LICENSE.

版本与发布记录

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

版本发布日期距今
0.25.0latest2026/11/077 天前
0.24.12026/11/059 天前
0.24.02026/10/2817 天前
0.23.02026/10/2817 天前
0.22.02026/10/2817 天前
0.21.32026/10/2718 天前

关于 @ekwo-ai/document-reading 的常见问题

@ekwo-ai/document-reading 是什么?

One reader for received invoices: CII, Factur-X and UBL read exactly, and a photograph or a plain PDF read by a vision model through a published schema and prompt — both into the EN 16931 ReceivedInvoice.

如何安装 @ekwo-ai/document-reading?

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

@ekwo-ai/document-reading 支持 TypeScript 吗?

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

@ekwo-ai/document-reading 支持 ESM 还是 CommonJS?

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

@ekwo-ai/document-reading 需要什么版本的 Node.js?

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

@ekwo-ai/document-reading 可以商用吗?

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

@ekwo-ai/document-reading 的下载量有多少?

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

@ekwo-ai/document-reading 还在维护吗?

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