# واجهة برمجة تطبيقات صور Gemini

> استخدم نقطة نهاية Gemini v1beta الأصلية من LMU AI للتحويل من النص إلى صورة، وتحرير الصور، والتحويل من صورة إلى صورة، مع دقة 1K / 2K / 4K، ونسب الأبعاد الشائعة، وتحليل Base64.

URL: https://docs.lmuai.com/ar/docs/api/gemini-image



توفر LMU AI واجهة برمجة تطبيقات متوافقة مع `v1beta` الأصلية لـ Gemini حتى تتمكن من استدعاء نماذج صور Gemini مباشرة لأغراض **التحويل من النص إلى صورة، وتحرير الصور، والتحويل من صورة إلى صورة**.

<Callout type="info" title="بروتوكول الواجهة">
  يستخدم توليد صور Gemini بروتوكول `generateContent` الأصلي من Google لـ Gemini — وليس `/v1/chat/completions` من OpenAI، ولا `/v1/images/generations` من OpenAI Images.

  نقطة النهاية الرئيسية:

  ```text
  POST https://api.lmuai.com/v1beta/models/{model}:generateContent
  ```
</Callout>

***

## 1. نظرة عامة على الواجهة [#1-نظرة-عامة-على-الواجهة]

| الطريقة | المسار                                                 | الوصف                                                                                                                                            |
| ------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `GET`   | `/v1beta/models`                                       | سرد النماذج الأصلية المتاحة لمفتاح الواجهة الحالي ضمن مجموعة Gemini                                                                              |
| `GET`   | `/v1beta/models/{model}`                               | الحصول على معلومات حول نموذج محدد                                                                                                                |
| `POST`  | `/v1beta/models/{model}:generateContent`               | نقطة النهاية الرئيسية للتحويل من النص إلى صورة، وتحرير الصور، والتحويل من صورة إلى صورة                                                          |
| `POST`  | `/v1beta/models/{model}:streamGenerateContent?alt=sse` | التوليد المتدفق؛ غير مُوصى به كخيار أول لحالات استخدام الصور                                                                                     |
| `GET`   | `/v1/models`                                           | قائمة نماذج متوافقة مع OpenAI، مناسبة لمُحدِّد نماذج عام                                                                                         |
| `POST`  | `/v1/images/batches`                                   | نقطة نهاية ملحق صور Gemini غير المتزامنة على دُفعات من LMU AI؛ راجع [واجهة برمجة تطبيقات صور Gemini على دُفعات](/ar/docs/api/gemini-image-batch) |

**عنوان URL الأساسي:**

```text
https://api.lmuai.com
```

***

## 2. المصادقة [#2-المصادقة]

### مُوصى به: ترويسة Gemini الأصلية [#مُوصى-به-ترويسة-gemini-الأصلية]

```http
x-goog-api-key: YOUR_API_KEY
```

### متوافق: ترويسة Bearer [#متوافق-ترويسة-bearer]

```http
Authorization: Bearer YOUR_API_KEY
```

يقرأ الخادم مفتاح الواجهة بترتيب الأولوية التالي:

1. `x-goog-api-key`؛
2. `Authorization: Bearer ...`؛
3. `x-api-key`؛
4. معلمة الاستعلام `?key=...` على مسارات `/v1beta`.

<Callout type="warn" title="لا تضع المفتاح في عنوان URL">
  `?api_key=...` مُهمَل ويعيد `400`. لا يزال `?key=...` يعمل، لكنه يُسجَّل بسهولة في سجل المتصفح، والوكلاء العكسيين، وسجلات الوصول — استخدم الترويسات في الإنتاج.
</Callout>

خطأ مصادقة نموذجي:

```json
{
  "error": {
    "code": 401,
    "message": "Invalid API key",
    "status": "UNAUTHENTICATED"
  }
}
```

يجب أن يكون مفتاح الواجهة مرتبطاً بمجموعة منصة `gemini`؛ وإلا فلن تتمكن من استدعاء واجهة Gemini الأصلية.

***

## 3. النماذج والتوافر [#3-النماذج-والتوافر]

تستخدم خدمة الصور هذه بشكل أساسي معرّفات النماذج التالية على جانب العميل:

