Skip to content

R2: Optional LLM dependency, configurable model, graceful no-LLM mode #83

@jmcte

Description

@jmcte

Goal

Make pip install mailplus-intelligence succeed without an LLM provider, and make every LLM-touching code path degrade gracefully when anthropic is absent or no API key is configured. Today the package imports anthropic from llm_extractor.py but does not declare it as a dependency, so a fresh install silently breaks LLM features.

This is R2 of six release milestones (R1–R6) leading to v0.1.0. Parent: see roadmap label release-v0.1.

Evidence

  • pyproject.toml:15dependencies = []
  • src/mailplus_intelligence/llm_extractor.py:119 — model literal claude-opus-4-7
  • src/mailplus_intelligence/llm_extractor.py constructs anthropic.Anthropic() at runtime; no guard for missing module or missing API key, no fallback to deterministic-only.

Acceptance criteria

Packaging

  • pyproject.toml declares anthropic (pinned to a known-working major) under an optional extra, e.g. [project.optional-dependencies] llm = ["anthropic>=0.40,<1.0"].
  • pip install mailplus-intelligence (no extras) installs successfully and the deterministic CLI flow (mpi search, mpi queue, mpi export, mpi doctor) works end-to-end without anthropic on the path.
  • pip install 'mailplus-intelligence[llm]' installs anthropic and unlocks LLM extraction.

Runtime guards

  • llm_extractor.py lazy-imports anthropic inside the call site, not at module top.
  • If anthropic is missing, raise a single LLMNotAvailable (or similar) with a message naming the install command (pip install 'mailplus-intelligence[llm]').
  • If ANTHROPIC_API_KEY is unset and no cassette is provided, raise the same / a sibling error with a clear next step. No silent crash from the SDK constructor.
  • Any caller that opted into LLM extraction (CLI, scheduler, etc.) catches LLMNotAvailable and either:
    • Falls back to deterministic-only with a warning printed once, or
    • Exits non-zero with the same actionable message — operator's choice via --llm {auto,off,required} flag (default auto = fall back).

Configurability

  • Model is sourced from (in order): --model CLI flag → MAILPLUS_LLM_MODEL env var → default constant in llm_extractor.py.
  • Default constant remains claude-opus-4-7 for v0.1 but lives in one named constant, not inline.
  • mpi doctor reports detected LLM availability, configured model, and whether an API key is present (without printing the key).

Tests

  • New tests/test_llm_extractor_no_anthropic.py (or equivalent) using sys.modules patching to prove the deterministic flow and the actionable error are correct when the SDK is absent.
  • Existing cassette-based LLM tests still pass.
  • Test asserting --llm off skips LLM calls even with everything installed.

Definition of done

  • All boxes above checked.
  • bash scripts/ci/run-fast-checks.sh green.
  • mpi doctor on a fresh pip install mailplus-intelligence (no extras) reports a clean "deterministic only" mode with a clear hint at how to enable LLM features.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions