208 lines
11 KiB
PHP
Executable File
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']]);
|
|
}
|