Pure syntagmatic rule-based parser for English RFQ documents (Vietnamese mode included) — LLM pre-processing layer for product name and spec extraction
Project description
rfq-parser
Pure syntagmatic rule-based parser for English RFQ documents and short product queries, with a Vietnamese mode.
No semantic model, no embeddings, no LLM — purely structural analysis of surface form. Built in Rust with Python bindings via PyO3, it runs in <1ms and is designed as a lightweight pre-processing layer for LLM-based product name and spec extraction: strip quantities, units, colours, sizes, and materials from the raw text so the model only sees what it actually needs to reason about.
Install
pip install rfq-parser
Quick start
import rfq_parser
# Short product query
result = rfq_parser.parse("500 polo shirts red size XL")
item = result.items[0]
print(item.chunks) # "polo shirts"
print(item.qty) # 500.0
print(item.uom) # None (implicit pieces)
print(item.colors) # ["red"]
print(item.sizes) # ["XL"]
# RFQ document
rfq = """
Please quote for:
1. Car Battery, 3000 Cartons, Black, 70Ah, Japan. Target: 12 USD/unit.
2. Silk Fabric, 500 Yards, Red, 150cm width, China.
"""
result = rfq_parser.parse(rfq)
for item in result.items:
print(f"{item.index}. {item.chunks} — qty={item.qty} {item.uom}")
print(f" colors={item.colors}, origin={item.origin}")
if result.is_rfq:
tt = result.trade_terms() # TradeTerms | None
Syntagmatic analysis in practice
Consider the ambiguous query:
"I want 500 shirts or polos in size XL"
A semantic model has to guess: does in size XL modify only polos, or the entire coordination shirts or polos? It will lean on world knowledge, training distribution, or hallucinate.
rfq-parser resolves this structurally. The surface parse is:
NP[qty=500]
├── NP "shirts"
│ └── COORD "or"
│ └── NP "polos"
└── PP[size] "in size XL" ← attaches to the root NP, shared across coordination
Rule: a post-head PP always scopes over the nearest NP node, which here is the coordinated root — not just polos. No semantic reasoning needed.
result = rfq_parser.parse("I want 500 shirts or polos in size XL")
item = result.items[0]
print(item.chunks) # "shirts or polos"
print(item.qty) # 500.0
print(item.sizes) # ["XL"]
The LLM downstream receives chunks = "shirts or polos" — a clean noun phrase — instead of the raw query. Quantity and size are already extracted; the model only needs to reason about the product names.
What it extracts
Each ParsedItem contains:
| Field | Description |
|---|---|
chunks |
Product noun phrase(s), connectors preserved ("polo shirt OR t-shirt") |
qty, qty_max, uom |
Quantity and unit of measure |
colors, sizes, materials |
Ontology-matched specs |
standards, specs |
Technical standards and extra specs |
origin, price, currency |
RFQ-specific fields |
dims |
Dimensions (e.g. ["150cm", "70Ah"]) |
ParseResult.trade_terms() returns a TradeTerms object with incoterm, currency, destination, lead_time, payment for RFQ documents.
Why syntagmatic / rule-based?
- No semantic bindings — no embeddings, no model weights, no ontology lookups at runtime
- Deterministic — same input always gives the same output
- Fast — <1ms, safe to call on every keystroke or in a streaming pipeline
- No dependencies — no model to download, no API key
- LLM pre-processing — strips structured fields (qty, uom, colours, sizes, materials) so downstream LLM calls receive a clean noun phrase, reducing token waste and hallucination surface
Supported
- English, Vietnamese, French product queries
- Informal RFQ emails and formal procurement documents
- Multi-item RFQs with trade terms (Incoterms, payment, lead time)
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rfq_parser-0.1.2.tar.gz.
File metadata
- Download URL: rfq_parser-0.1.2.tar.gz
- Upload date:
- Size: 54.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74295d88efa01784b7984d2a400fa40dbc299c7265d63525e38be57bed15b968
|
|
| MD5 |
261a665a2b434f0572bb68860bcea3fe
|
|
| BLAKE2b-256 |
b27e5dfc708cee59cbfe3f6e73830ab3ac251a40d0cfa0add961701b3500fcd6
|
Provenance
The following attestation bundles were made for rfq_parser-0.1.2.tar.gz:
Publisher:
publish.yml on progdinh/rfq-parser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rfq_parser-0.1.2.tar.gz -
Subject digest:
74295d88efa01784b7984d2a400fa40dbc299c7265d63525e38be57bed15b968 - Sigstore transparency entry: 1761575817
- Sigstore integration time:
-
Permalink:
progdinh/rfq-parser@9eba0ac8946353993717871c40b3619bc748bf14 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/progdinh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9eba0ac8946353993717871c40b3619bc748bf14 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rfq_parser-0.1.2-cp311-abi3-win_amd64.whl.
File metadata
- Download URL: rfq_parser-0.1.2-cp311-abi3-win_amd64.whl
- Upload date:
- Size: 786.2 kB
- Tags: CPython 3.11+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c9d19973ea339b24f62aabceb3e1346d82aaa2d5c022a4abc6fbd0d4139b93f
|
|
| MD5 |
d6e42fdec0cf387b1688c4a52cf69398
|
|
| BLAKE2b-256 |
d55ada70eac196e2d9b2fad250246f721f001594371a1fd37a4b8631413c4529
|
Provenance
The following attestation bundles were made for rfq_parser-0.1.2-cp311-abi3-win_amd64.whl:
Publisher:
publish.yml on progdinh/rfq-parser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rfq_parser-0.1.2-cp311-abi3-win_amd64.whl -
Subject digest:
5c9d19973ea339b24f62aabceb3e1346d82aaa2d5c022a4abc6fbd0d4139b93f - Sigstore transparency entry: 1761575984
- Sigstore integration time:
-
Permalink:
progdinh/rfq-parser@9eba0ac8946353993717871c40b3619bc748bf14 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/progdinh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9eba0ac8946353993717871c40b3619bc748bf14 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rfq_parser-0.1.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: rfq_parser-0.1.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 912.9 kB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c9ef6c4c8e83073935a486bd0953081ef7da394707480883d3c43fc11f64dc66
|
|
| MD5 |
71344807a3b98f2cfd4794b01d2051f3
|
|
| BLAKE2b-256 |
27c2336d48a19504e7b4f01061dfea3740ac04da759003f2ac02056f5e10ef68
|
Provenance
The following attestation bundles were made for rfq_parser-0.1.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on progdinh/rfq-parser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rfq_parser-0.1.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
c9ef6c4c8e83073935a486bd0953081ef7da394707480883d3c43fc11f64dc66 - Sigstore transparency entry: 1761576322
- Sigstore integration time:
-
Permalink:
progdinh/rfq-parser@9eba0ac8946353993717871c40b3619bc748bf14 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/progdinh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9eba0ac8946353993717871c40b3619bc748bf14 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rfq_parser-0.1.2-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: rfq_parser-0.1.2-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 820.4 kB
- Tags: CPython 3.11+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2aea0ae902636ca3bc943b46bb769351a689a8bc447943fbef5193f273917c9d
|
|
| MD5 |
893a0ae269102b0fae8b93c6a671cbb7
|
|
| BLAKE2b-256 |
781d640dc8f0248504fc55ddc0acec9c68f42ea8b2f49c40751469177e2a67ae
|
Provenance
The following attestation bundles were made for rfq_parser-0.1.2-cp311-abi3-macosx_11_0_arm64.whl:
Publisher:
publish.yml on progdinh/rfq-parser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rfq_parser-0.1.2-cp311-abi3-macosx_11_0_arm64.whl -
Subject digest:
2aea0ae902636ca3bc943b46bb769351a689a8bc447943fbef5193f273917c9d - Sigstore transparency entry: 1761576111
- Sigstore integration time:
-
Permalink:
progdinh/rfq-parser@9eba0ac8946353993717871c40b3619bc748bf14 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/progdinh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9eba0ac8946353993717871c40b3619bc748bf14 -
Trigger Event:
push
-
Statement type: