From 8b8e52a3720739d15df3a591b70b17d38e26966f Mon Sep 17 00:00:00 2001 From: Believethehype Date: Wed, 10 Jan 2024 10:10:22 +0100 Subject: [PATCH] move search ui to wrapper, show dvm feedback --- .gitignore | 1 + tests/gui/nostrAI_search_client.py | 210 +++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 tests/gui/nostrAI_search_client.py diff --git a/.gitignore b/.gitignore index 0c377e4..1813e8e 100644 --- a/.gitignore +++ b/.gitignore @@ -176,3 +176,4 @@ backends/nserver/modules/image_upscale/weights cache/ cookies_snapshot/satoshi2077@protonmail.com.json Todo.md +tests/gui/node_modules diff --git a/tests/gui/nostrAI_search_client.py b/tests/gui/nostrAI_search_client.py new file mode 100644 index 0000000..4cfe880 --- /dev/null +++ b/tests/gui/nostrAI_search_client.py @@ -0,0 +1,210 @@ +import asyncio +import json +import time +from datetime import timedelta +from pathlib import Path +from nicegui import run, ui +import dotenv +from nostr_sdk import Keys, Client, Tag, EventBuilder, Filter, HandleNotification, nip04_decrypt, \ + nip04_encrypt, Options, Timestamp, ZapRequestData, ClientSigner, EventId, Nip19Event, PublicKey + +from nostr_dvm.utils import dvmconfig +from nostr_dvm.utils.database_utils import fetch_user_metadata +from nostr_dvm.utils.dvmconfig import DVMConfig +from nostr_dvm.utils.nostr_utils import send_event, check_and_set_private_key, get_event_by_id, get_events_by_id +from nostr_dvm.utils.definitions import EventDefinitions + + +@ui.page('/', dark=True) +def init(): + keys = Keys.from_sk_str(check_and_set_private_key("test_client")) + opts = (Options().wait_for_send(False).send_timeout(timedelta(seconds=2)) + .skip_disconnected_relays(True)) + + signer = ClientSigner.keys(keys) + client = Client.with_opts(signer, opts) + relay_list = dvmconfig.DVMConfig.RELAY_LIST + + for relay in relay_list: + client.add_relay(relay) + client.connect() + data = [] + + def nostr_client_test_search(prompt, users=None, since="", until=""): + if users is None: + users = [] + + iTag = Tag.parse(["i", prompt, "text"]) + userTag = Tag.parse(['param', 'users', json.dumps(users)]) + sinceTag = Tag.parse(['param', 'since', since]) + untilTag = Tag.parse(['param', 'until', until]) + maxResultsTag = Tag.parse(['param', 'max_results', "100"]) + + 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 search content"]) + + tags = [iTag, relaysTag, alttag, maxResultsTag] + if users: + tags.append(userTag) + if since != "": + tags.append(sinceTag) + if until != "": + tags.append(untilTag) + event = EventBuilder(EventDefinitions.KIND_NIP90_CONTENT_SEARCH, str("Search.."), + tags).to_event(keys) + + config = DVMConfig + config.RELAY_LIST = relay_list + send_event(event, client=client, dvm_config=config) + return event + + def handledvm(now, eventid): + response = False + + feedbackfilter = Filter().pubkey(keys.public_key()).kinds( + [EventDefinitions.KIND_NIP90_RESULTS_CONTENT_SEARCH]).since(now).event(eventid) + feedbackfilter2 = Filter().pubkey(keys.public_key()).kinds( + [EventDefinitions.KIND_FEEDBACK]).since(now).event(eventid) + lastfeedback = "" + while not response: + events = client.get_events_of([feedbackfilter], timedelta(seconds=3)) + fevents = client.get_events_of([feedbackfilter2], timedelta(seconds=3)) + if len(fevents) > 0: + if lastfeedback != fevents[0].content(): + for tag in fevents[0].tags(): + if tag.as_vec()[0] == "status": + if tag.as_vec()[1] == "error": + with mainrow: + with maincolumn: + ui.notify(fevents[0].content(), type="negative") + lastfeedback = fevents[0].content() + break + else: + with mainrow: + with maincolumn: + ui.notify(fevents[0].content(), type="info") + lastfeedback = fevents[0].content() + break + + + if len(events) == 0: + response = False + time.sleep(1.0) + continue + else: + if events[0].content() == "[]": + return [] + + event_etags = json.loads(events[0].content()) + event_ids = [] + for etag in event_etags: + eventidob = EventId.from_hex(etag[1]) + event_ids.append(eventidob) + + config = DVMConfig() + events = get_events_by_id(event_ids, client, config) + if events is None: + return [] + + listui = [] + for event in events: + nip19event = Nip19Event(event.id(), event.pubkey(), dvmconfig.DVMConfig.RELAY_LIST) + nip19eventid = nip19event.to_bech32() + new = {'result': event.content(), 'author': event.pubkey().to_hex(), + 'eventid': str(event.id().to_hex()), + 'time': str(event.created_at().to_human_datetime()), + 'njump': "https://njump.me/" + nip19eventid, + 'highlighter': "https://highlighter.com/a/" + nip19eventid, + 'nostrudel': "https://nostrudel.ninja/#/n/" + nip19eventid + } + listui.append(new) + print(event.as_json()) + return listui + + async def search(): + table.visible = False + now = Timestamp.now() + taggedusersfrom = [str(word).lstrip('from:') for word in prompt.value.split() if + word.startswith('from:')] + taggedusersto = [str(word).lstrip('to:') for word in prompt.value.split() if word.startswith('to:')] + + search = prompt.value + + tags = [] + for word in taggedusersfrom: + search = str(search).replace(word, "") + user_pubkey = PublicKey.from_bech32(word.replace("@", "")).to_hex() + pTag = ["p", user_pubkey] + tags.append(pTag) + search = str(search).replace("from:", "").replace("to:", "").replace("@", "").lstrip().rstrip() + print(search) + ev = nostr_client_test_search(search, tags) + ui.notify('Request sent to DVM, awaiting results..') + + print("Sent: " + ev.as_json()) + print(str(now.to_human_datetime())) + data.clear() + # table.clear() + listui = await run.io_bound(handledvm, now, ev.id()) + ui.notify("Received results from DVM") + table.clear() + for element in listui: + table.add_rows(element) + + table.visible = True + ui.update(table) + return + + mainrow = ui.row().style('gap:10em').classes("row-1") + maincolumn = ui.column().classes("col-1") + + with mainrow: + with maincolumn: + ui.label('NostrAI Search Page').classes('text-2xl') + prompt = ui.input('Search').style('width: 20em') + + ui.button('Search', on_click=search).style('width: 15em') + # image = ui.image().style('width: 60em') + columns = [ + {'name': 'result', 'label': 'result', 'field': 'result', 'sortable': True, 'align': 'left', }, + {'name': 'time', 'label': 'time', 'field': 'time', 'sortable': True, 'align': 'left'}, + # {'name': 'eventid', 'label': 'eventid', 'field': 'eventid', 'sortable': True, 'align': 'left'}, + ] + + # table = ui.table(columns, rows=data).classes('w-full bordered') + table = ui.table(columns=columns, rows=data, row_key='result', + pagination={'rowsPerPage': 10, 'sortBy': 'time', 'descending': True, 'page': 1}).style( + 'width: 80em') + table.add_slot('header', r''' + + + + {{ col.label }} + + + ''') + table.add_slot('body', r''' + + + + + + {{ col.value }} + + + + + Njump + Highlighter + NoStrudel + + + ''') + + table.on('action', lambda msg: print(msg)) + table.visible = False + +ui.run(reload=True, port=1234)