Production-Ready Streaming AI Chat in WordPress: REST Endpoint, Nonces, and Redis Rate Limits

What we’re building
– A WordPress plugin that streams AI responses to the browser using Server-Sent Events (SSE)
– Front-end shortcode + minimal JS
– Backend REST endpoint with nonce validation, rate limits in Redis, and provider key isolation
– Works with any SSE-capable model provider (OpenAI, Anthropic, etc.)

High-level architecture
– Browser: Shortcode renders chat UI and script. JS posts user messages to /wp-json/ai/v1/chat and reads streaming tokens via EventSource.
– WordPress plugin: Validates nonce, enforces rate limit, proxies request to the AI provider, streams tokens back.
– Redis: Token bucket per user/IP for rate limiting.
– Secrets: API key injected via environment (wp-config.php), never exposed to front-end.

Prereqs
– WordPress 6.4+
– PHP 8.1+
– Redis available (phpredis or Predis). Fallback to transients if Redis isn’t available.
– Web server buffering disabled for the streaming route (notes below).

Plugin: file structure
– wp-content/plugins/ai-stream-chat/ai-stream-chat.php
– wp-content/plugins/ai-stream-chat/assets/chat.js

ai-stream-chat.php (core plugin)
esc_url_raw(rest_url(self::REST_NAMESPACE . ‘/’ . self::REST_ROUTE)),
‘nonce’ => wp_create_nonce(self::NONCE_ACTION),
]);
wp_enqueue_script($handle);
wp_enqueue_style(‘ai-sc-css’, false);
$css = ‘.ai-sc{max-width:720px;margin:1rem auto;padding:1rem;border:1px solid #ddd;border-radius:8px}.ai-sc-log{white-space:pre-wrap;font-family:system-ui, -apple-system, Segoe UI, Roboto, Arial;padding:.5rem;height:320px;overflow:auto;background:#fafafa;border:1px solid #eee;border-radius:6px;margin:.5rem 0}.ai-sc-row{display:flex;gap:.5rem}.ai-sc-row input{flex:1;padding:.6rem}.ai-sc-row button{padding:.6rem 1rem}’;
wp_add_inline_style(‘ai-sc-css’, $css);
}

public function shortcode($atts) {
ob_start(); ?>
<div class="ai-sc" data-endpoint="”>

WP_REST_Server::READABLE,
‘callback’ => [$this, ‘handle_sse’],
‘permission_callback’ => ‘__return_true’,
‘args’ => [
‘q’ => [‘required’ => true, ‘sanitize_callback’ => ‘sanitize_text_field’],
‘_wpnonce’ => [‘required’ => true],
],
]);
}

private function get_api_key() {
// Define in wp-config.php: define(‘AI_PROVIDER_KEY’, ‘sk-…’);
return defined(‘AI_PROVIDER_KEY’) ? AI_PROVIDER_KEY : getenv(‘AI_PROVIDER_KEY’);
}

private function get_client_id() {
$ip = $_SERVER[‘REMOTE_ADDR’] ?? ‘0.0.0.0’;
$uid = get_current_user_id();
return $uid ? “u:$uid” : “ip:$ip”;
}

private function redis() {
if (class_exists(‘Redis’)) {
static $r = null;
if (!$r) {
$r = new Redis();
$r->connect(defined(‘WP_REDIS_HOST’) ? WP_REDIS_HOST : ‘127.0.0.1’, defined(‘WP_REDIS_PORT’) ? WP_REDIS_PORT : 6379, 1.0);
if (defined(‘WP_REDIS_PASSWORD’) && WP_REDIS_PASSWORD) $r->auth(WP_REDIS_PASSWORD);
if (defined(‘WP_REDIS_DB’)) $r->select(WP_REDIS_DB);
}
return $r;
}
return null;
}

private function token_bucket_allow($key, $capacity, $refill_ms) {
$now = (int) (microtime(true) * 1000);
$r = $this->redis();
if ($r) {
$lua = ”
local key = KEYS[1]
local now = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local refill_ms = tonumber(ARGV[3])
local tokens = tonumber(redis.call(‘HGET’, key, ‘tokens’) or capacity)
local ts = tonumber(redis.call(‘HGET’, key, ‘ts’) or now)
local delta = math.max(0, now – ts)
local add = (capacity * delta) / refill_ms
tokens = math.min(capacity, tokens + add)
local allowed = 0
if tokens >= 1 then
tokens = tokens – 1
allowed = 1
end
redis.call(‘HSET’, key, ‘tokens’, tokens, ‘ts’, now)
redis.call(‘PEXPIRE’, key, refill_ms)
return allowed
“;
$res = $r->eval($lua, [$key, $now, $capacity, $refill_ms], 1);
return (bool) $res;
}
// Fallback to transients
$st = get_transient($key);
if (!$st) $st = [‘tokens’ => $capacity, ‘ts’ => $now];
$delta = max(0, $now – $st[‘ts’]);
$st[‘tokens’] = min($capacity, $st[‘tokens’] + ($capacity * $delta) / $refill_ms);
$allowed = $st[‘tokens’] >= 1;
if ($allowed) $st[‘tokens’] -= 1;
$st[‘ts’] = $now;
set_transient($key, $st, 60);
return $allowed;
}

