Skip to content

Phase 4C: request-scoped client disconnect cancellation #139

Description

@Amm1rr

Goal

When a client disconnects from a stateless Gemini request, stop only that request's provider/transport work, then clean up and release its lease without affecting unrelated concurrent requests.

Status

Blocked on request-scoped transport cancellation support.

Phase 4C is intentionally paused. Phase 4D remains independent and may proceed separately.

Why this is blocked

Current direct WebAPI call path:

WebAI-to-API
→ gemini-webapi
→ curl-cffi
→ libcurl
→ Gemini

Cancelling the WebAI endpoint coroutine does not currently provide a reliable request-scoped guarantee that the underlying curl transfer stops.

Closing the shared GeminiClient, AsyncSession, or shared AsyncCurl is not acceptable because concurrent requests may share those resources.

A best-effort disconnect watcher alone is therefore insufficient for the required contract.

Decisions already made

  • Do not use shared GeminiClient.close() for per-request cancellation.
  • Do not use shared AsyncSession.close() or shared AsyncCurl.close().
  • Do not treat coroutine cancellation alone as proof that provider work stopped.
  • Keep Phase 4D retry/concurrency isolation separate from Phase 4C.
  • Prefer an upstream-compatible request-scoped abort mechanism.
  • Do not integrate the current prototype API into WebAI or gemini-webapi until the upstream API/lifecycle direction is clearer.

External dependency

Upstream curl-cffi design issue:

A working D1 prototype exists in the curl-cffi fork, rebased onto current upstream main (0.16.2). It validates request isolation, streaming abort, retry-delay interruption, repeated abort handling, wait_closed(), and session reuse.

Current prototype branch:

  • feat/request-scoped-abort
  • prototype commit after rebase: e77cbb6be96be8e727fc9fb44f4697e292598ab9
  • preserved pre-rebase backup: backup/request-scoped-abort-v0.16.0

Known D1 gap: explicit abort does not yet interrupt a request blocked while waiting in pop_curl() for a pooled Curl handle.

Resume criteria

Resume Phase 4C when one of these is true:

  1. curl-cffi maintainers agree on an upstream request-scoped cancellation design; or
  2. we explicitly decide to use a temporary immutable fork/pin with a reviewed API while upstream work continues.

Next steps after unblock

  1. Finalize or redesign the curl-cffi request-scoped abort API based on upstream feedback.
  2. Add missing race/lifecycle coverage and prepare a focused upstream PR if appropriate.
  3. Add D2 propagation through gemini-webapi, including retry/reinit bypass for explicit abort.
  4. Add WebAI client-disconnect watching for buffered requests and buffered tool-generation paths.
  5. On disconnect: abort the exact provider request, await request-local cleanup, release lease, and avoid affecting concurrent requests.
  6. Add cancellation, cleanup, concurrency-isolation, and regression tests.
  7. Update stable API/lifecycle documentation only after the final contract is implemented.

Required Phase 4C contract

client disconnect
→ stop only that provider request
→ request-local transport cleanup completes
→ lease/resources are released
→ unrelated concurrent requests continue
→ shared client/session remains reusable

Non-goals

  • Playwright stateless backend support.
  • Phase 4D retry/concurrency isolation work.
  • Closing shared provider/session resources as a cancellation mechanism.
  • Broad provider lifecycle redesign unrelated to request-scoped cancellation.

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions