برنامه نویسی

ساخت افزونه های Litellm با `Async_pre_call_hook` برای حالت پروکسی

شما از پروکسی Litellm استفاده می کنید و از توانایی آن در متحد کردن دسترسی به مدلهای مختلف زبان بزرگ (LLM) قدردانی می کنید. اما اگر به بیشتر نیاز داشته باشید چه می کنید؟ چه می شود اگر شما نیاز دارید که درخواست های پویا را بر اساس بودجه کاربر مسیریابی کنید ، سیاست های امنیتی سفارشی را اجرا کنید پیش از ضربه زدن به یک مدل گران قیمت ، اصلاح در پرواز یا رد درخواست ها بر اساس منطق پیچیده تجارت؟ رویدادهای استاندارد تماس تلفنی پس از آنها اتفاق می افتد ، اما مداخله واقعی نیاز به درگیر شدن دارد پیش از عمل اصلی

این جایی است که افزونه های Litellm ، با استفاده از async_pre_call_hook، بیا بازی این قلاب مسلماً قدرتمندترین نقطه مداخله در چرخه چرخه درخواست پروکسی ، به طور خاص برای توسعه دهندگان که نیاز به رهگیری ، تجزیه و تحلیل ، اصلاح یا حتی درخواست های مسدود دارند ، طراحی شده است فقط لحظاتی قبل از ارسال آنها به ارائه دهنده Target LLM.

این راهنمای جامع شما را از طریق هر آنچه را که باید بدانید برای تسلط بر این است ، طی می کند async_pre_call_hook و افزونه های پیشرفته و سفارشی را برای استقرار پروکسی Litellm خود بسازید.

این راهنما برای کیست؟

  • توسعه دهندگان پایتون راه حل های سفارشی را در بالای پروکسی Litellm ایجاد می کنند.
  • مهندسان پلتفرم که نیاز به اجرای سیاست های خاص ، اقدامات امنیتی یا کنترل هزینه دارند.
  • هرکسی که مایل به اضافه کردن مسیریابی ، اعتبار سنجی یا درخواست غنی سازی سفارشی به طور مستقیم در لایه پروکسی است.
  • توسعه دهندگان با هدف ایجاد ماژول های پلاگین Litellm Sharable.

آنچه یاد خواهید گرفت:

  • نقش و زمان دقیق از async_pre_call_hook در جریان درخواست پروکسی.
  • یک شیرجه عمیق به امضای هوک: پارامترهای ورودی آن (user_api_key_dictبا cacheبا dataبا call_type) و اهمیت آنها.
  • چگونه می توان از زمینه غنی ارائه شده توسط استفاده کرد UserAPIKeyAuth برای منطق افزونه آگاه کاربر.
  • تکنیک های مؤثر برای بازرسی و با خیال راحت اصلاحات مهم data فرهنگ لغت
  • استفاده از DualCache برای ساخت افزونه های حالت (مانند محدود کننده های نرخ یا ردیاب های استفاده).
  • تسلط بر مقادیر بازگشت هوک (Noneبا dictبا str) و رسیدگی به استثنا (HTTPException) برای کنترل سرنوشت درخواست.
  • دستورالعمل های گام به گام برای ایجاد ، پیکربندی و آزمایش اولین افزونه خود.
  • الگوهای افزونه مشترک و پیشرفته: مسیریابی پویا ، اعتبار سنجی ورودی ، ماسک PII ، اجرای سیاست ، رد سفارشی ، تنظیم آزمایش A/B و موارد دیگر.
  • بهترین روشها برای نوشتن افزونه های اجرا ، ایمن و قابل نگهداری.
  • استراتژی های اشکال زدایی برای عیب یابی در اجرای قلاب خود.

قسمت 1: چرا async_pre_call_hook آیا شما برای افزونه ها هستید

Litellm نقاط مختلف پاسخ به تماس را ارائه می دهد (log_success_eventبا log_failure_event، و غیره) ، در درجه اول برای رعایت – ورود به سیستم آنچه اتفاق افتاده است پس از واقعیت در حالی که ضروری است ، آنها به شما اجازه نمی دهند تغییر دوره عمل

در async_pre_call_hook به دلیل ویژگی های منحصر به فرد متناسب با توسعه افزونه ، از هم جدا می شود:

  1. زمان بندی همه چیز است: این در بحرانی ترین مقطع اجرا می شود: پس از احراز هویت اولیه و درخواست آماده سازی ، اما پیش از فراخوان بالقوه پرهزینه و وقت گیر به API واقعی LLM (مانند Openai ، Anthropic ، Cohere و غیره). این پنجره طلایی شما برای مداخله است.
  2. مداخله ، نه فقط مشاهده: برخلاف ورود به سیستم تماس ، async_pre_call_hook برای عملبشر مقدار بازگشت آن یا استثنائاتی که افزایش می دهد مستقیماً دیکته می کند که آیا درخواست پیش می رود ، اصلاح می شود یا به طور کامل رد می شود.
  3. متن غنی: این نه تنها درخواست بار درخواست بلکه اطلاعات متنی حیاتی را نیز دریافت می کند:
    • داده های احراز هویت کاربر/کلید دقیق (UserAPIKeyAuth).
    • دسترسی به حافظه نهان مشترک پروکسی (DualCache).
    • نوع خاصی از تماس صورت گرفته (call_type).
  4. جهش پذیری: دریافت می کند data فرهنگ لغت (بار درخواست آماده شده برای LLM) و می تواند به طور مستقیم اصلاح کنید قبل از اجازه درخواست درخواست.
  5. قدرت خاص پروکسی: این قلاب یکی از ویژگی های پروکسی محیط زیست ، استفاده از زیرساخت های پروکسی (احراز هویت ، حافظه پنهان ، پیکربندی) برای ایجاد منطق پیچیده ای که در یک ادغام ساده کتابخانه امکان پذیر نیست.

به طور خلاصه ، اگر افزونه شما نیاز دارد:

  • مدل هدف را به صورت پویا تغییر دهید.
  • پارامترهای درخواست را اضافه ، حذف یا اصلاح کنید (temperatureبا max_tokensبا messages، و غیره).
  • قبل از هزینه کردن در یک تماس LLM ، در برابر قوانین سفارشی تأیید کنید.
  • کنترل دسترسی ریز دانه را بر اساس نقش های کاربر ، بودجه یا مجوزهای ذخیره شده در ابرداده کلیدی اجرا کنید.
  • درخواست ها را بر اساس تجزیه و تحلیل محتوا یا بررسی سیاست های خارجی رد کنید.
  • کنترل محدود کننده نرخ پیشرفته یا کنترل همزمان را اجرا کنید.
  • قبل از ترک زیرساخت های شما ، داده های حساس را ماسک کنید.

… پس async_pre_call_hook قلاب مورد نیاز شماست


قسمت 2: آناتومی async_pre_call_hook امضاء

درک ورودی ها و خروجی های قلاب برای ساخت افزونه ها اساسی است. بیایید امضای ارائه شده توسط CustomLogger کلاس پایه:

# Defined in litellm.integrations.custom_logger.CustomLogger
async def async_pre_call_hook(
    self,
    user_api_key_dict: UserAPIKeyAuth,  # <<< Your window into user/key context
    cache: DualCache,                   # <<< Your tool for managing state
    data: dict,                         # <<< The request payload (mutable!)
    call_type: Literal[                 # <<< The type of LLM operation
        "completion", "text_completion", "embeddings",
        "image_generation", "moderation", "audio_transcription",
        "pass_through_endpoint", "rerank", ... # (Potentially more types)
    ]
) -> Optional[Union[Exception, str, dict]]: # <<< Determines request outcome
    # Your plugin logic goes here
    pass
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بیایید هر مؤلفه را تجزیه کنیم:

  • self: نمونه کلاس کنترل کننده سفارشی شما (یکی از ارث می برد CustomLogger). به شما امکان می دهد به سایر روشها یا ویژگی هایی که ممکن است در کلاس خود تعریف کنید دسترسی پیدا کنید (به عنوان مثال ، پیکربندی بارگذاری شده در طول __init__).

  • user_api_key_dict: UserAPIKeyAuth: این مسلماً با ارزش ترین پارامتر برای افزونه های متنی است. این است نه فقط یک فرهنگ لغت ساده ، اما الف TypedDict (تعریف شده در proxy/_types.py) در مرحله احراز هویت جمعیت (user_api_key_auth.py). این اطلاعات زیادی در مورد کلید API معتبر و نهادهای مرتبط با آن ارائه می دهد. شما می توانید با اطمینان از زمینه هایی مانند:

    • token: هش نسخه کلید API استفاده شده است.
    • key_nameبا key_alias: شناسه های قابل خواندن انسانی برای کلید.
    • user_id: شناسه برای کاربر داخلی مرتبط با کلید (در صورت وجود).
    • team_id: شناسه برای تیم مرتبط با کلید (در صورت وجود).
    • org_id: شناسه سازمان (در صورت استفاده از ویژگی های org).
    • spendبا max_budgetبا soft_budgetبا budget_durationبا expires: بودجه و اطلاعات چرخه عمر برای خود کلید.
    • tpm_limitبا rpm_limitبا max_parallel_requests: محدودیت نرخ پیکربندی شده برای این کلید خاصبشر
    • models: لیستی از نام ها/الگوهای مدل این کلید مجاز به دسترسی است.
    • metadata: الف فرهنگ لغت بحرانی جایی که می توانید هنگام ایجاد/به روزرسانی کلید (از طریق API یا UI) جفت های ارزش کلید سفارشی را ذخیره کنید. از این به طور گسترده برای پیکربندی افزونه استفاده کنید، مانند ذخیره نقش های کاربر ، پرچم های مجوز ، قوانین مسیریابی سفارشی ، غلبه بر بودجه خاص و غیره.
    • team_spendبا team_max_budgetبا team_tpm_limitبا team_rpm_limitبا team_modelsبا team_metadata: اطلاعات مشابه ، اما از تیم مرتبط با کلید به ارث رسیده است. منطق افزونه شما ممکن است تنظیمات سطح کلید را در سطح تیم در اولویت قرار دهد یا آنها را با هم ترکیب کند.
    • user_role: نقش تعیین شده کاربر (به عنوان مثال ، LitellmUserRoles.PROXY_ADMINبا LitellmUserRoles.INTERNAL_USER).
    • end_user_idبا end_user_tpm_limitبا end_user_rpm_limitبا end_user_max_budget: اطلاعات مربوط به شناسه کاربر نهایی که در درخواست منتقل شده است (data['user']) ، در صورت وجود و ردیابی.
    • parent_otel_span: برای ادغام OpenTelemetry.
    • … و به طور بالقوه زمینه های بیشتری بسته به نسخه Litellm و پیکربندی.
      استفاده از افزونه: برای اجرای هرگونه منطقی که به هویت ، مجوزها ، بودجه یا تنظیمات از پیش تنظیم شده بستگی دارد ، ضروری است.
  • cache: DualCache: نمونه ای از DualCache کلاس (تعریف شده در caching/dual_cache.py). این شیء یک رابط کاربری برای سیستم ذخیره سازی Litellm ، که به طور معمول یک حافظه نهان در حافظه را ترکیب می کند (مانند InMemoryCache) برای سرعت و به طور بالقوه یک حافظه پنهان توزیع شده (مانند RedisCache) برای پایداری و به اشتراک گذاری وضعیت در چندین نمونه پروکسی.

    • روشهای کلیدی: async_get_cache(key)با async_set_cache(key, value, ttl=...)با async_batch_get_cache(keys)با async_batch_set_cache(cache_list, ttl=...)با async_increment_cache(key, value, ttl=...)با async_delete_cache(key)بشر
    • استفاده از افزونه: کاملاً ضروری برای افزونه های حالتبشر در parallel_request_limiter.py مثال برای ذخیره و به روزرسانی تعداد درخواست در هر کلید/کاربر/تیم در هر دقیقه به حافظه نهان متکی است. از آن برای:
      • پیشخوان های محدود کننده / پرتاب.
      • ردیابی فعالیت کاربر اخیر.
      • ذخیره پرچم های موقت یا حالت های مربوط به کاربر یا جریان درخواست.
      • ذخیره سازی از سیستم های خارجی که توسط افزونه پرس و جو شده است (با TTL های مناسب استفاده کنید).
  • data: dict: این است قلب بارگیری درخواست آن Litellm در حال آماده سازی برای ارسال به ارائه دهنده LLM اساسی است. از نظر مهم ، قابل تغییر است. تغییراتی که در این فرهنگ لغت ایجاد می کنید درون قلاب در تماس واقعی ساخته شده منعکس می شود ، اگر قلاب شما اجازه می دهد تا درخواست ادامه یابد.

    • محتوا: شامل پارامترهای استاندارد تماس LLM مانند modelبا messagesبا input (برای تعبیه) ، temperatureبا max_tokensبا streamبا toolsبا function_callبا user، و غیره
    • غنی سازی: همانطور که توسط litellm_pre_call_utils.py، این فرهنگ لغت از قبل به قلاب می رسد غنی شده با metadata (یا litellm_metadata) زیر فرهنگ لغت حاوی اطلاعات متنی حاصل از user_api_key_dict و پارامترهای درخواست/پرس و جو. این بدان معناست که شما اغلب نیازی به مجدداً اطلاعات اساسی از آن ندارید user_api_key_dict اگر از قبل به راحتی قرار داده شده است data['metadata']بشر
    • استفاده از افزونه:
      • بازرسی: مقادیر مانند را بخوانید data['model']با data['messages']با data['user'] برای تصمیم گیری
      • اصلاح: به طور مستقیم مقادیر را تغییر دهید: data['model'] = 'new-model'با data['max_tokens'] = 500با data['messages'].append(...)بشر
      • علاوه بر این: پارامترهای جدید را در صورت پشتیبانی توسط مدل هدف/LITELLM اضافه کنید: data['custom_param'] = 'value'بشر
      • حذف: پارامترها را حذف کنید: del data['frequency_penalty']بشر
  • call_type: Literal[...]: رشته ای که نشان دهنده نوع عملیات LLM است. این به افزونه شما اجازه می دهد تا منطق را به صورت انتخابی اعمال کند.

    • مثالها: "completion"با "embeddings"با "image_generation"با "moderation"با "audio_transcription"با "rerank"با "pass_through_endpoint"بشر
    • استفاده از افزونه: از منطق شرطی استفاده کنید (if call_type == "completion": ...) برای اطمینان از این که افزونه شما فقط برای عملیات مورد نظر به درستی اجرا می شود یا رفتار می کند. به عنوان مثال ، یک افزونه اصلاح سریع باید فقط برای آن اجرا شود "completion"بشر افزونه چک هزینه تعبیه شده فقط باید برای آن اجرا شود "embeddings"بشر
  • مقدار بازگشت (-> Optional[Union[Exception, str, dict]]): این سرنوشت درخواست را پس از اتمام قلاب شما تعیین می کند.

    • بازگشت None (به طور ضمنی یا صریح): ادامه دهید سیگنال هایی که چک های هوک به تصویب رسیده و درخواست باید به ارائه دهنده LLM ادامه یابد. در data فرهنگ لغت (به طور بالقوه در قلاب اصلاح شده) برای تماس استفاده می شود. این روش استاندارد برای اجازه درخواست پس از بازرسی/اصلاح است.
    • بازگشت data (dict): با اصلاحات ادامه دهید. صریحاً (به طور بالقوه اصلاح شده) را برمی گرداند data فرهنگ لغت از نظر عملکردی شبیه به بازگشت None پس از اصلاح data در جای خود ، اما می تواند واضح تر باشد. فرهنگ لغت برگشتی تعویض اصلی data برای تماس LLM
    • بازگشت str: با پاسخ سفارشی (فقط چت/تکمیل) رد کنید. برای call_type “تکمیل” یا “text_completion” ، بازگشت به رد سیگنال های رشته. Litellm این را رهگیری می کند ، LLM را صدا نمی کند، و رشته را به عنوان یک پاسخ موفقیت آمیز استاندارد از دستیار (همانطور که در مثال رد “سلام جهان” مشاهده می شود) قالب بندی می کند. برای دیگری call_typeS ، این ممکن است منجر به خطای 400 یا رفتار غیر منتظره شود – در درجه اول از این برای نقاط پایانی چت استفاده کنید.
    • بالا بردن Exception: با خطا رد کنید. این روش استاندارد برای رد زور درخواست به دلیل نقض خط مشی ، ورودی نامعتبر ، محدودیت بودجه یا چک های خارجی شکست خورده است.
      • افزایش fastapi.HTTPException(status_code=..., detail=...) بسیار توصیه می شود این امکان را به شما می دهد تا کد دقیق وضعیت HTTP را کنترل کنید (به عنوان مثال ، 400 درخواست بد ، 401 غیرمجاز ، 403 ممنوع ، 429 درخواست بیش از حد ، 500 خطای سرور داخلی) و پیام خطا به مشتری اصلی بازگشت.
      • در parallel_request_limiter.py کاربردهای HTTPException(status_code=429, ...) برای محدودیت نرخ بیش از خطاهای.

