Invoke Salesforce Prompt Template from LWC using Apex

Invoke Salesforce Prompt Template from LWC using Apex

How to Invoke a Salesforce Prompt Template from a Lightning Web Component (LWC)?

With the rise of Einstein GenAI, developers are looking for ways to bring the power of Generative AI directly into custom user interfaces. While Salesforce provides robust tools like Prompt Builder, there is currently a technical limitation: you cannot invoke a Prompt Template directly from a Lightning Web Component (LWC).

However, there is a powerful workaround. By using Apex as a bridge, you can trigger these templates and surface AI-generated responses within your custom LWC.

In this guide, we will walk through the architecture and code required to invoke a GenAI Prompt Template from an LWC.


The Architecture: LWC ↔ Apex ↔ Prompt Template

Since LWCs cannot call the ConnectApi for Einstein LLM directly, the flow follows these steps:

  1. LWC: Captures user input and calls an Apex method.
  2. Apex: Uses the ConnectApi.EinsteinLLM class to provide input to the Prompt Template and generate a response.
  3. LWC: Receives the string response from Apex and renders it for the user.

1. The Apex Controller

The following Apex class, KnowledgeBasePromptController, uses the ConnectApi to invoke a specific template named Knowledge_Base.

/**
 * @description Apex controller to invoke the GenAI Prompt Template 'Knowledge_Base' with user-provided input
 * and return the rendered output for Experience Cloud users.
 * Enforces sharing, FLS, and safe error handling for LWC usage.
 */
public with sharing class KnowledgeBasePromptController {

    /**
     * @description Invokes the Knowledge_Base GenAI Prompt Template with the provided input (strText)
     * and returns the rendered content as a String. Designed for Experience Cloud users.
     * Method is non-cacheable due to server-side execution and user input.
     * @param strText String user input that will be passed to the prompt template variable 'strText'
     * @return String Rendered content from the prompt template
     * @throws AuraHandledException if any error occurs during invocation
     */
    @AuraEnabled(cacheable=false)
    public static String invokeKnowledgeBase(String strText) {
        try {
            // Validate input existence to prevent unnecessary API calls
            if (String.isBlank(strText)) {
                return null;
            }

            // Prepare the input value wrapper required by ConnectApi
            ConnectApi.WrappedValue inputValueMap = new ConnectApi.WrappedValue();
            inputValueMap.value = strText;

            // Map the input variable name (Input:strText) to the wrapped value
            Map<String, ConnectApi.WrappedValue> inputParams = new Map<String, ConnectApi.WrappedValue>();
            inputParams.put('Input:strText', inputValueMap);

            // Configure the generation input settings
            ConnectApi.EinsteinPromptTemplateGenerationsInput promptGenerationsInput = new ConnectApi.EinsteinPromptTemplateGenerationsInput();
            promptGenerationsInput.inputParams = inputParams;
            
            // Set additional configuration for the LLM interaction
            promptGenerationsInput.additionalConfig = new ConnectApi.EinsteinLlmAdditionalConfigInput();
            promptGenerationsInput.additionalConfig.numGenerations = 1;
            promptGenerationsInput.additionalConfig.enablePiiMasking = true;
            promptGenerationsInput.additionalConfig.applicationName = 'PromptTemplateGenerationsInvocable';
            promptGenerationsInput.isPreview = false;

            // Invoke the Einstein LLM service for the 'Knowledge_Base' template
            ConnectApi.EinsteinPromptTemplateGenerationsRepresentation generationsOutput = 
                ConnectApi.EinsteinLLM.generateMessagesForPromptTemplate('Knowledge_Base', promptGenerationsInput);

            // Validate the response to ensure a generation was received
            if (generationsOutput == null || generationsOutput.generations == null || generationsOutput.generations.isEmpty()) {
                throw new AuraHandledException('No generations returned from the prompt template.');
            }

            // Extract the text from the first generation result
            ConnectApi.EinsteinLLMGenerationItemOutput response = generationsOutput.generations[0];
            
            // Log the response for debugging (consider removing for production)
            System.debug(response.text);

            return response.text;

        } catch (Exception e) {
            // Log the actual internal error for backend debugging
            System.debug(LoggingLevel.ERROR, 'Error invoking Knowledge Base: ' + e.getMessage());

            // Throw a generic user-facing error to avoid leaking system details
            // Note: Creating a new exception prevents the raw stack trace from reaching the client
            AuraHandledException ahe = new AuraHandledException(e.getMessage());
            ahe.setMessage(e.getMessage()); 
            throw ahe;
        }
    }
}

