import { O as getEnvString, o as logger } from "./logger-CtGm8dC_.js";
import { t as fetchWithProxy, y as getRequestTimeoutMs } from "./fetch-AchwhzAC.js";
import { r as fetchWithCache } from "./cache-wQj0h878.js";
//#region src/providers/quiverai.ts
const QUIVERAI_API_BASE_URL = "https://api.quiver.ai/v1";
const QUIVERAI_DEFAULT_MODEL = "arrow-1.1";
const SAMPLING_KEYS = [
	"temperature",
	"top_p",
	"presence_penalty",
	"max_output_tokens"
];
const GENERATION_KEYS = ["instructions", "n"];
const VECTORIZE_KEYS = ["auto_crop", "target_size"];
const NON_RETRYABLE_429_CODES = new Set(["weekly_limit_exceeded"]);
const MAX_RETRY_AFTER_MS = 6e4;
const MAX_RETRIES = 3;
/**
* QuiverAI provider for SVG vector graphics.
*
* Supports two endpoints:
*  - Text → SVG via `POST /v1/svgs/generations` (mode: 'generation', the default).
*  - Image → SVG via `POST /v1/svgs/vectorizations` (mode: 'vectorize').
*
* Streams by default for faster time-to-first-token; set `stream: false`
* to enable on-disk response caching.
*/
var QuiverAiProvider = class {
	config;
	modelName;
	mode;
	apiKey;
	apiBaseUrl;
	constructor(modelName, options = {}) {
		const { config, id, env, mode = "generation" } = options;
		this.modelName = modelName;
		this.mode = mode;
		this.apiKey = config?.apiKey || env?.QUIVERAI_API_KEY || getEnvString("QUIVERAI_API_KEY") || "";
		this.apiBaseUrl = config?.apiBaseUrl || QUIVERAI_API_BASE_URL;
		if (id) this.id = () => id;
		this.config = config || {};
	}
	id() {
		return this.mode === "vectorize" ? `quiverai:vectorize:${this.modelName}` : `quiverai:${this.modelName}`;
	}
	toString() {
		return `[QuiverAI ${this.mode === "vectorize" ? "Vectorize " : ""}Provider ${this.modelName}]`;
	}
	getApiUrl() {
		const path = this.mode === "vectorize" ? "/svgs/vectorizations" : "/svgs/generations";
		return `${this.apiBaseUrl}${path}`;
	}
	getHeaders() {
		return {
			"Content-Type": "application/json",
			Authorization: `Bearer ${this.apiKey}`
		};
	}
	async callApi(prompt, context) {
		if (!this.apiKey) return { error: "QuiverAI API key is not set. Set the QUIVERAI_API_KEY environment variable or add apiKey to the provider config. Get a key at https://app.quiver.ai/settings/api-keys" };
		const config = {
			...this.config,
			...context?.prompt?.config
		};
		const useStream = config.stream !== false;
		let body;
		try {
			body = this.buildRequestBody(prompt, config, useStream);
		} catch (error) {
			return { error: `${error.message}` };
		}
		try {
			if (useStream) return await this.callApiStreaming(body);
			return await this.callApiNonStreaming(body, context);
		} catch (error) {
			logger.error(`QuiverAI API call error: ${error}`);
			return { error: `QuiverAI API call error: ${error}` };
		}
	}
	buildRequestBody(prompt, config, useStream) {
		const sampling = pickDefined(config, [...SAMPLING_KEYS]);
		if (this.mode === "vectorize") {
			const image = getConfiguredVectorizeImage(config.image) ?? parsePromptAsImage(prompt);
			return {
				model: this.modelName,
				image,
				stream: useStream,
				...sampling,
				...pickDefined(config, [...VECTORIZE_KEYS])
			};
		}
		return {
			model: this.modelName,
			prompt,
			stream: useStream,
			...sampling,
			...pickDefined(config, [...GENERATION_KEYS]),
			...config.references && { references: normalizeReferences(config.references) }
		};
	}
	async callApiNonStreaming(body, context) {
		const { data, cached, status, statusText } = await fetchWithCache(this.getApiUrl(), {
			method: "POST",
			headers: this.getHeaders(),
			body: JSON.stringify(body)
		}, getRequestTimeoutMs(), "json", context?.bustCache ?? context?.debug);
		if (status < 200 || status >= 300) {
			if (data && typeof data === "object" && "code" in data && "message" in data) return { error: formatError(data) };
			return { error: `API error: ${status} ${statusText}\n${typeof data === "string" ? data : JSON.stringify(data)}` };
		}
		const response = data;
		return {
			cached,
			output: extractSvgOutput(response),
			tokenUsage: mapTokenUsage(response.usage, cached ? 0 : 1),
			metadata: buildMetadata({
				responseId: response.id,
				credits: response.credits
			})
		};
	}
	async callApiStreaming(body) {
		let lastResp;
		for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
			const controller = new AbortController();
			const timeout = setTimeout(() => controller.abort(), getRequestTimeoutMs());
			let reader;
			try {
				lastResp = await fetchWithProxy(this.getApiUrl(), {
					method: "POST",
					headers: this.getHeaders(),
					body: JSON.stringify(body),
					signal: controller.signal
				});
				if (lastResp.status === 429 && attempt < MAX_RETRIES && !await isNonRetryable429(lastResp)) {
					const waitMs = getRetryAfterMs(lastResp.headers, attempt);
					logger.debug(`QuiverAI: rate limited, retry ${attempt + 1}/${MAX_RETRIES} in ${waitMs}ms`);
					await lastResp.body?.cancel();
					await new Promise((resolve) => setTimeout(resolve, waitMs));
					continue;
				}
				if (!lastResp.ok) return await handleStreamingError(lastResp);
				if (!lastResp.body) return { error: "QuiverAI streaming response has no body" };
				reader = lastResp.body.getReader();
				return await readSSEStream(reader);
			} finally {
				reader?.cancel().catch(() => {});
				clearTimeout(timeout);
			}
		}
		return handleStreamingError(lastResp);
	}
};
async function isNonRetryable429(resp) {
	try {
		const data = await resp.clone().json();
		return typeof data?.code === "string" && NON_RETRYABLE_429_CODES.has(data.code);
	} catch {
		return false;
	}
}
async function handleStreamingError(resp) {
	try {
		return { error: formatError(await resp.json()) };
	} catch {
		return { error: `QuiverAI API error: HTTP ${resp.status}` };
	}
}
async function readSSEStream(reader) {
	const decoder = new TextDecoder();
	let buffer = "";
	const outputs = /* @__PURE__ */ new Map();
	let totalCredits;
	let lastUsage;
	let responseId;
	const ingest = (line) => {
		const data = parseSSEData(line);
		if (!data) return;
		if (data.type === "content" && typeof data.svg === "string") {
			const key = data.id ?? `${data.index ?? outputs.size}`;
			outputs.set(key, {
				index: data.index ?? outputs.size,
				svg: data.svg,
				credits: data.credits
			});
			if (typeof data.credits === "number") totalCredits = (totalCredits ?? 0) + data.credits;
			if (!responseId && data.id) responseId = data.id;
		}
		if (data.usage) lastUsage = data.usage;
	};
	while (true) {
		const { done, value } = await reader.read();
		if (done) break;
		buffer += decoder.decode(value, { stream: true });
		const lines = buffer.split("\n");
		buffer = lines.pop() || "";
		for (const line of lines) ingest(line);
	}
	if (buffer.trim()) ingest(buffer);
	if (outputs.size === 0) return { error: "QuiverAI streaming response contained no SVG content" };
	const ordered = [...outputs.values()].sort((a, b) => a.index - b.index).map((o) => o.svg);
	return {
		output: ordered.length === 1 ? ordered[0] : ordered.join("\n\n"),
		tokenUsage: mapTokenUsage(lastUsage, 1),
		metadata: buildMetadata({
			responseId,
			credits: totalCredits
		})
	};
}
function pickDefined(obj, keys) {
	return Object.fromEntries(keys.filter((k) => obj[k] != null).map((k) => [k, obj[k]]));
}
function formatError(err) {
	const base = `${err.message} [${err.code}]`;
	return err.request_id ? `${base} (request_id: ${err.request_id})` : base;
}
function mapTokenUsage(usage, numRequests) {
	return {
		total: usage?.total_tokens || 0,
		prompt: usage?.input_tokens || 0,
		completion: usage?.output_tokens || 0,
		numRequests
	};
}
function buildMetadata(parts) {
	const meta = {};
	if (parts.responseId) meta.responseId = parts.responseId;
	if (typeof parts.credits === "number") meta.credits = parts.credits;
	return Object.keys(meta).length ? meta : void 0;
}
function getRetryAfterMs(headers, attempt) {
	const retryAfter = headers.get("retry-after");
	if (retryAfter) {
		const seconds = Number(retryAfter);
		if (!Number.isNaN(seconds)) return Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS);
		const date = new Date(retryAfter);
		if (!Number.isNaN(date.getTime())) return Math.min(Math.max(0, date.getTime() - Date.now()), MAX_RETRY_AFTER_MS);
	}
	return Math.pow(2, attempt) * 1e3;
}
function parseSSEData(line) {
	if (!line.startsWith("data:")) return null;
	const payload = line.slice(line.startsWith("data: ") ? 6 : 5).trim();
	if (!payload || payload === "[DONE]") return null;
	try {
		return JSON.parse(payload);
	} catch {
		logger.debug(`QuiverAI: failed to parse SSE data: ${payload}`);
		return null;
	}
}
function extractSvgOutput(response) {
	if (Array.isArray(response.data)) {
		const svgs = response.data.map((item) => item.svg).filter(Boolean);
		if (svgs.length === 1) return svgs[0];
		if (svgs.length > 1) return svgs.join("\n\n");
	}
	return JSON.stringify(response);
}
function normalizeReferences(refs) {
	return refs.map((ref) => typeof ref === "string" ? { url: ref } : ref);
}
const BASE64_DATA_URL_PATTERN = /^data:image\/[a-zA-Z0-9.+-]+(?:;[^,;]+)*;base64,(.+)$/i;
function normalizeImageReference(value) {
	if (!value || typeof value !== "object" || Array.isArray(value)) return null;
	const candidate = value;
	if (typeof candidate.url === "string" && candidate.url.trim()) return { url: candidate.url };
	if (typeof candidate.base64 === "string" && candidate.base64.trim()) return { base64: candidate.base64 };
	return null;
}
function getConfiguredVectorizeImage(image) {
	if (image === void 0) return;
	const normalized = normalizeImageReference(image);
	if (!normalized) throw new Error("QuiverAI vectorize `image` must contain a non-empty `url` or `base64` string.");
	return normalized;
}
function parsePromptAsImage(prompt) {
	const trimmed = prompt.trim();
	if (!trimmed) throw new Error("QuiverAI vectorize requires an image: pass a URL or base64 string as the prompt, or set `image` in the provider config.");
	const dataUrlMatch = BASE64_DATA_URL_PATTERN.exec(trimmed);
	if (dataUrlMatch) return { base64: dataUrlMatch[1] };
	if (/^data:image\//i.test(trimmed)) throw new Error("QuiverAI vectorize data URLs must be base64-encoded.");
	if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
	if (trimmed.startsWith("{")) {
		let parsed;
		try {
			parsed = JSON.parse(trimmed);
		} catch {
			return { base64: trimmed };
		}
		if (parsed && typeof parsed === "object") {
			const image = normalizeImageReference(parsed);
			if (image) return image;
			if ("image" in parsed) {
				const nestedImage = normalizeImageReference(parsed.image);
				if (nestedImage) return nestedImage;
				throw new Error("QuiverAI vectorize nested `image` must contain a non-empty `url` or `base64` string.");
			}
			throw new Error("QuiverAI vectorize JSON image input must contain a non-empty `url` or `base64` string.");
		}
	}
	return { base64: trimmed };
}
function createQuiverAiProvider(providerPath, providerOptions = {}, env) {
	const splits = providerPath.split(":");
	const modelType = splits[1];
	let mode = "generation";
	let modelStart = 1;
	if (modelType === "vectorize") {
		mode = "vectorize";
		modelStart = 2;
	} else if (modelType === "generate" || modelType === "chat") modelStart = 2;
	return new QuiverAiProvider(splits.slice(modelStart).join(":") || QUIVERAI_DEFAULT_MODEL, {
		config: providerOptions.config,
		id: providerOptions.id,
		env: providerOptions.env ?? env,
		mode
	});
}
//#endregion
export { createQuiverAiProvider };

//# sourceMappingURL=quiverai-BB3AlIys.js.map