قسمت 3: اجرای افزونه خود-گام به گام

بیایید روند ایجاد یک افزونه ساده را طی کنیم. ما یک افزونه ایجاد خواهیم کرد که:

  1. بررسی می کند که آیا کاربر نقش خاصی را در ابرداده اصلی خود تعریف کرده است (plugin_access: true).
  2. اگر آنها دسترسی داشته باشند و یک تماس “تکمیل” را انجام می دهند ، حداکثر را تقویت می کند max_tokens محدود کردن
  3. اگر آنها دسترسی نداشته باشند ، درخواست را با خطای ممنوعه 403 رد می کند.

مرحله 1: پرونده کنترل کننده سفارشی را ایجاد کنید

یک فایل پایتون ایجاد کنید (به عنوان مثال ، my_plugins.py) جایی که پروکسی Litellm شما می تواند آن را وارد کند.

# my_plugins.py
import sys
from litellm.integrations.custom_logger import CustomLogger
from litellm.proxy._types import UserAPIKeyAuth # Import the type hint
from litellm.caching.dual_cache import DualCache # Import the type hint
from typing import Optional, Literal, Union
from fastapi import HTTPException # Import for raising specific errors
import litellm # Optional, but good practice

# Define your plugin class, inheriting from CustomLogger
class AccessAndTokenLimitPlugin(CustomLogger):

    def __init__(self, max_tokens_limit: int = 1024):
        """
        Initialize the plugin, potentially with configuration.
        """
        super().__init__() # Call the base class initializer
        self.max_tokens_limit = max_tokens_limit
        print(f"AccessAndTokenLimitPlugin Initialized with max_tokens={self.max_tokens_limit}")

    # Implement the async_pre_call_hook method
    async def async_pre_call_hook(
        self,
        user_api_key_dict: UserAPIKeyAuth,
        cache: DualCache,
        data: dict,
        call_type: Literal[
            "completion", "text_completion", "embeddings",
            "image_generation", "moderation", "audio_transcription",
            "pass_through_endpoint", "rerank" # Add other relevant types
        ],
    ) -> Optional[Union[dict, str]]: # Return type annotation (raising Exception is also an outcome)
        """
        This hook checks user access via metadata and enforces token limits.
        """
        litellm.print_verbose(f"------ AccessAndTokenLimitPlugin Start ------")
        litellm.print_verbose(f"Call Type: {call_type}")
        litellm.print_verbose(f"User/Key Info: {user_api_key_dict}") # Be careful logging sensitive info in prod

        # --- 1. Access Control ---
        # Check for 'plugin_access: true' in the key's metadata
        key_metadata = user_api_key_dict.metadata or {} # Safely get metadata
        if key_metadata.get("plugin_access") is not True:
            litellm.print_verbose(f"Access Denied: Key metadata missing 'plugin_access: true'. Metadata: {key_metadata}")
            # Reject with 403 Forbidden
            raise HTTPException(
                status_code=403, # Forbidden
                detail="Access Denied: Your API key does not have permission for this operation via the AccessAndTokenLimitPlugin."
            )

        litellm.print_verbose("Access Granted: 'plugin_access: true' found.")

        # --- 2. Enforce Max Tokens (Only for Completion Calls) ---
        if call_type == "completion":
            original_max_tokens = data.get("max_tokens")
            litellm.print_verbose(f"Original max_tokens: {original_max_tokens}")

            if original_max_tokens is None or original_max_tokens > self.max_tokens_limit:
                litellm.print_verbose(f"Enforcing max_tokens limit: Setting to {self.max_tokens_limit}")
                data["max_tokens"] = self.max_tokens_limit # Modify the data dictionary directly
            else:
                 litellm.print_verbose(f"Existing max_tokens ({original_max_tokens}) is within limit ({self.max_tokens_limit}).")

        # --- 3. Allow Request ---
        # If all checks pass, allow the request to proceed.
        # Returning None implicitly allows the request with any modifications made to 'data'.
        litellm.print_verbose(f"------ AccessAndTokenLimitPlugin End (Allowing) ------")
        return None # Or return data