public function handle_sse(WP_REST_Request $req) {
if (!wp_verify_nonce($req->get_param(‘_wpnonce’), self::NONCE_ACTION)) {
return new WP_REST_Response([‘error’ => ‘Invalid nonce’], 403);
}

$q = trim((string) $req->get_param(‘q’));
if ($q === ” || strlen($q) > 500) {
return new WP_REST_Response([‘error’ => ‘Invalid input’], 400);
}

$clientId = $this->get_client_id();
$bucketKey = self::RATE_LIMIT_BUCKET . ‘:’ . $clientId;
if (!$this->token_bucket_allow($bucketKey, self::RATE_LIMIT_CAPACITY, self::RATE_LIMIT_REFILL_MS)) {
return new WP_REST_Response([‘error’ => ‘Rate limited’], 429);
}

$apiKey = $this->get_api_key();
if (!$apiKey) {
return new WP_REST_Response([‘error’ => ‘Server not configured’], 500);
}

// Start SSE stream
nocache_headers();
header(‘Content-Type: text/event-stream’);
header(‘Cache-Control: no-cache, no-transform’);
header(‘X-Accel-Buffering: no’); // Nginx
header(‘Connection: keep-alive’);

// Disable WP/Apache buffering
if (function_exists(‘apache_setenv’)) @apache_setenv(‘no-gzip’, ‘1’);
@ini_set(‘output_buffering’, ‘off’);
@ini_set(‘zlib.output_compression’, ‘0’);
while (ob_get_level() > 0) @ob_end_flush();
@ob_implicit_flush(1);

$providerUrl = ‘https://api.openai.com/v1/chat/completions’;
$payload = [
‘model’ => ‘gpt-4o-mini’,
‘stream’ => true,
‘messages’ => [
[‘role’ => ‘system’, ‘content’ => ‘You are a concise assistant.’],
[‘role’ => ‘user’, ‘content’ => $q]
],
// Optional: ‘temperature’ => 0.2,
];
$ch = curl_init($providerUrl);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
‘Authorization: Bearer ‘ . $apiKey,
‘Content-Type: application/json’,
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_WRITEFUNCTION => function($ch, $data) {
// Pass provider SSE chunks to client
echo $data;
echo “n”; // Ensure flush cadence
flush();
return strlen($data);
},
CURLOPT_TIMEOUT => 60,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_RETURNTRANSFER => false,
]);

// Preface event to help client reset UI
echo “event: startndata: {“ok”:true}nn”;
flush();

curl_exec($ch);
if (curl_errno($ch)) {
$err = curl_error($ch);
echo “event: errorndata: ” . json_encode([‘message’ => $err]) . “nn”;
flush();
}
curl_close($ch);

echo “event: donendata: {“ok”:true}nn”;
flush();
exit; // Important: stop WordPress from adding anything else
}
}
new AI_Stream_Chat();

assets/chat.js (front-end)
(function(){
const log = document.getElementById(‘ai-sc-log’);
const input = document.getElementById(‘ai-sc-input’);
const btn = document.getElementById(‘ai-sc-send’);
const endpoint = (window.AI_SC && AI_SC.restUrl) || document.querySelector(‘.ai-sc’)?.dataset?.endpoint;

function append(text) {
log.textContent += text;
log.scrollTop = log.scrollHeight;
}

function startStream(q) {
// Transform REST GET with params for SSE
const url = new URL(endpoint);
url.searchParams.set(‘q’, q);
url.searchParams.set(‘_wpnonce’, AI_SC.nonce);

const es = new EventSource(url.toString());
let buffer = ”;

es.addEventListener(‘start’, () => {
append(‘Assistant: ‘);
});

es.onmessage = (e) => {
// Provider sends JSON chunks with choices[0].delta.content or similar; we handle plain text fragments too.
try {
const data = JSON.parse(e.data);
// OpenAI stream frames contain: choices[0].delta.content
const token = data?.choices?.[0]?.delta?.content ?? ”;
if (token) append(token);
} catch {
// Some providers send plain text lines
if (e.data && e.data !== ‘[DONE]’) append(e.data);
}
};

es.addEventListener(‘done’, () => {
append(‘n’);
es.close();
btn.disabled = false;
input.disabled = false;
});

es.addEventListener(‘error’, (e) => {
append(‘n[Stream error]n’);
es.close();
btn.disabled = false;
input.disabled = false;
});
}

btn?.addEventListener(‘click’, () => {
const q = (input.value || ”).trim();
if (!q) return;
btn.disabled = true;
input.disabled = true;
append(‘You: ‘ + q + ‘n’);
startStream(q);
input.value = ”;
});

input?.addEventListener(‘keydown’, (e) => {
if (e.key === ‘Enter’) btn.click();
});
})();

