Skip to content

Platform api changes to support consumer based rl and cost based rl#1818

Merged
Arshardh merged 2 commits intowso2:mainfrom
Saadha123:consumer-brl-platform-api
May 5, 2026
Merged

Platform api changes to support consumer based rl and cost based rl#1818
Arshardh merged 2 commits intowso2:mainfrom
Saadha123:consumer-brl-platform-api

Conversation

@Saadha123
Copy link
Copy Markdown
Contributor

Purpose

Updated llm_deployment.go file in platform api to support consumer based ratelimit

  • Consumer request limit: emits advanced-ratelimit with x-wso2-application-id in key extraction (fallback: default) and quota name consumer-request-limit
  • Consumer token limit: emits token-based-ratelimit with consumerBased: true
  • Consumer cost limit: emits llm-cost-based-ratelimit with consumerBased: true; hasPolicy guard prevents llm-cost appearing twice when both backend and consumer cost limits are configured
  • Fixed addOrAppendPolicyPath to be scope-aware so backend and consumer entries with the same policy name are not merged

Added unit tests to llm_deployment_test.go

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Summary

This PR extends the platform API to support consumer-based and cost-based rate limiting for LLM deployments.

Key Functional Changes

Consumer-Level Rate Limiting

  • Adds support for three types of consumer-scoped limits:
    • Consumer request limits: generates advanced-ratelimit policies with application-based key extraction
    • Consumer token limits: generates token-based-ratelimit policies with consumer scope
    • Consumer cost limits: generates llm-cost-based-ratelimit policies with consumer scope

Cost-Based Rate Limiting

  • Extends provider-level cost configuration to generate llm-cost-based-ratelimit policies
  • Supports global, default, and resource-wise cost limits with configurable reset durations
  • Automatically includes the llm-cost tracker policy when needed

Policy Management Improvements

  • Refines policy deduplication logic to prevent llm-cost from appearing twice when both backend and consumer cost limits are configured
  • Updates addOrAppendPolicyPath to merge policies only when consumer scope matches, preventing unintended consolidation of backend and consumer policies with the same name
  • Adds hasPolicy helper function for centralized policy existence checks

Test Coverage

Adds comprehensive unit test coverage including:

  • Consumer-only rate limiting scenarios
  • Backend-only rate limiting scenarios
  • Combined backend and consumer rate limiting scenarios
  • Edge cases for disabled limits
  • Regression tests for llm-cost deduplication

Walkthrough

The LLM deployment service extends its policy generation logic to support cost-based budgeting and enhanced consumer-scoped rate limiting. Provider-level cost configurations now generate llm-cost-based-ratelimit policies with formatted reset durations. Consumer-level rate limiting is expanded to emit consumer-scoped token, request, and cost policies with appropriate metadata. The policy merging logic is refined to match consumerBased scope, and a new hasPolicy helper function centralizes policy-existence checks. Deduplication logic prevents duplicate llm-cost policies when cost-based rate limits are enabled alongside existing cost policies.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers Purpose with specific implementation details, but lacks Goals, Approach, User stories, Documentation, Automation tests detail, Security checks, Samples, Related PRs, and Test environment sections from the template. Complete the description by adding missing template sections: Goals, Approach, User stories, Documentation, detailed Automation tests with coverage, Security checks, Samples, Related PRs, and Test environment information.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main changes: consumer-based rate limiting and cost-based rate limiting support added to the platform API.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.4)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies"


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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

Copy link
Copy Markdown
Contributor

@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

Caution

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

⚠️ Outside diff range comments (1)
platform-api/src/internal/service/llm_deployment.go (1)

1172-1187: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope matching still misses consumer request policies in resource-wise merges.

On Line 1172 and Line 1178, scope detection uses only consumerBased. Consumer request-limit paths don’t set that flag, so backend and consumer request policies for the same path can be treated as duplicates and one can be dropped at Line 1184.

