Spaces:
Sleeping
Sleeping
File size: 11,028 Bytes
2197ab7 5b40ec9 2197ab7 5fe3652 2197ab7 5fe3652 2197ab7 5fe3652 2197ab7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
import streamlit as st
import os
import pyperclip
from pathlib import Path
from src.fabrics_processor.database import initialize_qdrant_database
from src.fabrics_processor.database_updater import update_qdrant_database
from src.fabrics_processor.file_change_detector import detect_file_changes
from src.search_qdrant.database_query import query_qdrant_database
from src.fabrics_processor.obsidian2fabric import sync_folders
from src.fabrics_processor.logger import setup_logger
import logging
import atexit
from src.fabrics_processor.config import config
from src.fabrics_processor.deduplicator import remove_duplicates
# Configure logging
logger = setup_logger()
def init_session_state():
"""Initialize session state variables."""
if 'client' not in st.session_state:
client = initialize_qdrant_database(api_key=st.secrets["api_key"])
st.session_state.client = client
# Register cleanup function
atexit.register(lambda: client.close() if hasattr(client, '_transport') else None)
if 'selected_prompts' not in st.session_state:
st.session_state.selected_prompts = []
if 'comparing' not in st.session_state:
st.session_state.comparing = False
if 'comparison_selected' not in st.session_state:
st.session_state.comparison_selected = None
def show_comparison_view(prompts):
"""Show a full-width comparison view of the selected prompts."""
st.write("## Compare Selected Prompts")
# Add the back button at the top
if st.button("Back to search"):
st.session_state.comparing = False
st.rerun()
# Create columns for each prompt
cols = st.columns(len(prompts))
# Track which prompt is selected for copying
selected_idx = None
for idx, (col, prompt) in enumerate(zip(cols, prompts)):
with col:
st.markdown(f"### {prompt.metadata['filename']}")
# Create two columns for trigger and button
trigger_col, button_col = st.columns([0.7, 0.3])
with trigger_col:
# Add trigger field
current_trigger = prompt.metadata.get('trigger', '')
new_trigger = st.text_input("Trigger",
value=current_trigger,
key=f"trigger_{idx}")
# Update trigger if changed
if new_trigger != current_trigger:
try:
st.session_state.client.set_payload(
collection_name=config.embedding.collection_name,
payload={"trigger": new_trigger},
points=[prompt.id]
)
st.success(f"Updated trigger to: {new_trigger}")
except Exception as e:
st.error(f"Failed to update trigger: {str(e)}")
with button_col:
# Align button with text input using empty space
st.write("") # This creates some vertical space
if st.button(f"Use this prompt", key=f"compare_use_{idx}"):
selected_idx = idx
# Display content as markdown
st.markdown("### Content")
st.markdown(prompt.metadata["content"])
# Handle selection
if selected_idx is not None:
pyperclip.copy(prompts[selected_idx].metadata['content'])
st.success(f"Copied {prompts[selected_idx].metadata['filename']} to clipboard!")
# Clear comparison view
st.session_state.comparing = False
st.rerun()
def search_interface():
"""Show the search interface."""
if st.session_state.comparing:
show_comparison_view(st.session_state.selected_prompts)
return
st.subheader("Search for prompts")
query = st.text_area("What are you trying to accomplish? I will then search for good prompts to give you a good start.")
if query:
try:
results = query_qdrant_database(
query=query,
client=st.session_state.client,
num_results=5,
collection_name=config.embedding.collection_name
)
if results:
st.write("Which prompts would you like to investigate? Max 3.")
# Create checkboxes for selection
selected = []
for r in results:
if st.checkbox(f"{r.metadata['filename']}", key=f"select_{r.id}"):
selected.append(r)
st.session_state.selected_prompts = selected
if selected:
col1, col2 = st.columns(2)
with col1:
if st.button("Use: copy to clipboard"):
if len(selected) == 1:
pyperclip.copy(selected[0].metadata['content'])
st.success("Copied to clipboard!")
with col2:
if len(selected) > 1 and st.button("Compare"):
st.session_state.comparing = True
st.rerun()
except Exception as e:
logger.error(f"Error in search_interface: {e}", exc_info=True)
st.error(f"Error searching database: {e}")
def update_database():
"""Update the markdown folder with prompt files from Obsidian.
Then update the Qdrant database.
Finally based on the Qdrant database create a new espanso YAML file and
the Obsidian Textgenerator markdown files."""
try:
with st.spinner("Processing markdown files..."):
# First check if there are any changes in the prompt files in Obsidian.
# If so, add them to the markdown folder before updating the database.
sync_folders(source_dir=Path(config.obsidian_input_folder), target_dir=Path(config.fabric_patterns_folder))
# Get current collection info
collection_info = st.session_state.client.get_collection(config.embedding.collection_name)
initial_points = collection_info.points_count
# Detect file changes
new_files, modified_files, deleted_files = detect_file_changes(
client=st.session_state.client,
fabric_patterns_folder=config.fabric_patterns_folder
)
# Update the database if there are changes
if any([new_files, modified_files, deleted_files]):
st.info("Changes detected. Updating database...")
update_qdrant_database(
client=st.session_state.client,
collection_name=config.embedding.collection_name,
new_files=new_files,
modified_files=modified_files,
deleted_files=deleted_files
)
else:
st.info("No changes detected in input folders.")
# Create a separate section for deduplication - ALWAYS run this regardless of file changes
st.subheader("Deduplication Process")
with st.spinner("Checking for and removing duplicate entries..."):
# Run the deduplication process
duplicates_removed = remove_duplicates(
client=st.session_state.client,
collection_name=config.embedding.collection_name
)
if duplicates_removed > 0:
st.success(f"Successfully removed {duplicates_removed} duplicate entries from the database")
else:
st.info("No duplicate entries found in the database")
# Get updated collection info
collection_info = st.session_state.client.get_collection(config.embedding.collection_name)
final_points = collection_info.points_count
# Show summary
st.success(f"""
Database update completed successfully!
Changes detected:
- {len(new_files)} new files
- {len(modified_files)} modified files
- {len(deleted_files)} deleted files
- {duplicates_removed} duplicate entries removed
Database entries:
- Initial: {initial_points}
- Final: {final_points}
""")
except Exception as e:
logger.error(f"Error updating database: {e}", exc_info=True)
st.error(f"Error updating database: {e}")
def display_trigger_table():
"""Display the trigger table in the sidebar."""
with st.sidebar:
# Add some space to push the table to the bottom
st.markdown("<br>" * 10, unsafe_allow_html=True)
# Create the table
st.markdown("""
| trigger | description |
|---------|-------------|
| ;;c | code |
| ;;s | summarize and extract |
| ;;t | think |
""")
def main():
st.set_page_config(
page_title="Fabric to Espanso Prompt Manager",
layout="wide")
init_session_state()
# Sidebar
with st.sidebar:
# Add logo to sidebar
image_path = Path(__file__).parent.parent.parent / "data" / "Fab2Esp_transparent.png"
st.image(str(image_path), width=200, use_container_width=False)
st.title("Prompt Manager")
page = st.radio("Select Option:", ["Search for prompts", "Update database and prompt files"])
if st.button("Quit"):
if hasattr(st.session_state.client, '_transport'):
st.session_state.client.close()
st.success("Database connection closed.")
st.stop()
# Main content
if page == "Search for prompts":
search_interface()
else:
st.subheader("Update Database")
if st.button("Start Update"):
update_database()
# Add the trigger table at the end
display_trigger_table()
# Add credits at the bottom left
st.markdown("""
<style>
.credits {
position: fixed;
left: 1rem;
bottom: 1rem;
font-size: 0.8rem;
color: #666;
max-width: 600px;
}
</style>
<div class="credits">
This tool searches the great list of prompts available at <a href="https://github.com/danielmiessler/fabric">https://github.com/danielmiessler/fabric</a>.
A great commandline utilty build by Daniel Miessler to make the use of LLM more frictionless.<br>
All credits to him and his fellow fabric builders.
</div>
""", unsafe_allow_html=True)
if __name__ == "__main__":
main()
|