awacke1 commited on
Commit
d5aded6
Β·
verified Β·
1 Parent(s): 5894b31

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +487 -63
app.py CHANGED
@@ -3,43 +3,77 @@
3
  # =============================================================================
4
  import base64
5
  import glob
 
6
  import json
7
  import os
8
  import pandas as pd
9
  import pytz
 
10
  import re
11
  import shutil
12
  import streamlit as st
13
  import time
 
14
  import uuid
15
  import zipfile
 
16
  from azure.cosmos import CosmosClient, PartitionKey, exceptions
17
  from datetime import datetime
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  # =============================================================================
20
  # ───────────── APP CONFIGURATION ─────────────
21
  # =============================================================================
22
  Site_Name = 'πŸ™ GitCosmos'
23
  title = "πŸ™ GitCosmos"
 
 
 
24
  st.set_page_config(
25
  page_title=title,
26
- page_icon='πŸ™πŸŒŒπŸ’«',
27
  layout="wide",
28
- initial_sidebar_state="auto"
 
 
 
 
 
29
  )
30
 
31
- # Cosmos DB Configuration
32
  ENDPOINT = "https://acae-afd.documents.azure.com:443/"
33
- Key = os.environ.get("Key") # Ensure this is set in your environment
 
 
 
34
 
35
  # =============================================================================
36
  # ───────────── HELPER FUNCTIONS ─────────────
37
  # =============================================================================
38
- def generate_unique_id():
39
- timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
40
- unique_uuid = str(uuid.uuid4())
41
- return f"{timestamp}-{unique_uuid}"
42
-
43
  def get_download_link(file_path):
44
  with open(file_path, "rb") as file:
45
  contents = file.read()
@@ -47,6 +81,40 @@ def get_download_link(file_path):
47
  file_name = os.path.basename(file_path)
48
  return f'<a href="data:file/txt;base64,{b64}" download="{file_name}">Download {file_name} πŸ“‚</a>'
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  def sanitize_json_text(text):
51
  text = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
52
  text = text.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
@@ -97,6 +165,27 @@ def delete_record(container, record):
97
  except Exception as e:
98
  return False, f"Error: {str(e)} 😱"
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def archive_current_container(database_name, container_name, client):
101
  try:
102
  base_dir = "./cosmos_archive"
@@ -131,6 +220,129 @@ def create_new_container(database, container_id, partition_key_path):
131
  st.error(f"Error creating container: {str(e)}")
132
  return None
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  # =============================================================================
135
  # ───────────── UI FUNCTIONS ─────────────
136
  # =============================================================================
@@ -150,31 +362,33 @@ def edit_all_documents(container):
150
  if doc_key not in st.session_state:
151
  st.session_state[doc_key] = json.dumps(doc, indent=2)
152
  edited_content = st.text_area("Edit JSON", value=st.session_state[doc_key], height=300, key=doc_key)
153
- if st.button("πŸ’Ύ Save", key=f"save_{doc['id']}"):
154
- try:
155
- updated_doc = json.loads(sanitize_json_text(edited_content))
156
- # Preserve system fields and identity
157
- updated_doc['id'] = doc['id']
158
- updated_doc['pk'] = doc.get('pk', doc['id'])
159
- for field in ['_ts', '_rid', '_self', '_etag', '_attachments']:
160
- updated_doc.pop(field, None) # Remove system fields before upsert
161
- success, message = update_record(container, updated_doc)
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  if success:
163
- st.success(f"Saved {doc['id']}")
164
- st.session_state[doc_key] = json.dumps(updated_doc, indent=2)
165
  else:
166
  st.error(message)
167
- except json.JSONDecodeError:
168
- st.error("Invalid JSON format.")
169
- except Exception as e:
170
- st.error(f"Save error: {str(e)}")
171
- if st.button("πŸ—‘οΈ Delete", key=f"delete_{doc['id']}"):
172
- success, message = delete_record(container, doc)
173
- if success:
174
- st.success(message)
175
- st.rerun()
176
- else:
177
- st.error(message)
178
 
179
  def new_item_default(container):
180
  new_id = generate_unique_id()
@@ -193,37 +407,228 @@ def new_item_default(container):
193
  else:
194
  st.error(f"Error creating new item: {message}")
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  # =============================================================================
197
  # ───────────── MAIN FUNCTION ─────────────
