ErNewdev0 commited on
Commit
eb48ce9
·
verified ·
1 Parent(s): 8562792

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -723
app.py DELETED
@@ -1,723 +0,0 @@
1
- import gradio as gr
2
- import os
3
- import time
4
- from pathlib import Path
5
- import subprocess
6
- import requests
7
- import json
8
- from datetime import datetime
9
- import textwrap
10
- import google.generativeai as genai
11
- import asyncio
12
- from typing import Generator, AsyncGenerator, List
13
- from openai import AsyncOpenAI
14
- import dotenv
15
-
16
- # Load environment variables
17
- dotenv.load_dotenv()
18
-
19
- # Metadata
20
- CURRENT_TIME = "2025-05-23 12:57:22"
21
- CURRENT_USER = "ErRickow"
22
-
23
- # Default API Keys (fallback if user doesn't provide their own)
24
- DEFAULT_XAI_KEY = os.getenv(
25
- "XAI_API_KEY",
26
- "xai-vfjhklL384Z4HKdItsZomqpFlXubTZJAFnISQUpV7dE8lRnWwYBVPSCxSTlu08wDbAcv720bx2dDiQ9x",
27
- )
28
- DEFAULT_GEMINI_KEY = os.getenv("GEMINI_API_KEY")
29
-
30
- # API settings
31
- OLLAMA_API = os.environ.get("OLLAMA_API", "http://localhost:11434")
32
- XAI_BASE_URL = "https://api.x.ai/v1"
33
-
34
- # Model lists
35
- OLLAMA_MODELS = [
36
- "llama2",
37
- "codellama",
38
- "mistral",
39
- "neural-chat",
40
- "starling-lm",
41
- "dolphin-phi",
42
- "phi",
43
- "orca-mini",
44
- ]
45
-
46
- XAI_MODELS = [
47
- "grok-2-latest",
48
- "grok-1",
49
- ]
50
-
51
- GEMINI_MODELS = [
52
- "gemini-1.5-mini",
53
- "gemini-pro-vision",
54
- ]
55
-
56
- # Help texts
57
-
58
- GITHUB_TOKEN_HELP = """
59
- ### Cara Mendapatkan GitHub Token:
60
-
61
- 1. Kunjungi [GitHub Token Settings](https://github.com/settings/tokens)
62
- 2. Klik "Generate new token" > "Generate new token (classic)"
63
- 3. Beri nama token Anda di "Note"
64
- 4. Pilih scope:
65
- - `repo` (untuk akses repository private)
66
- - `read:packages` (opsional, untuk akses package)
67
- 5. Klik "Generate token"
68
- 6. **PENTING**: Salin token segera! Token hanya ditampilkan sekali
69
-
70
- Token diperlukan untuk:
71
- - Mengakses repository private
72
- - Clone repository dengan rate limit lebih tinggi
73
- - Mengakses fitur GitHub API
74
- """
75
-
76
- GEMINI_API_HELP = """
77
- ### Cara Mendapatkan Gemini API Key:
78
-
79
- 1. Kunjungi [Google AI Studio](https://makersuite.google.com/app/apikey)
80
- 2. Login dengan akun Google Anda
81
- 3. Klik "Create API Key"
82
- 4. Salin API Key yang dihasilkan
83
-
84
- Catatan:
85
- - Gemini memberikan kuota gratis setiap bulan
86
- - Key bisa dibuat ulang jika diperlukan
87
- - Monitor penggunaan di [Google Cloud Console](https://console.cloud.google.com/)
88
- """
89
-
90
- OLLAMA_HELP = """
91
- ### Cara Menggunakan Ollama:
92
-
93
- 1. Install Ollama dari [ollama.ai](https://ollama.ai)
94
- 2. Jalankan Ollama di komputer Anda
95
- 3. Pastikan Ollama berjalan di http://localhost:11434
96
-
97
- Catatan:
98
- - Ollama berjalan secara lokal di komputer Anda
99
- - Tidak memerlukan API key
100
- - Ideal untuk privasi dan penggunaan offline
101
- """
102
-
103
- XAI_API_HELP = """
104
- ### Cara Mendapatkan X.AI (Grok) API Key:
105
-
106
- 1. Kunjungi [X.AI Developer Portal](https://x.ai)
107
- 2. Daftar/Login ke akun Anda
108
- 3. Buat API Key baru
109
- 4. Salin API Key
110
-
111
- Note:
112
- - Jika tidak diisi, akan menggunakan API key default
113
- - Masukkan API key Anda sendiri jika default mencapai limit
114
- """
115
-
116
-
117
- class AIProvider:
118
- OLLAMA = "ollama"
119
- GEMINI = "gemini"
120
- XAI = "xai"
121
-
122
-
123
- class RepoAnalyzer:
124
- def __init__(self):
125
- self.current_repo = None
126
- self.repo_content = {}
127
- self.chat_history = []
128
-
129
- async def stream_xai_response(
130
- self, prompt: str, api_key: str = None, model: str = "grok-2-latest"
131
- ) -> AsyncGenerator[str, None]:
132
- """Stream response dari X.AI (Grok) API"""
133
- try:
134
- # Use default key if none provided
135
- actual_key = api_key if api_key else DEFAULT_XAI_KEY
136
-
137
- if not actual_key:
138
- yield "⚠️ API Key X.AI diperlukan. Gunakan key Anda sendiri atau tunggu reset limit default key."
139
- return
140
-
141
- client = AsyncOpenAI(api_key=actual_key, base_url=XAI_BASE_URL)
142
-
143
- # Prepare messages with repository context if available
144
- messages = [
145
- {
146
- "role": "system",
147
- "content": "Anda adalah asisten AI yang membantu menganalisis repository code. Berikan respons dalam Bahasa Indonesia.",
148
- }
149
- ]
150
-
151
- if self.current_repo:
152
- context = f"Repository: {self.current_repo}\n\n"
153
- repo_files = "\n".join(list(self.repo_content.keys()))
154
- context += f"Files in repository:\n{repo_files}\n\n"
155
- messages.append({"role": "system", "content": context})
156
-
157
- messages.append({"role": "user", "content": prompt})
158
-
159
- stream = await client.chat.completions.create(
160
- model=model, messages=messages, stream=True
161
- )
162
-
163
- full_response = ""
164
- async for chunk in stream:
165
- if chunk.choices[0].delta.content:
166
- content = chunk.choices[0].delta.content
167
- full_response += content
168
- yield content
169
-
170
- self.chat_history.append({"role": "user", "content": prompt})
171
- self.chat_history.append({"role": "assistant", "content": full_response})
172
-
173
- except Exception as e:
174
- error_msg = f"⚠️ Error dalam X.AI API: {str(e)}"
175
- print(error_msg)
176
- yield error_msg
177
-
178
- async def stream_gemini_response(
179
- self, prompt: str, api_key: str
180
- ) -> AsyncGenerator[str, None]:
181
- """Stream response dari Gemini API"""
182
- try:
183
- if not api_key:
184
- yield "⚠️ API Key Gemini diperlukan. Klik icon bantuan (?) di samping input API Key untuk panduan mendapatkan key."
185
- return
186
-
187
- genai.configure(api_key=api_key)
188
- model = genai.GenerativeModel("gemini-pro")
189
-
190
- # Tambahkan konteks repository jika ada
191
- if self.current_repo:
192
- context = f"Repository: {self.current_repo}\n\n"
193
- repo_files = "\n".join(list(self.repo_content.keys()))
194
- context += f"Files in repository:\n{repo_files}\n\n"
195
- prompt = context + prompt
196
-
197
- response = model.generate_content(
198
- prompt,
199
- generation_config={"temperature": 0.7, "top_p": 0.8, "top_k": 40},
200
- stream=True,
201
- )
202
-
203
- full_response = ""
204
- async for chunk in response:
205
- if chunk.text:
206
- full_response += chunk.text
207
- yield chunk.text
208
-
209
- self.chat_history.append({"role": "user", "content": prompt})
210
- self.chat_history.append({"role": "assistant", "content": full_response})
211
-
212
- except Exception as e:
213
- error_msg = f"⚠️ Error dalam Gemini API: {str(e)}\n\nPastikan API Key valid dan memiliki kuota yang cukup."
214
- print(error_msg)
215
- yield error_msg
216
-
217
- def clone_repository(
218
- self, repo_url: str, github_token: str, branch: str = None
219
- ) -> tuple[bool, str]:
220
- """Clone repository GitHub dengan autentikasi"""
221
- if not repo_url:
222
- return False, "⚠️ URL repository diperlukan"
223
-
224
- repo_name = repo_url.split("/")[-1].replace(".git", "")
225
-
226
- if os.path.exists(repo_name):
227
- subprocess.run(["rm", "-rf", repo_name], check=True)
228
-
229
- try:
230
- owner_repo = "/".join(repo_url.split("/")[-2:])
231
-
232
- # Cek apakah repository private
233
- headers = {"Authorization": f"token {github_token}"} if github_token else {}
234
- repo_check = requests.get(
235
- f"https://api.github.com/repos/{owner_repo}", headers=headers
236
- )
237
-
238
- if repo_check.status_code == 404:
239
- return False, "⚠️ Repository tidak ditemukan. Periksa URL repository."
240
- elif repo_check.status_code == 401:
241
- return (
242
- False,
243
- "⚠️ Token GitHub tidak valid. Klik icon bantuan (?) untuk panduan mendapatkan token.",
244
- )
245
- elif repo_check.status_code == 403 and repo_check.json().get(
246
- "private", False
247
- ):
248
- return (
249
- False,
250
- "⚠️ Ini adalah repository private. Token GitHub dengan akses 'repo' diperlukan.",
251
- )
252
-
253
- auth_url = (
254
- f"https://{github_token}@github.com/{owner_repo}"
255
- if github_token
256
- else f"https://github.com/{owner_repo}"
257
- )
258
-
259
- cmd = ["git", "clone"]
260
- if branch:
261
- cmd.extend(["--branch", branch])
262
- cmd.append(auth_url)
263
-
264
- process = subprocess.run(
265
- cmd,
266
- capture_output=True,
267
- text=True,
268
- env=dict(os.environ, GIT_ASKPASS="echo", GIT_TERMINAL_PROMPT="0"),
269
- )
270
-
271
- if process.returncode == 0:
272
- self.current_repo = repo_name
273
- # Scan dan simpan konten repository
274
- file_count = 0
275
- for file_path in Path(repo_name).rglob("*"):
276
- if file_path.is_file() and ".git" not in str(file_path):
277
- success, content = self.read_file_safely(str(file_path))
278
- if success:
279
- self.repo_content[str(file_path)] = content
280
- file_count += 1
281
-
282
- return (
283
- True,
284
- f"✅ Repository berhasil di-clone!\n\nNama: {repo_name}\nJumlah file: {file_count}\n\nAnda sekarang bisa mengajukan pertanyaan tentang repository ini.",
285
- )
286
- else:
287
- return False, f"⚠️ Gagal clone repository:\n{process.stderr}"
288
-
289
- except Exception as e:
290
- return False, f"⚠️ Error: {str(e)}"
291
-
292
- def read_file_safely(self, file_path: str) -> tuple[bool, str]:
293
- """Baca file dengan aman menggunakan berbagai encoding"""
294
- encodings = ["utf-8", "latin-1", "cp1252"]
295
- for encoding in encodings:
296
- try:
297
- with open(file_path, "r", encoding=encoding) as f:
298
- content = f.read()
299
- return True, content
300
- except Exception as e:
301
- continue
302
- return False, "Tidak dapat membaca file dengan encoding yang didukung"
303
-
304
- analyzer = RepoAnalyzer()
305
-
306
- async def handle_chat(
307
- message,
308
- history,
309
- provider_choice,
310
- model_name,
311
- xai_key,
312
- gemini_key,
313
- selected_files,
314
- analyzer=analyzer,
315
- ):
316
- """Menangani interaksi chat dengan model AI"""
317
- if not analyzer.current_repo:
318
- new_message = {
319
- "role": "assistant",
320
- "content": "⚠️ Mohon clone repository terlebih dahulu sebelum mengajukan pertanyaan.",
321
- }
322
- history = history or []
323
- history.append({"role": "user", "content": message})
324
- history.append(new_message)
325
- yield history
326
- return
327
-
328
- history = history or []
329
- history.append({"role": "user", "content": message})
330
- history.append({"role": "assistant", "content": ""})
331
-
332
- try:
333
- # Add context about selected files to the prompt
334
- file_context = ""
335
- if selected_files:
336
- file_context = "\n\nFile yang dipilih:\n"
337
- for file in selected_files:
338
- content = analyzer.repo_content.get(file, "")
339
- if content: # Only include files that exist
340
- file_context += f"\n{file}:\n```\n{content}\n```\n"
341
-
342
- enhanced_message = f"{message}\n{file_context}"
343
-
344
- full_response = ""
345
- if provider_choice == AIProvider.XAI:
346
- async for chunk in analyzer.stream_xai_response(
347
- enhanced_message, xai_key, model_name
348
- ):
349
- full_response += chunk
350
- # Add delay between chunks for readability
351
- await asyncio.sleep(0.05)
352
- history[-1]["content"] = full_response
353
- yield history
354
-
355
- elif provider_choice == AIProvider.GEMINI:
356
- async for chunk in analyzer.stream_gemini_response(
357
- enhanced_message, gemini_key or DEFAULT_GEMINI_KEY
358
- ):
359
- full_response += chunk
360
- # Add delay between chunks for readability
361
- await asyncio.sleep(0.05)
362
- history[-1]["content"] = full_response
363
- yield history
364
-
365
- else: # OLLAMA
366
- response = analyze_with_ollama(model_name, enhanced_message)
367
- # Simulate streaming for OLLAMA with delay
368
- words = response.split()
369
- for i in range(len(words)):
370
- full_response = " ".join(words[: i + 1])
371
- await asyncio.sleep(0.05)
372
- history[-1]["content"] = full_response
373
- yield history
374
-
375
- except Exception as e:
376
- history[-1]["content"] = f"⚠️ Error: {str(e)}"
377
- yield history
378
-
379
-
380
- def create_ui():
381
- """
382
- Membuat antarmuka pengguna untuk aplikasi Open Repo AI menggunakan Gradio.
383
- """
384
- # Gunakan analyzer global
385
- global analyzer
386
- current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
387
-
388
- # Define CSS styles outside the main UI for better maintainability
389
- CSS_STYLES = """
390
- <style>
391
- .container {
392
- max-width: 100% !important;
393
- padding: 1rem;
394
- margin: 0 auto;
395
- }
396
- .header {
397
- margin-bottom: 2rem;
398
- padding: 1rem;
399
- background: #f8f9fa;
400
- border-radius: 8px;
401
- }
402
- .mobile-full {
403
- width: 100% !important;
404
- }
405
- .file-list {
406
- margin: 10px 0;
407
- padding: 10px;
408
- border: 1px solid #ddd;
409
- border-radius: 4px;
410
- background: #fff;
411
- }
412
- .file-item {
413
- display: flex;
414
- justify-content: space-between;
415
- padding: 8px;
416
- margin: 4px 0;
417
- background: #f8f9fa;
418
- border-radius: 4px;
419
- }
420
- .quick-example-btn {
421
- margin: 5px;
422
- padding: 8px 15px;
423
- border: 1px solid #e9ecef;
424
- border-radius: 4px;
425
- background: #f8f9fa;
426
- cursor: pointer;
427
- transition: all 0.3s ease;
428
- }
429
- .quick-example-btn:hover {
430
- background: #e9ecef;
431
- transform: translateY(-1px);
432
- }
433
- .chat-container {
434
- margin-top: 2rem;
435
- padding: 1rem;
436
- background: #fff;
437
- border-radius: 8px;
438
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
439
- }
440
- .help-text {
441
- font-size: 0.9rem;
442
- color: #6c757d;
443
- margin: 0.5rem 0;
444
- }
445
- @media (max-width: 768px) {
446
- .gr-form {
447
- flex-direction: column !important;
448
- }
449
- .gr-group {
450
- margin: 0.5rem 0 !important;
451
- }
452
- .container {
453
- padding: 0.5rem;
454
- }
455
- }
456
- </style>
457
- """
458
-
459
- with gr.Blocks(title="Open Repo AI", theme=gr.themes.Soft()) as app:
460
- # Apply CSS
461
- gr.Markdown(CSS_STYLES)
462
-
463
- # Header Section
464
- with gr.Row(elem_classes="header") as header:
465
- gr.Markdown(f"""
466
- # 🤖 Open Repo AI Assistant
467
-
468
- 📅 Current Date and Time (UTC): {current_time}
469
- """)
470
-
471
- # Main Tabs
472
- with gr.Tabs() as tabs:
473
- # Configuration Tab
474
- with gr.Tab("🛠️ Configuration", elem_id="config-tab"):
475
- with gr.Group():
476
- provider = gr.Radio(
477
- choices=[AIProvider.XAI, AIProvider.GEMINI, AIProvider.OLLAMA],
478
- label="AI Provider",
479
- value=AIProvider.XAI,
480
- elem_classes="provider-select"
481
- )
482
-
483
- # API Settings
484
- with gr.Group(elem_classes="api-settings") as api_settings:
485
- with gr.Row():
486
- with gr.Column(scale=3):
487
- xai_key = gr.Textbox(
488
- label="X.AI (Grok) API Key",
489
- type="password",
490
- placeholder="Optional - Click (?) for info",
491
- elem_classes="api-key-input"
492
- )
493
- gemini_key = gr.Textbox(
494
- label="Gemini API Key",
495
- type="password",
496
- placeholder="Optional - Leave blank for default key",
497
- elem_classes="api-key-input"
498
- )
499
- with gr.Column(scale=1):
500
- gr.Markdown(XAI_API_HELP)
501
- gr.Markdown(GEMINI_API_HELP)
502
-
503
- # Model Selection
504
- model_dropdown = gr.Dropdown(
505
- label="AI Model",
506
- choices=XAI_MODELS,
507
- value="grok-2-latest",
508
- interactive=True,
509
- elem_classes="model-select"
510
- )
511
-
512
- # Repository Analysis Tab
513
- with gr.Tab("📊 Repository Analysis", elem_id="repo-tab"):
514
- with gr.Group(elem_classes="repo-config"):
515
- # Repository Settings
516
- with gr.Row():
517
- repo_url = gr.Textbox(
518
- label="GitHub Repository URL",
519
- placeholder="https://github.com/username/repository",
520
- elem_classes="repo-input"
521
- )
522
-
523
- with gr.Row():
524
- with gr.Column(scale=2):
525
- github_token = gr.Textbox(
526
- label="GitHub Token",
527
- type="password",
528
- placeholder="Click (?) for guide",
529
- elem_classes="token-input"
530
- )
531
- gr.Markdown(GITHUB_TOKEN_HELP)
532
- with gr.Column(scale=1):
533
- branch = gr.Textbox(
534
- label="Branch (optional)",
535
- placeholder="main",
536
- elem_classes="branch-input"
537
- )
538
-
539
- # Clone Section
540
- with gr.Row():
541
- clone_button = gr.Button(
542
- "🔄 Clone Repository",
543
- variant="primary",
544
- elem_classes="clone-btn"
545
- )
546
- clone_status = gr.Markdown(
547
- value="",
548
- label="Repository Status",
549
- elem_classes="clone-status"
550
- )
551
-
552
- # File Selection
553
- with gr.Group(elem_classes="file-selection"):
554
- gr.Markdown("### 📎 Selected Files")
555
- file_selector = gr.Dropdown(
556
- label="Select Files from Repository",
557
- choices=[],
558
- multiselect=True,
559
- value=[],
560
- allow_custom_value=True,
561
- max_choices=None,
562
- elem_classes="file-dropdown"
563
- )
564
- file_list = gr.HTML(
565
- value="<div class='file-list'>No files selected</div>",
566
- label="Selected Files List"
567
- )
568
-
569
- # Chat Interface
570
- with gr.Group(elem_classes="chat-container") as chat_group:
571
- chat_history = gr.Chatbot(
572
- label="💬 Chat History",
573
- height=500,
574
- show_label=True,
575
- elem_classes="chat-history"
576
- )
577
-
578
- with gr.Row():
579
- chat_input = gr.Textbox(
580
- label="Ask about Repository",
581
- placeholder="Type your question here...",
582
- lines=3,
583
- elem_classes="chat-input"
584
- )
585
-
586
- with gr.Row():
587
- send_button = gr.Button(
588
- "📤 Send",
589
- variant="primary",
590
- elem_classes="send-btn"
591
- )
592
- clear_button = gr.Button(
593
- "🧹 Clear",
594
- variant="secondary",
595
- elem_classes="clear-btn"
596
- )
597
-
598
- # Event Handlers
599
- def update_model_list(provider_choice: str) -> dict:
600
- """Update model dropdown based on selected provider."""
601
- models_map = {
602
- AIProvider.XAI: (XAI_MODELS, "grok-2-latest"),
603
- AIProvider.GEMINI: (GEMINI_MODELS, "gemini-1.5-mini"),
604
- AIProvider.OLLAMA: (OLLAMA_MODELS, "llama2")
605
- }
606
- choices, default = models_map.get(provider_choice, (XAI_MODELS, "grok-2-latest"))
607
- return gr.Dropdown(choices=choices, value=default)
608
-
609
- def handle_clone(repo_url: str, github_token: str, branch: str) -> tuple:
610
- """Handle repository cloning and update UI components."""
611
- if not repo_url:
612
- return (
613
- "⚠️ Repository URL is required!",
614
- gr.Dropdown(choices=[]),
615
- "<div class='file-list'>No files selected</div>"
616
- )
617
-
618
- success, message = analyzer.clone_repository(repo_url, github_token, branch)
619
-
620
- if success:
621
- files = sorted(list(analyzer.repo_content.keys()))
622
- return (
623
- message,
624
- gr.Dropdown(choices=files, value=[]),
625
- "<div class='file-list'>No files selected</div>"
626
- )
627
-
628
- return (
629
- message,
630
- gr.Dropdown(choices=[]),
631
- "<div class='file-list'>No files selected</div>"
632
- )
633
-
634
- def update_file_list(selected: List[str]) -> str:
635
- """Update the file list display."""
636
- if not selected:
637
- return "<div class='file-list'>No files selected</div>"
638
-
639
- file_items = "".join([
640
- f"<div class='file-item'><span>{file}</span></div>"
641
- for file in selected
642
- ])
643
- return f"<div class='file-list'>{file_items}</div>"
644
-
645
- def clear_chat_history() -> list:
646
- """Clear the chat history."""
647
- return []
648
-
649
- # Connect Events
650
- provider.change(
651
- fn=update_model_list,
652
- inputs=[provider],
653
- outputs=[model_dropdown]
654
- )
655
-
656
- clone_button.click(
657
- fn=handle_clone,
658
- inputs=[repo_url, github_token, branch],
659
- outputs=[clone_status, file_selector, file_list]
660
- )
661
-
662
- file_selector.change(
663
- fn=update_file_list,
664
- inputs=[file_selector],
665
- outputs=[file_list]
666
- )
667
-
668
- clear_button.click(
669
- fn=clear_chat_history,
670
- outputs=[chat_history]
671
- )
672
-
673
- # Chat Events
674
- chat_handlers = [
675
- send_button.click(
676
- fn=handle_chat,
677
- inputs=[
678
- chat_input,
679
- chat_history,
680
- provider,
681
- model_dropdown,
682
- xai_key,
683
- gemini_key,
684
- file_selector
685
- ],
686
- outputs=chat_history,
687
- show_progress=True
688
- ),
689
- chat_input.submit(
690
- fn=handle_chat,
691
- inputs=[
692
- chat_input,
693
- chat_history,
694
- provider,
695
- model_dropdown,
696
- xai_key,
697
- gemini_key,
698
- file_selector
699
- ],
700
- outputs=chat_history,
701
- show_progress=True
702
- )
703
- ]
704
-
705
- # Clear input after sending
706
- for handler in chat_handlers:
707
- handler.then(fn=lambda: "", outputs=chat_input)
708
-
709
- return app
710
-
711
- if __name__ == "__main__":
712
- print("""
713
- 🚀 Starting Repository Chat Analysis
714
- ====================================
715
- """)
716
-
717
- app = create_ui()
718
- app.launch(
719
- share=True,
720
- server_name="0.0.0.0",
721
- server_port=7860,
722
- debug=True
723
- )