Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
aisbf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
aisbf
Commits
11df0261
Commit
11df0261
authored
Jul 26, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feat/per-provider-rate-limit-per-second'
parents
979a1320
9a5299bb
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
25 additions
and
20 deletions
+25
-20
__init__.py
aisbf/__init__.py
+1
-1
config.py
aisbf/config.py
+1
-0
handlers.py
aisbf/handlers.py
+13
-17
pyproject.toml
pyproject.toml
+1
-1
setup.py
setup.py
+1
-1
providers.html
templates/dashboard/providers.html
+4
-0
user_providers.html
templates/dashboard/user_providers.html
+4
-0
No files found.
aisbf/__init__.py
View file @
11df0261
...
@@ -55,7 +55,7 @@ from .auth.qwen import QwenOAuth2
...
@@ -55,7 +55,7 @@ from .auth.qwen import QwenOAuth2
from
.handlers
import
RequestHandler
,
RotationHandler
,
AutoselectHandler
from
.handlers
import
RequestHandler
,
RotationHandler
,
AutoselectHandler
from
.utils
import
count_messages_tokens
,
split_messages_into_chunks
,
get_max_request_tokens_for_model
,
get_max_completion_tokens_for_model
from
.utils
import
count_messages_tokens
,
split_messages_into_chunks
,
get_max_request_tokens_for_model
,
get_max_completion_tokens_for_model
__version__
=
"0.99.9
5
"
__version__
=
"0.99.9
6
"
__all__
=
[
__all__
=
[
# Config
# Config
"config"
,
"config"
,
...
...
aisbf/config.py
View file @
11df0261
...
@@ -112,6 +112,7 @@ class ProviderConfig(BaseModel):
...
@@ -112,6 +112,7 @@ class ProviderConfig(BaseModel):
# Per-provider client rate limit: a global cap (all clients combined) on
# Per-provider client rate limit: a global cap (all clients combined) on
# requests forwarded to this provider, protecting the upstream from hammering
# requests forwarded to this provider, protecting the upstream from hammering
# (e.g. exhausting a per-IP free-model quota). 0 = unlimited.
# (e.g. exhausting a per-IP free-model quota). 0 = unlimited.
client_rate_limit_rps
:
int
=
0
# max client requests per second to this provider
client_rate_limit_rpm
:
int
=
0
# max client requests per minute to this provider
client_rate_limit_rpm
:
int
=
0
# max client requests per minute to this provider
client_rate_limit_rph
:
int
=
0
# max client requests per hour to this provider
client_rate_limit_rph
:
int
=
0
# max client requests per hour to this provider
api_key
:
Optional
[
str
]
=
None
# Optional API key in provider config
api_key
:
Optional
[
str
]
=
None
# Optional API key in provider config
...
...
aisbf/handlers.py
View file @
11df0261
...
@@ -196,24 +196,20 @@ def _provider_rl_check(bucket: str, window_seconds: int, max_requests: int) -> t
...
@@ -196,24 +196,20 @@ def _provider_rl_check(bucket: str, window_seconds: int, max_requests: int) -> t
def
_provider_rl_limits
(
provider_config
)
->
tuple
:
def
_provider_rl_limits
(
provider_config
)
->
tuple
:
"""Extract (rpm, rph) client rate limits from a provider config (object or dict)."""
"""Extract (rp
s, rp
m, rph) client rate limits from a provider config (object or dict)."""
if
provider_config
is
None
:
if
provider_config
is
None
:
return
0
,
0
return
0
,
0
,
0
if
isinstance
(
provider_config
,
dict
):
if
isinstance
(
provider_config
,
dict
):
rpm
=
provider_config
.
get
(
'client_rate_limit_rpm'
,
0
)
get
=
provider_config
.
get
rph
=
provider_config
.
get
(
'client_rate_limit_rph'
,
0
)
else
:
else
:
rpm
=
getattr
(
provider_config
,
'client_rate_limit_rpm'
,
0
)
get
=
lambda
k
,
d
=
0
:
getattr
(
provider_config
,
k
,
d
)
rph
=
getattr
(
provider_config
,
'client_rate_limit_rph'
,
0
)
out
=
[]
try
:
for
key
in
(
'client_rate_limit_rps'
,
'client_rate_limit_rpm'
,
'client_rate_limit_rph'
):
rpm
=
int
(
rpm
or
0
)
try
:
except
(
TypeError
,
ValueError
):
out
.
append
(
int
(
get
(
key
,
0
)
or
0
))
rpm
=
0
except
(
TypeError
,
ValueError
):
try
:
out
.
append
(
0
)
rph
=
int
(
rph
or
0
)
return
tuple
(
out
)
except
(
TypeError
,
ValueError
):
rph
=
0
return
rpm
,
rph
def
_enforce_provider_client_rate_limit
(
provider_id
:
str
,
provider_config
)
->
None
:
def
_enforce_provider_client_rate_limit
(
provider_id
:
str
,
provider_config
)
->
None
:
...
@@ -224,8 +220,8 @@ def _enforce_provider_client_rate_limit(provider_id: str, provider_config) -> No
...
@@ -224,8 +220,8 @@ def _enforce_provider_client_rate_limit(provider_id: str, provider_config) -> No
the provider's upstream quota. 0 = unlimited (the default), so providers
the provider's upstream quota. 0 = unlimited (the default), so providers
without a configured limit are unaffected.
without a configured limit are unaffected.
"""
"""
rpm
,
rph
=
_provider_rl_limits
(
provider_config
)
rp
s
,
rp
m
,
rph
=
_provider_rl_limits
(
provider_config
)
for
window
,
limit
,
label
in
((
60
,
rpm
,
"per-minute"
),
(
3600
,
rph
,
"per-hour"
)):
for
window
,
limit
,
label
in
((
1
,
rps
,
"per-second"
),
(
60
,
rpm
,
"per-minute"
),
(
3600
,
rph
,
"per-hour"
)):
allowed
,
retry_after
=
_provider_rl_check
(
f
"provider:{provider_id}:{window}"
,
window
,
limit
)
allowed
,
retry_after
=
_provider_rl_check
(
f
"provider:{provider_id}:{window}"
,
window
,
limit
)
if
not
allowed
:
if
not
allowed
:
logging
.
getLogger
(
__name__
)
.
warning
(
logging
.
getLogger
(
__name__
)
.
warning
(
...
...
pyproject.toml
View file @
11df0261
...
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
...
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
[project]
name
=
"aisbf"
name
=
"aisbf"
version
=
"0.99.9
5
"
version
=
"0.99.9
6
"
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme
=
"README.md"
readme
=
"README.md"
license
=
"GPL-3.0-or-later"
license
=
"GPL-3.0-or-later"
...
...
setup.py
View file @
11df0261
...
@@ -106,7 +106,7 @@ class InstallCommand(_install):
...
@@ -106,7 +106,7 @@ class InstallCommand(_install):
setup
(
setup
(
name
=
"aisbf"
,
name
=
"aisbf"
,
version
=
"0.99.9
5
"
,
version
=
"0.99.9
6
"
,
author
=
"AISBF Contributors"
,
author
=
"AISBF Contributors"
,
author_email
=
"stefy@nexlab.net"
,
author_email
=
"stefy@nexlab.net"
,
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
,
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
,
...
...
templates/dashboard/providers.html
View file @
11df0261
...
@@ -1810,6 +1810,10 @@ function renderProviderDetails(key) {
...
@@ -1810,6 +1810,10 @@ function renderProviderDetails(key) {
${
subPanel
(
`providers:
${
key
}
:ratelimit`
,
window
.
i18n
.
t
(
'providers.rate_limit_section'
)
||
'Rate Limiting'
,
`
${
subPanel
(
`providers:
${
key
}
:ratelimit`
,
window
.
i18n
.
t
(
'providers.rate_limit_section'
)
||
'Rate Limiting'
,
`
<div style="border-left: 3px solid #4a9eff; padding-left: 12px;">
<div style="border-left: 3px solid #4a9eff; padding-left: 12px;">
<div class="form-group">
<label>
${
window
.
i18n
.
t
(
'providers.client_rate_limit_rps'
)
||
'Max client requests per second'
}
(0 = unlimited)</label>
<input type="number" min="0" value="
${
provider
.
client_rate_limit_rps
||
0
}
" onchange="updateProvider('
${
key
}
', 'client_rate_limit_rps', this.value ? parseInt(this.value) : 0)">
</div>
<div class="form-group">
<div class="form-group">
<label>
${
window
.
i18n
.
t
(
'providers.client_rate_limit_rpm'
)
||
'Max client requests per minute'
}
(0 = unlimited)</label>
<label>
${
window
.
i18n
.
t
(
'providers.client_rate_limit_rpm'
)
||
'Max client requests per minute'
}
(0 = unlimited)</label>
<input type="number" min="0" value="
${
provider
.
client_rate_limit_rpm
||
0
}
" onchange="updateProvider('
${
key
}
', 'client_rate_limit_rpm', this.value ? parseInt(this.value) : 0)">
<input type="number" min="0" value="
${
provider
.
client_rate_limit_rpm
||
0
}
" onchange="updateProvider('
${
key
}
', 'client_rate_limit_rpm', this.value ? parseInt(this.value) : 0)">
...
...
templates/dashboard/user_providers.html
View file @
11df0261
...
@@ -1492,6 +1492,10 @@ function renderProviderDetails(key) {
...
@@ -1492,6 +1492,10 @@ function renderProviderDetails(key) {
<div style="background: var(--bg-panel); padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 3px solid #4a9eff;">
<div style="background: var(--bg-panel); padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 3px solid #4a9eff;">
<h4 style="margin: 0 0 15px 0; color: var(--color-link);">
${
window
.
i18n
.
t
(
'providers.rate_limit_section'
)
||
'Rate Limiting'
}
</h4>
<h4 style="margin: 0 0 15px 0; color: var(--color-link);">
${
window
.
i18n
.
t
(
'providers.rate_limit_section'
)
||
'Rate Limiting'
}
</h4>
<div class="form-group">
<label>
${
window
.
i18n
.
t
(
'providers.client_rate_limit_rps'
)
||
'Max client requests per second'
}
(0 = unlimited)</label>
<input type="number" min="0" value="
${
provider
.
client_rate_limit_rps
||
0
}
" onchange="updateProvider('
${
key
}
', 'client_rate_limit_rps', this.value ? parseInt(this.value) : 0)">
</div>
<div class="form-group">
<div class="form-group">
<label>
${
window
.
i18n
.
t
(
'providers.client_rate_limit_rpm'
)
||
'Max client requests per minute'
}
(0 = unlimited)</label>
<label>
${
window
.
i18n
.
t
(
'providers.client_rate_limit_rpm'
)
||
'Max client requests per minute'
}
(0 = unlimited)</label>
<input type="number" min="0" value="
${
provider
.
client_rate_limit_rpm
||
0
}
" onchange="updateProvider('
${
key
}
', 'client_rate_limit_rpm', this.value ? parseInt(this.value) : 0)">
<input type="number" min="0" value="
${
provider
.
client_rate_limit_rpm
||
0
}
" onchange="updateProvider('
${
key
}
', 'client_rate_limit_rpm', this.value ? parseInt(this.value) : 0)">
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment