Skip to content

A first conversion

Three commands take a module PDF to a draft adventure: price it, convert it, tour the artifacts.

1. Estimate

estimate preprocesses the PDF (renders pages, extracts text layers) and prices the conversion with pinned heuristics — no model call, no spend:

$ osrforge estimate my-module.pdf
pages: 48
page tokens: 27000 text + 43440 image
survey:   in=72440 out=3360
content:  in=88050 out=26400
monsters: in=5000 out=500
total:    in=165490 out=30260
estimated cost: $0.87

The estimate is deliberately rough (±40% on input tokens against the measured calibration runs); the band, not the point value, is the contract. The workdir it creates is warm for osrforge rerun survey, which continues from the rendered pages — a plain convert starts at preprocess by design and re-renders.

2. Convert

Configure the provider (provider setup), then:

$ osrforge convert my-module.pdf
preprocess: running
preprocess: completed
survey: running
survey: completed (in=71203 out=2988)
content: running
content: completed (in=83102 out=21444)
monsters: running
monsters: completed (in=4305 out=340)
assemble: running
assemble: completed
validation: passed
wrote my-module.forge/adventure.json
wrote my-module.forge/overrides.yaml (commented template)

The pipeline is preprocess → survey → content → monsters → mapread → assemble. A failure stops there, keeps everything upstream, and osrforge rerun <stage> resumes — see settings and rerun.

3. Tour the artifacts

Everything lands in my-module.forge/ — the workdir:

  • adventure.json — the draft, loadable by osrlib and anything built on it.
  • report.json — what the pipeline was and wasn't sure about: per-area confidence, source pages, and flags. osrforge report summarizes it without opening JSON by hand.
  • previews/ — one synthesized grid map per level, and an index.html pairing each map with the module's own map-page renders for eyeballing against the printed cartography.
  • overrides.yaml — a commented template, waiting for corrections.

A conversion produces a draft — the geometry is synthesized from the connection graph, not read off the map, and every gap and guess is flagged. The correction loop is how a draft becomes publishable.

The same three steps as a library

from pathlib import Path

from osrforge import ConversionSettings, convert
from osrforge.providers.foundry import FoundryProvider, FoundrySettings

provider = FoundryProvider(FoundrySettings.from_env())
result = convert(Path("my-module.pdf"), Path("my-module.forge"), provider, ConversionSettings())
print(result.report.validation.passed)

convert also takes an on_progress callback that receives each stage transition with its token usage — the stream a host app's worker records.