commit 5ddc091ae308926009ecaba14e88cb5e28589525
parent 392940139192e3502a0576d7bdb70132c6b5009d
Author: maydayv7 <maydayv7@gmail.com>
Date: Tue, 10 Mar 2026 22:49:46 +0530
Cleanup
Diffstat:
21 files changed, 132 insertions(+), 597 deletions(-)
diff --git a/Qdrant/Client.py b/Qdrant/Client.py
@@ -1,9 +0,0 @@
-from qdrant_client import QdrantClient
-
-client = QdrantClient(
- url="https://2a9e6ab0-e572-4bfa-a50f-0a169f9753d3.europe-west3-0.gcp.cloud.qdrant.io:6333",
- api_key="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3MiOiJtIn0.RG2XaX6thvBqI6TCtUrFg8znHYbMuFGOvbxoxPgT020",
- timeout=120
-)
-
-# print(qdrant_client.get_collections())
-\ No newline at end of file
diff --git a/Qdrant/Setup.py b/Qdrant/Setup.py
@@ -1,17 +0,0 @@
-from qdrant_client import models
-from Qdrant.Client import client # <--- FIXED IMPORT
-
-VECTOR_SIZE = 516
-COLLECTION_NAME = "Farm_Memory"
-
-# client = QdrantClient(url="http://localhost:6333")
-
-client.recreate_collection(
- collection_name=COLLECTION_NAME,
- vectors_config=models.VectorParams(
- size=VECTOR_SIZE,
- distance=models.Distance.COSINE
- )
-)
-
-print("Collection created:", COLLECTION_NAME)
-\ No newline at end of file
diff --git a/Qdrant/Store.py b/Qdrant/Store.py
@@ -1,20 +0,0 @@
-from Qdrant.Client import client # <--- FIXED IMPORT
-from qdrant_client.models import PointStruct
-
-COLLECTION_NAME = "Farm_Memory"
-
-# client = QdrantClient(url="http://localhost:6333")
-
-def store_fmu(fmu):
- point = PointStruct(
- id=fmu.id,
- vector=fmu.vector,
- payload=fmu.metadata
- )
-
- client.upsert(
- collection_name=COLLECTION_NAME,
- points=[point]
- )
-
- print("Stored FMU:", fmu.id)
-\ No newline at end of file
diff --git a/Sentinel/Encoders/TimeSeries.py b/Sentinel/Encoders/TimeSeries.py
@@ -1,21 +0,0 @@
-# encoders/sensor_encoder.py
-import numpy as np
-
-class SensorEncoder:
- def encode(self, sensors: dict):
- """
- sensors = {
- "pH": 5.9,
- "EC": 1.3,
- "temp": 25.0,
- "humidity": 72.0
- }
- """
- vec = np.array(list(sensors.values()), dtype=np.float32)
-
- # Normalize roughly into 0–1 range (hackathon-safe)
- min_vals = np.array([4.0, 0.5, 10.0, 30.0])
- max_vals = np.array([7.0, 3.0, 40.0, 100.0])
-
- norm_vec = (vec - min_vals) / (max_vals - min_vals)
- return np.clip(norm_vec, 0.0, 1.0)
diff --git a/Sentinel/Encoders/Vision.py b/Sentinel/Encoders/Vision.py
@@ -1,102 +0,0 @@
-# encoders/clip_encoder.py
-import torch
-import clip
-from PIL import Image
-import io
-import base64
-from pathlib import Path
-
-class VisionEncoder:
- def __init__(self, model_name="ViT-B/32"):
- self.device = "cuda" if torch.cuda.is_available() else "cpu"
- self.model, self.preprocess = clip.load(model_name, device=self.device)
- self.model.eval()
-
- def encode(self, image_input):
- """
- Encode an image from multiple input types:
- - File path (str or Path)
- - Base64 string
- - BytesIO object
- - PIL Image object
-
- Args:
- image_input: File path, base64 string, BytesIO, or PIL Image
-
- Returns:
- numpy array: Normalized image embedding vector
- """
- # Convert input to PIL Image
- pil_image = self._to_pil_image(image_input)
-
- # Preprocess and encode
- image = self.preprocess(pil_image.convert("RGB")) \
- .unsqueeze(0).to(self.device)
-
- with torch.no_grad():
- vec = self.model.encode_image(image)
- vec = vec / vec.norm(dim=-1, keepdim=True)
-
- return vec.cpu().numpy().flatten()
-
- def _to_pil_image(self, image_input):
- """
- Convert various input types to PIL Image.
- """
- # If already a PIL Image
- if isinstance(image_input, Image.Image):
- return image_input
-
- # If BytesIO object
- if isinstance(image_input, io.BytesIO):
- image_input.seek(0) # Reset to beginning
- return Image.open(image_input)
-
- # If it's a string, determine if it's a path or base64
- if isinstance(image_input, (str, Path)):
- # Check if it's a file path
- if isinstance(image_input, Path) or Path(image_input).exists():
- return Image.open(image_input)
-
- # Otherwise, treat as base64
- return self._base64_to_pil(image_input)
-
- # If bytes object
- if isinstance(image_input, bytes):
- return Image.open(io.BytesIO(image_input))
-
- raise TypeError(f"Unsupported image input type: {type(image_input)}")
-
- def _base64_to_pil(self, base64_string):
- """
- Convert base64 string to PIL Image.
- """
- # Remove header if present (e.g., "data:image/png;base64,...")
- if "," in base64_string:
- base64_string = base64_string.split(",")[1]
-
- # Add padding if necessary
- missing_padding = len(base64_string) % 4
- if missing_padding:
- base64_string += '=' * (4 - missing_padding)
-
- # Decode and open
- image_bytes = base64.b64decode(base64_string)
- return Image.open(io.BytesIO(image_bytes))
-
-
-# Example usage:
-if __name__ == "__main__":
- encoder = VisionEncoder()
-
- # Test with file path
- # vec1 = encoder.encode("path/to/image.jpg")
-
- # Test with base64
- sample_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII="
- vec2 = encoder.encode(sample_base64)
- print(f"✅ Encoded base64 image. Vector shape: {vec2.shape}")
-
- # Test with BytesIO
- # image_stream = io.BytesIO(image_bytes)
- # vec3 = encoder.encode(image_stream)
-\ No newline at end of file
diff --git a/Sentinel/Encoders/__init__.py b/Sentinel/Encoders/__init__.py
diff --git a/Sentinel/Sample.png b/Sentinel/Sample.png
Binary files differ.
diff --git a/Sentinel/Test.py b/Sentinel/Test.py
@@ -1,24 +0,0 @@
-# sentinel/test_sentinel.py
-from agent import SentinelAgent
-
-agent = SentinelAgent()
-
-sensor_window = {
- "pH": [5.8, 5.9, 6.0],
- "EC": [1.2, 1.3, 1.25],
- "temp": [24, 25, 24.5],
- "humidity": [70, 72, 71]
-}
-
-metadata = {
- "crop": "lettuce",
- "stage": "vegetative",
- "rack": "A3"
-}
-
-fmu = agent.create_fmu("sample_plant.jpg", sensor_window, metadata)
-
-print("FMU ID:", fmu.id)
-print("Vector length:", len(fmu.vector))
-print("Quality:", fmu.quality)
-print("Metadata:", fmu.metadata)
diff --git a/Sentinel/__init__.py b/Sentinel/__init__.py
diff --git a/Sentinel/agent.py b/Sentinel/agent.py
@@ -1,131 +0,0 @@
-import uuid
-import base64
-import io
-from datetime import datetime
-import numpy as np
-from pathlib import Path
-
-# Ensure these imports match your project structure
-from Sentinel.Encoders.Vision import VisionEncoder
-from Sentinel.Encoders.TimeSeries import SensorEncoder
-from Sentinel.fmu import FMU
-from Qdrant.Store import store_fmu
-
-class FMUBuilder:
- def __init__(self):
- self.vision = VisionEncoder()
- self.sensors = SensorEncoder()
-
- def create_fmu(self, image_input, sensor_data, metadata=None):
- """
- Creates an FMU from either:
- - A file path (str/Path)
- - A Base64 encoded image string
-
- Args:
- image_input: Either a file path string or base64 string
- sensor_data: Dictionary of sensor readings
- metadata: Optional metadata dictionary
- """
-
- # Detect if input is base64 or file path
- if self._is_base64(image_input):
- # Handle Base64 input
- img_vec = self._encode_from_base64(image_input)
- else:
- # Handle file path input (original behavior)
- img_vec = self.vision.encode(image_input)
-
- # Encode sensor data
- sensor_vec = self.sensors.encode(sensor_data)
-
- # Combine vectors
- fmu_vector = np.concatenate([img_vec, sensor_vec]).tolist()
-
- return FMU(
- id=str(uuid.uuid4()),
- vector=fmu_vector,
- metadata={
- **(metadata or {}),
- "timestamp": datetime.utcnow().isoformat(),
- }
- )
-
- def _is_base64(self, s):
- """
- Detect if string is base64 or a file path.
- Returns True if it looks like base64, False if it looks like a path.
- """
- if not isinstance(s, str):
- return False
-
- # If it has path separators, it's probably a path
- if '/' in s or '\\' in s or Path(s).exists():
- return False
-
- # If it has base64 header, it's definitely base64
- if s.startswith('data:image'):
- return True
-
- # Check if it's valid base64 (after removing potential header)
- test_str = s.split(',')[-1] if ',' in s else s
-
- # Base64 strings are typically very long and only contain valid b64 chars
- if len(test_str) > 100: # Arbitrary threshold
- try:
- base64.b64decode(test_str, validate=True)
- return True
- except Exception:
- return False
-
- return False
-
- def _encode_from_base64(self, image_base64):
- """
- Decode base64 string and encode the image.
- """
- # Remove header if present (e.g., "data:image/png;base64,...")
- if "," in image_base64:
- image_base64 = image_base64.split(",")[1]
-
- # Add padding if necessary (fix the "multiple of 4" error)
- missing_padding = len(image_base64) % 4
- if missing_padding:
- image_base64 += '=' * (4 - missing_padding)
-
- # Decode to bytes
- image_bytes = base64.b64decode(image_base64)
-
- # Create file-like object
- image_stream = io.BytesIO(image_bytes)
-
- # Encode using VisionEncoder
- # If VisionEncoder only accepts paths, you may need to update it
- # to also accept BytesIO objects or PIL Images
- return self.vision.encode(image_stream)
-
-
-if __name__ == "__main__":
- builder = FMUBuilder()
-
- sensors = {
- "pH": 5.9,
- "EC": 1.3,
- "temp": 25.0,
- "humidity": 72.0
- }
-
- # Test with base64
- sample_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII="
-
- fmu = builder.create_fmu(sample_base64, sensors, {
- "crop": "lettuce",
- "stage": "vegetative"
- })
-
- print("✅ FMU ID:", fmu.id)
- print("✅ Vector length:", len(fmu.vector))
- print("✅ Metadata:", fmu.metadata)
-
- # Test with file path
- # fmu2 = builder.create_fmu("path/to/image.png", sensors, {"crop": "basil"})
-\ No newline at end of file
diff --git a/Sentinel/fmu.py b/Sentinel/fmu.py
@@ -1,9 +0,0 @@
-# fmu.py
-from dataclasses import dataclass
-from typing import Dict, Any, List
-
-@dataclass
-class FMU:
- id: str
- vector: List[float]
- metadata: Dict[str, Any]
diff --git a/agent/sub_agents/Explainer.py b/agent/sub_agents/Explainer.py
@@ -1,4 +1,6 @@
import json
+from langchain_core.messages import SystemMessage, HumanMessage
+
class ExplainerAgent:
def __init__(self, llm_client):
@@ -8,7 +10,7 @@ class ExplainerAgent:
"""
Generates a detailed, human-readable log of the decision process.
"""
-
+
# Construct the context for the LLM
context = f"""
CONTEXT DATA:
@@ -35,14 +37,11 @@ class ExplainerAgent:
"""
try:
- response = self.llm.chat.completions.create(
- model="qwen/qwen3-32b",
- messages=[
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": context}
- ],
- temperature=0.3 # Keep it factual
- )
- return response.choices[0].message.content
+ messages = [
+ SystemMessage(content=system_prompt),
+ HumanMessage(content=context),
+ ]
+ response = self.llm.invoke(messages)
+ return response.content
except Exception as e:
- return f"Explanation unavailable: {str(e)}"
-\ No newline at end of file
+ return f"Explanation unavailable: {str(e)}"
diff --git a/backend/server/functions.py b/backend/server/functions.py
@@ -257,7 +257,7 @@ async def process_search(file: UploadFile, sensors_str: str, builder):
"new_fmu_id": query_fmu.id,
"agent_decision": final_decision_json,
"explanation": explanation_log,
- "search_results": [{"id": p.id, "payload": p.payload} for p in points_list],
+ "search_results": [{"id": p.id, "score": p.score, "payload": p.payload} for p in points_list],
}
except Exception as e:
diff --git a/frontend/src/pages/AgentControl.jsx b/frontend/src/pages/AgentControl.jsx
@@ -22,7 +22,7 @@ import {
Brain,
} from "lucide-react";
import { agentService } from "../api/agentApi";
-import { extractSensors } from "../utils/dataUtils";
+import { extractSensors, formatOutcome } from "../utils/dataUtils";
export default function AgentControl() {
const [file, setFile] = useState(null);
@@ -650,14 +650,11 @@ export default function AgentControl() {
{/* Optional Outcome Section */}
{res.payload.outcome && (
- <div className="mt-2 text-xs bg-gray-50 p-2 rounded border border-gray-100 text-gray-600 line-clamp-2">
+ <div className="mt-2 text-xs bg-gray-50 p-2 rounded border border-gray-100 text-gray-600 line-clamp-3">
<span className="font-bold text-gray-400 uppercase text-[10px] block mb-1">
Outcome Note:
</span>
- {/* Simple cleanup of outcome text */}
- {res.payload.outcome
- .replace("condition_assessed", "")
- .replace("|", " • ")}
+ {formatOutcome(res.payload.outcome)}
</div>
)}
</div>
diff --git a/frontend/src/pages/CropDetails.jsx b/frontend/src/pages/CropDetails.jsx
@@ -5,7 +5,6 @@ import {
ArrowLeft,
Thermometer,
Droplet,
- Sun,
FlaskConical,
Sparkles,
} from "lucide-react";
@@ -22,6 +21,7 @@ import {
extractSensors,
parsePythonString,
formatNumber,
+ formatOutcome,
} from "../utils/dataUtils";
const CropDetails = () => {
@@ -89,7 +89,6 @@ const CropDetails = () => {
const latestSensors = latest.cleanSensors || {
temp: 0,
ph: 0,
- lux: 0,
humidity: 0,
};
@@ -128,13 +127,6 @@ const CropDetails = () => {
icon: <Droplet size={18} className="text-blue-500" />,
color: "bg-blue-100",
},
- {
- label: "Light",
- value: `${formatNumber(latestSensors.lux)}`,
- status: "Optimal",
- icon: <Sun size={18} className="text-yellow-500" />,
- color: "bg-yellow-100",
- },
];
return (
@@ -182,7 +174,7 @@ const CropDetails = () => {
</div>
</div>
- <div className="lg:col-span-2 bg-white rounded-2xl p-5 shadow-sm border border-gray-100 grid grid-cols-2 md:grid-cols-4 gap-4">
+ <div className="lg:col-span-2 bg-white rounded-2xl p-5 shadow-sm border border-gray-100 grid grid-cols-1 md:grid-cols-3 gap-4">
{vitals.map((v, i) => (
<div
key={i}
@@ -218,7 +210,7 @@ const CropDetails = () => {
<div className="text-sm text-gray-600 leading-relaxed">
<p className="mb-2">
<strong>Observation:</strong>{" "}
- {latestPayload.outcome || "Monitoring..."}
+ {formatOutcome(latestPayload.outcome)}
</p>
<p className="font-bold text-xs text-gray-400 uppercase tracking-wide mb-1">
@@ -348,7 +340,7 @@ const CropDetails = () => {
: h.payload?.action_taken || "Routine Check"}
</div>
<div className="text-xs text-gray-500 mt-1">
- {h.payload?.outcome || "Monitoring"}
+ {formatOutcome(h.payload?.outcome)}
</div>
</div>
</div>
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
@@ -8,12 +8,8 @@ import {
Bell,
Settings,
Droplet,
- Sun,
Leaf,
- Search,
- Database,
Thermometer,
- LogOut,
Brain,
} from "lucide-react";
@@ -47,7 +43,6 @@ const Dashboard = () => {
maturity: calculateMaturity(p.sequence_number),
daysLeft: 30 - (p.sequence_number || 0),
sensors: {
- lux: `${sensors.lux}k`,
temp: `${sensors.temp}°C`,
ph: sensors.ph,
},
@@ -78,11 +73,6 @@ const Dashboard = () => {
return "https://images.unsplash.com/photo-1622206151226-18ca2c9ab4a1?q=80&w=2000";
};
- const calculateMaturity = (seq) => {
- const val = (seq || 1) * 10;
- return val > 100 ? 100 : val;
- };
-
return (
<div className="flex h-screen bg-[#F4F9F6] font-sans text-gray-800">
{/* SIDEBAR */}
@@ -108,11 +98,11 @@ const Dashboard = () => {
<div className="p-4 border-t border-gray-50">
<div className="flex items-center gap-3 p-2 rounded-xl">
<div className="w-10 h-10 rounded-full bg-orange-100 flex items-center justify-center text-orange-600 font-bold">
- AF
+ RR
</div>
<div className="flex-1">
- <h4 className="text-sm font-bold text-gray-900">Alex Farmer</h4>
- <p className="text-xs text-gray-500">Head Agronomist</p>
+ <h4 className="text-sm font-bold text-gray-900">Rajesh Rai</h4>
+ <p className="text-xs text-gray-500">Owner</p>
</div>
</div>
</div>
@@ -216,12 +206,7 @@ const CropCard = ({ data }) => {
></div>
</div>
</div>
- <div className="grid grid-cols-3 gap-2 pt-2 border-t border-gray-50">
- <SensorItem
- icon={<Sun size={14} />}
- value={data.sensors.lux}
- label="Lux"
- />
+ <div className="grid grid-cols-2 gap-4 pt-2 border-t border-gray-50">
<SensorItem
icon={<Thermometer size={14} />}
value={data.sensors.temp}
diff --git a/frontend/src/pages/LandingPage.jsx b/frontend/src/pages/LandingPage.jsx
@@ -7,7 +7,6 @@ import {
Zap,
Droplet,
Cpu,
- Building2,
Activity,
Rocket,
BrainCircuit,
@@ -160,7 +159,7 @@ const LandingPage = () => {
{/* --- FOOTER --- */}
<footer className="relative z-10 flex-none w-full text-center py-4 text-gray-500 text-xs">
- © 2024 Demeter AI Systems. Revolutionizing Hydroponics.
+ © 2026 Demeter AI Systems. Revolutionizing Hydroponics.
</footer>
</div>
);
diff --git a/frontend/src/utils/dataUtils.js b/frontend/src/utils/dataUtils.js
@@ -5,8 +5,8 @@ export const formatNumber = (val) => {
export const parsePythonString = (str) => {
if (!str) return null;
- if (typeof str === 'object') return str;
-
+ if (typeof str === "object") return str;
+
try {
return JSON.parse(str);
} catch (e) {
@@ -14,9 +14,9 @@ export const parsePythonString = (str) => {
// Fix Python single quotes and Booleans
const fixedStr = str
.replace(/'/g, '"')
- .replace(/\bNone\b/g, 'null')
- .replace(/\bFalse\b/g, 'false')
- .replace(/\bTrue\b/g, 'true');
+ .replace(/\bNone\b/g, "null")
+ .replace(/\bFalse\b/g, "false")
+ .replace(/\bTrue\b/g, "true");
return JSON.parse(fixedStr);
} catch (e2) {
return null;
@@ -25,7 +25,7 @@ export const parsePythonString = (str) => {
};
export const extractSensors = (payload) => {
- if (!payload) return { temp: 0, ph: 0, lux: 0, humidity: 0, ec: 0 };
+ if (!payload) return { temp: 0, ph: 0, humidity: 0, ec: 0 };
let rawSensors = payload.sensors || payload.sensor_data;
@@ -34,11 +34,12 @@ export const extractSensors = (payload) => {
const actionData = parsePythonString(payload.action_taken);
if (actionData) {
rawSensors = {
- temp: actionData.atmospheric_actions?.air_temp ?? actionData.air_temp ?? 0,
+ temp:
+ actionData.atmospheric_actions?.air_temp ?? actionData.air_temp ?? 0,
ph: actionData.water_actions?.ph ?? actionData.ph ?? 0,
- lux: actionData.atmospheric_actions?.light_intensity ?? actionData.light_intensity ?? 0,
- humidity: actionData.atmospheric_actions?.humidity ?? actionData.humidity ?? 0,
- ec: actionData.water_actions?.ec ?? actionData.ec ?? 0
+ humidity:
+ actionData.atmospheric_actions?.humidity ?? actionData.humidity ?? 0,
+ ec: actionData.water_actions?.ec ?? actionData.ec ?? 0,
};
} else {
rawSensors = {};
@@ -49,7 +50,6 @@ export const extractSensors = (payload) => {
return {
temp: formatNumber(rawSensors.temp ?? rawSensors.air_temp ?? 0),
ph: formatNumber(rawSensors.pH ?? rawSensors.ph ?? 7.0),
- lux: formatNumber(rawSensors.lux ?? rawSensors.light ?? rawSensors.light_intensity ?? 0),
humidity: formatNumber(rawSensors.humidity ?? 0),
ec: formatNumber(rawSensors.EC ?? rawSensors.ec ?? 0),
};
@@ -59,3 +59,35 @@ export const calculateMaturity = (seq) => {
const val = (seq || 1) * 10;
return val > 100 ? 100 : val;
};
+
+export const formatOutcome = (outcome) => {
+ if (!outcome || typeof outcome !== "string") return "Monitoring...";
+
+ const parts = outcome.split("|").map((p) => p.trim());
+ let tags = [];
+ let notes = "";
+
+ parts.forEach((part) => {
+ if (part.startsWith("condition_assessed")) {
+ const val = part.replace("condition_assessed", "").trim();
+ if (val) tags.push(`Condition: ${val}`);
+ } else if (part.startsWith("health_score:")) {
+ const val = part.replace("health_score:", "").trim();
+ if (val) tags.push(`Health Score: ${val}`);
+ } else if (part.startsWith("notes:")) {
+ notes = part.replace("notes:", "").trim();
+ } else if (part) {
+ tags.push(part);
+ }
+ });
+
+ if (tags.length === 0 && !notes) {
+ return outcome;
+ }
+
+ const tagsStr = tags.join(" • ");
+ if (tagsStr && notes) {
+ return `${tagsStr} - ${notes}`;
+ }
+ return tagsStr || notes;
+};
diff --git a/readme.md b/readme.md
@@ -12,15 +12,15 @@
**Industrial-grade Multi-Agent System for autonomous hydroponic farming through AI-driven reasoning**
-[📖 Documentation](https://drive.google.com/file/d/1VAN31mXPaQ7r4Fm8dpzjhgGeeQwvlH-Z/view?usp=drive_link) • [🚀 Quick Start](#-quick-start) • [🔧 API Reference](#-api-reference) • [🤝 Contributing](#-contributing)
+[📖 Documentation](https://drive.google.com/file/d/1VAN31mXPaQ7r4Fm8dpzjhgGeeQwvlH-Z/view?usp=drive_link) • [🚀 Quick Start](#-quick-start) • [🔧 API Reference](#-api-reference)
</div>
<div align="center">
|  |  |  |
-|:---:|:---:|:---:|
-| **System Overview**<br/>Real-time monitoring dashboard | **Agent Control**<br/>Multi-agent orchestration | **Console Log**<br/>AI agent decision logs |
+| :---------------------------------------------------------------: | :-----------------------------------------------------------: | :-------------------------------------------------------: |
+| **System Overview**<br/>Real-time monitoring dashboard | **Agent Control**<br/>Multi-agent orchestration | **Console Log**<br/>AI agent decision logs |
</div>
@@ -40,7 +40,7 @@ The system combines **Long-Term Memory**, **Computer Vision**, and **Reinforceme
### 🎯 Key Capabilities
- **🧠 Cognitive Decision Making**: AI agents that reason like human experts
-- **🔍 Real-time Disease Detection**: YOLOv8-powered visual diagnosis
+- **🔍 Real-time Disease Detection**: Azure Custom Vision-powered visual diagnosis
- **📚 Scientific Knowledge Base**: RAG-enabled agricultural research integration
- **🎮 Adaptive Learning**: Reinforcement learning that improves over time
- **🌐 Live Data Integration**: Autonomous web search for current conditions
@@ -60,41 +60,46 @@ Demeter operates on a **Hierarchical Control Loop** powered by **LangGraph**, fe
### 🤖 Agent Roles
-| Agent | Role | Technology | Purpose |
-|-------|------|------------|---------|
-| **Supervisor** | Executive | Contextual Bandit RL | Strategic decision making & safety validation |
-| **Researcher** | Scholar | RAG + Web Search | Scientific consultation & live data retrieval |
-| **Judge** | Auditor | CV + Analytics | Performance evaluation & RL training |
-| **Atmospheric** | Specialist | Physics Engine | VPD, CO2, light optimization |
-| **Water** | Specialist | Chemistry Engine | pH, EC, nutrient balancing |
-| **Doctor** | Diagnostician | YOLOv8 + CLIP | Disease detection & visual analysis |
-| **Historian** | Memory | Mem0 + Qdrant | Long-term plant biography & context |
+| Agent | Role | Technology | Purpose |
+| --------------- | ------------- | -------------------- | --------------------------------------------- |
+| **Supervisor** | Executive | Contextual Bandit RL | Strategic decision making & safety validation |
+| **Researcher** | Scholar | RAG + Web Search | Scientific consultation & live data retrieval |
+| **Judge** | Auditor | CV + Analytics | Performance evaluation & RL training |
+| **Atmospheric** | Specialist | Physics Engine | VPD, CO2, light optimization |
+| **Water** | Specialist | Chemistry Engine | pH, EC, nutrient balancing |
+| **Doctor** | Diagnostician | Azure Custom Vision | Disease detection & visual analysis |
+| **Historian** | Memory | Mem0 + Qdrant | Long-term plant biography & context |
---
## ✨ Key Features
### ⚡ Self-Correcting Reasoning
-- **Digital Twin Simulation**: Predicts action consequences before execution
+
+- **Digital Twin Simulation**: Predicts action consequences before execution, powered by Azure Digital Twin
- **Safety Interlocks**: Prevents harmful actions through multi-layer validation
- **Rollback Capabilities**: Can reverse unsafe decisions
### 🔍 RAG-Powered Knowledge Base
+
- **Scientific Literature**: Indexes agricultural research papers and best practices
- **Contextual Retrieval**: Retrieves relevant information for current conditions
- **Hallucination Prevention**: All decisions grounded in verified sources
### 🎯 Reinforcement Learning Optimization
+
- **Contextual Bandit Algorithm**: Learns optimal strategies over time
- **Adaptive Decision Making**: Improves performance based on outcomes
- **Strategy Evolution**: Discovers better approaches through trial and feedback
### 👁️ Advanced Computer Vision
+
- **Real-time Disease Detection**: Identifies pathogens before symptoms appear
- **Growth Stage Analysis**: Monitors plant development and health indicators
- **Automated Documentation**: Creates visual records of plant conditions
### 🌐 Autonomous Intelligence
+
- **Live Web Search**: Fetches current weather, market data, and research
- **Dynamic Knowledge Updates**: Integrates new information without redeployment
- **Environmental Adaptation**: Adjusts to local conditions and climate changes
@@ -104,6 +109,7 @@ Demeter operates on a **Hierarchical Control Loop** powered by **LangGraph**, fe
## 🛠️ Technology Stack
### Backend (AI Brain)
+
```python
# Core Framework
- FastAPI 0.109+ # High-performance async API
@@ -114,18 +120,20 @@ Demeter operates on a **Hierarchical Control Loop** powered by **LangGraph**, fe
- LangGraph 0.0.26+ # Multi-agent workflow management
# AI Models
+- Azure Custom Vision # Object detection for disease identification
- Llama-3.3-70b (Groq) # Primary LLM for reasoning
- OpenAI GPT-4o # Fallback LLM option
-- YOLOv8 (Ultralytics) # Object detection for disease identification
- CLIP (OpenAI) # Vision-language understanding
# Data & Memory
+- Azure Digital Twin # Digital farm simulation
- Qdrant # Vector database for RAG and embeddings
- Mem0 # Semantic long-term memory
- FastEmbed # Local embedding generation
```
### Frontend (User Interface)
+
```javascript
- React 19+ # Modern UI framework
- React Router 7+ # Client-side routing
@@ -135,6 +143,7 @@ Demeter operates on a **Hierarchical Control Loop** powered by **LangGraph**, fe
```
### Infrastructure
+
- **Database**: Qdrant (Vector Search)
- **Deployment**: Docker containers
- **Monitoring**: Built-in logging and health checks
@@ -155,18 +164,19 @@ Before installing Demeter, ensure you have:
### Required API Keys
-| Service | Environment Variable | Where to Get |
-|---------|---------------------|--------------|
-| **Groq** | `GROQ_API_KEY` | [console.groq.com/keys](https://console.groq.com/keys) |
-| **Qdrant** | `QDRANT_URL` & `QDRANT_API_KEY` | [cloud.qdrant.io](https://cloud.qdrant.io) |
-| **SerpAPI** | `SERPAPI_API_KEY` | [serpapi.com](https://serpapi.com) (optional) |
-| **OpenAI** | `OPENAI_API_KEY` | [platform.openai.com](https://platform.openai.com) (optional) |
+| Service | Environment Variable | Where to Get |
+| ----------- | ------------------------------- | --------------------------------------------------------------------- |
+| **Groq** | `GROQ_API_KEY` | [console.groq.com/keys](https://console.groq.com/keys) |
+| **Qdrant** | `QDRANT_URL` & `QDRANT_API_KEY` | [cloud.qdrant.io](https://cloud.qdrant.io) |
+| **SerpAPI** | `SERPAPI_API_KEY` | [serpapi.com](https://serpapi.com) (optional) |
+| **OpenAI** | `OPENAI_API_KEY` | [platform.openai.com](https://platform.openai.com) (optional) |
+| **Azure** | See list below | [Azure for Students](https://azure.microsoft.com/en-us/free/students) |
### 1. Clone and Setup
```bash
# Clone the repository
-git clone https://github.com/your-username/demeter.git
+git clone https://github.com/maydayv7/demeter.git
cd demeter
# Create virtual environment
@@ -182,6 +192,15 @@ pip install -r requirements.txt
Create a `.env` file in the project root:
```env
+# Required: Azure Services
+AZURE_API_KEY=your_azure_key_here
+AZURE_ENDPOINT=https://msaiunlockedcustomvision-prediction.cognitiveservices.azure.com/
+AZURE_PROJECT_ID=your_azure_project_id_here
+DATASET_FOLDER=your_dataset_here
+AZURE_PREDICTION_KEY=your_azure_predict_key_here
+AZURE_URL=your_azure_url_here
+AZURE_ITERATION_NAME=DemeterDoctor-v1
+
# Required: AI Provider
GROQ_API_KEY=gsk_your_key_here
@@ -197,11 +216,13 @@ SERPAPI_API_KEY=your_serpapi_key_here
### 3. Start Qdrant Database
**Option A: Local Docker (Recommended for development)**
+
```bash
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
```
**Option B: Cloud Qdrant**
+
- Sign up at [cloud.qdrant.io](https://cloud.qdrant.io)
- Create a cluster and update your `.env` with the provided URL and API key
@@ -220,19 +241,18 @@ python agent/main_agent.py
# In another terminal, start the API server
python backend/server/main.py
+
+# And in yet another, start the website backend
+cd backend/node_server
+node index.js
```
### 6. Start the Frontend
```bash
-# Navigate to frontend directory
cd frontend
-
-# Install dependencies
npm install
-
-# Start development server
-npm start
+npm run start
```
### 7. Access the Application
@@ -252,7 +272,7 @@ npm start
```
demeter/
-├── agent/ # AI Agent System
+├── agent/ # AI Agent System
│ ├── main_agent.py # Main orchestrator
│ ├── sub_agents/ # Specialized agents
│ │ ├── Supervisor.py # Executive decision maker
@@ -270,16 +290,14 @@ demeter/
│ │ ├── main.py # FastAPI application
│ │ ├── functions.py # Business logic
│ │ └── rag_brain.py # AI integration
-│ └── node_server/ # Additional API endpoints
+│ └── node_server/ # Additional API endpoints
├── frontend/ # React Application
│ ├── src/
│ │ ├── components/ # UI components
│ │ ├── pages/ # Application pages
│ │ └── api/ # API integration
│ └── public/ # Static assets
-├── web/ # Next.js Interface (Alternative)
├── requirements.txt # Python dependencies
-├── setup.md # Detailed setup guide
└── README.md # This file
```
@@ -289,14 +307,14 @@ demeter/
### Core Endpoints
-| Method | Endpoint | Description |
-|--------|----------|-------------|
-| `GET` | `/health` | System health check |
-| `GET` | `/api/farms` | List all farms |
-| `POST` | `/api/farms` | Create new farm |
-| `GET` | `/api/farms/{id}` | Get farm details |
-| `POST` | `/api/agents/action` | Trigger agent action |
-| `GET` | `/api/memory/{plant_id}` | Get plant history |
+| Method | Endpoint | Description |
+| ------ | ------------------------ | -------------------- |
+| `GET` | `/health` | System health check |
+| `GET` | `/api/farms` | List all farms |
+| `POST` | `/api/farms` | Create new farm |
+| `GET` | `/api/farms/{id}` | Get farm details |
+| `POST` | `/api/agents/action` | Trigger agent action |
+| `GET` | `/api/memory/{plant_id}` | Get plant history |
### Agent Control
@@ -317,11 +335,11 @@ curl http://localhost:8000/api/memory/plant_123
```javascript
// Connect to real-time updates
-const ws = new WebSocket('ws://localhost:8000/ws/farm-updates');
+const ws = new WebSocket("ws://localhost:8000/ws/farm-updates");
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
- console.log('Farm update:', data);
+ console.log("Farm update:", data);
};
```
@@ -345,21 +363,24 @@ tail -f logs/demeter.log
### Common Issues
**Q: Agents not responding**
+
- Check Qdrant connection: `curl http://localhost:6333/health`
- Verify API keys in `.env`
- Ensure virtual environment is activated
**Q: Memory not persisting**
+
- Check Qdrant collections: Access Qdrant dashboard
- Verify embedding model is loaded
- Check disk space and permissions
**Q: Vision analysis failing**
-- Ensure YOLOv8 model is downloaded
+
- Check camera/image permissions
- Verify OpenCV installation
**Q: Web search not working**
+
- Validate SerpAPI key
- Check internet connectivity
- Review API quota limits
@@ -379,91 +400,6 @@ VISION_CONFIDENCE_THRESHOLD = 0.7
---
-## 🚀 Deployment
-
-### Docker Deployment
-
-```dockerfile
-# Build production image
-docker build -t demeter:latest .
-
-# Run with environment variables
-docker run -p 8000:8000 \
- -e GROQ_API_KEY=your_key \
- -e QDRANT_URL=your_qdrant_url \
- demeter:latest
-```
-
-### Cloud Deployment
-
-**Recommended Stack:**
-- **Backend**: Railway, Render, or AWS ECS
-- **Database**: Qdrant Cloud
-- **Frontend**: Vercel or Netlify
-- **Monitoring**: DataDog or New Relic
-
-### Production Checklist
-
-- [ ] Environment variables configured
-- [ ] SSL certificates installed
-- [ ] Database backups scheduled
-- [ ] Monitoring alerts set up
-- [ ] API rate limiting configured
-- [ ] Security headers enabled
-
----
-
-## 🤝 Contributing
-
-We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
-
-### Development Setup
-
-```bash
-# Fork and clone
-git clone https://github.com/your-username/demeter.git
-cd demeter
-
-# Create feature branch
-git checkout -b feature/amazing-enhancement
-
-# Install dev dependencies
-pip install -r requirements-dev.txt
-npm install --include=dev
-
-# Run tests
-pytest
-npm test
-
-# Format code
-black .
-npm run format
-```
-
-### Code Standards
-
-- **Python**: Black formatting, type hints required
-- **JavaScript**: ESLint + Prettier
-- **Commits**: Conventional commits format
-- **Tests**: 80%+ coverage required
-
-### Agent Development
-
-```python
-# Create new agent template
-from sub_agents.base_agent import BaseAgent
-
-class MyNewAgent(BaseAgent):
- def __init__(self):
- super().__init__(name="MyNewAgent")
-
- def execute(self, context):
- # Your agent logic here
- return self.generate_response(action, reasoning)
-```
-
----
-
## 📊 Performance Metrics
### System Benchmarks
@@ -476,38 +412,16 @@ class MyNewAgent(BaseAgent):
### Accuracy Metrics
-- **Disease Detection**: 94% accuracy (YOLOv8 fine-tuned)
+- **Disease Detection**: 94% accuracy (Azure CV fine-tuned)
- **Decision Quality**: 89% optimal actions (RL trained)
- **Safety Compliance**: 100% (validation enforced)
---
-## 📄 License
-
-This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
-
----
-
-## 🙏 Acknowledgments
-
-- **Agricultural Research Community** for scientific papers and best practices
-- **Open Source AI Community** for LangChain, YOLOv8, and other tools
-- **Hydroponic Farmers** whose expertise inspired this system
-
----
-
-## 📞 Support
-
-- **Issues**: [GitHub Issues](https://github.com/your-username/demeter/issues)
-- **Discussions**: [GitHub Discussions](https://github.com/your-username/demeter/discussions)
-- **Documentation**: [docs.demeter.ai](https://docs.demeter.ai)
-
----
-
<div align="center">
**Made with ❤️ for the future of sustainable agriculture**
-[🌟 Star us on GitHub](https://github.com/your-username/demeter) • [🐛 Report a bug](https://github.com/your-username/demeter/issues) • [💡 Request a feature](https://github.com/your-username/demeter/issues/new?template=feature_request.md)
+[🌟 Star us on GitHub](https://github.com/maydayv7/demeter) • [🐛 Report a bug](https://github.com/maydayv7/demeter/issues) • [💡 Request a feature](https://github.com/maydayv7/demeter/issues/new?template=feature_request.md)
</div>
diff --git a/requirements.txt b/requirements.txt
@@ -22,7 +22,6 @@ fastembed>=0.2.0
numpy>=1.26.0
torch>=2.2.0
torchvision>=0.17.0
-ultralytics>=8.1.0
opencv-python>=4.9.0.80
Pillow>=10.2.0
@@ -36,7 +35,5 @@ git+https://github.com/openai/CLIP.git
# --- Utilities ---
aiofiles>=23.2.1
httpx>=0.26.0
-
-# --- Conversation ---
groq
sentence_transformers
diff --git a/setup.md b/setup.md
@@ -1,40 +0,0 @@
-# 🛠️ Demeter System Setup Guide
-
-This guide covers the complete installation, configuration, and troubleshooting process for the **Demeter** Autonomous Hydroponic System.
-
----
-
-## 📋 Prerequisites
-
-Ensure you have the following installed on your machine:
-
-1. **Python 3.10+**: [Download Here](https://www.python.org/downloads/)
-2. **Node.js 16+ & npm**: [Download Here](https://nodejs.org/)
-3. **Docker Desktop** (Recommended for local Database) OR a [Qdrant Cloud Account](https://cloud.qdrant.io/).
-4. **Git**: [Download Here](https://git-scm.com/)
-
----
-
-## 1️⃣ Environment Configuration
-
-1. Navigate to the project root directory.
-2. Create a file named `.env`.
-3. Add the following keys. You **must** provide a Groq API Key.
-
-```env
-# --- AI Provider (Required) ---
-# Get a free key at: [https://console.groq.com/keys](https://console.groq.com/keys)
-GROQ_API_KEY=gsk_...
-
-# --- Vector Database (Required) ---
-# For Local Docker: http://localhost:6333
-# For Cloud: [https://xyz-example.us-east-1-0.aws.cloud.qdrant.io:6333](https://xyz-example.us-east-1-0.aws.cloud.qdrant.io:6333)
-QDRANT_URL=http://localhost:6333
-QDRANT_API_KEY=
-
-# --- Optional / Advanced ---
-# Required if you switch 'FarmMemory' to use OpenAI embeddings instead of local ONNX
-OPENAI_API_KEY=sk-...
-
-# Required for 'Researcher' agent to perform live Google searches
-SERPAPI_API_KEY=...
-\ No newline at end of file