الاستجابات والبث

6.1 الطلب

POST /lmsp/v1/responses
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: 01J8M5T7Z2K4N6Q8S0V3X5B9DG
LMSP-Version: 1.0
{
  "model": "qwen3:14b-q4_k_m",
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "Describe a local model service in one sentence."
        }
      ]
    }
  ],
  "stream": false,
  "max_output_tokens": 512,
  "temperature": 0.7,
  "response_format": {
    "type": "text"
  },
  "store": true
}

يستخدم البروتوكول الأساسي بنية input وoutput على نمط Responses بدلًا من قائمة بسيطة من سلاسل الرسائل، ليتسع شكل طلب واحد لما يلي:

  • رسائل متعددة الأدوار؛
  • نصوصًا وصورًا وصوتًا وملفات؛
  • استدعاءات أدوات ونتائجها؛
  • مخرجات منظَّمة؛
  • ملخصات الاستدلال؛
  • مخرجات متعددة الوسائط.

يحدد LMSP 1.0 المحتوى النصي فقط. أما أنواع محتوى الصور والصوت والملفات فمحجوزة للإصدارات اللاحقة.

الحقل إلزامي المعنى
model نعم معرّف النموذج المراد استخدامه.
input نعم مصفوفة عناصر الإدخال. عنصر message له role (system أو user أو assistant) ومصفوفة content من الأجزاء، مثل input_text. ويضيف استخدام الأدوات عنصري function_call وfunction_call_output (القسم 6.5).
stream لا true لاستقبال أحداث يرسلها الخادم. والافتراضي false.
max_output_tokens لا حد أعلى لعدد الرموز المولَّدة.
temperature لا درجة حرارة أخذ العينات.
response_format لا {"type": "text"} (الافتراضي)، أو {"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}} للمخرجات المنظَّمة. يتطلب القدرة structured_output.
tools, tool_choice لا أدوات دالّية يمكن للنموذج استدعاؤها (القسم 6.5). يتطلب القدرة tools.
store لا يطلب من الخدمة الاحتفاظ بالاستجابة، لإمكان استرجاعها لاحقًا واستخدامها بوصفها previous_response_id. والافتراضي false. تتجاهله الخدمات التي لا تملك القدرة conversations.
previous_response_id لا يواصل من استجابة مخزَّنة. يتطلب القدرة conversations.
extensions لا امتدادات خاصة (القسم 9.1).

يُرفض الطلب الذي يحتاج إلى قدرة ليست لدى النموذج بـ 400 مع unsupported_capability. ويُرفض الطلب الذي لا تتسع له نافذة السياق بـ 400 مع context_length_exceeded.

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

6.2 الاستجابة المتزامنة

عندما تكون stream هي false، تعيد الخدمة 200 OK مع كائن استجابة بعد انتهاء التوليد.

{
  "id": "resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF",
  "object": "response",
  "created_at": 1794200000,
  "status": "completed",
  "model": "qwen3:14b-q4_k_m",
  "output": [
    {
      "id": "msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE",
      "type": "message",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "A local model service is a resident interface layer that discovers, manages and calls models on the user's own device."
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 28,
    "output_tokens": 31,
    "total_tokens": 59
  },
  "error": null,
  "incomplete_details": null
}
الحقل المعنى
id معرّف الاستجابة، مثل resp_....
object دائمًا "response".
created_at وقت الإنشاء بالثواني منذ حقبة Unix.
status in_progress أو completed أو incomplete أو failed أو cancelled. وتُعاد الاستجابة المتزامنة بحالة نهائية.
model معرّف النموذج الذي أنتج الاستجابة.
output مصفوفة عناصر المخرجات.
usage أعداد الرموز: input_tokens وoutput_tokens وtotal_tokens.
error null، أو كائن مشكلة (القسم 9.3) عندما تكون status هي failed.
incomplete_details null، أو كائن فيه reason مثل max_output_tokens عندما تكون status هي incomplete.

أنواع عناصر المخرجات:

نوع العنصر الشكل
message id وrole (assistant) وcontent، وهي مصفوفة أجزاء output_text يحمل كل منها text.
function_call id وcall_id وname وarguments، وهي سلسلة مرمَّزة بصيغة JSON.
reasoning id وsummary، وهي مصفوفة أجزاء summary_text يحمل كل منها text.

إذا فشل التوليد، يُرد على الطلب المتزامن بحالة خطأ ومستند مشكلة (القسم 9.3)، لا بكائن استجابة. وتظهر الحالة failed في التدفقات والمهام والاستجابات المخزَّنة.

يعيد GET /lmsp/v1/responses/{response_id} استجابة خُزّنت بـ store: true. وهو اختياري ويتطلب القدرة conversations. والمعرّف المجهول يعطي 404 مع response_not_found.

6.3 البث

للبث، اضبط stream في الطلب:

{
  "stream": true
}

ترد الخدمة بـ 200 OK مع Content-Type: text/event-stream وترسل أحداثًا دلالية. لكل حدث id واسم event وكائن JSON هو data:

id: 1
event: response.created
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"in_progress"}

id: 2
event: response.output_item.added
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[]}}

id: 3
event: response.output_text.delta
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"A local model "}

id: 4
event: response.output_text.delta
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"service is a resident interface layer."}

id: 5
event: response.output_item.done
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[{"type":"output_text","text":"A local model service is a resident interface layer."}]}}

id: 6
event: response.completed
data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"completed","usage":{"input_tokens":28,"output_tokens":12,"total_tokens":40}}

تُعرَّف هذه الأحداث على الأقل:

الحدث المعنى حقول البيانات
response.created أُنشئت الاستجابة. response_id، status (in_progress)
response.output_item.added بدأ عنصر مخرجات جديد. response_id، output_index، item
response.output_text.delta أُلحق نص بعنصر رسالة. response_id، item_id، delta
response.output_item.done اكتمل عنصر مخرجات. response_id، output_index، item
response.completed انتهت الاستجابة. الحدث الأخير. response_id، status، usage
response.incomplete توقف التوليد مبكرًا، مثلًا عند حد الرموز. الحدث الأخير. response_id، status، incomplete_details، usage
response.failed فشل التوليد. الحدث الأخير. response_id، status، error
response.cancelled أُلغيت الاستجابة. الحدث الأخير. response_id، status
error مشكلة غير مرتبطة باستجابة واحدة، ولا يمكن للتدفق أن يستمر بعدها. error، وهو كائن مشكلة

تغلق الخدمة التدفق بعد حدث أخير أو حدث error.

يجوز للخدمات إرسال أحداث إضافية، مثل أحداث لوسائط استدعاءات الأدوات والصور والصوت ودلتا JSON المنظَّم. ويجب على العملاء تجاهل أنواع الأحداث التي لا يعرفونها.

إذا أغلق العميل اتصال طلب مبثوث، ينبغي للخدمة أن تتوقف عن التوليد.

6.4 معرّفات الأحداث وإعادة الاتصال

يجب أن يكون id الحدث رقمًا تسلسليًا صحيحًا يزداد بصرامة داخل الاستجابة، ويبدأ من 1.

بعد انقطاع الاتصال، يستأنف العميل بإرسال آخر معرّف تلقّاه. ويفعل ذلك بترويسة:

Last-Event-ID: 2

أو بمعامل استعلام:

GET /lmsp/v1/tasks/{task_id}/events?after=2

وكلاهما يذهب إلى نقطة نهاية أحداث المهمة (القسم 7.4). وتعيد الخدمة إرسال الأحداث التي يزيد رقمها التسلسلي على 2 فقط. وإن وُجد الاثنان فالأسبقية لـ after.

إذا كانت الأحداث المطلوبة أقدم من مدة الاحتفاظ، ترد الخدمة بـ 410 Gone مع الخطأ event_history_expired. فيجلب العميل عندها النتيجة النهائية بدلًا من ذلك، بـ GET /lmsp/v1/tasks/{task_id}.

الاستجابة المبثوثة مباشرةً من POST /lmsp/v1/responses تخص اتصال HTTP ذاك ولا يمكن استئنافها. وينبغي للعميل الذي يحتاج إلى مخرجات قابلة للاستئناف أن ينشئ مهمة ويتابع أحداثها.

6.5 استدعاءات الأدوات

يحدد LMSP 1.0 أدوات دالّية ينفذها العميل. والخدمة لا تشغّل الأدوات بنفسها. والنطاق tools:execute محجوز للامتدادات التي تفعل ذلك (القسم 10.3).

أولًا، يسرد العميل الأدوات. لكل إدخال type قيمته function، وname، وdescription، وparameters وهو كائن JSON Schema. وقيمة tool_choice هي auto (الافتراضي) أو none أو required أو {"type": "function", "name": "get_weather"}.

{
  "model": "qwen3:14b-q4_k_m",
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "What is the weather in Hangzhou?"
        }
      ]
    }
  ],
  "tools": [
    {
      "type": "function",
      "name": "get_weather",
      "description": "Get the current weather for a city.",
      "parameters": {
        "type": "object",
        "properties": {
          "city": { "type": "string" }
        },
        "required": ["city"]
      }
    }
  ],
  "tool_choice": "auto"
}

عندما يقرر النموذج استدعاء أداة، تحتوي الاستجابة على عنصر function_call. وتكون arguments فيه سلسلة مرمَّزة بصيغة JSON. وفي التدفق يصل العنصر كاملًا في response.output_item.done.

{
  "id": "resp_01J8M7B3N6Q9T2V5X8Z1C4D7FH",
  "object": "response",
  "created_at": 1794200050,
  "status": "completed",
  "model": "qwen3:14b-q4_k_m",
  "output": [
    {
      "id": "fc_01J8M7B3N8R1S4W7Y0A3E6G9JK",
      "type": "function_call",
      "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM",
      "name": "get_weather",
      "arguments": "{\"city\":\"Hangzhou\"}"
    }
  ],
  "usage": {
    "input_tokens": 74,
    "output_tokens": 18,
    "total_tokens": 92
  },
  "error": null,
  "incomplete_details": null
}

ينفذ العميل الأداة، ثم يرسل طلبًا جديدًا يحمل السياق كاملًا: عناصر الإدخال السابقة، وعنصر function_call كما استُلم، وعنصر function_call_output بـ call_id نفسه. وoutput فيه سلسلة، وتكون عادةً مرمَّزة بصيغة JSON.

{
  "type": "function_call_output",
  "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM",
  "output": "{\"temperature_c\":17,\"condition\":\"light rain\"}"
}