File size: 11,118 Bytes
5f09953
2487f72
7e98397
2ee69ab
267e149
2ee69ab
24214ac
 
 
 
 
 
 
5f09953
7e98397
 
 
 
5f09953
10165e1
5f09953
 
 
 
 
 
2487f72
2ee69ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10165e1
2ee69ab
 
 
 
 
2487f72
2ee69ab
7e98397
 
 
 
2ee69ab
4b46cbe
69f0a3f
 
6ba9216
5f09953
 
 
 
2487f72
7e98397
 
 
 
 
 
 
 
4b46cbe
12c092b
2487f72
2ee69ab
2487f72
 
 
 
 
 
7e98397
 
 
 
 
 
 
 
 
 
2487f72
 
 
7e98397
 
 
 
 
2487f72
2ee69ab
2487f72
2ee69ab
7e98397
 
 
 
 
2ee69ab
 
 
7e98397
2ee69ab
 
8b0b583
2ee69ab
 
 
 
 
 
 
 
 
 
 
10165e1
7e98397
2ee69ab
8b0b583
2ee69ab
 
 
 
 
 
 
 
7e98397
 
 
10165e1
8b0b583
 
10165e1
2487f72
5f09953
2ee69ab
 
7e98397
 
2ee69ab
 
 
 
 
 
 
 
7e98397
 
2ee69ab
7e98397
 
 
 
 
5f09953
2ee69ab
7e98397
 
2487f72
 
2ee69ab
7e98397
2487f72
 
7e98397
2487f72
 
 
 
 
 
 
7e98397
2487f72
 
 
 
 
 
8b0b583
2ee69ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2487f72
5f09953
 
f03fd62
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
import random
import gradio as gr
from datetime import datetime
from transformers import pipeline

# ---------- Label Mapping (Unchanged) ----------
label_to_issue_type = {
    "LABEL_0": "Performance",
    "LABEL_1": "Error",
    "LABEL_2": "Security",
    "LABEL_3": "Best Practice"
}

suggestions = {
    "Performance": "Consider optimizing loops and database access. Use collections to reduce SOQL queries.",
    "Error": "Add proper error handling and null checks. Use try-catch blocks effectively.",
    "Security": "Avoid dynamic SOQL. Use binding variables to prevent SOQL injection.",
    "Best Practice": "Refactor for readability and use bulk-safe patterns, such as processing records in batches."
}

severities = {
    "Performance": "Medium",
    "Error": "High",
    "Security": "High",
    "Best Practice": "Low"
}

# ---------- Mock Salesforce Knowledge Base ----------
salesforce_knowledge_base = {
    "governor limits soql": "In Salesforce, the governor limit for SOQL queries is 100 per synchronous transaction and 200 per asynchronous transaction (e.g., Batch Apex, Queueable).",
    "governor limits dml": "The governor limit for DML statements is 150 per synchronous or asynchronous transaction.",
    "bulkify apex trigger": """
To bulkify an Apex trigger, ensure it handles multiple records to stay within governor limits:
- Use collections (e.g., Lists, Sets, Maps) to process records.
- Perform SOQL queries outside loops.
- Execute DML operations in bulk.
Example:
```apex
trigger AccountTrigger on Account (before insert) {
    Set<String> accountNames = new Set<String>();
    for (Account acc : Trigger.new) {
        accountNames.add(acc.Name);
    }
    List<Account> existingAccounts = [SELECT Name FROM Account WHERE Name IN :accountNames];
    // Process records
}
```""",
    "soql injection": """
To prevent SOQL injection:
- Use bind variables (e.g., `:variable`) instead of dynamic SOQL.
- Escape single quotes with `String.escapeSingleQuotes()`.
Example:
```apex
String userInput = 'Test';
List<Account> accounts = [SELECT Name FROM Account WHERE Name = :userInput];
```""",
    "lwc best practices": """
LWC best practices include:
- Use `@api` decorators for public properties.
- Leverage `@wire` for efficient data retrieval.
- Avoid hardcoding IDs; use dynamic queries.
- Follow SLDS for consistent UI.
Example:
```javascript
import { LightningElement, api } from 'lwc';
export default class MyComponent extends LightningElement {
    @api recordId;
}
```""",
    "batch apex": """
Batch Apex processes records in chunks to handle large data volumes:
- Implement `Database.Batchable<SObject>`.
- Use `start`, `execute`, and `finish` methods.
Example:
```apex
global class MyBatch implements Database.Batchable<SObject> {
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator('SELECT Id FROM Account');
    }
    global void execute(Database.BatchableContext bc, List<SObject> scope) {
        // Process records
    }
    global void finish(Database.BatchableContext bc) {}
}
```""",
}