198
  # =============================================================================
199
  def main():
200
- st.title("πŸ™ GitCosmos - CosmosDB Manager")
201
-
202
- # Initialize session state
203
- if "client" not in st.session_state:
204
- if not Key:
205
- st.error("Missing CosmosDB Key πŸ”‘βŒ")
206
- return
207
- st.session_state.client = CosmosClient(ENDPOINT, credential=Key)
208
- st.session_state.selected_database = None
209
- st.session_state.selected_container = None
 
 
210
 
211
  # Sidebar: Hierarchical Navigation
212
  st.sidebar.title("πŸ™ Navigator")
213
-
214
  # Databases Section
215
- st.sidebar.subheader("Databases")
 
 
216
  databases = get_databases(st.session_state.client)
217
- selected_db = st.sidebar.selectbox("πŸ—ƒοΈ Select Database", databases, key="db_select")
218
- if selected_db != st.session_state.selected_database:
219
  st.session_state.selected_database = selected_db
220
  st.session_state.selected_container = None
 
221
  st.rerun()
222
 
223
  # Containers Section
224
  if st.session_state.selected_database:
225
  database = st.session_state.client.get_database_client(st.session_state.selected_database)
226
- st.sidebar.subheader("Containers")
227
  if st.sidebar.button("πŸ†• New Container"):
228
  with st.sidebar.form("new_container_form"):
229
  container_id = st.text_input("Container ID", "new-container")
@@ -234,33 +639,52 @@ def main():
234
  st.success(f"Container '{container_id}' created!")
235
  st.rerun()
236
  containers = get_containers(database)
237
- selected_container = st.sidebar.selectbox("πŸ“ Select Container", containers, key="container_select")
238
- if selected_container != st.session_state.selected_container:
239
  st.session_state.selected_container = selected_container
 
240
  st.rerun()
241
 
242
  # Actions Section
243
- st.sidebar.subheader("Actions")
244
- if st.session_state.selected_container and st.sidebar.button("πŸ“¦ Export Container"):
245
- download_link = archive_current_container(st.session_state.selected_database, st.session_state.selected_container, st.session_state.client)
246
- if download_link.startswith('<a'):
247
- st.sidebar.markdown(download_link, unsafe_allow_html=True)
248
- else:
249
- st.sidebar.error(download_link)
250
 
251
  # Items Section
252
- st.sidebar.subheader("Items")
253
- if st.session_state.selected_container:
254
  if st.sidebar.button("βž• New Item"):
255
- container = database.get_container_client(st.session_state.selected_container)
256
- new_item_default(container)
 
 
 
 
 
 
 
 
257
 
258
- # Central Area: Display and Edit All Documents
259
- if st.session_state.selected_container:
260
- container = database.get_container_client(st.session_state.selected_container)
261
- edit_all_documents(container)
262
  else:
263
  st.info("Select a database and container to view and edit documents.")
264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  if __name__ == "__main__":
266
  main()
 
3
  # =============================================================================
4
  import base64
5
  import glob
6
+ import hashlib
7
  import json
8
  import os
9
  import pandas as pd
10
  import pytz
11
+ import random
12
  import re
13
  import shutil
14
  import streamlit as st
15
  import time
16
+ import traceback
17
  import uuid
18
  import zipfile
19
+ from PIL import Image
20
  from azure.cosmos import CosmosClient, PartitionKey, exceptions
21
  from datetime import datetime
