SDK Ruby per la Conversione dei File#

enconvert è la gem Ruby ufficiale per l'API EnConvert. Converte URL, immagini e documenti attraverso 43 endpoint di conversione implementati, ed espone un secondo namespace, client.v2, che legge le pagine web live in Markdown, JSON, screenshot e PDF pronti per gli agenti. Ogni lettura V2 porta con sé un punteggio render_quality, così una pagina bloccata o uno shell SPA vuoto non passano mai per contenuto reale. La gem richiede Ruby 3.0+ e non ha dipendenze a runtime, costruita su net/http, json e securerandom della libreria standard. Le risposte tornano come Struct con reader in snake_case.

RubyGems: enconvert · Sorgente: conversionapi/ruby-sdk · Ruby: 3.0.0 o versioni successive · Dipendenze a runtime: nessuna

Installazione#

gem install enconvert

Oppure aggiungila al tuo Gemfile:

gem "enconvert"
bundle install

Avvio rapido#

require "enconvert"

client = Enconvert::Client.new(api_key: ENV.fetch("ENCONVERT_API_KEY"))

# Converte una pagina live in PDF e la trasmette su disco.
result = client.convert_url_to_pdf("https://example.com", save_to: "page.pdf")
puts result.presigned_url

# Legge la stessa pagina come dovrebbe fare un agente, con un punteggio di qualità allegato.
op = client.v2.perceive("https://example.com", outputs: %w[markdown structured])
puts op.outputs["markdown"].url, op.render_quality  # ad es. 0.93

La gem è solo lato server. La tua chiave API privata deve restare sul server, quindi leggila da una variabile d'ambiente o da un secret manager e non spedirla mai a un browser o a un binario mobile.


Cosa espone il client#

Enconvert::Client porta direttamente la superficie di conversione dei file, e l'intera superficie di web intelligence pende da client.v2.

Gruppo Metodi Restituisce
URL singolo convert_url_to_pdf, convert_url_to_screenshot, convert_url_to_markdown ConversionResult
File caricato convert_image, convert_document, convert_to_markdown, convert_to_pdf ConversionResult
Batch di un intero sito convert_website_to_pdf, convert_website_to_screenshot BatchSubmission
Stato e polling get_job_status, get_batch_status, wait_for_batch JobStatus, BatchStatus
Web intelligence client.v2.*, 23 metodi su sei funzionalità Struct V2
Helper sui formati Enconvert.valid_outputs_for, Enconvert::IMPLEMENTED_CONVERSIONS Array, Set

Ogni tipo di risposta è una Struct creata con keyword_init: true, quindi leggi i campi come normali metodi: result.presigned_url, op.render_quality, job.total_chunks.


Conversione dei file#

convert_url_to_pdf#

Esegue il rendering di qualsiasi URL pubblico in PDF.

result = client.convert_url_to_pdf(
  "https://example.com",
  single_page: false,
  pdf_options: { page_size: "A4", orientation: "landscape" },
  viewport_width: 1440,
  save_to: "report.pdf"
)

puts result.filename, result.file_size
Opzione Tipo Predefinito Descrizione
save_to String nil Percorso locale su cui scaricare il PDF. Le directory padre vengono create automaticamente.
single_page Boolean true true produce una singola pagina continua. false pagina utilizzando pdf_options[:page_size].
pdf_options Hash nil Geometria di pagina. Vedi Opzioni PDF.
viewport_width Integer 1920 Larghezza del viewport del browser in pixel.
viewport_height Integer 1080 Altezza del viewport del browser in pixel.
load_media Boolean true Attende immagini e video prima della cattura.
enable_scroll Boolean true Scorre dall'alto verso il basso perché scattino i caricamenti lazy.
output_filename String nil Sovrascrive il nome file generato.
auth Hash nil Credenziali HTTP basic, per esempio { username: "user", password: "pass" }.
cookies, headers Array, Hash nil Cookie da impostare e header di richiesta aggiuntivi. Tutti e tre i campi di accesso al browser passano inalterati.
Non combinare auth con un header Authorization. L'API rifiuta il conflitto, quindi scegli l'uno o l'altro.

convert_url_to_screenshot#

Cattura un PNG a pagina intera di qualsiasi URL.

client.convert_url_to_screenshot("https://example.com", viewport_width: 1440, save_to: "shot.png")

