ErNewdev0 commited on
Commit
5152acc
Β·
verified Β·
1 Parent(s): f117e93

feat: adding artefact like chat ui

Browse files
Files changed (1) hide show
  1. app.py +201 -138
app.py CHANGED
@@ -4,11 +4,12 @@ from pathlib import Path
4
  import subprocess
5
  import requests
6
  import json
 
 
7
  from datetime import datetime
8
  import textwrap
9
  import google.generativeai as genai
10
- import asyncio
11
- from typing import Generator, AsyncGenerator
12
  from openai import AsyncOpenAI
13
  import dotenv
14
 
@@ -20,7 +21,7 @@ CURRENT_TIME = "2025-05-23 12:57:22"
20
  CURRENT_USER = "ErRickow"
21
 
22
  # Default API Keys (fallback if user doesn't provide their own)
23
- DEFAULT_XAI_KEY = os.getenv("XAI_API_KEY")
24
  DEFAULT_GEMINI_KEY = os.getenv("GEMINI_API_KEY")
25
 
26
  # API settings
@@ -278,36 +279,99 @@ class RepoAnalyzer:
278
  return False, "Tidak dapat membaca file dengan encoding yang didukung"
279
 
280
  def create_ui():
 
 
281
  analyzer = RepoAnalyzer()
282
 
283
  with gr.Blocks(title="Repository Chat Analysis", theme=gr.themes.Soft()) as app:
284
- # Add CSS for better mobile responsiveness
285
  gr.Markdown("""
286
  <style>
287
- .container {
288
- max-width: 100% !important;
289
- padding: 1rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
291
- .mobile-full {
292
- width: 100% !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  }
294
  @media (max-width: 768px) {
295
- .gr-form {
296
- flex-direction: column !important;
297
- }
298
- .gr-group {
299
- margin: 0.5rem 0 !important;
300
  }
301
  }
302
  </style>
 
 
 
 
 
 
 
 
 
 
 
303
  """)
304
 
305
  with gr.Row(elem_classes="container"):
306
  gr.Markdown(f"""
307
  # πŸ€– Repository Chat Analysis
308
 
309
- πŸ“… Waktu: {CURRENT_TIME}
310
- πŸ‘€ Pengguna: {CURRENT_USER}
311
  """)
312
 
313
  with gr.Tabs() as tabs:
@@ -378,8 +442,7 @@ def create_ui():
378
  placeholder="main",
379
  elem_classes="mobile-full"
380
  )
381
-
382
- # Fix clone repository functionality
383
  clone_button = gr.Button(
384
  "πŸ”„ Clone Repository",
385
  variant="primary",
@@ -391,140 +454,140 @@ def create_ui():
391
  label="Status Repository",
392
  elem_classes="mobile-full"
393
  )
394
-
395
- def handle_clone(repo_url, github_token, branch):
396
- if not repo_url:
397
- return "⚠️ URL repository diperlukan!"
398
- success, message = analyzer.clone_repository(repo_url, github_token, branch)
399
- return message
400
-
401
- # Connect clone button click event
402
- clone_button.click(
403
- fn=handle_clone,
404
- inputs=[repo_url, github_token, branch],
405
- outputs=clone_status
406
- )
407
 
408
- gr.Markdown("""
409
- ### πŸ’‘ Contoh Pertanyaan:
410
- - "Jelaskan struktur utama dari repository ini"
411
- - "Apa saja fitur-fitur utama dalam kode ini?"
412
- - "Bagaimana cara memperbaiki [masalah specific] di repository ini?"
413
- - "Tolong analisis kualitas kode di file [nama file]"
414
- """)
415
-
416
- # Improved chat interface
417
  with gr.Group():
418
- chat_input = gr.Textbox(
419
- label="πŸ’­ Tanyakan tentang Repository",
420
- placeholder="Ketik pertanyaan Anda di sini...",
421
- lines=3,
422
- elem_classes="mobile-full"
423
- )
424
- send_button = gr.Button(
425
- "πŸ“€ Kirim",
426
- variant="primary",
427
- elem_classes="mobile-full"
 
 
 
428
  )
429
-
430
- chat_history = gr.Chatbot(
431
- label="πŸ“ Riwayat Chat",
432
- height=500,
433
- show_label=True,
434
- elem_classes="mobile-full"
435
- )
436
 
437
- # Add loading indicator
438
- with gr.Row():
439
- loading_indicator = gr.HTML(
440
- '<div id="loading" style="display:none">Memproses permintaan...</div>'
 
 
 
441
  )
442
-
443
- async def handle_chat(message, history, provider_choice, model_name, xai_key, gemini_key):
444
- if not analyzer.current_repo:
445
- yield history + [[message, "⚠️ Mohon clone repository terlebih dahulu sebelum mengajukan pertanyaan."]]
446
- return
447
 
448
- history = history or []
449
- history.append([message, ""])
450
-
451
- try:
452
- full_response = ""
453
- if provider_choice == AIProvider.XAI:
454
- async for chunk in analyzer.stream_xai_response(message, xai_key, model_name):
455
- full_response += chunk
456
- history[-1][1] = full_response
457
- yield history
458
- elif provider_choice == AIProvider.GEMINI:
459
- async for chunk in analyzer.stream_gemini_response(message, gemini_key or DEFAULT_GEMINI_KEY):
460
- full_response += chunk
461
- history[-1][1] = full_response
462
- yield history
463
- else: # OLLAMA
464
- response = analyze_with_ollama(model_name, message)
465
- history[-1][1] = response
466
- yield history
467
- except Exception as e:
468
- history[-1][1] = f"⚠️ Error: {str(e)}"
469
- yield history
470
-
471
- # Connect chat events with loading states
472
- send_event = send_button.click(
473
- fn=handle_chat,
474
- inputs=[
475
- chat_input,
476
- chat_history,
477
- provider,
478
- model_dropdown,
479
- xai_key,
480
- gemini_key
481
- ],
482
- outputs=chat_history,
483
- show_progress=True
484
- ).then(
485
- fn=lambda: gr.update(value=""),
486
- outputs=chat_input
487
- )
488
 
489
- input_event = chat_input.submit(
490
- fn=handle_chat,
491
- inputs=[
492
- chat_input,
493
- chat_history,
494
- provider,
495
- model_dropdown,
496
- xai_key,
497
- gemini_key
498
- ],
499
- outputs=chat_history,
500
- show_progress=True
501
- ).then(
502
- fn=lambda: gr.update(value=""),
503
- outputs=chat_input
504
  )
505
 
506
- # Update provider change handler
507
- def update_model_list(provider_choice):
508
- if provider_choice == AIProvider.XAI:
509
- return gr.Dropdown(choices=XAI_MODELS, value="grok-2-latest")
510
- elif provider_choice == AIProvider.GEMINI:
511
- return gr.Dropdown(choices=GEMINI_MODELS, value="gemini-pro")
512
- else: # OLLAMA
513
- return gr.Dropdown(choices=OLLAMA_MODELS, value="llama2")
514
-
515
- provider.change(
516
- fn=update_model_list,
517
- inputs=[provider],
518
- outputs=[model_dropdown]
519
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
 
521
- return app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
 
523
  if __name__ == "__main__":
524
  print(f"""
525
  πŸš€ Memulai Repository Chat Analysis
526
- πŸ“… Waktu: {CURRENT_TIME}
527
- πŸ‘€ Pengguna: {CURRENT_USER}
528
  """)
529
 
530
  app = create_ui()
 
4
  import subprocess
5
  import requests
6
  import json
7
+ import time
8
+ import asyncio
9
  from datetime import datetime
10
  import textwrap
11
  import google.generativeai as genai
12
+ from typing import Generator, AsyncGenerator, List
 
13
  from openai import AsyncOpenAI
14
  import dotenv
15
 
 
21
  CURRENT_USER = "ErRickow"
22
 
23
  # Default API Keys (fallback if user doesn't provide their own)
24
+ DEFAULT_XAI_KEY = os.getenv("XAI_API_KEY", "xai-vfjhklL384Z4HKdItsZomqpFlXubTZJAFnISQUpV7dE8lRnWwYBVPSCxSTlu08wDbAcv720bx2dDiQ9x")
25
  DEFAULT_GEMINI_KEY = os.getenv("GEMINI_API_KEY")
26
 
27
  # API settings
 
279
  return False, "Tidak dapat membaca file dengan encoding yang didukung"
280
 
281
  def create_ui():
282
+ # Get UTC time
283
+ utc_time = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
284
  analyzer = RepoAnalyzer()
285
 
286
  with gr.Blocks(title="Repository Chat Analysis", theme=gr.themes.Soft()) as app:
 
287
  gr.Markdown("""
288
  <style>
289
+ .container { max-width: 100% !important; padding: 1rem; }
290
+ .mobile-full { width: 100% !important; }
291
+ .file-list { margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
292
+ .file-item { display: flex; justify-content: space-between; padding: 5px 0; }
293
+ .file-remove { color: red; cursor: pointer; }
294
+
295
+ /* Enhanced code block styling */
296
+ .wrapper-artifact {
297
+ border: 1px solid #e0e0e0;
298
+ border-radius: 8px;
299
+ margin: 16px 0;
300
+ background: #f8f9fa;
301
+ }
302
+ .header-artifact {
303
+ padding: 8px 16px;
304
+ background: #f1f3f4;
305
+ border-bottom: 1px solid #e0e0e0;
306
+ border-radius: 8px 8px 0 0;
307
+ font-family: monospace;
308
+ display: flex;
309
+ justify-content: space-between;
310
+ align-items: center;
311
+ }
312
+ .content-artifact {
313
+ padding: 16px;
314
+ overflow-x: auto;
315
+ background: #ffffff;
316
+ border-radius: 0 0 8px 8px;
317
+ }
318
+ .content-artifact pre {
319
+ margin: 0;
320
+ padding: 0;
321
+ }
322
+ .copy-button {
323
+ background: #e0e0e0;
324
+ border: none;
325
+ padding: 4px 8px;
326
+ border-radius: 4px;
327
+ cursor: pointer;
328
+ font-size: 12px;
329
  }
330
+ .copy-button:hover {
331
+ background: #d0d0d0;
332
+ }
333
+
334
+ /* Chat interface styling */
335
+ .fullscreen-chat {
336
+ height: calc(100vh - 200px) !important;
337
+ margin: 20px 0;
338
+ }
339
+ .chat-input {
340
+ position: sticky;
341
+ bottom: 0;
342
+ background: white;
343
+ padding: 10px 0;
344
+ border-top: 1px solid #eee;
345
+ }
346
+ .chat-message {
347
+ margin: 8px 0;
348
  }
349
  @media (max-width: 768px) {
350
+ .gr-form { flex-direction: column !important; }
351
+ .gr-group { margin: 0.5rem 0 !important; }
352
+ .fullscreen-chat {
353
+ height: calc(100vh - 300px) !important;
 
354
  }
355
  }
356
  </style>
357
+ <script>
358
+ function copyToClipboard(text) {
359
+ navigator.clipboard.writeText(text).then(() => {
360
+ const button = event.target;
361
+ button.textContent = 'Copied!';
362
+ setTimeout(() => {
363
+ button.textContent = 'Copy';
364
+ }, 2000);
365
+ });
366
+ }
367
+ </script>
368
  """)
369
 
370
  with gr.Row(elem_classes="container"):
371
  gr.Markdown(f"""
372
  # πŸ€– Repository Chat Analysis
373
 
374
+ πŸ“… Current Date and Time (UTC): {utc_time}
 
375
  """)
376
 
377
  with gr.Tabs() as tabs:
 
442
  placeholder="main",
443
  elem_classes="mobile-full"
444
  )
445
+
 
446
  clone_button = gr.Button(
447
  "πŸ”„ Clone Repository",
448
  variant="primary",
 
454
  label="Status Repository",
455
  elem_classes="mobile-full"
456
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
457
 
458
+ # File selection components
 
 
 
 
 
 
 
 
459
  with gr.Group():
460
+ gr.Markdown("### πŸ“Ž File yang Dipilih")
461
+
462
+ with gr.Row():
463
+ file_selector = gr.Dropdown(
464
+ label="Pilih File dari Repository",
465
+ choices=[],
466
+ multiselect=True,
467
+ elem_classes="mobile-full"
468
+ )
469
+
470
+ file_list = gr.HTML(
471
+ value="<div class='file-list'>Belum ada file yang dipilih</div>",
472
+ label="Daftar File Terpilih"
473
  )
 
 
 
 
 
 
 
474
 
475
+ # Enhanced chat interface with fullscreen support
476
+ with gr.Group():
477
+ chat_history = gr.Chatbot(
478
+ label="πŸ“ Riwayat Chat",
479
+ elem_classes="fullscreen-chat",
480
+ height=None, # Let CSS handle the height
481
+ show_label=True
482
  )
 
 
 
 
 
483
 
484
+ with gr.Group(elem_classes="chat-input"):
485
+ chat_input = gr.Textbox(
486
+ label="πŸ’­ Tanyakan tentang Repository",
487
+ placeholder="Ketik pertanyaan Anda di sini...",
488
+ lines=3,
489
+ elem_classes="mobile-full"
490
+ )
491
+ with gr.Row():
492
+ clear_button = gr.Button("🧹 Bersihkan", variant="secondary")
493
+ send_button = gr.Button("πŸ“€ Kirim", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
 
495
+ loading_indicator = gr.HTML(
496
+ '<div id="loading" style="display:none">Memproses permintaan...</div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  )
498
 
499
+ async def handle_chat(message, history, provider_choice, model_name, xai_key, gemini_key, selected_files):
500
+ if not analyzer.current_repo:
501
+ yield history + [[message, "⚠️ Mohon clone repository terlebih dahulu sebelum mengajukan pertanyaan."]]
502
+ return
503
+
504
+ history = history or []
505
+ history.append([message, ""])
506
+
507
+ try:
508
+ # Add context about selected files to the prompt
509
+ file_context = ""
510
+ if selected_files:
511
+ file_context = "\n\nFile yang dipilih:\n"
512
+ for file in selected_files:
513
+ content = analyzer.repo_content.get(file, "")
514
+ # Wrap code blocks in custom artifact styling
515
+ file_context += f"""
516
+ <div class="wrapper-artifact">
517
+ <div class="header-artifact">
518
+ <span>{file}</span>
519
+ <button class="copy-button" onclick="copyToClipboard(`{content.replace('`', '\\`')}`)">Copy</button>
520
+ </div>
521
+ <div class="content-artifact">
522
+ <pre><code>{content}</code></pre>
523
+ </div>
524
+ </div>
525
+ """
526
+
527
+ enhanced_message = f"{message}\n{file_context}"
528
+
529
+ full_response = ""
530
+ if provider_choice == AIProvider.XAI:
531
+ async for chunk in analyzer.stream_xai_response(enhanced_message, xai_key, model_name):
532
+ # Wrap code blocks in custom styling
533
+ chunk = process_code_blocks(chunk)
534
+ full_response += chunk
535
+ await asyncio.sleep(0.08)
536
+ history[-1][1] = full_response
537
+ yield history
538
+
539
+ elif provider_choice == AIProvider.GEMINI:
540
+ async for chunk in analyzer.stream_gemini_response(enhanced_message, gemini_key or DEFAULT_GEMINI_KEY):
541
+ chunk = process_code_blocks(chunk)
542
+ full_response += chunk
543
+ await asyncio.sleep(0.08)
544
+ history[-1][1] = full_response
545
+ yield history
546
+
547
+ else: # OLLAMA
548
+ response = analyze_with_ollama(model_name, enhanced_message)
549
+ response = process_code_blocks(response)
550
+ words = response.split()
551
+ for i in range(len(words)):
552
+ full_response = " ".join(words[:i+1])
553
+ await asyncio.sleep(0.08)
554
+ history[-1][1] = full_response
555
+ yield history
556
+
557
+ except Exception as e:
558
+ history[-1][1] = f"⚠️ Error: {str(e)}"
559
+ yield history
560
 
561
+ def process_code_blocks(text):
562
+ """Process markdown code blocks to use custom artifact styling"""
563
+ import re
564
+
565
+ # Pattern for code blocks with language specification
566
+ pattern = r"```(\w+)\n(.*?)```"
567
+
568
+ def replace_code_block(match):
569
+ language = match.group(1)
570
+ code = match.group(2)
571
+ return f"""
572
+ <div class="wrapper-artifact">
573
+ <div class="header-artifact">
574
+ <span>{language}</span>
575
+ <button class="copy-button" onclick="copyToClipboard(`{code.replace('`', '\\`')}`)">Copy</button>
576
+ </div>
577
+ <div class="content-artifact">
578
+ <pre><code>{code}</code></pre>
579
+ </div>
580
+ </div>
581
+ """
582
+
583
+ # Replace all code blocks in the text
584
+ processed_text = re.sub(pattern, replace_code_block, text, flags=re.DOTALL)
585
+ return processed_text
586
 
587
  if __name__ == "__main__":
588
  print(f"""
589
  πŸš€ Memulai Repository Chat Analysis
590
+ πŸ“… Current Date and Time (UTC): {datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")}
 
591
  """)
592
 
593
  app = create_ui()