# mxchat-basic/2.2.3/admin/class-pinecone-manager.php

MxChat – AI Chatbot &amp; Content Generation for WordPress, version 2.2.3. 999 lines.

- Page: https://pluginprobe.com/plugins/mxchat-basic/2.2.3/code/admin/class-pinecone-manager.php
- Raw: https://pluginprobe.com/plugins/mxchat-basic/2.2.3/raw/admin/class-pinecone-manager.php
- Modified: 2025-06-08T00:55:44+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/mxchat-basic/2.2.3/code/admin/class-pinecone-manager.php#L10-L20`.

```php
<?php
/**
 * File: admin/class-pinecone-manager.php
 *
 * Handles all Pinecone vector database operations for MxChat
 */

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

class MxChat_Pinecone_Manager {

    /**
     * Constructor
     */
    public function __construct() {
        // Hook into WordPress actions if needed
        add_action('mxchat_delete_content', array($this, 'mxchat_delete_from_pinecone_by_url'), 10, 1);
    }

    // ========================================
    // PINECONE FETCH OPERATIONS
    // ========================================

    /**
     * Fetches records from Pinecone using provided options
     */
     public function mxchat_fetch_pinecone_records($pinecone_options, $search_query = '', $page = 1, $per_page = 20) {
         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';
         $index = $pinecone_options['mxchat_pinecone_index'] ?? '';

         if (empty($api_key) || empty($host) || empty($index)) {
             return array('data' => array(), 'total' => 0);
         }

         try {
             // First, try to get index statistics to understand the data structure
             $stats_url = "https://{$host}/describe_index_stats";

             $stats_response = wp_remote_post($stats_url, array(
                 'headers' => array(
                     'Api-Key' => $api_key,
                     'Content-Type' => 'application/json'
                 ),
                 'body' => json_encode(array()),
                 'timeout' => 30
             ));

             // Check if we can get vector IDs from local cache first
             $cached_vector_ids = get_option('mxchat_pinecone_vector_ids_cache', array());

             $all_records = array();

             // Method 1: Use cached vector IDs to fetch specific vectors
             if (!empty($cached_vector_ids)) {
                 $all_records = $this->mxchat_fetch_vectors_by_ids($pinecone_options, $cached_vector_ids);
             }

             // Method 2: Fallback to scanning approach if cache is empty or incomplete
             if (empty($all_records)) {
                 $all_records = $this->mxchat_scan_pinecone_vectors($pinecone_options);
             }

             // Filter by search query if provided
             if (!empty($search_query)) {
                 $all_records = array_filter($all_records, function($record) use ($search_query) {
                     $content = $record->article_content ?? '';
                     $source_url = $record->source_url ?? '';
                     return stripos($content, $search_query) !== false ||
                            stripos($source_url, $search_query) !== false;
                 });
             }

             // Sort records by created_at in descending order (newest first)
             usort($all_records, function($a, $b) {
                 $time_a = is_numeric($a->created_at) ? $a->created_at : strtotime($a->created_at);
                 $time_b = is_numeric($b->created_at) ? $b->created_at : strtotime($b->created_at);
                 return $time_b - $time_a;
             });

             // Handle pagination
             $total = count($all_records);
             $offset = ($page - 1) * $per_page;
             $paged_records = array_slice($all_records, $offset, $per_page);

             return array(
                 'data' => $paged_records,
                 'total' => $total
             );

         } catch (Exception $e) {
             //error_log('Pinecone fetch exception: ' . $e->getMessage());
             return array('data' => array(), 'total' => 0);
         }
     }


    /**
     * Fetches vectors from Pinecone using provided IDs
     */
     public function mxchat_fetch_vectors_by_ids($pinecone_options, $vector_ids) {
         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';

         if (empty($api_key) || empty($host) || empty($vector_ids)) {
             return array();
         }

         try {
             $fetch_url = "https://{$host}/vectors/fetch";

             // Pinecone fetch API allows fetching specific vectors by ID
             $fetch_data = array(
                 'ids' => array_values($vector_ids)
             );

             $response = wp_remote_post($fetch_url, array(
                 'headers' => array(
                     'Api-Key' => $api_key,
                     'Content-Type' => 'application/json'
                 ),
                 'body' => json_encode($fetch_data),
                 'timeout' => 30
             ));

             if (is_wp_error($response)) {
                 //error_log('Pinecone fetch by IDs error: ' . $response->get_error_message());
                 return array();
             }

             $response_code = wp_remote_retrieve_response_code($response);
             if ($response_code !== 200) {
                 //error_log('Pinecone fetch by IDs failed with code: ' . $response_code);
                 return array();
             }

             $body = wp_remote_retrieve_body($response);
             $data = json_decode($body, true);

             if (!isset($data['vectors'])) {
                 return array();
             }

             $converted_records = array();
             foreach ($data['vectors'] as $vector_id => $vector_data) {
                 $metadata = $vector_data['metadata'] ?? array();

                 $converted_records[] = (object) array(
                     'id' => $vector_id,
                     'article_content' => $metadata['text'] ?? '',
                     'source_url' => $metadata['source_url'] ?? '',
                     'created_at' => $metadata['created_at'] ?? $metadata['last_updated'] ?? time(),
                     'data_source' => 'pinecone'
                 );
             }

             return $converted_records;

         } catch (Exception $e) {
             //error_log('Pinecone fetch by IDs exception: ' . $e->getMessage());
             return array();
         }
     }


    /**
     * Fetches vectors from Pinecone using provided IDs (duplicate method)
     */
     public function fetch_pinecone_vectors_by_ids($pinecone_options, $vector_ids) {
         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';

         if (empty($api_key) || empty($host) || empty($vector_ids)) {
             return array();
         }

         try {
             $fetch_url = "https://{$host}/vectors/fetch";

             // Pinecone fetch API allows fetching specific vectors by ID
             $fetch_data = array(
                 'ids' => array_values($vector_ids)
             );

             $response = wp_remote_post($fetch_url, array(
                 'headers' => array(
                     'Api-Key' => $api_key,
                     'Content-Type' => 'application/json'
                 ),
                 'body' => json_encode($fetch_data),
                 'timeout' => 30
             ));

             if (is_wp_error($response)) {
                 //error_log('Pinecone fetch by IDs error: ' . $response->get_error_message());
                 return array();
             }

             $response_code = wp_remote_retrieve_response_code($response);
             if ($response_code !== 200) {
                 //error_log('Pinecone fetch by IDs failed with code: ' . $response_code);
                 return array();
             }

             $body = wp_remote_retrieve_body($response);
             $data = json_decode($body, true);

             if (!isset($data['vectors'])) {
                 return array();
             }

             $processed_data = array();

             foreach ($data['vectors'] as $vector_id => $vector_data) {
                 $metadata = $vector_data['metadata'] ?? array();
                 $source_url = $metadata['source_url'] ?? '';

                 if (!empty($source_url)) {
                     $post_id = url_to_postid($source_url);
                     if ($post_id) {
                         $created_at = $metadata['created_at'] ?? '';
                         $processed_date = 'Recently'; // Default

                         if (!empty($created_at)) {
                             $timestamp = is_numeric($created_at) ? $created_at : strtotime($created_at);
                             if ($timestamp) {
                                 $processed_date = human_time_diff($timestamp, current_time('timestamp')) . ' ago';
                             }
                         }

                         $processed_data[$post_id] = array(
                             'db_id' => $vector_id,
                             'processed_date' => $processed_date,
                             'url' => $source_url,
                             'source' => 'pinecone',
                             'timestamp' => $timestamp ?? current_time('timestamp')
                         );
                     }
                 }
             }

             return $processed_data;

         } catch (Exception $e) {
             //error_log('Pinecone fetch by IDs exception: ' . $e->getMessage());
             return array();
         }
     }


    // ========================================
    // PINECONE SCAN & SEARCH OPERATIONS
    // ========================================

    /**
     * Validates API credentials and returns empty array if invalid
     */
     private function mxchat_scan_pinecone_vectors($pinecone_options) {
         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';

         if (empty($api_key) || empty($host)) {
             return array();
         }

         try {
             // Instead of using a dummy zero vector, try multiple random vectors
             // to get better coverage of the vector space
             $all_matches = array();
             $seen_ids = array();

             // Try 3-5 different random vectors to get better coverage
             for ($i = 0; $i < 3; $i++) {
                 $query_url = "https://{$host}/query";

                 // Generate a random unit vector instead of zeros
                 $random_vector = array();
                 for ($j = 0; $j < 1536; $j++) {
                     $random_vector[] = (rand(-1000, 1000) / 1000.0); // Random values between -1 and 1
                 }

                 // Normalize the vector to unit length
                 $magnitude = sqrt(array_sum(array_map(function($x) { return $x * $x; }, $random_vector)));
                 if ($magnitude > 0) {
                     $random_vector = array_map(function($x) use ($magnitude) { return $x / $magnitude; }, $random_vector);
                 }

                 $query_data = array(
                     'includeMetadata' => true,
                     'includeValues' => false,
                     'topK' => 10000, // Get many results
                     'vector' => $random_vector
                 );

                 $response = wp_remote_post($query_url, array(
                     'headers' => array(
                         'Api-Key' => $api_key,
                         'Content-Type' => 'application/json'
                     ),
                     'body' => json_encode($query_data),
                     'timeout' => 30
                 ));

                 if (is_wp_error($response)) {
                     continue;
                 }

                 $body = wp_remote_retrieve_body($response);
                 $data = json_decode($body, true);

                 if (isset($data['matches'])) {
                     foreach ($data['matches'] as $match) {
                         $match_id = $match['id'] ?? '';
                         if (!empty($match_id) && !isset($seen_ids[$match_id])) {
                             $all_matches[] = $match;
                             $seen_ids[$match_id] = true;
                         }
                     }
                 }
             }

             // Convert matches to records
             $converted_records = array();
             $vector_ids_cache = array();

             foreach ($all_matches as $match) {
                 $metadata = $match['metadata'] ?? array();
                 $match_id = $match['id'] ?? '';

                 if (!empty($match_id)) {
                     $vector_ids_cache[] = $match_id;
                 }

                 $converted_records[] = (object) array(
                     'id' => $match_id,
                     'article_content' => $metadata['text'] ?? '',
                     'source_url' => $metadata['source_url'] ?? '',
                     'created_at' => $metadata['created_at'] ?? $metadata['last_updated'] ?? time(),
                     'data_source' => 'pinecone'
                 );
             }

             // Update the cache with found vector IDs for future use
             if (!empty($vector_ids_cache)) {
                 update_option('mxchat_pinecone_vector_ids_cache', $vector_ids_cache);
             }

             return $converted_records;

         } catch (Exception $e) {
             //error_log('Pinecone scan exception: ' . $e->getMessage());
             return array();
         }
     }


    /**
     * Validates API key and host, returns empty array if missing
     */
     private function mxchat_scan_pinecone_for_processed_content($pinecone_options) {
         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';

         if (empty($api_key) || empty($host)) {
             return array();
         }

         try {
             // Use multiple random vectors to get better coverage
             $all_matches = array();
             $seen_ids = array();

             // Try 3 different random vectors to get better coverage
             for ($i = 0; $i < 3; $i++) {
                 $query_url = "https://{$host}/query";

                 // Generate a random unit vector instead of zeros
                 $random_vector = array();
                 for ($j = 0; $j < 1536; $j++) {
                     $random_vector[] = (rand(-1000, 1000) / 1000.0); // Random values between -1 and 1
                 }

                 // Normalize the vector to unit length
                 $magnitude = sqrt(array_sum(array_map(function($x) { return $x * $x; }, $random_vector)));
                 if ($magnitude > 0) {
                     $random_vector = array_map(function($x) use ($magnitude) { return $x / $magnitude; }, $random_vector);
                 }

                 $query_data = array(
                     'includeMetadata' => true,
                     'includeValues' => false,
                     'topK' => 10000, // Get many results
                     'vector' => $random_vector
                 );

                 $response = wp_remote_post($query_url, array(
                     'headers' => array(
                         'Api-Key' => $api_key,
                         'Content-Type' => 'application/json'
                     ),
                     'body' => json_encode($query_data),
                     'timeout' => 30
                 ));

                 if (is_wp_error($response)) {
                     continue;
                 }

                 $response_code = wp_remote_retrieve_response_code($response);
                 if ($response_code !== 200) {
                     continue;
                 }

                 $body = wp_remote_retrieve_body($response);
                 $data = json_decode($body, true);

                 if (isset($data['matches'])) {
                     foreach ($data['matches'] as $match) {
                         $match_id = $match['id'] ?? '';
                         if (!empty($match_id) && !isset($seen_ids[$match_id])) {
                             $all_matches[] = $match;
                             $seen_ids[$match_id] = true;
                         }
                     }
                 }
             }

             // Convert matches to processed data format
             $processed_data = array();
             $vector_ids_for_cache = array();

             foreach ($all_matches as $match) {
                 $metadata = $match['metadata'] ?? array();
                 $source_url = $metadata['source_url'] ?? '';
                 $match_id = $match['id'] ?? '';

                 if (!empty($source_url) && !empty($match_id)) {
                     $post_id = url_to_postid($source_url);
                     if ($post_id) {
                         $created_at = $metadata['created_at'] ?? '';
                         $processed_date = 'Recently'; // Default

                         if (!empty($created_at)) {
                             $timestamp = is_numeric($created_at) ? $created_at : strtotime($created_at);
                             if ($timestamp) {
                                 $processed_date = human_time_diff($timestamp, current_time('timestamp')) . ' ago';
                             }
                         }

                         $processed_data[$post_id] = array(
                             'db_id' => $match_id,
                             'processed_date' => $processed_date,
                             'url' => $source_url,
                             'source' => 'pinecone',
                             'timestamp' => $timestamp ?? current_time('timestamp')
                         );

                         $vector_ids_for_cache[] = $match_id;
                     }
                 }
             }

             // Update the vector IDs cache for future use
             if (!empty($vector_ids_for_cache)) {
                 update_option('mxchat_pinecone_vector_ids_cache', $vector_ids_for_cache);
             }

             return $processed_data;

         } catch (Exception $e) {
             //error_log('Pinecone scan exception: ' . $e->getMessage());
             return array();
         }
     }


    // ========================================
    // PINECONE DELETE OPERATIONS
    // ========================================

    /**
     * Deletes data from Pinecone using provided API credentials
     */
     public function mxchat_delete_all_from_pinecone($pinecone_options) {
         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';

         if (empty($api_key) || empty($host)) {
             return array(
                 'success' => false,
                 'message' => 'Missing Pinecone API credentials'
             );
         }

         try {
             // First, get all vector IDs
             $all_vector_ids = array();

             // Try to get from cache first
             $cached_vector_ids = get_option('mxchat_pinecone_vector_ids_cache', array());
             if (!empty($cached_vector_ids)) {
                 $all_vector_ids = $cached_vector_ids;
             } else {
                 // Fallback: scan to get vector IDs
                 $records = $this->mxchat_scan_pinecone_vectors($pinecone_options);
                 foreach ($records as $record) {
                     if (!empty($record->id)) {
                         $all_vector_ids[] = $record->id;
                     }
                 }
             }

             if (empty($all_vector_ids)) {
                 return array(
                     'success' => true,
                     'message' => 'No vectors found to delete'
                 );
             }

             // Delete vectors in batches (Pinecone has limits on batch operations)
             $batch_size = 100;
             $batches = array_chunk($all_vector_ids, $batch_size);
             $deleted_count = 0;
             $failed_batches = 0;

             foreach ($batches as $batch) {
                 $result = $this->mxchat_delete_pinecone_batch($batch, $api_key, $host);
                 if ($result['success']) {
                     $deleted_count += count($batch);
                 } else {
                     $failed_batches++;
                     //error_log('Failed to delete Pinecone batch: ' . $result['message']);
                 }
             }

             if ($failed_batches > 0) {
                 return array(
                     'success' => false,
                     'message' => sprintf('Deleted %d vectors, but %d batches failed', $deleted_count, $failed_batches)
                 );
             }

             return array(
                 'success' => true,
                 'message' => "Successfully deleted {$deleted_count} vectors from Pinecone"
             );

         } catch (Exception $e) {
             //error_log('Pinecone delete all exception: ' . $e->getMessage());
             return array(
                 'success' => false,
                 'message' => $e->getMessage()
             );
         }
     }


    /**
     * Deletes batch of vectors from Pinecone database
     */
     private function mxchat_delete_pinecone_batch($vector_ids, $api_key, $host) {
         // Build the API endpoint
         $api_endpoint = "https://{$host}/vectors/delete";

         // Prepare the request body with the IDs
         $request_body = array(
             'ids' => $vector_ids
         );

         // Make the deletion request
         $response = wp_remote_post($api_endpoint, array(
             'headers' => array(
                 'Api-Key' => $api_key,
                 'accept' => 'application/json',
                 'content-type' => 'application/json'
             ),
             'body' => wp_json_encode($request_body),
             'timeout' => 60, // Increased timeout for batch operations
             'method' => 'POST'
         ));

         // Handle WordPress HTTP API errors
         if (is_wp_error($response)) {
             return array(
                 'success' => false,
                 'message' => $response->get_error_message()
             );
         }

         // Check response status
         $response_code = wp_remote_retrieve_response_code($response);
         $response_body = wp_remote_retrieve_body($response);

         // Pinecone returns 200 for successful deletion
         if ($response_code !== 200) {
             //error_log('Pinecone batch deletion failed: HTTP ' . $response_code . ' - ' . $response_body);
             return array(
                 'success' => false,
                 'message' => sprintf(
                     'Pinecone API error (HTTP %d): %s',
                     $response_code,
                     $response_body
                 )
             );
         }

         return array(
             'success' => true,
             'message' => 'Batch deleted successfully from Pinecone'
         );
     }


    /**
     * Deletes vector from Pinecone using API request
     */
     public function mxchat_delete_from_pinecone_by_vector_id($vector_id, $api_key, $host) {
         // Build the API endpoint
         $api_endpoint = "https://{$host}/vectors/delete";

         // Prepare the request body with just the ID
         $request_body = array(
             'ids' => array($vector_id)
         );

         // Make the deletion request
         $response = wp_remote_post($api_endpoint, array(
             'headers' => array(
                 'Api-Key' => $api_key,
                 'accept' => 'application/json',
                 'content-type' => 'application/json'
             ),
             'body' => wp_json_encode($request_body),
             'timeout' => 30,
             'method' => 'POST'
         ));

         // Handle WordPress HTTP API errors
         if (is_wp_error($response)) {
             return array(
                 'success' => false,
                 'message' => $response->get_error_message()
             );
         }

         // Check response status
         $response_code = wp_remote_retrieve_response_code($response);
         $response_body = wp_remote_retrieve_body($response);

         // Pinecone returns 200 for successful deletion
         if ($response_code !== 200) {
             //error_log('Pinecone deletion failed: HTTP ' . $response_code . ' - ' . $response_body);
             return array(
                 'success' => false,
                 'message' => sprintf(
                     'Pinecone API error (HTTP %d): %s',
                     $response_code,
                     $response_body
                 )
             );
         }

         // Parse response to check if it was successful
         $response_data = json_decode($response_body, true);

         // Log successful deletion
         //error_log('Pinecone vector ' . $vector_id . ' deleted successfully');

         return array(
             'success' => true,
             'message' => 'Vector deleted successfully from Pinecone'
         );
     }


    /**
     * Deletes data from Pinecone using a source URL
     */
     public function mxchat_delete_from_pinecone_by_url($source_url, $pinecone_options) {
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';
         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';

         if (empty($host) || empty($api_key)) {
             //error_log('MXChat: Pinecone deletion failed - missing configuration');
             return false;
         }

         $api_endpoint = "https://{$host}/vectors/delete";
         $vector_id = md5($source_url);

         $request_body = array(
             'ids' => array($vector_id)
         );

         $response = wp_remote_post($api_endpoint, array(
             'headers' => array(
                 'Api-Key' => $api_key,
                 'accept' => 'application/json',
                 'content-type' => 'application/json'
             ),
             'body' => wp_json_encode($request_body),
             'timeout' => 30
         ));

         if (is_wp_error($response)) {
             //error_log('MXChat: Pinecone deletion error - ' . $response->get_error_message());
             return false;
         }

         $response_code = wp_remote_retrieve_response_code($response);
         if ($response_code !== 200) {
             //error_log('MXChat: Pinecone deletion failed with status ' . $response_code);
             return false;
         }

         return true;
     }


    /**
     * Deletes data from Pinecone index using API key
     */
     private function mxchat_delete_from_pinecone($urls, $api_key, $environment, $index_name) {
         // Get the Pinecone host from options (matching your store_in_pinecone_main pattern)
         $options = get_option('mxchat_pinecone_addon_options');
         $host = $options['mxchat_pinecone_host'] ?? '';

         if (empty($host)) {
             return array(
                 'success' => false,
                 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
             );
         }

         // Build API endpoint using the configured host
         $api_endpoint = "https://{$host}/vectors/delete";

         // Create vector IDs from URLs (matching your store method's ID generation)
         $vector_ids = array_map('md5', $urls);

         // Prepare the delete request body
         $request_body = array(
             'ids' => $vector_ids,
             'filter' => array(
                 'source_url' => array(
                     '$in' => $urls
                 )
             )
         );

         // Make the deletion request
         $response = wp_remote_post($api_endpoint, array(
             'headers' => array(
                 'Api-Key' => $api_key,
                 'accept' => 'application/json',
                 'content-type' => 'application/json'
             ),
             'body' => wp_json_encode($request_body),
             'timeout' => 30,
             'data_format' => 'body'
         ));

         // Handle WordPress HTTP API errors
         if (is_wp_error($response)) {
             return array(
                 'success' => false,
                 'message' => $response->get_error_message()
             );
         }

         // Check response status
         $response_code = wp_remote_retrieve_response_code($response);
         if ($response_code !== 200) {
             $body = wp_remote_retrieve_body($response);
             return array(
                 'success' => false,
                 'message' => sprintf(
                     'Pinecone API error (HTTP %d): %s',
                     $response_code,
                     $body
                 )
             );
         }

         // Parse response body
         $body = wp_remote_retrieve_body($response);
         $response_data = json_decode($body, true);

         // Final validation of the response
         if (json_last_error() !== JSON_ERROR_NONE) {
             return array(
                 'success' => false,
                 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg()
             );
         }

         return array(
             'success' => true,
             'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids))
         );
     }


    // ========================================
    // VECTOR CACHE MANAGEMENT
    // ========================================


    /**
     * Removes vector ID from cache array option
     */
     public function mxchat_remove_from_pinecone_vector_cache($vector_id) {
         $cached_ids = get_option('mxchat_pinecone_vector_ids_cache', array());
         $key = array_search($vector_id, $cached_ids);
         if ($key !== false) {
             unset($cached_ids[$key]);
             update_option('mxchat_pinecone_vector_ids_cache', array_values($cached_ids));
         }
     }

    /**
     * Removes vector ID from processed content caches
     */
     public function mxchat_remove_from_processed_content_caches($vector_id) {
         // Get all caches
         $pinecone_cache = get_option('mxchat_pinecone_processed_cache', array());
         $processed_cache = get_option('mxchat_processed_content_cache', array());

         // We need to find the post ID that corresponds to this vector ID
         // Vector ID is typically md5 of the source URL
         $post_id_to_remove = null;

         // Search through caches to find matching post
         foreach ($pinecone_cache as $post_id => $cache_data) {
             if (isset($cache_data['db_id']) && $cache_data['db_id'] === $vector_id) {
                 $post_id_to_remove = $post_id;
                 break;
             }
         }

         // Also check the processed cache
         if (!$post_id_to_remove) {
             foreach ($processed_cache as $post_id => $cache_data) {
                 if (isset($cache_data['db_id']) && $cache_data['db_id'] === $vector_id) {
                     $post_id_to_remove = $post_id;
                     break;
                 }
             }
         }

         // If we found the post ID, remove it from both caches
         if ($post_id_to_remove) {
             unset($pinecone_cache[$post_id_to_remove]);
             unset($processed_cache[$post_id_to_remove]);

             update_option('mxchat_pinecone_processed_cache', $pinecone_cache);
             update_option('mxchat_processed_content_cache', $processed_cache);

             //error_log('Removed post ID ' . $post_id_to_remove . ' from processed content caches');
         } else {
             // If we can't find by vector ID, we might need to reconstruct the URL
             // and find the post ID that way
             //error_log('Could not find post ID for vector ID: ' . $vector_id);
         }
     }


    /**
     * Retrieves and caches Pinecone API processed content
     */
     public function mxchat_get_pinecone_processed_content($pinecone_options) {
         // First check local cache for immediate updates
         $cached_data = get_option('mxchat_pinecone_processed_cache', array());

         $api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
         $host = $pinecone_options['mxchat_pinecone_host'] ?? '';

         if (empty($api_key) || empty($host)) {
             // Return only cached data if API credentials are missing
             return $cached_data;
         }

         $pinecone_data = array();

         try {
             // Method 1: Try to get vectors using cached vector IDs first
             $cached_vector_ids = get_option('mxchat_pinecone_vector_ids_cache', array());

             if (!empty($cached_vector_ids)) {
                 $pinecone_data = $this->fetch_pinecone_vectors_by_ids($pinecone_options, $cached_vector_ids);
             }

             // Method 2: If no cached IDs or fetch failed, use scanning approach
             if (empty($pinecone_data)) {
                 $pinecone_data = $this->mxchat_scan_pinecone_for_processed_content($pinecone_options);
             }

             // Method 3: Final fallback - try stats endpoint (if available)
             if (empty($pinecone_data)) {
                 $stats_url = "https://{$host}/describe_index_stats";

                 $response = wp_remote_post($stats_url, array(
                     'headers' => array(
                         'Api-Key' => $api_key,
                         'Content-Type' => 'application/json'
                     ),
                     'body' => json_encode(array()),
                     'timeout' => 30
                 ));

                 if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
                     $body = wp_remote_retrieve_body($response);
                     $stats_data = json_decode($body, true);

                     // Log stats for debugging but don't rely on them for vector listing
                     //error_log('Pinecone index stats: ' . print_r($stats_data, true));
                 }
             }

         } catch (Exception $e) {
             //error_log('Pinecone processed content exception: ' . $e->getMessage());
         }

         // Merge cached data with Pinecone data
         // Cache takes priority for recent updates (within last 5 minutes)
         $merged_data = $pinecone_data;

         foreach ($cached_data as $post_id => $cache_item) {
             $cache_timestamp = $cache_item['timestamp'] ?? 0;
             $time_diff = current_time('timestamp') - $cache_timestamp;

             // If cache item is recent (less than 5 minutes), prioritize it
             if ($time_diff < 300) { // 5 minutes = 300 seconds
                 $merged_data[$post_id] = $cache_item;
             } else {
                 // If not in Pinecone data and cache is old, keep cache but mark as potentially stale
                 if (!isset($merged_data[$post_id])) {
                     $merged_data[$post_id] = $cache_item;
                 }
             }
         }

         return $merged_data;
     }


    // ========================================
    // HELPER METHODS
    // ========================================

    /**
     * Validates Pinecone API credentials
     */
    private function mxchat_validate_pinecone_credentials($api_key, $host) {
        if (empty($api_key) || empty($host)) {
            return false;
        }
        return true;
    }

    /**
     * Get Pinecone API credentials from options
     */
    private function mxchat_get_pinecone_credentials() {
        $options = get_option('mxchat_options', array());
        return array(
            'api_key' => isset($options['pinecone_api_key']) ? $options['pinecone_api_key'] : '',
            'host' => isset($options['pinecone_host']) ? $options['pinecone_host'] : ''
        );
    }

    /**
     * Log Pinecone operation errors
     */
    private function log_pinecone_error($operation, $error_message) {
        //error_log("MxChat Pinecone {$operation} Error: " . $error_message);
    }

    // ========================================
    // STATIC ACCESS METHODS (for backward compatibility)
    // ========================================

    /**
     * Get singleton instance
     */
    public static function get_instance() {
        static $instance = null;
        if ($instance === null) {
            $instance = new self();
        }
        return $instance;
    }
}

// Initialize the Pinecone manager
$mxchat_pinecone_manager = MxChat_Pinecone_Manager::get_instance();

```
