What are Hooks?
The Werk24 API is asynchronous: lightweight results (like Page Thumbnails) are ready very quickly (≈ 300–500 ms), while heavy analyses (e.g., AskInsights) may take longer (up to \~50 s for complex drawings).
To keep your integration simple, Werk24 provides Hooks. A Hook ties a specific Ask (the what you want) to a function (the what to do when it’s ready). When the requested data becomes available, your function is called with the result—no polling loops needed on your side.
Key Concepts
-
Ask A declarative request for a specific artifact (e.g.,
W24AskPageThumbnail,W24AskVariantMeasures). You may register many Asks per file. -
Hook A (ask, function) pair. The function runs once per matching result. Hooks are evaluated independently—fast results arrive first.
-
Asynchrony Results are produced and delivered as they’re ready. There’s no guaranteed order between different Asks.
-
Idempotency Your functions should tolerate duplicates (e.g., on client restarts). Write hooks so re-execution doesn’t break anything.
Typical Latencies (Guidance)
- Page Thumbnail: 0.3–0.5 s
- Metadata: \~4–10 s
- Features: \~6–15 s (complexity-dependent)
- Insights: \~6–25 s (complexity-dependent)
These are not SLAs, just planning numbers to design UX and timeouts.
Delivery & Ordering Guarantees
- Each Hook fires as soon as its Ask is ready.
- Hooks for different Asks are independent and may fire in any order.
- If a failure occurs for a given Ask, you’ll receive an error (see below).
- Avoid shared mutable state between hook functions unless you protect it.
Example: Using Hooks in Python
Suppose you want to (i) save a page thumbnail and (ii) print extracted measures.
from werk24 import Hook, W24AskPageThumbnail, W24AskVariantMeasures
def save_file(result):
# result may contain bytes and metadata; adapt to your SDK object shape
content = getattr(result, "content", None) or getattr(result, "bytes", None)
name = getattr(result, "filename", "page_thumbnail.png")
if content:
with open(name, "wb") as f:
f.write(content)
print(f"Saved: {name}")
def print_measures(result):
# Prefer robust printing; large payloads -> summarize
measures = getattr(result, "measures", None) or result
print(f"Received {len(measures) if hasattr(measures, '__len__') else 'measures'}")
hooks = [
Hook(ask=W24AskPageThumbnail(), function=save_file),
Hook(ask=W24AskVariantMeasures(), function=print_measures),
]
This setup triggers save_file as soon as the thumbnail is available, and print_measures when measures arrive.
End-to-End Sketch (Upload + Hooks + Run)
from Werk24 import W24TechreadClient, W24Ask, Hook, W24AskPageThumbnail, W24AskVariantMeasures
client = W24TechreadClient(api_key="YOUR_KEY")
def save_thumbnail(res):
# idempotent write
path = "page-0-thumb.png"
data = getattr(res, "content", None)
if data and not (os.path.exists(path) and len(open(path, "rb").read()) == len(data)):
with open(path, "wb") as f:
f.write(data)
print(f"Thumbnail saved to {path}")
def log_measures(res):
# Avoid dumping huge JSON to stdout in production
count = getattr(res, "count", None) or len(getattr(res, "measures", []) or [])
print(f"Measures ready (count ~ {count})")
hooks = [
Hook(ask=W24AskPageThumbnail(), function=save_thumbnail),
Hook(ask=W24AskVariantMeasures(), function=log_measures),
]
with open("drawing.pdf", "rb") as f:
# The client typically accepts file-like objects and a list of asks/hooks
client.process(f, hooks=hooks)
Tip: Keep hook functions fast. If you do I/O or heavy post-processing, hand off to a worker queue so you don’t block other callbacks.
Designing Good Hooks
-
Idempotent Safe to call more than once (e.g., check if file exists before writing; use upserts).
-
Stateless or isolated state Keep per-file state keyed by a stable ID to avoid cross-talk.
-
Non-blocking Offload long work. Hooks should act like interrupt handlers: quick, reliable, minimal.
-
Defensive Validate payload shape; tolerate missing optional fields.
Backpressure & Concurrency
- If you register many Hooks across many files, your own callback work can become the bottleneck.
-
Use a thread pool or async queue to keep callbacks snappy:
-
CPU-bound →
concurrent.futures.ThreadPoolExecutororProcessPoolExecutor - I/O-bound →
asyncio.create_task+ boundedasyncio.Semaphore
FAQs
Do Hooks guarantee order? No. Each Hook fires when its Ask is ready. Don’t rely on ordering.
Can I run multiple Hooks for the same Ask? Yes—register multiple Hooks targeting the same Ask if you need to fan out actions (e.g., save + notify).
How do I limit callback fan-out? Wrap heavy logic in a queue/worker with bounded concurrency; keep the Hook handler tiny.
What if I only want the first page thumbnail? Scope your Ask accordingly (e.g., page index parameters if available). Otherwise, discard in the callback.
Can I correlate results back to my upload? Yes. Use stable identifiers (upload ID, filename, or a client-side UUID) passed along in your closure or captured in your hook function.
Best-Practice Checklist
- [ ] Separate Asks (what you need) from Hooks (what to do).
- [ ] Keep Hook functions idempotent and fast.
- [ ] Expect out-of-order arrivals; don’t share mutable state.
- [ ] Handle errors explicitly; log and retry where it’s safe.
- [ ] Use queues/executors for heavy post-processing.
- [ ] Provide great UX: show the thumbnail first, stream progress, reveal heavy results when ready.