Files

205 lines
12 KiB
PL/PgSQL
Raw Permalink Normal View History

2026-08-26 06:41:07 +08:00
BEGIN;
CREATE TABLE conversation_turn (
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,
conversation_id uuid NOT NULL,
turn_key varchar(255) NOT NULL,
sequence_no bigint NOT NULL CHECK (sequence_no > 0),
speaker varchar(24) NOT NULL CHECK (speaker IN ('subject', 'agent')),
start_at timestamptz NOT NULL,
end_at timestamptz NOT NULL,
final_text text NOT NULL CHECK (length(final_text) BETWEEN 1 AND 20000),
interrupted boolean NOT NULL DEFAULT false,
provider_ref varchar(255),
source_type varchar(24) NOT NULL CHECK (source_type IN ('provider', 'device')),
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT conversation_turn_time_order CHECK (end_at >= start_at),
CONSTRAINT conversation_turn_conversation_tenant_fk FOREIGN KEY (conversation_id, tenant_id)
REFERENCES conversation (id, tenant_id),
CONSTRAINT conversation_turn_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
REFERENCES subject_profile (id, tenant_id),
CONSTRAINT conversation_turn_id_tenant_key UNIQUE (id, tenant_id),
CONSTRAINT conversation_turn_business_key UNIQUE (conversation_id, turn_key),
CONSTRAINT conversation_turn_sequence_key UNIQUE (conversation_id, sequence_no)
);
CREATE INDEX conversation_turn_subject_time_idx ON conversation_turn (tenant_id, subject_id, start_at DESC);
CREATE INDEX conversation_turn_conversation_page_idx ON conversation_turn (conversation_id, sequence_no, id);
CREATE TABLE conversation_summary (
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,
conversation_id uuid NOT NULL,
summary_type varchar(32) NOT NULL CHECK (summary_type IN ('conversation_summary', 'open_topic')),
content text NOT NULL CHECK (length(content) BETWEEN 1 AND 20000),
source_turn_ids jsonb NOT NULL CHECK (jsonb_typeof(source_turn_ids)='array' AND jsonb_array_length(source_turn_ids)>0),
model_ref varchar(160) NOT NULL,
prompt_version varchar(80) NOT NULL,
generation_version bigint NOT NULL CHECK (generation_version > 0),
confidence numeric(5,4) CHECK (confidence BETWEEN 0 AND 1),
status varchar(24) NOT NULL DEFAULT 'READY' CHECK (status IN ('PROCESSING', 'READY', 'FAILED', 'SUPERSEDED')),
last_error varchar(500),
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT conversation_summary_conversation_tenant_fk FOREIGN KEY (conversation_id, tenant_id)
REFERENCES conversation (id, tenant_id),
CONSTRAINT conversation_summary_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
REFERENCES subject_profile (id, tenant_id),
CONSTRAINT conversation_summary_generation_key UNIQUE (conversation_id, summary_type, generation_version)
);
CREATE INDEX conversation_summary_conversation_idx ON conversation_summary (conversation_id, summary_type, generation_version DESC);
CREATE TABLE story_draft (
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,
conversation_id uuid NOT NULL,
title varchar(300) NOT NULL CHECK (length(title)>0),
status varchar(24) NOT NULL DEFAULT 'CANDIDATE'
CHECK (status IN ('CANDIDATE', 'IN_REVIEW', 'CONFIRMED', 'REJECTED', 'SUPERSEDED')),
confidence numeric(5,4) CHECK (confidence BETWEEN 0 AND 1),
sensitivity varchar(24) NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'sensitive', 'high')),
generation_version bigint NOT NULL CHECK (generation_version > 0),
current_revision_id uuid,
reviewed_by uuid,
reviewed_at timestamptz,
review_reason varchar(500),
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT story_draft_conversation_tenant_fk FOREIGN KEY (conversation_id, tenant_id)
REFERENCES conversation (id, tenant_id),
CONSTRAINT story_draft_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
REFERENCES subject_profile (id, tenant_id),
CONSTRAINT story_draft_reviewer_tenant_fk FOREIGN KEY (reviewed_by, tenant_id)
REFERENCES user_account (id, tenant_id),
CONSTRAINT story_draft_generation_key UNIQUE (conversation_id, generation_version)
);
CREATE INDEX story_draft_subject_status_idx ON story_draft (tenant_id, subject_id, status, created_at DESC);
CREATE TABLE story_revision (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
story_id uuid NOT NULL REFERENCES story_draft(id),
revision_no bigint NOT NULL CHECK (revision_no > 0),
content text NOT NULL CHECK (length(content) BETWEEN 1 AND 50000),
author_type varchar(24) NOT NULL CHECK (author_type IN ('model', 'user')),
author_id uuid,
edit_reason varchar(500),
base_revision_id uuid REFERENCES story_revision(id),
model_ref varchar(160),
prompt_version varchar(80),
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT story_revision_number_key UNIQUE (story_id, revision_no)
);
ALTER TABLE story_draft ADD CONSTRAINT story_draft_current_revision_fk
FOREIGN KEY (current_revision_id) REFERENCES story_revision(id);
CREATE TABLE story_source (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
story_revision_id uuid NOT NULL REFERENCES story_revision(id) ON DELETE CASCADE,
conversation_turn_id uuid NOT NULL REFERENCES conversation_turn(id),
source_start_offset integer CHECK (source_start_offset >= 0),
source_end_offset integer CHECK (source_end_offset >= source_start_offset),
source_role varchar(24) NOT NULL CHECK (source_role IN ('subject', 'agent')),
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT story_source_turn_key UNIQUE (story_revision_id, conversation_turn_id, source_start_offset, source_end_offset)
);
CREATE INDEX story_source_turn_idx ON story_source (conversation_turn_id);
CREATE TABLE fact_candidate (
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,
story_id uuid NOT NULL REFERENCES story_draft(id),
fact_type varchar(64) NOT NULL,
content text NOT NULL CHECK (length(content) BETWEEN 1 AND 4000),
statement_basis varchar(24) NOT NULL CHECK (statement_basis IN ('subject_statement', 'model_inference')),
time_range jsonb NOT NULL DEFAULT '{}'::jsonb,
confidence numeric(5,4) CHECK (confidence BETWEEN 0 AND 1),
sensitivity varchar(24) NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'sensitive', 'high')),
source_turn_ids jsonb NOT NULL CHECK (jsonb_typeof(source_turn_ids)='array' AND jsonb_array_length(source_turn_ids)>0),
status varchar(24) NOT NULL DEFAULT 'CANDIDATE'
CHECK (status IN ('CANDIDATE', 'CONFIRMED', 'REJECTED', 'SUPERSEDED')),
generation_version bigint NOT NULL CHECK (generation_version > 0),
reviewed_by uuid,
reviewed_at timestamptz,
review_reason varchar(500),
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT fact_candidate_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
REFERENCES subject_profile (id, tenant_id),
CONSTRAINT fact_candidate_reviewer_tenant_fk FOREIGN KEY (reviewed_by, tenant_id)
REFERENCES user_account (id, tenant_id)
);
CREATE INDEX fact_candidate_story_status_idx ON fact_candidate (story_id, status);
CREATE TABLE curated_memory (
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,
current_version_id uuid,
status varchar(24) NOT NULL DEFAULT 'ACTIVE'
CHECK (status IN ('ACTIVE', 'DISABLED', 'SUPERSEDED', 'PENDING_DELETE', 'DELETED')),
sensitivity varchar(24) NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'sensitive', 'high')),
valid_from timestamptz,
valid_to timestamptz,
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz,
CONSTRAINT curated_memory_valid_range CHECK (valid_to IS NULL OR valid_from IS NULL OR valid_to >= valid_from),
CONSTRAINT curated_memory_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
REFERENCES subject_profile (id, tenant_id)
);
CREATE INDEX curated_memory_subject_status_idx ON curated_memory (tenant_id, subject_id, status, updated_at DESC);
CREATE TABLE memory_version (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
memory_id uuid NOT NULL REFERENCES curated_memory(id),
version_no bigint NOT NULL CHECK (version_no > 0),
content text NOT NULL CHECK (length(content) BETWEEN 1 AND 50000),
source_refs jsonb NOT NULL CHECK (jsonb_typeof(source_refs)='array' AND jsonb_array_length(source_refs)>0),
confirmed_by uuid NOT NULL REFERENCES user_account(id),
confirmed_at timestamptz NOT NULL DEFAULT now(),
change_reason varchar(500),
content_digest char(64) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT memory_version_number_key UNIQUE (memory_id, version_no)
);
ALTER TABLE curated_memory ADD CONSTRAINT curated_memory_current_version_fk
FOREIGN KEY (current_version_id) REFERENCES memory_version(id);
CREATE TABLE memory_index_entry (
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,
memory_id uuid NOT NULL REFERENCES curated_memory(id),
memory_version_id uuid NOT NULL REFERENCES memory_version(id),
index_type varchar(24) NOT NULL CHECK (index_type IN ('pgvector', 'provider')),
embedding vector(1536),
provider_ref varchar(300),
status varchar(24) NOT NULL DEFAULT 'PENDING'
CHECK (status IN ('PENDING', 'ACTIVE', 'STALE', 'FAILED', 'DELETED')),
indexed_at timestamptz,
last_attempt_at timestamptz,
last_error varchar(500),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT memory_index_subject_tenant_fk FOREIGN KEY (subject_id, tenant_id)
REFERENCES subject_profile (id, tenant_id),
CONSTRAINT memory_index_version_key UNIQUE (memory_version_id, index_type)
);
CREATE INDEX memory_index_scope_idx ON memory_index_entry (tenant_id, subject_id, status, memory_id);
COMMIT;