Bhanumani12 commited on
Commit
60d0010
·
verified ·
1 Parent(s): 53e9bb8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -37
app.py CHANGED
@@ -114,13 +114,9 @@ def _strip_code_fences(text: str) -> str:
114
 
115
  def _escape_bare_ampersands(text: str) -> str:
116
  # Replace & that aren't valid entities with &
117
- return re.sub(
118
- r'&(?!amp;|lt;|gt;|apos;|quot;|#\d+;|#x[0-9a-fA-F]+;)',
119
- '&',
120
- text
121
- )
122
 
123
- # ---------- Metadata Validator (updated to handle non-XML gracefully) ----------
124
  def validate_metadata(metadata, admin_id=None):
125
  if not metadata.strip():
126
  return "No metadata provided.", "", ""
@@ -129,11 +125,10 @@ def validate_metadata(metadata, admin_id=None):
129
  issue = "Unknown"
130
  recommendation = "No recommendation found."
131
 
132
- # Log a short preview to help diagnose pastes
133
  preview = metadata[:100].replace("\n", "\\n")
134
  log_to_console({"preview": preview}, "Metadata Input Preview")
135
 
136
- # Clean typical paste artefacts
137
  cleaned = _strip_code_fences(metadata).lstrip("\ufeff").strip()
138
 
139
  # Only treat as XML if the first non-space character is '<'
@@ -144,16 +139,11 @@ def validate_metadata(metadata, admin_id=None):
144
  "<CustomField xmlns=\"http://soap.sforce.com/2006/04/metadata\">...</CustomField>"
145
  )
146
  else:
147
- # Escape stray ampersands (common XML error)
148
  cleaned = _escape_bare_ampersands(cleaned)
149
-
150
  try:
151
  root = ET.fromstring(cleaned)
152
  # Detect <description> regardless of namespace
153
- has_description = any(
154
- elem.tag.split('}')[-1].lower() == "description"
155
- for elem in root.iter()
156
- )
157
 
158
  if not has_description:
159
  issue = "Missing description"
@@ -161,7 +151,6 @@ def validate_metadata(metadata, admin_id=None):
161
  else:
162
  issue = "Unused field detected"
163
  recommendation = "Remove it to improve performance or document its purpose."
164
-
165
  except ET.ParseError as pe:
166
  issue = "Invalid XML"
167
  recommendation = f"Could not parse metadata XML. Error: {pe}."
@@ -169,7 +158,7 @@ def validate_metadata(metadata, admin_id=None):
169
  issue = "Validation Error"
170
  recommendation = f"Unexpected error while validating metadata: {str(e)}"
171
 
172
- # Log to Salesforce (unchanged)
173
  log_data = {
174
  "Name": f"MetadataLog_{mtype}",
175
  "MetadataType__c": mtype,
@@ -196,34 +185,45 @@ def validate_metadata(metadata, admin_id=None):
196
 
197
  return mtype, issue, recommendation
198
 
199
- # ---------- Salesforce Chatbot (Updated Prompt) ----------
200
- conversation_history = []
201
 
202
- def salesforce_chatbot(query, history=[]):
 
 
 
 
203
  global conversation_history
204
- if not query.strip():
205
  return "Please provide a valid Salesforce-related question."
206
 
207
  salesforce_keywords = [
208
  "apex", "soql", "trigger", "lwc", "aura", "visualforce", "salesforce", "governor limits",
209
  "dml", "metadata", "batch apex", "queueable", "future method", "api", "sfdc", "heap", "limits"
210
  ]
211
-
212
- if not any(keyword.lower() in query.lower() for keyword in salesforce_keywords):
213
  return "Please ask a Salesforce-related question (e.g., Apex, SOQL, LWC, limits, etc)."
214
 
 
 
 
 
 
 
 
 
 
215
  query_key = query.lower().strip()
216
  for kb_key, kb_answer in salesforce_knowledge_base.items():
217
  if kb_key in query_key:
218
  conversation_history.append((query, kb_answer))
219
- conversation_history = conversation_history[-6:]
220
  log_to_console({"Question": query, "Answer": kb_answer}, "Chatbot Query")
221
  return kb_answer
222
 
223
- history_summary = "\n".join([f"User: {q}\nAssistant: {a}" for q, a in conversation_history[-4:]])
224
-
225
  prompt = f"""
226
- You are an expert Salesforce developer and certified architect. You provide 100% accurate, clear, and practical answers for topics like Apex, SOQL, LWC, governor limits, triggers, metadata, and more.
 
227
  When answering:
228
  - ALWAYS give at least 2 lines of explanation.
229
  - Be clear, concise, and technically correct.
@@ -231,8 +231,10 @@ When answering:
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
  Conversation History:
235
  {history_summary}
 
236
  User: {query.strip()}
237
  Assistant:
238
  """
@@ -243,11 +245,13 @@ Assistant:
243
  if output.startswith("Assistant:"):
244
  output = output.replace("Assistant:", "").strip()
245
 
246
- if len(output) < 20:
247
- output = f"I'm sorry, I couldn't generate a detailed answer for '{query}'. You can also check https://developer.salesforce.com/docs."
 
 
248
 
249
  conversation_history.append((query, output))
250
- conversation_history = conversation_history[-6:]
251
  log_to_console({"Question": query, "Answer": output}, "Chatbot Query")
252
  return output
253
  except Exception as e:
@@ -257,6 +261,7 @@ Assistant:
257
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
258
  gr.Markdown("# 🤖 Advanced Salesforce AI Code Review & Chatbot")
259
 
 
260
  with gr.Tab("Code Review"):
261
  code_input = gr.Textbox(label="Apex / LWC Code", lines=8, placeholder="Enter your Apex or LWC code here")
262
  issue_type = gr.Textbox(label="Issue Type")
@@ -265,6 +270,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
265
  code_button = gr.Button("Analyze Code")
266
  code_button.click(analyze_code, inputs=code_input, outputs=[issue_type, suggestion, severity])
267
 
 
268
  with gr.Tab("Metadata Validation"):
269
  metadata_input = gr.Textbox(label="Metadata XML", lines=8, placeholder="Enter your metadata XML here")
270
  mtype = gr.Textbox(label="Type")
@@ -273,24 +279,30 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
273
  metadata_button = gr.Button("Validate Metadata")
274
  metadata_button.click(validate_metadata, inputs=metadata_input, outputs=[mtype, issue, recommendation])
275
 
 
276
  with gr.Tab("Salesforce Chatbot"):
277
  chatbot_output = gr.Chatbot(label="Conversation History", height=400, type="messages")
278
  query_input = gr.Textbox(label="Your Question", placeholder="e.g., What is heap size in Apex?")
279
  with gr.Row():
280
  chatbot_button = gr.Button("Ask")
281
  clear_button = gr.Button("Clear Chat")
 
 
282
  chat_state = gr.State(value=[])
283
 
284
- def update_chatbot(query, chat_history):
285
- if not query.strip():
286
- return chat_history, "Please enter a valid question."
287
- response = salesforce_chatbot(query, chat_history)
288
- chat_history.append((query, response))
289
- return chat_history, ""
 
 
 
 
 
290
 
291
  def clear_chat():
292
- global conversation_history
293
- conversation_history = []
294
  return [], ""
295
 
296
  chatbot_button.click(fn=update_chatbot, inputs=[query_input, chat_state], outputs=[chatbot_output, query_input])
 
114
 
115
  def _escape_bare_ampersands(text: str) -> str:
116
  # Replace & that aren't valid entities with &amp;
117
+ return re.sub(r'&(?!amp;|lt;|gt;|apos;|quot;|#\d+;|#x[0-9a-fA-F]+;)', '&amp;', 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
  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 '<'
 
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"
 
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}."
 
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,
 
185
 
186
  return mtype, issue, recommendation
187
 
188
+ # ---------- Salesforce Chatbot (messages format, 2+ line answers) ----------
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.
 
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
  """
 
245
  if output.startswith("Assistant:"):
246
  output = output.replace("Assistant:", "").strip()
247
 
248
+ # Enforce 2+ meaningful lines
249
+ if len(output.split()) < 20:
250
+ output = (output + "\n\n"
251
+ "Tip: You can verify limits and examples in the official docs at https://developer.salesforce.com/docs.")
252
 
253
  conversation_history.append((query, output))
254
+ conversation_history[:] = conversation_history[-6:]
255
  log_to_console({"Question": query, "Answer": output}, "Chatbot Query")
256
  return output
257
  except Exception as e:
 
261
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
262
  gr.Markdown("# 🤖 Advanced Salesforce AI Code Review & Chatbot")
263
 
264
+ # Code Review
265
  with gr.Tab("Code Review"):
266
  code_input = gr.Textbox(label="Apex / LWC Code", lines=8, placeholder="Enter your Apex or LWC code here")
267
  issue_type = gr.Textbox(label="Issue Type")
 
270
  code_button = gr.Button("Analyze Code")
271
  code_button.click(analyze_code, inputs=code_input, outputs=[issue_type, suggestion, severity])
272
 
273
+ # Metadata Validation
274
  with gr.Tab("Metadata Validation"):
275
  metadata_input = gr.Textbox(label="Metadata XML", lines=8, placeholder="Enter your metadata XML here")
276
  mtype = gr.Textbox(label="Type")
 
279
  metadata_button = gr.Button("Validate Metadata")
280
  metadata_button.click(validate_metadata, inputs=metadata_input, outputs=[mtype, issue, recommendation])
281
 
282
+ # Salesforce Chatbot (messages format)
283
  with gr.Tab("Salesforce Chatbot"):
284
  chatbot_output = gr.Chatbot(label="Conversation History", height=400, type="messages")
285
  query_input = gr.Textbox(label="Your Question", placeholder="e.g., What is heap size in Apex?")
286
  with gr.Row():
287
  chatbot_button = gr.Button("Ask")
288
  clear_button = gr.Button("Clear Chat")
289
+
290
+ # Messages state: list of {"role","content"}
291
  chat_state = gr.State(value=[])
292
 
293
+ def update_chatbot(query, chat_messages):
294
+ if not query or not query.strip():
295
+ return chat_messages, "Please enter a valid question."
296
+ # Get model answer
297
+ answer = salesforce_chatbot(query, chat_messages)
298
+ # Append messages in OpenAI style
299
+ chat_messages = (chat_messages or []) + [
300
+ {"role": "user", "content": query},
301
+ {"role": "assistant", "content": answer},
302
+ ]
303
+ return chat_messages, ""
304
 
305
  def clear_chat():
 
 
306
  return [], ""
307
 
308
  chatbot_button.click(fn=update_chatbot, inputs=[query_input, chat_state], outputs=[chatbot_output, query_input])