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.
|
|
3
|
-
"backend_template_version": "1.0.
|
|
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:
|
|
21
|
+
"generated_at": "2026-05-09T23:30:00Z"
|
|
21
22
|
}
|
package/backend-template/api.py
CHANGED
|
@@ -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
|
+
}
|