Reconciled survivors from PR #5: /v1beta surface, SSRF guard, truncation recovery, script restore - #6
Merged
Conversation
…, SSRF guard, truncation recovery, model-resolution fix, start script vs luuquangvu/main: - app/server/gemini.py + app/models/gemini_models.py + app/models/__init__.py + app/main.py + app/server/middleware.py: native Gemini /v1beta REST surface (models, generateContent, streamGenerateContent, x-goog-api-key auth, Google error envelopes) - unobjected in review - app/utils/helper.py + app/utils/config.py + config/config.yaml: SSRF guard (reject_unsafe_url, allow_private_url_fetch, url_fetch_timeout); recovery_timeout knob - app/services/client.py + app/server/chat.py: truncated-stream recovery via LIST_CONVERSATION_TURNS polling; fix chat.py:688 calling nonexistent client.resolve_model (lib: _resolve_model_by_name) - scripts/start-gemini-api.sh: restore local start/stop helper (deleted in 71c0db2 with no replacement) - app/services/pool.py: extract _init_one/_init_attempt (behavior-preserving)
Ruff E402/SIM105 in app/main.py (self-introduced by the survivors main.py edit): the mimetypes loop ran before module imports and used try/except-pass. Imports first, loop after (still at import time, before any upload), no noqa needed. All 4 CI steps now pass: ruff check, ruff format, ty, pyright.
kahlos
added a commit
to kahlos/Gemini-FastAPI
that referenced
this pull request
Aug 14, 2026
Mirrors survivors-pr b1ed8ba (PR luuquangvu#6): the mimetypes loop ran before module imports (E402 x6) with try/except-pass (SIM105) - self-introduced by the C1 survivors main.py edit, mislabeled 'pre-existing' in LOGBOOK. Imports first, loop after (still at import time, before any upload). Local line now matches PR luuquangvu#6 exactly: ruff check/format, ty, pyright all pass.
kahlos
added a commit
to kahlos/Gemini-FastAPI
that referenced
this pull request
Aug 14, 2026
The 9fa9059 merge dropped the VideoGeneration branch from the tool categorization loop, silently discarding video_generation tools, and left build_video_generation_instruction (helper.py:834) without callers (PR luuquangvu#6 pruned it upstream as dead - correct on that surface). Restore: import, video_tools list + isinstance/dict branches, instruction nudge next to the image one (chat.py:2710). Live probe: 200, model replies with its known placeholder refusal - async server-side video completion on this account; URL retrieval from LIST_CONVERSATION_TURNS remains open (piece 2).
- removing _recover_stream_output, as it’s now handled upstream. - removing fetch_last_model_turn since it’s already included upstream. - eliminating the unnecessary looks_like_html function.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to the review on #5 — we've reconciled the fork integration with your comments (details below) and this PR carries only the items that were not objected to.
What changed vs your main
/v1betaREST surface (app/server/gemini.py,app/models/gemini_models.py,app/server/middleware.py::verify_gemini_api_key,app/main.pywiring):GET /v1beta/models,GET /v1beta/models/{model},POST ...:generateContent,...:streamGenerateContent,x-goog-api-keyauth, Google-style error envelopes. Independent of model discovery — resolves against your dynamicAvailableModelregistry via_resolve_model_name.app/utils/helper.py::reject_unsafe_url+allow_private_url_fetch/url_fetch_timeoutconfig): blocks private/loopback targets for server-side URL image fetches; DNS-rebind TOCTOU documented. Left as an opt-out knob.app/services/client.py::fetch_last_model_turn+app/server/chat.py::_recover_stream_output): when a stream dies mid-response, Gemini finalizes the turn server-side; this pollsLIST_CONVERSATION_TURNSfor the completed turn (new rcid) and streams the missing tail. Different scope from timeout/keepalive: it recovers content after a dead connection, not the connection itself. Gated byrecovery_timeout(default 60s, 0 disables).app/server/chat.py_resolve_model_namecalledclient.resolve_model(...), which does not exist on the wrapper or the library — the library exposes_resolve_model_by_name(sameValueErrorcontract). Renamed.scripts/start-gemini-api.shrestored — deleted in 71c0db2 with no replacement inscripts/USAGE.md; used for local start/stop/status.app/services/pool.py:_init_one/_init_attemptextraction (behavior-preserving).Reconciliation with your review (PR #5)
storage.max_sizeremains the customization knobwait_for, watchdog, refresh); C4 droppedlen <= 8); your attachment fallback covers the case; droppedconfig/config.yaml; our fork's gitignored real-credentials posture stays localAvailableModel; droppedvalidate_session_on_initWe have integrated 71c0db2 + c8e0a83 into our fork and validated it live (
/health,/v1/models, chat stream/non-stream,/v1betagenerateContent all 200; pyright 0/0/0, ruff baseline).