2. The Lightning Web Component

The LWC provides a simple interface with a lightning-textarea for the user’s question and a lightning-button to trigger the AI generation.

HTML Template

<template>
    <section class="slds-card slds-p-around_medium">
        <header class="slds-m-bottom_medium">
            <h2 class="slds-text-heading_medium">Knowledge Base</h2>
        </header>
        <div class="slds-grid slds-wrap slds-gutters">
            <div class="slds-col slds-size_1-of-1 slds-m-bottom_small">
                <lightning-textarea
                    name="kbInput"
                    label="Your question"
                    value={userInput}
                    onchange={handleInputChange}
                    placeholder="Type your question...">
                </lightning-textarea>
            </div>
            <div class="slds-col slds-size_1-of-1 slds-m-bottom_small">
                <lightning-button
                    variant="brand"
                    label="Submit"
                    onclick={handleSubmit}
                    disabled={isSubmitDisabled}
                    class="slds-m-right_small">
                </lightning-button>
                <template if:true={isLoading}>
                    <lightning-spinner 
                        size="small" 
                        alternative-text="Loading">
                    </lightning-spinner>
                </template>
            </div>
            <div class="slds-col slds-size_1-of-1">
                <template if:true={errorMessage}>
                    <div class="slds-text-color_error slds-m-vertical_small" role="alert">
                        {errorMessage}
                    </div>
                </template>
                <template if:true={renderedHtml}>
                    <lightning-formatted-text 
                        value={renderedHtml} 
                        class="slds-m-vertical_small">
                    </lightning-formatted-text>
                </template>
            </div>
        </div>
    </section>
</template>

JavaScript Controller

/**
 * @description LWC controller for invoking the Knowledge_Base GenAI Prompt Template via Apex.
 * Accepts user input, calls Apex, and renders the output.
 */
import { LightningElement } from 'lwc';
import invokeKnowledgeBase from '@salesforce/apex/KnowledgeBasePromptController.invokeKnowledgeBase';

export default class KnowledgeBaseInvoker extends LightningElement {
    
    // Component state properties
    userInput = '';
    isLoading = false;
    errorMessage = '';
    renderedHtml = '';

    /**
     * @description Handles input changes for the textarea.
     * Updates the state and clears previous results/errors to reset the UI.
     * @param {Event} event - The change event from lightning-textarea
     */
    handleInputChange(event) {
        // capture value safely
        this.userInput = event.detail.value || '';
        
        // Clear previous output/error when user changes input to provide immediate feedback
        this.errorMessage = '';
        this.renderedHtml = '';
    }

    /**
     * @description Submits the input to Apex and renders the output.
     * Handles the asynchronous call and error states.
     * @returns {Promise<void>}
     */
    async handleSubmit() {
        // Prevent submission if the button should be disabled
        if (this.isSubmitDisabled) {
            return;
        }

        // Set loading state to show spinner
        this.isLoading = true;
        this.errorMessage = '';
        this.renderedHtml = '';

        try {
            // Invoke the Apex method
            const result = await invokeKnowledgeBase({ strText: this.userInput });
            
            // Log result for debugging (Consider removing for production)
            console.log('Raw result from Apex:', JSON.stringify(result));
            
            this.renderedHtml = result;

        } catch (e) {
            // Handle specific Apex errors (LWC specific structure) vs generic JS errors
            const msg = (e && e.body && e.body.message) 
                ? e.body.message 
                : (e && e.message) || 'An unexpected error occurred.';
            
            this.errorMessage = msg;
            
            // Log the full error object for developer debugging
            console.error('Error invoking Knowledge Base:', JSON.stringify(e));

        } finally {
            // Always turn off the spinner regardless of success or failure
            this.isLoading = false;
        }
    }

    /**
     * @description Computed state for disabling the submit button.
     * Ensures we don't submit empty strings or while already loading.
     * @returns {boolean} True if submission should be blocked
     */
    get isSubmitDisabled() {
        return this.isLoading || !this.userInput || this.userInput.trim().length === 0;
    }
}

Configuration (js-meta.xml)

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>65.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Knowledge Base Invoker</masterLabel>
    <description>Invokes the Knowledge_Base GenAI Prompt Template with user input and displays safe HTML. Designed for Experience Cloud.</description>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
        <target>lightning__RecordPage</target>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
</LightningComponentBundle>

Leave a Reply