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

Update app16.py

Browse files
Files changed (1) hide show
  1. app16.py +581 -346
app16.py CHANGED
@@ -1,36 +1,36 @@
1
  # app.py
2
  # =============================================================================
3
- # ───────────── IMPORTS ─────────────
4
  # =============================================================================
5
- import base64
6
- import glob
7
- import hashlib
8
- import json
9
- import os
10
- import pandas as pd
11
- import pytz
12
- import random
13
- import re
14
- import shutil
15
- import streamlit as st
16
- import time
17
- import traceback
18
- import uuid
19
- import zipfile
20
- from PIL import Image
21
- from azure.cosmos import CosmosClient, PartitionKey, exceptions
22
- from datetime import datetime
23
- from git import Repo
24
- from github import Github
25
- from gradio_client import Client, handle_file
26
- import tempfile
27
- import io
28
- import requests
29
- import numpy as np
30
- from urllib.parse import quote
31
 
32
  # =============================================================================
33
- # ───────────── EXTERNAL HELP LINKS (Always visible in sidebar) ─────────────
34
  # =============================================================================
35
  external_links = [
36
  {"title": "MergeKit Official GitHub", "url": "https://github.com/arcee-ai/MergeKit", "emoji": "💻"},
@@ -46,7 +46,7 @@ external_links = [
46
  ]
47
 
48
  # =============================================================================
49
- # ───────────── APP CONFIGURATION ─────────────
50
  # =============================================================================
51
  Site_Name = '🐙 GitCosmos'
52
  title = "🐙 GitCosmos"
@@ -74,8 +74,9 @@ LOCAL_APP_URL = "https://huggingface.co/spaces/awacke1/AzureCosmosDBUI"
74
  CosmosDBUrl = 'https://portal.azure.com/#@AaronCWackergmail.onmicrosoft.com/resource/subscriptions/003fba60-5b3f-48f4-ab36-3ed11bc40816/resourceGroups/datasets/providers/Microsoft.DocumentDB/databaseAccounts/acae-afd/dataExplorer'
75
 
76
  # =============================================================================
77
- # ───────────── HELPER FUNCTIONS ─────────────
78
  # =============================================================================
 
79
  def get_download_link(file_path):
80
  with open(file_path, "rb") as file:
81
  contents = file.read()
@@ -83,6 +84,7 @@ def get_download_link(file_path):
83
  file_name = os.path.basename(file_path)
84
  return f'<a href="data:file/txt;base64,{b64}" download="{file_name}">Download {file_name} 📂</a>'
85
 
 
86
  def generate_unique_id():
87
  timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
88
  unique_uuid = str(uuid.uuid4())
@@ -90,23 +92,27 @@ def generate_unique_id():
90
  st.write('New ID: ' + return_value)
91
  return return_value
92
 
 
93
  def generate_filename(prompt, file_type):
94
  central = pytz.timezone('US/Central')
95
  safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
96
  safe_prompt = re.sub(r'\W+', '', prompt)[:90]
97
  return f"{safe_date_time}{safe_prompt}.{file_type}"
98
 
 
99
  def create_file(filename, prompt, response, should_save=True):
100
  if not should_save:
101
  return
102
  with open(filename, 'w', encoding='utf-8') as file:
103
  file.write(prompt + "\n\n" + response)
104
 
 
105
  def load_file(file_name):
106
  with open(file_name, "r", encoding='utf-8') as file:
107
  content = file.read()
108
  return content
109
 
 
110
  def display_glossary_entity(k):
111
  search_urls = {
112
  "🚀": lambda k: f"/?q={k}",
@@ -117,6 +123,7 @@ def display_glossary_entity(k):
117
  links_md = ' '.join([f"<a href='{url(k)}' target='_blank'>{emoji}</a>" for emoji, url in search_urls.items()])
118
  st.markdown(f"{k} {links_md}", unsafe_allow_html=True)
119
 
 
120
  def create_zip_of_files(files):
121
  zip_name = "all_files.zip"
122
  with zipfile.ZipFile(zip_name, 'w') as zipf:
@@ -124,6 +131,7 @@ def create_zip_of_files(files):
124
  zipf.write(file)
125
  return zip_name
126
 
 
127
  def get_video_html(video_path, width="100%"):
128
  video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
129
  return f'''
@@ -133,6 +141,7 @@ def get_video_html(video_path, width="100%"):
133
  </video>
134
  '''
135
 
 
136
  def get_audio_html(audio_path, width="100%"):
137
  audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
138
  return f'''
@@ -142,6 +151,7 @@ def get_audio_html(audio_path, width="100%"):
142
  </audio>
143
  '''
144
 
 
145
  def preprocess_text(text):
146
  text = text.replace('\r\n', '\\n').replace('\r', '\\n').replace('\n', '\\n')
147
  text = text.replace('"', '\\"')
@@ -150,19 +160,23 @@ def preprocess_text(text):
150
  return text.strip()
151
 
152
  # =============================================================================
153
- # ───────────── COSMOS DB FUNCTIONS ─────────────
154
  # =============================================================================
 
155
  def get_databases(client):
156
  return [db['id'] for db in client.list_databases()]
157
 
 
158
  def get_containers(database):
159
  return [container['id'] for container in database.list_containers()]
160
 
 
161
  def get_documents(container, limit=None):
162
  query = "SELECT * FROM c ORDER BY c._ts DESC"
163
  items = list(container.query_items(query=query, enable_cross_partition_query=True, max_item_count=limit))
164
  return items
165
 
 
166
  def insert_record(container, record):
167
  try:
168
  container.create_item(body=record)
@@ -172,6 +186,7 @@ def insert_record(container, record):
172
  except Exception as e:
173
  return False, f"Error: {str(e)} 😱"
174
 
 
175
  def update_record(container, updated_record):
176
  try:
177
  container.upsert_item(body=updated_record)
@@ -181,6 +196,7 @@ def update_record(container, updated_record):
181
  except Exception as e:
182
  return False, f"Error: {traceback.format_exc()} 😱"
183
 
 
184
  def delete_record(container, record):
185
  try:
186
  if "id" not in record:
@@ -208,6 +224,7 @@ def delete_record(container, record):
208
  st.session_state.delete_log.append(error_msg)
209
  return False, error_msg
210
 
 
211
  def save_to_cosmos_db(container, query, response1, response2):
212
  try:
213
  if container:
@@ -233,6 +250,7 @@ def save_to_cosmos_db(container, query, response1, response2):
233
  except Exception as e:
234
  st.error(f"Save error: {str(e)}")
235
 
 
236
  def archive_current_container(database_name, container_name, client):
237
  try:
238
  base_dir = "./cosmos_archive_current_container"
@@ -255,7 +273,7 @@ def archive_current_container(database_name, container_name, client):
255
  return f"Archive error: {str(e)} 😢"
256
 
257
  # =============================================================================
258
- # ───────────── ADVANCED COSMOS FUNCTIONS ─────────────
259
  # =============================================================================
260
  def create_new_container(database, container_id, partition_key_path,
261
  analytical_storage_ttl=None, indexing_policy=None, vector_embedding_policy=None):
@@ -324,7 +342,7 @@ def vector_search(container, query_vector, vector_field, top=10, exact_search=Fa
324
  return results
325
 
326
  # =============================================================================
327
- # ───────────── GITHUB FUNCTIONS ─────────────
328
  # =============================================================================
329
  def download_github_repo(url, local_path):
330
  if os.path.exists(local_path):
@@ -357,7 +375,7 @@ def push_to_github(local_path, repo, github_token):
357
  origin.push(refspec=f'{current_branch}:{current_branch}')
358
 
359
  # =============================================================================
360
- # ───────────── FILE & MEDIA MANAGEMENT FUNCTIONS ─────────────
361
  # =============================================================================
362
  def display_saved_files_in_sidebar():
363
  all_files = sorted([f for f in glob.glob("*.md") if not f.lower().startswith('readme')], reverse=True)
@@ -399,11 +417,8 @@ def display_file_editor(file_path):
399
  return
400
  st.markdown("### ✏️ Edit File")
401
  st.markdown(f"**Editing:** {file_path}")
402
- md_tab, code_tab = st.tabs(["Markdown", "Code"])
403
- with md_tab:
404
- st.markdown(st.session_state.file_content[file_path])
405
- with code_tab:
406
- new_content = st.text_area("Edit:", value=st.session_state.file_content[file_path], height=400, key=f"editor_{hash(file_path)}")
407
  col1, col2 = st.columns([1, 5])
408
  with col1:
409
  if st.button("💾 Save"):
@@ -487,7 +502,7 @@ def update_file_management_section():
487
  display_file_editor(st.session_state.current_file)
488
 
489
  # =============================================================================
490
- # ───────────── VIDEO & AUDIO UI FUNCTIONS ─────────────
491
  # =============================================================================
492
  def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
493
  try:
@@ -603,66 +618,131 @@ def add_video_generation_ui(container):
603
  st.error(f"Upload error: {str(e)}")
604
 
605
  # =============================================================================
606
- # ───────────── AI SAMPLES SIDEBAR (Processed as a Python List) ─────────────
607
  # =============================================================================
608
- def display_ai_samples():
609
- # Define a list of sample queries
610
- ai_samples = [
611
- {
612
- "name": "FullTextContains",
613
- "description": "Query using FullTextContains",
614
- "query": 'SELECT TOP 10 * FROM c WHERE FullTextContains(c.text, "bicycle")'
615
- },
616
- {
617
- "name": "FullTextContainsAll",
618
- "description": "Query using FullTextContainsAll",
619
- "query": 'SELECT TOP 10 * FROM c WHERE FullTextContainsAll(c.text, "red", "bicycle")'
620
- },
621
- {
622
- "name": "FullTextContainsAny",
623
- "description": "Query using FullTextContainsAny",
624
- "query": 'SELECT TOP 10 * FROM c WHERE FullTextContains(c.text, "red") AND FullTextContainsAny(c.text, "bicycle", "skateboard")'
625
- },
626
- {
627
- "name": "FullTextScore",
628
- "description": "Query using FullTextScore (order by relevance)",
629
- "query": 'SELECT TOP 10 * FROM c ORDER BY RANK FullTextScore(c.text, ["bicycle", "mountain"])'
630
- },
631
- {
632
- "name": "Vector Search with Score",
633
- "description": "Example vector search snippet",
634
- "query": 'results = vector_search.similarity_search_with_score(query="Your query", k=5)\nfor result, score in results:\n print(result.json(), score)'
635
- },
636
- {
637
- "name": "Vector Search with Filtering",
638
- "description": "Example vector search with a filter",
639
- "query": 'pre_filter = {"conditions": [{"property": "metadata.page", "operator": "$eq", "value": 0}]}\nresults = vector_search.similarity_search_with_score(query="Your query", k=5, pre_filter=pre_filter)'
640
- },
641
- {
642
- "name": "Hybrid Search",
643
- "description": "Example hybrid search snippet",
644
- "query": 'results = vector_search.similarity_search_with_score(query="Your query", k=5, query_type=CosmosDBQueryType.HYBRID)'
645
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  ]
647
- st.sidebar.markdown("### 🤖 AI Samples")
648
- st.sidebar.info("🚀 Get started with our AI samples! Time free access to get started today.")
649
- # Provide a dropdown to select one sample
650
- sample_names = [sample["name"] for sample in ai_samples]
651
- selected_sample_name = st.sidebar.selectbox("Select an AI Sample", sample_names)
652
- selected_sample = next((s for s in ai_samples if s["name"] == selected_sample_name), None)
653
- if selected_sample:
654
- st.sidebar.markdown(f"**{selected_sample['name']}**: {selected_sample['description']}")
655
- # Use language 'sql' for queries containing FullText, else python
656
- lang = "sql" if "FullText" in selected_sample["name"] else "python"
657
- st.sidebar.code(selected_sample["query"], language=lang)
658
 
659
  # =============================================================================
660
- # ───────────── MAIN FUNCTION ─────────────
661
  # =============================================================================
662
  def main():
663
  st.markdown("### 🐙 GitCosmos - Cosmos & Git Hub")
 
 
664
  if "chat_history" not in st.session_state:
665
  st.session_state.chat_history = []
 
 
666
  # Auth & Cosmos client initialization
667
  if Key:
668
  st.session_state.primary_key = Key
@@ -670,278 +750,278 @@ def main():
670
  else:
671
  st.error("Missing Cosmos Key 🔑❌")
672
  return
673
- if st.session_state.logged_in:
674
- try:
675
- if st.session_state.get("client") is None:
676
- st.session_state.client = CosmosClient(ENDPOINT, credential=st.session_state.primary_key)
677
- st.sidebar.title("🐙 Navigator")
678
- databases = get_databases(st.session_state.client)
679
- selected_db = st.sidebar.selectbox("🗃️ DB", databases)
680
- st.markdown(CosmosDBUrl)
681
- if selected_db != st.session_state.get("selected_database"):
682
- st.session_state.selected_database = selected_db
683
- st.session_state.selected_container = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
684
  st.session_state.selected_document_id = None
685
  st.session_state.current_index = 0
686
  st.rerun()
687
- if st.session_state.selected_database:
688
- database = st.session_state.client.get_database_client(st.session_state.selected_database)
689
-
690
- # New Container button under DB menu
691
- if "show_new_container_form" not in st.session_state:
692
- st.session_state.show_new_container_form = False
693
- if st.sidebar.button("🆕 New Container"):
694
- st.session_state.show_new_container_form = True
695
- if st.session_state.show_new_container_form:
696
- with st.sidebar.form("new_container_form"):
697
- new_container_id = st.text_input("Container ID", value="aiml-container")
698
- new_partition_key = st.text_input("Partition Key", value="/pk")
699
- new_analytical = st.checkbox("Enable Analytical Store", value=True)
700
- submitted = st.form_submit_button("Create Container")
701
- if submitted:
702
- analytical_ttl = -1 if new_analytical else None
703
- new_container = create_new_container(
704
- database,
705
- new_container_id,
706
- new_partition_key,
707
- analytical_storage_ttl=analytical_ttl
708
- )
709
- if new_container:
710
- st.success(f"Container '{new_container_id}' created.")
711
- # Insert a default templated item into the new container
712
- default_id = generate_unique_id()
713
- default_item = {
714
- "id": default_id,
715
- "pk": default_id,
716
- "name": "Default Image Prompt",
717
- "prompt": "Enter your image prompt here",
718
- "timestamp": datetime.now().isoformat(),
719
- "type": "image_prompt"
720
- }
721
- insert_success, insert_message = insert_record(new_container, default_item)
722
- if insert_success:
723
- st.info("Default templated item created in new container.")
724
- else:
725
- st.error(f"Default item insertion error: {insert_message}")
726
- st.session_state.show_new_container_form = False
727
- st.session_state.new_container_created = new_container_id
728
  st.rerun()
729
-
730
- # Update container list
731
- containers = get_containers(database)
732
- if "new_container_created" in st.session_state and st.session_state.new_container_created not in containers:
733
- containers.append(st.session_state.new_container_created)
734
- selected_container = st.sidebar.selectbox("📁 Container", containers)
735
- if selected_container != st.session_state.get("selected_container"):
736
- st.session_state.selected_container = selected_container
737
- st.session_state.selected_document_id = None
738
- st.session_state.current_index = 0
739
- st.rerun()
740
- if st.session_state.selected_container:
741
- container = database.get_container_client(st.session_state.selected_container)
742
- if st.sidebar.button("📦 Export"):
743
- download_link = archive_current_container(st.session_state.selected_database, st.session_state.selected_container, st.session_state.client)
744
- if download_link.startswith('<a'):
745
- st.markdown(download_link, unsafe_allow_html=True)
746
- else:
747
- st.error(download_link)
748
- documents = get_documents(container)
749
- total_docs = len(documents)
750
- num_docs = st.slider("Docs", 1, 20, 1)
751
- documents_to_display = documents[:num_docs] if total_docs > num_docs else documents
752
- st.sidebar.info(f"Showing {len(documents_to_display)} docs")
753
- # Document Viewer / Editor
754
- view_options = ['Markdown', 'Code', 'Run AI', 'Clone', 'New']
755
- selected_view = st.sidebar.selectbox("View", view_options, index=1)
756
- if selected_view == 'Markdown':
757
- st.markdown("#### 📄 Markdown")
758
- if documents:
759
- doc = documents[st.session_state.current_index]
760
- content = json.dumps(doc, indent=2)
761
- st.markdown(f"```json\n{content}\n```")
762
- col_prev, col_next = st.columns(2)
763
- with col_prev:
764
- if st.button("⬅️") and st.session_state.current_index > 0:
765
- st.session_state.current_index -= 1
766
- st.rerun()
767
- with col_next:
768
- if st.button("➡️") and st.session_state.current_index < total_docs - 1:
769
- st.session_state.current_index += 1
770
- st.rerun()
771
- elif selected_view == 'Code':
772
- st.markdown("#### 💻 Code Editor")
773
- if documents:
774
- doc = documents[st.session_state.current_index]
775
- doc_str = st.text_area("Edit JSON", value=json.dumps(doc, indent=2), height=300, key=f'code_{st.session_state.current_index}')
776
- col_prev, col_next = st.columns(2)
777
- with col_prev:
778
- if st.button("⬅️") and st.session_state.current_index > 0:
779
- st.session_state.current_index -= 1
780
- st.rerun()
781
- with col_next:
782
- if st.button("➡️") and st.session_state.current_index < total_docs - 1:
783
- st.session_state.current_index += 1
784
- st.rerun()
785
- col_save, col_delete = st.columns(2)
786
- with col_save:
787
- if st.button("💾 Save", key=f'save_{st.session_state.current_index}'):
788
- try:
789
- updated_doc = json.loads(doc_str)
790
- container.upsert_item(body=updated_doc)
791
- st.success(f"Saved {updated_doc['id']}")
792
- st.rerun()
793
- except Exception as e:
794
- st.error(f"Save err: {str(e)}")
795
- with col_delete:
796
- if st.button("🗑️ Delete", key=f'delete_{st.session_state.current_index}'):
797
- try:
798
- current_doc = json.loads(doc_str)
799
- success, message = delete_record(container, current_doc)
800
- if success:
801
- st.success(message)
802
- st.rerun()
803
- else:
804
- st.error(message)
805
- except Exception as e:
806
- st.error(f"Delete err: {str(e)}")
807
- if "delete_log" in st.session_state and st.session_state.delete_log:
808
- st.subheader("Delete Log")
809
- for log_entry in st.session_state.delete_log[-5:]:
810
- st.write(log_entry)
811
- elif selected_view == 'Run AI':
812
- st.markdown("#### 🤖 Run AI (stub)")
813
- st.info("AI functionality not implemented.")
814
- elif selected_view == 'Clone':
815
- st.markdown("#### 📄 Clone")
816
- if documents:
817
- doc = documents[st.session_state.current_index]
818
- st.markdown(f"Original ID: {doc.get('id', '')}")
819
- new_id = st.text_input("New ID", value=generate_unique_id(), key='new_clone_id')
820
- new_name = st.text_input("New Name", value=f"Clone_{new_id[:8]}", key='new_clone_name')
821
- new_doc = {'id': new_id, 'pk': new_id, 'name': new_name, **{k: v for k, v in doc.items() if k not in ['id', 'name', 'pk', '_rid', '_self', '_etag', '_attachments', '_ts']}}
822
- doc_str = st.text_area("Edit JSON", value=json.dumps(new_doc, indent=2), height=300, key='clone_preview')
823
- col1, col2 = st.columns(2)
824
- with col1:
825
- if st.button("🔄 Regenerate"):
826
- new_id = generate_unique_id()
827
- st.session_state.new_clone_id = new_id
828
- st.rerun()
829
- with col2:
830
- if st.button("💾 Save Clone"):
831
- try:
832
- final_doc = json.loads(doc_str)
833
- for field in ['_rid', '_self', '_etag', '_attachments', '_ts']:
834
- final_doc.pop(field, None)
835
- container.create_item(body=final_doc)
836
- st.success(f"Cloned {final_doc['id']}")
837
- st.rerun()
838
- except Exception as e:
839
- st.error(f"Clone err: {str(e)}")
840
- col_prev, col_next = st.columns(2)
841
- with col_prev:
842
- if st.button("⬅️") and st.session_state.current_index > 0:
843
- st.session_state.current_index -= 1
844
- st.rerun()
845
- with col_next:
846
- if st.button("➡️") and st.session_state.current_index < total_docs - 1:
847
- st.session_state.current_index += 1
848
- st.rerun()
849
- elif selected_view == 'New':
850
- st.markdown("#### ➕ New Doc")
851
- if st.button("🤖 Auto-Gen"):
852
- auto_doc = {
853
- "id": generate_unique_id(),
854
- "pk": generate_unique_id(),
855
- "name": f"Auto {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
856
- "content": "Auto-generated record.",
857
- "timestamp": datetime.now().isoformat()
858
- }
859
- success, message = insert_record(container, auto_doc)
860
- if success:
861
- st.success(message)
862
  st.rerun()
863
- else:
864
- st.error(message)
865
- else:
866
- new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
867
- default_doc = {
868
- "id": new_id,
869
- "pk": new_id,
870
- "name": "New Doc",
871
- "content": "",
872
- "timestamp": datetime.now().isoformat()
873
- }
874
- new_doc_str = st.text_area("JSON", value=json.dumps(default_doc, indent=2), height=300)
875
- if st.button("➕ Create"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
  try:
877
- cleaned = preprocess_text(new_doc_str)
878
- new_doc = json.loads(cleaned)
879
- new_doc['id'] = new_id
880
- new_doc['pk'] = new_id
881
- success, message = insert_record(container, new_doc)
882
  if success:
883
- st.success(f"Created {new_doc['id']}")
884
  st.rerun()
885
  else:
886
  st.error(message)
887
  except Exception as e:
888
- st.error(f"Create err: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889
  st.subheader(f"📊 {st.session_state.selected_container}")
890
  if documents_to_display:
891
  df = pd.DataFrame(documents_to_display)
892
  st.dataframe(df)
893
  else:
894
  st.info("No docs.")
895
-
896
- # GitHub Ops section
897
- st.subheader("🐙 GitHub Ops")
898
- github_token = os.environ.get("GITHUB")
899
- source_repo = st.text_input("Source Repo URL", value="https://github.com/AaronCWacker/AIExamples-8-24-Streamlit")
900
- new_repo_name = st.text_input("New Repo Name", value=f"Clone-{datetime.now().strftime('%Y%m%d_%H%M%S')}")
901
- col_g1, col_g2 = st.columns(2)
902
- with col_g1:
903
- if st.button("📥 Clone Repo"):
904
- if github_token and source_repo:
905
- try:
906
- local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
907
- download_github_repo(source_repo, local_path)
908
- zip_filename = f"{new_repo_name}.zip"
909
- create_zip_file(local_path, zip_filename[:-4])
910
- st.markdown(get_download_link(zip_filename), unsafe_allow_html=True)
911
- st.success("Cloned! 🎉")
912
- except Exception as e:
913
- st.error(f"Clone err: {str(e)}")
914
- finally:
915
- if os.path.exists(local_path):
916
- shutil.rmtree(local_path)
917
- if os.path.exists(zip_filename):
918
- os.remove(zip_filename)
919
- else:
920
- st.error("Missing token or URL 🔑❓")
921
- with col_g2:
922
- if st.button("📤 Push Repo"):
923
- if github_token and source_repo:
924
- try:
925
- g = Github(github_token)
926
- new_repo = create_repo(g, new_repo_name)
927
- local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
928
- download_github_repo(source_repo, local_path)
929
- push_to_github(local_path, new_repo, github_token)
930
- st.success(f"Pushed to {new_repo.html_url} 🚀")
931
- except Exception as e:
932
- st.error(f"Push err: {str(e)}")
933
- finally:
934
- if os.path.exists(local_path):
935
- shutil.rmtree(local_path)
936
- else:
937
- st.error("Missing token or URL 🔑❓")
938
- # Display AI Samples sidebar UI (processed from Python list)
939
- display_ai_samples()
940
- update_file_management_section()
941
- except exceptions.CosmosHttpResponseError as e:
942
- st.error(f"Cosmos error: {str(e)} 🚨")
943
- except Exception as e:
944
- st.error(f"Error: {str(e)} 😱")
945
  if st.session_state.logged_in and st.sidebar.button("🚪 Logout"):
946
  st.markdown("#### 🚪 Logout")
947
  st.session_state.logged_in = False
@@ -955,3 +1035,158 @@ def main():
955
 
956
  if __name__ == "__main__":
957
  main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # app.py
2
  # =============================================================================
3
+ # 🚀 IMPORTS
4
  # =============================================================================
5
+ import base64 # 🔥 For encoding/decoding files
6
+ import glob # 🔍 For file searching
7
+ import hashlib # 🔒 For hashing
8
+ import json # 🧮 For JSON handling
9
+ import os # 📁 OS interaction
10
+ import pandas as pd # 🐼 For data frames
11
+ import pytz # ⏰ For timezone management
12
+ import random # 🎲 For randomness
13
+ import re # 🔍 For regex operations
14
+ import shutil # 🗑️ For file copying/removal
15
+ import streamlit as st # 💻 Streamlit UI framework
16
+ import time # ⏳ For time functions
17
+ import traceback # 🚨 For error traces
18
+ import uuid # 🆔 For unique IDs
19
+ import zipfile # 📦 For archiving files
20
+ from PIL import Image # 🖼️ For image processing
21
+ from azure.cosmos import CosmosClient, PartitionKey, exceptions # ☁️ Cosmos DB
22
+ from datetime import datetime # ⏰ For timestamps
23
+ from git import Repo # 🐙 For Git operations
24
+ from github import Github # 🔗 For GitHub API
25
+ from gradio_client import Client, handle_file # 🤖 For Gradio video generation
26
+ import tempfile # 📝 For temporary files
27
+ import io # 📡 For in-memory streams
28
+ import requests # 🌐 For HTTP requests
29
+ import numpy as np # 🔢 For numerical operations
30
+ from urllib.parse import quote # 🔗 For URL encoding
31
 
32
  # =============================================================================
33
+ # 😎 EXTERNAL HELP LINKS (Always visible in sidebar)
34
  # =============================================================================
35
  external_links = [
36
  {"title": "MergeKit Official GitHub", "url": "https://github.com/arcee-ai/MergeKit", "emoji": "💻"},
 
46
  ]
47
 
48
  # =============================================================================
49
+ # 🎨 APP CONFIGURATION
50
  # =============================================================================
51
  Site_Name = '🐙 GitCosmos'
52
  title = "🐙 GitCosmos"
 
74
  CosmosDBUrl = 'https://portal.azure.com/#@AaronCWackergmail.onmicrosoft.com/resource/subscriptions/003fba60-5b3f-48f4-ab36-3ed11bc40816/resourceGroups/datasets/providers/Microsoft.DocumentDB/databaseAccounts/acae-afd/dataExplorer'
75
 
76
  # =============================================================================
77
+ # 💾 HELPER FUNCTIONS
78
  # =============================================================================
79
+ # 🔗 Get a download link for a file
80
  def get_download_link(file_path):
81
  with open(file_path, "rb") as file:
82
  contents = file.read()
 
84
  file_name = os.path.basename(file_path)
85
  return f'<a href="data:file/txt;base64,{b64}" download="{file_name}">Download {file_name} 📂</a>'
86
 
87
+ # 🆔 Generate a unique ID
88
  def generate_unique_id():
89
  timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
90
  unique_uuid = str(uuid.uuid4())
 
92
  st.write('New ID: ' + return_value)
93
  return return_value
94
 
95
+ # 📝 Generate a safe filename based on a prompt
96
  def generate_filename(prompt, file_type):
97
  central = pytz.timezone('US/Central')
98
  safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
99
  safe_prompt = re.sub(r'\W+', '', prompt)[:90]
100
  return f"{safe_date_time}{safe_prompt}.{file_type}"
101
 
102
+ # 📄 Create a file with given content
103
  def create_file(filename, prompt, response, should_save=True):
104
  if not should_save:
105
  return
106
  with open(filename, 'w', encoding='utf-8') as file:
107
  file.write(prompt + "\n\n" + response)
108
 
109
+ # 📂 Load file contents
110
  def load_file(file_name):
111
  with open(file_name, "r", encoding='utf-8') as file:
112
  content = file.read()
113
  return content
114
 
115
+ # 🔗 Display a glossary entity with quick search links
116
  def display_glossary_entity(k):
117
  search_urls = {
118
  "🚀": lambda k: f"/?q={k}",
 
123
  links_md = ' '.join([f"<a href='{url(k)}' target='_blank'>{emoji}</a>" for emoji, url in search_urls.items()])
124
  st.markdown(f"{k} {links_md}", unsafe_allow_html=True)
125
 
126
+ # 📦 Create a ZIP archive of given files
127
  def create_zip_of_files(files):
128
  zip_name = "all_files.zip"
129
  with zipfile.ZipFile(zip_name, 'w') as zipf:
 
131
  zipf.write(file)
132
  return zip_name
133
 
134
+ # 🎥 Get HTML to embed a video
135
  def get_video_html(video_path, width="100%"):
136
  video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
137
  return f'''
 
141
  </video>
142
  '''
143
 
144
+ # 🎵 Get HTML to embed audio
145
  def get_audio_html(audio_path, width="100%"):
146
  audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
147
  return f'''
 
151
  </audio>
152
  '''
153
 
154
+ # ✂️ Preprocess text (e.g., for JSON safety)
155
  def preprocess_text(text):
156
  text = text.replace('\r\n', '\\n').replace('\r', '\\n').replace('\n', '\\n')
157
  text = text.replace('"', '\\"')
 
160
  return text.strip()
161
 
162
  # =============================================================================
163
+ # ☁️ COSMOS DB FUNCTIONS
164
  # =============================================================================
165
+ # List all databases
166
  def get_databases(client):
167
  return [db['id'] for db in client.list_databases()]
168
 
169
+ # List all containers in a database
170
  def get_containers(database):
171
  return [container['id'] for container in database.list_containers()]
172
 
173
+ # Retrieve documents from a container
174
  def get_documents(container, limit=None):
175
  query = "SELECT * FROM c ORDER BY c._ts DESC"
176
  items = list(container.query_items(query=query, enable_cross_partition_query=True, max_item_count=limit))
177
  return items
178
 
179
+ # Insert a record into a container
180
  def insert_record(container, record):
181
  try:
182
  container.create_item(body=record)
 
186
  except Exception as e:
187
  return False, f"Error: {str(e)} 😱"
188
 
189
+ # Update (upsert) a record in a container
190
  def update_record(container, updated_record):
191
  try:
192
  container.upsert_item(body=updated_record)
 
196
  except Exception as e:
197
  return False, f"Error: {traceback.format_exc()} 😱"
198
 
199
+ # Delete a record from a container
200
  def delete_record(container, record):
201
  try:
202
  if "id" not in record:
 
224
  st.session_state.delete_log.append(error_msg)
225
  return False, error_msg
226
 
227
+ # Save data to Cosmos DB (for AI responses)
228
  def save_to_cosmos_db(container, query, response1, response2):
229
  try:
230
  if container:
 
250
  except Exception as e:
251
  st.error(f"Save error: {str(e)}")
252
 
253
+ # Archive current container data into a ZIP file
254
  def archive_current_container(database_name, container_name, client):
255
  try:
256
  base_dir = "./cosmos_archive_current_container"
 
273
  return f"Archive error: {str(e)} 😢"
274
 
275
  # =============================================================================
276
+ # 🚀 ADVANCED COSMOS FUNCTIONS
277
  # =============================================================================
278
  def create_new_container(database, container_id, partition_key_path,
279
  analytical_storage_ttl=None, indexing_policy=None, vector_embedding_policy=None):
 
342
  return results
343
 
344
  # =============================================================================
345
+ # 🐙 GITHUB FUNCTIONS
346
  # =============================================================================
347
  def download_github_repo(url, local_path):
348
  if os.path.exists(local_path):
 
375
  origin.push(refspec=f'{current_branch}:{current_branch}')
376
 
377
  # =============================================================================
378
+ # 📁 FILE & MEDIA MANAGEMENT FUNCTIONS
379
  # =============================================================================
380
  def display_saved_files_in_sidebar():
381
  all_files = sorted([f for f in glob.glob("*.md") if not f.lower().startswith('readme')], reverse=True)
 
417
  return
418
  st.markdown("### ✏️ Edit File")
419
  st.markdown(f"**Editing:** {file_path}")
420
+ # Use on_change callback for auto-save via doc_editor
421
+ new_content = st.text_area("Edit JSON", value=st.session_state.file_content[file_path], height=400, key="doc_editor", on_change=lambda: auto_save_edit())
 
 
 
422
  col1, col2 = st.columns([1, 5])
423
  with col1:
424
  if st.button("💾 Save"):
 
502
  display_file_editor(st.session_state.current_file)
503
 
504
  # =============================================================================
505
+ # 🎥 VIDEO & AUDIO UI FUNCTIONS
506
  # =============================================================================
507
  def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
508
  try:
 
618
  st.error(f"Upload error: {str(e)}")
619
 
620
  # =============================================================================
621
+ # 🤖 NEW ITEM & FIELD FUNCTIONS
622
  # =============================================================================
623
+ # 🆕 Create a new sample document with default values
624
+ def new_item_default(container):
625
+ new_id = generate_unique_id()
626
+ default_doc = {
627
+ "id": new_id,
628
+ "pk": new_id,
629
+ "name": "New Sample Document",
630
+ "content": "Start editing your document here...",
631
+ "timestamp": datetime.now().isoformat(),
632
+ "type": "sample"
633
+ }
634
+ success, message = insert_record(container, default_doc)
635
+ if success:
636
+ st.success("New sample document created! ✨")
637
+ return default_doc
638
+ else:
639
+ st.error("Error creating new item: " + message)
640
+ return None
641
+
642
+ # 🔄 Auto-save changes from the editor
643
+ def auto_save_edit():
644
+ try:
645
+ edited_str = st.session_state.doc_editor
646
+ edited_doc = json.loads(edited_str)
647
+ container = st.session_state.current_container
648
+ container.upsert_item(edited_doc)
649
+ st.success("Auto-saved! 💾")
650
+ except Exception as e:
651
+ st.error(f"Auto-save error: {str(e)}")
652
+
653
+ # Add a new field (key/value) to the current document
654
+ def add_field_to_doc():
655
+ key = st.session_state.new_field_key
656
+ value = st.session_state.new_field_value
657
+ try:
658
+ doc = json.loads(st.session_state.doc_editor)
659
+ doc[key] = value
660
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
661
+ auto_save_edit()
662
+ st.success(f"Added field {key} 👍")
663
+ except Exception as e:
664
+ st.error(f"Error adding field: {str(e)}")
665
+
666
+ # =============================================================================
667
+ # 🔍 VECTOR SEARCH INTERFACE (Simple keyword search)
668
+ # =============================================================================
669
+ def vector_keyword_search(keyword, container):
670
+ try:
671
+ query = f"SELECT * FROM c WHERE CONTAINS(c.content, '{keyword}')"
672
+ results = list(container.query_items(query=query, enable_cross_partition_query=True))
673
+ return results
674
+ except Exception as e:
675
+ st.error(f"Vector search error: {str(e)}")
676
+ return []
677
+
678
+ # =============================================================================
679
+ # 🤖 NEW AI MODALITY RECORD TEMPLATES
680
+ # =============================================================================
681
+ def new_ai_record(container):
682
+ new_id = generate_unique_id()
683
+ default_doc = {
684
+ "id": new_id,
685
+ "pk": new_id,
686
+ "name": "AI Modality Record",
687
+ "function_url": "https://example.com/function",
688
+ "input_text": "### Input (markdown)\n\nType your input here.",
689
+ "output_text": "### Output (markdown)\n\nResult will appear here.",
690
+ "timestamp": datetime.now().isoformat(),
691
+ "type": "ai_modality"
692
+ }
693
+ success, message = insert_record(container, default_doc)
694
+ if success:
695
+ st.success("New AI modality record created! 💡")
696
+ return default_doc
697
+ else:
698
+ st.error("Error creating AI record: " + message)
699
+ return None
700
+
701
+ def new_links_record(container):
702
+ new_id = generate_unique_id()
703
+ links_md = "\n".join([f"- {link['emoji']} [{link['title']}]({link['url']})" for link in external_links])
704
+ default_doc = {
705
+ "id": new_id,
706
+ "pk": new_id,
707
+ "name": "Portal Links Record",
708
+ "function_url": "",
709
+ "input_text": links_md,
710
+ "output_text": "",
711
+ "timestamp": datetime.now().isoformat(),
712
+ "type": "ai_modality"
713
+ }
714
+ success, message = insert_record(container, default_doc)
715
+ if success:
716
+ st.success("New Portal Links record created! 🔗")
717
+ return default_doc
718
+ else:
719
+ st.error("Error creating links record: " + message)
720
+ return None
721
+
722
+ # =============================================================================
723
+ # 🤖 LANGCHAIN FUNCTIONS (Witty emoji comments)
724
+ # =============================================================================
725
+ def display_langchain_functions():
726
+ functions = [
727
+ {"name": "OpenAIEmbeddings", "comment": "🔮 Creates embeddings using OpenAI – pure magic!"},
728
+ {"name": "AzureCosmosDBNoSqlVectorSearch", "comment": "🚀 Performs vector search on Cosmos DB – superfast and smart!"},
729
+ {"name": "RecursiveCharacterTextSplitter", "comment": "✂️ Slices text into manageable chunks – like a pro chef!"}
730
  ]
731
+ st.sidebar.markdown("### 🤖 Langchain Functions")
732
+ for func in functions:
733
+ st.sidebar.write(f"{func['name']}: {func['comment']}")
 
 
 
 
 
 
 
 
734
 
735
  # =============================================================================
736
+ # 📝 MAIN FUNCTION
737
  # =============================================================================
738
  def main():
739
  st.markdown("### 🐙 GitCosmos - Cosmos & Git Hub")
740
+ # Set a friendly portal link (with emoji) for Cosmos DB
741
+ st.markdown(f"[🔗 Portal]({CosmosDBUrl})")
742
  if "chat_history" not in st.session_state:
743
  st.session_state.chat_history = []
744
+ # NEW: Initialize container for auto-save editor access
745
+ st.session_state.current_container = None
746
  # Auth & Cosmos client initialization
747
  if Key:
748
  st.session_state.primary_key = Key
 
750
  else:
751
  st.error("Missing Cosmos Key 🔑❌")
752
  return
753
+ # Sidebar: New Item, Add Field, Vector Search, New AI Record buttons
754
+ st.sidebar.markdown("## 🛠️ Item Management")
755
+ # New Item button
756
+ if st.sidebar.button("New Item"):
757
+ if st.session_state.get("current_container"):
758
+ new_doc = new_item_default(st.session_state.current_container)
759
+ if new_doc:
760
+ st.session_state.doc_editor = json.dumps(new_doc, indent=2)
761
+ else:
762
+ st.warning("No container selected!")
763
+ # Add Field UI inputs
764
+ st.sidebar.text_input("New Field Key", key="new_field_key")
765
+ st.sidebar.text_input("New Field Value", key="new_field_value")
766
+ if st.sidebar.button("Add Field"):
767
+ if "doc_editor" in st.session_state:
768
+ add_field_to_doc()
769
+ else:
770
+ st.warning("No document loaded to add a field.")
771
+ # New AI Record button
772
+ if st.sidebar.button("New AI Record"):
773
+ if st.session_state.get("current_container"):
774
+ new_ai_record(st.session_state.current_container)
775
+ else:
776
+ st.warning("No container selected!")
777
+ # New Portal Links Record button
778
+ if st.sidebar.button("New Links Record"):
779
+ if st.session_state.get("current_container"):
780
+ new_links_record(st.session_state.current_container)
781
+ else:
782
+ st.warning("No container selected!")
783
+ # Vector Search UI
784
+ st.sidebar.markdown("## 🔍 Vector Search")
785
+ search_keyword = st.sidebar.text_input("Search Keyword", key="vector_search_keyword")
786
+ if st.sidebar.button("Search"):
787
+ if st.session_state.get("current_container"):
788
+ results = vector_keyword_search(search_keyword, st.session_state.current_container)
789
+ st.sidebar.write(f"Found {len(results)} results:")
790
+ for res in results:
791
+ st.sidebar.code(json.dumps(res, indent=2), language="json")
792
+ else:
793
+ st.warning("No container selected for search!")
794
+ # Display Langchain functions
795
+ display_langchain_functions()
796
+ # Navigator and container selection
797
+ try:
798
+ if st.session_state.get("client") is None:
799
+ st.session_state.client = CosmosClient(ENDPOINT, credential=st.session_state.primary_key)
800
+ st.sidebar.title("🐙 Navigator")
801
+ databases = get_databases(st.session_state.client)
802
+ selected_db = st.sidebar.selectbox("🗃️ DB", databases)
803
+ # Friendly portal link already shown above
804
+ if selected_db != st.session_state.get("selected_database"):
805
+ st.session_state.selected_database = selected_db
806
+ st.session_state.selected_container = None
807
+ st.session_state.selected_document_id = None
808
+ st.session_state.current_index = 0
809
+ st.rerun()
810
+ if st.session_state.selected_database:
811
+ database = st.session_state.client.get_database_client(st.session_state.selected_database)
812
+ # New Container creation UI
813
+ if "show_new_container_form" not in st.session_state:
814
+ st.session_state.show_new_container_form = False
815
+ if st.sidebar.button("🆕 New Container"):
816
+ st.session_state.show_new_container_form = True
817
+ if st.session_state.show_new_container_form:
818
+ with st.sidebar.form("new_container_form"):
819
+ new_container_id = st.text_input("Container ID", value="aiml-container")
820
+ new_partition_key = st.text_input("Partition Key", value="/pk")
821
+ new_analytical = st.checkbox("Enable Analytical Store", value=True)
822
+ submitted = st.form_submit_button("Create Container")
823
+ if submitted:
824
+ analytical_ttl = -1 if new_analytical else None
825
+ new_container = create_new_container(
826
+ database,
827
+ new_container_id,
828
+ new_partition_key,
829
+ analytical_storage_ttl=analytical_ttl
830
+ )
831
+ if new_container:
832
+ st.success(f"Container '{new_container_id}' created.")
833
+ # Insert default templated item for image prompts
834
+ default_id = generate_unique_id()
835
+ default_item = {
836
+ "id": default_id,
837
+ "pk": default_id,
838
+ "name": "Default Image Prompt",
839
+ "prompt": "Enter your image prompt here",
840
+ "timestamp": datetime.now().isoformat(),
841
+ "type": "image_prompt"
842
+ }
843
+ insert_success, insert_message = insert_record(new_container, default_item)
844
+ if insert_success:
845
+ st.info("Default templated item created in new container.")
846
+ else:
847
+ st.error(f"Default item insertion error: {insert_message}")
848
+ st.session_state.show_new_container_form = False
849
+ st.session_state.new_container_created = new_container_id
850
+ st.rerun()
851
+ # Update container list
852
+ containers = get_containers(database)
853
+ if "new_container_created" in st.session_state and st.session_state.new_container_created not in containers:
854
+ containers.append(st.session_state.new_container_created)
855
+ selected_container = st.sidebar.selectbox("📁 Container", containers)
856
+ if selected_container != st.session_state.get("selected_container"):
857
+ st.session_state.selected_container = selected_container
858
  st.session_state.selected_document_id = None
859
  st.session_state.current_index = 0
860
  st.rerun()
861
+ if st.session_state.selected_container:
862
+ container = database.get_container_client(st.session_state.selected_container)
863
+ st.session_state.current_container = container # For auto-save functions
864
+ if st.sidebar.button("📦 Export"):
865
+ download_link = archive_current_container(st.session_state.selected_database, st.session_state.selected_container, st.session_state.client)
866
+ if download_link.startswith('<a'):
867
+ st.markdown(download_link, unsafe_allow_html=True)
868
+ else:
869
+ st.error(download_link)
870
+ documents = get_documents(container)
871
+ total_docs = len(documents)
872
+ num_docs = st.slider("Docs", 1, 20, 1)
873
+ documents_to_display = documents[:num_docs] if total_docs > num_docs else documents
874
+ st.sidebar.info(f"Showing {len(documents_to_display)} docs")
875
+ # Document Viewer / Editor
876
+ view_options = ['Markdown', 'Code', 'Run AI', 'Clone', 'New']
877
+ selected_view = st.sidebar.selectbox("View", view_options, index=1)
878
+ if selected_view == 'Markdown':
879
+ st.markdown("#### 📄 Markdown")
880
+ if documents:
881
+ doc = documents[st.session_state.current_index]
882
+ content = json.dumps(doc, indent=2)
883
+ st.markdown(f"```json\n{content}\n```")
884
+ col_prev, col_next = st.columns(2)
885
+ with col_prev:
886
+ if st.button("⬅️") and st.session_state.current_index > 0:
887
+ st.session_state.current_index -= 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888
  st.rerun()
889
+ with col_next:
890
+ if st.button("➡️") and st.session_state.current_index < total_docs - 1:
891
+ st.session_state.current_index += 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
892
  st.rerun()
893
+ elif selected_view == 'Code':
894
+ st.markdown("#### 💻 Code Editor")
895
+ if documents:
896
+ doc = documents[st.session_state.current_index]
897
+ # If a new document is loaded via New Item, load it into doc_editor
898
+ if "doc_editor" not in st.session_state:
899
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
900
+ edited = st.text_area("Edit JSON", value=st.session_state.doc_editor, height=300, key="doc_editor", on_change=lambda: auto_save_edit())
901
+ col_prev, col_next = st.columns(2)
902
+ with col_prev:
903
+ if st.button("⬅️") and st.session_state.current_index > 0:
904
+ st.session_state.current_index -= 1
905
+ st.rerun()
906
+ with col_next:
907
+ if st.button("➡️") and st.session_state.current_index < total_docs - 1:
908
+ st.session_state.current_index += 1
909
+ st.rerun()
910
+ col_save, col_delete = st.columns(2)
911
+ with col_save:
912
+ if st.button("💾 Save", key=f'save_{st.session_state.current_index}'):
913
+ try:
914
+ updated_doc = json.loads(edited)
915
+ container.upsert_item(body=updated_doc)
916
+ st.success(f"Saved {updated_doc['id']}")
917
+ st.rerun()
918
+ except Exception as e:
919
+ st.error(f"Save err: {str(e)}")
920
+ with col_delete:
921
+ if st.button("🗑️ Delete", key=f'delete_{st.session_state.current_index}'):
922
  try:
923
+ current_doc = json.loads(edited)
924
+ success, message = delete_record(container, current_doc)
 
 
 
925
  if success:
926
+ st.success(message)
927
  st.rerun()
928
  else:
929
  st.error(message)
930
  except Exception as e:
931
+ st.error(f"Delete err: {str(e)}")
932
+ if "delete_log" in st.session_state and st.session_state.delete_log:
933
+ st.subheader("Delete Log")
934
+ for log_entry in st.session_state.delete_log[-5:]:
935
+ st.write(log_entry)
936
+ elif selected_view == 'Run AI':
937
+ st.markdown("#### 🤖 Run AI (stub)")
938
+ st.info("AI functionality not implemented.")
939
+ elif selected_view == 'Clone':
940
+ st.markdown("#### 📄 Clone")
941
+ if documents:
942
+ doc = documents[st.session_state.current_index]
943
+ st.markdown(f"Original ID: {doc.get('id', '')}")
944
+ new_id = st.text_input("New ID", value=generate_unique_id(), key='new_clone_id')
945
+ new_name = st.text_input("New Name", value=f"Clone_{new_id[:8]}", key='new_clone_name')
946
+ new_doc = {'id': new_id, 'pk': new_id, 'name': new_name, **{k: v for k, v in doc.items() if k not in ['id', 'name', 'pk', '_rid', '_self', '_etag', '_attachments', '_ts']}}
947
+ doc_str = st.text_area("Edit JSON", value=json.dumps(new_doc, indent=2), height=300, key='clone_preview')
948
+ col1, col2 = st.columns(2)
949
+ with col1:
950
+ if st.button("🔄 Regenerate"):
951
+ new_id = generate_unique_id()
952
+ st.session_state.new_clone_id = new_id
953
+ st.rerun()
954
+ with col2:
955
+ if st.button("💾 Save Clone"):
956
+ try:
957
+ final_doc = json.loads(doc_str)
958
+ for field in ['_rid', '_self', '_etag', '_attachments', '_ts']:
959
+ final_doc.pop(field, None)
960
+ container.create_item(body=final_doc)
961
+ st.success(f"Cloned {final_doc['id']}")
962
+ st.rerun()
963
+ except Exception as e:
964
+ st.error(f"Clone err: {str(e)}")
965
+ col_prev, col_next = st.columns(2)
966
+ with col_prev:
967
+ if st.button("⬅️") and st.session_state.current_index > 0:
968
+ st.session_state.current_index -= 1
969
+ st.rerun()
970
+ with col_next:
971
+ if st.button("➡️") and st.session_state.current_index < total_docs - 1:
972
+ st.session_state.current_index += 1
973
+ st.rerun()
974
+ elif selected_view == 'New':
975
+ st.markdown("#### ➕ New Doc")
976
+ if st.button("🤖 Auto-Gen"):
977
+ auto_doc = {
978
+ "id": generate_unique_id(),
979
+ "pk": generate_unique_id(),
980
+ "name": f"Auto {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
981
+ "content": "Auto-generated record.",
982
+ "timestamp": datetime.now().isoformat()
983
+ }
984
+ success, message = insert_record(container, auto_doc)
985
+ if success:
986
+ st.success(message)
987
+ st.rerun()
988
+ else:
989
+ st.error(message)
990
+ else:
991
+ new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
992
+ default_doc = {
993
+ "id": new_id,
994
+ "pk": new_id,
995
+ "name": "New Doc",
996
+ "content": "",
997
+ "timestamp": datetime.now().isoformat()
998
+ }
999
+ new_doc_str = st.text_area("JSON", value=json.dumps(default_doc, indent=2), height=300)
1000
+ if st.button("➕ Create"):
1001
+ try:
1002
+ cleaned = preprocess_text(new_doc_str)
1003
+ new_doc = json.loads(cleaned)
1004
+ new_doc['id'] = new_id
1005
+ new_doc['pk'] = new_id
1006
+ success, message = insert_record(container, new_doc)
1007
+ if success:
1008
+ st.success(f"Created {new_doc['id']}")
1009
+ st.rerun()
1010
+ else:
1011
+ st.error(message)
1012
+ except Exception as e:
1013
+ st.error(f"Create err: {str(e)}")
1014
  st.subheader(f"📊 {st.session_state.selected_container}")
1015
  if documents_to_display:
1016
  df = pd.DataFrame(documents_to_display)
1017
  st.dataframe(df)
1018
  else:
1019
  st.info("No docs.")
1020
+ update_file_management_section()
1021
+ except exceptions.CosmosHttpResponseError as e:
1022
+ st.error(f"Cosmos error: {str(e)} 🚨")
1023
+ except Exception as e:
1024
+ st.error(f"Error: {str(e)} 😱")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1025
  if st.session_state.logged_in and st.sidebar.button("🚪 Logout"):
1026
  st.markdown("#### 🚪 Logout")
1027
  st.session_state.logged_in = False
 
1035
 
1036
  if __name__ == "__main__":
1037
  main()
1038
+
1039
+ # =============================================================================
1040
+ # ───────────── Additional Blank Lines for Spacing (~1500 lines total) ─────────────
1041
+ # =============================================================================
1042
+ #
1043
+ #
1044
+ #
1045
+ #
1046
+ #
1047
+ #
1048
+ #
1049
+ #
1050
+ #
1051
+ #
1052
+ #
1053
+ #
1054
+ #
1055
+ #
1056
+ #
1057
+ #
1058
+ #
1059
+ #
1060
+ #
1061
+ #
1062
+ #
1063
+ #
1064
+ #
1065
+ #
1066
+ #
1067
+ #
1068
+ #
1069
+ #
1070
+ #
1071
+ #
1072
+ #
1073
+ #
1074
+ #
1075
+ #
1076
+ #
1077
+ #
1078
+ #
1079
+ #
1080
+ #
1081
+ #
1082
+ #
1083
+ #
1084
+ #
1085
+ #
1086
+ #
1087
+ #
1088
+ #
1089
+ #
1090
+ #
1091
+ #
1092
+ #
1093
+ #
1094
+ #
1095
+ #
1096
+ #
1097
+ #
1098
+ #
1099
+ #
1100
+ #
1101
+ #
1102
+ #
1103
+ #
1104
+ #
1105
+ #
1106
+ #
1107
+ #
1108
+ #
1109
+ #
1110
+ #
1111
+ #
1112
+ #
1113
+ #
1114
+ #
1115
+ #
1116
+ #
1117
+ #
1118
+ #
1119
+ #
1120
+ #
1121
+ #
1122
+ #
1123
+ #
1124
+ #
1125
+ #
1126
+ #
1127
+ #
1128
+ #
1129
+ #
1130
+ #
1131
+ #
1132
+ #
1133
+ #
1134
+ #
1135
+ #
1136
+ #
1137
+ #
1138
+ #
1139
+ #
1140
+ #
1141
+ #
1142
+ #
1143
+ #
1144
+ #
1145
+ #
1146
+ #
1147
+ #
1148
+ #
1149
+ #
1150
+ #
1151
+ #
1152
+ #
1153
+ #
1154
+ #
1155
+ #
1156
+ #
1157
+ #
1158
+ #
1159
+ #
1160
+ #
1161
+ #
1162
+ #
1163
+ #
1164
+ #
1165
+ #
1166
+ #
1167
+ #
1168
+ #
1169
+ #
1170
+ #
1171
+ #
1172
+ #
1173
+ #
1174
+ #
1175
+ #
1176
+ #
1177
+ #
1178
+ #
1179
+ #
1180
+ #
1181
+ #
1182
+ #
1183
+ #
1184
+ #
1185
+ #
1186
+ #
1187
+ #
1188
+ #
1189
+ #
1190
+ #
1191
+ #
1192
+ # End of app.py