diff --git a/nostr_dvm/dvm.py b/nostr_dvm/dvm.py index ab5f7f3..02e5150 100644 --- a/nostr_dvm/dvm.py +++ b/nostr_dvm/dvm.py @@ -347,7 +347,7 @@ class DVM: def send_nostr_reply_event(content, original_event_as_str): original_event = Event.from_json(original_event_as_str) - request_tag = Tag.parse(["request", original_event_as_str.replace("\\", "")]) + request_tag = Tag.parse(["request", original_event_as_str]) e_tag = Tag.parse(["e", original_event.id().to_hex()]) p_tag = Tag.parse(["p", original_event.pubkey().to_hex()]) alt_tag = Tag.parse(["alt", "This is the result of a NIP90 DVM AI task with kind " + str( @@ -368,6 +368,7 @@ class DVM: reply_tags.append(i_tag) if encrypted: + print(content) content = nip04_encrypt(self.keys.secret_key(), PublicKey.from_hex(original_event.pubkey().to_hex()), content) diff --git a/nostr_dvm/tasks/README.md b/nostr_dvm/tasks/README.md index 238b860..bf2a9cc 100644 --- a/nostr_dvm/tasks/README.md +++ b/nostr_dvm/tasks/README.md @@ -12,9 +12,11 @@ Current List of Tasks: | SpeechToTextGoogle | 5000 | Extracts Speech from Media files via Google Services | googleAPI | | SpeechToTextWhisperX | 5000 | Extracts Speech from Media files via local WhisperX | nserver | | ImageInterrogator | 5000 | Extracts Prompts from Images | nserver | +| TextSummarizationHuggingChat | 5001 | Summarizes given Input | huggingface | | TranslationGoogle | 5002 | Translates Inputs to another language | googleAPI | | TranslationLibre | 5002 | Translates Inputs to another language | libreAPI | | TextGenerationLLMLite | 5050 | Chat with LLM backends like Ollama, ChatGPT etc | local/api/openai | +| TextGenerationHuggingChat | 5050 | Chat with LLM backend on Huggingface | huggingface | | ImageGenerationSDXL | 5100 | Generates an Image from Prompt with Stable Diffusion XL | nserver | | ImageGenerationSDXLIMG2IMG | 5100 | Generates an Image from an Image with Stable Diffusion XL | nserver | | ImageGenerationReplicateSDXL | 5100 | Generates an Image from Prompt with Stable Diffusion XL | replicate | @@ -26,7 +28,7 @@ Current List of Tasks: | TextToSpeech | 5250 | Generate Audio from a prompt | local | | TrendingNotesNostrBand | 5300 | Show trending notes on nostr.band | nostr.band api | | DiscoverInactiveFollows | 5301 | Find inactive Nostr users | local | -| AdvancedSearch | 5302 (inoff) | Search Content on nostr.band | local | +| AdvancedSearch | 5302 (inoff) | Search Content on nostr.band | local/nostr.band | Kinds with (inoff) are suggestions and not merged yet and might change in the future. Backends might require to add an API key to the .env file or run an external server/framework the dvm will communicate with. \ No newline at end of file diff --git a/nostr_dvm/tasks/summarization_huggingchat.py b/nostr_dvm/tasks/summarization_huggingchat.py new file mode 100644 index 0000000..7c622a5 --- /dev/null +++ b/nostr_dvm/tasks/summarization_huggingchat.py @@ -0,0 +1,130 @@ +import json +import os + +from nostr_dvm.interfaces.dvmtaskinterface import DVMTaskInterface, process_venv +from nostr_dvm.utils.admin_utils import AdminConfig +from nostr_dvm.utils.definitions import EventDefinitions +from nostr_dvm.utils.dvmconfig import DVMConfig, build_default_config +from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag +from nostr_dvm.utils.nostr_utils import get_referenced_event_by_id, get_event_by_id +from nostr_sdk import Tag + +""" +This File contains a Module to generate Text, based on a prompt using a LLM (local or API) (Ollama, custom model, chatgpt) + +Accepted Inputs: Prompt (text) +Outputs: Generated text +""" + + +class TextSummarizationHuggingChat(DVMTaskInterface): + KIND: int = EventDefinitions.KIND_NIP90_SUMMARIZE_TEXT + TASK: str = "summarization" + FIX_COST: float = 0 + dependencies = [("nostr-dvm", "nostr-dvm"), + ("hugchat", "hugchat")] + + def __init__(self, name, dvm_config: DVMConfig, nip89config: NIP89Config, + admin_config: AdminConfig = None, options=None): + dvm_config.SCRIPT = os.path.abspath(__file__) + super().__init__(name, dvm_config, nip89config, admin_config, options) + + def is_input_supported(self, tags, client=None, dvm_config=None): + 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 + + 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 = "" + + 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 input_type == "event": + evt = get_event_by_id(tag.as_vec()[1], client=client, config=dvm_config) + prompt = evt.content() + elif input_type == "job": + evt = get_referenced_event_by_id(event_id=tag.as_vec()[1], client=client, + kinds=[EventDefinitions.KIND_NIP90_RESULT_EXTRACT_TEXT, + EventDefinitions.KIND_NIP90_RESULT_SUMMARIZE_TEXT, + EventDefinitions.KIND_NIP90_RESULT_TRANSLATE_TEXT, + EventDefinitions.KIND_NIP90_RESULT_CONTENT_DISCOVERY], + dvm_config=dvm_config) + if evt.kind() == EventDefinitions.KIND_NIP90_RESULT_CONTENT_DISCOVERY: + result_list = json.loads(evt.content()) + prompt = "" + for tag in result_list: + e_tag = Tag.parse(tag) + evt = get_event_by_id(e_tag.as_vec()[1], client=client, config=dvm_config) + prompt += evt.content() + "\n" + + else: + prompt = evt.content() + options = { + "prompt": prompt, + } + request_form['options'] = json.dumps(options) + + return request_form + + def process(self, request_form): + from hugchat import hugchat + from hugchat.login import Login + sign = Login(os.getenv("HUGGINGFACE_EMAIL"), os.getenv("HUGGINGFACE_PASSWORD")) + cookie_path_dir = "./cookies_snapshot" + try: + cookies = sign.loadCookiesFromDir( + cookie_path_dir) # This will detect if the JSON file exists, return cookies if it does and raise an Exception if it's not. + except: + cookies = sign.login() + sign.saveCookiesToDir(cookie_path_dir) + + + options = DVMTaskInterface.set_options(request_form) + + try: + chatbot = hugchat.ChatBot(cookies=cookies.get_dict()) # or cookie_path="usercookies/.json" + query_result = chatbot.query("Summarize the following text in maximum 5 sentences: " + options["prompt"]) + print(query_result["text"]) # or query_result.text or query_result["text"] + return str(query_result["text"]).lstrip() + + except Exception as e: + print("Error in Module: " + str(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 = build_default_config(identifier) + admin_config.LUD16 = dvm_config.LN_ADDRESS + + nip89info = { + "name": name, + "image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg", + "about": "I use a LLM connected via Huggingchat", + "encryptionSupported": True, + "cashuAccepted": True, + "nip90Params": {} + } + + nip89config = NIP89Config() + nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"]) + nip89config.CONTENT = json.dumps(nip89info) + + return TextSummarizationHuggingChat(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config) + + +if __name__ == '__main__': + process_venv(TextSummarizationHuggingChat) diff --git a/tests/test_dvm_client.py b/tests/test_dvm_client.py index 4315487..8ab7c4a 100644 --- a/tests/test_dvm_client.py +++ b/tests/test_dvm_client.py @@ -51,7 +51,7 @@ def nostr_client_test_image(prompt): bidTag = Tag.parse(['bid', str(1000 * 1000), str(1000 * 1000)]) relaysTag = Tag.parse(['relays', "wss://relay.damus.io", "wss://blastr.f7z.xyz", "wss://relayable.org", "wss://nostr-pub.wellorder.net"]) - alttag = Tag.parse(["alt", "This is a NIP90 DVM AI task to translate a given Input"]) + alttag = Tag.parse(["alt", "This is a NIP90 DVM AI task to generate an Image from a given Input"]) event = EventBuilder(EventDefinitions.KIND_NIP90_GENERATE_IMAGE, str("Generate an Image."), [iTag, outTag, tTag, paramTag1, bidTag, relaysTag, alttag]).to_event(keys)