إشهار بوابات نماذج اللغات الكبيرة: الهندسة، المقاياضات والدروس القاسية — كانيش مانوجا، تويليو

AAI Engineer
Computing/SoftwareManagementInternet Technology

Transcript

00:00:00أنا جانيش مانوجا. أنا مهندس رئيسي في Twilio. لنبدأ برفع الأيدي سريعاً.
00:00:20من هنا رأى من قبل رسالة: حدث خطأ ما، يرجى المحاولة مرة أخرى؟
00:00:27حسنًا، لدينا عدد قليل من المحظوظين وقليل ممن تناولوا غداءً جيدًا.
00:00:33إذن، خلف هذه الرسالة البسيطة يوجد في الواقع نظام معقد للغاية يقدم لك تلك الرسالة على الرغم من تعطل موفري النماذج.
00:00:45وهذا ما سنقوم بتحويله إلى الإنتاج اليوم أو نناقش جعله جاهزًا للإنتاج اليوم.
00:00:50إذن، ما هي بوابة نماذج اللغات الكبيرة (LLM)؟
00:00:52بوابة نماذج اللغات الكبيرة هي نقطة دخول أو طبقة وسيطة بين تطبيقاتك وموفري النماذج من خلفها.
00:00:58فهي تقوم ببعض الأمور، مثل التوجيه، والمصادقة، والبديل الاحتياطي، وحدود المعدل، وكل أنواع الحوكمة التي قد تفكر فيها.
00:01:08وفي قلب البوابة مباشرة توجد صراع بين أربعة أمور.
00:01:13وهي التوفر، وزمن الانتقال، وضوابط الأمان، والتكاليف.
00:01:18في حالة حدوث تدهور، لا يمكنك تعظيم جميع الأمور الأربعة.
00:01:22عليك أن تختار ما تريده.
00:01:25لذا، من خلال هذا الحديث، إذا كنت تستخدم بوابة نموذج لغوي كبير، أود مساعدتك في تحقيق تلك المفاضلة بما يتناسب مع حالة الاستخدام الخاصة بك.
00:01:35وإذا قمت بتصميم بوابة، فأريدك أن تصمم أو توفر تلك الرافعات لمتصليك وعملائك حتى يكون عملاؤك سعداء.
00:01:46لنبدأ بالتوفر.
00:01:50إذا كان لديك موفر نموذج واحد، فإن الحد الأقصى لديهم هو الحد الأقصى لديك.
00:01:56انقطاعهم هو انقطاعك.
00:02:03لذا، في هندسة البرمجيات التقليدية، الطريقة التي تعالج بها الاعتماديات غير الموثوقة هي إعادة المحاولة.
00:02:11إعادة المحاولة مع تراجع أسي، واهتزازات.
00:02:15وعندما يفشل كل ذلك، يكون لديك قاطع دائرة يتعطل بعد رؤية إخفاقات كافية، وتتوقف عن استدعاء هذا الشيء اللعين.
00:02:24هذا لا يكفي لنماذج اللغات الكبيرة.
00:02:26تختلف نماذج اللغات الكبيرة تمامًا مقارنة بواجهات برمجة التطبيقات السريعة والرخيصة التي تعيد المحاولة معها.
00:02:32إعادة محاولة استدعاء واجهة برمجة تطبيقات نموذج لغة كبير تلتهم ميزانية زمن الانتقال لديك بسرعة حقًا.
00:02:38وأيضًا، التعثر في قاطع دائرة عندما يكون لديك موفر نموذج آخر جيد تمامًا للتوجيه إليه لا منطق فيه.
00:02:47يجب عليك استخدام موفر النموذج الثاني.
00:02:48وثالثًا، كما قلت، المكالمات بطيئة ومكلفة.
00:02:53لذا، فإن عمليات إعادة المحاولة العشوائية تضاعف فقط تكلفتك وأزمنة الانتقال المتأخرة.
00:02:58إذن، ما هي الفكرة الأفضل هنا؟
00:03:02إنها في الواقع آلية احتياطية لكل طلب.
00:03:05ما تعنيه هو أنه يمكنك فعليًا تجربة موفر النموذج A ثم، على التوالي، تجربة موفر النموذج B إذا فشل طلبك المقدم إلى موفر النموذج A.
00:03:14خيار آخر يجب مراعاته هنا هو أنه يمكنك إرسال الطلبات إلى كلا الموفرين بالتوازي، ولكن هذا فقط إذا كنت مهووسًا جدًا جدًا بأزمنة الانتقال، لأن ذلك سيوضاعف تكلفتك فقط.
00:03:26تنطبق بعض أنماط قطع الدائرة المماثلة هنا على نماذج اللغات الكبيرة أيضًا.
00:03:32إذا كنت تعلم أن موفر الخدمة الأساسي الخاص بك كان يفشل لبعض الوقت، فلا معنى لمحاولة استخدامه مرة أخرى.
00:03:40أنت تقوم بإزالته من موازن التحميل أو مسار الطلب الخاص بك وتضعه في فترة تهدئة ثم، بعد مرور بضعة دقائق، تحاول إعادته مرة أخرى.
00:03:51أحد الخيارات المثيرة للاهتمام التي يجب عليك اتخاذها هنا هو مكان تعايش عدادات الإخفاقات الخاصة بك.
00:03:59يمكنك أن تقرر أن تعيش عدادات الإخفاقات في الذاكرة على المثيلات التي تخدم حركة المرور الخاصة بك، أو يمكنك الحصول على معلومات مشتركة حيث يتم مشاركة عدادات الإخفاقات الخاصة بك عبر الأسطول بأكمله.
00:04:12هناك مقايضات.
00:04:14إذا كنت تريد عمليات انتقال سريعة عند الفشل، فإن النطاق الواسع للأسطول يساعد.
00:04:19 ومع المثيلات، مع عدادات الحالة المحلية، المشكلة التي تواجهها هي أنه متى قمت بتغيير حجم نشرك، تتغير تكوينك وتوقعاتك.
00:04:30لذا فهذا أمر يجب مراعاته.
00:04:34ما لم يظهره لك هذا الرسم البياني النظيف حقًا هو بعض العقبات الأخرى التي سأناقشها.
00:04:39لذا فإن الآليات الاحتياطية ليست شفافة.
00:04:41بينما تتقارب الصناعة على تنسيق متوافق مع واجهة برمجة تطبيقات OpenAI، أود أن أقول إنه لا تزال هناك الفروق الدقيقة.
00:04:49لذا تحتاج حقًا إلى اختبار آلياتك الاحتياطية بشكل جيد.
00:04:52يمكن أن يكون لها اختلافات في مخططات استدعاء الأدوات الخاصة بك، وحدود الرموز، وأسباب التوقف، وما إلى ذلك.
00:04:58لذا مع بوابات نماذج اللغات الكبيرة، يمكنك الحصول على طبقة تطبيع يمكنها ضمان قدرتك على إجراء آليات احتياطية عبر الموفرين أيضًا.
00:05:08شيء آخر وهو البث (Streaming).
00:05:15أساسا، لا أحد يريد الانتظار لمدة 30 ثانية لتظهر جدار من النصوص أمامهم.
00:05:22لذا فهناك حالات استخدام يكون فيها البث مطلوبًا تمامًا.
00:05:26ولكن هذا يأتي بتكلفة.
00:05:27أنت تتنازل عن راحلتك أو خياراتك.
00:05:29لا يمكنك -- بمجرد أن قررت الذهاب مع الموفر A، يجب عليك الاستمرار في الذهاب مع الموفر A.
00:05:36لا يمكنك تغيير الموفرين في منتصف البث.
00:05:39كل ما تم إرساله إلى العميل، انتهى الأمر.
00:05:42وهناك تظهر رسالة حدث خطأ ما.
00:05:46تلك هي الرسالة التي تراها.
00:05:48هذا ليس بسبب الكسل.
00:05:49إنه بحسب التصميم أن تراها هكذا.
00:05:52وهي إحدى المقايضات.
00:05:54أود تسليط الضوء على شيء آخر رأيت الفرق تتعثر فيه مرارًا وتكرارًا.
00:06:00فهُم يجهزون ويختبرون موفريهم الأساسيين بشكل جيد حقًا.
00:06:05ولكن الموفر الثاني، أو الموفر الاحتياطي، لا يحظى بالضرورة بنفس القدر من الاتمام والرعاية.
00:06:11وأود أن أجادل بأن إنتاجيتك أو سعتك أو هامشك يجب أن يكون أعلى حتى بالنسبة للموفر الثاني أو الموفر الاحتياطي.
00:06:21لأن هذا هو خط دفاعك الأخير.
00:06:23إذا تعطل ذلك، يتعطل تطبيقك.
00:06:29دعونا نناقش أزمنة الانتقال.
00:06:31فشل التوفر يكون واضحًا أمام عينيك.
00:06:34إنها تفشل.
00:06:36تتلقى تنبيهًا.
00:06:37يتم استدعاؤك بجهاز النداء (Paging).
00:06:38لكن أزمنة الانتقال العالية يمكن أن تكون هادئة ولا يُنتبه لها.
00:06:42وهي تحتاج إلى تلقي اهتمام أكبر من، أود أن أقول، ضبط خدماتك من أجل التوفر فقط.
00:06:49شيء واحد يجب الإشارة إليه.
00:06:54قد تشغل البوابة أحمال عمل مختلطة.
00:06:58ويمكن أن يكون لديك طلبات تضمين (embeddings) تستغرق أقل من ثانية.
00:07:04يمكن أن يكون لديك طلبات تصنيف تستغرق أقل من ثانية.
00:07:07لديك طلبات محادثة تستغرق ثلاث ثوانٍ.
00:07:10وطلبات استدلال تستغرق وقتًا طويلًا.
00:07:13عرض سريع للأيدي.
00:07:15عرض سريع للأيدي إذا كنت تقيس زمن الانتقال الإجمالي لخدمتك بأكملها.
00:07:20حسنًا، لقد كان هذا سؤالاً خداعيًا.
00:07:23عذرًا.
00:07:24يجب ألا تفعل ذلك.
00:07:25لا معنى له.
00:07:26إنه كذب.
00:07:27يجب أن تتابع قيمة النسبة المئوية 99 (P99) لكل نموذج ولكل مسار، وليس رقمًا يشمل البوابة بأكملها.
00:07:32الرقم الخاص بالبوابة بأكملها لا معنى له، خاصة إذا كنت تشغل أحمال عمل مختلطة.
00:07:36وآمل ألا يفعل أولئك الذين رفعوا أيديهم ذلك.
00:07:40شيء آخر يمكن حقًا -- لا أستطيع التأكيد على هذا بما يكفي، وهو أن تضبط مهلات زمنية لكل فئة نموذج ولكل مسار.
00:07:49هناك -- هذا هو السبب الرئيسي الأول لانقطاع الخدمة الصامت لديك.
00:07:54إذا لم يكن لديك مهلة زمنية، تعتقد بوابتك أن طلبك يُخدم بسعادة، بينما لا يكون الأمر كذلك.
00:08:00وسأترككم بهذه الرسالة لأزمنة الانتقال، وتحديدًا.
00:08:05الحالة العادية لنموذج الاستدلال هي في الواقع انقطاع لنموذج المحادثة.
00:08:09لذا فأنت بالتأكيد بحاجة إلى تتبع زمن الانتقال لكل مسار.
00:08:13حسناً، هذه هي الشريحة الأكثر إيلاماً أو التي سببت لي أكبر قدر من المخاوف، وهي نماذج الاستدلال والتوجيه.
00:08:24لذا فهنا يكون زمن الانتقال غير متوقع حقًا.
00:08:29 ونماذج الاستدلال، فهي لا تمنحك -- فهي شديدة عدم الحتمية، وأكثر عدم حتمية من نماذجك العادية.
00:08:40لا يمكنك ضبط درجة الحرارة على الصفر في كثير من الحالات.
00:08:43ويمكن أن يستغرق نفس المطالبة (prompt) ما بين ثانتين إلى 60 ثانية.
00:08:48وقد رأينا ذلك في الإنتاج، حيث قفزت قيمة P99 فجأة إلى 60 ثانية لسبب غير وجيه.
00:08:53لذا، بينما لا يوجد حل سحري لذلك، أوصي بأن تبدأ على الأقل بإصلاح مستوى الاستدلال لكل مسار.
00:09:03لذا مع نماذج الموجهات (routers)، فإنها تخفي هذا التجريد من ورائك.
00:09:08مثل، اختيار النماذج التي سيتم تشغيلها.
00:09:11وأوصي بشدة بأن تبذل قدر الإمكان -- أن تجعل الطلبات حتمية قدر الإمكان مع نظام غير حتمي.
00:09:23فكرة أخرى هي تحوط الذيل (hedging the tail).
00:09:26يمكنك -- يمكنك إطلاق طلب آخر إذا استهلك طلبك الأساسي بالفعل، فلنقل، P90 من ميزانية زمن الانتقال لديك.
00:09:37هذا يمكن أن يحوط -- يمكنه حقًا تحوط ذيل P99 لخدماتك.
00:09:44حسناً، هذه واحدة من المفضلة لدي.
00:09:47للحفاظ على أمان نموذجك، تحتاج إلى توفير ضوابط أمان (guardrails).
00:09:53ومع ذلك، فإن ضوابط الأمان ضرورية لمنع خدماتك من هجمات حقن التعليمات (prompt injection)، والحفاظ على مرشحات المعلومات الشخصية (PII) في مكانها، ووجود مرشحات السمية، ومنع نماذج اللغات من إهانة عملائك.
00:10:08وكل تلك الأمور الجيدة.
00:10:11ولكن تمامًا مثل موفر النماذج، هناك مقايضات أيضًا.
00:10:15ضوابط الأمان تشبه تمامًا خدمة أخرى.
00:10:18يمكن أن يتعطل.
00:10:19يمكن أن يكون غير موثوق به.
00:10:21وهنا تحتاج إلى الاختيار.
00:10:24هل تفشل في الوضع المفتوح أم تفشل في الوضع المغلق؟
00:10:27عندما أقول الفشل في الوضع المفتوح، يمكنك الاستمرار في تلبية الطلب حتى لو كانت ضوابط الأمان لديك معطلة.
00:10:32الفشل في الوضع المغلق، أنت تحظر الطلب وتقول، مهلا، أنا لست متاحا.
00:10:36هذه هي المقايضة بين الإتاحة والأمان إلى حد ما.
00:10:41وفي حين أنه لا توجد إجابة عامة، إلا أن الأمر يعتمد حقاً على حالة الاستخدام الخاصة بك.
00:10:45يمكنك أن تقرر، على سبيل المثال، عامل تصفية السمية، إذا لم يكن يعمل، فلا يزال بإمكانك خدمة هذا الطلب.
00:10:53لذا يجب أن يكون الخيار الافتراضي هو أسوأ سيناريو يمكنك التعايش معه.
00:11:02هناك بعض الأشياء التي يمكنك القيام بها بالفعل لتحسين سلوك أنظمتك في مواجهة تعطل حواجز الحماية وإدارة عدم موثوقية حواجز الحماية نفسها.
00:11:17إذن، أولاً هو ميزانية الوقت.
00:11:20يجب ألا يتم تقييد طلبك أبداً توقيت حواجز الحماية.
00:11:25يجب أن يكون نموذج اللغة الكبير دائماً هو الخطوة المحددة للمعدل.
00:11:29لذا تأكد من وجود مهلات زمنية وأن حواجز الحماية هذه تعمل بميزانية زمنية محددة.
00:11:38الشيء الهامة الآخر هو البديل الاحتياطي.
00:11:40لقد سمعت - ربما تعلم، وقد تحدثت عنه، فنحن نناقش دائماً البدائل الاحتياطي فيما يتعلق بمزودي النماذج.
00:11:47ولكن حواجز الحماية هي خدمات بالغة الأهمية أيضاً، حيث يمكنك النظر في البدائل الاحتياطية، ووجود مزودين ثانويين، وفحوصات ثانوية، والقرارات المؤقتة للحفاظ على توفر خدمتك عندما يتعطل مزود حواجز الحماية.
00:12:03هناك خيار مثير للاهتمام آخر يظهر فيما يتعلق بحواجز الحماية وهو وضع حواجز الحماية.
00:12:10عادةً، يمكنك وضع حاجز الحماية بثلاث طرق.
00:12:15يمكنك استخدام خطاف مسبق يتم تشغيله - حيث يعمل حاجز الحماية فعلياً على المدخلات.
00:12:19يمكنك - وهذا هو الأرجح الأمان، ولكنه يضيف زمن انتقال متسلسلاً إلى طلباتك.
00:12:26طريقة أخرى بشكل متوازٍ.
00:12:29هذه إحدى الطرق المفضلة لدي، ولكن تجدر الإشارة إلى أن البث لن يعمل بشكل جيد هنا مع التوازي.
00:12:35لذا، إذا كنت تنتج مخرجات منظمة بشكل خاص، فالرجاء عدم بثها.
00:12:40حاول توفير أوقات الاستجابة وتشغيل حواجز الحماية هذه في نفس الوقت لمخرجاتك المنظمة.
00:12:46خيار آخر هو الخطافات اللاحقة.
00:12:48هذه هي الأفضل لمراقبة المخرجات، ومراجعة مخرجاتك، وما إلى ذلك.
00:12:58إذن، حتى الآن، لقد تحدثنا جميعاً - لقد ناقشت كل الأشياء التي يمكن أن تسير بشكل خاطئ فيما يتعلق بتبعياتنا.
00:13:06لم نناقش أننا نضيف في الواقع تبعية أخرى في مسار الطلب نفسه، وهي البوابة المركزية - أو بوابة نموذج اللغة الكبير نفسها.
00:13:15هناك بعض الأمور التي واجهتنا فيها مشكلات، وتعلمنا بعض الدروس التي أود مشاركتها معك إذا كنت تعمل على بوابة نموذج لغة كبير أو تستخدم إحداها.
00:13:25أحدها هو الحدود المشتركة.
00:13:28تأكد من فصل مفاتيح واجهة برمجة التطبيقات الخاصة بك لكل مسار، ولكل حالة استخدام، إلى أدق مستوى ممكن - إلى أدق شيء يمكنك تخيله.
00:13:40يمكن أن يكون وجود مستأجر صاخب أحد أكبر المشكلات هنا.
00:13:47شيء آخر هو تفريغ الحمل.
00:13:50هذه ميزة يجب عليك، كجزء من أدلة التشغيل الخاصة بك، وأيام الألعاب، التأكد من أن البوابة التي تستخدمها تدعم تفريغ الحمل.
00:13:59لأنه عندما تكون لديك عاصفة إعادة المحاولة، يصبح من الصعب حقاً التوسع.
00:14:03لا يمكنك ببساطة توسيع نطاق الخدمات التي تخضع لعاصفة إعادة المحاولة.
00:14:07وكل خوادم الويب هذه تحتوي على طابور داخلي، وهي قابلة للتكوين.
00:14:13تأكد من أنها محددة بحدود، ولا يمكنها طلب - لا يمكنها قبول الطلبات غير المحدودة.
00:14:19وإذا كنت ترغب في الحصول على بعض المنطق المخصص، يمكنك حتى تحديد أولويات حركة المرور هنا أيضاً، للتأكد من خدمة حالات الاستخدام الأهم لديك بشكل جيد تحت الحمل.
00:14:29آخر شيء أود مناقشته هو فكرة البوابة المركزية بأكملها.
00:14:38إنها نقطة فشل وحيدة.
00:14:40لذا، إذا كنت تفكر في وجود بوابة مركزية لشركتك بأكملها لنموذجي لغة كبير، فأنا أوصي بإعادة التفكير في ذلك ومعرفة الأسباب التي تجعلك ترغب في ذلك.
00:14:52ما لاحظته هو أنه في معظم السيناريوهات، ليس البوابة المركزية هي ما يريدونه.
00:14:57إنهم يريدون حوكمة مركزية.
00:15:00وهناك مسار للأمام حيث يمكنك بالفعل اللامركزية البوابة مع الحفاظ على مركزية الحوكمة.
00:15:07لذا لا تحاول مركزية حركة المرور الخاصة بك، ولكن يمكنك توفير إضافات، ورمز مخصص يمكنه مركزية حوكمتك.
00:15:17يمكن أن تتخذ الحوكمة شكل تتبع التكاليف، وإدارة حدود المعدل، وهناك حلول أخرى ممكنة.
00:15:24لذا استكشف تلك الحلول قبل الشروع في امتلاك بوابة مركزية واحدة لشركتك بأكملها.
00:15:30يمكن إدارتها بواسطة فريق واحد، لكني لا أوصي بنشرها كنشر واحد للشركة بأكملها، على الرغم من أنها موزعة.
00:15:43مع قولي هذا، أود أن أنهي هذه المحادثة بملاحظة شخصية.
00:15:47إذن عيد ميلاد ابني اليوم، وأنا هنا أتحدث إلى الغرباء حول قاطع الدائرة.
00:15:54لذا فإن أقل ما يمكنك فعله من أجلي هو الذهاب ومنع حادثة واحدة لي ولعملائك.
00:16:02شكراً لك.
00:16:03إذا كان لديكم أي أسئلة، نعم.
00:16:06.

