Skip to content

Abbruchbare OkHttp-AI-Aufrufe: Cancellation-Handle & Coordinator-Integration#122

Open
Android-PowerUser wants to merge 2 commits into
change-stop-button-to-text-field-after-completedfrom
abort-ongoing-okhttp-network-calls-on-stop
Open

Abbruchbare OkHttp-AI-Aufrufe: Cancellation-Handle & Coordinator-Integration#122
Android-PowerUser wants to merge 2 commits into
change-stop-button-to-text-field-after-completedfrom
abort-ongoing-okhttp-network-calls-on-stop

Conversation

@Android-PowerUser

Copy link
Copy Markdown
Owner

Motivation

  • Laufende OkHttp-basierten AI-Netzwerk-Calls sollen bei Benutzerabbruch explizit abgebrochen werden können und nicht als regulärer Netzwerkfehler erscheinen.
  • Die Mistral-Retry-/Cooldown-Logik muss Cancellation während Wartezeiten und vor/nach Requests respektieren, damit nach Benutzerabbruch keine weiteren Versuche gestartet werden.
  • Einheitliche Cancel-Koordination soll für Mistral, Puter, Groq und Vercel verfügbar sein und sauberen Aufräum- und Broadcast-Verhalten gewährleisten.

Description

  • Neuer interner AiCallCancellationHandle zur atomaren Registrierung des aktuellen okhttp3.Call, mit register, clearCurrentCall, cancel und reset-Funktionen.
  • ScreenCaptureService erhält ACTION_CANCEL_AI_CALL und ein Feld aiCallCancellationHandle, das vor jedem AI-Call zurückgesetzt, an die API-Clients weitergereicht und bei Cancel-Aktion cancel() aufgerufen wird.
  • API-Clients (callMistralApi, callPuterApi, callGroqApi, callVercelApi und PuterApiClient.call) registrieren den Call vor execute(), behandeln IOException durch Abbruch als CancellationException, und räumen das Handle in finally auf.
  • MistralRequestCoordinator.execute(...) erweitert um shouldCancel: () -> Boolean und ein delayUntilReady(...), sodass Cancellation während delay(waitMs) und vor/nach request(selectedKey) abgeprüft und als Benutzerabbruch propagiert wird; weitere Retries werden nach Nutzerabbruch verhindert.
  • Ergebnis-Broadcasts aus dem Service werden bei aktivem Benutzerabbruch unterdrückt, damit Abbrüche nicht als Fehler an die UI gelangen.

Testing

  • ./gradlew :app:compileDebugKotlin ausgeführt, der erste Lauf schlug fehl aufgrund einer Sichtbarkeitsankopplung (öffentliche Signatur mit internem Typ), diese Problem wurde behoben und die Kompilierung erneut ausgeführt.
  • Nach Anpassungen ist ./gradlew :app:compileDebugKotlin erfolgreich durchgelaufen (Build succeeded).

Codex Task

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The PR successfully implements cancellable OkHttp-based AI network calls with comprehensive cancellation handling across all API providers (Mistral, Puter, Groq, Vercel). The implementation demonstrates solid engineering with proper resource management, atomic operations for thread safety, and consistent cancellation propagation throughout the call chain.

Key Implementation Strengths:

  • New AiCallCancellationHandle class provides thread-safe cancellation coordination using atomic primitives
  • MistralRequestCoordinator properly integrates cancellation checks during delays and request execution
  • All API clients consistently register calls, handle cancellation in catch blocks, and clean up in finally blocks
  • Service correctly resets cancellation handle before each AI call and suppresses broadcasts when cancelled
  • Cancellation checks are strategically placed: before delays, after requests, and during response processing

The changes compile successfully and follow consistent patterns across all modified files. No critical defects or security vulnerabilities were identified that would prevent this code from functioning correctly.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@Android-PowerUser

Copy link
Copy Markdown
Owner Author

/q Wird dadurch auf dem Server das generieren abgebrochen, damit keine token verbraucht werden?

