mirror of
https://github.com/xtekky/gpt4free.git
synced 2024-11-26 09:57:24 +03:00
Add new Client API with Docs
Use object urls for the preview of image uploads. Fix upload images in You provider Fix create image. It's now a single image. Improve system message for create images.
This commit is contained in:
parent
9aeae65b9b
commit
aba4b96f23
2
.github/workflows/unittest.yml
vendored
2
.github/workflows/unittest.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
run: pip install -r requirements-min.txt
|
run: pip install -r requirements-min.txt
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: python -m etc.unittest
|
run: python -m etc.unittest
|
||||||
- name: Set up Python 3.11
|
- name: Set up Python 3.12
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
|
17
README.md
17
README.md
@ -226,6 +226,23 @@ docker-compose down
|
|||||||
|
|
||||||
## 💡 Usage
|
## 💡 Usage
|
||||||
|
|
||||||
|
### New Client with Image Generation
|
||||||
|
```python
|
||||||
|
from g4f.client import Client
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
response = client.images.generate(
|
||||||
|
model="gemini",
|
||||||
|
prompt="a white siamese cat",
|
||||||
|
...
|
||||||
|
)
|
||||||
|
image_url = response.data[0].url
|
||||||
|
```
|
||||||
|
Result:
|
||||||
|
[![Image with cat](/docs/cat.jpeg)](/docs/client.md)
|
||||||
|
|
||||||
|
[to the client API](/docs/client.md)
|
||||||
|
|
||||||
### The Web UI
|
### The Web UI
|
||||||
|
|
||||||
To start the web interface, type the following codes in the command line.
|
To start the web interface, type the following codes in the command line.
|
||||||
|
BIN
docs/cat.jpeg
Normal file
BIN
docs/cat.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
71
docs/client.md
Normal file
71
docs/client.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
### Client API
|
||||||
|
##### from g4f (beta)
|
||||||
|
|
||||||
|
#### Start
|
||||||
|
This new client could:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from g4f.client import Client
|
||||||
|
```
|
||||||
|
replaces this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from openai import OpenAI
|
||||||
|
```
|
||||||
|
in your Python Code.
|
||||||
|
|
||||||
|
New client have the same API as OpenAI.
|
||||||
|
|
||||||
|
#### Client
|
||||||
|
|
||||||
|
Create the client with custom providers:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from g4f.client import Client
|
||||||
|
from g4f.Provider import BingCreateImages, OpenaiChat, Gemini
|
||||||
|
|
||||||
|
client = Client(
|
||||||
|
provider=OpenaiChat,
|
||||||
|
image_provider=Gemini,
|
||||||
|
proxies=None
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
Use the ChatCompletions:
|
||||||
|
|
||||||
|
```python
|
||||||
|
stream = client.chat.completions.create(
|
||||||
|
model="gpt-4",
|
||||||
|
messages=[{"role": "user", "content": "Say this is a test"}],
|
||||||
|
stream=True,
|
||||||
|
)
|
||||||
|
for chunk in stream:
|
||||||
|
if chunk.choices[0].delta.content is not None:
|
||||||
|
print(chunk.choices[0].delta.content, end="")
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use it for creating a image:
|
||||||
|
```python
|
||||||
|
response = client.images.generate(
|
||||||
|
model="dall-e-3",
|
||||||
|
prompt="a white siamese cat",
|
||||||
|
...
|
||||||
|
)
|
||||||
|
|
||||||
|
image_url = response.data[0].url
|
||||||
|
```
|
||||||
|
|
||||||
|
Also this works with the client:
|
||||||
|
```python
|
||||||
|
response = client.images.create_variation(
|
||||||
|
image=open('cat.jpg')
|
||||||
|
model="bing",
|
||||||
|
...
|
||||||
|
)
|
||||||
|
|
||||||
|
image_url = response.data[0].url
|
||||||
|
```
|
||||||
|
|
||||||
|
[to Home](/docs/client.md)
|
@ -1,60 +1,22 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
|
||||||
import os
|
import os
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
from ..cookies import get_cookies
|
from ..cookies import get_cookies
|
||||||
from ..webdriver import WebDriver, get_driver_cookies, get_browser
|
|
||||||
from ..image import ImageResponse
|
from ..image import ImageResponse
|
||||||
from ..errors import MissingRequirementsError, MissingAuthError
|
from ..errors import MissingRequirementsError, MissingAuthError
|
||||||
from .bing.create_images import BING_URL, create_images, create_session
|
from .bing.create_images import create_images, create_session, get_cookies_from_browser
|
||||||
|
|
||||||
BING_URL = "https://www.bing.com"
|
class BingCreateImages:
|
||||||
TIMEOUT_LOGIN = 1200
|
|
||||||
|
|
||||||
def wait_for_login(driver: WebDriver, timeout: int = TIMEOUT_LOGIN) -> None:
|
|
||||||
"""
|
|
||||||
Waits for the user to log in within a given timeout period.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
driver (WebDriver): Webdriver for browser automation.
|
|
||||||
timeout (int): Maximum waiting time in seconds.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
RuntimeError: If the login process exceeds the timeout.
|
|
||||||
"""
|
|
||||||
driver.get(f"{BING_URL}/")
|
|
||||||
start_time = time.time()
|
|
||||||
while not driver.get_cookie("_U"):
|
|
||||||
if time.time() - start_time > timeout:
|
|
||||||
raise RuntimeError("Timeout error")
|
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
||||||
def get_cookies_from_browser(proxy: str = None) -> dict[str, str]:
|
|
||||||
"""
|
|
||||||
Retrieves cookies from the browser using webdriver.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
proxy (str, optional): Proxy configuration.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict[str, str]: Retrieved cookies.
|
|
||||||
"""
|
|
||||||
with get_browser(proxy=proxy) as driver:
|
|
||||||
wait_for_login(driver)
|
|
||||||
time.sleep(1)
|
|
||||||
return get_driver_cookies(driver)
|
|
||||||
|
|
||||||
class CreateImagesBing:
|
|
||||||
"""A class for creating images using Bing."""
|
"""A class for creating images using Bing."""
|
||||||
|
|
||||||
def __init__(self, cookies: dict[str, str] = {}, proxy: str = None) -> None:
|
def __init__(self, cookies: dict[str, str] = {}, proxy: str = None) -> None:
|
||||||
self.cookies = cookies
|
self.cookies = cookies
|
||||||
self.proxy = proxy
|
self.proxy = proxy
|
||||||
|
|
||||||
def create_completion(self, prompt: str) -> Generator[ImageResponse, None, None]:
|
def create(self, prompt: str) -> Generator[ImageResponse, None, None]:
|
||||||
"""
|
"""
|
||||||
Generator for creating imagecompletion based on a prompt.
|
Generator for creating imagecompletion based on a prompt.
|
||||||
|
|
||||||
@ -91,4 +53,4 @@ class CreateImagesBing:
|
|||||||
proxy = self.proxy or os.environ.get("G4F_PROXY")
|
proxy = self.proxy or os.environ.get("G4F_PROXY")
|
||||||
async with create_session(cookies, proxy) as session:
|
async with create_session(cookies, proxy) as session:
|
||||||
images = await create_images(session, prompt, proxy)
|
images = await create_images(session, prompt, proxy)
|
||||||
return ImageResponse(images, prompt, {"preview": "{image}?w=200&h=200"})
|
return ImageResponse(images, prompt, {"preview": "{image}?w=200&h=200"} if len(images) > 1 else {})
|
@ -58,9 +58,14 @@ class You(AsyncGeneratorProvider):
|
|||||||
"selectedChatMode": chat_mode,
|
"selectedChatMode": chat_mode,
|
||||||
#"chat": json.dumps(chat),
|
#"chat": json.dumps(chat),
|
||||||
}
|
}
|
||||||
|
params = {
|
||||||
|
"userFiles": upload,
|
||||||
|
"selectedChatMode": chat_mode,
|
||||||
|
}
|
||||||
async with (client.post if chat_mode == "default" else client.get)(
|
async with (client.post if chat_mode == "default" else client.get)(
|
||||||
f"{cls.url}/api/streamingSearch",
|
f"{cls.url}/api/streamingSearch",
|
||||||
data=data,
|
data=data,
|
||||||
|
params=params,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
cookies=cookies
|
cookies=cookies
|
||||||
) as response:
|
) as response:
|
||||||
|
@ -53,7 +53,7 @@ from .Vercel import Vercel
|
|||||||
from .Ylokh import Ylokh
|
from .Ylokh import Ylokh
|
||||||
from .You import You
|
from .You import You
|
||||||
|
|
||||||
from .CreateImagesBing import CreateImagesBing
|
from .BingCreateImages import BingCreateImages
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -21,8 +21,10 @@ from ..create_images import CreateImagesProvider
|
|||||||
from ..helper import get_connector
|
from ..helper import get_connector
|
||||||
from ...base_provider import ProviderType
|
from ...base_provider import ProviderType
|
||||||
from ...errors import MissingRequirementsError
|
from ...errors import MissingRequirementsError
|
||||||
|
from ...webdriver import WebDriver, get_driver_cookies, get_browser
|
||||||
|
|
||||||
BING_URL = "https://www.bing.com"
|
BING_URL = "https://www.bing.com"
|
||||||
|
TIMEOUT_LOGIN = 1200
|
||||||
TIMEOUT_IMAGE_CREATION = 300
|
TIMEOUT_IMAGE_CREATION = 300
|
||||||
ERRORS = [
|
ERRORS = [
|
||||||
"this prompt is being reviewed",
|
"this prompt is being reviewed",
|
||||||
@ -35,6 +37,39 @@ BAD_IMAGES = [
|
|||||||
"https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg",
|
"https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def wait_for_login(driver: WebDriver, timeout: int = TIMEOUT_LOGIN) -> None:
|
||||||
|
"""
|
||||||
|
Waits for the user to log in within a given timeout period.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
driver (WebDriver): Webdriver for browser automation.
|
||||||
|
timeout (int): Maximum waiting time in seconds.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: If the login process exceeds the timeout.
|
||||||
|
"""
|
||||||
|
driver.get(f"{BING_URL}/")
|
||||||
|
start_time = time.time()
|
||||||
|
while not driver.get_cookie("_U"):
|
||||||
|
if time.time() - start_time > timeout:
|
||||||
|
raise RuntimeError("Timeout error")
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
def get_cookies_from_browser(proxy: str = None) -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
Retrieves cookies from the browser using webdriver.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
proxy (str, optional): Proxy configuration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict[str, str]: Retrieved cookies.
|
||||||
|
"""
|
||||||
|
with get_browser(proxy=proxy) as driver:
|
||||||
|
wait_for_login(driver)
|
||||||
|
time.sleep(1)
|
||||||
|
return get_driver_cookies(driver)
|
||||||
|
|
||||||
def create_session(cookies: Dict[str, str], proxy: str = None, connector: BaseConnector = None) -> ClientSession:
|
def create_session(cookies: Dict[str, str], proxy: str = None, connector: BaseConnector = None) -> ClientSession:
|
||||||
"""
|
"""
|
||||||
Creates a new client session with specified cookies and headers.
|
Creates a new client session with specified cookies and headers.
|
||||||
@ -141,6 +176,8 @@ def read_images(html_content: str) -> List[str]:
|
|||||||
"""
|
"""
|
||||||
soup = BeautifulSoup(html_content, "html.parser")
|
soup = BeautifulSoup(html_content, "html.parser")
|
||||||
tags = soup.find_all("img", class_="mimg")
|
tags = soup.find_all("img", class_="mimg")
|
||||||
|
if not tags:
|
||||||
|
tags = soup.find_all("img", class_="gir_mmimg")
|
||||||
images = [img["src"].split("?w=")[0] for img in tags]
|
images = [img["src"].split("?w=")[0] for img in tags]
|
||||||
if any(im in BAD_IMAGES for im in images):
|
if any(im in BAD_IMAGES for im in images):
|
||||||
raise RuntimeError("Bad images found")
|
raise RuntimeError("Bad images found")
|
||||||
@ -158,10 +195,10 @@ def patch_provider(provider: ProviderType) -> CreateImagesProvider:
|
|||||||
Returns:
|
Returns:
|
||||||
CreateImagesProvider: The patched provider with image creation capabilities.
|
CreateImagesProvider: The patched provider with image creation capabilities.
|
||||||
"""
|
"""
|
||||||
from ..CreateImagesBing import CreateImagesBing
|
from ..BingCreateImages import BingCreateImages
|
||||||
service = CreateImagesBing()
|
service = BingCreateImages()
|
||||||
return CreateImagesProvider(
|
return CreateImagesProvider(
|
||||||
provider,
|
provider,
|
||||||
service.create_completion,
|
service.create,
|
||||||
service.create_async
|
service.create_async
|
||||||
)
|
)
|
@ -7,10 +7,14 @@ from ..typing import CreateResult, Messages
|
|||||||
from ..base_provider import BaseProvider, ProviderType
|
from ..base_provider import BaseProvider, ProviderType
|
||||||
|
|
||||||
system_message = """
|
system_message = """
|
||||||
You can generate custom images with the DALL-E 3 image generator.
|
You can generate images, pictures, photos or img with the DALL-E 3 image generator.
|
||||||
To generate an image with a prompt, do this:
|
To generate an image with a prompt, do this:
|
||||||
|
|
||||||
<img data-prompt=\"keywords for the image\">
|
<img data-prompt=\"keywords for the image\">
|
||||||
Don't use images with data uri. It is important to use a prompt instead.
|
|
||||||
|
Never use own image links. Don't wrap it in backticks.
|
||||||
|
It is important to use a only a img tag with a prompt.
|
||||||
|
|
||||||
<img data-prompt=\"image caption\">
|
<img data-prompt=\"image caption\">
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -386,50 +386,47 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
|||||||
) as response:
|
) as response:
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
raise RuntimeError(f"Response {response.status_code}: {await response.text()}")
|
raise RuntimeError(f"Response {response.status_code}: {await response.text()}")
|
||||||
try:
|
last_message: int = 0
|
||||||
last_message: int = 0
|
async for line in response.iter_lines():
|
||||||
async for line in response.iter_lines():
|
if not line.startswith(b"data: "):
|
||||||
if not line.startswith(b"data: "):
|
continue
|
||||||
continue
|
elif line.startswith(b"data: [DONE]"):
|
||||||
elif line.startswith(b"data: [DONE]"):
|
break
|
||||||
break
|
try:
|
||||||
try:
|
line = json.loads(line[6:])
|
||||||
line = json.loads(line[6:])
|
except:
|
||||||
except:
|
continue
|
||||||
continue
|
if "message" not in line:
|
||||||
if "message" not in line:
|
continue
|
||||||
continue
|
if "error" in line and line["error"]:
|
||||||
if "error" in line and line["error"]:
|
raise RuntimeError(line["error"])
|
||||||
raise RuntimeError(line["error"])
|
if "message_type" not in line["message"]["metadata"]:
|
||||||
if "message_type" not in line["message"]["metadata"]:
|
continue
|
||||||
continue
|
try:
|
||||||
try:
|
image_response = await cls.get_generated_image(session, auth_headers, line)
|
||||||
image_response = await cls.get_generated_image(session, auth_headers, line)
|
if image_response:
|
||||||
if image_response:
|
yield image_response
|
||||||
yield image_response
|
except Exception as e:
|
||||||
except Exception as e:
|
yield e
|
||||||
yield e
|
if line["message"]["author"]["role"] != "assistant":
|
||||||
if line["message"]["author"]["role"] != "assistant":
|
continue
|
||||||
continue
|
if line["message"]["content"]["content_type"] != "text":
|
||||||
if line["message"]["content"]["content_type"] != "text":
|
continue
|
||||||
continue
|
if line["message"]["metadata"]["message_type"] not in ("next", "continue", "variant"):
|
||||||
if line["message"]["metadata"]["message_type"] not in ("next", "continue", "variant"):
|
continue
|
||||||
continue
|
conversation_id = line["conversation_id"]
|
||||||
conversation_id = line["conversation_id"]
|
parent_id = line["message"]["id"]
|
||||||
parent_id = line["message"]["id"]
|
if response_fields:
|
||||||
if response_fields:
|
response_fields = False
|
||||||
response_fields = False
|
yield ResponseFields(conversation_id, parent_id, end_turn)
|
||||||
yield ResponseFields(conversation_id, parent_id, end_turn)
|
if "parts" in line["message"]["content"]:
|
||||||
if "parts" in line["message"]["content"]:
|
new_message = line["message"]["content"]["parts"][0]
|
||||||
new_message = line["message"]["content"]["parts"][0]
|
if len(new_message) > last_message:
|
||||||
if len(new_message) > last_message:
|
yield new_message[last_message:]
|
||||||
yield new_message[last_message:]
|
last_message = len(new_message)
|
||||||
last_message = len(new_message)
|
if "finish_details" in line["message"]["metadata"]:
|
||||||
if "finish_details" in line["message"]["metadata"]:
|
if line["message"]["metadata"]["finish_details"]["type"] == "stop":
|
||||||
if line["message"]["metadata"]["finish_details"]["type"] == "stop":
|
end_turn.end()
|
||||||
end_turn.end()
|
|
||||||
except Exception as e:
|
|
||||||
raise e
|
|
||||||
if not auto_continue:
|
if not auto_continue:
|
||||||
break
|
break
|
||||||
action = "continue"
|
action = "continue"
|
||||||
|
@ -16,7 +16,8 @@ def get_model_and_provider(model : Union[Model, str],
|
|||||||
stream : bool,
|
stream : bool,
|
||||||
ignored : list[str] = None,
|
ignored : list[str] = None,
|
||||||
ignore_working: bool = False,
|
ignore_working: bool = False,
|
||||||
ignore_stream: bool = False) -> tuple[str, ProviderType]:
|
ignore_stream: bool = False,
|
||||||
|
**kwargs) -> tuple[str, ProviderType]:
|
||||||
"""
|
"""
|
||||||
Retrieves the model and provider based on input parameters.
|
Retrieves the model and provider based on input parameters.
|
||||||
|
|
||||||
|
267
g4f/client.py
Normal file
267
g4f/client.py
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .typing import Union, Generator, AsyncGenerator, Messages, ImageType
|
||||||
|
from .base_provider import BaseProvider, ProviderType
|
||||||
|
from .Provider.base_provider import AsyncGeneratorProvider
|
||||||
|
from .image import ImageResponse as ImageProviderResponse
|
||||||
|
from .Provider import BingCreateImages, Gemini, OpenaiChat
|
||||||
|
from .errors import NoImageResponseError
|
||||||
|
from . import get_model_and_provider
|
||||||
|
|
||||||
|
ImageProvider = Union[BaseProvider, object]
|
||||||
|
Proxies = Union[dict, str]
|
||||||
|
|
||||||
|
def read_json(text: str) -> dict:
|
||||||
|
"""
|
||||||
|
Parses JSON code block from a string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): A string containing a JSON code block.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary parsed from the JSON code block.
|
||||||
|
"""
|
||||||
|
match = re.search(r"```(json|)\n(?P<code>[\S\s]+?)\n```", text)
|
||||||
|
if match:
|
||||||
|
return match.group("code")
|
||||||
|
return text
|
||||||
|
|
||||||
|
def iter_response(
|
||||||
|
response: iter,
|
||||||
|
stream: bool,
|
||||||
|
response_format: dict = None,
|
||||||
|
max_tokens: int = None,
|
||||||
|
stop: list = None
|
||||||
|
) -> Generator:
|
||||||
|
content = ""
|
||||||
|
idx = 1
|
||||||
|
chunk = None
|
||||||
|
finish_reason = "stop"
|
||||||
|
for idx, chunk in enumerate(response):
|
||||||
|
content += str(chunk)
|
||||||
|
if max_tokens is not None and idx > max_tokens:
|
||||||
|
finish_reason = "max_tokens"
|
||||||
|
break
|
||||||
|
first = -1
|
||||||
|
word = None
|
||||||
|
if stop is not None:
|
||||||
|
for word in list(stop):
|
||||||
|
first = content.find(word)
|
||||||
|
if first != -1:
|
||||||
|
content = content[:first]
|
||||||
|
break
|
||||||
|
if stream:
|
||||||
|
if first != -1:
|
||||||
|
first = chunk.find(word)
|
||||||
|
if first != -1:
|
||||||
|
chunk = chunk[:first]
|
||||||
|
else:
|
||||||
|
first = 0
|
||||||
|
yield ChatCompletionChunk([ChatCompletionDeltaChoice(ChatCompletionDelta(chunk))])
|
||||||
|
if first != -1:
|
||||||
|
break
|
||||||
|
if not stream:
|
||||||
|
if response_format is not None and "type" in response_format:
|
||||||
|
if response_format["type"] == "json_object":
|
||||||
|
response = read_json(response)
|
||||||
|
yield ChatCompletion([ChatCompletionChoice(ChatCompletionMessage(response, finish_reason))])
|
||||||
|
|
||||||
|
async def aiter_response(
|
||||||
|
response: aiter,
|
||||||
|
stream: bool,
|
||||||
|
response_format: dict = None,
|
||||||
|
max_tokens: int = None,
|
||||||
|
stop: list = None
|
||||||
|
) -> AsyncGenerator:
|
||||||
|
content = ""
|
||||||
|
try:
|
||||||
|
idx = 0
|
||||||
|
chunk = None
|
||||||
|
async for chunk in response:
|
||||||
|
content += str(chunk)
|
||||||
|
if max_tokens is not None and idx > max_tokens:
|
||||||
|
break
|
||||||
|
first = -1
|
||||||
|
word = None
|
||||||
|
if stop is not None:
|
||||||
|
for word in list(stop):
|
||||||
|
first = content.find(word)
|
||||||
|
if first != -1:
|
||||||
|
content = content[:first]
|
||||||
|
break
|
||||||
|
if stream:
|
||||||
|
if first != -1:
|
||||||
|
first = chunk.find(word)
|
||||||
|
if first != -1:
|
||||||
|
chunk = chunk[:first]
|
||||||
|
else:
|
||||||
|
first = 0
|
||||||
|
yield ChatCompletionChunk([ChatCompletionDeltaChoice(ChatCompletionDelta(chunk))])
|
||||||
|
if first != -1:
|
||||||
|
break
|
||||||
|
idx += 1
|
||||||
|
except:
|
||||||
|
...
|
||||||
|
if not stream:
|
||||||
|
if response_format is not None and "type" in response_format:
|
||||||
|
if response_format["type"] == "json_object":
|
||||||
|
response = read_json(response)
|
||||||
|
yield ChatCompletion([ChatCompletionChoice(ChatCompletionMessage(response))])
|
||||||
|
|
||||||
|
class Model():
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return getattr(self, item)
|
||||||
|
|
||||||
|
class ChatCompletion(Model):
|
||||||
|
def __init__(self, choices: list):
|
||||||
|
self.choices = choices
|
||||||
|
|
||||||
|
class ChatCompletionChunk(Model):
|
||||||
|
def __init__(self, choices: list):
|
||||||
|
self.choices = choices
|
||||||
|
|
||||||
|
class ChatCompletionChoice(Model):
|
||||||
|
def __init__(self, message: ChatCompletionMessage):
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
class ChatCompletionMessage(Model):
|
||||||
|
def __init__(self, content: str, finish_reason: str):
|
||||||
|
self.content = content
|
||||||
|
self.finish_reason = finish_reason
|
||||||
|
self.index = 0
|
||||||
|
self.logprobs = None
|
||||||
|
|
||||||
|
class ChatCompletionDelta(Model):
|
||||||
|
def __init__(self, content: str):
|
||||||
|
self.content = content
|
||||||
|
|
||||||
|
class ChatCompletionDeltaChoice(Model):
|
||||||
|
def __init__(self, delta: ChatCompletionDelta):
|
||||||
|
self.delta = delta
|
||||||
|
|
||||||
|
class Client():
|
||||||
|
proxies: Proxies = None
|
||||||
|
chat: Chat
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
provider: ProviderType = None,
|
||||||
|
image_provider: ImageProvider = None,
|
||||||
|
proxies: Proxies = None,
|
||||||
|
**kwargs
|
||||||
|
) -> None:
|
||||||
|
self.proxies: Proxies = proxies
|
||||||
|
self.images = Images(self, image_provider)
|
||||||
|
self.chat = Chat(self, provider)
|
||||||
|
|
||||||
|
def get_proxy(self) -> Union[str, None]:
|
||||||
|
if isinstance(self.proxies, str) or self.proxies is None:
|
||||||
|
return self.proxies
|
||||||
|
elif "all" in self.proxies:
|
||||||
|
return self.proxies["all"]
|
||||||
|
elif "https" in self.proxies:
|
||||||
|
return self.proxies["https"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Completions():
|
||||||
|
def __init__(self, client: Client, provider: ProviderType = None):
|
||||||
|
self.client: Client = client
|
||||||
|
self.provider: ProviderType = provider
|
||||||
|
|
||||||
|
def create(
|
||||||
|
self,
|
||||||
|
messages: Messages,
|
||||||
|
model: str,
|
||||||
|
provider: ProviderType = None,
|
||||||
|
stream: bool = False,
|
||||||
|
response_format: dict = None,
|
||||||
|
max_tokens: int = None,
|
||||||
|
stop: list = None,
|
||||||
|
**kwargs
|
||||||
|
) -> Union[dict, Generator]:
|
||||||
|
if max_tokens is not None:
|
||||||
|
kwargs["max_tokens"] = max_tokens
|
||||||
|
if stop:
|
||||||
|
kwargs["stop"] = list(stop)
|
||||||
|
model, provider = get_model_and_provider(
|
||||||
|
model,
|
||||||
|
self.provider if provider is None else provider,
|
||||||
|
stream,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
response = provider.create_completion(model, messages, stream=stream, **kwargs)
|
||||||
|
if isinstance(provider, type) and issubclass(provider, AsyncGeneratorProvider):
|
||||||
|
response = iter_response(response, stream, response_format) # max_tokens, stop
|
||||||
|
else:
|
||||||
|
response = iter_response(response, stream, response_format, max_tokens, stop)
|
||||||
|
return response if stream else next(response)
|
||||||
|
|
||||||
|
class Chat():
|
||||||
|
completions: Completions
|
||||||
|
|
||||||
|
def __init__(self, client: Client, provider: ProviderType = None):
|
||||||
|
self.completions = Completions(client, provider)
|
||||||
|
|
||||||
|
class ImageModels():
|
||||||
|
gemini = Gemini
|
||||||
|
openai = OpenaiChat
|
||||||
|
|
||||||
|
def __init__(self, client: Client) -> None:
|
||||||
|
self.client = client
|
||||||
|
self.default = BingCreateImages(proxy=self.client.get_proxy())
|
||||||
|
|
||||||
|
def get(self, name: str) -> ImageProvider:
|
||||||
|
return getattr(self, name) if hasattr(self, name) else self.default
|
||||||
|
|
||||||
|
class ImagesResponse(Model):
|
||||||
|
data: list[Image]
|
||||||
|
|
||||||
|
def __init__(self, data: list) -> None:
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
class Image(Model):
|
||||||
|
url: str
|
||||||
|
|
||||||
|
def __init__(self, url: str) -> None:
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
class Images():
|
||||||
|
def __init__(self, client: Client, provider: ImageProvider = None):
|
||||||
|
self.client: Client = client
|
||||||
|
self.provider: ImageProvider = provider
|
||||||
|
self.models: ImageModels = ImageModels(client)
|
||||||
|
|
||||||
|
def generate(self, prompt, model: str = None, **kwargs):
|
||||||
|
provider = self.models.get(model) if model else self.provider or self.models.get(model)
|
||||||
|
if isinstance(provider, BaseProvider) or isinstance(provider, type) and issubclass(provider, BaseProvider):
|
||||||
|
prompt = f"create a image: {prompt}"
|
||||||
|
response = provider.create_completion(
|
||||||
|
"",
|
||||||
|
[{"role": "user", "content": prompt}],
|
||||||
|
True,
|
||||||
|
proxy=self.client.get_proxy()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
response = provider.create(prompt)
|
||||||
|
|
||||||
|
for chunk in response:
|
||||||
|
if isinstance(chunk, ImageProviderResponse):
|
||||||
|
return ImagesResponse([Image(image)for image in list(chunk.images)])
|
||||||
|
raise NoImageResponseError()
|
||||||
|
|
||||||
|
def create_variation(self, image: ImageType, model: str = None, **kwargs):
|
||||||
|
provider = self.models.get(model) if model else self.provider
|
||||||
|
if isinstance(provider, BaseProvider):
|
||||||
|
response = provider.create_completion(
|
||||||
|
"",
|
||||||
|
[{"role": "user", "content": "create a image like this"}],
|
||||||
|
True,
|
||||||
|
image=image,
|
||||||
|
proxy=self.client.get_proxy()
|
||||||
|
)
|
||||||
|
for chunk in response:
|
||||||
|
if isinstance(chunk, ImageProviderResponse):
|
||||||
|
return ImagesResponse([Image(image)for image in list(chunk.images)])
|
||||||
|
raise NoImageResponseError()
|
@ -1,35 +1,38 @@
|
|||||||
class ProviderNotFoundError(Exception):
|
class ProviderNotFoundError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class ProviderNotWorkingError(Exception):
|
class ProviderNotWorkingError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class StreamNotSupportedError(Exception):
|
class StreamNotSupportedError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class ModelNotFoundError(Exception):
|
class ModelNotFoundError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class ModelNotAllowedError(Exception):
|
class ModelNotAllowedError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class RetryProviderError(Exception):
|
class RetryProviderError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class RetryNoProviderError(Exception):
|
class RetryNoProviderError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class VersionNotFoundError(Exception):
|
class VersionNotFoundError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class NestAsyncioError(Exception):
|
class NestAsyncioError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class ModelNotSupportedError(Exception):
|
class ModelNotSupportedError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class MissingRequirementsError(Exception):
|
class MissingRequirementsError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
class MissingAuthError(Exception):
|
class MissingAuthError(Exception):
|
||||||
pass
|
...
|
||||||
|
|
||||||
|
class NoImageResponseError(Exception):
|
||||||
|
...
|
@ -52,6 +52,12 @@ const handle_ask = async () => {
|
|||||||
}
|
}
|
||||||
await add_message(window.conversation_id, "user", message);
|
await add_message(window.conversation_id, "user", message);
|
||||||
window.token = message_id();
|
window.token = message_id();
|
||||||
|
|
||||||
|
if (imageInput.dataset.src) URL.revokeObjectURL(imageInput.dataset.src);
|
||||||
|
const input = imageInput && imageInput.files.length > 0 ? imageInput : cameraInput
|
||||||
|
if (input.files.length > 0) imageInput.dataset.src = URL.createObjectURL(input.files[0]);
|
||||||
|
else delete imageInput.dataset.src
|
||||||
|
|
||||||
message_box.innerHTML += `
|
message_box.innerHTML += `
|
||||||
<div class="message">
|
<div class="message">
|
||||||
<div class="user">
|
<div class="user">
|
||||||
@ -64,10 +70,6 @@ const handle_ask = async () => {
|
|||||||
? '<img src="' + imageInput.dataset.src + '" alt="Image upload">'
|
? '<img src="' + imageInput.dataset.src + '" alt="Image upload">'
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
${cameraInput.dataset.src
|
|
||||||
? '<img src="' + cameraInput.dataset.src + '" alt="Image capture">'
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -683,24 +685,13 @@ observer.observe(message_input, { attributes: true });
|
|||||||
document.getElementById("version_text").innerHTML = text
|
document.getElementById("version_text").innerHTML = text
|
||||||
})()
|
})()
|
||||||
for (const el of [imageInput, cameraInput]) {
|
for (const el of [imageInput, cameraInput]) {
|
||||||
console.log(el.files);
|
|
||||||
el.addEventListener('click', async () => {
|
el.addEventListener('click', async () => {
|
||||||
el.value = '';
|
el.value = '';
|
||||||
delete el.dataset.src;
|
if (imageInput.dataset.src) {
|
||||||
});
|
URL.revokeObjectURL(imageInput.dataset.src);
|
||||||
do_load = async () => {
|
delete imageInput.dataset.src
|
||||||
if (el.files.length) {
|
|
||||||
delete imageInput.dataset.src;
|
|
||||||
delete cameraInput.dataset.src;
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.addEventListener('load', (event) => {
|
|
||||||
el.dataset.src = event.target.result;
|
|
||||||
});
|
|
||||||
reader.readAsDataURL(el.files[0]);
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
do_load()
|
|
||||||
el.addEventListener('change', do_load);
|
|
||||||
}
|
}
|
||||||
fileInput.addEventListener('click', async (event) => {
|
fileInput.addEventListener('click', async (event) => {
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
|
Loading…
Reference in New Issue
Block a user