The initial Gemini backend defaulted to 2.5 Flash, but large semantic extraction chunks can benefit from newer models and more output headroom. Move the default to Gemini 3 Flash Preview, add CLI and environment model overrides, and increase the Gemini completion budget while keeping low reasoning effort for cost control. Constraint: Google exposes Gemini through an OpenAI-compatible chat-completions endpoint Rejected: Hardcode Gemini 3.1 Pro as the default | higher cost for routine repository indexing Confidence: medium Scope-risk: narrow Directive: Keep --model and GRAPHIFY_GEMINI_MODEL working before changing Gemini defaults again Tested: uv run --directory vendor/graphify pytest tests/test_llm_backends.py tests/test_chunking.py -q Not-tested: Live Gemini 3 extraction on the full cloud-edge repo before this commit
98 lines
3.4 KiB
Python
98 lines
3.4 KiB
Python
"""Tests for direct semantic-extraction backend selection."""
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from graphify import llm
|
|
|
|
|
|
def _clear_backend_env(monkeypatch):
|
|
for env_key in (
|
|
"GEMINI_API_KEY",
|
|
"GOOGLE_API_KEY",
|
|
"MOONSHOT_API_KEY",
|
|
"ANTHROPIC_API_KEY",
|
|
"OPENAI_API_KEY",
|
|
):
|
|
monkeypatch.delenv(env_key, raising=False)
|
|
|
|
|
|
def test_gemini_accepts_gemini_api_key(monkeypatch):
|
|
_clear_backend_env(monkeypatch)
|
|
monkeypatch.setenv("GEMINI_API_KEY", "gemini-key")
|
|
|
|
assert llm.detect_backend() == "gemini"
|
|
assert llm._get_backend_api_key("gemini") == "gemini-key"
|
|
|
|
|
|
def test_gemini_accepts_google_api_key(monkeypatch):
|
|
_clear_backend_env(monkeypatch)
|
|
monkeypatch.setenv("GOOGLE_API_KEY", "google-key")
|
|
|
|
assert llm.detect_backend() == "gemini"
|
|
assert llm._get_backend_api_key("gemini") == "google-key"
|
|
|
|
|
|
def test_backend_detection_prefers_gemini(monkeypatch):
|
|
_clear_backend_env(monkeypatch)
|
|
monkeypatch.setenv("OPENAI_API_KEY", "openai-key")
|
|
monkeypatch.setenv("ANTHROPIC_API_KEY", "anthropic-key")
|
|
monkeypatch.setenv("MOONSHOT_API_KEY", "moonshot-key")
|
|
monkeypatch.setenv("GEMINI_API_KEY", "gemini-key")
|
|
|
|
assert llm.detect_backend() == "gemini"
|
|
|
|
|
|
def test_openai_backend_detected(monkeypatch):
|
|
_clear_backend_env(monkeypatch)
|
|
monkeypatch.setenv("OPENAI_API_KEY", "openai-key")
|
|
|
|
assert llm.detect_backend() == "openai"
|
|
assert llm._get_backend_api_key("openai") == "openai-key"
|
|
|
|
|
|
def test_extract_files_direct_routes_gemini_through_openai_compat(tmp_path, monkeypatch):
|
|
_clear_backend_env(monkeypatch)
|
|
monkeypatch.setenv("GOOGLE_API_KEY", "google-key")
|
|
source = tmp_path / "note.md"
|
|
source.write_text("# Architecture\n\nThe runner emits a snapshot.\n")
|
|
result = {"nodes": [], "edges": [], "hyperedges": [], "input_tokens": 1, "output_tokens": 1}
|
|
|
|
with patch("graphify.llm._call_openai_compat", return_value=result) as call:
|
|
assert llm.extract_files_direct([source], backend="gemini", root=tmp_path) is result
|
|
|
|
assert call.call_args.args[:4] == (
|
|
"https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
"google-key",
|
|
"gemini-3-flash-preview",
|
|
"=== note.md ===\n# Architecture\n\nThe runner emits a snapshot.\n",
|
|
)
|
|
assert call.call_args.kwargs["temperature"] == 0
|
|
assert call.call_args.kwargs["reasoning_effort"] == "low"
|
|
assert call.call_args.kwargs["max_completion_tokens"] == 16384
|
|
|
|
|
|
def test_gemini_model_can_be_overridden_by_env(tmp_path, monkeypatch):
|
|
_clear_backend_env(monkeypatch)
|
|
monkeypatch.setenv("GOOGLE_API_KEY", "google-key")
|
|
monkeypatch.setenv("GRAPHIFY_GEMINI_MODEL", "gemini-3.1-pro-preview")
|
|
source = tmp_path / "note.md"
|
|
source.write_text("# Architecture\n")
|
|
result = {"nodes": [], "edges": [], "hyperedges": [], "input_tokens": 1, "output_tokens": 1}
|
|
|
|
with patch("graphify.llm._call_openai_compat", return_value=result) as call:
|
|
llm.extract_files_direct([source], backend="gemini", root=tmp_path)
|
|
|
|
assert call.call_args.args[2] == "gemini-3.1-pro-preview"
|
|
|
|
|
|
def test_missing_gemini_key_names_both_supported_env_vars(monkeypatch):
|
|
_clear_backend_env(monkeypatch)
|
|
|
|
with pytest.raises(ValueError) as exc:
|
|
llm.extract_files_direct([Path("missing.md")], backend="gemini")
|
|
|
|
assert "GEMINI_API_KEY or GOOGLE_API_KEY" in str(exc.value)
|