UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

184 lines 7.72 kB
import { IdeogramModels } from "../constants/enums.js"; import { BaseProvider } from "../core/baseProvider.js"; import { isNeuroLink } from "../neurolink.js"; import { createProxyFetch } from "../proxy/proxyFetch.js"; import { AuthenticationError, ProviderError, RateLimitError, } from "../types/index.js"; import { logger } from "../utils/logger.js"; import { createIdeogramConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js"; const IDEOGRAM_DEFAULT_BASE_URL = "https://api.ideogram.ai"; const REQUEST_TIMEOUT_MS = 120_000; const getIdeogramApiKey = () => validateApiKey(createIdeogramConfig()); const getDefaultIdeogramModel = () => getProviderModel("IDEOGRAM_MODEL", IdeogramModels.IDEOGRAM_V3); /** * Ideogram Provider — direct image generation with strong typography. * * Hits api.ideogram.ai/api/v1/ideogram-v3/generate. Returns image URLs; * we download and surface as base64 to keep the imageOutput contract * uniform across image-gen providers. * * @see https://developer.ideogram.ai/api-reference/api-reference/post-v-1-ideogram-v-3-generate */ export class IdeogramProvider extends BaseProvider { apiKey; baseURL; proxyFetch; constructor(modelName, sdk, _region, credentials) { const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined; super(modelName, "ideogram", validatedNeurolink); const overrideKey = credentials?.apiKey?.trim(); this.apiKey = overrideKey && overrideKey.length > 0 ? overrideKey : getIdeogramApiKey(); this.baseURL = credentials?.baseURL ?? process.env.IDEOGRAM_BASE_URL ?? IDEOGRAM_DEFAULT_BASE_URL; this.proxyFetch = createProxyFetch(); logger.debug("Ideogram Provider initialized (image-gen only)", { modelName: this.modelName, baseURL: this.baseURL, }); } getProviderName() { return this.providerName; } getDefaultModel() { return getDefaultIdeogramModel(); } supportsTools() { return false; } getAISDKModel() { throw new Error("Ideogram is an image-generation-only provider; chat completions are not available."); } async executeStream(_options, _analysisSchema) { throw new Error("Ideogram is an image-generation-only provider; streaming chat is not available."); } formatProviderError(error) { const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error"; if (message.includes("401") || message.toLowerCase().includes("unauthorized")) { return new AuthenticationError("Invalid Ideogram API key. Get one at https://developer.ideogram.ai/", "ideogram"); } if (message.includes("429") || message.toLowerCase().includes("rate limit")) { return new RateLimitError("Ideogram rate limit exceeded. Back off and retry.", "ideogram"); } if (message.includes("safety") || message.includes("is_image_safe")) { return new ProviderError("Ideogram declined the request due to safety filters. Adjust the prompt and retry.", "ideogram"); } return new ProviderError(`Ideogram error: ${message}`, "ideogram"); } async executeImageGeneration(options) { const startTime = Date.now(); // Resolve per-call credentials first, then fall back to instance-level. const perCallCreds = options.credentials?.ideogram; const effectiveApiKey = perCallCreds?.apiKey?.trim() || this.apiKey; const effectiveBaseURL = perCallCreds?.baseURL || this.baseURL; const prompt = options.prompt ?? options.input?.text ?? ""; if (!prompt.trim()) { throw new Error("Ideogram image generation requires a prompt (input.text or prompt)"); } const extras = options; const body = { prompt, model: this.modelName, magic_prompt: extras.magicPrompt ?? "AUTO", }; if (extras.aspectRatio) { body.aspect_ratio = extras.aspectRatio; } if (extras.negativePrompt) { body.negative_prompt = extras.negativePrompt; } if (extras.seed !== undefined) { body.seed = extras.seed; } if (extras.style) { body.style_type = extras.style; } const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); let response; try { response = await this.proxyFetch(`${effectiveBaseURL}/v1/ideogram-v3/generate`, { method: "POST", headers: { "Api-Key": effectiveApiKey, "Content-Type": "application/json", }, body: JSON.stringify(body), signal: controller.signal, }); } catch (err) { if (err instanceof Error && err.name === "AbortError") { throw this.formatProviderError(new Error(`Ideogram image-gen request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`)); } throw this.formatProviderError(err); } finally { clearTimeout(timeoutId); } if (!response.ok) { const text = await response.text(); throw this.formatProviderError(new Error(`Ideogram image-gen failed: ${response.status}${text}`)); } const data = (await response.json()); const url = data.data?.[0]?.url; if (!url) { throw new Error("Ideogram returned no image URL"); } // Download the image and convert to base64 to match the imageOutput // contract used by other image-gen providers. Apply a 60s timeout so the // download cannot hang indefinitely. const dlController = new AbortController(); const dlTimeoutId = setTimeout(() => dlController.abort(), 60_000); let dl; try { dl = await this.proxyFetch(url, { signal: dlController.signal }); } catch (err) { if (err instanceof Error && err.name === "AbortError") { throw new Error("Ideogram image download timed out after 60s", { cause: err, }); } throw err; } finally { clearTimeout(dlTimeoutId); } if (!dl.ok) { throw new Error(`Failed to download Ideogram image: ${dl.status}`); } const buffer = Buffer.from(await dl.arrayBuffer()); const base64 = buffer.toString("base64"); const generationTimeMs = Date.now() - startTime; logger.info(`[IdeogramProvider] Generated image (${buffer.length} bytes) in ${generationTimeMs}ms — model ${this.modelName}`); return { content: prompt, provider: this.providerName, model: this.modelName, // output: 1000 = sentinel for per-image pricing (see pricing.ts) usage: { input: 0, output: 1000, total: 1000 }, imageOutput: { base64 }, }; } async validateConfiguration() { return typeof this.apiKey === "string" && this.apiKey.trim().length > 0; } getConfiguration() { return { provider: this.providerName, model: this.modelName, defaultModel: getDefaultIdeogramModel(), baseURL: this.baseURL, }; } } export default IdeogramProvider; //# sourceMappingURL=ideogram.js.map