Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -6,13 +6,14 @@ import json
|
|
6 |
import os # Still needed for API key potentially, but not model names
|
7 |
from pathlib import Path
|
8 |
import time
|
|
|
|
|
9 |
|
10 |
# --- Configuration ---
|
11 |
-
|
12 |
-
MAX_PROMPT_TOKENS_ESTIMATE = 800000 # Keep this estimate
|
13 |
RESULTS_PAGE_SIZE = 25
|
14 |
|
15 |
-
AVAILABLE_ANALYSES = {
|
16 |
"generate_docs": "Generate Missing Docstrings/Comments",
|
17 |
"find_bugs": "Identify Potential Bugs & Anti-patterns",
|
18 |
"check_style": "Check Style Guide Compliance (General)",
|
@@ -20,12 +21,11 @@ AVAILABLE_ANALYSES = { # Keep analyses config
|
|
20 |
"suggest_refactoring": "Suggest Refactoring Opportunities",
|
21 |
}
|
22 |
CODE_EXTENSIONS = {
|
23 |
-
'.py', '.js', '.java', '.c', '.cpp', '.h', '.cs', '.go', '.rb',
|
24 |
'.php', '.swift', '.kt', '.ts', '.html', '.css', '.scss', '.sql'
|
25 |
-
}
|
26 |
|
27 |
# --- Session State Initialization ---
|
28 |
-
# (Keep most session state, add one for the selected model)
|
29 |
if 'mock_api_call' not in st.session_state:
|
30 |
st.session_state.mock_api_call = False
|
31 |
if 'analysis_results' not in st.session_state:
|
@@ -35,79 +35,73 @@ 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 = {} #
|
|
|
|
|
|
|
|
|
41 |
|
42 |
# --- Gemini API Setup & Model Discovery ---
|
43 |
model = None # Global variable for the initialized model instance
|
44 |
|
45 |
-
|
46 |
-
@st.cache_data(ttl=3600) # Cache model list for an hour
|
47 |
def get_available_models():
|
48 |
"""Lists models supporting 'generateContent' using the API key."""
|
49 |
model_dict = {}
|
50 |
try:
|
51 |
if 'GEMINI_API_KEY' not in st.secrets:
|
52 |
-
# Don't stop here, let the main part handle it, but return empty
|
53 |
print("API key not found in secrets during model listing attempt.")
|
54 |
return {}
|
55 |
-
# Configure API key temporarily just for listing
|
56 |
genai.configure(api_key=st.secrets["GEMINI_API_KEY"])
|
57 |
print("Listing available models via API...")
|
58 |
for m in genai.list_models():
|
59 |
-
# Check if the model supports the 'generateContent' method
|
60 |
if 'generateContent' in m.supported_generation_methods:
|
61 |
-
# Store mapping: user-friendly name -> internal name
|
62 |
model_dict[m.display_name] = m.name
|
63 |
print(f"Found {len(model_dict)} compatible models.")
|
64 |
return model_dict
|
65 |
except Exception as e:
|
66 |
st.error(f"π¨ Error listing available models: {e}")
|
67 |
-
return {}
|
68 |
|
69 |
def initialize_gemini_model():
|
70 |
"""Initializes the Gemini model based on the selected name."""
|
71 |
global model
|
72 |
selected_name = st.session_state.get('selected_model_name')
|
73 |
-
|
74 |
if selected_name and model is None and not st.session_state.mock_api_call:
|
75 |
try:
|
76 |
if 'GEMINI_API_KEY' not in st.secrets:
|
77 |
st.error("π¨ Gemini API Key not found. Add it to `.streamlit/secrets.toml`.")
|
78 |
-
st.stop()
|
79 |
-
# Configure API key (might be redundant if list_models worked, but safe)
|
80 |
genai.configure(api_key=st.secrets["GEMINI_API_KEY"])
|
81 |
print(f"Initializing Gemini Model: {selected_name}")
|
82 |
-
# Use the selected model name from session state
|
83 |
model = genai.GenerativeModel(model_name=selected_name)
|
84 |
print(f"Gemini Model Initialized ({selected_name}).")
|
85 |
return True
|
86 |
except Exception as e:
|
87 |
st.error(f"π¨ Error initializing selected Gemini model '{selected_name}': {e}")
|
88 |
-
st.session_state.selected_model_name = None
|
89 |
st.stop()
|
90 |
return False
|
91 |
elif st.session_state.mock_api_call:
|
92 |
-
return True
|
93 |
elif model is not None and model.model_name == selected_name:
|
94 |
-
return True
|
95 |
elif model is not None and model.model_name != selected_name:
|
96 |
print("Model changed. Re-initializing...")
|
97 |
-
model = None
|
98 |
-
return initialize_gemini_model()
|
99 |
elif not selected_name and not st.session_state.mock_api_call:
|
100 |
-
|
101 |
-
|
102 |
-
return False # Default case
|
103 |
|
104 |
# --- Helper Functions ---
|
105 |
-
# Updated estimate_token_count to support integers and strings
|
106 |
def estimate_token_count(text):
|
107 |
"""
|
108 |
Estimates the token count.
|
109 |
-
If a string is provided,
|
110 |
-
If an integer is provided (e.g., total character count),
|
111 |
"""
|
112 |
if isinstance(text, int):
|
113 |
return text // 3
|
@@ -117,7 +111,7 @@ def estimate_token_count(text):
|
|
117 |
def process_zip_file_cached(file_id, file_size, file_content_bytes):
|
118 |
"""
|
119 |
Processes a ZIP file and extracts code files.
|
120 |
-
Returns
|
121 |
"""
|
122 |
code_files = {}
|
123 |
total_chars = 0
|
@@ -166,16 +160,13 @@ def process_zip_file_cached(file_id, file_size, file_content_bytes):
|
|
166 |
st.error(f"π¨ ZIP Error: {e}")
|
167 |
return None, 0, 0, []
|
168 |
if file_count == 0:
|
169 |
-
if not ignored_files
|
170 |
-
st.warning("No code files found.")
|
171 |
-
else:
|
172 |
-
st.warning("No code files found; some skipped.")
|
173 |
return code_files, total_chars, file_count, ignored_files
|
174 |
|
175 |
def construct_analysis_prompt(code_files_dict, requested_analyses):
|
176 |
"""
|
177 |
-
Constructs the prompt for analysis by including code files and JSON structure
|
178 |
-
Returns the full prompt and
|
179 |
"""
|
180 |
prompt_parts = ["Analyze the following codebase...\n\n"]
|
181 |
current_token_estimate = estimate_token_count(prompt_parts[0])
|
@@ -227,12 +218,11 @@ def construct_analysis_prompt(code_files_dict, requested_analyses):
|
|
227 |
def call_gemini_api(prompt):
|
228 |
"""
|
229 |
Calls the Gemini API using the provided prompt.
|
230 |
-
Returns
|
231 |
"""
|
232 |
if not prompt:
|
233 |
return None, "Prompt generation failed."
|
234 |
|
235 |
-
# MOCK MODE
|
236 |
if st.session_state.mock_api_call:
|
237 |
st.info(" MOCK MODE: Simulating API call...")
|
238 |
time.sleep(1)
|
@@ -245,12 +235,11 @@ def call_gemini_api(prompt):
|
|
245 |
})
|
246 |
st.success("Mock response generated.")
|
247 |
return json.loads(mock_json_response), None
|
248 |
-
# REAL API CALL
|
249 |
else:
|
250 |
if not initialize_gemini_model():
|
251 |
return None, "Gemini Model Initialization Failed."
|
252 |
if model is None:
|
253 |
-
return None, "Gemini model not selected or available."
|
254 |
try:
|
255 |
api_status = st.empty()
|
256 |
api_status.info(f"π‘ Sending request to {model.model_name} (Est. prompt tokens: {estimate_token_count(prompt):,})... Please wait.")
|
@@ -270,7 +259,6 @@ def call_gemini_api(prompt):
|
|
270 |
api_status.empty()
|
271 |
try:
|
272 |
json_response_text = response.text.strip()
|
273 |
-
# Remove markdown code fences if present
|
274 |
if json_response_text.startswith("```json"):
|
275 |
json_response_text = json_response_text[7:]
|
276 |
if json_response_text.startswith("```"):
|
@@ -320,7 +308,7 @@ def call_gemini_api(prompt):
|
|
320 |
|
321 |
def display_results(results_json, requested_analyses):
|
322 |
"""
|
323 |
-
Displays
|
324 |
"""
|
325 |
st.header("π Analysis Report")
|
326 |
if not isinstance(results_json, dict):
|
@@ -407,8 +395,21 @@ def display_results(results_json, requested_analyses):
|
|
407 |
st.set_page_config(page_title="Codebase Audit Assistant", layout="wide")
|
408 |
st.title("π€ Codebase Audit & Documentation Assistant")
|
409 |
|
410 |
-
# --- Sidebar ---
|
411 |
with st.sidebar:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
412 |
st.header("βοΈ Analysis Controls")
|
413 |
st.session_state.mock_api_call = st.toggle(
|
414 |
"π§ͺ Enable Mock API Mode",
|
@@ -418,27 +419,20 @@ with st.sidebar:
|
|
418 |
|
419 |
st.divider()
|
420 |
st.header("β Select Model")
|
421 |
-
# --- NEW: Dynamic Model Selection ---
|
422 |
if not st.session_state.mock_api_call:
|
423 |
-
# Get available models (uses cache)
|
424 |
st.session_state.available_models_dict = get_available_models()
|
425 |
model_display_names = list(st.session_state.available_models_dict.keys())
|
426 |
-
|
427 |
if model_display_names:
|
428 |
-
# Try to find the index of the previously selected model
|
429 |
current_model_display_name = None
|
430 |
if st.session_state.selected_model_name:
|
431 |
-
# Find display name matching the stored internal name
|
432 |
for disp_name, internal_name in st.session_state.available_models_dict.items():
|
433 |
if internal_name == st.session_state.selected_model_name:
|
434 |
current_model_display_name = disp_name
|
435 |
break
|
436 |
-
|
437 |
try:
|
438 |
selected_index = model_display_names.index(current_model_display_name) if current_model_display_name in model_display_names else 0
|
439 |
except ValueError:
|
440 |
-
selected_index = 0
|
441 |
-
|
442 |
selected_display_name = st.selectbox(
|
443 |
"Choose Gemini model:",
|
444 |
options=model_display_names,
|
@@ -446,20 +440,18 @@ with st.sidebar:
|
|
446 |
key="model_selector",
|
447 |
help="Select the Gemini model to use for analysis."
|
448 |
)
|
449 |
-
# Update session state with the internal name based on selection
|
450 |
st.session_state.selected_model_name = st.session_state.available_models_dict.get(selected_display_name)
|
451 |
st.info(f"Using REAL Gemini API ({st.session_state.selected_model_name})")
|
452 |
elif 'GEMINI_API_KEY' in st.secrets:
|
453 |
st.warning("No compatible models found or error listing models. Check API Key permissions.")
|
454 |
-
st.session_state.selected_model_name = None
|
455 |
else:
|
456 |
st.warning("Add GEMINI_API_KEY to secrets to list models.")
|
457 |
st.session_state.selected_model_name = None
|
458 |
-
else:
|
459 |
st.info("Mock API Mode ACTIVE")
|
460 |
-
st.session_state.selected_model_name = "mock_model"
|
461 |
-
|
462 |
-
|
463 |
st.divider()
|
464 |
st.header("π Select Analyses")
|
465 |
selected_analyses = [
|
@@ -481,7 +473,7 @@ with st.sidebar:
|
|
481 |
st.divider()
|
482 |
st.warning("β οΈ **Privacy:** Code sent to Google API if Mock Mode is OFF.")
|
483 |
|
484 |
-
# Update title
|
485 |
if st.session_state.selected_model_name and not st.session_state.mock_api_call:
|
486 |
st.markdown(f"Upload codebase (`.zip`) for analysis via **{st.session_state.selected_model_name}**.")
|
487 |
elif st.session_state.mock_api_call:
|
@@ -511,12 +503,13 @@ if uploaded_file:
|
|
511 |
file_id, uploaded_file.size, uploaded_file_bytes
|
512 |
)
|
513 |
if code_files is not None:
|
|
|
|
|
|
|
514 |
st.info(f"Found **{file_count}** code files ({total_chars:,} chars). Est. tokens: ~{estimate_token_count(total_chars):,}")
|
515 |
if ignored_files:
|
516 |
with st.expander(f"View {len(ignored_files)} Skipped/Ignored Files"):
|
517 |
st.code("\n".join(ignored_files), language='text')
|
518 |
-
|
519 |
-
# Disable button if no model selected (and not in mock mode)
|
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,11 +518,7 @@ if uploaded_file:
|
|
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,11 +530,7 @@ if uploaded_file:
|
|
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,7 +544,7 @@ if uploaded_file:
|
|
559 |
st.session_state.error_message = "Failed to generate analysis prompt."
|
560 |
st.rerun()
|
561 |
|
562 |
-
# Display results
|
563 |
if st.session_state.analysis_requested:
|
564 |
with results_placeholder:
|
565 |
st.divider()
|
@@ -575,5 +560,25 @@ if st.session_state.analysis_requested:
|
|
575 |
elif not uploaded_file:
|
576 |
results_placeholder.info("Upload a ZIP file to begin.")
|
577 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
578 |
results_placeholder.divider()
|
579 |
results_placeholder.markdown("_Assistant powered by Google Gemini._")
|
|
|
6 |
import os # Still needed for API key potentially, but not model names
|
7 |
from pathlib import Path
|
8 |
import time
|
9 |
+
import plotly.express as px
|
10 |
+
import pandas as pd
|
11 |
|
12 |
# --- Configuration ---
|
13 |
+
MAX_PROMPT_TOKENS_ESTIMATE = 800000 # Token estimate limit
|
|
|
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)",
|
|
|
21 |
"suggest_refactoring": "Suggest Refactoring Opportunities",
|
22 |
}
|
23 |
CODE_EXTENSIONS = {
|
24 |
+
'.py', '.js', '.java', '.c', '.cpp', '.h', '.cs', '.go', '.rb',
|
25 |
'.php', '.swift', '.kt', '.ts', '.html', '.css', '.scss', '.sql'
|
26 |
+
}
|
27 |
|
28 |
# --- Session State Initialization ---
|
|
|
29 |
if 'mock_api_call' not in st.session_state:
|
30 |
st.session_state.mock_api_call = False
|
31 |
if 'analysis_results' 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 # Internal model name
|
39 |
if 'available_models_dict' not in st.session_state:
|
40 |
+
st.session_state.available_models_dict = {} # Mapping: display -> internal
|
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
|
48 |
|
49 |
+
@st.cache_data(ttl=3600)
|
|
|
50 |
def get_available_models():
|
51 |
"""Lists models supporting 'generateContent' using the API key."""
|
52 |
model_dict = {}
|
53 |
try:
|
54 |
if 'GEMINI_API_KEY' not in st.secrets:
|
|
|
55 |
print("API key not found in secrets during model listing attempt.")
|
56 |
return {}
|
|
|
57 |
genai.configure(api_key=st.secrets["GEMINI_API_KEY"])
|
58 |
print("Listing available models via API...")
|
59 |
for m in genai.list_models():
|
|
|
60 |
if 'generateContent' in m.supported_generation_methods:
|
|
|
61 |
model_dict[m.display_name] = m.name
|
62 |
print(f"Found {len(model_dict)} compatible models.")
|
63 |
return model_dict
|
64 |
except Exception as e:
|
65 |
st.error(f"π¨ Error listing available models: {e}")
|
66 |
+
return {}
|
67 |
|
68 |
def initialize_gemini_model():
|
69 |
"""Initializes the Gemini model based on the selected name."""
|
70 |
global model
|
71 |
selected_name = st.session_state.get('selected_model_name')
|
|
|
72 |
if selected_name and model is None and not st.session_state.mock_api_call:
|
73 |
try:
|
74 |
if 'GEMINI_API_KEY' not in st.secrets:
|
75 |
st.error("π¨ Gemini API Key not found. Add it to `.streamlit/secrets.toml`.")
|
76 |
+
st.stop()
|
|
|
77 |
genai.configure(api_key=st.secrets["GEMINI_API_KEY"])
|
78 |
print(f"Initializing Gemini Model: {selected_name}")
|
|
|
79 |
model = genai.GenerativeModel(model_name=selected_name)
|
80 |
print(f"Gemini Model Initialized ({selected_name}).")
|
81 |
return True
|
82 |
except Exception as e:
|
83 |
st.error(f"π¨ Error initializing selected Gemini model '{selected_name}': {e}")
|
84 |
+
st.session_state.selected_model_name = None
|
85 |
st.stop()
|
86 |
return False
|
87 |
elif st.session_state.mock_api_call:
|
88 |
+
return True
|
89 |
elif model is not None and model.model_name == selected_name:
|
90 |
+
return True
|
91 |
elif model is not None and model.model_name != selected_name:
|
92 |
print("Model changed. Re-initializing...")
|
93 |
+
model = None
|
94 |
+
return initialize_gemini_model()
|
95 |
elif not selected_name and not st.session_state.mock_api_call:
|
96 |
+
return False
|
97 |
+
return False
|
|
|
98 |
|
99 |
# --- Helper Functions ---
|
|
|
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 is provided (e.g., total character count), uses that directly.
|
105 |
"""
|
106 |
if isinstance(text, int):
|
107 |
return text // 3
|
|
|
111 |
def process_zip_file_cached(file_id, file_size, file_content_bytes):
|
112 |
"""
|
113 |
Processes a ZIP file and extracts code files.
|
114 |
+
Returns (code_files dict, total_chars, file_count, ignored_files list).
|
115 |
"""
|
116 |
code_files = {}
|
117 |
total_chars = 0
|
|
|
160 |
st.error(f"π¨ ZIP Error: {e}")
|
161 |
return None, 0, 0, []
|
162 |
if file_count == 0:
|
163 |
+
st.warning("No code files found." if not ignored_files else "No code files found; some skipped.")
|
|
|
|
|
|
|
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 |
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."
|
225 |
|
|
|
226 |
if st.session_state.mock_api_call:
|
227 |
st.info(" MOCK MODE: Simulating API call...")
|
228 |
time.sleep(1)
|
|
|
235 |
})
|
236 |
st.success("Mock response generated.")
|
237 |
return json.loads(mock_json_response), None
|
|
|
238 |
else:
|
239 |
if not initialize_gemini_model():
|
240 |
return None, "Gemini Model Initialization Failed."
|
241 |
if model is None:
|
242 |
+
return None, "Gemini model not selected or available."
|
243 |
try:
|
244 |
api_status = st.empty()
|
245 |
api_status.info(f"π‘ Sending request to {model.model_name} (Est. prompt tokens: {estimate_token_count(prompt):,})... Please wait.")
|
|
|
259 |
api_status.empty()
|
260 |
try:
|
261 |
json_response_text = response.text.strip()
|
|
|
262 |
if json_response_text.startswith("```json"):
|
263 |
json_response_text = json_response_text[7:]
|
264 |
if json_response_text.startswith("```"):
|
|
|
308 |
|
309 |
def display_results(results_json, requested_analyses):
|
310 |
"""
|
311 |
+
Displays analysis results with pagination and a downloadable JSON report.
|
312 |
"""
|
313 |
st.header("π Analysis Report")
|
314 |
if not isinstance(results_json, dict):
|
|
|
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)
|
402 |
+
if dark_mode:
|
403 |
+
st.markdown(
|
404 |
+
"""
|
405 |
+
<style>
|
406 |
+
.reportview-container { background-color: #2E2E2E; color: white; }
|
407 |
+
.sidebar .sidebar-content { background-color: #1A1A1A; }
|
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",
|
|
|
419 |
|
420 |
st.divider()
|
421 |
st.header("β Select Model")
|
|
|
422 |
if not st.session_state.mock_api_call:
|
|
|
423 |
st.session_state.available_models_dict = get_available_models()
|
424 |
model_display_names = list(st.session_state.available_models_dict.keys())
|
|
|
425 |
if model_display_names:
|
|
|
426 |
current_model_display_name = None
|
427 |
if st.session_state.selected_model_name:
|
|
|
428 |
for disp_name, internal_name in st.session_state.available_models_dict.items():
|
429 |
if internal_name == st.session_state.selected_model_name:
|
430 |
current_model_display_name = disp_name
|
431 |
break
|
|
|
432 |
try:
|
433 |
selected_index = model_display_names.index(current_model_display_name) if current_model_display_name in model_display_names else 0
|
434 |
except ValueError:
|
435 |
+
selected_index = 0
|
|
|
436 |
selected_display_name = st.selectbox(
|
437 |
"Choose Gemini model:",
|
438 |
options=model_display_names,
|
|
|
440 |
key="model_selector",
|
441 |
help="Select the Gemini model to use for analysis."
|
442 |
)
|
|
|
443 |
st.session_state.selected_model_name = st.session_state.available_models_dict.get(selected_display_name)
|
444 |
st.info(f"Using REAL Gemini API ({st.session_state.selected_model_name})")
|
445 |
elif 'GEMINI_API_KEY' in st.secrets:
|
446 |
st.warning("No compatible models found or error listing models. Check API Key permissions.")
|
447 |
+
st.session_state.selected_model_name = None
|
448 |
else:
|
449 |
st.warning("Add GEMINI_API_KEY to secrets to list models.")
|
450 |
st.session_state.selected_model_name = None
|
451 |
+
else:
|
452 |
st.info("Mock API Mode ACTIVE")
|
453 |
+
st.session_state.selected_model_name = "mock_model"
|
454 |
+
|
|
|
455 |
st.divider()
|
456 |
st.header("π Select Analyses")
|
457 |
selected_analyses = [
|
|
|
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 |
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 |
elif analyze_button_disabled:
|
519 |
analyze_button_label = "Select Analyses or Upload Valid Code"
|
520 |
|
521 |
+
if analysis_button_placeholder.button(analyze_button_label, type="primary", disabled=analyze_button_disabled):
|
|
|
|
|
|
|
|
|
522 |
st.session_state.analysis_requested = True
|
523 |
st.session_state.analysis_results = None
|
524 |
st.session_state.error_message = None
|
|
|
530 |
st.warning("Please select a Gemini model from the sidebar.")
|
531 |
else:
|
532 |
with results_placeholder:
|
533 |
+
spinner_model_name = st.session_state.selected_model_name if not st.session_state.mock_api_call else "Mock Mode"
|
|
|
|
|
|
|
|
|
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 |
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 |
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._")
|