# ---------- Load QnA Model ----------
try:
    qa_pipeline = pipeline("text2text-generation", model="google/flan-t5-large")
except Exception as e:
    print(f"Model loading error: {e}. Falling back to flan-t5-base.")
    qa_pipeline = pipeline("text2text-generation", model="google/flan-t5-base")

# ---------- Local Logging (Unchanged) ----------
def log_to_console(data, log_type):
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{timestamp}] {log_type} Log: {data}")

# ---------- Code Analyzer (Unchanged) ----------
def analyze_code(code):
    if not code.strip():
        return "No code provided.", "", ""

    label = random.choice(list(label_to_issue_type.keys()))
    issue_type = label_to_issue_type[label]
    suggestion = suggestions[issue_type]
    severity = severities[issue_type]

    log_data = {
        "Name": f"Review_{issue_type}",
        "CodeSnippet": code,
        "IssueType": issue_type,
        "Suggestion": suggestion,
        "Severity": severity
    }
    log_to_console(log_data, "Code Review")

    return issue_type, suggestion, severity

# ---------- Metadata Validator (Unchanged) ----------
def validate_metadata(metadata):
    if not metadata.strip():
        return "No metadata provided.", "", ""

    mtype = "Field"
    issue = "Unused field detected"
    recommendation = "Remove it to improve performance or document its purpose."

    log_data = {
        "Name": f"MetadataLog_{mtype}",
        "MetadataType": mtype,
        "IssueDescription": issue,
        "Recommendation": recommendation,
        "Status": "Open"
    }
    log_to_console(log_data, "Metadata Validation")

    return mtype, issue, recommendation

# ---------- Advanced Salesforce Chatbot ----------
conversation_history = []

def salesforce_chatbot(query, history=[]):
    global conversation_history
    if not query.strip():
        return "Please provide a valid Salesforce-related question."

    # Expanded Salesforce keywords
    salesforce_keywords = [
        "apex", "soql", "sosl", "trigger", "lwc", "aura", "visualforce", "salesforce",
        "governor limits", "bulkification", "dml", "query", "metadata", "flow",
        "process builder", "sobject", "schema", "lightning", "custom object",
        "validation rule", "workflow", "platform event", "batch apex", "queueable",
        "future method", "lightning web component", "api", "rest", "soap", "integration",
        "trigger", "profile", "permission set", "sharing rule", "field", "record type",
        "crm", "sfdc", "force.com"
    ]

    # Check if query is Salesforce-related
    if not any(keyword.lower() in query.lower() for keyword in salesforce_keywords):
        return "Please ask a Salesforce-related question (e.g., about Apex, SOQL, LWC, or Salesforce platform features)."

    # Check knowledge base for exact matches
    query_key = query.lower().strip()
    for kb_key, kb_answer in salesforce_knowledge_base.items():
        if kb_key in query_key:
            # Store in conversation history
            conversation_history.append((query, kb_answer))
            conversation_history = conversation_history[-6:]  # Keep last 6 exchanges
            log_to_console({"Question": query, "Answer": kb_answer}, "Chatbot Query")
            return kb_answer

    # Build conversation context
    history_summary = "\n".join([f"User: {q}\nAssistant: {a}" for q, a in conversation_history[-4:]])
    prompt = f"""
You are an expert Salesforce developer with deep knowledge of Apex, SOQL, SOSL, LWC, Aura, Visualforce, and Salesforce platform features. Your role is to provide 100% accurate answers based strictly on Salesforce official documentation and best practices (e.g., governor limits: 100 SOQL queries, 150 DML statements per transaction). Follow these guidelines:
- Provide concise, accurate answers with no speculation.
- Include code snippets in ```apex``` or ```javascript``` blocks for technical questions.
- Reference governor limits or best practices explicitly.
- If the question is ambiguous, ask for clarification within the response.
- Use bullet points for lists or steps.
- Leverage conversation history for context.
- If unsure, admit limitations and suggest checking Salesforce documentation or Trailhead.

Conversation History:
{history_summary}

Question: {query.strip()}
Answer:
"""

    try:
        # Generate response with strict parameters
        result = qa_pipeline(prompt, max_new_tokens=1024, do_sample=False, temperature=0.1, top_k=50)
        output = result[0]["generated_text"].strip()

        # Clean up response
        if output.startswith("Answer:"):
            output = output[7:].strip()

        # Validate response quality
        if len(output) < 20 or output.lower() in ["unknown", "i don't know", "not sure"]:
            output = f"I'm sorry, I couldn't find a precise answer for '{query}'. Please clarify or refer to Salesforce documentation at https://developer.salesforce.com/docs."

        # Store in conversation history
        conversation_history.append((query, output))
        conversation_history = conversation_history[-6:]  # Keep last 6 exchanges

        # Log question and answer
        log_to_console({"Question": query, "Answer": output}, "Chatbot Query")

        return output
    except Exception as e:
        error_msg = f"⚠️ Error generating response: {str(e)}. Please try rephrasing your question or check Salesforce documentation at https://developer.salesforce.com/docs."
        log_to_console({"Question": query, "Error": error_msg}, "Chatbot Error")
        return error_msg

# ---------- Gradio UI ----------
with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 🤖 Advanced Salesforce AI Code Review & Chatbot")

    with gr.Tab("Code Review"):
        code_input = gr.Textbox(label="Apex / LWC Code", lines=8, placeholder="Enter your Apex or LWC code here")
        issue_type = gr.Textbox(label="Issue Type")
        suggestion = gr.Textbox(label="AI Suggestion")
        severity = gr.Textbox(label="Severity")
        code_button = gr.Button("Analyze Code")
        code_button.click(analyze_code, inputs=code_input, outputs=[issue_type, suggestion, severity])

    with gr.Tab("Metadata Validation"):
        metadata_input = gr.Textbox(label="Metadata XML", lines=8, placeholder="Enter your metadata XML here")
        mtype = gr.Textbox(label="Type")
        issue = gr.Textbox(label="Issue")
        recommendation = gr.Textbox(label="Recommendation")
        metadata_button = gr.Button("Validate Metadata")
        metadata_button.click(validate_metadata, inputs=metadata_input, outputs=[mtype, issue, recommendation])

    with gr.Tab("Salesforce Chatbot"):
        gr.Markdown("### Ask a Salesforce Question\nGet expert answers on Apex, SOQL, LWC, and more!")
        chatbot_output = gr.Chatbot(label="Conversation History", height=400)
        query_input = gr.Textbox(label="Your Question", placeholder="e.g., How do I bulkify an Apex trigger?")
        with gr.Row():
            chatbot_button = gr.Button("Ask")
            clear_button = gr.Button("Clear Chat")
        
        # State to manage chat history
        chat_state = gr.State(value=[])

        def update_chatbot(query, chat_history):
            if not query.strip():
                return chat_history, "Please enter a valid question."
            response = salesforce_chatbot(query, chat_history)
            chat_history.append((query, response))
            return chat_history, ""

        def clear_chat():
            global conversation_history
            conversation_history = []
            return [], ""

        chatbot_button.click(
            fn=update_chatbot,
            inputs=[query_input, chat_state],
            outputs=[chatbot_output, query_input]
        )
        clear_button.click(
            fn=clear_chat,
            inputs=None,
            outputs=[chatbot_output, query_input]
        )

# ---------- Start UI ----------
if __name__ == "__main__":
    demo.launch()