Skip to content

Commit

Permalink
adding examples, minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
believethehype committed Dec 19, 2023
1 parent 70e684f commit 0062d78
Show file tree
Hide file tree
Showing 11 changed files with 372 additions and 16 deletions.
26 changes: 26 additions & 0 deletions examples/ollama_dvm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# NostrAI: Nostr NIP90 Data Vending Machine Framework Example

Projects in this folder contain ready-to-use DVMs. To tun the DVM following the next steps:

## To get started:
- Install Python 3.10


Create a new venv in this directory by opening the terminal here, or navigate to this directory and type: `"python -m venv venv"`
- Place .env file (based on .env_example) in this folder.
- Recommended but optional:
- Create a `LNbits` account on an accessible instance of your choice, enter one account's id and admin key (this account will create other accounts for the dvms) Open the .env file and enter this info to `LNBITS_ADMIN_KEY`, `LNBITS_ADMIN_ID`, `LNBITS_HOST`.
- If you are running an own instance of `Nostdress` enter `NOSTDRESS_DOMAIN` or use the default one.
- Activate the venv with
- MacOS/Linux: source ./venv/bin/activate
- Windows: .\venv\Scripts\activate
- Type: `pip install nostr-dvm`
- Run `python3 main.py` (or python main.py)
- The framework will then automatically create keys, nip89 tags and zapable NIP57 `lightning addresses` for your dvms in this file.
- Check the .env file if these values look correct.
- Check the `main.py` file. You can update the image/description/name of your DVM before announcing it.
- You can then in main.py set `admin_config.REBROADCAST_NIP89` and
`admin_config.UPDATE_PROFILE` to `True` to announce the NIP89 info and update the npubs profile automatically.
- After this was successful you can set these back to False until the next time you want to update the NIP89 or profile.

You are now running your own DVM.
54 changes: 54 additions & 0 deletions examples/ollama_dvm/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import json
from pathlib import Path
import dotenv

from nostr_dvm.tasks.textgeneration_llmlite import TextGenerationLLMLite
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


def main():
identifier = "llama2"
name = "Ollama"

dvm_config = build_default_config(identifier)
admin_config = AdminConfig()
admin_config.REBROADCAST_NIP89 = False
admin_config.UPDATE_PROFILE = False
admin_config.LUD16 = dvm_config.LN_ADDRESS

options = {'default_model': "ollama/llama2", 'server': "http://localhost:11434"}

nip89info = {
"name": name,
"image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg",
"about": "I use a LLM connected via OLLAMA",
"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)

ollama = TextGenerationLLMLite(name=name, dvm_config=dvm_config, nip89config=nip89config, admin_config=admin_config,
options=options)
ollama.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} ')
main()
94 changes: 94 additions & 0 deletions examples/ollama_dvm/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import json
import time
from pathlib import Path
from threading import Thread

import dotenv
from nostr_sdk import Keys, Client, Tag, EventBuilder, Filter, HandleNotification, Timestamp, nip04_decrypt

from nostr_dvm.utils.dvmconfig import DVMConfig
from nostr_dvm.utils.nostr_utils import send_event, check_and_set_private_key
from nostr_dvm.utils.definitions import EventDefinitions


def nostr_client_test_llm(prompt):
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))

iTag = Tag.parse(["i", prompt, "text"])
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 generate TTSt"])
event = EventBuilder(EventDefinitions.KIND_NIP90_GENERATE_TEXT, str("Generate an Audio File."),
[iTag, relaysTag, alttag]).to_event(keys)

relay_list = ["wss://relay.damus.io", "wss://blastr.f7z.xyz", "wss://relayable.org",
"wss://nostr-pub.wellorder.net"]

client = Client(keys)
for relay in relay_list:
client.add_relay(relay)
client.connect()
config = DVMConfig
send_event(event, client=client, dvm_config=config)
return event.as_json()

def nostr_client():
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))
sk = keys.secret_key()
pk = keys.public_key()
print(f"Nostr Test Client public key: {pk.to_bech32()}, Hex: {pk.to_hex()} ")
client = Client(keys)
dvmconfig = DVMConfig()
for relay in dvmconfig.RELAY_LIST:
client.add_relay(relay)
client.connect()

