AI_INTEGRATION — pricing-service
Sibling: APPLICATION_LOGIC · EVENT_SCHEMAS · SECURITY_MODEL
Strategic anchors: 08 AI Architecture · 02 §11 AI Layer
The pricing-service consumes one AI capability — Dynamic Pricing Suggestion — provided by ai-orchestrator-service (which routes to Vertex AI Gemini models on GCP). Suggestions are advisory only. They never mutate live rates without an explicit human approval (HITL) action; they never directly affect a guest-facing quote.
1. Capability summary
| Property | Value |
|---|---|
| Capability id | pricing.dynamic_suggestion |
| Provider | ai-orchestrator-service (Vertex AI Gemini-1.5-Pro, region me-central2) |
| Mode | Advisory + HITL gate |
| Output | DynamicPricingSuggestion aggregate with rate range and rationale |
| Trigger | Manual (revenue manager UI) and scheduled (nightly batch per property) |
| SLA | < 8 s p95 per suggestion request |
| Cost budget | ≤ $0.04 per suggestion (input ≤ 4 KB, output ≤ 1 KB) |
| Audit | melmastoon.pricing.dynamic_suggestion.generated.v1 + AIProvenance block |
The pricing-service is never the AI executor. It only:
- Builds the request signal payload (deterministic, redacted).
- Calls
ai-orchestrator-servicevia theAIClientport. - Persists the response as a
DynamicPricingSuggestion. - Validates and applies the human-accepted choice as a
RateRule.
2. Signal inputs (built locally, redacted)
The signal payload is assembled by SuggestionSignalCollector, which queries siblings through their public/internal APIs (no direct DB reads) and applies a fixed redaction policy.
interface SuggestionSignals {
occupancyRatio: number; // 0..1, from inventory-service
bookingPaceWow: number; // week-over-week pace ratio, from analytics-service
avgDailyRateMicro: bigint; // ADR for the property/room-type, last 28 days
competitorRateProxyMicro?: bigint; // optional, from rate-shopping-connector-service (anonymised)
eventCalendar: Array<{ name: string; date: string; weight: number }>; // events-service
weather?: { tempC: number; precipMm: number }; // optional, weather connector
shariaCompliantPlan: boolean; // local — narrows model output
currency: CurrencyCode;
historicalElasticity?: number; // estimated from past suggestion outcomes
}
Redaction rules (enforced at the boundary, not in the model):
- No booker PII ever leaves the pricing service. Signals are aggregated; per-reservation data is forbidden in the prompt.
- Competitor proxy values are pre-rounded to the nearest 5% and capped at the 95th percentile to remove identifiability.
- Tenant id is never sent to the model; the orchestrator handles tenant isolation via its own session token.
3. Request envelope to ai-orchestrator-service
POST /v1/inferences:invoke
X-Capability-Id: pricing.dynamic_suggestion
Authorization: Bearer <service-mesh-jwt>
{
"capabilityId": "pricing.dynamic_suggestion",
"tenantContext": { "tenantId": "tnt_…", "propertyId": "pty_…", "roomTypeId": "rmt_…", "date": "2026-05-12" },
"signals": { /* see §2 */ },
"constraints": {
"currency": "USD",
"minMicro": "100000000", // safety floor; never suggest below
"maxMicro": "500000000", // safety ceiling; never suggest above
"maxRangeWidthPct": 25, // refuse low/high spread > 25%
"shariaCompliant": false
},
"preferences": {
"languages": ["en"],
"rationaleStyle": "concise"
},
"idempotencyKey": "01H8Z…"
}
The orchestrator routes to Vertex AI, applies pricing/dynamic.v3 prompt, validates the model output against the output schema, records provenance, and returns:
{
"result": {
"baselineMicro": "150000000",
"range": { "lowMicro": "160000000", "highMicro": "180000000" },
"currency": "USD",
"rationale": "Occupancy projected to 92%; competitor mean +12%; Eid demand window.",
"signals": { /* echoed */ }
},
"provenance": {
"modelId": "vertex.gemini-1.5-pro@2026-03",
"promptVersion": "pricing/dynamic.v3",
"inputDigest": "sha256:abcd…",
"outputDigest": "sha256:wxyz…",
"redactionPolicy": "pii_v1",
"latencyMs": 4123,
"costMicroUsd": 38000
}
}
4. HITL gate
A suggestion never becomes a price by itself. The AcceptDynamicPricingSuggestionUseCase enforces:
- Actor must hold a HITL role (
revenue_manager,gm,owner). - The
chosenMicromust lie within[range.lowMicro, range.highMicro](bounded by safety constraints from §3). - The suggestion must be
status: "generated"and not pastexpiresAt(default 24 h). - The resulting
RateRulecarriessource: "ai_accepted"and inherits the suggestion'saiProvenanceso every guest-facing quote can be traced back through:Quote.derivation.steps[*].notes → RateRule.aiProvenance → DynamicSuggestion.aiProvenance → AI orchestrator log.
Rejected suggestions are persisted with status: "rejected", the rejecting actor, and an optional reason; they are surfaced in the MELMASTOON.PRICING.DPS_REJECTED analytics dashboard so the AI prompt/version can be tuned over time.
5. Output schema (validated by orchestrator AND by pricing-service)
{
"type": "object",
"required": ["baselineMicro","range","currency","rationale"],
"properties": {
"baselineMicro": { "type": "string", "pattern": "^[0-9]+$" },
"range": {
"type": "object",
"required": ["lowMicro","highMicro"],
"properties": {
"lowMicro": { "type": "string", "pattern": "^[0-9]+$" },
"highMicro": { "type": "string", "pattern": "^[0-9]+$" }
}
},
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
"rationale": { "type": "string", "minLength": 16, "maxLength": 1000 }
}
}
Defensive checks performed in pricing-service after deserialisation:
low ≤ baseline ≤ high(high - low) / baseline ≤ maxRangeWidthPctlow ≥ minMicro && high ≤ maxMicrocurrencymatches the room type's plan currency
Any failure → MELMASTOON.AI.OUTPUT_INVALID, suggestion is not persisted, an audit event melmastoon.audit.ai_output_rejected.v1 is published, and the operator UI shows "AI suggestion unavailable; please retry".
6. AIProvenance and auditing
Every DynamicPricingSuggestion and every AI-derived RateRule carries the canonical AIProvenance block defined in 08 §5:
interface AIProvenance {
modelId: string; // immutable model+version pin
promptVersion: string; // immutable prompt version
inputDigest: string; // sha256 of redacted prompt
outputDigest: string; // sha256 of model output
redactionPolicy: string; // 'pii_v1' etc.
latencyMs?: number;
costMicroUsd?: number;
}
Provenance is written to two places:
- The
pricing.dynamic_suggestions.ai_provenanceJSONB column. - The
audit-serviceviamelmastoon.audit.ai_invocation.v1(emitted by the orchestrator) andmelmastoon.audit.ai_decision.v1(emitted by pricing-service on accept).
Auditors can replay any historical guest quote and see exactly which model produced the rule, which signals were fed in (digests can be matched against orchestrator-side stored prompts during a 90-day retention window), and which human approved it.
7. Safety, compliance, and refusal policy
The model must refuse (return a structured rejection that the orchestrator translates to MELMASTOON.AI.REFUSED) when:
- Constraint payload violates Sharia rules (
shariaCompliant=trueand prompt would suggest interest-bearing logic). - Signals indicate insufficient data (occupancy unknown for > 50% of the lookback window).
- The orchestrator detects that the input includes prohibited content (PII tokens, profanity, prompt-injection markers).
Refusals are persisted as dynamic_suggestions.status='rejected' with rejected_by={type:'system',id:'ai-orchestrator-service'} and a redacted reason. They never block authoring of a manual rule.
8. Cost & rate-limit guardrails
| Guardrail | Limit | Enforcement |
|---|---|---|
| Per-property generation rate | 10 / minute | API Gateway rate-limit |
| Per-tenant generation rate | 60 / minute | API Gateway rate-limit |
| Per-tenant daily cost cap | $200/day | ai-orchestrator-service budget gate; over-budget → MELMASTOON.AI.BUDGET_EXCEEDED |
| Auto-trigger nightly batch | 1 per (property × room_type × date) | scheduler dedupe key |
| Suggestion expiry | 24 h | DB expires_at |
9. Future capabilities (deferred)
- Promo-code suggestion (
pricing.promo_suggestion): suggest codes/caps to maximise revenue with elasticity-aware constraints. Not implemented; see SERVICE_RISK_REGISTER for the rationale (data sparsity). - Length-of-stay incentive optimiser: same shape as dynamic pricing but advises LOS discount curves. Tracked in roadmap.
Both will land as additional capability ids on ai-orchestrator-service and additional use cases here, mirroring the patterns above.