Upgrade fortune, ask, and trivia commands to use Ollama LLM
fortune: generates a fresh witty one-liner via Ollama on every call, falls back to static list if LLM is unavailable. ask: switched to /api/chat endpoint with a system prompt for better conversational quality; now uses ASK_MODEL (default: gemma3:latest) separately from the 8ball OLLAMA_MODEL so each can be tuned independently. trivia: LLM generates a fresh question each time (no more repeating the same 25 questions); supports !trivia <category> with six categories (gaming, tech, general, movies, music, science); falls back to static questions if JSON generation fails. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+229
-123
@@ -15,7 +15,7 @@ from utils import send_text, send_html, send_reaction, sanitize_input
|
||||
from wordle import handle_wordle
|
||||
from config import (
|
||||
MAX_DICE_SIDES, MAX_DICE_COUNT, BOT_PREFIX, ADMIN_USERS,
|
||||
OLLAMA_URL, OLLAMA_MODEL, COOLDOWN_SECONDS,
|
||||
OLLAMA_URL, OLLAMA_MODEL, ASK_MODEL, COOLDOWN_SECONDS,
|
||||
MINECRAFT_RCON_HOST, MINECRAFT_RCON_PORT, MINECRAFT_RCON_PASSWORD,
|
||||
RCON_TIMEOUT, MIN_USERNAME_LENGTH, MAX_USERNAME_LENGTH,
|
||||
)
|
||||
@@ -331,86 +331,119 @@ async def cmd_8ball(client: AsyncClient, room_id: str, sender: str, args: str):
|
||||
await send_html(client, room_id, plain, html)
|
||||
|
||||
|
||||
_FORTUNE_FALLBACKS = [
|
||||
"If you eat something & nobody sees you eat it, it has no calories",
|
||||
"Your pet is plotting world domination",
|
||||
"Error 404: Fortune not found. Try again after system reboot",
|
||||
"The fortune you seek is in another cookie",
|
||||
"A journey of a thousand miles begins with ordering delivery",
|
||||
"You will find great fortune... in between your couch cushions",
|
||||
"A true friend is someone who tells you when your stream is muted",
|
||||
"Your next competitive match will be legendary",
|
||||
"The cake is still a lie",
|
||||
"Press Alt+F4 for instant success",
|
||||
"You will not encounter any campers today",
|
||||
"Your tank will have a healer",
|
||||
"No one will steal your pentakill",
|
||||
"Your random teammate will have a mic",
|
||||
"You will find diamonds on your first dig",
|
||||
"The boss will drop the rare loot",
|
||||
"Your speedrun will be WR pace",
|
||||
"No lag spikes in your next match",
|
||||
"Your gaming chair will grant you powers",
|
||||
"The RNG gods will bless you",
|
||||
"You will not get third partied",
|
||||
"Your squad will actually stick together",
|
||||
"The enemy team will forfeit at 15",
|
||||
"Your aim will be crispy today",
|
||||
"You will escape the backrooms",
|
||||
"The imposter will not sus you",
|
||||
"Your Minecraft bed will remain unbroken",
|
||||
"You will get Play of the Game",
|
||||
"Your next meme will go viral",
|
||||
"Someone is talking about you in their Discord server",
|
||||
"Your FBI agent thinks you're hilarious",
|
||||
"Your next TikTok will hit the FYP, if the government doesn't ban it first",
|
||||
"Someone will actually read your Twitter thread",
|
||||
"Your DMs will be blessed with quality memes today",
|
||||
"Touch grass (respectfully)",
|
||||
"The algorithm will be in your favor today",
|
||||
"Your next Spotify shuffle will hit different",
|
||||
"Someone saved your Instagram post",
|
||||
"Your Reddit comment will get gold",
|
||||
"POV: You're about to go viral",
|
||||
"Main character energy detected",
|
||||
"No cap, you're gonna have a great day fr fr",
|
||||
"Your rizz levels are increasing",
|
||||
"You will not get ratio'd today",
|
||||
"Someone will actually use your custom emoji",
|
||||
"Your next selfie will be iconic",
|
||||
"Buy a dolphin - your life will have a porpoise",
|
||||
"Stop procrastinating - starting tomorrow",
|
||||
"Catch fire with enthusiasm - people will come for miles to watch you burn",
|
||||
"Your code will compile on the first try today",
|
||||
"A semicolon will save your day",
|
||||
"The bug you've been hunting is just a typo",
|
||||
"Your next Git commit will be perfect",
|
||||
"You will find the solution on the first StackOverflow link",
|
||||
"Your Docker container will build without errors",
|
||||
"The cloud is just someone else's computer",
|
||||
"Your backup strategy will soon prove its worth",
|
||||
"A mechanical keyboard is in your future",
|
||||
"You will finally understand regex... maybe",
|
||||
"Your CSS will align perfectly on the first try",
|
||||
"Someone will star your GitHub repo today",
|
||||
"Your Linux installation will not break after updates",
|
||||
"You will remember to push your changes before shutdown",
|
||||
"Your code comments will actually make sense in 6 months",
|
||||
"The missing curly brace is on line 247",
|
||||
"Have you tried turning it off and on again?",
|
||||
"Your next pull request will be merged without comments",
|
||||
"Your keyboard RGB will sync perfectly today",
|
||||
"You will find that memory leak",
|
||||
"Your next algorithm will have O(1) complexity",
|
||||
"The force quit was strong with this one",
|
||||
"Ctrl+S will save you today",
|
||||
"Your next Python script will need no debugging",
|
||||
"Your next API call will return 200 OK",
|
||||
]
|
||||
|
||||
|
||||
@command("fortune", "Get a fortune cookie message")
|
||||
async def cmd_fortune(client: AsyncClient, room_id: str, sender: str, args: str):
|
||||
fortunes = [
|
||||
"If you eat something & nobody sees you eat it, it has no calories",
|
||||
"Your pet is plotting world domination",
|
||||
"Error 404: Fortune not found. Try again after system reboot",
|
||||
"The fortune you seek is in another cookie",
|
||||
"A journey of a thousand miles begins with ordering delivery",
|
||||
"You will find great fortune... in between your couch cushions",
|
||||
"A true friend is someone who tells you when your stream is muted",
|
||||
"Your next competitive match will be legendary",
|
||||
"The cake is still a lie",
|
||||
"Press Alt+F4 for instant success",
|
||||
"You will not encounter any campers today",
|
||||
"Your tank will have a healer",
|
||||
"No one will steal your pentakill",
|
||||
"Your random teammate will have a mic",
|
||||
"You will find diamonds on your first dig",
|
||||
"The boss will drop the rare loot",
|
||||
"Your speedrun will be WR pace",
|
||||
"No lag spikes in your next match",
|
||||
"Your gaming chair will grant you powers",
|
||||
"The RNG gods will bless you",
|
||||
"You will not get third partied",
|
||||
"Your squad will actually stick together",
|
||||
"The enemy team will forfeit at 15",
|
||||
"Your aim will be crispy today",
|
||||
"You will escape the backrooms",
|
||||
"The imposter will not sus you",
|
||||
"Your Minecraft bed will remain unbroken",
|
||||
"You will get Play of the Game",
|
||||
"Your next meme will go viral",
|
||||
"Someone is talking about you in their Discord server",
|
||||
"Your FBI agent thinks you're hilarious",
|
||||
"Your next TikTok will hit the FYP, if the government doesn't ban it first",
|
||||
"Someone will actually read your Twitter thread",
|
||||
"Your DMs will be blessed with quality memes today",
|
||||
"Touch grass (respectfully)",
|
||||
"The algorithm will be in your favor today",
|
||||
"Your next Spotify shuffle will hit different",
|
||||
"Someone saved your Instagram post",
|
||||
"Your Reddit comment will get gold",
|
||||
"POV: You're about to go viral",
|
||||
"Main character energy detected",
|
||||
"No cap, you're gonna have a great day fr fr",
|
||||
"Your rizz levels are increasing",
|
||||
"You will not get ratio'd today",
|
||||
"Someone will actually use your custom emoji",
|
||||
"Your next selfie will be iconic",
|
||||
"Buy a dolphin - your life will have a porpoise",
|
||||
"Stop procrastinating - starting tomorrow",
|
||||
"Catch fire with enthusiasm - people will come for miles to watch you burn",
|
||||
"Your code will compile on the first try today",
|
||||
"A semicolon will save your day",
|
||||
"The bug you've been hunting is just a typo",
|
||||
"Your next Git commit will be perfect",
|
||||
"You will find the solution on the first StackOverflow link",
|
||||
"Your Docker container will build without errors",
|
||||
"The cloud is just someone else's computer",
|
||||
"Your backup strategy will soon prove its worth",
|
||||
"A mechanical keyboard is in your future",
|
||||
"You will finally understand regex... maybe",
|
||||
"Your CSS will align perfectly on the first try",
|
||||
"Someone will star your GitHub repo today",
|
||||
"Your Linux installation will not break after updates",
|
||||
"You will remember to push your changes before shutdown",
|
||||
"Your code comments will actually make sense in 6 months",
|
||||
"The missing curly brace is on line 247",
|
||||
"Have you tried turning it off and on again?",
|
||||
"Your next pull request will be merged without comments",
|
||||
"Your keyboard RGB will sync perfectly today",
|
||||
"You will find that memory leak",
|
||||
"Your next algorithm will have O(1) complexity",
|
||||
"The force quit was strong with this one",
|
||||
"Ctrl+S will save you today",
|
||||
"Your next Python script will need no debugging",
|
||||
"Your next API call will return 200 OK",
|
||||
]
|
||||
fortune = None
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=15)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(
|
||||
f"{OLLAMA_URL}/api/chat",
|
||||
json={
|
||||
"model": OLLAMA_MODEL,
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": (
|
||||
"You are a fortune cookie. Generate exactly one short, witty fortune. "
|
||||
"One or two sentences max. No preamble, no explanation, no quotation marks — "
|
||||
"just the fortune itself. Be clever, funny, or unexpectedly wise. "
|
||||
"Gaming, tech, and internet culture references are welcome."
|
||||
),
|
||||
},
|
||||
{"role": "user", "content": "Give me a fortune."},
|
||||
],
|
||||
},
|
||||
) as response:
|
||||
data = await response.json()
|
||||
text = data.get("message", {}).get("content", "").strip().strip('"')
|
||||
if text and len(text) > 5:
|
||||
fortune = text
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not fortune:
|
||||
fortune = random.choice(_FORTUNE_FALLBACKS)
|
||||
|
||||
fortune = random.choice(fortunes)
|
||||
plain = f"Fortune Cookie: {fortune}"
|
||||
html = f"<strong>Fortune Cookie</strong><br>{fortune}"
|
||||
await send_html(client, room_id, plain, html)
|
||||
@@ -607,46 +640,111 @@ async def cmd_agent(client: AsyncClient, room_id: str, sender: str, args: str):
|
||||
await send_html(client, room_id, plain, html)
|
||||
|
||||
|
||||
@command("trivia", "Play a trivia game")
|
||||
_TRIVIA_CATEGORIES = {
|
||||
"gaming": "video games, gaming history, game mechanics, esports",
|
||||
"tech": "technology, programming, computers, the internet, software",
|
||||
"general": "general knowledge, world facts, history, science, geography",
|
||||
"movies": "movies, film history, actors, directors, pop culture",
|
||||
"music": "music, bands, songs, music history, artists",
|
||||
"science": "science, biology, physics, chemistry, space",
|
||||
}
|
||||
|
||||
_TRIVIA_FALLBACKS = [
|
||||
{"q": "What year was the original Super Mario Bros. released?", "options": ["1983", "1985", "1987", "1990"], "answer": 1},
|
||||
{"q": "Which game features the quote 'The cake is a lie'?", "options": ["Half-Life 2", "Portal", "BioShock", "Minecraft"], "answer": 1},
|
||||
{"q": "What is the max level in League of Legends?", "options": ["16", "18", "20", "25"], "answer": 1},
|
||||
{"q": "How many Ender Dragon eggs can exist in a vanilla Minecraft world?", "options": ["1", "2", "Unlimited", "0"], "answer": 0},
|
||||
{"q": "What was the first battle royale game to hit mainstream popularity?", "options": ["Fortnite", "PUBG", "H1Z1", "Apex Legends"], "answer": 2},
|
||||
{"q": "In Minecraft, what is the rarest ore?", "options": ["Diamond", "Emerald", "Ancient Debris", "Lapis Lazuli"], "answer": 1},
|
||||
{"q": "What is the name of the main character in The Legend of Zelda?", "options": ["Zelda", "Link", "Ganondorf", "Epona"], "answer": 1},
|
||||
{"q": "What type of animal is Sonic?", "options": ["Fox", "Hedgehog", "Rabbit", "Echidna"], "answer": 1},
|
||||
{"q": "What does GG stand for in gaming?", "options": ["Get Good", "Good Game", "Go Go", "Great Going"], "answer": 1},
|
||||
{"q": "Which company developed Valorant?", "options": ["Blizzard", "Valve", "Riot Games", "Epic Games"], "answer": 2},
|
||||
{"q": "What is the highest rank in Valorant?", "options": ["Immortal", "Diamond", "Radiant", "Challenger"], "answer": 2},
|
||||
{"q": "What does HTTP stand for?", "options": ["HyperText Transfer Protocol", "High Tech Transfer Program", "HyperText Transmission Process", "Home Tool Transfer Protocol"], "answer": 0},
|
||||
{"q": "What year was Discord founded?", "options": ["2013", "2015", "2017", "2019"], "answer": 1},
|
||||
{"q": "What programming language has a logo that is a snake?", "options": ["Java", "Ruby", "Python", "Go"], "answer": 2},
|
||||
{"q": "How many bits are in a byte?", "options": ["4", "8", "16", "32"], "answer": 1},
|
||||
{"q": "What does 'RGB' stand for?", "options": ["Really Good Build", "Red Green Blue", "Red Gold Black", "Rapid Gaming Boost"], "answer": 1},
|
||||
{"q": "What does 'AFK' stand for?", "options": ["A Free Kill", "Away From Keyboard", "Always Fun Killing", "Another Fake Knockdown"], "answer": 1},
|
||||
{"q": "What animal is the Linux mascot?", "options": ["Fox", "Penguin", "Cat", "Dog"], "answer": 1},
|
||||
{"q": "What does 'NPC' stand for?", "options": ["Non-Player Character", "New Player Content", "Normal Playing Conditions", "Never Played Competitively"], "answer": 0},
|
||||
{"q": "In what year was the first iPhone released?", "options": ["2005", "2006", "2007", "2008"], "answer": 2},
|
||||
]
|
||||
|
||||
|
||||
async def _generate_trivia_question(category: str) -> dict | None:
|
||||
"""Ask the LLM to generate a trivia question. Returns None on failure."""
|
||||
topic = _TRIVIA_CATEGORIES.get(category, _TRIVIA_CATEGORIES["general"])
|
||||
prompt = (
|
||||
f"Generate a trivia question about {topic}. "
|
||||
"Respond with ONLY a JSON object, no markdown, no explanation. "
|
||||
'Format: {"q": "question text", "options": ["A text", "B text", "C text", "D text"], "answer": 0} '
|
||||
"where answer is the 0-based index of the correct option. "
|
||||
"The question should be clear, factual, and have exactly one correct answer."
|
||||
)
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=20)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(
|
||||
f"{OLLAMA_URL}/api/chat",
|
||||
json={
|
||||
"model": ASK_MODEL,
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a trivia question generator. Respond with only valid JSON, nothing else.",
|
||||
},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
},
|
||||
) as response:
|
||||
data = await response.json()
|
||||
text = data.get("message", {}).get("content", "").strip()
|
||||
# Strip markdown code fences if present
|
||||
if text.startswith("```"):
|
||||
text = text.split("```")[1]
|
||||
if text.startswith("json"):
|
||||
text = text[4:]
|
||||
parsed = json.loads(text)
|
||||
# Validate structure
|
||||
if (
|
||||
isinstance(parsed.get("q"), str)
|
||||
and isinstance(parsed.get("options"), list)
|
||||
and len(parsed["options"]) == 4
|
||||
and isinstance(parsed.get("answer"), int)
|
||||
and 0 <= parsed["answer"] <= 3
|
||||
):
|
||||
return parsed
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
@command("trivia", "Play a trivia game (!trivia [gaming|tech|general|movies|music|science])")
|
||||
async def cmd_trivia(client: AsyncClient, room_id: str, sender: str, args: str):
|
||||
questions = [
|
||||
{"q": "What year was the original Super Mario Bros. released?", "options": ["1983", "1985", "1987", "1990"], "answer": 1},
|
||||
{"q": "Which game features the quote 'The cake is a lie'?", "options": ["Half-Life 2", "Portal", "BioShock", "Minecraft"], "answer": 1},
|
||||
{"q": "What is the max level in League of Legends?", "options": ["16", "18", "20", "25"], "answer": 1},
|
||||
{"q": "Which Valorant agent has the codename 'Deadeye'?", "options": ["Jett", "Sova", "Chamber", "Cypher"], "answer": 2},
|
||||
{"q": "How many Ender Dragon eggs can exist in a vanilla Minecraft world?", "options": ["1", "2", "Unlimited", "0"], "answer": 0},
|
||||
{"q": "What was the first battle royale game to hit mainstream popularity?", "options": ["Fortnite", "PUBG", "H1Z1", "Apex Legends"], "answer": 2},
|
||||
{"q": "In Minecraft, what is the rarest ore?", "options": ["Diamond", "Emerald", "Ancient Debris", "Lapis Lazuli"], "answer": 1},
|
||||
{"q": "What is the name of the main character in The Legend of Zelda?", "options": ["Zelda", "Link", "Ganondorf", "Epona"], "answer": 1},
|
||||
{"q": "Which game has the most registered players of all time?", "options": ["Fortnite", "Minecraft", "League of Legends", "Roblox"], "answer": 1},
|
||||
{"q": "What type of animal is Sonic?", "options": ["Fox", "Hedgehog", "Rabbit", "Echidna"], "answer": 1},
|
||||
{"q": "In Among Us, what is the maximum number of impostors?", "options": ["1", "2", "3", "4"], "answer": 2},
|
||||
{"q": "What does GG stand for in gaming?", "options": ["Get Good", "Good Game", "Go Go", "Great Going"], "answer": 1},
|
||||
{"q": "Which company developed Valorant?", "options": ["Blizzard", "Valve", "Riot Games", "Epic Games"], "answer": 2},
|
||||
{"q": "What is the highest rank in Valorant?", "options": ["Immortal", "Diamond", "Radiant", "Challenger"], "answer": 2},
|
||||
{"q": "In League of Legends, what is Baron Nashor an anagram of?", "options": ["Baron Roshan", "Roshan", "Nashor Baron", "Nash Robot"], "answer": 1},
|
||||
{"q": "What does HTTP stand for?", "options": ["HyperText Transfer Protocol", "High Tech Transfer Program", "HyperText Transmission Process", "Home Tool Transfer Protocol"], "answer": 0},
|
||||
{"q": "What year was Discord founded?", "options": ["2013", "2015", "2017", "2019"], "answer": 1},
|
||||
{"q": "What programming language has a logo that is a snake?", "options": ["Java", "Ruby", "Python", "Go"], "answer": 2},
|
||||
{"q": "How many bits are in a byte?", "options": ["4", "8", "16", "32"], "answer": 1},
|
||||
{"q": "What does 'RGB' stand for?", "options": ["Really Good Build", "Red Green Blue", "Red Gold Black", "Rapid Gaming Boost"], "answer": 1},
|
||||
{"q": "What is the most subscribed YouTube channel?", "options": ["PewDiePie", "MrBeast", "T-Series", "Cocomelon"], "answer": 1},
|
||||
{"q": "What does 'AFK' stand for?", "options": ["A Free Kill", "Away From Keyboard", "Always Fun Killing", "Another Fake Knockdown"], "answer": 1},
|
||||
{"q": "What animal is the Linux mascot?", "options": ["Fox", "Penguin", "Cat", "Dog"], "answer": 1},
|
||||
{"q": "What does 'NPC' stand for?", "options": ["Non-Player Character", "New Player Content", "Normal Playing Conditions", "Never Played Competitively"], "answer": 0},
|
||||
{"q": "In what year was the first iPhone released?", "options": ["2005", "2006", "2007", "2008"], "answer": 2},
|
||||
]
|
||||
category = args.strip().lower() if args.strip().lower() in _TRIVIA_CATEGORIES else "general"
|
||||
if args.strip() and args.strip().lower() not in _TRIVIA_CATEGORIES:
|
||||
cats = ", ".join(_TRIVIA_CATEGORIES.keys())
|
||||
await send_text(client, room_id, f"Unknown category. Choose from: {cats}")
|
||||
return
|
||||
|
||||
question = await _generate_trivia_question(category)
|
||||
if question is None:
|
||||
# Fallback to static list (gaming questions only in fallback)
|
||||
question = random.choice(_TRIVIA_FALLBACKS)
|
||||
|
||||
labels = ["\U0001f1e6", "\U0001f1e7", "\U0001f1e8", "\U0001f1e9"] # A B C D regional indicators
|
||||
label_letters = ["A", "B", "C", "D"]
|
||||
question = random.choice(questions)
|
||||
cat_label = category.capitalize()
|
||||
|
||||
options_plain = "\n".join(f" {label_letters[i]}. {opt}" for i, opt in enumerate(question["options"]))
|
||||
options_html = "".join(f"<li><strong>{label_letters[i]}</strong>. {opt}</li>" for i, opt in enumerate(question["options"]))
|
||||
|
||||
plain = f"Trivia Time!\n{question['q']}\n{options_plain}\n\nReact with A/B/C/D — answer revealed in 30s!"
|
||||
plain = f"Trivia Time! [{cat_label}]\n{question['q']}\n{options_plain}\n\nReact with A/B/C/D — answer revealed in 30s!"
|
||||
html = (
|
||||
f"<strong>Trivia Time!</strong><br>"
|
||||
f"<strong>Trivia Time!</strong> <em>[{cat_label}]</em><br>"
|
||||
f"<em>{question['q']}</em><br>"
|
||||
f"<ul>{options_html}</ul>"
|
||||
f"React with A/B/C/D — answer revealed in 30s!"
|
||||
@@ -657,7 +755,6 @@ async def cmd_trivia(client: AsyncClient, room_id: str, sender: str, args: str):
|
||||
for emoji in labels:
|
||||
await send_reaction(client, room_id, resp.event_id, emoji)
|
||||
|
||||
# Reveal answer after 30 seconds
|
||||
async def reveal():
|
||||
await asyncio.sleep(30)
|
||||
correct = question["answer"]
|
||||
@@ -693,27 +790,36 @@ async def cmd_ask(client: AsyncClient, room_id: str, sender: str, args: str):
|
||||
await send_text(client, room_id, "Thinking...")
|
||||
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=60)
|
||||
timeout = aiohttp.ClientTimeout(total=90)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(
|
||||
f"{OLLAMA_URL}/api/generate",
|
||||
json={"model": OLLAMA_MODEL, "prompt": question, "stream": True},
|
||||
f"{OLLAMA_URL}/api/chat",
|
||||
json={
|
||||
"model": ASK_MODEL,
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": (
|
||||
"You are LotusBot, a helpful assistant in a Matrix chat room for a small gaming community. "
|
||||
"Answer questions clearly and concisely. Keep responses reasonably brief — "
|
||||
"a few sentences to a short paragraph unless the question genuinely needs more detail. "
|
||||
"Be friendly and conversational."
|
||||
),
|
||||
},
|
||||
{"role": "user", "content": question},
|
||||
],
|
||||
},
|
||||
) as response:
|
||||
full_response = ""
|
||||
async for line in response.content:
|
||||
try:
|
||||
chunk = json.loads(line)
|
||||
if "response" in chunk:
|
||||
full_response += chunk["response"]
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
data = await response.json()
|
||||
full_response = data.get("message", {}).get("content", "").strip()
|
||||
|
||||
if not full_response:
|
||||
full_response = "No response received from server."
|
||||
|
||||
plain = f"Lotus LLM\nQ: {question}\nA: {full_response}"
|
||||
plain = f"LotusBot\nQ: {question}\nA: {full_response}"
|
||||
html = (
|
||||
f"<strong>Lotus LLM</strong><br>"
|
||||
f"<strong>LotusBot</strong><br>"
|
||||
f"<em>Q:</em> {question}<br>"
|
||||
f"<em>A:</em> {full_response}"
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
||||
# Integrations
|
||||
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://10.10.10.157:11434")
|
||||
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "lotusllm")
|
||||
ASK_MODEL = os.getenv("ASK_MODEL", "gemma3:latest")
|
||||
MINECRAFT_RCON_HOST = os.getenv("MINECRAFT_RCON_HOST", "10.10.10.67")
|
||||
MINECRAFT_RCON_PORT = int(os.getenv("MINECRAFT_RCON_PORT", "25575"))
|
||||
MINECRAFT_RCON_PASSWORD = os.getenv("MINECRAFT_RCON_PASSWORD", "")
|
||||
|
||||
Reference in New Issue
Block a user