mirror of
https://github.com/Luchexx/claude-video.git
synced 2026-11-02 09:19:48 +00:00
feat: resolve watch engine and Gemini key in shared config
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
93822ce2bd
commit
ccf43c5718
@ -13,6 +13,8 @@ CONFIG_FILE = CONFIG_DIR / '.env'
|
|||||||
DEFAULT_DETAIL = 'balanced'
|
DEFAULT_DETAIL = 'balanced'
|
||||||
DETAILS = {'transcript', 'efficient', 'balanced', 'token-burner'}
|
DETAILS = {'transcript', 'efficient', 'balanced', 'token-burner'}
|
||||||
BACKENDS = {'auto', 'groq', 'openai', 'whisperx', 'none'}
|
BACKENDS = {'auto', 'groq', 'openai', 'whisperx', 'none'}
|
||||||
|
ENGINES = {'auto', 'gemini', 'local'}
|
||||||
|
DEFAULT_GEMINI_MODEL = 'gemini-3.7-flash'
|
||||||
|
|
||||||
|
|
||||||
class ConfigError(ValueError):
|
class ConfigError(ValueError):
|
||||||
@ -107,6 +109,26 @@ def load_api_key(preferred: str | None = None) -> tuple[str | None, str | None]:
|
|||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def load_gemini_key() -> str | None:
|
||||||
|
"""Environment, then the user config, then a project .env; never logged."""
|
||||||
|
value = os.environ.get('GEMINI_API_KEY', '').strip()
|
||||||
|
if not value:
|
||||||
|
for path in (CONFIG_FILE, Path.cwd() / '.env'):
|
||||||
|
value = read_env_file(path).get('GEMINI_API_KEY', '').strip()
|
||||||
|
if value:
|
||||||
|
break
|
||||||
|
return value or None
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_engine(choice: str, has_key: bool) -> str:
|
||||||
|
if choice not in ENGINES:
|
||||||
|
raise ConfigError('WATCH_ENGINE must be auto, gemini, or local.')
|
||||||
|
if choice == 'gemini' and not has_key:
|
||||||
|
raise ConfigError('The gemini engine needs GEMINI_API_KEY in the environment or '
|
||||||
|
f'{CONFIG_FILE}. Add it, or rerun with --engine local.')
|
||||||
|
return 'gemini' if choice == 'gemini' or (choice == 'auto' and has_key) else 'local'
|
||||||
|
|
||||||
|
|
||||||
def get_config(*, backend_override: str | None = None) -> dict:
|
def get_config(*, backend_override: str | None = None) -> dict:
|
||||||
file_values = read_env_file()
|
file_values = read_env_file()
|
||||||
def setting(name, default=''):
|
def setting(name, default=''):
|
||||||
@ -115,10 +137,16 @@ def get_config(*, backend_override: str | None = None) -> dict:
|
|||||||
backend = backend_override if backend_override is not None else setting('WATCH_WHISPER_BACKEND', 'auto')
|
backend = backend_override if backend_override is not None else setting('WATCH_WHISPER_BACKEND', 'auto')
|
||||||
if backend not in BACKENDS:
|
if backend not in BACKENDS:
|
||||||
raise ConfigError('WATCH_WHISPER_BACKEND must be auto, groq, openai, whisperx, or none.')
|
raise ConfigError('WATCH_WHISPER_BACKEND must be auto, groq, openai, whisperx, or none.')
|
||||||
|
engine = setting('WATCH_ENGINE', 'auto')
|
||||||
|
if engine not in ENGINES:
|
||||||
|
raise ConfigError('WATCH_ENGINE must be auto, gemini, or local.')
|
||||||
return {
|
return {
|
||||||
'detail': detail if detail in DETAILS else DEFAULT_DETAIL,
|
'detail': detail if detail in DETAILS else DEFAULT_DETAIL,
|
||||||
'config_file': str(CONFIG_FILE),
|
'config_file': str(CONFIG_FILE),
|
||||||
'whisper_backend': backend,
|
'whisper_backend': backend,
|
||||||
|
'engine': engine,
|
||||||
|
'gemini_model': setting('WATCH_GEMINI_MODEL', DEFAULT_GEMINI_MODEL) or DEFAULT_GEMINI_MODEL,
|
||||||
|
'gemini_timeout': positive_number(setting('WATCH_GEMINI_TIMEOUT', '600'), 'WATCH_GEMINI_TIMEOUT'),
|
||||||
'sub_lang': setting('WATCH_SUB_LANG', 'auto'),
|
'sub_lang': setting('WATCH_SUB_LANG', 'auto'),
|
||||||
'cookies_file': setting('WATCH_COOKIES_FILE'),
|
'cookies_file': setting('WATCH_COOKIES_FILE'),
|
||||||
'cookies_from_browser': setting('WATCH_COOKIES_FROM_BROWSER'),
|
'cookies_from_browser': setting('WATCH_COOKIES_FROM_BROWSER'),
|
||||||
|
|||||||
@ -22,7 +22,7 @@ def isolated_user(monkeypatch, tmp_path):
|
|||||||
cwd.mkdir()
|
cwd.mkdir()
|
||||||
for key in list(os.environ):
|
for key in list(os.environ):
|
||||||
if key.startswith("WATCH_") or key in {
|
if key.startswith("WATCH_") or key in {
|
||||||
"GROQ_API_KEY", "OPENAI_API_KEY", "SETUP_COMPLETE", "XDG_CONFIG_HOME",
|
"GROQ_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "SETUP_COMPLETE", "XDG_CONFIG_HOME",
|
||||||
}:
|
}:
|
||||||
monkeypatch.delenv(key)
|
monkeypatch.delenv(key)
|
||||||
monkeypatch.setenv("HOME", str(home))
|
monkeypatch.setenv("HOME", str(home))
|
||||||
|
|||||||
@ -74,3 +74,46 @@ def test_user_file_beats_project_file(monkeypatch):
|
|||||||
assert config.load_api_key() == ('groq', 'user-test')
|
assert config.load_api_key() == ('groq', 'user-test')
|
||||||
monkeypatch.setenv('GROQ_API_KEY', 'environment-test')
|
monkeypatch.setenv('GROQ_API_KEY', 'environment-test')
|
||||||
assert config.load_api_key() == ('groq', 'environment-test')
|
assert config.load_api_key() == ('groq', 'environment-test')
|
||||||
|
|
||||||
|
|
||||||
|
def test_engine_defaults_to_auto_and_local_without_key():
|
||||||
|
cfg = config.get_config()
|
||||||
|
assert cfg['engine'] == 'auto'
|
||||||
|
assert cfg['gemini_model'] == 'gemini-3.7-flash'
|
||||||
|
assert cfg['gemini_timeout'] == 600.0
|
||||||
|
assert config.load_gemini_key() is None
|
||||||
|
assert config.resolve_engine('auto', has_key=False) == 'local'
|
||||||
|
|
||||||
|
|
||||||
|
def test_auto_selects_gemini_when_key_present(monkeypatch):
|
||||||
|
monkeypatch.setenv('GEMINI_API_KEY', 'test-key')
|
||||||
|
assert config.load_gemini_key() == 'test-key'
|
||||||
|
assert config.resolve_engine('auto', has_key=True) == 'gemini'
|
||||||
|
assert config.resolve_engine('local', has_key=True) == 'local'
|
||||||
|
|
||||||
|
|
||||||
|
def test_gemini_key_lookup_order(monkeypatch, tmp_path):
|
||||||
|
config.CONFIG_DIR.mkdir(parents=True)
|
||||||
|
config.CONFIG_FILE.write_text('GEMINI_API_KEY=from-user-file\n')
|
||||||
|
(tmp_path / 'cwd' / '.env').write_text('GEMINI_API_KEY=from-cwd\n')
|
||||||
|
assert config.load_gemini_key() == 'from-user-file'
|
||||||
|
monkeypatch.setenv('GEMINI_API_KEY', 'from-env')
|
||||||
|
assert config.load_gemini_key() == 'from-env'
|
||||||
|
monkeypatch.delenv('GEMINI_API_KEY')
|
||||||
|
config.CONFIG_FILE.write_text('GEMINI_API_KEY=\n')
|
||||||
|
assert config.load_gemini_key() == 'from-cwd'
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_gemini_without_key_is_a_safe_error():
|
||||||
|
with pytest.raises(config.ConfigError, match='GEMINI_API_KEY'):
|
||||||
|
config.resolve_engine('gemini', has_key=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_engine_and_timeout_rejected(monkeypatch):
|
||||||
|
monkeypatch.setenv('WATCH_ENGINE', 'vertex')
|
||||||
|
with pytest.raises(config.ConfigError, match='WATCH_ENGINE'):
|
||||||
|
config.get_config()
|
||||||
|
monkeypatch.setenv('WATCH_ENGINE', 'auto')
|
||||||
|
monkeypatch.setenv('WATCH_GEMINI_TIMEOUT', '-5')
|
||||||
|
with pytest.raises(config.ConfigError, match='WATCH_GEMINI_TIMEOUT'):
|
||||||
|
config.get_config()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user