Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,155 changes: 2,174 additions & 1,981 deletions docs-site/package-lock.json

Large diffs are not rendered by default.

36 changes: 19 additions & 17 deletions docs-site/package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
{
"name": "my-static-site",
"version": "0.0.0",
"private": true,
"scripts": {
"typecheck": "tsc --noEmit",
"typegen": "wrangler types worker-configuration.d.ts",
"deploy": "wrangler deploy",
"dev": "wrangler dev"
},
"dependencies": {
"@cloudflare/workers-types": "^4.20250129.0",
"cookie": ">=0.7.0",
"esbuild": ">=0.25.0",
"marked": "^15.0.8",
"typescript": "^5.7.3",
"wrangler": "^3.106.0"
}
"name": "my-static-site",
"version": "0.0.0",
"private": true,
"scripts": {
"typecheck": "tsgo --noEmit",
"typegen": "wrangler types --strict-vars false --include-runtime false",
"deploy": "wrangler deploy",
"dev": "wrangler dev"
},
"dependencies": {
"@pydantic/logfire-api": "^0.8.2",
"@pydantic/logfire-cf-workers": "^0.8.2",
"marked": "^15.0.8"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20251004.0",
"@typescript/native-preview": "^7.0.0-dev.20251006.1",
"wrangler": "^4.42.0"
}
}
82 changes: 73 additions & 9 deletions docs-site/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { marked } from 'marked'
import { instrument } from '@pydantic/logfire-cf-workers'
import { marked } from 'marked'

