175 lines
6.5 KiB
Python
175 lines
6.5 KiB
Python
"""Test focalizado de Wave 2 (ValueEstimator + OfferStrategist) con deal real.
|
|
|
|
DEAL: 446 VERMONT Avenue, Green Cove Springs, FL 32043 — $219K MLS retail
|
|
- Clay County (NO court_records scraper — soft-fail NOT_IMPLEMENTED esperado)
|
|
- 2005 build, 3/2, 1461 sqft, tax $3348, insurance $876, rent $1789, no HOA
|
|
- ARV $240K (estimacion razonable para zona Class B/C 2005 build)
|
|
|
|
Criterios validados (8 puntos del spec del usuario):
|
|
1. ValueEstimator: comparison listing vs comps
|
|
2. ValueEstimator: rango low/mid/high
|
|
3. OfferStrategist: Strike/Stretch/Walk-Away
|
|
4. OfferStrategist: angulo de ataque (psychological points)
|
|
5. OfferStrategist: contra-ofertas anticipadas
|
|
6. OfferStrategist: si auction → MAB, si MLS → Strike (este deal es MLS)
|
|
7. Briefing: seccion "Valor Real vs Listing"
|
|
8. Briefing: seccion "Oferta Recomendada"
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from orchestrator import DealInputs, BuyerProfile, analyze_deal # noqa: E402
|
|
|
|
|
|
def status_cb(msg: str) -> None:
|
|
print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
|
|
|
|
|
|
def main() -> int:
|
|
print("=" * 70)
|
|
print("Wave 2 Validation — 446 Vermont Ave, Green Cove Springs FL $219K MLS")
|
|
print("=" * 70)
|
|
|
|
deal = DealInputs(
|
|
address="446 VERMONT Avenue, Green Cove Springs, FL 32043",
|
|
price=219_000,
|
|
rent=1_789,
|
|
property_tax=3_348,
|
|
insurance=876,
|
|
hoa=0,
|
|
sqft=1_461,
|
|
beds=3,
|
|
baths=2.0,
|
|
year_built=2005,
|
|
arv=240_000, # estimacion razonable; sin rehab significativo para 2005 build
|
|
rehab_override=8_000, # cosmetics solo, no major rehab
|
|
deal_type="mls", # NORMAL MLS retail, no auction
|
|
)
|
|
profile = BuyerProfile(
|
|
profile_class="C",
|
|
fico=720,
|
|
capital_available=65_000,
|
|
nationality="Argentina",
|
|
)
|
|
|
|
print(f"DEAL: {deal.address}")
|
|
print(f" price=${deal.price:,} rent=${deal.rent:,}/mo arv=${deal.arv:,}")
|
|
print(f" beds={deal.beds}/baths={deal.baths} sqft={deal.sqft} year={deal.year_built}")
|
|
print(f" tax=${deal.property_tax:,}/y insurance=${deal.insurance:,}/y hoa=${deal.hoa}/mo")
|
|
print(f" deal_type={deal.deal_type}, rehab_override=${deal.rehab_override:,}")
|
|
print()
|
|
|
|
t0 = time.perf_counter()
|
|
result = analyze_deal(deal, profile, photo_bytes=None, status_cb=status_cb)
|
|
elapsed = time.perf_counter() - t0
|
|
|
|
print()
|
|
print("=" * 70)
|
|
print(f"COMPLETADO en {elapsed:.0f}s ({elapsed/60:.1f} min)")
|
|
print("=" * 70)
|
|
|
|
# Get outputs
|
|
ve_out = (result.value_estimate or {}).get("output", "") or ""
|
|
os_out = (result.offer_strategy or {}).get("output", "") or ""
|
|
briefing_out = (result.executive_briefing or {}).get("output", "") or ""
|
|
|
|
# ===========================================================
|
|
# 8 CRITERIOS DE VALIDACION (spec del usuario)
|
|
# ===========================================================
|
|
print()
|
|
print("─" * 70)
|
|
print("VALIDACION 8 CRITERIOS")
|
|
print("─" * 70)
|
|
|
|
# 1. ValueEstimator: comparison listing vs comps
|
|
c1 = any(kw in ve_out.lower() for kw in [
|
|
"listing", "precio listado", "$219", "comp", "comparable"
|
|
])
|
|
print(f" 1. ValueEstimator: comparison listing vs comps: {'✅' if c1 else '❌'}")
|
|
|
|
# 2. ValueEstimator: rango low/mid/high
|
|
has_low = "low" in ve_out.lower() or "bajo:" in ve_out.lower()
|
|
has_mid = "mid" in ve_out.lower() or "medio:" in ve_out.lower()
|
|
has_high = "high" in ve_out.lower() or "alto:" in ve_out.lower()
|
|
c2 = has_low and has_mid and has_high
|
|
print(f" 2. ValueEstimator: rango low/mid/high: {'✅' if c2 else '⚠️ (parcial)'}")
|
|
|
|
# 3. OfferStrategist: Strike/Stretch/Walk-Away
|
|
has_strike = "strike" in os_out.lower()
|
|
has_stretch = "stretch" in os_out.lower()
|
|
has_walkaway = "walk-away" in os_out.lower() or "walk away" in os_out.lower()
|
|
c3 = has_strike and has_stretch and has_walkaway
|
|
print(f" 3. OfferStrategist: Strike/Stretch/Walk-Away: {'✅' if c3 else '⚠️ (parcial)'}")
|
|
|
|
# 4. OfferStrategist: angulo de ataque
|
|
c4 = any(kw in os_out.lower() for kw in [
|
|
"angulo de ataque", "ángulo de ataque", "angulo", "psicologic", "psicológ",
|
|
"presentacion", "argumento",
|
|
])
|
|
print(f" 4. OfferStrategist: angulo de ataque (psicologico): {'✅' if c4 else '❌'}")
|
|
|
|
# 5. OfferStrategist: contra-ofertas anticipadas
|
|
c5 = any(kw in os_out.lower() for kw in [
|
|
"contra-oferta", "contra oferta", "counter-offer", "counter offer",
|
|
"contraoferta", "anticipad",
|
|
])
|
|
print(f" 5. OfferStrategist: contra-ofertas anticipadas: {'✅' if c5 else '❌'}")
|
|
|
|
# 6. MLS deal → debe usar Strike NO MAB (este test es MLS)
|
|
has_mab_only = "mab" in os_out.lower() and not has_strike
|
|
c6 = has_strike and not has_mab_only
|
|
print(f" 6. MLS deal usa Strike (no MAB exclusivo): {'✅' if c6 else '❌'}")
|
|
|
|
# 7. Briefing: seccion "Valor Real" / "Valor vs Listing"
|
|
c7 = any(kw in briefing_out.lower() for kw in [
|
|
"valor real", "valor estimado", "estimación de valor", "estimacion de valor",
|
|
"valor vs listing", "valor del inmueble",
|
|
])
|
|
print(f" 7. Briefing incluye seccion Valor Real: {'✅' if c7 else '❌'}")
|
|
|
|
# 8. Briefing: seccion "Oferta Recomendada"
|
|
c8 = any(kw in briefing_out.lower() for kw in [
|
|
"oferta recomendada", "recomendación de oferta", "recomendacion de oferta",
|
|
"estrategia de oferta", "oferta sugerida", "strike", "walk-away",
|
|
])
|
|
print(f" 8. Briefing incluye seccion Oferta Recomendada: {'✅' if c8 else '❌'}")
|
|
|
|
print()
|
|
score = sum([c1, c2, c3, c4, c5, c6, c7, c8])
|
|
print(f"SCORE: {score}/8")
|
|
|
|
# ===========================================================
|
|
# Excerpts para revisión humana
|
|
# ===========================================================
|
|
print()
|
|
print("=" * 70)
|
|
print("EXCERPTS — ValueEstimator (primeras 800 chars)")
|
|
print("=" * 70)
|
|
print(ve_out[:800])
|
|
|
|
print()
|
|
print("=" * 70)
|
|
print("EXCERPTS — OfferStrategist (primeras 1000 chars)")
|
|
print("=" * 70)
|
|
print(os_out[:1000])
|
|
|
|
print()
|
|
print("=" * 70)
|
|
print("EXCERPTS — ContextualGlossaryAgent (primeras 1200 chars)")
|
|
print("=" * 70)
|
|
print(briefing_out[:1200])
|
|
|
|
return 0 if score >= 7 else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|