Node.js - obraz AI i pobranie gotowego wyniku
Przykład Node.js bez bibliotek: wyślij prompt do API, sprawdź odpowiedź HTTP, zapisz id zadania i odczytaj wynik generacji obrazu przez fetch.
Do jednego obrazu nie potrzebujesz rozbudowanego SDK. Wbudowany fetch wystarczy do zlecenia pracy i odczytu statusu. Ważniejsze od biblioteki są kontrola kodu HTTP, limit odczytów i zachowanie identyfikatora zadania na wypadek przerwania programu.
Przygotowanie i wykonanie
- Ustaw klucz w zmiennej środowiskowej JANTARIS_API_KEY. Nie zapisuj go w repozytorium ani w pliku z promptami.
- Wyślij JSON z identyfikatorem modelu i obiektem input. Sprawdź res.ok przed odczytem pól wyniku.
- Zapisz odpowiedź, szczególnie id i estimated_cost. Wyświetl je operatorowi programu.
- Odczytuj /v1/tasks/{id} z odstępem i limitem liczby prób. W razie przerwania zachowaj id do wznowienia.
- Po succeeded sprawdź tablicę output i pobierz wskazany plik. Nie zakładaj rozszerzenia wyłącznie na podstawie promptu.
- Po failed zapisz błąd bez ponawiania POST w nieskończoność. Zmień wejście tylko na podstawie zrozumiałej przyczyny.
Kontrola przed publikacją
- Klucz pochodzi ze środowiska.
- Nieudany HTTP jest obsłużony przed użyciem JSON.
- Pobrany plik trafia do własnego magazynu.
Przykładowe wejście API
Poniższy JSON jest treścią żądania POST /v1/generate. Jeśli potrzebna jest referencja, podstaw własny dostępny adres obrazu.
{
"model": "gpt-image-2.5",
"input": {
"prompt": "A glass teapot on a wooden table, soft daylight, product photography, no text"
}
}Zapisz przykład jako obraz.mjs i uruchom node obraz.mjs. Plik wynik.bin zawiera oryginalne bajty; rozszerzenie dobierz do wypisanego Content-Type. Nie ponawiaj POST automatycznie po błędzie sieci.
import { writeFile } from "node:fs/promises";
import { setTimeout as sleep } from "node:timers/promises";
const base = "https://jantaris.com/v1";
const headers = {
Authorization: `Bearer ${process.env.JANTARIS_API_KEY}`,
"Content-Type": "application/json"
};
async function api(path, options = {}) {
const response = await fetch(base + path, {
...options, headers, signal: AbortSignal.timeout(20000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
let task = await api("/generate", {
method: "POST",
body: JSON.stringify({ model: "gpt-image-2.5", input: {
prompt: "A glass teapot on a wooden table"
} })
});
await writeFile("task-id.txt", task.id); // Pozwala wznowic GET po restarcie.
console.log(task.id, task.estimated_cost);
for (let attempt = 0; attempt < 24; attempt++) {
if (["succeeded", "failed"].includes(task.status)) break;
await sleep(5000);
task = await api(`/tasks/${encodeURIComponent(task.id)}`);
}
if (task.status === "failed") throw new Error(JSON.stringify(task.error));
if (task.status !== "succeeded") throw new Error(`Sprawdz pozniej zadanie ${task.id}`);
const url = task.output?.[0]?.url;
if (!url) throw new Error("Brak pliku w zakonczonym zadaniu");
const file = await fetch(url, { signal: AbortSignal.timeout(30000) });
if (!file.ok) throw new Error(`Pobieranie: HTTP ${file.status}`);
await writeFile("wynik.bin", new Uint8Array(await file.arrayBuffer()));
console.log("Zapisano wynik.bin, typ:", file.headers.get("content-type"));Model i koszt
W tym procesie użyj GPT Image 2.5. Bieżąca stawka jednostkowa: 0,254 zł za obraz. Sprawdź cennik i kalkulator dla swoich ustawień. Kredyty nie przepadają, nie ma abonamentu, a nieudane generacje są bez opłat. Poprawny wynik jest płatny również wtedy, gdy nie zdecydujesz się go opublikować.
FAQ
Czy adres wyniku może być jedyną kopią obrazu?
Nie warto na nim polegać jako na archiwum. Link jest ważny ograniczony czas. Pobierz zaakceptowany wynik do własnego magazynu.
Czy mogę sprawdzić wejście przed płatnym uruchomieniem?
Tak. Sprawdź schemat modelu i wycenę w Studio. Test lokalnego demo-API sprawdza obsługę żądania, ale nie potwierdza jakości obrazu ani dźwięku rzeczywistego modelu.
Gdzie znajdę obsługiwane parametry?
Strona modelu i dokumentacja generowania opisują wejścia. Tryb podglądu nie dostarcza rzeczywistej generacji.












