| import gradio as gr | |
| import os, re, json, textwrap | |
| from typing import Dict, Tuple | |
| SKILLS_ROOT = os.path.join(os.path.dirname(__file__), "skills") | |
| def parse_frontmatter(md_text: str) -> Tuple[dict, str]: | |
| if not md_text.startswith("---"): | |
| return {}, md_text | |
| parts = md_text.split("\n") | |
| if parts[0].strip() != "---": | |
| return {}, md_text | |
| try: | |
| end_idx = parts[1:].index("---") + 1 | |
| except ValueError: | |
| return {}, md_text | |
| fm_lines = parts[1:end_idx] | |
| body = "\n".join(parts[end_idx+1:]) | |
| meta = {} | |
| for line in fm_lines: | |
| if ":" in line: | |
| k, v = line.split(":", 1) | |
| meta[k.strip()] = v.strip().strip('"').strip("'") | |
| return meta, body | |
| def load_skill(skill_slug: str): | |
| path = os.path.join(SKILLS_ROOT, skill_slug, "SKILL.md") | |
| if not os.path.exists(path): | |
| return {}, f"**SKILL.md not found for skill `{skill_slug}`.**" | |
| with open(path, "r", encoding="utf-8") as f: | |
| text = f.read() | |
| meta, body = parse_frontmatter(text) | |
| return meta, body | |
| def list_skills(): | |
| out = [] | |
| if not os.path.isdir(SKILLS_ROOT): | |
| return out | |
| for name in sorted(os.listdir(SKILLS_ROOT)): | |
| if os.path.isdir(os.path.join(SKILLS_ROOT, name)) and os.path.exists(os.path.join(SKILLS_ROOT, name, "SKILL.md")): | |
| meta, _ = load_skill(name) | |
| out.append((name, meta.get("name", name), meta.get("description", ""))) | |
| return out | |
| def list_linked_files(skill_slug: str): | |
| root = os.path.join(SKILLS_ROOT, skill_slug) | |
| if not os.path.isdir(root): | |
| return [] | |
| return [fn for fn in sorted(os.listdir(root)) if fn.lower().endswith(".md") and fn != "SKILL.md"] | |
| def read_linked_file(skill_slug: str, filename: str) -> str: | |
| path = os.path.join(SKILLS_ROOT, skill_slug, filename) | |
| if not os.path.exists(path): | |
| return f"**{filename} not found.**" | |
| with open(path, "r", encoding="utf-8") as f: | |
| return f.read() | |
| def run_pdf_tool(skill_slug: str, uploaded_pdf) -> str: | |
| try: | |
| if uploaded_pdf is None: | |
| return "Please upload a PDF." | |
| import runpy, json as _json, os as _os | |
| tool_path = os.path.join(SKILLS_ROOT, skill_slug, "tools", "extract_form_fields.py") | |
| if not os.path.exists(tool_path): | |
| return "Tool script not found. Expected tools/extract_form_fields.py" | |
| ns = runpy.run_path(tool_path) | |
| if "extract_fields" not in ns: | |
| return "extract_form_fields.py does not define extract_fields(pdf_path)." | |
| fn = ns["extract_fields"] | |
| result = fn(uploaded_pdf.name if hasattr(uploaded_pdf, "name") else uploaded_pdf) | |
| try: | |
| return "```json\n" + _json.dumps(result, indent=2) + "\n```" | |
| except Exception: | |
| return str(result) | |
| except Exception as e: | |
| return f"Error running tool: {e}" | |
| def explain_progressive_disclosure() -> str: | |
| return ( | |
| "### Progressive Disclosure\\n\\n" | |
| "1. **Startup**: Only skill *metadata* (name, description) is shown.\\n" | |
| "2. **Trigger**: Loading a skill reads **SKILL.md** into context.\\n" | |
| "3. **Deep Dive**: Linked files (e.g., `reference.md`, `forms.md`) are opened only when needed.\\n" | |
| "4. **Tools**: For the PDF skill, run the Python tool to extract form fields without adding the PDF to context." | |
| ) | |
| with gr.Blocks(title="Agent Skills β Progressive Disclosure Demo") as demo: | |
| gr.Markdown("# Equipping agents for the real world with Agent Skills\\nPublished Oct 16, 2025") | |
| gr.Markdown("This Space demonstrates **Agent Skills** as folders containing a `SKILL.md`, optional linked files, and tools.") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Installed Skills") | |
| skills = list_skills() | |
| skill_map = {f"{title} β {desc}": slug for (slug, title, desc) in skills} if skills else {} | |
| labels = list(skill_map.keys()) or ["(No skills found)"] | |
| skill_dd = gr.Dropdown(choices=labels, value=labels[0], label="Pick a skill") | |
| meta_out = gr.JSON(label="Skill metadata") | |
| def on_pick(label): | |
| if not skill_map: | |
| return {}, "", [], None, None | |
| slug = skill_map.get(label, None) | |
| if not slug: | |
| return {}, "", [], None, None | |
| meta, body = load_skill(slug) | |
| return meta, body, list_linked_files(slug), slug, None | |
| body_out = gr.Markdown(label="SKILL.md body") | |
| linked_files = gr.Dropdown(choices=[], label="Linked files") | |
| selected_slug_state = gr.State(value=None) | |
| _reset = gr.State(value=None) | |
| skill_dd.change(fn=on_pick, inputs=[skill_dd], outputs=[meta_out, body_out, linked_files, selected_slug_state, _reset]) | |
| with gr.Column(scale=2): | |
| gr.Markdown("### Progressive Disclosure") | |
| gr.Markdown(explain_progressive_disclosure()) | |
| gr.Markdown("---") | |
| gr.Markdown("### Linked File Viewer") | |
| linked_view = gr.Markdown() | |
| def on_view_linked(filename, slug): | |
| if not slug or not filename: | |
| return "Pick a skill and a linked file." | |
| return read_linked_file(slug, filename) | |
| view_btn = gr.Button("Open linked file") | |
| view_btn.click(fn=on_view_linked, inputs=[linked_files, selected_slug_state], outputs=[linked_view]) | |
| gr.Markdown("---") | |
| gr.Markdown("### Run Skill Tool (PDF form field extractor)") | |
| pdf_in = gr.File(label="Upload a PDF", file_count="single", type="filepath") | |
| tool_out = gr.Markdown() | |
| def run_tool_clicked(slug, pdf): | |
| if not slug: | |
| return "Pick a skill first." | |
| return run_pdf_tool(slug, pdf) | |
| run_tool_btn = gr.Button("Extract form fields") | |
| run_tool_btn.click(fn=run_tool_clicked, inputs=[selected_slug_state, pdf_in], outputs=[tool_out]) | |
| if __name__ == "__main__": | |
| demo.launch() | |