From 0ec688f9eb4bfc0e3725da1852b60215d1206743 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Fri, 25 Aug 2023 23:24:22 +0200 Subject: [PATCH 01/32] v0.1 --- __init__.py | 18 +++++------ __main__.py | 18 +++++------ openai_engine/chatgpt.py | 69 +++++++++++++++++++++++----------------- openai_engine/dalle.py | 17 ++++++++-- openai_engine/models.py | 13 +++++++- utils/audio_recorder.py | 45 ++++++++++++++++---------- utils/logger_config.py | 13 +++++++- utils/other.py | 13 +++++++- utils/transcriptors.py | 12 ++++++- utils/translators.py | 12 ++++++- utils/tts.py | 14 +++++++- 11 files changed, 169 insertions(+), 75 deletions(-) diff --git a/__init__.py b/__init__.py index 90b7e87..0fabcaf 100644 --- a/__init__.py +++ b/__init__.py @@ -1,12 +1,10 @@ # Engines -from .testrail_api_reporter.engines.at_coverage_reporter import ATCoverageReporter -from .testrail_api_reporter.engines.results_reporter import TestRailResultsReporter -from .testrail_api_reporter.engines.plotly_reporter import PlotlyReporter -from .testrail_api_reporter.engines.case_backup import TCBackup -# Publishers -from .testrail_api_reporter.publishers.confluence_sender import ConfluenceSender -from .testrail_api_reporter.publishers.email_sender import EmailSender -from .testrail_api_reporter.publishers.slack_sender import SlackSender -from .testrail_api_reporter.publishers.gdrive_uploader import GoogleDriveUploader +from .openai_engine.chatgpt import ChatGPT +from .openai_engine.dalle import DALLE # Utils -from .testrail_api_reporter.utils.reporter_utils import upload_image, zip_file, delete_file +from .utils.tts import CustomTTS +from .utils.transcriptors import CustomTranscriptor +from .utils.translators import CustomTranslator +from .utils.audio_recorder import AudioRecorder, record_and_convert_audio +from .utils.logger_config import setup_logger +from .utils.other import is_heroku_environment diff --git a/__main__.py b/__main__.py index b38aefd..b036852 100644 --- a/__main__.py +++ b/__main__.py @@ -1,12 +1,10 @@ # Engines -from testrail_api_reporter.engines.at_coverage_reporter import ATCoverageReporter -from testrail_api_reporter.engines.results_reporter import TestRailResultsReporter -from testrail_api_reporter.engines.plotly_reporter import PlotlyReporter -from testrail_api_reporter.engines.case_backup import TCBackup -# Publishers -from testrail_api_reporter.publishers.confluence_sender import ConfluenceSender -from testrail_api_reporter.publishers.email_sender import EmailSender -from testrail_api_reporter.publishers.slack_sender import SlackSender -from testrail_api_reporter.publishers.gdrive_uploader import GoogleDriveUploader +from openai_engine.chatgpt import ChatGPT +from openai_engine.dalle import DALLE # Utils -from testrail_api_reporter.utils.reporter_utils import upload_image, zip_file, delete_file +from utils.tts import CustomTTS +from utils.transcriptors import CustomTranscriptor +from utils.translators import CustomTranslator +from utils.audio_recorder import AudioRecorder, record_and_convert_audio +from utils.logger_config import setup_logger +from utils.other import is_heroku_environment diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index dd464e1..8950208 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -1,5 +1,16 @@ -""" This module contains implementation for ChatGPT """ # -*- coding: utf-8 -*- +""" +Filename: chatgpt.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This file contains implementation for ChatGPT +""" + import logging import openai @@ -18,10 +29,10 @@ class GPTStatistics: """ def __init__( - self, - prompt_tokens: int = 0, - completion_tokens: int = 0, - total_tokens: int = 0, + self, + prompt_tokens: int = 0, + completion_tokens: int = 0, + total_tokens: int = 0, ): """ Constructs all the necessary attributes for the GPTStatistics object. @@ -182,29 +193,29 @@ class ChatGPT: """ def __init__( - self, - auth_token, - organization, - model: str = COMPLETIONS[0], - choices: int = 1, - temperature: float = 1, - top_p: float = 1, - stream: bool = False, - stop: str = None, - max_tokens: int = 1024, - presence_penalty: float = 0, - frequency_penalty: float = 0, - logit_bias: map = None, - user: str = "", - functions: list = None, - function_call: str = None, - history_length: int = 5, - chats: dict = None, - current_chat: str = None, - prompt_method: bool = False, - logger: logging.Logger = None, - statistics: GPTStatistics = GPTStatistics(), - system_settings: str = None, + self, + auth_token, + organization, + model: str = COMPLETIONS[0], + choices: int = 1, + temperature: float = 1, + top_p: float = 1, + stream: bool = False, + stop: str = None, + max_tokens: int = 1024, + presence_penalty: float = 0, + frequency_penalty: float = 0, + logit_bias: map = None, + user: str = "", + functions: list = None, + function_call: str = None, + history_length: int = 5, + chats: dict = None, + current_chat: str = None, + prompt_method: bool = False, + logger: logging.Logger = None, + statistics: GPTStatistics = GPTStatistics(), + system_settings: str = None, ): """ General init @@ -688,7 +699,7 @@ async def chat(self, prompt, chat_name=None, default_choice=0): pass else: # Get last 'history_length' messages - messages = self.chats[chat_name][-self.history_length :] + messages = self.chats[chat_name][-self.history_length:] messages.insert(0, {"role": "system", "content": self.system_settings}) try: diff --git a/openai_engine/dalle.py b/openai_engine/dalle.py index 2629258..6a4fc7d 100644 --- a/openai_engine/dalle.py +++ b/openai_engine/dalle.py @@ -1,11 +1,22 @@ -""" This module contains implementation for DALL-E """ # -*- coding: utf-8 -*- -import tempfile +""" +Filename: dalle.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This file contains implementation for DALL-E +""" + import os +import tempfile import uuid +from io import BytesIO import aiohttp -from io import BytesIO import openai from PIL import Image diff --git a/openai_engine/models.py b/openai_engine/models.py index 5f30d3a..53d6c83 100644 --- a/openai_engine/models.py +++ b/openai_engine/models.py @@ -1,4 +1,15 @@ -""" This file contains OpenAI constants """ +# -*- coding: utf-8 -*- +""" +Filename: models.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This file contains OpenAI constants +""" COMPLETIONS = [ "gpt-4", diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index 47d050b..185ee79 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -1,12 +1,23 @@ -""" This module contains implementation for Audio Recorder """ +# -*- coding: utf-8 -*- +""" +Filename: audio_recorder.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This file contains implementation for Audio Recorder +""" + +import math import os import struct import tempfile import time import wave -# -*- coding: utf-8 -*- -import math import pyaudio import sounddevice as sd import soundfile as sf @@ -30,21 +41,21 @@ def record_and_convert_audio(duration=10, fs=44100): return mp3_file_path -class Recorder: +class AudioRecorder: def __init__( - self, - pyaudio_obj=pyaudio.PyAudio(), - threshold=15, - channels=1, - chunk=1024, - f_format=pyaudio.paInt16, - rate=16000, - swidth=2, - timeout_length=2, - temp_dir=tempfile.gettempdir(), - normalize=(1.0 / 32768.0), - pa_input=True, - pa_output=True, + self, + pyaudio_obj=pyaudio.PyAudio(), + threshold=15, + channels=1, + chunk=1024, + f_format=pyaudio.paInt16, + rate=16000, + swidth=2, + timeout_length=2, + temp_dir=tempfile.gettempdir(), + normalize=(1.0 / 32768.0), + pa_input=True, + pa_output=True, ): self.___pyaudio = pyaudio_obj self.___threshold = threshold diff --git a/utils/logger_config.py b/utils/logger_config.py index aba0161..3c453b3 100644 --- a/utils/logger_config.py +++ b/utils/logger_config.py @@ -1,4 +1,15 @@ -""" This file contains an configuration for loggers. """ +# -*- coding: utf-8 -*- +""" +Filename: logger_config.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This file contains configuration for loggers. +""" import logging import sys diff --git a/utils/other.py b/utils/other.py index a616737..7111eba 100644 --- a/utils/other.py +++ b/utils/other.py @@ -1,4 +1,15 @@ -""" This file contains several other stuff. """ +# -*- coding: utf-8 -*- +""" +Filename: other.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This file contains several other stuff. +""" import os diff --git a/utils/transcriptors.py b/utils/transcriptors.py index 4a4e078..d1bf2f6 100644 --- a/utils/transcriptors.py +++ b/utils/transcriptors.py @@ -1,5 +1,15 @@ -""" This module contains implementation for Custom Transcriptor """ # -*- coding: utf-8 -*- +""" +Filename: transcriptors.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This module contains implementation for Custom Transcriptor +""" import speech_recognition as sr diff --git a/utils/translators.py b/utils/translators.py index 0682d9a..e5fc4b3 100644 --- a/utils/translators.py +++ b/utils/translators.py @@ -1,5 +1,15 @@ -""" This module contains implementation for Custom Translator """ # -*- coding: utf-8 -*- +""" +Filename: translators.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This module contains implementation for Custom Translator +""" from deep_translator import GoogleTranslator diff --git a/utils/tts.py b/utils/tts.py index 364e583..4749a58 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -1,4 +1,16 @@ # -*- coding: utf-8 -*- +""" +Filename: tts.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 25.08.2023 + +Description: +This module contains implementation for Text-to-Speach tools +""" + import os import tempfile from time import sleep @@ -12,7 +24,7 @@ class CustomTTS: def __init__( - self, method="google", lang="en", speedup=1.3, frame=0.1, voice="com.apple.voice.enhanced.ru-RU.Katya" + self, method="google", lang="en", speedup=1.3, frame=0.1, voice="com.apple.voice.enhanced.ru-RU.Katya" ): self.___method = method self.___player = MPyg321Player() From b50025ce31be4528de94c32cee82ca0bd156255d Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Fri, 25 Aug 2023 23:27:01 +0200 Subject: [PATCH 02/32] v0.1 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 91e7593..f9b0044 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ aiohttp==3.8.5 -pyobjc # OpenAI openai==0.27.9 # TTS From de65ae8618b92773c3cdeeaa8d7d89c81786f2f7 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Fri, 25 Aug 2023 23:40:28 +0200 Subject: [PATCH 03/32] v0.1 --- .github/workflows/linters.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 9bc4f54..6e45a60 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -31,6 +31,10 @@ jobs: pip install mypy reorder-python-imports pip install wemake-python-styleguide reorder-python-imports pip install black reorder-python-imports + pip install --global-option='build_ext' \ + --global-option='-I/usr/local/include' \ + --global-option='-L/usr/local/lib' \ + pyaudio - name: Analysing the code with pylint id: pylint continue-on-error: true From 7958799a7167dd713eacd357e2b99685a2d2497b Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Fri, 25 Aug 2023 23:41:36 +0200 Subject: [PATCH 04/32] v0.1 --- .github/workflows/linters.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 6e45a60..e891ef4 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11.4"] + python-version: ["3.9", "3.10", "3.11.4"] steps: - name: Checkout uses: actions/checkout@v3 @@ -26,15 +26,16 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + pip install --global-option='build_ext' \ + --global-option='-I/usr/local/include' \ + --global-option='-L/usr/local/lib' \ + pyaudio pip install -r requirements.txt pip install pylint reorder-python-imports pip install mypy reorder-python-imports pip install wemake-python-styleguide reorder-python-imports pip install black reorder-python-imports - pip install --global-option='build_ext' \ - --global-option='-I/usr/local/include' \ - --global-option='-L/usr/local/lib' \ - pyaudio + - name: Analysing the code with pylint id: pylint continue-on-error: true From a47131917b058e223fb4a2265adcff2a5ec51dcf Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Fri, 25 Aug 2023 23:44:20 +0200 Subject: [PATCH 05/32] v0.1 --- .github/workflows/linters.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index e891ef4..47a07ed 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -25,11 +25,8 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | + sudo apt-get install libjack-jackd2-dev portaudio19-dev python -m pip install --upgrade pip - pip install --global-option='build_ext' \ - --global-option='-I/usr/local/include' \ - --global-option='-L/usr/local/lib' \ - pyaudio pip install -r requirements.txt pip install pylint reorder-python-imports pip install mypy reorder-python-imports From dafc5684fb3eafb297fc7b2a084979d795a084e0 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 00:12:19 +0200 Subject: [PATCH 06/32] v0.1 --- openai_engine/chatgpt.py | 10 +++---- openai_engine/dalle.py | 40 ++++++++++++++++++++++----- utils/logger_config.py | 12 ++++++++- utils/tts.py | 58 +++++++++++++++++++++++++++++++++------- 4 files changed, 98 insertions(+), 22 deletions(-) diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index 8950208..7a78298 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -15,12 +15,12 @@ import openai -from models import COMPLETIONS, TRANSCRIPTIONS, TRANSLATIONS +from openai_engine.models import COMPLETIONS, TRANSCRIPTIONS, TRANSLATIONS class GPTStatistics: """ - The GPTStatistics class is for creating an instance of the GPTStatistics model. + The GPTStatistics class is for managing an instance of the GPTStatistics model. Parameters: prompt_tokens (int): The number of tokens in the prompt. Default is 0. @@ -162,7 +162,7 @@ def get_tokens(self): class ChatGPT: """ - The ChatGPT class is for creating an instance of the ChatGPT model. + The ChatGPT class is for managing an instance of the ChatGPT model. Parameters: auth_token (str): Authentication bearer token. Required. @@ -194,8 +194,8 @@ class ChatGPT: def __init__( self, - auth_token, - organization, + auth_token: str, + organization: str, model: str = COMPLETIONS[0], choices: int = 1, temperature: float = 1, diff --git a/openai_engine/dalle.py b/openai_engine/dalle.py index 6a4fc7d..f2293d0 100644 --- a/openai_engine/dalle.py +++ b/openai_engine/dalle.py @@ -10,7 +10,7 @@ Description: This file contains implementation for DALL-E """ - +import logging import os import tempfile import uuid @@ -22,13 +22,39 @@ class DALLE: - def __init__(self, auth_token, organization, default_count=1, default_size="512x512", default_file_format='PNG', - user=None): + """ + The ChatGPT class is for managing an instance of the ChatGPT model. + + Parameters: + auth_token (str): Authentication bearer token. Required. + organization (str): Organization uses auth toke. Required. + default_count (int): Default count of images to produce. Default is 1. + default_size (str): Default dimensions for output images. Default is "512x512". "256x256" and "1024x1024" as option. + default_file_format (str): Default file format. Optional. Default is 'PNG'. + user (str, optional): The user ID. Default is ''. + logger (logging.Logger, optional): default logger. Default is None. + """ + + def __init__(self, auth_token: str, organization: str, default_count: int = 1, default_size: str = "512x512", + default_file_format: str = 'PNG', + user: str = '', logger: logging.Logger = None): + """ + General init + + :param auth_token (str): Authentication bearer token. Required. + :param organization (str): Organization uses auth toke. Required. + :param default_count: Default count of images to produce. Optional. Default is 1. + :param default_size: Default dimensions for output images. Optional. Default is "512x512". + :param default_file_format: Default file format. Optional. Optional. Default is 'PNG'. + :param user: The user ID. Optional. Default is ''. + :param logger: default logger. Optional. Default is None. + """ self.___default_count = default_count self.___default_size = default_size self.___default_file_format = default_file_format self.___user = user self.___set_auth(auth_token, organization) + self.___logger = logger @staticmethod def ___set_auth(token, organization): @@ -167,7 +193,7 @@ async def create_variation_from_file(self, file): :return: A PIL.Image object created from the image data received from the API. """ - response = await openai.Image.acreate_variation(file=file, n=self.default_count, size=self.default_size, + response = await openai.Image.acreate_variation(image=file, n=self.default_count, size=self.default_size, user=self.user) image_url = response["data"][0]["url"] async with aiohttp.ClientSession() as session: @@ -187,7 +213,7 @@ async def create_variation_from_url(self, url): async with session.get(url) as resp: image_data = await resp.read() - response = await openai.Image.acreate_variation(BytesIO(image_data), n=self.default_count, + response = await openai.Image.acreate_variation(image=BytesIO(image_data), n=self.default_count, size=self.default_size, user=self.user) image_url = response["data"][0]["url"] @@ -206,7 +232,7 @@ async def edit_image_from_file(self, file, prompt, mask=None): If provided, the mask will be applied to the image. :return: A PIL.Image object created from the image data received from the API. """ - response = await openai.Image.acreate_edit(file=file, prompt=prompt, mask=mask, + response = await openai.Image.acreate_edit(image=file, prompt=prompt, mask=mask, n=self.default_count, size=self.default_size, user=self.user) image_url = response["data"][0]["url"] @@ -231,7 +257,7 @@ async def edit_image_from_url(self, url, prompt, mask_url=None): async with aiohttp.ClientSession() as session: async with session.get(mask_url) as resp: mask_data = await resp.read() - response = await openai.Image.acreate_edit(file=BytesIO(image_data), prompt=prompt, mask=BytesIO(mask_data), + response = await openai.Image.acreate_edit(image=BytesIO(image_data), prompt=prompt, mask=BytesIO(mask_data), n=self.default_count, size=self.default_size, user=self.user) image_url = response["data"][0]["url"] diff --git a/utils/logger_config.py b/utils/logger_config.py index 3c453b3..8f82425 100644 --- a/utils/logger_config.py +++ b/utils/logger_config.py @@ -17,7 +17,17 @@ from utils.other import is_heroku_environment -def setup_logger(name, log_file, level=logging.DEBUG): +def setup_logger(name: str, log_file: str, level=logging.DEBUG): + """ + Method to setup logger + + :param name: (string) Name of the logger. + :param log_file: path to log_file + :param level: logging level. Default is logging.DEBUG + + :returns: logger object + + """ formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s") file_handler = logging.FileHandler(log_file) diff --git a/utils/tts.py b/utils/tts.py index 4749a58..75639ae 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -23,9 +23,29 @@ class CustomTTS: + """ + The GPTStatistics class is for managing an instance of the Custom Text-to-Speach models. + + Parameters: + method (str): Default method to use TTS. Default is 'google'. + lang: language in ISO 639-1 format. Default is 'en'. + speedup (float): Speedup ratio. Default is 1.3. + frame (float): audio sample frame in seconds. Default is 0.1. + voice (str): default TTS voice to use. Default is 'com.apple.voice.enhanced.ru-RU.Katya' + """ + def __init__( self, method="google", lang="en", speedup=1.3, frame=0.1, voice="com.apple.voice.enhanced.ru-RU.Katya" ): + """ + General init. + + :param method: Default method to use TTS. Default is 'google'. + :param lang: language in ISO 639-1 format. Default is 'en'. + :param speedup: Speedup ratio. Default is 1.3. + :param frame: audio sample frame in seconds. Default is 0.1. + :param voice: default TTS voice to use. Default is 'com.apple.voice.enhanced.ru-RU.Katya' + """ self.___method = method self.___player = MPyg321Player() self.___pytts = pyttsx_init() @@ -34,13 +54,18 @@ def __init__( self.___speedup = speedup self.___frame = frame - def __process_via_gtts(self, answer): + def __process_via_gtts(self, text): + """ + Converts text to speach using Google text-to-speach method + + :param text: Text needs to be converted to speach. + """ temp_dir = tempfile.gettempdir() # gtts - tts = gTTS(answer, lang=self.___lang) + tts = gTTS(text, lang=self.___lang) tts.save(f"{temp_dir}/raw.mp3") audio = AudioSegment.from_file(f"{temp_dir}/raw.mp3", format="mp3") - new = audio.speedup(1.3) # speed up by 2x + new = audio.speedup(self.___speedup) # speed up by 2x os.remove(f"{temp_dir}/raw.mp3") new.export(f"{temp_dir}/response.mp3", format="mp3") # player @@ -50,23 +75,38 @@ def __process_via_gtts(self, answer): self.___player.stop() os.remove(f"{temp_dir}/response.mp3") - def __process_via_pytts(self, answer): + def __process_via_pytts(self, text): + """ + Converts text to speach using python-tts text-to-speach method + + :param text: Text needs to be converted to speach. + """ engine = self.___pytts engine.setProperty("voice", self.___voice) - engine.say(answer) + engine.say(text) engine.startLoop(False) while engine.isBusy(): engine.iterate() - sleep(0.1) + sleep(self.___frame) engine.endLoop() - async def process(self, answer): + async def process(self, text): + """ + Converts text to speach using pre-defined model + + :param text: Text needs to be converted to speach. + """ if "google" in self.___method: - self.__process_via_gtts(answer) + self.__process_via_gtts(text) else: - self.__process_via_pytts(answer) + self.__process_via_pytts(text) def get_pytts_voices_list(self): + """ + Returns list of possible voices + + :return: (list) of possible voices + """ return self.___pytts.getProperty("voices") From 74c8c4f430b2fd118c1697a0822d600b1ae732f6 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 10:11:13 +0200 Subject: [PATCH 07/32] v0.1 --- __init__.py | 28 ++++++++++++++++++++-------- __main__.py | 28 ++++++++++++++++++++-------- openai_engine/chatgpt.py | 23 ++++++++++++++++++++++- openai_engine/dalle.py | 20 +++++++++++++++++++- utils/audio_recorder.py | 12 ++++++------ 5 files changed, 87 insertions(+), 24 deletions(-) diff --git a/__init__.py b/__init__.py index 0fabcaf..a9edf77 100644 --- a/__init__.py +++ b/__init__.py @@ -1,10 +1,22 @@ +# -*- coding: utf-8 -*- +""" +Filename: __main__.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 26.08.2023 + +Description: +This file is init point for project-wide structure. +""" # Engines -from .openai_engine.chatgpt import ChatGPT -from .openai_engine.dalle import DALLE +from .openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import +from .openai_engine.dalle import DALLE # pylint: disable=unused-import # Utils -from .utils.tts import CustomTTS -from .utils.transcriptors import CustomTranscriptor -from .utils.translators import CustomTranslator -from .utils.audio_recorder import AudioRecorder, record_and_convert_audio -from .utils.logger_config import setup_logger -from .utils.other import is_heroku_environment +from .utils.tts import CustomTTS # pylint: disable=unused-import +from .utils.transcriptors import CustomTranscriptor # pylint: disable=unused-import +from .utils.translators import CustomTranslator # pylint: disable=unused-import +from .utils.audio_recorder import AudioRecorder, record_and_convert_audio # pylint: disable=unused-import +from .utils.logger_config import setup_logger # pylint: disable=unused-import +from .utils.other import is_heroku_environment # pylint: disable=unused-import diff --git a/__main__.py b/__main__.py index b036852..745e0f1 100644 --- a/__main__.py +++ b/__main__.py @@ -1,10 +1,22 @@ +# -*- coding: utf-8 -*- +""" +Filename: __main__.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 26.08.2023 + +Description: +This file is entry point for project-wide structure. +""" # Engines -from openai_engine.chatgpt import ChatGPT -from openai_engine.dalle import DALLE +from .openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import +from .openai_engine.dalle import DALLE # pylint: disable=unused-import # Utils -from utils.tts import CustomTTS -from utils.transcriptors import CustomTranscriptor -from utils.translators import CustomTranslator -from utils.audio_recorder import AudioRecorder, record_and_convert_audio -from utils.logger_config import setup_logger -from utils.other import is_heroku_environment +from .utils.tts import CustomTTS # pylint: disable=unused-import +from .utils.transcriptors import CustomTranscriptor # pylint: disable=unused-import +from .utils.translators import CustomTranslator # pylint: disable=unused-import +from .utils.audio_recorder import AudioRecorder, record_and_convert_audio # pylint: disable=unused-import +from .utils.logger_config import setup_logger # pylint: disable=unused-import +from .utils.other import is_heroku_environment # pylint: disable=unused-import diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index 7a78298..3019c00 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -5,7 +5,7 @@ Copyright (c) 2023. All rights reserved. Created: 25.08.2023 -Last Modified: 25.08.2023 +Last Modified: 26.08.2023 Description: This file contains implementation for ChatGPT @@ -160,6 +160,7 @@ def get_tokens(self): } +# pylint: disable=too-many-instance-attributes,too-many-public-methods class ChatGPT: """ The ChatGPT class is for managing an instance of the ChatGPT model. @@ -193,6 +194,7 @@ class ChatGPT: """ def __init__( + # pylint: disable=too-many-locals self, auth_token: str, organization: str, @@ -600,6 +602,24 @@ def system_settings(self, value): """ self.___system_settings = value + @property + def logger(self): + """ + Getter for logger. + + :return: The logger object. + """ + return self.___logger + + @logger.setter + def logger(self, value): + """ + Setter for logger. + + :param value: The new logger object. + """ + self.___logger = value + async def process_chat(self, prompt, default_choice=None): """ Creates a new chat completion for the provided messages and parameters. @@ -609,6 +629,7 @@ async def process_chat(self, prompt, default_choice=None): :return: Returns answers by chunk if 'stream' is false True, otherwise return complete answer. """ + # pylint: disable=too-many-branches # Prepare parameters params = { "model": self.model, diff --git a/openai_engine/dalle.py b/openai_engine/dalle.py index f2293d0..be13707 100644 --- a/openai_engine/dalle.py +++ b/openai_engine/dalle.py @@ -5,7 +5,7 @@ Copyright (c) 2023. All rights reserved. Created: 25.08.2023 -Last Modified: 25.08.2023 +Last Modified: 26.08.2023 Description: This file contains implementation for DALL-E @@ -139,6 +139,24 @@ def user(self, value): """ self.___user = value + @property + def logger(self): + """ + Getter for logger. + + :return: The logger object. + """ + return self.___logger + + @logger.setter + def logger(self, value): + """ + Setter for logger. + + :param value: The new logger object. + """ + self.___logger = value + async def create_image(self, prompt): """ Creates an image using DALL-E Image API. diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index 185ee79..6ed32d2 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -109,12 +109,12 @@ def write(self, recording): n_files = len(os.listdir(self.___temp_dir)) filename = os.path.join(self.___temp_dir, f"{n_files}.wav") - wf = wave.open(filename, "wb") - wf.setnchannels(self.___channels) - wf.setsampwidth(self.p.get_sample_size(self.___format)) - wf.setframerate(self.___rate) - wf.writeframes(recording) - wf.close() + wave_form = wave.open(filename, "wb") + wave_form.setnchannels(self.___channels) + wave_form.setsampwidth(self.p.get_sample_size(self.___format)) + wave_form.setframerate(self.___rate) + wave_form.writeframes(recording) + wave_form.close() return filename def convert_to_mp3(self, filename): From 4db5c5ecd2c16e815cb5799020b65db5019dec2f Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 10:51:01 +0200 Subject: [PATCH 08/32] v0.1 --- openai_engine/chatgpt.py | 1 + utils/audio_recorder.py | 154 +++++++++++++++++++++++++++++++++------ utils/tts.py | 19 +++-- 3 files changed, 145 insertions(+), 29 deletions(-) diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index 3019c00..80f08b5 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -692,6 +692,7 @@ async def chat(self, prompt, chat_name=None, default_choice=0): :param chat_name: Name of the chat. If None, uses self.current_chat. :param default_choice: Index of the model's response choice. """ + # pylint: disable=too-many-branches # Set chat_name chat_name = chat_name if chat_name is not None else self.current_chat chat_name = await self.__handle_chat_name(chat_name, prompt) diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index 6ed32d2..3926d1c 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -5,7 +5,7 @@ Copyright (c) 2023. All rights reserved. Created: 25.08.2023 -Last Modified: 25.08.2023 +Last Modified: 26.08.2023 Description: This file contains implementation for Audio Recorder @@ -16,6 +16,7 @@ import struct import tempfile import time +import uuid import wave import pyaudio @@ -24,24 +25,54 @@ from pydub import AudioSegment -def record_and_convert_audio(duration=10, fs=44100): +def record_and_convert_audio(duration: int = 5, frequency_sample: int = 16000): + """ + Records audio for a specified duration and converts it to MP3 format. + + This function records audio for a given duration (in seconds) with a specified frequency sample. + The audio is then saved as a temporary .wav file, converted to .mp3 format, and the .wav file is deleted. + The function returns the path to the .mp3 file. + + :param duration: The duration of the audio recording in seconds. Default is 5 seconds. + :param frequency_sample: The frequency sample rate of the audio recording. Default is 16000 Hz. + + :return: The path to the saved .mp3 file. + """ print(f"Listening beginning for {duration}s...") - myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=1) + recording = sd.rec(int(duration * frequency_sample), samplerate=frequency_sample, channels=1) sd.wait() # Wait until recording is finished print("Recording complete!") temp_dir = tempfile.gettempdir() - wav_file_path = os.path.join(temp_dir, "my_audio.wav") - sf.write(wav_file_path, myrecording, fs) - print(f"Temp audiofile saved: {wav_file_path}") - audio = AudioSegment.from_wav(wav_file_path) - os.remove(os.path.join(temp_dir, "my_audio.wav")) - mp3_file_path = os.path.join(temp_dir, "my_audio.mp3") - audio.export(mp3_file_path, format="mp3") - print("Audio converted to MP3 and stored into {mp3_file_path}") - return mp3_file_path + wave_file = f"{temp_dir}/{str(uuid.uuid4())}.wav" + sf.write(wave_file, recording, frequency_sample) + print(f"Temp audiofile saved: {wave_file}") + audio = AudioSegment.from_wav(wave_file) + os.remove(wave_file) + mp3_file = f"{temp_dir}/{str(uuid.uuid4())}.mp3" + audio.export(mp3_file, format="mp3") + print(f"Audio converted to MP3 and stored into {mp3_file}") + return mp3_file class AudioRecorder: + """ + The AudioRecorder class is for managing an instance of the audio recording and conversion process. + + Parameters: + pyaudio_obj (PyAudio): Instance of PyAudio. Default is pyaudio.PyAudio(). + threshold (int): The RMS threshold for starting the recording. Default is 15. + channels (int): The number of channels in the audio stream. Default is 1. + chunk (int): The number of frames per buffer. Default is 1024. + f_format (int): The format of the audio stream. Default is pyaudio.paInt16. + rate (int): The sample rate of the audio stream. Default is 16000 Hz. + sample_width (int): The sample width (in bytes) of the audio stream. Default is 2. + timeout_length (int): The length of the timeout for the recording (in seconds). Default is 2 seconds. + temp_dir (str): The directory for storing the temporary .wav and .mp3 files. Default is the system's temporary dir. + normalize (float): The normalization factor for the audio samples. Default is 1.0 / 32768.0. + pa_input (bool): Specifies whether the stream is an input stream. Default is True. + pa_output (bool): Specifies whether the stream is an output stream. Default is True. + """ + def __init__( self, pyaudio_obj=pyaudio.PyAudio(), @@ -50,20 +81,39 @@ def __init__( chunk=1024, f_format=pyaudio.paInt16, rate=16000, - swidth=2, + sample_width=2, timeout_length=2, temp_dir=tempfile.gettempdir(), normalize=(1.0 / 32768.0), pa_input=True, pa_output=True, ): + """ + General init. + + This method initializes an instance of the AudioRecorder class with the specified parameters. + The default values are used for any parameters that are not provided. + + :param pyaudio_obj: Instance of PyAudio. Default is pyaudio.PyAudio(). + :param threshold: The RMS threshold for starting the recording. Default is 15. + :param channels: The number of channels in the audio stream. Default is 1. + :param chunk: The number of frames per buffer. Default is 1024. + :param f_format: The format of the audio stream. Default is pyaudio.paInt16. + :param rate: The sample rate of the audio stream. Default is 16000 Hz. + :param sample_width: The sample width (in bytes) of the audio stream. Default is 2. + :param timeout_length: The length of the timeout for the recording (in seconds). Default is 2 seconds. + :param temp_dir: The directory for storing the temporary .wav and .mp3 files. Default is temp dir. + :param normalize: The normalization factor for the audio samples. Default is 1.0 / 32768.0. + :param pa_input: Specifies whether the stream is an input stream. Default is True. + :param pa_output: Specifies whether the stream is an output stream. Default is True. + """ self.___pyaudio = pyaudio_obj self.___threshold = threshold self.___channels = channels self.___chunk = chunk self.___format = f_format self.___rate = rate - self.___swidth = swidth + self.___sample_width = sample_width self.___timeout_length = timeout_length self.___temp_dir = temp_dir self.___normalize = normalize @@ -79,6 +129,22 @@ def __init__( ) def init_stream(self, f_format, channels, rate, pa_input, pa_output, frames_per_buffer): + """ + Initializes an audio stream with the specified parameters. + + This function uses PyAudio to open an audio stream with the given format, channels, rate, input, output, + and frames per buffer. + + :param f_format: The format of the audio stream. + :param channels: The number of channels in the audio stream. + :param rate: The sample rate of the audio stream. + :param pa_input: Specifies whether the stream is an input stream. A true value indicates an input stream. + :param pa_output: Specifies whether the stream is an output stream. A true value indicates an output stream. + :param frames_per_buffer: The number of frames per buffer. + :type frames_per_buffer: int + + :return: The initialized audio stream. + """ return self.___pyaudio.open( format=f_format, channels=channels, @@ -89,6 +155,16 @@ def init_stream(self, f_format, channels, rate, pa_input, pa_output, frames_per_ ) def record(self): + """ + Starts recording audio when noise is detected. + + This function starts recording audio when noise above a certain threshold is detected. + The recording continues for a specified timeout length. + The recorded audio is then saved as a .wav file, converted to .mp3 format, and the .wav file is deleted. + The function returns the path to the .mp3 file. + + :return: The path to the saved .mp3 file. + """ print("Noise detected, recording beginning") rec = [] current = time.time() @@ -106,25 +182,52 @@ def record(self): return self.convert_to_mp3(filename) def write(self, recording): - n_files = len(os.listdir(self.___temp_dir)) - filename = os.path.join(self.___temp_dir, f"{n_files}.wav") + """ + Saves the recorded audio to a .wav file. + + This function saves the recorded audio to a .wav file with a unique filename. + The .wav file is saved in the specified temporary directory. + + :param recording: The recorded audio data. + + :return: The path to the saved .wav file. + """ + filename = os.path.join(self.___temp_dir, f"{str(uuid.uuid4())}.wav") wave_form = wave.open(filename, "wb") wave_form.setnchannels(self.___channels) - wave_form.setsampwidth(self.p.get_sample_size(self.___format)) + wave_form.setsampwidth(self.___pyaudio.get_sample_size(self.___format)) wave_form.setframerate(self.___rate) wave_form.writeframes(recording) wave_form.close() return filename def convert_to_mp3(self, filename): + """ + Converts a .wav file to .mp3 format. + + This function converts a .wav file to .mp3 format. The .wav file is deleted after the conversion. + The .mp3 file is saved with a unique filename in the specified temporary directory. + + :param filename: The path to the .wav file to be converted. + + :return: The path to the saved .mp3 file. + """ audio = AudioSegment.from_wav(filename) - mp3_file_path = os.path.join(self.___temp_dir, "my_audio.mp3") + mp3_file_path = os.path.join(self.___temp_dir, f"{str(uuid.uuid4())}.mp3") audio.export(mp3_file_path, format="mp3") os.remove(filename) return mp3_file_path def listen(self): + """ + Starts listening for audio. + + This function continuously listens for audio and starts recording when the + RMS value of the audio exceeds a certain threshold. + + :return: The path to the saved .mp3 file if recording was triggered. + """ print("Listening beginning...") while True: mic_input = self.stream.read(self.___chunk) @@ -133,14 +236,23 @@ def listen(self): return self.record() def rms(self, frame): - count = len(frame) / self.___swidth + """ + Calculates the Root Mean Square (RMS) value of the audio frame. + + This function calculates the RMS value of the audio frame, which is a measure of the power in the audio signal. + + :param frame: The audio frame for which to calculate the RMS value. + + :return: The RMS value of the audio frame. + """ + count = len(frame) / self.___sample_width f_format = "%dh" % count shorts = struct.unpack(f_format, frame) sum_squares = 0.0 for sample in shorts: - n = sample * self.___normalize - sum_squares += n * n + normal_sample = sample * self.___normalize + sum_squares += normal_sample * normal_sample rms = math.pow(sum_squares / count, 0.5) return rms * 1000 diff --git a/utils/tts.py b/utils/tts.py index 75639ae..3797779 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -14,6 +14,7 @@ import os import tempfile from time import sleep +from uuid import uuid4 from gtts import gTTS from mpyg321.mpyg321 import MPyg321Player @@ -63,17 +64,19 @@ def __process_via_gtts(self, text): temp_dir = tempfile.gettempdir() # gtts tts = gTTS(text, lang=self.___lang) - tts.save(f"{temp_dir}/raw.mp3") - audio = AudioSegment.from_file(f"{temp_dir}/raw.mp3", format="mp3") - new = audio.speedup(self.___speedup) # speed up by 2x - os.remove(f"{temp_dir}/raw.mp3") - new.export(f"{temp_dir}/response.mp3", format="mp3") + raw_file = f"{temp_dir}/{str(uuid4())}.mp3" + tts.save(raw_file) + audio = AudioSegment.from_file( raw_file, format="mp3") + new = audio.speedup(self.___speedup) + os.remove(raw_file) + response_file = f"{temp_dir}/{str(uuid4())}.mp3" + new.export(response_file, format="mp3") # player - self.___player.play_song(f"{temp_dir}/response.mp3") - audio = MP3(f"{temp_dir}/response.mp3") + self.___player.play_song(response_file) + audio = MP3(response_file) sleep(audio.info.length) self.___player.stop() - os.remove(f"{temp_dir}/response.mp3") + os.remove(response_file) def __process_via_pytts(self, text): """ From 3636b8ae0655ee751d10b4eeecce7bcba48ebf8f Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:12:31 +0200 Subject: [PATCH 09/32] v0.1 --- utils/audio_recorder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index 3926d1c..2780a5d 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -54,6 +54,7 @@ def record_and_convert_audio(duration: int = 5, frequency_sample: int = 16000): return mp3_file +# pylint: disable=too-many-instance-attributes class AudioRecorder: """ The AudioRecorder class is for managing an instance of the audio recording and conversion process. @@ -246,7 +247,7 @@ def rms(self, frame): :return: The RMS value of the audio frame. """ count = len(frame) / self.___sample_width - f_format = "%dh" % count + f_format = f"{count}dh" shorts = struct.unpack(f_format, frame) sum_squares = 0.0 From 22da98183da6d708babb0776e553864f99a014cb Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:22:49 +0200 Subject: [PATCH 10/32] v0.1 --- .flake8 | 2 +- __init__.py | 1 + __main__.py | 16 ++++++++-------- pyproject.toml | 10 +++++++++- setup.cfg | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/.flake8 b/.flake8 index b81b197..60648bc 100644 --- a/.flake8 +++ b/.flake8 @@ -4,4 +4,4 @@ inline-quotes = " multiline-quotes = """ docstring-quotes = """ avoid-escape = False -ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507 +ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR101,DAR201,WPS112,F401,WPS300,WPS412,WPS110,DAR301 diff --git a/__init__.py b/__init__.py index a9edf77..df9759d 100644 --- a/__init__.py +++ b/__init__.py @@ -10,6 +10,7 @@ Description: This file is init point for project-wide structure. """ + # Engines from .openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import from .openai_engine.dalle import DALLE # pylint: disable=unused-import diff --git a/__main__.py b/__main__.py index 745e0f1..f404e80 100644 --- a/__main__.py +++ b/__main__.py @@ -11,12 +11,12 @@ This file is entry point for project-wide structure. """ # Engines -from .openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import -from .openai_engine.dalle import DALLE # pylint: disable=unused-import +from openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import +from openai_engine.dalle import DALLE # pylint: disable=unused-import # Utils -from .utils.tts import CustomTTS # pylint: disable=unused-import -from .utils.transcriptors import CustomTranscriptor # pylint: disable=unused-import -from .utils.translators import CustomTranslator # pylint: disable=unused-import -from .utils.audio_recorder import AudioRecorder, record_and_convert_audio # pylint: disable=unused-import -from .utils.logger_config import setup_logger # pylint: disable=unused-import -from .utils.other import is_heroku_environment # pylint: disable=unused-import +from utils.tts import CustomTTS # pylint: disable=unused-import +from utils.transcriptors import CustomTranscriptor # pylint: disable=unused-import +from utils.translators import CustomTranslator # pylint: disable=unused-import +from utils.audio_recorder import AudioRecorder, record_and_convert_audio # pylint: disable=unused-import +from utils.logger_config import setup_logger # pylint: disable=unused-import +from utils.other import is_heroku_environment # pylint: disable=unused-import diff --git a/pyproject.toml b/pyproject.toml index 8610684..4fb58c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,15 @@ extend-ignore = """ D400, WPS317, S101, - WPS507 + WPS507, + DAR101, + DAR201, + WPS112, + F401, + WPS300, + WPS412, + WPS110, + DAR301 """ [tool.pylint] diff --git a/setup.cfg b/setup.cfg index d2dfd68..494b537 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [flake8] max-line-length = 120 -extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507 +extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,WPS110,DAR301 From e77f51e59a819e8a8c9aad94c6712dcc4745a07d Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:24:05 +0200 Subject: [PATCH 11/32] v0.1 --- .flake8 | 2 +- pyproject.toml | 1 - setup.cfg | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 60648bc..fb2cf9f 100644 --- a/.flake8 +++ b/.flake8 @@ -4,4 +4,4 @@ inline-quotes = " multiline-quotes = """ docstring-quotes = """ avoid-escape = False -ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR101,DAR201,WPS112,F401,WPS300,WPS412,WPS110,DAR301 +ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR101,DAR201,WPS112,F401,WPS300,WPS412,DAR301 diff --git a/pyproject.toml b/pyproject.toml index 4fb58c6..c6ff551 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,6 @@ extend-ignore = """ F401, WPS300, WPS412, - WPS110, DAR301 """ diff --git a/setup.cfg b/setup.cfg index 494b537..2243aa9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [flake8] max-line-length = 120 -extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,WPS110,DAR301 +extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,DAR301 From a306e117cf4101fa3ff92b8af3fb9839d82c97a7 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:28:28 +0200 Subject: [PATCH 12/32] v0.1 --- .flake8 | 2 +- __main__.py | 1 + pyproject.toml | 4 +++- setup.cfg | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index fb2cf9f..ed3e65b 100644 --- a/.flake8 +++ b/.flake8 @@ -4,4 +4,4 @@ inline-quotes = " multiline-quotes = """ docstring-quotes = """ avoid-escape = False -ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR101,DAR201,WPS112,F401,WPS300,WPS412,DAR301 +ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR101,DAR201,WPS112,F401,WPS300,WPS412,DAR301,D401,D205 diff --git a/__main__.py b/__main__.py index f404e80..372d9b0 100644 --- a/__main__.py +++ b/__main__.py @@ -10,6 +10,7 @@ Description: This file is entry point for project-wide structure. """ + # Engines from openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import from openai_engine.dalle import DALLE # pylint: disable=unused-import diff --git a/pyproject.toml b/pyproject.toml index c6ff551..542b177 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,9 @@ extend-ignore = """ F401, WPS300, WPS412, - DAR301 + DAR301, + D401, + D205 """ [tool.pylint] diff --git a/setup.cfg b/setup.cfg index 2243aa9..fd356a1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [flake8] max-line-length = 120 -extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,DAR301 +extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,DAR301D401,D205 From dbbae8a320ec7c35b5289d66975b3326bdfd026e Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:33:32 +0200 Subject: [PATCH 13/32] v0.1 --- __init__.py | 1 + __main__.py | 1 + openai_engine/chatgpt.py | 66 +++++++++++++++++++--------------------- openai_engine/dalle.py | 50 +++++++++++++++++++----------- utils/audio_recorder.py | 26 ++++++++-------- utils/translators.py | 1 + utils/tts.py | 4 +-- 7 files changed, 81 insertions(+), 68 deletions(-) diff --git a/__init__.py b/__init__.py index df9759d..74a8069 100644 --- a/__init__.py +++ b/__init__.py @@ -14,6 +14,7 @@ # Engines from .openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import from .openai_engine.dalle import DALLE # pylint: disable=unused-import + # Utils from .utils.tts import CustomTTS # pylint: disable=unused-import from .utils.transcriptors import CustomTranscriptor # pylint: disable=unused-import diff --git a/__main__.py b/__main__.py index 372d9b0..6f4ab08 100644 --- a/__main__.py +++ b/__main__.py @@ -14,6 +14,7 @@ # Engines from openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import from openai_engine.dalle import DALLE # pylint: disable=unused-import + # Utils from utils.tts import CustomTTS # pylint: disable=unused-import from utils.transcriptors import CustomTranscriptor # pylint: disable=unused-import diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index 80f08b5..8d221fa 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -29,10 +29,10 @@ class GPTStatistics: """ def __init__( - self, - prompt_tokens: int = 0, - completion_tokens: int = 0, - total_tokens: int = 0, + self, + prompt_tokens: int = 0, + completion_tokens: int = 0, + total_tokens: int = 0, ): """ Constructs all the necessary attributes for the GPTStatistics object. @@ -194,30 +194,30 @@ class ChatGPT: """ def __init__( - # pylint: disable=too-many-locals - self, - auth_token: str, - organization: str, - model: str = COMPLETIONS[0], - choices: int = 1, - temperature: float = 1, - top_p: float = 1, - stream: bool = False, - stop: str = None, - max_tokens: int = 1024, - presence_penalty: float = 0, - frequency_penalty: float = 0, - logit_bias: map = None, - user: str = "", - functions: list = None, - function_call: str = None, - history_length: int = 5, - chats: dict = None, - current_chat: str = None, - prompt_method: bool = False, - logger: logging.Logger = None, - statistics: GPTStatistics = GPTStatistics(), - system_settings: str = None, + # pylint: disable=too-many-locals + self, + auth_token: str, + organization: str, + model: str = COMPLETIONS[0], + choices: int = 1, + temperature: float = 1, + top_p: float = 1, + stream: bool = False, + stop: str = None, + max_tokens: int = 1024, + presence_penalty: float = 0, + frequency_penalty: float = 0, + logit_bias: map = None, + user: str = "", + functions: list = None, + function_call: str = None, + history_length: int = 5, + chats: dict = None, + current_chat: str = None, + prompt_method: bool = False, + logger: logging.Logger = None, + statistics: GPTStatistics = GPTStatistics(), + system_settings: str = None, ): """ General init @@ -721,7 +721,7 @@ async def chat(self, prompt, chat_name=None, default_choice=0): pass else: # Get last 'history_length' messages - messages = self.chats[chat_name][-self.history_length:] + messages = self.chats[chat_name][-self.history_length :] messages.insert(0, {"role": "system", "content": self.system_settings}) try: @@ -791,7 +791,7 @@ async def transcript(self, file, prompt=None, language="en", response_format="te language=language, response_format=response_format, temperature=self.temperature, - **kwargs + **kwargs, ) return transcription @@ -811,10 +811,6 @@ async def translate(self, file, prompt=None, response_format="text"): if prompt is not None: kwargs["prompt"] = prompt translation = await openai.Audio.atranslate( - model=TRANSLATIONS[0], - file=file, - response_format=response_format, - temperature=self.temperature, - **kwargs + model=TRANSLATIONS[0], file=file, response_format=response_format, temperature=self.temperature, **kwargs ) return translation diff --git a/openai_engine/dalle.py b/openai_engine/dalle.py index be13707..ab03165 100644 --- a/openai_engine/dalle.py +++ b/openai_engine/dalle.py @@ -35,9 +35,16 @@ class DALLE: logger (logging.Logger, optional): default logger. Default is None. """ - def __init__(self, auth_token: str, organization: str, default_count: int = 1, default_size: str = "512x512", - default_file_format: str = 'PNG', - user: str = '', logger: logging.Logger = None): + def __init__( + self, + auth_token: str, + organization: str, + default_count: int = 1, + default_size: str = "512x512", + default_file_format: str = "PNG", + user: str = "", + logger: logging.Logger = None, + ): """ General init @@ -165,8 +172,9 @@ async def create_image(self, prompt): :return: A PIL.Image object created from the image data received from the API. """ - response = await openai.Image.acreate(prompt=prompt, n=self.default_count, size=self.default_size, - user=self.user) + response = await openai.Image.acreate( + prompt=prompt, n=self.default_count, size=self.default_size, user=self.user + ) image_url = response["data"][0]["url"] async with aiohttp.ClientSession() as session: async with session.get(image_url) as resp: @@ -183,7 +191,7 @@ def show_image(image): image.show() def save_image(self, image, filename=None, file_format=None): - """ Saves an image to a file. + """Saves an image to a file. :param image: A PIL.Image object to be saved. :param filename: The name of the file where the image will be saved. @@ -195,7 +203,7 @@ def save_image(self, image, filename=None, file_format=None): if file_format is None: file_format = self.default_file_format if filename is None: - filename = os.path.join(tempfile.gettempdir(), f'{uuid.uuid4()}.{file_format.lower()}') + filename = os.path.join(tempfile.gettempdir(), f"{uuid.uuid4()}.{file_format.lower()}") try: image.save(filename, format=format) except Exception as error: @@ -211,8 +219,9 @@ async def create_variation_from_file(self, file): :return: A PIL.Image object created from the image data received from the API. """ - response = await openai.Image.acreate_variation(image=file, n=self.default_count, size=self.default_size, - user=self.user) + response = await openai.Image.acreate_variation( + image=file, n=self.default_count, size=self.default_size, user=self.user + ) image_url = response["data"][0]["url"] async with aiohttp.ClientSession() as session: async with session.get(image_url) as resp: @@ -231,9 +240,9 @@ async def create_variation_from_url(self, url): async with session.get(url) as resp: image_data = await resp.read() - response = await openai.Image.acreate_variation(image=BytesIO(image_data), n=self.default_count, - size=self.default_size, - user=self.user) + response = await openai.Image.acreate_variation( + image=BytesIO(image_data), n=self.default_count, size=self.default_size, user=self.user + ) image_url = response["data"][0]["url"] async with aiohttp.ClientSession() as session: async with session.get(image_url) as resp: @@ -250,9 +259,9 @@ async def edit_image_from_file(self, file, prompt, mask=None): If provided, the mask will be applied to the image. :return: A PIL.Image object created from the image data received from the API. """ - response = await openai.Image.acreate_edit(image=file, prompt=prompt, mask=mask, - n=self.default_count, size=self.default_size, - user=self.user) + response = await openai.Image.acreate_edit( + image=file, prompt=prompt, mask=mask, n=self.default_count, size=self.default_size, user=self.user + ) image_url = response["data"][0]["url"] async with aiohttp.ClientSession() as session: async with session.get(image_url) as resp: @@ -275,9 +284,14 @@ async def edit_image_from_url(self, url, prompt, mask_url=None): async with aiohttp.ClientSession() as session: async with session.get(mask_url) as resp: mask_data = await resp.read() - response = await openai.Image.acreate_edit(image=BytesIO(image_data), prompt=prompt, mask=BytesIO(mask_data), - n=self.default_count, size=self.default_size, - user=self.user) + response = await openai.Image.acreate_edit( + image=BytesIO(image_data), + prompt=prompt, + mask=BytesIO(mask_data), + n=self.default_count, + size=self.default_size, + user=self.user, + ) image_url = response["data"][0]["url"] async with aiohttp.ClientSession() as session: async with session.get(image_url) as resp: diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index 2780a5d..d71b561 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -75,19 +75,19 @@ class AudioRecorder: """ def __init__( - self, - pyaudio_obj=pyaudio.PyAudio(), - threshold=15, - channels=1, - chunk=1024, - f_format=pyaudio.paInt16, - rate=16000, - sample_width=2, - timeout_length=2, - temp_dir=tempfile.gettempdir(), - normalize=(1.0 / 32768.0), - pa_input=True, - pa_output=True, + self, + pyaudio_obj=pyaudio.PyAudio(), + threshold=15, + channels=1, + chunk=1024, + f_format=pyaudio.paInt16, + rate=16000, + sample_width=2, + timeout_length=2, + temp_dir=tempfile.gettempdir(), + normalize=(1.0 / 32768.0), + pa_input=True, + pa_output=True, ): """ General init. diff --git a/utils/translators.py b/utils/translators.py index e5fc4b3..c4d6ec9 100644 --- a/utils/translators.py +++ b/utils/translators.py @@ -18,6 +18,7 @@ class CustomTranslator(GoogleTranslator): """ This class implements wrapper for GoogleTranslator """ + def __init__(self, source, target, **kwargs): """ General init. diff --git a/utils/tts.py b/utils/tts.py index 3797779..38ef9c6 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -36,7 +36,7 @@ class CustomTTS: """ def __init__( - self, method="google", lang="en", speedup=1.3, frame=0.1, voice="com.apple.voice.enhanced.ru-RU.Katya" + self, method="google", lang="en", speedup=1.3, frame=0.1, voice="com.apple.voice.enhanced.ru-RU.Katya" ): """ General init. @@ -66,7 +66,7 @@ def __process_via_gtts(self, text): tts = gTTS(text, lang=self.___lang) raw_file = f"{temp_dir}/{str(uuid4())}.mp3" tts.save(raw_file) - audio = AudioSegment.from_file( raw_file, format="mp3") + audio = AudioSegment.from_file(raw_file, format="mp3") new = audio.speedup(self.___speedup) os.remove(raw_file) response_file = f"{temp_dir}/{str(uuid4())}.mp3" From 7bbb27029dc05a7509860362107b0f86dd0d375d Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:35:54 +0200 Subject: [PATCH 14/32] v0.1 --- utils/audio_recorder.py | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index d71b561..b6b4a96 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -172,7 +172,6 @@ def record(self): end = time.time() + self.___timeout_length while current <= end: - data = self.stream.read(self.___chunk) if self.rms(data) >= self.___threshold: end = time.time() + self.___timeout_length From 1b0897bd9ae009c48435bbc91c89923835a2b710 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:36:51 +0200 Subject: [PATCH 15/32] v0.1 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index fd356a1..e420340 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [flake8] max-line-length = 120 -extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,DAR301D401,D205 +extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,DAR301,D401,D205 From 884c2d35013e474feb4eb08a61ff193d65a089b2 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 11:54:35 +0200 Subject: [PATCH 16/32] v0.1 --- .flake8 | 2 +- openai_engine/chatgpt.py | 10 ++++------ openai_engine/models.py | 19 ++++++++++++------- pyproject.toml | 7 ++++++- setup.cfg | 2 +- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.flake8 b/.flake8 index ed3e65b..9096043 100644 --- a/.flake8 +++ b/.flake8 @@ -4,4 +4,4 @@ inline-quotes = " multiline-quotes = """ docstring-quotes = """ avoid-escape = False -ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR101,DAR201,WPS112,F401,WPS300,WPS412,DAR301,D401,D205 +ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR101,DAR201,WPS112,F401,WPS300,WPS412,DAR301,D401,D205,WPS615,I004,WPS110,WPS420,C812 diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index 8d221fa..bcf2e0b 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -713,7 +713,7 @@ async def chat(self, prompt, chat_name=None, default_choice=0): full_prompt += response["choices"][default_choice]["delta"]["content"] else: full_prompt += response["choices"][default_choice]["message"]["content"] - if finish_reason in ["stop", "length", "function_call"]: + if finish_reason is not None: yield "" else: break @@ -734,7 +734,7 @@ async def chat(self, prompt, chat_name=None, default_choice=0): full_prompt += response["choices"][default_choice]["delta"]["content"] else: full_prompt += response["choices"][default_choice]["message"]["content"] - if finish_reason in ["stop", "length", "function_call"]: + if finish_reason is not None: yield "" else: break @@ -785,7 +785,7 @@ async def transcript(self, file, prompt=None, language="en", response_format="te kwargs = {} if prompt is not None: kwargs["prompt"] = prompt - transcription = await openai.Audio.atranscribe( + return await openai.Audio.atranscribe( model=TRANSCRIPTIONS[0], file=file, language=language, @@ -793,7 +793,6 @@ async def transcript(self, file, prompt=None, language="en", response_format="te temperature=self.temperature, **kwargs, ) - return transcription async def translate(self, file, prompt=None, response_format="text"): """ @@ -810,7 +809,6 @@ async def translate(self, file, prompt=None, response_format="text"): kwargs = {} if prompt is not None: kwargs["prompt"] = prompt - translation = await openai.Audio.atranslate( + return await openai.Audio.atranslate( model=TRANSLATIONS[0], file=file, response_format=response_format, temperature=self.temperature, **kwargs ) - return translation diff --git a/openai_engine/models.py b/openai_engine/models.py index 53d6c83..371ffc7 100644 --- a/openai_engine/models.py +++ b/openai_engine/models.py @@ -11,7 +11,7 @@ This file contains OpenAI constants """ -COMPLETIONS = [ +COMPLETIONS = ( "gpt-4", "gpt-4-0613", "gpt-4-32k", @@ -20,9 +20,14 @@ "gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613", -] -TRANSCRIPTIONS = ["whisper-1"] -TRANSLATIONS = ["whisper-1"] -FINE_TUNES = ["davinci", "curie", "babbage", "ada"] -EMBEDDINGS = ["text-embedding-ada-002", "text-similarity-*-001", "text-search-*-*-001", "code-search-*-*-001"] -MODERATIONS = ["text-moderation-stable", "text-moderation-latest"] +) +TRANSCRIPTIONS = ("whisper-1",) +TRANSLATIONS = ("whisper-1",) +FINE_TUNES = ("davinci", "curie", "babbage", "ada") +EMBEDDINGS = ( + "text-embedding-ada-002", + "text-similarity-*-001", + "text-search-*-*-001", + "code-search-*-*-001" +) +MODERATIONS = ("text-moderation-stable", "text-moderation-latest") diff --git a/pyproject.toml b/pyproject.toml index 542b177..78b1e78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,12 @@ extend-ignore = """ WPS412, DAR301, D401, - D205 + D205, + WPS615, + I004, + WPS110, + WPS420, + C812 """ [tool.pylint] diff --git a/setup.cfg b/setup.cfg index e420340..fb69a11 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [flake8] max-line-length = 120 -extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,DAR301,D401,D205 +extend-ignore = Q000,WPS306,I001,I005,WPS229,D400,WPS317,S101,WPS507,DAR201,DAR101,WPS112,F401,WPS300,WPS412,DAR301,D401,D205,WPS615,I004,WPS110,WPS420,C812 From 2dfa322f4451436d1b33f7b5530b5bbbfc94afb8 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 12:00:40 +0200 Subject: [PATCH 17/32] v0.1 --- utils/tts.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/utils/tts.py b/utils/tts.py index 38ef9c6..da876bf 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -95,6 +95,14 @@ def __process_via_pytts(self, text): engine.endLoop() + def get_pytts_voices_list(self): + """ + Returns list of possible voices + + :return: (list) of possible voices + """ + return self.___pytts.getProperty("voices") + async def process(self, text): """ Converts text to speach using pre-defined model @@ -104,12 +112,4 @@ async def process(self, text): if "google" in self.___method: self.__process_via_gtts(text) else: - self.__process_via_pytts(text) - - def get_pytts_voices_list(self): - """ - Returns list of possible voices - - :return: (list) of possible voices - """ - return self.___pytts.getProperty("voices") + self.__process_via_pytts(text) \ No newline at end of file From b14a0cb591b59b87c63caf6140933e1fc216e4a4 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Sat, 26 Aug 2023 18:03:27 +0200 Subject: [PATCH 18/32] v0.1 --- openai_engine/chatgpt.py | 121 ++++++++++++++++++++++++++++++++++----- openai_engine/dalle.py | 69 +++++++++++++++++++++- utils/audio_recorder.py | 2 +- utils/transcriptors.py | 3 +- utils/tts.py | 2 +- 5 files changed, 179 insertions(+), 18 deletions(-) diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index bcf2e0b..f26590d 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -11,6 +11,7 @@ This file contains implementation for ChatGPT """ +import json import logging import openai @@ -181,6 +182,7 @@ class ChatGPT: user (str, optional): The user ID. Default is ''. functions (list, optional): The list of functions. Default is None. function_call (str, optional): The function call. Default is None. + function_dict (dict, optional): Dict of functions. Default is None. history_length (int, optional): Length of history. Default is 5. chats (dict, optional): Chats dictionary, contains all chat. Default is None. current_chat (str, optional): Default chat will be used. Default is None. @@ -218,6 +220,7 @@ def __init__( logger: logging.Logger = None, statistics: GPTStatistics = GPTStatistics(), system_settings: str = None, + function_dict: dict = None, ): """ General init @@ -237,6 +240,7 @@ def __init__( :param user: The user ID. Default is ''. :param functions: The list of functions. Default is None. :param function_call: The function call. Default is None. + :param function_dict: Dict of functions. Default is None. :param history_length: Length of history. Default is 5. :param chats: Chats dictionary, contains all chat. Default is None. :param current_chat: Default chat will be used. Default is None. @@ -258,6 +262,7 @@ def __init__( self.___user = user self.___functions = functions self.___function_call = function_call + self.___function_dict = function_dict self.___history_length = history_length self.___chats = chats if chats else {} self.___current_chat = current_chat @@ -512,6 +517,24 @@ def function_call(self, value): """ self.___function_call = value + @property + def function_dict(self): + """ + Getter for function_dict. + + :return: The function dict. + """ + return self.___function_dict + + @function_dict.setter + def function_dict(self, value): + """ + Setter for function_dict. + + :param value: The new function dict. + """ + self.___function_dict = value + @property def history_length(self): """ @@ -620,12 +643,13 @@ def logger(self, value): """ self.___logger = value - async def process_chat(self, prompt, default_choice=None): + async def process_chat(self, prompt, default_choice=0, chat_name=None): """ Creates a new chat completion for the provided messages and parameters. :param prompt: The prompt to pass to the model. :param default_choice: Default number of choice to monitor for stream end. By default, is None. + :param chat_name: Chat name for function tracking. Should be handled by caller. By default, is None. :return: Returns answers by chunk if 'stream' is false True, otherwise return complete answer. """ @@ -655,18 +679,76 @@ async def process_chat(self, prompt, default_choice=None): params["messages"] = prompt # Get response + func_response = None + func_call = dict() if self.stream: try: async for chunk in await openai.ChatCompletion.acreate(**params): - if default_choice is not None: + if "function_call" in chunk["choices"][default_choice]["delta"]: + raw_call = chunk["choices"][default_choice]["delta"]["function_call"] + for key, value in raw_call.items(): + if key in func_call and isinstance(value, str): + func_call[key] += value + elif key in func_call and isinstance(func_call[key], dict) and isinstance(value, dict): + func_call[key].update(value) + else: + func_call[key] = value + if chunk["choices"][default_choice]["finish_reason"] is not None: + if chunk["choices"][default_choice]["finish_reason"] == 'function_call': + print(func_call) + func_response = await self.process_function(function_call=func_call) + break + if "content" in chunk["choices"][default_choice]["delta"]: + if chunk["choices"][default_choice]["delta"]["content"]: + yield chunk + else: + continue + else: + continue + except GeneratorExit: + pass + try: + if func_response: + # Save to history + if chat_name: + self.chats[chat_name].append(func_response) + # Add new prompt + if self.prompt_method: + params.pop("prompt", None) + params["messages"] = list() + params["messages"].append(func_response) + else: + params["messages"].append(func_response) + async for chunk in await openai.ChatCompletion.acreate(**params): + yield chunk if chunk["choices"][default_choice]["finish_reason"] is not None: break - yield chunk except GeneratorExit: pass else: response = await openai.ChatCompletion.acreate(**params) - yield response + if response["choices"][default_choice]["finish_reason"] == 'function_call': + func_response = await self.process_function( + function_call=response["choices"][default_choice]["message"]["function_call"] + ) + try: + if func_response: + # Save to history + if chat_name: + self.chats[chat_name].append(func_response) + # Add new prompt + if self.prompt_method: + params.pop("prompt", None) + params["messages"] = list() + params["messages"].append(func_response) + else: + params["messages"].append(func_response) + response = await openai.ChatCompletion.acreate(**params) + yield response + else: + yield response + except GeneratorExit: + pass async def __handle_chat_name(self, chat_name, prompt): """ @@ -693,13 +775,6 @@ async def chat(self, prompt, chat_name=None, default_choice=0): :param default_choice: Index of the model's response choice. """ # pylint: disable=too-many-branches - # Set chat_name - chat_name = chat_name if chat_name is not None else self.current_chat - chat_name = await self.__handle_chat_name(chat_name, prompt) - - # Add new message to chat - self.chats[chat_name].append({"role": "user", "content": prompt}) - # Call process_chat full_prompt = "" if self.prompt_method: @@ -720,12 +795,19 @@ async def chat(self, prompt, chat_name=None, default_choice=0): except GeneratorExit: pass else: + # Set chat_name + chat_name = chat_name if chat_name is not None else self.current_chat + chat_name = await self.__handle_chat_name(chat_name, prompt) + # Add new message to chat + self.chats[chat_name].append({"role": "user", "content": prompt}) # Get last 'history_length' messages - messages = self.chats[chat_name][-self.history_length :] + messages = self.chats[chat_name][-self.history_length:] messages.insert(0, {"role": "system", "content": self.system_settings}) try: - async for response in self.process_chat(prompt=messages, default_choice=default_choice): + async for response in self.process_chat( + prompt=messages, default_choice=default_choice, chat_name=chat_name + ): if isinstance(response, dict): finish_reason = response["choices"][default_choice].get("finish_reason", "") yield response @@ -812,3 +894,16 @@ async def translate(self, file, prompt=None, response_format="text"): return await openai.Audio.atranslate( model=TRANSLATIONS[0], file=file, response_format=response_format, temperature=self.temperature, **kwargs ) + + async def process_function(self, function_call): + """ + Process function requested by ChatGPT. + + :param function_call: Function name and arguments. In JSON format. + + :return: transcription (text, json, srt, verbose_json or vtt) + """ + function_name = function_call["name"] + function_to_call = self.function_dict[function_name] + function_response = function_to_call(**json.loads(function_call["arguments"])) + return {"role": "function", "name": function_name, "content": function_response} diff --git a/openai_engine/dalle.py b/openai_engine/dalle.py index ab03165..4f75de1 100644 --- a/openai_engine/dalle.py +++ b/openai_engine/dalle.py @@ -17,6 +17,7 @@ from io import BytesIO import aiohttp +import asyncio import openai from PIL import Image @@ -175,12 +176,76 @@ async def create_image(self, prompt): response = await openai.Image.acreate( prompt=prompt, n=self.default_count, size=self.default_size, user=self.user ) - image_url = response["data"][0]["url"] + return response["data"] + + def screate_image(self, prompt): # FIXME let it be till experiment, then remove + """ + Creates an image using DALL-E Image API. + + :param prompt: The prompt to be used for image creation. + + :return: A PIL.Image object created from the image data received from the API. + """ + response = openai.Image.create( + prompt=prompt, n=self.default_count, size=self.default_size, user=self.user + ) + return response["data"] + + async def create_image_url(self, prompt): + """ + Creates an image using DALL-E Image API, returns list of URLs with images. + + :param prompt: The prompt to be used for image creation. + + :return: list of URLs + """ + image_urls = list() + for items in await self.create_image(prompt): + image_urls.append(items['url']) + return image_urls + + def screate_image_url(self, prompt): # FIXME let it be till experiment, then remove + """ + Creates an image using DALL-E Image API, returns list of URLs with images. (SYNC) + + :param prompt: The prompt to be used for image creation. + + :return: list of URLs + """ + image_urls = list() + for items in self.screate_image(prompt): + image_urls.append(items['url']) + return image_urls + + @staticmethod + async def convert_image_from_url_to_bytes(url): + """ + Converts image from URL to bytes format. + + :param url: URL of image. + + :return: URL + """ async with aiohttp.ClientSession() as session: - async with session.get(image_url) as resp: + async with session.get(url) as resp: image_data = await resp.read() return Image.open(BytesIO(image_data)) + async def create_image_data(self, prompt): + """ + Creates an image using DALL-E Image API, returns list of images (bytes format). + + :param prompt: The prompt to be used for image creation. + + :return: list of images (bytes format). + """ + tasks = [] + async for items in await self.create_image(prompt): + task = asyncio.ensure_future(self.convert_image_from_url_to_bytes(items['url'])) + tasks.append(task) + image_data = await asyncio.gather(*tasks) + return image_data + @staticmethod def show_image(image): """ diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index b6b4a96..b82da80 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -246,7 +246,7 @@ def rms(self, frame): :return: The RMS value of the audio frame. """ count = len(frame) / self.___sample_width - f_format = f"{count}dh" + f_format = "%dh" % count shorts = struct.unpack(f_format, frame) sum_squares = 0.0 diff --git a/utils/transcriptors.py b/utils/transcriptors.py index d1bf2f6..9661a88 100644 --- a/utils/transcriptors.py +++ b/utils/transcriptors.py @@ -54,7 +54,8 @@ def transcript(self): :return: transcripted text (string). """ print("Listening beginning...") - audio = self.___recognizer.listen(self.___source, timeout=5) + with self.___source as source: + audio = self.___recognizer.listen(source, timeout=5) user_input = None try: diff --git a/utils/tts.py b/utils/tts.py index da876bf..31b675a 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -112,4 +112,4 @@ async def process(self, text): if "google" in self.___method: self.__process_via_gtts(text) else: - self.__process_via_pytts(text) \ No newline at end of file + self.__process_via_pytts(text) From 116032e4f8cf64b8de80d83ef324cb1e1abc0f10 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Mon, 28 Aug 2023 20:14:55 +0200 Subject: [PATCH 19/32] v0.2 --- CHANGELOG.md | 4 + __init__.py | 1 + __main__.py | 1 + leonardo_engine/TODO | 1 + leonardo_engine/__init__.py | 0 leonardo_engine/leonardo.py | 409 ++++ leonardo_engine/models.py | 3608 +++++++++++++++++++++++++++++++++++ openai_engine/TODO | 3 +- requirements.txt | 2 + 9 files changed, 4028 insertions(+), 1 deletion(-) create mode 100644 leonardo_engine/TODO create mode 100644 leonardo_engine/__init__.py create mode 100644 leonardo_engine/leonardo.py create mode 100644 leonardo_engine/models.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f8b28de..77a5416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - audio_recorder - logger - transcriptors +- translators - tts +## [0.2] - 2023-08-28 +### Added +- Leonardo API diff --git a/__init__.py b/__init__.py index 74a8069..dc38a69 100644 --- a/__init__.py +++ b/__init__.py @@ -12,6 +12,7 @@ """ # Engines +from .leonardo_engine.leonardo import Leonardo # pylint: disable=unused-import from .openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import from .openai_engine.dalle import DALLE # pylint: disable=unused-import diff --git a/__main__.py b/__main__.py index 6f4ab08..303e34c 100644 --- a/__main__.py +++ b/__main__.py @@ -12,6 +12,7 @@ """ # Engines +from leonardo_engine.leonardo import Leonardo # pylint: disable=unused-import from openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import from openai_engine.dalle import DALLE # pylint: disable=unused-import diff --git a/leonardo_engine/TODO b/leonardo_engine/TODO new file mode 100644 index 0000000..c38e3b5 --- /dev/null +++ b/leonardo_engine/TODO @@ -0,0 +1 @@ +- Add logger \ No newline at end of file diff --git a/leonardo_engine/__init__.py b/leonardo_engine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leonardo_engine/leonardo.py b/leonardo_engine/leonardo.py new file mode 100644 index 0000000..8171294 --- /dev/null +++ b/leonardo_engine/leonardo.py @@ -0,0 +1,409 @@ +import json +import logging +import os + +import asyncio +import aiofiles +import aiohttp + + +class Leonardo: + """ + This class is for managing and interacting with Leonardo.ai service. + + Parameters: + auth_token (str): Auth Bearer token. Required. + logger (logging.Logger, optional): default logger. Default is None. + """ + + def __init__(self, auth_token: str, logger: logging.Logger = None): + """ + Constructs all the necessary attributes for the Leonardo object. + + :param auth_token: Auth Bearer token. Required. + :param logger: default logger. Default is None. + """ + self.___session = aiohttp.ClientSession(headers={"Authorization": f"Bearer {auth_token}"}) + self.___logger = logger + self.___get_headers = {"content-type": "application/json"} + self.___post_headers = {"accept": "application/json", "content-type": "application/json"} + + async def get_user_info(self): + """ + This endpoint will return your user information, including your user ID. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/me" + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def post_generations( + self, + prompt: str, + negative_prompt: str = None, + model_id: str = None, + sd_version: str = None, + num_images: int = 4, + width: int = 512, + height: int = 512, + num_inference_steps: int = None, + guidance_scale: int = None, + init_generation_image_id: str = None, + init_image_id: str = None, + init_strength: float = None, + scheduler: str = None, + preset_style: str = None, + tiling: bool = False, + public: bool = False, + prompt_magic: bool = True, + control_net: bool = None, + control_net_type: str = None, + ): + """ + This endpoint will generate images. + + :param prompt: The prompt used to generate images. + :param negative_prompt: The negative prompt used for the image generation. + :param model_id: The model ID used for the image generation. + :param sd_version: The base version of stable diffusion to use if not using a custom model. + :param num_images: The number of images to generate. Default is 4. + :param width: The width of the images. Default is 512px. + :param height: The height of the images. Default is 512px. + :param num_inference_steps: The number of inference steps to use for the generation. + :param guidance_scale: How strongly the generation should reflect the prompt. + :param init_generation_image_id: The ID of an existing image to use in image2image. + :param init_image_id: The ID of an Init Image to use in image2image. + :param init_strength: How strongly the generated images should reflect the original image in image2image. + :param scheduler: The scheduler to generate images with. + :param preset_style: The style to generate images with. + :param tiling: Whether the generated images should tile on all axis. Default is False. + :param public: Whether the generated images should show in the community feed. Default is False. + :param prompt_magic: Enable to use Prompt Magic. Default is True. + :param control_net: Enable to use ControlNet. Requires an init image to be provided. + Requires a model based on SD v1.5 + :param control_net_type: The type of ControlNet to use. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/generations" + payload = { + "prompt": prompt, + "negative_prompt": negative_prompt, + "modelId": model_id, + "sd_version": sd_version, + "num_images": num_images, + "width": width, + "height": height, + "num_inference_steps": num_inference_steps, + "guidance_scale": guidance_scale, + "init_generation_image_id": init_generation_image_id, + "init_image_id": init_image_id, + "init_strength": init_strength, + "scheduler": scheduler, + "presetStyle": preset_style, + "tiling": tiling, + "public": public, + "promptMagic": prompt_magic, + "controlNet": control_net, + "controlNetType": control_net_type, + } + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + return await response.json() + + async def get_single_generation(self, generation_id: str): + """ + This endpoint will provide information about a specific generation. + + :param generation_id: The ID of the generation to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def delete_single_generation(self, generation_id: str): + """ + This endpoint deletes a specific generation. + + :param generation_id: The ID of the generation to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def get_generations_by_user(self, user_id: str, offset: int = 0, limit: int = 10): + """ + This endpoint returns all generations by a specific user. + + :param user_id: The ID of the user. + :param offset: The offset for pagination. + :param limit: The limit for pagination. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/generations/user/{user_id}" + params = {"offset": offset, "limit": limit} + async with self.___session.get( + url, params=params, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def upload_init_image(self, file_path: str): + """ + This endpoint returns presigned details to upload an init image to S3. + + :param file_path: The path to the image file. + """ + valid_extensions = ["png", "jpg", "jpeg", "webp"] + extension = os.path.splitext(file_path)[1].strip(".") + if extension not in valid_extensions: + raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") + + url = "https://cloud.leonardo.ai/api/rest/v1/init-image" + payload = {"extension": extension} + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + data = await response.json() + + upload_url = data["uploadInitImage"]["url"] + fields = json.loads(data["uploadInitImage"]["fields"]) + + async with aiofiles.open(file_path, "rb") as f: + file_data = await f.read() + + fields.update({"file": file_data}) + + async with self.___session.post( + upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + return await response.text() + + async def get_single_init_image(self, image_id: str): + """ + This endpoint will return a single init image. + + :param image_id: The ID of the init image to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def delete_init_image(self, image_id: str): + """ + This endpoint deletes an init image. + + :param image_id: The ID of the init image to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def create_upscale(self, image_id: str): + """ + This endpoint will create an upscale for the provided image ID. + + :param image_id: The ID of the image to upscale. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/variations/upscale" + payload = {"id": image_id} + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + return await response.json() + + async def get_variation_by_id(self, generation_id: str): + """ + This endpoint will get the variation by ID. + + :param generation_id: The ID of the variation to get. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/variations/{generation_id}" + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def create_dataset(self, name: str, description: str = None): + """ + This endpoint creates a new dataset. + + :param name: The name of the dataset. + :param description: A description for the dataset. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/datasets" + payload = {"name": name, "description": description} + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + return await response.json() + + async def get_dataset_by_id(self, dataset_id: str): + """ + This endpoint gets the specific dataset. + + :param dataset_id: The ID of the dataset to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def delete_dataset_by_id(self, id: str): + """ + This endpoint deletes the specific dataset. + + :param id: The ID of the dataset to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{id}" + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def upload_dataset_image(self, dataset_id: str, file_path: str): + """ + This endpoint returns presigned details to upload a dataset image to S3. + + :param dataset_id: The ID of the dataset to which the image will be uploaded. + :param file_path: The path to the image file. + """ + valid_extensions = ["png", "jpg", "jpeg", "webp"] + extension = os.path.splitext(file_path)[1].strip(".") + if extension not in valid_extensions: + raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") + + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload" + payload = {"extension": extension} + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + data = await response.json() + + upload_url = data["uploadDatasetImage"]["url"] + fields = json.loads(data["uploadDatasetImage"]["fields"]) + + async with aiofiles.open(file_path, "rb") as f: + file_data = await f.read() + + fields.update({"file": file_data}) + + async with self.___session.post( + upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + return await response.text() + + async def upload_generated_image_to_dataset(self, dataset_id: str, generated_image_id: str): + """ + This endpoint will upload a previously generated image to the dataset. + + :param dataset_id: The ID of the dataset to upload the image to. + :param generated_image_id: The ID of the image to upload to the dataset. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload/gen" + payload = {"generatedImageId": generated_image_id} + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + return await response.json() + + async def train_custom_model( + self, + name: str, + dataset_id: str, + instance_prompt: str, + description: str = None, + model_type: str = "GENERAL", + nsfw: bool = False, + resolution: int = 512, + sd_version: str = None, + strength: str = "MEDIUM", + ): + """ + This endpoint will train a new custom model. + + :param name: The name of the model. + :param description: The description of the model. + :param dataset_id: The ID of the dataset to train the model on. + :param instance_prompt: The instance prompt to use during training. + :param model_type: The category the most accurately reflects the model. + :param nsfw: whether or not the model is NSFW. + :param resolution: The resolution for training. Must be 512 or 768. + :param sd_version: The base version of stable diffusion to use if not using a custom model. + :param strength: When training using the PIXEL_ART model type, this influences the training strength. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/models" + payload = { + "name": name, + "description": description, + "datasetId": dataset_id, + "instance_prompt": instance_prompt, + "modelType": model_type, + "nsfw": nsfw, + "resolution": resolution, + "sd_Version": sd_version, + "strength": strength, + } + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + return await response.json() + + async def get_custom_model_by_id(self, model_id: str): + """ + This endpoint gets the specific custom model. + + :param model_id: The ID of the custom model to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def delete_custom_model_by_id(self, model_id: str): + """ + This endpoint will delete a specific custom model. + + :param model_id: The ID of the model to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + return await response.json() + + async def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5): + """ + This method waits for the completion of image generation. + + :param generation_id: The ID of the generation to check. + :param image_index: (Optional) The index of the specific image to wait for. If None, waits for all images to complete. + :param poll_interval: (Optional) The time interval in seconds between each check. Default is 5 seconds. + + :raises IndexError: If an invalid image_index is provided. + + :return: The completed image(s) once generation is complete. + """ + while True: + response = await self.get_single_generation(generation_id) + generation = response.get('generations_by_pk', {}) + images = generation.get('generated_images', []) + + if image_index is not None: + if image_index >= len(images): + raise IndexError("Incorrect image index") + if images[image_index].get('status') == 'COMPLETE': + return images[image_index] + else: + if all(image.get('status') == 'COMPLETE' for image in images): + return images + + await asyncio.sleep(poll_interval) \ No newline at end of file diff --git a/leonardo_engine/models.py b/leonardo_engine/models.py new file mode 100644 index 0000000..d7a59eb --- /dev/null +++ b/leonardo_engine/models.py @@ -0,0 +1,3608 @@ +nsfw_models = { + "data": { + "custom_models": [ + { + "id": "95bf5c4a-2f7e-4585-9783-b312fc90f5aa", + "name": "Eitan Klein ", + "description": "This model is inspired by the photographer and Israeli artist Eitan Klein, who is involved in capturing the male model and specializes in exposure ...", + "instancePrompt": "photography by Eitan Klein", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-01T03:56:30.167", + "sdVersion": "v2", + "type": "CHARACTERS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "a6ac57fc-1d99-4c25-bde7-71c2912fe2e9", "username": "tomtom10_1", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a man", + "generated_images": [ + { + "id": "c379f001-f371-426e-ad1c-494069b59d26", + "url": "https://cdn.leonardo.ai/users/a6ac57fc-1d99-4c25-bde7-71c2912fe2e9/generations/8b5a1203-ae62-42db-bfcc-2bb09b11e575/Eitan_Klein_a_man_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "0b24aca5-012a-4340-826a-1bc56f46f1e4", + "name": "Realistic Portraits - NSFW", + "description": "Portrait imagery", + "instancePrompt": "high-end portrait photography", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-02-10T02:43:10.395", + "sdVersion": "v2", + "type": "FASHION", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "26675562-66e5-4085-9f62-b80925f54ef9", "username": "Tango_one", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/26675562-66e5-4085-9f62-b80925f54ef9/generations/4e522de3-e6ff-4820-a00d-2f97008473e4/Realistic_Portraits_NSFW_photorealistic_body_photography_of_a_beautiful_girl_sitting_2.jpg", + "id": "01c0ad36-8730-4fbf-a76d-e15b82c093d5", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "RAW photo, 8k ultrasharp photoshoot, instagram, stunning college girl", + "generated_images": [ + { + "id": "c8a86e5c-3e7c-4abb-8240-ef3cbc6c7004", + "url": "https://cdn.leonardo.ai/users/26675562-66e5-4085-9f62-b80925f54ef9/generations/639a85ef-a8a6-4b04-900c-ef74295b1c22/Realistic_Portraits_NSFW_RAW_photo_8k_ultrasharp_photoshoot_instagram_stunning_colle_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "63a10c3a-37fd-48a8-88ec-ca680fe08626", + "name": "car beetle pickup truck ram", + "description": "", + "instancePrompt": "A car", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-02-09T20:54:17.222", + "sdVersion": "v1_5", + "type": "PRODUCT_DESIGN", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "a256dfb3-302d-45dd-810c-285af352258d", "username": "Ronalt", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "car", + "generated_images": [ + { + "id": "7dc269d8-d26a-4fc3-80f6-794f6551fdd8", + "url": "https://cdn.leonardo.ai/users/cfb583d7-2b0a-44df-980c-8b09a4582b0f/generations/c7d3f0a8-9561-42e4-881f-0fd9e43c5ab2/car_beetle_pickup_truck_ram_car_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "7a54c523-b876-4bc0-9071-1119cabba6ab", + "name": "Schiele", + "description": "Egon Schiele art", + "instancePrompt": "a young man in the style of Egon Schiele", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-02-05T18:27:06.242", + "sdVersion": "v1_5", + "type": "ILLUSTRATIONS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "afd16662-7b51-4ad2-a1f5-ba24a021dc54", "username": "Izzyjim", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/f7888a81-6853-4751-91e0-f50212549cc7/Schiele_portrait_of_a_mean_young_woman_in_the_style_of_Egon_S_1.jpg", + "id": "250644d2-05c2-445a-b847-355cded5aa7c", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a young man standing a corner of a cul de in the style of Egon Schiele", + "generated_images": [ + { + "id": "6d06a436-cb1d-4591-84d3-6e794011d801", + "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/5e09ba42-a674-43dc-bf25-a7a72c3d7649/Schiele_a_young_man_standing_a_corner_of_a_cul_de_in_the_style_of_Ego_1.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "6ac1218f-52d5-4a02-80ca-7599a4b2d895", + "name": "cenobites", + "description": "Clive Barker's Cenobites", + "instancePrompt": "a Cenobite", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-02-03T14:06:54.566", + "sdVersion": "v2", + "type": "CHARACTERS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "afd16662-7b51-4ad2-a1f5-ba24a021dc54", "username": "Izzyjim", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/33bd142b-d430-4bb9-894f-662c75ef5694/cenobites_A_naturalskinned_Cenobite_with_freckles_wearing_a_wh_1.jpg", + "id": "bb866f93-51c8-4360-858a-b292d509453a", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " woman fronting camera open white shirt, fire brustings from between her breasts, dramatic seance, back lighting , studio lighting ", + "generated_images": [ + { + "id": "d8983ee2-954b-4427-80fd-1d4c5264fbdf", + "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/1807743a-fa39-45fd-82d0-2679d4127894/cenobites_woman_fronting_camera_open_white_shirt_fire_brustings_from_b_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "72db3416-b7e0-46bf-83f0-ac2dccc44fd2", + "name": "Grimdark", + "description": "Grimdark, warhammer 40k, horus heresy", + "instancePrompt": "warhammer", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-02-02T19:19:24.818", + "sdVersion": "v2", + "type": "ILLUSTRATIONS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "a5bca136-5cb9-4cca-8fbf-bf13835a523a", "username": "visceral3d", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "spacemarine", + "generated_images": [ + { + "id": "ae61c2ba-8ce0-4c86-9b58-6811cab8a636", + "url": "https://cdn.leonardo.ai/users/1a892409-c957-427a-8473-1f461b43cf09/generations/3c9dd566-898a-446c-aef1-8fa467e4a784/Grimdark_spacemarine_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "3a43f9bd-bb36-48ab-af07-cbd9c985a55b", + "name": "Scarred and damaged/bandaged characters", + "description": "a design for damaged characters that are slightly or heavily bandaged", + "instancePrompt": "A Slightly Bandaged Scarred Character.", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-01-31T04:38:57.094", + "sdVersion": "v2", + "type": "CHARACTERS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "e407c8d7-45a3-451f-b8cf-f39d86df2fa0", + "username": "ShopKeeperPyro", + "__typename": "users", + }, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/e407c8d7-45a3-451f-b8cf-f39d86df2fa0/generations/02e14a68-dbe1-427f-bb14-d9f6d5fcfbab/Scarred_and_damagedbandaged_characters_A_Slightly_Bandaged_Scarred_CharacterFull_body_character_d_3.jpg", + "id": "887740ce-48bb-4807-98e2-e9dd9e69e1d4", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "steampunk full body portrait, male character design with a bandaged face", + "generated_images": [ + { + "id": "cc601bfe-8d23-446d-a6a7-93a756855c57", + "url": "https://cdn.leonardo.ai/users/e407c8d7-45a3-451f-b8cf-f39d86df2fa0/generations/72aa748f-2f68-475d-9edc-59ad0fc0594c/Scarred_and_damagedbandaged_characters_steampunk_full_body_portrait_male_character_design_with_a_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "6b2fe263-b081-4b13-8cc1-04cdd2e1179f", + "name": "Artificial Busts ", + "description": "This is finetune of SD2.1 for generating realistic busts. Tuning upon CC0 images from MET New York and few other free resources. ", + "instancePrompt": "A bust ", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-01-22T00:21:12.931", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "6b0af8c4-7cd0-493f-82d3-ce5508dcdea8", "username": "rhizomuser", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " A detailed bust of a mysterious figure, rendered in a surrealistic style.", + "generated_images": [ + { + "id": "c4f0d816-d5fa-4673-bf6c-e29531796832", + "url": "https://cdn.leonardo.ai/users/6b0af8c4-7cd0-493f-82d3-ce5508dcdea8/generations/518a8b0d-e465-402b-b5c5-93a40612a881/Artificial_Bustes_A_detailed_bust_of_a_mysterious_figure_rendered_in_a_surrea_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "4136b90e-e16e-40de-ae8e-40f7313e6b91", + "name": " neon night", + "description": "bluish neon colors", + "instancePrompt": "neon night", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-01-19T06:33:29.285", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "353533d4-c015-47d7-90be-199ddfddcd90", "username": "jaspior", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "neon night", + "generated_images": [ + { + "id": "477ebda0-fc68-4048-8c04-cf05ee133567", + "url": "https://cdn.leonardo.ai/users/353533d4-c015-47d7-90be-199ddfddcd90/generations/4773e379-75de-46ce-bf69-85e12fb8f583/neon_night_neon_night_1.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "9af4f4a0-5c89-4458-b0a9-14d36208e24e", + "name": "Airbrush Design", + "description": "80s and 90s Airbrush Desing Style", + "instancePrompt": "An Airbrush Painting", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-01-18T01:59:27.292", + "sdVersion": "v2", + "type": "ILLUSTRATIONS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "019f0238-4481-4bc8-9789-759e481d2e4e", "username": "aiaiaiaiai", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/019f0238-4481-4bc8-9789-759e481d2e4e/generations/a67f9499-bfb2-4ab4-a99b-6e076e591c05/80s_and_90s_Airbrush_Paintings_and_Ads_An_airbrush_painting_of_Sneakers_0.jpg", + "id": "e37e564a-822f-422b-aba2-60ef4be21647", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "An airbrush painting of Sneakers", + "generated_images": [ + { + "id": "e37e564a-822f-422b-aba2-60ef4be21647", + "url": "https://cdn.leonardo.ai/users/019f0238-4481-4bc8-9789-759e481d2e4e/generations/a67f9499-bfb2-4ab4-a99b-6e076e591c05/80s_and_90s_Airbrush_Paintings_and_Ads_An_airbrush_painting_of_Sneakers_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "be795181-644d-4010-abd7-ff45805931bd", + "name": "macbass", + "description": "Stylization based on a particluar french band...", + "instancePrompt": "black and white doodle", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-01-17T01:31:22.187", + "sdVersion": "v1_5", + "type": "ILLUSTRATIONS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "cbabf1d4-23c3-4bd8-a520-48c53cd0e626", + "username": "FoxBonesMulder", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Gorgeous tattooed woman riding a dolphin", + "generated_images": [ + { + "id": "da1dd922-69ef-4b7e-85ea-25a3605b45a7", + "url": "https://cdn.leonardo.ai/users/cbabf1d4-23c3-4bd8-a520-48c53cd0e626/generations/cbb17167-5308-45e3-ab45-65b46c333d57/MacBass_Gorgeous_tattooed_woman_riding_a_dolphin_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "db02a078-a251-4858-b5c5-d9530f6a03a0", + "name": "Pin-Up Ladies", + "description": "Generate Pin-Up Styled Women!", + "instancePrompt": "Pin-Up, Lingerie", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-01-15T01:53:37.047", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "02a3d88b-3380-43d3-b28c-1350876851b9", "username": "LouCipher", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Lace, Breasts, Blonde", + "generated_images": [ + { + "id": "e29f1359-d757-4630-8262-4710255ec044", + "url": "https://cdn.leonardo.ai/users/02a3d88b-3380-43d3-b28c-1350876851b9/generations/31c1cfeb-5c22-49e8-90cc-52bd8d56a2b8/PinUp_Ladies_Lace_Breasts_Blonde_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "45cbd18f-877a-4f63-8226-86ab45eabc11", + "name": "Kojima-Bot v2.1", + "description": "Generate Hideo Kojima using v2.1!", + "instancePrompt": "Hideo Kojima", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-01-14T22:10:14.504", + "sdVersion": "v2", + "type": "CHARACTERS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "02a3d88b-3380-43d3-b28c-1350876851b9", "username": "LouCipher", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "kojima as a purple stuffed worm in flap jaw space with a tuning fork doing a raw blink on hara-kiri rock I need scissors! 61!", + "generated_images": [ + { + "id": "98d28288-ae1f-4b48-ae8e-9690c9261ad9", + "url": "https://cdn.leonardo.ai/users/02a3d88b-3380-43d3-b28c-1350876851b9/generations/97b40102-1f0e-47a1-97ee-31773d05fdca/KojimaBot_v21_kojima_as_a_purple_stuffed_worm_in_flap_jaw_space_with_a_tun_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "b42069d2-4954-4ac9-a264-05177bf2484b", + "name": '"Metal Gear" Style Characters', + "description": "Generate characters inspired by the artwork of Yoji Shinkawa.", + "instancePrompt": "drawn by Yoji Shinkawa", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-01-14T18:46:36.17", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "02a3d88b-3380-43d3-b28c-1350876851b9", "username": "LouCipher", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Hideo Kojima", + "generated_images": [ + { + "id": "7abfecfe-99ee-412e-981c-1eed7f69607b", + "url": "https://cdn.leonardo.ai/users/02a3d88b-3380-43d3-b28c-1350876851b9/generations/c3d4e247-319a-4d0d-b15e-f38bf6ff0d7f/Metal_Gear_Style_Characters_Hideo_Kojima_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "30e15b51-dbf2-4c70-9281-ba4c659e795b", + "name": "Borderland Style", + "description": "Borderland Style Effect Cel Shader", + "instancePrompt": "borderland style", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-01-07T03:53:05.932", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": True, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "b3b26e38-d8a9-404d-b910-a555e48ec64a", "username": "gruB", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Borderland Style futuristic fortress shaped like a church, unreal engine, cinematic, 8k", + "generated_images": [ + { + "id": "9c59a9e2-a898-4a84-8027-f5df908b5ef3", + "url": "https://cdn.leonardo.ai/users/b3b26e38-d8a9-404d-b910-a555e48ec64a/generations/f234eecf-6ca8-4400-813e-c101d33998c5/Borderland_Style_Borderland_Style_futuristic_fortress_shaped_like_a_church_un_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + ] + } +} + +custom_models = { + "data": { + "custom_models": [ + { + "id": "444cd35a-55c7-4e49-83ad-8660d993edc3", + "name": "the Drya", + "description": "", + "instancePrompt": "the Drya", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T19:26:51.187", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "7ab2c0de-12ed-4e10-941d-220575b14375", "username": "zserv", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/7ab2c0de-12ed-4e10-941d-220575b14375/generations/33ca9e68-d19a-4d35-910b-8d6b3c52bb15/the_Drya_the_Drya_as_toon_female_character_perfect_single_face_an_2.jpg", + "id": "efe26642-7d71-43d4-915b-243e80432cf2", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "(( the Pauk ) as an Nefertity ) | Horror Theme Steampunk | (single head) | Frontal view | centered | perfect anatomy | key visual | intricate | highly detailed | breathtaking | precise lineart | vibrant | abstract | Lovecraftian Noir | cinematic | Carne Griffiths | Conrad Roset hyper-perfect in-depth details, mind-blowing natural vivid colors, ultra-sharp, upscaled 8x, fine lines, cinematic lights, ultra wide scene, high in-depth details, Hollywood quality, a masterpiece, ultra high detailed designs, Camera: Sony A1 200MP, Lens: 85mm f/1.4, Shutter speed: 1/12 ISO:40, 24K, RAW, Sharpness from 1 to 100 (0), Noise from 1 to 100 (0), Post-processing in Adobe Lightroom, photorealistic , ultra photoreal , ultra detailed, intricate details", + "generated_images": [ + { + "id": "516ac2b2-cdac-47cf-9cad-12504c7bd759", + "url": "https://cdn.leonardo.ai/users/f1410d36-7283-4575-8a2b-a4b8d806285c/generations/aa32b2a3-b278-4611-b175-28a69ee4a1ef/the_Drya_the_Pauk_as_an_Nefertity_Horror_Theme_Steampunk_single_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "f84690eb-cfe7-431c-afd4-ccc2d628bede", + "name": "T Borodinauskas", + "description": "", + "instancePrompt": "T Borodinauskas", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T19:21:09.279", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "7ab2c0de-12ed-4e10-941d-220575b14375", "username": "zserv", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/7ab2c0de-12ed-4e10-941d-220575b14375/generations/b87ae51b-ed39-45a5-8f87-0d80e0c56107/T_Borodinauskas_T_Borodinauskas_as_roaring_single_faced_single_perfect_wid_0.jpg", + "id": "6fed79b1-6a86-40c8-a360-4a22ba8ab580", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Full body of Colorful T Borodinauskas: black and red ink flow: 8k resolution photorealistic masterpiece: by Aaron Horkey and Jeremy Mann, detailed gorgeous face and eyes, fine detailed face, intricately detailed fluid gouache painting: by Jean Baptiste Mongue: calligraphy: acrylic: watercolor art, professional photography, natural lighting, volumetric lighting maximalist photoillustration: by marton bobzert: 8k resolution concept art intricately detailed, complex, elegant, expansive, fantastical", + "generated_images": [ + { + "id": "7d7a6879-9f6b-4e11-b0ca-99e4c43c39c5", + "url": "https://cdn.leonardo.ai/users/7ab2c0de-12ed-4e10-941d-220575b14375/generations/8f0f866d-9fa1-447a-812e-c56b782a670f/T_Borodinauskas_Full_body_of_Colorful_T_Borodinauskas_black_and_red_ink_flow_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "26aab27c-fd9d-4238-b6d4-dd193f3edee2", + "name": "D O O M S A Y E R", + "description": "Generates occult priest, dark druids, and worshippers of doom (with a post-apocalyptic spin).", + "instancePrompt": "A doomsayer", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T17:53:33.228", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "4504b091-46ed-4471-ad0d-5a650533c743", + "username": "garbageguy420", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A doomsayer", + "generated_images": [ + { + "id": "21e7f193-6c00-46e2-bf55-d796ddc34f28", + "url": "https://cdn.leonardo.ai/users/4504b091-46ed-4471-ad0d-5a650533c743/generations/54606343-89f9-4183-be9c-3eb48d059362/D_O_O_M_S_A_Y_E_R_A_doomsayer_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "611c7c61-bacf-4909-8de8-e2c5cde907a7", + "name": "Anime Girls 1.0", + "description": "Anime girls", + "instancePrompt": "anime style", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T17:01:22.834", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "ce602c4c-6467-4373-b6c8-7516eb20fb87", "username": "Kryt", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "beautiful lady, freckles, dark makeup, hyperdetailed photography, soft light, head and shoulders portrait, cover", + "generated_images": [ + { + "id": "554594af-3394-495e-a936-43b61340b124", + "url": "https://cdn.leonardo.ai/users/ce602c4c-6467-4373-b6c8-7516eb20fb87/generations/d3199936-b13e-4a96-8137-fa8c8215dab2/Anime_Style_10_beautiful_lady_freckles_dark_makeup_hyperdetailed_photograp_1.jpg", + "likeCount": 12, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "12e494cf-1ed0-4b08-b2a1-c7b5b6221c0f", + "name": "Razumov Portrait Style", + "description": "Portraits in the style of K. razumov", + "instancePrompt": "A portrait of", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T16:53:29.778", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "daf80667-2bb3-4975-83c0-5115d5c0b85e", "username": "RickH1950", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/daf80667-2bb3-4975-83c0-5115d5c0b85e/generations/5c40f7af-179e-4534-96b5-92b53d5500ae/Razumov_Portrait_Style_a_photorealistic_portrait_of_stunningly_0.jpg", + "id": "03bf6882-abef-4ea2-9996-444bf26e8e67", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a beautiful girl, freckles, dark makeup, flat color, wide angle, clean detailed faces, intricate clothing, analogous colors, glowing shadows, beautiful gradient, depth of field, clean image, high quality, high detail, high definition, soft light", + "generated_images": [ + { + "id": "a2b7cf74-64b2-486b-b2f2-98b0c6a3401f", + "url": "https://cdn.leonardo.ai/users/5155974e-5b3a-4872-b613-3e3ea57b055b/generations/c27b559b-8059-43d9-9ed1-6f0d99dd5110/Razumov_Portrait_Style_a_beautiful_girl_freckles_dark_makeup_flat_color_wide_angl_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "d96b9c5d-89db-48b9-bbb7-2f3b5c74dbdc", + "name": "Summer frozen sweets", + "description": "water color on white background cartoon type images of frozen sweets", + "instancePrompt": "an ice cream cone", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T16:28:59.16", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "a4735e05-b1fb-4af6-aa04-af9d40c7d438", "username": "boop749", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "watercolor clipart for sticker summer ice cream cone", + "generated_images": [ + { + "id": "b9eda412-8b6c-4d41-8eb8-7bdfc0d8cca8", + "url": "https://cdn.leonardo.ai/users/a4735e05-b1fb-4af6-aa04-af9d40c7d438/generations/909f0b73-5a49-4726-88c7-0f1ba0034d07/Summer_frozen_sweets_watercolor_clipart_for_sticker_summer_ice_cream_cone_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "4ed57cd2-9e06-496d-abbe-b7e01f9a09e2", + "name": "Crypto Dreams", + "description": "Crypto NFT art based on BAYC", + "instancePrompt": "An NFT character", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T15:42:05.752", + "sdVersion": "v2", + "type": "ILLUSTRATIONS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "7bb48ccd-354a-4f0b-8aff-a8eebe8ee8d8", "username": "ElChuba", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/7bb48ccd-354a-4f0b-8aff-a8eebe8ee8d8/generations/77cb48c0-2e8b-4004-9bb1-54a249f0ee6f/Crypto_Dreams_an_zebra_as_an_nft_character_1.jpg", + "id": "80e0a3c5-5af0-4b42-8c10-7a1c0a5dd952", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "bayc, a bored ape by All Seing Seneca", + "generated_images": [ + { + "id": "817d9122-6f14-41b1-8156-2fbf23cd99ed", + "url": "https://cdn.leonardo.ai/users/7bb48ccd-354a-4f0b-8aff-a8eebe8ee8d8/generations/2d7e4972-8033-4e87-8651-abf658215ae5/Crypto_Dreams_bayc_a_bored_ape_by_All_Seing_Seneca_1.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "f70cb462-f48e-4c48-bf15-948a9718938a", + "name": "e-commerce landing page", + "description": "", + "instancePrompt": "landing page electronics ", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T14:19:23.18", + "sdVersion": "v2", + "type": "UI_ELEMENTS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "2e4d5a04-f8cd-41a1-b082-072d56b1fbeb", "username": "Viniciusm", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "landing page,", + "generated_images": [ + { + "id": "054d4120-a1fe-4432-8cb5-15b81a073057", + "url": "https://cdn.leonardo.ai/users/2e4d5a04-f8cd-41a1-b082-072d56b1fbeb/generations/d8bf9617-14db-4849-8852-2f5e1d46d822/landing_landing_page_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "b4fa336a-8b38-4a7e-a709-7670a89f1bd0", + "name": "flag", + "description": "flag", + "instancePrompt": "flag", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T13:52:27.78", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "cc3f8064-77f7-4809-8be4-feba69b5b80b", + "username": "SlimeHammer", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "flag of frog", + "generated_images": [ + { + "id": "c5d5100f-c264-4c3b-bdd2-92148dd16b19", + "url": "https://cdn.leonardo.ai/users/cc3f8064-77f7-4809-8be4-feba69b5b80b/generations/101643e0-9eb0-454e-871c-8498874acac0/flag_flag_of_frog_1.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "65ddee39-562f-4005-8376-015a618f8f70", + "name": "liliana15", + "description": "", + "instancePrompt": "a portrait of liliana15", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T13:36:34.166", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "4b788d63-9078-4dc8-9da4-6dcae596f0f3", + "username": "awesomeuser", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a portrait of liliana15, levitation, hovering, full body, 1 character, 1 body, centered character", + "generated_images": [ + { + "id": "af063ec8-ecf9-4d87-85d8-d10792a6b6ad", + "url": "https://cdn.leonardo.ai/users/4b788d63-9078-4dc8-9da4-6dcae596f0f3/generations/9f73bf2e-af36-4c1c-9fb5-6d1fb2336ddb/liliana15_a_portrait_of_liliana15_levitation_hovering_full_body_1_chara_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "37001f04-f71f-44b3-b40d-cb0ecef690c1", + "name": "Mamulena", + "description": "Beautiful woman with brown eyes", + "instancePrompt": "beautiful woman with brown eyes, beautiful woma", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T11:26:53.606", + "sdVersion": "v1_5", + "type": "PHOTOGRAPHY", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "ec4c8a0a-bae6-4f33-b62c-ec6fa756fc47", + "username": "Securbond_Phone", + "__typename": "users", + }, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/ec4c8a0a-bae6-4f33-b62c-ec6fa756fc47/generations/deaae567-e251-4e45-b809-777cd87e9dd3/Mamulena_crown_of_roses_on_the_head_earrings_necklaces_35mm_284185531_0.jpg", + "id": "5d7019bd-ca1e-43ee-b663-947c899f7ce1", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Gorgeous brunette, beautiful woman, wavy hair falls on her shoulders, playful look, dressed in an intricately designed light detailed black dress, extremely attractive, flirtatious and playful, neon glow, cloudy fantasy, excellent proportions of the female body, futuristic landscape, bright view, beautiful clearly defined armpits, fantasy adventure theme, extremely attractive, perfect slender female anatomy, film photography, analog photography, film grain, extreme detail, 4k, ultra HD, anna dittmann, hyperrealism, trending on artstation, polished, beautiful, shining, synesthesia, bright, photorealistic, backlight, hair light, 8k resolution, unreal engine 5", + "generated_images": [ + { + "id": "406272d8-9ebc-4d85-8c1b-258ab9987516", + "url": "https://cdn.leonardo.ai/users/720bd193-a7ea-4181-add2-68cb32b5ee6d/generations/59fc26aa-727f-4af8-81db-290b09d48a3a/Mamulena_Gorgeous_brunette_beautiful_woman_wavy_hair_falls_on_her_shou_1.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "dc8b1dc6-30a0-418f-b5db-a8deadc9d5e5", + "name": "Character portrait", + "description": "Good for creating fantasy portrait of human face. Best to use at 640x832. Keep promp weight at 7-9. Remember to add (a portrait of ) at the beginning of your prompt for best results.", + "instancePrompt": "Face", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T10:48:18.382", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "c5e92e90-48cb-4fa1-ae9f-f2b0530e6adf", "username": "Valsiren", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/c5e92e90-48cb-4fa1-ae9f-f2b0530e6adf/generations/82d01f47-41f2-495d-81e0-08bf2c7396f9/Random_face_34_shot_full_body_image_of_Insanely_detailed_photograph_of_A_0.jpg", + "id": "f189138f-a971-4887-919e-1caf30cab7ea", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "3/4 shot full body image of Insanely detailed photograph of An elaborate beautiful temptress glossy lipstick intricate glistening skin face long hair hyperdetailed painting, emerging out of waterfall, Tom Bagshaw Dan Witz CGSociety ZBrush Anna dittmann Central fantasy art 4K", + "generated_images": [ + { + "id": "f189138f-a971-4887-919e-1caf30cab7ea", + "url": "https://cdn.leonardo.ai/users/c5e92e90-48cb-4fa1-ae9f-f2b0530e6adf/generations/82d01f47-41f2-495d-81e0-08bf2c7396f9/Random_face_34_shot_full_body_image_of_Insanely_detailed_photograph_of_A_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "f1830509-5b3e-48d5-a57d-923815ca7a0c", + "name": "Little Heroes", + "description": "Make a Little Heroes", + "instancePrompt": "Little Wonder Women", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T07:46:12.362", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "463c9248-5fbd-4be6-91c5-a5e6e4ef9240", "username": "Remix", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/463c9248-5fbd-4be6-91c5-a5e6e4ef9240/generations/3a71a947-3a2c-4410-9075-12f0fdad630c/Little_Heroes_Little_Superman_white_costume_3d_full_body_highly_detailed_v_2.jpg", + "id": "aa1a7e33-629a-48ea-9310-c9ba6921c586", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Little Wonder Women", + "generated_images": [ + { + "id": "5b2cf668-1b5f-4e4a-ae1a-14dc4dd43166", + "url": "https://cdn.leonardo.ai/users/463c9248-5fbd-4be6-91c5-a5e6e4ef9240/generations/e02a7c58-b7e4-42db-8422-0dcdd28a3de1/Little_Heroes_Little_Wonder_Women_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "b260b08d-9db0-442c-b1d3-2604ccd330a7", + "name": "Cookie Run-v1", + "description": "", + "instancePrompt": "cookie run", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T06:25:36.973", + "sdVersion": "v1_5", + "type": "ILLUSTRATIONS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "2f0e87f7-44f6-4081-baeb-c7ea3023703a", "username": "Andrek", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/c0403e67-5be9-488c-b681-a75a83ffb5a5/Cookie_Runv1_A_cookie_run_character_who_doesnt_exist_in_the_game_yet_but_0.jpg", + "id": "739b695d-61fe-4a37-b11e-e0973d534cd9", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " A whimsical cookie run character with a mischievous smile, wearing a colorful striped shirt and a baker's hat.", + "generated_images": [ + { + "id": "49993490-2edc-4942-8175-2b990915c5c0", + "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/bde10e1f-a6fc-4cd2-aed8-f8424bb4545d/Cookie_Runv1_A_whimsical_cookie_run_character_with_a_mischievous_smile_w_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "5d77afd8-8f15-48fe-bb8e-30d62f9ff8fa", + "name": "Coldplay", + "description": "Model for IA to recognize the Coldplay Band Members", + "instancePrompt": "Chris Martin from Coldplay", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T05:37:37.855", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "419d1a7b-9f0a-45c3-a7f1-617a535a9c96", + "username": "imanolSante18", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Chris Martin from Coldplay, marvel comics, avengers, iron man suit, full body render, rembrandt, cgsociety, artstation trending, highly detailed, ultra realistic, concept art, intricate details, eerie, highly detailed, photorealistic, octane render, 8 k, unreal engine.", + "generated_images": [ + { + "id": "8842a983-fc27-473e-a443-2bf4e3caf166", + "url": "https://cdn.leonardo.ai/users/419d1a7b-9f0a-45c3-a7f1-617a535a9c96/generations/eca231af-25ee-4300-b740-9de1fa9c0aff/Coldplay_Chris_Martin_from_Coldplay_marvel_comics_avengers_iron_man_su_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "3474e4c5-11cc-419a-be7b-72654fd2ac4e", + "name": "Typography 1", + "description": "Creates images with words in solid style.", + "instancePrompt": "word", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T05:34:13.553", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "ca09e017-65ce-4c95-a06e-2390232398bb", "username": "Archie007", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Logo design, travel, Hotels", + "generated_images": [ + { + "id": "ae759317-eaee-474c-9a9c-4f4db29ffa72", + "url": "https://cdn.leonardo.ai/users/dfedd259-c3d7-46f9-a36d-fa2a33bf114f/generations/7d2c1903-3ddf-4b74-98e2-823f46f2c6fa/Typography_1_Logo_design_travel_Hotels_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "f232eee3-5be9-467b-a0c8-6c65311040c7", + "name": "Black and White Portraits", + "description": "Black and White Portraits", + "instancePrompt": "Portrait", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T05:12:29.909", + "sdVersion": "v2", + "type": "PHOTOGRAPHY", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "ee3d46dc-fcd3-4cb4-a7ca-e3d94847f3e5", + "username": "ProfTeddington", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Ginger man with short beard and glasses, portrait", + "generated_images": [ + { + "id": "edc5ffc1-22e4-45d7-9da9-2c8f595d34e1", + "url": "https://cdn.leonardo.ai/users/ee3d46dc-fcd3-4cb4-a7ca-e3d94847f3e5/generations/514ea837-573c-49b5-a4ec-ff72c633dd86/Black_and_White_Portraits_Ginger_man_with_short_beard_and_glasses_portrait_3.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "ad6f6660-693b-4d29-8d55-07b85d52ad55", + "name": "LitKillah", + "description": "LitKillah", + "instancePrompt": "LitKillah", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T04:30:52.253", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "ade3a833-b65f-4187-ad6e-f28e02666e7e", "username": "NowDesign", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "LitKillah wearing sunglasses, fullbody shot, unreal engine, 3d rendered, extremely detailed, volumetric lighting, 8k, high resolution, polished", + "generated_images": [ + { + "id": "92248a6a-02ee-463f-beb9-31d9de90d70b", + "url": "https://cdn.leonardo.ai/users/ade3a833-b65f-4187-ad6e-f28e02666e7e/generations/969b5d99-4278-4312-9538-605a0fd751ee/LitKillah_LitKillah_wearing_sunglasses_fullbody_shot_unreal_engine_3d_r_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "34cf15f4-5bcd-4563-a9ce-feecccabf5c9", + "name": "Tiktoker Generator", + "description": "Generate a new tiktoker!", + "instancePrompt": "A tiktoker", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T02:10:27.299", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", + "username": "myawsomename", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A tiktoker of a abstract beauty, Lizard Man, centered, looking at the camera, approaching perfection, dynamic, moonlight, highly detailed, watercolor painting, artstation, concept art, smooth, sharp focus, illustration", + "generated_images": [ + { + "id": "c2a255cc-077a-4485-8eed-d3edcb796ebf", + "url": "https://cdn.leonardo.ai/users/95664c54-3731-4512-8c68-c449364749d3/generations/f7d7547d-1f18-4138-93d5-a7d7a9f28e10/Tiktoker_Generator_A_tiktoker_of_a_abstract_beauty_Lizard_Man_centered_looking_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "8798d8ba-56fa-418d-9d61-9f83d0783087", + "name": " Custom Mobile Game Items", + "description": "model to generate images to generate images of magnets in isometric", + "instancePrompt": "a game item ", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-04T01:24:35.119", + "sdVersion": "v1_5", + "type": "GAME_ITEMS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a game item 🏘️", + "generated_images": [ + { + "id": "c97a21c0-931c-42a5-b5b2-b85dc3484f33", + "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/6d6f5b8b-f271-421f-8367-32543a09b5b4/Custom_Mobile_Game_Items_a_game_item_4.jpg", + "likeCount": 1, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "6cb74458-733b-4ef9-96e8-905f9f4d42ba", + "name": "Pencilistic", + "description": "A model containing a dataset of pencil drawings on paper, This isn’t near as perfect like traditional artists like how ai isn’t perfect, so expect ...", + "instancePrompt": "a drawing", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-04T00:58:23.469", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "2f0e87f7-44f6-4081-baeb-c7ea3023703a", "username": "Andrek", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/29deeeea-1a52-45e4-a847-7eb2500cf65f/Pencilistic_A_drawing_of_a_mug_filled_with_coffee_with_steam_coming_out_G_3.jpg", + "id": "ffe8ece4-c02a-449e-b99b-6b937071d832", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A drawing of a mug filled with coffee, with steam coming out, Graphite, Pencil, Pointillism, Trending on ArtStation", + "generated_images": [ + { + "id": "ffe8ece4-c02a-449e-b99b-6b937071d832", + "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/29deeeea-1a52-45e4-a847-7eb2500cf65f/Pencilistic_A_drawing_of_a_mug_filled_with_coffee_with_steam_coming_out_G_3.jpg", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "3cbb2148-1083-4b7c-a879-46c083a09154", + "name": "banheiro com banheira", + "description": "banheiras de hidromassagem", + "instancePrompt": "hidro1", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T23:59:51.704", + "sdVersion": "v1_5", + "type": "BUILDINGS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "2c34b750-fbb0-4535-ac81-e7889c5a1c35", "username": "Carolsouza", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a room, small bathroom interior design ,3D scene, black marble countertop, white metro tiles, wooden slatted wall, round mirror, black mable shower stall, lighting design, corona render, unreal, interior design magazine photograth", + "generated_images": [ + { + "id": "67773ce4-a8c5-463e-a6b8-373dc60db6ba", + "url": "https://cdn.leonardo.ai/users/2c34b750-fbb0-4535-ac81-e7889c5a1c35/generations/7e3ec43d-0f28-424a-bed1-1e8b1435e5cf/banheiro_com_banheira_a_room_small_bathroom_interior_design_3D_scene_black_marble_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "5ca77967-9ef1-4709-b18e-b5d4c0449075", + "name": "Cute Anime Girls 1.0", + "description": "Create anime-style girls in a smooth, flowing stroke", + "instancePrompt": "smooth anime style", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T23:48:09.972", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "ab0732f8-4ae5-4bd7-8822-e4f24758e7fb", + "username": "genesisivor", + "__typename": "users", + }, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/ab0732f8-4ae5-4bd7-8822-e4f24758e7fb/generations/b340f423-f0fd-4733-9967-0fbe714c26d9/Cute_Anime_Girls_10_smooth_anime_style_white_castle_flying_over_the_clouds_in_5.jpg", + "id": "6ecef57b-e216-4595-afbc-4df61a428f6b", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "dog girl, blue hair, nice pose, {{depth of field}}, extremely detailed girl, illustration, solo, sharp focus, light smile, closed mouth, beautiful detailed eyes, yellow eyes, {{{{{looking at viewer}}}}},{{{{{sharp focus}}}}}, {loli}, {{{{{masterpiece illustration}}}}},full body, normal angle,, long hair,", + "generated_images": [ + { + "id": "1ec2676e-60a5-42e3-b465-3c1eb08f0930", + "url": "https://cdn.leonardo.ai/users/ab0732f8-4ae5-4bd7-8822-e4f24758e7fb/generations/15e748c9-3556-4342-aae8-4b171b41ab15/Cute_Anime_Girls_Gen_10_dog_girl_blue_hair_nice_pose_depth_of_field_extremely_det_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "f19e584a-b678-4015-89d5-833fff66a6e2", + "name": "Aesthetic friend group pictures", + "description": "generate some friend goals pictures!", + "instancePrompt": "Aesthetic friend group", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T23:45:44.239", + "sdVersion": "v2", + "type": "PHOTOGRAPHY", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", + "username": "myawsomename", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " framework ", + "generated_images": [ + { + "id": "35119233-0f41-49d1-8c75-6280534913e8", + "url": "https://cdn.leonardo.ai/users/7b925e04-af28-4c7e-aad4-1c66cc671ff9/generations/9b9865f9-e8c7-48d6-b2b3-97a208a1d4ac/Aesthetic_friend_group_pictures_framework_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "c52bcbca-30c3-417f-a660-151c34600173", + "name": "vsco/preppy girls", + "description": "", + "instancePrompt": "vsco/preppy girl aesthetic", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T23:38:14.908", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", + "username": "myawsomename", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Create an Afrofuturist, surrealist, cyberpunk Igbo Mbari mask with intricate details, featuring colorful ceramic textures depicting asymmetrical faces with exaggerated eyes and whimsical features. The mask should challenge traditional European conceptions of beauty, with a focus on vibrant colors and playful shapes. To achieve the desired ceramic texture, use simple geometric shapes and patterns to create a tiled effect. Avoid using complex gradients or detailed shading, instead relying on bold, contrasting colors to create depth and texture. To further convey the surreal and cyberpunk themes of the mask, incorporate elements of futuristic technology and machinery into the design. These can be represented through simple shapes like gears, wires, or circuit boards, and should be used sparingly to avoid overwhelming the design. Finally, to tie the design back to its cultural roots, incorporate elements of the Igbo Mbari tradition into the mask, such as geometric patterns, stylized ani", + "generated_images": [ + { + "id": "f3b302b1-9711-4f39-b178-abc07635f23c", + "url": "https://cdn.leonardo.ai/users/6c582e58-fa16-4671-b148-3aab75e95266/generations/6067c115-9be5-46aa-be03-e1c61c2184b1/vscopreppy_girls_Create_an_Afrofuturist_surrealist_cyberpunk_Igbo_Mbari_mask_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "72c19c5d-06fd-42b5-9d01-1a9c80a0cc22", + "name": "Blythe Fantasy", + "description": "beautifull Doll", + "instancePrompt": "DOLL", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T23:05:44.022", + "sdVersion": "v1_5", + "type": "ILLUSTRATIONS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "d068039c-99eb-4776-9309-4de03c12dea1", + "username": "UpsetKitty_", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "lemon man relaxing on chair on beach drinking cocktail with boombox and seagulls and palmtree, photorealistic, hyperdetailed, golden ratio, cartoon, pixar, hdr, octane render, trending on artstation, cinematography lighting, ", + "generated_images": [ + { + "id": "66bbb033-3543-4c02-9b0c-d9efcd273f9f", + "url": "https://cdn.leonardo.ai/users/5b8c64d4-6a65-4864-b3dd-4668d4a378e2/generations/d3d4b5a3-db6f-4e63-ae76-93677fdbff49/Blythe_Fantasy_lemon_man_relaxing_on_chair_on_beach_drinking_cocktail_with_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "9e6e1a43-86b5-46b9-828a-ec1cef8f616e", + "name": "Ankhegs", + "description": "Ankhegs are huge insectoid monster with many slender limbs and large antennae", + "instancePrompt": "Ankheg", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T22:39:49.204", + "sdVersion": "v1_5", + "type": "PHOTOGRAPHY", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "d9739cd5-d837-40b9-9f7c-52701bcf837a", + "username": "BenTheArtMan", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A Ankheg, its mandibles dripping with venom, perched atop a pristine white snowdrift.", + "generated_images": [ + { + "id": "ed89a763-af79-419f-bd84-37be0c9e4318", + "url": "https://cdn.leonardo.ai/users/d9739cd5-d837-40b9-9f7c-52701bcf837a/generations/ab3fae85-a858-4ae0-a75c-6b39380beaa7/Ankhegs_A_Ankheg_its_mandibles_dripping_with_venom_perched_atop_a_pri_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "d984c24a-aa4c-4823-9ef3-b0dde9d21d86", + "name": "2ds", + "description": "2D Surreal Fantasy Yoshitaka Amano and aAyumi Kasai style", + "instancePrompt": "A human being", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T22:32:56.93", + "sdVersion": "v2", + "type": "ILLUSTRATIONS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "2f511681-fe38-4be5-8e24-1d820c3103f4", + "username": "paulangelogc", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A 2d surreal illustration of a RPG Character in the path of self love and autorecognition, high quality, hand made, and yoshitaka amano style", + "generated_images": [ + { + "id": "b9c7fe7b-2794-40f2-a1ee-2ab6fb93927a", + "url": "https://cdn.leonardo.ai/users/2f511681-fe38-4be5-8e24-1d820c3103f4/generations/2df20f6a-0143-4fed-9e4d-e2cb166e8925/2ds_A_2d_surreal_illustration_of_a_RPG_Character_in_the_path_of_2.jpg", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "e86fa62a-7b1e-472b-9416-12be31a8bc8d", + "name": "Supergirls", + "description": "compilation of comic style art of female characters only", + "instancePrompt": "Sgirls", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T22:14:45.492", + "sdVersion": "v1_5", + "type": "ILLUSTRATIONS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "cd5af38f-3279-4af2-bc9d-a2b267fee0f7", + "username": "Soyisraruiz", + "__typename": "users", + }, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/cd5af38f-3279-4af2-bc9d-a2b267fee0f7/generations/575c57e9-2007-46ab-a1f4-92df2120d676/Supergirls_girl_dressed_in_leather_jacket_with_the_zipper_of_the_jacket_0.jpg", + "id": "455d4e01-3567-4562-a5c7-4e9606780681", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "girl dressed in leather jacket, with the zipper of the jacket open without clothes inside, pink jean pants, dark glasses, hiding both hands behind her back, girl with straight hair, face with Asian features.", + "generated_images": [ + { + "id": "c4893da8-633b-49b0-8df5-356b5f9dcf1f", + "url": "https://cdn.leonardo.ai/users/cd5af38f-3279-4af2-bc9d-a2b267fee0f7/generations/dfe5513b-1d7c-4ba3-bdb4-0abd6fa0d35b/Supergirls_girl_dressed_in_leather_jacket_with_the_zipper_of_the_jacket_0.jpg", + "likeCount": 4, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "db524da0-4a30-4516-8f6b-b15e58ebd1ab", + "name": "The Prince's Lost Toys", + "description": " colorful light, intricate, highly detailed, my rendition, artstation, illustration, art by alphonse mucha and uang guangjian and gil elvgren and s...", + "instancePrompt": "anime prince", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T22:11:24.804", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "08fa806b-b4a5-424a-9efc-efd5978e1e61", "username": "DAigotenno", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "(portrait of disney anime 6 year old prince)surrounded by gold and silver magical toys, colorful light, intricate, elegant, highly detailed, my rendition, artstation, illustration, art by alphonse mucha and uang guangjian and gil elvgren and sachin teng, (symmetry) , octane render, ultra realistic, intricate, digital art, ambient lighting, highly detailed, unreal engine, digital painting, concept art, sharp focus ", + "generated_images": [ + { + "id": "9385738b-9596-4ab8-ae3b-e7262514d94c", + "url": "https://cdn.leonardo.ai/users/08fa806b-b4a5-424a-9efc-efd5978e1e61/generations/cd674fb9-9f2c-457c-872e-10e9a41b896a/The_Princes_Lost_Toys_portrait_of_disney_anime_6_year_old_princesurrounded_by_go_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "e9734aa0-00f5-4488-b76a-31cc60195023", + "name": "kpop idol generator", + "description": "", + "instancePrompt": "A kpop idol", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T22:04:21.041", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", + "username": "myawsomename", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A kpop idol", + "generated_images": [ + { + "id": "4128f051-b707-4833-a871-1bb5da5c8b50", + "url": "https://cdn.leonardo.ai/users/9afdb51a-2f05-4911-a4e5-36bf15ac4edf/generations/14514b9f-4475-4e4b-8c59-54ec46a7f077/kpop_idol_generator_A_kpop_idol_3.jpg", + "likeCount": 1, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "2f6ed8eb-8c40-4bb2-97bc-fbc1aab88d95", + "name": "Pixar/Disney", + "description": "Pixar/Disney", + "instancePrompt": "ObiPD", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T21:51:30.584", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "0c3ebcdc-889c-4f2a-980e-68ba77c7b843", "username": "Obiconcept", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/0c3ebcdc-889c-4f2a-980e-68ba77c7b843/generations/a83cd3d0-3063-445f-96f4-cd321c35ebb0/PixarDisney_cute_character_ultra_detailed_perfect_scenery_mysterious_prod_1.jpg", + "id": "368bd51d-250e-450e-8d2c-064a38d8d0c0", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "cute character, ultra detailed, perfect scenery, mysterious, glow, product photography, octane render, 8K ultra detailed, perfect scenery, mysterious, glowing green, product photography, octane render, 8K", + "generated_images": [ + { + "id": "c36a5865-16c2-46e1-af58-59a953ff753d", + "url": "https://cdn.leonardo.ai/users/0c3ebcdc-889c-4f2a-980e-68ba77c7b843/generations/31426272-22ac-4ec9-bdf7-2602e2c9dfda/PixarDisney_cute_character_ultra_detailed_perfect_scenery_mysterious_glow_0.jpg", + "likeCount": 6, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "9b52bcfb-de0f-40ab-b8fc-c39d8229fb69", + "name": "Ice Sculptures", + "description": "model to generate images of ice sculptures", + "instancePrompt": "a ice sculpture ", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T21:21:13.52", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/4fa07504-8215-4985-be5d-dd6d1eb5bc5a/Ice_Sculptures_a_ice_sculpture_tree_4.jpg", + "id": "2f5da5b2-0278-406e-bee4-5b4d638e01d8", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a ice sculpture 🤖", + "generated_images": [ + { + "id": "7d12f018-f8e3-4bd4-94a0-f646bf8bb647", + "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/382a80ea-e8b7-4ec0-9b16-183078256baf/Ice_Sculptures_a_ice_sculpture_4.jpg", + "likeCount": 5, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "dac0968c-65b5-4d4c-8d9a-83084fa3762b", + "name": "Game maps v1.8", + "description": "model to generate images of game maps", + "instancePrompt": "a game map", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T21:15:41.96", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a game map", + "generated_images": [ + { + "id": "315b6fa2-9031-40eb-949f-6f13afd8d7b9", + "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/abc1226a-4801-4a1c-afc9-93fba686c33b/Game_maps_v18_a_game_map_0.jpg", + "likeCount": 4, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "577cc4ff-72ba-44e4-baf3-88bb7031d780", + "name": "Movie/Tv show Poster generator", + "description": "", + "instancePrompt": "Movie poster", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T20:45:56.585", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "a55cb11c-af6b-40fa-8ef9-0910c3c139f2", + "username": "toriropedhis", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " The Twilight Zone, 8k resolution concept art intricately detailed, complex, elegant, expansive, fantastical, Masterpiece, best quality, chiaroscuro lighting, sfumato texture-collodion process fractal recursion chaos, plasma fireball of verdigris in the distance, octane render, chromatic density, chromatography ,background=fractal recursion chaos", + "generated_images": [ + { + "id": "8c77d196-2e2b-4deb-acbc-9fc2652fc98f", + "url": "https://cdn.leonardo.ai/users/ca86fddb-0c8a-45c8-9adc-51e25b5aef3a/generations/f5ca5b37-e313-4a8c-9f98-3ba3e3ae37ac/MovieTv_show_Poster_generator_The_Twilight_Zone_8k_resolution_concept_art_intricately_d_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "0dec12b4-0719-46ca-a15a-3faeb975d4bf", + "name": "Hokuto no ken character generator", + "description": "Create a character with Kenshiro School Hokuto", + "instancePrompt": "warrior, martial fighter", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T20:34:10.941", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "81cdba52-88eb-4760-a5ae-879af1da7808", "username": "huangugu", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/81cdba52-88eb-4760-a5ae-879af1da7808/generations/68f6945e-826d-43bf-af8f-b4c0a9d0c7b8/Hokuto_no_ken_character_generator_a_complete_image_of_a_ninja_in_the_style_Nanto_Seiken_bla_0.jpg", + "id": "0605f023-cca9-451d-8795-51c7c05f5100", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a complete image of a fighter in the style hokuto no ken, eyes with details, nose with details, mouth with details, kenshiro, manga style, 8k, octane render, global illumination, very detailed image very well colored, background in white", + "generated_images": [ + { + "id": "c97bad23-d174-435d-b6fd-c56ed0fbf598", + "url": "https://cdn.leonardo.ai/users/81cdba52-88eb-4760-a5ae-879af1da7808/generations/ed04276f-e481-4226-a4be-5004c5f36c8e/Hokuto_no_ken_character_generator_a_complete_image_of_a_fighter_in_the_style_hokuto_no_ken_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "790f5edb-1043-43d5-9dd5-056cc0c65113", + "name": "Ink / Line Art", + "description": "BW Ink and Line Art", + "instancePrompt": "Ink / Line Art", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T18:29:15.907", + "sdVersion": "v2", + "type": "ILLUSTRATIONS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "0eea719d-525b-40e3-b338-989c7bb0ee2c", "username": "LUKALAB", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/0eea719d-525b-40e3-b338-989c7bb0ee2c/generations/feeb07ac-a2c2-4a5a-a86a-34bbc50107cf/Ink_Line_Art_city_ink_line_art_1.jpg", + "id": "d40976a2-3863-4333-a438-0760d3c60b08", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "leaves flying in the air", + "generated_images": [ + { + "id": "0e396b06-a901-4766-8d05-f00782855bae", + "url": "https://cdn.leonardo.ai/users/0eea719d-525b-40e3-b338-989c7bb0ee2c/generations/cbf7adbc-e893-4984-9f08-e8b74a3b58f0/Ink_Line_Art_leaves_flying_in_the_air_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "02919502-26c4-40d0-b099-445b492bac65", + "name": "Obey Babay", + "description": "", + "instancePrompt": "Obey Babay", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T16:11:17.294", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "e5797a3f-0a7e-46f0-9483-68bab269fc1e", "username": "lapulkin", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/e5797a3f-0a7e-46f0-9483-68bab269fc1e/generations/67fed63c-863c-41fd-a182-7773b4a818f5/Obey_Babay_exact_true_copy_of_Obey_Babay_as_yawning_single_faced_an_2.jpg", + "id": "04da61c8-d949-4e82-b639-72bdc77abf4e", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "( exact true copy of ( Obey Babay ) as ape idol) | Horror Theme Clockpunk | ((single head)) full body illustration | Top view | centered | ((perfect anatomy)) | key visual | intricate | highly detailed | breathtaking | precise lineart | vibrant | abstract | Lovecraftian Noir | cinematic | H.R. Giger | Conrad Roset, diffuse lighting, hyper-perfect in-depth details, mind-blowing natural vivid colors, ultra-sharp, upscaled 8x, fine lines, cinematic lights, ultra wide scene, high in-depth details, Hollywood quality, a masterpiece, ultra high detailed designs, Camera: Sony A1 200MP, Lens: 85mm f/1.4, Shutter speed: 1/12 ISO:40, 24K, RAW, Sharpness from 1 to 100 (0), Noise from 1 to 100 (0), Post-processing in Adobe Lightroom, photorealistic, ultra photoreal, ultra detailed, intricate details", + "generated_images": [ + { + "id": "cb15510b-4528-4b05-88ee-cd29d6d646dd", + "url": "https://cdn.leonardo.ai/users/e5797a3f-0a7e-46f0-9483-68bab269fc1e/generations/5285376d-6b4a-431e-ba0b-97a59ce2fbc5/Obey_Babay_exact_true_copy_of_Obey_Babay_as_ape_idol_Horror_Theme_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "bab7bc01-7923-4dd4-b766-98f065240ce4", + "name": "Gangster Paradise", + "description": "a world and mood of gangsters of 1920", + "instancePrompt": "Gangster Paradise", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T13:44:39.705", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "698e8c2d-f790-4f6b-9c13-c65fa7920deb", "username": "Lugeer", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/698e8c2d-f790-4f6b-9c13-c65fa7920deb/generations/e1c9f06e-c47b-41d1-855e-25636c3fd617/Gangster_Paradise_Gangster_paradise_portrait_of_mafia_inside_a_bar_epic_contra_2.jpg", + "id": "91cb46e4-f0f3-4265-936d-2be2d8679ec9", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Gangster paradise portrait of mafia inside a bar epic contrasted light", + "generated_images": [ + { + "id": "477544dc-91c4-4c9c-828f-906a116d5f46", + "url": "https://cdn.leonardo.ai/users/698e8c2d-f790-4f6b-9c13-c65fa7920deb/generations/f1cbaf4e-64bb-45b7-98ab-bb2c474d8004/Gangster_Paradise_Gangster_paradise_portrait_of_mafia_inside_a_bar_epic_contra_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "26cb167b-629d-438e-8efc-2eb05d6181f9", + "name": "Zombie anime ", + "description": "mushrooms fantasy ", + "instancePrompt": "Portrait ", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T10:19:52.011", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "f4e66010-69af-4262-bb23-827e24c82516", "username": "JonRT", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/9f43a7f7-1b82-4c53-8523-4f452496728b/Mixed_modern_european_ink_painting_anime_female_zombie_bobblehead_m_3.jpg", + "id": "154d76c7-772c-4b42-a11c-aff921b285ea", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "cartoon rotoscoping robotic cyborg splash art glitch art action painting photography portrait image FLCL anime fashion wearing vintage robot helmet figurine TikTok island boy Kandinsky headdress flat dreads origami woodcut engraving Modern art marble statue Renaissance Delirium tremens Dali & Picasso style depiction of a beautiful animatronic fiber optics embossed multi purpose transfer LED runners Aeon Flux Bosch MC Escher cybertronic city market landscape made of mechanical Mark Ryden surrealism architecture with plasma face paint cyborg tech gears industrial circuitry parts dramatic light sharp focus artstyle Gensho Yasuda by Fausto de martini Wadim Kashin Z.W. gu Jamie Hewlett Antoni Tudisco Frederic Duquette Mumford hideyuki morioka ito ogure vofan victo ngai composition direction Keyshot Chromatic Aberration Global illumination Subsurface scattering Unreal Engine 5 Octane Render Ambient Occlusion Opulent Beautiful imaginative masterpiece 3d liquid detailing fluid acrylic", + "generated_images": [ + { + "id": "f9c50f01-ec4c-46bd-8a64-b3a857d0e516", + "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/4350b99f-e9cd-4f72-9426-14f0556f1237/Mixed_cartoon_rotoscoping_robotic_cyborg_splash_art_glitch_art_acti_3.jpg", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "8c4874c0-ec04-4a89-9433-dbf156d914f0", + "name": "Smoothie", + "description": "FruTech", + "instancePrompt": "a Fruity", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T09:56:18.619", + "sdVersion": "v2", + "type": "BUILDINGS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "4c4490f4-ee5e-4b62-9bc1-9038afa178b6", + "username": "ChadUltraF3", + "__typename": "users", + }, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/4c4490f4-ee5e-4b62-9bc1-9038afa178b6/generations/dd553f4d-c7d5-4923-a967-8c43812a8f03/Smoothie_a_fruity_bitcoin_house_1.jpg", + "id": "b5ef114a-f034-4c19-accd-27bfc01a63fa", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a fruity city made of berries", + "generated_images": [ + { + "id": "b1350d6e-6f8f-4eb4-be2a-8ae72986d549", + "url": "https://cdn.leonardo.ai/users/4c4490f4-ee5e-4b62-9bc1-9038afa178b6/generations/b23eae15-48d6-4222-a991-25ac9cf8e0c4/Smoothie_a_fruity_city_made_of_berries_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "ec0dd7d8-306f-48ce-b264-f681ec2ffbf7", + "name": "Landscape Wallpapers", + "description": "Create landscape wallpapers with any size, any resolution. See samples on my profile, search 'wallpaper' at https://app.leonardo.ai/profile/noerman", + "instancePrompt": "4k", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T09:42:39.156", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "dbc5103a-ca87-4323-b0c9-12f740e6e773", + "username": "misterprompts", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Tiangong Chinese style", + "generated_images": [ + { + "id": "e2d1a9d0-7e85-4227-9f37-e940afa70da1", + "url": "https://cdn.leonardo.ai/users/56729e70-0019-4b57-8b6a-0f2d360722f5/generations/0cf2bf9a-5734-43b9-bb0d-497b4c5ff5b4/Landscape_Wallpapers_Tiangong_Chinese_style_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "68b5e616-1834-43dc-a7c5-a0153a6dbc96", + "name": "Pixel Art Assets", + "description": "A Dataset for Pixel Art Images, but not just the Avatar, but Fullbody NPCs. Trained on Classic Video Games.", + "instancePrompt": "Pixel Character", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T09:15:36.661", + "sdVersion": "v2", + "type": "PIXEL_ART", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "0bbab906-ba22-47ef-8985-89ec3959e79e", "username": "bornmedia", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Full body pose of a mage holding a staff, pixel art, Pixel Character", + "generated_images": [ + { + "id": "f46b7aad-759f-406d-8649-f200e5f4c715", + "url": "https://cdn.leonardo.ai/users/0bbab906-ba22-47ef-8985-89ec3959e79e/generations/62fdafd0-d189-4184-aa0a-3da22a35df78/Pixel_Art_Assets_Full_body_pose_of_a_mage_holding_a_staff_pixel_art_Pixel_Ch_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "fe94035a-4c12-41c3-a618-c475a9547657", + "name": "Hong Kong Girl, HK", + "description": "", + "instancePrompt": "Hong Kong", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T07:36:03.158", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "832669e4-0939-44a7-92a5-1d1289d806a5", "username": "K2023HK", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " realistic beautiful no clothes orange haired female pianist performing a majestic galactic melody realistic with details", + "generated_images": [ + { + "id": "efb49db7-84ec-42a1-9e3c-b3416cb84c82", + "url": "https://cdn.leonardo.ai/users/ca3d54e7-88d1-41f6-bea4-3412b24e952d/generations/48e91a57-8701-488e-aa9e-7b0272aaebdb/Hong_Kong_Girl_HK_realistic_beautiful_no_clothes_orange_haired_female_pia_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "ee1c44df-e65c-4bdf-9573-067454e3e52a", + "name": "Viktor Safonkin", + "description": "", + "instancePrompt": "Viktor Safonkin", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T07:34:13.169", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "c5591d60-7ff7-4ba1-bacd-f3f256ef9190", + "username": "zaborservice", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "( exact true copy of ( the Pauk ) as an old pharaon ) | Horror Theme Steampunk | single head illustration | Frontal view | centered | perfect anatomy | key visual | intricate | highly detailed | breathtaking | precise lineart | vibrant | abstract | Lovecraftian Noir | cinematic | Viktor Safonkin, Chinese ice carving, diffuse lighting, hyper-perfect in-depth details, mind-blowing natural vivid colors, ultra-sharp, upscaled 8x, fine lines, cinematic lights, ultra wide scene, high in-depth details, Hollywood quality, a masterpiece, ultra high detailed designs, Camera: Sony A1 200MP, Lens: 85mm f/1.4, Shutter speed: 1/12 ISO:40, 24K, RAW, Sharpness from 1 to 100 (0), Noise from 1 to 100 (0), Post-processing in Adobe Lightroom, photorealistic, ultra photoreal, ultra detailed, intricate details", + "generated_images": [ + { + "id": "3851a7a9-62d4-4d16-8f95-f0f536e5c195", + "url": "https://cdn.leonardo.ai/users/f1410d36-7283-4575-8a2b-a4b8d806285c/generations/6abde543-0fbe-433a-9d13-4e2af8592ad2/Viktor_Safonkin_exact_true_copy_of_the_Pauk_as_an_old_pharaon_Horror_T_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "54695a14-0fc7-4878-8db9-583721c7f4c8", + "name": "Obese Famous character ", + "description": "This model will allow you to generate images of famous people in obese form, in a more streamlined and a little more precise way.", + "instancePrompt": "a Obese ", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T04:47:54.629", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a Obese catwoman", + "generated_images": [ + { + "id": "47323018-1273-4bc4-ab92-03bbb764dff0", + "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/58c5bc51-5a4e-4fcd-a2da-08c7fd6ca7cc/Obese_Famous_character_a_Obese_catwoman_2.jpg", + "likeCount": 1, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "020aad14-ab93-4781-8938-1d95eed5c35b", + "name": "Metropolitan Small Apartments", + "description": "This model will allow you to generate apartment photography images in metropolitan style and with little space in a more streamlined and a little m...", + "instancePrompt": "a apartments", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-03-03T04:38:34.738", + "sdVersion": "v1_5", + "type": "PHOTOGRAPHY", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a apartments under water ", + "generated_images": [ + { + "id": "5642733d-3954-42f5-8448-c7157756c52c", + "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/c1015fad-a807-425d-97e6-b17f99b44b02/Metropolitan_Small_Apartments_a_apartments_under_water_2.jpg", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "515602ee-14c2-4881-b54b-19b81dcb55c3", + "name": "meh idk", + "description": "mix ", + "instancePrompt": "cybertech", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T04:16:34.94", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "f4e66010-69af-4262-bb23-827e24c82516", "username": "JonRT", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/e39ab667-3e71-4c07-ba52-4284a702ed91/meh_idk_modern_european_ink_painting_anime_female_zombie_in_a_lush_m_0.jpg", + "id": "b4c95a13-827a-4a1c-8adf-5d8882496aa6", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "female hipster techno astronaut fashion model in a vast Dali landscape with Bosch small forest cities with lush kandinsky falling stars action painting by Jamie Hewlett Alberto Mielgo Mark Ryden Yoko d’Holbachie Yosuke Ueno Todd Schorr Amy Sol Jeff Soto DSLR HDR photo ray tracing ultraperfect asymmetrical ratio composition Relative Apparent Size Keyshot MarmosetToolbag 4 3d liquid detailing nvinkpunk, beautiful detailed realistic Neo noir style, Cinematic, neon lights glow in the background. Cinestill 800T film. Lens flare. Helios 44m, dreamwave colors, depth of field, bokeh, modern art marble statue renaissance+bosch+mark ryden surrealism+3/4 portrait high fashion model cyborg ink punk+with melting liquid led lights fiber optic hair+made of hiroaki takahashi art+ultra perfect composition+3d liquid detailing+fluid acrylic+kandinsky art style a vast shooting star filled sky+compositional direction+visual tension+structural net+an opulent cybernetic city background", + "generated_images": [ + { + "id": "b95ba0b4-87cf-4d59-b3e0-02115cfeb8ca", + "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/76672ed1-63fa-4662-90d3-465520e08b3d/Ai_drip_tek_female_hipster_techno_astronaut_fashion_model_in_a_vast_Dal_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "c053b5f4-d9d3-4ab9-8850-dce40a3a1d2f", + "name": "3D-Alike Comics", + "description": "Create 3D-alike characters or even scenes, perfect for your comic creations, also great for other usage. Please use simple sentence prompts.", + "instancePrompt": "3d", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T04:01:31.651", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": { + "id": "dbc5103a-ca87-4323-b0c9-12f740e6e773", + "username": "misterprompts", + "__typename": "users", + }, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a superhero named ScamBuster ", + "generated_images": [ + { + "id": "00296a84-30e4-4ec1-9dc5-e087fced2745", + "url": "https://cdn.leonardo.ai/users/7cb4854b-af78-4cfc-9328-d12404a0e6d3/generations/016688ea-2195-435b-9fd3-03f534a62930/3DAlike_Comics_a_superhero_named_ScamBuster_1.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "4eb7a7ea-dc80-45f5-ab58-f836d1c52fe5", + "name": "South Park", + "description": "A South Park trained model.", + "instancePrompt": "a South Park episode", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-03-03T02:33:53.334", + "sdVersion": "v1_5", + "type": "ILLUSTRATIONS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "2f0e87f7-44f6-4081-baeb-c7ea3023703a", "username": "Andrek", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/5e2f185b-d878-421e-8217-c5053e6215ba/South_Park_A_South_Park_episode_High_quality_illustration_3.jpg", + "id": "4215d973-dd4e-4b83-8262-aa0f4249718b", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A South Park character that doesn’t exist but has a unique design.", + "generated_images": [ + { + "id": "4b130145-3240-422e-b65c-4d5550818fe3", + "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/e2fdb127-4bf9-4c71-8209-bc488e7cb672/South_Park_A_South_Park_character_that_doesnt_exist_but_has_a_unique_de_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + ] + } +} + +platform_models = { + "data": { + "custom_models": [ + { + "id": "f1929ea3-b169-4c18-a16c-5d58b4292c69", + "name": "RPG v5", + "description": "Anashel returns with another great model, specialising in RPG characters of all kinds.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-07-28T12:02:43.911", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/ad43331b-c80c-40e5-8462-304aaaef3584/RPG_v5_a_stunning_photograph_of_a_grotesque_alien_creature_wit_1.jpg", + "id": "ea3c5232-3c5d-4b64-bd1a-be698576b769", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A vibrant cinematic photo of a female adventurer in the jungle, octane render, high quality", + "generated_images": [ + { + "id": "8b326529-2403-4238-810c-602a798c8c2c", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/95e14374-eaf9-4ac9-9611-855706aadf45/RPG_v5_A_vibrant_cinematic_photo_of_a_female_adventurer_in_the_0.jpg", + "likeCount": 6, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "b63f7119-31dc-4540-969b-2a9df997e173", + "name": "SDXL 0.9", + "description": "The latest Stable Diffusion model, currently in Beta.", + "instancePrompt": None, + "modelHeight": 768, + "modelWidth": 1024, + "coreModel": "SD", + "createdAt": "2023-07-12T14:37:01.33", + "sdVersion": "SDXL_0_9", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9ed4ccd8-649c-4a59-a7bb-9f5b704a91b1/SDXL_09_a_beautiful_woman_vivid_striking_colors_cinematic_phot_0.jpg", + "id": "4a5b68d4-ef23-4d36-a1d8-b027287da029", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a beautiful woman, vivid striking colors, cinematic photography", + "generated_images": [ + { + "id": "6cea9433-3e8c-4614-8a5e-4cbb969e6699", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9ed4ccd8-649c-4a59-a7bb-9f5b704a91b1/SDXL_09_a_beautiful_woman_vivid_striking_colors_cinematic_phot_1.jpg", + "likeCount": 5, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "d69c8273-6b17-4a30-a13e-d6637ae1c644", + "name": "3D Animation Style", + "description": "Great at 3D film vibes, capable of complex scenes with rich color. Storyboard time!", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-07-04T04:16:46.127", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/0e85d140-3ea0-4179-a708-ae95bf9329a3/3D_Animation_Style_a_masterpiece_image_of_an_older_tired_and_b_2.jpg", + "id": "ca1b67dc-8e39-49a8-8846-f930fb286ba9", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a masterpiece image of an older tired and battle-worn male detective named jack smith, highly detailed, HDR, cyberpunk, realistic, (photorealistic:1.4), best quality, ultra high res", + "generated_images": [ + { + "id": "aa84be9e-4156-4887-afe8-d7b858cbe0de", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/2183b6e1-29f7-4295-bd86-7ed3fa233d5c/PixarTypeB_a_masterpiece_image_of_an_older_tired_and_battlewor_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "ac614f96-1082-45bf-be9d-757f2d31c174", + "name": "DreamShaper v7", + "description": "Lykon is back with another update. This model is great at a range of different styles.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-07-04T04:16:25.719", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/911623f3-4a89-4db9-b49f-ace5ac086a3d/DreamShaper_v7_A_vibrant_cinematic_photo_of_a_female_adventure_1.jpg", + "id": "52550c00-4def-4283-91f4-ccd196a630bf", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "an older tired and battle-worn male detective, highly detailed, HDR, cyberpunk", + "generated_images": [ + { + "id": "fb390f8e-e9d6-4ee8-a67c-7cae9a132bab", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/bff69fae-6e4e-48d1-8a8f-6c75799be511/DreamShaper_v7_an_older_tired_and_battleworn_male_detective_hi_1.jpg", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "e316348f-7773-490e-adcd-46757c738eb7", + "name": "Absolute Reality v1.6", + "description": "A photorealistic style model from Lykon. Great at all sorts of photorealism.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-07-04T04:15:48.815", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9d7e2dbe-6dff-4bf5-b487-414dee2a10b9/Absolute_Reality_v16_a_stunning_photo_of_a_woman_with_aztech_t_1.jpg", + "id": "cf623adb-d7f3-43e6-8431-d2edd5a7a08e", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a photo of a woman with a tribal headress, stunning photography, masterpiece, hdr", + "generated_images": [ + { + "id": "cc14e950-ffdb-41ee-85d0-eb9419956a82", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/4836d001-a2df-4a29-a0dd-11786b857f55/Absolute_Reality_v16_a_photo_of_a_woman_with_a_tribal_headress_2.jpg", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "1aa0f478-51be-4efd-94e8-76bfc8f533af", + "name": "Anime Pastel Dream", + "description": "Pastel anime styling. Use with PMv3 and the anime preset for incredible range. Model by Lykon.", + "instancePrompt": None, + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2023-06-13T06:01:06.314", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8cc624c3-c1ba-40c9-b3cd-21056382728a/AnimePastelDream_fuji_film_candid_portrait_o_a_black_woman_wea_2.jpg", + "id": "c5510862-e82b-4705-861c-58658a89fa9c", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "fuji film candid portrait o a black woman wearing sunglasses rocking out on the streets of miami at night, 80s album cover, vaporwave, synthwave, retrowave, cinematic, intense, highly detailed, dark ambient, beautiful, dramatic lighting, hyperrealistic", + "generated_images": [ + { + "id": "66c689b4-dfaa-43c3-8f72-b664db7f21e5", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/db468128-7209-4d2d-9c17-6ddcf6e1f792/AnimePastelDream_fuji_film_candid_portrait_o_a_black_woman_wea_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "b7aa9939-abed-4d4e-96c4-140b8c65dd92", + "name": "DreamShaper v6", + "description": "A new update to an incredibly versatile model, excels at both people and environments, by Lykon.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-05-26T02:29:05.936", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8ab5c76a-eefb-4801-817f-458f68958db7/DreamShaperV6_a_masterpiece_image_of_Splash_art_music_album_ar_6.jpg", + "id": "e7a9a1ff-76be-4cbd-b560-7379f1af2c32", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a warrior fighting a dragon", + "generated_images": [ + { + "id": "b1c9e57c-acf1-442b-9669-6a34d5128fb7", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/0ca698f0-9623-4081-93e0-e2516fb0d3bd/DreamShaperV6_a_warrior_fighting_a_dragon_4.jpg", + "likeCount": 14, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "d2fb9cf9-7999-4ae5-8bfe-f0df2d32abf8", + "name": "DreamShaper v5", + "description": "A versatile model great at both photorealism and anime, includes noise offset training, by Lykon.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-04-13T04:56:36.796", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/bb22942b-40c8-4a06-a219-238808053ee0/DreamShaper_v5_extremely_intricate_fantasy_character_photoreal_0.jpg", + "id": "27bec45f-b450-4775-817f-761683f2cc5e", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "extremely intricate fantasy character, photorealistic, portrait of a great warrior, intricate details on clothing, professional oil painting, deviantart hd, artstation hd, concept art, cg society, dramatic, award winning matte drawing cinematic lighting octane render unreal engine volumetrics dtx", + "generated_images": [ + { + "id": "0e0394bb-c1e7-46f4-b6e4-b205f97ecec7", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/4aa70349-6456-4ef7-a942-8c3db37a8339/DreamShaper_v5_extremely_intricate_fantasy_character_photoreal_1.jpg", + "likeCount": 18, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "b820ea11-02bf-4652-97ae-9ac0cc00593d", + "name": "Leonardo Diffusion", + "description": "A model with incredible shading and contrast, great at both photos and artistic styles, by cac0e.", + "instancePrompt": None, + "modelHeight": 1024, + "modelWidth": 1024, + "coreModel": "SD", + "createdAt": "2023-02-28T20:26:06.053", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8905a8f0-9219-43ad-8ecb-1c37b4501dc5/Leonardo_Diffusion_ultra_detailed_artistic_photography_of_a_fashion_moden_3.jpg", + "id": "08b6f797-85f1-457a-a194-2e82f725bd6b", + "__typename": "generated_images", + }, + "imageCount": 5343, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A stunning image of a Vulcan warrior", + "generated_images": [ + { + "id": "14bcb95a-a52a-4dd0-837c-c782ab065d8f", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/bb70ebcb-48fe-488b-b220-fb19696b385e/Leonardo_Diffusion_A_stunning_image_of_a_Vulcan_warrior_2.jpg", + "likeCount": 4, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "a097c2df-8f0c-4029-ae0f-8fd349055e61", + "name": "RPG 4.0", + "description": "This model is best at creating RPG character portraits with the ability for great photorealism. Created by Anashel.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-02-09T11:47:04.271", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/20cd8cca-ad36-46b1-a0e3-8bd669112017/RPG_portrait_painting_of_a_redhead_feminine_royal_woman_with_a_fe_3.jpg", + "id": "419e4e5e-245d-49f0-b613-b1e8b02f6ca5", + "__typename": "generated_images", + }, + "imageCount": 1000070, + "__typename": "custom_models", + "generations": [ + { + "prompt": "Photography of a well built modern cottage house sitting on top of a lake covered in snow, sunset moment, surrounded by mountains, Unsplash contest winner, cozy interior, warm interior lighting, made of glass, Vray, minimalist, high details, denoise, insanely detailed and intricate, professional color graded, hyper realistic, super detailed, 8k,HDR,high-resolution DSLR photograph, shot on IMAX200", + "generated_images": [ + { + "id": "1cd73660-1c9c-4409-b856-e5da0d30d003", + "url": "https://cdn.leonardo.ai/users/b35decec-845a-475a-960f-a690332c3cf3/generations/926c827d-3504-4541-b4ff-49c5f4487858/RPG_Photography_of_a_well_built_modern_cottage_house_sitting_on_t_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "458ecfff-f76c-402c-8b85-f09f6fb198de", + "name": "Deliberate 1.1", + "description": "A powerful model created by XpucT that is great for both photorealism and artistic creations.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-02-08T10:25:54.556", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/da624520-f0c6-4afa-b171-9bfc3bbb27de/Deliberate_v11_waitress_pinup_art_style_kodak_portra_400_cinematic_smiling_0.jpg", + "id": "59776f05-378e-4c95-88c0-1740ca6c25a3", + "__typename": "generated_images", + }, + "imageCount": 1048521, + "__typename": "custom_models", + "generations": [ + { + "prompt": "High detail RAW color art, animation, cartoon, magical atmosphere, (detailed skin, skin texture), (intricately detailed, fine details, hyperdetailed), raytracing, subsurface scattering, (muted colors), diffused soft lighting, shallow depth of field, by (Oliver Wetter), photographed on a Canon EOS R5, 28mm lens, F/2.8, sharp focus bokeh, backlight, volumetric lighting, (by Anastasiya Dobrovolskaya)", + "generated_images": [ + { + "id": "2a6793b4-8482-4026-8bf7-09151ab3159f", + "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/ddf7239c-4972-429b-8a0a-1de6f9647738/Deliberate_High_detail_RAW_color_art_animation_cartoon_magical_atmospher_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "17e4edbf-690b-425d-a466-53c816f0de8a", + "name": "Vintage Style Photography", + "description": "This model can generate a broad range of imagery with a vintage style as if it was taken from a film camera", + "instancePrompt": "vintage style", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-01-29T09:40:50.165", + "sdVersion": "v2", + "type": "PHOTOGRAPHY", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59", "username": "Jachin", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/7a6f17f3-689c-461d-9c59-14a7ac88fa0e/Vintage_Style_Photography_Classic_convertible_driving_on_an_open_road_vintage_style_2.jpg", + "id": "7ffe80b5-11d6-419b-89d1-25abe7cd3fd0", + "__typename": "generated_images", + }, + "imageCount": 59908, + "__typename": "custom_models", + "generations": [ + { + "prompt": " A vibrant, sun-drenched vintage-style street scene with a classic car parked in the foreground.", + "generated_images": [ + { + "id": "c07a9677-2747-42c7-90fa-1638f36a08ea", + "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/f49e35b6-0ed8-4fa7-a015-c3e59a0bd4a3/Vintage_v1_A_vibrant_sundrenched_vintagestyle_street_scene_with_a_clas_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "f3296a34-9aef-4370-ad18-88daf26862c3", + "name": "DreamShaper 3.2", + "description": "This model by Lykon is great at a range of portrait styles as well as artistic backgrounds.", + "instancePrompt": None, + "modelHeight": 832, + "modelWidth": 640, + "coreModel": "SD", + "createdAt": "2023-01-23T11:21:17.532", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/b2919072-7c52-409e-9c2a-11d56c5c2ed2/DreamShaper_32_Full_body_Beautiful_final_fantasy_style_portrait_clean_detai_2.jpg", + "id": "758af0e6-ffcc-494a-9543-aa123612c968", + "__typename": "generated_images", + }, + "imageCount": 856247, + "__typename": "custom_models", + "generations": [ + { + "prompt": "beautiful elve,dragon , portait, highly detailed, 8k", + "generated_images": [ + { + "id": "43d66bae-8a8d-45c0-8b94-b252b556dc4c", + "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/c315ef67-1b35-43da-8440-e2b802b1008a/DreamShaper_beautiful_elvedragon_portait_highly_detailed_8k_0.jpg", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "cd2b2a15-9760-4174-a5ff-4d2925057376", + "name": "Leonardo Select", + "description": "A powerful finetune of SD2.1 that can achieve a high level of realism.", + "instancePrompt": None, + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-01-06T01:05:25.657", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 481673, + "__typename": "custom_models", + "generations": [ + { + "prompt": "portrait of female character wearing 80s clothing. she should look to the left. she should have blonde hair ", + "generated_images": [ + { + "id": "5c1a4725-160d-49cb-9b91-4ad2ac3de9a8", + "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/ff4e1c40-933a-47da-8c52-31689ed282d7/Leonardo_Select_portrait_of_female_character_wearing_80s_clothing_she_should_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "6bef9f1b-29cb-40c7-b9df-32b51c1f67d3", + "name": "Leonardo Creative", + "description": "An alternative finetune of SD 2.1 that brings a little more creative interpretation to the mix.", + "instancePrompt": None, + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2023-01-06T01:02:38.315", + "sdVersion": "v2", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 2416039, + "__typename": "custom_models", + "generations": [ + { + "prompt": "portrait of male character wearing 80s clothing. he should look to the left. he should have blonde hair and a mustache", + "generated_images": [ + { + "id": "d0841a38-0e78-4a84-acc7-62c669f8cf78", + "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/3665e729-cf65-4766-aebc-e351bc27e239/Leonardo_Creative_portrait_of_male_character_wearing_80s_clothing_he_should_lo_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "47a6232a-1d49-4c95-83c3-2cc5342f82c7", + "name": "Battle Axes", + "description": "Generate a variety of detailed axe designs with this model. From medieval battle axes to modern chopping axes, this model is great for creating a r...", + "instancePrompt": "Axe", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-30T12:20:12.519", + "sdVersion": "v1_5", + "type": "GAME_ITEMS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59", "username": "Jachin", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/1a60b46c-24d1-47c0-9683-e0e837b6f129/Battle_Axes_an_axe_blade_centre_of_frame_3.jpg", + "id": "29e709a4-00a0-48c1-af53-1d9640a6febd", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " A menacing, jagged-edged axe with a glowing, fiery blade, perfect for a fantasy RPG.", + "generated_images": [ + { + "id": "364f31e2-d337-40ad-bc5f-c683aea2cdf0", + "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/f5e64c82-0d31-40fc-bb8b-3a4c273243d4/Default_A_menacing_jaggededged_axe_with_a_glowing_fiery_blade_perfec_0.png", + "likeCount": 1, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "e5a291b6-3990-495a-b1fa-7bd1864510a6", + "name": "Pixel Art", + "description": "A pixel art model that's trained on headshots, but is surprisingly flexible with all sorts of subjects.", + "instancePrompt": "pixel art", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-30T06:21:16.301", + "sdVersion": None, + "type": "PIXEL_ART", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 85083, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A robotic cat with glowing eyes and sleek metal fur.", + "generated_images": [ + { + "id": "b582fad8-9149-4733-9798-19ec5fb84393", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/5628b06f-d989-4f89-87b2-9f7af4bb0ee7/Default_A_robotic_cat_with_glowing_eyes_and_sleek_metal_fur_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "302e258f-29b5-4dd8-9a7c-0cd898cb2143", + "name": "Chest Armor", + "description": "Create all sorts of chest armor with this model in a consistent style but with wide thematic range.", + "instancePrompt": "chest armor", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-29T21:13:02.608", + "sdVersion": "v1_5", + "type": "GAME_ITEMS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "test chest armor", + "generated_images": [ + { + "id": "050cb02c-5122-4e97-9810-f784e6bb64c4", + "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/465401c6-febf-4223-a9f1-fae4e54931bf/Default_test_chest_armor_2.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "102a8ee0-cf16-477c-8477-c76963a0d766", + "name": "Crystal Deposits", + "description": "A model for creating crystal deposits. Well-suited for use as items or in an isometric environment.", + "instancePrompt": "a crystal", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-29T21:13:02.608", + "sdVersion": "v1_5", + "type": "ENVIRONMENTS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a yellow tkwst2, object, 2d object, art by tekkonkinkreet, octane render, unreal engine, 3D, 8K, ultra-detailed, intricate, sharp focus", + "generated_images": [ + { + "id": "f7542346-c640-40ba-9f51-453e1cea3020", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/a9c191fa-6137-4e01-922c-943ee4e4788c/Default_a_yellow_tkwst2_object_2d_object_art_by_tekkonkinkreet_octane_2.png", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "45ab2421-87de-44c8-a07c-3b87e3bfdf84", + "name": "Magic Potions", + "description": 'A great model for creating incredible semi-realistic magic potions. Try appending "intricately detailed, 3d vray render" to your prompt.', + "instancePrompt": "a magic potion", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-29T21:13:02.608", + "sdVersion": "v1_5", + "type": "GAME_ITEMS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/6ae64207-79bc-48e2-b721-46c00d8c938b/Default_a_stunningly_beautiful_magic_potion_containing_a_galaxy_fili_1.png", + "id": "3aa11a5c-0496-40ca-b635-e3c78f161666", + "__typename": "generated_images", + }, + "imageCount": 15026, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a stunning magic potion, intricately detailed, 3d vray render", + "generated_images": [ + { + "id": "a7303236-34f8-4ce7-a187-a95966b858ff", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8765c4f3-55ed-40c0-9aeb-778f54ee27ab/Default_a_stunning_magic_potion_intricately_detailed_3d_vray_render_3.png", + "likeCount": 3, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "6c95de60-a0bc-4f90-b637-ee8971caf3b0", + "name": "Character Portraits", + "description": "A model that's for creating awesome RPG characters of varying classes in a consistent style.", + "instancePrompt": "character portrait", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-29T21:13:02.608", + "sdVersion": "v1_5", + "type": "CHARACTERS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "male king, green hair, detailed, soft, hyper-detailed, Cinematic, octane render", + "generated_images": [ + { + "id": "df6c66b3-882d-4303-8bb2-e8073c2e677b", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/1d554364-792b-498a-be17-ea0dfcca67d1/Default_male_king_green_hair_detailed_soft_hyperdetailed_Cinematic_oc_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "2d18c0af-374e-4391-9ca2-639f59837c85", + "name": "Magic Items", + "description": "Create a wide range of magical items like weapons, shields, boots, books. Very versatile.", + "instancePrompt": "an item", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-29T21:13:02.608", + "sdVersion": "v1_5", + "type": "GAME_ITEMS", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a red boot, object, concept art, dota 2 style, red abstract background, watercolor, epic smooth illustration", + "generated_images": [ + { + "id": "66dfe54f-2a56-49d7-b5aa-04ad012a9293", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/1a2ce3b1-998c-4795-8582-6f49aca28160/Default_a_red_boot_object_concept_art_dota_2_style_red_abstract_backg_1.png", + "likeCount": 3, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "ff883b60-9040-4c18-8d4e-ba7522c6b71d", + "name": "Amulets", + "description": "Create unique and intricate amulets, jewellery and more. Try loading up the prompt terms to steer it in interesting directions.", + "instancePrompt": "an amulet", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": " A close-up of an ancient amulet, illuminated by a single ray of light from a nearby window.", + "generated_images": [ + { + "id": "b68004dd-c030-4e51-8e68-d9638506e15a", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/edccace2-4291-482b-b3c6-d735ff06e640/Default_A_closeup_of_an_ancient_amulet_illuminated_by_a_single_ray_o_2.png", + "likeCount": 1, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "4b2e0f95-f15e-48d8-ada3-c071d6104db8", + "name": "Christmas Stickers", + "description": "Generate festive and fun Christmas stickers with this model. From cute and colorful to traditional and elegant.", + "instancePrompt": "a sticker", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "test sticker", + "generated_images": [ + { + "id": "3c737702-f32e-4dc0-b2d6-6e8a9a036be5", + "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/15aaa53a-6ee1-4887-bfba-f00213f64756/Default_test_sticker_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "50c4f43b-f086-4838-bcac-820406244cec", + "name": "Cute Characters", + "description": 'Create cute and charming game characters, perfect for adding some whimsy to your game design. Be sure to include the word "character" in your prompts for best results.', + "instancePrompt": "a character", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 45280, + "__typename": "custom_models", + "generations": [ + { + "prompt": "A zany game character clad in an outrageous cowboy grass skirt, wild paisley jacket, and light-up sneakers.", + "generated_images": [ + { + "id": "68ed1341-ea92-4637-9bb9-237432628c13", + "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/85cf7bd8-a233-42ca-a7d0-e900a0bdbb2a/Default_A_zany_game_character_clad_in_an_outrageous_cowboy_grass_skir_1.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "6908bfaf-8cf2-4fda-8c46-03f892d82e06", + "name": "Cute Animal Characters", + "description": "Perfect for creating adorable and cute animal characters - loveable and playful designs.", + "instancePrompt": "a character", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 96633, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a rabbit character", + "generated_images": [ + { + "id": "de3da9e6-eab2-4306-a336-43edce4b00b0", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/6ac55e1f-500a-4967-bf7e-e5af117c5f6d/Default_a_rabbit_character_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "5fdadebb-17ae-472c-bf76-877e657f97de", + "name": "Spirit Creatures", + "description": "From whimsical fairy-like beings to mythical creatures, create unique cute spirit characters.", + "instancePrompt": "a creature", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a creature with more than six eyes in rainbow colors", + "generated_images": [ + { + "id": "f9e470bc-7fd3-4472-ae77-c1c09eb8f5d5", + "url": "https://cdn.leonardo.ai/users/7a129367-fa22-48ff-a5eb-441861c60a20/generations/8874bdc9-9e80-4acb-ae5a-fa2c4db6819b/Default_a_creature_with_more_than_six_eyes_in_rainbow_colors_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "ab200606-5d09-4e1e-9050-0b05b839e944", + "name": "Isometric Fantasy", + "description": 'Create all sorts of isometric fantasy environments. Try appending "3d vray render, isometric" and using a guidance scale of 6. For the negative prompt, try "unclear, harsh, oversaturated, soft, blurry".', + "instancePrompt": "isometric fantasy", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": { + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8f5f48d1-0042-4625-b47a-83e266432abf/Isometric_Fantasy_Waterfall_isolated_on_white_3d_vray_render_isometric_ultra_d_3.jpg", + "id": "8aa79ad7-dca0-4d88-9b8b-e4766e1e9047", + "__typename": "generated_images", + }, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a fantasy diorama with a castle and lake", + "generated_images": [ + { + "id": "9eeddd56-fd49-4805-8c37-4ee03ddbc5f1", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9f8ee7a6-e5b9-4fe1-9a14-77d7f44ad4e1/Default_a_fantasy_diorama_with_a_castle_and_lake_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "ee0fc1a3-aacb-48bf-9234-ada3cc02748f", + "name": "Shields", + "description": "Create a variety of impressively varied and detailed shield designs. Allows for an incredible range of material types.", + "instancePrompt": "a shield", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "An extraterrestrial shield bearing delicate swirls and ethereal filigrees", + "generated_images": [ + { + "id": "da79e8e6-2bd1-4022-82c4-f21127bbc3fc", + "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/7b3c338c-8011-42e7-bcc7-4c9cfd450b71/Default_An_extraterrestrial_shield_bearing_delicate_swirls_and_ethere_1.png", + "likeCount": 1, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "7a65f0ab-64a7-4be2-bcf3-64a1cc56f627", + "name": "Isometric Scifi Buildings", + "description": 'Great at creating scifi buildings of varying themes. Append the word isometric to your prompt to ensure an isometric view. "3d vray render" also helps steer the generation well. ', + "instancePrompt": "isometric scifi", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2022-12-28T20:31:20", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a stunning scifi base, turrets and lasers, 3d vray render", + "generated_images": [ + { + "id": "08cf2772-080e-4220-ac97-91cc13f18e61", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/1aa5f16a-b64e-4a41-98d2-702fae7d4ba2/Default_a_stunning_scifi_base_turrets_and_lasers_3d_vray_render_0.png", + "likeCount": 1, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "5fce4543-8e23-4b77-9c3f-202b3f1c211e", + "name": "Crystal Deposits Alternate", + "description": 'An alternative crystal deposits model that gives a slightly more realistic feel with its creations. Try using "object" and "3d vray render" in your prompts.', + "instancePrompt": "1@t crystal", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-28T14:41:59.451", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "a crystal", + "generated_images": [ + { + "id": "510a3f3c-542d-4d6b-8c49-b3e0cf82e44a", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/49c5c8a5-5fd9-4222-bc15-6ac8cd63cb8e/Default_a_crystal_3.png", + "likeCount": 2, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "756be0a8-38b1-4946-ad62-c0ac832422e3", + "name": "Isometric Asteroid Tiles", + "description": 'A model for creating isometric asteroid environment tiles. Try appending "3d vray render, unreal engine, beautiful, intricately detailed, trending on artstation, 8k" to your prompt.', + "instancePrompt": "1@t isometric asteroid", + "modelHeight": 512, + "modelWidth": 512, + "coreModel": "SD", + "createdAt": "2022-12-28T06:54:35.42", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 0, + "__typename": "custom_models", + "generations": [ + { + "prompt": "isometric tile, 3d vray render, unreal engine, beautiful, intricately detailed, trending on artstation, 8k", + "generated_images": [ + { + "id": "1b5c1821-9ebf-4fea-867f-58e847c0995e", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8cd48839-28fe-4f25-9a32-b7a8a118583b/Default_isometric_tile_3d_vray_render_unreal_engine_beautiful_intrica_0.png", + "likeCount": 0, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + { + "id": "291be633-cb24-434f-898f-e662799936ad", + "name": "Leonardo Signature", + "description": "The core model of the Leonardo platform. An extremely powerful and diverse finetune which is highly effective for a wide range of uses.", + "instancePrompt": "", + "modelHeight": 768, + "modelWidth": 768, + "coreModel": "SD", + "createdAt": "2022-12-24T21:08:03.749", + "sdVersion": "v1_5", + "type": "GENERAL", + "nsfw": False, + "public": True, + "trainingStrength": "MEDIUM", + "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, + "generated_image": None, + "imageCount": 864451, + "__typename": "custom_models", + "generations": [ + { + "prompt": "an incredibly stunning photograph of a throne room, soft lighting", + "generated_images": [ + { + "id": "eec2d87e-d27c-4b95-8a1a-b948fd49df1e", + "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/2956bf2a-3039-43ee-83d6-65318e5f58fa/Default_an_incredibly_stunning_photograph_of_a_throne_room_soft_light_2.png", + "likeCount": 122, + "__typename": "generated_images", + } + ], + "__typename": "generations", + } + ], + "user_favourite_custom_models": [], + }, + ] + } +} diff --git a/openai_engine/TODO b/openai_engine/TODO index 5c579c2..04bf8a9 100644 --- a/openai_engine/TODO +++ b/openai_engine/TODO @@ -1,3 +1,4 @@ - ADD STATISTICS tracking - ADD Logger -- ADD Translator \ No newline at end of file +- Add more params and fixes +- Add image handling method (generalistic) diff --git a/requirements.txt b/requirements.txt index f9b0044..371fca8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,5 @@ soundfile==0.12.1 numpy==1.25.2 # Image pillow==10.0.0 +# Leonardo +aiofiles==23.2.1 \ No newline at end of file From d4383b39a329592ffe2049d95dab32025e1b16c3 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 29 Aug 2023 00:03:00 +0200 Subject: [PATCH 20/32] v0.2 --- leonardo_engine/TODO | 1 - leonardo_engine/leonardo.py | 451 +++++++++++++++++++++++++++--------- 2 files changed, 340 insertions(+), 112 deletions(-) diff --git a/leonardo_engine/TODO b/leonardo_engine/TODO index c38e3b5..e69de29 100644 --- a/leonardo_engine/TODO +++ b/leonardo_engine/TODO @@ -1 +0,0 @@ -- Add logger \ No newline at end of file diff --git a/leonardo_engine/leonardo.py b/leonardo_engine/leonardo.py index 8171294..f291a56 100644 --- a/leonardo_engine/leonardo.py +++ b/leonardo_engine/leonardo.py @@ -2,9 +2,9 @@ import logging import os -import asyncio import aiofiles import aiohttp +import asyncio class Leonardo: @@ -27,16 +27,29 @@ def __init__(self, auth_token: str, logger: logging.Logger = None): self.___logger = logger self.___get_headers = {"content-type": "application/json"} self.___post_headers = {"accept": "application/json", "content-type": "application/json"} + if self.___logger: + self.___logger.debug("Leonardo init complete") async def get_user_info(self): """ This endpoint will return your user information, including your user ID. """ url = "https://cloud.leonardo.ai/api/rest/v1/me" - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requesting user info: GET {url}") + try: + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"User info: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while getting user info: {str(error)}") + raise async def post_generations( self, @@ -106,10 +119,21 @@ async def post_generations( "controlNet": control_net, "controlNetType": control_net_type, } - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requesting post generations: POST {url} with payload: {payload}") + try: + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Post generations: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while post generations: {str(error)}") + raise async def get_single_generation(self, generation_id: str): """ @@ -118,10 +142,21 @@ async def get_single_generation(self, generation_id: str): :param generation_id: The ID of the generation to return. """ url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested single generations: GET {url} with generation_id={generation_id}") + try: + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Single generations: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while get single generations: {str(error)}") + raise async def delete_single_generation(self, generation_id: str): """ @@ -130,10 +165,21 @@ async def delete_single_generation(self, generation_id: str): :param generation_id: The ID of the generation to delete. """ url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Delete generations with generation_id={generation_id}: DELETE {url}") + try: + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Generations {generation_id} has been deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while delete generation: {str(error)}") + raise async def get_generations_by_user(self, user_id: str, offset: int = 0, limit: int = 10): """ @@ -145,10 +191,21 @@ async def get_generations_by_user(self, user_id: str, offset: int = 0, limit: in """ url = f"https://cloud.leonardo.ai/api/rest/v1/generations/user/{user_id}" params = {"offset": offset, "limit": limit} - async with self.___session.get( - url, params=params, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested generations for {user_id} with params {params}: GET {url}") + try: + async with self.___session.get( + url, params=params, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Generations for user {user_id} are: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while obtaining user's generations: {str(error)}") + raise async def upload_init_image(self, file_path: str): """ @@ -163,23 +220,39 @@ async def upload_init_image(self, file_path: str): url = "https://cloud.leonardo.ai/api/rest/v1/init-image" payload = {"extension": extension} - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - data = await response.json() - - upload_url = data["uploadInitImage"]["url"] - fields = json.loads(data["uploadInitImage"]["fields"]) - - async with aiofiles.open(file_path, "rb") as f: - file_data = await f.read() - - fields.update({"file": file_data}) - - async with self.___session.post( - upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - return await response.text() + if self.___logger: + self.___logger.debug(f"Init image {file_path} upload requested with payload = {payload}: POST {url}") + try: + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + data = await response.json() + if self.___logger: + self.___logger.debug(f"Init image {file_path} initiated: {data}") + + upload_url = data["uploadInitImage"]["url"] + fields = json.loads(data["uploadInitImage"]["fields"]) + + async with aiofiles.open(file_path, "rb") as f: + file_data = await f.read() + + fields.update({"file": file_data}) + + if self.___logger: + self.___logger.debug(f"Init image {file_path} uploading with as binary: POST {upload_url}") + async with self.___session.post( + upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + response = await response.text() + if self.___logger: + self.___logger.debug(f"Init image {file_path} has been uploaded: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while upload init image: {str(error)}") + raise async def get_single_init_image(self, image_id: str): """ @@ -188,10 +261,21 @@ async def get_single_init_image(self, image_id: str): :param image_id: The ID of the init image to return. """ url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested single image with image_id={image_id}: GET {url}") + try: + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Single image provided: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while obtain single init image: {str(error)}") + raise async def delete_init_image(self, image_id: str): """ @@ -200,10 +284,21 @@ async def delete_init_image(self, image_id: str): :param image_id: The ID of the init image to delete. """ url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested to delete single image with image_id={image_id}: DELETE {url}") + try: + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Single image deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while deleting init image: {str(error)}") + raise async def create_upscale(self, image_id: str): """ @@ -213,10 +308,21 @@ async def create_upscale(self, image_id: str): """ url = "https://cloud.leonardo.ai/api/rest/v1/variations/upscale" payload = {"id": image_id} - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested to upscale image with payload {payload}: POST {url}") + try: + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Upscale created: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while upscaling image: {str(error)}") + raise async def get_variation_by_id(self, generation_id: str): """ @@ -225,10 +331,21 @@ async def get_variation_by_id(self, generation_id: str): :param generation_id: The ID of the variation to get. """ url = f"https://cloud.leonardo.ai/api/rest/v1/variations/{generation_id}" - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested to obtain variation by id {generation_id}: GET {url}") + try: + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Get variation by ID: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while get variation by id: {str(error)}") + raise async def create_dataset(self, name: str, description: str = None): """ @@ -239,10 +356,21 @@ async def create_dataset(self, name: str, description: str = None): """ url = "https://cloud.leonardo.ai/api/rest/v1/datasets" payload = {"name": name, "description": description} - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested to create dataset with payload {payload}: POST {url}") + try: + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Dataset has been created: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while create dataset: {str(error)}") + raise async def get_dataset_by_id(self, dataset_id: str): """ @@ -251,22 +379,44 @@ async def get_dataset_by_id(self, dataset_id: str): :param dataset_id: The ID of the dataset to return. """ url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() - - async def delete_dataset_by_id(self, id: str): + if self.___logger: + self.___logger.debug(f"Requested to obtain dataset dataset_id={dataset_id}: GET {url}") + try: + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Dataset with dataset_id={dataset_id} provided: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while get dataset: {str(error)}") + raise + + async def delete_dataset_by_id(self, dataset_id: str): """ This endpoint deletes the specific dataset. - :param id: The ID of the dataset to delete. + :param dataset_id: The ID of the dataset to delete. """ - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{id}" - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" + if self.___logger: + self.___logger.debug(f"Requested to delete dataset dataset_id={dataset_id}: DELETE {url}") + try: + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug(f"Dataset with dataset_id={dataset_id} has been deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while delete dataset: {str(error)}") + raise async def upload_dataset_image(self, dataset_id: str, file_path: str): """ @@ -281,24 +431,45 @@ async def upload_dataset_image(self, dataset_id: str, file_path: str): raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload" - payload = {"extension": extension} - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - data = await response.json() - - upload_url = data["uploadDatasetImage"]["url"] - fields = json.loads(data["uploadDatasetImage"]["fields"]) - - async with aiofiles.open(file_path, "rb") as f: - file_data = await f.read() - - fields.update({"file": file_data}) - async with self.___session.post( - upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - return await response.text() + payload = {"extension": extension} + if self.___logger: + self.___logger.debug(f"Requested to upload dataset_id={dataset_id} from {file_path}: POST {url}") + try: + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + data = await response.json() + if self.___logger: + self.___logger.debug( + f"Dataset with dataset_id={dataset_id} started to upload from {file_path}:" f" {response}" + ) + + upload_url = data["uploadDatasetImage"]["url"] + fields = json.loads(data["uploadDatasetImage"]["fields"]) + + async with aiofiles.open(file_path, "rb") as f: + file_data = await f.read() + + fields.update({"file": file_data}) + + if self.___logger: + self.___logger.debug(f"Uploading dataset_id={dataset_id} from {file_path}: POST {url}") + async with self.___session.post( + upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) + ) as post_response: + post_response.raise_for_status() + post_response = await response.text() + if self.___logger: + self.___logger.debug( + f"Dataset with dataset_id={dataset_id} uploaded using {file_path}:" f" {post_response}" + ) + return post_response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred uploading dataset: {str(error)}") + raise async def upload_generated_image_to_dataset(self, dataset_id: str, generated_image_id: str): """ @@ -309,10 +480,27 @@ async def upload_generated_image_to_dataset(self, dataset_id: str, generated_ima """ url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload/gen" payload = {"generatedImageId": generated_image_id} - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug( + f"Requested to upload generated_image_id={generated_image_id} " + f"to dataset_id={dataset_id}: POST {url}" + ) + try: + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + response = await response.json() + if self.___logger: + self.___logger.debug( + f"Image with image_id={generated_image_id} has been uploaded to " + f"dataset_id={dataset_id}: {response}" + ) + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while upload generated image to dataset: {str(error)}") + raise async def train_custom_model( self, @@ -351,10 +539,21 @@ async def train_custom_model( "sd_Version": sd_version, "strength": strength, } - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested to train custom model with payload {payload}: POST {url}") + try: + async with self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) as response: + response.raise_for_status() + response = await response.text() + if self.___logger: + self.___logger.debug(f"Custom modal has been trained: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error training custom model: {str(error)}") + raise async def get_custom_model_by_id(self, model_id: str): """ @@ -363,10 +562,21 @@ async def get_custom_model_by_id(self, model_id: str): :param model_id: The ID of the custom model to return. """ url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() + if self.___logger: + self.___logger.debug(f"Requested to obtain custom model by model_id={model_id}: GET {url}") + try: + async with self.___session.get( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.text() + if self.___logger: + self.___logger.debug(f"Custom modal has been trained: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error obtaining custom model: {str(error)}") + raise async def delete_custom_model_by_id(self, model_id: str): """ @@ -375,35 +585,54 @@ async def delete_custom_model_by_id(self, model_id: str): :param model_id: The ID of the model to delete. """ url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - return await response.json() - - async def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5): + if self.___logger: + self.___logger.debug(f"Requested to delete custom model by model_id={model_id}: GET {url}") + try: + async with self.___session.delete( + url, headers=self.___session.headers.copy().update(self.___get_headers) + ) as response: + response.raise_for_status() + response = await response.text() + if self.___logger: + self.___logger.debug(f"Custom modal has been deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error delete custom model: {str(error)}") + raise + + async def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5, timeout=120): """ This method waits for the completion of image generation. :param generation_id: The ID of the generation to check. - :param image_index: (Optional) The index of the specific image to wait for. If None, waits for all images to complete. + :param image_index: (Optional) The index of the specific image to wait for. + If None, waits for all images to complete. :param poll_interval: (Optional) The time interval in seconds between each check. Default is 5 seconds. + :param timeout: (Optional) Waiting timeout. Default is 120 seconds. :raises IndexError: If an invalid image_index is provided. :return: The completed image(s) once generation is complete. """ + timeout_counter = 0 while True: response = await self.get_single_generation(generation_id) - generation = response.get('generations_by_pk', {}) - images = generation.get('generated_images', []) + generation = response.get("generations_by_pk", {}) + images = generation.get("generated_images", []) if image_index is not None: if image_index >= len(images): raise IndexError("Incorrect image index") - if images[image_index].get('status') == 'COMPLETE': + if images[image_index].get("status") == "COMPLETE": return images[image_index] else: - if all(image.get('status') == 'COMPLETE' for image in images): + if all(image.get("status") == "COMPLETE" for image in images): return images - await asyncio.sleep(poll_interval) \ No newline at end of file + await asyncio.sleep(poll_interval) + + if timeout_counter >= timeout / poll_interval: + raise TimeoutError(f"Image has not been generated in {timeout} seconds") + else: + timeout_counter += 1 From 63db83f4b865a5b8d56842c1381ef52d10d463b6 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 29 Aug 2023 12:32:01 +0200 Subject: [PATCH 21/32] v0.2 --- utils/tts.py | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/utils/tts.py b/utils/tts.py index 31b675a..c2a6618 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -5,22 +5,21 @@ Copyright (c) 2023. All rights reserved. Created: 25.08.2023 -Last Modified: 25.08.2023 +Last Modified: 29.08.2023 Description: This module contains implementation for Text-to-Speach tools """ import os +import threading import tempfile from time import sleep from uuid import uuid4 from gtts import gTTS -from mpyg321.mpyg321 import MPyg321Player -from mutagen.mp3 import MP3 -from pydub import AudioSegment from pyttsx4 import init as pyttsx_init +from pydub import AudioSegment, playback class CustomTTS: @@ -48,35 +47,27 @@ def __init__( :param voice: default TTS voice to use. Default is 'com.apple.voice.enhanced.ru-RU.Katya' """ self.___method = method - self.___player = MPyg321Player() self.___pytts = pyttsx_init() self.___lang = lang self.___voice = voice self.___speedup = speedup self.___frame = frame + self.semaphore = threading.Semaphore(1) - def __process_via_gtts(self, text): - """ - Converts text to speach using Google text-to-speach method + def play_audio(self, audio): + playback.play(audio) + self.semaphore.release() - :param text: Text needs to be converted to speach. - """ + async def __process_via_gtts(self, text): temp_dir = tempfile.gettempdir() - # gtts tts = gTTS(text, lang=self.___lang) raw_file = f"{temp_dir}/{str(uuid4())}.mp3" tts.save(raw_file) - audio = AudioSegment.from_file(raw_file, format="mp3") - new = audio.speedup(self.___speedup) + audio = AudioSegment.from_file(raw_file, format="mp3").speedup(self.___speedup) os.remove(raw_file) - response_file = f"{temp_dir}/{str(uuid4())}.mp3" - new.export(response_file, format="mp3") - # player - self.___player.play_song(response_file) - audio = MP3(response_file) - sleep(audio.info.length) - self.___player.stop() - os.remove(response_file) + self.semaphore.acquire() + player_thread = threading.Thread(target=self.play_audio, args=(audio,)) + player_thread.start() def __process_via_pytts(self, text): """ @@ -94,6 +85,7 @@ def __process_via_pytts(self, text): sleep(self.___frame) engine.endLoop() + self.semaphore.release() def get_pytts_voices_list(self): """ @@ -110,6 +102,8 @@ async def process(self, text): :param text: Text needs to be converted to speach. """ if "google" in self.___method: - self.__process_via_gtts(text) + await self.__process_via_gtts(text) else: - self.__process_via_pytts(text) + self.semaphore.acquire() + player_thread = threading.Thread(target=self.__process_via_pytts, args=(text,)) + player_thread.start() From ce3845376d40e82a58965574c90eee9bf4aa9e37 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 29 Aug 2023 15:08:06 +0200 Subject: [PATCH 22/32] v0.2 --- openai_engine/chatgpt.py | 4 ++-- utils/tts.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index f26590d..97bd52a 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -853,7 +853,7 @@ async def str_chat(self, prompt, chat_name=None, default_choice=0): async def transcript(self, file, prompt=None, language="en", response_format="text"): """ - Wrapper for the chat function. Returns only the content of the message. + Wrapper for the transcribe function. Returns only the content of the message. :param file: Path with filename to transcript. :param prompt: Previous prompt. Default is None. @@ -878,7 +878,7 @@ async def transcript(self, file, prompt=None, language="en", response_format="te async def translate(self, file, prompt=None, response_format="text"): """ - Wrapper for the chat function. Returns only the content of the message. + Wrapper for the translate function. Returns only the content of the message. :param file: Path with filename to transcript. :param prompt: Previous prompt. Default is None. diff --git a/utils/tts.py b/utils/tts.py index c2a6618..d681cb9 100644 --- a/utils/tts.py +++ b/utils/tts.py @@ -55,10 +55,19 @@ def __init__( self.semaphore = threading.Semaphore(1) def play_audio(self, audio): + """ Service method to play audio in monopoly mode using pydub + + :param audio: AudioSegment needs to be played. + """ playback.play(audio) self.semaphore.release() async def __process_via_gtts(self, text): + """ + Converts text to speach using gtts text-to-speach method + + :param text: Text needs to be converted to speach. + """ temp_dir = tempfile.gettempdir() tts = gTTS(text, lang=self.___lang) raw_file = f"{temp_dir}/{str(uuid4())}.mp3" From 9ab883ea29b4c35c9a7ec518f97eea6c32c4aba2 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 29 Aug 2023 17:24:29 +0200 Subject: [PATCH 23/32] v0.2 --- .../{leonardo.py => leonardo_async.py} | 33 +- leonardo_engine/leonardo_sync.py | 629 ++++++++++++++++++ 2 files changed, 652 insertions(+), 10 deletions(-) rename leonardo_engine/{leonardo.py => leonardo_async.py} (97%) create mode 100644 leonardo_engine/leonardo_sync.py diff --git a/leonardo_engine/leonardo.py b/leonardo_engine/leonardo_async.py similarity index 97% rename from leonardo_engine/leonardo.py rename to leonardo_engine/leonardo_async.py index f291a56..45b75e4 100644 --- a/leonardo_engine/leonardo.py +++ b/leonardo_engine/leonardo_async.py @@ -1,10 +1,23 @@ +# -*- coding: utf-8 -*- +""" +Filename: chatgpt.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 28.08.2023 +Last Modified: 28.09.2023 + +Description: +This file contains asynchronous implementation for Leonardo.ai API +""" + +import asyncio import json import logging import os import aiofiles import aiohttp -import asyncio class Leonardo: @@ -61,7 +74,7 @@ async def post_generations( width: int = 512, height: int = 512, num_inference_steps: int = None, - guidance_scale: int = None, + guidance_scale: int = 7, init_generation_image_id: str = None, init_image_id: str = None, init_strength: float = None, @@ -84,7 +97,7 @@ async def post_generations( :param width: The width of the images. Default is 512px. :param height: The height of the images. Default is 512px. :param num_inference_steps: The number of inference steps to use for the generation. - :param guidance_scale: How strongly the generation should reflect the prompt. + :param guidance_scale: How strongly the generation should reflect the prompt. Number from 1 to 20. Default is 7. :param init_generation_image_id: The ID of an existing image to use in image2image. :param init_image_id: The ID of an Init Image to use in image2image. :param init_strength: How strongly the generated images should reflect the original image in image2image. @@ -619,15 +632,15 @@ async def wait_for_image_generation(self, generation_id, image_index=None, poll_ while True: response = await self.get_single_generation(generation_id) generation = response.get("generations_by_pk", {}) - images = generation.get("generated_images", []) + status = generation.get("status") - if image_index is not None: - if image_index >= len(images): - raise IndexError("Incorrect image index") - if images[image_index].get("status") == "COMPLETE": + if status == "COMPLETE": + images = generation.get("generated_images", []) + if image_index is not None: + if image_index >= len(images): + raise IndexError("Incorrect image index") return images[image_index] - else: - if all(image.get("status") == "COMPLETE" for image in images): + else: return images await asyncio.sleep(poll_interval) diff --git a/leonardo_engine/leonardo_sync.py b/leonardo_engine/leonardo_sync.py new file mode 100644 index 0000000..0225f96 --- /dev/null +++ b/leonardo_engine/leonardo_sync.py @@ -0,0 +1,629 @@ +# -*- coding: utf-8 -*- +""" +Filename: chatgpt.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 29.08.2023 +Last Modified: 29.08.2023 + +Description: +This file contains synchronous implementation for Leonardo.ai API +""" + +import json +import logging +import os +import time + +import requests + + +class Leonardo: + """ + This class is for managing and interacting with Leonardo.ai service. + + Parameters: + auth_token (str): Auth Bearer token. Required. + logger (logging.Logger, optional): default logger. Default is None. + """ + + def __init__(self, auth_token: str, logger: logging.Logger = None): + """ + Constructs all the necessary attributes for the Leonardo object. + + :param auth_token: Auth Bearer token. Required. + :param logger: default logger. Default is None. + """ + self.___session = requests.Session() + self.___session.headers.update({"Authorization": f"Bearer {auth_token}"}) + self.___logger = logger + self.___get_headers = {"content-type": "application/json"} + self.___post_headers = {"accept": "application/json", "content-type": "application/json"} + if self.___logger: + self.___logger.debug("Leonardo init complete") + + def get_user_info(self): + """ + This endpoint will return your user information, including your user ID. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/me" + if self.___logger: + self.___logger.debug(f"Requesting user info: GET {url}") + try: + response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"User info: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while getting user info: {str(error)}") + raise + + async def post_generations( + self, + prompt: str, + negative_prompt: str = None, + model_id: str = None, + sd_version: str = None, + num_images: int = 4, + width: int = 512, + height: int = 512, + num_inference_steps: int = None, + guidance_scale: int = None, + init_generation_image_id: str = None, + init_image_id: str = None, + init_strength: float = None, + scheduler: str = None, + preset_style: str = None, + tiling: bool = False, + public: bool = False, + prompt_magic: bool = True, + control_net: bool = None, + control_net_type: str = None, + ): + """ + This endpoint will generate images. + + :param prompt: The prompt used to generate images. + :param negative_prompt: The negative prompt used for the image generation. + :param model_id: The model ID used for the image generation. + :param sd_version: The base version of stable diffusion to use if not using a custom model. + :param num_images: The number of images to generate. Default is 4. + :param width: The width of the images. Default is 512px. + :param height: The height of the images. Default is 512px. + :param num_inference_steps: The number of inference steps to use for the generation. + :param guidance_scale: How strongly the generation should reflect the prompt. + :param init_generation_image_id: The ID of an existing image to use in image2image. + :param init_image_id: The ID of an Init Image to use in image2image. + :param init_strength: How strongly the generated images should reflect the original image in image2image. + :param scheduler: The scheduler to generate images with. + :param preset_style: The style to generate images with. + :param tiling: Whether the generated images should tile on all axis. Default is False. + :param public: Whether the generated images should show in the community feed. Default is False. + :param prompt_magic: Enable to use Prompt Magic. Default is True. + :param control_net: Enable to use ControlNet. Requires an init image to be provided. + Requires a model based on SD v1.5 + :param control_net_type: The type of ControlNet to use. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/generations" + payload = { + "prompt": prompt, + "negative_prompt": negative_prompt, + "modelId": model_id, + "sd_version": sd_version, + "num_images": num_images, + "width": width, + "height": height, + "num_inference_steps": num_inference_steps, + "guidance_scale": guidance_scale, + "init_generation_image_id": init_generation_image_id, + "init_image_id": init_image_id, + "init_strength": init_strength, + "scheduler": scheduler, + "presetStyle": preset_style, + "tiling": tiling, + "public": public, + "promptMagic": prompt_magic, + "controlNet": control_net, + "controlNetType": control_net_type, + } + if self.___logger: + self.___logger.debug(f"Requesting post generations: POST {url} with payload: {payload}") + try: + response = self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Post generations: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while post generations: {str(error)}") + raise + + async def get_single_generation(self, generation_id: str): + """ + This endpoint will provide information about a specific generation. + + :param generation_id: The ID of the generation to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" + if self.___logger: + self.___logger.debug(f"Requested single generations: GET {url} with generation_id={generation_id}") + try: + response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Single generations: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while get single generations: {str(error)}") + raise + + async def delete_single_generation(self, generation_id: str): + """ + This endpoint deletes a specific generation. + + :param generation_id: The ID of the generation to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" + if self.___logger: + self.___logger.debug(f"Delete generations with generation_id={generation_id}: DELETE {url}") + try: + response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Generations {generation_id} has been deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while delete generation: {str(error)}") + raise + + async def get_generations_by_user(self, user_id: str, offset: int = 0, limit: int = 10): + """ + This endpoint returns all generations by a specific user. + + :param user_id: The ID of the user. + :param offset: The offset for pagination. + :param limit: The limit for pagination. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/generations/user/{user_id}" + params = {"offset": offset, "limit": limit} + if self.___logger: + self.___logger.debug(f"Requested generations for {user_id} with params {params}: GET {url}") + try: + response = self.___session.get( + url, params=params, headers=self.___session.headers.copy().update(self.___get_headers) + ) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Generations for user {user_id} are: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while obtaining user's generations: {str(error)}") + raise + + def upload_init_image(self, file_path: str): + """ + This endpoint returns presigned details to upload an init image to S3. + + :param file_path: The path to the image file. + """ + valid_extensions = ["png", "jpg", "jpeg", "webp"] + extension = os.path.splitext(file_path)[1].strip(".") + if extension not in valid_extensions: + raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") + + url = "https://cloud.leonardo.ai/api/rest/v1/init-image" + payload = {"extension": extension} + if self.___logger: + self.___logger.debug(f"Init image {file_path} upload requested with payload = {payload}: POST {url}") + try: + response = self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + data = response.json() + if self.___logger: + self.___logger.debug(f"Init image {file_path} initiated: {data}") + + upload_url = data["uploadInitImage"]["url"] + fields = json.loads(data["uploadInitImage"]["fields"]) + + with open(file_path, "rb") as f: + file_data = f.read() + + fields.update({"file": file_data}) + + if self.___logger: + self.___logger.debug(f"Init image {file_path} uploading as binary: POST {upload_url}") + response = self.___session.post( + upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + response = response.text() + if self.___logger: + self.___logger.debug(f"Init image {file_path} has been uploaded: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while upload init image: {str(error)}") + raise + + async def get_single_init_image(self, image_id: str): + """ + This endpoint will return a single init image. + + :param image_id: The ID of the init image to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" + if self.___logger: + self.___logger.debug(f"Requested single image with image_id={image_id}: GET {url}") + try: + response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Single image provided: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while obtain single init image: {str(error)}") + raise + + async def delete_init_image(self, image_id: str): + """ + This endpoint deletes an init image. + + :param image_id: The ID of the init image to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" + if self.___logger: + self.___logger.debug(f"Requested to delete single image with image_id={image_id}: DELETE {url}") + try: + response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Single image deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while deleting init image: {str(error)}") + raise + + async def create_upscale(self, image_id: str): + """ + This endpoint will create an upscale for the provided image ID. + + :param image_id: The ID of the image to upscale. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/variations/upscale" + payload = {"id": image_id} + if self.___logger: + self.___logger.debug(f"Requested to upscale image with payload {payload}: POST {url}") + try: + response = self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Upscale created: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while upscaling image: {str(error)}") + raise + + async def get_variation_by_id(self, generation_id: str): + """ + This endpoint will get the variation by ID. + + :param generation_id: The ID of the variation to get. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/variations/{generation_id}" + if self.___logger: + self.___logger.debug(f"Requested to obtain variation by id {generation_id}: GET {url}") + try: + response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Get variation by ID: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while get variation by id: {str(error)}") + raise + + async def create_dataset(self, name: str, description: str = None): + """ + This endpoint creates a new dataset. + + :param name: The name of the dataset. + :param description: A description for the dataset. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/datasets" + payload = {"name": name, "description": description} + if self.___logger: + self.___logger.debug(f"Requested to create dataset with payload {payload}: POST {url}") + try: + response = self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Dataset has been created: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while create dataset: {str(error)}") + raise + + async def get_dataset_by_id(self, dataset_id: str): + """ + This endpoint gets the specific dataset. + + :param dataset_id: The ID of the dataset to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" + if self.___logger: + self.___logger.debug(f"Requested to obtain dataset dataset_id={dataset_id}: GET {url}") + try: + response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Dataset with dataset_id={dataset_id} provided: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while get dataset: {str(error)}") + raise + + async def delete_dataset_by_id(self, dataset_id: str): + """ + This endpoint deletes the specific dataset. + + :param dataset_id: The ID of the dataset to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" + if self.___logger: + self.___logger.debug(f"Requested to delete dataset dataset_id={dataset_id}: DELETE {url}") + try: + response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug(f"Dataset with dataset_id={dataset_id} has been deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while delete dataset: {str(error)}") + raise + + def upload_dataset_image(self, dataset_id: str, file_path: str): + """ + This endpoint returns presigned details to upload a dataset image to S3. + + :param dataset_id: The ID of the dataset to which the image will be uploaded. + :param file_path: The path to the image file. + """ + valid_extensions = ["png", "jpg", "jpeg", "webp"] + extension = os.path.splitext(file_path)[1].strip(".") + if extension not in valid_extensions: + raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") + + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload" + + payload = {"extension": extension} + if self.___logger: + self.___logger.debug(f"Requested to upload dataset_id={dataset_id} from {file_path}: POST {url}") + try: + response = self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + data = response.json() + if self.___logger: + self.___logger.debug( + f"Dataset with dataset_id={dataset_id} started to upload from {file_path}:" f" {response}" + ) + + upload_url = data["uploadDatasetImage"]["url"] + fields = json.loads(data["uploadDatasetImage"]["fields"]) + + with open(file_path, "rb") as f: + file_data = f.read() + + fields.update({"file": file_data}) + + if self.___logger: + self.___logger.debug(f"Uploading dataset_id={dataset_id} from {file_path}: POST {url}") + response = self.___session.post( + upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + response = response.text() + if self.___logger: + self.___logger.debug(f"Dataset with dataset_id={dataset_id} uploaded using {file_path}:" f" {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred uploading dataset: {str(error)}") + raise + + async def upload_generated_image_to_dataset(self, dataset_id: str, generated_image_id: str): + """ + This endpoint will upload a previously generated image to the dataset. + + :param dataset_id: The ID of the dataset to upload the image to. + :param generated_image_id: The ID of the image to upload to the dataset. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload/gen" + payload = {"generatedImageId": generated_image_id} + if self.___logger: + self.___logger.debug( + f"Requested to upload generated_image_id={generated_image_id} " + f"to dataset_id={dataset_id}: POST {url}" + ) + try: + response = self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + response = response.json() + if self.___logger: + self.___logger.debug( + f"Image with image_id={generated_image_id} has been uploaded to " + f"dataset_id={dataset_id}: {response}" + ) + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error occurred while upload generated image to dataset: {str(error)}") + raise + + async def train_custom_model( + self, + name: str, + dataset_id: str, + instance_prompt: str, + description: str = None, + model_type: str = "GENERAL", + nsfw: bool = False, + resolution: int = 512, + sd_version: str = None, + strength: str = "MEDIUM", + ): + """ + This endpoint will train a new custom model. + + :param name: The name of the model. + :param description: The description of the model. + :param dataset_id: The ID of the dataset to train the model on. + :param instance_prompt: The instance prompt to use during training. + :param model_type: The category the most accurately reflects the model. + :param nsfw: whether or not the model is NSFW. + :param resolution: The resolution for training. Must be 512 or 768. + :param sd_version: The base version of stable diffusion to use if not using a custom model. + :param strength: When training using the PIXEL_ART model type, this influences the training strength. + """ + url = "https://cloud.leonardo.ai/api/rest/v1/models" + payload = { + "name": name, + "description": description, + "datasetId": dataset_id, + "instance_prompt": instance_prompt, + "modelType": model_type, + "nsfw": nsfw, + "resolution": resolution, + "sd_Version": sd_version, + "strength": strength, + } + if self.___logger: + self.___logger.debug(f"Requested to train custom model with payload {payload}: POST {url}") + try: + response = self.___session.post( + url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) + ) + response.raise_for_status() + response = await response.text() + if self.___logger: + self.___logger.debug(f"Custom modal has been trained: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error training custom model: {str(error)}") + raise + + async def get_custom_model_by_id(self, model_id: str): + """ + This endpoint gets the specific custom model. + + :param model_id: The ID of the custom model to return. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" + if self.___logger: + self.___logger.debug(f"Requested to obtain custom model by model_id={model_id}: GET {url}") + try: + response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = response.text() + if self.___logger: + self.___logger.debug(f"Custom modal has been trained: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error obtaining custom model: {str(error)}") + raise + + async def delete_custom_model_by_id(self, model_id: str): + """ + This endpoint will delete a specific custom model. + + :param model_id: The ID of the model to delete. + """ + url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" + if self.___logger: + self.___logger.debug(f"Requested to delete custom model by model_id={model_id}: GET {url}") + try: + response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) + response.raise_for_status() + response = await response.text() + if self.___logger: + self.___logger.debug(f"Custom modal has been deleted: {response}") + return response + except Exception as error: + if self.___logger: + self.___logger.error(f"Error delete custom model: {str(error)}") + raise + + async def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5, timeout=120): + """ + This method waits for the completion of image generation. + + :param generation_id: The ID of the generation to check. + :param image_index: (Optional) The index of the specific image to wait for. + If None, waits for all images to complete. + :param poll_interval: (Optional) The time interval in seconds between each check. Default is 5 seconds. + :param timeout: (Optional) Waiting timeout. Default is 120 seconds. + + :raises IndexError: If an invalid image_index is provided. + + :return: The completed image(s) once generation is complete. + """ + timeout_counter = 0 + while True: + response = await self.get_single_generation(generation_id) + generation = response.get("generations_by_pk", {}) + status = generation.get("status") + + if status == "COMPLETE": + images = generation.get("generated_images", []) + if image_index is not None: + if image_index >= len(images): + raise IndexError("Incorrect image index") + return images[image_index] + else: + return images + + time.sleep(poll_interval) + + if timeout_counter >= timeout / poll_interval: + raise TimeoutError(f"Image has not been generated in {timeout} seconds") + else: + timeout_counter += 1 From 551205c964d79e4a9b4bb87ce141afcb46a8e268 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 29 Aug 2023 18:06:36 +0200 Subject: [PATCH 24/32] v0.2 --- leonardo_engine/leonardo_sync.py | 38 ++++++++++++++++---------------- openai_engine/chatgpt.py | 1 - 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/leonardo_engine/leonardo_sync.py b/leonardo_engine/leonardo_sync.py index 0225f96..485210c 100644 --- a/leonardo_engine/leonardo_sync.py +++ b/leonardo_engine/leonardo_sync.py @@ -62,7 +62,7 @@ def get_user_info(self): self.___logger.error(f"Error occurred while getting user info: {str(error)}") raise - async def post_generations( + def post_generations( self, prompt: str, negative_prompt: str = None, @@ -146,7 +146,7 @@ async def post_generations( self.___logger.error(f"Error occurred while post generations: {str(error)}") raise - async def get_single_generation(self, generation_id: str): + def get_single_generation(self, generation_id: str): """ This endpoint will provide information about a specific generation. @@ -167,7 +167,7 @@ async def get_single_generation(self, generation_id: str): self.___logger.error(f"Error occurred while get single generations: {str(error)}") raise - async def delete_single_generation(self, generation_id: str): + def delete_single_generation(self, generation_id: str): """ This endpoint deletes a specific generation. @@ -188,7 +188,7 @@ async def delete_single_generation(self, generation_id: str): self.___logger.error(f"Error occurred while delete generation: {str(error)}") raise - async def get_generations_by_user(self, user_id: str, offset: int = 0, limit: int = 10): + def get_generations_by_user(self, user_id: str, offset: int = 0, limit: int = 10): """ This endpoint returns all generations by a specific user. @@ -261,7 +261,7 @@ def upload_init_image(self, file_path: str): self.___logger.error(f"Error occurred while upload init image: {str(error)}") raise - async def get_single_init_image(self, image_id: str): + def get_single_init_image(self, image_id: str): """ This endpoint will return a single init image. @@ -282,7 +282,7 @@ async def get_single_init_image(self, image_id: str): self.___logger.error(f"Error occurred while obtain single init image: {str(error)}") raise - async def delete_init_image(self, image_id: str): + def delete_init_image(self, image_id: str): """ This endpoint deletes an init image. @@ -303,7 +303,7 @@ async def delete_init_image(self, image_id: str): self.___logger.error(f"Error occurred while deleting init image: {str(error)}") raise - async def create_upscale(self, image_id: str): + def create_upscale(self, image_id: str): """ This endpoint will create an upscale for the provided image ID. @@ -327,7 +327,7 @@ async def create_upscale(self, image_id: str): self.___logger.error(f"Error occurred while upscaling image: {str(error)}") raise - async def get_variation_by_id(self, generation_id: str): + def get_variation_by_id(self, generation_id: str): """ This endpoint will get the variation by ID. @@ -348,7 +348,7 @@ async def get_variation_by_id(self, generation_id: str): self.___logger.error(f"Error occurred while get variation by id: {str(error)}") raise - async def create_dataset(self, name: str, description: str = None): + def create_dataset(self, name: str, description: str = None): """ This endpoint creates a new dataset. @@ -373,7 +373,7 @@ async def create_dataset(self, name: str, description: str = None): self.___logger.error(f"Error occurred while create dataset: {str(error)}") raise - async def get_dataset_by_id(self, dataset_id: str): + def get_dataset_by_id(self, dataset_id: str): """ This endpoint gets the specific dataset. @@ -394,7 +394,7 @@ async def get_dataset_by_id(self, dataset_id: str): self.___logger.error(f"Error occurred while get dataset: {str(error)}") raise - async def delete_dataset_by_id(self, dataset_id: str): + def delete_dataset_by_id(self, dataset_id: str): """ This endpoint deletes the specific dataset. @@ -466,7 +466,7 @@ def upload_dataset_image(self, dataset_id: str, file_path: str): self.___logger.error(f"Error occurred uploading dataset: {str(error)}") raise - async def upload_generated_image_to_dataset(self, dataset_id: str, generated_image_id: str): + def upload_generated_image_to_dataset(self, dataset_id: str, generated_image_id: str): """ This endpoint will upload a previously generated image to the dataset. @@ -497,7 +497,7 @@ async def upload_generated_image_to_dataset(self, dataset_id: str, generated_ima self.___logger.error(f"Error occurred while upload generated image to dataset: {str(error)}") raise - async def train_custom_model( + def train_custom_model( self, name: str, dataset_id: str, @@ -541,7 +541,7 @@ async def train_custom_model( url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) ) response.raise_for_status() - response = await response.text() + response = response.text() if self.___logger: self.___logger.debug(f"Custom modal has been trained: {response}") return response @@ -550,7 +550,7 @@ async def train_custom_model( self.___logger.error(f"Error training custom model: {str(error)}") raise - async def get_custom_model_by_id(self, model_id: str): + def get_custom_model_by_id(self, model_id: str): """ This endpoint gets the specific custom model. @@ -571,7 +571,7 @@ async def get_custom_model_by_id(self, model_id: str): self.___logger.error(f"Error obtaining custom model: {str(error)}") raise - async def delete_custom_model_by_id(self, model_id: str): + def delete_custom_model_by_id(self, model_id: str): """ This endpoint will delete a specific custom model. @@ -583,7 +583,7 @@ async def delete_custom_model_by_id(self, model_id: str): try: response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) response.raise_for_status() - response = await response.text() + response = response.text() if self.___logger: self.___logger.debug(f"Custom modal has been deleted: {response}") return response @@ -592,7 +592,7 @@ async def delete_custom_model_by_id(self, model_id: str): self.___logger.error(f"Error delete custom model: {str(error)}") raise - async def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5, timeout=120): + def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5, timeout=120): """ This method waits for the completion of image generation. @@ -608,7 +608,7 @@ async def wait_for_image_generation(self, generation_id, image_index=None, poll_ """ timeout_counter = 0 while True: - response = await self.get_single_generation(generation_id) + response = self.get_single_generation(generation_id) generation = response.get("generations_by_pk", {}) status = generation.get("status") diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py index 97bd52a..1cbbcb7 100644 --- a/openai_engine/chatgpt.py +++ b/openai_engine/chatgpt.py @@ -695,7 +695,6 @@ async def process_chat(self, prompt, default_choice=0, chat_name=None): func_call[key] = value if chunk["choices"][default_choice]["finish_reason"] is not None: if chunk["choices"][default_choice]["finish_reason"] == 'function_call': - print(func_call) func_response = await self.process_function(function_call=func_call) break if "content" in chunk["choices"][default_choice]["delta"]: From eb1e2adb08e151f3a41e7bf84de7b6068a446c66 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Wed, 30 Aug 2023 00:57:13 +0200 Subject: [PATCH 25/32] v0.2 --- .github/workflows/linters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 47a07ed..dc31c7f 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -83,7 +83,7 @@ jobs: fi - name: Check runner state run: | - if [[ "${{ steps.pylint.outcome }}" == "failure" || "${{ steps.black.outcome }}" == "failure" ]] || "${{ steps.mypy.outcome }}" == "failure" ]]; then + if [[ "${{ steps.pylint.outcome }}" == "failure" || "${{ steps.black.outcome }}" == "failure" || "${{ steps.mypy.outcome }}" == "failure" ]]; then echo "Linters failed, refer to related sections for info" exit 1 fi From 545b65b098a6a4287e547c4ecabb16e1350b6212 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 12 Sep 2023 17:02:03 +0200 Subject: [PATCH 26/32] v0.1 --- leonardo_engine/__init__.py | 0 leonardo_engine/leonardo_async.py | 651 ------ leonardo_engine/leonardo_sync.py | 629 ----- leonardo_engine/models.py | 3608 ----------------------------- requirements.txt | 2 +- 5 files changed, 1 insertion(+), 4889 deletions(-) delete mode 100644 leonardo_engine/__init__.py delete mode 100644 leonardo_engine/leonardo_async.py delete mode 100644 leonardo_engine/leonardo_sync.py delete mode 100644 leonardo_engine/models.py diff --git a/leonardo_engine/__init__.py b/leonardo_engine/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/leonardo_engine/leonardo_async.py b/leonardo_engine/leonardo_async.py deleted file mode 100644 index 45b75e4..0000000 --- a/leonardo_engine/leonardo_async.py +++ /dev/null @@ -1,651 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Filename: chatgpt.py -Author: Iliya Vereshchagin -Copyright (c) 2023. All rights reserved. - -Created: 28.08.2023 -Last Modified: 28.09.2023 - -Description: -This file contains asynchronous implementation for Leonardo.ai API -""" - -import asyncio -import json -import logging -import os - -import aiofiles -import aiohttp - - -class Leonardo: - """ - This class is for managing and interacting with Leonardo.ai service. - - Parameters: - auth_token (str): Auth Bearer token. Required. - logger (logging.Logger, optional): default logger. Default is None. - """ - - def __init__(self, auth_token: str, logger: logging.Logger = None): - """ - Constructs all the necessary attributes for the Leonardo object. - - :param auth_token: Auth Bearer token. Required. - :param logger: default logger. Default is None. - """ - self.___session = aiohttp.ClientSession(headers={"Authorization": f"Bearer {auth_token}"}) - self.___logger = logger - self.___get_headers = {"content-type": "application/json"} - self.___post_headers = {"accept": "application/json", "content-type": "application/json"} - if self.___logger: - self.___logger.debug("Leonardo init complete") - - async def get_user_info(self): - """ - This endpoint will return your user information, including your user ID. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/me" - if self.___logger: - self.___logger.debug(f"Requesting user info: GET {url}") - try: - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"User info: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while getting user info: {str(error)}") - raise - - async def post_generations( - self, - prompt: str, - negative_prompt: str = None, - model_id: str = None, - sd_version: str = None, - num_images: int = 4, - width: int = 512, - height: int = 512, - num_inference_steps: int = None, - guidance_scale: int = 7, - init_generation_image_id: str = None, - init_image_id: str = None, - init_strength: float = None, - scheduler: str = None, - preset_style: str = None, - tiling: bool = False, - public: bool = False, - prompt_magic: bool = True, - control_net: bool = None, - control_net_type: str = None, - ): - """ - This endpoint will generate images. - - :param prompt: The prompt used to generate images. - :param negative_prompt: The negative prompt used for the image generation. - :param model_id: The model ID used for the image generation. - :param sd_version: The base version of stable diffusion to use if not using a custom model. - :param num_images: The number of images to generate. Default is 4. - :param width: The width of the images. Default is 512px. - :param height: The height of the images. Default is 512px. - :param num_inference_steps: The number of inference steps to use for the generation. - :param guidance_scale: How strongly the generation should reflect the prompt. Number from 1 to 20. Default is 7. - :param init_generation_image_id: The ID of an existing image to use in image2image. - :param init_image_id: The ID of an Init Image to use in image2image. - :param init_strength: How strongly the generated images should reflect the original image in image2image. - :param scheduler: The scheduler to generate images with. - :param preset_style: The style to generate images with. - :param tiling: Whether the generated images should tile on all axis. Default is False. - :param public: Whether the generated images should show in the community feed. Default is False. - :param prompt_magic: Enable to use Prompt Magic. Default is True. - :param control_net: Enable to use ControlNet. Requires an init image to be provided. - Requires a model based on SD v1.5 - :param control_net_type: The type of ControlNet to use. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/generations" - payload = { - "prompt": prompt, - "negative_prompt": negative_prompt, - "modelId": model_id, - "sd_version": sd_version, - "num_images": num_images, - "width": width, - "height": height, - "num_inference_steps": num_inference_steps, - "guidance_scale": guidance_scale, - "init_generation_image_id": init_generation_image_id, - "init_image_id": init_image_id, - "init_strength": init_strength, - "scheduler": scheduler, - "presetStyle": preset_style, - "tiling": tiling, - "public": public, - "promptMagic": prompt_magic, - "controlNet": control_net, - "controlNetType": control_net_type, - } - if self.___logger: - self.___logger.debug(f"Requesting post generations: POST {url} with payload: {payload}") - try: - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Post generations: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while post generations: {str(error)}") - raise - - async def get_single_generation(self, generation_id: str): - """ - This endpoint will provide information about a specific generation. - - :param generation_id: The ID of the generation to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" - if self.___logger: - self.___logger.debug(f"Requested single generations: GET {url} with generation_id={generation_id}") - try: - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Single generations: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while get single generations: {str(error)}") - raise - - async def delete_single_generation(self, generation_id: str): - """ - This endpoint deletes a specific generation. - - :param generation_id: The ID of the generation to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" - if self.___logger: - self.___logger.debug(f"Delete generations with generation_id={generation_id}: DELETE {url}") - try: - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Generations {generation_id} has been deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while delete generation: {str(error)}") - raise - - async def get_generations_by_user(self, user_id: str, offset: int = 0, limit: int = 10): - """ - This endpoint returns all generations by a specific user. - - :param user_id: The ID of the user. - :param offset: The offset for pagination. - :param limit: The limit for pagination. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/generations/user/{user_id}" - params = {"offset": offset, "limit": limit} - if self.___logger: - self.___logger.debug(f"Requested generations for {user_id} with params {params}: GET {url}") - try: - async with self.___session.get( - url, params=params, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Generations for user {user_id} are: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while obtaining user's generations: {str(error)}") - raise - - async def upload_init_image(self, file_path: str): - """ - This endpoint returns presigned details to upload an init image to S3. - - :param file_path: The path to the image file. - """ - valid_extensions = ["png", "jpg", "jpeg", "webp"] - extension = os.path.splitext(file_path)[1].strip(".") - if extension not in valid_extensions: - raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") - - url = "https://cloud.leonardo.ai/api/rest/v1/init-image" - payload = {"extension": extension} - if self.___logger: - self.___logger.debug(f"Init image {file_path} upload requested with payload = {payload}: POST {url}") - try: - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - data = await response.json() - if self.___logger: - self.___logger.debug(f"Init image {file_path} initiated: {data}") - - upload_url = data["uploadInitImage"]["url"] - fields = json.loads(data["uploadInitImage"]["fields"]) - - async with aiofiles.open(file_path, "rb") as f: - file_data = await f.read() - - fields.update({"file": file_data}) - - if self.___logger: - self.___logger.debug(f"Init image {file_path} uploading with as binary: POST {upload_url}") - async with self.___session.post( - upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - response = await response.text() - if self.___logger: - self.___logger.debug(f"Init image {file_path} has been uploaded: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while upload init image: {str(error)}") - raise - - async def get_single_init_image(self, image_id: str): - """ - This endpoint will return a single init image. - - :param image_id: The ID of the init image to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" - if self.___logger: - self.___logger.debug(f"Requested single image with image_id={image_id}: GET {url}") - try: - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Single image provided: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while obtain single init image: {str(error)}") - raise - - async def delete_init_image(self, image_id: str): - """ - This endpoint deletes an init image. - - :param image_id: The ID of the init image to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" - if self.___logger: - self.___logger.debug(f"Requested to delete single image with image_id={image_id}: DELETE {url}") - try: - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Single image deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while deleting init image: {str(error)}") - raise - - async def create_upscale(self, image_id: str): - """ - This endpoint will create an upscale for the provided image ID. - - :param image_id: The ID of the image to upscale. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/variations/upscale" - payload = {"id": image_id} - if self.___logger: - self.___logger.debug(f"Requested to upscale image with payload {payload}: POST {url}") - try: - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Upscale created: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while upscaling image: {str(error)}") - raise - - async def get_variation_by_id(self, generation_id: str): - """ - This endpoint will get the variation by ID. - - :param generation_id: The ID of the variation to get. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/variations/{generation_id}" - if self.___logger: - self.___logger.debug(f"Requested to obtain variation by id {generation_id}: GET {url}") - try: - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Get variation by ID: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while get variation by id: {str(error)}") - raise - - async def create_dataset(self, name: str, description: str = None): - """ - This endpoint creates a new dataset. - - :param name: The name of the dataset. - :param description: A description for the dataset. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/datasets" - payload = {"name": name, "description": description} - if self.___logger: - self.___logger.debug(f"Requested to create dataset with payload {payload}: POST {url}") - try: - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Dataset has been created: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while create dataset: {str(error)}") - raise - - async def get_dataset_by_id(self, dataset_id: str): - """ - This endpoint gets the specific dataset. - - :param dataset_id: The ID of the dataset to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" - if self.___logger: - self.___logger.debug(f"Requested to obtain dataset dataset_id={dataset_id}: GET {url}") - try: - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Dataset with dataset_id={dataset_id} provided: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while get dataset: {str(error)}") - raise - - async def delete_dataset_by_id(self, dataset_id: str): - """ - This endpoint deletes the specific dataset. - - :param dataset_id: The ID of the dataset to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" - if self.___logger: - self.___logger.debug(f"Requested to delete dataset dataset_id={dataset_id}: DELETE {url}") - try: - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug(f"Dataset with dataset_id={dataset_id} has been deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while delete dataset: {str(error)}") - raise - - async def upload_dataset_image(self, dataset_id: str, file_path: str): - """ - This endpoint returns presigned details to upload a dataset image to S3. - - :param dataset_id: The ID of the dataset to which the image will be uploaded. - :param file_path: The path to the image file. - """ - valid_extensions = ["png", "jpg", "jpeg", "webp"] - extension = os.path.splitext(file_path)[1].strip(".") - if extension not in valid_extensions: - raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") - - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload" - - payload = {"extension": extension} - if self.___logger: - self.___logger.debug(f"Requested to upload dataset_id={dataset_id} from {file_path}: POST {url}") - try: - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - data = await response.json() - if self.___logger: - self.___logger.debug( - f"Dataset with dataset_id={dataset_id} started to upload from {file_path}:" f" {response}" - ) - - upload_url = data["uploadDatasetImage"]["url"] - fields = json.loads(data["uploadDatasetImage"]["fields"]) - - async with aiofiles.open(file_path, "rb") as f: - file_data = await f.read() - - fields.update({"file": file_data}) - - if self.___logger: - self.___logger.debug(f"Uploading dataset_id={dataset_id} from {file_path}: POST {url}") - async with self.___session.post( - upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) - ) as post_response: - post_response.raise_for_status() - post_response = await response.text() - if self.___logger: - self.___logger.debug( - f"Dataset with dataset_id={dataset_id} uploaded using {file_path}:" f" {post_response}" - ) - return post_response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred uploading dataset: {str(error)}") - raise - - async def upload_generated_image_to_dataset(self, dataset_id: str, generated_image_id: str): - """ - This endpoint will upload a previously generated image to the dataset. - - :param dataset_id: The ID of the dataset to upload the image to. - :param generated_image_id: The ID of the image to upload to the dataset. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload/gen" - payload = {"generatedImageId": generated_image_id} - if self.___logger: - self.___logger.debug( - f"Requested to upload generated_image_id={generated_image_id} " - f"to dataset_id={dataset_id}: POST {url}" - ) - try: - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - response = await response.json() - if self.___logger: - self.___logger.debug( - f"Image with image_id={generated_image_id} has been uploaded to " - f"dataset_id={dataset_id}: {response}" - ) - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while upload generated image to dataset: {str(error)}") - raise - - async def train_custom_model( - self, - name: str, - dataset_id: str, - instance_prompt: str, - description: str = None, - model_type: str = "GENERAL", - nsfw: bool = False, - resolution: int = 512, - sd_version: str = None, - strength: str = "MEDIUM", - ): - """ - This endpoint will train a new custom model. - - :param name: The name of the model. - :param description: The description of the model. - :param dataset_id: The ID of the dataset to train the model on. - :param instance_prompt: The instance prompt to use during training. - :param model_type: The category the most accurately reflects the model. - :param nsfw: whether or not the model is NSFW. - :param resolution: The resolution for training. Must be 512 or 768. - :param sd_version: The base version of stable diffusion to use if not using a custom model. - :param strength: When training using the PIXEL_ART model type, this influences the training strength. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/models" - payload = { - "name": name, - "description": description, - "datasetId": dataset_id, - "instance_prompt": instance_prompt, - "modelType": model_type, - "nsfw": nsfw, - "resolution": resolution, - "sd_Version": sd_version, - "strength": strength, - } - if self.___logger: - self.___logger.debug(f"Requested to train custom model with payload {payload}: POST {url}") - try: - async with self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) as response: - response.raise_for_status() - response = await response.text() - if self.___logger: - self.___logger.debug(f"Custom modal has been trained: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error training custom model: {str(error)}") - raise - - async def get_custom_model_by_id(self, model_id: str): - """ - This endpoint gets the specific custom model. - - :param model_id: The ID of the custom model to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" - if self.___logger: - self.___logger.debug(f"Requested to obtain custom model by model_id={model_id}: GET {url}") - try: - async with self.___session.get( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.text() - if self.___logger: - self.___logger.debug(f"Custom modal has been trained: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error obtaining custom model: {str(error)}") - raise - - async def delete_custom_model_by_id(self, model_id: str): - """ - This endpoint will delete a specific custom model. - - :param model_id: The ID of the model to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" - if self.___logger: - self.___logger.debug(f"Requested to delete custom model by model_id={model_id}: GET {url}") - try: - async with self.___session.delete( - url, headers=self.___session.headers.copy().update(self.___get_headers) - ) as response: - response.raise_for_status() - response = await response.text() - if self.___logger: - self.___logger.debug(f"Custom modal has been deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error delete custom model: {str(error)}") - raise - - async def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5, timeout=120): - """ - This method waits for the completion of image generation. - - :param generation_id: The ID of the generation to check. - :param image_index: (Optional) The index of the specific image to wait for. - If None, waits for all images to complete. - :param poll_interval: (Optional) The time interval in seconds between each check. Default is 5 seconds. - :param timeout: (Optional) Waiting timeout. Default is 120 seconds. - - :raises IndexError: If an invalid image_index is provided. - - :return: The completed image(s) once generation is complete. - """ - timeout_counter = 0 - while True: - response = await self.get_single_generation(generation_id) - generation = response.get("generations_by_pk", {}) - status = generation.get("status") - - if status == "COMPLETE": - images = generation.get("generated_images", []) - if image_index is not None: - if image_index >= len(images): - raise IndexError("Incorrect image index") - return images[image_index] - else: - return images - - await asyncio.sleep(poll_interval) - - if timeout_counter >= timeout / poll_interval: - raise TimeoutError(f"Image has not been generated in {timeout} seconds") - else: - timeout_counter += 1 diff --git a/leonardo_engine/leonardo_sync.py b/leonardo_engine/leonardo_sync.py deleted file mode 100644 index 485210c..0000000 --- a/leonardo_engine/leonardo_sync.py +++ /dev/null @@ -1,629 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Filename: chatgpt.py -Author: Iliya Vereshchagin -Copyright (c) 2023. All rights reserved. - -Created: 29.08.2023 -Last Modified: 29.08.2023 - -Description: -This file contains synchronous implementation for Leonardo.ai API -""" - -import json -import logging -import os -import time - -import requests - - -class Leonardo: - """ - This class is for managing and interacting with Leonardo.ai service. - - Parameters: - auth_token (str): Auth Bearer token. Required. - logger (logging.Logger, optional): default logger. Default is None. - """ - - def __init__(self, auth_token: str, logger: logging.Logger = None): - """ - Constructs all the necessary attributes for the Leonardo object. - - :param auth_token: Auth Bearer token. Required. - :param logger: default logger. Default is None. - """ - self.___session = requests.Session() - self.___session.headers.update({"Authorization": f"Bearer {auth_token}"}) - self.___logger = logger - self.___get_headers = {"content-type": "application/json"} - self.___post_headers = {"accept": "application/json", "content-type": "application/json"} - if self.___logger: - self.___logger.debug("Leonardo init complete") - - def get_user_info(self): - """ - This endpoint will return your user information, including your user ID. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/me" - if self.___logger: - self.___logger.debug(f"Requesting user info: GET {url}") - try: - response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"User info: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while getting user info: {str(error)}") - raise - - def post_generations( - self, - prompt: str, - negative_prompt: str = None, - model_id: str = None, - sd_version: str = None, - num_images: int = 4, - width: int = 512, - height: int = 512, - num_inference_steps: int = None, - guidance_scale: int = None, - init_generation_image_id: str = None, - init_image_id: str = None, - init_strength: float = None, - scheduler: str = None, - preset_style: str = None, - tiling: bool = False, - public: bool = False, - prompt_magic: bool = True, - control_net: bool = None, - control_net_type: str = None, - ): - """ - This endpoint will generate images. - - :param prompt: The prompt used to generate images. - :param negative_prompt: The negative prompt used for the image generation. - :param model_id: The model ID used for the image generation. - :param sd_version: The base version of stable diffusion to use if not using a custom model. - :param num_images: The number of images to generate. Default is 4. - :param width: The width of the images. Default is 512px. - :param height: The height of the images. Default is 512px. - :param num_inference_steps: The number of inference steps to use for the generation. - :param guidance_scale: How strongly the generation should reflect the prompt. - :param init_generation_image_id: The ID of an existing image to use in image2image. - :param init_image_id: The ID of an Init Image to use in image2image. - :param init_strength: How strongly the generated images should reflect the original image in image2image. - :param scheduler: The scheduler to generate images with. - :param preset_style: The style to generate images with. - :param tiling: Whether the generated images should tile on all axis. Default is False. - :param public: Whether the generated images should show in the community feed. Default is False. - :param prompt_magic: Enable to use Prompt Magic. Default is True. - :param control_net: Enable to use ControlNet. Requires an init image to be provided. - Requires a model based on SD v1.5 - :param control_net_type: The type of ControlNet to use. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/generations" - payload = { - "prompt": prompt, - "negative_prompt": negative_prompt, - "modelId": model_id, - "sd_version": sd_version, - "num_images": num_images, - "width": width, - "height": height, - "num_inference_steps": num_inference_steps, - "guidance_scale": guidance_scale, - "init_generation_image_id": init_generation_image_id, - "init_image_id": init_image_id, - "init_strength": init_strength, - "scheduler": scheduler, - "presetStyle": preset_style, - "tiling": tiling, - "public": public, - "promptMagic": prompt_magic, - "controlNet": control_net, - "controlNetType": control_net_type, - } - if self.___logger: - self.___logger.debug(f"Requesting post generations: POST {url} with payload: {payload}") - try: - response = self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Post generations: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while post generations: {str(error)}") - raise - - def get_single_generation(self, generation_id: str): - """ - This endpoint will provide information about a specific generation. - - :param generation_id: The ID of the generation to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" - if self.___logger: - self.___logger.debug(f"Requested single generations: GET {url} with generation_id={generation_id}") - try: - response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Single generations: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while get single generations: {str(error)}") - raise - - def delete_single_generation(self, generation_id: str): - """ - This endpoint deletes a specific generation. - - :param generation_id: The ID of the generation to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/generations/{generation_id}" - if self.___logger: - self.___logger.debug(f"Delete generations with generation_id={generation_id}: DELETE {url}") - try: - response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Generations {generation_id} has been deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while delete generation: {str(error)}") - raise - - def get_generations_by_user(self, user_id: str, offset: int = 0, limit: int = 10): - """ - This endpoint returns all generations by a specific user. - - :param user_id: The ID of the user. - :param offset: The offset for pagination. - :param limit: The limit for pagination. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/generations/user/{user_id}" - params = {"offset": offset, "limit": limit} - if self.___logger: - self.___logger.debug(f"Requested generations for {user_id} with params {params}: GET {url}") - try: - response = self.___session.get( - url, params=params, headers=self.___session.headers.copy().update(self.___get_headers) - ) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Generations for user {user_id} are: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while obtaining user's generations: {str(error)}") - raise - - def upload_init_image(self, file_path: str): - """ - This endpoint returns presigned details to upload an init image to S3. - - :param file_path: The path to the image file. - """ - valid_extensions = ["png", "jpg", "jpeg", "webp"] - extension = os.path.splitext(file_path)[1].strip(".") - if extension not in valid_extensions: - raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") - - url = "https://cloud.leonardo.ai/api/rest/v1/init-image" - payload = {"extension": extension} - if self.___logger: - self.___logger.debug(f"Init image {file_path} upload requested with payload = {payload}: POST {url}") - try: - response = self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - data = response.json() - if self.___logger: - self.___logger.debug(f"Init image {file_path} initiated: {data}") - - upload_url = data["uploadInitImage"]["url"] - fields = json.loads(data["uploadInitImage"]["fields"]) - - with open(file_path, "rb") as f: - file_data = f.read() - - fields.update({"file": file_data}) - - if self.___logger: - self.___logger.debug(f"Init image {file_path} uploading as binary: POST {upload_url}") - response = self.___session.post( - upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - response = response.text() - if self.___logger: - self.___logger.debug(f"Init image {file_path} has been uploaded: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while upload init image: {str(error)}") - raise - - def get_single_init_image(self, image_id: str): - """ - This endpoint will return a single init image. - - :param image_id: The ID of the init image to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" - if self.___logger: - self.___logger.debug(f"Requested single image with image_id={image_id}: GET {url}") - try: - response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Single image provided: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while obtain single init image: {str(error)}") - raise - - def delete_init_image(self, image_id: str): - """ - This endpoint deletes an init image. - - :param image_id: The ID of the init image to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/init-image/{image_id}" - if self.___logger: - self.___logger.debug(f"Requested to delete single image with image_id={image_id}: DELETE {url}") - try: - response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Single image deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while deleting init image: {str(error)}") - raise - - def create_upscale(self, image_id: str): - """ - This endpoint will create an upscale for the provided image ID. - - :param image_id: The ID of the image to upscale. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/variations/upscale" - payload = {"id": image_id} - if self.___logger: - self.___logger.debug(f"Requested to upscale image with payload {payload}: POST {url}") - try: - response = self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Upscale created: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while upscaling image: {str(error)}") - raise - - def get_variation_by_id(self, generation_id: str): - """ - This endpoint will get the variation by ID. - - :param generation_id: The ID of the variation to get. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/variations/{generation_id}" - if self.___logger: - self.___logger.debug(f"Requested to obtain variation by id {generation_id}: GET {url}") - try: - response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Get variation by ID: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while get variation by id: {str(error)}") - raise - - def create_dataset(self, name: str, description: str = None): - """ - This endpoint creates a new dataset. - - :param name: The name of the dataset. - :param description: A description for the dataset. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/datasets" - payload = {"name": name, "description": description} - if self.___logger: - self.___logger.debug(f"Requested to create dataset with payload {payload}: POST {url}") - try: - response = self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Dataset has been created: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while create dataset: {str(error)}") - raise - - def get_dataset_by_id(self, dataset_id: str): - """ - This endpoint gets the specific dataset. - - :param dataset_id: The ID of the dataset to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" - if self.___logger: - self.___logger.debug(f"Requested to obtain dataset dataset_id={dataset_id}: GET {url}") - try: - response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Dataset with dataset_id={dataset_id} provided: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while get dataset: {str(error)}") - raise - - def delete_dataset_by_id(self, dataset_id: str): - """ - This endpoint deletes the specific dataset. - - :param dataset_id: The ID of the dataset to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}" - if self.___logger: - self.___logger.debug(f"Requested to delete dataset dataset_id={dataset_id}: DELETE {url}") - try: - response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug(f"Dataset with dataset_id={dataset_id} has been deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while delete dataset: {str(error)}") - raise - - def upload_dataset_image(self, dataset_id: str, file_path: str): - """ - This endpoint returns presigned details to upload a dataset image to S3. - - :param dataset_id: The ID of the dataset to which the image will be uploaded. - :param file_path: The path to the image file. - """ - valid_extensions = ["png", "jpg", "jpeg", "webp"] - extension = os.path.splitext(file_path)[1].strip(".") - if extension not in valid_extensions: - raise ValueError(f"Invalid file extension. Must be one of {valid_extensions}") - - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload" - - payload = {"extension": extension} - if self.___logger: - self.___logger.debug(f"Requested to upload dataset_id={dataset_id} from {file_path}: POST {url}") - try: - response = self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - data = response.json() - if self.___logger: - self.___logger.debug( - f"Dataset with dataset_id={dataset_id} started to upload from {file_path}:" f" {response}" - ) - - upload_url = data["uploadDatasetImage"]["url"] - fields = json.loads(data["uploadDatasetImage"]["fields"]) - - with open(file_path, "rb") as f: - file_data = f.read() - - fields.update({"file": file_data}) - - if self.___logger: - self.___logger.debug(f"Uploading dataset_id={dataset_id} from {file_path}: POST {url}") - response = self.___session.post( - upload_url, data=fields, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - response = response.text() - if self.___logger: - self.___logger.debug(f"Dataset with dataset_id={dataset_id} uploaded using {file_path}:" f" {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred uploading dataset: {str(error)}") - raise - - def upload_generated_image_to_dataset(self, dataset_id: str, generated_image_id: str): - """ - This endpoint will upload a previously generated image to the dataset. - - :param dataset_id: The ID of the dataset to upload the image to. - :param generated_image_id: The ID of the image to upload to the dataset. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/datasets/{dataset_id}/upload/gen" - payload = {"generatedImageId": generated_image_id} - if self.___logger: - self.___logger.debug( - f"Requested to upload generated_image_id={generated_image_id} " - f"to dataset_id={dataset_id}: POST {url}" - ) - try: - response = self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - response = response.json() - if self.___logger: - self.___logger.debug( - f"Image with image_id={generated_image_id} has been uploaded to " - f"dataset_id={dataset_id}: {response}" - ) - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error occurred while upload generated image to dataset: {str(error)}") - raise - - def train_custom_model( - self, - name: str, - dataset_id: str, - instance_prompt: str, - description: str = None, - model_type: str = "GENERAL", - nsfw: bool = False, - resolution: int = 512, - sd_version: str = None, - strength: str = "MEDIUM", - ): - """ - This endpoint will train a new custom model. - - :param name: The name of the model. - :param description: The description of the model. - :param dataset_id: The ID of the dataset to train the model on. - :param instance_prompt: The instance prompt to use during training. - :param model_type: The category the most accurately reflects the model. - :param nsfw: whether or not the model is NSFW. - :param resolution: The resolution for training. Must be 512 or 768. - :param sd_version: The base version of stable diffusion to use if not using a custom model. - :param strength: When training using the PIXEL_ART model type, this influences the training strength. - """ - url = "https://cloud.leonardo.ai/api/rest/v1/models" - payload = { - "name": name, - "description": description, - "datasetId": dataset_id, - "instance_prompt": instance_prompt, - "modelType": model_type, - "nsfw": nsfw, - "resolution": resolution, - "sd_Version": sd_version, - "strength": strength, - } - if self.___logger: - self.___logger.debug(f"Requested to train custom model with payload {payload}: POST {url}") - try: - response = self.___session.post( - url, json=payload, headers=self.___session.headers.copy().update(self.___post_headers) - ) - response.raise_for_status() - response = response.text() - if self.___logger: - self.___logger.debug(f"Custom modal has been trained: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error training custom model: {str(error)}") - raise - - def get_custom_model_by_id(self, model_id: str): - """ - This endpoint gets the specific custom model. - - :param model_id: The ID of the custom model to return. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" - if self.___logger: - self.___logger.debug(f"Requested to obtain custom model by model_id={model_id}: GET {url}") - try: - response = self.___session.get(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.text() - if self.___logger: - self.___logger.debug(f"Custom modal has been trained: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error obtaining custom model: {str(error)}") - raise - - def delete_custom_model_by_id(self, model_id: str): - """ - This endpoint will delete a specific custom model. - - :param model_id: The ID of the model to delete. - """ - url = f"https://cloud.leonardo.ai/api/rest/v1/models/{model_id}" - if self.___logger: - self.___logger.debug(f"Requested to delete custom model by model_id={model_id}: GET {url}") - try: - response = self.___session.delete(url, headers=self.___session.headers.copy().update(self.___get_headers)) - response.raise_for_status() - response = response.text() - if self.___logger: - self.___logger.debug(f"Custom modal has been deleted: {response}") - return response - except Exception as error: - if self.___logger: - self.___logger.error(f"Error delete custom model: {str(error)}") - raise - - def wait_for_image_generation(self, generation_id, image_index=None, poll_interval=5, timeout=120): - """ - This method waits for the completion of image generation. - - :param generation_id: The ID of the generation to check. - :param image_index: (Optional) The index of the specific image to wait for. - If None, waits for all images to complete. - :param poll_interval: (Optional) The time interval in seconds between each check. Default is 5 seconds. - :param timeout: (Optional) Waiting timeout. Default is 120 seconds. - - :raises IndexError: If an invalid image_index is provided. - - :return: The completed image(s) once generation is complete. - """ - timeout_counter = 0 - while True: - response = self.get_single_generation(generation_id) - generation = response.get("generations_by_pk", {}) - status = generation.get("status") - - if status == "COMPLETE": - images = generation.get("generated_images", []) - if image_index is not None: - if image_index >= len(images): - raise IndexError("Incorrect image index") - return images[image_index] - else: - return images - - time.sleep(poll_interval) - - if timeout_counter >= timeout / poll_interval: - raise TimeoutError(f"Image has not been generated in {timeout} seconds") - else: - timeout_counter += 1 diff --git a/leonardo_engine/models.py b/leonardo_engine/models.py deleted file mode 100644 index d7a59eb..0000000 --- a/leonardo_engine/models.py +++ /dev/null @@ -1,3608 +0,0 @@ -nsfw_models = { - "data": { - "custom_models": [ - { - "id": "95bf5c4a-2f7e-4585-9783-b312fc90f5aa", - "name": "Eitan Klein ", - "description": "This model is inspired by the photographer and Israeli artist Eitan Klein, who is involved in capturing the male model and specializes in exposure ...", - "instancePrompt": "photography by Eitan Klein", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-01T03:56:30.167", - "sdVersion": "v2", - "type": "CHARACTERS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "a6ac57fc-1d99-4c25-bde7-71c2912fe2e9", "username": "tomtom10_1", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a man", - "generated_images": [ - { - "id": "c379f001-f371-426e-ad1c-494069b59d26", - "url": "https://cdn.leonardo.ai/users/a6ac57fc-1d99-4c25-bde7-71c2912fe2e9/generations/8b5a1203-ae62-42db-bfcc-2bb09b11e575/Eitan_Klein_a_man_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "0b24aca5-012a-4340-826a-1bc56f46f1e4", - "name": "Realistic Portraits - NSFW", - "description": "Portrait imagery", - "instancePrompt": "high-end portrait photography", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-02-10T02:43:10.395", - "sdVersion": "v2", - "type": "FASHION", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "26675562-66e5-4085-9f62-b80925f54ef9", "username": "Tango_one", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/26675562-66e5-4085-9f62-b80925f54ef9/generations/4e522de3-e6ff-4820-a00d-2f97008473e4/Realistic_Portraits_NSFW_photorealistic_body_photography_of_a_beautiful_girl_sitting_2.jpg", - "id": "01c0ad36-8730-4fbf-a76d-e15b82c093d5", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "RAW photo, 8k ultrasharp photoshoot, instagram, stunning college girl", - "generated_images": [ - { - "id": "c8a86e5c-3e7c-4abb-8240-ef3cbc6c7004", - "url": "https://cdn.leonardo.ai/users/26675562-66e5-4085-9f62-b80925f54ef9/generations/639a85ef-a8a6-4b04-900c-ef74295b1c22/Realistic_Portraits_NSFW_RAW_photo_8k_ultrasharp_photoshoot_instagram_stunning_colle_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "63a10c3a-37fd-48a8-88ec-ca680fe08626", - "name": "car beetle pickup truck ram", - "description": "", - "instancePrompt": "A car", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-02-09T20:54:17.222", - "sdVersion": "v1_5", - "type": "PRODUCT_DESIGN", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "a256dfb3-302d-45dd-810c-285af352258d", "username": "Ronalt", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "car", - "generated_images": [ - { - "id": "7dc269d8-d26a-4fc3-80f6-794f6551fdd8", - "url": "https://cdn.leonardo.ai/users/cfb583d7-2b0a-44df-980c-8b09a4582b0f/generations/c7d3f0a8-9561-42e4-881f-0fd9e43c5ab2/car_beetle_pickup_truck_ram_car_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "7a54c523-b876-4bc0-9071-1119cabba6ab", - "name": "Schiele", - "description": "Egon Schiele art", - "instancePrompt": "a young man in the style of Egon Schiele", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-02-05T18:27:06.242", - "sdVersion": "v1_5", - "type": "ILLUSTRATIONS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "afd16662-7b51-4ad2-a1f5-ba24a021dc54", "username": "Izzyjim", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/f7888a81-6853-4751-91e0-f50212549cc7/Schiele_portrait_of_a_mean_young_woman_in_the_style_of_Egon_S_1.jpg", - "id": "250644d2-05c2-445a-b847-355cded5aa7c", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a young man standing a corner of a cul de in the style of Egon Schiele", - "generated_images": [ - { - "id": "6d06a436-cb1d-4591-84d3-6e794011d801", - "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/5e09ba42-a674-43dc-bf25-a7a72c3d7649/Schiele_a_young_man_standing_a_corner_of_a_cul_de_in_the_style_of_Ego_1.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "6ac1218f-52d5-4a02-80ca-7599a4b2d895", - "name": "cenobites", - "description": "Clive Barker's Cenobites", - "instancePrompt": "a Cenobite", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-02-03T14:06:54.566", - "sdVersion": "v2", - "type": "CHARACTERS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "afd16662-7b51-4ad2-a1f5-ba24a021dc54", "username": "Izzyjim", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/33bd142b-d430-4bb9-894f-662c75ef5694/cenobites_A_naturalskinned_Cenobite_with_freckles_wearing_a_wh_1.jpg", - "id": "bb866f93-51c8-4360-858a-b292d509453a", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " woman fronting camera open white shirt, fire brustings from between her breasts, dramatic seance, back lighting , studio lighting ", - "generated_images": [ - { - "id": "d8983ee2-954b-4427-80fd-1d4c5264fbdf", - "url": "https://cdn.leonardo.ai/users/afd16662-7b51-4ad2-a1f5-ba24a021dc54/generations/1807743a-fa39-45fd-82d0-2679d4127894/cenobites_woman_fronting_camera_open_white_shirt_fire_brustings_from_b_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "72db3416-b7e0-46bf-83f0-ac2dccc44fd2", - "name": "Grimdark", - "description": "Grimdark, warhammer 40k, horus heresy", - "instancePrompt": "warhammer", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-02-02T19:19:24.818", - "sdVersion": "v2", - "type": "ILLUSTRATIONS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "a5bca136-5cb9-4cca-8fbf-bf13835a523a", "username": "visceral3d", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "spacemarine", - "generated_images": [ - { - "id": "ae61c2ba-8ce0-4c86-9b58-6811cab8a636", - "url": "https://cdn.leonardo.ai/users/1a892409-c957-427a-8473-1f461b43cf09/generations/3c9dd566-898a-446c-aef1-8fa467e4a784/Grimdark_spacemarine_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "3a43f9bd-bb36-48ab-af07-cbd9c985a55b", - "name": "Scarred and damaged/bandaged characters", - "description": "a design for damaged characters that are slightly or heavily bandaged", - "instancePrompt": "A Slightly Bandaged Scarred Character.", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-01-31T04:38:57.094", - "sdVersion": "v2", - "type": "CHARACTERS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "e407c8d7-45a3-451f-b8cf-f39d86df2fa0", - "username": "ShopKeeperPyro", - "__typename": "users", - }, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/e407c8d7-45a3-451f-b8cf-f39d86df2fa0/generations/02e14a68-dbe1-427f-bb14-d9f6d5fcfbab/Scarred_and_damagedbandaged_characters_A_Slightly_Bandaged_Scarred_CharacterFull_body_character_d_3.jpg", - "id": "887740ce-48bb-4807-98e2-e9dd9e69e1d4", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "steampunk full body portrait, male character design with a bandaged face", - "generated_images": [ - { - "id": "cc601bfe-8d23-446d-a6a7-93a756855c57", - "url": "https://cdn.leonardo.ai/users/e407c8d7-45a3-451f-b8cf-f39d86df2fa0/generations/72aa748f-2f68-475d-9edc-59ad0fc0594c/Scarred_and_damagedbandaged_characters_steampunk_full_body_portrait_male_character_design_with_a_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "6b2fe263-b081-4b13-8cc1-04cdd2e1179f", - "name": "Artificial Busts ", - "description": "This is finetune of SD2.1 for generating realistic busts. Tuning upon CC0 images from MET New York and few other free resources. ", - "instancePrompt": "A bust ", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-01-22T00:21:12.931", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "6b0af8c4-7cd0-493f-82d3-ce5508dcdea8", "username": "rhizomuser", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " A detailed bust of a mysterious figure, rendered in a surrealistic style.", - "generated_images": [ - { - "id": "c4f0d816-d5fa-4673-bf6c-e29531796832", - "url": "https://cdn.leonardo.ai/users/6b0af8c4-7cd0-493f-82d3-ce5508dcdea8/generations/518a8b0d-e465-402b-b5c5-93a40612a881/Artificial_Bustes_A_detailed_bust_of_a_mysterious_figure_rendered_in_a_surrea_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "4136b90e-e16e-40de-ae8e-40f7313e6b91", - "name": " neon night", - "description": "bluish neon colors", - "instancePrompt": "neon night", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-01-19T06:33:29.285", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "353533d4-c015-47d7-90be-199ddfddcd90", "username": "jaspior", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "neon night", - "generated_images": [ - { - "id": "477ebda0-fc68-4048-8c04-cf05ee133567", - "url": "https://cdn.leonardo.ai/users/353533d4-c015-47d7-90be-199ddfddcd90/generations/4773e379-75de-46ce-bf69-85e12fb8f583/neon_night_neon_night_1.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "9af4f4a0-5c89-4458-b0a9-14d36208e24e", - "name": "Airbrush Design", - "description": "80s and 90s Airbrush Desing Style", - "instancePrompt": "An Airbrush Painting", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-01-18T01:59:27.292", - "sdVersion": "v2", - "type": "ILLUSTRATIONS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "019f0238-4481-4bc8-9789-759e481d2e4e", "username": "aiaiaiaiai", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/019f0238-4481-4bc8-9789-759e481d2e4e/generations/a67f9499-bfb2-4ab4-a99b-6e076e591c05/80s_and_90s_Airbrush_Paintings_and_Ads_An_airbrush_painting_of_Sneakers_0.jpg", - "id": "e37e564a-822f-422b-aba2-60ef4be21647", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "An airbrush painting of Sneakers", - "generated_images": [ - { - "id": "e37e564a-822f-422b-aba2-60ef4be21647", - "url": "https://cdn.leonardo.ai/users/019f0238-4481-4bc8-9789-759e481d2e4e/generations/a67f9499-bfb2-4ab4-a99b-6e076e591c05/80s_and_90s_Airbrush_Paintings_and_Ads_An_airbrush_painting_of_Sneakers_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "be795181-644d-4010-abd7-ff45805931bd", - "name": "macbass", - "description": "Stylization based on a particluar french band...", - "instancePrompt": "black and white doodle", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-01-17T01:31:22.187", - "sdVersion": "v1_5", - "type": "ILLUSTRATIONS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "cbabf1d4-23c3-4bd8-a520-48c53cd0e626", - "username": "FoxBonesMulder", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Gorgeous tattooed woman riding a dolphin", - "generated_images": [ - { - "id": "da1dd922-69ef-4b7e-85ea-25a3605b45a7", - "url": "https://cdn.leonardo.ai/users/cbabf1d4-23c3-4bd8-a520-48c53cd0e626/generations/cbb17167-5308-45e3-ab45-65b46c333d57/MacBass_Gorgeous_tattooed_woman_riding_a_dolphin_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "db02a078-a251-4858-b5c5-d9530f6a03a0", - "name": "Pin-Up Ladies", - "description": "Generate Pin-Up Styled Women!", - "instancePrompt": "Pin-Up, Lingerie", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-01-15T01:53:37.047", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "02a3d88b-3380-43d3-b28c-1350876851b9", "username": "LouCipher", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Lace, Breasts, Blonde", - "generated_images": [ - { - "id": "e29f1359-d757-4630-8262-4710255ec044", - "url": "https://cdn.leonardo.ai/users/02a3d88b-3380-43d3-b28c-1350876851b9/generations/31c1cfeb-5c22-49e8-90cc-52bd8d56a2b8/PinUp_Ladies_Lace_Breasts_Blonde_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "45cbd18f-877a-4f63-8226-86ab45eabc11", - "name": "Kojima-Bot v2.1", - "description": "Generate Hideo Kojima using v2.1!", - "instancePrompt": "Hideo Kojima", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-01-14T22:10:14.504", - "sdVersion": "v2", - "type": "CHARACTERS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "02a3d88b-3380-43d3-b28c-1350876851b9", "username": "LouCipher", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "kojima as a purple stuffed worm in flap jaw space with a tuning fork doing a raw blink on hara-kiri rock I need scissors! 61!", - "generated_images": [ - { - "id": "98d28288-ae1f-4b48-ae8e-9690c9261ad9", - "url": "https://cdn.leonardo.ai/users/02a3d88b-3380-43d3-b28c-1350876851b9/generations/97b40102-1f0e-47a1-97ee-31773d05fdca/KojimaBot_v21_kojima_as_a_purple_stuffed_worm_in_flap_jaw_space_with_a_tun_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "b42069d2-4954-4ac9-a264-05177bf2484b", - "name": '"Metal Gear" Style Characters', - "description": "Generate characters inspired by the artwork of Yoji Shinkawa.", - "instancePrompt": "drawn by Yoji Shinkawa", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-01-14T18:46:36.17", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "02a3d88b-3380-43d3-b28c-1350876851b9", "username": "LouCipher", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Hideo Kojima", - "generated_images": [ - { - "id": "7abfecfe-99ee-412e-981c-1eed7f69607b", - "url": "https://cdn.leonardo.ai/users/02a3d88b-3380-43d3-b28c-1350876851b9/generations/c3d4e247-319a-4d0d-b15e-f38bf6ff0d7f/Metal_Gear_Style_Characters_Hideo_Kojima_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "30e15b51-dbf2-4c70-9281-ba4c659e795b", - "name": "Borderland Style", - "description": "Borderland Style Effect Cel Shader", - "instancePrompt": "borderland style", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-01-07T03:53:05.932", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": True, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "b3b26e38-d8a9-404d-b910-a555e48ec64a", "username": "gruB", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Borderland Style futuristic fortress shaped like a church, unreal engine, cinematic, 8k", - "generated_images": [ - { - "id": "9c59a9e2-a898-4a84-8027-f5df908b5ef3", - "url": "https://cdn.leonardo.ai/users/b3b26e38-d8a9-404d-b910-a555e48ec64a/generations/f234eecf-6ca8-4400-813e-c101d33998c5/Borderland_Style_Borderland_Style_futuristic_fortress_shaped_like_a_church_un_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - ] - } -} - -custom_models = { - "data": { - "custom_models": [ - { - "id": "444cd35a-55c7-4e49-83ad-8660d993edc3", - "name": "the Drya", - "description": "", - "instancePrompt": "the Drya", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T19:26:51.187", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "7ab2c0de-12ed-4e10-941d-220575b14375", "username": "zserv", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/7ab2c0de-12ed-4e10-941d-220575b14375/generations/33ca9e68-d19a-4d35-910b-8d6b3c52bb15/the_Drya_the_Drya_as_toon_female_character_perfect_single_face_an_2.jpg", - "id": "efe26642-7d71-43d4-915b-243e80432cf2", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "(( the Pauk ) as an Nefertity ) | Horror Theme Steampunk | (single head) | Frontal view | centered | perfect anatomy | key visual | intricate | highly detailed | breathtaking | precise lineart | vibrant | abstract | Lovecraftian Noir | cinematic | Carne Griffiths | Conrad Roset hyper-perfect in-depth details, mind-blowing natural vivid colors, ultra-sharp, upscaled 8x, fine lines, cinematic lights, ultra wide scene, high in-depth details, Hollywood quality, a masterpiece, ultra high detailed designs, Camera: Sony A1 200MP, Lens: 85mm f/1.4, Shutter speed: 1/12 ISO:40, 24K, RAW, Sharpness from 1 to 100 (0), Noise from 1 to 100 (0), Post-processing in Adobe Lightroom, photorealistic , ultra photoreal , ultra detailed, intricate details", - "generated_images": [ - { - "id": "516ac2b2-cdac-47cf-9cad-12504c7bd759", - "url": "https://cdn.leonardo.ai/users/f1410d36-7283-4575-8a2b-a4b8d806285c/generations/aa32b2a3-b278-4611-b175-28a69ee4a1ef/the_Drya_the_Pauk_as_an_Nefertity_Horror_Theme_Steampunk_single_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "f84690eb-cfe7-431c-afd4-ccc2d628bede", - "name": "T Borodinauskas", - "description": "", - "instancePrompt": "T Borodinauskas", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T19:21:09.279", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "7ab2c0de-12ed-4e10-941d-220575b14375", "username": "zserv", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/7ab2c0de-12ed-4e10-941d-220575b14375/generations/b87ae51b-ed39-45a5-8f87-0d80e0c56107/T_Borodinauskas_T_Borodinauskas_as_roaring_single_faced_single_perfect_wid_0.jpg", - "id": "6fed79b1-6a86-40c8-a360-4a22ba8ab580", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Full body of Colorful T Borodinauskas: black and red ink flow: 8k resolution photorealistic masterpiece: by Aaron Horkey and Jeremy Mann, detailed gorgeous face and eyes, fine detailed face, intricately detailed fluid gouache painting: by Jean Baptiste Mongue: calligraphy: acrylic: watercolor art, professional photography, natural lighting, volumetric lighting maximalist photoillustration: by marton bobzert: 8k resolution concept art intricately detailed, complex, elegant, expansive, fantastical", - "generated_images": [ - { - "id": "7d7a6879-9f6b-4e11-b0ca-99e4c43c39c5", - "url": "https://cdn.leonardo.ai/users/7ab2c0de-12ed-4e10-941d-220575b14375/generations/8f0f866d-9fa1-447a-812e-c56b782a670f/T_Borodinauskas_Full_body_of_Colorful_T_Borodinauskas_black_and_red_ink_flow_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "26aab27c-fd9d-4238-b6d4-dd193f3edee2", - "name": "D O O M S A Y E R", - "description": "Generates occult priest, dark druids, and worshippers of doom (with a post-apocalyptic spin).", - "instancePrompt": "A doomsayer", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T17:53:33.228", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "4504b091-46ed-4471-ad0d-5a650533c743", - "username": "garbageguy420", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A doomsayer", - "generated_images": [ - { - "id": "21e7f193-6c00-46e2-bf55-d796ddc34f28", - "url": "https://cdn.leonardo.ai/users/4504b091-46ed-4471-ad0d-5a650533c743/generations/54606343-89f9-4183-be9c-3eb48d059362/D_O_O_M_S_A_Y_E_R_A_doomsayer_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "611c7c61-bacf-4909-8de8-e2c5cde907a7", - "name": "Anime Girls 1.0", - "description": "Anime girls", - "instancePrompt": "anime style", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T17:01:22.834", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "ce602c4c-6467-4373-b6c8-7516eb20fb87", "username": "Kryt", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "beautiful lady, freckles, dark makeup, hyperdetailed photography, soft light, head and shoulders portrait, cover", - "generated_images": [ - { - "id": "554594af-3394-495e-a936-43b61340b124", - "url": "https://cdn.leonardo.ai/users/ce602c4c-6467-4373-b6c8-7516eb20fb87/generations/d3199936-b13e-4a96-8137-fa8c8215dab2/Anime_Style_10_beautiful_lady_freckles_dark_makeup_hyperdetailed_photograp_1.jpg", - "likeCount": 12, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "12e494cf-1ed0-4b08-b2a1-c7b5b6221c0f", - "name": "Razumov Portrait Style", - "description": "Portraits in the style of K. razumov", - "instancePrompt": "A portrait of", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T16:53:29.778", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "daf80667-2bb3-4975-83c0-5115d5c0b85e", "username": "RickH1950", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/daf80667-2bb3-4975-83c0-5115d5c0b85e/generations/5c40f7af-179e-4534-96b5-92b53d5500ae/Razumov_Portrait_Style_a_photorealistic_portrait_of_stunningly_0.jpg", - "id": "03bf6882-abef-4ea2-9996-444bf26e8e67", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a beautiful girl, freckles, dark makeup, flat color, wide angle, clean detailed faces, intricate clothing, analogous colors, glowing shadows, beautiful gradient, depth of field, clean image, high quality, high detail, high definition, soft light", - "generated_images": [ - { - "id": "a2b7cf74-64b2-486b-b2f2-98b0c6a3401f", - "url": "https://cdn.leonardo.ai/users/5155974e-5b3a-4872-b613-3e3ea57b055b/generations/c27b559b-8059-43d9-9ed1-6f0d99dd5110/Razumov_Portrait_Style_a_beautiful_girl_freckles_dark_makeup_flat_color_wide_angl_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "d96b9c5d-89db-48b9-bbb7-2f3b5c74dbdc", - "name": "Summer frozen sweets", - "description": "water color on white background cartoon type images of frozen sweets", - "instancePrompt": "an ice cream cone", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T16:28:59.16", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "a4735e05-b1fb-4af6-aa04-af9d40c7d438", "username": "boop749", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "watercolor clipart for sticker summer ice cream cone", - "generated_images": [ - { - "id": "b9eda412-8b6c-4d41-8eb8-7bdfc0d8cca8", - "url": "https://cdn.leonardo.ai/users/a4735e05-b1fb-4af6-aa04-af9d40c7d438/generations/909f0b73-5a49-4726-88c7-0f1ba0034d07/Summer_frozen_sweets_watercolor_clipart_for_sticker_summer_ice_cream_cone_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "4ed57cd2-9e06-496d-abbe-b7e01f9a09e2", - "name": "Crypto Dreams", - "description": "Crypto NFT art based on BAYC", - "instancePrompt": "An NFT character", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T15:42:05.752", - "sdVersion": "v2", - "type": "ILLUSTRATIONS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "7bb48ccd-354a-4f0b-8aff-a8eebe8ee8d8", "username": "ElChuba", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/7bb48ccd-354a-4f0b-8aff-a8eebe8ee8d8/generations/77cb48c0-2e8b-4004-9bb1-54a249f0ee6f/Crypto_Dreams_an_zebra_as_an_nft_character_1.jpg", - "id": "80e0a3c5-5af0-4b42-8c10-7a1c0a5dd952", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "bayc, a bored ape by All Seing Seneca", - "generated_images": [ - { - "id": "817d9122-6f14-41b1-8156-2fbf23cd99ed", - "url": "https://cdn.leonardo.ai/users/7bb48ccd-354a-4f0b-8aff-a8eebe8ee8d8/generations/2d7e4972-8033-4e87-8651-abf658215ae5/Crypto_Dreams_bayc_a_bored_ape_by_All_Seing_Seneca_1.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "f70cb462-f48e-4c48-bf15-948a9718938a", - "name": "e-commerce landing page", - "description": "", - "instancePrompt": "landing page electronics ", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T14:19:23.18", - "sdVersion": "v2", - "type": "UI_ELEMENTS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "2e4d5a04-f8cd-41a1-b082-072d56b1fbeb", "username": "Viniciusm", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "landing page,", - "generated_images": [ - { - "id": "054d4120-a1fe-4432-8cb5-15b81a073057", - "url": "https://cdn.leonardo.ai/users/2e4d5a04-f8cd-41a1-b082-072d56b1fbeb/generations/d8bf9617-14db-4849-8852-2f5e1d46d822/landing_landing_page_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "b4fa336a-8b38-4a7e-a709-7670a89f1bd0", - "name": "flag", - "description": "flag", - "instancePrompt": "flag", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T13:52:27.78", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "cc3f8064-77f7-4809-8be4-feba69b5b80b", - "username": "SlimeHammer", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "flag of frog", - "generated_images": [ - { - "id": "c5d5100f-c264-4c3b-bdd2-92148dd16b19", - "url": "https://cdn.leonardo.ai/users/cc3f8064-77f7-4809-8be4-feba69b5b80b/generations/101643e0-9eb0-454e-871c-8498874acac0/flag_flag_of_frog_1.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "65ddee39-562f-4005-8376-015a618f8f70", - "name": "liliana15", - "description": "", - "instancePrompt": "a portrait of liliana15", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T13:36:34.166", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "4b788d63-9078-4dc8-9da4-6dcae596f0f3", - "username": "awesomeuser", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a portrait of liliana15, levitation, hovering, full body, 1 character, 1 body, centered character", - "generated_images": [ - { - "id": "af063ec8-ecf9-4d87-85d8-d10792a6b6ad", - "url": "https://cdn.leonardo.ai/users/4b788d63-9078-4dc8-9da4-6dcae596f0f3/generations/9f73bf2e-af36-4c1c-9fb5-6d1fb2336ddb/liliana15_a_portrait_of_liliana15_levitation_hovering_full_body_1_chara_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "37001f04-f71f-44b3-b40d-cb0ecef690c1", - "name": "Mamulena", - "description": "Beautiful woman with brown eyes", - "instancePrompt": "beautiful woman with brown eyes, beautiful woma", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T11:26:53.606", - "sdVersion": "v1_5", - "type": "PHOTOGRAPHY", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "ec4c8a0a-bae6-4f33-b62c-ec6fa756fc47", - "username": "Securbond_Phone", - "__typename": "users", - }, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/ec4c8a0a-bae6-4f33-b62c-ec6fa756fc47/generations/deaae567-e251-4e45-b809-777cd87e9dd3/Mamulena_crown_of_roses_on_the_head_earrings_necklaces_35mm_284185531_0.jpg", - "id": "5d7019bd-ca1e-43ee-b663-947c899f7ce1", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Gorgeous brunette, beautiful woman, wavy hair falls on her shoulders, playful look, dressed in an intricately designed light detailed black dress, extremely attractive, flirtatious and playful, neon glow, cloudy fantasy, excellent proportions of the female body, futuristic landscape, bright view, beautiful clearly defined armpits, fantasy adventure theme, extremely attractive, perfect slender female anatomy, film photography, analog photography, film grain, extreme detail, 4k, ultra HD, anna dittmann, hyperrealism, trending on artstation, polished, beautiful, shining, synesthesia, bright, photorealistic, backlight, hair light, 8k resolution, unreal engine 5", - "generated_images": [ - { - "id": "406272d8-9ebc-4d85-8c1b-258ab9987516", - "url": "https://cdn.leonardo.ai/users/720bd193-a7ea-4181-add2-68cb32b5ee6d/generations/59fc26aa-727f-4af8-81db-290b09d48a3a/Mamulena_Gorgeous_brunette_beautiful_woman_wavy_hair_falls_on_her_shou_1.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "dc8b1dc6-30a0-418f-b5db-a8deadc9d5e5", - "name": "Character portrait", - "description": "Good for creating fantasy portrait of human face. Best to use at 640x832. Keep promp weight at 7-9. Remember to add (a portrait of ) at the beginning of your prompt for best results.", - "instancePrompt": "Face", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T10:48:18.382", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "c5e92e90-48cb-4fa1-ae9f-f2b0530e6adf", "username": "Valsiren", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/c5e92e90-48cb-4fa1-ae9f-f2b0530e6adf/generations/82d01f47-41f2-495d-81e0-08bf2c7396f9/Random_face_34_shot_full_body_image_of_Insanely_detailed_photograph_of_A_0.jpg", - "id": "f189138f-a971-4887-919e-1caf30cab7ea", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "3/4 shot full body image of Insanely detailed photograph of An elaborate beautiful temptress glossy lipstick intricate glistening skin face long hair hyperdetailed painting, emerging out of waterfall, Tom Bagshaw Dan Witz CGSociety ZBrush Anna dittmann Central fantasy art 4K", - "generated_images": [ - { - "id": "f189138f-a971-4887-919e-1caf30cab7ea", - "url": "https://cdn.leonardo.ai/users/c5e92e90-48cb-4fa1-ae9f-f2b0530e6adf/generations/82d01f47-41f2-495d-81e0-08bf2c7396f9/Random_face_34_shot_full_body_image_of_Insanely_detailed_photograph_of_A_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "f1830509-5b3e-48d5-a57d-923815ca7a0c", - "name": "Little Heroes", - "description": "Make a Little Heroes", - "instancePrompt": "Little Wonder Women", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T07:46:12.362", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "463c9248-5fbd-4be6-91c5-a5e6e4ef9240", "username": "Remix", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/463c9248-5fbd-4be6-91c5-a5e6e4ef9240/generations/3a71a947-3a2c-4410-9075-12f0fdad630c/Little_Heroes_Little_Superman_white_costume_3d_full_body_highly_detailed_v_2.jpg", - "id": "aa1a7e33-629a-48ea-9310-c9ba6921c586", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Little Wonder Women", - "generated_images": [ - { - "id": "5b2cf668-1b5f-4e4a-ae1a-14dc4dd43166", - "url": "https://cdn.leonardo.ai/users/463c9248-5fbd-4be6-91c5-a5e6e4ef9240/generations/e02a7c58-b7e4-42db-8422-0dcdd28a3de1/Little_Heroes_Little_Wonder_Women_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "b260b08d-9db0-442c-b1d3-2604ccd330a7", - "name": "Cookie Run-v1", - "description": "", - "instancePrompt": "cookie run", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T06:25:36.973", - "sdVersion": "v1_5", - "type": "ILLUSTRATIONS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "2f0e87f7-44f6-4081-baeb-c7ea3023703a", "username": "Andrek", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/c0403e67-5be9-488c-b681-a75a83ffb5a5/Cookie_Runv1_A_cookie_run_character_who_doesnt_exist_in_the_game_yet_but_0.jpg", - "id": "739b695d-61fe-4a37-b11e-e0973d534cd9", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " A whimsical cookie run character with a mischievous smile, wearing a colorful striped shirt and a baker's hat.", - "generated_images": [ - { - "id": "49993490-2edc-4942-8175-2b990915c5c0", - "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/bde10e1f-a6fc-4cd2-aed8-f8424bb4545d/Cookie_Runv1_A_whimsical_cookie_run_character_with_a_mischievous_smile_w_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "5d77afd8-8f15-48fe-bb8e-30d62f9ff8fa", - "name": "Coldplay", - "description": "Model for IA to recognize the Coldplay Band Members", - "instancePrompt": "Chris Martin from Coldplay", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T05:37:37.855", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "419d1a7b-9f0a-45c3-a7f1-617a535a9c96", - "username": "imanolSante18", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Chris Martin from Coldplay, marvel comics, avengers, iron man suit, full body render, rembrandt, cgsociety, artstation trending, highly detailed, ultra realistic, concept art, intricate details, eerie, highly detailed, photorealistic, octane render, 8 k, unreal engine.", - "generated_images": [ - { - "id": "8842a983-fc27-473e-a443-2bf4e3caf166", - "url": "https://cdn.leonardo.ai/users/419d1a7b-9f0a-45c3-a7f1-617a535a9c96/generations/eca231af-25ee-4300-b740-9de1fa9c0aff/Coldplay_Chris_Martin_from_Coldplay_marvel_comics_avengers_iron_man_su_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "3474e4c5-11cc-419a-be7b-72654fd2ac4e", - "name": "Typography 1", - "description": "Creates images with words in solid style.", - "instancePrompt": "word", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T05:34:13.553", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "ca09e017-65ce-4c95-a06e-2390232398bb", "username": "Archie007", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Logo design, travel, Hotels", - "generated_images": [ - { - "id": "ae759317-eaee-474c-9a9c-4f4db29ffa72", - "url": "https://cdn.leonardo.ai/users/dfedd259-c3d7-46f9-a36d-fa2a33bf114f/generations/7d2c1903-3ddf-4b74-98e2-823f46f2c6fa/Typography_1_Logo_design_travel_Hotels_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "f232eee3-5be9-467b-a0c8-6c65311040c7", - "name": "Black and White Portraits", - "description": "Black and White Portraits", - "instancePrompt": "Portrait", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T05:12:29.909", - "sdVersion": "v2", - "type": "PHOTOGRAPHY", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "ee3d46dc-fcd3-4cb4-a7ca-e3d94847f3e5", - "username": "ProfTeddington", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Ginger man with short beard and glasses, portrait", - "generated_images": [ - { - "id": "edc5ffc1-22e4-45d7-9da9-2c8f595d34e1", - "url": "https://cdn.leonardo.ai/users/ee3d46dc-fcd3-4cb4-a7ca-e3d94847f3e5/generations/514ea837-573c-49b5-a4ec-ff72c633dd86/Black_and_White_Portraits_Ginger_man_with_short_beard_and_glasses_portrait_3.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "ad6f6660-693b-4d29-8d55-07b85d52ad55", - "name": "LitKillah", - "description": "LitKillah", - "instancePrompt": "LitKillah", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T04:30:52.253", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "ade3a833-b65f-4187-ad6e-f28e02666e7e", "username": "NowDesign", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "LitKillah wearing sunglasses, fullbody shot, unreal engine, 3d rendered, extremely detailed, volumetric lighting, 8k, high resolution, polished", - "generated_images": [ - { - "id": "92248a6a-02ee-463f-beb9-31d9de90d70b", - "url": "https://cdn.leonardo.ai/users/ade3a833-b65f-4187-ad6e-f28e02666e7e/generations/969b5d99-4278-4312-9538-605a0fd751ee/LitKillah_LitKillah_wearing_sunglasses_fullbody_shot_unreal_engine_3d_r_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "34cf15f4-5bcd-4563-a9ce-feecccabf5c9", - "name": "Tiktoker Generator", - "description": "Generate a new tiktoker!", - "instancePrompt": "A tiktoker", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T02:10:27.299", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", - "username": "myawsomename", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A tiktoker of a abstract beauty, Lizard Man, centered, looking at the camera, approaching perfection, dynamic, moonlight, highly detailed, watercolor painting, artstation, concept art, smooth, sharp focus, illustration", - "generated_images": [ - { - "id": "c2a255cc-077a-4485-8eed-d3edcb796ebf", - "url": "https://cdn.leonardo.ai/users/95664c54-3731-4512-8c68-c449364749d3/generations/f7d7547d-1f18-4138-93d5-a7d7a9f28e10/Tiktoker_Generator_A_tiktoker_of_a_abstract_beauty_Lizard_Man_centered_looking_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "8798d8ba-56fa-418d-9d61-9f83d0783087", - "name": " Custom Mobile Game Items", - "description": "model to generate images to generate images of magnets in isometric", - "instancePrompt": "a game item ", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-04T01:24:35.119", - "sdVersion": "v1_5", - "type": "GAME_ITEMS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a game item 🏘️", - "generated_images": [ - { - "id": "c97a21c0-931c-42a5-b5b2-b85dc3484f33", - "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/6d6f5b8b-f271-421f-8367-32543a09b5b4/Custom_Mobile_Game_Items_a_game_item_4.jpg", - "likeCount": 1, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "6cb74458-733b-4ef9-96e8-905f9f4d42ba", - "name": "Pencilistic", - "description": "A model containing a dataset of pencil drawings on paper, This isn’t near as perfect like traditional artists like how ai isn’t perfect, so expect ...", - "instancePrompt": "a drawing", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-04T00:58:23.469", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "2f0e87f7-44f6-4081-baeb-c7ea3023703a", "username": "Andrek", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/29deeeea-1a52-45e4-a847-7eb2500cf65f/Pencilistic_A_drawing_of_a_mug_filled_with_coffee_with_steam_coming_out_G_3.jpg", - "id": "ffe8ece4-c02a-449e-b99b-6b937071d832", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A drawing of a mug filled with coffee, with steam coming out, Graphite, Pencil, Pointillism, Trending on ArtStation", - "generated_images": [ - { - "id": "ffe8ece4-c02a-449e-b99b-6b937071d832", - "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/29deeeea-1a52-45e4-a847-7eb2500cf65f/Pencilistic_A_drawing_of_a_mug_filled_with_coffee_with_steam_coming_out_G_3.jpg", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "3cbb2148-1083-4b7c-a879-46c083a09154", - "name": "banheiro com banheira", - "description": "banheiras de hidromassagem", - "instancePrompt": "hidro1", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T23:59:51.704", - "sdVersion": "v1_5", - "type": "BUILDINGS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "2c34b750-fbb0-4535-ac81-e7889c5a1c35", "username": "Carolsouza", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a room, small bathroom interior design ,3D scene, black marble countertop, white metro tiles, wooden slatted wall, round mirror, black mable shower stall, lighting design, corona render, unreal, interior design magazine photograth", - "generated_images": [ - { - "id": "67773ce4-a8c5-463e-a6b8-373dc60db6ba", - "url": "https://cdn.leonardo.ai/users/2c34b750-fbb0-4535-ac81-e7889c5a1c35/generations/7e3ec43d-0f28-424a-bed1-1e8b1435e5cf/banheiro_com_banheira_a_room_small_bathroom_interior_design_3D_scene_black_marble_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "5ca77967-9ef1-4709-b18e-b5d4c0449075", - "name": "Cute Anime Girls 1.0", - "description": "Create anime-style girls in a smooth, flowing stroke", - "instancePrompt": "smooth anime style", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T23:48:09.972", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "ab0732f8-4ae5-4bd7-8822-e4f24758e7fb", - "username": "genesisivor", - "__typename": "users", - }, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/ab0732f8-4ae5-4bd7-8822-e4f24758e7fb/generations/b340f423-f0fd-4733-9967-0fbe714c26d9/Cute_Anime_Girls_10_smooth_anime_style_white_castle_flying_over_the_clouds_in_5.jpg", - "id": "6ecef57b-e216-4595-afbc-4df61a428f6b", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "dog girl, blue hair, nice pose, {{depth of field}}, extremely detailed girl, illustration, solo, sharp focus, light smile, closed mouth, beautiful detailed eyes, yellow eyes, {{{{{looking at viewer}}}}},{{{{{sharp focus}}}}}, {loli}, {{{{{masterpiece illustration}}}}},full body, normal angle,, long hair,", - "generated_images": [ - { - "id": "1ec2676e-60a5-42e3-b465-3c1eb08f0930", - "url": "https://cdn.leonardo.ai/users/ab0732f8-4ae5-4bd7-8822-e4f24758e7fb/generations/15e748c9-3556-4342-aae8-4b171b41ab15/Cute_Anime_Girls_Gen_10_dog_girl_blue_hair_nice_pose_depth_of_field_extremely_det_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "f19e584a-b678-4015-89d5-833fff66a6e2", - "name": "Aesthetic friend group pictures", - "description": "generate some friend goals pictures!", - "instancePrompt": "Aesthetic friend group", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T23:45:44.239", - "sdVersion": "v2", - "type": "PHOTOGRAPHY", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", - "username": "myawsomename", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " framework ", - "generated_images": [ - { - "id": "35119233-0f41-49d1-8c75-6280534913e8", - "url": "https://cdn.leonardo.ai/users/7b925e04-af28-4c7e-aad4-1c66cc671ff9/generations/9b9865f9-e8c7-48d6-b2b3-97a208a1d4ac/Aesthetic_friend_group_pictures_framework_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "c52bcbca-30c3-417f-a660-151c34600173", - "name": "vsco/preppy girls", - "description": "", - "instancePrompt": "vsco/preppy girl aesthetic", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T23:38:14.908", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", - "username": "myawsomename", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Create an Afrofuturist, surrealist, cyberpunk Igbo Mbari mask with intricate details, featuring colorful ceramic textures depicting asymmetrical faces with exaggerated eyes and whimsical features. The mask should challenge traditional European conceptions of beauty, with a focus on vibrant colors and playful shapes. To achieve the desired ceramic texture, use simple geometric shapes and patterns to create a tiled effect. Avoid using complex gradients or detailed shading, instead relying on bold, contrasting colors to create depth and texture. To further convey the surreal and cyberpunk themes of the mask, incorporate elements of futuristic technology and machinery into the design. These can be represented through simple shapes like gears, wires, or circuit boards, and should be used sparingly to avoid overwhelming the design. Finally, to tie the design back to its cultural roots, incorporate elements of the Igbo Mbari tradition into the mask, such as geometric patterns, stylized ani", - "generated_images": [ - { - "id": "f3b302b1-9711-4f39-b178-abc07635f23c", - "url": "https://cdn.leonardo.ai/users/6c582e58-fa16-4671-b148-3aab75e95266/generations/6067c115-9be5-46aa-be03-e1c61c2184b1/vscopreppy_girls_Create_an_Afrofuturist_surrealist_cyberpunk_Igbo_Mbari_mask_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "72c19c5d-06fd-42b5-9d01-1a9c80a0cc22", - "name": "Blythe Fantasy", - "description": "beautifull Doll", - "instancePrompt": "DOLL", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T23:05:44.022", - "sdVersion": "v1_5", - "type": "ILLUSTRATIONS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "d068039c-99eb-4776-9309-4de03c12dea1", - "username": "UpsetKitty_", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "lemon man relaxing on chair on beach drinking cocktail with boombox and seagulls and palmtree, photorealistic, hyperdetailed, golden ratio, cartoon, pixar, hdr, octane render, trending on artstation, cinematography lighting, ", - "generated_images": [ - { - "id": "66bbb033-3543-4c02-9b0c-d9efcd273f9f", - "url": "https://cdn.leonardo.ai/users/5b8c64d4-6a65-4864-b3dd-4668d4a378e2/generations/d3d4b5a3-db6f-4e63-ae76-93677fdbff49/Blythe_Fantasy_lemon_man_relaxing_on_chair_on_beach_drinking_cocktail_with_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "9e6e1a43-86b5-46b9-828a-ec1cef8f616e", - "name": "Ankhegs", - "description": "Ankhegs are huge insectoid monster with many slender limbs and large antennae", - "instancePrompt": "Ankheg", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T22:39:49.204", - "sdVersion": "v1_5", - "type": "PHOTOGRAPHY", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "d9739cd5-d837-40b9-9f7c-52701bcf837a", - "username": "BenTheArtMan", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A Ankheg, its mandibles dripping with venom, perched atop a pristine white snowdrift.", - "generated_images": [ - { - "id": "ed89a763-af79-419f-bd84-37be0c9e4318", - "url": "https://cdn.leonardo.ai/users/d9739cd5-d837-40b9-9f7c-52701bcf837a/generations/ab3fae85-a858-4ae0-a75c-6b39380beaa7/Ankhegs_A_Ankheg_its_mandibles_dripping_with_venom_perched_atop_a_pri_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "d984c24a-aa4c-4823-9ef3-b0dde9d21d86", - "name": "2ds", - "description": "2D Surreal Fantasy Yoshitaka Amano and aAyumi Kasai style", - "instancePrompt": "A human being", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T22:32:56.93", - "sdVersion": "v2", - "type": "ILLUSTRATIONS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "2f511681-fe38-4be5-8e24-1d820c3103f4", - "username": "paulangelogc", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A 2d surreal illustration of a RPG Character in the path of self love and autorecognition, high quality, hand made, and yoshitaka amano style", - "generated_images": [ - { - "id": "b9c7fe7b-2794-40f2-a1ee-2ab6fb93927a", - "url": "https://cdn.leonardo.ai/users/2f511681-fe38-4be5-8e24-1d820c3103f4/generations/2df20f6a-0143-4fed-9e4d-e2cb166e8925/2ds_A_2d_surreal_illustration_of_a_RPG_Character_in_the_path_of_2.jpg", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "e86fa62a-7b1e-472b-9416-12be31a8bc8d", - "name": "Supergirls", - "description": "compilation of comic style art of female characters only", - "instancePrompt": "Sgirls", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T22:14:45.492", - "sdVersion": "v1_5", - "type": "ILLUSTRATIONS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "cd5af38f-3279-4af2-bc9d-a2b267fee0f7", - "username": "Soyisraruiz", - "__typename": "users", - }, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/cd5af38f-3279-4af2-bc9d-a2b267fee0f7/generations/575c57e9-2007-46ab-a1f4-92df2120d676/Supergirls_girl_dressed_in_leather_jacket_with_the_zipper_of_the_jacket_0.jpg", - "id": "455d4e01-3567-4562-a5c7-4e9606780681", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "girl dressed in leather jacket, with the zipper of the jacket open without clothes inside, pink jean pants, dark glasses, hiding both hands behind her back, girl with straight hair, face with Asian features.", - "generated_images": [ - { - "id": "c4893da8-633b-49b0-8df5-356b5f9dcf1f", - "url": "https://cdn.leonardo.ai/users/cd5af38f-3279-4af2-bc9d-a2b267fee0f7/generations/dfe5513b-1d7c-4ba3-bdb4-0abd6fa0d35b/Supergirls_girl_dressed_in_leather_jacket_with_the_zipper_of_the_jacket_0.jpg", - "likeCount": 4, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "db524da0-4a30-4516-8f6b-b15e58ebd1ab", - "name": "The Prince's Lost Toys", - "description": " colorful light, intricate, highly detailed, my rendition, artstation, illustration, art by alphonse mucha and uang guangjian and gil elvgren and s...", - "instancePrompt": "anime prince", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T22:11:24.804", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "08fa806b-b4a5-424a-9efc-efd5978e1e61", "username": "DAigotenno", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "(portrait of disney anime 6 year old prince)surrounded by gold and silver magical toys, colorful light, intricate, elegant, highly detailed, my rendition, artstation, illustration, art by alphonse mucha and uang guangjian and gil elvgren and sachin teng, (symmetry) , octane render, ultra realistic, intricate, digital art, ambient lighting, highly detailed, unreal engine, digital painting, concept art, sharp focus ", - "generated_images": [ - { - "id": "9385738b-9596-4ab8-ae3b-e7262514d94c", - "url": "https://cdn.leonardo.ai/users/08fa806b-b4a5-424a-9efc-efd5978e1e61/generations/cd674fb9-9f2c-457c-872e-10e9a41b896a/The_Princes_Lost_Toys_portrait_of_disney_anime_6_year_old_princesurrounded_by_go_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "e9734aa0-00f5-4488-b76a-31cc60195023", - "name": "kpop idol generator", - "description": "", - "instancePrompt": "A kpop idol", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T22:04:21.041", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "9afdb51a-2f05-4911-a4e5-36bf15ac4edf", - "username": "myawsomename", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A kpop idol", - "generated_images": [ - { - "id": "4128f051-b707-4833-a871-1bb5da5c8b50", - "url": "https://cdn.leonardo.ai/users/9afdb51a-2f05-4911-a4e5-36bf15ac4edf/generations/14514b9f-4475-4e4b-8c59-54ec46a7f077/kpop_idol_generator_A_kpop_idol_3.jpg", - "likeCount": 1, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "2f6ed8eb-8c40-4bb2-97bc-fbc1aab88d95", - "name": "Pixar/Disney", - "description": "Pixar/Disney", - "instancePrompt": "ObiPD", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T21:51:30.584", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "0c3ebcdc-889c-4f2a-980e-68ba77c7b843", "username": "Obiconcept", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/0c3ebcdc-889c-4f2a-980e-68ba77c7b843/generations/a83cd3d0-3063-445f-96f4-cd321c35ebb0/PixarDisney_cute_character_ultra_detailed_perfect_scenery_mysterious_prod_1.jpg", - "id": "368bd51d-250e-450e-8d2c-064a38d8d0c0", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "cute character, ultra detailed, perfect scenery, mysterious, glow, product photography, octane render, 8K ultra detailed, perfect scenery, mysterious, glowing green, product photography, octane render, 8K", - "generated_images": [ - { - "id": "c36a5865-16c2-46e1-af58-59a953ff753d", - "url": "https://cdn.leonardo.ai/users/0c3ebcdc-889c-4f2a-980e-68ba77c7b843/generations/31426272-22ac-4ec9-bdf7-2602e2c9dfda/PixarDisney_cute_character_ultra_detailed_perfect_scenery_mysterious_glow_0.jpg", - "likeCount": 6, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "9b52bcfb-de0f-40ab-b8fc-c39d8229fb69", - "name": "Ice Sculptures", - "description": "model to generate images of ice sculptures", - "instancePrompt": "a ice sculpture ", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T21:21:13.52", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/4fa07504-8215-4985-be5d-dd6d1eb5bc5a/Ice_Sculptures_a_ice_sculpture_tree_4.jpg", - "id": "2f5da5b2-0278-406e-bee4-5b4d638e01d8", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a ice sculpture 🤖", - "generated_images": [ - { - "id": "7d12f018-f8e3-4bd4-94a0-f646bf8bb647", - "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/382a80ea-e8b7-4ec0-9b16-183078256baf/Ice_Sculptures_a_ice_sculpture_4.jpg", - "likeCount": 5, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "dac0968c-65b5-4d4c-8d9a-83084fa3762b", - "name": "Game maps v1.8", - "description": "model to generate images of game maps", - "instancePrompt": "a game map", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T21:15:41.96", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a game map", - "generated_images": [ - { - "id": "315b6fa2-9031-40eb-949f-6f13afd8d7b9", - "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/abc1226a-4801-4a1c-afc9-93fba686c33b/Game_maps_v18_a_game_map_0.jpg", - "likeCount": 4, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "577cc4ff-72ba-44e4-baf3-88bb7031d780", - "name": "Movie/Tv show Poster generator", - "description": "", - "instancePrompt": "Movie poster", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T20:45:56.585", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "a55cb11c-af6b-40fa-8ef9-0910c3c139f2", - "username": "toriropedhis", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " The Twilight Zone, 8k resolution concept art intricately detailed, complex, elegant, expansive, fantastical, Masterpiece, best quality, chiaroscuro lighting, sfumato texture-collodion process fractal recursion chaos, plasma fireball of verdigris in the distance, octane render, chromatic density, chromatography ,background=fractal recursion chaos", - "generated_images": [ - { - "id": "8c77d196-2e2b-4deb-acbc-9fc2652fc98f", - "url": "https://cdn.leonardo.ai/users/ca86fddb-0c8a-45c8-9adc-51e25b5aef3a/generations/f5ca5b37-e313-4a8c-9f98-3ba3e3ae37ac/MovieTv_show_Poster_generator_The_Twilight_Zone_8k_resolution_concept_art_intricately_d_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "0dec12b4-0719-46ca-a15a-3faeb975d4bf", - "name": "Hokuto no ken character generator", - "description": "Create a character with Kenshiro School Hokuto", - "instancePrompt": "warrior, martial fighter", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T20:34:10.941", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "81cdba52-88eb-4760-a5ae-879af1da7808", "username": "huangugu", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/81cdba52-88eb-4760-a5ae-879af1da7808/generations/68f6945e-826d-43bf-af8f-b4c0a9d0c7b8/Hokuto_no_ken_character_generator_a_complete_image_of_a_ninja_in_the_style_Nanto_Seiken_bla_0.jpg", - "id": "0605f023-cca9-451d-8795-51c7c05f5100", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a complete image of a fighter in the style hokuto no ken, eyes with details, nose with details, mouth with details, kenshiro, manga style, 8k, octane render, global illumination, very detailed image very well colored, background in white", - "generated_images": [ - { - "id": "c97bad23-d174-435d-b6fd-c56ed0fbf598", - "url": "https://cdn.leonardo.ai/users/81cdba52-88eb-4760-a5ae-879af1da7808/generations/ed04276f-e481-4226-a4be-5004c5f36c8e/Hokuto_no_ken_character_generator_a_complete_image_of_a_fighter_in_the_style_hokuto_no_ken_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "790f5edb-1043-43d5-9dd5-056cc0c65113", - "name": "Ink / Line Art", - "description": "BW Ink and Line Art", - "instancePrompt": "Ink / Line Art", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T18:29:15.907", - "sdVersion": "v2", - "type": "ILLUSTRATIONS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "0eea719d-525b-40e3-b338-989c7bb0ee2c", "username": "LUKALAB", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/0eea719d-525b-40e3-b338-989c7bb0ee2c/generations/feeb07ac-a2c2-4a5a-a86a-34bbc50107cf/Ink_Line_Art_city_ink_line_art_1.jpg", - "id": "d40976a2-3863-4333-a438-0760d3c60b08", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "leaves flying in the air", - "generated_images": [ - { - "id": "0e396b06-a901-4766-8d05-f00782855bae", - "url": "https://cdn.leonardo.ai/users/0eea719d-525b-40e3-b338-989c7bb0ee2c/generations/cbf7adbc-e893-4984-9f08-e8b74a3b58f0/Ink_Line_Art_leaves_flying_in_the_air_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "02919502-26c4-40d0-b099-445b492bac65", - "name": "Obey Babay", - "description": "", - "instancePrompt": "Obey Babay", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T16:11:17.294", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "e5797a3f-0a7e-46f0-9483-68bab269fc1e", "username": "lapulkin", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/e5797a3f-0a7e-46f0-9483-68bab269fc1e/generations/67fed63c-863c-41fd-a182-7773b4a818f5/Obey_Babay_exact_true_copy_of_Obey_Babay_as_yawning_single_faced_an_2.jpg", - "id": "04da61c8-d949-4e82-b639-72bdc77abf4e", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "( exact true copy of ( Obey Babay ) as ape idol) | Horror Theme Clockpunk | ((single head)) full body illustration | Top view | centered | ((perfect anatomy)) | key visual | intricate | highly detailed | breathtaking | precise lineart | vibrant | abstract | Lovecraftian Noir | cinematic | H.R. Giger | Conrad Roset, diffuse lighting, hyper-perfect in-depth details, mind-blowing natural vivid colors, ultra-sharp, upscaled 8x, fine lines, cinematic lights, ultra wide scene, high in-depth details, Hollywood quality, a masterpiece, ultra high detailed designs, Camera: Sony A1 200MP, Lens: 85mm f/1.4, Shutter speed: 1/12 ISO:40, 24K, RAW, Sharpness from 1 to 100 (0), Noise from 1 to 100 (0), Post-processing in Adobe Lightroom, photorealistic, ultra photoreal, ultra detailed, intricate details", - "generated_images": [ - { - "id": "cb15510b-4528-4b05-88ee-cd29d6d646dd", - "url": "https://cdn.leonardo.ai/users/e5797a3f-0a7e-46f0-9483-68bab269fc1e/generations/5285376d-6b4a-431e-ba0b-97a59ce2fbc5/Obey_Babay_exact_true_copy_of_Obey_Babay_as_ape_idol_Horror_Theme_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "bab7bc01-7923-4dd4-b766-98f065240ce4", - "name": "Gangster Paradise", - "description": "a world and mood of gangsters of 1920", - "instancePrompt": "Gangster Paradise", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T13:44:39.705", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "698e8c2d-f790-4f6b-9c13-c65fa7920deb", "username": "Lugeer", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/698e8c2d-f790-4f6b-9c13-c65fa7920deb/generations/e1c9f06e-c47b-41d1-855e-25636c3fd617/Gangster_Paradise_Gangster_paradise_portrait_of_mafia_inside_a_bar_epic_contra_2.jpg", - "id": "91cb46e4-f0f3-4265-936d-2be2d8679ec9", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Gangster paradise portrait of mafia inside a bar epic contrasted light", - "generated_images": [ - { - "id": "477544dc-91c4-4c9c-828f-906a116d5f46", - "url": "https://cdn.leonardo.ai/users/698e8c2d-f790-4f6b-9c13-c65fa7920deb/generations/f1cbaf4e-64bb-45b7-98ab-bb2c474d8004/Gangster_Paradise_Gangster_paradise_portrait_of_mafia_inside_a_bar_epic_contra_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "26cb167b-629d-438e-8efc-2eb05d6181f9", - "name": "Zombie anime ", - "description": "mushrooms fantasy ", - "instancePrompt": "Portrait ", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T10:19:52.011", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "f4e66010-69af-4262-bb23-827e24c82516", "username": "JonRT", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/9f43a7f7-1b82-4c53-8523-4f452496728b/Mixed_modern_european_ink_painting_anime_female_zombie_bobblehead_m_3.jpg", - "id": "154d76c7-772c-4b42-a11c-aff921b285ea", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "cartoon rotoscoping robotic cyborg splash art glitch art action painting photography portrait image FLCL anime fashion wearing vintage robot helmet figurine TikTok island boy Kandinsky headdress flat dreads origami woodcut engraving Modern art marble statue Renaissance Delirium tremens Dali & Picasso style depiction of a beautiful animatronic fiber optics embossed multi purpose transfer LED runners Aeon Flux Bosch MC Escher cybertronic city market landscape made of mechanical Mark Ryden surrealism architecture with plasma face paint cyborg tech gears industrial circuitry parts dramatic light sharp focus artstyle Gensho Yasuda by Fausto de martini Wadim Kashin Z.W. gu Jamie Hewlett Antoni Tudisco Frederic Duquette Mumford hideyuki morioka ito ogure vofan victo ngai composition direction Keyshot Chromatic Aberration Global illumination Subsurface scattering Unreal Engine 5 Octane Render Ambient Occlusion Opulent Beautiful imaginative masterpiece 3d liquid detailing fluid acrylic", - "generated_images": [ - { - "id": "f9c50f01-ec4c-46bd-8a64-b3a857d0e516", - "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/4350b99f-e9cd-4f72-9426-14f0556f1237/Mixed_cartoon_rotoscoping_robotic_cyborg_splash_art_glitch_art_acti_3.jpg", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "8c4874c0-ec04-4a89-9433-dbf156d914f0", - "name": "Smoothie", - "description": "FruTech", - "instancePrompt": "a Fruity", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T09:56:18.619", - "sdVersion": "v2", - "type": "BUILDINGS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "4c4490f4-ee5e-4b62-9bc1-9038afa178b6", - "username": "ChadUltraF3", - "__typename": "users", - }, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/4c4490f4-ee5e-4b62-9bc1-9038afa178b6/generations/dd553f4d-c7d5-4923-a967-8c43812a8f03/Smoothie_a_fruity_bitcoin_house_1.jpg", - "id": "b5ef114a-f034-4c19-accd-27bfc01a63fa", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a fruity city made of berries", - "generated_images": [ - { - "id": "b1350d6e-6f8f-4eb4-be2a-8ae72986d549", - "url": "https://cdn.leonardo.ai/users/4c4490f4-ee5e-4b62-9bc1-9038afa178b6/generations/b23eae15-48d6-4222-a991-25ac9cf8e0c4/Smoothie_a_fruity_city_made_of_berries_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "ec0dd7d8-306f-48ce-b264-f681ec2ffbf7", - "name": "Landscape Wallpapers", - "description": "Create landscape wallpapers with any size, any resolution. See samples on my profile, search 'wallpaper' at https://app.leonardo.ai/profile/noerman", - "instancePrompt": "4k", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T09:42:39.156", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "dbc5103a-ca87-4323-b0c9-12f740e6e773", - "username": "misterprompts", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Tiangong Chinese style", - "generated_images": [ - { - "id": "e2d1a9d0-7e85-4227-9f37-e940afa70da1", - "url": "https://cdn.leonardo.ai/users/56729e70-0019-4b57-8b6a-0f2d360722f5/generations/0cf2bf9a-5734-43b9-bb0d-497b4c5ff5b4/Landscape_Wallpapers_Tiangong_Chinese_style_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "68b5e616-1834-43dc-a7c5-a0153a6dbc96", - "name": "Pixel Art Assets", - "description": "A Dataset for Pixel Art Images, but not just the Avatar, but Fullbody NPCs. Trained on Classic Video Games.", - "instancePrompt": "Pixel Character", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T09:15:36.661", - "sdVersion": "v2", - "type": "PIXEL_ART", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "0bbab906-ba22-47ef-8985-89ec3959e79e", "username": "bornmedia", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Full body pose of a mage holding a staff, pixel art, Pixel Character", - "generated_images": [ - { - "id": "f46b7aad-759f-406d-8649-f200e5f4c715", - "url": "https://cdn.leonardo.ai/users/0bbab906-ba22-47ef-8985-89ec3959e79e/generations/62fdafd0-d189-4184-aa0a-3da22a35df78/Pixel_Art_Assets_Full_body_pose_of_a_mage_holding_a_staff_pixel_art_Pixel_Ch_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "fe94035a-4c12-41c3-a618-c475a9547657", - "name": "Hong Kong Girl, HK", - "description": "", - "instancePrompt": "Hong Kong", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T07:36:03.158", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "832669e4-0939-44a7-92a5-1d1289d806a5", "username": "K2023HK", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " realistic beautiful no clothes orange haired female pianist performing a majestic galactic melody realistic with details", - "generated_images": [ - { - "id": "efb49db7-84ec-42a1-9e3c-b3416cb84c82", - "url": "https://cdn.leonardo.ai/users/ca3d54e7-88d1-41f6-bea4-3412b24e952d/generations/48e91a57-8701-488e-aa9e-7b0272aaebdb/Hong_Kong_Girl_HK_realistic_beautiful_no_clothes_orange_haired_female_pia_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "ee1c44df-e65c-4bdf-9573-067454e3e52a", - "name": "Viktor Safonkin", - "description": "", - "instancePrompt": "Viktor Safonkin", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T07:34:13.169", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "c5591d60-7ff7-4ba1-bacd-f3f256ef9190", - "username": "zaborservice", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "( exact true copy of ( the Pauk ) as an old pharaon ) | Horror Theme Steampunk | single head illustration | Frontal view | centered | perfect anatomy | key visual | intricate | highly detailed | breathtaking | precise lineart | vibrant | abstract | Lovecraftian Noir | cinematic | Viktor Safonkin, Chinese ice carving, diffuse lighting, hyper-perfect in-depth details, mind-blowing natural vivid colors, ultra-sharp, upscaled 8x, fine lines, cinematic lights, ultra wide scene, high in-depth details, Hollywood quality, a masterpiece, ultra high detailed designs, Camera: Sony A1 200MP, Lens: 85mm f/1.4, Shutter speed: 1/12 ISO:40, 24K, RAW, Sharpness from 1 to 100 (0), Noise from 1 to 100 (0), Post-processing in Adobe Lightroom, photorealistic, ultra photoreal, ultra detailed, intricate details", - "generated_images": [ - { - "id": "3851a7a9-62d4-4d16-8f95-f0f536e5c195", - "url": "https://cdn.leonardo.ai/users/f1410d36-7283-4575-8a2b-a4b8d806285c/generations/6abde543-0fbe-433a-9d13-4e2af8592ad2/Viktor_Safonkin_exact_true_copy_of_the_Pauk_as_an_old_pharaon_Horror_T_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "54695a14-0fc7-4878-8db9-583721c7f4c8", - "name": "Obese Famous character ", - "description": "This model will allow you to generate images of famous people in obese form, in a more streamlined and a little more precise way.", - "instancePrompt": "a Obese ", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T04:47:54.629", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a Obese catwoman", - "generated_images": [ - { - "id": "47323018-1273-4bc4-ab92-03bbb764dff0", - "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/58c5bc51-5a4e-4fcd-a2da-08c7fd6ca7cc/Obese_Famous_character_a_Obese_catwoman_2.jpg", - "likeCount": 1, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "020aad14-ab93-4781-8938-1d95eed5c35b", - "name": "Metropolitan Small Apartments", - "description": "This model will allow you to generate apartment photography images in metropolitan style and with little space in a more streamlined and a little m...", - "instancePrompt": "a apartments", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-03-03T04:38:34.738", - "sdVersion": "v1_5", - "type": "PHOTOGRAPHY", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "56547323-5449-448d-8560-27fd38e61b25", "username": "Blademort", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a apartments under water ", - "generated_images": [ - { - "id": "5642733d-3954-42f5-8448-c7157756c52c", - "url": "https://cdn.leonardo.ai/users/56547323-5449-448d-8560-27fd38e61b25/generations/c1015fad-a807-425d-97e6-b17f99b44b02/Metropolitan_Small_Apartments_a_apartments_under_water_2.jpg", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "515602ee-14c2-4881-b54b-19b81dcb55c3", - "name": "meh idk", - "description": "mix ", - "instancePrompt": "cybertech", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T04:16:34.94", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "f4e66010-69af-4262-bb23-827e24c82516", "username": "JonRT", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/e39ab667-3e71-4c07-ba52-4284a702ed91/meh_idk_modern_european_ink_painting_anime_female_zombie_in_a_lush_m_0.jpg", - "id": "b4c95a13-827a-4a1c-8adf-5d8882496aa6", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "female hipster techno astronaut fashion model in a vast Dali landscape with Bosch small forest cities with lush kandinsky falling stars action painting by Jamie Hewlett Alberto Mielgo Mark Ryden Yoko d’Holbachie Yosuke Ueno Todd Schorr Amy Sol Jeff Soto DSLR HDR photo ray tracing ultraperfect asymmetrical ratio composition Relative Apparent Size Keyshot MarmosetToolbag 4 3d liquid detailing nvinkpunk, beautiful detailed realistic Neo noir style, Cinematic, neon lights glow in the background. Cinestill 800T film. Lens flare. Helios 44m, dreamwave colors, depth of field, bokeh, modern art marble statue renaissance+bosch+mark ryden surrealism+3/4 portrait high fashion model cyborg ink punk+with melting liquid led lights fiber optic hair+made of hiroaki takahashi art+ultra perfect composition+3d liquid detailing+fluid acrylic+kandinsky art style a vast shooting star filled sky+compositional direction+visual tension+structural net+an opulent cybernetic city background", - "generated_images": [ - { - "id": "b95ba0b4-87cf-4d59-b3e0-02115cfeb8ca", - "url": "https://cdn.leonardo.ai/users/f4e66010-69af-4262-bb23-827e24c82516/generations/76672ed1-63fa-4662-90d3-465520e08b3d/Ai_drip_tek_female_hipster_techno_astronaut_fashion_model_in_a_vast_Dal_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "c053b5f4-d9d3-4ab9-8850-dce40a3a1d2f", - "name": "3D-Alike Comics", - "description": "Create 3D-alike characters or even scenes, perfect for your comic creations, also great for other usage. Please use simple sentence prompts.", - "instancePrompt": "3d", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T04:01:31.651", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": { - "id": "dbc5103a-ca87-4323-b0c9-12f740e6e773", - "username": "misterprompts", - "__typename": "users", - }, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a superhero named ScamBuster ", - "generated_images": [ - { - "id": "00296a84-30e4-4ec1-9dc5-e087fced2745", - "url": "https://cdn.leonardo.ai/users/7cb4854b-af78-4cfc-9328-d12404a0e6d3/generations/016688ea-2195-435b-9fd3-03f534a62930/3DAlike_Comics_a_superhero_named_ScamBuster_1.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "4eb7a7ea-dc80-45f5-ab58-f836d1c52fe5", - "name": "South Park", - "description": "A South Park trained model.", - "instancePrompt": "a South Park episode", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-03-03T02:33:53.334", - "sdVersion": "v1_5", - "type": "ILLUSTRATIONS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "2f0e87f7-44f6-4081-baeb-c7ea3023703a", "username": "Andrek", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/5e2f185b-d878-421e-8217-c5053e6215ba/South_Park_A_South_Park_episode_High_quality_illustration_3.jpg", - "id": "4215d973-dd4e-4b83-8262-aa0f4249718b", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A South Park character that doesn’t exist but has a unique design.", - "generated_images": [ - { - "id": "4b130145-3240-422e-b65c-4d5550818fe3", - "url": "https://cdn.leonardo.ai/users/2f0e87f7-44f6-4081-baeb-c7ea3023703a/generations/e2fdb127-4bf9-4c71-8209-bc488e7cb672/South_Park_A_South_Park_character_that_doesnt_exist_but_has_a_unique_de_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - ] - } -} - -platform_models = { - "data": { - "custom_models": [ - { - "id": "f1929ea3-b169-4c18-a16c-5d58b4292c69", - "name": "RPG v5", - "description": "Anashel returns with another great model, specialising in RPG characters of all kinds.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-07-28T12:02:43.911", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/ad43331b-c80c-40e5-8462-304aaaef3584/RPG_v5_a_stunning_photograph_of_a_grotesque_alien_creature_wit_1.jpg", - "id": "ea3c5232-3c5d-4b64-bd1a-be698576b769", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A vibrant cinematic photo of a female adventurer in the jungle, octane render, high quality", - "generated_images": [ - { - "id": "8b326529-2403-4238-810c-602a798c8c2c", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/95e14374-eaf9-4ac9-9611-855706aadf45/RPG_v5_A_vibrant_cinematic_photo_of_a_female_adventurer_in_the_0.jpg", - "likeCount": 6, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "b63f7119-31dc-4540-969b-2a9df997e173", - "name": "SDXL 0.9", - "description": "The latest Stable Diffusion model, currently in Beta.", - "instancePrompt": None, - "modelHeight": 768, - "modelWidth": 1024, - "coreModel": "SD", - "createdAt": "2023-07-12T14:37:01.33", - "sdVersion": "SDXL_0_9", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9ed4ccd8-649c-4a59-a7bb-9f5b704a91b1/SDXL_09_a_beautiful_woman_vivid_striking_colors_cinematic_phot_0.jpg", - "id": "4a5b68d4-ef23-4d36-a1d8-b027287da029", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a beautiful woman, vivid striking colors, cinematic photography", - "generated_images": [ - { - "id": "6cea9433-3e8c-4614-8a5e-4cbb969e6699", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9ed4ccd8-649c-4a59-a7bb-9f5b704a91b1/SDXL_09_a_beautiful_woman_vivid_striking_colors_cinematic_phot_1.jpg", - "likeCount": 5, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "d69c8273-6b17-4a30-a13e-d6637ae1c644", - "name": "3D Animation Style", - "description": "Great at 3D film vibes, capable of complex scenes with rich color. Storyboard time!", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-07-04T04:16:46.127", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/0e85d140-3ea0-4179-a708-ae95bf9329a3/3D_Animation_Style_a_masterpiece_image_of_an_older_tired_and_b_2.jpg", - "id": "ca1b67dc-8e39-49a8-8846-f930fb286ba9", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a masterpiece image of an older tired and battle-worn male detective named jack smith, highly detailed, HDR, cyberpunk, realistic, (photorealistic:1.4), best quality, ultra high res", - "generated_images": [ - { - "id": "aa84be9e-4156-4887-afe8-d7b858cbe0de", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/2183b6e1-29f7-4295-bd86-7ed3fa233d5c/PixarTypeB_a_masterpiece_image_of_an_older_tired_and_battlewor_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "ac614f96-1082-45bf-be9d-757f2d31c174", - "name": "DreamShaper v7", - "description": "Lykon is back with another update. This model is great at a range of different styles.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-07-04T04:16:25.719", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/911623f3-4a89-4db9-b49f-ace5ac086a3d/DreamShaper_v7_A_vibrant_cinematic_photo_of_a_female_adventure_1.jpg", - "id": "52550c00-4def-4283-91f4-ccd196a630bf", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "an older tired and battle-worn male detective, highly detailed, HDR, cyberpunk", - "generated_images": [ - { - "id": "fb390f8e-e9d6-4ee8-a67c-7cae9a132bab", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/bff69fae-6e4e-48d1-8a8f-6c75799be511/DreamShaper_v7_an_older_tired_and_battleworn_male_detective_hi_1.jpg", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "e316348f-7773-490e-adcd-46757c738eb7", - "name": "Absolute Reality v1.6", - "description": "A photorealistic style model from Lykon. Great at all sorts of photorealism.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-07-04T04:15:48.815", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9d7e2dbe-6dff-4bf5-b487-414dee2a10b9/Absolute_Reality_v16_a_stunning_photo_of_a_woman_with_aztech_t_1.jpg", - "id": "cf623adb-d7f3-43e6-8431-d2edd5a7a08e", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a photo of a woman with a tribal headress, stunning photography, masterpiece, hdr", - "generated_images": [ - { - "id": "cc14e950-ffdb-41ee-85d0-eb9419956a82", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/4836d001-a2df-4a29-a0dd-11786b857f55/Absolute_Reality_v16_a_photo_of_a_woman_with_a_tribal_headress_2.jpg", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "1aa0f478-51be-4efd-94e8-76bfc8f533af", - "name": "Anime Pastel Dream", - "description": "Pastel anime styling. Use with PMv3 and the anime preset for incredible range. Model by Lykon.", - "instancePrompt": None, - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2023-06-13T06:01:06.314", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8cc624c3-c1ba-40c9-b3cd-21056382728a/AnimePastelDream_fuji_film_candid_portrait_o_a_black_woman_wea_2.jpg", - "id": "c5510862-e82b-4705-861c-58658a89fa9c", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "fuji film candid portrait o a black woman wearing sunglasses rocking out on the streets of miami at night, 80s album cover, vaporwave, synthwave, retrowave, cinematic, intense, highly detailed, dark ambient, beautiful, dramatic lighting, hyperrealistic", - "generated_images": [ - { - "id": "66c689b4-dfaa-43c3-8f72-b664db7f21e5", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/db468128-7209-4d2d-9c17-6ddcf6e1f792/AnimePastelDream_fuji_film_candid_portrait_o_a_black_woman_wea_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "b7aa9939-abed-4d4e-96c4-140b8c65dd92", - "name": "DreamShaper v6", - "description": "A new update to an incredibly versatile model, excels at both people and environments, by Lykon.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-05-26T02:29:05.936", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8ab5c76a-eefb-4801-817f-458f68958db7/DreamShaperV6_a_masterpiece_image_of_Splash_art_music_album_ar_6.jpg", - "id": "e7a9a1ff-76be-4cbd-b560-7379f1af2c32", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a warrior fighting a dragon", - "generated_images": [ - { - "id": "b1c9e57c-acf1-442b-9669-6a34d5128fb7", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/0ca698f0-9623-4081-93e0-e2516fb0d3bd/DreamShaperV6_a_warrior_fighting_a_dragon_4.jpg", - "likeCount": 14, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "d2fb9cf9-7999-4ae5-8bfe-f0df2d32abf8", - "name": "DreamShaper v5", - "description": "A versatile model great at both photorealism and anime, includes noise offset training, by Lykon.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-04-13T04:56:36.796", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/bb22942b-40c8-4a06-a219-238808053ee0/DreamShaper_v5_extremely_intricate_fantasy_character_photoreal_0.jpg", - "id": "27bec45f-b450-4775-817f-761683f2cc5e", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "extremely intricate fantasy character, photorealistic, portrait of a great warrior, intricate details on clothing, professional oil painting, deviantart hd, artstation hd, concept art, cg society, dramatic, award winning matte drawing cinematic lighting octane render unreal engine volumetrics dtx", - "generated_images": [ - { - "id": "0e0394bb-c1e7-46f4-b6e4-b205f97ecec7", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/4aa70349-6456-4ef7-a942-8c3db37a8339/DreamShaper_v5_extremely_intricate_fantasy_character_photoreal_1.jpg", - "likeCount": 18, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "b820ea11-02bf-4652-97ae-9ac0cc00593d", - "name": "Leonardo Diffusion", - "description": "A model with incredible shading and contrast, great at both photos and artistic styles, by cac0e.", - "instancePrompt": None, - "modelHeight": 1024, - "modelWidth": 1024, - "coreModel": "SD", - "createdAt": "2023-02-28T20:26:06.053", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8905a8f0-9219-43ad-8ecb-1c37b4501dc5/Leonardo_Diffusion_ultra_detailed_artistic_photography_of_a_fashion_moden_3.jpg", - "id": "08b6f797-85f1-457a-a194-2e82f725bd6b", - "__typename": "generated_images", - }, - "imageCount": 5343, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A stunning image of a Vulcan warrior", - "generated_images": [ - { - "id": "14bcb95a-a52a-4dd0-837c-c782ab065d8f", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/bb70ebcb-48fe-488b-b220-fb19696b385e/Leonardo_Diffusion_A_stunning_image_of_a_Vulcan_warrior_2.jpg", - "likeCount": 4, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "a097c2df-8f0c-4029-ae0f-8fd349055e61", - "name": "RPG 4.0", - "description": "This model is best at creating RPG character portraits with the ability for great photorealism. Created by Anashel.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-02-09T11:47:04.271", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/20cd8cca-ad36-46b1-a0e3-8bd669112017/RPG_portrait_painting_of_a_redhead_feminine_royal_woman_with_a_fe_3.jpg", - "id": "419e4e5e-245d-49f0-b613-b1e8b02f6ca5", - "__typename": "generated_images", - }, - "imageCount": 1000070, - "__typename": "custom_models", - "generations": [ - { - "prompt": "Photography of a well built modern cottage house sitting on top of a lake covered in snow, sunset moment, surrounded by mountains, Unsplash contest winner, cozy interior, warm interior lighting, made of glass, Vray, minimalist, high details, denoise, insanely detailed and intricate, professional color graded, hyper realistic, super detailed, 8k,HDR,high-resolution DSLR photograph, shot on IMAX200", - "generated_images": [ - { - "id": "1cd73660-1c9c-4409-b856-e5da0d30d003", - "url": "https://cdn.leonardo.ai/users/b35decec-845a-475a-960f-a690332c3cf3/generations/926c827d-3504-4541-b4ff-49c5f4487858/RPG_Photography_of_a_well_built_modern_cottage_house_sitting_on_t_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "458ecfff-f76c-402c-8b85-f09f6fb198de", - "name": "Deliberate 1.1", - "description": "A powerful model created by XpucT that is great for both photorealism and artistic creations.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-02-08T10:25:54.556", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/da624520-f0c6-4afa-b171-9bfc3bbb27de/Deliberate_v11_waitress_pinup_art_style_kodak_portra_400_cinematic_smiling_0.jpg", - "id": "59776f05-378e-4c95-88c0-1740ca6c25a3", - "__typename": "generated_images", - }, - "imageCount": 1048521, - "__typename": "custom_models", - "generations": [ - { - "prompt": "High detail RAW color art, animation, cartoon, magical atmosphere, (detailed skin, skin texture), (intricately detailed, fine details, hyperdetailed), raytracing, subsurface scattering, (muted colors), diffused soft lighting, shallow depth of field, by (Oliver Wetter), photographed on a Canon EOS R5, 28mm lens, F/2.8, sharp focus bokeh, backlight, volumetric lighting, (by Anastasiya Dobrovolskaya)", - "generated_images": [ - { - "id": "2a6793b4-8482-4026-8bf7-09151ab3159f", - "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/ddf7239c-4972-429b-8a0a-1de6f9647738/Deliberate_High_detail_RAW_color_art_animation_cartoon_magical_atmospher_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "17e4edbf-690b-425d-a466-53c816f0de8a", - "name": "Vintage Style Photography", - "description": "This model can generate a broad range of imagery with a vintage style as if it was taken from a film camera", - "instancePrompt": "vintage style", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-01-29T09:40:50.165", - "sdVersion": "v2", - "type": "PHOTOGRAPHY", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59", "username": "Jachin", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/7a6f17f3-689c-461d-9c59-14a7ac88fa0e/Vintage_Style_Photography_Classic_convertible_driving_on_an_open_road_vintage_style_2.jpg", - "id": "7ffe80b5-11d6-419b-89d1-25abe7cd3fd0", - "__typename": "generated_images", - }, - "imageCount": 59908, - "__typename": "custom_models", - "generations": [ - { - "prompt": " A vibrant, sun-drenched vintage-style street scene with a classic car parked in the foreground.", - "generated_images": [ - { - "id": "c07a9677-2747-42c7-90fa-1638f36a08ea", - "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/f49e35b6-0ed8-4fa7-a015-c3e59a0bd4a3/Vintage_v1_A_vibrant_sundrenched_vintagestyle_street_scene_with_a_clas_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "f3296a34-9aef-4370-ad18-88daf26862c3", - "name": "DreamShaper 3.2", - "description": "This model by Lykon is great at a range of portrait styles as well as artistic backgrounds.", - "instancePrompt": None, - "modelHeight": 832, - "modelWidth": 640, - "coreModel": "SD", - "createdAt": "2023-01-23T11:21:17.532", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/b2919072-7c52-409e-9c2a-11d56c5c2ed2/DreamShaper_32_Full_body_Beautiful_final_fantasy_style_portrait_clean_detai_2.jpg", - "id": "758af0e6-ffcc-494a-9543-aa123612c968", - "__typename": "generated_images", - }, - "imageCount": 856247, - "__typename": "custom_models", - "generations": [ - { - "prompt": "beautiful elve,dragon , portait, highly detailed, 8k", - "generated_images": [ - { - "id": "43d66bae-8a8d-45c0-8b94-b252b556dc4c", - "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/c315ef67-1b35-43da-8440-e2b802b1008a/DreamShaper_beautiful_elvedragon_portait_highly_detailed_8k_0.jpg", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "cd2b2a15-9760-4174-a5ff-4d2925057376", - "name": "Leonardo Select", - "description": "A powerful finetune of SD2.1 that can achieve a high level of realism.", - "instancePrompt": None, - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-01-06T01:05:25.657", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 481673, - "__typename": "custom_models", - "generations": [ - { - "prompt": "portrait of female character wearing 80s clothing. she should look to the left. she should have blonde hair ", - "generated_images": [ - { - "id": "5c1a4725-160d-49cb-9b91-4ad2ac3de9a8", - "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/ff4e1c40-933a-47da-8c52-31689ed282d7/Leonardo_Select_portrait_of_female_character_wearing_80s_clothing_she_should_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "6bef9f1b-29cb-40c7-b9df-32b51c1f67d3", - "name": "Leonardo Creative", - "description": "An alternative finetune of SD 2.1 that brings a little more creative interpretation to the mix.", - "instancePrompt": None, - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2023-01-06T01:02:38.315", - "sdVersion": "v2", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 2416039, - "__typename": "custom_models", - "generations": [ - { - "prompt": "portrait of male character wearing 80s clothing. he should look to the left. he should have blonde hair and a mustache", - "generated_images": [ - { - "id": "d0841a38-0e78-4a84-acc7-62c669f8cf78", - "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/3665e729-cf65-4766-aebc-e351bc27e239/Leonardo_Creative_portrait_of_male_character_wearing_80s_clothing_he_should_lo_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "47a6232a-1d49-4c95-83c3-2cc5342f82c7", - "name": "Battle Axes", - "description": "Generate a variety of detailed axe designs with this model. From medieval battle axes to modern chopping axes, this model is great for creating a r...", - "instancePrompt": "Axe", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-30T12:20:12.519", - "sdVersion": "v1_5", - "type": "GAME_ITEMS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59", "username": "Jachin", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/1a60b46c-24d1-47c0-9683-e0e837b6f129/Battle_Axes_an_axe_blade_centre_of_frame_3.jpg", - "id": "29e709a4-00a0-48c1-af53-1d9640a6febd", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " A menacing, jagged-edged axe with a glowing, fiery blade, perfect for a fantasy RPG.", - "generated_images": [ - { - "id": "364f31e2-d337-40ad-bc5f-c683aea2cdf0", - "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/f5e64c82-0d31-40fc-bb8b-3a4c273243d4/Default_A_menacing_jaggededged_axe_with_a_glowing_fiery_blade_perfec_0.png", - "likeCount": 1, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "e5a291b6-3990-495a-b1fa-7bd1864510a6", - "name": "Pixel Art", - "description": "A pixel art model that's trained on headshots, but is surprisingly flexible with all sorts of subjects.", - "instancePrompt": "pixel art", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-30T06:21:16.301", - "sdVersion": None, - "type": "PIXEL_ART", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 85083, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A robotic cat with glowing eyes and sleek metal fur.", - "generated_images": [ - { - "id": "b582fad8-9149-4733-9798-19ec5fb84393", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/5628b06f-d989-4f89-87b2-9f7af4bb0ee7/Default_A_robotic_cat_with_glowing_eyes_and_sleek_metal_fur_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "302e258f-29b5-4dd8-9a7c-0cd898cb2143", - "name": "Chest Armor", - "description": "Create all sorts of chest armor with this model in a consistent style but with wide thematic range.", - "instancePrompt": "chest armor", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-29T21:13:02.608", - "sdVersion": "v1_5", - "type": "GAME_ITEMS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "test chest armor", - "generated_images": [ - { - "id": "050cb02c-5122-4e97-9810-f784e6bb64c4", - "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/465401c6-febf-4223-a9f1-fae4e54931bf/Default_test_chest_armor_2.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "102a8ee0-cf16-477c-8477-c76963a0d766", - "name": "Crystal Deposits", - "description": "A model for creating crystal deposits. Well-suited for use as items or in an isometric environment.", - "instancePrompt": "a crystal", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-29T21:13:02.608", - "sdVersion": "v1_5", - "type": "ENVIRONMENTS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a yellow tkwst2, object, 2d object, art by tekkonkinkreet, octane render, unreal engine, 3D, 8K, ultra-detailed, intricate, sharp focus", - "generated_images": [ - { - "id": "f7542346-c640-40ba-9f51-453e1cea3020", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/a9c191fa-6137-4e01-922c-943ee4e4788c/Default_a_yellow_tkwst2_object_2d_object_art_by_tekkonkinkreet_octane_2.png", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "45ab2421-87de-44c8-a07c-3b87e3bfdf84", - "name": "Magic Potions", - "description": 'A great model for creating incredible semi-realistic magic potions. Try appending "intricately detailed, 3d vray render" to your prompt.', - "instancePrompt": "a magic potion", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-29T21:13:02.608", - "sdVersion": "v1_5", - "type": "GAME_ITEMS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/6ae64207-79bc-48e2-b721-46c00d8c938b/Default_a_stunningly_beautiful_magic_potion_containing_a_galaxy_fili_1.png", - "id": "3aa11a5c-0496-40ca-b635-e3c78f161666", - "__typename": "generated_images", - }, - "imageCount": 15026, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a stunning magic potion, intricately detailed, 3d vray render", - "generated_images": [ - { - "id": "a7303236-34f8-4ce7-a187-a95966b858ff", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8765c4f3-55ed-40c0-9aeb-778f54ee27ab/Default_a_stunning_magic_potion_intricately_detailed_3d_vray_render_3.png", - "likeCount": 3, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "6c95de60-a0bc-4f90-b637-ee8971caf3b0", - "name": "Character Portraits", - "description": "A model that's for creating awesome RPG characters of varying classes in a consistent style.", - "instancePrompt": "character portrait", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-29T21:13:02.608", - "sdVersion": "v1_5", - "type": "CHARACTERS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "male king, green hair, detailed, soft, hyper-detailed, Cinematic, octane render", - "generated_images": [ - { - "id": "df6c66b3-882d-4303-8bb2-e8073c2e677b", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/1d554364-792b-498a-be17-ea0dfcca67d1/Default_male_king_green_hair_detailed_soft_hyperdetailed_Cinematic_oc_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "2d18c0af-374e-4391-9ca2-639f59837c85", - "name": "Magic Items", - "description": "Create a wide range of magical items like weapons, shields, boots, books. Very versatile.", - "instancePrompt": "an item", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-29T21:13:02.608", - "sdVersion": "v1_5", - "type": "GAME_ITEMS", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a red boot, object, concept art, dota 2 style, red abstract background, watercolor, epic smooth illustration", - "generated_images": [ - { - "id": "66dfe54f-2a56-49d7-b5aa-04ad012a9293", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/1a2ce3b1-998c-4795-8582-6f49aca28160/Default_a_red_boot_object_concept_art_dota_2_style_red_abstract_backg_1.png", - "likeCount": 3, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "ff883b60-9040-4c18-8d4e-ba7522c6b71d", - "name": "Amulets", - "description": "Create unique and intricate amulets, jewellery and more. Try loading up the prompt terms to steer it in interesting directions.", - "instancePrompt": "an amulet", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": " A close-up of an ancient amulet, illuminated by a single ray of light from a nearby window.", - "generated_images": [ - { - "id": "b68004dd-c030-4e51-8e68-d9638506e15a", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/edccace2-4291-482b-b3c6-d735ff06e640/Default_A_closeup_of_an_ancient_amulet_illuminated_by_a_single_ray_o_2.png", - "likeCount": 1, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "4b2e0f95-f15e-48d8-ada3-c071d6104db8", - "name": "Christmas Stickers", - "description": "Generate festive and fun Christmas stickers with this model. From cute and colorful to traditional and elegant.", - "instancePrompt": "a sticker", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "test sticker", - "generated_images": [ - { - "id": "3c737702-f32e-4dc0-b2d6-6e8a9a036be5", - "url": "https://cdn.leonardo.ai/users/2e1f21ed-7ca4-4f78-80ed-1646ea463a01/generations/15aaa53a-6ee1-4887-bfba-f00213f64756/Default_test_sticker_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "50c4f43b-f086-4838-bcac-820406244cec", - "name": "Cute Characters", - "description": 'Create cute and charming game characters, perfect for adding some whimsy to your game design. Be sure to include the word "character" in your prompts for best results.', - "instancePrompt": "a character", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 45280, - "__typename": "custom_models", - "generations": [ - { - "prompt": "A zany game character clad in an outrageous cowboy grass skirt, wild paisley jacket, and light-up sneakers.", - "generated_images": [ - { - "id": "68ed1341-ea92-4637-9bb9-237432628c13", - "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/85cf7bd8-a233-42ca-a7d0-e900a0bdbb2a/Default_A_zany_game_character_clad_in_an_outrageous_cowboy_grass_skir_1.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "6908bfaf-8cf2-4fda-8c46-03f892d82e06", - "name": "Cute Animal Characters", - "description": "Perfect for creating adorable and cute animal characters - loveable and playful designs.", - "instancePrompt": "a character", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 96633, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a rabbit character", - "generated_images": [ - { - "id": "de3da9e6-eab2-4306-a336-43edce4b00b0", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/6ac55e1f-500a-4967-bf7e-e5af117c5f6d/Default_a_rabbit_character_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "5fdadebb-17ae-472c-bf76-877e657f97de", - "name": "Spirit Creatures", - "description": "From whimsical fairy-like beings to mythical creatures, create unique cute spirit characters.", - "instancePrompt": "a creature", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a creature with more than six eyes in rainbow colors", - "generated_images": [ - { - "id": "f9e470bc-7fd3-4472-ae77-c1c09eb8f5d5", - "url": "https://cdn.leonardo.ai/users/7a129367-fa22-48ff-a5eb-441861c60a20/generations/8874bdc9-9e80-4acb-ae5a-fa2c4db6819b/Default_a_creature_with_more_than_six_eyes_in_rainbow_colors_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "ab200606-5d09-4e1e-9050-0b05b839e944", - "name": "Isometric Fantasy", - "description": 'Create all sorts of isometric fantasy environments. Try appending "3d vray render, isometric" and using a guidance scale of 6. For the negative prompt, try "unclear, harsh, oversaturated, soft, blurry".', - "instancePrompt": "isometric fantasy", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": { - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8f5f48d1-0042-4625-b47a-83e266432abf/Isometric_Fantasy_Waterfall_isolated_on_white_3d_vray_render_isometric_ultra_d_3.jpg", - "id": "8aa79ad7-dca0-4d88-9b8b-e4766e1e9047", - "__typename": "generated_images", - }, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a fantasy diorama with a castle and lake", - "generated_images": [ - { - "id": "9eeddd56-fd49-4805-8c37-4ee03ddbc5f1", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/9f8ee7a6-e5b9-4fe1-9a14-77d7f44ad4e1/Default_a_fantasy_diorama_with_a_castle_and_lake_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "ee0fc1a3-aacb-48bf-9234-ada3cc02748f", - "name": "Shields", - "description": "Create a variety of impressively varied and detailed shield designs. Allows for an incredible range of material types.", - "instancePrompt": "a shield", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "An extraterrestrial shield bearing delicate swirls and ethereal filigrees", - "generated_images": [ - { - "id": "da79e8e6-2bd1-4022-82c4-f21127bbc3fc", - "url": "https://cdn.leonardo.ai/users/4e397cdd-4af3-48e5-b0e7-c2b5d1ebee59/generations/7b3c338c-8011-42e7-bcc7-4c9cfd450b71/Default_An_extraterrestrial_shield_bearing_delicate_swirls_and_ethere_1.png", - "likeCount": 1, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "7a65f0ab-64a7-4be2-bcf3-64a1cc56f627", - "name": "Isometric Scifi Buildings", - "description": 'Great at creating scifi buildings of varying themes. Append the word isometric to your prompt to ensure an isometric view. "3d vray render" also helps steer the generation well. ', - "instancePrompt": "isometric scifi", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2022-12-28T20:31:20", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a stunning scifi base, turrets and lasers, 3d vray render", - "generated_images": [ - { - "id": "08cf2772-080e-4220-ac97-91cc13f18e61", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/1aa5f16a-b64e-4a41-98d2-702fae7d4ba2/Default_a_stunning_scifi_base_turrets_and_lasers_3d_vray_render_0.png", - "likeCount": 1, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "5fce4543-8e23-4b77-9c3f-202b3f1c211e", - "name": "Crystal Deposits Alternate", - "description": 'An alternative crystal deposits model that gives a slightly more realistic feel with its creations. Try using "object" and "3d vray render" in your prompts.', - "instancePrompt": "1@t crystal", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-28T14:41:59.451", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "a crystal", - "generated_images": [ - { - "id": "510a3f3c-542d-4d6b-8c49-b3e0cf82e44a", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/49c5c8a5-5fd9-4222-bc15-6ac8cd63cb8e/Default_a_crystal_3.png", - "likeCount": 2, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "756be0a8-38b1-4946-ad62-c0ac832422e3", - "name": "Isometric Asteroid Tiles", - "description": 'A model for creating isometric asteroid environment tiles. Try appending "3d vray render, unreal engine, beautiful, intricately detailed, trending on artstation, 8k" to your prompt.', - "instancePrompt": "1@t isometric asteroid", - "modelHeight": 512, - "modelWidth": 512, - "coreModel": "SD", - "createdAt": "2022-12-28T06:54:35.42", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 0, - "__typename": "custom_models", - "generations": [ - { - "prompt": "isometric tile, 3d vray render, unreal engine, beautiful, intricately detailed, trending on artstation, 8k", - "generated_images": [ - { - "id": "1b5c1821-9ebf-4fea-867f-58e847c0995e", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/8cd48839-28fe-4f25-9a32-b7a8a118583b/Default_isometric_tile_3d_vray_render_unreal_engine_beautiful_intrica_0.png", - "likeCount": 0, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - { - "id": "291be633-cb24-434f-898f-e662799936ad", - "name": "Leonardo Signature", - "description": "The core model of the Leonardo platform. An extremely powerful and diverse finetune which is highly effective for a wide range of uses.", - "instancePrompt": "", - "modelHeight": 768, - "modelWidth": 768, - "coreModel": "SD", - "createdAt": "2022-12-24T21:08:03.749", - "sdVersion": "v1_5", - "type": "GENERAL", - "nsfw": False, - "public": True, - "trainingStrength": "MEDIUM", - "user": {"id": "384ab5c8-55d8-47a1-be22-6a274913c324", "username": "Leonardo", "__typename": "users"}, - "generated_image": None, - "imageCount": 864451, - "__typename": "custom_models", - "generations": [ - { - "prompt": "an incredibly stunning photograph of a throne room, soft lighting", - "generated_images": [ - { - "id": "eec2d87e-d27c-4b95-8a1a-b948fd49df1e", - "url": "https://cdn.leonardo.ai/users/384ab5c8-55d8-47a1-be22-6a274913c324/generations/2956bf2a-3039-43ee-83d6-65318e5f58fa/Default_an_incredibly_stunning_photograph_of_a_throne_room_soft_light_2.png", - "likeCount": 122, - "__typename": "generated_images", - } - ], - "__typename": "generations", - } - ], - "user_favourite_custom_models": [], - }, - ] - } -} diff --git a/requirements.txt b/requirements.txt index 371fca8..5fabb29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,4 +19,4 @@ numpy==1.25.2 # Image pillow==10.0.0 # Leonardo -aiofiles==23.2.1 \ No newline at end of file +aiofiles==23.2.1 From 9ff4db76971b04f963dadf16ed73655e42694ca4 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 12 Sep 2023 17:12:03 +0200 Subject: [PATCH 27/32] v0.1 --- requirements.txt | 4 ++-- utils/article_extractor.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 utils/article_extractor.py diff --git a/requirements.txt b/requirements.txt index 5fabb29..e82a057 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,5 +18,5 @@ soundfile==0.12.1 numpy==1.25.2 # Image pillow==10.0.0 -# Leonardo -aiofiles==23.2.1 +# Artuicles +readability==0.3.1 diff --git a/utils/article_extractor.py b/utils/article_extractor.py new file mode 100644 index 0000000..f7915eb --- /dev/null +++ b/utils/article_extractor.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +Filename: article_extractor.py +Author: Iliya Vereshchagin +Copyright (c) 2023. All rights reserved. + +Created: 25.08.2023 +Last Modified: 12.09.2023 + +Description: +This file contains implementation for Article Extractor from internet page +""" + +import requests +from readability import Document + +# FIXME: This is a temporary solution. We need to find a better way to extract + + +def get_content(url): + session = requests.Session() + response = session.get(url) + doc = Document(response.text) + content = doc.summary() + return content From 1c43def55e77db9de16fb36fe0255708c455ac2b Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 12 Sep 2023 17:12:36 +0200 Subject: [PATCH 28/32] v0.1 --- leonardo_engine/TODO | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 leonardo_engine/TODO diff --git a/leonardo_engine/TODO b/leonardo_engine/TODO deleted file mode 100644 index e69de29..0000000 From 24c5f6b22750adcf761ccde47f72021d8e26b128 Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 12 Sep 2023 17:14:03 +0200 Subject: [PATCH 29/32] v0.1 --- .gitmodules | 3 +++ leonardo_api | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 leonardo_api diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..34c3211 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "leonardo_api"] + path = leonardo_api + url = git@github.com:wwakabobik/leonardo_api.git diff --git a/leonardo_api b/leonardo_api new file mode 160000 index 0000000..a1baea0 --- /dev/null +++ b/leonardo_api @@ -0,0 +1 @@ +Subproject commit a1baea0fb4e8350783bfd9360025cd719775d9b7 From 1d7915bda49d05a655742db3e25f61d0f258d99c Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 12 Sep 2023 23:17:30 +0200 Subject: [PATCH 30/32] v0.1 --- .gitmodules | 3 + CHANGELOG.md | 8 +- README.md | 3 +- TODO.md | 3 + __init__.py | 7 +- __main__.py | 7 +- openai_api | 1 + openai_engine/TODO | 4 - openai_engine/__init__.py | 0 openai_engine/chatgpt.py | 908 -------------------------------------- openai_engine/dalle.py | 364 --------------- openai_engine/models.py | 33 -- requirements.txt | 5 +- 13 files changed, 19 insertions(+), 1327 deletions(-) create mode 100644 TODO.md create mode 160000 openai_api delete mode 100644 openai_engine/TODO delete mode 100644 openai_engine/__init__.py delete mode 100644 openai_engine/chatgpt.py delete mode 100644 openai_engine/dalle.py delete mode 100644 openai_engine/models.py diff --git a/.gitmodules b/.gitmodules index 34c3211..ca4d8e1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "leonardo_api"] path = leonardo_api url = git@github.com:wwakabobik/leonardo_api.git +[submodule "openai_api"] + path = openai_api + url = git@github.com:wwakabobik/openai_api.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 77a5416..e125406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,15 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1] - 2023-08-25 ### Added -- ChatGPT engine -- DALL-E engine + - audio_recorder - logger - transcriptors - translators - tts +- OpenAI API +- Leonardo API -## [0.2] - 2023-08-28 -### Added -- Leonardo API diff --git a/README.md b/README.md index 8e2bdc7..2973a52 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ -# ai_engines \ No newline at end of file +# ai_engines +Playground and utils libraries for AI stuff diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..5659fec --- /dev/null +++ b/TODO.md @@ -0,0 +1,3 @@ +- Add more params and fixes +- Add image handling methods (generalistic) +- Rework as pypi package diff --git a/__init__.py b/__init__.py index dc38a69..451bb7f 100644 --- a/__init__.py +++ b/__init__.py @@ -5,16 +5,15 @@ Copyright (c) 2023. All rights reserved. Created: 25.08.2023 -Last Modified: 26.08.2023 +Last Modified: 12.09.2023 Description: This file is init point for project-wide structure. """ # Engines -from .leonardo_engine.leonardo import Leonardo # pylint: disable=unused-import -from .openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import -from .openai_engine.dalle import DALLE # pylint: disable=unused-import +from .openai_api.src.openai_api.chatgpt import ChatGPT # pylint: disable=unused-import +from .openai_api.src.openai_api.dalle import DALLE # pylint: disable=unused-import # Utils from .utils.tts import CustomTTS # pylint: disable=unused-import diff --git a/__main__.py b/__main__.py index 303e34c..b0d22f8 100644 --- a/__main__.py +++ b/__main__.py @@ -5,16 +5,15 @@ Copyright (c) 2023. All rights reserved. Created: 25.08.2023 -Last Modified: 26.08.2023 +Last Modified: 12.09.2023 Description: This file is entry point for project-wide structure. """ # Engines -from leonardo_engine.leonardo import Leonardo # pylint: disable=unused-import -from openai_engine.chatgpt import ChatGPT # pylint: disable=unused-import -from openai_engine.dalle import DALLE # pylint: disable=unused-import +from openai_api.src.openai_api.chatgpt import ChatGPT # pylint: disable=unused-import +from openai_api.src.openai_api.dalle import DALLE # pylint: disable=unused-import # Utils from utils.tts import CustomTTS # pylint: disable=unused-import diff --git a/openai_api b/openai_api new file mode 160000 index 0000000..51ee463 --- /dev/null +++ b/openai_api @@ -0,0 +1 @@ +Subproject commit 51ee4630af01dfb6c62971f28c6d02beda718282 diff --git a/openai_engine/TODO b/openai_engine/TODO deleted file mode 100644 index 04bf8a9..0000000 --- a/openai_engine/TODO +++ /dev/null @@ -1,4 +0,0 @@ -- ADD STATISTICS tracking -- ADD Logger -- Add more params and fixes -- Add image handling method (generalistic) diff --git a/openai_engine/__init__.py b/openai_engine/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/openai_engine/chatgpt.py b/openai_engine/chatgpt.py deleted file mode 100644 index 1cbbcb7..0000000 --- a/openai_engine/chatgpt.py +++ /dev/null @@ -1,908 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Filename: chatgpt.py -Author: Iliya Vereshchagin -Copyright (c) 2023. All rights reserved. - -Created: 25.08.2023 -Last Modified: 26.08.2023 - -Description: -This file contains implementation for ChatGPT -""" - -import json -import logging - -import openai - -from openai_engine.models import COMPLETIONS, TRANSCRIPTIONS, TRANSLATIONS - - -class GPTStatistics: - """ - The GPTStatistics class is for managing an instance of the GPTStatistics model. - - Parameters: - prompt_tokens (int): The number of tokens in the prompt. Default is 0. - completion_tokens (int): The number of tokens in the completion. Default is 0. - total_tokens (int): The total number of tokens. Default is 0. - """ - - def __init__( - self, - prompt_tokens: int = 0, - completion_tokens: int = 0, - total_tokens: int = 0, - ): - """ - Constructs all the necessary attributes for the GPTStatistics object. - - :param prompt_tokens: The number of tokens in the prompt. Default is 0. - :param completion_tokens: The number of tokens in the completion. Default is 0. - :param total_tokens: The total number of tokens. Default is 0. - """ - self.___prompt_tokens = prompt_tokens - self.___completion_tokens = completion_tokens - self.___total_tokens = total_tokens - - @property - def prompt_tokens(self): - """ - Getter for prompt_tokens. - - :return: The prompt tokens. - """ - return self.___prompt_tokens - - @prompt_tokens.setter - def prompt_tokens(self, value): - """ - Setter for prompt_tokens. - - :param value: The prompt tokens. - """ - self.___prompt_tokens = value - - @property - def completion_tokens(self): - """ - Getter for completion_tokens. - - :return: The completion tokens. - """ - return self.___completion_tokens - - @completion_tokens.setter - def completion_tokens(self, value): - """ - Setter for completion_tokens. - - :param value: The completion tokens. - """ - self.___completion_tokens = value - - @property - def total_tokens(self): - """ - Getter for completion_tokens. - - :return: The completion tokens. - """ - return self.___total_tokens - - @total_tokens.setter - def total_tokens(self, value): - """ - Setter for total_tokens. - - :param value: The total tokens. - """ - self.___total_tokens = value - - def add_prompt_tokens(self, value): - """ - Adder for prompt_tokens. - - :param value: The prompt tokens. - """ - self.prompt_tokens += value - - def add_completion_tokens(self, value): - """ - Adder for completion_tokens. - - :param value: The completion tokens. - """ - self.completion_tokens += value - - def add_total_tokens(self, value): - """ - Adder for total_tokens. - - :param value: The total tokens. - """ - self.total_tokens += value - - def set_tokens(self, prompt_tokens, completion_tokens, total_tokens): - """ - Sets all tokens statistics in bulk - - :param prompt_tokens: The prompt tokens. - :param completion_tokens: The prompt tokens. - :param total_tokens: The prompt tokens. - """ - self.prompt_tokens = prompt_tokens - self.completion_tokens = completion_tokens - self.total_tokens = total_tokens - - def add_tokens(self, prompt_tokens, completion_tokens, total_tokens): - """ - Adds all tokens statistics in bulk - - :param prompt_tokens: The prompt tokens. - :param completion_tokens: The prompt tokens. - :param total_tokens: The prompt tokens. - """ - self.prompt_tokens += prompt_tokens - self.completion_tokens += completion_tokens - self.total_tokens += total_tokens - - def get_tokens(self): - """ - Returns a dictionary of the class attributes and their values. - - :return: dict with tokens statistics - """ - return { - "prompt_tokens": self.___prompt_tokens, - "completion_tokens": self.___completion_tokens, - "total_tokens": self.___total_tokens, - } - - -# pylint: disable=too-many-instance-attributes,too-many-public-methods -class ChatGPT: - """ - The ChatGPT class is for managing an instance of the ChatGPT model. - - Parameters: - auth_token (str): Authentication bearer token. Required. - organization (str): Organization uses auth toke. Required. - model (str): The name of the model, Default is 'gpt-4'. - choices (int, optional): The number of response options. Default is 1. - temperature (float, optional): The temperature of the model's output. Default is 1. - top_p (float, optional): The top-p value for nucleus sampling. Default is 1. - stream (bool, optional): If True, the model will return intermediate results. Default is False. - stop (str, optional): The stop sequence at which the model should stop generating further tokens. Default is None. - max_tokens (int, optional): The maximum number of tokens in the output. Default is 1024. - presence_penalty (float, optional): The penalty for new token presence. Default is 0. - frequency_penalty (float, optional): The penalty for token frequency. Default is 0. - logit_bias (map, optional): The bias for the logits before sampling. Default is None. - user (str, optional): The user ID. Default is ''. - functions (list, optional): The list of functions. Default is None. - function_call (str, optional): The function call. Default is None. - function_dict (dict, optional): Dict of functions. Default is None. - history_length (int, optional): Length of history. Default is 5. - chats (dict, optional): Chats dictionary, contains all chat. Default is None. - current_chat (str, optional): Default chat will be used. Default is None. - prompt_method (bool, optional): prompt method. Use messages if False, otherwise - prompt. Default if False. - logger (logging.Logger, optional): default logger. Default is None. - statistic (GPTStatistics, optional): statistics logger. If none, will be initialized with zeros. - system_settings (str, optional): general instructions for chat. Default is None. - echo # TODO - best_of # TODO - suffix # TODO - """ - - def __init__( - # pylint: disable=too-many-locals - self, - auth_token: str, - organization: str, - model: str = COMPLETIONS[0], - choices: int = 1, - temperature: float = 1, - top_p: float = 1, - stream: bool = False, - stop: str = None, - max_tokens: int = 1024, - presence_penalty: float = 0, - frequency_penalty: float = 0, - logit_bias: map = None, - user: str = "", - functions: list = None, - function_call: str = None, - history_length: int = 5, - chats: dict = None, - current_chat: str = None, - prompt_method: bool = False, - logger: logging.Logger = None, - statistics: GPTStatistics = GPTStatistics(), - system_settings: str = None, - function_dict: dict = None, - ): - """ - General init - - :param auth_token (str): Authentication bearer token. Required. - :param organization (str): Organization uses auth toke. Required. - :param model: The name of the model. - :param choices: The number of response options. Default is 1. - :param temperature: The temperature of the model's output. Default is 1. - :param top_p: The top-p value for nucleus sampling. Default is 1. - :param stream: If True, the model will return intermediate results. Default is False. - :param stop: The stop sequence at which the model should stop generating further tokens. Default is None. - :param max_tokens: The maximum number of tokens in the output. Default is 1024. - :param presence_penalty: The penalty for new token presence. Default is 0. - :param frequency_penalty: The penalty for token frequency. Default is 0. - :param logit_bias: The bias for the logits before sampling. Default is None. - :param user: The user ID. Default is ''. - :param functions: The list of functions. Default is None. - :param function_call: The function call. Default is None. - :param function_dict: Dict of functions. Default is None. - :param history_length: Length of history. Default is 5. - :param chats: Chats dictionary, contains all chat. Default is None. - :param current_chat: Default chat will be used. Default is None. - :param prompt_method: prompt method. Use messages if False, otherwise - prompt. Default if False. - :param logger: default logger. Default is None. - :param statistics: statistics logger. If none, will be initialized with zeros. - :param system_settings: general system instructions for bot. Default is ''. - """ - self.___model = model - self.___choices = choices - self.___temperature = temperature - self.___top_p = top_p - self.___stream = stream - self.___stop = stop - self.___max_tokens = max_tokens - self.___presence_penalty = presence_penalty - self.___frequency_penalty = frequency_penalty - self.___logit_bias = logit_bias - self.___user = user - self.___functions = functions - self.___function_call = function_call - self.___function_dict = function_dict - self.___history_length = history_length - self.___chats = chats if chats else {} - self.___current_chat = current_chat - self.___prompt_method = prompt_method - self.___set_auth(auth_token, organization) - self.___logger = logger - self.___statistics = statistics - self.___system_settings = system_settings if system_settings else "" - - @staticmethod - def ___set_auth(token, organization): - """ - Method to set auth bearer. - - :param token: authentication bearer token. - :param organization: organization, which drives the chat. - """ - openai.api_key = token - openai.organization = organization - - @property - def model(self): - """ - Getter for model. - - :return: The name of the model. - """ - return self.___model - - @model.setter - def model(self, value): - """ - Setter for model. - - :param value: The new name of the model. - """ - self.___model = value - - @property - def choices(self): - """ - Getter for choices. - - :return: The number of response options. - """ - return self.___choices - - @choices.setter - def choices(self, value): - """ - Setter for choices. - - :param value: The new number of response options. - """ - self.___choices = value - - @property - def temperature(self): - """ - Getter for temperature. - - :return: The temperature of the model's output. - """ - return self.___temperature - - @temperature.setter - def temperature(self, value): - """ - Setter for temperature. - - :param value: The new temperature of the model's output. - """ - self.___temperature = value - - @property - def top_p(self): - """ - Getter for top_p. - - :return: The top-p value for nucleus sampling. - """ - return self.___top_p - - @top_p.setter - def top_p(self, value): - """ - Setter for top_p. - - :param value: The new top-p value for nucleus sampling. - """ - self.___top_p = value - - @property - def stream(self): - """ - Getter for stream. - - :return: If True, the model will return intermediate results. - """ - return self.___stream - - @stream.setter - def stream(self, value): - """ - Setter for stream. - - :param value: The new value for stream. - """ - self.___stream = value - - @property - def stop(self): - """ - Getter for stop. - - :return: The stop sequence at which the model should stop generating further tokens. - """ - return self.___stop - - @stop.setter - def stop(self, value): - """ - Setter for stop. - - :param value: The new stop sequence. - """ - self.___stop = value - - @property - def max_tokens(self): - """ - Getter for max_tokens. - - :return: The maximum number of tokens in the output. - """ - return self.___max_tokens - - @max_tokens.setter - def max_tokens(self, value): - """ - Setter for max_tokens. - - :param value: The new maximum number of tokens in the output. - """ - self.___max_tokens = value - - @property - def presence_penalty(self): - """ - Getter for presence_penalty. - - :return: The penalty for new token presence. - """ - return self.___presence_penalty - - @presence_penalty.setter - def presence_penalty(self, value): - """ - Setter for presence_penalty. - - :param value: The new penalty for new token presence. - """ - self.___presence_penalty = value - - @property - def frequency_penalty(self): - """ - Getter for frequency_penalty. - - :return: The penalty for token frequency. - """ - return self.___frequency_penalty - - @frequency_penalty.setter - def frequency_penalty(self, value): - """ - Setter for frequency_penalty. - - :param value: The new penalty for token frequency. - """ - self.___frequency_penalty = value - - @property - def logit_bias(self): - """ - Getter for logit_bias. - - :return: The bias for the logits before sampling. - """ - return self.___logit_bias - - @logit_bias.setter - def logit_bias(self, value): - """ - Setter for logit_bias. - - :param value: The new bias for the logits before sampling. - """ - self.___logit_bias = value - - @property - def user(self): - """ - Getter for user. - - :return: The user ID. - """ - return self.___user - - @user.setter - def user(self, value): - """ - Setter for user. - - :param value: The new user ID. - """ - self.___user = value - - @property - def functions(self): - """ - Getter for functions. - - :return: The list of functions. - """ - return self.___functions - - @functions.setter - def functions(self, value): - """ - Setter for functions. - - :param value: The new list of functions. - """ - self.___functions = value - - @property - def function_call(self): - """ - Getter for function_call. - - :return: The function call. - """ - return self.___function_call - - @function_call.setter - def function_call(self, value): - """ - Setter for function_call. - - :param value: The new function call. - """ - self.___function_call = value - - @property - def function_dict(self): - """ - Getter for function_dict. - - :return: The function dict. - """ - return self.___function_dict - - @function_dict.setter - def function_dict(self, value): - """ - Setter for function_dict. - - :param value: The new function dict. - """ - self.___function_dict = value - - @property - def history_length(self): - """ - Getter for history_length. - - :return: The history length. - """ - return self.___history_length - - @history_length.setter - def history_length(self, value): - """ - Setter for history_length. - - :param value: The new history length. - """ - self.___history_length = value - - @property - def chats(self): - """ - Getter for chats. - - :return: The chats. - """ - return self.___chats - - @chats.setter - def chats(self, value): - """ - Setter for chats. - - :param value: The new chats. - """ - self.___chats = value - - @property - def current_chat(self): - """ - Getter for current_chat. - - :return: The current chat. - """ - return self.___current_chat - - @current_chat.setter - def current_chat(self, value): - """ - Setter for current_chat. - - :param value: The current chat. - """ - self.___current_chat = value - - @property - def prompt_method(self): - """ - Getter for prompt_method. - - :return: The prompt method. - """ - return self.___prompt_method - - @prompt_method.setter - def prompt_method(self, value): - """ - Setter for prompt_method. - - :param value: The prompt method. - """ - self.___prompt_method = value - - @property - def system_settings(self): - """ - Getter for system_settings. - - :return: The system settings method. - """ - return self.___system_settings - - @system_settings.setter - def system_settings(self, value): - """ - Setter for system_settings. - - :param value: The system settings. - """ - self.___system_settings = value - - @property - def logger(self): - """ - Getter for logger. - - :return: The logger object. - """ - return self.___logger - - @logger.setter - def logger(self, value): - """ - Setter for logger. - - :param value: The new logger object. - """ - self.___logger = value - - async def process_chat(self, prompt, default_choice=0, chat_name=None): - """ - Creates a new chat completion for the provided messages and parameters. - - :param prompt: The prompt to pass to the model. - :param default_choice: Default number of choice to monitor for stream end. By default, is None. - :param chat_name: Chat name for function tracking. Should be handled by caller. By default, is None. - - :return: Returns answers by chunk if 'stream' is false True, otherwise return complete answer. - """ - # pylint: disable=too-many-branches - # Prepare parameters - params = { - "model": self.model, - "max_tokens": self.max_tokens, - "n": self.choices, - "temperature": self.temperature, - "top_p": self.top_p, - "presence_penalty": self.presence_penalty, - "frequency_penalty": self.frequency_penalty, - "user": self.user, - "functions": self.functions, - "function_call": self.function_call, - "stream": self.stream, - } - - # Remove None values - params = {k: v for k, v in params.items() if v is not None} - - # Add 'prompt' or 'messages' parameter - if self.prompt_method: - params["prompt"] = prompt - else: - params["messages"] = prompt - - # Get response - func_response = None - func_call = dict() - if self.stream: - try: - async for chunk in await openai.ChatCompletion.acreate(**params): - if "function_call" in chunk["choices"][default_choice]["delta"]: - raw_call = chunk["choices"][default_choice]["delta"]["function_call"] - for key, value in raw_call.items(): - if key in func_call and isinstance(value, str): - func_call[key] += value - elif key in func_call and isinstance(func_call[key], dict) and isinstance(value, dict): - func_call[key].update(value) - else: - func_call[key] = value - if chunk["choices"][default_choice]["finish_reason"] is not None: - if chunk["choices"][default_choice]["finish_reason"] == 'function_call': - func_response = await self.process_function(function_call=func_call) - break - if "content" in chunk["choices"][default_choice]["delta"]: - if chunk["choices"][default_choice]["delta"]["content"]: - yield chunk - else: - continue - else: - continue - except GeneratorExit: - pass - try: - if func_response: - # Save to history - if chat_name: - self.chats[chat_name].append(func_response) - # Add new prompt - if self.prompt_method: - params.pop("prompt", None) - params["messages"] = list() - params["messages"].append(func_response) - else: - params["messages"].append(func_response) - async for chunk in await openai.ChatCompletion.acreate(**params): - yield chunk - if chunk["choices"][default_choice]["finish_reason"] is not None: - break - except GeneratorExit: - pass - else: - response = await openai.ChatCompletion.acreate(**params) - if response["choices"][default_choice]["finish_reason"] == 'function_call': - func_response = await self.process_function( - function_call=response["choices"][default_choice]["message"]["function_call"] - ) - try: - if func_response: - # Save to history - if chat_name: - self.chats[chat_name].append(func_response) - # Add new prompt - if self.prompt_method: - params.pop("prompt", None) - params["messages"] = list() - params["messages"].append(func_response) - else: - params["messages"].append(func_response) - response = await openai.ChatCompletion.acreate(**params) - yield response - else: - yield response - except GeneratorExit: - pass - - async def __handle_chat_name(self, chat_name, prompt): - """ - Handles the chat name. If chat_name is None, sets it to the first 40 characters of the prompt. - If chat_name is not present in self.chats, adds it. - - :param chat_name: Name of the chat. - :param prompt: Message from the user. - :return: Processed chat name. - """ - if chat_name is None: - chat_name = prompt[:40] - self.current_chat = chat_name - if chat_name not in self.chats: - self.chats[chat_name] = [] - return chat_name - - async def chat(self, prompt, chat_name=None, default_choice=0): - """ - Wrapper for the process_chat function. Adds new messages to the chat and calls process_chat. - - :param prompt: Message from the user. - :param chat_name: Name of the chat. If None, uses self.current_chat. - :param default_choice: Index of the model's response choice. - """ - # pylint: disable=too-many-branches - # Call process_chat - full_prompt = "" - if self.prompt_method: - try: - async for response in self.process_chat(prompt=prompt, default_choice=default_choice): - if isinstance(response, dict): - finish_reason = response["choices"][default_choice].get("finish_reason", "") - yield response - if self.stream: - if "content" in response["choices"][default_choice]["delta"].keys(): - full_prompt += response["choices"][default_choice]["delta"]["content"] - else: - full_prompt += response["choices"][default_choice]["message"]["content"] - if finish_reason is not None: - yield "" - else: - break - except GeneratorExit: - pass - else: - # Set chat_name - chat_name = chat_name if chat_name is not None else self.current_chat - chat_name = await self.__handle_chat_name(chat_name, prompt) - # Add new message to chat - self.chats[chat_name].append({"role": "user", "content": prompt}) - # Get last 'history_length' messages - messages = self.chats[chat_name][-self.history_length:] - messages.insert(0, {"role": "system", "content": self.system_settings}) - - try: - async for response in self.process_chat( - prompt=messages, default_choice=default_choice, chat_name=chat_name - ): - if isinstance(response, dict): - finish_reason = response["choices"][default_choice].get("finish_reason", "") - yield response - if self.stream: - if "content" in response["choices"][default_choice]["delta"].keys(): - full_prompt += response["choices"][default_choice]["delta"]["content"] - else: - full_prompt += response["choices"][default_choice]["message"]["content"] - if finish_reason is not None: - yield "" - else: - break - except GeneratorExit: - pass - - # Add last response to chat - self.chats[chat_name].append({"role": "assistant", "content": full_prompt}) - - async def str_chat(self, prompt, chat_name=None, default_choice=0): - """ - Wrapper for the chat function. Returns only the content of the message. - - :param prompt: Message from the user. - :param chat_name: Name of the chat. If None, uses self.current_chat. - :param default_choice: Index of the model's response choice. - - :return: Content of the message. - """ - try: - async for response in self.chat(prompt, chat_name, default_choice): - if isinstance(response, dict): - if self.stream: - if "content" in response["choices"][default_choice]["delta"].keys(): - yield response["choices"][default_choice]["delta"]["content"] - else: - yield "" - else: - yield response["choices"][default_choice]["message"]["content"] - else: - break - except GeneratorExit: - pass - - async def transcript(self, file, prompt=None, language="en", response_format="text"): - """ - Wrapper for the transcribe function. Returns only the content of the message. - - :param file: Path with filename to transcript. - :param prompt: Previous prompt. Default is None. - :param language: Language on which audio is. Default is 'en'. - :param response_format: default response format, by default is 'text'. - Possible values are: json, text, srt, verbose_json, or vtt. - - - :return: transcription (text, json, srt, verbose_json or vtt) - """ - kwargs = {} - if prompt is not None: - kwargs["prompt"] = prompt - return await openai.Audio.atranscribe( - model=TRANSCRIPTIONS[0], - file=file, - language=language, - response_format=response_format, - temperature=self.temperature, - **kwargs, - ) - - async def translate(self, file, prompt=None, response_format="text"): - """ - Wrapper for the translate function. Returns only the content of the message. - - :param file: Path with filename to transcript. - :param prompt: Previous prompt. Default is None. - :param response_format: default response format, by default is 'text'. - Possible values are: json, text, srt, verbose_json, or vtt. - - - :return: transcription (text, json, srt, verbose_json or vtt) - """ - kwargs = {} - if prompt is not None: - kwargs["prompt"] = prompt - return await openai.Audio.atranslate( - model=TRANSLATIONS[0], file=file, response_format=response_format, temperature=self.temperature, **kwargs - ) - - async def process_function(self, function_call): - """ - Process function requested by ChatGPT. - - :param function_call: Function name and arguments. In JSON format. - - :return: transcription (text, json, srt, verbose_json or vtt) - """ - function_name = function_call["name"] - function_to_call = self.function_dict[function_name] - function_response = function_to_call(**json.loads(function_call["arguments"])) - return {"role": "function", "name": function_name, "content": function_response} diff --git a/openai_engine/dalle.py b/openai_engine/dalle.py deleted file mode 100644 index 4f75de1..0000000 --- a/openai_engine/dalle.py +++ /dev/null @@ -1,364 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Filename: dalle.py -Author: Iliya Vereshchagin -Copyright (c) 2023. All rights reserved. - -Created: 25.08.2023 -Last Modified: 26.08.2023 - -Description: -This file contains implementation for DALL-E -""" -import logging -import os -import tempfile -import uuid -from io import BytesIO - -import aiohttp -import asyncio -import openai -from PIL import Image - - -class DALLE: - """ - The ChatGPT class is for managing an instance of the ChatGPT model. - - Parameters: - auth_token (str): Authentication bearer token. Required. - organization (str): Organization uses auth toke. Required. - default_count (int): Default count of images to produce. Default is 1. - default_size (str): Default dimensions for output images. Default is "512x512". "256x256" and "1024x1024" as option. - default_file_format (str): Default file format. Optional. Default is 'PNG'. - user (str, optional): The user ID. Default is ''. - logger (logging.Logger, optional): default logger. Default is None. - """ - - def __init__( - self, - auth_token: str, - organization: str, - default_count: int = 1, - default_size: str = "512x512", - default_file_format: str = "PNG", - user: str = "", - logger: logging.Logger = None, - ): - """ - General init - - :param auth_token (str): Authentication bearer token. Required. - :param organization (str): Organization uses auth toke. Required. - :param default_count: Default count of images to produce. Optional. Default is 1. - :param default_size: Default dimensions for output images. Optional. Default is "512x512". - :param default_file_format: Default file format. Optional. Optional. Default is 'PNG'. - :param user: The user ID. Optional. Default is ''. - :param logger: default logger. Optional. Default is None. - """ - self.___default_count = default_count - self.___default_size = default_size - self.___default_file_format = default_file_format - self.___user = user - self.___set_auth(auth_token, organization) - self.___logger = logger - - @staticmethod - def ___set_auth(token, organization): - """ - Method to set auth bearer. - - :param token: authentication bearer token. - :param organization: organization, which drives the chat. - """ - openai.api_key = token - openai.organization = organization - - @property - def default_count(self): - """ - Getter for default_count. - - :return: Returns default_count value. - """ - return self.___default_count - - @default_count.setter - def default_count(self, value): - """ - Setter for default_count. - - :param value: The new value of default_count. - """ - self.___default_count = value - - @property - def default_size(self): - """ - Getter for default_size. - - :return: Returns default_size value. - """ - return self.___default_size - - @default_size.setter - def default_size(self, value): - """ - Setter for default_size. - - :param value: The new value of default_size. - """ - self.___default_size = value - - @property - def default_file_format(self): - """ - Getter for default_file_format. - - :return: Returns default_file_format value. - """ - return self.___default_file_format - - @default_file_format.setter - def default_file_format(self, value): - """ - Setter for default_size. - - :param value: The new value of default_file_format. - """ - self.___default_file_format = value - - @property - def user(self): - """ - Getter for user. - - :return: The user. - """ - return self.___user - - @user.setter - def user(self, value): - """ - Setter for user. - - :param value: The user. - """ - self.___user = value - - @property - def logger(self): - """ - Getter for logger. - - :return: The logger object. - """ - return self.___logger - - @logger.setter - def logger(self, value): - """ - Setter for logger. - - :param value: The new logger object. - """ - self.___logger = value - - async def create_image(self, prompt): - """ - Creates an image using DALL-E Image API. - - :param prompt: The prompt to be used for image creation. - - :return: A PIL.Image object created from the image data received from the API. - """ - response = await openai.Image.acreate( - prompt=prompt, n=self.default_count, size=self.default_size, user=self.user - ) - return response["data"] - - def screate_image(self, prompt): # FIXME let it be till experiment, then remove - """ - Creates an image using DALL-E Image API. - - :param prompt: The prompt to be used for image creation. - - :return: A PIL.Image object created from the image data received from the API. - """ - response = openai.Image.create( - prompt=prompt, n=self.default_count, size=self.default_size, user=self.user - ) - return response["data"] - - async def create_image_url(self, prompt): - """ - Creates an image using DALL-E Image API, returns list of URLs with images. - - :param prompt: The prompt to be used for image creation. - - :return: list of URLs - """ - image_urls = list() - for items in await self.create_image(prompt): - image_urls.append(items['url']) - return image_urls - - def screate_image_url(self, prompt): # FIXME let it be till experiment, then remove - """ - Creates an image using DALL-E Image API, returns list of URLs with images. (SYNC) - - :param prompt: The prompt to be used for image creation. - - :return: list of URLs - """ - image_urls = list() - for items in self.screate_image(prompt): - image_urls.append(items['url']) - return image_urls - - @staticmethod - async def convert_image_from_url_to_bytes(url): - """ - Converts image from URL to bytes format. - - :param url: URL of image. - - :return: URL - """ - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - image_data = await resp.read() - return Image.open(BytesIO(image_data)) - - async def create_image_data(self, prompt): - """ - Creates an image using DALL-E Image API, returns list of images (bytes format). - - :param prompt: The prompt to be used for image creation. - - :return: list of images (bytes format). - """ - tasks = [] - async for items in await self.create_image(prompt): - task = asyncio.ensure_future(self.convert_image_from_url_to_bytes(items['url'])) - tasks.append(task) - image_data = await asyncio.gather(*tasks) - return image_data - - @staticmethod - def show_image(image): - """ - Shows image interactively. - - :param image: image object. - """ - image.show() - - def save_image(self, image, filename=None, file_format=None): - """Saves an image to a file. - - :param image: A PIL.Image object to be saved. - :param filename: The name of the file where the image will be saved. - If None, a random filename in the system's temporary directory will be used. - :param file_format: The format of the file. This is optional and defaults to 'PNG'. - - :return: The full path of the file where the image was saved, or None if the image could not be saved. - """ - if file_format is None: - file_format = self.default_file_format - if filename is None: - filename = os.path.join(tempfile.gettempdir(), f"{uuid.uuid4()}.{file_format.lower()}") - try: - image.save(filename, format=format) - except Exception as error: - print(f"Can't save image: {error}") - return None - return filename - - async def create_variation_from_file(self, file): - """ - Creates an image variation from file using DALL-E Image API. - - :param file: file of the image (bytes). - - :return: A PIL.Image object created from the image data received from the API. - """ - response = await openai.Image.acreate_variation( - image=file, n=self.default_count, size=self.default_size, user=self.user - ) - image_url = response["data"][0]["url"] - async with aiohttp.ClientSession() as session: - async with session.get(image_url) as resp: - image_data = await resp.read() - return Image.open(BytesIO(image_data)) - - async def create_variation_from_url(self, url): - """ - Creates an image variation from URL using DALL-E Image API. - - :param url: URL of the image. - - :return: A PIL.Image object created from the image data received from the API. - """ - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - image_data = await resp.read() - - response = await openai.Image.acreate_variation( - image=BytesIO(image_data), n=self.default_count, size=self.default_size, user=self.user - ) - image_url = response["data"][0]["url"] - async with aiohttp.ClientSession() as session: - async with session.get(image_url) as resp: - variation_image_data = await resp.read() - return Image.open(BytesIO(variation_image_data)) - - async def edit_image_from_file(self, file, prompt, mask=None): - """ - Edits an image using OpenAI's Image API. - - :param file: A file-like object opened in binary mode containing the image to be edited. - :param prompt: The prompt to be used for image editing. - :param mask: An optional file-like object opened in binary mode containing the mask image. - If provided, the mask will be applied to the image. - :return: A PIL.Image object created from the image data received from the API. - """ - response = await openai.Image.acreate_edit( - image=file, prompt=prompt, mask=mask, n=self.default_count, size=self.default_size, user=self.user - ) - image_url = response["data"][0]["url"] - async with aiohttp.ClientSession() as session: - async with session.get(image_url) as resp: - image_data = await resp.read() - return Image.open(BytesIO(image_data)) - - async def edit_image_from_url(self, url, prompt, mask_url=None): - """ - Edits an image using OpenAI's Image API. - - :param url: A url of image to be edited. - :param prompt: The prompt to be used for image editing. - :param mask_url: Url containing mask image. If provided, the mask will be applied to the image. - :return: A PIL.Image object created from the image data received from the API. - """ - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - image_data = await resp.read() - - async with aiohttp.ClientSession() as session: - async with session.get(mask_url) as resp: - mask_data = await resp.read() - response = await openai.Image.acreate_edit( - image=BytesIO(image_data), - prompt=prompt, - mask=BytesIO(mask_data), - n=self.default_count, - size=self.default_size, - user=self.user, - ) - image_url = response["data"][0]["url"] - async with aiohttp.ClientSession() as session: - async with session.get(image_url) as resp: - image_data = await resp.read() - return Image.open(BytesIO(image_data)) diff --git a/openai_engine/models.py b/openai_engine/models.py deleted file mode 100644 index 371ffc7..0000000 --- a/openai_engine/models.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Filename: models.py -Author: Iliya Vereshchagin -Copyright (c) 2023. All rights reserved. - -Created: 25.08.2023 -Last Modified: 25.08.2023 - -Description: -This file contains OpenAI constants -""" - -COMPLETIONS = ( - "gpt-4", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-16k-0613", -) -TRANSCRIPTIONS = ("whisper-1",) -TRANSLATIONS = ("whisper-1",) -FINE_TUNES = ("davinci", "curie", "babbage", "ada") -EMBEDDINGS = ( - "text-embedding-ada-002", - "text-similarity-*-001", - "text-search-*-*-001", - "code-search-*-*-001" -) -MODERATIONS = ("text-moderation-stable", "text-moderation-latest") diff --git a/requirements.txt b/requirements.txt index 373f295..8149784 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,3 @@ -aiohttp==3.8.5 -# OpenAI -openai==0.28.0 # TTS gtts==2.3.2 pyttsx4==3.0.15 @@ -18,5 +15,5 @@ soundfile==0.12.1 numpy==1.25.2 # Image pillow==10.0.0 -# Artuicles +# Articles readability==0.3.1 From d76eccb702c3ad9065380f6e0a636166105da2cf Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 12 Sep 2023 23:26:59 +0200 Subject: [PATCH 31/32] v0.1 - add templates --- .github/workflows/linters.yml | 4 +- CODE_OF_CONDUCT.md | 128 ++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 54 ++++++++++++++ SECURITY.md | 17 +++++ 4 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index dc31c7f..340c366 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -13,14 +13,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11.4"] + python-version: ["3.9", "3.10", "3.11.5"] steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..18c9147 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..350de5b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,54 @@ +# Contributing to Transcriptase +We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: + +- Reporting a bug +- Discussing the current state of the code +- Submitting a fix +- Proposing new features +- Becoming a maintainer + +## We Develop with Github +We use github to host code, to track issues and feature requests, as well as accept pull requests. + +## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests +Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: + +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests. +3. Always update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints: pylint, mypy and black code formatter. +6. Issue that pull request! + +## Any contributions you make will be under the MIT Software License +In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. + +## Report bugs using Github's [issues](https://github.com/briandk/transcriptase-atom/issues) +We use GitHub issues to track public bugs. Report a bug by [opening a new issue](); it's that easy! + +## Write bug reports with detail, background, and sample code +[This is an example](http://stackoverflow.com/q/12488905/180626) of a bug report I wrote, and I think it's not a bad model. Here's [another example from Craig Hockenberry](http://www.openradar.me/11905408), an app developer whom I greatly respect. + +**Great Bug Reports** tend to have: + +- A quick summary and/or background +- Steps to reproduce + - Be specific! + - Give sample code if you can. [My stackoverflow question](http://stackoverflow.com/q/12488905/180626) includes sample code that *anyone* with a base R setup can run to reproduce what I was seeing +- What you expected would happen +- What actually happens +- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) + +People *love* thorough bug reports. I'm not even kidding. + +## Use a Consistent Coding Style +I'm again borrowing these from [Facebook's Guidelines](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md) + +* 2 spaces for indentation rather than tabs +* You can try running `npm run lint` for style unification + +## License +By contributing, you agree that your contributions will be licensed under its MIT License. + +## References +This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..87cd27b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 0.0.1 | :white_check_mark: | + + +## Reporting a Vulnerability + +In case of issues, please report to: https://github.com/wwakabobik/leonardo_api/issues + +Some ignored/declined issues will be described bellow, please check them prior to create new issues. From 88f7f4d7bac36019d1e5851bd3cb31ce2b917b6f Mon Sep 17 00:00:00 2001 From: wwakabobik Date: Tue, 12 Sep 2023 23:33:25 +0200 Subject: [PATCH 32/32] v0.1 - add templates --- utils/article_extractor.py | 9 +++++++++ utils/audio_recorder.py | 1 + 2 files changed, 10 insertions(+) diff --git a/utils/article_extractor.py b/utils/article_extractor.py index f7915eb..98dc334 100644 --- a/utils/article_extractor.py +++ b/utils/article_extractor.py @@ -18,6 +18,15 @@ def get_content(url): + """ + This function extracts content from internet page. + Args: + url: URL of internet page. + + Returns: + Content of internet page. + + """ session = requests.Session() response = session.get(url) doc = Document(response.text) diff --git a/utils/audio_recorder.py b/utils/audio_recorder.py index b82da80..ee50285 100644 --- a/utils/audio_recorder.py +++ b/utils/audio_recorder.py @@ -246,6 +246,7 @@ def rms(self, frame): :return: The RMS value of the audio frame. """ count = len(frame) / self.___sample_width + # pylint: disable=C0209 f_format = "%dh" % count shorts = struct.unpack(f_format, frame)