Spaces:
Runtime error
Runtime error
| """GitHub repository inspection utilities.""" | |
| from __future__ import annotations | |
| import re | |
| from pathlib import Path | |
| import httpx | |
| GITHUB_API = "https://api.github.com" | |
| HEADERS = { | |
| "Accept": "application/vnd.github.v3+json", | |
| "User-Agent": "ResearchLink-AI/0.1", | |
| } | |
| def parse_github_url(url: str) -> tuple[str | None, str | None]: | |
| """Extract (owner, repo) from a GitHub URL.""" | |
| match = re.search(r"github\.com[/:]([^/]+)/([^/\s.]+?)(?:\.git)?(?:/|$)", url) | |
| if match: | |
| return match.group(1), match.group(2) | |
| return None, None | |
| def fetch_repo_metadata(owner: str, repo: str) -> dict: | |
| """Fetch basic repo metadata via GitHub API (unauthenticated, rate-limited).""" | |
| url = f"{GITHUB_API}/repos/{owner}/{repo}" | |
| try: | |
| resp = httpx.get(url, headers=HEADERS, timeout=15.0) | |
| if resp.status_code == 200: | |
| return resp.json() | |
| return {"error": f"HTTP {resp.status_code}"} | |
| except Exception as e: | |
| return {"error": str(e)} | |
| def fetch_readme(owner: str, repo: str) -> str | None: | |
| """Fetch the raw README content via GitHub API.""" | |
| url = f"{GITHUB_API}/repos/{owner}/{repo}/readme" | |
| try: | |
| resp = httpx.get(url, headers={**HEADERS, "Accept": "application/vnd.github.raw"}, timeout=15.0) | |
| if resp.status_code == 200: | |
| return resp.text | |
| return None | |
| except Exception: | |
| return None | |
| def fetch_tree(owner: str, repo: str) -> list[dict]: | |
| """Fetch the top-level tree (non-recursive) via GitHub API.""" | |
| url = f"{GITHUB_API}/repos/{owner}/{repo}/git/trees/HEAD" | |
| try: | |
| resp = httpx.get(url, headers=HEADERS, timeout=15.0) | |
| if resp.status_code == 200: | |
| data = resp.json() | |
| return data.get("tree", []) | |
| return [] | |
| except Exception: | |
| return [] | |
| def detect_language_and_frameworks(tree_items: list[dict], readme: str | None) -> dict: | |
| """Heuristically detect language and frameworks from repo tree.""" | |
| files = [item["path"] for item in tree_items if item.get("type") == "blob"] | |
| folders = [item["path"] for item in tree_items if item.get("type") == "tree"] | |
| languages: list[str] = [] | |
| frameworks: list[str] = [] | |
| setup_commands: list[str] = [] | |
| has_tests = False | |
| test_paths: list[str] = [] | |
| ext_map = {".py": "Python", ".js": "JavaScript", ".ts": "TypeScript", ".go": "Go", | |
| ".rs": "Rust", ".cpp": "C++", ".c": "C", ".java": "Java", ".r": "R"} | |
| for f in files: | |
| ext = Path(f).suffix.lower() | |
| if ext in ext_map and ext_map[ext] not in languages: | |
| languages.append(ext_map[ext]) | |
| if "requirements.txt" in files: | |
| setup_commands.append("pip install -r requirements.txt") | |
| if "setup.py" in files or "pyproject.toml" in files: | |
| setup_commands.append("pip install -e .") | |
| if "environment.yml" in files: | |
| setup_commands.append("conda env create -f environment.yml") | |
| if "Makefile" in files: | |
| setup_commands.append("make install") | |
| for f in files + folders: | |
| if "test" in f.lower() or "spec" in f.lower(): | |
| has_tests = True | |
| test_paths.append(f) | |
| text_to_check = " ".join(files + folders + ([readme] if readme else [])) | |
| fw_hints = { | |
| "PyTorch": ["torch", "pytorch"], | |
| "TensorFlow": ["tensorflow", "tf"], | |
| "JAX": ["jax", "flax"], | |
| "Gymnasium": ["gymnasium", "gym"], | |
| "RLlib": ["rllib"], | |
| "Stable-Baselines3": ["stable_baselines", "sb3"], | |
| "FastAPI": ["fastapi"], | |
| "Flask": ["flask"], | |
| "NumPy": ["numpy"], | |
| } | |
| for fw, hints in fw_hints.items(): | |
| if any(h in text_to_check.lower() for h in hints): | |
| frameworks.append(fw) | |
| return { | |
| "languages": languages, | |
| "frameworks": frameworks, | |
| "setup_commands": setup_commands, | |
| "has_tests": has_tests, | |
| "test_paths": test_paths[:10], | |
| } | |