Upload 4 files
Browse files- app.py +3 -2
- github_utils.py +46 -11
- services.py +9 -3
app.py
CHANGED
|
@@ -877,7 +877,8 @@ async def generate_campaign(repo_url: str, time_range: str, anthropic_key: str,
|
|
| 877 |
)
|
| 878 |
return
|
| 879 |
|
| 880 |
-
#
|
|
|
|
| 881 |
custom_anthropic_key = anthropic_key.strip() if anthropic_key else None
|
| 882 |
custom_github_token = github_token.strip() if github_token else None
|
| 883 |
|
|
@@ -1690,4 +1691,4 @@ if __name__ == "__main__":
|
|
| 1690 |
share=False,
|
| 1691 |
show_error=True,
|
| 1692 |
ssr_mode=False, # Disable experimental SSR to avoid potential issues
|
| 1693 |
-
)
|
|
|
|
| 877 |
)
|
| 878 |
return
|
| 879 |
|
| 880 |
+
# API key priority: Environment variables (HF Spaces secrets) > User input
|
| 881 |
+
# This allows HF Spaces to use pre-configured secrets while still allowing user override
|
| 882 |
custom_anthropic_key = anthropic_key.strip() if anthropic_key else None
|
| 883 |
custom_github_token = github_token.strip() if github_token else None
|
| 884 |
|
|
|
|
| 1691 |
share=False,
|
| 1692 |
show_error=True,
|
| 1693 |
ssr_mode=False, # Disable experimental SSR to avoid potential issues
|
| 1694 |
+
)
|
github_utils.py
CHANGED
|
@@ -24,7 +24,12 @@ def get_since_iso(time_range: str) -> str:
|
|
| 24 |
|
| 25 |
|
| 26 |
async def fetch_github_repo(repo_url: str, github_token: Optional[str] = None) -> dict:
|
| 27 |
-
"""Fetch repository metadata from GitHub.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 29 |
if not match:
|
| 30 |
raise ValueError("Invalid GitHub URL")
|
|
@@ -32,7 +37,8 @@ async def fetch_github_repo(repo_url: str, github_token: Optional[str] = None) -
|
|
| 32 |
owner, repo = match.groups()
|
| 33 |
repo_name = repo.rstrip(".git")
|
| 34 |
|
| 35 |
-
|
|
|
|
| 36 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 37 |
if token:
|
| 38 |
headers["Authorization"] = f"token {token}"
|
|
@@ -105,7 +111,12 @@ async def fetch_recent_commits(
|
|
| 105 |
until_iso: Optional[str] = None,
|
| 106 |
github_token: Optional[str] = None,
|
| 107 |
) -> list[dict]:
|
| 108 |
-
"""Fetch recent commits from a GitHub repository.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 110 |
if not match:
|
| 111 |
raise ValueError("Invalid GitHub URL")
|
|
@@ -113,7 +124,8 @@ async def fetch_recent_commits(
|
|
| 113 |
owner, repo = match.groups()
|
| 114 |
repo_name = repo.rstrip(".git")
|
| 115 |
|
| 116 |
-
|
|
|
|
| 117 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 118 |
if token:
|
| 119 |
headers["Authorization"] = f"token {token}"
|
|
@@ -184,7 +196,12 @@ async def fetch_commit_diff(
|
|
| 184 |
commit_sha: str,
|
| 185 |
github_token: Optional[str] = None,
|
| 186 |
) -> dict:
|
| 187 |
-
"""Fetch the diff for a specific commit.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 189 |
if not match:
|
| 190 |
raise ValueError("Invalid GitHub URL")
|
|
@@ -192,7 +209,8 @@ async def fetch_commit_diff(
|
|
| 192 |
owner, repo = match.groups()
|
| 193 |
repo_name = repo.rstrip(".git")
|
| 194 |
|
| 195 |
-
|
|
|
|
| 196 |
headers = {"Accept": "application/vnd.github.v3.diff"}
|
| 197 |
if token:
|
| 198 |
headers["Authorization"] = f"token {token}"
|
|
@@ -238,7 +256,12 @@ async def fetch_readme_at_commit(
|
|
| 238 |
commit_sha: str,
|
| 239 |
github_token: Optional[str] = None,
|
| 240 |
) -> str:
|
| 241 |
-
"""Fetch README content at a specific commit.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 243 |
if not match:
|
| 244 |
raise ValueError("Invalid GitHub URL")
|
|
@@ -246,7 +269,8 @@ async def fetch_readme_at_commit(
|
|
| 246 |
owner, repo = match.groups()
|
| 247 |
repo_name = repo.rstrip(".git")
|
| 248 |
|
| 249 |
-
|
|
|
|
| 250 |
headers = {"Accept": "application/vnd.github.raw"}
|
| 251 |
if token:
|
| 252 |
headers["Authorization"] = f"token {token}"
|
|
@@ -274,7 +298,12 @@ async def fetch_first_commit_sha(
|
|
| 274 |
repo_url: str,
|
| 275 |
github_token: Optional[str] = None,
|
| 276 |
) -> Optional[str]:
|
| 277 |
-
"""Fetch the SHA of the first (oldest) commit in the repository.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 279 |
if not match:
|
| 280 |
raise ValueError("Invalid GitHub URL")
|
|
@@ -282,7 +311,8 @@ async def fetch_first_commit_sha(
|
|
| 282 |
owner, repo = match.groups()
|
| 283 |
repo_name = repo.rstrip(".git")
|
| 284 |
|
| 285 |
-
|
|
|
|
| 286 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 287 |
if token:
|
| 288 |
headers["Authorization"] = f"token {token}"
|
|
@@ -360,6 +390,10 @@ async def fetch_readme_changes(
|
|
| 360 |
Fetch README changes: initial README and diff within the time period.
|
| 361 |
Returns both the original README (for project understanding) and
|
| 362 |
any README changes during the analysis period (highest priority for DevRel).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
"""
|
| 364 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 365 |
if not match:
|
|
@@ -368,7 +402,8 @@ async def fetch_readme_changes(
|
|
| 368 |
owner, repo = match.groups()
|
| 369 |
repo_name = repo.rstrip(".git")
|
| 370 |
|
| 371 |
-
|
|
|
|
| 372 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 373 |
if token:
|
| 374 |
headers["Authorization"] = f"token {token}"
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
async def fetch_github_repo(repo_url: str, github_token: Optional[str] = None) -> dict:
|
| 27 |
+
"""Fetch repository metadata from GitHub.
|
| 28 |
+
|
| 29 |
+
Priority for GitHub token:
|
| 30 |
+
1. Environment variable GITHUB_TOKEN (for Hugging Face Spaces secrets)
|
| 31 |
+
2. User-provided github_token parameter (for manual override)
|
| 32 |
+
"""
|
| 33 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 34 |
if not match:
|
| 35 |
raise ValueError("Invalid GitHub URL")
|
|
|
|
| 37 |
owner, repo = match.groups()
|
| 38 |
repo_name = repo.rstrip(".git")
|
| 39 |
|
| 40 |
+
# Environment variable takes priority (for HF Spaces secrets)
|
| 41 |
+
token = os.environ.get("GITHUB_TOKEN") or github_token
|
| 42 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 43 |
if token:
|
| 44 |
headers["Authorization"] = f"token {token}"
|
|
|
|
| 111 |
until_iso: Optional[str] = None,
|
| 112 |
github_token: Optional[str] = None,
|
| 113 |
) -> list[dict]:
|
| 114 |
+
"""Fetch recent commits from a GitHub repository.
|
| 115 |
+
|
| 116 |
+
Priority for GitHub token:
|
| 117 |
+
1. Environment variable GITHUB_TOKEN (for Hugging Face Spaces secrets)
|
| 118 |
+
2. User-provided github_token parameter (for manual override)
|
| 119 |
+
"""
|
| 120 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 121 |
if not match:
|
| 122 |
raise ValueError("Invalid GitHub URL")
|
|
|
|
| 124 |
owner, repo = match.groups()
|
| 125 |
repo_name = repo.rstrip(".git")
|
| 126 |
|
| 127 |
+
# Environment variable takes priority (for HF Spaces secrets)
|
| 128 |
+
token = os.environ.get("GITHUB_TOKEN") or github_token
|
| 129 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 130 |
if token:
|
| 131 |
headers["Authorization"] = f"token {token}"
|
|
|
|
| 196 |
commit_sha: str,
|
| 197 |
github_token: Optional[str] = None,
|
| 198 |
) -> dict:
|
| 199 |
+
"""Fetch the diff for a specific commit.
|
| 200 |
+
|
| 201 |
+
Priority for GitHub token:
|
| 202 |
+
1. Environment variable GITHUB_TOKEN (for Hugging Face Spaces secrets)
|
| 203 |
+
2. User-provided github_token parameter (for manual override)
|
| 204 |
+
"""
|
| 205 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 206 |
if not match:
|
| 207 |
raise ValueError("Invalid GitHub URL")
|
|
|
|
| 209 |
owner, repo = match.groups()
|
| 210 |
repo_name = repo.rstrip(".git")
|
| 211 |
|
| 212 |
+
# Environment variable takes priority (for HF Spaces secrets)
|
| 213 |
+
token = os.environ.get("GITHUB_TOKEN") or github_token
|
| 214 |
headers = {"Accept": "application/vnd.github.v3.diff"}
|
| 215 |
if token:
|
| 216 |
headers["Authorization"] = f"token {token}"
|
|
|
|
| 256 |
commit_sha: str,
|
| 257 |
github_token: Optional[str] = None,
|
| 258 |
) -> str:
|
| 259 |
+
"""Fetch README content at a specific commit.
|
| 260 |
+
|
| 261 |
+
Priority for GitHub token:
|
| 262 |
+
1. Environment variable GITHUB_TOKEN (for Hugging Face Spaces secrets)
|
| 263 |
+
2. User-provided github_token parameter (for manual override)
|
| 264 |
+
"""
|
| 265 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 266 |
if not match:
|
| 267 |
raise ValueError("Invalid GitHub URL")
|
|
|
|
| 269 |
owner, repo = match.groups()
|
| 270 |
repo_name = repo.rstrip(".git")
|
| 271 |
|
| 272 |
+
# Environment variable takes priority (for HF Spaces secrets)
|
| 273 |
+
token = os.environ.get("GITHUB_TOKEN") or github_token
|
| 274 |
headers = {"Accept": "application/vnd.github.raw"}
|
| 275 |
if token:
|
| 276 |
headers["Authorization"] = f"token {token}"
|
|
|
|
| 298 |
repo_url: str,
|
| 299 |
github_token: Optional[str] = None,
|
| 300 |
) -> Optional[str]:
|
| 301 |
+
"""Fetch the SHA of the first (oldest) commit in the repository.
|
| 302 |
+
|
| 303 |
+
Priority for GitHub token:
|
| 304 |
+
1. Environment variable GITHUB_TOKEN (for Hugging Face Spaces secrets)
|
| 305 |
+
2. User-provided github_token parameter (for manual override)
|
| 306 |
+
"""
|
| 307 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 308 |
if not match:
|
| 309 |
raise ValueError("Invalid GitHub URL")
|
|
|
|
| 311 |
owner, repo = match.groups()
|
| 312 |
repo_name = repo.rstrip(".git")
|
| 313 |
|
| 314 |
+
# Environment variable takes priority (for HF Spaces secrets)
|
| 315 |
+
token = os.environ.get("GITHUB_TOKEN") or github_token
|
| 316 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 317 |
if token:
|
| 318 |
headers["Authorization"] = f"token {token}"
|
|
|
|
| 390 |
Fetch README changes: initial README and diff within the time period.
|
| 391 |
Returns both the original README (for project understanding) and
|
| 392 |
any README changes during the analysis period (highest priority for DevRel).
|
| 393 |
+
|
| 394 |
+
Priority for GitHub token:
|
| 395 |
+
1. Environment variable GITHUB_TOKEN (for Hugging Face Spaces secrets)
|
| 396 |
+
2. User-provided github_token parameter (for manual override)
|
| 397 |
"""
|
| 398 |
match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", repo_url)
|
| 399 |
if not match:
|
|
|
|
| 402 |
owner, repo = match.groups()
|
| 403 |
repo_name = repo.rstrip(".git")
|
| 404 |
|
| 405 |
+
# Environment variable takes priority (for HF Spaces secrets)
|
| 406 |
+
token = os.environ.get("GITHUB_TOKEN") or github_token
|
| 407 |
headers = {"Accept": "application/vnd.github.v3+json"}
|
| 408 |
if token:
|
| 409 |
headers["Authorization"] = f"token {token}"
|
services.py
CHANGED
|
@@ -70,10 +70,16 @@ class ContentVariant:
|
|
| 70 |
# Claude Service
|
| 71 |
class ClaudeService:
|
| 72 |
def __init__(self, api_key: Optional[str] = None):
|
| 73 |
-
"""Initialize Claude service
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
if not self.api_key:
|
| 76 |
-
raise ValueError("ANTHROPIC_API_KEY not configured. Please provide your own API key.")
|
| 77 |
self.client = anthropic.Anthropic(api_key=self.api_key)
|
| 78 |
|
| 79 |
def extract_repo_profile(self, repo_data: dict) -> dict:
|
|
|
|
| 70 |
# Claude Service
|
| 71 |
class ClaudeService:
|
| 72 |
def __init__(self, api_key: Optional[str] = None):
|
| 73 |
+
"""Initialize Claude service.
|
| 74 |
+
|
| 75 |
+
Priority for API key:
|
| 76 |
+
1. Environment variable ANTHROPIC_API_KEY (for Hugging Face Spaces secrets)
|
| 77 |
+
2. User-provided api_key parameter (for manual override)
|
| 78 |
+
"""
|
| 79 |
+
# Environment variable takes priority (for HF Spaces secrets)
|
| 80 |
+
self.api_key = os.environ.get("ANTHROPIC_API_KEY") or api_key
|
| 81 |
if not self.api_key:
|
| 82 |
+
raise ValueError("ANTHROPIC_API_KEY not configured. Please provide your own API key in Settings.")
|
| 83 |
self.client = anthropic.Anthropic(api_key=self.api_key)
|
| 84 |
|
| 85 |
def extract_repo_profile(self, repo_data: dict) -> dict:
|