Skip to content

Commit 9b1913e

Browse files
authored
plain text docs, fix redirects and add logfire (#3096)
1 parent 8ab8bd0 commit 9b1913e

File tree

8 files changed

+2315
-2026
lines changed

8 files changed

+2315
-2026
lines changed

docs-site/package-lock.json

Lines changed: 2174 additions & 1981 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs-site/package.json

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
{
2-
"name": "my-static-site",
3-
"version": "0.0.0",
4-
"private": true,
5-
"scripts": {
6-
"typecheck": "tsc --noEmit",
7-
"typegen": "wrangler types worker-configuration.d.ts",
8-
"deploy": "wrangler deploy",
9-
"dev": "wrangler dev"
10-
},
11-
"dependencies": {
12-
"@cloudflare/workers-types": "^4.20250129.0",
13-
"cookie": ">=0.7.0",
14-
"esbuild": ">=0.25.0",
15-
"marked": "^15.0.8",
16-
"typescript": "^5.7.3",
17-
"wrangler": "^3.106.0"
18-
}
2+
"name": "my-static-site",
3+
"version": "0.0.0",
4+
"private": true,
5+
"scripts": {
6+
"typecheck": "tsgo --noEmit",
7+
"typegen": "wrangler types --strict-vars false --include-runtime false",
8+
"deploy": "wrangler deploy",
9+
"dev": "wrangler dev"
10+
},
11+
"dependencies": {
12+
"@pydantic/logfire-api": "^0.8.2",
13+
"@pydantic/logfire-cf-workers": "^0.8.2",
14+
"marked": "^15.0.8"
15+
},
16+
"devDependencies": {
17+
"@cloudflare/workers-types": "^4.20251004.0",
18+
"@typescript/native-preview": "^7.0.0-dev.20251006.1",
19+
"wrangler": "^4.42.0"
20+
}
1921
}

docs-site/src/index.ts

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
1-
import { marked } from 'marked'
1+
import { instrument } from '@pydantic/logfire-cf-workers'
2+
import { marked } from 'marked'
23

3-
export default {
4+
const handler = {
45
async fetch(request, env): Promise<Response> {
56
const url = new URL(request.url)
67
if (url.pathname === '/changelog.html') {
78
const changelog = await getChangelog(env.KV, env.GIT_COMMIT_SHA)
89
return new Response(changelog, { headers: {'content-type': 'text/html'} })
910
}
11+
const maybeTextResponse = await maybeGetTextResponse(request, env)
12+
if (maybeTextResponse) {
13+
return maybeTextResponse
14+
}
1015
const r = await env.ASSETS.fetch(request)
11-
if (r.status == 404) {
16+
if (r.status === 404) {
1217
const redirectPath = redirect(url.pathname)
1318
if (redirectPath) {
14-
url.pathname = redirectPath
15-
return Response.redirect(url.toString(), 301)
19+
if (redirectPath.startsWith('http')) {
20+
return Response.redirect(redirectPath, 301)
21+
} else {
22+
url.pathname = redirectPath
23+
return Response.redirect(url.toString(), 301)
24+
}
1625
}
1726
url.pathname = '/404.html'
1827
const r = await env.ASSETS.fetch(url)
@@ -22,14 +31,34 @@ export default {
2231
},
2332
} satisfies ExportedHandler<Env>
2433

34+
export default instrument(handler, {
35+
service: {
36+
name: 'pai-docs',
37+
},
38+
baseUrl: 'https://api.logfire.dev',
39+
})
40+
2541
const redirect_lookup: Record<string, string> = {
2642
'/common_tools': '/common-tools/',
2743
'/testing-evals': '/testing/',
2844
'/result': '/output/',
45+
'/mcp/run-python': 'https://github.com/pydantic/mcp-run-python',
46+
'/temporal': '/durable_execution/temporal/',
47+
'/api': '/api/agent/',
48+
'/examples/question-graph': '/graph/',
49+
'/api/models/vertexai': '/models/google/',
50+
'/models/gemini': '/models/google/',
51+
'/api/models/gemini': '/api/models/google/',
52+
'/contributing': '/contributing/',
53+
'/api/format_as_xml': '/api/format_prompt/',
54+
'/api/models/ollama': '/models/openai/#ollama',
55+
'/examples': 'examples/setup/',
56+
'/mcp': '/mcp/overview/',
57+
'/models': '/models/overview/',
2958
}
3059

3160
function redirect(pathname: string): string | null {
32-
return redirect_lookup[pathname.replace(/\/+$/, '')] ?? null
61+
return redirect_lookup[pathname.replace(/[/:]+$/, '')] ?? null
3362
}
3463

3564
async function getChangelog(kv: KVNamespace, commitSha: string): Promise<string> {
@@ -44,8 +73,8 @@ async function getChangelog(kv: KVNamespace, commitSha: string): Promise<string>
4473
}
4574
let url: string | undefined = 'https://api.github.com/repos/pydantic/pydantic-ai/releases'
4675
const releases: Release[] = []
47-
while (typeof url == 'string') {
48-
const response = await fetch(url, { headers })
76+
while (typeof url === 'string') {
77+
const response: Response = await fetch(url, { headers })
4978
if (!response.ok) {
5079
const text = await response.text()
5180
throw new Error(`Failed to fetch changelog: ${response.status} ${response.statusText} ${text}`)
@@ -77,7 +106,7 @@ function prepRelease(release: Release): string {
77106
const body = release.body
78107
.replace(/(#+)/g, (m) => `##${m}`)
79108
.replace(/https:\/\/github.com\/pydantic\/pydantic-ai\/pull\/(\d+)/g, (url, id) => `[#${id}](${url})`)
80-
.replace(/(\s)@([\w\-]+)/g, (_, s, u) => `${s}[@${u}](https://github.com/${u})`)
109+
.replace(/(\s)@([\w-]+)/g, (_, s, u) => `${s}[@${u}](https://github.com/${u})`)
81110
.replace(/\*\*Full Changelog\*\*: (\S+)/, (_, url) => `[${githubIcon} Compare diff](${url}).`)
82111
return `
83112
### ${release.name}
@@ -87,3 +116,38 @@ ${body}
87116
[${githubIcon} View ${release.tag_name} release](${release.html_url}).
88117
`
89118
}
119+
120+
/** Logic to return text (the markdown document where available) when the Accept header prefers plain text over html
121+
* See https://x.com/threepointone/status/1971988718052651300
122+
*/
123+
async function maybeGetTextResponse(request: Request, env: Env): Promise<Response | undefined> {
124+
if (!preferText(request)) {
125+
return
126+
}
127+
const url = new URL(request.url)
128+
url.pathname = `${url.pathname.replace(/[/:]+$/, '')}/index.md`
129+
const r = await env.ASSETS.fetch(url)
130+
if (r.status === 200) {
131+
return new Response(r.body, {
132+
headers: {
133+
'content-type': 'text/plain',
134+
},
135+
})
136+
}
137+
}
138+
139+
function preferText(request: Request): boolean {
140+
const accept = request.headers.get('accept')
141+
if (!accept || request.method !== 'GET') {
142+
return false
143+
}
144+
for (const option of accept.split(',')) {
145+
const lowerOption = option.toLowerCase()
146+
if (lowerOption.includes('html')) {
147+
return false
148+
} else if (lowerOption.includes('text/plain')) {
149+
return true
150+
}
151+
}
152+
return false
153+
}

docs-site/tsconfig.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
"noUnusedParameters": true,
1212
"noFallthroughCasesInSwitch": true,
1313
"noUncheckedSideEffectImports": true,
14+
"skipLibCheck": true,
1415

15-
"types": ["@cloudflare/workers-types"]
16+
"types": ["@cloudflare/workers-types", "./worker-configuration.d.ts"]
1617
},
17-
"include": ["src", "worker-configuration.d.ts"]
18+
"include": ["src"]
1819
}
Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
// Generated by Wrangler by running `wrangler types worker-configuration.d.ts`
2-
3-
interface Env {
4-
KV: KVNamespace;
5-
GIT_COMMIT_SHA: string;
6-
GIT_BRANCH: string;
7-
ASSETS: Fetcher;
1+
/* eslint-disable */
2+
// Generated by Wrangler by running `wrangler types --strict-vars false --include-runtime false` (hash: 09d2a4367f7a2ea42a9ecda5118790a2)
3+
declare namespace Cloudflare {
4+
interface GlobalProps {
5+
mainModule: typeof import("./src/index");
6+
}
7+
interface Env {
8+
KV: KVNamespace;
9+
GIT_COMMIT_SHA: string;
10+
GIT_BRANCH: string;
11+
LOGFIRE_TOKEN: string;
12+
LOGFIRE_ENVIRONMENT: string;
13+
ASSETS: Fetcher;
14+
}
815
}
16+
interface Env extends Cloudflare.Env {}

docs-site/wrangler.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
#:schema node_modules/wrangler/config-schema.json
22
name = "pydantic-ai"
3-
compatibility_date = "2025-01-24"
3+
compatibility_date = "2025-06-17"
44
routes = ["ai.pydantic.dev/*"]
55
main = "src/index.ts"
66
workers_dev = false
7+
compatibility_flags = [ "nodejs_compat_v2" ]
8+
9+
[vars]
10+
LOGFIRE_ENVIRONMENT = "prod"
711

812
[assets]
913
directory = "../site"

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Introduction {.hide}
1+
# Pydantic AI {.hide}
22

33
--8<-- "docs/.partials/index-header.html"
44

mkdocs.yml

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ edit_uri: edit/main/docs/
1010
copyright: © Pydantic Services Inc. 2024 to present
1111

1212
nav:
13-
- Introduction: index.md
13+
- index.md
1414
- install.md
1515
- help.md
1616
- troubleshooting.md
@@ -286,34 +286,51 @@ plugins:
286286
Pydantic AI is a Python agent framework designed to make it less painful to build production grade
287287
applications with Generative AI.
288288
sections:
289+
Introduction:
290+
- index.md
291+
- install.md
292+
- help.md
293+
- troubleshooting.md
294+
- changelog.md
289295
Concepts documentation:
296+
- a2a.md
297+
- ag-ui.md
290298
- agents.md
299+
- builtin-tools.md
291300
- dependencies.md
301+
- deferred-tools.md
302+
- direct.md
303+
- input.md
292304
- tools.md
293305
- common-tools.md
294-
- results.md
306+
- output.md
307+
- retries.md
295308
- message-history.md
296309
- multi-agent-applications.md
310+
- thinking.md
311+
- third-party-tools.md
312+
- tools-advanced.md
313+
- toolsets.md
297314
Models:
298315
- models/*.md
299316
Graphs:
300317
- graph.md
301318
API Reference:
302-
- api/*.md
319+
- api/*/*.md
303320
Evals:
304321
- evals.md
322+
Durable Execution:
323+
- durable_execution/*.md
305324
MCP:
306325
- mcp/*.md
307326
Optional:
308327
- testing.md
309328
- cli.md
310329
- logfire.md
330+
- contributing.md
311331
- examples/*.md
312-
- redirects:
313-
redirect_maps:
314-
"examples/index.md": "examples/setup.md"
315-
"mcp/index.md": "mcp/overview.md"
316-
"models/index.md": "models/overview.md"
332+
333+
# DON'T PUT REDIRECTS IN THIS FILE! Instead add them to docs-site/src/index.ts
317334

318335
hooks:
319336
- "docs/.hooks/main.py"

0 commit comments

Comments
 (0)