22
+ from git import Repo
23
+ from github import Github
24
+ from gradio_client import Client
25
+ import tempfile
26
+ import io
27
+ import requests
28
+ import numpy as np
29
+ from urllib.parse import quote
30
+
31
+ # =============================================================================
32
+ # ───────────── EXTERNAL HELP LINKS ─────────────
33
+ # =============================================================================
34
+ external_links = [
35
+ {"title": "CosmosDB GenAI Full Text Search", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/gen-ai/full-text-search", "emoji": "πŸ’»"},
36
+ {"title": "CosmosDB SQL API Client Library", "url": "https://learn.microsoft.com/en-us/python/api/overview/azure/cosmos-readme?view=azure-python", "emoji": "πŸ’»"},
37
+ {"title": "CosmosDB Index and Query Vectors", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-python-vector-index-query", "emoji": "πŸ’»"},
38
+ {"title": "CosmosDB NoSQL Materialized Views", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/materialized-views", "emoji": "πŸ’»"},
39
+ {"title": "LangChain Vector Store Guide", "url": "https://python.langchain.com/docs/integrations/vectorstores/azure_cosmos_db_no_sql/", "emoji": "πŸ’»"},
40
+ {"title": "Vector Database Prompt Engineering RAG for Python", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/vector-database?source=recommendations", "emoji": "πŸ’»"},
41
+ {"title": "MergeKit Official GitHub", "url": "https://github.com/arcee-ai/MergeKit", "emoji": "πŸ’»"},
42
+ {"title": "MergeKit Sample Usage", "url": "https://github.com/arcee-ai/MergeKit#examples", "emoji": "πŸ“š"},
43
+ {"title": "DistillKit Official GitHub", "url": "https://github.com/arcee-ai/DistillKit", "emoji": "πŸ’»"},
44
+ {"title": "DistillKit Sample Usage", "url": "https://github.com/arcee-ai/DistillKit#usage", "emoji": "πŸ“š"},
45
+ {"title": "arcee.ai Official Website", "url": "https://arcee.ai", "emoji": "🌐"},
46
+ ]
47
 
48
  # =============================================================================
49
  # ───────────── APP CONFIGURATION ─────────────
50
  # =============================================================================
51
  Site_Name = 'πŸ™ GitCosmos'
52
  title = "πŸ™ GitCosmos"
53
+ helpURL = 'https://huggingface.co/awacke1'
54
+ bugURL = 'https://huggingface.co/spaces/awacke1/AzureCosmosDBUI/'
55
+ icons = 'πŸ™πŸŒŒπŸ’«'
56
  st.set_page_config(
57
  page_title=title,
58
+ page_icon=icons,
59
  layout="wide",
60
+ initial_sidebar_state="auto",
61
+ menu_items={
62
+ 'Get Help': helpURL,
63
+ 'Report a bug': bugURL,
64
+ 'About': title
65
+ }
66
  )
67
 
 
68
  ENDPOINT = "https://acae-afd.documents.azure.com:443/"
69
+ DATABASE_NAME = os.environ.get("COSMOS_DATABASE_NAME")
70
+ CONTAINER_NAME = os.environ.get("COSMOS_CONTAINER_NAME")
71
+ Key = os.environ.get("Key")
72
+ CosmosDBUrl = 'https://portal.azure.com/#@AaronCWackergmail.onmicrosoft.com/resource/subscriptions/003fba60-5b3f-48f4-ab36-3ed11bc40816/resourceGroups/datasets/providers/Microsoft.DocumentDB/databaseAccounts/acae-afd/dataExplorer'
73
 
74
  # =============================================================================
75
  # ───────────── HELPER FUNCTIONS ─────────────
76
  # =============================================================================
 
 
 
 
 
77
  def get_download_link(file_path):
78
  with open(file_path, "rb") as file:
79
  contents = file.read()
 
81
  file_name = os.path.basename(file_path)
82
  return f'<a href="data:file/txt;base64,{b64}" download="{file_name}">Download {file_name} πŸ“‚</a>'
83
 
84
+ def generate_unique_id():
85
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
86
+ unique_uuid = str(uuid.uuid4())
87
+ return f"{timestamp}-{unique_uuid}"
88
+
89
+ def generate_filename(prompt, file_type):
90
+ central = pytz.timezone('US/Central')
91
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
92
+ safe_prompt = re.sub(r'\W+', '', prompt)[:90]
93
+ return f"{safe_date_time}{safe_prompt}.{file_type}"
94
+
95
+ def create_file(filename, prompt, response, should_save=True):
96
+ if should_save:
97
+ with open(filename, 'w', encoding='utf-8') as file:
98
+ file.write(prompt + "\n\n" + response)
99
+
100
+ def load_file(file_name):
101
+ with open(file_name, "r", encoding='utf-8') as file:
102
+ return file.read()
103
+
104
+ def create_zip_of_files(files):
105
+ zip_name = "all_files.zip"
106
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
107
+ for file in files:
108
+ zipf.write(file)
109
+ return zip_name
110
+
111
+ def preprocess_text(text):
112
+ text = text.replace('\r\n', '\\n').replace('\r', '\\n').replace('\n', '\\n')
113
+ text = text.replace('"', '\\"')
114
+ text = re.sub(r'[\t]', ' ', text)
115
+ text = re.sub(r'[^\x00-\x7F]+', '', text)
116
+ return text.strip()
117
+
118
  def sanitize_json_text(text):
119
  text = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
120
  text = text.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
 
165
  except Exception as e:
166
  return False, f"Error: {str(e)} 😱"
167
 
168
+ def save_to_cosmos_db(container, query, response1, response2):
169
+ try:
170
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
171
+ unique_uuid = str(uuid.uuid4())
172
+ new_id = f"{timestamp}-{unique_uuid}"
173
+ record = {
174
+ "id": new_id,
175
+ "pk": new_id,
176
+ "name": new_id,
177
+ "query": query,
178
+ "response1": response1,
179
+ "response2": response2,
180
+ "timestamp": datetime.utcnow().isoformat(),
181
+ "type": "ai_response",
182
+ "version": "1.0"
183
+ }
184
+ container.create_item(body=record)
185
+ st.success(f"Saved: {record['id']}")
186
+ except Exception as e:
187
+ st.error(f"Save error: {str(e)}")
188
+
189
  def archive_current_container(database_name, container_name, client):
190
  try:
191
  base_dir = "./cosmos_archive"
 
220
  st.error(f"Error creating container: {str(e)}")
221
  return None
222
 
223
+ # =============================================================================
224
+ # ───────────── GITHUB FUNCTIONS ─────────────
225
+ # =============================================================================
226
+ def download_github_repo(url, local_path):
227
+ if os.path.exists(local_path):
228
+ shutil.rmtree(local_path)
229
+ Repo.clone_from(url, local_path)
230
+
231
+ def create_zip_file(source_dir, output_filename):
232
+ shutil.make_archive(output_filename, 'zip', source_dir)
233
+
234
+ def create_repo(g, repo_name):
235
+ user = g.get_user()
236
+ return user.create_repo(repo_name)
237
+
238
+ def push_to_github(local_path, repo, github_token):
239
+ repo_url = f"https://{github_token}@github.com/{repo.full_name}.git"
240
+ local_repo = Repo(local_path)
241
+ if 'origin' in [remote.name for remote in local_repo.remotes]:
242
+ origin = local_repo.remote('origin')
243
+ origin.set_url(repo_url)
244
+ else:
245
+ origin = local_repo.create_remote('origin', repo_url)
246
+ if not local_repo.heads:
247
+ local_repo.git.checkout('-b', 'main')
248
+ current_branch = 'main'
249
+ else:
250
+ current_branch = local_repo.active_branch.name
251
+ local_repo.git.add(A=True)
252
+ if local_repo.is_dirty():
253
+ local_repo.git.commit('-m', 'Initial commit')
254
+ origin.push(refspec=f'{current_branch}:{current_branch}')
255
+
256
+ # =============================================================================
257
+ # ───────────── FILE & MEDIA MANAGEMENT FUNCTIONS ─────────────
258
+ # =============================================================================
259
+ def display_file_viewer(file_path):
260
+ content = load_file(file_path)
261
+ if content:
262
+ st.markdown("### πŸ“„ File Viewer")
263
+ st.markdown(f"**{file_path}**")
264
+ file_stats = os.stat(file_path)
265
+ st.markdown(f"**Mod:** {datetime.fromtimestamp(file_stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')} | **Size:** {file_stats.st_size} bytes")
266
+ st.markdown("---")
267
+ st.markdown(content)
268
+ st.download_button("⬇️", data=content, file_name=os.path.basename(file_path), mime="text/markdown")
269
+
270
+ def display_file_editor(file_path):
271
+ if 'file_content' not in st.session_state:
272
+ st.session_state.file_content = {}
273
+ if file_path not in st.session_state.file_content:
274
+ content = load_file(file_path)
275
+ if content:
276
+ st.session_state.file_content[file_path] = content
277
+ st.markdown("### ✏️ Edit File")
278
+ st.markdown(f"**Editing:** {file_path}")
279
+ new_content = st.text_area("Edit:", value=st.session_state.file_content.get(file_path, ""), height=400, key=f"editor_{hash(file_path)}")
280
+ col1, col2 = st.columns([1, 5])
281
+ with col1:
282
+ if st.button("πŸ’Ύ Save"):
283
+ sanitized = sanitize_json_text(new_content)
284
+ try:
285
+ json.loads(sanitized)
286
+ with open(file_path, 'w', encoding='utf-8') as file:
287
+ file.write(sanitized)
288
+ st.session_state.file_content[file_path] = sanitized
289
+ st.success("Saved! πŸŽ‰")
290
+ time.sleep(1)
291
+ st.rerun()
292
+ except Exception as e:
293
+ st.error(f"Save error: {str(e)}")
294
+ with col2:
295
+ st.download_button("⬇️", data=new_content, file_name=os.path.basename(file_path), mime="text/markdown")
296
+
297
+ def update_file_management_section():
298
+ if 'file_view_mode' not in st.session_state:
299
+ st.session_state.file_view_mode = None
300
+ if 'current_file' not in st.session_state:
301
+ st.session_state.current_file = None
302
+ all_files = sorted(glob.glob("*.md"), reverse=True)
303
+ st.sidebar.subheader("πŸ“ Files")
304
+ if st.sidebar.button("πŸ—‘ Delete All"):
305
+ for file in all_files:
306
+ os.remove(file)
307
+ st.session_state.file_content = {}
308
+ st.session_state.current_file = None
309
+ st.session_state.file_view_mode = None
310
+ st.rerun()
311
+ if st.sidebar.button("⬇️ Download All"):
312
+ zip_file = create_zip_of_files(all_files)
313
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
314
+ for file in all_files:
315
+ col1, col2, col3, col4 = st.sidebar.columns([1, 3, 1, 1])
316
+ with col1:
317
+ if st.button("🌐", key=f"view_{file}"):
318
+ st.session_state.current_file = file
319
+ st.session_state.file_view_mode = 'view'
320
+ st.rerun()
321
+ with col2:
322
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
323
+ with col3:
324
+ if st.button("πŸ“‚", key=f"edit_{file}"):
325
+ st.session_state.current_file = file
326
+ st.session_state.file_view_mode = 'edit'
327
+ st.rerun()
328
+ with col4:
329
+ if st.button("πŸ—‘", key=f"delete_{file}"):
330
+ os.remove(file)
331
+ if file in st.session_state.file_content:
332
+ del st.session_state.file_content[file]
333
+ if st.session_state.current_file == file:
334
+ st.session_state.current_file = None
335
+ st.session_state.file_view_mode = None
336
+ st.rerun()
337
+ st.sidebar.subheader("External Help Links")
338
+ for link in external_links:
339
+ st.sidebar.markdown(f"{link['emoji']} [{link['title']}]({link['url']})", unsafe_allow_html=True)
340
+ if st.session_state.current_file:
341
+ if st.session_state.file_view_mode == 'view':
342
+ display_file_viewer(st.session_state.current_file)
343
+ elif st.session_state.file_view_mode == 'edit':
344
+ display_file_editor(st.session_state.current_file)
345
+
346
  # =============================================================================
347
  # ───────────── UI FUNCTIONS ─────────────
348
  # =============================================================================
 
362
  if doc_key not in st.session_state:
363
  st.session_state[doc_key] = json.dumps(doc, indent=2)
364
  edited_content = st.text_area("Edit JSON", value=st.session_state[doc_key], height=300, key=doc_key)
365
+ col_save, col_delete = st.columns(2)
366
+ with col_save:
367
+ if st.button("πŸ’Ύ Save", key=f"save_{doc['id']}"):
368
+ try:
369
+ updated_doc = json.loads(sanitize_json_text(edited_content))
370
+ updated_doc['id'] = doc['id']
371
+ updated_doc['pk'] = doc.get('pk', doc['id'])
372
+ for field in ['_ts', '_rid', '_self', '_etag', '_attachments']:
373
+ updated_doc.pop(field, None)
374
+ success, message = update_record(container, updated_doc)
375
+ if success:
376
+ st.success(f"Saved {doc['id']}")
377
+ st.session_state[doc_key] = json.dumps(updated_doc, indent=2)
378
+ else:
379
+ st.error(message)
380
+ except json.JSONDecodeError:
381
+ st.error("Invalid JSON format.")
382
+ except Exception as e:
383
+ st.error(f"Save error: {str(e)}")
384
+ with col_delete:
385
+ if st.button("πŸ—‘οΈ Delete", key=f"delete_{doc['id']}"):
386
+ success, message = delete_record(container, doc)
387
  if success:
388
+ st.success(message)
389
+ st.rerun()
390
  else:
391
  st.error(message)
 
 
 
 
 
 
 
 
 
 
 
392
 
393
  def new_item_default(container):
394
  new_id = generate_unique_id()
 
407
  else:
408
  st.error(f"Error creating new item: {message}")
409
 
410
+ def add_field_to_doc():
411
+ key = st.session_state.new_field_key
412
+ value = st.session_state.new_field_value
413
+ try:
414
+ doc = json.loads(st.session_state.doc_editor)
415
+ doc[key] = value
416
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
417
+ container = st.session_state.current_container
418
+ success, message = update_record(container, doc)
419
+ if success:
420
+ st.success(f"Added field {key} πŸ‘")
421
+ else:
422
+ st.error(message)
423
+ except Exception as e:
424
+ st.error(f"Error adding field: {str(e)}")
425
+
426
+ def new_ai_record(container):
427
+ new_id = generate_unique_id()
428
+ default_doc = {
429
+ "id": new_id,
430
+ "pk": new_id,
431
+ "name": "AI Modality Record",
432
+ "function_url": "https://example.com/function",
433
+ "input_text": "### Input (markdown)\n\nType your input here.",
434
+ "output_text": "### Output (markdown)\n\nResult will appear here.",
435
+ "timestamp": datetime.now().isoformat(),
436
+ "type": "ai_modality"
437
+ }
438
+ success, message = insert_record(container, default_doc)
439
+ if success:
440
+ st.success("New AI modality record created! πŸ’‘")
441
+ st.rerun()
442
+ else:
443
+ st.error(f"Error creating AI record: {message}")
444
+
445
+ def new_links_record(container):
446
+ new_id = generate_unique_id()
447
+ links_md = "\n".join([f"- {link['emoji']} [{link['title']}]({link['url']})" for link in external_links])
448
+ default_doc = {
449
+ "id": new_id,
450
+ "pk": new_id,
451
+ "name": "Portal Links Record",
452
+ "function_url": "",
453
+ "input_text": links_md,
454
+ "output_text": "",
455
+ "timestamp": datetime.now().isoformat(),
456
+ "type": "ai_modality"
457
+ }
458
+ success, message = insert_record(container, default_doc)
459
+ if success:
460
+ st.success("New Portal Links record created! πŸ”—")
461
+ st.rerun()
462
+ else:
463
+ st.error(f"Error creating links record: {message}")
464
+
465
+ def vector_keyword_search(keyword, container):
466
+ try:
467
+ query = f"SELECT * FROM c WHERE CONTAINS(c.content, '{keyword}')"
468
+ results = list(container.query_items(query=query, enable_cross_partition_query=True))
469
+ return results
470
+ except Exception as e:
471
+ st.error(f"Search error: {str(e)}")
472
+ return []
473
+
474
+ def display_search_results(keyword, container):
475
+ results = vector_keyword_search(keyword, container)
476
+ st.markdown("### πŸ” Search Results")
477
+ for res in results:
478
+ doc_id = res.get("id", "")
479
+ with st.expander(f"Result {doc_id}"):
480
+ edited = st.text_area("Edit Document", value=json.dumps(res, indent=2), key=f"search_{doc_id}")
481
+ if st.button(f"πŸ’Ύ Save changes", key=f"save_search_{doc_id}"):
482
+ try:
483
+ updated_doc = json.loads(edited)
484
+ success, message = update_record(container, updated_doc)
485
+ if success:
486
+ st.success(f"Updated {doc_id}!")
487
+ st.rerun()
488
+ else:
489
+ st.error(message)
490
+ except Exception as e:
491
+ st.error(f"Error saving: {str(e)}")
492
+
493
+ def search_documents_ui(container):
494
+ with st.sidebar.form("search_form"):
495
+ keyword = st.text_input("Search Keyword", key="search_keyword")
496
+ if st.form_submit_button("πŸ” Search") and keyword:
497
+ display_search_results(keyword, container)
498
+
499
+ def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
500
+ try:
501
+ if isinstance(file_data, bytes):
502
+ img = Image.open(io.BytesIO(file_data))
503
+ elif hasattr(file_data, 'read'):
504
+ if hasattr(file_data, 'seek'):
505
+ file_data.seek(0)
506
+ img = Image.open(file_data)
507
+ else:
508
+ raise ValueError(f"Unsupported input: {type(file_data)}")
509
+ if img.mode != 'RGB':
510
+ img = img.convert('RGB')
511
+ aspect_ratio = img.size[0] / img.size[1]
512
+ if aspect_ratio > target_size[0] / target_size[1]:
513
+ new_width = target_size[0]
514
+ new_height = int(new_width / aspect_ratio)
515
+ else:
516
+ new_height = target_size[1]
517
+ new_width = int(new_height * aspect_ratio)
518
+ new_width = (new_width // 2) * 2
519
+ new_height = (new_height // 2) * 2
520
+ resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
521
+ final_img = Image.new('RGB', target_size, (255, 255, 255))
522
+ paste_x = (target_size[0] - new_width) // 2
523
+ paste_y = (target_size[1] - new_height) // 2
524
+ final_img.paste(resized_img, (paste_x, paste_y))
525
+ return final_img
526
+ except Exception as e:
527
+ st.error(f"Image error: {str(e)}")
528
+ return None
529
+
530
+ def add_video_generation_ui(container):
531
+ st.markdown("### πŸŽ₯ Video Generation")
532
+ col1, col2 = st.columns([2, 1])
533
+ with col1:
534
+ uploaded_file = st.file_uploader("Upload Image πŸ–ΌοΈ", type=['png', 'jpg', 'jpeg'])
535
+ with col2:
536
+ st.markdown("#### Parameters")
537
+ motion = st.slider("🌊 Motion", 1, 255, 127)
538
+ fps = st.slider("🎬 FPS", 1, 30, 6)
539
+ with st.expander("Advanced"):
540
+ use_custom = st.checkbox("Custom Seed")
541
+ seed = st.number_input("Seed", value=int(time.time() * 1000)) if use_custom else None
542
+ if uploaded_file:
543
+ file_data = uploaded_file.read()
544
+ preview1, preview2 = st.columns(2)
545
+ with preview1:
546
+ st.write("Original")
547
+ st.image(Image.open(io.BytesIO(file_data)), use_column_width=True)
548
+ with preview2:
549
+ proc_img = validate_and_preprocess_image(io.BytesIO(file_data))
550
+ if proc_img:
551
+ st.write("Processed")
552
+ st.image(proc_img, use_column_width=True)
553
+ else:
554
+ return
555
+ if st.button("πŸŽ₯ Generate"):
556
+ with st.spinner("Generating video..."):
557
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
558
+ proc_img.save(temp_file.name, format='PNG')
559
+ try:
560
+ client = Client("awacke1/stable-video-diffusion", hf_token=os.environ.get("HUGGINGFACE_TOKEN"))
561
+ result = client.predict(
562
+ image=temp_file.name,
563
+ seed=seed if seed is not None else int(time.time() * 1000),
564
+ randomize_seed=seed is None,
565
+ motion_bucket_id=motion,
566
+ fps_id=fps,
567
+ api_name="/video"
568
+ )
569
+ video_path = result[0].get('video') if isinstance(result[0], dict) else None
570
+ if video_path and os.path.exists(video_path):
571
+ video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
572
+ shutil.copy(video_path, video_filename)
573
+ st.success(f"Video generated! πŸŽ‰")
574
+ st.video(video_filename)
575
+ if container:
576
+ video_record = {
577
+ "id": generate_unique_id(),
578
+ "pk": generate_unique_id(),
579
+ "type": "generated_video",
580
+ "filename": video_filename,
581
+ "seed": seed if seed is not None else "random",
582
+ "motion": motion,
583
+ "fps": fps,
584
+ "timestamp": datetime.now().isoformat()
585
+ }
586
+ success, message = insert_record(container, video_record)
587
+ if success:
588
+ st.success("DB record saved!")
589
+ else:
590
+ st.error(f"DB error: {message}")
591
+ except Exception as e:
592
+ st.error(f"Video gen error: {str(e)}")
593
+ finally:
594
+ os.unlink(temp_file.name)
595
+
596
  # =============================================================================
597
  # ───────────── MAIN FUNCTION ─────────────
598
  # =============================================================================
599
  def main():
600
+ st.markdown("### πŸ™ GitCosmos - Cosmos & Git Hub")
601
+ st.markdown(f"[πŸ”— Portal]({CosmosDBUrl})")
602
+
603
+ if "chat_history" not in st.session_state:
604
+ st.session_state.chat_history = []
605
+ if "current_container" not in st.session_state:
606
+ st.session_state.current_container = None
607
+ if not Key:
608
+ st.error("Missing Cosmos Key πŸ”‘βŒ")
609
+ return
610
+ st.session_state.primary_key = Key
611
+ st.session_state.logged_in = True
612
 
613
  # Sidebar: Hierarchical Navigation
614
  st.sidebar.title("πŸ™ Navigator")
615
+
616
  # Databases Section
617
+ st.sidebar.subheader("πŸ—ƒοΈ Databases")
618
+ if "client" not in st.session_state:
619
+ st.session_state.client = CosmosClient(ENDPOINT, credential=Key)
620
  databases = get_databases(st.session_state.client)
621
+ selected_db = st.sidebar.selectbox("Select Database", databases, key="db_select")
622
+ if selected_db != st.session_state.get("selected_database"):
623
  st.session_state.selected_database = selected_db
624
  st.session_state.selected_container = None
625
+ st.session_state.current_container = None
626
  st.rerun()
627
 
628
  # Containers Section
629
  if st.session_state.selected_database:
630
  database = st.session_state.client.get_database_client(st.session_state.selected_database)
631
+ st.sidebar.subheader("πŸ“ Containers")
632
  if st.sidebar.button("πŸ†• New Container"):
633
  with st.sidebar.form("new_container_form"):
634
  container_id = st.text_input("Container ID", "new-container")
 
639
  st.success(f"Container '{container_id}' created!")
640
  st.rerun()
641
  containers = get_containers(database)
642
+ selected_container = st.sidebar.selectbox("Select Container", containers, key="container_select")
643
+ if selected_container != st.session_state.get("selected_container"):
644
  st.session_state.selected_container = selected_container
645
+ st.session_state.current_container = database.get_container_client(selected_container)
646
  st.rerun()
647
 
648
  # Actions Section
649
+ st.sidebar.subheader("βš™οΈ Actions")
650
+ if st.session_state.current_container:
651
+ if st.sidebar.button("πŸ“¦ Export Container"):
652
+ download_link = archive_current_container(st.session_state.selected_database, st.session_state.selected_container, st.session_state.client)
653
+ st.sidebar.markdown(download_link, unsafe_allow_html=True) if download_link.startswith('<a') else st.sidebar.error(download_link)
 
 
654
 
655
  # Items Section
656
+ st.sidebar.subheader("πŸ“‘ Items")
657
+ if st.session_state.current_container:
658
  if st.sidebar.button("βž• New Item"):
659
+ new_item_default(st.session_state.current_container)
660
+ st.sidebar.text_input("New Field Key", key="new_field_key")
661
+ st.sidebar.text_input("New Field Value", key="new_field_value")
662
+ if st.sidebar.button("βž• Add Field") and "doc_editor" in st.session_state:
663
+ add_field_to_doc()
664
+ if st.sidebar.button("πŸ€– New AI Record"):
665
+ new_ai_record(st.session_state.current_container)
666
+ if st.sidebar.button("πŸ”— New Links Record"):
667
+ new_links_record(st.session_state.current_container)
668
+ search_documents_ui(st.session_state.current_container)
669
 
670
+ # Central Area: Editable Documents
671
+ if st.session_state.current_container:
672
+ edit_all_documents(st.session_state.current_container)
 
673
  else:
674
  st.info("Select a database and container to view and edit documents.")
675
 
676
+ # Additional Features
677
+ update_file_management_section()
678
+ add_video_generation_ui(st.session_state.current_container if st.session_state.current_container else None)
679
+
680
+ # Logout
681
+ if st.session_state.logged_in and st.sidebar.button("πŸšͺ Logout"):
682
+ st.session_state.logged_in = False
683
+ st.session_state.client = None
684
+ st.session_state.selected_database = None
685
+ st.session_state.selected_container = None
686
+ st.session_state.current_container = None
687
+ st.rerun()
688
+
689
  if __name__ == "__main__":
690
  main()