rrg92 commited on
Commit
9a37ff5
·
verified ·
1 Parent(s): 4f5cccc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -473
app.py CHANGED
@@ -1,473 +1,55 @@
1
- import gradio as gr
2
- import spaces
3
- from sentence_transformers import SentenceTransformer
4
- from sentence_transformers.util import cos_sim
5
- from sentence_transformers.quantization import quantize_embeddings
6
- import pymssql
7
- import os
8
- import pandas as pd
9
- from openai import OpenAI
10
- from pydantic import BaseModel, Field
11
- import json
12
- from sentence_transformers import CrossEncoder
13
- from torch import nn
14
- import time
15
-
16
-
17
- SqlServer = os.environ['SQL_SERVER']
18
- SqlDatabase = os.environ['SQL_DB']
19
- SqlUser = os.environ['SQL_USER']
20
- SqlPass = os.environ['SQL_PASS']
21
-
22
-
23
- OpenaiApiKey = os.environ.get("OPENAI_API_KEY")
24
- OpenaiBaseUrl = os.environ.get("OPENAI_BASE_URL","https://generativelanguage.googleapis.com/v1beta/openai")
25
-
26
-
27
- def sql(query,db=SqlDatabase, login_timeout = 120,onConnectionError = None):
28
-
29
- start_time = time.time()
30
-
31
- while True:
32
- try:
33
- cnxn = pymssql.connect(SqlServer,SqlUser,SqlPass,db, login_timeout = 5)
34
- break;
35
- except Exception as e:
36
- if onConnectionError:
37
- onConnectionError(e)
38
-
39
- if time.time() - start_time > login_timeout:
40
- raise TimeoutError("SQL Connection Timeout");
41
-
42
- time.sleep(1) # Espera 1 segundo antes de tentar novamente
43
-
44
-
45
- cursor = cnxn.cursor()
46
- cursor.execute(query)
47
-
48
- columns = [column[0] for column in cursor.description]
49
- results = [dict(zip(columns, row)) for row in cursor.fetchall()]
50
-
51
- return results;
52
-
53
-
54
-
55
- @spaces.GPU
56
- def embed(text):
57
-
58
- query_embedding = Embedder.encode(text)
59
- return query_embedding.tolist();
60
-
61
-
62
- @spaces.GPU
63
- def rerank(query,documents, **kwargs):
64
- return Reranker.rank(query, documents, **kwargs)
65
-
66
- ClientOpenai = OpenAI(
67
- api_key=OpenaiApiKey
68
- ,base_url=OpenaiBaseUrl
69
- )
70
-
71
- def llm(messages, ResponseFormat = None, **kwargs):
72
-
73
- fn = ClientOpenai.chat.completions.create
74
-
75
- if ResponseFormat:
76
- fn = ClientOpenai.beta.chat.completions.parse
77
-
78
- params = {
79
- 'model':"gemini-2.0-flash"
80
- ,'n':1
81
- ,'messages':messages
82
- ,'response_format':ResponseFormat
83
- }
84
-
85
- params.update(kwargs);
86
-
87
- response = fn(**params)
88
-
89
- if params.get('stream'):
90
- return response
91
-
92
- return response.choices[0];
93
-
94
- def ai(system,user, schema, **kwargs):
95
- msg = [
96
- {'role':"system",'content':system}
97
- ,{'role':"user",'content':user}
98
- ]
99
-
100
- return llm(msg, schema, **kwargs);
101
-
102
-
103
- def search(text, top = 10, onConnectionError = None):
104
-
105
- EnglishText = text
106
-
107
- embeddings = embed(text);
108
-
109
- query = f"""
110
- declare @search vector(1024) = '{embeddings}'
111
-
112
- select top {top}
113
- *
114
- from (
115
- select
116
- RelPath
117
- ,Similaridade = 1-CosDistance
118
- ,ScriptContent = ChunkContent
119
- ,ContentLength = LEN(ChunkContent)
120
- ,CosDistance
121
- from
122
- (
123
- select
124
- *
125
- ,CosDistance = vector_distance('cosine',embeddings,@search)
126
- from
127
- Scripts
128
- ) C
129
- ) v
130
- order by
131
- CosDistance
132
- """
133
-
134
- queryResults = sql(query, onConnectionError = onConnectionError);
135
-
136
-
137
-
138
- return queryResults
139
-
140
-
141
- print("Loading embedding model");
142
- Embedder = SentenceTransformer("mixedbread-ai/mxbai-embed-large-v1")
143
-
144
- print("Loading reranker");
145
- Reranker = CrossEncoder("mixedbread-ai/mxbai-rerank-large-v1", activation_fn=nn.Sigmoid())
146
-
147
- class rfTranslatedText(BaseModel):
148
- text: str = Field(description='Translated text')
149
- lang: str = Field(description='source language')
150
-
151
- class rfGenericText(BaseModel):
152
- text: str = Field(description='The text result')
153
-
154
- def ChatFunc(message, history, LangMode, ChooseLang):
155
-
156
-
157
- # Determinar se o user quer fazer uma nova pesquisa!
158
- IsNewSearch = True;
159
-
160
- messages = []
161
- CurrentTable = None;
162
-
163
- def ChatBotOutput():
164
- return [messages,CurrentTable]
165
-
166
- class BotMessage():
167
- def __init__(self, *args, **kwargs):
168
- self.Message = gr.ChatMessage(*args, **kwargs)
169
- self.LastContent = None
170
- messages.append(self.Message);
171
-
172
- def __call__(self, content, noNewLine = False):
173
- if not content:
174
- return;
175
-
176
- self.Message.content += content;
177
- self.LastContent = None;
178
-
179
- if not noNewLine:
180
- self.Message.content += "\n";
181
-
182
- return ChatBotOutput();
183
-
184
- def update(self,content):
185
-
186
- if not self.LastContent:
187
- self.LastContent = self.Message.content
188
-
189
- self.Message.content = self.LastContent +" "+content+"\n";
190
-
191
- return ChatBotOutput();
192
-
193
- def done(self):
194
- self.Message.metadata['status'] = "done";
195
- return ChatBotOutput();
196
-
197
- def Reply(msg):
198
- m = BotMessage(msg);
199
- return ChatBotOutput();
200
-
201
- m = BotMessage("",metadata={"title":"Searching scripts...","status":"pending"});
202
-
203
-
204
- def OnConnError(err):
205
- print("Sql connection error:", err)
206
-
207
-
208
- try:
209
- # Responder algo sobre o historico!
210
- if IsNewSearch:
211
-
212
- yield m("Enhancing the prompt...")
213
-
214
- LLMResult = ai("""
215
- Translate the user's message to English.
216
- The message is a question related to a SQL Server T-SQL script that the user is searching for.
217
- You must do following actions:
218
- - Identify the language of user text, using BCP 47 code (example: pt-BR, en-US, ja-JP, etc.)
219
- - Generate translated user text to english
220
- Return both source language and translated text.
221
- """,message, rfTranslatedText)
222
- Question = LLMResult.message.parsed.text;
223
-
224
- if LangMode == "auto":
225
- SourceLang = LLMResult.message.parsed.lang;
226
- else:
227
- SourceLang = ChooseLang
228
-
229
- yield m(f"Lang:{SourceLang}({LangMode}), English Prompt: {Question}")
230
-
231
- yield m("searching...")
232
- try:
233
- FoundScripts = search(Question, onConnectionError = OnConnError)
234
- except:
235
- yield m("Houve alguma falha ao executar a consulta no banco. Tente novamente. Se persistir, veja orientações na aba Help!")
236
- return;
237
-
238
- yield m("Doing rerank");
239
- doclist = [doc['ScriptContent'] for doc in FoundScripts]
240
-
241
- # Faz o reranker!
242
- for score in rerank(Question, doclist):
243
- i = score['corpus_id'];
244
- FoundScripts[i]['rank'] = str(score['score'])
245
-
246
- RankedScripts = sorted(FoundScripts, key=lambda item: float(item['rank']), reverse=True)
247
-
248
-
249
-
250
- ScriptTable = []
251
- for script in RankedScripts:
252
- link = "https://github.com/rrg92/sqlserver-lib/tree/main/" + script['RelPath']
253
- script['link'] = link;
254
-
255
- ScriptTable.append({
256
- 'Link': f'<a title="{link}" href="{link}" target="_blank">{script["RelPath"]}</a>'
257
- ,'Length': script['ContentLength']
258
- ,'Cosine Similarity': script['Similaridade']
259
- ,'Rank': script['rank']
260
- })
261
-
262
-
263
- CurrentTable = pd.DataFrame(ScriptTable)
264
- yield m("Found scripts, check Rank tab for details!")
265
-
266
-
267
- WaitMessage = ai(f"""
268
- You will analyze some T-SQL scripts in order to check which is best for the user.
269
- You found scripts, presented them to the user, and now will do some work that takes time.
270
- Generate a message to tell the user to wait while you work, in the same language as the user.
271
- You will receive the question the user sent that triggered this process.
272
- Use the user’s original question to customize the message.
273
- Answer in lang: {SourceLang}
274
- """,message,rfGenericText).message.parsed.text
275
-
276
- yield Reply(WaitMessage);
277
-
278
- yield m(f"Analyzing scripts...")
279
-
280
-
281
- ResultJson = json.dumps(RankedScripts);
282
-
283
- SystemPrompt = f"""
284
- You are an assistant that helps users find the best T-SQL scripts for their specific needs.
285
- These scripts were created by Rodrigo Ribeiro Gomes and are publicly available for users to query and use.
286
-
287
- The user will provide a short description of what they are looking for, and your task is to present the most relevant scripts.
288
-
289
- To assist you, here is a JSON object with the top matches based on the current user query:
290
- {ResultJson}
291
-
292
- ---
293
- This JSON contains all the scripts that matched the user's input.
294
- Analyze each script's name and content, and create a ranked summary of the best recommendations according to the user's need.
295
-
296
- Only use the information available in the provided JSON. Do not reference or mention anything outside of this list.
297
- You can include parts of the scripts in your answer to illustrate or give usage examples based on the user's request.
298
-
299
- Re-rank the results if necessary, presenting them from the most to the least relevant.
300
- You may filter out scripts that appear unrelated to the user query.
301
-
302
- ---
303
- ### Output Rules
304
-
305
- - Review each script and evaluate how well it matches the user’s request.
306
- - Summarize each script, ordering from the most relevant to the least relevant.
307
- - Write personalized and informative review text for each recommendation.
308
- - If applicable, explain how the user should run the script, including parameters or sections (like `WHERE` clauses) they might need to customize.
309
- - When referencing a script, include the link provided in the JSON — all scripts are hosted on GitHub
310
- - YOU MUST ANSWER THAT LANGUAGE: {SourceLang}
311
- """
312
-
313
- ScriptPrompt = [
314
- { 'role':'system', 'content':SystemPrompt }
315
- ,{ 'role':'user', 'content':message }
316
- ]
317
-
318
-
319
-
320
-
321
- llmanswer = llm(ScriptPrompt, stream = True)
322
- yield m.done()
323
-
324
- answer = BotMessage("");
325
-
326
- for chunk in llmanswer:
327
- content = chunk.choices[0].delta.content
328
- yield answer(content, noNewLine = True)
329
- finally:
330
- yield m.done()
331
-
332
- def SearchFiles(message):
333
-
334
- Question = message;
335
-
336
- try:
337
- FoundScripts = search(Question)
338
- except:
339
- return m("Houve alguma falha ao executar a consulta no banco. Tente novamente. Se persistir, veja orientações na aba Help!")
340
- return;
341
-
342
- doclist = [doc['ScriptContent'] for doc in FoundScripts]
343
-
344
- # Faz o reranker!
345
-
346
-
347
-
348
- ScriptTable = [];
349
- for score in rerank(Question, doclist):
350
- i = score['corpus_id'];
351
- script = FoundScripts[i];
352
- script['rank'] = str(score['score'])
353
- link = "https://github.com/rrg92/sqlserver-lib/tree/main/" + script['RelPath']
354
- script['link'] = link;
355
-
356
- if not AsJson:
357
- ScriptTable.append({
358
- 'Link': f'<a title="{link}" href="{link}" target="_blank">{script["RelPath"]}</a>'
359
- ,'Length': script['ContentLength']
360
- ,'Cosine Similarity': script['Similaridade']
361
- ,'Rank': script['rank']
362
- })
363
-
364
- RankedScripts = sorted(FoundScripts, key=lambda item: float(item['rank']), reverse=True)
365
-
366
- #result = pd.DataFrame(ScriptTable)
367
- jsonresult = json.dumps(RankedScripts)
368
-
369
- return jsonresult;
370
-
371
- resultTable = gr.Dataframe(datatype = ['html','number','number'], interactive = False, show_search = "search");
372
- TextResults = gr.Textbox()
373
-
374
- with gr.Blocks(fill_height=True) as demo:
375
-
376
- with gr.Column():
377
-
378
- tabSettings = gr.Tab("Settings", render = False)
379
-
380
- with tabSettings:
381
- LangOpts = gr.Radio([("Auto Detect from text","auto"), ("Use browser language","browser")], value="auto", label="Language", info="Choose lang used by AI to answer you!")
382
- LangChoose = gr.Textbox(info = "This will be filled with detect browser language, but you can change")
383
-
384
- LangOpts.change(None, [LangOpts],[LangChoose], js = """
385
- function(opt){
386
- if(opt == "browser"){
387
- return navigator ? navigator.language : "en-US";
388
- }
389
- }
390
- """)
391
-
392
-
393
- with gr.Tab("Chat", scale = 1):
394
- ChatTextBox = gr.Textbox(max_length = 500, info = "Which script are you looking for?", submit_btn = True);
395
-
396
- gr.ChatInterface(
397
- ChatFunc
398
- ,additional_outputs=[resultTable]
399
- ,additional_inputs=[LangOpts,LangChoose]
400
- ,type="messages"
401
- ,textbox = ChatTextBox
402
- )
403
-
404
- tabSettings.render()
405
-
406
-
407
- with gr.Tab("Rank"):
408
- txtSearchTable = gr.Textbox(label="Search script files",info="Description of what you want", visible = False)
409
- AsJson = gr.Checkbox(visible = False)
410
- resultTable.render();
411
-
412
-
413
- txtSearchTable.submit(SearchFiles, [txtSearchTable],[TextResults])
414
-
415
- with gr.Tab("Help"):
416
- gr.Markdown("""
417
- Bem-vindo ao Space SQL Server Lib
418
- Este space permite que você encontre scripts SQL do https://github.com/rrg92/sqlserver-lib com base nas suas necessidades
419
-
420
-
421
- ## Instruções de Uso
422
- Apenas descreva o que você precisa no campo de chat e aguarde a IA analisar os melhores scripts do repositório para você.
423
- Além de uma explicação feita pela IA, a aba "Rank", contém uma tabela com os scripts encontrados e seus respectictos rank.
424
- A coluna Cosine Similarity é o nível de similaridades da sua pergunta com o script (calculado baseado nos embeddings do seu texto e do script).
425
- A coluna Rank é um score onde quanto maior o valor mais relacionado ao seu texto o script é (calculado usando rerank/cross encoders). A tabela vem ordenada por essa coluna.
426
-
427
-
428
- ## Fluxo básico
429
- - Quando você digita o texto, iremos fazer uma busca usando embeddings em um banco Azure SQL Database
430
- - Os embeddings são calculados usando um modelo carregado no proprio script, via ZeroGPU.
431
- - Os top 20 resultados mais similares são retornados e então um rerank é feito
432
- - O rerank também é feito por um modelo que roda no próprio script, em ZeroGPU
433
- - Estes resultados ordenados por reran, são então enviados ao LLM para que analise e monte uma resposta para você.
434
-
435
-
436
- ## Sobre o uso e eventuais erros
437
- Eu tento usar o máximo de recursos FREE e open possíveis, e portanto, eventualmente, o Space pode falhar por alguma limitação.
438
- Alguns possíveis pontos de falha:
439
- - Créditos free do google ou rate limit
440
- - Azure SQL database offline devido a crédito ou ao auto-pause (devido ao free tier)
441
- - Limites de uso do ZeroGPU do Hugging Face.
442
-
443
- Você pode me procurar no [linkedin](https://www.linkedin.com/in/rodrigoribeirogomes/), caso receba erroslimit
444
-
445
- """)
446
-
447
- with gr.Tab("Other", visible = False):
448
- txtEmbed = gr.Text(label="Text to embed", visible=False)
449
- btnEmbed = gr.Button("embed");
450
- btnEmbed.click(embed, [txtEmbed], [txtEmbed])
451
-
452
- TextResults.render();
453
-
454
-
455
-
456
-
457
-
458
-
459
-
460
-
461
-
462
-
463
-
464
-
465
-
466
- if __name__ == "__main__":
467
- demo.launch(
468
- share=False,
469
- debug=False,
470
- server_port=7860,
471
- server_name="0.0.0.0",
472
- allowed_paths=[]
473
- )
 
