commit d790a441f23359b1cc6fe0e0bba26854aa53edca
parent 0e867fc4c1f00e6e5476c4fffcf6addf4e60a227
Author: AbhinavRai01 <abhinavrai004@gmail.com>
Date: Sun, 1 Mar 2026 04:50:24 +0000
directory
Diffstat:
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