wp-config.php secrets
– Add: define(‘AI_PROVIDER_KEY’, ‘sk-live-…’);
– Optional Redis: define(‘WP_REDIS_HOST’, ‘127.0.0.1’); define(‘WP_REDIS_PORT’, 6379); define(‘WP_REDIS_PASSWORD’, ‘…’); define(‘WP_REDIS_DB’, 1);

Nginx/Apache streaming notes
– Nginx: add to location for /wp-json/ai/v1/chat: proxy_buffering off; add_header X-Accel-Buffering no;
– Cloudflare/Proxies: ensure response is not buffered; disable HTML minification for the route.
– PHP-FPM: set fastcgi_buffering off; increase read timeout to 60s if needed.

Security hardening
– Nonce required per request; no provider key in the browser.
– Input sanitized and length-limited.
– Rate limiting per user/IP; tune capacity/refill.
– Consider restricting to logged-in users only, or use an additional secret header from server-rendered page.
– Log abuse via WP_DEBUG_LOG or a dedicated audit table.

Performance considerations
– Streaming prevents large payload waits; flush early, flush often.
– Keep REST handler stateless; no session locks.
– Use short provider timeouts and surface errors.
– If no Redis, transients work but are weaker under load.

Extending to production
– Add system prompt controls in wp-admin.
– Store minimal chat history server-side (post meta or custom table) with TTL.
– Swap providers by moving providerUrl/payload to a strategy class.
– Add billing/quotas if exposing to public users.

Usage
– Activate plugin
– Place [ai_chat] on a page
– Set AI_PROVIDER_KEY
– Test: open page, send a message, verify streaming

Build a Secure AI Inference Proxy for WordPress (Caching, Rate Limits, No Exposed Keys)

Why this pattern
– Keep API keys off the client and out of theme code.
– Centralize validation, rate limits, and model allowlists.
– Add caching and observability for cost and performance control.

High-level architecture
– Client: fetches /wp-json/ai/v1/infer with a nonce.
– WordPress plugin: validates input, enforces rate limits, caches responses, proxies to the LLM vendor with server-side keys.
– Vendor API: OpenAI, Anthropic, or your inference server.
– Optional queue: for long-running generations.

Minimal plugin (secure proxy)
File: wp-content/plugins/ai-inference-proxy/ai-inference-proxy.php

‘POST’,
‘callback’ => [$this, ‘handle_infer’],
‘permission_callback’ => function() {
return current_user_can(‘read’) || is_user_logged_in() || wp_doing_ajax() || true;
},
‘args’ => [
‘prompt’ => [‘type’ => ‘string’, ‘required’ => true],
‘model’ => [‘type’ => ‘string’, ‘required’ => false],
‘max_tokens’ => [‘type’ => ‘integer’, ‘required’ => false],
‘temperature’ => [‘type’ => ‘number’, ‘required’ => false],
],
]);
}

private function get_client_fingerprint(WP_REST_Request $req) {
$user = get_current_user_id();
$ip = $_SERVER[‘REMOTE_ADDR’] ?? ‘0.0.0.0’;
return $user ? “u:$user” : “ip:$ip”;
}

private function rate_limit_key($fp) {
return “aigil_rl_{$fp}”;
}

private function cache_key($model, $prompt, $params) {
$h = wp_hash($model . ‘|’ . $prompt . ‘|’ . json_encode($params));
return “aigil_cache_$h”;
}

