Skip to content

Conversation

matdev83
Copy link
Owner

@matdev83 matdev83 commented Oct 12, 2025

Summary

  • close the AuthorizedSession used by the Gemini Cloud Project connector during project validation, health checks, and chat completion flows so HTTP resources are released
  • ensure streaming responses close the session when the generator finishes
  • add regression tests that confirm sessions are closed after validation and health check code paths

Testing

  • pytest --override-ini addopts="" tests/unit/connectors/test_gemini_cloud_project_resource_management.py
  • pytest --override-ini addopts="" (fails: missing pytest_asyncio, respx, pytest_httpx)

https://chatgpt.com/codex/tasks/task_e_68ec2685724083339dafb8594c547032

Summary by CodeRabbit

  • Bug Fixes

    • Improved session lifecycle management to ensure connections are reliably closed after validations, health checks, and both standard and streaming requests, including generator-driven streams and error paths.
    • Added safe, non-fatal cleanup to prevent resource leaks and enhance stability.
  • Tests

    • Added unit tests to verify sessions are closed after project validation and health checks, validating robust resource management.

@coderabbitai
Copy link

coderabbitai bot commented Oct 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds explicit ADC-authenticated session management and safe cleanup (using try/finally and contextlib.suppress) across project validation, health checks, and both standard and streaming chat completion flows; adds unit tests asserting sessions are closed.

Changes

Cohort / File(s) Summary
Connector session lifecycle handling
src/connectors/gemini_cloud_project.py
Introduces local auth_session variables across _validate_project_access, _perform_health_check, _chat_completions_standard, and _chat_completions_streaming; ensures auth_session.close() is called in finally blocks wrapped with contextlib.suppress(Exception); ties stream generator lifecycle to session cleanup. No public API changes.
Unit tests for resource management
tests/unit/connectors/test_gemini_cloud_project_resource_management.py
Adds tests with _DummyResponse and _DummySession helpers plus a connector fixture; tests verify sessions returned by _get_adc_authorized_session are closed after _validate_project_access() and _perform_health_check(), mocking ADC session creation and HTTP calls.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant Connector
  participant ADC as ADC Auth
  participant API as Gemini API

  rect rgba(230,240,255,0.5)
    note over Connector: _validate_project_access
    Caller->>Connector: _validate_project_access()
    Connector->>ADC: _get_adc_authorized_session()
    ADC-->>Connector: auth_session
    Connector->>API: GET project info (auth_session)
    API-->>Connector: project details
    alt finally
      note right of Connector: suppress errors on close
      Connector->>ADC: auth_session.close()
    end
    Connector-->>Caller: result/exception
  end

  rect rgba(235,255,235,0.5)
    note over Connector: _perform_health_check
    Caller->>Connector: _perform_health_check()
    Connector->>ADC: _get_adc_authorized_session()
    ADC-->>Connector: auth_session
    Connector->>API: GET /health (auth_session)
    API-->>Connector: 200 OK / error
    note right of Connector: suppress errors on close
    Connector->>ADC: auth_session.close()
    Connector-->>Caller: True/False
  end

  rect rgba(255,245,230,0.5)
    note over Connector: _chat_completions_standard
    Caller->>Connector: completions(...)
    Connector->>ADC: _get_adc_authorized_session()
    ADC-->>Connector: auth_session
    Connector->>API: POST /chat (auth_session)
    API-->>Connector: response
    note right of Connector: suppress errors on close
    Connector->>ADC: auth_session.close()
    Connector-->>Caller: completion
  end

  rect rgba(255,240,245,0.5)
    note over Connector: _chat_completions_streaming
    Caller->>Connector: streaming_completions(...)
    Connector->>ADC: _get_adc_authorized_session()
    ADC-->>Connector: auth_session
    Connector->>API: POST /chat:stream (auth_session)
    API-->>Connector: stream
    loop generator yields
      Connector-->>Caller: chunk
    end
    note right of Connector: on generator end/error, suppress close errors
    Connector->>ADC: auth_session.close()
    Connector-->>Caller: done
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hop through sessions, snug and neat,
Close every door so no leaks meet.
Health checks hum and streams comply,
Tokens refreshed as I wink an eye.
A tidy trail—then I leap high. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the core change of the pull request—ensuring that Gemini Cloud Project sessions are properly closed—and directly reflects the main objective without extraneous detail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/fix-improper-resource-management-bug-qmjf4d

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09d3d9c and a5e4c53.

