CosmickVisions commited on
Commit
7e70b0d
·
verified ·
1 Parent(s): dbabe5b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +205 -299
app.py CHANGED
@@ -5,7 +5,7 @@ import tempfile
5
  import uuid
6
  from dotenv import load_dotenv
7
  from langchain_community.vectorstores import FAISS
8
- from langchain_community.embeddings import SentenceTransformerEmbeddings
9
  from langchain.text_splitter import RecursiveCharacterTextSplitter
10
  import fitz # PyMuPDF
11
  import base64
@@ -25,13 +25,21 @@ client = groq.Client(api_key=os.getenv("GROQ_TECH_API_KEY"))
25
 
26
  # Initialize embeddings with error handling
27
  try:
28
- embeddings = SentenceTransformerEmbeddings(
29
- model_name="all-MiniLM-L6-v2",
30
- model_kwargs={"device": "cpu"}
 
31
  )
32
  except Exception as e:
33
- print(f"Error loading embeddings: {e}")
34
- embeddings = None
 
 
 
 
 
 
 
35
 
36
  # Directory to store FAISS indexes with better naming
37
  FAISS_INDEX_DIR = "faiss_indexes_tech_cpu"
@@ -41,246 +49,69 @@ if not os.path.exists(FAISS_INDEX_DIR):
41
  # Dictionary to store user-specific vectorstores
42
  user_vectorstores = {}
43
 
44
- # Custom CSS to match HTML exactly
45
  custom_css = """
46
  :root {
47
  --primary-color: #4285F4;
48
  --secondary-color: #34A853;
49
  --accent-color: #EA4335;
50
- --yellow-color: #FBBC05;
51
  --light-background: #F8F9FA;
52
  --dark-text: #202124;
53
  --white: #FFFFFF;
54
  --border-color: #DADCE0;
55
  --code-bg: #F1F3F4;
56
- --shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
57
- --shadow-md: 0 4px 6px rgba(0,0,0,0.1);
58
- --shadow-lg: 0 10px 20px rgba(0,0,0,0.1);
59
- --transition: all 0.3s cubic-bezier(.25,.8,.25,1);
60
- }
61
-
62
- /* Global Styles */
63
- body {
64
- background-color: var(--light-background);
65
- font-family: 'Roboto', sans-serif;
66
- color: var(--dark-text);
67
- line-height: 1.6;
68
- }
69
-
70
- /* Header */
71
- .header {
72
- background-color: var(--white);
73
- box-shadow: var(--shadow-sm);
74
- position: sticky;
75
- top: 0;
76
- z-index: 100;
77
- padding: 16px 0;
78
- }
79
-
80
- .logo {
81
- display: flex;
82
- align-items: center;
83
- gap: 12px;
84
- }
85
-
86
- .logo-icon {
87
- color: var(--primary-color);
88
- font-size: 24px;
89
  }
90
-
91
- /* Main Layout */
92
- .container {
93
- max-width: 1400px;
94
- margin: 0 auto;
95
- padding: 40px 20px;
96
  }
97
-
98
- .main-content {
99
- display: grid;
100
- grid-template-columns: 320px 1fr;
101
- gap: 30px;
102
  }
103
-
104
- /* Sidebar */
105
- .sidebar {
106
  background-color: var(--white);
107
- border-radius: 12px;
108
- box-shadow: var(--shadow-md);
109
- overflow: hidden;
110
- }
111
-
112
- .sidebar-section {
113
- padding: 20px;
114
  border-bottom: 1px solid var(--border-color);
115
- }
116
-
117
- /* File Upload */
118
- .file-upload {
119
- border: 2px dashed var(--border-color);
120
- border-radius: 8px;
121
- padding: 20px;
122
- text-align: center;
123
- cursor: pointer;
124
- transition: var(--transition);
125
- margin-bottom: 16px;
126
- }
127
-
128
- /* Tabs */
129
- .tabs {
130
- display: flex;
131
- background-color: var(--white);
132
  border-radius: 12px 12px 0 0;
133
- overflow: hidden;
134
- box-shadow: var(--shadow-sm);
135
  }
136
-
137
- .tab {
138
- padding: 16px 24px;
139
- font-family: 'Google Sans', sans-serif;
140
- font-weight: 500;
141
- cursor: pointer;
142
- transition: var(--transition);
143
- border-bottom: 3px solid transparent;
144
- }
145
-
146
- .tab.active {
147
  color: var(--primary-color);
148
- border-bottom-color: var(--primary-color);
149
- }
150
-
151
- /* Chat Section */
152
- .chat-section {
153
- background-color: var(--white);
154
- border-radius: 12px;
155
- box-shadow: var(--shadow-md);
156
- overflow: hidden;
157
- margin-top: 24px;
158
- }
159
-
160
- .chat-header {
161
- padding: 16px;
162
- background-color: var(--primary-color);
163
- color: var(--white);
164
- display: flex;
165
- justify-content: space-between;
166
- align-items: center;
167
- }
168
-
169
- .chat-messages {
170
- height: 400px;
171
- overflow-y: auto;
172
- padding: 16px;
173
- }
174
-
175
- .message {
176
- max-width: 80%;
177
- padding: 12px 16px;
178
- border-radius: 18px;
179
- margin-bottom: 12px;
180
- }
181
-
182
- .message-user {
183
- background-color: var(--primary-color);
184
- color: var(--white);
185
- margin-left: auto;
186
- border-radius: 18px 18px 4px 18px;
187
  }
188
-
189
- .message-bot {
190
- background-color: var(--light-background);
191
  color: var(--dark-text);
192
- margin-right: auto;
193
- border-radius: 18px 18px 18px 4px;
194
- }
195
-
196
- /* Buttons */
197
- .primary-button {
198
- background-color: var(--primary-color);
199
- color: var(--white);
200
- border: none;
201
- border-radius: 4px;
202
- padding: 10px 16px;
203
- font-family: 'Google Sans', sans-serif;
204
- font-weight: 500;
205
- cursor: pointer;
206
- transition: var(--transition);
207
- }
208
-
209
- .primary-button:hover {
210
- background-color: #3367d6;
211
- }
212
- """
213
-
214
- # Custom JavaScript to enhance UI
215
- custom_js = """
216
- document.addEventListener('DOMContentLoaded', function() {
217
- // Add Font Awesome
218
- const fontAwesome = document.createElement('link');
219
- fontAwesome.rel = 'stylesheet';
220
- fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css';
221
- document.head.appendChild(fontAwesome);
222
-
223
- // Add Google Fonts
224
- const googleFonts = document.createElement('link');
225
- googleFonts.rel = 'stylesheet';
226
- googleFonts.href = 'https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&family=Roboto:wght@300;400;500&family=Roboto+Mono&display=swap';
227
- document.head.appendChild(googleFonts);
228
-
229
- // Initialize UI enhancements
230
- setTimeout(enhanceUI, 1000);
231
- });
232
-
233
- function enhanceUI() {
234
- // Add icons to headers
235
- addIconToHeader('Upload Code', 'fa-upload');
236
- addIconToHeader('Developer Tools', 'fa-tools');
237
- addIconToHeader('Tech Assistant', 'fa-robot');
238
-
239
- // Setup tabs
240
- setupTabs();
241
-
242
- // Setup file upload area
243
- setupFileUpload();
244
  }
245
-
246
- function addIconToHeader(text, iconClass) {
247
- document.querySelectorAll('h3').forEach(header => {
248
- if (header.textContent.includes(text)) {
249
- const icon = document.createElement('i');
250
- icon.className = `fas ${iconClass}`;
251
- header.insertBefore(icon, header.firstChild);
252
- header.style.display = 'flex';
253
- header.style.alignItems = 'center';
254
- header.style.gap = '8px';
255
- }
256
- });
257
  }
258
-
259
- function setupTabs() {
260
- const tabs = document.querySelectorAll('.tab');
261
- tabs.forEach(tab => {
262
- tab.addEventListener('click', () => {
263
- tabs.forEach(t => t.classList.remove('active'));
264
- tab.classList.add('active');
265
- });
266
- });
267
  }
268
-
269
- function setupFileUpload() {
270
- const dropzone = document.querySelector('.file-upload');
271
- if (!dropzone) return;
272
-
273
- dropzone.addEventListener('dragover', (e) => {
274
- e.preventDefault();
275
- dropzone.style.borderColor = 'var(--primary-color)';
276
- dropzone.style.backgroundColor = 'rgba(66, 133, 244, 0.05)';
277
- });
278
-
279
- dropzone.addEventListener('dragleave', (e) => {
280
- e.preventDefault();
281
- dropzone.style.borderColor = 'var(--border-color)';
282
- dropzone.style.backgroundColor = 'transparent';
283
- });
284
  }
285
  """
286
 
@@ -753,106 +584,181 @@ def process_code_file(file_obj):
753
  except Exception as e:
754
  return None, f"Error processing file: {str(e)}", {}
755
 
756
- # Create the Gradio interface
757
- with gr.Blocks(css=custom_css, js=custom_js, theme=gr.themes.Soft()) as demo:
758
- # Header
 
 
759
  gr.HTML("""
760
- <header class="header">
761
- <div class="container">
762
- <div class="logo">
763
- <i class="fas fa-code logo-icon"></i>
764
- <span class="logo-text">Tech-Vision AI</span>
765
- </div>
766
- </div>
767
- </header>
768
  """)
769
 
770
- with gr.Row(elem_classes="container main-content"):
771
- # Sidebar
772
- with gr.Column(scale=1, min_width=320, elem_classes="sidebar"):
773
- with gr.Group(elem_classes="sidebar-section"):
774
- gr.Markdown("### Upload Code")
775
- file_input = gr.File(
776
- label="Drag & drop your code file here",
777
- file_types=[".py", ".js", ".java", ".cpp", ".c", ".cs", ".php", ".rb", ".go", ".ts"],
778
- elem_classes="file-upload"
779
- )
780
- analyze_btn = gr.Button("Analyze Code", elem_classes="primary-button")
781
-
782
- model_dropdown = gr.Dropdown(
783
- choices=["llama3-70b-8192", "mixtral-8x7b-32768", "gemma-7b-it"],
784
- value="llama3-70b-8192",
785
- label="Select Model"
786
- )
787
 
788
- with gr.Group(elem_classes="sidebar-section"):
789
- gr.Markdown("### Developer Tools")
 
790
  with gr.Tabs():
791
  with gr.TabItem("GitHub Search"):
792
- repo_query = gr.Textbox(label="Search Query")
793
  with gr.Row():
794
  language = gr.Dropdown(
795
- choices=["any", "JavaScript", "Python", "Java", "C++"],
796
  value="any",
797
  label="Language"
798
  )
799
  min_stars = gr.Dropdown(
800
- choices=["0", "10", "50", "100", "1000"],
801
  value="0",
802
  label="Min Stars"
803
  )
804
- repo_search_btn = gr.Button("Search", elem_classes="primary-button")
 
 
 
 
 
805
 
806
  with gr.TabItem("Stack Overflow"):
807
- stack_query = gr.Textbox(label="Search Query")
808
- tag = gr.Dropdown(
809
- choices=["any", "python", "javascript", "java"],
810
- value="any",
811
- label="Tag"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  )
813
- stack_search_btn = gr.Button("Search", elem_classes="primary-button")
814
-
815
- # Main Area
816
- with gr.Column(scale=3):
817
- with gr.Tabs(elem_classes="tabs"):
818
  with gr.TabItem("Code Analysis"):
819
- with gr.Row():
820
- with gr.Column():
821
- gr.Markdown("### Code Metrics")
822
- metrics = gr.JSON(label="")
823
- with gr.Column():
824
- gr.Markdown("### Recommendations")
825
- recommendations = gr.Markdown()
826
 
827
  with gr.TabItem("GitHub Results"):
828
- repo_results = gr.Markdown()
829
 
830
- with gr.TabItem("Stack Results"):
831
- stack_results = gr.Markdown()
832
-
833
- # Chat Section
834
- with gr.Group(elem_classes="chat-section"):
835
- with gr.Row(elem_classes="chat-header"):
836
- gr.Markdown("### Tech Assistant")
837
- clear_btn = gr.Button("Clear", elem_classes="chat-control-btn")
838
-
839
- chatbot = gr.Chatbot(
840
- height=400,
841
- elem_classes="chat-messages",
842
- show_copy_button=True,
843
- type="messages"
 
 
 
 
 
844
  )
845
-
846
- with gr.Row():
847
- msg = gr.Textbox(
848
- placeholder="Ask about your code...",
849
- show_label=False,
850
- elem_classes="chat-input"
851
- )
852
- send_btn = gr.Button("Send", elem_classes="primary-button")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
853
 
854
- # Add event handlers (implementation details in previous messages)
855
- # ... (event handlers remain the same)
 
 
 
 
856
 
 
857
  if __name__ == "__main__":
858
  demo.launch()
 
5
  import uuid
6
  from dotenv import load_dotenv
7
  from langchain_community.vectorstores import FAISS
8
+ from langchain_community.embeddings import HuggingFaceInstructEmbeddings
9
  from langchain.text_splitter import RecursiveCharacterTextSplitter
10
  import fitz # PyMuPDF
11
  import base64
 
25
 
26
  # Initialize embeddings with error handling
27
  try:
28
+ # Force CPU usage for embeddings
29
+ embeddings = HuggingFaceInstructEmbeddings(
30
+ model_name="hkunlp/instructor-base",
31
+ model_kwargs={"device": "cpu"} # Force CPU usage
32
  )
33
  except Exception as e:
34
+ print(f"Warning: Failed to load primary embeddings model: {e}")
35
+ try:
36
+ embeddings = HuggingFaceInstructEmbeddings(
37
+ model_name="all-MiniLM-L6-v2",
38
+ model_kwargs={"device": "cpu"} # Force CPU usage
39
+ )
40
+ except Exception as e:
41
+ print(f"Warning: Failed to load fallback embeddings model: {e}")
42
+ embeddings = None
43
 
44
  # Directory to store FAISS indexes with better naming
45
  FAISS_INDEX_DIR = "faiss_indexes_tech_cpu"
 
49
  # Dictionary to store user-specific vectorstores
50
  user_vectorstores = {}
51
 
52
+ # Custom CSS for Tech theme
53
  custom_css = """
54
  :root {
55
  --primary-color: #4285F4;
56
  --secondary-color: #34A853;
57
  --accent-color: #EA4335;
 
58
  --light-background: #F8F9FA;
59
  --dark-text: #202124;
60
  --white: #FFFFFF;
61
  --border-color: #DADCE0;
62
  --code-bg: #F1F3F4;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
+ body {
65
+ background-color: var(--light-background);
66
+ font-family: 'Google Sans', 'Roboto', sans-serif;
 
 
 
67
  }
68
+ .container {
69
+ max-width: 1200px !important;
70
+ margin: 0 auto !important;
71
+ padding: 10px;
 
72
  }
73
+ .header {
 
 
74
  background-color: var(--white);
 
 
 
 
 
 
 
75
  border-bottom: 1px solid var(--border-color);
76
+ padding: 15px 0;
77
+ margin-bottom: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  border-radius: 12px 12px 0 0;
79
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
 
80
  }
81
+ .header-title {
 
 
 
 
 
 
 
 
 
 
82
  color: var(--primary-color);
83
+ font-size: 1.8rem;
84
+ font-weight: 700;
85
+ text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
87
+ .header-subtitle {
 
 
88
  color: var(--dark-text);
89
+ font-size: 1rem;
90
+ text-align: center;
91
+ margin-top: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  }
93
+ .chat-container {
94
+ border-radius: 12px !important;
95
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important;
96
+ background-color: var(--white) !important;
97
+ border: 1px solid var(--border-color) !important;
98
+ min-height: 500px;
 
 
 
 
 
 
99
  }
100
+ .tool-container {
101
+ background-color: var(--white);
102
+ border-radius: 12px;
103
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
104
+ padding: 15px;
105
+ margin-bottom: 20px;
 
 
 
106
  }
107
+ .code-block {
108
+ background-color: var(--code-bg);
109
+ padding: 12px;
110
+ border-radius: 8px;
111
+ font-family: 'Roboto Mono', monospace;
112
+ overflow-x: auto;
113
+ margin: 10px 0;
114
+ border-left: 3px solid var(--primary-color);
 
 
 
 
 
 
 
 
115
  }
116
  """
