Your task is to review pull requests. Instructions:
- Provide the response in following JSON format: {"reviews": [{"line": <line_number>, "body": "<review comment>"}]}
- Do not give positive comments or compliments.
- Provide comments and suggestions ONLY if there is something to improve, otherwise "reviews" should be an empty array.
- Write the comment in GitHub Markdown format.
- Use the given description only for the overall context and only comment the code.
- IMPORTANT: NEVER suggest adding comments to the code.

Review the following code diff in the file "g4f/api/__init__.py" and take the pull request title and description into account when writing the response.
  
Pull request title: [pull] main from xtekky:main
Pull request description:
---
See Commits and Changes for more details.

-----
Created by [<img src="https://prod.download/pull-18h-svg" valign="bottom"/> **pull[bot]**](https://github.com/wei/pull)

_Can you help keep this open source service alive? **[💖 Please sponsor : )](https://prod.download/pull-pr-sponsor)**_
---

Each line is prefixed by its number. Code to review:
```
14: from fastapi.encoders import jsonable_encoder
15: from fastapi.middleware.cors import CORSMiddleware
16: from pydantic import BaseModel
-from typing import Union, Optional
17:+from typing import Union, Optional, Iterator
18: 
19: import g4f
20: import g4f.debug
-from g4f.client import Client
21:+from g4f.client import Client, ChatCompletion, ChatCompletionChunk, ImagesResponse
22: from g4f.typing import Messages
23: from g4f.cookies import read_cookie_files
24: 
-def create_app():
25:+logger = logging.getLogger(__name__)
26:+
27:+def create_app(g4f_api_key: str = None):
28:     app = FastAPI()
-    api = Api(app)
29:+
30:+    # Add CORS middleware
31:     app.add_middleware(
32:         CORSMiddleware,
33:         allow_origin_regex=".*",
35:         allow_methods=["*"],
36:         allow_headers=["*"],
37:     )
38:+
39:+    api = Api(app, g4f_api_key=g4f_api_key)
40:     api.register_routes()
41:     api.register_authorization()
42:     api.register_validation_exception_handler()
43:+
44:+    # Read cookie files if not ignored
45:     if not AppConfig.ignore_cookie_files:
46:         read_cookie_files()
-    return app
47: 
-def create_app_debug():
-    g4f.debug.logging = True
-    return create_app()
48:+    return app
49: 
-class ChatCompletionsForm(BaseModel):
50:+class ChatCompletionsConfig(BaseModel):
51:     messages: Messages
52:     model: str
53:     provider: Optional[str] = None
59:     web_search: Optional[bool] = None
60:     proxy: Optional[str] = None
61: 
-class ImagesGenerateForm(BaseModel):
-    model: Optional[str] = None
-    provider: Optional[str] = None
62:+class ImageGenerationConfig(BaseModel):
63:     prompt: str
-    response_format: Optional[str] = None
-    api_key: Optional[str] = None
-    proxy: Optional[str] = None
64:+    model: Optional[str] = None
65:+    response_format: str = "url"
66: 
-class AppConfig():
67:+class AppConfig:
68:     ignored_providers: Optional[list[str]] = None
69:     g4f_api_key: Optional[str] = None
70:     ignore_cookie_files: bool = False
75:         for key, value in data.items():
76:             setattr(cls, key, value)
77: 
78:+list_ignored_providers: list[str] = None
79:+
80:+def set_list_ignored_providers(ignored: list[str]):
81:+    global list_ignored_providers
82:+    list_ignored_providers = ignored
83:+
84: class Api:
-    def __init__(self, app: FastAPI) -> None:
85:+    def __init__(self, app: FastAPI, g4f_api_key=None) -> None:
86:         self.app = app
87:         self.client = Client()
88:+        self.g4f_api_key = g4f_api_key
89:         self.get_g4f_api_key = APIKeyHeader(name="g4f-api-key")
90: 
91:     def register_authorization(self):
92:         @self.app.middleware("http")
93:         async def authorization(request: Request, call_next):
-            if AppConfig.g4f_api_key and request.url.path in ["/v1/chat/completions", "/v1/completions"]:
94:+            if self.g4f_api_key and request.url.path in ["/v1/chat/completions", "/v1/completions", "/v1/images/generate"]:
95:                 try:
96:                     user_g4f_api_key = await self.get_g4f_api_key(request)
97:                 except HTTPException as e:
100:                             status_code=HTTP_401_UNAUTHORIZED,
101:                             content=jsonable_encoder({"detail": "G4F API key required"}),
102:                         )
-                if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
103:+                if not secrets.compare_digest(self.g4f_api_key, user_g4f_api_key):
104:                     return JSONResponse(
105:                         status_code=HTTP_403_FORBIDDEN,
106:                         content=jsonable_encoder({"detail": "Invalid G4F API key"}),
107:                     )
-            return await call_next(request)
108:+
109:+            response = await call_next(request)
110:+            return response
111: 
112:     def register_validation_exception_handler(self):
113:         @self.app.exception_handler(RequestValidationError)
114:         async def validation_exception_handler(request: Request, exc: RequestValidationError):
115:             details = exc.errors()
-            modified_details = [{
-                "loc": error["loc"],
-                "message": error["msg"],
-                "type": error["type"],
-            } for error in details]
116:+            modified_details = []
117:+            for error in details:
118:+                modified_details.append({
119:+                    "loc": error["loc"],
120:+                    "message": error["msg"],
121:+                    "type": error["type"],
122:+                })
123:             return JSONResponse(
124:                 status_code=HTTP_422_UNPROCESSABLE_ENTITY,
125:                 content=jsonable_encoder({"detail": modified_details}),
133:         @self.app.get("/v1")
134:         async def read_root_v1():
135:             return HTMLResponse('g4f API: Go to '
-                                '<a href="/v1/chat/completions">chat/completions</a> '
-                                'or <a href="/v1/models">models</a>.')
136:+                                '<a href="/v1/chat/completions">chat/completions</a>, '
137:+                                '<a href="/v1/models">models</a>, or '
138:+                                '<a href="/v1/images/generate">images/generate</a>.')
139: 
140:         @self.app.get("/v1/models")
141:         async def models():
-            model_list = {
-                model: g4f.models.ModelUtils.convert[model]
142:+            model_list = dict(
143:+                (model, g4f.models.ModelUtils.convert[model])
144:                 for model in g4f.Model.__all__()
-            }
145:+            )
146:             model_list = [{
147:                 'id': model_id,
148:                 'object': 'model',
149:                 'created': 0,
150:                 'owned_by': model.base_provider
151:             } for model_id, model in model_list.items()]
-            return JSONResponse({
-                "object": "list",
-                "data": model_list,
-            })
152:+            return JSONResponse(model_list)
153: 
154:         @self.app.get("/v1/models/{model_name}")
155:         async def model_info(model_name: str):
165:                 return JSONResponse({"error": "The model does not exist."})
166: 
167:         @self.app.post("/v1/chat/completions")
-        async def chat_completions(config: ChatCompletionsForm, request: Request = None, provider: str = None):
168:+        async def chat_completions(config: ChatCompletionsConfig, request: Request = None, provider: str = None):
169:             try:
170:                 config.provider = provider if config.provider is None else config.provider
171:                 if config.api_key is None and request is not None:
174:                         auth_header = auth_header.split(None, 1)[-1]
175:                         if auth_header and auth_header != "Bearer":
176:                             config.api_key = auth_header
-                # Use the asynchronous create method and await it
-                response = await self.client.chat.completions.async_create(
177:+
178:+                # Create the completion response
179:+                response = self.client.chat.completions.create(
180:                     **{
181:                         **AppConfig.defaults,
182:                         **config.dict(exclude_none=True),
183:                     },
184:                     ignored=AppConfig.ignored_providers
185:                 )
-                if not config.stream:
186:+
187:+                # Check if the response is synchronous or asynchronous
188:+                if isinstance(response, ChatCompletion):
189:+                    # Synchronous response
190:                     return JSONResponse(response.to_json())
191: 
192:+                if not config.stream:
193:+                    # If the response is an iterator but not streaming, collect the result
194:+                    response_list = list(response) if isinstance(response, Iterator) else [response]
195:+                    return JSONResponse(response_list[0].to_json())
196:+
197:+                # Streaming response
198:+                async def async_generator(sync_gen):
199:+                    for item in sync_gen:
200:+                        yield item
201:+
202:                 async def streaming():
203:                     try:
-                        async for chunk in response:
204:+                        async for chunk in async_generator(response):
205:                             yield f"data: {json.dumps(chunk.to_json())}\n\n"
206:                     except GeneratorExit:
207:                         pass
208:                     except Exception as e:
-                        logging.exception(e)
209:+                        logger.exception(e)
210:                         yield f'data: {format_exception(e, config)}\n\n'
211:                     yield "data: [DONE]\n\n"
212:+
213:                 return StreamingResponse(streaming(), media_type="text/event-stream")
214: 
215:             except Exception as e:
-                logging.exception(e)
216:+                logger.exception(e)
217:                 return Response(content=format_exception(e, config), status_code=500, media_type="application/json")
218: 
-        @self.app.post("/v1/completions")
-        async def completions():
-            return Response(content=json.dumps({'info': 'Not working yet.'}, indent=4), media_type="application/json")
-
-        @self.app.post("/v1/images/generations")
-        async def images_generate(config: ImagesGenerateForm, request: Request = None, provider: str = None):
219:+        @self.app.post("/v1/images/generate")
220:+        async def generate_image(config: ImageGenerationConfig):
221:             try:
-                config.provider = provider if config.provider is None else config.provider
-                if config.api_key is None and request is not None:
-                    auth_header = request.headers.get("Authorization")
-                    if auth_header is not None:
-                        auth_header = auth_header.split(None, 1)[-1]
-                        if auth_header and auth_header != "Bearer":
-                            config.api_key = auth_header
-                # Use the asynchronous generate method and await it
-                response = await self.client.images.async_generate(
-                    **config.dict(exclude_none=True),
222:+                response: ImagesResponse = await self.client.images.async_generate(
223:+                    prompt=config.prompt,
224:+                    model=config.model,
225:+                    response_format=config.response_format
226:                 )
-                return JSONResponse(response.to_json())
227:+                # Convert Image objects to dictionaries
228:+                response_data = [{"url": image.url, "b64_json": image.b64_json} for image in response.data]
229:+                return JSONResponse({"data": response_data})
230:             except Exception as e:
-                logging.exception(e)
231:+                logger.exception(e)
232:                 return Response(content=format_exception(e, config), status_code=500, media_type="application/json")
233: 
-def format_exception(e: Exception, config: ChatCompletionsForm) -> str:
234:+        @self.app.post("/v1/completions")
235:+        async def completions():
236:+            return Response(content=json.dumps({'info': 'Not working yet.'}, indent=4), media_type="application/json")
237:+
238:+def format_exception(e: Exception, config: Union[ChatCompletionsConfig, ImageGenerationConfig]) -> str:
239:     last_provider = g4f.get_last_provider(True)
240:     return json.dumps({
241:         "error": {"message": f"{e.__class__.__name__}: {e}"},
-        "model": last_provider.get("model") if last_provider else config.model,
-        "provider": last_provider.get("name") if last_provider else config.provider
242:+        "model": last_provider.get("model") if last_provider else getattr(config, 'model', None),
243:+        "provider": last_provider.get("name") if last_provider else getattr(config, 'provider', None)
244:     })
245: 
246: def run_api(
249:     bind: str = None,
250:     debug: bool = False,
251:     workers: int = None,
-    use_colors: bool = None
252:+    use_colors: bool = None,
253:+    g4f_api_key: str = None
254: ) -> None:
255:     print(f'Starting server... [g4f v-{g4f.version.utils.current_version}]' + (" (debug)" if debug else ""))
256:     if use_colors is None:
257:         use_colors = debug
258:     if bind is not None:
259:         host, port = bind.split(":")
260:+    if debug:
261:+        g4f.debug.logging = True
262:     uvicorn.run(
-        f"g4f.api:create_app{'_debug' if debug else ''}",
-        host=host, port=int(port),
-        workers=workers,
-        use_colors=use_colors,
-        factory=True,
263:+        "g4f.api:create_app", 
264:+        host=host, 
265:+        port=int(port), 
266:+        workers=workers, 
267:+        use_colors=use_colors, 
268:+        factory=True, 
269:         reload=debug
270:     )
```