| معرّف النموذج                    | الاستخدام المُوصى به             | ملاحظات تحرير الصور                                                                    |
| -------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------- |
| `gemini-3.1-flash-image`         | التوصية الافتراضية               | تم التحقق منه في الإنتاج مع تحرير الصور عبر `inlineData`                               |
| `gemini-3.1-flash-image-preview` | توافق المعاينة                   | يستخدم بروتوكول تحرير `generateContent` نفسه؛ تحقق منه بشكل منفصل قبل التكامل الإنتاجي |
| `gemini-3-pro-image`             | الجودة أولاً / التحريرات المعقدة | يستخدم بروتوكول تحرير `generateContent` نفسه؛ خاضع لتوافر مفتاحك الحالي                |
| `gemini-3-pro-image-preview`     | توافق معاينة Pro                 | يستخدم بروتوكول التحرير نفسه؛ النموذج المُنفِّذ هو ما تشير إليه استجابة الخادم         |
| `gemini-3.1-flash-lite-image`    | حالات الاستخدام الخفيفة          | استخدمه فقط عندما يعيده سرد النماذج ويتم التحقق من إخراج الصورة                        |

### سرد نماذج Gemini للمفتاح الحالي [#سرد-نماذج-gemini-للمفتاح-الحالي]

```bash
curl 'https://api.lmuai.com/v1beta/models' \
  -H 'x-goog-api-key: YOUR_API_KEY'
```

قائمة نماذج متوافقة مع OpenAI:

```bash
curl 'https://api.lmuai.com/v1/models' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

<Callout type="info" title="قد يتم تعيين معرّفات النماذج على جانب الخادم">
  يُرسل العميل معرّف نموذج للطلب. تدعم LMU AI أسماء نماذج بديلة متوافقة، لذا فإن اسم النموذج المطلوب ليس بالضرورة نفس إصدار النموذج النهائي في الاستجابة.

  لا تخمّن التوافر من اسم النموذج وحده؛ اعتمد على النتيجة الفعلية لاستدعاء `/v1beta/models` بمفتاحك الحالي.
</Callout>

***

## 4. البدء السريع: من النص إلى صورة [#4-البدء-السريع-من-النص-إلى-صورة]

```bash
curl --request POST \
  'https://api.lmuai.com/v1beta/models/gemini-3.1-flash-image:generateContent' \
  --header 'x-goog-api-key: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "contents": [
      {
        "role": "user",
        "parts": [
          {
            "text": "An orange cat wearing an astronaut helmet, cinematic lighting, exquisite detail"
          }
        ]
      }
    ],
    "generationConfig": {
      "responseModalities": ["TEXT", "IMAGE"],
      "imageConfig": {
        "aspectRatio": "1:1",
        "imageSize": "1K"
      }
    }
  }'
