1. The Streaming Gateway Topology
The AI gateway acts as the secure intermediary between external client applications and upstream foundation models. It authenticates users, injects system prompt constraints, negotiates rate limits, and streams tokens over Server-Sent Events (SSE).
2. Handling Client Disconnects & Runaway Costs
If an Android user navigates away or loses cellular connectivity mid-stream, standard HTTP endpoints often continue executing in the background until the LLM completes generation. In AI systems, where inference cost is calculated per output token, this leads to significant resource waste.
| Architecture Strategy | System Mechanism | Impact on Compute & Spend |
|---|---|---|
| Unmonitored Generator | Server continues pulling tokens from LLM API even after the TCP connection to the mobile device terminates. | Wastes compute and money; burns API tokens for responses that are never displayed. |
| Socket Polling / Cancellation | Engine checks request.is_disconnected() on every generated token chunk and cancels the
upstream context. |
Immediately aborts the LLM API call, terminating token generation and freeing memory. |
3. Dual-Stack Implementations: Streaming AI Server
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import StreamingResponse
from openai import AsyncOpenAI
import asyncio
app = FastAPI(title="AI Gateway")
ai_client = AsyncOpenAI()
async def event_generator(prompt: str, request: Request):
try:
# Request stream from inference engine
stream = await ai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
stream=True
)
async for chunk in stream:
# Check if the client closed connection
if await request.is_disconnected():
print("Client disconnected. Aborting upstream generation.")
break
content = chunk.choices[0].delta.content
if content:
# Emit Server-Sent Events (SSE) standard protocol
yield f"data: {content}\n\n"
yield "data: [DONE]\n\n"
except asyncio.CancelledError:
print("Task explicitly cancelled by gateway.")
raise
@app.post("/v1/stream")
async def stream_inference(request: Request):
body = await request.json()
prompt = body.get("prompt")
if not prompt:
raise HTTPException(status_code=400, detail="Missing prompt parameter")
return StreamingResponse(
event_generator(prompt, request),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no" # Prevents proxy buffering (NGINX/Cloudflare)
}
)
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.request.*
import io.ktor.http.*
import io.ktor.utils.io.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun Application.aiModule() {
routing {
post("/v1/stream") {
val parameters = call.receive<Map<String, String>>()
val prompt = parameters["prompt"] ?: return@post call.respond(HttpStatusCode.BadRequest)
call.response.cacheControl(CacheControl.NoCache(null))
call.respondTextWriter(contentType = ContentType.Text.EventStream) {
try {
// Simulated asynchronous model token stream Flow
mockModelTokenStream(prompt).collect { token ->
write("data: $token\n\n")
flush()
}
write("data: [DONE]\n\n")
flush()
} catch (e: CancellationException) {
// Triggered when client socket closes mid-stream
println("Ktor client disconnected. Coroutine cancelled.")
throw e
}
}
}
}
}
fun mockModelTokenStream(prompt: String): Flow<String> = flow {
val tokens = listOf("Structured ", "concurrency ", "powers ", "scalable ", "gateways.")
for (token in tokens) {
delay(100) // Simulating network generation interval
emit(token)
}
}.flowOn(Dispatchers.IO)
4. Containerization & Cloud Run Tuning
A lightweight production Docker container paired with optimized Google Cloud Run configurations ensures minimal cold-start times and high concurrency.
FROM python:3.11-slim as base
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PORT=8080
WORKDIR /app
# Install runtime dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Run with Uvicorn ASGI server
CMD exec uvicorn main:app --host 0.0.0.0 --port ${PORT} --workers 2 --timeout-keep-alive 65
# Deploy service to Cloud Run with optimized streaming concurrency
gcloud run deploy ai-serving-gateway \
--image gcr.io/my-project/ai-gateway:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--concurrency 80 \
--cpu 2 \
--memory 2Gi \
--min-instances 1 \
--max-instances 20 \
--timeout 300
5. Progressive Glossary
| Term | Technical Definition | Android / Systems Analogy |
|---|---|---|
| ASGI | Asynchronous Server Gateway Interface: the modern asynchronous Python standard for handling concurrent HTTP and WebSockets. | Kotlin Coroutines Dispatchers.IO replacing legacy single-threaded handlers. |
| Concurrency Limit | The maximum number of simultaneous HTTP requests a single Cloud Run container instance can process concurrently. | OkHttp Dispatcher.maxRequestsPerHost connection pool ceiling. |
| Cold Start | The latency delay incurred when a serverless platform boots up a new container image from zero instances. | Android Application Cold Launch (Application class initialization before first frame). |
| Proxy Buffering | Intermediate proxies (NGINX, Cloudflare) holding partial chunks until a large buffer is full before sending to client. | A bad custom Flow operator collecting all emissions before emitting down the chain. |
Sources & Reference Standards
- FastAPI Documentation: Asynchronous Streaming Responses & Background Tasks
- Google Cloud Run Architecture: Optimizing Concurrency for Generative AI Applications
- W3C Working Group: Server-Sent Events (SSE) Standard Protocol