1
+ import gradio as gr
2
+ import spaces
3
+
4
+
5
+
6
+ @spaces.GPU
7
+ def RunGpu(p):
8
+ return p;
9
+
10
+
11
+ def ZeroGpuTest(p):
12
+ try:
13
+ return RunGpu(p)
14
+ except Exception as e:
15
+ return e;
16
+
17
+
18
+
19
+
20
+ with gr.Blocks() as demo:
21
+
22
+ gr.Markdown("""
23
+ This space demonstrates a problem with ZeroGPU and the public host link of the space, which is the link used when you embed it.
24
+ Steps to reproduce:
25
+ - Just type something in the textbox below, then click the submit button.
26
+ - If you have minutes left, the script runs fine.
27
+ - Now, access the public host URL of this space: https://rrg92-sqlserver-lib-assistant-debug.hf.space.
28
+ - Try doing the same thing. You will receive an error related to ZeroGPU minutes being left!
29
+ """)
30
+
31
+
32
+ text = gr.Textbox(submit_btn = True);
33
+ result = gr.Textbox()
34
+
35
+ text.submit(ZeroGpuTest,[text],[result])
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+ if __name__ == "__main__":
49
+ demo.launch(
50
+ share=False,
51
+ debug=False,
52
+ server_port=7860,
53
+ server_name="0.0.0.0",
54
+ allowed_paths=[]
55
+ )