-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup_env.py
More file actions
executable file
·222 lines (179 loc) · 9.47 KB
/
setup_env.py
File metadata and controls
executable file
·222 lines (179 loc) · 9.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import os
import secrets
import sys
import uuid
from getpass import getpass
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
ENV_FILE = SCRIPT_DIR / ".env"
BANNER = """
╔═══════════════════════════════════════════════════════════════════╗
║ LLMOps Stack - Environment Setup ║
╠═══════════════════════════════════════════════════════════════════╣
║ This script generates a .env file with all required secrets. ║
║ You only need to provide your LLM provider API keys. ║
╚═══════════════════════════════════════════════════════════════════╝
"""
def generate_hex(length: int = 32) -> str:
"""Generate a cryptographically secure hex string."""
return secrets.token_hex(length)
def generate_uuid() -> str:
"""Generate a random UUID v4."""
return str(uuid.uuid4())
def prompt_secret(prompt_text: str, allow_empty: bool = False) -> str:
"""Prompt for a secret value using getpass (masked input)."""
while True:
value = getpass(prompt_text).strip()
if value or allow_empty:
return value
print(" Error: value cannot be empty. Please try again.")
def prompt_input(prompt_text: str, default: str = "") -> str:
"""Prompt for a non-secret value with optional default."""
suffix = f" [{default}]" if default else ""
value = input(f"{prompt_text}{suffix}: ").strip()
return value if value else default
def confirm_overwrite() -> bool:
"""Ask user to confirm overwriting an existing .env file."""
print(f"\n WARNING: {ENV_FILE} already exists.")
print(" Overwriting will replace ALL existing secrets.")
answer = input(" Do you want to continue? (y/N): ").strip().lower()
return answer in ("y", "yes")
def main() -> None:
print(BANNER)
if ENV_FILE.exists():
if not confirm_overwrite():
print("\n Aborted. Existing .env was NOT modified.")
sys.exit(0)
print()
print("─── LLM Provider API Keys ─────────────────────────────────────────")
print(" (input is hidden for security)\n")
gemini_key = prompt_secret(" Google AI Studio (Gemini) API key: ")
openrouter_key = prompt_secret(" OpenRouter API key: ")
print("\n─── Langfuse Initial Admin User ───────────────────────────────────")
print(" These credentials are used for headless initialization on first")
print(" boot. You will use them to log in to the Langfuse UI.\n")
langfuse_admin_email = prompt_input(" Admin email", default="admin@localhost")
langfuse_admin_name = prompt_input(" Admin name", default="Admin")
default_admin_pw = generate_hex(16)
print(f"\n A secure password has been generated: {default_admin_pw}")
custom_pw = input(" Press Enter to accept, or type a custom password: ").strip()
langfuse_admin_password = custom_pw if custom_pw else default_admin_pw
print("\n─── Generating secrets ────────────────────────────────────────────")
litellm_master_key = f"sk-{generate_hex(32)}"
litellm_salt_key = generate_hex(32)
postgres_password = generate_hex(32)
clickhouse_password = generate_hex(32)
minio_root_password = generate_hex(32)
redis_auth = generate_hex(32)
langfuse_public_key = f"lf_pk_{generate_hex(16)}"
langfuse_secret_key = f"lf_sk_{generate_hex(32)}"
nextauth_secret = generate_hex(32)
langfuse_salt = generate_hex(16)
langfuse_encryption_key = generate_hex(32) # exactly 64 hex chars
webui_secret_key = generate_hex(32)
langfuse_init_org_id = generate_uuid()
langfuse_init_project_id = generate_uuid()
print(" All secrets generated successfully.\n")
# --- Build .env content ---
env_content = f"""\
# =============================================================================
# LLMOps Stack - Environment Variables
# =============================================================================
# Generated by setup_env.py
# IMPORTANT: Do NOT commit this file to version control.
# =============================================================================
# ---------------------------------------------------------------------------
# LLM Provider API Keys
# ---------------------------------------------------------------------------
GEMINI_API_KEY={gemini_key}
OPENROUTER_API_KEY={openrouter_key}
# ---------------------------------------------------------------------------
# LiteLLM Proxy
# ---------------------------------------------------------------------------
# Master key for admin access (starts with sk-)
LITELLM_MASTER_KEY={litellm_master_key}
# Salt key for encrypting/decrypting LLM API credentials in DB
# WARNING: Do NOT change this after adding models - it cannot be rotated
LITELLM_SALT_KEY={litellm_salt_key}
# ---------------------------------------------------------------------------
# PostgreSQL (shared by LiteLLM + Langfuse)
# ---------------------------------------------------------------------------
POSTGRES_USER=llmops
POSTGRES_PASSWORD={postgres_password}
POSTGRES_DB=litellm
# Langfuse uses a separate database on the same Postgres instance
LANGFUSE_DB=langfuse
# ---------------------------------------------------------------------------
# ClickHouse (Langfuse OLAP)
# ---------------------------------------------------------------------------
CLICKHOUSE_USER=default
CLICKHOUSE_PASSWORD={clickhouse_password}
# ---------------------------------------------------------------------------
# MinIO (S3-compatible blob storage for Langfuse)
# ---------------------------------------------------------------------------
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD={minio_root_password}
# S3 bucket names for Langfuse
LANGFUSE_S3_EVENT_UPLOAD_BUCKET=langfuse-events
LANGFUSE_S3_MEDIA_UPLOAD_BUCKET=langfuse-media
LANGFUSE_S3_REGION=us-east-1
# ---------------------------------------------------------------------------
# Redis
# ---------------------------------------------------------------------------
REDIS_AUTH={redis_auth}
# ---------------------------------------------------------------------------
# Langfuse
# ---------------------------------------------------------------------------
# Public/Secret keys for the Langfuse API (used by LiteLLM to send traces)
LANGFUSE_PUBLIC_KEY={langfuse_public_key}
LANGFUSE_SECRET_KEY={langfuse_secret_key}
# NextAuth secret for Langfuse session management
NEXTAUTH_SECRET={nextauth_secret}
# Langfuse password salt
LANGFUSE_SALT={langfuse_salt}
# Langfuse encryption key (exactly 64 hex characters = 32 bytes)
LANGFUSE_ENCRYPTION_KEY={langfuse_encryption_key}
# ---------------------------------------------------------------------------
# Langfuse Headless Initialization
# ---------------------------------------------------------------------------
# These variables configure automatic org/project/user creation on first boot.
# See: https://langfuse.com/self-hosting/administration/headless-initialization
LANGFUSE_INIT_ORG_ID={langfuse_init_org_id}
LANGFUSE_INIT_ORG_NAME=LLMOps
LANGFUSE_INIT_PROJECT_ID={langfuse_init_project_id}
LANGFUSE_INIT_PROJECT_NAME=Default
LANGFUSE_INIT_PROJECT_PUBLIC_KEY={langfuse_public_key}
LANGFUSE_INIT_PROJECT_SECRET_KEY={langfuse_secret_key}
LANGFUSE_INIT_USER_EMAIL={langfuse_admin_email}
LANGFUSE_INIT_USER_NAME={langfuse_admin_name}
LANGFUSE_INIT_USER_PASSWORD={langfuse_admin_password}
# ---------------------------------------------------------------------------
# Open WebUI
# ---------------------------------------------------------------------------
# Secret key for session management
WEBUI_SECRET_KEY={webui_secret_key}
"""
# --- Write .env file ---
ENV_FILE.write_text(env_content)
os.chmod(ENV_FILE, 0o600) # owner read/write only
# --- Summary ---
print("═══════════════════════════════════════════════════════════════════")
print(f" .env written to: {ENV_FILE}")
print(f" File permissions: 600 (owner read/write only)")
print()
print(" Key values to remember:")
print(f" LiteLLM Master Key : {litellm_master_key}")
print(f" Langfuse URL : http://localhost:3001")
print(f" Langfuse Admin : {langfuse_admin_email}")
print(f" Langfuse Password : {langfuse_admin_password}")
print(f" Open WebUI URL : http://localhost:3000")
print()
print(" Next steps:")
print(" 1. docker compose up -d")
print(" 2. Wait ~60s for all services to start")
print(" 3. Open http://localhost:3000 (Open WebUI)")
print(" 4. Open http://localhost:3001 (Langfuse)")
print(" 4. Open http://localhost:4000 (Litellm Proxy)")
print("═══════════════════════════════════════════════════════════════════")
if __name__ == "__main__":
main()