dm_zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM,
EventDefinitions.KIND_ZAP]).since(
Timestamp.now()) # events to us specific
dvm_filter = (Filter().kinds([EventDefinitions.KIND_NIP90_RESULT_GENERATE_TEXT,
EventDefinitions.KIND_FEEDBACK]).since(Timestamp.now())) # public events
client.subscribe([dm_zap_filter, dvm_filter])


nostr_client_test_llm("Tell me a joke about a purple Ostrich!")
print("Sending Job Request")


#nostr_client_test_image_private("a beautiful ostrich watching the sunset")
class NotificationHandler(HandleNotification):
def handle(self, relay_url, event):
print(f"Received new event from {relay_url}: {event.as_json()}")
if event.kind() == 7000:
print("[Nostr Client]: " + event.as_json())
elif 6000 < event.kind() < 6999:
print("[Nostr Client]: " + event.as_json())
print("[Nostr Client]: " + event.content())

elif event.kind() == 4:
dec_text = nip04_decrypt(sk, event.pubkey(), event.content())
print("[Nostr Client]: " + f"Received new msg: {dec_text}")

elif event.kind() == 9735:
print("[Nostr Client]: " + f"Received new zap:")
print(event.as_json())

def handle_msg(self, relay_url, msg):
return

client.handle_notifications(NotificationHandler())
while True:
time.sleep(5.0)


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} ')

nostr_dvm_thread = Thread(target=nostr_client())
nostr_dvm_thread.start()
26 changes: 26 additions & 0 deletions examples/tts_dvm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# NostrAI: Nostr NIP90 Data Vending Machine Framework Example

Projects in this folder contain ready-to-use DVMs. To tun the DVM following the next steps:

## To get started:
- Install Python 3.10


Create a new venv in this directory by opening the terminal here, or navigate to this directory and type: `"python -m venv venv"`
- Place .env file (based on .env_example) in this folder.
- Recommended but optional:
- Create a `LNbits` account on an accessible instance of your choice, enter one account's id and admin key (this account will create other accounts for the dvms) Open the .env file and enter this info to `LNBITS_ADMIN_KEY`, `LNBITS_ADMIN_ID`, `LNBITS_HOST`.
- If you are running an own instance of `Nostdress` enter `NOSTDRESS_DOMAIN` or use the default one.
- Activate the venv with
- MacOS/Linux: source ./venv/bin/activate
- Windows: .\venv\Scripts\activate
- Type: `pip install nostr-dvm`
- Run `python3 main.py` (or python main.py)
- The framework will then automatically create keys, nip89 tags and zapable NIP57 `lightning addresses` for your dvms in this file.
- Check the .env file if these values look correct.
- Check the `main.py` file. You can update the image/description/name of your DVM before announcing it.
- You can then in main.py set `admin_config.REBROADCAST_NIP89` and
`admin_config.UPDATE_PROFILE` to `True` to announce the NIP89 info and update the npubs profile automatically.
- After this was successful you can set these back to False until the next time you want to update the NIP89 or profile.

You are now running your own DVM.
60 changes: 60 additions & 0 deletions examples/tts_dvm/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import json
from pathlib import Path
import dotenv
from nostr_dvm.tasks.texttospeech import TextToSpeech
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


def main():
identifier = "tts"
name = "Guy Swann Clone"

dvm_config = build_default_config(identifier)
admin_config = AdminConfig()
admin_config.REBROADCAST_NIP89 = False
admin_config.UPDATE_PROFILE = False
admin_config.LUD16 = dvm_config.LN_ADDRESS

# Use default file if paramter is empty, else overwrite with any local wav file
options = {'input_file': ""}

nip89info = {
"name": name,
"image": "https://image.nostr.build/c33ca6fc4cc038ca4adb46fdfdfda34951656f87ee364ef59095bae1495ce669.jpg",
"about": "I Generate Speech from Text",
"encryptionSupported": True,
"cashuAccepted": True,
"nip90Params": {
"language": {
"required": False,
"values": []
}
}
}

nip89config = NIP89Config()
nip89config.DTAG = check_and_set_d_tag(identifier, name, dvm_config.PRIVATE_KEY, nip89info["image"])
nip89config.CONTENT = json.dumps(nip89info)

