Spaces:
Sleeping
Sleeping
Commit
·
3206264
1
Parent(s):
862b712
two modes
Browse files- app.py +160 -128
- miscellaneous/instructions.md +258 -0
- tools.py +69 -0
app.py
CHANGED
|
@@ -1,16 +1,11 @@
|
|
| 1 |
# ==============================================================================
|
| 2 |
-
# Aura Mind Glow -
|
| 3 |
# ==============================================================================
|
| 4 |
"""
|
| 5 |
-
This script launches
|
| 6 |
-
It
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
1. A fine-tuned Unsloth/Gemma-3N vision model analyzes an uploaded image to
|
| 10 |
-
diagnose the plant's condition (e.g., "Phosphorus Deficiency").
|
| 11 |
-
2. A local RAG (Retrieval-Augmented Generation) pipeline uses the diagnosis
|
| 12 |
-
text to search an in-memory knowledge base for relevant remedies.
|
| 13 |
-
3. The diagnosis and the retrieved remedies are displayed to the user.
|
| 14 |
"""
|
| 15 |
|
| 16 |
# --- Step 0: Essential Imports ---
|
|
@@ -19,6 +14,8 @@ import torch
|
|
| 19 |
from PIL import Image
|
| 20 |
import os
|
| 21 |
import warnings
|
|
|
|
|
|
|
| 22 |
|
| 23 |
# Suppress potential warnings for a cleaner console
|
| 24 |
warnings.filterwarnings("ignore")
|
|
@@ -28,23 +25,33 @@ os.environ["TORCH_COMPILE_DISABLE"] = "1" # Ensure torch compile is off
|
|
| 28 |
from unsloth import FastVisionModel
|
| 29 |
from transformers import AutoProcessor
|
| 30 |
|
| 31 |
-
# LangChain and RAG components
|
| 32 |
from langchain_community.vectorstores import FAISS
|
| 33 |
from langchain_community.document_loaders import TextLoader
|
| 34 |
from langchain_huggingface import HuggingFaceEmbeddings
|
| 35 |
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
print("✅ All libraries imported successfully.")
|
| 38 |
|
| 39 |
-
# --- Step 1: Global Setup - Vision Model
|
| 40 |
# This expensive setup runs only ONCE when the application starts.
|
| 41 |
|
| 42 |
-
print("Performing initial setup for
|
| 43 |
VISION_MODEL = None
|
| 44 |
PROCESSOR = None
|
|
|
|
| 45 |
ADAPTER_PATH = "surfiniaburger/maize-health-diagnosis-adapter"
|
| 46 |
|
| 47 |
try:
|
|
|
|
| 48 |
VISION_MODEL, PROCESSOR = FastVisionModel.from_pretrained(
|
| 49 |
model_name="unsloth/gemma-3n-E2B-it-unsloth-bnb-4bit",
|
| 50 |
max_seq_length=2048,
|
|
@@ -55,18 +62,7 @@ try:
|
|
| 55 |
VISION_MODEL.load_adapter(ADAPTER_PATH)
|
| 56 |
print(f"✅ Vision model and adapter '{ADAPTER_PATH}' loaded successfully!")
|
| 57 |
|
| 58 |
-
|
| 59 |
-
print(f"❌ CRITICAL ERROR during vision model loading: {e}")
|
| 60 |
-
# We will allow the app to launch so the user can see the error in the UI
|
| 61 |
-
pass
|
| 62 |
-
|
| 63 |
-
# --- Step 2: Global Setup - RAG Knowledge Base (The 'Remedy Retriever') ---
|
| 64 |
-
# We create an in-memory vector store with remedy information. This also runs only once.
|
| 65 |
-
|
| 66 |
-
print("Building the RAG knowledge base for remedies...")
|
| 67 |
-
RETRIEVER = None
|
| 68 |
-
try:
|
| 69 |
-
# This text is our local, offline knowledge base.
|
| 70 |
remedy_knowledge_text = """
|
| 71 |
# Maize Health Guide
|
| 72 |
## Phosphorus Deficiency
|
|
@@ -89,132 +85,168 @@ try:
|
|
| 89 |
**Symptoms:** Vigorous growth with lush, uniformly dark green leaves. No visible spots, lesions, or discoloration.
|
| 90 |
**Local Remedy:** No remedy needed. Maintain good agricultural practices, including proper watering, fertilization, and pest management to keep the plant healthy.
|
| 91 |
"""
|
| 92 |
-
|
| 93 |
-
# Use LangChain to load and process the text
|
| 94 |
with open("knowledge.txt", "w") as f:
|
| 95 |
f.write(remedy_knowledge_text)
|
| 96 |
-
|
| 97 |
loader = TextLoader("knowledge.txt")
|
| 98 |
documents = loader.load()
|
| 99 |
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
|
| 100 |
docs = text_splitter.split_documents(documents)
|
| 101 |
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
| 102 |
db = FAISS.from_documents(docs, embeddings)
|
| 103 |
-
|
| 104 |
-
# Create the retriever object that will find the relevant remedies
|
| 105 |
-
RETRIEVER = db.as_retriever(search_kwargs={"k": 1}) # Retrieve the single most relevant remedy
|
| 106 |
print("✅ RAG knowledge base and retriever created successfully!")
|
| 107 |
|
| 108 |
except Exception as e:
|
| 109 |
-
print(f"❌ CRITICAL ERROR during
|
| 110 |
-
# Allow app to launch to show error
|
| 111 |
pass
|
| 112 |
|
| 113 |
-
# --- Step
|
| 114 |
-
|
| 115 |
-
def diagnose_plant_from_image(uploaded_image: Image.Image) -> str:
|
| 116 |
-
"""
|
| 117 |
-
Takes a PIL Image and runs the fine-tuned vision model to get a diagnosis.
|
| 118 |
-
"""
|
| 119 |
-
if VISION_MODEL is None or PROCESSOR is None or uploaded_image is None:
|
| 120 |
-
raise gr.Error("Vision model is not loaded. Cannot perform diagnosis. Check logs.")
|
| 121 |
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
# This logic specifically finds the text after "model\n"
|
| 136 |
-
answer_start_index = response.rfind("model\n")
|
| 137 |
-
if answer_start_index != -1:
|
| 138 |
-
return response[answer_start_index + len("model\n"):
|
| 139 |
-
].strip()
|
| 140 |
-
|
| 141 |
-
# Fallback for a different output format
|
| 142 |
-
answer_start_index = response.rfind("assistant\n")
|
| 143 |
-
if answer_start_index != -1:
|
| 144 |
-
return response[answer_start_index + len("assistant\n"):
|
| 145 |
-
].strip()
|
| 146 |
|
| 147 |
-
return "Could not parse diagnosis from model output."
|
| 148 |
|
|
|
|
| 149 |
|
| 150 |
-
def
|
| 151 |
-
"""
|
| 152 |
-
|
| 153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
"""
|
| 155 |
-
if uploaded_image is None:
|
| 156 |
-
return "Please upload an image of a maize plant first."
|
| 157 |
-
if RETRIEVER is None:
|
| 158 |
-
raise gr.Error("Knowledge base is not loaded. Cannot find remedy. Check logs.")
|
| 159 |
-
|
| 160 |
-
try:
|
| 161 |
-
# Step 1: Get the initial diagnosis from the vision model.
|
| 162 |
-
print("Workflow Step 1: Running diagnosis...")
|
| 163 |
-
diagnosis = diagnose_plant_from_image(uploaded_image)
|
| 164 |
-
print(f"Diagnosis received: {diagnosis}")
|
| 165 |
-
|
| 166 |
-
if "Could not parse" in diagnosis:
|
| 167 |
-
return f"Sorry, I couldn't identify the condition from the image. Raw output: {diagnosis}"
|
| 168 |
-
|
| 169 |
-
# Step 2: Retrieve remedy from the local knowledge base.
|
| 170 |
-
print(f"Workflow Step 2: Retrieving remedy for '{diagnosis}' from local DB...")
|
| 171 |
-
retrieved_docs = RETRIEVER.invoke(diagnosis)
|
| 172 |
-
|
| 173 |
-
# Step 3: Format the output for the user.
|
| 174 |
-
remedy_text = retrieved_docs[0].page_content if retrieved_docs else "No specific remedy found in the knowledge base for this condition."
|
| 175 |
-
|
| 176 |
-
final_response = f"""
|
| 177 |
-
## Diagnosis Report
|
| 178 |
-
|
| 179 |
-
**Condition Identified:**
|
| 180 |
-
### {diagnosis}
|
| 181 |
-
|
| 182 |
-
---
|
| 183 |
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
"""
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
# --- Step 4: Build and Launch the Gradio Interface ---
|
| 197 |
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
-
#
|
| 201 |
-
css = """
|
| 202 |
-
footer {visibility: hidden !important;}
|
| 203 |
-
.gradio-container {font-family: 'IBM Plex Sans', sans-serif;}
|
| 204 |
-
"""
|
| 205 |
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
theme=gr.themes.Soft(primary_hue="teal", secondary_hue="orange"),
|
| 215 |
-
css=css
|
| 216 |
-
)
|
| 217 |
|
| 218 |
if __name__ == "__main__":
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# ==============================================================================
|
| 2 |
+
# Aura Mind Glow - Main Application
|
| 3 |
# ==============================================================================
|
| 4 |
"""
|
| 5 |
+
This script launches the Aura Mind Glow application.
|
| 6 |
+
It features two modes:
|
| 7 |
+
1. Field Mode (100% Offline): For quick diagnosis and remedy retrieval.
|
| 8 |
+
2. Connected Mode (Online): A conversational agent powered by the Google ADK.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
"""
|
| 10 |
|
| 11 |
# --- Step 0: Essential Imports ---
|
|
|
|
| 14 |
from PIL import Image
|
| 15 |
import os
|
| 16 |
import warnings
|
| 17 |
+
import socket
|
| 18 |
+
import asyncio
|
| 19 |
|
| 20 |
# Suppress potential warnings for a cleaner console
|
| 21 |
warnings.filterwarnings("ignore")
|
|
|
|
| 25 |
from unsloth import FastVisionModel
|
| 26 |
from transformers import AutoProcessor
|
| 27 |
|
| 28 |
+
# LangChain and RAG components
|
| 29 |
from langchain_community.vectorstores import FAISS
|
| 30 |
from langchain_community.document_loaders import TextLoader
|
| 31 |
from langchain_huggingface import HuggingFaceEmbeddings
|
| 32 |
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
| 33 |
|
| 34 |
+
# Google ADK
|
| 35 |
+
from google.adk import Agent, Runner
|
| 36 |
+
from google.adk.sessions import InMemorySessionService
|
| 37 |
+
from google.genai import types
|
| 38 |
+
|
| 39 |
+
# Custom Tools
|
| 40 |
+
from tools import PlantDiagnosisTool, RemedyRetrievalTool
|
| 41 |
+
|
| 42 |
print("✅ All libraries imported successfully.")
|
| 43 |
|
| 44 |
+
# --- Step 1: Global Setup - Vision Model and RAG ---
|
| 45 |
# This expensive setup runs only ONCE when the application starts.
|
| 46 |
|
| 47 |
+
print("Performing initial setup for Vision Model and RAG...")
|
| 48 |
VISION_MODEL = None
|
| 49 |
PROCESSOR = None
|
| 50 |
+
RETRIEVER = None
|
| 51 |
ADAPTER_PATH = "surfiniaburger/maize-health-diagnosis-adapter"
|
| 52 |
|
| 53 |
try:
|
| 54 |
+
# Load Vision Model
|
| 55 |
VISION_MODEL, PROCESSOR = FastVisionModel.from_pretrained(
|
| 56 |
model_name="unsloth/gemma-3n-E2B-it-unsloth-bnb-4bit",
|
| 57 |
max_seq_length=2048,
|
|
|
|
| 62 |
VISION_MODEL.load_adapter(ADAPTER_PATH)
|
| 63 |
print(f"✅ Vision model and adapter '{ADAPTER_PATH}' loaded successfully!")
|
| 64 |
|
| 65 |
+
# Build RAG Knowledge Base
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
remedy_knowledge_text = """
|
| 67 |
# Maize Health Guide
|
| 68 |
## Phosphorus Deficiency
|
|
|
|
| 85 |
**Symptoms:** Vigorous growth with lush, uniformly dark green leaves. No visible spots, lesions, or discoloration.
|
| 86 |
**Local Remedy:** No remedy needed. Maintain good agricultural practices, including proper watering, fertilization, and pest management to keep the plant healthy.
|
| 87 |
"""
|
|
|
|
|
|
|
| 88 |
with open("knowledge.txt", "w") as f:
|
| 89 |
f.write(remedy_knowledge_text)
|
|
|
|
| 90 |
loader = TextLoader("knowledge.txt")
|
| 91 |
documents = loader.load()
|
| 92 |
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
|
| 93 |
docs = text_splitter.split_documents(documents)
|
| 94 |
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
| 95 |
db = FAISS.from_documents(docs, embeddings)
|
| 96 |
+
RETRIEVER = db.as_retriever(search_kwargs={"k": 1})
|
|
|
|
|
|
|
| 97 |
print("✅ RAG knowledge base and retriever created successfully!")
|
| 98 |
|
| 99 |
except Exception as e:
|
| 100 |
+
print(f"❌ CRITICAL ERROR during global setup: {e}")
|
|
|
|
| 101 |
pass
|
| 102 |
|
| 103 |
+
# --- Step 2: Initialize ADK Tools and Agent ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
+
print("Initializing ADK Tools and Agent...")
|
| 106 |
+
DIAGNOSIS_TOOL = None
|
| 107 |
+
REMEDY_TOOL = None
|
| 108 |
+
ADK_AGENT = None
|
| 109 |
+
ADK_RUNNER = None
|
| 110 |
+
SESSION_SERVICE = None
|
| 111 |
|
| 112 |
+
try:
|
| 113 |
+
if VISION_MODEL and PROCESSOR and RETRIEVER:
|
| 114 |
+
DIAGNOSIS_TOOL = PlantDiagnosisTool(model=VISION_MODEL, processor=PROCESSOR)
|
| 115 |
+
REMEDY_TOOL = RemedyRetrievalTool(retriever=RETRIEVER)
|
| 116 |
+
|
| 117 |
+
ADK_AGENT = Agent(
|
| 118 |
+
name="AuraMindGlowAgent",
|
| 119 |
+
model="gemini-1.5-flash-001",
|
| 120 |
+
description="A farming assistant that can diagnose plant health and suggest remedies.",
|
| 121 |
+
instruction="You are a friendly farming assistant. Your goal is to help users identify plant health issues and find solutions. Use your tools to diagnose the plant from an image and then find a remedy.",
|
| 122 |
+
tools=[DIAGNOSIS_TOOL, REMEDY_TOOL]
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
SESSION_SERVICE = InMemorySessionService()
|
| 126 |
+
ADK_RUNNER = Runner(agent=ADK_AGENT, app_name="AuraMindGlow", session_service=SESSION_SERVICE)
|
| 127 |
+
print("✅ ADK Agent and Runner initialized successfully!")
|
| 128 |
+
else:
|
| 129 |
+
print("❌ Skipping ADK setup due to errors in model or RAG loading.")
|
| 130 |
|
| 131 |
+
except Exception as e:
|
| 132 |
+
print(f"❌ CRITICAL ERROR during ADK setup: {e}")
|
| 133 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
|
|
|
|
| 135 |
|
| 136 |
+
# --- Step 3: Define Gradio UIs ---
|
| 137 |
|
| 138 |
+
def create_field_mode_ui():
|
| 139 |
+
"""Creates the Gradio UI for the offline Field Mode."""
|
| 140 |
+
|
| 141 |
+
def get_diagnosis_and_remedy(uploaded_image: Image.Image) -> str:
|
| 142 |
+
if uploaded_image is None:
|
| 143 |
+
return "Please upload an image of a maize plant first."
|
| 144 |
+
if RETRIEVER is None:
|
| 145 |
+
raise gr.Error("Knowledge base is not loaded. Cannot find remedy. Check logs.")
|
| 146 |
+
|
| 147 |
+
try:
|
| 148 |
+
diagnosis_tool = PlantDiagnosisTool(model=VISION_MODEL, processor=PROCESSOR)
|
| 149 |
+
diagnosis = diagnosis_tool(uploaded_image)
|
| 150 |
+
print(f"Diagnosis received: {diagnosis}")
|
| 151 |
+
|
| 152 |
+
if "Could not parse" in diagnosis:
|
| 153 |
+
return f"Sorry, I couldn't identify the condition from the image. Raw output: {diagnosis}"
|
| 154 |
+
|
| 155 |
+
remedy_tool = RemedyRetrievalTool(retriever=RETRIEVER)
|
| 156 |
+
remedy = remedy_tool(diagnosis)
|
| 157 |
+
|
| 158 |
+
final_response = f"""
|
| 159 |
+
## Diagnosis Report
|
| 160 |
+
|
| 161 |
+
**Condition Identified:**
|
| 162 |
+
### {diagnosis}
|
| 163 |
+
|
| 164 |
+
---
|
| 165 |
+
|
| 166 |
+
## Suggested Remedy
|
| 167 |
+
|
| 168 |
+
{remedy}
|
| 169 |
+
"""
|
| 170 |
+
print("Workflow complete. Returning response.")
|
| 171 |
+
return final_response
|
| 172 |
+
|
| 173 |
+
except Exception as e:
|
| 174 |
+
print(f"An error occurred during the analysis workflow: {e}")
|
| 175 |
+
raise gr.Error(f"An unexpected error occurred: {e}")
|
| 176 |
+
|
| 177 |
+
css = """
|
| 178 |
+
footer {visibility: hidden !important;}
|
| 179 |
+
.gradio-container {font-family: 'IBM Plex Sans', sans-serif;}
|
| 180 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
+
return gr.Interface(
|
| 183 |
+
fn=get_diagnosis_and_remedy,
|
| 184 |
+
inputs=gr.Image(type="pil", label="Upload Maize Plant Image", sources=["upload", "webcam"]),
|
| 185 |
+
outputs=gr.Markdown(label="Diagnosis and Remedy Report", value="The report will appear here..."),
|
| 186 |
+
title="🌽 Aura Mind Glow: Field Mode (Offline)",
|
| 187 |
+
description="**A 100% Offline-Capable Farming Assistant.** Upload an image of a maize plant. The AI will diagnose its condition and retrieve a treatment plan from its local knowledge base.",
|
| 188 |
+
article="<p style='text-align: center;'>Built with Unsloth, LangChain, and Gradio. Version 1.1</p>",
|
| 189 |
+
allow_flagging="never",
|
| 190 |
+
theme=gr.themes.Soft(primary_hue="teal", secondary_hue="orange"),
|
| 191 |
+
css=css
|
| 192 |
+
)
|
|
|
|
|
|
|
| 193 |
|
| 194 |
+
def create_connected_mode_ui():
|
| 195 |
+
"""Creates the Gradio UI for the online Connected Mode."""
|
| 196 |
+
with gr.Blocks(theme=gr.themes.Soft(primary_hue="green", secondary_hue="lime")) as demo:
|
| 197 |
+
gr.Markdown("# 🌽 Aura Mind Glow: Connected Mode 🤖")
|
| 198 |
+
gr.Markdown("I am an AI farming assistant, powered by Google's ADK. I can help you diagnose plant health, get remedies, and more. How can I help you today?")
|
| 199 |
+
|
| 200 |
+
chatbot = gr.Chatbot()
|
| 201 |
+
msg = gr.Textbox(label="Your message")
|
| 202 |
+
clear = gr.Button("Clear")
|
| 203 |
+
|
| 204 |
+
async def user(user_message, history):
|
| 205 |
+
# This is a placeholder for image handling
|
| 206 |
+
# In a real implementation, we would handle image uploads here
|
| 207 |
+
return "", history + [[user_message, None]]
|
| 208 |
+
|
| 209 |
+
async def bot(history):
|
| 210 |
+
user_message = history[-1][0]
|
| 211 |
+
|
| 212 |
+
# Create a session for the user
|
| 213 |
+
session = await SESSION_SERVICE.create_session(app_name="AuraMindGlow", user_id="user123", session_id="session123")
|
| 214 |
+
content = types.Content(role='user', parts=[types.Part(text=user_message)])
|
| 215 |
+
|
| 216 |
+
# Stream the agent's response
|
| 217 |
+
events = ADK_RUNNER.run_async(user_id="user123", session_id="session123", new_message=content)
|
| 218 |
+
|
| 219 |
+
history[-1][1] = ""
|
| 220 |
+
async for event in events:
|
| 221 |
+
if event.is_final_response():
|
| 222 |
+
final_response = event.content.parts[0].text
|
| 223 |
+
history[-1][1] = final_response
|
| 224 |
+
yield history
|
| 225 |
+
|
| 226 |
+
msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
|
| 227 |
+
bot, chatbot, chatbot
|
| 228 |
+
)
|
| 229 |
+
clear.click(lambda: None, None, chatbot, queue=False)
|
| 230 |
+
|
| 231 |
+
return demo
|
| 232 |
|
| 233 |
+
# --- Step 4: App Launcher ---
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
+
def check_internet_connection(host="8.8.8.8", port=53, timeout=3):
|
| 236 |
+
"""Check for internet connectivity."""
|
| 237 |
+
try:
|
| 238 |
+
socket.setdefaulttimeout(timeout)
|
| 239 |
+
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
|
| 240 |
+
return True
|
| 241 |
+
except socket.error:
|
| 242 |
+
return False
|
|
|
|
|
|
|
|
|
|
| 243 |
|
| 244 |
if __name__ == "__main__":
|
| 245 |
+
if check_internet_connection() and ADK_RUNNER:
|
| 246 |
+
print("✅ Internet connection detected. Launching Connected Mode.")
|
| 247 |
+
ui = create_connected_mode_ui()
|
| 248 |
+
else:
|
| 249 |
+
print("❌ No internet connection or ADK setup failed. Launching Field Mode (Offline).")
|
| 250 |
+
ui = create_field_mode_ui()
|
| 251 |
+
|
| 252 |
+
ui.launch(share=True, debug=True)
|
miscellaneous/instructions.md
CHANGED
|
@@ -2872,3 +2872,261 @@ Next
|
|
| 2872 |
Built-in tools
|
| 2873 |
Copyright Google 2025
|
| 2874 |
Made with Material for MkDocs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2872 |
Built-in tools
|
| 2873 |
Copyright Google 2025
|
| 2874 |
Made with Material for MkDocs
|
| 2875 |
+
|
| 2876 |
+
|
| 2877 |
+
|
| 2878 |
+
Third Party Tools¶
|
| 2879 |
+
python_only
|
| 2880 |
+
|
| 2881 |
+
ADK is designed to be highly extensible, allowing you to seamlessly integrate tools from other AI Agent frameworks like CrewAI and LangChain. This interoperability is crucial because it allows for faster development time and allows you to reuse existing tools.
|
| 2882 |
+
|
| 2883 |
+
1. Using LangChain Tools¶
|
| 2884 |
+
ADK provides the LangchainTool wrapper to integrate tools from the LangChain ecosystem into your agents.
|
| 2885 |
+
|
| 2886 |
+
Example: Web Search using LangChain's Tavily tool¶
|
| 2887 |
+
Tavily provides a search API that returns answers derived from real-time search results, intended for use by applications like AI agents.
|
| 2888 |
+
|
| 2889 |
+
Follow ADK installation and setup guide.
|
| 2890 |
+
|
| 2891 |
+
Install Dependencies: Ensure you have the necessary LangChain packages installed. For example, to use the Tavily search tool, install its specific dependencies:
|
| 2892 |
+
|
| 2893 |
+
|
| 2894 |
+
pip install langchain_community tavily-python
|
| 2895 |
+
Obtain a Tavily API KEY and export it as an environment variable.
|
| 2896 |
+
|
| 2897 |
+
|
| 2898 |
+
export TAVILY_API_KEY=<REPLACE_WITH_API_KEY>
|
| 2899 |
+
Import: Import the LangchainTool wrapper from ADK and the specific LangChain tool you wish to use (e.g, TavilySearchResults).
|
| 2900 |
+
|
| 2901 |
+
|
| 2902 |
+
from google.adk.tools.langchain_tool import LangchainTool
|
| 2903 |
+
from langchain_community.tools import TavilySearchResults
|
| 2904 |
+
Instantiate & Wrap: Create an instance of your LangChain tool and pass it to the LangchainTool constructor.
|
| 2905 |
+
|
| 2906 |
+
|
| 2907 |
+
# Instantiate the LangChain tool
|
| 2908 |
+
tavily_tool_instance = TavilySearchResults(
|
| 2909 |
+
max_results=5,
|
| 2910 |
+
search_depth="advanced",
|
| 2911 |
+
include_answer=True,
|
| 2912 |
+
include_raw_content=True,
|
| 2913 |
+
include_images=True,
|
| 2914 |
+
)
|
| 2915 |
+
|
| 2916 |
+
# Wrap it with LangchainTool for ADK
|
| 2917 |
+
adk_tavily_tool = LangchainTool(tool=tavily_tool_instance)
|
| 2918 |
+
Add to Agent: Include the wrapped LangchainTool instance in your agent's tools list during definition.
|
| 2919 |
+
|
| 2920 |
+
|
| 2921 |
+
from google.adk import Agent
|
| 2922 |
+
|
| 2923 |
+
# Define the ADK agent, including the wrapped tool
|
| 2924 |
+
my_agent = Agent(
|
| 2925 |
+
name="langchain_tool_agent",
|
| 2926 |
+
model="gemini-2.0-flash",
|
| 2927 |
+
description="Agent to answer questions using TavilySearch.",
|
| 2928 |
+
instruction="I can answer your questions by searching the internet. Just ask me anything!",
|
| 2929 |
+
tools=[adk_tavily_tool] # Add the wrapped tool here
|
| 2930 |
+
)
|
| 2931 |
+
Full Example: Tavily Search¶
|
| 2932 |
+
Here's the full code combining the steps above to create and run an agent using the LangChain Tavily search tool.
|
| 2933 |
+
|
| 2934 |
+
|
| 2935 |
+
# Copyright 2025 Google LLC
|
| 2936 |
+
#
|
| 2937 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 2938 |
+
# you may not use this file except in compliance with the License.
|
| 2939 |
+
# You may obtain a copy of the License at
|
| 2940 |
+
#
|
| 2941 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 2942 |
+
#
|
| 2943 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 2944 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 2945 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 2946 |
+
# See the License for the specific language governing permissions and
|
| 2947 |
+
# limitations under the License.
|
| 2948 |
+
|
| 2949 |
+
import os
|
| 2950 |
+
from google.adk import Agent, Runner
|
| 2951 |
+
from google.adk.sessions import InMemorySessionService
|
| 2952 |
+
from google.adk.tools.langchain_tool import LangchainTool
|
| 2953 |
+
from google.genai import types
|
| 2954 |
+
from langchain_community.tools import TavilySearchResults
|
| 2955 |
+
|
| 2956 |
+
# Ensure TAVILY_API_KEY is set in your environment
|
| 2957 |
+
if not os.getenv("TAVILY_API_KEY"):
|
| 2958 |
+
print("Warning: TAVILY_API_KEY environment variable not set.")
|
| 2959 |
+
|
| 2960 |
+
APP_NAME = "news_app"
|
| 2961 |
+
USER_ID = "1234"
|
| 2962 |
+
SESSION_ID = "session1234"
|
| 2963 |
+
|
| 2964 |
+
# Instantiate LangChain tool
|
| 2965 |
+
tavily_search = TavilySearchResults(
|
| 2966 |
+
max_results=5,
|
| 2967 |
+
search_depth="advanced",
|
| 2968 |
+
include_answer=True,
|
| 2969 |
+
include_raw_content=True,
|
| 2970 |
+
include_images=True,
|
| 2971 |
+
)
|
| 2972 |
+
|
| 2973 |
+
# Wrap with LangchainTool
|
| 2974 |
+
adk_tavily_tool = LangchainTool(tool=tavily_search)
|
| 2975 |
+
|
| 2976 |
+
# Define Agent with the wrapped tool
|
| 2977 |
+
my_agent = Agent(
|
| 2978 |
+
name="langchain_tool_agent",
|
| 2979 |
+
model="gemini-2.0-flash",
|
| 2980 |
+
description="Agent to answer questions using TavilySearch.",
|
| 2981 |
+
instruction="I can answer your questions by searching the internet. Just ask me anything!",
|
| 2982 |
+
tools=[adk_tavily_tool] # Add the wrapped tool here
|
| 2983 |
+
)
|
| 2984 |
+
|
| 2985 |
+
async def setup_session_and_runner():
|
| 2986 |
+
session_service = InMemorySessionService()
|
| 2987 |
+
session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
|
| 2988 |
+
runner = Runner(agent=my_agent, app_name=APP_NAME, session_service=session_service)
|
| 2989 |
+
return session, runner
|
| 2990 |
+
|
| 2991 |
+
# Agent Interaction
|
| 2992 |
+
async def call_agent_async(query):
|
| 2993 |
+
content = types.Content(role='user', parts=[types.Part(text=query)])
|
| 2994 |
+
session, runner = await setup_session_and_runner()
|
| 2995 |
+
events = runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=content)
|
| 2996 |
+
|
| 2997 |
+
async for event in events:
|
| 2998 |
+
if event.is_final_response():
|
| 2999 |
+
final_response = event.content.parts[0].text
|
| 3000 |
+
print("Agent Response: ", final_response)
|
| 3001 |
+
|
| 3002 |
+
# Note: In Colab, you can directly use 'await' at the top level.
|
| 3003 |
+
# If running this code as a standalone Python script, you'll need to use asyncio.run() or manage the event loop.
|
| 3004 |
+
await call_agent_async("stock price of GOOG")
|
| 3005 |
+
2. Using CrewAI tools¶
|
| 3006 |
+
ADK provides the CrewaiTool wrapper to integrate tools from the CrewAI library.
|
| 3007 |
+
|
| 3008 |
+
Example: Web Search using CrewAI's Serper API¶
|
| 3009 |
+
Serper API provides access to Google Search results programmatically. It allows applications, like AI agents, to perform real-time Google searches (including news, images, etc.) and get structured data back without needing to scrape web pages directly.
|
| 3010 |
+
|
| 3011 |
+
Follow ADK installation and setup guide.
|
| 3012 |
+
|
| 3013 |
+
Install Dependencies: Install the necessary CrewAI tools package. For example, to use the SerperDevTool:
|
| 3014 |
+
|
| 3015 |
+
|
| 3016 |
+
pip install crewai-tools
|
| 3017 |
+
Obtain a Serper API KEY and export it as an environment variable.
|
| 3018 |
+
|
| 3019 |
+
|
| 3020 |
+
export SERPER_API_KEY=<REPLACE_WITH_API_KEY>
|
| 3021 |
+
Import: Import CrewaiTool from ADK and the desired CrewAI tool (e.g, SerperDevTool).
|
| 3022 |
+
|
| 3023 |
+
|
| 3024 |
+
from google.adk.tools.crewai_tool import CrewaiTool
|
| 3025 |
+
from crewai_tools import SerperDevTool
|
| 3026 |
+
Instantiate & Wrap: Create an instance of the CrewAI tool. Pass it to the CrewaiTool constructor. Crucially, you must provide a name and description to the ADK wrapper, as these are used by ADK's underlying model to understand when to use the tool.
|
| 3027 |
+
|
| 3028 |
+
|
| 3029 |
+
# Instantiate the CrewAI tool
|
| 3030 |
+
serper_tool_instance = SerperDevTool(
|
| 3031 |
+
n_results=10,
|
| 3032 |
+
save_file=False,
|
| 3033 |
+
search_type="news",
|
| 3034 |
+
)
|
| 3035 |
+
|
| 3036 |
+
# Wrap it with CrewaiTool for ADK, providing name and description
|
| 3037 |
+
adk_serper_tool = CrewaiTool(
|
| 3038 |
+
name="InternetNewsSearch",
|
| 3039 |
+
description="Searches the internet specifically for recent news articles using Serper.",
|
| 3040 |
+
tool=serper_tool_instance
|
| 3041 |
+
)
|
| 3042 |
+
Add to Agent: Include the wrapped CrewaiTool instance in your agent's tools list.
|
| 3043 |
+
|
| 3044 |
+
|
| 3045 |
+
from google.adk import Agent
|
| 3046 |
+
|
| 3047 |
+
# Define the ADK agent
|
| 3048 |
+
my_agent = Agent(
|
| 3049 |
+
name="crewai_search_agent",
|
| 3050 |
+
model="gemini-2.0-flash",
|
| 3051 |
+
description="Agent to find recent news using the Serper search tool.",
|
| 3052 |
+
instruction="I can find the latest news for you. What topic are you interested in?",
|
| 3053 |
+
tools=[adk_serper_tool] # Add the wrapped tool here
|
| 3054 |
+
)
|
| 3055 |
+
Full Example: Serper API¶
|
| 3056 |
+
Here's the full code combining the steps above to create and run an agent using the CrewAI Serper API search tool.
|
| 3057 |
+
|
| 3058 |
+
|
| 3059 |
+
# Copyright 2025 Google LLC
|
| 3060 |
+
#
|
| 3061 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 3062 |
+
# you may not use this file except in compliance with the License.
|
| 3063 |
+
# You may obtain a copy of the License at
|
| 3064 |
+
#
|
| 3065 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 3066 |
+
#
|
| 3067 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 3068 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 3069 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 3070 |
+
# See the License for the specific language governing permissions and
|
| 3071 |
+
# limitations under the License.
|
| 3072 |
+
|
| 3073 |
+
import os
|
| 3074 |
+
from google.adk import Agent, Runner
|
| 3075 |
+
from google.adk.sessions import InMemorySessionService
|
| 3076 |
+
from google.adk.tools.crewai_tool import CrewaiTool
|
| 3077 |
+
from google.genai import types
|
| 3078 |
+
from crewai_tools import SerperDevTool
|
| 3079 |
+
|
| 3080 |
+
|
| 3081 |
+
# Constants
|
| 3082 |
+
APP_NAME = "news_app"
|
| 3083 |
+
USER_ID = "user1234"
|
| 3084 |
+
SESSION_ID = "1234"
|
| 3085 |
+
|
| 3086 |
+
# Ensure SERPER_API_KEY is set in your environment
|
| 3087 |
+
if not os.getenv("SERPER_API_KEY"):
|
| 3088 |
+
print("Warning: SERPER_API_KEY environment variable not set.")
|
| 3089 |
+
|
| 3090 |
+
serper_tool_instance = SerperDevTool(
|
| 3091 |
+
n_results=10,
|
| 3092 |
+
save_file=False,
|
| 3093 |
+
search_type="news",
|
| 3094 |
+
)
|
| 3095 |
+
|
| 3096 |
+
adk_serper_tool = CrewaiTool(
|
| 3097 |
+
name="InternetNewsSearch",
|
| 3098 |
+
description="Searches the internet specifically for recent news articles using Serper.",
|
| 3099 |
+
tool=serper_tool_instance
|
| 3100 |
+
)
|
| 3101 |
+
|
| 3102 |
+
serper_agent = Agent(
|
| 3103 |
+
name="basic_search_agent",
|
| 3104 |
+
model="gemini-2.0-flash",
|
| 3105 |
+
description="Agent to answer questions using Google Search.",
|
| 3106 |
+
instruction="I can answer your questions by searching the internet. Just ask me anything!",
|
| 3107 |
+
# Add the Serper tool
|
| 3108 |
+
tools=[adk_serper_tool]
|
| 3109 |
+
)
|
| 3110 |
+
|
| 3111 |
+
# Session and Runner
|
| 3112 |
+
async def setup_session_and_runner():
|
| 3113 |
+
session_service = InMemorySessionService()
|
| 3114 |
+
session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
|
| 3115 |
+
runner = Runner(agent=serper_agent, app_name=APP_NAME, session_service=session_service)
|
| 3116 |
+
return session, runner
|
| 3117 |
+
|
| 3118 |
+
|
| 3119 |
+
# Agent Interaction
|
| 3120 |
+
async def call_agent_async(query):
|
| 3121 |
+
content = types.Content(role='user', parts=[types.Part(text=query)])
|
| 3122 |
+
session, runner = await setup_session_and_runner()
|
| 3123 |
+
events = runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=content)
|
| 3124 |
+
|
| 3125 |
+
async for event in events:
|
| 3126 |
+
if event.is_final_response():
|
| 3127 |
+
final_response = event.content.parts[0].text
|
| 3128 |
+
print("Agent Response: ", final_response)
|
| 3129 |
+
|
| 3130 |
+
# Note: In Colab, you can directly use 'await' at the top level.
|
| 3131 |
+
# If running this code as a standalone Python script, you'll need to use asyncio.run() or manage the event loop.
|
| 3132 |
+
await call_agent_async("what's the latest news on AI Agents?")
|
tools.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# aura-mind-glow/tools.py
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
from PIL import Image
|
| 5 |
+
from transformers import AutoProcessor
|
| 6 |
+
from unsloth import FastVisionModel
|
| 7 |
+
from langchain_community.vectorstores import FAISS
|
| 8 |
+
|
| 9 |
+
class PlantDiagnosisTool:
|
| 10 |
+
"""A tool to diagnose the condition of a maize plant from an image."""
|
| 11 |
+
|
| 12 |
+
def __init__(self, model: FastVisionModel, processor: AutoProcessor):
|
| 13 |
+
self.model = model
|
| 14 |
+
self.processor = processor
|
| 15 |
+
self.name = "PlantDiagnoser"
|
| 16 |
+
self.description = "Analyzes an image of a maize plant and returns a diagnosis of its health condition (e.g., 'Phosphorus Deficiency', 'Healthy Plant')."
|
| 17 |
+
|
| 18 |
+
def __call__(self, image: Image.Image) -> str:
|
| 19 |
+
"""
|
| 20 |
+
Takes a PIL Image and returns the diagnosis string.
|
| 21 |
+
"""
|
| 22 |
+
if self.model is None or self.processor is None or image is None:
|
| 23 |
+
return "Error: Vision model is not loaded or no image was provided."
|
| 24 |
+
|
| 25 |
+
image = image.convert("RGB")
|
| 26 |
+
messages = [
|
| 27 |
+
{"role": "user", "content": [{"type": "text", "text": "What is the condition of this maize plant? Provide only the name of the condition."}, {"type": "image", "image": image}]}
|
| 28 |
+
]
|
| 29 |
+
text_prompt = self.processor.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
| 30 |
+
inputs = self.processor(text=text_prompt, images=image, return_tensors="pt").to(self.model.device)
|
| 31 |
+
|
| 32 |
+
with torch.inference_mode():
|
| 33 |
+
outputs = self.model.generate(**inputs, max_new_tokens=48, use_cache=True)
|
| 34 |
+
|
| 35 |
+
response = self.processor.batch_decode(outputs, skip_special_tokens=True)[0]
|
| 36 |
+
|
| 37 |
+
# Clean up the model's output to get only the diagnosis
|
| 38 |
+
answer_start_index = response.rfind("model\n")
|
| 39 |
+
if answer_start_index != -1:
|
| 40 |
+
return response[answer_start_index + len("model\n"):
|
| 41 |
+
].strip()
|
| 42 |
+
|
| 43 |
+
answer_start_index = response.rfind("assistant\n")
|
| 44 |
+
if answer_start_index != -1:
|
| 45 |
+
return response[answer_start_index + len("assistant\n"):
|
| 46 |
+
].strip()
|
| 47 |
+
|
| 48 |
+
return "Could not parse diagnosis from model output."
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class RemedyRetrievalTool:
|
| 52 |
+
"""A tool to retrieve remedies for a given plant diagnosis."""
|
| 53 |
+
|
| 54 |
+
def __init__(self, retriever: FAISS):
|
| 55 |
+
self.retriever = retriever
|
| 56 |
+
self.name = "RemedyRetriever"
|
| 57 |
+
self.description = "Takes a plant health diagnosis (e.g., 'Phosphorus Deficiency') and returns a suggested remedy from a local knowledge base."
|
| 58 |
+
|
| 59 |
+
def __call__(self, diagnosis: str) -> str:
|
| 60 |
+
"""
|
| 61 |
+
Takes a diagnosis string and returns the remedy text.
|
| 62 |
+
"""
|
| 63 |
+
if self.retriever is None:
|
| 64 |
+
return "Error: Knowledge base is not loaded."
|
| 65 |
+
|
| 66 |
+
retrieved_docs = self.retriever.invoke(diagnosis)
|
| 67 |
+
if retrieved_docs:
|
| 68 |
+
return retrieved_docs[0].page_content
|
| 69 |
+
return "No specific remedy found in the knowledge base for this condition."
|