Skip to content

Conversation

StageDog
Copy link

@StageDog StageDog commented Aug 31, 2025

PR 类型

  • 新功能

PR 是否包含破坏性更新?

PR 描述

例图 image

Summary by CodeRabbit

  • New Features
    • Added per-channel maximum context token limit. Admins can set this in Channel > Advanced settings via a numeric input.
    • Requests exceeding the channel’s limit are now rejected upfront with a clear error, preventing unnecessary processing and charges.
    • Token limits are propagated across the system to enforce constraints consistently in text relay flows.
    • API clients may receive a new error when prompts exceed the configured token limit.

Summary by CodeRabbit

  • New Features
    • Added per-channel token limit: set a maximum context length in Channel > Advanced Settings (default 0 = no limit).
    • Requests exceeding a channel’s token limit are now blocked early with a clear “prompt too large” error.

Copy link
Contributor

coderabbitai bot commented Aug 31, 2025

Walkthrough

Adds a per-channel token limit: new context key and Channel model field/method, middleware propagation into context, RelayInfo field, relay text validation that can return a new error code, and a web UI input to configure the limit.

Changes

Cohort / File(s) Summary
Context key & propagation
constant/context_key.go, middleware/distributor.go, relay/common/relay_info.go
Added ContextKeyChannelTokenLimit; middleware sets it from channel.GetTokenLimit(); GenRelayInfo reads it and populates RelayInfo.ChannelTokenLimit.
Channel model
model/channel.go
Added TokenLimit *int (json:"token_limit" gorm:"default:0") and GetTokenLimit() int accessor returning 0 when nil/<=0.
Relay text enforcement
relay/relay-text.go
Added checkPromptTokensInBotChannel and invoked it in TextHelper to enforce RelayInfo.ChannelTokenLimit; returns new error on violation.
Error codes
types/error.go
Added ErrorCodePromptTokensTooLarge = "prompt_tokens_too_large".
Web UI
web/src/pages/Channel/EditChannel.js
Added token_limit to originInputs (default 0) and an Advanced Settings numeric input (Form.InputNumber) to edit token_limit, included in submit payload.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as Web UI (Edit Channel)
  participant API as API Server
  participant MW as Middleware (Distributor)
  participant RC as RelayCommon (GenRelayInfo)
  participant RT as RelayText (TextHelper)

  UI->>API: Save Channel with token_limit
  Note right of API: token_limit persisted on Channel

  API->>MW: Incoming request (selected channel)
  MW->>MW: ctx[ContextKeyChannelTokenLimit] = channel.GetTokenLimit()
  MW->>RC: GenRelayInfo(ctx)
  RC->>RC: RelayInfo.ChannelTokenLimit = ctx value
  RC-->>RT: RelayInfo

  RT->>RT: Count promptTokens
  RT->>RT: checkPromptTokensInBotChannel(promptTokens, RelayInfo)
  alt tokens > ChannelTokenLimit
    RT-->>API: Error: prompt_tokens_too_large
    API-->>UI: 4xx with error code
  else OK
    RT->>RT: continue pricing/relay
    RT-->>API: normal response
    API-->>UI: success
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I nibble bytes and count each crumb,
A cap on tokens—no more glum.
Context whispers, “limit set,”
Relay checks with gentle vet.
UI slider shines; the burrow hums. 🥕✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f200496 and 7ac2070.

📒 Files selected for processing (7)
  • constant/context_key.go (1 hunks)
  • middleware/distributor.go (1 hunks)
  • model/channel.go (2 hunks)
  • relay/common/relay_info.go (3 hunks)
  • relay/relay-text.go (2 hunks)
  • types/error.go (1 hunks)
  • web/src/pages/Channel/EditChannel.js (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • middleware/distributor.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • constant/context_key.go
  • types/error.go
  • relay/common/relay_info.go
  • web/src/pages/Channel/EditChannel.js
  • relay/relay-text.go
  • model/channel.go
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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)
relay/relay-text.go (1)

116-128: Bug: relayInfo.PromptTokens isn’t set when computed here (breaks fallback usage/logging).

