File size: 11,699 Bytes
fb39837
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
import os
import gradio as gr
import time
import uuid
from typing import Dict, List
import markdown
import re
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter

# Untuk integrasi dengan model AI (misalnya Hugging Face Transformers atau API eksternal)
import requests
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

# Konfigurasi dasar
AI_MODEL_ID = os.environ.get("AI_MODEL_ID", "TheBloke/Mistral-7B-Instruct-v0.2-GPTQ")
API_TOKEN = os.environ.get("HUGGINGFACE_API_TOKEN", None)
MAX_HISTORY_LENGTH = 10
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"

# Dictionary untuk menyimpan semua sesi chat
active_sessions: Dict[str, List[Dict]] = {}

# Inisialisasi model dan tokenizer
@torch.inference_mode()
def initialize_model():
    try:
        print(f"Loading model {AI_MODEL_ID} on {DEVICE}...")
        tokenizer = AutoTokenizer.from_pretrained(AI_MODEL_ID)
        model = AutoModelForCausalLM.from_pretrained(
            AI_MODEL_ID,
            device_map=DEVICE,
            torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32
        )
        print("Model loaded successfully!")
        return model, tokenizer
    except Exception as e:
        print(f"Error loading model: {e}")
        return None, None

model, tokenizer = initialize_model()

# Fungsi untuk memformat kode dengan syntax highlighting
def format_code_blocks(text):
    def replace_code_block(match):
        language = match.group(1) or "python"
        code = match.group(2)
        try:
            lexer = get_lexer_by_name(language, stripall=True)
            formatter = HtmlFormatter(style="github", cssclass="syntax-highlight")
            result = highlight(code, lexer, formatter)
            return f'<div class="code-block">{result}</div>'
        except:
            # Fallback jika bahasa tidak dikenali
            return f'<pre><code class="{language}">{code}</code></pre>'
    
    # Cari dan ganti semua code blocks markdown
    pattern = r'```(\w+)?\n([\s\S]+?)\n```'
    return re.sub(pattern, replace_code_block, text)

# Fungsi untuk memproses pesan dan mendapatkan respons AI
@torch.inference_mode()
def process_message(message, history, session_id):
    if session_id not in active_sessions:
        active_sessions[session_id] = []
    
    # Tambahkan pesan pengguna ke history sesi
    if len(active_sessions[session_id]) >= MAX_HISTORY_LENGTH:
        active_sessions[session_id].pop(0)
    active_sessions[session_id].append({"role": "user", "content": message})
    
    # Konversi history ke format prompt untuk model
    prompt = format_prompt(active_sessions[session_id])
    
    # Jalankan inferensi dengan animasi loading
    yield "βŒ› Thinking..."
    time.sleep(0.5)
    yield "βŒ› Generating response..."
    
    try:
        # Gunakan model untuk generate respons
        inputs = tokenizer(prompt, return_tensors="pt").to(DEVICE)
        output = model.generate(
            inputs["input_ids"],
            max_length=2048,
            temperature=0.7,
            top_p=0.9,
            do_sample=True,
            pad_token_id=tokenizer.eos_token_id
        )
        
        # Proses output
        response = tokenizer.decode(output[0], skip_special_tokens=True)
        # Ekstrak hanya bagian respons AI dari keseluruhan output
        ai_response = extract_ai_response(response, prompt)
        
        # Tambahkan respons AI ke history sesi
        active_sessions[session_id].append({"role": "assistant", "content": ai_response})
        
        # Format respons dengan markdown dan syntax highlighting untuk kode
        formatted_response = format_code_blocks(markdown.markdown(ai_response))
        
        return formatted_response
    except Exception as e:
        error_msg = f"Error generating response: {str(e)}"
        print(error_msg)
        return f"<span style='color: red;'>{error_msg}</span>"

# Format prompt sesuai dengan kebutuhan model
def format_prompt(messages):
    prompt = ""
    for msg in messages:
        if msg["role"] == "user":
            prompt += f"USER: {msg['content']}\n"
        else:
            prompt += f"ASSISTANT: {msg['content']}\n"
    prompt += "ASSISTANT: "
    return prompt

# Ekstrak respons AI dari output model
def extract_ai_response(full_response, prompt):
    # Hapus prompt dari respons untuk mendapatkan hanya output baru
    if full_response.startswith(prompt):
        return full_response[len(prompt):].strip()
    return full_response.strip()

# Fungsi untuk membuat sesi baru
def create_new_session():
    session_id = str(uuid.uuid4())
    active_sessions[session_id] = []
    return session_id, []

# Debug mode untuk membantu debugging kode
def debug_code(code, session_id):
    try:
        # Simulasi proses debugging
        yield "πŸ” Analyzing code..."
        time.sleep(1)
        
        # Cek sintaks dasar
        compile(code, '<string>', 'exec')
        yield "βœ… Syntax check passed"
        time.sleep(0.5)
        
        # Analisis kode sederhana
        lines = code.split('\n')
        issues = []
        
        # Cek beberapa masalah umum
        for i, line in enumerate(lines):
            if 'print(' in line and not line.strip().endswith(')'):
                issues.append(f"Line {i+1}: Missing closing parenthesis in print statement")
            if '#' not in line and line.strip().endswith(':') and i+1 < len(lines) and not lines[i+1].startswith(' '):
                issues.append(f"Line {i+1}: Missing indentation after control statement")
        
        if issues:
            yield "πŸ”΄ Found potential issues:\n" + "\n".join(issues)
        else:
            # Simulasi eksekusi
            yield "🟒 No obvious issues detected. Running code..."
            time.sleep(1)
            
            # Tambahkan respons ke sesi
            if session_id in active_sessions:
                active_sessions[session_id].append({
                    "role": "assistant", 
                    "content": f"I've analyzed your code and it looks good syntactically. Here are some tips for improvement:\n\n```python\n{code}\n```\n\nConsider adding more comments and error handling for better robustness."
                })
            
            yield "βœ… Code analysis complete. The code appears to be valid Python code."
    except SyntaxError as e:
        error_msg = f"πŸ”΄ Syntax Error: {str(e)}"
        yield error_msg
        
        # Tambahkan analisis ke sesi
        if session_id in active_sessions:
            active_sessions[session_id].append({
                "role": "assistant", 
                "content": f"I found a syntax error in your code:\n\n```python\n{code}\n```\n\nError: {str(e)}\n\nPlease check your syntax and try again."
            })
    except Exception as e:
        error_msg = f"πŸ”΄ Error during analysis: {str(e)}"
        yield error_msg

# CSS kustom untuk UI yang lebih baik
custom_css = """
.container {max-width: 850px; margin: auto;}
.chat-message {padding: 12px; border-radius: 10px; margin-bottom: 10px; position: relative;}
.user-message {background-color: #e6f7ff; text-align: right; margin-left: 20%;}
.bot-message {background-color: #f2f2f2; margin-right: 20%;}
.timestamp {font-size: 0.7em; color: #888; position: absolute; bottom: 2px; right: 10px;}
.syntax-highlight {border-radius: 5px; padding: 10px !important; margin: 15px 0 !important; overflow-x: auto;}
.code-block {border: 1px solid #ddd; border-radius: 5px; margin: 10px 0;}
.typing-indicator {font-style: italic; color: #888;}
"""

# Fungsi untuk membangun antarmuka Gradio
def build_ui():
    with gr.Blocks(css=custom_css) as demo:
        gr.Markdown("# AI Chat with Code Capabilities")
        
        with gr.Row():
            with gr.Column(scale=3):
                # Chat interface utama
                chatbot = gr.Chatbot(
                    label="Conversation",
                    height=500,
                    elem_classes="container"
                )
                
                with gr.Row():
                    message_input = gr.Textbox(
                        label="Your message",
                        placeholder="Ask anything or paste code for debugging...",
                        lines=3
                    )
                
                with gr.Row():
                    submit_btn = gr.Button("Send", variant="primary")
                    clear_btn = gr.Button("Clear Chat")
                    debug_btn = gr.Button("Debug Code", variant="secondary")
            
            with gr.Column(scale=1):
                # Sidebar untuk manajemen sesi
                new_session_btn = gr.Button("New Session")
                session_info = gr.Textbox(label="Current Session", value="", visible=False)
                
                # Info model
                gr.Markdown(f"### Model Info\n- Using: {AI_MODEL_ID}\n- Device: {DEVICE}")
                
                # Settings
                temperature = gr.Slider(
                    minimum=0.1, maximum=1.5, value=0.7, step=0.1,
                    label="Temperature (Creativity)"
                )
                
                # Status
                status_box = gr.Textbox(label="Status", value="Ready")
        
        # Hidden state untuk session ID
        session_id = gr.State(str(uuid.uuid4()))
        
        # Fungsi callback
        def on_submit(message, chat_history, sid):
            if not message.strip():
                return "", chat_history
            
            # Update chat history untuk UI
            chat_history.append([message, None])
            status_box.update(value="Generating response...")
            
            return "", chat_history
        
        submit_btn.click(
            on_submit, 
            [message_input, chatbot, session_id], 
            [message_input, chatbot]
        ).then(
            process_message,
            [message_input, chatbot, session_id],
            chatbot
        ).then(
            lambda: "Ready",
            None,
            status_box
        )
        
        # Debug code button behavior
        def on_debug(message, chat_history, sid):
            if not message.strip():
                return chat_history, "Please enter code to debug"
            
            chat_history.append([message, None])
            return chat_history, "Debugging code..."
        
        debug_btn.click(
            on_debug,
            [message_input, chatbot, session_id],
            [chatbot, status_box]
        ).then(
            debug_code,
            [message_input, session_id],
            chatbot
        ).then(
            lambda: "Ready",
            None,
            status_box
        )
        
        # New session button behavior
        def start_new_session():
            new_sid = str(uuid.uuid4())
            active_sessions[new_sid] = []
            return new_sid, [], f"New session started: {new_sid[:8]}...", "Ready"
        
        new_session_btn.click(
            start_new_session,
            None,
            [session_id, chatbot, session_info, status_box]
        )
        
        # Clear chat button behavior
        clear_btn.click(lambda sid: ([], f"Session cleared: {sid[:8]}...", "Ready"), 
                        [session_id], 
                        [chatbot, session_info, status_box])
    
    return demo

# Main function
def main():
    demo = build_ui()
    
    # Launch app
    demo.queue(concurrency_count=5).launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        debug=False
    )

if __name__ == "__main__":
    main()