Webhooks
أرسل إشعارات webhook عبر HTTP موقّعة بـ HMAC من Potato لسبعة أنواع من أحداث التعليق، مع إعادة محاولة بتراجع أسّي، ومراقبة إدارية، وتحقق بـ Python وNode.
جديد في الإصدار v2.4.0
تتيح الـ webhooks لـ Potato إشعار الأنظمة الخارجية حين تقع أحداث التعليق، من دون استطلاع دوري. اربطها بخطوط معالجة البيانات، أو أطلق تنبيهات، أو حدّث لوحات المعلومات، أو ابدأ معالجة لاحقة تلقائياً.
نظرة عامة
يرسل Potato طلب HTTP POST إلى نقطة النهاية التي هيّأتها كلما وقع حدث مدعوم. والحمولات بصيغة JSON وموقّعة بـ HMAC-SHA256 حتى تتحقق من أنها جاءت من نسختك من Potato.
وتسليم الـ webhook غير حاجب بالكامل، فلا تتأخر طلبات التعليق أبداً بينما الـ webhooks في الطريق. أما التسليمات الفاشلة فيُعاد تجريبها بتراجع أسّي.
التهيئة
أضف قسم webhooks إلى تهيئة YAML لديك:
webhooks:
enabled: true
endpoints:
- name: "my-pipeline"
url: "https://your-system.example.com/potato-events"
secret: "your-signing-secret" # optional but recommended
events:
- annotation.created
- item.fully_annotated
- task.completed
active: true
timeout: 10نقاط نهاية متعددة
يمكنك تهيئة عدة نقاط نهاية، لكل واحدة اشتراكات أحداث مختلفة:
webhooks:
enabled: true
endpoints:
- name: "data-pipeline"
url: "https://pipeline.example.com/annotations"
secret: ${WEBHOOK_SECRET_1}
events:
- annotation.created
- item.fully_annotated
- name: "slack-alerts"
url: "https://hooks.slack.com/services/..."
events:
- task.completed
- quality.attention_check_failed
- name: "catch-all"
url: "https://logging.example.com/potato"
events:
- "*" # subscribe to all eventsأنواع الأحداث
| الحدث | يقع حين |
|---|---|
annotation.created | يرسل معلّق وسماً لعنصر |
annotation.updated | يعدّل معلّق وسماً أرسله سابقاً |
item.fully_annotated | يبلغ عنصر عدد التداخل المطلوب من التعليقات |
task.completed | تكتمل تعليقات كل العناصر في المهمة |
user.phase_completed | ينهي معلّق مرحلة (سير عمل الوضع المنفرد) |
quality.attention_check_failed | يخفق معلّق في فحص انتباه |
webhook.test | يُطلق يدوياً عبر واجهة الإدارة للاختبار |
استخدم "*" للاشتراك في كل أنواع الأحداث الحالية والمستقبلية.
صيغة الحمولة
تتشارك كل الأحداث في مغلّف واحد:
{
"event_id": "evt_01HXYZ...",
"event_type": "annotation.created",
"timestamp": "2026-03-17T14:23:01Z",
"task_name": "sentiment-study",
"data": {
...
}
}حمولة annotation.created
{
"event_type": "annotation.created",
"data": {
"annotator_id": "user123",
"instance_id": "doc_042",
"annotation": {
"sentiment": "positive",
"confidence": "high"
},
"submitted_at": "2026-03-17T14:23:01Z"
}
}حمولة item.fully_annotated
{
"event_type": "item.fully_annotated",
"data": {
"instance_id": "doc_042",
"annotator_count": 3,
"annotations": [
{"annotator_id": "user1", "sentiment": "positive"},
{"annotator_id": "user2", "sentiment": "positive"},
{"annotator_id": "user3", "sentiment": "neutral"}
]
}
}حمولة task.completed
{
"event_type": "task.completed",
"data": {
"task_name": "sentiment-study",
"total_instances": 500,
"total_annotations": 1500,
"completed_at": "2026-03-17T15:00:00Z"
}
}التحقق من التواقيع
حين يكون secret مهيّأً، يوقّع Potato كل طلب باستخدام Standard Webhooks (HMAC-SHA256). وتُضمَّن ثلاث ترويسات:
| الترويسة | القيمة |
|---|---|
webhook-id | معرّف تسليم فريد |
webhook-timestamp | طابع Unix الزمني للتسليم |
webhook-signature | توقيع HMAC-SHA256 |
التحقق في Python
import hmac
import hashlib
import time
def verify_webhook(payload_bytes: bytes, headers: dict, secret: str) -> bool:
webhook_id = headers.get("webhook-id", "")
timestamp = headers.get("webhook-timestamp", "")
signature = headers.get("webhook-signature", "")
# Reject stale requests (older than 5 minutes)
if abs(time.time() - int(timestamp)) > 300:
return False
signed_content = f"{webhook_id}.{timestamp}.{payload_bytes.decode()}"
expected = hmac.new(
secret.encode(),
signed_content.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"v1,{expected}", signature)التحقق في Node.js
const crypto = require('crypto');
function verifyWebhook(payload, headers, secret) {
const webhookId = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const signature = headers['webhook-signature'];
// Reject stale requests
if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
return false;
}
const signedContent = `${webhookId}.${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(`v1,${expected}`),
Buffer.from(signature)
);
}سلوك إعادة المحاولة
تُعاد محاولة التسليمات الفاشلة (استجابة خارج نطاق 2xx أو انتهاء مهلة) تلقائياً:
| المحاولة | التأخير |
|---|---|
| 1 (الأولى) | فوراً |
| 2 | 5 ثوانٍ |
| 3 | 30 ثانية |
| 4 | 5 دقائق |
| 5 | 30 دقيقة |
| 6 | ساعة واحدة |
وبعد ست محاولات فاشلة، يُوسم التسليم بأنه فاشل نهائياً. ويُحفظ طابور إعادة المحاولة في SQLite في {output_dir}/.webhooks/webhook_retries.db، فتبقى المحاولات المعلّقة بعد إعادة تشغيل الخادم.
المراقبة والاختبار
واجهة الإدارة
اطّلع على حالة الـ webhooks وإحصاءاتها:
# List all webhooks and their delivery statistics
curl -H "X-API-Key: $ADMIN_API_KEY" \
http://localhost:8000/admin/api/webhooksالاستجابة:
{
"endpoints": [
{
"name": "my-pipeline",
"url": "https://...",
"events": ["annotation.created"],
"active": true,
"stats": {
"total_emitted": 1240,
"total_failed": 3,
"pending_retries": 0,
"last_success": "2026-03-17T14:23:01Z"
}
}
]
}إرسال webhook تجريبي
curl -X POST -H "X-API-Key: $ADMIN_API_KEY" \
http://localhost:8000/admin/api/webhooks/test \
-H "Content-Type: application/json" \
-d '{"endpoint_name": "my-pipeline"}'وهذا يطلق حدث webhook.test إلى نقطة النهاية المسمّاة فوراً.
مرجع التهيئة الكامل
webhooks:
enabled: true
endpoints:
- name: string # unique name for this endpoint
url: string # HTTPS URL to POST to
secret: string # optional HMAC secret for signature verification
events: # list of event types, or ["*"] for all
- annotation.created
active: true # set false to disable without removing
timeout: 10 # request timeout in seconds (default: 10)
max_retries: 6 # max retry attempts (default: 6)قراءات إضافية
- لوحة الإدارة -- راقب تقدم التعليق وأدر المستخدمين
- ضبط الجودة -- هيّئ فحوص الانتباه التي تطلق
quality.attention_check_failed - الوضع المنفرد -- سير عمل قائم على المراحل يطلق
user.phase_completed - صيغ التصدير -- طرق بديلة لإخراج التعليقات من Potato
للاطلاع على تفاصيل التنفيذ، انظر الوثائق المصدرية.