private function hit_rate_limit($key) {
$now = time();
$entry = get_transient($key);
if (!$entry) {
$entry = [‘count’ => 1, ‘reset’ => $now + self::WINDOW_SEC];
set_transient($key, $entry, self::WINDOW_SEC);
return false;
}
if ($entry[‘reset’] 1, ‘reset’ => $now + self::WINDOW_SEC];
set_transient($key, $entry, self::WINDOW_SEC);
return false;
}
$entry[‘count’]++;
set_transient($key, $entry, $entry[‘reset’] – $now);
return $entry[‘count’] > self::RATE_LIMIT;
}

private function vendor_request($body) {
// Read server-side secrets from wp-config.php or environment.
$api_key = defined(‘OPENAI_API_KEY’) ? OPENAI_API_KEY : getenv(‘OPENAI_API_KEY’);
if (!$api_key) return new WP_Error(‘no_key’, ‘Server not configured’, [‘status’ => 500]);

// Map to your vendor endpoint. Example: OpenAI Responses API
$url = ‘https://api.openai.com/v1/responses’;

$args = [
‘timeout’ => 20,
‘redirection’ => 0,
‘blocking’ => true,
‘headers’ => [
‘Authorization’ => ‘Bearer ‘ . $api_key,
‘Content-Type’ => ‘application/json’,
],
‘body’ => wp_json_encode($body),
];

$resp = wp_remote_post($url, $args);
if (is_wp_error($resp)) return $resp;

$code = wp_remote_retrieve_response_code($resp);
$data = json_decode(wp_remote_retrieve_body($resp), true);

if ($code >= 400) {
return new WP_Error(‘vendor_error’, ‘Upstream error’, [
‘status’ => 502,
‘details’ => [‘code’ => $code, ‘body’ => $data]
]);
}
return $data;
}

public function handle_infer(WP_REST_Request $req) {
// Input hardening
$prompt = trim((string) $req->get_param(‘prompt’));
if ($prompt === ” || mb_strlen($prompt) > 4000) {
return new WP_Error(‘bad_input’, ‘Invalid prompt’, [‘status’ => 400]);
}

$model = (string) ($req->get_param(‘model’) ?: ‘gpt-4o-mini’);
$allow = [‘gpt-4o-mini’, ‘gpt-4o’, ‘o3-mini’];
if (!in_array($model, $allow, true)) {
return new WP_Error(‘model_not_allowed’, ‘Model not allowed’, [‘status’ => 400]);
}

$max_tokens = min(800, max(50, (int) ($req->get_param(‘max_tokens’) ?: 400)));
$temperature = max(0.0, min(1.0, (float) ($req->get_param(‘temperature’) ?: 0.2)));

// Rate limiting
$fp = $this->get_client_fingerprint($req);
$rl_key = $this->rate_limit_key($fp);
if ($this->hit_rate_limit($rl_key)) {
return new WP_Error(‘rate_limited’, ‘Too many requests’, [‘status’ => 429]);
}

// Cache check
$cache_params = [‘model’ => $model, ‘max_tokens’ => $max_tokens, ‘temperature’ => $temperature];
$ckey = $this->cache_key($model, $prompt, $cache_params);
$cached = wp_cache_get($ckey, ‘aigil’);
if ($cached) {
return rest_ensure_response([
‘cached’ => true,
‘model’ => $model,
‘output’ => $cached,
]);
}

// Build vendor body (OpenAI Responses format)
$body = [
‘model’ => $model,
‘input’ => [
[‘role’ => ‘system’, ‘content’ => ‘Be concise and helpful.’],
[‘role’ => ‘user’, ‘content’ => $prompt],
],
‘max_output_tokens’ => $max_tokens,
‘temperature’ => $temperature,
];

// Call vendor
$data = $this->vendor_request($body);
if (is_wp_error($data)) return $data;

// Extract text safely (Responses API)
$text = ”;
if (isset($data[‘output’]) && is_array($data[‘output’])) {
foreach ($data[‘output’] as $item) {
if (($item[‘type’] ?? ”) === ‘message’ && isset($item[‘content’][0][‘text’])) {
$text .= $item[‘content’][0][‘text’];
}
}
} elseif (isset($data[‘choices’][0][‘message’][‘content’])) {
$text = $data[‘choices’][0][‘message’][‘content’];
}

$text = trim((string) $text);

// Cache store (object cache/Redis-aware)
if ($text !== ”) {
wp_cache_set($ckey, $text, ‘aigil’, self::CACHE_TTL);
}

// Minimal analytics log (avoid PII)
error_log(sprintf(‘[AI_PROXY] model=%s len=%d user=%s’, $model, strlen($text), $fp));

return rest_ensure_response([
‘cached’ => false,
‘model’ => $model,
‘output’ => $text,
]);
}
}

new AIGIL_Proxy();

Server config
– Store keys server-side:
– In wp-config.php: define(‘OPENAI_API_KEY’, ‘sk-xxx’);
– Or environment: set in Docker/K8s secret, read via getenv.
– Enable persistent object cache (Redis or Memcached) for effective caching.
– Set correct timeouts at PHP-FPM and reverse proxy (Nginx) > plugin timeout.

Front-end usage (nonce + fetch)
1) Enqueue and localize in your theme or plugin:

esc_url_raw( rest_url(‘ai/v1’) ),
‘nonce’ => wp_create_nonce(‘wp_rest’),
]);
});

2) ai-client.js:

async function askLLM(prompt) {
const res = await fetch(`${AIGIL.root}/infer`, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’,
‘X-WP-Nonce’: AIGIL.nonce
},
body: JSON.stringify({
prompt,
model: ‘gpt-4o-mini’,
max_tokens: 400,
temperature: 0.2
})
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err?.message || `HTTP ${res.status}`);
}
return res.json();
}

askLLM(‘Summarize today’s sales KPIs.’).then(console.log).catch(console.error);

Production notes
– Validate input length and strip HTML from user content if taking from forms.
– Model allowlist blocks costlier or experimental models by default.
– Rate limits: move to IP + user + UA combo if needed. For high-traffic, use Redis INCR with TTL.
– Caching: hash prompt + params. For authenticated/private use, consider user-scoped keys to avoid data leakage.
– Timeouts/retries: prefer a single attempt with a 20–30s timeout; log upstream latency.
– Logging: ship anonymized logs to a central sink (e.g., CloudWatch, ELK). Never log full prompts with PII.
– Streaming: if you need token streaming, prefer a Node/Python edge worker and forward via Server-Sent Events; WordPress can stream, but proxies and PHP buffers often break it.
– Cost control: apply server-side prompt templates and max token caps. Add a simple quota per user.
– Security: do not expose keys client-side; use HTTPS; audit access to the REST route; consider capability checks for admin-only models.

Extending the proxy
– Add tool/function calling with an allowlisted function registry and strict JSON schemas.
– Queue long jobs using Action Scheduler; return a job_id and poll a status route.
– Add vendor adapters (Anthropic, OpenRouter, local) with a small interface for portability.

This proxy pattern keeps your WordPress stack secure, fast, and maintainable while integrating LLM features in production.

Production-Ready AI Chat Endpoint for WordPress: Secure REST API, Token Budgeting, and Queueing

Why this matters
– Most “AI for WordPress” attempts call providers directly from the browser. That leaks keys, invites prompt injection, and breaks at scale.
– This post shows a production-ready pattern: a secure WordPress REST endpoint that enqueues requests, budgets tokens, calls a server-side AI proxy, and returns cached results.

Architecture overview
– Client (WP front end or headless): POST /wp-json/ai/v1/chat with a conversation id and message.
– WordPress plugin:
– Validates nonce/JWT and role capability.
– Enforces per-user and per-route rate limits.
– Computes token budgets and truncates history.
– Enqueues a background job via Action Scheduler.
– Returns a request id; client polls GET /wp-json/ai/v1/chat/{id}.
– AI Proxy (recommended): a small backend (e.g., Django/FastAPI) that holds provider API keys, normalizes providers (OpenAI, Anthropic), handles retries, and redacts PII per policy.
– Storage:
– wp_posts or custom table for ai_requests (status, input hash, output, token usage).
– Transient or object cache for hot responses.
– Optional: SSE/WebSocket via a small Node/Edge worker if you need streaming.

Data model (custom table)
– Table: wp_ai_requests
– id (bigint), user_id, status (pending, running, done, error)
– route (chat, summarize, classify)
– input_hash (sha256 for cache dedupe)
– prompt_json (sanitized, compact)
– result_json
– provider (openai, anthropic)
– tokens_in, tokens_out, cost_usd, created_at, updated_at

Minimal plugin (core pieces)
File: ai-chat-endpoint/ai-chat-endpoint.php
/*
Plugin Name: AI Chat Endpoint
Description: Secure AI chat REST API with queueing and token budgeting.
Version: 0.1.0
*/

if (!defined(‘ABSPATH’)) exit;

class AIGuyLA_AI_Endpoint {
const NS = ‘ai/v1’;

public function __construct() {
add_action(‘rest_api_init’, [$this, ‘routes’]);
add_action(‘ai_chat_process_request’, [$this, ‘process_request’], 10, 1);
}

public function routes() {
register_rest_route(self::NS, ‘/chat’, [
‘methods’ => ‘POST’,
‘callback’ => [$this, ‘create_request’],
‘permission_callback’ => [$this, ‘can_use_ai’],
‘args’ => [
‘conversation_id’ => [‘required’ => true],
‘message’ => [‘required’ => true],
],
]);

register_rest_route(self::NS, ‘/chat/(?Pd+)’, [
‘methods’ => ‘GET’,
‘callback’ => [$this, ‘get_request’],
‘permission_callback’ => [$this, ‘can_use_ai’],
]);
}

public function can_use_ai(WP_REST_Request $req) {
// Nonce or JWT check; fallback to logged-in capability.
if (is_user_logged_in() && current_user_can(‘read’)) return true;
return false;
}

private function rate_limited($user_id) {
$key = ‘ai_rl_’ . $user_id;
$hits = (int) get_transient($key);
if ($hits > 30) return true; // 30 req / 5 min
set_transient($key, $hits + 1, 5 * MINUTE_IN_SECONDS);
return false;
}

private function tokenize_estimate($text) {
// Cheap heuristic; replace with tiktoken server-side if needed.
$wc = str_word_count($text);
return (int) max(1, $wc * 1.3);
}

private function trim_history($messages, $max_tokens) {
$budget = $max_tokens;
$out = [];
for ($i = count($messages) – 1; $i >= 0; $i–) {
$t = $this->tokenize_estimate(json_encode($messages[$i]));
if ($t > $budget) break;
$out[] = $messages[$i];
$budget -= $t;
}
return array_reverse($out);
}

public function create_request(WP_REST_Request $req) {
$user_id = get_current_user_id();
if ($this->rate_limited($user_id)) {
return new WP_REST_Response([‘error’ => ‘rate_limited’], 429);
}

$conv_id = sanitize_text_field($req[‘conversation_id’]);
$message = wp_kses_post($req[‘message’]);

// Build messages (fetch last N from your store).
$history = []; // TODO: load from your conversation table.
$messages = array_merge($history, [[‘role’ => ‘user’, ‘content’ => $message]]);

$messages = $this->trim_history($messages, 6000); // leave room for output

$payload = [
‘provider’ => ‘openai:gpt-4o-mini’,
‘temperature’ => 0.2,
‘messages’ => $messages,
‘system’ => ‘You are a concise assistant.’,
‘max_output_tokens’ => 800,
‘metadata’ => [‘wp_user’ => $user_id, ‘conversation_id’ => $conv_id],
];

$input_hash = hash(‘sha256’, json_encode($payload));

global $wpdb;
$table = $wpdb->prefix . ‘ai_requests’;
$wpdb->insert($table, [
‘user_id’ => $user_id,
‘status’ => ‘pending’,
‘route’ => ‘chat’,
‘input_hash’ => $input_hash,
‘prompt_json’ => wp_json_encode($payload),
‘created_at’ => current_time(‘mysql’, 1),
‘updated_at’ => current_time(‘mysql’, 1),
]);
$id = (int) $wpdb->insert_id;

if (function_exists(‘as_enqueue_async_action’)) {
as_enqueue_async_action(‘ai_chat_process_request’, [$id], ‘ai’);
} else {
// Fallback: process inline (not recommended in prod).
$this->process_request($id);
}

return [‘id’ => $id, ‘status’ => ‘queued’];
}

public function get_request(WP_REST_Request $req) {
global $wpdb;
$table = $wpdb->prefix . ‘ai_requests’;
$row = $wpdb->get_row($wpdb->prepare(“SELECT * FROM $table WHERE id=%d”, (int) $req[‘id’]), ARRAY_A);
if (!$row) return new WP_REST_Response([‘error’ => ‘not_found’], 404);

// Limit data exposure.
return [
‘id’ => (int) $row[‘id’],
‘status’ => $row[‘status’],
‘result’ => $row[‘result_json’] ? json_decode($row[‘result_json’], true) : null,
‘tokens’ => [
‘in’ => (int) $row[‘tokens_in’],
‘out’ => (int) $row[‘tokens_out’],
]
];
}

public function process_request($id) {
global $wpdb;
$table = $wpdb->prefix . ‘ai_requests’;
$row = $wpdb->get_row($wpdb->prepare(“SELECT * FROM $table WHERE id=%d”, (int)$id), ARRAY_A);
if (!$row || $row[‘status’] !== ‘pending’) return;

$wpdb->update($table, [‘status’ => ‘running’, ‘updated_at’ => current_time(‘mysql’, 1)], [‘id’ => $id]);

$payload = json_decode($row[‘prompt_json’], true);

// Call your secure proxy instead of provider directly.
$proxy_url = getenv(‘AI_PROXY_URL’);
$proxy_key = getenv(‘AI_PROXY_KEY’);

$resp = wp_remote_post($proxy_url . ‘/v1/chat’, [
‘timeout’ => 30,
‘headers’ => [
‘Authorization’ => ‘Bearer ‘ . $proxy_key,
‘Content-Type’ => ‘application/json’,
],
‘body’ => wp_json_encode($payload),
]);

if (is_wp_error($resp)) {
$wpdb->update($table, [‘status’ => ‘error’, ‘result_json’ => wp_json_encode([‘error’ => $resp->get_error_message()])], [‘id’ => $id]);
return;
}

$code = wp_remote_retrieve_response_code($resp);
$body = wp_remote_retrieve_body($resp);

if ($code !== 200) {
$wpdb->update($table, [‘status’ => ‘error’, ‘result_json’ => $body], [‘id’ => $id]);
return;
}

$data = json_decode($body, true);
$tokens_in = isset($data[‘usage’][‘prompt_tokens’]) ? (int)$data[‘usage’][‘prompt_tokens’] : 0;
$tokens_out = isset($data[‘usage’][‘completion_tokens’]) ? (int)$data[‘usage’][‘completion_tokens’] : 0;

$wpdb->update($table, [
‘status’ => ‘done’,
‘result_json’ => wp_json_encode($data),
‘tokens_in’ => $tokens_in,
‘tokens_out’ => $tokens_out,
‘updated_at’ => current_time(‘mysql’, 1)
], [‘id’ => $id]);
}
}
new AIGuyLA_AI_Endpoint();

Register the table on activation
– Create the table using dbDelta.
– Install Action Scheduler (composer or plugin) for reliable background jobs.

Security hardening
– Never embed provider API keys in JS. Use a server-side proxy with IP allowlist and per-tenant keys.
– Validate nonce or JWT on every request. For headless, use short-lived JWT via a login endpoint.
– Enforce:
– Per-user rate limit (transient/object cache).
– Per-route max tokens and max output tokens.
– Allowed roles/capabilities (e.g., manage_options for admin-only routes).
– Sanitize content and strip HTML from user prompts where not needed.
– Log only necessary fields; avoid storing raw PII.

Performance considerations
– Cache identical requests by input_hash for 5–30 minutes to eliminate repeats.
– Use persistent object cache (Redis) to reduce db hits.
– Set timeouts and retry with exponential backoff in the proxy, not in WordPress.
– Batch-cron Action Scheduler to run with a dedicated queue (group “ai”) and WP-CLI runner.
– Keep payloads compact; remove redundant system prompts and reduce message metadata.

Server-side AI proxy (FastAPI example sketch)
– Endpoints: POST /v1/chat
– Responsibilities:
– Map provider models, inject safety/system prompts, enforce token ceilings.
– Retry on 429/5xx with jitter.
– Return normalized JSON with usage and finish_reason.
– Sign results with an HMAC if you need tamper detection.

Client usage example
– POST /wp-json/ai/v1/chat with:
– conversation_id: “abc123”
– message: “Summarize the last 3 updates in this thread.”
– Response: { id: 42, status: “queued” }
– Poll GET /wp-json/ai/v1/chat/42 until status = “done”, then render result. For streaming, offload to an SSE service.

Observability and cost control
– Store tokens_in/tokens_out and compute cost_usd in a nightly job.
– Add a WP-CLI command: wp ai:stats to print per-user usage.
– Alert when provider 5xx rate > 2% over 15 minutes or latency > 6s p95.

Deployment checklist
– Put AI_PROXY_URL and AI_PROXY_KEY in wp-config.php via environment variables.
– Enforce HTTPS everywhere; HSTS on the proxy.
– Enable Redis object cache and Action Scheduler health checks.
– Backup the ai_requests table; set a 30–60 day retention policy.

What to build next
– SSE streaming endpoint via a tiny Node worker subscribed to a Redis pub/sub channel.
– Vector augmentation: add a retrieval step (pgvector) before the chat call.
– UI block: a Gutenberg block that handles nonce, posting, and polling with exponential backoff.

This pattern keeps secrets off the client, scales with queues, and provides clear control over cost, latency, and reliability—all within a WordPress environment.

Developing Smart WordPress & Web Solutions

WordPress is not just a blogging platform; it’s a robust content management system (CMS) that powers over 40% of the websites on the internet. When businesses invest in smart WordPress and web solutions, they are essentially tailoring the web’s most popular CMS to meet their exact needs. For a growing company, an off-the-shelf theme or plugin can only go so far. To stand out in a crowded digital landscape you need a site that reflects your brand identity, streamlines operations and scales with you. Smart solutions go beyond aesthetics — they fuse design, functionality, marketing and automation into a unified digital strategy. Whether you run a local shop, a membership community or a global e-commerce store, crafting a custom experience shows customers that you care about their journey.

A smart build starts with solid foundations. Use a lightweight, well-maintained theme or create a child theme to safely override styles and functions without risking future updates. Custom themes let you control every pixel, ensuring your site looks polished on desktop and mobile. For dynamic features, build or commission bespoke plugins instead of piling on third-party extensions that slow down your site. A custom plugin can handle a specific business need like calculating shipping rates, managing event registrations or creating a tailored booking workflow. By keeping your code lean and tailored, you avoid bloated features you don’t need and reduce security risks from abandoned plugins. A modular approach also makes future changes easier because you know exactly how each component works.

Integration is where a WordPress site becomes a true business hub. Instead of manually copying data between systems, connect your forms, storefront and membership areas to CRMs, marketing platforms and payment gateways via secure APIs. For instance, your contact form can feed leads directly into HubSpot or Salesforce, triggering follow-up sequences. WooCommerce orders can sync with your inventory management software so stock levels stay accurate in real time. Appointment bookings might update your calendar and send meeting invites automatically. Automations built with tools like Zapier or custom webhook handlers reduce repetitive tasks, improve accuracy and free up your team for higher-value work. When data flows seamlessly across systems you get a holistic view of your customer journey and can make smarter decisions.

Smart web solutions prioritize performance and security from day one. A slow site not only frustrates visitors but also hurts your search rankings. Optimize performance by compressing images, lazy loading media, minifying CSS and JavaScript files and leveraging browser caching and content delivery networks (CDNs). Conduct regular audits to identify plugins or scripts that slow down page load times. Security is equally important. Keep WordPress core, themes and plugins up to date, enforce strong passwords and two-factor authentication and install a reputable security plugin to monitor suspicious activity. Back up your site daily and test your disaster recovery plan. Implement HTTPS everywhere and limit login attempts to deter brute-force attacks. By proactively addressing performance and security you build trust with your visitors and safeguard your business.

Artificial intelligence and machine learning can elevate a WordPress site into a smart digital assistant. AI-powered chatbots built with platforms like Dialogflow or ChatGPT can handle pre-sales questions, schedule appointments, offer product recommendations and even troubleshoot common issues around the clock. Recommendation engines analyze user behavior to suggest content, products or services that increase engagement and conversion rates. Natural language processing can summarize blog posts or generate SEO-friendly meta descriptions automatically. Image recognition tools can tag photos for accessibility and search. These capabilities are no longer reserved for big companies; cloud-based APIs make it affordable to integrate AI into smaller sites. By automating routine interactions, AI frees up human staff for high-touch tasks while delivering a personalized experience for every visitor.

To continuously improve your website, you need data. Built-in analytics tools like Google Analytics or Matomo provide traffic insights, but smart sites go further. Use heatmaps to see where users click and scroll, and session recordings to identify friction points. Implement structured data (schema markup) so search engines understand your content and feature your site in rich snippets. Run A/B tests on headlines, page layouts and call-to-action buttons using tools like Google Optimize to find what resonates most with your audience. Set up goal tracking for sign-ups, purchases and other conversions, and tie that data back to your marketing campaigns. Regularly reviewing analytics helps you refine your content strategy, optimize funnels and allocate resources where they have the greatest impact.

Ultimately, developing smart WordPress and web solutions is an iterative process. Start by outlining your business goals and the user journeys that support them, then translate those into technical requirements. Work with experienced developers who understand both WordPress best practices and broader web standards, and ensure each feature you add has a clear purpose. Keep your site lean, secure and fast, integrate it with the tools that power your business and embrace automation and AI where it makes sense. By treating your website as a living system rather than a static brochure, you’ll create a platform that adapts to changing needs, delivers measurable results and grows alongside your business.

Start by choosing a lightweight theme and enhancing it with custom blocks or child themes. Use plugins like WooCommerce for e‑commerce, then connect them to your CRM so new orders trigger emails and updates. Consider adding AI chatbots or recommendation engines to improve conversion and customer satisfaction.

Performance and security matter, too. Optimize images, enable caching and keep your software up to date. Regularly review analytics and A/B test pages to understand how visitors behave. By building smart WordPress solutions, you’ll deliver better experiences and free up time to focus on growth.