Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions litellm/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,26 @@ def strptime(cls, date_str: str, fmt: str) -> original_datetime:
datetime.datetime = RobustDatetime
sys.stdout.flush()

# Register both RobustDatetime AND the original datetime with Prisma's
# singledispatch serializer. When entrypoint.py replaces datetime.datetime
# with RobustDatetime before Prisma loads, Prisma's own
# @serializer.register(datetime.datetime) ends up registering RobustDatetime.
# But database drivers (psycopg2) return the *original* C-level datetime
# instances, which no longer match. We must register both classes.
try:
from prisma.builder import serializer
def _serialize_dt(dt):
"""Serialize datetime to ISO8601 with timezone (UTC if naive)."""
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.isoformat()
serializer.register(original_datetime, _serialize_dt)
serializer.register(RobustDatetime, _serialize_dt)
print("🩹 Registered original_datetime + RobustDatetime with Prisma serializer")
except Exception:
pass
sys.stdout.flush()

# Start LiteLLM Proxy
import litellm
from litellm.proxy.proxy_cli import run_server
Expand Down
36 changes: 18 additions & 18 deletions pod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ spec:
- 'no'
- --loglevel
- warning
image: valkey/valkey:9.1.0-alpine
image: docker.io/valkey/valkey:9.1.0-alpine
livenessProbe:
tcpSocket:
port: 6379
Expand All @@ -34,17 +34,17 @@ spec:
- /app/entrypoint.py
env:
- name: DATABASE_URL
value: postgresql://postgres:***@127.0.0.1:5432/postgres
value: postgresql://postgres:POSTGRES_PASSWORD_ENCODED_PLACEHOLDER@127.0.0.1:5432/postgres
- name: STORE_MODEL_IN_DB
value: 'True'
- name: LITELLM_LOG
value: INFO
- name: LANGFUSE_HOST
value: http://127.0.0.1:3001
- name: LITELLM_MASTER_KEY
value: sk-lit...33bf
value: LITELLM_MASTER_KEY_PLACEHOLDER
- name: OLLAMA_API_KEY
value: 3cd542f8fca14fd08bedfd4f2ab36f9f.6G7Iukbvu1Keyi9x8eKckNEO
value: OLLAMA_API_KEY_PLACEHOLDER
image: ghcr.io/berriai/litellm:v1.90.1
livenessProbe:
exec:
Expand Down Expand Up @@ -89,19 +89,19 @@ spec:
- name: LITELLM_CONFIG_PATH
value: /config/litellm_dir/config.yaml
- name: DATABASE_URL
value: postgresql://postgres:***@127.0.0.1:5432/postgres
value: postgresql://postgres:POSTGRES_PASSWORD_ENCODED_PLACEHOLDER@127.0.0.1:5432/postgres
- name: DBUS_SESSION_BUS_ADDRESS
value: unix:path=/run/user/1000/bus
- name: LITELLM_MASTER_KEY
value: sk-lit...33bf
value: LITELLM_MASTER_KEY_PLACEHOLDER
- name: LANGFUSE_PUBLIC_KEY
value: pk-lf-200bbf48-1d23-447d-a79a-8614106497e6
value: LANGFUSE_PUBLIC_KEY_PLACEHOLDER
- name: LANGFUSE_SECRET_KEY
value: sk-lf-...3b49
value: LANGFUSE_SECRET_KEY_PLACEHOLDER
- name: LANGFUSE_HOST
value: http://127.0.0.1:3001
- name: OLLAMA_API_KEY
value: 3cd542f8fca14fd08bedfd4f2ab36f9f.6G7Iukbvu1Keyi9x8eKckNEO
value: OLLAMA_API_KEY_PLACEHOLDER
- name: LOG_LEVEL
value: info
image: localhost/llm-triage-router:latest
Expand Down Expand Up @@ -154,10 +154,10 @@ spec:
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres-password-***
value: POSTGRES_PASSWORD_RAW_PLACEHOLDER
- name: POSTGRES_DB
value: langfuse
image: pgvector/pgvector:0.8.3-pg18
image: docker.io/pgvector/pgvector:0.8.3-pg18
livenessProbe:
exec:
command:
Expand Down Expand Up @@ -188,7 +188,7 @@ spec:
value: clickhouse
- name: CLICKHOUSE_PASSWORD
value: clickhouse
image: clickhouse/clickhouse-server:26.6.1.1193
image: docker.io/clickhouse/clickhouse-server:26.6.1.1193
livenessProbe:
exec:
command:
Expand Down Expand Up @@ -227,7 +227,7 @@ spec:
- noeviction
- --loglevel
- warning
image: valkey/valkey:9.1.0-alpine
image: docker.io/valkey/valkey:9.1.0-alpine
livenessProbe:
tcpSocket:
port: 6380
Expand All @@ -253,7 +253,7 @@ spec:
# Langfuse Web — observability dashboard
- env:
- name: DATABASE_URL
value: postgresql://postgres:***@127.0.0.1:5432/langfuse
value: postgresql://postgres:POSTGRES_PASSWORD_ENCODED_PLACEHOLDER@127.0.0.1:5432/langfuse
- name: NEXTAUTH_SECRET
value: NEXTAUTH_SECRET_PLACEHOLDER
- name: NEXTAUTH_URL
Expand Down Expand Up @@ -310,7 +310,7 @@ spec:
value: admin-local-pw-2026
- name: LANGFUSE_LOG_LEVEL
value: warn
image: langfuse/langfuse:3.202.1
image: docker.io/langfuse/langfuse:3.202.1
livenessProbe:
exec:
command:
Expand All @@ -337,7 +337,7 @@ spec:
# Langfuse Worker — background job processor
- env:
- name: DATABASE_URL
value: postgresql://postgres:***@127.0.0.1:5432/langfuse
value: postgresql://postgres:POSTGRES_PASSWORD_ENCODED_PLACEHOLDER@127.0.0.1:5432/langfuse
- name: CLICKHOUSE_URL
value: http://127.0.0.1:8123
- name: CLICKHOUSE_USER
Expand Down Expand Up @@ -372,7 +372,7 @@ spec:
value: minioadmin
- name: LANGFUSE_LOG_LEVEL
value: warn
image: langfuse/langfuse-worker:3.202.1
image: docker.io/langfuse/langfuse-worker:3.202.1
livenessProbe:
exec:
command:
Expand All @@ -396,7 +396,7 @@ spec:
value: minioadmin
- name: MINIO_ROOT_PASSWORD
value: minioadmin
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
image: docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z
livenessProbe:
httpGet:
path: /minio/health/live
Expand Down
79 changes: 69 additions & 10 deletions start-stack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if [ -f "$ENV_FILE" ]; then
fi

# Ensure openssl is installed if we need to generate passwords/keys
if [ -z "$POSTGRES_PASSWORD" ] || [ -z "$NEXTAUTH_SECRET" ] || [ -z "$SALT" ] || [ -z "$ENCRYPTION_KEY" ] || [ -z "$LITELLM_MASTER_KEY" ]; then
if [ -z "$POSTGRES_PASSWORD" ] || [ -z "$NEXTAUTH_SECRET" ] || [ -z "$SALT" ] || [ -z "$ENCRYPTION_KEY" ] || [ -z "$LITELLM_MASTER_KEY" ] || [ -z "$ROUTER_API_KEY" ] || [ -z "$LANGFUSE_PUBLIC_KEY" ] || [ -z "$LANGFUSE_SECRET_KEY" ]; then
if ! command -v openssl &>/dev/null; then
echo "❌ Error: 'openssl' is required to generate secure random keys but was not found in PATH."
exit 1
Expand Down Expand Up @@ -105,7 +105,7 @@ else
echo "⚠️ Warning: Host agy daemon not responding on port 5005"
fi

if [ -z "$NEXTAUTH_SECRET" ] || [ -z "$SALT" ] || [ -z "$ENCRYPTION_KEY" ] || [ -z "$LITELLM_MASTER_KEY" ] || [ -z "$ROUTER_API_KEY" ]; then
if [ -z "$NEXTAUTH_SECRET" ] || [ -z "$SALT" ] || [ -z "$ENCRYPTION_KEY" ] || [ -z "$LITELLM_MASTER_KEY" ] || [ -z "$ROUTER_API_KEY" ] || [ -z "$LANGFUSE_PUBLIC_KEY" ] || [ -z "$LANGFUSE_SECRET_KEY" ]; then
Comment thread
sheepdestroyer marked this conversation as resolved.
if ! command -v openssl &>/dev/null; then
echo "❌ Error: 'openssl' is required to generate secure random keys but was not found in PATH."
exit 1
Expand All @@ -116,6 +116,17 @@ fi
touch "$ENV_FILE"
chmod 600 "$ENV_FILE"