tts = TextToSpeech(name=name,
dvm_config=dvm_config,
nip89config=nip89config,
admin_config=admin_config,
options=options)
tts.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} ')
main()
98 changes: 98 additions & 0 deletions examples/tts_dvm/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import json
import time
from pathlib import Path
from threading import Thread

import dotenv
from nostr_sdk import Keys, Client, Tag, EventBuilder, Filter, HandleNotification, Timestamp, nip04_decrypt

from nostr_dvm.utils.dvmconfig import DVMConfig
from nostr_dvm.utils.nostr_utils import send_event, check_and_set_private_key
from nostr_dvm.utils.definitions import EventDefinitions


def nostr_client_test_tts(prompt):
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))

iTag = Tag.parse(["i", prompt, "text"])
paramTag1 = Tag.parse(["param", "language", "en"])


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 generate TTSt"])
event = EventBuilder(EventDefinitions.KIND_NIP90_TEXT_TO_SPEECH, str("Generate an Audio File."),
[iTag, paramTag1, bidTag, relaysTag, alttag]).to_event(keys)

relay_list = ["wss://relay.damus.io", "wss://blastr.f7z.xyz", "wss://relayable.org",
"wss://nostr-pub.wellorder.net"]

client = Client(keys)
for relay in relay_list:
client.add_relay(relay)
client.connect()
config = DVMConfig
send_event(event, client=client, dvm_config=config)
return event.as_json()

def nostr_client():
keys = Keys.from_sk_str(check_and_set_private_key("test_client"))
sk = keys.secret_key()
pk = keys.public_key()
print(f"Nostr Test Client public key: {pk.to_bech32()}, Hex: {pk.to_hex()} ")
client = Client(keys)
dvmconfig = DVMConfig()
for relay in dvmconfig.RELAY_LIST:
client.add_relay(relay)
client.connect()

dm_zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM,
EventDefinitions.KIND_ZAP]).since(
Timestamp.now()) # events to us specific
dvm_filter = (Filter().kinds([EventDefinitions.KIND_NIP90_RESULT_TEXT_TO_SPEECH,
EventDefinitions.KIND_FEEDBACK]).since(Timestamp.now())) # public events
client.subscribe([dm_zap_filter, dvm_filter])


nostr_client_test_tts("Hello, this is a test. Mic check one, two.")
print("Sending Job Request")


#nostr_client_test_image_private("a beautiful ostrich watching the sunset")
class NotificationHandler(HandleNotification):
def handle(self, relay_url, event):
print(f"Received new event from {relay_url}: {event.as_json()}")
if event.kind() == 7000:
print("[Nostr Client]: " + event.as_json())
elif 6000 < event.kind() < 6999:
print("[Nostr Client]: " + event.as_json())
print("[Nostr Client]: " + event.content())

elif event.kind() == 4:
dec_text = nip04_decrypt(sk, event.pubkey(), event.content())
print("[Nostr Client]: " + f"Received new msg: {dec_text}")

elif event.kind() == 9735:
print("[Nostr Client]: " + f"Received new zap:")
print(event.as_json())

def handle_msg(self, relay_url, msg):
return

client.handle_notifications(NotificationHandler())
while True:
time.sleep(5.0)


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} ')

nostr_dvm_thread = Thread(target=nostr_client())
nostr_dvm_thread.start()
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def playground():
env_path = Path('.env')
if not env_path.is_file():
with open('.env', 'w') as f:
print("Wrting new .env file")
print("Writing new .env file")
f.write('')
if env_path.is_file():
print(f'loading environment from {env_path.resolve()}')
Expand Down
8 changes: 5 additions & 3 deletions nostr_dvm/dvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,11 @@ def do_work(job_event, amount):
print("Finished processing, loading data..")

with open(os.path.abspath('output.txt')) as f:
result = f.readlines()[0]
print(result)
#f.close()
resultall = f.readlines()
result = ""
for line in resultall:
if line != '\n':
result += line
os.remove(os.path.abspath('output.txt'))
else: #Some components might have issues with running code in otuside venv.
# We install locally in these cases for now
Expand Down
Loading

0 comments on commit 0062d78

Please sign in to comment.