文件操作 - class_settings.php
返回文件管理
返回主菜单
删除本文件
文件: /usr/local/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{ return AIFileHandler::write($this->file_path, $this->data); } /** * 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; } }
修改文件时间
将文件时间修改为当前时间的前一年
删除文件