📒 Files selected for processing (1)
  • src/connectors/gemini_cloud_project.py (9 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Avoid using emojis in Python code comments or docstrings
Follow PEP 8 and use type hints for all functions
Import order must be: standard library, third-party, then local imports, separated by blank lines
Naming conventions: snake_case for variables and functions; PascalCase for classes
Error handling: use specific exceptions and include meaningful error messages
Prefer f-strings for string formatting

Files:

  • src/connectors/gemini_cloud_project.py
src/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.py: Catch specific exceptions; avoid broad except Exception blocks
If a broad exception must be caught, log with exc_info=True and re-raise a specific custom exception
Use the most specific exception class from src.core.common.exceptions that accurately describes the error
Provide clear, helpful error messages and include relevant details in the details dictionary of custom exceptions

Files:

  • src/connectors/gemini_cloud_project.py
🔇 Additional comments (5)
src/connectors/gemini_cloud_project.py (5)

46-46: LGTM!

The contextlib import is correctly added to support safe cleanup operations using contextlib.suppress(Exception) in the finally blocks.


875-933: LGTM!

The session lifecycle is correctly managed:

  • Session initialization at line 877 is safe (just setting to None)
  • Actual session creation at line 890 occurs inside the try block (starting at line 878)
  • Finally block at lines 930-933 properly checks for None and uses contextlib.suppress for safe cleanup

1049-1210: LGTM!

The session lifecycle is correctly managed:

  • Session initialization at line 1057 (setting to None)
  • Session creation at line 1059 occurs inside the try block (starting at line 1058)
  • Finally block at lines 1207-1210 properly checks for None and uses contextlib.suppress for safe cleanup

1212-1429: LGTM! Session lifecycle correctly delegated to generator.

The session management is well-structured:

  • Session creation at line 1223 occurs inside the try block (starting at line 1222)
  • The stream_prepared flag at line 1221 tracks whether the generator was successfully created
  • Finally block at lines 1426-1429 only closes the session if the stream was NOT prepared, correctly delegating cleanup to the generator when it exists
  • The generator's finally block (lines 1403-1408) handles session closure after streaming completes

1287-1408: LGTM! Generator cleanup is robust.

The finally block at lines 1403-1408 correctly handles cleanup:

  • Closes the streaming response at lines 1404-1405
  • Closes the auth_session at lines 1406-1408 using contextlib.suppress for safe cleanup
  • Python's generator semantics ensure the finally block executes even if the generator is garbage collected before full consumption

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

Comment on lines 1057 to 1062
auth_session = self._get_adc_authorized_session()
try:
# Use ADC for API calls (matches gemini CLI behavior for project-id auth)
auth_session = self._get_adc_authorized_session()

# Ensure project is onboarded for standard-tier
project_id = await self._ensure_project_onboarded(auth_session)

Choose a reason for hiding this comment

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

P1 Badge Keep ADC session creation inside error-handling block

Moving auth_session = self._get_adc_authorized_session() outside the surrounding try means any failure in acquiring Application Default Credentials (for example google.auth.exceptions.DefaultCredentialsError) will now escape without being logged or wrapped in the function’s BackendError fallback. Callers of _chat_completions_standard previously only saw BackendError/AuthenticationError, so this regression will leak raw exceptions and bypass the existing error logging. Consider creating the session inside the try and closing it in finally with a guard so the original error handling remains intact.

Useful? React with 👍 / 👎.

Comment on lines 1218 to 1224
auth_session = self._get_adc_authorized_session()
stream_prepared = False
try:
# Use ADC for streaming API calls
auth_session = self._get_adc_authorized_session()

# Ensure project is onboarded for standard-tier
project_id = await self._ensure_project_onboarded(auth_session)

Choose a reason for hiding this comment

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

P1 Badge Streaming path now bypasses error wrapping for ADC session failures

The streaming variant now obtains the ADC AuthorizedSession before entering the try/except. If _get_adc_authorized_session() raises (e.g. missing credentials or networking errors), the exception bypasses the function’s error logging and conversion to BackendError, and will propagate as a raw Exception. This diverges from prior behaviour and from the non-streaming code paths that callers rely on for consistent error semantics. Acquire the session inside the try and keep the finally guarded so it is still closed when an error occurs.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 326702b and 09d3d9c.

📒 Files selected for processing (2)
  • src/connectors/gemini_cloud_project.py (9 hunks)
  • tests/unit/connectors/test_gemini_cloud_project_resource_management.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Avoid using emojis in Python code comments or docstrings
Follow PEP 8 and use type hints for all functions
Import order must be: standard library, third-party, then local imports, separated by blank lines
Naming conventions: snake_case for variables and functions; PascalCase for classes
Error handling: use specific exceptions and include meaningful error messages
Prefer f-strings for string formatting

Files:

  • tests/unit/connectors/test_gemini_cloud_project_resource_management.py
  • src/connectors/gemini_cloud_project.py
src/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.py: Catch specific exceptions; avoid broad except Exception blocks
If a broad exception must be caught, log with exc_info=True and re-raise a specific custom exception
Use the most specific exception class from src.core.common.exceptions that accurately describes the error
Provide clear, helpful error messages and include relevant details in the details dictionary of custom exceptions

Files:

  • src/connectors/gemini_cloud_project.py
🧬 Code graph analysis (2)
tests/unit/connectors/test_gemini_cloud_project_resource_management.py (2)
src/connectors/gemini_cloud_project.py (2)
  • _validate_project_access (797-842)
  • _perform_health_check (875-933)
src/core/services/translation_service.py (1)
  • TranslationService (20-636)
src/connectors/gemini_cloud_project.py (5)
tests/unit/connectors/test_gemini_cloud_project_resource_management.py (1)
  • close (26-27)
src/connectors/gemini_oauth_personal.py (1)
  • stream_generator (1620-1782)
src/connectors/gemini.py (1)
  • stream_generator (292-322)
src/core/domain/responses.py (1)
  • StreamingResponseEnvelope (27-53)
tests/unit/openai_connector_tests/test_streaming_response.py (1)
  • content (57-58)
🪛 Ruff (0.13.3)
tests/unit/connectors/test_gemini_cloud_project_resource_management.py

49-49: Unused method argument: args

(ARG002)


49-49: Unused method argument: kwargs

(ARG002)


73-73: Possible hardcoded password assigned to: "token"

(S105)


75-75: Unused method argument: request

(ARG002)


76-76: Possible hardcoded password assigned to: "token"

(S105)


83-83: Unused function argument: url

(ARG001)


83-83: Unused function argument: headers

(ARG001)


83-83: Unused function argument: timeout

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test (3.10)

Comment on lines +30 to +41
@pytest.fixture()
def connector() -> GeminiCloudProjectConnector:
cfg = AppConfig()
client = httpx.AsyncClient()
backend = GeminiCloudProjectConnector(
client,
cfg,
translation_service=TranslationService(),
gcp_project_id="test-project",
)
backend.gemini_api_base_url = "https://example.com"
return backend
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ensure the AsyncClient is closed in the fixture

httpx.AsyncClient keeps background resources alive until aclose() runs; leaving it open in the fixture causes resource leaks and runtime warnings after the tests complete. Convert the fixture to an async fixture that async with-closes the client so each test frees the session.

-@pytest.fixture()
-def connector() -> GeminiCloudProjectConnector:
-    cfg = AppConfig()
-    client = httpx.AsyncClient()
-    backend = GeminiCloudProjectConnector(
-        client,
-        cfg,
-        translation_service=TranslationService(),
-        gcp_project_id="test-project",
-    )
-    backend.gemini_api_base_url = "https://example.com"
-    return backend
+@pytest_asyncio.fixture()
+async def connector() -> GeminiCloudProjectConnector:
+    cfg = AppConfig()
+    async with httpx.AsyncClient() as client:
+        backend = GeminiCloudProjectConnector(
+            client,
+            cfg,
+            translation_service=TranslationService(),
+            gcp_project_id="test-project",
+        )
+        backend.gemini_api_base_url = "https://example.com"
+        yield backend

Remember to import pytest_asyncio alongside the other pytest imports.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.fixture()
def connector() -> GeminiCloudProjectConnector:
cfg = AppConfig()
client = httpx.AsyncClient()
backend = GeminiCloudProjectConnector(
client,
cfg,
translation_service=TranslationService(),
gcp_project_id="test-project",
)
backend.gemini_api_base_url = "https://example.com"
return backend
@pytest_asyncio.fixture()
async def connector() -> GeminiCloudProjectConnector:
cfg = AppConfig()
async with httpx.AsyncClient() as client:
backend = GeminiCloudProjectConnector(
client,
cfg,
translation_service=TranslationService(),
gcp_project_id="test-project",
)
backend.gemini_api_base_url = "https://example.com"
yield backend
🤖 Prompt for AI Agents
In tests/unit/connectors/test_gemini_cloud_project_resource_management.py around
lines 30 to 41, the fixture creates an httpx.AsyncClient but never closes it;
convert the fixture to an async fixture (use pytest_asyncio import) and create
the client with an async context manager (async with httpx.AsyncClient() as
client:) or call await client.aclose() in a finally block, then yield the
GeminiCloudProjectConnector instance so tests receive it and the AsyncClient is
properly closed after each test.

@codecov
Copy link

codecov bot commented Oct 15, 2025

Codecov Report

❌ Patch coverage is 33.33333% with 14 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/connectors/gemini_cloud_project.py 33.33% 14 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant