Sari la conținut

UlazAI documente pentru dezvoltatori

Agent de pornire rapidă

Integrare point-and-shoot pentru agenți

Utilizați această pagină când doriți alte site-uri web, agenți AI sau instrumente de automatizare pentru a apela direct UlazAI fără cookie-uri de sesiune sau manipulare CSRF.

Utilizați Authorization: Bearer YOUR_ULAZAI_KEY la fiecare cerere.

Abilități instalabile + clienți gata

Dacă utilizați Skills CLI, instalați skill-ul UlazAI direct din GitHub.

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

Dacă depozitul sursă este privat, utilizați un depozit public de competențe (recomandat) sau instalați dintr-o cale locală: npx skills add /absolute/path/to/repo --skill ulazai-point-and-shoot

De ce echipele folosesc această abilitate: puncte finale low-cost pentru cele mai noi modele, o suprafață stabilă UlazAI API și integrare rapidă pentru aplicațiile cu etichetă albă.
Descărcați fișierul
"""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"
  }'
```

Curgerea de 60 de secunde

Pasul Punct final Scop
1 /api/v1/models/image/ / /api/v1/models/video/ Descoperire valabilă model şi model_slug valorile
2 /api/v1/generate/ sau /api/v1/video-studio/generate/ Creați un job de generare de imagini sau videoclipuri
3 /api/v1/generate/{generation_id}/ sau /api/v1/video-studio/status/{job_id}/ Sondaj până la completed și citiți URL-urile rezultatelor

Rețete de curl minime

Descoperiți modele de imagini

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

Generați imagine (punct final unificat)

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"
  }'

Generați videoclip (toate modelele video)

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"
  }'

Starea videoclipului sondajului

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

Abilitatea de a copia și lipi agentul

Lipiți acest bloc în propriul prompt al sistemului de agent sau în configurația abilităților.

# 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.

Arhitectură recomandată pentru aplicațiile cu etichetă albă

  1. Păstrați cheile UlazAI numai pe partea serverului. Nu expuneți niciodată cheile brute în codul public front-end.
  2. Creați un adaptor backend cu un singur format de solicitare pentru propria dvs. aplicație.
  3. Hartați numele modelelor dvs. interne la identificatorii de model UlazAI de la punctele finale de descoperire.
  4. Persista generation_id şi job_id deci sondajele și reîncercările sunt idempotente.
  5. Memorați în cache răspunsurile la capacitatea modelului timp de 5-15 minute pentru a reduce traficul de metadate.