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
|
||||
- name: Run tests
|
||||
run: python -m etc.unittest
|
||||
- name: Set up Python 3.11
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
17
README.md
17
README.md
@ -226,6 +226,23 @@ docker-compose down
|
||||
|
||||
## 💡 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
|
||||
|
||||
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
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
import os
|
||||
from typing import Generator
|
||||
|
||||
from ..cookies import get_cookies
|
||||
from ..webdriver import WebDriver, get_driver_cookies, get_browser
|
||||
from ..image import ImageResponse
|
||||
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"
|
||||
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:
|
||||
class BingCreateImages:
|
||||
"""A class for creating images using Bing."""
|
||||
|
||||
def __init__(self, cookies: dict[str, str] = {}, proxy: str = None) -> None:
|
||||
self.cookies = cookies
|
||||
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.
|
||||
|
||||
@ -91,4 +53,4 @@ class CreateImagesBing:
|
||||
proxy = self.proxy or os.environ.get("G4F_PROXY")
|
||||
async with create_session(cookies, proxy) as session:
|
||||
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,
|
||||
#"chat": json.dumps(chat),
|
||||
}
|
||||
params = {
|
||||
"userFiles": upload,
|
||||
"selectedChatMode": chat_mode,
|
||||
}
|
||||
async with (client.post if chat_mode == "default" else client.get)(
|
||||
f"{cls.url}/api/streamingSearch",
|
||||
data=data,
|
||||
params=params,
|
||||
headers=headers,
|
||||
cookies=cookies
|
||||
) as response:
|
||||
|
@ -53,7 +53,7 @@ from .Vercel import Vercel
|
||||
from .Ylokh import Ylokh
|
||||
from .You import You
|
||||
|
||||
from .CreateImagesBing import CreateImagesBing
|
||||
from .BingCreateImages import BingCreateImages
|
||||
|
||||
import sys
|
||||
|
||||
|
@ -21,8 +21,10 @@ from ..create_images import CreateImagesProvider
|
||||
from ..helper import get_connector
|
||||
from ...base_provider import ProviderType
|
||||
from ...errors import MissingRequirementsError
|
||||
from ...webdriver import WebDriver, get_driver_cookies, get_browser
|
||||
|
||||
BING_URL = "https://www.bing.com"
|
||||
TIMEOUT_LOGIN = 1200
|
||||
TIMEOUT_IMAGE_CREATION = 300
|
||||
ERRORS = [
|
||||
"this prompt is being reviewed",
|
||||
@ -35,6 +37,39 @@ BAD_IMAGES = [
|
||||
"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:
|
||||
"""
|
||||
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")
|
||||
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]
|
||||
if any(im in BAD_IMAGES for im in images):
|
||||
raise RuntimeError("Bad images found")
|
||||
@ -158,10 +195,10 @@ def patch_provider(provider: ProviderType) -> CreateImagesProvider:
|
||||
Returns:
|
||||
CreateImagesProvider: The patched provider with image creation capabilities.
|
||||
"""
|
||||
from ..CreateImagesBing import CreateImagesBing
|
||||
service = CreateImagesBing()
|
||||
from ..BingCreateImages import BingCreateImages
|
||||
service = BingCreateImages()
|
||||
return CreateImagesProvider(
|
||||
provider,
|
||||
service.create_completion,
|
||||
service.create,
|
||||
service.create_async
|
||||
)
|
@ -7,10 +7,14 @@ from ..typing import CreateResult, Messages
|
||||
from ..base_provider import BaseProvider, ProviderType
|
||||
|
||||
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:
|
||||
|
||||
<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\">
|
||||
"""
|
||||
|
||||
|
@ -386,7 +386,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||
) as response:
|
||||
if not response.ok:
|
||||
raise RuntimeError(f"Response {response.status_code}: {await response.text()}")
|
||||
try:
|
||||
last_message: int = 0
|
||||
async for line in response.iter_lines():
|
||||
if not line.startswith(b"data: "):
|
||||
@ -428,8 +427,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||
if "finish_details" in line["message"]["metadata"]:
|
||||
if line["message"]["metadata"]["finish_details"]["type"] == "stop":
|
||||
end_turn.end()
|
||||
except Exception as e:
|
||||
raise e
|
||||
if not auto_continue:
|
||||
break
|
||||
action = "continue"
|
||||
|
@ -16,7 +16,8 @@ def get_model_and_provider(model : Union[Model, str],
|
||||
stream : bool,
|
||||
ignored : list[str] = None,
|
||||
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.
|
||||
|
||||
|
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):
|
||||
pass
|
||||
...
|
||||
|
||||
class ProviderNotWorkingError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class StreamNotSupportedError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class ModelNotFoundError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class ModelNotAllowedError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class RetryProviderError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class RetryNoProviderError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class VersionNotFoundError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class NestAsyncioError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class ModelNotSupportedError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class MissingRequirementsError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class MissingAuthError(Exception):
|
||||
pass
|
||||
...
|
||||
|
||||
class NoImageResponseError(Exception):
|
||||
...
|
@ -52,6 +52,12 @@ const handle_ask = async () => {
|
||||
}
|
||||
await add_message(window.conversation_id, "user", message);
|
||||
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 += `
|
||||
<div class="message">
|
||||
<div class="user">
|
||||
@ -64,10 +70,6 @@ const handle_ask = async () => {
|
||||
? '<img src="' + imageInput.dataset.src + '" alt="Image upload">'
|
||||
: ''
|
||||
}
|
||||
${cameraInput.dataset.src
|
||||
? '<img src="' + cameraInput.dataset.src + '" alt="Image capture">'
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -683,24 +685,13 @@ observer.observe(message_input, { attributes: true });
|
||||
document.getElementById("version_text").innerHTML = text
|
||||
})()
|
||||
for (const el of [imageInput, cameraInput]) {
|
||||
console.log(el.files);
|
||||
el.addEventListener('click', async () => {
|
||||
el.value = '';
|
||||
delete el.dataset.src;
|
||||
});
|
||||
do_load = async () => {
|
||||
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]);
|
||||
if (imageInput.dataset.src) {
|
||||
URL.revokeObjectURL(imageInput.dataset.src);
|
||||
delete imageInput.dataset.src
|
||||
}
|
||||
}
|
||||
do_load()
|
||||
el.addEventListener('change', do_load);
|
||||
});
|
||||
}
|
||||
fileInput.addEventListener('click', async (event) => {
|
||||
fileInput.value = '';
|
||||
|
Loading…
Reference in New Issue
Block a user