Build an EU GPSR listing
The EU listing fields pulled from your product sheet and label, each Article 19 item marked found or missing, and warnings translated with every number checked.
Built on: Language pack, Signed record
Loading the tool…
Use it your way
Use it from your codeThe hosted API with your key, and prompts to paste into a coding agent
Get an API key
- Call the gpsr listing pack API from your own code in minutes.
- Every model answer carries a signed receipt.
- Nothing to install; we run the models.
Run it yourself, on request
- The same open models and app, on 1x RTX PRO 6000 (96 GB): Qwen3.8-27B NVFP4 plus Hy-MT2-7B (about 18 GB); the checks run on CPU.
- Data never leaves your machines, and there are no Decosa charges.
- One prompt for Claude Code or Codex assembles the whole stack.
- Early access: the container images are not public yet and the source needs access; the prompt says how to ask.
Build with it
Paste one of these into Claude Code, Codex or another coding agent. The first wires your project to the hosted API with your DECOSA_API_KEY. The second pulls our containers and runs the same stack on your own GPU, with no Decosa charges.
- Base URL
- https://api.decosa.ai
- Auth
Authorization: Bearer $DECOSA_API_KEY(or a demo session token)- Tool id
- gpsr-listing-pack
Use the hosted API
# Decosa GPSR listing pack: use the hosted API
You are wiring Decosa's GPSR listing pack into this project. Given a supplier's product sheet and the product label (text
or a photo), it extracts the listing information Article 19 of Regulation (EU) 2023/988 (GPSR) asks an online offer to
show, looks every value up in the documents, marks each Article 19 element found or missing, flags a label that disagrees
with the sheet, translates the warnings into up to five languages with every number, unit, date, code, negation and
locked term checked, and returns a signed record. Use only what is listed below. If you need something else, stop and ask.
- Base URL: `https://api.decosa.ai`
- Health check: `GET https://api.decosa.ai/healthz`.
- It never says an offer or a product complies with the Regulation, and Decosa is not a responsible person. Do not add
such a claim in your UI. A person checks the flags and the translations before listing.
- The hosted API takes synthetic or non-confidential documents (`"synthetic": true` is required for your own documents).
Confidential supplier sheets belong on a self-hosted box.
## Auth: API key (or a demo session)
1. Preferred: an API key (`dk_…`) from "Get an API key" on the tool page, kept in `DECOSA_API_KEY`, sent as
`Authorization: Bearer $DECOSA_API_KEY`.
2. Without a key: `POST https://api.decosa.ai/demo/session` with `{"vertical": "gpsr-listing-pack"}` returns `{"token", ...}`.
Sessions per IP are limited (HTTP 429 with `Retry-After`).
3. A pack needs about 1,600 generated tokens plus 400 per language (402 otherwise). One run at a time per demo token.
## Endpoints
- `GET /gpsr/info`, `GET /gpsr/samples` (no token): Article 19 verbatim with the date read and the EUR-Lex link, the
elements checked, languages, limits; four synthetic products.
- `POST /gpsr/pack` (token): `{"sheet": "...", "label": "..." | "label_image_b64": "...", "label_image_type": "image/png",
"picture": true, "languages": ["de", "fr", "pl"], "synthetic": true, "stream": true}` (or `{"sample_id": ...}`).
Languages: up to five of de, fr, es, it, nl, pl, pt, cs. Answer (JSON, or SSE: `ready`, `label`, `fields`, `receipt`,
`translation` per language, `completeness`, `result`, `budget`, `done`): `{status: ok | check | fail, fields,
elements: [{id, art, label, status: found | missing | check | not_required}], flags: [{kind: ungrounded |
label_sheet_mismatch, severity, why}], translations: {lang: {status, warnings, safety_information, segments: [{source,
text, status, flags}]}}, listing: {lang: {manufacturer, responsible_person, product, warnings, safety_information}},
record}`.
- `POST /lang/check` (no token, no model): `{"source", "target", "target_lang"}` checks a translation you already have.
- `POST /record/verify` (no token): the `record` → `{ok, checks, summary}`.
## Build it like this
Show the `missing` elements first with their article, then label-vs-sheet flags, then each language's warnings with any
flag under its line. Export `listing` to your marketplace fields; keep the `record` with the listing.
## Errors
400 bad input (the message names the field), 401/403 token, 402 budget, 409 a run already going on this demo token,
413 body over 10 MB, 429 busy (`Retry-After`), 503 the language model or the translation model is not available.
Run it yourself (containers)
On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.
# Decosa GPSR listing pack: run it yourself (containers)
You are setting up the Decosa GPSR listing pack on this machine, so confidential supplier sheets and labels never leave
it. It prepares the Article 19 listing information of Regulation (EU) 2023/988 from a product sheet and a label, checks
each element, compares the label with the sheet and translates the warnings with their numbers checked. It never says an
offer complies; a person checks before listing.
Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.
Ask me before any command that needs sudo, and show me the command first.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/gpsr-listing-pack.zip (2 KB, 6 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py gpsr-listing-pack` (the api image carries the same bundle under /app/rehearsal/gpsr-listing-pack/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py gpsr-listing-pack --bundle gpsr-listing-pack.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "only the elements this product has are found (manufacturer name and addresses, picture, type, identifier, warnings, language); the EU responsible person is not", "the three responsible-person elements are missing (Art. 19(b), 16(1))", "the overall status is fail"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## Steps
1. Docker: if `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
instructions for this distribution (docs.docker.com/engine/install). Install the NVIDIA container toolkit and check
`docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. Fetch the compose file: `mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml`. Read it.
Keep the `llm` service (Qwen3.8-27B on vLLM with image input) and the `api` service, and add an `mt` service: vLLM
serving `tencent/Hy-MT2-7B` (revision 9b0eb4e8f001def3e5ff6469a0ac96fdb39ec223, Apache-2.0) with `--enforce-eager
--gpu-memory-utilization 0.18 --max-model-len 8192`. On the `api` set `DECOSA_LLM_ROUTE=direct`,
`DECOSA_LLM_URL=http://llm:8000/v1`, `DECOSA_LLM_MODEL=qwen3.8-27b`, `DECOSA_LANG_MT_URL=http://mt:8000/v1`, and bind
every port to 127.0.0.1.
3. Pull and start: `docker compose pull && docker compose up -d`. Wait for the health checks (about 35 GB of weights).
4. Check: `curl -fsS http://127.0.0.1:<PORT>/gpsr/info` shows Article 19 read on 2026-09-27; `GET /lang/info` shows the
translation service reachable; `GET /attest/signing-key` shows this box's public key.
5. Smoke test: get a token (`POST /demo/session {"vertical":"gpsr-listing-pack"}`) and run `POST /gpsr/pack
{"sample_id": "charger-no-rp"}`: expect status `fail`, the three `rp_*` elements `missing` (a UK manufacturer with no
EU responsible person), and German, Spanish and Dutch warnings with status `ok`. Send the `record` to
`POST /record/verify`: `ok` must be true.
6. Report back: the public key, the smoke results, and how long the pack took.
Off by default. Joining as a provider serves other people's requests on this GPU; never do it on a box that holds
confidential supplier documents. If I ask for it later, follow the Provide page instead of improvising.
Run it on your own hardwareWhat it needs, and the prompt that sets it up
Run it on your own GPU
Same app, same pinned models, your hardware. Nothing goes to our servers and there are no Decosa charges.
Hardware check
Check your own hardware- CPU only, 64 GB RAMDoesn't fit
Qwen3.8-27B (NVFP4, vision tower on) needs a GPU.
- GeForce RTX 4090Doesn't fit
Needs about 38 GB of GPU memory at the smallest settings; 24 GB available.
- GeForce RTX 5090Doesn't fit
Needs about 46 GB of GPU memory at the smallest settings; 32 GB available.
- 2x GeForce RTX 5090standard tierRuns
The standard tier fits with changes: Qwen3.8-27B (NVFP4, vision tower on): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.
- L40SDoesn't fit
Needs about 51.6 GB of GPU memory at the smallest settings; 48 GB available.
- H100 80 GB (SXM)best tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVFP4, vision tower on) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU. The best tier fits too.
- RTX PRO 6000 Blackwell 96 GBbest tierRuns
The standard tier fits (75.6 of 96 GB). The best tier fits too.
- 2x RTX PRO 6000 Blackwell 96 GBbest tierRuns
The standard tier fits (75.6 of 192 GB). The best tier fits too.
- Apple M3 Ultra (Mac Studio), 96 GBCan't tell
Memory not known for Hy-MT2-7B has no mapped Apple Silicon build.
- Apple M5 Max, 64 GBCan't tell
Memory not known for Hy-MT2-7B has no mapped Apple Silicon build.
Memory per component comes from measured footprints, the tool's stack.json, or an estimate from its parameter count, and each is labelled that way below. Only an RTX PRO 6000 and an M3 Ultra Mac Studio have actually been run.
On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.
- 1
Check the GPU, Docker and the NVIDIA Container Toolkit
The driver must see the GPU, and Docker must be able to pass it into a container.
nvidia-smi docker compose version docker run --rm --gpus all ubuntu nvidia-smi
- 2
Fetch the compose file
One file describes the API and the language model as services.
mkdir -p ~/decosa && cd ~/decosa curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml - 3
Pull and start
The first start downloads pinned model weights, tens of gigabytes.
docker compose pull docker compose up -d
- 4
Check health
Wait until the API reports ok with the language model loaded. Then point your app at the local base URL.
curl -fsS http://localhost:<PORT>/healthz # {"ok": true, "llm": true, ...} curl -fsS -X POST http://localhost:<PORT>/demo/session \ -H 'Content-Type: application/json' -d '{"vertical":"gpsr-listing-pack"}'
Set up with a coding agent, rehearse on mock data, then go private
- Set up with a coding agent. Paste the self-host prompt into a coding agent on the machine that will run the service. We recommend Claude Code with Claude Opus 5.5; any capable coding agent works.
- Rehearse on mock data. The agent runs the tool on a bundle of synthetic inputs and checks each answer against the bundle's
expected.json. Every check must print PASS. - Go private. Only then do you run your own data against the local API, yourself, on that machine. Never give the agent real data during setup: a coding agent that runs in the cloud sees everything in its context, so keep real data out of the chat and out of the files it reads.
docker compose exec api python scripts/rehearse.py gpsr-listing-pack
Download the mock-data bundle (2 KB, 6 checks)expected.json
A synthetic product sheet and label for an e-bike charger made by a UK company, with no EU responsible person named. The pack must find the manufacturer's name, postal and electronic address, flag the three responsible-person elements of Article 19(b) as missing, translate the warnings into German and Polish with no high-severity number, unit or negation flag, attach a receipt to every model call and sign a record that verifies.
What the rehearsal checks
- only the elements this product has are found (manufacturer name and addresses, picture, type, identifier, warnings, language); the EU responsible person is not
- the three responsible-person elements are missing (Art. 19(b), 16(1))
- the overall status is fail
- German and Polish safety text come back with no high flag
- the Polish text has no high flag either
- the signed record verifies
Licence: Sheet and label written for this bundle (CC0); every company, address and number is invented. Part of decosa-api, AGPL-3.0-or-later.
Prompt for your coding agent
# Decosa GPSR listing pack: run it yourself (containers)
You are setting up the Decosa GPSR listing pack on this machine, so confidential supplier sheets and labels never leave
it. It prepares the Article 19 listing information of Regulation (EU) 2023/988 from a product sheet and a label, checks
each element, compares the label with the sheet and translates the warnings with their numbers checked. It never says an
offer complies; a person checks before listing.
Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.
Ask me before any command that needs sudo, and show me the command first.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/gpsr-listing-pack.zip (2 KB, 6 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py gpsr-listing-pack` (the api image carries the same bundle under /app/rehearsal/gpsr-listing-pack/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py gpsr-listing-pack --bundle gpsr-listing-pack.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "only the elements this product has are found (manufacturer name and addresses, picture, type, identifier, warnings, language); the EU responsible person is not", "the three responsible-person elements are missing (Art. 19(b), 16(1))", "the overall status is fail"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## Steps
1. Docker: if `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
instructions for this distribution (docs.docker.com/engine/install). Install the NVIDIA container toolkit and check
`docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. Fetch the compose file: `mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml`. Read it.
Keep the `llm` service (Qwen3.8-27B on vLLM with image input) and the `api` service, and add an `mt` service: vLLM
serving `tencent/Hy-MT2-7B` (revision 9b0eb4e8f001def3e5ff6469a0ac96fdb39ec223, Apache-2.0) with `--enforce-eager
--gpu-memory-utilization 0.18 --max-model-len 8192`. On the `api` set `DECOSA_LLM_ROUTE=direct`,
`DECOSA_LLM_URL=http://llm:8000/v1`, `DECOSA_LLM_MODEL=qwen3.8-27b`, `DECOSA_LANG_MT_URL=http://mt:8000/v1`, and bind
every port to 127.0.0.1.
3. Pull and start: `docker compose pull && docker compose up -d`. Wait for the health checks (about 35 GB of weights).
4. Check: `curl -fsS http://127.0.0.1:<PORT>/gpsr/info` shows Article 19 read on 2026-09-27; `GET /lang/info` shows the
translation service reachable; `GET /attest/signing-key` shows this box's public key.
5. Smoke test: get a token (`POST /demo/session {"vertical":"gpsr-listing-pack"}`) and run `POST /gpsr/pack
{"sample_id": "charger-no-rp"}`: expect status `fail`, the three `rp_*` elements `missing` (a UK manufacturer with no
EU responsible person), and German, Spanish and Dutch warnings with status `ok`. Send the `record` to
`POST /record/verify`: `ok` must be true.
6. Report back: the public key, the smoke results, and how long the pack took.
Off by default. Joining as a provider serves other people's requests on this GPU; never do it on a box that holds
confidential supplier documents. If I ask for it later, follow the Provide page instead of improvising.
Help me customise for my hardware
Pick your GPU or Mac, or enter its memory. You get the tier that fits, the model swaps it needs, measured speed where we have it, and a setup prompt with those choices written in.
GeForce RTX 5090: 32 GB GDDR7, 1,792 GB/s, FP8 and NVFP4. NVIDIA product page
Doesn't fitGPSR listing pack on GeForce RTX 5090
Needs about 46 GB of GPU memory at the smallest settings; 32 GB available.
Standard · Qwen3.8-27B plus Hy-MT2-7B (hosted demo): what changesuses estimates
- Needs about 46 GB of GPU memory at the smallest settings; 32 GB available.
Memory per component
- Article 19 checklist, value lookup against th...: decosa-api GPSR pack (decosa_api/verticals/gpsr), on the language-pack block (decosa_api/lang) and the signed record (07). CPU. Runs on CPU (vram_gb 0 in stack.json).
- Label reading: Qwen3.8-27B (NVFP4, vision tower on). ~57.6 GB (at least ~28 GB), weights 21.4 GB (from stack.json). Qwen3.8-27B NVFP4: Weights 19.9 GiB (21.4 GB), measured (field stack.json). The compose file gives the server 0.60 of a 96 GB card (57.6 GB) so the rest is FP8 KV cache for several sessions. The 28 GB minimum is an estimate: weights plus a short-context KV cache, which is why several stacks list a 32 GB RTX 5090 as 'estimate'. (stack.json lists 20 GB for this component.)
- Translation of warnings and safety information: Hy-MT2-7B. ~18 GB (from stack.json). vram_gb 18 in stack.json.
Expected speed
Not measured.
Not measured on this hardware. The only measured setups are an RTX PRO 6000 Blackwell and a Mac Studio M3 Ultra.
Setup prompt for this hardware
The self-host prompt for GPSR listing pack, with a hardware plan for GeForce RTX 5090 added after Step 0. Loading the full prompt; until then it points your agent at the prompt's URL.
# Set up GPSR listing pack on my hardware Fetch https://decosa.ai/prompts/gpsr-listing-pack-selfhost.md and follow it (including Step 0: rehearse on mock data first), with the hardware plan below applied. ## Hardware plan for this machine (from https://decosa.ai/self-host/hardware?use=gpsr-listing-pack) Target machine: GeForce RTX 5090 (32 GB of GPU memory; CUDA, FP8 and NVFP4). Quality tier: Standard · Qwen3.8-27B plus Hy-MT2-7B (hosted demo) (standard). Fit check: doesn't fit; some memory numbers are estimates, not measurements. First, check the machine: run `nvidia-smi` (or `rocm-smi`, or `sysctl hw.memsize` on a Mac) and confirm the GPUs and free memory match the line above. If they do not, stop and tell me before pulling anything. Use these components (the setup below describes the standard tier; change it to match): - Article 19 checklist, value lookup against th...: decosa-api GPSR pack (decosa_api/verticals/gpsr), on the language-pack block (decosa_api/lang) and the signed record (07), CPU - Label reading: Qwen3.8-27B (NVFP4, vision tower on) (nvidia/Qwen3.8-27B-NVFP4), 57.6 GB - Translation of warnings and safety information: Hy-MT2-7B (tencent/Hy-MT2-7B), 18 GB Warning: the fit check says this tier does not fit: Needs about 46 GB of GPU memory at the smallest settings; 32 GB available. Tell me before going further. During the rehearsal, watch GPU memory. If a model fails to load or runs out of memory, lower its --max-model-len and --max-num-seqs first, then its memory share, and tell me what you changed. The stack's own component list and compose layout: https://decosa.ai/prompts/gpsr-listing-pack-assemble.md
The proof
How we tested itEval results and end-to-end checks, hosted and self-hosted, with dates
Verified end to end
Hosted: verified 30 Sep 2026 · measured 30 Sep 2026: · p50 12 s · p95 18 s (8 runs) · ~$0.004 per run · 37 receipts
Loading the nightly status…
Self-host: not yet verified
Measured cost to run: about $0.53 per 100 listings (hosted, 30 Sep 2026). Self-hosting is free: the code is open and the models are open-weight. You pay only for your own hardware and power.
Known limits (4)
- Synthetic products only in the eval; no real supplier sheets or label photos were measured.
- Harmonisation legislation (toys, electrical, cosmetics) adds label rules this pack does not check.
- Translations are checked for numbers, units, dates, codes, negations and locked terms, not for wording; a native speaker should read them.
- Scanned PDF sheets need the document reader block's service, which is not in the hosted demo; label photos are read by Qwen3.8.
How it's builtThe steps, the models and what each one checks
Get an API key
- Call the gpsr listing pack API from your own code in minutes.
- Every model answer carries a signed receipt.
- Nothing to install; we run the models.
Run it yourself, on request
- The same open models and app, on 1x RTX PRO 6000 (96 GB): Qwen3.8-27B NVFP4 plus Hy-MT2-7B (about 18 GB); the checks run on CPU.
- Data never leaves your machines, and there are no Decosa charges.
- One prompt for Claude Code or Codex assembles the whole stack.
- Early access: the container images are not public yet and the source needs access; the prompt says how to ask.
A product sheet and its label in, the EU listing fields out: Article 19 elements found or missing, label checked against the sheet, warnings in up to five of the 23 EU languages with every number checked.
For marketplace sellers shipping to the EU and the responsible-person services that support them. It reads the supplier's product sheet and the label (text or a photo), extracts the manufacturer, the EU responsible person, the product identifiers and the warnings, looks every value up in the documents, marks each Article 19 element of Regulation (EU) 2023/988 found or missing, flags a label that disagrees with the sheet, and translates the warnings into up to five of the 23 other EU official languages with the language pack's number lock and its term bank (the GPSR's defined terms in each language, such as 'Bevollmächtigter' for authorised representative, the MDR or IVDR definitions on request when the product is a device, plus your own reviewed terms). It is a preparation aid with a signed record, never a statement that an offer meets the Regulation.
- Deployment
- Hosted or self-host
- Regulatory
- Checked 27 Sep 2026 against the text of Regulation (EU) 2023/988 (GPSR), OJ L 135, 23.5.2023, p. 1 (CELEX 32023R0988, read from the EU Publications Office). It applies from 13 December 2024 (Article 52). Article 19: an offer made online or at a distance must clearly and visibly indicate (a) the manufacturer's name, registered trade name or trade mark and postal and electronic address; (b) where the manufacturer is not established in the Union, the name, postal and electronic address of the responsible person under Article 16(1) or Article 4(1) of Regulation (EU) 2019/1020; (c) information allowing the product to be identified, including a picture, its type and any other product identifier; and (d) any warning or safety information to be affixed to the product or packaging or included in an accompanying document, in a language consumers can easily understand, as determined by the Member State where the product is made available. Article 9(5)-(7) sets the manufacturer's labelling (type, batch or serial number; name and addresses; instructions and safety information in the consumer's language), and Article 22(9) asks online marketplaces to let traders provide the Article 19 information. The pack checks for these elements and compares the label with the sheet; it does not judge whether a product is safe, it does not check harmonisation legislation (toys, electrical equipment, cosmetics and others add their own rules), which language a Member State requires is for that Member State, and Decosa is not a responsible person. Not legal advice. Model licences: Apache-2.0 (Qwen3.8-27B, Hy-MT2-7B).
Text description
A product sheet and a label (text or photo) go to decosa-api. Qwen3.8-27B reads the label image and extracts the fields as JSON, each call receipted by our gateway. Code looks every value up in the documents, marks each Article 19 element found or missing, and compares the label with the sheet. Hy-MT2-7B (eight languages) or Qwen3.8-27B (the other fifteen) translates the warnings into up to five languages; code checks every number, unit, date, code, negation and locked term, with a model-call attestation per call. Out come the listing fields per language, the checklist, the flags and a signed record.
At a glance
- Data retention
- Nothing stored: the sheet, the label and the result live in memory for the request, and logs carry counts only. The signed record holds hashes, statuses, flags, the extracted fields and the translations.
- What leaves the box
- Hosted: the Qwen3.8 calls go through our gateway to the GPU serving the model; translation runs on Decosa's hosted service, and the meaning check sends each English line with its translation and back-translation to Qwen3.8 through the gateway. Self-hosted on the direct route: nothing leaves the box.
- What it will not say
- That an offer or a product complies with Regulation (EU) 2023/988. It lists the Article 19 elements found or missing and what disagrees; Decosa is not a responsible person.
- Languages
- Any EU official language as a target (23 besides English, up to five per pack). Thirteen of the fifteen added on 27 Sep met the language pack's FLORES bar; Irish and Maltese are returned marked "draft, needs a reviewer".
- Typical run
- One Qwen3.8-27B call to read the sheet (two with a label photo), a few translation calls per language, and for every translated line a back-translation and one Qwen3.8 judgment (the meaning check, on by default). The meaning check is most of the time and model cost of a run; the measured figures are shown on this page. Send "meaning": false to skip it: numbers, units, negations and names are still checked in code.
Pick the tier for the quality you need
Same app at every tier. What changes is the models, the hardware they need, and whether receipts are signed. Scores are measured with the source named, or marked not measured.
- In the hosted demo
Standard
Qwen3.8-27B plus Hy-MT2-7B (hosted demo)
What the hosted demo runs: Qwen3.8-27B extracts, Hy-MT2-7B translates, code checks and signs.
- Models
- decosa-api GPSR pack (decosa_api/verticals/gpsr), on the language-pack block (decosa_api/lang) and the signed record (07)
- Qwen3.8-27B (NVFP4, vision tower on)
- Hy-MT2-7B
- Qwen3.8-27B (the language-pack block's route for these languages)
- Hardware
- 2x RTX PRO 6000 96 GB shared (measured); 1x 96 GB should fit (not measured)
- Quality evidence
- Missing Article 19 elements found (24 held-out synthetic products)15 of 19 found, 0 false (frozen); 18 of 18 after one fix (22 products)docs/evals/gpsr-listing-pack.md, test split, 27 Sep 2026
- Label-vs-sheet mismatches found5 / 5, 0 false flagsdocs/evals/gpsr-listing-pack.md, test split
- Extracted fields right (model, GTIN, manufacturer name and e-mail)87 / 88docs/evals/gpsr-listing-pack.md, test split
- Planted translation errors caught (numbers, units, negations, codes, dates; 6 languages)1,314 / 1,328 (98.9%) frozen; 1,320 / 1,328 after fixesdocs/evals/language-pack.md, drift test split
- Latency
- measured end to end with the meaning check on (the default), on the shared gateway; the figures are in the latency table and come from docs/evals/gpsr-listing-pack/measure-default.json
- Verification
- Proof: partialQwen3.8 calls: gateway receipts. Translation calls: model-call attestations signed by decosa-api, not countersigned by the gateway yet.
Best
the 30B translation model (not measured)
Hy-MT2-30B-A3B-FP8 for translation; not run by us.
- Models
- decosa-api GPSR pack (decosa_api/verticals/gpsr), on the language-pack block (decosa_api/lang) and the signed record (07)
- Qwen3.8-27B (NVFP4, vision tower on)
- Hy-MT2-30B-A3B-FP8
- Hardware
- 1x RTX PRO 6000 96 GB (estimate: 20 GB Qwen3.8 NVFP4 weights plus 31 GB MT)
- Quality evidence
- FLORES-200 XCOMET-XXL (vendor)not measured yetHy-MT2 report, arXiv 2605.22064 (vendor figure 89.83)
- Latency
- not measured
- Verification
- Proof: partialSelf-host only
Every model in the stack
| Model | Tiers | Params · VRAM | Verification | Details |
|---|---|---|---|---|
Article 19 checklist, value lookup against the documents, label-vs-sheet comparison, listing export and signed record (no model; CPU)decosa-api GPSR pack (decosa_api/verticals/gpsr), on the language-pack block (decosa_api/lang) and the signed record (07) 0 GBProof: partial | StandardBest | 0 GB | Proof: partial | |
| ||||
Label reading (image input) and field extraction as JSONQwen3.8-27B (NVFP4, vision tower on)nvidia/Qwen3.8-27B-NVFP4 on Hugging Face (opens in a new tab) 27B · 20 GBProof: strong | StandardBest | 27B · 20 GB | Proof: strong | |
| ||||
Translation of warnings and safety information (the language-pack block): German, French, Spanish, Italian, Dutch, Polish, Portuguese, CzechHy-MT2-7Btencent/Hy-MT2-7B on Hugging Face (opens in a new tab) 7.5B · 18 GBProof: partial | Standard | 7.5B · 18 GB | Proof: partial | |
| ||||
Translation of warnings and safety information: the fifteen other EU languages (Swedish, Danish, Finnish, Greek, Romanian, Hungarian, Bulgarian, Croatian, Slovak, Slovenian, Lithuanian, Latvian, Estonian; Irish and Maltese as drafts)Qwen3.8-27B (the language-pack block's route for these languages)Qwen/Qwen3.8-27B on Hugging Face (opens in a new tab) 27B · 0 GBProof: strong | Standard | 27B · 0 GB | Proof: strong | |
| ||||
Translation, larger modelHy-MT2-30B-A3B-FP8tencent/Hy-MT2-30B-A3B-FP8 on Hugging Face (opens in a new tab) 30B (3B active) · about 31 GB (estimate)No proof yet | Best | 30B (3B active) · about 31 GB (estimate) | No proof yet | |
| ||||
Tools, services and hardware
Tools
- Regulation (EU) 2023/988 (GPSR) (opens in a new tab)EU legislation (public)
Articles 9(5)-(7), 16(1), 19 and 22(9), read 27 Sep 2026; applies from 13 December 2024.
- Language pack (decosa_api/lang)AGPL-3.0-or-later
The number, unit, date, code, negation and glossary lock on every translated line.
Services
- decosa-api:8445
${DECOSA_REGISTRY}/decosa-api:0.1.0GET /gpsr/info, /gpsr/samples; POST /gpsr/pack (SSE or JSON). No GPU.
- decosa-llm:8000
${DECOSA_REGISTRY}/decosa-llm:0.1.0vLLM OpenAI endpoint for Qwen3.8-27B with image input.
- decosa-lang-mt:8491
vllm/vllm-openai@sha256:c2914767605584b6d8f45686b82de173ecc99e781897aa3d0a66dacd72c51ae1Hy-MT2-7B, the language pack's translation model (deploy/systemd/decosa-lang-mt.service).
Hardware
- 2x RTX PRO 6000 Blackwell 96 GB (shared) Fits
Measured: Qwen3.8-27B on one card; Hy-MT2-7B (18.3 GB) on the other beside other services.
- 1x RTX PRO 6000 96 GB
Not measured: Qwen3.8-27B NVFP4 (about 20 GB of weights) and Hy-MT2-7B (18 GB with its KV cache) should fit together with a reduced LLM KV cache.
- CPU only Does not fit
The checklist and the record are CPU; extraction and translation need the two models.
Latency per lane
- one pack, 5 languages, default run (meaning check on), shared gateway18.7 s
Measuredmeasured on our server 2026-09-30: median over 24 held-out test products through the HTTP API, every translated line back-translated and its meaning checked; production (api.decosa.ai) (docs/evals/gpsr-listing-pack/measure-default.json)
- one pack with a label photo, 5 languages, default run18.1 s
Measuredmeasured on our server 2026-09-30: the candle sample (label read from the photo), the slower middle of 2 runs; production (api.decosa.ai) (docs/evals/gpsr-listing-pack/measure-default.json)
Run this exact stack on your machine
Paste into Claude Code / Codex to assemble this stack locally. The prompt checks your GPU, pulls the pinned models, writes the compose file and runs a smoke test.
# Assemble the Decosa GPSR listing pack on this machine
You are setting up a preparation aid for sellers who list products in the EU and for responsible-person services. Given a
supplier's product sheet and the product label (text or a photo), it extracts the listing information Article 19 of
Regulation (EU) 2023/988 (GPSR) asks an online offer to show, looks every value up in the documents, marks each Article 19
element found or missing, flags a label that disagrees with the sheet, translates the warnings into up to five languages
with every number, unit, date, code, negation and name checked, and signs a record of what was checked. It never says an
offer meets the Regulation, and it is not a responsible person. Work step by step, show me each command before you run
anything with `sudo`, and stop to ask if a check fails.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/gpsr-listing-pack.zip (2 KB, 6 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py gpsr-listing-pack` (the api image carries the same bundle under /app/rehearsal/gpsr-listing-pack/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py gpsr-listing-pack --bundle gpsr-listing-pack.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "only the elements this product has are found (manufacturer name and addresses, picture, type, identifier, warnings, language); the EU responsible person is not", "the three responsible-person elements are missing (Art. 19(b), 16(1))", "the overall status is fail"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## 0. Ground rules and licences
- Models: Qwen3.8-27B (Apache-2.0) reads a label photo and extracts the fields as JSON; Hy-MT2-7B (Apache-2.0, no
territory clause) translates. The checklist, the lookups, the label-vs-sheet check, the translation checks and the record
are decosa-api (AGPL-3.0-or-later) on CPU.
- Supplier sheets and unreleased labels can be confidential: bind every port to 127.0.0.1 and use the direct LLM route.
Nothing is stored; logs carry counts only. Keep it that way.
- Do not add a "compliant" or "GPSR-ready" badge anywhere. The record says what was checked.
## 1. Check the machine
1. `nvidia-smi`: 96 GB of GPU in total for both models (Qwen3.8-27B NVFP4 has about 20 GB of weights, Hy-MT2-7B takes
about 18 GB with its KV cache at `--gpu-memory-utilization 0.18` of a 96 GB card). We measured the two on two cards;
one 96 GB card should fit with a smaller LLM KV cache (not measured). Driver 570 or newer; NVFP4 needs Blackwell,
otherwise use `Qwen/Qwen3.8-27B-FP8`.
2. `docker --version` and `docker compose version`. If Docker or the NVIDIA container toolkit is missing, install them
from the official Docker and NVIDIA repositories after asking me, then run
`docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
3. Disk: about 45 GB free (Qwen3.8 NVFP4 about 20 GB, Hy-MT2-7B 15 GB).
## 2. Images and weights
- `${DECOSA_REGISTRY}/decosa-api:<tag>` (**publishing soon**). If the pull fails, build from source:
`git clone <decosa-api source: on request at https://decosa.ai/contact?topic=self-host>` (access required), check out the newest release tag that
contains `decosa_api/verticals/gpsr/` and `decosa_api/lang/` (`main` until one does), and build `docker/api/Dockerfile`.
- `vllm/vllm-openai:v0.29.0` for both models.
- Weights: `nvidia/Qwen3.8-27B-NVFP4` (revision `482ca0f3832238542f8f5295dde86b5f22711d80`) and `tencent/Hy-MT2-7B`
(revision `9b0eb4e8f001def3e5ff6469a0ac96fdb39ec223`; its LICENSE.txt is Apache-2.0). Download with
`hf download tencent/Hy-MT2-7B --revision 9b0eb4e8f001def3e5ff6469a0ac96fdb39ec223 --local-dir ~/models/Hy-MT2-7B`.
## 3. docker-compose.yml
Write this in `~/decosa/gpsr/`:
```yaml
services:
llm:
image: vllm/vllm-openai:v0.29.0
command: ["--model", "nvidia/Qwen3.8-27B-NVFP4", "--served-model-name", "qwen3.8-27b", "--max-model-len", "32768",
"--limit-mm-per-prompt", "{\"image\":4}", "--gpu-memory-utilization", "0.70"]
ports: ["127.0.0.1:8114:8000"]
volumes: ["~/.cache/huggingface:/root/.cache/huggingface"]
deploy: { resources: { reservations: { devices: [{ driver: nvidia, count: 1, capabilities: [gpu] }] } } }
healthcheck: { test: ["CMD", "curl", "-fs", "http://localhost:8000/v1/models"], interval: 30s, retries: 20 }
mt:
image: vllm/vllm-openai:v0.29.0
# vLLM 0.29 serves Hy-MT2 through its Transformers backend; CUDA-graph capture fails there, hence --enforce-eager
command: ["/model", "--served-model-name", "hy-mt2-7b", "--max-model-len", "8192", "--gpu-memory-utilization", "0.18",
"--max-num-seqs", "16", "--enforce-eager", "--generation-config", "vllm"]
ports: ["127.0.0.1:8491:8000"]
volumes: ["~/models/Hy-MT2-7B:/model:ro"]
deploy: { resources: { reservations: { devices: [{ driver: nvidia, count: 1, capabilities: [gpu] }] } } }
healthcheck: { test: ["CMD", "curl", "-fs", "http://localhost:8000/v1/models"], interval: 30s, retries: 20 }
api:
image: ${DECOSA_REGISTRY}/decosa-api:<tag>
ports: ["127.0.0.1:8445:8445"]
environment:
DECOSA_HOST: 0.0.0.0
DECOSA_PORT: "8445"
DECOSA_DATA_DIR: /data
DECOSA_LLM_ROUTE: direct
DECOSA_LLM_URL: http://llm:8000/v1
DECOSA_LLM_MODEL: qwen3.8-27b
DECOSA_LANG_MT_URL: http://mt:8000/v1
DECOSA_LANG_MT_MODEL: hy-mt2-7b
DECOSA_GPSR_MAX_CONCURRENT: "3"
volumes: ["decosa-data:/data"]
depends_on: { llm: { condition: service_healthy }, mt: { condition: service_healthy } }
healthcheck: { test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8445/gpsr/info', timeout=4)"], interval: 30s, retries: 10 }
volumes:
decosa-data:
```
The api keeps its state (keys, receipts, this box's signing key) in the named volume `decosa-data`, not in a host folder:
the image runs as an unprivileged user (uid 10001), and a host folder Docker creates is owned by root. Start everything:
`docker compose up -d`. On a single card, lower the llm's `--gpu-memory-utilization` so both fit and tell me what you set.
On the first start the api creates this box's Ed25519 key in the volume (`/data/attest/`, mode 0600). Back it up with
`docker compose cp api:/data/attest ./attest-backup`, keep it private, never print it. Every model call on the direct
route, and every translation call, gets a receipt signed with that key (status `attested`): an operator attestation.
## 4. Smoke test
1. `curl -s localhost:8445/gpsr/info | jq '{applies: .source.applies_from, elements: (.elements | length)}'` shows
`2024-12-13` and 11 elements.
2. No model: check a bad German translation of a warning with the language pack's code check.
```
curl -s -XPOST localhost:8445/lang/check -H 'content-type: application/json' -d '{"source":"Do not charge above 40 °C.","target":"Bei über 45 °C laden.","target_lang":"de"}' | jq '[.segments[0].flags[].kind]'
```
Expect `number_changed` and `negation_missing`.
3. Token: `T=$(curl -s -XPOST localhost:8445/demo/session -H 'content-type: application/json' -d '{"vertical":"gpsr-listing-pack"}' | jq -r .token)`.
4. `curl -s -XPOST localhost:8445/gpsr/pack -H "authorization: Bearer $T" -H 'content-type: application/json' -d '{"sample_id":"charger-no-rp"}' > pack.json`,
then `jq '{status, missing: [.elements[] | select(.status=="missing") | .id], langs: (.translations | map_values(.status))}' pack.json`.
Expect `fail`, the three `rp_*` elements missing (a UK manufacturer with no EU responsible person), and `ok` for
German, Spanish and Dutch.
5. `jq '{record: .record}' pack.json | curl -s -XPOST localhost:8445/record/verify -H 'content-type: application/json' -d @- | jq .ok`
must print `true`.
6. If decosa-api's source is at hand, `python scripts/rehearse.py gpsr-listing-pack --base-url http://127.0.0.1:8445` and
`python scripts/rehearse.py language-pack --base-url http://127.0.0.1:8445` print PASS or FAIL per property.
7. Time it and tell me what you measure (about 6 s per pack with five languages on our shared GPUs).
## 5. Point the app at the local API
Set `NEXT_PUBLIC_DECOSA_API=http://127.0.0.1:8445` in the site's `.env.local`, or call `POST /gpsr/pack` from your
listing workflow and keep `record` with the listing. Contract: `API_CONTRACT.md`, section "Changes (language pack block,
gpsr-listing-pack, lay summary Member-State versions, 2026-09-27)".
Off by default. Joining serves other people's requests on this GPU; never do it on a box that holds confidential supplier
documents. If I ask for it, follow the provider guide at `/provide` on the site, and do not enable it without my explicit yes.Rules and regulations it checks againstDated, linked to the primary source; not legal advice
Regulation watch
Loading the watch status…
2 laws, rules and guidance pages cited; 2 watched nightly at the primary source. A change marks this page for a human re-check; nothing is edited automatically. What we cite and how it is watched
Technical detailsModels, where it runs, labels
In short
Last reviewed
- What it is
- The GPSR listing pack prepares the GPSR listing information Article 19 of Regulation (EU) 2023/988 asks an online offer to show, from a supplier's sheet and the label, and checks it: every value found in your documents, each element found or missing, the label compared with the sheet, the warnings translated with their numbers checked.
- Who it's for
- Marketplace sellers shipping to the EU and responsible-person services that check their clients' listings and labels.
- Where it runs
- Hosted or self-host
- Key numbers
On 24 held-out synthetic products it found 15 of 19 missing elements with no false alarm (18 of 18 after one fix) and all 5 label-vs-sheet mismatches; translations caught 98.9% of planted number errors.
- 15 / 19 Missing Article 19 elements found, frozen (test split, n = 19)
- 18 / 18 Missing elements found after that fix (test split, n = 18)
- 5 / 5 Label-vs-sheet mismatches found (test split, n = 5)
- 12.0 s Median end-to-end run, hosted (QA sweep 2026-09-30)
- Models
- Qwen3.8-27B (label reading, field extraction; translation into 15 EU languages) · Hy-MT2-7B (translation into 8, language-pack block)
- Where
- Hosted or self-host
- Checks
- Gateway receipts for Qwen3.8 calls; model-call attestation per translation call; signed record of the fields, elements, flags and translations
- Industry
- Compliance and trust · Sales and marketing
- Output
- Structured data · Signed record or verdict
- Data
- Confidential business data
- Hardware
- 1× 96 GB GPU
- Licence
- Permissive (Apache-2.0, MIT)
- Runs in
- Decosa hosted · Self-host
- Built from
- Language pack · Signed record
Questions people ask
What GPSR listing information does it prepare?
The Article 19 items: the manufacturer's name and postal and electronic address; the EU responsible person's name and addresses when the manufacturer is outside the Union; the product's type and other identifiers such as batch or GTIN; and the warnings and safety information, in the languages you pick. It also reminds you that the offer needs a picture.
Does it tell me my listing is compliant?
No. It says which Article 19 elements it found in your documents, which are missing and where the label disagrees with the sheet, and it signs a record of that. It does not judge whether the product is safe or check toy, electrical or cosmetics rules, and Decosa is not a responsible person.
How are the translations checked?
Hy-MT2-7B translates each warning line, and code checks that every number, unit, date, code, negation and locked term came through, in the target language's number format. On our held-out drift set it caught 1,314 of 1,328 planted errors; wording is not checked, so a native speaker should read the text.
Can it read a photo of the label?
Yes: Qwen3.8-27B transcribes the label from a PNG, JPEG or WebP, then the same checks run. We measured it on one rendered label, not on phone photos of real products.
Ask a question or leave feedbackWe read every message and publish useful answers
Ask about GPSR listing pack
We read every message. Questions, comments and our answers show here once we have reviewed and approved them.
Loading questions…