Files
wevads-platform/scripts/api_provider-router.php
2026-02-26 04:53:11 +01:00

208 lines
11 KiB
PHP
Executable File

<?php
/**
* Provider Router API — Multi-Account IA with Failover
* Combines hamid_providers (7 real) + ia_provider_accounts (multi-persona)
*
* Endpoints:
* ?action=providers — List all available providers with account count
* ?action=chat — Chat with provider selection + rotation
* ?action=rotate_test — Test rotation across multiple accounts
* ?action=activate — Activate a pending account with real key
*/
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }
$input = json_decode(file_get_contents('php://input'), true) ?: [];
$action = $_GET['action'] ?? $input['action'] ?? 'providers';
$message = $_GET['message'] ?? $input['message'] ?? '';
$provider = $_GET['provider'] ?? $input['provider'] ?? '';
try {
$pdo = new PDO('pgsql:host=127.0.0.1;dbname=adx_system','admin','admin123');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (Exception $e) { die(json_encode(['error'=>'DB connection failed'])); }
// ═══════════════════════════════════════
// PROVIDER SOURCES
// ═══════════════════════════════════════
function getHamidProviders($pdo) {
try {
return $pdo->query("SELECT provider_name, model, api_key, api_url, priority, is_active,
COALESCE(requests_total,0) as requests_total, last_used
FROM admin.hamid_providers WHERE is_active=true AND api_key IS NOT NULL AND LENGTH(api_key)>10
ORDER BY priority")->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) { return []; }
}
function getMultiAccounts($pdo, $providerFilter = null) {
try {
$sql = "SELECT ia.id, ia.provider_name, ia.model, ia.api_key, ia.api_url, ia.account_email,
ia.status, ia.requests_today, ia.requests_total, ia.last_used, ia.persona_id,
p.first_name, p.last_name, p.email as persona_email
FROM admin.ia_provider_accounts ia
LEFT JOIN admin.personas p ON ia.persona_id = p.id
WHERE ia.api_key IS NOT NULL AND LENGTH(ia.api_key) > 10
AND ia.api_key NOT LIKE '%PENDING%' AND ia.status IN ('active','pending')";
if ($providerFilter) $sql .= " AND ia.provider_name ILIKE '%{$providerFilter}%'";
$sql .= " ORDER BY ia.requests_today ASC, RANDOM() LIMIT 50";
return $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) { return []; }
}
// ═══════════════════════════════════════
// CALL PROVIDER (same as hamid-chef)
// ═══════════════════════════════════════
function callProvider($name, $key, $url, $model, $system, $user) {
if (stripos($name, 'gemini') !== false) {
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$key}";
$body = json_encode(['contents'=>[['parts'=>[['text'=>$system."\n\nUser: ".$user]]]],'generationConfig'=>['maxOutputTokens'=>2000]]);
$headers = ['Content-Type: application/json'];
} elseif (stripos($name, 'claude') !== false) {
$body = json_encode(['model'=>$model,'max_tokens'=>2000,'system'=>$system,'messages'=>[['role'=>'user','content'=>$user]]]);
$headers = ['Content-Type: application/json','x-api-key: '.$key,'anthropic-version: 2023-06-01'];
} else {
$body = json_encode(['model'=>$model,'messages'=>[['role'=>'system','content'=>$system],['role'=>'user','content'=>$user]],'max_tokens'=>2000,'temperature'=>0.3]);
$headers = ['Content-Type: application/json','Authorization: Bearer '.$key];
}
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_POST=>true,CURLOPT_POSTFIELDS=>$body,CURLOPT_HTTPHEADER=>$headers,CURLOPT_RETURNTRANSFER=>true,CURLOPT_TIMEOUT=>30,CURLOPT_SSL_VERIFYPEER=>false]);
$start = microtime(true);
$resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$latency = round((microtime(true) - $start) * 1000);
if ($code !== 200 || !$resp) return null;
$data = json_decode($resp, true);
if (!$data) return null;
$text = null;
if (stripos($name,'gemini')!==false) $text = $data['candidates'][0]['content']['parts'][0]['text'] ?? null;
elseif (stripos($name,'claude')!==false) $text = $data['content'][0]['text'] ?? null;
else $text = $data['choices'][0]['message']['content'] ?? null;
return $text ? ['text'=>$text, 'latency'=>$latency] : null;
}
// ═══════════════════════════════════════
// ROUTER
// ═══════════════════════════════════════
switch ($action) {
case 'providers':
$hamid = getHamidProviders($pdo);
$multi = [];
try {
$multi = $pdo->query("SELECT provider_name, COUNT(*) as total_accounts,
COUNT(*) FILTER (WHERE api_key IS NOT NULL AND LENGTH(api_key)>10 AND api_key NOT LIKE '%PENDING%') as real_keys,
COUNT(*) FILTER (WHERE status='active') as active_accounts,
SUM(COALESCE(requests_total,0)) as total_requests
FROM admin.ia_provider_accounts
GROUP BY provider_name ORDER BY real_keys DESC")->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {}
echo json_encode([
'status'=>'success',
'hamid_providers'=>array_map(fn($p) => [
'name'=>$p['provider_name'],'model'=>$p['model'],'priority'=>$p['priority'],
'requests'=>$p['requests_total'],'last_used'=>$p['last_used']
], $hamid),
'multi_accounts'=>$multi,
'summary'=>[
'hamid_active'=>count($hamid),
'multi_providers'=>count($multi),
'total_with_real_keys'=>array_sum(array_column($multi,'real_keys')),
'total_accounts'=>array_sum(array_column($multi,'total_accounts'))
]
]);
break;
case 'chat':
if (empty($message)) { echo json_encode(['error'=>'No message']); exit; }
$system = "Tu es un assistant IA multi-provider WEVADS. Reponds en francais, concis et utile.";
$tried = [];
$result = null;
// Strategy: if provider specified, try that first (hamid_providers then ia_provider_accounts)
if ($provider) {
// Try hamid_providers first
foreach (getHamidProviders($pdo) as $p) {
if (stripos($p['provider_name'], $provider) !== false) {
$r = callProvider($p['provider_name'], $p['api_key'], $p['api_url'], $p['model'], $system, $message);
$tried[] = $p['provider_name'];
if ($r) {
// Track usage
try { $pdo->prepare("UPDATE admin.hamid_providers SET requests_total=COALESCE(requests_total,0)+1, last_used=NOW() WHERE provider_name=?")->execute([$p['provider_name']]); } catch(Exception $e){}
$result = ['response'=>$r['text'],'provider'=>$p['provider_name'],'model'=>$p['model'],'latency_ms'=>$r['latency'],'source'=>'hamid','account'=>null];
break;
}
}
}
// Try multi-accounts if hamid failed
if (!$result) {
foreach (getMultiAccounts($pdo, $provider) as $a) {
$r = callProvider($a['provider_name'], $a['api_key'], $a['api_url'], $a['model'], $system, $message);
$tried[] = $a['provider_name'].'#'.$a['id'];
if ($r) {
try { $pdo->prepare("UPDATE admin.ia_provider_accounts SET requests_total=COALESCE(requests_total,0)+1, requests_today=COALESCE(requests_today,0)+1, last_used=NOW(), status='active' WHERE id=?")->execute([$a['id']]); } catch(Exception $e){}
$result = ['response'=>$r['text'],'provider'=>$a['provider_name'],'model'=>$a['model'],'latency_ms'=>$r['latency'],'source'=>'multi_account',
'account'=>['id'=>$a['id'],'email'=>$a['account_email'],'persona'=>trim(($a['first_name']??'').' '.($a['last_name']??''))]];
break;
}
}
}
}
// Fallback: auto-rotate through hamid_providers
if (!$result) {
foreach (getHamidProviders($pdo) as $p) {
if (in_array($p['provider_name'], $tried)) continue;
$r = callProvider($p['provider_name'], $p['api_key'], $p['api_url'], $p['model'], $system, $message);
$tried[] = $p['provider_name'];
if ($r) {
try { $pdo->prepare("UPDATE admin.hamid_providers SET requests_total=COALESCE(requests_total,0)+1, last_used=NOW() WHERE provider_name=?")->execute([$p['provider_name']]); } catch(Exception $e){}
$result = ['response'=>$r['text'],'provider'=>$p['provider_name'],'model'=>$p['model'],'latency_ms'=>$r['latency'],'source'=>'hamid_auto','account'=>null];
break;
}
}
}
if ($result) {
$result['tried'] = $tried;
echo json_encode($result);
} else {
echo json_encode(['error'=>'All providers failed','tried'=>$tried]);
}
break;
case 'activate':
$id = intval($input['id'] ?? 0);
$key = $input['api_key'] ?? '';
if (!$id || !$key) { echo json_encode(['error'=>'Need id and api_key']); exit; }
try {
$pdo->prepare("UPDATE admin.ia_provider_accounts SET api_key=?, status='active' WHERE id=?")->execute([$key, $id]);
echo json_encode(['status'=>'success','message'=>"Account #$id activated"]);
} catch (Exception $e) { echo json_encode(['error'=>$e->getMessage()]); }
break;
case 'rotate_test':
$testProvider = $provider ?: 'Cerebras';
$results = [];
// Test hamid_providers
foreach (getHamidProviders($pdo) as $p) {
if (stripos($p['provider_name'], $testProvider) !== false) {
$r = callProvider($p['provider_name'], $p['api_key'], $p['api_url'], $p['model'], 'Say OK in 1 word', 'ping');
$results[] = ['source'=>'hamid','provider'=>$p['provider_name'],'model'=>$p['model'],'ok'=>!!$r,'latency'=>$r['latency']??0];
}
}
// Test multi-accounts
foreach (getMultiAccounts($pdo, $testProvider) as $a) {
$r = callProvider($a['provider_name'], $a['api_key'], $a['api_url'], $a['model'], 'Say OK in 1 word', 'ping');
$results[] = ['source'=>'multi','provider'=>$a['provider_name'],'account_id'=>$a['id'],'email'=>$a['account_email'],'ok'=>!!$r,'latency'=>$r['latency']??0];
if (count($results) >= 10) break;
}
echo json_encode(['status'=>'success','provider'=>$testProvider,'results'=>$results]);
break;
default:
echo json_encode(['error'=>'Unknown action','actions'=>['providers','chat','activate','rotate_test']]);
}