Spaces:
Sleeping
Sleeping
| """ | |
| Real GitHub repository + code search via GitHub REST API. | |
| No auth required for public repos (60 req/hr). | |
| Set GH_TOKEN or GITHUB_TOKEN env var for 5000 req/hr. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| import time | |
| from typing import Any | |
| import httpx | |
| logger = logging.getLogger("dolor3v.tools.github_search") | |
| _GITHUB_API = "https://api.github.com" | |
| _HEADERS_BASE = { | |
| "Accept": "application/vnd.github+json", | |
| "X-GitHub-Api-Version": "2022-11-28", | |
| "User-Agent": "DOLOR3V-TravelerDev/1.0", | |
| } | |
| def _auth_headers() -> dict[str, str]: | |
| token = os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN", "") | |
| if token: | |
| return {**_HEADERS_BASE, "Authorization": f"Bearer {token}"} | |
| return dict(_HEADERS_BASE) | |
| async def search_repositories( | |
| query: str, | |
| sort: str = "stars", | |
| order: str = "desc", | |
| per_page: int = 10, | |
| ) -> dict[str, Any]: | |
| """Search GitHub repositories. Returns real API data.""" | |
| url = f"{_GITHUB_API}/search/repositories" | |
| params = {"q": query, "sort": sort, "order": order, "per_page": per_page} | |
| t0 = time.monotonic() | |
| async with httpx.AsyncClient(timeout=15.0) as client: | |
| resp = await client.get(url, params=params, headers=_auth_headers()) | |
| latency_ms = round((time.monotonic() - t0) * 1000) | |
| if resp.status_code == 403: | |
| remaining = resp.headers.get("X-RateLimit-Remaining", "?") | |
| reset = resp.headers.get("X-RateLimit-Reset", "?") | |
| return { | |
| "error": "GitHub rate limit exceeded", | |
| "remaining": remaining, | |
| "reset_at": reset, | |
| "tip": "Set GH_TOKEN or GITHUB_TOKEN env var for 5000 req/hr", | |
| } | |
| if resp.status_code != 200: | |
| return { | |
| "error": f"GitHub API returned {resp.status_code}", | |
| "body": resp.text[:500], | |
| } | |
| data = resp.json() | |
| items = data.get("items", []) | |
| results = [] | |
| for item in items: | |
| results.append({ | |
| "name": item.get("full_name"), | |
| "description": item.get("description"), | |
| "stars": item.get("stargazers_count"), | |
| "forks": item.get("forks_count"), | |
| "language": item.get("language"), | |
| "url": item.get("html_url"), | |
| "clone_url": item.get("clone_url"), | |
| "topics": item.get("topics", []), | |
| "updated_at": item.get("updated_at"), | |
| "open_issues": item.get("open_issues_count"), | |
| }) | |
| return { | |
| "query": query, | |
| "total_count": data.get("total_count", 0), | |
| "returned": len(results), | |
| "latency_ms": latency_ms, | |
| "results": results, | |
| "rate_limit_remaining": resp.headers.get("X-RateLimit-Remaining", "unknown"), | |
| } | |
| async def search_code( | |
| query: str, | |
| per_page: int = 10, | |
| ) -> dict[str, Any]: | |
| """Search GitHub code. Requires GH_TOKEN for reliable access.""" | |
| url = f"{_GITHUB_API}/search/code" | |
| params = {"q": query, "per_page": per_page} | |
| token = os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN", "") | |
| if not token: | |
| return { | |
| "error": "Code search requires authentication", | |
| "tip": "Set GH_TOKEN env var", | |
| } | |
| t0 = time.monotonic() | |
| async with httpx.AsyncClient(timeout=15.0) as client: | |
| resp = await client.get(url, params=params, headers=_auth_headers()) | |
| latency_ms = round((time.monotonic() - t0) * 1000) | |
| if resp.status_code != 200: | |
| return {"error": f"GitHub code search returned {resp.status_code}", "body": resp.text[:500]} | |
| data = resp.json() | |
| items = data.get("items", []) | |
| return { | |
| "query": query, | |
| "total_count": data.get("total_count", 0), | |
| "latency_ms": latency_ms, | |
| "results": [ | |
| { | |
| "name": i.get("name"), | |
| "path": i.get("path"), | |
| "repo": i.get("repository", {}).get("full_name"), | |
| "url": i.get("html_url"), | |
| "sha": i.get("sha"), | |
| } | |
| for i in items | |
| ], | |
| } | |