Accetta le stesse opzioni di viewport, media, scroll, nome file e accesso al browser di convert_url_to_pdf. Non accetta single_pagepdf_options.

convert_url_to_markdown#

Estrae Markdown pulito in stile GitHub-Flavored da qualsiasi URL. Il convertitore rimuove navigazione, footer, pubblicità e script, mantiene il corpo principale dell'articolo e antepone un frontmatter YAML con titolo, descrizione, url, link e immagini.

client.convert_url_to_markdown("https://example.com/article", save_to: "article.md")

Utile per alimentare una pipeline RAG, importare contenuti di terze parti in un CMS o costruire un corpus di addestramento.

convert_image#

Converte tra jpeg, png, svg, heic e webp in qualsiasi direzione, oppure rasterizza un PDF in JPEG.

# Da un percorso su disco.
client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp")

# Da byte che hai già in mano.
bytes = File.binread("photo.heic")
client.convert_image({ data: bytes, filename: "photo.heic" }, output_format: "webp", save_to: "photo.webp")

# Rasterizza la prima pagina di un PDF.
client.convert_image("invoice.pdf", output_format: "jpeg", save_to: "invoice.jpg")

Il formato di input viene ricavato dall'estensione del nome file, quindi qui non si può usare un IO grezzo privo di nome file. Passa invece un percorso oppure un Hash { data:, filename: }.

Opzione Tipo Obbligatorio Descrizione
output_format String Formato di destinazione. Gli alias jpg, yml, htm e md vengono normalizzati automaticamente.
save_to String No Percorso locale su cui scaricare il risultato.
output_filename String No Sovrascrive il nome file generato.

convert_document#

Converte documenti e formati di dati. output_format vale "pdf" per impostazione predefinita.

# da docx a pdf
client.convert_document("report.docx", save_to: "report.pdf")

# da json a yaml
client.convert_document("data.json", output_format: "yaml", save_to: "data.yaml")

# da markdown a pdf con impostazioni di pagina personalizzate
client.convert_document(
  "README.md",
  output_format: "pdf",
  pdf_options: { page_size: "A4", margins: { top: 20, bottom: 20, left: 25, right: 25 } },
  save_to: "readme.pdf"
)

Estensioni di input riconosciute: .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml.

EPUB non ha una coppia documentale dedicata. Invia invece i file .epub a convert_to_pdf oppure a convert_to_markdown.

Opzione Tipo Predefinito Descrizione
output_format String "pdf" Formato di destinazione.
save_to String nil Percorso locale su cui scaricare il risultato.
output_filename String nil Sovrascrive il nome file generato.
pdf_options Hash nil Impostazioni di pagina, rispettate quando l'output è PDF.

convert_to_markdown#

Converte in Markdown pulito un file caricato di quasi qualsiasi formato documentale. Il formato viene rilevato sul server.

client.convert_to_markdown("handbook.docx", save_to: "handbook.md")

Input accettati: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, oltre ai formati office legacy e ODF. Le immagini non sono supportate su questo endpoint.

L'output è un unico file .md strutturato per intestazioni, il che lo rende un buon mattone per l'ingestion RAG: un chunker semantico può suddividere sulla gerarchia di intestazioni del documento invece che su conteggi arbitrari di caratteri. Le uniche opzioni sono save_to: e output_filename:.

convert_to_pdf#

Converte in PDF un file caricato di quasi qualsiasi formato.

# da pptx a pdf
client.convert_to_pdf("slides.pptx", save_to: "slides.pdf")

# pdf in passthrough, normalizzato in scala di grigi
client.convert_to_pdf("scan.pdf", pdf_options: { grayscale: true }, save_to: "gray.pdf")

Input accettati: office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, testo semplice, immagini raster, SVG, EPUB, oppure un PDF esistente che viene passato in passthrough e normalizzato.

Qui viene rispettato solo pdf_options[:grayscale]. Gli altri campi di geometria di pagina vengono ignorati su questo endpoint. Quando ti serve il controllo completo su dimensione pagina, orientamento e margini, usa convert_document oppure convert_url_to_pdf.

Le opzioni sono save_to:, output_filename: e pdf_options: (solo grayscale).

convert_website_to_pdf e convert_website_to_screenshot#