```

عند النجاح، اقرأ الصورة من:

```text
candidates[].content.parts[].inlineData.data
```

***

## 5. بنية طلب التحويل من النص إلى صورة [#5-بنية-طلب-التحويل-من-النص-إلى-صورة]

```json
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "A cinematic rainy-night city street, neon lights reflected on the wet pavement, wide-angle composition"
        }
      ]
    }
  ],
  "generationConfig": {
    "responseModalities": ["TEXT", "IMAGE"],
    "imageConfig": {
      "aspectRatio": "16:9",
      "imageSize": "2K"
    }
  }
}
```

### حقول الطلب [#حقول-الطلب]

| الحقل                                 |     النوع |              مطلوب             | الوصف                                                                |
| ------------------------------------- | --------: | :----------------------------: | -------------------------------------------------------------------- |
| `contents`                            |    مصفوفة |               نعم              | مصفوفة محتوى المحادثة؛ يجب أن تحتوي على رسالة مستخدم واحدة على الأقل |
| `contents[].role`                     |     سلسلة |            مُوصى به            | استخدم `user` لإدخال المستخدم                                        |
| `contents[].parts`                    |    مصفوفة |               نعم              | أجزاء نصية أو أجزاء صورة إدخال                                       |
| `parts[].text`                        |     سلسلة | مطلوب للتحويل من النص إلى صورة | موجّه الصورة                                                         |
| `generationConfig`                    |      كائن |               نعم              | معلمات التوليد                                                       |
| `generationConfig.responseModalities` | string\[] |               نعم              | يجب أن يتضمن `IMAGE` لتوليد الصور؛ يُوصى بـ `['TEXT', 'IMAGE']`      |
| `generationConfig.imageConfig`        |      كائن |               نعم              | إعداد دقة الصورة ونسبة الأبعاد                                       |

<Callout type="warn" title="يجب أن تذهب معلمات الصورة إلى imageConfig">
  استخدم `generationConfig.imageConfig`. لا تستخدم `responseFormat.image`؛ فقد لا يثير هذا الحقل خطأ في المعلمات، لكنه لن يُطبَّق كمعلمات صورة أصلية لـ Gemini.
</Callout>

***

## 6. الدقة ونسبة الأبعاد [#6-الدقة-ونسبة-الأبعاد]

### الدقات المدعومة [#الدقات-المدعومة]

| `imageSize` | الاستخدام المُوصى به                                    | الخصائص                          |
| ----------- | ------------------------------------------------------- | -------------------------------- |
| `1K`        | المسودات، المعاينات السريعة، الفرز على دُفعات           | عادةً أسرع وأرخص                 |
| `2K`        | التسليم القياسي، صور المقالات، أصول التجارة الإلكترونية | توازن بين الجودة والوقت والتكلفة |
| `4K`        | الصور الكبيرة الدقيقة، التسليم عالي الجودة              | عادةً وقت توليد ونقل أطول        |

يجب أن يكون `imageSize` بأحرف كبيرة:

```json
{
  "imageSize": "2K"
}
```

### نسب الأبعاد المدعومة [#نسب-الأبعاد-المدعومة]

`aspectRatio` هي **قدرة على مستوى النموذج**؛ لا يمكنك استخدام نسب Flash الموسّعة على نماذج Pro. تتحقق LMU AI من النسبة مقابل النموذج المطلوب لتجنّب إرسال تركيبات غير صالحة معروفة إلى الطرف الأعلى.

**10 نسب شائعة:**

```text
1:1
2:3
3:2
3:4
4:3
4:5
5:4
9:16
16:9
21:9
```

**4 نسب Flash موسّعة:**

```text
1:4
1:8
4:1
8:1
```

### مصفوفة نسب النموذج [#مصفوفة-نسب-النموذج]

| معرّف نموذج العميل               | النسب المؤكَّد دعمها      | العدد |
| -------------------------------- | ------------------------- | ----: |
| `gemini-3.1-flash-image`         | 10 شائعة + 4 Flash موسّعة |    14 |
| `gemini-3.1-flash-image-preview` | 10 شائعة + 4 Flash موسّعة |    14 |
| `gemini-3.1-flash-lite-image`    | 10 شائعة + 4 Flash موسّعة |    14 |
| `gemini-3-pro-image`             | 10 شائعة فقط              |    10 |
| `gemini-3-pro-image-preview`     | 10 شائعة فقط              |    10 |

<Callout type="warn" title="نماذج Pro لا تدعم نسب Flash الموسّعة">
  تمرير `1:4` أو `1:8` أو `4:1` أو `8:1` إلى `gemini-3-pro-image` أو `gemini-3-pro-image-preview` يعيد `INVALID_ARGUMENT` أو خطأ معلمة `400` من الترحيل. على سبيل المثال:

  ```json
  {
    "error": {
      "code": 400,
      "message": "generationConfig.imageConfig.aspectRatio has an unsupported value",
      "status": "INVALID_ARGUMENT"
    }
  }
  ```
</Callout>

تجمع المصفوفة أعلاه بين وثائق النماذج الرسمية من Google واختبار واجهة برمجة تطبيقات LMU AI الإنتاجية: كل نماذج Flash / Flash Lite الثلاثة تجتاز اختبارات النسب الموسّعة، بينما يرفض كلا نموذجي Pro صراحةً جميع النسب الموسّعة الأربع. بالنسبة للنماذج غير المعروفة أو الجديدة، استخدم النسب الشائعة العشر حتى تتحقق منها.

عندما لا تحدد `aspectRatio`، يقرر النموذج نسبة الأبعاد بناءً على محتوى الإدخال وسياسته الافتراضية. لا تمرّر أرقاماً عشرية عشوائية أو `WIDTHxHEIGHT` عشوائياً؛ يجب أن تستخدم قيمة نسبة مُعدَّدة يقبلها النموذج المستهدف.

مثال:

```json
{
  "generationConfig": {
    "responseModalities": ["TEXT", "IMAGE"],
    "imageConfig": {
      "aspectRatio": "9:16",
      "imageSize": "4K"
    }
  }
}
```

<Callout type="info" title="مستويات الدقة ليست أبعاد بكسل ثابتة">
  `1K` و`2K` و`4K` هي مستويات دقة للنموذج. يُحسب العرض والارتفاع الفعليان بواسطة النموذج من المستوى ونسبة الأبعاد؛ لا ينبغي للعميل أن يفترض أنها دائماً `1024×1024` أو `2048×2048` أو `4096×4096`.
</Callout>

***

## 7. تحرير الصور / من صورة إلى صورة [#7-تحرير-الصور--من-صورة-إلى-صورة]

Gemini **يدعم تحرير الصور** — إنه فقط لا يمتلك نقطة نهاية منفصلة `/v1/images/edits` مثل GPT.

كل من التحويل من النص إلى صورة وتحرير الصور يستدعيان:

```text
POST /v1beta/models/{model}:generateContent
```

الفرق بينهما هو:

| حالة الاستخدام                 | محتوى `contents[].parts[]`                  |
| ------------------------------ | ------------------------------------------- |
| من النص إلى صورة               | موجّه نصي فقط                               |
| تحرير الصور / من صورة إلى صورة | تعليمة تحرير نصية + صورة إدخال `inlineData` |

<Callout type="info" title="تم التحقق منه في الإنتاج">
  مع `gemini-3.1-flash-image`، أدى إرسال صورة إدخال JPEG عبر `inlineData` بنجاح إلى إعادة:

  * HTTP `200`؛
  * `finishReason: STOP`؛
  * نتيجة محرَّرة بصيغة `image/png`؛
  * `inlineData.data` غير فارغة؛
  * عدد رموز `usageMetadata` بنمط الصورة.

  قد تحتوي الاستجابة على جزء صورة فقط دون جزء نصي، لذا يجب ألّا يشترط العميل وجود نص في الاستجابة.
</Callout>

### 7.1 طلب تحرير الصور الأدنى [#71-طلب-تحرير-الصور-الأدنى]

```json
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Keep the person, composition, and lighting; change the background to a rainy-night neon street"
        },
        {
          "inlineData": {
            "mimeType": "image/png",
            "data": "INPUT_IMAGE_BASE64"
          }
        }
      ]
    }
  ],
  "generationConfig": {
    "responseModalities": ["TEXT", "IMAGE"],
    "imageConfig": {
      "aspectRatio": "1:1",
      "imageSize": "1K"
    }
  }
}
```

### 7.2 حقول صورة الإدخال [#72-حقول-صورة-الإدخال]

| الحقل                     | النوع | مطلوب | الوصف                                                                              |
| ------------------------- | ----: | :---: | ---------------------------------------------------------------------------------- |
| `contents[].parts[].text` | سلسلة |  نعم  | تعليمة التحرير؛ اذكر بوضوح ما يجب الاحتفاظ به وما يجب تغييره                       |
| `inlineData.mimeType`     | سلسلة |  نعم  | مثل `image/png` و`image/jpeg` و`image/webp`                                        |
| `inlineData.data`         | سلسلة |  نعم  | Base64 خام، دون بادئة Data URL                                                     |
| `imageConfig.aspectRatio` | سلسلة |   لا  | نسبة أبعاد الإخراج؛ عيّن النسبة المطابقة إذا كنت بحاجة إلى الحفاظ على نسبة الإدخال |
| `imageConfig.imageSize`   | سلسلة |   لا  | مستوى الإخراج: `1K` و`2K` و`4K`، خاضع لقدرة النموذج                                |

<Callout type="warn" title="لا تُضمِّن بادئة Data URL في Base64">
  صحيح:

  ```text
  /9j/4AAQSkZJRgABAQ...
  ```

  لا تمرّر:

  ```text
  data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...
  ```

  تؤدي صورة إدخال كبيرة جداً إلى زيادة وقت الرفع والمعالجة وقد تعيد `413` بسبب حدود نص طلب البوابة.
</Callout>

### 7.3 مثال curl كامل [#73-مثال-curl-كامل]

أولاً حوّل صورة محلية إلى Base64 في سطر واحد:

```bash
IMAGE_BASE64=$(base64 < input.jpg | tr -d '\n')
```

ثم استدعِ نموذج الصورة:

```bash
curl --request POST \
  'https://api.lmuai.com/v1beta/models/gemini-3.1-flash-image:generateContent' \
  --header 'x-goog-api-key: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw "{
    \"contents\": [{
      \"role\": \"user\",
      \"parts\": [
        {
          \"text\": \"Keep the cup, composition, lighting, and background unchanged; only change the cup from blue to purple, and add three white star patterns on the cup body; do not add any text\"
        },
        {
          \"inlineData\": {
            \"mimeType\": \"image/jpeg\",
            \"data\": \"${IMAGE_BASE64}\"
          }
        }
      ]
    }],
    \"generationConfig\": {
      \"responseModalities\": [\"TEXT\", \"IMAGE\"],
      \"imageConfig\": {
        \"aspectRatio\": \"3:2\",
        \"imageSize\": \"1K\"
      }
    }
  }"
