-- CreateEnum CREATE TYPE "ai_privacy_level" AS ENUM ('AI_OFF', 'TRANSCRIPTION_ONLY', 'TRANSCRIPTION_AND_ANALYSIS'); -- CreateEnum CREATE TYPE "ai_provider_scope" AS ENUM ('GLOBAL', 'TENANT'); -- CreateEnum CREATE TYPE "ai_capability" AS ENUM ('TRANSCRIPTION', 'DIARIZATION', 'TEXT_ANALYSIS', 'STRUCTURED_OUTPUT', 'EMBEDDINGS', 'REALTIME_AUDIO'); -- CreateEnum CREATE TYPE "ai_prompt_purpose" AS ENUM ('ANALYSIS', 'SCORECARD'); -- CreateEnum CREATE TYPE "ai_job_type" AS ENUM ('TRANSCRIPTION', 'ANALYSIS', 'REANALYSIS'); -- CreateEnum CREATE TYPE "ai_job_status" AS ENUM ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED', 'RETRYING', 'CANCELLED'); -- CreateEnum CREATE TYPE "transcription_status" AS ENUM ('PENDING', 'COMPLETED', 'FAILED'); -- CreateEnum CREATE TYPE "transcript_speaker" AS ENUM ('AGENT', 'CUSTOMER', 'UNKNOWN'); -- CreateEnum CREATE TYPE "ai_usage_type" AS ENUM ('AI_TRANSCRIPTION_SECONDS', 'AI_ANALYSIS_REQUEST', 'AI_INPUT_TOKENS', 'AI_OUTPUT_TOKENS'); -- AlterTable ALTER TABLE "campaigns" ADD COLUMN "analysis_prompt_template_id" UUID; -- AlterTable ALTER TABLE "queues" ADD COLUMN "ai_privacy_level" "ai_privacy_level"; -- AlterTable ALTER TABLE "tenants" ADD COLUMN "ai_privacy_level" "ai_privacy_level" NOT NULL DEFAULT 'AI_OFF'; -- CreateTable CREATE TABLE "ai_providers" ( "id" UUID NOT NULL, "scope" "ai_provider_scope" NOT NULL, "tenant_id" UUID, "provider_type" TEXT NOT NULL, "name" TEXT NOT NULL, "base_url" TEXT, "encrypted_api_key" TEXT NOT NULL, "organization" TEXT, "project" TEXT, "enabled" BOOLEAN NOT NULL DEFAULT true, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL, CONSTRAINT "ai_providers_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "ai_models" ( "id" UUID NOT NULL, "tenant_id" UUID, "provider_id" UUID NOT NULL, "external_model_id" TEXT NOT NULL, "display_name" TEXT NOT NULL, "capabilities" "ai_capability"[], "input_cost" DOUBLE PRECISION, "output_cost" DOUBLE PRECISION, "audio_cost" DOUBLE PRECISION, "enabled" BOOLEAN NOT NULL DEFAULT true, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL, CONSTRAINT "ai_models_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "ai_prompt_templates" ( "id" UUID NOT NULL, "tenant_id" UUID, "purpose" "ai_prompt_purpose" NOT NULL, "name" TEXT NOT NULL, "active_version_id" UUID, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL, CONSTRAINT "ai_prompt_templates_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "ai_prompt_versions" ( "id" UUID NOT NULL, "tenant_id" UUID, "template_id" UUID NOT NULL, "version" INTEGER NOT NULL, "content" TEXT NOT NULL, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "ai_prompt_versions_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "ai_jobs" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "call_id" UUID NOT NULL, "type" "ai_job_type" NOT NULL, "status" "ai_job_status" NOT NULL DEFAULT 'PENDING', "attempt_count" INTEGER NOT NULL DEFAULT 0, "max_attempts" INTEGER NOT NULL DEFAULT 5, "last_error" TEXT, "scheduled_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL, "completed_at" TIMESTAMP(3), CONSTRAINT "ai_jobs_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "call_transcriptions" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "call_id" UUID NOT NULL, "provider_id" UUID, "model" TEXT, "language" TEXT, "text" TEXT, "status" "transcription_status" NOT NULL DEFAULT 'PENDING', "duration_seconds" INTEGER, "provider_request_id" TEXT, "input_usage" INTEGER, "output_usage" INTEGER, "provider_cost" DOUBLE PRECISION, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "call_transcriptions_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "call_transcript_segments" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "transcription_id" UUID NOT NULL, "speaker" "transcript_speaker" NOT NULL DEFAULT 'UNKNOWN', "start_ms" INTEGER NOT NULL, "end_ms" INTEGER NOT NULL, "text" TEXT NOT NULL, "confidence" DOUBLE PRECISION, CONSTRAINT "call_transcript_segments_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "call_ai_analyses" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "call_id" UUID NOT NULL, "provider_id" UUID, "model" TEXT, "summary" TEXT, "customer_intent" TEXT, "outcome" TEXT, "sentiment" TEXT, "topics" TEXT[], "keywords" TEXT[], "objections" TEXT[], "questions" TEXT[], "action_items" TEXT[], "compliance_flags" TEXT[], "risk_flags" TEXT[], "quality_score" INTEGER, "agent_score" INTEGER, "customer_sentiment_score" DOUBLE PRECISION, "sales_opportunity" BOOLEAN, "next_best_action" TEXT, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "call_ai_analyses_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "quality_scorecards" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "name" TEXT NOT NULL, "enabled" BOOLEAN NOT NULL DEFAULT true, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL, CONSTRAINT "quality_scorecards_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "quality_scorecard_items" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "scorecard_id" UUID NOT NULL, "name" TEXT NOT NULL, "weight" DOUBLE PRECISION NOT NULL DEFAULT 1, "description" TEXT, "evaluation_prompt" TEXT, CONSTRAINT "quality_scorecard_items_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "quality_evaluations" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "call_id" UUID NOT NULL, "scorecard_id" UUID NOT NULL, "score" INTEGER NOT NULL, "criterion_scores" JSONB NOT NULL, "summary_justification" TEXT, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "quality_evaluations_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "ai_usage_records" ( "id" UUID NOT NULL, "tenant_id" UUID NOT NULL, "call_id" UUID, "type" "ai_usage_type" NOT NULL, "quantity" DOUBLE PRECISION NOT NULL, "provider_id" UUID, "model" TEXT, "occurred_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "ai_usage_records_pkey" PRIMARY KEY ("id") ); -- CreateIndex CREATE INDEX "ai_providers_tenant_id_idx" ON "ai_providers"("tenant_id"); -- CreateIndex CREATE INDEX "ai_models_tenant_id_idx" ON "ai_models"("tenant_id"); -- CreateIndex CREATE UNIQUE INDEX "ai_models_provider_id_external_model_id_key" ON "ai_models"("provider_id", "external_model_id"); -- CreateIndex CREATE UNIQUE INDEX "ai_prompt_templates_active_version_id_key" ON "ai_prompt_templates"("active_version_id"); -- CreateIndex CREATE INDEX "ai_prompt_templates_tenant_id_idx" ON "ai_prompt_templates"("tenant_id"); -- CreateIndex CREATE INDEX "ai_prompt_versions_tenant_id_idx" ON "ai_prompt_versions"("tenant_id"); -- CreateIndex CREATE UNIQUE INDEX "ai_prompt_versions_template_id_version_key" ON "ai_prompt_versions"("template_id", "version"); -- CreateIndex CREATE INDEX "ai_jobs_tenant_id_status_scheduled_at_idx" ON "ai_jobs"("tenant_id", "status", "scheduled_at"); -- CreateIndex CREATE INDEX "ai_jobs_tenant_id_call_id_idx" ON "ai_jobs"("tenant_id", "call_id"); -- CreateIndex CREATE INDEX "call_transcriptions_tenant_id_call_id_idx" ON "call_transcriptions"("tenant_id", "call_id"); -- CreateIndex CREATE INDEX "call_transcript_segments_tenant_id_idx" ON "call_transcript_segments"("tenant_id"); -- CreateIndex CREATE INDEX "call_transcript_segments_transcription_id_idx" ON "call_transcript_segments"("transcription_id"); -- CreateIndex CREATE INDEX "call_ai_analyses_tenant_id_call_id_idx" ON "call_ai_analyses"("tenant_id", "call_id"); -- CreateIndex CREATE INDEX "quality_scorecards_tenant_id_idx" ON "quality_scorecards"("tenant_id"); -- CreateIndex CREATE INDEX "quality_scorecard_items_tenant_id_idx" ON "quality_scorecard_items"("tenant_id"); -- CreateIndex CREATE INDEX "quality_scorecard_items_scorecard_id_idx" ON "quality_scorecard_items"("scorecard_id"); -- CreateIndex CREATE INDEX "quality_evaluations_tenant_id_call_id_idx" ON "quality_evaluations"("tenant_id", "call_id"); -- CreateIndex CREATE INDEX "ai_usage_records_tenant_id_occurred_at_idx" ON "ai_usage_records"("tenant_id", "occurred_at"); -- CreateIndex CREATE INDEX "ai_usage_records_tenant_id_type_idx" ON "ai_usage_records"("tenant_id", "type"); -- AddForeignKey ALTER TABLE "campaigns" ADD CONSTRAINT "campaigns_analysis_prompt_template_id_fkey" FOREIGN KEY ("analysis_prompt_template_id") REFERENCES "ai_prompt_templates"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_providers" ADD CONSTRAINT "ai_providers_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_models" ADD CONSTRAINT "ai_models_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_models" ADD CONSTRAINT "ai_models_provider_id_fkey" FOREIGN KEY ("provider_id") REFERENCES "ai_providers"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_prompt_templates" ADD CONSTRAINT "ai_prompt_templates_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_prompt_templates" ADD CONSTRAINT "ai_prompt_templates_active_version_id_fkey" FOREIGN KEY ("active_version_id") REFERENCES "ai_prompt_versions"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_prompt_versions" ADD CONSTRAINT "ai_prompt_versions_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_prompt_versions" ADD CONSTRAINT "ai_prompt_versions_template_id_fkey" FOREIGN KEY ("template_id") REFERENCES "ai_prompt_templates"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_jobs" ADD CONSTRAINT "ai_jobs_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_jobs" ADD CONSTRAINT "ai_jobs_call_id_fkey" FOREIGN KEY ("call_id") REFERENCES "calls"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_transcriptions" ADD CONSTRAINT "call_transcriptions_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_transcriptions" ADD CONSTRAINT "call_transcriptions_call_id_fkey" FOREIGN KEY ("call_id") REFERENCES "calls"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_transcriptions" ADD CONSTRAINT "call_transcriptions_provider_id_fkey" FOREIGN KEY ("provider_id") REFERENCES "ai_providers"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_transcript_segments" ADD CONSTRAINT "call_transcript_segments_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_transcript_segments" ADD CONSTRAINT "call_transcript_segments_transcription_id_fkey" FOREIGN KEY ("transcription_id") REFERENCES "call_transcriptions"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_ai_analyses" ADD CONSTRAINT "call_ai_analyses_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_ai_analyses" ADD CONSTRAINT "call_ai_analyses_call_id_fkey" FOREIGN KEY ("call_id") REFERENCES "calls"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "call_ai_analyses" ADD CONSTRAINT "call_ai_analyses_provider_id_fkey" FOREIGN KEY ("provider_id") REFERENCES "ai_providers"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "quality_scorecards" ADD CONSTRAINT "quality_scorecards_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "quality_scorecard_items" ADD CONSTRAINT "quality_scorecard_items_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "quality_scorecard_items" ADD CONSTRAINT "quality_scorecard_items_scorecard_id_fkey" FOREIGN KEY ("scorecard_id") REFERENCES "quality_scorecards"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "quality_evaluations" ADD CONSTRAINT "quality_evaluations_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "quality_evaluations" ADD CONSTRAINT "quality_evaluations_call_id_fkey" FOREIGN KEY ("call_id") REFERENCES "calls"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "quality_evaluations" ADD CONSTRAINT "quality_evaluations_scorecard_id_fkey" FOREIGN KEY ("scorecard_id") REFERENCES "quality_scorecards"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_usage_records" ADD CONSTRAINT "ai_usage_records_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_usage_records" ADD CONSTRAINT "ai_usage_records_call_id_fkey" FOREIGN KEY ("call_id") REFERENCES "calls"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "ai_usage_records" ADD CONSTRAINT "ai_usage_records_provider_id_fkey" FOREIGN KEY ("provider_id") REFERENCES "ai_providers"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- Tabelas de negocio tenant-scoped: RLS obrigatorio em todas (ver docs/TENANT_ISOLATION.md). -- RLS padrao (tenant_id NOT NULL, so' visivel/gravavel no proprio tenant). ALTER TABLE "ai_jobs" ENABLE ROW LEVEL SECURITY; ALTER TABLE "ai_jobs" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "ai_jobs" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); ALTER TABLE "call_transcriptions" ENABLE ROW LEVEL SECURITY; ALTER TABLE "call_transcriptions" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "call_transcriptions" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); ALTER TABLE "call_transcript_segments" ENABLE ROW LEVEL SECURITY; ALTER TABLE "call_transcript_segments" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "call_transcript_segments" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); ALTER TABLE "call_ai_analyses" ENABLE ROW LEVEL SECURITY; ALTER TABLE "call_ai_analyses" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "call_ai_analyses" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); ALTER TABLE "quality_scorecards" ENABLE ROW LEVEL SECURITY; ALTER TABLE "quality_scorecards" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "quality_scorecards" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); ALTER TABLE "quality_scorecard_items" ENABLE ROW LEVEL SECURITY; ALTER TABLE "quality_scorecard_items" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "quality_scorecard_items" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); ALTER TABLE "quality_evaluations" ENABLE ROW LEVEL SECURITY; ALTER TABLE "quality_evaluations" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "quality_evaluations" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); ALTER TABLE "ai_usage_records" ENABLE ROW LEVEL SECURITY; ALTER TABLE "ai_usage_records" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "ai_usage_records" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid); -- RLS hibrida (tenant_id NULLABLE): scope=GLOBAL (tenant_id null, cadastrado -- pelo platform admin) e' visivel de QUALQUER contexto de tenant (mesma -- tecnica ja usada pra tenant_memberships na descoberta de tenant durante -- login, agente.md secao 100 "Provider Global e BYOK"). Quem pode ESCREVER -- num registro GLOBAL e' decidido na camada de servico (so' platform -- admin), nao pela RLS -- RLS aqui so' garante isolamento entre tenants, -- nunca vaza BYOK de um tenant pra outro. ALTER TABLE "ai_providers" ENABLE ROW LEVEL SECURITY; ALTER TABLE "ai_providers" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "ai_providers" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid OR tenant_id IS NULL); ALTER TABLE "ai_models" ENABLE ROW LEVEL SECURITY; ALTER TABLE "ai_models" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "ai_models" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid OR tenant_id IS NULL); ALTER TABLE "ai_prompt_templates" ENABLE ROW LEVEL SECURITY; ALTER TABLE "ai_prompt_templates" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "ai_prompt_templates" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid OR tenant_id IS NULL); ALTER TABLE "ai_prompt_versions" ENABLE ROW LEVEL SECURITY; ALTER TABLE "ai_prompt_versions" FORCE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON "ai_prompt_versions" USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid OR tenant_id IS NULL);