Individua ogni pagina di un sito web, converte ciascuna in background e raccoglie i risultati in un unico ZIP. Entrambi i metodi sono asincroni e restituiscono subito un BatchSubmission.

batch = client.convert_website_to_pdf(
  "https://example.com",
  crawl_mode: "sitemap",
  exclude_patterns: ["/tag/"],
  notification_email: "[email protected]"
)

status = client.wait_for_batch(batch.batch_id, save_to: "site.zip")
puts "#{status.completed} of #{status.total} pages converted"
status.items.each { |item| puts "#{item.status}\t#{item.source_url}" }

convert_website_to_screenshot funziona in modo identico e produce uno ZIP di PNG.

Opzione Tipo Descrizione
crawl_mode String Come vengono individuate le pagine, per esempio "sitemap".
include_patterns, exclude_patterns Array Sottoponi al crawl, oppure salta, solo gli URL che corrispondono a questi pattern.
notification_email String Indirizzo email da avvisare quando il batch termina.
callback_url String Webhook chiamato al completamento.
output_filename String Sovrascrive il nome file ZIP generato.
viewport_width, viewport_height, load_media, enable_scroll Integer, Boolean Comportamento di rendering per pagina. Inviati solo quando li imposti, altrimenti il gateway applica i propri valori predefiniti.
auth, cookies, headers Hash, Array, Hash Accesso del browser alle pagine protette.
single_page, pdf_options Boolean, Hash Solo per i batch PDF.

wait_for_batch(batch_id, interval: 5, timeout: 1800, save_to: nil) esegue il polling finché il batch non esce da "processing", poi restituisce il BatchStatus finale. Con save_to scarica anche lo ZIP. Genera Enconvert::APIError con stato 504 quando viene raggiunto il timeout, e con stato 500 quando un batch termina senza uno ZIP da salvare.

get_job_status#

Interroga un singolo job di conversione tramite id.

status = client.get_job_status("job_abc123")
case status.status
when "success" then puts status.presigned_url
when "failed"  then warn status.error
else                puts "still processing"
end

Restituisce un JobStatus con status, presigned_url, object_key ed error. Raramente devi chiamarlo tu stesso, perché l'SDK lo interroga già per tuo conto. Vedi Recupero dei timeout.

Conversioni supportate#

La gem porta con sé la tabella completa degli endpoint {input}-to-{output} implementati e valida ogni coppia localmente, quindi una coppia non supportata genera Enconvert::Error prima che una richiesta lasci il tuo processo.

Enconvert.valid_outputs_for("json")      # => ["csv", "toml", "xml", "yaml"]
Enconvert.valid_outputs_for("pdf")       # => ["jpeg"]
Enconvert::IMPLEMENTED_CONVERSIONS.size  # => 43
Input Output
json csv, toml, xml, yaml
xml csv, json
yaml json
csv json, xml
toml json
markdown html, pdf
html pdf
doc, excel, ppt, odt, ods, odp, ots, pages, numbers pdf
jpeg, png, svg, heic, webp tra loro, tutte le 20 coppie
pdf jpeg

Sono 43 endpoint: 13 di testo strutturato, 9 documentali e 21 di conversione immagini.


Web intelligence (V2)#

client.v2 trasforma le pagine web live in dati pronti per gli agenti: rendering, enumerazione, ricerca, estrazione, ingestion e monitoraggio. Ogni rendering porta con sé render_quality, un punteggio da 0.0 a 1.0 associato a ogni lettura. Un punteggio basso significa che la pagina non è stata renderizzata in modo pulito, tipicamente una pagina di challenge, un cookie wall, un blocco di login o uno shell JavaScript vuoto. Il contenuto viene comunque restituito, ma segnalato, insieme a un array warnings e a un Hash deductions che dice quali controlli sono scattati. Leggi il punteggio prima di mettere il contenuto da qualche parte, e una lettura difettosa non entrerà mai silenziosamente nel contesto del tuo agente.

Tutti gli endpoint V2 richiedono una chiave API privata. Vedi la panoramica V2 per il riferimento a livello di endpoint.

Perceive#

Esegue il rendering di un URL negli artefatti che richiedi. perceive è sincrono e restituisce l'operazione completata con gli URL degli artefatti appena firmati.

op = client.v2.perceive(
  "https://example.com",
  outputs: %w[markdown screenshot structured],
  extract: %w[tables metadata],
  only_main_content: true
)

