2025-09-26 10:43:05 +03:00
|
|
|
|
<?php
|
|
|
|
|
|
// aiassist/vectorgpt.php
|
|
|
|
|
|
require_once 'config.php';
|
|
|
|
|
|
require_once 'logger.php';
|
|
|
|
|
|
|
|
|
|
|
|
function createVectorStore() {
|
|
|
|
|
|
$curl = curl_init();
|
|
|
|
|
|
$payload = json_encode(['name' => 'Vector Store']);
|
|
|
|
|
|
logMessage("Создание Vector Store. Отправляем payload: " . $payload);
|
|
|
|
|
|
curl_setopt_array($curl, [
|
|
|
|
|
|
CURLOPT_URL => OPENAI_VECTOR_STORES_API,
|
|
|
|
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
|
CURLOPT_POST => true,
|
|
|
|
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
|
|
|
|
CURLOPT_HTTPHEADER => [
|
|
|
|
|
|
'Content-Type: application/json',
|
|
|
|
|
|
'Authorization: Bearer ' . OPENAI_API_KEY,
|
|
|
|
|
|
'OpenAI-Beta: assistants=v2'
|
|
|
|
|
|
]
|
|
|
|
|
|
]);
|
|
|
|
|
|
$response = curl_exec($curl);
|
|
|
|
|
|
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
|
|
|
|
$curlError = curl_error($curl);
|
|
|
|
|
|
curl_close($curl);
|
|
|
|
|
|
logMessage("Ответ OpenAI (создание Vector Store): HTTP $httpCode - " . $response);
|
|
|
|
|
|
if ($curlError) {
|
|
|
|
|
|
logMessage("Ошибка cURL при создании Vector Store: " . $curlError);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
$decoded = json_decode($response, true);
|
|
|
|
|
|
if ($httpCode !== 200 || !isset($decoded['id'])) {
|
|
|
|
|
|
logMessage("Ошибка при создании Vector Store: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
return $decoded['id'];
|
|
|
|
|
|
}
|
|
|
|
|
|
function uploadFileToOpenAI($filePath) {
|
|
|
|
|
|
if (!file_exists($filePath) || filesize($filePath) == 0) {
|
|
|
|
|
|
logMessage("❌ Ошибка: Файл не найден или пустой! $filePath");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logMessage("📤 Загружаем файл в OpenAI: $filePath");
|
|
|
|
|
|
logMessage("🔑 Используем API-ключ: " . substr(OPENAI_API_KEY, 0, 8) . "********");
|
|
|
|
|
|
|
|
|
|
|
|
$curl = curl_init();
|
|
|
|
|
|
curl_setopt_array($curl, [
|
|
|
|
|
|
CURLOPT_URL => OPENAI_FILES_API,
|
|
|
|
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
|
CURLOPT_POST => true,
|
|
|
|
|
|
CURLOPT_POSTFIELDS => [
|
|
|
|
|
|
'file' => new CURLFile($filePath),
|
|
|
|
|
|
'purpose' => 'assistants'
|
|
|
|
|
|
],
|
|
|
|
|
|
CURLOPT_HTTPHEADER => [
|
|
|
|
|
|
"Authorization: Bearer " . OPENAI_API_KEY,
|
2025-10-16 11:17:21 +03:00
|
|
|
|
"OpenAI-Beta: assistants=v2"
|
|
|
|
|
|
// Content-Type: multipart/form-data автоматически добавляется cURL с правильным boundary
|
2025-09-26 10:43:05 +03:00
|
|
|
|
]
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$response = curl_exec($curl);
|
|
|
|
|
|
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
|
|
|
|
$curlError = curl_error($curl);
|
|
|
|
|
|
curl_close($curl);
|
|
|
|
|
|
|
|
|
|
|
|
logMessage("📡 Ответ OpenAI (загрузка файла): HTTP $httpCode - " . $response);
|
|
|
|
|
|
|
|
|
|
|
|
if ($curlError) {
|
|
|
|
|
|
logMessage("❌ Ошибка cURL при загрузке файла: " . $curlError);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$decoded = json_decode($response, true);
|
|
|
|
|
|
if ($httpCode !== 200 || !isset($decoded['id'])) {
|
|
|
|
|
|
logMessage("❌ Ошибка при загрузке файла в OpenAI: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logMessage("✅ Файл успешно загружен в OpenAI (ID: " . $decoded['id'] . ")");
|
|
|
|
|
|
return $decoded['id'];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*function uploadFileToOpenAI($filePath) {
|
|
|
|
|
|
logMessage("Загрузка файла в OpenAI: $filePath");
|
|
|
|
|
|
$curl = curl_init();
|
|
|
|
|
|
curl_setopt_array($curl, [
|
|
|
|
|
|
CURLOPT_URL => OPENAI_FILES_API,
|
|
|
|
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
|
CURLOPT_POST => true,
|
|
|
|
|
|
CURLOPT_POSTFIELDS => [
|
|
|
|
|
|
'file' => new CURLFile($filePath),
|
|
|
|
|
|
'purpose' => 'assistants'
|
|
|
|
|
|
],
|
|
|
|
|
|
CURLOPT_HTTPHEADER => [
|
|
|
|
|
|
'Authorization: Bearer ' . OPENAI_API_KEY,
|
|
|
|
|
|
'OpenAI-Beta: assistants=v2'
|
|
|
|
|
|
]
|
|
|
|
|
|
]);
|
|
|
|
|
|
$response = curl_exec($curl);
|
|
|
|
|
|
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
|
|
|
|
$curlError = curl_error($curl);
|
|
|
|
|
|
curl_close($curl);
|
|
|
|
|
|
logMessage("Ответ OpenAI (загрузка файла): HTTP $httpCode - " . $response);
|
|
|
|
|
|
if ($curlError) {
|
|
|
|
|
|
logMessage("Ошибка cURL при загрузке файла: " . $curlError);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
$decoded = json_decode($response, true);
|
|
|
|
|
|
if ($httpCode !== 200 || !isset($decoded['id'])) {
|
|
|
|
|
|
logMessage("Ошибка при загрузке файла: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
return $decoded['id'];
|
|
|
|
|
|
}
|
|
|
|
|
|
*/
|
|
|
|
|
|
function addFileToVectorStore($vectorStoreId, $fileId) {
|
|
|
|
|
|
$curl = curl_init();
|
|
|
|
|
|
$payload = json_encode(['file_id' => $fileId]);
|
|
|
|
|
|
logMessage("Добавление файла в Vector Store. Payload: " . $payload);
|
|
|
|
|
|
curl_setopt_array($curl, [
|
|
|
|
|
|
CURLOPT_URL => OPENAI_VECTOR_STORES_API . "/$vectorStoreId/files",
|
|
|
|
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
|
CURLOPT_POST => true,
|
|
|
|
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
|
|
|
|
CURLOPT_HTTPHEADER => [
|
|
|
|
|
|
'Content-Type: application/json',
|
|
|
|
|
|
'Authorization: Bearer ' . OPENAI_API_KEY,
|
|
|
|
|
|
'OpenAI-Beta: assistants=v2'
|
|
|
|
|
|
]
|
|
|
|
|
|
]);
|
|
|
|
|
|
$response = curl_exec($curl);
|
|
|
|
|
|
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
|
|
|
|
$curlError = curl_error($curl);
|
|
|
|
|
|
curl_close($curl);
|
|
|
|
|
|
logMessage("Ответ OpenAI (добавление файла): HTTP $httpCode - " . $response);
|
|
|
|
|
|
if ($curlError) {
|
|
|
|
|
|
logMessage("Ошибка cURL при добавлении файла в Vector Store: " . $curlError);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
$decoded = json_decode($response, true);
|
|
|
|
|
|
if ($httpCode !== 200 || !isset($decoded['id'])) {
|
|
|
|
|
|
logMessage("Ошибка добавления файла: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateAssistantWithVectorStore($vectorStoreId) {
|
|
|
|
|
|
$data = [
|
|
|
|
|
|
'tool_resources' => [
|
|
|
|
|
|
'file_search' => [
|
|
|
|
|
|
'vector_store_ids' => [$vectorStoreId]
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
$curl = curl_init();
|
|
|
|
|
|
$payload = json_encode($data);
|
|
|
|
|
|
logMessage("Обновление ассистента. Payload: " . $payload);
|
|
|
|
|
|
curl_setopt_array($curl, [
|
|
|
|
|
|
CURLOPT_URL => OPENAI_ASSISTANT_API . "/" . ASSISTANT_ID,
|
|
|
|
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
|
CURLOPT_CUSTOMREQUEST => 'POST',
|
|
|
|
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
|
|
|
|
CURLOPT_HTTPHEADER => [
|
|
|
|
|
|
'Content-Type: application/json',
|
|
|
|
|
|
'Authorization: Bearer ' . OPENAI_API_KEY,
|
|
|
|
|
|
'OpenAI-Beta: assistants=v2'
|
|
|
|
|
|
]
|
|
|
|
|
|
]);
|
|
|
|
|
|
$response = curl_exec($curl);
|
|
|
|
|
|
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
|
|
|
|
$curlError = curl_error($curl);
|
|
|
|
|
|
curl_close($curl);
|
|
|
|
|
|
logMessage("Ответ OpenAI (обновление ассистента): HTTP $httpCode - " . $response);
|
|
|
|
|
|
if ($curlError) {
|
|
|
|
|
|
logMessage("Ошибка обновления ассистента: " . $curlError);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
$decoded = json_decode($response, true);
|
|
|
|
|
|
if ($httpCode !== 200 || !isset($decoded['id'])) {
|
|
|
|
|
|
logMessage("Ошибка обновления ассистента: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
function getTextEmbedding($text) {
|
|
|
|
|
|
$command = "sudo -u fastuser /var/www/laws/legal/bin/python3 /var/www/laws/legal/vectorize.py";
|
|
|
|
|
|
|
|
|
|
|
|
$descriptors = [
|
|
|
|
|
|
0 => ["pipe", "r"], // stdin (передаем текст)
|
|
|
|
|
|
1 => ["pipe", "w"], // stdout (читаем результат)
|
|
|
|
|
|
2 => ["pipe", "w"] // stderr (читаем ошибки)
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// Логируем команду перед её выполнением
|
|
|
|
|
|
file_put_contents(__DIR__ . '/logs/vectorizer_debug.log',
|
|
|
|
|
|
date('Y-m-d H:i:s') . " - Executing command: $command\n", FILE_APPEND);
|
|
|
|
|
|
|
|
|
|
|
|
$process = proc_open($command, $descriptors, $pipes);
|
|
|
|
|
|
|
|
|
|
|
|
if (!is_resource($process)) {
|
|
|
|
|
|
error_log("Ошибка запуска Python через proc_open: $command");
|
|
|
|
|
|
return [
|
|
|
|
|
|
"embedding_1024" => array_fill(0, 1024, 0.001),
|
|
|
|
|
|
"embedding_2048" => array_fill(0, 2048, 0.001)
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Передаем текст в stdin
|
|
|
|
|
|
fwrite($pipes[0], $text);
|
|
|
|
|
|
fclose($pipes[0]);
|
|
|
|
|
|
|
|
|
|
|
|
// Читаем результат из stdout
|
|
|
|
|
|
$output = stream_get_contents($pipes[1]);
|
|
|
|
|
|
fclose($pipes[1]);
|
|
|
|
|
|
|
|
|
|
|
|
// Читаем stderr (ошибки)
|
|
|
|
|
|
$errorOutput = stream_get_contents($pipes[2]);
|
|
|
|
|
|
fclose($pipes[2]);
|
|
|
|
|
|
|
|
|
|
|
|
proc_close($process);
|
|
|
|
|
|
|
|
|
|
|
|
// Логируем результат выполнения Python
|
|
|
|
|
|
file_put_contents(__DIR__ . '/logs/vectorizer_debug.log',
|
|
|
|
|
|
date('Y-m-d H:i:s') . " - Raw output: " . $output . "\n", FILE_APPEND);
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($errorOutput)) {
|
|
|
|
|
|
file_put_contents(__DIR__ . '/logs/vectorizer_debug.log',
|
|
|
|
|
|
date('Y-m-d H:i:s') . " - Python error: " . $errorOutput . "\n", FILE_APPEND);
|
|
|
|
|
|
return [
|
|
|
|
|
|
"embedding_1024" => array_fill(0, 1024, 0.001),
|
|
|
|
|
|
"embedding_2048" => array_fill(0, 2048, 0.001)
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$decoded = json_decode($output, true);
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
"embedding_1024" => $decoded["embedding_1024"] ?? array_fill(0, 1024, 0.001),
|
|
|
|
|
|
"embedding_2048" => $decoded["embedding_2048"] ?? array_fill(0, 2048, 0.001)
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function logVectorStoreFiles($vectorStoreId) {
|
|
|
|
|
|
$url = OPENAI_VECTOR_STORES_API . '/' . $vectorStoreId . '/files';
|
|
|
|
|
|
|
|
|
|
|
|
$ch = curl_init();
|
|
|
|
|
|
curl_setopt_array($ch, [
|
|
|
|
|
|
CURLOPT_URL => $url,
|
|
|
|
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
|
CURLOPT_HTTPHEADER => [
|
|
|
|
|
|
"Content-Type: application/json",
|
|
|
|
|
|
"Authorization: Bearer " . OPENAI_API_KEY,
|
|
|
|
|
|
"OpenAI-Beta: assistants=v2" // добавляем заголовок для использования Assistants v2
|
|
|
|
|
|
]
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$response = curl_exec($ch);
|
|
|
|
|
|
|
|
|
|
|
|
// Проверка ошибок cURL
|
|
|
|
|
|
if (curl_errno($ch)) {
|
|
|
|
|
|
logMessage("❌ Ошибка cURL: " . curl_error($ch));
|
|
|
|
|
|
curl_close($ch);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
curl_close($ch);
|
|
|
|
|
|
|
|
|
|
|
|
// Проверка на ошибки декодирования JSON
|
|
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
|
|
|
|
logMessage("❌ Ошибка при декодировании JSON: " . json_last_error_msg());
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Проверка, что данные о файлах действительно получены
|
|
|
|
|
|
if (isset($data['data'])) {
|
|
|
|
|
|
logMessage("📦 Содержимое Vector Store ($vectorStoreId):");
|
|
|
|
|
|
|
|
|
|
|
|
// Логируем данные по каждому файлу в Vector Store
|
|
|
|
|
|
foreach ($data['data'] as $file) {
|
|
|
|
|
|
$id = $file['id'] ?? '???';
|
|
|
|
|
|
$filename = $file['filename'] ?? 'без имени';
|
|
|
|
|
|
$bytes = $file['bytes'] ?? 0;
|
|
|
|
|
|
$status = $file['status'] ?? 'неизвестно';
|
|
|
|
|
|
|
|
|
|
|
|
// Логируем информацию о файле
|
|
|
|
|
|
logMessage("📄 [$id] $filename | Статус: $status | Размер: " . round($bytes / 1024, 1) . " КБ");
|
|
|
|
|
|
|
|
|
|
|
|
// Если файл проиндексирован, извлекаем первые 400 символов текста
|
|
|
|
|
|
if ($status === 'completed') {
|
|
|
|
|
|
$text = extractTextFromFile($filename); // извлекаем текст из файла
|
|
|
|
|
|
$fragment = mb_substr($text, 0, 400, 'UTF-8');
|
|
|
|
|
|
logMessage("📜 Фрагмент из $filename: $fragment...");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logMessage("⚠️ Файл не проиндексирован или текст не доступен: $filename");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logMessage("❌ Не удалось получить файлы из Vector Store ID: $vectorStoreId");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function checkFileStatus($vectorStoreId) {
|
|
|
|
|
|
// $url = "https://api.proxyapi.ru/openai/v1/vector_stores/$vectorStoreId/files";
|
|
|
|
|
|
$url = OPENAI_VECTOR_STORES_API . '/' . $vectorStoreId . '/files';
|
|
|
|
|
|
$ch = curl_init();
|
|
|
|
|
|
curl_setopt_array($ch, [
|
|
|
|
|
|
CURLOPT_URL => $url,
|
|
|
|
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
|
|
|
|
CURLOPT_HTTPHEADER => [
|
|
|
|
|
|
"Content-Type: application/json",
|
|
|
|
|
|
"Authorization: Bearer " . OPENAI_API_KEY,
|
|
|
|
|
|
"OpenAI-Beta: assistants=v2" // Этот заголовок нужно добавить!
|
|
|
|
|
|
]
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$response = curl_exec($ch);
|
|
|
|
|
|
curl_close($ch);
|
|
|
|
|
|
|
|
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
|
|
if (isset($data['data'])) {
|
|
|
|
|
|
foreach ($data['data'] as $file) {
|
|
|
|
|
|
$id = $file['id'] ?? '???';
|
|
|
|
|
|
$filename = $file['filename'] ?? 'без имени';
|
|
|
|
|
|
$status = $file['status'] ?? 'неизвестно';
|
|
|
|
|
|
logMessage("📄 Файл: $filename | Статус: $status");
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logMessage("❌ Ошибка при получении статуса файлов из Vector Store.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
function getTextEmbedding($text) {
|
|
|
|
|
|
$command = "sudo -u fastuser /var/www/laws/legal/bin/python3 /var/www/laws/legal/vectorize.py";
|
|
|
|
|
|
|
|
|
|
|
|
$descriptors = [
|
|
|
|
|
|
0 => ["pipe", "r"],
|
|
|
|
|
|
1 => ["pipe", "w"],
|
|
|
|
|
|
2 => ["pipe", "w"]
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
$process = proc_open($command, $descriptors, $pipes);
|
|
|
|
|
|
|
|
|
|
|
|
if (!is_resource($process)) {
|
|
|
|
|
|
error_log("Ошибка запуска Python через proc_open: $command");
|
|
|
|
|
|
return [
|
|
|
|
|
|
"embedding_1024" => array_fill(0, 1024, 0.001),
|
|
|
|
|
|
"embedding_2048" => array_fill(0, 2048, 0.001)
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fwrite($pipes[0], $text);
|
|
|
|
|
|
fclose($pipes[0]);
|
|
|
|
|
|
|
|
|
|
|
|
$output = stream_get_contents($pipes[1]);
|
|
|
|
|
|
fclose($pipes[1]);
|
|
|
|
|
|
|
|
|
|
|
|
$errorOutput = stream_get_contents($pipes[2]);
|
|
|
|
|
|
fclose($pipes[2]);
|
|
|
|
|
|
|
|
|
|
|
|
proc_close($process);
|
|
|
|
|
|
|
|
|
|
|
|
file_put_contents(__DIR__ . '/logs/vectorizer.log',
|
|
|
|
|
|
date('Y-m-d H:i:s') . " - Raw output: " . $output . "\n", FILE_APPEND);
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($errorOutput)) {
|
|
|
|
|
|
error_log("Ошибка Python: " . $errorOutput);
|
|
|
|
|
|
return [
|
|
|
|
|
|
"embedding_1024" => array_fill(0, 1024, 0.001),
|
|
|
|
|
|
"embedding_2048" => array_fill(0, 2048, 0.001)
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$decoded = json_decode($output, true);
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
"embedding_1024" => $decoded["embedding_1024"] ?? array_fill(0, 1024, 0.001),
|
|
|
|
|
|
"embedding_2048" => $decoded["embedding_2048"] ?? array_fill(0, 2048, 0.001)
|
|
|
|
|
|
];
|
|
|
|
|
|
}*/
|
|
|
|
|
|
?>
|