awacke1 commited on
Commit
74ed01e
·
verified ·
1 Parent(s): 993d89a

Create app20.py

Browse files
Files changed (1) hide show
  1. app20.py +1438 -0
app20.py ADDED
@@ -0,0 +1,1438 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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": "CosmosDB GenAI Full Text Search", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/gen-ai/full-text-search", "emoji": "💻"},
37
+ {"title": "CosmosDB SQL API Client Library", "url": "https://learn.microsoft.com/en-us/python/api/overview/azure/cosmos-readme?view=azure-python", "emoji": "💻"},
38
+ {"title": "CosmosDB Index and Query Vectors", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-python-vector-index-query", "emoji": "💻"},
39
+ {"title": "CosmosDB NoSQL Materialized Views", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/materialized-views", "emoji": "💻"},
40
+ {"title": "LangChain Vector Store Guide", "url": "https://python.langchain.com/docs/integrations/vectorstores/azure_cosmos_db_no_sql/", "emoji": "💻"},
41
+ {"title": "Vector Database Prompt Engineering RAG for Python", "url": "https://learn.microsoft.com/en-us/azure/cosmos-db/vector-database?source=recommendations", "emoji": "💻"},
42
+ {"title": "MergeKit Official GitHub", "url": "https://github.com/arcee-ai/MergeKit", "emoji": "💻"},
43
+ {"title": "MergeKit Sample Usage", "url": "https://github.com/arcee-ai/MergeKit#examples", "emoji": "📚"},
44
+ {"title": "DistillKit Official GitHub", "url": "https://github.com/arcee-ai/DistillKit", "emoji": "💻"},
45
+ {"title": "DistillKit Sample Usage", "url": "https://github.com/arcee-ai/DistillKit#usage", "emoji": "📚"},
46
+ {"title": "arcee.ai Official Website", "url": "https://arcee.ai", "emoji": "🌐"},
47
+ ]
48
+
49
+ # =============================================================================
50
+ # ───────────── APP CONFIGURATION ─────────────
51
+ # =============================================================================
52
+ Site_Name = '🐙 GitCosmos'
53
+ title = "🐙 GitCosmos"
54
+ helpURL = 'https://huggingface.co/awacke1'
55
+ bugURL = 'https://huggingface.co/spaces/awacke1/AzureCosmosDBUI/'
56
+ icons = '🐙🌌💫'
57
+ st.set_page_config(
58
+ page_title=title,
59
+ page_icon=icons,
60
+ layout="wide",
61
+ initial_sidebar_state="auto",
62
+ menu_items={
63
+ 'Get Help': helpURL,
64
+ 'Report a bug': bugURL,
65
+ 'About': title
66
+ }
67
+ )
68
+
69
+ # Cosmos DB & App URLs
70
+ ENDPOINT = "https://acae-afd.documents.azure.com:443/"
71
+ DATABASE_NAME = os.environ.get("COSMOS_DATABASE_NAME")
72
+ CONTAINER_NAME = os.environ.get("COSMOS_CONTAINER_NAME")
73
+ Key = os.environ.get("Key")
74
+ LOCAL_APP_URL = "https://huggingface.co/spaces/awacke1/AzureCosmosDBUI"
75
+ CosmosDBUrl = 'https://portal.azure.com/#@AaronCWackergmail.onmicrosoft.com/resource/subscriptions/003fba60-5b3f-48f4-ab36-3ed11bc40816/resourceGroups/datasets/providers/Microsoft.DocumentDB/databaseAccounts/acae-afd/dataExplorer'
76
+
77
+ # =============================================================================
78
+ # ───────────── HELPER FUNCTIONS ─────────────
79
+ # =============================================================================
80
+ def get_download_link(file_path):
81
+ with open(file_path, "rb") as file:
82
+ contents = file.read()
83
+ b64 = base64.b64encode(contents).decode()
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
+ def generate_unique_id():
88
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
89
+ unique_uuid = str(uuid.uuid4())
90
+ return_value = f"{timestamp}-{unique_uuid}"
91
+ st.write('New ID: ' + return_value)
92
+ return return_value
93
+
94
+ def generate_filename(prompt, file_type):
95
+ central = pytz.timezone('US/Central')
96
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
97
+ safe_prompt = re.sub(r'\W+', '', prompt)[:90]
98
+ return f"{safe_date_time}{safe_prompt}.{file_type}"
99
+
100
+ def create_file(filename, prompt, response, should_save=True):
101
+ if not should_save:
102
+ return
103
+ with open(filename, 'w', encoding='utf-8') as file:
104
+ file.write(prompt + "\n\n" + response)
105
+
106
+ def load_file(file_name):
107
+ with open(file_name, "r", encoding='utf-8') as file:
108
+ content = file.read()
109
+ return content
110
+
111
+ def display_glossary_entity(k):
112
+ search_urls = {
113
+ "🚀": lambda k: f"/?q={k}",
114
+ "📖": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
115
+ "🔍": lambda k: f"https://www.google.com/search?q={quote(k)}",
116
+ "🎥": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
117
+ }
118
+ links_md = ' '.join([f"<a href='{url(k)}' target='_blank'>{emoji}</a>" for emoji, url in search_urls.items()])
119
+ st.markdown(f"{k} {links_md}", unsafe_allow_html=True)
120
+
121
+ def create_zip_of_files(files):
122
+ zip_name = "all_files.zip"
123
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
124
+ for file in files:
125
+ zipf.write(file)
126
+ return zip_name
127
+
128
+ def get_video_html(video_path, width="100%"):
129
+ video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
130
+ return f'''
131
+ <video width="{width}" controls autoplay loop>
132
+ <source src="{video_url}" type="video/mp4">
133
+ Your browser does not support video.
134
+ </video>
135
+ '''
136
+
137
+ def get_audio_html(audio_path, width="100%"):
138
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
139
+ return f'''
140
+ <audio controls style="width:{width}">
141
+ <source src="{audio_url}" type="audio/mpeg">
142
+ Your browser does not support audio.
143
+ </audio>
144
+ '''
145
+
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
+ # NEW: Sanitize JSON text before saving (remove problematic control characters)
154
+ def sanitize_json_text(text):
155
+ # Remove control characters except newline, carriage return, and tab
156
+ text = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', text)
157
+ # Escape newline, tab, and carriage return
158
+ text = text.replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
159
+ return text
160
+
161
+ # =============================================================================
162
+ # ───────────── COSMOS DB FUNCTIONS ─────────────
163
+ # =============================================================================
164
+ def get_databases(client):
165
+ return [db['id'] for db in client.list_databases()]
166
+
167
+ def get_containers(database):
168
+ return [container['id'] for container in database.list_containers()]
169
+
170
+ def get_documents(container, limit=None):
171
+ query = "SELECT * FROM c ORDER BY c._ts DESC"
172
+ items = list(container.query_items(query=query, enable_cross_partition_query=True, max_item_count=limit))
173
+ return items
174
+
175
+ def insert_record(container, record):
176
+ try:
177
+ container.create_item(body=record)
178
+ return True, "Inserted! 🎉"
179
+ except exceptions.CosmosHttpResponseError as e:
180
+ return False, f"HTTP error: {str(e)} 🚨"
181
+ except Exception as e:
182
+ return False, f"Error: {str(e)} 😱"
183
+
184
+ def update_record(container, updated_record):
185
+ try:
186
+ container.upsert_item(body=updated_record)
187
+ return True, f"Updated {updated_record['id']} 🛠️"
188
+ except exceptions.CosmosHttpResponseError as e:
189
+ return False, f"HTTP error: {str(e)} 🚨"
190
+ except Exception as e:
191
+ return False, f"Error: {traceback.format_exc()} 😱"
192
+
193
+ def delete_record(container, record):
194
+ try:
195
+ if "id" not in record:
196
+ return False, "Record must contain an 'id' field. 🛑"
197
+ doc_id = record["id"]
198
+ if "delete_log" not in st.session_state:
199
+ st.session_state.delete_log = []
200
+ st.session_state.delete_log.append(f"Attempting to delete document: {json.dumps(record, indent=2)}")
201
+ partition_key_value = record.get("pk", doc_id)
202
+ st.session_state.delete_log.append(f"Using ID and Partition Key: {partition_key_value}")
203
+ container.delete_item(item=doc_id, partition_key=partition_key_value)
204
+ success_msg = f"Record {doc_id} successfully deleted from Cosmos DB. 🗑️"
205
+ st.session_state.delete_log.append(success_msg)
206
+ return True, success_msg
207
+ except exceptions.CosmosResourceNotFoundError:
208
+ success_msg = f"Record {doc_id} not found in Cosmos DB (already deleted or never existed). 🗑️"
209
+ st.session_state.delete_log.append(success_msg)
210
+ return True, success_msg
211
+ except exceptions.CosmosHttpResponseError as e:
212
+ error_msg = f"HTTP error deleting {doc_id}: {str(e)}. 🚨"
213
+ st.session_state.delete_log.append(error_msg)
214
+ return False, error_msg
215
+ except Exception as e:
216
+ error_msg = f"Unexpected error deleting {doc_id}: {str(traceback.format_exc())}. 😱"
217
+ st.session_state.delete_log.append(error_msg)
218
+ return False, error_msg
219
+
220
+ def save_to_cosmos_db(container, query, response1, response2):
221
+ try:
222
+ if container:
223
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
224
+ unique_uuid = str(uuid.uuid4())
225
+ new_id = f"{timestamp}-{unique_uuid}"
226
+ record = {
227
+ "id": new_id,
228
+ "pk": new_id,
229
+ "name": new_id,
230
+ "query": query,
231
+ "response1": response1,
232
+ "response2": response2,
233
+ "timestamp": datetime.utcnow().isoformat(),
234
+ "type": "ai_response",
235
+ "version": "1.0"
236
+ }
237
+ container.create_item(body=record)
238
+ st.success(f"Saved: {record['id']}")
239
+ st.session_state.documents = get_documents(container)
240
+ else:
241
+ st.error("Cosmos container not initialized.")
242
+ except Exception as e:
243
+ st.error(f"Save error: {str(e)}")
244
+
245
+ def archive_current_container(database_name, container_name, client):
246
+ try:
247
+ base_dir = "./cosmos_archive_current_container"
248
+ if os.path.exists(base_dir):
249
+ shutil.rmtree(base_dir)
250
+ os.makedirs(base_dir)
251
+ db_client = client.get_database_client(database_name)
252
+ container_client = db_client.get_container_client(container_name)
253
+ items = list(container_client.read_all_items())
254
+ container_dir = os.path.join(base_dir, container_name)
255
+ os.makedirs(container_dir)
256
+ for item in items:
257
+ item_id = item.get('id', f"unknown_{datetime.now().strftime('%Y%m%d%H%M%S')}")
258
+ with open(os.path.join(container_dir, f"{item_id}.json"), 'w') as f:
259
+ json.dump(item, f, indent=2)
260
+ archive_name = f"{container_name}_archive_{datetime.now().strftime('%Y%m%d%H%M%S')}"
261
+ shutil.make_archive(archive_name, 'zip', base_dir)
262
+ return get_download_link(f"{archive_name}.zip")
263
+ except Exception as e:
264
+ return f"Archive error: {str(e)} 😢"
265
+
266
+ # =============================================================================
267
+ # ───────────── ADVANCED COSMOS FUNCTIONS ─────────────
268
+ # =============================================================================
269
+ def create_new_container(database, container_id, partition_key_path,
270
+ analytical_storage_ttl=None, indexing_policy=None, vector_embedding_policy=None):
271
+ try:
272
+ if analytical_storage_ttl is not None:
273
+ container = database.create_container(
274
+ id=container_id,
275
+ partition_key=PartitionKey(path=partition_key_path),
276
+ analytical_storage_ttl=analytical_storage_ttl,
277
+ indexing_policy=indexing_policy,
278
+ vector_embedding_policy=vector_embedding_policy
279
+ )
280
+ else:
281
+ container = database.create_container(
282
+ id=container_id,
283
+ partition_key=PartitionKey(path=partition_key_path),
284
+ indexing_policy=indexing_policy,
285
+ vector_embedding_policy=vector_embedding_policy
286
+ )
287
+ except exceptions.CosmosHttpResponseError as e:
288
+ if analytical_storage_ttl is not None and "analyticalStorageTtl" in str(e):
289
+ try:
290
+ container = database.create_container(
291
+ id=container_id,
292
+ partition_key=PartitionKey(path=partition_key_path),
293
+ indexing_policy=indexing_policy,
294
+ vector_embedding_policy=vector_embedding_policy
295
+ )
296
+ except Exception as e2:
297
+ st.error(f"Error creating container without analytical_storage_ttl: {str(e2)}")
298
+ return None
299
+ elif isinstance(e, exceptions.CosmosResourceExistsError):
300
+ container = database.get_container_client(container_id)
301
+ else:
302
+ st.error(f"Error creating container: {str(e)}")
303
+ return None
304
+ return container
305
+
306
+ def advanced_insert_item(container, item):
307
+ try:
308
+ container.upsert_item(item)
309
+ return True, f"Item {item.get('id', '')} inserted. ➕"
310
+ except Exception as e:
311
+ return False, str(e)
312
+
313
+ def advanced_update_item(container, item):
314
+ try:
315
+ container.upsert_item(item)
316
+ return True, f"Item {item.get('id', '')} updated. ✏️"
317
+ except Exception as e:
318
+ return False, str(e)
319
+
320
+ def advanced_delete_item(container, item_id, partition_key_value):
321
+ try:
322
+ container.delete_item(item=item_id, partition_key=partition_key_value)
323
+ return True, f"Item {item_id} deleted. 🗑️"
324
+ except Exception as e:
325
+ return False, str(e)
326
+
327
+ def vector_search(container, query_vector, vector_field, top=10, exact_search=False):
328
+ query_vector_str = json.dumps(query_vector)
329
+ query = f"""SELECT TOP {top} c.id, VectorDistance(c.{vector_field}, {query_vector_str}, {str(exact_search).lower()},
330
+ {{'dataType':'float32','distanceFunction':'cosine'}}) AS SimilarityScore
331
+ FROM c ORDER BY SimilarityScore"""
332
+ results = list(container.query_items(query=query, enable_cross_partition_query=True))
333
+ return results
334
+
335
+ # =============================================================================
336
+ # ───────────── GITHUB FUNCTIONS ─────────────
337
+ # =============================================================================
338
+ def download_github_repo(url, local_path):
339
+ if os.path.exists(local_path):
340
+ shutil.rmtree(local_path)
341
+ Repo.clone_from(url, local_path)
342
+
343
+ def create_zip_file(source_dir, output_filename):
344
+ shutil.make_archive(output_filename, 'zip', source_dir)
345
+
346
+ def create_repo(g, repo_name):
347
+ user = g.get_user()
348
+ return user.create_repo(repo_name)
349
+
350
+ def push_to_github(local_path, repo, github_token):
351
+ repo_url = f"https://{github_token}@github.com/{repo.full_name}.git"
352
+ local_repo = Repo(local_path)
353
+ if 'origin' in [remote.name for remote in local_repo.remotes]:
354
+ origin = local_repo.remote('origin')
355
+ origin.set_url(repo_url)
356
+ else:
357
+ origin = local_repo.create_remote('origin', repo_url)
358
+ if not local_repo.heads:
359
+ local_repo.git.checkout('-b', 'main')
360
+ current_branch = 'main'
361
+ else:
362
+ current_branch = local_repo.active_branch.name
363
+ local_repo.git.add(A=True)
364
+ if local_repo.is_dirty():
365
+ local_repo.git.commit('-m', 'Initial commit')
366
+ origin.push(refspec=f'{current_branch}:{current_branch}')
367
+
368
+ # =============================================================================
369
+ # ───────────── FILE & MEDIA MANAGEMENT FUNCTIONS ─────────────
370
+ # =============================================================================
371
+ def display_saved_files_in_sidebar():
372
+ all_files = sorted([f for f in glob.glob("*.md") if not f.lower().startswith('readme')], reverse=True)
373
+ st.sidebar.markdown("## 📁 Files")
374
+ for file in all_files:
375
+ col1, col2, col3 = st.sidebar.columns([6, 2, 1])
376
+ with col1:
377
+ st.markdown(f"📄 {file}")
378
+ with col2:
379
+ st.sidebar.download_button(
380
+ label="⬇️",
381
+ data=open(file, 'rb').read(),
382
+ file_name=file
383
+ )
384
+ with col3:
385
+ if st.sidebar.button("🗑", key=f"delete_{file}"):
386
+ os.remove(file)
387
+ st.rerun()
388
+
389
+ def display_file_viewer(file_path):
390
+ content = load_file(file_path)
391
+ if content:
392
+ st.markdown("### 📄 File Viewer")
393
+ st.markdown(f"**{file_path}**")
394
+ file_stats = os.stat(file_path)
395
+ st.markdown(f"**Mod:** {datetime.fromtimestamp(file_stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')} | **Size:** {file_stats.st_size} bytes")
396
+ st.markdown("---")
397
+ st.markdown(content)
398
+ st.download_button("⬇️", data=content, file_name=os.path.basename(file_path), mime="text/markdown")
399
+
400
+ def display_file_editor(file_path):
401
+ if 'file_content' not in st.session_state:
402
+ st.session_state.file_content = {}
403
+ if file_path not in st.session_state.file_content:
404
+ content = load_file(file_path)
405
+ if content is not None:
406
+ st.session_state.file_content[file_path] = content
407
+ else:
408
+ return
409
+ st.markdown("### ✏️ Edit File")
410
+ st.markdown(f"**Editing:** {file_path}")
411
+ md_tab, code_tab = st.tabs(["Markdown", "Code"])
412
+ with md_tab:
413
+ st.markdown(st.session_state.file_content[file_path])
414
+ with code_tab:
415
+ new_content = st.text_area("Edit:", value=st.session_state.file_content[file_path], height=400, key=f"editor_{hash(file_path)}", on_change=lambda: auto_save_edit())
416
+ col1, col2 = st.columns([1, 5])
417
+ with col1:
418
+ if st.button("💾 Save"):
419
+ sanitized = sanitize_json_text(new_content)
420
+ try:
421
+ json.loads(sanitized)
422
+ except Exception as e:
423
+ st.error(f"Sanitization failed: {str(e)}")
424
+ return
425
+ if save_file_content(file_path, sanitized):
426
+ st.session_state.file_content[file_path] = sanitized
427
+ st.success("Saved! 🎉")
428
+ time.sleep(1)
429
+ st.rerun()
430
+ with col2:
431
+ st.download_button("⬇️", data=new_content, file_name=os.path.basename(file_path), mime="text/markdown")
432
+
433
+ def save_file_content(file_path, content):
434
+ try:
435
+ with open(file_path, 'w', encoding='utf-8') as file:
436
+ file.write(content)
437
+ return True
438
+ except Exception as e:
439
+ st.error(f"Save error: {str(e)}")
440
+ return False
441
+
442
+ def update_file_management_section():
443
+ if 'file_view_mode' not in st.session_state:
444
+ st.session_state.file_view_mode = None
445
+ if 'current_file' not in st.session_state:
446
+ st.session_state.current_file = None
447
+ if 'file_content' not in st.session_state:
448
+ st.session_state.file_content = {}
449
+ all_files = sorted(glob.glob("*.md"), reverse=True)
450
+ st.sidebar.title("📁 Files")
451
+ if st.sidebar.button("🗑 Delete All"):
452
+ for file in all_files:
453
+ os.remove(file)
454
+ st.session_state.file_content = {}
455
+ st.session_state.current_file = None
456
+ st.session_state.file_view_mode = None
457
+ st.rerun()
458
+ if st.sidebar.button("⬇️ Download All"):
459
+ zip_file = create_zip_of_files(all_files)
460
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
461
+ for file in all_files:
462
+ col1, col2, col3, col4 = st.sidebar.columns([1, 3, 1, 1])
463
+ with col1:
464
+ if st.button("🌐", key=f"view_{file}"):
465
+ st.session_state.current_file = file
466
+ st.session_state.file_view_mode = 'view'
467
+ if file not in st.session_state.file_content:
468
+ content = load_file(file)
469
+ if content is not None:
470
+ st.session_state.file_content[file] = content
471
+ st.rerun()
472
+ with col2:
473
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
474
+ with col3:
475
+ if st.button("📂", key=f"edit_{file}"):
476
+ st.session_state.current_file = file
477
+ st.session_state.file_view_mode = 'edit'
478
+ if file not in st.session_state.file_content:
479
+ content = load_file(file)
480
+ if content is not None:
481
+ st.session_state.file_content[file] = content
482
+ st.rerun()
483
+ with col4:
484
+ if st.button("🗑", key=f"delete_{file}"):
485
+ os.remove(file)
486
+ if file in st.session_state.file_content:
487
+ del st.session_state.file_content[file]
488
+ if st.session_state.current_file == file:
489
+ st.session_state.current_file = None
490
+ st.session_state.file_view_mode = None
491
+ st.rerun()
492
+ st.sidebar.markdown("---")
493
+ st.sidebar.title("External Help Links")
494
+ for link in external_links:
495
+ st.sidebar.markdown(f"{link['emoji']} [{link['title']}]({link['url']})", unsafe_allow_html=True)
496
+ if st.session_state.current_file:
497
+ if st.session_state.file_view_mode == 'view':
498
+ display_file_viewer(st.session_state.current_file)
499
+ elif st.session_state.file_view_mode == 'edit':
500
+ display_file_editor(st.session_state.current_file)
501
+
502
+ # =============================================================================
503
+ # ───────────── SIDEBAR DATA GRID (Records with formatted timestamps) ─────────────
504
+ # =============================================================================
505
+ def show_sidebar_data_grid():
506
+ if st.session_state.get("current_container"):
507
+ try:
508
+ records = get_documents(st.session_state.current_container)
509
+ data = []
510
+ for rec in records:
511
+ ts = rec.get("timestamp", "")
512
+ try:
513
+ dt = datetime.fromisoformat(ts)
514
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
515
+ except Exception:
516
+ formatted = ts
517
+ data.append({
518
+ "ID": rec.get("id", ""),
519
+ "Name": rec.get("name", ""),
520
+ "Timestamp": formatted
521
+ })
522
+ df = pd.DataFrame(data)
523
+ st.sidebar.markdown("### 📊 Data Grid")
524
+ st.sidebar.dataframe(df[["Name", "Timestamp"]])
525
+ except Exception as e:
526
+ st.sidebar.error(f"Data grid error: {str(e)}")
527
+ else:
528
+ st.sidebar.info("No container selected for data grid.")
529
+
530
+ # =============================================================================
531
+ # ───────────── SEARCH RESULTS DISPLAY (Editable Code Editors)
532
+ # =============================================================================
533
+ def display_search_results(keyword, container):
534
+ results = vector_keyword_search(keyword, container)
535
+ st.markdown("### 🔍 Search Results")
536
+ for res in results:
537
+ doc_id = res.get("id", "")
538
+ exp = st.expander(f"Result {doc_id}")
539
+ with exp:
540
+ edited = st.text_area("Edit Document", value=json.dumps(res, indent=2), key=f"search_{doc_id}")
541
+ if st.button(f"💾 Save changes for {doc_id}", key=f"save_search_{doc_id}"):
542
+ try:
543
+ updated_doc = json.loads(edited)
544
+ container.upsert_item(body=updated_doc)
545
+ st.success(f"Updated {doc_id}!")
546
+ st.experimental_rerun()
547
+ except Exception as e:
548
+ st.error(f"Error saving {doc_id}: {str(e)}")
549
+
550
+ # =============================================================================
551
+ # ───────────── DOCUMENTS LIST VIEW (Editable List with Sorting)
552
+ # =============================================================================
553
+ def edit_documents_list(container):
554
+ records = get_documents(container)
555
+ sort_option = st.selectbox("Sort by", ["Timestamp", "Name"], key="sort_option")
556
+ if sort_option == "Name":
557
+ records.sort(key=lambda r: r.get("name", "").lower())
558
+ else:
559
+ records.sort(key=lambda r: r.get("timestamp", ""), reverse=True)
560
+ data = []
561
+ for rec in records:
562
+ ts = rec.get("timestamp", "")
563
+ try:
564
+ dt = datetime.fromisoformat(ts)
565
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
566
+ except Exception:
567
+ formatted = ts
568
+ data.append({
569
+ "ID": rec.get("id", ""),
570
+ "Name": rec.get("name", ""),
571
+ "Content": rec.get("content", "")[:100] + "..." if rec.get("content", "") else "",
572
+ "Timestamp": formatted
573
+ })
574
+ df = pd.DataFrame(data)
575
+ edited_df = st.data_editor(df[["Name", "Content", "Timestamp"]], key="docs_editor", num_rows="dynamic")
576
+ if st.button("💾 Save List Changes"):
577
+ for idx, row in edited_df.iterrows():
578
+ original = data[idx]
579
+ if row["Name"] != original["Name"] or row["Content"] != original["Content"]:
580
+ doc_id = original["ID"]
581
+ doc = next((r for r in records if r.get("id") == doc_id), None)
582
+ if doc:
583
+ doc["name"] = row["Name"]
584
+ doc["content"] = row["Content"]
585
+ success, message = update_record(container, doc)
586
+ if success:
587
+ st.success(f"Updated {doc_id} 👍")
588
+ else:
589
+ st.error(f"Error updating {doc_id}: {message}")
590
+ st.experimental_rerun()
591
+
592
+ # =============================================================================
593
+ # ───────────── VIDEO & AUDIO UI FUNCTIONS ─────────────
594
+ # =============================================================================
595
+ def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
596
+ try:
597
+ st.write("Preprocessing image...")
598
+ if isinstance(file_data, bytes):
599
+ img = Image.open(io.BytesIO(file_data))
600
+ elif hasattr(file_data, 'read'):
601
+ if hasattr(file_data, 'seek'):
602
+ file_data.seek(0)
603
+ img = Image.open(file_data)
604
+ elif isinstance(file_data, Image.Image):
605
+ img = file_data
606
+ else:
607
+ raise ValueError(f"Unsupported input: {type(file_data)}")
608
+ if img.mode != 'RGB':
609
+ img = img.convert('RGB')
610
+ aspect_ratio = img.size[0] / img.size[1]
611
+ if aspect_ratio > target_size[0] / target_size[1]:
612
+ new_width = target_size[0]
613
+ new_height = int(new_width / aspect_ratio)
614
+ else:
615
+ new_height = target_size[1]
616
+ new_width = int(new_height * aspect_ratio)
617
+ new_width = (new_width // 2) * 2
618
+ new_height = (new_height // 2) * 2
619
+ resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
620
+ final_img = Image.new('RGB', target_size, (255, 255, 255))
621
+ paste_x = (target_size[0] - new_width) // 2
622
+ paste_y = (target_size[1] - new_height) // 2
623
+ final_img.paste(resized_img, (paste_x, paste_y))
624
+ return final_img
625
+ except Exception as e:
626
+ st.error(f"Image error: {str(e)}")
627
+ return None
628
+
629
+ def add_video_generation_ui(container):
630
+ st.markdown("### 🎥 Video Gen")
631
+ col1, col2 = st.columns([2, 1])
632
+ with col1:
633
+ uploaded_file = st.file_uploader("Upload Image 🖼️", type=['png', 'jpg', 'jpeg'])
634
+ with col2:
635
+ st.markdown("#### Params")
636
+ motion = st.slider("🌊 Motion", 1, 255, 127)
637
+ fps = st.slider("🎬 FPS", 1, 30, 6)
638
+ with st.expander("Advanced"):
639
+ use_custom = st.checkbox("Custom Seed")
640
+ seed = st.number_input("Seed", value=int(time.time() * 1000)) if use_custom else None
641
+ if uploaded_file is not None:
642
+ try:
643
+ file_data = uploaded_file.read()
644
+ preview1, preview2 = st.columns(2)
645
+ with preview1:
646
+ st.write("Original")
647
+ st.image(Image.open(io.BytesIO(file_data)), use_column_width=True)
648
+ with preview2:
649
+ proc_img = validate_and_preprocess_image(io.BytesIO(file_data))
650
+ if proc_img:
651
+ st.write("Processed")
652
+ st.image(proc_img, use_column_width=True)
653
+ else:
654
+ st.error("Preprocess failed")
655
+ return
656
+ if st.button("🎥 Generate"):
657
+ with st.spinner("Generating video..."):
658
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
659
+ proc_img.save(temp_file.name, format='PNG')
660
+ try:
661
+ client = Client("awacke1/stable-video-diffusion", hf_token=os.environ.get("HUGGINGFACE_TOKEN"))
662
+ result = client.predict(
663
+ image=temp_file.name,
664
+ seed=seed if seed is not None else int(time.time() * 1000),
665
+ randomize_seed=seed is None,
666
+ motion_bucket_id=motion,
667
+ fps_id=fps,
668
+ api_name="/video"
669
+ )
670
+ if result and isinstance(result, tuple) and len(result) >= 1:
671
+ video_path = result[0].get('video') if isinstance(result[0], dict) else None
672
+ if video_path and os.path.exists(video_path):
673
+ video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
674
+ shutil.copy(video_path, video_filename)
675
+ st.success(f"Video generated! 🎉")
676
+ st.video(video_filename)
677
+ if container:
678
+ video_record = {
679
+ "id": generate_unique_id(),
680
+ "pk": generate_unique_id(),
681
+ "type": "generated_video",
682
+ "filename": video_filename,
683
+ "seed": seed if seed is not None else "random",
684
+ "motion": motion,
685
+ "fps": fps,
686
+ "timestamp": datetime.now().isoformat()
687
+ }
688
+ success, message = insert_record(container, video_record)
689
+ if success:
690
+ st.success("DB record saved!")
691
+ else:
692
+ st.error(f"DB error: {message}")
693
+ else:
694
+ st.error("Invalid result format")
695
+ else:
696
+ st.error("No result returned")
697
+ except Exception as e:
698
+ st.error(f"Video gen error: {str(e)}")
699
+ finally:
700
+ try:
701
+ os.unlink(temp_file.name)
702
+ st.write("Temp file removed")
703
+ except Exception as e:
704
+ st.warning(f"Cleanup error: {str(e)}")
705
+ except Exception as e:
706
+ st.error(f"Upload error: {str(e)}")
707
+
708
+ # =============================================================================
709
+ # ───────────── NEW ITEM & FIELD FUNCTIONS
710
+ # =============================================================================
711
+ def new_item_default(container):
712
+ new_id = generate_unique_id()
713
+ default_doc = {
714
+ "id": new_id,
715
+ "pk": new_id,
716
+ "name": "New Sample Document",
717
+ "content": "Start editing your document here...",
718
+ "timestamp": datetime.now().isoformat(),
719
+ "type": "sample"
720
+ }
721
+ success, message = insert_record(container, default_doc)
722
+ if success:
723
+ st.success("New sample document created! ✨")
724
+ return default_doc
725
+ else:
726
+ st.error("Error creating new item: " + message)
727
+ return None
728
+
729
+ def auto_save_edit():
730
+ try:
731
+ edited_str = st.session_state.doc_editor
732
+ try:
733
+ json.loads(edited_str)
734
+ except Exception:
735
+ edited_str = sanitize_json_text(edited_str)
736
+ edited_doc = json.loads(edited_str)
737
+ container = st.session_state.current_container
738
+ container.upsert_item(edited_doc)
739
+ st.success("Auto-saved! 💾")
740
+ except Exception as e:
741
+ st.error(f"Auto-save error: {str(e)}")
742
+
743
+ def add_field_to_doc():
744
+ key = st.session_state.new_field_key
745
+ value = st.session_state.new_field_value
746
+ try:
747
+ doc = json.loads(st.session_state.doc_editor)
748
+ doc[key] = value
749
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
750
+ auto_save_edit()
751
+ st.success(f"Added field {key} 👍")
752
+ except Exception as e:
753
+ st.error(f"Error adding field: {str(e)}")
754
+
755
+ # =============================================================================
756
+ # ───────────── VECTOR SEARCH INTERFACE (Simple keyword search)
757
+ # =============================================================================
758
+ def vector_keyword_search(keyword, container):
759
+ try:
760
+ query = f"SELECT * FROM c WHERE CONTAINS(c.content, '{keyword}')"
761
+ results = list(container.query_items(query=query, enable_cross_partition_query=True))
762
+ return results
763
+ except Exception as e:
764
+ st.error(f"Vector search error: {str(e)}")
765
+ return []
766
+
767
+ # =============================================================================
768
+ # ───────────── NEW AI MODALITY RECORD TEMPLATES
769
+ # =============================================================================
770
+ def new_ai_record(container):
771
+ new_id = generate_unique_id()
772
+ default_doc = {
773
+ "id": new_id,
774
+ "pk": new_id,
775
+ "name": "AI Modality Record",
776
+ "function_url": "https://example.com/function",
777
+ "input_text": "### Input (markdown)\n\nType your input here.",
778
+ "output_text": "### Output (markdown)\n\nResult will appear here.",
779
+ "timestamp": datetime.now().isoformat(),
780
+ "type": "ai_modality"
781
+ }
782
+ success, message = insert_record(container, default_doc)
783
+ if success:
784
+ st.success("New AI modality record created! 💡")
785
+ return default_doc
786
+ else:
787
+ st.error("Error creating AI record: " + message)
788
+ return None
789
+
790
+ def new_links_record(container):
791
+ new_id = generate_unique_id()
792
+ links_md = "\n".join([f"- {link['emoji']} [{link['title']}]({link['url']})" for link in external_links])
793
+ default_doc = {
794
+ "id": new_id,
795
+ "pk": new_id,
796
+ "name": "Portal Links Record",
797
+ "function_url": "",
798
+ "input_text": links_md,
799
+ "output_text": "",
800
+ "timestamp": datetime.now().isoformat(),
801
+ "type": "ai_modality"
802
+ }
803
+ success, message = insert_record(container, default_doc)
804
+ if success:
805
+ st.success("New Portal Links record created! 🔗")
806
+ return default_doc
807
+ else:
808
+ st.error("Error creating links record: " + message)
809
+ return None
810
+
811
+ # =============================================================================
812
+ # ───────────── LANGCHAIN FUNCTIONS (Witty emoji comments)
813
+ # =============================================================================
814
+ def display_langchain_functions():
815
+ functions = [
816
+ {"name": "OpenAIEmbeddings", "comment": "🔮 Creates embeddings using OpenAI – pure magic!"},
817
+ {"name": "AzureCosmosDBNoSqlVectorSearch", "comment": "🚀 Performs vector search on Cosmos DB – superfast and smart!"},
818
+ {"name": "RecursiveCharacterTextSplitter", "comment": "✂️ Slices text into manageable chunks – like a pro chef!"}
819
+ ]
820
+ st.sidebar.markdown("### 🤖 Langchain Functions")
821
+ for func in functions:
822
+ st.sidebar.write(f"{func['name']}: {func['comment']}")
823
+
824
+ # =============================================================================
825
+ # ───────────── SIDEBAR DATA GRID (Editable Names Grid)
826
+ # =============================================================================
827
+ def edit_names_grid(container):
828
+ records = get_documents(container)
829
+ data = []
830
+ for rec in records:
831
+ ts = rec.get("timestamp", "")
832
+ try:
833
+ dt = datetime.fromisoformat(ts)
834
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
835
+ except Exception:
836
+ formatted = ts
837
+ data.append({
838
+ "ID": rec.get("id", ""),
839
+ "Name": rec.get("name", ""),
840
+ "Timestamp": formatted
841
+ })
842
+ df = pd.DataFrame(data)
843
+ edited_df = st.sidebar.data_editor(df[["Name", "Timestamp"]], key="names_editor", num_rows="dynamic")
844
+ if st.sidebar.button("💾 Save Name Changes"):
845
+ for idx, row in edited_df.iterrows():
846
+ original = df.iloc[idx]
847
+ if row["Name"] != original["Name"]:
848
+ doc_id = original["ID"]
849
+ doc = next((r for r in records if r.get("id") == doc_id), None)
850
+ if doc:
851
+ doc["name"] = row["Name"]
852
+ success, message = update_record(container, doc)
853
+ if success:
854
+ st.sidebar.success(f"Updated Name for {doc_id} to '{row['Name']}'")
855
+ else:
856
+ st.sidebar.error(f"Update error for {doc_id}: {message}")
857
+ st.experimental_rerun()
858
+
859
+ # =============================================================================
860
+ # ───────────── SEARCH RESULTS DISPLAY (Editable Code Editors)
861
+ # =============================================================================
862
+ def display_search_results(keyword, container):
863
+ results = vector_keyword_search(keyword, container)
864
+ st.markdown("### 🔍 Search Results")
865
+ for res in results:
866
+ doc_id = res.get("id", "")
867
+ exp = st.expander(f"Result {doc_id}")
868
+ with exp:
869
+ edited = st.text_area("Edit Document", value=json.dumps(res, indent=2), key=f"search_{doc_id}")
870
+ if st.button(f"💾 Save changes for {doc_id}", key=f"save_search_{doc_id}"):
871
+ try:
872
+ updated_doc = json.loads(edited)
873
+ container.upsert_item(body=updated_doc)
874
+ st.success(f"Updated {doc_id}!")
875
+ st.experimental_rerun()
876
+ except Exception as e:
877
+ st.error(f"Error saving {doc_id}: {str(e)}")
878
+
879
+ # =============================================================================
880
+ # ───────────── DOCUMENTS LIST VIEW (Editable List with Sorting)
881
+ # =============================================================================
882
+ def edit_documents_list(container):
883
+ records = get_documents(container)
884
+ sort_option = st.selectbox("Sort by", ["Timestamp", "Name"], key="sort_option")
885
+ if sort_option == "Name":
886
+ records.sort(key=lambda r: r.get("name", "").lower())
887
+ else:
888
+ records.sort(key=lambda r: r.get("timestamp", ""), reverse=True)
889
+ data = []
890
+ for rec in records:
891
+ ts = rec.get("timestamp", "")
892
+ try:
893
+ dt = datetime.fromisoformat(ts)
894
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
895
+ except Exception:
896
+ formatted = ts
897
+ data.append({
898
+ "ID": rec.get("id", ""),
899
+ "Name": rec.get("name", ""),
900
+ "Content": rec.get("content", "")[:100] + "..." if rec.get("content", "") else "",
901
+ "Timestamp": formatted
902
+ })
903
+ df = pd.DataFrame(data)
904
+ edited_df = st.data_editor(df[["Name", "Content", "Timestamp"]], key="docs_editor", num_rows="dynamic")
905
+ if st.button("💾 Save List Changes"):
906
+ for idx, row in edited_df.iterrows():
907
+ original = data[idx]
908
+ if row["Name"] != original["Name"] or row["Content"] != original["Content"]:
909
+ doc_id = original["ID"]
910
+ doc = next((r for r in records if r.get("id") == doc_id), None)
911
+ if doc:
912
+ doc["name"] = row["Name"]
913
+ doc["content"] = row["Content"]
914
+ success, message = update_record(container, doc)
915
+ if success:
916
+ st.success(f"Updated {doc_id} 👍")
917
+ else:
918
+ st.error(f"Error updating {doc_id}: {message}")
919
+ st.experimental_rerun()
920
+
921
+ # =============================================================================
922
+ # ───────────── VIDEO & AUDIO UI FUNCTIONS ─────────────
923
+ # =============================================================================
924
+ def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
925
+ try:
926
+ st.write("Preprocessing image...")
927
+ if isinstance(file_data, bytes):
928
+ img = Image.open(io.BytesIO(file_data))
929
+ elif hasattr(file_data, 'read'):
930
+ if hasattr(file_data, 'seek'):
931
+ file_data.seek(0)
932
+ img = Image.open(file_data)
933
+ elif isinstance(file_data, Image.Image):
934
+ img = file_data
935
+ else:
936
+ raise ValueError(f"Unsupported input: {type(file_data)}")
937
+ if img.mode != 'RGB':
938
+ img = img.convert('RGB')
939
+ aspect_ratio = img.size[0] / img.size[1]
940
+ if aspect_ratio > target_size[0] / target_size[1]:
941
+ new_width = target_size[0]
942
+ new_height = int(new_width / aspect_ratio)
943
+ else:
944
+ new_height = target_size[1]
945
+ new_width = int(new_height * aspect_ratio)
946
+ new_width = (new_width // 2) * 2
947
+ new_height = (new_height // 2) * 2
948
+ resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
949
+ final_img = Image.new('RGB', target_size, (255, 255, 255))
950
+ paste_x = (target_size[0] - new_width) // 2
951
+ paste_y = (target_size[1] - new_height) // 2
952
+ final_img.paste(resized_img, (paste_x, paste_y))
953
+ return final_img
954
+ except Exception as e:
955
+ st.error(f"Image error: {str(e)}")
956
+ return None
957
+
958
+ def add_video_generation_ui(container):
959
+ st.markdown("### 🎥 Video Gen")
960
+ col1, col2 = st.columns([2, 1])
961
+ with col1:
962
+ uploaded_file = st.file_uploader("Upload Image 🖼️", type=['png', 'jpg', 'jpeg'])
963
+ with col2:
964
+ st.markdown("#### Params")
965
+ motion = st.slider("🌊 Motion", 1, 255, 127)
966
+ fps = st.slider("🎬 FPS", 1, 30, 6)
967
+ with st.expander("Advanced"):
968
+ use_custom = st.checkbox("Custom Seed")
969
+ seed = st.number_input("Seed", value=int(time.time() * 1000)) if use_custom else None
970
+ if uploaded_file is not None:
971
+ try:
972
+ file_data = uploaded_file.read()
973
+ preview1, preview2 = st.columns(2)
974
+ with preview1:
975
+ st.write("Original")
976
+ st.image(Image.open(io.BytesIO(file_data)), use_column_width=True)
977
+ with preview2:
978
+ proc_img = validate_and_preprocess_image(io.BytesIO(file_data))
979
+ if proc_img:
980
+ st.write("Processed")
981
+ st.image(proc_img, use_column_width=True)
982
+ else:
983
+ st.error("Preprocess failed")
984
+ return
985
+ if st.button("🎥 Generate"):
986
+ with st.spinner("Generating video..."):
987
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
988
+ proc_img.save(temp_file.name, format='PNG')
989
+ try:
990
+ client = Client("awacke1/stable-video-diffusion", hf_token=os.environ.get("HUGGINGFACE_TOKEN"))
991
+ result = client.predict(
992
+ image=temp_file.name,
993
+ seed=seed if seed is not None else int(time.time() * 1000),
994
+ randomize_seed=seed is None,
995
+ motion_bucket_id=motion,
996
+ fps_id=fps,
997
+ api_name="/video"
998
+ )
999
+ if result and isinstance(result, tuple) and len(result) >= 1:
1000
+ video_path = result[0].get('video') if isinstance(result[0], dict) else None
1001
+ if video_path and os.path.exists(video_path):
1002
+ video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
1003
+ shutil.copy(video_path, video_filename)
1004
+ st.success(f"Video generated! 🎉")
1005
+ st.video(video_filename)
1006
+ if container:
1007
+ video_record = {
1008
+ "id": generate_unique_id(),
1009
+ "pk": generate_unique_id(),
1010
+ "type": "generated_video",
1011
+ "filename": video_filename,
1012
+ "seed": seed if seed is not None else "random",
1013
+ "motion": motion,
1014
+ "fps": fps,
1015
+ "timestamp": datetime.now().isoformat()
1016
+ }
1017
+ success, message = insert_record(container, video_record)
1018
+ if success:
1019
+ st.success("DB record saved!")
1020
+ else:
1021
+ st.error(f"DB error: {message}")
1022
+ else:
1023
+ st.error("Invalid result format")
1024
+ else:
1025
+ st.error("No result returned")
1026
+ except Exception as e:
1027
+ st.error(f"Video gen error: {str(e)}")
1028
+ finally:
1029
+ try:
1030
+ os.unlink(temp_file.name)
1031
+ st.write("Temp file removed")
1032
+ except Exception as e:
1033
+ st.warning(f"Cleanup error: {str(e)}")
1034
+ except Exception as e:
1035
+ st.error(f"Upload error: {str(e)}")
1036
+
1037
+ # =============================================================================
1038
+ # ───────────── NEW ITEM & FIELD FUNCTIONS
1039
+ # =============================================================================
1040
+ def new_item_default(container):
1041
+ new_id = generate_unique_id()
1042
+ default_doc = {
1043
+ "id": new_id,
1044
+ "pk": new_id,
1045
+ "name": "New Sample Document",
1046
+ "content": "Start editing your document here...",
1047
+ "timestamp": datetime.now().isoformat(),
1048
+ "type": "sample"
1049
+ }
1050
+ success, message = insert_record(container, default_doc)
1051
+ if success:
1052
+ st.success("New sample document created! ✨")
1053
+ return default_doc
1054
+ else:
1055
+ st.error("Error creating new item: " + message)
1056
+ return None
1057
+
1058
+ def auto_save_edit():
1059
+ try:
1060
+ edited_str = st.session_state.doc_editor
1061
+ try:
1062
+ json.loads(edited_str)
1063
+ except Exception:
1064
+ edited_str = sanitize_json_text(edited_str)
1065
+ edited_doc = json.loads(edited_str)
1066
+ container = st.session_state.current_container
1067
+ container.upsert_item(edited_doc)
1068
+ st.success("Auto-saved! 💾")
1069
+ except Exception as e:
1070
+ st.error(f"Auto-save error: {str(e)}")
1071
+
1072
+ def add_field_to_doc():
1073
+ key = st.session_state.new_field_key
1074
+ value = st.session_state.new_field_value
1075
+ try:
1076
+ doc = json.loads(st.session_state.doc_editor)
1077
+ doc[key] = value
1078
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
1079
+ auto_save_edit()
1080
+ st.success(f"Added field {key} 👍")
1081
+ except Exception as e:
1082
+ st.error(f"Error adding field: {str(e)}")
1083
+
1084
+ # =============================================================================
1085
+ # ───────────── SEARCH RESULTS DISPLAY (Editable Code Editors)
1086
+ # =============================================================================
1087
+ def display_search_results(keyword, container):
1088
+ results = vector_keyword_search(keyword, container)
1089
+ st.markdown("### 🔍 Search Results")
1090
+ for res in results:
1091
+ doc_id = res.get("id", "")
1092
+ exp = st.expander(f"Result {doc_id}")
1093
+ with exp:
1094
+ edited = st.text_area("Edit Document", value=json.dumps(res, indent=2), key=f"search_{doc_id}")
1095
+ if st.button(f"💾 Save changes for {doc_id}", key=f"save_search_{doc_id}"):
1096
+ try:
1097
+ updated_doc = json.loads(edited)
1098
+ container.upsert_item(body=updated_doc)
1099
+ st.success(f"Updated {doc_id}!")
1100
+ st.experimental_rerun()
1101
+ except Exception as e:
1102
+ st.error(f"Error saving {doc_id}: {str(e)}")
1103
+
1104
+ # =============================================================================
1105
+ # ───────────── DOCUMENTS LIST VIEW (Editable List with Sorting)
1106
+ # =============================================================================
1107
+ def edit_documents_list(container):
1108
+ records = get_documents(container)
1109
+ sort_option = st.selectbox("Sort by", ["Timestamp", "Name"], key="sort_option")
1110
+ if sort_option == "Name":
1111
+ records.sort(key=lambda r: r.get("name", "").lower())
1112
+ else:
1113
+ records.sort(key=lambda r: r.get("timestamp", ""), reverse=True)
1114
+ data = []
1115
+ for rec in records:
1116
+ ts = rec.get("timestamp", "")
1117
+ try:
1118
+ dt = datetime.fromisoformat(ts)
1119
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
1120
+ except Exception:
1121
+ formatted = ts
1122
+ data.append({
1123
+ "ID": rec.get("id", ""),
1124
+ "Name": rec.get("name", ""),
1125
+ "Content": rec.get("content", "")[:100] + "..." if rec.get("content", "") else "",
1126
+ "Timestamp": formatted
1127
+ })
1128
+ df = pd.DataFrame(data)
1129
+ edited_df = st.data_editor(df[["Name", "Content", "Timestamp"]], key="docs_editor", num_rows="dynamic")
1130
+ if st.button("💾 Save List Changes"):
1131
+ for idx, row in edited_df.iterrows():
1132
+ original = data[idx]
1133
+ if row["Name"] != original["Name"] or row["Content"] != original["Content"]:
1134
+ doc_id = original["ID"]
1135
+ doc = next((r for r in records if r.get("id") == doc_id), None)
1136
+ if doc:
1137
+ doc["name"] = row["Name"]
1138
+ doc["content"] = row["Content"]
1139
+ success, message = update_record(container, doc)
1140
+ if success:
1141
+ st.success(f"Updated {doc_id} 👍")
1142
+ else:
1143
+ st.error(f"Error updating {doc_id}: {message}")
1144
+ st.experimental_rerun()
1145
+
1146
+ # =============================================================================
1147
+ # ───────────── SEARCH DOCUMENTS UI (Enter Key triggers search)
1148
+ # =============================================================================
1149
+ def search_documents_ui(container):
1150
+ with st.sidebar.form("search_form"):
1151
+ keyword = st.text_input("Search Keyword", key="search_keyword")
1152
+ submitted = st.form_submit_button("🔍 Search")
1153
+ if submitted and keyword:
1154
+ display_search_results(keyword, container)
1155
+
1156
+ # =============================================================================
1157
+ # ───────────── MAIN FUNCTION ─────────────
1158
+ # =============================================================================
1159
+ def main():
1160
+ st.markdown("### 🐙 GitCosmos - Cosmos & Git Hub")
1161
+ st.markdown(f"[🔗 Portal]({CosmosDBUrl})")
1162
+ if "chat_history" not in st.session_state:
1163
+ st.session_state.chat_history = []
1164
+ st.session_state.setdefault("current_container", None)
1165
+ if Key:
1166
+ st.session_state.primary_key = Key
1167
+ st.session_state.logged_in = True
1168
+ else:
1169
+ st.error("Missing Cosmos Key 🔑❌")
1170
+ return
1171
+ st.sidebar.markdown("## 🛠️ Item Management")
1172
+ if st.sidebar.button("New Item"):
1173
+ if st.session_state.get("current_container"):
1174
+ new_doc = new_item_default(st.session_state.current_container)
1175
+ if new_doc:
1176
+ st.session_state.doc_editor = json.dumps(new_doc, indent=2)
1177
+ else:
1178
+ st.warning("No container selected!")
1179
+ st.sidebar.text_input("New Field Key", key="new_field_key")
1180
+ st.sidebar.text_input("New Field Value", key="new_field_value")
1181
+ if st.sidebar.button("Add Field"):
1182
+ if "doc_editor" in st.session_state:
1183
+ add_field_to_doc()
1184
+ else:
1185
+ st.warning("No document loaded to add a field.")
1186
+ if st.sidebar.button("New AI Record"):
1187
+ if st.session_state.get("current_container"):
1188
+ new_ai_record(st.session_state.current_container)
1189
+ else:
1190
+ st.warning("No container selected!")
1191
+ if st.sidebar.button("New Links Record"):
1192
+ if st.session_state.get("current_container"):
1193
+ new_links_record(st.session_state.current_container)
1194
+ else:
1195
+ st.warning("No container selected!")
1196
+ st.sidebar.markdown("## 🔍 Vector Search")
1197
+ search_documents_ui(st.session_state.get("current_container"))
1198
+ show_sidebar_data_grid()
1199
+ display_langchain_functions()
1200
+ if st.session_state.get("current_container"):
1201
+ edit_names_grid(st.session_state.current_container)
1202
+ try:
1203
+ if st.session_state.get("client") is None:
1204
+ st.session_state.client = CosmosClient(ENDPOINT, credential=st.session_state.primary_key)
1205
+ st.sidebar.title("🐙 Navigator")
1206
+ databases = get_databases(st.session_state.client)
1207
+ selected_db = st.sidebar.selectbox("🗃️ DB", databases)
1208
+ st.markdown(CosmosDBUrl)
1209
+ if selected_db != st.session_state.get("selected_database"):
1210
+ st.session_state.selected_database = selected_db
1211
+ st.session_state.selected_container = None
1212
+ st.session_state.selected_document_id = None
1213
+ st.session_state.current_index = 0
1214
+ st.rerun()
1215
+ if st.session_state.selected_database:
1216
+ database = st.session_state.client.get_database_client(st.session_state.selected_database)
1217
+ if "show_new_container_form" not in st.session_state:
1218
+ st.session_state.show_new_container_form = False
1219
+ if st.sidebar.button("🆕 New Container"):
1220
+ st.session_state.show_new_container_form = True
1221
+ if st.session_state.show_new_container_form:
1222
+ with st.sidebar.form("new_container_form"):
1223
+ new_container_id = st.text_input("Container ID", value="aiml-container")
1224
+ new_partition_key = st.text_input("Partition Key", value="/pk")
1225
+ new_analytical = st.checkbox("Enable Analytical Store", value=True)
1226
+ submitted = st.form_submit_button("Create Container")
1227
+ if submitted:
1228
+ analytical_ttl = -1 if new_analytical else None
1229
+ new_container = create_new_container(database, new_container_id, new_partition_key, analytical_storage_ttl=analytical_ttl)
1230
+ if new_container:
1231
+ st.success(f"Container '{new_container_id}' created.")
1232
+ default_id = generate_unique_id()
1233
+ default_item = {
1234
+ "id": default_id,
1235
+ "pk": default_id,
1236
+ "name": "Default Image Prompt",
1237
+ "prompt": "Enter your image prompt here",
1238
+ "timestamp": datetime.now().isoformat(),
1239
+ "type": "image_prompt"
1240
+ }
1241
+ insert_success, insert_message = insert_record(new_container, default_item)
1242
+ if insert_success:
1243
+ st.info("Default templated item created in new container.")
1244
+ else:
1245
+ st.error(f"Default item insertion error: {insert_message}")
1246
+ st.session_state.show_new_container_form = False
1247
+ st.session_state.new_container_created = new_container_id
1248
+ st.rerun()
1249
+ containers = get_containers(database)
1250
+ if "new_container_created" in st.session_state and st.session_state.new_container_created not in containers:
1251
+ containers.append(st.session_state.new_container_created)
1252
+ selected_container = st.sidebar.selectbox("📁 Container", containers)
1253
+ if selected_container != st.session_state.get("selected_container"):
1254
+ st.session_state.selected_container = selected_container
1255
+ st.session_state.selected_document_id = None
1256
+ st.session_state.current_index = 0
1257
+ st.rerun()
1258
+ if st.session_state.selected_container:
1259
+ container = database.get_container_client(st.session_state.selected_container)
1260
+ st.session_state.current_container = container
1261
+ if st.sidebar.button("📦 Export"):
1262
+ download_link = archive_current_container(st.session_state.selected_database, st.session_state.selected_container, st.session_state.client)
1263
+ if download_link.startswith('<a'):
1264
+ st.markdown(download_link, unsafe_allow_html=True)
1265
+ else:
1266
+ st.error(download_link)
1267
+ documents = get_documents(container)
1268
+ total_docs = len(documents)
1269
+ num_docs = st.slider("Docs", 1, 20, 1)
1270
+ documents_to_display = documents[:num_docs] if total_docs > num_docs else documents
1271
+ st.sidebar.info(f"Showing {len(documents_to_display)} docs")
1272
+ view_options = ['List', 'Markdown', 'Code', 'Run AI', 'Clone', 'New']
1273
+ selected_view = st.sidebar.selectbox("View", view_options, index=1)
1274
+ if selected_view == 'List':
1275
+ edit_documents_list(container)
1276
+ elif selected_view == 'Markdown':
1277
+ st.markdown("#### 📄 Markdown")
1278
+ if documents:
1279
+ doc = documents[st.session_state.current_index]
1280
+ content = json.dumps(doc, indent=2)
1281
+ st.markdown(f"```json\n{content}\n```")
1282
+ col_prev, col_next = st.columns(2)
1283
+ with col_prev:
1284
+ if st.button("⬅️") and st.session_state.current_index > 0:
1285
+ st.session_state.current_index -= 1
1286
+ st.rerun()
1287
+ with col_next:
1288
+ if st.button("➡️") and st.session_state.current_index < total_docs - 1:
1289
+ st.session_state.current_index += 1
1290
+ st.rerun()
1291
+ elif selected_view == 'Code':
1292
+ st.markdown("#### 💻 Code Editor")
1293
+ if documents:
1294
+ doc = documents[st.session_state.current_index]
1295
+ if "doc_editor" not in st.session_state:
1296
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
1297
+ edited = st.text_area("Edit JSON", value=st.session_state.doc_editor, height=300, key="doc_editor", on_change=lambda: auto_save_edit())
1298
+ col_prev, col_next = st.columns(2)
1299
+ with col_prev:
1300
+ if st.button("⬅️") and st.session_state.current_index > 0:
1301
+ st.session_state.current_index -= 1
1302
+ st.rerun()
1303
+ with col_next:
1304
+ if st.button("➡️") and st.session_state.current_index < total_docs - 1:
1305
+ st.session_state.current_index += 1
1306
+ st.rerun()
1307
+ col_save, col_delete = st.columns(2)
1308
+ with col_save:
1309
+ if st.button("💾 Save", key=f'save_{st.session_state.current_index}'):
1310
+ try:
1311
+ updated_doc = json.loads(edited)
1312
+ container.upsert_item(body=updated_doc)
1313
+ st.success(f"Saved {updated_doc['id']}")
1314
+ st.rerun()
1315
+ except Exception as e:
1316
+ st.error(f"Save err: {str(e)}")
1317
+ with col_delete:
1318
+ if st.button("🗑️ Delete", key=f'delete_{st.session_state.current_index}'):
1319
+ try:
1320
+ current_doc = json.loads(edited)
1321
+ success, message = delete_record(container, current_doc)
1322
+ if success:
1323
+ st.success(message)
1324
+ st.rerun()
1325
+ else:
1326
+ st.error(message)
1327
+ except Exception as e:
1328
+ st.error(f"Delete err: {str(e)}")
1329
+ if "delete_log" in st.session_state and st.session_state.delete_log:
1330
+ st.subheader("Delete Log")
1331
+ for log_entry in st.session_state.delete_log[-5:]:
1332
+ st.write(log_entry)
1333
+ elif selected_view == 'Run AI':
1334
+ st.markdown("#### 🤖 Run AI")
1335
+ ai_query = st.text_area("Enter your query for ArXiv search:", key="arxiv_query", height=100)
1336
+ if st.button("Send"):
1337
+ st.session_state.last_query = ai_query
1338
+ perform_ai_lookup(ai_query, vocal_summary=True, extended_refs=False, titles_summary=True, full_audio=True, useArxiv=True, useArxivAudio=False)
1339
+ elif selected_view == 'Clone':
1340
+ st.markdown("#### 📄 Clone")
1341
+ if documents:
1342
+ doc = documents[st.session_state.current_index]
1343
+ st.markdown(f"Original ID: {doc.get('id', '')}")
1344
+ new_id = st.text_input("New ID", value=generate_unique_id(), key='new_clone_id')
1345
+ new_name = st.text_input("New Name", value=f"Clone_{new_id[:8]}", key='new_clone_name')
1346
+ 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']}}
1347
+ doc_str = st.text_area("Edit JSON", value=json.dumps(new_doc, indent=2), height=300, key='clone_preview')
1348
+ col1, col2 = st.columns(2)
1349
+ with col1:
1350
+ if st.button("🔄 Regenerate"):
1351
+ new_id = generate_unique_id()
1352
+ st.session_state.new_clone_id = new_id
1353
+ st.rerun()
1354
+ with col2:
1355
+ if st.button("💾 Save Clone"):
1356
+ try:
1357
+ final_doc = json.loads(doc_str)
1358
+ for field in ['_rid', '_self', '_etag', '_attachments', '_ts']:
1359
+ final_doc.pop(field, None)
1360
+ container.create_item(body=final_doc)
1361
+ st.success(f"Cloned {final_doc['id']}")
1362
+ st.rerun()
1363
+ except Exception as e:
1364
+ st.error(f"Clone err: {str(e)}")
1365
+ col_prev, col_next = st.columns(2)
1366
+ with col_prev:
1367
+ if st.button("⬅️") and st.session_state.current_index > 0:
1368
+ st.session_state.current_index -= 1
1369
+ st.rerun()
1370
+ with col_next:
1371
+ if st.button("➡️") and st.session_state.current_index < total_docs - 1:
1372
+ st.session_state.current_index += 1
1373
+ st.rerun()
1374
+ elif selected_view == 'New':
1375
+ st.markdown("#### ➕ New Doc")
1376
+ if st.button("🤖 Auto-Gen"):
1377
+ auto_doc = {
1378
+ "id": generate_unique_id(),
1379
+ "pk": generate_unique_id(),
1380
+ "name": f"Auto {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
1381
+ "content": "Auto-generated record.",
1382
+ "timestamp": datetime.now().isoformat()
1383
+ }
1384
+ success, message = insert_record(container, auto_doc)
1385
+ if success:
1386
+ st.success(message)
1387
+ st.rerun()
1388
+ else:
1389
+ st.error(message)
1390
+ else:
1391
+ new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
1392
+ default_doc = {
1393
+ "id": new_id,
1394
+ "pk": new_id,
1395
+ "name": "New Doc",
1396
+ "content": "",
1397
+ "timestamp": datetime.now().isoformat()
1398
+ }
1399
+ new_doc_str = st.text_area("JSON", value=json.dumps(default_doc, indent=2), height=300)
1400
+ if st.button("➕ Create"):
1401
+ try:
1402
+ cleaned = preprocess_text(new_doc_str)
1403
+ new_doc = json.loads(cleaned)
1404
+ new_doc['id'] = new_id
1405
+ new_doc['pk'] = new_id
1406
+ success, message = insert_record(container, new_doc)
1407
+ if success:
1408
+ st.success(f"Created {new_doc['id']}")
1409
+ st.rerun()
1410
+ else:
1411
+ st.error(message)
1412
+ except Exception as e:
1413
+ st.error(f"Create err: {str(e)}")
1414
+ st.subheader(f"📊 {st.session_state.selected_container}")
1415
+ if documents_to_display:
1416
+ df = pd.DataFrame(documents_to_display)
1417
+ st.dataframe(df)
1418
+ else:
1419
+ st.info("No docs.")
1420
+ update_file_management_section()
1421
+ except exceptions.CosmosHttpResponseError as e:
1422
+ st.error(f"Cosmos error: {str(e)} 🚨")
1423
+ except Exception as e:
1424
+ st.error(f"Error: {str(e)} 😱")
1425
+ if st.session_state.logged_in and st.sidebar.button("🚪 Logout"):
1426
+ st.markdown("#### 🚪 Logout")
1427
+ st.session_state.logged_in = False
1428
+ st.session_state.selected_records = []
1429
+ st.session_state.client = None
1430
+ st.session_state.selected_database = None
1431
+ st.session_state.selected_container = None
1432
+ st.session_state.selected_document_id = None
1433
+ st.session_state.current_index = 0
1434
+ st.rerun()
1435
+ show_sidebar_data_grid()
1436
+
1437
+ if __name__ == "__main__":
1438
+ main()