Suggested patch
+func isConsumerScopedPath(path api.LLMPolicyPath) bool {
+	if v, ok := path.Params["consumerBased"].(bool); ok {
+		return v
+	}
+	if quotas, ok := path.Params["quotas"].([]map[string]interface{}); ok {
+		for _, q := range quotas {
+			if name, _ := q["name"].(string); name == "consumer-request-limit" {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 func addOrAppendPolicyPath(policies *[]api.LLMPolicy, name, version string, path api.LLMPolicyPath) {
-	newConsumerBased, _ := path.Params["consumerBased"].(bool)
+	newConsumerBased := isConsumerScopedPath(path)
 
 	for i := range *policies {
 		if (*policies)[i].Name == name && (*policies)[i].Version == version {
 			// Only merge entries that share the same scope (backend vs consumer)
 			if len((*policies)[i].Paths) > 0 {
-				existingConsumerBased, _ := (*policies)[i].Paths[0].Params["consumerBased"].(bool)
+				existingConsumerBased := isConsumerScopedPath((*policies)[i].Paths[0])
 				if existingConsumerBased != newConsumerBased {
 					continue // different scope — skip, look for another entry
 				}
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platform-api/src/internal/service/llm_deployment.go` around lines 1172 -
1187, The scope comparison currently only compares boolean values
(newConsumerBased vs existingConsumerBased) which treats a missing consumerBased
key as equal to false and causes consumer request policies without that flag to
be merged/dropped; change the check in the loop over (*policies)[i].Paths to
compare both the presence of the consumerBased key and its boolean value (i.e.,
inspect whether Params contains "consumerBased" for new and existing paths as
well as their bool values) and treat a mismatch in presence as a scope
difference so the code continues instead of merging; update the logic around
newConsumerBased, existingConsumerBased and path.Params lookups to use the
existence flag plus value when deciding to continue.
🧹 Nitpick comments (2)
platform-api/src/internal/service/llm_deployment_test.go (2)

49-86: ⚡ Quick win

Add an assertion for fallback: default in consumer request tests.

The consumer request test validates x-wso2-application-id but not the fallback field, so this requirement can regress silently.

Suggested patch
 if !strings.Contains(yaml, "x-wso2-application-id") {
 	t.Error("expected x-wso2-application-id in key extraction for consumer request limit")
 }
+if !strings.Contains(yaml, "fallback: default") {
+	t.Error("expected fallback: default in key extraction for consumer request limit")
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@platform-api/src/internal/service/llm_deployment_test.go` around lines 49 -
86, Add an assertion in TestGenerateYAML_ConsumerRequestLimit to ensure the
generated YAML includes the consumer key-extraction fallback value by checking
that the string "fallback: default" appears (alongside the existing check for
"x-wso2-application-id"); modify the test around
generateLLMProviderDeploymentYAML/providerWithConsumerLimits to assert the
presence of "fallback: default" so this requirement cannot regress.

345-373: ⚡ Quick win

Add a resource-wise same-path backend+consumer request regression test.

Current combined request coverage is global-only. A resource-wise case with the same path for backend and consumer request limits is needed to validate non-merging behavior.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@platform-api/src/internal/service/llm_deployment.go`:
- Around line 934-937: The metadata extractor entries inside the "keyExtraction"
maps for consumer request key generation are missing the fallback field; update
the metadata extractor objects that currently read {"type": "metadata", "key":
"x-wso2-application-id"} to include the fallback property (e.g., "fallback":
"default") so the generated policy matches the intended consumer request-limit
shape—apply this change to both occurrences where "keyExtraction" contains the
routename and metadata extractor entries.

---

Outside diff comments:
In `@platform-api/src/internal/service/llm_deployment.go`:
- Around line 1172-1187: The scope comparison currently only compares boolean
values (newConsumerBased vs existingConsumerBased) which treats a missing
consumerBased key as equal to false and causes consumer request policies without
that flag to be merged/dropped; change the check in the loop over
(*policies)[i].Paths to compare both the presence of the consumerBased key and
its boolean value (i.e., inspect whether Params contains "consumerBased" for new
and existing paths as well as their bool values) and treat a mismatch in
presence as a scope difference so the code continues instead of merging; update
the logic around newConsumerBased, existingConsumerBased and path.Params lookups
to use the existence flag plus value when deciding to continue.

---

Nitpick comments:
In `@platform-api/src/internal/service/llm_deployment_test.go`:
- Around line 49-86: Add an assertion in TestGenerateYAML_ConsumerRequestLimit
to ensure the generated YAML includes the consumer key-extraction fallback value
by checking that the string "fallback: default" appears (alongside the existing
check for "x-wso2-application-id"); modify the test around
generateLLMProviderDeploymentYAML/providerWithConsumerLimits to assert the
presence of "fallback: default" so this requirement cannot regress.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 95c03838-c07a-44e2-bd82-cb3d2131015c

📥 Commits

Reviewing files that changed from the base of the PR and between 60c4463 and 036f8c5.

📒 Files selected for processing (2)
  • platform-api/src/internal/service/llm_deployment.go
  • platform-api/src/internal/service/llm_deployment_test.go

Comment on lines +934 to +937
"keyExtraction": []map[string]interface{}{
{"type": "routename"},
{"type": "metadata", "key": "x-wso2-application-id"},
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the missing metadata fallback in consumer request key extraction.

On Line 936 and Line 1024, the metadata extractor omits fallback: default, so the generated policy does not match the intended consumer request-limit shape.

Suggested patch
 "keyExtraction": []map[string]interface{}{
 	{"type": "routename"},
-	{"type": "metadata", "key": "x-wso2-application-id"},
+	{"type": "metadata", "key": "x-wso2-application-id", "fallback": "default"},
 },

Also applies to: 1022-1025

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

In `@platform-api/src/internal/service/llm_deployment.go` around lines 934 - 937,
The metadata extractor entries inside the "keyExtraction" maps for consumer
request key generation are missing the fallback field; update the metadata
extractor objects that currently read {"type": "metadata", "key":
"x-wso2-application-id"} to include the fallback property (e.g., "fallback":
"default") so the generated policy matches the intended consumer request-limit
shape—apply this change to both occurrences where "keyExtraction" contains the
routename and metadata extractor entries.

@Arshardh Arshardh merged commit 26c810e into wso2:main May 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants