بلاگ پر واپس جائیں

خرابیوں کا ازالہگائیڈ

HTTP 200 کے بعد اسٹریم ناکام: Python میں درمیان اسٹریم غلطیاں پکڑیں

200 اسٹیٹس کے بعد اسٹریم کیوں ٹوٹ سکتی ہے، event: error کیسے پہچانیں، اور ایک Python ریٹرائی جو جزوی متن رکھتی ہے مگر جواب کی نقل نہیں کرتی۔

از CheapTokens Teamشائع شدہ 8 منٹ کا مطالعہ

HTTP 200 OK والی اسٹریم جو message_stop کے بجائے event: error اور overloaded_error پر ختم ہوتی ہے
اس صفحہ پر
  1. 200 واقعی کیا وعدہ کرتا ہے
  2. Python میں اسے پکڑیں
  3. جواب دو بار پرنٹ کیے بغیر دوبارہ کوشش کریں
  4. ہر جلدی رکنا غلطی نہیں ہے
  5. شپ کرنے سے پہلے

آپ کے چیٹ UI نے آدھا جواب اسٹریم کیا، جم گیا، اور لاگ کہتا ہے 200 OK۔ یہ کوئی تضاد نہیں: 200 صرف یہ بتاتا ہے کہ اسٹریم شروع ہوا، یہ نہیں کہ وہ مکمل ہوا۔ اگر بعد میں کچھ ٹوٹ جائے، تو سرور صرف اسٹریم کے اندر غلطی بھیج سکتا ہے، اس لیے اسٹیٹس کوڈ پر چیک اسے کبھی نہیں دیکھتا۔ حل یہ ہے کہ اسٹریم کو صرف اسی وقت کامیاب سمجھا جائے جب وہ اپنے فنش سگنل کے ساتھ ختم ہو، اور جب ایسا نہ ہو تو صاف بفر سے دوبارہ کوشش کی جائے۔

اس صفحے کا باقی حصہ دکھاتا ہے کہ ٹوٹی ہوئی اسٹریم کیسی دکھتی ہے، دو Python ہینڈلرز (Anthropic Messages اور OpenAI SDK)، اور ایک ریٹرائی جو جواب کا آغاز دو بار پرنٹ نہیں کرتی۔ دونوں ہینڈلرز CheapTokens کو API اینڈ پوائنٹ کے طور پر استعمال کرتے ہیں: docs میں بیس URLs ہیں، اور نمونہ ماڈل DeepSeek-V4-Flash ہے (id deepseek-ai/DeepSeek-V4-Flash-0731، 380K context، فی جواب زیادہ سے زیادہ 32,768 آؤٹ پٹ ٹوکنز، کسی بھی وقت ایک قیمت — ماڈل صفحہ دیکھیں)۔

200 واقعی کیا وعدہ کرتا ہے#

ایک کورئیر کا تصور کریں جو دروازے پر پارسل کے لیے دستخط کرتا ہے: دستخط ثابت کرتے ہیں کہ وہ پہنچا، یہ نہیں کہ پارسل صحیح سلامت ہے۔ HTTP اسی طرح کام کرتا ہے۔ سرور پہلے اسٹیٹس لائن اور ہیڈرز بھیجتا ہے، پھر باڈی ٹکڑوں میں آتی ہے۔ اگر ماڈل بیک اینڈ آدھے راستے میں اوورلوڈ ہو جائے، تو اسٹیٹس لائن پہلے ہی جا چکی ہوتی ہے۔

Anthropic کا API errors صفحہ (1 نومبر 2026 کو چیک کیا گیا) براہ راست کہتا ہے: جب آپ SSE پر اسٹریمنگ جواب وصول کرتے ہیں، تو API کے 200 واپس کرنے کے بعد غلطی ہو سکتی ہے، اور پھر معیاری ایرر ہینڈلنگ لاگو نہیں ہوتی۔ اس کے اسٹریمنگ docs اس ایونٹ کی شکل بتاتے ہیں:

text
event: error
data: {"type": "error", "error": {"type": "overloaded_error", "message": "Overloaded"}}

