Tracy André commited on
Commit
7cf64b9
·
1 Parent(s): a764c8f
Files changed (2) hide show
  1. app.py +105 -88
  2. requirements.txt +2 -6
app.py CHANGED
@@ -1,15 +1,5 @@
1
  import os
2
  import gradio as gr
3
- from fastapi import FastAPI
4
- from gradio.routes import mount_gradio_app
5
-
6
- from starlette.middleware import Middleware
7
- from starlette.middleware.base import BaseHTTPMiddleware
8
- from starlette.requests import Request
9
- from starlette.responses import JSONResponse
10
-
11
- # ==== FastMCP ====
12
- from fastmcp import FastMCP
13
 
14
  # Import your existing Gradio app and analysis tools
15
  from gradio_app import create_gradio_app
@@ -18,34 +8,29 @@ from analysis_tools import AgriculturalAnalyzer
18
 
19
  # --------- Config ---------
20
  PORT = int(os.environ.get("PORT", 7860))
21
- MCP_BEARER = os.getenv("MCP_BEARER", "") # ajouter dans Settings > Variables & secrets si besoin
22
 
23
  # Initialize agricultural components
24
  data_loader = AgriculturalDataLoader()
25
  analyzer = AgriculturalAnalyzer(data_loader)
26
 
27
- # --------- Auth middleware (optionnel) ---------
28
- class BearerAuthMiddleware(BaseHTTPMiddleware):
29
- def __init__(self, app, token: str | None):
30
- super().__init__(app)
31
- self.token = token
32
-
33
- async def dispatch(self, request: Request, call_next):
34
- if self.token:
35
- auth = request.headers.get("authorization", "")
36
- if not auth.startswith("Bearer ") or auth.split(" ", 1)[1] != self.token:
37
- return JSONResponse({"detail": "Unauthorized"}, status_code=401)
38
- return await call_next(request)
39
-
40
- # --------- Déclare le serveur MCP ---------
41
- mcp = FastMCP("Agricultural Analysis Tools")
42
-
43
- # --------- Outils MCP agricoles ---------
44
- @mcp.tool
45
- def analyze_weed_pressure(years: list[int] | None = None, plots: list[str] | None = None) -> str:
46
- """Analyze weed pressure trends using IFT herbicide data from Kerguéhennec experimental station."""
47
  try:
48
- trends = analyzer.analyze_weed_pressure_trends(years=years, plots=plots)
 
 
 
 
49
  summary_stats = trends['summary']
50
 
51
  result = f"""🌿 ANALYSE DE LA PRESSION ADVENTICES (IFT Herbicides)
@@ -67,11 +52,19 @@ def analyze_weed_pressure(years: list[int] | None = None, plots: list[str] | Non
67
  except Exception as e:
68
  return f"❌ Erreur lors de l'analyse: {str(e)}"
69
 
70
- @mcp.tool
71
- def predict_future_pressure(target_years: list[int] | None = None, max_ift: float = 1.0) -> str:
72
- """Predict future weed pressure and identify suitable plots for sensitive crops."""
 
 
 
 
 
 
 
 
73
  try:
74
- year_list = target_years or [2025, 2026, 2027]
75
  predictions = analyzer.predict_weed_pressure(target_years=year_list)
76
  model_perf = predictions['model_performance']
77
 
@@ -106,9 +99,13 @@ def predict_future_pressure(target_years: list[int] | None = None, max_ift: floa
106
  except Exception as e:
107
  return f"❌ Erreur lors de la prédiction: {str(e)}"
108
 
109
- @mcp.tool
110
  def analyze_crop_rotation() -> str:
111
- """Analyze the impact of crop rotations on weed pressure at Kerguéhennec station."""
 
 
 
 
112
  try:
113
  rotation_impact = analyzer.analyze_crop_rotation_impact()
114
 
@@ -129,9 +126,13 @@ def analyze_crop_rotation() -> str:
129
  except Exception as e:
130
  return f"❌ Erreur lors de l'analyse des rotations: {str(e)}"
131
 
132
- @mcp.tool
133
  def get_dataset_summary() -> str:
134
- """Get a comprehensive summary of the agricultural dataset from Kerguéhennec experimental station."""
 
 
 
 
135
  try:
136
  df = data_loader.load_all_files()
137
  if df.empty:
@@ -157,57 +158,73 @@ def get_dataset_summary() -> str:
157
  except Exception as e:
158
  return f"❌ Erreur lors du chargement des données: {str(e)}"
159
 
160
- @mcp.resource("agricultural://dataset/summary")
161
  def dataset_resource() -> str:
162
- """Agricultural dataset summary resource."""
163
  return get_dataset_summary()
164
 
165
- # Crée l'app ASGI FastMCP et protège-la éventuellement
166
- mcp_app = mcp.http_app( # cf. doc: http_app() retourne une app ASGI montable
167
- path="/", # endpoint interne de l'app MCP => "/"
168
- custom_middleware=[
169
- Middleware(BearerAuthMiddleware, token=MCP_BEARER)
170
- ] if MCP_BEARER else []
171
- )
172
-
173
- # --------- App FastAPI parente (lifespan important) ---------
174
- app = FastAPI(title="Agricultural Analysis - Gradio + FastMCP", lifespan=mcp_app.lifespan)
175
-
176
- # Health simple
177
- @app.get("/health")
178
- def health():
179
- return {"ok": True, "service": "agricultural-mcp-server", "version": "1.0.0"}
180
-
181
- # Monte MCP sous /mcp/ (final: /mcp/…)
182
- app.mount("/mcp", mcp_app)
183
-
184
- # --------- UI Gradio (utilise ton interface existante) ---------
 
 
 
 
 
 
 
 
 
 
185
  demo = create_gradio_app()
186
 
187
- # Monte Gradio à la racine
188
- app = mount_gradio_app(app, demo, path="/")
189
-
190
- # --------- Entrée locale facultative ---------
191
  if __name__ == "__main__":
192
- import uvicorn
193
- uvicorn.run(app, host="0.0.0.0", port=PORT)
194
-
195
- # ========= Tests FastMCP (exemples curl) =========
196
- # Health
197
- # curl -s https://hackathoncra-mcp.hf.space/health
198
-
199
- # MCP tools list
200
- # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp/ \
201
- # -H "Content-Type: application/json" \
202
- # -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'
203
-
204
- # MCP tool call
205
- # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp/ \
206
- # -H "Content-Type: application/json" \
207
- # -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "get_dataset_summary", "arguments": {}}}'
208
-
209
- # MCP avec Bearer (si MCP_BEARER défini)
210
- # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp/ \
211
- # -H "Authorization: Bearer VOTRE_TOKEN" \
212
- # -H "Content-Type: application/json" \
213
- # -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
3
 
4
  # Import your existing Gradio app and analysis tools
5
  from gradio_app import create_gradio_app
 
8
 
9
  # --------- Config ---------
10
  PORT = int(os.environ.get("PORT", 7860))
 
11
 
12
  # Initialize agricultural components
13
  data_loader = AgriculturalDataLoader()
14
  analyzer = AgriculturalAnalyzer(data_loader)
15
 
16
+ # --------- Fonctions MCP pour outils agricoles ---------
17
+ @gr.mcp.tool()
18
+ def analyze_weed_pressure(years: str = "", plots: str = "") -> str:
19
+ """Analyze weed pressure trends using IFT herbicide data from Kerguéhennec experimental station.
20
+
21
+ Args:
22
+ years: Comma-separated list of years to analyze (e.g., "2020,2021,2022"). Leave empty for all years.
23
+ plots: Comma-separated list of plot names to analyze (e.g., "P1,P2,P3"). Leave empty for all plots.
24
+
25
+ Returns:
26
+ Detailed analysis of weed pressure with IFT statistics and interpretation.
27
+ """
 
 
 
 
 
 
 
 
28
  try:
29
+ # Parse parameters
30
+ year_list = [int(y.strip()) for y in years.split(",")] if years.strip() else None
31
+ plot_list = [p.strip() for p in plots.split(",")] if plots.strip() else None
32
+
33
+ trends = analyzer.analyze_weed_pressure_trends(years=year_list, plots=plot_list)
34
  summary_stats = trends['summary']
35
 
36
  result = f"""🌿 ANALYSE DE LA PRESSION ADVENTICES (IFT Herbicides)
 
52
  except Exception as e:
53
  return f"❌ Erreur lors de l'analyse: {str(e)}"
54
 
55
+ @gr.mcp.tool()
56
+ def predict_future_pressure(target_years: str = "2025,2026,2027", max_ift: float = 1.0) -> str:
57
+ """Predict future weed pressure and identify suitable plots for sensitive crops.
58
+
59
+ Args:
60
+ target_years: Comma-separated list of years to predict (e.g., "2025,2026,2027")
61
+ max_ift: Maximum IFT threshold for sensitive crops (default: 1.0)
62
+
63
+ Returns:
64
+ Predictions for each year with suitable plots for sensitive crops.
65
+ """
66
  try:
67
+ year_list = [int(y.strip()) for y in target_years.split(",")]
68
  predictions = analyzer.predict_weed_pressure(target_years=year_list)
69
  model_perf = predictions['model_performance']
70
 
 
99
  except Exception as e:
100
  return f"❌ Erreur lors de la prédiction: {str(e)}"
101
 
102
+ @gr.mcp.tool()
103
  def analyze_crop_rotation() -> str:
104
+ """Analyze the impact of crop rotations on weed pressure at Kerguéhennec station.
105
+
106
+ Returns:
107
+ Analysis of the best crop rotations with lowest average IFT herbicide usage.
108
+ """
109
  try:
