Commit 0c895e05 authored by Your Name's avatar Your Name

feat: Add intelligent 429 rate limit handling and improve configuration

- Implement intelligent 429 rate limit parsing in providers
  - Parse Retry-After and X-RateLimit-Reset headers
  - Extract wait time from response body fields
  - Parse error messages for time patterns
  - Automatically disable providers for exact duration specified

- Update aisbf.sh to read port from config file dynamically
  - Add get_port() function to parse aisbf.json
  - Remove hardcoded port 17765
  - Show port number when starting server

- Implement SHA256 password hashing for dashboard
  - Store password as hash in aisbf.json
  - Hash passwords during login validation
  - Hash passwords when updating settings

- Add templates to package distribution
  - Update setup.py to include dashboard templates
  - Update MANIFEST.in (already included templates)
  - Add aisbf.json to package

- Update documentation
  - Add OpenAI-compatible v1 endpoints to README
  - Add dashboard endpoints documentation
  - Create comprehensive API_EXAMPLES.md with examples in cURL, Python, and JavaScript

- Add GPL license headers to all template files
  - HTML templates in templates/ directory
  - JavaScript code in templates/base.html

All changes maintain backward compatibility while adding new features.
parent 3f6647d2
This diff is collapsed.
......@@ -7,4 +7,7 @@ include aisbf.sh
include cli.py
recursive-include config *.json
recursive-include config *.md
recursive-include aisbf *.py
\ No newline at end of file
recursive-include aisbf *.py
recursive-include templates *.html
recursive-include templates *.css
recursive-include templates *.js
\ No newline at end of file
......@@ -109,9 +109,26 @@ See `config/providers.json` and `config/rotations.json` for configuration exampl
### General Endpoints
- `GET /` - Server status and provider list (includes providers, rotations, and autoselect)
### Provider Endpoints
### OpenAI-Compatible v1 Endpoints (Recommended)
These endpoints follow the standard OpenAI API format with `provider/model` notation:
- `POST /api/v1/chat/completions` - Chat completions (model format: `provider/model-name`)
- Example: `{"model": "openai/gpt-4", "messages": [...]}`
- Supports providers, rotations, and autoselect
- `GET /api/v1/models` - List all available models from all providers
- `POST /api/v1/audio/transcriptions` - Audio transcription (model format: `provider/model-name`)
- `POST /api/v1/audio/speech` - Text-to-speech (model format: `provider/model-name`)
- `POST /api/v1/images/generations` - Image generation (model format: `provider/model-name`)
- `POST /api/v1/embeddings` - Text embeddings (model format: `provider/model-name`)
- `GET /api/proxy/{content_id}` - Proxy generated content (images, audio, etc.)
### Provider Endpoints (Legacy)
- `POST /api/{provider_id}/chat/completions` - Chat completions for a specific provider
- `GET /api/{provider_id}/models` - List available models for a specific provider
- `POST /api/{provider_id}/audio/transcriptions` - Audio transcription
- `POST /api/{provider_id}/audio/speech` - Text-to-speech
- `POST /api/{provider_id}/images/generations` - Image generation
- `POST /api/{provider_id}/embeddings` - Text embeddings
### Rotation Endpoints
- `GET /api/rotations` - List all available rotation configurations
......@@ -131,6 +148,17 @@ See `config/providers.json` and `config/rotations.json` for configuration exampl
- Supports both streaming and non-streaming responses
- `GET /api/autoselect/models` - List all models across all autoselect configurations
### Dashboard Endpoints
- `GET /dashboard` - Web-based configuration dashboard
- `GET /dashboard/login` - Dashboard login page
- `POST /dashboard/login` - Handle dashboard authentication
- `GET /dashboard/logout` - Logout from dashboard
- `GET /dashboard/providers` - Edit providers configuration
- `GET /dashboard/rotations` - Edit rotations configuration
- `GET /dashboard/autoselect` - Edit autoselect configuration
- `GET /dashboard/settings` - Edit server settings
- `POST /dashboard/restart` - Restart the server
## Error Handling
- Rate limiting for failed requests
- Automatic retry with provider rotation
......
......@@ -41,6 +41,37 @@ fi
# Create log directory if it doesn't exist
mkdir -p "$LOG_DIR"
# Function to get port from config file
get_port() {
local CONFIG_FILE="$SHARE_DIR/config/aisbf.json"
local DEFAULT_PORT=17765
# Check if config file exists
if [ ! -f "$CONFIG_FILE" ]; then
echo "$DEFAULT_PORT"
return
fi
# Try to read port from config using Python
local PORT=$(python3 -c "
import json
import sys
try:
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
print(config.get('port', $DEFAULT_PORT))
except:
print($DEFAULT_PORT)
" 2>/dev/null)
# Validate port is a number
if [[ "$PORT" =~ ^[0-9]+$ ]]; then
echo "$PORT"
else
echo "$DEFAULT_PORT"
fi
}
# Function to create venv if it doesn't exist
ensure_venv() {
if [ ! -d "$VENV_DIR" ]; then
......@@ -79,15 +110,20 @@ start_server() {
# Update venv packages silently
update_venv
# Get port from config
PORT=$(get_port)
# Activate the virtual environment
source $VENV_DIR/bin/activate
# Change to share directory where main.py is located
cd $SHARE_DIR
echo "Starting AISBF on port $PORT..."
# Start the proxy server with logging
# Redirect stderr to suppress BrokenPipeError during shutdown
uvicorn main:app --host 127.0.0.1 --port 17765 2>&1 | while IFS= read -r line; do
uvicorn main:app --host 127.0.0.1 --port $PORT 2>&1 | while IFS= read -r line; do
# Filter out BrokenPipeError logging errors
if [[ "$line" != *"--- Logging error ---"* ]] && [[ "$line" != *"BrokenPipeError"* ]] && [[ "$line" != *"Call stack:"* ]] && [[ "$line" != *"File "*"/python"* ]] && [[ "$line" != *"Message:"* ]] && [[ "$line" != *"Arguments:"* ]]; then
echo "$line" | tee -a "$LOG_DIR/aisbf_stdout.log"
......@@ -115,9 +151,14 @@ start_daemon() {
# Update venv packages silently
update_venv
# Get port from config
PORT=$(get_port)
echo "Starting AISBF on port $PORT in background..."
# Start in background with nohup and logging
# Filter out BrokenPipeError logging errors
nohup bash -c "source $VENV_DIR/bin/activate && cd $SHARE_DIR && uvicorn main:app --host 127.0.0.1 --port 17765 2>&1 | grep -v '--- Logging error ---' | grep -v 'BrokenPipeError' | grep -v 'Call stack:' | grep -v 'File .*python' | grep -v 'Message:' | grep -v 'Arguments:'" >> "$LOG_DIR/aisbf_stdout.log" 2>&1 &
nohup bash -c "source $VENV_DIR/bin/activate && cd $SHARE_DIR && uvicorn main:app --host 127.0.0.1 --port $PORT 2>&1 | grep -v '--- Logging error ---' | grep -v 'BrokenPipeError' | grep -v 'Call stack:' | grep -v 'File .*python' | grep -v 'Message:' | grep -v 'Arguments:'" >> "$LOG_DIR/aisbf_stdout.log" 2>&1 &
PID=$!
echo $PID > "$PIDFILE"
echo "AISBF started in background (PID: $PID)"
......
This diff is collapsed.
This diff is collapsed.
......@@ -16,7 +16,7 @@
"dashboard": {
"enabled": true,
"username": "admin",
"password": "admin"
"password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"
},
"internal_model": {
"model_id": "huihui-ai/Qwen2.5-0.5B-Instruct-abliterated-v3"
......
......@@ -24,7 +24,8 @@
"rate_limit_TPD": 1000000,
"context_size": 1000000,
"condense_context": 80,
"condense_method": ["hierarchical", "semantic"]
"condense_method": ["hierarchical", "semantic"],
"capabilities": ["t2t", "vision", "function_calling"]
},
{
"name": "gemini-1.5-pro",
......@@ -35,7 +36,8 @@
"rate_limit_TPD": 1000000,
"context_size": 2000000,
"condense_context": 85,
"condense_method": "conversational"
"condense_method": "conversational",
"capabilities": ["t2t", "vision", "function_calling"]
}
]
},
......
This diff is collapsed.
......@@ -86,6 +86,7 @@ setup(
'config/rotations.json',
'config/autoselect.json',
'config/autoselect.md',
'config/aisbf.json',
]),
# Install aisbf package to share directory for venv installation
('share/aisbf/aisbf', [
......@@ -98,6 +99,16 @@ setup(
'aisbf/utils.py',
'aisbf/database.py',
]),
# Install dashboard templates
('share/aisbf/templates', [
'templates/base.html',
]),
('share/aisbf/templates/dashboard', [
'templates/dashboard/login.html',
'templates/dashboard/index.html',
'templates/dashboard/edit_config.html',
'templates/dashboard/settings.html',
]),
],
entry_points={
"console_scripts": [
......
<!--
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE html>
<html lang="en">
<head>
......@@ -9,7 +25,8 @@
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
.header { background: #2c3e50; color: white; padding: 20px 0; margin-bottom: 30px; }
.header h1 { font-size: 24px; font-weight: 600; }
.header h1 { font-size: 24px; font-weight: 600; display: inline-block; }
.header-actions { float: right; }
.nav { background: white; padding: 15px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.nav a { color: #2c3e50; text-decoration: none; margin-right: 20px; padding: 8px 12px; border-radius: 4px; }
.nav a:hover { background: #ecf0f1; }
......@@ -19,28 +36,67 @@
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; color: #2c3e50; }
.form-group input, .form-group textarea, .form-group select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
.form-group textarea { min-height: 200px; font-family: 'Courier New', monospace; }
.btn { padding: 10px 20px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; }
.btn { padding: 10px 20px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; text-decoration: none; display: inline-block; margin-left: 10px; }
.btn:hover { background: #2980b9; }
.btn-secondary { background: #95a5a6; }
.btn-secondary:hover { background: #7f8c8d; }
.btn-danger { background: #e74c3c; }
.btn-danger:hover { background: #c0392b; }
.btn-warning { background: #f39c12; }
.btn-warning:hover { background: #e67e22; }
.alert { padding: 15px; border-radius: 4px; margin-bottom: 20px; }
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.logout { float: right; }
.alert-info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #f8f9fa; font-weight: 600; }
.code { background: #f8f9fa; padding: 15px; border-radius: 4px; font-family: 'Courier New', monospace; font-size: 13px; overflow-x: auto; }
</style>
<script>
/*
* Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
function restartServer() {
if (confirm('Are you sure you want to restart the server? This will disconnect all active connections.')) {
fetch('/dashboard/restart', {
method: 'POST',
headers: {'Content-Type': 'application/json'}
})
.then(response => response.json())
.then(data => {
alert(data.message + ' The page will reload in 5 seconds.');
setTimeout(() => window.location.reload(), 5000);
})
.catch(error => {
alert('Error restarting server: ' + error);
});
}
}
</script>
</head>
<body>
<div class="header">
<div class="container">
<h1>AISBF Dashboard</h1>
{% if session.logged_in %}
<a href="/dashboard/logout" class="btn btn-secondary logout">Logout</a>
<div class="header-actions">
<button onclick="restartServer()" class="btn btn-warning">Restart Server</button>
<a href="/dashboard/logout" class="btn btn-secondary">Logout</a>
</div>
{% endif %}
</div>
</div>
......
<!--
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}{{ title }} - AISBF Dashboard{% endblock %}
......
<!--
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Overview - AISBF Dashboard{% endblock %}
......
<!--
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Login - AISBF Dashboard{% endblock %}
......
<!--
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Settings - AISBF Dashboard{% endblock %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment