Skip to content

feat: add optional llm extra with OpenRouter client - #25

Open
m-messer wants to merge 1 commit into
cli-groupfrom
llm-client
Open

m-messer wants to merge 1 commit into
cli-groupfrom
llm-client

Conversation

@m-messer

@m-messer m-messer commented Sep 1, 2026

Copy link
Copy Markdown
Member

New in2lambda.llm package behind a Poetry llm extra (pip install 'in2lambda[llm]'openai, python-dotenv, pydantic):

  • client.get_client() returns an openai.OpenAI pointed at https://openrouter.ai/api/v1, keyed by $OPENROUTER_API_KEY (loaded from a .env file if present). Clear RuntimeError when the extra is missing or the key is unset.
  • client.resolve_model() picks the model slug: --model value, else $IN2LAMBDA_MODEL, else DEFAULT_MODEL (openai/gpt-4o-mini).

The package imports cleanly without the extra (openai/dotenv guarded), so --doctest-modules still works on a bare install. CI now installs --all-extras. Tests are fully offline.

openai floor is ^1.55 — older releases resolve against a too-new httpx and break with a proxies TypeError.


Stack: cli-group ← llm-client ← mathpix ← wizard-command
Base: cli-group#24.

🤖 Generated with Claude Code

New in2lambda.llm package behind a Poetry `llm` extra
(pip install 'in2lambda[llm]' -> openai, python-dotenv, pydantic):

- client.get_client() returns an openai.OpenAI pointed at
  https://openrouter.ai/api/v1, keyed by $OPENROUTER_API_KEY (loaded from a
  .env file if present). Raises a clear RuntimeError when the extra is
  missing or the key is unset.
- client.resolve_model() picks the model slug: --model value, else
  $IN2LAMBDA_MODEL, else DEFAULT_MODEL (openai/gpt-4o-mini).

The package imports cleanly without the extra (openai/dotenv guarded), so
--doctest-modules still works on a bare install. CI now installs
--all-extras so the llm code is covered. Tests are fully offline.

openai floor is ^1.55 - older releases resolve against a too-new httpx and
break with a `proxies` TypeError.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017VXb8aZqgFBjoeuuddjW6r
@peterbjohnson

Copy link
Copy Markdown
Member

One real bug here, in .env handling.

get_client() calls load_dotenv() from inside the library (client.py:66). python-dotenv looks for the file by walking up from the calling module's directory, so once in2lambda is installed normally, it searches inside site-packages and never sees the user's .env. It only appears to work in a development checkout, where the package happens to sit under the project folder.

Minimal reproduction, with a library in one folder and a project in another:

project/.env            -> DEMO_KEY=from_project_dotenv
project/run_app.py      -> from libpkg import get_key; print(get_key())
site_packages/libpkg/   -> calls load_dotenv() then os.getenv("DEMO_KEY")

$ cd project && PYTHONPATH=../site_packages python3 run_app.py
key seen by library: None

find_dotenv(usecwd=True) finds it; the default doesn't. (It's easy to convince yourself otherwise from a REPL or python -c, where python-dotenv falls back to the working directory.)

This matters because the wizard docs in the next PR tell people a .env in the working directory is picked up automatically, which is exactly the case that fails.

Suggestions: load the .env once in the CLI entry point with usecwd=True, or drop python-dotenv and let click read the key via envvar=. Loading it from library code also changes the whole process's environment, which is a bit surprising for anyone importing in2lambda as a library.

Smaller points: resolve_model() duplicates what click's envvar=/default= already do on the --model option, and openai = "^1.55" caps us below openai 2.x, which will bite anyone combining in2lambda with a newer openai.

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