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 = """