Skip to content

Add AI configuration file support (#911) - #985

Open
emeryberger wants to merge 2 commits into
masterfrom
feature/ai-config-911
Open

Add AI configuration file support (#911)#985
emeryberger wants to merge 2 commits into
masterfrom
feature/ai-config-911

Conversation

@emeryberger

Copy link
Copy Markdown
Member

Summary

Addresses #911 - Add configuration file support for Scalene's AI-powered optimization features.

This PR adds support for storing AI provider settings in ~/.scalene/config.json, allowing users to:

  • Store API keys and settings persistently without environment variables
  • Configure custom endpoints for self-hosted or proxy setups
  • Set default providers and models

Changes

New scalene config CLI command

scalene config list                           # list all config values
scalene config set openai_api_key sk-xxx     # set a value
scalene config get openai_api_key            # get a value (shows source)
scalene config clear                          # clear all config
scalene config path                           # show config file path

Configuration Priority

Values are resolved in this order:

  1. Environment variables (highest priority)
  2. Config file (~/.scalene/config.json)
  3. Browser localStorage (lowest priority)

Supported Configuration Keys

Category Keys
API Keys openai_api_key, anthropic_api_key, gemini_api_key, azure_api_key, aws_access_key, aws_secret_key
Custom Models openai_model, anthropic_model, gemini_model, azure_model, aws_model, ollama_model
Custom URLs openai_url, anthropic_url, azure_api_url, ollama_host, ollama_port
Other aws_region, azure_api_version, default_provider

Files Changed

  • New: scalene/scalene_ai_config.py - Core configuration module
  • Modified: scalene/scalene_parseargs.py - Added config subcommand
  • Modified: scalene/scalene_utility.py - Use centralized config loading
  • Modified: scalene/scalene-gui/* - Extended GUI support for config fields
  • Modified: CLAUDE.md - Documentation

Test plan

  • scalene config --help shows usage
  • scalene config set/get/list/clear/path commands work correctly
  • API keys are masked in output
  • Config file is created at ~/.scalene/config.json
  • Environment variables take precedence over config file
  • All existing tests pass
  • mypy and ruff pass

🤖 Generated with Claude Code

Add support for storing AI provider settings in ~/.scalene/config.json,
addressing the need for persistent configuration across sessions.

Features:
- New `scalene config` CLI command with list/set/get/clear/path subcommands
- Configuration priority: env vars > config file > defaults
- Support for all AI providers: OpenAI, Anthropic, Gemini, Azure, AWS Bedrock, Ollama
- Extended GUI to support custom models, URLs, and default provider from config

New files:
- scalene/scalene_ai_config.py: Core configuration module

Modified files:
- scalene/scalene_parseargs.py: Added config subcommand handling
- scalene/scalene_utility.py: Use centralized config loading
- scalene/scalene-gui/*: Extended to support additional config fields
- CLAUDE.md: Updated documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
display_value = value[:8] + "..." if len(value) > 8 else "***"
else:
display_value = value
print(f"{key}: {display_value} (from {source})")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.

Copilot Autofix

AI 7 months ago

To fix the problem, we need to ensure that sensitive configuration values (API keys, secrets) are never printed in clear text, even partially. The existing logic in _handle_config_command already distinguishes sensitive keys by checking whether "key" or "secret" appears in the key name; we can strengthen that logic so it never exposes any part of the actual value. Instead, for such keys we should always print a constant placeholder such as "***" or "(set)", regardless of the value length.

Concretely, in scalene/scalene_parseargs.py:

  • In the "list" subcommand loop (lines 980–986), change the masking for "key"/"secret" keys from value[:8] + "..." if len(value) > 8 else "***" to a fixed placeholder such as "***", so no characters from the secret are shown.
  • In the "get" subcommand (lines 1011–1017), similarly change the masking logic for sensitive keys to always show a fixed placeholder, never the first 8 characters of the secret.
  • The "set" subcommand already prints "***" for keys containing "key" or "secret", so no change is needed there.

No new functions or imports are needed; we only adjust the string formatting logic in the two relevant branches.

Suggested changeset 1
scalene/scalene_parseargs.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/scalene/scalene_parseargs.py b/scalene/scalene_parseargs.py
--- a/scalene/scalene_parseargs.py
+++ b/scalene/scalene_parseargs.py
@@ -980,7 +980,8 @@
                 for key, value in sorted(config.items()):
                     # Mask sensitive values
                     if "key" in key or "secret" in key:
-                        display_value = value[:8] + "..." if len(value) > 8 else "***"
+                        # Never reveal any part of secret values
+                        display_value = "***"
                     else:
                         display_value = value
                     print(f"  {key}: {display_value}")
@@ -1011,7 +1012,8 @@
             if value:
                 # Mask sensitive values
                 if "key" in key or "secret" in key:
-                    display_value = value[:8] + "..." if len(value) > 8 else "***"
+                    # Do not reveal any portion of secret values
+                    display_value = "***"
                 else:
                     display_value = value
                 print(f"{key}: {display_value} (from {source})")
EOF
@@ -980,7 +980,8 @@
for key, value in sorted(config.items()):
# Mask sensitive values
if "key" in key or "secret" in key:
display_value = value[:8] + "..." if len(value) > 8 else "***"
# Never reveal any part of secret values
display_value = "***"
else:
display_value = value
print(f" {key}: {display_value}")
@@ -1011,7 +1012,8 @@
if value:
# Mask sensitive values
if "key" in key or "secret" in key:
display_value = value[:8] + "..." if len(value) > 8 else "***"
# Do not reveal any portion of secret values
display_value = "***"
else:
display_value = value
print(f"{key}: {display_value} (from {source})")
Copilot is powered by AI and may make mistakes. Always verify output.
@emeryberger emeryberger committed this autofix suggestion 7 months ago.
Comment on lines +13 to +22
from scalene.scalene_ai_config import (
VALID_CONFIG_KEYS,
clear_config,
get_all_ai_config,
get_config_file,
get_config_source,
get_config_value,
list_config,
set_config_value,
)

Check notice

Code scanning / CodeQL

Unused import Note

Import of 'get_all_ai_config' is not used.

Copilot Autofix

AI 7 months ago

To fix an unused import, remove only the unused symbol from the import statement, keeping the rest of the imports unchanged so existing behavior is preserved.

Concretely, in scalene/scalene_parseargs.py on the from scalene.scalene_ai_config import (...) block (lines 13–22 in your snippet), delete get_all_ai_config from the parenthesized list. Do not touch the other imported names (VALID_CONFIG_KEYS, clear_config, get_config_file, get_config_source, get_config_value, list_config, set_config_value), as they may be used elsewhere in the file. No new methods, imports, or definitions are needed; we are only simplifying the existing import.

Suggested changeset 1
scalene/scalene_parseargs.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/scalene/scalene_parseargs.py b/scalene/scalene_parseargs.py
--- a/scalene/scalene_parseargs.py
+++ b/scalene/scalene_parseargs.py
@@ -13,7 +13,6 @@
 from scalene.scalene_ai_config import (
     VALID_CONFIG_KEYS,
     clear_config,
-    get_all_ai_config,
     get_config_file,
     get_config_source,
     get_config_value,
EOF
@@ -13,7 +13,6 @@
from scalene.scalene_ai_config import (
VALID_CONFIG_KEYS,
clear_config,
get_all_ai_config,
get_config_file,
get_config_source,
get_config_value,
Copilot is powered by AI and may make mistakes. Always verify output.
…sensitive information

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants