surfiniaburger commited on
Commit
b5b714b
Β·
1 Parent(s): 2a632a7
Files changed (2) hide show
  1. app.py +161 -172
  2. app2.py +247 -0
app.py CHANGED
@@ -1,247 +1,236 @@
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 ---
12
  import gradio as gr
13
  import torch
14
  from PIL import Image
15
- import os
16
- import warnings
17
- import socket
18
- import asyncio
19
- import tempfile
20
-
21
- # Suppress potential warnings for a cleaner console
22
- warnings.filterwarnings("ignore")
23
- os.environ["TORCH_COMPILE_DISABLE"] = "1" # Ensure torch compile is off
24
-
25
- # Unsloth and Transformers
26
  from unsloth import FastVisionModel
27
  from transformers import AutoProcessor
 
28
 
29
  # LangChain and RAG components
30
  from langchain_community.vectorstores import FAISS
31
  from langchain_community.document_loaders import TextLoader
32
  from langchain_huggingface import HuggingFaceEmbeddings
33
  from langchain.text_splitter import RecursiveCharacterTextSplitter
34
-
35
- # Google ADK
36
- from google.adk import Agent, Runner
37
- from google.adk.sessions import InMemorySessionService
38
- from google.genai import types
39
-
40
- # Custom Tools
41
- from tools import create_plant_diagnosis_tool, create_remedy_retrieval_tool
42
 
43
  print("βœ… All libraries imported successfully.")
44
 
45
- # --- Step 1: Global Setup - Vision Model and RAG ---
46
  # This expensive setup runs only ONCE when the application starts.
47
 
48
- print("Performing initial setup for Vision Model and RAG...")
 
 
 
 
 
49
  VISION_MODEL = None
50
  PROCESSOR = None
51
- RETRIEVER = None
52
- ADAPTER_PATH = "surfiniaburger/maize-health-diagnosis-adapter"
53
 
54
  try:
55
- # Load Vision Model
56
  VISION_MODEL, PROCESSOR = FastVisionModel.from_pretrained(
57
- model_name="unsloth/gemma-3n-E2B-it-unsloth-bnb-4bit",
58
  max_seq_length=2048,
59
  load_in_4bit=True,
60
  dtype=None,
61
  )
62
  FastVisionModel.for_inference(VISION_MODEL)
63
  VISION_MODEL.load_adapter(ADAPTER_PATH)
64
- print(f"βœ… Vision model and adapter '{ADAPTER_PATH}' loaded successfully!")
 
 
 
 
 
 
65
 
66
- # Build RAG Knowledge Base
 
 
67
  remedy_knowledge_text = """
68
  # Maize Health Guide
69
- ## Phosphorus Deficiency
70
  **Symptoms:** Plants are often stunted, dark green or purplish. This condition is sometimes called Purple leaf disease. The purplish discoloration is most evident on the tips of leaves of young plants.
71
  **Cause:** Lack of available phosphorus in the soil, often due to incorrect pH or cold soil temperatures.
72
- **Local Remedy:** Apply a high-phosphorus starter fertilizer at planting time. In organic systems, bone meal or rock phosphate are excellent sources. A foliar spray of a water-soluable phosphate fertilizer can be applied directly to the leaves for a quick, short-term fix. Ensure soil pH is between 6.0 and 7.0 for optimal phosphorus uptake.
73
  ## Nitrogen Deficiency
74
- **Symptoms:** General yellowing (chlorosis) of the plant, starting with the lower, older leaves in a distinct "V" shape that progresses up the midrib.
75
- **Cause:** Insufficient nitrogen in the soil, which is a mobile nutrient.
76
- **Local Remedy:** Side-dress with a nitrogen-rich fertilizer like urea, ammonium nitrate, or composted manure whenplants are about knee-high. For organic farming, blood meal or feather meal are effective sources.
77
  ## Leaf Spot
78
  **Symptoms:** Small, circular to oval spots with tan centers and dark borders on the leaves. This is a common fungal disease.
79
- **Cause:** Fungal pathogens that spread via water splash and wind.
80
- **Local Remedy:** Use resistant maize varieties if available. Apply appropriate fungicides if the infection is severe, following label directions. To prevent future outbreaks, improve air circulation by not overcrowding plants and destroy infected crop debris after harvest.
81
  ## Leaf Blight
82
- **Symptoms:** Large, irregular, grayish-green or tan lesions on the leaves, often appearing cigar-shaped. These lesions can grow and merge, leading to significant leaf death.
83
  **Cause:** Fungal pathogens that thrive in warm, humid conditions.
84
- **Local Remedy:** The primary defense is planting resistant hybrids. Fungicide applications may be necessary and should be timed based on disease scouting and weather forecasts. Crop rotation and tilling under infected residue can help reduce the pathogen for the next season.
85
  ## Healthy Plant
86
- **Symptoms:** Vigorous growth with lush, uniformly dark green leaves. No visible spots, lesions, or discoloration.
87
- **Local Remedy:** No remedy needed. Maintain good agricultural practices, including proper watering, fertilization, and pest management to keep the plant healthy.
88
  """
 
 
 
89
  with open("knowledge.txt", "w") as f:
90
  f.write(remedy_knowledge_text)
 
91
  loader = TextLoader("knowledge.txt")
92
  documents = loader.load()
 
 
93
  text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
94
  docs = text_splitter.split_documents(documents)
 
 
95
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
 
 
96
  db = FAISS.from_documents(docs, embeddings)
97
- RETRIEVER = db.as_retriever(search_kwargs={"k": 1})
 
 
 
98
  print("βœ… RAG knowledge base and retriever created successfully!")
99
 
100
  except Exception as e:
101
- print(f"❌ CRITICAL ERROR during global setup: {e}")
102
- pass
103
 
104
- # --- Step 2: Initialize ADK Tools and Agent ---
105
-
106
- print("Initializing ADK Tools and Agent...")
107
- DIAGNOSIS_TOOL = None
108
- REMEDY_TOOL = None
109
- ADK_AGENT = None
110
- ADK_RUNNER = None
111
- SESSION_SERVICE = None
112
 
 
113
  try:
114
- if VISION_MODEL and PROCESSOR and RETRIEVER:
115
- DIAGNOSIS_TOOL = create_plant_diagnosis_tool(model=VISION_MODEL, processor=PROCESSOR)
116
- REMEDY_TOOL = create_remedy_retrieval_tool(retriever=RETRIEVER)
117
-
118
- ADK_AGENT = Agent(
119
- name="AuraMindGlowAgent",
120
- model="gemini-2.5-flash",
121
- description="A farming assistant that can diagnose plant health and suggest remedies.",
122
- 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.",
123
- tools=[DIAGNOSIS_TOOL, REMEDY_TOOL]
124
- )
125
-
126
- SESSION_SERVICE = InMemorySessionService()
127
- ADK_RUNNER = Runner(agent=ADK_AGENT, app_name="AuraMindGlow", session_service=SESSION_SERVICE)
128
- print("βœ… ADK Agent and Runner initialized successfully!")
129
  else:
130
- print("❌ Skipping ADK setup due to errors in model or RAG loading.")
131
-
132
- except Exception as e:
133
- print(f"❌ CRITICAL ERROR during ADK setup: {e}")
134
- pass
135
 
136
 
137
- # --- Step 3: Define Gradio UIs ---
 
 
 
 
 
 
138
 
139
- def create_field_mode_ui():
140
- """Creates the Gradio UI for the offline Field Mode."""
 
 
 
 
141
 
142
- def get_diagnosis_and_remedy(uploaded_image: Image.Image) -> str:
143
- if uploaded_image is None:
144
- return "Please upload an image of a maize plant first."
145
- if RETRIEVER is None:
146
- raise gr.Error("Knowledge base is not loaded. Cannot find remedy. Check logs.")
147
-
148
- temp_file_path = None
149
- try:
150
- with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as temp_file:
151
- uploaded_image.save(temp_file.name)
152
- temp_file_path = temp_file.name
153
 
154
- diagnosis = DIAGNOSIS_TOOL(temp_file_path)
155
- print(f"Diagnosis received: {diagnosis}")
 
156
 
157
- if "Could not parse" in diagnosis:
158
- return f"Sorry, I couldn't identify the condition from the image. Raw output: {diagnosis}"
159
 
160
- remedy = REMEDY_TOOL(diagnosis)
161
-
162
- final_response = f"""
163
- ## Diagnosis Report
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
- **Condition Identified:**
166
- ### {diagnosis}
167
 
168
- ---
 
 
 
 
 
 
169
 
170
- ## Suggested Remedy
 
 
 
171
 
172
- {remedy}
173
- """
174
- print("Workflow complete. Returning response.")
175
- return final_response
176
 
 
 
 
 
 
 
 
 
 
177
  except Exception as e:
178
- print(f"An error occurred during the analysis workflow: {e}")
179
- raise gr.Error(f"An unexpected error occurred: {e}")
180
- finally:
181
- if temp_file_path and os.path.exists(temp_file_path):
182
- os.remove(temp_file_path)
183
-
184
- css = """
185
- footer {visibility: hidden !important;}
186
- .gradio-container {font-family: 'IBM Plex Sans', sans-serif;}
187
- """
188
 
189
- return gr.Interface(
190
- fn=get_diagnosis_and_remedy,
191
- inputs=gr.Image(type="pil", label="Upload Maize Plant Image", sources=["upload", "webcam"]),
192
- outputs=gr.Markdown(label="Diagnosis and Remedy Report", value="The report will appear here..."),
193
- title="🌽 Aura Mind Glow: Field Mode (Offline)",
194
- 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.",
195
- article="<p style='text-align: center;'>Built with Unsloth, LangChain, and Gradio. Version 1.3</p>",
196
- allow_flagging="never",
197
- theme=gr.themes.Soft(primary_hue="teal", secondary_hue="orange"),
198
- css=css
199
- )
200
 
201
- def create_connected_mode_ui():
202
- """Creates the Gradio UI for the online Connected Mode."""
203
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="green", secondary_hue="lime")) as demo:
204
- gr.Markdown("# 🌽 Aura Mind Glow: Connected Mode πŸ€–")
205
- gr.Markdown("I am an AI farming assistant. Upload an image to get started.")
206
-
207
- chatbot = gr.Chatbot()
208
- msg = gr.MultimodalTextbox(file_types=["image"], label="Upload an image for diagnosis")
209
-
210
- async def respond(chat_input, history):
211
- files = chat_input["files"]
212
- if files:
213
- file_path = files[0]
214
- # Directly call the diagnosis tool for testing
215
- diagnosis = DIAGNOSIS_TOOL(file_path)
216
- bot_message = f"**Diagnosis:** {diagnosis}"
217
- history.append([(file_path,), bot_message])
218
- else:
219
- bot_message = "Please upload an image for diagnosis."
220
- history.append([chat_input["text"], bot_message])
221
-
222
- return history, gr.MultimodalTextbox(value=None)
223
-
224
- msg.submit(respond, [msg, chatbot], [chatbot, msg])
225
-
226
- return demo
227
-
228
- # --- Step 4: App Launcher ---
229
-
230
- def check_internet_connection(host="8.8.8.8", port=53, timeout=3):
231
- """Check for internet connectivity."""
232
- try:
233
- socket.setdefaulttimeout(timeout)
234
- socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
235
- return True
236
- except socket.error:
237
- return False
238
-
239
- if __name__ == "__main__":
240
- if check_internet_connection() and ADK_RUNNER:
241
- print("βœ… Internet connection detected. Launching Connected Mode.")
242
- ui = create_connected_mode_ui()
243
- else:
244
- print("❌ No internet connection or ADK setup failed. Launching Field Mode (Offline).")
245
- ui = create_field_mode_ui()
246
-
247
- ui.launch(share=True, debug=True)
 
1
  # ==============================================================================
2
+ # GRADIO APP WITH LANGCHAIN AGENT (DIAGNOSIS + REMEDY)
3
  # ==============================================================================
4
  """
5
+ This script launches a multi-step agentic application for maize health.
6
+ The workflow is as follows:
7
+ 1. A fine-tuned Gemma-3N vision model (the 'Diagnoser Tool') analyzes an
8
+ uploaded image to identify the plant's condition.
9
+ 2. The diagnosis text is passed to a LangChain agent.
10
+ 3. The agent uses a 'Remedy Retriever Tool' (a RAG pipeline) to search a
11
+ knowledge base for relevant treatments.
12
+ 4. The agent synthesizes the diagnosis and the retrieved remedy into a
13
+ comprehensive, helpful response.
14
  """
15
 
16
  # --- Step 0: Essential Imports ---
17
  import gradio as gr
18
  import torch
19
  from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
20
  from unsloth import FastVisionModel
21
  from transformers import AutoProcessor
22
+ import os
23
 
24
  # LangChain and RAG components
25
  from langchain_community.vectorstores import FAISS
26
  from langchain_community.document_loaders import TextLoader
27
  from langchain_huggingface import HuggingFaceEmbeddings
28
  from langchain.text_splitter import RecursiveCharacterTextSplitter
29
+ from langchain.prompts import ChatPromptTemplate
30
+ from langchain_core.runnables import RunnablePassthrough
31
+ from langchain_core.output_parsers import StrOutputParser
32
+ from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint
 
 
 
 
33
 
34
  print("βœ… All libraries imported successfully.")
35
 
36
+ # --- Step 1: Global Setup - Vision Model (The 'Diagnoser') ---
37
  # This expensive setup runs only ONCE when the application starts.
38
 
39
+ print("Performing initial setup for the Vision Model...")
40
+ BASE_MODEL_NAME = "unsloth/gemma-3n-E2B-it-unsloth-bnb-4bit"
41
+
42
+ # !!! IMPORTANT: Replace this with the model repo ID you created on the Hub !!!
43
+ ADAPTER_PATH = "surfiniaburger/maize-health-diagnosis-adapter" # <--- YOUR HF ADAPTER REPO
44
+
45
  VISION_MODEL = None
46
  PROCESSOR = None
 
 
47
 
48
  try:
 
49
  VISION_MODEL, PROCESSOR = FastVisionModel.from_pretrained(
50
+ model_name=BASE_MODEL_NAME,
51
  max_seq_length=2048,
52
  load_in_4bit=True,
53
  dtype=None,
54
  )
55
  FastVisionModel.for_inference(VISION_MODEL)
56
  VISION_MODEL.load_adapter(ADAPTER_PATH)
57
+ print("βœ… Vision model and adapter loaded successfully!")
58
+
59
+ except Exception as e:
60
+ print(f"❌ CRITICAL ERROR during vision model loading: {e}")
61
+
62
+ # --- Step 2: Global Setup - RAG Knowledge Base (The 'Remedy Retriever') ---
63
+ # We create an in-memory vector store with remedy information. This also runs only once.
64
 
65
+ print("Building the RAG knowledge base for remedies...")
66
+ try:
67
+ # In a real enterprise app, this text would come from a database or file system.
68
  remedy_knowledge_text = """
69
  # Maize Health Guide
70
+ ## Phosphorus Deficiency / Purple Leaf Disease
71
  **Symptoms:** Plants are often stunted, dark green or purplish. This condition is sometimes called Purple leaf disease. The purplish discoloration is most evident on the tips of leaves of young plants.
72
  **Cause:** Lack of available phosphorus in the soil, often due to incorrect pH or cold soil temperatures.
73
+ **Local Remedy:** Apply a high-phosphorus starter fertilizer at planting time. In organic systems, bone meal or rock phosphate are excellent sources. A foliar spray of a water-soluable phosphate fertilizer can be applied directly to the leaves.
74
  ## Nitrogen Deficiency
75
+ **Symptoms:** General yellowing (chlorosis) of the plant, starting with the lower, older leaves in a distinct "V" shape.
76
+ **Cause:** Insufficient nitrogen in the soil.
77
+ **Local Remedy:** Side-dress with a nitrogen-rich fertilizer like urea or composted manure.
78
  ## Leaf Spot
79
  **Symptoms:** Small, circular to oval spots with tan centers and dark borders on the leaves. This is a common fungal disease.
80
+ **Local Remedy:** Use resistant maize varieties if available. Apply appropriate fungicides if the infection is severe, following label directions. Improve air circulation and destroy infected crop debris after harvest.
 
81
  ## Leaf Blight
82
+ **Symptoms:** Large, irregular, grayish-green or tan lesions on the leaves. These lesions can grow and merge, leading to significant leaf death.
83
  **Cause:** Fungal pathogens that thrive in warm, humid conditions.
84
+ **Local Remedy:** The primary defense is planting resistant hybrids. Fungicide applications may be necessary and should be timed based on disease scouting. Crop rotation and tilling under infected residue can help reduce the pathogen for the next season.
85
  ## Healthy Plant
86
+ **Symptoms:** Vigorous growth with lush, uniformly dark green leaves.
87
+ **Local Remedy:** No remedy needed. Maintain good agricultural practices.
88
  """
89
+
90
+
91
+ # Create a temporary file to load the text
92
  with open("knowledge.txt", "w") as f:
93
  f.write(remedy_knowledge_text)
94
+
95
  loader = TextLoader("knowledge.txt")
96
  documents = loader.load()
97
+
98
+ # Split documents into chunks
99
  text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
100
  docs = text_splitter.split_documents(documents)
101
+
102
+ # Create embeddings model
103
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
104
+
105
+ # Create FAISS vector store from documents
106
  db = FAISS.from_documents(docs, embeddings)
107
+
108
+ # Create the retriever object
109
+ RETRIEVER = db.as_retriever(search_kwargs={"k": 2}) # Retrieves the top 2 most relevant chunks
110
+
111
  print("βœ… RAG knowledge base and retriever created successfully!")
112
 
113
  except Exception as e:
114
+ print(f"❌ CRITICAL ERROR during RAG setup: {e}")
 
115
 
116
+ # --- Step 3: Global Setup - The LangChain Agent (The 'Synthesizer') ---
117
+ # The agent's "brain" is a powerful LLM from the Hugging Face Hub.
118
+ # You must set your HF_TOKEN in the Space's secrets for this to work.
 
 
 
 
 
119
 
120
+ print("Initializing the LangChain agent...")
121
  try:
122
+ # *** THE FIX IS APPLIED HERE ***
123
+ # 1. Explicitly get the token from the environment secrets.
124
+ hf_token = os.environ.get("HF_TOKEN")
125
+ if not hf_token:
126
+ # This provides a clear error in the logs if the secret is missing.
127
+ raise ValueError("HF_TOKEN secret not found. Please add it in your Space settings.")
 
 
 
 
 
 
 
 
 
128
  else:
129
+ print("βœ… HF_TOKEN secret found successfully.")
 
 
 
 
130
 
131
 
132
+ prompt = ChatPromptTemplate.from_messages(
133
+ [
134
+ ("system", "You are an expert agricultural assistant. Your goal is to provide a clear, actionable guide based on a plant's diagnosis and retrieved context. Synthesize the information into a helpful, well-structured response."),
135
+ ("human", "My plant has the following diagnosis: {diagnosis}\n\nUsing this information, please provide a remedy based on the following retrieved context:\n\nCONTEXT:\n{context}")
136
+ ]
137
+ )
138
+ print(f"DEBUG: Created prompt of type: {type(prompt)}")
139
 
140
+ base_llm = HuggingFaceEndpoint(
141
+ repo_id="HuggingFaceH4/zephyr-7b-beta",
142
+ huggingfacehub_api_token=hf_token,
143
+ max_new_tokens=512,
144
+ temperature=0.2,
145
+ )
146
 
147
+ # 2. Wrap the base LLM with ChatHuggingFace, passing the base_llm as the required 'llm' field.
148
+ chat_model = ChatHuggingFace(llm=base_llm)
149
+ # This is the full LCEL (LangChain Expression Language) chain.
150
+ # It defines the entire multi-step workflow.
151
+ AGENT_CHAIN = (
152
+ {"context": RETRIEVER, "diagnosis": RunnablePassthrough()}
153
+ | prompt | chat_model | StrOutputParser()
154
+ )
155
+ print("βœ… LangChain agent initialized successfully!")
 
 
156
 
157
+ except Exception as e:
158
+ print(f"❌ CRITICAL ERROR during LangChain agent initialization: {e}")
159
+ AGENT_CHAIN = None
160
 
161
+ # --- Step 4: Define the Core Functions for the Gradio App ---
 
162
 
