Browser
Submits a title and abstract to the custom domain over HTTPS.
A practical tour of the request path, vector data, storage choices, bounded search, caching, and deployment. It starts with what the browser does and works backwards to the offline data build.
The public site is one Worker and the similarity engine is another. They communicate through a private Cloudflare service binding. The API embeds the manuscript once, compares it with a small derived index, and returns HTML-ready data to the UI.
Submits a title and abstract to the custom domain over HTTPS.
Validates the form, calls the private API, and renders HTML with Hono and htmx.
Embeds the query, ranks journal centroids, then searches only the leading journal shards.
Workers AI creates the query vector; immutable assets provide derived indexes and map data.
/picker/analyse.picker-ui parses and validates it. A missing title is rejected locally. The UI makes exactly one application-service call.picker-api. This is Worker-to-Worker communication, not a fetch to a public API hostname.picker-apiRebuilding indexes can read the whole portfolio because it runs deliberately, outside a user request. It validates that the article ID at row N still belongs to vector row N; a mismatch stops the build.
Journal ranking always has 74 comparisons. Related-article ranking is deliberately scoped by the first result: only the three best journal shards are loaded. That makes the extra work explainable and prevents a feature request from quietly restoring a full-portfolio scan.
The query, article vectors, centroids, and map all use
@cf/baai/bge-small-en-v1.5. Older SPECTER map
coordinates are not reused because numbers from different
embedding models are not comparable.
The map starts with the same 74 BGE centroids used for ranking. Principal component analysis (PCA) projects those 384-dimensional points into two dimensions during the offline build. Nearest-neighbour lines reveal local relationships; numbered circles identify the current recommendations; a diamond places the submitted article.
Seeing neighbouring journal communities, broad disciplines, and where a manuscript sits relative to the portfolio.
Reading exact similarity from pixel distance. A 2D projection necessarily discards information from the original 384 dimensions.
| Place | What lives there | On the live request path? |
|---|---|---|
| R2 | Canonical article metadata, full text, and full vector datasets with catalogs and checksums. | No. It is the published source and rebuild origin. |
| picker-api static assets | Journal centroids, per-journal article vector shards, sidecars, and PCA map parameters. | Yes, but only through the Worker's private ASSETS binding. |
| picker-ui static assets | Pico CSS, htmx, and this architecture page. | Yes. |
| D1 (optional) | Picker activity and agree/disagree feedback when configured. | Only for logging and admin views; never required for recommendations. |
| Local files | Rebuild mirror, scripts, tests, and generated deployment assets. | No. |
D1 is Cloudflare's managed SQL database with SQLite semantics; it is
different from putting a writable .sqlite3 file inside
a Worker. The Worker's deployed filesystem is not used as mutable
application storage.
The tiny centroid matrix and map snapshot are parsed once per warm Worker isolate. Article shards use a six-entry least-recently-used cache, so repeated queries in similar fields avoid reparsing while a long-lived isolate cannot gradually retain the full corpus.
A new isolate reads the required immutable assets again. Correctness never depends on a cache being warm; a miss causes bounded extra I/O, not a portfolio-wide analytics rebuild.
Submitted manuscript text and query vectors are not written into a vector cache or R2 object by the recommendation API. Optional D1 activity logging is a separate feature and can be disabled.
If related-article assets or map data cannot load, the API still returns the core journal ranking. If D1 is unavailable, matching still works and only activity recording is skipped.
| Component | Exposure | Bindings |
|---|---|---|
picker-ui | journal-picker.bmj-cloudflare-hackathon.com | PICKER_API, ASSETS, optional D1 and admin secrets |
picker-api | Private; workers.dev disabled | AI, ASSETS |
| R2 data hosts | Public dataset domains | Published catalogs and objects; not application RPC endpoints |
The custom domain belongs to picker-ui. Cloudflare
manages the hostname routing and certificate. The UI calls the API
using the configured service binding, so the backend does not need a
public DNS record.
The account target is 3984c21be244d542dcd9f617b249db50. Credentials remain in local ignored files or Wrangler's authenticated profile; they do not belong in source or this page.
# Rebuild the bounded derived assets
python3 scripts/build_journal_centroids.py
python3 scripts/build_related_article_shards.py
python3 scripts/build_journal_map.py
# Validate and test
python3 scripts/validate_hackathon_assets.py
(cd workers/picker-api && npm test && npm run check && npm run dry-run)
(cd workers/picker-ui && npm test && npm run check && npm run dry-run)
# Deploy backend first, then the public UI
(cd workers/picker-api && npx wrangler deploy)
(cd workers/picker-ui && npx wrangler deploy)
Healthy means: the picker loads, one real request returns journals, related articles link to metadata, the map renders, and /architecture remains reachable.