Class: Enconvert::V2
- Inherits:
-
Object
- Object
- Enconvert::V2
- Includes:
- Internal
- Defined in:
- lib/enconvert/v2.rb,
lib/enconvert/v2_results.rb
Defined Under Namespace
Modules: Enums Classes: DiscoverResult, DistillItem, DistillResult, IngestJob, IngestJobList, IngestJobSummary, LookupItem, LookupResult, PerceiveBatchResult, PerceiveDirectResult, PerceiveResult, V2OutputArtifact, V2Tokens, Watcher, WatcherList, WatcherSnapshot, WatcherSnapshotList, WatcherSummary, WebhookRetryResult, WebhookSecret
Instance Method Summary collapse
-
#cancel_ingest_job(job_id) ⇒ Object
Cancel a queued/processing ingest job.
-
#create_watcher(url, frequency_minutes: nil, diff_mode: nil, track_fields: nil, webhook_url: nil, notify_email: nil) ⇒ Object
Create a watcher that re-renders
urlon a fixed cadence (hourly floor) and notifies on changes via email and/or webhook. -
#delete_watcher(watcher_id) ⇒ Object
Soft-delete a watcher (idempotent).
-
#discover(url, **opts) ⇒ Object
List a site's URLs via sitemap, HTTP crawl, or both.
-
#distill(urls: nil, discover_from: nil, schema: nil, css_schema: nil, wait_for: nil, wait_timeout_ms: nil, headers: nil, cookies: nil, respect_robots: nil) ⇒ Object
Extract structured data matching
schemafrom explicit URLs or from a discovered site. -
#download_perceive_artifact(operation_id, output: nil) ⇒ Object
Download one stored artifact of an earlier perceive operation as raw bytes.
-
#get_ingest_job(job_id) ⇒ Object
Get one ingest job by id (
ing_...). -
#get_perceive_batch(job_id) ⇒ Object
Poll a perceive batch by job_id.
-
#get_perceive_operation(operation_id) ⇒ Object
Re-fetch a perceive operation by id (
per_...). -
#get_watcher(watcher_id) ⇒ Object
Get one watcher by id (
wat_...). -
#get_watcher_snapshots(watcher_id, limit: nil) ⇒ Object
Page through a watcher's check history, newest first.
-
#get_webhook_secret ⇒ Object
Get (creating on first call) the project's webhook signing secret and the header/scheme details needed to verify deliveries.
-
#ingest(mode: nil, url: nil, urls: nil, max_pages: nil, max_depth: nil, same_domain_only: nil, include_patterns: nil, exclude_patterns: nil, respect_robots: nil, wait_for: nil, wait_timeout_ms: nil, chunk: nil, webhook_url: nil) ⇒ Object
Start an ingest job: turn explicit URLs or a discovered site into chunked, RAG-ready JSONL.
-
#ingest_files(files, chunk: nil, webhook_url: nil) ⇒ Object
Ingest one or more uploaded FILES into RAG-ready JSONL chunks — the file counterpart of ingest(), sharing the same job lifecycle (mode "files").
-
#initialize(transport) ⇒ V2
constructor
A new instance of V2.
-
#list_ingest_jobs(skip: nil, limit: nil) ⇒ Object
List ingest jobs, newest first.
-
#list_watchers(skip: nil, limit: nil) ⇒ Object
List watchers, newest first.
-
#lookup(query, **opts) ⇒ Object
Run a categorized web search.
-
#perceive(url, **opts) ⇒ Object
Render one URL into the requested outputs (markdown, screenshots, PDF, links, structured data, ...).
-
#perceive_batch(urls, output_mode: nil, **render_opts) ⇒ Object
Perceive up to 1000 URLs with one shared options block.
-
#perceive_direct(url, **opts) ⇒ Object
Perceive with direct_download forced on: the response body IS the artifact bytes (
content), with metadata in headers — no signed URL round-trip. -
#retry_ingest_webhook(job_id) ⇒ Object
Re-deliver the completion webhook of a completed job (409 if the job is not completed, 400 if it has no webhook configured).
-
#rotate_webhook_secret ⇒ Object
Rotate the webhook signing secret.
-
#update_watcher(watcher_id, frequency_minutes: nil, diff_mode: nil, track_fields: nil, webhook_url: nil, notify_email: nil, status: nil) ⇒ Object
Update a watcher.
Methods included from Internal
build_multipart, extract_error_message, new_job_id, raise_for_status, serialize_pdf_options, to_file_part
Constructor Details
#initialize(transport) ⇒ V2
Returns a new instance of V2.
26 27 28 |
# File 'lib/enconvert/v2.rb', line 26 def initialize(transport) @transport = transport end |
Instance Method Details
#cancel_ingest_job(job_id) ⇒ Object
Cancel a queued/processing ingest job. Idempotent: canceling an already-terminal job returns it unchanged.
279 280 281 |
# File 'lib/enconvert/v2.rb', line 279 def cancel_ingest_job(job_id) to_ingest_job(delete_request("/v2/ingest/#{encode_path(job_id)}")) end |
#create_watcher(url, frequency_minutes: nil, diff_mode: nil, track_fields: nil, webhook_url: nil, notify_email: nil) ⇒ Object
Create a watcher that re-renders url on a fixed cadence (hourly
floor) and notifies on changes via email and/or webhook.
314 315 316 317 318 319 320 321 322 323 |
# File 'lib/enconvert/v2.rb', line 314 def create_watcher(url, frequency_minutes: nil, diff_mode: nil, track_fields: nil, webhook_url: nil, notify_email: nil) body = { url: url } body[:frequency_minutes] = frequency_minutes unless frequency_minutes.nil? body[:diff_mode] = diff_mode unless diff_mode.nil? body[:track_fields] = track_fields unless track_fields.nil? body[:webhook_url] = webhook_url unless webhook_url.nil? body[:notify_email] = notify_email unless notify_email.nil? to_watcher(post("/v2/watch", body)) end |
#delete_watcher(watcher_id) ⇒ Object
Soft-delete a watcher (idempotent). Returns the tombstoned watcher with status "deleted".
373 374 375 |
# File 'lib/enconvert/v2.rb', line 373 def delete_watcher(watcher_id) to_watcher(delete_request("/v2/watch/#{encode_path(watcher_id)}")) end |
#discover(url, **opts) ⇒ Object
List a site's URLs via sitemap, HTTP crawl, or both. No browser rendering — fast and does not consume perceive quota.
opts accepts: mode, max_urls, max_depth, include_patterns, exclude_patterns, same_domain_only, respect_robots.
112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/enconvert/v2.rb', line 112 def discover(url, **opts) body = { url: url } body[:mode] = opts[:mode] if opts.key?(:mode) body[:max_urls] = opts[:max_urls] if opts.key?(:max_urls) body[:max_depth] = opts[:max_depth] if opts.key?(:max_depth) body[:include_patterns] = opts[:include_patterns] if opts.key?(:include_patterns) body[:exclude_patterns] = opts[:exclude_patterns] if opts.key?(:exclude_patterns) body[:same_domain_only] = opts[:same_domain_only] if opts.key?(:same_domain_only) body[:respect_robots] = opts[:respect_robots] if opts.key?(:respect_robots) to_discover_result(post("/v2/discover", body)) end |
#distill(urls: nil, discover_from: nil, schema: nil, css_schema: nil, wait_for: nil, wait_timeout_ms: nil, headers: nil, cookies: nil, respect_robots: nil) ⇒ Object
Extract structured data matching schema from explicit URLs or from
a discovered site. An optional css_schema answers fields for free;
anything it misses escalates to the LLM tier (plan-gated).
Provide exactly one of urls (non-empty Array) or discover_from
(Hash with :url, :mode, :max_pages). schema is required.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/enconvert/v2.rb', line 158 def distill(urls: nil, discover_from: nil, schema: nil, css_schema: nil, wait_for: nil, wait_timeout_ms: nil, headers: nil, cookies: nil, respect_robots: nil) has_urls = urls.is_a?(Array) && !urls.empty? has_discover = !discover_from.nil? raise Enconvert::Error, "distill: provide exactly one of 'urls' or 'discover_from'" if has_urls == has_discover raise Enconvert::Error, "distill: 'schema' is required and must be a Hash" unless schema.is_a?(Hash) body = { schema: schema } body[:urls] = urls if has_urls if discover_from df = { url: discover_from[:url] } df[:mode] = discover_from[:mode] if discover_from.key?(:mode) df[:max_pages] = discover_from[:max_pages] if discover_from.key?(:max_pages) body[:discover_from] = df end body[:css_schema] = serialize_css_schema(css_schema) unless css_schema.nil? body[:wait_for] = wait_for unless wait_for.nil? body[:wait_timeout_ms] = wait_timeout_ms unless wait_timeout_ms.nil? body[:headers] = headers unless headers.nil? body[:cookies] = unless .nil? body[:respect_robots] = respect_robots unless respect_robots.nil? to_distill_result(post("/v2/distill", body)) end |
#download_perceive_artifact(operation_id, output: nil) ⇒ Object
Download one stored artifact of an earlier perceive operation as raw
bytes. output may be omitted when the operation produced exactly
one artifact (400 otherwise, listing the available outputs). 410 once
the artifact passed the plan's retention window.
81 82 83 84 85 86 87 |
# File 'lib/enconvert/v2.rb', line 81 def download_perceive_artifact(operation_id, output: nil) query = "?direct_download=true" query += "&output=#{encode_path(output)}" unless output.nil? resp = @transport.request(:get, "/v2/perceive/#{encode_path(operation_id)}#{query}") raise_for_status(resp) to_perceive_direct_result(resp) end |
#get_ingest_job(job_id) ⇒ Object
Get one ingest job by id (ing_...).
273 274 275 |
# File 'lib/enconvert/v2.rb', line 273 def get_ingest_job(job_id) to_ingest_job(get("/v2/ingest/#{encode_path(job_id)}")) end |
#get_perceive_batch(job_id) ⇒ Object
Poll a perceive batch by job_id. Items fill in as URLs complete.
99 100 101 |
# File 'lib/enconvert/v2.rb', line 99 def get_perceive_batch(job_id) to_perceive_batch_result(get("/v2/perceive/batch/#{encode_path(job_id)}")) end |
#get_perceive_operation(operation_id) ⇒ Object
Re-fetch a perceive operation by id (per_...). Artifact URLs are
freshly re-signed on every call.
73 74 75 |
# File 'lib/enconvert/v2.rb', line 73 def get_perceive_operation(operation_id) to_perceive_result(get("/v2/perceive/#{encode_path(operation_id)}")) end |
#get_watcher(watcher_id) ⇒ Object
Get one watcher by id (wat_...). Deleted watchers read as 404.
338 339 340 |
# File 'lib/enconvert/v2.rb', line 338 def get_watcher(watcher_id) to_watcher(get("/v2/watch/#{encode_path(watcher_id)}")) end |
#get_watcher_snapshots(watcher_id, limit: nil) ⇒ Object
Page through a watcher's check history, newest first.
343 344 345 346 347 348 349 350 351 352 |
# File 'lib/enconvert/v2.rb', line 343 def get_watcher_snapshots(watcher_id, limit: nil) query = limit.nil? ? "" : "?limit=#{limit}" d = get("/v2/watch/#{encode_path(watcher_id)}/snapshots#{query}") snapshots = d["snapshots"].is_a?(Array) ? d["snapshots"] : [] WatcherSnapshotList.new( watcher_id: str(d["watcher_id"]), snapshots: snapshots.map { |s| to_watcher_snapshot(s) }, limit: num(d["limit"], 20) ) end |
#get_webhook_secret ⇒ Object
Get (creating on first call) the project's webhook signing secret and the header/scheme details needed to verify deliveries.
298 299 300 |
# File 'lib/enconvert/v2.rb', line 298 def get_webhook_secret to_webhook_secret(get("/v2/ingest/webhook-secret")) end |
#ingest(mode: nil, url: nil, urls: nil, max_pages: nil, max_depth: nil, same_domain_only: nil, include_patterns: nil, exclude_patterns: nil, respect_robots: nil, wait_for: nil, wait_timeout_ms: nil, chunk: nil, webhook_url: nil) ⇒ Object
Start an ingest job: turn explicit URLs or a discovered site into chunked, RAG-ready JSONL. Always asynchronous — returns the queued job; poll get_ingest_job or configure webhook_url for completion.
mode defaults to "urls" for validation purposes but is only sent on
the wire when explicitly given (the API defaults it itself).
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/enconvert/v2.rb', line 192 def ingest(mode: nil, url: nil, urls: nil, max_pages: nil, max_depth: nil, same_domain_only: nil, include_patterns: nil, exclude_patterns: nil, respect_robots: nil, wait_for: nil, wait_timeout_ms: nil, chunk: nil, webhook_url: nil) effective_mode = mode || "urls" if effective_mode == "urls" raise Enconvert::Error, "ingest: mode 'urls' requires a non-empty 'urls' list" if urls.nil? || urls.empty? raise Enconvert::Error, "ingest: mode 'urls' does not accept 'url'" unless url.nil? else raise Enconvert::Error, "ingest: mode '#{effective_mode}' requires a seed 'url'" if url.nil? raise Enconvert::Error, "ingest: mode '#{effective_mode}' does not accept 'urls'" unless urls.nil? end body = {} body[:mode] = mode unless mode.nil? body[:url] = url unless url.nil? body[:urls] = urls unless urls.nil? body[:max_pages] = max_pages unless max_pages.nil? body[:max_depth] = max_depth unless max_depth.nil? body[:same_domain_only] = same_domain_only unless same_domain_only.nil? body[:include_patterns] = include_patterns unless include_patterns.nil? body[:exclude_patterns] = exclude_patterns unless exclude_patterns.nil? body[:respect_robots] = respect_robots unless respect_robots.nil? body[:wait_for] = wait_for unless wait_for.nil? body[:wait_timeout_ms] = wait_timeout_ms unless wait_timeout_ms.nil? if chunk c = {} c[:max_words] = chunk[:max_words] if chunk.key?(:max_words) c[:sentence_overlap] = chunk[:sentence_overlap] if chunk.key?(:sentence_overlap) body[:chunk] = c end body[:webhook_url] = webhook_url unless webhook_url.nil? to_ingest_job(post("/v2/ingest", body)) end |
#ingest_files(files, chunk: nil, webhook_url: nil) ⇒ Object
Ingest one or more uploaded FILES into RAG-ready JSONL chunks — the file counterpart of ingest(), sharing the same job lifecycle (mode "files"). PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD and legacy/ODF office are accepted. Always asynchronous; poll get_ingest_job or configure a webhook.
files accepts the same file-input union as convert_image (path
string, IO-like/readable object, or { data:, filename: }); at least
one file is required.
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/enconvert/v2.rb', line 235 def ingest_files(files, chunk: nil, webhook_url: nil) unless files.is_a?(Array) && !files.empty? raise Enconvert::Error, "ingest_files: provide at least one file" end fields = files.map do |file| part = to_file_part(file) { name: "files", value: part.bytes, filename: part.filename, content_type: part.content_type } end if chunk fields << { name: "max_words", value: chunk[:max_words] } if chunk.key?(:max_words) fields << { name: "sentence_overlap", value: chunk[:sentence_overlap] } if chunk.key?(:sentence_overlap) end fields << { name: "webhook_url", value: webhook_url } unless webhook_url.nil? body, boundary = build_multipart(fields) resp = @transport.request( :post, "/v2/ingest/files", headers: { "Content-Type" => "multipart/form-data; boundary=#{boundary}" }, body: body ) raise_for_status(resp) to_ingest_job(JSON.parse(resp.body)) end |
#list_ingest_jobs(skip: nil, limit: nil) ⇒ Object
List ingest jobs, newest first.
261 262 263 264 265 266 267 268 269 270 |
# File 'lib/enconvert/v2.rb', line 261 def list_ingest_jobs(skip: nil, limit: nil) d = get("/v2/ingest#{list_query(skip, limit)}") jobs = d["jobs"].is_a?(Array) ? d["jobs"] : [] IngestJobList.new( jobs: jobs.map { |j| to_ingest_job_summary(j) }, skip: num(d["skip"]), limit: num(d["limit"], 20), has_more: d["has_more"] == true ) end |
#list_watchers(skip: nil, limit: nil) ⇒ Object
List watchers, newest first.
326 327 328 329 330 331 332 333 334 335 |
# File 'lib/enconvert/v2.rb', line 326 def list_watchers(skip: nil, limit: nil) d = get("/v2/watch#{list_query(skip, limit)}") watchers = d["watchers"].is_a?(Array) ? d["watchers"] : [] WatcherList.new( watchers: watchers.map { |w| to_watcher_summary(w) }, skip: num(d["skip"]), limit: num(d["limit"], 20), has_more: d["has_more"] == true ) end |
#lookup(query, **opts) ⇒ Object
Run a categorized web search. With perceive_top > 0, the top-N result URLs are auto-perceived (each consumes one perceive-quota unit) and carry their full PerceiveResult inline.
opts accepts: category, country, locale, time_filter, num_results, page, location, autocorrect, perceive_top.
134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/enconvert/v2.rb', line 134 def lookup(query, **opts) body = { query: query } body[:category] = opts[:category] if opts.key?(:category) body[:country] = opts[:country] if opts.key?(:country) body[:locale] = opts[:locale] if opts.key?(:locale) body[:time_filter] = opts[:time_filter] if opts.key?(:time_filter) body[:num_results] = opts[:num_results] if opts.key?(:num_results) body[:page] = opts[:page] if opts.key?(:page) body[:location] = opts[:location] if opts.key?(:location) body[:autocorrect] = opts[:autocorrect] if opts.key?(:autocorrect) body[:perceive_top] = opts[:perceive_top] if opts.key?(:perceive_top) to_lookup_result(post("/v2/lookup", body)) end |
#perceive(url, **opts) ⇒ Object
Render one URL into the requested outputs (markdown, screenshots, PDF, links, structured data, ...). Synchronous: returns the completed operation with 15-minute signed artifact URLs.
opts accepts: outputs, extract, schema, wait_for, wait_timeout_ms, js_code, viewport, headers, cookies, auth, proxy_url, geolocation, action_chain, cache_mode, pdf_options, block_resources, respect_robots, mobile, only_main_content, direct_download.
42 43 44 45 46 47 48 49 50 |
# File 'lib/enconvert/v2.rb', line 42 def perceive(url, **opts) body = (opts) body[:url] = url # direct_download is accepted by POST /v2/perceive only (the batch # endpoint rejects it with 422), so it stays out of the shared # options serializer. body[:direct_download] = opts[:direct_download] if opts.key?(:direct_download) to_perceive_result(post("/v2/perceive", body)) end |
#perceive_batch(urls, output_mode: nil, **render_opts) ⇒ Object
Perceive up to 1000 URLs with one shared options block. Small batches run inline (completed result); larger ones return status "queued" — poll get_perceive_batch with the job_id.
92 93 94 95 96 |
# File 'lib/enconvert/v2.rb', line 92 def perceive_batch(urls, output_mode: nil, **render_opts) body = { urls: urls, options: (render_opts) } body[:output_mode] = output_mode unless output_mode.nil? to_perceive_batch_result(post("/v2/perceive/batch", body)) end |
#perceive_direct(url, **opts) ⇒ Object
Perceive with direct_download forced on: the response body IS the
artifact bytes (content), with metadata in headers — no signed URL
round-trip. Requires exactly one artifact-producing output
("structured" may ride along; it stays inline server-side and is not
returned here). Accepts the same opts as perceive.
57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/enconvert/v2.rb', line 57 def perceive_direct(url, **opts) require_one_artifact_output("perceive_direct", opts) body = (opts) body[:url] = url body[:direct_download] = true resp = @transport.request( :post, "/v2/perceive", headers: { "Content-Type" => "application/json" }, body: JSON.generate(body) ) raise_for_status(resp) to_perceive_direct_result(resp) end |
#retry_ingest_webhook(job_id) ⇒ Object
Re-deliver the completion webhook of a completed job (409 if the job is not completed, 400 if it has no webhook configured).
285 286 287 288 289 290 291 292 293 294 |
# File 'lib/enconvert/v2.rb', line 285 def retry_ingest_webhook(job_id) d = post("/v2/ingest/#{encode_path(job_id)}/retry-webhook", nil) WebhookRetryResult.new( job_id: str(d["job_id"]), delivered: d["delivered"] == true, attempts: num(d["attempts"]), status_code: opt_num(d["status_code"]), detail: str(d["detail"]) ) end |
#rotate_webhook_secret ⇒ Object
Rotate the webhook signing secret. Signatures made with the previous secret stop verifying immediately.
304 305 306 |
# File 'lib/enconvert/v2.rb', line 304 def rotate_webhook_secret to_webhook_secret(post("/v2/ingest/webhook-secret/rotate", nil)) end |
#update_watcher(watcher_id, frequency_minutes: nil, diff_mode: nil, track_fields: nil, webhook_url: nil, notify_email: nil, status: nil) ⇒ Object
Update a watcher. At least one field is required. Set webhook_url to "" to clear the webhook; resuming a paused watcher re-checks the plan's watcher cap.
357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/enconvert/v2.rb', line 357 def update_watcher(watcher_id, frequency_minutes: nil, diff_mode: nil, track_fields: nil, webhook_url: nil, notify_email: nil, status: nil) body = {} body[:frequency_minutes] = frequency_minutes unless frequency_minutes.nil? body[:diff_mode] = diff_mode unless diff_mode.nil? body[:track_fields] = track_fields unless track_fields.nil? body[:webhook_url] = webhook_url unless webhook_url.nil? body[:notify_email] = notify_email unless notify_email.nil? body[:status] = status unless status.nil? raise Enconvert::Error, "update_watcher: provide at least one field to update" if body.empty? to_watcher(patch("/v2/watch/#{encode_path(watcher_id)}", body)) end |