import gradio as gr import pandas as pd import plotly.express as px import plotly.graph_objects as go from datasets import load_dataset # --------------------------------------------------------------------------- # Data loading # --------------------------------------------------------------------------- COLUMNS = [ "id", "type", "category", "name", "content", "details", "pros", "cons", "tools", "source_url", "language", ] def load_data(): """Load both FR and EN datasets and return as DataFrames.""" try: ds_fr = load_dataset("AYI-NEDJIMI/rag-langchain-fr", split="train") df_fr = ds_fr.to_pandas() except Exception: df_fr = pd.DataFrame(columns=COLUMNS) try: ds_en = load_dataset("AYI-NEDJIMI/rag-langchain-en", split="train") df_en = ds_en.to_pandas() except Exception: df_en = pd.DataFrame(columns=COLUMNS) return df_fr, df_en DF_FR, DF_EN = load_data() CATEGORIES = [ "document_loader", "text_splitter", "embedding_model", "vector_store", "retriever", "chain", ] LABELS = { "FR": { "title": "RAG & LangChain Explorer", "explorer": "Explorateur", "details": "Détails", "qna": "Q&R", "statistics": "Statistiques", "search": "Rechercher…", "category": "Catégorie", "all": "Toutes", "select_item": "Sélectionner un élément", "no_results": "Aucun résultat.", "by_category": "Répartition par catégorie", "by_type": "Répartition par type", "items_per_cat": "Nombre d'éléments par catégorie", "ask": "Posez votre question sur RAG / LangChain", "answer": "Réponse", "qa_placeholder": "Ex : Qu'est-ce qu'un Text Splitter ?", "no_match": "Aucune correspondance trouvée. Essayez un autre terme.", "name_col": "Nom", "type_col": "Type", "category_col": "Catégorie", "content_lbl": "Contenu", "details_lbl": "Détails", "pros_lbl": "Avantages", "cons_lbl": "Inconvénients", "tools_lbl": "Outils", "source_lbl": "Source", }, "EN": { "title": "RAG & LangChain Explorer", "explorer": "Explorer", "details": "Details", "qna": "Q&A", "statistics": "Statistics", "search": "Search…", "category": "Category", "all": "All", "select_item": "Select an item", "no_results": "No results.", "by_category": "Distribution by category", "by_type": "Distribution by type", "items_per_cat": "Number of items per category", "ask": "Ask a question about RAG / LangChain", "answer": "Answer", "qa_placeholder": "E.g. What is a Text Splitter?", "no_match": "No match found. Try another term.", "name_col": "Name", "type_col": "Type", "category_col": "Category", "content_lbl": "Content", "details_lbl": "Details", "pros_lbl": "Pros", "cons_lbl": "Cons", "tools_lbl": "Tools", "source_lbl": "Source", }, } # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def get_df(lang: str) -> pd.DataFrame: return DF_FR.copy() if lang == "FR" else DF_EN.copy() def filter_table(search: str, category: str, lang: str): df = get_df(lang) if df.empty: return pd.DataFrame() if category and category not in ("Toutes", "All"): df = df[df["category"] == category] if search: mask = df.apply( lambda r: search.lower() in " ".join(r.astype(str)).lower(), axis=1 ) df = df[mask] display_cols = ["name", "type", "category"] available = [c for c in display_cols if c in df.columns] return df[available].reset_index(drop=True) def get_item_names(lang: str): df = get_df(lang) if df.empty: return [] return sorted(df["name"].dropna().unique().tolist()) def get_item_details(name: str, lang: str): L = LABELS[lang] df = get_df(lang) if df.empty or not name: return L["no_results"] row = df[df["name"] == name] if row.empty: return L["no_results"] r = row.iloc[0] parts = [] parts.append(f"## {r.get('name', '')}") parts.append(f"**{L['category_col']}**: {r.get('category', '')}") parts.append(f"**{L['type_col']}**: {r.get('type', '')}") if pd.notna(r.get("content")): parts.append(f"\n### {L['content_lbl']}\n{r['content']}") if pd.notna(r.get("details")): parts.append(f"\n### {L['details_lbl']}\n{r['details']}") if pd.notna(r.get("pros")): parts.append(f"\n### {L['pros_lbl']}\n{r['pros']}") if pd.notna(r.get("cons")): parts.append(f"\n### {L['cons_lbl']}\n{r['cons']}") if pd.notna(r.get("tools")): parts.append(f"\n### {L['tools_lbl']}\n{r['tools']}") if pd.notna(r.get("source_url")): parts.append(f"\n### {L['source_lbl']}\n[Link]({r['source_url']})") return "\n".join(parts) def answer_question(question: str, lang: str): L = LABELS[lang] if not question or not question.strip(): return "" df = get_df(lang) if df.empty: return L["no_match"] q = question.lower() # Search across content-heavy columns search_cols = ["name", "content", "details", "category", "type"] scores = [] for _, row in df.iterrows(): text = " ".join(str(row.get(c, "")) for c in search_cols).lower() score = sum(1 for w in q.split() if w in text) scores.append(score) df = df.copy() df["_score"] = scores best = df.sort_values("_score", ascending=False).head(3) best = best[best["_score"] > 0] if best.empty: return L["no_match"] parts = [] for _, r in best.iterrows(): parts.append(f"### {r.get('name', '')}") parts.append(f"**{L['category_col']}**: {r.get('category', '')}") if pd.notna(r.get("content")): parts.append(f"{r['content'][:500]}") parts.append("---") return "\n".join(parts) def make_category_chart(lang: str): L = LABELS[lang] df = get_df(lang) if df.empty: return go.Figure() counts = df["category"].value_counts().reset_index() counts.columns = ["category", "count"] fig = px.bar( counts, x="category", y="count", title=L["items_per_cat"], color="category", color_discrete_sequence=px.colors.qualitative.Set2, ) fig.update_layout(showlegend=False, xaxis_title="", yaxis_title="") return fig def make_type_chart(lang: str): L = LABELS[lang] df = get_df(lang) if df.empty: return go.Figure() counts = df["type"].value_counts().reset_index() counts.columns = ["type", "count"] fig = px.pie( counts, names="type", values="count", title=L["by_type"], color_discrete_sequence=px.colors.qualitative.Pastel, ) return fig def make_category_pie(lang: str): L = LABELS[lang] df = get_df(lang) if df.empty: return go.Figure() counts = df["category"].value_counts().reset_index() counts.columns = ["category", "count"] fig = px.pie( counts, names="category", values="count", title=L["by_category"], color_discrete_sequence=px.colors.qualitative.Set2, ) return fig # --------------------------------------------------------------------------- # UI # --------------------------------------------------------------------------- FOOTER_HTML = """
Built by AYI-NEDJIMI Consultants
""" with gr.Blocks( title="RAG & LangChain Explorer", theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple"), ) as demo: gr.Markdown("# RAG & LangChain Explorer") lang_toggle = gr.Radio( choices=["FR", "EN"], value="FR", label="Language / Langue", interactive=True, ) with gr.Tabs(): # ---- Explorer tab ---- with gr.Tab("Explorer / Explorateur"): with gr.Row(): search_box = gr.Textbox( label="Search…", placeholder="Search…", scale=3, ) cat_filter = gr.Dropdown( choices=["Toutes"] + CATEGORIES, value="Toutes", label="Category", scale=1, ) table_output = gr.Dataframe( value=filter_table("", "Toutes", "FR"), interactive=False, ) search_box.change( filter_table, [search_box, cat_filter, lang_toggle], table_output, ) cat_filter.change( filter_table, [search_box, cat_filter, lang_toggle], table_output, ) lang_toggle.change( filter_table, [search_box, cat_filter, lang_toggle], table_output, ) # ---- Details tab ---- with gr.Tab("Details / Détails"): item_dropdown = gr.Dropdown( choices=get_item_names("FR"), label="Select an item / Sélectionner un élément", interactive=True, ) detail_output = gr.Markdown() item_dropdown.change( get_item_details, [item_dropdown, lang_toggle], detail_output, ) def refresh_names(lang): return gr.update(choices=get_item_names(lang), value=None) lang_toggle.change(refresh_names, lang_toggle, item_dropdown) # ---- Q&A tab ---- with gr.Tab("Q&A / Q&R"): qa_input = gr.Textbox( label="Ask a question / Posez votre question", placeholder="E.g. What is a Text Splitter?", lines=2, ) qa_btn = gr.Button("Search / Rechercher") qa_output = gr.Markdown() qa_btn.click(answer_question, [qa_input, lang_toggle], qa_output) qa_input.submit(answer_question, [qa_input, lang_toggle], qa_output) # ---- Statistics tab ---- with gr.Tab("Statistics / Statistiques"): with gr.Row(): cat_bar = gr.Plot(value=make_category_chart("FR")) cat_pie = gr.Plot(value=make_category_pie("FR")) with gr.Row(): type_pie = gr.Plot(value=make_type_chart("FR")) def refresh_stats(lang): return ( make_category_chart(lang), make_category_pie(lang), make_type_chart(lang), ) lang_toggle.change(refresh_stats, lang_toggle, [cat_bar, cat_pie, type_pie]) gr.HTML(FOOTER_HTML) if __name__ == "__main__": demo.launch()