overloaded_error عام طور پر نان اسٹریمنگ کال پر HTTP 529 کے مساوی ہے (زیادہ ٹریفک سے عارضی اوورلوڈ)۔ وہی صفحہ 500 api_error (ایکسپونینشل بیک آف کے ساتھ دوبارہ کوشش) اور 504 timeout_error (لمبی درخواستوں کے لیے اسٹریمنگ استعمال کریں) کی فہرست دیتا ہے۔ وائر پر ٹوٹی ہوئی اسٹریم ایسی دکھتی ہے:

ایک اسٹریم جو 200 OK سے شروع ہوتی ہے اور message_stop کے بجائے event: error پر ختم ہوتی ہے
درمیان اسٹریم غلطی کی ساخت: کنٹینٹ ایونٹس رک جاتے ہیں اور event: error وہاں آتا ہے جہاں message_stop ہونا چاہیے (شکل Anthropic کے اسٹریمنگ docs سے)

Docs یہ بھی خبردار کرتے ہیں کہ نئی ایونٹ اقسام شامل کی جا سکتی ہیں، اس لیے آپ کے کوڈ کو نامعلوم ایونٹس کو خوش اسلوبی سے ہینڈل کرنا چاہیے۔ اسی لیے نیچے دیے گئے ہینڈلرز ہر ایرر بائٹ کو پہچاننے کی کوشش نہیں کرتے۔ وہ ایک سوال پوچھتے ہیں: کیا اسٹریم اپنے فنش سگنل کے ساتھ ختم ہوئی؟

  • Anthropic Messages: آخری ایونٹ message_stop ہے، اور message_delta میں stop_reason بتاتا ہے کہ یہ کیوں ختم ہوا (end_turn، max_tokens، stop_sequence)۔
  • OpenAI Chat Completions: آخری چنک میں غیر خالی finish_reason ہوتا ہے (stop، length، …)۔

کوئی فنش سگنل نہیں، یا راستے میں کوئی استثنا — جواب نامکمل ہے، اسٹیٹس نے کچھ بھی کہا ہو۔

Python میں اسے پکڑیں#

ایک سپورٹ بوٹ لیں جو براؤزر کو جوابات اسٹریم کرتا ہے۔ لوپ متن کو بفر میں جمع کرتا ہے اور تین چیزیں واپس بتاتا ہے: اب تک کا متن، آیا اسٹریم مکمل ہوئی، اور اگر کوئی غلطی تھی تو وہ۔ جو SDK آپ استعمال کرتے ہیں اس کا ٹیب منتخب کریں۔

ایک ہینڈلر، دو SDKs

Anthropic SDK

python
import os
import anthropic

