From dbbfbd638ff98ed1ddde8880ecaaa5a0061ca4e6 Mon Sep 17 00:00:00 2001 From: Aaron Crate Date: Fri, 26 Jun 2026 19:24:36 -0500 Subject: [PATCH] ruff format --- etc/moneybot.py | 36 +++++++--- garfmain.py | 17 ++--- garfpy/aod.py | 5 +- garfpy/garfai.py | 171 +++++++++++++++++++++++++++++++++++------------ garfpy/qr.py | 4 +- jonbot/jonbot.py | 18 ++--- 6 files changed, 176 insertions(+), 75 deletions(-) diff --git a/etc/moneybot.py b/etc/moneybot.py index 428aef1..4412ddd 100644 --- a/etc/moneybot.py +++ b/etc/moneybot.py @@ -13,26 +13,40 @@ intents.messages = True intents.message_content = True client = discord.Client(intents=intents) + @client.event async def on_ready(): print(f"Logged in as {client.user.name} running {model}.", flush=True) + @client.event async def on_message(message): if message.author == client.user: return - if message.content.lower().startswith("hey money") or isinstance(message.channel, discord.DMChannel): - question = message.content[9:] if message.content.lower().startswith("hey money") else message.content + if message.content.lower().startswith("hey money") or isinstance( + message.channel, discord.DMChannel + ): + question = ( + message.content[9:] + if message.content.lower().startswith("hey money") + else message.content + ) try: response = openai.ChatCompletion.create( model=model, messages=[ - {"role": "system", "content": "Pretend you are eccentric conspiracy theorist Planetside 2 gamer named Dr. Moneypants."}, - {"role": "user", "content": f"{question} please keep it short with religious undertones"} - ], - max_tokens=400 + { + "role": "system", + "content": "Pretend you are eccentric conspiracy theorist Planetside 2 gamer named Dr. Moneypants.", + }, + { + "role": "user", + "content": f"{question} please keep it short with religious undertones", + }, + ], + max_tokens=400, ) - answer = response['choices'][0]['message']['content'] + answer = response["choices"][0]["message"]["content"] answer = answer.replace("an AI language model", "a man of God") answer = answer.replace("language model AI", "man of God") await message.channel.send(answer) @@ -40,14 +54,16 @@ async def on_message(message): e = str(e) await message.channel.send(f"`MoneyBot Error: {e}`") + async def moneybot_connect(): while True: try: await client.start(moneykey) except Exception as e: - e = str(e) - logger.error(f"Moneybot couldn't connect! {e}") - await asyncio.sleep(60) + e = str(e) + logger.error(f"Moneybot couldn't connect! {e}") + await asyncio.sleep(60) + if __name__ == "__main__": asyncio.run(moneybot_connect()) diff --git a/garfmain.py b/garfmain.py index 6e1e5d5..77f06de 100644 --- a/garfmain.py +++ b/garfmain.py @@ -42,18 +42,19 @@ weather = WeatherAPI() URL_PATTERNS = [ - r'https?://(?:www\.)?youtube\.com/watch\?[^\s]*', - r'https?://youtu\.be/[^\s]*', - r'https?://(?:open\.)?spotify\.com/[^\s]*', + r"https?://(?:www\.)?youtube\.com/watch\?[^\s]*", + r"https?://youtu\.be/[^\s]*", + r"https?://(?:open\.)?spotify\.com/[^\s]*", ] + def clean_url(url): try: parsed = urlparse(url) - if 'youtube.com' in parsed.hostname: + if "youtube.com" in parsed.hostname: params = parse_qs(parsed.query) - video_id = params.get('v', [None])[0] + video_id = params.get("v", [None])[0] if not video_id: return None # timestamp = params.get('t', [None])[0] @@ -61,15 +62,15 @@ def clean_url(url): # return f"https://www.youtube.com/watch?v={video_id}&t={timestamp}" return f"https://www.youtube.com/watch?v={video_id}" - if 'youtu.be' in parsed.hostname: + if "youtu.be" in parsed.hostname: return f"https://youtu.be{parsed.path}" - if 'spotify.com' in parsed.hostname: + if "spotify.com" in parsed.hostname: return f"https://open.spotify.com{parsed.path}" except Exception: return None - + @garfbot.event async def on_ready(): diff --git a/garfpy/aod.py b/garfpy/aod.py index 9747e07..aa3bdb8 100644 --- a/garfpy/aod.py +++ b/garfpy/aod.py @@ -121,7 +121,7 @@ async def aod_message(garfbot, message): for field, values in zip(table_fields, table_columns): stats_embed.add_field(name=field, value="\n".join(values), inline=True) await message.channel.send(embed=stats_embed) - + # # Boy You Said It # words = re.findall(r"[a-zA-Z']+", message.content.lower()) # stops = {"a", "an", "the", "and", "or", "but", "is", "it", "in", "on", "at", "to", "of"} @@ -131,7 +131,6 @@ async def aod_message(garfbot, message): # firsts = [w[0] for w in words] # commons = max(set(firsts), key=firsts.count) # count = firsts.count(commons) - + # if count >= 3 or (len(words) >= 2 and count / len(words) >= 0.75): # await message.channel.send("Boy, you said it!") - diff --git a/garfpy/garfai.py b/garfpy/garfai.py index 0b6f35a..a2fc5ea 100644 --- a/garfpy/garfai.py +++ b/garfpy/garfai.py @@ -13,13 +13,31 @@ from garfpy import logger INVOKEAI_BASE = config.INVOKEAI_URL -_MODEL_KEY = "0eb50094-5c9b-431b-ba01-87e145edb849" -_VAE_KEY = "dde3627c-8a45-4088-93d1-66c44acbb337" +_MODEL_KEY = "0eb50094-5c9b-431b-ba01-87e145edb849" +_VAE_KEY = "dde3627c-8a45-4088-93d1-66c44acbb337" _ENCODER_KEY = "7ba22542-4687-4946-a52e-c92f925f4b75" -_MODEL_REF = {"key": _MODEL_KEY, "hash": "blake3:c3ee838d71d99497db01fae6f304eafd9e734e935f3b783e968d50febb56be2c", "name": "FLUX.2 Klein 4B (GGUF Q4)", "base": "flux2", "type": "main"} -_VAE_REF = {"key": _VAE_KEY, "hash": "blake3:531855de70db993d0f6181f82cde27d15411d58b7ffa3b2fdce2b9434c0173c2", "name": "FLUX.2 VAE", "base": "flux2", "type": "vae"} -_ENCODER_REF = {"key": _ENCODER_KEY, "hash": "blake3:af5840e6770dc99f678e69867949c8b9264835915eb82a990e940fa6e4fa6c81", "name": "FLUX.2 Klein Qwen3 4B Encoder", "base": "any", "type": "qwen3_encoder"} +_MODEL_REF = { + "key": _MODEL_KEY, + "hash": "blake3:c3ee838d71d99497db01fae6f304eafd9e734e935f3b783e968d50febb56be2c", + "name": "FLUX.2 Klein 4B (GGUF Q4)", + "base": "flux2", + "type": "main", +} +_VAE_REF = { + "key": _VAE_KEY, + "hash": "blake3:531855de70db993d0f6181f82cde27d15411d58b7ffa3b2fdce2b9434c0173c2", + "name": "FLUX.2 VAE", + "base": "flux2", + "type": "vae", +} +_ENCODER_REF = { + "key": _ENCODER_KEY, + "hash": "blake3:af5840e6770dc99f678e69867949c8b9264835915eb82a990e940fa6e4fa6c81", + "name": "FLUX.2 Klein Qwen3 4B Encoder", + "base": "any", + "type": "qwen3_encoder", +} _POLL_INTERVAL = 2 _POLL_ATTEMPTS = 60 @@ -31,49 +49,103 @@ def _node_id(prefix: str) -> str: def _build_graph(prompt: str) -> dict: - seed = int(time.time() * 1000) % (2 ** 31) + seed = int(time.time() * 1000) % (2**31) - p = _node_id("positive_prompt") - ml = _node_id("flux2_klein_model_loader") - te = _node_id("flux2_klein_text_encoder") - dn = _node_id("flux2_denoise") + p = _node_id("positive_prompt") + ml = _node_id("flux2_klein_model_loader") + te = _node_id("flux2_klein_text_encoder") + dn = _node_id("flux2_denoise") out = _node_id("canvas_output") nodes = { - p: {"id": p, "is_intermediate": True, "use_cache": True, "value": prompt, "type": "string"}, - ml: {"id": ml, "is_intermediate": True, "use_cache": True, "type": "flux2_klein_model_loader", - "model": _MODEL_REF, "vae_model": _VAE_REF, "qwen3_encoder_model": _ENCODER_REF}, - te: {"id": te, "is_intermediate": True, "use_cache": True, "type": "flux2_klein_text_encoder"}, - dn: {"id": dn, "is_intermediate": True, "use_cache": True, "type": "flux2_denoise", "seed": seed}, - out: {"id": out, "is_intermediate": False, "use_cache": False, "type": "flux2_vae_decode"}, + p: { + "id": p, + "is_intermediate": True, + "use_cache": True, + "value": prompt, + "type": "string", + }, + ml: { + "id": ml, + "is_intermediate": True, + "use_cache": True, + "type": "flux2_klein_model_loader", + "model": _MODEL_REF, + "vae_model": _VAE_REF, + "qwen3_encoder_model": _ENCODER_REF, + }, + te: { + "id": te, + "is_intermediate": True, + "use_cache": True, + "type": "flux2_klein_text_encoder", + }, + dn: { + "id": dn, + "is_intermediate": True, + "use_cache": True, + "type": "flux2_denoise", + "seed": seed, + }, + out: { + "id": out, + "is_intermediate": False, + "use_cache": False, + "type": "flux2_vae_decode", + }, } edges = [ - {"source": {"node_id": ml, "field": "qwen3_encoder"}, "destination": {"node_id": te, "field": "qwen3_encoder"}}, - {"source": {"node_id": ml, "field": "max_seq_len"}, "destination": {"node_id": te, "field": "max_seq_len"}}, - {"source": {"node_id": p, "field": "value"}, "destination": {"node_id": te, "field": "prompt"}}, - {"source": {"node_id": ml, "field": "transformer"}, "destination": {"node_id": dn, "field": "transformer"}}, - {"source": {"node_id": ml, "field": "vae"}, "destination": {"node_id": dn, "field": "vae"}}, - {"source": {"node_id": te, "field": "conditioning"}, "destination": {"node_id": dn, "field": "positive_text_conditioning"}}, - {"source": {"node_id": ml, "field": "vae"}, "destination": {"node_id": out, "field": "vae"}}, - {"source": {"node_id": dn, "field": "latents"}, "destination": {"node_id": out, "field": "latents"}}, + { + "source": {"node_id": ml, "field": "qwen3_encoder"}, + "destination": {"node_id": te, "field": "qwen3_encoder"}, + }, + { + "source": {"node_id": ml, "field": "max_seq_len"}, + "destination": {"node_id": te, "field": "max_seq_len"}, + }, + { + "source": {"node_id": p, "field": "value"}, + "destination": {"node_id": te, "field": "prompt"}, + }, + { + "source": {"node_id": ml, "field": "transformer"}, + "destination": {"node_id": dn, "field": "transformer"}, + }, + { + "source": {"node_id": ml, "field": "vae"}, + "destination": {"node_id": dn, "field": "vae"}, + }, + { + "source": {"node_id": te, "field": "conditioning"}, + "destination": {"node_id": dn, "field": "positive_text_conditioning"}, + }, + { + "source": {"node_id": ml, "field": "vae"}, + "destination": {"node_id": out, "field": "vae"}, + }, + { + "source": {"node_id": dn, "field": "latents"}, + "destination": {"node_id": out, "field": "latents"}, + }, ] return {"nodes": nodes, "edges": edges} async def _poll_batch(session: aiohttp.ClientSession, base: str, batch_id: str) -> bool: - """Poll batch status until completed, failed, or timed out. Returns True on success.""" for _ in range(_POLL_ATTEMPTS): await asyncio.sleep(_POLL_INTERVAL) try: - async with session.get(f"{base}/api/v1/queue/default/b/{batch_id}/status") as resp: + async with session.get( + f"{base}/api/v1/queue/default/b/{batch_id}/status" + ) as resp: if not resp.ok: continue s = await resp.json(content_type=None) - total = s.get("total", 0) + total = s.get("total", 0) completed = s.get("completed", 0) - failed = s.get("failed", 0) + failed = s.get("failed", 0) if total > 0 and failed >= total: logger.error(f"Batch {batch_id} failed") return False @@ -84,7 +156,9 @@ async def _poll_batch(session: aiohttp.ClientSession, base: str, batch_id: str) return False -async def _get_image_name(session: aiohttp.ClientSession, base: str, batch_id: str) -> str | None: +async def _get_image_name( + session: aiohttp.ClientSession, base: str, batch_id: str +) -> str | None: try: async with session.get(f"{base}/api/v1/queue/default/i/{batch_id}") as resp: if resp.ok: @@ -110,8 +184,9 @@ async def _get_image_name(session: aiohttp.ClientSession, base: str, batch_id: s return None -async def _fetch_image_bytes(session: aiohttp.ClientSession, base: str, name: str) -> bytes | None: - """Try the full image endpoint, then fall back to thumbnail.""" +async def _fetch_image_bytes( + session: aiohttp.ClientSession, base: str, name: str +) -> bytes | None: urls = [ f"{base}/api/v1/images/i/{name}/full", f"{base}/api/v1/images/i/{name}/thumbnail", @@ -119,9 +194,11 @@ async def _fetch_image_bytes(session: aiohttp.ClientSession, base: str, name: st for url in urls: try: async with session.get(url) as resp: - ct = resp.headers.get("Content-Type", "") + ct = resp.headers.get("Content-Type", "") data = await resp.read() - logger.info(f"Image fetch {url}: status={resp.status} content-type={ct} size={len(data)}") + logger.info( + f"Image fetch {url}: status={resp.status} content-type={ct} size={len(data)}" + ) if "html" not in ct and len(data) >= _MIN_IMAGE_BYTES: return data except Exception as e: @@ -131,10 +208,10 @@ async def _fetch_image_bytes(session: aiohttp.ClientSession, base: str, name: st class GarfAI: def __init__(self): - self.baseurl = config.BASE_URL + self.baseurl = config.BASE_URL self.sysprompt = config.SYSTEM_PROMPT - self.txtmodel = config.TXT_MODEL - self.imgmodel = config.IMG_MODEL + self.txtmodel = config.TXT_MODEL + self.imgmodel = config.IMG_MODEL self._oai = AsyncOpenAI( api_key=config.OPENAI_TOKEN, @@ -145,7 +222,9 @@ class GarfAI: async def garfpic(self, ctx, prompt): await self.image_request_queue.put({"ctx": ctx, "prompt": prompt}) - async def generate_image(self, session: aiohttp.ClientSession, prompt: str) -> bytes | str: + async def generate_image( + self, session: aiohttp.ClientSession, prompt: str + ) -> bytes | str: base = INVOKEAI_BASE try: @@ -157,7 +236,7 @@ class GarfAI: text = await resp.text() logger.error(f"InvokeAI enqueue failed {resp.status}: {text}") return "`GarfBot Error: InvokeAI rejected the request`" - data = await resp.json(content_type=None) + data = await resp.json(content_type=None) batch_id = data["batch"]["batch_id"] except Exception as e: logger.error(f"InvokeAI enqueue error: {e}") @@ -182,19 +261,21 @@ class GarfAI: return "`GarfBot Error: Odie`" async def process_image_requests(self): - async with aiohttp.ClientSession(headers={"Accept": "application/json"}) as session: + async with aiohttp.ClientSession( + headers={"Accept": "application/json"} + ) as session: while True: request = await self.image_request_queue.get() - ctx = request["ctx"] + ctx = request["ctx"] prompt = request["prompt"] result = await self.generate_image(session, prompt) if isinstance(result, bytes): logger.info("Sending image...") - image = io.BytesIO(result) + image = io.BytesIO(result) timestamp = ctx.message.created_at.strftime("%Y%m%d%H%M%S") - filename = f"{timestamp}_generated_image.png" + filename = f"{timestamp}_generated_image.png" try: await ctx.reply(file=discord.File(fp=image, filename=filename)) except Exception as e: @@ -211,7 +292,7 @@ class GarfAI: model=self.txtmodel, messages=[ {"role": "system", "content": self.sysprompt}, - {"role": "user", "content": question}, + {"role": "user", "content": question}, ], max_tokens=400, temperature=1.2, @@ -231,7 +312,9 @@ class GarfAI: async def wikisum(self, query: str) -> str: try: summary = wikipedia.summary(query) - return await self.generate_chat(f"Please summarize in your own words: {summary}") + return await self.generate_chat( + f"Please summarize in your own words: {summary}" + ) except wikipedia.exceptions.DisambiguationError as e: options = ", ".join(e.options[:3]) return f"`GarfBot Error: Ambiguous query — did you mean: {options}?`" diff --git a/garfpy/qr.py b/garfpy/qr.py index 0779241..31a7028 100644 --- a/garfpy/qr.py +++ b/garfpy/qr.py @@ -47,7 +47,7 @@ async def generate_qr(text): qr = qrcode.QRCode( version=version, - error_correction=qrcode.constants.ERROR_CORRECT_L, # type: ignore + error_correction=qrcode.constants.ERROR_CORRECT_L, # type: ignore box_size=box_size, border=4, ) @@ -58,7 +58,7 @@ async def generate_qr(text): qr_image = qr.make_image(fill_color="black", back_color="white") img_buffer = BytesIO() - qr_image.save(img_buffer, format="PNG") # type: ignore + qr_image.save(img_buffer, format="PNG") # type: ignore img_buffer.seek(0) return img_buffer diff --git a/jonbot/jonbot.py b/jonbot/jonbot.py index b4e292d..d07092d 100644 --- a/jonbot/jonbot.py +++ b/jonbot/jonbot.py @@ -46,10 +46,12 @@ client = commands.Bot( intents=intents, ) + @client.event async def on_ready(): print(f"Logged in as {client.user.name} running {txtmodel}.", flush=True) + @client.command(name="chat") async def jonchat(ctx, *, prompt): if "is this true" in prompt.lower(): @@ -62,6 +64,7 @@ async def jonchat(ctx, *, prompt): ) await ctx.reply(answer) + @client.event async def on_message(message): if message.author == client.user: @@ -69,9 +72,7 @@ async def on_message(message): content = message.content.strip() lower = content.lower() - if lower.startswith("hey jon") or isinstance( - message.channel, discord.DMChannel - ): + if lower.startswith("hey jon") or isinstance(message.channel, discord.DMChannel): ctx = await client.get_context(message) await jonchat(ctx, prompt=content) @@ -81,13 +82,14 @@ oai = AsyncOpenAI( base_url=config.BASE_URL, ) + async def generate_chat(question: str) -> str: try: response = await oai.chat.completions.create( model=txtmodel, messages=[ {"role": "system", "content": sysprompt}, - {"role": "user", "content": question}, + {"role": "user", "content": question}, ], max_tokens=400, temperature=1.2, @@ -105,15 +107,15 @@ async def generate_chat(question: str) -> str: return "`JonBot Error: Liz`" - async def jonbot_connect(): while True: try: await client.start(jonkey) except Exception as e: - e = str(e) - logger.error(f"Jonbot couldn't connect! {e}") - await asyncio.sleep(60) + e = str(e) + logger.error(f"Jonbot couldn't connect! {e}") + await asyncio.sleep(60) + if __name__ == "__main__": asyncio.run(jonbot_connect())