# Create an instance of your plugin handler
# You can pass configuration here if needed
plugin_handler_instance = AccessAndTokenLimitPlugin(max_tokens_limit=512)

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مرحله 2: پروکسی Litellm را پیکربندی کنید (config.yaml)

پروکسی خود را تغییر دهید config.yaml برای اینکه به Litellm بگویید از کنترل کننده افزونه جدید خود استفاده کند.

# config.yaml

model_list:
  - model_name: gpt-3.5-turbo
    litellm_params:
      model: openai/gpt-3.5-turbo
  # Add other models as needed

litellm_settings:
  # Register your plugin instance(s) here
  # Make sure the path (my_plugins.plugin_handler_instance) is correct
  # relative to where you run the `litellm` command.
  callbacks: [my_plugins.plugin_handler_instance]
  # You can add other callbacks too:
  # success_callback: ["langfuse", "my_other_logger.instance"]
  set_verbose: true # Recommended for debugging plugins

general_settings:
  # Add any other general settings
  master_key: sk-1234 # Example, use a real key management strategy

# Example key definitions (if not using DB/UI)
# Ensure keys used for testing have the required metadata
keys:
  - key: sk-plugin-allowed-key
    metadata:
      plugin_access: true # This key *should* pass the plugin check
      user_id: "user-allowed"
  - key: sk-plugin-denied-key
    metadata:
      # plugin_access is missing or false
      user_id: "user-denied"
  - key: sk-no-metadata-key
    user_id: "user-no-meta" # This key will also be denied by the plugin

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مرحله 3: پروکسی را اجرا کنید و تست کنید

  1. ذخیره پرونده ها: تضمین کردن my_plugins.py وت config.yaml به درستی ذخیره می شوند
  2. پروکسی را شروع کنید: پروکسی را از ترمینال خود اجرا کنید ، اطمینان حاصل کنید که می تواند پرونده های شما را پیدا کند.

    litellm --config config.yaml --logs
    
  3. مورد آزمون 1: کاربر مجاز (حداکثر نشانه های اجباری)

    • استفاده کردن sk-plugin-allowed-keyبشر
    • یک /chat/completions درخواست تماس بیشتر از حد افزونه (512 در مثال ما).
    curl -X POST http://localhost:4000/chat/completions \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer sk-plugin-allowed-key" \
      -d '{
        "model": "gpt-3.5-turbo",
        "messages": [{"role": "user", "content": "Tell me a story."}],
        "max_tokens": 1000
      }'
    