Key Takeaway

تتطلب إدارة بوابات نماذج اللغات الكبيرة موازنة دقيقة بين التوفر وزمن الانتقال عبر آليات بديلة لكل طلب ومسار منفصل بدلاً من الحلول المركزية البسيطة.

Highlights

  • تعتمد بوابات نماذج اللغات الكبيرة على المفاضلة بين أربعة عناصر رئيسية وهي التوفر، وزمن الانتقال، وضوابط الأمان، والتكاليف.

  • تتسبب إعادة المحاولة التقليدية في استهلاك ميزانية زمن الانتقال ومضاعفة التكاليف عند التعامل مع نماذج اللغات الكبيرة.

  • تتطلب حوكمة البوابات استخدام آليات احتياطية لكل طلب بدلاً من الاعتماد المطلق على قواطع الدائرة.

  • تؤدي البوابات المركزية بالكامل إلى نقطة فشل وحيدة، مما يستوجب اللامركزية في تشغيل حركة المرور مع الحفاظ على مركزية الحوكمة.

  • تتراوح أزمنة الاستجابة في نماذج الاستدلال بين ثانيتين و60 ثانية لنفس الطلب، مما يجعل التتبع لكل مسار أمراً إلزامياً.

Timeline

مفهوم بوابات نماذج اللغات الكبيرة والمفاضلات الأساسية

  • تمثل بوابة نماذج اللغات الكبيرة نقطة دخول وسيطة بين التطبيقات وموفري النماذج.
  • ترتكز البوابة على صراع مستمر بين التوفر، وزمن الانتقال، وضوابط الأمان، والتكاليف.

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

