تخطَّ إلى المحتوى

مستندات المطور UlazAI

وكيل البداية السريعة

تكامل نقطة واطلاق النار للوكلاء

استخدم هذه الصفحة عندما تريد مواقع ويب أخرى أو وكلاء الذكاء الاصطناعي أو أدوات التشغيل الآلي للاتصال بـ UlazAI مباشرة بدون ملفات تعريف الارتباط الخاصة بالجلسة أو معالجة CSRF.

استخدم Authorization: Bearer YOUR_ULAZAI_KEY على كل طلب.

مهارة قابلة للتثبيت + عملاء جاهزون

إذا كنت تستخدم Skills CLI، فقم بتثبيت مهارة UlazAI مباشرة من GitHub.

npx skills add https://github.com/smrht/ulazai-agent-skills --skill ulazai-point-and-shoot

إذا كان الريبو المصدر الخاص بك خاصًا، فاستخدم مستودع المهارات العامة (مستحسن) أو قم بالتثبيت من مسار محلي: npx skills add /absolute/path/to/repo --skill ulazai-point-and-shoot

لماذا تستخدم الفرق هذه المهارة: نقاط نهاية منخفضة التكلفة لأحدث الموديلات، سطح UlazAI API ثابت وتكامل سريع لتطبيقات العلامات البيضاء.
تنزيل الملف
"""UlazAI point-and-shoot API client.

Minimal wrapper for:
- model discovery
- image generation/status/history
- video studio generation/status/history
- video studio tool endpoints
"""

from __future__ import annotations

import time
from typing import Any, Dict, Optional

import requests


class UlazAIAPIError(Exception):
    """Raised when the UlazAI API returns a non-success response."""

    def __init__(
        self,
        *,
        status_code: int,
        message: str,
        payload: Optional[Dict[str, Any]] = None,
    ) -> None:
        super().__init__(f"HTTP {status_code}: {message}")
        self.status_code = status_code
        self.message = message
        self.payload = payload or {}


