/home/edulekha/crm.edulekha.com/modules/openai/libraries/Fine_tuner.php
<?php

use app\services\ai\AiProviderRegistry;
use Perfexcrm\Openai\OpenAiProvider;

defined('BASEPATH') or exit('No direct script access allowed');

class Fine_tuner
{
    /**
     * CodeIgniter instance.
     */
    private $ci;

    /**
     * Fine_tuner constructor.
     */
    public function __construct()
    {
        $this->ci = &get_instance();
        $this->ci->load->model('knowledge_base_model');
    }

    /**
     * Get all knowledge base articles for fine-tuning.
     */
    public function getKnowledgeBaseData(): array
    {
        if (! hooks()->apply_filters('openai_fine_tuning_knowledge_base', true)) {
            return [];
        }

        // Get all active and non internal knowledge base articles
        $this->ci->db->where('active', 1);
        $this->ci->db->where('staff_article', 0);
        $articles = $this->ci->db->get('knowledge_base')->result_array();

        return array_map(function ($article) {
            return [
                'title'   => $article['subject'],
                'content' => $this->formatArticleContent($article),
            ];
        }, $articles);
    }

    /**
     * Get all predefined replies for fine-tuning.
     */
    public function getPredefinedRepliesData(): array
    {
        if (! hooks()->apply_filters('openai_fine_tuning_predefined_replies', true)) {
            return [];
        }

        $replies = $this->ci->db->get('tickets_predefined_replies')->result_array();

        return array_map(function ($reply) {
            return [
                'title'   => $reply['name'],
                'content' => $this->formatPredefinedReplyContent($reply),
            ];
        }, $replies);
    }

    /**
     * Format article content for fine-tuning.
     */
    private function formatArticleContent(array $article): string
    {
        // Strip HTML tags from the content but preserve structure where possible
        $content = strip_tags($article['description'], '<p><br><a><ul><ol><li><h1><h2><h3><h4><h5>');

        // Add the title at the beginning
        $formattedContent = '# ' . $article['subject'] . "\n\n";

        // Add description
        $formattedContent .= $content;

        // Add more info url
        $url = site_url('knowledge_base/article/' . $article['slug']);
        $formattedContent .= "\n\nFor more information, visit: " . $url;

        return $formattedContent;
    }

    /**
     * Format predefined reply for fine-tuning.
     */
    private function formatPredefinedReplyContent(array $article): string
    {
        // Strip HTML tags from the content but preserve structure where possible
        $content = strip_tags($article['message'], '<p><br><a><ul><ol><li><h1><h2><h3><h4><h5>');

        // Add the title at the beginning
        $formattedContent = '# ' . $article['name'] . "\n\n";

        // Add description
        $formattedContent .= $content;

        return $formattedContent;
    }

    /**
     * Start the fine-tuning process with knowledge base data.
     */
    public function startFineTuning(): array
    {
        $knowledgeBaseData     = $this->getKnowledgeBaseData();
        $predefinedRepliesData = $this->getPredefinedRepliesData();

        if (empty($knowledgeBaseData) && empty($predefinedRepliesData)) {
            return [
                'success' => false,
                'message' => 'Not enough data found for fine-tuning.',
            ];
        }

        /** @var OpenAiProvider */
        $provider = AiProviderRegistry::getProvider('openai');

        // Start the fine-tuning job
        $jobId = $provider->createFineTuningJob([...$knowledgeBaseData, ...$predefinedRepliesData]);

        if (! $jobId) {
            return [
                'success' => false,
                'message' => 'Failed to create fine-tuning job. Check logs for more details.',
            ];
        }

        return [
            'success' => true,
            'message' => 'Fine-tuning job started successfully.',
            'job_id'  => $jobId,
        ];
    }

    /**
     * Check the status of a fine-tuning job.
     */
    public function checkFineTuningStatus(?string $jobId = null): array
    {
        if (! $jobId) {
            $jobId = get_option('openai_fine_tuning_last_job_id');
        }

        if (! $jobId) {
            return [
                'success' => false,
                'message' => 'No fine-tuning job ID found.',
            ];
        }

        /** @var OpenAiProvider */
        $provider = AiProviderRegistry::getProvider('openai');

        // Check job status
        $status = $provider->checkFineTuningStatus($jobId);

        if (isset($status['status']) && $status['status'] === 'error') {
            return [
                'success' => false,
                'message' => 'Error checking fine-tuning job status: ' . ($status['error'] ?? 'Unknown error'),
            ];
        }

        return [
            'success' => true,
            'data'    => $status,
        ];
    }
}