HarshitaSuri commited on
Commit
2c3a2c7
Β·
verified Β·
1 Parent(s): f39c016

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -217
app.py CHANGED
@@ -1,233 +1,87 @@
1
- import gradio as gr
2
  import pandas as pd
3
  import requests
4
- import json
5
- from sentence_transformers import SentenceTransformer
6
- from sklearn.metrics.pairwise import cosine_similarity
7
- import numpy as np
8
- from typing import Dict, Tuple
9
- import io
10
 
11
  # Load semantic similarity model
12
- model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
13
 
14
- def calculate_semantic_similarity(text1: str, text2: str) -> float:
15
- """Calculate semantic similarity between two texts using AI embeddings"""
16
- embeddings = model.encode([text1, text2])
17
- similarity = cosine_similarity([embeddings[0]], [embeddings[1]])[0][0]
18
- return float(similarity)
19
 
20
- def is_empty_response(response):
21
- """Check if response is empty or null"""
22
- if response is None or response == '':
23
- return True
24
- if isinstance(response, str):
25
- return response.strip() in ['', 'null', 'undefined']
26
- return False
 
 
27
 
28
- def validate_json_response(expected, actual) -> Tuple[bool, str]:
29
- """Validate if JSON responses match"""
30
- try:
31
- expected_json = json.loads(expected) if isinstance(expected, str) else expected
32
- actual_json = json.loads(actual) if isinstance(actual, str) else actual
33
-
34
- if expected_json == actual_json:
35
- return True, "Exact match"
36
- return False, "JSON structure mismatch"
37
- except Exception as e:
38
- return False, f"Invalid JSON: {str(e)}"
39
-
40
- def run_single_test(endpoint: str, method: str, bearer_token: str,
41
- expected_status: int, expected_body: str) -> Dict:
42
- """Run a single API test with AI-powered semantic validation"""
43
- result = {
44
- 'Endpoint': endpoint,
45
- 'Method': method,
46
- 'Expected Status': expected_status,
47
- 'Actual Status': None,
48
- 'Expected Body': expected_body[:100] + '...' if len(expected_body) > 100 else expected_body,
49
- 'Actual Body': None,
50
- 'Outcome': 'FAIL',
51
- 'Similarity': None,
52
- 'Notes': ''
53
- }
54
 
55
- try:
56
- headers = {}
57
- if bearer_token and bearer_token.strip():
58
- headers['Authorization'] = f'Bearer {bearer_token.strip()}'
59
-
60
- # Make API call
61
- if method.upper() == 'GET':
62
- response = requests.get(endpoint, headers=headers, timeout=10)
63
- elif method.upper() == 'POST':
64
- response = requests.post(endpoint, headers=headers, timeout=10)
65
- elif method.upper() == 'PUT':
66
- response = requests.put(endpoint, headers=headers, timeout=10)
67
- elif method.upper() == 'DELETE':
68
- response = requests.delete(endpoint, headers=headers, timeout=10)
69
- else:
70
- result['Notes'] = f'Unsupported method: {method}'
71
- return result
72
-
73
- result['Actual Status'] = response.status_code
74
- actual_body = response.text
75
- result['Actual Body'] = actual_body[:100] + '...' if len(actual_body) > 100 else actual_body
76
-
77
- # Check status code
78
- if response.status_code != expected_status:
79
- result['Outcome'] = 'FAIL'
80
- result['Notes'] = f'Status mismatch: expected {expected_status}, got {response.status_code}'
81
- return result
82
-
83
- # Status matches - now check body
84
- if not expected_body or is_empty_response(expected_body):
85
- result['Outcome'] = 'PASS'
86
- result['Notes'] = 'Status matches, no body validation'
87
- return result
88
 
89
- if is_empty_response(actual_body):
90
- result['Outcome'] = 'UNCERTAIN'
91
- result['Similarity'] = 0.0
92
- result['Notes'] = 'Response is empty (manual review needed)'
93
- return result
94
-
95
- # Try JSON validation first
96
- json_match, json_reason = validate_json_response(expected_body, actual_body)
97
- if json_match:
98
- result['Outcome'] = 'PASS'
99
- result['Similarity'] = 1.0
100
- result['Notes'] = 'Exact JSON match'
101
- return result
102
-
103
- # Use AI semantic similarity
104
  try:
