Spaces:
Sleeping
Sleeping
Commit
Β·
b5b714b
1
Parent(s):
2a632a7
s2
Browse files
app.py
CHANGED
|
@@ -1,247 +1,236 @@
|
|
| 1 |
# ==============================================================================
|
| 2 |
-
#
|
| 3 |
# ==============================================================================
|
| 4 |
"""
|
| 5 |
-
This script launches
|
| 6 |
-
|
| 7 |
-
1.
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 36 |
-
from
|
| 37 |
-
from
|
| 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
|
| 46 |
# This expensive setup runs only ONCE when the application starts.
|
| 47 |
|
| 48 |
-
print("Performing initial setup for Vision Model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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=
|
| 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(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
-
|
|
|
|
|
|
|
| 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
|
| 73 |
## Nitrogen Deficiency
|
| 74 |
-
**Symptoms:** General yellowing (chlorosis) of the plant, starting with the lower, older leaves in a distinct "V" shape
|
| 75 |
-
**Cause:** Insufficient nitrogen in the soil
|
| 76 |
-
**Local Remedy:** Side-dress with a nitrogen-rich fertilizer like urea
|
| 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 |
-
**
|
| 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
|
| 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
|
| 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 |
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 |
-
|
|
|
|
|
|
|
|
|
|
| 98 |
print("β
RAG knowledge base and retriever created successfully!")
|
| 99 |
|
| 100 |
except Exception as e:
|
| 101 |
-
print(f"β CRITICAL ERROR during
|
| 102 |
-
pass
|
| 103 |
|
| 104 |
-
# --- Step
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
DIAGNOSIS_TOOL = None
|
| 108 |
-
REMEDY_TOOL = None
|
| 109 |
-
ADK_AGENT = None
|
| 110 |
-
ADK_RUNNER = None
|
| 111 |
-
SESSION_SERVICE = None
|
| 112 |
|
|
|
|
| 113 |
try:
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 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("
|
| 131 |
-
|
| 132 |
-
except Exception as e:
|
| 133 |
-
print(f"β CRITICAL ERROR during ADK setup: {e}")
|
| 134 |
-
pass
|
| 135 |
|
| 136 |
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
-
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
uploaded_image.save(temp_file.name)
|
| 152 |
-
temp_file_path = temp_file.name
|
| 153 |
|
| 154 |
-
|
| 155 |
-
|
|
|
|
| 156 |
|
| 157 |
-
|
| 158 |
-
return f"Sorry, I couldn't identify the condition from the image. Raw output: {diagnosis}"
|
| 159 |
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
-
**Condition Identified:**
|
| 166 |
-
### {diagnosis}
|
| 167 |
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
print("Workflow complete. Returning response.")
|
| 175 |
-
return final_response
|
| 176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
except Exception as e:
|
| 178 |
-
print(f"
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 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 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 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)
|