Geocoding fallback: use the registered AI model when the table can't resolve
CI/CD / CI · dotnet build (push) Successful in 1m15s
CI/CD / Deploy · hamkadr (push) Successful in 1m1s

Where deterministic geocoding gives up (neighborhood not in the TehranGeo table),
fall back to the registered AI model: the auditor now also returns approximate
lat/lng for a recognized Tehran neighborhood (folded into the existing single
audit call — no extra requests), and Publish uses it only after the source ad and
the local table, and only when it falls inside greater Tehran (InTehran bbox
guard rejects hallucinated points). Coords order: Divar point → TehranGeo → AI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-20 15:48:42 +03:30
parent 85a5191c45
commit e582597b20
2 changed files with 14 additions and 2 deletions
@@ -314,6 +314,10 @@ public class IngestionService
if (appLat is null && city.Name == "تهران"
&& TehranGeo.Locate(district?.Name, districtName, parsed.AreaNote) is { } g)
{ appLat = g.lat; appLng = g.lng; }
// Last resort — the AI model's inferred coords, but ONLY when they fall inside greater Tehran
// (rejects a hallucinated point elsewhere). Uses the registered model where the rules can't decide.
if (appLat is null && d?.Lat is double aLat && d?.Lng is double aLng && InTehran(aLat, aLng))
{ appLat = aLat; appLng = aLng; }
var kindStr = (d?.Kind ?? parsed.Kind.ToString()).ToLowerInvariant();
@@ -544,6 +548,9 @@ public class IngestionService
private static string Clamp(string s, int max) => s.Length <= max ? s : s[..max].Trim();
/// <summary>Greater-Tehran bounding box — rejects out-of-area (hallucinated) AI coordinates.</summary>
private static bool InTehran(double lat, double lng) => lat is >= 35.4 and <= 35.95 && lng is >= 51.0 and <= 51.8;
// Gender/seniority tokens that don't belong in a role name (they go to tags / the Gender field).
private static readonly string[] RoleModifierWords =
{ "آقا", "خانم", "خانوم", "بانو", "مرد", "زن", "کارآموز", "کارورز", "ارشد", "مبتدی" };