105
- similarity = calculate_semantic_similarity(expected_body, actual_body)
106
- result['Similarity'] = round(similarity, 3)
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
- if similarity >= 0.75:
109
- result['Outcome'] = 'PASS'
110
- result['Notes'] = f'High semantic similarity: {similarity:.1%}'
111
- elif similarity >= 0.5:
112
- result['Outcome'] = 'UNCERTAIN'
113
- result['Notes'] = f'Medium semantic similarity: {similarity:.1%} (manual review needed)'
114
  else:
115
- result['Outcome'] = 'UNCERTAIN'
116
- result['Notes'] = f'Low semantic similarity: {similarity:.1%} (manual review needed)'
117
- except Exception as sim_error:
118
- result['Outcome'] = 'UNCERTAIN'
119
- result['Notes'] = f'Cannot compute similarity: {str(sim_error)}'
120
-
121
- return result
122
-
123
- except requests.exceptions.Timeout:
124
- result['Notes'] = 'Request timeout'
125
- return result
126
- except requests.exceptions.ConnectionError:
127
- result['Notes'] = 'Connection error'
128
- return result
129
- except Exception as e:
130
- result['Notes'] = f'Error: {str(e)}'
131
- return result
132
-
133
- def process_excel_file(file, bearer_token: str) -> Tuple[pd.DataFrame, str]:
134
- """Process uploaded Excel file and run all tests"""
135
- try:
136
- df = pd.read_excel(file)
137
-
138
- required_columns = ['Endpoint', 'Method', 'Expected Status Code', 'Expected Response Body']
139
- missing_columns = [col for col in required_columns if col not in df.columns]
140
-
141
- if missing_columns:
142
- return None, f"❌ Missing columns: {', '.join(missing_columns)}"
143
-
144
- results = []
145
- for _, row in df.iterrows():
146
- result = run_single_test(
147
- endpoint=str(row['Endpoint']),
148
- method=str(row['Method']),
149
- bearer_token=bearer_token,
150
- expected_status=int(row['Expected Status Code']),
151
- expected_body=str(row['Expected Response Body'])
152
- )
153
- results.append(result)
154
-
155
- results_df = pd.DataFrame(results)
156
-
157
- total = len(results_df)
158
- passed = len(results_df[results_df['Outcome'] == 'PASS'])
159
- failed = len(results_df[results_df['Outcome'] == 'FAIL'])
160
- uncertain = len(results_df[results_df['Outcome'] == 'UNCERTAIN'])
161
-
162
- summary = f"""
163
- πŸ“Š **Test Summary**
164
- - Total Tests: {total}
165
- - βœ… Passed: {passed}
166
- - ❌ Failed: {failed}
167
- - ⚠️ Uncertain: {uncertain}
168
- """
169
-
170
- return results_df, summary
171
 
172
- except Exception as e:
173
- return None, f"❌ Error processing file: {str(e)}"
174
-
175
- def download_results(df):
176
- """Convert DataFrame to Excel for download"""
177
- if df is None or df.empty:
178
- return None
179
-
180
- output = io.BytesIO()
181
- with pd.ExcelWriter(output, engine='openpyxl') as writer:
182
- df.to_excel(writer, index=False, sheet_name='Test Results')
183
 
184
- output.seek(0)
185
- return output
186
 
187
- # Gradio UI
188
- with gr.Blocks(title="API Test Runner with AI", theme=gr.themes.Soft()) as app:
189
- gr.Markdown("""
190
- # πŸš€ API Test Runner with AI-Powered Validation
191
-
192
- Upload an Excel file with your API test cases and get automated validation with semantic similarity analysis.
193
- """)
194
-
195
- with gr.Row():
196
- with gr.Column(scale=2):
197
- file_input = gr.File(label="Upload Excel File (.xlsx)", file_types=['.xlsx'])
198
- token_input = gr.Textbox(
199
- label="Bearer Token (Optional)",
200
- placeholder="Enter your API bearer token if required",
201
- type="password"
202
- )
203
- run_button = gr.Button("πŸ” Run Tests", variant="primary", size="lg")
204
-
205
- with gr.Column(scale=1):
206
- summary_output = gr.Markdown(label="Summary")
207
-
208
- with gr.Row():
209
- results_output = gr.Dataframe(
210
- label="Test Results",
211
- headers=['Endpoint', 'Method', 'Expected Status', 'Actual Status',
212
- 'Outcome', 'Similarity', 'Notes'],
213
- interactive=False
214
- )
215
-
216
- with gr.Row():
217
- download_button = gr.Button("πŸ’Ύ Download Results")
218
- download_output = gr.File(label="Download Excel Report")
219
-
220
- run_button.click(
221
- fn=process_excel_file,
222
- inputs=[file_input, token_input],
223
- outputs=[results_output, summary_output]
224
- )
225
-
226
- download_button.click(
227
- fn=download_results,
228
- inputs=[results_output],
229
- outputs=[download_output]
230
- )
231
 
