Skip to content

fix: preserve duplicate-named policies for LLM providers and proxies#2868

Open
renuka-fernando wants to merge 3 commits into
wso2:mainfrom
renuka-fernando:duplicate-policy-drop
Open

fix: preserve duplicate-named policies for LLM providers and proxies#2868
renuka-fernando wants to merge 3 commits into
wso2:mainfrom
renuka-fernando:duplicate-policy-drop

Conversation

@renuka-fernando

@renuka-fernando renuka-fernando commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Purpose

Fix #2823
Fix #2850

Attaching two policies with the same name (e.g. two set-headers guardrails) at the API/global level for an LLM provider or proxy silently collapsed them to a single instance, so only the last one reached the gateway. The problem existed across the whole stack — the gateway-controller, platform-api, and the AI workspace portal each independently deduplicated by policy name — and was reproducible both when editing an existing provider/proxy and when creating a new provider.

Goals

Allow the same policy to be attached more than once at the API/global scope, so every duplicate-named policy is preserved end-to-end from the UI through platform-api and the gateway-controller to the gateway.

Approach

  • gateway-controller: collectAPIPolicies used a map keyed by policy name, overwriting earlier entries. Return an ordered slice instead and simplify buildPolicyChain to prepend it. Fixes all kinds (RestApi, WebSubApi, Mcp, LlmProvider, LlmProxy) at once.
  • platform-api: the globalPolicies carry-through loop checked hasGlobalPolicy against the accumulator it was appending to, dropping the second user duplicate before it reached the WebSocket. Snapshot the system-generated policy names first and skip only against those, in both the provider and proxy deployment paths.
  • ai-workspace (edit tabs): the provider and proxy guardrails tabs deduplicated globalPolicies by name+version before sending. Always push new global-scope guardrails, and always append the new path to a resource-scope policy instead of skipping when it already exists.
  • ai-workspace (provider create): the new-provider guardrails section deduplicated staged guardrails by name and was React-keyed by name, so duplicates collided and remove-by-name deleted every copy. Add a unique id to each staged guardrail, always append, and key/remove by id — clearing the drawer selection only when no duplicate of that name remains.

Operation-level policies were unaffected in every case.

User stories

As an API platform user, I can attach the same policy (e.g. two set-headers guardrails) more than once to an LLM provider or proxy at the API/global level, and all instances take effect at the gateway.

Documentation

N/A — bug fix restoring intended behaviour; no user-facing API or doc change.

Automation tests

  • Unit tests

    Added regression tests covering the duplicate API/global-level case in gateway-controller (restapi_test.go) and platform-api (llm_deployment_test.go).

  • Integration tests

    N/A — covered by the added unit tests.

Security checks

Samples

N/A

Related PRs

N/A

Test environment

Go workspace (gateway-controller, platform-api); AI workspace portal via Vite dev server, Chrome.

Checklist

  • Tests added or updated (unit, integration, etc.)
  • Samples updated (if applicable)

Two policies with the same name (e.g. two set-headers guardrails)
attached at the API/global level were collapsed to a single instance,
so only the latest reached the gateway. Two independent root causes:

- gateway-controller: collectAPIPolicies used a map keyed by policy
  name, overwriting earlier entries. Return an ordered slice instead
  and simplify buildPolicyChain to prepend it. Fixes all kinds
  (RestApi, WebSubApi, Mcp, LlmProvider, LlmProxy) at once.
- platform-api: the globalPolicies carry-through loop checked
  hasGlobalPolicy against the accumulator it was appending to, dropping
  the second user duplicate before it reached the WebSocket. Snapshot
  the system-generated policy names first and skip only against those,
  in both the provider and proxy deployment paths.

Operation-level policies were unaffected in both services. Adds
regression tests covering the duplicate API/global-level case.
The provider and proxy guardrails tabs deduplicated globalPolicies by
name+version before sending, updating the existing entry instead of
adding a new one — so a second same-named guardrail (e.g. two
set-headers) could never be attached. This mirrored the backend drop
now fixed in gateway-controller and platform-api.

- Always push new global-scope guardrails instead of merging by
  name+version
- Always append the new path to a resource-scope policy instead of
  skipping when the path already exists
- Add a /proxy dev route to the vite proxy config
The new-provider guardrails section deduplicated staged guardrails by
name, replacing the existing entry, so a second same-named guardrail
(e.g. two set-headers) could never be attached. It was also React-keyed
by name, so duplicates collided and remove-by-name deleted every copy.

- Add a unique id to each staged GuardrailSelection
- Always append a new guardrail instead of merging by name
- Key the pill list by id and remove by id, clearing the drawer
  selection only when no duplicate of that name remains

Signed-off-by: Renuka Fernando <renukapiyumal@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The changes preserve duplicate guardrail policies across workspace state, deployment YAML, and gateway policy chains. Guardrail removal now uses unique staged-entry IDs. The AI workspace also updates its development image tag and adds a BFF proxy route.

Changes

Duplicate policy preservation

Layer / File(s) Summary
Guardrail entry identity
portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/AddNewProvider/...
Staged guardrails now use unique IDs for rendering, removal, and duplicate-name handling.
Guardrail policy submission
portals/ai-workspace/src/pages/appShell/appShellPages/{proxies,serviceProvider}/...
Global policies and repeated resource paths are appended instead of merged or deduplicated.
Deployment policy serialization
platform-api/internal/service/llm_deployment*.go
Generated system policy names are snapshotted before user policies are appended, preserving duplicate user policies.
Gateway policy chain construction
gateway/gateway-controller/pkg/transform/restapi*.go
API-level policies are collected as ordered slices and duplicate instances are retained in policy chains, with regression coverage.

AI workspace development updates

Layer / File(s) Summary
Workspace runtime configuration
portals/ai-workspace/docker-compose.yaml, portals/ai-workspace/vite.config.ts
The workspace image uses the snapshot tag, and /proxy requests are forwarded to the configurable BFF target.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GuardrailsUI
  participant PlatformAPI
  participant GatewayTransformer
  GuardrailsUI->>PlatformAPI: submit repeated guardrail policies
  PlatformAPI->>PlatformAPI: preserve duplicate user policies in deployment YAML
  PlatformAPI->>GatewayTransformer: provide ordered policy configuration
  GatewayTransformer-->>GuardrailsUI: produce policy chains with duplicate instances
Loading

Possibly related PRs

Suggested reviewers: malinthaprasan, rakhitharr, tharindu1st, anugayan, tharsanan1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and matches the main change: preserving duplicate-named policies for LLM providers and proxies.
Description check ✅ Passed The description includes the required sections and gives clear purpose, approach, tests, security, and environment details.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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.

[Bug]: When the same policy attached twice in global policies only latest is applied [Bug]: Not possible to add same policy twice for LLM Providers

1 participant