Skip to content

feat(proxy): add project-level model_itpm_limit/model_otpm_limit - #35058

Closed
shivijain2323 wants to merge 13 commits into
BerriAI:litellm_internal_stagingfrom
shivijain2323:feature/bedrock-mantle-quota-project
Closed

feat(proxy): add project-level model_itpm_limit/model_otpm_limit#35058
shivijain2323 wants to merge 13 commits into
BerriAI:litellm_internal_stagingfrom
shivijain2323:feature/bedrock-mantle-quota-project

Conversation

@shivijain2323

@shivijain2323 shivijain2323 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

We already support model_tpm_limit/model_rpm_limit per project, but that treats input and output tokens as one combined bucket, which doesn't match how Bedrock Mantle actually meters usage. This adds model_itpm_limit/model_otpm_limit so a project can cap input and output tokens separately, same shape as the existing fields.

There's already deployment-level itpm/otpm support in router_utils/pre_call_checks/io_token_rate_limit_check.py, but that code only ever deals with one deployment at a time, whereas the proxy hook (parallel_request_limiter_v3.py) has to juggle key/team/org/project limits together on the same request. Rather than bolt a second copy of the reservation logic on, this just adds project-scoped ITPM/OTPM as two more descriptors into the same reservation path the hook already uses for TPM/RPM.

If a project sets both the combined TPM limit and the new itpm/otpm limits on the same model, both get enforced, not one overriding the other, matching what the deployment-level check already does. Input and output are reserved separately up front, and if one side reserves fine but the other is over limit, we roll back the side that already went through so nothing gets left over-counted. On the way back, cached prompt tokens get excluded from the ITPM count since that's how Bedrock bills it, but that's purely for the rate limit math, cost and usage logging still see the full token count.

Problem this solves:

  • Bedrock Mantle bills input and output tokens separately, not one combined bucket
  • Project-level quotas only support combined TPM/RPM today, not split limits

How it solves it:

  • Adds model_itpm_limit/model_otpm_limit on project, same shape as model_tpm_limit
  • Enforces both via the project rate-limit hook's existing reservation/reconciliation path

Relevant issues

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Type

🆕 New Feature

Changes

Adds model_itpm_limit/model_otpm_limit to LiteLLM_ProjectTable, NewProjectRequest, and UpdateProjectRequest, routed into project metadata the same way model_tpm_limit already is, with a matching Prisma migration.

Extends parallel_request_limiter_v3.py with project-scoped ITPM/OTPM rate-limit descriptors, built alongside the existing combined-TPM descriptor rather than replacing it, so both are enforced together when a project configures both (with a one-time warning), matching how the deployment-level itpm/otpm check already handles that overlap. Input and output token estimates are reserved atomically per bucket ahead of the call, with the input-side reservation rolled back if the output side turns out to be over limit. On success, the reservation reconciles against actual usage, excluding cached prompt tokens from the input count to match Bedrock's own metering, without touching cost or usage logging elsewhere. On failure, both reservations are refunded.

Adds get_project_model_itpm_limit/get_project_model_otpm_limit to auth_utils.py for parity with the existing key/team/project rate-limit accessors.

Fixes review findings from Greptile and veria-ai: two spots (the combined-TPM and OTPM output-cap checks in parallel_request_limiter_v3.py) still classified any request with data["input"] set as an embedding, which also misclassifies the Responses API (it puts its prompt in "input" too, but does generate output) and skipped the output cap entirely for it. Both now check call_type the same way the token estimator already does. Also routes Responses API input through the standard transform_responses_api_input_to_messages helper before token counting, since token_counter's text argument only joins plain strings in a list and was silently dropping input_image content blocks from the ITPM estimate.

Fixes a second Greptile finding on the same code: both output-cap spots wrote the implicit cap to data["max_tokens"], but the Responses-to-chat-completion transformation only reads max_output_tokens, so the cap was silently dropped before provider dispatch for Responses calls, letting an unbounded generation blow past the reserved TPM/OTPM budget. Both call sites now go through one shared helper (_apply_implicit_output_cap) that picks the field the request type actually honors.

Fixes a third Greptile finding: explicit_max_tokens was resolved via a truthy max_tokens or max_completion_tokens or max_output_tokens chain, so an explicit 0 in a non-last field got folded away as absent. The project OTPM reservation also unconditionally floored estimated_output_tokens to at least 1, which overrode even a correctly-resolved explicit zero and could false-reject a genuine zero-output request against an already-exhausted OTPM bucket. The resolver now returns the first non-None field regardless of falsiness, and the OTPM floor is skipped whenever the caller set an explicit output cap.

