From 95309584dbb7d4c5185a675877f3f16073cc4500 Mon Sep 17 00:00:00 2001 From: Karan Date: Fri, 21 Nov 2025 09:49:08 +0530 Subject: [PATCH 1/5] Add Base Prompt Rule functionality to Guardrail Policies - Introduced Base Prompt Rule in GuardrailPoliciesAction to auto-populate base prompts based on selected agent collections. - Updated GuardrailPolicies DTO to include BasePromptRule with properties for enabling, auto-detection, and confidence score. - Enhanced CreateGuardrailModal to manage Base Prompt Rule settings, including UI elements for configuration. - Modified GuardrailPolicies component to handle Base Prompt Rule data in the UI. - Added logic to fetch detected base prompts from agent collections. --- .../akto/action/GuardrailPoliciesAction.java | 57 ++++++++++ .../pages/guardrails/GuardrailPolicies.jsx | 2 + .../components/CreateGuardrailModal.jsx | 106 +++++++++++++++++- .../main/java/com/akto/dto/ApiCollection.java | 11 ++ .../java/com/akto/dto/GuardrailPolicies.java | 23 +++- 5 files changed, 192 insertions(+), 7 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java b/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java index 129ccc6717..257bdee030 100644 --- a/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java @@ -1,7 +1,9 @@ package com.akto.action; +import com.akto.dao.ApiCollectionsDao; import com.akto.dao.GuardrailPoliciesDao; import com.akto.dao.context.Context; +import com.akto.dto.ApiCollection; import com.akto.dto.GuardrailPolicies; import com.akto.dto.User; import com.akto.log.LoggerMaker; @@ -10,6 +12,7 @@ import com.mongodb.client.model.Filters; import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.Updates; +import org.apache.commons.lang3.StringUtils; import lombok.Getter; import lombok.Setter; @@ -20,6 +23,7 @@ import java.util.List; import java.util.Map; + public class GuardrailPoliciesAction extends UserAction { private static final LoggerMaker loggerMaker = new LoggerMaker(GuardrailPoliciesAction.class, LogDb.DASHBOARD); @@ -43,6 +47,11 @@ public String fetchGuardrailPolicies() { this.guardrailPolicies = GuardrailPoliciesDao.instance.findAllSortedByCreatedTimestamp(0, 20); this.total = GuardrailPoliciesDao.instance.getTotalCount(); + // Populate basePrompt for policies with autoDetect enabled + for (GuardrailPolicies policy : guardrailPolicies) { + populateBasePromptIfNeeded(policy); + } + loggerMaker.info("Fetched " + guardrailPolicies.size() + " guardrail policies out of " + total + " total"); return SUCCESS.toUpperCase(); @@ -52,6 +61,53 @@ public String fetchGuardrailPolicies() { } } + /** + * Populates basePrompt in basePromptRule if: + * 1. basePromptRule exists and is enabled + * 2. autoDetect is true + * 3. basePrompt is not already set (or is empty) + * 4. There are selected agent servers + * + * Fetches detectedBasePrompt from the first selected agent collection + */ + private void populateBasePromptIfNeeded(GuardrailPolicies policy) { + try { + GuardrailPolicies.BasePromptRule basePromptRule = policy.getBasePromptRule(); + if (basePromptRule == null || !basePromptRule.isEnabled() || !basePromptRule.isAutoDetect()) { + return; + } + + // If basePrompt is already set, use it + if (StringUtils.isNotBlank(basePromptRule.getBasePrompt())) { + return; + } + + // Get selected agent servers (prefer V2 format, fallback to old format) + List agentServers = policy.getEffectiveSelectedAgentServers(); + if (agentServers == null || agentServers.isEmpty()) { + return; + } + + // Try to fetch detected base prompt from the first selected agent collection + try { + int firstAgentCollectionId = Integer.parseInt(agentServers.get(0).getId()); + ApiCollection agentCollection = ApiCollectionsDao.instance.getMeta(firstAgentCollectionId); + + if (agentCollection != null && StringUtils.isNotBlank(agentCollection.getDetectedBasePrompt())) { + basePromptRule.setBasePrompt(agentCollection.getDetectedBasePrompt()); + loggerMaker.debug("Populated basePrompt from collection " + firstAgentCollectionId + + " for policy: " + policy.getName()); + } + } catch (NumberFormatException e) { + loggerMaker.debug("Invalid agent collection ID format: " + agentServers.get(0).getId()); + } catch (Exception e) { + loggerMaker.debug("Error fetching detected base prompt for policy " + policy.getName() + ": " + e.getMessage()); + } + } catch (Exception e) { + loggerMaker.debug("Error populating base prompt: " + e.getMessage()); + } + } + public String createGuardrailPolicy() { try { User user = getSUser(); @@ -96,6 +152,7 @@ public String createGuardrailPolicy() { updates.add(Updates.set("regexPatternsV2", policy.getRegexPatternsV2())); updates.add(Updates.set("contentFiltering", policy.getContentFiltering())); updates.add(Updates.set("llmRule", policy.getLlmRule())); + updates.add(Updates.set("basePromptRule", policy.getBasePromptRule())); updates.add(Updates.set("selectedMcpServers", policy.getSelectedMcpServers())); updates.add(Updates.set("selectedAgentServers", policy.getSelectedAgentServers())); updates.add(Updates.set("selectedMcpServersV2", policy.getSelectedMcpServersV2())); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/GuardrailPolicies.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/GuardrailPolicies.jsx index 6d8784c961..2f35332264 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/GuardrailPolicies.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/GuardrailPolicies.jsx @@ -430,6 +430,8 @@ function GuardrailPolicies() { contentFiltering: guardrailData.contentFilters || {}, // Add LLM policy if present ...(guardrailData.llmRule ? { llmRule: guardrailData.llmRule } : {}), + // Add Base Prompt Rule if present + ...(guardrailData.basePromptRule ? { basePromptRule: guardrailData.basePromptRule } : {}), applyOnResponse: guardrailData.applyOnResponse || false, applyOnRequest: guardrailData.applyOnRequest || false, url: guardrailData.url || '', diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx index 6abb84a17a..8b57caea43 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx @@ -76,6 +76,12 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i const [enableLlmRule, setEnableLlmRule] = useState(false); const [llmConfidenceScore, setLlmConfidenceScore] = useState(0.5); + // Step 6.5: Base Prompt Rule + const [enableBasePromptRule, setEnableBasePromptRule] = useState(false); + const [basePrompt, setBasePrompt] = useState(""); + const [basePromptAutoDetect, setBasePromptAutoDetect] = useState(true); + const [basePromptConfidenceScore, setBasePromptConfidenceScore] = useState(0.5); + // Step 7: URL and Confidence Score const [url, setUrl] = useState(""); const [confidenceScore, setConfidenceScore] = useState(25); // Start with 25 (first checkpoint) @@ -162,12 +168,18 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i }, { number: 7, + title: "Base Prompt Rule", + optional: true, + summary: enableBasePromptRule ? `Enabled${basePromptAutoDetect ? ' (Auto-detect)' : ''}${basePrompt ? ` - ${basePrompt.substring(0, 30)}${basePrompt.length > 30 ? '...' : ''}` : ''}, Confidence: ${basePromptConfidenceScore.toFixed(2)}` : null + }, + { + number: 8, title: "URL and Confidence Score", optional: true, summary: url ? `URL: ${url.substring(0, 30)}${url.length > 30 ? '...' : ''}, Confidence: ${confidenceScore}` : null }, { - number: 8, + number: 9, title: "Server and application settings", optional: false, summary: (selectedMcpServers.length > 0 || selectedAgentServers.length > 0) @@ -289,6 +301,10 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i setLlmRule(""); setEnableLlmRule(false); setLlmConfidenceScore(0.5); + setEnableBasePromptRule(false); + setBasePrompt(""); + setBasePromptAutoDetect(true); + setBasePromptConfidenceScore(0.5); setUrl(""); setConfidenceScore(25); setUrlError(""); @@ -359,6 +375,19 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i setLlmConfidenceScore(0.5); } + // Base Prompt Rule + if (policy.basePromptRule) { + setEnableBasePromptRule(policy.basePromptRule.enabled || false); + setBasePrompt(policy.basePromptRule.basePrompt || ""); + setBasePromptAutoDetect(policy.basePromptRule.autoDetect !== undefined ? policy.basePromptRule.autoDetect : true); + setBasePromptConfidenceScore(policy.basePromptRule.confidenceScore !== undefined ? policy.basePromptRule.confidenceScore : 0.5); + } else { + setEnableBasePromptRule(false); + setBasePrompt(""); + setBasePromptAutoDetect(true); + setBasePromptConfidenceScore(0.5); + } + // URL and Confidence Score setUrl(policy.url || ""); // Map existing confidence score to nearest checkpoint @@ -406,7 +435,7 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i }; const handleSkipToServers = () => { - setCurrentStep(8); + setCurrentStep(9); }; const handleSave = async () => { @@ -465,6 +494,14 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i confidenceScore: llmConfidenceScore } } : {}), + ...(enableBasePromptRule ? { + basePromptRule: { + enabled: true, + basePrompt: basePromptAutoDetect ? null : basePrompt.trim(), // Only save manual prompt, auto-detect will fetch on read + autoDetect: basePromptAutoDetect, + confidenceScore: basePromptConfidenceScore + } + } : {}), url: url || null, confidenceScore: confidenceScore, selectedMcpServers: selectedMcpServers, // Old format (just IDs) @@ -958,6 +995,62 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i ); const renderStep7 = () => ( + + + Base Prompt Rule + + Configure a base prompt rule to check the intent of user input in agent prompts with placeholders like {`{var}`} or {`{}`}. + + + + + {enableBasePromptRule && ( + <> + + + {!basePromptAutoDetect && ( + + )} + + + Confidence Score: {basePromptConfidenceScore.toFixed(2)} + + + + + + )} + + + ); + + const renderStep8 = () => ( URL and Confidence Score @@ -1001,7 +1094,7 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i ); - const renderStep8 = () => ( + const renderStep9 = () => ( Server and application settings @@ -1069,6 +1162,7 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i case 6: return renderStep6(); case 7: return renderStep7(); case 8: return renderStep8(); + case 9: return renderStep9(); default: return renderStep1(); } }; @@ -1083,7 +1177,7 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i }); } - if (currentStep > 1 && currentStep < 8) { + if (currentStep > 1 && currentStep < 9) { actions.push({ content: "Skip to Server settings", onAction: handleSkipToServers @@ -1094,14 +1188,14 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i }; const getPrimaryAction = () => { - if (currentStep === 8) { + if (currentStep === 9) { return { content: isEditMode ? "Update Guardrail" : "Create Guardrail", onAction: handleSave, loading: loading, disabled: !name.trim() || !blockedMessage.trim() || urlError }; - } else if (currentStep < 8) { + } else if (currentStep < 9) { return { content: "Next", onAction: handleNext, diff --git a/libs/dao/src/main/java/com/akto/dto/ApiCollection.java b/libs/dao/src/main/java/com/akto/dto/ApiCollection.java index fce69a80a7..a416e26bcc 100644 --- a/libs/dao/src/main/java/com/akto/dto/ApiCollection.java +++ b/libs/dao/src/main/java/com/akto/dto/ApiCollection.java @@ -72,6 +72,9 @@ public class ApiCollection { String registryStatus; public static final String REGISTRY_STATUS = "registryStatus"; + String detectedBasePrompt; + public static final String DETECTED_BASE_PROMPT = "detectedBasePrompt"; + private static final List ENV_KEYWORDS_WITH_DOT = Arrays.asList( "staging", "preprod", "qa", "demo", "dev", "test", "svc", "localhost", "local", "intranet", "lan", "example", "invalid", @@ -467,4 +470,12 @@ public String getRegistryStatus() { public void setRegistryStatus(String registryStatus) { this.registryStatus = registryStatus; } + + public String getDetectedBasePrompt() { + return detectedBasePrompt; + } + + public void setDetectedBasePrompt(String detectedBasePrompt) { + this.detectedBasePrompt = detectedBasePrompt; + } } diff --git a/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java b/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java index 0ef74a0e0b..a7a1264289 100644 --- a/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java +++ b/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java @@ -50,6 +50,9 @@ public class GuardrailPolicies { private LLMRule llmRule; + // Step 6.5: Base Prompt Rule - for checking intent of user input in agent base prompts with placeholders + private BasePromptRule basePromptRule; + // Step 7: Server and application settings (old format - backward compatibility) private List selectedMcpServers; private List selectedAgentServers; @@ -118,7 +121,7 @@ public GuardrailPolicies(String name, String description, String blockedMessage, int updatedTimestamp, String createdBy, String updatedBy, String selectedCollection, String selectedModel, List deniedTopics, List piiTypes, List regexPatterns, List regexPatternsV2, Map contentFiltering, - LLMRule llmRule, + LLMRule llmRule, BasePromptRule basePromptRule, List selectedMcpServers, List selectedAgentServers, List selectedMcpServersV2, List selectedAgentServersV2, boolean applyOnResponse, boolean applyOnRequest, String url, double confidenceScore, boolean active) { @@ -138,6 +141,7 @@ public GuardrailPolicies(String name, String description, String blockedMessage, this.regexPatternsV2 = regexPatternsV2; this.contentFiltering = contentFiltering; this.llmRule = llmRule; + this.basePromptRule = basePromptRule; this.selectedMcpServers = selectedMcpServers; this.selectedAgentServers = selectedAgentServers; this.selectedMcpServersV2 = selectedMcpServersV2; @@ -217,4 +221,21 @@ public LLMRule(boolean enabled, String userPrompt, double confidenceScore) { this.confidenceScore = confidenceScore; } } + + @Getter + @Setter + @NoArgsConstructor + public static class BasePromptRule { + private boolean enabled; + private String basePrompt; // Base prompt with placeholders like {var} or {} + private boolean autoDetect; // Whether to auto-detect base_prompt from traffic + private double confidenceScore; + + public BasePromptRule(boolean enabled, String basePrompt, boolean autoDetect, double confidenceScore) { + this.enabled = enabled; + this.basePrompt = basePrompt; + this.autoDetect = autoDetect; + this.confidenceScore = confidenceScore; + } + } } \ No newline at end of file From 91a0fcc033674b86abe11654c67fad4a638e3275 Mon Sep 17 00:00:00 2001 From: Karan Date: Mon, 1 Dec 2025 02:44:40 +0530 Subject: [PATCH 2/5] Refactor CreateGuardrailModal to support new server selection format with names --- .../pages/guardrails/components/CreateGuardrailModal.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx index 6d3cc1a2ef..b423182674 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx @@ -481,8 +481,10 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i } : {}), url: url || null, confidenceScore: confidenceScore, - selectedMcpServers: transformedMcpServers, - selectedAgentServers: transformedAgentServers, + selectedMcpServers: selectedMcpServers, // Old format (just IDs) + selectedAgentServers: selectedAgentServers, // Old format (just IDs) + selectedMcpServersV2: transformedMcpServers, // New format (with names) + selectedAgentServersV2: transformedAgentServers, // New format (with names) applyOnResponse, applyOnRequest, // Add edit mode information From 2c430eeddbec685b864db932388cb98f96a0b331 Mon Sep 17 00:00:00 2001 From: Karan Date: Thu, 4 Dec 2025 02:41:07 +0530 Subject: [PATCH 3/5] Enhance ApiDetails to display detected base prompt template and improve placeholder detection logic. Update CreateGuardrailModal for better layout consistency and adjust BasePromptStep for confidence threshold labeling. --- .../components/CreateGuardrailModal.jsx | 72 +++++++++---------- .../components/steps/BasePromptStep.jsx | 8 ++- .../observe/api_collections/ApiDetails.jsx | 55 ++++++++++++++ .../web/polaris_web/web/src/util/func.js | 36 ++++++++++ .../src/main/java/com/akto/dto/ApiInfo.java | 11 +++ 5 files changed, 143 insertions(+), 39 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx index b423182674..05c72dc75d 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx @@ -523,8 +523,8 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i }; const renderAllSteps = () => ( - - {steps.map((step) => ( + + {steps.map((step) => ( stepRefs.current[step.number] = el} @@ -545,17 +545,17 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i > @@ -565,47 +565,47 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i - - {step.title} - + fontWeight={step.number === currentStep ? "bold" : "regular"} + > + {step.title} + {!step.isValid && step.number !== currentStep && ( - )} - + )} + {step.number !== currentStep && ( <> - {step.summary && ( - - {step.summary} - + {step.summary && ( + + {step.summary} + )} {!step.isValid && step.errorMessage && ( {step.errorMessage} - + )} - )} - - - - + )} + + + + {step.number === currentStep && ( - + {renderStepContent(step.number)} - - )} - + + )} + + + - - ))} - + ); const renderStepContent = (stepNumber) => { diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx index bd8912dab6..22a0de8b66 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx @@ -12,7 +12,8 @@ export const BasePromptConfig = { if (!enableBasePromptRule) return null; const autoDetectText = basePromptAutoDetect ? ' (Auto-detect)' : ''; const promptText = basePrompt ? ` - ${basePrompt.substring(0, 30)}${basePrompt.length > 30 ? '...' : ''}` : ''; - return `Enabled${autoDetectText}${promptText}, Confidence: ${basePromptConfidenceScore.toFixed(2)}`; + if (!(autoDetectText && promptText)) return null; + return `${autoDetectText}${promptText}, Confidence: ${basePromptConfidenceScore.toFixed(2)}`; } }; @@ -50,6 +51,7 @@ const BasePromptStep = ({ helpText="Automatically detect the base prompt pattern from agent traffic. If disabled, you must provide the base prompt manually." /> + { /* TODO: Add auto-detected base prompt display with fallback placeholder text when auto-detect is enabled */ } {!basePromptAutoDetect && ( Confidence Score: {basePromptConfidenceScore.toFixed(2)} { try { @@ -250,6 +252,25 @@ function ApiDetails(props) { const { apiCollectionId, endpoint, method, description } = apiDetail setSelectedUrl({ url: endpoint, method: method }) + // Fetch ApiInfo to get detectedBasePrompt + try { + const apiInfoResp = await api.fetchEndpoint({ + url: endpoint, + method: method, + apiCollectionId: apiCollectionId + }); + // Check both apiInfoResp.data and apiInfoResp.data.apiInfo (depending on response structure) + const detectedPrompt = apiInfoResp?.data?.detectedBasePrompt || apiInfoResp?.detectedBasePrompt; + if (detectedPrompt && detectedPrompt.trim().length > 0) { + setDetectedBasePrompt(detectedPrompt); + } else { + setDetectedBasePrompt(null); + } + } catch (error) { + console.error("Error fetching ApiInfo:", error); + setDetectedBasePrompt(null); + } + try { await api.checkIfDependencyGraphAvailable(apiCollectionId, endpoint, method).then((resp) => { if (!resp.dependencyGraphExists) { @@ -384,6 +405,8 @@ function ApiDetails(props) { fetchData(); setHeadersWithData([]) + // Reset detectedBasePrompt when apiDetail changes + setDetectedBasePrompt(null); }, [apiDetail]) useEffect(() => { @@ -574,6 +597,37 @@ function ApiDetails(props) { /> } + + const BasePromptTab = { + id: 'base-prompt', + content: "Prompt Template", + component: + + + + + Detected Prompt Template + + + + + + Auto-detected prompt template with placeholders from agent traffic. This template represents the common structure of prompts sent to this endpoint. + + + + + + } const ValuesTab = { id: 'values', content: "Values", @@ -784,6 +838,7 @@ function ApiDetails(props) { tabs={[ ValuesTab, SchemaTab, + ...(detectedBasePrompt ? [BasePromptTab] : []), ...(hasIssues ? [IssuesTab] : []), ApiCallStatsTab, DependencyTab diff --git a/apps/dashboard/web/polaris_web/web/src/util/func.js b/apps/dashboard/web/polaris_web/web/src/util/func.js index 48a2e5af30..531f4b1008 100644 --- a/apps/dashboard/web/polaris_web/web/src/util/func.js +++ b/apps/dashboard/web/polaris_web/web/src/util/func.js @@ -2329,6 +2329,42 @@ showConfirmationModal(modalContent, primaryActionContent, primaryAction) { }, isLimitedAccount(){ return window?.ACTIVE_ACCOUNT === 1753372418 + }, + /** + * Find all placeholder positions in a text string (e.g., {}, {var}, {variable}) + * Returns an array of objects with start, end, and phrase properties for highlighting + * @param {string} text - The text to search for placeholders + * @returns {Array} Array of placeholder objects with {start, end, phrase} + */ + findPlaceholders: function(text) { + if (!text) return []; + const placeholders = []; + // Find all individual {} pairs, including overlapping ones like {{}} + // We search from each position to find the nearest closing brace + for (let i = 0; i < text.length; i++) { + if (text[i] === '{') { + // Find the nearest matching closing brace + let depth = 1; + for (let j = i + 1; j < text.length && depth > 0; j++) { + if (text[j] === '{') { + depth++; + } else if (text[j] === '}') { + depth--; + if (depth === 0) { + // Found a complete placeholder + const placeholder = text.substring(i, j + 1); + placeholders.push({ + start: i, + end: j + 1, + phrase: placeholder + }); + break; // Found the match for this opening brace, move on + } + } + } + } + } + return placeholders; } } diff --git a/libs/dao/src/main/java/com/akto/dto/ApiInfo.java b/libs/dao/src/main/java/com/akto/dto/ApiInfo.java index dbe9a510bd..92ceaf05f5 100644 --- a/libs/dao/src/main/java/com/akto/dto/ApiInfo.java +++ b/libs/dao/src/main/java/com/akto/dto/ApiInfo.java @@ -73,6 +73,9 @@ public class ApiInfo { public static final String PARENT_MCP_TOOL_NAMES = "parentMcpToolNames"; private List parentMcpToolNames; + public static final String DETECTED_BASE_PROMPT = "detectedBasePrompt"; + private String detectedBasePrompt; + public enum ApiType { REST, GRAPHQL, GRPC, SOAP } @@ -543,4 +546,12 @@ public List getParentMcpToolNames() { public void setParentMcpToolNames(List parentMcpToolNames) { this.parentMcpToolNames = parentMcpToolNames; } + + public String getDetectedBasePrompt() { + return detectedBasePrompt; + } + + public void setDetectedBasePrompt(String detectedBasePrompt) { + this.detectedBasePrompt = detectedBasePrompt; + } } From 8f7fb1e5651971a2d12223743bd85612240e3fd9 Mon Sep 17 00:00:00 2001 From: Karan Date: Thu, 4 Dec 2025 15:01:29 +0530 Subject: [PATCH 4/5] Refactor GuardrailPoliciesAction and CreateGuardrailModal to remove base prompt auto-detection logic. Update BasePromptStep to enhance intent verification messaging and simplify state management. Adjust GuardrailPolicies DTO to reflect changes in base prompt handling. --- .../akto/action/GuardrailPoliciesAction.java | 54 --------------- .../components/CreateGuardrailModal.jsx | 20 +----- .../components/steps/BasePromptStep.jsx | 67 +++++-------------- .../java/com/akto/dto/GuardrailPolicies.java | 6 +- 4 files changed, 21 insertions(+), 126 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java b/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java index 967ee628ca..192848822a 100644 --- a/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/GuardrailPoliciesAction.java @@ -1,9 +1,7 @@ package com.akto.action; -import com.akto.dao.ApiCollectionsDao; import com.akto.dao.GuardrailPoliciesDao; import com.akto.dao.context.Context; -import com.akto.dto.ApiCollection; import com.akto.dto.GuardrailPolicies; import com.akto.dto.User; import com.akto.log.LoggerMaker; @@ -12,7 +10,6 @@ import com.mongodb.client.model.Filters; import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.Updates; -import org.apache.commons.lang3.StringUtils; import lombok.Getter; import lombok.Setter; @@ -50,11 +47,6 @@ public String fetchGuardrailPolicies() { this.guardrailPolicies = GuardrailPoliciesDao.instance.findAllSortedByCreatedTimestamp(0, 20); this.total = GuardrailPoliciesDao.instance.getTotalCount(); - // Populate basePrompt for policies with autoDetect enabled - for (GuardrailPolicies policy : guardrailPolicies) { - populateBasePromptIfNeeded(policy); - } - loggerMaker.info("Fetched " + guardrailPolicies.size() + " guardrail policies out of " + total + " total"); return SUCCESS.toUpperCase(); @@ -64,52 +56,6 @@ public String fetchGuardrailPolicies() { } } - /** - * Populates basePrompt in basePromptRule if: - * 1. basePromptRule exists and is enabled - * 2. autoDetect is true - * 3. basePrompt is not already set (or is empty) - * 4. There are selected agent servers - * - * Fetches detectedBasePrompt from the first selected agent collection - */ - private void populateBasePromptIfNeeded(GuardrailPolicies policy) { - try { - GuardrailPolicies.BasePromptRule basePromptRule = policy.getBasePromptRule(); - if (basePromptRule == null || !basePromptRule.isEnabled() || !basePromptRule.isAutoDetect()) { - return; - } - - // If basePrompt is already set, use it - if (StringUtils.isNotBlank(basePromptRule.getBasePrompt())) { - return; - } - - // Get selected agent servers (prefer V2 format, fallback to old format) - List agentServers = policy.getEffectiveSelectedAgentServers(); - if (agentServers == null || agentServers.isEmpty()) { - return; - } - - // Try to fetch detected base prompt from the first selected agent collection - try { - int firstAgentCollectionId = Integer.parseInt(agentServers.get(0).getId()); - ApiCollection agentCollection = ApiCollectionsDao.instance.getMeta(firstAgentCollectionId); - - if (agentCollection != null && StringUtils.isNotBlank(agentCollection.getDetectedBasePrompt())) { - basePromptRule.setBasePrompt(agentCollection.getDetectedBasePrompt()); - loggerMaker.debug("Populated basePrompt from collection " + firstAgentCollectionId + - " for policy: " + policy.getName()); - } - } catch (NumberFormatException e) { - loggerMaker.debug("Invalid agent collection ID format: " + agentServers.get(0).getId()); - } catch (Exception e) { - loggerMaker.debug("Error fetching detected base prompt for policy " + policy.getName() + ": " + e.getMessage()); - } - } catch (Exception e) { - loggerMaker.debug("Error populating base prompt: " + e.getMessage()); - } - } public String createGuardrailPolicy() { try { diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx index 05c72dc75d..46e04f6a9e 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx @@ -78,10 +78,8 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i const [llmPrompt, setLlmPrompt] = useState(""); const [llmConfidenceScore, setLlmConfidenceScore] = useState(0.5); - // Step 7: Base Prompt Rule + // Step 7: Base Prompt Based Validation (AI Agents) const [enableBasePromptRule, setEnableBasePromptRule] = useState(false); - const [basePrompt, setBasePrompt] = useState(""); - const [basePromptAutoDetect, setBasePromptAutoDetect] = useState(true); const [basePromptConfidenceScore, setBasePromptConfidenceScore] = useState(0.5); // Step 8: External model based evaluation @@ -122,8 +120,6 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i llmConfidenceScore, // Step 7 enableBasePromptRule, - basePromptAutoDetect, - basePrompt, basePromptConfidenceScore, // Step 8 url, @@ -289,8 +285,6 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i setLlmPrompt(""); setLlmConfidenceScore(0.5); setEnableBasePromptRule(false); - setBasePrompt(""); - setBasePromptAutoDetect(true); setBasePromptConfidenceScore(0.5); setUrl(""); setConfidenceScore(25); @@ -360,16 +354,12 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i setLlmConfidenceScore(0.5); } - // Base Prompt Rule + // Base Prompt Based Validation (AI Agents) if (policy.basePromptRule) { setEnableBasePromptRule(policy.basePromptRule.enabled || false); - setBasePrompt(policy.basePromptRule.basePrompt || ""); - setBasePromptAutoDetect(policy.basePromptRule.autoDetect !== undefined ? policy.basePromptRule.autoDetect : true); setBasePromptConfidenceScore(policy.basePromptRule.confidenceScore !== undefined ? policy.basePromptRule.confidenceScore : 0.5); } else { setEnableBasePromptRule(false); - setBasePrompt(""); - setBasePromptAutoDetect(true); setBasePromptConfidenceScore(0.5); } @@ -474,8 +464,6 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i ...(enableBasePromptRule ? { basePromptRule: { enabled: true, - basePrompt: basePromptAutoDetect ? "" : basePrompt.trim(), // Send empty if auto-detect - autoDetect: basePromptAutoDetect, confidenceScore: basePromptConfidenceScore } } : {}), @@ -677,10 +665,6 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx index 22a0de8b66..3fb3ebedad 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx @@ -1,84 +1,53 @@ -import { VerticalStack, Text, TextField, RangeSlider, FormLayout, Checkbox, Box } from "@shopify/polaris"; +import { VerticalStack, Text, RangeSlider, FormLayout, Checkbox, Box } from "@shopify/polaris"; export const BasePromptConfig = { number: 7, - title: "Base Prompt Rule", + title: "Intent verification using base prompt (AI Agents)", validate: () => { return { isValid: true, errorMessage: null }; }, - getSummary: ({ enableBasePromptRule, basePromptAutoDetect, basePrompt, basePromptConfidenceScore }) => { + getSummary: ({ enableBasePromptRule, basePromptConfidenceScore }) => { if (!enableBasePromptRule) return null; - const autoDetectText = basePromptAutoDetect ? ' (Auto-detect)' : ''; - const promptText = basePrompt ? ` - ${basePrompt.substring(0, 30)}${basePrompt.length > 30 ? '...' : ''}` : ''; - if (!(autoDetectText && promptText)) return null; - return `${autoDetectText}${promptText}, Confidence: ${basePromptConfidenceScore.toFixed(2)}`; + return `Auto-detect from traffic, Confidence: ${basePromptConfidenceScore.toFixed(2)}`; } }; const BasePromptStep = ({ enableBasePromptRule, setEnableBasePromptRule, - basePrompt, - setBasePrompt, - basePromptAutoDetect, - setBasePromptAutoDetect, basePromptConfidenceScore, setBasePromptConfidenceScore }) => { return ( - Base Prompt Rule - Configure a base prompt rule to check the intent of user input in agent prompts with placeholders like {`{var}`} or {`{}`}. + Verify if agent requests match the intent of the base prompt. The base prompt is automatically detected from traffic, and user inputs filling placeholders like {`{var}`} or {`{}`} are checked against this intent. {enableBasePromptRule && ( - <> - - - { /* TODO: Add auto-detected base prompt display with fallback placeholder text when auto-detect is enabled */ } - {!basePromptAutoDetect && ( - + + - )} - - - Confidence Score: {basePromptConfidenceScore.toFixed(2)} - - - - + )} diff --git a/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java b/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java index a7a1264289..a207bfd7b1 100644 --- a/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java +++ b/libs/dao/src/main/java/com/akto/dto/GuardrailPolicies.java @@ -227,14 +227,10 @@ public LLMRule(boolean enabled, String userPrompt, double confidenceScore) { @NoArgsConstructor public static class BasePromptRule { private boolean enabled; - private String basePrompt; // Base prompt with placeholders like {var} or {} - private boolean autoDetect; // Whether to auto-detect base_prompt from traffic private double confidenceScore; - public BasePromptRule(boolean enabled, String basePrompt, boolean autoDetect, double confidenceScore) { + public BasePromptRule(boolean enabled, double confidenceScore) { this.enabled = enabled; - this.basePrompt = basePrompt; - this.autoDetect = autoDetect; this.confidenceScore = confidenceScore; } } From 6a4d873094501775eb45faec42296f4a7f9b23f4 Mon Sep 17 00:00:00 2001 From: Karan Date: Thu, 4 Dec 2025 15:36:43 +0530 Subject: [PATCH 5/5] Refactor ApiDetails to conditionally display the base prompt tab based on the presence of a 'gen-ai' tag in the API collection. Remove detectedBasePrompt from ApiCollection DTO to streamline data handling. --- .../observe/api_collections/ApiDetails.jsx | 57 +++++++++++++------ .../main/java/com/akto/dto/ApiCollection.java | 11 ---- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx index 70472db443..a41db3431f 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx @@ -72,6 +72,7 @@ function ApiDetails(props) { const [loading, setLoading] = useState(false) const [showMoreActions, setShowMoreActions] = useState(false) const setSelectedSampleApi = PersistStore(state => state.setSelectedSampleApi) + const allCollections = PersistStore(state => state.allCollections) const [disabledTabs, setDisabledTabs] = useState([]) const [description, setDescription] = useState("") const [headersWithData, setHeadersWithData] = useState([]) @@ -608,21 +609,31 @@ function ApiDetails(props) { Detected Prompt Template - - - - - Auto-detected prompt template with placeholders from agent traffic. This template represents the common structure of prompts sent to this endpoint. - + {detectedBasePrompt ? ( + <> + + + + + Auto-detected prompt template with placeholders from agent traffic. This template represents the common structure of prompts sent to this endpoint. + + + ) : ( + + + No prompt template detected yet. The base prompt template will be automatically detected from agent traffic when requests are made to this endpoint. + + + )} @@ -829,6 +840,20 @@ function ApiDetails(props) { ) + // Check if collection has gen-ai tag (same pattern as CreateGuardrailModal.jsx) + const hasGenAiTag = () => { + if (!apiDetail?.apiCollectionId || !allCollections) return false; + const collection = allCollections.find(c => c.id === apiDetail.apiCollectionId); + if (!collection) return false; + + return collection.envType && collection.envType.some(envType => + envType.keyName === 'gen-ai' + ); + }; + + // Always show BasePromptTab for AI agents (collections with gen-ai tag) + const shouldShowBasePromptTab = hasGenAiTag(); + const components = showForbidden ? [] : [ @@ -838,7 +863,7 @@ function ApiDetails(props) { tabs={[ ValuesTab, SchemaTab, - ...(detectedBasePrompt ? [BasePromptTab] : []), + ...(shouldShowBasePromptTab ? [BasePromptTab] : []), ...(hasIssues ? [IssuesTab] : []), ApiCallStatsTab, DependencyTab diff --git a/libs/dao/src/main/java/com/akto/dto/ApiCollection.java b/libs/dao/src/main/java/com/akto/dto/ApiCollection.java index a416e26bcc..fce69a80a7 100644 --- a/libs/dao/src/main/java/com/akto/dto/ApiCollection.java +++ b/libs/dao/src/main/java/com/akto/dto/ApiCollection.java @@ -72,9 +72,6 @@ public class ApiCollection { String registryStatus; public static final String REGISTRY_STATUS = "registryStatus"; - String detectedBasePrompt; - public static final String DETECTED_BASE_PROMPT = "detectedBasePrompt"; - private static final List ENV_KEYWORDS_WITH_DOT = Arrays.asList( "staging", "preprod", "qa", "demo", "dev", "test", "svc", "localhost", "local", "intranet", "lan", "example", "invalid", @@ -470,12 +467,4 @@ public String getRegistryStatus() { public void setRegistryStatus(String registryStatus) { this.registryStatus = registryStatus; } - - public String getDetectedBasePrompt() { - return detectedBasePrompt; - } - - public void setDetectedBasePrompt(String detectedBasePrompt) { - this.detectedBasePrompt = detectedBasePrompt; - } }