🐘
api2.php
Back
📝 Php ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
<?php // api.php — unified chat gateway for DeepSeek, xAI Grok, and OpenAI // - Robust JSON input + CORS // - Loads keys from env OR ../keys/keys.php (then ./keys/keys.php) // - Provider-aware payload shaping (GPT-5 uses max_completion_tokens) // - Session-backed mini history + usage tally // - Clear, JSON-safe error responses declare(strict_types=1); session_start(); /* ---------- CORS ---------- */ header('Content-Type: application/json; charset=utf-8'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With'); if (($_SERVER['REQUEST_METHOD'] ?? '') === 'OPTIONS') { http_response_code(204); exit; } /* ---------- Helpers ---------- */ function jerr(string $msg, array $debug = []): void { http_response_code(200); echo json_encode(['error' => $msg, 'debug' => $debug], JSON_UNESCAPED_SLASHES); exit; } function cut(string $s, int $n = 800): string { return function_exists('mb_substr') ? mb_substr($s, 0, $n) : substr($s, 0, $n); } function load_keys_from_file(?string $path): array { if (!$path || !is_file($path)) return []; $cfg = include $path; return is_array($cfg) ? $cfg : []; } function read_json_body(): array { $raw = file_get_contents('php://input'); if ($raw === false || $raw === '') return []; $data = json_decode($raw, true); if (json_last_error() !== JSON_ERROR_NONE) { jerr('Invalid JSON body: '.json_last_error_msg(), ['raw' => cut($raw, 300)]); } return is_array($data) ? $data : []; } /* ---------- CONFIG: env → ../keys/keys.php → ./keys/keys.php ---------- */ $cfg = []; $try1 = realpath(__DIR__ . '/../keys/keys.php'); // one folder up $try2 = realpath(__DIR__ . '/keys/keys.php'); // same folder (fallback) if (!$cfg) $cfg = load_keys_from_file($try1); if (!$cfg) $cfg = load_keys_from_file($try2); $DEEPSEEK_API_KEY = getenv('DEEPSEEK_API_KEY') ?: ($cfg['DEEPSEEK_API_KEY'] ?? ''); $OPENAI_API_KEY = getenv('OPENAI_API_KEY') ?: ($cfg['OPENAI_API_KEY'] ?? ''); $XAI_API_KEY = getenv('XAI_API_KEY') ?: ($cfg['XAI_API_KEY'] ?? ''); $ENABLE_HISTORY = true; $MAX_HISTORY_MESSAGES = 12; $TIMEOUT_SECONDS = 120; /* ---------- Input ---------- */ $req = read_json_body(); if (!$req && !empty($_POST)) $req = $_POST; // form-post fallback $question = trim((string)($req['question'] ?? '')); $model = (string)($req['model'] ?? 'deepseek-chat'); $maxTokens = (int)($req['maxTokens'] ?? 800); $temperature = (float)($req['temperature'] ?? 0.7); $system = (string)($req['system'] ?? "You are a helpful, accurate assistant. Be concise and clear. Use markdown when it helps readability."); if ($question === '') jerr('Please enter a question.'); /* ---------- Provider/endpoint selection ---------- */ $provider = 'openai'; $api_url = 'https://api.openai.com/v1/chat/completions'; $api_key = $OPENAI_API_KEY; // default if (str_starts_with($model, 'deepseek')) { $provider = 'deepseek'; $api_url = 'https://api.deepseek.com/chat/completions'; $api_key = $DEEPSEEK_API_KEY; if ($api_key === '') jerr('Missing DeepSeek API key. Set env or put it in keys.php.'); } elseif (preg_match('/^grok[-_]/i', $model)) { $provider = 'xai'; $api_url = 'https://api.x.ai/v1/chat/completions'; $api_key = $XAI_API_KEY; if ($api_key === '') jerr('Missing xAI (Grok) API key. Set env or put it in keys.php.'); } else { $provider = 'openai'; $api_url = 'https://api.openai.com/v1/chat/completions'; $api_key = $OPENAI_API_KEY; if ($api_key === '') jerr('Missing OpenAI API key. Set env or put it in keys.php.'); } /* ---------- Model allowlists (non-blocking; warn helpfully) ---------- */ $valid_openai = ['gpt-5','gpt-5-mini','gpt-5-nano','gpt-5-thinking','gpt-5-pro','gpt-4o','gpt-4o-mini']; $valid_deepseek = ['deepseek-chat','deepseek-reasoner']; $valid_xai = ['grok-3','grok-3-mini','grok-code-fast-1','grok-4-0709']; $warn_invalid = null; if ($provider === 'openai' && !in_array($model, $valid_openai, true)) { $warn_invalid = "Unrecognized OpenAI model '$model' (will still attempt)."; } if ($provider === 'deepseek' && !in_array($model, $valid_deepseek, true)) { $warn_invalid = "Unrecognized DeepSeek model '$model' (will still attempt)."; } if ($provider === 'xai' && !in_array($model, $valid_xai, true)) { $warn_invalid = "Unrecognized xAI model '$model' (will still attempt)."; } /* ---------- Messages + optional session history ---------- */ $messages = [['role' => 'system', 'content' => $system]]; if ($ENABLE_HISTORY && !empty($_SESSION['chat_log'])) { $slice = array_slice($_SESSION['chat_log'], -$MAX_HISTORY_MESSAGES); foreach ($slice as $m) { if (($m['role'] ?? '') === 'user' || ($m['role'] ?? '') === 'assistant') { $messages[] = ['role' => $m['role'], 'content' => (string)$m['content']]; } } } $messages[] = ['role' => 'user', 'content' => $question]; /* ---------- Payload shaping per provider/model ---------- */ $payload = [ 'model' => $model, 'messages' => $messages, ]; if ($provider === 'openai' && str_starts_with($model, 'gpt-5')) { // GPT-5 uses max_completion_tokens on Chat Completions $payload['max_completion_tokens'] = $maxTokens; if (!empty($req['forceTemperature'])) { $payload['temperature'] = $temperature; } } else { // DeepSeek + xAI + OpenAI non-gpt-5 $payload['max_tokens'] = $maxTokens; $payload['temperature'] = $temperature; } // Optional: structured JSON outputs if (!empty($req['response_format'])) { $payload['response_format'] = $req['response_format']; // e.g. ['type'=>'json_object'] } /* ---------- cURL ---------- */ $ch = curl_init($api_url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $api_key, 'User-Agent: blocksnips-unified/1.0', ], CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_SLASHES), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $TIMEOUT_SECONDS, ]); $raw = curl_exec($ch); $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); $cerr = curl_error($ch); curl_close($ch); /* ---------- Response handling ---------- */ if ($cerr) { jerr('cURL error: '.$cerr, ['provider'=>$provider,'model'=>$model,'url'=>$api_url]); } if ($http < 200 || $http >= 300) { $maybe = json_decode((string)$raw, true); $msg = is_array($maybe) ? ($maybe['error']['message'] ?? $maybe['message'] ?? ("HTTP $http")) : ("HTTP $http"); error_log("API Error: HTTP $http | provider=$provider model=$model | resp=" . cut((string)$raw, 800)); jerr($msg, [ 'http_code'=>$http, 'provider'=>$provider, 'model'=>$model, 'url'=>$api_url, 'upstream'=>cut((string)$raw, 800) ]); } $json = json_decode((string)$raw, true); if (json_last_error() !== JSON_ERROR_NONE) { jerr('Upstream returned non-JSON', ['http_code'=>$http,'snippet'=>cut((string)$raw, 200)]); } // OpenAI/xAI/DeepSeek all mirror OpenAI’s Chat format: choices[0].message.content $answer = $json['choices'][0]['message']['content'] ?? '(no content)'; $usage = $json['usage'] ?? null; /* ---------- Session bookkeeping ---------- */ $_SESSION['chat_log'] = $_SESSION['chat_log'] ?? []; $_SESSION['usage_totals'] = $_SESSION['usage_totals'] ?? ['prompt'=>0,'completion'=>0,'total'=>0]; $_SESSION['chat_log'][] = ['role'=>'user', 'content'=>$question, 'ts'=>time()]; $_SESSION['chat_log'][] = ['role'=>'assistant', 'content'=>$answer, 'ts'=>time()]; $usage_payload = null; if (is_array($usage)) { $pt = (int)($usage['prompt_tokens'] ?? 0); $ct = (int)($usage['completion_tokens'] ?? 0); $tt = (int)($usage['total_tokens'] ?? 0); $_SESSION['last_usage'] = ['prompt'=>$pt,'completion'=>$ct,'total'=>$tt]; $_SESSION['usage_totals']['prompt'] += $pt; $_SESSION['usage_totals']['completion'] += $ct; $_SESSION['usage_totals']['total'] += $tt; $usage_payload = [ 'prompt_tokens' => $pt, 'completion_tokens' => $ct, 'total_tokens' => $tt, 'total_prompt' => $_SESSION['usage_totals']['prompt'], 'total_completion' => $_SESSION['usage_totals']['completion'], 'total_tokens_cumulative' => $_SESSION['usage_totals']['total'], ]; } /* ---------- Success ---------- */ $out = [ 'success' => true, 'provider' => $provider, 'model' => $model, 'answer' => $answer, 'usage' => $usage_payload, ]; if ($warn_invalid) $out['warning'] = $warn_invalid; echo json_encode($out, JSON_UNESCAPED_SLASHES);