Cozmo AI Chat Buddy
text · 19 views · 4 unique · 2026-08-13 12:41:48 · raw
#!/usr/bin/env python3
"""
Stage 2 - Cozmo Chat Buddy
- Local Open WebUI / Ollama
- Faster-Whisper (local STT) with Google fallback
- eSpeak voice through Cozmo's speaker
- Cute head tilts while speaking
"""
import time
import threading
import subprocess
import tempfile
import os
import queue
from openai import OpenAI
import pycozmo
import speech_recognition as sr
# Optional local STT
try:
from faster_whisper import WhisperModel
LOCAL_STT_AVAILABLE = True
except ImportError:
LOCAL_STT_AVAILABLE = False
print("faster-whisper not installed – will use Google STT")
# ====================== CONFIG ======================
OPENWEBUI_BASE = "http://localhost:3000/api" # or your Cloudflare URL
API_KEY = "MyAPIkey n sheeeeeeiit"
MODEL = "qwen3:8b or whatever"
SYSTEM_PROMPT = """You are Cozmo aka Little Buddy, a tiny adorable robot sitting on your charger.
You are curious, slightly chaotic, friendly, and a little dramatic. Keep answers short (1 sentence max). Speak like a cute little robot. and do not use * in your speech. You are chatting on hobostreamer.com
Never break character. Never mention you are an AI.
"""
# Voice settings (eSpeak)
ESPEAK_VOICE = "en+f3" # higher / cuter
ESPEAK_SPEED = "155"
# Head movement
HEAD_BASE = 0.28
HEAD_TILT_AMOUNT = 0.16
# Listening
USE_LOCAL_STT = True # set to False to force Google
WHISPER_MODEL_SIZE = "base" # tiny / base / small
# ====================================================
client = OpenAI(base_url=OPENWEBUI_BASE, api_key=API_KEY)
recognizer = sr.Recognizer()
microphone = sr.Microphone()
# Load local Whisper if available
whisper_model = None
if LOCAL_STT_AVAILABLE and USE_LOCAL_STT:
print("Loading local Whisper model...")
whisper_model = WhisperModel(WHISPER_MODEL_SIZE, device="cpu", compute_type="int8")
print("Local STT ready.")
def speak_espeak(text: str, cli):
"""Speak through Cozmo using eSpeak"""
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
wav_path = f.name
try:
subprocess.run([
"espeak",
"-v", ESPEAK_VOICE,
"-s", ESPEAK_SPEED,
"-w", wav_path,
text
], check=True, capture_output=True)
with open(wav_path, "rb") as audio_file:
cli.play_audio(audio_file)
except Exception as e:
print(f"Cozmo audio error: {e}")
# Fallback – play on PC so you can still hear it
os.system(f'start /min "" "{wav_path}"')
finally:
try:
os.unlink(wav_path)
except:
pass
def cute_head_tilt(cli, duration=3.0):
"""Gentle rhythmic head tilt while speaking"""
start = time.time()
direction = 1
while time.time() - start < duration:
angle = HEAD_BASE + (HEAD_TILT_AMOUNT * direction)
try:
cli.set_head_angle(angle)
except:
break
time.sleep(0.30)
direction *= -1
try:
cli.set_head_angle(HEAD_BASE)
except:
pass
def listen_once():
"""Listen and return transcribed text"""
with microphone as source:
print("\nListening... (speak now)")
recognizer.adjust_for_ambient_noise(source, duration=0.35)
try:
audio = recognizer.listen(source, timeout=7, phrase_time_limit=10)
except sr.WaitTimeoutError:
print("No speech detected.")
return None
# Try local Whisper first
if whisper_model is not None:
try:
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
wav_path = f.name
with open(wav_path, "wb") as out:
out.write(audio.get_wav_data())
segments, _ = whisper_model.transcribe(wav_path, language="en")
text = " ".join([seg.text for seg in segments]).strip()
os.unlink(wav_path)
if text:
print(f"You said (local): {text}")
return text
except Exception as e:
print(f"Local STT failed: {e}")
# Fallback to Google
try:
text = recognizer.recognize_google(audio)
print(f"You said (Google): {text}")
return text
except sr.UnknownValueError:
print("Didn't catch that.")
return None
except Exception as e:
print(f"Recognition error: {e}")
return None
def ask_llm(user_text: str) -> str:
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_text}
],
temperature=0.85,
max_tokens=120
)
return response.choices[0].message.content.strip()
def main():
print("Connecting to Cozmo...")
with pycozmo.connect(enable_procedural_face=True) as cli:
cli.enable_procedural_face(True)
cli.set_head_angle(HEAD_BASE)
time.sleep(0.5)
print("Cozmo is ready on the charger.")
print("Talk to him! (Ctrl+C to quit)\n")
while True:
try:
user_text = listen_once()
if not user_text:
continue
print("Thinking...")
reply = ask_llm(user_text)
print(f"Cozmo: {reply}\n")
# Head tilt + speak at the same time
tilt_thread = threading.Thread(
target=cute_head_tilt,
args=(cli, 3.2),
daemon=True
)
tilt_thread.start()
speak_espeak(reply, cli)
tilt_thread.join(timeout=4.0)
time.sleep(0.3)
except KeyboardInterrupt:
print("\nBye little buddy!")
break
except Exception as e:
print(f"Error: {e}")
time.sleep(1)
if __name__ == "__main__":
main()