summaryrefslogtreecommitdiffstats
path: root/g4f
diff options
context:
space:
mode:
authorTekky <98614666+xtekky@users.noreply.github.com>2023-09-26 15:06:06 +0200
committerGitHub <noreply@github.com>2023-09-26 15:06:06 +0200
commit41af8aff6ce09be4131d50e0e24b8622628441fc (patch)
tree1519231f3513b57acf2548b77f1fc89e092fc4cb /g4f
parent~ (diff)
parentAdd ChatgptDuo and Aibn Provider (diff)
downloadgpt4free-41af8aff6ce09be4131d50e0e24b8622628441fc.tar
gpt4free-41af8aff6ce09be4131d50e0e24b8622628441fc.tar.gz
gpt4free-41af8aff6ce09be4131d50e0e24b8622628441fc.tar.bz2
gpt4free-41af8aff6ce09be4131d50e0e24b8622628441fc.tar.lz
gpt4free-41af8aff6ce09be4131d50e0e24b8622628441fc.tar.xz
gpt4free-41af8aff6ce09be4131d50e0e24b8622628441fc.tar.zst
gpt4free-41af8aff6ce09be4131d50e0e24b8622628441fc.zip
Diffstat (limited to 'g4f')
-rw-r--r--g4f/Provider/Aibn.py51
-rw-r--r--g4f/Provider/ChatgptDuo.py51
-rw-r--r--g4f/Provider/PerplexityAi.py2
-rw-r--r--g4f/Provider/Vercel.py2
-rw-r--r--g4f/Provider/__init__.py4
-rw-r--r--g4f/Provider/base_provider.py102
-rw-r--r--g4f/Provider/helper.py54
-rw-r--r--g4f/__init__.py5
-rw-r--r--g4f/models.py9
-rw-r--r--g4f/requests.py40
10 files changed, 226 insertions, 94 deletions
diff --git a/g4f/Provider/Aibn.py b/g4f/Provider/Aibn.py
new file mode 100644
index 00000000..1ef928be
--- /dev/null
+++ b/g4f/Provider/Aibn.py
@@ -0,0 +1,51 @@
+from __future__ import annotations
+
+import time
+import hashlib
+
+from ..typing import AsyncGenerator
+from g4f.requests import AsyncSession
+from .base_provider import AsyncGeneratorProvider
+
+
+class Aibn(AsyncGeneratorProvider):
+ url = "https://aibn.cc"
+ supports_gpt_35_turbo = True
+ working = True
+
+ @classmethod
+ async def create_async_generator(
+ cls,
+ model: str,
+ messages: list[dict[str, str]],
+ **kwargs
+ ) -> AsyncGenerator:
+ async with AsyncSession(impersonate="chrome107") as session:
+ timestamp = int(time.time())
+ data = {
+ "messages": messages,
+ "pass": None,
+ "sign": generate_signature(timestamp, messages[-1]["content"]),
+ "time": timestamp
+ }
+ async with session.post(f"{cls.url}/api/generate", json=data) as response:
+ response.raise_for_status()
+ async for chunk in response.content.iter_any():
+ yield chunk.decode()
+
+ @classmethod
+ @property
+ def params(cls):
+ params = [
+ ("model", "str"),
+ ("messages", "list[dict[str, str]]"),
+ ("stream", "bool"),
+ ("temperature", "float"),
+ ]
+ param = ", ".join([": ".join(p) for p in params])
+ return f"g4f.provider.{cls.__name__} supports: ({param})"
+
+
+def generate_signature(timestamp: int, message: str, secret: str = "undefined"):
+ data = f"{timestamp}:{message}:{secret}"
+ return hashlib.sha256(data.encode()).hexdigest() \ No newline at end of file
diff --git a/g4f/Provider/ChatgptDuo.py b/g4f/Provider/ChatgptDuo.py
new file mode 100644
index 00000000..07f4c16c
--- /dev/null
+++ b/g4f/Provider/ChatgptDuo.py
@@ -0,0 +1,51 @@
+from __future__ import annotations
+
+from g4f.requests import AsyncSession
+from .base_provider import AsyncProvider, format_prompt
+
+
+class ChatgptDuo(AsyncProvider):
+ url = "https://chatgptduo.com"
+ supports_gpt_35_turbo = True
+ working = True
+
+ @classmethod
+ async def create_async(
+ cls,
+ model: str,
+ messages: list[dict[str, str]],
+ **kwargs
+ ) -> str:
+ async with AsyncSession(impersonate="chrome107") as session:
+ prompt = format_prompt(messages),
+ data = {
+ "prompt": prompt,
+ "search": prompt,
+ "purpose": "ask",
+ }
+ async with session.post(f"{cls.url}/", data=data) as response:
+ response.raise_for_status()
+ data = await response.json()
+
+ cls._sources = [{
+ "title": source["title"],
+ "url": source["link"],
+ "snippet": source["snippet"]
+ } for source in data["results"]]
+
+ return data["answer"]
+
+ @classmethod
+ def get_sources(cls):
+ return cls._sources
+
+ @classmethod
+ @property
+ def params(cls):
+ params = [
+ ("model", "str"),
+ ("messages", "list[dict[str, str]]"),
+ ("stream", "bool"),
+ ]
+ param = ", ".join([": ".join(p) for p in params])
+ return f"g4f.provider.{cls.__name__} supports: ({param})" \ No newline at end of file
diff --git a/g4f/Provider/PerplexityAi.py b/g4f/Provider/PerplexityAi.py
index 269cdafd..fc0fd48c 100644
--- a/g4f/Provider/PerplexityAi.py
+++ b/g4f/Provider/PerplexityAi.py
@@ -58,7 +58,7 @@ class PerplexityAi(AsyncProvider):
result = json.loads(json.loads(line[3:])[0]["text"])
cls._sources = [{
- "name": source["name"],
+ "title": source["name"],
"url": source["url"],
"snippet": source["snippet"]
} for source in result["web_results"]]
diff --git a/g4f/Provider/Vercel.py b/g4f/Provider/Vercel.py
index 4102c07b..2d20ca6a 100644
--- a/g4f/Provider/Vercel.py
+++ b/g4f/Provider/Vercel.py
@@ -62,7 +62,7 @@ class Vercel(BaseProvider):
response.raise_for_status()
except:
continue
- for token in response.iter_content(chunk_size=8):
+ for token in response.iter_content(chunk_size=None):
yield token.decode()
break
diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py
index ebe01603..59c91dd5 100644
--- a/g4f/Provider/__init__.py
+++ b/g4f/Provider/__init__.py
@@ -1,5 +1,6 @@
from __future__ import annotations
from .Acytoo import Acytoo
+from .Aibn import Aibn
from .Aichat import Aichat
from .Ails import Ails
from .AiService import AiService
@@ -10,6 +11,7 @@ from .Bard import Bard
from .Bing import Bing
from .ChatBase import ChatBase
from .ChatgptAi import ChatgptAi
+from .ChatgptDuo import ChatgptDuo
from .ChatgptLogin import ChatgptLogin
from .CodeLinkAva import CodeLinkAva
from .DeepAi import DeepAi
@@ -49,6 +51,7 @@ __all__ = [
'AsyncGeneratorProvider',
'RetryProvider',
'Acytoo',
+ 'Aibn',
'Aichat',
'Ails',
'AiService',
@@ -59,6 +62,7 @@ __all__ = [
'Bing',
'ChatBase',
'ChatgptAi',
+ 'ChatgptDuo',
'ChatgptLogin',
'CodeLinkAva',
'DeepAi',
diff --git a/g4f/Provider/base_provider.py b/g4f/Provider/base_provider.py
index e8a54f78..a21dc871 100644
--- a/g4f/Provider/base_provider.py
+++ b/g4f/Provider/base_provider.py
@@ -1,13 +1,10 @@
from __future__ import annotations
-import asyncio
-import functools
-from asyncio import SelectorEventLoop, AbstractEventLoop
+from asyncio import AbstractEventLoop
from concurrent.futures import ThreadPoolExecutor
from abc import ABC, abstractmethod
-import browser_cookie3
-
+from .helper import get_event_loop, get_cookies, format_prompt
from ..typing import AsyncGenerator, CreateResult
@@ -40,20 +37,18 @@ class BaseProvider(ABC):
**kwargs
) -> str:
if not loop:
- loop = asyncio.get_event_loop()
-
- partial_func = functools.partial(
- cls.create_completion,
- model,
- messages,
- False,
- **kwargs
- )
- response = await loop.run_in_executor(
+ loop = get_event_loop()
+ def create_func():
+ return "".join(cls.create_completion(
+ model,
+ messages,
+ False,
+ **kwargs
+ ))
+ return await loop.run_in_executor(
executor,
- partial_func
+ create_func
)
- return "".join(response)
@classmethod
@property
@@ -76,11 +71,9 @@ class AsyncProvider(BaseProvider):
stream: bool = False,
**kwargs
) -> CreateResult:
- loop = create_event_loop()
- try:
- yield loop.run_until_complete(cls.create_async(model, messages, **kwargs))
- finally:
- loop.close()
+ loop = get_event_loop()
+ coro = cls.create_async(model, messages, **kwargs)
+ yield loop.run_until_complete(coro)
@staticmethod
@abstractmethod
@@ -103,22 +96,19 @@ class AsyncGeneratorProvider(AsyncProvider):
stream: bool = True,
**kwargs
) -> CreateResult:
- loop = create_event_loop()
- try:
- generator = cls.create_async_generator(
- model,
- messages,
- stream=stream,
- **kwargs
- )
- gen = generator.__aiter__()
- while True:
- try:
- yield loop.run_until_complete(gen.__anext__())
- except StopAsyncIteration:
- break
- finally:
- loop.close()
+ loop = get_event_loop()
+ generator = cls.create_async_generator(
+ model,
+ messages,
+ stream=stream,
+ **kwargs
+ )
+ gen = generator.__aiter__()
+ while True:
+ try:
+ yield loop.run_until_complete(gen.__anext__())
+ except StopAsyncIteration:
+ break
@classmethod
async def create_async(
@@ -143,38 +133,4 @@ class AsyncGeneratorProvider(AsyncProvider):
messages: list[dict[str, str]],
**kwargs
) -> AsyncGenerator:
- raise NotImplementedError()
-
-
-# Don't create a new event loop in a running async loop.
-# Force use selector event loop on windows and linux use it anyway.
-def create_event_loop() -> SelectorEventLoop:
- try:
- asyncio.get_running_loop()
- except RuntimeError:
- return SelectorEventLoop()
- raise RuntimeError(
- 'Use "create_async" instead of "create" function in a running event loop.')
-
-
-_cookies = {}
-
-def get_cookies(cookie_domain: str) -> dict:
- if cookie_domain not in _cookies:
- _cookies[cookie_domain] = {}
- try:
- for cookie in browser_cookie3.load(cookie_domain):
- _cookies[cookie_domain][cookie.name] = cookie.value
- except:
- pass
- return _cookies[cookie_domain]
-
-
-def format_prompt(messages: list[dict[str, str]], add_special_tokens=False):
- if add_special_tokens or len(messages) > 1:
- formatted = "\n".join(
- ["%s: %s" % ((message["role"]).capitalize(), message["content"]) for message in messages]
- )
- return f"{formatted}\nAssistant:"
- else:
- return messages[0]["content"] \ No newline at end of file
+ raise NotImplementedError() \ No newline at end of file
diff --git a/g4f/Provider/helper.py b/g4f/Provider/helper.py
new file mode 100644
index 00000000..e14ae65e
--- /dev/null
+++ b/g4f/Provider/helper.py
@@ -0,0 +1,54 @@
+from __future__ import annotations
+
+import asyncio
+import sys
+from asyncio import AbstractEventLoop
+
+import browser_cookie3
+
+_cookies: dict[str, dict[str, str]] = {}
+
+# Use own event_loop_policy with a selector event loop on windows.
+if sys.platform == 'win32':
+ _event_loop_policy = asyncio.WindowsSelectorEventLoopPolicy()
+else:
+ _event_loop_policy = asyncio.get_event_loop_policy()
+
+# If event loop is already running, handle nested event loops
+# If "nest_asyncio" is installed, patch the event loop.
+def get_event_loop() -> AbstractEventLoop:
+ try:
+ asyncio.get_running_loop()
+ except RuntimeError:
+ return _event_loop_policy.get_event_loop()
+ try:
+ event_loop = _event_loop_policy.get_event_loop()
+ if not hasattr(event_loop.__class__, "_nest_patched"):
+ import nest_asyncio
+ nest_asyncio.apply(event_loop)
+ return event_loop
+ except ImportError:
+ raise RuntimeError(
+ 'Use "create_async" instead of "create" function in a running event loop. Or install the "nest_asyncio" package.')
+
+# Load cookies for a domain from all supported browser.
+# Cache the results in the "_cookies" variable
+def get_cookies(cookie_domain: str) -> dict:
+ if cookie_domain not in _cookies:
+ _cookies[cookie_domain] = {}
+ try:
+ for cookie in browser_cookie3.load(cookie_domain):
+ _cookies[cookie_domain][cookie.name] = cookie.value
+ except:
+ pass
+ return _cookies[cookie_domain]
+
+
+def format_prompt(messages: list[dict[str, str]], add_special_tokens=False):
+ if add_special_tokens or len(messages) > 1:
+ formatted = "\n".join(
+ ["%s: %s" % ((message["role"]).capitalize(), message["content"]) for message in messages]
+ )
+ return f"{formatted}\nAssistant:"
+ else:
+ return messages[0]["content"] \ No newline at end of file
diff --git a/g4f/__init__.py b/g4f/__init__.py
index d6388fb5..b27ad820 100644
--- a/g4f/__init__.py
+++ b/g4f/__init__.py
@@ -61,13 +61,8 @@ class ChatCompletion:
provider : Union[type[BaseProvider], None] = None,
**kwargs
) -> str:
-
model, provider = get_model_and_provider(model, provider, False)
- provider_type = provider if isinstance(provider, type) else type(provider)
- if not issubclass(provider_type, AsyncProvider):
- raise Exception(f"Provider: {provider.__name__} doesn't support create_async")
-
return await provider.create_async(model.name, messages, **kwargs)
class Completion:
diff --git a/g4f/models.py b/g4f/models.py
index 23fc8e65..7c2d6822 100644
--- a/g4f/models.py
+++ b/g4f/models.py
@@ -20,6 +20,8 @@ from .Provider import (
AItianhuSpace,
Aichat,
Myshell,
+ Aibn,
+ ChatgptDuo,
)
@dataclass(unsafe_hash=True)
@@ -39,7 +41,8 @@ default = Model(
Wewordle, # Responds with markdown
Yqcloud, # Answers short questions in chinese
ChatBase, # Don't want to answer creatively
- DeepAi, ChatgptLogin, ChatgptAi, Aivvm, GptGo, AItianhu, AItianhuSpace, Aichat, Myshell,
+ ChatgptDuo, # Include search results
+ DeepAi, ChatgptLogin, ChatgptAi, Aivvm, GptGo, AItianhu, AItianhuSpace, Aichat, Myshell, Aibn,
])
)
@@ -48,7 +51,7 @@ gpt_35_turbo = Model(
name = 'gpt-3.5-turbo',
base_provider = 'openai',
best_provider = RetryProvider([
- DeepAi, ChatgptLogin, ChatgptAi, Aivvm, GptGo, AItianhu, Aichat, AItianhuSpace, Myshell,
+ DeepAi, ChatgptLogin, ChatgptAi, Aivvm, GptGo, AItianhu, Aichat, AItianhuSpace, Myshell, Aibn,
])
)
@@ -56,7 +59,7 @@ gpt_4 = Model(
name = 'gpt-4',
base_provider = 'openai',
best_provider = RetryProvider([
- Aivvm, Myshell, AItianhuSpace,
+ Myshell, AItianhuSpace,
])
)
diff --git a/g4f/requests.py b/g4f/requests.py
index 1a0c612c..51d31e1e 100644
--- a/g4f/requests.py
+++ b/g4f/requests.py
@@ -1,12 +1,13 @@
from __future__ import annotations
import json, sys
+from functools import partialmethod
+
from aiohttp import StreamReader
from aiohttp.base_protocol import BaseProtocol
-from curl_cffi.requests import AsyncSession
-from curl_cffi.requests.cookies import Request
-from curl_cffi.requests.cookies import Response
+from curl_cffi.requests import AsyncSession as BaseSession
+from curl_cffi.requests.cookies import Request, Response
class StreamResponse:
@@ -17,6 +18,8 @@ class StreamResponse:
self.status_code = inner.status_code
self.reason = inner.reason
self.ok = inner.ok
+ self.headers = inner.headers
+ self.cookies = inner.cookies
async def text(self) -> str:
content = await self.content.read()
@@ -29,7 +32,6 @@ class StreamResponse:
async def json(self, **kwargs):
return json.loads(await self.content.read(), **kwargs)
-
class StreamRequest:
def __init__(self, session: AsyncSession, method: str, url: str, **kwargs):
self.session = session
@@ -50,10 +52,13 @@ class StreamRequest:
def on_done(self, task):
self.content.feed_eof()
+ self.curl.clean_after_perform()
+ self.curl.reset()
+ self.session.push_curl(self.curl)
async def __aenter__(self) -> StreamResponse:
self.curl = await self.session.pop_curl()
- self.enter = self.session.loop.create_future()
+ self.enter = self.loop.create_future()
request, _, header_buffer = self.session._set_curl_options(
self.curl,
self.method,
@@ -61,8 +66,8 @@ class StreamRequest:
content_callback=self.on_content,
**self.options
)
- handle = self.session.acurl.add_handle(self.curl)
- self.handle = self.session.loop.create_task(handle)
+ await self.session.acurl.add_handle(self.curl, False)
+ self.handle = self.session.acurl._curl2future[self.curl]
self.handle.add_done_callback(self.on_done)
await self.enter
return StreamResponse(
@@ -72,7 +77,20 @@ class StreamRequest:
)
async def __aexit__(self, exc_type, exc, tb):
- await self.handle
- self.curl.clean_after_perform()
- self.curl.reset()
- self.session.push_curl(self.curl) \ No newline at end of file
+ pass
+
+class AsyncSession(BaseSession):
+ def request(
+ self,
+ method: str,
+ url: str,
+ **kwargs
+ ) -> StreamRequest:
+ return StreamRequest(self, method, url, **kwargs)
+
+ head = partialmethod(request, "HEAD")
+ get = partialmethod(request, "GET")
+ post = partialmethod(request, "POST")
+ put = partialmethod(request, "PUT")
+ patch = partialmethod(request, "PATCH")
+ delete = partialmethod(request, "DELETE") \ No newline at end of file