Viewing File: /usr/local/cpanel/whostmgr/docroot/cgi/softaculous/lib/ai/core/class_settings.php
<?php
if(!defined('SOFTACULOUS')){
die('Hacking Attempt');
}
require_once(__DIR__ . '/class_ai_file_handler.php');
class AISettings {
private $file_path;
private $data;
public function __construct(string $username){
$dir = AIFileHandler::get_ai_base_dir($username);
$this->file_path = $dir . '/settings.json.php';
$json_data = AIFileHandler::read($this->file_path);
if($json_data === null){
$this->data = ['providers' => []];
}else{
$this->data = $json_data;
}
}
public function get_connected_providers(): array{
return $this->data['providers'] ?? [];
}
public function is_provider_connected(string $provider_id): bool{
if(!empty($this->data['providers'][$provider_id])) return true;
$no_key = ['opencode_zen'];
if(in_array($provider_id, $no_key)) return true;
if(strpos($provider_id, 'custom:') === 0 && !empty($this->data['providers'][$provider_id])) return true;
return false;
}
public function get_provider_config(string $provider_id): ?array{
return $this->data['providers'][$provider_id] ?? null;
}
public function get_api_key(string $provider_id): string{
$config = $this->data['providers'][$provider_id] ?? [];
$key = $config['api_key'] ?? '';
if(empty($key)){
$no_key = ['opencode_zen'];
if(in_array($provider_id, $no_key)) return 'public';
if(!empty($config['auth_type']) && $config['auth_type'] === 'none') return 'public';
}
return $key;
}
public function save_provider(string $provider_id, array $config): void{
$config['connected_at'] = $config['connected_at'] ?? time();
$this->data['providers'][$provider_id] = $config;
$this->save();
}
public function delete_provider(string $provider_id): void{
unset($this->data['providers'][$provider_id]);
$this->save();
}
public function get_all_providers(): array{
require_once(__DIR__ . '/../providers/interface_ai_provider.php');
require_once(__DIR__ . '/../providers/class_providers.php');
$builtin = [
['id' => 'openai', 'name' => 'OpenAI', 'logo' => 'openai', 'auth_type' => 'api_key',
'models' => ['gpt-4o' => 'GPT-4o', 'gpt-4o-mini' => 'GPT-4o Mini', 'gpt-4-turbo' => 'GPT-4 Turbo']],
['id' => 'anthropic', 'name' => 'Anthropic', 'logo' => 'anthropic', 'auth_type' => 'api_key',
'models' => $this->get_flat_models('anthropic', $this->data['providers']['anthropic'] ?? [])],
['id' => 'google', 'name' => 'Google', 'logo' => 'google', 'auth_type' => 'api_key',
'models' => ['gemini-2.5-pro' => 'Gemini 2.5 Pro', 'gemini-2.5-flash' => 'Gemini 2.5 Flash', 'gemini-1.5-pro' => 'Gemini 1.5 Pro']],
['id' => 'openrouter', 'name' => 'OpenRouter', 'logo' => 'openrouter', 'auth_type' => 'api_key',
'models' => ['anthropic/claude-sonnet-4.5' => 'Claude Sonnet 4.5', 'openai/gpt-4o' => 'GPT-4o', 'google/gemini-2.5-pro' => 'Gemini 2.5 Pro']],
['id' => 'groq', 'name' => 'Groq', 'logo' => 'groq', 'auth_type' => 'api_key',
'base_url' => 'https://api.groq.com/openai/v1',
'models' => ['llama-3.3-70b-versatile' => 'Llama 3.3 70B', 'llama-3.1-8b-instant' => 'Llama 3.1 8B']],
['id' => 'deepseek', 'name' => 'DeepSeek', 'logo' => 'deepseek', 'auth_type' => 'api_key',
'base_url' => 'https://api.deepseek.com/v1',
'models' => ['deepseek-chat' => 'DeepSeek Chat', 'deepseek-coder' => 'DeepSeek Coder']],
['id' => 'together', 'name' => 'Together AI', 'logo' => 'together', 'auth_type' => 'api_key',
'models' => ['meta-llama/Llama-3-70b' => 'Llama 3 70B', 'deepseek-ai/DeepSeek-R1' => 'DeepSeek R1']],
['id' => 'xai', 'name' => 'xAI (Grok)', 'logo' => 'xai', 'auth_type' => 'api_key',
'base_url' => 'https://api.x.ai/v1',
'models' => ['grok-4.3' => 'Grok 4.3', 'grok-4.20-0309-reasoning' => 'Grok 4.20 (Reasoning)']],
['id' => 'mistral', 'name' => 'Mistral AI', 'logo' => 'mistral', 'auth_type' => 'api_key',
'base_url' => 'https://api.mistral.ai/v1',
'models' => ['mistral-large-latest' => 'Mistral Large', 'codestral-latest' => 'Codestral']],
['id' => 'perplexity', 'name' => 'Perplexity', 'logo' => 'perplexity', 'auth_type' => 'api_key',
'base_url' => 'https://api.perplexity.ai',
'models' => ['sonar-pro' => 'Sonar Pro', 'sonar-reasoning' => 'Sonar Reasoning']],
['id' => 'ollama', 'name' => 'Ollama (Local)', 'logo' => 'ollama', 'auth_type' => 'none',
'models' => ['llama3' => 'Llama 3', 'codellama' => 'Code Llama', 'mistral' => 'Mistral']],
['id' => 'ollama_cloud', 'name' => 'Ollama Cloud', 'logo' => 'ollama', 'auth_type' => 'api_key',
'base_url' => 'https://api.ollama.com/v1',
'models' => $this->get_flat_models('ollama_cloud')],
['id' => 'minimax', 'name' => 'MiniMax', 'logo' => 'minimax', 'auth_type' => 'api_key',
'base_url' => 'https://api.minimax.chat/v1',
'models' => ['MiniMax-Text-01' => 'MiniMax-Text-01', 'MiniMax-M1' => 'MiniMax-M1']],
['id' => 'opencode_zen', 'name' => 'OpenCode Zen (Free)', 'logo' => 'opencode', 'auth_type' => 'none',
'base_url' => 'https://opencode.ai/zen/v1',
'models' => $this->get_flat_models('opencode_zen'),
'no_key' => true],
['id' => 'opencode_zen_premium', 'name' => 'OpenCode Zen (Premium)', 'logo' => 'opencode', 'auth_type' => 'api_key',
'base_url' => 'https://opencode.ai/zen/v1',
'extra_headers' => ['HTTP-Referer: https://opencode.ai/', 'X-Title: opencode'],
'models' => $this->get_flat_models('opencode_zen_premium'),
'no_key' => false],
['id' => 'custom', 'name' => 'Custom Provider (OpenAI Compatible)', 'logo' => 'custom', 'auth_type' => 'api_key',
'is_custom_type' => true, 'models' => [], 'no_key' => false],
];
foreach($builtin as &$p){
if(!empty($p['is_custom_type'])){
$p['connected'] = false;
}else{
$p['connected'] = $this->is_provider_connected($p['id']);
}
// Auto-detect models from models.dev for this provider
if(!empty($p['id']) && empty($p['is_custom_type'])){
$detected = $this->detect_models_from_dev($p['id']);
if(!empty($detected)){
// Merge: keep existing models, add new ones from models.dev
$p['models'] = array_merge($p['models'] ?? [], $detected);
}
}
}
foreach($this->data['providers'] as $pid => $config){
if(strpos($pid, 'custom:') === 0 && empty($config['_from_filter'])){
$entry = [
'id' => $pid,
'name' => $config['name'] ?? __('Custom Provider'),
'logo' => 'custom',
'auth_type' => 'api_key',
'models' => $config['models'] ?? [],
'connected' => true,
'is_custom' => true
];
if(!empty($config['base_url'])){
$entry['base_url'] = $config['base_url'];
}
$builtin[] = $entry;
}
}
$original_ids = array();
foreach($builtin as $p){
$original_ids[$p['id']] = true;
}
if(function_exists('apply_filters')){
$builtin = apply_filters('softaculous_ai_providers', $builtin);
}
// Post-process the filter's output. We intentionally DO NOT write anything to
// $this->data['providers'] here - the filter's api_key/base_url are loaded
// at runtime from filter.php on every request (see ai_php_get_filter_provider_config()
// in ai_launcher.php) and never persisted into the user's settings file
// (which lives in their home directory and would otherwise leak the key).
foreach($builtin as &$p){
$pid = $p['id'];
$is_new = !isset($original_ids[$pid]);
// Auto-prefix 'custom:' to any non-built-in provider ID returned by the
// filter, so the entry is always routed through the CustomProvider class.
if(!empty($p['id']) && !empty($is_new) && function_exists('ai_php_normalize_filter_provider_id')){
$pid = $p['id'] = ai_php_normalize_filter_provider_id($p['id']);
}
$auth_type = !empty($p['auth_type']) ? $p['auth_type'] : 'api_key';
$has_predefined_key = !empty($p['api_key']);
$is_keyless = ($auth_type === 'none');
if($is_keyless || $has_predefined_key){
$p['connected'] = true;
$p['no_key'] = true;
if($is_new){
$p['is_managed'] = true;
}
}else{
$p['connected'] = $this->is_provider_connected($pid);
}
unset($p['api_key']);
}
unset($p);
$seen_ids = array();
$deduped = array();
foreach($builtin as $p){
$id = $p['id'];
if(isset($seen_ids[$id])){
$deduped[$seen_ids[$id]] = $p;
}else{
$seen_ids[$id] = count($deduped);
$deduped[] = $p;
}
}
return $deduped;
}
private function get_flat_models($provider_id, $config = array()) {
if ($provider_id === 'opencode_zen' || $provider_id === 'opencode_zen_premium') {
$provider = new OpenCodeZenProvider($provider_id);
} elseif ($provider_id === 'ollama_cloud') {
$provider = new OllamaCloudProvider();
} elseif ($provider_id === 'anthropic') {
$provider = new AnthropicProvider();
if (!empty($config['api_key'])) {
$provider->set_runtime_api_key($config['api_key']);
}
} else {
return array();
}
$models = $provider->get_supported_models();
$flat = array();
foreach ($models as $mid => $minfo) {
$flat[$mid] = is_array($minfo) ? ($minfo['name'] ?? $mid) : $minfo;
}
return $flat;
}
public function get_all_models(): array{
$providers = $this->get_all_providers();
$models = [];
foreach($providers as $p){
if(!$p['connected']) continue;
foreach($p['models'] as $mid => $mname){
$models[] = ['id' => $mid, 'name' => is_array($mname) ? ($mname['name'] ?? $mid) : $mname, 'provider' => $p['id'], 'provider_name' => $p['name']];
}
}
return $models;
}
public function get_favorite_models(): array{
return $this->data['favorite_models'] ?? [];
}
public function add_favorite_model(string $model_id): void{
if(!isset($this->data['favorite_models'])){
$this->data['favorite_models'] = [];
}
if(!in_array($model_id, $this->data['favorite_models'])){
$this->data['favorite_models'][] = $model_id;
$this->save();
}
}
public function remove_favorite_model(string $model_id): void{
if(!isset($this->data['favorite_models'])){
$this->data['favorite_models'] = [];
return;
}
$this->data['favorite_models'] = array_values(array_diff($this->data['favorite_models'], [$model_id]));
$this->save();
}
public function get_permissions(): array{
return $this->data['permissions'] ?? [];
}
public function set_permission(string $key, string $value): void{
if(!isset($this->data['permissions'])){
$this->data['permissions'] = [];
}
$this->data['permissions'][$key] = $value;
$this->save();
}
public function save(): bool{
$result = AIFileHandler::write($this->file_path, $this->data);
if($result){
@chmod($this->file_path, 0600);
}
return $result;
}
/**
* Auto-detect models from models.dev cache for a given provider.
* Returns a model_id => model_name array of models not already in the hardcoded list.
*/
private function detect_models_from_dev($provider_id): array{
if(!function_exists('ai_get_models_dev_cache')){
return array();
}
$cache = ai_get_models_dev_cache();
if(empty($cache)) return array();
$models = array();
foreach($cache as $key => $info){
// Match by provider ID
if(($info['provider'] ?? '') === $provider_id){
$model_id = $info['model'] ?? '';
$model_name = $info['name'] ?? $model_id;
if($model_id && !isset($models[$model_id])){
$models[$model_id] = $model_name;
}
}
}
return $models;
}
}
Back to Directory