Files
wevads-platform/scripts/api_ia-provider-factory.php
2026-02-26 04:53:11 +01:00

390 lines
23 KiB
PHP
Executable File

<?php
require_once("/opt/wevads/config/credentials.php");
/**
* IA Provider Factory v2 — Multi-Account + Persona + Token Rotation
* - Multi-account per provider (persona-based)
* - Auto key rotation on rate limit
* - Token budget tracking
* - Account creation workflow
* - Key health monitoring + renewal
*/
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$db = pg_connect("host=localhost dbname=adx_system user=admin password=".WEVADS_DB_PASS);
if(!$db){echo json_encode(['status'=>'error','message'=>'DB fail']);exit;}
pg_query($db,"SET search_path TO admin");
$action = $_GET['action'] ?? $_POST['action'] ?? json_decode(file_get_contents('php://input'),true)['action'] ?? 'dashboard';
// ═══ PROVIDER CATALOG ═══
$PROVIDERS = [
'Cerebras'=>['url'=>'https://api.cerebras.ai/v1/chat/completions','model'=>'llama-3.3-70b','type'=>'free','daily_tokens'=>1000000,'daily_reqs'=>1000,'signup'=>'https://cloud.cerebras.ai/','format'=>'openai'],
'Groq'=>['url'=>'https://api.groq.com/openai/v1/chat/completions','model'=>'llama-3.3-70b-versatile','type'=>'free','daily_tokens'=>500000,'daily_reqs'=>500,'signup'=>'https://console.groq.com/','format'=>'openai'],
'DeepSeek'=>['url'=>'https://api.deepseek.com/v1/chat/completions','model'=>'deepseek-chat','type'=>'paid','daily_tokens'=>2000000,'daily_reqs'=>1000,'signup'=>'https://platform.deepseek.com/','format'=>'openai'],
'Gemini'=>['url'=>'https://generativelanguage.googleapis.com/v1beta/models','model'=>'gemini-2.0-flash','type'=>'free','daily_tokens'=>1500000,'daily_reqs'=>1500,'signup'=>'https://aistudio.google.com/','format'=>'gemini'],
'SambaNova'=>['url'=>'https://api.sambanova.ai/v1/chat/completions','model'=>'Meta-Llama-3.3-70B-Instruct','type'=>'free','daily_tokens'=>500000,'daily_reqs'=>500,'signup'=>'https://cloud.sambanova.ai/','format'=>'openai'],
'Hyperbolic'=>['url'=>'https://api.hyperbolic.xyz/v1/chat/completions','model'=>'meta-llama/Llama-3.3-70B-Instruct','type'=>'free','daily_tokens'=>500000,'daily_reqs'=>500,'signup'=>'https://app.hyperbolic.xyz/','format'=>'openai'],
'Mistral'=>['url'=>'https://api.mistral.ai/v1/chat/completions','model'=>'mistral-large-latest','type'=>'free','daily_tokens'=>500000,'daily_reqs'=>500,'signup'=>'https://console.mistral.ai/','format'=>'openai'],
'Cohere'=>['url'=>'https://api.cohere.ai/v1/chat','model'=>'command-r-plus','type'=>'free','daily_tokens'=>1000000,'daily_reqs'=>1000,'signup'=>'https://dashboard.cohere.com/','format'=>'cohere'],
'OpenRouter'=>['url'=>'https://openrouter.ai/api/v1/chat/completions','model'=>'meta-llama/llama-3.1-70b-instruct','type'=>'free','daily_tokens'=>1000000,'daily_reqs'=>1000,'signup'=>'https://openrouter.ai/','format'=>'openai'],
'Together'=>['url'=>'https://api.together.xyz/v1/chat/completions','model'=>'meta-llama/Llama-3.3-70B-Instruct-Turbo','type'=>'free','daily_tokens'=>500000,'daily_reqs'=>500,'signup'=>'https://api.together.xyz/','format'=>'openai'],
'Lepton'=>['url'=>'https://llama3-70b.lepton.run/api/v1/chat/completions','model'=>'llama3-70b','type'=>'free','daily_tokens'=>1000000,'daily_reqs'=>1000,'signup'=>'https://www.lepton.ai/','format'=>'openai'],
'Novita'=>['url'=>'https://api.novita.ai/v3/openai/chat/completions','model'=>'meta-llama/llama-3.1-70b','type'=>'free','daily_tokens'=>500000,'daily_reqs'=>500,'signup'=>'https://novita.ai/','format'=>'openai'],
];
switch($action) {
// ═══════════════════════════════════════════════════
// DASHBOARD: Full overview of all accounts + tokens
// ═══════════════════════════════════════════════════
case 'dashboard':
$accounts = [];
$r = pg_query($db, "SELECT ipa.*, p.first_name, p.last_name, p.email as persona_email
FROM ia_provider_accounts ipa
LEFT JOIN personas p ON ipa.persona_id=p.id
ORDER BY ipa.provider_name, ipa.priority");
while($row=pg_fetch_assoc($r)) $accounts[] = $row;
// Aggregate by provider
$byProvider = [];
foreach($accounts as $a) {
$pn = $a['provider_name'];
if(!isset($byProvider[$pn])) $byProvider[$pn] = ['accounts'=>0,'active'=>0,'keys'=>0,'tokens_used'=>0,'tokens_limit'=>0,'requests_today'=>0];
$byProvider[$pn]['accounts']++;
if($a['status']=='active') $byProvider[$pn]['active']++;
if($a['api_key'] && strlen($a['api_key'])>5) $byProvider[$pn]['keys']++;
$byProvider[$pn]['tokens_used'] += (int)$a['tokens_used_today'];
$byProvider[$pn]['tokens_limit'] += (int)$a['tokens_limit_daily'];
$byProvider[$pn]['requests_today'] += (int)$a['requests_today'];
}
// Token budget
$totalTokensAvail = 0; $totalTokensUsed = 0;
foreach($byProvider as $p=>$d) {
$totalTokensAvail += $d['tokens_limit'];
$totalTokensUsed += $d['tokens_used'];
}
// Alerts
$alerts = [];
foreach($PROVIDERS as $pn=>$cfg) {
$pa = $byProvider[$pn] ?? null;
if(!$pa || $pa['keys']==0) $alerts[] = ['level'=>'critical','msg'=>"$pn: 0 API keys — add accounts via personas"];
elseif($pa['accounts']<2) $alerts[] = ['level'=>'warning','msg'=>"$pn: only {$pa['accounts']} account — add more personas for token boost"];
if($pa && $pa['tokens_limit']>0 && $pa['tokens_used']/$pa['tokens_limit']>0.8)
$alerts[] = ['level'=>'warning','msg'=>"$pn: ".round($pa['tokens_used']/$pa['tokens_limit']*100)."% token budget used — rotate keys"];
}
// Persona coverage
$pr = pg_query($db,"SELECT COUNT(*) as total, COUNT(DISTINCT persona_id) FILTER(WHERE persona_id IS NOT NULL) as with_persona FROM ia_provider_accounts");
$pcov = pg_fetch_assoc($pr);
echo json_encode([
'status'=>'success',
'total_accounts'=>count($accounts),
'active_accounts'=>count(array_filter($accounts,fn($a)=>$a['status']=='active')),
'total_keys'=>count(array_filter($accounts,fn($a)=>$a['api_key']&&strlen($a['api_key'])>5)),
'providers'=>$byProvider,
'token_budget'=>[
'total_daily_limit'=>$totalTokensAvail,
'used_today'=>$totalTokensUsed,
'remaining'=>$totalTokensAvail-$totalTokensUsed,
'usage_pct'=>$totalTokensAvail>0?round($totalTokensUsed/$totalTokensAvail*100,1):0
],
'persona_coverage'=>$pcov,
'alerts'=>$alerts,
'accounts'=>$accounts,
'catalog'=>array_map(fn($c)=>['type'=>$c['type'],'signup'=>$c['signup'],'model'=>$c['model'],'daily_tokens'=>$c['daily_tokens']],$PROVIDERS)
]);
break;
// ═══════════════════════════════════════════════════
// GET_BEST_KEY: Smart rotation — get best available key
// ═══════════════════════════════════════════════════
case 'get_best_key':
$provider = $_GET['provider'] ?? $_POST['provider'] ?? null;
$where = $provider ? "AND ipa.provider_name='".pg_escape_string($db,$provider)."'" : "";
$r = pg_query($db, "SELECT ipa.id, ipa.provider_name, ipa.api_key, ipa.model, ipa.api_url,
ipa.tokens_used_today, ipa.tokens_limit_daily, ipa.requests_today, ipa.requests_limit_daily,
ipa.latency_ms, ipa.success_rate, ipa.priority
FROM ia_provider_accounts ipa
WHERE ipa.status='active' AND ipa.api_key IS NOT NULL AND LENGTH(ipa.api_key)>5
AND (ipa.cooldown_until IS NULL OR ipa.cooldown_until < NOW())
AND ipa.requests_today < ipa.requests_limit_daily
$where
ORDER BY
ipa.priority ASC,
(ipa.tokens_used_today::float / NULLIF(ipa.tokens_limit_daily,0)) ASC,
ipa.success_rate DESC,
ipa.latency_ms ASC
LIMIT 1");
$best = pg_fetch_assoc($r);
if(!$best) {
echo json_encode(['status'=>'error','message'=>'No available key — all rate limited or no keys','action'=>'add_more_accounts']);
break;
}
// Update usage
pg_query($db, "UPDATE ia_provider_accounts SET requests_today=requests_today+1, last_used=NOW() WHERE id={$best['id']}");
echo json_encode([
'status'=>'success',
'account_id'=>$best['id'],
'provider'=>$best['provider_name'],
'api_key'=>$best['api_key'],
'model'=>$best['model'],
'api_url'=>$best['api_url'],
'budget_remaining_pct'=>$best['tokens_limit_daily']>0 ? round((1-$best['tokens_used_today']/$best['tokens_limit_daily'])*100,1) : 100
]);
break;
// ═══════════════════════════════════════════════════
// LOG_USAGE: Track token consumption per call
// ═══════════════════════════════════════════════════
case 'log_usage':
$data = json_decode(file_get_contents('php://input'),true);
$accId = (int)($data['account_id'] ?? 0);
$tokIn = (int)($data['tokens_input'] ?? 0);
$tokOut = (int)($data['tokens_output'] ?? 0);
$tokTotal = $tokIn + $tokOut;
$latency = (int)($data['latency_ms'] ?? 0);
$success = ($data['success'] ?? true) ? 'true' : 'false';
$error = pg_escape_string($db, $data['error'] ?? '');
// Update account
pg_query($db, "UPDATE ia_provider_accounts SET
tokens_used_today=tokens_used_today+$tokTotal,
tokens_used_total=tokens_used_total+$tokTotal,
requests_total=requests_total+1,
latency_ms=$latency,
last_used=NOW(),
success_rate = CASE WHEN $success THEN LEAST(100, success_rate+0.1) ELSE GREATEST(0, success_rate-5) END
".($success=='false'?",error_count=error_count+1,last_error='$error'":"")."
WHERE id=$accId");
// Check if rate limited
$acc = pg_fetch_assoc(pg_query($db,"SELECT tokens_used_today,tokens_limit_daily,requests_today,requests_limit_daily FROM ia_provider_accounts WHERE id=$accId"));
$rateLimited = false;
if($acc && ($acc['tokens_used_today']>=$acc['tokens_limit_daily']*0.95 || $acc['requests_today']>=$acc['requests_limit_daily']*0.95)) {
pg_query($db,"UPDATE ia_provider_accounts SET status='rate_limited',cooldown_until=NOW()+interval '1 hour' WHERE id=$accId");
$rateLimited = true;
// Log rotation
pg_query($db,"INSERT INTO ia_key_rotation_log(old_account_id,provider_name,reason) SELECT $accId,provider_name,'rate_limited' FROM ia_provider_accounts WHERE id=$accId");
}
// Log
pg_query($db,"INSERT INTO ia_token_usage_log(account_id,provider_name,tokens_input,tokens_output,tokens_total,model,latency_ms,success,error_message)
SELECT $accId,provider_name,$tokIn,$tokOut,$tokTotal,model,$latency,$success,'$error' FROM ia_provider_accounts WHERE id=$accId");
echo json_encode(['status'=>'success','tokens_logged'=>$tokTotal,'rate_limited'=>$rateLimited]);
break;
// ═══════════════════════════════════════════════════
// CREATE_ACCOUNT: Link persona → provider account
// ═══════════════════════════════════════════════════
case 'create_account':
$data = json_decode(file_get_contents('php://input'),true) ?: $_POST;
$provider = pg_escape_string($db, $data['provider'] ?? '');
$apiKey = pg_escape_string($db, $data['api_key'] ?? '');
$personaId = (int)($data['persona_id'] ?? 0);
$email = pg_escape_string($db, $data['email'] ?? '');
if(!$provider){echo json_encode(['status'=>'error','message'=>'provider required']);break;}
$cat = $PROVIDERS[$provider] ?? null;
$model = pg_escape_string($db, $data['model'] ?? ($cat['model']??''));
$apiUrl = pg_escape_string($db, $data['api_url'] ?? ($cat['url']??''));
$tokLimit = $cat['daily_tokens'] ?? 500000;
$reqLimit = $cat['daily_reqs'] ?? 500;
// Check persona
$personaClause = $personaId>0 ? $personaId : 'NULL';
$r = pg_query($db, "INSERT INTO ia_provider_accounts(provider_name,persona_id,account_email,api_key,model,api_url,tokens_limit_daily,requests_limit_daily,status,signup_method)
VALUES('$provider',$personaClause,'$email','$apiKey','$model','$apiUrl',$tokLimit,$reqLimit,
".($apiKey?"'active'":"'pending'").",'manual') RETURNING id");
$new = pg_fetch_assoc($r);
echo json_encode(['status'=>'success','account_id'=>$new['id'],'message'=>"Account created for $provider"]);
break;
// ═══════════════════════════════════════════════════
// BATCH_CREATE: Auto-assign personas to providers
// ═══════════════════════════════════════════════════
case 'batch_create':
$data = json_decode(file_get_contents('php://input'),true) ?: $_POST;
$targetPerProvider = (int)($data['target_per_provider'] ?? 3);
// Get available personas without accounts
$personas = [];
$r = pg_query($db, "SELECT p.id, p.first_name, p.last_name, p.email
FROM personas p WHERE p.is_active=true
AND p.id NOT IN (SELECT persona_id FROM ia_provider_accounts WHERE persona_id IS NOT NULL)
ORDER BY random() LIMIT 100");
while($row=pg_fetch_assoc($r)) $personas[] = $row;
$created = [];
$pIdx = 0;
foreach($PROVIDERS as $pn=>$cfg) {
if($cfg['type']=='paid') continue; // Skip paid providers for auto-create
// Count existing accounts
$existing = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as c FROM ia_provider_accounts WHERE provider_name='$pn'"));
$need = max(0, $targetPerProvider - (int)$existing['c']);
for($i=0; $i<$need && $pIdx<count($personas); $i++) {
$p = $personas[$pIdx++];
$email = strtolower($p['first_name'].'.'.$p['last_name'].rand(10,99).'@gmail.com');
pg_query($db, "INSERT INTO ia_provider_accounts(provider_name,persona_id,account_email,model,api_url,tokens_limit_daily,requests_limit_daily,status,signup_method,signup_url)
VALUES('$pn',{$p['id']},'$email','{$cfg['model']}','{$cfg['url']}',{$cfg['daily_tokens']},{$cfg['daily_reqs']},'pending','automated','{$cfg['signup']}')");
$created[] = ['provider'=>$pn,'persona'=>$p['first_name'].' '.$p['last_name'],'email'=>$email,'status'=>'pending'];
}
}
echo json_encode(['status'=>'success','created'=>count($created),'accounts'=>$created,
'message'=>count($created).' persona accounts created. Set API keys to activate.',
'next_step'=>'For each pending account: signup at provider URL, get API key, call update_key']);
break;
// ═══════════════════════════════════════════════════
// UPDATE_KEY: Add/update API key for account
// ═══════════════════════════════════════════════════
case 'update_key':
$data = json_decode(file_get_contents('php://input'),true) ?: $_POST;
$accId = (int)($data['account_id'] ?? 0);
$apiKey = pg_escape_string($db, $data['api_key'] ?? '');
if(!$accId||!$apiKey){echo json_encode(['status'=>'error','message'=>'account_id + api_key required']);break;}
pg_query($db, "UPDATE ia_provider_accounts SET api_key='$apiKey', status='active', key_created_at=NOW(), updated_at=NOW() WHERE id=$accId");
echo json_encode(['status'=>'success','message'=>'Key updated, account activated']);
break;
// ═══════════════════════════════════════════════════
// TEST_ALL: Test every active key
// ═══════════════════════════════════════════════════
case 'test_all':
case 'scan':
$accounts = [];
$r = pg_query($db, "SELECT id,provider_name,api_key,model,api_url FROM ia_provider_accounts WHERE api_key IS NOT NULL AND LENGTH(api_key)>5 ORDER BY provider_name");
$results = [];
while($acc=pg_fetch_assoc($r)) {
$res = testKey($acc['provider_name'],$acc['api_key'],$acc['model'],$acc['api_url']);
$status = $res['http_code']==200 ? 'active' : ($res['http_code']==429 ? 'rate_limited' : 'error');
pg_query($db, "UPDATE ia_provider_accounts SET status='$status', last_tested=NOW(), last_test_result='$status', latency_ms={$res['latency_ms']} WHERE id={$acc['id']}");
$results[] = ['id'=>$acc['id'],'provider'=>$acc['provider_name'],'status'=>$status,'latency'=>$res['latency_ms'].'ms','http'=>$res['http_code']];
}
echo json_encode(['status'=>'success','tested'=>count($results),'results'=>$results]);
break;
// ═══════════════════════════════════════════════════
// ROTATE: Reset daily counters + unblock rate_limited
// ═══════════════════════════════════════════════════
case 'rotate':
case 'reset_daily':
pg_query($db, "UPDATE ia_provider_accounts SET tokens_used_today=0, requests_today=0, status='active' WHERE status='rate_limited' AND cooldown_until<NOW()");
pg_query($db, "UPDATE ia_provider_accounts SET tokens_used_today=0, requests_today=0 WHERE tokens_used_today>0");
$r = pg_fetch_assoc(pg_query($db, "SELECT COUNT(*) as reset FROM ia_provider_accounts WHERE tokens_used_today=0"));
echo json_encode(['status'=>'success','message'=>'Daily counters reset','accounts_reset'=>$r['reset']]);
break;
// ═══════════════════════════════════════════════════
// TOKEN_REPORT: Detailed token usage analytics
// ═══════════════════════════════════════════════════
case 'token_report':
$days = (int)($_GET['days'] ?? 7);
$r = pg_query($db, "SELECT provider_name,
SUM(tokens_total) as total_tokens,
COUNT(*) as total_requests,
AVG(latency_ms) as avg_latency,
SUM(CASE WHEN success THEN 1 ELSE 0 END)*100.0/COUNT(*) as success_pct
FROM ia_token_usage_log
WHERE logged_at > NOW()-interval '$days days'
GROUP BY provider_name ORDER BY total_tokens DESC");
$report = [];
while($row=pg_fetch_assoc($r)) $report[] = $row;
echo json_encode(['status'=>'success','period_days'=>$days,'usage'=>$report]);
break;
// ═══════════════════════════════════════════════════
// PENDING: Show accounts needing API keys
// ═══════════════════════════════════════════════════
case 'pending':
$r = pg_query($db, "SELECT ipa.id, ipa.provider_name, ipa.account_email, ipa.signup_url,
p.first_name, p.last_name
FROM ia_provider_accounts ipa
LEFT JOIN personas p ON ipa.persona_id=p.id
WHERE ipa.status='pending' ORDER BY ipa.provider_name");
$pending = [];
while($row=pg_fetch_assoc($r)) $pending[] = $row;
echo json_encode(['status'=>'success','pending_count'=>count($pending),'accounts'=>$pending]);
break;
// ═══════════════════════════════════════════════════
// STATUS: Quick health check
// ═══════════════════════════════════════════════════
case 'status':
default:
$stats = pg_fetch_assoc(pg_query($db, "SELECT
COUNT(*) as total,
SUM(CASE WHEN status='active' THEN 1 ELSE 0 END) as active,
SUM(CASE WHEN status='pending' THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status='rate_limited' THEN 1 ELSE 0 END) as rate_limited,
SUM(CASE WHEN api_key IS NOT NULL AND LENGTH(api_key)>5 THEN 1 ELSE 0 END) as with_keys,
COUNT(DISTINCT provider_name) as providers,
COUNT(DISTINCT persona_id) FILTER(WHERE persona_id IS NOT NULL) as personas_linked,
SUM(tokens_limit_daily) as total_daily_tokens,
SUM(tokens_used_today) as tokens_used_today
FROM ia_provider_accounts"));
$health = 'EXCELLENT';
if((int)$stats['with_keys']<5) $health = 'NEEDS_MORE_KEYS';
if((int)$stats['active']<3) $health = 'CRITICAL';
if((int)$stats['total']>10 && (int)$stats['personas_linked']<5) $health = 'NEEDS_PERSONA_LINKS';
echo json_encode([
'status'=>'success',
'health'=>$health,
'stats'=>$stats,
'token_budget'=>[
'daily_limit'=>(int)$stats['total_daily_tokens'],
'used_today'=>(int)$stats['tokens_used_today'],
'remaining'=>(int)$stats['total_daily_tokens']-(int)$stats['tokens_used_today']
]
]);
break;
}
// ═══ TEST KEY FUNCTION ═══
function testKey($provider,$key,$model,$apiUrl) {
$start = microtime(true);
$ch = curl_init();
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>10, CURLOPT_SSL_VERIFYPEER=>false]);
if(stripos($provider,'gemini')!==false) {
curl_setopt($ch, CURLOPT_URL, "$apiUrl/$model:generateContent?key=$key");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['contents'=>[['parts'=>[['text'=>'OK']]]]]));
} elseif(stripos($provider,'cohere')!==false) {
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json',"Authorization: Bearer $key"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['model'=>$model,'message'=>'OK']));
} else {
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json',"Authorization: Bearer $key"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['model'=>$model,'messages'=>[['role'=>'user','content'=>'Say OK']],'max_tokens'=>10]));
}
curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$lat = round((microtime(true)-$start)*1000);
curl_close($ch);
return ['http_code'=>$http,'latency_ms'=>$lat];
}
?>