Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -57,21 +57,7 @@ models = {path: AutoModelForSequenceClassification.from_pretrained(path) for pat
|
|
| 57 |
def get_quality_name(model_name):
|
| 58 |
return quality_mapping.get(model_name.split('/')[-1], "Unknown Quality")
|
| 59 |
|
| 60 |
-
|
| 61 |
-
"""Optimized: Using inference_mode and keeping models on GPU during the loop."""
|
| 62 |
-
model.to(device)
|
| 63 |
-
model.eval()
|
| 64 |
-
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True, max_length=512)
|
| 65 |
-
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 66 |
-
|
| 67 |
-
with torch.inference_mode():
|
| 68 |
-
outputs = model(**inputs)
|
| 69 |
-
logits = outputs.logits
|
| 70 |
-
probs = softmax(logits.cpu().numpy(), axis=1)
|
| 71 |
-
|
| 72 |
-
avg_prob = np.mean(probs[:, 1])
|
| 73 |
-
return avg_prob
|
| 74 |
-
|
| 75 |
azure_api_key = os.getenv("AZURE_OPENAI_API_KEY")
|
| 76 |
|
| 77 |
if not azure_api_key:
|
|
@@ -85,9 +71,9 @@ azure_client = AzureOpenAI(
|
|
| 85 |
azure_deployment_name = "gpt-5.4-nano-AM-karthik-prod"
|
| 86 |
|
| 87 |
def generate_explanation(issue_text, top_quality):
|
| 88 |
-
"""Generates an explanation using Azure OpenAI
|
| 89 |
if not top_quality:
|
| 90 |
-
return "
|
| 91 |
|
| 92 |
quality_name = top_quality[0][0]
|
| 93 |
|
|
@@ -110,47 +96,65 @@ Provide your response directly without preamble. Use a clear separation between
|
|
| 110 |
{"role": "system", "content": "You are an expert software engineering assistant specializing in software quality analysis."},
|
| 111 |
{"role": "user", "content": prompt}
|
| 112 |
],
|
| 113 |
-
max_completion_tokens=250,
|
| 114 |
temperature=0.2,
|
| 115 |
top_p=0.9
|
| 116 |
)
|
| 117 |
explanation = response.choices[0].message.content.strip()
|
| 118 |
-
|
|
|
|
|
|
|
| 119 |
return formatted_explanation
|
| 120 |
|
| 121 |
except Exception as e:
|
| 122 |
logging.error(f"Error during Azure OpenAI generation: {e}")
|
| 123 |
-
return "
|
| 124 |
|
| 125 |
@spaces.GPU(duration=60)
|
| 126 |
-
def
|
| 127 |
-
|
| 128 |
-
return "<div style='color: red;'>No text provided. Please enter a valid issue description.</div>", "", ""
|
| 129 |
-
|
| 130 |
-
if len(text) < 30:
|
| 131 |
-
return "<div style='color: red;'>Text is less than 30 characters.</div>", "", ""
|
| 132 |
-
|
| 133 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 134 |
results = []
|
| 135 |
|
| 136 |
for model_path, model in models.items():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
quality_name = get_quality_name(model_path)
|
| 138 |
-
|
| 139 |
if avg_prob >= 0.95:
|
| 140 |
results.append((quality_name, avg_prob))
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
|
| 143 |
if not results:
|
| 144 |
return "<div style='color: red;'>No recommendation. Prediction probability is below the threshold.</div>", "", ""
|
| 145 |
|
|
|
|
| 146 |
top_result = sorted(results, key=lambda x: x[1], reverse=True)
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
output_html = "<div style='color: red;'>No quality tag met the prediction probability threshold (>= 0.95).</div>"
|
| 153 |
-
explanation = ""
|
| 154 |
|
| 155 |
return output_html, "", explanation
|
| 156 |
|
|
@@ -174,6 +178,7 @@ example_texts = [
|
|
| 174 |
["The service-oriented architecture does not effectively isolate failures, leading to cascading failures across services.\n\nEnvironment: Microservices architecture\nReproduction: Simulate a service failure and observe the impact on other services."]
|
| 175 |
]
|
| 176 |
|
|
|
|
| 177 |
css = """
|
| 178 |
.quality-container {
|
| 179 |
font-family: Arial, sans-serif;
|
|
@@ -198,23 +203,16 @@ css = """
|
|
| 198 |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
| 199 |
}
|
| 200 |
|
|
|
|
| 201 |
.explanation-box {
|
| 202 |
-
border: 1px solid var(--border-color-primary, #ccc);
|
| 203 |
-
padding: 15px;
|
| 204 |
-
margin-top: 15px;
|
| 205 |
-
border-radius: 8px;
|
| 206 |
-
background-color: var(--background-fill-primary, #fff);
|
| 207 |
-
color: var(--body-text-color, #000);
|
| 208 |
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
| 209 |
-
line-height: 1.5;
|
| 210 |
-
}
|
| 211 |
-
|
| 212 |
-
.explanation-box p {
|
| 213 |
-
margin: 8px 0;
|
| 214 |
-
}
|
| 215 |
-
|
| 216 |
-
.explanation-box b {
|
| 217 |
-
color: #007bff;
|
| 218 |
}
|
| 219 |
|
| 220 |
.dark .quality-container {
|
|
@@ -224,20 +222,22 @@ css = """
|
|
| 224 |
}
|
| 225 |
|
| 226 |
.dark .explanation-box {
|
| 227 |
-
background-color: #1f2937;
|
| 228 |
-
border-color: #374151;
|
| 229 |
-
color: #f3f4f6;
|
| 230 |
-
box-shadow: 0 1px 3px rgba(255,255,255,0.05);
|
| 231 |
}
|
| 232 |
"""
|
| 233 |
|
| 234 |
interface = gr.Interface(
|
|
|
|
| 235 |
fn=main_interface,
|
| 236 |
inputs=gr.Textbox(lines=7, label="Issue Description", placeholder="Enter your issue text here"),
|
| 237 |
outputs=[
|
| 238 |
gr.HTML(label="Prediction Output"),
|
| 239 |
gr.Textbox(label="Predictions", visible=False),
|
| 240 |
-
|
|
|
|
| 241 |
],
|
| 242 |
title="QualityTagger",
|
| 243 |
description="This tool classifies text into different quality domains such as Security, Usability, Maintainability, Reliability etc., and provides explanations.",
|
|
|
|
| 57 |
def get_quality_name(model_name):
|
| 58 |
return quality_mapping.get(model_name.split('/')[-1], "Unknown Quality")
|
| 59 |
|
| 60 |
+
# --- Azure OpenAI Setup ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
azure_api_key = os.getenv("AZURE_OPENAI_API_KEY")
|
| 62 |
|
| 63 |
if not azure_api_key:
|
|
|
|
| 71 |
azure_deployment_name = "gpt-5.4-nano-AM-karthik-prod"
|
| 72 |
|
| 73 |
def generate_explanation(issue_text, top_quality):
|
| 74 |
+
"""Generates an explanation using Azure OpenAI (Pure Markdown output)"""
|
| 75 |
if not top_quality:
|
| 76 |
+
return "**No explanation available** as no quality tags met the threshold."
|
| 77 |
|
| 78 |
quality_name = top_quality[0][0]
|
| 79 |
|
|
|
|
| 96 |
{"role": "system", "content": "You are an expert software engineering assistant specializing in software quality analysis."},
|
| 97 |
{"role": "user", "content": prompt}
|
| 98 |
],
|
| 99 |
+
max_completion_tokens=250,
|
| 100 |
temperature=0.2,
|
| 101 |
top_p=0.9
|
| 102 |
)
|
| 103 |
explanation = response.choices[0].message.content.strip()
|
| 104 |
+
|
| 105 |
+
# FIX 1: Return pure markdown. Do not wrap in HTML <div> tags.
|
| 106 |
+
formatted_explanation = f"### Why this is a {quality_name} issue:\n\n{explanation}"
|
| 107 |
return formatted_explanation
|
| 108 |
|
| 109 |
except Exception as e:
|
| 110 |
logging.error(f"Error during Azure OpenAI generation: {e}")
|
| 111 |
+
return f"**Error:** An error occurred while communicating with the Azure OpenAI service: {e}"
|
| 112 |
|
| 113 |
@spaces.GPU(duration=60)
|
| 114 |
+
def run_classification_models(text):
|
| 115 |
+
"""FIX 2: Isolate the GPU work so we aren't holding the GPU while waiting for the network API call."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 117 |
results = []
|
| 118 |
|
| 119 |
for model_path, model in models.items():
|
| 120 |
+
model.to(device)
|
| 121 |
+
model.eval()
|
| 122 |
+
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True, max_length=512)
|
| 123 |
+
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 124 |
+
|
| 125 |
+
with torch.inference_mode():
|
| 126 |
+
outputs = model(**inputs)
|
| 127 |
+
logits = outputs.logits
|
| 128 |
+
probs = softmax(logits.cpu().numpy(), axis=1)
|
| 129 |
+
|
| 130 |
+
avg_prob = np.mean(probs[:, 1])
|
| 131 |
quality_name = get_quality_name(model_path)
|
| 132 |
+
|
| 133 |
if avg_prob >= 0.95:
|
| 134 |
results.append((quality_name, avg_prob))
|
| 135 |
+
|
| 136 |
+
return results
|
| 137 |
+
|
| 138 |
+
def main_interface(text):
|
| 139 |
+
if not text.strip():
|
| 140 |
+
return "<div style='color: red;'>No text provided. Please enter a valid issue description.</div>", "", ""
|
| 141 |
+
|
| 142 |
+
if len(text) < 30:
|
| 143 |
+
return "<div style='color: red;'>Text is less than 30 characters.</div>", "", ""
|
| 144 |
+
|
| 145 |
+
# 1. Run the GPU models
|
| 146 |
+
results = run_classification_models(text)
|
| 147 |
|
| 148 |
if not results:
|
| 149 |
return "<div style='color: red;'>No recommendation. Prediction probability is below the threshold.</div>", "", ""
|
| 150 |
|
| 151 |
+
# 2. Process results
|
| 152 |
top_result = sorted(results, key=lambda x: x[1], reverse=True)
|
| 153 |
+
top_quality = top_result[:1]
|
| 154 |
+
output_html = render_html_output(top_quality)
|
| 155 |
+
|
| 156 |
+
# 3. Fetch the OpenAI Explanation (Safely running on CPU/Network, off the ZeroGPU queue)
|
| 157 |
+
explanation = generate_explanation(text, top_quality)
|
|
|
|
|
|
|
| 158 |
|
| 159 |
return output_html, "", explanation
|
| 160 |
|
|
|
|
| 178 |
["The service-oriented architecture does not effectively isolate failures, leading to cascading failures across services.\n\nEnvironment: Microservices architecture\nReproduction: Simulate a service failure and observe the impact on other services."]
|
| 179 |
]
|
| 180 |
|
| 181 |
+
# FIX 3: We apply styles directly to Gradio's components using !important overrides
|
| 182 |
css = """
|
| 183 |
.quality-container {
|
| 184 |
font-family: Arial, sans-serif;
|
|
|
|
| 203 |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
| 204 |
}
|
| 205 |
|
| 206 |
+
/* Updated: Target the Markdown component natively */
|
| 207 |
.explanation-box {
|
| 208 |
+
border: 1px solid var(--border-color-primary, #ccc) !important;
|
| 209 |
+
padding: 15px !important;
|
| 210 |
+
margin-top: 15px !important;
|
| 211 |
+
border-radius: 8px !important;
|
| 212 |
+
background-color: var(--background-fill-primary, #fff) !important;
|
| 213 |
+
color: var(--body-text-color, #000) !important;
|
| 214 |
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
|
| 215 |
+
line-height: 1.5 !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
}
|
| 217 |
|
| 218 |
.dark .quality-container {
|
|
|
|
| 222 |
}
|
| 223 |
|
| 224 |
.dark .explanation-box {
|
| 225 |
+
background-color: #1f2937 !important;
|
| 226 |
+
border-color: #374151 !important;
|
| 227 |
+
color: #f3f4f6 !important;
|
| 228 |
+
box-shadow: 0 1px 3px rgba(255,255,255,0.05) !important;
|
| 229 |
}
|
| 230 |
"""
|
| 231 |
|
| 232 |
interface = gr.Interface(
|
| 233 |
+
# Note: main_interface is no longer @spaces.GPU wrapped!
|
| 234 |
fn=main_interface,
|
| 235 |
inputs=gr.Textbox(lines=7, label="Issue Description", placeholder="Enter your issue text here"),
|
| 236 |
outputs=[
|
| 237 |
gr.HTML(label="Prediction Output"),
|
| 238 |
gr.Textbox(label="Predictions", visible=False),
|
| 239 |
+
# FIX 4: Add the CSS class natively to the Markdown block
|
| 240 |
+
gr.Markdown(label="Explanation", elem_classes="explanation-box")
|
| 241 |
],
|
| 242 |
title="QualityTagger",
|
| 243 |
description="This tool classifies text into different quality domains such as Security, Usability, Maintainability, Reliability etc., and provides explanations.",
|