awacke1 commited on
Commit
c3ff99f
Β·
verified Β·
1 Parent(s): 6fba3c6

Update app15.py

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