Spaces:
Sleeping
Sleeping
| 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 | |
| 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) | |
| 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 | |
| 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, | |
| } | |
| ) | |
| 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) | |