Skip to content

Commit e7e8521

Browse files
committed
Add AI 2027 and Most Important Century
AI 2027 (Kokotajlo et al., April 2025): month-by-month scenario forecast with two branching endings. build_ai_2027.py's fetch_image is now the same Pillow re-encode used by build_situational_awareness.py, plus an SVG passthrough since Pillow can't decode SVG. The Most Important Century (Karnofsky, 2021): Cold Takes already publishes an author-maintained EPUB, so build_most_important_century.py just downloads and validates that file rather than re-scraping. Covers: ai-2027.jpg is the site's landscape og:image (placeholder until a portrait cover exists); most-important-century.jpg is extracted from the author's EPUB.
1 parent 80e9a6e commit e7e8521

6 files changed

Lines changed: 138 additions & 27 deletions
8.02 MB
Binary file not shown.

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,28 @@ Nine-part series arguing that AGI by roughly 2027 is plausible from straight-lin
3030

3131
---
3232

33+
<table>
34+
<tr>
35+
<td width="200" valign="top">
36+
<a href="./Daniel%20Kokotajlo%20et%20al.%20-%20AI%202027.epub"><img src="covers/ai-2027.jpg" width="180" alt="AI 2027 cover" /></a>
37+
</td>
38+
<td valign="top">
39+
40+
### AI 2027
41+
42+
**Daniel Kokotajlo, Scott Alexander, Thomas Larsen, Eli Lifland, Romeo Dean** · April 2025 · ~90 pages
43+
44+
A month-by-month scenario forecast from mid-2025 through the end of 2027, told as a concrete narrative: an "OpenBrain" lab automates AI R&D, China responds, and the world reaches a branch point. Includes both endings — the *Slowdown* branch and the *Race* branch — plus 99 footnotes.
45+
46+
📖 [`Daniel Kokotajlo et al. - AI 2027.epub`](./Daniel%20Kokotajlo%20et%20al.%20-%20AI%202027.epub) · main scenario + both endings + footnotes; interactive timeline chart and research supplements not included<br>
47+
🌐 Read online: [ai-2027.com](https://ai-2027.com/) · [Official PDF](https://ai-2027.com/ai-2027.pdf)
48+
49+
</td>
50+
</tr>
51+
</table>
52+
53+
---
54+
3355
<table>
3456
<tr>
3557
<td width="200" valign="top">
@@ -74,6 +96,28 @@ The companion piece to *Machines of Loving Grace*. The risks side: national secu
7496

7597
---
7698

99+
<table>
100+
<tr>
101+
<td width="200" valign="top">
102+
<a href="./Holden%20Karnofsky%20-%20The%20Most%20Important%20Century.epub"><img src="covers/most-important-century.jpg" width="180" alt="The Most Important Century cover" /></a>
103+
</td>
104+
<td valign="top">
105+
106+
### The Most Important Century
107+
108+
**Holden Karnofsky** · 2021 · ~200 pages
109+
110+
The accessible on-ramp to this whole discourse. Argues the 21st century could be the most pivotal in human history because of transformative AI — via digital people, PASTA (automating scientific and technological advancement), and what to do about it. This EPUB is Holden's own author-published file, mirrored as-is.
111+
112+
📖 [`Holden Karnofsky - The Most Important Century.epub`](./Holden%20Karnofsky%20-%20The%20Most%20Important%20Century.epub) · 8 MB · author's official EPUB<br>
113+
🌐 Read online: [cold-takes.com/most-important-century](https://www.cold-takes.com/most-important-century/) · [Official PDF](https://www.cold-takes.com/assets/files/most-important-century-consolidated.pdf) · [AZW3](https://www.cold-takes.com/assets/files/most-important-century-kindle-file.azw3)
114+
115+
</td>
116+
</tr>
117+
</table>
118+
119+
---
120+
77121
<table>
78122
<tr>
79123
<td width="200" valign="top">
@@ -100,15 +144,17 @@ Pre-GPT-4 essay arguing that scale alone might be sufficient for general intelli
100144

101145
All EPUBs are produced by scraping just the article content from each author's canonical page (with [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/)), caching figures locally (re-encoding GIFs and WebP to static PNGs that Kindle can render), repairing invalid block-inside-inline HTML left by WordPress / Webflow plugins, and converting via [Calibre](https://calibre-ebook.com/)'s `ebook-convert` to EPUB 3.
102146

103-
All four EPUBs validate clean against [`epubcheck`](https://www.w3.org/publishing/epubcheck/) (0 errors, 0 warnings).
147+
All EPUBs built here validate clean against [`epubcheck`](https://www.w3.org/publishing/epubcheck/) (0 errors, 0 warnings). *The Most Important Century* is the exception to the pipeline: Cold Takes already publishes an author-maintained EPUB, so the script simply mirrors that file rather than rebuilding it.
104148

105149
```bash
106150
pip install beautifulsoup4 Pillow
107151
brew install --cask calibre # macOS, or install Calibre from elsewhere
108152
brew install epubcheck # optional, for validation
109153

110154
python3 scripts/build_situational_awareness.py
155+
python3 scripts/build_ai_2027.py
111156
python3 scripts/build_dario_essays.py
157+
python3 scripts/build_most_important_century.py # downloads author's own EPUB
112158
python3 scripts/build_gwern_essay.py
113159
```
114160

@@ -118,6 +164,6 @@ Drop any of the `.epub` files on [Send to Kindle](https://www.amazon.com/sendtok
118164

119165
## Attribution & licensing
120166

121-
All essays are © their respective authors (Leopold Aschenbrenner, Dario Amodei, Gwern Branwen). Cover images are from each author's own website. The EPUBs are mirrored here for offline reading convenience with prominent attribution and links to the canonical sources. The authors retain all rights; please respect their preferences if they ask for content to be removed.
167+
All essays are © their respective authors (Leopold Aschenbrenner, Daniel Kokotajlo et al., Dario Amodei, Holden Karnofsky, Gwern Branwen). Cover images are from each author's own website. The EPUBs are mirrored here for offline reading convenience with prominent attribution and links to the canonical sources. The authors retain all rights; please respect their preferences if they ask for content to be removed.
122168

123169
The build scripts in this repository are MIT-licensed (see [`LICENSE`](./LICENSE)).

covers/ai-2027.jpg

58.2 KB
Loading

covers/most-important-century.jpg

350 KB
Loading

scripts/build_ai_2027.py

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,34 +79,57 @@ def resolve_img_src(src: str) -> str:
7979
return src
8080

8181

82+
_PIL_FMT_TO_EXT = {"JPEG": ".jpg", "PNG": ".png", "GIF": ".png", "WEBP": ".png", "BMP": ".png", "TIFF": ".jpg"}
83+
84+
8285
def fetch_image(url: str, img_dir: Path) -> str:
86+
"""Fetch an image and re-encode through Pillow so the EPUB always gets a
87+
clean PNG/JPEG with the correct extension (fixes extensionless CDN URLs,
88+
animated GIF/WebP, and content-type/filename mismatches). SVG passes
89+
through untouched — Pillow can't decode it, and EPUB 3 supports it natively.
90+
"""
91+
from io import BytesIO
92+
from PIL import Image
93+
8394
parsed = urllib.parse.urlparse(url)
84-
name = os.path.basename(parsed.path) or "img"
85-
name = re.sub(r"[^A-Za-z0-9._-]", "_", name)[:80]
86-
if "." not in name:
87-
name += ".png"
95+
raw_name = os.path.basename(parsed.path) or "img"
96+
stem = re.sub(r"[^A-Za-z0-9_-]", "_", os.path.splitext(raw_name)[0])[:60] or "img"
8897
prefix = hashlib.md5(url.encode()).hexdigest()[:6]
89-
safe = f"{prefix}_{name}"
90-
local = img_dir / safe
91-
if not local.exists():
92-
try:
93-
data = fetch(url)
94-
local.write_bytes(data)
95-
# Re-encode formats Send-to-Kindle chokes on (WebP, animated GIF, SVG-as-img)
96-
ext = os.path.splitext(name)[1].lower()
97-
if ext in (".webp", ".gif", ".svg"):
98-
try:
99-
from PIL import Image
100-
png = local.with_suffix(".png")
101-
Image.open(local).convert("RGB").save(png, "PNG")
102-
local.unlink()
103-
return f"images/{png.name}"
104-
except Exception:
105-
pass
106-
except Exception as e:
107-
print(f" ! image failed {url}: {e}")
108-
return url
109-
return f"images/{safe}"
98+
99+
for existing in img_dir.glob(f"{prefix}_{stem}.*"):
100+
return f"images/{existing.name}"
101+
102+
try:
103+
data = fetch(url)
104+
except Exception as e:
105+
print(f" ! image fetch failed {url}: {e}")
106+
return url
107+
108+
if data.lstrip()[:4] in (b"<svg", b"<?xm") or raw_name.lower().endswith(".svg"):
109+
safe = f"{prefix}_{stem}.svg"
110+
(img_dir / safe).write_bytes(data)
111+
return f"images/{safe}"
112+
113+
try:
114+
im = Image.open(BytesIO(data))
115+
if getattr(im, "is_animated", False):
116+
im.seek(0)
117+
fmt = (im.format or "PNG").upper()
118+
if fmt == "JPEG" and im.mode not in ("RGB", "L"):
119+
im = im.convert("RGB")
120+
elif fmt != "JPEG" and im.mode not in ("RGB", "RGBA", "L"):
121+
im = im.convert("RGBA")
122+
out_ext = _PIL_FMT_TO_EXT.get(fmt, ".png")
123+
out_fmt = "JPEG" if out_ext == ".jpg" else "PNG"
124+
safe = f"{prefix}_{stem}{out_ext}"
125+
save_kwargs = {"optimize": True}
126+
if out_fmt == "JPEG":
127+
save_kwargs["quality"] = 85
128+
im.save(img_dir / safe, format=out_fmt, **save_kwargs)
129+
return f"images/{safe}"
130+
except Exception as e:
131+
print(f" ! image decode failed {url}: {e}")
132+
return url
110133

111134

112135
def clean_fragment(frag, img_dir: Path) -> None:
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Fetch Holden Karnofsky's official EPUB of the "Most Important Century" series.
4+
5+
Unlike the other essays in this repo, Cold Takes already publishes an
6+
author-maintained EPUB (plus AZW3 and PDF) of the full series, so there's no
7+
need to scrape and rebuild — this script just downloads the canonical file and
8+
renames it to the repo's `Author - Title.epub` convention.
9+
10+
Canonical source: https://www.cold-takes.com/most-important-century/
11+
Direct file: https://www.cold-takes.com/assets/files/most-important-century-ebook.epub
12+
13+
Usage: python3 scripts/build_most_important_century.py
14+
Output: ./Holden Karnofsky - The Most Important Century.epub
15+
"""
16+
17+
import sys
18+
import urllib.request
19+
import zipfile
20+
from pathlib import Path
21+
22+
URL = "https://www.cold-takes.com/assets/files/most-important-century-ebook.epub"
23+
OUTFILE = "Holden Karnofsky - The Most Important Century.epub"
24+
25+
26+
def main() -> None:
27+
out = Path.cwd() / OUTFILE
28+
print(f"fetching {URL}")
29+
req = urllib.request.Request(URL, headers={"User-Agent": "ai-reading-list/1.0"})
30+
with urllib.request.urlopen(req, timeout=120) as r:
31+
out.write_bytes(r.read())
32+
size = out.stat().st_size
33+
if size < 100_000:
34+
sys.exit(f"downloaded file is suspiciously small ({size} bytes) — check {URL}")
35+
with zipfile.ZipFile(out) as z:
36+
if "mimetype" not in z.namelist() or z.read("mimetype") != b"application/epub+zip":
37+
sys.exit("downloaded file is not a valid EPUB container")
38+
print(f"→ {out} ({size:,} bytes, {len(z.namelist())} entries)")
39+
40+
41+
if __name__ == "__main__":
42+
main()

0 commit comments

Comments
 (0)