ai_backend / app.py
sumit989's picture
Update app.py
3a2ee6c verified
# imports
import os
import json
import re
from time import time
from flask import Flask, request, jsonify
from flask_cors import CORS
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()# load env
app = Flask(__name__)
# frontend lock
allowed_origins = os.getenv("ALLOWED_ORIGINS", "https://sumit989bishnoi-crypto.github.io")
CORS(app, origins=allowed_origins.split(","))
# model call
API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-Coder-7B-Instruct")
API_KEY = os.getenv("API_KEY") or os.getenv("HF_TOKEN")
client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY) if API_KEY else None
_last_request: dict[str, float] = {}
# rate limit 1 requesr for 2 sec
def is_rate_limited(ip: str) -> bool:
now = time()
if ip in _last_request and now - _last_request[ip] < 2:
return True
_last_request[ip] = now
return False
def extract_json(raw: str) -> dict | None:
"""Strip markdown fences then attempt JSON parse two ways."""
cleaned = re.sub(r"^```(?:json)?\s*", "", raw.strip())
cleaned = re.sub(r"\s*```$", "", cleaned).strip()
try:
return json.loads(cleaned)
except json.JSONDecodeError:
pass
# stops exactly where JSON ends
try:
idx = cleaned.index("{")
decoded, _ = json.JSONDecoder().raw_decode(cleaned, idx)
return decoded
except (ValueError, json.JSONDecodeError):
return None
# always return a valid result
def safe_parse(raw: str) -> dict:
"""
Always returns a valid dict.
Runs extract_json first; falls back to a safe default on any failure
so a bad model response never crashes the endpoint.
"""
# low confidence
if not raw:
return {
"language": "unknown",
"explanation": "The AI returned an empty response. Please try again.",
"fixed_code": "",
"confidence": "low",
}
result = extract_json(raw)
if result and isinstance(result, dict):
return result
# always return a valid explanation
return {
"language": "unknown",
"explanation": "Output parsing failed — the AI did not return valid JSON.",
"fixed_code": raw[:1500],
"confidence": "low",
}
# status
@app.route("/")
def index():
return jsonify({"name": "CodeRescue API", "version": "42.0", "status": "running"}) # to show when backend is running correct
# health check
@app.route("/health")
def health():
return jsonify({"status": "ok"})
# analyze
@app.route("/analyze", methods=["POST"])
def analyze_code():
forwarded = request.headers.get("X-Forwarded-For", "")
ip = forwarded.split(",")[0].strip() if forwarded else (request.remote_addr or "unknown")
# rate limit error to show
if is_rate_limited(ip):
return jsonify({
"error": "Too many requests. Please wait a moment.",
"fixed_code": "",
"language": ""
}), 429
if not client:
return jsonify({"error": "API key not configured", "fixed_code": "", "language": ""}), 500
start = time()
data = request.get_json(silent=True)
if not data or not data.get("code", "").strip():
return jsonify({"error": "No code provided"}), 400
raw_code = data["code"].strip()
warning = ""
if len(raw_code) > 1500:
warning = "Code was truncated to 1500 characters for processing."
user_code = raw_code[:1500]
try:
response = client.chat.completions.create(
model=MODEL_NAME,
max_tokens=400, # max tokens usage limit
messages=[
{"role": "system",
"content": (
"You are a code debugger.\n"
"RULES:\n"
"1. Return ONLY valid JSON.\n" # always give json
"2. No markdown, no extra text.\n"
"3. Keep explanation to MAX 2 lines.\n"
"4. Always return FULL fixed code.\n"
"5. Keep code minimal and clean.\n"
"6.Prefer fixes that preserve original logic and intent over simply avoiding errors.\n" # for low error
"If code is too large:\n" # faster speed
"- Simplify it while preserving logic.\n"
"- Remove unnecessary parts if safe.\n"
"JSON FORMAT:\n" # formoat to show
"{\"language\":\"...\","
"\"explanation\":\"...\","
"\"fixed_code\":\"...\","
"\"confidence\":\"high|medium|low\"}"
),
},
{
"role": "user",
"content": f"Code:\n{user_code}",
},
],
)
# calculate latency
end = time()
raw = response.choices[0].message.content.strip()
# safe_parse always returns a dict — never raises
parsed = safe_parse(raw)
fixed_code = parsed.get("fixed_code", "")
# Unescape literal \n / \t the model emits inside JSON strings
fixed_code = fixed_code.replace("\\n", "\n").replace("\\t", "\t")
return jsonify({
"explanation": parsed.get("explanation", "No explanation provided."),
"fixed_code": fixed_code,
"language": parsed.get("language", "unknown"),
"confidence": parsed.get("confidence", "medium"),
"warning": warning,
"time_taken": round(end - start, 3),
})
# warning when it is server side error
except Exception as e:
return jsonify({
"explanation": f"Server error: {str(e)}",
"fixed_code": "",
"language": "unknown",
"confidence": "low",
"warning": warning
}), 500
# route for /openenv/reset &&& /reset
@app.route("/openenv/reset", methods=["POST"])
@app.route("/reset", methods=["POST"])
def openenv_reset():
return jsonify({"status": "success"})
# ip && port
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
app.run(host="0.0.0.0", port=port)