Realtime HTTP API

بناء خط زمني للمتحدثين من صوت ما زال يصل

أرسل إطارات PCM16 متسلسلة لكل UUID ووفّق إضافات المقاطع النهائية مع اللقطة النشطة.

POST/http/diarization-stream

استخدم هذه العملية لبناء خط زمني للمتحدثين بينما لا يزال صوت PCM يصل. وهي لا تنسخ الكلام ولا تتعرف على أشخاص حقيقيين. تستخدم حزم SDK لـJavaScript وPython في الإصدار 0.18.0 بروتوكول Socket.IO؛ ولا ترسل طلب HTTP هذا.

أرسل من خلفية موثوقة إطارًا ثنائيًا مكتملًا واحدًا في كل POST مع X-Api-Key وقدرة تمييز المتحدثين. أعد استخدام UUID جديد غير صفري واحد حتى ينتهي التدفق، ولا تُبقِ أكثر من POST واحد قيد التنفيذ لهذا UUID. قد تستبدل الطلبات المتزامنة للـUUID نفسه ملكية الاستجابة. التقط الصوت في قائمة انتظار محدودة، ودع مرسلاً واحدًا يفرغها ويغلق كل استجابة قبل إرسال الإطار التالي. يمكن تشغيل تدفقات UUID مختلفة بالتوازي.

لكل إطار ترويسة من 18 بايتًا يتبعها صوت PCM16 خام أحادي القناة غير فارغ بترتيب little-endian وتردد 16 kHz. البايتات 0..15 هي UUID. في البايت 16، يمثل bit 0 الحقل is_start ويمثل bit 1 الحقل is_final؛ ويجب أن تكون البتات المحجوزة 2..7 أصفارًا، ويُرفض أي إطار يضبط أيًا منها بالحالة 400 VALIDATION_INVALID_FORMAT. يجب أن يكون البايت 17 أحد 0 (العربية) أو 1 (الإنجليزية) أو 2 (تبديل اللغات) أو 255 (تلقائي)، لكنه يُهمل بعد التحقق ولا يغير تمييز المتحدثين. اضبط علم البدء في الإطار الأول فقط، وعلم النهاية في آخر إطار صوت فعلي، والعلمين معًا (0x03) لتدفق من إطار واحد. يجب أن يتضمن كل طلب حمولة PCM غير فارغة ذات طول زوجي؛ ولا يوجد فاصل نهاية فارغ.

قد تحتوي كل استجابة 200 صفرًا أو أكثر من سجلات NDJSON. خزّن قراءات الشبكة ولا تقسّم إلا عند السطر الجديد. اجمع السجلات من كل استجابة للـUUID. راكم فروق final_segments التي لم تُر من قبل، واستبدل لقطة active_segments السابقة، ورتب الخط الزمني الموحّد بحسب start_time. تسميات المتحدثين نسبية إلى تدفق واحد وليست هويات. أزمنة المقاطع بالثواني منذ بدء التدفق. أبقِ الذيل النشط غير الفارغ في السجل النهائي مؤقتًا، ولا تعِد تسميته نهائيًا.

لا يكمل التدفق إلا سجل ملحوظ يحتوي is_final: true. لا يكمله بت النهاية في الطلب ولا 200 فارغة ولا EOF للاستجابة ولا المهلة. وإذا بدأ الخرج، ينهي أي فشل لاحق بثَّ 200 الجزئي من دون إلحاق خطأ JSON. ويحافظ انتهاء نافذة الاستجابة غير النهائية العادية بعد ثانيتين على الجلسة. ويلغي إجهاض طلب POST أو انقضاء مهلة الاستجابة النهائية الجلسةَ، كما تنتهي صلاحية الجلسة بعد 60 ثانية من دون نشاط من العميل أو من محرك الاستدلال. لا يوجد عقد عبر HTTP لإعادة تشغيل المقاطع أو الاستئناف أو idempotency. بعد فشل ملتبس، أوقف المنتج وأغلق كل استجابة واحتفظ بالخط الزمني بوصفه غير مكتمل، ثم تعافَ باستخدام UUID جديد بدل إعادة تشغيل مقطع قديم.

تختار الخدمة نموذج تمييز المتحدثين الفوري؛ ولا يملك العملاء محدد نموذج. يعيد غياب ضبط النموذج 400 DIARIZATION_MODEL_NOT_FOUND. وتندمج أخطاء سعة الخلفية والاستدلال حاليًا في 500 DIARIZATION_FAILED قابل لإعادة المحاولة؛ وقد تعيد بوابة الإنتاج بصورة مستقلة 429 بتفاصيل تعتمد على النشر.

المصادقة

ApiKeyAuth
X-Api-Key<token>

الموضع: header

جسم الطلب

application/octet-stream

جسم الاستجابة

application/x-ndjson

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -sS --fail-with-body --connect-timeout 10 --max-time 120 -X POST \  "https://example.com/http/diarization-stream" \  -H "Origin: https://example.com" \  -H "Content-Type: application/octet-stream" \  --data-binary @request.bin

صفر أو أكثر من سجلات تمييز المتحدثين بصيغة NDJSON. بعد بدء الخرج، ينهي أي فشل لاحق البثَّ الجزئي من دون إلحاق خطأ JSON. ويتطلب الاكتمال ملاحظة سجل يحتوي is_final: true.

application/x-ndjson

فرق المقاطع النهائية مع اللقطة النشطة الحالية

{
  "id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "final_segments": [
    {
      "start_time": 0,
      "end_time": 1.5,
      "speaker": "SPEAKER_01"
    }
  ],
  "active_segments": [
    {
      "start_time": 1.5,
      "end_time": 3,
      "speaker": "SPEAKER_02"
    }
  ],
  "is_final": false
}

فرق نهائي لاحق مع أفضل ذيل مؤقت معروف

{
  "id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "final_segments": [
    {
      "start_time": 1.5,
      "end_time": 3,
      "speaker": "SPEAKER_02"
    }
  ],
  "active_segments": [
    {
      "start_time": 3,
      "end_time": 3.4,
      "speaker": "SPEAKER_01"
    }
  ],
  "is_final": true
}

إطار تمييز متحدثين أو بدء تدفق أو ضبط نموذج خدمة غير صالح

application/json

الترويسة قصيرة أو UUID صفري

