sumit989 commited on
Commit
b7a1727
Β·
verified Β·
1 Parent(s): bb31e74

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +33 -0
  2. app.py +218 -0
  3. inference.py +94 -0
  4. requirements.txt +6 -0
Dockerfile ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use lightweight Python image
2
+ FROM python:3.10-slim
3
+
4
+ # Prevent Python from writing .pyc files
5
+ ENV PYTHONDONTWRITEBYTECODE=1
6
+
7
+ # Ensure logs appear immediately
8
+ ENV PYTHONUNBUFFERED=1
9
+
10
+ # Set working directory
11
+ WORKDIR /app
12
+
13
+ # Install system dependencies (minimal but safe)
14
+ RUN apt-get update && \
15
+ apt-get install -y --no-install-recommends \
16
+ build-essential \
17
+ curl && \
18
+ rm -rf /var/lib/apt/lists/*
19
+
20
+ # Copy requirements first (for caching)
21
+ COPY requirements.txt .
22
+
23
+ # Install Python dependencies
24
+ RUN pip install --no-cache-dir -r requirements.txt
25
+
26
+ # Copy application files
27
+ COPY . .
28
+
29
+ # Expose port used by Flask / Gunicorn
30
+ EXPOSE 7860
31
+
32
+ # Start server (production-safe)
33
+ CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:7860", "--workers", "1", "--timeout", "120"]
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ from time import time
5
+ from flask import Flask, request, jsonify
6
+ from flask_cors import CORS
7
+ from dotenv import load_dotenv
8
+ from openai import OpenAI
9
+
10
+ # api load
11
+ load_dotenv()
12
+
13
+ app = Flask(__name__)
14
+
15
+ # frontend lock so other canot use
16
+ allowed_origins = os.getenv("ALLOWED_ORIGINS", "https://sumit989bishnoi-crypto.github.io")
17
+ CORS(app, origins=allowed_origins.split(","))
18
+
19
+ # models
20
+ API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
21
+ MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-7B-Instruct")
22
+
23
+ # api and hftoken both can pull as user request
24
+ API_KEY = os.getenv("API_KEY") or os.getenv("HF_TOKEN")
25
+
26
+ # when no api key found
27
+ client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY) if API_KEY else None
28
+
29
+ # language lmt
30
+ SUPPORTED_LANGUAGES = [
31
+ "python", "javascript", "typescript", "java", "c", "cpp",
32
+ "csharp", "go", "rust", "php", "ruby", "swift", "kotlin", "bash"
33
+ ]
34
+
35
+ # map ip
36
+ _last_request: dict[str, float] = {}
37
+
38
+
39
+ def is_rate_limited(ip: str) -> bool:
40
+ """Block the same IP if it hits us again within 2 seconds."""
41
+ now = time()
42
+ if ip in _last_request and now - _last_request[ip] < 2:
43
+ return True
44
+ _last_request[ip] = now
45
+ return False
46
+
47
+
48
+ def extract_json(raw: str) -> dict | None:
49
+ """
50
+ Try to parse JSON out of the AI's raw response.
51
+ The model sometimes wraps output in markdown fences or adds extra prose,
52
+ so we strip that first, then fall back to a boundary-aware parser.
53
+ """
54
+ cleaned = re.sub(r"^```(?:json)?\s*", "", raw.strip())
55
+ cleaned = re.sub(r"\s*```$", "", cleaned).strip()
56
+
57
+ # fast path
58
+ try:
59
+ return json.loads(cleaned)
60
+ except json.JSONDecodeError:
61
+ pass
62
+
63
+ # slow path
64
+ # raw decode stops exactly where the JSON ends
65
+ try:
66
+ idx = cleaned.index("{")
67
+ decoded, _ = json.JSONDecoder().raw_decode(cleaned, idx)
68
+ return decoded
69
+ except (ValueError, json.JSONDecodeError):
70
+ return None
71
+
72
+
73
+ # routes start
74
+
75
+ @app.route("/")
76
+ def index():
77
+ # basic info of endpoint
78
+ return jsonify({"name": "CodeRescue API", "version": "1.0.0", "status": "running"})
79
+
80
+
81
+ @app.route("/health")
82
+ def health():
83
+ # health check code
84
+ return jsonify({"status": "ok"})
85
+
86
+
87
+ # languages support
88
+ @app.route("/languages")
89
+ def languages():
90
+ return jsonify({"languages": SUPPORTED_LANGUAGES})
91
+
92
+
93
+ @app.route("/analyze", methods=["POST"])
94
+ def analyze_code():
95
+ # rate limit
96
+
97
+ # x forwarded for is set by proxies
98
+ forwarded = request.headers.get("X-Forwarded-For", "")
99
+ ip = forwarded.split(",")[0].strip() if forwarded else (request.remote_addr or "unknown")
100
+
101
+ if is_rate_limited(ip):
102
+ return jsonify({
103
+ "error": "Too many requests. Please wait a moment.",
104
+ "fixed_code": "",
105
+ "language": ""
106
+ }), 429
107
+
108
+ # pre checks
109
+
110
+ if not client:
111
+ # to stop request here
112
+ return jsonify({"error": "API key not configured", "fixed_code": "", "language": ""}), 500
113
+
114
+ data = request.get_json(silent=True)
115
+ if not data or not data.get("code", "").strip():
116
+ return jsonify({"error": "No code provided"}), 400
117
+
118
+ raw_code = data["code"].strip()
119
+ language = data.get("language", "python").lower()
120
+
121
+ if language not in SUPPORTED_LANGUAGES:
122
+ return jsonify({
123
+ "error": f"Unsupported language '{language}'. Supported: {SUPPORTED_LANGUAGES}"
124
+ }), 400
125
+
126
+ # input prep
127
+
128
+ # input limit
129
+ warning = ""
130
+ if len(raw_code) > 800:
131
+ warning = "Code was truncated to 800 characters for processing."
132
+ user_code = raw_code[:800]
133
+
134
+ # call model
135
+ try:
136
+ response = client.chat.completions.create(
137
+ model=MODEL_NAME,
138
+ max_tokens=800, # max token
139
+ messages=[
140
+ {
141
+ "role": "system",
142
+ "content": (
143
+ "You are an expert developer and code debugger.\n"
144
+ "Return ONLY valid JSON β€” no markdown, no extra text.\n"
145
+ "Explain briefly WHY the error happened (1-2 lines).\n"
146
+ "Fixed code must be SHORT and COMPLETE.\n"
147
+ "Preserve all newlines and indentation in fixed_code.\n"
148
+ "Do NOT cut output mid-way.\n"
149
+ "Set confidence to 'high', 'medium', or 'low' based on how certain you are.\n"
150
+ "Format strictly:\n"
151
+ "{\"explanation\":\"...\",\"fixed_code\":\"...\","
152
+ "\"language\":\"...\",\"confidence\":\"high|medium|low\"}"
153
+ ),
154
+ },
155
+ {
156
+ "role": "user",
157
+ "content": f"Language: {language}\n\nCode:\n{user_code}",
158
+ },
159
+ ],
160
+ )
161
+
162
+ raw = response.choices[0].message.content.strip()
163
+
164
+ # model returned nothing
165
+ if not raw:
166
+ return jsonify({
167
+ "explanation": "Empty response from AI.",
168
+ "fixed_code": "",
169
+ "language": language,
170
+ "confidence": "low",
171
+ "warning": warning
172
+ }), 200
173
+
174
+ # parse response
175
+ parsed = extract_json(raw)
176
+
177
+ # return the raw text so the user sees something
178
+ if not parsed:
179
+ return jsonify({
180
+ "explanation": "AI response was not clean JSON β€” raw output returned.",
181
+ "fixed_code": raw[:800],
182
+ "language": language,
183
+ "confidence": "low",
184
+ "warning": warning
185
+ }), 200
186
+
187
+ # unescape literal \n and \t that the model sometimes emits inside strings
188
+ fixed_code = parsed.get("fixed_code", "")
189
+ fixed_code = fixed_code.replace("\\n", "\n").replace("\\t", "\t")
190
+
191
+ return jsonify({
192
+ "explanation": parsed.get("explanation", "No explanation provided."),
193
+ "fixed_code": fixed_code,
194
+ "language": parsed.get("language", language),
195
+ "confidence": parsed.get("confidence", "medium"),
196
+ "warning": warning,
197
+ })
198
+
199
+ except Exception as e:
200
+ return jsonify({
201
+ "explanation": f"Server error: {str(e)}",
202
+ "fixed_code": "",
203
+ "language": language,
204
+ "confidence": "low",
205
+ "warning": warning
206
+ }), 500
207
+
208
+
209
+ #donot havev to change this
210
+ @app.route("/openenv/reset", methods=["POST"])
211
+ @app.route("/reset", methods=["POST"])
212
+ def openenv_reset():
213
+ return jsonify({"status": "success"})
214
+
215
+
216
+ if __name__ == "__main__":
217
+ port = int(os.environ.get("PORT", 7860))
218
+ app.run(host="0.0.0.0", port=port)
inference.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from openai import OpenAI
4
+
5
+ # ── Config ────────────────────────────────────────────────────────────────────
6
+ API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
7
+ MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-7B-Instruct")
8
+ API_KEY = os.getenv("API_KEY") or os.getenv("HF_TOKEN")
9
+
10
+ client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY) if API_KEY else None
11
+
12
+
13
+ def solve(task_name: str, task_input: str) -> str:
14
+ print(f"[START] task={task_name}", flush=True)
15
+ print(f"[STEP] step=1 reward=0.5", flush=True)
16
+
17
+ if not client:
18
+ print(f"[END] task={task_name} score=0.1 steps=1", flush=True)
19
+ return "Error: API key not configured"
20
+
21
+ response = client.chat.completions.create(
22
+ model=MODEL_NAME,
23
+ max_tokens=2048,
24
+ messages=[
25
+ {
26
+ "role": "system",
27
+ "content": (
28
+ "You are an expert developer.\n"
29
+ "Return ONLY valid JSON.\n"
30
+ "Explanation must be MAX 2 lines.\n"
31
+ "Fixed code must be SHORT and COMPLETE.\n"
32
+ "Preserve all newlines and indentation in fixed_code.\n"
33
+ "Do NOT cut output.\n"
34
+ "Format strictly:\n"
35
+ "{\"explanation\":\"...\",\"fixed_code\":\"...\",\"language\":\"...\"}"
36
+ ),
37
+ },
38
+ {
39
+ "role": "user",
40
+ "content": f"Fix this code and explain the errors:\n{task_input}",
41
+ },
42
+ ],
43
+ )
44
+
45
+ output = response.choices[0].message.content
46
+ score = grade(task_input, output)
47
+
48
+ print(f"[STEP] step=2 reward={score}", flush=True)
49
+ print(f"[END] task={task_name} score={score} steps=2", flush=True)
50
+
51
+ return output
52
+
53
+
54
+ def grade(task_input: str, output: str) -> float:
55
+ if not output or len(output.strip()) < 5:
56
+ return 0.1
57
+
58
+ score = 0.5
59
+ try:
60
+ raw = output.strip().replace("```json", "").replace("```", "").strip()
61
+ parsed = json.loads(raw)
62
+ if parsed.get("explanation"):
63
+ score += 0.15
64
+ if parsed.get("fixed_code"):
65
+ score += 0.15
66
+ if parsed.get("language"):
67
+ score += 0.1
68
+ except Exception:
69
+ score = 0.3
70
+
71
+ return round(min(max(score, 0.1), 0.9), 2)
72
+
73
+
74
+ # ── Tasks ─────────────────────────────────────────────────────────────────────
75
+ TASKS = [
76
+ {
77
+ "id": "task_1",
78
+ "description": "Fix syntax error in Python",
79
+ "input": "def hello(\n print('hello world')",
80
+ },
81
+ {
82
+ "id": "task_2",
83
+ "description": "Fix logic bug in JavaScript",
84
+ "input": "function add(a, b) { return a - b; }",
85
+ },
86
+ {
87
+ "id": "task_3",
88
+ "description": "Fix type error and missing await in async function",
89
+ "input": "async function fetchData() { let data = fetchFromAPI(); return data.json; }",
90
+ },
91
+ ]
92
+
93
+ for task in TASKS:
94
+ output = solve(task["id"], task["input"])
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ flask
2
+ flask-cors
3
+ python-dotenv
4
+ openai
5
+ gunicorn
6
+ requests