ابنِ على جْبْ. العربية أولًا، ومتوافق مع OpenAI.
عنوان أساسي واحد ومفتاح واحد. وجّه مكتبة OpenAI إلى جْبْ لتحصل على ردود عربية ومتعددة اللغات قوية، وبثٍّ مباشر، ودوالّ من تطبيقك، وأدوات مدمجة.
https://api.gebai.ca/v2يلزم تسجيل الدخول. تحصل الحسابات المجانية على 500 استدعاء شهريًا. لا رسوم على الرموز.
غيّر العنوان الأساسي والمفتاح فقط. تعمل واجهة إكمال المحادثة والبثّ واستدعاء الدوال كما تتوقع.
تتبع الردود لغة مستخدمك، بالعربية الفصحى الحديثة أو بالعامية المصرية عندما تطلب ذلك.
مفاتيح عدم التكرار، ورموز أخطاء واضحة، وردّ على كل استدعاء حتى عندما يُقطع التوليد قبل اكتماله.
1.ما تقدمه الواجهة البرمجية وما لا تقدمه
تمنح الواجهة البرمجية لجْبْ تطبيقك النموذج نفسه الذي يردّ داخل تطبيق جْبْ، عبر واجهة إكمال المحادثة التي تتحدث بها مكتبات عملاء OpenAI أصلًا. وجّه حزمة OpenAI SDK إلى https://api.gebai.ca/v2، واستخدم مفتاح جْبْ، وستعمل مباشرة.
تسمّي جْبْ هذه الواجهة API v2، وتبدأ مسارات عناوين URL فيها بـ /v2. ويستمر العملاء الحاليون الذين ما زالوا يستدعون /v1 في العمل: إذ إن /v1/... اسم بديل يصل إلى المعالجات نفسها تمامًا، بالسلوك والترويسات والأخطاء والحصص نفسها، ويُحتسب الاستدعاء مرة واحدة أيًّا كانت البادئة المستخدمة. وينبغي للشيفرة الجديدة أن تستخدم /v2.
ما تحصل عليه
- إكمال المحادثة عند
POST /v2/chat/completions، بشكل مكتمل أو مبثوث عبر أحداث يرسلها الخادم. - ردود قوية بالعربية وبلغات متعددة. تردّ جْبْ بلغة آخر رسالة من مستخدمك، في 23 لغة. العربية لغة من الدرجة الأولى، وليست طبقة ترجمة.
- دوالّك الخاصة عبر الحقل القياسي
tools(استدعاء الدوال). - الأدوات المدمجة في جْبْ، مثل البحث في الويب والطقس ومنشئات المستندات وجداول البيانات، عندما تفعّلها بالاسم.
- مخرجات منظَّمة عبر
response_format(json_objectوjson_schema). - إدخال الصور في رسائل المستخدم.
- التحكم في الاستدلال بمستويات الجهد.
- محادثات مخزَّنة يمكنك متابعتها بالمعرّف، وتبقى منفصلة عن أي ذاكرة للحساب.
- قواعد الرد (
geb_acceptance): صرّح بنطاقات الكلمات والعبارات المحظورة ومتطلبات الإسناد، وتتحقق جْبْ من الرد وتحسّنه قبل إعادته. - لا رسوم لكل رمز. تتضمن باقتك عددًا من الاستدعاءات في كل فترة فوترة (انظر القسم 10). لا تُحتسب الرموز.
ما لا تقدمه
- ليست وسيطًا لـ OpenAI. كل رد يأتي من نموذج جْبْ نفسه. ولا يُسلَّم طلبك أبدًا إلى نموذج تابع لمورّد آخر.
- لا تدرك الذاكرة. لا تقرأ استدعاءات API ذاكرة حساب جْبْ الخاص بك ولا تكتب إليها. يُبنى الاستدعاء من طلبك وحده (إضافة إلى سلسلة محادثة مخزَّنة إذا طلبتها، انظر القسم 5.5).
- إكمال المحادثة فقط. لا توجد نقاط نهاية للتضمينات أو الصوت أو توليد الصور أو الملفات أو المعالجة الدفعية أو الضبط الدقيق، ولا لواجهتي Assistants وResponses.
- اختيار واحد فقط. يُرفض كل من
nوlogprobsوالحقل القديمfunctions. - بعض حقول أخذ العينات مقبولة ومتجاهلة. لا تتسبب
stopوuserوseedوtemperatureوtop_pوpresence_penaltyوfrequency_penaltyوlogit_biasوmin_pوtop_kوrepetition_penaltyفي أي خطأ، وليس لها أي أثر اليوم. - لا يمكن استدعاؤها من متصفح على موقع شخص آخر. تردّ الواجهة على طلبات التحقق المسبق عبر الأصول المختلفة لمواقع جْبْ فقط. استدعِها من خادمك، لا من JavaScript في جهة العميل. ولا تضع مفتاحًا أبدًا في صفحة ويب أو تطبيق جوال.
2.البدء السريع
أول استدعاء لك في خمس دقائق. اختر لغتك؛ يُحفظ اختيارك في كل الأمثلة في الصفحة.
- احصل على مفتاح (القسم 3). يبدو بالشكل
geb_live_متبوعًا بـ 43 حرفًا. - ضعه في متغير بيئة. ولا تلصقه أبدًا في الشيفرة المصدرية.
- أجرِ استدعاءً.
export GEB_API_KEY="geb_live_..." # paste your key here, in your shell only
curl https://api.gebai.ca/v2/chat/completions \
-H "Authorization: Bearer $GEB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "geb",
"messages": [
{"role": "user", "content": "Greet me in Arabic and in English, one line each."}
]
}'# pip install openai
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.gebai.ca/v2",
api_key=os.environ["GEB_API_KEY"],
)
reply = client.chat.completions.create(
model="geb",
messages=[{"role": "user", "content": "Greet me in Arabic and in English, one line each."}],
)
print(reply.choices[0].message.content)// npm install openai
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.gebai.ca/v2",
apiKey: process.env.GEB_API_KEY,
});
const reply = await client.chat.completions.create({
model: "geb",
messages: [{ role: "user", content: "Greet me in Arabic and in English, one line each." }],
});
console.log(reply.choices[0].message.content);الحصول على مفتاح
- سجّل الدخول إلى gebai.ca.
- افتح الملف الشخصي، ثم مفاتيح API.
- أنشئ مفتاحًا، وانسخه مرة واحدة، واحتفظ به على خادمك.
ما تحصل عليه في الرد
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1790000000,
"model": "<model-id>",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 }
}model هو معرّف النموذج الذي ردّ، كما يرد في GET /v2/models. ويشمل usage.prompt_tokens تعليمات جْبْ الأساسية، التي تتصدر كل موجّه.
إذا حصلت على 401 فتحقق من المفتاح ومن ترويسة Authorization (القسم 4). وإذا حصلت على 429 فاقرأ القسم 10.
3.الحصول على مفتاح
تُنشأ المفاتيح بواسطة حساب جْبْ مسجَّل الدخول، في تطبيق ويب جْبْ.
- أنشئ حسابك أو سجّل الدخول إليه على
https://gebai.ca. - افتح الملف الشخصي، ثم مفاتيح API (
https://gebai.ca/profile/api-keys). - اختر إنشاء مفتاح API. أعطه اسمًا، واختياريًا مدة صلاحية بالأيام (من 1 إلى 365).
- انسخ المفتاح فورًا. يُعرض مرة واحدة ولا يمكن استرجاعه لاحقًا. وتخزّن جْبْ قيمة تجزئة فقط.
أمور ينبغي معرفتها:
- يمكن للحساب أن يحمل حتى 10 مفاتيح نشطة. ألغِ أحدها لتحرير خانة.
- ألغِ أي مفتاح من الصفحة نفسها في أي وقت. المفتاح الملغى أو المنتهي يردّ بـ
401منذ الطلب التالي. - كل مفتاح يتبع الحساب الذي أنشأه. وتُحتسب الاستدعاءات والحصة وحدود المعدّل لكل حساب، عبر جميع مفاتيحه.
- نطاق المفتاح الوحيد هو
chat:write، وهو يغطي كل نقطة نهاية في هذا الدليل. - كل باقة تستطيع استخدام الواجهة، ومنها المجانية: يمكن للحساب المجاني إنشاء مفاتيح وإجراء 500 استدعاء في كل فترة فوترة. وعدد الاستدعاءات التي تتضمنها كل باقة مذكور في القسم 10.
أبقِ المفاتيح على خادمك. استخدم مفتاحًا واحدًا لكل تطبيق أو بيئة، لتتمكن من إلغاء أحدها دون المساس بالبقية.
4.المصادقة
أرسل المفتاح كرمز حامل (bearer token) في كل طلب.
Authorization: Bearer geb_live_...curl https://api.gebai.ca/v2/models -H "Authorization: Bearer $GEB_API_KEY"import os, requests
headers = {"Authorization": f"Bearer {os.environ['GEB_API_KEY']}"}
print(requests.get("https://api.gebai.ca/v2/models", headers=headers).json())const res = await fetch("https://api.gebai.ca/v2/models", {
headers: { Authorization: `Bearer ${process.env.GEB_API_KEY}` },
});
console.log(await res.json());الطلب بلا مفتاح، أو بترويسة مشوَّهة، أو بنظام مصادقة مختلف (مثل Basic)، أو بمفتاح مجهول أو ملغى أو منتهي، كلها تعيد الرد نفسه:
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
x-request-id: 0f658b6d9742d46fec9ce4b984f99667
{"error":{"message":"A valid developer API key is required.","type":"invalid_api_key","param":null,"code":null,"request_id":"0f658b6d9742d46fec9ce4b984f99667"}}يعني 403 permission_error أن المفتاح صالح لكن الحساب غير مسموح له باستخدام الواجهة (مثلًا الحساب معلّق).
5.نقاط النهاية
جميع نقاط النهاية تحت https://api.gebai.ca، وتستخدم JSON، وتتطلب مفتاح الحامل. والبادئة المعتمدة هي /v2. أما المسارات نفسها بالبادئة /v1 فهي اسم بديل للعملاء الحاليين وتسلك السلوك نفسه. وعندما يذكر رد أو خطأ مسارًا فإنه يذكر البادئة التي استخدمتها.
| الطريقة والمسار | الغرض |
|---|---|
GET /v2/models | يعرض النموذج الذي يمكنك استدعاؤه، مع قدراته الحية. |
GET /v2/models/{id} | يسترجع ذلك النموذج. المعرّف المجهول يعطي 404. |
POST /v2/chat/completions | ينشئ إكمال محادثة، مكتملًا أو مبثوثًا. |
GET /v2/conversations | يعرض محادثاتك المخزَّنة. |
GET /v2/conversations/{id} | يقرأ محادثة مخزَّنة واحدة. |
DELETE /v2/conversations/{id} | يحذف محادثة مخزَّنة واحدة. |
يمكن ضغط أجسام الطلبات بـ gzip أو deflate (Content-Encoding: gzip). والحد الأقصى لجسم الطلب 32 MiB بعد فك الضغط.
5.1 قائمة النماذج
curl https://api.gebai.ca/v2/models -H "Authorization: Bearer $GEB_API_KEY"for m in client.models.list():
print(m.id)for await (const m of client.models.list()) console.log(m.id);الرد قائمة بصيغة OpenAI. ويحمل كل نموذج معلومات حية عما تستطيع الواجهة فعله الآن:
{
"object": "list",
"data": [
{
"id": "<model-id>",
"object": "model",
"created": 1755475200,
"owned_by": "geb-ai",
"context_window": 0,
"modalities": { "input": ["text", "image"], "output": ["text"] },
"capabilities": { "streaming": true, "tools": true, "vision": true, "reasoning": true },
"reasoning": {
"always_on": true,
"efforts": ["low", "medium", "high", "ultra"],
"default_effort": "<level>",
"parameters": ["reasoning", "reasoning_effort", "thinking", "enable_thinking", "..."]
},
"limits": {
"max_tokens": "answer_ceiling",
"max_tokens_default": "derived_from_context_window",
"long_non_streamed": "keepalive"
},
"geb_tools": ["web_search", "weather_forecast", "..."],
"status": { "healthy": true, "state": "<state>" }
}
]
}context_windowهو عدد الرموز التي يستطيع النموذج استيعابها في طلب واحد. وقيمتهnullعندما لا تستطيع جْبْ قياسه حاليًا.capabilities.visionو"image"فيmodalities.inputحيّان: لا تكونانtrueإلا ما دام طلب يحتوي صورة سيُقبل في هذه اللحظة.geb_toolsهي القائمة الدقيقة لأسماء الأدوات المدمجة التي يجوز للطلب تسميتها (القسم 7.2).- القيم أعلاه توضيحية. اقرأ القيم الفعلية من استدعائك أنت.
تخدم الواجهة اليوم نموذجًا واحدًا من جْبْ. الحقل model في الإكمال مقبول لأغراض التوافق مع OpenAI (أي نص مقبول) ولا يختار بين النماذج. ويذكر الرد المعرّف الذي ردّ فعلًا.
5.2 إنشاء إكمال محادثة
POST /v2/chat/completions
| المعامل | النوع | ملاحظات |
|---|---|---|
model | نص | اختياري. مقبول للتوافق، انظر أعلاه. |
messages | مصفوفة | مطلوب. الأدوار system وuser وassistant وtool. وcontent نص، أو في رسائل user مصفوفة من أجزاء text وimage_url. |
stream | قيمة منطقية | true يبثّ أحداثًا يرسلها الخادم (القسم 6). |
max_tokens | عدد صحيح | سقف للإجابة المرئية. وتُحتسب ميزانية التفكير بشكل منفصل (القسم 8). |
tools, tool_choice, parallel_tool_calls | دوالّك الخاصة (القسم 7.1). | |
response_format | كائن | {"type":"json_object"} أو {"type":"json_schema", ...}. |
stream_options | كائن | {"include_usage": true} يضيف مقطع استخدام أخيرًا إلى البثّ. |
reasoning, reasoning_effort, thinking, enable_thinking | التحكم في الاستدلال (القسم 8). | |
geb_tools | قيمة منطقية أو مصفوفة | الأدوات المدمجة في جْبْ، معطّلة افتراضيًا (القسم 7.2). |
geb_acceptance | كائن | قواعد الرد (القسم 5.6). |
conversation_id | نص | "new" يبدأ محادثة مخزَّنة، ومعرّف يتابع محادثة (القسم 5.5). |
locale | "en", "ar", "fr" | اختياري. تلميح للغة الرد عندما لا تحددها الرسالة نفسها. |
مقبولة لكن متجاهلة (بلا خطأ وبلا أثر): stop وuser وseed وtemperature وtop_p وpresence_penalty وfrequency_penalty وlogit_bias وmin_p وtop_k وrepetition_penalty.
مرفوضة بـ 400: n وlogprobs وfunctions. كما يُرفض geb_memory: true (memory_unavailable)، لأن استدعاءات API لا ذاكرة لها.
الحقول المجهولة أخطاء. الخاصية التي لا تعرفها جْبْ، مثل max_completion_tokens أو store، تعيد 400 برسالة مثل property store should not exist. استخدم max_tokens. وإذا كانت مكتبة العميل لديك تضيف حقولًا من تلقاء نفسها، فأزِلها.
ينبغي أن يكون جسم الطلب JSON صالحًا. تُرفض الفواصل الزائدة في النهاية والتعليقات وNaN وسائر أخطاء الصياغة بالرمز 400 invalid_json_body. أما الحقل الذي يظهر مرتين في كائن واحد فليس خطأً: كما في أي محلّل JSON قياسي، تفوز القيمة الأخيرة. لا ترسل حقولًا مكررة عمدًا.
تعليمات جْبْ نفسها تتصدر كل موجّه. تردّ جْبْ بصفتها جْبْ، ولا يمكن تجاوز قواعد السلامة الخاصة بها. وتأتي رسالة system الخاصة بك بعدها وتؤطّر المحادثة. وتُحتسب هذه التعليمات ضمن usage.prompt_tokens.
لغة الرد. تردّ جْبْ بلغة آخر رسالة من مستخدمك وتتبع أي تحوّل في منتصف المحادثة. والتعليمة الصريحة مثل «رُدَّ بالفرنسية» لها الأولوية. والرسالة التي لا تحسم اللغة («ok»، أو شيفرة) تُبقي لغة آخر رسالة واضحة. وتبقى الشيفرة والأوامر ووسائط الأدوات بلغتها الطبيعية. وبالنسبة للعربية، تحدد رسالة system الخاصة بك المستوى اللغوي: اطلب «العربية الفصحى» أو «العربية المصرية». وفي غياب ذلك تكون المخرجات المكتوبة والترجمات بالعربية الفصحى، وتكون المحادثة بالعربية المصرية. وطلب الترجمة يعيد الترجمة وحدها.
الرد. يشبه الإكمال العادي رد البدء السريع. وتكون finish_reason هي stop (اكتمل)، أو length (قُطع عند max_tokens الخاص بك، أو قُطع مبكرًا، انظر القسم 6.4)، أو tool_calls (يجري استدعاء دالتك)، أو content_filter. تحقق منها دائمًا قبل أن تستخرج JSON من أي رد.
r = client.chat.completions.create(
model="geb",
messages=[
{"role": "system", "content": "You are a concise assistant for a travel app."},
{"role": "user", "content": "اقترح لي خطة ليوم واحد في القاهرة"},
],
max_tokens=800,
)
print(r.choices[0].message.content, r.choices[0].finish_reason, r.usage.total_tokens)const r = await client.chat.completions.create({
model: "geb",
messages: [
{ role: "system", content: "You are a concise assistant for a travel app." },
{ role: "user", content: "Plan one day in Cairo." },
],
max_tokens: 800,
});
console.log(r.choices[0].message.content, r.choices[0].finish_reason, r.usage.total_tokens);5.3 المخرجات المنظَّمة
curl https://api.gebai.ca/v2/chat/completions \
-H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
-d '{
"messages": [{"role":"user","content":"Extract: Layla, 34, lives in Giza."}],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person",
"strict": true,
"schema": {
"type": "object",
"properties": {"name":{"type":"string"},"age":{"type":"integer"},"city":{"type":"string"}},
"required": ["name","age","city"],
"additionalProperties": false
}
}
}
}'- ردود
json_schemaصالحة وفق المخطط. أرسل"strict": trueلتتحقق جْبْ من مخططك أولًا: عندئذ يحتاج كل كائن إلىadditionalProperties: falseولا بد من إدراج كل خاصية فيrequired، ويُرفض المخطط الذي لا يستطيع تحقيق ذلك بـ400مع تسمية المسار المخالف. وبدون الحقلstrictتستخدم جْبْ مخططك كما أُرسل ولا ترفضه، فالمخطط الذي يخالف القواعد الصارمة ليس خطأً، لكن ردوده لا تكون مقيّدة إلا بقدر ما يقيّدها المخطط. - تحقق من
finish_reason. إذا كانتlengthفإن JSON ناقص. ولا يُبلَّغ أبدًا عن جسم JSON مقطوع على أنهstop. - يُحتجز البثّ في استدعاء
response_formatإلى أن تتحقق جْبْ من الرد، ثم يُسلَّم (القسم 6.3). وتُبقي نبضات الاتصال الاتصال مفتوحًا في أثناء ذلك. - لا تعمل الأدوات المدمجة في جْبْ في استدعاء
response_format.
5.4 إدخال الصور
يمكن أن تحمل رسائل المستخدم صورًا على هيئة أجزاء محتوى OpenAI. وتذهب الصور إلى نموذج الرؤية الخاص بجْبْ.
r = client.chat.completions.create(
model="geb",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What is in this picture?"},
{"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}},
],
}],
)const r = await client.chat.completions.create({
model: "geb",
messages: [{
role: "user",
content: [
{ type: "text", text: "What is in this picture?" },
{ type: "image_url", image_url: { url: "https://example.com/cat.jpg" } },
],
}],
});- المصادر: عناوين
https://(تجلبها جْبْ) أو معرّفاتdata:image/{png,jpeg,webp,gif};base64,.... وتُرفضhttp://والأنظمة الأخرى. - الصيغ: PNG وJPEG وWebP وGIF (الإطار الأول). ولا بد أن تطابق البايتات النوع المعلن.
- الحدود: 16 صورة كحد أقصى لكل طلب، و20 MB لكل صورة، و64 MB من بيانات الصور المفكوكة لكل طلب. أرسل الصور الكبيرة كعناوين URL: فجسم الطلب كاملًا محدود بـ 32 MiB.
- تُرفض العناوين التي تُحلّ إلى عناوين خاصة أو داخلية (
image_url_forbidden). - تُعرض الصور في الطلب مع آخر رسالة للمستخدم، بالترتيب. والرسالة التي تحتوي صورة فقط صالحة. وتُرفض الأجزاء غير
textوimage_url(الصوت، الملفات). - إذا كان نموذج الرؤية غير متاح في تلك اللحظة تحصل على
503 vision_unavailable، ولا تحصل أبدًا على تخمين نصي فقط. ويُبلغGET /v2/modelsعنcapabilities.vision: falseفي الحالة نفسها.
5.5 المحادثات
يمكنك إرسال السجل كاملًا مع كل طلب، كما في أي واجهة محادثة. ويمكن لجْبْ أيضًا تخزين السلسلة نيابةً عنك.
- أرسل
"conversation_id": "new". يعود المعرّف في الرد (conversation_id) وفي مقطع البثّ الأخير. - أرسل ذلك المعرّف في الاستدعاءات اللاحقة للمتابعة.
- إذا كان طلبك يحتوي أي رسالة
assistantأوtoolفقد أرسلت السجل: تُستخدم رسائلك كما أُرسلت تمامًا. وإذا كان يحتوي رسائلsystemوuserفقط، تضيف جْبْ السلسلة المخزَّنة إلى الموجّه، بدءًا بأحدث الأدوار، بقدر ما يتسع في نافذة السياق. ويُخزَّن الدور الجديد في الحالتين. - السلسلة المخزَّنة ليست ذاكرة حساب. لا تقرؤها المحادثة أبدًا، ولا يستطيع أي حساب آخر قراءتها.
| المسار | النتيجة |
|---|---|
GET /v2/conversations?limit=50&after=<cursor> | {"object":"list","data":[{"id","object":"conversation","created_at","last_message_at","title"}],"has_more","next_cursor"}، الأحدث أولًا. |
GET /v2/conversations/{id}?limit=100&after=<cursor> | المحادثة مع messages: [{"role","content","created_at"}] الأقدم أولًا، إضافة إلى message_count وhas_more وnext_cursor. |
DELETE /v2/conversations/{id} | {"id","object":"conversation.deleted","deleted":true}. |
المعرّف الذي لا يخصك يردّ بـ 404. وتُحتسب هذه المسارات الثلاثة استدعاءات API على حصتك، مثل الإكمال؛ والاستدعاء الذي يفشل لا يستهلك استدعاءً.
curl https://api.gebai.ca/v2/chat/completions \
-H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
-d '{"conversation_id":"new","messages":[{"role":"user","content":"Remember: my shop sells dates."}]}'5.6 قواعد الرد (`geb_acceptance`)
للأعمال التي ينبغي أن يستوفي فيها الرد قائمة تحقق، صرّح بالقواعد. تكتب جْبْ مسودة، وتتحقق، وتحسّن ما يفشل، وتعيد أفضل نسخة مع تقرير. ولا تُحتسب فحوص القواعد في الفوترة: يبقى الاستدعاء استدعاءً واحدًا.
{
"messages": [{"role": "user", "content": "Write a 300 word product summary from the sources below. ..."}],
"geb_acceptance": {
"version": 1,
"rules": [
{"type": "word_range", "path": "", "min": 250, "max": 350},
{"type": "forbidden_phrases", "paths": ["/**"], "phrases": ["world-class", "cutting-edge"]},
{"type": "claims_grounded", "paths": ["/**"]}
],
"on_fail": "return_best_with_report"
}
}- أنواع القواعد:
word_rangeوforbidden_phrasesوno_repeated_paragraphsوcited_ids_namedوmin_named_sourcesوclaims_groundedوno_copy_fromوphrases_require_source. والمسارpathهو JSON Pointer إلى الرد؛ وفي الرد النصي العادي استخدم""أو/**. - يحمل الرد
x_geb_acceptance: {"passed": true, "rounds": 1}، أوpassed: falseمع قائمةfailures. on_fail: "error_422"يحوّل الفشل النهائي إلى422 geb_acceptance_failedبدلًا من ذلك.- العقد غير الصالح هو
400 invalid_geb_acceptance، حيث يسمّيparamالقاعدة ويبيّنrule_indexموضعها. - يُحتجز البثّ الذي يحمل
geb_acceptanceحتى تنتهي الفحوص (القسم 6.3).
5.7 الترويسات في كل استجابة
| الترويسة | المعنى |
|---|---|
x-request-id | معرّف هذا الاستدعاء، في كل استجابة، ناجحة أو غير ناجحة. ويساوي error.request_id عند الخطأ. اذكره عند التواصل مع الدعم. |
x-geb-api-version | 2 على البادئة /v2 وعلى البادئة /v1 معًا. |
X-RateLimit-Limit | حد الدقيقة، وهو 120 في كل باقة (القسم 10.2). |
X-Quota-Limit-Month, X-Quota-Remaining-Month, X-Quota-Reset | موقعك من حصة الاستدعاءات الشهرية (القسم 10.1). |
x-geb-reasoning-effort, x-geb-reasoning-adjusted | مستوى الاستدلال المستخدم (القسم 8.4). |
6.البثّ
اضبط "stream": true. الرد من النوع text/event-stream: أحداث يرسلها الخادم، كل منها سطر data: <json> يليه سطر فارغ.
curl -N https://api.gebai.ca/v2/chat/completions \
-H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
-d '{"stream":true,"stream_options":{"include_usage":true},
"messages":[{"role":"user","content":"Count to five in Arabic."}]}'stream = client.chat.completions.create(
model="geb",
messages=[{"role": "user", "content": "Count to five in Arabic."}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.choices and chunk.choices[0].finish_reason:
print("\nfinish_reason:", chunk.choices[0].finish_reason)const stream = await client.chat.completions.create({
model: "geb",
messages: [{ role: "user", content: "Count to five in Arabic." }],
stream: true,
stream_options: { include_usage: true },
});
for await (const chunk of stream) {
const choice = chunk.choices[0];
if (choice?.delta?.content) process.stdout.write(choice.delta.content);
if (choice?.finish_reason) console.log("\nfinish_reason:", choice.finish_reason);
}6.1 ترتيب الإطارات
- سطر تعليق من الحشو في البداية (تبدأ التعليقات بـ
:)، وهو يفرّغ ما تحتجزه الوسطاء. تجاهله. - أول
chat.completion.chunkيحملdeltaفيهrole: "assistant". - مقاطع المحتوى، يحمل كل منها نص
delta.content. - مقطع أخير فيه
delta: {}وfinish_reason. ويحمل أيضًاconversation_idعند استخدامك المحادثات. - إذا ضبطت
stream_options.include_usage: مقطع واحد إضافي فيه"choices": []وكائنusage. data: [DONE].
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[{"index":0,"delta":{"role":"assistant","content":"One"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[{"index":0,"delta":{"content":", two"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[],"usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}}
data: [DONE]المقاطع ذات المصفوفة choices الفارغة بيانات وصفية (الاستخدام، وامتدادات جْبْ مثل x_geb_acceptance وx_geb_partial). وعلى المحلّل الصارم أن يتخطاها عند قراءة النص. ولا تبثّ جْبْ نص استدلالها أبدًا.
6.2 نبضات الإبقاء
في أثناء تفكير جْبْ أو فحصها للرد يبقى الاتصال مفتوحًا وتكتب جْبْ سطر تعليق، : heartbeat، كل 15 ثانية تقريبًا. وكل محلّل SSE يتجاهل التعليقات. وإذا كتبت محلّلًا خاصًا بك فتخطَّ أي سطر يبدأ بـ :.
تتضمن ترويسات الرد في البثّ x-request-id وترويسات X-Quota-* الخاصة بك (القسم 10) وترويسات الاستدلال (القسم 8). وإذا أغلقت الاتصال تتوقف جْبْ عن التوليد.
6.3 البثّ المحتجز
مع response_format أو geb_acceptance تتحقق جْبْ من الرد قبل إرساله. يُفتح البثّ بالتعليق : geb is checking the reply before sending it، ثم أسطر : heartbeat فقط، ثم المحتوى بعد التحقق. وإذا احتجت إلى الرموز لحظة توليدها فلا ترسل أيًّا من الحقلين.
6.4 كيف ينتهي البثّ
ينتهي البثّ دائمًا بـ data: [DONE]، بإحدى ثلاث طرق.
- مكتمل. مقطع أخير فيه
finish_reasonهوstop(أوtool_calls)، ثم[DONE]. - رد جزئي. إذا توقف التوليد أو فشل بعد إرسال نص بالفعل، تنهي جْبْ البثّ بما أرسلته: مقطع وصفي
{"choices":[],"x_geb_partial":{"reason":"stalled","code":"geb_dispatch_stalled","complete":false,"chars":123}}، ثم مقطع أخير فيهfinish_reason: "length"، ثم[DONE]. اعتبره إجابة ناقصة. وتكونreasonإحدى القيمstalledأوedge_deadlineأوdeadlineأوupstream_error. ولا تُعرض الردود الجزئية إلا في الاستدعاءات النصية العادية؛ أما الاستدعاء الذي يحملtoolsأوresponse_formatفيحصل على إطار الخطأ أدناه بدلًا من ذلك. - خطأ ضمن البثّ. إذا لم يُنتج شيء صالح للاستخدام، يأتي إطار
data: {"error": {...}}يليهdata: [DONE]. وكانت حالة HTTP قد أُرسلت200بالفعل، فلا يمكن أن يكون الخطأ حالة HTTP. وتُطلق حزم OpenAI SDK الرسمية خطأً عند رؤية هذا الإطار.
البثّ الذي يُغلق من دون [DONE] يعني أن الاتصال فُقد. اعتبره فشلًا وأعد المحاولة (استخدم Idempotency-Key، القسم 9.3).
المشكلات التي تستطيع جْبْ رؤيتها قبل أن تبدأ، مثل مفتاح خاطئ أو خطأ تحقق أو حصة مستنفدة، تعود بحالات خطأ HTTP حقيقية، لا بالحالة 200.
6.5 تحليل البثّ من دون SDK
import json, os, requests
r = requests.post(
"https://api.gebai.ca/v2/chat/completions",
headers={"Authorization": f"Bearer {os.environ['GEB_API_KEY']}"},
json={"stream": True, "messages": [{"role": "user", "content": "Hello"}]},
stream=True,
)
r.raise_for_status()
done = False
for raw in r.iter_lines(decode_unicode=True):
if not raw or raw.startswith(":"):
continue # blank line or keepalive comment
payload = raw[len("data:"):].strip()
if payload == "[DONE]":
done = True
break
frame = json.loads(payload)
if "error" in frame:
raise RuntimeError(frame["error"])
for choice in frame.get("choices", []):
print(choice["delta"].get("content") or "", end="")
if not done:
raise RuntimeError("stream ended without [DONE]; retry")const res = await fetch("https://api.gebai.ca/v2/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GEB_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ stream: true, messages: [{ role: "user", content: "Hello" }] }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
const decoder = new TextDecoder();
let buffer = "";
let done = false;
for await (const part of res.body) {
buffer += decoder.decode(part, { stream: true });
let i;
while ((i = buffer.indexOf("\n")) >= 0) {
const line = buffer.slice(0, i).trim();
buffer = buffer.slice(i + 1);
if (!line || line.startsWith(":")) continue;
const payload = line.slice(5).trim();
if (payload === "[DONE]") { done = true; continue; }
const frame = JSON.parse(payload);
if (frame.error) throw new Error(JSON.stringify(frame.error));
for (const c of frame.choices ?? []) process.stdout.write(c.delta?.content ?? "");
}
}
if (!done) throw new Error("stream ended without [DONE]; retry");6.6 الاستدعاءات المكتملة الطويلة
الاستدعاء غير المبثوث الذي ما زال يعمل بعد نحو 70 ثانية يتلقى ترويسات 200 مبكرًا، يتبعها حرف مسافة واحد كل 25 ثانية تقريبًا إلى أن يصل جسم JSON. والمسافة في البداية صالحة في JSON، فيقرأ JSON.parse وfetch().json() وPython requests وحزم OpenAI SDK الجسم دون تغيير. ويحمل الرد x-geb-keepalive: on عند حدوث ذلك.
إذا فشل الاستدعاء بعد إرسال تلك الترويسات تبقى الحالة 200 ويكون الجسم كائن الخطأ، لذا عند وجود x-geb-keepalive: on تحقق من وجود مفتاح error في المستوى الأعلى. أرسل ترويسة الطلب x-geb-keepalive: off لتعطيل ذلك وتحصل بدلًا منه على 504 geb_edge_deadline عند نحو 95 ثانية.
للتوليدات الطويلة، استخدم البثّ. فالرد المبثوث لا حد زمنيًا إجماليًا له ما دام يحرز تقدمًا.
7.استدعاءات الأدوات واستدعاء الدوال
هناك نوعان مختلفان من الأدوات. وهما مستقلان ويمكن إرسالهما معًا.
tools (دوالّك) | geb_tools (الأدوات المدمجة في جْبْ) | |
|---|---|---|
| دوالّ من | دوالّك أنت | دوالّ جْبْ: البحث في الويب والطقس والمستندات والشيفرة |
| من يشغّلها | أنت | جْبْ |
| الآثار الجانبية | لا شيء من جهة جْبْ | حقيقية (مثل ملف مولَّد) |
| الافتراضي | تُستخدم عند إرسالها | معطّلة |
إذا أرسلت النوعين معًا، يُعرض على النموذج دوالّك، وأي استدعاءات يعيدها تتولى أنت تنفيذها، ولا يعمل مشغّل الأدوات الخاص بجْبْ في ذلك الدور.
7.1 دوالّك (`tools`)
تصف أنت دوالّك، وتقرر جْبْ متى تستدعي إحداها، وتشغّلها أنت وترسل النتيجة. هذا بروتوكول OpenAI القياسي، فتعمل حزم SDK دون تغيير.
curl https://api.gebai.ca/v2/chat/completions \
-H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
-d '{
"parallel_tool_calls": false,
"messages": [{"role":"user","content":"What is the weather in Alexandria?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}]
}'يردّ النموذج بـ finish_reason: "tool_calls" مع ضبط message.content على null:
{
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "chatcmpl-tool-9d22d13b24ef97ed",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\": \"Alexandria\"}" }
}]
},
"finish_reason": "tool_calls"
}]
}شغّل الدالة، ثم أرسل طلبًا ثانيًا يحتوي سجلّك، ودور المساعد كما استلمته تمامًا، ورسالة tool واحدة لكل استدعاء:
import json
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "What is the weather in Alexandria?"}]
first = client.chat.completions.create(
model="geb", messages=messages, tools=tools, parallel_tool_calls=False
)
call = first.choices[0].message.tool_calls[0]
result = {"city": "Alexandria", "temp_c": 27, "sky": "clear"} # your code runs here
messages.append(first.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"name": call.function.name,
"content": json.dumps(result),
})
final = client.chat.completions.create(model="geb", messages=messages, tools=tools)
print(final.choices[0].message.content)const tools = [{
type: "function",
function: {
name: "get_weather",
description: "Current weather for a city.",
parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
},
}];
const messages = [{ role: "user", content: "What is the weather in Alexandria?" }];
const first = await client.chat.completions.create({
model: "geb", messages, tools, parallel_tool_calls: false,
});
const call = first.choices[0].message.tool_calls[0];
const result = { city: "Alexandria", temp_c: 27, sky: "clear" }; // your code runs here
messages.push(first.choices[0].message);
messages.push({
role: "tool",
tool_call_id: call.id,
name: call.function.name,
content: JSON.stringify(result),
});
const final = await client.chat.completions.create({ model: "geb", messages, tools });
console.log(final.choices[0].message.content);القواعد:
- ينبغي أن يكون
tools[].typeهو"function". ويطابقfunction.nameالنمط^[a-zA-Z0-9_-]{1,64}$ولا بد أن يكون فريدًا. وfunction.parametersكائن JSON Schema. بحد أقصى 128 أداة، و128 KiB بعد التسلسل. function.argumentsنص مرمَّز بـ JSON، في الاتجاهين. ولا بد أن تكون رسالةtoolذاتcontentنصًا: سلسل نتيجتك بنفسك.- ردّ على كل استدعاء. إذا أصدر دور المساعد ثلاثة استدعاءات فأرسل ثلاث رسائل
toolقبل الإكمال التالي. وإلا تحصل على400 unanswered_tool_callيسمّي المعرّفات الناقصة. وهذا مقصود: فعند ترك الاستدعاء بلا نتيجة قد يختلق النموذج النتائج الناقصة. tool_choice: عند عدم إرساله أو"auto"يقرر النموذج، و"none"يمنع الاستدعاءات، و"required"يفرض استدعاءً واحدًا على الأقل، و{"type":"function","function":{"name":"..."}}يسمح بتلك الدالة وحدها. ومع"required"أو دالة مسمّاة، إذا ردّ النموذج بنص عادي بدلًا من الاستدعاء، تطلب منه جْبْ مرة أخرى قبل الرد. وهذا يكلّف مرور نموذج إضافيًا من زمن الاستجابة، لا استدعاءً ثانيًا على حصتك. وإذا ظل النموذج يردّ بنص عادي، فتتلقى ذلك النص معfinish_reason: "stop"، لذا تحقق منfinish_reasonقبل أن تقرأtool_calls. ومع الاختيار المسمّى تصفّي جْبْ الرد أيضًا على تلك الدالة. وإذا احتجت إلى أقوى ضمان فاستخدم"required"مع فهرس يحتوي دالة واحدة. وفي البثّ مع"required"أو دالة مسمّاة يُحتجز النص حتى ينتهي الدور ثم يُرسل، فيصل الرد النصي العادي معًا في النهاية.- أرسل
"parallel_tool_calls": falseما لم تكن تريد عدة استدعاءات في الدور الواحد. فهو يعطي استدعاءً واحدًا نظيفًا بالضبط في كل دور. - تزيل جْبْ الاستدعاءات المكررة المتطابقة بايتًا ببايت، وتُسقط الاستدعاء الأخير إذا قُطع بسبب
length. وتُمرَّر الوسائط فيما عدا ذلك كما هي حرفيًا، بما فيها الوسائط غير الصالحة بصفتها JSON، فتحقق منها قبل الاستخدام. - البثّ مدعوم. تصل استدعاءات الأدوات بإطارات
delta.tool_callsالقياسية (إطار افتتاحي فيهidوname، ثم أجزاءarguments، تُجمَّع حسبindex). وتصل معًا في نهاية الدور، لا متداخلة مع التوليد. والناتج المجمَّع مطابق لبثّ OpenAI. - رحلة الأداة ذهابًا وإيابًا طلبان، ويُحتسب كل منهما استدعاءً.
7.2 الأدوات المدمجة في جْبْ (`geb_tools`)
افتراضيًا يكون استدعاء API استدعاءً عاديًا للنموذج: توليد واحد، بلا أدوات، وبلا آثار جانبية. وفعّل الأدوات عبر geb_tools.
| القيمة | السلوك |
|---|---|
غائبة، false، [] | لا أدوات من جْبْ. هذا هو الافتراضي. |
true | كل أداة من جْبْ متاحة الآن. ويقرر النموذج متى يستخدمها. |
["web_search", "weather_forecast"] | الأدوات المسمّاة فقط. يُعرض على النموذج هذه الأدوات بالضبط، ويُرفض استدعاء أي أداة أخرى. |
curl https://api.gebai.ca/v2/chat/completions \
-H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
-d '{
"messages": [{"role":"user","content":"What did the ECB decide at its last meeting?"}],
"geb_tools": ["web_search"]
}'r = client.chat.completions.create(
model="geb",
messages=[{"role": "user", "content": "Weather in Cairo this weekend?"}],
extra_body={"geb_tools": ["weather_forecast"]}, # the SDK merges extra_body into the request body
)const r = await client.chat.completions.create({
model: "geb",
messages: [{ role: "user", content: "Weather in Cairo this weekend?" }],
geb_tools: ["weather_forecast"], // TypeScript: add // @ts-expect-error, the SDK types do not know this field
});أسماء الأدوات العامة (القائمة الحية لاستدعائك هي geb_tools في GET /v2/models):
| الاسم | ما تفعله |
|---|---|
web_search | بحث في الويب: عناوين وروابط ومقتطفات. |
web_grounded_search | بحث في الويب يقرأ أيضًا أهم الصفحات، لإجابات موثَّقة بمراجع. |
url_fetch | يجلب عنوان URL واحدًا ويقرؤه. |
polinex_news | أخبار حديثة من تغذية Polinex للدول التي تغطيها. |
weather_forecast | الأحوال الحالية والتوقعات لمكان ما. |
currency_convert | تحويل العملات الحي. |
wikipedia_summary | ملخص مقالة من Wikipedia. |
places_search | الأماكن والأعمال التجارية قرب موقع. |
geocode | من العنوان إلى الإحداثيات والعكس. |
directions | مسار بين أماكن. |
distance_matrix | المسافة وزمن السفر بين عدة نقاط. |
static_map | صورة خريطة لموقع. |
code_execute | يشغّل Python في بيئة معزولة. وتُعاد الملفات التي يكتبها كروابط. |
generate_text_file | ينتج ملفًا نصيًا (md وcsv وjson وhtml وغيرها) ويعيد رابط تنزيل. |
build_document | ينشئ مستند Word (.docx) ويعيد رابط تنزيل. |
build_workbook | ينشئ مصنّف Excel (.xlsx) ويعيد رابط تنزيل. |
build_slides | ينشئ عرض PowerPoint (.pptx) ويعيد رابط تنزيل. |
- الاسم الذي لا تعرفه جْبْ، أو المعطّل، يعيد
400 invalid_geb_toolsمع قائمة الأسماء الصالحة فيerror.valid_geb_tools. بحد أقصى 32 اسمًا. - مع القائمة يُفرض التقييد مرتين: لا يرى النموذج إلا الأدوات المسمّاة، ولا يُنفَّذ أي استدعاء لأداة أخرى (يتلقى النموذج خطأ أداة ويكمل رده دونها).
- الأدوات الثلاث
build_*منفصلة عمدًا. فتسميةbuild_documentتسمح بملف Word فقط. - يبدأ بناء المستندات من نص تعليمتك أنت، لا من كلمات صيغ الملفات داخل محتوى لصقته. فإذا كان الذكر الوحيد لـ «xlsx» أو «slides» داخل كتلة شيفرة أو نص ملصوق طويل، فإن جْبْ تردّ نصًا.
- قد تستغرق الأدوات وقتًا أطول من الإكمال العادي. وتُخزَّن الملفات التي تنتجها الأدوات تحت حسابك وتُعاد كروابط محدودة المدة.
- ذاكرة حسابك وسجل محادثاتك والموصّلات (Google وMicrosoft وDropbox وغيرها) وأدوات الحساب الأخرى غير متاحة على الواجهة.
8.الاستدلال
تفكر جْبْ دائمًا قبل أن تردّ. وأنت تختار مقدار الجهد. لا تعيد جْبْ نص استدلالها؛ تتلقى الإجابة، وتُحتسب رموز التفكير ضمن usage.completion_tokens.
8.1 المستويات
| المستوى | المعنى |
|---|---|
low | أقصر تفكير. |
medium | الافتراضي للأسئلة الأصعب. |
high | تفكير طويل. يقابل أعلى إعداد في النموذج. |
ultra | أعلى إعداد في النموذج. |
يعرض GET /v2/models المستويات (reasoning.efforts) وافتراضي الخادم (reasoning.default_effort).
8.2 معنى «إيقاف الاستدلال»
لا يُوقَف التفكير أبدًا. كل صيغ «الإيقاف» ("off" و"none" وfalse و"disabled" و0) تُبقي التفكير مفعّلًا مع ميزانية تفكير قدرها صفر رمز: يغلق النموذج استدلاله فورًا ويبدأ الإجابة. فتحصل على إجابة عادية بلا خطوة استدلال. استخدمه للطلبات البسيطة الحساسة لزمن الاستجابة. وللمسائل الصعبة فضّل medium أو high.
8.3 كيف تطلب
استخدم أي صيغة يرسلها عميلك أصلًا. لا يُرفض أي منها أبدًا.
| المعامل | أمثلة |
|---|---|
reasoning | "high", "off", "on", {"effort": "medium"} |
reasoning_effort | "low", "medium", "high", "ultra", "minimal", "none", "xhigh" |
thinking | {"type": "enabled", "budget_tokens": 3000}, {"type": "disabled"}, true |
enable_thinking | true, false |
chat_template_kwargs | {"enable_thinking": false}, {"reasoning_effort": "high"} |
extra_body | {"reasoning_effort": "medium"}، للعملاء الذين يرسلون extra_body حرفيًا |
التحويلات: minimal تصبح low. وxhigh وmax وmaximum تصبح ultra. وقيمة budget_tokens تُحوَّل إلى أقرب مستوى (حتى 1024 هي low، وحتى 2048 هي medium، وحتى 4096 هي high، وما فوق ذلك ultra). والمستوى المجهول (مثل "turbo") يستخدم افتراضي الخادم.
إذا أرسلت عدة صيغ متعارضة، فتفوز أول صيغة في هذه القائمة تسمّي مستوى: reasoning وreasoning_effort وthinking وenable_thinking وchat_template_kwargs وextra_body. والصيغة التي تفعّل التفكير فقط لا تتجاوز أبدًا مستوى مسمًّى في موضع آخر.
curl https://api.gebai.ca/v2/chat/completions \
-H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
-d '{"reasoning_effort":"high","messages":[{"role":"user","content":"Plan a 3-city sales route to minimise travel."}]}' -ir = client.chat.completions.create(
model="geb",
messages=[{"role": "user", "content": "Plan a 3-city sales route to minimise travel."}],
reasoning_effort="high",
)
# "ultra" is not in the SDK's typed list, send it through extra_body:
r = client.chat.completions.create(
model="geb",
messages=[{"role": "user", "content": "Prove that the square root of 2 is irrational."}],
extra_body={"reasoning": "ultra"},
)const r = await client.chat.completions.create({
model: "geb",
messages: [{ role: "user", content: "Plan a 3-city sales route to minimise travel." }],
reasoning_effort: "high",
});8.4 ما يعود إليك
| ترويسة الرد | القيمة |
|---|---|
x-geb-reasoning-effort | المستوى المستخدم، أو default عندما لا ترسل شيئًا. |
x-geb-reasoning-adjusted | تظهر عندما حُوّلت قيمة أو تُجوهلت، مثل enable_thinking=false->budget 0 (thinking stays on; think block closed). |
8.5 max_tokens والتفكير
max_tokens هو السقف على الإجابة المرئية. وتُحتسب ميزانية التفكير بشكل منفصل فوقه، فلا يُضعف max_tokens الصغير الاستدلال، ولا تضرب جْبْ رقمك أبدًا. وإذا بلغت الإجابة السقف قُطعت عنده وكانت finish_reason هي length. وإذا لم تضبط max_tokens اشتُقّت ميزانية الإجابة من نافذة السياق. ويشمل usage.completion_tokens رموز التفكير.
9.الأخطاء وإعادة المحاولة
9.1 الغلاف
كل رد خطأ من مسار /v2 (ومن اسمه البديل /v1) هو JSON بشكل واحد:
{
"error": {
"message": "A human-readable explanation.",
"type": "invalid_request_error",
"param": "messages",
"code": "invalid_json_body",
"request_id": "0f658b6d9742d46fec9ce4b984f99667"
}
}typeهو فئة الخطأ. تفرّع علىtypeوcode، لا على صياغةmessageأبدًا.paramيسمّي الحقل المخالف عندما يكون هناك حقل مسؤول، وإلا فهوnull.codeسبب أكثر تحديدًا مقروء آليًا، أوnull.request_idموجود دائمًا ويُرسل أيضًا كترويسة الردx-request-id. ويحمل كل ردx-request-id، ومنها الردود الناجحة. اذكره عند التواصل مع الدعم. يمكنك إرسالx-request-idخاص بك إن شئت؛ وقد تعيد جْبْ مع ذلك معرّف التتبع الخاص بها.- تضيف بعض الأخطاء
retry_after_msفي المستوى الأعلى بجانبerror، وRetry-Afterكترويسة (بالثواني). - الإخفاقات بعد بدء البثّ تصل ضمن البثّ (القسم 6.4). والإخفاقات بعد بدء استدعاء مكتمل طويل تصل كجسم
200فيه مفتاحerror(القسم 6.6). - الطلب إلى مسار غير موجود يعيد 404 عاديًا بصيغة
{"message", "error", "statusCode"}، لا هذا الغلاف. وإذا رأيته فتحقق من العنوان.
9.2 الفهرس
| HTTP | type | code | المعنى وما ينبغي فعله |
|---|---|---|---|
| 400 | invalid_request_error | null | فشل تحقق: خاصية مجهولة، أو نوع خاطئ، أو messages فارغة، أو حقل غير مدعوم (n وlogprobs وfunctions)، أو رسالة أكبر من اللازم. أصلح الطلب. |
| 400 | invalid_request_error | invalid_json_body | الجسم ليس JSON صارمًا. |
| 400 | invalid_request_error | invalid_content_encoding | الجسم ليس gzip أو deflate صالحًا بالنسبة إلى Content-Encoding. |
| 400 | invalid_request_error | invalid_geb_tools | قيمة geb_tools خاطئة. يسرد error.valid_geb_tools الأسماء المقبولة. |
| 400 | invalid_request_error | invalid_geb_acceptance | قيمة geb_acceptance خاطئة. يحدد param وrule_index موضع القاعدة. |
| 400 | invalid_request_error | memory_unavailable, invalid_geb_memory | geb_memory: true، أو قيمة غير منطقية. لا ذاكرة في استدعاءات API. |
| 400 | invalid_request_error | unsupported_tool_choice_mode, unknown_tool, duplicate_tool_name, too_many_tools, tools_too_large, invalid_type, invalid_value, missing_required_parameter | مشكلة في tools أو tool_choice. يشير param إلى المسار، مثل tools[2].function.name. |
| 400 | invalid_request_error | unknown_tool_call_id, unanswered_tool_call | رسالة tool لا تطابق استدعاءً سابقًا، أو استدعاء بلا نتيجة. |
| 400 | invalid_request_error | invalid_content, unsupported_content_part, invalid_image_url, unsupported_image_type, invalid_image, too_many_images, image_too_large, images_too_large, image_url_forbidden, image_fetch_failed, image_fetch_timeout | مشكلة في صورة أو جزء محتوى (القسم 5.4). |
| 401 | invalid_api_key | null | مفتاح مفقود أو مشوَّه أو مجهول أو ملغى أو منتهي. لا تعد المحاولة. |
| 403 | permission_error | null | المفتاح صالح لكن الحساب غير مسموح له باستخدام الواجهة. لا تعد المحاولة. |
| 404 | not_found_error | null | معرّف نموذج مجهول عند الاسترجاع، أو محادثة لا تخصك. |
| 409 | idempotency_key_reuse, idempotent_request_in_progress | الاسم نفسه الذي في type | Idempotency-Key نفسه مع جسم مختلف، أو أن الاستدعاء الأول ما زال يعمل (القسم 9.3). |
| 413 | invalid_request_error | request_too_large | الجسم بعد فك الضغط يتجاوز حد 32 MiB. |
| 415 | invalid_request_error | unsupported_content_encoding | Content-Encoding غير gzip أو deflate. |
| 422 | invalid_request_error | geb_acceptance_failed | on_fail: "error_422" وما زال الرد يخالف قواعدك. لا يُحتسب على حصتك. |
| 429 | rate_limit_exceeded | null | حد الاندفاع في الدقيقة. انتظر Retry-After ثانية. |
| 429 | rate_limit_error | geb_concurrency_limit | عدد كبير من الطلبات الجارية في وقت واحد لحسابك. انتظر Retry-After، أو انتظر انتهاء أحدها. |
| 429 | quota_exceeded | null | استُنفدت حصة الاستدعاءات الشهرية. الحل الوحيد هو انتظار إعادة الضبط، أو تغيير الباقة. |
| 502 | server_error | empty_answer | أنهى النموذج دون إجابة. أعد المحاولة فورًا، فالطلب نفسه ينجح عادة. وقيمة retry_after_ms هي 0. |
| 503 | server_busy | null أو maintenance | جْبْ مشغولة أو غير متاحة لوقت قصير. انتظر Retry-After (نحو 5 ثوانٍ عند الانشغال، وحتى 120 عند الصيانة). |
| 503 | server_error | vision_unavailable | نموذج الرؤية غير متاح الآن. أعد المحاولة لاحقًا أو أرسل بلا صور. |
| 503 | server_error | degenerate_output | قُطع الرد لأسباب تتعلق بالجودة ولم تتوفر إعادة محاولة نظيفة. أعد المحاولة. |
| 503 | server_error | geb_instance_shutdown | نسخة الخادم قيد إعادة التشغيل. أعد المحاولة فورًا. |
| 504 | server_error | geb_edge_deadline, geb_dispatch_stalled, geb_dispatch_deadline | استغرق الطلب وقتًا طويلًا أو لم يحرز تقدمًا. أعد المحاولة مع "stream": true. |
| 5xx | server_error | null | أي حالة أخرى. الرسالة عامة عمدًا. أعد المحاولة مع تراجع تدريجي، واذكر request_id إذا استمرت المشكلة. |
في البثّ تصل الأخطاء نفسها كإطار الخطأ ضمن البثّ، إضافة إلى geb_incomplete_stream (انتهى التوليد قبل اكتماله؛ أعد المحاولة).
9.3 إرشادات إعادة المحاولة
- لا تعد المحاولة أبدًا مع
400و401و403و404و409 idempotency_key_reuseو413و415و422. فلا بد من تغيير الطلب. - أعد المحاولة بعد الانتظار مع
429 rate_limit_exceededوgeb_concurrency_limit(استخدمRetry-After)، و503 server_busy. - لا تُلحّ على
429 quota_exceeded. اقرأreset_atفي الجسم أوX-Quota-Reset، وتوقف حتى ذلك الحين. - أعد المحاولة فورًا، مرة أو مرتين مع
502 empty_answerو503 geb_instance_shutdown. - انتقل إلى البثّ ثم أعد المحاولة مع
504. - أضف تراجعًا أسيًّا مع تشويش عشوائي لسائر حالات
5xx، بحد أقصى بضع محاولات. - أرسل دائمًا
Idempotency-Keyفي الاستدعاءات التي قد تعيد محاولتها. وهو أي نص حتى 255 حرفًا، فريد لكل طلب منطقي. يعمل الاستدعاء الأول عادةً. والتكرار بالمفتاح نفسه والجسم نفسه يعيد الإجابة المخزَّنة (Idempotency-Replayed: true)، دون توليد جديد ودون استهلاك حصة، لمدة 24 ساعة. وللإعادة المحتوى والحقول والقيم نفسها كما في الإجابة الأولى؛ وقد يختلف ترتيب الحقول داخل كائن JSON، لذا قارن JSON بعد تحليله، لا البايتات. والمفتاح نفسه مع جسم مختلف هو409 idempotency_key_reuse. والتكرار والاستدعاء الأول ما زال يعمل هو409 idempotent_request_in_progress: انتظر ولا تدُر في حلقة. والبثّ الذي فشل في منتصفه لا يُخزَّن، فتعمل إعادة المحاولة من جديد. وتذكر ترويسةIdempotency-Statusإحدى القيمstoredأوreplayedأوdisabled. - اضبط مهلة عميلك بسخاء، أو استخدم البثّ. ترسل حزم OpenAI SDK مهلتها كترويسة، وتعيد جْبْ أفضل إجابة جزئية (
finish_reason: "length"،x_geb_partial) بدلًا من لا شيء عندما تقترب مهلتك أنت.
ينتهي كل استدعاء بواحد من ثلاثة أشياء: الإجابة، أو إجابة جزئية موسومة بـ finish_reason: "length" مع x_geb_partial، أو خطأ منظَّم. وهكذا تبدو الإجابة الجزئية غير المبثوثة:
{
"choices": [{"index": 0, "message": {"role": "assistant", "content": "..."}, "finish_reason": "length"}],
"x_geb_partial": {"reason": "stalled", "code": "geb_dispatch_stalled", "complete": false, "chars": 812}
}9.4 مساعد إعادة المحاولة
import os, random, time, uuid, requests
URL = "https://api.gebai.ca/v2/chat/completions"
HEADERS = {"Authorization": f"Bearer {os.environ['GEB_API_KEY']}"}
def complete(body, attempts=5):
key = str(uuid.uuid4()) # same key on every attempt
for n in range(attempts):
r = requests.post(URL, json=body, headers={**HEADERS, "Idempotency-Key": key}, timeout=620)
if r.ok:
data = r.json()
if "error" in data: # error after a keepalive commit
raise RuntimeError(data["error"])
return data
err = r.json().get("error", {})
code, kind = err.get("code"), err.get("type")
if r.status_code in (400, 401, 403, 404, 413, 415, 422) or kind == "quota_exceeded":
raise RuntimeError(f"{r.status_code} {kind}/{code}: {err.get('message')} ({err.get('request_id')})")
if code in ("empty_answer", "geb_instance_shutdown"):
wait = 0
elif r.status_code == 504:
raise RuntimeError("timed out: retry with stream=True")
else:
wait = float(r.headers.get("Retry-After", 2 ** n)) + random.random()
time.sleep(wait)
raise RuntimeError("gave up after retries")import { randomUUID } from "node:crypto";
export async function complete(body, attempts = 5) {
const key = randomUUID(); // same key on every attempt
for (let n = 0; n < attempts; n++) {
const res = await fetch("https://api.gebai.ca/v2/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GEB_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: JSON.stringify(body),
});
const data = await res.json();
if (res.ok && !data.error) return data;
const err = data.error ?? {};
if ([400, 401, 403, 404, 413, 415, 422].includes(res.status) || err.type === "quota_exceeded")
throw new Error(`${res.status} ${err.type}/${err.code}: ${err.message} (${err.request_id})`);
if (res.status === 504) throw new Error("timed out: retry with stream: true");
const wait = ["empty_answer", "geb_instance_shutdown"].includes(err.code)
? 0
: (Number(res.headers.get("Retry-After")) || 2 ** n) * 1000 + Math.random() * 1000;
await new Promise((r) => setTimeout(r, wait));
}
throw new Error("gave up after retries");
}10.الحدود والحصص
10.1 الحصة الشهرية للاستدعاءات
كل باقة تتضمن الواجهة، ومنها المجانية، بعدد من استدعاءات API في كل فترة فوترة. لا تُحتسب الرموز ولا توجد رسوم لكل رمز.
| الباقة | الاستدعاءات في الفترة |
|---|---|
| المجانية | 500 |
| الأساسية | 1,500 |
| المحترف | 10,000 |
| الأستاذ | 100,000 |
| المطوّر | غير محدودة |
- الفترة هي فترة فوترة اشتراكك، أو الشهر التقويمي (UTC) إن لم يكن لديك اشتراك.
- الاستدعاءات الناجحة فقط تُحتسب. والاستدعاء الذي يفشل لا يستهلك حصة. وإعادة الرد المخزَّن من
Idempotency-Keyلا تستهلك حصة. وتُحتسب مسارات المحادثات استدعاءات. ورحلة الأداة ذهابًا وإيابًا استدعاءان. - عند تجاوز الحصة تردّ الواجهة بـ
429 quota_exceededمع التفاصيل:
{
"error": {
"type": "quota_exceeded",
"message": "Monthly API request quota reached: your Free plan includes 500 calls per billing period and 500 have been used. The quota resets at 2026-11-01T00:00:00.000Z.",
"tier": "free",
"limit": 500,
"used": 500,
"remaining": 0,
"reset_at": "2026-11-01T00:00:00.000Z",
"request_id": "..."
}
}يحمل كل رد ناجح موقعك في الترويسات: X-Quota-Limit-Month وX-Quota-Remaining-Month وX-Quota-Reset. ويحملها أيضًا 429 الخاص بالحصة، مع ضبط Retry-After على الثواني حتى إعادة الضبط.
curl -s -D - -o /dev/null https://api.gebai.ca/v2/models -H "Authorization: Bearer $GEB_API_KEY" | grep -i "x-quota\|x-ratelimit"r = requests.post(URL, json=body, headers=HEADERS)
print(r.headers.get("X-Quota-Remaining-Month"), "calls left until", r.headers.get("X-Quota-Reset"))const res = await fetch(url, { method: "POST", headers, body });
console.log(res.headers.get("X-Quota-Remaining-Month"), "calls left until", res.headers.get("X-Quota-Reset"));10.2 حدود المعدّل
| الحد | القيمة |
|---|---|
| الطلبات في الدقيقة (اندفاع)، لكل حساب | 120 في كل باقة: المجانية والأساسية والمحترف والأستاذ والمطوّر. تُحتسب عبر جميع مفاتيح الحساب. |
| الطلبات المتزامنة، لكل حساب | حصة عادلة من السعة المتاحة في تلك اللحظة. وعند تجاوزها: 429 geb_concurrency_limit. |
يُقاس استخدام API بعدد الاستدعاءات الشهري (القسم 10.1). وحد الدقيقة موجود فقط لمنع الاندفاعات وإساءة الاستخدام، لذلك هو نفسه في كل باقة. ويُرسل كـ X-RateLimit-Limit. ويحمل 429 الخاص بالاندفاع Retry-After. وزّع الاندفاعات، أو استخدم البثّ بطلبات أقل وأطول.
10.3 حدود الطلب
| الحد | القيمة |
|---|---|
| جسم الطلب | 32 MiB بعد فك الضغط. gzip وdeflate مقبولان. |
| الرسائل وحجم الرسالة | محدودة بنافذة سياق النموذج (context_window في GET /v2/models). وتجاوز الحد هو 400 يسمّيه. |
| الصور | 16 لكل طلب، و20 MB لكل صورة، و64 MB مفكوكة في المجموع. |
أدواتك (tools) | 128 دالة، و128 KiB بعد التسلسل. |
geb_tools | 32 اسمًا. |
Idempotency-Key | 255 حرفًا، تُحفظ 24 ساعة. |
| المفاتيح النشطة لكل حساب | 10. |
| المحادثات المخزَّنة | بلا حد، وتُحفظ حتى تحذفها. |
10.4 الزمن
لا حد زمنيًا إجماليًا للبثّ ما دام النموذج يحرز تقدمًا. والاستدعاء غير المبثوث يُبقى حيًّا بعد مهلة الحافة (القسم 6.6). والاستدعاء الذي لا يحرز تقدمًا لوقت طويل جدًا ينتهي بإجابة جزئية أو 504 بدلًا من أن يعلق.
11.التوافق مع OpenAI SDK
تعمل حزمتا OpenAI لـ Python وJavaScript بتغييرين: عنوان الأساس والمفتاح.
from openai import OpenAI
client = OpenAI(base_url="https://api.gebai.ca/v2", api_key="geb_live_...")import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.gebai.ca/v2", apiKey: "geb_live_..." });# Anything that reads the standard environment variables also works:
export OPENAI_BASE_URL="https://api.gebai.ca/v2"
export OPENAI_API_KEY="geb_live_..."ما يعمل بالطريقة نفسها: chat.completions.create (مكتملًا ومبثوثًا) وmodels.list وmodels.retrieve واستدعاء الدوال وresponse_format وstream_options.include_usage وأجزاء محتوى الصور وأخطاء بصيغة OpenAI مع Retry-After.
فروق ينبغي التخطيط لها:
- إكمال المحادثة فقط. الاستدعاءات إلى التضمينات أو الصوت أو الصور أو الملفات أو الدُّفعات أو واجهة Responses غير موجودة.
- الحقول المجهولة تعيد 400. مرّر فقط الحقول المذكورة في القسم 5.2. وعلى وجه الخصوص استخدم
max_tokensلاmax_completion_tokens، ولا ترسلstoreأوmetadata. - حقول جْبْ الخاصة (
geb_toolsوgeb_acceptanceوconversation_idوlocale) تمرّ عبرextra_bodyفي Python (تدمجه الحزمة في جسم الطلب). وفي JavaScript مرّرها كخصائص إضافية. - حقول أخذ العينات متجاهلة.
temperatureوtop_pوstopوseedوالعقوبات مقبولة وبلا أثر. - نموذج واحد. لا يختار
modelشيئًا. استخدمclient.models.list()لترى ما يُقدَّم. - مستويات الاستدلال. يقبل
reasoning_effortالقيمlowوmediumوhigh، وكذلكultra(أرسله عبرextra_bodyإذا كانت نسخة الحزمة لديك تحدد نوع الحقل). - الاستخدام من جهة الخادم. لا تستدعِ الواجهة من متصفح على نطاقك الخاص.
- حقول إضافية في الرد. قد تحمل الردود حقول جْبْ مثل
conversation_idوx_geb_*. وهي إضافية فقط. - إعادة المحاولة. تعيد الحزم المحاولة تلقائيًا عند
429و5xx. أضفIdempotency-Keyعبرextra_headersحتى لا يُشغَّل الاستدعاء المعاد ولا يُحتسب مرتين:
client.chat.completions.create(
model="geb",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={"Idempotency-Key": "order-7421-reply-1"},
)await client.chat.completions.create(
{ model: "geb", messages: [{ role: "user", content: "Hello" }] },
{ headers: { "Idempotency-Key": "order-7421-reply-1" } },
);12.التعامل مع البيانات
- لا ذاكرة حساب. لا تقرأ استدعاءات API من ذاكرة حساب جْبْ الخاص بك ولا تكتب إليها. وهذا يسري على كل مفتاح وكل باقة.
- المحادثات المخزَّنة لا توجد إلا إذا استخدمت
conversation_id. وهي منفصلة عن الذاكرة، ولا يقرؤها إلا حسابك، وتُحذف عند حذفك المحادثة. - سجلات API. يُسجَّل كل استدعاء لأغراض التشغيل والدعم ومنع إساءة الاستخدام: محتوى الطلب والرد، والنموذج، والمعاملات، وأعداد الرموز، وزمن الاستجابة، والحالة، ووكيل المستخدم، ومعرّف المفتاح، وبلد المتصل. وتُحجب بيانات الاعتماد والأسرار الموجودة في المحتوى (المفاتيح والرموز وكلمات المرور والروابط الموقَّعة) قبل التخزين. وتُخزَّن الصور المضمَّنة بحجمها وبصمتها فقط. كما تظهر نسخة من كل استدعاء لك، ويمكنك حذفها، ضمن بياناتك في تطبيق جْبْ. ويُحتفظ بالسجل التشغيلي طوال عمر الحساب ويُحذف عند حذف الحساب. ولا تُستخدم سجلات API أبدًا كذاكرة.
- الأدوات والملفات المولَّدة تُخزَّن تحت حسابك وتُعاد كروابط محدودة المدة.
لا ترسل بيانات لا يُسمح لك بإرسالها إلى خدمة طرف ثالث. وإذا احتجت إلى اتفاقية معالجة بيانات فتواصل مع جْبْ قبل الإطلاق.
13.الإصدار والدعم
واجهة جْبْ API v2. تُقدَّم على https://api.gebai.ca/v2، مع بقاء /v1 اسمًا بديلًا للعملاء الحاليين. مراجعة الدليل 1.1، 28 أكتوبر 2026.
يصف هذا الدليل سلوك الواجهة الحية في ذلك التاريخ. وقد تُعتمد الحقول المعلَّمة بأنها متجاهلة لاحقًا؛ وقد تظهر حقول اختيارية جديدة في الردود. عامل الحقول المجهولة في الرد على أنها إضافية.
عندما تحتاج إلى مساعدة فأرفق request_id من جسم الخطأ (أو ترويسة x-request-id)، ووقت الاستدعاء، وشكل الطلب دون المفتاح. وتواصل مع جْبْ عبر https://gebai.ca/contact.