HEX
Server: nginx/1.18.0
System: Linux test-ipsremont 5.4.0-214-generic #234-Ubuntu SMP Fri Mar 14 23:50:27 UTC 2025 x86_64
User: ips (1000)
PHP: 8.0.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/html/laravel/app/Models/NotamGptRequest.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use JsonSchema\Validator;

class NotamGptRequest extends Model
{

    protected $table = 'notam_gpt_requests';
    protected $fillable = [
        'input',
        'output',
        'chatgpt_response',
        'input_tokens',
        'output_tokens',
        'total_tokens',
        'prompt_id',
        'match_percents',
        'model_name',
        'examples_number',
        'gpt_processing_time',
        'input_hash',
        'is_valid',
    ];

    public function process()
    {
        try {
            $prompt = Prompt::query()->find($this->prompt_id);
            $promptText = $prompt->prompt . "\n" . $prompt->json;

            $initTime = microtime(true);
            $response = callGptProxi([
                'input' => $this->input,
                'key' => env('OPEN_AI_KEY'),
                'prompt' => $promptText,
                'model' => $this->model_name,
                'temperature' => (float) $prompt->temperature,
                'max_tokens' => 0,
            ], 'notamNew');

            $endTime = microtime(true);
            $executionTime = $endTime - $initTime;

            // Gpt Model result
            if (property_exists($response, 'choices')) {
                $usage = (array) $response->usage;
                $iniMessage = $response->choices[0]->message->content ?? '';
                $message = Notam::cleanJson($iniMessage);

                if (!$message) {
                    $message = $iniMessage;
                }

                $message = Notam::prepareJson($message);

                $messageData = json_decode($message);

                if (is_object($messageData)) {
                    if (!property_exists($messageData, 'CheckNeeded') || !$messageData->CheckNeeded) {
                        $messageData->CheckNeeded = true;
                        $message = json_encode($messageData);
                    }
                }

                $this->output = $message;
                $this->chatgpt_response = json_encode($response);
                $this->input_tokens = $usage['prompt_tokens'];
                $this->output_tokens = $usage['completion_tokens'];
                $this->total_tokens = $usage['total_tokens'];
                $this->gpt_processing_time = $executionTime;

                $notam = new Notam();
                $validationErrors = $notam->validateDataFormat($message);
                if (!count($validationErrors)
                    || (count($validationErrors) == 1 && substr_count($validationErrors[0], 'CheckNeeded'))) {
                    $this->is_valid = 1;
                } else {
                    $this->is_valid = 0;
                }
                $this->save();

                $this->compareResults();
            }
        } catch (\Exception $exception) {
            echo $exception->getMessage();
        }
    }

    public function compareResults()
    {
        $hash = $this->input_hash;

        $notamsBatch = NotamGptRequest::query()
            ->where('input_hash', $hash)
            ->get();

        $canCompare = true;
        foreach ($notamsBatch as $notam) {
            if (!$notam->total_tokens) { // Can compare only when GPT result exist for all notams
                $canCompare = false;
                break;
            }
        }

        if ($canCompare) {
            foreach ($notamsBatch as $notam) {
                $matchPercents = [];
                    foreach ($notamsBatch as $notam2) {
                        if ($notam2->id !== $notam->id) {
                            $matchPercents[$notam2->model_name] = $this->compareGptResponses($notam->output, $notam2->output);
                        }
                    }

                    $notam->match_percents = json_encode($matchPercents);
                    $notam->save();
            }
        }


    }

    public static function calculateGptCost($model, $inputTokens, $outputTokens)
    {
        $allCosts = [
            'gpt-5.2' => ['input' => 1.75, 'output' => 14.00],
            'gpt-5.1' => ['input' => 1.25, 'output' => 10.00],
            'gpt-5' => ['input' => 1.25, 'output' => 10.00],
            'gpt-5-mini' => ['input' => 0.25, 'output' => 2.00],
            'gpt-5-nano' => ['input' => 0.05, 'output' => 0.40],
            'gpt-5.2-chat-latest' => ['input' => 1.75, 'output' => 14.00],
            'gpt-5.1-chat-latest' => ['input' => 1.25, 'output' => 10.00],
            'gpt-5-chat-latest' => ['input' => 1.25, 'output' => 10.00],
            'gpt-5.1-codex-max' => ['input' => 1.25, 'output' => 10.00],
            'gpt-5.1-codex' => ['input' => 1.25, 'output' => 10.00],
            'gpt-5-codex' => ['input' => 1.25, 'output' => 10.00],
            'gpt-5.2-pro' => ['input' => 21.00, 'output' => 168.00],
            'gpt-5-pro' => ['input' => 15.00, 'output' => 120.00],
            'gpt-4.1' => ['input' => 2.00, 'output' => 8.00],
            'gpt-4.1-mini' => ['input' => 0.40, 'output' => 1.60],
            'gpt-4.1-nano' => ['input' => 0.10, 'output' => 0.40],
            'gpt-4o' => ['input' => 2.50, 'output' => 10.00],
            'gpt-4o-2024-05-13' => ['input' => 5.00, 'output' => 15.00],
            'gpt-4o-mini' => ['input' => 0.15, 'output' => 0.60],
        ];

        if ($model && $inputTokens !== null && $outputTokens !== null) {
            $modelKey = strtolower($model);

            $foundCost = [];
            foreach ($allCosts as $key => $cost) {
                if (substr_count($modelKey, $key)) {
                    $foundCost = $cost;
                }
            }

            if (!empty($foundCost)) {
                $costPerMillionInput = $foundCost['input'];
                $costPerMillionOutput = $foundCost['output'];

                $inputCost = round(($inputTokens / 1000000) * $costPerMillionInput, 6);
                $outputCost = round(($outputTokens / 1000000) * $costPerMillionOutput, 6);

                return $inputCost + $outputCost;
            }
        }

        return 0;

    }


