UNPKG

@just-every/ensemble

Version:

LLM provider abstraction layer with unified streaming interface

150 lines 6.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.lumaProvider = exports.LumaProvider = void 0; const base_provider_js_1 = require("./base_provider.cjs"); const cost_tracker_js_1 = require("../utils/cost_tracker.cjs"); const fetch_with_timeout_js_1 = require("../utils/fetch_with_timeout.cjs"); const llm_logger_js_1 = require("../utils/llm_logger.cjs"); const LUMA_BASE = 'https://api.lumalabs.ai/dream-machine/v1'; function mapAspect(size) { if (!size) return undefined; const s = String(size); if (s === 'square' || s === '1024x1024' || s === '512x512' || s === '256x256') return '1:1'; if (s === 'landscape' || s === '1792x1024' || s === '1536x1024') return '16:9'; if (s === 'portrait' || s === '1024x1792' || s === '1024x1536') return '9:16'; return undefined; } class LumaProvider extends base_provider_js_1.BaseModelProvider { constructor() { super('luma'); } async *createResponseStream() { throw new Error('Luma provider does not support text streaming'); } async createImage(prompt, model, agent, opts) { const apiKey = process.env.LUMA_API_KEY; if (!apiKey) throw new Error('Luma provider: LUMA_API_KEY is not set'); const requestId = (0, llm_logger_js_1.log_llm_request)(agent.agent_id || 'default', 'luma', model, { prompt, opts }, new Date()); let success = false; try { const aspect = mapAspect(opts?.size); const body = { prompt, model: model.replace('luma-', ''), format: 'png' }; if (aspect) body.aspect_ratio = aspect; const srcs = []; if (opts?.source_images) { const arr = Array.isArray(opts.source_images) ? opts.source_images : [opts.source_images]; for (const s of arr) { const v = typeof s === 'string' ? s : s?.data || ''; if (typeof v === 'string' && /^https?:\/\//i.test(v)) srcs.push(v); } } if (srcs.length) body.image_ref = [{ image_url: srcs[0] }]; const shouldRetry = (error) => { const message = String(error?.message || '').toLowerCase(); return error?.name === 'AbortError' || message.includes('timed out'); }; const withRetries = async (fn, attempts = 3) => { let lastError; for (let attempt = 1; attempt <= attempts; attempt++) { try { return await fn(); } catch (error) { lastError = error; if (!shouldRetry(error) || attempt === attempts) throw error; await new Promise(r => setTimeout(r, attempt * 500)); } } throw lastError; }; const createTimeout = 20000; let res = await withRetries(() => (0, fetch_with_timeout_js_1.fetchWithTimeout)(`${LUMA_BASE}/generations/image`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), }, createTimeout), 3); if (!res.ok) { const text = await res.text(); if (srcs.length && (res.status === 400 || res.status === 422)) { delete body.image_ref; res = await withRetries(() => (0, fetch_with_timeout_js_1.fetchWithTimeout)(`${LUMA_BASE}/generations/image`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), }, 20000), 3); } if (!res.ok) throw new Error(`Luma create failed: ${res.status} ${text}`); } const job = await res.json(); if (job?.state === 'completed' && (job?.assets?.image || job?.assets?.image_url)) { const url = job.assets?.image || job.assets?.image_url; if (url) { cost_tracker_js_1.costTracker.addUsage({ model, image_count: 1, request_id: opts?.request_id, metadata: { source: 'luma', mode: 'sync' } }); success = true; return [url]; } } const id = job.id || job.generation_id || job.data?.id; if (!id) throw new Error('Luma: missing generation id in response'); const started = Date.now(); const timeoutMs = 120000; const intervalMs = 1500; while (true) { let r; try { r = await withRetries(() => (0, fetch_with_timeout_js_1.fetchWithTimeout)(`${LUMA_BASE}/generations/${id}`, { headers: { Authorization: `Bearer ${apiKey}` }, }, 15000), 2); } catch (error) { throw new Error(`Luma poll failed for ${id}: ${error?.message || error}`); } if (!r.ok) throw new Error(`Luma poll failed: ${r.status} ${await r.text()}`); const data = await r.json(); const state = data.state || data.status; if (state === 'completed' || state === 'succeeded' || state === 'success') { const url = data.assets?.image || data.output?.image_url || data.url; if (!url) throw new Error('Luma: completed without image url'); cost_tracker_js_1.costTracker.addUsage({ model, image_count: 1, request_id: opts?.request_id, metadata: { source: 'luma' } }); success = true; return [url]; } if (state === 'failed' || state === 'error' || state === 'canceled') { throw new Error(`Luma generation failed: ${state}`); } if (Date.now() - started > timeoutMs) throw new Error('Luma generation timed out'); await new Promise(r => setTimeout(r, intervalMs)); } } catch (err) { (0, llm_logger_js_1.log_llm_error)(requestId, err); throw err; } finally { (0, llm_logger_js_1.log_llm_response)(requestId, { ok: success }); } } } exports.LumaProvider = LumaProvider; exports.lumaProvider = new LumaProvider(); //# sourceMappingURL=luma.js.map