Settings - Part 02
**Source path:** Spiralist/wp-content/plugins/spiralist-ai-runtime/includes/Config/Settings.php
<label><input name="<?php echo esc_attr(self::OPTION); ?>[allow_user_keys]" type="checkbox" value="1" <?php checked((string) $settings['allow_user_keys'], '1'); ?> /> Let users store their own provider keys and backup keys</label>
</td>
</tr>
</table>
<h2>Providers</h2>
<p>Leave API key fields blank when saving to keep the current secret. Check the clear box only when you intentionally want to remove a stored key.</p>
<table class="form-table" role="presentation">
<?php foreach (self::provider_labels() as $provider => $label) : ?>
<?php $provider_settings = (array) ($settings['providers'][$provider] ?? []); ?>
<?php $primary_key_saved = trim((string) ($provider_settings['primary_api_key'] ?? '')) !== ''; ?>
<?php $backup_key_saved = trim((string) ($provider_settings['backup_api_key'] ?? '')) !== ''; ?>
<tr id="spiralist-ai-runtime-provider-<?php echo esc_attr($provider); ?>">
<th scope="row"><?php echo esc_html($label); ?></th>
<td>
<label><input name="<?php echo esc_attr(self::OPTION); ?>[providers][<?php echo esc_attr($provider); ?>][enabled]" type="checkbox" value="1" <?php checked((string) ($provider_settings['enabled'] ?? '0'), '1'); ?> /> Enabled</label>
<p><label>Base URL<br /><input class="regular-text code" name="<?php echo esc_attr(self::OPTION); ?>[providers][<?php echo esc_attr($provider); ?>][base_url]" type="url" value="<?php echo esc_attr((string) ($provider_settings['base_url'] ?? '')); ?>" /></label></p>
<p><label>Default Model<br /><input class="regular-text" name="<?php echo esc_attr(self::OPTION); ?>[providers][<?php echo esc_attr($provider); ?>][default_model]" type="text" value="<?php echo esc_attr((string) ($provider_settings['default_model'] ?? '')); ?>" /></label></p>
<p>
<?php if ($primary_key_saved) : ?>
<label>Saved Primary Key<br /><input class="regular-text code" type="text" value="<?php echo esc_attr($this->masked_secret_value((string) ($provider_settings['primary_api_key'] ?? ''))); ?>" readonly="readonly" aria-readonly="true" /></label><br />
<?php endif; ?>
<label>Primary API Key<br /><input class="regular-text code" name="<?php echo esc_attr(self::OPTION); ?>[providers][<?php echo esc_attr($provider); ?>][primary_api_key]" type="password" value="" autocomplete="new-password" placeholder="<?php echo esc_attr($primary_key_saved ? 'Stored primary key on file. Paste a new one only if you want to replace it.' : 'Paste primary API key'); ?>" /></label>
<label style="margin-left:1rem;"><input name="<?php echo esc_attr(self::OPTION); ?>[providers][<?php echo esc_attr($provider); ?>][clear_primary_api_key]" type="checkbox" value="1" /> Clear stored primary key</label>
<br /><span class="description"><?php echo esc_html($primary_key_saved ? 'Primary key is currently stored. The masked field above confirms it.' : 'No primary key stored.'); ?></span></p>
<p>
<?php if ($backup_key_saved) : ?>
<label>Saved Backup Key<br /><input class="regular-text code" type="text" value="<?php echo esc_attr($this->masked_secret_value((string) ($provider_settings['backup_api_key'] ?? ''))); ?>" readonly="readonly" aria-readonly="true" /></label><br />
<?php endif; ?>
<label>Backup API Key<br /><input class="regular-text code" name="<?php echo esc_attr(self::OPTION); ?>[providers][<?php echo esc_attr($provider); ?>][backup_api_key]" type="password" value="" autocomplete="new-password" placeholder="<?php echo esc_attr($backup_key_saved ? 'Stored backup key on file. Paste a new one only if you want to replace it.' : 'Paste backup API key'); ?>" /></label>
<label style="margin-left:1rem;"><input name="<?php echo esc_attr(self::OPTION); ?>[providers][<?php echo esc_attr($provider); ?>][clear_backup_api_key]" type="checkbox" value="1" /> Clear stored backup key</label>
<br /><span class="description"><?php echo esc_html($backup_key_saved ? 'Backup key is currently stored. The masked field above confirms it.' : 'No backup key stored.'); ?></span></p>
</td>
</tr>
<?php endforeach; ?>
</table>
<?php submit_button('Save AI Runtime Settings'); ?>
</form>
</div>
<?php
}
public static function provider_labels(): array
{
return [
'openai' => 'OpenAI',
'freedomgpt' => 'FreedomGPT',
'lmstudio' => 'LM Studio',
];
}
public static function defaults(): array
{
return [
'default_provider' => 'openai',
'allow_user_provider_override' => '1',
'allow_user_keys' => '1',
'default_credential_preference' => 'personal_for_heavy',
'heavy_request_token_threshold' => '5000',
'providers' => [
'openai' => [
'enabled' => '1',
'base_url' => 'https://api.openai.com/v1',
'default_model' => 'gpt-5-mini',
'primary_api_key' => '',
'backup_api_key' => '',
],
'freedomgpt' => [
'enabled' => '0',
'base_url' => 'https://chat.freedomgpt.com/api/v1',
'default_model' => 'liberty',
'primary_api_key' => '',
'backup_api_key' => '',
],
'lmstudio' => [
'enabled' => '0',
'base_url' => 'http://127.0.0.1:1234/v1',
'default_model' => 'local-model',
'primary_api_key' => '',
'backup_api_key' => '',
],
],
];
}
private function merge_defaults(array $defaults, array $stored): array
{
foreach ($defaults as $key => $value) {
if (is_array($value)) {
$defaults[$key] = $this->merge_defaults($value, is_array($stored[$key] ?? null) ? $stored[$key] : []);
continue;
}
if (array_key_exists($key, $stored)) {
$defaults[$key] = $stored[$key];
}
}
return $defaults;
}
private function sanitize_provider(string $provider): string
{
$provider = sanitize_key($provider);
return array_key_exists($provider, self::provider_labels()) ? $provider : 'openai';
}
private function sanitize_credential_preference(string $value, bool $allow_site_default): string
{
$value = sanitize_key($value);
$allowed = ['site_only', 'personal_for_heavy', 'always_personal'];
if ($allow_site_default) {
$allowed[] = 'site_default';
}
return in_array($value, $allowed, true) ? $value : ($allow_site_default ? 'site_default' : 'personal_for_heavy');
}
private function sanitize_base_url(string $url): string
{
return untrailingslashit(esc_url_raw(trim($url)));
}
private function sanitize_secret(string $submitted, string $existing, bool $clear): string
{
if ($clear) {
return '';
}
$submitted = trim($submitted);
return $submitted !== '' ? $submitted : $existing;
}
private function masked_secret_value(string $secret): string
{
return trim($secret) === '' ? '' : 'Stored key on file ' . str_repeat('•', 12);
}
}