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 # --------------------------------------------------------------------------- DATASETS = { "EN": "AYI-NEDJIMI/ai-code-multimodal-en", "FR": "AYI-NEDJIMI/ai-code-multimodal-fr", } _cache: dict[str, pd.DataFrame] = {} def get_df(lang: str = "EN") -> pd.DataFrame: if lang not in _cache: ds = load_dataset(DATASETS[lang], split="train") _cache[lang] = ds.to_pandas() return _cache[lang] # Pre-load both for _l in DATASETS: get_df(_l) COLS_TABLE = ["name", "vendor", "category", "subcategory", "pricing"] DETAIL_FIELDS = [ "name", "vendor", "category", "subcategory", "description", "features", "strengths", "weaknesses", "ide_support", "privacy_model", "supported_languages", "pricing", "source_url", ] LABELS = { "EN": { "title": "AI Code & Multimodal Explorer", "tab_explorer": "Tools Explorer", "tab_details": "Details", "tab_compare": "Comparison", "tab_stats": "Statistics", "search": "Search by name or vendor...", "category": "Category", "all_categories": "All categories", "select_tool": "Select a tool", "select_tool_a": "Tool A", "select_tool_b": "Tool B", "compare_btn": "Compare", "no_tool": "No tool selected.", "chart_cat": "Tools by Category", "chart_vendor": "Top 15 Vendors by Number of Tools", "chart_pricing": "Pricing Distribution", "chart_cat_vendor": "Category × Vendor Heatmap", }, "FR": { "title": "AI Code & Multimodal Explorer", "tab_explorer": "Explorateur", "tab_details": "Détails", "tab_compare": "Comparaison", "tab_stats": "Statistiques", "search": "Rechercher par nom ou éditeur...", "category": "Catégorie", "all_categories": "Toutes les catégories", "select_tool": "Sélectionner un outil", "select_tool_a": "Outil A", "select_tool_b": "Outil B", "compare_btn": "Comparer", "no_tool": "Aucun outil sélectionné.", "chart_cat": "Outils par catégorie", "chart_vendor": "Top 15 éditeurs par nombre d'outils", "chart_pricing": "Répartition des tarifs", "chart_cat_vendor": "Carte thermique Catégorie × Éditeur", }, } # --------------------------------------------------------------------------- # Helper functions # --------------------------------------------------------------------------- def filter_table(search: str, category: str, lang: str) -> pd.DataFrame: df = get_df(lang) if category and category != LABELS[lang]["all_categories"]: df = df[df["category"] == category] if search: mask = ( df["name"].str.contains(search, case=False, na=False) | df["vendor"].str.contains(search, case=False, na=False) ) df = df[mask] return df[COLS_TABLE].reset_index(drop=True) def get_tool_names(lang: str) -> list[str]: return sorted(get_df(lang)["name"].dropna().unique().tolist()) def get_categories(lang: str) -> list[str]: cats = sorted(get_df(lang)["category"].dropna().unique().tolist()) return [LABELS[lang]["all_categories"]] + cats def tool_detail_md(name: str | None, lang: str) -> str: if not name: return LABELS[lang]["no_tool"] df = get_df(lang) rows = df[df["name"] == name] if rows.empty: return LABELS[lang]["no_tool"] r = rows.iloc[0] lines = [] for f in DETAIL_FIELDS: val = r.get(f, "") if pd.isna(val) or val == "": val = "—" label = f.replace("_", " ").title() if f == "source_url" and val != "—": lines.append(f"**{label}:** [{val}]({val})") else: lines.append(f"**{label}:** {val}") return "\n\n".join(lines) def compare_tools(name_a: str | None, name_b: str | None, lang: str) -> str: if not name_a or not name_b: return LABELS[lang]["no_tool"] md_a = tool_detail_md(name_a, lang) md_b = tool_detail_md(name_b, lang) sep = "\n\n---\n\n" return f"## {name_a}\n\n{md_a}{sep}## {name_b}\n\n{md_b}" # --------------------------------------------------------------------------- # Charts # --------------------------------------------------------------------------- def chart_category(lang: str): df = get_df(lang) counts = df["category"].value_counts().reset_index() counts.columns = ["category", "count"] fig = px.bar( counts, x="category", y="count", color="category", title=LABELS[lang]["chart_cat"], color_discrete_sequence=px.colors.qualitative.Set2, ) fig.update_layout(showlegend=False, template="plotly_white") return fig def chart_vendor(lang: str): df = get_df(lang) counts = df["vendor"].value_counts().head(15).reset_index() counts.columns = ["vendor", "count"] fig = px.bar( counts, x="count", y="vendor", orientation="h", title=LABELS[lang]["chart_vendor"], color="count", color_continuous_scale="Viridis", ) fig.update_layout(yaxis=dict(autorange="reversed"), template="plotly_white") return fig def chart_pricing(lang: str): df = get_df(lang) counts = df["pricing"].value_counts().reset_index() counts.columns = ["pricing", "count"] fig = px.pie( counts, names="pricing", values="count", title=LABELS[lang]["chart_pricing"], color_discrete_sequence=px.colors.qualitative.Pastel, ) fig.update_layout(template="plotly_white") return fig def chart_heatmap(lang: str): df = get_df(lang) ct = pd.crosstab(df["category"], df["vendor"]) top_vendors = df["vendor"].value_counts().head(12).index.tolist() ct = ct[[v for v in top_vendors if v in ct.columns]] fig = go.Figure(data=go.Heatmap( z=ct.values, x=ct.columns.tolist(), y=ct.index.tolist(), colorscale="YlOrRd", )) fig.update_layout( title=LABELS[lang]["chart_cat_vendor"], template="plotly_white", height=400, ) return fig # --------------------------------------------------------------------------- # Footer # --------------------------------------------------------------------------- FOOTER_HTML = """

Built by AYI-NEDJIMI Consultants

🌐 Website | LinkedIn | GitHub | X / Twitter

""" # --------------------------------------------------------------------------- # Gradio UI # --------------------------------------------------------------------------- theme = gr.themes.Soft( primary_hue="indigo", secondary_hue="purple", font=gr.themes.GoogleFont("Inter"), ) with gr.Blocks(theme=theme, title="AI Code & Multimodal Explorer") as demo: lang_state = gr.State("EN") gr.Markdown("# AI Code & Multimodal Explorer") with gr.Row(): lang_toggle = gr.Radio( choices=["EN", "FR"], value="EN", label="Language / Langue", interactive=True, ) # ---- Tab: Explorer ---- with gr.Tabs() as tabs: with gr.TabItem("Tools Explorer", id="explorer"): with gr.Row(): search_box = gr.Textbox( label="Search", placeholder="Search by name or vendor...", scale=3, ) cat_filter = gr.Dropdown( choices=get_categories("EN"), value=LABELS["EN"]["all_categories"], label="Category", scale=1, ) explorer_table = gr.Dataframe( value=filter_table("", LABELS["EN"]["all_categories"], "EN"), headers=COLS_TABLE, interactive=False, wrap=True, ) # ---- Tab: Details ---- with gr.TabItem("Details", id="details"): tool_select = gr.Dropdown( choices=get_tool_names("EN"), label="Select a tool", interactive=True, ) detail_output = gr.Markdown(LABELS["EN"]["no_tool"]) # ---- Tab: Comparison ---- with gr.TabItem("Comparison", id="compare"): with gr.Row(): tool_a = gr.Dropdown( choices=get_tool_names("EN"), label="Tool A", scale=1, ) tool_b = gr.Dropdown( choices=get_tool_names("EN"), label="Tool B", scale=1, ) compare_btn = gr.Button("Compare", variant="primary") compare_output = gr.Markdown(LABELS["EN"]["no_tool"]) # ---- Tab: Statistics ---- with gr.TabItem("Statistics", id="stats"): with gr.Row(): plot_cat = gr.Plot(value=chart_category("EN")) plot_vendor = gr.Plot(value=chart_vendor("EN")) with gr.Row(): plot_pricing = gr.Plot(value=chart_pricing("EN")) plot_heatmap = gr.Plot(value=chart_heatmap("EN")) gr.HTML(FOOTER_HTML) # ---- Callbacks ---- def on_lang_change(lang): cats = get_categories(lang) names = get_tool_names(lang) default_cat = cats[0] table = filter_table("", default_cat, lang) return ( lang, # lang_state gr.update(choices=cats, value=default_cat), # cat_filter table, # explorer_table gr.update(choices=names, value=None), # tool_select LABELS[lang]["no_tool"], # detail_output gr.update(choices=names, value=None), # tool_a gr.update(choices=names, value=None), # tool_b LABELS[lang]["no_tool"], # compare_output chart_category(lang), chart_vendor(lang), chart_pricing(lang), chart_heatmap(lang), ) lang_toggle.change( on_lang_change, inputs=[lang_toggle], outputs=[ lang_state, cat_filter, explorer_table, tool_select, detail_output, tool_a, tool_b, compare_output, plot_cat, plot_vendor, plot_pricing, plot_heatmap, ], ) def on_filter(search, category, lang): return filter_table(search, category, lang) search_box.change(on_filter, [search_box, cat_filter, lang_state], explorer_table) cat_filter.change(on_filter, [search_box, cat_filter, lang_state], explorer_table) tool_select.change( lambda name, lang: tool_detail_md(name, lang), [tool_select, lang_state], detail_output, ) compare_btn.click( lambda a, b, lang: compare_tools(a, b, lang), [tool_a, tool_b, lang_state], compare_output, ) demo.launch()