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()