Free lesson · GenAI Application Engineering
થિંકિંગ બજેટ સાથે Gemini 2.5 Flash streaming adapter અમલમાં મૂકો
તમે adapters/gemini_stream.py માં google.genai.Client (નવું unified SDK, deprecated google-generativeai નહીં) ને wrap કરતો GeminiStreamAdapter class બનાવશો. આ adapter stream_chat(messages, model, temperature, thinking_enabled) -> AsyncGenerator[StreamChunk, None] ઉજાગર કરે છે, જે client.aio.models.generate_content_stream() ને કૉલ કરે છે. જ્યારે thinking_enabled True હોય, ત્યારે વિસ્તૃત reasoning માટે config=GenerateContentConfig(thinking_config=ThinkingConfig(thinking_budget=1024)) પાસ કરો; જ્યારે False હોય, ત્યારે thinking_budget=0 સેટ કરો. દરેક GenerateContentResponse chunk માટે, chunk.candidates[0].content.parts[0].text કાઢો અને તેને StreamChunk માં map કરો. candidate.finish_reason ને FinishReason.SAFETY સામે તપાસીને અને StreamError emit કરીને safety blocks હેન્ડલ કરો. આ adapter Gemini ની 'model' role ને 'assistant' માં normalize કરે છે. Configuration Settings માંથી GEMINI_API_KEY વાંચે છે.
Course: Full-Stack GenAI Applications · Chapter 1 · Chat Completion API with Streaming
Free to read — no subscription required.
પરિચય
જ્યારે તમે Gemini 2.5 Flash ને streaming chat endpoint માં integrate કરો છો, ત્યારે માત્ર stream=True flag પૂરતો નથી: Gemini બે અલગ token streams પરત કરે છે — thought tokens અને text tokens — અને તેમને એકસરખા ગણવાથી કાં તો reasoning યુઝરને દેખાતા chat bubbles માં leak થાય છે, અથવા જે queries માટે કોઈ reasoning ની જરૂર જ નહોતી તેના પર તમારું token બિલ ચૂપચાપ 2–3× વધી જાય છે. એન્જિનિયરોને ઘણીવાર આ વાતની ખબર deploy કર્યા પછી જ પડે છે, જ્યારે યુઝર્સ ગૂંચવણભર્યા output ની ફરિયાદ કરે છે અથવા billing alerts અનપેક્ષિત રીતે fire થાય છે. આ પાઠના અંતે તમે દરેક request માટે ThinkingConfig બનાવી શકશો, part.thought flag નો ઉપયોગ કરીને thought અને text tokens વચ્ચેની phase boundary શોધી શકશો, અને classified output ને FastAPI SSE endpoint મારફતે browser EventSource client સુધી stream કરી શકશો.
મુખ્ય પરિભાષા
- ThinkingConfig: એક
google.genai.typesdataclass જે Gemini 2.5 Flash ની extended reasoning વર્તણૂકને દરેક request ના આધારે કન્ફિગર કરે છે; તેનુંthinking_budgetfield નિયંત્રિત કરે છે કે દેખાતો text આપતા પહેલાં મોડેલ reasoning માટે કેટલા tokens વાપરી શકે. - thinking_budget:
ThinkingConfigની અંદર પાસ કરવામાં આવતો એક integer (0–24576) જે reasoning tokens ની મર્યાદા નક્કી કરે છે.0thinking ને સંપૂર્ણપણે બંધ કરે છે (સૌથી ઝડપી, સૌથી સસ્તું); વધુ મૂલ્યો multi-step સમસ્યાઓ પર ચોકસાઈ માટે latency અને ખર્ચનો સોદો કરે છે. - SSE (Server-Sent Events): એક W3C-standard એકદિશીય streaming protocol જેમાં server લાંબા સમય સુધી ચાલતા HTTP response પર
data: <json>\n\nframes મોકલે છે.media_type="text/event-stream"સાથે FastAPI નોStreamingResponseએ Gemini ના બે-phase token stream ને browserEventSourceclient સુધી પહોંચાડવાની પ્રમાણભૂત રીત છે.
વિભાવનાઓ
Gemini 2.5 Flash streaming adapter માં દરેક નિર્ણય બે વિચારો પર આધારિત છે: thinking_budget નું મૂલ્ય દરેક request માટે latency–ખર્ચ–ચોકસાઈનો સોદો નક્કી કરે છે, અને પરિણામી response એ બે-phase stream છે (પહેલાં thought tokens, પછી text tokens) જેને તમારા server એ SSE frames મોકલતા પહેલાં classify કરવો જ પડે. નીચેનો પેટાવિભાગ budget કેવી રીતે પસંદ કરવું તે સમજાવે છે; પછી Code Walkthrough બતાવે છે કે phase transition કેવી રીતે શોધવું અને દરેક chunk ને યોગ્ય SSE event type માં કેવી રીતે route કરવું.
Thinking Budget પસંદગી માટે વ્યવહારુ વિચારણાઓ
યોગ્ય thinking budget પસંદ કરવામાં latency, ખર્ચ અને જવાબની ગુણવત્તા વચ્ચે સંતુલન સાધવું પડે છે. જ્યારે thinking_budget 0 હોય, ત્યારે Gemini 2.5 Flash તેના non-thinking પુરોગામી જેવી latency સાથે જવાબ આપે છે — ટૂંકા prompts માટે સામાન્ય રીતે 150–300ms time-to-first-token. thinking_budget=1024 પર, પહેલો દેખાતો token આવે તે પહેલાં મોડેલ થોડાક સો milliseconds reasoning માં વિતાવે છે, જે અનુભવી શકાય તેવો વિલંબ ઉમેરે છે પરંતુ multi-step સમસ્યાઓ પર ચોકસાઈમાં અર્થપૂર્ણ સુધારો કરે છે. thinking_budget=8192 અથવા તેથી વધુ પર, text tokens વહેવા શરૂ થાય તે પહેલાં તમને 2–5 સેકન્ડની "thinking" શાંતિ જોવા મળી શકે છે, જેના માટે યુઝર્સ ગૂંચવાય નહીં તે માટે તમારા frontend એ "reasoning..." indicator બતાવવું જરૂરી બને છે.
Thinking budget token billing ને પણ અસર કરે છે. Thought tokens તમારા output token વપરાશમાં ગણાય છે, તેથી thinking_budget=8192 સાથેની request જે તેનું budget પૂરેપૂરું વાપરે છે તે thinking બંધ કરેલી એ જ request કરતાં આશરે 2–3x વધુ ખર્ચ કરે છે. આ જ કારણે per-request નિયંત્રણ મહત્વનું છે: તમારી application સરળ queries (greetings, factual lookups) ને thinking_budget=0 સાથે adapter મારફતે route કરી શકે છે અને જટિલ queries (code debugging, multi-step math, planning tasks) ને dynamically વધુ budgets પર escalate કરી શકે છે.
જ્યારે thinking સક્રિય હોય, ત્યારે Gemini API કોઈપણ સ્પષ્ટ temperature મૂલ્યને નકારી કાઢે છે: જો તમે non-zero thinking budget સાથે temperature=0.3 પાસ કરો, તો API તમારું મૂલ્ય લાગુ કરવાને બદલે validation error પરત કરે છે. આ જ કારણે adapter જ્યારે પણ thinking_budget શૂન્યથી વધુ હોય ત્યારે temperature field છોડી દે છે, અને માત્ર non-thinking path પર જ્યાં thinking_budget=0 હોય ત્યાં જ temperature=1.0 સેટ કરે છે. આ રીતે temperature ને શરતી રીતે સેટ કરવાથી બંને code paths માન્ય રહે છે અને request નિષ્ફળ થતી અટકે છે.
Code Walkthrough
હવે જ્યારે તમે સમજી ગયા છો કે thinking_budget latency–ખર્ચ–ચોકસાઈના સોદાને કેવી રીતે નિયંત્રિત કરે છે, ત્યારે implementation બે કાર્યોમાં સમેટાઈ જાય છે: એક adapter બનાવો જે દરેક chunk ના part.thought flag ને તપાસે અને તેને thought token અથવા દેખાતા token તરીકે classify કરે, પછી તે adapter ને media_type="text/event-stream" સાથે FastAPI StreamingResponse માં wire કરો.
Adapter: adapters/gemini_stream.py
GeminiStreamAdapter class એક google.genai.Client બનાવે છે અને stream_chat generator expose કરે છે. તેની અંદર, requested budget પર સેટ કરેલા ThinkingConfig સાથે GenerateContentConfig બનાવવામાં આવે છે. એક મહત્વની મર્યાદા: જ્યારે thinking_budget શૂન્યથી વધુ હોય ત્યારે temperature field છોડી દેવું જ પડે — બંને સેટ હોય તો API validation error પરત કરે છે. જ્યારે thinking_budget 0 હોય, ત્યારે temperature=1.0 સ્પષ્ટપણે સેટ કરવાથી response style non-thinking path સાથે સુસંગત રહે છે. Iteration loop ની અંદર, part.thought એ flag છે જે phase boundary દર્શાવે છે: reasoning phase દરમિયાન True, અને દેખાતો text શરૂ થાય ત્યારે False.
Code snippetpython
1from google import genai 2from google.genai import types 3 4class GeminiStreamAdapter: 5 def __init__(self, api_key: str, model: str = "gemini-2.5-flash"): 6 self.client = genai.Client(api_key=api_key) 7 self.model = model 8 9 def stream_chat(self, message: str, thinking_budget: int = 0): 10 """Yields SSE-ready dicts: type='thinking'|'token'|'done'.""" 11 config_kwargs: dict = { 12 "thinking_config": types.ThinkingConfig(thinking_budget=thinking_budget), 13 } 14 if thinking_budget == 0: 15 config_kwargs["temperature"] = 1.0 16 config = types.GenerateContentConfig(**config_kwargs) 17 for chunk in self.client.models.generate_content_stream( 18 model=self.model, contents=message, config=config 19 ): 20 for part in chunk.candidates[0].content.parts: 21 if part.thought: 22 yield {"type": "thinking", "token": part.text} 23 else: 24 yield {"type": "token", "token": part.text} 25 yield {"type": "done"}
FastAPI SSE endpoint
Endpoint stream_chat ને async generator માં wrap કરે છે અને તેને StreamingResponse ને પાસ કરે છે. દરેક yield થયેલ dict ને data: …\n\n frame માં serialize કરવામાં આવે છે — W3C SSE wire format જેને browser EventSource client natively વાંચે છે. thinking_budget query parameter સીધો request માંથી આવે છે, તેથી callers route બદલ્યા વિના reasoning ચાલુ અથવા બંધ કરી શકે છે.
Code snippetpython
1import json 2from fastapi import FastAPI 3from fastapi.responses import StreamingResponse 4from adapters.gemini_stream import GeminiStreamAdapter 5 6app = FastAPI() 7adapter = GeminiStreamAdapter(api_key="your-api-key") 8 9@app.post("/chat/stream") 10async def chat_stream(message: str, thinking_budget: int = 0): 11 async def event_generator(): 12 for event in adapter.stream_chat(message, thinking_budget): 13 yield f"data: {json.dumps(event)}\n\n" 14 15 return StreamingResponse(event_generator(), media_type="text/event-stream")
ખાતરી કરો કે POST /chat/stream?message=hello&thinking_budget=0 મોકલવાથી text/event-stream response પરત મળે છે જેના frames માં ઓછામાં ઓછું એક {"type":"token","token":"..."} event હોય અને જે અંતિમ {"type":"done"} frame સાથે સમાપ્ત થાય.
શું કરવું અને શું ન કરવું
શું કરવું
- જ્યારે પણ
thinking_budget > 0હોય ત્યારેGenerateContentConfigમાંથીtemperatureછોડી દો — જો એક જ request માંThinkingConfigઅને temperature મૂલ્ય બંને હાજર હોય તો Gemini API validation error પરત કરે છે; સ્પષ્ટtemperature=1.0assignment નેthinking_budget == 0path માટે જ રાખો જેથી non-thinking requests સુસંગત રીતે વર્તે. - Yield કરતા પહેલાં tokens ને classify કરવા માટે દરેક chunk ના દરેક part પર
part.thoughtતપાસો — Gemini 2.5 Flash એક જ stream માં thought tokens અને દેખાતા text tokens ને આંતરે-આંતરે મૂકે છે, અને તેમને અલગ SSE event types ("thinking"વિરુદ્ધ"token") માં route કરવું એ raw reasoning યુઝરને દેખાતા chat bubble માં દેખાય તે અટકાવવાનો એકમાત્ર રસ્તો છે. StreamingResponseપરmedia_type="text/event-stream"સેટ કરો અને દરેક yield થયેલ dict નેdata: …\n\nframe તરીકે serialize કરો — content-type અથવા double-newline frame delimiter છોડી દેવાથી W3C SSE contract તૂટે છે અને browserEventSourceclient દરેક token માટે events fire કરવાને બદલે અનિશ્ચિત સમય સુધી buffer કરે છે.
શું ન કરવું
- Gemini stream ને એક જ અવિભાજિત token sequence તરીકે ન ગણો —
part.thoughtતપાસ્યા વિનાchunk.candidates[0].content.partsપર iterate કરવાથી thought tokens અને દેખાતા tokens એક જ stream માં ભળી જાય છે, જે multi-sentence reasoning chains ને ચૂપચાપ UI માં leak કરે છે અને reasoning-enabled requests પર યુઝરને દેખાતા output ને 2–3× ફુલાવી દે છે. - Requests વચ્ચે એક જ hardcoded
GenerateContentConfigશેર ન કરો — startup પર એક વારconfig_kwargsબનાવવાથી callers runtime પર query parameter મારફતેthinking_budgettoggle કરી શકતા નથી;ThinkingConfigદરેક call માટેstream_chatની અંદર બનાવવું જ પડે જેથી દરેક request સ્વતંત્ર રીતે reasoning phase ચાલુ કે બંધ કરી શકે. - SSE generator માંથી અંતિમ
{"type": "done"}frame કાઢી ન નાખો — browserEventSourceclient આ sentinel પર આધાર રાખે છે એ જાણવા માટે કે stream સ્વચ્છ રીતે સમાપ્ત થયો છે; તેને છોડી દેવાથી client પહેલેથી બંધ થયેલા connection ને poll કરતો રહે છે અને error states ધીમા response થી અલગ પાડી શકાતા નથી.
3 hands-on labs come with this lesson — real code, in a cloud IDE. Create a free account to run them. No card.
Free account · no card · straight to the labs
Or get the full path — from
Listen to this lesson
Audio overviews of this lesson's labs and its chapter, from GenBodha Bytes.
More free lessons in Full-Stack GenAI Applications
- Ch 1Build a FastAPI SSE streaming response endpoint
- Ch 1Implement an OpenAI GPT-4o streaming adapter
- Ch 1Implement a Gemini 2.5 Flash streaming adapter with thinking budgetYou are here
- Ch 1Implement an Anthropic Claude streaming adapter
- Ch 1Build a Llama 4 Maverick streaming adapter via Together.ai
- Ch 2Extract structured output with Instructor + Pydantic
- Ch 2Build a usage logging system with token + cost capture