"""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())