返回提交历史
Modified
docker/Dockerfile-slim
+1
-1
Modified
g4f/Provider/PollinationsAI.py
+1
-2
Modified
g4f/Provider/needs_auth/Gemini.py
+1
-0
Modified
g4f/api/__init__.py
+70
-45
Modified
g4f/cli.py
+3
-1
Modified
g4f/client/__init__.py
+10
-10
Modified
g4f/client/stubs.py
+128
-108
Modified
g4f/gui/__init__.py
+16
-12
Modified
g4f/gui/server/api.py
+4
-36
Modified
g4f/gui/server/backend.py
+16
-7
Modified
g4f/gui/server/website.py
+4
-3
Modified
g4f/requests/raise_for_status.py
+1
-1
XFEstudio/gpt4free
Arm2 (#2414)
* Fix arm v7 build / improve api * Update stubs.py * Fix unit tests
804a80bc
代码差异
12 个文件
+255
-226
@@ -47,7 +47,7 @@ RUN python -m pip install --upgrade pip \
47
47
--global-option=build_ext \
48
48
--global-option=-j8 \
49
49
pydantic==${PYDANTIC_VERSION} \
50
&& cat requirements.txt | xargs -n 1 pip install --no-cache-dir \
50
&& cat requirements-slim.txt | xargs -n 1 pip install --no-cache-dir || true \
51
51
# Remove build packages
52
52
&& pip uninstall --yes \
53
53
Cython \
@@ -46,8 +46,7 @@ class PollinationsAI(OpenaiAPI):
46
46
seed: str = None,
47
47
**kwargs
48
48
) -> AsyncResult:
49
if model:
50
model = cls.get_model(model)
49
model = cls.get_model(model)
51
50
if model in cls.image_models:
52
51
if prompt is None:
53
52
prompt = messages[-1]["content"]
@@ -313,6 +313,7 @@ class Conversation(BaseConversation):
313
313
self.conversation_id = conversation_id
314
314
self.response_id = response_id
315
315
self.choice_id = choice_id
316
316
317
async def iter_filter_base64(response_iter: AsyncIterator[bytes]) -> AsyncIterator[bytes]:
317
318
search_for = b'[["wrb.fr","XqA3Ic","[\\"'
318
319
end_with = b'\\'
@@ -8,21 +8,29 @@ import os
8
8
import shutil
9
9
10
10
import os.path
11
from fastapi import FastAPI, Response, Request, UploadFile
11
from fastapi import FastAPI, Response, Request, UploadFile, Depends
12
from fastapi.middleware.wsgi import WSGIMiddleware
12
13
from fastapi.responses import StreamingResponse, RedirectResponse, HTMLResponse, JSONResponse
13
14
from fastapi.exceptions import RequestValidationError
14
15
from fastapi.security import APIKeyHeader
15
16
from starlette.exceptions import HTTPException
16
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY, HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
17
from starlette.status import (
18
HTTP_200_OK,
19
HTTP_422_UNPROCESSABLE_ENTITY,
20
HTTP_404_NOT_FOUND,
21
HTTP_401_UNAUTHORIZED,
22
HTTP_403_FORBIDDEN
23
)
17
24
from fastapi.encoders import jsonable_encoder
25
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
18
26
from fastapi.middleware.cors import CORSMiddleware
19
27
from starlette.responses import FileResponse
20
from pydantic import BaseModel
21
from typing import Union, Optional, List
28
from pydantic import BaseModel, Field
29
from typing import Union, Optional, List, Annotated
22
30
23
31
import g4f
24
32
import g4f.debug
25
from g4f.client import AsyncClient, ChatCompletion, convert_to_provider
33
from g4f.client import AsyncClient, ChatCompletion, ImagesResponse, convert_to_provider
26
34
from g4f.providers.response import BaseConversation
27
35
from g4f.client.helper import filter_none
28
36
from g4f.image import is_accepted_format, images_dir
@@ -30,6 +38,7 @@ from g4f.typing import Messages
30
38
from g4f.errors import ProviderNotFoundError
31
39
from g4f.cookies import read_cookie_files, get_cookies_dir
32
40
from g4f.Provider import ProviderType, ProviderUtils, __providers__
41
from g4f.gui import get_gui_app
33
42
34
43
logger = logging.getLogger(__name__)
35
44
@@ -50,6 +59,10 @@ def create_app(g4f_api_key: str = None):
50
59
api.register_authorization()
51
60
api.register_validation_exception_handler()
52
61
62
if AppConfig.gui:
63
gui_app = WSGIMiddleware(get_gui_app())
64
app.mount("/", gui_app)
65
53
66
# Read cookie files if not ignored
54
67
if not AppConfig.ignore_cookie_files:
55
68
read_cookie_files()
@@ -61,17 +74,17 @@ def create_app_debug(g4f_api_key: str = None):
61
74
return create_app(g4f_api_key)
62
75
63
76
class ChatCompletionsConfig(BaseModel):
64
messages: Messages
65
model: str
66
provider: Optional[str] = None
77
messages: Messages = Field(examples=[[{"role": "system", "content": ""}, {"role": "user", "content": ""}]])
78
model: str = Field(default="")
79
provider: Optional[str] = Field(examples=[None])
67
80
stream: bool = False
68
temperature: Optional[float] = None
69
max_tokens: Optional[int] = None
70
stop: Union[list[str], str, None] = None
71
api_key: Optional[str] = None
72
web_search: Optional[bool] = None
73
proxy: Optional[str] = None
74
conversation_id: str = None
81
temperature: Optional[float] = Field(examples=[None])
82
max_tokens: Optional[int] = Field(examples=[None])
83
stop: Union[list[str], str, None] = Field(examples=[None])
84
api_key: Optional[str] = Field(examples=[None])
85
web_search: Optional[bool] = Field(examples=[None])
86
proxy: Optional[str] = Field(examples=[None])
87
conversation_id: Optional[str] = Field(examples=[None])
75
88
76
89
class ImageGenerationConfig(BaseModel):
77
90
prompt: str
@@ -101,6 +114,9 @@ class ModelResponseModel(BaseModel):
101
114
created: int
102
115
owned_by: Optional[str]
103
116
117
class ErrorResponseModel(BaseModel):
118
error: str
119
104
120
class AppConfig:
105
121
ignored_providers: Optional[list[str]] = None
106
122
g4f_api_key: Optional[str] = None
@@ -109,6 +125,7 @@ class AppConfig:
109
125
provider: str = None
110
126
image_provider: str = None
111
127
proxy: str = None
128
gui: bool = False
112
129
113
130
@classmethod
114
131
def set_config(cls, **data):
@@ -129,6 +146,8 @@ class Api:
129
146
self.get_g4f_api_key = APIKeyHeader(name="g4f-api-key")
130
147
self.conversations: dict[str, dict[str, BaseConversation]] = {}
131
148
149
security = HTTPBearer(auto_error=False)
150
132
151
def register_authorization(self):
133
152
@self.app.middleware("http")
134
153
async def authorization(request: Request, call_next):
@@ -192,7 +211,7 @@ class Api:
192
211
} for model_id, model in model_list.items()]
193
212
194
213
@self.app.get("/v1/models/{model_name}")
195
async def model_info(model_name: str):
214
async def model_info(model_name: str) -> ModelResponseModel:
196
215
if model_name in g4f.models.ModelUtils.convert:
197
216
model_info = g4f.models.ModelUtils.convert[model_name]
198
217
return JSONResponse({
@@ -201,20 +220,20 @@ class Api:
201
220
'created': 0,
202
221
'owned_by': model_info.base_provider
203
222
})
204
return JSONResponse({"error": "The model does not exist."}, 404)
205
206
@self.app.post("/v1/chat/completions")
207
async def chat_completions(config: ChatCompletionsConfig, request: Request = None, provider: str = None):
223
return JSONResponse({"error": "The model does not exist."}, HTTP_404_NOT_FOUND)
224
225
@self.app.post("/v1/chat/completions", response_model=ChatCompletion)
226
async def chat_completions(
227
config: ChatCompletionsConfig,
228
credentials: Annotated[HTTPAuthorizationCredentials, Depends(Api.security)] = None,
229
provider: str = None
230
):
208
231
try:
209
232
config.provider = provider if config.provider is None else config.provider
210
233
if config.provider is None:
211
234
config.provider = AppConfig.provider
212
if config.api_key is None and request is not None:
213
auth_header = request.headers.get("Authorization")
214
if auth_header is not None:
215
api_key = auth_header.split(None, 1)[-1]
216
if api_key and api_key != "Bearer":
217
config.api_key = api_key
235
if credentials is not None:
236
config.api_key = credentials.credentials
218
237
219
238
conversation = return_conversation = None
220
239
if config.conversation_id is not None and config.provider is not None:
@@ -242,8 +261,7 @@ class Api:
242
261
)
243
262
244
263
if not config.stream:
245
response: ChatCompletion = await response
246
return JSONResponse(response.to_json())
264
return await response
247
265
248
266
async def streaming():
249
267
try:
@@ -254,7 +272,7 @@ class Api:
254
272
self.conversations[config.conversation_id] = {}
255
273
self.conversations[config.conversation_id][config.provider] = chunk
256
274
else:
257
yield f"data: {json.dumps(chunk.to_json())}\n\n"
275
yield f"data: {chunk.json()}\n\n"
258
276
except GeneratorExit:
259
277
pass
260
278
except Exception as e:
@@ -268,15 +286,15 @@ class Api:
268
286
logger.exception(e)
269
287
return Response(content=format_exception(e, config), status_code=500, media_type="application/json")
270
288
271
@self.app.post("/v1/images/generate")
272
@self.app.post("/v1/images/generations")
273
async def generate_image(config: ImageGenerationConfig, request: Request):
274
if config.api_key is None:
275
auth_header = request.headers.get("Authorization")
276
if auth_header is not None:
277
api_key = auth_header.split(None, 1)[-1]
278
if api_key and api_key != "Bearer":
279
config.api_key = api_key
289
@self.app.post("/v1/images/generate", response_model=ImagesResponse)
290
@self.app.post("/v1/images/generations", response_model=ImagesResponse)
291
async def generate_image(
292
request: Request,
293
config: ImageGenerationConfig,
294
credentials: Annotated[HTTPAuthorizationCredentials, Depends(Api.security)] = None
295
):
296
if credentials is not None:
297
config.api_key = credentials.credentials
280
298
try:
281
299
response = await self.client.images.generate(
282
300
prompt=config.prompt,
@@ -291,7 +309,7 @@ class Api:
291
309
for image in response.data:
292
310
if hasattr(image, "url") and image.url.startswith("/"):
293
311
image.url = f"{request.base_url}{image.url.lstrip('/')}"
294
return JSONResponse(response.to_json())
312
return response
295
313
except Exception as e:
296
314
logger.exception(e)
297
315
return Response(content=format_exception(e, config, True), status_code=500, media_type="application/json")
@@ -342,22 +360,29 @@ class Api:
342
360
file.file.close()
343
361
return response_data
344
362
345
@self.app.get("/v1/synthesize/{provider}")
363
@self.app.get("/v1/synthesize/{provider}", responses={
364
HTTP_200_OK: {"content": {"audio/*": {}}},
365
HTTP_404_NOT_FOUND: {"model": ErrorResponseModel},
366
HTTP_422_UNPROCESSABLE_ENTITY: {"model": ErrorResponseModel},
367
})
346
368
async def synthesize(request: Request, provider: str):
347
369
try:
348
370
provider_handler = convert_to_provider(provider)
349
371
except ProviderNotFoundError:
350
return Response("Provider not found", 404)
372
return JSONResponse({"error": "Provider not found"}, HTTP_404_NOT_FOUND)
351
373
if not hasattr(provider_handler, "synthesize"):
352
return Response("Provider doesn't support synthesize", 500)
374
return JSONResponse({"error": "Provider doesn't support synthesize"}, HTTP_404_NOT_FOUND)
353
375
if len(request.query_params) == 0:
354
return Response("Missing query params", 500)
376
return JSONResponse({"error": "Missing query params"}, HTTP_422_UNPROCESSABLE_ENTITY)
355
377
response_data = provider_handler.synthesize({**request.query_params})
356
378
content_type = getattr(provider_handler, "synthesize_content_type", "application/octet-stream")
357
379
return StreamingResponse(response_data, media_type=content_type)
358
380
359
@self.app.get("/images/{filename}")
360
async def get_image(filename) -> FileResponse:
381
@self.app.get("/images/{filename}", response_class=FileResponse, responses={
382
HTTP_200_OK: {"content": {"image/*": {}}},
383
HTTP_404_NOT_FOUND: {}
384
})
385
async def get_image(filename):
361
386
target = os.path.join(images_dir, filename)
362
387
363
388
if not os.path.isfile(target):
@@ -12,6 +12,7 @@ def main():
12
12
api_parser = subparsers.add_parser("api")
13
13
api_parser.add_argument("--bind", default="0.0.0.0:1337", help="The bind string.")
14
14
api_parser.add_argument("--debug", action="store_true", help="Enable verbose logging.")
15
api_parser.add_argument("--gui", "-g", default=False, action="store_true", help="Add gui to the api.")
15
16
api_parser.add_argument("--model", default=None, help="Default model for chat completion. (incompatible with --reload and --workers)")
16
17
api_parser.add_argument("--provider", choices=[provider.__name__ for provider in Provider.__providers__ if provider.working],
17
18
default=None, help="Default provider for chat completion. (incompatible with --reload and --workers)")
@@ -48,7 +49,8 @@ def run_api_args(args):
48
49
provider=args.provider,
49
50
image_provider=args.image_provider,
50
51
proxy=args.proxy,
51
model=args.model
52
model=args.model,
53
gui=args.gui,
52
54
)
53
55
g4f.cookies.browsers = [g4f.cookies[browser] for browser in args.cookie_browsers]
54
56
run_api(
@@ -73,7 +73,7 @@ def iter_response(
73
73
finish_reason = "stop"
74
74
75
75
if stream:
76
yield ChatCompletionChunk(chunk, None, completion_id, int(time.time()))
76
yield ChatCompletionChunk.model_construct(chunk, None, completion_id, int(time.time()))
77
77
78
78
if finish_reason is not None:
79
79
break
@@ -83,12 +83,12 @@ def iter_response(
83
83
finish_reason = "stop" if finish_reason is None else finish_reason
84
84
85
85
if stream:
86
yield ChatCompletionChunk(None, finish_reason, completion_id, int(time.time()))
86
yield ChatCompletionChunk.model_construct(None, finish_reason, completion_id, int(time.time()))
87
87
else:
88
88
if response_format is not None and "type" in response_format:
89
89
if response_format["type"] == "json_object":
90
90
content = filter_json(content)
91
yield ChatCompletion(content, finish_reason, completion_id, int(time.time()))
91
yield ChatCompletion.model_construct(content, finish_reason, completion_id, int(time.time()))
92
92
93
93
# Synchronous iter_append_model_and_provider function
94
94
def iter_append_model_and_provider(response: ChatCompletionResponseType) -> ChatCompletionResponseType:
@@ -137,7 +137,7 @@ async def async_iter_response(
137
137
finish_reason = "stop"
138
138
139
139
if stream:
140
yield ChatCompletionChunk(chunk, None, completion_id, int(time.time()))
140
yield ChatCompletionChunk.model_construct(chunk, None, completion_id, int(time.time()))
141
141
142
142
if finish_reason is not None:
143
143
break
@@ -145,12 +145,12 @@ async def async_iter_response(
145
145
finish_reason = "stop" if finish_reason is None else finish_reason
146
146
147
147
if stream:
148
yield ChatCompletionChunk(None, finish_reason, completion_id, int(time.time()))
148
yield ChatCompletionChunk.model_construct(None, finish_reason, completion_id, int(time.time()))
149
149
else:
150
150
if response_format is not None and "type" in response_format:
151
151
if response_format["type"] == "json_object":
152
152
content = filter_json(content)
153
yield ChatCompletion(content, finish_reason, completion_id, int(time.time()))
153
yield ChatCompletion.model_construct(content, finish_reason, completion_id, int(time.time()))
154
154
finally:
155
155
if hasattr(response, 'aclose'):
156
156
await safe_aclose(response)
@@ -394,13 +394,13 @@ class Images:
394
394
if response_format == "b64_json":
395
395
with open(os.path.join(images_dir, os.path.basename(image_file)), "rb") as file:
396
396
image_data = base64.b64encode(file.read()).decode()
397
return Image(url=image_file, b64_json=image_data, revised_prompt=response.alt)
398
return Image(url=image_file, revised_prompt=response.alt)
397
return Image.model_construct(url=image_file, b64_json=image_data, revised_prompt=response.alt)
398
return Image.model_construct(url=image_file, revised_prompt=response.alt)
399
399
images = await asyncio.gather(*[process_image_item(image) for image in images])
400
400
else:
401
images = [Image(url=image, revised_prompt=response.alt) for image in response.get_list()]
401
images = [Image.model_construct(url=image, revised_prompt=response.alt) for image in response.get_list()]
402
402
last_provider = get_last_provider(True)
403
return ImagesResponse(
403
return ImagesResponse.model_construct(
404
404
images,
405
405
model=last_provider.get("model") if model is None else model,
406
406
provider=last_provider.get("name") if provider is None else provider
@@ -1,130 +1,150 @@
1
1
from __future__ import annotations
2
2
3
from typing import Union
3
from typing import Optional, List, Dict
4
4
from time import time
5
5
6
class Model():
7
...
6
from .helper import filter_none
7
8
try:
9
from pydantic import BaseModel, Field
10
except ImportError:
11
class BaseModel():
12
@classmethod
13
def model_construct(cls, **data):
14
new = cls()
15
for key, value in data.items():
16
setattr(new, key, value)
17
return new
18
class Field():
19
def __init__(self, **config):
20
pass
21
22
class ChatCompletionChunk(BaseModel):
23
id: str
24
object: str
25
created: int
26
model: str
27
provider: Optional[str]
28
choices: List[ChatCompletionDeltaChoice]
8
29
9
class ChatCompletion(Model):
10
def __init__(
11
self,
30
@classmethod
31
def model_construct(
32
cls,
12
33
content: str,
13
34
finish_reason: str,
14
35
completion_id: str = None,
15
36
created: int = None
16
37
):
17
self.id: str = f"chatcmpl-{completion_id}" if completion_id else None
18
self.object: str = "chat.completion"
19
self.created: int = created
20
self.model: str = None
21
self.provider: str = None
22
self.choices = [ChatCompletionChoice(ChatCompletionMessage(content), finish_reason)]
23
self.usage: dict[str, int] = {
24
"prompt_tokens": 0, #prompt_tokens,
25
"completion_tokens": 0, #completion_tokens,
26
"total_tokens": 0, #prompt_tokens + completion_tokens,
27
}
28
29
def to_json(self):
30
return {
31
**self.__dict__,
32
"choices": [choice.to_json() for choice in self.choices]
33
}
34
35
class ChatCompletionChunk(Model):
36
def __init__(
37
self,
38
return super().model_construct(
39
id=f"chatcmpl-{completion_id}" if completion_id else None,
40
object="chat.completion.cunk",
41
created=created,
42
model=None,
43
provider=None,
44
choices=[ChatCompletionDeltaChoice.model_construct(
45
ChatCompletionDelta.model_construct(content),
46
finish_reason
47
)]
48
)
49
50
class ChatCompletionMessage(BaseModel):
51
role: str
52
content: str
53
54
@classmethod
55
def model_construct(cls, content: str):
56
return super().model_construct(role="assistant", content=content)
57
58
class ChatCompletionChoice(BaseModel):
59
index: int
60
message: ChatCompletionMessage
61
finish_reason: str
62
63
@classmethod
64
def model_construct(cls, message: ChatCompletionMessage, finish_reason: str):
65
return super().model_construct(index=0, message=message, finish_reason=finish_reason)
66
67
class ChatCompletion(BaseModel):
68
id: str
69
object: str
70
created: int
71
model: str
72
provider: Optional[str]
73
choices: List[ChatCompletionChoice]
74
usage: Dict[str, int] = Field(examples=[{
75
"prompt_tokens": 0, #prompt_tokens,
76
"completion_tokens": 0, #completion_tokens,
77
"total_tokens": 0, #prompt_tokens + completion_tokens,
78
}])
79
80
@classmethod
81
def model_construct(
82
cls,
38
83
content: str,
39
84
finish_reason: str,
40
85
completion_id: str = None,
41
86
created: int = None
42
87
):
43
self.id: str = f"chatcmpl-{completion_id}" if completion_id else None
44
self.object: str = "chat.completion.chunk"
45
self.created: int = created
46
self.model: str = None
47
self.provider: str = None
48
self.choices = [ChatCompletionDeltaChoice(ChatCompletionDelta(content), finish_reason)]
49
50
def to_json(self):
51
return {
52
**self.__dict__,
53
"choices": [choice.to_json() for choice in self.choices]
54
}
55
56
class ChatCompletionMessage(Model):
57
def __init__(self, content: Union[str, None]):
58
self.role = "assistant"
59
self.content = content
60
61
def to_json(self):
62
return self.__dict__
63
64
class ChatCompletionChoice(Model):
65
def __init__(self, message: ChatCompletionMessage, finish_reason: str):
66
self.index = 0
67
self.message = message
68
self.finish_reason = finish_reason
69
70
def to_json(self):
71
return {
72
**self.__dict__,
73
"message": self.message.to_json()
74
}
75
76
class ChatCompletionDelta(Model):
77
content: Union[str, None] = None
78
79
def __init__(self, content: Union[str, None]):
80
if content is not None:
81
self.content = content
82
self.role = "assistant"
83
84
def to_json(self):
85
return self.__dict__
86
87
class ChatCompletionDeltaChoice(Model):
88
def __init__(self, delta: ChatCompletionDelta, finish_reason: Union[str, None]):
89
self.index = 0
90
self.delta = delta
91
self.finish_reason = finish_reason
92
93
def to_json(self):
94
return {
95
**self.__dict__,
96
"delta": self.delta.to_json()
97
}
98
99
class Image(Model):
100
def __init__(self, url: str = None, b64_json: str = None, revised_prompt: str = None) -> None:
101
if url is not None:
102
self.url = url
103
if b64_json is not None:
104
self.b64_json = b64_json
105
if revised_prompt is not None:
106
self.revised_prompt = revised_prompt
107
108
def to_json(self):
109
return self.__dict__
110
111
class ImagesResponse(Model):
88
return super().model_construct(
89
id=f"chatcmpl-{completion_id}" if completion_id else None,
90
object="chat.completion",
91
created=created,
92
model=None,
93
provider=None,
94
choices=[ChatCompletionChoice.model_construct(
95
ChatCompletionMessage.model_construct(content),
96
finish_reason
97
)],
98
usage={
99
"prompt_tokens": 0, #prompt_tokens,
100
"completion_tokens": 0, #completion_tokens,
101
"total_tokens": 0, #prompt_tokens + completion_tokens,
102
}
103
)
104
105
class ChatCompletionDelta(BaseModel):
106
role: str
107
content: str
108
109
@classmethod
110
def model_construct(cls, content: Optional[str]):
111
return super().model_construct(role="assistant", content=content)
112
113
class ChatCompletionDeltaChoice(BaseModel):
114
index: int
115
delta: ChatCompletionDelta
116
finish_reason: Optional[str]
117
118
@classmethod
119
def model_construct(cls, delta: ChatCompletionDelta, finish_reason: Optional[str]):
120
return super().model_construct(index=0, delta=delta, finish_reason=finish_reason)
121
122
class Image(BaseModel):
123
url: Optional[str]
124
b64_json: Optional[str]
125
revised_prompt: Optional[str]
126
127
@classmethod
128
def model_construct(cls, url: str = None, b64_json: str = None, revised_prompt: str = None):
129
return super().model_construct(**filter_none(
130
url=url,
131
b64_json=b64_json,
132
revised_prompt=revised_prompt
133
))
134
135
class ImagesResponse(BaseModel):
112
136
data: list[Image]
113
137
model: str
114
138
provider: str
115
139
created: int
116
140
117
def __init__(self, data: list[Image], created: int = None, model: str = None, provider: str = None) -> None:
118
self.data = data
141
@classmethod
142
def model_construct(cls, data: list[Image], created: int = None, model: str = None, provider: str = None):
119
143
if created is None:
120
144
created = int(time())
121
self.model = model
122
if provider is not None:
123
self.provider = provider
124
self.created = created
125
126
def to_json(self):
127
return {
128
**self.__dict__,
129
"data": [image.to_json() for image in self.data]
130
}
145
return super().model_construct(
146
data=data,
147
model=model,
148
provider=provider,
149
created=created
150
)
@@ -8,22 +8,13 @@ try:
8
8
except ImportError as e:
9
9
import_error = e
10
10
11
def run_gui(host: str = '0.0.0.0', port: int = 8080, debug: bool = False) -> None:
12
if import_error is not None:
13
raise MissingRequirementsError(f'Install "gui" requirements | pip install -U g4f[gui]\n{import_error}')
14
15
config = {
16
'host' : host,
17
'port' : port,
18
'debug': debug
19
}
20
11
def get_gui_app():
21
12
site = Website(app)
22
13
for route in site.routes:
23
14
app.add_url_rule(
24
15
route,
25
view_func = site.routes[route]['function'],
26
methods = site.routes[route]['methods'],
16
view_func=site.routes[route]['function'],
17
methods=site.routes[route]['methods'],
27
18
)
28
19
29
20
backend_api = Backend_Api(app)
@@ -33,6 +24,19 @@ def run_gui(host: str = '0.0.0.0', port: int = 8080, debug: bool = False) -> Non
33
24
view_func = backend_api.routes[route]['function'],
34
25
methods = backend_api.routes[route]['methods'],
35
26
)
27
return app
28
29
def run_gui(host: str = '0.0.0.0', port: int = 8080, debug: bool = False) -> None:
30
if import_error is not None:
31
raise MissingRequirementsError(f'Install "gui" requirements | pip install -U g4f[gui]\n{import_error}')
32
33
config = {
34
'host' : host,
35
'port' : port,
36
'debug': debug
37
}
38
39
get_gui_app()
36
40
37
41
print(f"Running on port {config['port']}")
38
42
app.run(**config)
@@ -22,11 +22,11 @@ conversations: dict[dict[str, BaseConversation]] = {}
22
22
23
23
class Api:
24
24
@staticmethod
25
def get_models() -> list[str]:
25
def get_models():
26
26
return models._all_models
27
27
28
28
@staticmethod
29
def get_provider_models(provider: str, api_key: str = None) -> list[dict]:
29
def get_provider_models(provider: str, api_key: str = None):
30
30
if provider in __map__:
31
31
provider: ProviderType = __map__[provider]
32
32
if issubclass(provider, ProviderModelMixin):
@@ -46,39 +46,7 @@ class Api:
46
46
return []
47
47
48
48
@staticmethod
49
def get_image_models() -> list[dict]:
50
image_models = []
51
index = []
52
for provider in __providers__:
53
if hasattr(provider, "image_models"):
54
if hasattr(provider, "get_models"):
55
provider.get_models()
56
parent = provider
57
if hasattr(provider, "parent"):
58
parent = __map__[provider.parent]
59
if parent.__name__ not in index:
60
for model in provider.image_models:
61
image_models.append({
62
"provider": parent.__name__,
63
"url": parent.url,
64
"label": parent.label if hasattr(parent, "label") else None,
65
"image_model": model,
66
"vision_model": getattr(parent, "default_vision_model", None)
67
})
68
index.append(parent.__name__)
69
elif hasattr(provider, "default_vision_model") and provider.__name__ not in index:
70
image_models.append({
71
"provider": provider.__name__,
72
"url": provider.url,
73
"label": provider.label if hasattr(provider, "label") else None,
74
"image_model": None,
75
"vision_model": provider.default_vision_model
76
})
77
index.append(provider.__name__)
78
return image_models
79
80
@staticmethod
81
def get_providers() -> list[str]:
49
def get_providers() -> dict[str, str]:
82
50
return {
83
51
provider.__name__: (provider.label if hasattr(provider, "label") else provider.__name__)
84
52
+ (" (Image Generation)" if getattr(provider, "image_models", None) else "")
@@ -90,7 +58,7 @@ class Api:
90
58
}
91
59
92
60
@staticmethod
93
def get_version():
61
def get_version() -> dict:
94
62
try:
95
63
current_version = version.utils.current_version
96
64
except VersionNotFoundError:
@@ -3,7 +3,7 @@ import flask
3
3
import os
4
4
import logging
5
5
import asyncio
6
from flask import request, Flask
6
from flask import Flask, request, jsonify
7
7
from typing import Generator
8
8
from werkzeug.utils import secure_filename
9
9
@@ -42,17 +42,26 @@ class Backend_Api(Api):
42
42
app (Flask): Flask application instance to attach routes to.
43
43
"""
44
44
self.app: Flask = app
45
46
def jsonify_models(**kwargs):
47
response = self.get_models(**kwargs)
48
if isinstance(response, list):
49
return jsonify(response)
50
return response
51
52
def jsonify_provider_models(**kwargs):
53
response = self.get_provider_models(**kwargs)
54
if isinstance(response, list):
55
return jsonify(response)
56
return response
57
45
58
self.routes = {
46
59
'/backend-api/v2/models': {
47
'function': self.get_models,
60
'function': jsonify_models,
48
61
'methods': ['GET']
49
62
},
50
63
'/backend-api/v2/models/<provider>': {
51
'function': self.get_provider_models,
52
'methods': ['GET']
53
},
54
'/backend-api/v2/image_models': {
55
'function': self.get_image_models,
64
'function': jsonify_provider_models,
56
65
'methods': ['GET']
57
66
},
58
67
'/backend-api/v2/providers': {
@@ -1,11 +1,12 @@
1
1
import uuid
2
2
from flask import render_template, redirect
3
3
4
def redirect_home():
5
return redirect('/chat')
6
4
7
class Website:
5
8
def __init__(self, app) -> None:
6
9
self.app = app
7
def redirect_home():
8
return redirect('/chat')
9
10
self.routes = {
10
11
'/': {
11
12
'function': redirect_home,
@@ -35,7 +36,7 @@ class Website:
35
36
36
37
def _chat(self, conversation_id):
37
38
if '-' not in conversation_id:
38
return redirect('/chat')
39
return redirect_home()
39
40
return render_template('index.html', chat_id=conversation_id)
40
41
41
42
def _index(self):
@@ -11,7 +11,7 @@ class CloudflareError(ResponseStatusError):
11
11
...
12
12
13
13
def is_cloudflare(text: str) -> bool:
14
if "Generated by cloudfront" in text:
14
if "Generated by cloudfront" in text or '<p id="cf-spinner-please-wait">' in text:
15
15
return True
16
16
elif "<title>Attention Required! | Cloudflare</title>" in text or 'id="cf-cloudflare-status"' in text:
17
17
return True