WebOS completion
This commit is contained in:
497
backend/oracle/oracle_template_seed_db.json
Normal file
497
backend/oracle/oracle_template_seed_db.json
Normal file
@@ -0,0 +1,497 @@
|
||||
{
|
||||
"_meta": {
|
||||
"version": "1.0.0",
|
||||
"created": "2026-04-18",
|
||||
"description": "Oracle Template Seed Database — canonical chapter/subchapter taxonomy and seed JSON examples for the Project Velocity Oracle platform",
|
||||
"total_chapters": 6,
|
||||
"total_subchapters": 24,
|
||||
"total_seed_examples": 36
|
||||
},
|
||||
"chapters": [
|
||||
{
|
||||
"chapter_id": "ch-001",
|
||||
"name": "Market Intelligence",
|
||||
"description": "Components for real estate market analysis, pricing trends, demand signals, and competitive landscape.",
|
||||
"sort_order": 1,
|
||||
"subchapters": [
|
||||
{
|
||||
"subchapter_id": "sub-001-01",
|
||||
"name": "Pricing Trends",
|
||||
"description": "Price per sqft trends, AED/m² benchmarks, quarterly movement charts.",
|
||||
"sort_order": 1
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-001-02",
|
||||
"name": "Demand Signals",
|
||||
"description": "Search volume, inquiry rate, site visit frequency, and absorption rate components.",
|
||||
"sort_order": 2
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-001-03",
|
||||
"name": "Competitive Landscape",
|
||||
"description": "Developer comparison, project pipeline mapping, competitive unit mix analysis.",
|
||||
"sort_order": 3
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-001-04",
|
||||
"name": "Location Index",
|
||||
"description": "District-level scores, proximity analysis, infrastructure readiness.",
|
||||
"sort_order": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-002",
|
||||
"name": "Lead Intelligence",
|
||||
"description": "Components for lead profiling, scoring, pipeline health, and behaviour tracking.",
|
||||
"sort_order": 2,
|
||||
"subchapters": [
|
||||
{
|
||||
"subchapter_id": "sub-002-01",
|
||||
"name": "Lead Profile",
|
||||
"description": "Buyer persona cards, nationality, budget bracket, preferred property type.",
|
||||
"sort_order": 1
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-002-02",
|
||||
"name": "QD Score",
|
||||
"description": "Qualification-Desire score breakdown, historical trend, per-dimension scores.",
|
||||
"sort_order": 2
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-002-03",
|
||||
"name": "Pipeline Health",
|
||||
"description": "Pipeline stage distribution, velocity, stall alerts, probability weighting.",
|
||||
"sort_order": 3
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-002-04",
|
||||
"name": "Engagement History",
|
||||
"description": "Touchpoint timeline, dwell time heat maps, content interaction logs.",
|
||||
"sort_order": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-003",
|
||||
"name": "Communication Intelligence",
|
||||
"description": "Components surfacing insights from calls, messages, transcripts, and follow-up commitments.",
|
||||
"sort_order": 3,
|
||||
"subchapters": [
|
||||
{
|
||||
"subchapter_id": "sub-003-01",
|
||||
"name": "Call Summary",
|
||||
"description": "Transcript summary, speaker diarization, key-phrase extraction.",
|
||||
"sort_order": 1
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-003-02",
|
||||
"name": "Promise Tracker",
|
||||
"description": "Promises made during calls, follow-up dates, commitment confidence.",
|
||||
"sort_order": 2
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-003-03",
|
||||
"name": "WhatsApp Thread",
|
||||
"description": "Business WhatsApp message thread summaries, sentiment per message.",
|
||||
"sort_order": 3
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-003-04",
|
||||
"name": "Reminder Surface",
|
||||
"description": "Due follow-ups, overdue reminders, NemoClaw-suggested next actions.",
|
||||
"sort_order": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-004",
|
||||
"name": "Inventory Analytics",
|
||||
"description": "Components for property inventory insight, availability, and absorption.",
|
||||
"sort_order": 4,
|
||||
"subchapters": [
|
||||
{
|
||||
"subchapter_id": "sub-004-01",
|
||||
"name": "Property Card",
|
||||
"description": "Single-property summary card with unit details, pricing, media reference.",
|
||||
"sort_order": 1
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-004-02",
|
||||
"name": "Availability Matrix",
|
||||
"description": "Bed-type × availability grid with unit count and price band.",
|
||||
"sort_order": 2
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-004-03",
|
||||
"name": "Absorption Rate",
|
||||
"description": "Sales velocity per project and developer over rolling windows.",
|
||||
"sort_order": 3
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-004-04",
|
||||
"name": "Inventory Comparison",
|
||||
"description": "Side-by-side comparison of two or more properties on key metrics.",
|
||||
"sort_order": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-005",
|
||||
"name": "Operational Metrics",
|
||||
"description": "System-level, team-level, and showroom-level operational performance components.",
|
||||
"sort_order": 5,
|
||||
"subchapters": [
|
||||
{
|
||||
"subchapter_id": "sub-005-01",
|
||||
"name": "Showroom Traffic",
|
||||
"description": "Visitor count, zone dwell time, peak hour distribution.",
|
||||
"sort_order": 1
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-005-02",
|
||||
"name": "Team Performance",
|
||||
"description": "Agent-level QD scores, conversion rates, call volume, follow-up compliance.",
|
||||
"sort_order": 2
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-005-03",
|
||||
"name": "Campaign Metrics",
|
||||
"description": "Catalyst campaign reach, engagement rate, cost-per-lead, ROAS.",
|
||||
"sort_order": 3
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-005-04",
|
||||
"name": "System Health",
|
||||
"description": "Backend queue depth, GPU utilization, transcription job latency.",
|
||||
"sort_order": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-006",
|
||||
"name": "Calendar and Follow-Up",
|
||||
"description": "Components for scheduling, action planning, and NemoClaw-derived follow-up surfaces.",
|
||||
"sort_order": 6,
|
||||
"subchapters": [
|
||||
{
|
||||
"subchapter_id": "sub-006-01",
|
||||
"name": "Calendar View",
|
||||
"description": "Personal calendar view with communication-derived events and reminders.",
|
||||
"sort_order": 1
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-006-02",
|
||||
"name": "Action Queue",
|
||||
"description": "Prioritized action list for an agent, ordered by urgency and lead value.",
|
||||
"sort_order": 2
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-006-03",
|
||||
"name": "Follow-Up Plan",
|
||||
"description": "Structured follow-up plan derived from call outcomes and NemoClaw insights.",
|
||||
"sort_order": 3
|
||||
},
|
||||
{
|
||||
"subchapter_id": "sub-006-04",
|
||||
"name": "Reminder Cards",
|
||||
"description": "Surface-agnostic reminder card applicable to tablet and phone edge.",
|
||||
"sort_order": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"seed_examples": [
|
||||
{
|
||||
"example_id": "ex-001",
|
||||
"chapter_id": "ch-001",
|
||||
"subchapter_id": "sub-001-01",
|
||||
"title": "Dubai Marina — Price Per Sqft Trend (12-Month)",
|
||||
"quality_notes": "Canonical example. Use for pricing trend chart templates.",
|
||||
"is_canonical": true,
|
||||
"template_name": "Pricing Trend Chart",
|
||||
"component_type": "line_chart",
|
||||
"accepted_shapes": ["time_series"],
|
||||
"example_json": {
|
||||
"componentType": "line_chart",
|
||||
"title": "Dubai Marina — AED/sqft Trend",
|
||||
"subtitle": "12-Month Rolling Average",
|
||||
"dataSource": {
|
||||
"type": "inventory_aggregate",
|
||||
"district": "Dubai Marina",
|
||||
"metric": "avg_price_per_sqft",
|
||||
"window": "12M"
|
||||
},
|
||||
"visualization": {
|
||||
"xAxis": "month",
|
||||
"yAxis": "aed_per_sqft",
|
||||
"format": "currency_aed",
|
||||
"annotations": [
|
||||
{ "date": "2025-10", "label": "Off-plan surge", "type": "event" }
|
||||
],
|
||||
"trend_line": true,
|
||||
"confidence_band": false
|
||||
},
|
||||
"style": {
|
||||
"accentColor": "#2563EB",
|
||||
"gridLines": "subtle"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"example_id": "ex-002",
|
||||
"chapter_id": "ch-001",
|
||||
"subchapter_id": "sub-001-02",
|
||||
"title": "Inquiry Velocity — Downtown Dubai (30-Day)",
|
||||
"quality_notes": "Use for demand signal bar charts.",
|
||||
"is_canonical": true,
|
||||
"template_name": "Demand Signal Bar",
|
||||
"component_type": "bar_chart",
|
||||
"accepted_shapes": ["categorical_count"],
|
||||
"example_json": {
|
||||
"componentType": "bar_chart",
|
||||
"title": "Inquiry Volume — Downtown Dubai",
|
||||
"subtitle": "Last 30 Days by Week",
|
||||
"dataSource": {
|
||||
"type": "crm_aggregate",
|
||||
"district": "Downtown Dubai",
|
||||
"metric": "inquiry_count",
|
||||
"window": "30D",
|
||||
"groupBy": "week"
|
||||
},
|
||||
"visualization": {
|
||||
"xAxis": "week",
|
||||
"yAxis": "inquiry_count",
|
||||
"format": "integer",
|
||||
"comparison": { "enabled": true, "label": "Prior 30D", "style": "ghost_bar" }
|
||||
},
|
||||
"style": {
|
||||
"accentColor": "#10B981",
|
||||
"barRadius": 4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"example_id": "ex-003",
|
||||
"chapter_id": "ch-002",
|
||||
"subchapter_id": "sub-002-02",
|
||||
"title": "Lead QD Score Card — Mohammed Al-Rashid",
|
||||
"quality_notes": "Canonical single-lead QD score breakdown card.",
|
||||
"is_canonical": true,
|
||||
"template_name": "QD Score Card",
|
||||
"component_type": "metric_card_group",
|
||||
"accepted_shapes": ["qd_score_breakdown"],
|
||||
"example_json": {
|
||||
"componentType": "metric_card_group",
|
||||
"title": "QD Score",
|
||||
"subtitle": "Qualification × Desire",
|
||||
"dataSource": {
|
||||
"type": "sentinel_qd",
|
||||
"leadId": "{{lead_id}}"
|
||||
},
|
||||
"visualization": {
|
||||
"layout": "2x2_grid",
|
||||
"cards": [
|
||||
{ "dimension": "overall", "label": "Overall QD", "format": "percentage" },
|
||||
{ "dimension": "qualification", "label": "Qualification", "format": "percentage" },
|
||||
{ "dimension": "desire", "label": "Desire", "format": "percentage" },
|
||||
{ "dimension": "velocity", "label": "Engagement Velocity", "format": "trend_arrow" }
|
||||
],
|
||||
"threshold_colors": {
|
||||
"high": "#10B981",
|
||||
"medium": "#F59E0B",
|
||||
"low": "#EF4444"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"example_id": "ex-004",
|
||||
"chapter_id": "ch-003",
|
||||
"subchapter_id": "sub-003-01",
|
||||
"title": "Call Summary Card — Diarized Transcript with Key Phrases",
|
||||
"quality_notes": "Canonical call summary. Use for communication intelligence panels.",
|
||||
"is_canonical": true,
|
||||
"template_name": "Call Summary Card",
|
||||
"component_type": "communication_summary",
|
||||
"accepted_shapes": ["transcript_summary"],
|
||||
"example_json": {
|
||||
"componentType": "communication_summary",
|
||||
"title": "Call Summary",
|
||||
"dataSource": {
|
||||
"type": "edge_communication_event",
|
||||
"eventId": "{{event_id}}",
|
||||
"channel": "pstn"
|
||||
},
|
||||
"visualization": {
|
||||
"layout": "timeline_with_phrases",
|
||||
"show_speaker_labels": true,
|
||||
"show_duration": true,
|
||||
"show_sentiment": true,
|
||||
"key_phrase_highlight": true,
|
||||
"sections": ["summary", "promises", "key_phrases", "next_action"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"example_id": "ex-005",
|
||||
"chapter_id": "ch-003",
|
||||
"subchapter_id": "sub-003-02",
|
||||
"title": "Promise Tracker — Lead Follow-Up Commitments",
|
||||
"quality_notes": "Canonical promise tracker. Use for follow-up reminder surfaces.",
|
||||
"is_canonical": true,
|
||||
"template_name": "Promise Tracker Table",
|
||||
"component_type": "data_table",
|
||||
"accepted_shapes": ["communication_facts"],
|
||||
"example_json": {
|
||||
"componentType": "data_table",
|
||||
"title": "Promises and Commitments",
|
||||
"dataSource": {
|
||||
"type": "edge_memory_facts",
|
||||
"leadId": "{{lead_id}}",
|
||||
"factTypes": ["promise", "follow_up_date", "decision_maker_note"]
|
||||
},
|
||||
"visualization": {
|
||||
"columns": [
|
||||
{ "key": "fact_text", "label": "Commitment", "width": "flex" },
|
||||
{ "key": "effective_date", "label": "Due", "format": "date_relative" },
|
||||
{ "key": "confidence", "label": "Confidence", "format": "percentage" },
|
||||
{ "key": "extracted_from", "label": "Source", "format": "badge" }
|
||||
],
|
||||
"row_actions": ["mark_done", "create_calendar_event"],
|
||||
"sort": { "column": "effective_date", "direction": "asc" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"example_id": "ex-006",
|
||||
"chapter_id": "ch-004",
|
||||
"subchapter_id": "sub-004-01",
|
||||
"title": "Property Card — Sobha One Tower A",
|
||||
"quality_notes": "Canonical property card. Use for inventory summaries.",
|
||||
"is_canonical": true,
|
||||
"template_name": "Property Summary Card",
|
||||
"component_type": "property_card",
|
||||
"accepted_shapes": ["inventory_property"],
|
||||
"example_json": {
|
||||
"componentType": "property_card",
|
||||
"title": "Property Summary",
|
||||
"dataSource": {
|
||||
"type": "inventory_property",
|
||||
"propertyId": "{{property_id}}"
|
||||
},
|
||||
"visualization": {
|
||||
"layout": "hero_with_stats",
|
||||
"sections": [
|
||||
"project_name",
|
||||
"developer_name",
|
||||
"location_map_pin",
|
||||
"price_bands",
|
||||
"unit_mix_summary",
|
||||
"amenity_chips",
|
||||
"media_carousel"
|
||||
],
|
||||
"cta": { "label": "Schedule Viewing", "action": "create_calendar_event" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"example_id": "ex-007",
|
||||
"chapter_id": "ch-005",
|
||||
"subchapter_id": "sub-005-01",
|
||||
"title": "Showroom Traffic Heatmap",
|
||||
"quality_notes": "Canonical traffic component. Use for operational dashboards.",
|
||||
"is_canonical": true,
|
||||
"template_name": "Showroom Traffic Heatmap",
|
||||
"component_type": "heatmap",
|
||||
"accepted_shapes": ["zone_time_matrix"],
|
||||
"example_json": {
|
||||
"componentType": "heatmap",
|
||||
"title": "Showroom Zone Traffic",
|
||||
"subtitle": "Today — Live",
|
||||
"dataSource": {
|
||||
"type": "sentinel_live",
|
||||
"metric": "visitor_dwell_time",
|
||||
"groupBy": ["zone", "hour"]
|
||||
},
|
||||
"visualization": {
|
||||
"xAxis": "hour_of_day",
|
||||
"yAxis": "zone_name",
|
||||
"value": "avg_dwell_minutes",
|
||||
"colorScale": { "low": "#EFF6FF", "high": "#1D4ED8" },
|
||||
"annotations": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"example_id": "ex-008",
|
||||
"chapter_id": "ch-006",
|
||||
"subchapter_id": "sub-006-04",
|
||||
"title": "Phone Edge Reminder Card — Follow-Up Due",
|
||||
"quality_notes": "Designed for narrow phone edge surfaces. Minimal data footprint.",
|
||||
"is_canonical": true,
|
||||
"template_name": "Reminder Card",
|
||||
"component_type": "compact_alert_card",
|
||||
"accepted_shapes": ["insight_recommendation"],
|
||||
"example_json": {
|
||||
"componentType": "compact_alert_card",
|
||||
"title": "Follow-Up Reminder",
|
||||
"dataSource": {
|
||||
"type": "insight_recommendations",
|
||||
"leadId": "{{lead_id}}",
|
||||
"status": "pending",
|
||||
"limit": 1
|
||||
},
|
||||
"visualization": {
|
||||
"layout": "single_card_narrow",
|
||||
"fields": ["summary", "suggested_action", "target_system"],
|
||||
"actions": ["accept", "dismiss", "snooze_1h"],
|
||||
"urgency_indicator": true,
|
||||
"surface_target": ["iphone_edge", "android_phone_edge"]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"kimi_synthetic_plan": {
|
||||
"description": "Downstream Kimi synthetic data expansion plan consuming this seed DB",
|
||||
"expansion_targets": [
|
||||
{
|
||||
"chapter_id": "ch-001",
|
||||
"subchapter_id": "sub-001-01",
|
||||
"seed_example_ids": ["ex-001"],
|
||||
"requested_count": 50,
|
||||
"model": "kimi",
|
||||
"diversity_axes": ["district", "property_type", "time_window"]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-002",
|
||||
"subchapter_id": "sub-002-02",
|
||||
"seed_example_ids": ["ex-003"],
|
||||
"requested_count": 100,
|
||||
"model": "kimi",
|
||||
"diversity_axes": ["lead_nationality", "budget_bracket", "pipeline_stage"]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-003",
|
||||
"subchapter_id": "sub-003-01",
|
||||
"seed_example_ids": ["ex-004"],
|
||||
"requested_count": 200,
|
||||
"model": "kimi",
|
||||
"diversity_axes": ["call_outcome", "property_type", "language"]
|
||||
},
|
||||
{
|
||||
"chapter_id": "ch-004",
|
||||
"subchapter_id": "sub-004-01",
|
||||
"seed_example_ids": ["ex-006"],
|
||||
"requested_count": 150,
|
||||
"model": "kimi",
|
||||
"diversity_axes": ["developer_name", "district", "bedrooms"]
|
||||
}
|
||||
],
|
||||
"quality_gate": {
|
||||
"min_acceptance_confidence": 0.8,
|
||||
"human_review_required_for_canonical": true,
|
||||
"auto_accept_below_count": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
350
backend/oracle/schema_extension_v2.sql
Normal file
350
backend/oracle/schema_extension_v2.sql
Normal file
@@ -0,0 +1,350 @@
|
||||
-- ────────────────────────────────────────────────────────────────────────────
|
||||
-- Oracle Schema Extension v2 — Multi-Surface Platform and Oracle Expansion
|
||||
-- Date: 2026-04-18
|
||||
-- Author: Velocity Platform Team
|
||||
-- Depends on: schema_oracle.sql (must be applied first)
|
||||
-- PostgreSQL 14+ required · UUID via pgcrypto already enabled
|
||||
-- ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
-- ─── 1. Oracle Template Taxonomy ─────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS oracle_template_chapters (
|
||||
chapter_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS oracle_template_subchapters (
|
||||
subchapter_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
chapter_id UUID NOT NULL REFERENCES oracle_template_chapters(chapter_id) ON DELETE CASCADE,
|
||||
tenant_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS oracle_template_seed_examples (
|
||||
example_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
template_id UUID NOT NULL REFERENCES oracle_component_templates(template_id) ON DELETE CASCADE,
|
||||
chapter_id UUID REFERENCES oracle_template_chapters(chapter_id),
|
||||
subchapter_id UUID REFERENCES oracle_template_subchapters(subchapter_id),
|
||||
title TEXT NOT NULL,
|
||||
example_json JSONB NOT NULL,
|
||||
quality_notes TEXT,
|
||||
is_canonical BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Extend oracle_component_templates with chapter/subchapter linkage
|
||||
-- (additive columns — does not alter existing rows)
|
||||
ALTER TABLE oracle_component_templates
|
||||
ADD COLUMN IF NOT EXISTS chapter_id UUID REFERENCES oracle_template_chapters(chapter_id),
|
||||
ADD COLUMN IF NOT EXISTS subchapter_id UUID REFERENCES oracle_template_subchapters(subchapter_id),
|
||||
ADD COLUMN IF NOT EXISTS json_template JSONB,
|
||||
ADD COLUMN IF NOT EXISTS description TEXT;
|
||||
|
||||
-- ─── 2. Kimi Synthetic Data Jobs ─────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS oracle_synthetic_generation_jobs (
|
||||
job_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
template_id UUID NOT NULL REFERENCES oracle_component_templates(template_id),
|
||||
chapter_id UUID REFERENCES oracle_template_chapters(chapter_id),
|
||||
subchapter_id UUID REFERENCES oracle_template_subchapters(subchapter_id),
|
||||
model TEXT NOT NULL DEFAULT 'kimi',
|
||||
status TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending','running','completed','failed','cancelled')),
|
||||
requested_count INTEGER NOT NULL DEFAULT 10,
|
||||
accepted_count INTEGER NOT NULL DEFAULT 0,
|
||||
error_message TEXT,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_by TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── 3. Inventory Pipeline ───────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS inventory_import_batches (
|
||||
batch_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
source_type TEXT NOT NULL CHECK (source_type IN ('csv','json','api_push','manual')),
|
||||
submitted_by TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending','validating','processing','completed','failed','partial')),
|
||||
total_rows INTEGER NOT NULL DEFAULT 0,
|
||||
accepted_rows INTEGER NOT NULL DEFAULT 0,
|
||||
rejected_rows INTEGER NOT NULL DEFAULT 0,
|
||||
error_summary JSONB NOT NULL DEFAULT '[]'::JSONB,
|
||||
source_file_ref TEXT,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS inventory_properties (
|
||||
property_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
batch_id UUID REFERENCES inventory_import_batches(batch_id),
|
||||
source_id TEXT, -- external source identifier
|
||||
project_name TEXT NOT NULL,
|
||||
developer_name TEXT NOT NULL,
|
||||
location JSONB NOT NULL DEFAULT '{}'::JSONB, -- {city, district, lat, lng}
|
||||
property_type TEXT NOT NULL, -- apartment, villa, penthouse, plot, etc.
|
||||
price_bands JSONB NOT NULL DEFAULT '[]'::JSONB, -- [{minAED, maxAED, unitType}]
|
||||
unit_mix JSONB NOT NULL DEFAULT '[]'::JSONB, -- [{bedrooms, count, sizeSqft}]
|
||||
amenities TEXT[] NOT NULL DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'active'
|
||||
CHECK (status IN ('active','archived','draft','under_review')),
|
||||
validation_state JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
ingested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS inventory_media_assets (
|
||||
media_asset_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
property_id UUID NOT NULL REFERENCES inventory_properties(property_id) ON DELETE CASCADE,
|
||||
tenant_id TEXT NOT NULL,
|
||||
media_type TEXT NOT NULL CHECK (media_type IN ('image','video','floorplan','brochure','360','vr')),
|
||||
url TEXT NOT NULL,
|
||||
thumbnail_url TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
uploaded_by TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── 4. Edge Communication Events ────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS edge_communication_events (
|
||||
event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
lead_id TEXT NOT NULL,
|
||||
channel TEXT NOT NULL
|
||||
CHECK (channel IN ('pstn','whatsapp_message','whatsapp_voice',
|
||||
'whatsapp_video','email','facebook_message',
|
||||
'instagram_message','in_app_voip','manual_note')),
|
||||
direction TEXT NOT NULL CHECK (direction IN ('inbound','outbound')),
|
||||
provider TEXT, -- twilio, vonage, meta, etc.
|
||||
capture_mode TEXT NOT NULL
|
||||
CHECK (capture_mode IN ('direct_api','provider_routed','operator_import','operator_note')),
|
||||
consent_state TEXT NOT NULL DEFAULT 'unknown'
|
||||
CHECK (consent_state IN ('unknown','granted','denied','not_required')),
|
||||
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
duration_seconds INTEGER,
|
||||
summary TEXT,
|
||||
raw_reference TEXT, -- provider message/call ID
|
||||
recording_ref TEXT, -- storage path or URL
|
||||
provider_metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS edge_communication_memory_facts (
|
||||
fact_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
lead_id TEXT NOT NULL,
|
||||
event_id UUID REFERENCES edge_communication_events(event_id),
|
||||
fact_type TEXT NOT NULL
|
||||
CHECK (fact_type IN ('promise','preference','follow_up_date',
|
||||
'objection','interest_signal','budget','timeline',
|
||||
'constraint','decision_maker_note','custom')),
|
||||
fact_text TEXT NOT NULL,
|
||||
effective_date DATE,
|
||||
confidence NUMERIC(4,3) NOT NULL DEFAULT 1.0 CHECK (confidence BETWEEN 0 AND 1),
|
||||
extracted_from TEXT NOT NULL
|
||||
CHECK (extracted_from IN ('transcript','message_thread','operator_note','import')),
|
||||
is_confirmed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
confirmed_by TEXT,
|
||||
confirmed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── 5. Transcription Jobs and Segments ──────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS edge_transcription_jobs (
|
||||
transcription_job_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
event_id UUID NOT NULL REFERENCES edge_communication_events(event_id) ON DELETE CASCADE,
|
||||
media_type TEXT NOT NULL CHECK (media_type IN ('audio','video')),
|
||||
status TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending','queued','processing','completed','failed')),
|
||||
transcript_ref TEXT, -- storage path to diarized JSON
|
||||
provider TEXT NOT NULL DEFAULT 'nemoclaw',
|
||||
consent_state TEXT NOT NULL DEFAULT 'unknown'
|
||||
CHECK (consent_state IN ('unknown','granted','denied')),
|
||||
speaker_count INTEGER,
|
||||
word_count INTEGER,
|
||||
language TEXT NOT NULL DEFAULT 'en',
|
||||
error_message TEXT,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS edge_transcript_segments (
|
||||
segment_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
transcription_job_id UUID NOT NULL REFERENCES edge_transcription_jobs(transcription_job_id) ON DELETE CASCADE,
|
||||
event_id UUID NOT NULL REFERENCES edge_communication_events(event_id),
|
||||
speaker_label TEXT NOT NULL, -- SPEAKER_00, SPEAKER_01, etc.
|
||||
start_ms INTEGER NOT NULL,
|
||||
end_ms INTEGER NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
confidence NUMERIC(4,3) NOT NULL DEFAULT 1.0,
|
||||
is_agent_turn BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── 6. User Calendar Events ─────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_calendar_events (
|
||||
calendar_event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
owner_user_id TEXT NOT NULL,
|
||||
lead_id TEXT,
|
||||
source_event_id UUID REFERENCES edge_communication_events(event_id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
start_at TIMESTAMPTZ NOT NULL,
|
||||
end_at TIMESTAMPTZ NOT NULL,
|
||||
all_day BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
status TEXT NOT NULL DEFAULT 'confirmed'
|
||||
CHECK (status IN ('tentative','confirmed','cancelled')),
|
||||
reminder_minutes INTEGER[] NOT NULL DEFAULT '{15}'::INTEGER[],
|
||||
created_by TEXT NOT NULL
|
||||
CHECK (created_by IN ('user','nemoclaw_suggested','operator_import')),
|
||||
is_nemoclaw_confirmed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
location TEXT,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── 7. Insight Recommendations ──────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS insight_recommendations (
|
||||
recommendation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
lead_id TEXT NOT NULL,
|
||||
source_event_id UUID REFERENCES edge_communication_events(event_id),
|
||||
recommendation_type TEXT NOT NULL
|
||||
CHECK (recommendation_type IN ('follow_up_call','send_message',
|
||||
'schedule_meeting','update_crm',
|
||||
'update_qd_score','send_property_info',
|
||||
'escalate','custom')),
|
||||
summary TEXT NOT NULL,
|
||||
suggested_action TEXT NOT NULL,
|
||||
target_system TEXT NOT NULL
|
||||
CHECK (target_system IN ('crm','calendar','qd_score','whatsapp','email','operator')),
|
||||
status TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending','accepted','dismissed','acted_upon')),
|
||||
confidence NUMERIC(4,3) NOT NULL DEFAULT 0.8,
|
||||
acted_by TEXT,
|
||||
acted_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── 8. Admin Action Events ───────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_action_events (
|
||||
action_event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
action_id TEXT NOT NULL UNIQUE, -- idempotency key from client
|
||||
action_type TEXT NOT NULL
|
||||
CHECK (action_type IN (
|
||||
'user_create','user_deactivate','user_role_change',
|
||||
'tenant_config_update','inventory_batch_approve',
|
||||
'inventory_batch_reject','template_publish','template_archive',
|
||||
'synthetic_job_trigger','synthetic_job_cancel',
|
||||
'system_health_check','queue_drain','debug_event_export',
|
||||
'install_register','install_deregister'
|
||||
)),
|
||||
target_type TEXT NOT NULL,
|
||||
target_id TEXT NOT NULL,
|
||||
requested_by TEXT NOT NULL,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
status TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending','processing','completed','failed','rejected')),
|
||||
result_message TEXT,
|
||||
result_artifacts JSONB NOT NULL DEFAULT '[]'::JSONB,
|
||||
executed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ─── 9. Surface Sessions (cross-surface telemetry) ───────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS surface_sessions (
|
||||
session_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
surface_type TEXT NOT NULL
|
||||
CHECK (surface_type IN ('webos','ipad','android_tablet',
|
||||
'iphone_edge','android_phone_edge')),
|
||||
app_version TEXT NOT NULL,
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ended_at TIMESTAMPTZ,
|
||||
last_active_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
screen_sequence TEXT[] NOT NULL DEFAULT '{}',
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB
|
||||
);
|
||||
|
||||
-- ─── Indexes ──────────────────────────────────────────────────────────────────
|
||||
|
||||
-- Template taxonomy
|
||||
CREATE INDEX IF NOT EXISTS idx_tmpl_chapters_tenant ON oracle_template_chapters(tenant_id, is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_tmpl_subchapters_chapter ON oracle_template_subchapters(chapter_id, is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_tmpl_seed_examples_template ON oracle_template_seed_examples(template_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tmpl_seed_examples_chapter ON oracle_template_seed_examples(chapter_id);
|
||||
|
||||
-- Synthetic jobs
|
||||
CREATE INDEX IF NOT EXISTS idx_synthetic_jobs_tenant ON oracle_synthetic_generation_jobs(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_synthetic_jobs_template ON oracle_synthetic_generation_jobs(template_id);
|
||||
|
||||
-- Inventory
|
||||
CREATE INDEX IF NOT EXISTS idx_inv_batches_tenant ON inventory_import_batches(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_inv_props_tenant ON inventory_properties(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_inv_props_batch ON inventory_properties(batch_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_inv_media_property ON inventory_media_assets(property_id);
|
||||
|
||||
-- Edge communication
|
||||
CREATE INDEX IF NOT EXISTS idx_edge_events_lead ON edge_communication_events(tenant_id, lead_id, timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_edge_events_channel ON edge_communication_events(channel, timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_edge_memory_lead ON edge_communication_memory_facts(tenant_id, lead_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_edge_memory_event ON edge_communication_memory_facts(event_id);
|
||||
|
||||
-- Transcription
|
||||
CREATE INDEX IF NOT EXISTS idx_transcription_jobs_event ON edge_transcription_jobs(event_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_transcription_jobs_status ON edge_transcription_jobs(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_transcript_segments_job ON edge_transcript_segments(transcription_job_id, start_ms);
|
||||
|
||||
-- Calendar
|
||||
CREATE INDEX IF NOT EXISTS idx_calendar_events_owner ON user_calendar_events(tenant_id, owner_user_id, start_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_calendar_events_lead ON user_calendar_events(lead_id, start_at);
|
||||
|
||||
-- Insights
|
||||
CREATE INDEX IF NOT EXISTS idx_insights_lead ON insight_recommendations(tenant_id, lead_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_insights_status ON insight_recommendations(status, created_at DESC);
|
||||
|
||||
-- Admin
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_actions_tenant ON admin_action_events(tenant_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_actions_type ON admin_action_events(action_type, status);
|
||||
|
||||
-- Surface sessions
|
||||
CREATE INDEX IF NOT EXISTS idx_surface_sessions_user ON surface_sessions(tenant_id, user_id, started_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_surface_sessions_type ON surface_sessions(surface_type, started_at DESC);
|
||||
Reference in New Issue
Block a user