Skip to content

[ FIX ] Resolve request models for global routing policies - #3433

Open
Aakashwije wants to merge 1 commit into
wso2:ai-gw-fixesfrom
Aakashwije:fix/global-model-routing-request-model
Open

Aakashwije wants to merge 1 commit into
wso2:ai-gw-fixesfrom
Aakashwije:fix/global-model-routing-request-model

Conversation

@Aakashwije

Copy link
Copy Markdown
Contributor

Purpose

Globally attached model-routing policies were not receiving the correct
provider-template requestModel mapping for each API resource.

An LLM provider template can define a default model location and override it for
specific resources. For example:

requestModel:
  location: payload
  identifier: $.model

resourceMappings:
  resources:
    - resource: /responses
      requestModel:
        location: payload
        identifier: $.request.model

When an API contains only a wildcard operation such as POST /*, the controller
previously created one global policy configuration using the default mapping.
Consequently, requests to /responses could incorrectly use $.model instead
of the resource-specific $.request.model.

Operation-level policies already worked because the existing operation expansion
logic materialized concrete provider-resource paths and merged the corresponding
provider-template parameters into each operation. Global policies did not have
equivalent route-specific resolution.

This affects the following globally attached policies:

  • cost-based-model-routing
  • time-based-model-routing
  • semantic-model-routing

The issue applies to both LlmProvider and LlmProxy configurations.

Resolves: N/A — no related issue link was provided.

Goals

  • Support globally attached cost-based, time-based, and semantic model-routing
    policies.
  • Resolve the correct provider-template requestModel for every concrete route.
  • Support resource-specific requestModel overrides.
  • Preserve the original wildcard operation as the fallback route.
  • Preserve existing operation-level policy behavior.
  • Preserve global policy scope, execution order, version, condition, and other
    parameters.
  • Prevent one route's resolved parameters from leaking into another route.
  • Apply the same behavior to both providers and proxies.
  • Avoid creating unnecessary operations when a resource mapping does not change
    requestModel.

Approach

The implementation introduces two related transformation steps.

1. Materialize routes that require different model mappings

During the transformation of an LlmProvider or LlmProxy,
expandGlobalModelRoutingOperations checks whether one of the supported policies
is globally attached.

For every existing operation, it examines the matching provider-template
resource mappings. A concrete operation is created only when that resource has a
different requestModel from the operation that currently covers it.

For example:

Declared operation:
POST /*

Generated internal operations:
POST /responses → resource-specific requestModel
POST /*          → default requestModel and fallback

The original wildcard operation is retained. Therefore, resources without an
override continue to follow the existing route.

Existing operations are not replaced or duplicated. When a concrete operation
already exists, the expansion logic leaves it unchanged. Cloned operations also
retain their existing policies, access-control behavior, resilience
configuration, and other operation state.

Expansion is intentionally limited to:

  • cost-based-model-routing
  • time-based-model-routing
  • semantic-model-routing

This matches the policy-side changes in Gateway Controllers PR #303 and avoids
changing route topology for unrelated policies.

2. Resolve global policy parameters for each route

After RestAPITransformer creates the runtime routes and policy chains,
ResolveGlobalRequestModels obtains the relevant provider template:

  • For an LlmProvider, it uses the provider's configured template.
  • For an LlmProxy, it resolves the referenced provider and uses that
    provider's template.

The controller then calls the existing provider-template mapping selection logic
for every concrete route. The selected requestModel is merged into each
supported API-level policy instance.

For example:

POST /responses → requestModel.identifier = $.request.model
POST /*         → requestModel.identifier = $.model

Only policies marked as API-level attachments are updated. Operation-level
policies are intentionally skipped because they already receive their
operation-specific provider-template parameters during the earlier
transformation stage.

The merge creates a new parameter map instead of modifying the original map in
place. API-level policy parameters are initially shared by multiple route
chains; copying them prevents the mapping selected for one route from
overwriting the mapping used by another route.

The implementation also validates that:

  • A requestModel mapping exists for affected routes.
  • semantic-model-routing receives a payload-based mapping, which is the only
    location currently supported by that policy.

Policy order, execution conditions, versions, global scope, and unrelated
parameters remain unchanged.

This change does not affect the UI.

User stories

  • As an API developer, I want a globally attached model-routing policy to use the
    model location defined by the selected provider template.
  • As an API developer, I want provider-template resource overrides to work with
    globally attached policies.
  • As an API developer, I want requests handled by a wildcard operation to use
    the correct route-specific model mapping.
  • As a proxy developer, I want global model-routing behavior to be consistent
    between providers and proxies.
  • As an existing user of operation-level policies, I want the current behavior,
    policy order, and parameters to remain unchanged.
  • As a gateway operator, I want deployment to fail with a clear error when a
    required provider-template model mapping is missing or unsupported.

Documentation

N/A — the API Platform changes are internal gateway-controller transformation
changes and do not introduce a new user-facing configuration format.

The corresponding policy definitions and user-facing policy documentation are
updated in:

Automation tests

  • Unit tests

    Added focused coverage in llm_global_model_routing_test.go for:

    • Preserving global scope and policy order.
    • Preserving operation-level policy parameters.
    • Keeping parameter maps isolated between routes.
    • Expanding only resources whose requestModel differs.
    • Avoiding duplicate concrete operations.
    • Applying expansion to both providers and proxies.
    • Resolving the default and most-specific provider-template mappings.
    • Rejecting missing requestModel mappings.
    • Rejecting non-payload mappings for semantic-model-routing.

    The pkg/utils and pkg/transform test suites passed.

    go vet and formatting checks passed.

    No standalone coverage percentage was collected for this change.

  • Integration tests

    Added and executed end-to-end coverage for all three supported policies:

    • Time-Based Model Routing
      • Provider operation attachment
      • Provider global attachment
      • Proxy operation attachment
      • Proxy global attachment
    • Semantic Model Routing
      • Provider operation attachment
      • Provider global attachment
      • Proxy operation attachment
      • Proxy global attachment
    • Cost-Based Model Routing
      • Provider operation attachment
      • Provider global attachment
      • Proxy operation attachment
      • Proxy global attachment

    Results:

    Scenarios: 12 passed, 0 failed, 0 skipped
    Steps:     120 passed, 0 failed
    Result:    PASS
    

    The tests verify both operation-level compatibility and global route-specific
    requestModel resolution through providers and proxies.

Security checks

Samples

N/A — no new sample format is introduced.

The integration tests contain representative provider and proxy configurations
covering operation-level and global policy attachment with default and
resource-specific provider-template mappings.

Related PRs

The Gateway Controllers policy changes and this API Platform controller change
must be released together. PR #303 allows the policies to receive
requestModel as a runtime policy parameter, while this PR resolves and injects
the correct value for each route.

Test environment

  • Go: go1.26.5 darwin/amd64
  • Host operating system: macOS 15.7.9 (24G830)
  • Container runtime: Rancher Desktop
  • Docker client: 29.5.3-rd
  • Docker server: 29.1.3
  • Rancher Desktop VM: Alpine Linux v3.23
  • Architecture: x86_64
  • JDK: N/A
  • Database: N/A for the controller unit tests; integration dependencies were
    provided by the containerized test environment
  • Browser: N/A

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 7a8143f9-cad9-460f-9915-2b848bebb59a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@Aakashwije Aakashwije changed the title fix: resolve request models for global routing policies [ FIX ] Resolve request models for global routing policies Sep 14, 2026
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.

1 participant