danielrosehill/Assistant-Config-Library
0
1import os2import streamlit as st3import pyperclip4import yaml5from datetime import datetime6 7# Function to format date8def format_date(timestamp):9 return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")10 11# Function to convert folder names to readable format12def format_folder_name(folder_name):13 return folder_name.replace("-", " ").title()14 15# Function to load markdown files and extract YAML front matter and content16def load_markdown(file_path):17 with open(file_path, 'r') as file:18 content = file.read()19 20 # Check for YAML front matter21 if content.startswith('---'):22 parts = content.split('---', 2)23 if len(parts) >= 3:24 yaml_content = parts[1].strip()25 markdown_content = parts[2].strip()26 try:27 front_matter = yaml.safe_load(yaml_content)28 # Ensure tags is a list29 if 'tags' in front_matter:30 if isinstance(front_matter['tags'], str):31 front_matter['tags'] = [tag.strip() for tag in front_matter['tags'].split(',')]32 else:33 front_matter['tags'] = []34 return front_matter, markdown_content35 except yaml.YAMLError:36 return {'tags': []}, content37 return {'tags': []}, content38 39# Function to extract the first header from markdown content40def extract_title_and_body(markdown_content):41 lines = markdown_content.split('\n')42 title = "Untitled Configuration"43 body = markdown_content44 45 for i, line in enumerate(lines):46 if line.startswith('# '):47 title = line.strip('# ').strip()48 body = '\n'.join(lines[i+1:]).strip()49 break50 51 return title, body52 53# Function to recursively build the sidebar navigation54def build_sidebar_navigation(base_path, current_path):55 items = os.listdir(current_path)56 for item in sorted(items):57 item_path = os.path.join(current_path, item)58 if os.path.isdir(item_path):59 with st.sidebar.expander(format_folder_name(item)):60 build_sidebar_navigation(base_path, item_path)61 elif item.endswith('.md'):62 front_matter, markdown_content = load_markdown(item_path)63 title, _ = extract_title_and_body(markdown_content)64 # Add binoculars icon if vision is enabled65 vision = front_matter.get("vision", "")66 vision_icon = " ๐ญ" if str(vision).lower() == "yes" else ""67 if st.sidebar.button(f"{title}{vision_icon}", key=item_path):68 st.session_state['selected_file'] = item_path69 70# Function to search for configurations71def search_configurations(base_path, search_term, selected_tags=None):72 matches = []73 for root, dirs, files in os.walk(base_path):74 for file in files:75 if file.endswith('.md'):76 file_path = os.path.join(root, file)77 front_matter, markdown_content = load_markdown(file_path)78 title, body = extract_title_and_body(markdown_content)79 80 # Check if content matches search term and tags81 content_matches = search_term.lower() in markdown_content.lower()82 tags_match = True83 if selected_tags:84 tags_match = any(tag in front_matter.get('tags', []) for tag in selected_tags)85 86 if content_matches and tags_match:87 matches.append((file_path, title, front_matter.get('tags', [])))88 return matches89 90# Main function to run the Streamlit app91def main():92 st.set_page_config(page_title="AI Agent Configurations", layout="wide")93 94 # Define the base path for agent configurations95 base_path = "agent-configs"96 97 # Initialize session state98 if 'selected_file' not in st.session_state:99 st.session_state['selected_file'] = None100 if 'dark_mode' not in st.session_state:101 st.session_state['dark_mode'] = False102 if 'favorites' not in st.session_state:103 st.session_state['favorites'] = set()104 105 # Apply dark mode if enabled106 if st.session_state['dark_mode']:107 st.markdown("""108 <style>109 .stApp {110 background-color: #1E1E1E;111 color: #FFFFFF;112 }113 .sidebar .sidebar-content {114 background-color: #2D2D2D;115 }116 </style>117 """, unsafe_allow_html=True)118 119 # Sidebar for navigation and search120 st.sidebar.title("AI Agent Configurations")121 122 # Dark mode toggle123 st.sidebar.checkbox("Dark Mode", key="dark_mode", value=st.session_state['dark_mode'])124 125 # GitHub repository badge in sidebar126 st.sidebar.markdown(127 "[](https://github.com/danielrosehill/LLM-Assistants-Web-Library)",128 unsafe_allow_html=True129 )130 131 # Collect all configurations and their metadata132 all_configs = []133 all_tags = set()134 for root, _, files in os.walk(base_path):135 for file in files:136 if file.endswith('.md'):137 file_path = os.path.join(root, file)138 front_matter, _ = load_markdown(file_path)139 title, _ = extract_title_and_body(_)140 last_modified = os.path.getmtime(file_path)141 all_configs.append({142 'path': file_path,143 'title': title,144 'tags': front_matter.get('tags', []),145 'last_modified': last_modified146 })147 all_tags.update(front_matter.get('tags', []))148 149 # Sorting options150 sort_options = {151 'Title (A-Z)': lambda x: x['title'].lower(),152 'Title (Z-A)': lambda x: x['title'].lower(),153 'Last Modified (Newest)': lambda x: x['last_modified'],154 'Last Modified (Oldest)': lambda x: x['last_modified']155 }156 sort_by = st.sidebar.selectbox('Sort by', list(sort_options.keys()))157 158 # Sort configurations159 all_configs.sort(key=sort_options[sort_by])160 if sort_by == 'Title (Z-A)' or sort_by == 'Last Modified (Newest)':161 all_configs.reverse()162 163 # Search and filter functionality164 search_term = st.sidebar.text_input("Search configurations")165 selected_tags = st.sidebar.multiselect("Filter by tags", sorted(list(all_tags)))166 167 # Show favorites section if there are any168 if st.session_state['favorites']:169 st.sidebar.markdown("### Favorites")170 for file_path in st.session_state['favorites']:171 front_matter, _ = load_markdown(file_path)172 title, _ = extract_title_and_body(_)173 if st.sidebar.button(f"โญ {title}", key=f"fav_{file_path}"):174 st.session_state['selected_file'] = file_path175 st.sidebar.divider()176 177 if search_term or selected_tags:178 matches = search_configurations(base_path, search_term, selected_tags)179 if matches:180 st.sidebar.write("Search Results:")181 for file_path, title, tags in matches:182 if st.sidebar.button(f"{title} ({', '.join(tags)})", key=f"search_{file_path}"):183 st.session_state['selected_file'] = file_path184 else:185 st.sidebar.write("No matches found.")186 else:187 # Build the sidebar navigation188 build_sidebar_navigation(base_path, base_path)189 190 # Main content area191 st.title("Daniel Rosehill AI Assistant Library")192 st.markdown(193 """194 <div style="background-color: #f0f0f0; padding: 10px; border-radius: 10px; margin-bottom: 20px;">195 <p style="margin: 0;">This microsite contains open source configurations for AI assistants.</p>196 </div>197 """,198 unsafe_allow_html=True199 )200 201 if st.session_state['selected_file']:202 front_matter, markdown_content = load_markdown(st.session_state['selected_file'])203 title, body = extract_title_and_body(markdown_content)204 205 # Display title and metadata206 st.markdown(f"# {title}")207 208 # Display tags if present209 if front_matter.get('tags'):210 st.markdown("**Tags:** " + ", ".join(f"`{tag}`" for tag in front_matter['tags']))211 212 # Display last modified date213 last_modified = os.path.getmtime(st.session_state['selected_file'])214 st.markdown(f"**Last Modified:** {format_date(last_modified)}")215 216 # Create a container for buttons217 with st.container():218 st.write("Options:")219 button_col1, button_col2, button_col3, button_col4, _ = st.columns([2, 2, 2, 2, 4])220 221 with button_col1:222 if st.button("๐ Copy Title", 223 key="copy_title",224 help="Copy title to clipboard",225 type="secondary"):226 pyperclip.copy(title)227 st.success("Title copied!")228 229 with button_col2:230 if st.button("๐ Copy Content", 231 key="copy_body",232 help="Copy full content to clipboard",233 type="secondary"):234 pyperclip.copy(body)235 st.success("Content copied!")236 237 with button_col3:238 is_favorite = st.session_state['selected_file'] in st.session_state['favorites']239 if st.button("โญ " + ("Unfavorite" if is_favorite else "Favorite"),240 key="toggle_favorite",241 help="Add/remove from favorites",242 type="secondary"):243 if is_favorite:244 st.session_state['favorites'].remove(st.session_state['selected_file'])245 else:246 st.session_state['favorites'].add(st.session_state['selected_file'])247 248 with button_col4:249 if st.button("๐ Share",250 key="share_config",251 help="Copy shareable link",252 type="secondary"):253 share_url = f"https://github.com/danielrosehill/LLM-Assistants-Web-Library/blob/main/{st.session_state['selected_file']}"254 pyperclip.copy(share_url)255 st.success("Share link copied!")256 257 st.divider()258 259 # Display markdown content260 st.markdown(body, unsafe_allow_html=True)261 else:262 st.write("Select a configuration from the sidebar to view its details.")263 264 # Footer with GitHub badge265 st.markdown(266 """267 <div style="position: fixed; bottom: 0; width: 100%; background-color: #f0f0f0; padding: 10px; text-align: center;">268 <a href="https://github.com/danielrosehill/LLM-Assistants-Web-Library" target="_blank">269 <img src="https://img.shields.io/badge/View_on_GitHub-181717?style=for-the-badge&logo=github&logoColor=white" alt="View on GitHub">270 </a>271 </div>272 """,273 unsafe_allow_html=True274 )275 276if __name__ == "__main__":277 main()278 