Bhanumani12 commited on
Commit
fe32a7c
·
verified ·
1 Parent(s): d3cdd12

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -104
app.py CHANGED
@@ -1,12 +1,11 @@
1
  import os
2
  import random
3
- import re
4
  import gradio as gr
5
  from datetime import datetime
6
  from transformers import pipeline
7
  from simple_salesforce import Salesforce, SalesforceLogin
8
  from dotenv import load_dotenv
9
- import xml.etree.ElementTree as ET
10
 
11
  # ---------- Load Environment Variables ----------
12
  load_dotenv()
@@ -36,7 +35,7 @@ severities = {
36
  "Best Practice": "Low"
37
  }
38
 
39
- # ---------- Mock Knowledge Base ----------
40
  salesforce_knowledge_base = {
41
  "governor limits soql": "In Salesforce, the governor limit for SOQL queries is 100 per synchronous transaction and 200 per asynchronous transaction.",
42
  "governor limits dml": "The governor limit for DML statements is 150 per transaction.",
@@ -105,18 +104,7 @@ def analyze_code(code):
105
 
106
  return issue_type, suggestion, severity
107
 
108
- # ---------- Helpers for Metadata XML ----------
109
- def _strip_code_fences(text: str) -> str:
110
- # Remove ```xml ... ``` or plain ``` ... ```
111
- t = re.sub(r'^\s*```(?:xml)?\s*', '', text, flags=re.IGNORECASE)
112
- t = re.sub(r'\s*```\s*$', '', t)
113
- return t
114
-
115
- def _escape_bare_ampersands(text: str) -> str:
116
- # Replace & that aren't valid entities with &
117
- return re.sub(r'&(?!amp;|lt;|gt;|apos;|quot;|#\d+;|#x[0-9a-fA-F]+;)', '&', text)
118
-
119
- # ---------- Metadata Validator (handles non-XML gracefully) ----------
120
  def validate_metadata(metadata, admin_id=None):
121
  if not metadata.strip():
122
  return "No metadata provided.", "", ""
@@ -125,40 +113,22 @@ def validate_metadata(metadata, admin_id=None):
125
  issue = "Unknown"
126
  recommendation = "No recommendation found."
127
 
128
- # Preview to help diagnose pastes
129
- preview = metadata[:100].replace("\n", "\\n")
130
- log_to_console({"preview": preview}, "Metadata Input Preview")
131
-
132
- cleaned = _strip_code_fences(metadata).lstrip("\ufeff").strip()
133
 
134
- # Only treat as XML if the first non-space character is '<'
135
- if not cleaned or not cleaned.lstrip().startswith("<"):
136
- issue = "Invalid Input (Not XML)"
137
- recommendation = (
138
- "Please paste Salesforce metadata XML (starts with '<'). Example:\n"
139
- "<CustomField xmlns=\"http://soap.sforce.com/2006/04/metadata\">...</CustomField>"
140
- )
141
- else:
142
- cleaned = _escape_bare_ampersands(cleaned)
143
- try:
144
- root = ET.fromstring(cleaned)
145
- # Detect <description> regardless of namespace
146
- has_description = any(elem.tag.split('}')[-1].lower() == "description" for elem in root.iter())
147
 
148
- if not has_description:
149
- issue = "Missing description"
150
- recommendation = "Add a meaningful <description> to improve maintainability and clarity."
151
- else:
152
- issue = "Unused field detected"
153
- recommendation = "Remove it to improve performance or document its purpose."
154
- except ET.ParseError as pe:
155
- issue = "Invalid XML"
156
- recommendation = f"Could not parse metadata XML. Error: {pe}."
157
- except Exception as e:
158
- issue = "Validation Error"
159
- recommendation = f"Unexpected error while validating metadata: {str(e)}"
160
 
161
- # Log to Salesforce
162
  log_data = {
163
  "Name": f"MetadataLog_{mtype}",
164
  "MetadataType__c": mtype,
@@ -166,6 +136,7 @@ def validate_metadata(metadata, admin_id=None):
166
  "Recommendation__c": recommendation,
167
  "Status__c": "Open"
168
  }
 
169
  if admin_id:
170
  log_data["Admin__c"] = admin_id
171
 
@@ -185,71 +156,50 @@ def validate_metadata(metadata, admin_id=None):
185
 
186
  return mtype, issue, recommendation
187
 
188
- # ---------- Salesforce Chatbot (messages format, no tip line) ----------
189
- conversation_history = [] # optional global memory
190
 
191
- def salesforce_chatbot(query: str, chat_messages: list):
192
- """
193
- query: latest user question string
194
- chat_messages: list[{"role":"user"|"assistant","content":str}] from the UI state
195
- """
196
  global conversation_history
197
- if not query or not query.strip():
198
  return "Please provide a valid Salesforce-related question."
199
 
200
  salesforce_keywords = [
201
  "apex", "soql", "trigger", "lwc", "aura", "visualforce", "salesforce", "governor limits",
202
- "dml", "metadata", "batch apex", "queueable", "future method", "api", "sfdc", "heap", "limits"
203
  ]
204
- if not any(k in query.lower() for k in salesforce_keywords):
205
- return "Please ask a Salesforce-related question (e.g., Apex, SOQL, LWC, limits, etc)."
206
-
207
- # Build history summary from messages (last few pairs)
208
- chat_messages = chat_messages or []
209
- history_summary = []
210
- for m in chat_messages[-8:]:
211
- prefix = "User" if m.get("role") == "user" else "Assistant"
212
- history_summary.append(f"{prefix}: {m.get('content','')}")
213
- history_summary = "\n".join(history_summary)
214
-
215
- # Quick KB shortcut
216
  query_key = query.lower().strip()
217
  for kb_key, kb_answer in salesforce_knowledge_base.items():
218
  if kb_key in query_key:
219
  conversation_history.append((query, kb_answer))
220
- conversation_history[:] = conversation_history[-6:]
221
  log_to_console({"Question": query, "Answer": kb_answer}, "Chatbot Query")
222
  return kb_answer
223
 
 
224
  prompt = f"""
225
- You are an expert Salesforce developer and certified architect. Provide accurate, clear, and practical answers for topics like Apex, SOQL, LWC, governor limits, triggers, metadata, and more.
226
-
227
- When answering:
228
- - ALWAYS give at least 2 lines of explanation.
229
- - Be clear, concise, and technically correct.
230
- - Mention official Salesforce limits if applicable.
231
- - Use bullet points or code snippets when helpful.
232
- - Avoid speculation — if unknown, say so and suggest Trailhead or official docs.
233
- - Examples must be realistic and follow best practices.
234
 
235
  Conversation History:
236
  {history_summary}
237
 
238
- User: {query.strip()}
239
- Assistant:
240
  """
241
 
242
  try:
243
  result = qa_pipeline(prompt, max_new_tokens=1024, do_sample=False, temperature=0.1, top_k=50)
244
  output = result[0]["generated_text"].strip()
245
- if output.startswith("Assistant:"):
246
- output = output.replace("Assistant:", "").strip()
247
-
248
- # NOTE: No extra "Tip:" or any appended lines here.
249
- # We rely purely on the model output (prompt encourages 2+ lines).
250
-
251
  conversation_history.append((query, output))
252
- conversation_history[:] = conversation_history[-6:]
253
  log_to_console({"Question": query, "Answer": output}, "Chatbot Query")
254
  return output
255
  except Exception as e:
@@ -259,7 +209,6 @@ Assistant:
259
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
260
  gr.Markdown("# 🤖 Advanced Salesforce AI Code Review & Chatbot")
261
 
262
- # Code Review
263
  with gr.Tab("Code Review"):
264
  code_input = gr.Textbox(label="Apex / LWC Code", lines=8, placeholder="Enter your Apex or LWC code here")
265
  issue_type = gr.Textbox(label="Issue Type")
@@ -268,7 +217,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
268
  code_button = gr.Button("Analyze Code")
269
  code_button.click(analyze_code, inputs=code_input, outputs=[issue_type, suggestion, severity])
270
 
271
- # Metadata Validation
272
  with gr.Tab("Metadata Validation"):
273
  metadata_input = gr.Textbox(label="Metadata XML", lines=8, placeholder="Enter your metadata XML here")
274
  mtype = gr.Textbox(label="Type")
@@ -277,30 +225,24 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
277
  metadata_button = gr.Button("Validate Metadata")
278
  metadata_button.click(validate_metadata, inputs=metadata_input, outputs=[mtype, issue, recommendation])
279
 
280
- # Salesforce Chatbot (messages format)
281
  with gr.Tab("Salesforce Chatbot"):
282
- chatbot_output = gr.Chatbot(label="Conversation History", height=400, type="messages")
283
- query_input = gr.Textbox(label="Your Question", placeholder="e.g., What is heap size in Apex?")
284
  with gr.Row():
285
  chatbot_button = gr.Button("Ask")
286
  clear_button = gr.Button("Clear Chat")
287
-
288
- # Messages state: list of {"role","content"}
289
  chat_state = gr.State(value=[])
290
 
291
- def update_chatbot(query, chat_messages):
292
- if not query or not query.strip():
293
- return chat_messages, "Please enter a valid question."
294
- # Get model answer
295
- answer = salesforce_chatbot(query, chat_messages)
296
- # Append messages in OpenAI style
297
- chat_messages = (chat_messages or []) + [
298
- {"role": "user", "content": query},
299
- {"role": "assistant", "content": answer},
300
- ]
301
- return chat_messages, ""
302
 
303
  def clear_chat():
 
 
304
  return [], ""
305
 
306
  chatbot_button.click(fn=update_chatbot, inputs=[query_input, chat_state], outputs=[chatbot_output, query_input])
 
1
  import os
2
  import random
 
3
  import gradio as gr
4
  from datetime import datetime
5
  from transformers import pipeline
6
  from simple_salesforce import Salesforce, SalesforceLogin
7
  from dotenv import load_dotenv
8
+ import xml.etree.ElementTree as ET # NEW: for parsing metadata
9
 
10
  # ---------- Load Environment Variables ----------
11
  load_dotenv()
 
35
  "Best Practice": "Low"
36
  }
37
 
38
+ # ---------- Knowledge Base ----------
39
  salesforce_knowledge_base = {
40
  "governor limits soql": "In Salesforce, the governor limit for SOQL queries is 100 per synchronous transaction and 200 per asynchronous transaction.",
41
  "governor limits dml": "The governor limit for DML statements is 150 per transaction.",
 
104
 
105
  return issue_type, suggestion, severity
106
 
107
+ # ---------- Metadata Validator (Updated with dynamic XML parsing) ----------
 
 
 
 
 
 
 
 
 
 
 
108
  def validate_metadata(metadata, admin_id=None):
109
  if not metadata.strip():
110
  return "No metadata provided.", "", ""
 
113
  issue = "Unknown"
114
  recommendation = "No recommendation found."
115
 
116
+ try:
117
+ root = ET.fromstring(metadata)
118
+ # Detect missing <description> tag
119
+ description_found = any(elem.tag.endswith('description') for elem in root)
 
120
 
121
+ if not description_found:
122
+ issue = "Missing description"
123
+ recommendation = "Add a meaningful <description> to improve maintainability and clarity."
124
+ else:
125
+ issue = "Unused field detected"
126
+ recommendation = "Remove it to improve performance or document its purpose."
 
 
 
 
 
 
 
127
 
128
+ except Exception as e:
129
+ issue = "Invalid XML"
130
+ recommendation = f"Could not parse metadata XML. Error: {str(e)}"
 
 
 
 
 
 
 
 
 
131
 
 
132
  log_data = {
133
  "Name": f"MetadataLog_{mtype}",
134
  "MetadataType__c": mtype,
 
136
  "Recommendation__c": recommendation,
137
  "Status__c": "Open"
138
  }
139
+
140
  if admin_id:
141
  log_data["Admin__c"] = admin_id
142
 
 
156
 
157
  return mtype, issue, recommendation
158
 
159
+ # ---------- Salesforce Chatbot ----------
160
+ conversation_history = []
161
 
162
+ def salesforce_chatbot(query, history=[]):
 
 
 
 
163
  global conversation_history
164
+ if not query.strip():
165
  return "Please provide a valid Salesforce-related question."
166
 
167
  salesforce_keywords = [
168
  "apex", "soql", "trigger", "lwc", "aura", "visualforce", "salesforce", "governor limits",
169
+ "dml", "metadata", "batch apex", "queueable", "future method", "api", "sfdc"
170
  ]
171
+
172
+ if not any(keyword.lower() in query.lower() for keyword in salesforce_keywords):
173
+ return "Please ask a Salesforce-related question."
174
+
 
 
 
 
 
 
 
 
175
  query_key = query.lower().strip()
176
  for kb_key, kb_answer in salesforce_knowledge_base.items():
177
  if kb_key in query_key:
178
  conversation_history.append((query, kb_answer))
179
+ conversation_history = conversation_history[-6:]
180
  log_to_console({"Question": query, "Answer": kb_answer}, "Chatbot Query")
181
  return kb_answer
182
 
183
+ history_summary = "\n".join([f"User: {q}\nAssistant: {a}" for q, a in conversation_history[-4:]])
184
  prompt = f"""
185
+ You are an expert Salesforce developer...
 
 
 
 
 
 
 
 
186
 
187
  Conversation History:
188
  {history_summary}
189
 
190
+ Question: {query.strip()}
191
+ Answer:
192
  """
193
 
194
  try:
195
  result = qa_pipeline(prompt, max_new_tokens=1024, do_sample=False, temperature=0.1, top_k=50)
196
  output = result[0]["generated_text"].strip()
197
+ if output.startswith("Answer:"):
198
+ output = output[7:].strip()
199
+ if len(output) < 20:
200
+ output = f"I'm sorry, I couldn't find a precise answer for '{query}'. Please refer to Salesforce docs."
 
 
201
  conversation_history.append((query, output))
202
+ conversation_history = conversation_history[-6:]
203
  log_to_console({"Question": query, "Answer": output}, "Chatbot Query")
204
  return output
205
  except Exception as e:
 
209
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
210
  gr.Markdown("# 🤖 Advanced Salesforce AI Code Review & Chatbot")
211
 
 
212
  with gr.Tab("Code Review"):
213
  code_input = gr.Textbox(label="Apex / LWC Code", lines=8, placeholder="Enter your Apex or LWC code here")
214
  issue_type = gr.Textbox(label="Issue Type")
 
217
  code_button = gr.Button("Analyze Code")
218
  code_button.click(analyze_code, inputs=code_input, outputs=[issue_type, suggestion, severity])
219
 
 
220
  with gr.Tab("Metadata Validation"):
221
  metadata_input = gr.Textbox(label="Metadata XML", lines=8, placeholder="Enter your metadata XML here")
222
  mtype = gr.Textbox(label="Type")
 
225
  metadata_button = gr.Button("Validate Metadata")
226
  metadata_button.click(validate_metadata, inputs=metadata_input, outputs=[mtype, issue, recommendation])
227
 
 
228
  with gr.Tab("Salesforce Chatbot"):
229
+ chatbot_output = gr.Chatbot(label="Conversation History", height=400)
230
+ query_input = gr.Textbox(label="Your Question", placeholder="e.g., How do I bulkify an Apex trigger?")
231
  with gr.Row():
232
  chatbot_button = gr.Button("Ask")
233
  clear_button = gr.Button("Clear Chat")
 
 
234
  chat_state = gr.State(value=[])
235
 
236
+ def update_chatbot(query, chat_history):
237
+ if not query.strip():
238
+ return chat_history, "Please enter a valid question."
239
+ response = salesforce_chatbot(query, chat_history)
240
+ chat_history.append((query, response))
241
+ return chat_history, ""
 
 
 
 
 
242
 
243
  def clear_chat():
244
+ global conversation_history
245
+ conversation_history = []
246
  return [], ""
247
 
248
  chatbot_button.click(fn=update_chatbot, inputs=[query_input, chat_state], outputs=[chatbot_output, query_input])