| """Gradio UI for Hugging Face Spaces — same behavior as index.html.""" | |
| import html | |
| from pathlib import Path | |
| import gradio as gr | |
| from fastapi import HTTPException | |
| from main import ( | |
| _extensions_in_tree, | |
| fetch_repo_data_github, | |
| fetch_repo_data_lcoaldisk, | |
| llm_structure_from_tree, | |
| llm_summary_from_code, | |
| llm_technologies_from_requirements, | |
| parse_github_url, | |
| ) | |
| def _error_message(detail: str | list | dict) -> str: | |
| if isinstance(detail, str): | |
| return detail | |
| if isinstance(detail, list): | |
| parts = [] | |
| for item in detail: | |
| if isinstance(item, dict) and "msg" in item: | |
| loc = item.get("loc", []) | |
| parts.append(f"{'.'.join(str(x) for x in loc)}: {item['msg']}") | |
| else: | |
| parts.append(str(item)) | |
| return "; ".join(parts) if parts else str(detail) | |
| return str(detail) | |
| def _technologies_html(technologies: list[str]) -> str: | |
| if not technologies: | |
| return "" | |
| tags = "".join( | |
| f'<span style="display:inline-block;padding:0.25rem 0.5rem;margin:0.15rem;' | |
| f'font-size:0.85rem;background:#e8edf2;border-radius:4px;">' | |
| f"{html.escape(str(t))}</span>" | |
| for t in technologies | |
| ) | |
| return f'<div style="display:flex;flex-wrap:wrap;gap:0.35rem;">{tags}</div>' | |
| def _error_html(message: str) -> str: | |
| return f'<p style="color:#cf2222;margin:0;">{html.escape(message)}</p>' | |
| async def summarize(repo_source: str) -> tuple[str, str, str, str]: | |
| """Run /summarize logic. Returns (summary, technologies_html, structure, error_html).""" | |
| repo_source = (repo_source or "").strip() | |
| if not repo_source: | |
| return "", "", "", _error_html("repo_source is required") | |
| try: | |
| if "github.com" in repo_source: | |
| owner, repo = parse_github_url(repo_source) | |
| code_context, requirements_content, directory_tree, files_included = ( | |
| await fetch_repo_data_github(owner, repo) | |
| ) | |
| else: | |
| path = Path(repo_source).expanduser().resolve() | |
| if not path.exists() or not path.is_dir(): | |
| return ( | |
| "", | |
| "", | |
| "", | |
| _error_html( | |
| "repo_source must be either a valid GitHub URL " | |
| "or an existing local directory path" | |
| ), | |
| ) | |
| code_context, requirements_content, directory_tree, files_included = ( | |
| await fetch_repo_data_lcoaldisk(str(path)) | |
| ) | |
| if files_included == 0: | |
| return ( | |
| "", | |
| "", | |
| "", | |
| _error_html("No readable text files found in the repository."), | |
| ) | |
| extensions_in_project = _extensions_in_tree(directory_tree) | |
| summary_text = await llm_summary_from_code(code_context) | |
| technologies_list = await llm_technologies_from_requirements( | |
| requirements_content, extensions_in_project | |
| ) | |
| structure_text = await llm_structure_from_tree(directory_tree) | |
| return summary_text, _technologies_html(technologies_list), structure_text, "" | |
| except HTTPException as exc: | |
| return "", "", "", _error_html(_error_message(exc.detail)) | |
| except ValueError as exc: | |
| return "", "", "", _error_html(str(exc)) | |
| except Exception as exc: | |
| return "", "", "", _error_html(str(exc) or "Internal server error") | |
| with gr.Blocks(title="Repo Summarizer") as demo: | |
| gr.Markdown("# Repo Summarizer") | |
| repo_input = gr.Textbox( | |
| label="GitHub URL/Local Path Repository", | |
| placeholder="https://github.com/owner/repo or /path/to/repo", | |
| ) | |
| submit_btn = gr.Button("Summarize", variant="primary") | |
| with gr.Column(): | |
| error_out = gr.HTML("") | |
| gr.Markdown("### Summary") | |
| summary_out = gr.Markdown("") | |
| gr.Markdown("### Technologies") | |
| technologies_out = gr.HTML("") | |
| gr.Markdown("### Structure") | |
| structure_out = gr.Markdown("") | |
| async def run_and_show(repo_source: str): | |
| summary, tech_html, structure, err_html = await summarize(repo_source) | |
| return err_html, summary, tech_html, structure | |
| outputs = [error_out, summary_out, technologies_out, structure_out] | |
| submit_btn.click( | |
| fn=run_and_show, | |
| inputs=[repo_input], | |
| outputs=outputs, | |
| show_progress=True, | |
| ) | |
| repo_input.submit( | |
| fn=run_and_show, | |
| inputs=[repo_input], | |
| outputs=outputs, | |
| show_progress=True, | |
| ) | |
| demo.queue() | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| ssr_mode=False, | |
| share=False, | |
| ) | |