{
  "error": "invalid audio upload",
  "code": "VALIDATION_FILE_CORRUPT",
  "detail": "invalid audio upload",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

بايت اللغة ليس 0 أو 1 أو 2 أو 255

{
  "error": "invalid language",
  "code": "VALIDATION_INVALID_LANGUAGE",
  "detail": "invalid language",
  "job_id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

حمولة PCM فارغة

{
  "error": "audio upload is empty",
  "code": "VALIDATION_FILE_CORRUPT",
  "detail": "audio upload is empty",
  "job_id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

عدد بايتات حمولة PCM فردي

{
  "error": "audio must contain int16 samples",
  "code": "VALIDATION_INVALID_FORMAT",
  "detail": "audio must contain int16 samples",
  "job_id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

الإطار الأول لا يضبط بت البدء

{
  "error": "missing is_start flag",
  "code": "VALIDATION_REQUIRED_FIELD",
  "detail": "missing is_start flag",
  "job_id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

تمييز المتحدثين الفوري غير مضبوط

{
  "error": "diarization model not found",
  "code": "DIARIZATION_MODEL_NOT_FOUND",
  "detail": "diarization model not found",
  "job_id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

غير مصرح

application/json

المثال missing_key

{
  "error": "Invalid authentication",
  "code": "AUTH_UNAUTHORIZED",
  "detail": "Invalid authentication",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

لا يمنح مفتاح API صلاحية الوصول إلى قدرة الصوت المطلوبة

application/json

المثال scope_denied

{
  "error": "scope not permitted",
  "code": "AUTH_FORBIDDEN",
  "detail": "scope not permitted",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

الطريقة غير مسموحة

application/json

المثال wrong_method

{
  "error": "method not allowed",
  "code": "METHOD_NOT_ALLOWED",
  "detail": "method not allowed",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

لم يصل أي صوت على شبه الجلسة هذه داخل نافذة سكونها، فأُنهيت الجلسة (RFC 9110 15.5.9). وهي غير قابلة لإعادة المحاولة بمعرّف الجلسة نفسه، الذي صار مسجَّلًا كمنتهٍ ولا يُعاد استخدامه: ابدأ جلسة جديدة بمعرّف جديد ومع is_start.

application/json

المثال session_went_idle

{
  "error": "session idle timeout exceeded; start a new session",
  "code": "SESSION_IDLE_TIMEOUT",
  "detail": "session idle timeout exceeded; start a new session",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z",
  "data": {
    "limit": 900,
    "observed": 901,
    "unit": "seconds",
    "bound": "session_idle"
  }
}

يتعارض المقطع مع حالة شبه الجلسة التابع لها (RFC 9110 15.5.10): فالمعرّف لم يُبدأ قط، أو أُنهي فعلًا، أو وصل is_start لمعرّف حيّ بالفعل. وإجابة واحدة تغطي كل هذه الحالات، لذلك لا يمكن للتوقيت أن يغيّر العقد. ابدأ جلسة جديدة بمعرّف جديد.

application/json

المثال not_live

{
  "error": "session is not live; start a new session with is_start and a new id",
  "code": "SESSION_EXPIRED",
  "detail": "session is not live; start a new session with is_start and a new id",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z"
}

تجاوز جسم الطلب حد البايتات المضبوط لهذا المسار الصوتي: 64 MiB لرفع Fast، و16 MiB لإطارات ASR الفوري، و16 MiB لإطارات تمييز المتحدثين الفوري. وهو غير قابل لإعادة المحاولة بالحجم نفسه؛ أعد إرسال وحدة أو مقطع أصغر.

ويسمّي data.bound حد البايتات الذي تم بلوغه — fast_audio_bytes أو realtime_asr_frame_bytes أو realtime_diarization_frame_bytes. و data.observed هو حجم الطلب الدقيق عندما يعلن العميل Content-Length، وهو فيما عدا ذلك حد أدنى (الحد زائد بايت واحد)، لأن الجسم الذي لا يعلن طوله يُقطع في أثناء القراءة ولا يُعرف حجمه الحقيقي أبدًا.

ولا تُبلَغ هذه الحالة إلا من عدّ بايتات. أما الطلب المقبولة بايتاته والذي يطول صوته بعد فك الترميز فهو الحالة 422 برمز AUDIO_DURATION_EXCEEDED بدلًا منها.

application/json

أُعلن `Content-Length`، فالقيمة المرصودة دقيقة

{
  "error": "request body too large",
  "code": "PAYLOAD_TOO_LARGE",
  "detail": "request body too large",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z",
  "data": {
    "limit": 16777216,
    "observed": 20971520,
    "unit": "bytes",
    "bound": "realtime_asr_frame_bytes"
  }
}

لا طول معلن، فالقيمة المرصودة هي الحد زائد بايت واحد

{
  "error": "request body too large",
  "code": "PAYLOAD_TOO_LARGE",
  "detail": "request body too large",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z",
  "data": {
    "limit": 67108864,
    "observed": 67108865,
    "unit": "bytes",
    "bound": "fast_audio_bytes"
  }
}

حُلِّل الطلب بصورة صحيحة وكانت بايتاته مقبولة، لكن مقدار الصوت الذي يطلب من الخدمة معالجته يتجاوز سقف هذه النقطة (RFC 9110 15.5.21). ورفع مضغوط صغير يُفك إلى ساعات كثيرة هو هذه الحالة بالضبط، ولهذا لا تكون الحالة 413.

ويسمّي data.bound السقف الصوتي الذي تم بلوغه:

  • fast_audio_duration — إرسال Fast واحد فُك إلى أكثر من 1800 ثانية. قسّم التسجيل أو استخدم واجهة النسخ الدفعي.
  • session_audio_duration — أرسلت جلسة فورية الآن محتوى صوتيًا إجماليًا يزيد على حصتها البالغة 14400 ثانية (4 ساعات). وقد أُنهيت الجلسة؛ فابدأ جلسة جديدة.

وdata.observed بالثواني الكاملة، مقرَّبًا إلى الأعلى. وحيث توقفت الخدمة عن فك الترميز عند السقف فإنها لم تعرف الطول الإجمالي الحقيقي، ولذلك تكون القيمة المرصودة حدًا أدنى لا قياسًا دقيقًا.

وهي غير قابلة لإعادة المحاولة: فإعادة إرسال الصوت نفسه لا يمكن أن تنجح. قصّر الوحدة، أو انتقل إلى واجهة النسخ الدفعي.

application/json

المثال fast_decoded_audio_too_long

{
  "error": "decoded audio duration exceeds the maximum for this endpoint; split the recording or use the batch transcription API",
  "code": "AUDIO_DURATION_EXCEEDED",
  "detail": "decoded audio duration exceeds the maximum for this endpoint; split the recording or use the batch transcription API",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z",
  "data": {
    "limit": 1800,
    "observed": 3601,
    "unit": "seconds",
    "bound": "fast_audio_duration"
  }
}

المثال session_audio_allowance_spent

{
  "error": "session maximum audio duration exceeded; start a new session",
  "code": "AUDIO_DURATION_EXCEEDED",
  "detail": "session maximum audio duration exceeded; start a new session",
  "retryable": false,
  "timestamp": "2026-01-15T10:30:00Z",
  "data": {
    "limit": 14400,
    "observed": 14401,
    "unit": "seconds",
    "bound": "session_audio_duration"
  }
}

تم تحديد معدل طلب فوري. وتشترك ثلاثة مصادر مختلفة في هذه الحالة على هذه المسارات، ويميز بينها حقل code:

  • SESSION_BYTE_RATE_EXCEEDED — الصوت يصل أسرع مما يسمح به المعدل المستدام للجلسة (أربعة أضعاف الزمن الحقيقي، مع اندفاع 16 MiB). التزم بـRetry-After؛ فتنجح الحمولة نفسها بعدها. وتبقى الجلسة حيّة. وdata.bound هو session_audio_rate_burst.
  • CONCURRENCY_LIMIT_EXCEEDED — لدى الحساب القابل للفوترة فعلًا من العمليات المتزامنة من هذا النوع قيد التنفيذ ما تسمح به خطته. وdata.bound هو account_concurrency_<workload>.
  • SESSION_SLOTS_EXHAUSTED — يحتفظ الحساب من أشباه جلسات HTTP المتزامنة بقدر ما تسمح به هذه العملية.

كما يجيب حد معدل الطلبات لكل مفتاح في البوابة بالحالة 429 أيضًا ويبلّغ RATE_LIMIT_EXCEEDED، وشكل جسمه يعتمد على النشر. وكل هذه قابلة لإعادة المحاولة، ولا يستهلك أي منها رصيدًا ولا حصة.

application/json

المثال audio_arriving_too_fast

{
  "error": "audio is arriving faster than this session allows; slow down to real time and retry",
  "code": "SESSION_BYTE_RATE_EXCEEDED",
  "detail": "audio is arriving faster than this session allows; slow down to real time and retry",
  "retryable": true,
  "timestamp": "2026-01-15T10:30:00Z",
  "data": {
    "limit": 16777216,
    "observed": 33554432,
    "unit": "bytes",
    "bound": "session_audio_rate_burst"
  }
}

المثال account_concurrency_exhausted

{
  "error": "too many concurrent operations for this account",
  "code": "CONCURRENCY_LIMIT_EXCEEDED",
  "detail": "too many concurrent operations for this account",
  "retryable": true,
  "timestamp": "2026-01-15T10:30:00Z",
  "data": {
    "limit": 8,
    "observed": 8,
    "unit": "operations",
    "bound": "account_concurrency_realtime_asr"
  }
}

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

application/json

المثال diarization_failed

{
  "error": "realtime diarization failed",
  "code": "DIARIZATION_FAILED",
  "detail": "realtime diarization failed",
  "job_id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
  "retryable": true,
  "timestamp": "2026-01-15T10:30:00Z"
}

الخطوات التالية

أكمل دورة حياة التدفق الحي قبل استهلاك تسميات المتحدثين: احتفظ بـUUID واحد عبر المقاطع المؤطرة، وأرسل طلبات UUID نفسه بالتتابع، ووفّق الإضافات النهائية مع أحدث لقطة نشطة، ونظّف عند نتيجة نهائية أو خطأ منظم أو موعد نهائي.

في هذه الصفحة