Skip to main content

property-service — AI_INTEGRATION

Companion: DOMAIN_MODEL · API_CONTRACTS · SECURITY_MODEL · ../../docs/08-ai-architecture.md · ../../docs/02-enterprise-architecture.md §AI Architecture

property-service consumes AI capabilities for content authoring assistance, never for autonomous mutation of guest-visible data. Every AI call traverses ai-orchestrator-service, never a model provider directly. Every AI-derived field carries AIProvenance and stays in a suggested state until a human accepts it (HITL).

Hard rule: No AI suggestion ever flips a property to published, mutates a price, changes a policy field, or alters a status transition. AI is a drafting peer; the operator is the publisher.


1. Capabilities

IDCapabilityModels routed by orchestratorTriggerOutputHITL gate
prop.describe.draftMulti-lingual property description draftVertex AI Gemini (cloud); fallback ONNX (edge) for low-bandwidth tenantsPOST /properties/:id/ai/draft-description or operator action in desktop{ locale, draft, rationale, sourceFields }[] for each enabled localeYes — decision_pending until `decision.recorded.v1(accept
prop.translateTranslate an existing description to a missing localeVertex AI GeminiPOST /properties/:id/ai/translate with targetLocales[]translated draft per localeYes
prop.photo.tagAuto-tag photos (front_facade, room_interior, pool, bathroom, lobby, …)Vertex AI VisionTriggered by inbound melmastoon.media.asset.scanned.v1 for photos linked to a propertytags[], confidence per tagYes — operator edits before commit
prop.photo.amenity.suggestSuggest amenity codes from photo evidenceVertex AI Vision + GeminiSame trigger as above; runs after taggingamenitySuggestions[]: { code, confidence, evidencePhotoIds }Yes
prop.photo.altTextGenerate accessibility alt text per photoVertex AI VisionAfter taggingaltText: I18nStringYes
prop.geocode.fallbackAddress → lat/lng when the primary geocoder (Mapbox / Google Places via geo-service) declines or returns low confidenceVertex AI Gemini with grounded searchOnly when geo-service returns confidence < 0.6 or unavailable{ lat, lng, confidence, source: 'ai_fallback' }Yes — operator must visually confirm pin
prop.policy.tone.lintLints free-text policy fields for clarity, tone, and culturally appropriate phrasingVertex AI GeminiOperator-triggeredsuggestions[] (rewrites)Yes

2. Orchestrator Contract

All calls go to ai-orchestrator-service over its internal RPC surface:

POST /v1/orchestration/run
{
"capability": "prop.describe.draft",
"tenantId": "tnt_…",
"callerService": "property-service",
"callerRequestId": "req_…",
"input": { "propertyId": "ppt_…", "locales": ["en","ps","fa","tg"], "tone": "warm-neutral" },
"constraints": { "maxLatencyMs": 8000, "redactPII": true },
"policy": { "moderation": "strict", "hitl": "required" }
}

The orchestrator returns:

{
"runId": "run_01H...",
"modelRoutingDecision": "vertex.gemini-1.5-pro",
"tokensIn": 1820, "tokensOut": 940,
"latencyMs": 4230,
"moderation": { "flagged": false, "categories": [] },
"output": { /* capability-specific */ },
"provenance": {
"runId": "run_01H...",
"provider": "vertex",
"model": "gemini-1.5-pro-002",
"policyVersion": "v3",
"promptHash": "sha256:...",
"occurredAt": "2026-04-22T10:00:00.000Z"
}
}

property-service persists provenance on the affected entity (e.g., photos.ai_provenance, property_translations.ai_provenance).


3. Suggestion Lifecycle

  1. Issue. A capability is invoked (operator-triggered or event-driven).
  2. Stage. Output is persisted as a suggestion: column _suggested_* or row in ai_suggestions (per DOMAIN_MODEL §11). The visible field on the entity is not mutated.
  3. Surface. The desktop / tenant booking BFF surfaces a "Review AI suggestion" badge with aiProvenance.runId shown.
  4. Decide. The operator accepts (full / partial), rejects, or edits-then-accepts. The decision is recorded via POST /ai/decisions and emits melmastoon.ai.decision.recorded.v1.
  5. Apply. On accept, the visible field is updated and aiProvenance is preserved alongside the value. On reject, the suggestion is archived.

Every applied AI value carries (forever):

  • aiProvenance.runId
  • aiProvenance.acceptedBy (usr_…)
  • aiProvenance.acceptedAt
  • aiProvenance.editedBeforeAccept (boolean)

4. Edge / Offline Inference

For tenants in low-bandwidth regions, prop.describe.draft and prop.photo.tag may run via ONNX Runtime packaged with the Electron app. The orchestrator decides routing based on the tenant's ai.routingPreference (cloud_only | edge_preferred | auto). Edge-derived suggestions still emit aiProvenance with provider: "onnx-edge" and a model digest.

The orchestrator (not property-service) is responsible for ONNX model distribution; property-service only consumes the orchestrator's run result.


5. Moderation

All free-text outputs (prop.describe.draft, prop.translate, prop.policy.tone.lint) pass through the orchestrator's moderation layer before being staged as suggestions. Categories enforced:

  • Hate / political extremism
  • Adult content
  • Cultural / religious insensitivity (regional rule pack: Pashto/Dari/Persian/Tajik audiences)
  • Disallowed claims (e.g., "best in country", "luxury" without star rating support)

A flagged output is not staged. The orchestrator returns { moderation.flagged: true } and property-service records a MELMASTOON.AI.SUGGESTION_BLOCKED_BY_MODERATION audit row; no domain event is emitted.


6. PII Handling

Property descriptions and addresses occasionally contain incidental PII (former owner names, contact phones). The orchestrator request sets constraints.redactPII: true, which directs the orchestrator to strip detected PII before model invocation. The redaction map is preserved in the run record (under platform retention) but is not echoed back to property-service.


7. Geocoding Fallback Specifics

The prop.geocode.fallback capability is only invoked when:

  • geo-service returns confidence < 0.6, or
  • geo-service is unavailable past its timeout, and
  • the operator has explicitly toggled "Try AI fallback" on the address form.

Output requires explicit operator confirmation (the desktop renders the candidate pin on a Leaflet map; no auto-commit). On accept, the property's geo_source is set to 'ai_fallback' and the resulting event melmastoon.property.updated.v1 carries aiProvenance for observability.


8. Cost & Quota Posture

  • Each AI capability has a per-tenant monthly quota enforced by the orchestrator. property-service surfaces a friendly MELMASTOON.AI.QUOTA_EXHAUSTED (HTTP 429) response with Retry-After.
  • Operators can run any capability up to N times per entity per day (default 5 for description drafts, 20 for photo tagging on bulk uploads). Limits configurable per tenant.
  • property-service emits melmastoon.property.ai.usage.recorded.v1 (audit-class event) for billing reconciliation.

9. Failure Modes

FailureBehavior
Orchestrator unavailableAPI returns 503 with Retry-After; UI degrades to "AI temporarily unavailable" without blocking manual editing
Moderation flaggedSuggestion not staged; operator sees neutral message: "Couldn't generate a suitable draft."
Quota exhausted429 MELMASTOON.AI.QUOTA_EXHAUSTED
Output schema mismatchHard fail; alerts on-call (property_ai_schema_violations_total)
Edge model digest mismatchRun rejected; orchestrator pushes a new digest before retrying

10. Anti-Patterns (forbidden)

  • Calling Vertex AI / OpenAI / any model provider SDK directly from property-service.
  • Auto-applying any AI output without an explicit decision.recorded event.
  • Suggesting a price, an availability change, a policy override, or a publication transition.
  • Persisting an AI value without aiProvenance.

Provenance schema and orchestrator API are defined platform-wide in docs/08-ai-architecture.md. Suggestion endpoint shapes live in API_CONTRACTS §AI.