db->charset ) ) $charset_collate = "DEFAULT CHARACTER SET " . WPF()->db->charset; if( ! empty( WPF()->db->collate ) ) $charset_collate .= " COLLATE " . WPF()->db->collate; $engine = version_compare( WPF()->db->db_version(), '5.6.4', '>=' ) ? 'InnoDB' : 'MyISAM'; return [ WPF()->tables->boards => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->boards . "`( `boardid` INT UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(100) NOT NULL, `slug` VARCHAR(255) NOT NULL, `pageid` BIGINT NOT NULL, `modules` TEXT, `locale` VARCHAR(10) DEFAULT '', `is_standalone` TINYINT(1) NOT NULL DEFAULT 0, `excld_urls` TEXT, `status` TINYINT(1) NOT NULL DEFAULT 1, `settings` TEXT, PRIMARY KEY (`boardid`), UNIQUE KEY `unique_slug` (`slug`(191)), KEY `bulk_index` (`slug`(191), `locale`, `is_standalone`, `status`) ) ENGINE=INNODB $charset_collate", WPF()->tables->forums => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->forums . "`( `forumid` INT UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(255) NOT NULL, `slug` VARCHAR(255) NOT NULL, `description` LONGTEXT, `parentid` INT UNSIGNED NOT NULL DEFAULT 0, `icon` VARCHAR(255), `cover` BIGINT UNSIGNED NOT NULL DEFAULT 0, `cover_height` INT(4) UNSIGNED NOT NULL DEFAULT 150, `last_topicid` INT UNSIGNED NOT NULL DEFAULT 0, `last_postid` INT UNSIGNED NOT NULL DEFAULT 0, `last_userid` VARCHAR(255) NOT NULL, `last_post_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', `topics` INT NOT NULL DEFAULT 0, `posts` INT NOT NULL DEFAULT 0, `permissions` TEXT, `meta_key` TEXT, `meta_desc` TEXT, `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `is_cat` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `layout` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `order` INT UNSIGNED NOT NULL DEFAULT 0, `color` VARCHAR(7) NOT NULL DEFAULT '', `type` VARCHAR(20) NOT NULL DEFAULT 'forum', PRIMARY KEY (`forumid`), UNIQUE KEY `UNIQUE SLUG` (`slug`(191)), KEY `order` (`order`), KEY `status` (`status`), KEY `parentid` (`parentid`), KEY `last_postid` (`last_postid`), KEY `last_userid` (`last_userid`(191)), KEY `is_cat` (`is_cat`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->topics => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->topics . "`( `topicid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `forumid` INT UNSIGNED NOT NULL, `first_postid` BIGINT UNSIGNED NOT NULL DEFAULT 0, `userid` INT UNSIGNED NOT NULL, `title` VARCHAR(255) NOT NULL, `slug` VARCHAR(255) NOT NULL, `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', `last_post` BIGINT UNSIGNED NOT NULL DEFAULT 0, `posts` INT NOT NULL DEFAULT 0, `votes` INT NOT NULL DEFAULT 0, `answers` INT NOT NULL DEFAULT 0, `views` INT UNSIGNED NOT NULL DEFAULT 0, `meta_key` TEXT, `meta_desc` TEXT, `type` TINYINT NOT NULL DEFAULT 0, `solved` TINYINT(1) NOT NULL DEFAULT 0, `closed` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `has_attach` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `private` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `name` VARCHAR(50) NOT NULL DEFAULT '', `email` VARCHAR(50) NOT NULL DEFAULT '', `prefix` VARCHAR(100) NOT NULL DEFAULT '', `tags` TEXT, `indexed` VARCHAR(32) DEFAULT NULL, `local` TINYINT(1) NOT NULL DEFAULT 0, `cloud` TINYINT(1) NOT NULL DEFAULT 0, `task_tag` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`topicid`), KEY `slug` (`slug`(191)), FULLTEXT KEY `title` (`title`), KEY `forumid` (`forumid`), KEY `first_postid` (`first_postid`), KEY `created` (`created`), KEY `modified` (`modified`), KEY `last_post` (`last_post`), KEY `type` (`type`), KEY `status` (`status`), KEY `email` (`email`), KEY `solved` (`solved`), KEY `is_private` (`private`), KEY `own_private` (`userid`,`private`), KEY `forumid_status` (`forumid`,`status`), KEY `forumid_status_private` (`forumid`,`status`,`private`), KEY `prefix` (`prefix`), KEY `indexed` (`indexed`), KEY `local` (`local`), KEY `cloud` (`cloud`), KEY `task_tag` (`task_tag`) ) ENGINE=$engine $charset_collate", WPF()->tables->postmeta => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->postmeta . "`( `metaid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `postid` BIGINT UNSIGNED NOT NULL, `metakey` VARCHAR(255) NOT NULL, `metavalue` MEDIUMTEXT, `forumid` INT UNSIGNED NOT NULL DEFAULT 0, `topicid` BIGINT UNSIGNED NOT NULL DEFAULT 0, `is_first_post` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, `status` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, `private` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`metaid`), KEY `postid_metakey` (`postid`, `metakey`(191)), KEY `forumid` (`forumid`), KEY `topicid` (`topicid`), KEY `is_first_post` (`is_first_post`), KEY `status` (`status`), KEY `private` (`private`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->posts => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->posts . "`( `postid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `parentid` BIGINT UNSIGNED NOT NULL DEFAULT 0, `forumid` INT UNSIGNED NOT NULL, `topicid` BIGINT UNSIGNED NOT NULL, `userid` INT UNSIGNED NOT NULL, `title` varchar(255), `body` LONGTEXT, `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', `likes` INT UNSIGNED NOT NULL DEFAULT 0, `votes` INT NOT NULL DEFAULT 0, `is_answer` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `is_first_post` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `name` VARCHAR(50) NOT NULL DEFAULT '', `email` VARCHAR(50) NOT NULL DEFAULT '', `private` tinyint(1) UNSIGNED NOT NULL DEFAULT 0, `root` BIGINT NULL DEFAULT NULL, PRIMARY KEY (`postid`), FULLTEXT KEY `title`(`title`(191)), FULLTEXT KEY `body` (`body`), FULLTEXT KEY `title_plus_body` (`title`,`body`), KEY `topicid` (`topicid`), KEY `forumid` (`forumid`), KEY `userid` (`userid`), KEY `created` (`created`), KEY `parentid` (`parentid`), KEY `is_answer` (`is_answer`), KEY `is_first_post` (`is_first_post`), KEY `status` (`status`), KEY `email` (`email`), KEY `is_private` (`private`), KEY `root` (`root`), KEY `forumid_status` (`forumid`,`status`), KEY `topicid_status` (`topicid`,`status`), KEY `topicid_solved` (`topicid`,`is_answer`), KEY `topicid_parentid` (`topicid`,`parentid`), KEY `forumid_status_private` (`forumid`, `status`, `private`), KEY `forumid_answer_first` (`forumid`, `is_answer`, `is_first_post`) ) ENGINE=$engine $charset_collate", WPF()->tables->profiles => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->profiles . "` ( `userid` BIGINT UNSIGNED NOT NULL, `title` VARCHAR(255) NOT NULL DEFAULT 'member', `groupid` INT UNSIGNED NOT NULL, `secondary_groupids` VARCHAR(255) NOT NULL DEFAULT '', `avatar` VARCHAR(991) NOT NULL DEFAULT '', `cover` VARCHAR(255) NOT NULL DEFAULT '', `posts` INT UNSIGNED NOT NULL DEFAULT 0, `topics` INT UNSIGNED NOT NULL DEFAULT 0, `questions` INT UNSIGNED NOT NULL DEFAULT 0, `answers` INT UNSIGNED NOT NULL DEFAULT 0, `comments` INT UNSIGNED NOT NULL DEFAULT 0, `reactions_in` TEXT, `reactions_out` TEXT, `points` INT NOT NULL DEFAULT 0, `custom_points` INT NOT NULL DEFAULT 0, `online_time` INT UNSIGNED NOT NULL DEFAULT 0, `timezone` VARCHAR(255) NOT NULL DEFAULT '', `location` VARCHAR(255) NOT NULL DEFAULT '', `signature` TEXT, `about` TEXT, `occupation` TEXT, `status` VARCHAR(8) NOT NULL DEFAULT 'active' COMMENT 'active, blocked, trashed, spamer', `is_email_confirmed` TINYINT(1) NOT NULL DEFAULT 0, `is_mention_muted` TINYINT(1) NOT NULL DEFAULT 0, `fields` LONGTEXT, PRIMARY KEY (`userid`), KEY `groupid` (`groupid`), KEY `online_time` (`online_time`), KEY `posts` (`posts`), KEY `status` (`status`), KEY `is_email_confirmed` (`is_email_confirmed`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->usergroups => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->usergroups . "`( `groupid` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `cans` LONGTEXT NOT NULL COMMENT 'board permissions', `description` TEXT, `utitle` VARCHAR(100) NOT NULL DEFAULT '', `role` VARCHAR(50) NOT NULL DEFAULT '', `access` VARCHAR(50) NOT NULL DEFAULT '', `color` VARCHAR(7) NOT NULL DEFAULT '', `visible` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, `secondary` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, `is_default` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`groupid`), KEY `visible` (`visible`), KEY `secondary` (`secondary`), UNIQUE KEY `UNIQUE_GROUP_NAME` (`name`(191)) ) ENGINE=MyISAM $charset_collate", WPF()->tables->languages => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->languages . "`( `langid` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `status` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, PRIMARY KEY (`langid`), UNIQUE KEY `UNIQUE language name` (`name`(191)) ) ENGINE=MyISAM $charset_collate", WPF()->tables->phrases => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->phrases . "` ( `phraseid` INT UNSIGNED NOT NULL AUTO_INCREMENT, `langid` INT UNSIGNED NOT NULL, `phrase_key` text NOT NULL, `phrase_value` text NOT NULL, `package` VARCHAR(255) NOT NULL DEFAULT 'wpforo', PRIMARY KEY (`phraseid`), KEY `langid` (`langid`), KEY `phrase_key` (`phrase_key`(191)), UNIQUE KEY lng_and_key_uniq (`langid`, `phrase_key`(191)) ) ENGINE=MyISAM $charset_collate", WPF()->tables->accesses => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->accesses . "`( `accessid` INT UNSIGNED NOT NULL AUTO_INCREMENT, `access` VARCHAR(255) NOT NULL, `title` VARCHAR(255) NOT NULL, `cans` LONGTEXT NOT NULL COMMENT 'forum permissions', PRIMARY KEY (`accessid`), UNIQUE KEY ( `access`(191) ) ) ENGINE=MyISAM $charset_collate", WPF()->tables->subscribes => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->subscribes . "` ( `subid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `itemid` BIGINT UNSIGNED NOT NULL, `type` VARCHAR(5) NOT NULL, `confirmkey` varchar(32) NOT NULL, `userid` BIGINT UNSIGNED NOT NULL, `active` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, `user_name` VARCHAR(60) NOT NULL, `user_email` VARCHAR(60) NOT NULL, PRIMARY KEY (`subid`), UNIQUE KEY `fld_group_unq` (`itemid`,`type`,`userid`,`user_email`(60)), UNIQUE KEY `confirmkey` (`confirmkey`), KEY `itemid_2` (`itemid`), KEY `userid` (`userid`) ) ENGINE=INNODB $charset_collate", WPF()->tables->follows => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->follows . "` ( `followid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `itemid` BIGINT UNSIGNED NOT NULL, `itemtype` VARCHAR(5) NOT NULL DEFAULT 'user', `confirmkey` VARCHAR(32) NOT NULL, `userid` BIGINT UNSIGNED NOT NULL, `active` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, `name` VARCHAR(60) NOT NULL, `email` VARCHAR(60) NOT NULL, PRIMARY KEY (`followid`), UNIQUE KEY `fld_group_unq` (`itemid`,`itemtype`,`userid`,`email`(60)), UNIQUE KEY `confirmkey` (`confirmkey`), KEY `itemid` (`itemid`), KEY `userid` (`userid`) ) ENGINE=INNODB $charset_collate", WPF()->tables->visits => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->visits . "` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `userid` BIGINT UNSIGNED NOT NULL, `name` VARCHAR(60) NOT NULL, `ip` VARCHAR(60) NOT NULL, `time` INT UNSIGNED NOT NULL, `forumid` INT UNSIGNED NOT NULL, `topicid` BIGINT UNSIGNED NOT NULL, PRIMARY KEY (`id`), KEY `userid` (`userid`), KEY `forumid` (`forumid`), KEY `topicid` (`topicid`), KEY `time` (`time`), KEY `ip` (`ip`), KEY `time_forumid` (`time`,`forumid`), KEY `time_topicid` (`time`,`topicid`), UNIQUE KEY `unique_tracking` (`userid`,`ip`,`forumid`,`topicid`) ) ENGINE=INNODB $charset_collate", WPF()->tables->activity => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->activity . "` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `type` VARCHAR(60) NOT NULL, `itemid` BIGINT UNSIGNED NOT NULL, `itemtype` VARCHAR(60) NOT NULL, `itemid_second` BIGINT UNSIGNED NOT NULL DEFAULT 0, `userid` BIGINT UNSIGNED NOT NULL DEFAULT 0, `name` VARCHAR(60) NOT NULL DEFAULT '', `email` VARCHAR(70) NOT NULL DEFAULT '', `date` INT UNSIGNED NOT NULL DEFAULT 0, `content` TEXT, `permalink` VARCHAR(1024) NOT NULL DEFAULT '', `new` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `type` (`type`), KEY `type_objid_objtype` (`type`,`itemid`,`itemtype`), KEY `type_objid_objtype_userid` (`type`,`itemid`,`itemtype`,`userid`), KEY `itemtype_userid_new` (`itemtype`,`userid`,`new`), KEY `date` (`date`) ) ENGINE=INNODB $charset_collate", WPF()->tables->post_revisions => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->post_revisions . "` ( `revisionid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `userid` BIGINT UNSIGNED NOT NULL DEFAULT 0, `textareaid` VARCHAR(50) NOT NULL, `postid` BIGINT UNSIGNED NOT NULL DEFAULT 0, `body` LONGTEXT, `created` INT UNSIGNED NOT NULL DEFAULT 0, `version` SMALLINT NOT NULL DEFAULT 0, `email` VARCHAR(50) NOT NULL DEFAULT '', `url` TEXT, PRIMARY KEY (`revisionid`), KEY `userid_textareaid_postid_email` (`userid`, `textareaid`, `postid`, `email`, `url`(70)) ) ENGINE=INNODB $charset_collate", WPF()->tables->tags => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->tags . "`( `tagid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `tag` VARCHAR(255) NOT NULL, `prefix` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, `count` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`tagid`), UNIQUE KEY `tag` (`tag`(190)), KEY (`prefix`) ) ENGINE=MyISAM $charset_collate", WPF()->tables->logs => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->logs . "`( `logid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `sessionid` VARCHAR(255) NOT NULL, `key` VARCHAR(255) NOT NULL, `value` MEDIUMTEXT NOT NULL, PRIMARY KEY (`logid`), KEY `sessionid_key` (`sessionid`(20), `key`(160)) ) ENGINE=InnoDB $charset_collate", WPF()->tables->reactions => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->reactions . "` ( `reactionid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `userid` BIGINT UNSIGNED NOT NULL, `postid` BIGINT UNSIGNED NOT NULL, `post_userid` BIGINT UNSIGNED NOT NULL DEFAULT 0, `reaction` TINYINT(4) NOT NULL DEFAULT 1, `type` VARCHAR(50) NOT NULL DEFAULT 'up', `name` VARCHAR(50) DEFAULT NULL, `email` VARCHAR(100) DEFAULT NULL, PRIMARY KEY (`reactionid`), UNIQUE KEY `unique_reaction` (`userid`,`postid`,`type`,`email`), KEY `performance` (`postid`,`post_userid`,`type`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->bookmarks => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->bookmarks . "` ( `bookmarkid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `userid` BIGINT UNSIGNED NOT NULL, `boardid` INT UNSIGNED NOT NULL, `postid` BIGINT UNSIGNED NOT NULL, `created` INT UNSIGNED NOT NULL, `status` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, PRIMARY KEY (`bookmarkid`), UNIQUE KEY `unique_bookmark` (`userid`,`boardid`,`postid`), KEY `performance` (`userid`,`boardid`,`postid`,`status`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_cache => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_cache . "` ( `type` VARCHAR(50) NOT NULL COMMENT 'Cache type', `cache_key` BINARY(16) NOT NULL COMMENT 'MD5 hash', `response` MEDIUMTEXT NOT NULL COMMENT 'JSON response data', `expires_at` INT UNSIGNED NOT NULL COMMENT 'Unix timestamp for cache expiry (0=never)', `postid` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Post ID', PRIMARY KEY (`type`, `cache_key`), KEY `idx_expires` (`expires_at`), KEY `idx_postid` (`postid`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_chat_conversations => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_chat_conversations . "` ( `conversation_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `userid` BIGINT UNSIGNED NOT NULL COMMENT 'User ID who owns this conversation', `title` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Conversation title (auto-generated from first message)', `running_summary` TEXT COMMENT 'AI-generated running summary of the conversation', `key_facts` TEXT COMMENT 'JSON array of extracted key facts', `message_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Number of messages in conversation', `total_tokens` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total tokens used in conversation', `total_credits` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total credits spent on conversation', `last_message_at` DATETIME DEFAULT NULL COMMENT 'Timestamp of last message', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`conversation_id`), KEY `idx_userid` (`userid`), KEY `idx_userid_updated` (`userid`, `updated_at`), KEY `idx_last_message` (`last_message_at`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_chat_messages => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_chat_messages . "` ( `message_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `conversation_id` BIGINT UNSIGNED NOT NULL COMMENT 'Parent conversation ID', `role` ENUM('user', 'assistant') NOT NULL COMMENT 'Message role', `content` TEXT NOT NULL COMMENT 'Message content', `tokens_used` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Tokens used for this message', `credits_spent` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Credits spent for this response', `quality_tier` VARCHAR(20) NOT NULL DEFAULT 'fast' COMMENT 'Quality tier used (fast, balanced, advanced)', `sources_count` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Number of RAG sources used', `sources_json` TEXT COMMENT 'JSON array of source references', `context_updated` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Whether context was updated after this message', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`message_id`), KEY `idx_conversation` (`conversation_id`), KEY `idx_conversation_created` (`conversation_id`, `created_at`), KEY `idx_role` (`role`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_tasks => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_tasks . "` ( `task_id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `task_name` VARCHAR(100) NOT NULL COMMENT 'User-defined task name', `task_type` VARCHAR(30) NOT NULL COMMENT 'topic_generator or reply_generator', `status` VARCHAR(20) NOT NULL DEFAULT 'paused' COMMENT 'active, paused, error', `board_id` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Board ID for multi-board support', `config` LONGTEXT NOT NULL COMMENT 'JSON configuration for task-specific settings', `frequency` VARCHAR(30) NOT NULL DEFAULT 'daily' COMMENT 'hourly, 3hours, 6hours, daily, 3days, weekly, custom', `next_run_time` DATETIME DEFAULT NULL COMMENT 'When task should run next', `last_run_time` DATETIME DEFAULT NULL COMMENT 'When task last ran', `last_run_status` VARCHAR(20) DEFAULT NULL COMMENT 'success, failure, skipped', `total_runs` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total execution count', `successful_runs` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Successful execution count', `failed_runs` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Failed execution count', `items_created` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total topics/replies created', `credits_used` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Total credits consumed', `credit_stop_threshold` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Stop when credits below this', `auto_pause_on_limit` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Auto-pause when credit threshold reached', `created_by` INT UNSIGNED NOT NULL COMMENT 'User ID who created the task', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`task_id`), KEY `idx_status` (`status`), KEY `idx_board_id` (`board_id`), KEY `idx_task_type` (`task_type`), KEY `idx_next_run` (`next_run_time`), KEY `idx_status_next_run` (`status`, `next_run_time`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_task_logs => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_task_logs . "` ( `log_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `task_id` INT UNSIGNED NOT NULL COMMENT 'Foreign key to ai_tasks', `execution_time` DATETIME NOT NULL COMMENT 'When execution started', `status` VARCHAR(20) NOT NULL COMMENT 'success, failure, skipped', `items_created` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Topics/replies created this run', `credits_used` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Credits consumed this run', `execution_duration` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Duration in milliseconds', `result_data` LONGTEXT COMMENT 'JSON with created item IDs, skipped counts, etc.', `error_message` TEXT COMMENT 'Error details if failed', `api_request_summary` TEXT COMMENT 'Sanitized API request summary', `api_response_summary` TEXT COMMENT 'Truncated API response summary', `api_response_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'API response time in ms', PRIMARY KEY (`log_id`), KEY `idx_task_id` (`task_id`), KEY `idx_execution_time` (`execution_time`), KEY `idx_status` (`status`), KEY `idx_task_status` (`task_id`, `status`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_moderation => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_moderation . "` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `content_type` VARCHAR(20) NOT NULL COMMENT 'topic or post', `content_id` BIGINT UNSIGNED NOT NULL COMMENT 'Topic ID or Post ID', `topicid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Parent topic ID (for posts)', `forumid` INT UNSIGNED NOT NULL COMMENT 'Forum ID', `userid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Author user ID', `moderation_type` VARCHAR(50) NOT NULL COMMENT 'spam, toxicity, quality, compliance', `score` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Score 0-100', `is_flagged` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Whether content was flagged', `confidence` DECIMAL(3,2) NOT NULL DEFAULT 0.00 COMMENT 'AI confidence 0.00-1.00', `action_taken` VARCHAR(50) DEFAULT NULL COMMENT 'approve, hold, delete, ban_user, etc.', `action_reason` TEXT COMMENT 'Reason for the action', `indicators` LONGTEXT COMMENT 'JSON array of detected indicators', `analysis_summary` TEXT COMMENT 'Brief AI analysis summary', `quality_tier` VARCHAR(20) NOT NULL DEFAULT 'balanced' COMMENT 'fast, balanced, advanced, premium', `credits_used` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Credits consumed', `context_used` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Whether RAG context was used', `indexed_topics_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Number of indexed topics available', `detection_time_ms` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Processing time in ms', `content_preview` TEXT COMMENT 'First 500 chars of moderated content', `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'When moderation was performed', `reviewed_by` BIGINT UNSIGNED DEFAULT NULL COMMENT 'Admin who reviewed (if manually reviewed)', `reviewed_at` DATETIME DEFAULT NULL COMMENT 'When manually reviewed', `review_action` VARCHAR(50) DEFAULT NULL COMMENT 'Override action taken by admin', `review_notes` TEXT COMMENT 'Admin notes on review', PRIMARY KEY (`id`), KEY `idx_content` (`content_type`, `content_id`), KEY `idx_topicid` (`topicid`), KEY `idx_forumid` (`forumid`), KEY `idx_userid` (`userid`), KEY `idx_moderation_type` (`moderation_type`), KEY `idx_is_flagged` (`is_flagged`), KEY `idx_action_taken` (`action_taken`), KEY `idx_created` (`created`), KEY `idx_reviewed` (`reviewed_by`, `reviewed_at`), KEY `idx_score` (`score`), KEY `idx_composite` (`content_type`, `moderation_type`, `is_flagged`, `created`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_embeddings => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_embeddings . "` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `topicid` BIGINT UNSIGNED NOT NULL COMMENT 'Parent topic ID', `postid` BIGINT UNSIGNED NOT NULL COMMENT 'Post/chunk ID', `forumid` INT UNSIGNED NOT NULL COMMENT 'Forum ID for filtering', `userid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Post author ID', `embedding_vector` LONGBLOB NOT NULL COMMENT 'Binary packed float32 vector', `vector_dimensions` SMALLINT UNSIGNED NOT NULL DEFAULT 1024 COMMENT 'Vector dimensions (256, 512, 1024)', `vector_magnitude` DOUBLE NOT NULL DEFAULT 1.0 COMMENT 'Pre-computed magnitude for optimization', `model_name` VARCHAR(100) NOT NULL DEFAULT 'amazon.titan-embed-text-v2' COMMENT 'Embedding model used', `content_hash` VARCHAR(32) NOT NULL COMMENT 'MD5 hash of content for change detection', `content_preview` TEXT DEFAULT NULL COMMENT 'Full chunk content for search results', `content_type` VARCHAR(20) NOT NULL DEFAULT 'forum' COMMENT 'forum or WordPress post_type', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_post_model` (`postid`, `model_name`), KEY `idx_topicid` (`topicid`), KEY `idx_forumid` (`forumid`), KEY `idx_userid` (`userid`), KEY `idx_content_hash` (`content_hash`), KEY `idx_model_name` (`model_name`), KEY `idx_content_type` (`content_type`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_embeddings_cache => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_embeddings_cache . "` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `source_type` VARCHAR(20) NOT NULL COMMENT 'topic or post', `source_id` BIGINT UNSIGNED NOT NULL COMMENT 'Source topic/post ID', `similar_type` VARCHAR(20) NOT NULL COMMENT 'topic or post', `similar_id` BIGINT UNSIGNED NOT NULL COMMENT 'Similar item ID', `similarity_score` FLOAT NOT NULL COMMENT 'Cosine similarity score 0.0-1.0', `rank_position` TINYINT UNSIGNED NOT NULL COMMENT 'Rank 1-20', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `expires_at` DATETIME NOT NULL COMMENT 'Cache expiry time', PRIMARY KEY (`id`), UNIQUE KEY `uk_source_similar` (`source_type`, `source_id`, `similar_type`, `similar_id`), KEY `idx_source` (`source_type`, `source_id`, `rank_position`), KEY `idx_expires` (`expires_at`), KEY `idx_similar` (`similar_type`, `similar_id`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->ai_logs => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->ai_logs . "` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `action_type` VARCHAR(50) NOT NULL COMMENT 'AI action type: semantic_search, translation, bot_reply, etc.', `userid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'User ID (0 for guests/cron/system)', `user_type` VARCHAR(20) NOT NULL DEFAULT 'user' COMMENT 'user, guest, cron, system', `credits_used` INT UNSIGNED NOT NULL DEFAULT 0, `status` VARCHAR(20) NOT NULL DEFAULT 'success' COMMENT 'success, error, cached', `content_type` VARCHAR(20) DEFAULT NULL COMMENT 'topic, post, etc.', `content_id` BIGINT UNSIGNED DEFAULT NULL, `forumid` INT UNSIGNED DEFAULT NULL, `topicid` BIGINT UNSIGNED DEFAULT NULL, `request_summary` TEXT COMMENT 'Sanitized request summary', `response_summary` TEXT COMMENT 'Truncated response summary', `error_message` TEXT DEFAULT NULL, `duration_ms` INT UNSIGNED NOT NULL DEFAULT 0, `ip_address` VARCHAR(45) DEFAULT NULL, `extra_data` LONGTEXT COMMENT 'JSON with additional context', `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_action_type` (`action_type`), KEY `idx_userid` (`userid`), KEY `idx_created` (`created`), KEY `idx_status` (`status`), KEY `idx_content` (`content_type`, `content_id`), KEY `idx_composite` (`action_type`, `created`, `status`) ) ENGINE=InnoDB $charset_collate", WPF()->tables->email_queue => "CREATE TABLE IF NOT EXISTS `" . WPF()->tables->email_queue . "` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `email` VARCHAR(255) NOT NULL, `subject` VARCHAR(255) NOT NULL, `message` LONGTEXT NOT NULL, `headers` TEXT, `priority` TINYINT UNSIGNED NOT NULL DEFAULT 10, `status` ENUM('pending','processing','sent','failed') NOT NULL DEFAULT 'pending', `attempts` TINYINT UNSIGNED NOT NULL DEFAULT 0, `max_attempts` TINYINT UNSIGNED NOT NULL DEFAULT 3, `created_at` DATETIME NOT NULL, `scheduled_at` DATETIME NOT NULL, `processed_at` DATETIME DEFAULT NULL, `next_retry_at` DATETIME DEFAULT NULL, `error_message` TEXT, `context` VARCHAR(50) NOT NULL DEFAULT 'general', `related_id` BIGINT UNSIGNED NOT NULL DEFAULT 0, `boardid` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `idx_status_scheduled` (`status`, `scheduled_at`), KEY `idx_status_next_retry` (`status`, `next_retry_at`), KEY `idx_context_status` (`context`, `status`), KEY `idx_boardid` (`boardid`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB $charset_collate", ]; } }