tigerstyle-heat/includes/api/class-ai-client.php
Ryan Malloy 0028738e33 Initial commit: TigerStyle Heat v2.0.0
Make your WordPress site irresistible. Natural SEO attraction with:
- robots.txt management
- sitemap.xml generation
- LLMs.txt support
- Google integration (Analytics, Search Console, Tag Manager)
- Schema.org structured data
- Open Graph / Twitter Card meta tags
- AMP support
- Visual elements gallery
- Built-in backup/restore module

Includes build.sh and .distignore for WordPress-installable release ZIPs.
2026-05-27 13:41:35 -06:00

388 lines
12 KiB
PHP

<?php
/**
* OpenAI-compatible API Client for TigerStyle Heat
* Provides a simple wrapper around WordPress HTTP API for OpenAI-compatible requests
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class TigerStyleSEO_AI_Client {
private $base_url;
private $api_key;
private $default_model;
private $timeout;
public function __construct($base_url, $api_key, $default_model = null) {
$this->base_url = rtrim($base_url, '/');
$this->api_key = $api_key;
$this->default_model = $default_model;
$this->timeout = 60; // Default 60 second timeout
}
/**
* Set request timeout
*/
public function set_timeout($timeout) {
$this->timeout = $timeout;
return $this;
}
/**
* Make a chat completion request
*/
public function chat_completion($params) {
// Set default model if not provided
if (!isset($params['model']) && $this->default_model) {
$params['model'] = $this->default_model;
}
// Validate required parameters
if (!isset($params['model'])) {
throw new Exception('Model is required for chat completion');
}
if (!isset($params['messages']) || empty($params['messages'])) {
throw new Exception('Messages array is required for chat completion');
}
// Set default parameters
$params = array_merge(array(
'max_tokens' => 1000,
'temperature' => 0.7,
'top_p' => 1.0,
'frequency_penalty' => 0,
'presence_penalty' => 0
), $params);
return $this->make_request('POST', '/chat/completions', $params);
}
/**
* Make a text completion request (for older models)
*/
public function text_completion($params) {
// Set default model if not provided
if (!isset($params['model']) && $this->default_model) {
$params['model'] = $this->default_model;
}
// Validate required parameters
if (!isset($params['model'])) {
throw new Exception('Model is required for text completion');
}
if (!isset($params['prompt'])) {
throw new Exception('Prompt is required for text completion');
}
// Set default parameters
$params = array_merge(array(
'max_tokens' => 1000,
'temperature' => 0.7,
'top_p' => 1.0,
'frequency_penalty' => 0,
'presence_penalty' => 0
), $params);
return $this->make_request('POST', '/completions', $params);
}
/**
* List available models
*/
public function list_models() {
return $this->make_request('GET', '/models');
}
/**
* Get model information
*/
public function get_model($model_id) {
return $this->make_request('GET', '/models/' . $model_id);
}
/**
* Create embeddings
*/
public function create_embeddings($params) {
// Set default model if not provided
if (!isset($params['model']) && $this->default_model) {
$params['model'] = $this->default_model;
}
// Validate required parameters
if (!isset($params['model'])) {
throw new Exception('Model is required for embeddings');
}
if (!isset($params['input'])) {
throw new Exception('Input is required for embeddings');
}
return $this->make_request('POST', '/embeddings', $params);
}
/**
* Create image generation request (DALL-E style)
*/
public function create_image($params) {
// Validate required parameters
if (!isset($params['prompt'])) {
throw new Exception('Prompt is required for image generation');
}
// Set defaults
$params = array_merge(array(
'n' => 1,
'size' => '512x512',
'response_format' => 'url'
), $params);
return $this->make_request('POST', '/images/generations', $params);
}
/**
* Moderate content
*/
public function moderate_content($params) {
if (!isset($params['input'])) {
throw new Exception('Input is required for moderation');
}
return $this->make_request('POST', '/moderations', $params);
}
/**
* Create fine-tuning job
*/
public function create_fine_tune($params) {
if (!isset($params['training_file'])) {
throw new Exception('Training file is required for fine-tuning');
}
return $this->make_request('POST', '/fine-tunes', $params);
}
/**
* List fine-tuning jobs
*/
public function list_fine_tunes() {
return $this->make_request('GET', '/fine-tunes');
}
/**
* Get fine-tuning job
*/
public function get_fine_tune($fine_tune_id) {
return $this->make_request('GET', '/fine-tunes/' . $fine_tune_id);
}
/**
* Cancel fine-tuning job
*/
public function cancel_fine_tune($fine_tune_id) {
return $this->make_request('POST', '/fine-tunes/' . $fine_tune_id . '/cancel');
}
/**
* Upload file
*/
public function upload_file($file_path, $purpose = 'fine-tune') {
if (!file_exists($file_path)) {
throw new Exception('File not found: ' . $file_path);
}
// For file uploads, we need to use a different approach
$boundary = wp_generate_password(16, false);
$file_content = file_get_contents($file_path);
$filename = basename($file_path);
$body = '';
$body .= '--' . $boundary . "\r\n";
$body .= 'Content-Disposition: form-data; name="purpose"' . "\r\n\r\n";
$body .= $purpose . "\r\n";
$body .= '--' . $boundary . "\r\n";
$body .= 'Content-Disposition: form-data; name="file"; filename="' . $filename . '"' . "\r\n";
$body .= 'Content-Type: application/octet-stream' . "\r\n\r\n";
$body .= $file_content . "\r\n";
$body .= '--' . $boundary . '--' . "\r\n";
$response = wp_remote_post($this->base_url . '/files', array(
'headers' => array(
'Authorization' => 'Bearer ' . $this->api_key,
'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
'User-Agent' => 'TigerStyle-SEO/' . TIGERSTYLE_HEAT_VERSION
),
'body' => $body,
'timeout' => $this->timeout
));
return $this->process_response($response);
}
/**
* List files
*/
public function list_files() {
return $this->make_request('GET', '/files');
}
/**
* Get file
*/
public function get_file($file_id) {
return $this->make_request('GET', '/files/' . $file_id);
}
/**
* Delete file
*/
public function delete_file($file_id) {
return $this->make_request('DELETE', '/files/' . $file_id);
}
/**
* Make HTTP request to OpenAI API
*/
private function make_request($method, $endpoint, $params = null) {
$url = $this->base_url . $endpoint;
$args = array(
'method' => $method,
'headers' => array(
'Authorization' => 'Bearer ' . $this->api_key,
'Content-Type' => 'application/json',
'User-Agent' => 'TigerStyle-SEO/' . TIGERSTYLE_HEAT_VERSION
),
'timeout' => $this->timeout
);
if ($params && ($method === 'POST' || $method === 'PUT' || $method === 'PATCH')) {
$args['body'] = json_encode($params);
} elseif ($params && $method === 'GET') {
$url = add_query_arg($params, $url);
}
$response = wp_remote_request($url, $args);
return $this->process_response($response);
}
/**
* Process API response
*/
private function process_response($response) {
if (is_wp_error($response)) {
throw new Exception('HTTP Request failed: ' . $response->get_error_message());
}
$status_code = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
// Try to decode JSON response
$data = json_decode($body, true);
if ($status_code >= 400) {
$error_message = 'HTTP ' . $status_code;
if ($data && isset($data['error'])) {
if (is_string($data['error'])) {
$error_message .= ': ' . $data['error'];
} elseif (isset($data['error']['message'])) {
$error_message .= ': ' . $data['error']['message'];
}
} else {
$error_message .= ': ' . $body;
}
throw new Exception($error_message);
}
return $data;
}
/**
* Helper method to create a simple chat message
*/
public function simple_chat($message, $system_prompt = null, $model = null) {
$messages = array();
if ($system_prompt) {
$messages[] = array('role' => 'system', 'content' => $system_prompt);
}
$messages[] = array('role' => 'user', 'content' => $message);
$params = array('messages' => $messages);
if ($model) {
$params['model'] = $model;
}
return $this->chat_completion($params);
}
/**
* Helper method to extract text from chat response
*/
public function extract_text($response) {
if (isset($response['choices'][0]['message']['content'])) {
return trim($response['choices'][0]['message']['content']);
}
if (isset($response['choices'][0]['text'])) {
return trim($response['choices'][0]['text']);
}
throw new Exception('Unable to extract text from response');
}
/**
* Helper method for SEO-specific prompts
*/
public function generate_meta_description($content, $max_length = 155) {
$prompt = "Generate an SEO-optimized meta description (maximum {$max_length} characters) for the following content. The description should be compelling, include relevant keywords, and encourage clicks:\n\n{$content}";
$response = $this->simple_chat($prompt, "You are an SEO expert specializing in creating compelling meta descriptions that improve search engine rankings and click-through rates.");
return $this->extract_text($response);
}
/**
* Helper method to generate SEO title
*/
public function generate_seo_title($content, $max_length = 60) {
$prompt = "Generate an SEO-optimized title (maximum {$max_length} characters) for the following content. The title should be compelling, include relevant keywords, and be click-worthy:\n\n{$content}";
$response = $this->simple_chat($prompt, "You are an SEO expert specializing in creating compelling titles that improve search engine rankings and click-through rates.");
return $this->extract_text($response);
}
/**
* Helper method to extract keywords
*/
public function extract_keywords($content, $count = 10) {
$prompt = "Extract the {$count} most important SEO keywords from the following content. Return only the keywords, separated by commas:\n\n{$content}";
$response = $this->simple_chat($prompt, "You are an SEO expert specializing in keyword research and content analysis.");
$keywords = $this->extract_text($response);
return array_map('trim', explode(',', $keywords));
}
/**
* Helper method to analyze content for SEO
*/
public function analyze_seo_content($content) {
$prompt = "Analyze the following content for SEO optimization and provide specific recommendations:\n\n{$content}";
$response = $this->simple_chat($prompt, "You are an SEO expert. Analyze content and provide actionable SEO recommendations including keyword usage, content structure, readability, and technical SEO factors.");
return $this->extract_text($response);
}
}