ساخت افزونه های 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
به دلیل ویژگی های منحصر به فرد متناسب با توسعه افزونه ، از هم جدا می شود:
- زمان بندی همه چیز است: این در بحرانی ترین مقطع اجرا می شود: پس از احراز هویت اولیه و درخواست آماده سازی ، اما پیش از فراخوان بالقوه پرهزینه و وقت گیر به API واقعی LLM (مانند Openai ، Anthropic ، Cohere و غیره). این پنجره طلایی شما برای مداخله است.
- مداخله ، نه فقط مشاهده: برخلاف ورود به سیستم تماس ،
async_pre_call_hook
برای عملبشر مقدار بازگشت آن یا استثنائاتی که افزایش می دهد مستقیماً دیکته می کند که آیا درخواست پیش می رود ، اصلاح می شود یا به طور کامل رد می شود. - متن غنی: این نه تنها درخواست بار درخواست بلکه اطلاعات متنی حیاتی را نیز دریافت می کند:
- داده های احراز هویت کاربر/کلید دقیق (
UserAPIKeyAuth
). - دسترسی به حافظه نهان مشترک پروکسی (
DualCache
). - نوع خاصی از تماس صورت گرفته (
call_type
).
- داده های احراز هویت کاربر/کلید دقیق (
- جهش پذیری: دریافت می کند
data
فرهنگ لغت (بار درخواست آماده شده برای LLM) و می تواند به طور مستقیم اصلاح کنید قبل از اجازه درخواست درخواست. - قدرت خاص پروکسی: این قلاب یکی از ویژگی های پروکسی محیط زیست ، استفاده از زیرساخت های پروکسی (احراز هویت ، حافظه پنهان ، پیکربندی) برای ایجاد منطق پیچیده ای که در یک ادغام ساده کتابخانه امکان پذیر نیست.
به طور خلاصه ، اگر افزونه شما نیاز دارد:
- مدل هدف را به صورت پویا تغییر دهید.
- پارامترهای درخواست را اضافه ، حذف یا اصلاح کنید (
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']
بشر
- بازرسی: مقادیر مانند را بخوانید
- محتوا: شامل پارامترهای استاندارد تماس LLM مانند
-
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_type
S ، این ممکن است منجر به خطای 400 یا رفتار غیر منتظره شود – در درجه اول از این برای نقاط پایانی چت استفاده کنید. - بالا بردن
Exception
: با خطا رد کنید. این روش استاندارد برای رد زور درخواست به دلیل نقض خط مشی ، ورودی نامعتبر ، محدودیت بودجه یا چک های خارجی شکست خورده است.- افزایش
fastapi.HTTPException(status_code=..., detail=...)
بسیار توصیه می شود این امکان را به شما می دهد تا کد دقیق وضعیت HTTP را کنترل کنید (به عنوان مثال ، 400 درخواست بد ، 401 غیرمجاز ، 403 ممنوع ، 429 درخواست بیش از حد ، 500 خطای سرور داخلی) و پیام خطا به مشتری اصلی بازگشت. - در
parallel_request_limiter.py
کاربردهایHTTPException(status_code=429, ...)
برای محدودیت نرخ بیش از خطاهای.
- افزایش
- بازگشت
قسمت 3: اجرای افزونه خود-گام به گام
بیایید روند ایجاد یک افزونه ساده را طی کنیم. ما یک افزونه ایجاد خواهیم کرد که:
- بررسی می کند که آیا کاربر نقش خاصی را در ابرداده اصلی خود تعریف کرده است (
plugin_access: true
). - اگر آنها دسترسی داشته باشند و یک تماس “تکمیل” را انجام می دهند ، حداکثر را تقویت می کند
max_tokens
محدود کردن - اگر آنها دسترسی نداشته باشند ، درخواست را با خطای ممنوعه 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: پروکسی را اجرا کنید و تست کنید
- ذخیره پرونده ها: تضمین کردن
my_plugins.py
وتconfig.yaml
به درستی ذخیره می شوند -
پروکسی را شروع کنید: پروکسی را از ترمینال خود اجرا کنید ، اطمینان حاصل کنید که می تواند پرونده های شما را پیدا کند.
litellm --config config.yaml --logs
-
مورد آزمون 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`.
-
مورد آزمایش 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`.
-
مورد آزمایش 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
بشر مقایسه در برابر amax_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 را نادیده بگیرد.
مثال هماهنگی افزونه حالت (محدود کننده نرخ مفهومی):
-
async_pre_call_hook
:- محدودیت ها را از
user_api_key_dict
بشر - تعداد فعلی را از آن بخوانید
cache
بشر - اگر
count + 1 > limit
، بالا بردنHTTPException(429)
بشر - در صورت مجاز ،
await cache.async_increment_cache(key, 1)
بشر - بازگشت
None
بشر
- محدودیت ها را از
-
async_log_success_event
:-
await cache.async_increment_cache(key, -1)
(تعداد کاهش). - TPM را بر اساس به روز کنید
response_obj.usage
بشر
-
-
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: اشکال زدایی افزونه خود
قلاب های اشکال زدایی می توانند مشکل باشند. در اینجا چند استراتژی آورده شده است:
- ورود به سیستم Verbose: فعال کردن
litellm_settings.set_verbose: true
در شماconfig.yaml
بشر استفاده کردنlitellm.print_verbose(...)
برای چاپ مقادیر متغیر ، مراحل اجرای و تصمیمات به طور آزادانه در قلاب خود. - ورود به سیستم استاندارد: برای ورود به سیستم ساختاری بیشتر ، یک لاجگر Python مناسب را در کلاس افزونه خود پیاده سازی کنید.
- عبارات چاپ: پیر خوب
print()
بیانیه ها می توانند در طول توسعه اولیه مفید باشند (به یاد داشته باشید که بعداً آنها را حذف کنید). - قلاب را جدا کنید: به طور موقت در مورد سایر تماسهای برگشتی یا منطق پیچیده در اظهار نظر کنید
config.yaml
برای جداسازی رفتار خاص خودasync_pre_call_hook
بشر - ورودی های خاص آزمون: کاردستی
curl
درخواست یا استفاده از مشتری های API (مانند پستچی ، بی خوابی) برای ارسال بارهای خاص که مسیرهای مختلف را در منطق قلاب شما ایجاد می کند. - بررسی کردن
UserAPIKeyAuth
: کل را چاپ کنیدuser_api_key_dict
در شروع قلاب خود در هنگام اشکال زدایی برای تأیید اینکه زمینه ، محدودیت ها و ابرداده های مورد انتظار را دریافت می کنید. - بررسی کردن
data
: چاپdata
فرهنگ لغت قبل و بعد از اصلاحات برای تأیید تغییرات. - Debugger Python: اگر به صورت محلی اجرا می شود ، می توانید استفاده کنید
pdb
یا اشکال زدایی IDE شما. یک نقطه شکست را تنظیم کنید (import pdb; pdb.set_trace()
) داخل روش قلاب خود. شما باید اشکال زدایی را به دویدن وصل کنیدlitellm
روند
نتیجه گیری: ساخت ، گسترش ، کنترل
در async_pre_call_hook
سنگ بنای پلاگین های تأثیرگذار ساختمان برای پروکسی Litellm است. این پروکسی را از یک ارسال کننده درخواست ساده به یک لایه بسیار قابل تنظیم و هوشمند تبدیل می کند که قادر به اجرای خط مشی های پیچیده ، تطبیق پویا درخواست ها و ادغام منطق دست و پنجه نرم مستقیم در گردش کار LLM است.
با تسلط بر امضای آن ، درک داده های متنی که ارائه می دهد (UserAPIKeyAuth
با cache
، غنی شده data
) ، و با دقت کنترل خروجی آن ، می توانید افزونه هایی بسازید که امنیت را افزایش می دهد ، هزینه ها را به طور مؤثر مدیریت می کند ، تجربه کاربر را بهبود می بخشد و پروکسی LITELLM را دقیقاً مطابق با نیازهای برنامه شما تنظیم می کند.
ساده را شروع کنید ، کاملاً آزمایش کنید ، به بهترین روشها رعایت کنید و از قدرت کامل این قلاب استفاده کنید تا سطح بعدی سفارشی سازی را برای دروازه AI خود باز کنید. وصل کردن مبارک!