awacke1 commited on
Commit
e3b615c
Β·
verified Β·
1 Parent(s): c433764

Create app16.py

Browse files
Files changed (1) hide show
  1. app16.py +957 -0
app16.py ADDED
@@ -0,0 +1,957 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ # =============================================================================
3
+ # ───────────── IMPORTS ─────────────
4
+ # =============================================================================
5
+ import base64
6
+ import glob
7
+ import hashlib
8
+ import json
9
+ import os
10
+ import pandas as pd
11
+ import pytz
12
+ import random
13
+ import re
14
+ import shutil
15
+ import streamlit as st
16
+ import time
17
+ import traceback
18
+ import uuid
19
+ import zipfile
20
+ from PIL import Image
21
+ from azure.cosmos import CosmosClient, PartitionKey, exceptions
22
+ from datetime import datetime
23
+ from git import Repo
24
+ from github import Github
25
+ from gradio_client import Client, handle_file
26
+ import tempfile
27
+ import io
28
+ import requests
29
+ import numpy as np
30
+ from urllib.parse import quote
31
+
32
+ # =============================================================================
33
+ # ───────────── EXTERNAL HELP LINKS (Always visible in sidebar) ─────────────
34
+ # =============================================================================
35
+ external_links = [
36
+ {"title": "MergeKit Official GitHub", "url": "https://github.com/arcee-ai/MergeKit", "emoji": "πŸ’»"},
37
+ {"title": "MergeKit arXiv Paper", "url": "https://arxiv.org/abs/xxxx.xxxxx", "emoji": "πŸ“˜"},
38
+ {"title": "MergeKit Tutorial", "url": "https://huggingface.co/blog/mergekit-tutorial", "emoji": "✍️"},
39
+ {"title": "MergeKit Sample Usage", "url": "https://github.com/arcee-ai/MergeKit#examples", "emoji": "πŸ“š"},
40
+ {"title": "DistillKit Official GitHub", "url": "https://github.com/arcee-ai/DistillKit", "emoji": "πŸ’»"},
41
+ {"title": "DistillKit Announcing Blog Post", "url": "https://arcee.ai/blog/distillkit-announcement", "emoji": "✍️"},
42
+ {"title": "DistillKit Sample Usage", "url": "https://github.com/arcee-ai/DistillKit#usage", "emoji": "πŸ“š"},
43
+ {"title": "Spectrum Hugging Face Blog Post", "url": "https://huggingface.co/blog/spectrum", "emoji": "✍️"},
44
+ {"title": "Hugging Face Model Merging Docs", "url": "https://huggingface.co/docs/peft/model_merging", "emoji": "πŸ“š"},
45
+ {"title": "arcee.ai Official Website", "url": "https://arcee.ai", "emoji": "🌐"},
46
+ ]
47
+
48
+ # =============================================================================
49
+ # ───────────── APP CONFIGURATION ─────────────
50
+ # =============================================================================
51
+ Site_Name = 'πŸ™ GitCosmos'
52
+ title = "πŸ™ GitCosmos"
53
+ helpURL = 'https://huggingface.co/awacke1'
54
+ bugURL = 'https://huggingface.co/spaces/awacke1/AzureCosmosDBUI/'
55
+ icons = 'πŸ™πŸŒŒπŸ’«'
56
+ st.set_page_config(
57
+ page_title=title,
58
+ page_icon=icons,
59
+ layout="wide",
60
+ initial_sidebar_state="auto",
61
+ menu_items={
62
+ 'Get Help': helpURL,
63
+ 'Report a bug': bugURL,
64
+ 'About': title
65
+ }
66
+ )
67
+
68
+ # Cosmos DB & App URLs
69
+ ENDPOINT = "https://acae-afd.documents.azure.com:443/"
70
+ DATABASE_NAME = os.environ.get("COSMOS_DATABASE_NAME")
71
+ CONTAINER_NAME = os.environ.get("COSMOS_CONTAINER_NAME")
72
+ Key = os.environ.get("Key")
73
+ LOCAL_APP_URL = "https://huggingface.co/spaces/awacke1/AzureCosmosDBUI"
74
+ CosmosDBUrl = 'https://portal.azure.com/#@AaronCWackergmail.onmicrosoft.com/resource/subscriptions/003fba60-5b3f-48f4-ab36-3ed11bc40816/resourceGroups/datasets/providers/Microsoft.DocumentDB/databaseAccounts/acae-afd/dataExplorer'
75
+
76
+ # =============================================================================
77
+ # ───────────── HELPER FUNCTIONS ─────────────
78
+ # =============================================================================
79
+ def get_download_link(file_path):
80
+ with open(file_path, "rb") as file:
81
+ contents = file.read()
82
+ b64 = base64.b64encode(contents).decode()
83
+ file_name = os.path.basename(file_path)
84
+ return f'<a href="data:file/txt;base64,{b64}" download="{file_name}">Download {file_name} πŸ“‚</a>'
85
+
86
+ def generate_unique_id():
87
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
88
+ unique_uuid = str(uuid.uuid4())
89
+ return_value = f"{timestamp}-{unique_uuid}"
90
+ st.write('New ID: ' + return_value)
91
+ return return_value
92
+
93
+ def generate_filename(prompt, file_type):
94
+ central = pytz.timezone('US/Central')
95
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
96
+ safe_prompt = re.sub(r'\W+', '', prompt)[:90]
97
+ return f"{safe_date_time}{safe_prompt}.{file_type}"
98
+
99
+ def create_file(filename, prompt, response, should_save=True):
100
+ if not should_save:
101
+ return
102
+ with open(filename, 'w', encoding='utf-8') as file:
103
+ file.write(prompt + "\n\n" + response)
104
+
105
+ def load_file(file_name):
106
+ with open(file_name, "r", encoding='utf-8') as file:
107
+ content = file.read()
108
+ return content
109
+
110
+ def display_glossary_entity(k):
111
+ search_urls = {
112
+ "πŸš€": lambda k: f"/?q={k}",
113
+ "πŸ“–": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
114
+ "πŸ”": lambda k: f"https://www.google.com/search?q={quote(k)}",
115
+ "πŸŽ₯": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
116
+ }
117
+ links_md = ' '.join([f"<a href='{url(k)}' target='_blank'>{emoji}</a>" for emoji, url in search_urls.items()])
118
+ st.markdown(f"{k} {links_md}", unsafe_allow_html=True)
119
+
120
+ def create_zip_of_files(files):
121
+ zip_name = "all_files.zip"
122
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
123
+ for file in files:
124
+ zipf.write(file)
125
+ return zip_name
126
+
127
+ def get_video_html(video_path, width="100%"):
128
+ video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
129
+ return f'''
130
+ <video width="{width}" controls autoplay loop>
131
+ <source src="{video_url}" type="video/mp4">
132
+ Your browser does not support video.
133
+ </video>
134
+ '''
135
+
136
+ def get_audio_html(audio_path, width="100%"):
137
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
138
+ return f'''
139
+ <audio controls style="width:{width}">
140
+ <source src="{audio_url}" type="audio/mpeg">
141
+ Your browser does not support audio.
142
+ </audio>
143
+ '''
144
+
145
+ def preprocess_text(text):
146
+ text = text.replace('\r\n', '\\n').replace('\r', '\\n').replace('\n', '\\n')
147
+ text = text.replace('"', '\\"')
148
+ text = re.sub(r'[\t]', ' ', text)
149
+ text = re.sub(r'[^\x00-\x7F]+', '', text)
150
+ return text.strip()
151
+
152
+ # =============================================================================
153
+ # ───────────── COSMOS DB FUNCTIONS ─────────────
154
+ # =============================================================================
155
+ def get_databases(client):
156
+ return [db['id'] for db in client.list_databases()]
157
+
158
+ def get_containers(database):
159
+ return [container['id'] for container in database.list_containers()]
160
+
161
+ def get_documents(container, limit=None):
162
+ query = "SELECT * FROM c ORDER BY c._ts DESC"
163
+ items = list(container.query_items(query=query, enable_cross_partition_query=True, max_item_count=limit))
164
+ return items
165
+
166
+ def insert_record(container, record):
167
+ try:
168
+ container.create_item(body=record)
169
+ return True, "Inserted! πŸŽ‰"
170
+ except exceptions.CosmosHttpResponseError as e:
171
+ return False, f"HTTP error: {str(e)} 🚨"
172
+ except Exception as e:
173
+ return False, f"Error: {str(e)} 😱"
174
+
175
+ def update_record(container, updated_record):
176
+ try:
177
+ container.upsert_item(body=updated_record)
178
+ return True, f"Updated {updated_record['id']} πŸ› οΈ"
179
+ except exceptions.CosmosHttpResponseError as e:
180
+ return False, f"HTTP error: {str(e)} 🚨"
181
+ except Exception as e:
182
+ return False, f"Error: {traceback.format_exc()} 😱"
183
+
184
+ def delete_record(container, record):
185
+ try:
186
+ if "id" not in record:
187
+ return False, "Record must contain an 'id' field. πŸ›‘"
188
+ doc_id = record["id"]
189
+ if "delete_log" not in st.session_state:
190
+ st.session_state.delete_log = []
191
+ st.session_state.delete_log.append(f"Attempting to delete document: {json.dumps(record, indent=2)}")
192
+ partition_key_value = record.get("pk", doc_id)
193
+ st.session_state.delete_log.append(f"Using ID and Partition Key: {partition_key_value}")
194
+ container.delete_item(item=doc_id, partition_key=partition_key_value)
195
+ success_msg = f"Record {doc_id} successfully deleted from Cosmos DB. πŸ—‘οΈ"
196
+ st.session_state.delete_log.append(success_msg)
197
+ return True, success_msg
198
+ except exceptions.CosmosResourceNotFoundError:
199
+ success_msg = f"Record {doc_id} not found in Cosmos DB (already deleted or never existed). πŸ—‘οΈ"
200
+ st.session_state.delete_log.append(success_msg)
201
+ return True, success_msg
202
+ except exceptions.CosmosHttpResponseError as e:
203
+ error_msg = f"HTTP error deleting {doc_id}: {str(e)}. 🚨"
204
+ st.session_state.delete_log.append(error_msg)
205
+ return False, error_msg
206
+ except Exception as e:
207
+ error_msg = f"Unexpected error deleting {doc_id}: {str(traceback.format_exc())}. 😱"
208
+ st.session_state.delete_log.append(error_msg)
209
+ return False, error_msg
210
+
211
+ def save_to_cosmos_db(container, query, response1, response2):
212
+ try:
213
+ if container:
214
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
215
+ unique_uuid = str(uuid.uuid4())
216
+ new_id = f"{timestamp}-{unique_uuid}"
217
+ record = {
218
+ "id": new_id,
219
+ "pk": new_id,
220
+ "name": new_id,
221
+ "query": query,
222
+ "response1": response1,
223
+ "response2": response2,
224
+ "timestamp": datetime.utcnow().isoformat(),
225
+ "type": "ai_response",
226
+ "version": "1.0"
227
+ }
228
+ container.create_item(body=record)
229
+ st.success(f"Saved: {record['id']}")
230
+ st.session_state.documents = get_documents(container)
231
+ else:
232
+ st.error("Cosmos container not initialized.")
233
+ except Exception as e:
234
+ st.error(f"Save error: {str(e)}")
235
+
236
+ def archive_current_container(database_name, container_name, client):
237
+ try:
238
+ base_dir = "./cosmos_archive_current_container"
239
+ if os.path.exists(base_dir):
240
+ shutil.rmtree(base_dir)
241
+ os.makedirs(base_dir)
242
+ db_client = client.get_database_client(database_name)
243
+ container_client = db_client.get_container_client(container_name)
244
+ items = list(container_client.read_all_items())
245
+ container_dir = os.path.join(base_dir, container_name)
246
+ os.makedirs(container_dir)
247
+ for item in items:
248
+ item_id = item.get('id', f"unknown_{datetime.now().strftime('%Y%m%d%H%M%S')}")
249
+ with open(os.path.join(container_dir, f"{item_id}.json"), 'w') as f:
250
+ json.dump(item, f, indent=2)
251
+ archive_name = f"{container_name}_archive_{datetime.now().strftime('%Y%m%d%H%M%S')}"
252
+ shutil.make_archive(archive_name, 'zip', base_dir)
253
+ return get_download_link(f"{archive_name}.zip")
254
+ except Exception as e:
255
+ return f"Archive error: {str(e)} 😒"
256
+
257
+ # =============================================================================
258
+ # ───────────── ADVANCED COSMOS FUNCTIONS ─────────────
259
+ # =============================================================================
260
+ def create_new_container(database, container_id, partition_key_path,
261
+ analytical_storage_ttl=None, indexing_policy=None, vector_embedding_policy=None):
262
+ try:
263
+ if analytical_storage_ttl is not None:
264
+ container = database.create_container(
265
+ id=container_id,
266
+ partition_key=PartitionKey(path=partition_key_path),
267
+ analytical_storage_ttl=analytical_storage_ttl,
268
+ indexing_policy=indexing_policy,
269
+ vector_embedding_policy=vector_embedding_policy
270
+ )
271
+ else:
272
+ container = database.create_container(
273
+ id=container_id,
274
+ partition_key=PartitionKey(path=partition_key_path),
275
+ indexing_policy=indexing_policy,
276
+ vector_embedding_policy=vector_embedding_policy
277
+ )
278
+ except exceptions.CosmosHttpResponseError as e:
279
+ if analytical_storage_ttl is not None and "analyticalStorageTtl" in str(e):
280
+ try:
281
+ container = database.create_container(
282
+ id=container_id,
283
+ partition_key=PartitionKey(path=partition_key_path),
284
+ indexing_policy=indexing_policy,
285
+ vector_embedding_policy=vector_embedding_policy
286
+ )
287
+ except Exception as e2:
288
+ st.error(f"Error creating container without analytical_storage_ttl: {str(e2)}")
289
+ return None
290
+ elif isinstance(e, exceptions.CosmosResourceExistsError):
291
+ container = database.get_container_client(container_id)
292
+ else:
293
+ st.error(f"Error creating container: {str(e)}")
294
+ return None
295
+ return container
296
+
297
+ def advanced_insert_item(container, item):
298
+ try:
299
+ container.upsert_item(item)
300
+ return True, f"Item {item.get('id', '')} inserted. βž•"
301
+ except Exception as e:
302
+ return False, str(e)
303
+
304
+ def advanced_update_item(container, item):
305
+ try:
306
+ container.upsert_item(item)
307
+ return True, f"Item {item.get('id', '')} updated. ✏️"
308
+ except Exception as e:
309
+ return False, str(e)
310
+
311
+ def advanced_delete_item(container, item_id, partition_key_value):
312
+ try:
313
+ container.delete_item(item=item_id, partition_key=partition_key_value)
314
+ return True, f"Item {item_id} deleted. πŸ—‘οΈ"
315
+ except Exception as e:
316
+ return False, str(e)
317
+
318
+ def vector_search(container, query_vector, vector_field, top=10, exact_search=False):
319
+ query_vector_str = json.dumps(query_vector)
320
+ query = f"""SELECT TOP {top} c.id, VectorDistance(c.{vector_field}, {query_vector_str}, {str(exact_search).lower()},
321
+ {{'dataType':'float32','distanceFunction':'cosine'}}) AS SimilarityScore
322
+ FROM c ORDER BY SimilarityScore"""
323
+ results = list(container.query_items(query=query, enable_cross_partition_query=True))
324
+ return results
325
+
326
+ # =============================================================================
327
+ # ───────────── GITHUB FUNCTIONS ─────────────
328
+ # =============================================================================
329
+ def download_github_repo(url, local_path):
330
+ if os.path.exists(local_path):
331
+ shutil.rmtree(local_path)
332
+ Repo.clone_from(url, local_path)
333
+
334
+ def create_zip_file(source_dir, output_filename):
335
+ shutil.make_archive(output_filename, 'zip', source_dir)
336
+
337
+ def create_repo(g, repo_name):
338
+ user = g.get_user()
339
+ return user.create_repo(repo_name)
340
+
341
+ def push_to_github(local_path, repo, github_token):
342
+ repo_url = f"https://{github_token}@github.com/{repo.full_name}.git"
343
+ local_repo = Repo(local_path)
344
+ if 'origin' in [remote.name for remote in local_repo.remotes]:
345
+ origin = local_repo.remote('origin')
346
+ origin.set_url(repo_url)
347
+ else:
348
+ origin = local_repo.create_remote('origin', repo_url)
349
+ if not local_repo.heads:
350
+ local_repo.git.checkout('-b', 'main')
351
+ current_branch = 'main'
352
+ else:
353
+ current_branch = local_repo.active_branch.name
354
+ local_repo.git.add(A=True)
355
+ if local_repo.is_dirty():
356
+ local_repo.git.commit('-m', 'Initial commit')
357
+ origin.push(refspec=f'{current_branch}:{current_branch}')
358
+
359
+ # =============================================================================
360
+ # ───────────── FILE & MEDIA MANAGEMENT FUNCTIONS ─────────────
361
+ # =============================================================================
362
+ def display_saved_files_in_sidebar():
363
+ all_files = sorted([f for f in glob.glob("*.md") if not f.lower().startswith('readme')], reverse=True)
364
+ st.sidebar.markdown("## πŸ“ Files")
365
+ for file in all_files:
366
+ col1, col2, col3 = st.sidebar.columns([6, 2, 1])
367
+ with col1:
368
+ st.markdown(f"πŸ“„ {file}")
369
+ with col2:
370
+ st.sidebar.download_button(
371
+ label="⬇️",
372
+ data=open(file, 'rb').read(),
373
+ file_name=file
374
+ )
375
+ with col3:
376
+ if st.sidebar.button("πŸ—‘", key=f"delete_{file}"):
377
+ os.remove(file)
378
+ st.rerun()
379
+
380
+ def display_file_viewer(file_path):
381
+ content = load_file(file_path)
382
+ if content:
383
+ st.markdown("### πŸ“„ File Viewer")
384
+ st.markdown(f"**{file_path}**")
385
+ file_stats = os.stat(file_path)
386
+ st.markdown(f"**Mod:** {datetime.fromtimestamp(file_stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')} | **Size:** {file_stats.st_size} bytes")
387
+ st.markdown("---")
388
+ st.markdown(content)
389
+ st.download_button("⬇️", data=content, file_name=os.path.basename(file_path), mime="text/markdown")
390
+
391
+ def display_file_editor(file_path):
392
+ if 'file_content' not in st.session_state:
393
+ st.session_state.file_content = {}
394
+ if file_path not in st.session_state.file_content:
395
+ content = load_file(file_path)
396
+ if content is not None:
397
+ st.session_state.file_content[file_path] = content
398
+ else:
399
+ return
400
+ st.markdown("### ✏️ Edit File")
401
+ st.markdown(f"**Editing:** {file_path}")
402
+ md_tab, code_tab = st.tabs(["Markdown", "Code"])
403
+ with md_tab:
404
+ st.markdown(st.session_state.file_content[file_path])
405
+ with code_tab:
406
+ new_content = st.text_area("Edit:", value=st.session_state.file_content[file_path], height=400, key=f"editor_{hash(file_path)}")
407
+ col1, col2 = st.columns([1, 5])
408
+ with col1:
409
+ if st.button("πŸ’Ύ Save"):
410
+ if save_file_content(file_path, new_content):
411
+ st.session_state.file_content[file_path] = new_content
412
+ st.success("Saved! πŸŽ‰")
413
+ time.sleep(1)
414
+ st.rerun()
415
+ with col2:
416
+ st.download_button("⬇️", data=new_content, file_name=os.path.basename(file_path), mime="text/markdown")
417
+
418
+ def save_file_content(file_path, content):
419
+ try:
420
+ with open(file_path, 'w', encoding='utf-8') as file:
421
+ file.write(content)
422
+ return True
423
+ except Exception as e:
424
+ st.error(f"Save error: {str(e)}")
425
+ return False
426
+
427
+ def update_file_management_section():
428
+ if 'file_view_mode' not in st.session_state:
429
+ st.session_state.file_view_mode = None
430
+ if 'current_file' not in st.session_state:
431
+ st.session_state.current_file = None
432
+ if 'file_content' not in st.session_state:
433
+ st.session_state.file_content = {}
434
+ all_files = sorted(glob.glob("*.md"), reverse=True)
435
+ st.sidebar.title("πŸ“ Files")
436
+ if st.sidebar.button("πŸ—‘ Delete All"):
437
+ for file in all_files:
438
+ os.remove(file)
439
+ st.session_state.file_content = {}
440
+ st.session_state.current_file = None
441
+ st.session_state.file_view_mode = None
442
+ st.rerun()
443
+ if st.sidebar.button("⬇️ Download All"):
444
+ zip_file = create_zip_of_files(all_files)
445
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
446
+ for file in all_files:
447
+ col1, col2, col3, col4 = st.sidebar.columns([1, 3, 1, 1])
448
+ with col1:
449
+ if st.button("🌐", key=f"view_{file}"):
450
+ st.session_state.current_file = file
451
+ st.session_state.file_view_mode = 'view'
452
+ if file not in st.session_state.file_content:
453
+ content = load_file(file)
454
+ if content is not None:
455
+ st.session_state.file_content[file] = content
456
+ st.rerun()
457
+ with col2:
458
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
459
+ with col3:
460
+ if st.button("πŸ“‚", key=f"edit_{file}"):
461
+ st.session_state.current_file = file
462
+ st.session_state.file_view_mode = 'edit'
463
+ if file not in st.session_state.file_content:
464
+ content = load_file(file)
465
+ if content is not None:
466
+ st.session_state.file_content[file] = content
467
+ st.rerun()
468
+ with col4:
469
+ if st.button("πŸ—‘", key=f"delete_{file}"):
470
+ os.remove(file)
471
+ if file in st.session_state.file_content:
472
+ del st.session_state.file_content[file]
473
+ if st.session_state.current_file == file:
474
+ st.session_state.current_file = None
475
+ st.session_state.file_view_mode = None
476
+ st.rerun()
477
+
478
+ st.sidebar.markdown("---")
479
+ st.sidebar.title("External Help Links")
480
+ for link in external_links:
481
+ st.sidebar.markdown(f"{link['emoji']} [{link['title']}]({link['url']})", unsafe_allow_html=True)
482
+
483
+ if st.session_state.current_file:
484
+ if st.session_state.file_view_mode == 'view':
485
+ display_file_viewer(st.session_state.current_file)
486
+ elif st.session_state.file_view_mode == 'edit':
487
+ display_file_editor(st.session_state.current_file)
488
+
489
+ # =============================================================================
490
+ # ───────────── VIDEO & AUDIO UI FUNCTIONS ─────────────
491
+ # =============================================================================
492
+ def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
493
+ try:
494
+ st.write("Preprocessing image...")
495
+ if isinstance(file_data, bytes):
496
+ img = Image.open(io.BytesIO(file_data))
497
+ elif hasattr(file_data, 'read'):
498
+ if hasattr(file_data, 'seek'):
499
+ file_data.seek(0)
500
+ img = Image.open(file_data)
501
+ elif isinstance(file_data, Image.Image):
502
+ img = file_data
503
+ else:
504
+ raise ValueError(f"Unsupported input: {type(file_data)}")
505
+ if img.mode != 'RGB':
506
+ img = img.convert('RGB')
507
+ aspect_ratio = img.size[0] / img.size[1]
508
+ if aspect_ratio > target_size[0] / target_size[1]:
509
+ new_width = target_size[0]
510
+ new_height = int(new_width / aspect_ratio)
511
+ else:
512
+ new_height = target_size[1]
513
+ new_width = int(new_height * aspect_ratio)
514
+ new_width = (new_width // 2) * 2
515
+ new_height = (new_height // 2) * 2
516
+ resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
517
+ final_img = Image.new('RGB', target_size, (255, 255, 255))
518
+ paste_x = (target_size[0] - new_width) // 2
519
+ paste_y = (target_size[1] - new_height) // 2
520
+ final_img.paste(resized_img, (paste_x, paste_y))
521
+ return final_img
522
+ except Exception as e:
523
+ st.error(f"Image error: {str(e)}")
524
+ return None
525
+
526
+ def add_video_generation_ui(container):
527
+ st.markdown("### πŸŽ₯ Video Gen")
528
+ col1, col2 = st.columns([2, 1])
529
+ with col1:
530
+ uploaded_file = st.file_uploader("Upload Image πŸ–ΌοΈ", type=['png', 'jpg', 'jpeg'])
531
+ with col2:
532
+ st.markdown("#### Params")
533
+ motion = st.slider("🌊 Motion", 1, 255, 127)
534
+ fps = st.slider("🎬 FPS", 1, 30, 6)
535
+ with st.expander("Advanced"):
536
+ use_custom = st.checkbox("Custom Seed")
537
+ seed = st.number_input("Seed", value=int(time.time() * 1000)) if use_custom else None
538
+ if uploaded_file is not None:
539
+ try:
540
+ file_data = uploaded_file.read()
541
+ preview1, preview2 = st.columns(2)
542
+ with preview1:
543
+ st.write("Original")
544
+ st.image(Image.open(io.BytesIO(file_data)), use_column_width=True)
545
+ with preview2:
546
+ proc_img = validate_and_preprocess_image(io.BytesIO(file_data))
547
+ if proc_img:
548
+ st.write("Processed")
549
+ st.image(proc_img, use_column_width=True)
550
+ else:
551
+ st.error("Preprocess failed")
552
+ return
553
+ if st.button("πŸŽ₯ Generate"):
554
+ with st.spinner("Generating video..."):
555
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
556
+ proc_img.save(temp_file.name, format='PNG')
557
+ try:
558
+ client = Client("awacke1/stable-video-diffusion", hf_token=os.environ.get("HUGGINGFACE_TOKEN"))
559
+ result = client.predict(
560
+ image=temp_file.name,
561
+ seed=seed if seed is not None else int(time.time() * 1000),
562
+ randomize_seed=seed is None,
563
+ motion_bucket_id=motion,
564
+ fps_id=fps,
565
+ api_name="/video"
566
+ )
567
+ if result and isinstance(result, tuple) and len(result) >= 1:
568
+ video_path = result[0].get('video') if isinstance(result[0], dict) else None
569
+ if video_path and os.path.exists(video_path):
570
+ video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
571
+ shutil.copy(video_path, video_filename)
572
+ st.success(f"Video generated! πŸŽ‰")
573
+ st.video(video_filename)
574
+ if container:
575
+ video_record = {
576
+ "id": generate_unique_id(),
577
+ "pk": generate_unique_id(),
578
+ "type": "generated_video",
579
+ "filename": video_filename,
580
+ "seed": seed if seed is not None else "random",
581
+ "motion": motion,
582
+ "fps": fps,
583
+ "timestamp": datetime.now().isoformat()
584
+ }
585
+ success, message = insert_record(container, video_record)
586
+ if success:
587
+ st.success("DB record saved!")
588
+ else:
589
+ st.error(f"DB error: {message}")
590
+ else:
591
+ st.error("Invalid result format")
592
+ else:
593
+ st.error("No result returned")
594
+ except Exception as e:
595
+ st.error(f"Video gen error: {str(e)}")
596
+ finally:
597
+ try:
598
+ os.unlink(temp_file.name)
599
+ st.write("Temp file removed")
600
+ except Exception as e:
601
+ st.warning(f"Cleanup error: {str(e)}")
602
+ except Exception as e:
603
+ st.error(f"Upload error: {str(e)}")
604
+
605
+ # =============================================================================
606
+ # ───────────── AI SAMPLES SIDEBAR (Processed as a Python List) ─────────────
607
+ # =============================================================================
608
+ def display_ai_samples():
609
+ # Define a list of sample queries
610
+ ai_samples = [
611
+ {
612
+ "name": "FullTextContains",
613
+ "description": "Query using FullTextContains",
614
+ "query": 'SELECT TOP 10 * FROM c WHERE FullTextContains(c.text, "bicycle")'
615
+ },
616
+ {
617
+ "name": "FullTextContainsAll",
618
+ "description": "Query using FullTextContainsAll",
619
+ "query": 'SELECT TOP 10 * FROM c WHERE FullTextContainsAll(c.text, "red", "bicycle")'
620
+ },
621
+ {
622
+ "name": "FullTextContainsAny",
623
+ "description": "Query using FullTextContainsAny",
624
+ "query": 'SELECT TOP 10 * FROM c WHERE FullTextContains(c.text, "red") AND FullTextContainsAny(c.text, "bicycle", "skateboard")'
625
+ },
626
+ {
627
+ "name": "FullTextScore",
628
+ "description": "Query using FullTextScore (order by relevance)",
629
+ "query": 'SELECT TOP 10 * FROM c ORDER BY RANK FullTextScore(c.text, ["bicycle", "mountain"])'
630
+ },
631
+ {
632
+ "name": "Vector Search with Score",
633
+ "description": "Example vector search snippet",
634
+ "query": 'results = vector_search.similarity_search_with_score(query="Your query", k=5)\nfor result, score in results:\n print(result.json(), score)'
635
+ },
636
+ {
637
+ "name": "Vector Search with Filtering",
638
+ "description": "Example vector search with a filter",
639
+ "query": 'pre_filter = {"conditions": [{"property": "metadata.page", "operator": "$eq", "value": 0}]}\nresults = vector_search.similarity_search_with_score(query="Your query", k=5, pre_filter=pre_filter)'
640
+ },
641
+ {
642
+ "name": "Hybrid Search",
643
+ "description": "Example hybrid search snippet",
644
+ "query": 'results = vector_search.similarity_search_with_score(query="Your query", k=5, query_type=CosmosDBQueryType.HYBRID)'
645
+ }
646
+ ]
647
+ st.sidebar.markdown("### πŸ€– AI Samples")
648
+ st.sidebar.info("πŸš€ Get started with our AI samples! Time free access to get started today.")
649
+ # Provide a dropdown to select one sample
650
+ sample_names = [sample["name"] for sample in ai_samples]
651
+ selected_sample_name = st.sidebar.selectbox("Select an AI Sample", sample_names)
652
+ selected_sample = next((s for s in ai_samples if s["name"] == selected_sample_name), None)
653
+ if selected_sample:
654
+ st.sidebar.markdown(f"**{selected_sample['name']}**: {selected_sample['description']}")
655
+ # Use language 'sql' for queries containing FullText, else python
656
+ lang = "sql" if "FullText" in selected_sample["name"] else "python"
657
+ st.sidebar.code(selected_sample["query"], language=lang)
658
+
659
+ # =============================================================================
660
+ # ───────────── MAIN FUNCTION ─────────────
661
+ # =============================================================================
662
+ def main():
663
+ st.markdown("### πŸ™ GitCosmos - Cosmos & Git Hub")
664
+ if "chat_history" not in st.session_state:
665
+ st.session_state.chat_history = []
666
+ # Auth & Cosmos client initialization
667
+ if Key:
668
+ st.session_state.primary_key = Key
669
+ st.session_state.logged_in = True
670
+ else:
671
+ st.error("Missing Cosmos Key πŸ”‘βŒ")
672
+ return
673
+ if st.session_state.logged_in:
674
+ try:
675
+ if st.session_state.get("client") is None:
676
+ st.session_state.client = CosmosClient(ENDPOINT, credential=st.session_state.primary_key)
677
+ st.sidebar.title("πŸ™ Navigator")
678
+ databases = get_databases(st.session_state.client)
679
+ selected_db = st.sidebar.selectbox("πŸ—ƒοΈ DB", databases)
680
+ st.markdown(CosmosDBUrl)
681
+ if selected_db != st.session_state.get("selected_database"):
682
+ st.session_state.selected_database = selected_db
683
+ st.session_state.selected_container = None
684
+ st.session_state.selected_document_id = None
685
+ st.session_state.current_index = 0
686
+ st.rerun()
687
+ if st.session_state.selected_database:
688
+ database = st.session_state.client.get_database_client(st.session_state.selected_database)
689
+
690
+ # New Container button under DB menu
691
+ if "show_new_container_form" not in st.session_state:
692
+ st.session_state.show_new_container_form = False
693
+ if st.sidebar.button("πŸ†• New Container"):
694
+ st.session_state.show_new_container_form = True
695
+ if st.session_state.show_new_container_form:
696
+ with st.sidebar.form("new_container_form"):
697
+ new_container_id = st.text_input("Container ID", value="aiml-container")
698
+ new_partition_key = st.text_input("Partition Key", value="/pk")
699
+ new_analytical = st.checkbox("Enable Analytical Store", value=True)
700
+ submitted = st.form_submit_button("Create Container")
701
+ if submitted:
702
+ analytical_ttl = -1 if new_analytical else None
703
+ new_container = create_new_container(
704
+ database,
705
+ new_container_id,
706
+ new_partition_key,
707
+ analytical_storage_ttl=analytical_ttl
708
+ )
709
+ if new_container:
710
+ st.success(f"Container '{new_container_id}' created.")
711
+ # Insert a default templated item into the new container
712
+ default_id = generate_unique_id()
713
+ default_item = {
714
+ "id": default_id,
715
+ "pk": default_id,
716
+ "name": "Default Image Prompt",
717
+ "prompt": "Enter your image prompt here",
718
+ "timestamp": datetime.now().isoformat(),
719
+ "type": "image_prompt"
720
+ }
721
+ insert_success, insert_message = insert_record(new_container, default_item)
722
+ if insert_success:
723
+ st.info("Default templated item created in new container.")
724
+ else:
725
+ st.error(f"Default item insertion error: {insert_message}")
726
+ st.session_state.show_new_container_form = False
727
+ st.session_state.new_container_created = new_container_id
728
+ st.rerun()
729
+
730
+ # Update container list
731
+ containers = get_containers(database)
732
+ if "new_container_created" in st.session_state and st.session_state.new_container_created not in containers:
733
+ containers.append(st.session_state.new_container_created)
734
+ selected_container = st.sidebar.selectbox("πŸ“ Container", containers)
735
+ if selected_container != st.session_state.get("selected_container"):
736
+ st.session_state.selected_container = selected_container
737
+ st.session_state.selected_document_id = None
738
+ st.session_state.current_index = 0
739
+ st.rerun()
740
+ if st.session_state.selected_container:
741
+ container = database.get_container_client(st.session_state.selected_container)
742
+ if st.sidebar.button("πŸ“¦ Export"):
743
+ download_link = archive_current_container(st.session_state.selected_database, st.session_state.selected_container, st.session_state.client)
744
+ if download_link.startswith('<a'):
745
+ st.markdown(download_link, unsafe_allow_html=True)
746
+ else:
747
+ st.error(download_link)
748
+ documents = get_documents(container)
749
+ total_docs = len(documents)
750
+ num_docs = st.slider("Docs", 1, 20, 1)
751
+ documents_to_display = documents[:num_docs] if total_docs > num_docs else documents
752
+ st.sidebar.info(f"Showing {len(documents_to_display)} docs")
753
+ # Document Viewer / Editor
754
+ view_options = ['Markdown', 'Code', 'Run AI', 'Clone', 'New']
755
+ selected_view = st.sidebar.selectbox("View", view_options, index=1)
756
+ if selected_view == 'Markdown':
757
+ st.markdown("#### πŸ“„ Markdown")
758
+ if documents:
759
+ doc = documents[st.session_state.current_index]
760
+ content = json.dumps(doc, indent=2)
761
+ st.markdown(f"```json\n{content}\n```")
762
+ col_prev, col_next = st.columns(2)
763
+ with col_prev:
764
+ if st.button("⬅️") and st.session_state.current_index > 0:
765
+ st.session_state.current_index -= 1
766
+ st.rerun()
767
+ with col_next:
768
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
769
+ st.session_state.current_index += 1
770
+ st.rerun()
771
+ elif selected_view == 'Code':
772
+ st.markdown("#### πŸ’» Code Editor")
773
+ if documents:
774
+ doc = documents[st.session_state.current_index]
775
+ doc_str = st.text_area("Edit JSON", value=json.dumps(doc, indent=2), height=300, key=f'code_{st.session_state.current_index}')
776
+ col_prev, col_next = st.columns(2)
777
+ with col_prev:
778
+ if st.button("⬅️") and st.session_state.current_index > 0:
779
+ st.session_state.current_index -= 1
780
+ st.rerun()
781
+ with col_next:
782
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
783
+ st.session_state.current_index += 1
784
+ st.rerun()
785
+ col_save, col_delete = st.columns(2)
786
+ with col_save:
787
+ if st.button("πŸ’Ύ Save", key=f'save_{st.session_state.current_index}'):
788
+ try:
789
+ updated_doc = json.loads(doc_str)
790
+ container.upsert_item(body=updated_doc)
791
+ st.success(f"Saved {updated_doc['id']}")
792
+ st.rerun()
793
+ except Exception as e:
794
+ st.error(f"Save err: {str(e)}")
795
+ with col_delete:
796
+ if st.button("πŸ—‘οΈ Delete", key=f'delete_{st.session_state.current_index}'):
797
+ try:
798
+ current_doc = json.loads(doc_str)
799
+ success, message = delete_record(container, current_doc)
800
+ if success:
801
+ st.success(message)
802
+ st.rerun()
803
+ else:
804
+ st.error(message)
805
+ except Exception as e:
806
+ st.error(f"Delete err: {str(e)}")
807
+ if "delete_log" in st.session_state and st.session_state.delete_log:
808
+ st.subheader("Delete Log")
809
+ for log_entry in st.session_state.delete_log[-5:]:
810
+ st.write(log_entry)
811
+ elif selected_view == 'Run AI':
812
+ st.markdown("#### πŸ€– Run AI (stub)")
813
+ st.info("AI functionality not implemented.")
814
+ elif selected_view == 'Clone':
815
+ st.markdown("#### πŸ“„ Clone")
816
+ if documents:
817
+ doc = documents[st.session_state.current_index]
818
+ st.markdown(f"Original ID: {doc.get('id', '')}")
819
+ new_id = st.text_input("New ID", value=generate_unique_id(), key='new_clone_id')
820
+ new_name = st.text_input("New Name", value=f"Clone_{new_id[:8]}", key='new_clone_name')
821
+ new_doc = {'id': new_id, 'pk': new_id, 'name': new_name, **{k: v for k, v in doc.items() if k not in ['id', 'name', 'pk', '_rid', '_self', '_etag', '_attachments', '_ts']}}
822
+ doc_str = st.text_area("Edit JSON", value=json.dumps(new_doc, indent=2), height=300, key='clone_preview')
823
+ col1, col2 = st.columns(2)
824
+ with col1:
825
+ if st.button("πŸ”„ Regenerate"):
826
+ new_id = generate_unique_id()
827
+ st.session_state.new_clone_id = new_id
828
+ st.rerun()
829
+ with col2:
830
+ if st.button("πŸ’Ύ Save Clone"):
831
+ try:
832
+ final_doc = json.loads(doc_str)
833
+ for field in ['_rid', '_self', '_etag', '_attachments', '_ts']:
834
+ final_doc.pop(field, None)
835
+ container.create_item(body=final_doc)
836
+ st.success(f"Cloned {final_doc['id']}")
837
+ st.rerun()
838
+ except Exception as e:
839
+ st.error(f"Clone err: {str(e)}")
840
+ col_prev, col_next = st.columns(2)
841
+ with col_prev:
842
+ if st.button("⬅️") and st.session_state.current_index > 0:
843
+ st.session_state.current_index -= 1
844
+ st.rerun()
845
+ with col_next:
846
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
847
+ st.session_state.current_index += 1
848
+ st.rerun()
849
+ elif selected_view == 'New':
850
+ st.markdown("#### βž• New Doc")
851
+ if st.button("πŸ€– Auto-Gen"):
852
+ auto_doc = {
853
+ "id": generate_unique_id(),
854
+ "pk": generate_unique_id(),
855
+ "name": f"Auto {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
856
+ "content": "Auto-generated record.",
857
+ "timestamp": datetime.now().isoformat()
858
+ }
859
+ success, message = insert_record(container, auto_doc)
860
+ if success:
861
+ st.success(message)
862
+ st.rerun()
863
+ else:
864
+ st.error(message)
865
+ else:
866
+ new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
867
+ default_doc = {
868
+ "id": new_id,
869
+ "pk": new_id,
870
+ "name": "New Doc",
871
+ "content": "",
872
+ "timestamp": datetime.now().isoformat()
873
+ }
874
+ new_doc_str = st.text_area("JSON", value=json.dumps(default_doc, indent=2), height=300)
875
+ if st.button("βž• Create"):
876
+ try:
877
+ cleaned = preprocess_text(new_doc_str)
878
+ new_doc = json.loads(cleaned)
879
+ new_doc['id'] = new_id
880
+ new_doc['pk'] = new_id
881
+ success, message = insert_record(container, new_doc)
882
+ if success:
883
+ st.success(f"Created {new_doc['id']}")
884
+ st.rerun()
885
+ else:
886
+ st.error(message)
887
+ except Exception as e:
888
+ st.error(f"Create err: {str(e)}")
889
+ st.subheader(f"πŸ“Š {st.session_state.selected_container}")
890
+ if documents_to_display:
891
+ df = pd.DataFrame(documents_to_display)
892
+ st.dataframe(df)
893
+ else:
894
+ st.info("No docs.")
895
+
896
+ # GitHub Ops section
897
+ st.subheader("πŸ™ GitHub Ops")
898
+ github_token = os.environ.get("GITHUB")
899
+ source_repo = st.text_input("Source Repo URL", value="https://github.com/AaronCWacker/AIExamples-8-24-Streamlit")
900
+ new_repo_name = st.text_input("New Repo Name", value=f"Clone-{datetime.now().strftime('%Y%m%d_%H%M%S')}")
901
+ col_g1, col_g2 = st.columns(2)
902
+ with col_g1:
903
+ if st.button("πŸ“₯ Clone Repo"):
904
+ if github_token and source_repo:
905
+ try:
906
+ local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
907
+ download_github_repo(source_repo, local_path)
908
+ zip_filename = f"{new_repo_name}.zip"
909
+ create_zip_file(local_path, zip_filename[:-4])
910
+ st.markdown(get_download_link(zip_filename), unsafe_allow_html=True)
911
+ st.success("Cloned! πŸŽ‰")
912
+ except Exception as e:
913
+ st.error(f"Clone err: {str(e)}")
914
+ finally:
915
+ if os.path.exists(local_path):
916
+ shutil.rmtree(local_path)
917
+ if os.path.exists(zip_filename):
918
+ os.remove(zip_filename)
919
+ else:
920
+ st.error("Missing token or URL πŸ”‘β“")
921
+ with col_g2:
922
+ if st.button("πŸ“€ Push Repo"):
923
+ if github_token and source_repo:
924
+ try:
925
+ g = Github(github_token)
926
+ new_repo = create_repo(g, new_repo_name)
927
+ local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
928
+ download_github_repo(source_repo, local_path)
929
+ push_to_github(local_path, new_repo, github_token)
930
+ st.success(f"Pushed to {new_repo.html_url} πŸš€")
931
+ except Exception as e:
932
+ st.error(f"Push err: {str(e)}")
933
+ finally:
934
+ if os.path.exists(local_path):
935
+ shutil.rmtree(local_path)
936
+ else:
937
+ st.error("Missing token or URL πŸ”‘β“")
938
+ # Display AI Samples sidebar UI (processed from Python list)
939
+ display_ai_samples()
940
+ update_file_management_section()
941
+ except exceptions.CosmosHttpResponseError as e:
942
+ st.error(f"Cosmos error: {str(e)} 🚨")
943
+ except Exception as e:
944
+ st.error(f"Error: {str(e)} 😱")
945
+ if st.session_state.logged_in and st.sidebar.button("πŸšͺ Logout"):
946
+ st.markdown("#### πŸšͺ Logout")
947
+ st.session_state.logged_in = False
948
+ st.session_state.selected_records = []
949
+ st.session_state.client = None
950
+ st.session_state.selected_database = None
951
+ st.session_state.selected_container = None
952
+ st.session_state.selected_document_id = None
953
+ st.session_state.current_index = 0
954
+ st.rerun()
955
+
956
+ if __name__ == "__main__":
957
+ main()