Files
docs/scripts/generate_code_snippet_mdx.py
Naomi Pentrel e1b5bd8216 add more code samples tested in deepagents (#4747)
Turning more code samples into testable code samples
2026-07-06 19:21:04 +02:00

297 lines
11 KiB
Python

"""Generate MDX snippet files from extracted code snippet files.
Reads .snippet.*.py, .snippet.*.ts, .snippet.*.java, .snippet.*.kt, .snippet.*.go, and .snippet.*.sh files from src/code-samples-generated/
(produced by ``scripts/extract_code_snippets.py``, Bluehawk-compatible layout).
and creates corresponding MDX files in src/snippets/code-samples/ for use in docs.
When a snippet uses a LangChain-style model argument (`model=""` in Python or
`model: ""` in TypeScript), the generated MDX can be wrapped in <CodeGroup> with the same
seven provider/model options as /oss/deepagents/quickstart (Google, OpenAI, Anthropic,
OpenRouter, Fireworks, Baseten, Ollama). Both `provider:model-id` and bare model names
(for example `claude-sonnet-4-5-20250929`) are recognized.
Snippets are left as a single fenced block when no model argument is found, or when all
model arguments are marked to keep.
To keep a specific model line:
- In Python, put `# KEEP MODEL` on the line immediately before the `model="..."` line.
- In TypeScript, put `// KEEP MODEL` on the line immediately before the `model: "..."` line.
The marker line is stripped during processing and that model occurrence is not
replaced/expanded.
Run as part of `make code-snippets` after `extract_code_snippets.py`.
Optional **CodeGroup tab label** (Mintlify `` ```lang TabTitle``` `` inside ``<CodeGroup>``):
- Put as the **first line inside** the snippet body (after ``:snippet-start:``): ``# :codegroup-tab: Python`` or ``// :codegroup-tab: Java``. Stripped from emitted code.
- Optional **fence modifiers** (for example long samples): the **next** line after a tab marker, or the **first** line when there is no tab, can be ``# :codegroup-fence-mods: expandable wrap`` or ``// :codegroup-fence-mods: expandable wrap``. Stripped from emitted code. Omit for short snippets.
- The fence becomes e.g. `` ```java Java``, `` ```python expandable wrap`` (mods only), or `` ```java Java expandable wrap`` (tab + mods).
"""
from __future__ import annotations
import re
from pathlib import Path
# Optional prefix lines in extracted snippet body; stripped from output. See module docstring.
_CODEGROUP_TAB_MARKER_RE = re.compile(
r"^\s*(?:#|//)\s*:codegroup-tab:\s*(.+?)\s*$",
)
_CODEGROUP_FENCE_MODS_RE = re.compile(
r"^\s*(?:#|//)\s*:codegroup-fence-mods:\s*(.+?)\s*$",
)
# Python: keyword argument model="…" (init_chat_model / create_deep_agent / etc.).
DEEPAGENTS_PY_MODEL_KWARG_RE = re.compile(r'\bmodel\s*=\s*"([^"]+)"')
# TypeScript: object property model: "…" (ChatAnthropic, createDeepAgent, …).
DEEPAGENTS_TS_MODEL_KWARG_RE = re.compile(r'\bmodel\s*:\s*"([^"]+)"')
# Tab title and full `model=` / `model:` token for each variant (matches
# src/oss/deepagents/quickstart.mdx Python tabs; JS uses google-genai spelling).
DEEPAGENTS_QUICKSTART_PY_MODEL_TABS: list[tuple[str, str]] = [
("Google", 'model="google_genai:gemini-3.5-flash"'),
("OpenAI", 'model="openai:gpt-5.5"'),
("Anthropic", 'model="anthropic:claude-sonnet-4-6"'),
("OpenRouter", 'model="openrouter:z-ai/glm-5.2"'),
("Fireworks", 'model="fireworks:accounts/fireworks/models/glm-5p2"'),
("Baseten", 'model="baseten:zai-org/GLM-5.2"'),
("Ollama", 'model="ollama:north-mini-code-1.0"'),
]
DEEPAGENTS_QUICKSTART_TS_MODEL_TABS: list[tuple[str, str]] = [
("Google", 'model: "google-genai:gemini-3.5-flash"'),
("OpenAI", 'model: "openai:gpt-5.5"'),
("Anthropic", 'model: "anthropic:claude-sonnet-4-6"'),
("OpenRouter", 'model: "openrouter:openrouter:z-ai/glm-5.2"'),
("Fireworks", 'model: "fireworks:accounts/fireworks/models/glm-5p2"'),
("Baseten", 'model: "baseten:zai-org/GLM-5.2"'),
("Ollama", 'model: "ollama:north-mini-code-1.0"'),
]
def _model_id_from_py_tab_token(tab_token: str) -> str:
m = re.match(r'model="([^"]+)"', tab_token)
if not m:
msg = f"expected model= tab token, got {tab_token!r}"
raise ValueError(msg)
return m.group(1)
def _model_id_from_ts_tab_token(tab_token: str) -> str:
m = re.match(r'model:\s*"([^"]+)"', tab_token)
if not m:
msg = f"expected model: tab token, got {tab_token!r}"
raise ValueError(msg)
return m.group(1)
DEEPAGENTS_PY_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset()
DEEPAGENTS_TS_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset()
def _id_after_first_colon(tab_id: str) -> str:
"""For openai:gpt-5.4 return gpt-5.4; for bare ids return as-is."""
if ":" not in tab_id:
return tab_id
return tab_id.split(":", 1)[1]
KEEP_MODEL_MARKER_PY = "# KEEP MODEL"
KEEP_MODEL_MARKER_TS = "// KEEP MODEL"
def _strip_codegroup_markers(content: str) -> tuple[str | None, str | None, str]:
"""Strip optional ``:codegroup-tab:`` and ``:codegroup-fence-mods:`` prefix lines.
Returns ``(tab_title, fence_mods, rest)``. Tab is optional; fence-mods may follow a tab
or appear alone as the first line (for standalone fenced blocks outside ``<CodeGroup>``).
"""
if not content:
return None, None, content
lines = content.splitlines(keepends=True)
if not lines:
return None, None, content
i = 0
tab_title: str | None = None
fence_mods: str | None = None
first = lines[0].splitlines()[0] if lines[0] else ""
m = _CODEGROUP_TAB_MARKER_RE.match(first)
if m:
tab_title = m.group(1).strip()
i = 1
if i < len(lines):
line = lines[i].splitlines()[0] if lines[i] else ""
m2 = _CODEGROUP_FENCE_MODS_RE.match(line)
if m2:
fence_mods = m2.group(1).strip()
i += 1
if tab_title is None and fence_mods is None:
return None, None, content
rest = "".join(lines[i:])
return tab_title, fence_mods, rest
def _codegroup_fence(tab_title: str, fence_lang: str, code: str) -> str:
"""One fenced code block inside a <CodeGroup> (indent matches docs conventions)."""
body = "\n".join(" " + line for line in code.splitlines())
return "\n".join(
[
f" ```{fence_lang} {tab_title}",
body,
" ```",
]
)
def _replace_span(text: str, start: int, end: int, replacement: str) -> str:
return text[:start] + replacement + text[end:]
def _expand_to_deepagents_codegroup(
content: str,
*,
canonical_span: tuple[int, int],
tab_definitions: list[tuple[str, str]],
fence_lang: str,
) -> str:
"""Wrap `content` in a CodeGroup, one tab per quickstart model variant."""
start, end = canonical_span
parts = [
_codegroup_fence(
title, fence_lang, _replace_span(content, start, end, model_token)
)
for title, model_token in tab_definitions
]
return "<CodeGroup>\n" + "\n\n".join(parts) + "\n</CodeGroup>\n"
def maybe_expand_deepagents_quickstart_codegroup(
content: str,
*,
language: str,
fence_lang: str,
) -> tuple[str | None, str]:
"""Return (expanded_mdx_or_none, content_with_keep_markers_stripped)."""
model_re: re.Pattern[str]
tab_definitions: list[tuple[str, str]]
keep_marker: str
if language == "python":
model_re = DEEPAGENTS_PY_MODEL_KWARG_RE
tab_definitions = DEEPAGENTS_QUICKSTART_PY_MODEL_TABS
keep_marker = KEEP_MODEL_MARKER_PY
elif language == "ts":
model_re = DEEPAGENTS_TS_MODEL_KWARG_RE
tab_definitions = DEEPAGENTS_QUICKSTART_TS_MODEL_TABS
keep_marker = KEEP_MODEL_MARKER_TS
else:
return None, content
# Strip marker lines while recording which model occurrence to expand.
out_lines: list[str] = []
keep_next_model = False
canonical_span: tuple[int, int] | None = None
for line in content.splitlines(keepends=True):
if line.strip() == keep_marker:
keep_next_model = True
continue
out_offset = sum(len(l) for l in out_lines)
m = model_re.search(line)
if m is not None:
if keep_next_model:
keep_next_model = False
elif canonical_span is None:
canonical_span = (out_offset + m.start(), out_offset + m.end())
out_lines.append(line)
stripped = "".join(out_lines)
if canonical_span is None:
return None, stripped
return (
_expand_to_deepagents_codegroup(
stripped,
canonical_span=canonical_span,
tab_definitions=tab_definitions,
fence_lang=fence_lang,
),
stripped,
)
def format_snippet_mdx(content: str, *, language: str, fence_lang: str) -> str:
"""Return final MDX body for a snippet file."""
content = content.rstrip() + "\n"
tab_title, fence_mods, content = _strip_codegroup_markers(content)
expanded, content = maybe_expand_deepagents_quickstart_codegroup(
content, language=language, fence_lang=fence_lang
)
if expanded is not None:
return expanded
if tab_title is not None:
parts = [fence_lang, tab_title]
if fence_mods:
parts.append(fence_mods)
fence_opener = " ".join(parts)
elif fence_mods:
fence_opener = f"{fence_lang} {fence_mods}"
else:
fence_opener = fence_lang
return f"```{fence_opener}\n{content.rstrip()}\n```\n"
def main() -> None:
repo_root = Path(__file__).resolve().parent.parent
generated_dir = repo_root / "src" / "code-samples-generated"
snippets_dir = repo_root / "src" / "snippets" / "code-samples"
if not generated_dir.exists():
return
snippets_dir.mkdir(parents=True, exist_ok=True)
snippet_configs = [
("*.snippet.*.py", "python", "python"),
("*.snippet.*.ts", "ts", "ts"),
("*.snippet.*.java", "java", "java"),
("*.snippet.*.kt", "kotlin", "kotlin"),
("*.snippet.*.go", "go", "go"),
("*.snippet.*.sh", "bash", "bash"),
]
lang_suffix = {
"python": "-py",
"ts": "-js",
"java": "-java",
"kotlin": "-kt",
"go": "-go",
"bash": "-sh",
}
for glob_pattern, language, fence_lang in snippet_configs:
for snippet_file in generated_dir.rglob(glob_pattern):
snippet_name = ".".join(snippet_file.stem.split(".")[2:])
expected_suffix = lang_suffix[language]
if not snippet_name.endswith(expected_suffix):
continue
content = snippet_file.read_text(encoding="utf-8")
mdx_content = format_snippet_mdx(
content, language=language, fence_lang=fence_lang
)
rel_parent = snippet_file.parent.relative_to(generated_dir)
out_subdir = snippets_dir / rel_parent
out_subdir.mkdir(parents=True, exist_ok=True)
mdx_path = out_subdir / f"{snippet_name}.mdx"
mdx_path.write_text(mdx_content, encoding="utf-8")
print(f"Generated {mdx_path.relative_to(repo_root)}")
if __name__ == "__main__":
main()