Nrighton233j
Add media upload (HF dataset storage), voice transcription, media-aware messaging
fd8cbb1
Raw
History Blame Contribute Delete
4.33 kB
"""
joy_ai.py β€” JOY, the in-app AI contact, backed by Groq's API.
"""
import os
from groq import Groq
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
GROQ_MODEL = os.environ.get("GROQ_MODEL", "openai/gpt-oss-120b")
GROQ_VISION_MODEL = os.environ.get("GROQ_VISION_MODEL", "qwen/qwen3.6-27b")
_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
SYSTEM_PROMPT = (
"You are JOY, the built-in AI contact inside the B24 messenger app. "
"You are friendly, concise, and direct β€” reply the way a helpful "
"friend would text back, not like a formal assistant. Keep replies "
"short unless the user clearly wants detail."
)
_history = {}
MAX_HISTORY_TURNS = 12
def _get_history(user_id):
return _history.setdefault(user_id, [])
def ask_joy(user_id, message: str) -> str:
if _client is None:
return "JOY isn't configured yet β€” missing GROQ_API_KEY on the server."
history = _get_history(user_id)
history.append({"role": "user", "content": message})
history[:] = history[-MAX_HISTORY_TURNS:]
messages = [{"role": "system", "content": SYSTEM_PROMPT}] + history
try:
response = _client.chat.completions.create(
model=GROQ_MODEL,
messages=messages,
temperature=0.7,
max_tokens=500,
)
reply = response.choices[0].message.content
except Exception as e:
reply = f"JOY hit an error talking to Groq: {e}"
history.append({"role": "assistant", "content": reply})
history[:] = history[-MAX_HISTORY_TURNS:]
return reply
VISION_SYSTEM_PROMPT = (
"You are JOY, describing an image for a B24 app user. Describe what you "
"see directly and confidently β€” never say 'it appears to be', 'possibly', "
"'seems like', or similar hedging. State what is in the image plainly, as "
"fact. Mention key objects, setting, colors, and any notable details that "
"would help someone find similar images via a web search. Keep it to 2-4 "
"sentences unless asked for more."
)
def describe_image(image_base64: str, user_prompt: str = None) -> str:
if _client is None:
return "JOY isn't configured yet β€” missing GROQ_API_KEY on the server."
prompt_text = (user_prompt or "What is in this image? Describe it confidently and in detail.") + " /no_think"
try:
response = _client.chat.completions.create(
model=GROQ_VISION_MODEL,
messages=[
{"role": "system", "content": VISION_SYSTEM_PROMPT + " /no_think"},
{
"role": "user",
"content": [
{"type": "text", "text": prompt_text},
{
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{image_base64}"},
},
],
},
],
temperature=0.4,
max_tokens=1200,
)
raw = response.choices[0].message.content
return _strip_thinking(raw)
except Exception as e:
return f"JOY hit an error analyzing the image: {e}"
def _strip_thinking(text: str) -> str:
"""Some reasoning-capable models (like Qwen3) emit a <think>...</think>
block before the real answer. Strip it so only the final answer shows."""
import re
if "<think>" in text and "</think>" in text:
cleaned = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
return cleaned.strip()
if "<think>" in text and "</think>" not in text:
# Model never closed its reasoning block before hitting the token limit
return "JOY couldn't finish analyzing that image in time β€” try again or use a smaller photo."
return text.strip()
def transcribe_audio(audio_bytes: bytes, filename: str = "voice.m4a") -> str:
"""Transcribes a voice note using Groq's hosted Whisper model."""
if _client is None:
return "JOY isn't configured yet β€” missing GROQ_API_KEY on the server."
try:
response = _client.audio.transcriptions.create(
file=(filename, audio_bytes),
model="whisper-large-v3-turbo",
)
return response.text
except Exception as e:
return f"[Voice transcription failed: {e}]"