{"openapi":"3.1.0","info":{"title":"Banita — AI Image Generation API","description":"Генерация изображений с помощью Stable Diffusion. Каждый промпт — новый скетч.","version":"1.3.0"},"servers":[{"url":"https://banita.canfly.org","description":"Production"},{"url":"http://localhost:3000","description":"Local dev"}],"paths":{"/v1/images/generations":{"post":{"summary":"OpenAI-compatible image generation","description":"Совместимо с openai.images.generate(). Возвращает URL изображения синхронно.","operationId":"openai_image_generation","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIImageRequest"}}}},"responses":{"200":{"description":"OpenAI-compatible image response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIImageResponse"}}}},"401":{"description":"Invalid API key"}}}},"/v1/models":{"get":{"summary":"OpenAI-compatible model list","operationId":"openai_list_models","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Model list"},"401":{"description":"Invalid API key"}}}},"/health":{"get":{"summary":"Health Check","description":"Проверка состояния сервиса","operationId":"health_check","responses":{"200":{"description":"Сервис работает","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/generate":{"post":{"summary":"Создать задачу на генерацию","description":"Отправьте промпт, получите job_id. Генерация идёт фоново. Опрашивайте /jobs/{id} для статуса.","operationId":"create_generation_job","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateRequest"}}}},"responses":{"202":{"description":"Задача создана","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCreateResponse"}}}},"400":{"description":"Промпт не указан","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/generate-image":{"post":{"summary":"Сгенерировать изображение (синхронно)","description":"Ждёт результат до 60 секунд. Для API используйте /generate + polling.","operationId":"generate_image_sync","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateRequest"}}}},"responses":{"200":{"description":"Изображение сгенерировано","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSyncResponse"}}}}}}},"/jobs/{job_id}":{"get":{"summary":"Статус задачи","description":"Получить статус генерации по job_id","operationId":"get_job_status","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Статус задачи","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStatusResponse"}}}},"404":{"description":"Задача не найдена","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/jobs/{job_id}/result":{"get":{"summary":"Результат генерации","description":"Получить изображение (доступно когда status=completed)","operationId":"get_job_result","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Результат","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResultResponse"}}}},"400":{"description":"Задача ещё не завершена","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/generations":{"get":{"summary":"Список генераций","description":"Лента скетчей, новые сверху. Пагинация по курсору: передайте nextCursor из предыдущего ответа в параметр cursor. Поле total приходит только на первой странице (без cursor), на остальных — null.","operationId":"list_generations","parameters":[{"name":"cursor","in":"query","required":false,"description":"id последнего полученного скетча. Отдаёт записи строго старше него.","schema":{"type":"integer","minimum":1}},{"name":"limit","in":"query","required":false,"description":"Размер страницы.","schema":{"type":"integer","minimum":1,"maximum":48,"default":24}}],"responses":{"200":{"description":"Страница генераций","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationPage"}}}},"400":{"description":"Некорректный cursor или limit"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"OpenAIImageRequest":{"type":"object","required":["prompt"],"properties":{"model":{"type":"string","default":"banita-sketch"},"prompt":{"type":"string","minLength":1,"maxLength":2000},"n":{"type":"integer","enum":[1],"default":1},"response_format":{"type":"string","enum":["url","b64_json"],"default":"url"},"size":{"type":"string","description":"Принимается для совместимости; сейчас игнорируется"},"quality":{"type":"string","description":"Принимается для совместимости; сейчас игнорируется"},"user":{"type":"string"}}},"OpenAIImageResponse":{"type":"object","required":["created","data"],"properties":{"created":{"type":"integer"},"data":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"b64_json":{"type":"string"}}}}}},"HealthResponse":{"type":"object","required":["overall_status","services"],"properties":{"overall_status":{"type":"string","enum":["healthy","degraded"]},"timestamp":{"type":"string","format":"date-time"},"services":{"type":"object","properties":{"huggingface":{"$ref":"#/components/schemas/ServiceStatus"},"database":{"$ref":"#/components/schemas/ServiceStatus"}}}}},"ServiceStatus":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["healthy","error","unknown"]},"message":{"type":"string"},"response_time":{"type":"integer"}}},"GenerateRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","description":"Текстовое описание изображения (русский или английский)","minLength":1,"maxLength":2000,"examples":["кот сидит на подоконнике, акварель"]}}},"JobCreateResponse":{"type":"object","required":["job_id","status"],"properties":{"job_id":{"type":"string","format":"uuid","description":"ID задачи"},"status":{"type":"string","enum":["pending"],"description":"Начальный статус"},"message":{"type":"string"}}},"JobStatusResponse":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","processing","completed","failed"]},"image_url":{"type":"string","format":"uri","nullable":true},"error_message":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"JobResultResponse":{"type":"object","required":["prompt","image_url","created_at"],"properties":{"prompt":{"type":"string"},"image_url":{"type":"string","format":"uri"},"created_at":{"type":"string","format":"date-time"}}},"GenerateSyncResponse":{"type":"object","required":["url","executionTime"],"properties":{"url":{"type":"string","format":"uri"},"executionTime":{"type":"integer","description":"Миллисекунды"}}},"Generation":{"type":"object","required":["id","prompt","image_url","created_at"],"properties":{"id":{"type":"integer"},"prompt":{"type":"string"},"image_url":{"type":"string","format":"uri"},"created_at":{"type":"string","format":"date-time"}}},"GenerationPage":{"type":"object","required":["items","nextCursor","total"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Generation"}},"nextCursor":{"type":["integer","null"],"description":"Передайте в cursor для следующей страницы. null — лента кончилась."},"total":{"type":["integer","null"],"description":"Всего скетчей. Приходит только на первой странице, иначе null."}}},"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"details":{"type":"string"}}}}}}