summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiner Lohaus <hlohaus@users.noreply.github.com>2024-11-24 23:34:59 +0100
committerHeiner Lohaus <hlohaus@users.noreply.github.com>2024-11-24 23:34:59 +0100
commitc57321e2873c533a4f5d1cd5b278e3a8ca108656 (patch)
tree092f528d250071a604dd9c9c86858110ec450cb4
parentImprove slim docker build, Add openapi.json to release (diff)
downloadgpt4free-c57321e2873c533a4f5d1cd5b278e3a8ca108656.tar
gpt4free-c57321e2873c533a4f5d1cd5b278e3a8ca108656.tar.gz
gpt4free-c57321e2873c533a4f5d1cd5b278e3a8ca108656.tar.bz2
gpt4free-c57321e2873c533a4f5d1cd5b278e3a8ca108656.tar.lz
gpt4free-c57321e2873c533a4f5d1cd5b278e3a8ca108656.tar.xz
gpt4free-c57321e2873c533a4f5d1cd5b278e3a8ca108656.tar.zst
gpt4free-c57321e2873c533a4f5d1cd5b278e3a8ca108656.zip
Diffstat (limited to '')
-rw-r--r--.github/workflows/publish-workflow.yaml4
-rw-r--r--docker/Dockerfile-slim44
-rw-r--r--g4f/api/__init__.py29
3 files changed, 25 insertions, 52 deletions
diff --git a/.github/workflows/publish-workflow.yaml b/.github/workflows/publish-workflow.yaml
index 49ff03a5..9ad68bd8 100644
--- a/.github/workflows/publish-workflow.yaml
+++ b/.github/workflows/publish-workflow.yaml
@@ -16,7 +16,9 @@ jobs:
python-version: "3.8"
cache: 'pip'
- name: Install requirements
- run: pip install fastapi uvicorn python-multipart
+ run: |
+ pip install fastapi uvicorn python-multipart
+ pip install -r requirements-min.txt
- name: Generate openapi.json
run: |
python -m etc.tool.openapi
diff --git a/docker/Dockerfile-slim b/docker/Dockerfile-slim
index 0a09395b..04238144 100644
--- a/docker/Dockerfile-slim
+++ b/docker/Dockerfile-slim
@@ -1,9 +1,8 @@
-FROM python:bookworm
+FROM python:slim-bookworm
ARG G4F_VERSION
ARG G4F_USER=g4f
ARG G4F_USER_ID=1000
-ARG PYDANTIC_VERSION=1.8.1
ENV G4F_VERSION $G4F_VERSION
ENV G4F_USER $G4F_USER
@@ -12,60 +11,29 @@ ENV G4F_DIR /app
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y git \
- && apt-get install --quiet --yes --no-install-recommends \
- build-essential \
# Add user and user group
&& groupadd -g $G4F_USER_ID $G4F_USER \
&& useradd -rm -G sudo -u $G4F_USER_ID -g $G4F_USER_ID $G4F_USER \
&& mkdir -p /var/log/supervisor \
&& chown "${G4F_USER_ID}:${G4F_USER_ID}" /var/log/supervisor \
&& echo "${G4F_USER}:${G4F_USER}" | chpasswd \
- && python -m pip install --upgrade pip
+ && python -m pip install --upgrade pip \
+ && apt-get clean \
+ && rm --recursive --force /var/lib/apt/lists/* /tmp/* /var/tmp/*
USER $G4F_USER_ID
WORKDIR $G4F_DIR
ENV HOME /home/$G4F_USER
-ENV PATH "${HOME}/.local/bin:${HOME}/.cargo/bin:${PATH}"
+ENV PATH "${HOME}/.local/bin:${PATH}"
# Create app dir and copy the project's requirements file into it
RUN mkdir -p $G4F_DIR
COPY requirements-min.txt $G4F_DIR
COPY requirements-slim.txt $G4F_DIR
-# Install rust toolchain
-RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
-
# Upgrade pip for the latest features and install the project's Python dependencies.
-RUN pip install --no-cache-dir -r requirements-min.txt \
- && pip install --no-cache-dir --no-binary setuptools \
- Cython==0.29.22 \
- setuptools \
- # Install PyDantic
- && pip install \
- -vvv \
- --no-cache-dir \
- --no-binary :all: \
- --global-option=build_ext \
- --global-option=-j8 \
- pydantic==${PYDANTIC_VERSION} \
- && cat requirements-slim.txt | xargs -n 1 pip install --no-cache-dir || true \
- # Remove build packages
- && pip uninstall --yes \
- Cython \
- setuptools
-
-USER root
-
-# Clean up build deps
-RUN rm --recursive --force "${HOME}/.rustup" \
- && rustup self uninstall -y \
- && apt-get purge --auto-remove --yes \
- build-essential \
- && apt-get clean \
- && rm --recursive --force /var/lib/apt/lists/* /tmp/* /var/tmp/*
-
-USER $G4F_USER_ID
+RUN cat requirements-slim.txt | xargs -n 1 pip install --no-cache-dir || true
# Copy the entire package into the container.
ADD --chown=$G4F_USER:$G4F_USER g4f $G4F_DIR/g4f \ No newline at end of file
diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py
index e8e979b3..2f34fa6a 100644
--- a/g4f/api/__init__.py
+++ b/g4f/api/__init__.py
@@ -147,6 +147,9 @@ class ErrorResponse(Response):
def from_message(cls, message: str, status_code: int = HTTP_500_INTERNAL_SERVER_ERROR):
return cls(format_exception(message), status_code)
+ def render(self, content) -> bytes:
+ return str(content).encode(errors="ignore")
+
class AppConfig:
ignored_providers: Optional[list[str]] = None
g4f_api_key: Optional[str] = None
@@ -186,9 +189,9 @@ class Api:
user_g4f_api_key = await self.get_g4f_api_key(request)
except HTTPException as e:
if e.status_code == 403:
- return ErrorResponse("G4F API key required", HTTP_401_UNAUTHORIZED)
+ return ErrorResponse.from_message("G4F API key required", HTTP_401_UNAUTHORIZED)
if not secrets.compare_digest(self.g4f_api_key, user_g4f_api_key):
- return ErrorResponse("Invalid G4F API key", HTTP_403_FORBIDDEN)
+ return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
return await call_next(request)
def register_validation_exception_handler(self):
@@ -249,7 +252,7 @@ class Api:
'created': 0,
'owned_by': model_info.base_provider
})
- return ErrorResponse("The model does not exist.", HTTP_404_NOT_FOUND)
+ return ErrorResponse.from_message("The model does not exist.", HTTP_404_NOT_FOUND)
@self.app.post("/v1/chat/completions", responses={
HTTP_200_OK: {"model": ChatCompletion},
@@ -318,13 +321,13 @@ class Api:
except (ModelNotFoundError, ProviderNotFoundError) as e:
logger.exception(e)
- return ErrorResponse(e, HTTP_404_NOT_FOUND)
+ return ErrorResponse.from_exception(e, config, HTTP_404_NOT_FOUND)
except MissingAuthError as e:
logger.exception(e)
- return ErrorResponse(e, HTTP_401_UNAUTHORIZED)
+ return ErrorResponse.from_exception(e, config, HTTP_401_UNAUTHORIZED)
except Exception as e:
logger.exception(e)
- return ErrorResponse(e, HTTP_500_INTERNAL_SERVER_ERROR)
+ return ErrorResponse.from_exception(e, config, HTTP_500_INTERNAL_SERVER_ERROR)
responses = {
HTTP_200_OK: {"model": ImagesResponse},
@@ -359,13 +362,13 @@ class Api:
return response
except (ModelNotFoundError, ProviderNotFoundError) as e:
logger.exception(e)
- return ErrorResponse(e, HTTP_404_NOT_FOUND)
+ return ErrorResponse.from_exception(e, config, HTTP_404_NOT_FOUND)
except MissingAuthError as e:
logger.exception(e)
- return ErrorResponse(e, HTTP_401_UNAUTHORIZED)
+ return ErrorResponse.from_exception(e, config, HTTP_401_UNAUTHORIZED)
except Exception as e:
logger.exception(e)
- return ErrorResponse(e, HTTP_500_INTERNAL_SERVER_ERROR)
+ return ErrorResponse.from_exception(e, config, HTTP_500_INTERNAL_SERVER_ERROR)
@self.app.get("/v1/providers", responses={
HTTP_200_OK: {"model": List[ProviderResponseModel]},
@@ -428,12 +431,12 @@ class Api:
async def synthesize(request: Request, provider: str):
try:
provider_handler = convert_to_provider(provider)
- except ProviderNotFoundError:
- return ErrorResponse("Provider not found", HTTP_404_NOT_FOUND)
+ except ProviderNotFoundError as e:
+ return ErrorResponse.from_exception(e, status_code=HTTP_404_NOT_FOUND)
if not hasattr(provider_handler, "synthesize"):
- return ErrorResponse("Provider doesn't support synthesize", HTTP_404_NOT_FOUND)
+ return ErrorResponse.from_message("Provider doesn't support synthesize", HTTP_404_NOT_FOUND)
if len(request.query_params) == 0:
- return ErrorResponse("Missing query params", HTTP_422_UNPROCESSABLE_ENTITY)
+ return ErrorResponse.from_message("Missing query params", HTTP_422_UNPROCESSABLE_ENTITY)
response_data = provider_handler.synthesize({**request.query_params})
content_type = getattr(provider_handler, "synthesize_content_type", "application/octet-stream")
return StreamingResponse(response_data, media_type=content_type)