File size: 5,722 Bytes
1b8f38d
45ad2d3
1b8f38d
da14dd5
 
45ad2d3
c0bf4e7
45ad2d3
156abc5
da14dd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbac21f
da14dd5
 
 
 
 
 
156abc5
da14dd5
 
 
 
1b8f38d
 
156abc5
1b8f38d
 
 
 
 
 
 
479b25d
156abc5
 
 
1b8f38d
 
 
 
 
 
da14dd5
1b8f38d
 
e930e3c
1b8f38d
e930e3c
 
 
 
 
e5725e4
e930e3c
 
 
 
1b8f38d
da14dd5
90865a6
180d94b
 
 
e79845d
4056b9a
 
 
db2c51d
4056b9a
 
e79845d
bd8e79a
db2c51d
bd8e79a
0b4b5fb
 
e79845d
 
bd8e79a
 
 
 
db2c51d
bd8e79a
db2c51d
e79845d
 
 
bd8e79a
 
e79845d
 
 
 
 
 
0b4b5fb
 
4056b9a
0b4b5fb
 
bd8e79a
 
e79845d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce648f5
22d7670
ce648f5
 
 
 
0b4b5fb
0b5541e
180d94b
22d7670
 
ce648f5
2ada91d
ce648f5
0b5541e
180d94b
0b5541e
180d94b
 
 
 
 
0b5541e
db2c51d
180d94b
e79845d
0b5541e
db2c51d
 
0b5541e
db2c51d
 
0b5541e
db2c51d
 
180d94b
e930e3c
298cb2c
90865a6
156abc5
0b5541e
156abc5
90865a6
156abc5
0b5541e
 
156abc5
829ae92
156abc5
e930e3c
 
0b5541e
 
e5725e4
156abc5
e930e3c
90865a6
da14dd5
dbac21f
94d1c9f
156abc5
 
 
dbac21f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import os
import gradio as gr
from openai import OpenAI
import subprocess
import tempfile

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# --- Core logic ---
def analyze_code(language, code):
    error_output = ""

    if language.lower() == "python":
        with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as tmp:
            tmp.write(code.encode("utf-8"))
            tmp.flush()
            try:
                subprocess.run(
                    ["python3", tmp.name],
                    check=True,
                    capture_output=True,
                    text=True,
                    timeout=5,
                )
            except subprocess.CalledProcessError as e:
                error_output = e.stderr
            except Exception as e:
                error_output = str(e)
    else:
        error_output = "Automatic execution only supported for Python. Please paste your error manually."

    if not error_output.strip():
        error_output = "βœ… No runtime error detected. Let's review for logic or syntax improvements."

    return help_with_code(language, code, error_output)


def help_with_code(language, code, error):
    prompt = f"""
    You are a skilled coding instructor identifying skill gaps in students learning {language}.
    --- CODE START ---
    {code}
    --- CODE END ---
    --- ERROR ---
    {error}
    --- END ERROR ---
    Your task:
    1. Identify my coding skill gaps (syntax, logic, structure, or language fundamentals).
    2. Explain clearly what went wrong and why.
    3. Suggest one or two short, focused practice exercises to improve that specific skill.
    4. Do NOT rewrite or fix the code.
    """

    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.4,
        )
        return response.choices[0].message.content.strip()

    except Exception as e:
        err_msg = str(e)

        # βœ… Handle inactive billing error cleanly
        if "billing_not_active" in err_msg or "Error code: 429" in err_msg:
            return (
                "⚠️ Please contact an RC staff member β€” the billing is currently not active."
            )
        
        # Generic fallback
        return "⚠️ An unexpected error occurred. Please try again later or verify your API key."


# --- Gradio UI ---
with gr.Blocks(
    theme="soft",
    css="""
/* === Apply theme colors immediately === */
body, .gradio-container, .dark, [data-theme="dark"] {
  background-color: var(--body-background-fill) !important;
  color: var(--body-text-color) !important;
  transition: none !important;
}

/* === Base Theme Variables (Light Mode) === */
:root {
  --body-background-fill: #ffffff;
  --body-text-color: #1a1a1a;
  --codebox-background: #f8f8f8;
  --codebox-border: #EDEBEB;
  --scrollbar-thumb: #c1c1c1;
  --scrollbar-track: #f8f8f8;
}

/* πŸŒ™ Dark Mode Theme */
:root .dark, [data-theme="dark"] {
  --body-background-fill: #1e1e1f !important;
  --body-text-color: #e5e5e5 !important;
  --codebox-background: #2a2a2b !important;
  --codebox-border: #3a3a3b !important;
  --scrollbar-thumb: #555 !important;
  --scrollbar-track: #2a2a2b !important;
}

/* 🧱 Code boxes, dropdowns, textareas unified */
#code-box,
select,
textarea,
pre,
code {
  background-color: var(--codebox-background) !important;
  border: 1px solid var(--codebox-border) !important;
  border-radius: 6px !important;
  color: var(--body-text-color) !important;
  transition: none !important;
}

/* ✨ Consistent scrollbar theme */
#code-box::-webkit-scrollbar,
textarea::-webkit-scrollbar,
pre::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
#code-box::-webkit-scrollbar-thumb,
textarea::-webkit-scrollbar-thumb,
pre::-webkit-scrollbar-thumb {
  background-color: var(--scrollbar-thumb);
  border-radius: 10px;
}
#code-box::-webkit-scrollbar-track,
textarea::-webkit-scrollbar-track,
pre::-webkit-scrollbar-track {
  background-color: var(--scrollbar-track);
}

/* 🎯 Submit button */
#submit-btn {
  background-color: #e40014 !important;
  color: white !important;
  border: none !important;
  border-radius: 6px !important;
  transition: background-color 0.2s ease-in-out;
  margin-top: 6px !important;
  width: 100%;
}
#submit-btn:hover {
  background-color: #c00010 !important;
}

/* 🧩 Compact layout */
.gr-block, .gr-form, .gr-column, .gr-row {
  gap: 4px !important;
  padding: 0 !important;
  margin: 0 !important;
}
.gr-markdown {
  margin: 0 !important;
  padding: 2px !important;
  line-height: 1.4 !important;
}

/* βœ… Feedback area spacing */
.gr-markdown p {
  margin-top: 0 !important;
  margin-bottom: 4px !important;
}

/* Prevent reflow on feedback render */
.gr-column > *:not(:last-child) {
  margin-bottom: 4px !important;
}
"""
) as demo:
    with gr.Row(equal_height=True):
        with gr.Column(scale=2):
            # Removed markdown labels to reduce vertical spacing
            language = gr.Dropdown(
                ["JavaScript", "HTML", "CSS", "Python"],
                value="Python",
                show_label=True,
                label="Language",
            )
            
            code_input = gr.Code(
                language="python",
                lines=22,
                show_label=True,
                label="Your Code",
                elem_id="code-box",
            )
            
            analyze_button = gr.Button("Submit", variant="secondary", elem_id="submit-btn")

        with gr.Column(scale=1):
            output = gr.Markdown("*Feedback will show here!*")

    analyze_button.click(analyze_code, inputs=[language, code_input], outputs=output)

demo.launch()