awacke1 commited on
Commit
f8ea9b9
Β·
verified Β·
1 Parent(s): 8da5992

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1134 -0
app.py ADDED
@@ -0,0 +1,1134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import anthropic
3
+ import base64
4
+ import glob
5
+ import hashlib
6
+ import json
7
+ import os
8
+ import pandas as pd
9
+ import pytz
10
+ import random
11
+ import re
12
+ import shutil
13
+ import streamlit as st
14
+ import time
15
+ import traceback
16
+ import uuid
17
+ import zipfile
18
+
19
+ from PIL import Image
20
+ from azure.cosmos import CosmosClient, exceptions
21
+ from datetime import datetime
22
+ from git import Repo
23
+ from github import Github
24
+ from gradio_client import Client
25
+ from urllib.parse import quote
26
+
27
+ # 🎭 App Configuration - Because every app needs a good costume!
28
+ Site_Name = 'πŸ™Git🌌CosmosπŸ’« - Azure Cosmos DB and Github Agent'
29
+ title = "πŸ™Git🌌CosmosπŸ’« - Azure Cosmos DB and Github Agent"
30
+ helpURL = 'https://huggingface.co/awacke1'
31
+ bugURL = 'https://huggingface.co/spaces/awacke1'
32
+ icons = 'πŸ™πŸŒŒπŸ’«'
33
+
34
+ st.set_page_config(
35
+ page_title=title,
36
+ page_icon=icons,
37
+ layout="wide",
38
+ initial_sidebar_state="auto",
39
+ menu_items={
40
+ 'Get Help': helpURL,
41
+ 'Report a bug': bugURL,
42
+ 'About': title
43
+ }
44
+ )
45
+
46
+ # 🌌 Cosmos DB configuration - Where data goes to party!
47
+ ENDPOINT = "https://acae-afd.documents.azure.com:443/"
48
+ DATABASE_NAME = os.environ.get("COSMOS_DATABASE_NAME")
49
+ CONTAINER_NAME = os.environ.get("COSMOS_CONTAINER_NAME")
50
+ Key = os.environ.get("Key")
51
+
52
+ # 🌐 Your local app URL - Home sweet home
53
+ LOCAL_APP_URL = "https://huggingface.co/spaces/awacke1/AzureCosmosDBUI"
54
+
55
+ # πŸ€– Anthropic configuration - Teaching machines to be more human (and funnier)
56
+ client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
57
+
58
+ # 🧠 Initialize session state - Because even apps need a good memory
59
+ if "chat_history" not in st.session_state:
60
+ st.session_state.chat_history = []
61
+
62
+ # πŸ› οΈ Helper Functions - The unsung heroes of our code
63
+
64
+ # πŸ“Ž Get a file download link - Making file sharing as easy as stealing candy from a baby
65
+ def get_download_link(file_path):
66
+ with open(file_path, "rb") as file:
67
+ contents = file.read()
68
+ b64 = base64.b64encode(contents).decode()
69
+ file_name = os.path.basename(file_path)
70
+ return f'<a href="data:file/txt;base64,{b64}" download="{file_name}">Download {file_name}πŸ“‚</a>'
71
+
72
+ # 🎲 Generate a unique ID - Because being unique is important (just ask your mother)
73
+ def generate_unique_id():
74
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
75
+ unique_uuid = str(uuid.uuid4())
76
+ returnValue = f"{timestamp}-{unique_uuid}"
77
+ st.write('New Unique ID:' + returnValue)
78
+ return
79
+
80
+ # πŸ“ Generate a filename - Naming files like a pro (or a very confused librarian)
81
+ def generate_filename(prompt, file_type):
82
+ central = pytz.timezone('US/Central')
83
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
84
+ safe_prompt = re.sub(r'\W+', '', prompt)[:90]
85
+ return f"{safe_date_time}{safe_prompt}.{file_type}"
86
+
87
+ # πŸ’Ύ Create and save a file - Because data hoarding is a legitimate hobby
88
+ def create_file(filename, prompt, response, should_save=True):
89
+ if not should_save:
90
+ return
91
+ with open(filename, 'w', encoding='utf-8') as file:
92
+ file.write(prompt + "\n\n" + response)
93
+
94
+ # πŸ“– Load file content - Bringing words back from the digital grave
95
+ def load_file(file_name):
96
+ with open(file_name, "r", encoding='utf-8') as file:
97
+ content = file.read()
98
+ return content
99
+
100
+ # πŸ” Display glossary entity - Making search fun again (as if it ever was)
101
+ def display_glossary_entity(k):
102
+ search_urls = {
103
+ "πŸš€πŸŒŒArXiv": lambda k: f"/?q={quote(k)}",
104
+ "πŸ“–": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
105
+ "πŸ”": lambda k: f"https://www.google.com/search?q={quote(k)}",
106
+ "πŸŽ₯": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
107
+ }
108
+ links_md = ' '.join([f"<a href='{url(k)}' target='_blank'>{emoji}</a>" for emoji, url in search_urls.items()])
109
+ st.markdown(f"{k} {links_md}", unsafe_allow_html=True)
110
+
111
+ # πŸ—œοΈ Create zip of files - Squeezing files together like sardines in a can
112
+ def create_zip_of_files(files):
113
+ zip_name = "all_files.zip"
114
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
115
+ for file in files:
116
+ zipf.write(file)
117
+ return zip_name
118
+
119
+ # 🎬 Get video HTML - Making videos play nice (or at least trying to)
120
+ def get_video_html(video_path, width="100%"):
121
+ video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
122
+ return f'''
123
+ <video width="{width}" controls autoplay loop>
124
+ <source src="{video_url}" type="video/mp4">
125
+ Your browser does not support the video tag.
126
+ </video>
127
+ '''
128
+
129
+ # 🎡 Get audio HTML - Let the music play (and hope it's not Baby Shark)
130
+ def get_audio_html(audio_path, width="100%"):
131
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
132
+ return f'''
133
+ <audio controls style="width:{width}">
134
+ <source src="{audio_url}" type="audio/mpeg">
135
+ Your browser does not support the audio element.
136
+ </audio>
137
+ '''
138
+
139
+ # 🌌 Cosmos DB functions - Where data goes to live its best life
140
+
141
+ # πŸ“š Get databases - Collecting databases like Pokemon cards
142
+ def get_databases(client):
143
+ return [db['id'] for db in client.list_databases()]
144
+
145
+ # πŸ“¦ Get containers - Finding where all the good stuff is hidden
146
+ def get_containers(database):
147
+ return [container['id'] for container in database.list_containers()]
148
+
149
+ # πŸ“„ Get documents - Retrieving the sacred texts (or just some JSON)
150
+ def get_documents(container, limit=None):
151
+ query = "SELECT * FROM c ORDER BY c._ts DESC"
152
+ items = list(container.query_items(query=query, enable_cross_partition_query=True, max_item_count=limit))
153
+ return items
154
+
155
+ # πŸ“₯ Insert record - Adding new data (and crossing fingers it doesn't break anything)
156
+ def insert_record(container, record):
157
+ try:
158
+ container.create_item(body=record)
159
+ return True, "Record inserted successfully! πŸŽ‰"
160
+ except exceptions.CosmosHttpResponseError as e:
161
+ return False, f"HTTP error occurred: {str(e)} 🚨"
162
+ except Exception as e:
163
+ return False, f"An unexpected error occurred: {str(e)} 😱"
164
+
165
+ # πŸ”„ Update record - Giving data a makeover
166
+ def update_record(container, updated_record):
167
+ try:
168
+ container.upsert_item(body=updated_record)
169
+ return True, f"Record with id {updated_record['id']} successfully updated. πŸ› οΈ"
170
+ except exceptions.CosmosHttpResponseError as e:
171
+ return False, f"HTTP error occurred: {str(e)} 🚨"
172
+ except Exception as e:
173
+ return False, f"An unexpected error occurred: {traceback.format_exc()} 😱"
174
+
175
+ # πŸ—‘οΈ Delete record - Saying goodbye to data (it's not you, it's me)
176
+ def delete_record(container, name, id):
177
+ try:
178
+ container.delete_item(item=id, partition_key=id)
179
+ return True, f"Successfully deleted record with name: {name} and id: {id} πŸ—‘οΈ"
180
+ except exceptions.CosmosResourceNotFoundError:
181
+ return False, f"Record with id {id} not found. It may have been already deleted. πŸ•΅οΈβ€β™‚οΈ"
182
+ except exceptions.CosmosHttpResponseError as e:
183
+ return False, f"HTTP error occurred: {str(e)} 🚨"
184
+ except Exception as e:
185
+ return False, f"An unexpected error occurred: {traceback.format_exc()} 😱"
186
+
187
+ # πŸ’Ύ Save to Cosmos DB - Preserving data for future generations (or just until the next update)
188
+ def save_to_cosmos_db(container, query, response1, response2):
189
+ try:
190
+ if container:
191
+ record = {
192
+ "id": generate_unique_id(),
193
+ "query": query,
194
+ "response1": response1,
195
+ "response2": response2,
196
+ "timestamp": datetime.utcnow().isoformat()
197
+ }
198
+ try:
199
+ container.create_item(body=record)
200
+ st.success(f"Record saved successfully with ID: {record['id']}")
201
+ # Refresh the documents display
202
+ st.session_state.documents = get_documents(container)
203
+ except exceptions.CosmosHttpResponseError as e:
204
+ st.error(f"Error saving record to Cosmos DB: {e}")
205
+ else:
206
+ st.error("Cosmos DB container is not initialized.")
207
+ except Exception as e:
208
+ st.error(f"An unexpected error occurred: {str(e)}")
209
+
210
+ # πŸ™ GitHub functions - Where code goes to socialize
211
+
212
+ # πŸ“₯ Download GitHub repo - Cloning repos like it's going out of style
213
+ def download_github_repo(url, local_path):
214
+ if os.path.exists(local_path):
215
+ shutil.rmtree(local_path)
216
+ Repo.clone_from(url, local_path)
217
+
218
+ # πŸ—œοΈ Create zip file - Squeezing files tighter than your budget
219
+ def create_zip_file(source_dir, output_filename):
220
+ shutil.make_archive(output_filename, 'zip', source_dir)
221
+
222
+ # πŸ—οΈ Create repo - Building digital homes for lonely code
223
+ def create_repo(g, repo_name):
224
+ user = g.get_user()
225
+ return user.create_repo(repo_name)
226
+
227
+ # πŸš€ Push to GitHub - Sending code to the cloud (hopefully not the rainy kind)
228
+ def push_to_github(local_path, repo, github_token):
229
+ repo_url = f"https://{github_token}@github.com/{repo.full_name}.git"
230
+ local_repo = Repo(local_path)
231
+
232
+ if 'origin' in [remote.name for remote in local_repo.remotes]:
233
+ origin = local_repo.remote('origin')
234
+ origin.set_url(repo_url)
235
+ else:
236
+ origin = local_repo.create_remote('origin', repo_url)
237
+
238
+ if not local_repo.heads:
239
+ local_repo.git.checkout('-b', 'main')
240
+ current_branch = 'main'
241
+ else:
242
+ current_branch = local_repo.active_branch.name
243
+
244
+ local_repo.git.add(A=True)
245
+
246
+ if local_repo.is_dirty():
247
+ local_repo.git.commit('-m', 'Initial commit')
248
+
249
+ origin.push(refspec=f'{current_branch}:{current_branch}')
250
+
251
+
252
+ def save_or_clone_to_cosmos_db(container, document=None, clone_id=None):
253
+ def generate_complex_unique_id():
254
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
255
+ random_component = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=8))
256
+ return f"{timestamp}-{random_component}-{str(uuid.uuid4())}"
257
+
258
+ max_retries = 10
259
+ base_delay = 0.1
260
+
261
+ for attempt in range(max_retries):
262
+ try:
263
+ new_id = generate_complex_unique_id()
264
+
265
+ if clone_id:
266
+ try:
267
+ existing_doc = container.read_item(item=clone_id, partition_key=clone_id)
268
+ new_doc = {
269
+ 'id': new_id,
270
+ 'originalText': existing_doc.get('originalText', ''),
271
+ 'qtPrompts': existing_doc.get('qtPrompts', []),
272
+ 'cloned_from': clone_id,
273
+ 'cloned_at': datetime.utcnow().isoformat()
274
+ }
275
+ except exceptions.CosmosResourceNotFoundError:
276
+ return False, f"Document with ID {clone_id} not found for cloning."
277
+ else:
278
+ if document is None:
279
+ return False, "No document provided for saving"
280
+
281
+ document['id'] = new_id
282
+ document['created_at'] = datetime.utcnow().isoformat()
283
+ new_doc = document
284
+
285
+ response = container.create_item(body=new_doc)
286
+ return True, f"{'Cloned' if clone_id else 'New'} document saved successfully with ID: {response['id']}"
287
+
288
+ except exceptions.CosmosHttpResponseError as e:
289
+ if e.status_code == 409:
290
+ delay = base_delay * (2 ** attempt) + random.uniform(0, 0.1)
291
+ time.sleep(delay)
292
+ continue
293
+ return False, f"Error saving to Cosmos DB: {str(e)}"
294
+ except Exception as e:
295
+ return False, f"An unexpected error occurred: {str(e)}"
296
+
297
+ return False, "Failed to save document after maximum retries."
298
+
299
+
300
+ # πŸ“¦ Archive current container - Packing up data like you're moving to a new digital house
301
+ def archive_current_container(database_name, container_name, client):
302
+ try:
303
+ base_dir = "./cosmos_archive_current_container"
304
+ if os.path.exists(base_dir):
305
+ shutil.rmtree(base_dir)
306
+ os.makedirs(base_dir)
307
+
308
+ db_client = client.get_database_client(database_name)
309
+ container_client = db_client.get_container_client(container_name)
310
+ items = list(container_client.read_all_items())
311
+
312
+ container_dir = os.path.join(base_dir, container_name)
313
+ os.makedirs(container_dir)
314
+
315
+ for item in items:
316
+ item_id = item.get('id', f"unknown_{datetime.now().strftime('%Y%m%d%H%M%S')}")
317
+ with open(os.path.join(container_dir, f"{item_id}.json"), 'w') as f:
318
+ json.dump(item, f, indent=2)
319
+
320
+ archive_name = f"{container_name}_archive_{datetime.now().strftime('%Y%m%d%H%M%S')}"
321
+ shutil.make_archive(archive_name, 'zip', base_dir)
322
+
323
+ return get_download_link(f"{archive_name}.zip")
324
+ except Exception as e:
325
+ return f"An error occurred while archiving data: {str(e)} 😒"
326
+
327
+ # πŸ” Search glossary - Finding needles in digital haystacks
328
+ def search_glossary(query):
329
+ st.markdown(f"### πŸ” SearchGlossary for: {query}")
330
+
331
+ # Dropdown for model selection
332
+ model_options = ['mistralai/Mixtral-8x7B-Instruct-v0.1', 'mistralai/Mistral-7B-Instruct-v0.2', 'google/gemma-7b-it', 'None']
333
+ model_choice = st.selectbox('🧠 Select LLM Model', options=model_options, index=1)
334
+
335
+ # Dropdown for database selection
336
+ database_options = ['Semantic Search', 'Arxiv Search - Latest - (EXPERIMENTAL)']
337
+ database_choice = st.selectbox('πŸ“š Select Database', options=database_options, index=0)
338
+
339
+ # πŸ•΅οΈβ€β™‚οΈ Searching the glossary for: query
340
+ all_results = ""
341
+ st.markdown(f"- {query}")
342
+
343
+ # πŸ” ArXiv RAG researcher expert ~-<>-~ Paper Summary & Ask LLM
344
+ client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
345
+
346
+ # πŸ” ArXiv RAG researcher expert ~-<>-~ Paper Summary & Ask LLM - api_name: /ask_llm
347
+ result = client.predict(
348
+ prompt=query,
349
+ llm_model_picked="mistralai/Mixtral-8x7B-Instruct-v0.1",
350
+ stream_outputs=True,
351
+ api_name="/ask_llm"
352
+ )
353
+ st.markdown(result)
354
+ st.code(result, language="python", line_numbers=True)
355
+
356
+ # πŸ” ArXiv RAG researcher expert ~-<>-~ Paper Summary & Ask LLM - api_name: /ask_llm
357
+ result2 = client.predict(
358
+ prompt=query,
359
+ llm_model_picked="mistralai/Mistral-7B-Instruct-v0.2",
360
+ stream_outputs=True,
361
+ api_name="/ask_llm"
362
+ )
363
+ st.markdown(result2)
364
+ st.code(result2, language="python", line_numbers=True)
365
+
366
+ # πŸ” ArXiv RAG researcher expert ~-<>-~ Paper Summary & Ask LLM - api_name: /ask_llm
367
+ result3 = client.predict(
368
+ prompt=query,
369
+ llm_model_picked="google/gemma-7b-it",
370
+ stream_outputs=True,
371
+ api_name="/ask_llm"
372
+ )
373
+ st.markdown(result3)
374
+ st.code(result3, language="python", line_numbers=True)
375
+
376
+ # πŸ” ArXiv RAG researcher expert ~-<>-~ Paper Summary & Ask LLM - api_name: /update_with_rag_md
377
+ response2 = client.predict(
378
+ message=query, # str in 'parameter_13' Textbox component
379
+ llm_results_use=10,
380
+ database_choice="Semantic Search",
381
+ llm_model_picked="mistralai/Mistral-7B-Instruct-v0.2",
382
+ api_name="/update_with_rag_md"
383
+ )
384
+
385
+ st.markdown(response2[0])
386
+ st.code(response2[0], language="python", line_numbers=True, wrap_lines=True)
387
+
388
+ st.markdown(response2[1])
389
+ st.code(response2[1], language="python", line_numbers=True, wrap_lines=True)
390
+
391
+ return result, result2, result3, response2
392
+
393
+
394
+
395
+
396
+
397
+ # πŸ“ Generate a safe filename from the first few lines of content
398
+ def generate_filename_from_content(content, file_type="md"):
399
+ # Extract the first few lines or sentences
400
+ first_sentence = content.split('\n', 1)[0][:90] # Limit the length to 90 characters
401
+ # Remove special characters to make it a valid filename
402
+ safe_name = re.sub(r'[^\w\s-]', '', first_sentence)
403
+ # Limit length to be compatible with Windows and Linux
404
+ safe_name = safe_name[:50].strip() # Adjust length limit
405
+ return f"{safe_name}.{file_type}"
406
+
407
+ # πŸ’Ύ Create and save a file
408
+ def create_file_from_content(content, should_save=True):
409
+ if not should_save:
410
+ return
411
+ filename = generate_filename_from_content(content)
412
+ with open(filename, 'w', encoding='utf-8') as file:
413
+ file.write(content)
414
+ return filename
415
+
416
+ # πŸ“‚ Display list of saved .md files in the sidebar
417
+ def display_saved_files_in_sidebar():
418
+ all_files = glob.glob("*.md")
419
+ all_files.sort(reverse=True)
420
+ all_files = [file for file in all_files if not file.lower().startswith('readme')] # Exclude README.md
421
+
422
+ st.sidebar.markdown("## πŸ“ Saved Markdown Files")
423
+ for file in all_files:
424
+ col1, col2, col3 = st.sidebar.columns([6, 2, 1])
425
+ with col1:
426
+ st.markdown(f"πŸ“„ {file}")
427
+ with col2:
428
+ st.sidebar.download_button(
429
+ label="⬇️ Download",
430
+ data=open(file, 'rb').read(),
431
+ file_name=file
432
+ )
433
+ with col3:
434
+ if st.sidebar.button("πŸ—‘", key=f"delete_{file}"):
435
+ os.remove(file)
436
+ st.rerun()
437
+
438
+
439
+ def clone_record(container, clone_id):
440
+ try:
441
+ existing_doc = container.read_item(item=clone_id, partition_key=clone_id)
442
+ new_doc = existing_doc.copy()
443
+ new_doc['id'] = generate_unique_id() # Generate new unique ID with timestamp
444
+ new_doc['name'] = new_doc['id'] # Generate new unique ID with timestamp
445
+ new_doc['createdAt'] = datetime.utcnow().isoformat() # Update the creation time
446
+ new_doc['_rid'] = None # Reset _rid or any system-managed fields
447
+ new_doc['_self'] = None
448
+ new_doc['_etag'] = None
449
+ new_doc['_attachments'] = None
450
+ new_doc['_ts'] = None # Reset timestamp to be updated by Cosmos DB automatically
451
+
452
+ # Insert the cloned document
453
+ response = container.create_item(body=new_doc)
454
+ st.success(f"Cloned document saved successfully with ID: {new_doc['id']} πŸŽ‰")
455
+
456
+ # Refresh the documents in session state
457
+ st.session_state.documents = list(container.query_items(
458
+ query="SELECT * FROM c ORDER BY c._ts DESC",
459
+ enable_cross_partition_query=True
460
+ ))
461
+
462
+ except exceptions.CosmosResourceNotFoundError:
463
+ st.error(f"Document with ID {clone_id} not found for cloning.")
464
+ except exceptions.CosmosHttpResponseError as e:
465
+ st.error(f"HTTP error occurred: {str(e)} 🚨")
466
+ except Exception as e:
467
+ st.error(f"An unexpected error occurred: {str(e)} 😱")
468
+
469
+
470
+
471
+
472
+ def create_new_blank_record(container):
473
+ try:
474
+ # Get the structure of the latest document (to preserve schema)
475
+ latest_doc = container.query_items(query="SELECT * FROM c ORDER BY c._ts DESC", enable_cross_partition_query=True, max_item_count=1)
476
+ if latest_doc:
477
+ new_doc_structure = latest_doc[0].copy()
478
+ else:
479
+ new_doc_structure = {}
480
+
481
+ new_doc = {key: "" for key in new_doc_structure.keys()} # Set all fields to blank
482
+ new_doc['id'] = generate_unique_id() # Generate new unique ID
483
+ new_doc['createdAt'] = datetime.utcnow().isoformat() # Set creation time
484
+
485
+ # Insert the new blank document
486
+ response = container.create_item(body=new_doc)
487
+ st.success(f"New blank document saved successfully with ID: {new_doc['id']} πŸŽ‰")
488
+
489
+ # Refresh the documents in session state
490
+ st.session_state.documents = list(container.query_items(
491
+ query="SELECT * FROM c ORDER BY c._ts DESC",
492
+ enable_cross_partition_query=True
493
+ ))
494
+
495
+ except exceptions.CosmosHttpResponseError as e:
496
+ st.error(f"HTTP error occurred: {str(e)} 🚨")
497
+ except Exception as e:
498
+ st.error(f"An unexpected error occurred: {str(e)} 😱")
499
+
500
+ # Function to preprocess the pasted content
501
+ def preprocess_text(text):
502
+ # Replace CRLF and other newline variations with the JSON newline escape sequence
503
+ text = text.replace('\r\n', '\\n')
504
+ text = text.replace('\r', '\\n')
505
+ text = text.replace('\n', '\\n')
506
+
507
+ # Escape double quotes inside the text
508
+ text = text.replace('"', '\\"')
509
+
510
+ # Optionally remove or handle other special characters that might not be JSON-safe
511
+ # Here, we remove characters like tabs or non-ASCII characters (as an example)
512
+ text = re.sub(r'[\t]', ' ', text) # Replace tabs with spaces
513
+ text = re.sub(r'[^\x00-\x7F]+', '', text) # Remove non-ASCII characters
514
+
515
+ # Normalize spaces (strip leading/trailing whitespace)
516
+ text = text.strip()
517
+ return text
518
+
519
+
520
+
521
+
522
+ # 🎭 Main function - "All the world's a stage, and all the code merely players" -Shakespeare, probably
523
+ def main():
524
+ st.title("πŸ™Git🌌CosmosπŸ’« - Azure Cosmos DB and Github Agent")
525
+
526
+
527
+
528
+ # 🎲 Session state vars - "Life is like a session state, you never know what you're gonna get"
529
+ if 'logged_in' not in st.session_state:
530
+ st.session_state.logged_in = False
531
+ if 'selected_records' not in st.session_state:
532
+ st.session_state.selected_records = []
533
+ if 'client' not in st.session_state:
534
+ st.session_state.client = None
535
+ if 'selected_database' not in st.session_state:
536
+ st.session_state.selected_database = None
537
+ if 'selected_container' not in st.session_state:
538
+ st.session_state.selected_container = None
539
+ if 'selected_document_id' not in st.session_state:
540
+ st.session_state.selected_document_id = None
541
+ if 'current_index' not in st.session_state:
542
+ st.session_state.current_index = 0
543
+ if 'cloned_doc' not in st.session_state:
544
+ st.session_state.cloned_doc = None
545
+
546
+
547
+
548
+ # πŸ” Query processing - "To search or not to search, that is the query"
549
+ try:
550
+ query_params = st.query_params
551
+ query = query_params.get('q') or query_params.get('query') or ''
552
+ if query:
553
+ result, result2, result3, response2 = search_glossary(query)
554
+
555
+ # πŸ’Ύ Save results - "Every file you save is a future you pave"
556
+ try:
557
+ if st.button("Save AI Output"):
558
+ filename = create_file_from_content(result)
559
+ st.success(f"File saved: {filename}")
560
+ filename = create_file_from_content(result2)
561
+ st.success(f"File saved: {filename}")
562
+ filename = create_file_from_content(result3)
563
+ st.success(f"File saved: {filename}")
564
+ filename = create_file_from_content(response2)
565
+ st.success(f"File saved: {filename}")
566
+
567
+ display_saved_files_in_sidebar()
568
+ except Exception as e:
569
+ st.error(f"An unexpected error occurred: {str(e)} 😱")
570
+
571
+ # 🌟 Cosmos DB operations - "In Cosmos DB we trust, but we still handle errors we must"
572
+ try:
573
+ save_to_cosmos_db(st.session_state.cosmos_container, query, result, result)
574
+ save_to_cosmos_db(st.session_state.cosmos_container, query, result2, result2)
575
+ save_to_cosmos_db(st.session_state.cosmos_container, query, result3, result3)
576
+ save_to_cosmos_db(st.session_state.cosmos_container, query, response2[0], response2[0])
577
+ save_to_cosmos_db(st.session_state.cosmos_container, query, response2[1], response2[1])
578
+ except exceptions.CosmosHttpResponseError as e:
579
+ st.error(f"HTTP error occurred: {str(e)} 🚨")
580
+ except Exception as e:
581
+ st.error(f"An unexpected error occurred: {str(e)} 😱")
582
+
583
+ st.stop()
584
+ except Exception as e:
585
+ st.markdown(' ')
586
+
587
+
588
+
589
+ # πŸ” Auth check - "With great keys come great connectivity"
590
+ if Key:
591
+ st.session_state.primary_key = Key
592
+ st.session_state.logged_in = True
593
+ else:
594
+ st.error("Cosmos DB Key is not set in environment variables. πŸ”‘βŒ")
595
+ return
596
+
597
+
598
+
599
+ if st.session_state.logged_in:
600
+ # 🌌 DB initialization - "In the beginning, there was connection string..."
601
+ try:
602
+ if st.session_state.client is None:
603
+ st.session_state.client = CosmosClient(ENDPOINT, credential=st.session_state.primary_key)
604
+
605
+ # πŸ“š Navigation setup - "Navigation is not about where you are, but where you're going"
606
+ st.sidebar.title("πŸ™Git🌌CosmosπŸ’«πŸ—„οΈNavigator")
607
+
608
+ databases = get_databases(st.session_state.client)
609
+ selected_db = st.sidebar.selectbox("πŸ—ƒοΈ Select Database", databases)
610
+
611
+
612
+
613
+ # πŸ”„ State management - "Change is the only constant in state management"
614
+ if selected_db != st.session_state.selected_database:
615
+ st.session_state.selected_database = selected_db
616
+ st.session_state.selected_container = None
617
+ st.session_state.selected_document_id = None
618
+ st.session_state.current_index = 0
619
+ st.rerun()
620
+
621
+
622
+
623
+ if st.session_state.selected_database:
624
+ database = st.session_state.client.get_database_client(st.session_state.selected_database)
625
+ containers = get_containers(database)
626
+ selected_container = st.sidebar.selectbox("πŸ“ Select Container", containers)
627
+
628
+ # πŸ”„ Container state handling - "Container changes, state arranges"
629
+ if selected_container != st.session_state.selected_container:
630
+ st.session_state.selected_container = selected_container
631
+ st.session_state.selected_document_id = None
632
+ st.session_state.current_index = 0
633
+ st.rerun()
634
+
635
+ if st.session_state.selected_container:
636
+ container = database.get_container_client(st.session_state.selected_container)
637
+
638
+ # πŸ“¦ Export functionality - "Pack it, zip it, ship it"
639
+ if st.sidebar.button("πŸ“¦ Export Container Data"):
640
+ download_link = archive_current_container(st.session_state.selected_database,
641
+ st.session_state.selected_container,
642
+ st.session_state.client)
643
+ if download_link.startswith('<a'):
644
+ st.markdown(download_link, unsafe_allow_html=True)
645
+ else:
646
+ st.error(download_link)
647
+
648
+
649
+
650
+ # πŸ“ Document handling - "Document, document, on the wall, who's the most recent of them all?"
651
+ documents = get_documents(container)
652
+ total_docs = len(documents)
653
+
654
+ # Add a slider to let the user choose how many documents to display
655
+ num_docs_to_display = st.slider(
656
+ "Select number of documents to display", 5, 500, 20
657
+ )
658
+
659
+ # Adjust the document display logic based on the slider value
660
+ if total_docs > num_docs_to_display:
661
+ documents_to_display = documents[:num_docs_to_display]
662
+ st.sidebar.info(f"Showing top {num_docs_to_display} most recent documents.")
663
+ else:
664
+ documents_to_display = documents
665
+ st.sidebar.info(f"Showing all {len(documents_to_display)} documents.")
666
+
667
+
668
+
669
+ if documents_to_display:
670
+ # 🎨 View options - "Different strokes for different folks"
671
+ view_options = ['Show as Markdown', 'Show as Code Editor', 'Show as Edit, Save, Run AI', 'Clone Document', 'New Record']
672
+ selected_view = st.sidebar.selectbox("Select Viewer/Editor", view_options, index=2)
673
+
674
+ if selected_view == 'Show as Markdown':
675
+ Label = '# πŸ“„ Markdown view - Mark it down, mark it up'
676
+ st.markdown(Label)
677
+ total_docs = len(documents)
678
+ doc = documents[st.session_state.current_index]
679
+ st.markdown(f"#### Document ID: {doc.get('id', '')}")
680
+
681
+ # πŸ•΅οΈ Value extraction - "Finding spaces in all the right places"
682
+ values_with_space = []
683
+ def extract_values(obj):
684
+ if isinstance(obj, dict):
685
+ for k, v in obj.items():
686
+ extract_values(v)
687
+ elif isinstance(obj, list):
688
+ for item in obj:
689
+ extract_values(item)
690
+ elif isinstance(obj, str):
691
+ if ' ' in obj:
692
+ values_with_space.append(obj)
693
+
694
+ extract_values(doc)
695
+ st.markdown("#### πŸ”— Links for Extracted Texts")
696
+ for term in values_with_space:
697
+ display_glossary_entity(term)
698
+
699
+ content = json.dumps(doc, indent=2)
700
+ st.markdown(f"```json\n{content}\n```")
701
+
702
+
703
+
704
+ # β¬…οΈβž‘οΈ Navigation - "Left and right, day and night"
705
+ col_prev, col_next = st.columns([1, 1])
706
+ with col_prev:
707
+ if st.button("⬅️ Previous", key='prev_markdown'):
708
+ if st.session_state.current_index > 0:
709
+ st.session_state.current_index -= 1
710
+ st.rerun()
711
+ with col_next:
712
+ if st.button("➑️ Next", key='next_markdown'):
713
+ if st.session_state.current_index < total_docs - 1:
714
+ st.session_state.current_index += 1
715
+ st.rerun()
716
+
717
+
718
+
719
+ elif selected_view == 'Show as Code Editor':
720
+ Label = '# πŸ’» Code editor view - Code is poetry, bugs are typos'
721
+ st.markdown(Label)
722
+ total_docs = len(documents)
723
+ doc = documents[st.session_state.current_index]
724
+ st.markdown(f"#### Document ID: {doc.get('id', '')}")
725
+ doc_str = st.text_area("Edit Document",
726
+ value=json.dumps(doc, indent=2),
727
+ height=300,
728
+ key=f'code_editor_{st.session_state.current_index}')
729
+
730
+
731
+
732
+ # β¬…οΈβž‘οΈ Navigation and save controls
733
+ col_prev, col_next = st.columns([1, 1])
734
+ with col_prev:
735
+ if st.button("⬅️ Previous", key='prev_code'):
736
+ if st.session_state.current_index > 0:
737
+ st.session_state.current_index -= 1
738
+ st.rerun()
739
+ with col_next:
740
+ if st.button("➑️ Next", key='next_code'):
741
+ if st.session_state.current_index < total_docs - 1:
742
+ st.session_state.current_index += 1
743
+ st.rerun()
744
+
745
+
746
+
747
+ # πŸ’Ύ Save functionality - "Save early, save often"
748
+ if st.button("πŸ’Ύ Save Changes", key=f'save_button_{st.session_state.current_index}'):
749
+ try:
750
+ updated_doc = json.loads(doc_str)
751
+ success, message = update_record(container, updated_doc)
752
+ if success:
753
+ st.success(f"Document {updated_doc['id']} saved successfully.")
754
+ st.session_state.selected_document_id = updated_doc['id']
755
+ st.rerun()
756
+ else:
757
+ st.error(message)
758
+ except json.JSONDecodeError as e:
759
+ st.error(f"Invalid JSON: {str(e)} 🚫")
760
+
761
+
762
+
763
+
764
+ elif selected_view == 'Show as Edit, Save, Run AI':
765
+ Label = '# ✏️ Edit and save view - Edit with wisdom, save with precision'
766
+ st.markdown(Label)
767
+ st.markdown("#### Edit the document fields below:")
768
+
769
+ num_cols = len(documents_to_display)
770
+ cols = st.columns(num_cols)
771
+
772
+
773
+
774
+ for idx, (col, doc) in enumerate(zip(cols, documents_to_display)):
775
+ with col:
776
+
777
+ st.markdown(f"##### Document ID: {doc.get('id', '')}")
778
+ editable_id = st.text_input("ID", value=doc.get('id', ''), key=f'edit_id_{idx}')
779
+ editable_doc = doc.copy()
780
+ editable_doc.pop('id', None)
781
+
782
+ st.markdown(f"##### Document Name: {doc.get('name', '')}")
783
+ editable_id = st.text_input("Name", value=doc.get('name', ''), key=f'edit_name_{idx}')
784
+ editable_doc = doc.copy()
785
+ editable_doc.pop('name', None)
786
+
787
+
788
+ doc_str = st.text_area("Document Content (in JSON format)",
789
+ value=json.dumps(editable_doc, indent=2),
790
+ height=300,
791
+ key=f'doc_str_{idx}')
792
+
793
+
794
+
795
+ # πŸ’ΎπŸ€– Save and AI operations
796
+ col_save, col_ai, col_delete = st.columns(3)
797
+ with col_save:
798
+ if st.button("πŸ’Ύ Save Changes", key=f'save_button_{idx}'):
799
+ try:
800
+ updated_doc = json.loads(doc_str)
801
+ updated_doc['id'] = editable_id
802
+ success, message = update_record(container, updated_doc)
803
+ if success:
804
+ st.success(f"Document {updated_doc['id']} saved successfully.")
805
+ st.session_state.selected_document_id = updated_doc['id']
806
+ st.rerun()
807
+ else:
808
+ st.error(message)
809
+ except json.JSONDecodeError as e:
810
+ st.error(f"Invalid JSON: {str(e)} 🚫")
811
+
812
+ with col_ai:
813
+ if st.button("πŸ€– Run AI", key=f'run_with_ai_button_{idx}'):
814
+ search_glossary(json.dumps(editable_doc, indent=2))
815
+
816
+ with col_delete:
817
+ if st.button("πŸ—‘οΈ Delete Record", key=f'delete_button_{idx}'):
818
+ success, message = delete_record(container, doc.get('name', ''), doc.get('id', ''))
819
+ if success:
820
+ st.success(message)
821
+ st.rerun() # Refresh the page after deletion
822
+ else:
823
+ st.error(message)
824
+
825
+
826
+ elif selected_view == 'Clone Document':
827
+ st.markdown("#### Clone a document:")
828
+
829
+ for idx, doc in enumerate(documents_to_display):
830
+ st.markdown(f"##### Original Document ID: {doc.get('id', '')}")
831
+
832
+ if st.button("πŸ“„ Clone Document", key=f'clone_button_{idx}'):
833
+ # Create new document with unique ID and name
834
+ new_doc = {
835
+ 'id': str(uuid.uuid4()),
836
+ 'name': f"Clone_{str(uuid.uuid4())[:8]}",
837
+ **{k: v for k, v in doc.items() if k not in ['id', 'name', '_rid', '_self', '_etag', '_attachments', '_ts']}
838
+ }
839
+
840
+ # Show editable preview
841
+ edited_doc = st.text_area(
842
+ "Edit cloned document:",
843
+ value=json.dumps(new_doc, indent=2),
844
+ height=300,
845
+ key=f'edit_clone_{idx}'
846
+ )
847
+
848
+ if st.button("πŸ’Ύ Save Clone", key=f'save_clone_{idx}'):
849
+ try:
850
+ final_doc = json.loads(edited_doc)
851
+ # Use container.create_item() instead of update_record()
852
+ response = container.create_item(body=final_doc)
853
+ if response:
854
+ st.success(f"New cloned document created with ID: {final_doc['id']}")
855
+ st.rerun()
856
+ else:
857
+ st.error("Failed to create new document")
858
+ except Exception as e:
859
+ st.error(f"Error creating document: {str(e)}")
860
+ elif selected_view == 'New Record':
861
+ st.markdown("#### Create a new document:")
862
+
863
+ if st.button("πŸ€– Insert Auto-Generated Record"):
864
+ auto_doc = {
865
+ "id": generate_unique_id(),
866
+ "name": f"Auto-generated Record {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
867
+ "content": "This is an auto-generated record.",
868
+ "timestamp": datetime.now().isoformat()
869
+ }
870
+ success, message = save_or_clone_to_cosmos_db(container, document=auto_doc)
871
+ if success:
872
+ st.success(message)
873
+ st.rerun()
874
+ else:
875
+ st.error(message)
876
+ else:
877
+ new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
878
+ default_doc = {
879
+ "id": new_id,
880
+ "name": "New Document",
881
+ "content": "",
882
+ "timestamp": datetime.now().isoformat()
883
+ }
884
+ new_doc_str = st.text_area("Document Content (in JSON format)",
885
+ value=json.dumps(default_doc, indent=2),
886
+ height=300)
887
+
888
+ if st.button("βž• Create New Document"):
889
+ try:
890
+ # Preprocess the text before loading it into JSON
891
+ cleaned_doc_str = preprocess_text(new_doc_str)
892
+ new_doc = json.loads(cleaned_doc_str)
893
+ new_doc['id'] = new_id # Ensure ID matches input field
894
+
895
+ success, message = insert_record(container, new_doc)
896
+ if success:
897
+ st.success(f"New document created with id: {new_doc['id']} πŸŽ‰")
898
+ st.session_state.selected_document_id = new_doc['id']
899
+ st.rerun()
900
+ else:
901
+ st.error(message)
902
+ except json.JSONDecodeError as e:
903
+ st.error(f"Invalid JSON: {str(e)} 🚫")
904
+
905
+ st.subheader(f"πŸ“Š Container: {st.session_state.selected_container}")
906
+ if st.session_state.selected_container:
907
+ if documents_to_display:
908
+ Label = '# πŸ“Š Data display - Data tells tales that words cannot'
909
+ st.markdown(Label)
910
+ df = pd.DataFrame(documents_to_display)
911
+ st.dataframe(df)
912
+ else:
913
+ st.info("No documents to display. 🧐")
914
+
915
+ elif selected_view == 'New Record2':
916
+ st.markdown("#### Create a new document:")
917
+
918
+ if st.button("πŸ€– Insert Auto-Generated Record"):
919
+ auto_doc = {
920
+ "id": generate_unique_id(),
921
+ "name": f"Auto-generated Record {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
922
+ "content": "This is an auto-generated record.",
923
+ "timestamp": datetime.now().isoformat()
924
+ }
925
+ success, message = save_or_clone_to_cosmos_db(container, document=auto_doc)
926
+ if success:
927
+ st.success(message)
928
+ st.rerun()
929
+ else:
930
+ st.error(message)
931
+ else:
932
+ new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
933
+ default_doc = {
934
+ "id": new_id,
935
+ "name": "New Document",
936
+ "content": "",
937
+ "timestamp": datetime.now().isoformat()
938
+ }
939
+ new_doc_str = st.text_area("Document Content (in JSON format)",
940
+ value=json.dumps(default_doc, indent=2),
941
+ height=300)
942
+
943
+ if st.button("βž• Create New Document"):
944
+ try:
945
+ new_doc = json.loads(new_doc_str)
946
+ new_doc['id'] = new_id # Ensure ID matches input field
947
+ success, message = insert_record(container, new_doc)
948
+ if success:
949
+ st.success(f"New document created with id: {new_doc['id']} πŸŽ‰")
950
+ st.session_state.selected_document_id = new_doc['id']
951
+ st.rerun()
952
+ else:
953
+ st.error(message)
954
+ except json.JSONDecodeError as e:
955
+ st.error(f"Invalid JSON: {str(e)} 🚫")
956
+
957
+
958
+ st.subheader(f"πŸ“Š Container: {st.session_state.selected_container}")
959
+ if st.session_state.selected_container:
960
+ if documents_to_display:
961
+ Label = '# πŸ“Š Data display - Data tells tales that words cannot'
962
+ st.markdown(Label)
963
+ df = pd.DataFrame(documents_to_display)
964
+ st.dataframe(df)
965
+ else:
966
+ st.info("No documents to display. 🧐")
967
+
968
+
969
+
970
+ Label = '# πŸ™ GitHub integration - Git happens'
971
+ st.subheader("πŸ™ GitHub Operations")
972
+ github_token = os.environ.get("GITHUB")
973
+ source_repo = st.text_input("Source GitHub Repository URL",
974
+ value="https://github.com/AaronCWacker/AIExamples-8-24-Streamlit")
975
+ new_repo_name = st.text_input("New Repository Name (for cloning)",
976
+ value=f"AIExample-Clone-{datetime.now().strftime('%Y%m%d_%H%M%S')}")
977
+
978
+
979
+
980
+ col1, col2 = st.columns(2)
981
+ with col1:
982
+ if st.button("πŸ“₯ Clone Repository"):
983
+ if github_token and source_repo:
984
+
985
+ st.markdown(Label)
986
+ try:
987
+ local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d%H%M%S')}"
988
+ download_github_repo(source_repo, local_path)
989
+ zip_filename = f"{new_repo_name}.zip"
990
+ create_zip_file(local_path, zip_filename[:-4])
991
+ st.markdown(get_download_link(zip_filename), unsafe_allow_html=True)
992
+ st.success("Repository cloned successfully! πŸŽ‰")
993
+ except Exception as e:
994
+ st.error(f"An error occurred: {str(e)} 😒")
995
+ finally:
996
+ if os.path.exists(local_path):
997
+ shutil.rmtree(local_path)
998
+ if os.path.exists(zip_filename):
999
+ os.remove(zip_filename)
1000
+ else:
1001
+ st.error("Please ensure GitHub token is set in environment variables and source repository URL is provided. πŸ”‘β“")
1002
+
1003
+
1004
+
1005
+ with col2:
1006
+ if st.button("πŸ“€ Push to New Repository"):
1007
+ if github_token and source_repo:
1008
+
1009
+ st.markdown(Label)
1010
+ try:
1011
+ g = Github(github_token)
1012
+ new_repo = create_repo(g, new_repo_name)
1013
+ local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d%H%M%S')}"
1014
+ download_github_repo(source_repo, local_path)
1015
+ push_to_github(local_path, new_repo, github_token)
1016
+ st.success(f"Repository pushed successfully to {new_repo.html_url} πŸš€")
1017
+ except Exception as e:
1018
+ st.error(f"An error occurred: {str(e)} 😒")
1019
+ finally:
1020
+ if os.path.exists(local_path):
1021
+ shutil.rmtree(local_path)
1022
+ else:
1023
+ st.error("Please ensure GitHub token is set in environment variables and source repository URL is provided. πŸ”‘β“")
1024
+
1025
+
1026
+
1027
+ st.subheader("πŸ’¬ Chat with Claude")
1028
+ user_input = st.text_area("Message πŸ“¨:", height=100)
1029
+
1030
+ if st.button("Send πŸ“¨"):
1031
+ Label = '# πŸ’¬ Chat functionality - Every chat is a chance to learn'
1032
+ st.markdown(Label)
1033
+ if user_input:
1034
+ response = client.messages.create(
1035
+ model="claude-3-sonnet-20240229",
1036
+ max_tokens=1000,
1037
+ messages=[
1038
+ {"role": "user", "content": user_input}
1039
+ ]
1040
+ )
1041
+ st.write("Claude's reply 🧠:")
1042
+ st.write(response.content[0].text)
1043
+
1044
+ filename = generate_filename(user_input, "md")
1045
+ create_file(filename, user_input, response.content[0].text)
1046
+
1047
+ st.session_state.chat_history.append({"user": user_input, "claude": response.content[0].text})
1048
+
1049
+ # Save to Cosmos DB
1050
+ save_to_cosmos_db(container, user_input, response.content[0].text, "")
1051
+
1052
+
1053
+
1054
+ # πŸ“œ Chat history display - "History repeats itself, first as chat, then as wisdom"
1055
+ st.subheader("Past Conversations πŸ“œ")
1056
+ for chat in st.session_state.chat_history:
1057
+ st.text_area("You said πŸ’¬:", chat["user"], height=100, disabled=True)
1058
+ st.text_area("Claude replied πŸ€–:", chat["claude"], height=200, disabled=True)
1059
+ st.markdown("---")
1060
+
1061
+
1062
+
1063
+ # πŸ“ File editor - "Edit with care, save with flair"
1064
+ if hasattr(st.session_state, 'current_file'):
1065
+ st.subheader(f"Editing: {st.session_state.current_file} πŸ› ")
1066
+ new_content = st.text_area("File Content ✏️:", st.session_state.file_content, height=300)
1067
+
1068
+ # Preprocess the text before loading it into JSON - Added to protect copy paste into JSON to keep format.
1069
+ cleaned_doc_str = preprocess_text(new_content)
1070
+ new_doc = json.loads(cleaned_doc_str)
1071
+ new_content = cleaned_doc_str
1072
+
1073
+ if st.button("Save Changes πŸ’Ύ"):
1074
+ with open(st.session_state.current_file, 'w', encoding='utf-8') as file:
1075
+ file.write(new_content)
1076
+ st.success("File updated successfully! πŸŽ‰")
1077
+
1078
+
1079
+
1080
+ # πŸ“‚ File management - "Manage many, maintain order"
1081
+ st.sidebar.title("πŸ“ File Management")
1082
+
1083
+ all_files = glob.glob("*.md")
1084
+ all_files.sort(reverse=True)
1085
+
1086
+ if st.sidebar.button("πŸ—‘ Delete All Files"):
1087
+ for file in all_files:
1088
+ os.remove(file)
1089
+ st.rerun()
1090
+
1091
+ if st.sidebar.button("⬇️ Download All Files"):
1092
+ zip_file = create_zip_of_files(all_files)
1093
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
1094
+
1095
+ for file in all_files:
1096
+ col1, col2, col3, col4 = st.sidebar.columns([1,3,1,1])
1097
+ with col1:
1098
+ if st.button("🌐", key="view_"+file):
1099
+ st.session_state.current_file = file
1100
+ st.session_state.file_content = load_file(file)
1101
+ with col2:
1102
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
1103
+ with col3:
1104
+ if st.button("πŸ“‚", key="edit_"+file):
1105
+ st.session_state.current_file = file
1106
+ st.session_state.file_content = load_file(file)
1107
+ with col4:
1108
+ if st.button("πŸ—‘", key="delete_"+file):
1109
+ os.remove(file)
1110
+ st.rerun()
1111
+
1112
+
1113
+
1114
+ except exceptions.CosmosHttpResponseError as e:
1115
+ st.error(f"Failed to connect to Cosmos DB. HTTP error: {str(e)} 🚨")
1116
+ except Exception as e:
1117
+ st.error(f"An unexpected error occurred: {str(e)} 😱")
1118
+
1119
+
1120
+
1121
+ if st.session_state.logged_in and st.sidebar.button("πŸšͺ Logout"):
1122
+ Label = '# πŸšͺ Logout - All good things must come to an end'
1123
+ st.markdown(Label)
1124
+ st.session_state.logged_in = False
1125
+ st.session_state.selected_records.clear()
1126
+ st.session_state.client = None
1127
+ st.session_state.selected_database = None
1128
+ st.session_state.selected_container = None
1129
+ st.session_state.selected_document_id = None
1130
+ st.session_state.current_index = 0
1131
+ st.rerun()
1132
+
1133
+ if __name__ == "__main__":
1134
+ main()