Out of scope for this PR: key/team/org-level model_itpm_limit/model_otpm_limit, and Admin UI support (only the auto-generated schema.d.ts types are updated; no form fields yet).

Final Attestation

  • [ X] The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Docs PR BerriAI/litellm-docs#638

shivijain2323 and others added 6 commits July 29, 2026 09:28
We already support model_tpm_limit/model_rpm_limit per project, but
that treats input and output tokens as one combined bucket, which
doesn't match how Bedrock Mantle actually meters usage. This adds
model_itpm_limit/model_otpm_limit so a project can cap input and output
tokens separately, same shape as the existing fields.

There's already deployment-level itpm/otpm support in
router_utils/pre_call_checks/io_token_rate_limit_check.py, but that
code only ever deals with one deployment at a time, whereas the proxy
hook (parallel_request_limiter_v3.py) has to juggle key/team/org/project
limits together on the same request. Rather than bolt a second copy of
the reservation logic on, this just adds project-scoped ITPM/OTPM as
two more descriptors into the same reservation path the hook already
uses for TPM/RPM.

If a project sets both the combined TPM limit and the new itpm/otpm
limits on the same model, both get enforced, not one overriding the
other, matching what the deployment-level check already does. Input
and output are reserved separately up front, and if one side reserves
fine but the other is over limit, we roll back the side that already
went through so nothing gets left over-counted. On the way back,
cached prompt tokens get excluded from the ITPM count since that's how
Bedrock bills it, but that's purely for the rate limit math, cost and
usage logging still see the full token count.
…ection

async_post_call_failure_hook had its own refund path, separate from
async_log_failure_event, for requests rejected before the LLM call ever
ran (e.g. a downstream guardrail block). That path predates itpm/otpm
and only knew about the combined TPM reservation: it refunded every
token descriptor by the flat combined amount, which is wrong once the
project itpm/otpm descriptors are in that same list, and it returned
early whenever the combined reservation was zero, which is exactly the
case when a project only sets model_itpm_limit/model_otpm_limit with
no model_tpm_limit at all.
async_release_max_parallel_requests_on_disconnect only released the
parallel slot; any stashed ITPM/OTPM reservations stayed inflated until
the window TTL, letting a caller repeatedly start and cancel streams to
exhaust the project quota without consuming tokens.

The fix mirrors the refund path already in async_log_failure_event:
check the shared release guard, build pipeline ops to zero out each
bucket, flush, then mark released so failure callbacks can't
double-refund if they somehow fire later.

The slot release is now conditional (if acquisition is not None) rather
than an early-return gate, so the refund runs even on requests where the
parallel slot was already cleared.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shivijain2323
shivijain2323 requested a review from a team July 29, 2026 06:18
Comment thread litellm/proxy/hooks/parallel_request_limiter_v3.py Outdated
@greptile-apps

greptile-apps Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds project-scoped input and output token-per-minute limits.

  • Adds project model ITPM/OTPM fields across the database schema, API models, authentication metadata, and generated UI types.
  • Extends the parallel request limiter with separate project input/output reservations, rollback, refund, and post-call reconciliation.
  • Updates Responses API token estimation and output-cap handling, including multimodal input accounting and cached-input exclusions.
  • Adds tests for project metadata propagation, reservation behavior, reconciliation, concurrency, and Responses API edge cases.

Confidence Score: 3/5

This PR is not yet safe to merge because zero-output Responses requests can reserve no project OTPM while the dispatched request permits up to 16 output tokens.

The limiter preserves max_output_tokens=0 and skips the reservation floor, but the downstream OpenAI Responses transformation normalizes that value to 16, allowing concurrent requests to generate unreserved output before post-call reconciliation.

Files Needing Attention: litellm/proxy/hooks/parallel_request_limiter_v3.py, litellm/llms/openai/responses/transformation.py, tests/test_litellm/proxy/hooks/test_tpm_concurrent.py

Important Files Changed

