Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -10,10 +10,10 @@ import plotly.express as px
|
|
10 |
import pandas as pd
|
11 |
|
12 |
# --- Configuration ---
|
13 |
-
MAX_PROMPT_TOKENS_ESTIMATE = 800000 #
|
14 |
RESULTS_PAGE_SIZE = 25
|
15 |
|
16 |
-
AVAILABLE_ANALYSES = {
|
17 |
"generate_docs": "Generate Missing Docstrings/Comments",
|
18 |
"find_bugs": "Identify Potential Bugs & Anti-patterns",
|
19 |
"check_style": "Check Style Guide Compliance (General)",
|
@@ -35,13 +35,9 @@ if 'error_message' not in st.session_state:
|
|
35 |
if 'analysis_requested' not in st.session_state:
|
36 |
st.session_state.analysis_requested = False
|
37 |
if 'selected_model_name' not in st.session_state:
|
38 |
-
st.session_state.selected_model_name = None #
|
39 |
if 'available_models_dict' not in st.session_state:
|
40 |
-
st.session_state.available_models_dict = {} # Mapping
|
41 |
-
if 'file_count' not in st.session_state:
|
42 |
-
st.session_state.file_count = 0
|
43 |
-
if 'total_chars' not in st.session_state:
|
44 |
-
st.session_state.total_chars = 0
|
45 |
|
46 |
# --- Gemini API Setup & Model Discovery ---
|
47 |
model = None # Global variable for the initialized model instance
|
@@ -100,8 +96,8 @@ def initialize_gemini_model():
|
|
100 |
def estimate_token_count(text):
|
101 |
"""
|
102 |
Estimates the token count.
|
103 |
-
If a string is provided, calculates based on length.
|
104 |
-
If an integer
|
105 |
"""
|
106 |
if isinstance(text, int):
|
107 |
return text // 3
|
@@ -160,13 +156,16 @@ def process_zip_file_cached(file_id, file_size, file_content_bytes):
|
|
160 |
st.error(f"π¨ ZIP Error: {e}")
|
161 |
return None, 0, 0, []
|
162 |
if file_count == 0:
|
163 |
-
|
|
|
|
|
|
|
164 |
return code_files, total_chars, file_count, ignored_files
|
165 |
|
166 |
def construct_analysis_prompt(code_files_dict, requested_analyses):
|
167 |
"""
|
168 |
-
Constructs the prompt for analysis by including code files and JSON structure.
|
169 |
-
Returns the full prompt and list of included files.
|
170 |
"""
|
171 |
prompt_parts = ["Analyze the following codebase...\n\n"]
|
172 |
current_token_estimate = estimate_token_count(prompt_parts[0])
|
@@ -218,7 +217,7 @@ def construct_analysis_prompt(code_files_dict, requested_analyses):
|
|
218 |
def call_gemini_api(prompt):
|
219 |
"""
|
220 |
Calls the Gemini API using the provided prompt.
|
221 |
-
Returns parsed JSON insights or an error message.
|
222 |
"""
|
223 |
if not prompt:
|
224 |
return None, "Prompt generation failed."
|
@@ -249,7 +248,7 @@ def call_gemini_api(prompt):
|
|
249 |
generation_config=genai.types.GenerationConfig(temperature=0.2),
|
250 |
safety_settings=[
|
251 |
{"category": c, "threshold": "BLOCK_MEDIUM_AND_ABOVE"}
|
252 |
-
for c in ["HARM_CATEGORY_HARASSMENT", "HARM_CATEGORY_HATE_SPEECH",
|
253 |
"HARM_CATEGORY_SEXUALLY_EXPLICIT", "HARM_CATEGORY_DANGEROUS_CONTENT"]
|
254 |
]
|
255 |
)
|
@@ -308,7 +307,7 @@ def call_gemini_api(prompt):
|
|
308 |
|
309 |
def display_results(results_json, requested_analyses):
|
310 |
"""
|
311 |
-
Displays analysis results with pagination and
|
312 |
"""
|
313 |
st.header("π Analysis Report")
|
314 |
if not isinstance(results_json, dict):
|
@@ -395,7 +394,7 @@ def display_results(results_json, requested_analyses):
|
|
395 |
st.set_page_config(page_title="Codebase Audit Assistant", layout="wide")
|
396 |
st.title("π€ Codebase Audit & Documentation Assistant")
|
397 |
|
398 |
-
# --- Sidebar Enhancements ---
|
399 |
with st.sidebar:
|
400 |
# Dark Mode Toggle
|
401 |
dark_mode = st.checkbox("Enable Dark Mode", value=False)
|
@@ -403,13 +402,14 @@ with st.sidebar:
|
|
403 |
st.markdown(
|
404 |
"""
|
405 |
<style>
|
406 |
-
.reportview-container {
|
407 |
-
|
|
|
|
|
408 |
</style>
|
409 |
""",
|
410 |
unsafe_allow_html=True
|
411 |
)
|
412 |
-
|
413 |
st.header("βοΈ Analysis Controls")
|
414 |
st.session_state.mock_api_call = st.toggle(
|
415 |
"π§ͺ Enable Mock API Mode",
|
@@ -455,7 +455,7 @@ with st.sidebar:
|
|
455 |
st.divider()
|
456 |
st.header("π Select Analyses")
|
457 |
selected_analyses = [
|
458 |
-
key for key, name in AVAILABLE_ANALYSES.items()
|
459 |
if st.checkbox(name, value=True, key=f"cb_{key}")
|
460 |
]
|
461 |
st.divider()
|
@@ -473,7 +473,7 @@ with st.sidebar:
|
|
473 |
st.divider()
|
474 |
st.warning("β οΈ **Privacy:** Code sent to Google API if Mock Mode is OFF.")
|
475 |
|
476 |
-
# Update title based on selected model
|
477 |
if st.session_state.selected_model_name and not st.session_state.mock_api_call:
|
478 |
st.markdown(f"Upload codebase (`.zip`) for analysis via **{st.session_state.selected_model_name}**.")
|
479 |
elif st.session_state.mock_api_call:
|
@@ -503,13 +503,20 @@ if uploaded_file:
|
|
503 |
file_id, uploaded_file.size, uploaded_file_bytes
|
504 |
)
|
505 |
if code_files is not None:
|
506 |
-
# Save these metrics to session state for dashboard use
|
507 |
-
st.session_state.file_count = file_count
|
508 |
-
st.session_state.total_chars = total_chars
|
509 |
st.info(f"Found **{file_count}** code files ({total_chars:,} chars). Est. tokens: ~{estimate_token_count(total_chars):,}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
if ignored_files:
|
511 |
with st.expander(f"View {len(ignored_files)} Skipped/Ignored Files"):
|
512 |
st.code("\n".join(ignored_files), language='text')
|
|
|
513 |
model_ready = bool(st.session_state.selected_model_name) or st.session_state.mock_api_call
|
514 |
analyze_button_disabled = (not selected_analyses or file_count == 0 or not model_ready)
|
515 |
analyze_button_label = "Analyze Codebase"
|
@@ -518,7 +525,11 @@ if uploaded_file:
|
|
518 |
elif analyze_button_disabled:
|
519 |
analyze_button_label = "Select Analyses or Upload Valid Code"
|
520 |
|
521 |
-
if analysis_button_placeholder.button(
|
|
|
|
|
|
|
|
|
522 |
st.session_state.analysis_requested = True
|
523 |
st.session_state.analysis_results = None
|
524 |
st.session_state.error_message = None
|
@@ -530,7 +541,11 @@ if uploaded_file:
|
|
530 |
st.warning("Please select a Gemini model from the sidebar.")
|
531 |
else:
|
532 |
with results_placeholder:
|
533 |
-
spinner_model_name =
|
|
|
|
|
|
|
|
|
534 |
spinner_msg = f"π Preparing prompt & contacting AI ({spinner_model_name})... Please wait."
|
535 |
with st.spinner(spinner_msg):
|
536 |
analysis_prompt, included_files_in_prompt = construct_analysis_prompt(code_files, selected_analyses)
|
@@ -544,7 +559,6 @@ if uploaded_file:
|
|
544 |
st.session_state.error_message = "Failed to generate analysis prompt."
|
545 |
st.rerun()
|
546 |
|
547 |
-
# Display analysis results if available
|
548 |
if st.session_state.analysis_requested:
|
549 |
with results_placeholder:
|
550 |
st.divider()
|
@@ -560,25 +574,5 @@ if st.session_state.analysis_requested:
|
|
560 |
elif not uploaded_file:
|
561 |
results_placeholder.info("Upload a ZIP file to begin.")
|
562 |
|
563 |
-
# --- Audit Dashboard ---
|
564 |
-
# If analysis results exist, display an interactive dashboard summarizing key metrics.
|
565 |
-
if st.session_state.analysis_results:
|
566 |
-
st.subheader("Audit Dashboard")
|
567 |
-
# Compute metrics from session state and analysis results
|
568 |
-
metrics = {
|
569 |
-
"Files Analyzed": st.session_state.file_count,
|
570 |
-
"Total Tokens": estimate_token_count(st.session_state.total_chars)
|
571 |
-
}
|
572 |
-
metrics["Documentation Suggestions"] = len(st.session_state.analysis_results.get("documentation_suggestions", []))
|
573 |
-
metrics["Potential Bugs"] = len(st.session_state.analysis_results.get("potential_bugs", []))
|
574 |
-
metrics["Style Issues"] = len(st.session_state.analysis_results.get("style_issues", []))
|
575 |
-
metrics["Module Summaries"] = len(st.session_state.analysis_results.get("module_summaries", []))
|
576 |
-
metrics["Refactoring Suggestions"] = len(st.session_state.analysis_results.get("refactoring_suggestions", []))
|
577 |
-
|
578 |
-
# Create a DataFrame and a bar chart using Plotly
|
579 |
-
df_metrics = pd.DataFrame(list(metrics.items()), columns=["Metric", "Value"])
|
580 |
-
fig = px.bar(df_metrics, x="Metric", y="Value", title="Audit Summary Metrics")
|
581 |
-
st.plotly_chart(fig)
|
582 |
-
|
583 |
results_placeholder.divider()
|
584 |
results_placeholder.markdown("_Assistant powered by Google Gemini._")
|
|
|
10 |
import pandas as pd
|
11 |
|
12 |
# --- Configuration ---
|
13 |
+
MAX_PROMPT_TOKENS_ESTIMATE = 800000 # Keep this estimate
|
14 |
RESULTS_PAGE_SIZE = 25
|
15 |
|
16 |
+
AVAILABLE_ANALYSES = { # Keep analyses config
|
17 |
"generate_docs": "Generate Missing Docstrings/Comments",
|
18 |
"find_bugs": "Identify Potential Bugs & Anti-patterns",
|
19 |
"check_style": "Check Style Guide Compliance (General)",
|
|
|
35 |
if 'analysis_requested' not in st.session_state:
|
36 |
st.session_state.analysis_requested = False
|
37 |
if 'selected_model_name' not in st.session_state:
|
38 |
+
st.session_state.selected_model_name = None # Will hold the "models/..." name
|
39 |
if 'available_models_dict' not in st.session_state:
|
40 |
+
st.session_state.available_models_dict = {} # Mapping display_name -> name
|
|
|
|
|
|
|
|
|
41 |
|
42 |
# --- Gemini API Setup & Model Discovery ---
|
43 |
model = None # Global variable for the initialized model instance
|
|
|
96 |
def estimate_token_count(text):
|
97 |
"""
|
98 |
Estimates the token count.
|
99 |
+
If a string is provided, calculates based on its length.
|
100 |
+
If an integer (e.g. total char count) is provided, uses that directly.
|
101 |
"""
|
102 |
if isinstance(text, int):
|
103 |
return text // 3
|
|
|
156 |
st.error(f"π¨ ZIP Error: {e}")
|
157 |
return None, 0, 0, []
|
158 |
if file_count == 0:
|
159 |
+
if not ignored_files:
|
160 |
+
st.warning("No code files found.")
|
161 |
+
else:
|
162 |
+
st.warning("No code files found; some skipped.")
|
163 |
return code_files, total_chars, file_count, ignored_files
|
164 |
|
165 |
def construct_analysis_prompt(code_files_dict, requested_analyses):
|
166 |
"""
|
167 |
+
Constructs the prompt for analysis by including code files and a JSON structure for output.
|
168 |
+
Returns the full prompt and a list of included files.
|
169 |
"""
|
170 |
prompt_parts = ["Analyze the following codebase...\n\n"]
|
171 |
current_token_estimate = estimate_token_count(prompt_parts[0])
|
|
|
217 |
def call_gemini_api(prompt):
|
218 |
"""
|
219 |
Calls the Gemini API using the provided prompt.
|
220 |
+
Returns the parsed JSON insights or an error message.
|
221 |
"""
|
222 |
if not prompt:
|
223 |
return None, "Prompt generation failed."
|
|
|
248 |
generation_config=genai.types.GenerationConfig(temperature=0.2),
|
249 |
safety_settings=[
|
250 |
{"category": c, "threshold": "BLOCK_MEDIUM_AND_ABOVE"}
|
251 |
+
for c in ["HARM_CATEGORY_HARASSMENT", "HARM_CATEGORY_HATE_SPEECH",
|
252 |
"HARM_CATEGORY_SEXUALLY_EXPLICIT", "HARM_CATEGORY_DANGEROUS_CONTENT"]
|
253 |
]
|
254 |
)
|
|
|
307 |
|
308 |
def display_results(results_json, requested_analyses):
|
309 |
"""
|
310 |
+
Displays the analysis results with pagination and allows JSON download.
|
311 |
"""
|
312 |
st.header("π Analysis Report")
|
313 |
if not isinstance(results_json, dict):
|
|
|
394 |
st.set_page_config(page_title="Codebase Audit Assistant", layout="wide")
|
395 |
st.title("π€ Codebase Audit & Documentation Assistant")
|
396 |
|
397 |
+
# --- Sidebar with Enhancements ---
|
398 |
with st.sidebar:
|
399 |
# Dark Mode Toggle
|
400 |
dark_mode = st.checkbox("Enable Dark Mode", value=False)
|
|
|
402 |
st.markdown(
|
403 |
"""
|
404 |
<style>
|
405 |
+
.reportview-container, .main {
|
406 |
+
background-color: #2E2E2E;
|
407 |
+
color: white;
|
408 |
+
}
|
409 |
</style>
|
410 |
""",
|
411 |
unsafe_allow_html=True
|
412 |
)
|
|
|
413 |
st.header("βοΈ Analysis Controls")
|
414 |
st.session_state.mock_api_call = st.toggle(
|
415 |
"π§ͺ Enable Mock API Mode",
|
|
|
455 |
st.divider()
|
456 |
st.header("π Select Analyses")
|
457 |
selected_analyses = [
|
458 |
+
key for key, name in AVAILABLE_ANALYSES.items()
|
459 |
if st.checkbox(name, value=True, key=f"cb_{key}")
|
460 |
]
|
461 |
st.divider()
|
|
|
473 |
st.divider()
|
474 |
st.warning("β οΈ **Privacy:** Code sent to Google API if Mock Mode is OFF.")
|
475 |
|
476 |
+
# Update title dynamically based on selected model
|
477 |
if st.session_state.selected_model_name and not st.session_state.mock_api_call:
|
478 |
st.markdown(f"Upload codebase (`.zip`) for analysis via **{st.session_state.selected_model_name}**.")
|
479 |
elif st.session_state.mock_api_call:
|
|
|
503 |
file_id, uploaded_file.size, uploaded_file_bytes
|
504 |
)
|
505 |
if code_files is not None:
|
|
|
|
|
|
|
506 |
st.info(f"Found **{file_count}** code files ({total_chars:,} chars). Est. tokens: ~{estimate_token_count(total_chars):,}")
|
507 |
+
# --- Interactive Metrics Visualization ---
|
508 |
+
metrics = {
|
509 |
+
"Metric": ["Files Analyzed", "Total Characters", "Token Estimate", "Ignored Files"],
|
510 |
+
"Value": [file_count, total_chars, estimate_token_count(total_chars), len(ignored_files)]
|
511 |
+
}
|
512 |
+
df_metrics = pd.DataFrame(metrics)
|
513 |
+
fig = px.bar(df_metrics, x="Metric", y="Value", title="Upload Summary Metrics")
|
514 |
+
st.plotly_chart(fig)
|
515 |
+
# --- End Metrics Visualization ---
|
516 |
if ignored_files:
|
517 |
with st.expander(f"View {len(ignored_files)} Skipped/Ignored Files"):
|
518 |
st.code("\n".join(ignored_files), language='text')
|
519 |
+
|
520 |
model_ready = bool(st.session_state.selected_model_name) or st.session_state.mock_api_call
|
521 |
analyze_button_disabled = (not selected_analyses or file_count == 0 or not model_ready)
|
522 |
analyze_button_label = "Analyze Codebase"
|
|
|
525 |
elif analyze_button_disabled:
|
526 |
analyze_button_label = "Select Analyses or Upload Valid Code"
|
527 |
|
528 |
+
if analysis_button_placeholder.button(
|
529 |
+
analyze_button_label,
|
530 |
+
type="primary",
|
531 |
+
disabled=analyze_button_disabled
|
532 |
+
):
|
533 |
st.session_state.analysis_requested = True
|
534 |
st.session_state.analysis_results = None
|
535 |
st.session_state.error_message = None
|
|
|
541 |
st.warning("Please select a Gemini model from the sidebar.")
|
542 |
else:
|
543 |
with results_placeholder:
|
544 |
+
spinner_model_name = (
|
545 |
+
st.session_state.selected_model_name
|
546 |
+
if not st.session_state.mock_api_call
|
547 |
+
else "Mock Mode"
|
548 |
+
)
|
549 |
spinner_msg = f"π Preparing prompt & contacting AI ({spinner_model_name})... Please wait."
|
550 |
with st.spinner(spinner_msg):
|
551 |
analysis_prompt, included_files_in_prompt = construct_analysis_prompt(code_files, selected_analyses)
|
|
|
559 |
st.session_state.error_message = "Failed to generate analysis prompt."
|
560 |
st.rerun()
|
561 |
|
|
|
562 |
if st.session_state.analysis_requested:
|
563 |
with results_placeholder:
|
564 |
st.divider()
|
|
|
574 |
elif not uploaded_file:
|
575 |
results_placeholder.info("Upload a ZIP file to begin.")
|
576 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
577 |
results_placeholder.divider()
|
578 |
results_placeholder.markdown("_Assistant powered by Google Gemini._")
|