mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-16 12:14:44 +00:00
fix(quotas): keyless agent chat bucket, keep disabled policies disabled, cached rates
- check_usage treats a request through a keyless (draft) agent as agent traffic, matching how its usage rows are bucketed and the headless rule - dashboard edits carry the stored enabled flag instead of re-enabling the policy; disabled policies are labelled in the Quotas tab and the editor - quota 429s send x-should-retry: false so OpenAI SDK clients do not retry a refusal that cannot succeed before the reset - cached-input and cache-write rates for Anthropic, OpenRouter and Groq gpt-oss-120b; refresh OpenRouter deepseek-v3.2 list prices - UsageQuota reuses usagePercent; docs note that a user override needs an existing user
This commit is contained in:
1 parent
b5296df8a9
commit
1e14605ee7
15 files changed
+87
-22
No files matched your search
@@ -132,7 +132,9 @@ class AnswerResource(Resource, BaseAnswerResource):
|
||||
return make_response({"error": "Unauthorized"}, 401)
|
||||
|
||||
if error := self.check_usage(
|
||||
processor.agent_config, processor.decoded_token
|
||||
processor.agent_config,
|
||||
processor.decoded_token,
|
||||
agent_id=processor.agent_id,
|
||||
):
|
||||
return error
|
||||
|
||||
|
||||
@@ -115,7 +115,10 @@ class BaseAnswerResource:
|
||||
return prepared
|
||||
|
||||
def check_usage(
|
||||
self, agent_config: Dict, decoded_token: Optional[Dict] = None
|
||||
self,
|
||||
agent_config: Dict,
|
||||
decoded_token: Optional[Dict] = None,
|
||||
agent_id: Optional[str] = None,
|
||||
) -> Optional[Response]:
|
||||
"""Refuse the request when a usage limit is exhausted.
|
||||
|
||||
@@ -127,6 +130,8 @@ class BaseAnswerResource:
|
||||
agent_config: The config dict of agent instance
|
||||
decoded_token: The request's resolved identity; its ``sub`` is the
|
||||
billable user.
|
||||
agent_id: The agent the request runs through. A draft agent has no
|
||||
key, but its usage rows carry the agent id, so it is agent traffic.
|
||||
|
||||
Returns:
|
||||
None or Response if either of limits exceeded.
|
||||
@@ -134,7 +139,7 @@ class BaseAnswerResource:
|
||||
"""
|
||||
api_key = agent_config.get("user_api_key")
|
||||
user_id = (decoded_token or {}).get("sub") or agent_config.get("user_id")
|
||||
exceeded = QuotaService.check(user_id, "agent" if api_key else "direct")
|
||||
exceeded = QuotaService.check(user_id, "agent" if api_key or agent_id else "direct")
|
||||
if exceeded is not None:
|
||||
return quota_exceeded_response(exceeded)
|
||||
if not api_key:
|
||||
@@ -227,7 +232,9 @@ class BaseAnswerResource:
|
||||
Returns:
|
||||
None, or the refusal Response.
|
||||
"""
|
||||
error = self.check_usage(processor.agent_config, processor.decoded_token)
|
||||
error = self.check_usage(
|
||||
processor.agent_config, processor.decoded_token, agent_id=processor.agent_id
|
||||
)
|
||||
if error is None or not conversation_id:
|
||||
return error
|
||||
user = processor.initial_user_id or (processor.decoded_token or {}).get("sub")
|
||||
|
||||
@@ -154,7 +154,9 @@ class StreamResource(Resource, BaseAnswerResource):
|
||||
)
|
||||
|
||||
if error := self.check_usage(
|
||||
processor.agent_config, processor.decoded_token
|
||||
processor.agent_config,
|
||||
processor.decoded_token,
|
||||
agent_id=processor.agent_id,
|
||||
):
|
||||
return error
|
||||
should_persist, visibility = resolve_persistence(
|
||||
|
||||
@@ -344,7 +344,9 @@ def chat_completions():
|
||||
if claimed_conversation_id:
|
||||
usage_error = helper.check_usage_on_resume(processor, claimed_conversation_id)
|
||||
else:
|
||||
usage_error = helper.check_usage(processor.agent_config, processor.decoded_token)
|
||||
usage_error = helper.check_usage(
|
||||
processor.agent_config, processor.decoded_token, agent_id=processor.agent_id
|
||||
)
|
||||
if usage_error:
|
||||
return usage_error
|
||||
|
||||
|
||||
Reference in new issue
Block a user