Filename Overview
litellm/proxy/hooks/parallel_request_limiter_v3.py Adds project ITPM/OTPM reservation and reconciliation, but explicit zero-output Responses requests remain under-reserved because downstream normalization permits 16 output tokens.
litellm/models/project.py Exposes the new dedicated project rate-limit columns through merged project metadata.
litellm/proxy/auth/user_api_key_auth.py Propagates merged project metadata through the existing authentication paths.
litellm/proxy/_types.py Adds the project ITPM/OTPM fields to project create and update request models.
litellm-proxy-extras/litellm_proxy_extras/migrations/20260722200000_add_project_itpm_otpm_limit/migration.sql Adds non-null JSONB columns for project model ITPM and OTPM limits with empty-object defaults.
tests/test_litellm/proxy/hooks/test_parallel_request_limiter_v3.py Expands limiter tests for split input/output reservations, refunds, reconciliation, and Responses API behavior.
tests/test_litellm/proxy/hooks/test_tpm_concurrent.py Adds concurrency and zero-cap coverage, but the zero-cap expectation does not account for downstream normalization to 16 output tokens.

Reviews (10): Last reviewed commit: "fix(proxy): honor an explicit zero outpu..." | Re-trigger Greptile

Comment thread litellm/proxy/hooks/parallel_request_limiter_v3.py Outdated
Comment thread litellm/proxy/hooks/parallel_request_limiter_v3.py Outdated
self._build_reservation_aware_tpm_ops(
targets=list(itpm_scopes),
reserved_scopes=itpm_scopes,
actual_tokens=0,

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.

Medium: Stream cancellation can refund consumed tokens

Disconnect cleanup assumes actual usage is zero and refunds the full ITPM/OTPM reservation. The caller reaches this path whenever disconnect-time success logging is unavailable—for example when disable_streaming_logging is enabled or partial response assembly fails—even if chunks were already delivered. A project key can repeatedly read most of a stream and disconnect to consume tokens without retaining any quota charge. Keep the reservation when partial usage is unknown, or reconcile it from collected stream usage instead of treating the request as unused.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This method is the last-resort branch: it only runs when neither the success nor failure logging callback ever fired for the request, which per the existing docstring on _arelease_max_parallel_requests_on_disconnect is distinct from the case where a client reads most of a stream then disconnects (that's the "partial-spend billing" event, which fires its own success callback with real usage and reconciles normally through the existing path, never reaching this method). In the actual case this method handles, there is no partial usage available to reconcile against at all, so refunding the full reservation is strictly better than the pre-fix behavior of leaking it until the window's TTL. Note the disable_streaming_logging flag cited in the finding doesn't exist in this codebase. Not changing this further; flagging for visibility.

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.

The fallback remains reachable after delivered chunks: disable_streaming_logging exists and returns False at litellm/proxy/common_request_processing.py:193-194, while assembly failures do likewise at lines 217-221; cleanup then invokes the full-refund path at lines 2713-2720. That path reconciles both reservations with actual_tokens=0 at litellm/proxy/hooks/parallel_request_limiter_v3.py:4123-4140, so consumed input/output tokens can remain uncharged.

@veria-ai

veria-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

PR overview

This pull request adds project-level per-model input and output token-per-minute limits to the proxy and integrates them with project configuration and parallel request limiting.

Six issues remain open, including a project configuration handling flaw that can disable configured model RPM, TPM, ITPM, and OTPM enforcement. Several request and streaming edge cases also allow project keys to under-reserve tokens or refund consumed capacity, enabling quota bypass and excess provider usage. Three issues have already been addressed, but the remaining enforcement gaps should be resolved before relying on these limits as a security or cost-control boundary.

Open issues (6)

Fixed/addressed: 3 · PR risk: 6/10

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.42765% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/proxy/hooks/parallel_request_limiter_v3.py 97.61% 7 Missing ⚠️
litellm/proxy/auth/user_api_key_auth.py 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

… image content

Two remaining spots still used data.get("input") is not None alone to
detect embeddings for the project TPM/OTPM output-cap logic, missing the
call_type check the estimator itself already uses. That misclassified
Responses API calls as embeddings and skipped the max_tokens output cap
entirely, letting an unbounded generation blow past OTPM before post-call
reconciliation.

Also route Responses API input through the standard
transform_responses_api_input_to_messages helper before token counting.
token_counter's text argument only joins plain strings in a list, so an
input_image content block contributed close to zero tokens to the ITPM
estimate instead of the real image token count.
Comment thread litellm/proxy/hooks/parallel_request_limiter_v3.py Outdated
@codspeed-hq

codspeed-hq Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing shivijain2323:feature/bedrock-mantle-quota-project (2e29f34) with litellm_internal_staging (2f7574d)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (c274cf3) during the generation of this report, so 2f7574d was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

- _refund_reserved_tokens no-op when amount is zero
- reserve_io_tokens early-return when no ITPM/OTPM descriptors present
- reserve_io_tokens ITPM-only path (no OTPM descriptors)
- _warn_project_io_token_and_tpm_coexist_once suppression after first warn
- _strip_audio_content_blocks passthrough for non-list, non-dict, and
  non-list-content edge cases
- OTPM over-limit path releases a stashed parallel slot before raising
- ITPM/OTPM-only request (no combined TPM/RPM) stores rate-limit
  response status in data
- _get_reserved_itpm/otpm_tokens_from_kwargs returns 0 for non-numeric
  corrupted stash
- _narrow_reserved_scopes returns empty set for non-list input
- _resolve_io_token_reconcile_usage: ResponseAPIUsage with cached tokens,
  dict-shaped usage, and unrecognised usage type
- _build_io_token_reservation_ops returns empty list when usage is
  unresolvable
- disconnect cleanup returns early when reservation already released
- async_post_call_failure_hook skips RPM-only descriptors (no
  tokens_per_unit) in the combined-TPM refund loop
- exception in disconnect IO refund is swallowed, not propagated

Also removes one dead line: the  after _handle_rate_limit_error
in _reserve_project_io_tokens_or_raise (unreachable since that function
always raises).
@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptileai review

Previously every input_audio block reserved a flat DEFAULT_AUDIO_TOKEN_ESTIMATE
(300) tokens regardless of audio length. A caller could pack a 10-minute
recording into a single block and issue concurrent requests that each reserved
only 300 tokens, exhausting the project ITPM quota while paying negligible
quota cost.

_count_audio_content_blocks is replaced by two new methods:

- _estimate_audio_block_tokens: derives the token estimate for one block from
  the base64 payload length (decoded_bytes // _AUDIO_BYTES_PER_TOKEN), assuming
  the lowest reasonable bitrate (8 kHz mono PCM-16, 1600 bytes/token) so longer
  recordings always reserve proportionally more. Floored at
  DEFAULT_AUDIO_TOKEN_ESTIMATE (so reference-only blocks and genuinely short
  clips still get a non-trivial reservation) and capped at
  _MAX_AUDIO_TOKENS_PER_BLOCK (6000, roughly 10 minutes) so malformed or
  enormous payloads can't claim unbounded reservations.
- _estimate_audio_content_tokens: sums _estimate_audio_block_tokens across all
  messages; drives both the strip-before-counting decision and the audio token
  add-on in _estimate_precise_input_tokens.

Also covers remaining uncovered lines on the PR patch with 19 new unit tests
(see previous commit), and removes one dead unreachable return statement.

Co-authored-by: Cursor <cursoragent@cursor.com>
token_counter(
model=model or "",
messages=countable_messages,
text=None if countable_messages is not None else (data.get("prompt") or data.get("input")),

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.

Medium: Referenced files bypass ITPM reservation

The Responses transformer converts input_file into a file content block, which token_counter rejects as unsupported. The exception fallback then counts only the serialized file_id or URL, not the referenced document, so a project key can concurrently submit a large uploaded file while each request reserves only a handful of ITPM tokens. Resolve referenced file size/token usage before reservation, or apply a conservative reservation and reject file references when their content cannot be measured.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A complete fix requires fetching file content from the Files API at pre-call time, which adds a mandatory network round-trip to every Responses request. The post-call reconciliation path already adjusts the ITPM counter to actual usage after the call, so the window is bounded to the in-flight period. Deferring to a follow-up.

…adata

project_metadata was populated from _project_obj.metadata (the freeform
JSONB column), so get_project_model_itpm_limit / get_project_model_otpm_limit
always returned None even when the dedicated model_itpm_limit /
model_otpm_limit columns were set. Add LiteLLM_ProjectTable.merged_metadata,
which overlays the four dedicated rate-limit columns on top of the base
metadata dict (None columns are skipped so legacy metadata values are not
clobbered). Switch all four call-sites in user_api_key_auth.py to use
merged_metadata instead of .metadata.
@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptileai review


messages = data.get("messages")
if messages is None and call_type in RESPONSES_API_CALL_TYPES and data.get("input") is not None:
messages = self._responses_input_to_chat_messages(data)

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.

Low: Chained Responses requests bypass ITPM reservation

This transforms only the current input; the Responses bridge appends history referenced by previous_response_id later in async_responses_api_session_handler. A project key can create a large response and launch concurrent tiny continuations referencing it, with each request reserving only the tiny new input while the provider processes the full chain. Resolve and count the stored context before reservation, or conservatively reserve its recorded input usage.

@shivijain2323 shivijain2323 Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a pre-existing limitation shared with model_tpm_limit. Fixing it properly requires fetching stored context token counts from the spend log before reservation , a synchronous DB call in the pre-call hot path for all Responses API traffic.

@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread litellm/proxy/hooks/parallel_request_limiter_v3.py Outdated
@greptile-apps

greptile-apps Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

…/TPM

Both has_explicit_max_tokens checks (TPM path and OTPM path) only looked at
max_tokens and max_completion_tokens, so a Responses API request that set
max_output_tokens was treated as unbounded. The hook then injected a smaller
data["max_tokens"] = capped_floor, silently truncating the response despite
the reservation already honoring the caller's max_output_tokens.

Add max_output_tokens to both checks so Responses API requests with an
explicit output bound are excluded from the cap-injection branch.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptileai review

1 similar comment
@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread litellm/models/project.py
"model_rpm_limit": self.model_rpm_limit,
"model_tpm_limit": self.model_tpm_limit,
}.items()
if v is not None

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.

Medium: Project rate-limit bypass

The project request validators move these limits into metadata, but the Prisma columns are non-null and default to {}. Consequently, every loaded row includes non-None empty dictionaries here, which overwrite the configured metadata limits before authentication passes them to the limiter; a project key can then exceed its configured model RPM, TPM, ITPM, and OTPM quotas. Persist these fields in the dedicated columns, or merge them in a way that preserves metadata-backed limits while providing an explicit migration/clearing strategy.

…ens for Responses calls

The Responses-to-chat-completion transformation only reads
max_output_tokens, so writing the implicit output cap to data["max_tokens"]
was silently dropped before provider dispatch, letting an unbounded
Responses generation blow past the TPM/OTPM budget. Both injection sites
now go through one shared helper that picks the field the request type
actually honors.
@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread litellm/proxy/hooks/parallel_request_limiter_v3.py Outdated
…eservation

explicit_max_tokens was resolved via a truthy `or` chain across
max_tokens/max_completion_tokens/max_output_tokens, so an explicit 0 in a
non-last field was folded away as absent. Separately, the OTPM reservation
unconditionally floored estimated_output_tokens to at least 1, overriding
even a correctly-resolved explicit zero and false-rejecting a zero-output
request against an already-exhausted OTPM bucket. Both are fixed: the
resolver now returns the first non-None field regardless of falsiness, and
the OTPM floor is skipped whenever the caller set an explicit output cap.
a legitimate zero-output request -- counts as explicit.
"""
return (
data.get("max_tokens") is not None

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.

Low: Ignored output fields bypass token reservation

These checks need to be endpoint-specific. For example, /v1/responses accepts raw JSON here but its bridge only applies max_output_tokens; sending max_tokens: 1 therefore reserves one OTPM token and suppresses the implicit cap, while the field is later ignored and the model can generate an unbounded response. The inverse applies to chat requests carrying max_output_tokens. Select the recognized cap field from call_type and ignore unsupported aliases when estimating or deciding whether to inject a cap.

@shivijain2323

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment on lines +2814 to +2815
if not self._has_explicit_output_cap(data):
estimated_output_tokens = max(estimated_output_tokens, 1)

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.

P1 Zero cap under-reserves OTPM

When an OpenAI Responses request sets max_output_tokens=0, this branch skips the output reservation floor and reserves zero tokens, but the downstream OpenAI transformation raises the effective cap to 16. The provider can therefore generate up to 16 unreserved output tokens, allowing concurrent requests to exceed the project OTPM limit before reconciliation.

Knowledge Base Used: Proxy Server Request Flow

"""
from litellm import token_counter

messages = data.get("messages")

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.

Low: Ignored fields shadow billable input

/v1/responses passes raw JSON through this hook before unsupported parameters are filtered, so a caller can send a large input together with messages: []. This branch counts the empty messages, while the Responses transformation later discards that field and sends the large input, allowing concurrent requests to exceed the project's ITPM limit. Select the token-counting source by call_type; Responses calls should always transform input, while embeddings should count input, completions should count prompt, and chat calls should count messages.

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