Krina2005 commited on
Commit
dabc7ce
·
1 Parent(s): c994a2e

initial commit

Browse files
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ .env
2
+ venv
3
+ __pycache__
4
+ .venv
5
+ .env.example
Dockerfile ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Python 3.12.1
2
+ FROM python:3.12.1-slim
3
+
4
+ # Prevent Python from writing .pyc files
5
+ ENV PYTHONDONTWRITEBYTECODE=1
6
+
7
+ # Ensure Python output is sent straight to terminal
8
+ ENV PYTHONUNBUFFERED=1
9
+
10
+ # Disable pip cache
11
+ ENV PIP_NO_CACHE_DIR=1
12
+
13
+ # Set working directory
14
+ WORKDIR /app
15
+
16
+ # Install required system dependencies
17
+ RUN apt-get update && apt-get install -y \
18
+ build-essential \
19
+ gcc \
20
+ git \
21
+ && rm -rf /var/lib/apt/lists/*
22
+
23
+ # Copy requirements first for Docker layer caching
24
+ COPY requirements.txt .
25
+
26
+ # Upgrade pip and install dependencies
27
+ RUN pip install --upgrade pip && \
28
+ pip install -r requirements.txt
29
+
30
+ # Pre-download Sentence Transformer model (optional but recommended)
31
+ RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('BAAI/bge-small-en-v1.5')"
32
+
33
+ # Copy application
34
+ COPY . .
35
+
36
+ # Hugging Face Spaces exposes port 7860
37
+ ENV PORT=7860
38
+
39
+ EXPOSE 7860
40
+
41
+ # Start FastAPI
42
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,99 @@
 
 
 
 
1
  ---
2
- title: Farmrisk
3
- emoji: 🌖
4
- colorFrom: indigo
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FarmRisk Backend
2
+
3
+ This is the backend service for FarmRisk, a multilingual agro-meteorological decision support platform. It is built using **FastAPI** and integrates Open-Meteo forecasts, Pinecone vector retrievals (RAG), and Gemini/Groq LLMs to generate crop-specific agricultural advisories.
4
+
5
  ---
6
+
7
+ ## Directory Structure
8
+
9
+ ```
10
+ backend/
11
+ ├── app/ # Main application package
12
+ │ ├── api/ # API routers (FastAPI endpoints)
13
+ │ │ ├── advisory.py # Handles /api/advisory (orchestrating generation & translation)
14
+ │ │ └── location.py # Handles /api/location (location utilities)
15
+ │ │
16
+ │ ├── core/ # Core system modules
17
+ │ │ ├── caching.py # In-memory spatial & translation cache manager
18
+ │ │ ├── config.py # Global application configurations & environment loading
19
+ │ │ └── logging.py # Structured logging configuration
20
+ │ │
21
+ │ ├── llm/ # Large Language Model services
22
+ │ │ ├── advisory_engine.py# Core advisory prompt synthesis and generation
23
+ │ │ └── providers.py # LLM clients for Gemini and Groq (JSON/Text generation)
24
+ │ │
25
+ │ ├── models/ # Data validation schemas (Pydantic)
26
+ │ │ └── schemas.py # Request, Response, and Location models
27
+ │ │
28
+ │ ├── rag/ # Retrieval-Augmented Generation
29
+ │ │ └── retriever.py # Pinecone query retriever for scientific guidelines
30
+ │ │
31
+ │ ├── services/ # External services and utility classes
32
+ │ │ ├── location.py # Nominatim reverse geocoding
33
+ │ │ ├── season.py # Month-based agricultural season resolver (Kharif/Rabi)
34
+ │ │ ├── translation.py # Multi-language translation pipeline
35
+ │ │ └── weather.py # Open-Meteo 10-day forecast integration & risk rules
36
+ │ │
37
+ │ └── main.py # FastAPI entry point & lifespan events
38
+
39
+ ├── config/ # Static JSON configuration files
40
+ │ ├── crops.json # Supported crops configuration
41
+ │ └── states.json # Standardized Indian States configuration
42
+
43
+ ├── data/ # Data directory (scientific guides, JSON extracts)
44
+
45
+ ├── pipeline/ # Data parsing, chunking, and index uploading scripts
46
+ │ ├── 02_parse.py # Parse raw ICAR guides into JSON
47
+ │ ├── 03_validate.py # Validate guidelines format
48
+ │ ├── 04_chunk.py # Text chunking for vector database
49
+ │ ├── 05_embed_upload.py # Embed text chunks and upload to Pinecone
50
+ │ └── run_all.py # Master script to run the pipeline
51
+
52
+ ├── rag/ # RAG data extraction script
53
+ │ └── extract.py # Extract content text from PDFs
54
+
55
+ ├── .env # Local environment variable configuration
56
+ ├── requirements.txt # Python dependencies
57
+ └── venv/ # Python virtual environment
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Core Components
63
+
64
+ ### 1. API Endpoints (`app/api/`)
65
+ * **Advisory (`advisory.py`)**: Receives farmer coordinate location, crop, target language, and crop stage. It resolves the coordinates into village/district boundaries, retrieves the 10-day weather forecast, performs semantic vector search for ICAR guidelines, triggers Gemini/Groq to generate the summary, caches results, and runs translation.
66
+ * **Location (`location.py`)**: Provides endpoints for searching or geocoding locations.
67
+
68
+ ### 2. LLM Advisory Engine (`app/llm/`)
69
+ * **Advisory Engine (`advisory_engine.py`)**: Synthesizes the weather forecast context, risk alerts, and scientific guidelines into a prompt. It enforces strict two-paragraph output constraints, plain text styling, word count range (120-180 words), and structural partitions (expected weather in paragraph 1, recommendations in paragraph 2 ending with outlook).
70
+ * **LLM Providers (`providers.py`)**: Integrates with the official `google-genai` SDK and the `groq` SDK, implementing retry/backoff wrappers for transient error tolerance, supporting JSON and plain-text output formats.
71
+
72
+ ### 3. Translation Pipeline (`app/services/translation.py`)
73
+ * Extracts the generated `advisory_summary` and calls the translation provider.
74
+ * Prompt rules enforce the strict preservation of double-newline paragraph breaks (`\n\n`), sentence order, wording, and meaning without rephrasing or summarizing.
75
+
76
+ ### 4. RAG Retriever (`app/rag/`)
77
+ * Performs a metadata-filtered vector similarity search on the Pinecone index `farmrisk` based on selected crop, state, and resolved agricultural season to retrieve precise scientific guidelines.
78
+
79
+ ### 5. Weather Service (`app/services/weather.py`)
80
+ * Connects to the Open-Meteo Forecast API.
81
+ * Runs local rule-based evaluations for extreme temperatures, high winds, or heavy rainfall to output alert flags.
82
+ * Generates a stable forecast hash used for temporal caching.
83
+
84
  ---
85
 
86
+ ## Running the Backend
87
+
88
+ 1. **Activate the Virtual Environment**:
89
+ ```powershell
90
+ .\venv\Scripts\Activate.ps1
91
+ ```
92
+ 2. **Install Dependencies**:
93
+ ```bash
94
+ pip install -r requirements.txt
95
+ ```
96
+ 3. **Start the Development Server**:
97
+ ```bash
98
+ python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
99
+ ```
app/api/advisory.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException
2
+ from typing import Dict, Any
3
+ from app.models.schemas import AdvisoryRequest, AdvisoryResponse
4
+ from app.services.location import NominatimLocationResolver
5
+ from app.services.weather import WeatherService
6
+ from app.services.season import MonthSeasonResolver
7
+ from app.rag.retriever import AdvisoryRetriever
8
+ from app.llm.advisory_engine import AdvisoryEngine
9
+ from app.services.translation import TranslationService
10
+ from app.core.caching import cache_manager
11
+ from app.core.logging import logger
12
+
13
+ router = APIRouter(prefix="/api/advisory", tags=["Advisory"])
14
+
15
+ # Services
16
+ location_resolver = NominatimLocationResolver()
17
+ weather_service = WeatherService()
18
+ season_resolver = MonthSeasonResolver()
19
+
20
+ # Try initializing vector retriever, fallback if Pinecone config is missing/offline
21
+ try:
22
+ retriever = AdvisoryRetriever()
23
+ except Exception as e:
24
+ logger.warning(f"Failed to initialize AdvisoryRetriever: {e}. Falling back to empty search context.")
25
+ retriever = None
26
+
27
+ advisory_engine = AdvisoryEngine()
28
+ translation_service = TranslationService()
29
+
30
+ @router.post("", response_model=Dict[str, Any])
31
+ async def generate_crop_advisory(request: AdvisoryRequest):
32
+ """
33
+ Generate agrometeorological advisory for a given coordinate grid location and crop.
34
+ Retrieves weather context, performs Pinecone RAG search, uses Gemini Flash, and translates.
35
+ """
36
+ logger.info(
37
+ f"Processing advisory request: crop={request.crop}, lat={request.latitude}, lon={request.longitude}, lang={request.language}"
38
+ )
39
+
40
+ try:
41
+ # Step 1: Geocode coordinates to get state and village details
42
+ location_detail = await location_resolver.reverse_geocode(request.latitude, request.longitude)
43
+ logger.info(
44
+ f"Geocoded coordinates: state={location_detail.state}, district={location_detail.district}, village={location_detail.village}"
45
+ )
46
+
47
+ # Step 2: Fetch current weather and compute forecast hash
48
+ weather_data = await weather_service.fetch_10day_forecast(request.latitude, request.longitude)
49
+ weather_hash = weather_data["weather_hash"]
50
+
51
+ # Step 3: Resolve current agricultural season
52
+ season = season_resolver.resolve_season(request.latitude, request.longitude)
53
+ logger.info(f"Resolved season: {season}")
54
+
55
+ # Step 4: Spatial Cache Lookup (Check if advisory exists for this crop + location grid + weather forecast)
56
+ english_advisory = cache_manager.get_advisory(request.crop, request.latitude, request.longitude, weather_hash)
57
+
58
+ if english_advisory:
59
+ logger.info("Advisory spatial cache HIT.")
60
+ # Verify structure and convert back to schema if needed
61
+ advisory_obj = AdvisoryResponse(**english_advisory)
62
+ else:
63
+ logger.info("Advisory spatial cache MISS. Initiating RAG pipeline.")
64
+
65
+ # Step 5: Retrieve relevant RAG guidelines from Pinecone
66
+ rag_context = []
67
+ if retriever:
68
+ # Query vector database
69
+ rag_context = retriever.retrieve(
70
+ crop=request.crop,
71
+ state=location_detail.state,
72
+ season=season
73
+ )
74
+
75
+ logger.info(f"Retrieved {len(rag_context)} documents from Pinecone.")
76
+
77
+ # Step 6: Generate crop advisory via Gemini Flash
78
+ advisory_obj = await advisory_engine.generate_advisory(
79
+ crop=request.crop,
80
+ state=location_detail.state,
81
+ district=location_detail.district,
82
+ village=location_detail.village,
83
+ season=season,
84
+ weather_data=weather_data,
85
+ rag_context=rag_context,
86
+ crop_stage=request.crop_stage
87
+ )
88
+
89
+ # Save raw English advisory to spatial cache
90
+ cache_manager.set_advisory(
91
+ crop=request.crop,
92
+ latitude=request.latitude,
93
+ longitude=request.longitude,
94
+ weather_hash=weather_hash,
95
+ advisory_data=advisory_obj.model_dump()
96
+ )
97
+ logger.info("Cached raw English advisory.")
98
+
99
+ # Step 7: Translation Cache Lookup
100
+ english_dump = advisory_obj.model_dump()
101
+ translated_advisory = cache_manager.get_translation(english_dump, request.language)
102
+
103
+ if translated_advisory:
104
+ logger.info(f"Translation cache HIT for language: {request.language}")
105
+ return translated_advisory
106
+
107
+ logger.info(f"Translation cache MISS for language: {request.language}. Triggering translator.")
108
+
109
+ # Step 8: Translate response values using LLM provider pipeline
110
+ translation_result = await translation_service.translate_advisory(advisory_obj, request.language)
111
+ translated_advisory = translation_result.data
112
+
113
+ # Cache translation result only if translation succeeded
114
+ if translation_result.translated:
115
+ cache_manager.set_translation(english_dump, request.language, translated_advisory)
116
+ logger.info(f"Cached translation for language: {request.language}")
117
+ else:
118
+ logger.warning("Translation cache skipped because translation failed")
119
+
120
+ # Inject resolved geographic location details into response metadata for UI display
121
+ translated_advisory["location"] = location_detail.model_dump()
122
+
123
+ return translated_advisory
124
+
125
+ except Exception as e:
126
+ logger.error(f"Failed to generate crop advisory: {e}", exc_info=True)
127
+ raise HTTPException(status_code=500, detail=f"Advisory generation failed: {str(e)}")
app/api/location.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Query, HTTPException
2
+ from typing import List
3
+ from app.models.schemas import LocationDetail
4
+ from app.services.location import NominatimLocationResolver
5
+
6
+ router = APIRouter(prefix="/api/location", tags=["Location"])
7
+ resolver = NominatimLocationResolver()
8
+
9
+ @router.get("/search", response_model=List[LocationDetail])
10
+ async def search_locations(q: str = Query(..., min_length=2, description="Search term for village/city/town")):
11
+ """
12
+ Search for villages and towns in India via Nominatim.
13
+ Returns details (village, district, state, latitude, longitude)
14
+ """
15
+ try:
16
+ results = await resolver.search(q)
17
+ return results
18
+ except Exception as e:
19
+ raise HTTPException(status_code=500, detail=f"Location search failed: {str(e)}")
app/core/caching.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import hashlib
3
+ from typing import Optional, Dict, Any
4
+ from app.core.config import settings
5
+
6
+ class InMemoryCache:
7
+ def __init__(self):
8
+ # Format: key -> (value, expiry_time_seconds)
9
+ self._cache: Dict[str, tuple] = {}
10
+
11
+ def get(self, key: str) -> Optional[Any]:
12
+ if key not in self._cache:
13
+ return None
14
+ val, expiry = self._cache[key]
15
+ if expiry and time.time() > expiry:
16
+ del self._cache[key]
17
+ return None
18
+ return val
19
+
20
+ def set(self, key: str, value: Any, ttl_seconds: int = 43200):
21
+ expiry = time.time() + ttl_seconds if ttl_seconds else None
22
+ self._cache[key] = (value, expiry)
23
+
24
+ def clear(self):
25
+ self._cache.clear()
26
+
27
+ class CacheManager:
28
+ def __init__(self):
29
+ self.provider = InMemoryCache()
30
+ # In the future, if settings.CACHE_TYPE == "redis", we could instantiate a Redis provider.
31
+
32
+ def get_advisory(self, crop: str, latitude: float, longitude: float, weather_hash: str) -> Optional[Dict[str, Any]]:
33
+ # Round coordinates to 3 decimal places (approx. 110m grid accuracy)
34
+ lat_grid = f"{latitude:.3f}"
35
+ lon_grid = f"{longitude:.3f}"
36
+ key = f"adv:{crop.lower().strip()}:{lat_grid}:{lon_grid}:{weather_hash}"
37
+ return self.provider.get(key)
38
+
39
+ def set_advisory(self, crop: str, latitude: float, longitude: float, weather_hash: str, advisory_data: Dict[str, Any], ttl: int = 43200):
40
+ lat_grid = f"{latitude:.3f}"
41
+ lon_grid = f"{longitude:.3f}"
42
+ key = f"adv:{crop.lower().strip()}:{lat_grid}:{lon_grid}:{weather_hash}"
43
+ self.provider.set(key, advisory_data, ttl)
44
+
45
+ def get_translation(self, english_json: Dict[str, Any], language: str) -> Optional[Dict[str, Any]]:
46
+ # Generate hash of English JSON structure
47
+ serialized = json_stable_hash(english_json)
48
+ key = f"trans:{serialized}:{language.lower().strip()}"
49
+ return self.provider.get(key)
50
+
51
+ def set_translation(self, english_json: Dict[str, Any], language: str, translated_data: Dict[str, Any], ttl: int = 43200):
52
+ serialized = json_stable_hash(english_json)
53
+ key = f"trans:{serialized}:{language.lower().strip()}"
54
+ self.provider.set(key, translated_data, ttl)
55
+
56
+ def json_stable_hash(data: Dict[str, Any]) -> str:
57
+ """Serialize dict in stable sorted way and SHA256 hash it."""
58
+ import json
59
+ serialized = json.dumps(data, sort_keys=True, ensure_ascii=False)
60
+ return hashlib.sha256(serialized.encode("utf-8")).hexdigest()[:16]
61
+
62
+ cache_manager = CacheManager()
app/core/config.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ from dotenv import load_dotenv
4
+
5
+ BASE_DIR = Path(__file__).resolve().parent.parent.parent
6
+ load_dotenv(dotenv_path=BASE_DIR / ".env")
7
+
8
+ class Settings:
9
+ # FastAPI
10
+ HOST: str = os.getenv("HOST", "127.0.0.1")
11
+ PORT: int = int(os.getenv("PORT", "8000"))
12
+ DEBUG: bool = os.getenv("DEBUG", "true").lower() in ("true", "1", "yes")
13
+ APP_ENV: str = os.getenv("APP_ENV", "development")
14
+
15
+ # Pinecone
16
+ PINECONE_API_KEY: str = os.getenv("PINECONE_API_KEY", "")
17
+ PINECONE_INDEX_NAME: str = os.getenv("PINECONE_INDEX_NAME", "farmrisk-advisories")
18
+
19
+ # LLM Providers Configuration
20
+ LLM_PROVIDER: str = os.getenv("LLM_PROVIDER", "gemini")
21
+ ENABLE_GROQ_FALLBACK: bool = os.getenv("ENABLE_GROQ_FALLBACK", "true").lower() in ("true", "1", "yes")
22
+
23
+ # Gemini
24
+ GOOGLE_API_KEY: str = os.getenv("GOOGLE_API_KEY", "")
25
+ GEMINI_MODEL: str = os.getenv("GEMINI_MODEL", "gemini-3.5-flash")
26
+ TEMPERATURE: float = float(os.getenv("TEMPERATURE", "0.2"))
27
+
28
+ # Groq
29
+ GROQ_API_KEY: str = os.getenv("GROQ_API_KEY", "")
30
+ GROQ_MODEL: str = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
31
+
32
+ # Caching
33
+ CACHE_TYPE: str = os.getenv("CACHE_TYPE", "in_memory")
34
+ REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379/0")
35
+ ADVISORY_CACHE_TTL: int = int(os.getenv("ADVISORY_CACHE_TTL", "43200"))
36
+
37
+ # Logging
38
+ LOG_FORMAT: str = os.getenv("LOG_FORMAT", "TEXT")
39
+ LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
40
+
41
+ settings = Settings()
app/core/logging.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import json
3
+ import sys
4
+ from app.core.config import settings
5
+
6
+ class JSONFormatter(logging.Formatter):
7
+ def format(self, record):
8
+ log_entry = {
9
+ "timestamp": self.formatTime(record, self.datefmt),
10
+ "level": record.levelname,
11
+ "logger": record.name,
12
+ "message": record.getMessage()
13
+ }
14
+ if record.exc_info:
15
+ log_entry["exception"] = self.formatException(record.exc_info)
16
+ # Include extra attributes if passed
17
+ if hasattr(record, "extra_fields"):
18
+ log_entry.update(record.extra_fields)
19
+ return json.dumps(log_entry)
20
+
21
+ def setup_logging():
22
+ log_level_map = {
23
+ "DEBUG": logging.DEBUG,
24
+ "INFO": logging.INFO,
25
+ "WARNING": logging.WARNING,
26
+ "ERROR": logging.ERROR,
27
+ "CRITICAL": logging.CRITICAL
28
+ }
29
+
30
+ level = log_level_map.get(settings.LOG_LEVEL.upper(), logging.INFO)
31
+ logger = logging.getLogger("farmrisk")
32
+ logger.setLevel(level)
33
+
34
+ # Avoid duplicate handlers
35
+ if logger.handlers:
36
+ return logger
37
+
38
+ handler = logging.StreamHandler(sys.stdout)
39
+
40
+ if settings.LOG_FORMAT.upper() == "JSON":
41
+ formatter = JSONFormatter(datefmt="%Y-%m-%dT%H:%M:%S")
42
+ else:
43
+ formatter = logging.Formatter(
44
+ fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
45
+ datefmt="%Y-%m-%d %H:%M:%S"
46
+ )
47
+
48
+ handler.setFormatter(formatter)
49
+ logger.addHandler(handler)
50
+
51
+ # Configure root logger lightly
52
+ root_logger = logging.getLogger()
53
+ if not root_logger.handlers:
54
+ root_logger.addHandler(handler)
55
+ root_logger.setLevel(level)
56
+
57
+ return logger
58
+
59
+ logger = setup_logging()
app/llm/advisory_engine.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from typing import List, Dict, Any, Optional
3
+ from app.core.config import settings
4
+ from app.models.schemas import AdvisoryResponse
5
+ from app.llm.providers import get_primary_provider, get_fallback_provider
6
+ from app.core.logging import logger
7
+ from datetime import datetime
8
+
9
+ class AdvisoryEngine:
10
+ def __init__(self):
11
+ pass
12
+
13
+ async def generate_advisory(
14
+ self,
15
+ crop: str,
16
+ state: str,
17
+ district: str,
18
+ village: str,
19
+ season: str,
20
+ weather_data: Dict[str, Any],
21
+ rag_context: List[Dict[str, Any]],
22
+ crop_stage: Optional[str] = None
23
+ ) -> AdvisoryResponse:
24
+ """Generate a two-paragraph plain-text agrometeorological advisory using LLMProvider and RAG context."""
25
+
26
+ # Format the context from Pinecone
27
+ formatted_context = ""
28
+ for idx, item in enumerate(rag_context, start=1):
29
+ formatted_context += f"Source [{item['source']} page {item['page']}]:\n{item['content']}\n\n"
30
+
31
+ # Format the weather data
32
+ formatted_weather = json.dumps(weather_data["forecast"], indent=2)
33
+ weather_risks = ", ".join(weather_data["risks"]) if weather_data["risks"] else "None"
34
+
35
+ # Extract forecast period dates
36
+ # start_date = weather_data["forecast"][0]["date"] if weather_data["forecast"] else "N/A"
37
+ # end_date = weather_data["forecast"][-1]["date"] if weather_data["forecast"] else "N/A"
38
+ start_date = datetime.strptime(weather_data["forecast"][0]["date"], "%Y-%m-%d").strftime("%d/%m/%Y")
39
+ end_date = datetime.strptime(weather_data["forecast"][-1]["date"], "%Y-%m-%d").strftime("%d/%m/%Y")
40
+
41
+ crop_stage_str = f"Crop Stage: {crop_stage}" if crop_stage else "Crop Stage: Not specified (infer from the current season and date)"
42
+
43
+ prompt = f"""
44
+ You are an expert agrometeorologist assistant at FarmRisk.
45
+ Generate a professional, extension-style agricultural advisory bulletin for a farmer growing {crop} in {village}, {district}, {state} during the {season} season.
46
+
47
+ CROP PARAMETERS:
48
+ - Crop: {crop}
49
+ - {crop_stage_str}
50
+
51
+ WEATHER PARAMETERS (10-Day Forecast from {start_date} to {end_date}):
52
+ {formatted_weather}
53
+
54
+ WEATHER RISK ALERTS:
55
+ {weather_risks}
56
+
57
+ AGRICULTURAL ADVISORY CONTEXT (Retrieved ICAR Scientific guidelines):
58
+ {formatted_context}
59
+
60
+ INSTRUCTIONS:
61
+ Generate exactly one advisory consisting of exactly two paragraphs in plain text.
62
+ - Always use indian date stamp format (DD/MM/YYYY).
63
+ - Always highlight the important words and numbers using asterisks. Like this: *word* and *number*
64
+ - Do NOT return JSON.
65
+ - Do NOT use headings.
66
+ - Do NOT use bullet points.
67
+ - Do NOT use numbering.
68
+ - The total length of both paragraphs combined MUST be between 120 and 180 words.
69
+ - All text in the response must be written in English.
70
+
71
+ Paragraph 1: Weather and Crop Impact
72
+ - Must begin exactly with: "From {start_date} to {end_date}, "
73
+ - Describe expected weather conditions and crop/soil impacts over the 10-day period.
74
+ - Include cumulative rainfall (mm), rainfall pattern (light, moderate, or heavy), and rainfall-related risks.
75
+ - Include minimum and maximum temperature range.
76
+ - Include wind conditions, humidity, and expected soil moisture trend.
77
+ - Describe the expected impact on the selected crop ({crop}).
78
+ - This paragraph must describe ONLY weather conditions and crop impacts. Do NOT include recommendations, actions, or guidelines here.
79
+
80
+ Paragraph 2: Crop Advisory
81
+ - Provide practical crop-specific agricultural recommendations.
82
+ - Recommendations must be based ONLY on the weather forecast, crop stage, and the retrieved ICAR advisory context. Never invent recommendations or hallucinate info.
83
+ - Include guidance where applicable for: irrigation, sowing or transplanting, fertilizer timing, pesticide spraying (e.g., matching wind conditions), drainage management, pest/disease monitoring, and harvesting.
84
+ - End the paragraph with exactly one concluding sentence stating the overall agricultural outlook:
85
+ - "Overall, the agricultural outlook for this period is Favorable."
86
+ - "Overall, the agricultural outlook for this period is Cautionary."
87
+ - "Overall, the agricultural outlook for this period is Unfavorable."
88
+ Choose the single option that best matches the weather and crop impact.
89
+ """
90
+
91
+ primary_provider = get_primary_provider()
92
+ fallback_provider = get_fallback_provider()
93
+
94
+ try:
95
+ raw_text = await primary_provider.generate_text(
96
+ prompt=prompt,
97
+ temperature=settings.TEMPERATURE
98
+ )
99
+ logger.info(f"{settings.LLM_PROVIDER} advisory succeeded")
100
+ return AdvisoryResponse(advisory_summary=raw_text.strip())
101
+ except Exception as primary_exc:
102
+ logger.warning(f"Primary provider {settings.LLM_PROVIDER} failed to generate advisory: {primary_exc}")
103
+
104
+ if fallback_provider:
105
+ logger.info("Switching to Groq fallback for advisory generation")
106
+ try:
107
+ raw_text = await fallback_provider.generate_text(
108
+ prompt=prompt,
109
+ temperature=settings.TEMPERATURE
110
+ )
111
+ logger.info("Groq advisory succeeded")
112
+ return AdvisoryResponse(advisory_summary=raw_text.strip())
113
+ except Exception as fallback_exc:
114
+ logger.error(f"Fallback provider Groq failed to generate advisory: {fallback_exc}")
115
+
116
+ logger.warning("All LLM providers failed to generate advisory. Using local English mock fallback.")
117
+ return self._get_mock_advisory(crop, village, start_date, end_date)
118
+
119
+ def _get_mock_advisory(self, crop: str, village: str, start_date: str, end_date: str) -> AdvisoryResponse:
120
+ """Fallback mock advisory for local testing without Gemini credentials."""
121
+ paragraph_1 = f"From {start_date} to {end_date}, the region of {village} is expected to experience a cumulative rainfall of 25 mm, characterized by a light and intermittent rainfall pattern that poses minimal immediate flood risks. Maximum temperatures will peak around 36°C while minimums drop to 23°C. These conditions will maintain moderate soil moisture trends, which is highly beneficial for the active vegetative growth phase of {crop} but may also encourage early weed emergence."
122
+ paragraph_2 = f"Based on the weather forecast and standard guidelines, farmers should optimize irrigation schedules by pausing watering on days with light showers and ensuring active weeding. Apply nitrogenous fertilizers during dry breaks and monitor the crop closely for sucking pests and fungal leaf spots, ensuring that drainage channels are completely clear of debris. Overall, the agricultural outlook for this period is Favorable."
123
+
124
+ advisory_text = f"{paragraph_1}\n\n{paragraph_2}"
125
+ return AdvisoryResponse(advisory_summary=advisory_text)
126
+
app/llm/providers.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import json
3
+ import httpx
4
+ from typing import Protocol, Type, TypeVar, Any, Optional
5
+ from pydantic import BaseModel
6
+ from google import genai
7
+ from google.genai import types
8
+ from google.genai.errors import APIError
9
+ from groq import AsyncGroq
10
+ from app.core.config import settings
11
+ from app.core.logging import logger
12
+
13
+ T = TypeVar("T", bound=BaseModel)
14
+
15
+ class LLMProvider(Protocol):
16
+ async def generate_json(self, prompt: str, schema: Type[T], temperature: float = 0.2) -> T:
17
+ """Generate structured JSON response conforming to the Pydantic schema."""
18
+ ...
19
+
20
+ async def generate_text(self, prompt: str, temperature: float = 0.2) -> str:
21
+ """Generate raw plain text response."""
22
+ ...
23
+
24
+ def is_transient_error(e: Exception) -> bool:
25
+ """Determine if an exception is a transient error that should be retried."""
26
+ # 1. APIError from google-genai SDK
27
+ if isinstance(e, APIError):
28
+ status_code = getattr(e, "code", getattr(e, "status_code", None))
29
+ if status_code in [429, 500, 502, 503, 504]:
30
+ return True
31
+ # Check string representation if code/status_code not found
32
+ err_str = str(e)
33
+ for code in ["429", "500", "502", "503", "504"]:
34
+ if code in err_str:
35
+ return True
36
+
37
+ # 2. Timeout and connection errors
38
+ if isinstance(e, (httpx.TimeoutException, httpx.ConnectError, httpx.NetworkError, TimeoutError, asyncio.TimeoutError)):
39
+ return True
40
+
41
+ # Check general error strings for transient issues
42
+ err_str = str(e).lower()
43
+ for term in ["timeout", "timed out", "connection", "network", "temporary", "unavailable", "rate limit", "resource exhausted", "deadline exceeded"]:
44
+ if term in err_str:
45
+ # Do NOT retry authentication or client-side errors
46
+ if any(auth_term in err_str for auth_term in ["400", "401", "403", "unauthorized", "api_key", "invalid key", "invalid credential"]):
47
+ return False
48
+ return True
49
+
50
+ return False
51
+
52
+ class GeminiProvider:
53
+ def __init__(self):
54
+ self.enabled = bool(settings.GOOGLE_API_KEY and settings.GOOGLE_API_KEY != "your_google_api_key")
55
+ if self.enabled:
56
+ self.client = genai.Client(api_key=settings.GOOGLE_API_KEY)
57
+ else:
58
+ logger.warning("GOOGLE_API_KEY not configured. GeminiProvider is running in disabled/mock state.")
59
+
60
+ async def generate_json(self, prompt: str, schema: Type[T], temperature: float = 0.2) -> T:
61
+ if not self.enabled:
62
+ raise ValueError("Gemini API key not configured or provider disabled.")
63
+
64
+ attempts = 3
65
+ backoffs = [1.0, 2.0, 4.0]
66
+
67
+ for attempt in range(1, attempts + 1):
68
+ try:
69
+ logger.info(f"Gemini attempt {attempt}/{attempts}")
70
+ response = self.client.models.generate_content(
71
+ model=settings.GEMINI_MODEL,
72
+ contents=prompt,
73
+ config=types.GenerateContentConfig(
74
+ response_mime_type="application/json",
75
+ response_schema=schema,
76
+ temperature=temperature,
77
+ )
78
+ )
79
+ data = json.loads(response.text)
80
+ return schema.model_validate(data)
81
+ except Exception as e:
82
+ logger.warning(f"Gemini attempt {attempt} failed: {e}")
83
+
84
+ # Check if this is a transient error and we have attempts remaining
85
+ if attempt < attempts and is_transient_error(e):
86
+ wait_time = backoffs[attempt - 1]
87
+ logger.info(f"Gemini retrying after transient error {e}. Waiting {wait_time}s...")
88
+ await asyncio.sleep(wait_time)
89
+ else:
90
+ if attempt == attempts:
91
+ logger.error("Gemini retries exhausted.")
92
+ if is_transient_error(e):
93
+ wait_time = backoffs[attempt - 1]
94
+ logger.info(f"Waiting {wait_time}s after final failure...")
95
+ await asyncio.sleep(wait_time)
96
+ raise e
97
+
98
+ async def generate_text(self, prompt: str, temperature: float = 0.2) -> str:
99
+ if not self.enabled:
100
+ raise ValueError("Gemini API key not configured or provider disabled.")
101
+
102
+ attempts = 3
103
+ backoffs = [1.0, 2.0, 4.0]
104
+
105
+ for attempt in range(1, attempts + 1):
106
+ try:
107
+ logger.info(f"Gemini attempt {attempt}/{attempts} (text generation)")
108
+ response = self.client.models.generate_content(
109
+ model=settings.GEMINI_MODEL,
110
+ contents=prompt,
111
+ config=types.GenerateContentConfig(
112
+ temperature=temperature,
113
+ )
114
+ )
115
+ return response.text
116
+ except Exception as e:
117
+ logger.warning(f"Gemini text attempt {attempt} failed: {e}")
118
+ if attempt < attempts and is_transient_error(e):
119
+ wait_time = backoffs[attempt - 1]
120
+ logger.info(f"Gemini retrying after transient error {e}. Waiting {wait_time}s...")
121
+ await asyncio.sleep(wait_time)
122
+ else:
123
+ if attempt == attempts:
124
+ logger.error("Gemini retries exhausted.")
125
+ if is_transient_error(e):
126
+ wait_time = backoffs[attempt - 1]
127
+ logger.info(f"Waiting {wait_time}s after final failure...")
128
+ await asyncio.sleep(wait_time)
129
+ raise e
130
+
131
+
132
+ class GroqProvider:
133
+ def __init__(self):
134
+ self.enabled = bool(settings.GROQ_API_KEY)
135
+ if self.enabled:
136
+ self.client = AsyncGroq(api_key=settings.GROQ_API_KEY)
137
+ else:
138
+ logger.warning("GROQ_API_KEY not configured. GroqProvider is running in disabled/mock state.")
139
+
140
+ async def generate_json(self, prompt: str, schema: Type[T], temperature: float = 0.2) -> T:
141
+ if not self.enabled:
142
+ raise ValueError("Groq API key not configured or provider disabled.")
143
+
144
+ chat_completion = await self.client.chat.completions.create(
145
+ messages=[
146
+ {
147
+ "role": "user",
148
+ "content": prompt,
149
+ }
150
+ ],
151
+ model=settings.GROQ_MODEL,
152
+ response_format={"type": "json_object"},
153
+ temperature=temperature,
154
+ )
155
+ content = chat_completion.choices[0].message.content
156
+ data = json.loads(content)
157
+ return schema.model_validate(data)
158
+
159
+ async def generate_text(self, prompt: str, temperature: float = 0.2) -> str:
160
+ if not self.enabled:
161
+ raise ValueError("Groq API key not configured or provider disabled.")
162
+
163
+ chat_completion = await self.client.chat.completions.create(
164
+ messages=[
165
+ {
166
+ "role": "user",
167
+ "content": prompt,
168
+ }
169
+ ],
170
+ model=settings.GROQ_MODEL,
171
+ temperature=temperature,
172
+ )
173
+ return chat_completion.choices[0].message.content
174
+
175
+
176
+ def get_primary_provider() -> LLMProvider:
177
+ """Factory to get the primary LLM provider based on settings."""
178
+ if settings.LLM_PROVIDER.lower() == "groq":
179
+ return GroqProvider()
180
+ return GeminiProvider()
181
+
182
+ def get_fallback_provider() -> Optional[LLMProvider]:
183
+ """Get the fallback provider (Groq) if configured and primary is Gemini."""
184
+ if settings.LLM_PROVIDER.lower() == "gemini" and settings.ENABLE_GROQ_FALLBACK:
185
+ return GroqProvider()
186
+ return None
app/main.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from contextlib import asynccontextmanager
4
+ from app.core.config import settings
5
+ from app.core.logging import logger
6
+ from app.api.location import router as location_router, resolver as location_resolver
7
+ from app.api.advisory import router as advisory_router, weather_service
8
+
9
+ @asynccontextmanager
10
+ async def lifespan(app: FastAPI):
11
+ # Startup actions
12
+ logger.info(f"Starting FarmRisk Backend in {settings.APP_ENV} mode...")
13
+ yield
14
+ # Shutdown actions
15
+ logger.info("Shutting down FarmRisk Backend...")
16
+ await location_resolver.close()
17
+ await weather_service.close()
18
+ logger.info("FarmRisk Backend shutdown complete.")
19
+
20
+ app = FastAPI(
21
+ title="FarmRisk AI Backend API",
22
+ description="Production-grade agrometeorological advisory and village resolution system.",
23
+ version="1.0.0",
24
+ lifespan=lifespan
25
+ )
26
+
27
+ # Enable CORS for Next.js frontend
28
+ app.add_middleware(
29
+ CORSMiddleware,
30
+ allow_origins=["*"], # Adjust in production to frontend domain
31
+ allow_credentials=True,
32
+ allow_methods=["*"],
33
+ allow_headers=["*"],
34
+ )
35
+
36
+ # Mount Routers
37
+ app.include_router(location_router)
38
+ app.include_router(advisory_router)
39
+
40
+ @app.get("/health", tags=["Health"])
41
+ async def health_check():
42
+ """Simple API health check endpoint."""
43
+ return {"status": "healthy", "environment": settings.APP_ENV}
app/models/schemas.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from typing import List, Optional, Dict, Any
3
+
4
+ class LocationDetail(BaseModel):
5
+ village: Optional[str] = Field(None, description="Name of the village, town, or settlement")
6
+ district: Optional[str] = Field(None, description="Name of the district or county")
7
+ state: str = Field(..., description="Standardized name of the Indian State")
8
+ latitude: float = Field(..., description="Latitude coordinate")
9
+ longitude: float = Field(..., description="Longitude coordinate")
10
+
11
+ class AdvisoryRequest(BaseModel):
12
+ latitude: float = Field(..., description="Latitude coordinate of the farmer's location")
13
+ longitude: float = Field(..., description="Longitude coordinate of the farmer's location")
14
+ crop: str = Field(..., description="Name of the crop (e.g. Cotton, Rice)")
15
+ language: str = Field(..., description="Target language (e.g. Gujarati, Hindi, Marathi, Tamil, English)")
16
+ crop_stage: Optional[str] = Field(None, description="Optional current crop stage (e.g. vegetative, flowering, maturity)")
17
+
18
+ class AdvisoryResponse(BaseModel):
19
+ advisory_summary: str = Field(..., description="Two-paragraph professional agrometeorological advisory summary.")
20
+
21
+ class TranslationResult(BaseModel):
22
+ data: Dict[str, Any]
23
+ translated: bool
24
+ provider: Optional[str] = None
25
+
26
+
app/rag/retriever.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict, Any
2
+ from sentence_transformers import SentenceTransformer
3
+ from pinecone import Pinecone
4
+ from app.core.config import settings
5
+
6
+ class AdvisoryRetriever:
7
+ def __init__(self):
8
+ # Local SentenceTransformer
9
+ self.model = SentenceTransformer("BAAI/bge-small-en-v1.5")
10
+
11
+ # Pinecone
12
+ self.pc = Pinecone(api_key=settings.PINECONE_API_KEY)
13
+ self.index = self.pc.Index(settings.PINECONE_INDEX_NAME)
14
+
15
+ def retrieve(self, crop: str, state: str, season: str, top_k: int = 3) -> List[Dict[str, Any]]:
16
+ """Retrieve relevant context chunks from Pinecone using metadata pre-filtering."""
17
+ query_text = f"Advisory and recommendations for growing {crop} in {state} during {season} season."
18
+ query_vector = self.model.encode(query_text, normalize_embeddings=True).tolist()
19
+
20
+ # Stage 1: Strict metadata filter
21
+ meta_filter = {
22
+ "crop": crop,
23
+ "state": state,
24
+ "season": season
25
+ }
26
+
27
+ try:
28
+ response = self.index.query(
29
+ vector=query_vector,
30
+ top_k=top_k,
31
+ filter=meta_filter,
32
+ include_metadata=True
33
+ )
34
+
35
+ matches = response.get("matches", [])
36
+
37
+ # Stage 2: Fallback (if no results for specific state, query national/wider region by crop & season)
38
+ if not matches:
39
+ # Fallback filter omitting the state to capture national guidelines
40
+ fallback_filter = {
41
+ "crop": crop,
42
+ "season": season
43
+ }
44
+ response = self.index.query(
45
+ vector=query_vector,
46
+ top_k=top_k,
47
+ filter=fallback_filter,
48
+ include_metadata=True
49
+ )
50
+ matches = response.get("matches", [])
51
+
52
+ results = []
53
+ for match in matches:
54
+ metadata = match.get("metadata", {})
55
+ results.append({
56
+ "id": match.get("id"),
57
+ "score": match.get("score"),
58
+ "content": metadata.get("content", ""),
59
+ "state": metadata.get("state"),
60
+ "crop": metadata.get("crop"),
61
+ "season": metadata.get("season"),
62
+ "page": metadata.get("page"),
63
+ "source": metadata.get("source")
64
+ })
65
+ return results
66
+
67
+ except Exception as e:
68
+ print(f"Error querying Pinecone index: {e}")
69
+ return []
app/services/location.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from pathlib import Path
3
+ from typing import List, Optional, Protocol
4
+ import httpx
5
+ from app.models.schemas import LocationDetail
6
+ from app.core.config import settings
7
+
8
+ # Load canonical states at startup
9
+ BASE_DIR = Path(__file__).resolve().parent.parent.parent
10
+ with open(BASE_DIR / "config" / "states.json", "r", encoding="utf-8") as f:
11
+ CANONICAL_STATES = json.load(f)
12
+
13
+ # Helper to normalize resolved state names to the canonical ones
14
+ def normalize_state(resolved_state: str) -> str:
15
+ cleaned = resolved_state.lower().strip().replace("&", "and").replace(",", "")
16
+ for state in CANONICAL_STATES:
17
+ state_clean = state.lower().replace("&", "and").replace(",", "")
18
+ if cleaned == state_clean or state_clean in cleaned or cleaned in state_clean:
19
+ return state
20
+ # Fallback if no match is found
21
+ return resolved_state
22
+
23
+ class LocationResolver(Protocol):
24
+ async def search(self, query: str) -> List[LocationDetail]:
25
+ """Search location names and autocomplete to details."""
26
+ ...
27
+
28
+ async def reverse_geocode(self, latitude: float, longitude: float) -> LocationDetail:
29
+ """Resolve coordinates to detailed location schema."""
30
+ ...
31
+
32
+ class NominatimLocationResolver:
33
+ def __init__(self):
34
+ self.client = httpx.AsyncClient(
35
+ timeout=10.0,
36
+ headers={"User-Agent": "FarmRiskAI-App/1.0 (contact@farmrisk.ai)"}
37
+ )
38
+
39
+ async def search(self, query: str) -> List[LocationDetail]:
40
+ url = "https://nominatim.openstreetmap.org/search"
41
+ params = {
42
+ "q": query,
43
+ "format": "json",
44
+ "countrycodes": "in", # India only
45
+ "addressdetails": "1",
46
+ "accept-language": "en"
47
+ }
48
+
49
+ try:
50
+ response = await self.client.get(url, params=params)
51
+ response.raise_for_status()
52
+ results = response.json()
53
+
54
+ locations = []
55
+ for item in results:
56
+ address = item.get("address", {})
57
+
58
+ # Derive village/settlement
59
+ village = (
60
+ address.get("village") or
61
+ address.get("town") or
62
+ address.get("suburb") or
63
+ address.get("hamlet") or
64
+ address.get("neighbourhood") or
65
+ address.get("municipality") or
66
+ address.get("city")
67
+ )
68
+
69
+ # Derive district
70
+ district = address.get("district") or address.get("county") or address.get("state_district")
71
+
72
+ # Derive state
73
+ state_raw = address.get("state")
74
+ if not state_raw:
75
+ continue
76
+ state = normalize_state(state_raw)
77
+
78
+ locations.append(LocationDetail(
79
+ village=village,
80
+ district=district,
81
+ state=state,
82
+ latitude=float(item["lat"]),
83
+ longitude=float(item["lon"])
84
+ ))
85
+ return locations
86
+
87
+ except Exception as e:
88
+ # Return empty list in case of errors
89
+ print(f"Error calling Nominatim search: {e}")
90
+ return []
91
+
92
+ async def reverse_geocode(self, latitude: float, longitude: float) -> LocationDetail:
93
+ url = "https://nominatim.openstreetmap.org/reverse"
94
+ params = {
95
+ "lat": latitude,
96
+ "lon": longitude,
97
+ "format": "json",
98
+ "addressdetails": "1",
99
+ "accept-language": "en"
100
+ }
101
+
102
+ try:
103
+ response = await self.client.get(url, params=params)
104
+ response.raise_for_status()
105
+ data = response.json()
106
+ address = data.get("address", {})
107
+
108
+ village = (
109
+ address.get("village") or
110
+ address.get("town") or
111
+ address.get("suburb") or
112
+ address.get("hamlet") or
113
+ address.get("neighbourhood") or
114
+ address.get("municipality") or
115
+ address.get("city") or
116
+ "Unknown Village"
117
+ )
118
+
119
+ district = address.get("district") or address.get("county") or address.get("state_district") or "Unknown District"
120
+
121
+ state_raw = address.get("state") or "Rajasthan" # Default fallback state
122
+ state = normalize_state(state_raw)
123
+
124
+ return LocationDetail(
125
+ village=village,
126
+ district=district,
127
+ state=state,
128
+ latitude=latitude,
129
+ longitude=longitude
130
+ )
131
+
132
+ except Exception as e:
133
+ # Fallback in case Nominatim reverse lookup fails
134
+ print(f"Error calling Nominatim reverse geocode: {e}")
135
+ return LocationDetail(
136
+ village="Unknown Village",
137
+ district="Unknown District",
138
+ state="Rajasthan", # Safe fallback for Pinecone query
139
+ latitude=latitude,
140
+ longitude=longitude
141
+ )
142
+
143
+ async def close(self):
144
+ await self.client.aclose()
app/services/season.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from typing import Protocol
3
+
4
+ class SeasonResolver(Protocol):
5
+ def resolve_season(self, latitude: float, longitude: float, date: datetime = None) -> str:
6
+ """Resolve agricultural season (Kharif or Rabi) for given coordinates and date."""
7
+ ...
8
+
9
+ class MonthSeasonResolver:
10
+ """Standard month-based agricultural season detector for India."""
11
+
12
+ def resolve_season(self, latitude: float, longitude: float, date: datetime = None) -> str:
13
+ if date is None:
14
+ date = datetime.now()
15
+
16
+ month = date.month
17
+
18
+ # In India, Kharif season is June - October; Rabi season is November - May
19
+ if 6 <= month <= 10:
20
+ return "Kharif"
21
+ else:
22
+ return "Rabi"
app/services/translation.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from typing import Dict, Any, List, Optional, Tuple
3
+ from app.core.config import settings
4
+ from app.models.schemas import AdvisoryResponse, TranslationResult
5
+ from pydantic import BaseModel
6
+
7
+ class TranslationResponse(BaseModel):
8
+ translations: List[str]
9
+
10
+ from app.llm.providers import get_primary_provider, get_fallback_provider
11
+ from app.core.logging import logger
12
+
13
+ class TranslationService:
14
+ def __init__(self):
15
+ pass
16
+
17
+ async def translate_advisory(self, advisory: AdvisoryResponse, target_language: str) -> TranslationResult:
18
+ """Translate the values of the AdvisoryResponse into the target language."""
19
+ if not target_language or target_language.lower() == "english":
20
+ return TranslationResult(
21
+ data=advisory.model_dump(),
22
+ translated=True,
23
+ provider=None
24
+ )
25
+
26
+ # Step 1: Extract and flatten text fields to translate
27
+ texts_to_translate = [advisory.advisory_summary]
28
+
29
+ # Step 2: Batch translate using providers
30
+ translated_texts, provider = await self._batch_translate(texts_to_translate, target_language)
31
+
32
+ if translated_texts is None or not translated_texts:
33
+ # Translation failed completely
34
+ logger.warning("Translation failed. Returning original English advisory.")
35
+ return TranslationResult(
36
+ data=advisory.model_dump(),
37
+ translated=False,
38
+ provider=None
39
+ )
40
+
41
+ # Step 3: Reconstruct the advisory dictionary
42
+ try:
43
+ translated_data = {
44
+ "advisory_summary": translated_texts[0]
45
+ }
46
+ return TranslationResult(
47
+ data=translated_data,
48
+ translated=True,
49
+ provider=provider
50
+ )
51
+ except Exception as e:
52
+ logger.error(f"Error reconstructing translation data: {e}")
53
+ return TranslationResult(
54
+ data=advisory.model_dump(),
55
+ translated=False,
56
+ provider=None
57
+ )
58
+
59
+
60
+ async def _batch_translate(self, texts: List[str], target_language: str) -> Tuple[Optional[List[str]], Optional[str]]:
61
+ # Check if settings allow actual provider runs
62
+ # If no keys are set, fallback to mock translator
63
+ has_gemini = bool(settings.GOOGLE_API_KEY and settings.GOOGLE_API_KEY != "your_google_api_key")
64
+ has_groq = bool(settings.GROQ_API_KEY)
65
+
66
+ if not has_gemini and not has_groq:
67
+ # Mock translator: just appends language suffix
68
+ logger.info("Running translation in mock mode (no provider keys configured)")
69
+ mocked = [f"{text} [{target_language}]" for text in texts]
70
+ return mocked, None
71
+
72
+ prompt = f"""
73
+ You are a precise translator. Translate the following list of strings from English into {target_language}.
74
+
75
+ RULES:
76
+ 1. Maintain the exact order and number of elements in the list.
77
+ 2. Return a JSON object with a key "translations" containing the array of translated strings of the exact same length.
78
+ 3. Translate the meaning accurately. Do not summarize, rephrase, rewrite, or add any formatting.
79
+ 4. Keep technical agricultural terms accurate in the target language.
80
+ 5. Crucially, preserve paragraph separation (e.g. double newlines), exact wording, meaning, and sentence order. Do not regenerate the advisory, do not summarize, and do not rewrite.
81
+
82
+
83
+ Input list:
84
+ {json.dumps(texts, ensure_ascii=False)}
85
+ """
86
+
87
+ primary_provider = get_primary_provider()
88
+ fallback_provider = get_fallback_provider()
89
+
90
+ # Try primary provider
91
+ try:
92
+ result = await primary_provider.generate_json(
93
+ prompt=prompt,
94
+ schema=TranslationResponse,
95
+ temperature=0.1
96
+ )
97
+ logger.info(f"{settings.LLM_PROVIDER} translation succeeded")
98
+ return result.translations, settings.LLM_PROVIDER.lower()
99
+ except Exception as primary_exc:
100
+ logger.warning(f"Primary provider {settings.LLM_PROVIDER} failed translation: {primary_exc}")
101
+
102
+ # Switch to Groq fallback
103
+ if fallback_provider:
104
+ logger.info("Switching to Groq fallback for translation")
105
+ try:
106
+ result = await fallback_provider.generate_json(
107
+ prompt=prompt,
108
+ schema=TranslationResponse,
109
+ temperature=0.1
110
+ )
111
+ logger.info("Groq translation succeeded")
112
+ return result.translations, "groq"
113
+ except Exception as fallback_exc:
114
+ logger.error(f"Fallback provider Groq failed translation: {fallback_exc}")
115
+
116
+ return None, None
app/services/weather.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib
2
+ import httpx
3
+ from typing import Dict, Any, List
4
+
5
+ class WeatherService:
6
+ def __init__(self):
7
+ self.client = httpx.AsyncClient(timeout=10.0)
8
+
9
+ async def fetch_10day_forecast(self, latitude: float, longitude: float) -> Dict[str, Any]:
10
+ """Fetch 10-day meteorological data from Open-Meteo."""
11
+ url = "https://api.open-meteo.com/v1/forecast"
12
+ params = {
13
+ "latitude": latitude,
14
+ "longitude": longitude,
15
+ "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,relative_humidity_2m_max",
16
+ "timezone": "auto",
17
+ "forecast_days": 10
18
+ }
19
+
20
+ try:
21
+ response = await self.client.get(url, params=params)
22
+ response.raise_for_status()
23
+ data = response.json()
24
+
25
+ daily = data.get("daily", {})
26
+ time_list = daily.get("time", [])
27
+ temp_max = daily.get("temperature_2m_max", [])
28
+ temp_min = daily.get("temperature_2m_min", [])
29
+ precip = daily.get("precipitation_sum", [])
30
+ wind = daily.get("wind_speed_10m_max", [])
31
+ humidity = daily.get("relative_humidity_2m_max", [])
32
+
33
+ forecast = []
34
+ for idx, date_str in enumerate(time_list):
35
+ forecast.append({
36
+ "day": idx + 1,
37
+ "date": date_str,
38
+ "temp_max": temp_max[idx] if idx < len(temp_max) else None,
39
+ "temp_min": temp_min[idx] if idx < len(temp_min) else None,
40
+ "precipitation_sum": precip[idx] if idx < len(precip) else 0.0,
41
+ "wind_speed_max": wind[idx] if idx < len(wind) else 0.0,
42
+ "humidity_max": humidity[idx] if idx < len(humidity) else 0.0
43
+ })
44
+
45
+ # Perform rule-based risk evaluation
46
+ risks = self._assess_risks(forecast)
47
+
48
+ # Generate a hash for spatial/temporal caching
49
+ weather_hash = self._generate_weather_hash(forecast)
50
+
51
+ return {
52
+ "forecast": forecast,
53
+ "risks": risks,
54
+ "weather_hash": weather_hash
55
+ }
56
+
57
+ except Exception as e:
58
+ print(f"Error fetching weather forecast: {e}")
59
+ # Fallback mock/empty weather forecast so RAG doesn't crash the server
60
+ fallback_forecast = []
61
+ for d in range(1, 11):
62
+ fallback_forecast.append({
63
+ "day": d,
64
+ "date": f"Day {d}",
65
+ "temp_max": 30.0,
66
+ "temp_min": 20.0,
67
+ "precipitation_sum": 0.0,
68
+ "wind_speed_max": 10.0,
69
+ "humidity_max": 60.0
70
+ })
71
+ return {
72
+ "forecast": fallback_forecast,
73
+ "risks": ["No current warnings. (Weather API Offline fallback)"],
74
+ "weather_hash": "offline_fallback"
75
+ }
76
+
77
+ def _assess_risks(self, forecast: List[Dict[str, Any]]) -> List[str]:
78
+ warnings = []
79
+ has_heatwave = False
80
+ has_heavy_rain = False
81
+ has_gale = False
82
+ has_frost = False
83
+
84
+ for day in forecast:
85
+ tmax = day.get("temp_max") or 0.0
86
+ tmin = day.get("temp_min") or 0.0
87
+ prec = day.get("precipitation_sum") or 0.0
88
+ wind = day.get("wind_speed_max") or 0.0
89
+
90
+ if tmax > 42.0 and not has_heatwave:
91
+ warnings.append("Extreme Heat Alert: Maximum temperatures exceeding 42°C expected.")
92
+ has_heatwave = True
93
+ if tmin < 5.0 and not has_frost:
94
+ warnings.append("Frost Warning: Night temperatures dropping below 5°C expected.")
95
+ has_frost = True
96
+ if prec > 40.0 and not has_heavy_rain:
97
+ warnings.append(f"Heavy Rain Advisory: Daily precipitation exceeding 40mm predicted (Day {day['day']}).")
98
+ has_heavy_rain = True
99
+ if wind > 35.0 and not has_gale:
100
+ warnings.append(f"High Wind Warning: Wind gust speeds exceeding 35 km/h expected (Day {day['day']}).")
101
+ has_gale = True
102
+
103
+ return warnings
104
+
105
+ def _generate_weather_hash(self, forecast: List[Dict[str, Any]]) -> str:
106
+ """Create a hash of the forecast parameters (rounded) to check cache stability."""
107
+ hash_string = ""
108
+ for day in forecast[:5]: # Focus on next 5 days for stability
109
+ tmax = round(day.get("temp_max") or 30.0)
110
+ prec = round(day.get("precipitation_sum") or 0.0, 1)
111
+ hash_string += f"{tmax}:{prec}|"
112
+ return hashlib.sha256(hash_string.encode()).hexdigest()[:12]
113
+
114
+ async def close(self):
115
+ await self.client.aclose()
config/crops.json ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ "Rice",
3
+ "Paddy",
4
+ "Wheat",
5
+ "Cotton",
6
+ "Maize",
7
+ "Millets",
8
+ "Bajra",
9
+ "Jowar",
10
+ "Barley",
11
+ "Groundnut",
12
+ "Soybean",
13
+ "Mustard",
14
+ "Rapeseed",
15
+ "Sesame",
16
+ "Sunflower",
17
+ "Castor",
18
+ "Linseed",
19
+ "Sugarcane",
20
+ "Chickpea",
21
+ "Lentil",
22
+ "Pea",
23
+ "Black gram",
24
+ "Green gram",
25
+ "Moong",
26
+ "Urad",
27
+ "Tur",
28
+ "Pigeonpea",
29
+ "Cowpea",
30
+ "Tomato",
31
+ "Brinjal",
32
+ "Okra",
33
+ "Onion",
34
+ "Garlic",
35
+ "Potato",
36
+ "Cabbage",
37
+ "Cauliflower",
38
+ "Chilli",
39
+ "Capsicum",
40
+ "Cucumber",
41
+ "Pumpkin",
42
+ "Bottle gourd",
43
+ "Ridge gourd",
44
+ "Bitter gourd",
45
+ "Banana",
46
+ "Mango",
47
+ "Papaya",
48
+ "Guava",
49
+ "Coconut",
50
+ "Arecanut",
51
+ "Turmeric",
52
+ "Ginger",
53
+ "Black Pepper",
54
+ "Grapes",
55
+ "Pomegranate"
56
+ ]
config/states.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ "Andaman & Nicobar Islands",
3
+ "Andhra Pradesh",
4
+ "Arunachal Pradesh",
5
+ "Assam",
6
+ "Bihar",
7
+ "Chhattisgarh",
8
+ "Goa",
9
+ "Gujarat",
10
+ "Haryana",
11
+ "Delhi",
12
+ "Haryana & Delhi",
13
+ "Himachal Pradesh",
14
+ "Jammu and Kashmir",
15
+ "Jharkhand",
16
+ "Karnataka",
17
+ "Kerala",
18
+ "Ladakh",
19
+ "Lakshadweep",
20
+ "Madhya Pradesh",
21
+ "Maharashtra",
22
+ "Manipur",
23
+ "Meghalaya",
24
+ "Mizoram",
25
+ "Nagaland",
26
+ "Odisha",
27
+ "Punjab",
28
+ "Rajasthan",
29
+ "Sikkim",
30
+ "Tamil Nadu",
31
+ "Telangana",
32
+ "Tripura",
33
+ "Uttar Pradesh",
34
+ "Uttarakhand",
35
+ "West Bengal"
36
+ ]
data/extracted/ICAR.json ADDED
The diff for this file is too large to render. See raw diff
 
data/extracted/Rabi-Agro-Advisory-2021-22.json ADDED
The diff for this file is too large to render. See raw diff
 
data/extracted/inspect_icar.txt ADDED
@@ -0,0 +1,416 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Total Pages: 310
2
+
3
+ ================ Page 5 ================
4
+ (ii)
5
+ eSa bl egRoiw.kZ iqLrd ds fy, blds laikndksa] oSKkfudksa] foLrkj dk;ZdrkZvksa ,oa lHkh
6
+ lgHkkxh laLFkkvksa dks c/kkbZ nsrk gw¡] ftUgksaus [kjhQ ekSle dh [ksrh ds fy, ,d mi;ksxh]
7
+ ljy ,oa çHkkoh ijke'kZ xkbM rS;kj dh gSA eq>s iw.kZ fo'okl gS fd ;g iqLrd gekjs
8
+ vUunkrkvksa dks [kjhQ Qlyksa dh oSKkfud [ksrh gsrq l'kä ekxZn'kZu çnku djsxh]
9
+ ftlls mudh mit] vk; ,oa thou Lrj esa mÙkjksÙkj lq/kkj gksxkA
10
+ vkb,] ge lc feydj —f"k {ks= dks l'kä cuk,a] rkfd ,d le`)] vkRefuHkZj vkSj
11
+ fVdkÅ ^fodflr Hkkjr* ds lius dks lkdkj fd;k tk ldsA
12
+ vk/kkfjr fu.kZ; vkSj mUur rduhdksa dh tkudkjh lqyHk :i esa çnku djsxhA ;g ç;kl
13
+ u dsoy [ksrh dks vf/kd ykHkdkjh cukus esa enn djsxk] cfYd ^fodflr Hkkjr &2047* ds
14
+ gekjs jk"Vªh; ladYi dks Hkh l'kä vk/kkj çnku djsxkA
15
+ t; toku] t; fdlku] t; foKku] t; vuqla/kkuA
16
+ Hkkjr ekrk dh t;A
17
+
18
+
19
+ ================ Page 10 ================
20
+ I congratulate the scientists, editors, and extension professionals involved in
21
+ compiling this publication. I also encourage ϐield functionaries and stakeholders to
22
+ use this resource extensively to support farmers during the Kharif season.
23
+ Let us continue to work together to transform Indian agriculture into a more
24
+ resilient, productive, and sustainable sector.
25
+ This book compiles region-speciϐic advisories that reϐlect the latest research and
26
+ ϐield-level insights from ICAR institutes, Agricultural Universities, and Krishi Vigyan
27
+ Kendras (KVKs) across the country. It is designed to serve as a practical guide for
28
+ extension personnel, progressive farmers, farmer producer organizations (FPOs),
29
+ and agri-entrepreneurs engaged in Kharif crop planning and management. The
30
+ advisories encompass timely, location-speciϐic, and scientiϐically backed
31
+ recommendations and cover major crops, livestock, and ϐisheries, with a focus on
32
+ improving productivity, proϐitability, and sustainability.
33
+ At ICAR, we believe that technology transfer and knowledge dissemination are as
34
+ vital as research itself. The role of the extension system, particularly KVKs, has been
35
+ pivotal in bridging the gap between research institutions and farming communities.
36
+ This book is a result of collaborative efforts between research and extension systems
37
+ and exempliϐies our commitment to farmer-centric innovation.
38
+ (Rajbir Singh)
39
+ India's agricultural landscape is rich in diversity, yet increasingly inϐluenced by
40
+ climatic uncertainties, resource constraints, and changing socio-economic
41
+ dynamics. To address these challenges effectively, there is a growing need to equip
42
+ farmers with timely, localized, and scientiϐically validated information that enhances
43
+ their decision-making capacity. This compilation “ICAR Kharif Agro-Advisories for
44
+ Farmers 2025” is a well-conceived effort in this direction.
45
+ Dr. Rajbir Singh
46
+ Deputy Director General (Agricultural Extension), ICAR
47
+ (vii)
48
+ PREFACE
49
+
50
+
51
+ ================ Page 20 ================
52
+ Andhra Pradesh
53
+ Godavari Zone: Swarna, Indra, Amara, Bhima, Pushyami, Ksheera, Sravani,
54
+ Maruteru Samba & Masoori, MTU 1212, 1280, 1281, 1293, 1310, 1321, 1318, 1232,
55
+ Varieties
56
+ Late Nursery Transplanting: Swarna, Indra, Amara, Maruteru Masoori, MTU Rice
57
+ 1318, Bapatla Masoori, Bhavapuri Sannalu, Teja, Panduranga, MCM Rice 103. BPH
58
+ (Brown Plant Hopper) Infestation-Prone Areas: Krishnaveni, Indra, Amara,
59
+ Maruteru Samba & Masoori, MTU 1271, Bhavapuri Sannalu, Teja, Sasya, BPT Rice
60
+ 2846, Nellore Siri.
61
+ CEREAL CROPS
62
+ North Coastal Zone: Pushkala, Srisatya, Cotton Dora Sannalu, Tarangini, Nellore
63
+ Masoori, Nellore Dhanyarashi, NLR Rice 3238.
64
+ HAT Zone: Cotton Dora Sannalu, Tarangini, Chandra, Pushkala, Srisatya, NLR 3238.
65
+ Krishna Zone: Swarna, Indra, Amara, Krishnaveni, Varam, MTU Rice 1318, MTU 1232,
66
+ Panduranga, MCM Rice 103, Samba Masoori, Akshaya, Bhavapuri Sannalu, Bapatla
67
+ Masoori, Nellore Siri, Teja, Bhavathi, Sasya, BPT Rice 2841 & 2846, Maruteru Samba,
68
+ Maruteru Masoori, MTU Rice 1271 & 1278, Nellore Sona, Nellore Siri, NLR 3238.
69
+ Flood-Prone Areas: Amara, Indra, Bhima, MTU Rice 1232 & 1318. Saline Soils: Indra,
70
+ Panduranga, MCM Rice 103.
71
+ Paddy
72
+ Late Planting: Cotton Dora Sannalu, Tarangini, MTU 1293, Nellore Sona, Nellore
73
+ Masoori, Nellore Siri, NLR 3238.
74
+ Drought-Prone Areas: Cotton Dora Sannalu, Vijetha, Tarangini, Chandar, MTU 1293,
75
+ NLR 3238.
76
+ Southern Zone: Sweta, Bharani, Somasila, Nellore Masoori, Nellore Dhanyarashi,
77
+ Nellore Sugandha, NLR 3238, Cotton Dora Sannalu, Tarangini, Chandra.
78
+ 5
79
+
80
+
81
+ ================ Page 30 ================
82
+ Andhra Pradesh
83
+ Stage-Based Feeding: Implement a feeding strategy based on the pig's life stages for
84
+ optimal growth.
85
+ Sheep Farming
86
+ Health & Disease Control: Implement regular vaccination and deworming
87
+ schedules and provide practical training on poultry health management.
88
+ Young Lamb Management: Ensure colostrum feeding, deworming, and timely
89
+ vaccinations to improve lamb health and reduce mortality.
90
+ Heifers & Dairy Animals: Provide mineral supplementation, hormonal
91
+ interventions for repeat breeding, and manage endometritis to improve
92
+ reproductive efϐiciency.
93
+ Backyard Poultry Farming
94
+ Housing & Welfare: Use low-cost, secure enclosures for bird safety, and improve
95
+ transportation practices to reduce stress.
96
+ Feeding Strategies: Promote Azolla cultivation as a cost-effective, high-protein feed
97
+ supplement and use locally available feed ingredients to cut costs.
98
+ Swine Farming
99
+ Breed Improvement: Replace non-descriptive breeds with the SVVU T-17 breed to
100
+ enhance productivity.
101
+ Dairy Buffaloes: Promote clean milking practices, calcium supplementation to
102
+ prevent milk fever, and strategies to avoid ketosis and ruminal acidosis.
103
+ Breed Improvement: Replace native low-productive breeds with high-
104
+ performance breeds like Rajasri and Gramapriya.
105
+ Feeding Strategy: Avoid feeding swill and provide balanced concentrate feed to
106
+ improve reproductive performance.
107
+ Fodder Supply: Promote high-yielding fodder varieties, use crop residues, and
108
+ implement silage making to combat fodder shortages. Provide green fodder during
109
+ the day and roughages at night to manage heat stress.
110
+ Breeding Stock: Replace non-descriptive breeds with the Macherla breed, which is
111
+ well-adapted to local conditions.
112
+ Supplementary Feeding & Genetic Diversity: Provide supplementary feeding
113
+ during pregnancy and exchange rams for improved genetic diversity.
114
+ Sub soiler results cutting soil strata up to a depth of 40 to 75 cm and creates more
115
+ space for rain water entry and storage. In-situ water conservation and to sustain
116
+ crop in prolonged dry spell. Easily operated by any 35 to 45 hp tractor. It is useful in
117
+ dryland agricultural crops. Use Trampler for green manure mixing, it cuts and
118
+ presses green manure leaves into the soil, helping them decompose efϐiciently and
119
+ FARM MACHINERY & TOOLS
120
+ Agricultural Crops
121
+ Health Management: Ensure regular deworming and vaccination schedules, and
122
+ administer iron dextran injections to piglets to reduce mortality.
123
+ 15
124
+
125
+
126
+ ================ Page 40 ================
127
+ Assam
128
+ Transplanting: One ploughing should be given at least 21 days prior to
129
+ transplanting. An irrigation for land soaking should be applied before preparatory
130
+ tillage. The ϐinal puddling should be done 4-5 days prior to transplanting. One
131
+ irrigation should be applied before ϐinal puddling. 25 days old seedlings of short and
132
+ medium duration varieties should be transplanted by maintaining a row-to-row and
133
+ plant-to-plant spacing of 20 cm × 15 cm. In case of long duration varieties, 35-40 days
134
+ old seedlings are to be transplanted at 25 cm × 15 cm spacing. It is advised to plant 2-
135
+ 3 seedlings per hill, at a depth of 4-5 cm, for all varieties.
136
+ Nutrient management: Well rotten FYM or compost @ 10 t/ha has to be applied during
137
+ ϐield preparation. In addition, for semidwarf varieties, 60 kg N/ha, 20 kg P₂O₅/ha and 40
138
+ kg K₂O/ha and for tall varieties, 20 kg N/ha, 10 kg P₂O₅/ha and 10 kg K₂O/ are to be
139
+ applied in areas with moderate fertility level. In case of poor soil, the rates of fertilizers
140
+ may be required to increase to the extent of 60:30:30 kg/ha N, P₂O₅ and K₂O respectively.
141
+ Half of urea and whole of super phosphate and muriate of potash should be applied at the
142
+ time of ϐinal puddling. Of the remaining part of urea, half at tillering stage i.e. 20-30 days
143
+ after transplanting and other half at panicle initiation stage should be applied. Two
144
+ weedings should be given with paddy weeder or hoe at 20 and 40 days after
145
+ transplanting. For weed control, pretilachlor @ 0.75 kg/ha or anilofos is to be applied @
146
+ 0.4 kg/ha at 3 days after transplanting. In sali rice, application of 5 cm irrigation water 3
147
+ days after disappearance of ponding water is recommended in medium and heavy soils.
148
+ In rainfed kharif rice, height of bunds should be 30 cm to retain rainwater.
149
+ Plant protection measures: To control the rice stem borer, whorl maggot, gall
150
+ midge and leaf folder, spray ϐipronil 5SC @1.5-2 ml/l of water. To control thrips and
151
+ plant hopper, spray imidacloprid 70 WG @0.3 g/l of water or thiamethoxam 25
152
+ WG@0.03 g/l of water. Against Rice Hispa, spray lamda-cyhalothrin 5 EC @ 12.5g
153
+ /per ha. To control rice pests, erect 50 'T'-perches per ha 2 ft (60 cm) above crop
154
+ canopy as roosting site for insectivorous birds, which are to be removed before
155
+ ϐlowering in order to prevent activity of granivorous birds. To prevent rice blast,
156
+ spray hexaconazole 5EC @ 2g/l of water at tillering stage (40-55 days after sowing)
157
+ and subsequently give two more sprays of ediphenphos @ 1ml/l of water, one at
158
+ panicle initiation stage and the other when the tip of the panicle just comes out.
159
+ Management of direct seeded late Sali: Field should be prepared just after
160
+ recession of ϐlood by ploughing, cross ploughing and laddering to bring it to a puddle
161
+ Seedlings of Flood Tolerant Rice variety-
162
+ Ranjit sub-1 during ϐlood
163
+ Installation of T Perch in Rice Field
164
+ 25
165
+
166
+
167
+ ================ Page 50 ================
168
+ Bihar
169
+ Nutrient management involves basal application of 20:40:30 kg/ha N:P₂O₅: K₂O
170
+ (through urea, SSP, and MOP), with an additional 250 kg/ha gypsum at ϐlowering for
171
+ better pod development. In zinc-deϐicient soils, apply 25 kg ZnSO₄/ha. Provide life-
172
+ saving irrigation at ϐlowering and pod development stages if monsoon rains are
173
+ erratic. Proper soil moisture during pegging is crucial for good pod formation. Major
174
+ pests and diseases include leaf spot, collar rot, aphids and bud necrosis virus. Leaf
175
+ spots can be managed by spraying Mancozeb 75 WP @2.5 g/L while sucking pests
176
+ should be controlled using Imidacloprid 17.8 SL 0.3 ml/L. Harvest should be done
177
+ when most leaves yellow and pods mature internally (dark shell markings). Avoid
178
+ delay in harvesting to prevent aϐlatoxin contamination. Under recommended
179
+ practices, yields of 15-18 q/ha can be achieved with potential to exceed 25 q/ha
180
+ under favourable conditions. Storing of oilseed in new bags after proper sun drying
181
+ and treatments. Farmers are advised to purchase the inputs like seed, fertilizers and
182
+ fungicide for seed treatment well in advance.
183
+ FRUIT & VEGETABLE CROPS
184
+ Farmers are advised to grow fruit crops like Mango, Litchi, Guava, Papaya and Citrus
185
+ fruit. Irrigate mango and litchi plants at weekly interval for proper fruit growth and
186
+ development. To control premature fruit dropping in mango and litchi, farmers are
187
+ advised to spray Planoϐix@4ml/10 L of water at an interval of 10-12 days. Fruit
188
+ cracking is major problem in Litchi, it can be prevented with regular irrigation,
189
+ mulching and fresh water spraying during dry spells. For better fruit development
190
+ farmers are advice to apply n-triacontanol (Miraculan) @0.5 ml/L and moisture
191
+ conservation can be done by the practice of basin mulching using straw/grasses. For
192
+ control of mango mealy bug spraying of Dimethoate 30EC @1.0 ml/L or Neem oil
193
+ @5ml/L twice at 10-12 days interval followed by 2 spray of Planoϐix @4 ml/10 L
194
+ water at 10-12 interval to check fruit drop.
195
+ For fruit borer in mango, spray chlorpyriphos 50 % EC+Cypermethrin 5% EC @2.5
196
+ ml/L water or Lamda-cyhalothrin 5% EC @0.5 ml /L water or Indoxacarb 14.5 SC
197
+ @0.5 -1 ml /L water. For fruit ϐly control, spray Deltamethrin 2.8 EC @1 ml/L water or
198
+ Spinoza 45 SC @1 ml/water at 15 days internal. For setting of pheromone traps in
199
+ mango orchards, 10 traps per ha is advised in Mango orchards. For Mango hopper,
200
+ spray with Imidacloprid 17.8 SL @0.5- 1 ml/L water at 15 days internal. To control
201
+ Field preparation
202
+ 35
203
+
204
+
205
+ ================ Page 60 ================
206
+ Chhattisgarh
207
+ Poultry
208
+ During this hot summer provide good ventilation, maintain proper shed temperature
209
+ and make availability of ample cold drinking water to maintain body temperature of
210
+ birds. Use anti-stress vitamins such as vimeral to increase immunity and to reduce
211
+ climate stress. Local poultry farmers are advised to feed concentrate mix with local
212
+ available grain in ratio of 2:1 in the diet to maintain the health and growth of poultry
213
+ birds during the scarcity of quality feed. The moisture and quality of the litter materials
214
+ in poultry shed need to be maintain to prevent coccidiosis infestation. All the
215
+ equipment in the shed should be disinfected using hot water and with any other proper
216
+ disinfectant. Disinfect the premises of poultry houses with 1% sodium hypochlorite
217
+ and inhibit the entry of outsider to the poultry houses and premises. Poultry farmers
218
+ are advised to vaccinate the chicks at the age of 5-7 days against Ranikhet disease.
219
+ Ensure vaccination of chicks against Ranikhet disease, if not done earlier.
220
+ Deworm the birds using piperazine before onset of monsoon season. After removing
221
+ adult birds from the previous ϐlock, thoroughly clean and disinfect the poultry shed.
222
+ Maintain a 3 to 4-week gap between two ϐlocks to ensure ϐloor and environment
223
+ sanitation (known as downtime). Use a brooder guard to create a circular space about 5
224
+ feet in diameter; this is sufϐicient for about 200 to 250 chicks. Place a heat source (such
225
+ as infrared bulb, regular bulb, or gas brooder) in the center of the circle. Spread a 2-inch
226
+ thick layer of straw or wood shavings inside the circle and cover it with old newspaper.
227
+ Arrange feeders and drinkers in a circular pattern like the spokes of a wheel.
228
+ FOOD AND NUTRITION
229
+ During the summer, farmers and farm women should prioritize staying hydrated,
230
+ consuming seasonal fruits and vegetables, and maintaining a balanced diet rich in protein
231
+ and ϐiber. It is important to remain vigilant about food safety during the hot season and focus
232
+ on nutrient-rich, easily digestible meals. Heat can cause dehydration, so drink water
233
+ frequently throughout the day, not just when you feel thirsty. Avoid sugary drinks, they can
234
+ lead to dehydration and other health issues. Summer offers a variety of nutritious options
235
+ like watermelon, mangoes, cucumbers, and tomatoes. Always choose local produce, it is
236
+ often fresher and more affordable. Include protein rich foods like lentils, beans, chickpeas,
237
+ and paneer are excellent sources of protein. Eat ϐiber-rich foods like whole grains,
238
+ vegetables, and fruits support digestion and help prevent constipation. Prefer light and
239
+ refreshing foods, salads and curd can be easier to digest during summer. Avoid heavy, fried,
240
+ or processed foods. These can be hard to digest and may contribute to dehydration.
241
+ Pit for planting of fruit sapling
242
+ Vaccination of animals
243
+ 45
244
+
245
+
246
+ ================ Page 70 ================
247
+ Gujarat
248
+ For millet cultivation use high-yielding, region-appropriate varieties such as GPU
249
+ 28, CO 13, and PR 202 for ϐinger millet; OLM 203 and OLM 205 for little millet; and
250
+ SiA 3085 and SiA 3156 for foxtail millet. The best sowing window is from mid-June
251
+ to mid-July, coinciding with the onset of the monsoon. Land preparation involves
252
+ ploughing 2-3 times to achieve a ϐine tilth, followed by levelling the ϐield to ensure
253
+ proper drainage. The recommended seed rate for line sowing is 8-10 kg per
254
+ hectare, with row spacing of 22.5-30 cm and plant-to-plant spacing of 8-10 cm. Use
255
+ Thiram or Carbendazim at 2 g/kg for seed treatment and to protect against seed-
256
+ borne diseases. Fertilizer application should include 40 kg of nitrogen, 20 kg of
257
+ phosphorus, and 20 kg of potassium per hectare. Apply full dose of phosphorus
258
+ and potassium, along with half of the nitrogen, at sowing, and the remaining
259
+ nitrogen at 30 days after sowing (DAS). Weed management can be achieved with
260
+ the ϐirst weeding at 15-20 DAS and the second at 30-35 DAS, using inter-cultivation
261
+ or a pre-emergence herbicide like Pendimethalin at 1.0 kg/ha.
262
+ During the monsoon, ensure good drainage to prevent water stagnation. Intercultural
263
+ operations should include keeping the ϐield weed-free and mulching around the base of
264
+ The ideal planting time for bananas is during June-July, coinciding with the onset of
265
+ the monsoon. Recommended varieties include Grand Naine (G9), Dwarf Cavendish,
266
+ Basrai, Shrimanti, and BRS-1, depending on local suitability. For land preparation,
267
+ deep ploughing and leveling are necessary, followed by digging pits of 60 x 60 x 60
268
+ cm. These pits should be ϐilled with 10-15 kg of farmyard manure (FYM) and soil
269
+ before planting. Spacing for G9 plants should be maintained at 1.8 m x 1.5 m or 2 m x 2
270
+ m, depending on the irrigation method and variety. Planting material should consist
271
+ of tissue-cultured plants or healthy sword suckers that are disease-free. Apply FYM
272
+ at 50 kg per plant per year. The recommended fertilizer dose per plant annually
273
+ includes 200-250 g of N, 60-80 g of P₂O₅, and 200-300 g of K₂O, applied in 4-5 splits,
274
+ with the ϐirst dose applied after planting and subsequent doses at two-month
275
+ intervals. Irrigation management should focus on water efϐiciency, ideally through
276
+ drip irrigation.
277
+ Banana
278
+ FRUIT & VEGETABLE CROPS
279
+ Finger Millet, Little Millet and Foxtail Millet
280
+ MILLETS
281
+ Millets are mostly rainfed, but supplemental irrigation may be necessary during
282
+ ϐlowering and grain ϐilling if rainfall is insufϐicient. For plant protection, control shoot
283
+ ϐly by applying Carbofuran at 10 kg/ha in furrows at sowing. To manage blast or leaf
284
+ spot diseases, spray Mancozeb at 2 g/L at the onset of symptoms. Crop rotation and
285
+ ϐield hygiene are also important practices for disease management. Harvesting
286
+ should occur when the grains are mature and hard, typically 90-110 days after
287
+ sowing, depending on the species. Ensure the grains are well-dried before threshing
288
+ and storage to prevent mold. Additionally, millets can be intercropped with pulses
289
+ such as green gram and black gram, or with oilseeds like sesame, to improve returns
290
+ and enhance soil fertility.
291
+ 55
292
+
293
+
294
+ ================ Page 80 ================
295
+ Haryana & Delhi
296
+ Fertilizer use: Apply 80-100 Kg N per ha for American Cotton and 50 kg N for desi
297
+ cotton along with 30 kg P₂O₅/ha. In case of hybrid cotton, 150 kg N, 60 kg P₂O₅, 60 kg
298
+ K₂O and 25 kg ZnSo₄ per ha has been recommended. Apply 1/3 dose of N, full dose of
299
+ P₂O₅, full dose of potash and full dose of ZnSo₄ at time of sowing. Remaining dose of N
300
+ should be applied in two equal splits at square formation and ϐlowering stage. In
301
+ sandy soil the research results have revealed that 90 per cent dose of fertilizer
302
+ through soil application and 10 per cent through foliar spray at the boll development
303
+ stage gave the highest seed cotton yield. Inoculation of cotton seed with C2, M4 and
304
+ Azospirillum culture resulted into saving of 25-27 kg N/ha.
305
+ Seed rate: Use 15-20 kg delinted seed of improved American cotton varieties for
306
+ sowing in one ha area. 12.5 kg of seed/ha is required for Desi cotton varieties. Seed
307
+ rate of 3-3.750 kg /ha is required for American cotton hybrids and desi cotton
308
+ hybrids. Seed rate of 2.125 kg/ha is required for Bt Cotton hybrids.
309
+ Seed treatment and seed soaking: Before sowing, seed should be dipped in water
310
+ up to 5-6 hours for better germination. Treat the seed with 5 gm Emisan, 1 gm
311
+ streptocyclin and 1 gm succinic acid in 10 litres of water. In termite affected areas
312
+ treat the seed with 10 ml chlorpyriphos apart from above mentioned chemicals. Seed
313
+ treatment with carbendazim @ 2 gm/kg in the root rot affected areas is essential.
314
+ Seed treatment with Imidacloprid @ 7.5 gm/kg seed to escape the crops from
315
+ sucking pests up to 40-60 days.
316
+ Mango
317
+ Method of Sowing: Sow the crop in lines 67.5 cm apart with a cotton sowing drill or
318
+ cotton planter and plant to plant spacing of 60 cm or row to row spacing of 100cm
319
+ and plant to plant spacing of 45 cm. Sowing should be done at a depth of 4-5 cm.
320
+ In horticultural crops at fruiting stage such as mango, while carrying out ϐield operations
321
+ related to nutrient sprays and crop protection adequate precautions in handling of inputs,
322
+ Varieties and hybrids: Improved American cotton varieties: HS-6, H-1117, H-1126, H-
323
+ 1098 Improved, H-1236, H-1300. Recommended improved hybrids- HHH-223, HHH-
324
+ 287. Desi Cotton varieties- HD-123, HD-324, HD-432. Desi Cotton hybrid - AAH-1
325
+ results in yield reduction. For all desi cotton varieties/hybrids, best sowing time is
326
+ mid-April to 1ƗƘ week of May. The mortality of seedling is very high during May &
327
+ June.
328
+ Plant protection: Leaf sucking pest & Leaf Curl Virus disease: seed should be treated
329
+ with 4 g Thiamethaxom 70 WS/kg seed before sowing. Close monitoring on white ϐly
330
+ host plants like vegetables, ϐlower plants, weeds & unwanted plants. These host
331
+ plants must be uprooted & burnt from time to time. Some vegetables like Ladies
332
+ Finger, Brinjal, Tomato & Chilli also act as host plants for whiteϐly, accordingly, as per
333
+ need these must be sprayed with Thiamethaxom 25 WG @ 0.5g/litre water. Bacterial
334
+ blight: seed should be treated with soaking in 1 g Streptomycin or 10 g
335
+ Plantomycin/10 litre water before sowing. Spray Thiamethaxom 25 WG @ 100g/ha
336
+ and Profenophos 50 EC @1250 ml/ha for sucking pest. Proclaim (emamectin
337
+ benzoate) 5 SG @250g/ha for pink, spotted and American bollworm.
338
+ FRUIT & VEGETABLE CROPS
339
+ 65
340
+
341
+
342
+ ================ Page 90 ================
343
+ Himachal Pradesh
344
+ and blight diseases. Drip irrigation with mulching should be used for better crop
345
+ management and conservation of water. In Kharif tomato production system,
346
+ nursery raising may be started in the month of June and transplanting should be
347
+ completed by July. In areas where blossom end rot is a problem apply calcium
348
+ chloride @ 5g/ L of water as foliar application.
349
+ For the management of early blight, buckeye rot and fruit rot diseases, apply copper
350
+ oxychloride @ 3g/ L, Ridomil MZ @ 2.5 g/L and Mancozeb 45 @ 2.5 g or Kavach @ 2g/ L
351
+ respectively as and when symptoms appear. For the management of fruit borer, apply
352
+ Profenophos @ 1ml/ L or Chlorpyriphos @2ml or Cypermethrin 10EC @ 1ml per litre of
353
+ water .
354
+ Bell pepper
355
+ Cultural Practices along with Fertilizer application: Elite open pollinated varieties viz.,
356
+ California Wonder, Solan Bharpur, Solan Shakti should be planted up to end of April to get
357
+ higher yields. Optimum spacing of 60 cm may be maintained between row to row and 45
358
+ cm between plant to plant while transplanting of open pollinated varieties or hybrids.
359
+ Apply total FYM (250q/ha), Single Super Phosphate (475kg/ha), Muriate of Potash
360
+ (90kg/ha) with half of Urea (200kg/ha) at the time of ϐield preparation. Remaining half of
361
+ urea should be applied in two equal split doses one after one month of sowing and
362
+ another before ϐlowering. Under protected conditions, capsicum (coloured/ green) can
363
+ be transplanted by May end. Apply FYM @ 120t/ha and NPK mixture @ 50kg/ ha before
364
+ transplanting. Urea 11g, SSP 30g and MOP 8.4g per square meter should also be applied to
365
+ get higher yield. Apply soluble fertilizers like Polyfeed (19:19:19) @150 kg/ha twice a
366
+ Demonstration of mulch with drip irrigation at farmer's ϐields in Solan disrict
367
+ Demonstration on coloured bell
368
+ peppers under protected conditions
369
+ Solan Shakti variety of bell pepper
370
+ 75
371
+
372
+
373
+ ================ Page 100 ================
374
+ Jammu & Kashmir
375
+ oxychloride 3 g/L or apply bleaching powder @ 10 kg/acre; for leaf blight, spray
376
+ propiconazole @ 0.1% at disease onset.
377
+ Use 6-8 kg seed/acre; varieties include Pant U-19, Uttara, Pant U-31, NDU 99-3, KUG
378
+ 479; spray chlorpyriphos 20EC @ 2 ml/litre or imidacloprid 17.8 SL @ 0.3 ml/litre to
379
+ manage hairy caterpillar and whiteϐly.
380
+ Use 800 g-1 kg seed/acre in July; for pests like hairy caterpillars and whiteϐly, spray
381
+ chlorpyriphos 20EC @ 2 ml/litre or imidacloprid 17.8 SL @ 0.3 ml/litre.
382
+ Intercropped with maize (row spacing 75 × 20 cm); sow 9.5 kg seed/acre; treat with
383
+ copper oxychloride @ 3g/kg; control aphids and anthracnose with chlorothalonil @
384
+ 2 g/L spray.
385
+ Mash (Black Gram)
386
+ Use 6-8 kg seed/acre; varieties include Pant Mung-6, Pusa Vishal, SML 668, Pusa
387
+ 0672, Satya; manage YMV and sucking pests with resistant varieties and spray
388
+ dimethoate 30 EC @ 1 ml/litre or thiamethoxam @ 0.03%.
389
+ Hybrid Jowar (Sorghum)
390
+ Rajmash (Bhaderwah Local/Chinta Selection)
391
+ FODDER CROPS
392
+ Use 2 kg of hybrid seed/acre, apply 40:24:10 kg NPK/acre, and manage pests like leaf
393
+ caterpillars and weevils with chlorpyriphos 1.5% D @ 10 kg/acre.
394
+ OILSEED CROPS
395
+ Sesame (Til)
396
+ PULSE CROPS
397
+ Moong (Green Gram)
398
+ COMMERCIAL CROPS
399
+ Bajra (Pearl Millet)
400
+ Sow 5 kg seed/acre with 20:12:6 kg NPK; for insect pests like shoot ϐly and stem
401
+ borer, apply cartap hydrochloride 4G @ 8 kg/acre in central whorls at 10-20 DAS.
402
+ Sugarcane
403
+ Saffron requires 20-24 q corms/acre, with fertigation using 8 kg of Urea, 24 kg of DAP,
404
+ and 13 kg of MOP, and management involves deep ploughing, raised beds, disease-
405
+ free corms, and rodent control.
406
+ Saffron
407
+ Bajra and Jowar are important kharif fodder crops, with varieties like Giant Bajra,
408
+ FBC-16, PCB 164 for Bajra, and MP Chari, Haryana Chari-260, Proagro Chari (SSG-
409
+ 998) for Jowar, suitable for both irrigated and rainfed conditions.
410
+ Grown in parts of Jammu and Kathua, high-yielding sugarcane varieties like COJ-64,
411
+ COJ-81, and CO-1148 are preferred. Manage termites with chlorpyriphos, borers
412
+ with cartap, and diseases like red rot and grassy shoot using resistant varieties and
413
+ appropriate sprays.
414
+ 85
415
+
416
+
data/extracted/inspect_rabi.txt ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Total Pages: 755
2
+
3
+ ================ Page 5 ================
4
+ o”kZ 2022 rd Ñ”kdksa dh vk; dks nksxquh djus ds egRodka{kh
5
+ y{; dh izkfIr ds fy;s Ñf”k {ks= esa izlaLdj.k] Hk.Mkj.k] Ñf”kd
6
+ mRiknu laxBuksa dk xBu dk xBu ,oa ,xzh bUÝkLVªDpj Q.M dh
7
+ ?kks”k.kk vkRefuHkZj Hkkjr fe’ku ds varxZr dh xbZ gSA blls vxys dqN
8
+ o”kks± esa Ñf”k dks csgrj xfr iznku dh tk ldsxhA
9
+ Hkkjrh; Ñf”k vuqla/kku ifj”kn us dksfoM egkekjh ls mRiUu
10
+ pqukSfr;ksa dks xaHkhjrk ls ysrs gq, ykWdMkmu ls izHkkfor fdlkuksa dks
11
+ bl eqf’dy le; esa gj laHko O;ogkfjd lek/kku o lq>ko miyC/k
12
+ djkus esa lfØ; Hkwfedk fuHkkbZ gSA fu/kkZfjr y{;ksa dh izkfIr ds fy,
13
+ Hkkjrh; Ñf”k vuqla/kku ifj”kn ,oa jk”Vªh; Ñf”k vuqla/kku iz.kkyh
14
+ ds vU; ?kVdksa dk ;ksxnku vfregRoiw.kZ gSA blh ifjizs{; esa jch
15
+ 2021&22 ds fy, Hkkjrh; Ñf”k vuqla/kku ifj”kn }kjk Ñf”k o vU;
16
+ fo/kkvksa ij vk/kkfjr lykg ¼,Mokbtjh½ fodflr dj Ñ”kdksa dks lgh
17
+ le; esa miyC/k djkus dk ;g iz;kl ljkguh, gSA eq>s iw.kZ fo’okl
18
+ gS fd fdlkuksa ds fy, ;g tkudkjh dkQh mi;qDr ,oa ykHkdkjh
19
+ gksxh vkSj mUgsa blls Ñf”k ds {ks= esa mfpr ykHk izkfIr ds lkFk gh
20
+ lkFk mudh vkenuh nksxquh djus esa enn feysxhA
21
+
22
+
23
+ ================ Page 10 ================
24
+ xi
25
+ PRefACe
26
+ The COVID-19 Pandemic has created unprecedented situation
27
+ throughout the world. It has been more than a year and half since
28
+ COVID-19 Pandemic threatened the human life by killing millions
29
+ across the globe. During this crisis the economies of countries suffered
30
+ heavily but agriculture sector showed lot of resilience. Growth of
31
+ agriculture sector was not thwarted in India and it continued to set
32
+ records of food grains production. Krishi Vigyan Kendras (KVKs), in
33
+ tune with the policy directions and guidelines of Government of India
34
+ and Indian Council of Agricultural Research (ICAR), reached farmers
35
+ across the nation using Information and Communication Technology
36
+ to issue farm advisories. KVKs also provided necessary input support
37
+ by making available seeds and planting materials and agro advisories for
38
+ farmers in regional languages.
39
+ The Agricultural Technology Application Research Institutes
40
+ (ATARIs) throughout India, collaborated with Research Institutes
41
+ and State Agricultural Universities, State Agriculture and other Line
42
+ Departments to develop Rabi advisories for the benefit of farming
43
+ community across the country. The advisories include scientifically
44
+ proven best practices related to crops, horticulture, livestock and
45
+ fisheries to be followed by the farmers to obtain optimum production
46
+ levels with maximum profit during Rabi season 2021-22.
47
+ I am very hopeful that these advisories will help farmers and
48
+ farmers’ groups in appropriate decision making in maximizing yields
49
+ and enhancing farm income. I congratulate the team for bringing out
50
+ such important compilation in real time frame for benefit of farming
51
+ community of country.
52
+ (A. K. Singh)
53
+ Deputy Director General (Agricultural Extension)
54
+ ICAR, New Delhi
55
+
56
+
57
+ ================ Page 20 ================
58
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
59
+ 10
60
+ advised to the farmers. For chickpea use of high yielding and pest
61
+ tolerant varieties JG 14, JG 11, JG 130, JG 16, JAKI 92-18, JG 63, JG
62
+ 412, JG 226, JG 36, PBG 1, BG 267, GNG146, RVG 201, RVG 202,
63
+ JGK 1, JGK 2, JGK 3, KAK 2, GG-1 (Gujarat Gram-1), Vaibhav,
64
+ JG-14, Indira Chana-1, JSC-55, JSC-56, BGD-128 (Pusa Shubhra),
65
+ IPCK-2002-29, IPCK-2004-29, IPC-2066-77 and JGG 1 has been
66
+ advised. For better yield and returns from Lentil cultivation of
67
+ improved varieties Lens-4076, IPL-81 (Noori), JL-3, IPL-316, RVL
68
+ 11-6, L-4717 (Pusa Ageti Masoor), RKL 14-20 (Kota Masoor-2),
69
+ L-4727, Kota Masoor-1 (RKL-607-1), and Chhattisgarh Masoor-1
70
+ is advised. For more returns from Linseed cultivation, sowing of
71
+ high yielding multiple resistant varieties viz., JLS 66, JLS 73, JLS 95,
72
+ RLC 148, RLC 164, JLS 79, R-552, Kiran, T-397, Padmini, Shekhar,
73
+ Indira Alsi-32, Kartika, Deepika, Indravati Alsi, RLC-133, RLC-
74
+ 143, RLC-153 and RLC 167 is advised. Sowing of mustard varieties
75
+ Pusa Tarak, Pusa Mahak, Pusa Agrani, Pusa Jai Kisan (BW-902),
76
+ Pusa Bold, Kranti (PR-15), Vardan (RK 1467), Varuna (T-59),
77
+ Chhattisgarh Sarson-1, Indira Toria-1 and Pusa Aditya are advised.
78
+ High yielding and high sugar varieties viz. CoJN 86-600, CoJN
79
+ 86-141, CoJN 9505, COC 671, Co 94008 (Shyama), CoM 88121
80
+ (Krishna), Co 86032 (Nayana) of sugarcane is recommended for
81
+ cultivation.
82
+
83
+ Farmers are advised to follow improved practices for cultivation of
84
+ fruits (mango, guava, pomegranate, ber) and vegetable (Tomato,
85
+ Cauliflower, Cabbage, Onion, Vegetable Pea, Chilli, Potato) crops
86
+ during Rabi season.
87
+
88
+ Advisory for management of Animal Husbandry enterprises viz.
89
+ dairy, poultry, fisheries, and sheep & goat during Rabi season is
90
+ also given to the farmers of the zone.
91
+
92
+ In Zone-X (Andhra Pradesh, Telangana, Tamil Nadu &
93
+ Pondicherry) Rice, maize, green gram, black gram, bengal
94
+ gram, groundnut, sesame, and sugarcane are major crops. The
95
+ major improved varieties of rice advised for cultivation during
96
+
97
+
98
+ ================ Page 30 ================
99
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
100
+ 21
101
+ Fruit crops
102
+ Apple
103
+
104
+ Prepare tree basins and apply recommended dose of FYM (100 kg/
105
+ plant), Nitrogen (1.5 kg Urea per tree basin), Phosphorous (SSP
106
+ 2 kg per plant) and potash (MOP 1.7 kg per plant) for plants of
107
+ age more than 10 years. Complete dose of Potash and phosphorous
108
+ should be given at the Time of basin preparation along with FYM
109
+ during December- January.
110
+
111
+ Half dose of Nitrogen (750 g per tree) should be given 2-3 weeks
112
+ before flowering and remaining quantity (750 g per tree) should be
113
+ applied after one month.
114
+
115
+ The fallen leaves of apple should be collected and decomposed in a
116
+ compost pit or spray of 5% urea (10 kg in 200lt water) may be done
117
+ on orchard floor on fallen leaves to ensure fast decomposition of
118
+ infected leaves.
119
+
120
+ During winters (November- December), expose the root system
121
+ of infected trees and cut the infected portion and apply Bordeaux
122
+ paint / chaubatia paste for the control of White root rot.
123
+
124
+ Remove all dead, diseased braches at the time of pruning and apply
125
+ Bordeaux paint/ chaubatia paste or any other Copper fungicide-
126
+ based paint.
127
+
128
+ Scarify wounds near collar region and apply Bordeaux paint/
129
+ chaubatia paste or any other Copper fungicide-based paint during
130
+ winter season.
131
+
132
+ For the management of canker, scarify the diseased portion upto
133
+ healthy region and apply Bordeaux paint/ chaubatia paste or any
134
+ other Copper fungicide-based paint during winter season.
135
+
136
+ Apply a mixture of lime + copper sulphate + linseed oil (30kg lime+
137
+ 500 gm copper sulphate + 500 ml linseed oil in 100 L water) on
138
+ stems upto a height of 2-3 ft from the ground level during October-
139
+ November for protection against sun burning.
140
+
141
+
142
+ ================ Page 40 ================
143
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
144
+ 31
145
+
146
+ Harvesting and Threshing: Harvest the crop when siliquae turn
147
+ yellow.
148
+ Sugarcane
149
+ Improved varieties-
150
+
151
+ Early Maturing Varieties: CoPb 92, Co 118, CoJ 85 and CoJ 64
152
+
153
+ Mid-Late Maturing Varieties: CoPb 93, CoPb 94, Co 238, CoPb
154
+ 91 and CoJ 88
155
+ Time of Planting
156
+
157
+ Mid-February to the end of March is the optimum time for planting
158
+ sugarcane in the Punjab. Do not plant early maturing varieties after
159
+ March. Avoid late planting.
160
+ Seed Selection
161
+
162
+ The seed should be free from red-rot, wilt, smut, ratoon-stunting
163
+ and grassy shoot diseases. Use only the top two-third portion of
164
+ the selected canes for planting.
165
+ Seed Rate
166
+
167
+ Use 20 thousand three-budded setts or 15 thousand four-budded
168
+ sets or 12 thousand five-budded setts per acre. In other words, 30-
169
+ 35 quintal of seed is required for sowing one acre. Due to thick
170
+ canes, seed rate of Co 118 and CoJ 85 should be kept about 10%
171
+ higher (on weight basis).
172
+ Seed Treatment
173
+
174
+ To improve germination, soak the setts in ethrel solution
175
+ overnight by dissolving 25 ml of Ethrel 39 SL in 100 liters of water.
176
+ Alternatively, soak the setts in water for 24 hours before planting.
177
+ Spacing and Planting Techniques
178
+
179
+ Trench Planting: Plant crop in rows 75 cm apart and 20-25 cm
180
+ deep trenches. After placing the setts in trenches, cover the setts
181
+
182
+
183
+ ================ Page 50 ================
184
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
185
+ 41
186
+ water and isolate them in the calving pen about 10 days before
187
+ calving.
188
+
189
+ After calving, wipe clean the piglets with a clean cloth and assist
190
+ them to suckle the colostrum.
191
+
192
+ Install a light bulb about two feet high to keep children warm.
193
+
194
+ Cut the needle teeth within two days before birth and giveiron
195
+ supplementation on the third and thirteenth day to deal with
196
+ anaemia. Start giving solid food in the second week.
197
+ March
198
+
199
+ Wean the piglets 45-60 days after calving and start giving a solid
200
+ diet with 20-22% raw protein.
201
+
202
+ Sell male children who are sold by 15 days of age
203
+ Poultry farming
204
+ October
205
+
206
+ Assess the cleanliness and biosecurity of the entire farm.
207
+
208
+ Restrict workers/ visitors entering the shed unnecessarily.
209
+ November
210
+
211
+ Continue the work for the month of October
212
+
213
+ Take measures to maintain the temperature and humidity inside
214
+ the shed according to the changing weather. Check electrical
215
+ appliances, light blowers, etc. inside the shed.
216
+ December
217
+
218
+ Continue with the chores of the month of October-November.
219
+
220
+ Keep the chicks under the brooder. Use plastic curtains, heaters, or
221
+ heaters to keep the temperature constant in the shed.
222
+
223
+ Include appropriate medicine/coccidiostats in the diet to prevent
224
+ coccidiosis/ bloody diarrhea.
225
+
226
+ Add partially dried berseem to the diet of poultry birds.
227
+
228
+
229
+ ================ Page 60 ================
230
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
231
+ 51
232
+ Radish
233
+
234
+ Varieties: Japanese White, Minowase, Pusa Chetki, Pusa Himani,
235
+ Pusa Reshmi, White Icicle, Pusa Desi, Arka Nishant
236
+
237
+ Seed rate is 4-4.8 kg/acre for Asiatic types, 6-7.2 kg/acre for
238
+ European types and 1.6-2.0 kg/acre with Dibbling Method.
239
+
240
+ 24:12:20 kg/acre of NPK is recommended dose of fertilizers. Apply
241
+ 12 t/acre of FYM together with P2O5 and K2O and half of N at the
242
+ time of field preparation. Remaining half N should be applied at
243
+ the time of earthing-up.
244
+ Carrot
245
+
246
+ The optimum temperature for growth is 16-18°C and colour
247
+ development in 20-22°C.
248
+
249
+ Varieties: Pusa Kesar, Nantes, Chaman, PusaYamdagini
250
+
251
+ Seeds may be soaked in water for 12-24 hours prior to sowing to
252
+ improve germination
253
+
254
+ 24:12:20 kg/acre of NPK is recommended dose of fertilizers. Apply
255
+ 12 t/acre of FYM together with P2O5 and K2O and half of N at the
256
+ time of field preparation. Remaining half N should be applied at
257
+ the time of earthing up.
258
+ Garlic
259
+
260
+ It requires cool and moist period during growth and relatively dry
261
+ period during maturity
262
+
263
+ Varieties: Agrifound Parvati-2 (G-408), Yamuna Safed (G-1):
264
+
265
+ Planting Time in Sub Tropical areas is September- October; in
266
+ Intermediate (low) is August -September and Intermediate (High)
267
+ is March-April.
268
+
269
+ Seed rate is 2.0-2.4 q/acre (Cloves) with Spacing 15 cm x 7.5 cm
270
+ and 40:20:20 NPK & 8 t/acre of FYM.
271
+
272
+ The weeds in garlic can be initially controlled by the application of
273
+ pendimelhalin @ 0.8-1.0 litres in 250 litres of water or 1.2- 1.6ml/
274
+
275
+
276
+ ================ Page 70 ================
277
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
278
+ 61
279
+ iodine test and starch rating should be from 2.00 to 2.5 on 1- 6
280
+ rating scale for prolonged storage.
281
+
282
+ In Apples, fruit firmness test should be done with the help of
283
+ pressure tester and fruit pressure should range between 15 to 17
284
+ lbs/ sq inch.
285
+
286
+ Remove twigs infested with WAA and apply Chaubatia paste on
287
+ cut areas for Woolly apple aphid.
288
+ Apple fruit borer:
289
+
290
+ Maintain good sanitation in the infested orchards, all the dropped
291
+ and infested fruits of apple should be collected and buried deep in
292
+ the soil.
293
+
294
+ Burlapping practice should be followed, and the overwintering
295
+ stages should be destroyed along with the burlap.
296
+ Apple stem borer:
297
+
298
+ Heavily infested branches, twigs and completely dried trees should
299
+ be uprooted, removed from the orchard, and destroyed.
300
+
301
+ Insertion of petrol-soaked cotton deep in the holes of apple tree,
302
+ followed by plastering with mud containing insecticide dust/ WP
303
+ 10% in 6:1 ratio. OR
304
+
305
+ Pressurized injection of Petrol in the holes, followed by plastering
306
+ as mentioned above.
307
+ San Jose scale & Woolly apple aphid
308
+
309
+ Remove twigs infested with SJS and WAA during pruning and
310
+ dispose them away from the orchard. Apply Chaubatia paste on
311
+ cut areas.
312
+ European red mite
313
+
314
+ If the population is more than 20 mites per leaf, spray Fenazaquin
315
+ 10 EC (40ml) per 100 litres of water.
316
+
317
+
318
+ ================ Page 80 ================
319
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
320
+ 71
321
+ Goat & Sheep
322
+ Disease
323
+ Symptoms
324
+ Prevention
325
+ Bacterial Disease
326
+ Haemorrhagic
327
+ Septicemia
328
+ Fever, dysentery, swelling
329
+ of lower mandible and
330
+ death more occurred.
331
+ Vaccinate first dose at 3-4 month
332
+ of age and booster at 3-4 week after
333
+ first dose. Repeat at every 6/12
334
+ months interval in sep/oct.
335
+ Brucellosis
336
+ Abortion during late
337
+ pregnancy, infertility,
338
+ scrotal swelling in male,
339
+ joint swelling
340
+ Disposal of dead foetus and
341
+ placenta. Use gloves while handling
342
+ infected items as it affects human
343
+ beings.
344
+ Pneumonia
345
+ Fever, respiratory distress,
346
+ mucous discharge from
347
+ nostril, reduced feed intake
348
+ and weight gain, cough
349
+ Clean water, well ventilated house.
350
+ Enterotoxaemia
351
+ Sudden death in young
352
+ growing kids. Mucous
353
+ diarrhoea may also seen
354
+ during death
355
+ Vaccinate first dose at 3-4 month of
356
+ age, booster at 3-4 weeks after first
357
+ dose. Repeat every 6/12 months
358
+ interval.
359
+ Collibacillinum
360
+ Diarrhoea, Sudden Death,
361
+ reduce feed intake, usually
362
+ occur in young ones
363
+ Clean and disinfect the lamb/kid
364
+ shelter. Provide clean water
365
+ Viral Disease
366
+ Peste Des Petits
367
+ Ruminants
368
+ (PPR)
369
+ Fever, Occular and nasal
370
+ mucous discharge, mouth
371
+ lesion, respiratory distress
372
+ First dose at 3 month of age and
373
+ repeat every 3 years.
374
+ Separation of infected one from
375
+ healthy animals.
376
+ Foot and
377
+ Mouth Disease
378
+ Fever, wound lesion in
379
+ foot and mouth, excess
380
+ salivary secretion, difficult
381
+ in walking
382
+ First vaccination at 3-4 month and
383
+ Booster 3-4 week after fist dose.
384
+ Repeat every 6/12 months interval
385
+ in Oct and April
386
+ Contagious
387
+ Ecthyma
388
+ (Khalay)
389
+ Postules, Scrab formation,
390
+ Extension Lession around
391
+ Mouth slips. Anorexia and
392
+ starvation. Kids are more
393
+ susceptible to the disease
394
+ Wash with 1% potassium
395
+ Permagnate solution. Apply tincture
396
+ iodine followed by glycerine. Isolate
397
+ the affected animals from other
398
+ healthy animals
399
+
400
+
401
+ ================ Page 90 ================
402
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
403
+ 81
404
+
405
+ Varieties for hill areas Marsid, Nanperil, IXL, Ne-plus-altra, Texas
406
+ Fish and Pond Maintenance during Winter Season
407
+
408
+ Fish, being a cold-blooded aquatic animal, needs special care
409
+ during winters. As temperature of the surface water is colder than
410
+ the bottom layers, the fish prefers to live in the bottom zone. Famers
411
+ shall keep the water depth up to 6 feet, so that it gets enough space
412
+ for hibernating in the warmer bottom zone. In shallow waters, the
413
+ whole water column becomes cold, which affects the fish and can
414
+ prove fatal.
415
+
416
+ As day length and light intensity also decreases during winters,
417
+ oxygen levels decline in ponds due to reduced photosynthetic
418
+ activity. The situation further aggravates during continuous cloudy
419
+ days. The farmers are advised to aerate their ponds either by adding
420
+ fresh water or by using aerators, especially during early hours of
421
+ the day.
422
+
423
+ Feed intake of fish decreases with decrease in temperature as its
424
+ digestive system becomes sluggish. Hence, it is essential to reduce
425
+ the feeding rate by 50-70 % depending on the temperature.
426
+
427
+ In case the temperature falls below 50 degrees, it is advice to stop
428
+ feeding. Excess feed remains unconsumed and accumulates at the
429
+ pond bottom, which deteriorates the water quality.
430
+
431
+ Farmers re further advised to use low protein diets. It is also
432
+ necessary to reduce/stop adding organic manures such as cow
433
+ dung, poultry droppings, and pig dung in the pond as rate of
434
+ decomposition of organic manures declines due to poor microbial
435
+ activity during winters. It is also advised to go for periodic raking of
436
+ bottom soil (with the help of barbed wire) to prevent any suspected
437
+ accumulation of toxic gases at the pond bottom.
438
+ Animal care during winter season
439
+
440
+ Feed more roughages (like hay, straws. etc.) or forages (berseem) to
441
+ maintain the milk production and body heat of the dairy animals.
442
+
443
+
444
+ ================ Page 100 ================
445
+ ICAR RABI AgRo-AdvIsoRy foR fARmeRs
446
+ 91
447
+ Other activities to be performed:
448
+
449
+ To achieve higher water use efficiency (WUE) of available irrigation
450
+ water, especially in canal area, land should be levelled with Laser
451
+ Land Leveller before sowing of Rabi season crops.
452
+
453
+ Collect, Harvest & Conserve maximum rainwater as much as
454
+ possible in Farm Pond or Plastic based water pond and minimize
455
+ the losses of conserved rainwater for life saving irrigation on
456
+ different critical stages during moisture deficit conditions, to
457
+ achieve higher productivity level.
458
+
459
+ As per availability of resources, prepare proper work plan for
460
+ sustainable farming/ organic farming/ Paramparagat Krishi in
461
+ Rabi season and for this nearby KVK may help you in providing
462
+ proper advisory and guidance.
463
+
464
+ Adopt appropriate IFS Models as per availability of different
465
+ resources, infrastructure, and facilities on their farm.
466
+
467
+ Eligible farmers register themselves for Pradhanmantri Fasal
468
+ Bima Yojna (PMFBY) benefits, to minimize the risk from natural
469
+ calamities.
470
+ Horticultural Crops
471
+ Fruit crops
472
+ Ber
473
+
474
+ Recommended varieties: Seb, Gola, Umran, Ilaichi, Kaithali,
475
+ Mundiya
476
+
477
+ General Management of orchard:
478
+
479
+ Orchard should be clean and weed free.
480
+
481
+ To get higher fruit yield it is recommended to place two
482
+ honeybee colonies or hive/Acre in September month for
483
+ better pollination because honey bees play important role in
484
+ pollination. Irrigation should be applied regularly at 10-15
485
+ days’ interval.
486
+
487
+
data/parsed/advisories.json ADDED
The diff for this file is too large to render. See raw diff
 
data/parsed/chunks.json ADDED
The diff for this file is too large to render. See raw diff
 
data/parsed/valid_advisories.json ADDED
The diff for this file is too large to render. See raw diff
 
data/quarantine/failed_advisories.json ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "season": "Kharif",
4
+ "source": "ICAR.pdf",
5
+ "state": "Assam",
6
+ "category": "Oilseed Crops",
7
+ "crop": "Maize",
8
+ "page": 42,
9
+ "content": "Maize Variety Bio 9544",
10
+ "validation_errors": [
11
+ "Content too short (22 chars)"
12
+ ]
13
+ },
14
+ {
15
+ "season": "Kharif",
16
+ "source": "ICAR.pdf",
17
+ "state": "Gujarat",
18
+ "category": "Poultry",
19
+ "crop": "Groundnut",
20
+ "page": 65,
21
+ "content": "SOUTH SAURASHTRA",
22
+ "validation_errors": [
23
+ "Content too short (16 chars)"
24
+ ]
25
+ },
26
+ {
27
+ "season": "Kharif",
28
+ "source": "ICAR.pdf",
29
+ "state": "Gujarat",
30
+ "category": "Poultry",
31
+ "crop": "Soybean",
32
+ "page": 66,
33
+ "content": "Cotton (Bt & Desi) Groundnut Demo Plot",
34
+ "validation_errors": [
35
+ "Content too short (38 chars)"
36
+ ]
37
+ },
38
+ {
39
+ "season": "Kharif",
40
+ "source": "ICAR.pdf",
41
+ "state": "Gujarat",
42
+ "category": "Fruit & Vegetable Crops",
43
+ "crop": "Banana",
44
+ "page": 71,
45
+ "content": "Finger Millet, Little Millet and Foxtail Millet",
46
+ "validation_errors": [
47
+ "Content too short (47 chars)"
48
+ ]
49
+ },
50
+ {
51
+ "season": "Kharif",
52
+ "source": "ICAR.pdf",
53
+ "state": "Maharashtra",
54
+ "category": "Fruit Crops",
55
+ "crop": "Pomegranate",
56
+ "page": 172,
57
+ "content": "Fruit cover in Pomegranate",
58
+ "validation_errors": [
59
+ "Content too short (26 chars)"
60
+ ]
61
+ },
62
+ {
63
+ "season": "Kharif",
64
+ "source": "ICAR.pdf",
65
+ "state": "Manipur",
66
+ "category": "Cereal Crops",
67
+ "crop": "Maize",
68
+ "page": 184,
69
+ "content": "Varietal demonstration in paddy",
70
+ "validation_errors": [
71
+ "Content too short (31 chars)"
72
+ ]
73
+ },
74
+ {
75
+ "season": "Kharif",
76
+ "source": "ICAR.pdf",
77
+ "state": "Mizoram",
78
+ "category": "Vegetable Crops",
79
+ "crop": "Pumpkin",
80
+ "page": 191,
81
+ "content": "Carrot",
82
+ "validation_errors": [
83
+ "Content too short (6 chars)"
84
+ ]
85
+ },
86
+ {
87
+ "season": "Rabi",
88
+ "source": "Rabi-Agro-Advisory-2021-22.pdf",
89
+ "state": "Mizoram",
90
+ "category": "Rabi Crops",
91
+ "crop": "Ginger",
92
+ "page": 298,
93
+ "content": "25 g Aniseed 15 g",
94
+ "zone": "Zone-Vii",
95
+ "validation_errors": [
96
+ "Content too short (17 chars)"
97
+ ]
98
+ },
99
+ {
100
+ "season": "Rabi",
101
+ "source": "Rabi-Agro-Advisory-2021-22.pdf",
102
+ "state": "Mizoram",
103
+ "category": "Rabi Crops",
104
+ "crop": "Onion",
105
+ "page": 298,
106
+ "content": "50 g Clove 6 nos.",
107
+ "zone": "Zone-Vii",
108
+ "validation_errors": [
109
+ "Content too short (17 chars)"
110
+ ]
111
+ },
112
+ {
113
+ "season": "Rabi",
114
+ "source": "Rabi-Agro-Advisory-2021-22.pdf",
115
+ "state": "Mizoram",
116
+ "category": "Rabi Crops",
117
+ "crop": "Garlic",
118
+ "page": 298,
119
+ "content": "10 g Tamarind pulp 50 g Red chilli 15 g",
120
+ "zone": "Zone-Vii",
121
+ "validation_errors": [
122
+ "Content too short (39 chars)"
123
+ ]
124
+ },
125
+ {
126
+ "season": "Rabi",
127
+ "source": "Rabi-Agro-Advisory-2021-22.pdf",
128
+ "state": "Mizoram",
129
+ "category": "Rabi Crops",
130
+ "crop": "Mustard",
131
+ "page": 298,
132
+ "content": "50 g",
133
+ "zone": "Zone-Vii",
134
+ "validation_errors": [
135
+ "Content too short (4 chars)"
136
+ ]
137
+ }
138
+ ]
pipeline/02_parse.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import re
3
+ from pathlib import Path
4
+
5
+ # Paths
6
+ BASE_DIR = Path(__file__).resolve().parent.parent
7
+ EXTRACTED_DIR = BASE_DIR / "data" / "extracted"
8
+ PARSED_DIR = BASE_DIR / "data" / "parsed"
9
+ CONFIG_DIR = BASE_DIR / "config"
10
+
11
+ PARSED_DIR.mkdir(parents=True, exist_ok=True)
12
+
13
+ # Load Canonical Lists
14
+ with open(CONFIG_DIR / "crops.json", "r", encoding="utf-8") as f:
15
+ CANONICAL_CROPS = json.load(f)
16
+
17
+ with open(CONFIG_DIR / "states.json", "r", encoding="utf-8") as f:
18
+ CANONICAL_STATES = json.load(f)
19
+
20
+ # Helper to normalize ligatures and spaces
21
+ def clean_text(text: str) -> str:
22
+ text = text.replace("\u00ad", "") # soft hyphen
23
+ text = text.replace("\ufb01", "fi").replace("ϐ", "fi").replace("ϐield", "field")
24
+ text = text.replace("\ufb02", "fl")
25
+ text = text.replace("ff", "ff").replace("fi", "fi").replace("fl", "fl")
26
+
27
+ # Remove repeated headers/footers
28
+ text = re.sub(r"(ICAR\s+KHARIF\s+AGRO-ADVISORY|ICAR\s+RABI\s+AgRo-AdvIsoRy\s+foR\s+fARmeRs|AgRo-AdvIsoRy\s+foR\s+fARmeRs)", "", text, flags=re.I)
29
+
30
+ # Remove line numbers or solitary numbers (page numbers) at start/end of lines
31
+ lines = []
32
+ for line in text.split("\n"):
33
+ line_strip = line.strip()
34
+ # Skip solitary page numbers
35
+ if line_strip.isdigit():
36
+ continue
37
+ # Skip empty lines
38
+ if not line_strip:
39
+ continue
40
+ lines.append(line)
41
+
42
+ return "\n".join(lines)
43
+
44
+ # Check if line matches a state
45
+ def detect_state(line: str) -> str:
46
+ line_clean = line.strip().lower().replace("&", "and").replace(",", "")
47
+ for state in CANONICAL_STATES:
48
+ state_clean = state.lower().replace("&", "and").replace(",", "")
49
+ # Match exact line or boundary
50
+ if line_clean == state_clean or line_clean.startswith(state_clean + " "):
51
+ return state
52
+ return None
53
+
54
+ # Check if line matches a crop
55
+ def detect_crop(line: str) -> str:
56
+ line_clean = line.strip().rstrip(":").strip().lower()
57
+
58
+ # Handle direct exact matches
59
+ for crop in CANONICAL_CROPS:
60
+ if line_clean == crop.lower():
61
+ return crop
62
+
63
+ # Handle composite crop names, e.g. "Mash (Black Gram)" matching black gram
64
+ for crop in CANONICAL_CROPS:
65
+ crop_clean = crop.lower()
66
+ if "(" in crop_clean:
67
+ # Extract name and bracketed parts
68
+ parts = re.findall(r'\b[a-z\s]+\b', crop_clean)
69
+ for part in parts:
70
+ part = part.strip()
71
+ if len(part) > 3 and line_clean == part:
72
+ return crop
73
+
74
+ # Handle common sub-crop titles
75
+ if line_clean == "paddy":
76
+ return "Paddy"
77
+ if line_clean == "moong" or line_clean == "moong (green gram)":
78
+ return "Moong"
79
+ if line_clean == "urad" or line_clean == "black gram":
80
+ return "Black gram"
81
+
82
+ return None
83
+
84
+ # Categories
85
+ CATEGORIES = [
86
+ "CEREAL CROPS", "PULSES", "OILSEEDS", "FRUIT AND VEGETABLE CROPS",
87
+ "FRUIT & VEGETABLE CROPS", "VEGETABLE CROPS", "FRUIT CROPS",
88
+ "ROOT AND TUBER CROPS", "LIVESTOCK", "POULTRY", "FISHERIES",
89
+ "COMMERCIAL CROPS", "FODDER CROPS", "PULSE CROPS", "OILSEED CROPS"
90
+ ]
91
+
92
+ def detect_category(line: str) -> str:
93
+ line_clean = line.strip().upper()
94
+ for cat in CATEGORIES:
95
+ if line_clean == cat:
96
+ return cat.title()
97
+ return None
98
+
99
+ def parse_kharif():
100
+ print("Parsing Kharif advisories...")
101
+ input_file = EXTRACTED_DIR / "ICAR.json"
102
+ if not input_file.exists():
103
+ print(f"Extraction file {input_file} not found!")
104
+ return []
105
+
106
+ with open(input_file, "r", encoding="utf-8") as f:
107
+ pages = json.load(f)
108
+
109
+ records = []
110
+ current_state = None
111
+ current_category = None
112
+ current_crop = None
113
+ buffer = []
114
+ current_page = None
115
+
116
+ for page_data in pages:
117
+ page_no = page_data["page"]
118
+ text = clean_text(page_data["text"])
119
+ lines = text.split("\n")
120
+
121
+ for line in lines:
122
+ line_strip = line.strip()
123
+ if not line_strip:
124
+ continue
125
+
126
+ # Detect state change
127
+ state = detect_state(line_strip)
128
+ if state:
129
+ # Save previous crop if exists
130
+ if current_crop and buffer:
131
+ records.append({
132
+ "season": "Kharif",
133
+ "source": "ICAR.pdf",
134
+ "state": current_state,
135
+ "category": current_category,
136
+ "crop": current_crop,
137
+ "page": current_page,
138
+ "content": " ".join(buffer)
139
+ })
140
+ buffer = []
141
+ current_state = state
142
+ current_crop = None
143
+ continue
144
+
145
+ # Detect category change
146
+ category = detect_category(line_strip)
147
+ if category:
148
+ current_category = category
149
+ continue
150
+
151
+ # Detect crop change
152
+ crop = detect_crop(line_strip)
153
+ if crop:
154
+ if current_crop and buffer:
155
+ records.append({
156
+ "season": "Kharif",
157
+ "source": "ICAR.pdf",
158
+ "state": current_state,
159
+ "category": current_category,
160
+ "crop": current_crop,
161
+ "page": current_page,
162
+ "content": " ".join(buffer)
163
+ })
164
+ buffer = []
165
+ current_crop = crop
166
+ current_page = page_no
167
+ continue
168
+
169
+ # Accumulate content if inside a crop
170
+ if current_crop:
171
+ buffer.append(line_strip)
172
+
173
+ # Save last crop
174
+ if current_crop and buffer:
175
+ records.append({
176
+ "season": "Kharif",
177
+ "source": "ICAR.pdf",
178
+ "state": current_state,
179
+ "category": current_category,
180
+ "crop": current_crop,
181
+ "page": current_page,
182
+ "content": " ".join(buffer)
183
+ })
184
+
185
+ return records
186
+
187
+ def parse_rabi():
188
+ print("Parsing Rabi advisories...")
189
+ input_file = EXTRACTED_DIR / "Rabi-Agro-Advisory-2021-22.json"
190
+ if not input_file.exists():
191
+ print(f"Extraction file {input_file} not found!")
192
+ return []
193
+
194
+ with open(input_file, "r", encoding="utf-8") as f:
195
+ pages = json.load(f)
196
+
197
+ records = []
198
+ current_zone = None
199
+ current_state = None
200
+ current_crop = None
201
+ buffer = []
202
+ current_page = None
203
+
204
+ for page_data in pages:
205
+ page_no = page_data["page"]
206
+ text = clean_text(page_data["text"])
207
+ lines = text.split("\n")
208
+
209
+ for line in lines:
210
+ line_strip = line.strip()
211
+ if not line_strip:
212
+ continue
213
+
214
+ # Detect zone pattern, e.g. "Zone-III" or "Zone III"
215
+ zone_match = re.search(r'\b(Zone-\w+|\bZone\s+\w+)\b', line_strip, re.I)
216
+ if zone_match:
217
+ current_zone = zone_match.group(1).title()
218
+
219
+ # Detect state mention
220
+ state = detect_state(line_strip)
221
+ if state:
222
+ if current_crop and buffer:
223
+ records.append({
224
+ "season": "Rabi",
225
+ "source": "Rabi-Agro-Advisory-2021-22.pdf",
226
+ "state": current_state,
227
+ "category": "Rabi Crops",
228
+ "crop": current_crop,
229
+ "page": current_page,
230
+ "content": " ".join(buffer),
231
+ "zone": current_zone
232
+ })
233
+ buffer = []
234
+ current_state = state
235
+ current_crop = None
236
+ continue
237
+
238
+ # Detect crop change
239
+ crop = detect_crop(line_strip)
240
+ if crop:
241
+ if current_crop and buffer:
242
+ records.append({
243
+ "season": "Rabi",
244
+ "source": "Rabi-Agro-Advisory-2021-22.pdf",
245
+ "state": current_state,
246
+ "category": "Rabi Crops",
247
+ "crop": current_crop,
248
+ "page": current_page,
249
+ "content": " ".join(buffer),
250
+ "zone": current_zone
251
+ })
252
+ buffer = []
253
+ current_crop = crop
254
+ current_page = page_no
255
+ continue
256
+
257
+ # Accumulate content
258
+ if current_crop:
259
+ buffer.append(line_strip)
260
+
261
+ # Save last crop
262
+ if current_crop and buffer:
263
+ records.append({
264
+ "season": "Rabi",
265
+ "source": "Rabi-Agro-Advisory-2021-22.pdf",
266
+ "state": current_state,
267
+ "category": "Rabi Crops",
268
+ "crop": current_crop,
269
+ "page": current_page,
270
+ "content": " ".join(buffer),
271
+ "zone": current_zone
272
+ })
273
+
274
+ return records
275
+
276
+ def main():
277
+ kharif_records = parse_kharif()
278
+ rabi_records = parse_rabi()
279
+
280
+ all_records = kharif_records + rabi_records
281
+
282
+ output_file = PARSED_DIR / "advisories.json"
283
+ with open(output_file, "w", encoding="utf-8") as f:
284
+ json.dump(all_records, f, indent=2, ensure_ascii=False)
285
+
286
+ print(f"Successfully compiled {len(all_records)} raw records.")
287
+ print(f"Saved to {output_file}")
288
+
289
+ if __name__ == "__main__":
290
+ main()
pipeline/03_validate.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import hashlib
3
+ from pathlib import Path
4
+
5
+ # Paths
6
+ BASE_DIR = Path(__file__).resolve().parent.parent
7
+ PARSED_DIR = BASE_DIR / "data" / "parsed"
8
+ QUARANTINE_DIR = BASE_DIR / "data" / "quarantine"
9
+ CONFIG_DIR = BASE_DIR / "config"
10
+
11
+ QUARANTINE_DIR.mkdir(parents=True, exist_ok=True)
12
+
13
+ # Load Canonical Lists
14
+ with open(CONFIG_DIR / "crops.json", "r", encoding="utf-8") as f:
15
+ CANONICAL_CROPS = set(crop.lower() for crop in json.load(f))
16
+
17
+ with open(CONFIG_DIR / "states.json", "r", encoding="utf-8") as f:
18
+ CANONICAL_STATES = set(state.lower() for state in json.load(f))
19
+
20
+ def main():
21
+ input_file = PARSED_DIR / "advisories.json"
22
+ if not input_file.exists():
23
+ print(f"Parsed advisories file {input_file} not found!")
24
+ return
25
+
26
+ with open(input_file, "r", encoding="utf-8") as f:
27
+ records = json.load(f)
28
+
29
+ valid_records = []
30
+ quarantined_records = []
31
+ seen_hashes = set()
32
+
33
+ for r in records:
34
+ state = r.get("state")
35
+ crop = r.get("crop")
36
+ season = r.get("season")
37
+ content = r.get("content", "").strip()
38
+
39
+ errors = []
40
+
41
+ if not state:
42
+ errors.append("Missing state")
43
+ elif state.lower() not in CANONICAL_STATES:
44
+ errors.append(f"State '{state}' is not in canonical list")
45
+
46
+ if not crop:
47
+ errors.append("Missing crop")
48
+ elif crop.lower() not in CANONICAL_CROPS:
49
+ errors.append(f"Crop '{crop}' is not in canonical list")
50
+
51
+ if not season:
52
+ errors.append("Missing season")
53
+
54
+ if not content:
55
+ errors.append("Empty content")
56
+ elif len(content) < 50:
57
+ errors.append(f"Content too short ({len(content)} chars)")
58
+
59
+ # Deduplication
60
+ if not errors:
61
+ # Generate SHA-256 hash
62
+ content_hash = hashlib.sha256(
63
+ f"{state.lower()}|{crop.lower()}|{season.lower()}|{content}".encode("utf-8")
64
+ ).hexdigest()
65
+
66
+ if content_hash in seen_hashes:
67
+ errors.append("Duplicate advisory record")
68
+ else:
69
+ seen_hashes.add(content_hash)
70
+
71
+ if errors:
72
+ r_failed = r.copy()
73
+ r_failed["validation_errors"] = errors
74
+ quarantined_records.append(r_failed)
75
+ else:
76
+ valid_records.append(r)
77
+
78
+ # Save valid records
79
+ valid_output = PARSED_DIR / "valid_advisories.json"
80
+ with open(valid_output, "w", encoding="utf-8") as f:
81
+ json.dump(valid_records, f, indent=2, ensure_ascii=False)
82
+
83
+ # Save quarantined records
84
+ quarantine_output = QUARANTINE_DIR / "failed_advisories.json"
85
+ with open(quarantine_output, "w", encoding="utf-8") as f:
86
+ json.dump(quarantined_records, f, indent=2, ensure_ascii=False)
87
+
88
+ print(f"Validation summary:")
89
+ print(f" Valid records: {len(valid_records)}")
90
+ print(f" Quarantined records: {len(quarantined_records)}")
91
+ print(f" Outputs saved to: {valid_output} and {quarantine_output}")
92
+
93
+ if __name__ == "__main__":
94
+ main()
pipeline/04_chunk.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import tiktoken
3
+ from pathlib import Path
4
+
5
+ # Paths
6
+ BASE_DIR = Path(__file__).resolve().parent.parent
7
+ PARSED_DIR = BASE_DIR / "data" / "parsed"
8
+
9
+ # Tokenizer
10
+ TOKENIZER_NAME = "cl100k_base" # Standard GPT-4/Gemini cl100k_base
11
+ encoding = tiktoken.get_encoding(TOKENIZER_NAME)
12
+
13
+ def count_tokens(text: str) -> int:
14
+ return len(encoding.encode(text))
15
+
16
+ def chunk_text(text: str, target_size: int = 700, overlap: int = 100) -> list:
17
+ """Split text into chunks of target_size with overlap using tokens."""
18
+ tokens = encoding.encode(text)
19
+ total_tokens = len(tokens)
20
+
21
+ if total_tokens <= 800:
22
+ return [text]
23
+
24
+ chunks = []
25
+ start = 0
26
+ step = target_size - overlap
27
+
28
+ while start < total_tokens:
29
+ end = min(start + target_size, total_tokens)
30
+ chunk_tokens = tokens[start:end]
31
+ chunk_text = encoding.decode(chunk_tokens)
32
+ chunks.append(chunk_text)
33
+
34
+ # If we reached the end, break
35
+ if end == total_tokens:
36
+ break
37
+
38
+ start += step
39
+
40
+ return chunks
41
+
42
+ def main():
43
+ input_file = PARSED_DIR / "valid_advisories.json"
44
+ if not input_file.exists():
45
+ print(f"Valid advisories file {input_file} not found!")
46
+ return
47
+
48
+ with open(input_file, "r", encoding="utf-8") as f:
49
+ records = json.load(f)
50
+
51
+ all_chunks = []
52
+
53
+ for r in records:
54
+ content = r["content"]
55
+ chunks = chunk_text(content, target_size=700, overlap=100)
56
+
57
+ for idx, chunk_content in enumerate(chunks, start=1):
58
+ chunk_record = {
59
+ "season": r["season"],
60
+ "source": r["source"],
61
+ "state": r["state"],
62
+ "category": r["category"],
63
+ "crop": r["crop"],
64
+ "page": r["page"],
65
+ "chunk_id": idx,
66
+ "content": chunk_content.strip(),
67
+ "token_count": count_tokens(chunk_content)
68
+ }
69
+ all_chunks.append(chunk_record)
70
+
71
+ output_file = PARSED_DIR / "chunks.json"
72
+ with open(output_file, "w", encoding="utf-8") as f:
73
+ json.dump(all_chunks, f, indent=2, ensure_ascii=False)
74
+
75
+ print(f"Chunking summary:")
76
+ print(f" Total source advisories: {len(records)}")
77
+ print(f" Generated chunks: {len(all_chunks)}")
78
+ print(f" Saved to: {output_file}")
79
+
80
+ if __name__ == "__main__":
81
+ main()
pipeline/05_embed_upload.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import hashlib
4
+ from pathlib import Path
5
+ from dotenv import load_dotenv
6
+ from sentence_transformers import SentenceTransformer
7
+ from pinecone import Pinecone, ServerlessSpec
8
+
9
+ # Load Environment Variables
10
+ BASE_DIR = Path(__file__).resolve().parent.parent
11
+ load_dotenv(dotenv_path=BASE_DIR / ".env")
12
+
13
+ PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
14
+ PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME")
15
+
16
+ PARSED_DIR = BASE_DIR / "data" / "parsed"
17
+
18
+ def main():
19
+ if not PINECONE_API_KEY or PINECONE_API_KEY == "your_pinecone_api_key":
20
+ print("Error: PINECONE_API_KEY is not set in environment variables.")
21
+ return
22
+ if not PINECONE_INDEX_NAME or PINECONE_INDEX_NAME == "farmrisk-advisories":
23
+ print("Error: PINECONE_INDEX_NAME is not set in environment variables.")
24
+ return
25
+
26
+ # Load Chunks
27
+ chunks_file = PARSED_DIR / "chunks.json"
28
+ if not chunks_file.exists():
29
+ print(f"Chunks file {chunks_file} not found!")
30
+ return
31
+
32
+ with open(chunks_file, "r", encoding="utf-8") as f:
33
+ chunks = json.load(f)
34
+
35
+ if not chunks:
36
+ print("No chunks to upload.")
37
+ return
38
+
39
+ print(f"Loading SentenceTransformer model 'BAAI/bge-small-en-v1.5'...")
40
+ model = SentenceTransformer("BAAI/bge-small-en-v1.5")
41
+
42
+ print("Connecting to Pinecone...")
43
+ pc = Pinecone(api_key=PINECONE_API_KEY)
44
+
45
+ # Create index if it does not exist
46
+ existing_indexes = [index.name for index in pc.list_indexes()]
47
+ if PINECONE_INDEX_NAME not in existing_indexes:
48
+ print(f"Index '{PINECONE_INDEX_NAME}' not found. Creating a new index...")
49
+ pc.create_index(
50
+ name=PINECONE_INDEX_NAME,
51
+ dimension=384,
52
+ metric="cosine",
53
+ spec=ServerlessSpec(
54
+ cloud="aws",
55
+ region="us-east-1"
56
+ )
57
+ )
58
+ print(f"Index '{PINECONE_INDEX_NAME}' created successfully.")
59
+ else:
60
+ print(f"Index '{PINECONE_INDEX_NAME}' already exists.")
61
+
62
+ index = pc.Index(PINECONE_INDEX_NAME)
63
+
64
+ print(f"Embedding and uploading {len(chunks)} chunks in batches of 100...")
65
+ batch_size = 100
66
+ for i in range(0, len(chunks), batch_size):
67
+ batch = chunks[i:i + batch_size]
68
+
69
+ # Prepare contents to embed
70
+ texts = [item["content"] for item in batch]
71
+ embeddings = model.encode(texts, normalize_embeddings=True)
72
+
73
+ upsert_data = []
74
+ for idx, item in enumerate(batch):
75
+ # Create a unique ID
76
+ safe_state = item["state"].lower().replace(" ", "_").replace("&", "and")
77
+ safe_crop = item["crop"].lower().replace(" ", "_")
78
+ safe_season = item["season"].lower()
79
+ unique_id = f"{safe_state}_{safe_crop}_{safe_season}_{item['chunk_id']}"
80
+
81
+ upsert_data.append((
82
+ unique_id,
83
+ embeddings[idx].tolist(),
84
+ {
85
+ "crop": item["crop"],
86
+ "state": item["state"],
87
+ "season": item["season"],
88
+ "source": item["source"],
89
+ "page": item["page"],
90
+ "category": item["category"]
91
+ }
92
+ ))
93
+
94
+ index.upsert(vectors=upsert_data)
95
+ print(f" Upserted batch {i//batch_size + 1}/{len(chunks)//batch_size + 1} (items {i} to {i + len(batch)})")
96
+
97
+ print("Pinecone upload complete!")
98
+
99
+ if __name__ == "__main__":
100
+ main()
pipeline/run_all.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ BASE_DIR = Path(__file__).resolve().parent.parent
6
+
7
+ def run_script(script_name: str):
8
+ script_path = BASE_DIR / "pipeline" / script_name
9
+ print(f"\n==========================================")
10
+ print(f"Running: {script_name}")
11
+ print(f"==========================================\n")
12
+
13
+ result = subprocess.run([sys.executable, str(script_path)], cwd=BASE_DIR)
14
+ if result.returncode != 0:
15
+ print(f"\nError: {script_name} failed with return code {result.returncode}")
16
+ sys.exit(result.returncode)
17
+
18
+ def main():
19
+ # Note: 01_extract.py is omitted as it has already been executed to dump raw text JSONs.
20
+ run_script("02_parse.py")
21
+ run_script("03_validate.py")
22
+ run_script("04_chunk.py")
23
+ run_script("05_embed_upload.py")
24
+ print("\nFull Ingestion Pipeline completed successfully!")
25
+
26
+ if __name__ == "__main__":
27
+ main()
rag/extract.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import fitz
3
+ import json
4
+ from tqdm import tqdm
5
+
6
+ PDF_FOLDER = Path("data/pdfs")
7
+ OUTPUT_FOLDER = Path("data/extracted")
8
+
9
+ OUTPUT_FOLDER.mkdir(exist_ok=True)
10
+
11
+ for pdf_file in PDF_FOLDER.glob("*.pdf"):
12
+
13
+ print(f"Reading {pdf_file.name}")
14
+
15
+ pdf = fitz.open(pdf_file)
16
+
17
+ pages = []
18
+
19
+ for page_number, page in enumerate(tqdm(pdf), start=1):
20
+
21
+ text = page.get_text("text")
22
+
23
+ pages.append({
24
+ "page": page_number,
25
+ "text": text
26
+ })
27
+
28
+ output_file = OUTPUT_FOLDER / f"{pdf_file.stem}.json"
29
+
30
+ with open(output_file, "w", encoding="utf-8") as f:
31
+
32
+ json.dump(
33
+ pages,
34
+ f,
35
+ ensure_ascii=False,
36
+ indent=2
37
+ )
38
+
39
+ print("Done")
requirements.txt ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ annotated-doc==0.0.4
2
+ annotated-types==0.7.0
3
+ anyio==4.14.1
4
+ click==8.4.2
5
+ colorama==0.4.6
6
+ fastapi==0.138.1
7
+ h11==0.16.0
8
+ idna==3.18
9
+ numpy==2.5.0
10
+ pandas==3.0.3
11
+ pydantic==2.13.4
12
+ pydantic_core==2.46.4
13
+ PyMuPDF==1.27.2.3
14
+ python-dateutil==2.9.0.post0
15
+ python-dotenv==1.2.2
16
+ six==1.17.0
17
+ starlette==1.3.1
18
+ typing-inspection==0.4.2
19
+ typing_extensions==4.15.0
20
+ tzdata==2026.2
21
+ uvicorn==0.49.0
22
+
23
+ # Added for AI Knowledge Pipeline
24
+ sentence-transformers==3.0.1
25
+ pinecone-client==5.0.1
26
+ tiktoken==0.7.0
27
+ google-genai>=1.0.0
28
+ httpx==0.27.0
29
+ structlog==24.4.0
30
+ groq>=0.9.0