k96beni commited on
Commit
ba33430
·
verified ·
1 Parent(s): e4a34f5

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -396
app.py DELETED
@@ -1,396 +0,0 @@
1
- #kicker
2
-
3
- import os
4
- import pandas as pd
5
- import torch
6
- import gradio as gr
7
- import textwrap # för chunk-exempel
8
-
9
- from huggingface_hub import login, Repository
10
- from sentence_transformers import SentenceTransformer, util
11
-
12
- # ====================================================
13
- # KONFIGURATION OCH INIT
14
- # ====================================================
15
- HF_TOKEN = os.getenv("HUGGINGFACE_TOKEN") # Miljövariabel med din Hugging Face-token
16
- if not HF_TOKEN:
17
- raise ValueError("HUGGINGFACE_TOKEN miljövariabel saknas!")
18
-
19
- login(token=HF_TOKEN)
20
-
21
- # Ange var du har din Space och CSV
22
- REPO_ID = "ChargeNodeEurope/Chatbot_4o_mini" # Exempel
23
- REPO_LOCAL_PATH = "chatbot_faq_repo"
24
- FAQ_CSV_PATH = "faq_städad_utf8_fixed.csv"
25
-
26
- # Klona från Space:en (OBS: repo_type="space")
27
- repo = Repository(
28
- local_dir=REPO_LOCAL_PATH,
29
- clone_from=REPO_ID,
30
- repo_type="space",
31
- use_auth_token=HF_TOKEN
32
- )
33
-
34
- # Ladda FAQ
35
- faq_path = os.path.join(REPO_LOCAL_PATH, FAQ_CSV_PATH)
36
- try:
37
- df = pd.read_csv(faq_path, sep=";")
38
- df.dropna(subset=["Fråga", "Svar"], inplace=True)
39
- except Exception as e:
40
- raise FileNotFoundError(f"Kunde inte ladda FAQ-filen: {str(e)}")
41
-
42
- # -- Standardparametrar vi kan ändra i Admin-UI --
43
- CHUNK_SIZE = 0 # 0 innebär ingen chunkning som default
44
- TEMPERATURE = 0.0 # "Heat param", 0 = ingen slump, 1 = maximal slump (demonstration)
45
-
46
- # Ladda modell
47
- model_name = "sentence-transformers/all-MiniLM-L6-v2"
48
- model = SentenceTransformer(model_name)
49
-
50
- # ------------------------------------------------
51
- # ENKEL CHUNKING (illustrativt exempel)
52
- # ------------------------------------------------
53
- def chunk_text(text: str, chunk_size: int):
54
- """
55
- Delar upp en textsträng i bitar om 'chunk_size' tecken.
56
- Returnerar en lista med textchunkar.
57
- Om chunk_size <= 0, returneras texten som en enda bit.
58
- """
59
- if chunk_size <= 0:
60
- return [text]
61
- # Använder textwrap.wrap eller en egen slicing:
62
- wrapped = textwrap.wrap(text, width=chunk_size)
63
- return wrapped
64
-
65
- def build_faq_embeddings(df: pd.DataFrame, chunk_size: int):
66
- """
67
- Bygger en lista med (fråga, svar, kategori) men chunkar
68
- svaret om chunk_size > 0. Returnerar:
69
- - en DataFrame expanded_df
70
- - embeddings (tensor)
71
- Varje "rad" i expanded_df representerar en chunk av svaret.
72
- """
73
- all_rows = []
74
- for _, row in df.iterrows():
75
- fråga = row["Fråga"]
76
- svar = row["Svar"]
77
- kategori = row["Kategori"]
78
- # Chunka svaret
79
- svar_chunks = chunk_text(str(svar), chunk_size)
80
- # Skapa en ny rad för varje chunk
81
- for chunk in svar_chunks:
82
- all_rows.append((fråga, chunk, kategori))
83
-
84
- expanded_df = pd.DataFrame(all_rows, columns=["Fråga", "Svar_chunk", "Kategori"])
85
- # Nu skapar vi embeddings baserat på "Fråga + Svar_chunk" eller bara "Fråga"?
86
- # Här visar vi ett exempel där vi enbart använder FAQ-frågan som embed.
87
- # Om du vill även ta med chunkade svaret i embeddings kan du ändra här.
88
- faq_embeddings = model.encode(expanded_df["Fråga"].tolist(), convert_to_tensor=True)
89
- return expanded_df, faq_embeddings
90
-
91
- # Bygg initial embeddings
92
- expanded_df, faq_embeddings = build_faq_embeddings(df, CHUNK_SIZE)
93
-
94
- # ====================================================
95
- # FUNKTIONER FÖR SÖK & FAQ-HANTERING
96
- # ====================================================
97
- def uppdatera_parametrar(chunk_size, temperature):
98
- """
99
- Uppdaterar våra globala parametrar för chunkning och "temperature".
100
- Bygger om embeddings om chunk_size ändras.
101
- """
102
- global CHUNK_SIZE, TEMPERATURE, df, expanded_df, faq_embeddings
103
-
104
- # Sätt nya värden
105
- CHUNK_SIZE = int(chunk_size)
106
- TEMPERATURE = float(temperature)
107
-
108
- # Bygg om embeddings med nya chunk_size
109
- expanded_df, faq_embeddings = build_faq_embeddings(df, CHUNK_SIZE)
110
-
111
- return f"Parametrar uppdaterade:\n- CHUNK_SIZE={CHUNK_SIZE}\n- TEMPERATURE={TEMPERATURE}"
112
-
113
- def sök_faq(question, top_k, threshold):
114
- """
115
- Söker i expanded_df med semantisk likhet.
116
- 'temperature' används här som en "slump-faktor" (demonstration).
117
- - En låg temperature -> sortera normalt efter cos sim
118
- - En högre temperature -> slumpa lite i top-k
119
- Returnerar en DataFrame med max top_k rader.
120
- """
121
- question = question.strip()
122
- if not question:
123
- return pd.DataFrame(columns=["Liknande FAQ-fråga", "Svar_chunk", "Kategori", "Confidence"])
124
-
125
- # Skapa embedding för query
126
- query_emb = model.encode(question, convert_to_tensor=True)
127
- cos_scores = util.cos_sim(query_emb, faq_embeddings)[0]
128
-
129
- # Hämta index och scores
130
- # Ex: top_k = 5
131
- top_results = torch.topk(cos_scores, k=int(top_k))
132
- indices = top_results.indices.tolist()
133
- scores = top_results.values.tolist()
134
-
135
- # Om TEMPERATURE > 0, "skaka om" listan lite (demonstrationssyfte).
136
- # Ju högre temperatur, desto större slumpmoment i ordningen.
137
- # (OBS: Detta är inte en "officiell" generativ temperatur-implementation,
138
- # men visar hur man kan introducera variation.)
139
- if TEMPERATURE > 0:
140
- import random
141
- pairs = list(zip(indices, scores))
142
- # slumpa om med en vikt av 'TEMPERATURE'
143
- # t.ex. multiplicera score med en slumpfaktor
144
- new_pairs = []
145
- for (idx, sc) in pairs:
146
- random_factor = 1.0 + (random.random() - 0.5) * TEMPERATURE
147
- new_pairs.append((idx, sc * random_factor))
148
- # sortera om
149
- new_pairs.sort(key=lambda x: x[1], reverse=True)
150
- indices = [p[0] for p in new_pairs]
151
- scores = [p[1] for p in new_pairs]
152
-
153
- data = []
154
- for i, (idx, score) in enumerate(zip(indices, scores)):
155
- if score >= threshold:
156
- row = expanded_df.iloc[idx]
157
- data.append({
158
- "Liknande FAQ-fråga": row["Fråga"],
159
- "Svar_chunk": row["Svar_chunk"],
160
- "Kategori": row["Kategori"],
161
- "Confidence": round(float(score), 3)
162
- })
163
-
164
- # Begränsa faktiskt antal rader till top_k
165
- data = data[:int(top_k)]
166
- results_df = pd.DataFrame(data)
167
- return results_df
168
-
169
- def hitta_topp3_förslag(fråga):
170
- """
171
- En enkel variant som alltid hämtar top_k=3, threshold=0.0
172
- (används i de snabba sökexemplen).
173
- """
174
- return sök_faq(fråga, top_k=3, threshold=0.0)
175
-
176
- def lägg_till_faq(fråga, svar, kategori):
177
- """
178
- Lägger till en ny rad i FAQ-DataFrame och pushar ändringen.
179
- """
180
- global df, expanded_df, faq_embeddings
181
-
182
- fråga = fråga.strip()
183
- svar = svar.strip()
184
- kategori = kategori.strip()
185
-
186
- if not fråga or not svar or not kategori:
187
- return "Fråga, svar och kategori får inte vara tomma!"
188
-
189
- try:
190
- # Lägg till rad
191
- ny_rad = pd.DataFrame([[fråga, svar, kategori]], columns=["Fråga", "Svar", "Kategori"])
192
- df = pd.concat([df, ny_rad], ignore_index=True)
193
-
194
- # Spara CSV
195
- df.to_csv(faq_path, index=False, sep=";")
196
-
197
- # Bygg nya embeddings med nya df
198
- expanded_df, faq_embeddings = build_faq_embeddings(df, CHUNK_SIZE)
199
-
200
- # Git commit & push
201
- repo.git_add()
202
- repo.git_commit(f"Lade till FAQ: {fråga[:50]}...")
203
- repo.git_push()
204
-
205
- return "Fråga tillagd och synkad med Hugging Face!"
206
- except Exception as e:
207
- return f"Fel vid uppdatering: {str(e)}"
208
-
209
- def visa_senaste_faq(antal=10):
210
- """Returnerar de sista N raderna från FAQ-DataFrame."""
211
- return df.tail(antal)
212
-
213
- def uppdatera_faq(gammal_fråga, nytt_svar, ny_kategori):
214
- """
215
- Uppdaterar en befintlig rad (matchar på 'gammal_fråga').
216
- """
217
- global df, expanded_df, faq_embeddings
218
- gammal_fråga = gammal_fråga.strip()
219
- nytt_svar = nytt_svar.strip()
220
- ny_kategori = ny_kategori.strip()
221
-
222
- if not gammal_fråga:
223
- return "Ingen fråga vald."
224
-
225
- match_index = df.index[df["Fråga"] == gammal_fråga]
226
- if len(match_index) == 0:
227
- return "Ingen matchande FAQ-fråga hittad."
228
-
229
- # Uppdatera
230
- if nytt_svar:
231
- df.loc[match_index, "Svar"] = nytt_svar
232
- if ny_kategori:
233
- df.loc[match_index, "Kategori"] = ny_kategori
234
-
235
- # Spara & uppdatera embeddings
236
- try:
237
- df.to_csv(faq_path, index=False, sep=";")
238
- expanded_df, faq_embeddings = build_faq_embeddings(df, CHUNK_SIZE)
239
- repo.git_add()
240
- repo.git_commit(f"Uppdaterade FAQ: {gammal_fråga[:50]}...")
241
- repo.git_push()
242
- return f"FAQ uppdaterad: {gammal_fråga}"
243
- except Exception as e:
244
- return f"Fel vid uppdatering: {str(e)}"
245
-
246
- def ta_bort_faq(fråga_att_radera):
247
- """
248
- Tar bort en FAQ-rad baserat på exakt match på 'fråga'.
249
- """
250
- global df, expanded_df, faq_embeddings
251
- fråga_att_radera = fråga_att_radera.strip()
252
- if not fråga_att_radera:
253
- return "Ingen fråga vald."
254
-
255
- match_index = df.index[df["Fråga"] == fråga_att_radera]
256
- if len(match_index) == 0:
257
- return "Ingen matchande FAQ-fråga hittad."
258
-
259
- # Ta bort rad
260
- df.drop(match_index, inplace=True)
261
-
262
- # Spara, uppdatera embeddings
263
- try:
264
- df.to_csv(faq_path, index=False, sep=";")
265
- expanded_df, faq_embeddings = build_faq_embeddings(df, CHUNK_SIZE)
266
- repo.git_add()
267
- repo.git_commit(f"Raderade FAQ: {fråga_att_radera[:50]}...")
268
- repo.git_push()
269
- return f"FAQ borttagen: {fråga_att_radera}"
270
- except Exception as e:
271
- return f"Fel vid borttagning: {str(e)}"
272
-
273
-
274
- # ====================================================
275
- # GRADIO-GRÄNSSNITT
276
- # ====================================================
277
- with gr.Blocks() as demo:
278
- gr.Markdown("# ChargeNode Chatbot – Utvecklarläge\n"
279
- "Ett admin-UI där du kan hantera FAQ-poster, testa sökning och "
280
- "leka med parametrar (chunk size & \"heat\").")
281
-
282
- # =======================
283
- # (A) Parametrar
284
- # =======================
285
- with gr.Accordion("Parametrar", open=True):
286
- gr.Markdown("Justera chunk-storlek och \"temperature\" (heat).")
287
- chunk_slider = gr.Slider(minimum=0, maximum=200, step=10, value=0,
288
- label="CHUNK_SIZE (tecken per bit). 0 = av.")
289
- temp_slider = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, value=0.0,
290
- label="TEMPERATURE (heat). 0 = ingen slump, 1 = max slump.")
291
- uppdatera_btn = gr.Button("Uppdatera parametrar")
292
- param_output = gr.Textbox(label="Status")
293
-
294
- uppdatera_btn.click(
295
- fn=uppdatera_parametrar,
296
- inputs=[chunk_slider, temp_slider],
297
- outputs=param_output
298
- )
299
-
300
- # =======================
301
- # (B) SÖK I FAQ
302
- # =======================
303
- with gr.Accordion("Sök i FAQ", open=False):
304
- gr.Markdown("Skriv in en fråga och se topp K träffar.")
305
- fråga_input = gr.Textbox(label="Din fråga", placeholder="Ex: Var finns närmaste laddstation?")
306
- topk_slider = gr.Slider(minimum=1, maximum=10, step=1, value=3, label="Antal toppträffar (top_k)")
307
- threshold_slider = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, value=0.0, label="Confidence threshold")
308
- sök_knapp = gr.Button("Sök")
309
- sök_result = gr.Dataframe(headers=["Liknande FAQ-fråga", "Svar_chunk", "Kategori", "Confidence"],
310
- label="Sökresultat")
311
-
312
- sök_knapp.click(
313
- fn=sök_faq,
314
- inputs=[fråga_input, topk_slider, threshold_slider],
315
- outputs=sök_result
316
- )
317
-
318
- # =======================
319
- # (C) KORTVARIANTE: Hitta topp 3
320
- # =======================
321
- with gr.Accordion("Enkel sökning (topp 3)", open=False):
322
- gr.Markdown("Här kan du snabbt få topp 3 träffar (threshold=0) utan att ställa in parametrar.")
323
- fråga_input2 = gr.Textbox(label="Din fråga", placeholder="Ex: Hur ändrar jag lösenord?")
324
- knapp2 = gr.Button("Hämta topp 3")
325
- result2 = gr.Dataframe(label="Topp 3 förslag")
326
- knapp2.click(fn=hitta_topp3_förslag, inputs=fråga_input2, outputs=result2)
327
-
328
- # =======================
329
- # (D) Lägg till ny FAQ-rad
330
- # =======================
331
- with gr.Accordion("Lägg till en ny FAQ-rad", open=False):
332
- ny_fråga = gr.Textbox(label="Ny fråga", placeholder="Ex: Hur registrerar jag ett konto?")
333
- nytt_svar = gr.Textbox(label="Nytt svar", placeholder="Skriv ditt svar här...")
334
- ny_kategori = gr.Textbox(label="Ny kategori", placeholder="Ex: Konto")
335
-
336
- lägg_till_knapp = gr.Button("Lägg till i FAQ")
337
- lägg_till_output = gr.Textbox(label="Status")
338
-
339
- def clear_add_fields():
340
- return "", "", ""
341
-
342
- reset_ny = gr.Button("Rensa fält")
343
-
344
- lägg_till_knapp.click(
345
- fn=lägg_till_faq,
346
- inputs=[ny_fråga, nytt_svar, ny_kategori],
347
- outputs=lägg_till_output
348
- )
349
- reset_ny.click(
350
- fn=clear_add_fields,
351
- inputs=[],
352
- outputs=[ny_fråga, nytt_svar, ny_kategori]
353
- )
354
-
355
- # =======================
356
- # (E) Redigera/ta bort FAQ
357
- # =======================
358
- with gr.Accordion("Redigera eller ta bort befintlig FAQ", open=False):
359
- befintliga_frågor = gr.Dropdown(choices=df["Fråga"].tolist(),
360
- label="Välj en befintlig fråga")
361
- nytt_svar_edit = gr.Textbox(label="Nytt svar (valfritt)")
362
- ny_kategori_edit = gr.Textbox(label="Ny kategori (valfritt)")
363
- uppdatera_btn2 = gr.Button("Uppdatera vald FAQ")
364
- uppdatera_output = gr.Textbox(label="Status")
365
-
366
- radera_btn = gr.Button("Ta bort vald FAQ")
367
- radera_output = gr.Textbox(label="Status")
368
-
369
- uppdatera_btn2.click(
370
- fn=uppdatera_faq,
371
- inputs=[befintliga_frågor, nytt_svar_edit, ny_kategori_edit],
372
- outputs=uppdatera_output
373
- )
374
- radera_btn.click(
375
- fn=ta_bort_faq,
376
- inputs=befintliga_frågor,
377
- outputs=radera_output
378
- )
379
-
380
- # =======================
381
- # (F) Visa senaste FAQ-poster
382
- # =======================
383
- with gr.Accordion("Visa senaste FAQ-poster", open=False):
384
- visa_btn = gr.Button("Visa senaste 10")
385
- logg_output = gr.Dataframe(label="Sista 10 FAQ-rader")
386
- visa_btn.click(fn=visa_senaste_faq, inputs=[], outputs=logg_output)
387
-
388
- gr.Markdown("---")
389
- gr.Markdown("**OBS:** Detta skript använder en \"Repository\"-klass som är på väg att fasas ut "
390
- "i huggingface_hub. Det fungerar i dagsläget, men vill du framöver undvika detta "
391
- "kan du gå över till HTTP-baserade metoder för att ladda upp/ned filer. "
392
- "Se Hugging Face-dokumentationen för mer info.")
393
-
394
- # Starta Gradio
395
- if __name__ == "__main__":
396
- demo.launch()