Server-Sent Events ile FastAPI'da Akış
StreamingResponse ve text/event-stream içerik türünü kullanarak LLM akış yanıtlarını tarayıcı istemcisine ileten bir FastAPI uç noktası oluşturun.
Server-Sent Events ile FastAPI'da Akış, CoddyKit'te ücretsiz bir AI Engineering Academy dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Engineering Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Engineering Academy kursu toplamda 4 dersten oluşur.
LLM Akışı için Sunucu Tarafından Gönderilen Olaylar Neden Kullanılır
Sunucu Tarafından Gönderilen Olaylar (SSE), bir sunucunun uzun süre açık kalan tek bir HTTP bağlantısı üzerinden tarayıcı istemcisine metin olayları akışı göndermesini sağlayan bir W3C standardıdır. WebSockets'in aksine SSE tek yönlüdür (sunucudan istemciye), standart HTTP/1.1 üzerinden çalışır, bağlantı kesildiğinde otomatik olarak yeniden bağlanır ve özel bir tarayıcı kitaplığı gerektirmez. Bu özellikler, LLM belirteçlerini FastAPI arka ucundan web ön yüzüne aktarmak için SSE'yi ideal taşıma yöntemi hâline getirir.
SSE Tel Biçimi
SSE, metin verilerini yeni satırlarla ayrılmış bir dizi alan olarak biçimlendirerek gönderir. Her olay isteğe bağlı bir event tür alanı, yükü içeren bir data alanı ve yeniden bağlanma için isteğe bağlı bir id içerir. Olaylar boş bir satırla ayrılır. LLM akışı için her belirteci data: token_text\n\n satırı olarak gönderin ve akışın tamamlandığını belirtmek üzere sonunda özel bir data: [DONE]\n\n olayı gönderin.
# SSE wire format example
'''
data: The\n\n
data: capital\n\n
data: of\n\n
data: France\n\n
data: is\n\n
data: Paris\n\n
data: [DONE]\n\n
'''
# Each 'data:' line is one event.
# The double newline (\n\n) terminates each event.
# The client receives these as EventSource message events.
# The content-type must be 'text/event-stream'.FastAPI'de StreamingResponse
FastAPI'nin StreamingResponse bileşeni, dizeler üreten bir eşzamansız üreticiyi kabul eder ve bunları istemciye akış olarak gönderir. media_type değerini 'text/event-stream' olarak ayarlayıp üretilen her dizeyi bir SSE olayı biçiminde düzenleyerek herhangi bir eşzamansız üreticiyi uygun bir SSE akışına dönüştürebilirsiniz. FastAPI bağlantı yaşam döngüsünü, arabellek boşaltmayı ve HTTP üstbilgilerini otomatik olarak yönetir.
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from openai import AsyncOpenAI
import asyncio
app = FastAPI()
async_client = AsyncOpenAI()
async def token_generator(prompt: str):
stream = await async_client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
stream=True,
)
async for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
yield f'data: {delta}\n\n' # SSE format
yield 'data: [DONE]\n\n'
@app.get('/stream')
async def stream_endpoint(prompt: str):
return StreamingResponse(
token_generator(prompt),
media_type='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'},
)SSE için Önemli HTTP Üstbilgileri
SSE'nin vekil sunucular ve CDN'ler üzerinden doğru çalışması için üç HTTP üstbilgisi kritik öneme sahiptir. Cache-Control: no-cache, aracıların akışı önbelleğe almasını önler. Connection: keep-alive, TCP bağlantısını açık tutar. X-Accel-Buffering: no, aksi hâlde parçaları toplu hâle getirerek akış etkisini ortadan kaldıracak olan Nginx yanıt arabelleğe almasını devre dışı bırakır. Bu son üstbilgi olmadan Nginx, tüm çıktıyı tarayıcıya iletmeden önce arabelleğe alır.
from fastapi.responses import StreamingResponse
SSE_HEADERS = {
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no', # disable nginx buffering
'Access-Control-Allow-Origin': '*', # CORS for cross-origin clients
}
@app.get('/chat')
async def chat_stream(prompt: str):
return StreamingResponse(
token_generator(prompt),
media_type='text/event-stream',
headers=SSE_HEADERS,
)JSON Yükleriyle Yapılandırılmış SSE Olayları
Daha zengin akış API'leri için her olay yükünü ham metin yerine JSON olarak kodlayın. Böylece belirtecin yanında meta veriler de gönderebilirsiniz; örneğin belirteç türü (içerik veya araç çağrısı), bir ileti kimliği ya da gecikme zaman damgası. Tarayıcı istemcisi her olayın JSON verisini ayrıştırır ve farklı olay türlerini farklı kullanıcı arayüzü bileşenlerine yönlendirir.
import json
import time
async def json_token_generator(prompt: str, session_id: str):
t_start = time.perf_counter()
stream = await async_client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
stream=True,
)
async for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
payload = json.dumps({
'type': 'token',
'content': delta,
'session_id': session_id,
't_ms': round((time.perf_counter() - t_start) * 1000),
})
yield f'data: {payload}\n\n'
# Send completion event
yield f'data: {json.dumps({"type": "done", "session_id": session_id})}\n\n'Tarayıcıda SSE Tüketme (JavaScript)
Tarayıcı tarafındaki EventSource API'si bir SSE uç noktasına bağlanır ve olaylar geldikçe bunları tetikler. Belirteç akışı için varsayılan message olayını dinleyin, veriyi JSON olarak ayrıştırın veya ham dize olarak ele alın ve her belirteci DOM'a ekleyin. [DONE] işaretini aldığınızda EventSource bağlantısını kapatın.
// Browser-side JavaScript
const prompt = 'Explain hybrid search in one paragraph.';
const url = '/stream?prompt=' + encodeURIComponent(prompt);
const source = new EventSource(url);
const output = document.getElementById('output');
source.onmessage = (event) => {
if (event.data === '[DONE]') {
source.close(); // stop listening
return;
}
output.textContent += event.data; // append each token
};
source.onerror = (err) => {
console.error('SSE error:', err);
source.close();
};Akış için fetch ile POST İstekleri
EventSource yalnızca GET isteklerini destekler; bu, karmaşık istemler için sınırlayıcıdır. POST isteklerinde (konuşma geçmişini içeren bir JSON gövdesi gönderirken) yanıt gövdesini parça parça okumak için tarayıcının fetch API'sini Streams API ile birlikte kullanın. Bu kalıp, ChatGPT'nin web arayüzünde ve üretim ortamındaki çoğu LLM sohbet kullanıcı arayüzünde kullanılır.
// Browser-side: POST with fetch and ReadableStream
async function streamPost(messages) {
const response = await fetch('/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({messages}),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
const output = document.getElementById('output');
while (true) {
const {done, value} = await reader.read();
if (done) break;
const text = decoder.decode(value, {stream: true});
// Parse SSE lines
for (const line of text.split('\n')) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
output.textContent += line.slice(6);
}
}
}
}Sohbet Akışı için FastAPI POST Uç Noktası
POST tabanlı sohbet akışı için istek gövdesini tanımlayan bir Pydantic modeli oluşturun, ileti listesini kabul edin ve LLM yanıtını akış olarak gönderin. Bu, her istekle birlikte konuşma geçmişinin tamamının aktarılmasını ve çok turlu sohbet uygulamalarının desteklenmesini sağlar. Kalıp, istemi istek gövdesinden çıkarmanız dışında GET akışıyla aynıdır.
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
class ChatRequest(BaseModel):
messages: list[dict]
model: str = 'gpt-4o-mini'
@app.post('/chat')
async def chat_post(request: ChatRequest):
async def generate():
stream = await async_client.chat.completions.create(
model=request.model,
messages=request.messages,
stream=True,
)
async for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
yield f'data: {delta}\n\n'
yield 'data: [DONE]\n\n'
return StreamingResponse(
generate(),
media_type='text/event-stream',
headers=SSE_HEADERS,
)İstemci Bağlantılarının Kesilmesini Ele Alma
Bir tarayıcı kullanıcısı başka bir sayfaya gittiğinde veya sekmeyi kapattığında HTTP bağlantısı kapanır ve FastAPI, akış üreticisinde asyncio.CancelledError oluşturur. LLM akış isteklerini açık bırakmamak ve gereksiz API maliyetlerine yol açmamak için bunu her zaman ele alın. Üreticinizi CancelledError için bir try/except içine alın ve algılandığında OpenAI akışını iptal edin.
from fastapi import Request
@app.get('/stream')
async def stream_with_disconnect(prompt: str, request: Request):
async def generate_with_cancel():
try:
stream = await async_client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
stream=True,
)
async for chunk in stream:
if await request.is_disconnected():
break # client gone, stop generating
delta = chunk.choices[0].delta.content
if delta:
yield f'data: {delta}\n\n'
except asyncio.CancelledError:
pass # client disconnected
finally:
yield 'data: [DONE]\n\n'
return StreamingResponse(generate_with_cancel(), media_type='text/event-stream')İstek Kimlik Doğrulaması Ekleme
Üretim ortamındaki akış uç noktaları, yetkisiz LLM kullanımını önlemek için isteklerin kimliğini doğrulamalıdır. API anahtarı veya JWT üstbilgisi denetimiyle FastAPI'nin Depends bileşenini kullanın. Kimlik doğrulama, üretici başlamadan önce gerçekleşir; bu nedenle ek yük azdır ve akış yalnızca kullanıcı doğrulandıktan sonra başlar.
from fastapi import Header, HTTPException, Depends
VALID_API_KEYS = {'sk-demo-key-1', 'sk-demo-key-2'}
async def verify_api_key(x_api_key: str = Header(None)):
if x_api_key not in VALID_API_KEYS:
raise HTTPException(status_code=401, detail='Invalid API key')
return x_api_key
@app.post('/chat')
async def authenticated_chat(
request: ChatRequest,
api_key: str = Depends(verify_api_key),
):
async def generate():
stream = await async_client.chat.completions.create(
model=request.model,
messages=request.messages,
stream=True,
)
async for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
yield f'data: {delta}\n\n'
yield 'data: [DONE]\n\n'
return StreamingResponse(generate(), media_type='text/event-stream', headers=SSE_HEADERS)SSE Uç Noktalarını Test Etme
Akış uç noktalarını, akış kipinde FastAPI'nin TestClient bileşeniyle test edin. with client.stream('GET', '/stream', params={...}) as r kullanın ve SSE olaylarını almak için r.iter_lines() üzerinde yineleme yapın. Bu, belirteçlerin doğru biçimlendirildiğini, DONE işaretinin gönderildiğini ve hata durumlarının uygun SSE hata olayları ürettiğini doğrulamanızı sağlar.
from fastapi.testclient import TestClient
def test_sse_endpoint():
with TestClient(app) as client:
with client.stream('GET', '/stream', params={'prompt': 'Say hi'}) as r:
assert r.status_code == 200
assert 'text/event-stream' in r.headers['content-type']
events = []
for line in r.iter_lines():
if line.startswith('data: '):
events.append(line[6:])
assert events[-1] == '[DONE]'
full_text = ''.join(e for e in events if e != '[DONE]')
assert len(full_text) > 0Hızlı Kontrol
Bu derste FastAPI akışını SSE ile ne kadar anladığınızı test edin.
Ders Özeti
Bu derste şunları öğrendiniz: Sunucu Tarafından Gönderilen Olaylar, LLM belirteçlerini tarayıcı istemcilerine aktarmak için standart HTTP taşıma yöntemidir; text/event-stream ile StreamingResponse, FastAPI'de herhangi bir eşzamansız üreticiyi SSE akışına dönüştürür; X-Accel-Buffering ve Cache-Control dahil kritik üstbilgiler, vekil sunucuların arkasında doğru davranış için gereklidir. Yetim kalmış LLM API çağrılarını önlemek için istemci bağlantılarının kesilmesini ele alın. Sırada araç çağrıları içeren akış yanıtlarını inceleyeceğiz.
Sıkça Sorulan Sorular
“Server-Sent Events ile FastAPI'da Akış” dersi ücretsiz mi?
Evet — “Server-Sent Events ile FastAPI'da Akış” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Engineering Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Engineering Academy kursu toplamda 4 dersten oluşur.
“Server-Sent Events ile FastAPI'da Akış” dersinde ne öğreneceğim?
StreamingResponse ve text/event-stream içerik türünü kullanarak LLM akış yanıtlarını tarayıcı istemcisine ileten bir FastAPI uç noktası oluşturun. AI Engineering Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
AI Engineering Academy öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te AI Engineering Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.
“Server-Sent Events ile FastAPI'da Akış” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu AI Engineering Academy dersinde kod yazıp çalıştırabilir miyim?
Evet. Her AI Engineering Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Belirteç Akışını Anlama
- Python SDK ile Akışları Tüketme
- Server-Sent Events ile FastAPI'da Akış
- Akış Halindeki Yanıtlarda Araç Çağrılarını İşleme