agroplan-ai-cli 1.0.27 → 1.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,6 +20,10 @@ DATA_MODE=hybrid
20
20
  WEATHER_PROVIDER=open-meteo
21
21
  PROVIDER_CACHE_TTL=3600
22
22
 
23
+ # Debug Configuration
24
+ # Set to true to expose detailed error tracebacks (development only)
25
+ DEBUG_ERRORS=false
26
+
23
27
  # Cache Administration (optional)
24
28
  # If set, /cache/limpar endpoint requires X-Cache-Token header
25
29
  # Leave empty to disable protection (not recommended for production)
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli_version": "1.0.27",
3
- "backend_template_version": "1.0.27",
2
+ "cli_version": "1.0.28",
3
+ "backend_template_version": "1.0.28",
4
4
  "zarc_index_version": "2025-2026-fast-index-v2",
5
5
  "price_index_version": "2025-05-reference-v1",
6
6
  "features": [
@@ -15,7 +15,8 @@
15
15
  "price_unit_normalization",
16
16
  "market_profit_estimate",
17
17
  "market_profit_validation",
18
- "market_profit_confidence_refinement"
18
+ "market_profit_confidence_refinement",
19
+ "market_profit_comparative_evaluation"
19
20
  ],
20
- "generated_at": "2026-05-09T23:00:00Z"
21
+ "generated_at": "2026-05-09T23:30:00Z"
21
22
  }
@@ -28,6 +28,7 @@ CORS_ORIGINS = os.getenv("CORS_ORIGINS", "http://localhost:3000,http://127.0.0.1
28
28
  DATA_MODE = os.getenv("DATA_MODE", "hybrid") # simulated, real, hybrid
29
29
  WEATHER_PROVIDER = os.getenv("WEATHER_PROVIDER", "open-meteo")
30
30
  PROVIDER_CACHE_TTL = int(os.getenv("PROVIDER_CACHE_TTL", "3600"))
31
+ DEBUG_ERRORS = os.getenv("DEBUG_ERRORS", "false").lower() == "true"
31
32
 
32
33
  # Cache em memória para resultados pesados
33
34
  _resultados_cache = {}
@@ -580,6 +581,69 @@ def get_debug_lucro_mercado(
580
581
  except Exception as e:
581
582
  raise HTTPException(status_code=500, detail=str(e))
582
583
 
584
+ @app.get("/comparar/lucro-mercado")
585
+ def comparar_lucro_mercado(
586
+ objetivo: str = Query("equilibrado", description="Objetivo de otimização"),
587
+ seed: int = Query(42, description="Seed para reprodutibilidade"),
588
+ geracoes: int = Query(100, description="Número de gerações do AG"),
589
+ populacao: int = Query(50, description="Tamanho da população"),
590
+ lat: Optional[float] = Query(None, description="Latitude"),
591
+ lon: Optional[float] = Query(None, description="Longitude"),
592
+ days: int = Query(30, description="Dias para análise climática"),
593
+ uf: Optional[str] = Query(None, description="Unidade Federativa (ex: SP, PR)"),
594
+ municipio: Optional[str] = Query(None, description="Município"),
595
+ safra: str = Query("2025/2026", description="Safra ZARC")
596
+ ):
597
+ """
598
+ Avalia o plano do sistema usando lucro de mercado para comparação.
599
+
600
+ NÃO gera um plano otimizado por mercado. Apenas:
601
+ 1. Gera plano normal com AG usando lucro do sistema
602
+ 2. Avalia esse mesmo plano com lucro de mercado normalizado
603
+ 3. Compara os dois valores de lucro
604
+
605
+ Retorna:
606
+ - modo: "avaliacao_comparativa"
607
+ - plano_sistema: Plano otimizado pelo AG normal
608
+ - avaliacao_mercado: Avaliação do mesmo plano com lucro de mercado
609
+ - comparacao: Diferenças e validação
610
+ """
611
+ try:
612
+ from core.market_profit_comparator import comparar_plano_sistema_com_avaliacao_mercado
613
+
614
+ culturas, talhoes, regras = get_dados()
615
+
616
+ resultado = comparar_plano_sistema_com_avaliacao_mercado(
617
+ culturas=culturas,
618
+ talhoes=talhoes,
619
+ regras=regras,
620
+ uf=uf,
621
+ municipio=municipio,
622
+ safra=safra,
623
+ objetivo=objetivo,
624
+ seed=seed,
625
+ geracoes=geracoes,
626
+ populacao=populacao
627
+ )
628
+
629
+ return converter_tipos_python(resultado)
630
+
631
+ except Exception as e:
632
+ import traceback
633
+ if DEBUG_ERRORS:
634
+ raise HTTPException(
635
+ status_code=500,
636
+ detail={
637
+ "error": str(e),
638
+ "traceback": traceback.format_exc()
639
+ }
640
+ )
641
+ else:
642
+ raise HTTPException(
643
+ status_code=500,
644
+ detail="Erro ao gerar avaliação comparativa de lucro de mercado."
645
+ )
646
+
583
647
  @app.get("/dashboard")
584
648
  def get_dashboard(
585
649
  lat: Optional[float] = None,
@@ -0,0 +1,171 @@
1
+ """
2
+ Avaliação Comparativa: Plano Sistema vs Avaliação com Lucro de Mercado
3
+
4
+ Avalia o plano otimizado pelo sistema usando lucro de mercado normalizado.
5
+ NÃO gera um novo plano otimizado por mercado - apenas avalia o plano atual.
6
+
7
+ Bloqueia uso automático se houver itens críticos ou baixa confiabilidade.
8
+ """
9
+
10
+ from typing import Dict, List, Optional
11
+ from core.planner import gerar_plano_genetico
12
+ from core.price_adapter import aplicar_precos_no_plano
13
+ from core.market_profit_validator import validar_plano_lucro_mercado
14
+
15
+
16
+ def comparar_plano_sistema_com_avaliacao_mercado(
17
+ culturas: List[Dict],
18
+ talhoes: List[Dict],
19
+ regras: List[Dict],
20
+ uf: Optional[str] = None,
21
+ municipio: Optional[str] = None,
22
+ safra: str = "2025/2026",
23
+ objetivo: str = "equilibrado",
24
+ seed: int = 42,
25
+ geracoes: int = 100,
26
+ populacao: int = 50
27
+ ) -> Dict:
28
+ """
29
+ Avalia o plano do sistema usando lucro de mercado para comparação.
30
+
31
+ NÃO gera um plano otimizado por mercado. Apenas:
32
+ 1. Gera plano normal com AG usando lucro do sistema
33
+ 2. Avalia esse mesmo plano com lucro de mercado normalizado
34
+ 3. Compara os dois valores de lucro
35
+
36
+ Args:
37
+ culturas: Lista de culturas disponíveis
38
+ talhoes: Lista de talhões
39
+ regras: Regras de compatibilidade
40
+ uf: Unidade Federativa
41
+ municipio: Município
42
+ safra: Safra agrícola
43
+ objetivo: Objetivo de otimização
44
+ seed: Seed para reprodutibilidade
45
+ geracoes: Número de gerações do AG
46
+ populacao: Tamanho da população
47
+
48
+ Returns:
49
+ Dict com modo, plano_sistema, avaliacao_mercado e comparacao
50
+ """
51
+
52
+ # 1. Gerar plano principal normalmente
53
+ resultado = gerar_plano_genetico(
54
+ culturas=culturas,
55
+ talhoes=talhoes,
56
+ regras=regras,
57
+ objetivo=objetivo,
58
+ seed=seed,
59
+ geracoes=geracoes,
60
+ populacao=populacao
61
+ )
62
+
63
+ # 2. Aplicar ZARC, se houver UF
64
+ if uf:
65
+ try:
66
+ from core.zarc_adapter import enriquecer_plano_com_zarc
67
+ resultado = enriquecer_plano_com_zarc(
68
+ resultado,
69
+ uf=uf,
70
+ municipio=municipio,
71
+ safra=safra
72
+ )
73
+ except Exception as zarc_error:
74
+ resultado["zarc_error"] = str(zarc_error)
75
+
76
+ # 3. Aplicar preços
77
+ # IMPORTANTE: aplicar_precos_no_plano só aceita uf por enquanto
78
+ resultado = aplicar_precos_no_plano(resultado, uf=uf)
79
+
80
+ # 4. Validar lucro de mercado
81
+ resultado = validar_plano_lucro_mercado(resultado)
82
+
83
+ plano = resultado.get("plano", [])
84
+
85
+ lucro_sistema_total = float(resultado.get("lucro_total", 0) or 0)
86
+
87
+ lucro_mercado_total = 0.0
88
+ itens_mercado = []
89
+
90
+ for item in plano:
91
+ lucro_mercado = item.get("lucro_mercado_estimado")
92
+
93
+ if lucro_mercado is not None:
94
+ try:
95
+ lucro_mercado_float = float(lucro_mercado)
96
+ lucro_mercado_total += lucro_mercado_float
97
+ except Exception:
98
+ lucro_mercado_float = None
99
+ else:
100
+ lucro_mercado_float = None
101
+
102
+ itens_mercado.append({
103
+ "talhao": item.get("talhao"),
104
+ "cultura": item.get("cultura"),
105
+ "lucro_sistema": item.get("lucro_estimado"),
106
+ "lucro_mercado_estimado": lucro_mercado_float,
107
+ "preco_real": item.get("preco_real"),
108
+ "preco_normalizado": item.get("preco_normalizado"),
109
+ "validacao_lucro_mercado": item.get("validacao_lucro_mercado")
110
+ })
111
+
112
+ diferenca_absoluta = lucro_mercado_total - lucro_sistema_total
113
+
114
+ if lucro_sistema_total != 0:
115
+ diferenca_percentual = (diferenca_absoluta / abs(lucro_sistema_total)) * 100
116
+ else:
117
+ diferenca_percentual = 0
118
+
119
+ validacao = resultado.get("validacao_lucro_mercado", {}) or {}
120
+
121
+ total = len(plano)
122
+ alta = int(validacao.get("itens_alta_confiabilidade", 0) or 0)
123
+ media = int(validacao.get("itens_media_confiabilidade", 0) or 0)
124
+ baixa = int(validacao.get("itens_baixa_confiabilidade", 0) or 0)
125
+ criticos = int(validacao.get("itens_criticos", 0) or 0)
126
+
127
+ percentual_alta = (alta / total * 100) if total else 0
128
+
129
+ pode_usar_mercado = (
130
+ criticos == 0
131
+ and baixa == 0
132
+ and percentual_alta >= 70
133
+ )
134
+
135
+ motivo_bloqueio = None
136
+ if not pode_usar_mercado:
137
+ motivos = []
138
+ if criticos > 0:
139
+ motivos.append(f"{criticos} item(ns) crítico(s)")
140
+ if baixa > 0:
141
+ motivos.append(f"{baixa} item(ns) de baixa confiabilidade")
142
+ if percentual_alta < 70:
143
+ motivos.append(f"apenas {percentual_alta:.1f}% dos itens têm alta confiabilidade")
144
+
145
+ motivo_bloqueio = (
146
+ "O lucro de mercado não deve ser usado como recomendação principal: "
147
+ + "; ".join(motivos)
148
+ )
149
+
150
+ return {
151
+ "modo": "avaliacao_comparativa",
152
+ "descricao": "Avalia o plano principal usando lucro de mercado normalizado, sem substituir a recomendação oficial.",
153
+ "plano_sistema": resultado,
154
+ "avaliacao_mercado": {
155
+ "lucro_mercado_total": lucro_mercado_total,
156
+ "itens": itens_mercado
157
+ },
158
+ "comparacao": {
159
+ "lucro_sistema_total": lucro_sistema_total,
160
+ "lucro_mercado_total": lucro_mercado_total,
161
+ "diferenca_absoluta": diferenca_absoluta,
162
+ "diferenca_percentual": diferenca_percentual,
163
+ "itens_alta_confiabilidade": alta,
164
+ "itens_media_confiabilidade": media,
165
+ "itens_baixa_confiabilidade": baixa,
166
+ "itens_criticos": criticos,
167
+ "percentual_alta_confiabilidade": percentual_alta,
168
+ "pode_usar_mercado": pode_usar_mercado,
169
+ "motivo_bloqueio": motivo_bloqueio
170
+ }
171
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroplan-ai-cli",
3
- "version": "1.0.27",
3
+ "version": "1.0.28",
4
4
  "description": "CLI global para AgroPlan AI - modo local acelerado",
5
5
  "type": "module",
6
6
  "bin": {