110
  rotation_impact = analyzer.analyze_crop_rotation_impact()
111
 
 
126
  except Exception as e:
127
  return f"❌ Erreur lors de l'analyse des rotations: {str(e)}"
128
 
129
+ @gr.mcp.tool()
130
  def get_dataset_summary() -> str:
131
+ """Get a comprehensive summary of the agricultural dataset from Kerguéhennec experimental station.
132
+
133
+ Returns:
134
+ Complete summary with statistics, top crops, top plots and data coverage.
135
+ """
136
  try:
137
  df = data_loader.load_all_files()
138
  if df.empty:
 
158
  except Exception as e:
159
  return f"❌ Erreur lors du chargement des données: {str(e)}"
160
 
161
+ @gr.mcp.resource("agricultural://dataset/summary")
162
  def dataset_resource() -> str:
163
+ """Agricultural dataset summary resource for Kerguéhennec experimental station."""
164
  return get_dataset_summary()
165
 
166
+ @gr.mcp.prompt()
167
+ def agricultural_analysis_prompt(analysis_type: str = "general", focus: str = "sustainability") -> str:
168
+ """Generate analysis prompts for agricultural data interpretation.
169
+
170
+ Args:
171
+ analysis_type: Type of analysis (general, weed_pressure, rotation, prediction)
172
+ focus: Focus area (sustainability, productivity, reduction)
173
+
174
+ Returns:
175
+ Customized prompt for agricultural analysis.
176
+ """
177
+ prompts = {
178
+ "general": "Analyze the agricultural data to provide insights on farming practices and sustainability",
179
+ "weed_pressure": "Focus on weed pressure analysis and herbicide usage patterns",
180
+ "rotation": "Examine crop rotation strategies and their impact on weed management",
181
+ "prediction": "Predict future agricultural trends and provide recommendations"
182
+ }
183
+
184
+ focus_additions = {
185
+ "sustainability": "with emphasis on sustainable and eco-friendly practices",
186
+ "productivity": "focusing on maximizing crop productivity and yield",
187
+ "reduction": "prioritizing herbicide reduction and organic alternatives"
188
+ }
189
+
190
+ base_prompt = prompts.get(analysis_type, prompts["general"])
191
+ focus_addition = focus_additions.get(focus, focus_additions["sustainability"])
192
+
193
+ return f"{base_prompt} {focus_addition}. Consider IFT values, crop rotations, and environmental impact in your analysis."
194
+
195
+ # --------- Interface Gradio principale ---------
196
  demo = create_gradio_app()
197
 
198
+ # --------- Lancement avec serveur MCP intégré ---------
 
 
 
199
  if __name__ == "__main__":
200
+ demo.launch(
201
+ mcp_server=True, # Active le serveur MCP intégré
202
+ server_name="0.0.0.0",
203
+ server_port=PORT,
204
+ share=False
205
+ )
206
+
207
+ # ========= Configuration MCP pour clients =========
208
+ # L'endpoint MCP sera disponible à : https://hackathoncra-mcp.hf.space/gradio_api/mcp/sse
209
+ #
210
+ # Configuration pour MCP Inspector ou autres clients:
211
+ # {
212
+ # "mcpServers": {
213
+ # "agricultural-analysis": {
214
+ # "url": "https://hackathoncra-mcp.hf.space/gradio_api/mcp/sse"
215
+ # }
216
+ # }
217
+ # }
218
+ #
219
+ # Pour Claude Desktop (avec mcp-remote):
220
+ # {
221
+ # "mcpServers": {
222
+ # "agricultural-analysis": {
223
+ # "command": "npx",
224
+ # "args": [
225
+ # "mcp-remote",
226
+ # "https://hackathoncra-mcp.hf.space/gradio_api/mcp/sse"
227
+ # ]
228
+ # }
229
+ # }
230
+ # }
requirements.txt CHANGED
@@ -1,7 +1,4 @@
1
- fastapi>=0.112
2
- uvicorn[standard]>=0.30
3
- gradio>=4.43
4
- fastmcp>=2.3.2
5
  pandas>=2.0.0
6
  numpy>=1.24.0
7
  matplotlib>=3.6.0
@@ -10,5 +7,4 @@ scikit-learn>=1.3.0
10
  datasets>=2.14.0
11
  huggingface_hub>=0.17.0
12
  openpyxl>=3.1.0
13
- plotly>=5.15.0
14
- python-multipart>=0.0.6
 
1
+ gradio[mcp]>=4.43
 
 
 
2
  pandas>=2.0.0
3
  numpy>=1.24.0
4
  matplotlib>=3.6.0
 
7
  datasets>=2.14.0
8
  huggingface_hub>=0.17.0
9
  openpyxl>=3.1.0
10
+ plotly>=5.15.0