232
- if __name__ == "__main__":
233
- app.launch()
 
 
1
  import pandas as pd
2
  import requests
3
+ import gradio as gr
4
+ from sentence_transformers import SentenceTransformer, util
5
+ from sklearn.linear_model import LogisticRegression
6
+ import os
 
 
7
 
8
  # Load semantic similarity model
9
+ model = SentenceTransformer("multi-qa-mpnet-base-dot-v1")
10
 
11
+ # Initialize classifier
12
+ classifier = LogisticRegression()
13
+ feedback_file = "feedback.csv"
 
 
14
 
15
+ # Load feedback if available
16
+ if os.path.exists(feedback_file):
17
+ feedback_data = pd.read_csv(feedback_file)
18
+ if len(feedback_data) > 10:
19
+ X = feedback_data[["similarity", "resp_length"]]
20
+ y = feedback_data["label"]
21
+ classifier.fit(X, y)
22
+ else:
23
+ feedback_data = pd.DataFrame(columns=["similarity","resp_length","label"])
24
 
25
+ def run_tests(file, bearer_token=None):
26
+ df = pd.read_excel(file)
27
+ results = []
28
+ headers = {}
29
+ if bearer_token:
30
+ headers["Authorization"] = f"Bearer {bearer_token}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ for _, row in df.iterrows():
33
+ test_name = row["TestName"]
34
+ endpoint = row["Endpoint"]
35
+ method = row["Method"]
36
+ payload = row.get("Payload", None)
37
+ expected_status = row["ExpectedStatus"]
38
+ expected_body = str(row["ExpectedBody"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  try:
41
+ response = requests.request(method, endpoint, headers=headers, data=payload)
42
+ status_match = response.status_code == expected_status
43
+ body = response.text
44
+ except Exception as e:
45
+ results.append((test_name, "Error", str(e)))
46
+ continue
47
+
48
+ if not status_match:
49
+ result = "Fail"
50
+ elif body == expected_body:
51
+ result = "Pass"
52
+ else:
53
+ sim = util.cos_sim(model.encode(expected_body), model.encode(body)).item()
54
+ resp_length = len(body)
55
 
56
+ # If classifier trained, use it
57
+ if len(feedback_data) > 10:
58
+ pred = classifier.predict([[sim, resp_length]])[0]
59
+ result = "Pass" if pred == 1 else "Fail"
 
 
60
  else:
61
+ result = "Pass" if sim > 0.75 else "Uncertain"
62
+
63
+ # Store similarity for feedback loop
64
+ results.append((test_name, result, sim))
65
+ continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ results.append((test_name, result, None))
 
 
 
 
 
 
 
 
 
 
68
 
69
+ return pd.DataFrame(results, columns=["TestName", "Result", "Similarity"])
 
70
 
71
+ def feedback_handler(similarity, resp_length, label):
72
+ global feedback_data
73
+ feedback_data = feedback_data.append({"similarity": similarity,
74
+ "resp_length": resp_length,
75
+ "label": label}, ignore_index=True)
76
+ feedback_data.to_csv(feedback_file, index=False)
77
+ return "Feedback saved!"
78
+
79
+ demo = gr.Interface(
80
+ fn=run_tests,
81
+ inputs=[gr.File(label="Upload Excel"), gr.Textbox(label="Bearer Token")],
82
+ outputs="dataframe",
83
+ title="Adaptive API Test Runner",
84
+ description="Upload test cases in Excel, run API tests, and classify results with semantic similarity + ML feedback loop."
85
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ demo.launch()