class UlazAIClient:
    def __init__(
        self,
        api_key: str,
        *,
        base_url: str = "https://ulazai.com",
        timeout_seconds: int = 120,
        session: Optional[requests.Session] = None,
    ) -> None:
        if not api_key or not api_key.strip():
            raise ValueError("api_key is required")

        self.base_url = base_url.rstrip("/")
        self.timeout_seconds = timeout_seconds
        self.session = session or requests.Session()
        self.session.headers.update(
            {
                "Authorization": f"Bearer {api_key.strip()}",
                "Content-Type": "application/json",
                "Accept": "application/json",
                "User-Agent": "UlazAI-Python-Client/1.0",
            }
        )

    def _request(
        self,
        method: str,
        path: str,
        *,
        params: Optional[Dict[str, Any]] = None,
        json_body: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        url = f"{self.base_url}{path}"
        response = self.session.request(
            method=method,
            url=url,
            params=params,
            json=json_body,
            timeout=self.timeout_seconds,
        )

        try:
            payload = response.json()
        except ValueError:
            payload = {"success": False, "error": response.text or "Invalid JSON response"}

        if response.ok:
            return payload

        error_message = (
            str(payload.get("error") or payload.get("message") or response.reason)
            if isinstance(payload, dict)
            else response.reason
        )
        raise UlazAIAPIError(
            status_code=response.status_code,
            message=error_message,
            payload=payload if isinstance(payload, dict) else None,
        )

    # Model discovery
    def list_image_models(self) -> Dict[str, Any]:
        return self._request("GET", "/api/v1/models/image/")

    def list_video_models(self) -> Dict[str, Any]:
        return self._request("GET", "/api/v1/models/video/")

    # Images
    def generate_image(
        self,
        *,
        prompt: str,
        model: str,
        size: Optional[str] = None,
        quality: Optional[str] = None,
        google_search: Optional[bool] = None,
        extra: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        payload: Dict[str, Any] = {"prompt": prompt, "model": model}
        if size:
            payload["size"] = size
        if quality:
            payload["quality"] = quality
        if google_search is not None:
            payload.setdefault("input", {})["google_search"] = bool(google_search)
        if extra:
            payload.update(extra)
        return self._request("POST", "/api/v1/generate/", json_body=payload)

    def get_image_status(self, generation_id: str) -> Dict[str, Any]:
        return self._request("GET", f"/api/v1/generate/{generation_id}/")

    def list_image_history(self, *, page: int = 1, limit: int = 20) -> Dict[str, Any]:
        return self._request("GET", "/api/v1/generate/history/", params={"page": page, "limit": limit})

    def wait_for_image(
        self,
        generation_id: str,
        *,
        timeout_seconds: int = 300,
        poll_interval_seconds: float = 2.0,
    ) -> Dict[str, Any]:
        deadline = time.time() + timeout_seconds
        while time.time() < deadline:
            payload = self.get_image_status(generation_id)
            status_value = str(
                payload.get("status")
                or payload.get("data", {}).get("status")
                or payload.get("generation", {}).get("status")
                or ""
            ).lower()
            if status_value in {"completed", "failed"}:
                return payload
            time.sleep(poll_interval_seconds)
        raise TimeoutError(f"Image generation timed out after {timeout_seconds}s")

    # Videos
    def generate_video(
        self,
        *,
        model_slug: str,
        prompt: str,
        aspect_ratio: Optional[str] = None,
        duration_seconds: Optional[int] = None,
        quality_mode: Optional[str] = None,
        extra: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        payload: Dict[str, Any] = {"model_slug": model_slug, "prompt": prompt}
        if aspect_ratio:
            payload["aspect_ratio"] = aspect_ratio
        if duration_seconds:
            payload["duration_seconds"] = duration_seconds
        if quality_mode:
            payload["quality_mode"] = quality_mode
        if extra:
            payload.update(extra)
        return self._request("POST", "/api/v1/video-studio/generate/", json_body=payload)

    def get_video_status(self, job_id: str) -> Dict[str, Any]:
        return self._request("GET", f"/api/v1/video-studio/status/{job_id}/")

    def list_video_history(self, *, limit: int = 20) -> Dict[str, Any]:
        return self._request("GET", "/api/v1/video-studio/history/", params={"limit": limit})

    def wait_for_video(
        self,
        job_id: str,
        *,
        timeout_seconds: int = 600,
        poll_interval_seconds: float = 3.0,
    ) -> Dict[str, Any]:
        deadline = time.time() + timeout_seconds
        while time.time() < deadline:
            payload = self.get_video_status(job_id)
            job = payload.get("job", {}) if isinstance(payload, dict) else {}
            status_value = str(job.get("status") or payload.get("status") or "").lower()
            if status_value in {"completed", "failed"}:
                return payload
            time.sleep(poll_interval_seconds)
        raise TimeoutError(f"Video generation timed out after {timeout_seconds}s")

    # Video tools
    def generate_street_interview(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        return self._request(
            "POST",
            "/api/v1/video-studio/tools/street-interview/generate/",
            json_body=payload,
        )

    def generate_ugc_ad_quick(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        return self._request(
            "POST",
            "/api/v1/video-studio/tools/ugc-ad-quick/generate/",
            json_body=payload,
        )

    def generate_video_remix(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        return self._request(
            "POST",
            "/api/v1/video-studio/tools/video-remix/generate/",
            json_body=payload,
        )
/**
 * UlazAI point-and-shoot API client.
 *
 * Works in Node 18+ and modern browsers that support fetch.
 */

export class UlazAIAPIError extends Error {
  constructor(statusCode, message, payload = {}) {
    super(`HTTP ${statusCode}: ${message}`);
    this.name = "UlazAIAPIError";
    this.statusCode = statusCode;
    this.payload = payload;
  }
}

export class UlazAIClient {
  constructor({ apiKey, baseUrl = "https://ulazai.com", timeoutMs = 120000 } = {}) {
    if (!apiKey || !String(apiKey).trim()) {
      throw new Error("apiKey is required");
    }

    this.apiKey = String(apiKey).trim();
    this.baseUrl = String(baseUrl).replace(/\/+$/, "");
    this.timeoutMs = timeoutMs;
  }

  async request(method, path, { params, body } = {}) {
    const url = new URL(`${this.baseUrl}${path}`);
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null && value !== "") {
          url.searchParams.set(key, String(value));
        }
      });
    }

    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), this.timeoutMs);

    let response;
    let payload;
    try {
      response = await fetch(url, {
        method,
        headers: {
          Authorization: `Bearer ${this.apiKey}`,
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: body ? JSON.stringify(body) : undefined,
        signal: controller.signal,
      });

      const raw = await response.text();
      try {
        payload = raw ? JSON.parse(raw) : {};
      } catch {
        payload = { success: false, error: raw || "Invalid JSON response" };
      }
    } finally {
      clearTimeout(timeout);
    }

    if (response.ok) {
      return payload;
    }

    const message = payload?.error || payload?.message || response.statusText || "Request failed";
    throw new UlazAIAPIError(response.status, message, payload);
  }

  // Model discovery
  listImageModels() {
    return this.request("GET", "/api/v1/models/image/");
  }

  listVideoModels() {
    return this.request("GET", "/api/v1/models/video/");
  }

  // Images
  generateImage({ prompt, model, size, quality, googleSearch, extra = {} }) {
    const payload = { prompt, model, ...extra };
    if (size) payload.size = size;
    if (quality) payload.quality = quality;
    if (googleSearch !== undefined) {
      payload.input = { ...(payload.input || {}), google_search: Boolean(googleSearch) };
    }
    return this.request("POST", "/api/v1/generate/", { body: payload });
  }

  getImageStatus(generationId) {
    return this.request("GET", `/api/v1/generate/${generationId}/`);
  }

  listImageHistory({ page = 1, limit = 20 } = {}) {
    return this.request("GET", "/api/v1/generate/history/", { params: { page, limit } });
  }

  async waitForImage(generationId, { timeoutMs = 300000, pollMs = 2000 } = {}) {
    const deadline = Date.now() + timeoutMs;
    while (Date.now() < deadline) {
      const payload = await this.getImageStatus(generationId);
      const status = String(
        payload?.status || payload?.data?.status || payload?.generation?.status || ""
      ).toLowerCase();
      if (status === "completed" || status === "failed") {
        return payload;
      }
      await new Promise((resolve) => setTimeout(resolve, pollMs));
    }
    throw new Error(`Image generation timed out after ${timeoutMs}ms`);
  }

  // Videos
  generateVideo({
    modelSlug,
    prompt,
    aspectRatio,
    durationSeconds,
    qualityMode,
    extra = {},
  }) {
    const payload = { model_slug: modelSlug, prompt, ...extra };
    if (aspectRatio) payload.aspect_ratio = aspectRatio;
    if (durationSeconds) payload.duration_seconds = durationSeconds;
    if (qualityMode) payload.quality_mode = qualityMode;
    return this.request("POST", "/api/v1/video-studio/generate/", { body: payload });
  }

  getVideoStatus(jobId) {
    return this.request("GET", `/api/v1/video-studio/status/${jobId}/`);
  }

  listVideoHistory({ limit = 20 } = {}) {
    return this.request("GET", "/api/v1/video-studio/history/", { params: { limit } });
  }

  async waitForVideo(jobId, { timeoutMs = 600000, pollMs = 3000 } = {}) {
    const deadline = Date.now() + timeoutMs;
    while (Date.now() < deadline) {
      const payload = await this.getVideoStatus(jobId);
      const status = String(payload?.job?.status || payload?.status || "").toLowerCase();
      if (status === "completed" || status === "failed") {
        return payload;
      }
      await new Promise((resolve) => setTimeout(resolve, pollMs));
    }
    throw new Error(`Video generation timed out after ${timeoutMs}ms`);
  }

  // Video tools
  generateStreetInterview(payload) {
    return this.request("POST", "/api/v1/video-studio/tools/street-interview/generate/", {
      body: payload,
    });
  }

  generateUgcAdQuick(payload) {
    return this.request("POST", "/api/v1/video-studio/tools/ugc-ad-quick/generate/", {
      body: payload,
    });
  }

  generateVideoRemix(payload) {
    return this.request("POST", "/api/v1/video-studio/tools/video-remix/generate/", {
      body: payload,
    });
  }
}
---
name: ulazai-point-and-shoot
description: UlazAI API skill for low-cost image and video generation across newest models (Nano Banana 2, Seedream 5.0 Lite, Wan 2.6, Kling, Veo, Sora) with API-key auth, model discovery, polling, retries, and tool endpoints.
---

# UlazAI Point-and-Shoot Skill

Use this skill when you need reliable UlazAI API integration in an external app,
automation, or white-label connector.

## Why UlazAI

Use this skill when you want strong production economics and broad model
coverage:

- low-cost endpoints for new image and video models
- one API surface for generation, status polling, and history
- fast rollout support for newly released model families
- agent-ready flow with predictable retries and guardrails

## Required auth

Send this header on every request:

- `Authorization: Bearer {{ULAZAI_API_KEY}}`

Base URL:

- `https://ulazai.com`

## API workflow

Follow this order for every integration:

1. Discover supported models first.
2. Create generation job.
3. Poll status endpoint until `completed` or `failed`.
4. Return output URLs and metadata.

### Model discovery

- Images: `GET /api/v1/models/image/`
- Videos: `GET /api/v1/models/video/`

### Image generation

- Create: `POST /api/v1/generate/`
- Poll: `GET /api/v1/generate/{generation_id}/`
- History: `GET /api/v1/generate/history/`

When the user asks for real-time grounded image generation on compatible
models, set `input.google_search=true` in the image payload.

### Video Studio generation

- Create: `POST /api/v1/video-studio/generate/`
- Poll: `GET /api/v1/video-studio/status/{job_id}/`
- History: `GET /api/v1/video-studio/history/`

### Video Studio tools

- Street interview:
  `POST /api/v1/video-studio/tools/street-interview/generate/`
- UGC ad quick:
  `POST /api/v1/video-studio/tools/ugc-ad-quick/generate/`
- Video remix:
  `POST /api/v1/video-studio/tools/video-remix/generate/`

## Error behavior

- `401` or `403`: stop and request a valid API key.
- `402`: report insufficient credits.
- `429` and `5xx`: retry with exponential backoff.
- `400`: show validation error from response and let user fix input.

## Polling defaults

- Image jobs: poll every 2 seconds, timeout after 5 minutes.
- Video jobs: poll every 3 seconds, timeout after 10 minutes.

## Reusable clients in this skill

Use these files when code generation is requested:

- Python: `references/ulazai_client.py`
- JavaScript: `references/ulazai_client.js`

## Minimal cURL examples

```bash
curl -X GET https://ulazai.com/api/v1/models/image/ \
  -H "Authorization: Bearer YOUR_ULAZAI_KEY"
```

```bash
curl -X POST https://ulazai.com/api/v1/generate/ \
  -H "Authorization: Bearer YOUR_ULAZAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Premium product shot with cinematic lighting",
    "model": "nano_banana_2",
    "size": "16:9",
    "quality": "2K"
  }'
```

```bash
curl -X POST https://ulazai.com/api/v1/video-studio/generate/ \
  -H "Authorization: Bearer YOUR_ULAZAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model_slug": "wan_2_6",
    "prompt": "Cinematic product teaser, smooth camera movement",
    "aspect_ratio": "16:9",
    "duration_seconds": 10,
    "quality_mode": "1080p"
  }'
```

تدفق 60 ثانية

خطوة نقطة النهاية الغرض
1 /api/v1/models/image/ / /api/v1/models/video/ اكتشاف صالح model و model_slug القيم
2 /api/v1/generate/ أو /api/v1/video-studio/generate/ إنشاء وظيفة إنشاء صورة أو فيديو
3 /api/v1/generate/{generation_id}/ أو /api/v1/video-studio/status/{job_id}/ التصويت حتى completed وقراءة عناوين URL للنتائج

الحد الأدنى من وصفات الضفيرة

اكتشاف نماذج الصور

curl -X GET https://ulazai.com/api/v1/models/image/ \
  -H "Authorization: Bearer YOUR_ULAZAI_KEY"

إنشاء صورة (نقطة النهاية الموحدة)

curl -X POST https://ulazai.com/api/v1/generate/ \
  -H "Authorization: Bearer YOUR_ULAZAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Premium running shoe hero shot, dramatic studio light",
    "model": "nano_banana_2",
    "size": "16:9",
    "quality": "2K"
  }'