If prompt_tokens isn’t already in the Gin context, you compute it but never assign to relayInfo.PromptTokens. Later, postConsumeQuota uses relayInfo.PromptTokens when usage is nil, resulting in 0.

     } else {
         promptTokens, err = getPromptTokens(textRequest, relayInfo)
         // count messages token error 计算promptTokens错误
         if err != nil {
             return types.NewError(err, types.ErrorCodeCountTokenFailed)
         }
         c.Set("prompt_tokens", promptTokens)
+        relayInfo.PromptTokens = promptTokens
     }
🧹 Nitpick comments (8)
constant/context_key.go (1)

34-34: Document the semantics (0 = unlimited) and unit for the new key.

Please add a brief comment clarifying that this value is a per-channel prompt token cap, measured in tokens, and that 0 disables the cap. This avoids ambiguity across middleware/relay layers.

Apply this diff:

- ContextKeyChannelTokenLimit        ContextKey = "channel_token_limit"
+ // per-channel prompt token limit (prompt/context tokens only); 0 means unlimited
+ ContextKeyChannelTokenLimit        ContextKey = "channel_token_limit"

Can you confirm that the enforcement code treats values <= 0 as “unlimited”?

types/error.go (1)

41-41: Set client error status for prompt-token limit
In relay/relay-text.go at the checkPromptTokensInBotChannel error path (line 132), types.NewError(err, types.ErrorCodePromptTokensTooLarge) defaults to HTTP 500. Replace it with types.NewErrorWithStatusCode(err, types.ErrorCodePromptTokensTooLarge, http.StatusBadRequest) (or 422) so token-limit violations return a 4xx status.

web/src/pages/Channel/EditChannel.js (1)

1375-1384: Clarify UX: show “0 = unlimited” and what is being limited.

Add brief help text and a clearer placeholder to reduce confusion between prompt vs output tokens.

Apply this diff:

 <Form.InputNumber
   field='token_limit'
-  label={t('最大上下文')}
-  placeholder={t('最大上下文')}
+  label={t('最大上下文')}
+  placeholder={t('最大上下文,0 表示不限制')}
   min={0}
   onNumberChange={(value) => handleInputChange('token_limit', value)}
   style={{ width: '100%' }}
+  extraText={t('仅限制提示词 tokens(输入上下文),不限制输出 tokens')}
 />
relay/common/relay_info.go (2)

65-65: Document the semantics of ChannelTokenLimit (0 = unlimited) and avoid negative surprises.

Add a short field comment to make the contract explicit. Consider treating negatives as 0.

-    ChannelTokenLimit int
+    ChannelTokenLimit int // 0 means unlimited; negative values are treated as 0

219-221: Clamp channelTokenLimit to zero if negative.

Add a guard after reading the value to prevent negative limits from blocking requests:

-    channelTokenLimit := common.GetContextKeyInt(c, constant.ContextKeyChannelTokenLimit)
+    channelTokenLimit := common.GetContextKeyInt(c, constant.ContextKeyChannelTokenLimit)
+    if channelTokenLimit < 0 {
+        channelTokenLimit = 0
+    }
relay/relay-text.go (3)

130-134: Return 400 Bad Request on limit violation (don’t use 500).

Exceeding a per-channel prompt limit is a client error. Use NewErrorWithStatusCode to return 400.

-    err = checkPromptTokensInBotChannel(promptTokens, relayInfo)
-    if err != nil {
-        return types.NewError(err, types.ErrorCodePromptTokensTooLarge)
-    }
+    err = checkPromptTokensInBotChannel(promptTokens, relayInfo)
+    if err != nil {
+        return types.NewErrorWithStatusCode(err, types.ErrorCodePromptTokensTooLarge, http.StatusBadRequest)
+    }

268-274: Name/message polish and defensive semantics.

  • Function name can be clearer; current name mixes “Bot” jargon. Suggest “checkPromptTokensAgainstChannelLimit”.
  • Grammar: “tokens are greater…”
  • Optional: treat negative limits as unlimited (defensive).
-func checkPromptTokensInBotChannel(promptTokens int, info *relaycommon.RelayInfo) error {
-    if info.ChannelTokenLimit > 0 && promptTokens > info.ChannelTokenLimit {
-        return fmt.Errorf("prompt tokens (%d) is greater than channel token limit (%d)", promptTokens, info.ChannelTokenLimit)
-    }
-    return nil
-}
+func checkPromptTokensAgainstChannelLimit(promptTokens int, info *relaycommon.RelayInfo) error {
+    limit := info.ChannelTokenLimit
+    if limit < 0 {
+        limit = 0
+    }
+    if limit > 0 && promptTokens > limit {
+        return fmt.Errorf("prompt tokens (%d) are greater than channel token limit (%d)", promptTokens, limit)
+    }
+    return nil
+}

And update the call site:

-err = checkPromptTokensInBotChannel(promptTokens, relayInfo)
+err = checkPromptTokensAgainstChannelLimit(promptTokens, relayInfo)

268-274: Optional: enforce “prompt + planned completion” against the limit.

If “最大上下文” is intended as total context tokens, consider also checking promptTokens + max(textRequest.MaxTokens, textRequest.MaxCompletionTokens) against the limit. Would require passing the request into the check function; keep it off by default to avoid false rejections on generous limits.

Do you want me to draft this change gated by a config flag (e.g., enforce_total_context_limit: bool) so it can be rolled out safely?

Also applies to: 130-134

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c826d06 and f200496.

📒 Files selected for processing (7)
  • constant/context_key.go (1 hunks)
  • middleware/distributor.go (1 hunks)
  • model/channel.go (2 hunks)
  • relay/common/relay_info.go (3 hunks)
  • relay/relay-text.go (3 hunks)
  • types/error.go (1 hunks)
  • web/src/pages/Channel/EditChannel.js (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-21T03:37:41.726Z
Learnt from: 9Ninety
PR: QuantumNous/new-api#1273
File: relay/channel/gemini/relay-gemini.go:97-116
Timestamp: 2025-06-21T03:37:41.726Z
Learning: In relay/channel/gemini/relay-gemini.go, the thinking budget calculation logic (including the MaxOutputTokens multiplication) was introduced in PR #1247. PR #1273 focused specifically on decoupling the thoughts summary feature from thinking budget settings and did not modify the existing thinking budget behavior.

Applied to files:

  • relay/relay-text.go
🧬 Code graph analysis (4)
middleware/distributor.go (2)
common/gin.go (1)
  • SetContextKey (49-51)
constant/context_key.go (1)
  • ContextKeyChannelTokenLimit (34-34)
relay/common/relay_info.go (2)
common/gin.go (1)
  • GetContextKeyInt (61-63)
constant/context_key.go (1)
  • ContextKeyChannelTokenLimit (34-34)
web/src/pages/Channel/EditChannel.js (1)
web/src/pages/Detail/index.js (1)
  • handleInputChange (624-630)
relay/relay-text.go (2)
types/error.go (2)
  • NewError (146-154)
  • ErrorCodePromptTokensTooLarge (41-41)
relay/common/relay_info.go (1)
  • RelayInfo (62-113)
🔇 Additional comments (5)
model/channel.go (1)

47-47: Pointer + default: verify migration and allow‐null semantics

  • Confirm &model.Channel is included in the DB.AutoMigrate(...) call (model/main.go) so the token_limit column is created.
  • Ensure the column is NULL-able and that GetTokenLimit() correctly treats nil as 0.
  • Server-side must still sanitize negative token_limit values from raw API clients, despite the UI’s min=0.
web/src/pages/Channel/EditChannel.js (1)

105-105: LGTM: sensible default.

Defaulting token_limit to 0 (unlimited) matches server semantics.

middleware/distributor.go (1)

270-270: LGTM: propagates channel token limit via context.

This surfaces the limit early for relay code to consume. With the accessor clamping negatives to 0, the context value will be safe to read.

Please confirm relay/common/relay_info.GenRelayInfo reads this via common.GetContextKeyInt and that enforcement checks use “> 0” before comparing.

relay/common/relay_info.go (1)

240-241: LGTM — value is plumbed into RelayInfo.

relay/relay-text.go (1)

504-504: LGTM — simpler string concatenation over fmt.Sprintf.

Minor readability/micro-alloc improvement.

@StageDog StageDog changed the base branch from main to alpha August 31, 2025 13:41
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