puts op.operation_id, op.render_quality          # "per_...", poi da 0.0 a 1.0
puts op.outputs["markdown"].url                  # URL firmato dell'artefatto
puts op.outputs["markdown"].expires_in           # secondi prima che smetta di funzionare
puts op.structured, op.warnings.inspect          # Hash Ruby semplice, poi Array di String

Rifirma in qualsiasi momento gli URL degli artefatti di un'operazione precedente:

again = client.v2.get_perceive_operation(op.operation_id)

Puoi percepire fino a 1000 URL con un unico blocco di opzioni condiviso. I batch piccoli girano inline e tornano completati. Quelli più grandi tornano con stato "queued", quindi interrogali tramite job_id:

batch = client.v2.perceive_batch(
  ["https://a.example.com", "https://b.example.com"],
  outputs: %w[markdown],
  output_mode: "zip"
)

done = client.v2.get_perceive_batch(batch.job_id)
puts "#{done.completed}/#{done.total} complete, #{done.failed} failed"
puts done.zip&.url

Salta del tutto il round trip dell'URL firmato con perceive_direct, dove il corpo della risposta HTTP è l'artefatto stesso:

direct = client.v2.perceive_direct("https://example.com", outputs: %w[markdown])
puts direct.filename, direct.content_type, direct.content.bytesize
File.binwrite("page.md", direct.content)

# Riscarica un artefatto archiviato di un'operazione precedente.
raw = client.v2.download_perceive_artifact(op.operation_id, output: "markdown")

perceive_direct richiede esattamente un output che produca artefatti e altrimenti genera Enconvert::Error localmente. structured può viaggiare insieme, perché resta inline lato server e non viene mai trasmesso in streaming. download_perceive_artifact prende un output: opzionale, che puoi omettere quando l'operazione ha prodotto esattamente un artefatto e devi indicare quando ne ha prodotti più di uno, e restituisce 410 quando un artefatto ha superato la sua finestra di conservazione.

Opzione Tipo Descrizione
outputs Array Uno o più tra markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured. Lato server il valore predefinito è ["markdown", "structured"].
extract Array Uno o più tra tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
schema Hash Descrizioni dei campi per l'estrazione strutturata.
wait_for, wait_timeout_ms String, Integer Selettore CSS da attendere prima della cattura, e il limite su quell'attesa.
js_code String JavaScript da eseguire nella pagina prima della cattura.
viewport, mobile Hash, Boolean Dimensioni del viewport, e se renderizzare con un profilo mobile.
headers, cookies, auth Hash, Array, Hash Accesso del browser alle pagine protette.
proxy_url String Instrada il rendering attraverso il tuo proxy.
geolocation Hash Geolocalizzazione simulata per il contesto del browser.
action_chain Array Click, scroll e input programmati prima della cattura.
cache_mode String enabled, bypass oppure refresh.
pdf_options Hash Geometria di pagina quando pdf è tra gli output.
block_resources Array Tipi di risorsa da bloccare, per esempio image, font, script.
respect_robots Boolean Rispetta robots.txt.
only_main_content Boolean Rimuove navigazione ed elementi ripetuti dall'output Markdown.
direct_download Boolean Accettato solo da perceive. perceive_batch lo rifiuta.

La semantica completa dei parametri si trova in Perceive e in Parametri e opzioni.

Discover#

Enumera gli URL di un sito senza renderizzare nulla. Non è coinvolto alcun browser, quindi è veloce ed economico.

found = client.v2.discover(
  "https://example.com",
  mode: "hybrid",            # "sitemap", "crawl" oppure "hybrid"
  max_urls: 200,
  max_depth: 3,
  exclude_patterns: ["/tag/"],
  same_domain_only: true,
  respect_robots: true
)

puts found.total, found.urls.first(10)
puts found.truncated        # true quando max_urls ha limitato il risultato
puts found.sources.inspect  # da dove proviene ciascun URL

Vedi Discover per il comportamento modalità per modalità.

Lookup#

Esegue una ricerca web categorizzata e, facoltativamente, percepisce i risultati migliori nella stessa chiamata.

search = client.v2.lookup(
  "best static site generators",
  category: "web",           # web, news, images, scholar, patents, maps
  num_results: 10,
  time_filter: "month",      # hour, day, week, month, year
  perceive_top: 3
)

search.results.each do |hit|
  puts hit.position, hit.title, hit.url
  puts hit.perceive&.render_quality  # presente per i risultati percepiti automaticamente
end

puts search.answer_box.inspect, search.knowledge_graph.inspect

Con perceive_top: 3, i primi tre URL dei risultati vengono renderizzati e portano inline un PerceiveResult completo su hit.perceive. I loro id di operazione sono raccolti anche in search.perceive_operation_ids. Altro in Lookup.

Distill#

Estrazione strutturata guidata da schema. Fornisci esattamente uno tra urls: e discover_from:, e schema: è sempre obbligatorio. Se sbagli, l'SDK genera Enconvert::Error localmente.

extraction = client.v2.distill(
  urls: ["https://example.com/pricing"],
  schema: { plans: "list of plan names with monthly prices" },
  css_schema: {
    base_selector: ".plan-card",
    fields: [
      { name: "name",  type: "text", selector: "h3" },
      { name: "price", type: "text", selector: ".price" }
    ]
  }
)

item = extraction.results.first
puts item.data.inspect
puts item.extraction_tier   # "css", "llm", "mixed" oppure "none"
puts item.fields_from_css, item.fields_from_llm

Il css_schema opzionale viene eseguito per primo e risponde a tutto ciò che può con i soli selettori. Solo i campi che non riesce a coprire passano al livello del modello linguistico, ed è per questo che fields_from_css e fields_from_llm vengono riportati separatamente.

Individua ed estrai in un'unica chiamata:

client.v2.distill(
  discover_from: { url: "https://example.com", mode: "sitemap", max_pages: 10 },
  schema: { title: "page title", summary: "one-line summary" }
)

I tipi di campo CSS sono text, attribute, html, regex, nested, list e nested_list. Gli elenchi di campi annidati vengono serializzati in modo ricorsivo. Vedi Distill.

Ingest#

Trasforma un intero sito, o un insieme di documenti caricati, in JSONL suddiviso in chunk e pronto per il RAG attraverso un'unica pipeline. Ingest è sempre asincrono.

job = client.v2.ingest(
  mode: "sitemap",           # urls, sitemap, crawl
  url: "https://docs.example.com",
  max_pages: 100,
  chunk: { max_words: 512, sentence_overlap: 1 },
  webhook_url: "https://my.app/hooks/enconvert"
)

status = client.v2.get_ingest_job(job.job_id)
puts status.status                                   # queued, discovering, processing, completed, failed, canceled
puts status.pages_processed, status.total_chunks
puts status.output_url if status.status == "completed"  # JSONL

La modalità "urls" prende un elenco urls: e rifiuta url:. Ogni altra modalità prende un url: seed e rifiuta urls:. Entrambe le regole vengono applicate localmente prima che la richiesta venga inviata.

Carica file invece di fare il crawl:

file_job = client.v2.ingest_files(
  ["handbook.pdf", "notes.docx"],
  chunk: { max_words: 512, sentence_overlap: 1 }
)

ingest_files accetta PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT e MD, oltre ai formati office legacy e ODF. Ogni voce può essere un percorso, un oggetto simile a un IO oppure un Hash { data:, filename: }.

Gestisci i job e la consegna dei webhook:

list = client.v2.list_ingest_jobs(limit: 20)
list.jobs.each { |j| puts "#{j.job_id}\t#{j.status}\t#{j.total_chunks}" }
puts list.has_more

client.v2.cancel_ingest_job(job.job_id)   # idempotente

secret = client.v2.get_webhook_secret
puts secret.secret, secret.signature_header, secret.timestamp_header,
     secret.signature_scheme, secret.replay_tolerance_seconds

client.v2.rotate_webhook_secret            # le vecchie firme smettono subito di essere valide
client.v2.retry_ingest_webhook(job.job_id) # rispedisce il webhook di un job completato

retry_ingest_webhook restituisce un WebhookRetryResult con delivered, attempts, status_code e detail. Risponde 409 quando il job non è completato e 400 quando il job non ha alcun webhook configurato. Vedi Ingest.

Watch#

Rifà il rendering di un URL a cadenza fissa e ti avvisa quando cambia.

watcher = client.v2.create_watcher(
  "https://example.com/pricing",
  frequency_minutes: 60,     # minimo orario
  diff_mode: "auto",         # auto, text, structured, tables, metadata
  webhook_url: "https://my.app/hooks/changes",
  notify_email: true
)

