fix: streaming scroll override, Gemini 3.x models, read-only workspace, two-container UID — v0.50.87 (closes #677 #669 #670 #668)
- #677: renderMessages() and appendThinking() use scrollIfPinned() during stream; scroll threshold 80→150px; floating ↓ scroll-to-bottom button added - #669: Gemini 3.1 Pro Preview, 3 Flash Preview, 3.1 Flash Lite Preview added to all provider sections; gemini-3.1-flash-lite-preview was the missing ID causing API_KEY_INVALID; GEMINI_API_KEY env var detection added - #670: docker_init.bash guards chown/write-test with [ -w ]; :ro workspace mounts no longer crash startup - #668: UID/GID auto-detect probes /home/hermeswebui/.hermes and HERMES_HOME before /workspace; two-container Zeabur/Compose setups inherit correct UID automatically - 18 new tests; 1441 total passing
This commit is contained in:
@@ -230,3 +230,88 @@ class TestLiveModelFetching:
|
||||
assert live_route_pos > handle_get_pos, (
|
||||
"/api/models/live must be inside handle_get() (#375)"
|
||||
)
|
||||
|
||||
|
||||
# ── #669: Gemini model IDs must be valid for Google AI Studio endpoint ────────
|
||||
|
||||
class TestGeminiModelIds:
|
||||
"""Gemini 3.x model IDs must be valid for the native Google AI Studio provider.
|
||||
|
||||
The original code had gemini-3.1-flash-lite-preview missing from the
|
||||
dropdown. The fallback list also erroneously used gemini-3.1-pro-preview
|
||||
in some provider sections while omitting gemini-3.1-flash-lite-preview.
|
||||
All provider sections must now include the full current Gemini 3.x lineup.
|
||||
"""
|
||||
|
||||
VALID_GEMINI_3 = [
|
||||
"gemini-3.1-pro-preview",
|
||||
"gemini-3-flash-preview",
|
||||
"gemini-3.1-flash-lite-preview",
|
||||
]
|
||||
|
||||
def test_gemini_provider_models_has_3x(self):
|
||||
"""_PROVIDER_MODELS['gemini'] must contain valid Gemini 3.x model IDs (#669)."""
|
||||
gemini_block_start = CONFIG_PY.find('"gemini": [')
|
||||
assert gemini_block_start != -1, "_PROVIDER_MODELS['gemini'] block not found"
|
||||
gemini_block = CONFIG_PY[gemini_block_start:gemini_block_start + 600]
|
||||
for mid in self.VALID_GEMINI_3:
|
||||
assert mid in gemini_block, (
|
||||
f"_PROVIDER_MODELS['gemini'] must contain {mid!r} — "
|
||||
f"this is a valid Google AI Studio model ID (#669)"
|
||||
)
|
||||
|
||||
def test_gemini_provider_models_has_flash_lite(self):
|
||||
"""_PROVIDER_MODELS['gemini'] must contain gemini-3.1-flash-lite-preview (#669).
|
||||
|
||||
This was the model the reporter selected from the wizard — it must appear
|
||||
in the native gemini provider model list so users can select it.
|
||||
"""
|
||||
gemini_block_start = CONFIG_PY.find('"gemini": [')
|
||||
assert gemini_block_start != -1
|
||||
gemini_block = CONFIG_PY[gemini_block_start:gemini_block_start + 600]
|
||||
assert "gemini-3.1-flash-lite-preview" in gemini_block, (
|
||||
"_PROVIDER_MODELS['gemini'] missing gemini-3.1-flash-lite-preview — "
|
||||
"this was the exact model the #669 reporter tried and got API_KEY_INVALID"
|
||||
)
|
||||
|
||||
def test_fallback_models_has_gemini_3x(self):
|
||||
"""_FALLBACK_MODELS must contain valid Gemini 3.x OpenRouter model IDs (#669)."""
|
||||
fallback_start = CONFIG_PY.find("_FALLBACK_MODELS = [")
|
||||
fallback_end = CONFIG_PY.find("]", fallback_start + len("_FALLBACK_MODELS = ["))
|
||||
# Find the closing bracket for the list (multi-line)
|
||||
depth = 0
|
||||
pos = fallback_start + len("_FALLBACK_MODELS = [")
|
||||
for i, ch in enumerate(CONFIG_PY[pos:], start=pos):
|
||||
if ch == '[':
|
||||
depth += 1
|
||||
elif ch == ']':
|
||||
if depth == 0:
|
||||
fallback_end = i
|
||||
break
|
||||
depth -= 1
|
||||
fallback_block = CONFIG_PY[fallback_start:fallback_end]
|
||||
for mid in ("google/gemini-3.1-pro-preview", "google/gemini-3-flash-preview"):
|
||||
assert mid in fallback_block, (
|
||||
f"_FALLBACK_MODELS must contain {mid!r} for OpenRouter Google models (#669)"
|
||||
)
|
||||
|
||||
def test_gemini_provider_also_has_stable_25(self):
|
||||
"""_PROVIDER_MODELS['gemini'] must retain stable Gemini 2.5 models (#669)."""
|
||||
gemini_block_start = CONFIG_PY.find('"gemini": [')
|
||||
assert gemini_block_start != -1
|
||||
gemini_block = CONFIG_PY[gemini_block_start:gemini_block_start + 600]
|
||||
assert "gemini-2.5-pro" in gemini_block, (
|
||||
"_PROVIDER_MODELS['gemini'] must keep gemini-2.5-pro as a stable fallback"
|
||||
)
|
||||
|
||||
def test_no_invalid_gemini_3_pro_model(self):
|
||||
"""gemini-3-pro-preview must not appear — it was shut down March 9 2026 (#669)."""
|
||||
assert "gemini-3-pro-preview" not in CONFIG_PY or "gemini-3.1-pro-preview" in CONFIG_PY, (
|
||||
"gemini-3-pro-preview was shut down — use gemini-3.1-pro-preview instead (#669)"
|
||||
)
|
||||
# More precise: ensure the bare (non-.1) version isn't the only one present
|
||||
count_bare = CONFIG_PY.count('"gemini-3-pro-preview"')
|
||||
assert count_bare == 0, (
|
||||
f"gemini-3-pro-preview appears {count_bare} time(s) in config.py — "
|
||||
"it was shut down March 9 2026, use gemini-3.1-pro-preview (#669)"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user