返回提交历史
Modified
g4f/Provider/Bing.py
+5
-2
Modified
g4f/Provider/DeepInfra.py
+1
-16
Added
g4f/Provider/Ollama.py
+33
-0
Modified
g4f/Provider/__init__.py
+1
-0
Modified
g4f/Provider/needs_auth/Openai.py
+17
-4
Modified
g4f/api/__init__.py
+1
-1
Modified
g4f/cli.py
+13
-8
Modified
g4f/requests/__init__.py
+1
-1
XFEstudio/gpt4free
Add Ollama provider, Add vision support to Openai
8fcf618b
代码差异
8 个文件
+72
-32
@@ -457,10 +457,13 @@ async def stream_generate(
457
457
returned_text = ''
458
458
message_id = None
459
459
while do_read:
460
msg = await wss.receive_str()
460
try:
461
msg = await wss.receive_str()
462
except TypeError:
463
continue
461
464
objects = msg.split(Defaults.delimiter)
462
465
for obj in objects:
463
if obj is None or not obj:
466
if not obj:
464
467
continue
465
468
try:
466
469
response = json.loads(obj)
@@ -1,8 +1,7 @@
1
1
from __future__ import annotations
2
2
3
3
import requests
4
from ..typing import AsyncResult, Messages, ImageType
5
from ..image import to_data_uri
4
from ..typing import AsyncResult, Messages
6
5
from .needs_auth.Openai import Openai
7
6
8
7
class DeepInfra(Openai):
@@ -33,7 +32,6 @@ class DeepInfra(Openai):
33
32
model: str,
34
33
messages: Messages,
35
34
stream: bool,
36
image: ImageType = None,
37
35
api_base: str = "https://api.deepinfra.com/v1/openai",
38
36
temperature: float = 0.7,
39
37
max_tokens: int = 1028,
@@ -54,19 +52,6 @@ class DeepInfra(Openai):
54
52
'sec-ch-ua-mobile': '?0',
55
53
'sec-ch-ua-platform': '"macOS"',
56
54
}
57
if image is not None:
58
if not model:
59
model = cls.default_vision_model
60
messages[-1]["content"] = [
61
{
62
"type": "image_url",
63
"image_url": {"url": to_data_uri(image)}
64
},
65
{
66
"type": "text",
67
"text": messages[-1]["content"]
68
}
69
]
70
55
return super().create_async_generator(
71
56
model, messages,
72
57
stream=stream,
@@ -0,0 +1,33 @@
1
from __future__ import annotations
2
3
import requests
4
5
from .needs_auth.Openai import Openai
6
from ..typing import AsyncResult, Messages
7
8
class Ollama(Openai):
9
label = "Ollama"
10
url = "https://ollama.com"
11
needs_auth = False
12
working = True
13
14
@classmethod
15
def get_models(cls):
16
if not cls.models:
17
url = 'http://127.0.0.1:11434/api/tags'
18
models = requests.get(url).json()["models"]
19
cls.models = [model['name'] for model in models]
20
cls.default_model = cls.models[0]
21
return cls.models
22
23
@classmethod
24
def create_async_generator(
25
cls,
26
model: str,
27
messages: Messages,
28
api_base: str = "http://localhost:11434/v1",
29
**kwargs
30
) -> AsyncResult:
31
return super().create_async_generator(
32
model, messages, api_base=api_base, **kwargs
33
)
@@ -43,6 +43,7 @@ from .Llama import Llama
43
43
from .Local import Local
44
44
from .MetaAI import MetaAI
45
45
from .MetaAIAccount import MetaAIAccount
46
from .Ollama import Ollama
46
47
from .PerplexityLabs import PerplexityLabs
47
48
from .Pi import Pi
48
49
from .Replicate import Replicate
@@ -4,9 +4,10 @@ import json
4
4
5
5
from ..helper import filter_none
6
6
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, FinishReason
7
from ...typing import Union, Optional, AsyncResult, Messages
7
from ...typing import Union, Optional, AsyncResult, Messages, ImageType
8
8
from ...requests import StreamSession, raise_for_status
9
9
from ...errors import MissingAuthError, ResponseError
10
from ...image import to_data_uri
10
11
11
12
class Openai(AsyncGeneratorProvider, ProviderModelMixin):
12
13
label = "OpenAI API"
@@ -23,6 +24,7 @@ class Openai(AsyncGeneratorProvider, ProviderModelMixin):
23
24
messages: Messages,
24
25
proxy: str = None,
25
26
timeout: int = 120,
27
image: ImageType = None,
26
28
api_key: str = None,
27
29
api_base: str = "https://api.openai.com/v1",
28
30
temperature: float = None,
@@ -36,6 +38,19 @@ class Openai(AsyncGeneratorProvider, ProviderModelMixin):
36
38
) -> AsyncResult:
37
39
if cls.needs_auth and api_key is None:
38
40
raise MissingAuthError('Add a "api_key"')
41
if image is not None:
42
if not model and hasattr(cls, "default_vision_model"):
43
model = cls.default_vision_model
44
messages[-1]["content"] = [
45
{
46
"type": "image_url",
47
"image_url": {"url": to_data_uri(image)}
48
},
49
{
50
"type": "text",
51
"text": messages[-1]["content"]
52
}
53
]
39
54
async with StreamSession(
40
55
proxies={"all": proxy},
41
56
headers=cls.get_headers(stream, api_key, headers),
@@ -51,7 +66,6 @@ class Openai(AsyncGeneratorProvider, ProviderModelMixin):
51
66
stream=stream,
52
67
**extra_data
53
68
)
54
55
69
async with session.post(f"{api_base.rstrip('/')}/chat/completions", json=data) as response:
56
70
await raise_for_status(response)
57
71
if not stream:
@@ -103,8 +117,7 @@ class Openai(AsyncGeneratorProvider, ProviderModelMixin):
103
117
"Content-Type": "application/json",
104
118
**(
105
119
{"Authorization": f"Bearer {api_key}"}
106
if cls.needs_auth and api_key is not None
107
else {}
120
if api_key is not None else {}
108
121
),
109
122
**({} if headers is None else headers)
110
123
}
@@ -201,7 +201,7 @@ def run_api(
201
201
if bind is not None:
202
202
host, port = bind.split(":")
203
203
uvicorn.run(
204
f"g4f.api:{'create_app_debug' if debug else 'create_app'}",
204
f"g4f.api:create_app{'_debug' if debug else ''}",
205
205
host=host, port=int(port),
206
206
workers=workers,
207
207
use_colors=use_colors,
@@ -11,6 +11,10 @@ def main():
11
11
api_parser = subparsers.add_parser("api")
12
12
api_parser.add_argument("--bind", default="0.0.0.0:1337", help="The bind string.")
13
13
api_parser.add_argument("--debug", action="store_true", help="Enable verbose logging.")
14
api_parser.add_argument("--model", default=None, help="Default model for chat completion. (incompatible with --debug and --workers)")
15
api_parser.add_argument("--provider", choices=[provider.__name__ for provider in Provider.__providers__ if provider.working],
16
default=None, help="Default provider for chat completion. (incompatible with --debug and --workers)")
17
api_parser.add_argument("--proxy", default=None, help="Default used proxy.")
14
18
api_parser.add_argument("--workers", type=int, default=None, help="Number of workers.")
15
19
api_parser.add_argument("--disable-colors", action="store_true", help="Don't use colors.")
16
20
api_parser.add_argument("--ignore-cookie-files", action="store_true", help="Don't read .har and cookie files.")
@@ -31,14 +35,15 @@ def main():
31
35
def run_api_args(args):
32
36
from g4f.api import AppConfig, run_api
33
37
34
AppConfig.set_ignore_cookie_files(
35
args.ignore_cookie_files
36
)
37
AppConfig.set_list_ignored_providers(
38
args.ignored_providers
39
)
40
AppConfig.set_g4f_api_key(
41
args.g4f_api_key
38
AppConfig.set_config(
39
ignore_cookie_files=args.ignore_cookie_files,
40
ignored_providers=args.ignored_providers,
41
g4f_api_key=args.g4f_api_key,
42
defaults={
43
"model": args.model,
44
"provider": args.provider,
45
"proxy": args.proxy
46
}
42
47
)
43
48
run_api(
44
49
bind=args.bind,
@@ -40,7 +40,7 @@ async def get_args_from_webview(url: str) -> dict:
40
40
"Referer": window.real_url
41
41
}
42
42
cookies = [list(*cookie.items()) for cookie in window.get_cookies()]
43
cookies = dict([(name, cookie.value) for name, cookie in cookies])
43
cookies = {name: cookie.value for name, cookie in cookies}
44
44
window.destroy()
45
45
return {"headers": headers, "cookies": cookies}
46
46