```

### 7.4 مثال تحرير الصور بلغة Python [#74-مثال-تحرير-الصور-بلغة-python]

```python
import base64
import requests

api_key = "YOUR_API_KEY"
model = "gemini-3.1-flash-image"
input_path = "input.jpg"

with open(input_path, "rb") as f:
    image_base64 = base64.b64encode(f.read()).decode("utf-8")

payload = {
    "contents": [{
        "role": "user",
        "parts": [
            {
                "text": "Keep the subject and composition; change the background to a rainy-night neon street"
            },
            {
                "inlineData": {
                    "mimeType": "image/jpeg",
                    "data": image_base64,
                }
            },
        ],
    }],
    "generationConfig": {
        "responseModalities": ["TEXT", "IMAGE"],
        "imageConfig": {
            "aspectRatio": "3:2",
            "imageSize": "1K",
        },
    },
}

response = requests.post(
    f"https://api.lmuai.com/v1beta/models/{model}:generateContent",
    headers={
        "x-goog-api-key": api_key,
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=300,
)
response.raise_for_status()
result = response.json()

saved = False
for candidate in result.get("candidates", []):
    for part in candidate.get("content", {}).get("parts", []):
        inline_data = part.get("inlineData", {})
        if inline_data.get("data"):
            mime_type = inline_data.get("mimeType", "image/png")
            extension = "jpg" if "jpeg" in mime_type else "webp" if "webp" in mime_type else "png"
            with open(f"gemini-edited.{extension}", "wb") as f:
                f.write(base64.b64decode(inline_data["data"]))
            saved = True
            break
    if saved:
        break

if not saved:
    raise RuntimeError("HTTP request succeeded, but the response contains no edited image")
```

### 7.5 نصائح موجّه التحرير [#75-نصائح-موجّه-التحرير]

بالنسبة لموجّهات تحرير الصور، من الأفضل فصل "ما يجب الاحتفاظ به" عن "ما يجب تغييره" بوضوح:

```text
Keep: subject identity, pose, camera angle, composition, and lighting.
Change: change the background to a rainy-night neon street.
Forbidden: do not add text; do not change the person's face.
```

تُنتج هذه البنية نتائج أكثر اتساقاً من مجرد كتابة "اجعله يبدو أجمل".

### 7.6 صور مرجعية متعددة [#76-صور-مرجعية-متعددة]

يمكن لبعض نماذج صور Gemini قبول صور `inlineData` متعددة في `parts[]` نفسها للمرجعية الأسلوبية، أو مرجعية الشخصيات، أو مزج الأصول. مع ذلك، يختلف عدد الصور المرجعية المسموح به وإجمالي حجم نص الطلب حسب النموذج، لذا تحقق مقابل نموذجك المحدد قبل الاستخدام الإنتاجي.

لا تفترض أن نموذج صورة معيّناً يدعم عدداً غير محدود من الصور المرجعية لمجرد أن `/v1beta/models` أعاده.

***

## 8. الاستجابة الناجحة [#8-الاستجابة-الناجحة]

استجابة نموذجية:

```json
{
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": [
          {
            "text": "Image generated as requested."
          },
          {
            "inlineData": {
              "mimeType": "image/png",
              "data": "BASE64_IMAGE_DATA"
            }
          }
        ]
      },
      "finishReason": "STOP",
      "index": 0
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 123,
    "candidatesTokenCount": 1120,
    "totalTokenCount": 1450,
    "candidatesTokensDetails": [
      {
        "modality": "IMAGE",
        "tokenCount": 1024
      }
    ]
  },
  "modelVersion": "MODEL_VERSION"
}
```

### حقول الاستجابة الرئيسية [#حقول-الاستجابة-الرئيسية]

| الحقل                          | الوصف                                                           |
| ------------------------------ | --------------------------------------------------------------- |
| `candidates[]`                 | قائمة المخرجات المرشّحة                                         |
| `candidates[].content.parts[]` | أجزاء نصية أو أجزاء صورة                                        |
| `parts[].inlineData.mimeType`  | نوع MIME للصورة المُعادة                                        |
| `parts[].inlineData.data`      | محتوى Base64 للصورة المُعادة                                    |
| `candidates[].finishReason`    | سبب الإنهاء؛ القيمة الشائعة للنجاح هي `STOP`                    |
| `usageMetadata`                | عدد رموز الإدخال والإخراج ونمط الصورة                           |
| `modelVersion`                 | إصدار النموذج الفعلي المُعاد من الطرف الأعلى، يُمرَّر عند وجوده |

### تحديد نجاح توليد الصورة بشكل صحيح [#تحديد-نجاح-توليد-الصورة-بشكل-صحيح]

ينبغي للعميل التحقق من جميع ما يلي:

1. رمز حالة HTTP هو `2xx`؛
2. `candidates` غير فارغة؛
3. جزء واحد على الأقل من `parts[]` يحتوي على `inlineData.data` غير فارغة؛
4. يُفكّ ترميز Base64 بنجاح؛
5. تحقق من `finishReason` عند الضرورة.

<Callout type="warn" title="HTTP 200 لا يضمن توليد صورة">
  قد يعيد الطرف الأعلى HTTP `200` دون `inlineData.data` في الاستجابة. يجب معاملة هذه الطلبات على أنها "فشل تجاري في توليد الصورة" ويجب ألّا تُحتسب كصور ناجحة.
</Callout>

***

## 9. مثال Node.js [#9-مثال-nodejs]

```js
import { writeFile } from 'node:fs/promises';

