"""
JAIM - Gradio Web Interface
A beautiful, interactive UI for querying the Vrikshayurveda knowledge base.
"""
import gradio as gr
from rag_pipeline import JAIMPipeline
from inference.engine import VrikshayurvedaInferenceEngine, DiagnosisResult
# ─── Initialize Pipeline ─────────────────────────────────────────────────────
pipeline = JAIMPipeline()
# ─── Visualizer HTML (Phase 8a) ──────────────────────────────────────────────
VISUALIZER_HTML = """
Facts in memory:
0
Rules fired:
0
Diagnosis:
—
symptom
derived
dosha / disorder
remedy
"""
# ─── Query Handler ────────────────────────────────────────────────────────────
def handle_query(user_query: str, num_results: int) -> tuple[str, str]:
"""Process user query and return formatted response + sources."""
if not user_query.strip():
return "⚠️ Please enter a question about plant disorders.", ""
result = pipeline.query(user_query, top_k=int(num_results))
diagnosis = result.get("diagnosis")
# Build inference block from new chaining engine
inference_block = ""
if diagnosis:
inference_block = (
"## 🧠 Inference Engine Analysis (Forward + Backward Chaining)\n"
f"- **Primary Diagnosis:** {diagnosis.primary_diagnosis}\n"
f"- **Dosha:** {diagnosis.dosha.capitalize()}\n"
)
if diagnosis.forward_trace:
inference_block += "- **Reasoning Chain:**\n"
for step in diagnosis.forward_trace:
inference_block += f" - {step}\n"
remedies = [
f.replace("recommend_", "").replace("_", " ").title()
for f in diagnosis.all_facts
if f.startswith("recommend_")
]
if remedies:
inference_block += f"- **Recommended Treatments:** {', '.join(remedies)}\n"
response_text = inference_block + "\n\n## JAIM Response (RAG + LLM)\n" + result["response"]
# Format sources
sources_text = ""
for src in result["sources"]:
meta = src["metadata"]
score_bar = "█" * int(src["score"] * 20) + "░" * (20 - int(src["score"] * 20))
sources_text += (
f"**{src['id']}** — Score: `{src['score']}` {score_bar}\n"
f"- **Dosha:** {meta.get('cause_given', 'N/A')} | "
f"**Disorder:** {meta.get('disorder', 'N/A')}\n"
f"- **Symptoms:** {meta.get('symptoms', 'N/A')[:120]}...\n\n"
)
return response_text, sources_text
# ─── Symptom Chain Explorer functions (Phase 8b) ─────────────────────────────
def _wrap_in_iframe(html_content: str) -> str:
"""Wrap HTML in an iframe srcdoc so JavaScript executes (Gradio strips scripts)."""
escaped = html_content.replace("&", "&").replace('"', """)
return f''
def _render_viz_with_facts(facts: list[str], mode: str = "run") -> str:
"""
Injects a JS bootstrap call into the visualizer HTML
so the canvas fires immediately on load.
"""
if not facts:
return _wrap_in_iframe(VISUALIZER_HTML)
facts_js = str(facts).replace("'", '"')
call = (
f"runChainWithFacts({facts_js});"
if mode == "run"
else f"stepChainWithFacts({facts_js});"
)
modified_html = VISUALIZER_HTML.replace(
"initCanvas(0);\ndraw();",
f"initCanvas(0);\ndraw();\nsetTimeout(function(){{{call}}},300);",
)
return _wrap_in_iframe(modified_html)
def explorer_run(selected_symptoms):
if not selected_symptoms:
return (
_wrap_in_iframe(VISUALIZER_HTML),
"No symptoms selected. Check at least one symptom and try again."
)
return (
_render_viz_with_facts(selected_symptoms, mode="run"),
f"Auto-running chain for: {', '.join(s.replace('_',' ') for s in selected_symptoms)}"
)
def explorer_step(selected_symptoms):
if not selected_symptoms:
return (
_wrap_in_iframe(VISUALIZER_HTML),
"No symptoms selected. Check at least one symptom and try again."
)
return (
_render_viz_with_facts(selected_symptoms, mode="step"),
"Step mode — click '→ Step one rule at a time' repeatedly to advance."
)
def explorer_reset():
return (
_wrap_in_iframe(VISUALIZER_HTML),
"Reset. Select symptoms above, then choose auto-run or step through."
)
# ─── Example Queries ──────────────────────────────────────────────────────────
EXAMPLES = [
["My tree trunk is bent and the fruits are hard and not juicy"],
["The fruits are bland and overripe with oozing"],
["Leaves are withering early and flowers are decaying"],
["My tree has been wounded by cutting"],
["There are ants on my plants and they smell bad"],
["I overwatered my plants and they look sick"],
["My seeds are not growing into productive trees"],
]
# ─── Gradio UI ────────────────────────────────────────────────────────────────
CUSTOM_CSS = """
.gradio-container {
max-width: 960px !important;
margin: auto !important;
}
.main-title {
text-align: center;
background: linear-gradient(135deg, #2d5016 0%, #4a7c23 50%, #6ba33e 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 2.8em !important;
font-weight: 800 !important;
margin-bottom: 0 !important;
}
.subtitle {
text-align: center;
color: #6b7280;
font-size: 1.1em;
margin-top: 0;
margin-bottom: 1.5em;
}
footer { display: none !important; }
"""
with gr.Blocks(
title="RAG for Vrikshayurveda",
) as app:
# Header
gr.HTML("""
🌿 RAG for Vrikshayurveda
Retrieval-Augmented Generation • Surapala's Science of Plant Life (वृक्षायुर्वेद)
""")
with gr.Tab("🩺 Diagnose"):
with gr.Row():
with gr.Column(scale=3):
query_input = gr.Textbox(
label="🔍 Describe your plant's symptoms",
placeholder="e.g., My tree trunk is bent, fruits are hard and not juicy, leaves are yellowing...",
lines=3,
max_lines=5,
)
with gr.Column(scale=1):
num_results = gr.Slider(
minimum=1, maximum=7, value=3, step=1,
label="📊 Results to retrieve",
)
query_btn = gr.Button(
"🌱 Diagnose & Treat",
variant="primary",
size="lg",
)
# Example queries
gr.Examples(
examples=EXAMPLES,
inputs=query_input,
label="💡 Try these examples",
)
# Response section
with gr.Accordion("🩺 Diagnosis & Treatment", open=True):
response_output = gr.Markdown(
value="*Enter your plant's symptoms above and click **Diagnose & Treat** to get Ayurvedic guidance.*"
)
with gr.Accordion("📚 Retrieved Sources (from Pinecone)", open=False):
sources_output = gr.Markdown(
value="*Sources will appear here after a query.*"
)
# Event handlers
query_btn.click(
fn=handle_query,
inputs=[query_input, num_results],
outputs=[response_output, sources_output],
)
query_input.submit(
fn=handle_query,
inputs=[query_input, num_results],
outputs=[response_output, sources_output],
)
# ─── Symptom Chain Explorer tab (Phase 8c) ────────────────────────────────
with gr.Tab("🔗 Symptom Chain Explorer"):
gr.Markdown("### Select symptoms and watch the inference engine reason step by step.")
symptom_selector = gr.CheckboxGroup(
choices=[
# Vata symptoms
("Trunk bent / crooked", "trunk_bent"),
("Knots on trunk or leaves", "knots_on_trunk"),
("Hard / dry fruits", "hard_fruits"),
("Slow defoliation", "slow_defoliation"),
("Flower / fruit loss", "flower_fruit_loss"),
("General yellowing", "general_yellowing"),
# Kapha symptoms
("Delayed fruiting", "delayed_fruiting"),
("Bland overripe fruits", "bland_overripe_fruits"),
("Oozing without injury", "oozing_without_injury"),
# Pitta symptoms
("Early leaf withering", "early_leaf_withering"),
("Early fruit / flower decay", "early_fruit_flower_decay"),
# External / shared symptoms
("Vata-like symptoms (external)", "vata_like_symptoms"),
("Tree drying up", "tree_drying"),
("Lightning strike", "lightning_strike"),
("Tree uprooting", "tree_uprooting"),
("Branch breaking", "branch_breaking"),
("Tree twisting", "tree_twisting"),
("Mechanical wounds (axe etc.)", "tree_wounds"),
("Tree unproductive", "tree_unproductive"),
("Foul smell", "foul_smell"),
("Fragrance loss", "fragrance_loss"),
("Reduced leaf size", "reduced_leaf_size"),
("Stunted seedlings", "stunted_seedlings"),
("Tree indigestion / waterlogged","tree_indigestion"),
("Tree destruction from water", "tree_destruction"),
# Seasonal context
("Winter / spring season", "winter_spring_season"),
("End of summer season", "end_of_summer"),
],
label="Observed symptoms",
)
with gr.Row():
run_btn = gr.Button("▶ Auto-run chain", variant="primary")
step_btn = gr.Button("→ Step one rule at a time")
reset_btn = gr.Button("↺ Reset")
status_box = gr.Textbox(
value="Select symptoms above, then choose auto-run or step through.",
interactive=False,
max_lines=1,
label="",
show_label=False,
)
viz_html = gr.HTML(value=_wrap_in_iframe(VISUALIZER_HTML), label="Chain visualizer")
run_btn.click(
fn=explorer_run,
inputs=[symptom_selector],
outputs=[viz_html, status_box],
)
step_btn.click(
fn=explorer_step,
inputs=[symptom_selector],
outputs=[viz_html, status_box],
)
reset_btn.click(
fn=explorer_reset,
inputs=[],
outputs=[viz_html, status_box],
)
# Footer
gr.HTML("""
This app uses RAG (Retrieval-Augmented Generation) combining
Pinecone vector search with AI
Embedding Model: BAAI/bge-large-en-v1.5 •
LLM: Llama 3.3 70B •
Inference: Forward + Backward Chaining •
Knowledge Base: Vrikshayurveda by Surapala
""")
# ─── Launch ───────────────────────────────────────────────────────────────────
if __name__ == "__main__":
app.launch(
server_name="127.0.0.1",
server_port=7860,
share=False,
show_error=True,
css=CUSTOM_CSS,
theme=gr.themes.Soft(
primary_hue="green",
secondary_hue="emerald",
neutral_hue="slate",
font=gr.themes.GoogleFont("Inter"),
)
)