AR ▾

API LLM بدون رقابة متوافق مع OpenAIuncensoredapis.com

الرئيسيةالأخطاء

أخطاء وحدود المعدل: دليل عملي لـ Uncensored API

تقضي أكواد الإنتاج وقتاً أطول في مسارات الفشل منها في المسار الناجح. يدرج هذا الدليل كل رمز حالة يعيده Uncensored API، ويوضح أيها يستحق إعادة المحاولة، ويوفر لك تخفيف الحمل من جانب العميل لحد 300 طلب في الدقيقة في Python asyncio وNode، بالإضافة إلى طريقة هادئة لعرض خطأ 402 للمستخدمين.

تم التحديث

نقاط رئيسية

  1. أعد المحاولة فقط مع أخطاء 429 و503 والأخطاء النقلية؛ أصلح أخطاء 400 و401 و403 و404 و402 عند السبب.
  2. تصل الأخطاء بصيغة JSON مع كائن خطأ يحتوي على الرمز والرسالة، لذا افصل حسب الحالة أولاً، ثم الرمز.
  3. دلو الرموز الذي يمتلئ بـ 5 رموز في الثانية يضمن بقاء مفتاح واحد ضمن حد 300 طلب في الدقيقة دون ضغط من الخادم.
  4. يعني 402 أن رصيد المسبق الدفع قد نفد أو انتهت التجربة؛ عامله كحالة منتج، وليس كانهيار.

غلاف الخطأ

يعيد كل فشل جسم JSON يحتوي على كائن واحد في المستوى الأعلى error. حقلان مهمان: code، سلسلة نصية للآلة قصيرة، وmessage، نص مخصص للسجلات والبشر.

{
  "error": {
    "code": "no_credit",
    "message": "Balance used up or trial expired."
  }
}

قرر بهذا الترتيب. انظر إلى حالة HTTP أولاً، لأنها تخبرك بفئة المشكلة. استخدم code فقط لفصل الحالات التي تشترك في حالة واحدة، ولا تحلل message للمنطق، لأن الصياغة قد تتغير. لشكل الاستجابات الناجحة، انظر إلى مرجع API.

رموز الحالة والأسباب والإجراءات

الحالةالرمزالسبب النموذجيإعادة المحاولة؟الإجراء
400طلب غير صالحJSON غير صالح، أو رموز الموجّه زائد max_tokens فوق 100,000لاأصلح الحمولة؛ قلل سجل المحادثة أو اخفض max_tokens
401مفتاح غير صالحرأس مفقود، خطأ مطبعي، أو مفتاح تم تجديدهلاأعد تحميل المفتاح من متجر الأسرار الخاص بك
402no_creditالرصيد مستنفد أو انتهت التجربة المجانيةلااشحن رصيد المسبق الدفع، ثم استأنف
403content_blockedالطلب يتضمن محتوى جنسيًا يتعلق بالقاصرينلالا تعيد الإرسال؛ اعرض رسالة السياسة الخاصة بك
404غير موجودمسار خاطئ، مثل بادئة /v1 المفقودةلاتحقق من Base URL
429حد المعدلأكثر من 300 طلب في الدقيقة على مفتاح واحدنعمتراجع مع اهتزاز؛ أضف تخفيف حمل العميل
503upstream_busyضغط سعة مؤقتنعمأعد المحاولة بعد بضع ثوانٍ

يحتاج 403 إلى تركيز: إنه قرار سياسة، وليس عطلاً عابراً. إعادة محاولة الطلب نفسه يهدر الرصيد ويعيد نفس الإجابة. حالة 400 التي تُفاجئ الناس أكثر هي مجموع السياق. موجّه بحجم 60,000 رمز مع max_tokens بقيمة 8,000 يتجاوز السعة بمقدار 4,000 ويفشل رغم أن كل رقم صالح بمفرده.

سياسة إعادة تعمل بشكل صحيح

استخدم سياسة صغيرة ومحددة وفعّلها فقط حيث يمكن أن تساعد إعادة المحاولة.

  1. أدر قائمة السماح بالأخطاء القابلة لإعادة المحاولة: 429، 503، إعادة تعيين الاتصال وانتهاء المهلة. كل الأخطاء الأخرى تُرفع فوراً.
  2. تراجع أسي مع زحزحة: انتظر 1، 2، 4، 8 ثوانٍ زائد جزء عشوائي من الثانية، بحد أقصى 30. تمنع الزحزحة محاولة العديد من العمال للمراجعة في تزامن.
  3. حدد المحاولات بخمسة. بعد ذلك، يكون العطل أكثر احتمالاً من الحظ السيء، ويجب أن تفشل المهام المكدسة بشكل واضح.
  4. عطّل إعادة المحاولة التلقائية في SDK (max_retries=0) عند تشغيل حلقة خاصة بك، وإلا ستتضاعف المحاولات ويفقد جهاز الحدّ عدده.
  5. أعد محاولة البث من البداية. البث الذي يفشل في منتصف الطريق لا يمكن استئنافه؛ تخلص من النص الجزئي وأعد إصدار الطلب، أو احتفظ به واطلب استمراراً.

بالنسبة لـ 503، التوجيه هو الانتظار بضع ثوانٍ، لذا ابدأ تأخيرك الأول عند ثانية أو ثانيتين بدلاً من الضرب الفوري. بالنسبة لـ 429، العلاج هو إبطاء السرعة، وهو ما تautomate القسم التالي.

البقاء تحت 300 طلب في الدقيقة

الحد هو 300 طلب في الدقيقة لكل مفتاح، أي خمسة في الثانية متوسطاً. يفرض دلو الرموز ذلك بالضبط: يحتفظ بحد صغير من الرموز، ويمتلئ بـ 5 رموز في الثانية، ويجعل كل طلب يستهلك رمزاً واحداً. إذا كان الدلو فارغاً، ينام المتصل حتى يوجد رمز.

يستخدم الإصدار بايثون asyncio. تتشارك جميع المهام العشرين دلو الرمز المميز وقفل، لذا لا يمكن للتعاونيات المتزامنة أن تتجاوز الحد. لاحظ أن الدلو يحسب كل محاولة، بما في ذلك إعادة المحاولة.

import asyncio, os, random, time
from openai import AsyncOpenAI, APIStatusError, APIConnectionError

class TokenBucket:
    """Allows `rate` acquisitions per `per` seconds, with a small burst."""
    def __init__(self, rate=300, per=60.0, burst=10):
        self.fill = rate / per          # tokens added per second (5.0)
        self.cap = burst
        self.tokens = burst
        self.stamp = time.monotonic()
        self.lock = asyncio.Lock()

    async def acquire(self):
        async with self.lock:
            while True:
                now = time.monotonic()
                self.tokens = min(self.cap, self.tokens + (now - self.stamp) * self.fill)
                self.stamp = now
                if self.tokens >= 1:
                    self.tokens -= 1
                    return
                await asyncio.sleep((1 - self.tokens) / self.fill)

client = AsyncOpenAI(base_url="https://api.uncensoredapis.com/v1",
                     api_key=os.environ["API_KEY"], max_retries=0)
bucket = TokenBucket()

RETRYABLE = {429, 503}

async def ask(prompt, attempts=5):
    for n in range(attempts):
        await bucket.acquire()
        try:
            r = await client.chat.completions.create(
                model="uncensored",
                messages=[{"role": "user", "content": prompt}],
                max_tokens=200,
            )
            return r.choices[0].message.content
        except APIStatusError as e:
            if e.status_code not in RETRYABLE or n == attempts - 1:
                raise
        except APIConnectionError:
            if n == attempts - 1:
                raise
        await asyncio.sleep(min(30, 2 ** n) + random.uniform(0, 1))

async def main():
    prompts = [f"Give one-line fact number {i} about tides." for i in range(1, 21)]
    results = await asyncio.gather(*(ask(p) for p in prompts))
    print(len(results), "answers; first:", results[0])

asyncio.run(main())

شغّله مع تصدير API_KEY. تتسع عشرين طلباً داخل الارتفاع زائد بضع ثوانٍ من التعبئة، لذا سترى أنه ينتهي بسرعة؛ ارفع العدد إلى 600 وتمتد العملية إلى نحو دقيقتين، وهو ما يفعله الحدّ في عمله.

يستخدم الإصدار Node الحزمة الرسمية في وحدة ES، ويربط عمليات الاستحواذ عبر طابور وعد حتى يتم تقديم المتصلين بالترتيب.

import OpenAI from "openai";

class TokenBucket {
  constructor(rate = 300, perMs = 60000, burst = 10) {
    this.fillPerMs = rate / perMs;
    this.cap = burst;
    this.tokens = burst;
    this.stamp = Date.now();
    this.queue = Promise.resolve();
  }
  acquire() {
    // Serialize callers so the bucket math never races.
    this.queue = this.queue.then(async () => {
      for (;;) {
        const now = Date.now();
        this.tokens = Math.min(this.cap, this.tokens + (now - this.stamp) * this.fillPerMs);
        this.stamp = now;
        if (this.tokens >= 1) { this.tokens -= 1; return; }
        await new Promise((r) => setTimeout(r, Math.ceil((1 - this.tokens) / this.fillPerMs)));
      }
    });
    return this.queue;
  }
}

const client = new OpenAI({
  baseURL: "https://api.uncensoredapis.com/v1",
  apiKey: process.env.API_KEY,
  maxRetries: 0,
});
const bucket = new TokenBucket();

async function ask(prompt, attempts = 5) {
  for (let n = 0; n < attempts; n++) {
    await bucket.acquire();
    try {
      const r = await client.chat.completions.create({
        model: "uncensored",
        messages: [{ role: "user", content: prompt }],
        max_tokens: 200,
      });
      return r.choices[0].message.content;
    } catch (err) {
      const retryable = err.status === 429 || err.status === 503 || err.status === undefined;
      if (!retryable || n === attempts - 1) throw err;
      await new Promise((r) => setTimeout(r, Math.min(30000, 2 ** n * 1000) + Math.random() * 1000));
    }
  }
}

const prompts = Array.from({ length: 20 }, (_, i) => `Give one-line fact number ${i + 1} about tides.`);
const answers = await Promise.all(prompts.map((p) => ask(p)));
console.log(answers.length, "answers; first:", answers[0]);

حدد حجم الدفعة بحذر. تسمح الدفعة بـ 10 بحد قصير بينما يبقى المعدل المستدام خمسة في الثانية. إذا شاركت عدة عمليات مفتاحاً واحداً، قسم المعدل بينها، مثلاً 100 في الدقيقة لكل منها عبر ثلاثة عمال، أو وجه كل الحركة عبر طابور واحد.

التعامل مع خطأ 402 بسلاسة في منتجك

يعني 402 مع الرمز no_credit أن رصيد مسبق الدفع وصل إلى الصفر أو انتهت صلاحية رصيد تجريبي مجاني. إنه الخطأ الوحيد الذي لا ينبغي أن يراه مستخدموك النهائيون كسجل مكدس، وغالباً ما يشير إلى شيء يمكنك أنت فقط إصلاحه: شحن الرصيد.

ابنِ ثلاث طبقات. أولاً، detect ذلك مركزياً في غلاف واحد حتى لا يضطر أي موقع استدعاء للذاكرة. ثانياً، translate ذلك: يتلقى المتصفح أو تطبيق الهاتف المحمول حالة محايدة مثل capacity_paused ويعرض شاشة ودودة، وليس رسالة المصدر. ثالثاً، alert نفسك، لأن كل مستخدم يتأثر في وقت واحد؛ أرسل إشعاراً لمالك الحساب وقم بتبديل مفتاح الميزة حتى يتوقف واجهة المستخدم عن إرسال الطلبات.

# server side: translate the upstream 402 into a product state
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from openai import AsyncOpenAI, APIStatusError
import os

app = FastAPI()
client = AsyncOpenAI(base_url="https://api.uncensoredapis.com/v1", api_key=os.environ["API_KEY"])

@app.post("/api/reply")
async def reply(body: dict):
    try:
        r = await client.chat.completions.create(
            model="uncensored", messages=body["messages"], max_tokens=400)
        return {"text": r.choices[0].message.content}
    except APIStatusError as e:
        if e.status_code == 402:
            # Tell the client app to show a "temporarily unavailable" screen,
            # and page yourself to top up the prepaid balance.
            return JSONResponse({"state": "capacity_paused"}, status_code=503)
        if e.status_code == 429 or e.status_code == 503:
            return JSONResponse({"state": "busy_retry"}, status_code=503)
        raise

أضف حماية استباقية أيضاً. اجمع usage.total_tokens لكل طلب في دفتر حساباتك الخاص وحذر عند عتبة تختارها، حتى يحدث شحن الرصيد قبل أن يُحظر المستخدم. تذكر أن رصيد تجريبي مجاني هو $0.50 ويستمر 7 أيام، لذا يمكن لنموذج أولي أن يصل إلى 402 من انتهاء الصلاحية وحدها مع بقاء المال غير منفق. أسعار التخطيط موجودة في صفحة الأسعار.

بمجرد أن يصبح الرصيد موجباً مرة أخرى، أعد تشغيل العمل الذي كدسته أثناء الإيقاف المؤقت. يجب أن يزيل غلافك علم الإيقاف المؤقت عند أول نجاح.

ما الذي يجب تسجيله والتنبيه عليه

السجلات الجيدة تجعل الحوادث قصيرة. سجّل، لكل طلب: معرف الطلب الخاص بك، معرف الإكمال id عند وجوده، حالة HTTP، الخطأ code، رقم المحاولة، واستخدام الرمز المميز. لا تسجل رأس التفويض أو الموجّهات الكاملة التي تحمل بيانات شخصية.

التنبيهات المفيدة، حسب ترتيب الأولوية: أي خطأ 402 (فوري)، سلسلة مستمرة من أخطاء 401 (لم يتم نشر المفتاح المتجدد في كل مكان)، معدل 429 أعلى من بضع في المئة من المكالمات (جهاز التحكم في المعدل غير مضبوط بشكل صحيح)، وعناقيد 503 (تراجع على نطاق أوسع، فكر في طابور). لوحة معلومات لعدد الحالات في الدقيقة تجعل جميع الأخطاء الأربعة مرئية في لمحة. عند التعامل معها بشكل جيد، تصبح هذه الأكواد إشارات روتينية بدلاً من انقطاعات.

اختبار مسارات الفشل الخاصة بك قبل الإطلاق

معالجة الأخطاء التي لم تعمل أبداً هي تخمين. يمكنك ممارسة معظم الفروع دون انتظار انقطاع حقيقي، باستخدام طلبات رخيصة ونماذجك المزدوجة الخاصة.

  • 401: اتصل بـ GET /v1/models بمفتاح خاطئ عمداً وتأكد من أن تطبيقك يظهر خطأ تكوين للمشغلين، وليس للعملاء.
  • 404: طلب /v1/chat/completion (مفرد) مرة واحدة وتأكد من أن سطر السجل يحدد المسار، بحيث يكون الخطأ في عنوان URL الأساسي واضحاً في دقائق.
  • 400: أرسل max_tokens بقيمة 32,000 مع موجّه مدعم إلى نحو 50,000 رمز. المجموع يتجاوز 100,000، ويمكنك مشاهدة مسار التحقق من الصحة يشتغل.
  • 429 و503: لا يمكنك استداعاهما عند الطلب، لذا قم بتضميدهما. لف عميل HTTP الخاص بك في مزيف يرفع الحالة لأول مكالمتين، ثم ينجح، ويتحقق من أن حلقتك تنام، وإعادة المحاولة وتعيد الإجابة النهائية.
  • 402: ضمده أيضاً، وتأكد من أن واجهة المستخدم تعرض شاشة الإيقاف المؤقت، وينطلق التنبيه ولا يتم إعادة محاولة أي شيء في حلقة ضيقة.

اكتب هذه كاختبارات آلية تعمل في التكامل المستمر. اختبار قائم على جدول مع صف واحد لكل حالة من الجداول أعلاه يستغرق دقائق للكتابة ويحمي السياسة من إعادة الهيكلة ذات النوايا الحسنة. أخيراً، شغل اختبار حمل قصير ضد جهاز الحدّ الخاص بك مع محاكاة API: أطلق 1,000 عملية شراء وتأكد من أن أي نافذة 60 ثانية لا تحتوي على أكثر من 300 منحة زائد الارتفاع. هذا يثبت الحساب قبل أن يعتمد حركة المرور الحقيقية عليه. عندما تقرأ دليل حلقة الوكلاء لاحقاً، أعد استخدام نفس الغلاف هناك، لأن الوكلاء يضاعفون عدد الطلبات ويصلون إلى هذه الحدود أولاً.

أسئلة وأجوبة

هل يعد خطأ 429 ضد رصيدي؟

تحقق من أرقام الاستخدام في دفتر حساباتك الخاص بدلاً من الافتراض. على أي حال، فإن الإصلاح هو إبطاء السرعة وإعادة المحاولة مع التراجع.

هل يجب أن أعيد محاولة استجابة content_blocked 403؟

لا. إنه حظر سياسة وطلب مطابق يعيد نفس النتيجة. أظهر رسالتك الخاصة وتقدم.

يعيد مفتاحي فجأة خطأ 401. ماذا تغير؟

في معظم الأحيان، تم تجديد المفتاح، مما يبطل القديم على الفور. انسخ المفتاح الجديد من حسابك وأعد نشره.

كم عدد العمال الذين يمكنهم مشاركة مفتاح واحد؟

جميعهم يشاركون حد 300 طلب في الدقيقة. قسم المعدل عبر العمال أو وجه المكالمات عبر طابور واحد.

مفتاحك على بُعد نموذج واحد

أنشئ حساباً، انسخ المفتاح، غيّر عنوان URL الأساسي. هذا هو الإعداد الكامل.