puts watcher.watcher_id, watcher.next_check_at

history = client.v2.get_watcher_snapshots(watcher.watcher_id, limit: 10)
history.snapshots.each do |snap|
  puts snap.checked_at, snap.has_changes, snap.similarity, snap.change_count, snap.changes.inspect
end

client.v2.list_watchers(limit: 20)
client.v2.get_watcher(watcher.watcher_id)
client.v2.update_watcher(watcher.watcher_id, status: "paused")
client.v2.update_watcher(watcher.watcher_id, webhook_url: "")  # cancella il webhook
client.v2.delete_watcher(watcher.watcher_id)                   # soft delete, idempotente

update_watcher genera Enconvert::Error se lo chiami senza campi da aggiornare. delete_watcher restituisce il watcher marcato come eliminato con stato "deleted", e un watcher eliminato viene riletto come 404. Semantica completa in Watch.


Opzioni PDF#

pdf_options è un semplice Hash con chiavi simbolo, accettato da convert_url_to_pdf, convert_website_to_pdf, convert_document, convert_to_pdf (solo grayscale) e client.v2.perceive quando pdf è tra gli output. Sul filo vengono inviate solo le chiavi che imposti.

client.convert_url_to_pdf(
  "https://example.com",
  pdf_options: { page_size: "A4", orientation: "landscape", scale: 0.9,
                 margins: { top: 10, bottom: 10, left: 15, right: 15 } },
  save_to: "report.pdf"
)
Chiave Tipo Descrizione
page_size String "A4", "A3", "Letter", "Legal" e simili.
page_width Number Larghezza di pagina esplicita, in alternativa a page_size.
page_height Number Altezza di pagina esplicita.
orientation String "portrait" oppure "landscape".
margins Hash { top:, bottom:, left:, right: }, tutti opzionali.
scale Number Scala di rendering, per esempio 0.9 per il 90 percento.
grayscale Boolean Post-elabora il PDF in scala di grigi.
header Hash Testo dell'intestazione per regione di pagina.
footer Hash Testo del piè di pagina per regione di pagina.

Gestione degli errori#

Ogni errore è un Enconvert::Error oppure una delle sue sottoclassi, quindi le clausole rescue possono essere ampie o strette quanto vuoi. Gli errori di validazione lato client, come una coppia di conversione non supportata o uno schema mancante, generano la classe base Enconvert::Error prima che venga effettuata qualsiasi richiesta HTTP.

begin
  client.v2.perceive("https://example.com")
rescue Enconvert::AuthenticationError
  warn "Invalid or missing API key"
rescue Enconvert::QuotaError
  warn "Request was rejected with 402"
rescue Enconvert::RateLimitError
  warn "Too many requests, back off and retry"
rescue Enconvert::APIError => e
  warn "API error [#{e.status_code}]: #{e.message}"
rescue Enconvert::Error => e
  warn "Client-side validation failed: #{e.message}"
end
Classe Generata per Codice di stato
Enconvert::AuthenticationError Chiave API non valida, mancante o revocata 401, 403
Enconvert::QuotaError Generata su HTTP 402 402
Enconvert::RateLimitError Troppe richieste 429
Enconvert::APIError Qualsiasi altra risposta da 400 in su il codice effettivo
Enconvert::Error Classe base, più tutti gli errori di validazione locali nessuno

APIError#status_code ti dà lo stato HTTP, e il messaggio viene preso dal campo detail oppure error del corpo della risposta quando il corpo è JSON. La mappa completa dei messaggi si trova in Codici di errore.


Recupero dei timeout#

Un rendering lungo da URL a PDF o la conversione di un documento di grandi dimensioni possono superare il timeout del reverse proxy anche quando la conversione stessa riesce sul server. L'SDK recupera la situazione in modo trasparente, senza codice da parte tua.

  1. Prima di ogni richiesta di conversione il client genera un job id di 32 caratteri e lo invia come job_id nel corpo o nel form multipart.
  2. Se la richiesta torna con uno stato pari o superiore a 500, il client passa a interrogare GET /v1/convert/status/{job_id} ogni 3 secondi. Un 404 mentre la riga del job è ancora in scrittura viene ignorato e il polling continua.
  3. Non appena il job risulta success, il risultato viene restituito. Non appena risulta failed, viene generato Enconvert::APIError con stato 500 e il messaggio di errore del server.
  4. Il limite di tempo per il polling è di 300 secondi. Superato quello, il client genera Enconvert::APIError con stato 504 e il messaggio Conversion timed out.

