oceansweep commited on
Commit
be0884e
1 Parent(s): e04fa0c

Update App_Function_Libraries/Gradio_UI/Embeddings_tab.py

Browse files
App_Function_Libraries/Gradio_UI/Embeddings_tab.py CHANGED
@@ -1,715 +1,715 @@
1
- # Embeddings_tabc.py
2
- # Description: This file contains the code for the RAG Chat tab in the Gradio UI
3
- #
4
- # Imports
5
- import json
6
- import logging
7
- import os
8
- #
9
- # External Imports
10
- import gradio as gr
11
- import numpy as np
12
- from tqdm import tqdm
13
- #
14
- # Local Imports
15
- from App_Function_Libraries.DB.DB_Manager import get_all_content_from_database, get_all_conversations, \
16
- get_conversation_text, get_note_by_id
17
- from App_Function_Libraries.DB.RAG_QA_Chat_DB import get_all_notes
18
- from App_Function_Libraries.RAG.ChromaDB_Library import chroma_client, \
19
- store_in_chroma, situate_context
20
- from App_Function_Libraries.RAG.Embeddings_Create import create_embedding, create_embeddings_batch
21
- from App_Function_Libraries.Chunk_Lib import improved_chunking_process, chunk_for_embedding
22
- from App_Function_Libraries.Utils.Utils import load_and_log_configs
23
-
24
-
25
- #
26
- ########################################################################################################################
27
- #
28
- # Functions:
29
-
30
- def create_embeddings_tab():
31
- # Load configuration first
32
- config = load_and_log_configs()
33
- if not config:
34
- raise ValueError("Could not load configuration")
35
-
36
- # Get database paths from config
37
- db_config = config['db_config']
38
- media_db_path = db_config['sqlite_path']
39
- rag_qa_db_path = os.path.join(os.path.dirname(media_db_path), "rag_qa.db")
40
- character_chat_db_path = os.path.join(os.path.dirname(media_db_path), "chatDB.db")
41
- chroma_db_path = db_config['chroma_db_path']
42
-
43
- with gr.TabItem("Create Embeddings", visible=True):
44
- gr.Markdown("# Create Embeddings for All Content")
45
-
46
- with gr.Row():
47
- with gr.Column():
48
- # Database selection at the top
49
- database_selection = gr.Radio(
50
- choices=["Media DB", "RAG Chat", "Character Chat"],
51
- label="Select Content Source",
52
- value="Media DB",
53
- info="Choose which database to create embeddings from"
54
- )
55
-
56
- # Add database path display
57
- current_db_path = gr.Textbox(
58
- label="Current Database Path",
59
- value=media_db_path,
60
- interactive=False
61
- )
62
-
63
- embedding_provider = gr.Radio(
64
- choices=["huggingface", "local", "openai"],
65
- label="Select Embedding Provider",
66
- value=config['embedding_config']['embedding_provider'] or "huggingface"
67
- )
68
- gr.Markdown("Note: Local provider requires a running Llama.cpp/llamafile server.")
69
- gr.Markdown("OpenAI provider requires a valid API key.")
70
-
71
- huggingface_model = gr.Dropdown(
72
- choices=[
73
- "jinaai/jina-embeddings-v3",
74
- "Alibaba-NLP/gte-large-en-v1.5",
75
- "dunzhang/setll_en_400M_v5",
76
- "custom"
77
- ],
78
- label="Hugging Face Model",
79
- value="jinaai/jina-embeddings-v3",
80
- visible=True
81
- )
82
-
83
- openai_model = gr.Dropdown(
84
- choices=[
85
- "text-embedding-3-small",
86
- "text-embedding-3-large"
87
- ],
88
- label="OpenAI Embedding Model",
89
- value="text-embedding-3-small",
90
- visible=False
91
- )
92
-
93
- custom_embedding_model = gr.Textbox(
94
- label="Custom Embedding Model",
95
- placeholder="Enter your custom embedding model name here",
96
- visible=False
97
- )
98
-
99
- embedding_api_url = gr.Textbox(
100
- label="API URL (for local provider)",
101
- value=config['embedding_config']['embedding_api_url'],
102
- visible=False
103
- )
104
-
105
- # Add chunking options with config defaults
106
- chunking_method = gr.Dropdown(
107
- choices=["words", "sentences", "paragraphs", "tokens", "semantic"],
108
- label="Chunking Method",
109
- value="words"
110
- )
111
- max_chunk_size = gr.Slider(
112
- minimum=1, maximum=8000, step=1,
113
- value=config['embedding_config']['chunk_size'],
114
- label="Max Chunk Size"
115
- )
116
- chunk_overlap = gr.Slider(
117
- minimum=0, maximum=4000, step=1,
118
- value=config['embedding_config']['overlap'],
119
- label="Chunk Overlap"
120
- )
121
- adaptive_chunking = gr.Checkbox(
122
- label="Use Adaptive Chunking",
123
- value=False
124
- )
125
-
126
- create_button = gr.Button("Create Embeddings")
127
-
128
- with gr.Column():
129
- status_output = gr.Textbox(label="Status", lines=10)
130
- progress = gr.Progress()
131
-
132
- def update_provider_options(provider):
133
- if provider == "huggingface":
134
- return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
135
- elif provider == "local":
136
- return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
137
- else: # OpenAI
138
- return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
139
-
140
- def update_huggingface_options(model):
141
- if model == "custom":
142
- return gr.update(visible=True)
143
- else:
144
- return gr.update(visible=False)
145
-
146
- def update_database_path(database_type):
147
- if database_type == "Media DB":
148
- return media_db_path
149
- elif database_type == "RAG Chat":
150
- return rag_qa_db_path
151
- else: # Character Chat
152
- return character_chat_db_path
153
-
154
- def create_all_embeddings(provider, hf_model, openai_model, custom_model, api_url, method,
155
- max_size, overlap, adaptive, database_type, progress=gr.Progress()):
156
- try:
157
- # Initialize content based on database selection
158
- if database_type == "Media DB":
159
- all_content = get_all_content_from_database()
160
- content_type = "media"
161
- elif database_type == "RAG Chat":
162
- all_content = []
163
- page = 1
164
- while True:
165
- conversations, total_pages, _ = get_all_conversations(page=page)
166
- if not conversations:
167
- break
168
- all_content.extend([{
169
- 'id': conv['conversation_id'],
170
- 'content': get_conversation_text(conv['conversation_id']),
171
- 'title': conv['title'],
172
- 'type': 'conversation'
173
- } for conv in conversations])
174
- progress(page / total_pages, desc=f"Loading conversations... Page {page}/{total_pages}")
175
- page += 1
176
- else: # Character Chat
177
- all_content = []
178
- page = 1
179
- while True:
180
- notes, total_pages, _ = get_all_notes(page=page)
181
- if not notes:
182
- break
183
- all_content.extend([{
184
- 'id': note['id'],
185
- 'content': f"{note['title']}\n\n{note['content']}",
186
- 'conversation_id': note['conversation_id'],
187
- 'type': 'note'
188
- } for note in notes])
189
- progress(page / total_pages, desc=f"Loading notes... Page {page}/{total_pages}")
190
- page += 1
191
-
192
- if not all_content:
193
- return "No content found in the selected database."
194
-
195
- chunk_options = {
196
- 'method': method,
197
- 'max_size': max_size,
198
- 'overlap': overlap,
199
- 'adaptive': adaptive
200
- }
201
-
202
- collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
203
- collection = chroma_client.get_or_create_collection(name=collection_name)
204
-
205
- # Determine the model to use
206
- if provider == "huggingface":
207
- model = custom_model if hf_model == "custom" else hf_model
208
- elif provider == "openai":
209
- model = openai_model
210
- else:
211
- model = api_url
212
-
213
- total_items = len(all_content)
214
- for idx, item in enumerate(all_content):
215
- progress((idx + 1) / total_items, desc=f"Processing item {idx + 1} of {total_items}")
216
-
217
- content_id = item['id']
218
- text = item['content']
219
-
220
- chunks = improved_chunking_process(text, chunk_options)
221
- for chunk_idx, chunk in enumerate(chunks):
222
- chunk_text = chunk['text']
223
- chunk_id = f"{database_type.lower()}_{content_id}_chunk_{chunk_idx}"
224
-
225
- try:
226
- embedding = create_embedding(chunk_text, provider, model, api_url)
227
- metadata = {
228
- 'content_id': str(content_id),
229
- 'chunk_index': int(chunk_idx),
230
- 'total_chunks': int(len(chunks)),
231
- 'chunking_method': method,
232
- 'max_chunk_size': int(max_size),
233
- 'chunk_overlap': int(overlap),
234
- 'adaptive_chunking': bool(adaptive),
235
- 'embedding_model': model,
236
- 'embedding_provider': provider,
237
- 'content_type': item.get('type', 'media'),
238
- 'conversation_id': item.get('conversation_id'),
239
- **{k: (int(v) if isinstance(v, str) and v.isdigit() else v)
240
- for k, v in chunk['metadata'].items()}
241
- }
242
- store_in_chroma(collection_name, [chunk_text], [embedding], [chunk_id], [metadata])
243
-
244
- except Exception as e:
245
- logging.error(f"Error processing chunk {chunk_id}: {str(e)}")
246
- continue
247
-
248
- return f"Embeddings created and stored successfully for all {database_type} content."
249
- except Exception as e:
250
- logging.error(f"Error during embedding creation: {str(e)}")
251
- return f"Error: {str(e)}"
252
-
253
- # Event handlers
254
- embedding_provider.change(
255
- fn=update_provider_options,
256
- inputs=[embedding_provider],
257
- outputs=[huggingface_model, openai_model, custom_embedding_model, embedding_api_url]
258
- )
259
-
260
- huggingface_model.change(
261
- fn=update_huggingface_options,
262
- inputs=[huggingface_model],
263
- outputs=[custom_embedding_model]
264
- )
265
-
266
- database_selection.change(
267
- fn=update_database_path,
268
- inputs=[database_selection],
269
- outputs=[current_db_path]
270
- )
271
-
272
- create_button.click(
273
- fn=create_all_embeddings,
274
- inputs=[
275
- embedding_provider, huggingface_model, openai_model, custom_embedding_model,
276
- embedding_api_url, chunking_method, max_chunk_size, chunk_overlap,
277
- adaptive_chunking, database_selection
278
- ],
279
- outputs=status_output
280
- )
281
-
282
-
283
- def create_view_embeddings_tab():
284
- # Load configuration first
285
- config = load_and_log_configs()
286
- if not config:
287
- raise ValueError("Could not load configuration")
288
-
289
- # Get database paths from config
290
- db_config = config['db_config']
291
- media_db_path = db_config['sqlite_path']
292
- rag_qa_db_path = os.path.join(os.path.dirname(media_db_path), "rag_chat.db")
293
- character_chat_db_path = os.path.join(os.path.dirname(media_db_path), "character_chat.db")
294
- chroma_db_path = db_config['chroma_db_path']
295
-
296
- with gr.TabItem("View/Update Embeddings", visible=True):
297
- gr.Markdown("# View and Update Embeddings")
298
- # Initialize item_mapping as a Gradio State
299
-
300
-
301
- with gr.Row():
302
- with gr.Column():
303
- # Add database selection
304
- database_selection = gr.Radio(
305
- choices=["Media DB", "RAG Chat", "Character Chat"],
306
- label="Select Content Source",
307
- value="Media DB",
308
- info="Choose which database to view embeddings from"
309
- )
310
-
311
- # Add database path display
312
- current_db_path = gr.Textbox(
313
- label="Current Database Path",
314
- value=media_db_path,
315
- interactive=False
316
- )
317
-
318
- item_dropdown = gr.Dropdown(label="Select Item", choices=[], interactive=True)
319
- refresh_button = gr.Button("Refresh Item List")
320
- embedding_status = gr.Textbox(label="Embedding Status", interactive=False)
321
- embedding_preview = gr.Textbox(label="Embedding Preview", interactive=False, lines=5)
322
- embedding_metadata = gr.Textbox(label="Embedding Metadata", interactive=False, lines=10)
323
-
324
- with gr.Column():
325
- create_new_embedding_button = gr.Button("Create New Embedding")
326
- embedding_provider = gr.Radio(
327
- choices=["huggingface", "local", "openai"],
328
- label="Select Embedding Provider",
329
- value="huggingface"
330
- )
331
- gr.Markdown("Note: Local provider requires a running Llama.cpp/llamafile server.")
332
- gr.Markdown("OpenAI provider requires a valid API key.")
333
-
334
- huggingface_model = gr.Dropdown(
335
- choices=[
336
- "jinaai/jina-embeddings-v3",
337
- "Alibaba-NLP/gte-large-en-v1.5",
338
- "dunzhang/stella_en_400M_v5",
339
- "custom"
340
- ],
341
- label="Hugging Face Model",
342
- value="jinaai/jina-embeddings-v3",
343
- visible=True
344
- )
345
-
346
- openai_model = gr.Dropdown(
347
- choices=[
348
- "text-embedding-3-small",
349
- "text-embedding-3-large"
350
- ],
351
- label="OpenAI Embedding Model",
352
- value="text-embedding-3-small",
353
- visible=False
354
- )
355
-
356
- custom_embedding_model = gr.Textbox(
357
- label="Custom Embedding Model",
358
- placeholder="Enter your custom embedding model name here",
359
- visible=False
360
- )
361
-
362
- embedding_api_url = gr.Textbox(
363
- label="API URL (for local provider)",
364
- value=config['embedding_config']['embedding_api_url'],
365
- visible=False
366
- )
367
-
368
- chunking_method = gr.Dropdown(
369
- choices=["words", "sentences", "paragraphs", "tokens", "semantic"],
370
- label="Chunking Method",
371
- value="words"
372
- )
373
- max_chunk_size = gr.Slider(
374
- minimum=1, maximum=8000, step=5, value=500,
375
- label="Max Chunk Size"
376
- )
377
- chunk_overlap = gr.Slider(
378
- minimum=0, maximum=5000, step=5, value=200,
379
- label="Chunk Overlap"
380
- )
381
- adaptive_chunking = gr.Checkbox(
382
- label="Use Adaptive Chunking",
383
- value=False
384
- )
385
- contextual_api_choice = gr.Dropdown(
386
- choices=["Local-LLM", "OpenAI", "Anthropic", "Cohere", "Groq", "DeepSeek", "Mistral", "OpenRouter", "Llama.cpp", "Kobold", "Ooba", "Tabbyapi", "VLLM", "ollama", "HuggingFace"],
387
- label="Select API for Contextualized Embeddings",
388
- value="OpenAI"
389
- )
390
- use_contextual_embeddings = gr.Checkbox(
391
- label="Use Contextual Embeddings",
392
- value=True
393
- )
394
- contextual_api_key = gr.Textbox(label="API Key", lines=1)
395
-
396
- item_mapping = gr.State(value={})
397
-
398
- def update_database_path(database_type):
399
- if database_type == "Media DB":
400
- return media_db_path
401
- elif database_type == "RAG Chat":
402
- return rag_qa_db_path
403
- else: # Character Chat
404
- return character_chat_db_path
405
-
406
- def get_items_with_embedding_status(database_type):
407
- try:
408
- # Get items based on database selection
409
- if database_type == "Media DB":
410
- items = get_all_content_from_database()
411
- elif database_type == "RAG Chat":
412
- conversations, _, _ = get_all_conversations(page=1)
413
- items = [{
414
- 'id': conv['conversation_id'],
415
- 'title': conv['title'],
416
- 'type': 'conversation'
417
- } for conv in conversations]
418
- else: # Character Chat
419
- notes, _, _ = get_all_notes(page=1)
420
- items = [{
421
- 'id': note['id'],
422
- 'title': note['title'],
423
- 'type': 'note'
424
- } for note in notes]
425
-
426
- collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
427
- collection = chroma_client.get_or_create_collection(name=collection_name)
428
-
429
- choices = []
430
- new_item_mapping = {}
431
- for item in items:
432
- try:
433
- chunk_id = f"{database_type.lower()}_{item['id']}_chunk_0"
434
- result = collection.get(ids=[chunk_id])
435
- embedding_exists = result is not None and result.get('ids') and len(result['ids']) > 0
436
- status = "Embedding exists" if embedding_exists else "No embedding"
437
- except Exception as e:
438
- print(f"Error checking embedding for item {item['id']}: {str(e)}")
439
- status = "Error checking"
440
- choice = f"{item['title']} ({status})"
441
- choices.append(choice)
442
- new_item_mapping[choice] = item['id']
443
- return gr.update(choices=choices), new_item_mapping
444
- except Exception as e:
445
- print(f"Error in get_items_with_embedding_status: {str(e)}")
446
- return gr.update(choices=["Error: Unable to fetch items"]), {}
447
-
448
- def update_provider_options(provider):
449
- if provider == "huggingface":
450
- return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
451
- elif provider == "local":
452
- return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
453
- else: # OpenAI
454
- return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
455
-
456
- def update_huggingface_options(model):
457
- if model == "custom":
458
- return gr.update(visible=True)
459
- else:
460
- return gr.update(visible=False)
461
-
462
- def check_embedding_status(selected_item, database_type, item_mapping):
463
- if not selected_item:
464
- return "Please select an item", "", ""
465
-
466
- if item_mapping is None:
467
- # If mapping is None, try to refresh it
468
- try:
469
- _, item_mapping = get_items_with_embedding_status(database_type)
470
- except Exception as e:
471
- return f"Error initializing item mapping: {str(e)}", "", ""
472
-
473
- try:
474
- item_id = item_mapping.get(selected_item)
475
- if item_id is None:
476
- return f"Invalid item selected: {selected_item}", "", ""
477
-
478
- item_title = selected_item.rsplit(' (', 1)[0]
479
- collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
480
- collection = chroma_client.get_or_create_collection(name=collection_name)
481
- chunk_id = f"{database_type.lower()}_{item_id}_chunk_0"
482
-
483
- try:
484
- result = collection.get(ids=[chunk_id], include=["embeddings", "metadatas"])
485
- except Exception as e:
486
- logging.error(f"ChromaDB get error: {str(e)}")
487
- return f"Error retrieving embedding for '{item_title}': {str(e)}", "", ""
488
-
489
- # Check if result exists and has the expected structure
490
- if not result or not isinstance(result, dict):
491
- return f"No embedding found for item '{item_title}' (ID: {item_id})", "", ""
492
-
493
- # Check if we have any results
494
- if not result.get('ids') or len(result['ids']) == 0:
495
- return f"No embedding found for item '{item_title}' (ID: {item_id})", "", ""
496
-
497
- # Check if embeddings exist
498
- if not result.get('embeddings') or not result['embeddings'][0]:
499
- return f"Embedding data missing for item '{item_title}' (ID: {item_id})", "", ""
500
-
501
- embedding = result['embeddings'][0]
502
- metadata = result.get('metadatas', [{}])[0] if result.get('metadatas') else {}
503
- embedding_preview = str(embedding[:50])
504
- status = f"Embedding exists for item '{item_title}' (ID: {item_id})"
505
- return status, f"First 50 elements of embedding:\n{embedding_preview}", json.dumps(metadata, indent=2)
506
-
507
- except Exception as e:
508
- logging.error(f"Error in check_embedding_status: {str(e)}", exc_info=True)
509
- return f"Error processing item: {selected_item}. Details: {str(e)}", "", ""
510
-
511
- def refresh_and_update(database_type):
512
- choices_update, new_mapping = get_items_with_embedding_status(database_type)
513
- return choices_update, new_mapping
514
-
515
- def create_new_embedding_for_item(selected_item, database_type, provider, hf_model, openai_model,
516
- custom_model, api_url, method, max_size, overlap, adaptive,
517
- item_mapping, use_contextual, contextual_api_choice=None):
518
- if not selected_item:
519
- return "Please select an item", "", ""
520
-
521
- try:
522
- item_id = item_mapping.get(selected_item)
523
- if item_id is None:
524
- return f"Invalid item selected: {selected_item}", "", ""
525
-
526
- # Get item content based on database type
527
- if database_type == "Media DB":
528
- items = get_all_content_from_database()
529
- item = next((item for item in items if item['id'] == item_id), None)
530
- elif database_type == "RAG Chat":
531
- item = {
532
- 'id': item_id,
533
- 'content': get_conversation_text(item_id),
534
- 'title': selected_item.rsplit(' (', 1)[0],
535
- 'type': 'conversation'
536
- }
537
- else: # Character Chat
538
- note = get_note_by_id(item_id)
539
- item = {
540
- 'id': item_id,
541
- 'content': f"{note['title']}\n\n{note['content']}",
542
- 'title': note['title'],
543
- 'type': 'note'
544
- }
545
-
546
- if not item:
547
- return f"Item not found: {item_id}", "", ""
548
-
549
- chunk_options = {
550
- 'method': method,
551
- 'max_size': max_size,
552
- 'overlap': overlap,
553
- 'adaptive': adaptive
554
- }
555
-
556
- logging.info(f"Chunking content for item: {item['title']} (ID: {item_id})")
557
- chunks = chunk_for_embedding(item['content'], item['title'], chunk_options)
558
- collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
559
- collection = chroma_client.get_or_create_collection(name=collection_name)
560
-
561
- # Delete existing embeddings for this item
562
- existing_ids = [f"{database_type.lower()}_{item_id}_chunk_{i}" for i in range(len(chunks))]
563
- collection.delete(ids=existing_ids)
564
- logging.info(f"Deleted {len(existing_ids)} existing embeddings for item {item_id}")
565
-
566
- texts, ids, metadatas = [], [], []
567
- chunk_count = 0
568
- logging.info("Generating contextual summaries and preparing chunks for embedding")
569
- for i, chunk in enumerate(chunks):
570
- chunk_text = chunk['text']
571
- chunk_metadata = chunk['metadata']
572
- if use_contextual:
573
- logging.debug(f"Generating contextual summary for chunk {chunk_count}")
574
- context = situate_context(contextual_api_choice, item['content'], chunk_text)
575
- contextualized_text = f"{chunk_text}\n\nContextual Summary: {context}"
576
- else:
577
- contextualized_text = chunk_text
578
- context = None
579
-
580
- chunk_id = f"{database_type.lower()}_{item_id}_chunk_{i}"
581
-
582
- # Determine the model to use
583
- if provider == "huggingface":
584
- model = custom_model if hf_model == "custom" else hf_model
585
- elif provider == "openai":
586
- model = openai_model
587
- else:
588
- model = custom_model
589
-
590
- metadata = {
591
- "content_id": str(item_id),
592
- "chunk_index": i,
593
- "total_chunks": len(chunks),
594
- "chunking_method": method,
595
- "max_chunk_size": max_size,
596
- "chunk_overlap": overlap,
597
- "adaptive_chunking": adaptive,
598
- "embedding_model": model,
599
- "embedding_provider": provider,
600
- "original_text": chunk_text,
601
- "use_contextual_embeddings": use_contextual,
602
- "contextual_summary": context,
603
- **chunk_metadata
604
- }
605
-
606
- texts.append(contextualized_text)
607
- ids.append(chunk_id)
608
- metadatas.append(metadata)
609
- chunk_count += 1
610
-
611
- # Create embeddings in batch
612
- logging.info(f"Creating embeddings for {len(texts)} chunks")
613
- embeddings = create_embeddings_batch(texts, provider, model, api_url)
614
-
615
- # Store in Chroma
616
- store_in_chroma(collection_name, texts, embeddings, ids, metadatas)
617
-
618
- # Create a preview of the first embedding
619
- if isinstance(embeddings, np.ndarray) and embeddings.size > 0:
620
- embedding_preview = str(embeddings[0][:50])
621
- elif isinstance(embeddings, list) and len(embeddings) > 0:
622
- embedding_preview = str(embeddings[0][:50])
623
- else:
624
- embedding_preview = "No embeddings created"
625
-
626
- # Return status message
627
- status = f"New embeddings created and stored for item: {item['title']} (ID: {item_id})"
628
-
629
- # Add contextual summaries to status message if enabled
630
- if use_contextual:
631
- status += " (with contextual summaries)"
632
-
633
- # Return status message, embedding preview, and metadata
634
- return status, f"First 50 elements of new embedding:\n{embedding_preview}", json.dumps(metadatas[0],
635
- indent=2)
636
- except Exception as e:
637
- logging.error(f"Error in create_new_embedding_for_item: {str(e)}", exc_info=True)
638
- return f"Error creating embedding: {str(e)}", "", ""
639
-
640
- # Wire up all the event handlers
641
- database_selection.change(
642
- update_database_path,
643
- inputs=[database_selection],
644
- outputs=[current_db_path]
645
- )
646
-
647
- refresh_button.click(
648
- get_items_with_embedding_status,
649
- inputs=[database_selection],
650
- outputs=[item_dropdown, item_mapping]
651
- )
652
-
653
- item_dropdown.change(
654
- check_embedding_status,
655
- inputs=[item_dropdown, database_selection, item_mapping],
656
- outputs=[embedding_status, embedding_preview, embedding_metadata]
657
- )
658
-
659
- create_new_embedding_button.click(
660
- create_new_embedding_for_item,
661
- inputs=[item_dropdown, embedding_provider, huggingface_model, openai_model, custom_embedding_model, embedding_api_url,
662
- chunking_method, max_chunk_size, chunk_overlap, adaptive_chunking, item_mapping,
663
- use_contextual_embeddings, contextual_api_choice],
664
- outputs=[embedding_status, embedding_preview, embedding_metadata]
665
- )
666
- embedding_provider.change(
667
- update_provider_options,
668
- inputs=[embedding_provider],
669
- outputs=[huggingface_model, openai_model, custom_embedding_model, embedding_api_url]
670
- )
671
- huggingface_model.change(
672
- update_huggingface_options,
673
- inputs=[huggingface_model],
674
- outputs=[custom_embedding_model]
675
- )
676
-
677
- return (item_dropdown, refresh_button, embedding_status, embedding_preview, embedding_metadata,
678
- create_new_embedding_button, embedding_provider, huggingface_model, openai_model,
679
- custom_embedding_model, embedding_api_url, chunking_method, max_chunk_size,
680
- chunk_overlap, adaptive_chunking, use_contextual_embeddings,
681
- contextual_api_choice, contextual_api_key)
682
-
683
-
684
- def create_purge_embeddings_tab():
685
- with gr.TabItem("Purge Embeddings", visible=True):
686
- gr.Markdown("# Purge Embeddings")
687
-
688
- with gr.Row():
689
- with gr.Column():
690
- purge_button = gr.Button("Purge All Embeddings")
691
- with gr.Column():
692
- status_output = gr.Textbox(label="Status", lines=10)
693
-
694
- def purge_all_embeddings():
695
- try:
696
- # It came to me in a dream....I literally don't remember how the fuck this works, cant find documentation...
697
- collection_name = "all_content_embeddings"
698
- chroma_client.delete_collection(collection_name)
699
- chroma_client.create_collection(collection_name)
700
- logging.info(f"All embeddings have been purged successfully.")
701
- return "All embeddings have been purged successfully."
702
- except Exception as e:
703
- logging.error(f"Error during embedding purge: {str(e)}")
704
- return f"Error: {str(e)}"
705
-
706
- purge_button.click(
707
- fn=purge_all_embeddings,
708
- outputs=status_output
709
- )
710
-
711
-
712
-
713
- #
714
- # End of file
715
- ########################################################################################################################
 
1
+ # Embeddings_tabc.py
2
+ # Description: This file contains the code for the RAG Chat tab in the Gradio UI
3
+ #
4
+ # Imports
5
+ import json
6
+ import logging
7
+ import os
8
+ #
9
+ # External Imports
10
+ import gradio as gr
11
+ import numpy as np
12
+ from tqdm import tqdm
13
+ #
14
+ # Local Imports
15
+ from App_Function_Libraries.DB.DB_Manager import get_all_content_from_database, get_all_conversations, \
16
+ get_conversation_text, get_note_by_id
17
+ from App_Function_Libraries.DB.RAG_QA_Chat_DB import get_all_notes
18
+ from App_Function_Libraries.RAG.ChromaDB_Library import chroma_client, \
19
+ store_in_chroma, situate_context
20
+ from App_Function_Libraries.RAG.Embeddings_Create import create_embedding, create_embeddings_batch
21
+ from App_Function_Libraries.Chunk_Lib import improved_chunking_process, chunk_for_embedding
22
+ from App_Function_Libraries.Utils.Utils import load_and_log_configs
23
+
24
+
25
+ #
26
+ ########################################################################################################################
27
+ #
28
+ # Functions:
29
+
30
+ def create_embeddings_tab():
31
+ # Load configuration first
32
+ config = load_and_log_configs()
33
+ if not config:
34
+ raise ValueError("Could not load configuration")
35
+
36
+ # Get database paths from config
37
+ db_config = config['db_config']
38
+ media_db_path = 'Databases/media_summary.db'
39
+ character_chat_db_path = os.path.join(os.path.dirname(media_db_path), "chatDB.db")
40
+ rag_chat_db_path = os.path.join(os.path.dirname(media_db_path), "rag_qa.db")
41
+ chroma_db_path = "Databases/chroma.db"
42
+
43
+ with gr.TabItem("Create Embeddings", visible=True):
44
+ gr.Markdown("# Create Embeddings for All Content")
45
+
46
+ with gr.Row():
47
+ with gr.Column():
48
+ # Database selection at the top
49
+ database_selection = gr.Radio(
50
+ choices=["Media DB", "RAG Chat", "Character Chat"],
51
+ label="Select Content Source",
52
+ value="Media DB",
53
+ info="Choose which database to create embeddings from"
54
+ )
55
+
56
+ # Add database path display
57
+ current_db_path = gr.Textbox(
58
+ label="Current Database Path",
59
+ value=media_db_path,
60
+ interactive=False
61
+ )
62
+
63
+ embedding_provider = gr.Radio(
64
+ choices=["huggingface", "local", "openai"],
65
+ label="Select Embedding Provider",
66
+ value=config['embedding_config']['embedding_provider'] or "huggingface"
67
+ )
68
+ gr.Markdown("Note: Local provider requires a running Llama.cpp/llamafile server.")
69
+ gr.Markdown("OpenAI provider requires a valid API key.")
70
+
71
+ huggingface_model = gr.Dropdown(
72
+ choices=[
73
+ "jinaai/jina-embeddings-v3",
74
+ "Alibaba-NLP/gte-large-en-v1.5",
75
+ "dunzhang/setll_en_400M_v5",
76
+ "custom"
77
+ ],
78
+ label="Hugging Face Model",
79
+ value="jinaai/jina-embeddings-v3",
80
+ visible=True
81
+ )
82
+
83
+ openai_model = gr.Dropdown(
84
+ choices=[
85
+ "text-embedding-3-small",
86
+ "text-embedding-3-large"
87
+ ],
88
+ label="OpenAI Embedding Model",
89
+ value="text-embedding-3-small",
90
+ visible=False
91
+ )
92
+
93
+ custom_embedding_model = gr.Textbox(
94
+ label="Custom Embedding Model",
95
+ placeholder="Enter your custom embedding model name here",
96
+ visible=False
97
+ )
98
+
99
+ embedding_api_url = gr.Textbox(
100
+ label="API URL (for local provider)",
101
+ value=config['embedding_config']['embedding_api_url'],
102
+ visible=False
103
+ )
104
+
105
+ # Add chunking options with config defaults
106
+ chunking_method = gr.Dropdown(
107
+ choices=["words", "sentences", "paragraphs", "tokens", "semantic"],
108
+ label="Chunking Method",
109
+ value="words"
110
+ )
111
+ max_chunk_size = gr.Slider(
112
+ minimum=1, maximum=8000, step=1,
113
+ value=config['embedding_config']['chunk_size'],
114
+ label="Max Chunk Size"
115
+ )
116
+ chunk_overlap = gr.Slider(
117
+ minimum=0, maximum=4000, step=1,
118
+ value=config['embedding_config']['overlap'],
119
+ label="Chunk Overlap"
120
+ )
121
+ adaptive_chunking = gr.Checkbox(
122
+ label="Use Adaptive Chunking",
123
+ value=False
124
+ )
125
+
126
+ create_button = gr.Button("Create Embeddings")
127
+
128
+ with gr.Column():
129
+ status_output = gr.Textbox(label="Status", lines=10)
130
+ progress = gr.Progress()
131
+
132
+ def update_provider_options(provider):
133
+ if provider == "huggingface":
134
+ return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
135
+ elif provider == "local":
136
+ return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
137
+ else: # OpenAI
138
+ return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
139
+
140
+ def update_huggingface_options(model):
141
+ if model == "custom":
142
+ return gr.update(visible=True)
143
+ else:
144
+ return gr.update(visible=False)
145
+
146
+ def update_database_path(database_type):
147
+ if database_type == "Media DB":
148
+ return media_db_path
149
+ elif database_type == "RAG Chat":
150
+ return rag_qa_db_path
151
+ else: # Character Chat
152
+ return character_chat_db_path
153
+
154
+ def create_all_embeddings(provider, hf_model, openai_model, custom_model, api_url, method,
155
+ max_size, overlap, adaptive, database_type, progress=gr.Progress()):
156
+ try:
157
+ # Initialize content based on database selection
158
+ if database_type == "Media DB":
159
+ all_content = get_all_content_from_database()
160
+ content_type = "media"
161
+ elif database_type == "RAG Chat":
162
+ all_content = []
163
+ page = 1
164
+ while True:
165
+ conversations, total_pages, _ = get_all_conversations(page=page)
166
+ if not conversations:
167
+ break
168
+ all_content.extend([{
169
+ 'id': conv['conversation_id'],
170
+ 'content': get_conversation_text(conv['conversation_id']),
171
+ 'title': conv['title'],
172
+ 'type': 'conversation'
173
+ } for conv in conversations])
174
+ progress(page / total_pages, desc=f"Loading conversations... Page {page}/{total_pages}")
175
+ page += 1
176
+ else: # Character Chat
177
+ all_content = []
178
+ page = 1
179
+ while True:
180
+ notes, total_pages, _ = get_all_notes(page=page)
181
+ if not notes:
182
+ break
183
+ all_content.extend([{
184
+ 'id': note['id'],
185
+ 'content': f"{note['title']}\n\n{note['content']}",
186
+ 'conversation_id': note['conversation_id'],
187
+ 'type': 'note'
188
+ } for note in notes])
189
+ progress(page / total_pages, desc=f"Loading notes... Page {page}/{total_pages}")
190
+ page += 1
191
+
192
+ if not all_content:
193
+ return "No content found in the selected database."
194
+
195
+ chunk_options = {
196
+ 'method': method,
197
+ 'max_size': max_size,
198
+ 'overlap': overlap,
199
+ 'adaptive': adaptive
200
+ }
201
+
202
+ collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
203
+ collection = chroma_client.get_or_create_collection(name=collection_name)
204
+
205
+ # Determine the model to use
206
+ if provider == "huggingface":
207
+ model = custom_model if hf_model == "custom" else hf_model
208
+ elif provider == "openai":
209
+ model = openai_model
210
+ else:
211
+ model = api_url
212
+
213
+ total_items = len(all_content)
214
+ for idx, item in enumerate(all_content):
215
+ progress((idx + 1) / total_items, desc=f"Processing item {idx + 1} of {total_items}")
216
+
217
+ content_id = item['id']
218
+ text = item['content']
219
+
220
+ chunks = improved_chunking_process(text, chunk_options)
221
+ for chunk_idx, chunk in enumerate(chunks):
222
+ chunk_text = chunk['text']
223
+ chunk_id = f"{database_type.lower()}_{content_id}_chunk_{chunk_idx}"
224
+
225
+ try:
226
+ embedding = create_embedding(chunk_text, provider, model, api_url)
227
+ metadata = {
228
+ 'content_id': str(content_id),
229
+ 'chunk_index': int(chunk_idx),
230
+ 'total_chunks': int(len(chunks)),
231
+ 'chunking_method': method,
232
+ 'max_chunk_size': int(max_size),
233
+ 'chunk_overlap': int(overlap),
234
+ 'adaptive_chunking': bool(adaptive),
235
+ 'embedding_model': model,
236
+ 'embedding_provider': provider,
237
+ 'content_type': item.get('type', 'media'),
238
+ 'conversation_id': item.get('conversation_id'),
239
+ **{k: (int(v) if isinstance(v, str) and v.isdigit() else v)
240
+ for k, v in chunk['metadata'].items()}
241
+ }
242
+ store_in_chroma(collection_name, [chunk_text], [embedding], [chunk_id], [metadata])
243
+
244
+ except Exception as e:
245
+ logging.error(f"Error processing chunk {chunk_id}: {str(e)}")
246
+ continue
247
+
248
+ return f"Embeddings created and stored successfully for all {database_type} content."
249
+ except Exception as e:
250
+ logging.error(f"Error during embedding creation: {str(e)}")
251
+ return f"Error: {str(e)}"
252
+
253
+ # Event handlers
254
+ embedding_provider.change(
255
+ fn=update_provider_options,
256
+ inputs=[embedding_provider],
257
+ outputs=[huggingface_model, openai_model, custom_embedding_model, embedding_api_url]
258
+ )
259
+
260
+ huggingface_model.change(
261
+ fn=update_huggingface_options,
262
+ inputs=[huggingface_model],
263
+ outputs=[custom_embedding_model]
264
+ )
265
+
266
+ database_selection.change(
267
+ fn=update_database_path,
268
+ inputs=[database_selection],
269
+ outputs=[current_db_path]
270
+ )
271
+
272
+ create_button.click(
273
+ fn=create_all_embeddings,
274
+ inputs=[
275
+ embedding_provider, huggingface_model, openai_model, custom_embedding_model,
276
+ embedding_api_url, chunking_method, max_chunk_size, chunk_overlap,
277
+ adaptive_chunking, database_selection
278
+ ],
279
+ outputs=status_output
280
+ )
281
+
282
+
283
+ def create_view_embeddings_tab():
284
+ # Load configuration first
285
+ config = load_and_log_configs()
286
+ if not config:
287
+ raise ValueError("Could not load configuration")
288
+
289
+ # Get database paths from config
290
+ db_config = config['db_config']
291
+ media_db_path = db_config['sqlite_path']
292
+ rag_qa_db_path = os.path.join(os.path.dirname(media_db_path), "rag_chat.db")
293
+ character_chat_db_path = os.path.join(os.path.dirname(media_db_path), "character_chat.db")
294
+ chroma_db_path = db_config['chroma_db_path']
295
+
296
+ with gr.TabItem("View/Update Embeddings", visible=True):
297
+ gr.Markdown("# View and Update Embeddings")
298
+ # Initialize item_mapping as a Gradio State
299
+
300
+
301
+ with gr.Row():
302
+ with gr.Column():
303
+ # Add database selection
304
+ database_selection = gr.Radio(
305
+ choices=["Media DB", "RAG Chat", "Character Chat"],
306
+ label="Select Content Source",
307
+ value="Media DB",
308
+ info="Choose which database to view embeddings from"
309
+ )
310
+
311
+ # Add database path display
312
+ current_db_path = gr.Textbox(
313
+ label="Current Database Path",
314
+ value=media_db_path,
315
+ interactive=False
316
+ )
317
+
318
+ item_dropdown = gr.Dropdown(label="Select Item", choices=[], interactive=True)
319
+ refresh_button = gr.Button("Refresh Item List")
320
+ embedding_status = gr.Textbox(label="Embedding Status", interactive=False)
321
+ embedding_preview = gr.Textbox(label="Embedding Preview", interactive=False, lines=5)
322
+ embedding_metadata = gr.Textbox(label="Embedding Metadata", interactive=False, lines=10)
323
+
324
+ with gr.Column():
325
+ create_new_embedding_button = gr.Button("Create New Embedding")
326
+ embedding_provider = gr.Radio(
327
+ choices=["huggingface", "local", "openai"],
328
+ label="Select Embedding Provider",
329
+ value="huggingface"
330
+ )
331
+ gr.Markdown("Note: Local provider requires a running Llama.cpp/llamafile server.")
332
+ gr.Markdown("OpenAI provider requires a valid API key.")
333
+
334
+ huggingface_model = gr.Dropdown(
335
+ choices=[
336
+ "jinaai/jina-embeddings-v3",
337
+ "Alibaba-NLP/gte-large-en-v1.5",
338
+ "dunzhang/stella_en_400M_v5",
339
+ "custom"
340
+ ],
341
+ label="Hugging Face Model",
342
+ value="jinaai/jina-embeddings-v3",
343
+ visible=True
344
+ )
345
+
346
+ openai_model = gr.Dropdown(
347
+ choices=[
348
+ "text-embedding-3-small",
349
+ "text-embedding-3-large"
350
+ ],
351
+ label="OpenAI Embedding Model",
352
+ value="text-embedding-3-small",
353
+ visible=False
354
+ )
355
+
356
+ custom_embedding_model = gr.Textbox(
357
+ label="Custom Embedding Model",
358
+ placeholder="Enter your custom embedding model name here",
359
+ visible=False
360
+ )
361
+
362
+ embedding_api_url = gr.Textbox(
363
+ label="API URL (for local provider)",
364
+ value=config['embedding_config']['embedding_api_url'],
365
+ visible=False
366
+ )
367
+
368
+ chunking_method = gr.Dropdown(
369
+ choices=["words", "sentences", "paragraphs", "tokens", "semantic"],
370
+ label="Chunking Method",
371
+ value="words"
372
+ )
373
+ max_chunk_size = gr.Slider(
374
+ minimum=1, maximum=8000, step=5, value=500,
375
+ label="Max Chunk Size"
376
+ )
377
+ chunk_overlap = gr.Slider(
378
+ minimum=0, maximum=5000, step=5, value=200,
379
+ label="Chunk Overlap"
380
+ )
381
+ adaptive_chunking = gr.Checkbox(
382
+ label="Use Adaptive Chunking",
383
+ value=False
384
+ )
385
+ contextual_api_choice = gr.Dropdown(
386
+ choices=["Local-LLM", "OpenAI", "Anthropic", "Cohere", "Groq", "DeepSeek", "Mistral", "OpenRouter", "Llama.cpp", "Kobold", "Ooba", "Tabbyapi", "VLLM", "ollama", "HuggingFace"],
387
+ label="Select API for Contextualized Embeddings",
388
+ value="OpenAI"
389
+ )
390
+ use_contextual_embeddings = gr.Checkbox(
391
+ label="Use Contextual Embeddings",
392
+ value=True
393
+ )
394
+ contextual_api_key = gr.Textbox(label="API Key", lines=1)
395
+
396
+ item_mapping = gr.State(value={})
397
+
398
+ def update_database_path(database_type):
399
+ if database_type == "Media DB":
400
+ return media_db_path
401
+ elif database_type == "RAG Chat":
402
+ return rag_qa_db_path
403
+ else: # Character Chat
404
+ return character_chat_db_path
405
+
406
+ def get_items_with_embedding_status(database_type):
407
+ try:
408
+ # Get items based on database selection
409
+ if database_type == "Media DB":
410
+ items = get_all_content_from_database()
411
+ elif database_type == "RAG Chat":
412
+ conversations, _, _ = get_all_conversations(page=1)
413
+ items = [{
414
+ 'id': conv['conversation_id'],
415
+ 'title': conv['title'],
416
+ 'type': 'conversation'
417
+ } for conv in conversations]
418
+ else: # Character Chat
419
+ notes, _, _ = get_all_notes(page=1)
420
+ items = [{
421
+ 'id': note['id'],
422
+ 'title': note['title'],
423
+ 'type': 'note'
424
+ } for note in notes]
425
+
426
+ collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
427
+ collection = chroma_client.get_or_create_collection(name=collection_name)
428
+
429
+ choices = []
430
+ new_item_mapping = {}
431
+ for item in items:
432
+ try:
433
+ chunk_id = f"{database_type.lower()}_{item['id']}_chunk_0"
434
+ result = collection.get(ids=[chunk_id])
435
+ embedding_exists = result is not None and result.get('ids') and len(result['ids']) > 0
436
+ status = "Embedding exists" if embedding_exists else "No embedding"
437
+ except Exception as e:
438
+ print(f"Error checking embedding for item {item['id']}: {str(e)}")
439
+ status = "Error checking"
440
+ choice = f"{item['title']} ({status})"
441
+ choices.append(choice)
442
+ new_item_mapping[choice] = item['id']
443
+ return gr.update(choices=choices), new_item_mapping
444
+ except Exception as e:
445
+ print(f"Error in get_items_with_embedding_status: {str(e)}")
446
+ return gr.update(choices=["Error: Unable to fetch items"]), {}
447
+
448
+ def update_provider_options(provider):
449
+ if provider == "huggingface":
450
+ return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
451
+ elif provider == "local":
452
+ return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
453
+ else: # OpenAI
454
+ return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
455
+
456
+ def update_huggingface_options(model):
457
+ if model == "custom":
458
+ return gr.update(visible=True)
459
+ else:
460
+ return gr.update(visible=False)
461
+
462
+ def check_embedding_status(selected_item, database_type, item_mapping):
463
+ if not selected_item:
464
+ return "Please select an item", "", ""
465
+
466
+ if item_mapping is None:
467
+ # If mapping is None, try to refresh it
468
+ try:
469
+ _, item_mapping = get_items_with_embedding_status(database_type)
470
+ except Exception as e:
471
+ return f"Error initializing item mapping: {str(e)}", "", ""
472
+
473
+ try:
474
+ item_id = item_mapping.get(selected_item)
475
+ if item_id is None:
476
+ return f"Invalid item selected: {selected_item}", "", ""
477
+
478
+ item_title = selected_item.rsplit(' (', 1)[0]
479
+ collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
480
+ collection = chroma_client.get_or_create_collection(name=collection_name)
481
+ chunk_id = f"{database_type.lower()}_{item_id}_chunk_0"
482
+
483
+ try:
484
+ result = collection.get(ids=[chunk_id], include=["embeddings", "metadatas"])
485
+ except Exception as e:
486
+ logging.error(f"ChromaDB get error: {str(e)}")
487
+ return f"Error retrieving embedding for '{item_title}': {str(e)}", "", ""
488
+
489
+ # Check if result exists and has the expected structure
490
+ if not result or not isinstance(result, dict):
491
+ return f"No embedding found for item '{item_title}' (ID: {item_id})", "", ""
492
+
493
+ # Check if we have any results
494
+ if not result.get('ids') or len(result['ids']) == 0:
495
+ return f"No embedding found for item '{item_title}' (ID: {item_id})", "", ""
496
+
497
+ # Check if embeddings exist
498
+ if not result.get('embeddings') or not result['embeddings'][0]:
499
+ return f"Embedding data missing for item '{item_title}' (ID: {item_id})", "", ""
500
+
501
+ embedding = result['embeddings'][0]
502
+ metadata = result.get('metadatas', [{}])[0] if result.get('metadatas') else {}
503
+ embedding_preview = str(embedding[:50])
504
+ status = f"Embedding exists for item '{item_title}' (ID: {item_id})"
505
+ return status, f"First 50 elements of embedding:\n{embedding_preview}", json.dumps(metadata, indent=2)
506
+
507
+ except Exception as e:
508
+ logging.error(f"Error in check_embedding_status: {str(e)}", exc_info=True)
509
+ return f"Error processing item: {selected_item}. Details: {str(e)}", "", ""
510
+
511
+ def refresh_and_update(database_type):
512
+ choices_update, new_mapping = get_items_with_embedding_status(database_type)
513
+ return choices_update, new_mapping
514
+
515
+ def create_new_embedding_for_item(selected_item, database_type, provider, hf_model, openai_model,
516
+ custom_model, api_url, method, max_size, overlap, adaptive,
517
+ item_mapping, use_contextual, contextual_api_choice=None):
518
+ if not selected_item:
519
+ return "Please select an item", "", ""
520
+
521
+ try:
522
+ item_id = item_mapping.get(selected_item)
523
+ if item_id is None:
524
+ return f"Invalid item selected: {selected_item}", "", ""
525
+
526
+ # Get item content based on database type
527
+ if database_type == "Media DB":
528
+ items = get_all_content_from_database()
529
+ item = next((item for item in items if item['id'] == item_id), None)
530
+ elif database_type == "RAG Chat":
531
+ item = {
532
+ 'id': item_id,
533
+ 'content': get_conversation_text(item_id),
534
+ 'title': selected_item.rsplit(' (', 1)[0],
535
+ 'type': 'conversation'
536
+ }
537
+ else: # Character Chat
538
+ note = get_note_by_id(item_id)
539
+ item = {
540
+ 'id': item_id,
541
+ 'content': f"{note['title']}\n\n{note['content']}",
542
+ 'title': note['title'],
543
+ 'type': 'note'
544
+ }
545
+
546
+ if not item:
547
+ return f"Item not found: {item_id}", "", ""
548
+
549
+ chunk_options = {
550
+ 'method': method,
551
+ 'max_size': max_size,
552
+ 'overlap': overlap,
553
+ 'adaptive': adaptive
554
+ }
555
+
556
+ logging.info(f"Chunking content for item: {item['title']} (ID: {item_id})")
557
+ chunks = chunk_for_embedding(item['content'], item['title'], chunk_options)
558
+ collection_name = f"{database_type.lower().replace(' ', '_')}_embeddings"
559
+ collection = chroma_client.get_or_create_collection(name=collection_name)
560
+
561
+ # Delete existing embeddings for this item
562
+ existing_ids = [f"{database_type.lower()}_{item_id}_chunk_{i}" for i in range(len(chunks))]
563
+ collection.delete(ids=existing_ids)
564
+ logging.info(f"Deleted {len(existing_ids)} existing embeddings for item {item_id}")
565
+
566
+ texts, ids, metadatas = [], [], []
567
+ chunk_count = 0
568
+ logging.info("Generating contextual summaries and preparing chunks for embedding")
569
+ for i, chunk in enumerate(chunks):
570
+ chunk_text = chunk['text']
571
+ chunk_metadata = chunk['metadata']
572
+ if use_contextual:
573
+ logging.debug(f"Generating contextual summary for chunk {chunk_count}")
574
+ context = situate_context(contextual_api_choice, item['content'], chunk_text)
575
+ contextualized_text = f"{chunk_text}\n\nContextual Summary: {context}"
576
+ else:
577
+ contextualized_text = chunk_text
578
+ context = None
579
+
580
+ chunk_id = f"{database_type.lower()}_{item_id}_chunk_{i}"
581
+
582
+ # Determine the model to use
583
+ if provider == "huggingface":
584
+ model = custom_model if hf_model == "custom" else hf_model
585
+ elif provider == "openai":
586
+ model = openai_model
587
+ else:
588
+ model = custom_model
589
+
590
+ metadata = {
591
+ "content_id": str(item_id),
592
+ "chunk_index": i,
593
+ "total_chunks": len(chunks),
594
+ "chunking_method": method,
595
+ "max_chunk_size": max_size,
596
+ "chunk_overlap": overlap,
597
+ "adaptive_chunking": adaptive,
598
+ "embedding_model": model,
599
+ "embedding_provider": provider,
600
+ "original_text": chunk_text,
601
+ "use_contextual_embeddings": use_contextual,
602
+ "contextual_summary": context,
603
+ **chunk_metadata
604
+ }
605
+
606
+ texts.append(contextualized_text)
607
+ ids.append(chunk_id)
608
+ metadatas.append(metadata)
609
+ chunk_count += 1
610
+
611
+ # Create embeddings in batch
612
+ logging.info(f"Creating embeddings for {len(texts)} chunks")
613
+ embeddings = create_embeddings_batch(texts, provider, model, api_url)
614
+
615
+ # Store in Chroma
616
+ store_in_chroma(collection_name, texts, embeddings, ids, metadatas)
617
+
618
+ # Create a preview of the first embedding
619
+ if isinstance(embeddings, np.ndarray) and embeddings.size > 0:
620
+ embedding_preview = str(embeddings[0][:50])
621
+ elif isinstance(embeddings, list) and len(embeddings) > 0:
622
+ embedding_preview = str(embeddings[0][:50])
623
+ else:
624
+ embedding_preview = "No embeddings created"
625
+
626
+ # Return status message
627
+ status = f"New embeddings created and stored for item: {item['title']} (ID: {item_id})"
628
+
629
+ # Add contextual summaries to status message if enabled
630
+ if use_contextual:
631
+ status += " (with contextual summaries)"
632
+
633
+ # Return status message, embedding preview, and metadata
634
+ return status, f"First 50 elements of new embedding:\n{embedding_preview}", json.dumps(metadatas[0],
635
+ indent=2)
636
+ except Exception as e:
637
+ logging.error(f"Error in create_new_embedding_for_item: {str(e)}", exc_info=True)
638
+ return f"Error creating embedding: {str(e)}", "", ""
639
+
640
+ # Wire up all the event handlers
641
+ database_selection.change(
642
+ update_database_path,
643
+ inputs=[database_selection],
644
+ outputs=[current_db_path]
645
+ )
646
+
647
+ refresh_button.click(
648
+ get_items_with_embedding_status,
649
+ inputs=[database_selection],
650
+ outputs=[item_dropdown, item_mapping]
651
+ )
652
+
653
+ item_dropdown.change(
654
+ check_embedding_status,
655
+ inputs=[item_dropdown, database_selection, item_mapping],
656
+ outputs=[embedding_status, embedding_preview, embedding_metadata]
657
+ )
658
+
659
+ create_new_embedding_button.click(
660
+ create_new_embedding_for_item,
661
+ inputs=[item_dropdown, embedding_provider, huggingface_model, openai_model, custom_embedding_model, embedding_api_url,
662
+ chunking_method, max_chunk_size, chunk_overlap, adaptive_chunking, item_mapping,
663
+ use_contextual_embeddings, contextual_api_choice],
664
+ outputs=[embedding_status, embedding_preview, embedding_metadata]
665
+ )
666
+ embedding_provider.change(
667
+ update_provider_options,
668
+ inputs=[embedding_provider],
669
+ outputs=[huggingface_model, openai_model, custom_embedding_model, embedding_api_url]
670
+ )
671
+ huggingface_model.change(
672
+ update_huggingface_options,
673
+ inputs=[huggingface_model],
674
+ outputs=[custom_embedding_model]
675
+ )
676
+
677
+ return (item_dropdown, refresh_button, embedding_status, embedding_preview, embedding_metadata,
678
+ create_new_embedding_button, embedding_provider, huggingface_model, openai_model,
679
+ custom_embedding_model, embedding_api_url, chunking_method, max_chunk_size,
680
+ chunk_overlap, adaptive_chunking, use_contextual_embeddings,
681
+ contextual_api_choice, contextual_api_key)
682
+
683
+
684
+ def create_purge_embeddings_tab():
685
+ with gr.TabItem("Purge Embeddings", visible=True):
686
+ gr.Markdown("# Purge Embeddings")
687
+
688
+ with gr.Row():
689
+ with gr.Column():
690
+ purge_button = gr.Button("Purge All Embeddings")
691
+ with gr.Column():
692
+ status_output = gr.Textbox(label="Status", lines=10)
693
+
694
+ def purge_all_embeddings():
695
+ try:
696
+ # It came to me in a dream....I literally don't remember how the fuck this works, cant find documentation...
697
+ collection_name = "all_content_embeddings"
698
+ chroma_client.delete_collection(collection_name)
699
+ chroma_client.create_collection(collection_name)
700
+ logging.info(f"All embeddings have been purged successfully.")
701
+ return "All embeddings have been purged successfully."
702
+ except Exception as e:
703
+ logging.error(f"Error during embedding purge: {str(e)}")
704
+ return f"Error: {str(e)}"
705
+
706
+ purge_button.click(
707
+ fn=purge_all_embeddings,
708
+ outputs=status_output
709
+ )
710
+
711
+
712
+
713
+ #
714
+ # End of file
715
+ ########################################################################################################################