35604-vm/ai/CustomOpenAI.php
2026-01-27 07:34:48 +00:00

110 lines
3.2 KiB
PHP

<?php
/**
* CustomOpenAI - Direct client for OpenAI API to support multimodal features (Vision).
*/
class CustomOpenAI {
private string $apiKey;
private string $baseUrl = 'https://api.openai.com/v1/chat/completions';
public function __construct(string $apiKey) {
$this->apiKey = $apiKey;
}
/**
* Analyze an image (base64 or URL) with a prompt.
*/
public function analyze(array $params): array {
$model = $params['model'] ?? 'gpt-4o';
$prompt = $params['prompt'] ?? 'Analyze this image';
$content = [['type' => 'text', 'text' => $prompt]];
if (!empty($params['image_base64'])) {
$type = $params['image_type'] ?? 'image/jpeg';
$content[] = [
'type' => 'image_url',
'image_url' => [
'url' => "data:$type;base64," . $params['image_base64']
]
];
} elseif (!empty($params['image_url'])) {
$content[] = [
'type' => 'image_url',
'image_url' => [
'url' => $params['image_url']
]
];
}
$payload = [
'model' => $model,
'messages' => [
[
'role' => 'user',
'content' => $content
]
],
'max_tokens' => 2000
];
return $this->request($payload);
}
private function request(array $payload): array {
$ch = curl_init($this->baseUrl);
$jsonPayload = json_encode($payload);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $jsonPayload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->apiKey
],
CURLOPT_TIMEOUT => 60
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($response === false) {
return [
'success' => false,
'error' => 'cURL Error: ' . $curlError
];
}
$decoded = json_decode($response, true);
if ($httpCode !== 200) {
return [
'success' => false,
'error' => $decoded['error']['message'] ?? 'OpenAI API Error (Status: ' . $httpCode . ')',
'raw' => $decoded
];
}
return [
'success' => true,
'data' => $decoded
];
}
public static function extractText(array $response): string {
return $response['data']['choices'][0]['message']['content'] ?? '';
}
public static function decodeJson(array $response): ?array {
$text = self::extractText($response);
if (empty($text)) return null;
$decoded = json_decode($text, true);
if (is_array($decoded)) return $decoded;
// Try stripping markdown fences
$stripped = preg_replace('/^```json|```$/m', '', trim($text));
return json_decode($stripped, true);
}
}