Il job_id generato dal client viene anche unito a ogni risposta riuscita, quindi result.job_id è sempre popolato e più tardi puoi interrogare get_job_status tu stesso, se vuoi.

Gli invii batch dei siti web sono esclusi. convert_website_to_pdf e convert_website_to_screenshot non creano una riga per singolo job, quindi lì un 5xx significa che l'invio stesso è fallito e viene riportato subito invece di essere interrogato.

Configurazione#

client = Enconvert::Client.new(
  api_key: ENV.fetch("ENCONVERT_API_KEY"),
  timeout: 300,
  base_url: "https://api.enconvert.com"
)
Opzione Tipo Predefinito Descrizione
api_key String obbligatorio La tua chiave API privata. Un valore nil o vuoto genera Enconvert::Error alla costruzione.
timeout Integer 300 Timeout di apertura e lettura in secondi, applicato a ogni richiesta.
base_url String "https://api.enconvert.com" URL base dell'API. Gli slash finali vengono rimossi. Sovrascrivilo per un gateway self-hosted.

Le richieste si autenticano con l'header X-API-Key. Quando passi save_to, il download aggira deliberatamente quell'header, perché l'URL presigned dello storage non deve ricevere la tua chiave API.

Non inserire mai la chiave API direttamente nel codice. Leggila da una variabile d'ambiente, dalle credenziali di Rails o dal tuo secret manager. Chiunque abbia la tua chiave privata può eseguire conversioni a tuo nome. Ruotala dalla dashboard se trapela.

Struttura del risultato#

Ogni conversione di un singolo file e di una singola pagina restituisce un Enconvert::ConversionResult:

result = client.convert_document("report.docx", save_to: "report.pdf")

result.presigned_url            # URL firmato per l'output convertito
result.object_key               # chiave dell'oggetto nello storage
result.filename                 # nome file lato server
result.file_size                # byte, oppure nil
result.conversion_time_seconds  # secondi, oppure nil
result.job_id                   # sempre popolato dal client

Le altre Struct V1 sono JobStatus (status, presigned_url, object_key, error), BatchSubmission (batch_id, status, url_count, total_discovered, discovery_method, output_format), BatchStatus (batch_id, status, total, completed, failed, in_progress, output_mode, zip_download_url, items) e BatchItem (source_url, status, download_url, output_file_size, duration).

Sul lato V2, PerceiveResult è quella da conoscere:

op.operation_id                              # "per_..."
op.status                                    # queued, processing, completed, failed
op.url, op.url_final, op.content_hash
op.render_quality, op.cache_hit              # da 0.0 a 1.0 (oppure nil), e se proviene dalla cache
op.outputs                                   # Hash di nome => V2OutputArtifact
op.structured, op.extraction_tier            # Hash (oppure nil), e come è stato estratto
op.tokens.input, op.tokens.output, op.cost_cents, op.duration_ms
op.error, op.warnings                        # String oppure nil, e Array di String
op.status_code                               # stato HTTP a monte della pagina
op.deductions, op.options_echo               # perché render_quality è calato, e le opzioni usate

Un V2OutputArtifact porta url, object_key, size_bytes, content_type ed expires_in (900 secondi per impostazione predefinita), quindi gli URL firmati degli artefatti hanno vita breve. Rifirmali con get_perceive_operation, oppure scarica i byte con download_perceive_artifact. I payload forniti dall'utente, come schemi, dati estratti, campi monitorati e voci di diff, passano intatti come semplici Hash Ruby.

Anche gli URL presigned delle conversioni V1 sono temporanei. Passa save_to quando vuoi i byte su disco, oppure copiali nel tuo bucket per un'archiviazione permanente.


Sorgente e problemi#


Domande frequenti#

Come converto i file in Ruby con una gem?#

Esegui gem install enconvert, costruisci un client con Enconvert::Client.new(api_key: ENV.fetch("ENCONVERT_API_KEY")) e chiama un metodo come convert_document, convert_image oppure convert_url_to_pdf. Passa save_to: e l'SDK scarica per te il risultato su quel percorso, creando le directory padre se servono.

