215 lines
11 KiB
PL/PgSQL
215 lines
11 KiB
PL/PgSQL
BEGIN;
|
|
|
|
CREATE TABLE subject_profile (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
|
|
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
household_id uuid NOT NULL,
|
|
preferred_name varchar(120) NOT NULL,
|
|
status varchar(24) NOT NULL DEFAULT 'active'
|
|
CHECK (status IN ('active', 'inactive', 'deleted')),
|
|
profile_version bigint NOT NULL DEFAULT 1 CHECK (profile_version > 0),
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
deleted_at timestamptz,
|
|
CONSTRAINT subject_household_tenant_fk FOREIGN KEY (household_id, tenant_id)
|
|
REFERENCES household (id, tenant_id),
|
|
CONSTRAINT subject_id_tenant_key UNIQUE (id, tenant_id)
|
|
);
|
|
CREATE INDEX subject_tenant_household_status_idx ON subject_profile (tenant_id, household_id, status);
|
|
|
|
CREATE TABLE device (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
|
|
tenant_id uuid REFERENCES tenant(id),
|
|
serial_digest char(64) NOT NULL UNIQUE,
|
|
model varchar(120) NOT NULL,
|
|
hardware_revision varchar(80),
|
|
status varchar(24) NOT NULL DEFAULT 'registered'
|
|
CHECK (status IN ('registered', 'quarantined', 'revoked')),
|
|
credential_ref varchar(500) NOT NULL,
|
|
minimum_security_version varchar(80),
|
|
registered_at timestamptz NOT NULL DEFAULT now(),
|
|
revoked_at timestamptz,
|
|
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT device_id_tenant_key UNIQUE (id, tenant_id)
|
|
);
|
|
CREATE INDEX device_tenant_status_idx ON device (tenant_id, status);
|
|
|
|
CREATE TABLE device_binding (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
|
|
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
device_id uuid NOT NULL,
|
|
household_id uuid NOT NULL,
|
|
subject_id uuid NOT NULL,
|
|
bound_at timestamptz NOT NULL DEFAULT now(),
|
|
unbound_at timestamptz,
|
|
bound_by uuid,
|
|
unbound_by uuid,
|
|
reason varchar(255),
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT device_binding_device_tenant_fk FOREIGN KEY (device_id, tenant_id)
|
|
REFERENCES device (id, tenant_id),
|
|
CONSTRAINT device_binding_household_tenant_fk FOREIGN KEY (household_id, tenant_id)
|
|
REFERENCES household (id, tenant_id),
|
|
CONSTRAINT device_binding_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
|
|
REFERENCES subject_profile (id, tenant_id),
|
|
CONSTRAINT device_binding_bound_by_tenant_fk FOREIGN KEY (bound_by, tenant_id)
|
|
REFERENCES user_account (id, tenant_id),
|
|
CONSTRAINT device_binding_unbound_by_tenant_fk FOREIGN KEY (unbound_by, tenant_id)
|
|
REFERENCES user_account (id, tenant_id)
|
|
);
|
|
CREATE UNIQUE INDEX device_binding_current_device_key ON device_binding (device_id) WHERE unbound_at IS NULL;
|
|
CREATE INDEX device_binding_tenant_subject_idx ON device_binding (tenant_id, subject_id) WHERE unbound_at IS NULL;
|
|
|
|
CREATE TABLE device_twin (
|
|
device_id uuid PRIMARY KEY,
|
|
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
desired_revision bigint NOT NULL DEFAULT 1 CHECK (desired_revision > 0),
|
|
desired_config jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
reported_revision bigint,
|
|
reported_config jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
firmware_version varchar(80),
|
|
board_revision varchar(80),
|
|
rtc_sdk_version varchar(80),
|
|
capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
last_reported_at timestamptz,
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT device_twin_device_tenant_fk FOREIGN KEY (device_id, tenant_id)
|
|
REFERENCES device (id, tenant_id)
|
|
);
|
|
|
|
CREATE TABLE consent_snapshot (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
|
|
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
subject_id uuid NOT NULL,
|
|
snapshot_version bigint NOT NULL CHECK (snapshot_version > 0),
|
|
effective_consents jsonb NOT NULL,
|
|
source_consent_ids jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT consent_snapshot_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
|
|
REFERENCES subject_profile (id, tenant_id),
|
|
CONSTRAINT consent_snapshot_id_tenant_key UNIQUE (id, tenant_id)
|
|
);
|
|
|
|
CREATE TABLE context_snapshot (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
|
|
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
subject_id uuid NOT NULL,
|
|
profile_version bigint NOT NULL CHECK (profile_version > 0),
|
|
consent_snapshot_id uuid NOT NULL,
|
|
memory_versions jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
prompt_bundle_version varchar(80) NOT NULL,
|
|
engine_mode varchar(32) NOT NULL CHECK (engine_mode IN ('s2s_pure', 's2s_hybrid', 'cascade')),
|
|
content_digest char(64) NOT NULL,
|
|
sanitized_summary varchar(1000),
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT context_snapshot_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
|
|
REFERENCES subject_profile (id, tenant_id),
|
|
CONSTRAINT context_snapshot_consent_tenant_fk FOREIGN KEY (consent_snapshot_id, tenant_id)
|
|
REFERENCES consent_snapshot (id, tenant_id),
|
|
CONSTRAINT context_snapshot_id_tenant_key UNIQUE (id, tenant_id)
|
|
);
|
|
|
|
CREATE TABLE conversation (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
|
|
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
household_id uuid NOT NULL,
|
|
subject_id uuid NOT NULL,
|
|
device_id uuid NOT NULL,
|
|
consent_snapshot_id uuid NOT NULL,
|
|
context_snapshot_id uuid NOT NULL,
|
|
provider_task_ref varchar(200) NOT NULL UNIQUE,
|
|
engine_mode varchar(32) NOT NULL CHECK (engine_mode IN ('s2s_pure', 's2s_hybrid', 'cascade')),
|
|
retention_mode varchar(32) NOT NULL CHECK (retention_mode IN ('none', 'transcript_only', 'transcript_and_audio')),
|
|
status varchar(32) NOT NULL CHECK (status IN ('PREPARING', 'ROOM_ISSUED', 'STARTING_AGENT', 'ACTIVE', 'STOPPING', 'COMPLETED', 'FAILED', 'EXPIRED')),
|
|
allocation_expires_at timestamptz NOT NULL,
|
|
lease_expires_at timestamptz,
|
|
last_heartbeat_seq bigint NOT NULL DEFAULT 0 CHECK (last_heartbeat_seq >= 0),
|
|
started_at timestamptz,
|
|
ended_at timestamptz,
|
|
stop_reason varchar(64),
|
|
failure_code varchar(64),
|
|
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT conversation_household_tenant_fk FOREIGN KEY (household_id, tenant_id)
|
|
REFERENCES household (id, tenant_id),
|
|
CONSTRAINT conversation_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
|
|
REFERENCES subject_profile (id, tenant_id),
|
|
CONSTRAINT conversation_device_tenant_fk FOREIGN KEY (device_id, tenant_id)
|
|
REFERENCES device (id, tenant_id),
|
|
CONSTRAINT conversation_consent_tenant_fk FOREIGN KEY (consent_snapshot_id, tenant_id)
|
|
REFERENCES consent_snapshot (id, tenant_id),
|
|
CONSTRAINT conversation_context_tenant_fk FOREIGN KEY (context_snapshot_id, tenant_id)
|
|
REFERENCES context_snapshot (id, tenant_id),
|
|
CONSTRAINT conversation_id_tenant_key UNIQUE (id, tenant_id)
|
|
);
|
|
CREATE INDEX conversation_tenant_subject_time_idx ON conversation (tenant_id, subject_id, created_at DESC);
|
|
CREATE INDEX conversation_device_status_idx ON conversation (device_id, status);
|
|
CREATE UNIQUE INDEX conversation_one_active_per_device ON conversation (device_id)
|
|
WHERE status NOT IN ('COMPLETED', 'FAILED', 'EXPIRED');
|
|
|
|
CREATE TABLE conversation_state_history (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id uuid NOT NULL REFERENCES conversation(id),
|
|
from_status varchar(32),
|
|
to_status varchar(32) NOT NULL,
|
|
reason varchar(255),
|
|
source varchar(40) NOT NULL CHECK (source IN ('device', 'platform', 'provider', 'worker', 'operator')),
|
|
provider_event_id uuid,
|
|
request_id varchar(128),
|
|
occurred_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX conversation_history_time_idx ON conversation_state_history (conversation_id, occurred_at, id);
|
|
|
|
CREATE TABLE rtc_task (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id uuid NOT NULL UNIQUE REFERENCES conversation(id),
|
|
provider varchar(40) NOT NULL,
|
|
provider_task_ref varchar(200) NOT NULL UNIQUE,
|
|
room_id varchar(200) NOT NULL UNIQUE,
|
|
device_rtc_user_id varchar(200) NOT NULL,
|
|
agent_rtc_user_id varchar(200) NOT NULL,
|
|
status varchar(32) NOT NULL CHECK (status IN ('ALLOCATED', 'STARTING', 'ACTIVE', 'STOPPING', 'COMPLETED', 'FAILED', 'UNKNOWN')),
|
|
lease_expires_at timestamptz,
|
|
start_attempts integer NOT NULL DEFAULT 0 CHECK (start_attempts >= 0),
|
|
stop_attempts integer NOT NULL DEFAULT 0 CHECK (stop_attempts >= 0),
|
|
last_checked_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT rtc_distinct_users CHECK (device_rtc_user_id <> agent_rtc_user_id)
|
|
);
|
|
|
|
CREATE TABLE provider_event (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
conversation_id uuid NOT NULL,
|
|
provider varchar(40) NOT NULL,
|
|
provider_event_key varchar(255) NOT NULL,
|
|
provider_task_ref varchar(200) NOT NULL,
|
|
event_type varchar(100) NOT NULL,
|
|
occurred_at timestamptz NOT NULL,
|
|
received_at timestamptz NOT NULL DEFAULT now(),
|
|
payload_digest char(64) NOT NULL,
|
|
sanitized_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
processing_status varchar(24) NOT NULL DEFAULT 'pending'
|
|
CHECK (processing_status IN ('pending', 'processed', 'ignored', 'failed')),
|
|
processed_at timestamptz,
|
|
last_error varchar(500),
|
|
CONSTRAINT provider_event_conversation_tenant_fk FOREIGN KEY (conversation_id, tenant_id)
|
|
REFERENCES conversation (id, tenant_id),
|
|
CONSTRAINT provider_event_key UNIQUE (provider, provider_event_key)
|
|
);
|
|
CREATE INDEX provider_event_conversation_time_idx ON provider_event (conversation_id, received_at);
|
|
CREATE INDEX provider_event_pending_idx ON provider_event (processing_status, received_at)
|
|
WHERE processing_status IN ('pending', 'failed');
|
|
|
|
COMMIT;
|