client = anthropic.Anthropic(
    base_url='https://api.cheaptokens.dev',  # no /v1 for the Anthropic SDK
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'deepseek-ai/DeepSeek-V4-Flash-0731'

def ask_once(prompt, on_text=lambda t: None):
    parts, finished = [], False
    try:
        with client.messages.stream(
            model=MODEL,
            max_tokens=1024,
            messages=[{'role': 'user', 'content': prompt}],
        ) as stream:
            for event in stream:
                if event.type == 'content_block_delta' and event.delta.type == 'text_delta':
                    parts.append(event.delta.text)
                    on_text(event.delta.text)
                elif event.type == 'message_stop':
                    finished = True
    except anthropic.APIError as err:
        return ''.join(parts), False, err
    return ''.join(parts), finished, None

OpenAI SDK

python
import os
import openai

client = openai.OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'deepseek-ai/DeepSeek-V4-Flash-0731'

def ask_once(prompt, on_text=lambda t: None):
    parts, finish = [], None
    try:
        stream = client.chat.completions.create(
            model=MODEL,
            max_tokens=1024,
            stream=True,
            messages=[{'role': 'user', 'content': prompt}],
        )
        for chunk in stream:
            if not chunk.choices:
                continue
            choice = chunk.choices[0]
            if choice.delta.content:
                parts.append(choice.delta.content)
                on_text(choice.delta.content)
            if choice.finish_reason:
                finish = choice.finish_reason
    except openai.APIError as err:
        return ''.join(parts), False, err
    return ''.join(parts), finish is not None, None

دو تفصیلیں دوبارہ دیکھنے کے قابل ہیں۔ پہلی، دونوں ہینڈلرز SDK کی بنیادی ایرر کلاس (anthropic.APIError، openai.APIError) پکڑتے ہیں، اس لیے فرق نہیں پڑتا کہ SDK کسی مخصوص ناکامی کے لیے کون سی ذیلی کلاس اٹھاتا ہے۔ دوسری، finished فلیگ استثنا سے الگ ہے: ایک اسٹریم بغیر کسی استثنا کے بھی جلدی ختم ہو سکتی ہے، اور اس صورت کو بھی ناکامی شمار ہونا چاہیے۔

openai-python README مزید بتاتا ہے کہ OpenAI کی طرف کیا توقع رکھیں (1 نومبر 2026 کو چیک کیا گیا): اسٹریم میں ریڈ ٹائم آؤٹ APITimeoutError اٹھاتا ہے، دیگر HTTPX ناکامیاں APIConnectionError اٹھاتی ہیں اور اصل استثنا __cause__ میں ہوتا ہے۔ اگر اسٹریم ناکام ہونے کے بجائے رک جائے، تو OpenAI Python SDK میں ٹائم آؤٹس اور ریٹرائیز دیکھیں۔

جواب دو بار پرنٹ کیے بغیر دوبارہ کوشش کریں#

یہاں ایک بگ ہے جسے شپ کرنا آسان ہے۔ پہلی کوشش "The refund policy is…" پرنٹ کرتی ہے، مر جاتی ہے، ریٹرائی دوبارہ شروع ہوتی ہے اور پھر "The refund policy is…" پرنٹ کرتی ہے — اب صارف آغاز دو بار دیکھتا ہے، یا ایک جملہ اپنی ہی ری اسٹارٹ سے چپکا ہوا۔ عام وجہ ایک مشترکہ بفر ہے: answer += text کوشش کے باہر رہتا ہے، اس لیے دوسری کوشش پہلی میں شامل کر دیتی ہے۔

علاج بورنگ ہے۔ ایک کوشش ایک بفر کی مالک ہے، ناکام کوشش کا متن پھینک دیا جاتا ہے (یا صرف لاگ کے لیے رکھا جاتا ہے)، اور ریٹرائی شروع سے ہوتی ہے:

python
import time

def ask_with_retry(prompt, attempts=3, on_text=lambda t: None, on_restart=lambda: None):
    for attempt in range(attempts):
        text, finished, err = ask_once(prompt, on_text)
        if finished:
            return text
        status = getattr(err, 'status_code', None)
        if status is not None and 400 <= status < 500 and status not in (408, 409, 429):
            raise err  # a 400/401/404 will not fix itself
        print(f'attempt {attempt + 1} failed after {len(text)} chars: {err!r}')
        if attempt == attempts - 1:
            break
        on_restart()  # clear the message box in your UI
        time.sleep(2 ** attempt)  # 1 s, 2 s
    raise RuntimeError('stream failed after all attempts')

on_restart وہ حصہ ہے جو نقل روکتا ہے۔ ویب UI میں یہ اگلی کوشش کے اسٹریم ہونے سے پہلے میسج بلبلہ خالی کر دیتا ہے۔ سادہ ٹرمینل میں آپ ان پرنٹ نہیں کر سکتے، اس لیے یا تو صرف finished کے درست ہونے کے بعد پرنٹ کریں، یا ایک نظر آنے والی --- retrying --- لائن پرنٹ کریں۔ شروع سے دوبارہ کوشش پورا پرامپٹ دوبارہ بھیجتی ہے، اس لیے نئی کوشش کے لیے ان پٹ کا دوبارہ بل لگتا ہے؛ زیادہ تر چیٹ جوابات کے لیے یہ صاف جواب کی مناسب قیمت ہے۔

ترتیب دیں

ریٹرائی کے مراحل کو اس ترتیب میں رکھیں جس ترتیب سے آپ کا کوڈ انہیں چلانا چاہیے

  1. ٹوٹی ہوئی اسٹریم پڑھنا بند کریں اور اس کا جزوی متن لاگ کے لیے رکھیں
  2. غلطی چیک کریں: 408، 409 اور 429 کے علاوہ 4xx کا مطلب رکنا ہے، دوبارہ کوشش نہیں
  3. میسج باکس صاف کریں اور نیا بفر شروع کریں
  4. ایکسپونینشل بیک آف کے ساتھ انتظار کریں (1 سیکنڈ، پھر 2 سیکنڈ)
  5. پوری درخواست دوبارہ بھیجیں اور صرف نیا جواب دکھائیں

Anthropic کا SDK پہلے ہی عارضی ناکامیوں — کنکشن ایررز، ریٹ لمٹس، 5xx — کو ایکسپونینشل بیک آف کے ساتھ دوبارہ کوشش کرتا ہے، بطور ڈیفالٹ 2 بار، retry-after کا احترام کرتے ہوئے، اور max_retries اسے تبدیل کرتا ہے۔ یہ درخواست کا احاطہ کرتا ہے۔ 200 کے بعد مرنے والی اسٹریم الگ کہانی ہے: docs کہتے ہیں کہ وہاں معیاری ایرر ہینڈلنگ لاگو نہیں ہوتی، اس لیے اوپر کا لوپ آپ کو رکھنا ہے۔ صرف 429 کے لیے، ایک الگ واک تھرو ہے: 429 Too Many Requests: بیک آف کے ساتھ ریٹرائیز۔

ہر جلدی رکنا غلطی نہیں ہے#

دوبارہ کوشش کرنے سے پہلے، چیک کریں کہ جواب کیوں ختم ہوا۔ ایک اسٹریم جو stop_reason: max_tokens (Anthropic) یا finish_reason: length (OpenAI) کے ساتھ ختم ہوتی ہے، وہ مکمل ہوئی ہے — اس نے صرف آپ کی مقرر کردہ حد کو چھو لیا۔ اسے دوبارہ کوشش کرنے سے وہی کٹا ہوا متن اور وہی بل آتا ہے۔ اس کے بجائے حد بڑھائیں؛ کٹے ہوئے جواب کی گائیڈ اس پر روشنی ڈالتی ہے۔

ایک تیسرا گروپ ہے: نیٹ ورک۔ آئیڈل کنکشنز راستے میں گر سکتے ہیں، جو کنکشن ایرر یا بغیر کسی ایونٹ کے رکنے والی اسٹریم کے طور پر ظاہر ہوتا ہے۔ اسے درمیان اسٹریم غلطی کی طرح ہینڈل کریں، اور اگر یہ بار بار ہو تو عام کنکشن ایررز دیکھیں۔ لمبی جنریشنز کے لیے، Anthropic ایک بہت بڑی نان اسٹریمنگ max_tokens کے بجائے اسٹریمنگ تجویز کرتا ہے، اس لیے آپ پہلے ہی اس کے درست پہلو پر ہیں۔

شپ کرنے سے پہلے#

ہر ناکام کوشش کی ریکویسٹ id محفوظ کریں۔ Anthropic کے ایرر باڈیز میں request_id فیلڈ اور request-id رسپانس ہیڈر ہوتا ہے، اور Python SDK message._request_id ظاہر کرتا ہے؛ اس id کے ساتھ ناکامی قابلِ سراغ ہوتی ہے، محض زبانی نہیں۔

چیک لسٹ

اسٹریمنگ ایرر ہینڈلنگ، درست طریقے سے

اگر آپ ہینڈلر کو چلتا دیکھنا چاہتے ہیں، تو اسے اپنی کلید پر پوائنٹ کریں: docs میں API کلید بنائیں، CHEAPTOKENS_API_KEY سیٹ کریں، اور ask_with_retry('Explain SSE in two sentences') کال کریں۔ Python میں اسٹریمنگ کی بنیادی باتوں کے لیے، Python میں اسٹریمنگ جوابات سے شروع کریں؛ Anthropic کی طرف CheapTokens کے ساتھ Anthropic Python SDK میں شامل ہے۔

ریٹرائی فلو: اسٹریم شروع ہوتی ہے، غلطی آتی ہے، جزوی متن گرا دیا جاتا ہے، درخواست دوبارہ بھیجی جاتی ہے
پانچ مراحل میں ریٹرائی لوپ: شروع، ایرر ایونٹ، جزوی بفر گرا دیں، بیک آف، دوبارہ پوچھیں