From 86d96a4772f8f217bb372c7d811277420b6cc930 Mon Sep 17 00:00:00 2001 From: dbth <1097224+believethehype@users.noreply.github.com> Date: Fri, 10 Jan 2025 21:47:35 +0100 Subject: [PATCH] update llm example --- tests/dvm_llm_agents.py | 186 +++++++++++++++++++++++++++++++++ tests/generic_dvm_duck_chat.py | 159 ---------------------------- 2 files changed, 186 insertions(+), 159 deletions(-) create mode 100644 tests/dvm_llm_agents.py delete mode 100644 tests/generic_dvm_duck_chat.py diff --git a/tests/dvm_llm_agents.py b/tests/dvm_llm_agents.py new file mode 100644 index 0000000..71601e8 --- /dev/null +++ b/tests/dvm_llm_agents.py @@ -0,0 +1,186 @@ +import random +import time +from pathlib import Path +import dotenv +from typing import Dict, List +import requests, json + +from nostr_sdk import Kind + +from nostr_dvm.framework import DVMFramework +from nostr_dvm.tasks.generic_dvm import GenericDVM +from nostr_dvm.utils.admin_utils import AdminConfig +from nostr_dvm.utils.dvmconfig import build_default_config +from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag +from nostr_dvm.utils.outbox_utils import AVOID_OUTBOX_RELAY_LIST + +RELAY_LIST = ["wss://nostr.mom", + "wss://relay.nostrdvm.com", + "wss://nostr.oxtr.dev", + #"wss://relay.nostr.net" + ] + +SYNC_DB_RELAY_LIST = ["wss://relay.damus.io", + #"wss://relay.primal.net", + "wss://nostr.oxtr.dev"] + + + + +def playground(announce): + framework = DVMFramework() + + # An Agent that pretends to be a purple Ostrich + name = "PurpleOstrichIntelligence" + identifier = "duckduckchat" # Chose a unique identifier in order to get a lnaddress + description = "I'm a funny purple Ostrich" + picture = "https://image.nostr.build/afd3aaf3fba8e71e1a45611e833967c71ae69f1e1c7a271df4370b5d03feee6d.jpg" + system_prompt = "You are a funny purple ostrich. Reply in a funny way." + #purple_ostrich = build_dvm(identifier, name, description, picture, system_prompt, announce) + #framework.add(purple_ostrich) + + # An Agent that pretends to be a Satoshi Nakamoto + name = "Nakamoto" + identifier = "satoshichat" # Chose a unique identifier in order to get a lnaddress + description = "If you don't believe me or don't get it, I don't have time to try to convince you, sorry." + picture = "https://image.nostr.build/34583da23265d955bc09550890029d815c44a1351613b7a76e324c8ad6e1e5a7.jpg" + system_prompt = "Answer as if you were Satoshi Nakamoto, the inventor of Bitcoin." + satoshi = build_dvm(identifier, name, description, picture, system_prompt, announce) + framework.add(satoshi) + + # An Agent that pretends to be a Satoshi Nakamoto + + name = "SamurAI" + identifier = "samuraichat" # Chose a unique identifier in order to get a lnaddress + description = "I follow the way of the Samurai" + picture = "https://image.nostr.build/c370203fe9fefe0f80b8f72e2fdeee926a22fbce7af13d0013b0071756f1f5e7.jpg" + system_prompt = "Answer as if you were a wise Samrurai." + #satoshi = build_dvm(identifier, name, description, picture, system_prompt, announce) + #framework.add(satoshi) + + + + + + + framework.run() + + + +def build_dvm(identifier, name, description, picture, system_prompt, announce): + options = { + "system_prompt": system_prompt, + "input": "", + } + + kind = 5050 + admin_config = AdminConfig() + admin_config.REBROADCAST_NIP89 = announce + admin_config.REBROADCAST_NIP65_RELAY_LIST = announce + admin_config.UPDATE_PROFILE = announce + + dvm_config = build_default_config(identifier) + dvm_config.KIND = Kind(kind) # Manually set the Kind Number (see data-vending-machines.org) + dvm_config.SEND_FEEDBACK_EVENTS = False + dvm_config.AVOID_OUTBOX_RELAY_LIST = AVOID_OUTBOX_RELAY_LIST + dvm_config.RELAY_LIST = RELAY_LIST + dvm_config.SYNC_DB_RELAY_LIST = SYNC_DB_RELAY_LIST + + # Add NIP89 + nip89info = { + "name": name, + "picture": picture, + "about": description, + "supportsEncryption": True, + "acceptsNutZaps": dvm_config.ENABLE_NUTZAP, + "nip90Params": { + } + } + + nip89config = NIP89Config() + nip89config.KIND = Kind(kind) + nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["picture"]) + nip89config.CONTENT = json.dumps(nip89info) + + dvm = GenericDVM(name=name, dvm_config=dvm_config, nip89config=nip89config, + admin_config=admin_config, options=options) + + async def process(request_form): + options = dvm.set_options(request_form) + result = "Kinda rate limited." + try: + class ConversationOver(Exception): + """Raised when the conversation limit is reached.""" + pass + + class ChatModel: + """Available models for chat.""" + claude = "claude-3-haiku-20240307" + gpt = "gpt-4o-mini" + llama = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo" + mistral = "mistralai/Mixtral-8x7B-Instruct-v0.1" + + class ChatInstance: + def __init__(self, model: str): + self.base = "https://duckduckgo.com/duckchat/v1%s" + self.vqd: str = requests.get( + self.base % "/status", + headers={"x-vqd-accept": "1"}, + timeout=5 + ).headers["x-vqd-4"] + self.model: str = model + self.transcript: List[Dict[str, str]] = [] + + def chat(self, message: str) -> str: + """ + Chat with the chosen model. Takes a message and returns the model's response. + """ + self.transcript.append({"role": "user", "content": message}) + res = requests.post( + self.base % "/chat", + headers={"x-vqd-4": self.vqd}, + timeout=10, + json={"model": self.model, "messages": self.transcript}, + ) + self.vqd = res.headers["x-vqd-4"] + + out: str = "" + for item in (i.removeprefix("data: ") for i in res.text.split("\n\n")): + if item.startswith("[DONE]"): + if item.endswith("[LIMIT_CONVERSATION]"): + raise ConversationOver + break + out += json.loads(item).get("message", "").encode("latin-1").decode() + + self.transcript.append({"role": "assistant", "content": out}) + return out + + chat = ChatInstance(ChatModel.llama) + query = ("{role: system, content: " + options["system_prompt"] + "}" + " {role: user, content:" + + options["input"] + "}") + + + result = chat.chat(query) + print(result) + + except Exception as e: + print(e) + return result + + dvm.process = process # overwrite the process function with the above one + return dvm + + +if __name__ == '__main__': + env_path = Path('.env') + if not env_path.is_file(): + with open('.env', 'w') as f: + print("Writing new .env file") + f.write('') + 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} ') + + playground(announce=True) diff --git a/tests/generic_dvm_duck_chat.py b/tests/generic_dvm_duck_chat.py deleted file mode 100644 index 3188051..0000000 --- a/tests/generic_dvm_duck_chat.py +++ /dev/null @@ -1,159 +0,0 @@ -import json -from pathlib import Path - -import dotenv -from duck_chat import ModelType -from nostr_sdk import Kind - -from nostr_dvm.framework import DVMFramework -from nostr_dvm.tasks.generic_dvm import GenericDVM -from nostr_dvm.utils.admin_utils import AdminConfig -from nostr_dvm.utils.dvmconfig import build_default_config -from nostr_dvm.utils.nip89_utils import NIP89Config, check_and_set_d_tag -from nostr_dvm.utils.outbox_utils import AVOID_OUTBOX_RELAY_LIST - -RELAY_LIST = ["wss://nostr.mom", - #"wss://relay.primal.net", - "wss://nostr.oxtr.dev", - #"wss://relay.nostr.net" - ] - -SYNC_DB_RELAY_LIST = ["wss://relay.damus.io", - #"wss://relay.primal.net", - "wss://nostr.oxtr.dev"] - - - -def playground(announce=False): - - framework = DVMFramework() - kind = 5050 - admin_config = AdminConfig() - admin_config.REBROADCAST_NIP89 = announce - admin_config.REBROADCAST_NIP65_RELAY_LIST = announce - admin_config.UPDATE_PROFILE = announce - - name = "DuckChat" - identifier = "duckduckchat" # Chose a unique identifier in order to get a lnaddress - dvm_config = build_default_config(identifier) - dvm_config.KIND = Kind(kind) # Manually set the Kind Number (see data-vending-machines.org) - dvm_config.SEND_FEEDBACK_EVENTS = False - dvm_config.AVOID_OUTBOX_RELAY_LIST = AVOID_OUTBOX_RELAY_LIST - dvm_config.RELAY_LIST = RELAY_LIST - dvm_config.SYNC_DB_RELAY_LIST = SYNC_DB_RELAY_LIST - - - # Add NIP89 - nip89info = { - "name": name, - "picture": "https://image.nostr.build/28da676a19841dcfa7dcf7124be6816842d14b84f6046462d2a3f1268fe58d03.png", - "about": "I'm briding DuckDuckAI'", - "supportsEncryption": True, - "acceptsNutZaps": dvm_config.ENABLE_NUTZAP, - "nip90Params": { - } - } - - nip89config = NIP89Config() - nip89config.KIND = Kind(kind) - nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["picture"]) - nip89config.CONTENT = json.dumps(nip89info) - - options = { - "system_prompt": "You are a funny purple ostrich. Reply in a funny way.", - "input": "", - } - - dvm = GenericDVM(name=name, dvm_config=dvm_config, nip89config=nip89config, - admin_config=admin_config, options=options) - - async def process(request_form): - #pip install -U https://github.com/mrgick/duckduckgo-chat-ai/archive/master.zip - - from duck_chat import DuckChat - options = dvm.set_options(request_form) - result = "No worky" - try: - """ - Simple Python client for ddg.gg/chat - """ - - from typing import Dict, List - import requests, json - - class ConversationOver(Exception): - """Raised when the conversation limit is reached.""" - pass - - class ChatModel: - """Available models for chat.""" - claude = "claude-3-haiku-20240307" - gpt = "gpt-4o-mini" - llama = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo" - mistral = "mistralai/Mixtral-8x7B-Instruct-v0.1" - - class ChatInstance: - def __init__(self, model: str): - self.base = "https://duckduckgo.com/duckchat/v1%s" - self.vqd: str = requests.get( - self.base % "/status", - headers={"x-vqd-accept": "1"}, - timeout=5 - ).headers["x-vqd-4"] - self.model: str = model - self.transcript: List[Dict[str, str]] = [] - - def chat(self, message: str) -> str: - """ - Chat with the chosen model. Takes a message and returns the model's response. - """ - self.transcript.append({"role": "user", "content": message}) - res = requests.post( - self.base % "/chat", - headers={"x-vqd-4": self.vqd}, - timeout=5, - json={"model": self.model, "messages": self.transcript}, - ) - self.vqd = res.headers["x-vqd-4"] - - out: str = "" - for item in (i.removeprefix("data: ") for i in res.text.split("\n\n")): - if item.startswith("[DONE]"): - if item.endswith("[LIMIT_CONVERSATION]"): - raise ConversationOver - break - out += json.loads(item).get("message", "").encode("latin-1").decode() - - self.transcript.append({"role": "assistant", "content": out}) - return out - - - chat = ChatInstance(ChatModel.gpt) - query = ("{role: system, content: " + options["system_prompt"] + "}" + " {role: user, content:" + - options["input"] + "}") - - result = chat.chat(query) - print(result) - - except Exception as e: - print(e) - return result - - dvm.process = process # overwrite the process function with the above one - framework.add(dvm) - framework.run() - - -if __name__ == '__main__': - env_path = Path('.env') - if not env_path.is_file(): - with open('.env', 'w') as f: - print("Writing new .env file") - f.write('') - 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} ') - - playground(announce=False)