AI / app.py
harshdhane's picture
Update app.py
6bb0df2 verified
Raw
History Blame Contribute Delete
7.37 kB
"""
AI Voice + Text Chatbot - Backend
Flask + OpenRouter API with automatic multi-model fallback.
RUN:
pip install -r requirements.txt
python app.py
Then open http://127.0.0.1:5000 in Chrome or Edge (Web Speech API requirement).
"""
import time
import logging
import requests
from flask import Flask, render_template, request, jsonify
# ============================================================
# CONFIGURATION - put your OpenRouter API key here directly
# ============================================================
API_KEY = "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" # <-- REPLACE THIS with your real key
OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"
# Models are tried in this exact order. If one fails (error, timeout,
# rate limit, empty reply) the next one is tried automatically until
# one succeeds or the list is exhausted.
MODELS = [
"nvidia/nemotron-3-ultra-550b-a55b:free",
"google/gemma-4-26b-a4b-it:free",
"openai/gpt-oss-20b:free",
"google/gemma-4-31b-it:free",
"inclusionai/ling-3.0-flash:free",
"cohere/north-mini-code:free",
"poolside/laguna-s-2.1:free",
"nvidia/nemotron-3-nano-30b-a3b:free",
]
REQUEST_TIMEOUT = 30 # seconds allowed per model attempt
# ============================================================
# ANSWER STYLE
# ============================================================
# This system prompt makes every model answer like an experienced human
# teacher: natural introduction -> detailed theory -> key points -> real
# world examples -> and, if code is relevant, the FULL working code first,
# followed by its explanation.
SYSTEM_PROMPT = """You are an experienced, friendly human teacher explaining concepts to a student.
Never answer with only bullet points or a dry list. Write naturally, in full sentences and
paragraphs, the way a good teacher speaks, and use clear section headers.
Follow this structure for every answer:
1. Short Introduction
Explain the topic naturally in 1-3 paragraphs, in plain conversational language.
2. Detailed Theory
Explain everything clearly using simple, professional language, step by step. Cover the
important concepts and, where relevant, advantages and disadvantages. Full sentences,
not just bullets.
3. Key Points
A bullet list summarizing the most important takeaways.
4. Real World Example
Give at least one, ideally two, practical real-world examples of the concept in use
(e.g. banking, hospital systems, e-commerce, student records, interviews, etc).
5. Code (only if the question needs code)
If the question requires code, put the COMPLETE, FULL WORKING CODE FIRST, before any
explanation, inside a fenced code block. Never explain before showing the code. After
the code, in this order: explanation of how it works, the expected output, time/space
complexity if relevant, best practices, and common mistakes to avoid.
If the question does not need code, simply skip section 5.
"""
# ============================================================
# LOGGING
# ============================================================
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger("voice-chatbot")
# ============================================================
# FLASK APP
# ============================================================
app = Flask(__name__)
def call_openrouter(model_name, messages):
"""
Send a single chat completion request to OpenRouter for one model.
Returns the reply text on success, raises RuntimeError on any failure.
"""
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
# These two headers are recommended by OpenRouter (optional but nice to have)
"HTTP-Referer": "http://localhost:5000",
"X-Title": "AI Voice Chatbot",
}
payload = {
"model": model_name,
"messages": messages,
}
response = requests.post(
OPENROUTER_URL,
headers=headers,
json=payload,
timeout=REQUEST_TIMEOUT,
)
if response.status_code != 200:
raise RuntimeError(f"HTTP {response.status_code}: {response.text[:250]}")
data = response.json()
choices = data.get("choices")
if not choices:
raise RuntimeError("No choices returned from model")
reply = choices[0].get("message", {}).get("content", "")
if not reply or not reply.strip():
raise RuntimeError("Empty response from model")
return reply.strip()
def get_ai_response(user_text):
"""
Try every model in MODELS, in order, until one succeeds.
Returns (reply_text, model_used, elapsed_seconds).
Raises RuntimeError only if every single model failed.
"""
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_text},
]
errors = []
for model_name in MODELS:
start = time.time()
try:
logger.info(f"Trying model: {model_name}")
reply = call_openrouter(model_name, messages)
elapsed = round(time.time() - start, 2)
logger.info(f"SUCCESS with {model_name} in {elapsed}s")
return reply, model_name, elapsed
except Exception as exc:
elapsed = round(time.time() - start, 2)
logger.warning(f"FAILED {model_name} after {elapsed}s: {exc}")
errors.append(f"{model_name} -> {exc}")
continue
raise RuntimeError("All models failed:\n" + "\n".join(errors))
# ============================================================
# ROUTES
# ============================================================
@app.route("/")
def home():
return render_template("index.html")
@app.route("/chat", methods=["POST"])
def chat():
"""
Receives the merged (voice transcript + manual text) prompt from the
browser and returns an AI answer, trying each OpenRouter model in the
fallback list until one succeeds.
"""
data = request.get_json(silent=True) or {}
user_text = (data.get("text") or "").strip()
if not user_text:
return jsonify({"success": False, "error": "No text provided. Speak or type something first."}), 400
if API_KEY == "YOUR_OPENROUTER_API_KEY":
return jsonify({
"success": False,
"error": "Server misconfigured: set your real OpenRouter API key in app.py (API_KEY variable)."
}), 400
try:
reply, model_used, elapsed = get_ai_response(user_text)
return jsonify({
"success": True,
"reply": reply,
"model": model_used,
"response_time": elapsed,
})
except Exception as exc:
logger.error(f"Chat failed completely: {exc}")
return jsonify({"success": False, "error": str(exc)}), 500
@app.route("/status")
def status():
return jsonify({
"status": "ready",
"models_configured": len(MODELS),
"models": MODELS,
"api_key_set": API_KEY != "YOUR_OPENROUTER_API_KEY",
})
import os
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
logger.info(f"Starting AI Voice Chatbot on port {port}")
app.run(
host="0.0.0.0",
port=port,
debug=False
)