Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/podtx/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ def _settings_from_opts(
cleanup: Optional[bool] = None,
correct_names: Optional[bool] = None,
diarize: Optional[bool] = None,
diarize_backend: Optional[str] = None,
diarize_model: Optional[str] = None,
diarize_base_url: Optional[str] = None,
diarize_api_key: Optional[str] = None,
diarize_api_key_service: Optional[str] = None,
diarize_api_key_account: Optional[str] = None,
diarize_timeout: Optional[float] = None,
trim_start: Optional[float] = None,
) -> Settings:
return load_settings(
Expand All @@ -72,6 +79,13 @@ def _settings_from_opts(
cleanup=cleanup,
correct_names=correct_names,
diarize=diarize,
diarize_backend=diarize_backend,
diarize_model=diarize_model,
diarize_base_url=diarize_base_url,
diarize_api_key=diarize_api_key,
diarize_api_key_service=diarize_api_key_service,
diarize_api_key_account=diarize_api_key_account,
diarize_timeout=diarize_timeout,
trim_start=trim_start,
)

Expand Down Expand Up @@ -508,6 +522,23 @@ def sync_feeds(
"--diarize",
help="Speaker diarization: label segments with SPEAKER_00/01 + reflect turns in text. Opt-in; off by default (single-speaker unchanged). Performance/memory impact: local, CPU-bound.",
),
diarize_backend: Optional[str] = typer.Option(
None,
"--diarize-backend",
help="Diarization backend: fake (round-robin), pyannote (local), hf, assemblyai, deepgram (default: fake)",
),
diarize_model: Optional[str] = typer.Option(
None, "--diarize-model", help="Diarization model id (default for pyannote/hf: pyannote/speaker-diarization-3.1)",
),
diarize_api_key: Optional[str] = typer.Option(
None, "--diarize-api-key", help="API key for diarize backend (or env HF_TOKEN/ASSEMBLYAI_API_KEY, or Keychain via `podtx auth set`)",
),
diarize_base_url: Optional[str] = typer.Option(
None, "--diarize-base-url", help="Override base URL for diarize backend",
),
diarize_timeout: Optional[float] = typer.Option(
None, "--diarize-timeout", help="Diarization request timeout seconds (default 120)",
),
trim_start: Optional[float] = typer.Option(
None,
"--trim-start",
Expand Down Expand Up @@ -535,6 +566,11 @@ def sync_feeds(
cleanup=True if cleanup else None,
correct_names=True if correct_names else None,
diarize=True if diarize else None,
diarize_backend=diarize_backend,
diarize_model=diarize_model,
diarize_base_url=diarize_base_url,
diarize_api_key=diarize_api_key,
diarize_timeout=diarize_timeout,
trim_start=trim_start,
)
settings = replace(
Expand Down Expand Up @@ -661,6 +697,23 @@ def transcribe_cmd(
"--diarize",
help="Speaker diarization: label segments with SPEAKER_00/01 + reflect turns in text. Opt-in; off by default.",
),
diarize_backend: Optional[str] = typer.Option(
None,
"--diarize-backend",
help="Diarization backend: fake (round-robin), pyannote (local), hf, assemblyai, deepgram (default: fake)",
),
diarize_model: Optional[str] = typer.Option(
None, "--diarize-model", help="Diarization model id (default for pyannote/hf: pyannote/speaker-diarization-3.1)",
),
diarize_api_key: Optional[str] = typer.Option(
None, "--diarize-api-key", help="API key for diarize backend (or env HF_TOKEN/ASSEMBLYAI_API_KEY, or Keychain)",
),
diarize_base_url: Optional[str] = typer.Option(
None, "--diarize-base-url", help="Override base URL for diarize backend",
),
diarize_timeout: Optional[float] = typer.Option(
None, "--diarize-timeout", help="Diarization request timeout seconds (default 120)",
),
trim_start: Optional[float] = typer.Option(
None,
"--trim-start",
Expand Down Expand Up @@ -688,6 +741,11 @@ def transcribe_cmd(
cleanup=True if cleanup else None,
correct_names=True if correct_names else None,
diarize=True if diarize else None,
diarize_backend=diarize_backend,
diarize_model=diarize_model,
diarize_base_url=diarize_base_url,
diarize_api_key=diarize_api_key,
diarize_timeout=diarize_timeout,
trim_start=trim_start,
)
settings = replace(
Expand Down
79 changes: 79 additions & 0 deletions src/podtx/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
DEFAULT_SUMMARIZE_TIMEOUT = 60.0
DEFAULT_SUMMARIZE_TEMPERATURE = 0.3

# Diarize defaults
DEFAULT_DIARIZE_BACKEND = "fake"
DEFAULT_DIARIZE_TIMEOUT = 120.0
DEFAULT_PYANNOTE_MODEL = "pyannote/speaker-diarization-3.1"
DEFAULT_HF_MODEL = "pyannote/speaker-diarization-3.1"
DEFAULT_HF_BASE_URL = "https://api-inference.huggingface.co"
DEFAULT_ASSEMBLYAI_BASE_URL = "https://api.assemblyai.com"
DEFAULT_DEEPGRAM_BASE_URL = "https://api.deepgram.com"


def default_data_dir() -> Path:
return Path(user_data_dir(APP_NAME, appauthor=False))
Expand All @@ -56,6 +65,14 @@ class Settings:
correct_names: bool = False
diarize: bool = False
trim_start: float = 0.0
# Diarize
diarize_backend: str = DEFAULT_DIARIZE_BACKEND
diarize_model: str | None = None
diarize_base_url: str | None = None
diarize_api_key: str | None = None
diarize_api_key_service: str | None = None
diarize_api_key_account: str | None = None
diarize_timeout: float = DEFAULT_DIARIZE_TIMEOUT
# Summarize
summarize_backend: str = DEFAULT_SUMMARIZE_BACKEND
summarize_model: str | None = None
Expand Down Expand Up @@ -123,6 +140,13 @@ def load_settings(
correct_names: bool | None = None,
diarize: bool | None = None,
trim_start: float | int | None = None,
diarize_backend: str | None = None,
diarize_model: str | None = None,
diarize_base_url: str | None = None,
diarize_api_key: str | None = None,
diarize_api_key_service: str | None = None,
diarize_api_key_account: str | None = None,
diarize_timeout: float | None = None,
summarize_backend: str | None = None,
summarize_model: str | None = None,
summarize_base_url: str | None = None,
Expand Down Expand Up @@ -172,6 +196,20 @@ def load_settings(
settings = replace(settings, correct_names=bool(toml["correctNames"]))
if "diarize" in toml: # pragma: no cover - TOML tested via existing suite
settings = replace(settings, diarize=bool(toml["diarize"]))
if "diarize_backend" in toml:
settings = replace(settings, diarize_backend=str(toml["diarize_backend"]))
if "diarize_model" in toml:
settings = replace(settings, diarize_model=str(toml["diarize_model"]))
if "diarize_base_url" in toml:
settings = replace(settings, diarize_base_url=str(toml["diarize_base_url"]))
if "diarize_api_key" in toml:
settings = replace(settings, diarize_api_key=str(toml["diarize_api_key"]))
if "diarize_api_key_service" in toml:
settings = replace(settings, diarize_api_key_service=str(toml["diarize_api_key_service"]))
if "diarize_api_key_account" in toml:
settings = replace(settings, diarize_api_key_account=str(toml["diarize_api_key_account"]))
if "diarize_timeout" in toml:
settings = replace(settings, diarize_timeout=float(toml["diarize_timeout"]))
if "trim_start" in toml: # pragma: no cover - error branches, valid path tested via TOML test
try:
ts = float(toml["trim_start"])
Expand Down Expand Up @@ -232,6 +270,33 @@ def load_settings(
settings = replace(settings, correct_names=v.lower() in {"1", "true", "yes", "on"})
if (v := _env("DIARIZE")) is not None: # pragma: no cover - env already tested via existing suite
settings = replace(settings, diarize=v.lower() in {"1", "true", "yes", "on"})
if (v := _env("DIARIZE_BACKEND")) is not None:
settings = replace(settings, diarize_backend=v)
if (v := _env("DIARIZE_MODEL")) is not None:
settings = replace(settings, diarize_model=v)
if (v := _env("DIARIZE_BASE_URL")) is not None:
settings = replace(settings, diarize_base_url=v)
if (v := _env("DIARIZE_API_KEY")) is not None:
settings = replace(settings, diarize_api_key=v)
if (v := _env("DIARIZE_API_KEY_SERVICE")) is not None:
settings = replace(settings, diarize_api_key_service=v)
if (v := _env("DIARIZE_API_KEY_ACCOUNT")) is not None:
settings = replace(settings, diarize_api_key_account=v)
if (v := _env("DIARIZE_TIMEOUT")) is not None:
settings = replace(settings, diarize_timeout=float(v))
# Provider-specific diarize env aliases
if (v := os.environ.get("HF_TOKEN")) is not None:
settings = replace(settings, diarize_api_key=v)
if (v := os.environ.get("HUGGINGFACE_API_KEY")) is not None:
settings = replace(settings, diarize_api_key=v)
if (v := os.environ.get("ASSEMBLYAI_API_KEY")) is not None:
settings = replace(settings, diarize_api_key=v)
if (v := os.environ.get("DEEPGRAM_API_KEY")) is not None:
settings = replace(settings, diarize_api_key=v)
if (v := os.environ.get("HF_BASE_URL")) is not None:
settings = replace(settings, diarize_base_url=v)
if (v := os.environ.get("ASSEMBLYAI_BASE_URL")) is not None:
settings = replace(settings, diarize_base_url=v)
if (v := _env("TRIM_START")) is not None: # pragma: no cover - error branches, happy path tested via env test
try:
ts_env = float(v)
Expand Down Expand Up @@ -304,6 +369,20 @@ def load_settings(
settings = replace(settings, correct_names=correct_names)
if diarize is not None: # pragma: no cover - CLI tested via CliRunner
settings = replace(settings, diarize=diarize)
if diarize_backend is not None:
settings = replace(settings, diarize_backend=diarize_backend)
if diarize_model is not None:
settings = replace(settings, diarize_model=diarize_model)
if diarize_base_url is not None:
settings = replace(settings, diarize_base_url=diarize_base_url)
if diarize_api_key is not None:
settings = replace(settings, diarize_api_key=diarize_api_key)
if diarize_api_key_service is not None:
settings = replace(settings, diarize_api_key_service=diarize_api_key_service)
if diarize_api_key_account is not None:
settings = replace(settings, diarize_api_key_account=diarize_api_key_account)
if diarize_timeout is not None:
settings = replace(settings, diarize_timeout=diarize_timeout)
if trim_start is not None: # pragma: no cover - error branch, happy path tested via CLI flag test
ts_cli = float(trim_start)
if ts_cli < 0: # pragma: no cover
Expand Down
Loading
Loading