diff --git a/main.py b/main.py index 984d6ff..7264809 100644 --- a/main.py +++ b/main.py @@ -1,21 +1,33 @@ +import json import os -import signal -import time from pathlib import Path import dotenv +from nostr_sdk import PublicKey from bot.bot import Bot -from playground import build_pdf_extractor, build_googletranslator, build_unstable_diffusion, build_sketcher, \ - build_dalle, \ - build_whisperx, build_libretranslator, build_external_dvm, build_media_converter, build_inactive_follows_finder, \ - build_image_converter, build_googletranscribe +from interfaces.dvmtaskinterface import DVMTaskInterface + +import tasks.convert_media as convert_media +import tasks.discovery_inactive_follows as discovery_inactive_follows +import tasks.imagegeneration_openai_dalle as imagegeneration_openai_dalle +import tasks.imagegeneration_sdxl as imagegeneration_sdxl +import tasks.imagegeneration_sdxlimg2img as imagegeneration_sdxlimg2img +import tasks.textextraction_pdf as textextraction_pdf +import tasks.textextraction_google as textextraction_google +import tasks.textextraction_whisperx as textextraction_whisperx +import tasks.translation_google as translation_google +import tasks.translation_libretranslate as translation_libretranslate + +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 PostProcessFunctionType -def run_nostr_dvm_with_local_config(): +def playground(): # We will run an optional bot that can communicate with the DVMs # Note this is very basic for now and still under development bot_config = DVMConfig() @@ -24,54 +36,108 @@ def run_nostr_dvm_with_local_config(): bot_config.LNBITS_ADMIN_KEY = os.getenv("LNBITS_ADMIN_KEY") # The bot will forward zaps for us, use responsibly bot_config.LNBITS_URL = os.getenv("LNBITS_HOST") + # Generate an optional Admin Config, in this case, whenever we give our DVMs this config, they will (re)broadcast + # their NIP89 announcement + # You can create individual admins configs and hand them over when initializing the dvm, + # for example to whilelist users or add to their balance. + # If you use this global config, options will be set for all dvms that use it. + admin_config = AdminConfig() + admin_config.REBROADCAST_NIP89 = False + # Set rebroadcast to true once you have set your NIP89 descriptions and d tags. You only need to rebroadcast once you + # want to update your NIP89 descriptions + admin_config.UPDATE_PROFILE = False + admin_config.LUD16 = "" + # Spawn some DVMs in the playground and run them # You can add arbitrary DVMs there and instantiate them here # Spawn DVM1 Kind 5000: A local Text Extractor from PDFs - pdfextractor = build_pdf_extractor("PDF Extractor", "pdf_extractor") + pdfextractor = textextraction_pdf.build_example("PDF Extractor", "pdf_extractor", admin_config) # If we don't add it to the bot, the bot will not provide access to the DVM pdfextractor.run() # Spawn DVM2 Kind 5002 Local Text TranslationGoogle, calling the free Google API. - translator = build_googletranslator("Google Translator", "google_translator") + translator = translation_google.build_example("Google Translator", "google_translator", admin_config) bot_config.SUPPORTED_DVMS.append(translator) # We add translator to the bot translator.run() # Spawn DVM3 Kind 5002 Local Text TranslationLibre, calling the free LibreTranslateApi, as an alternative. # This will only run and appear on the bot if an endpoint is set in the .env if os.getenv("LIBRE_TRANSLATE_ENDPOINT") is not None and os.getenv("LIBRE_TRANSLATE_ENDPOINT") != "": - libre_translator = build_libretranslator("Libre Translator", "libre_translator") + libre_translator = translation_libretranslate.build_example("Libre Translator", "libre_translator", admin_config) bot_config.SUPPORTED_DVMS.append(libre_translator) # We add translator to the bot libre_translator.run() # Spawn DVM3 Kind 5100 Image Generation This one uses a specific backend called nova-server. # If you want to use it, see the instructions in backends/nova_server if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "": - unstable_artist = build_unstable_diffusion("Unstable Diffusion", "unstable_diffusion") - bot_config.SUPPORTED_DVMS.append(unstable_artist) # We add unstable Diffusion to the bot - unstable_artist.run() + stable_artist = imagegeneration_sdxl.build_example("Stable Diffusion XL", "stable_diffusion", + admin_config, os.getenv("NOVA_SERVER"), + "stabilityai/stable-diffusion-xl", + "") + bot_config.SUPPORTED_DVMS.append(stable_artist) # We add unstable Diffusion to the bot + stable_artist.run() # Spawn DVM4, another Instance of text-to-image, as before but use a different privatekey, model and lora this time. if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "": - sketcher = build_sketcher("Sketcher", "sketcher") + # Let's not use one of the examples in this one, but generate our own variation of the dvm. We make a new DVM + # called "Sketcher", with a predefined model and lora, so it will always make sketches of prompts + def build_sketcher(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") + + nip90params = { + "negative_prompt": { + "required": False, + "values": [] + }, + "ratio": { + "required": False, + "values": ["1:1", "4:3", "16:9", "3:4", "9:16", "10:16"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/229c14e440895da30de77b3ca145d66d4b04efb4027ba3c44ca147eecde891f1.jpg", + "about": "I draw images based on a prompt in the style of paper sketches", + "nip90Params": nip90params + } + + # A module might have options it can be initialized with, here we set a default model, lora and the nova-server + # address it should use. These parameters can be freely defined in the task component + options = {'default_model': "mohawk", 'default_lora': "timburton", 'nova_server': os.getenv("NOVA_SERVER")} + + nip89config = NIP89Config() + nip89config.DTAG = 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 imagegeneration_sdxl.ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config, options=options) + + sketcher = build_sketcher("Sketcher", "sketcher", admin_config) bot_config.SUPPORTED_DVMS.append(sketcher) # We also add Sketcher to the bot sketcher.run() # Spawn DVM5, image-to-image, . if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "": - imageconverter = build_image_converter("Image Converter Inkpunk", "image_converter_inkpunk") + imageconverter = imagegeneration_sdxlimg2img.build_example("Image Converter Inkpunk", + "image_converter_inkpunk", admin_config, + os.getenv("NOVA_SERVER"), + "inkpunk", 0.6) bot_config.SUPPORTED_DVMS.append(imageconverter) # We also add Sketcher to the bot imageconverter.run() - # Spawn DVM5, Another script on nova-server calling WhisperX to transcribe media files if os.getenv("NOVA_SERVER") is not None and os.getenv("NOVA_SERVER") != "": - whisperer = build_whisperx("Whisperer", "whisperx") + whisperer = textextraction_whisperx.build_example("Whisperer", "whisperx", admin_config, os.getenv("NOVA_SERVER")) bot_config.SUPPORTED_DVMS.append(whisperer) # We also add Sketcher to the bot whisperer.run() - transcriptor = build_googletranscribe("Transcriptor", "speech_recognition") + transcriptor = textextraction_google.build_example("Transcriptor", "speech_recognition", admin_config) bot_config.SUPPORTED_DVMS.append(transcriptor) # We also add Sketcher to the bot transcriptor.run() @@ -79,56 +145,68 @@ def run_nostr_dvm_with_local_config(): # per call. Make sure you have enough balance and the DVM's cost is set higher than what you pay yourself, except, you know, # you're being generous. if os.getenv("OPENAI_API_KEY") is not None and os.getenv("OPENAI_API_KEY") != "": - dalle = build_dalle("Dall-E 3", "dalle3") + dalle = imagegeneration_openai_dalle.build_example("Dall-E 3", "dalle3", admin_config) bot_config.SUPPORTED_DVMS.append(dalle) dalle.run() + + #Let's define a function so we can add external DVMs to our bot, we will instanciate it afterwards + def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost, + external_post_process=PostProcessFunctionType.NONE): + dvm_config = DVMConfig() + dvm_config.PUBLIC_KEY = PublicKey.from_hex(pubkey).to_hex() + dvm_config.FIX_COST = fix_cost + dvm_config.PER_UNIT_COST = per_unit_cost + dvm_config.EXTERNAL_POST_PROCESS_TYPE = external_post_process + nip89info = { + "name": name, + } + nip89config = NIP89Config() + nip89config.KIND = kind + nip89config.CONTENT = json.dumps(nip89info) + + interface = DVMTaskInterface(name=name, dvm_config=dvm_config, nip89config=nip89config, task=task) + + return interface + # Spawn DVM7.. oh wait, actually we don't spawn a new DVM, we use the dvmtaskinterface to define an external dvm by providing some info about it, such as # their pubkey, a name, task, kind etc. (unencrypted) tasktiger_external = build_external_dvm(name="External DVM: TaskTiger", - pubkey="d483935d6bfcef3645195c04c97bbb70aedb6e65665c5ea83e562ca3c7acb978", - task="text-to-image", - kind=EventDefinitions.KIND_NIP90_GENERATE_IMAGE, - fix_cost=80, per_unit_cost=0) + pubkey="d483935d6bfcef3645195c04c97bbb70aedb6e65665c5ea83e562ca3c7acb978", + task="text-to-image", + kind=EventDefinitions.KIND_NIP90_GENERATE_IMAGE, + fix_cost=80, per_unit_cost=0) - tasktiger_external.SUPPORTS_ENCRYPTION = False # if the dvm does not support encrypted events, just send a regular event and mark it with p tag. Other dvms might initial answer + tasktiger_external.SUPPORTS_ENCRYPTION = False # if the dvm does not support encrypted events, just send a regular NIP90 event and mark it with p tag. Other dvms might answer initally bot_config.SUPPORTED_DVMS.append(tasktiger_external) # Don't run it, it's on someone else's machine, and we simply make the bot aware of it. + # Spawn DVM8 A Media Grabber/Converter + media_bringer = convert_media.build_example("Media Bringer", "media_converter", admin_config) + bot_config.SUPPORTED_DVMS.append(media_bringer) + media_bringer.run() - # DVM: 8 Another external dvm for recommendations: + # DVM: 9 Another external dvm for recommendations: ymhm_external = build_external_dvm(name="External DVM: You might have missed", pubkey="6b37d5dc88c1cbd32d75b713f6d4c2f7766276f51c9337af9d32c8d715cc1b93", task="content-discovery", kind=EventDefinitions.KIND_NIP90_CONTENT_DISCOVERY, fix_cost=0, per_unit_cost=0, external_post_process=PostProcessFunctionType.LIST_TO_EVENTS) + # If we get back a list of people or events, we can post-process it to make it readable in social clients - ymhm_external.SUPPORTS_ENCRYPTION = False # if the dvm does not support encrypted events, just send a regular event and mark it with p tag. Other dvms might initial answer + ymhm_external.SUPPORTS_ENCRYPTION = False bot_config.SUPPORTED_DVMS.append(ymhm_external) - # Spawn DVM9.. A Media Grabber/Converter - media_bringer = build_media_converter("Media Bringer", "media_converter") - bot_config.SUPPORTED_DVMS.append(media_bringer) - media_bringer.run() - - # Spawn DVM10 Discover inactive followers - discover_inactive = build_inactive_follows_finder("Bygones", "discovery_inactive_follows") + # Spawn DVM10 Find inactive followers + discover_inactive = discovery_inactive_follows.build_example("Bygones", "discovery_inactive_follows", + admin_config) bot_config.SUPPORTED_DVMS.append(discover_inactive) discover_inactive.run() - - - Bot(bot_config) - # Keep the main function alive for libraries that require it, like openai - try: - while True: - time.sleep(10) - except KeyboardInterrupt: - os.kill(os.getpid(), signal.SIGKILL) - exit(1) + keep_alive() if __name__ == '__main__': @@ -138,4 +216,4 @@ if __name__ == '__main__': dotenv.load_dotenv(env_path, verbose=True, override=True) else: raise FileNotFoundError(f'.env file not found at {env_path} ') - run_nostr_dvm_with_local_config() + playground() diff --git a/playground.py b/playground.py deleted file mode 100644 index c8544d7..0000000 --- a/playground.py +++ /dev/null @@ -1,455 +0,0 @@ -import json -import os - -from nostr_sdk import PublicKey - -from interfaces.dvmtaskinterface import DVMTaskInterface -from tasks.convert_media import MediaConverter -from tasks.discovery_inactive_follows import DiscoverInactiveFollows -from tasks.imagegeneration_openai_dalle import ImageGenerationDALLE -from tasks.imagegeneration_sdxl import ImageGenerationSDXL -from tasks.imagegeneration_sdxlimg2img import ImageGenerationSDXLIMG2IMG -from tasks.textextraction_google import SpeechToTextGoogle -from tasks.textextraction_whisperx import SpeechToTextWhisperX -from tasks.textextraction_pdf import TextExtractionPDF -from tasks.translation_google import TranslationGoogle -from tasks.translation_libretranslate import TranslationLibre -from utils.admin_utils import AdminConfig -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 PostProcessFunctionType - -""" -This File is a playground to create DVMs. It shows some examples of DVMs that make use of the modules in the tasks folder -These DVMs should be considered examples and will be extended in the future. -Keys and dtags will be automatically generated and stored in the .env file. -If you already have a pk and dtag you can replace them there before publishing the nip89 - - -Note that the admin_config is optional, and if given commands as defined in admin_utils will be called at start of the -DVM. For example the NIP89 event can be rebroadcasted. - -If LNBITS_INVOICE_KEY is not set (=""), the DVM is still zappable but a lud16 address in required in the profile. - -Additional options can be set, for example to preinitalize vaiables or give parameters that are required to perform a -task, for example an address or an API key. - - -""" - -# Generate an optional Admin Config, in this case, whenever we give our DVMs this config, they will (re)broadcast -# their NIP89 announcement -# You can create individual admins configs and hand them over when initializing the dvm, -# for example to whilelist users or add to their balance. -# If you use this global config, options will be set for all dvms that use it. -admin_config = AdminConfig() -admin_config.REBROADCAST_NIP89 = False -# Set rebroadcast to true once you have set your NIP89 descriptions and d tags. You only need to rebroadcast once you -# want to update your NIP89 descriptions - -admin_config.UPDATE_PROFILE = False -admin_config.LUD16 = "" - - -# Auto update the profiles of your privkeys based on the nip89 information. -# Ideally set a lightning address in the LUD16 field above so our DVMs can get zapped from everywhere - - -# We build a couple of example dvms, create privatekeys and dtags and set their NIP89 descriptions -# We currently call these from the main function and start the dvms there. - -def build_pdf_extractor(name, identifier): - 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") - # Add NIP89 - nip90params = {} - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I extract text from pdf documents", - "nip90Params": nip90params - } - - nip89config = NIP89Config() - nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"]) - nip89config.CONTENT = json.dumps(nip89info) - return TextExtractionPDF(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config) - - -def build_googletranslator(name, identifier): - 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") - - nip90params = { - "language": { - "required": False, - "values": ["en", "az", "be", "bg", "bn", "bs", "ca", "ceb", "co", "cs", "cy", "da", "de", "el", "eo", "es", - "et", "eu", "fa", "fi", "fr", "fy", "ga", "gd", "gl", "gu", "ha", "haw", "hi", "hmn", "hr", "ht", - "hu", "hy", "id", "ig", "is", "it", "he", "ja", "jv", "ka", "kk", "km", "kn", "ko", "ku", "ky", - "la", "lb", "lo", "lt", "lv", "mg", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my", "ne", "nl", - "no", "ny", "or", "pa", "pl", "ps", "pt", "ro", "ru", "sd", "si", "sk", "sl", "sm", "sn", "so", - "sq", "sr", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "tl", "tr", "ug", "uk", "ur", "uz", - "vi", "xh", "yi", "yo", "zh", "zu"] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I translate text from given text/event/job. Currently using Google TranslationGoogle Services to translate " - "input into the language defined in params.", - "nip90Params": nip90params - } - nip89config = NIP89Config() - nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, - nip89info["image"]) - nip89config.CONTENT = json.dumps(nip89info) - return TranslationGoogle(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config) - - -def build_libretranslator(name, identifier): - 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") - - options = {'libre_end_point': os.getenv("LIBRE_TRANSLATE_ENDPOINT"), - 'libre_api_key': os.getenv("LIBRE_TRANSLATE_API_KEY")} - nip90params = { - "language": { - "required": False, - "values": ["en", "az", "be", "bg", "bn", "bs", "ca", "ceb", "co", "cs", "cy", "da", "de", "el", "eo", "es", - "et", "eu", "fa", "fi", "fr", "fy", "ga", "gd", "gl", "gu", "ha", "haw", "hi", "hmn", "hr", "ht", - "hu", "hy", "id", "ig", "is", "it", "he", "ja", "jv", "ka", "kk", "km", "kn", "ko", "ku", "ky", - "la", "lb", "lo", "lt", "lv", "mg", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my", "ne", "nl", - "no", "ny", "or", "pa", "pl", "ps", "pt", "ro", "ru", "sd", "si", "sk", "sl", "sm", "sn", "so", - "sq", "sr", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "tl", "tr", "ug", "uk", "ur", "uz", - "vi", "xh", "yi", "yo", "zh", "zu"] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I translate text from given text/event/job using LibreTranslate Services to translate " - "input into the language defined in params.", - "nip90Params": nip90params - } - nip89config = NIP89Config() - nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, - nip89info["image"]) - nip89config.CONTENT = json.dumps(nip89info) - return TranslationLibre(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) - - -def build_unstable_diffusion(name, identifier): - dvm_config = DVMConfig() - dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier) - dvm_config.LNBITS_INVOICE_KEY = "" # This one will not use Lnbits to create invoices, but rely on zaps - dvm_config.LNBITS_URL = "" - - # A module might have options it can be initialized with, here we set a default model, and the nova-server - # address it should use. These parameters can be freely defined in the task component - options = {'default_model': "unstable", 'nova_server': os.getenv("NOVA_SERVER")} - - nip90params = { - "negative_prompt": { - "required": False, - "values": [] - }, - "ratio": { - "required": False, - "values": ["1:1", "4:3", "16:9", "3:4", "9:16", "10:16"] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I draw images based on a prompt with a Model called unstable diffusion", - "nip90Params": nip90params - } - nip89config = NIP89Config() - nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, - nip89info["image"]) - nip89config.CONTENT = json.dumps(nip89info) - return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) - - -def build_whisperx(name, identifier): - 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") - - # A module might have options it can be initialized with, here we set a default model, and the nova-server - # address it should use. These parameters can be freely defined in the task component - options = {'default_model': "base", 'nova_server': os.getenv("NOVA_SERVER")} - - nip90params = { - "model": { - "required": False, - "values": ["base", "tiny", "small", "medium", "large-v1", "large-v2", "tiny.en", "base.en", "small.en", - "medium.en"] - }, - "alignment": { - "required": False, - "values": ["raw", "segment", "word"] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I extract text from media files with WhisperX", - "nip90Params": nip90params - } - nip89config = NIP89Config() - nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, - nip89info["image"]) - nip89config.CONTENT = json.dumps(nip89info) - return SpeechToTextWhisperX(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) - - -def build_googletranscribe(name, identifier): - 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") - options = {'api_key': None} - # A module might have options it can be initialized with, here we set a default model, and the nova-server - # address it should use. These parameters can be freely defined in the task component - - nip90params = { - "language": { - "required": False, - "values": ["en-US"] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I extract text from media files with the Google API. I understand English by default", - "nip90Params": nip90params - } - nip89config = NIP89Config() - nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, - nip89info["image"]) - nip89config.CONTENT = json.dumps(nip89info) - return SpeechToTextGoogle(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) - - -def build_sketcher(name, identifier): - 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") - - nip90params = { - "negative_prompt": { - "required": False, - "values": [] - }, - "ratio": { - "required": False, - "values": ["1:1", "4:3", "16:9", "3:4", "9:16", "10:16"] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/229c14e440895da30de77b3ca145d66d4b04efb4027ba3c44ca147eecde891f1.jpg", - "about": "I draw images based on a prompt in the style of paper sketches", - "nip90Params": nip90params - } - - # A module might have options it can be initialized with, here we set a default model, lora and the nova-server - # address it should use. These parameters can be freely defined in the task component - options = {'default_model': "mohawk", 'default_lora': "timburton", 'nova_server': os.getenv("NOVA_SERVER")} - - nip89config = NIP89Config() - nip89config.DTAG = 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 ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) - - -def build_image_converter(name, identifier): - 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") - - nip90params = { - "negative_prompt": { - "required": False, - "values": [] - }, - "lora": { - "required": False, - "values": ["inkpunk", "timburton", "voxel"] - }, - - "strength": { - "required": False, - "values": [] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/229c14e440895da30de77b3ca145d66d4b04efb4027ba3c44ca147eecde891f1.jpg", - "about": "I convert an image to another image, kinda random for now. ", - "nip90Params": nip90params - } - - # A module might have options it can be initialized with, here we set a default model, lora and the nova-server - options = {'default_lora': "inkpunk", 'strength': 0.5, 'nova_server': os.getenv("NOVA_SERVER")} - - nip89config = NIP89Config() - - nip89config.DTAG = 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 ImageGenerationSDXLIMG2IMG(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) - - -def build_dalle(name, identifier): - 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 OpenAI's DALL·E 3", - "nip90Params": nip90params - } - - # A module might have options it can be initialized with, here we set a default model, lora and the nova-server - # address it should use. These parameters can be freely defined in the task component - - nip89config = NIP89Config() - nip89config.DTAG = 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 ImageGenerationDALLE(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config) - - -def build_media_converter(name, identifier): - 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") - # Add NIP89 - nip90params = { - "media_format": { - "required": False, - "values": ["video/mp4", "audio/mp3"] - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I convert videos from urls to given output format.", - "nip90Params": nip90params - } - - nip89config = NIP89Config() - nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"]) - nip89config.CONTENT = json.dumps(nip89info) - return MediaConverter(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config) - - -def build_inactive_follows_finder(name, identifier): - 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") - # Add NIP89 - nip90params = { - "user": { - "required": False, - "values": [], - "description": "Do the task for another user" - }, - "since_days": { - "required": False, - "values": [], - "description": "The number of days a user has not been active to be considered inactive" - - } - } - nip89info = { - "name": name, - "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", - "about": "I discover users you follow, but that have been inactive on Nostr", - "nip90Params": nip90params - } - - nip89config = NIP89Config() - nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, - nip89info["image"]) - - nip89config.CONTENT = json.dumps(nip89info) - return DiscoverInactiveFollows(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config) - - -# This function can be used to build a DVM object for a DVM we don't control, but we want the bot to be aware of. -# See main.py for examples. -def build_external_dvm(name, pubkey, task, kind, fix_cost, per_unit_cost, external_post_process=PostProcessFunctionType.NONE): - dvm_config = DVMConfig() - dvm_config.PUBLIC_KEY = PublicKey.from_hex(pubkey).to_hex() - dvm_config.FIX_COST = fix_cost - dvm_config.PER_UNIT_COST = per_unit_cost - dvm_config.EXTERNAL_POST_PROCESS_TYPE = external_post_process - nip89info = { - "name": name, - } - nip89config = NIP89Config() - nip89config.KIND = kind - nip89config.CONTENT = json.dumps(nip89info) - - interface = DVMTaskInterface(name=name, dvm_config=dvm_config, nip89config=nip89config, task=task) - - return interface - - -# Little optional Gimmick: -# For Dalle where we have to pay 4cent per image to openai, we fetch current sat price in fiat from coinstats api -# and update cost at each start -def get_price_per_sat(currency): - import requests - - url = "https://api.coinstats.app/public/v1/coins" - params = {"skip": 0, "limit": 1, "currency": currency} - try: - response = requests.get(url, params=params) - response_json = response.json() - - bitcoin_price = response_json["coins"][0]["price"] - price_currency_per_sat = bitcoin_price / 100000000.0 - except: - price_currency_per_sat = 0.0004 - - return price_currency_per_sat diff --git a/tasks/convert_media.py b/tasks/convert_media.py index 21a6a9d..3c25e9e 100644 --- a/tasks/convert_media.py +++ b/tasks/convert_media.py @@ -1,11 +1,19 @@ import json +import os +import signal +import time +from pathlib import Path + +import dotenv 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 +from utils.nip89_utils import NIP89Config, check_and_set_d_tag from utils.mediasource_utils import organize_input_media_data +from utils.nostr_utils import check_and_set_private_key from utils.output_utils import upload_media_to_hoster """ @@ -57,7 +65,8 @@ class MediaConverter(DVMTaskInterface): if param == "format": # check for param type media_format = tag.as_vec()[2] - filepath = organize_input_media_data(url, input_type, start_time, end_time, dvm_config, client, True, media_format) + filepath = organize_input_media_data(url, input_type, start_time, end_time, dvm_config, client, True, + media_format) options = { "filepath": filepath } @@ -70,3 +79,52 @@ class MediaConverter(DVMTaskInterface): url = upload_media_to_hoster(options["filepath"]) return url + + +# 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") + # Add NIP89 + nip90params = { + "media_format": { + "required": False, + "values": ["video/mp4", "audio/mp3"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I convert videos from urls to given output format.", + "nip90Params": nip90params + } + + nip89config = NIP89Config() + nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + + return MediaConverter(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("Media Bringer", "media_converter", admin_config) + dvm.run() + + keep_alive() diff --git a/tasks/discovery_inactive_follows.py b/tasks/discovery_inactive_follows.py index 6f5229c..7712fea 100644 --- a/tasks/discovery_inactive_follows.py +++ b/tasks/discovery_inactive_follows.py @@ -2,16 +2,20 @@ import json import os import re from datetime import timedelta +from pathlib import Path from threading import Thread +import dotenv from nostr_sdk import Client, Timestamp, PublicKey, Tag, Keys, Options, Alphabet from interfaces.dvmtaskinterface import DVMTaskInterface +from tasks.convert_media import MediaConverter 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 -from utils.nostr_utils import get_event_by_id +from utils.nip89_utils import NIP89Config, check_and_set_d_tag +from utils.nostr_utils import get_event_by_id, check_and_set_private_key from utils.output_utils import post_process_list_to_users """ @@ -163,3 +167,59 @@ class DiscoverInactiveFollows(DVMTaskInterface): # if not text/plain, don't post-process return result + +# 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") + # Add NIP89 + nip90params = { + "user": { + "required": False, + "values": [], + "description": "Do the task for another user" + }, + "since_days": { + "required": False, + "values": [], + "description": "The number of days a user has not been active to be considered inactive" + + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I discover users you follow, but that have been inactive on Nostr", + "nip90Params": nip90params + } + + nip89config = NIP89Config() + nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + + nip89config.CONTENT = json.dumps(nip89info) + return DiscoverInactiveFollows(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("Bygones", "discovery_inactive_follows", admin_config) + dvm.run() + + keep_alive() diff --git a/tasks/imagegeneration_openai_dalle.py b/tasks/imagegeneration_openai_dalle.py index 08c1170..5759f19 100644 --- a/tasks/imagegeneration_openai_dalle.py +++ b/tasks/imagegeneration_openai_dalle.py @@ -1,15 +1,21 @@ 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 +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. @@ -116,3 +122,54 @@ class ImageGenerationDALLE(DVMTaskInterface): 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 OpenAI's DALL·E 3", + "nip90Params": nip90params + } + + + nip89config = NIP89Config() + nip89config.DTAG = 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 ImageGenerationDALLE(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("Dall-E 3", "dalle3", admin_config) + dvm.run() + + keep_alive() diff --git a/tasks/imagegeneration_sdxl.py b/tasks/imagegeneration_sdxl.py index c7657ea..d50e1bd 100644 --- a/tasks/imagegeneration_sdxl.py +++ b/tasks/imagegeneration_sdxl.py @@ -1,12 +1,18 @@ import json +import os from multiprocessing.pool import ThreadPool +from pathlib import Path + +import dotenv from backends.nova_server import check_nova_server_status, send_request_to_nova_server from interfaces.dvmtaskinterface import DVMTaskInterface from utils.admin_utils import AdminConfig +from utils.backend_utils import keep_alive from utils.dvmconfig import DVMConfig -from utils.nip89_utils import NIP89Config +from utils.nip89_utils import NIP89Config, check_and_set_d_tag from utils.definitions import EventDefinitions +from utils.nostr_utils import check_and_set_private_key """ This File contains a Module to transform Text input on NOVA-Server and receive results back. @@ -51,7 +57,7 @@ class ImageGenerationSDXL(DVMTaskInterface): prompt = "" negative_prompt = "" - if self.options.get("default_model"): + if self.options.get("default_model") and self.options.get("default_model") != "": model = self.options['default_model'] else: model = "stabilityai/stable-diffusion-xl-base-1.0" @@ -60,7 +66,7 @@ class ImageGenerationSDXL(DVMTaskInterface): ratio_height = "1" width = "" height = "" - if self.options.get("default_lora"): + if self.options.get("default_lora") and self.options.get("default_lora") != "": lora = self.options['default_lora'] else: lora = "" @@ -155,3 +161,58 @@ class ImageGenerationSDXL(DVMTaskInterface): except Exception as e: 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, server_address, default_model="stabilityai/stable-diffusion-xl" + "-base-1.0", default_lora=""): + dvm_config = DVMConfig() + dvm_config.PRIVATE_KEY = check_and_set_private_key(identifier) + dvm_config.LNBITS_INVOICE_KEY = "" # This one will not use Lnbits to create invoices, but rely on zaps + dvm_config.LNBITS_URL = "" + + # A module might have options it can be initialized with, here we set a default model, and the nova-server + # address it should use. These parameters can be freely defined in the task component + options = {'default_model': default_model, 'default_lora': default_lora, 'nova_server': server_address} + + nip90params = { + "negative_prompt": { + "required": False, + "values": [] + }, + "ratio": { + "required": False, + "values": ["1:1", "4:3", "16:9", "3:4", "9:16", "10:16"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I draw images based on a prompt with a Model called unstable diffusion", + "nip90Params": nip90params + } + nip89config = NIP89Config() + nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + return ImageGenerationSDXL(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config, options=options) + + +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("Unstable Diffusion", "unstable_diffusion", admin_config, os.getenv("NOVA_SERVER"), "stabilityai/stable-diffusion-xl", "") + dvm.run() + + keep_alive() diff --git a/tasks/imagegeneration_sdxlimg2img.py b/tasks/imagegeneration_sdxlimg2img.py index f764585..905098b 100644 --- a/tasks/imagegeneration_sdxlimg2img.py +++ b/tasks/imagegeneration_sdxlimg2img.py @@ -1,12 +1,18 @@ import json +import os from multiprocessing.pool import ThreadPool +from pathlib import Path + +import dotenv from backends.nova_server import check_nova_server_status, send_request_to_nova_server from interfaces.dvmtaskinterface import DVMTaskInterface from utils.admin_utils import AdminConfig +from utils.backend_utils import keep_alive from utils.dvmconfig import DVMConfig -from utils.nip89_utils import NIP89Config +from utils.nip89_utils import NIP89Config, check_and_set_d_tag from utils.definitions import EventDefinitions +from utils.nostr_utils import check_and_set_private_key """ This File contains a Module to transform Text input on NOVA-Server and receive results back. @@ -69,7 +75,7 @@ class ImageGenerationSDXLIMG2IMG(DVMTaskInterface): width = "" height = "" - if self.options.get("default_lora"): + if self.options.get("default_lora") and self.options.get("default_lora") != "": lora = self.options['default_lora'] else: lora = "" @@ -184,3 +190,64 @@ class ImageGenerationSDXLIMG2IMG(DVMTaskInterface): except Exception as e: 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, server_address, default_lora="", strength=0.6): + 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") + + nip90params = { + "negative_prompt": { + "required": False, + "values": [] + }, + "lora": { + "required": False, + "values": ["inkpunk", "timburton", "voxel"] + }, + + "strength": { + "required": False, + "values": [] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/229c14e440895da30de77b3ca145d66d4b04efb4027ba3c44ca147eecde891f1.jpg", + "about": "I convert an image to another image, kinda random for now. ", + "nip90Params": nip90params + } + + # A module might have options it can be initialized with, here we set a default model, lora and the nova-server + options = {'default_lora': default_lora, 'strength': strength, 'nova_server': server_address} + + nip89config = NIP89Config() + + nip89config.DTAG = 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 ImageGenerationSDXLIMG2IMG(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config, options=options) + + +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("Image Converter Inkpunk", "image_converter_inkpunk", admin_config, os.getenv("NOVA_SERVER"), "inkpunk", 0.6) + dvm.run() + + keep_alive() diff --git a/tasks/textextraction_google.py b/tasks/textextraction_google.py index b454cd3..c393450 100644 --- a/tasks/textextraction_google.py +++ b/tasks/textextraction_google.py @@ -4,13 +4,17 @@ import time from multiprocessing.pool import ThreadPool 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 from utils.dvmconfig import DVMConfig from utils.mediasource_utils import organize_input_media_data -from utils.nip89_utils import NIP89Config +from utils.nip89_utils import NIP89Config, check_and_set_d_tag from utils.definitions import EventDefinitions +from utils.nostr_utils import check_and_set_private_key """ This File contains a Module to transform a media file input on Google Cloud @@ -123,3 +127,52 @@ class SpeechToTextGoogle(DVMTaskInterface): return "error" return result + +# 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") + options = {'api_key': None} + # A module might have options it can be initialized with, here we set a default model, and the nova-server + # address it should use. These parameters can be freely defined in the task component + + nip90params = { + "language": { + "required": False, + "values": ["en-US"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I extract text from media files with the Google API. I understand English by default", + "nip90Params": nip90params + } + nip89config = NIP89Config() + nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + return SpeechToTextGoogle(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config, options=options) + + +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("Transcriptor", "speech_recognition", admin_config) + dvm.run() + + keep_alive() diff --git a/tasks/textextraction_pdf.py b/tasks/textextraction_pdf.py index 2423b53..5d8ab55 100644 --- a/tasks/textextraction_pdf.py +++ b/tasks/textextraction_pdf.py @@ -1,13 +1,17 @@ import json import os import re +from pathlib import Path + +import dotenv 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 -from utils.nostr_utils import get_event_by_id +from utils.nip89_utils import NIP89Config, check_and_set_d_tag +from utils.nostr_utils import get_event_by_id, check_and_set_private_key """ This File contains a Module to extract Text from a PDF file locally on the DVM Machine @@ -84,3 +88,45 @@ class TextExtractionPDF(DVMTaskInterface): return text except Exception as e: 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") + # Add NIP89 + nip90params = {} + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I extract text from pdf documents", + "nip90Params": nip90params + } + + nip89config = NIP89Config() + nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + return TextExtractionPDF(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("PDF Extractor", "pdf_extractor", admin_config) + dvm.run() + + keep_alive() diff --git a/tasks/textextraction_whisperx.py b/tasks/textextraction_whisperx.py index 137505c..c3883d3 100644 --- a/tasks/textextraction_whisperx.py +++ b/tasks/textextraction_whisperx.py @@ -4,13 +4,17 @@ import time from multiprocessing.pool import ThreadPool 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 from utils.dvmconfig import DVMConfig from utils.mediasource_utils import organize_input_media_data -from utils.nip89_utils import NIP89Config +from utils.nip89_utils import NIP89Config, check_and_set_d_tag from utils.definitions import EventDefinitions +from utils.nostr_utils import check_and_set_private_key """ This File contains a Module to transform A media file input on NOVA-Server and receive results back. @@ -142,3 +146,58 @@ class SpeechToTextWhisperX(DVMTaskInterface): except Exception as e: 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, server_address): + 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") + + # A module might have options it can be initialized with, here we set a default model, and the nova-server + # address it should use. These parameters can be freely defined in the task component + options = {'default_model': "base", 'nova_server': server_address} + + nip90params = { + "model": { + "required": False, + "values": ["base", "tiny", "small", "medium", "large-v1", "large-v2", "tiny.en", "base.en", "small.en", + "medium.en"] + }, + "alignment": { + "required": False, + "values": ["raw", "segment", "word"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I extract text from media files with WhisperX", + "nip90Params": nip90params + } + nip89config = NIP89Config() + nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + return SpeechToTextWhisperX(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config, options=options) + + +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("Whisperer", "whisperx", admin_config, os.getenv("NOVA_SERVER")) + dvm.run() + + keep_alive() diff --git a/tasks/translation_google.py b/tasks/translation_google.py index b433aec..ad34d6b 100644 --- a/tasks/translation_google.py +++ b/tasks/translation_google.py @@ -1,11 +1,16 @@ import json +import os +from pathlib import Path + +import dotenv 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 -from utils.nostr_utils import get_referenced_event_by_id, get_event_by_id +from utils.nip89_utils import NIP89Config, check_and_set_d_tag +from utils.nostr_utils import get_referenced_event_by_id, get_event_by_id, check_and_set_private_key """ This File contains a Module to call Google Translate Services locally on the DVM Machine @@ -101,3 +106,54 @@ class TranslationGoogle(DVMTaskInterface): translated_text = translated_text + translated_text_part return translated_text + +# 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") + + nip90params = { + "language": { + "required": False, + "values": ["en", "az", "be", "bg", "bn", "bs", "ca", "ceb", "co", "cs", "cy", "da", "de", "el", "eo", "es", + "et", "eu", "fa", "fi", "fr", "fy", "ga", "gd", "gl", "gu", "ha", "haw", "hi", "hmn", "hr", "ht", + "hu", "hy", "id", "ig", "is", "it", "he", "ja", "jv", "ka", "kk", "km", "kn", "ko", "ku", "ky", + "la", "lb", "lo", "lt", "lv", "mg", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my", "ne", "nl", + "no", "ny", "or", "pa", "pl", "ps", "pt", "ro", "ru", "sd", "si", "sk", "sl", "sm", "sn", "so", + "sq", "sr", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "tl", "tr", "ug", "uk", "ur", "uz", + "vi", "xh", "yi", "yo", "zh", "zu"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I translate text from given text/event/job. Currently using Google TranslationGoogle Services to translate " + "input into the language defined in params.", + "nip90Params": nip90params + } + nip89config = NIP89Config() + nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + return TranslationGoogle(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("Google Translator", "google_translator", admin_config) + dvm.run() + + keep_alive() diff --git a/tasks/translation_libretranslate.py b/tasks/translation_libretranslate.py index 5afcbc9..3a8965c 100644 --- a/tasks/translation_libretranslate.py +++ b/tasks/translation_libretranslate.py @@ -1,13 +1,17 @@ import json +import os +from pathlib import Path +import dotenv import requests 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 -from utils.nostr_utils import get_referenced_event_by_id, get_event_by_id +from utils.nip89_utils import NIP89Config, check_and_set_d_tag +from utils.nostr_utils import get_referenced_event_by_id, get_event_by_id, check_and_set_private_key """ This File contains a Module to call Google Translate Services locally on the DVM Machine @@ -98,3 +102,59 @@ class TranslationLibre(DVMTaskInterface): return response.text return translated_text + + +# 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") + + options = {'libre_end_point': os.getenv("LIBRE_TRANSLATE_ENDPOINT"), + 'libre_api_key': os.getenv("LIBRE_TRANSLATE_API_KEY")} + nip90params = { + "language": { + "required": False, + "values": ["en", "az", "be", "bg", "bn", "bs", "ca", "ceb", "co", "cs", "cy", "da", "de", "el", "eo", "es", + "et", "eu", "fa", "fi", "fr", "fy", "ga", "gd", "gl", "gu", "ha", "haw", "hi", "hmn", "hr", "ht", + "hu", "hy", "id", "ig", "is", "it", "he", "ja", "jv", "ka", "kk", "km", "kn", "ko", "ku", "ky", + "la", "lb", "lo", "lt", "lv", "mg", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my", "ne", "nl", + "no", "ny", "or", "pa", "pl", "ps", "pt", "ro", "ru", "sd", "si", "sk", "sl", "sm", "sn", "so", + "sq", "sr", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "tl", "tr", "ug", "uk", "ur", "uz", + "vi", "xh", "yi", "yo", "zh", "zu"] + } + } + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I translate text from given text/event/job using LibreTranslate Services to translate " + "input into the language defined in params.", + "nip90Params": nip90params + } + nip89config = NIP89Config() + nip89config.DTAG = nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, + nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + return TranslationLibre(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config, options=options) + + +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("Libre Translator", "libre_translator", admin_config) + dvm.run() + + keep_alive() diff --git a/utils/backend_utils.py b/utils/backend_utils.py index 9808b19..a27522d 100644 --- a/utils/backend_utils.py +++ b/utils/backend_utils.py @@ -1,4 +1,6 @@ -import typing +import os +import signal +import time import requests from nostr_sdk import Event, Tag @@ -184,3 +186,13 @@ def get_amount_per_task(task, dvm_config, duration=1): print("[" + dvm_config.SUPPORTED_DVMS[ 0].NAME + "] Task " + task + " is currently not supported by this instance, skipping") return None + + + +def keep_alive(): + try: + while True: + time.sleep(10) + except KeyboardInterrupt: + os.kill(os.getpid(), signal.SIGKILL) + exit(1) \ No newline at end of file diff --git a/utils/zap_utils.py b/utils/zap_utils.py index 40c937e..65b7c51 100644 --- a/utils/zap_utils.py +++ b/utils/zap_utils.py @@ -318,3 +318,20 @@ def redeem_cashu(cashu, required_amount, config, client) -> (bool, str): print(e) return False, "" + +def get_price_per_sat(currency): + import requests + + url = "https://api.coinstats.app/public/v1/coins" + params = {"skip": 0, "limit": 1, "currency": currency} + try: + response = requests.get(url, params=params) + response_json = response.json() + + bitcoin_price = response_json["coins"][0]["price"] + price_currency_per_sat = bitcoin_price / 100000000.0 + except: + price_currency_per_sat = 0.0004 + + return price_currency_per_sat +