117
 
 
584
  except Exception as e:
585
  return None, f"Error processing file: {str(e)}", {}
586
 
587
+ # Update the Gradio interface
588
+ with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
589
+ current_session_id = gr.State(None)
590
+ code_state = gr.State({})
591
+
592
  gr.HTML("""
593
+ <div class="header">
594
+ <div class="header-title">Tech-Vision AI</div>
595
+ <div class="header-subtitle">Advanced Code Analysis & Technical Assistant</div>
596
+ </div>
 
 
 
 
597
  """)
598
 
599
+ with gr.Row(elem_classes="container"):
600
+ with gr.Column(scale=1, min_width=300):
601
+ file_input = gr.File(
602
+ label="Upload Code File",
603
+ file_types=[".py", ".js", ".java", ".cpp", ".c", ".cs", ".php", ".rb", ".go", ".ts"],
604
+ type="binary"
605
+ )
606
+ upload_button = gr.Button("Analyze Code", variant="primary")
607
+ file_status = gr.Markdown("No file uploaded yet")
608
+ model_dropdown = gr.Dropdown(
609
+ choices=["llama3-70b-8192", "mixtral-8x7b-32768", "gemma-7b-it"],
610
+ value="llama3-70b-8192",
611
+ label="Select Model"
612
+ )
 
 
 
613
 
614
+ # Developer Tools Section
615
+ gr.Markdown("### Developer Tools", elem_classes="tool-title")
616
+ with gr.Group(elem_classes="tool-container"): # Replace Box with Group
617
  with gr.Tabs():
618
  with gr.TabItem("GitHub Search"):
619
+ repo_query = gr.Textbox(label="Search Query", placeholder="Enter keywords to search for repositories")
620
  with gr.Row():
621
  language = gr.Dropdown(
622
+ choices=["any", "JavaScript", "Python", "Java", "C++", "TypeScript", "Go", "Rust", "PHP", "C#"],
623
  value="any",
624
  label="Language"
625
  )
626
  min_stars = gr.Dropdown(
627
+ choices=["0", "10", "50", "100", "1000", "10000"],
628
  value="0",
629
  label="Min Stars"
630
  )
631
+ sort_by = gr.Dropdown(
632
+ choices=["stars", "forks", "updated"],
633
+ value="stars",
634
+ label="Sort By"
635
+ )
636
+ repo_search_btn = gr.Button("Search Repositories")
637
 
638
  with gr.TabItem("Stack Overflow"):
639
+ stack_query = gr.Textbox(label="Search Query", placeholder="Enter your technical question")
640
+ with gr.Row():
641
+ tag = gr.Dropdown(
642
+ choices=["any", "python", "javascript", "java", "c++", "react", "node.js", "android", "ios", "sql"],
643
+ value="any",
644
+ label="Tag"
645
+ )
646
+ so_sort_by = gr.Dropdown(
647
+ choices=["votes", "newest", "activity"],
648
+ value="votes",
649
+ label="Sort By"
650
+ )
651
+ so_search_btn = gr.Button("Search Stack Overflow")
652
+
653
+ with gr.TabItem("Code Explainer"):
654
+ code_input = gr.Textbox(
655
+ label="Code to Explain",
656
+ placeholder="Paste your code here...",
657
+ lines=10
658
  )
659
+ explain_btn = gr.Button("Explain Code")
660
+
661
+ with gr.Column(scale=2, min_width=600):
662
+ with gr.Tabs():
 
663
  with gr.TabItem("Code Analysis"):
