awacke1 commited on
Commit
0db4e48
Β·
verified Β·
1 Parent(s): 668494b

Update app.py

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