Skip to content

Commit 1563a68

Browse files
fix(streamlit): migrate deprecated html embeds and harden AI roast flow
- Replace deprecated st.components.v1.html usage with iframe-based renderer - Remove obsolete components import - Reuse loaded profile data in AI roast to avoid secondary 403 failures - Normalize roast profile payload shape for language data - Add IssueCommentEvent and PullRequestReviewCommentEvent to known event types
1 parent e79744f commit 1563a68

3 files changed

Lines changed: 52 additions & 13 deletions

File tree

‎app.py‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
# HEX color regex validation pattern
88
HEX_COLOR_REGEX = re.compile(r'^#[0-9A-Fa-f]{6}$')
9-
import streamlit.components.v1 as components
109
from dotenv import load_dotenv
1110
from config.settings import get_settings
1211
from roast_widget_streamlit import render_roast_widget
@@ -413,6 +412,12 @@ def show_code_area(code_content, label="Markdown Code"):
413412
st.markdown(f"**{label}** (Copy below)")
414413
st.text_area(label, value=code_content, height=100, label_visibility="collapsed")
415414

415+
416+
def render_embedded_html(html_content: str, *, height: int) -> None:
417+
"""Render inline HTML via iframe to avoid deprecated st.components.v1.html."""
418+
html_b64 = base64.b64encode(html_content.encode("utf-8")).decode("ascii")
419+
st.iframe(f"data:text/html;base64,{html_b64}", height=height, scrolling=False)
420+
416421
def render_tab(svg_bytes, endpoint, username, selected_theme, custom_colors, hide_params=None, code_template=None, excluded_languages=None, output_format="Markdown", font_override=None, extra_params=None):
417422
col1, col2 = st.columns([1.5, 1])
418423
with col1:
@@ -433,7 +438,7 @@ def render_tab(svg_bytes, endpoint, username, selected_theme, custom_colors, hid
433438
svg_b64 = base64.b64encode(svg_bytes.encode("utf-8")).decode("utf-8")
434439
filename_prefix_safe = json.dumps(f"{endpoint}_{username}")
435440

436-
components.html(f"""
441+
render_embedded_html(f"""
437442
<div style="display:flex; flex-direction:column; gap:8px; margin-top:4px;">
438443
<button onclick="downloadSVGAs('png')" style="
439444
width:100%; padding:8px; font-size:14px; cursor:pointer;
@@ -619,7 +624,7 @@ def render_tab(svg_bytes, endpoint, username, selected_theme, custom_colors, hid
619624

620625
with spark_col1:
621626
# We wrap it in a component so the animations and styles actually trigger
622-
st.components.v1.html(f"""
627+
render_embedded_html(f"""
623628
<div style="background: {current_theme_opts.get('bg_color', '#0d1117')}; border-radius: 12px; border: 1px solid {current_theme_opts.get('border_color', '#30363d')}; overflow: hidden; position: relative; margin-bottom: 5px;">
624629
<!-- Corner Accents -->
625630
<div style="position: absolute; top: 0; left: 0; width: 10px; height: 10px; border-top: 2px solid {theme_color}; border-left: 2px solid {theme_color};"></div>
@@ -884,7 +889,7 @@ def render_tab(svg_bytes, endpoint, username, selected_theme, custom_colors, hid
884889
st.markdown("Let AI roast your GitHub profile with humor!")
885890

886891
if username:
887-
render_roast_widget(username)
892+
render_roast_widget(username, profile_data=data)
888893
else:
889894
st.warning("Please enter a GitHub username in the sidebar.")
890895

‎roast_widget_streamlit.py‎

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,41 @@
44
"""
55

66
import streamlit as st
7-
import os
7+
from typing import Any
88
from ai.ai_roast_service import generate_profile_roast
99
from utils.github_utils import fetch_github_stats
1010

1111

12-
def render_roast_widget(username: str):
12+
def _normalize_profile_data(profile_data: dict[str, Any] | None, username: str) -> dict[str, Any]:
13+
"""Normalize profile shape so AI roast service always gets expected keys."""
14+
base: dict[str, Any] = {
15+
"username": username,
16+
"public_repos": 0,
17+
"total_commits": 0,
18+
"top_languages": [],
19+
}
20+
if not profile_data:
21+
return base
22+
23+
normalized = dict(base)
24+
normalized["username"] = profile_data.get("username") or profile_data.get("login") or username
25+
normalized["public_repos"] = profile_data.get("public_repos", 0)
26+
normalized["total_commits"] = profile_data.get("total_commits", 0)
27+
28+
raw_langs = profile_data.get("top_languages", [])
29+
langs: list[dict[str, Any]] = []
30+
for entry in raw_langs:
31+
if isinstance(entry, dict):
32+
name = entry.get("name")
33+
if name:
34+
langs.append({"name": name, "count": entry.get("count", 0)})
35+
elif isinstance(entry, (list, tuple)) and entry:
36+
langs.append({"name": str(entry[0]), "count": entry[1] if len(entry) > 1 else 0})
37+
normalized["top_languages"] = langs
38+
return normalized
39+
40+
41+
def render_roast_widget(username: str, profile_data: dict[str, Any] | None = None):
1342
"""
1443
Render the AI Roast widget in Streamlit
1544
@@ -90,15 +119,19 @@ def render_roast_widget(username: str):
90119
if st.button("🎭 Generate Roast", use_container_width=True, type="primary"):
91120
with st.spinner("🔥 Cooking up a roast..."):
92121
try:
93-
# Fetch GitHub stats
94-
profile_data = fetch_github_stats(username)
95-
96-
if profile_data:
122+
# Reuse already loaded profile data first to avoid rate-limit failures.
123+
source_profile = _normalize_profile_data(profile_data, username)
124+
if source_profile.get("total_commits", 0) <= 0 and not source_profile.get("top_languages"):
125+
fetched_profile = fetch_github_stats(username)
126+
if fetched_profile:
127+
source_profile = _normalize_profile_data(fetched_profile, username)
128+
129+
if source_profile:
97130
# Generate roast
98-
roast_result = generate_profile_roast(profile_data)
131+
roast_result = generate_profile_roast(source_profile)
99132
st.session_state.roast_data = {
100133
'roast': roast_result['roast'],
101-
'profile': profile_data,
134+
'profile': source_profile,
102135
'source': roast_result['source']
103136
}
104137
else:

‎utils/api_validators.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ class GitHubEvent(BaseModel):
8989
def validate_type(cls, v):
9090
allowed_types = {
9191
'PullRequestEvent', 'IssuesEvent', 'PushEvent', 'CreateEvent',
92-
'DeleteEvent', 'ForkEvent', 'WatchEvent', 'ReleaseEvent'
92+
'DeleteEvent', 'ForkEvent', 'WatchEvent', 'ReleaseEvent',
93+
'IssueCommentEvent', 'PullRequestReviewCommentEvent'
9394
}
9495
if v not in allowed_types:
9596
# Log unknown type but don't fail validation

0 commit comments

Comments
 (0)