Skip to content

feat: add Google Gemini provider via OpenAI-compatible endpoint#5

Open
anonymort wants to merge 14 commits intoShinMegamiBoson:mainfrom
anonymort:feat/gemini-provider
Open

feat: add Google Gemini provider via OpenAI-compatible endpoint#5
anonymort wants to merge 14 commits intoShinMegamiBoson:mainfrom
anonymort:feat/gemini-provider

Conversation

@anonymort
Copy link

Summary

  • Adds Google Gemini as a 5th provider — zero new model classes
  • Reuses OpenAICompatibleModel pointed at Google's official OpenAI-compatible endpoint (https://generativelanguage.googleapis.com/v1beta/openai) with strict_tools=False
  • Supports OPENPLANTER_GEMINI_API_KEY, GEMINI_API_KEY, and GOOGLE_API_KEY env var fallback chain

Files Changed

File Change
agent/credentials.py gemini_api_key in CredentialBundle (7 locations); env var priority chain
agent/config.py gemini_api_key, gemini_base_url on AgentConfig; "gemini" in PROVIDER_DEFAULT_MODELS
agent/builder.py _GEMINI_RE, infer_provider_for_model, build_model_factory, build_engine, factory guard
agent/engine.py _model_tier keyword mapping; _lowest_tier_model Gemini branch; 5 _MODEL_CONTEXT_WINDOWS entries
agent/model.py EchoFallbackModel.note mentions Gemini
tests/test_gemini.py 28 tests, no live API calls
docs/plans/2026-02-20-gemini-provider-design.md Design doc

How to Use

export GEMINI_API_KEY=AIzaSy-...

# By provider (uses gemini-2.5-flash by default)
openplanter --provider gemini

# By model name (provider inferred from prefix)
openplanter --model gemini-2.5-pro

Key Design Decisions

  • strict_tools=False — Google's compat layer doesn't enforce OpenAI strict mode schemas; must be set explicitly in both build_engine and build_model_factory
  • Keyword tier mapping"pro"→1, "lite"→3, else→2; version-agnostic so gemini-3-pro works without a code change
  • Regex ^gemini- — hyphen required to prevent false matches on hypothetical geminimax etc.
  • GOOGLE_API_KEY fallback — picks up keys already set in Google AI Studio environments

Pre-implementation Opus Review

All findings addressed before writing a line of code. See design doc for full table.

Test Results

438 passed, 4 failed (all 4 pre-existing, unrelated to this PR)
tests/test_gemini.py: 28 passed
ruff: clean
mypy: no errors

Add Apache 2.0 license file, contribution guide with code of conduct,
and [project.optional-dependencies] dev group (pytest, mypy, ruff).
Update README to reference the new files.
Ruff violations (12 fixed):
- N806: Rename _PARALLEL_TOOLS to parallel_tools (engine.py)
- B007: Rename unused loop var attempt to _attempt (model.py)
- RUF012: Add ClassVar annotation to mutable class attribute (tui.py)
- F841: Remove unused fed variable (cast_to_video.py)
- C408: Convert dict() calls to dict literals (5 files)
- RUF002: Replace × with x in docstring (test_integration.py)
- RUF005: Use iterable unpacking instead of concatenation (test_replay_log.py)
- RUF059: Prefix unused unpacked variable with underscore (test_settings.py)
- RUF015: Replace slice[0] with next() (test_tool_defs.py)

Mypy errors (19 fixed):
- Add type parameters to untyped dicts (settings.py, builder.py, tools.py)
- Add missing return type annotations (tools.py, tui.py)
- Fix incompatible type assignments (tools.py, demo.py, builder.py, engine.py)
- Fix Generator import from collections.abc (tools.py)
- Add TextIO type annotation (tools.py)
- Add cast for json.loads return value (runtime.py)
- Add type annotations for Rich integration (tui.py)
- Remove unused type: ignore comments (tools.py, engine.py, tui.py)
- Keep necessary type: ignore for Rich library compatibility (engine.py, tui.py)

All checks now pass:
- ruff: 0 violations
- mypy: 0 errors
- pytest: 412 passing tests
Replaces the 260-line if/elif chain in _apply_tool_call with a dict
registry pattern. Each stateless tool gets a dedicated _handle_* method;
subtask/execute are extracted into _apply_subtask/_apply_execute. Zero
behavior change — all argument extraction and validation logic is
copied verbatim from the original branches.
@anonymort
Copy link
Author

Again largely vibe coded - caution

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.

1 participant