Skip to content

Comments

Semantic routing policy and Intelligent Model Routing #1236

Open
pamaljayasinghe wants to merge 6 commits intowso2:mainfrom
pamaljayasinghe:semantic_routing
Open

Semantic routing policy and Intelligent Model Routing #1236
pamaljayasinghe wants to merge 6 commits intowso2:mainfrom
pamaljayasinghe:semantic_routing

Conversation

@pamaljayasinghe
Copy link

@pamaljayasinghe pamaljayasinghe commented Jan 9, 2026

Semantic routing policy and Intelligent Model Routing Front End changes

Summary by CodeRabbit

  • New Features

    • Added Intelligent Model Routing and Semantic Routing policy UIs with Production/Sandbox toggles, per-environment default model/endpoint selection, rule-based routing, utterance management, and score thresholds.
    • Added Model Guardrail policy UI and consolidated content-path input and routing rule management.
  • Localization

    • Added numerous English UI text keys for IntelligentModelRouting, SemanticRouting, and ModelGuardrail.
  • Bug Fixes

    • Validation enforced for manual-mode policy forms to prevent saving invalid configurations.

@coderabbitai
Copy link

coderabbitai bot commented Jan 9, 2026

Walkthrough

Adds two new policy UIs—Intelligent Model Routing and Semantic Routing—plus localization keys and integration into the policy form to support manual-mode editing, validation, and provider checks via two new React components and updated localization.

Changes

Cohort / File(s) Summary
Localization
portals/publisher/src/main/webapp/site/public/locales/en.json
Added ~31 new localization keys for IntelligentModelRouting, SemanticRouting, and ModelGuardrail (accordion labels, defaults, routing labels, utterances, warnings/help text).
Policy Form Integration
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx
Treats semanticRouting and intelligentModelRouting as manual-mode policies; renders the new components; adds local validation state (isManualFormValid, showValidationErrors); enforces validation on submit and wires setManualPolicyConfig/manual policy save logic.
Intelligent Model Routing Component
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx
New default-export React component implementing production/sandbox toggles, contentPath, default model/endpoint selection, add/update/delete routing rules, provider/endpoint/model fetching, JSON parsing/serialization, and sync via setManualPolicyConfig. Includes TypeScript interfaces for config shapes.
Semantic Routing Component
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx
New default-export React component with production/sandbox toggles, content path normalization, default model/endpoint selection, routing rules with utterances and score thresholds, provider/endpoint/model fetching, validation, and backend-format string sync.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant General as Policy Form (General.tsx)
    participant IMR as IntelligentModelRouting
    participant API as API Context (Endpoints/Models)
    participant Parent as Parent State Manager

    User->>General: Select intelligentModelRouting policy
    General->>IMR: Render with setManualPolicyConfig/manualPolicyConfig
    IMR->>API: Fetch endpoints and LLM models
    API-->>IMR: Return endpoints and models
    IMR->>IMR: Parse/init config (production/sandbox/contentPath)
    User->>IMR: Modify defaults / add/update/delete routing rules
    IMR->>IMR: Update state and stringify config
    IMR->>Parent: setManualPolicyConfig(stringified config)
    IMR->>Parent: setProviderNotConfigured? (if providers missing)
    User->>General: Click Save
    General->>General: Validate manual form & provider flags
    alt Providers configured & form valid
        General->>Parent: Persist policy with manual config
    else
        General->>User: Prevent save, show validation/provider warnings
    end
Loading
sequenceDiagram
    participant User
    participant General as Policy Form (General.tsx)
    participant SR as SemanticRouting
    participant API as API Context (Endpoints/Models/Embeddings)
    participant Parent as Parent State Manager

    User->>General: Select semanticRouting policy
    General->>SR: Render with setManualPolicyConfig/manualPolicyConfig
    SR->>API: Fetch endpoints and embedding models
    API-->>SR: Return endpoints and models
    SR->>SR: Parse/init config (production/sandbox/contentPath)
    User->>SR: Edit content path, utterances, thresholds, defaults
    SR->>SR: Validate thresholds, normalize path, update rules
    SR->>SR: Stringify config
    SR->>Parent: setManualPolicyConfig(stringified config)
    SR->>Parent: setProviderNotConfigured if embedding provider missing
    User->>General: Click Save
    General->>General: Check provider & validation flags
    alt All providers present & form valid
        General->>Parent: Save policy
    else
        General->>User: Block save, show embedding/provider warnings
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through routes both wise and new,
Models and endpoints in a twining view,
Utterances hum and thresholds glow bright,
Warnings whisper softly through the night,
A rabbit cheers: configuration takes flight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: addition of Semantic routing policy and Intelligent Model Routing components as frontend functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ashera96
Copy link
Contributor

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
portals/publisher/src/main/webapp/site/public/locales/en.json (1)

1389-1442: Tighten consistency for new routing policy strings (casing + pluralization).

A few values look inconsistent (e.g., “Add Rule” vs “Add rule”, “Routing Rules” vs “Routing rule”). If these are user-facing labels/buttons, aligning casing/pluralization will make the two new policy UIs feel cohesive.

portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx (1)

136-148: Reset isManual/providerNotConfigured when policyObj changes (current code can get “stuck”).

Right now you only ever call setManual(true); if the same drawer instance is reused for a different policy, isManual may remain true and/or providerNotConfigured may remain true, breaking subsequent policy editing.

Proposed fix
 useEffect(() => {
-    if (
-        (policyObj && policyObj.name === 'modelRoundRobin') ||
-        (policyObj && policyObj.name === 'modelWeightedRoundRobin') ||
-        (policyObj && policyObj.name === 'modelFailover') ||
-        (policyObj && policyObj.name === 'semanticRouting') ||
-        (policyObj && policyObj.name === 'intelligentModelRouting')
-    ) {
-        setManual(true);
-    }
+    const manualPolicies = new Set([
+        'modelRoundRobin',
+        'modelWeightedRoundRobin',
+        'modelFailover',
+        'semanticRouting',
+        'intelligentModelRouting',
+    ]);
+    setManual(manualPolicies.has(policyObj?.name ?? ''));
+    // Avoid “sticky” disable if user switches away from routing policies.
+    setProviderNotConfigured(false);
 }, [policyObj]);
🤖 Fix all issues with AI agents
In
@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx:
- Around line 492-505: The save-button disabled logic ignores the global saving
state for manual policies and allows empty manual configs; update the disabled
expression so saving always disables the button (include saving independent of
isManual) and add a validation check that manualPolicyConfig is non-empty when
isManual (use the same manualPolicyConfig value passed to
SemanticRouting/IntelligentModelRouting, e.g.,
getValue(policySpec.policyAttributes[0]) or the manualPolicyConfig state), while
preserving the existing providerNotConfigured and the non-manual validation
branches; adjust the disabled prop wherever used (refer to
providerNotConfigured, isManual, saving, manualPolicyConfig) including the other
occurrence mentioned.
- Around line 258-261: The code assumes policySpec.policyAttributes[0] exists
which can throw if the array is empty or reordered; before assigning
updateCandidates[policySpec.policyAttributes[0].name] = manualPolicyConfig,
guard access by verifying policySpec.policyAttributes is defined and non-empty
and ideally locate the correct attribute by name (e.g., const attr =
policySpec.policyAttributes.find(a => a.name === expectedName) or fall back to
the first element), then use attr.name when setting updateCandidates; ensure
this check is applied in the block that handles policyObj values
(modelRoundRobin, modelWeightedRoundRobin, modelFailover, semanticRouting,
intelligentModelRouting) so you don’t dereference an undefined element.

