XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Improve error handling in api, Update openapi.json workflow

c57321e2
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

3 个文件 +25 -52
Modified .github/workflows/publish-workflow.yaml +3 -1
@@ -16,7 +16,9 @@ jobs:
16 16 python-version: "3.8"
17 17 cache: 'pip'
18 18 - name: Install requirements
19 run: pip install fastapi uvicorn python-multipart
19 run: |
20 pip install fastapi uvicorn python-multipart
21 pip install -r requirements-min.txt
20 22 - name: Generate openapi.json
21 23 run: |
22 24 python -m etc.tool.openapi
Modified docker/Dockerfile-slim +6 -38
@@ -1,9 +1,8 @@
1 FROM python:bookworm
1 FROM python:slim-bookworm
2 2
3 3 ARG G4F_VERSION
4 4 ARG G4F_USER=g4f
5 5 ARG G4F_USER_ID=1000
6 ARG PYDANTIC_VERSION=1.8.1
7 6
8 7 ENV G4F_VERSION $G4F_VERSION
9 8 ENV G4F_USER $G4F_USER
@@ -12,60 +11,29 @@ ENV G4F_DIR /app
12 11
13 12 RUN apt-get update && apt-get upgrade -y \
14 13 && apt-get install -y git \
15 && apt-get install --quiet --yes --no-install-recommends \
16 build-essential \
17 14 # Add user and user group
18 15 && groupadd -g $G4F_USER_ID $G4F_USER \
19 16 && useradd -rm -G sudo -u $G4F_USER_ID -g $G4F_USER_ID $G4F_USER \
20 17 && mkdir -p /var/log/supervisor \
21 18 && chown "${G4F_USER_ID}:${G4F_USER_ID}" /var/log/supervisor \
22 19 && echo "${G4F_USER}:${G4F_USER}" | chpasswd \
23 && python -m pip install --upgrade pip
20 && python -m pip install --upgrade pip \
21 && apt-get clean \
22 && rm --recursive --force /var/lib/apt/lists/* /tmp/* /var/tmp/*
24 23
25 24 USER $G4F_USER_ID
26 25 WORKDIR $G4F_DIR
27 26
28 27 ENV HOME /home/$G4F_USER
29 ENV PATH "${HOME}/.local/bin:${HOME}/.cargo/bin:${PATH}"
28 ENV PATH "${HOME}/.local/bin:${PATH}"
30 29
31 30 # Create app dir and copy the project's requirements file into it
32 31 RUN mkdir -p $G4F_DIR
33 32 COPY requirements-min.txt $G4F_DIR
34 33 COPY requirements-slim.txt $G4F_DIR
35 34
36 # Install rust toolchain
37 RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
38
39 35 # Upgrade pip for the latest features and install the project's Python dependencies.
40 RUN pip install --no-cache-dir -r requirements-min.txt \
41 && pip install --no-cache-dir --no-binary setuptools \
42 Cython==0.29.22 \
43 setuptools \
44 # Install PyDantic
45 && pip install \
46 -vvv \
47 --no-cache-dir \
48 --no-binary :all: \
49 --global-option=build_ext \
50 --global-option=-j8 \
51 pydantic==${PYDANTIC_VERSION} \
52 && cat requirements-slim.txt | xargs -n 1 pip install --no-cache-dir || true \
53 # Remove build packages
54 && pip uninstall --yes \
55 Cython \
56 setuptools
57
58 USER root
59
60 # Clean up build deps
61 RUN rm --recursive --force "${HOME}/.rustup" \
62 && rustup self uninstall -y \
63 && apt-get purge --auto-remove --yes \
64 build-essential \
65 && apt-get clean \
66 && rm --recursive --force /var/lib/apt/lists/* /tmp/* /var/tmp/*
67
68 USER $G4F_USER_ID
36 RUN cat requirements-slim.txt | xargs -n 1 pip install --no-cache-dir || true
69 37
70 38 # Copy the entire package into the container.
71 39 ADD --chown=$G4F_USER:$G4F_USER g4f $G4F_DIR/g4f
Modified g4f/api/__init__.py +16 -13
@@ -147,6 +147,9 @@ class ErrorResponse(Response):
147 147 def from_message(cls, message: str, status_code: int = HTTP_500_INTERNAL_SERVER_ERROR):
148 148 return cls(format_exception(message), status_code)
149 149
150 def render(self, content) -> bytes:
151 return str(content).encode(errors="ignore")
152
150 153 class AppConfig:
151 154 ignored_providers: Optional[list[str]] = None
152 155 g4f_api_key: Optional[str] = None
@@ -186,9 +189,9 @@ class Api:
186 189 user_g4f_api_key = await self.get_g4f_api_key(request)
187 190 except HTTPException as e:
188 191 if e.status_code == 403:
189 return ErrorResponse("G4F API key required", HTTP_401_UNAUTHORIZED)
192 return ErrorResponse.from_message("G4F API key required", HTTP_401_UNAUTHORIZED)
190 193 if not secrets.compare_digest(self.g4f_api_key, user_g4f_api_key):
191 return ErrorResponse("Invalid G4F API key", HTTP_403_FORBIDDEN)
194 return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
192 195 return await call_next(request)
193 196
194 197 def register_validation_exception_handler(self):
@@ -249,7 +252,7 @@ class Api:
249 252 'created': 0,
250 253 'owned_by': model_info.base_provider
251 254 })
252 return ErrorResponse("The model does not exist.", HTTP_404_NOT_FOUND)
255 return ErrorResponse.from_message("The model does not exist.", HTTP_404_NOT_FOUND)
253 256
254 257 @self.app.post("/v1/chat/completions", responses={
255 258 HTTP_200_OK: {"model": ChatCompletion},
@@ -318,13 +321,13 @@ class Api:
318 321
319 322 except (ModelNotFoundError, ProviderNotFoundError) as e:
320 323 logger.exception(e)
321 return ErrorResponse(e, HTTP_404_NOT_FOUND)
324 return ErrorResponse.from_exception(e, config, HTTP_404_NOT_FOUND)
322 325 except MissingAuthError as e:
323 326 logger.exception(e)
324 return ErrorResponse(e, HTTP_401_UNAUTHORIZED)
327 return ErrorResponse.from_exception(e, config, HTTP_401_UNAUTHORIZED)
325 328 except Exception as e:
326 329 logger.exception(e)
327 return ErrorResponse(e, HTTP_500_INTERNAL_SERVER_ERROR)
330 return ErrorResponse.from_exception(e, config, HTTP_500_INTERNAL_SERVER_ERROR)
328 331
329 332 responses = {
330 333 HTTP_200_OK: {"model": ImagesResponse},
@@ -359,13 +362,13 @@ class Api:
359 362 return response
360 363 except (ModelNotFoundError, ProviderNotFoundError) as e:
361 364 logger.exception(e)
362 return ErrorResponse(e, HTTP_404_NOT_FOUND)
365 return ErrorResponse.from_exception(e, config, HTTP_404_NOT_FOUND)
363 366 except MissingAuthError as e:
364 367 logger.exception(e)
365 return ErrorResponse(e, HTTP_401_UNAUTHORIZED)
368 return ErrorResponse.from_exception(e, config, HTTP_401_UNAUTHORIZED)
366 369 except Exception as e:
367 370 logger.exception(e)
368 return ErrorResponse(e, HTTP_500_INTERNAL_SERVER_ERROR)
371 return ErrorResponse.from_exception(e, config, HTTP_500_INTERNAL_SERVER_ERROR)
369 372
370 373 @self.app.get("/v1/providers", responses={
371 374 HTTP_200_OK: {"model": List[ProviderResponseModel]},
@@ -428,12 +431,12 @@ class Api:
428 431 async def synthesize(request: Request, provider: str):
429 432 try:
430 433 provider_handler = convert_to_provider(provider)
431 except ProviderNotFoundError:
432 return ErrorResponse("Provider not found", HTTP_404_NOT_FOUND)
434 except ProviderNotFoundError as e:
435 return ErrorResponse.from_exception(e, status_code=HTTP_404_NOT_FOUND)
433 436 if not hasattr(provider_handler, "synthesize"):
434 return ErrorResponse("Provider doesn't support synthesize", HTTP_404_NOT_FOUND)
437 return ErrorResponse.from_message("Provider doesn't support synthesize", HTTP_404_NOT_FOUND)
435 438 if len(request.query_params) == 0:
436 return ErrorResponse("Missing query params", HTTP_422_UNPROCESSABLE_ENTITY)
439 return ErrorResponse.from_message("Missing query params", HTTP_422_UNPROCESSABLE_ENTITY)
437 440 response_data = provider_handler.synthesize({**request.query_params})
438 441 content_type = getattr(provider_handler, "synthesize_content_type", "application/octet-stream")
439 442 return StreamingResponse(response_data, media_type=content_type)