*   **Expected Outcome:** The request should succeed. Check the proxy logs for `AccessAndTokenLimitPlugin` messages. You should see "Access Granted" and "Enforcing max_tokens limit: Setting to 512". The actual LLM call will use `max_tokens: 512`.
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. مورد آزمایش 2: کاربر مجاز (حداکثر نشانه ها در حد مجاز)

    • استفاده کردن sk-plugin-allowed-keyبشر
    • درخواست max_tokens کمتر از یا برابر با حد مجاز (به عنوان مثال ، 200).
    curl -X POST http://localhost:4000/chat/completions \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer sk-plugin-allowed-key" \
      -d '{
        "model": "gpt-3.5-turbo",
        "messages": [{"role": "user", "content": "Tell me a joke."}],
        "max_tokens": 200
      }'
    
*   **Expected Outcome:** The request should succeed. Logs should show "Access Granted" and "Existing max_tokens (200) is within limit (512)". The LLM call will use `max_tokens: 200`.
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. مورد آزمایش 3: کاربر را انکار کرد

    • استفاده کردن sk-plugin-denied-key یا sk-no-metadata-keyبشر
    curl -X POST http://localhost:4000/chat/completions \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer sk-plugin-denied-key" \
      -d '{
        "model": "gpt-3.5-turbo",
        "messages": [{"role": "user", "content": "Will this work?"}]
      }'
    
*   **Expected Outcome:** The request should **fail** with an **HTTP 403 Forbidden** error. The response body should contain the detail message: `"Access Denied: Your API key does not have permission..."`. The proxy logs should show "Access Denied: Key metadata missing 'plugin_access: true'". The LLM will *not* be called.
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


قسمت 4: الگوهای افزونه مشترک و موارد استفاده

در async_pre_call_hook طیف گسترده ای از قابلیت های افزونه قدرتمند را فعال می کند. در اینجا برخی از الگوهای مشترک:

1. مسیریابی مدل پویا:

  • هدف: مدل LLM را بر اساس پارامترهای درخواست ، ابرداده کاربر یا منطق دیگر انتخاب کنید.
  • منطق: بازرسی کردن data (به عنوان مثال ، طول سریع ، کلمات کلیدی خاص) یا user_api_key_dict (به عنوان مثال ، ردیف کاربر ، بودجه باقی مانده). تغییر دادن data['model'] قبل از بازگشت Noneبشر
  • قطعه مثال:

    async def async_pre_call_hook(self, ..., data: dict, call_type: Literal[...]):
        if call_type == "completion":
            user_metadata = user_api_key_dict.metadata or {}
            if user_metadata.get("tier") == "premium":
                data['model'] = "gpt-4-turbo" # Route premium users to a better model
            elif len(str(data.get('messages', ''))) > 4000: # Example: check prompt length
                 data['model'] = "claude-3-haiku" # Route long prompts to a model with a large context
            # else: use default model passed in request
        return None
    

