Μετάβαση στο περιεχόμενο

Έγγραφα προγραμματιστή UlazAI

Πράκτορας γρήγορη εκκίνηση

Ενσωμάτωση point-and-shoot για πράκτορες

Χρησιμοποιήστε αυτήν τη σελίδα όταν θέλετε άλλους ιστότοπους, πράκτορες τεχνητής νοημοσύνης ή εργαλεία αυτοματισμού για να καλέσετε απευθείας το UlazAI χωρίς cookie περιόδου λειτουργίας ή χειρισμό 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/ Το Discover ισχύει 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

Ανακαλύψτε μοντέλα εικόνων

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 ή στο skill config.

# 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 λεπτά για μείωση της επισκεψιμότητας μεταδεδομένων.