B. API-based: LLM API with requests + .env (python-dotenv)
Challenge Task
Token usage
Asynchronous multi-model
Assignment
Show the reference solution
Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.
solution.pyPython
"""Rule-based console chatbot — works without an API key"""
import json
import re
from pathlib import Path
from datetime import datetime
HISTORY = Path("chat_history.json")
RULES = [
(r"Hello|hello|hi", "Hello!"),
(r"Name|Who", "I am a learning chatbot."),
(r"Weather", "Sorry, I cannot provide real-time weather."),
(r"Python|python", "Python is an interpreted language released in 1991."),
(r"Thanks|thank you|thanks", "You're welcome!"),
(r"(\d+)\s*\+\s*(\d+)", lambda m: f"= {int(m.group(1)) + int(m.group(2))}"),
]
def respond(text):
for pattern, reply in RULES:
m = re.search(pattern, text)
if m:
return reply(m) if callable(reply) else reply
return "I don't know. Please say it again."
def save(history):
HISTORY.write_text(json.dumps(history, ensure_ascii=False, indent=2), encoding="utf-8")
def load():
return json.loads(HISTORY.read_text(encoding="utf-8")) if HISTORY.exists() else []
def main():
history = []
print("Chatbot start (/quit to exit, /help for commands)")
while True:
user = input("\nyou: ").strip()
if not user: continue
if user.startswith("/"):
cmd = user[1:]
if cmd in ("quit", "exit"): break
elif cmd == "help":
print("/clear /save /load /history /quit")
elif cmd == "clear": history = []; print("Reset history")
elif cmd == "save": save(history); print("Saved")
elif cmd == "load": history = load(); print(f"Loaded ({len(history)} items)")
elif cmd == "history":
for h in history[-5:]:
print(f" [{h['time']}] {h['who']}: {h['text']}")
continue
bot_reply = respond(user)
ts = datetime.now().strftime("%H:%M:%S")
history.append({"time": ts, "who": "user", "text": user})
history.append({"time": ts, "who": "bot", "text": bot_reply})
print(f"bot: {bot_reply}")
if __name__ == "__main__":
main()