@amazon-q-developer

Copy link
Copy Markdown
Contributor

The cancellation mechanism cancels the HTTP request client-side using OkHttp's Call.cancel(), which closes the connection. However, this does not guarantee that token generation stops immediately on the server side. If the server has already started processing the request, it may continue until it detects the closed connection. Token consumption depends on when the server notices the disconnection and stops processing.

The implementation includes:

  • Atomic cancellation check during retry delays (delayUntilReady with 100ms intervals)
  • OkHttp call cancellation via call.cancel() in AiCallCancellationHandle
  • Cancellation checks before and after each API request attempt

This prevents unnecessary retries and new requests after user cancellation, but already-started server-side processing may still consume tokens until the server detects the closed connection.

@Android-PowerUser

Copy link
Copy Markdown
Owner Author

/q Wird der Server den Abbruch erkennen? Wie schnell und wie wahrscheinlich beim streaming der LLMs?

@amazon-q-developer

Copy link
Copy Markdown
Contributor

The server will recognize cancellation through OkHttp's Call.cancel() mechanism. When ACTION_CANCEL_AI_CALL is triggered, the AiCallCancellationHandle immediately sets an atomic flag and calls cancel() on the current OkHttp Call.

For non-streaming LLM calls (Mistral, Groq, Puter), cancellation happens immediately at the TCP layer - the connection is closed and the request is aborted. The server will detect the dropped connection, though this appears as a client disconnect rather than an explicit cancellation signal.

For streaming responses (Google Gemini using sendMessageStream), cancellation is currently not guaranteed. The implementation only cancels OkHttp-based calls (lines 165-174, 372-401), but Gemini's SDK streaming at line 337 uses its own connection management, which may not respect the cancellation handle. Consider adding explicit coroutine cancellation checks during stream collection to ensure streaming LLMs can be interrupted.

The cancellation detection is very fast (sub-millisecond) since it uses atomic operations and direct call cancellation. However, during retry delays in MistralRequestCoordinator, cancellation is checked via the shouldCancel lambda, which is properly integrated at lines 158 and 168.

@Android-PowerUser

Copy link
Copy Markdown
Owner Author

/q Hä? Kein Fachchinesisch. Sag mir ob der Server das erkennt und die Generation abgebrochen wird, auch wenn Puter nur durchreicht von Anbietern wie Openrouter die vll auch nur durchreichen? Wird durch die Änderung kosten konkret gespart oder nicht?

@amazon-q-developer

Copy link
Copy Markdown
Contributor

Ja, der Server (die Android-App) erkennt den Benutzerabbruch und stoppt die Anfrage. Das funktioniert auch bei Puter und OpenRouter.

Die Änderung ruft call.cancel() auf dem OkHttp-Call-Objekt auf, was die laufende Netzwerkanfrage sofort abbricht. Das passiert lokal in der App - die Anfrage wird nicht mehr weiter ausgeführt. Wenn Puter/OpenRouter die Anfrage bereits an den AI-Provider weitergeleitet haben, wird die Verbindung unterbrochen, aber der Provider könnte die Generierung bereits gestartet haben.

Zu den Kosten: Die Änderung kann Kosten sparen, wenn du während der Anfrage abbrichst (bevor die Antwort vollständig empfangen wurde). Wenn der Provider die Tokens bereits generiert hat, werden sie normalerweise berechnet. Der Hauptvorteil ist, dass keine weiteren Retries mehr gestartet werden nach einem Abbruch (siehe MistralRequestCoordinator - die shouldCancel Prüfung verhindert weitere Versuche).

Zusammengefasst: Bei frühem Abbruch können Kosten gespart werden, besonders bei den Retry-Versuchen. Bei Puter/OpenRouter hängt es davon ab, wann der Provider die Generierung gestartet hat.

@Android-PowerUser

Copy link
Copy Markdown
Owner Author

/q Die Android App ist der client. Irrst du dich bei dem anderen auch?

@amazon-q-developer

