Class: DoclingClient
- Inherits:
-
Object
- Object
- DoclingClient
- Defined in:
- app/services/docling_client.rb
Overview
Thin HTTP client for the docling-serve document-extraction service
(https://github.com/docling-project/docling-serve): file in → Markdown out,
layout-aware (real tables) with OCR for scanned pages built in.
Deployment mirrors PlaywrightRuntime: the app image ships no Python —
docling runs as the docling Kamal accessory in production/staging
(config/deploy*.yml) and as part of the default docker-compose stack in
development (docker compose up -d). The app reaches it over
HTTP via the DOCLING_SERVER_URL env var (e.g. http://heatwave-docling:5001);
when unset, DoclingClient.convert raises NotConfigured.
Spike comparison vs markitdown and integration notes:
doc/integrations/DOCLING_DOCUMENT_EXTRACTION.md.
Defined Under Namespace
Classes: Error, NotConfigured, Timeout
Constant Summary collapse
- SUPPORTED_EXTENSIONS =
File extensions docling-serve can convert (PDF/Office/HTML/CSV/images).
Callers use this to pre-validate before shipping bytes over the network. %w[.pdf .docx .xlsx .pptx .html .htm .csv .md .adoc .png .jpg .jpeg .tif .tiff .bmp .webp].freeze
- CONVERT_TIMEOUT =
Whole-request timeout in seconds. CPU OCR on a large scanned PDF is the
slow path (~2-30s/page observed in the spike); generous but bounded. 300
Class Method Summary collapse
-
.configured? ⇒ Boolean
Whether a docling-serve endpoint is configured.
-
.convert(path, filename: File.basename(path.to_s)) ⇒ String
Convert a local file to Markdown via docling-serve's synchronous
/v1/convert/fileendpoint. -
.server_url ⇒ String?
Base URL of the docling-serve instance.
-
.supported_file?(filename) ⇒ Boolean
Whether docling-serve accepts this file type.
Class Method Details
.configured? ⇒ Boolean
Returns whether a docling-serve endpoint is configured.
54 55 56 |
# File 'app/services/docling_client.rb', line 54 def configured? server_url.present? end |
.convert(path, filename: File.basename(path.to_s)) ⇒ String
Convert a local file to Markdown via docling-serve's synchronous
/v1/convert/file endpoint. OCR runs automatically on scanned/bitmap
content (server default do_ocr=true); tables use the accurate
TableFormer mode (server default). Images become <!-- image -->
placeholders — the output is meant for LLM/text consumption.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'app/services/docling_client.rb', line 76 def convert(path, filename: File.basename(path.to_s)) raise NotConfigured, 'DOCLING_SERVER_URL is not set — boot the docling accessory (prod/staging) or `docker compose up -d` (dev default stack)' unless configured? response = HTTP.timeout(CONVERT_TIMEOUT).post( "#{server_url}/v1/convert/file", form: { files: HTTP::FormData::File.new(path, filename: filename), to_formats: 'md', image_export_mode: 'placeholder' } ) raise Error, "docling-serve returned HTTP #{response.status}: #{response.body.to_s.truncate(300)}" unless response.status.success? payload = JSON.parse(response.body.to_s) status = payload['status'].to_s raise Error, "docling conversion #{status.presence || 'failed'}: #{Array(payload['errors']).join('; ').truncate(300)}" unless status.in?(%w[success partial_success]) payload.dig('document', 'md_content').to_s rescue HTTP::ConnectTimeoutError => e # A connection that never established is transient (accessory down/rebooting, # network blip) — a plain Error so the worker's retry kicks in. Must precede # HTTP::TimeoutError (ConnectTimeoutError < TimeoutError). raise Error, "docling-serve connection failed: #{e.}" rescue HTTP::TimeoutError => e # The conversion blew the 300s sync deadline — permanent (see {Timeout}), not # worth a retry. Must precede HTTP::Error (TimeoutError < HTTP::Error). raise Timeout, "docling-serve timed out (>#{CONVERT_TIMEOUT}s): #{e.}" rescue HTTP::Error => e raise Error, "docling-serve request failed: #{e.}" rescue JSON::ParserError raise Error, 'docling-serve returned invalid JSON' end |
.server_url ⇒ String?
Returns base URL of the docling-serve instance.
49 50 51 |
# File 'app/services/docling_client.rb', line 49 def server_url ENV['DOCLING_SERVER_URL'].presence end |
.supported_file?(filename) ⇒ Boolean
Returns whether docling-serve accepts this file type.
60 61 62 |
# File 'app/services/docling_client.rb', line 60 def supported_file?(filename) File.extname(filename.to_s).downcase.in?(SUPPORTED_EXTENSIONS) end |