Skip to content

Commit 2741835

Browse files
build(Dockerfile): move prisma build to dockerfile
Seems to solve - BerriAI#1321
1 parent 6f9d3fc commit 2741835

File tree

7 files changed

+150
-38
lines changed

7 files changed

+150
-38
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ proxy_server_config_@.yaml
3131
.gitignore
3232
proxy_server_config_2.yaml
3333
litellm/proxy/secret_managers/credentials.json
34+
hosted_config.yaml

Dockerfile

+14-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ ARG LITELLM_BUILD_IMAGE=python:3.9
33

44
# Runtime image
55
ARG LITELLM_RUNTIME_IMAGE=python:3.9-slim
6-
76
# Builder stage
87
FROM $LITELLM_BUILD_IMAGE as builder
98

@@ -35,8 +34,12 @@ RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt
3534

3635
# Runtime stage
3736
FROM $LITELLM_RUNTIME_IMAGE as runtime
37+
ARG with_database
3838

3939
WORKDIR /app
40+
# Copy the current directory contents into the container at /app
41+
COPY . .
42+
RUN ls -la /app
4043

4144
# Copy the built wheel from the builder stage to the runtime stage; assumes only one wheel file is present
4245
COPY --from=builder /app/dist/*.whl .
@@ -45,9 +48,17 @@ COPY --from=builder /wheels/ /wheels/
4548
# Install the built wheel using pip; again using a wildcard if it's the only file
4649
RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ && rm -f *.whl && rm -rf /wheels
4750

51+
# Check if the with_database argument is set to 'true'
52+
RUN echo "Value of with_database is: ${with_database}"
53+
# If true, execute the following instructions
54+
RUN if [ "$with_database" = "true" ]; then \
55+
prisma generate; \
56+
chmod +x /app/retry_push.sh; \
57+
/app/retry_push.sh; \
58+
fi
4859

49-
EXPOSE 4000/tcp
60+
EXPOSE 8000/tcp
5061

5162
# Set your entrypoint and command
5263
ENTRYPOINT ["litellm"]
53-
CMD ["--port", "4000"]
64+
CMD ["--config", "./hosted_config.yaml", "--port", "8000", "--num_workers", "8"]

docker/.env.example

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
LITELLM_MASTER_KEY="sk-1234"
77

88
############
9-
# Database - You can change these to any PostgreSQL database that has logical replication enabled.
9+
# Database - You can change these to any PostgreSQL database.
1010
############
1111

12-
# LITELLM_DATABASE_URL="your-postgres-db-url"
12+
LITELLM_DATABASE_URL="your-postgres-db-url"
1313

1414

1515
############

litellm/proxy/proxy_server.py

+43-10
Original file line numberDiff line numberDiff line change
@@ -519,16 +519,12 @@ async def get_config(self, config_file_path: Optional[str] = None) -> dict:
519519
user_config_file_path = config_file_path
520520
# Load existing config
521521
## Yaml
522-
if os.path.exists(f"{file_path}"):
523-
with open(f"{file_path}", "r") as config_file:
524-
config = yaml.safe_load(config_file)
525-
else:
526-
config = {
527-
"model_list": [],
528-
"general_settings": {},
529-
"router_settings": {},
530-
"litellm_settings": {},
531-
}
522+
if file_path is not None:
523+
if os.path.exists(f"{file_path}"):
524+
with open(f"{file_path}", "r") as config_file:
525+
config = yaml.safe_load(config_file)
526+
else:
527+
raise Exception(f"File not found! - {file_path}")
532528

533529
## DB
534530
if (
@@ -2328,6 +2324,21 @@ async def update_config(config_info: ConfigYAML):
23282324
raise HTTPException(status_code=500, detail=f"An error occurred - {str(e)}")
23292325

23302326

2327+
@router.get(
2328+
"/config/get",
2329+
tags=["config.yaml"],
2330+
dependencies=[Depends(user_api_key_auth)],
2331+
)
2332+
async def get_config():
2333+
"""
2334+
Master key only.
2335+
2336+
Returns the config. Mainly used for testing.
2337+
"""
2338+
global proxy_config
2339+
return await proxy_config.get_config()
2340+
2341+
23312342
@router.get("/config/yaml", tags=["config.yaml"])
23322343
async def config_yaml_endpoint(config_info: ConfigYAML):
23332344
"""
@@ -2416,6 +2427,28 @@ async def health_endpoint(
24162427
}
24172428

24182429

2430+
@router.get("/health/readiness", tags=["health"])
2431+
async def health_readiness():
2432+
"""
2433+
Unprotected endpoint for checking if worker can receive requests
2434+
"""
2435+
global prisma_client
2436+
if prisma_client is not None: # if db passed in, check if it's connected
2437+
if prisma_client.db.is_connected() == True:
2438+
return {"status": "healthy"}
2439+
else:
2440+
return {"status": "healthy"}
2441+
raise HTTPException(status_code=503, detail="Service Unhealthy")
2442+
2443+
2444+
@router.get("/health/liveliness", tags=["health"])
2445+
async def health_liveliness():
2446+
"""
2447+
Unprotected endpoint for checking if worker is alive
2448+
"""
2449+
return "I'm alive!"
2450+
2451+
24192452
@router.get("/")
24202453
async def home(request: Request):
24212454
return "LiteLLM: RUNNING"

litellm/proxy/utils.py

+29-23
Original file line numberDiff line numberDiff line change
@@ -250,30 +250,36 @@ def on_backoff(details):
250250

251251
class PrismaClient:
252252
def __init__(self, database_url: str, proxy_logging_obj: ProxyLogging):
253-
print_verbose(
254-
"LiteLLM: DATABASE_URL Set in config, trying to 'pip install prisma'"
255-
)
256-
## init logging object
257-
self.proxy_logging_obj = proxy_logging_obj
258-
os.environ["DATABASE_URL"] = database_url
259-
# Save the current working directory
260-
original_dir = os.getcwd()
261-
# set the working directory to where this script is
262-
abspath = os.path.abspath(__file__)
263-
dname = os.path.dirname(abspath)
264-
os.chdir(dname)
265-
253+
### Check if prisma client can be imported (setup done in Docker build)
266254
try:
267-
subprocess.run(["prisma", "generate"])
268-
subprocess.run(
269-
["prisma", "db", "push", "--accept-data-loss"]
270-
) # this looks like a weird edge case when prisma just wont start on render. we need to have the --accept-data-loss
271-
finally:
272-
os.chdir(original_dir)
273-
# Now you can import the Prisma Client
274-
from prisma import Client # type: ignore
275-
276-
self.db = Client() # Client to connect to Prisma db
255+
from prisma import Client # type: ignore
256+
257+
self.db = Client() # Client to connect to Prisma db
258+
except: # if not - go through normal setup process
259+
print_verbose(
260+
"LiteLLM: DATABASE_URL Set in config, trying to 'pip install prisma'"
261+
)
262+
## init logging object
263+
self.proxy_logging_obj = proxy_logging_obj
264+
os.environ["DATABASE_URL"] = database_url
265+
# Save the current working directory
266+
original_dir = os.getcwd()
267+
# set the working directory to where this script is
268+
abspath = os.path.abspath(__file__)
269+
dname = os.path.dirname(abspath)
270+
os.chdir(dname)
271+
272+
try:
273+
subprocess.run(["prisma", "generate"])
274+
subprocess.run(
275+
["prisma", "db", "push", "--accept-data-loss"]
276+
) # this looks like a weird edge case when prisma just wont start on render. we need to have the --accept-data-loss
277+
finally:
278+
os.chdir(original_dir)
279+
# Now you can import the Prisma Client
280+
from prisma import Client # type: ignore
281+
282+
self.db = Client() # Client to connect to Prisma db
277283

278284
def hash_token(self, token: str):
279285
# Hash the string using SHA-256

retry_push.sh

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/bash
2+
3+
retry_count=0
4+
max_retries=3
5+
exit_code=1
6+
7+
until [ $retry_count -ge $max_retries ] || [ $exit_code -eq 0 ]
8+
do
9+
retry_count=$((retry_count+1))
10+
echo "Attempt $retry_count..."
11+
12+
# Run the Prisma db push command
13+
prisma db push --accept-data-loss
14+
15+
exit_code=$?
16+
17+
if [ $exit_code -ne 0 ] && [ $retry_count -lt $max_retries ]; then
18+
echo "Retrying in 10 seconds..."
19+
sleep 10
20+
fi
21+
done
22+
23+
if [ $exit_code -ne 0 ]; then
24+
echo "Unable to push database changes after $max_retries retries."
25+
exit 1
26+
fi
27+
28+
echo "Database push successful!"

schema.prisma

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
datasource client {
2+
provider = "postgresql"
3+
url = env("DATABASE_URL")
4+
}
5+
6+
generator client {
7+
provider = "prisma-client-py"
8+
}
9+
10+
model LiteLLM_UserTable {
11+
user_id String @unique
12+
max_budget Float?
13+
spend Float @default(0.0)
14+
user_email String?
15+
}
16+
17+
// required for token gen
18+
model LiteLLM_VerificationToken {
19+
token String @unique
20+
spend Float @default(0.0)
21+
expires DateTime?
22+
models String[]
23+
aliases Json @default("{}")
24+
config Json @default("{}")
25+
user_id String?
26+
max_parallel_requests Int?
27+
metadata Json @default("{}")
28+
}
29+
30+
model LiteLLM_Config {
31+
param_name String @id
32+
param_value Json?
33+
}

0 commit comments

Comments
 (0)