diff --git "a/refApp.py" "b/refApp.py" --- "a/refApp.py" +++ "b/refApp.py" @@ -3,10 +3,17 @@ from datetime import datetime, date import copy import json import requests -import os + +hide_streamlit_style = """ + +""" +st.markdown(hide_streamlit_style, unsafe_allow_html=True) # --- PAGE CONFIGURATION (MUST BE THE FIRST STREAMLIT COMMAND) --- -# Fixed deployment issue st.set_page_config(layout="wide", page_title="🛠️ Le laboratoire des Prompts IA", initial_sidebar_state="collapsed" ) # --- CUSTOM CSS FOR SIDEBAR TOGGLE TEXT --- @@ -38,8 +45,8 @@ st.markdown(""" /* Cible le div conteneur direct à l'intérieur de stCodeBlock s'il existe et gère le scroll */ div[data-testid="stCodeBlock"] > div:first-child { - /* height: 120px !important; - SUPPRIMÉ : causait des problèmes de layout */ - max-height: 520px !important; /* Correspond à la valeur du pre ci-dessus */ + height: 120px !important; /* Doit correspondre à la valeur ci-dessus */ + max-height: 120px !important; overflow-y: auto !important; display: block !important; visibility: visible !important; @@ -79,23 +86,15 @@ st.markdown(""" /* === SOLUTION POUR COMPRESSION LATERALE DE LA SIDEBAR === */ /* Force le contenu principal à se comprimer au lieu d'être décalé */ section[data-testid="stSidebar"] { - width: 31.5rem !important; - min-width: 31.5rem !important; - max-width: 31.5rem !important; - } - - /* RÉDUIRE LA SIDEBAR QUAND FERMÉE */ - section[data-testid="stSidebar"][aria-expanded="false"] { - width: 0rem !important; - min-width: 0rem !important; - max-width: 0rem !important; - overflow: hidden !important; + width: 21rem !important; + min-width: 21rem !important; + max-width: 21rem !important; } /* Ajustement du conteneur principal pour la compression */ .main .block-container { - max-width: calc(100vw - 31.5rem) !important; - width: calc(100vw - 31.5rem) !important; + max-width: calc(100vw - 21rem) !important; + width: calc(100vw - 21rem) !important; } /* Quand la sidebar est fermée, reprendre toute la largeur */ @@ -110,26 +109,6 @@ st.markdown(""" transition: width 0.3s ease, max-width 0.3s ease !important; } - /* PADDING SPECIFIQUE POUR INTERPRO1_LIGHT */ - [data-testid="stMainBlockContainer"] { - padding-top: 3rem !important; - } - - .css-1d391kg, .css-18e3th9 { - padding-top: 3rem !important; - } - - /* REDUCTION SPECIFIQUE POUR LA PAGE D'ACCUEIL UNIQUEMENT */ - h1[data-testid="stHeading"]:first-of-type { - margin-top: -2rem !important; - padding-top: 0rem !important; - } - - h1:contains("Bienvenue dans votre laboratoire") { - margin-top: -2rem !important; - padding-top: 0rem !important; - } - /* Responsive: sur petits écrans, garder le comportement normal */ @media (max-width: 768px) { .main .block-container { @@ -138,172 +117,12 @@ st.markdown(""" } } - /* === RÈGLES SPÉCIFIQUES POUR st.code() QUI CAUSE LE PROBLÈME === */ - /* Forcer la compression sur TOUS les blocs de code */ - [data-testid="stCodeBlock"], - div[data-testid="stCodeBlock"], - .stCodeBlock { - max-width: 100vw !important; - width: 100vw !important; - box-sizing: border-box !important; - } - - /* Quand sidebar ouverte, compresser les blocs de code */ - section[data-testid="stSidebar"][aria-expanded="true"] ~ .main [data-testid="stCodeBlock"], - section[data-testid="stSidebar"][aria-expanded="true"] ~ .main div[data-testid="stCodeBlock"], - section[data-testid="stSidebar"][aria-expanded="true"] ~ .main .stCodeBlock { - max-width: calc(100vw - 31.5rem) !important; - width: calc(100vw - 31.5rem) !important; - } - - /* Forcer sur les éléments internes du code block */ - [data-testid="stCodeBlock"] > div, - [data-testid="stCodeBlock"] pre, - [data-testid="stCodeBlock"] code { - max-width: 100% !important; - width: 100% !important; - box-sizing: border-box !important; - overflow-x: auto !important; - } - - /* Quand sidebar ouverte, aussi sur les éléments internes */ - section[data-testid="stSidebar"][aria-expanded="true"] ~ .main [data-testid="stCodeBlock"] > div, - section[data-testid="stSidebar"][aria-expanded="true"] ~ .main [data-testid="stCodeBlock"] pre, - section[data-testid="stSidebar"][aria-expanded="true"] ~ .main [data-testid="stCodeBlock"] code { - max-width: calc(100vw - 31.5rem) !important; - width: calc(100vw - 31.5rem) !important; - } - - - + - """, unsafe_allow_html=True) - - # No vertical spacing to align at top - - # Create the access form - with st.container(): - st.markdown(""" -
- Veuillez entrer le code d'accès pour utiliser l'application -
-- Pour demander un accès, contactez votre référent métier ou l'administrateur de l'application : - arthur.causse-prestataire@laposte.fr -
- """, unsafe_allow_html=True) - - # Stop execution here if access not granted - st.stop() - # --- Session State Initialization --- if 'editable_prompts' not in st.session_state: - st.session_state.editable_prompts = load_editable_prompts_from_local() + st.session_state.editable_prompts = load_editable_prompts_from_gist() if 'view_mode' not in st.session_state: st.session_state.view_mode = "accueil" # Nouvelle vue par défaut @@ -665,18 +444,11 @@ if 'confirming_delete_details' not in st.session_state: st.session_state.confirm if 'confirming_delete_family_name' not in st.session_state: st.session_state.confirming_delete_family_name = None if 'library_search_term' not in st.session_state: st.session_state.library_search_term = "" if 'library_selected_tags' not in st.session_state: st.session_state.library_selected_tags = [] -# Track previous filter values for change detection -if 'previous_search_term' not in st.session_state: st.session_state.previous_search_term = "" -if 'previous_selected_tags' not in st.session_state: st.session_state.previous_selected_tags = [] if 'variable_type_to_create' not in st.session_state: st.session_state.variable_type_to_create = None if 'active_generated_prompt' not in st.session_state: st.session_state.active_generated_prompt = "" if 'duplicating_use_case_details' not in st.session_state: st.session_state.duplicating_use_case_details = None if 'go_to_config_section' not in st.session_state: st.session_state.go_to_config_section = False -# Generator session state variables -if 'generator_selected_family' not in st.session_state: st.session_state.generator_selected_family = None -if 'generator_selected_use_case' not in st.session_state: st.session_state.generator_selected_use_case = None - if 'injection_selected_family' not in st.session_state: st.session_state.injection_selected_family = None if 'injection_json_text' not in st.session_state: @@ -695,11 +467,235 @@ if 'assistant_existing_prompt_value' not in st.session_state: # --- Sidebar Navigation with Tabs --- st.sidebar.header("Menu Principal") -tab_bibliotheque, tab_injection = st.sidebar.tabs([ +tab_bibliotheque, tab_edition_generation, tab_injection = st.sidebar.tabs([ "📚 Bibliothèque", + "✍️ Édition", "💡 Assistant" ]) +# --- Tab: Édition (Sidebar content) --- +with tab_edition_generation: + st.subheader("Explorateur de Prompts") + available_families = list(st.session_state.editable_prompts.keys()) + default_family_idx_edit = 0 + current_family_for_edit = st.session_state.get('family_selector_edition') + + if st.session_state.force_select_family_name and st.session_state.force_select_family_name in available_families: + current_family_for_edit = st.session_state.force_select_family_name + st.session_state.family_selector_edition = current_family_for_edit + elif current_family_for_edit and current_family_for_edit in available_families: + pass + elif available_families: + current_family_for_edit = available_families[0] + st.session_state.family_selector_edition = current_family_for_edit + else: + current_family_for_edit = None + st.session_state.family_selector_edition = None + + if current_family_for_edit and current_family_for_edit in available_families: + default_family_idx_edit = available_families.index(current_family_for_edit) + elif available_families: + default_family_idx_edit = 0 + current_family_for_edit = available_families[0] + st.session_state.family_selector_edition = current_family_for_edit + else: + default_family_idx_edit = 0 + + if not available_families: + st.info("Aucune famille de métier de cas d'usage. Créez-en une via les options ci-dessous.") + else: + prev_family_selection_edit = st.session_state.get('family_selector_edition') + selected_family_ui_edit = st.selectbox( + "Métier :", + options=available_families, + index=default_family_idx_edit, + key='family_selectbox_widget_edit', + help="Sélectionnez une métier pour voir ses cas d'usage." + ) + if st.session_state.family_selector_edition != selected_family_ui_edit : + st.session_state.family_selector_edition = selected_family_ui_edit + + if prev_family_selection_edit != selected_family_ui_edit: + st.session_state.use_case_selector_edition = None + st.session_state.force_select_use_case_name = None + st.session_state.view_mode = "edit" + st.session_state.active_generated_prompt = "" + st.session_state.variable_type_to_create = None + st.session_state.editing_variable_info = None + st.rerun() + + current_selected_family_for_edit_logic = st.session_state.get('family_selector_edition') + use_cases_in_current_family_edit_options = [] + if current_selected_family_for_edit_logic and current_selected_family_for_edit_logic in st.session_state.editable_prompts: + use_cases_in_current_family_edit_options = list(st.session_state.editable_prompts[current_selected_family_for_edit_logic].keys()) + + if use_cases_in_current_family_edit_options: + default_uc_idx_edit = 0 + current_uc_for_edit = st.session_state.get('use_case_selector_edition') + + if st.session_state.force_select_use_case_name and st.session_state.force_select_use_case_name in use_cases_in_current_family_edit_options: + current_uc_for_edit = st.session_state.force_select_use_case_name + elif current_uc_for_edit and current_uc_for_edit in use_cases_in_current_family_edit_options: + pass + else: + current_uc_for_edit = use_cases_in_current_family_edit_options[0] + + st.session_state.use_case_selector_edition = current_uc_for_edit + + if current_uc_for_edit: + default_uc_idx_edit = use_cases_in_current_family_edit_options.index(current_uc_for_edit) + + prev_uc_selection_edit = st.session_state.get('use_case_selector_edition') + selected_use_case_ui_edit = st.radio( + "Cas d'usage :", + options=use_cases_in_current_family_edit_options, + index=default_uc_idx_edit, + key='use_case_radio_widget_edit', + help="Sélectionnez un cas d'usage pour générer un prompt ou le paramétrer." + ) + if st.session_state.use_case_selector_edition != selected_use_case_ui_edit: + st.session_state.use_case_selector_edition = selected_use_case_ui_edit + + if prev_uc_selection_edit != selected_use_case_ui_edit: + st.session_state.view_mode = "edit" + st.session_state.active_generated_prompt = "" + st.session_state.variable_type_to_create = None + st.session_state.editing_variable_info = None + st.rerun() + + elif current_selected_family_for_edit_logic: + st.info(f"Aucun cas d'usage dans '{current_selected_family_for_edit_logic}'. Créez-en un.") + st.session_state.use_case_selector_edition = None + + if st.session_state.force_select_family_name: st.session_state.force_select_family_name = None + if st.session_state.force_select_use_case_name: st.session_state.force_select_use_case_name = None + st.markdown("---") + + with st.expander("🗂️ Gérer les familles de prompts par métier", expanded=False): + with st.form("new_family_form_sidebar", clear_on_submit=True): + new_family_name = st.text_input("Nom du nouveau métier:", key="new_fam_name_sidebar") + submitted_new_family = st.form_submit_button("➕ Créer métier") + if submitted_new_family and new_family_name.strip(): + if new_family_name.strip() in st.session_state.editable_prompts: + st.error(f"Le métier '{new_family_name.strip()}' existe déjà.") + else: + st.session_state.editable_prompts[new_family_name.strip()] = {} + save_editable_prompts_to_gist() + st.success(f"Métier '{new_family_name.strip()}' créée.") + st.session_state.force_select_family_name = new_family_name.strip() + st.session_state.use_case_selector_edition = None + st.session_state.view_mode = "edit" + st.rerun() + elif submitted_new_family: + st.error("Le nom du métier ne peut pas être vide.") + + if available_families and current_selected_family_for_edit_logic : + st.markdown("---") + with st.form("rename_family_form_sidebar"): + st.write(f"Renommer le métier : **{current_selected_family_for_edit_logic}**") + renamed_family_name_input = st.text_input("Nouveau nom :", value=current_selected_family_for_edit_logic, key="ren_fam_name_sidebar") + submitted_rename_family = st.form_submit_button("✏️ Renommer") + if submitted_rename_family and renamed_family_name_input.strip(): + renamed_family_name = renamed_family_name_input.strip() + if renamed_family_name == current_selected_family_for_edit_logic: + st.info("Le nouveau nom est identique à l'ancien.") + elif renamed_family_name in st.session_state.editable_prompts: + st.error(f"Un métier nommé '{renamed_family_name}' existe déjà.") + else: + st.session_state.editable_prompts[renamed_family_name] = st.session_state.editable_prompts.pop(current_selected_family_for_edit_logic) + save_editable_prompts_to_gist() + st.success(f"Métier '{current_selected_family_for_edit_logic}' renommé en '{renamed_family_name}'.") + st.session_state.force_select_family_name = renamed_family_name + if st.session_state.library_selected_family_for_display == current_selected_family_for_edit_logic: + st.session_state.library_selected_family_for_display = renamed_family_name + st.session_state.view_mode = "edit" + st.rerun() + elif submitted_rename_family: + st.error("Le nouveau nom du métier ne peut pas être vide.") + + st.markdown("---") + st.write(f"Supprimer le métier : **{current_selected_family_for_edit_logic}**") + if st.session_state.confirming_delete_family_name == current_selected_family_for_edit_logic: + st.warning(f"Supprimer '{current_selected_family_for_edit_logic}' et tous ses cas d'usage ? Action irréversible.") + + _text_confirm_delete = f"Oui, supprimer définitivement '{current_selected_family_for_edit_logic}'" + if st.button(_text_confirm_delete, type="primary", key=f"confirm_del_fam_sb_{current_selected_family_for_edit_logic}", use_container_width=True): + deleted_fam_name = current_selected_family_for_edit_logic + del st.session_state.editable_prompts[current_selected_family_for_edit_logic] + save_editable_prompts_to_gist() + st.success(f"Métier '{deleted_fam_name}' supprimée.") + st.session_state.confirming_delete_family_name = None + st.session_state.family_selector_edition = None + st.session_state.use_case_selector_edition = None + if st.session_state.library_selected_family_for_display == deleted_fam_name: + st.session_state.library_selected_family_for_display = None + st.session_state.view_mode = "edit" + st.rerun() + + if st.button("Non, annuler la suppression", key=f"cancel_del_fam_sb_{current_selected_family_for_edit_logic}", use_container_width=True): + st.session_state.confirming_delete_family_name = None + st.session_state.view_mode = "edit" + st.rerun() + else: + if st.button(f"🗑️ Supprimer le métier Sélectionnée", key=f"del_fam_btn_sb_{current_selected_family_for_edit_logic}"): + st.session_state.confirming_delete_family_name = current_selected_family_for_edit_logic + st.session_state.view_mode = "edit" + st.rerun() + elif not available_families: + st.caption("Créez un métier pour pouvoir le gérer.") + else: + st.caption("Sélectionnez un métier (ci-dessus) pour le gérer.") + + st.markdown("---") + + with st.expander("➕ Créer un Cas d'Usage", expanded=st.session_state.get('show_create_new_use_case_form', False)): + if not available_families: + st.caption("Veuillez d'abord créer une famille de métier pour y ajouter des cas d'usage.") + else: + if st.button("Afficher/Masquer Formulaire de Création de Cas d'Usage", key="toggle_create_uc_form_in_exp"): + st.session_state.show_create_new_use_case_form = not st.session_state.get('show_create_new_use_case_form', False) + st.rerun() + + if st.session_state.get('show_create_new_use_case_form', False): + with st.form("new_use_case_form_in_exp", clear_on_submit=True): + default_create_family_idx_tab = 0 + if current_selected_family_for_edit_logic and current_selected_family_for_edit_logic in available_families: + default_create_family_idx_tab = available_families.index(current_selected_family_for_edit_logic) + + uc_parent_family = st.selectbox( + "Métier parent du nouveau cas d'usage:", + options=available_families, + index=default_create_family_idx_tab, + key="new_uc_parent_fam_in_exp" + ) + uc_name_input = st.text_input("Nom du Nouveau Cas d'Usage:", key="new_uc_name_in_exp") + uc_template_input = st.text_area("Template Initial du Cas d'Usage:", height=100, key="new_uc_template_in_exp", value="Nouveau prompt...") + submitted_new_uc = st.form_submit_button("Créer Cas d'Usage") + + if submitted_new_uc: + parent_family_val = uc_parent_family + uc_name_val = uc_name_input.strip() + uc_template_val = uc_template_input + + if not uc_name_val: + st.error("Le nom du cas d'usage ne peut pas être vide.") + elif uc_name_val in st.session_state.editable_prompts.get(parent_family_val, {}): + st.error(f"Le cas d'usage '{uc_name_val}' existe déjà dans le métier '{parent_family_val}'.") + else: + now_iso_create, now_iso_update = get_default_dates() + st.session_state.editable_prompts[parent_family_val][uc_name_val] = { + "template": uc_template_val or "Nouveau prompt...", + "variables": [], "tags": [], + "usage_count": 0, "created_at": now_iso_create, "updated_at": now_iso_update + } + save_editable_prompts_to_gist() + st.success(f"Cas d'usage '{uc_name_val}' créé avec succès dans '{parent_family_val}'.") + st.session_state.show_create_new_use_case_form = False + st.session_state.force_select_family_name = parent_family_val + st.session_state.force_select_use_case_name = uc_name_val + st.session_state.view_mode = "edit" + st.session_state.active_generated_prompt = "" + st.rerun() # --- Tab: Bibliothèque (Sidebar content) --- with tab_bibliotheque: @@ -719,29 +715,6 @@ with tab_bibliotheque: options=all_tags_list, default=st.session_state.get("library_selected_tags", []) ) - - # Detect filter changes and auto-redirect to global search results - current_search_term = st.session_state.get("library_search_term", "").strip() - current_selected_tags = st.session_state.get("library_selected_tags", []) - - # Check if filters have changed - search_changed = current_search_term != st.session_state.get("previous_search_term", "") - tags_changed = current_selected_tags != st.session_state.get("previous_selected_tags", []) - - # If filters changed and we have active filters, redirect to global search - if (search_changed or tags_changed) and (current_search_term or current_selected_tags): - # Update previous values - st.session_state.previous_search_term = current_search_term - st.session_state.previous_selected_tags = current_selected_tags.copy() - # Redirect to global search page - if st.session_state.view_mode != "select_family_for_library": - st.session_state.view_mode = "select_family_for_library" - st.rerun() - # Update previous values even when filters are cleared - elif not current_search_term and not current_selected_tags: - st.session_state.previous_search_term = current_search_term - st.session_state.previous_selected_tags = current_selected_tags.copy() - st.markdown("---") if not st.session_state.editable_prompts or not any(st.session_state.editable_prompts.values()): @@ -791,22 +764,13 @@ with tab_injection: st.rerun() # --- Main Display Area --- -# Handle force selection after injection -if st.session_state.get('force_select_family_name'): - st.session_state.family_selector_edition = st.session_state.force_select_family_name - st.session_state.force_select_family_name = None - -if st.session_state.get('force_select_use_case_name'): - st.session_state.use_case_selector_edition = st.session_state.force_select_use_case_name - st.session_state.force_select_use_case_name = None - final_selected_family_edition = st.session_state.get('family_selector_edition') final_selected_use_case_edition = st.session_state.get('use_case_selector_edition') library_family_to_display = st.session_state.get('library_selected_family_for_display') # NOUVELLE SECTION POUR LA PAGE D'ACCUEIL if st.session_state.view_mode == "accueil": - st.header("Bienvenue dans votre laboratoire des prompts IA ! 💡") + st.header("Bienvenue à tous dans votre laboratoire des prompts IA ! 💡") st.caption(f"Créé par le pôle Data / IA") st.markdown(""" Vous êtes au bon endroit pour maîtriser l'art de "parler" aux Intelligences Artificielles (IA) et obtenir d'elles exactement ce dont vous avez besoin ! @@ -843,127 +807,32 @@ elif st.session_state.view_mode == "select_family_for_library": if st.button("⬅️ Retour à l'accueil", key="back_to_accueil_from_select_family"): st.session_state.view_mode = "accueil" st.rerun() - - # Check if global filters are active - search_term_lib = st.session_state.get("library_search_term", "").strip().lower() - selected_tags_lib = st.session_state.get("library_selected_tags", []) - has_active_filters = bool(search_term_lib or selected_tags_lib) - - if has_active_filters: - st.header("📚 Résultats de recherche dans tous les métiers") - if search_term_lib and selected_tags_lib: - st.markdown(f"Recherche: **{search_term_lib}** | Tags: **{', '.join(selected_tags_lib)}**") - elif search_term_lib: - st.markdown(f"Recherche: **{search_term_lib}**") - elif selected_tags_lib: - st.markdown(f"Tags: **{', '.join(selected_tags_lib)}**") + st.header("📚 Explorer les prompts par métier") + st.markdown("Cliquez sur le nom d'un métier pour afficher les prompts associés.") + st.markdown("---") + + available_families = list(st.session_state.editable_prompts.keys()) + + if not available_families: + st.info("Aucun métier de prompts n'a été créé pour le moment.") + st.markdown("Vous pouvez en créer via l'onglet **Édition** dans le menu latéral (accessible via l'icône Menu en haut à gauche).") st.markdown("---") + + else: + sorted_families = sorted(available_families) - # Global filtering across all families - global_filtered_results = {} - for family_name, use_cases in st.session_state.editable_prompts.items(): - family_filtered_use_cases = {} - for uc_name, uc_config in use_cases.items(): - # Apply same filtering logic as in library view - match_search = True - if search_term_lib: - match_search = (search_term_lib in uc_name.lower() or - search_term_lib in uc_config.get("template", "").lower() or - any(search_term_lib in var.get("name","").lower() or search_term_lib in var.get("label","").lower() - for var in uc_config.get("variables", [])) or - any(search_term_lib in tag.lower() for tag in uc_config.get("tags", []))) - match_tags = True - if selected_tags_lib: - match_tags = all(tag in uc_config.get("tags", []) for tag in selected_tags_lib) - - if match_search and match_tags: - family_filtered_use_cases[uc_name] = uc_config - - if family_filtered_use_cases: - global_filtered_results[family_name] = family_filtered_use_cases - - if not global_filtered_results: - st.info("Aucun prompt ne correspond à vos critères de recherche dans tous les métiers.") - else: - # Display results grouped by family - for family_name, filtered_use_cases in sorted(global_filtered_results.items()): - st.subheader(f"🔹 {family_name}") - sorted_use_cases = sorted(filtered_use_cases.keys()) - for uc_name in sorted_use_cases: - uc_config = filtered_use_cases[uc_name] - exp_title = f"{uc_name}" - if uc_config.get("usage_count", 0) > 0: - exp_title += f" (Utilisé {uc_config.get('usage_count')} fois)" - - with st.expander(exp_title, expanded=False): - # Display tags in the same format as standard library view - tags_display = uc_config.get("tags", []) - if tags_display: - st.markdown(f"**Tags :** {', '.join([f'`{tag}`' for tag in tags_display])}") - - # Display creation and modification dates - created_at_str = uc_config.get('created_at', get_default_dates()[0]) - updated_at_str = uc_config.get('updated_at', get_default_dates()[1]) - st.caption(f"Créé le : {datetime.fromisoformat(created_at_str).strftime('%d/%m/%Y %H:%M')} | Modifié le : {datetime.fromisoformat(updated_at_str).strftime('%d/%m/%Y %H:%M')}") - - # Three buttons in columns - same format as standard library view - col_btn_lib1, col_btn_lib2, col_btn_lib3 = st.columns(3) - with col_btn_lib1: - if st.button(f"✍️ Utiliser ce prompt", key=f"global_lib_use_{family_name.replace(' ', '_')}_{uc_name.replace(' ', '_')}", use_container_width=True): - st.session_state.view_mode = "generator" - st.session_state.generator_selected_family = family_name - st.session_state.generator_selected_use_case = uc_name - st.session_state.active_generated_prompt = "" - st.rerun() - with col_btn_lib2: - if st.button(f"📋 Dupliquer ce prompt", key=f"global_lib_duplicate_{family_name.replace(' ', '_')}_{uc_name.replace(' ', '_')}", use_container_width=True): - st.session_state.duplicating_use_case_details = { - "family": family_name, - "use_case": uc_name - } - st.rerun() - with col_btn_lib3: - if st.button(f"🗑️ Supprimer ce prompt", key=f"global_lib_delete_{family_name.replace(' ', '_')}_{uc_name.replace(' ', '_')}", use_container_width=True): - st.session_state.confirming_delete_details = { - "family": family_name, - "use_case": uc_name - } - st.rerun() - st.markdown("---") + # Vous pouvez ajuster le nombre de colonnes si vous avez beaucoup de familles + num_cols = 3 + cols = st.columns(num_cols) + for i, family_name in enumerate(sorted_families): + with cols[i % num_cols]: + if st.button(f"{family_name}", key=f"select_family_for_lib_btn_{family_name}", use_container_width=True, help=f"Voir les prompts du métier '{family_name}'"): + st.session_state.library_selected_family_for_display = family_name + st.session_state.view_mode = "library" # Redirige vers la bibliothèque avec la famille sélectionnée + st.rerun() - # Clear filters option - if st.button("🗑️ Effacer les filtres", type="secondary"): - st.session_state.library_search_term = "" - st.session_state.library_selected_tags = [] - st.rerun() - - else: - st.header("📚 Explorer les prompts par métier") - st.markdown("Cliquez sur le nom d'un métier pour afficher les prompts associés.") st.markdown("---") - available_families = list(st.session_state.editable_prompts.keys()) - - if not available_families: - st.info("Aucun métier de prompts n'a été créé pour le moment.") - st.markdown("Vous pouvez en créer via l'onglet **Édition** dans le menu latéral (accessible via l'icône Menu en haut à gauche).") - st.markdown("---") - - else: - sorted_families = sorted(available_families) - - # Vous pouvez ajuster le nombre de colonnes si vous avez beaucoup de familles - num_cols = 3 - cols = st.columns(num_cols) - for i, family_name in enumerate(sorted_families): - with cols[i % num_cols]: - if st.button(f"{family_name}", key=f"select_family_for_lib_btn_{family_name}", use_container_width=True, help=f"Voir les prompts du métier '{family_name}'"): - st.session_state.library_selected_family_for_display = family_name - st.session_state.view_mode = "library" # Redirige vers la bibliothèque avec la famille sélectionnée - st.rerun() - - st.markdown("---") - elif st.session_state.view_mode == "library": if st.button("⬅️ Retour à la sélection des métiers", key="back_to_select_family_from_library"): st.session_state.view_mode = "select_family_for_library" @@ -989,8 +858,7 @@ elif st.session_state.view_mode == "library": match_search = (search_term_lib in uc_name.lower() or search_term_lib in uc_config.get("template", "").lower() or any(search_term_lib in var.get("name","").lower() or search_term_lib in var.get("label","").lower() - for var in uc_config.get("variables", [])) or - any(search_term_lib in tag.lower() for tag in uc_config.get("tags", []))) + for var in uc_config.get("variables", []))) match_tags = True if selected_tags_lib: match_tags = all(tag in uc_config.get("tags", []) for tag in selected_tags_lib) if match_search and match_tags: filtered_use_cases[uc_name] = uc_config @@ -998,98 +866,6 @@ elif st.session_state.view_mode == "library": if not use_cases_in_family_display: st.info(f"Le métier '{library_family_to_display}' ne contient actuellement aucun prompt.") else: st.info("Aucun prompt ne correspond à vos critères de recherche/filtre dans cette métier.") else: - # Gestion de la duplication de cas d'usage - if st.session_state.duplicating_use_case_details and \ - st.session_state.duplicating_use_case_details["family"] == library_family_to_display: - - original_uc_name_for_dup = st.session_state.duplicating_use_case_details["use_case"] - original_family_name_for_dup = st.session_state.duplicating_use_case_details["family"] - - st.markdown(f"### 📋 Dupliquer '{original_uc_name_for_dup}' (depuis: {original_family_name_for_dup})") - - form_key_duplicate = f"form_duplicate_lib_{original_family_name_for_dup.replace(' ','_')}_{original_uc_name_for_dup.replace(' ','_')}" - with st.form(key=form_key_duplicate): - available_families_list = list(st.session_state.editable_prompts.keys()) - try: - default_family_idx = available_families_list.index(original_family_name_for_dup) - except ValueError: - default_family_idx = 0 - - selected_target_family_for_duplicate = st.selectbox( - "Choisir la famille de destination pour la copie :", - options=available_families_list, - index=default_family_idx, - key=f"target_family_dup_select_{form_key_duplicate}" - ) - - suggested_new_name_base = f"{original_uc_name_for_dup} (copie)" - suggested_new_name = suggested_new_name_base - temp_copy_count = 1 - while suggested_new_name in st.session_state.editable_prompts.get(selected_target_family_for_duplicate, {}): - suggested_new_name = f"{suggested_new_name_base} {temp_copy_count}" - temp_copy_count += 1 - - new_duplicated_uc_name_input = st.text_input( - "Nouveau nom pour le cas d'usage dupliqué:", - value=suggested_new_name, - key=f"new_dup_name_input_{form_key_duplicate}" - ) - - submitted_duplicate_form = st.form_submit_button("✅ Confirmer la Duplication", use_container_width=True) - - if submitted_duplicate_form: - new_uc_name_val_from_form = new_duplicated_uc_name_input.strip() - target_family_on_submit = selected_target_family_for_duplicate - - if not new_uc_name_val_from_form: - st.error("Le nom du nouveau cas d'usage ne peut pas être vide.") - elif new_uc_name_val_from_form in st.session_state.editable_prompts.get(target_family_on_submit, {}): - st.error(f"Un cas d'usage nommé '{new_uc_name_val_from_form}' existe déjà dans la famille '{target_family_on_submit}'.") - else: - current_prompt_config = st.session_state.editable_prompts[original_family_name_for_dup][original_uc_name_for_dup] - st.session_state.editable_prompts[target_family_on_submit][new_uc_name_val_from_form] = copy.deepcopy(current_prompt_config) - now_iso_dup_create, now_iso_dup_update = get_default_dates() - st.session_state.editable_prompts[target_family_on_submit][new_uc_name_val_from_form]["created_at"] = now_iso_dup_create - st.session_state.editable_prompts[target_family_on_submit][new_uc_name_val_from_form]["updated_at"] = now_iso_dup_update - st.session_state.editable_prompts[target_family_on_submit][new_uc_name_val_from_form]["usage_count"] = 0 - save_editable_prompts_to_local() - st.success(f"Cas d'usage '{original_uc_name_for_dup}' dupliqué en '{new_uc_name_val_from_form}' dans la famille '{target_family_on_submit}'.") - - st.session_state.duplicating_use_case_details = None - if target_family_on_submit != library_family_to_display: - st.session_state.library_selected_family_for_display = target_family_on_submit - st.rerun() - - cancel_key_duplicate = f"cancel_dup_process_lib_{original_family_name_for_dup.replace(' ','_')}_{original_uc_name_for_dup.replace(' ','_')}" - if st.button("❌ Annuler la Duplication", key=cancel_key_duplicate, use_container_width=True): - st.session_state.duplicating_use_case_details = None - st.rerun() - - st.markdown("---") - - # Gestion de la suppression de cas d'usage - if st.session_state.confirming_delete_details and \ - st.session_state.confirming_delete_details["family"] == library_family_to_display: - - details = st.session_state.confirming_delete_details - st.warning(f"⚠️ Supprimer '{details['use_case']}' de '{details['family']}' ? Action irréversible.") - - c1_del_uc, c2_del_uc, _ = st.columns([1,1,3]) - if c1_del_uc.button(f"Oui, supprimer '{details['use_case']}'", key=f"del_yes_lib_{details['family']}_{details['use_case']}", type="primary"): - deleted_uc_name_for_msg = details['use_case'] - deleted_uc_fam_for_msg = details['family'] - del st.session_state.editable_prompts[details["family"]][details["use_case"]] - save_editable_prompts_to_local() - st.success(f"'{deleted_uc_name_for_msg}' supprimé de '{deleted_uc_fam_for_msg}'.") - st.session_state.confirming_delete_details = None - st.rerun() - - if c2_del_uc.button("Non, annuler", key=f"del_no_lib_{details['family']}_{details['use_case']}"): - st.session_state.confirming_delete_details = None - st.rerun() - - st.markdown("---") - sorted_use_cases_display = sorted(list(filtered_use_cases.keys())) for use_case_name_display in sorted_use_cases_display: prompt_config_display = filtered_use_cases[use_case_name_display] @@ -1102,26 +878,15 @@ elif st.session_state.view_mode == "library": if tags_display: st.markdown(f"**Tags :** {', '.join([f'`{tag}`' for tag in tags_display])}") created_at_str = prompt_config_display.get('created_at', get_default_dates()[0]) updated_at_str = prompt_config_display.get('updated_at', get_default_dates()[1]) - st.caption(f"Créé le : {datetime.fromisoformat(created_at_str).strftime('%d/%m/%Y %H:%M')} | Modifié le : {datetime.fromisoformat(updated_at_str).strftime('%d/%m/%Y %H:%M')}") + st.caption(f"Créé le: {datetime.fromisoformat(created_at_str).strftime('%d/%m/%Y %H:%M')} | Modifié le: {datetime.fromisoformat(updated_at_str).strftime('%d/%m/%Y %H:%M')}") - col_btn_lib1, col_btn_lib2, col_btn_lib3 = st.columns(3) + col_btn_lib1, col_btn_lib2 = st.columns(2) with col_btn_lib1: if st.button(f"✍️ Utiliser ce prompt", key=f"main_lib_use_{library_family_to_display.replace(' ', '_')}_{use_case_name_display.replace(' ', '_')}", use_container_width=True): - st.session_state.view_mode = "generator"; st.session_state.generator_selected_family = library_family_to_display; st.session_state.generator_selected_use_case = use_case_name_display; st.session_state.active_generated_prompt = ""; st.rerun() + st.session_state.view_mode = "edit"; st.session_state.force_select_family_name = library_family_to_display; st.session_state.force_select_use_case_name = use_case_name_display; st.session_state.go_to_config_section = False; st.session_state.active_generated_prompt = ""; st.session_state.variable_type_to_create = None; st.session_state.editing_variable_info = None; st.session_state.confirming_delete_details = None; st.rerun() with col_btn_lib2: - if st.button(f"📋 Dupliquer ce prompt", key=f"main_lib_duplicate_{library_family_to_display.replace(' ', '_')}_{use_case_name_display.replace(' ', '_')}", use_container_width=True): - st.session_state.duplicating_use_case_details = { - "family": library_family_to_display, - "use_case": use_case_name_display - } - st.rerun() - with col_btn_lib3: - if st.button(f"🗑️ Supprimer ce prompt", key=f"main_lib_delete_{library_family_to_display.replace(' ', '_')}_{use_case_name_display.replace(' ', '_')}", use_container_width=True): - st.session_state.confirming_delete_details = { - "family": library_family_to_display, - "use_case": use_case_name_display - } - st.rerun() + if st.button(f"⚙️ Éditer ce prompt", key=f"main_lib_edit_{library_family_to_display.replace(' ', '_')}_{use_case_name_display.replace(' ', '_')}", use_container_width=True): + st.session_state.view_mode = "edit"; st.session_state.force_select_family_name = library_family_to_display; st.session_state.force_select_use_case_name = use_case_name_display; st.session_state.go_to_config_section = True; st.session_state.active_generated_prompt = ""; st.session_state.variable_type_to_create = None; st.session_state.editing_variable_info = None; st.session_state.confirming_delete_details = None; st.rerun() else: st.info("Aucun métier n'est actuellement sélectionnée dans la bibliothèque ou le métier sélectionné n'existe plus.") available_families_check = list(st.session_state.editable_prompts.keys()) @@ -1140,14 +905,20 @@ elif st.session_state.view_mode == "edit": current_prompt_config = st.session_state.editable_prompts[final_selected_family_edition][final_selected_use_case_edition] st.header(f"Cas d'usage: {final_selected_use_case_edition}") created_at_str_edit = current_prompt_config.get('created_at', get_default_dates()[0]); updated_at_str_edit = current_prompt_config.get('updated_at', get_default_dates()[1]) - st.caption(f"Métier : {final_selected_family_edition} | Utilisé {current_prompt_config.get('usage_count', 0)} fois. Créé le : {datetime.fromisoformat(created_at_str_edit).strftime('%d/%m/%Y')}, Modifié le : {datetime.fromisoformat(updated_at_str_edit).strftime('%d/%m/%Y')}") - # Afficher la description si elle existe - description = current_prompt_config.get("description", "").strip() - if description: - st.markdown(f"*{description}*") + st.caption(f"Métier : {final_selected_family_edition} | Utilisé {current_prompt_config.get('usage_count', 0)} fois. Créé: {datetime.fromisoformat(created_at_str_edit).strftime('%d/%m/%Y')}, Modifié: {datetime.fromisoformat(updated_at_str_edit).strftime('%d/%m/%Y')}") + st.markdown(""" +💡 Bon à savoir : Le modèle de base de ce prompt (le "template") ainsi que la liste des variables demandées sont entièrement personnalisables ! Vous pouvez les modifier dans la section "⚙️ Paramétrage du Prompt" qui se trouve plus bas sur cette même page (dans le menu déroulant).
+