generate_uuid() {
local val
val=$(openssl rand -hex 16 2>/dev/null)
local status=$?
if [ $status -ne 0 ] || [ ${#val} -ne 32 ]; then
echo "❌ Error: Failed to generate secure random UUID (openssl rand returned exit status $status, length ${#val})." >&2
return 1
fi
echo "${val:0:8}-${val:8:4}-${val:12:4}-${val:16:4}-${val:20:12}"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if [ -z "$NEXTAUTH_SECRET" ]; then
NEXTAUTH_SECRET="$(openssl rand -base64 32)"
echo "NEXTAUTH_SECRET=\"$NEXTAUTH_SECRET\"" >> "$ENV_FILE"
Expand Down Expand Up @@ -151,6 +162,48 @@ if [ -z "$ROUTER_API_KEY" ]; then
echo "✓ Generated new ROUTER_API_KEY and saved to $ENV_FILE"
fi

if [ -z "$LANGFUSE_PUBLIC_KEY" ]; then
uuid=$(generate_uuid)
if [ $? -ne 0 ] || [ -z "$uuid" ]; then
echo "❌ Error: Failed to generate LANGFUSE_PUBLIC_KEY." >&2
exit 1
fi
LANGFUSE_PUBLIC_KEY="pk-lf-$uuid"
echo "LANGFUSE_PUBLIC_KEY=\"$LANGFUSE_PUBLIC_KEY\"" >> "$ENV_FILE"
chmod 600 "$ENV_FILE"
echo "✓ Generated new LANGFUSE_PUBLIC_KEY and saved to $ENV_FILE"
fi

if [ -z "$LANGFUSE_SECRET_KEY" ]; then
uuid=$(generate_uuid)
if [ $? -ne 0 ] || [ -z "$uuid" ]; then
echo "❌ Error: Failed to generate LANGFUSE_SECRET_KEY." >&2
exit 1
fi
LANGFUSE_SECRET_KEY="sk-lf-$uuid"
echo "LANGFUSE_SECRET_KEY=\"$LANGFUSE_SECRET_KEY\"" >> "$ENV_FILE"
chmod 600 "$ENV_FILE"
echo "✓ Generated new LANGFUSE_SECRET_KEY and saved to $ENV_FILE"
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if [ -z "$OLLAMA_API_KEY" ]; then
if [ -t 0 ]; then
echo "🔑 OLLAMA_API_KEY not found."
echo -n "Please enter your Ollama API Key (input will be hidden): "
read -rs OLLAMA_API_KEY
echo ""
echo "OLLAMA_API_KEY=\"$OLLAMA_API_KEY\"" >> "$ENV_FILE"
chmod 600 "$ENV_FILE"
echo "✓ Ollama API key saved securely to $ENV_FILE"
else
echo "❌ Error: OLLAMA_API_KEY is not set in your environment or in $ENV_FILE."
echo "Please run this script interactively first, or create the file manually:"
echo " echo 'OLLAMA_API_KEY=your_key_here' >> $ENV_FILE"
echo " chmod 600 $ENV_FILE"
exit 1
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi


# DYNAMIC_LITELLM_MASTER_KEY_PLACEHOLDER in router config is resolved at runtime from env

Expand Down Expand Up @@ -360,7 +413,7 @@ if podman pod exists agent-router-pod 2>/dev/null; then
fi

render_pod_yaml() {
export WORKDIR HOME LITELLM_MASTER_KEY POSTGRES_PASSWORD NEXTAUTH_SECRET SALT ENCRYPTION_KEY
export WORKDIR HOME LITELLM_MASTER_KEY POSTGRES_PASSWORD NEXTAUTH_SECRET SALT ENCRYPTION_KEY OLLAMA_API_KEY LANGFUSE_PUBLIC_KEY LANGFUSE_SECRET_KEY
python3 - "$WORKDIR/pod.yaml" <<'PY'
import os, sys, urllib.parse
uid = os.getuid()
Expand All @@ -370,12 +423,15 @@ placeholders = [
"/home/gpav/Vrac/LAB/AI/LLM-Routing",
"/home/gpav/",
"/run/user/1000",
"sk-lit...33bf",
"postgres:***",
"LITELLM_MASTER_KEY_PLACEHOLDER",
"POSTGRES_PASSWORD_RAW_PLACEHOLDER",
"POSTGRES_PASSWORD_ENCODED_PLACEHOLDER",
"NEXTAUTH_SECRET_PLACEHOLDER",
"SALT_PLACEHOLDER",
"ENCRYPTION_KEY_PLACEHOLDER",
"postgres-password-***"
"OLLAMA_API_KEY_PLACEHOLDER",
"LANGFUSE_PUBLIC_KEY_PLACEHOLDER",
"LANGFUSE_SECRET_KEY_PLACEHOLDER"
]
for ph in placeholders:
if ph not in text:
Expand All @@ -384,14 +440,17 @@ for ph in placeholders:
text = text.replace("/home/gpav/Vrac/LAB/AI/LLM-Routing", os.environ["WORKDIR"])
text = text.replace("/home/gpav/", os.environ["HOME"] + "/")
text = text.replace("/run/user/1000", f"/run/user/{uid}")
text = text.replace("sk-lit...33bf", os.environ["LITELLM_MASTER_KEY"])
text = text.replace("LITELLM_MASTER_KEY_PLACEHOLDER", os.environ["LITELLM_MASTER_KEY"])
text = text.replace("POSTGRES_PASSWORD_RAW_PLACEHOLDER", os.environ["POSTGRES_PASSWORD"])
# URL-encode the postgres password for DSN insertion
encoded_password = urllib.parse.quote_plus(os.environ['POSTGRES_PASSWORD'])
text = text.replace("postgres:***", f"postgres:{encoded_password}")
text = text.replace("postgres-password-***", os.environ["POSTGRES_PASSWORD"])
encoded_password = urllib.parse.quote(os.environ['POSTGRES_PASSWORD'], safe='')
text = text.replace("POSTGRES_PASSWORD_ENCODED_PLACEHOLDER", encoded_password)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
text = text.replace("NEXTAUTH_SECRET_PLACEHOLDER", os.environ["NEXTAUTH_SECRET"])
text = text.replace("SALT_PLACEHOLDER", os.environ["SALT"])
text = text.replace("ENCRYPTION_KEY_PLACEHOLDER", os.environ["ENCRYPTION_KEY"])
text = text.replace("OLLAMA_API_KEY_PLACEHOLDER", os.environ["OLLAMA_API_KEY"])
text = text.replace("LANGFUSE_PUBLIC_KEY_PLACEHOLDER", os.environ["LANGFUSE_PUBLIC_KEY"])
text = text.replace("LANGFUSE_SECRET_KEY_PLACEHOLDER", os.environ["LANGFUSE_SECRET_KEY"])
sys.stdout.write(text)
PY
}
Expand Down