diff --git a/.env_example b/.env_example index 1e7b65b..aaa3c46 100644 --- a/.env_example +++ b/.env_example @@ -9,6 +9,7 @@ LNBITS_HOST = "https://lnbits.com" OPENAI_API_KEY = "" # Enter your OpenAI API Key to use DVMs with OpenAI services LIBRE_TRANSLATE_ENDPOINT = "" # Url to LibreTranslate Endpoint e.g. https://libretranslate.com LIBRE_TRANSLATE_API_KEY = "" # API Key, if required (You can host your own instance where you don't need it) +REPLICATE_API_TOKEN = "" #API Key to run models on replicate.com # We will automatically create dtags and private keys based on the identifier variable in main. # If your DVM already has a dtag and private key you can replace it here before publishing the DTAG to not create a new one. diff --git a/.gitignore b/.gitignore index ff37a51..4f1d54f 100644 --- a/.gitignore +++ b/.gitignore @@ -167,3 +167,5 @@ app_deploy.py app.py app_deploy.py db/Cashu/wallet.sqlite3 +.idea/misc.xml +.idea/misc.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 8b60158..3ce312c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/main.py b/main.py index ae440f1..f5adac5 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,7 @@ import tasks.textextraction_pdf as textextraction_pdf import tasks.textextraction_google as textextraction_google import tasks.translation_google as translation_google import tasks.translation_libretranslate as translation_libretranslate +from tasks import imagegeneration_replicate from utils.admin_utils import AdminConfig from utils.backend_utils import keep_alive @@ -75,6 +76,12 @@ def playground(): bot_config.SUPPORTED_DVMS.append(dalle) dalle.run() + if os.getenv("REPLICATE_API_TOKEN") is not None and os.getenv("REPLICATE_API_TOKEN") != "": + sdxlreplicate = imagegeneration_replicate.build_example("Stable Diffusion XL", "replicate_sdxl", admin_config) + bot_config.SUPPORTED_DVMS.append(sdxlreplicate) + sdxlreplicate.run() + + #Let's define a function so we can add external DVMs to our bot, we will instanciate it afterwards diff --git a/tasks/imagegeneration_openai_dalle.py b/tasks/imagegeneration_openai_dalle.py index 6017060..5b02fbc 100644 --- a/tasks/imagegeneration_openai_dalle.py +++ b/tasks/imagegeneration_openai_dalle.py @@ -18,12 +18,10 @@ from utils.output_utils import upload_media_to_hoster from utils.zap_utils import get_price_per_sat """ -This File contains a Module to transform Text input on NOVA-Server and receive results back. +This File contains a Module to transform Text input on OpenAI's servers with DALLE-3 and receive results back. Accepted Inputs: Prompt (text) Outputs: An url to an Image -Params: -model # models: juggernaut, dynavision, colossusProject, newreality, unstable - -lora # loras (weights on top of models) voxel, """ diff --git a/tasks/imagegeneration_replicate.py b/tasks/imagegeneration_replicate.py new file mode 100644 index 0000000..ca47178 --- /dev/null +++ b/tasks/imagegeneration_replicate.py @@ -0,0 +1,170 @@ +import json +import os +from io import BytesIO +from pathlib import Path + +import dotenv +import requests +from PIL import Image + +from interfaces.dvmtaskinterface import DVMTaskInterface +from utils.admin_utils import AdminConfig +from utils.backend_utils import keep_alive +from utils.definitions import EventDefinitions +from utils.dvmconfig import DVMConfig +from utils.nip89_utils import NIP89Config, check_and_set_d_tag +from utils.nostr_utils import check_and_set_private_key +from utils.output_utils import upload_media_to_hoster +from utils.zap_utils import get_price_per_sat + +""" +This File contains a Module to transform Text input on NOVA-Server and receive results back. + +Accepted Inputs: Prompt (text) +Outputs: An url to an Image +Params: +""" + + +class ImageGenerationReplicate(DVMTaskInterface): + KIND: int = EventDefinitions.KIND_NIP90_GENERATE_IMAGE + TASK: str = "text-to-image" + FIX_COST: float = 120 + + def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, + admin_config: AdminConfig = None, options=None): + super().__init__(name, dvm_config, nip89config, admin_config, options) + + def is_input_supported(self, tags): + for tag in tags: + if tag.as_vec()[0] == 'i': + input_value = tag.as_vec()[1] + input_type = tag.as_vec()[2] + if input_type != "text": + return False + + elif tag.as_vec()[0] == 'output': + output = tag.as_vec()[1] + if (output == "" or + not (output == "image/png" or "image/jpg" + or output == "image/png;format=url" or output == "image/jpg;format=url")): + print("Output format not supported, skipping..") + return False + + return True + + def create_request_from_nostr_event(self, event, client=None, dvm_config=None): + request_form = {"jobID": event.id().to_hex() + "_" + self.NAME.replace(" ", "")} + prompt = "" + width = "1024" + height = "1024" + + + for tag in event.tags(): + if tag.as_vec()[0] == 'i': + input_type = tag.as_vec()[2] + if input_type == "text": + prompt = tag.as_vec()[1] + + elif tag.as_vec()[0] == 'param': + print("Param: " + tag.as_vec()[1] + ": " + tag.as_vec()[2]) + if tag.as_vec()[1] == "size": + if len(tag.as_vec()) > 3: + width = (tag.as_vec()[2]) + height = (tag.as_vec()[3]) + elif len(tag.as_vec()) == 3: + split = tag.as_vec()[2].split("x") + if len(split) > 1: + width = split[0] + height = split[1] + elif tag.as_vec()[1] == "model": + model = tag.as_vec()[2] + elif tag.as_vec()[1] == "quality": + quality = tag.as_vec()[2] + + options = { + "prompt": prompt, + "size": width + "x" + height, + "number": 1 + } + request_form['options'] = json.dumps(options) + + return request_form + + def process(self, request_form): + try: + options = DVMTaskInterface.set_options(request_form) + + import replicate + width = int(options["size"].split("x")[0]) + height = int(options["size"].split("x")[1]) + output = replicate.run( + "stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b", + input={"prompt": options["prompt"], + "width": width, + "height": height, + "disable_safety_checker": True} + ) + print(output[0]) + response = requests.get(output[0]) + image = Image.open(BytesIO(response.content)).convert("RGB") + image.save("./outputs/image.jpg") + result = upload_media_to_hoster("./outputs/image.jpg") + return result + + except Exception as e: + print("Error in Module") + raise Exception(e) + +# We build an example here that we can call by either calling this file directly from the main directory, +# or by adding it to our playground. You can call the example and adjust it to your needs or redefine it in the +# playground or elsewhere +def build_example(name, identifier, admin_config): + dvm_config = DVMConfig() + dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier) + dvm_config.LNBITS_INVOICE_KEY = os.getenv("LNBITS_INVOICE_KEY") + dvm_config.LNBITS_URL = os.getenv("LNBITS_HOST") + profit_in_sats = 10 + dvm_config.FIX_COST = int(((4.0 / (get_price_per_sat("USD") * 100)) + profit_in_sats)) + + nip90params = { + "size": { + "required": False, + "values": ["1024:1024", "1024x1792", "1792x1024"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I use Replicate to run StableDiffusion XL", + "encryptionSupported": True, + "cashuAccepted": True, + "nip90Params": nip90params + } + + + nip89config = NIP89Config() + nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + # We add an optional AdminConfig for this one, and tell the dvm to rebroadcast its NIP89 + return ImageGenerationReplicate(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config) + + +if __name__ == '__main__': + env_path = Path('.env') + if env_path.is_file(): + print(f'loading environment from {env_path.resolve()}') + dotenv.load_dotenv(env_path, verbose=True, override=True) + else: + raise FileNotFoundError(f'.env file not found at {env_path} ') + + admin_config = AdminConfig() + admin_config.REBROADCAST_NIP89 = False + admin_config.UPDATE_PROFILE = False + admin_config.LUD16 = "" + + dvm = build_example("Stable Diffusion XL", "replicate_sdxl", admin_config) + dvm.run() + + keep_alive() diff --git a/tasks/textextraction_google.py b/tasks/textextraction_google.py index c48d786..fe47b86 100644 --- a/tasks/textextraction_google.py +++ b/tasks/textextraction_google.py @@ -6,7 +6,6 @@ from pathlib import Path import dotenv -from backends.nova_server import check_nova_server_status, send_request_to_nova_server, send_file_to_nova_server from interfaces.dvmtaskinterface import DVMTaskInterface from utils.admin_utils import AdminConfig from utils.backend_utils import keep_alive