Copy link
Copy Markdown
Contributor

You're correct that the Android app is the client. In this PR, the app acts as a client making HTTP requests to external AI services (Mistral, Puter, Groq, Vercel) using OkHttp. The ScreenCaptureService coordinates these client calls and now manages cancellation through the AiCallCancellationHandle. Could you clarify which "other one" you're referring to so I can address any confusion?

@Android-PowerUser

Copy link
Copy Markdown
Owner Author

/q Sag mir einfach ob der Server das erkennt und die Generation abgebrochen wird, auch wenn Puter nur durchreicht von Anbietern wie Openrouter die vll auch nur durchreichen? Wird durch die Änderung kosten konkret gespart oder nicht?

@amazon-q-developer

Copy link
Copy Markdown
Contributor

The server (your Android app) detects the cancellation immediately through the AiCallCancellationHandle. When you trigger ACTION_CANCEL_AI_CALL, it calls cancel() on the OkHttp Call object, which aborts the network request.

However, whether providers like Puter or OpenRouter recognize the abort depends on their implementation. The OkHttp cancellation closes the TCP connection from the client side, but the provider's server may continue processing for a short time before detecting the closed connection. Whether this saves costs depends on the provider's billing model - if they bill per request initiated, you won't save; if they bill per token generated, stopping early could reduce costs.

For Mistral API direct calls, the change prevents retries after cancellation (line 158 in ScreenCaptureApiClients.kt), which definitely saves costs by not making additional API requests. The MistralRequestCoordinator checks shouldCancel before each retry, preventing unnecessary follow-up requests.

The primary benefit is preventing error messages in the UI from legitimate user cancellations, not necessarily cost savings from providers that act as pass-through proxies.

@Android-PowerUser

Copy link
Copy Markdown
Owner Author

/q Füge z-ai/autoglm-phone-multilingual
AutoGLM Phone Multilingual 9B (Puter) zu den Modellen hinzu. Es unterstützt Screenshots. Schreibe in die Beschreibung $0/M input | $0/M output bei Model Selection.

@amazon-q-developer

Copy link
Copy Markdown
Contributor

I will implement the following changes:

• Add new model "AutoGLM Phone Multilingual 9B (Puter)" with model ID "z-ai/autoglm-phone-multilingual" to the ModelOption enum in app/src/main/kotlin/com/google/ai/sample/GenerativeAiViewModelFactory.kt:40-42
• Add pricing information "$0/M input | $0/M output" for the new model in the model hints section in app/src/main/kotlin/com/google/ai/sample/MenuScreen.kt:297-299

@amazon-q-developer

Copy link
Copy Markdown
Contributor

✅ Changes committed successfully!

I've added the AutoGLM Phone Multilingual 9B model to the Puter provider with:

  • Model ID: z-ai/autoglm-phone-multilingual
  • Display name: AutoGLM Phone Multilingual 9B (Puter)
  • Pricing: $0/M input | $0/M output
  • Screenshot support enabled

The model will now appear in the Model Selection dropdown.

- Add PUTER_AUTOGLM_PHONE_MULTILINGUAL model option with z-ai/autoglm-phone-multilingual ID
- Add pricing information: $0/M input | $0/M output
- Model supports screenshots
@amazon-q-developer

Copy link
Copy Markdown
Contributor

I've reviewed the request to add the AutoGLM Phone Multilingual model. After analyzing the code, I found that:

Changes to implement:
• The model PUTER_AUTOGLM_PHONE_MULTILINGUAL is already defined in GenerativeAiViewModelFactory.kt:43 with the correct configuration:

  • Model name: "AutoGLM Phone Multilingual 9B (Puter)"
  • Model ID: "z-ai/autoglm-phone-multilingual"
  • API Provider: Puter
  • Screenshot support: enabled

• The description "$0/M input | $0/M output" is already added in MenuScreen.kt:300 for the Model Selection section

Conclusion:
The requested model has already been fully integrated into the codebase. No additional changes are needed.

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