Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| import zipfile | |
| import io | |
| import os | |
| import tempfile | |
| from pypdf import PdfReader | |
| # Video URL of The Count | |
| VIDEO_URL = "https://images-ext-1.discordapp.net/external/PD3SdBHrOGxZtFU8zypW2PjGXabBG-OwY3rXB8XWG7Y/https/media.tenor.com/1nFPylOyXZIAAAPo/sesame-street-the-count.mp4" | |
| def get_count_response(count): | |
| if count == 0: | |
| return "Zero! Zero em dashes! Ah ah ah!" | |
| elif count == 1: | |
| return "One! One em dash! Ah ah ah!" | |
| elif count <= 20: | |
| counting = " ".join([f"{i}!" for i in range(1, count + 1)]) | |
| return f"{counting} {count} em dashes! Ah ah ah!" | |
| else: | |
| return f"One! Two! Three! ... {count}! {count} em dashes! Ah ah ah!" | |
| def find_em_dashes_in_bytes(content_bytes, filename): | |
| locations = [] | |
| ext = os.path.splitext(filename.lower())[1] | |
| if ext == '.pdf': | |
| try: | |
| reader = PdfReader(io.BytesIO(content_bytes)) | |
| for page_number, page in enumerate(reader.pages, start=1): | |
| text = page.extract_text() | |
| if text: | |
| for line_number, line in enumerate(text.splitlines(), start=1): | |
| count = line.count('—') | |
| if count: | |
| locations.append({ | |
| "file": filename, | |
| "page": page_number, | |
| "line": line_number, | |
| "count": count, | |
| "text": line.strip(), | |
| }) | |
| except Exception: | |
| pass | |
| else: | |
| # Assume text-based for other common extensions | |
| try: | |
| content = content_bytes.decode('utf-8', errors='ignore') | |
| for line_number, line in enumerate(content.splitlines(), start=1): | |
| count = line.count('—') | |
| if count: | |
| locations.append({ | |
| "file": filename, | |
| "page": None, | |
| "line": line_number, | |
| "count": count, | |
| "text": line.strip(), | |
| }) | |
| except Exception: | |
| pass | |
| return locations | |
| def markdown_code(text): | |
| return text.replace("`", "ˋ") | |
| def create_audit_markdown(locations): | |
| total_count = sum(item["count"] for item in locations) | |
| lines = [ | |
| "# Em Dash Audit", | |
| "", | |
| "> **Instruction for the LLM agent:** Audit every em dash at the locations listed below, including its surrounding text and repository context. Report recommended edits that could clean up the repository. **Do not edit any files during this audit.** Only recommend changes and provide a plan for applying them.", | |
| "", | |
| f"**Summary:** {total_count} em dash{'es' if total_count != 1 else ''} across {len(locations)} line{'s' if len(locations) != 1 else ''}.", | |
| "", | |
| "## Locations", | |
| "", | |
| ] | |
| if not locations: | |
| lines.append("No em dashes were found.") | |
| else: | |
| current_file = None | |
| for item in locations: | |
| if item["file"] != current_file: | |
| current_file = item["file"] | |
| lines.extend([f"### `{markdown_code(current_file)}`", ""]) | |
| position = f"Page {item['page']}, line {item['line']}" if item["page"] else f"Line {item['line']}" | |
| occurrence_label = f" — {item['count']} occurrences" if item["count"] > 1 else "" | |
| lines.append(f"- **{position}**{occurrence_label}: `{markdown_code(item['text'])}`") | |
| lines.append("") | |
| with tempfile.NamedTemporaryFile( | |
| mode="w", | |
| encoding="utf-8", | |
| suffix=".md", | |
| prefix="em-dash-audit-", | |
| delete=False, | |
| ) as audit_file: | |
| audit_file.write("\n".join(lines)) | |
| return audit_file.name | |
| def process_input(input_url, uploaded_file): | |
| locations = [] | |
| processed = False | |
| # Handle Uploaded File | |
| if uploaded_file is not None: | |
| with open(uploaded_file.name, "rb") as f: | |
| file_bytes = f.read() | |
| locations.extend(find_em_dashes_in_bytes(file_bytes, os.path.basename(uploaded_file.name))) | |
| processed = True | |
| # Handle URL | |
| if input_url and input_url.strip(): | |
| url = input_url.strip() | |
| is_github = "github.com" in url | |
| is_hf = "huggingface.co" in url | |
| # Check if it's likely a Repo (GitHub or Hugging Face) | |
| if (is_github or is_hf) and "/archive/" not in url and not any(url.lower().endswith(ext) for ext in ['.pdf', '.txt', '.md', '.py', '.js']): | |
| if is_github: | |
| # Normalize GitHub URL | |
| base_url = url.rstrip('/') | |
| if base_url.endswith('.git'): | |
| base_url = base_url[:-4] | |
| branches = ['main', 'master'] | |
| r = None | |
| for branch in branches: | |
| test_url = f"{base_url}/archive/refs/heads/{branch}.zip" | |
| try: | |
| response = requests.get(test_url, timeout=20) | |
| if response.status_code == 200: | |
| r = response | |
| break | |
| except Exception: | |
| continue | |
| if r: | |
| try: | |
| with zipfile.ZipFile(io.BytesIO(r.content)) as z: | |
| for filename in z.namelist(): | |
| if filename.endswith('/'): continue | |
| text_extensions = {'.py', '.md', '.txt', '.js', '.ts', '.html', '.css', '.c', '.cpp', '.h', '.java', '.rs', '.go', '.json', '.yml', '.yaml'} | |
| if any(filename.lower().endswith(ext) for ext in text_extensions): | |
| with z.open(filename) as f: | |
| locations.extend(find_em_dashes_in_bytes(f.read(), filename)) | |
| processed = True | |
| except Exception: | |
| pass | |
| elif is_hf: | |
| try: | |
| # Parse Hugging Face URL to get repo paths | |
| parts = url.split("huggingface.co/")[-1].strip("/").split("/") | |
| if len(parts) >= 2: | |
| if parts[0] in ["datasets", "spaces"]: | |
| repo_path = f"{parts[0]}/{parts[1]}/{parts[2]}" | |
| repo_type = parts[0] | |
| repo_id = f"{parts[1]}/{parts[2]}" | |
| else: | |
| repo_path = f"{parts[0]}/{parts[1]}" | |
| repo_type = "models" | |
| repo_id = repo_path | |
| # Fetch root directory tree via API | |
| api_url = f"https://huggingface.co/api/{repo_type}/{repo_id}/tree/main" | |
| tree_response = requests.get(api_url, timeout=20) | |
| if tree_response.status_code == 200: | |
| text_extensions = {'.py', '.md', '.txt', '.js', '.ts', '.html', '.css', '.c', '.cpp', '.h', '.java', '.rs', '.go', '.json', '.yml', '.yaml'} | |
| for item in tree_response.json(): | |
| if item.get("type") == "file": | |
| filename = item.get("path") | |
| if any(filename.lower().endswith(ext) for ext in text_extensions): | |
| # Fetch raw file content | |
| raw_url = f"https://huggingface.co/{repo_path}/resolve/main/{filename}" | |
| file_resp = requests.get(raw_url, timeout=20) | |
| if file_resp.status_code == 200: | |
| locations.extend(find_em_dashes_in_bytes(file_resp.content, filename)) | |
| processed = True | |
| except Exception: | |
| pass | |
| else: | |
| # Handle as single file URL | |
| try: | |
| response = requests.get(url, timeout=20) | |
| if response.status_code == 200: | |
| filename = url.split('/')[-1] or "file.txt" | |
| locations.extend(find_em_dashes_in_bytes(response.content, filename)) | |
| processed = True | |
| except Exception: | |
| pass | |
| if not processed: | |
| return ( | |
| "I could not find anything to count! Provide a valid URL or upload a file! Ah ah ah!", | |
| gr.update(visible=False), | |
| gr.update(value=None, visible=False), | |
| ) | |
| total_count = sum(item["count"] for item in locations) | |
| audit_path = create_audit_markdown(locations) | |
| return get_count_response(total_count), gr.update(visible=True), gr.update(value=audit_path, visible=True) | |
| # Define custom CSS for a Sesame Street / The Count theme | |
| custom_css = """ | |
| body, .gradio-container { background-color: #000000 !important; color: #e0e0e0 !important; font-family: 'Georgia', serif !important; } | |
| .gr-box { background-color: #1a0633 !important; border: 2px solid #4b0082 !important; } | |
| #large-input textarea, #large-input input { | |
| background-color: #2b0b4d !important; | |
| color: #ffffff !important; | |
| font-size: 1.5rem !important; | |
| border: 2px solid #9932cc !important; | |
| } | |
| #large-output textarea, #large-output input { | |
| background-color: #000000 !important; | |
| color: #32cd32 !important; | |
| font-size: 1.8rem !important; | |
| font-weight: bold !important; | |
| border: 3px solid #32cd32 !important; | |
| text-shadow: 2px 2px #1a0633; | |
| } | |
| #large-button { | |
| background-color: #4b0082 !important; | |
| color: #32cd32 !important; | |
| font-size: 1.6rem !important; | |
| font-weight: bold !important; | |
| border: 4px solid #32cd32 !important; | |
| height: 80px !important; | |
| box-shadow: 0 0 10px #4b0082; | |
| transition: all 0.3s ease; | |
| cursor: pointer; | |
| } | |
| #large-button:hover { | |
| background-color: #9932cc !important; | |
| color: #ffffff !important; | |
| box-shadow: 0 0 20px #32cd32; | |
| transform: scale(1.02); | |
| } | |
| .gr-form label span { | |
| font-size: 1.4rem !important; | |
| color: #9932cc !important; | |
| font-weight: bold !important; | |
| text-transform: uppercase; | |
| letter-spacing: 2px; | |
| } | |
| h1 { color: #9932cc !important; text-shadow: 2px 2px #000000 !important; font-size: 3rem !important; text-align: center !important; } | |
| h3 { color: #e0e0e0 !important; text-align: center !important; margin-bottom: 2rem !important; } | |
| .file-upload { background-color: #2b0b4d !important; border: 2px dashed #9932cc !important; } | |
| """ | |
| with gr.Blocks(title="The Count's Em Dash Counter") as demo: | |
| gr.Markdown("# 🧛♂️ The Count's Em Dash Counter") | |
| gr.Markdown("### Provide a GitHub repo, a file URL, or upload documents to count em dashes (—)! Ah ah ah!") | |
| with gr.Row(): | |
| with gr.Column(scale=4): | |
| repo_url = gr.Textbox( | |
| label="GitHub or File URL", | |
| placeholder="https://github.com/... OR https://example.com/file.pdf", | |
| lines=1, | |
| elem_id="large-input" | |
| ) | |
| file_upload = gr.File( | |
| label="Upload Documents (PDF, TXT, MD...)", | |
| file_types=[".pdf", ".txt", ".md", ".py", ".js", ".ts", ".html", ".css", ".json"], | |
| elem_classes="file-upload" | |
| ) | |
| count_btn = gr.Button("Count them! Ah ah ah!", variant="primary", elem_id="large-button") | |
| result_text = gr.Textbox( | |
| label="The Count Says:", | |
| interactive=False, | |
| elem_id="large-output", | |
| lines=3 | |
| ) | |
| with gr.Row(): | |
| gr.Markdown("Download a Markdown location report with audit-only instructions for an LLM agent.") | |
| audit_download = gr.DownloadButton( | |
| "DOWNLOAD", | |
| visible=False, | |
| variant="secondary", | |
| ) | |
| with gr.Column(scale=5): | |
| video = gr.Video( | |
| value=VIDEO_URL, | |
| label="The Count", | |
| autoplay=True, | |
| loop=True, | |
| show_label=False, | |
| interactive=False, | |
| visible=False | |
| ) | |
| count_btn.click( | |
| fn=process_input, | |
| inputs=[repo_url, file_upload], | |
| outputs=[result_text, video, audit_download] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(css=custom_css) | |