File size: 13,761 Bytes
59c2194 691964f 59c2194 691964f 59c2194 691964f 59c2194 691964f 59c2194 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
import gradio as gr # type:ignore
import os
from core.github_client import fetch_beginner_issues
from core.llm_handler import get_simple_issue_suggestion, plan_onboarding_kit_components
from core.kit_generator import generate_kit_from_plan
import utils.config_loader
CURATED_TOPIC_SLUGS = sorted(list(set([
"javascript", "css", "config", "python", "html", "cli", "typescript", "tailwindcss", "github config", "llm", # "github config"
"deep neural networks", "deep learning", "neural network", # Changed to spaces
"tensorflow", "pytorch", "ml",
"distributed systems", # Changed to spaces
"web development", "mobile development", "game development", "machine learning",
"data science", "artificial intelligence", "devops", "cybersecurity", "blockchain",
"iot", "cloud computing", "big data", "robotics", "bioinformatics", "ar vr", # "ar vr" might be better as "augmented reality", "virtual reality" or keep as is if it's a common tag
"natural language processing", "computer vision", "data visualization",
"react", "angular", "vue", "nextjs", "nodejs", "svelte",
"django", "flask", "spring", "dotnet", "ruby on rails", # "ruby on rails"
"android", "ios", "flutter", "react native", # "react native"
"scikit learn", "keras", "pandas", "numpy", # "scikit learn"
"docker", "kubernetes", "aws", "azure", "google cloud platform", "serverless", # "google cloud platform"
"sql", "nosql", "mongodb", "postgresql", "mysql", "graphql",
"api", "gui", "testing", "documentation", "education", "accessibility",
"raspberry pi", "arduino", "linux", "windows", "macos", "gaming", "graphics", "fintech" # "raspberry pi"
])))
CURATED_LANGUAGE_SLUGS = sorted([
"python", "javascript", "java", "c#", "c++", "c", "go", "rust", "ruby", "php",
"swift", "kotlin", "typescript", "html", "css", "sql", "r", "perl", "scala",
"haskell", "lua", "dart", "elixir", "clojure", "objective-c", "shell", "powershell",
"assembly", "matlab", "groovy", "julia", "ocaml", "pascal", "fortran", "lisp",
"prolog", "erlang", "f#", "zig", "nim", "crystal", "svelte", "vue" # Svelte/Vue also as languages for their specific file types
])
def find_and_suggest_issues(
selected_language: str | None, # From language dropdown
selected_curated_topics: list[str] | None, # From topics dropdown (multiselect)
custom_topics_str: str | None # From topics textbox
):
print(f"Gradio app received language: '{selected_language}', curated_topics: {selected_curated_topics}, custom_topics: '{custom_topics_str}'")
# issues_markdown, llm_suggestion, raw_issues_state,
# dropdown_update, button_update, controls_section_update, display_section_update,
# language_searched_state
empty_error_return = (
"Error or no input.", None, None,
gr.update(choices=[], value=None, visible=False), gr.update(visible=False),
gr.update(visible=False), gr.update(visible=False),
"" # language_searched_state
)
no_issues_found_return_factory = lambda lang, topics_str: (
f"No beginner-friendly issues found for '{lang}'" +
(f" with topics '{topics_str}'" if topics_str else "") +
" using current labels. Try different criteria.",
None, None,
gr.update(choices=[], value=None, visible=False), gr.update(visible=False),
gr.update(visible=False), gr.update(visible=False),
lang or ""
)
if not selected_language: # Language is now from a dropdown, should always have a value if user interacts
return ("Please select a programming language.", None, None,
gr.update(choices=[], value=None, visible=False), gr.update(visible=False),
gr.update(visible=False), gr.update(visible=False),
"")
language_to_search = selected_language.strip().lower() # Already a slug from dropdown
# --- Combine curated and custom topics ---
final_topics_set = set()
if selected_curated_topics: # This will be a list from multiselect dropdown
for topic in selected_curated_topics:
if topic and topic.strip():
final_topics_set.add(topic.strip().lower()) # Already slugs
if custom_topics_str:
custom_topics_list = [ct.strip().lower() for ct in custom_topics_str.split(',') if ct.strip()]
for topic in custom_topics_list:
final_topics_set.add(topic) # Add directly, github_client handles quoting if needed
final_topics_list = list(final_topics_set) if final_topics_set else None
print(f"Final parsed topics for search: {final_topics_list}")
fetched_issues_list = fetch_beginner_issues(
language_to_search,
topics=final_topics_list,
per_page=5 # Fetch 5 issues
)
if fetched_issues_list is None: # GitHub API call failed
return ("Error: Could not fetch issues from GitHub. Check server logs.", None, None,
gr.update(choices=[], value=None, visible=False), gr.update(visible=False),
gr.update(visible=False), gr.update(visible=False),
language_to_search)
if not fetched_issues_list: # No issues found
return no_issues_found_return_factory(language_to_search, ", ".join(final_topics_list) if final_topics_list else None)
issues_display_list = []
issue_titles_for_dropdown = []
for i, issue in enumerate(fetched_issues_list[:5]):
title = issue.get('title', 'N/A')
issues_display_list.append(
f"{i+1}. **{title}**\n"
f" - Repo: [{issue.get('repository_html_url', '#')}]({issue.get('repository_html_url', '#')})\n"
f" - URL: [{issue.get('html_url', '#')}]({issue.get('html_url', '#')})\n"
f" - Labels: {', '.join(issue.get('labels', []))}\n"
)
issue_titles_for_dropdown.append(f"{i+1}. {title}")
issues_markdown = "\n---\n".join(issues_display_list)
issues_for_llm = fetched_issues_list[:3]
llm_suggestion_text = "Could not get LLM suggestion at this moment."
if issues_for_llm and utils.config_loader.OPENAI_API_KEY:
suggestion = get_simple_issue_suggestion( # Pass language_to_search
issues_for_llm, language_to_search, target_count=1
# additional_prompt_context for uncommon language is removed
)
if suggestion: llm_suggestion_text = f"**🤖 AI Navigator's Suggestion:**\n\n{suggestion}"
else: llm_suggestion_text = "LLM processed the request but gave an empty response or an error occurred."
elif not utils.config_loader.OPENAI_API_KEY:
llm_suggestion_text = "OpenAI API Key not configured. LLM suggestion skipped."
elif not issues_for_llm :
llm_suggestion_text = "No issues were available to provide a suggestion for."
kit_dropdown_update = gr.update(choices=issue_titles_for_dropdown, value=issue_titles_for_dropdown[0] if issue_titles_for_dropdown else None, visible=True)
kit_button_visibility_update = gr.update(visible=True)
kit_controls_section_update = gr.update(visible=True)
kit_display_section_update = gr.update(visible=True)
return (issues_markdown, llm_suggestion_text, fetched_issues_list,
kit_dropdown_update, kit_button_visibility_update,
kit_controls_section_update, kit_display_section_update,
language_to_search) # Return the searched language for state
def handle_kit_generation(selected_issue_title_with_num: str, current_issues_state: list[dict], language_searched_state: str ):
checklist_update_on_error = gr.update(value=[], visible=False)
if not selected_issue_title_with_num or not current_issues_state:
return "Please select an issue first...", checklist_update_on_error
if not language_searched_state:
language_searched_state = "the project's primary language"
selected_issue_obj = None
try:
for i, issue_in_state in enumerate(current_issues_state):
numbered_title_in_state = f"{i+1}. {issue_in_state.get('title', 'N/A')}"
if numbered_title_in_state == selected_issue_title_with_num:
selected_issue_obj = issue_in_state
break
if not selected_issue_obj:
return f"Error: Could not find data for issue '{selected_issue_title_with_num}'.", checklist_update_on_error
plan_response = plan_onboarding_kit_components(selected_issue_obj, language_searched_state)
if not plan_response or "error" in plan_response:
error_detail = plan_response.get("details", "") if plan_response else "Planner None"
return f"Error planning kit: {plan_response.get('error', 'Unknown')}. {error_detail}", checklist_update_on_error
components_to_include = plan_response.get("include_components", [])
if not components_to_include:
return "AI planner decided no kit components needed.", checklist_update_on_error
kit_markdown_content = generate_kit_from_plan(selected_issue_obj, language_searched_state, components_to_include)
checklist_update_on_success = gr.update(value=[], visible=True)
return kit_markdown_content, checklist_update_on_success
except Exception as e:
import traceback
traceback.print_exc()
return f"Unexpected error generating kit: {str(e)}", checklist_update_on_error
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🤖 ContribNavigator: Your AI Guide to Open Source Contributions")
gr.Markdown("Select a programming language and optional topics to find beginner-friendly open source issues.") # MODIFIED
with gr.Row():
with gr.Column(scale=1): # Input column
lang_dropdown_input = gr.Dropdown(
label="Programming Language (*)",
choices=CURATED_LANGUAGE_SLUGS,
value=CURATED_LANGUAGE_SLUGS[CURATED_LANGUAGE_SLUGS.index("python")] if "python" in CURATED_LANGUAGE_SLUGS else CURATED_LANGUAGE_SLUGS[0] if CURATED_LANGUAGE_SLUGS else None, # Default to python or first in list
interactive=True,
)
curated_topics_dropdown = gr.Dropdown(
label="Select Common Topics (Optional, Multi-Select)",
choices=CURATED_TOPIC_SLUGS,
multiselect=True,
interactive=True
)
custom_topics_input = gr.Textbox(
label="Or, Add Custom Topics (Optional, comma-separated slugs)",
placeholder="e.g., my-niche-topic, another-custom-tag"
)
find_button = gr.Button("🔍 Find Beginner Issues", variant="primary")
with gr.Column(visible=False) as kit_controls_section:
selected_issue_dropdown = gr.Dropdown(
label="Select an Issue to Generate Kit:", choices=[], interactive=True, visible=True
)
generate_kit_button = gr.Button("🛠️ Generate Onboarding Kit", visible=False)
with gr.Column(scale=2): # Output column
gr.Markdown("## Recommended Issues:")
issues_output = gr.Markdown(value="Your recommended issues will appear here...")
gr.Markdown("## Navigator's Insights:")
llm_suggestion_output = gr.Markdown(value="AI-powered suggestions will appear here...")
with gr.Column(visible=False) as kit_display_section:
gr.Markdown("## 📖 Your Onboarding Kit:")
kit_output = gr.Markdown("Your onboarding kit will appear here...")
# --- Using INITIAL_CHECKLIST_ITEMS constant for choices ---
INITIAL_CHECKLIST_ITEMS = [
"Understand the Issue: Read the issue description carefully.",
"Explore the Repository: Use the 'Quick Look' section in the kit to get familiar.",
"Read Contribution Guidelines: Review the project's contribution rules and setup (see kit).",
"Clone the Repository: Get the code on your local machine (see kit for command).",
"Set Up Development Environment: Follow any setup instructions in the guidelines.",
"Create a New Branch: For your changes (e.g., `git checkout -b my-fix-for-issue-123`).",
"Make Initial Contact (Optional but good): Leave a comment on the GitHub issue expressing your interest.",
"Start Investigating/Coding!",
"Ask Questions: If you're stuck, don't hesitate to ask for help on the issue or project's communication channels."
]
checklist_group_output = gr.CheckboxGroup(
label="✅ Your First Steps Checklist:",
choices=INITIAL_CHECKLIST_ITEMS,
value=[],
interactive=True,
visible=False # Starts hidden
)
raw_issues_state = gr.State([])
language_searched_state = gr.State("")
find_button.click(
fn=find_and_suggest_issues,
inputs=[lang_dropdown_input, curated_topics_dropdown, custom_topics_input], # UPDATED
outputs=[
issues_output, llm_suggestion_output, raw_issues_state,
selected_issue_dropdown, generate_kit_button,
kit_controls_section, kit_display_section,
language_searched_state
]
)
generate_kit_button.click(
fn=handle_kit_generation,
inputs=[selected_issue_dropdown, raw_issues_state, language_searched_state],
outputs=[kit_output, checklist_group_output] # Targets CheckboxGroup
)
if __name__ == "__main__":
print("Launching ContribNavigator Gradio App...")
demo.launch() |