إدارة التوفر والبدائل الاحتياطية

  • تؤدي إعادة المحاولة التقليدية مع تراجع أسي إلى استنزاف ميزانية زمن الانتقال وتضاعف التكاليف.
  • تعتمد الآلية الفعالة على إرسال طلب بديل إلى موفر ثانٍ بشكل متسلسل عند فشل الطلب الأول.

تختلف نماذج اللغات الكبيرة عن واجهات برمجة التطبيقات العادية في كونها بطيئة ومكلفة. يتطلب ضمان استمرارية الخدمة تجهيز موفر احتياطي بكفاءة لا تقل عن الموفر الأساسي، مع تجنب تغيير الموفرين أثناء البث.

أزمنة الانتقال ونماذج الاستدلال

  • يؤدي قياس زمن الانتقال للبوابة بأكملها عند تشغيل أحمال عمل مختلطة إلى قراءات مضللة.
  • تظهر نماذج الاستدلال عدم حتمية واضحة تجعل نفس الطلب يستغرق وقتا يتراوح بين ثانيتين و60 ثانية.

يتطلب تتبع زمن الانتقال مراقبة النسبة المئوية 99 لكل نموذج ومسار على حدة مع تحديد مهلات زمنية صارمة. تحمى أزمنة الاستجابة العالية عبر تتبع دقيق واستخدام آليات تحوط الذيل.

ضوابط الأمان والتحديات التشغيلية

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

تفرض حواجز الحماية مقاضاة بين الفشل في الوضع المفتوح أو المغلق بناء على حالة الاستخدام. تتطلب البوابات آليات لتفريغ الحمل تحت الضغط، وتجنب الاعتماد على نقطة مركزية واحدة لحركة المرور بالكامل.

Community Posts

View all posts