2. اعتبار سنجی و ضد عفونی ورودی:

  • هدف: اطمینان حاصل کنید که Payload Payload با معیارهای خاص (به عنوان مثال ، پارامترهای مورد نیاز ، قالب پیام ، محتوای مجاز) یا ضد عفونی کردن ورودی (به عنوان مثال ، ماسک PII) مطابقت دارد.
  • منطق: بازرسی کردن dataبشر در صورت عدم موفقیت اعتبار ، افزایش دهید HTTPException(status_code=400, detail="Validation failed...")بشر برای ضد عفونی ، اصلاح کنید data (به عنوان مثال ، data['messages']) در جای خود
  • مثال قطعه (مفهوم نقاب سازی PII):

    async def async_pre_call_hook(self, ..., data: dict, call_type: Literal[...]):
        if call_type == "completion" and "messages" in data:
            for message in data['messages']:
                if message.get('role') == 'user':
                    # Replace this with actual PII detection/masking logic
                    if "email:" in message.get('content', ''):
                       # Caution: Simple string replacement is naive. Use proper libraries.
                       message['content'] = message['content'].replace("email:", "masked_email:")
                       litellm.print_verbose("Masked potential email in user message.")
        return None
    

    (توجه: پوشیدن مناسب PII به کتابخانه های قوی مانند Presidio یا منطق سفارشی نیاز دارد).

3. درخواست غنی سازی:

  • هدف: به طور خودکار پارامترها را بر اساس زمینه کاربر یا تنظیمات جهانی اضافه کنید.
  • منطق: اصلاح data فرهنگ لغت برای اضافه کردن یا به روزرسانی کلیدها مانند temperatureبا max_tokens، یا تزریق سیستم خاص یا ابرداده.
  • قطعه مثال:

    async def async_pre_call_hook(self, ..., data: dict, call_type: Literal[...]):
        if call_type == "completion":
            # Ensure a user ID is always passed if available from the key
            if data.get("user") is None and user_api_key_dict.user_id:
                data["user"] = user_api_key_dict.user_id
    
            # Add default safety settings if not provided
            if data.get("safety_settings") is None:
                 data["safety_settings"] = {"block_hate_speech": True} # Example
        return None
    

4. اجرای سیاست (بودجه ، مجوزها):

  • هدف: درخواست هایی را که نقض محدودیت های بودجه یا قوانین مجوز تعریف شده در ابرداده کلید/تیم/کاربر است ، رد کنید.
  • منطق: بودجه/صرف اطلاعات یا پرچم های مجوز سفارشی را از آن بخوانید user_api_key_dictبشر اگر سیاستی نقض شده است ، مطرح کنید HTTPException (به عنوان مثال ، 403 ممنوع برای مجوزها ، 429 درخواست بیش از حد برای محدودیت بودجه/نرخ).
  • مثال قطعه (بررسی بودجه ساده):

    async def async_pre_call_hook(self, ..., data: dict, call_type: Literal[...]):
        key_spend = user_api_key_dict.spend or 0.0
        key_max_budget = user_api_key_dict.max_budget
    
        if key_max_budget is not None and key_spend >= key_max_budget:
            raise HTTPException(
                status_code=429, # Use 429 for budget/rate limits
                detail=f"API Key budget limit exceeded. Spend: {key_spend}, Budget: {key_max_budget}"
            )
        # Add checks for team budget, user budget etc. if needed
        return None
    

    (توجه: محدود کننده نرخ داخلی با استفاده از حافظه نهان برای بررسی های زمان واقعی ، پیشرفته تر است. این یک پیش بررسی ساده است).

5. منطق رد سفارشی:

  • هدف: درخواست ها را بر اساس محرک محتوای خاص یا قوانین تجاری رد کنید ، به طور بالقوه یک پیام سفارشی را به کاربر ارائه می دهد.
  • منطق: بازرسی کردن dataبشر در صورت رعایت معیارهای رد:
    • برای چت/تکمیل: return "Your request was rejected because..."
    • برای انواع دیگر یا خطاهای خاص تر: raise HTTPException(...)
  • قطعه مثال (بازگشت رشته):

    from litellm.utils import get_formatted_prompt # Helper to get text
    
    async def async_pre_call_hook(self, ..., data: dict, call_type: Literal[...]):
        if call_type == "completion":
            try:
                prompt_text = get_formatted_prompt(data=data, call_type=call_type)
                if "forbidden phrase" in prompt_text.lower():
                    return "Request rejected due to containing a forbidden phrase." # LiteLLM formats this
            except Exception as e:
                 litellm.print_verbose(f"Error getting formatted prompt: {e}")
        return None
    

6. تخمین و پیشگیری هزینه:

  • هدف: هزینه بالقوه یک درخواست را تخمین بزنید (به عنوان مثال ، بر اساس نشانه های سریع برای تکمیل) و در صورت فراتر از آستانه تعریف شده در ابرداده ، آن را رد کنید.
  • منطق: استفاده کردن litellm.token_counter (به طور بالقوه آن را برای متن async یا فراخوانی همزمان با دقت سازگار می کند) data['messages']بشر محاسبه هزینه تخمین زده شده با استفاده از litellm.model_costبشر مقایسه در برابر a max_request_cost مقدار از user_api_key_dict.metadataبشر بالا بردن HTTPException(400) اگر خیلی زیاد باشد
  • نیاز دارد: اجرای دقیق برای جلوگیری از افزودن تأخیر بیش از حد. ممکن است برای سناریوهای بسیار پر هزینه بهتر باشد.