const BASE_URL = process.env.GEMINI_BASE_URL || 'https://api.lmuai.com';
const API_KEY = process.env.GEMINI_API_KEY;
const MODEL = 'gemini-3.1-flash-image';

if (!API_KEY) throw new Error('Missing GEMINI_API_KEY');

const payload = {
  contents: [
    {
      role: 'user',
      parts: [
        { text: 'A seaside lighthouse at sunset, watercolor illustration, warm tones, delicate paper texture' },
      ],
    },
  ],
  generationConfig: {
    responseModalities: ['TEXT', 'IMAGE'],
    imageConfig: {
      aspectRatio: '16:9',
      imageSize: '2K',
    },
  },
};

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 300_000);

try {
  const response = await fetch(
    `${BASE_URL}/v1beta/models/${encodeURIComponent(MODEL)}:generateContent`,
    {
      method: 'POST',
      headers: {
        'x-goog-api-key': API_KEY,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(payload),
      signal: controller.signal,
    },
  );

  const text = await response.text();
  let data;
  try {
    data = JSON.parse(text);
  } catch {
    throw new Error(`Non-JSON response from the service: HTTP ${response.status}`);
  }

  if (!response.ok) {
    throw new Error(
      `HTTP ${response.status}: ${data?.error?.message || JSON.stringify(data)}`,
    );
  }

  const parts = data.candidates?.flatMap((candidate) => candidate.content?.parts || []) || [];
  const imagePart = parts.find((part) => part.inlineData?.data);

  if (!imagePart) {
    const reasons = data.candidates?.map((candidate) => candidate.finishReason).filter(Boolean);
    throw new Error(`Request completed but no image, finishReason=${reasons?.join(',') || 'unknown'}`);
  }

  const mimeType = imagePart.inlineData.mimeType || 'image/png';
  const extension = mimeType === 'image/jpeg'
    ? 'jpg'
    : mimeType === 'image/webp'
      ? 'webp'
      : 'png';

  await writeFile(
    `gemini-output.${extension}`,
    Buffer.from(imagePart.inlineData.data, 'base64'),
  );

  console.log('Image saved, usageMetadata:', data.usageMetadata || null);
} finally {
  clearTimeout(timer);
}
```

***

## 10. مثال Python [#10-مثال-python]

```python
import base64
import os
from pathlib import Path
import requests

BASE_URL = os.getenv("GEMINI_BASE_URL", "https://api.lmuai.com")
API_KEY = os.environ["GEMINI_API_KEY"]
MODEL = "gemini-3.1-flash-image"

payload = {
    "contents": [
        {
            "role": "user",
            "parts": [
                {"text": "A futuristic building complex, early-morning mist, ultra-wide-angle photography, realistic materials"}
            ],
        }
    ],
    "generationConfig": {
        "responseModalities": ["TEXT", "IMAGE"],
        "imageConfig": {
            "aspectRatio": "16:9",
            "imageSize": "2K",
        },
    },
}

response = requests.post(
    f"{BASE_URL}/v1beta/models/{MODEL}:generateContent",
    headers={
        "x-goog-api-key": API_KEY,
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=300,
)

data = response.json()
if not response.ok:
    message = data.get("error", {}).get("message", data)
    raise RuntimeError(f"HTTP {response.status_code}: {message}")

image_part = None
for candidate in data.get("candidates", []):
    for part in candidate.get("content", {}).get("parts", []):
        if part.get("inlineData", {}).get("data"):
            image_part = part
            break
    if image_part:
        break

if not image_part:
    reasons = [candidate.get("finishReason") for candidate in data.get("candidates", [])]
    raise RuntimeError(f"Request completed but no image was returned, finishReason={reasons}")

inline_data = image_part["inlineData"]
mime_type = inline_data.get("mimeType", "image/png")
extension = {
    "image/jpeg": "jpg",
    "image/webp": "webp",
}.get(mime_type, "png")

Path(f"gemini-output.{extension}").write_bytes(
    base64.b64decode(inline_data["data"])
)

print("Image saved")
print("usageMetadata:", data.get("usageMetadata"))
```

***

## 11. الأخطاء الشائعة [#11-الأخطاء-الشائعة]

عادةً ما تعيد نقطة نهاية Gemini الأصلية أخطاء بنمط Google:

```json
{
  "error": {
    "code": 429,
    "message": "upstream rate limit exceeded",
    "status": "RESOURCE_EXHAUSTED"
  }
}
```

|     حالة HTTP | السبب الشائع                                               | التوصية                                                            |
| ------------: | ---------------------------------------------------------- | ------------------------------------------------------------------ |
|         `400` | بنية طلب أو مسار نموذج أو منصة مجموعة غير صحيحة            | أصلح الطلب؛ لا تُعِد المحاولة فقط                                  |
|         `401` | مفتاح الواجهة مفقود أو غير صالح أو معطّل                   | تحقق من المفتاح والترويسات                                         |
| `402` / `403` | رصيد غير كافٍ أو اشتراك أو أهلية فوترة أو أذونات           | تحقق من الحساب وأذونات المجموعة                                    |
|         `413` | نص طلب من صورة إلى صورة كبير جداً                          | اضغط صورة الإدخال                                                  |
|         `429` | حد التزامن للمستخدم أو تحديد معدل من الطرف الأعلى          | تراجع أُسّي؛ قلّل التزامن وعدد الطلبات في الدقيقة                  |
|         `500` | خطأ داخلي أو خطأ سعة                                       | سجّل رمز الخطأ ومعرّف الطلب؛ أعِد المحاولة عدداً محدوداً من المرات |
|         `502` | فشل مصادقة أو أذونات أو خدمة مؤقت من الطرف الأعلى          | تراجع وأعِد المحاولة؛ اتصل بمسؤول إذا لزم الأمر                    |
|         `503` | لا يوجد حساب Gemini متاح أو الطرف الأعلى مُحمَّل بشكل زائد | أعِد المحاولة بعد تأخير وقلّل حركة المرور                          |
|         `504` | انتهاء مهلة البوابة أو الطرف الأعلى                        | أعِد الإصدار كطلب مستقل                                            |

إذا كانت رسالة الخطأ تقول إن جميع الرموز معطّلة أو في فترة تهدئة أو مقفلة أو منتهية الصلاحية، فهذه مشكلة سعة خدمة، وليست خطأ في تنسيق الموجّه. توقف عن إعادة المحاولة بقوة وقدّم رمز الخطأ ومعرّف الطلب إلى مسؤول.

***

## 12. المهلات وإعادة المحاولة والتزامن [#12-المهلات-وإعادة-المحاولة-والتزامن]

### توصيات مهلة العميل [#توصيات-مهلة-العميل]

| الدقة | المهلة الإجمالية المُوصى بها |
| ----- | ---------------------------: |
| `1K`  |          120 ثانية على الأقل |
| `2K`  |          180 ثانية على الأقل |
| `4K`  |           يُوصى بـ 300 ثانية |

هذه توصيات تكامل، وليست اتفاقية مستوى خدمة ثابتة. إذا مرّت طلباتك أيضاً عبر Nginx أو CDN أو بوابة واجهة برمجة تطبيقات خاصة بك، فاضبط مهلات القراءة لتلك المكونات وفقاً لذلك.

### توصيات إعادة المحاولة [#توصيات-إعادة-المحاولة]

يُوصى بإعادة المحاولة:

* `429`؛
* `502` و`503` و`504`؛
* انقطاعات الشبكة، وإعادة تعيين الاتصال، وانتهاء مهلة القراءة؛
* عندما تحصل على HTTP `200` دون صورة، يمكنك إعادة المحاولة مرة واحدة (محدودة) وحفظ الاستجابة الخام.

عادةً لا تُعِد المحاولة:

* `400`؛
* `401`؛
* أخطاء الرصيد أو الأذونات الصريحة؛
* أخطاء معلمات الطلب أو سياسة المحتوى.

أعِد المحاولة 2–3 مرات على الأكثر، باستخدام تراجع أُسّي:

```text
Attempt 1: 1–2 seconds random jitter
Attempt 2: 3–5 seconds random jitter
Attempt 3: 8–12 seconds random jitter
```

### صور فورية متعددة [#صور-فورية-متعددة]

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

* الحد الأقصى للتزامن قيد التنفيذ؛
* الطلبات في الدقيقة (RPM)؛
* عدد المهام لكل مستخدم؛
* المهلة والحد الأقصى لعدد مرات إعادة المحاولة.

إذا كنت بحاجة إلى إرسال عشرات إلى مئات الموجّهات وانتظار النتائج بشكل غير متزامن، فاستخدم [واجهة برمجة تطبيقات صور Gemini على دُفعات](/ar/docs/api/gemini-image-batch).

***

## 13. الاستخدام والفوترة [#13-الاستخدام-والفوترة]

يمكن استخدام `usageMetadata` في الاستجابة لتحليل رموز الإدخال والإخراج ونمط الصورة، لكنها ليست بالضرورة مساوية للمبلغ النهائي المفوتر.

قد يتأثر المبلغ الفعلي بما يلي:

* معرّف النموذج المطلوب مقابل النموذج المُعيَّن فعلياً؛
* مستوى الصورة `1K` أو `2K` أو `4K`؛
* سعر المجموعة لكل صورة؛
* مُضاعِف مجموعة المستخدم ومُضاعِف حساب الطرف الأعلى؛
* قواعد الفوترة في بيئة النشر.

للمبلغ النهائي، اعتمد على تفاصيل الاستخدام في وحدة تحكم LMU AI والتغيّر في رصيد حسابك.

عند تشغيل اختبارات الجودة أو التزامن، سجّل:

1. الرصيد قبل الاختبار؛
2. الرصيد بعد الاختبار؛
3. عدد الطلبات الناجحة؛
4. العدد الفعلي للصور المُعادة؛
5. النموذج والدقة ونسبة الأبعاد؛
6. الفرق في الرصيد؛
7. متوسط التكلفة لكل صورة ناجحة.

قد تُنشر سجلات الاستخدام بشكل غير متزامن، لذا انتظر فترة بعد الاختبار قبل تسوية المبلغ النهائي.

***

## 14. توصيات الأمان [#14-توصيات-الأمان]

* خزّن مفتاح الواجهة فقط في متغيرات بيئة على جانب الخادم أو في مدير أسرار؛
* لا تُضمِّن المفتاح في واجهات المتصفح الأمامية، أو حزم تطبيقات الهاتف، أو مستودعات الكود العامة؛
* لا تسجّل مفتاح الواجهة الكامل أو Base64 الكامل للصورة؛
* تحقق من نوع MIME لصورة الإدخال وحجم الملف وصلاحية Base64؛
* اختر امتداد الملف بناءً على `inlineData.mimeType` عند حفظ الاستجابات؛
* سجّل معرّف التتبع الخاص بك، ووقت الطلب، والنموذج، والدقة، وحالة HTTP لكل طلب تجاري؛
* بعد انتهاء المهلة، لا تُعِد إنشاء عدد كبير من الطلبات المتطابقة في فترة قصيرة جداً.

***

## 15. قائمة تحقق قبول التكامل [#15-قائمة-تحقق-قبول-التكامل]

* [ ] يمكنك استخدام `/v1beta/models` للحصول على قائمة نماذج Gemini للمفتاح الحالي؛
* [ ] يمكنك المصادقة باستخدام ترويسة `x-goog-api-key` أو Bearer؛
* [ ] يمكنك إكمال توليد `1K / 1:1` من النص إلى صورة؛
* [ ] يمكنك إكمال توليد `2K` و`4K` من النص إلى صورة؛
* [ ] يمكنك إكمال تحرير صورة واحد على الأقل / من صورة إلى صورة؛
* [ ] يمكنك قراءة `inlineData.mimeType` و`inlineData.data`؛
* [ ] يمكنك وضع علامة على HTTP 200 دون صورة كفشل؛
* [ ] لقد ضبطت مهلة لطلبات الصور؛
* [ ] لقد نفّذت تراجعاً أُسّياً محدوداً لـ 429 و5xx؛
* [ ] لقد أكّدت السعر والرصيد والتزامن وعدد الطلبات في الدقيقة؛
* [ ] سجلاتك لا تسرّب مفتاح الواجهة أو Base64 الكامل.

***

## الخطوات التالية [#الخطوات-التالية]

* التوليد غير المتزامن للعديد من الموجّهات: [واجهة برمجة تطبيقات صور Gemini على دُفعات](/ar/docs/api/gemini-image-batch)
* سرد النماذج المتاحة للمفتاح الحالي: [معرض النماذج](/ar/docs/guide/models)
* تفاصيل البروتوكول وعنوان URL الأساسي: [بروتوكولات الواجهة](/ar/docs/guide/api-protocols)
* استعلام استخدام الطلبات: [تصدير تفاصيل الاستخدام](/ar/docs/api/usage-export)
