Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [v0.3.0] - 2026-11-07

### Added

- **`invoice_extraction` returns the currency and the line items**: each `Invoice` gains `currency`, the ISO 4217 code its totals and lines are expressed in, read from the document, never taken from a converted amount or a card payment in another currency, and left empty when the document does not determine it, and `line_items`, a list of the new `InvoiceLineItem` concept (`label`, `quantity`, `unit_price`, `amount`) holding every priced line in the order printed, fees and discounts included and the subtotal, tax, total and payment lines never. `description` becomes a one-line summary of the purchase. Every existing field keeps its name and its type.

### Changed

- **`invoice_extraction` states `vat_rates` as percentages (Breaking)**: a rate of 20 % reads `20`, where the field's description previously left the model free to answer `0.2`. A caller that moves its address from `v0.2.0` and computes with a rate as a fraction, multiplying an amount by it, must divide the rate by 100 first, or its tax comes out a hundred times too large.

## [v0.2.0] - 2026-10-16

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This repository is the public library of packaged [MTHDS](https://mthds.ai) meth
| [`documents`](methods/documents/) | Document extraction toolbox: text pages, markdown pages, page contents with images and page views, single-text concatenation | `extract_document_markdown` |
| [`doc_summarizer`](methods/doc_summarizer/) | Deep document summarization: profile the document and extract importance-ranked key points in parallel, then synthesize a structured summary with themes and open questions | `summarize_document` |
| [`cv_analyzer`](methods/cv_analyzer/) | End-to-end candidate screening: match a CV against a job offer, then generate tailored interview questions or draft a courteous refusal email | `screen_candidate` |
| [`invoice_extraction`](methods/invoice_extraction/) | Classify each page as bill or receipt, then extract structured invoice data (amounts, VAT, vendor, buyer) from OCR text and page views | `process_invoice` |
| [`invoice_extraction`](methods/invoice_extraction/) | Classify each page as bill or receipt, then extract structured invoice data (amounts with their currency, VAT, vendor, buyer and the purchase line by line) from OCR text and page views | `process_invoice` |
| [`table_extraction`](methods/table_extraction/) | Turn a screenshot of a table into faithful HTML, with a vision review pass to correct text and formatting | `extract_html_table_and_review` |
| [`slide_designer`](methods/slide_designer/) | Turn a rough slide-deck brief into design proposals: multiple visual themes, a rendered mockup image for each, and an HTML report presenting them all | `generate_design_proposals_from_rough_brief` |
| [`image_generation`](methods/image_generation/) | Generate images from text: render a description directly, or refine it into an optimized image prompt first | `illustrate` |
Expand Down
2 changes: 1 addition & 1 deletion methods/cv_analyzer/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "cv_analyzer"
display_name = "CV Analyzer"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "End-to-end candidate screening: extract a CV and a job offer, analyze the match, then either generate tailored interview questions or draft a courteous refusal email."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/doc_summarizer/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "doc_summarizer"
display_name = "Document Summarizer"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Deep document summarization: profile the document and extract importance-ranked key points in parallel, then synthesize a structured summary with themes and open questions."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/documents/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "documents"
display_name = "Documents"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Document extraction methods for text, images, and page views."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/image_card/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "image_card"
display_name = "Image Card"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Generate an image from a text description with a small, fast image model, and present the image and its description side by side in an HTML page."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/image_generation/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "image_generation"
display_name = "Image Generation"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Image generation methods: render a description directly, or refine it into an optimized image prompt first."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/image_generation_fast/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "image_generation_fast"
display_name = "Image Generation Fast"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Fast image generation: render a text description directly with a small image generation model."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/invoice_extraction/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "invoice_extraction"
display_name = "Invoice Extraction"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Extract structured invoice data from a document: classify each page as bill or receipt, then extract amounts, VAT, vendor and buyer details using both the OCR text and the page view."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
78 changes: 78 additions & 0 deletions methods/invoice_extraction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Invoice Extraction

`invoice_extraction` reads a document holding invoices or receipts and returns what each page says: the vendor and the buyer, the dates, the totals and the tax, the currency the amounts are in, and the purchase line by line. It works from both the text extracted from each page and a picture of the page, so a scanned receipt reads as well as a generated PDF.

```
github.com/Pipelex/methods/invoice_extraction
```

## What it takes

`process_invoice`, the package's entry pipe, takes one input:

| Input | Concept | What to give |
|---|---|---|
| `document` | `Document` | A PDF or an image of one or more invoices or receipts. |

The sample [`inputs.json`](inputs.json) points at a scanned take-out receipt.

## What it returns

A list of `Invoice`, **one per page** of the document. Each page is first classified as a formal bill or a simple receipt, then read on its own, so an invoice whose lines run over two pages comes back as two results, each carrying that page's lines.

### `Invoice`

| Field | Type | Meaning |
|---|---|---|
| `invoice_id` | text | A unique identifier for the invoice. |
| `invoice_number` | text | The invoice number as shown on the document. |
| `issue_date` | date | The date the invoice was issued. |
| `time` | text | The time of the transaction, when the document prints one. |
| `amount_incl_tax` | number | The total including taxes, in `currency`. |
| `amount_excl_tax` | number | The net amount excluding taxes, in `currency`. |
| `vat_amount` | number | The total VAT or tax amount, in `currency`. |
| `currency` | text | The ISO 4217 code of the currency the invoice's totals and lines are expressed in, such as `EUR` or `USD`. Empty when the document does not determine it (see below). |
| `vat_rates` | list of numbers | The VAT rates applied, as percentages: `20` for 20 %. |
| `vendor` | text | The vendor's name. |
| `vendor_address` | text | The vendor's address. |
| `vendor_siret` | text | The vendor's SIRET number, for a French company. |
| `vendor_vat_number` | text | The vendor's VAT registration number. |
| `company_name` | text | The purchasing company's name. |
| `company_address` | text | The purchasing company's address. |
| `description` | text | A one-line summary of what was purchased. |
| `line_items` | list of `InvoiceLineItem` | The priced lines of the invoice, in the order printed (see below). |
| `category` | `InvoiceDetails` | Whether the page is a `bill` or a `receipt`, with the reason. |
| `text` | text | The raw text extracted from the page. |

Every field may be empty when the page does not show it.

### `InvoiceLineItem`

| Field | Type | Meaning |
|---|---|---|
| `label` | text | What the line is for, as printed: the product, service, fee or discount. Always present. |
| `quantity` | number | How many units, or how much, as printed. Fractional for a weighed or measured item. Empty when the line prints no quantity. |
| `unit_price` | number | The price of one unit, in `currency`, when the line prints it. |
| `amount` | number | The line's total, in `currency`, as printed. Negative for a discount or a refund. |

## How the currency is determined

The method reads the currency from the document and never assumes one. It is the currency of the invoice's own amounts, its totals and its lines, and only what qualifies those amounts counts: a converted amount, an exchange rate or a card-payment line printed in another currency, such as a card slip in the cardholder's currency, is ignored. It takes, in this order:

1. a currency code the document gives for those amounts, beside them or in a statement such as "all prices in EUR";
2. otherwise, a symbol printed with them that names one currency only, such as `€` or `₹`;
3. otherwise, for a symbol several currencies share, such as `$`, `£`, `¥` or `kr`, where the document was issued, as the document itself shows it: the vendor's address, a city or a landmark in its name or header, its phone number's format, its tax identifiers or the name its tax goes by.

When none of these settles it, or when the totals and lines are themselves in more than one currency, `currency` stays empty: a missing currency is safer to display than a wrong one. A second currency on the document, such as a converted total, is not represented.

## Which lines are line items

Every priced line of the document, in the order printed: each product or service, and each fee, deposit, surcharge, shipping charge or discount wherever it is printed, a surcharge below the subtotal included. The subtotal, tax, total, payment, tender and change lines are never line items, and neither is a line that only sums lines already listed, such as a subtotal per category: the totals and the tax are the invoice's own amounts, and the payment lines describe how it was paid rather than what was bought. A line's values are taken as printed and never recomputed, so a line that prints no unit price has none, and line amounts are before or after tax exactly as the document itemizes them.

## Running it

```bash
pipelex run bundle methods/invoice_extraction -i methods/invoice_extraction/inputs.json
```

Or by its address on the hosted API, pinned to a library tag, as the [library's README](../../README.md#running-a-method-by-address) describes.
29 changes: 23 additions & 6 deletions methods/invoice_extraction/invoice_extraction.mthds
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ category = { type = "text", description = "The category of the invoice", choices
] }
explanation = { type = "text", description = "Explanation of the classification" }

[concept.InvoiceLineItem]
description = "One priced line of an invoice or a receipt, as printed: a product, a service, a fee or a discount"

[concept.InvoiceLineItem.structure]
label = { type = "text", description = "What the line is for, as printed: the product, service, fee or discount", required = true }
quantity = { type = "number", description = "How many units, or how much, as printed; may be fractional for a weighed or measured item. Empty when the line prints no quantity" }
unit_price = { type = "number", description = "Price of one unit, in the invoice's currency, when the line prints it" }
amount = { type = "number", description = "Total of the line, in the invoice's currency, as printed; negative for a discount or a refund" }

[concept.Invoice]
description = "Invoice information extracted from text, supporting both formal bills and receipts"

Expand All @@ -20,17 +29,19 @@ invoice_id = { type = "text", description = "Unique identifier for the in
invoice_number = { type = "text", description = "Invoice number as shown on the document" }
issue_date = { type = "date", description = "Date when the invoice was issued" }
time = { type = "text", description = "Time of the transaction if available" }
amount_incl_tax = { type = "number", description = "Total amount including taxes" }
amount_excl_tax = { type = "number", description = "Net amount excluding taxes" }
vat_amount = { type = "number", description = "Total VAT/tax amount" }
vat_rates = { type = "list", item_type = "number", description = "List of VAT rates applied" }
amount_incl_tax = { type = "number", description = "Total amount including taxes, in the invoice's currency" }
amount_excl_tax = { type = "number", description = "Net amount excluding taxes, in the invoice's currency" }
vat_amount = { type = "number", description = "Total VAT or tax amount, in the invoice's currency" }
currency = { type = "text", description = "ISO 4217 code of the currency the invoice's totals and lines are expressed in, three uppercase letters such as EUR, USD or GBP; never the currency of a converted amount or of a card payment in another currency. Empty when the document does not determine it" }
vat_rates = { type = "list", item_type = "number", description = "VAT rates applied, as percentages: 20 for 20 %" }
vendor = { type = "text", description = "Name of the vendor/seller" }
vendor_address = { type = "text", description = "Complete address of the vendor" }
vendor_siret = { type = "text", description = "SIRET number of the vendor (French company registration)" }
vendor_vat_number = { type = "text", description = "VAT registration number of the vendor" }
company_name = { type = "text", description = "Name of the purchasing company" }
company_address = { type = "text", description = "Address of the purchasing company" }
description = { type = "text", description = "Description of goods or services purchased" }
description = { type = "text", description = "One-line summary of what was purchased; the purchase line by line is in line_items" }
line_items = { type = "list", item_type = "concept", item_concept_ref = "invoice_extraction.InvoiceLineItem", description = "The priced lines of the invoice, in the order printed, fees and discounts included; never the subtotal, tax, total or payment lines" }
category = { type = "concept", concept_ref = "invoice_extraction.InvoiceDetails", description = "Category or type of expense" }
text = { type = "text", description = "Raw text extracted from the invoice" }

Expand Down Expand Up @@ -80,7 +91,13 @@ inputs = { invoice_details = "InvoiceDetails", invoice_page = "Page" }
output = "Invoice"
model = "$vision"
prompt = """
Extract invoice information from this invoice:
Extract invoice information from this invoice.

Currency: give the ISO 4217 code of the currency the invoice's own amounts are expressed in, its totals and its lines. Read it only from what qualifies those amounts, never from a converted amount, an exchange rate or a card-payment line printed in another currency. Take it from a currency code the document gives for those amounts, beside them or in a statement such as "all prices in EUR"; otherwise from a symbol printed with them that names one currency only, such as € or ₹; otherwise, for a symbol several currencies share, such as the dollar sign, the pound sign, the yen sign or kr, from where the document was issued, as the document itself shows it: the vendor's address, a city or a landmark in its name or header (an airport, a station), its phone number's format, its tax identifiers or the name its tax goes by. When nothing on the document settles it, or when the totals and lines are themselves in more than one currency, leave the currency empty rather than guess.

Line items: list every priced line of the document, in the order printed: each product or service, and each fee, deposit, surcharge, shipping charge or discount wherever it is printed, below the subtotal included, a discount or a refund having a negative amount. Never list the subtotal, tax, total, payment, tender or change lines, nor a line that only sums lines already listed, such as a subtotal per category. Take each line's quantity, unit price and amount as printed, leave out what the line does not print, and never compute a value the document does not show.

Description: summarize what was purchased in one line, without listing the items.

The category of this invoice is: $invoice_details.category.

Expand Down
2 changes: 1 addition & 1 deletion methods/slide_designer/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "slide_designer"
display_name = "Slide Designer"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Turn a rough slide-deck brief into design proposals: polish the brief, generate multiple visual themes, render a mockup image for each, and compose an HTML report presenting them all."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/table_extraction/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "table_extraction"
display_name = "Table Extraction"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Extract a data table from a screenshot into faithful HTML, then review the result against the image to correct text and formatting."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/text_stats/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "text_stats"
display_name = "Text Stats"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Deterministic text statistics computed in pure Python: character, word, sentence and paragraph counts, vocabulary richness, most frequent words, and estimated reading and speaking times, reported as Markdown."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion methods/tweet_optimizer/METHODS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "tweet_optimizer"
display_name = "Tweet Optimizer"
address = "github.com/Pipelex/methods"
version = "0.2.0"
version = "0.3.0"
description = "Optimize a tech tweet: score the draft for fluffiness, cringiness, humblebragging and vagueness, then rewrite it in your own writing style following Twitter/X best practices."
authors = ["Evotis S.A.S"]
license = "MIT"
Expand Down
Loading