# استخدم حزمة OpenAI

> وجّه حزم OpenAI الرسمية في Python وJavaScript إلى K-Agent — معرّف الوكيل هو اسم النموذج — مع الجلسات والبث والعملاء وامتداد kagent.

يتحدث K-Agent صيغة OpenAI Chat Completions على المسار `/openai/v1`. احتفظ بحزمة OpenAI والكود الذي لديك، وغيّر إعدادين، فيجيب وكيلك عن كل طلب — بمعرفته وأدواته وضوابطه وتحويلاته.

| الإعداد | القيمة |
|---|---|
| عنوان الأساس (Base URL) | `https://api.k-agent.kerneltics.com/openai/v1` |
| مفتاح API | **المفتاح السري** لـ K-Agent ‏(`kt_sk_…`) |
| `model` | المعرّف النصي للوكيل (`store-assistant`) أو معرّفه (`agt_…`) |

## أول طلب

**Python**

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.k-agent.kerneltics.com/openai/v1",
    api_key=os.environ["KAGENT_API_KEY"],
)

completion = client.chat.completions.create(
    model="store-assistant",
    messages=[{"role": "user", "content": "كم رسوم التوصيل؟"}],
)
print(completion.choices[0].message.content)
print(completion.model_extra["kagent"])  # {"run_id": "run_…", "session": None, "handoff": None, "units": 0.25}
```

**JavaScript**

```js
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://api.k-agent.kerneltics.com/openai/v1',
  apiKey: process.env.KAGENT_API_KEY,
});

const completion = await client.chat.completions.create({
  model: 'store-assistant',
  messages: [{ role: 'user', content: 'كم رسوم التوصيل؟' }],
});
console.log(completion.choices[0].message.content);
console.log(completion.kagent); // { run_id: 'run_…', session: null, handoff: null, units: 0.25 }
```

يعيد `client.models.list()` وكلاءك، ومعرّفاتهم النصية أسماءً للنماذج.

## بلا حالة أو داخل جلسة

**دون جلسة** (الافتراضي) يكون كل طلب مستقلًا، مثل [`ask`](/docs/guides/one-shot/):

- **آخر رسالة من المستخدم** هي المدخل؛ والرسائل السابقة تُمرَّر إلى الوكيل سجلًا من المستدعي غير موثَّق؛
- رسائل `system` أو `developer` في البداية تعيد `400 system_message_not_allowed` — فتعليمات الوكيل نفسه هي السارية. وإذا سمح الوكيل بتجاوز `instructions_append` تُستخدم تعليماتٍ إضافية لذلك الطلب بدلًا من ذلك؛
- يُفوتر مثل `ask`.

**داخل جلسة** يحتفظ K-Agent بالسجل وترسل أنت الرسالة الجديدة فقط. سمِّ الجلسة بحقل `session` في المستوى الأعلى (`extra_body` في Python) أو بالترويسة `x-kagent-session`. وهو جلب أو إنشاء، مثل `POST /v1/sessions`، ويقبل معرّف `sess_` أو معرّفك `external_id`:

**Python**

```python
completion = client.chat.completions.create(
    model="store-assistant",
    messages=[{"role": "user", "content": "وين طلبي؟"}],
    user="cus_1042",                      # your end user's ID: verified, since this is your server
    extra_body={"session": "order-8812"},  # get-or-create the session
)
```

**JavaScript**

```js
const completion = await client.chat.completions.create(
  {
    model: 'store-assistant',
    messages: [{ role: 'user', content: 'وين طلبي؟' }],
    user: 'cus_1042', // your end user's ID: verified, since this is your server
  },
  { headers: { 'x-kagent-session': 'order-8812' } }, // get-or-create the session
);
```

- السجل المحفوظ هو المرجع. لا يقرأ K-Agent إلا الرسائل الواقعة بعد آخر رسالة `assistant` في طلبك؛ وإذا تجاهل رسائل أقدم تتضمن الاستجابة `"client_history_ignored"` في `kagent.warnings`.
- استخدام الجلسة نفسها مع وكيل مختلف يعيد `409 session_agent_mismatch`.
- ما دامت المحادثة بيد فريقك (وضع الموظف) تكون الاستجابة `200` مع `content` فارغ و`finish_reason: "stop"` و`kagent.mode: "human"`.

## البث

`stream: true` يعيد سلسلة الأجزاء المعتادة. وفي K-Agent تفصيل واحد: قبل `data: [DONE]` مباشرة يأتي **جزء أخير بمصفوفة `choices` فارغة** يحمل كائن `kagent`. تحقّق منه:

**Python**

```python
stream = client.chat.completions.create(
    model="store-assistant",
    messages=[{"role": "user", "content": "أقدر أغير عنوان التوصيل؟"}],
    extra_body={"session": "order-8812"},
    stream=True,
)
for chunk in stream:
    if chunk.choices:
        print(chunk.choices[0].delta.content or "", end="", flush=True)
    else:
        kagent = chunk.model_extra["kagent"]  # run_id, session, handoff, units
