adf0d17497
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
153 lines
5.0 KiB
Python
153 lines
5.0 KiB
Python
import json
|
|
import re
|
|
from pathlib import Path
|
|
from subprocess import run
|
|
|
|
import html2text
|
|
from bs4 import BeautifulSoup
|
|
from huggingface_hub import HfApi
|
|
|
|
api = HfApi()
|
|
|
|
|
|
def obj_divs_to_markdown(html: str) -> str:
|
|
soup = BeautifulSoup(html, "html.parser")
|
|
objs = soup.select("div.obj")
|
|
if not objs:
|
|
return ""
|
|
obj_html = "\n".join(div.decode_contents() for div in objs)
|
|
md_text = html2text.html2text(obj_html).replace(
|
|
"",
|
|
"",
|
|
)
|
|
md_text = md_text.replace("🔗\n", "")
|
|
md_text = re.sub(r"###\s*Guides[\s\S]*\Z", "", md_text)
|
|
return md_text
|
|
|
|
|
|
MAX_LEN = 2000
|
|
OVERLAP = 200
|
|
H3_PATTERN = re.compile(r"^(###\s+.+)$", re.M)
|
|
H2_PATTERN = re.compile(r"^(##\s+.+)$", re.M)
|
|
|
|
|
|
def split_with_overlap(text: str, max_len: int = MAX_LEN, overlap: int = OVERLAP):
|
|
"""Yield slices of text each ≤ max_len, adding `overlap` chars of context."""
|
|
start = 0
|
|
while start < len(text):
|
|
end = start + max_len
|
|
yield text[start:end]
|
|
start = end - overlap
|
|
|
|
|
|
def markdown_to_docs(
|
|
markdown: str,
|
|
*,
|
|
page_url: str,
|
|
page_title: str,
|
|
docs: list,
|
|
split_by_h2: bool = False,
|
|
):
|
|
"""
|
|
Convert a Markdown string into an array of doc dictionaries, each ≤ 2000 chars.
|
|
Sections are split on h_type headings; oversized sections are broken up with overlap.
|
|
"""
|
|
|
|
H_PATTERN = H2_PATTERN if split_by_h2 else H3_PATTERN
|
|
|
|
headings = [
|
|
(m.start(), m.end(), m.group(1).lstrip("# ").strip())
|
|
for m in H_PATTERN.finditer(markdown)
|
|
]
|
|
headings.append((len(markdown), len(markdown), None))
|
|
|
|
for idx in range(len(headings) - 1):
|
|
start, h_end, h_text = headings[idx]
|
|
next_start, _, _ = headings[idx + 1]
|
|
section_body = markdown[h_end:next_start].lstrip()
|
|
|
|
chunks = (
|
|
[section_body]
|
|
if len(section_body) <= MAX_LEN
|
|
else list(split_with_overlap(section_body))
|
|
)
|
|
|
|
for i, chunk in enumerate(chunks):
|
|
heading = h_text
|
|
docs.append(
|
|
dict(
|
|
text=chunk.replace("# ", "").replace("#", ""),
|
|
heading1=heading,
|
|
source_page_url=page_url,
|
|
source_page_title=page_title,
|
|
)
|
|
)
|
|
return docs
|
|
|
|
|
|
if __name__ == "__main__":
|
|
ROOT = Path(__file__).parent.parent
|
|
DOCS_DIR = (ROOT / "js/_website/build/docs").resolve()
|
|
GUIDES_DIR = (ROOT / "guides").resolve()
|
|
docs = []
|
|
for dir in DOCS_DIR.iterdir():
|
|
if dir.is_dir():
|
|
for file in dir.iterdir():
|
|
if file.is_file() and file.suffix == ".html":
|
|
html = file.read_text(encoding="utf-8")
|
|
markdown = obj_divs_to_markdown(html)
|
|
|
|
page_url = f"https://gradio.app/docs/{dir.name}/{file.name.replace('.html', '')}"
|
|
page_title = f"{dir.name.replace('-', ' ').title()} - {file.name.replace('.html', '').replace('-', ' ').title()} Docs"
|
|
|
|
docs = markdown_to_docs(
|
|
markdown, page_url=page_url, page_title=page_title, docs=docs
|
|
)
|
|
|
|
for dir in GUIDES_DIR.iterdir():
|
|
if dir.is_dir() and dir.name not in ["assets", "cn"]:
|
|
for file in dir.iterdir():
|
|
if file.is_file() and file.suffix == ".md":
|
|
markdown = file.read_text(encoding="utf-8")
|
|
page_name = file.name.replace(".md", "")
|
|
page_name = re.sub(r"^\d+_", "", page_name)
|
|
dir_name = dir.name.replace("-", " ")
|
|
dir_name = re.sub(r"^\d+_", "", dir_name)
|
|
page_url = f"https://gradio.app/guides/{page_name}"
|
|
page_title = f"{dir_name.title()} - {page_name.replace('-', ' ').title()} Guide"
|
|
|
|
docs = markdown_to_docs(
|
|
markdown,
|
|
page_url=page_url,
|
|
page_title=page_title,
|
|
docs=docs,
|
|
split_by_h2=True,
|
|
)
|
|
|
|
print(f"Generated {len(docs)} chunks.")
|
|
|
|
with open((ROOT / "scripts/docs.json").resolve(), "w") as f:
|
|
json.dump(docs, f)
|
|
|
|
result = run(
|
|
["git", "log", "-1", "--pretty=format:%H|%s"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
)
|
|
sha, subject = result.stdout.strip().split("|", 1)
|
|
commit_hash = sha[:7]
|
|
commit_message = subject if len(subject) <= 30 else subject[:30] + "..."
|
|
|
|
try:
|
|
commit_info = api.upload_file(
|
|
path_or_fileobj=(ROOT / "scripts/docs.json").resolve(),
|
|
path_in_repo="docs.json",
|
|
repo_id="gradio/docs",
|
|
repo_type="dataset",
|
|
commit_message=f"Changes from: {commit_hash} '{commit_message}'",
|
|
)
|
|
print("✅ docs.json uploaded")
|
|
except Exception as e:
|
|
print(f"❌ Error uploading docs.json: {e}")
|