export default {
const handler = {
async fetch(request, env): Promise<Response> {
const url = new URL(request.url)
if (url.pathname === '/changelog.html') {
const changelog = await getChangelog(env.KV, env.GIT_COMMIT_SHA)
return new Response(changelog, { headers: {'content-type': 'text/html'} })
}
const maybeTextResponse = await maybeGetTextResponse(request, env)
if (maybeTextResponse) {
return maybeTextResponse
}
const r = await env.ASSETS.fetch(request)
if (r.status == 404) {
if (r.status === 404) {
const redirectPath = redirect(url.pathname)
if (redirectPath) {
url.pathname = redirectPath
return Response.redirect(url.toString(), 301)
if (redirectPath.startsWith('http')) {
return Response.redirect(redirectPath, 301)
} else {
url.pathname = redirectPath
return Response.redirect(url.toString(), 301)
}
}
url.pathname = '/404.html'
const r = await env.ASSETS.fetch(url)
Expand All @@ -22,14 +31,34 @@ export default {
},
} satisfies ExportedHandler<Env>

export default instrument(handler, {
service: {
name: 'pai-docs',
},
baseUrl: 'https://api.logfire.dev',
})

const redirect_lookup: Record<string, string> = {
'/common_tools': '/common-tools/',
'/testing-evals': '/testing/',
'/result': '/output/',
'/mcp/run-python': 'https://github.com/pydantic/mcp-run-python',
'/temporal': '/durable_execution/temporal/',
'/api': '/api/agent/',
'/examples/question-graph': '/graph/',
'/api/models/vertexai': '/models/google/',
'/models/gemini': '/models/google/',
'/api/models/gemini': '/api/models/google/',
'/contributing': '/contributing/',
'/api/format_as_xml': '/api/format_prompt/',
'/api/models/ollama': '/models/openai/#ollama',
'/examples': 'examples/setup/',
'/mcp': '/mcp/overview/',
'/models': '/models/overview/',
}

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

async function getChangelog(kv: KVNamespace, commitSha: string): Promise<string> {
Expand All @@ -44,8 +73,8 @@ async function getChangelog(kv: KVNamespace, commitSha: string): Promise<string>
}
let url: string | undefined = 'https://api.github.com/repos/pydantic/pydantic-ai/releases'
const releases: Release[] = []
while (typeof url == 'string') {
const response = await fetch(url, { headers })
while (typeof url === 'string') {
const response: Response = await fetch(url, { headers })
if (!response.ok) {
const text = await response.text()
throw new Error(`Failed to fetch changelog: ${response.status} ${response.statusText} ${text}`)
Expand Down Expand Up @@ -77,7 +106,7 @@ function prepRelease(release: Release): string {
const body = release.body
.replace(/(#+)/g, (m) => `##${m}`)
.replace(/https:\/\/github.com\/pydantic\/pydantic-ai\/pull\/(\d+)/g, (url, id) => `[#${id}](${url})`)
.replace(/(\s)@([\w\-]+)/g, (_, s, u) => `${s}[@${u}](https://github.com/${u})`)
.replace(/(\s)@([\w-]+)/g, (_, s, u) => `${s}[@${u}](https://github.com/${u})`)
.replace(/\*\*Full Changelog\*\*: (\S+)/, (_, url) => `[${githubIcon} Compare diff](${url}).`)
return `
### ${release.name}
Expand All @@ -87,3 +116,38 @@ ${body}
[${githubIcon} View ${release.tag_name} release](${release.html_url}).
`
}

/** Logic to return text (the markdown document where available) when the Accept header prefers plain text over html
* See https://x.com/threepointone/status/1971988718052651300
*/
async function maybeGetTextResponse(request: Request, env: Env): Promise<Response | undefined> {
if (!preferText(request)) {
return
}
const url = new URL(request.url)
url.pathname = `${url.pathname.replace(/[/:]+$/, '')}/index.md`
const r = await env.ASSETS.fetch(url)
if (r.status === 200) {
return new Response(r.body, {
headers: {
'content-type': 'text/plain',
},
})
}
}

function preferText(request: Request): boolean {
const accept = request.headers.get('accept')
if (!accept || request.method !== 'GET') {
return false
}
for (const option of accept.split(',')) {
const lowerOption = option.toLowerCase()
if (lowerOption.includes('html')) {
return false
} else if (lowerOption.includes('text/plain')) {
return true
}
}
return false
}
5 changes: 3 additions & 2 deletions docs-site/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"skipLibCheck": true,

"types": ["@cloudflare/workers-types"]
"types": ["@cloudflare/workers-types", "./worker-configuration.d.ts"]
},
"include": ["src", "worker-configuration.d.ts"]
"include": ["src"]
}
22 changes: 15 additions & 7 deletions docs-site/worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
// Generated by Wrangler by running `wrangler types worker-configuration.d.ts`

interface Env {
KV: KVNamespace;
GIT_COMMIT_SHA: string;
GIT_BRANCH: string;
ASSETS: Fetcher;
/* eslint-disable */
// Generated by Wrangler by running `wrangler types --strict-vars false --include-runtime false` (hash: 09d2a4367f7a2ea42a9ecda5118790a2)
declare namespace Cloudflare {
interface GlobalProps {
mainModule: typeof import("./src/index");
}
interface Env {
KV: KVNamespace;
GIT_COMMIT_SHA: string;
GIT_BRANCH: string;
LOGFIRE_TOKEN: string;
LOGFIRE_ENVIRONMENT: string;
ASSETS: Fetcher;
}
}
interface Env extends Cloudflare.Env {}
6 changes: 5 additions & 1 deletion docs-site/wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#:schema node_modules/wrangler/config-schema.json
name = "pydantic-ai"
compatibility_date = "2025-01-24"
compatibility_date = "2025-06-17"
routes = ["ai.pydantic.dev/*"]
main = "src/index.ts"
workers_dev = false
compatibility_flags = [ "nodejs_compat_v2" ]

[vars]
LOGFIRE_ENVIRONMENT = "prod"

[assets]
directory = "../site"
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Introduction {.hide}
# Pydantic AI {.hide}

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

Expand Down
33 changes: 25 additions & 8 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ edit_uri: edit/main/docs/
copyright: © Pydantic Services Inc. 2024 to present

nav:
- Introduction: index.md
- index.md
- install.md
- help.md
- troubleshooting.md
Expand Down Expand Up @@ -286,34 +286,51 @@ plugins:
Pydantic AI is a Python agent framework designed to make it less painful to build production grade
applications with Generative AI.
sections:
Introduction:
- index.md
- install.md
- help.md
- troubleshooting.md
- changelog.md
Concepts documentation:
- a2a.md
- ag-ui.md
- agents.md
- builtin-tools.md
- dependencies.md
- deferred-tools.md
- direct.md
- input.md
- tools.md
- common-tools.md
- results.md
- output.md
- retries.md
- message-history.md
- multi-agent-applications.md
- thinking.md
- third-party-tools.md
- tools-advanced.md
- toolsets.md
Models:
- models/*.md
Graphs:
- graph.md
API Reference:
- api/*.md
- api/*/*.md
Evals:
- evals.md
Durable Execution:
- durable_execution/*.md
MCP:
- mcp/*.md
Optional:
- testing.md
- cli.md
- logfire.md
- contributing.md
- examples/*.md
- redirects:
redirect_maps:
"examples/index.md": "examples/setup.md"
"mcp/index.md": "mcp/overview.md"
"models/index.md": "models/overview.md"

# DON'T PUT REDIRECTS IN THIS FILE! Instead add them to docs-site/src/index.ts

hooks:
- "docs/.hooks/main.py"
Expand Down