7. تنظیم آزمایش A/B:

  • هدف: درصدی از درخواست ها برای یک مدل خاص به یک مدل جایگزین یا اضافه کردن ابرداده خاص برای ردیابی تست های A/B را مسیر دهید.
  • منطق: استفاده کردن random.random() یا شناسه کاربر برای تعیین اینکه آیا یک درخواست در گروه آزمون قرار می گیرد یا خیر. تغییر دادن data['model'] یا پرچم ها را به data['metadata'] (به عنوان مثال ، data['metadata']['ab_test_group'] = 'B'). از cache اگر برای یک جلسه کاربر به مسیریابی چسبنده نیاز دارید.

قسمت 5: تعامل با قلاب های دیگر (هماهنگی کلیدی است)

در حالی که async_pre_call_hook قدرتمند است ، اغلب با هماهنگی سایر قلاب ها ، به ویژه برای افزونه های حالت ، بهترین کار را انجام می دهد:

  • async_log_success_event / async_log_failure_event: ضروری برای بروزرسانی دولت بر اساس نتیجه از تماس LLM در parallel_request_limiter از اینها برای کاهش تعداد درخواست و به روزرسانی TPM استفاده می کند پس از تماس تمام می شود. اگر قلاب قبل از تماس شما به طرز خوش بین پیشخوان را افزایش می دهد یا پرچم را تنظیم می کند ، احتمالاً به این قلاب های پس از تماس نیاز دارید تا بر اساس موفقیت یا عدم موفقیت آن حالت را تنظیم یا پاک کنید.
  • async_post_call_success_hook: اگر افزونه شما نیاز به اصلاح دارد مفید است پاسخ بر اساس اقدامات انجام شده در قلاب قبل از تماس یا بر اساس حالت نهایی. محدود کننده نرخ از آن برای افزودن استفاده می کند X-RateLimit هدره هایی که تعداد نهایی را منعکس می کنند.
  • async_moderation_hook: دویدن به طور موازی به تماس اصلی LLM. اگر افزونه شما شامل یک چک بالقوه کند (مانند اعتدال محتوای پیچیده) است که نباید درخواست اصلی را مسدود کند ، به جای استفاده از این قلاب را در نظر بگیرید async_pre_call_hookبشر آگاه باشید که اگر async_moderation_hook یک استثنا را ایجاد می کند ، ممکن است پاسخ واقعی LLM را نادیده بگیرد.

مثال هماهنگی افزونه حالت (محدود کننده نرخ مفهومی):

  1. async_pre_call_hook:
    • محدودیت ها را از user_api_key_dictبشر
    • تعداد فعلی را از آن بخوانید cacheبشر
    • اگر count + 1 > limit، بالا بردن HTTPException(429)بشر
    • در صورت مجاز ، await cache.async_increment_cache(key, 1)بشر
    • بازگشت Noneبشر
  2. async_log_success_event:
    • await cache.async_increment_cache(key, -1) (تعداد کاهش).
    • TPM را بر اساس به روز کنید response_obj.usageبشر
  3. async_log_failure_event:
    • اگر exception است ، نه 429 مطرح شده توسط قلاب قبل از تماس:
      • await cache.async_increment_cache(key, -1) (تعداد کاهش).

قسمت 6: تکنیک های پیشرفته

  • حالت پیچیده با cache: بیش از پیشخوان های ساده را ذخیره کنید. فرهنگ لغت حافظه نهان یا رشته های JSON که جلسات کاربر ، حالت های فرآیند چند مرحله ای یا زمان بندی فعالیت اخیر را نشان می دهد. به یاد داشته باشید که از TTL های مناسب استفاده کنید.
  • تماس های API خارجی: قلاب شما قوطی await تماس با خدمات خارجی (به عنوان مثال ، یک نقطه تصمیم گیری سیاست ، یک سرویس پرچم ویژگی ، API تشخیص PII). از احتیاط شدید استفاده کنید:
    • این تماس ها را سریع و قابل اعتماد نگه دارید. یک تماس خارجی آهسته تأخیر را به هر درخواستبشر
    • برای تماس های خارجی ، رسیدگی به خطای قوی و زمان بندی را اجرا کنید. اجازه ندهید که یک سرویس خارجی ناکام ، پروکسی شما را پایین بیاورد.
    • با استفاده از تماس های خارجی نتایج ذخیره را در نظر بگیرید DualCacheبشر
  • دستکاری داده های پیچیده: برای کارهایی مانند قالب بندی سریع و یا تبدیل داده ها ، ممکن است توابع یا کلاس های یاری را تعریف کنید که در جای دیگری در پرونده افزونه خود یا ماژول های وارداتی تعریف شده اند. منطق قلاب خود را تمیز و متمرکز بر روی ارکستراسیون نگه دارید.

قسمت 7: بهترین شیوه ها و مشکلات

  • عملکرد مهم است: کد در داخل async_pre_call_hook تأخیر را اضافه می کند پیش از تماس LLM آن را لاغر و سریع نگه دارید. از I/O همزمان ، محاسبات پیچیده یا حلقه های ناکارآمد خودداری کنید. در صورت لزوم پروفایل. استفاده کردن asyncio.create_task برای به روزرسانی های پس زمینه غیر بحرانی (مانند حافظه نهان در محدود کننده نرخ).
  • رسیدگی به خطای قوی: منطق قلاب خود را در try...exceptبشر یک استثناء غیرقانونی در قلاب شما به طور بالقوه می تواند مسدود کند همه درخواست عبور از پروکسی. خطاهای ورود به سیستم برای اشکال زدایی به وضوح در قلاب خود (litellm.print_verbose یا یک الگوی مناسب). در نظر بگیرید که اگر قلاب شما با یک خطای غیر منتظره روبرو شود چه اتفاقی می افتد – در صورت رد درخواست (افزایش HTTPException(500)) یا اجازه دهید آن را ادامه دهد (شکست)؟
  • آگاهی امنیتی: هنگام اصلاح data فرهنگ لغت ، به ویژه data['messages'] یا پارامترهایی که بر رفتار مدل تأثیر می گذارد. هر ورودی خارجی را که توسط افزونه خود استفاده می شود ، ضدعفونی کنید. از معرفی آسیب پذیری های سریع تزریق خودداری کنید.
  • idempotency: در حالی که به طور معمول یک بار در هر درخواست درخواست کنید ، در نظر بگیرید که آیا تعامل با حافظه نهان یا سیستم های خارجی نیاز به idempotent دارد (برای اجرای چندین بار با همان نتیجه ایمن است) اگر ترمیم ها به طور بالقوه می توانند قلاب را دوباره تحریک کنند (هرچند که منطق آزمایش مجدد Litellm اتفاق می افتد پس از اجرای اولیه قلاب شکست می خورد).
  • اصلاحات پاک: آن را آشکار کنید که افزونه شما به چه کاری انجام می دهد data فرهنگ لغت نظرات را اضافه کنید. اطمینان حاصل کنید که تغییرات در یک بار معتبر برای LLM Target است.
  • منطق هدفمند (call_type): استفاده کردن call_type بررسی ها برای اطمینان از اینکه افزونه شما فقط بر عملیات LLM در نظر گرفته شده است.
  • پیکربندی: از مقادیر سخت کدگذاری خودداری کنید. از __init__ روش کلاس کنترل کننده خود برای پذیرش پیکربندی (مانند max_tokens_limit در مثال ما) یا افزونه خود را برای خواندن تنظیمات از user_api_key_dict.metadataبشر
  • آزمایش: افزونه خود را با ورودی های مختلف ، موارد لبه ، کلیدهای معتبر ، کلیدهای نامعتبر ، متفاوت تست کنید call_typeس ، و سناریوهای شکست انتظار می رود.

قسمت 8: اشکال زدایی افزونه خود

قلاب های اشکال زدایی می توانند مشکل باشند. در اینجا چند استراتژی آورده شده است:

  1. ورود به سیستم Verbose: فعال کردن litellm_settings.set_verbose: true در شما config.yamlبشر استفاده کردن litellm.print_verbose(...) برای چاپ مقادیر متغیر ، مراحل اجرای و تصمیمات به طور آزادانه در قلاب خود.
  2. ورود به سیستم استاندارد: برای ورود به سیستم ساختاری بیشتر ، یک لاجگر Python مناسب را در کلاس افزونه خود پیاده سازی کنید.
  3. عبارات چاپ: پیر خوب print() بیانیه ها می توانند در طول توسعه اولیه مفید باشند (به یاد داشته باشید که بعداً آنها را حذف کنید).
  4. قلاب را جدا کنید: به طور موقت در مورد سایر تماسهای برگشتی یا منطق پیچیده در اظهار نظر کنید config.yaml برای جداسازی رفتار خاص خود async_pre_call_hookبشر
  5. ورودی های خاص آزمون: کاردستی curl درخواست یا استفاده از مشتری های API (مانند پستچی ، بی خوابی) برای ارسال بارهای خاص که مسیرهای مختلف را در منطق قلاب شما ایجاد می کند.
  6. بررسی کردن UserAPIKeyAuth: کل را چاپ کنید user_api_key_dict در شروع قلاب خود در هنگام اشکال زدایی برای تأیید اینکه زمینه ، محدودیت ها و ابرداده های مورد انتظار را دریافت می کنید.
  7. بررسی کردن data: چاپ data فرهنگ لغت قبل و بعد از اصلاحات برای تأیید تغییرات.
  8. Debugger Python: اگر به صورت محلی اجرا می شود ، می توانید استفاده کنید pdb یا اشکال زدایی IDE شما. یک نقطه شکست را تنظیم کنید (import pdb; pdb.set_trace()) داخل روش قلاب خود. شما باید اشکال زدایی را به دویدن وصل کنید litellm روند

نتیجه گیری: ساخت ، گسترش ، کنترل

در async_pre_call_hook سنگ بنای پلاگین های تأثیرگذار ساختمان برای پروکسی Litellm است. این پروکسی را از یک ارسال کننده درخواست ساده به یک لایه بسیار قابل تنظیم و هوشمند تبدیل می کند که قادر به اجرای خط مشی های پیچیده ، تطبیق پویا درخواست ها و ادغام منطق دست و پنجه نرم مستقیم در گردش کار LLM است.

با تسلط بر امضای آن ، درک داده های متنی که ارائه می دهد (UserAPIKeyAuthبا cache، غنی شده data) ، و با دقت کنترل خروجی آن ، می توانید افزونه هایی بسازید که امنیت را افزایش می دهد ، هزینه ها را به طور مؤثر مدیریت می کند ، تجربه کاربر را بهبود می بخشد و پروکسی LITELLM را دقیقاً مطابق با نیازهای برنامه شما تنظیم می کند.

ساده را شروع کنید ، کاملاً آزمایش کنید ، به بهترین روشها رعایت کنید و از قدرت کامل این قلاب استفاده کنید تا سطح بعدی سفارشی سازی را برای دروازه AI خود باز کنید. وصل کردن مبارک!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا