demeter

Autonomous Hydroponic Intelligence
commit d790a441f23359b1cc6fe0e0bba26854aa53edca
parent 0e867fc4c1f00e6e5476c4fffcf6addf4e60a227
Author: AbhinavRai01 <abhinavrai004@gmail.com>
Date:   Sun,  1 Mar 2026 04:50:24 +0000

directory

Diffstat:
M.gitignore | 2+-
Aagent/main_agent.py | 0
Aagent/sub_agents/action_orchestrator.py | 0
Aagent/sub_agents/atmospheric_agent.py | 0
Aagent/sub_agents/fetching_agent.py | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aagent/sub_agents/historian_agent.py | 0
Aagent/sub_agents/researcher_agent.py | 0
Aagent/sub_agents/water_agent.py | 0
Aagent/tools/db_tools.py | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aagent/tools/processing_tools.py | 36++++++++++++++++++++++++++++++++++++
10 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -1,4 +1,4 @@ -/.venv +/venv .env node_modules/ __pycache__/ diff --git a/agent/main_agent.py b/agent/main_agent.py diff --git a/agent/sub_agents/action_orchestrator.py b/agent/sub_agents/action_orchestrator.py diff --git a/agent/sub_agents/atmospheric_agent.py b/agent/sub_agents/atmospheric_agent.py diff --git a/agent/sub_agents/fetching_agent.py b/agent/sub_agents/fetching_agent.py @@ -0,0 +1,64 @@ +import requests +import json +from Sentinel.agent import FMUBuilder +from tools.db_tools import DBTools # Assuming you have the DB tool from previous steps + +class FetchingAgent: + def __init__(self, simulator_url="https://unexhumed-melaine-bouncingly.ngrok-free.dev/simulation/state"): + self.sim_url = simulator_url + self.builder = FMUBuilder() + self.db = DBTools() + + def fetch_and_process(self): + print(f"[Fetcher] 📡 Requesting data from {self.sim_url}...") + + try: + # 1. GET Request to Simulator + # Expecting JSON: { "sensors": {...}, "image": "base64...", "metadata": {...} } + response = requests.get(self.sim_url) + + if response.status_code == 200: + data = response.json() + + # Extract Data + sensors = data.get("sensors", {}) + image_b64 = data.get("image", "") # Expecting pure Base64 string + metadata = data.get("metadata", {}) + + print(f"[Fetcher] ✅ Data received. Sensors: {sensors}") + + # 2. Create Vector & FMU (Using Base64) + fmu = self.builder.create_fmu(image_b64, sensors, metadata) + print(f"[Fetcher] 🧠 FMU Created (ID: {fmu.id})") + + # 3. Store in Database (Optional step here, or return to Orchestrator) + self.db.store_fmu(fmu) + + return fmu + else: + print(f"[Fetcher] ❌ Error: Simulator returned {response.status_code}") + return None + + except Exception as e: + print(f"[Fetcher] ❌ Connection Failed: {e}") + return None + +# --- Quick Test --- +if __name__ == "__main__": + # Mocking a server response for testing logic without a real server + from unittest.mock import MagicMock + + agent = FetchingAgent() + + # Mock request + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = { + "sensors": {"pH": 5.9, "EC": 1.3, "temp": 25.0, "humidity": 72.0}, + "metadata": {"crop": "lettuce", "stage": "vegetative"}, + # A tiny white pixel in Base64 + "image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg==" + } + requests.get = MagicMock(return_value=mock_response) + + agent.fetch_and_process() +\ No newline at end of file diff --git a/agent/sub_agents/historian_agent.py b/agent/sub_agents/historian_agent.py diff --git a/agent/sub_agents/researcher_agent.py b/agent/sub_agents/researcher_agent.py diff --git a/agent/sub_agents/water_agent.py b/agent/sub_agents/water_agent.py diff --git a/agent/tools/db_tools.py b/agent/tools/db_tools.py @@ -0,0 +1,48 @@ +# tools/db_tools.py +from qdrant_client import QdrantClient, models +from qdrant_client.models import PointStruct + +class DBTools: + def __init__(self, host="https://2a9e6ab0-e572-4bfa-a50f-0a169f9753d3.europe-west3-0.gcp.cloud.qdrant.io", collection_name="farm_memory"): + self.client = QdrantClient(url=host) + self.collection_name = collection_name + self.vector_size = 516 # As seen in Qdrant/Setup.py + + def setup_database(self): + """Creates or resets the memory collection.""" + self.client.recreate_collection( + collection_name=self.collection_name, + vectors_config=models.VectorParams( + size=self.vector_size, + distance=models.Distance.COSINE + ) + ) + print(f"[DB] Collection '{self.collection_name}' ready.") + + def store_fmu(self, fmu_data): + """ + Stores a Farm Memory Unit (FMU). + Derived from Qdrant/Store.py + """ + point = PointStruct( + id=fmu_data.id, + vector=fmu_data.vector, + payload=fmu_data.metadata + ) + self.client.upsert( + collection_name=self.collection_name, + points=[point] + ) + print(f"[DB] Stored FMU ID: {fmu_data.id}") + + def search_similar(self, vector, limit=5): + """ + Finds similar past states. + Derived from backend/server/main.py search endpoint + """ + hits = self.client.search( + collection_name=self.collection_name, + query_vector=vector, + limit=limit + ) + return [{"score": hit.score, "payload": hit.payload} for hit in hits] +\ No newline at end of file diff --git a/agent/tools/processing_tools.py b/agent/tools/processing_tools.py @@ -0,0 +1,35 @@ +# tools/processing_tools.py +import os +import json +# Assuming Sentinel is available in the python path as per original code +from Sentinel.agent import FMUBuilder +from Sentinel.Encoders.Vision import VisionEncoder + +class ProcessingTools: + def __init__(self): + self.builder = FMUBuilder() + self.vision = VisionEncoder() + + def create_fmu(self, image_path: str, sensors: dict, metadata: dict): + """ + Converts raw inputs into a standardized FMU object. + Derived from the '/ingest' endpoint in main.py + """ + # Ensure path is absolute as required by original logic + abs_path = os.path.abspath(image_path) + fmu = self.builder.create_fmu(abs_path, sensors, metadata) + return fmu + + def encode_image(self, image_path: str): + """ + Converts an image into a vector embedding. + Derived from the '/search' endpoint in main.py + """ + abs_path = os.path.abspath(image_path) + # Returns a numpy array or list based on VisionEncoder implementation + vector = self.vision.encode(abs_path) + + # Ensure it's a list for Qdrant + if hasattr(vector, 'tolist'): + return vector.tolist() + return vector +\ No newline at end of file