Compare commits

..
3 Commits
Author SHA1 Message Date
Abiba 0ca3b65ad4 chore: add .gitignore for .bak and .backup files 2026-06-25 20:33:50 +00:00
Abiba 13eb8cb75b Merge branch 'main' of http://192.168.68.17:3000/SyslogSolution/syslog-harness
# Conflicts:
#	litellm_config.yaml
#	nginx/nginx.conf
2026-06-25 20:33:46 +00:00
Abiba 08680b0f9e fix: LiteLLM OIDC + Admin UI fixes - Authentik integration restored
- Added extra_hosts for auth.sysloggh.net to LiteLLM container
- Fixed DOCS_URL=/docs (was /litellm/docs - path mismatch)
- Added Authentik self-signed cert to CA bundle
- Added nginx auth proxy for token/userinfo endpoints (SSL verify off)
- Changed OIDC token/userinfo endpoints to use nginx internal proxy
- Admin UI serving correctly on :4001/ui/ and /litellm/ui/
- Swagger API docs working at /docs and /litellm/docs
- ReDoc API docs working at /redoc and /litellm/redoc
- OIDC login flow verified working end-to-end
2026-06-25 20:33:22 +00:00
5 changed files with 118 additions and 278 deletions
+3
View File
@@ -1,3 +1,6 @@
.git .git
__pycache__/ __pycache__/
*.pyc *.pyc
*.bak
*.backup*
.env
+6 -4
View File
@@ -57,12 +57,12 @@ services:
condition: service_healthy condition: service_healthy
litellm: litellm:
image: ghcr.io/berriai/litellm:main-stable image: docker.litellm.ai/berriai/litellm:1.90.0-rc.1
command: ["--config", "/app/config.yaml", "--port", "4000"] command: ["--config", "/app/config.yaml", "--port", "4000"]
container_name: harness-litellm container_name: harness-litellm
restart: unless-stopped restart: unless-stopped
ports: ports:
- "127.0.0.1:4001:4000" - "4001:4000"
volumes: volumes:
- ./litellm_config.yaml:/app/config.yaml - ./litellm_config.yaml:/app/config.yaml
- /opt/combined-ca-bundle.pem:/etc/ssl/certs/ca-certificates.crt:ro - /opt/combined-ca-bundle.pem:/etc/ssl/certs/ca-certificates.crt:ro
@@ -77,17 +77,19 @@ services:
- UI_PASSWORD=syslog-admin-2026 - UI_PASSWORD=syslog-admin-2026
- OPENAI_API_KEY=not-used - OPENAI_API_KEY=not-used
- PROXY_BASE_URL=https://litellm.sysloggh.net - PROXY_BASE_URL=https://litellm.sysloggh.net
- DOCS_URL=/docs
- ANTHROPIC_API_KEY=not-used - ANTHROPIC_API_KEY=not-used
- GENERIC_CLIENT_ID=FHd7bs9dP5gHad2Ki23iUL5kQvFa0GRaj3nlLnNU - GENERIC_CLIENT_ID=FHd7bs9dP5gHad2Ki23iUL5kQvFa0GRaj3nlLnNU
- GENERIC_CLIENT_SECRET=aDkXQx82duqpxc98xxqp0quzzUf1mawsnOTqj7sx1acaS7rWSt02N5ksBCi92n8ZilRavigoYME6fLakP20Ixc9H2pxnSZFiOqQLb7BPi8UtsvfxmzXklD0HJIdbKFxe - GENERIC_CLIENT_SECRET=aDkXQx82duqpxc98xxqp0quzzUf1mawsnOTqj7sx1acaS7rWSt02N5ksBCi92n8ZilRavigoYME6fLakP20Ixc9H2pxnSZFiOqQLb7BPi8UtsvfxmzXklD0HJIdbKFxe
- GENERIC_AUTHORIZATION_ENDPOINT=https://auth.sysloggh.net/application/o/authorize/ - GENERIC_AUTHORIZATION_ENDPOINT=https://auth.sysloggh.net/application/o/authorize/
- GENERIC_TOKEN_ENDPOINT=https://auth.sysloggh.net/application/o/token/ - GENERIC_TOKEN_ENDPOINT=http://harness-nginx/application/o/token/
- GENERIC_USERINFO_ENDPOINT=https://auth.sysloggh.net/application/o/userinfo/ - GENERIC_USERINFO_ENDPOINT=http://harness-nginx/application/o/userinfo/
- GENERIC_SCOPE=openid email profile - GENERIC_SCOPE=openid email profile
- GENERIC_ROLE_MAPPINGS_DEFAULT_ROLE=proxy_admin - GENERIC_ROLE_MAPPINGS_DEFAULT_ROLE=proxy_admin
- SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt - SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
extra_hosts: extra_hosts:
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
- "auth.sysloggh.net:192.168.68.11"
healthcheck: healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:4000/health/liveliness')"] test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:4000/health/liveliness')"]
interval: 15s interval: 15s
+65 -75
View File
@@ -1,99 +1,89 @@
# LiteLLM Gateway Configuration — Layer 1 of 2-Layer Architecture
# Deployed on CT 116 (192.168.68.116) alongside custom router on :9000
# Last updated: 2026-06-16
general_settings: general_settings:
master_key: os.environ/LITELLM_MASTER_KEY master_key: os.environ/LITELLM_MASTER_KEY
# database_url: using DATABASE_URL env var instead
store_model_in_db: true store_model_in_db: true
model_list:
# Content-based auto-routing (router picks GPU via 5-tier analysis)
- model_name: syslog-auto
litellm_params:
model: openai/syslog-auto
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
rpm: 600
# Individual GPU strict passthrough (exact GPU, no silent fallback)
- model_name: qwen3.6-35B-A3B
litellm_params:
model: openai/qwen3.6-35B-A3B
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
- model_name: qwen3.6-27B-code
litellm_params:
model: openai/qwen3.6-27B-code
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
- model_name: gemma-4-12b
litellm_params:
model: openai/gemma-4-12b
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
# Guardrails: Pre-call and post-call content moderation
guardrails: guardrails:
- guardrail_name: "input-moderation" - guardrail_name: input-moderation
litellm_params: litellm_params:
guardrail: openai_moderation guardrail: openai_moderation
mode: "pre_call" mode: pre_call
- guardrail_name: output-moderation
- guardrail_name: "output-moderation"
litellm_params: litellm_params:
guardrail: openai_moderation guardrail: openai_moderation
mode: "post_call" mode: post_call
- guardrail_name: harmful-content-filter
- guardrail_name: "harmful-content-filter"
litellm_params: litellm_params:
guardrail: litellm_content_filter
mode: "pre_call"
categories: categories:
- category: "harmful_self_harm" - action: BLOCK
category: harmful_self_harm
enabled: true enabled: true
action: "BLOCK" severity_threshold: medium
severity_threshold: "medium" - action: BLOCK
- category: "harmful_violence" category: harmful_violence
enabled: true enabled: true
action: "BLOCK" severity_threshold: medium
severity_threshold: "medium" - action: BLOCK
- category: "harmful_illegal_weapons" category: harmful_illegal_weapons
enabled: true enabled: true
action: "BLOCK" severity_threshold: medium
severity_threshold: "medium" guardrail: litellm_content_filter
mode: pre_call
litellm_settings: litellm_settings:
num_retries: 0 # Disabled — our router handles retry logic failure_callback:
request_timeout: 600 # Match 10-min llama-server timeout - prometheus
set_verbose: true
failure_callback: ["prometheus"] # Export metrics to Prometheus
sso_callback: "/sso/callback"
model_cost: model_cost:
syslog-auto: gemma-4-12b:
input_cost_per_token: 0.0
output_cost_per_token: 0.0
qwen3.6-35B-A3B:
input_cost_per_token: 0.0 input_cost_per_token: 0.0
output_cost_per_token: 0.0 output_cost_per_token: 0.0
qwen3.6-27B-code: qwen3.6-27B-code:
input_cost_per_token: 0.0 input_cost_per_token: 0.0
output_cost_per_token: 0.0 output_cost_per_token: 0.0
gemma-4-12b: qwen3.6-35B-A3B:
input_cost_per_token: 0.0 input_cost_per_token: 0.0
output_cost_per_token: 0.0 output_cost_per_token: 0.0
syslog-auto:
input_cost_per_token: 0.0
output_cost_per_token: 0.0
num_retries: 0
request_timeout: 600
set_verbose: true
sso_callback: /sso/callback
model_list:
- litellm_params:
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
model: openai/syslog-auto
rpm: 600
model_name: syslog-auto
- litellm_params:
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
model: openai/qwen3.6-35B-A3B
model_name: qwen3.6-35B-A3B
- litellm_params:
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
model: openai/qwen3.6-27B-code
model_name: qwen3.6-27B-code
- litellm_params:
api_base: http://router:9000/v1
api_key: os.environ/ROUTER_API_KEY
model: openai/gemma-4-12b
model_name: gemma-4-12b
router_settings: router_settings:
routing_strategy: "usage-based-routing" # For external models only allowed_fails: 100
enable_loadbalancing_on_proxy: false # Disable LiteLLM internal LB enable_loadbalancing_on_proxy: false
allowed_fails: 100 # Router returns 503 on saturated GPUs
# Fallback chains: LiteLLM retries down the chain when router returns saturated.
# This gives accurate per-model metrics because router no longer silently reroutes.
# The router's circuit breaker prevents cascading failures to dead GPUs.
fallbacks: fallbacks:
- syslog-auto: ["qwen3.6-35B-A3B", "qwen3.6-27B-code", "gemma-4-12b"] - syslog-auto:
- qwen3.6-35B-A3B: ["qwen3.6-27B-code", "gemma-4-12b"] - qwen3.6-35B-A3B
- qwen3.6-27B-code: ["qwen3.6-35B-A3B", "gemma-4-12b"] - qwen3.6-27B-code
- gemma-4-12b: ["qwen3.6-27B-code", "qwen3.6-35B-A3B"] - gemma-4-12b
- qwen3.6-35B-A3B:
- qwen3.6-27B-code
- gemma-4-12b
- qwen3.6-27B-code:
- qwen3.6-35B-A3B
- gemma-4-12b
- gemma-4-12b:
- qwen3.6-27B-code
- qwen3.6-35B-A3B
routing_strategy: usage-based-routing
+33 -188
View File
@@ -25,19 +25,10 @@ http {
map $host $litellm_backend_url { map $host $litellm_backend_url {
default http://harness-litellm:4000; default http://harness-litellm:4000;
} }
# Detect Cloudflare Tunnel requests (cloudflared always sets CF-Connecting-IP).
# Direct LAN browser access to :4000 has no such header -> redirect to canonical https,
# preventing the cross-origin localStorage footgun that traps the UI at the login page.
map $http_cf_connecting_ip $is_cloudflared {
default 1; # any non-empty value = request came through Cloudflare
"" 0; # empty = direct access
}
# ════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════
# Server :80 — existing harness entrypoint # Server :80 — harness entrypoint
# dashboard (/), router API (/v1/, /admin/, /stream, /api/, /metrics), # dashboard (/), router API (/v1/, /admin/, /stream, /api/, /metrics),
# router fallback, LiteLLM UI via /litellm/ prefix, health # router fallback, health
# ════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════
server { server {
listen 80; listen 80;
@@ -99,71 +90,7 @@ http {
proxy_set_header Host $host; proxy_set_header Host $host;
} }
# LiteLLM gateway access (agents with new virtual keys)
location /litellm/v1/ {
proxy_pass $litellm_backend_url/v1/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
proxy_connect_timeout 10s;
proxy_read_timeout 600s;
proxy_buffering off;
}
# LiteLLM UI static assets
location /litellm-asset-prefix/ {
proxy_pass $litellm_backend_url/litellm-asset-prefix/;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
# LiteLLM Admin UI at /ui/ (public access via Traefik → port 80)
location /ui/ {
proxy_pass $litellm_backend_url/ui/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
proxy_buffering off;
}
# SSO callback
location /sso/ {
proxy_pass $litellm_backend_url/sso/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_read_timeout 600s;
}
# LiteLLM UI static assets on port 80
location /litellm-asset-prefix/ {
proxy_pass $litellm_backend_url/litellm-asset-prefix/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# LiteLLM Admin UI via /litellm/ prefix (LAN/internal access on :80)
location /litellm/ {
proxy_pass $litellm_backend_url/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
proxy_buffering off;
proxy_redirect http://$host/ /litellm/;
proxy_redirect https://$host/ /litellm/;
}
location /dashboard/ { location /dashboard/ {
proxy_pass $dashboard_ui_url/; proxy_pass $dashboard_ui_url/;
@@ -171,31 +98,46 @@ http {
proxy_set_header Host $host; proxy_set_header Host $host;
} }
# Dedicated /openapi.json block — must come before catch-all / # LiteLLM redirect target /litellm (no trailing slash) -> add slash back
# LiteLLM serves valid openapi: 3.1.0 JSON at this path internally, location = /litellm {
# but the catch-all location / strips the URI path. This block return 301 /litellm/;
# preserves the full path so the spec JSON is returned instead of }
# the Swagger UI SPA HTML.
location /openapi.json { # LiteLLM static assets (Next.js chunks, CSS, fonts)
proxy_pass $litellm_backend_url/openapi.json; location /litellm-asset-prefix/ {
proxy_pass $litellm_backend_url;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
proxy_connect_timeout 10s; proxy_connect_timeout 10s;
proxy_read_timeout 600s; proxy_read_timeout 60s;
}
# LiteLLM admin UI and API proxy — strip /litellm prefix so /litellm/ui/ → /ui/
location /litellm/ {
rewrite ^/litellm(/.*)$ $1 break;
proxy_pass $litellm_backend_url;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off; proxy_buffering off;
} }
location / { # Auth proxy to Authentik — accepts HTTP from LiteLLM, proxies HTTPS to .11 with SSL verify off
proxy_pass $litellm_backend_url/; location /application/o/ {
proxy_http_version 1.1; proxy_pass https://192.168.68.11;
proxy_set_header Host $host; proxy_ssl_verify off;
proxy_set_header Host auth.sysloggh.net;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
proxy_connect_timeout 10s; proxy_connect_timeout 10s;
proxy_read_timeout 600s; proxy_read_timeout 30s;
proxy_buffering off; }
# All other requests → 404
location / {
return 404;
} }
location /metrics/ { location /metrics/ {
@@ -229,101 +171,4 @@ http {
} }
} }
# ════════════════════════════════════════════════════════════════
# Server :4000 — external LiteLLM entrypoint (cloudflared target)
# Fixes the /ui redirect: forces correct Host + scheme so LiteLLM
# builds external URLs instead of leaking 192.168.68.116:4000.
# LiteLLM itself is now bound to 127.0.0.1 only (see compose).
# ════════════════════════════════════════════════════════════════
server {
listen 4000;
# Canonical external identity — overrides whatever Host cloudflared sends
proxy_set_header Host litellm.sysloggh.net;
proxy_set_header X-Forwarded-Host litellm.sysloggh.net;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
# /ui → /ui/ — absolute redirect (cloudflared rewrites Host to origin IP,
# so a relative return would be absolutized to http://192.168.68.116:4000/).
location = /ui { return 301 https://litellm.sysloggh.net/ui/; }
# LiteLLM Admin UI + WebSocket
location /ui/ {
proxy_pass $litellm_backend_url/ui/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
proxy_buffering off;
proxy_redirect $litellm_backend_url/ https://litellm.sysloggh.net/;
proxy_redirect http://litellm.sysloggh.net:4000/ https://litellm.sysloggh.net/;
}
# UI static assets
location /litellm-asset-prefix/ {
proxy_pass $litellm_backend_url/litellm-asset-prefix/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# SSO callback
location /sso/ {
proxy_pass $litellm_backend_url/sso/;
proxy_http_version 1.1;
proxy_read_timeout 600s;
}
# API
location /v1/ {
proxy_pass $litellm_backend_url/v1/;
proxy_http_version 1.1;
proxy_set_header Authorization $http_authorization;
proxy_read_timeout 600s;
proxy_buffering off;
}
# Key management + admin API
location /key/ {
proxy_pass $litellm_backend_url/key/;
proxy_http_version 1.1;
proxy_set_header Authorization $http_authorization;
}
location /user/ {
proxy_pass $litellm_backend_url/user/;
proxy_http_version 1.1;
proxy_set_header Authorization $http_authorization;
}
location /model/ {
proxy_pass $litellm_backend_url/model/;
proxy_http_version 1.1;
proxy_set_header Authorization $http_authorization;
}
location /team/ {
proxy_pass $litellm_backend_url/team/;
proxy_http_version 1.1;
proxy_set_header Authorization $http_authorization;
}
# Health
location /health {
proxy_pass $litellm_backend_url/health;
proxy_http_version 1.1;
}
# Root + everything else → LiteLLM (UI index, /configs, /spend, etc.)
location / {
proxy_pass $litellm_backend_url/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
proxy_buffering off;
proxy_redirect http://192.168.68.116:4000/ https://litellm.sysloggh.net/;
proxy_redirect https://192.168.68.116:4000/ https://litellm.sysloggh.net/;
}
}
} }
+2 -2
View File
@@ -70,7 +70,7 @@ GPU_LABELS = {
} }
GPU_MAX_CONCURRENT = { GPU_MAX_CONCURRENT = {
"qwen3.6-35B-A3B": 1, # 2 slots (cross-agent spread prevents overheating) "qwen3.6-35B-A3B": 2, # 2 slots (cross-agent spread prevents overheating)
"qwen3.6-27B-code": 2, # 2 slots (128K context frees VRAM) "qwen3.6-27B-code": 2, # 2 slots (128K context frees VRAM)
"gemma-4-12b": 2, # 2 slots (7.1GB VRAM) "gemma-4-12b": 2, # 2 slots (7.1GB VRAM)
} }
@@ -626,7 +626,7 @@ def chat():
except Exception: pass except Exception: pass
start = time.time() start = time.time()
resp = requests.post(url+"/chat/completions", json=rd, resp = requests.post(url+"/chat/completions", json=rd,
headers={"Content-Type":"application/json","Authorization":"Bearer not-needed"}, timeout=300, stream=is_stream) headers={"Content-Type":"application/json","Authorization":"Bearer not-needed"}, timeout=900, stream=is_stream)
lat = int((time.time()-start)*1000) lat = int((time.time()-start)*1000)
gpu_release_slot(model) gpu_release_slot(model)