File size: 21,169 Bytes
d628748
 
 
 
 
 
 
 
dc56f4d
d628748
b9a39f9
 
153e3d6
 
d628748
 
 
 
 
 
dc56f4d
d628748
 
 
 
 
 
 
 
dc56f4d
d628748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dc56f4d
 
 
d628748
 
dc56f4d
 
 
 
 
 
 
 
 
 
 
 
 
 
a50f7ce
dc56f4d
 
 
 
 
 
 
 
a50f7ce
dc56f4d
 
 
 
a50f7ce
 
 
 
 
dc56f4d
 
 
 
 
 
 
a50f7ce
d628748
dc56f4d
 
 
 
 
 
 
 
 
 
 
 
d628748
 
a50f7ce
d628748
 
 
 
 
 
 
 
 
 
dc56f4d
d628748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dc56f4d
d628748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a50f7ce
d628748
 
 
 
 
 
 
 
 
a50f7ce
d628748
 
 
 
 
 
 
 
 
 
 
 
 
a50f7ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d628748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a50f7ce
d628748
a50f7ce
d628748
 
 
 
 
 
 
 
 
dc56f4d
 
 
d628748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a50f7ce
 
 
d628748
 
 
 
 
dc56f4d
 
 
 
 
 
 
d628748
 
 
 
 
 
 
 
 
 
 
 
dc56f4d
 
 
 
 
 
d628748
dc56f4d
d628748
 
 
 
 
 
 
 
 
 
dc56f4d
d628748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a50f7ce
 
d628748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
import os   ## replace with Path
from pathlib import Path
import numpy as np  ##SMY

import gradio as gr
#from watchfiles import run_process  ##gradio reload watch
from app_gradio_lightrag import LightRAGApp  ##SMY lightrag logging
from utils.llm_login import get_login_token
from utils.file_utils import accumulate_dir

import asyncio
import nest_asyncio
nest_asyncio.apply  #

import logging, logging.config

from dotenv import load_dotenv
# Load environment variables
load_dotenv()

'''
# Pythonic error handling decorator
def handle_errors(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            return gr.update(value=f"Error: {e}")
    return wrapper
'''

# Instantiate app logic
#app_logic = LightRAGApp()  ## See main()

