Skip to content
wiki.fftac.org

Settings - Part 02

Back to Settings

**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);
    }
}