- TTS: xtts-v2 integration with voice cloning support
- Document: docling integration for PDF/DOCX/PPTX processing
- Memory Service: added /facts/upsert, /facts/{key}, /facts endpoints
- Added required dependencies (TTS, docling)
449 lines
18 KiB
PL/PgSQL
449 lines
18 KiB
PL/PgSQL
-- Migration 046: Memory Service Full Schema
|
||
-- Повна схема пам'яті для агентів (episodic, semantic, group identity)
|
||
-- Дата: 2026-01-17
|
||
-- Автор: DAARION Team
|
||
|
||
-- ============================================================================
|
||
-- 1. GROUPS — Групові чати
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS groups (
|
||
group_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
platform VARCHAR(50) NOT NULL, -- telegram, discord, matrix, slack
|
||
platform_group_id VARCHAR(255) NOT NULL, -- ID групи на платформі
|
||
name VARCHAR(255),
|
||
description TEXT,
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
metadata JSONB DEFAULT '{}',
|
||
|
||
UNIQUE(platform, platform_group_id)
|
||
);
|
||
|
||
CREATE INDEX idx_groups_platform ON groups(platform);
|
||
CREATE INDEX idx_groups_platform_id ON groups(platform, platform_group_id);
|
||
|
||
-- ============================================================================
|
||
-- 2. GROUP_MEMBERS — Учасники груп
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS group_members (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
group_id UUID NOT NULL REFERENCES groups(group_id) ON DELETE CASCADE,
|
||
platform_user_id VARCHAR(255) NOT NULL, -- Стабільний ID користувача на платформі
|
||
nickname VARCHAR(255), -- Поточний нікнейм (може змінюватись)
|
||
first_seen_at TIMESTAMPTZ DEFAULT NOW(),
|
||
last_seen_at TIMESTAMPTZ DEFAULT NOW(),
|
||
last_message_at TIMESTAMPTZ,
|
||
message_count INTEGER DEFAULT 0,
|
||
no_memory_in_group BOOLEAN DEFAULT FALSE, -- Opt-out flag
|
||
status VARCHAR(20) DEFAULT 'active', -- active, left, banned
|
||
|
||
UNIQUE(group_id, platform_user_id)
|
||
);
|
||
|
||
CREATE INDEX idx_group_members_group ON group_members(group_id);
|
||
CREATE INDEX idx_group_members_user ON group_members(platform_user_id);
|
||
CREATE INDEX idx_group_members_no_memory ON group_members(no_memory_in_group) WHERE no_memory_in_group = TRUE;
|
||
|
||
-- ============================================================================
|
||
-- 3. GROUP_MEMBER_PROFILES — Профілі учасників у групах
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS group_member_profiles (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
group_id UUID NOT NULL REFERENCES groups(group_id) ON DELETE CASCADE,
|
||
platform_user_id VARCHAR(255) NOT NULL,
|
||
|
||
-- Роль/контекст (виключно для цієї групи)
|
||
role_hint VARCHAR(100), -- investor, engineer, moderator, newcomer, etc.
|
||
language_preference VARCHAR(10) DEFAULT 'uk',
|
||
communication_style VARCHAR(50), -- formal, casual, technical
|
||
|
||
-- Інтереси/теми (в контексті групи)
|
||
topics_of_interest TEXT[], -- ['BioMiner', 'tokenomics', 'governance']
|
||
last_topics JSONB DEFAULT '[]', -- Останні обговорювані теми
|
||
|
||
-- Преференції спілкування
|
||
preferences_json JSONB DEFAULT '{}',
|
||
notes_short TEXT, -- Короткі нотатки агента про учасника
|
||
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
||
UNIQUE(group_id, platform_user_id)
|
||
);
|
||
|
||
CREATE INDEX idx_group_profiles_group ON group_member_profiles(group_id);
|
||
CREATE INDEX idx_group_profiles_user ON group_member_profiles(platform_user_id);
|
||
CREATE INDEX idx_group_profiles_role ON group_member_profiles(role_hint);
|
||
|
||
-- ============================================================================
|
||
-- 4. USERS — Глобальні користувачі (для DM та cross-platform)
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS memory_users (
|
||
user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
||
-- Платформенні ідентифікатори
|
||
telegram_id VARCHAR(50),
|
||
discord_id VARCHAR(50),
|
||
matrix_id VARCHAR(255),
|
||
email VARCHAR(255),
|
||
|
||
-- Глобальні налаштування
|
||
display_name VARCHAR(255),
|
||
global_memory_enabled BOOLEAN DEFAULT TRUE,
|
||
pii_allowed BOOLEAN DEFAULT FALSE, -- Дозвіл на збереження PII
|
||
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
||
UNIQUE(telegram_id),
|
||
UNIQUE(discord_id),
|
||
UNIQUE(matrix_id)
|
||
);
|
||
|
||
CREATE INDEX idx_users_telegram ON memory_users(telegram_id) WHERE telegram_id IS NOT NULL;
|
||
CREATE INDEX idx_users_discord ON memory_users(discord_id) WHERE discord_id IS NOT NULL;
|
||
|
||
-- ============================================================================
|
||
-- 5. CONSENT — Згода на обробку пам'яті
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS memory_consent (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES memory_users(user_id) ON DELETE CASCADE,
|
||
platform_user_id VARCHAR(255), -- Якщо user_id ще не створено
|
||
|
||
-- Типи згоди
|
||
memory_enabled BOOLEAN DEFAULT TRUE,
|
||
pii_allowed BOOLEAN DEFAULT FALSE,
|
||
cross_group_memory BOOLEAN DEFAULT FALSE, -- Дозвіл на перенос між групами
|
||
dm_memory_enabled BOOLEAN DEFAULT TRUE,
|
||
|
||
-- Retention policy
|
||
retention_policy VARCHAR(50) DEFAULT 'default', -- default, minimal, extended, forever
|
||
retention_days INTEGER DEFAULT 365,
|
||
|
||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_by VARCHAR(100) DEFAULT 'user',
|
||
|
||
UNIQUE(user_id),
|
||
UNIQUE(platform_user_id)
|
||
);
|
||
|
||
-- ============================================================================
|
||
-- 6. CONVERSATIONS — Розмови/сесії
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS conversations (
|
||
conversation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
||
-- Контекст
|
||
user_id UUID REFERENCES memory_users(user_id),
|
||
platform_user_id VARCHAR(255), -- Fallback якщо немає user_id
|
||
group_id UUID REFERENCES groups(group_id),
|
||
|
||
-- Метадані
|
||
channel VARCHAR(50) NOT NULL, -- dm, group, channel
|
||
platform VARCHAR(50) NOT NULL,
|
||
agent_id VARCHAR(100) DEFAULT 'helion',
|
||
|
||
-- Часові межі
|
||
started_at TIMESTAMPTZ DEFAULT NOW(),
|
||
ended_at TIMESTAMPTZ,
|
||
last_message_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
||
-- Статистика
|
||
message_count INTEGER DEFAULT 0,
|
||
token_count INTEGER DEFAULT 0,
|
||
|
||
-- Стан
|
||
status VARCHAR(20) DEFAULT 'active', -- active, ended, archived
|
||
summary TEXT, -- Автоматичне резюме після завершення
|
||
|
||
metadata JSONB DEFAULT '{}'
|
||
);
|
||
|
||
CREATE INDEX idx_conversations_user ON conversations(user_id);
|
||
CREATE INDEX idx_conversations_platform_user ON conversations(platform_user_id);
|
||
CREATE INDEX idx_conversations_group ON conversations(group_id);
|
||
CREATE INDEX idx_conversations_status ON conversations(status);
|
||
CREATE INDEX idx_conversations_started ON conversations(started_at DESC);
|
||
|
||
-- ============================================================================
|
||
-- 7. MESSAGES — Повідомлення
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS messages (
|
||
message_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
conversation_id UUID NOT NULL REFERENCES conversations(conversation_id) ON DELETE CASCADE,
|
||
|
||
-- Автор
|
||
role VARCHAR(20) NOT NULL, -- user, assistant, system, tool
|
||
platform_user_id VARCHAR(255), -- Для групових чатів
|
||
|
||
-- Контент
|
||
content TEXT NOT NULL,
|
||
content_type VARCHAR(50) DEFAULT 'text', -- text, image, audio, file
|
||
|
||
-- Часова мітка
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
||
-- PII/Redaction
|
||
has_pii BOOLEAN DEFAULT FALSE,
|
||
redaction_state VARCHAR(20) DEFAULT 'none', -- none, partial, full
|
||
original_content_hash VARCHAR(64), -- Для аудиту
|
||
|
||
-- Токени
|
||
token_count INTEGER,
|
||
model_used VARCHAR(100),
|
||
|
||
metadata JSONB DEFAULT '{}'
|
||
);
|
||
|
||
CREATE INDEX idx_messages_conversation ON messages(conversation_id);
|
||
CREATE INDEX idx_messages_created ON messages(created_at DESC);
|
||
CREATE INDEX idx_messages_role ON messages(role);
|
||
CREATE INDEX idx_messages_pii ON messages(has_pii) WHERE has_pii = TRUE;
|
||
|
||
-- ============================================================================
|
||
-- 8. MEMORIES — Довготривала пам'ять (episodic + semantic)
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS memories (
|
||
memory_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
||
-- Власник пам'яті
|
||
user_id UUID REFERENCES memory_users(user_id),
|
||
platform_user_id VARCHAR(255),
|
||
group_id UUID REFERENCES groups(group_id), -- NULL = глобальна/DM пам'ять
|
||
|
||
-- Тип пам'яті
|
||
memory_type VARCHAR(50) NOT NULL, -- episodic, semantic, procedural
|
||
category VARCHAR(100), -- preference, fact, interaction, topic_interest
|
||
|
||
-- Контент
|
||
content TEXT NOT NULL,
|
||
summary TEXT, -- Короткий опис для швидкого retrieval
|
||
|
||
-- Важливість та TTL
|
||
importance FLOAT DEFAULT 0.5, -- 0.0 - 1.0
|
||
confidence FLOAT DEFAULT 0.8,
|
||
ttl_days INTEGER, -- NULL = безстроково
|
||
expires_at TIMESTAMPTZ,
|
||
|
||
-- Джерело
|
||
source_message_ids UUID[],
|
||
source_conversation_id UUID REFERENCES conversations(conversation_id),
|
||
extraction_method VARCHAR(50) DEFAULT 'explicit', -- explicit, inferred, llm_extracted
|
||
|
||
-- Embedding (для vector search)
|
||
embedding_id VARCHAR(255), -- ID в Qdrant/pgvector
|
||
embedding_model VARCHAR(100),
|
||
|
||
-- Часові мітки
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
last_accessed_at TIMESTAMPTZ,
|
||
access_count INTEGER DEFAULT 0,
|
||
|
||
-- Статус
|
||
is_active BOOLEAN DEFAULT TRUE,
|
||
is_verified BOOLEAN DEFAULT FALSE,
|
||
|
||
metadata JSONB DEFAULT '{}'
|
||
);
|
||
|
||
CREATE INDEX idx_memories_user ON memories(user_id);
|
||
CREATE INDEX idx_memories_platform_user ON memories(platform_user_id);
|
||
CREATE INDEX idx_memories_group ON memories(group_id);
|
||
CREATE INDEX idx_memories_type ON memories(memory_type);
|
||
CREATE INDEX idx_memories_category ON memories(category);
|
||
CREATE INDEX idx_memories_importance ON memories(importance DESC);
|
||
CREATE INDEX idx_memories_active ON memories(is_active) WHERE is_active = TRUE;
|
||
CREATE INDEX idx_memories_expires ON memories(expires_at) WHERE expires_at IS NOT NULL;
|
||
|
||
-- ============================================================================
|
||
-- 9. MEMORY_EVENTS — Аудит (хто/що/коли змінював пам'ять)
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS memory_events (
|
||
event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
||
-- Що змінилось
|
||
memory_id UUID REFERENCES memories(memory_id) ON DELETE SET NULL,
|
||
user_id UUID REFERENCES memory_users(user_id),
|
||
group_id UUID REFERENCES groups(group_id),
|
||
|
||
-- Дія
|
||
action VARCHAR(50) NOT NULL, -- created, updated, deleted, accessed, opt_out, opt_in
|
||
|
||
-- Хто зробив
|
||
actor VARCHAR(100) NOT NULL, -- user, agent:helion, system, admin
|
||
actor_user_id VARCHAR(255),
|
||
|
||
-- Деталі
|
||
old_value JSONB,
|
||
new_value JSONB,
|
||
reason TEXT,
|
||
|
||
-- Час
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
||
-- Контекст
|
||
ip_address INET,
|
||
user_agent TEXT,
|
||
metadata JSONB DEFAULT '{}'
|
||
);
|
||
|
||
CREATE INDEX idx_memory_events_memory ON memory_events(memory_id);
|
||
CREATE INDEX idx_memory_events_user ON memory_events(user_id);
|
||
CREATE INDEX idx_memory_events_action ON memory_events(action);
|
||
CREATE INDEX idx_memory_events_created ON memory_events(created_at DESC);
|
||
|
||
-- ============================================================================
|
||
-- 10. GROUP_INTERACTIONS — Взаємодії в групах (опціонально)
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS group_interactions (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
group_id UUID NOT NULL REFERENCES groups(group_id) ON DELETE CASCADE,
|
||
platform_user_id VARCHAR(255) NOT NULL,
|
||
|
||
-- Тема/контекст взаємодії
|
||
topic VARCHAR(255),
|
||
interaction_type VARCHAR(50), -- question, answer, discussion, feedback
|
||
|
||
-- Часові мітки
|
||
first_interaction_at TIMESTAMPTZ DEFAULT NOW(),
|
||
last_interaction_at TIMESTAMPTZ DEFAULT NOW(),
|
||
interaction_count INTEGER DEFAULT 1,
|
||
|
||
-- Якість взаємодії
|
||
sentiment_score FLOAT, -- -1.0 до 1.0
|
||
helpfulness_score FLOAT, -- 0.0 до 1.0
|
||
|
||
metadata JSONB DEFAULT '{}'
|
||
);
|
||
|
||
CREATE INDEX idx_group_interactions_group ON group_interactions(group_id);
|
||
CREATE INDEX idx_group_interactions_user ON group_interactions(platform_user_id);
|
||
CREATE INDEX idx_group_interactions_topic ON group_interactions(topic);
|
||
|
||
-- ============================================================================
|
||
-- 11. HELPER FUNCTIONS
|
||
-- ============================================================================
|
||
|
||
-- Функція для автоматичного оновлення updated_at
|
||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||
RETURNS TRIGGER AS $$
|
||
BEGIN
|
||
NEW.updated_at = NOW();
|
||
RETURN NEW;
|
||
END;
|
||
$$ language 'plpgsql';
|
||
|
||
-- Тригери для updated_at
|
||
CREATE TRIGGER update_groups_updated_at BEFORE UPDATE ON groups
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
||
CREATE TRIGGER update_group_member_profiles_updated_at BEFORE UPDATE ON group_member_profiles
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
||
CREATE TRIGGER update_memory_users_updated_at BEFORE UPDATE ON memory_users
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
||
CREATE TRIGGER update_memories_updated_at BEFORE UPDATE ON memories
|
||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
||
-- Функція для opt-out користувача з групи
|
||
CREATE OR REPLACE FUNCTION memory_opt_out_group(
|
||
p_group_id UUID,
|
||
p_platform_user_id VARCHAR(255)
|
||
) RETURNS VOID AS $$
|
||
BEGIN
|
||
-- Позначити учасника як no_memory
|
||
UPDATE group_members
|
||
SET no_memory_in_group = TRUE
|
||
WHERE group_id = p_group_id AND platform_user_id = p_platform_user_id;
|
||
|
||
-- Деактивувати всі пам'яті цього користувача в групі
|
||
UPDATE memories
|
||
SET is_active = FALSE
|
||
WHERE group_id = p_group_id AND platform_user_id = p_platform_user_id;
|
||
|
||
-- Видалити профіль
|
||
DELETE FROM group_member_profiles
|
||
WHERE group_id = p_group_id AND platform_user_id = p_platform_user_id;
|
||
|
||
-- Записати в аудит
|
||
INSERT INTO memory_events (user_id, group_id, action, actor, actor_user_id, reason)
|
||
VALUES (NULL, p_group_id, 'opt_out', 'user', p_platform_user_id, 'User requested opt-out from group memory');
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
|
||
-- Функція для повного видалення користувача з групи (forget)
|
||
CREATE OR REPLACE FUNCTION memory_forget_in_group(
|
||
p_group_id UUID,
|
||
p_platform_user_id VARCHAR(255)
|
||
) RETURNS VOID AS $$
|
||
BEGIN
|
||
-- Видалити всі пам'яті
|
||
DELETE FROM memories
|
||
WHERE group_id = p_group_id AND platform_user_id = p_platform_user_id;
|
||
|
||
-- Видалити профіль
|
||
DELETE FROM group_member_profiles
|
||
WHERE group_id = p_group_id AND platform_user_id = p_platform_user_id;
|
||
|
||
-- Очистити дані учасника (але залишити запис)
|
||
UPDATE group_members
|
||
SET no_memory_in_group = TRUE, nickname = NULL
|
||
WHERE group_id = p_group_id AND platform_user_id = p_platform_user_id;
|
||
|
||
-- Записати в аудит
|
||
INSERT INTO memory_events (group_id, action, actor, actor_user_id, reason)
|
||
VALUES (p_group_id, 'deleted', 'user', p_platform_user_id, 'User requested full memory deletion in group');
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
|
||
-- ============================================================================
|
||
-- 12. VIEWS для зручності
|
||
-- ============================================================================
|
||
|
||
-- View: Активні пам'яті користувача
|
||
CREATE OR REPLACE VIEW v_active_user_memories AS
|
||
SELECT
|
||
m.*,
|
||
u.display_name,
|
||
g.name as group_name
|
||
FROM memories m
|
||
LEFT JOIN memory_users u ON m.user_id = u.user_id
|
||
LEFT JOIN groups g ON m.group_id = g.group_id
|
||
WHERE m.is_active = TRUE
|
||
AND (m.expires_at IS NULL OR m.expires_at > NOW());
|
||
|
||
-- View: Учасники групи з профілями
|
||
CREATE OR REPLACE VIEW v_group_members_with_profiles AS
|
||
SELECT
|
||
gm.*,
|
||
gmp.role_hint,
|
||
gmp.language_preference,
|
||
gmp.topics_of_interest,
|
||
gmp.notes_short,
|
||
g.name as group_name,
|
||
g.platform
|
||
FROM group_members gm
|
||
LEFT JOIN group_member_profiles gmp
|
||
ON gm.group_id = gmp.group_id AND gm.platform_user_id = gmp.platform_user_id
|
||
JOIN groups g ON gm.group_id = g.group_id
|
||
WHERE gm.status = 'active';
|
||
|
||
-- ============================================================================
|
||
-- Result
|
||
-- ============================================================================
|
||
|
||
SELECT 'Migration 046 completed: Memory Service full schema created' AS result;
|