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);
}
}