163
+ def diagnose_plant_from_image(uploaded_image: Image.Image) -> str:
164
+ """
165
+ This function takes a PIL Image and runs ONLY the vision model diagnosis.
166
+ It's the first step in our agentic workflow.
167
+ """
168
+ if VISION_MODEL is None or PROCESSOR is None or uploaded_image is None:
169
+ return "ERROR: Vision model not loaded."
170
+
171
+ image = uploaded_image.convert("RGB")
172
+ messages = [
173
+ {"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}]}
174
+ ]
175
+ text_prompt = PROCESSOR.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
176
+ inputs = PROCESSOR(text=text_prompt, images=image, return_tensors="pt").to(VISION_MODEL.device)
177
+
178
+ with torch.inference_mode():
179
+ outputs = VISION_MODEL.generate(**inputs, max_new_tokens=48, use_cache=True)
180
+
181
+ response = PROCESSOR.batch_decode(outputs, skip_special_tokens=True)[0]
182
+ answer_start_index = response.rfind("model\n")
183
+ if answer_start_index != -1:
184
+ return response[answer_start_index + len("model\n"):].strip()
185
+ return "Could not parse diagnosis."
186
 
 
 
187
 
188
+ def run_full_analysis(uploaded_image: Image.Image) -> str:
189
+ """
190
+ This is the main function for the Gradio interface. It orchestrates the entire
191
+ diagnosis-to-remedy workflow.
192
+ """
193
+ if uploaded_image is None:
194
+ return "Please upload an image of a maize plant."
195
 
196
+ # Step 1: Get the initial diagnosis from the vision model.
197
+ print("Workflow Step 1: Running diagnosis...")
198
+ diagnosis = diagnose_plant_from_image(uploaded_image)
199
+ print(f"Diagnosis received: {diagnosis}")
200
 
201
+ if "ERROR" in diagnosis or "Could not parse" in diagnosis:
202
+ return f"Sorry, I couldn't identify the condition from the image. Raw output: {diagnosis}"
 
 
203
 
204
+ # Step 2: Invoke the LangChain agent with the diagnosis to get the remedy.
205
+ print("Workflow Step 2: Invoking agent for remedy retrieval...")
206
+ if AGENT_CHAIN:
207
+ try:
208
+ retrieved_docs = RETRIEVER.invoke(diagnosis)
209
+ print(f"DEBUG: Retrieved docs for '{diagnosis}': {retrieved_docs}")
210
+ response = AGENT_CHAIN.invoke(diagnosis)
211
+ print("Agent invocation successful.")
212
+ return response
213
  except Exception as e:
214
+ print(f"Agent invocation failed: {e}")
215
+ return "The diagnosis was successful, but I failed to retrieve a remedy. Please check the logs."
216
+ else:
217
+ return "ERROR: The main analysis agent is not available."
 
 
 
 
 
 
218
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
+ # --- Step 5: Build and Launch the Gradio Interface ---
221
+
222
+ print("Building Gradio interface...")
223
+
224
+
225
+ demo = gr.Interface(
226
+ fn=run_full_analysis,
227
+ inputs=gr.Image(type="pil", label="Upload Maize Plant Image"),
228
+ outputs=gr.Markdown(label="Full Analysis and Remedy", value="The agent's report will appear here..."),
229
+ title="🌽 Maize Health Automated Diagnosis & Remedy Agent",
230
+ description="**This is an advanced, multi-step agent.** Upload an image of a maize plant. The AI will first **diagnose** the condition using a fine-tuned vision model, then **retrieve** a detailed treatment plan from its knowledge base.",
231
+ article="Built with Unsloth, LangChain, and Gradio. Powered by Gemma-3N and Zephyr-7B.",
232
+ allow_flagging="never",
233
+ )
234
+
235
+ print("Launching Gradio app...")
236
+ demo.launch(share=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app2.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ---
12
+ import gradio as gr
13
+ import torch
14
+ from PIL import Image
15
+ import os
16
+ import warnings
17
+ import socket
18
+ import asyncio
19
+ import tempfile
20
+
21
+ # Suppress potential warnings for a cleaner console
22
+ warnings.filterwarnings("ignore")
23
+ os.environ["TORCH_COMPILE_DISABLE"] = "1" # Ensure torch compile is off
24
+
25
+ # Unsloth and Transformers
26
+ from unsloth import FastVisionModel
27
+ from transformers import AutoProcessor
28
+
29
+ # LangChain and RAG components
30
+ from langchain_community.vectorstores import FAISS
31
+ from langchain_community.document_loaders import TextLoader
32
+ from langchain_huggingface import HuggingFaceEmbeddings
33
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
34
+
35
+ # Google ADK
36
+ from google.adk import Agent, Runner
37
+ from google.adk.sessions import InMemorySessionService
38
+ from google.genai import types
39
+
40
+ # Custom Tools
41
+ from tools import create_plant_diagnosis_tool, create_remedy_retrieval_tool
42
+
43
+ print("βœ… All libraries imported successfully.")
44
+
45
+ # --- Step 1: Global Setup - Vision Model and RAG ---
46
+ # This expensive setup runs only ONCE when the application starts.
47
+
48
+ print("Performing initial setup for Vision Model and RAG...")
49
+ VISION_MODEL = None
50
+ PROCESSOR = None
51
+ RETRIEVER = None
52
+ ADAPTER_PATH = "surfiniaburger/maize-health-diagnosis-adapter"
53
+
54
+ try:
55
+ # Load Vision Model
56
+ VISION_MODEL, PROCESSOR = FastVisionModel.from_pretrained(
57
+ model_name="unsloth/gemma-3n-E2B-it-unsloth-bnb-4bit",
58
+ max_seq_length=2048,
59
+ load_in_4bit=True,
60
+ dtype=None,
61
+ )
62
+ FastVisionModel.for_inference(VISION_MODEL)
63
+ VISION_MODEL.load_adapter(ADAPTER_PATH)
64
+ print(f"βœ… Vision model and adapter '{ADAPTER_PATH}' loaded successfully!")
65
+
66
+ # Build RAG Knowledge Base
67
+ remedy_knowledge_text = """
68
+ # Maize Health Guide
69
+ ## Phosphorus Deficiency
70
+ **Symptoms:** Plants are often stunted, dark green or purplish. This condition is sometimes called Purple leaf disease. The purplish discoloration is most evident on the tips of leaves of young plants.
71
+ **Cause:** Lack of available phosphorus in the soil, often due to incorrect pH or cold soil temperatures.
72
+ **Local Remedy:** Apply a high-phosphorus starter fertilizer at planting time. In organic systems, bone meal or rock phosphate are excellent sources. A foliar spray of a water-soluable phosphate fertilizer can be applied directly to the leaves for a quick, short-term fix. Ensure soil pH is between 6.0 and 7.0 for optimal phosphorus uptake.
73
+ ## Nitrogen Deficiency
74
+ **Symptoms:** General yellowing (chlorosis) of the plant, starting with the lower, older leaves in a distinct "V" shape that progresses up the midrib.
75
+ **Cause:** Insufficient nitrogen in the soil, which is a mobile nutrient.
76
+ **Local Remedy:** Side-dress with a nitrogen-rich fertilizer like urea, ammonium nitrate, or composted manure whenplants are about knee-high. For organic farming, blood meal or feather meal are effective sources.
77
+ ## Leaf Spot
78
+ **Symptoms:** Small, circular to oval spots with tan centers and dark borders on the leaves. This is a common fungal disease.
79
+ **Cause:** Fungal pathogens that spread via water splash and wind.
80
+ **Local Remedy:** Use resistant maize varieties if available. Apply appropriate fungicides if the infection is severe, following label directions. To prevent future outbreaks, improve air circulation by not overcrowding plants and destroy infected crop debris after harvest.
81
+ ## Leaf Blight
82
+ **Symptoms:** Large, irregular, grayish-green or tan lesions on the leaves, often appearing cigar-shaped. These lesions can grow and merge, leading to significant leaf death.
83
+ **Cause:** Fungal pathogens that thrive in warm, humid conditions.
84
+ **Local Remedy:** The primary defense is planting resistant hybrids. Fungicide applications may be necessary and should be timed based on disease scouting and weather forecasts. Crop rotation and tilling under infected residue can help reduce the pathogen for the next season.
85
+ ## Healthy Plant
86
+ **Symptoms:** Vigorous growth with lush, uniformly dark green leaves. No visible spots, lesions, or discoloration.
87
+ **Local Remedy:** No remedy needed. Maintain good agricultural practices, including proper watering, fertilization, and pest management to keep the plant healthy.
88
+ """
89
+ with open("knowledge.txt", "w") as f:
90
+ f.write(remedy_knowledge_text)
91
+ loader = TextLoader("knowledge.txt")
92
+ documents = loader.load()
93
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
94
+ docs = text_splitter.split_documents(documents)
95
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
96
+ db = FAISS.from_documents(docs, embeddings)
97
+ RETRIEVER = db.as_retriever(search_kwargs={"k": 1})
98
+ print("βœ… RAG knowledge base and retriever created successfully!")
99
+
100
+ except Exception as e:
101
+ print(f"❌ CRITICAL ERROR during global setup: {e}")
102
+ pass
103
+
104
+ # --- Step 2: Initialize ADK Tools and Agent ---
105
+
106
+ print("Initializing ADK Tools and Agent...")
107
+ DIAGNOSIS_TOOL = None
108
+ REMEDY_TOOL = None
109
+ ADK_AGENT = None
110
+ ADK_RUNNER = None
111
+ SESSION_SERVICE = None
112
+
113
+ try:
114
+ if VISION_MODEL and PROCESSOR and RETRIEVER:
115
+ DIAGNOSIS_TOOL = create_plant_diagnosis_tool(model=VISION_MODEL, processor=PROCESSOR)
116
+ REMEDY_TOOL = create_remedy_retrieval_tool(retriever=RETRIEVER)
117
+
118
+ ADK_AGENT = Agent(
119
+ name="AuraMindGlowAgent",
120
+ model="gemini-2.5-flash",
121
+ description="A farming assistant that can diagnose plant health and suggest remedies.",
122
+ 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.",
123
+ tools=[DIAGNOSIS_TOOL, REMEDY_TOOL]
124
+ )
125
+
126
+ SESSION_SERVICE = InMemorySessionService()
127
+ ADK_RUNNER = Runner(agent=ADK_AGENT, app_name="AuraMindGlow", session_service=SESSION_SERVICE)
128
+ print("βœ… ADK Agent and Runner initialized successfully!")
129
+ else:
130
+ print("❌ Skipping ADK setup due to errors in model or RAG loading.")
131
+
132
+ except Exception as e:
133
+ print(f"❌ CRITICAL ERROR during ADK setup: {e}")
134
+ pass
135
+
136
+
137
+ # --- Step 3: Define Gradio UIs ---
138
+
139
+ def create_field_mode_ui():
140
+ """Creates the Gradio UI for the offline Field Mode."""
141
+
142
+ def get_diagnosis_and_remedy(uploaded_image: Image.Image) -> str:
143
+ if uploaded_image is None:
144
+ return "Please upload an image of a maize plant first."
145
+ if RETRIEVER is None:
146
+ raise gr.Error("Knowledge base is not loaded. Cannot find remedy. Check logs.")
147
+
148
+ temp_file_path = None
149
+ try:
150
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as temp_file:
151
+ uploaded_image.save(temp_file.name)
152
+ temp_file_path = temp_file.name
153
+
154
+ diagnosis = DIAGNOSIS_TOOL(temp_file_path)
155
+ print(f"Diagnosis received: {diagnosis}")
156
+
157
+ if "Could not parse" in diagnosis:
158
+ return f"Sorry, I couldn't identify the condition from the image. Raw output: {diagnosis}"
159
+
160
+ remedy = REMEDY_TOOL(diagnosis)
161
+
162
+ final_response = f"""
163
+ ## Diagnosis Report
164
+
165
+ **Condition Identified:**
166
+ ### {diagnosis}
167
+
168
+ ---
169
+
170
+ ## Suggested Remedy
171
+
172
+ {remedy}
173
+ """
174
+ print("Workflow complete. Returning response.")
175
+ return final_response
176
+
177
+ except Exception as e:
178
+ print(f"An error occurred during the analysis workflow: {e}")
179
+ raise gr.Error(f"An unexpected error occurred: {e}")
180
+ finally:
181
+ if temp_file_path and os.path.exists(temp_file_path):
182
+ os.remove(temp_file_path)
183
+
184
+ css = """
185
+ footer {visibility: hidden !important;}
186
+ .gradio-container {font-family: 'IBM Plex Sans', sans-serif;}
187
+ """
188
+
189
+ return gr.Interface(
190
+ fn=get_diagnosis_and_remedy,
191
+ inputs=gr.Image(type="pil", label="Upload Maize Plant Image", sources=["upload", "webcam"]),
192
+ outputs=gr.Markdown(label="Diagnosis and Remedy Report", value="The report will appear here..."),
193
+ title="🌽 Aura Mind Glow: Field Mode (Offline)",
194
+ 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.",
195
+ article="<p style='text-align: center;'>Built with Unsloth, LangChain, and Gradio. Version 1.3</p>",
196
+ allow_flagging="never",
197
+ theme=gr.themes.Soft(primary_hue="teal", secondary_hue="orange"),
198
+ css=css
199
+ )
200
+
201
+ def create_connected_mode_ui():
202
+ """Creates the Gradio UI for the online Connected Mode."""
203
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="green", secondary_hue="lime")) as demo:
204
+ gr.Markdown("# 🌽 Aura Mind Glow: Connected Mode πŸ€–")
205
+ gr.Markdown("I am an AI farming assistant. Upload an image to get started.")
206
+
207
+ chatbot = gr.Chatbot()
208
+ msg = gr.MultimodalTextbox(file_types=["image"], label="Upload an image for diagnosis")
209
+
210
+ async def respond(chat_input, history):
211
+ files = chat_input["files"]
212
+ if files:
213
+ file_path = files[0]
214
+ # Directly call the diagnosis tool for testing
215
+ diagnosis = DIAGNOSIS_TOOL(file_path)
216
+ bot_message = f"**Diagnosis:** {diagnosis}"
217
+ history.append([(file_path,), bot_message])
218
+ else:
219
+ bot_message = "Please upload an image for diagnosis."
220
+ history.append([chat_input["text"], bot_message])
221
+
222
+ return history, gr.MultimodalTextbox(value=None)
223
+
224
+ msg.submit(respond, [msg, chatbot], [chatbot, msg])
225
+
226
+ return demo
227
+
228
+ # --- Step 4: App Launcher ---
229
+
230
+ def check_internet_connection(host="8.8.8.8", port=53, timeout=3):
231
+ """Check for internet connectivity."""
232
+ try:
233
+ socket.setdefaulttimeout(timeout)
234
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
235
+ return True
236
+ except socket.error:
237
+ return False
238
+
239
+ if __name__ == "__main__":
240
+ if check_internet_connection() and ADK_RUNNER:
241
+ print("βœ… Internet connection detected. Launching Connected Mode.")
242
+ ui = create_connected_mode_ui()
243
+ else:
244
+ print("❌ No internet connection or ADK setup failed. Launching Field Mode (Offline).")
245
+ ui = create_field_mode_ui()
246
+
247
+ ui.launch(share=True, debug=True)