mirror of https://github.com/skygpu/skynet.git
Compare commits
4 Commits
bb1c82eb66
...
86ed291875
Author | SHA1 | Date |
---|---|---|
|
86ed291875 | |
|
48fc101e26 | |
|
e66f29fa09 | |
|
3954564f20 |
|
@ -77,7 +77,8 @@ explicit = true
|
|||
torch = { index = "torch" }
|
||||
triton = { index = "torch" }
|
||||
torchvision = { index = "torch" }
|
||||
py-leap = { git = "https://github.com/guilledk/py-leap.git", branch = "struct_unwrap" }
|
||||
# py-leap = { git = "https://github.com/guilledk/py-leap.git", branch = "struct_unwrap" }
|
||||
py-leap = { path = "../py-leap", editable = true }
|
||||
pytest-dockerctl = { git = "https://github.com/pikers/pytest-dockerctl.git", branch = "g_update" }
|
||||
|
||||
[build-system]
|
||||
|
|
|
@ -165,7 +165,7 @@ def run(*args, **kwargs):
|
|||
|
||||
@run.command()
|
||||
def db():
|
||||
from .db import open_new_database
|
||||
from skynet.frontend.chatbot.db import open_new_database
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
with open_new_database(cleanup=False) as db_params:
|
||||
|
|
|
@ -32,12 +32,17 @@ class FrontendConfig(msgspec.Struct):
|
|||
account: str
|
||||
permission: str
|
||||
key: str
|
||||
node_url: str
|
||||
hyperion_url: str
|
||||
ipfs_url: str
|
||||
token: str
|
||||
db_host: str
|
||||
db_user: str
|
||||
db_pass: str
|
||||
db_name: str = 'skynet'
|
||||
node_url: str = 'https://testnet.telos.net'
|
||||
hyperion_url: str = 'https://testnet.skygpu.net'
|
||||
ipfs_domain: str = 'ipfs.skygpu.net'
|
||||
explorer_domain: str = 'explorer.skygpu.net'
|
||||
request_timeout: int = 60 * 3
|
||||
proto_version: int = 0
|
||||
reward: str = '20.0000 GPU'
|
||||
receiver: str = 'gpu.scd'
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import random
|
||||
|
||||
from ..constants import *
|
||||
from ..constants import (
|
||||
MODELS,
|
||||
get_model_by_shortname,
|
||||
MAX_STEP, MIN_STEP, MAX_WIDTH, MAX_HEIGHT, MAX_GUIDANCE
|
||||
)
|
||||
|
||||
|
||||
class ConfigRequestFormatError(BaseException):
|
||||
|
@ -26,17 +30,17 @@ class ConfigSizeDivisionByEight(BaseException):
|
|||
def validate_user_config_request(req: str):
|
||||
params = req.split(' ')
|
||||
|
||||
if len(params) < 3:
|
||||
if len(params) < 2:
|
||||
raise ConfigRequestFormatError('config request format incorrect')
|
||||
|
||||
else:
|
||||
try:
|
||||
attr = params[1]
|
||||
attr = params[0]
|
||||
|
||||
match attr:
|
||||
case 'model' | 'algo':
|
||||
attr = 'model'
|
||||
val = params[2]
|
||||
val = params[1]
|
||||
shorts = [model_info.short for model_info in MODELS.values()]
|
||||
if val not in shorts:
|
||||
raise ConfigUnknownAlgorithm(f'no model named {val}')
|
||||
|
@ -44,38 +48,38 @@ def validate_user_config_request(req: str):
|
|||
val = get_model_by_shortname(val)
|
||||
|
||||
case 'step':
|
||||
val = int(params[2])
|
||||
val = int(params[1])
|
||||
val = max(min(val, MAX_STEP), MIN_STEP)
|
||||
|
||||
case 'width':
|
||||
val = max(min(int(params[2]), MAX_WIDTH), 16)
|
||||
val = max(min(int(params[1]), MAX_WIDTH), 16)
|
||||
if val % 8 != 0:
|
||||
raise ConfigSizeDivisionByEight(
|
||||
'size must be divisible by 8!')
|
||||
|
||||
case 'height':
|
||||
val = max(min(int(params[2]), MAX_HEIGHT), 16)
|
||||
val = max(min(int(params[1]), MAX_HEIGHT), 16)
|
||||
if val % 8 != 0:
|
||||
raise ConfigSizeDivisionByEight(
|
||||
'size must be divisible by 8!')
|
||||
|
||||
case 'seed':
|
||||
val = params[2]
|
||||
val = params[1]
|
||||
if val == 'auto':
|
||||
val = None
|
||||
else:
|
||||
val = int(params[2])
|
||||
val = int(params[1])
|
||||
|
||||
case 'guidance':
|
||||
val = float(params[2])
|
||||
val = float(params[1])
|
||||
val = max(min(val, MAX_GUIDANCE), 0)
|
||||
|
||||
case 'strength':
|
||||
val = float(params[2])
|
||||
val = float(params[1])
|
||||
val = max(min(val, 0.99), 0.01)
|
||||
|
||||
case 'upscaler':
|
||||
val = params[2]
|
||||
val = params[1]
|
||||
if val == 'off':
|
||||
val = None
|
||||
elif val != 'x4':
|
||||
|
@ -83,7 +87,7 @@ def validate_user_config_request(req: str):
|
|||
f'\"{val}\" is not a valid upscaler')
|
||||
|
||||
case 'autoconf':
|
||||
val = params[2]
|
||||
val = params[1]
|
||||
if val == 'on':
|
||||
val = True
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ from skynet.constants import (
|
|||
HELP_TOPICS,
|
||||
HELP_UNKWNOWN_PARAM,
|
||||
COOL_WORDS,
|
||||
DONATION_INFO
|
||||
DONATION_INFO,
|
||||
UNKNOWN_CMD_TEXT
|
||||
)
|
||||
from skynet.frontend import validate_user_config_request
|
||||
from skynet.frontend.chatbot.db import FrontendUserDB
|
||||
|
@ -54,7 +55,11 @@ def perform_auto_conf(config: dict) -> dict:
|
|||
|
||||
|
||||
def sanitize_params(params: dict) -> dict:
|
||||
if 'seed' not in params:
|
||||
if (
|
||||
'seed' not in params
|
||||
or
|
||||
params['seed'] is None
|
||||
):
|
||||
params['seed'] = randint(0, 0xffffffff)
|
||||
|
||||
s_params = {}
|
||||
|
@ -82,7 +87,8 @@ class BaseChatbot(ABC):
|
|||
self.config = config
|
||||
self.ipfs = AsyncIPFSHTTP(config.ipfs_url)
|
||||
self.cleos = CLEOS(endpoint=config.node_url)
|
||||
self.cleos.load_abi('gpu.scd', GPU_CONTRACT_ABI)
|
||||
self.cleos.load_abi(config.receiver, GPU_CONTRACT_ABI)
|
||||
self.cleos.import_key(config.account, config.key)
|
||||
self.hyperion = HyperionAPI(config.hyperion_url)
|
||||
|
||||
async def init(self):
|
||||
|
@ -117,9 +123,12 @@ class BaseChatbot(ABC):
|
|||
'''
|
||||
...
|
||||
|
||||
async def create_status_msg(self, msg: BaseMessage, init_text: str) -> tuple[BaseUser, BaseMessage, dict]:
|
||||
async def create_status_msg(self, msg: BaseMessage, init_text: str, force_user: BaseUser | None = None) -> tuple[BaseUser, BaseMessage, dict]:
|
||||
# maybe init user
|
||||
user = msg.author
|
||||
if force_user:
|
||||
user = force_user
|
||||
|
||||
user_row = await self.db.get_or_create_user(user.id)
|
||||
|
||||
# create status msg
|
||||
|
@ -151,7 +160,7 @@ class BaseChatbot(ABC):
|
|||
...
|
||||
|
||||
@abstractmethod
|
||||
async def update_request_status_step_0(self, status_msg: BaseMessage, user: BaseUser):
|
||||
async def update_request_status_step_0(self, status_msg: BaseMessage, user_msg: BaseMessage):
|
||||
'''
|
||||
First step in request status message lifecycle, should notify which user sent the request
|
||||
and that we are about to broadcast the request to chain
|
||||
|
@ -196,19 +205,24 @@ class BaseChatbot(ABC):
|
|||
|
||||
async def handle_request(
|
||||
self,
|
||||
msg: BaseMessage
|
||||
msg: BaseMessage,
|
||||
force_user: BaseUser | None = None
|
||||
):
|
||||
if msg.chat.is_private:
|
||||
return
|
||||
|
||||
if len(msg.text) == 0:
|
||||
await self.reply_to(msg.id, 'empty prompt ignored.')
|
||||
if (
|
||||
len(msg.text) == 0
|
||||
and
|
||||
msg.command != BaseCommands.REDO
|
||||
):
|
||||
await self.reply_to(msg, 'empty prompt ignored.')
|
||||
return
|
||||
|
||||
# maybe initialize user db row and send a new msg thats gonna
|
||||
# be updated throughout the request lifecycle
|
||||
user, status_msg, user_row = await self.create_status_msg(
|
||||
msg, 'started processing a {msg.command} request...')
|
||||
msg, f'started processing a {msg.command} request...', force_user=force_user)
|
||||
|
||||
# if this is a redo msg, we attempt to get the input params from db
|
||||
# else use msg properties
|
||||
|
@ -224,18 +238,28 @@ class BaseChatbot(ABC):
|
|||
inputs = await self.db.get_last_inputs_of(user.id)
|
||||
|
||||
if not prompt:
|
||||
await self.reply_to(msg.id, 'no last prompt found, try doing a non-redo request first')
|
||||
await self.reply_to(msg, 'no last prompt found, try doing a non-redo request first')
|
||||
return
|
||||
|
||||
case _:
|
||||
await self.reply_to(msg.id, f'unknown request of type {msg.command}')
|
||||
await self.reply_to(msg, f'unknown request of type {msg.command}')
|
||||
return
|
||||
|
||||
if (
|
||||
msg.command == BaseCommands.IMG2IMG
|
||||
and
|
||||
len(inputs) == 0
|
||||
):
|
||||
await self.edit_msg(status_msg, 'seems you tried to do an img2img command without sending image')
|
||||
return
|
||||
|
||||
# maybe apply recomended settings to this request
|
||||
del user_row['id']
|
||||
if user_row['autoconf']:
|
||||
user_row = perform_auto_conf(user_row)
|
||||
|
||||
user_row = sanitize_params(user_row)
|
||||
|
||||
body = BodyV0(
|
||||
method=command,
|
||||
params=BodyV0Params(
|
||||
|
@ -247,7 +271,7 @@ class BaseChatbot(ABC):
|
|||
# publish inputs to ipfs
|
||||
input_cids = []
|
||||
for i in inputs:
|
||||
i.publish()
|
||||
await i.publish(self.ipfs, user_row)
|
||||
input_cids.append(i.cid)
|
||||
|
||||
inputs_str = ','.join((i for i in input_cids))
|
||||
|
@ -261,22 +285,25 @@ class BaseChatbot(ABC):
|
|||
last_inputs=inputs
|
||||
)
|
||||
|
||||
await self.update_request_status_step_0(status_msg)
|
||||
await self.update_request_status_step_0(status_msg, msg)
|
||||
|
||||
# prepare and send enqueue request
|
||||
request_time = datetime.now().isoformat()
|
||||
str_body = msgspec.json.encode(body).decode('utf-8')
|
||||
|
||||
enqueue_receipt = await self.cleos.a_push_action(
|
||||
self.receiver,
|
||||
self.config.receiver,
|
||||
'enqueue',
|
||||
(
|
||||
[
|
||||
self.config.account,
|
||||
str_body,
|
||||
inputs_str,
|
||||
self.config.reward,
|
||||
1
|
||||
)
|
||||
],
|
||||
self.config.account,
|
||||
key=self.cleos.private_keys[self.config.account],
|
||||
permission=self.config.permission
|
||||
)
|
||||
|
||||
await self.update_request_status_step_1(status_msg, enqueue_receipt)
|
||||
|
@ -295,7 +322,7 @@ class BaseChatbot(ABC):
|
|||
'''
|
||||
request_id, nonce = console_lines[-1].rstrip().split(':')
|
||||
request_hash = sha256(
|
||||
(nonce + str_body + inputs_str).encode('utf-8')).hexdigest.upper()
|
||||
(nonce + str_body + inputs_str).encode('utf-8')).hexdigest().upper()
|
||||
|
||||
request_id = int(request_id)
|
||||
|
||||
|
@ -343,13 +370,14 @@ class BaseChatbot(ABC):
|
|||
data = action['act']['data']
|
||||
result_cid = data['ipfs_hash']
|
||||
worker = data['worker']
|
||||
logging.info('found matching submit! tx: {submit_tx_hash} cid: {ipfs_hash}')
|
||||
logging.info(f'found matching submit! tx: {submit_tx_hash} cid: {result_cid}')
|
||||
break
|
||||
|
||||
except json.JSONDecodeError:
|
||||
if i < self.config.request_timeout:
|
||||
logging.error('network error while searching for submit, retry...')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# if we found matching submit submit_tx_hash, worker, and result_cid will not be None
|
||||
if not result_cid:
|
||||
|
@ -365,7 +393,7 @@ class BaseChatbot(ABC):
|
|||
result_img = None
|
||||
if get_img_response and get_img_response.status_code == 200:
|
||||
try:
|
||||
with Image.open(io.BytesIO(get_img_response.raw)) as img:
|
||||
with Image.open(io.BytesIO(get_img_response.read())) as img:
|
||||
w, h = img.size
|
||||
|
||||
if (
|
||||
|
@ -386,7 +414,7 @@ class BaseChatbot(ABC):
|
|||
logging.warning(f'couldn\'t get ipfs result at {result_link}!')
|
||||
|
||||
await self.update_request_status_final(
|
||||
msg, status_msg, user, body, inputs, submit_tx_hash, worker, result_img)
|
||||
msg, status_msg, user, body.params, inputs, submit_tx_hash, worker, result_img)
|
||||
|
||||
await self.db.increment_generated(user.id)
|
||||
|
||||
|
@ -442,7 +470,14 @@ class BaseChatbot(ABC):
|
|||
await self.reply_to(msg, DONATION_INFO)
|
||||
|
||||
async def say(self, msg: BaseMessage):
|
||||
if not msg.chat.is_private or not msg.author.is_admin:
|
||||
if (
|
||||
msg.chat.is_private
|
||||
or
|
||||
not msg.author.is_admin
|
||||
):
|
||||
return
|
||||
|
||||
await self.new_msg(self.main_group, msg.text)
|
||||
|
||||
async def echo_unknown(self, msg: BaseMessage):
|
||||
await self.reply_to(msg, UNKNOWN_CMD_TEXT)
|
||||
|
|
|
@ -59,25 +59,6 @@ CREATE TABLE IF NOT EXISTS skynet.user_requests(
|
|||
);
|
||||
"""
|
||||
|
||||
def try_decode_uid(uid: str) -> tuple[str | None, int | None]:
|
||||
"""
|
||||
Attempts to decode the user ID. The user ID can be just an integer
|
||||
or of the format 'proto+uid'. Returns (None, int) if it's just an
|
||||
integer or (proto, int) if it's 'proto+uid'. Returns (None, None)
|
||||
if neither format is valid.
|
||||
"""
|
||||
try:
|
||||
return None, int(uid)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
proto, uid_str = uid.split("+", 1)
|
||||
return proto, int(uid_str)
|
||||
except ValueError:
|
||||
logging.warning(f"Got non-chat-proto UID?: {uid}")
|
||||
return None, None
|
||||
|
||||
|
||||
@cm
|
||||
def open_new_database(cleanup: bool = True):
|
||||
|
@ -118,14 +99,17 @@ def open_new_database(cleanup: bool = True):
|
|||
time.sleep(1)
|
||||
logging.info("Creating 'skynet' database...")
|
||||
|
||||
with psycopg2.connect(
|
||||
conn = psycopg2.connect(
|
||||
user="postgres", password=root_password, host="localhost", port=port
|
||||
) as conn:
|
||||
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(f"CREATE USER skynet WITH PASSWORD '{skynet_password}'")
|
||||
cursor.execute("CREATE DATABASE skynet")
|
||||
cursor.execute("GRANT ALL PRIVILEGES ON DATABASE skynet TO skynet")
|
||||
)
|
||||
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
conn.autocommit = True
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(f"CREATE USER skynet WITH PASSWORD '{skynet_password}'")
|
||||
cursor.execute("CREATE DATABASE skynet")
|
||||
cursor.execute("GRANT ALL PRIVILEGES ON DATABASE skynet TO skynet")
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
logging.info("Database setup complete.")
|
||||
yield container, skynet_password, db_host
|
||||
|
@ -204,7 +188,7 @@ class FrontendUserDB:
|
|||
records = await conn.fetch(
|
||||
"SELECT * FROM skynet.user_config WHERE id = $1", user_id
|
||||
)
|
||||
return records[0] if len(records) == 1 else None
|
||||
return dict(records[0]) if len(records) == 1 else None
|
||||
|
||||
async def get_user(self, user_id: int):
|
||||
"""Alias for get_user_config (same data returned)."""
|
||||
|
@ -430,7 +414,7 @@ class FrontendUserDB:
|
|||
)
|
||||
|
||||
if not last_inputs_str:
|
||||
return None
|
||||
return []
|
||||
|
||||
last_inputs = []
|
||||
for i in last_inputs_str.split(','):
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import json
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from typing import Self, Awaitable
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from telebot.types import (
|
||||
AsyncTeleBot,
|
||||
User as TGUser,
|
||||
Chat as TGChat,
|
||||
PhotoSize as TGPhotoSize,
|
||||
Message as TGMessage,
|
||||
CallbackQuery,
|
||||
InputMediaPhoto,
|
||||
InlineKeyboardButton,
|
||||
InlineKeyboardMarkup
|
||||
)
|
||||
from telebot.async_telebot import ExceptionHandler
|
||||
from telebot.async_telebot import AsyncTeleBot, ExceptionHandler
|
||||
from telebot.formatting import hlink
|
||||
|
||||
from skynet.types import BodyV0Params
|
||||
|
@ -21,7 +23,7 @@ from skynet.config import FrontendConfig
|
|||
from skynet.constants import VERSION
|
||||
from skynet.frontend.chatbot import BaseChatbot
|
||||
from skynet.frontend.chatbot.db import FrontendUserDB
|
||||
from skynet.frontend.types import (
|
||||
from skynet.frontend.chatbot.types import (
|
||||
BaseUser,
|
||||
BaseChatRoom,
|
||||
BaseFileInput,
|
||||
|
@ -30,6 +32,7 @@ from skynet.frontend.types import (
|
|||
)
|
||||
|
||||
GROUP_ID = -1001541979235
|
||||
TEST_GROUP_ID = -4099622703
|
||||
ADMIN_USER_ID = 383385940
|
||||
|
||||
|
||||
|
@ -100,6 +103,9 @@ class TelegramFileInput(BaseFileInput):
|
|||
|
||||
raise ValueError
|
||||
|
||||
def set_cid(self, cid: str):
|
||||
self._cid = cid
|
||||
|
||||
async def download(self, bot: AsyncTeleBot) -> bytes:
|
||||
file_path = (await bot.get_file(self.id)).file_path
|
||||
self._raw = await bot.download_file(file_path)
|
||||
|
@ -112,6 +118,9 @@ class TelegramMessage(BaseMessage):
|
|||
self._msg = msg
|
||||
self._cmd = cmd
|
||||
self._chat = TelegramChatRoom(msg.chat)
|
||||
self._inputs: list[TelegramFileInput] | None = None
|
||||
|
||||
self._author = None
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
|
@ -123,10 +132,17 @@ class TelegramMessage(BaseMessage):
|
|||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
return self._msg.text[len(self._cmd) + 1:]
|
||||
# remove command name, slash and first space
|
||||
if self._msg.text:
|
||||
return self._msg.text[len(self._cmd) + 2:]
|
||||
|
||||
return self._msg.caption[len(self._cmd) + 2:]
|
||||
|
||||
@property
|
||||
def author(self) -> TelegramUser:
|
||||
if self._author:
|
||||
return self._author
|
||||
|
||||
return TelegramUser(self._msg.from_user)
|
||||
|
||||
@property
|
||||
|
@ -135,10 +151,15 @@ class TelegramMessage(BaseMessage):
|
|||
|
||||
@property
|
||||
def inputs(self) -> list[TelegramFileInput]:
|
||||
return [
|
||||
TelegramFileInput(photo=p)
|
||||
for p in self._msg.photo
|
||||
]
|
||||
if self._inputs is None:
|
||||
self._inputs = []
|
||||
if self._msg.photo:
|
||||
self._inputs = [
|
||||
TelegramFileInput(photo=p)
|
||||
for p in self._msg.photo
|
||||
]
|
||||
|
||||
return self._inputs
|
||||
|
||||
|
||||
# generic tg utils
|
||||
|
@ -186,19 +207,18 @@ def prepare_metainfo_caption(user: TelegramUser, worker: str, reward: str, param
|
|||
|
||||
|
||||
def generate_reply_caption(
|
||||
config: FrontendConfig,
|
||||
user: TelegramUser,
|
||||
params: BodyV0Params,
|
||||
tx_hash: str,
|
||||
worker: str,
|
||||
reward: str,
|
||||
explorer_domain: str
|
||||
):
|
||||
explorer_link = hlink(
|
||||
'SKYNET Transaction Explorer',
|
||||
f'https://{explorer_domain}/v2/explore/transaction/{tx_hash}'
|
||||
f'https://{config.explorer_domain}/v2/explore/transaction/{tx_hash}'
|
||||
)
|
||||
|
||||
meta_info = prepare_metainfo_caption(user, worker, reward, params)
|
||||
meta_info = prepare_metainfo_caption(user, worker, config.reward, params)
|
||||
|
||||
final_msg = '\n'.join([
|
||||
'Worker finished your task!',
|
||||
|
@ -239,14 +259,43 @@ class TelegramChatbot(BaseChatbot):
|
|||
append_handler(bot, BaseCommands.SAY, self.say)
|
||||
|
||||
append_handler(bot, BaseCommands.TXT2IMG, self.handle_request)
|
||||
|
||||
append_handler(bot, BaseCommands.IMG2IMG, self.handle_request)
|
||||
|
||||
@bot.message_handler(func=lambda _: True, content_types=['photo', 'document'])
|
||||
async def handle_img2img(tg_msg: TGMessage):
|
||||
msg = TelegramMessage(cmd='img2img', msg=tg_msg)
|
||||
for file in msg.inputs:
|
||||
await file.download(bot)
|
||||
await self.handle_request(msg)
|
||||
|
||||
append_handler(bot, BaseCommands.REDO, self.handle_request)
|
||||
|
||||
@bot.message_handler(func=lambda _: True)
|
||||
async def unknown_cmd(tg_msg: TGMessage):
|
||||
if tg_msg.text[0] == '/':
|
||||
msg = TelegramMessage(cmd='unknown', msg=tg_msg)
|
||||
await self.echo_unknown(msg)
|
||||
|
||||
@bot.callback_query_handler(func=lambda _: True)
|
||||
async def callback_query(call: CallbackQuery):
|
||||
call_json = json.loads(call.data)
|
||||
method = call_json.get('method')
|
||||
match method:
|
||||
case 'redo':
|
||||
msg = await self.new_msg(self.main_group, 'processing a redo request...')
|
||||
msg._cmd = 'redo'
|
||||
await self.handle_request(msg, force_user=TelegramUser(user=call.from_user))
|
||||
await bot.delete_message(chat_id=self.main_group.id, message_id=msg.id)
|
||||
|
||||
self.bot = bot
|
||||
|
||||
self._main_room: TelegramChatRoom | None = None
|
||||
|
||||
async def init(self):
|
||||
tg_group = await self.bot.get_chat(GROUP_ID)
|
||||
tg_group = await self.bot.get_chat(TEST_GROUP_ID)
|
||||
self._main_room = TelegramChatRoom(chat=tg_group)
|
||||
logging.info('initialized')
|
||||
|
||||
async def run(self):
|
||||
await self.init()
|
||||
|
@ -281,14 +330,14 @@ class TelegramChatbot(BaseChatbot):
|
|||
f'\n[{timestamp_pretty()}] <b>timeout processing request</b>',
|
||||
)
|
||||
|
||||
async def update_request_status_step_0(self, status_msg: TelegramMessage):
|
||||
async def update_request_status_step_0(self, status_msg: TelegramMessage, user_msg: TelegramMessage):
|
||||
'''
|
||||
First step in request status message lifecycle, should notify which user sent the request
|
||||
and that we are about to broadcast the request to chain
|
||||
'''
|
||||
await self.update_status_msg(
|
||||
status_msg,
|
||||
f'processing a \'{status_msg.command}\' request by {status_msg.author.name}\n'
|
||||
f'processing a \'{user_msg.command}\' request by {user_msg.author.name}\n'
|
||||
f'[{timestamp_pretty()}] <i>broadcasting transaction to chain...</i>'
|
||||
)
|
||||
|
||||
|
@ -300,7 +349,7 @@ class TelegramChatbot(BaseChatbot):
|
|||
enqueue_tx_id = tx_result['transaction_id']
|
||||
enqueue_tx_link = hlink(
|
||||
'Your request on Skynet Explorer',
|
||||
f'https://{self.explorer_domain}/v2/explore/transaction/{enqueue_tx_id}'
|
||||
f'https://{self.config.explorer_domain}/v2/explore/transaction/{enqueue_tx_id}'
|
||||
)
|
||||
await self.append_status_msg(
|
||||
status_msg,
|
||||
|
@ -316,7 +365,7 @@ class TelegramChatbot(BaseChatbot):
|
|||
'''
|
||||
tx_link = hlink(
|
||||
'Your result on Skynet Explorer',
|
||||
f'https://{self.explorer_domain}/v2/explore/transaction/{submit_tx_hash}'
|
||||
f'https://{self.config.explorer_domain}/v2/explore/transaction/{submit_tx_hash}'
|
||||
)
|
||||
await self.append_status_msg(
|
||||
status_msg,
|
||||
|
@ -342,7 +391,7 @@ class TelegramChatbot(BaseChatbot):
|
|||
reply caption and if provided also sent the found result img
|
||||
'''
|
||||
caption = generate_reply_caption(
|
||||
user, worker, self.config.reward, params)
|
||||
self.config, user, params, submit_tx_hash, worker)
|
||||
|
||||
await self.bot.delete_message(
|
||||
chat_id=status_msg.chat.id,
|
||||
|
@ -364,8 +413,8 @@ class TelegramChatbot(BaseChatbot):
|
|||
parse_mode='HTML'
|
||||
)
|
||||
|
||||
case 1:
|
||||
_input = inputs.pop()
|
||||
case _:
|
||||
_input = inputs[-1]
|
||||
await self.bot.send_media_group(
|
||||
status_msg.chat.id,
|
||||
media=[
|
||||
|
@ -373,6 +422,3 @@ class TelegramChatbot(BaseChatbot):
|
|||
InputMediaPhoto(result_img, caption=caption, parse_mode='HTML')
|
||||
]
|
||||
)
|
||||
|
||||
case _:
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import io
|
||||
|
||||
from ABC import ABC, abstractproperty, abstractmethod
|
||||
from abc import ABC, abstractproperty, abstractmethod
|
||||
from enum import StrEnum
|
||||
from typing import Self
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
|
||||
from skynet.ipfs import AsyncIPFSHTTP
|
||||
|
@ -52,6 +53,10 @@ class BaseFileInput(ABC):
|
|||
async def download(self, *args) -> bytes:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def set_cid(self, cid: str):
|
||||
...
|
||||
|
||||
async def publish(self, ipfs_api: AsyncIPFSHTTP, user_row: dict):
|
||||
with Image.open(io.BytesIO(self._raw)) as img:
|
||||
w, h = img.size
|
||||
|
@ -63,11 +68,12 @@ class BaseFileInput(ABC):
|
|||
):
|
||||
img.thumbnail((user_row['width'], user_row['height']))
|
||||
|
||||
img_path = '/tmp/ipfs-staging/img.png'
|
||||
img_path = Path('/tmp/ipfs-staging/img.png')
|
||||
img.save(img_path, format='PNG')
|
||||
|
||||
ipfs_info = await ipfs_api.add(img_path)
|
||||
ipfs_hash = ipfs_info['Hash']
|
||||
self.set_cid(ipfs_hash)
|
||||
await ipfs_api.pin(ipfs_hash)
|
||||
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ class BodyV0Params(Struct):
|
|||
strength: str | float | None = None
|
||||
output_type: str | None = 'png'
|
||||
upscaler: str | None = None
|
||||
autoconf: bool | None = None
|
||||
|
||||
|
||||
class BodyV0(Struct):
|
||||
|
|
48
uv.lock
48
uv.lock
|
@ -1,4 +1,5 @@
|
|||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.10, <3.13"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12' and sys_platform == 'darwin'",
|
||||
|
@ -271,7 +272,7 @@ name = "cffi"
|
|||
version = "1.17.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pycparser" },
|
||||
{ name = "pycparser", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
|
||||
wheels = [
|
||||
|
@ -1271,7 +1272,7 @@ name = "nvidia-cudnn-cu12"
|
|||
version = "9.1.0.70"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas-cu12" },
|
||||
{ name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 },
|
||||
|
@ -1298,9 +1299,9 @@ name = "nvidia-cusolver-cu12"
|
|||
version = "11.4.5.107"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas-cu12" },
|
||||
{ name = "nvidia-cusparse-cu12" },
|
||||
{ name = "nvidia-nvjitlink-cu12" },
|
||||
{ name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/1d/8de1e5c67099015c834315e333911273a8c6aaba78923dd1d1e25fc5f217/nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd", size = 124161928 },
|
||||
|
@ -1311,7 +1312,7 @@ name = "nvidia-cusparse-cu12"
|
|||
version = "12.1.0.106"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-nvjitlink-cu12" },
|
||||
{ name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/65/5b/cfaeebf25cd9fdec14338ccb16f6b2c4c7fa9163aefcf057d86b9cc248bb/nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c", size = 195958278 },
|
||||
|
@ -1598,7 +1599,7 @@ wheels = [
|
|||
[[package]]
|
||||
name = "py-leap"
|
||||
version = "0.1a35"
|
||||
source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#18b3c73e724922a060db5f8ea2b9d9727b6152cc" }
|
||||
source = { editable = "../py-leap" }
|
||||
dependencies = [
|
||||
{ name = "base58" },
|
||||
{ name = "cryptos" },
|
||||
|
@ -1608,6 +1609,33 @@ dependencies = [
|
|||
{ name = "ripemd-hash" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "base58", specifier = ">=2.1.1,<3" },
|
||||
{ name = "cryptos", specifier = ">=2.0.9,<3" },
|
||||
{ name = "httpx", specifier = ">=0.28.1,<0.29" },
|
||||
{ name = "msgspec", specifier = ">=0.19.0" },
|
||||
{ name = "requests", specifier = "<2.32.0" },
|
||||
{ name = "ripemd-hash", specifier = ">=1.0.1,<2" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "docker", specifier = ">=6.1.3,<7" },
|
||||
{ name = "pdbpp", specifier = ">=0.10.3,<0.11" },
|
||||
{ name = "pytest", specifier = ">=8.3.4,<9" },
|
||||
{ name = "pytest-trio", specifier = ">=0.8.0,<0.9" },
|
||||
]
|
||||
docs = [
|
||||
{ name = "sphinx", specifier = "==7.1.2" },
|
||||
{ name = "sphinx-rtd-theme", specifier = "==1.3.0" },
|
||||
]
|
||||
snaps = [
|
||||
{ name = "bs4", specifier = ">=0.0.2,<0.0.3" },
|
||||
{ name = "tdqm", specifier = ">=0.0.1,<0.0.2" },
|
||||
{ name = "zstandard", specifier = ">=0.21.0,<0.22" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.22"
|
||||
|
@ -1633,8 +1661,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/61/74/49f5d20c514ccc631b940cc9dfec45dcce418dc84a98463a2e2ebec33904/pycryptodomex-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:52e23a0a6e61691134aa8c8beba89de420602541afaae70f66e16060fdcd677e", size = 2257982 },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/4b/d33ef74e2cc0025a259936661bb53432c5bbbadc561c5f2e023bcd73ce4c/pycryptodomex-3.21.0-cp36-abi3-win32.whl", hash = "sha256:a3d77919e6ff56d89aada1bd009b727b874d464cb0e2e3f00a49f7d2e709d76e", size = 1779052 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/be/7c991840af1184009fc86267160948350d1bf875f153c97bb471ad944e40/pycryptodomex-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b0e9765f93fe4890f39875e6c90c96cb341767833cfa767f41b490b506fa9ec0", size = 1816307 },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/ac/24125ad36778914a36f08d61ba5338cb9159382c638d9761ee19c8de822c/pycryptodomex-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:feaecdce4e5c0045e7a287de0c4351284391fe170729aa9182f6bd967631b3a8", size = 1694999 },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/73/be7a54a5903508070e5508925ba94493a1f326cfeecfff750e3eb250ea28/pycryptodomex-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:365aa5a66d52fd1f9e0530ea97f392c48c409c2f01ff8b9a39c73ed6f527d36c", size = 1769437 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/9f/39a6187f3986841fa6a9f35c6fdca5030ef73ff708b45a993813a51d7d10/pycryptodomex-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3efddfc50ac0ca143364042324046800c126a1d63816d532f2e19e6f2d8c0c31", size = 1619607 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/70/60bb08e9e9841b18d4669fb69d84b64ce900aacd7eb0ebebd4c7b9bdecd3/pycryptodomex-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df2608682db8279a9ebbaf05a72f62a321433522ed0e499bc486a6889b96bf3", size = 1653571 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/6f/191b73509291c5ff0dddec9cc54797b1d73303c12b2e4017b24678e57099/pycryptodomex-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5823d03e904ea3e53aebd6799d6b8ec63b7675b5d2f4a4bd5e3adcb512d03b37", size = 1691548 },
|
||||
|
@ -2126,7 +2152,7 @@ requires-dist = [
|
|||
{ name = "outcome", specifier = ">=1.3.0.post0" },
|
||||
{ name = "pillow", specifier = ">=10.0.1,<11" },
|
||||
{ name = "protobuf", specifier = ">=5.29.3,<6" },
|
||||
{ name = "py-leap", git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap" },
|
||||
{ name = "py-leap", editable = "../py-leap" },
|
||||
{ name = "pytz", specifier = "~=2023.3.post1" },
|
||||
{ name = "toml", specifier = ">=0.10.2,<0.11" },
|
||||
{ name = "trio", specifier = ">=0.22.2,<0.23" },
|
||||
|
@ -2436,7 +2462,7 @@ name = "triton"
|
|||
version = "3.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "filelock" },
|
||||
{ name = "filelock", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/98/29/69aa56dc0b2eb2602b553881e34243475ea2afd9699be042316842788ff5/triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8", size = 209460013 },
|
||||
|
|
Loading…
Reference in New Issue