In
@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx:
- Around line 207-219: fetchModelList currently calls
JSON.parse(apiFromContext.subtypeConfiguration.configuration) without validation
which will throw if subtypeConfiguration or configuration is missing or invalid;
wrap the parse in a try/catch and/or use a safe check (e.g., optional chaining
to verify apiFromContext?.subtypeConfiguration?.configuration exists) before
calling JSON.parse, validate that the parsed object contains llmProviderId, and
only then call API.getLLMProviderModelList; on parse or validation failure, log
the error via console.error or process logger and return early, and keep the
existing .catch for the API call to handle network/response errors before
calling setModelList.
- Around line 269-279: The current useEffect builds configForBackend and then
corrupts valid JSON by doing JSON.stringify(...) followed by
jsonString.replace(/"/g, "'"); remove that replace and preserve the valid JSON
(i.e., call setManualPolicyConfig(JSON.stringify(configForBackend))); if the
backend truly requires single quotes instead of double quotes, instead encode
the JSON safely (e.g., Base64 encode the JSON string or use encodeURIComponent)
and have the backend decode it; update the useEffect around configForBackend,
JSON.stringify, and setManualPolicyConfig accordingly (or coordinate backend
decoding) rather than blindly replacing quotes.
- Around line 760-768: The production routing rules are being keyed by array
index in the map over config.production.routingrules (and rendered via
renderRuleCard), which breaks reconciliation when items are deleted; update the
RoutingRuleConfig model to include a unique id field (e.g., id: string), ensure
any code that creates/adds rules (the add rule handler) generates a stable
unique id for each new rule, then change the map to use rule.id as the React key
and pass that id as the key prop to the Paper returned by renderRuleCard; also
update handleProductionRuleDelete to locate and remove rules by id rather than
by index to avoid index-shift bugs.
- Around line 221-224: The useEffect that calls fetchModelList and
fetchEndpoints needs cleanup and correct dependencies: add apiFromContext.id and
apiFromContext.subtypeConfiguration to the effect dependency array and ensure
both async functions accept an AbortSignal (or check a mounted flag) so
in-flight requests are aborted on unmount; inside fetchModelList and
fetchEndpoints pass the AbortSignal to fetch/axios calls, catch abort errors,
and guard any setState calls (e.g., setModelList, setEndpoints) so they only run
when the request wasn’t aborted.
- Around line 653-673: The delete button Grid item in
IntelligentModelRouting.tsx is placed after the Grid container is closed so it
isn't a direct child of the Grid container; move the conditional block that
renders the IconButton (the Grid with data-testid='rule-delete' and its
IconButton/DeleteIcon children) so it is inside the surrounding Grid container
(or wrap it with a Grid container) that contains the other Grid items, ensuring
all Grid items are direct children of the same Grid container for correct
layout.

In
@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx:
- Around line 208-220: fetchModelList currently calls
JSON.parse(apiFromContext.subtypeConfiguration.configuration) without guarding
for missing or invalid JSON which can throw and crash the UI; update
fetchModelList to first verify apiFromContext.subtypeConfiguration.configuration
exists, wrap the JSON.parse in a try/catch (or use a safe parse helper) to
handle malformed JSON, only call API.getLLMProviderModelList when a valid
llmProviderId is extracted, and on error setModelList to an empty array or keep
prior state and log the parse/API error so the policy drawer won't crash
(references: fetchModelList, apiFromContext.subtypeConfiguration.configuration,
API.getLLMProviderModelList, setModelList).
- Around line 227-300: The current parsing/serialization in the useEffect
(reading manualPolicyConfig) and the serialization block that builds
configForBackend uses blind string replacements of quotes which corrupts
contentpath and utterances; remove the manual .replace(/\'/g, "'").replace(/'/g,
'"') and the JSON.stringify(...).replace(/"/g, "'") steps and instead keep
manualPolicyConfig as valid JSON (double-quoted) end-to-end or use a proper
serializer/escaper; update the parsing useEffect to
JSON.parse(manualPolicyConfig) (or JSON5.parse if you adopt JSON5) and update
the writer useEffect to setManualPolicyConfig(JSON.stringify(configForBackend))
(or JSON5.stringify) so contentpath and user strings are preserved, and
similarly remove quote normalization from normalizeContentPath and
handleContentPathUpdate; reference functions/vars: manualPolicyConfig,
setManualPolicyConfig, useEffect blocks that parse/serialize configForBackend,
normalizeContentPath, and handleContentPathUpdate.
🧹 Nitpick comments (3)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx (2)

764-789: Switch + Accordion onChange likely double-toggles; stop propagation or pick one control path.

Clicking the Switch often triggers Accordion expansion as well; you’re handling both paths, which can cause redundant state updates (and double-resets when turning off).

Also applies to: 849-874


480-483: Consider localizing remaining hard-coded UI strings (loading + helper/placeholder text).

You added locale keys for much of the UI, but a few strings are still hard-coded (e.g., loading message, helperText, placeholders). Aligning these with en.json will keep i18n coverage complete.

Also applies to: 501-515, 683-684

portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx (1)

200-204: Silent error handling provides no feedback to users.

API errors are only logged to console. If endpoint fetching fails, users see an empty list with no explanation or retry option.

♻️ Consider adding error state
+const [fetchError, setFetchError] = useState<string | null>(null);

 const fetchEndpoints = () => {
     setLoading(true);
+    setFetchError(null);
     const endpointsPromise = API.getApiEndpoints(apiFromContext.id);
     endpointsPromise
         .then((response) => {
             // ... existing logic
         }).catch((error) => {
             console.error(error);
+            setFetchError('Failed to load endpoints. Please try again.');
         }).finally(() => {
             setLoading(false);
         });
 }

Then display the error in the UI with a retry button.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eabc659 and 9b64b91.

📒 Files selected for processing (4)
  • portals/publisher/src/main/webapp/site/public/locales/en.json
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx (1)
portals/admin/src/main/webapp/source/src/app/components/Base/Navigator.jsx (1)
  • policyObj (132-132)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx (1)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/Types.d.ts (1)
  • ModelVendor (35-38)
🔇 Additional comments (2)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx (2)

51-82: LGTM!

The interface definitions are well-structured and provide clear type safety for the routing configuration. The separation of EnvironmentConfig, RoutingRuleConfig, DefaultModelConfig, and ContentPathConfig promotes good modularity.


678-686: Good user feedback for missing LLM provider configuration.

The warning alert with documentation link provides helpful guidance when the LLM provider is not configured. The use of rel='noopener noreferrer' on the external link is a good security practice.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
4 Security Hotspots
20.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@pamaljayasinghe pamaljayasinghe marked this pull request as ready for review January 16, 2026 10:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@portals/publisher/src/main/webapp/site/public/locales/en.json`:
- Around line 1428-1429: The string value for the localization key
"Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route" currently reads
"Add rule"; update it to "Add Rule" to match the casing used by the
IntelligentModelRouting UI and ensure consistent routing UI text across the app.
♻️ Duplicate comments (4)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx (2)

231-241: Quote replacement still corrupts strings containing quotes.

The previous review flagged this pattern as corrupting user data (e.g., model names, contexts containing quotes). The comment was marked as addressed, but the code still uses .replace(/"/g, "'") on line 239.

If string values contain double quotes, this corrupts the JSON structure. If the backend truly requires single-quoted strings, consider:

  1. Coordinating with backend to accept standard JSON
  2. Using Base64 encoding for the payload
  3. At minimum, only escaping the outer structure while preserving inner string values

188-192: Parsing also uses quote replacement that can fail on edge cases.

Line 191 uses .replace(/'/g, '"') to convert single quotes back to double quotes before parsing. If user-entered values (rule names, contexts) contain apostrophes, the round-trip through .replace(/"/g, "'") (serialization) and .replace(/'/g, '"') (parsing) will corrupt them.

Example: A context like "User's input" becomes 'User's input' on serialization, then fails to parse or becomes malformed on the next load.

portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx (2)

357-367: Content path normalization corrupts valid JSONPath expressions.

The normalizeContentPath function converts single quotes to double quotes, but JSONPath expressions commonly use single quotes for string literals (as shown in the placeholder at line 473: $.messages[?(@.role=='user')].content).

Converting 'user' to "user" may cause the JSONPath to fail at runtime depending on the parser implementation.


239-262: Same quote replacement issue as IntelligentModelRouting.

The serialization at line 260 uses .replace(/"/g, "'") which corrupts any user-entered strings containing double quotes (utterances, etc.).

🧹 Nitpick comments (4)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx (2)

243-259: Consider using stable IDs for rules.

The ID generation on line 245 uses Date.now() combined with a random suffix, which provides good uniqueness. However, Math.random().toString(36).substr(2, 9) uses the deprecated substr method.

♻️ Minor improvement: use substring instead of substr
 const handleAddProductionRule = () => {
     const newRule: RoutingRuleConfig = {
-        id: `rule-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+        id: `rule-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
         name: '',
         context: '',
         model: '',
         endpointId: '',
     };

283-299: Same substr deprecation applies to sandbox rule ID generation.

♻️ Apply the same fix here
 const handleAddSandboxRule = () => {
     const newRule: RoutingRuleConfig = {
-        id: `rule-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+        id: `rule-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
         name: '',
         context: '',
         model: '',
         endpointId: '',
     };
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx (2)

264-280: Same substr deprecation in route ID generation.

♻️ Use substring instead of substr
 const handleAddProductionRoute = () => {
     const newRoute: RoutingConfig = {
-        id: `route-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+        id: `route-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
         model: '',
         endpointId: '',
         utterances: [],
         scorethreshold: '0.8',
     };

304-320: Same substr deprecation for sandbox route ID.

♻️ Use substring instead of substr
 const handleAddSandboxRoute = () => {
     const newRoute: RoutingConfig = {
-        id: `route-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+        id: `route-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
         model: '',
         endpointId: '',
         utterances: [],
         scorethreshold: '0.8',
     };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b64b91 and 8a0fcbd.

📒 Files selected for processing (4)
  • portals/publisher/src/main/webapp/site/public/locales/en.json
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: pamaljayasinghe
Repo: wso2/apim-apps PR: 1236
File: portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx:257-260
Timestamp: 2026-01-16T10:16:10.352Z
Learning: In `portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx`, the built-in WSO2 LLM routing policies (modelRoundRobin, modelWeightedRoundRobin, modelFailover, semanticRouting, intelligentModelRouting) have guaranteed policy specs that always include at least one attribute in policySpec.policyAttributes, so accessing policySpec.policyAttributes[0] is safe without defensive guards for these specific policies.
Learnt from: pamaljayasinghe
Repo: wso2/apim-apps PR: 1236
File: portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx:221-224
Timestamp: 2026-01-16T07:33:18.136Z
Learning: In LLM policy components (ModelRoundRobin, ModelFailover, ModelWeightedRoundRobin, IntelligentModelRouting, SemanticRouting) in the APIM publisher portal, `useEffect(() => { fetchModelList(); fetchEndpoints(); }, []);` uses an empty dependency array for intentional mount-once fetching behavior. This is an established pattern across all policy components to avoid unnecessary re-fetches.
Learnt from: pamaljayasinghe
Repo: wso2/apim-apps PR: 1236
File: portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx:207-219
Timestamp: 2026-01-16T07:31:21.015Z
Learning: In LLM policy components (ModelRoundRobin, ModelFailover, ModelWeightedRoundRobin, IntelligentModelRouting, SemanticRouting) in the APIM publisher portal, JSON.parse(apiFromContext.subtypeConfiguration.configuration) is used without try-catch because parent components enforce that these components only render when the API has a valid LLM subtype configuration.
📚 Learning: 2026-01-16T10:16:10.352Z
Learnt from: pamaljayasinghe
Repo: wso2/apim-apps PR: 1236
File: portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx:257-260
Timestamp: 2026-01-16T10:16:10.352Z
Learning: In `portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx`, the built-in WSO2 LLM routing policies (modelRoundRobin, modelWeightedRoundRobin, modelFailover, semanticRouting, intelligentModelRouting) have guaranteed policy specs that always include at least one attribute in policySpec.policyAttributes, so accessing policySpec.policyAttributes[0] is safe without defensive guards for these specific policies.

Applied to files:

  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx
  • portals/publisher/src/main/webapp/site/public/locales/en.json
📚 Learning: 2026-01-16T07:33:18.136Z
Learnt from: pamaljayasinghe
Repo: wso2/apim-apps PR: 1236
File: portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx:221-224
Timestamp: 2026-01-16T07:33:18.136Z
Learning: In LLM policy components (ModelRoundRobin, ModelFailover, ModelWeightedRoundRobin, IntelligentModelRouting, SemanticRouting) in the APIM publisher portal, `useEffect(() => { fetchModelList(); fetchEndpoints(); }, []);` uses an empty dependency array for intentional mount-once fetching behavior. This is an established pattern across all policy components to avoid unnecessary re-fetches.

Applied to files:

  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx
📚 Learning: 2026-01-16T07:31:21.015Z
Learnt from: pamaljayasinghe
Repo: wso2/apim-apps PR: 1236
File: portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx:207-219
Timestamp: 2026-01-16T07:31:21.015Z
Learning: In LLM policy components (ModelRoundRobin, ModelFailover, ModelWeightedRoundRobin, IntelligentModelRouting, SemanticRouting) in the APIM publisher portal, JSON.parse(apiFromContext.subtypeConfiguration.configuration) is used without try-catch because parent components enforce that these components only render when the API has a valid LLM subtype configuration.

Applied to files:

  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx
📚 Learning: 2026-01-16T07:33:10.845Z
Learnt from: pamaljayasinghe
Repo: wso2/apim-apps PR: 1236
File: portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx:221-224
Timestamp: 2026-01-16T07:33:10.845Z
Learning: In policy components (ModelRoundRobin, ModelFailover, ModelWeightedRoundRobin, IntelligentModelRouting, SemanticRouting) within the APIM publisher portal, use useEffect(() => { fetchModelList(); fetchEndpoints(); }, []) to perform mount-once data fetching. This pattern should be applied across all TSX policy components to avoid unnecessary re-fetches. If a dependency-based re-fetch is needed, specify exact dependencies in the array.

Applied to files:

  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx
  • portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx
🔇 Additional comments (11)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx (3)

100-122: Component structure and state initialization looks good.

The state setup with proper TypeScript interfaces, reasonable defaults, and clear separation of concerns (production vs sandbox, endpoints, model list, loading state, toggle flags) is well-organized.


495-644: Rule card rendering is well-structured.

The renderRuleCard function properly encapsulates the UI for each routing rule with:

  • Model and endpoint selection from fetched data
  • Proper form controls with labels and IDs
  • Delete button correctly placed inside the Grid container (addressed from past review)
  • Clean separation of change handlers per environment

646-828: Main render structure with accordions is well-implemented.

The Production and Sandbox accordions with:

  • Toggle switches for enabling/disabling
  • Proper alerts for missing models/endpoints
  • Dynamic rule rendering with unique keys (rule.id || fallback)
  • Content path configuration section

The UI structure is clear and follows MUI patterns correctly.

portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx (4)

573-583: Score threshold validation is well-implemented.

The validation correctly:

  • Allows empty string (for clearing)
  • Validates numeric range 0-1
  • Prevents invalid input from being stored

657-702: Utterance ChipInput implementation looks good.

The utterance management with:

  • Add handler that trims and validates non-empty values
  • Delete handler using index lookup
  • Proper ChipInput component integration
  • Helpful placeholder and helper text

101-122: Component structure mirrors IntelligentModelRouting consistently.

The state setup follows the same established pattern as other LLM policy components, maintaining consistency across the codebase.


729-911: Main render structure is consistent with IntelligentModelRouting.

The accordion-based UI for Production/Sandbox environments with proper:

  • Toggle switches and state management
  • Alert messages for missing models/endpoints
  • Dynamic route rendering with unique keys
  • Content path configuration

The overall structure maintains consistency with the sibling component.

portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx (4)

48-49: New routing policy components imported cleanly.


137-145: Manual-policy detection now covers the new routing types.


257-259: Manual policy save mapping extended appropriately for new policies.


491-502: Semantic & Intelligent routing editors are wired into manual mode as expected.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +1428 to +1429
"Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route": "Add rule",
"Apis.Details.Policies.CustomPolicies.SemanticRouting.contentpath": "Content Path",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize “Add rule” casing to match other routing UIs.

Line 1428 uses “Add rule” while IntelligentModelRouting uses “Add Rule”. Recommend consistent casing for a uniform UI.

✅ Proposed fix
-  "Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route": "Add rule",
+  "Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route": "Add Rule",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route": "Add rule",
"Apis.Details.Policies.CustomPolicies.SemanticRouting.contentpath": "Content Path",
"Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route": "Add Rule",
"Apis.Details.Policies.CustomPolicies.SemanticRouting.contentpath": "Content Path",
🤖 Prompt for AI Agents
In `@portals/publisher/src/main/webapp/site/public/locales/en.json` around lines
1428 - 1429, The string value for the localization key
"Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route" currently reads
"Add rule"; update it to "Add Rule" to match the casing used by the
IntelligentModelRouting UI and ensure consistent routing UI text across the app.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
4 Security Hotspots
22.8% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx (3)

172-176: validateForm missing from useEffect dependency array.

validateForm is re-created on every render and is not in the dep array. Since all values it closes over (config, productionEnabled, sandboxEnabled) are already listed, there is no stale-closure risk in practice, but react-hooks/exhaustive-deps will flag it. The cleanest fix is to inline the validation logic or wrap validateForm in useCallback with the same deps.

♻️ Option: inline or add to deps
  useEffect(() => {
      if (setIsFormValid) {
          setIsFormValid(validateForm());
      }
- }, [config, productionEnabled, sandboxEnabled, setIsFormValid]);
+ }, [config, productionEnabled, sandboxEnabled, setIsFormValid, validateForm]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`
around lines 172 - 176, The useEffect calls validateForm but does not include
validateForm in its dependency array, causing a linter warning; fix by
stabilizing validateForm or inlining the logic: either wrap the validateForm
function in useCallback with dependencies [config, productionEnabled,
sandboxEnabled] (so the function identity is stable) and then include
validateForm in the useEffect deps, or remove the helper and inline the
validation expression directly inside the useEffect that calls setIsFormValid
(keeping deps [config, productionEnabled, sandboxEnabled, setIsFormValid]);
reference validateForm, useEffect, setIsFormValid, config, productionEnabled,
sandboxEnabled when applying the change.

243-316: Bidirectional useEffect dependency creates unnecessary extra render cycles.

Effect A (line 243, deps [manualPolicyConfig]) calls setConfig; Effect B (line 293, deps [config, ...]) calls setManualPolicyConfig. Every user action triggers a 2-cycle ping-pong: the user's action updates config → Effect B writes a new string → the parent re-renders → Effect A parses it back (same values, new object reference) → Effect B fires again with the same string → React bails out via string equality.

A useRef guard prevents the redundant reverse-parse:

♻️ Proposed refactor
+    const isParsing = useRef(false);

     useEffect(() => {
         if (manualPolicyConfig !== '') {
             try {
+                isParsing.current = true;
                 const parsedConfig = JSON.parse(manualPolicyConfig);
                 // ... setConfig call ...
             } catch (error) { ... }
         }
     }, [manualPolicyConfig]);

     useEffect(() => {
+        if (isParsing.current) {
+            isParsing.current = false;
+            return;
+        }
         const configForBackend: any = { ... };
         setManualPolicyConfig(JSON.stringify(configForBackend));
     }, [config, setManualPolicyConfig]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`
around lines 243 - 316, The two useEffect hooks (one reading manualPolicyConfig
to call setConfig, the other serializing config to call setManualPolicyConfig)
cause a ping-pong render; add a useRef guard to skip the reverse-parse: create a
ref like skipParseRef, in the serialization effect (the one that builds
configForBackend and calls setManualPolicyConfig) set skipParseRef.current =
true immediately before calling setManualPolicyConfig, and in the parse effect
(the one that JSON.parse manualPolicyConfig and calls setConfig) check if
skipParseRef.current is true — if so, set it back to false and return early;
keep references to manualPolicyConfig, setConfig, config, setManualPolicyConfig
and the two useEffect blocks when applying this change.

320-320: substr is deprecated; replace with slice.

String.prototype.substr() is deprecated and is defined only in Annex B as normative-optional for non-browser runtimes; String.prototype.substring() or slice() should be used instead.

♻️ Proposed fix
- id: `route-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+ id: `route-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,

The same pattern appears at line 360 in handleAddSandboxRoute.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`
at line 320, Replace uses of the deprecated String.prototype.substr in the
id-generation expressions inside SemanticRouting (both in the handler that
creates routes and in handleAddSandboxRoute): change the substr(2, 9) call on
Math.random().toString(36) to use slice instead by computing slice(start,
start+length) (i.e., start=2 and length=9 → slice(2, 11)), updating both
occurrences so the generated id remains unchanged but avoids the deprecated API.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`:
- Around line 527-531: The TextField for contentpath in the SemanticRouting
component is marked required but the payload only includes contentpath when
non-empty (see config.contentpath check), so remove the misleading required
attribute on that TextField (or make it conditional) to avoid browser
validation; locate the TextField usage in SemanticRouting.tsx (the element
rendering the "contentpath" input) and delete the required prop (or set
required={!!someCondition} if you intend it to be required only in certain
modes).
- Around line 293-316: The effect building configForBackend should strip the
UI-only id from each route before sending to backend: when constructing
production and sandbox routes (config.production.routes and
config.sandbox.routes) map over each route and create a new object omitting the
id property (e.g., using object rest to exclude id or explicitly pick allowed
fields) so configForBackend.production.routes and
configForBackend.sandbox.routes contain route objects without the
client-generated id; keep the rest of the logic (contentpath handling,
JSON.stringify and quote-replacement) and then call setManualPolicyConfig with
the formatted string.

---

Duplicate comments:
In `@portals/publisher/src/main/webapp/site/public/locales/en.json`:
- Around line 1427-1429: The localization key
Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route currently uses
"Add rule" and should be normalized to title case to match other routing UIs;
update the value for
Apis.Details.Policies.CustomPolicies.SemanticRouting.add.route from "Add rule"
to "Add Rule" in en.json so it matches IntelligentModelRouting and other
entries.

In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`:
- Around line 825-832: The component SemanticRouting shows the "No models
available" alert while fetchModelList is in-flight because fetchModelList never
toggles a loading flag; add a boolean state (e.g., modelsLoading) in the
component, set modelsLoading = true before calling fetchModelList and set it
false in finally (or both success/error branches), and update the render checks
that currently use modelList.length === 0 (the alert locations in the component)
to only show the warning when !modelsLoading && modelList.length === 0;
reference fetchModelList, modelList, and the SemanticRouting render where the
Alert is returned and apply the same guard to both alert occurrences.
- Line 246: Stop the quote-replacement round-trip: remove the manual replacement
of single quotes to double quotes in the JSON.parse call for manualPolicyConfig
(the line using JSON.parse(manualPolicyConfig.replace(/'/g, '"'))) and instead
parse manualPolicyConfig as standard JSON; likewise remove any serialization
code that converts double quotes to single quotes when writing
manualPolicyConfig (the serialize block mentioned around lines 314–315) so the
config remains valid JSON end-to-end. Also update normalizeContentPath to avoid
converting single quotes inside JSONPath expressions (do not replace "'" → '"'
within JSONPath handling), and ensure any error handling around JSON.parse fails
gracefully without resetting the entire form.
- Around line 126-169: The validateForm function currently omits checking
route.scorethreshold so a route with an empty threshold can pass; update
validateForm (inside the production loop over config.production.routes and the
sandbox loop over config.sandbox.routes) to also require a non-empty
scorethreshold (same validation logic as used by handleThresholdChange where ''
is considered invalid) for each route before returning true so both production
and sandbox routes fail validation when scorethreshold is empty.

---

Nitpick comments:
In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`:
- Around line 172-176: The useEffect calls validateForm but does not include
validateForm in its dependency array, causing a linter warning; fix by
stabilizing validateForm or inlining the logic: either wrap the validateForm
function in useCallback with dependencies [config, productionEnabled,
sandboxEnabled] (so the function identity is stable) and then include
validateForm in the useEffect deps, or remove the helper and inline the
validation expression directly inside the useEffect that calls setIsFormValid
(keeping deps [config, productionEnabled, sandboxEnabled, setIsFormValid]);
reference validateForm, useEffect, setIsFormValid, config, productionEnabled,
sandboxEnabled when applying the change.
- Around line 243-316: The two useEffect hooks (one reading manualPolicyConfig
to call setConfig, the other serializing config to call setManualPolicyConfig)
cause a ping-pong render; add a useRef guard to skip the reverse-parse: create a
ref like skipParseRef, in the serialization effect (the one that builds
configForBackend and calls setManualPolicyConfig) set skipParseRef.current =
true immediately before calling setManualPolicyConfig, and in the parse effect
(the one that JSON.parse manualPolicyConfig and calls setConfig) check if
skipParseRef.current is true — if so, set it back to false and return early;
keep references to manualPolicyConfig, setConfig, config, setManualPolicyConfig
and the two useEffect blocks when applying this change.
- Line 320: Replace uses of the deprecated String.prototype.substr in the
id-generation expressions inside SemanticRouting (both in the handler that
creates routes and in handleAddSandboxRoute): change the substr(2, 9) call on
Math.random().toString(36) to use slice instead by computing slice(start,
start+length) (i.e., start=2 and length=9 → slice(2, 11)), updating both
occurrences so the generated id remains unchanged but avoids the deprecated API.

Comment on lines +293 to +316
useEffect(() => {
// Convert to backend format with production/sandbox each having defaultModel and routes
const configForBackend: any = {
production: {
defaultModel: config.production.defaultModel,
routes: config.production.routes,
},
sandbox: {
defaultModel: config.sandbox.defaultModel,
routes: config.sandbox.routes,
},
};

// Add path as single object (not array) - backend expects this format
if (config.contentpath && config.contentpath.trim() !== '') {
configForBackend.path = { contentpath: config.contentpath.trim() };
}

// Convert to string format expected by backend
// Backend expects single quotes for the string representation
const jsonString = JSON.stringify(configForBackend);
const formattedString = jsonString.replace(/"/g, "'");
setManualPolicyConfig(formattedString);
}, [config, setManualPolicyConfig]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx | sed -n '290,320p'

Repository: wso2/apim-apps

Length of output: 1549


🏁 Script executed:

# Find the RoutingConfig type definition
rg "type RoutingConfig|interface RoutingConfig" --type ts --type tsx -B 2 -A 10

Repository: wso2/apim-apps

Length of output: 85


🏁 Script executed:

# Search for where routes are constructed or modified
rg "routes.*map|routes.*filter" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/ --type ts --type tsx -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 85


🏁 Script executed:

# Check if there's any backend API documentation or schema validation
fd "schema|backend|api" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/ --type ts --type tsx

Repository: wso2/apim-apps

Length of output: 227


🏁 Script executed:

rg "type RoutingConfig|interface RoutingConfig" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/ -B 2 -A 10

Repository: wso2/apim-apps

Length of output: 1851


🏁 Script executed:

# Search for RoutingConfig more broadly
rg "RoutingConfig" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/ -B 1 -A 5

Repository: wso2/apim-apps

Length of output: 11073


🏁 Script executed:

# Check how configForBackend is used - where it's sent to backend
rg "setManualPolicyConfig|manualPolicyConfig" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 1080


🏁 Script executed:

# Look for imports and type definitions at the top of the file
head -50 portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx

Repository: wso2/apim-apps

Length of output: 2271


🏁 Script executed:

# Search for where the policy config is sent to backend API
rg "manualPolicyConfig" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/ -B 3 -A 3

Repository: wso2/apim-apps

Length of output: 21797


🏁 Script executed:

# Search for API calls to save/update policy
rg "updatePolicy|savePolicy|policyConfig" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/ -l

Repository: wso2/apim-apps

Length of output: 270


🏁 Script executed:

# Look for the parent component or where this component is used
rg "SemanticRouting" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/ -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 23775


🏁 Script executed:

# Find where the policy is saved to backend
rg "policySpec|setManualPolicyConfig" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx -B 3 -A 3 | head -100

Repository: wso2/apim-apps

Length of output: 4095


🏁 Script executed:

# Look for API calls or policy save methods
rg "updatePolicy|submitPolicy|savePolicyConfig" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/ -B 2 -A 5

Repository: wso2/apim-apps

Length of output: 2106


🏁 Script executed:

# Check the full AttachedPolicyForm component to understand data flow
wc -l portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx

Repository: wso2/apim-apps

Length of output: 174


🏁 Script executed:

# Search for backend validation or schema references
rg "semanticRouting|routes.*schema" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/ -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 3380


🏁 Script executed:

# Look for any type definitions or interfaces that specify what routes should contain
rg "interface.*Route|type.*Route" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/ -B 1 -A 5

Repository: wso2/apim-apps

Length of output: 40


🏁 Script executed:

# Search for test files that might show expected backend format
fd "test|spec" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/

Repository: wso2/apim-apps

Length of output: 40


🏁 Script executed:

# Look for any backend validation error handling
rg "400|validation|reject|unknown" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/ -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 2425


🏁 Script executed:

# Search for backend-related documentation or comments about id field
rg "id.*client|client.*id|route.*id" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 724


🏁 Script executed:

# Check if there's any documentation in README or docs folder about the policy format
fd "README|POLICY|semantic" . -name "*.md" -o -name "*.txt" | head -20

Repository: wso2/apim-apps

Length of output: 288


🏁 Script executed:

# Look at IntelligentModelRouting to see if it has similar issue or if it filters the id
rg "routes.*routes|routes.*map" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 40


🏁 Script executed:

# Look at IntelligentModelRouting implementation
rg "configForBackend|routes.*:" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/IntelligentModelRouting.tsx -B 3 -A 3

Repository: wso2/apim-apps

Length of output: 503


🏁 Script executed:

# Search for any place where id field is explicitly filtered or removed
rg "id.*rest|{ id|destructur" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/ -B 2 -A 2

Repository: wso2/apim-apps

Length of output: 166


🏁 Script executed:

# Check the backend API endpoint being called to understand format expectations
rg "updateApiOperations|updateAllApiOperations" portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/AttachedPolicyForm/General.tsx -B 5 -A 10

Repository: wso2/apim-apps

Length of output: 1563


Filter out client-generated id field when serializing routes for backend.

The config.production.routes and config.sandbox.routes include the full RoutingConfig objects with the client-generated id field (e.g., "route-1704000000000-xj3f2"). These should be omitted when building the backend payload, as id is a UI-internal identifier not part of the backend schema.

Proposed fix
- routes: config.production.routes,
+ routes: config.production.routes.map(({ id, ...rest }) => rest),

Apply the same to config.sandbox.routes.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
// Convert to backend format with production/sandbox each having defaultModel and routes
const configForBackend: any = {
production: {
defaultModel: config.production.defaultModel,
routes: config.production.routes,
},
sandbox: {
defaultModel: config.sandbox.defaultModel,
routes: config.sandbox.routes,
},
};
// Add path as single object (not array) - backend expects this format
if (config.contentpath && config.contentpath.trim() !== '') {
configForBackend.path = { contentpath: config.contentpath.trim() };
}
// Convert to string format expected by backend
// Backend expects single quotes for the string representation
const jsonString = JSON.stringify(configForBackend);
const formattedString = jsonString.replace(/"/g, "'");
setManualPolicyConfig(formattedString);
}, [config, setManualPolicyConfig]);
const configForBackend: any = {
production: {
defaultModel: config.production.defaultModel,
routes: config.production.routes.map(({ id, ...rest }) => rest),
},
sandbox: {
defaultModel: config.sandbox.defaultModel,
routes: config.sandbox.routes.map(({ id, ...rest }) => rest),
},
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`
around lines 293 - 316, The effect building configForBackend should strip the
UI-only id from each route before sending to backend: when constructing
production and sandbox routes (config.production.routes and
config.sandbox.routes) map over each route and create a new object omitting the
id property (e.g., using object rest to exclude id or explicitly pick allowed
fields) so configForBackend.production.routes and
configForBackend.sandbox.routes contain route objects without the
client-generated id; keep the rest of the logic (contentpath handling,
JSON.stringify and quote-replacement) and then call setManualPolicyConfig with
the formatted string.

Comment on lines +527 to +531
placeholder="$.messages[?(@.role=='user')].content"
helperText="The JSONPath expression used to extract content from the payload. If not specified, the entire payload will be used for validation."
required
/>
</Grid>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

required prop on an optional field is misleading.

contentpath is added to the payload only when non-empty (line 307: if (config.contentpath && config.contentpath.trim() !== '')), making it optional. The required attribute on the TextField (line 529) contradicts this and may trigger unwanted browser-level validation.

🐛 Proposed fix
-            required
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
placeholder="$.messages[?(@.role=='user')].content"
helperText="The JSONPath expression used to extract content from the payload. If not specified, the entire payload will be used for validation."
required
/>
</Grid>
placeholder="$.messages[?(@.role=='user')].content"
helperText="The JSONPath expression used to extract content from the payload. If not specified, the entire payload will be used for validation."
/>
</Grid>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/CustomPolicies/SemanticRouting.tsx`
around lines 527 - 531, The TextField for contentpath in the SemanticRouting
component is marked required but the payload only includes contentpath when
non-empty (see config.contentpath check), so remove the misleading required
attribute on that TextField (or make it conditional) to avoid browser
validation; locate the TextField usage in SemanticRouting.tsx (the element
rendering the "contentpath" input) and delete the required prop (or set
required={!!someCondition} if you intend it to be required only in certain
modes).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants