Skip to content

feat: select Gemini model in settings (#180)#320

Merged
hessius merged 2 commits intoversion/2.4.0from
feature/180-select-model
Mar 26, 2026
Merged

feat: select Gemini model in settings (#180)#320
hessius merged 2 commits intoversion/2.4.0from
feature/180-select-model

Conversation

@hessius
Copy link
Owner

@hessius hessius commented Mar 25, 2026

Summary

Adds a model selector dropdown to Settings allowing users to choose which Gemini model to use for AI profile generation, shot analysis, and recommendations.

Changes

Backend (Python/FastAPI):

  • Added geminiModel to settings schema with default gemini-2.5-flash
  • GET /api/settings returns current model (env var takes precedence)
  • POST /api/settings persists model choice and hot-reloads GEMINI_MODEL env var
  • Startup hydration maps stored geminiModelGEMINI_MODEL env var

Frontend (React/TypeScript):

  • Added GEMINI_MODEL key to STORAGE_KEYS constants
  • BrowserAIService: replaced hard-coded model with getGeminiModel() reading from localStorage
  • SettingsView: model selector dropdown (Gemini 2.5 Flash / 2.5 Pro / 2.0 Flash)
  • Works in both proxy mode (server settings) and direct mode (localStorage)

i18n: Translation keys added for all 6 locales (en, sv, de, es, fr, it)

Available Models

Model Best For
Gemini 2.5 Flash (default) Fast, balanced performance
Gemini 2.5 Pro Higher quality, slower
Gemini 2.0 Flash Legacy compatibility

Closes #180

Add model selector dropdown to Settings allowing users to choose between
Gemini 2.5 Flash, Gemini 2.5 Pro, and Gemini 2.0 Flash for AI operations.

Backend: geminiModel added to settings schema, GET/POST endpoints, and
hydrated into GEMINI_MODEL env var with hot-reload support.

Frontend: model selection persisted via localStorage (direct mode) or
POST /api/settings (proxy mode). BrowserAIService reads model dynamically.

i18n: translations added for all 6 locales (en, sv, de, es, fr, it).

Closes #180

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hessius hessius added this to the 2.4 milestone Mar 25, 2026
@hessius hessius requested a review from Copilot March 25, 2026 21:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-user configurability for which Gemini model MeticAI uses, wiring the selection through Settings (UI + persistence) and ensuring backend/runtime environment hydration supports it.

Changes:

  • Add geminiModel to persisted settings (default gemini-2.5-flash) and hydrate/hot-reload GEMINI_MODEL on the server.
  • Add a Gemini model selector to the Settings UI and persist the choice (server in proxy mode, localStorage in direct mode).
  • Add i18n keys across all locales for the new Settings text.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
apps/web/src/services/ai/BrowserAIService.ts Reads selected model from localStorage and uses it for Gemini calls in direct mode.
apps/web/src/lib/constants.ts Adds STORAGE_KEYS.GEMINI_MODEL for persisted model selection.
apps/web/src/components/SettingsView.tsx Adds Gemini model dropdown; loads/saves model in both proxy and direct modes.
apps/web/public/locales/en/translation.json Adds settings.geminiModel* i18n keys (and related settings keys updates).
apps/web/public/locales/sv/translation.json Adds settings.geminiModel* i18n keys (and related settings keys updates).
apps/web/public/locales/de/translation.json Adds settings.geminiModel* i18n keys (and related settings keys updates).
apps/web/public/locales/es/translation.json Adds settings.geminiModel* i18n keys (and related settings keys updates).
apps/web/public/locales/fr/translation.json Adds settings.geminiModel* i18n keys (and related settings keys updates).
apps/web/public/locales/it/translation.json Adds settings.geminiModel* i18n keys (and related settings keys updates).
apps/server/services/settings_service.py Adds geminiModel default to settings schema.
apps/server/main.py Hydrates GEMINI_MODEL from stored settings on startup.
apps/server/api/routes/system.py Returns geminiModel (env precedence) and persists/hot-reloads it on save.

Comment on lines +988 to +990
<option value="gemini-2.5-flash">Gemini 2.5 Flash</option>
<option value="gemini-2.5-pro">Gemini 2.5 Pro</option>
<option value="gemini-2.0-flash">Gemini 2.0 Flash</option>
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Gemini model dropdown option labels are hard-coded strings. Repo convention requires all user-facing strings to go through t() (see .github/CONVENTIONS.md i18n section). Add i18n keys for each option label (or otherwise route these labels through t()), even if the underlying value stays as the raw model id.

Suggested change
<option value="gemini-2.5-flash">Gemini 2.5 Flash</option>
<option value="gemini-2.5-pro">Gemini 2.5 Pro</option>
<option value="gemini-2.0-flash">Gemini 2.0 Flash</option>
<option value="gemini-2.5-flash">{t('settings.geminiModelOptionFlash25')}</option>
<option value="gemini-2.5-pro">{t('settings.geminiModelOptionPro25')}</option>
<option value="gemini-2.0-flash">{t('settings.geminiModelOptionFlash20')}</option>

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 5e51ea9. Wrapped all three Gemini model dropdown labels in t() with keys settings.geminiModel25Flash, settings.geminiModel25Pro, and settings.geminiModel20Flash. Added corresponding translations to all 6 locale files (en, sv, de, es, fr, it).

Comment on lines 1262 to 1265
current_settings["geminiModel"] = str(body["geminiModel"]).strip()

# For IP and API key changes, also update .env file
env_updated = False
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

geminiModel from the request body is persisted as a raw stripped string, which allows an empty string to be written to settings.json. Treat blank/whitespace-only values as “unset” (e.g., delete the key or set the default) to avoid storing an invalid/ambiguous model value.

Suggested change
current_settings["geminiModel"] = str(body["geminiModel"]).strip()
# For IP and API key changes, also update .env file
env_updated = False
gemini_model = str(body["geminiModel"]).strip()
if gemini_model:
# Persist only non-empty model values
current_settings["geminiModel"] = gemini_model
else:
# Treat blank/whitespace-only input as "unset"

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 5e51ea9. Empty/whitespace-only geminiModel now falls back to the default gemini-2.5-flash instead of persisting an empty string.

Comment on lines +1439 to +1447
# Hot-reload Gemini model into process environment
if "geminiModel" in body:
new_model = str(body["geminiModel"]).strip()
if new_model:
os.environ["GEMINI_MODEL"] = new_model
_update_s6_env("GEMINI_MODEL", new_model, request_id)
services_restarted.append("gemini_model")
logger.info("Updated GEMINI_MODEL to %s", new_model,
extra={"request_id": request_id})
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New Gemini model selection behavior in GET/POST /api/settings is not covered by existing endpoint tests (there are tests for /api/settings, but none assert geminiModel env precedence or persistence/hot-reload). Please add tests in apps/server/test_main.py for: GET returning env override; POST updating stored settings and os.environ['GEMINI_MODEL'] (and/or s6 env update call).

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 5e51ea9. Added 3 tests: model persistence round-trip, empty-string fallback to default, and geminiModel presence in GET response. All 824 backend tests passing.

- i18n: wrap Gemini model dropdown labels in t() with keys for all 6 locales
- validation: treat empty/whitespace geminiModel as default (gemini-2.5-flash)
- tests: add 3 backend tests for geminiModel persistence and retrieval

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hessius
Copy link
Owner Author

hessius commented Mar 25, 2026

Review Findings Addressed

  • i18n for model labels — Wrapped all 3 dropdown options in t(), added keys to all 6 locale files
  • Empty string validation — Blank/whitespace geminiModel now falls back to gemini-2.5-flash
  • Backend tests added — 3 new tests: persistence round-trip, empty fallback, presence in GET

Issue #180 completeness: All acceptance criteria met — model selector visible in Settings, works in proxy and direct mode, selected model used for all AI operations, default model set. Ready to merge.

@hessius hessius merged commit 98eba1e into version/2.4.0 Mar 26, 2026
6 checks passed
@hessius hessius deleted the feature/180-select-model branch March 26, 2026 05:55
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