# Gradio UI
def gradio_ui(app_logic: LightRAGApp):
    
    css_icon = """
    /* Make reveal button looks like an icon rather than a standard button */
    .password-box {
        position: relative;
        display: flex;
        align-items: center;
        min-width: 30;
    }
    .password-box > :first-child {
        flex-grow: 1;
    }
    .icon-button {
        position: absolute;
        right: 10px;
        top: 70%;
        transform: translateY(-50%);
        border: none;
        background: none;
        color: #4B4B4B;
        font-size: 1.2em;
        padding: 0;
        min-width: 0;
        box-shadow: none;
        cursor: pointer;
        z-index: 100;       /* on top */
    }
    """
              
    with gr.Blocks(theme=gr.themes.Soft(), title="SemmyKG - LightRAG Knowledge Graph App", css=css_icon) as gradio_ui: #demo:
        gr.Markdown("""
        # SemmyKG: LightRAG-based Knowledge Graph RAG
        Upload your markdown docs, index and build a knowledge graph, and query with OpenAI or Ollama. Visualise the KG interactively.
        """)

        # Step 0: Section 1

        # Define ext type (in lieu of getting from global var)
        #ext = (".md", "md")        #SMY disused: 'tuple' object has no attribute '_id'
        # Define openai_api textbox initial value
        openai_api_key_init = os.getenv("OPENAI_API_KEY", "jan-ai")

        with gr.Sidebar(position="right"):
            system_prompt_tb = gr.Textbox(
                value="You are a helpful assistant. You answer questions based on the provided context.",    # If you don't know the answer, just say so. Don't make up information.", 
                label="System Prompt", 
                lines=3, 
                interactive=True, 
                show_copy_button=True,
            )
            
            with gr.Accordion(label="πŸ›ž LLM settings", open=False):
                with gr.Row():    
                    llm_backend_cb = gr.Radio(["OpenAI", "Ollama", "GenAI"], value="OpenAI", label="LLM Backend: OpenAI, Local or GenAI")
                    llm_model_name_tb = gr.Textbox(value=os.getenv("LLM_MODEL", "openai/gpt-oss-120b"), label="LLM Model Name", show_copy_button=True)  #.split('/')[1], label="LLM Model Name") "meta-llama/Llama-4-Maverick-17B-128E-Instruct")),  #image-Text-to-Text  #"openai/gpt-oss-120b",
                #with gr.Row():
                    with gr.Row():  #elem_classes="password-box"):
                        #openai_key_tb = gr.Textbox(value=os.getenv("OPENAI_API_KEY", "jan-ai"), label="OpenAI API Key", 
                        #                           type="password", elem_classes="password-box", container=False, interactive=True, info="OpenAI API Key") #, show_copy_button=True)
                        openai_key_tb = gr.Textbox(value=openai_api_key_init, label="OpenAI API Key", 
                                                type="password", elem_classes="password-box", container=False, interactive=True, info="OpenAI API Key") #, show_copy_button=True)
                        toggle_btn_openai_key = gr.Button(
                                    value="πŸ‘οΈ",  # Initial eye icon
                                    elem_classes="icon-button", size="sm")  #, min_width=50)
                with gr.Row():
                    openai_baseurl_tb = gr.Textbox(value=os.getenv("OPENAI_API_BASE", "https://router.huggingface.co/v1"), label="OpenAI baseurl", show_copy_button=True)
                    ollama_host_tb = gr.Textbox(value=os.getenv("OLLAMA_HOST", "http://localhost:1234/v1"), label="Ollama Host", show_copy_button=True)
                    #ollama_host_tb = gr.Textbox(value=os.getenv("OPENAI_API_EMBED_BASE", ""), label="Ollama Host")
                with gr.Row():
                    embed_backend_dd = gr.Dropdown(choices=["Transformer", "Provider"], value="Transformer", label="Embedding Type")
                    openai_baseurl_embed_tb = gr.Textbox(placeholder=os.getenv("OPENAI_API_EMBED_BASE", "http://localhost:1234/v1"), label="LLM Embed baseurl", show_copy_button=True)
                    
                with gr.Row():
                    llm_model_embed_tb = gr.Textbox(placeholder=os.getenv("LLM_MODEL_EMBED","text-embedding-bge-m3"), label="LLM Embedding Model", show_copy_button=True) #.split('/')[1], label="Embedding Model")
                    with gr.Row():  #elem_classes="password-box"):
                        openai_key_embed_tb = gr.Textbox(value=os.getenv("OPENAI_API_KEY_EMBED", "jan-ai"), label="LLM API Key Embed",   #lm-studio
                                                type="password", elem_classes="password-box", container=False, interactive=True, info="LLM API Key Embed") #, show_copy_button=True)
                        toggle_btn_openai_key_embed = gr.Button(
                                    value="πŸ‘οΈ",  # Initial eye icon
                                    elem_classes="icon-button", size="sm")  #, min_width=50)
                    #openai_key_embed_tb = gr.Textbox(value=os.getenv("OPENAI_API_KEY_EMBED", "jan-ai"), label="OpenAI API Key Embed", type="password", show_copy_button=True)  #("OLLAMA_API_KEY", ""), label="OpenAI API Key Embed", type="password")
                
        # Step 1: Section 2
        with gr.Row():
                with gr.Column():
                    #data_folder_tb = gr.Textbox(value="dataset/data/docs2", label="Data Folder (markdown only)", show_copy_button=True)
                    dir_btn = gr.UploadButton(
                        #value='dataset/data/',      #docs2     #[Errno 13] Permission denied
                        label="πŸ“ Upload Folder",
                        #file_types=ext,           #["file"],
                        file_count="directory",
                    )
                    upload_count_md = gr.Markdown(visible=False)
                working_dir_tb = gr.Textbox(value="./working_folder1", label="lightRAG working folder", show_copy_button=True)
                working_dir_reset_cb = gr.Checkbox(value=False, label="Reset working files?")
        with gr.Accordion("πŸ€— HuggingFace Client Control", open=True):  #, open=False):
            # HuggingFace controls
            hf_login_logout_btn = gr.LoginButton( variant="huggingface", value="Sign in to HuggingFace πŸ€—", logout_value="Logout of HF: ({}) πŸ€— \n    [NB: check LLM settings & reload files (upload folder)]",)

        gr.Markdown("---")   #gr.HTML("<hr>")
        
        setup_btn = gr.Button("Initialise App", variant="primary")
        status_box = gr.Textbox(label="Status / Progress", interactive=True)  #interactive=False)
        
        # Step 2: Section 3
        gr.HTML("<hr>")   #gr.Markdown("---")
        
        with gr.Row():
            index_btn = gr.Button("Index Documents", interactive=False)
            stop_btn = gr.Button("Stop", variant="stop")  ## Add cancel event button
            query_text_tb = gr.Textbox(label="Your Query")
            mode_dd = gr.Dropdown(["naive", "local", "global", "hybrid", "mix"], value="hybrid", label="Query Mode")
            query_btn = gr.Button("Query")
        answer_box_md = gr.Markdown(label="Answer")
        
        # Step 3: Section 4
        kg_btn = gr.Button("Visualise Knowledge Graph")
        kg_html = gr.HTML(label="Knowledge Graph Visualisation")
        
        # Add progress tracking
        progress_tb = gr.Textbox(label="Progress", interactive=False)
        
        
        ##### Processing #####
        ## Note: 1.4.9 query `references` field, `user_prompt`  | lightRAG 1.4.0: QueryParam updated. Remove dependency on graspologic 

        # Initialise gr.State  ##gr.State component initial value must be able to be deepcopied
        st_openai_key = gr.State(value=openai_api_key_init)    #gr.State("")
        st_password1 = gr.State(value="password")
        st_password2 = gr.State(value="password")
        state_uploaded_file_list = gr.State(value=[])


        ### Change handling
        # Change Handling: update state value
        def update_state_stored_value(new_component_input):
            """ Updates stored state for Gradio Component
            for instance: st_openai_key.value = openai_key_tb
            Args:
                new_component_input: New value from component
            Returns:
                Updated value for state
            """ 
            return new_component_input
        
        # Change Handling: update Ollama
        def update_ollama(llm_backend):
            """ Update LLM settings fields with ollama values"""
            # Get model name excluding the model provider: # llm_model_name.rpartition("/")[-1]
            
            if llm_backend == "Ollama":
                return {
                #llm_backend_cb: gr.update(value="Ollama"),
                llm_model_name_tb: gr.update(value=os.getenv("LLM_MODEL", "meta-llama/Llama-4-Maverick-17B-128E-Instruct").rpartition("/")[-1]),  #image-Text-to-Text  #"openai/gpt-oss-120b",  ##Text-to-Text)    #(value="llama2"),
                openai_key_tb: gr.update(value=os.getenv("OPENAI_API_KEY", "jan-ai"), info="LLM API Key"),
                openai_baseurl_tb: gr.update(value=os.getenv("OPENAI_API_BASE", "https://router.huggingface.co/v1")),
                ollama_host_tb: gr.update(value=os.getenv("OLLAMA_HOST", "http://localhost:1234/v1")), #"http://localhost:11434"
                openai_baseurl_embed_tb: gr.update(value=os.getenv("OPENAI_API_EMBED_BASE", "http://localhost:1234/v1")),   #"http://localhost:1234/v1/embeddings"
                llm_model_embed_tb: gr.update(value=os.getenv("LLM_MODEL_EMBED","nomic-embed-text")),   #"nomic-ai/nomic-embed-text-v1.5"
                openai_key_embed_tb: gr.update(value=os.getenv("OPENAI_API_KEY_EMBED", "jan-ai"))
                }
            elif llm_backend == "GenAI":
                return {
                llm_model_name_tb: gr.update(value=os.getenv("LLM_MODEL", "google/gemini-2.5-flash-preview-09-2025").rpartition("/")[-1]),  #image-Text-to-Text #"google/gemini-2.0-flash-exp:free" #"openai/gpt-oss-120b",  ##Text-to-Text)    #(value="llama2"),
                openai_key_tb: gr.update(value=os.getenv("GEMINI_API_KEY", "jan-ai"), info="GenAI API Key"),
                openai_baseurl_tb: gr.update(value=os.getenv("GEMINI_API_BASE", "https://generativelanguage.googleapis.com/v1beta/openai/"), label="GenAI baaseurl"),
                ollama_host_tb: gr.update(value=os.getenv("OLLAMA_HOST", "http://localhost:11434")), #"http://localhost:1234/v1"
                openai_baseurl_embed_tb: gr.update(value=os.getenv("OPENAI_API_EMBED_BASE", "http://localhost:1234/v1")),   #"http://localhost:1234/v1/embeddings"
                llm_model_embed_tb: gr.update(value=os.getenv("LLM_MODEL_EMBED", "nomic-ai/nomic-embed-text-v1.5")), #"all-MiniLM-L6-v2")),
                openai_key_embed_tb: gr.update(value=os.getenv("OPENAI_API_KEY_EMBED", "jan-ai"))
                }
            elif llm_backend == "OpenAI":
                return {
                    llm_model_name_tb: gr.update(value=os.getenv("LLM_MODEL", "openai/gpt-oss-120b")),  #image-Text-to-Text  #"openai/gpt-oss-120b",  ##Text-to-Text)    #(value="llama2"),
                    openai_key_tb: gr.update(value=os.getenv("OPENAI_API_KEY", ""), info="OpenAI API Key"),
                    openai_baseurl_tb: gr.update(value=os.getenv("OPENAI_API_BASE", "https://router.huggingface.co/v1"), label="OpenAI baseurl"),
                    ollama_host_tb: gr.update(value=os.getenv("OLLAMA_HOST", "http://localhost:11434")), #"http://localhost:1234/v1"
                    openai_baseurl_embed_tb: gr.update(value=os.getenv("OPENAI_API_EMBED_BASE", "https://api.openai.com/v1")),   #"http://localhost:1234/v1/embeddings"
                    llm_model_embed_tb: gr.update(value=os.getenv("LLM_MODEL_EMBED","text-embedding-3-small")),
                    openai_key_embed_tb: gr.update(value=os.getenv("OPENAI_API_KEY_EMBED", ""))
                }
        
        # Change Handling: update Ollama
        def update_embedding_backend(embedding_backend):
            """ Update LLM settings fields with ollama values"""
            # Get model name excluding the model provider: # llm_model_name.rpartition("/")[-1]
            
            if embedding_backend == "Provider":
                return {
                openai_baseurl_embed_tb: gr.update(value=os.getenv("OPENAI_API_EMBED_BASE", "http://localhost:1234/v1")),   #"http://localhost:1234/v1/embeddings"
                llm_model_embed_tb: gr.update(value=os.getenv("LLM_MODEL_EMBED","nomic-embed-text")),
                openai_key_embed_tb: gr.update(value=os.getenv("OPENAI_API_KEY_EMBED", "jan-ai"))
                }
            elif embedding_backend == "Transformer":
                return { 
                openai_baseurl_embed_tb: gr.update(value=None, placeholder=os.getenv("OPENAI_API_EMBED_BASE", "http://localhost:1234/v1")),   #"http://localhost:1234/v1/embeddings"
                llm_model_embed_tb: gr.update(value=None, placeholder=os.getenv("LLM_MODEL_EMBED", "nomic-ai/nomic-embed-text-v1.5")),   #(value="all-MiniLM-L6-v2"),
                openai_key_embed_tb: gr.update(value=None, placeholder="jan-ai")
                }
        
        # Change Handling: Update password reveal state - reusable function for toggling password visibility
        def toggle_password(current_state):
            """ Change state
            Change password input field between visible/hidden
            Args:
                current_state: Current password visibility state
            Returns:
                Tuple of updates for textbox, button and state
            """ 
            
            new_state = "text" if current_state == "password" else "password"
            new_icon = "πŸ‘οΈ" if new_state == "password" else "πŸ‘οΈβ€πŸ—¨οΈ" # Change icon with state
            return [            #(
                gr.update(type=new_state),  #gr.Textbox.update(type=new_state),
                gr.update(value=new_icon),  #gr.Button.update(value=new_icon),
                new_state ]     #)
        
        # Update gr.State values on HF login change.
        def custom_do_logout(openai_key, oauth_token: gr.OAuthToken | None=None,):
            #'''  ##SMY: TO DELETE
            st_openai_key_get = os.getenv("OPENAI_API_KEY", default="") #""  ##SMY:  # UnboundLocalError: not catching
            try:
                if oauth_token or oauth_token is not None:  ##SMY: hack: is not None!
                    st_openai_key_get= update_state_stored_value(oauth_token.token)   ##SMY: currently not used optimally
            except AttributeError:
                st_openai_key_get= get_login_token(openai_key)  #(openai_key_tb)
            #'''            
            #return gr.update(value="Sign in to HuggingFace πŸ€—")
            return gr.update(value="Sign in to HuggingFace πŸ€—"), gr.update(value=st_openai_key_get)     #, gr.update(visible=True, value=msg)  #, state_api_token_arg

        
        # Button logic with async handling
        async def setup_wrapper(df, wd, wd_reset, llm_back, embed_back, oai, base, base_embed, model, model_embed, host, embedkey, sys_prompt):
            return await app_logic.setup(df, wd, wd_reset, llm_back, embed_back, oai, 
                                         base, base_embed, model, model_embed, host, embedkey, sys_prompt)
            
        async def index_wrapper(df):
            return await app_logic.index_documents(df)
            
        async def query_wrapper(q, m):
            return await app_logic.query(q, m)
        
        def stop_wrapper():  ##SMY sync or async
            """Cancel event wrapper"""
            app_logic.trigger_cancel()
            return "Cancellation requested. Awaiting current step to finish..."
        
        ### Change handlers
        llm_backend_cb.change(show_progress="hidden", fn=update_ollama, inputs=llm_backend_cb,  #inputs=None, 
                              outputs=[llm_model_name_tb, openai_key_tb, openai_baseurl_tb, ollama_host_tb, openai_baseurl_embed_tb, llm_model_embed_tb, openai_key_embed_tb])

        embed_backend_dd.change(show_progress="hidden", fn=update_embedding_backend, inputs=embed_backend_dd, 
                                outputs=[openai_baseurl_embed_tb,llm_model_embed_tb, openai_key_embed_tb],)
        
        ### Button handlers

        #hf_login_logout_btn.click(update_state_stored_value, inputs=openai_key_tb, outputs=st_openai_key)
        hf_login_logout_btn.click(fn=custom_do_logout, inputs=openai_key_tb, outputs=[hf_login_logout_btn, st_openai_key])
        
        dir_btn.upload(
            fn=accumulate_dir,
            inputs=[dir_btn, state_uploaded_file_list],
            outputs=[state_uploaded_file_list, index_btn, upload_count_md, status_box],
            show_progress="hidden"
            )
        
        toggle_btn_openai_key.click(
            fn=toggle_password,
            inputs=[st_password1],
            outputs=[openai_key_tb, toggle_btn_openai_key, st_password1],
            show_progress="hidden"
            )
        toggle_btn_openai_key_embed.click(
            fn=toggle_password,
            inputs=[st_password2],
            outputs=[openai_key_embed_tb, toggle_btn_openai_key_embed, st_password2],
            show_progress="hidden"
            )
        '''
        async def setup(self, data_folder: str, working_dir: str, wdir_reset: bool, llm_backend: str, embed_backend: str, openai_key: str, 
             openai_baseurl: str, openai_baseurl_embed: str, llm_model_name: str, llm_model_embed: str, 
             ollama_host: str, embed_key: str, system_prompt: str) -> str:
        '''
        inputs_arg = [state_uploaded_file_list, working_dir_tb, working_dir_reset_cb, llm_backend_cb, embed_backend_dd, st_openai_key, #openai_key_tb, 
                      openai_baseurl_tb, openai_baseurl_embed_tb, llm_model_name_tb, llm_model_embed_tb, 
                      ollama_host_tb, openai_key_embed_tb, system_prompt_tb]      #data_folder_tb, 
        
        setup_btn.click(
            fn=setup_wrapper,
            #inputs=[data_folder_tb, working_dir_tb, llm_backend_cb, openai_key_tb, openai_baseurl_tb, openai_baseurl_embed_tb, llm_model_name_tb, llm_model_embed_tb, ollama_host_tb, openai_key_embed_tb],
            inputs=inputs_arg,
            outputs=status_box,
            show_progress=True
            )
        index_btn.click(
            fn=index_wrapper,
            inputs=state_uploaded_file_list,     #[data_folder_tb],
            outputs=[status_box, progress_tb],
            show_progress=True
            )
        query_btn.click(
            fn=query_wrapper,
            inputs=[query_text_tb, mode_dd],
            outputs=answer_box_md
            )
        kg_btn.click(
            fn=app_logic.show_kg,
            inputs=None,
            outputs=kg_html,
            show_progress=True
            )
        stop_btn.click(
            fn=stop_wrapper,
            inputs=[],
            outputs=[status_box]
            )
        
    return gradio_ui

if __name__ == "__main__":
    #gradio_ui().launch() 
    
    ##SMY: assist: https://www.gradio.app/guides/developing-faster-with-reload-mode
    ##SMY: NB: gradio app_gradio_lightrag.py --demo-name=gradio_ui
    async def main():
        from app_gradio_lightrag import LightRAGApp
        # Instantiate LightRAG and launch Gradio
        try:
            app_logic = LightRAGApp()
            # Launch Gradio with queue enable to enable >60s timeout
            gradio_ui(app_logic).queue().launch()    #(server_port=7866)
        except Exception as e:
            print(f"An error occurred: {e}")
        finally:
            if app_logic.rag:
                await app_logic.rag.finalize_storages()
 
    from utils.logger import get_logger, setup_logging
    setup_logging()         ## set logging
    logger_kg = get_logger("semmyKG")   ## app logging

    ##SMY Initialise logging before running the main function: See lightrag_openai_compatible_demo.py
    from app_gradio_lightrag import handle_errors, configure_logging
    configure_logging()     ## lightRAG logging
    
    asyncio.run(main())

    ##SMY: gradio reload-mode watch: https://github.com/huggingface/smolagents/issues/789
    #run_process(".", target=gradio_ui)