    public function getStats()
    {
        $stats = NotamGptRequest::query()
            ->select('model_name', DB::raw('sum(is_valid) as total'), DB::raw('sum(gpt_processing_time)/count(gpt_processing_time) as gpt_processing_time'))
            ->groupBy('model_name')
            ->get();

        $allPercentMatch = [];
        $validPercentMatch = [];

        $allNotams = NotamGptRequest::query()
                        ->whereNotNull('chatgpt_response')
                        ->select('model_name', 'match_percents', 'is_valid')
                        ->get();
        foreach ($allNotams as $notam) {
            $notam->match_percents = json_decode($notam->match_percents);
            if (!$notam->match_percents) continue;
            foreach ($notam->match_percents as $model => $matchPercent) {
                if (!substr_count($model, '4o-mini')) continue;
                if (empty($allPercentMatch[$notam->model_name])) {
                    $allPercentMatch[$notam->model_name] = [];
                }
                if (empty($validPercentMatch[$notam->model_name])) {
                    $validPercentMatch[$notam->model_name] = [];
                }

                $allPercentMatch[$notam->model_name][] = $matchPercent;
                if ($notam->is_valid) {
                    $validPercentMatch[$notam->model_name][] = $matchPercent;
                }
            }
        }

        foreach ($allPercentMatch as $model => $matchPercents) {
            if (!count($matchPercents)) continue;
            $allPercentMatch[$model] = array_sum($matchPercents)/count($matchPercents);
        }
        foreach ($validPercentMatch as $model => $matchPercents) {
            if (!count($matchPercents)) continue;
            $validPercentMatch[$model] = array_sum($matchPercents)/count($matchPercents);
        }

        foreach ($stats as $stat) {
            $stat['allPercentMatch'] = $allPercentMatch[$stat->model_name] ?? 0;
            $stat['validPercentMatch'] = $validPercentMatch[$stat->model_name] ?? 0;
        }

        return $stats;
    }

    public function compareGptResponses($gptResponse, $gptResponse2)
    {
        $matchPercent = 0;

        try {
            if (!$gptResponse || !$gptResponse2) {
                echo 'NO INPUT'; die;
                return $matchPercent;
            }

            $gptResponse = Notam::cleanJson($this->prepareJson($this->reachOutput($gptResponse)));
            $localModelResponse = Notam::cleanJson($this->prepareJson($this->reachOutput($gptResponse2)));

            if ($gptResponse !== $localModelResponse) {
                similar_text(
                    $gptResponse,
                    $localModelResponse,
                    $matchPercent,
                );
            } else {
                $matchPercent = 100;
            }

        } catch (\Exception $e) {
            echo $e->getMessage();die;
            $matchPercent = 0;
        }

        return floor($matchPercent);
    }

    public static function prepareJson($jsonString): string
    {
        // Removes } from strings like :"something}
        $jsonString = preg_replace('/(Line":")([^{}]*)\},/', '$1$2,', $jsonString);
        $jsonString = str_replace('”', '\"', $jsonString);
        $jsonArray = json_decode($jsonString, true);
        if (is_array($jsonArray)) {
            Notam::formatJson($jsonArray);
            Notam::sortJson($jsonArray);

            return json_encode($jsonArray, JSON_UNESCAPED_UNICODE);
        }

        return $jsonString ?? '';
    }

    public function reachOutput($correctedResponse = false): string|false
    {
        if ($correctedResponse === false) {
            $correctedResponse = $this->output;
        }

        $output = json_decode(self::prepareJson($correctedResponse));

        $output = [
            'CheckNeeded' => $output->CheckNeeded ?? true,
            'A_Line' => $output->A_Line ?? null,
            'B_Line' => $output->B_Line ?? null,
            'C_Line' => $output->C_Line ?? null,
            'D_Line' => $output->D_Line ?? null,
            'E_Line' => $output->E_Line ?? null,
            'F_Line' => $output->F_Line ?? null,
            'G_Line' => $output->G_Line ?? null,
        ];

        foreach ($output as $key => $item) {
            if (empty($item) && !substr_count($key, 'Line') && !substr_count($key, 'CheckNeeded')) {
                unset($output[$key]);
            }
        }

        return json_encode($output, JSON_UNESCAPED_UNICODE);
    }

}