mcp-qiskit-learn / theory_tools.py
Ludek Matyska
feat: get_theory tools
eba4c06
raw
history blame
3.63 kB
import re
import requests
import nbformat
RAW_BASE = "https://raw.githubusercontent.com/Qiskit/textbook/main/notebooks/intro/"
README_URL = RAW_BASE + "README.md"
FALLBACK = [
"what-is-quantum.ipynb",
"entangled-states.ipynb",
"superdense-coding.ipynb",
"teleportation.ipynb",
]
# ────────────────────────────────────────────────────────────────────
# Internal helpers
# ────────────────────────────────────────────────────────────────────
def _discover_notebooks() -> list[str]:
"""Scrape notebooks/intro/README.md for *.ipynb links; fallback if offline."""
try:
md = requests.get(README_URL, timeout=10).text
found = re.findall(r"\(([^)]+?\.ipynb)\)", md)
if found:
return found
except requests.RequestException:
pass
return FALLBACK
def _pretty(name: str) -> str:
"""'superdense-coding.ipynb' ➜ 'Superdense Coding'."""
return name.replace("-", " ").replace(".ipynb", "").title()
# ────────────────────────────────────────────────────────────────────
# Public API
# ────────────────────────────────────────────────────────────────────
def get_theory_topics() -> dict[str, str]:
"""
Return a mapping of *friendly topic name* β†’ *notebook filename*.
Example
-------
{'What Is Quantum?': 'what-is-quantum.ipynb', ...}
"""
return {_pretty(f): f for f in _discover_notebooks()}
def get_theory(
topic: str,
markdown_only: bool = True,
include_headers: bool = True,
) -> str:
"""
Download **one** intro notebook and return its content as text.
Parameters
----------
topic
Accepts pretty title (β€œTeleportation”), slug (β€œteleportation”)
or exact filename (β€œteleportation.ipynb”).
markdown_only
True (default) ➜ keep only Markdown cells;
False ➜ include code cells fenced as ```python.
include_headers
Prepend an H1 title for readability.
Raises
------
ValueError
If *topic* cannot be resolved.
Returns
-------
str
Concatenated notebook text.
"""
topics = get_theory_topics()
# Build a lenient lookup table
lookup: dict[str, str] = {}
for pretty, fname in topics.items():
slug = fname.removesuffix(".ipynb")
lookup[pretty.lower()] = fname
lookup[slug.lower()] = fname
lookup[fname.lower()] = fname
key = topic.lower()
if key not in lookup:
raise ValueError(
f"Unknown topic '{topic}'. "
f"Known: {', '.join(topics.keys())}."
)
fname = lookup[key]
raw_json = requests.get(RAW_BASE + fname, timeout=20).text
nb = nbformat.reads(raw_json, as_version=4)
parts: list[str] = []
if include_headers:
parts.append(f"# {_pretty(fname)}\n")
for cell in nb.cells:
if cell.cell_type == "markdown":
parts.append(cell.source)
elif cell.cell_type == "code" and not markdown_only:
parts.append(f"```python\n{cell.source}\n```")
return "\n\n".join(parts)