Skip to content

Commit 2c430ee

Browse files
committed
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.
1 parent 91a0fcc commit 2c430ee

File tree

5 files changed

+143
-39
lines changed

5 files changed

+143
-39
lines changed

apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/CreateGuardrailModal.jsx

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,8 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i
523523
};
524524

525525
const renderAllSteps = () => (
526-
<VerticalStack gap="2">
527-
{steps.map((step) => (
526+
<VerticalStack gap="2">
527+
{steps.map((step) => (
528528
<Box
529529
key={step.number}
530530
ref={(el) => stepRefs.current[step.number] = el}
@@ -545,17 +545,17 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i
545545
>
546546
<HorizontalStack gap="3" blockAlign="center">
547547
<Box style={{
548-
width: "24px",
549-
height: "24px",
550-
borderRadius: "50%",
551-
backgroundColor: step.number === currentStep ? "#0070f3" :
548+
width: "24px",
549+
height: "24px",
550+
borderRadius: "50%",
551+
backgroundColor: step.number === currentStep ? "#0070f3" :
552552
(!step.isValid && step.number < currentStep) ? "#d72c0d" :
553-
step.number < currentStep ? "#008060" : "#e1e3e5",
553+
step.number < currentStep ? "#008060" : "#e1e3e5",
554554
color: step.number <= currentStep || !step.isValid ? "white" : "#6d7175",
555-
display: "flex",
556-
alignItems: "center",
557-
justifyContent: "center",
558-
fontSize: "12px",
555+
display: "flex",
556+
alignItems: "center",
557+
justifyContent: "center",
558+
fontSize: "12px",
559559
fontWeight: "bold",
560560
flexShrink: 0
561561
}}>
@@ -565,47 +565,47 @@ const CreateGuardrailModal = ({ isOpen, onClose, onSave, editingPolicy = null, i
565565
<Box style={{ flexGrow: 1 }}>
566566
<VerticalStack gap="1">
567567
<HorizontalStack gap="2" blockAlign="center">
568-
<Text
569-
variant="bodyMd"
568+
<Text
569+
variant="bodyMd"
570570
color={step.number === currentStep ? "success" : "subdued"}
571-
fontWeight={step.number === currentStep ? "bold" : "regular"}
572-
>
573-
{step.title}
574-
</Text>
571+
fontWeight={step.number === currentStep ? "bold" : "regular"}
572+
>
573+
{step.title}
574+
</Text>
575575
{!step.isValid && step.number !== currentStep && (
576576
<Icon source={AlertMinor} color="critical" />
577-
)}
578-
</HorizontalStack>
577+
)}
578+
</HorizontalStack>
579579
{step.number !== currentStep && (
580580
<>
581-
{step.summary && (
582-
<Text variant="bodySm" color="subdued" fontWeight="medium">
583-
{step.summary}
584-
</Text>
581+
{step.summary && (
582+
<Text variant="bodySm" color="subdued" fontWeight="medium">
583+
{step.summary}
584+
</Text>
585585
)}
586586
{!step.isValid && step.errorMessage && (
587587
<Text variant="bodySm" color="critical" fontWeight="medium">
588588
{step.errorMessage}
589-
</Text>
589+
</Text>
590590
)}
591591
</>
592-
)}
593-
</VerticalStack>
594-
</Box>
595-
</HorizontalStack>
596-
</Box>
592+
)}
593+
</VerticalStack>
594+
</Box>
595+
</HorizontalStack>
596+
</Box>
597597

598598
{step.number === currentStep && (
599-
<Box paddingBlockStart="2">
599+
<Box paddingBlockStart="2">
600600
{renderStepContent(step.number)}
601-
</Box>
602-
)}
603-
</VerticalStack>
601+
</Box>
602+
)}
603+
</VerticalStack>
604+
</Box>
605+
</LegacyCard>
604606
</Box>
605-
</LegacyCard>
606-
</Box>
607607
))}
608-
</VerticalStack>
608+
</VerticalStack>
609609
);
610610

611611
const renderStepContent = (stepNumber) => {

apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/guardrails/components/steps/BasePromptStep.jsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export const BasePromptConfig = {
1212
if (!enableBasePromptRule) return null;
1313
const autoDetectText = basePromptAutoDetect ? ' (Auto-detect)' : '';
1414
const promptText = basePrompt ? ` - ${basePrompt.substring(0, 30)}${basePrompt.length > 30 ? '...' : ''}` : '';
15-
return `Enabled${autoDetectText}${promptText}, Confidence: ${basePromptConfidenceScore.toFixed(2)}`;
15+
if (!(autoDetectText && promptText)) return null;
16+
return `${autoDetectText}${promptText}, Confidence: ${basePromptConfidenceScore.toFixed(2)}`;
1617
}
1718
};
1819

@@ -50,6 +51,7 @@ const BasePromptStep = ({
5051
helpText="Automatically detect the base prompt pattern from agent traffic. If disabled, you must provide the base prompt manually."
5152
/>
5253

54+
{ /* TODO: Add auto-detected base prompt display with fallback placeholder text when auto-detect is enabled */ }
5355
{!basePromptAutoDetect && (
5456
<TextField
5557
label="Base Prompt Template"
@@ -65,11 +67,11 @@ const BasePromptStep = ({
6567
<Text variant="bodyMd" fontWeight="medium">Confidence Score: {basePromptConfidenceScore.toFixed(2)}</Text>
6668
<Box paddingBlockStart="2">
6769
<RangeSlider
68-
label=""
70+
label="Confidence Threshold"
6971
value={basePromptConfidenceScore}
7072
min={0}
7173
max={1}
72-
step={0.01}
74+
step={0.1}
7375
output
7476
onChange={setBasePromptConfidenceScore}
7577
helpText="Set the confidence threshold (0-1). Higher values require more confidence to block content."

apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import FlyLayout from "../../../components/layouts/FlyLayout";
44
import GithubCell from "../../../components/tables/cells/GithubCell";
55
import ApiGroups from "../../../components/shared/ApiGroups";
66
import SampleDataList from "../../../components/shared/SampleDataList";
7+
import SampleData from "../../../components/shared/SampleData";
78
import { useEffect, useState, useRef } from "react";
89
import api from "../api";
910
import ApiSchema from "./ApiSchema";
@@ -87,6 +88,7 @@ function ApiDetails(props) {
8788
const apiDistributionAvailableRef = useRef(false);
8889
const [selectedTabId, setSelectedTabId] = useState('values');
8990
const [showForbidden, setShowForbidden] = useState(false);
91+
const [detectedBasePrompt, setDetectedBasePrompt] = useState(null);
9092

9193
const statusFunc = getStatus ? getStatus : (x) => {
9294
try {
@@ -250,6 +252,25 @@ function ApiDetails(props) {
250252
const { apiCollectionId, endpoint, method, description } = apiDetail
251253
setSelectedUrl({ url: endpoint, method: method })
252254

255+
// Fetch ApiInfo to get detectedBasePrompt
256+
try {
257+
const apiInfoResp = await api.fetchEndpoint({
258+
url: endpoint,
259+
method: method,
260+
apiCollectionId: apiCollectionId
261+
});
262+
// Check both apiInfoResp.data and apiInfoResp.data.apiInfo (depending on response structure)
263+
const detectedPrompt = apiInfoResp?.data?.detectedBasePrompt || apiInfoResp?.detectedBasePrompt;
264+
if (detectedPrompt && detectedPrompt.trim().length > 0) {
265+
setDetectedBasePrompt(detectedPrompt);
266+
} else {
267+
setDetectedBasePrompt(null);
268+
}
269+
} catch (error) {
270+
console.error("Error fetching ApiInfo:", error);
271+
setDetectedBasePrompt(null);
272+
}
273+
253274
try {
254275
await api.checkIfDependencyGraphAvailable(apiCollectionId, endpoint, method).then((resp) => {
255276
if (!resp.dependencyGraphExists) {
@@ -384,6 +405,8 @@ function ApiDetails(props) {
384405

385406
fetchData();
386407
setHeadersWithData([])
408+
// Reset detectedBasePrompt when apiDetail changes
409+
setDetectedBasePrompt(null);
387410
}, [apiDetail])
388411

389412
useEffect(() => {
@@ -574,6 +597,37 @@ function ApiDetails(props) {
574597
/>
575598
</Box>
576599
}
600+
601+
const BasePromptTab = {
602+
id: 'base-prompt',
603+
content: "Prompt Template",
604+
component: <Box paddingBlockStart={"4"}>
605+
<VerticalStack gap="4">
606+
<Box background="bg-surface-secondary" padding="4" borderRadius="2">
607+
<VerticalStack gap="2">
608+
<Text variant="headingSm" fontWeight="semibold">
609+
Detected Prompt Template
610+
</Text>
611+
<Box background="bg-surface" padding="2" borderRadius="1" style={{ minHeight: '200px' }}>
612+
<SampleData
613+
data={{
614+
message: detectedBasePrompt,
615+
vulnerabilitySegments: func.findPlaceholders(detectedBasePrompt)
616+
}}
617+
editorLanguage="plaintext"
618+
readOnly={true}
619+
minHeight="200px"
620+
wordWrap={true}
621+
/>
622+
</Box>
623+
<Text variant="bodySm" color="subdued">
624+
Auto-detected prompt template with placeholders from agent traffic. This template represents the common structure of prompts sent to this endpoint.
625+
</Text>
626+
</VerticalStack>
627+
</Box>
628+
</VerticalStack>
629+
</Box>
630+
}
577631
const ValuesTab = {
578632
id: 'values',
579633
content: "Values",
@@ -784,6 +838,7 @@ function ApiDetails(props) {
784838
tabs={[
785839
ValuesTab,
786840
SchemaTab,
841+
...(detectedBasePrompt ? [BasePromptTab] : []),
787842
...(hasIssues ? [IssuesTab] : []),
788843
ApiCallStatsTab,
789844
DependencyTab

apps/dashboard/web/polaris_web/web/src/util/func.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,6 +2329,42 @@ showConfirmationModal(modalContent, primaryActionContent, primaryAction) {
23292329
},
23302330
isLimitedAccount(){
23312331
return window?.ACTIVE_ACCOUNT === 1753372418
2332+
},
2333+
/**
2334+
* Find all placeholder positions in a text string (e.g., {}, {var}, {variable})
2335+
* Returns an array of objects with start, end, and phrase properties for highlighting
2336+
* @param {string} text - The text to search for placeholders
2337+
* @returns {Array} Array of placeholder objects with {start, end, phrase}
2338+
*/
2339+
findPlaceholders: function(text) {
2340+
if (!text) return [];
2341+
const placeholders = [];
2342+
// Find all individual {} pairs, including overlapping ones like {{}}
2343+
// We search from each position to find the nearest closing brace
2344+
for (let i = 0; i < text.length; i++) {
2345+
if (text[i] === '{') {
2346+
// Find the nearest matching closing brace
2347+
let depth = 1;
2348+
for (let j = i + 1; j < text.length && depth > 0; j++) {
2349+
if (text[j] === '{') {
2350+
depth++;
2351+
} else if (text[j] === '}') {
2352+
depth--;
2353+
if (depth === 0) {
2354+
// Found a complete placeholder
2355+
const placeholder = text.substring(i, j + 1);
2356+
placeholders.push({
2357+
start: i,
2358+
end: j + 1,
2359+
phrase: placeholder
2360+
});
2361+
break; // Found the match for this opening brace, move on
2362+
}
2363+
}
2364+
}
2365+
}
2366+
}
2367+
return placeholders;
23322368
}
23332369
}
23342370

libs/dao/src/main/java/com/akto/dto/ApiInfo.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public class ApiInfo {
7373
public static final String PARENT_MCP_TOOL_NAMES = "parentMcpToolNames";
7474
private List<String> parentMcpToolNames;
7575

76+
public static final String DETECTED_BASE_PROMPT = "detectedBasePrompt";
77+
private String detectedBasePrompt;
78+
7679
public enum ApiType {
7780
REST, GRAPHQL, GRPC, SOAP
7881
}
@@ -543,4 +546,12 @@ public List<String> getParentMcpToolNames() {
543546
public void setParentMcpToolNames(List<String> parentMcpToolNames) {
544547
this.parentMcpToolNames = parentMcpToolNames;
545548
}
549+
550+
public String getDetectedBasePrompt() {
551+
return detectedBasePrompt;
552+
}
553+
554+
public void setDetectedBasePrompt(String detectedBasePrompt) {
555+
this.detectedBasePrompt = detectedBasePrompt;
556+
}
546557
}

0 commit comments

Comments
 (0)