Come converto DOCX in PDF in Ruby?#

client.convert_document("report.docx", save_to: "report.pdf"). Il formato di output predefinito è "pdf", quindi ti serve output_format: solo quando vuoi qualcos'altro. La stessa chiamata funziona per .doc, .xls, .xlsx, .ppt, .pptx, .odt, .ods, .odp, .ots, .pages e .numbers.

Come converto un URL in PDF in Ruby?#

client.convert_url_to_pdf("https://example.com", save_to: "page.pdf"). Imposta single_page: false per paginare invece di produrre una singola pagina continua, e passa pdf_options: per dimensione pagina, orientamento, margini e scala. Per una pagina protetta da HTTP basic auth, aggiungi auth: { username: ..., password: ... }.

Come converto HEIC in WebP in Ruby?#

client.convert_image("photo.heic", output_format: "webp", save_to: "photo.webp"). Il formato di input viene ricavato dall'estensione del nome file, e la gem converte liberamente tra jpeg, png, svg, heic e webp. Rasterizza anche un PDF in JPEG con output_format: "jpeg".

Come estraggo una pagina web in Markdown da Ruby?#

Ci sono due opzioni. client.convert_url_to_markdown(url, save_to: "article.md") ti dà Markdown pulito in stile GitHub-Flavored con frontmatter YAML. client.v2.perceive(url, outputs: %w[markdown]) ti dà lo stesso contenuto più un punteggio render_quality, gli avvisi e l'estrazione strutturata opzionale, che è ciò che ti serve quando a leggere il risultato sarà un agente.

Che cosa significa render_quality e perché dovrei controllarlo?#

È un punteggio da 0.0 a 1.0 associato a ogni lettura V2, che riflette quanto pulitamente la pagina è stata effettivamente renderizzata. Una pagina di challenge, un cookie wall, un blocco di login, una pagina di errore HTTP o uno shell JavaScript vuoto ottengono tutti un punteggio basso. Il contenuto viene comunque restituito così puoi ispezionarlo, con warnings e deductions a spiegare il punteggio. Usalo come filtro prima di archiviare il testo o darlo in pasto a un modello.

L'SDK Ruby riprova quando una conversione lunga va in timeout?#

Sì. Ogni richiesta di conversione porta con sé un job_id generato dal client e, se la richiesta fallisce con uno stato pari o superiore a 500, l'SDK interroga GET /v1/convert/status/{job_id} ogni 3 secondi per un massimo di 300 secondi. Restituisce il risultato una volta che il job riesce e genera Enconvert::APIError con stato 504 se il limite scade. Gli invii batch di interi siti sono esclusi, perché non hanno una riga per singolo job.

Che cosa succede se chiedo una coppia di conversione che l'API non implementa?#

La gem genera Enconvert::Error localmente, prima di qualsiasi chiamata di rete, e il messaggio elenca gli output validi per quell'input. Puoi controllare tu stesso la stessa tabella con Enconvert.valid_outputs_for("json"), che restituisce ["csv", "toml", "xml", "yaml"].

Posso usare la gem enconvert dentro Rails o in un background job?#

Sì, e un background job è il posto giusto per usarla. La gem è puro net/http senza dipendenze a runtime e senza stato globale, quindi un'istanza di Enconvert::Client si può costruire per singolo job oppure memoizzare per processo. Le conversioni lunghe bloccano il thread chiamante fino al timeout configurato, che vale 300 secondi per impostazione predefinita, quindi tienile fuori dal ciclo di una richiesta web.

Quali versioni di Ruby supporta l'SDK?#

Ruby 3.0.0 e versioni successive, come dichiarato da required_ruby_version nella gemspec. Non ci sono dipendenze gem a runtime, quindi si installa in qualsiasi gruppo Bundler senza trascinarsi dietro un albero di dipendenze.

Come verifico la firma di un webhook di ingest?#

Chiama client.v2.get_webhook_secret, che crea il secret di firma del progetto al primo utilizzo e lo restituisce insieme a signature_header, timestamp_header, signature_scheme e replay_tolerance_seconds. Calcola l'HMAC sul corpo consegnato e confrontalo con l'header della firma. Usa rotate_webhook_secret per invalidare il vecchio secret, e retry_ingest_webhook(job_id) per rispedire la notifica di un job completato.