```

**JavaScript**

```js
const stream = await client.chat.completions.create(
  { model: 'store-assistant', messages: [{ role: 'user', content: 'أقدر أغير عنوان التوصيل؟' }], stream: true },
  { headers: { 'x-kagent-session': 'order-8812' } },
);
for await (const chunk of stream) {
  if (chunk.choices.length) process.stdout.write(chunk.choices[0].delta.content ?? '');
  else console.log(chunk.kagent); // run_id, session, handoff, units
}
```

حين يكون رد التصعيد مُعدًّا يُبث النص جولةً جولة، فلا يناقض ما تعرضه الردَّ الذي تفرضه سياستك.

## امتداد `kagent`

تحمل كل استجابة كائن `kagent` بجانب الحقول القياسية (في TypeScript اقرأه بالصيغة `(completion as any).kagent`):

| الحقل | الوصف |
|---|---|
| `run_id` | تشغيل K-Agent. اجلب تتبّعه عبر `GET /v1/runs/{run}/steps`. |
| `session` | الجلسة، إن سمّيت واحدة. |
| `handoff` | `null`، أو التحويل الذي أجراه الوكيل (`{id, reason_type, summary, status}`). |
| `units` | وحدات المحادثات الذكية التي استهلكها هذا الطلب. |
| `mode` و`warnings` | تظهر عند الحاجة، مثل `"human"` و`["client_history_ignored"]`. |

ويجمع `usage` كل استدعاءات النموذج التي أجراها الوكيل للرد.

## الأدوات

- أدوات الوكيل الخاصة (المدمجة وأدوات HTTP) تعمل داخل K-Agent؛ ولا تراها `tool_calls`.
- لاستخدام **أدوات جهة العميل** عرّفها في الوكيل، ثم مرّر `tools` بالأسماء نفسها في طلب **بلا حالة**. وحين يستدعي النموذج إحداها تحصل على `finish_reason: "tool_calls"` كالمعتاد؛ وأرسل النتيجة رسالةً بدور `tool` في طلبك التالي، وهو تشغيل جديد بلا حالة.
- اسم أداة لا يعرّفه الوكيل يعيد `400 tool_not_declared`، وإرسال `tools` مع جلسة يعيد `400 tools_not_allowed_with_session`. وفي الجلسات استخدم [مسار أدوات جهة العميل الأصلي](/docs/guides/client-tools/).

## المعاملات

| المعامل | السلوك |
|---|---|
| `model` | المعرّف النصي للوكيل أو معرّفه. |
| `messages` | حتى 100. انظر «بلا حالة أو داخل جلسة» أعلاه. |
| `user` | يُربط بـ `external_id` للعميل، موثّقًا. |
| `stream` | مدعوم. |
| `temperature` | يُستخدم فقط إذا سمح الوكيل بتجاوز `temperature`. |
| `max_tokens` و`max_completion_tokens` | تُحدّ بقيمة `max_reply_tokens` في الوكيل. |
| `n` أكبر من 1، و`logprobs`، و`response_format` مع `json_schema` | `400 unsupported_parameter`. |
| أي شيء آخر | يُتجاهل. |

تأتي الأخطاء بصيغة OpenAI — ‏`{"error": {"message", "type", "param", "code"}}` — مع [رموز أخطاء](/docs/reference/errors/) K-Agent الثابتة. ولا تعمل هنا إلا المفاتيح السرية، ولا تُرسل ترويسات CORS: استدعِ هذه الواجهة من خادمك.