664
+ with gr.Column(elem_classes="code-viewer-container"):
665
+ code_metrics = gr.Markdown("No code analyzed yet", elem_classes="stats-box")
666
+ code_recommendations = gr.Markdown("", elem_classes="recommendations-box")
 
 
 
 
667
 
668
  with gr.TabItem("GitHub Results"):
669
+ repo_results = gr.Markdown("Search for repositories to see results here")
670
 
671
+ with gr.TabItem("Stack Overflow Results"):
672
+ stack_results = gr.Markdown("Search for questions to see results here")
673
+
674
+ with gr.TabItem("Code Explanation"):
675
+ code_explanation = gr.Markdown("Paste your code and click 'Explain Code' to see an explanation here")
676
+
677
+ with gr.Row(elem_classes="container"):
678
+ with gr.Column(scale=2, min_width=600):
679
+ chatbot = gr.Chatbot(
680
+ height=500,
681
+ show_copy_button=True,
682
+ elem_classes="chat-container",
683
+ type="messages"
684
+ )
685
+ with gr.Row():
686
+ msg = gr.Textbox(
687
+ show_label=False,
688
+ placeholder="Ask about your code, type /github to search repos, or /stack to search Stack Overflow...",
689
+ scale=5
690
  )
691
+ send_btn = gr.Button("Send", scale=1)
692
+ clear_btn = gr.Button("Clear Conversation")
693
+
694
+ # Update event handlers
695
+ upload_button.click(
696
+ lambda x: process_code_file(x),
697
+ inputs=[file_input],
698
+ outputs=[current_session_id, file_status, code_state]
699
+ ).then(
700
+ lambda state: (
701
+ f"### Code Analysis Results\n\n"
702
+ f"**Language:** {state.get('language', 'Unknown')}\n"
703
+ f"**Total Lines:** {state.get('total_lines', 0)}\n"
704
+ f"**Code Lines:** {state.get('code_lines', 0)}\n"
705
+ f"**Comment Lines:** {state.get('comments', 0)}\n"
706
+ f"**Functions:** {state.get('functions', 0)}\n"
707
+ f"**Classes:** {state.get('classes', 0)}\n"
708
+ f"**Complexity Score:** {state.get('cyclomatic_complexity', 0)}\n"
709
+ ),
710
+ inputs=[code_state],
711
+ outputs=[code_metrics]
712
+ ).then(
713
+ lambda state: generate_recommendations(state),
714
+ inputs=[code_state],
715
+ outputs=[code_recommendations]
716
+ )
717
+
718
+ msg.submit(
719
+ generate_response,
720
+ inputs=[msg, current_session_id, model_dropdown, chatbot],
721
+ outputs=[chatbot]
722
+ ).then(lambda: "", None, [msg])
723
+
724
+ send_btn.click(
725
+ generate_response,
726
+ inputs=[msg, current_session_id, model_dropdown, chatbot],
727
+ outputs=[chatbot]
728
+ ).then(lambda: "", None, [msg])
729
+
730
+ clear_btn.click(
731
+ lambda: ([], None, "No file uploaded", {}, None),
732
+ None,
733
+ [chatbot, current_session_id, file_status, code_state, code_metrics]
734
+ )
735
+
736
+ # Tech tool handlers
737
+ repo_search_btn.click(
738
+ perform_repo_search,
739
+ inputs=[repo_query, language, sort_by, min_stars],
740
+ outputs=[repo_results]
741
+ )
742
+
743
+ so_search_btn.click(
744
+ perform_stack_search,
745
+ inputs=[stack_query, tag, so_sort_by],
746
+ outputs=[stack_results]
747
+ )
748
+
749
+ explain_btn.click(
750
+ explain_code,
751
+ inputs=[code_input],
752
+ outputs=[code_explanation]
753
+ )
754
 
755
+ # Add footer with attribution
756
+ gr.HTML("""
757
+ <div style="text-align: center; margin-top: 20px; padding: 10px; color: #666; font-size: 0.8rem; border-top: 1px solid #eee;">
758
+ Created by Calvin Allen Crawford
759
+ </div>
760
+ """)
761
 
762
+ # Launch the app
763
  if __name__ == "__main__":
764
  demo.launch()