feat(delivery): add POST /delivery/parsePDF SSE route
- Accepts multipart/form-data PDF, streams LLM extraction via SSE - Uses libllmorbiter (OpenRouterProvider) with API key from companysetting keys.openrouter - Extracts manifest basedata, notes, and line items from invoices/delivery notes - Events: status, llm_call, tool_call, usage, progress, result, error - Extends generateApiDocs.js: multipart field table + x-sse-events table support - Docs: docs/api/routes/delivery/parsePDF.md (auto-generated)
This commit is contained in:
@@ -4040,6 +4040,110 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/delivery/parsePDF": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Delivery"
|
||||
],
|
||||
"summary": "Parse PDF (SSE)",
|
||||
"description": "Accepts a PDF invoice or delivery note (`multipart/form-data`, field `pdf`)\nand streams extraction results as Server-Sent Events until parsing is complete.\n\nThe PDF is read via `pdftotext -layout` and fed page-by-page to an LLM\n(OpenRouter) which fills a structured manifest of header data and line items.\nThe OpenRouter API key is read from the authenticated user's company setting\n`keys.openrouter`; the endpoint returns `400` if the key is not configured.\n\nThe stream ends after the final `result` event (or an `error` event on failure).\n",
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
}
|
||||
],
|
||||
"x-markdown": "delivery/parsePDF.md",
|
||||
"x-sse-events": [
|
||||
{
|
||||
"name": "status",
|
||||
"description": "PDF parse confirmation and LLM status messages",
|
||||
"payload": "{ \"message\": \"…\" }"
|
||||
},
|
||||
{
|
||||
"name": "llm_call",
|
||||
"description": "Fired before each LLM request",
|
||||
"payload": "{ \"model\": \"…\", \"promptSize\": 0, \"messages\": 0 }"
|
||||
},
|
||||
{
|
||||
"name": "tool_call",
|
||||
"description": "Fired when the output_manifest tool is invoked",
|
||||
"payload": "{ \"name\": \"output_manifest\" }"
|
||||
},
|
||||
{
|
||||
"name": "usage",
|
||||
"description": "Token usage after each LLM response",
|
||||
"payload": "{ \"input\": 0, \"cached\": 0, \"output\": 0, \"cost\": 0 }"
|
||||
},
|
||||
{
|
||||
"name": "progress",
|
||||
"description": "Intermediate accumulated extraction state (basedata / notes / items)",
|
||||
"payload": "ManifestResult"
|
||||
},
|
||||
{
|
||||
"name": "result",
|
||||
"description": "Final accumulated extraction — emitted just before the stream closes",
|
||||
"payload": "ManifestResult"
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"description": "Emitted on PDF parse or LLM failure; stream closes immediately after",
|
||||
"payload": "{ \"message\": \"…\" }"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"multipart/form-data": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"pdf"
|
||||
],
|
||||
"properties": {
|
||||
"pdf": {
|
||||
"type": "string",
|
||||
"format": "binary",
|
||||
"description": "PDF file to parse (max 50 MB, application/pdf)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "`text/event-stream` — emits `status`, `llm_call`, `tool_call`, `usage`,\n`progress`, `result` (and `error` on failure). See SSE Events table below.\n\n**ManifestResult shape** (used by `progress` and `result`):\n\n```json\n{\n \"basedata\": {\n \"date\": \"2026-01-15\",\n \"kLieferant\": 42,\n \"lieferantName\": \"Muster GmbH\",\n \"manifestNr\": \"LS-2026-001\",\n \"manifestType\": \"lieferschein\"\n },\n \"notes\": [\"Pos. 3: VPE-Angabe fehlte, Standardwert 1 angenommen.\"],\n \"items\": {\n \"pagesFrom\": 1,\n \"pagesTo\": 2,\n \"items\": [\n {\n \"position\": 1,\n \"count\": 10,\n \"vpe\": 1,\n \"unit\": \"Stk.\",\n \"articleName\": \"Einhängesieb Stahl\",\n \"liefArtNr\": \"ES-100\",\n \"mwst\": \"19\",\n \"eknetto\": 450,\n \"page\": 1\n }\n ]\n }\n}\n```\n",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "No PDF uploaded or OpenRouter key not configured",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/delivery": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
Reference in New Issue
Block a user