Files

62 lines
4.1 KiB
PL/PgSQL
Raw Permalink Normal View History

-- Durable, coalescing persona synchronization. No personal facts in job payloads.
ALTER TABLE subject_voice_agent
ADD COLUMN persona_base_role text NOT NULL DEFAULT '',
ADD COLUMN persona_revision bigint NOT NULL DEFAULT 1 CHECK(persona_revision>0),
ADD COLUMN persona_synced_revision bigint NOT NULL DEFAULT 0,
ADD COLUMN persona_sync_state text NOT NULL DEFAULT 'PENDING' CHECK(persona_sync_state IN ('PENDING','SYNCED','DEFERRED','FAILED')),
ADD COLUMN persona_sync_attempts integer NOT NULL DEFAULT 0,
ADD COLUMN persona_next_retry_at timestamptz NOT NULL DEFAULT now(),
ADD COLUMN persona_error_code text NOT NULL DEFAULT '',
ADD COLUMN persona_synced_at timestamptz,
ADD COLUMN persona_digest text NOT NULL DEFAULT '',
ADD COLUMN persona_memory_allowed boolean NOT NULL DEFAULT false,
ADD COLUMN persona_voice_allowed boolean NOT NULL DEFAULT false,
ADD COLUMN persona_session_dirty boolean NOT NULL DEFAULT false;
-- Remove only the old compiler's trailing name sentence, retaining custom personas.
UPDATE subject_voice_agent SET persona_base_role=regexp_replace(COALESCE(config#>>'{S2SConfig,ProviderParams,dialog,system_role}',''),E'\n你正在陪伴的长者称呼是[^\n]*。$','');
CREATE FUNCTION queue_elder_persona_sync(sid uuid) RETURNS void LANGUAGE sql AS $$
UPDATE subject_voice_agent SET persona_revision=persona_revision+1,persona_sync_state='PENDING',persona_sync_attempts=0,persona_next_retry_at=now(),persona_error_code='' WHERE subject_id=sid
$$;
-- Use the same elder lock as chat preparation and the synchronizer. Take it before
-- the change so a provider request cannot race a newly committed profile version.
CREATE FUNCTION elder_persona_profile_changed() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
IF ROW(OLD.preferred_name,OLD.language,OLD.profile_data->'background',OLD.profile_data->'interests',
OLD.conversation_preferences->'conversation_goal',OLD.conversation_preferences->'pace',
OLD.conversation_preferences->'preferred_topics',OLD.conversation_preferences->'avoided_topics',OLD.status)
IS DISTINCT FROM
ROW(NEW.preferred_name,NEW.language,NEW.profile_data->'background',NEW.profile_data->'interests',
NEW.conversation_preferences->'conversation_goal',NEW.conversation_preferences->'pace',
NEW.conversation_preferences->'preferred_topics',NEW.conversation_preferences->'avoided_topics',NEW.status) THEN
PERFORM pg_advisory_xact_lock(hashtextextended(NEW.id::text,64));
PERFORM queue_elder_persona_sync(NEW.id);
END IF;
RETURN NEW;
END $$;
CREATE TRIGGER elder_persona_profile_changed BEFORE UPDATE ON subject_profile FOR EACH ROW EXECUTE FUNCTION elder_persona_profile_changed();
CREATE FUNCTION elder_persona_consent_changed() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
-- Do not take the elder lock here: privacy fencing has a separate lock order.
IF NEW.consent_type IN ('personalized_memory','voice_chat') THEN PERFORM queue_elder_persona_sync(NEW.subject_id); END IF;
RETURN NEW;
END $$;
CREATE TRIGGER elder_persona_consent_changed AFTER INSERT OR UPDATE ON consent_record FOR EACH ROW EXECUTE FUNCTION elder_persona_consent_changed();
CREATE FUNCTION elder_persona_config_changed() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
IF ROW(OLD.config,OLD.agent_config,OLD.persona_base_role) IS DISTINCT FROM ROW(NEW.config,NEW.agent_config,NEW.persona_base_role) THEN
IF OLD.config#>>'{S2SConfig,ProviderParams,dialog,system_role}' IS DISTINCT FROM NEW.config#>>'{S2SConfig,ProviderParams,dialog,system_role}' THEN
NEW.persona_base_role=regexp_replace(COALESCE(NEW.config#>>'{S2SConfig,ProviderParams,dialog,system_role}',''),E'\n你正在陪伴的长者称呼是[^\n]*。$','');
END IF;
NEW.persona_revision=OLD.persona_revision+1;
NEW.persona_sync_state='PENDING'; NEW.persona_sync_attempts=0; NEW.persona_next_retry_at=now(); NEW.persona_error_code='';
END IF;
RETURN NEW;
END $$;
CREATE TRIGGER elder_persona_config_changed BEFORE UPDATE ON subject_voice_agent FOR EACH ROW EXECUTE FUNCTION elder_persona_config_changed();
CREATE INDEX elder_persona_retry_idx ON subject_voice_agent(persona_next_retry_at) WHERE status='READY';