import { i as logger_default } from "./logger-BudIH0qK.js";
import "./fetch-D70iPJNm.js";
import { a as createEmptyTokenUsage } from "./tokenUsageUtils-BCCamZKN.js";
import "./accounts-DGRucLWV.js";
import "./telemetry-DqK1Fjch.js";
import { t as AwsBedrockGenericProvider } from "./base-D2p0nB2d.js";
import { Buffer } from "node:buffer";
import { Subject, firstValueFrom } from "rxjs";
import { take } from "rxjs/operators";

//#region src/providers/bedrock/nova-sonic.ts
function categorizeError(error) {
	const err = error instanceof Error ? error : new Error(String(error));
	const message = err.message.toLowerCase();
	if (message.includes("econnrefused") || message.includes("enotfound")) return {
		type: "connection",
		message: "Failed to connect to AWS Bedrock. Check your network and AWS configuration.",
		originalError: err
	};
	if (message.includes("timeout") || message.includes("timed out") || message.includes("aborted")) return {
		type: "timeout",
		message: "Request timed out. The operation took too long to complete.",
		originalError: err
	};
	if (message.includes("session") || message.includes("not found")) return {
		type: "session",
		message: "Session error. The bidirectional stream session may have been invalidated.",
		originalError: err
	};
	if (message.includes("json") || message.includes("parse") || message.includes("unexpected")) return {
		type: "parsing",
		message: "Failed to parse response from Bedrock. The response format was unexpected.",
		originalError: err
	};
	if (message.includes("access") || message.includes("credential") || message.includes("auth")) return {
		type: "api",
		message: "AWS authentication error. Check your credentials and permissions.",
		originalError: err
	};
	return {
		type: "unknown",
		message: err.message,
		originalError: err
	};
}
const DEFAULT_CONFIG = {
	inference: {
		maxTokens: 1024,
		topP: .9,
		temperature: .7
	},
	audio: {
		input: {
			audioType: "SPEECH",
			encoding: "base64",
			mediaType: "audio/lpcm",
			sampleRateHertz: 8e3,
			sampleSizeBits: 16,
			channelCount: 1
		},
		output: {
			audioType: "SPEECH",
			encoding: "base64",
			mediaType: "audio/lpcm",
			sampleRateHertz: 16e3,
			sampleSizeBits: 16,
			channelCount: 1,
			voiceId: "tiffany"
		}
	},
	text: { mediaType: "text/plain" }
};
var NovaSonicProvider = class extends AwsBedrockGenericProvider {
	sessions = /* @__PURE__ */ new Map();
	bedrockClient;
	config;
	constructor(modelName = "amazon.nova-sonic-v1:0", options = {}) {
		super(modelName, options);
		this.config = options.config;
	}
	async getBedrockClient() {
		if (this.bedrockClient) return this.bedrockClient;
		const sessionTimeout = this.config?.sessionTimeout ?? 3e5;
		const requestTimeout = this.config?.requestTimeout ?? 3e5;
		try {
			const { BedrockRuntimeClient } = await import("@aws-sdk/client-bedrock-runtime");
			const { NodeHttp2Handler } = await import("@smithy/node-http-handler");
			this.bedrockClient = new BedrockRuntimeClient({
				region: this.getRegion(),
				requestHandler: new NodeHttp2Handler({
					requestTimeout,
					sessionTimeout,
					disableConcurrentStreams: false,
					maxConcurrentStreams: 20
				})
			});
			return this.bedrockClient;
		} catch (err) {
			const categorized = categorizeError(err);
			logger_default.error(`Error loading AWS SDK packages: ${categorized.message}`, { error: err });
			throw new Error("The @aws-sdk/client-bedrock-runtime and @smithy/node-http-handler packages are required for Nova Sonic provider. Please install them: npm install @aws-sdk/client-bedrock-runtime @smithy/node-http-handler");
		}
	}
	createSession(sessionId = crypto.randomUUID()) {
		if (this.sessions.has(sessionId)) throw new Error(`Session ${sessionId} already exists`);
		const session = {
			queue: [],
			queueSignal: new Subject(),
			closeSignal: new Subject(),
			responseHandlers: /* @__PURE__ */ new Map(),
			isActive: true,
			audioContentId: crypto.randomUUID(),
			promptName: crypto.randomUUID()
		};
		this.sessions.set(sessionId, session);
		return session;
	}
	async sendEvent(sessionId, event) {
		if (Object.keys(event.event)[0] !== "audioInput") logger_default.debug("sendEvent: " + Object.keys(event.event)[0]);
		const session = this.sessions.get(sessionId);
		if (!session?.isActive) {
			logger_default.error(`Session ${sessionId} is not active`);
			return;
		}
		session.queue.push(event);
		session.queueSignal.next();
	}
	async endSession(sessionId) {
		const session = this.sessions.get(sessionId);
		if (!session) throw new Error(`Session ${sessionId} not found`);
		else if (!session.isActive) {
			logger_default.debug(`Session ${sessionId} is not active`);
			return;
		}
		await new Promise((resolve) => setTimeout(resolve, 1e3));
		await this.sendEvent(sessionId, { event: { promptEnd: { promptName: session.promptName } } });
		await new Promise((resolve) => setTimeout(resolve, 1e3));
		await this.sendEvent(sessionId, { event: { sessionEnd: {} } });
		session.isActive = false;
		logger_default.debug("Session closed");
	}
	async sendTextMessage(sessionId, role, prompt) {
		const session = this.sessions.get(sessionId);
		if (!session) throw new Error(`Session ${sessionId} not found`);
		const textPromptID = crypto.randomUUID();
		logger_default.debug("sendTextMessage: " + prompt);
		this.sendEvent(sessionId, { event: { contentStart: {
			promptName: session.promptName,
			contentName: textPromptID,
			type: "TEXT",
			interactive: false,
			role,
			textInputConfiguration: this.config?.textInputConfiguration || DEFAULT_CONFIG.text
		} } });
		this.sendEvent(sessionId, { event: { textInput: {
			promptName: session.promptName,
			contentName: textPromptID,
			content: prompt
		} } });
		this.sendEvent(sessionId, { event: { contentEnd: {
			promptName: session.promptName,
			contentName: textPromptID
		} } });
	}
	async sendSystemPrompt(sessionId, prompt) {
		return this.sendTextMessage(sessionId, "SYSTEM", prompt);
	}
	async sendChatTextHistory(sessionId, role, prompt) {
		return this.sendTextMessage(sessionId, role, prompt);
	}
	async callApi(prompt, context) {
		const sessionId = crypto.randomUUID();
		const session = this.createSession(sessionId);
		let assistantTranscript = "";
		let userTranscript = "";
		let audioContent = "";
		let hasAudioContent = false;
		let functionCallOccurred = false;
		logger_default.debug("prompt: " + prompt.slice(0, 1e3));
		session.responseHandlers.set("textOutput", (data) => {
			logger_default.debug("textOutput: " + JSON.stringify(data));
			if (data.role === "USER") userTranscript += data.content + "\n";
			else if (data.role === "ASSISTANT") assistantTranscript += data.content + "\n";
		});
		session.responseHandlers.set("contentEnd", async (data) => {
			logger_default.debug("contentEnd");
			if (data.stopReason === "END_TURN") await this.endSession(sessionId);
		});
		session.responseHandlers.set("audioOutput", (data) => {
			hasAudioContent = true;
			logger_default.debug("audioOutput");
			audioContent += data.content;
		});
		session.responseHandlers.set("toolUse", async (data) => {
			logger_default.debug("toolUse");
			functionCallOccurred = true;
			const result = "Tool result";
			const toolResultId = crypto.randomUUID();
			await this.sendEvent(sessionId, { event: { contentStart: {
				promptName: session.promptName,
				contentName: toolResultId,
				interactive: false,
				type: "TOOL",
				role: "TOOL",
				toolResultInputConfiguration: {
					toolUseId: data.toolUseId,
					type: "TEXT",
					textInputConfiguration: { mediaType: "text/plain" }
				}
			} } });
			await this.sendEvent(sessionId, { event: { toolResult: {
				promptName: session.promptName,
				contentName: toolResultId,
				content: JSON.stringify(result)
			} } });
			await this.sendEvent(sessionId, { event: { contentEnd: {
				promptName: session.promptName,
				contentName: toolResultId
			} } });
		});
		try {
			const bedrockClient = await this.getBedrockClient();
			const { InvokeModelWithBidirectionalStreamCommand } = await import("@aws-sdk/client-bedrock-runtime");
			const request = bedrockClient.send(new InvokeModelWithBidirectionalStreamCommand({
				modelId: this.modelName,
				body: this.createAsyncIterable(sessionId)
			}));
			logger_default.debug("Sending sessionStart");
			await this.sendEvent(sessionId, { event: { sessionStart: { inferenceConfiguration: this.config?.interfaceConfig || DEFAULT_CONFIG.inference } } });
			logger_default.debug("Sending promptStart");
			await this.sendEvent(sessionId, { event: { promptStart: {
				promptName: session.promptName,
				textOutputConfiguration: this.config?.textOutputConfiguration || DEFAULT_CONFIG.text,
				audioOutputConfiguration: this.config?.audioOutputConfiguration || DEFAULT_CONFIG.audio.output,
				...this.config?.toolConfig && { toolConfiguration: this.config?.toolConfig }
			} } });
			logger_default.debug("Sending system prompt");
			await this.sendSystemPrompt(sessionId, context?.test?.metadata?.systemPrompt || "");
			logger_default.debug("Processing conversation history");
			let promptText = prompt;
			try {
				const parsedPrompt = JSON.parse(prompt);
				if (Array.isArray(parsedPrompt)) {
					for (const [index, message] of parsedPrompt.entries()) if (message.role !== "system" && index !== parsedPrompt.length - 1) await this.sendTextMessage(sessionId, message.role.toUpperCase(), message.content[0].text);
					promptText = parsedPrompt[parsedPrompt.length - 1].content[0].text;
				}
			} catch (err) {
				logger_default.error(`Error processing conversation history: ${err}`);
			}
			logger_default.debug("Sending audioInput start");
			await this.sendEvent(sessionId, { event: { contentStart: {
				promptName: session.promptName,
				contentName: session.audioContentId,
				type: "AUDIO",
				interactive: true,
				role: "USER",
				audioInputConfiguration: this.config?.audioInputConfiguration || DEFAULT_CONFIG.audio.input
			} } });
			logger_default.debug("Sending audioInput chunks");
			const chunks = promptText?.match(/.{1,1024}/g)?.map((chunk) => Buffer.from(chunk)) || [];
			logger_default.debug("audioInput in chunks: " + chunks.length);
			for (const chunk of chunks) {
				await this.sendEvent(sessionId, { event: { audioInput: {
					promptName: session.promptName,
					contentName: session.audioContentId,
					content: chunk.toString()
				} } });
				await new Promise((resolve) => setTimeout(resolve, 30));
			}
			logger_default.debug("Sending audioInput end");
			await this.sendEvent(sessionId, { event: { contentEnd: {
				promptName: session.promptName,
				contentName: session.audioContentId
			} } });
			const response = await request;
			if (response.body) for await (const event of response.body) {
				if (!session.isActive) break;
				if (event.chunk?.bytes) {
					const data = JSON.parse(new TextDecoder().decode(event.chunk.bytes));
					const eventType = Object.keys(data.event || {})[0];
					logger_default.debug("processing eventType: " + eventType);
					const handler = session.responseHandlers.get(eventType);
					if (handler) await handler(data.event[eventType]);
				}
			}
			const audioConfig = this.config?.audioOutputConfiguration || DEFAULT_CONFIG.audio.output;
			const audioOutput = hasAudioContent && audioContent ? {
				audio: {
					data: this.convertRawToWav(Buffer.from(audioContent, "base64"), audioConfig.sampleRateHertz, audioConfig.sampleSizeBits, audioConfig.channelCount).toString("base64"),
					format: "wav",
					transcript: assistantTranscript
				},
				userTranscript
			} : {};
			return {
				output: assistantTranscript || "[No response received from API]",
				...audioOutput,
				tokenUsage: createEmptyTokenUsage(),
				cached: false,
				metadata: {
					...audioOutput,
					functionCallOccurred
				}
			};
		} catch (error) {
			const categorized = categorizeError(error);
			logger_default.error(`Nova Sonic provider error [${categorized.type}]: ${categorized.message}`, { error });
			return {
				error: categorized.message,
				metadata: { errorType: categorized.type }
			};
		} finally {
			await this.endSession(sessionId);
			this.sessions.delete(sessionId);
		}
	}
	createAsyncIterable(sessionId) {
		const session = this.sessions.get(sessionId);
		if (!session) throw new Error(`Session ${sessionId} not found`);
		return { [Symbol.asyncIterator]: () => ({ async next() {
			if (!session.isActive) return {
				done: true,
				value: void 0
			};
			if (session.queue.length === 0) try {
				await Promise.race([firstValueFrom(session.queueSignal.pipe(take(1))), firstValueFrom(session.closeSignal.pipe(take(1)))]);
			} catch {
				return {
					done: true,
					value: void 0
				};
			}
			const nextEvent = session.queue.shift();
			if (nextEvent) return {
				value: { chunk: { bytes: new TextEncoder().encode(JSON.stringify(nextEvent)) } },
				done: false
			};
			else return {
				done: true,
				value: void 0
			};
		} }) };
	}
	convertRawToWav(audioData, sampleRate = 8e3, bitsPerSample = 16, channels = 1) {
		const dataLength = audioData.length;
		const fileLength = 44 + dataLength;
		const header = Buffer.alloc(44);
		header.write("RIFF", 0);
		header.writeUInt32LE(fileLength - 8, 4);
		header.write("WAVE", 8);
		header.write("fmt ", 12);
		header.writeUInt32LE(16, 16);
		header.writeUInt16LE(1, 20);
		header.writeUInt16LE(channels, 22);
		header.writeUInt32LE(sampleRate, 24);
		header.writeUInt32LE(sampleRate * channels * bitsPerSample / 8, 28);
		header.writeUInt16LE(channels * bitsPerSample / 8, 32);
		header.writeUInt16LE(bitsPerSample, 34);
		header.write("data", 36);
		header.writeUInt32LE(dataLength, 40);
		return Buffer.concat([header, audioData]);
	}
};

//#endregion
export { NovaSonicProvider };
//# sourceMappingURL=nova-sonic-DrwXfN5u.js.map