import os import uuid import tempfile from pathlib import Path from flask import Flask, request, jsonify, send_file, render_template # Optional OpenAI features try: import openai OPENAI_AVAILABLE = True except Exception: OPENAI_AVAILABLE = False # TTS (gTTS) from gtts import gTTS app = Flask(__name__, static_folder="static", template_folder="templates") # Avatar stored in repo root → /app/avatar.png AVATAR_LOCAL = "/app/avatar.png" # Storage dir for generated audio OUT_DIR = Path(tempfile.gettempdir()) / "nola_audio" OUT_DIR.mkdir(parents=True, exist_ok=True) # Load OpenAI key from env OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") or os.environ.get("OPENAI_KEY") if OPENAI_AVAILABLE and OPENAI_API_KEY: openai.api_key = OPENAI_API_KEY else: OPENAI_AVAILABLE = False @app.route("/") def index(): # Serve local avatar avatar_url = "/avatar.png" if Path(AVATAR_LOCAL).exists() else "/static/avatar.png" return render_template("index.html", avatar_src=avatar_url) @app.route("/avatar.png") def serve_avatar(): """Serve avatar from repo root.""" if Path(AVATAR_LOCAL).exists(): return send_file(AVATAR_LOCAL, mimetype="image/png") return jsonify({"error": "avatar missing"}), 404 @app.route("/api/message", methods=["POST"]) def api_message(): text = None if "text" in request.form and request.form["text"].strip(): text = request.form["text"].strip() audio_file = request.files.get("audio") transcription = "" if audio_file: uid = uuid.uuid4().hex[:10] in_path = OUT_DIR / f"in_{uid}.webm" audio_file.save(str(in_path)) if OPENAI_AVAILABLE: try: with open(in_path, "rb") as fh: resp = openai.Audio.transcriptions.create( file=fh, model="gpt-4o-mini-transcribe" ) transcription = resp.text if hasattr(resp, "text") else resp.get("text", "") except Exception: transcription = "" else: transcription = "" user_text = text or transcription or "Hello! I didn't hear clearly. Try again." # AI Response if OPENAI_AVAILABLE: try: chat_resp = openai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": user_text}], max_tokens=200, ) assistant_text = chat_resp.choices[0].message["content"].strip() except Exception: assistant_text = "Sorry, I couldn't think of a reply." else: assistant_text = f"You said: {user_text[:200]}. Ask me anything." # Generate TTS lang = request.form.get("lang", "en") try: uid2 = uuid.uuid4().hex[:10] out_mp3 = OUT_DIR / f"reply_{uid2}.mp3" tts = gTTS(text=assistant_text, lang=lang, slow=False) tts.save(str(out_mp3)) audio_url = f"/audio/{out_mp3.name}" except Exception: silent_path = OUT_DIR / f"silent_{uid2}.mp3" with open(silent_path, "wb") as f: f.write(b"") audio_url = f"/audio/{silent_path.name}" return jsonify( { "ok": True, "user_text": user_text, "assistant_text": assistant_text, "audio_url": audio_url, } ) @app.route("/audio/") def serve_audio(filename): fpath = OUT_DIR / filename if fpath.exists(): return send_file(str(fpath), mimetype="audio/mpeg", conditional=True) return jsonify({"ok": False, "error": "file not found"}), 404 if __name__ == "__main__": app.run(host="0.0.0.0", port=7860)