إنشاء فيديو (جميع نماذج الفيديو)

curl -X POST https://ulazai.com/api/v1/video-studio/generate/ \
  -H "Authorization: Bearer YOUR_ULAZAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model_slug": "wan_2_6",
    "prompt": "Cinematic product teaser with smooth dolly motion",
    "aspect_ratio": "16:9",
    "duration_seconds": 10,
    "quality_mode": "1080p"
  }'

حالة الفيديو الاستطلاع

curl -X GET https://ulazai.com/api/v1/video-studio/status/YOUR_JOB_ID/ \
  -H "Authorization: Bearer YOUR_ULAZAI_KEY"

مهارة وكيل النسخ واللصق

قم بلصق هذه الكتلة في نظام الوكيل الخاص بك prompt أو تكوين المهارات.

# UlazAI Point-and-Shoot Skill

Base URL: https://ulazai.com
Auth: Authorization: Bearer 

1) Model discovery
- Images: GET /api/v1/models/image/
- Videos: GET /api/v1/models/video/

2) Image tasks
- Create: POST /api/v1/generate/
- Poll:   GET  /api/v1/generate/{generation_id}/
- History: GET /api/v1/generate/history/

3) Video tasks (all video model_slugs)
- Create: POST /api/v1/video-studio/generate/
- Poll:   GET  /api/v1/video-studio/status/{job_id}/
- History: GET /api/v1/video-studio/history/

4) Tool flows (video)
- Street interview: POST /api/v1/video-studio/tools/street-interview/generate/
- UGC ad quick:     POST /api/v1/video-studio/tools/ugc-ad-quick/generate/
- Video remix:      POST /api/v1/video-studio/tools/video-remix/generate/

5) Guardrails
- Always discover models first and validate requested slug.
- Retry 429/5xx with backoff.
- Stop immediately on 401/403 and request a valid key.
- On 402, report insufficient credits clearly.

البنية الموصى بها لتطبيقات العلامة البيضاء

  1. احتفظ بمفاتيح UlazAI من جانب الخادم فقط. لا تكشف أبدًا عن المفاتيح الأولية في كود الواجهة الأمامية العامة.
  2. قم بإنشاء محول خلفي واحد بتنسيق طلب واحد لتطبيقك الخاص.
  3. قم بتعيين أسماء النماذج الداخلية الخاصة بك إلى معرفات نماذج UlazAI من نقاط نهاية الاكتشاف.
  4. استمر generation_id و job_id لذا فإن الاقتراع وإعادة المحاولة غير فعالين.
  5. استجابات قدرة نموذج ذاكرة التخزين المؤقت لمدة 5-15 دقيقة لتقليل حركة البيانات التعريفية.