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
8a71da25
Commit
8a71da25
authored
Apr 24, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bump version to 0.99.60
parent
09c0a774
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
82 additions
and
4 deletions
+82
-4
__init__.py
aisbf/__init__.py
+1
-1
main.py
main.py
+74
-1
pyproject.toml
pyproject.toml
+1
-1
setup.py
setup.py
+1
-1
settings.html
templates/dashboard/settings.html
+5
-0
No files found.
aisbf/__init__.py
View file @
8a71da25
...
...
@@ -54,7 +54,7 @@ from .auth.qwen import QwenOAuth2
from
.handlers
import
RequestHandler
,
RotationHandler
,
AutoselectHandler
from
.utils
import
count_messages_tokens
,
split_messages_into_chunks
,
get_max_request_tokens_for_model
__version__
=
"0.99.
59
"
__version__
=
"0.99.
60
"
__all__
=
[
# Config
"config"
,
...
...
main.py
View file @
8a71da25
...
...
@@ -6011,6 +6011,31 @@ async def dashboard_settings_save(
dashboard_username
:
str
=
Form
(
...
),
condensation_model_id
:
str
=
Form
(
...
),
autoselect_model_id
:
str
=
Form
(
...
),
nsfw_classifier
:
str
=
Form
(
"michelleli99/NSFW_text_classifier"
),
privacy_classifier
:
str
=
Form
(
"iiiorg/piiranha-v1-detect-personal-information"
),
semantic_vectorization
:
str
=
Form
(
"sentence-transformers/all-MiniLM-L6-v2"
),
classify_nsfw
:
bool
=
Form
(
False
),
classify_privacy
:
bool
=
Form
(
False
),
classify_semantic
:
bool
=
Form
(
False
),
batching_enabled
:
bool
=
Form
(
False
),
batching_window_ms
:
int
=
Form
(
100
),
batching_max_batch_size
:
int
=
Form
(
8
),
batching_openai_enabled
:
bool
=
Form
(
False
),
batching_openai_max_batch_size
:
int
=
Form
(
10
),
batching_anthropic_enabled
:
bool
=
Form
(
False
),
batching_anthropic_max_batch_size
:
int
=
Form
(
5
),
adaptive_rate_limiting_enabled
:
bool
=
Form
(
False
),
adaptive_initial_rate_limit
:
float
=
Form
(
0
),
adaptive_learning_rate
:
float
=
Form
(
0.1
),
adaptive_headroom_percent
:
float
=
Form
(
10
),
adaptive_recovery_rate
:
float
=
Form
(
0.05
),
adaptive_max_rate_limit
:
float
=
Form
(
60
),
adaptive_min_rate_limit
:
float
=
Form
(
0.1
),
adaptive_backoff_base
:
float
=
Form
(
2
),
adaptive_jitter_factor
:
float
=
Form
(
0.25
),
adaptive_history_window
:
int
=
Form
(
3600
),
adaptive_consecutive_successes
:
int
=
Form
(
10
),
active_tab
:
str
=
Form
(
"server"
),
database_type
:
str
=
Form
(
"sqlite"
),
sqlite_path
:
str
=
Form
(
"~/.aisbf/aisbf.db"
),
mysql_host
:
str
=
Form
(
"localhost"
),
...
...
@@ -6252,6 +6277,50 @@ async def dashboard_settings_save(
aisbf_config
[
'dashboard'
][
'password'
]
=
_db_hash_password
(
new_admin_password
)
request
.
session
.
pop
(
'must_change_password'
,
None
)
# Update classification config
aisbf_config
[
'classify_nsfw'
]
=
classify_nsfw
aisbf_config
[
'classify_privacy'
]
=
classify_privacy
aisbf_config
[
'classify_semantic'
]
=
classify_semantic
# Update internal model classifiers
if
'internal_model'
not
in
aisbf_config
:
aisbf_config
[
'internal_model'
]
=
{}
aisbf_config
[
'internal_model'
][
'nsfw_classifier'
]
=
nsfw_classifier
aisbf_config
[
'internal_model'
][
'privacy_classifier'
]
=
privacy_classifier
aisbf_config
[
'internal_model'
][
'semantic_vectorization'
]
=
semantic_vectorization
# Update batching config
if
'batching'
not
in
aisbf_config
:
aisbf_config
[
'batching'
]
=
{}
aisbf_config
[
'batching'
][
'enabled'
]
=
batching_enabled
aisbf_config
[
'batching'
][
'window_ms'
]
=
batching_window_ms
aisbf_config
[
'batching'
][
'max_batch_size'
]
=
batching_max_batch_size
if
'provider_settings'
not
in
aisbf_config
[
'batching'
]:
aisbf_config
[
'batching'
][
'provider_settings'
]
=
{}
if
'openai'
not
in
aisbf_config
[
'batching'
][
'provider_settings'
]:
aisbf_config
[
'batching'
][
'provider_settings'
][
'openai'
]
=
{}
aisbf_config
[
'batching'
][
'provider_settings'
][
'openai'
][
'enabled'
]
=
batching_openai_enabled
aisbf_config
[
'batching'
][
'provider_settings'
][
'openai'
][
'max_batch_size'
]
=
batching_openai_max_batch_size
if
'anthropic'
not
in
aisbf_config
[
'batching'
][
'provider_settings'
]:
aisbf_config
[
'batching'
][
'provider_settings'
][
'anthropic'
]
=
{}
aisbf_config
[
'batching'
][
'provider_settings'
][
'anthropic'
][
'enabled'
]
=
batching_anthropic_enabled
aisbf_config
[
'batching'
][
'provider_settings'
][
'anthropic'
][
'max_batch_size'
]
=
batching_anthropic_max_batch_size
# Update adaptive rate limiting config
if
'adaptive_rate_limiting'
not
in
aisbf_config
:
aisbf_config
[
'adaptive_rate_limiting'
]
=
{}
aisbf_config
[
'adaptive_rate_limiting'
][
'enabled'
]
=
adaptive_rate_limiting_enabled
aisbf_config
[
'adaptive_rate_limiting'
][
'initial_rate_limit'
]
=
adaptive_initial_rate_limit
aisbf_config
[
'adaptive_rate_limiting'
][
'learning_rate'
]
=
adaptive_learning_rate
aisbf_config
[
'adaptive_rate_limiting'
][
'headroom_percent'
]
=
adaptive_headroom_percent
aisbf_config
[
'adaptive_rate_limiting'
][
'recovery_rate'
]
=
adaptive_recovery_rate
aisbf_config
[
'adaptive_rate_limiting'
][
'max_rate_limit'
]
=
adaptive_max_rate_limit
aisbf_config
[
'adaptive_rate_limiting'
][
'min_rate_limit'
]
=
adaptive_min_rate_limit
aisbf_config
[
'adaptive_rate_limiting'
][
'backoff_base'
]
=
adaptive_backoff_base
aisbf_config
[
'adaptive_rate_limiting'
][
'jitter_factor'
]
=
adaptive_jitter_factor
aisbf_config
[
'adaptive_rate_limiting'
][
'history_window'
]
=
adaptive_history_window
aisbf_config
[
'adaptive_rate_limiting'
][
'consecutive_successes_for_recovery'
]
=
adaptive_consecutive_successes
# Save config
config_path
=
Path
.
home
()
/
'.aisbf'
/
'aisbf.json'
config_path
.
parent
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
...
...
@@ -6262,6 +6331,9 @@ async def dashboard_settings_save(
if
server_config
is
not
None
:
server_config
[
'dashboard_config'
]
=
aisbf_config
.
get
(
'dashboard'
,
{})
# Hot-reload global config so changes take effect without restart
_reload_global_config
()
return
templates
.
TemplateResponse
(
request
=
request
,
name
=
"dashboard/settings.html"
,
...
...
@@ -6270,7 +6342,8 @@ async def dashboard_settings_save(
"session"
:
request
.
session
,
"config"
:
aisbf_config
,
"os"
:
os
,
"success"
:
"Settings saved successfully! Restart server for changes to take effect."
"active_tab"
:
active_tab
,
"success"
:
"Settings saved and reloaded successfully."
}
)
...
...
pyproject.toml
View file @
8a71da25
...
...
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name
=
"aisbf"
version
=
"0.99.
59
"
version
=
"0.99.
60
"
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme
=
"README.md"
license
=
"GPL-3.0-or-later"
...
...
setup.py
View file @
8a71da25
...
...
@@ -49,7 +49,7 @@ class InstallCommand(_install):
setup
(
name
=
"aisbf"
,
version
=
"0.99.
59
"
,
version
=
"0.99.
60
"
,
author
=
"AISBF Contributors"
,
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"
,
...
...
templates/dashboard/settings.html
View file @
8a71da25
...
...
@@ -69,6 +69,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div>
<form
method=
"POST"
>
<input
type=
"hidden"
name=
"active_tab"
id=
"active_tab_input"
value=
"{{ active_tab if active_tab else 'server' }}"
>
<div
class=
"settings-section active"
id=
"tab-server"
>
<div
class=
"section-title"
><i
class=
"fas fa-server"
></i>
Server Configuration
</div>
...
...
@@ -981,6 +982,7 @@ function switchTab(name) {
document
.
querySelectorAll
(
'.settings-section'
).
forEach
(
s
=>
s
.
classList
.
remove
(
'active'
));
document
.
querySelector
(
`[onclick="switchTab('
${
name
}
')"]`
).
classList
.
add
(
'active'
);
document
.
getElementById
(
'tab-'
+
name
).
classList
.
add
(
'active'
);
document
.
getElementById
(
'active_tab_input'
).
value
=
name
;
}
// Validate admin password match before form submit
...
...
@@ -1220,6 +1222,9 @@ document.addEventListener('DOMContentLoaded', function() {
setInterval
(
refreshCacheStats
,
10000
);
{
%
if
warning
==
'default_password'
%
}
switchTab
(
'admin'
);
{
%
else
%
}
const
savedTab
=
document
.
getElementById
(
'active_tab_input'
).
value
;
if
(
savedTab
&&
savedTab
!==
'server'
)
switchTab
(
savedTab
);
{
%
endif
%
}
});
...
...
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