import { calculateCost } from "../utils/costs.js";
import { tryRequire } from "../utils/resolve-module.js";
import { StreamingAccumulator } from "../utils/streaming.js";
import { BaseAIProvider, applySessionContext, contextFields } from "./base.js";

//#region src/providers/bedrock.ts
const _resolved = tryRequire("@aws-sdk/client-bedrock-runtime");
const BEDROCK_AVAILABLE = _resolved != null;
const _BedrockModule = _resolved;
var Bedrock = class extends BaseAIProvider {
	_client;
	_bedrockMod;
	constructor(options) {
		super({
			amplitude: options.amplitude,
			privacyConfig: options.privacyConfig,
			providerName: "bedrock"
		});
		this._client = options.client;
		this._bedrockMod = options.bedrockModule ?? _BedrockModule;
	}
	async converse(params) {
		const client = this._client;
		const modelId = String(params.modelId ?? "unknown");
		if (this._bedrockMod == null) throw new Error("@aws-sdk/client-bedrock-runtime is required. Install it with: npm install @aws-sdk/client-bedrock-runtime — or pass the module directly via the bedrockModule option.");
		const ConverseCommand = this._bedrockMod.ConverseCommand;
		const command = new ConverseCommand(params);
		const startTime = performance.now();
		try {
			const response = await client.send(command);
			const latencyMs = performance.now() - startTime;
			const extracted = extractBedrockResponse(response);
			let costUsd = null;
			if (extracted.inputTokens != null && extracted.outputTokens != null) try {
				costUsd = calculateCost({
					modelName: modelId,
					inputTokens: extracted.inputTokens,
					outputTokens: extracted.outputTokens,
					cacheReadInputTokens: extracted.cacheReadTokens ?? 0,
					cacheCreationInputTokens: extracted.cacheWriteTokens ?? 0,
					defaultProvider: "bedrock"
				});
			} catch {}
			const ctx = applySessionContext();
			this._track({
				...contextFields(ctx),
				modelName: modelId,
				provider: "bedrock",
				responseContent: extracted.text,
				latencyMs,
				inputTokens: extracted.inputTokens,
				outputTokens: extracted.outputTokens,
				totalTokens: extracted.totalTokens,
				totalCostUsd: costUsd,
				finishReason: extracted.stopReason,
				toolCalls: extracted.toolCalls.length > 0 ? extracted.toolCalls : void 0,
				toolDefinitions: extractBedrockToolDefinitions(params),
				systemPrompt: extracted.systemPrompt,
				temperature: extracted.temperature,
				topP: extracted.topP,
				maxOutputTokens: extracted.maxOutputTokens,
				isStreaming: false
			});
			return response;
		} catch (error) {
			const latencyMs = performance.now() - startTime;
			const ctx = applySessionContext();
			this._track({
				...contextFields(ctx),
				modelName: modelId,
				provider: "bedrock",
				responseContent: "",
				latencyMs,
				isError: true,
				errorMessage: error instanceof Error ? error.message : String(error)
			});
			throw error;
		}
	}
	async converseStream(params) {
		const modelId = String(params.modelId ?? "unknown");
		const ctx = applySessionContext();
		const startTime = performance.now();
		try {
			const client = this._client;
			if (this._bedrockMod == null) throw new Error("@aws-sdk/client-bedrock-runtime is required. Install it with: npm install @aws-sdk/client-bedrock-runtime — or pass the module directly via the bedrockModule option.");
			const ConverseStreamCommand = this._bedrockMod.ConverseStreamCommand;
			if (ConverseStreamCommand == null) throw new Error("Bedrock SDK does not expose ConverseStreamCommand");
			const command = new ConverseStreamCommand(params);
			const response = await client.send(command);
			const stream = response.stream;
			if (!_isAsyncIterable(stream)) throw new Error("Bedrock stream response is not AsyncIterable");
			return {
				...response,
				stream: this._wrapConverseStream(modelId, params, stream, ctx)
			};
		} catch (error) {
			this._track({
				...contextFields(ctx),
				modelName: modelId,
				provider: "bedrock",
				responseContent: "",
				latencyMs: performance.now() - startTime,
				isError: true,
				errorMessage: error instanceof Error ? error.message : String(error),
				isStreaming: true
			});
			throw error;
		}
	}
	async invokeModel(params) {
		const client = this._client;
		const modelId = String(params.modelId ?? "unknown");
		if (this._bedrockMod == null) throw new Error("@aws-sdk/client-bedrock-runtime is required. Install it with: npm install @aws-sdk/client-bedrock-runtime — or pass the module directly via the bedrockModule option.");
		const InvokeModelCommand = this._bedrockMod.InvokeModelCommand;
		if (InvokeModelCommand == null) throw new Error("Bedrock SDK does not expose InvokeModelCommand");
		const command = new InvokeModelCommand(params);
		const startTime = performance.now();
		try {
			const response = await client.send(command);
			const latencyMs = performance.now() - startTime;
			const extracted = extractBedrockInvokeModelResponse(modelId, parseMaybeJson(params.body), parseMaybeJson(await decodeBedrockBlob(response.body)));
			let costUsd = null;
			if (extracted.inputTokens != null && extracted.outputTokens != null) try {
				costUsd = calculateCost({
					modelName: modelId,
					inputTokens: extracted.inputTokens,
					outputTokens: extracted.outputTokens,
					defaultProvider: "bedrock"
				});
			} catch {}
			const ctx = applySessionContext();
			this._track({
				...contextFields(ctx),
				modelName: modelId,
				provider: "bedrock",
				responseContent: extracted.text,
				latencyMs,
				inputTokens: extracted.inputTokens,
				outputTokens: extracted.outputTokens,
				totalTokens: extracted.totalTokens,
				totalCostUsd: costUsd,
				finishReason: extracted.stopReason,
				systemPrompt: extracted.systemPrompt,
				temperature: extracted.temperature,
				topP: extracted.topP,
				maxOutputTokens: extracted.maxOutputTokens,
				isStreaming: false
			});
			return response;
		} catch (error) {
			const latencyMs = performance.now() - startTime;
			const ctx = applySessionContext();
			this._track({
				...contextFields(ctx),
				modelName: modelId,
				provider: "bedrock",
				responseContent: "",
				latencyMs,
				isError: true,
				errorMessage: error instanceof Error ? error.message : String(error)
			});
			throw error;
		}
	}
	async invokeModelWithResponseStream(params) {
		const modelId = String(params.modelId ?? "unknown");
		const ctx = applySessionContext();
		const startTime = performance.now();
		try {
			const client = this._client;
			if (this._bedrockMod == null) throw new Error("@aws-sdk/client-bedrock-runtime is required. Install it with: npm install @aws-sdk/client-bedrock-runtime — or pass the module directly via the bedrockModule option.");
			const InvokeModelWithResponseStreamCommand = this._bedrockMod.InvokeModelWithResponseStreamCommand;
			if (InvokeModelWithResponseStreamCommand == null) throw new Error("Bedrock SDK does not expose InvokeModelWithResponseStreamCommand");
			const command = new InvokeModelWithResponseStreamCommand(params);
			const response = await client.send(command);
			const stream = response.body;
			if (!_isAsyncIterable(stream)) throw new Error("Bedrock stream response is not AsyncIterable");
			return {
				...response,
				body: this._wrapInvokeModelStream(modelId, params, stream, ctx)
			};
		} catch (error) {
			this._track({
				...contextFields(ctx),
				modelName: modelId,
				provider: "bedrock",
				responseContent: "",
				latencyMs: performance.now() - startTime,
				isError: true,
				errorMessage: error instanceof Error ? error.message : String(error),
				isStreaming: true
			});
			throw error;
		}
	}
	get client() {
		return this._client;
	}
	async *_wrapInvokeModelStream(modelId, params, stream, ctx) {
		const accumulator = new StreamingAccumulator();
		accumulator.model = modelId;
		const requestBody = parseMaybeJson(params.body);
		try {
			for await (const rawEvent of stream) {
				const chunk = rawEvent.chunk;
				const decoded = parseMaybeJson(decodeBedrockChunkBytes(chunk?.bytes));
				if (decoded != null) {
					const delta = extractBedrockInvokeModelStreamDelta(modelId, decoded);
					if (delta.text) accumulator.addContent(delta.text);
					if (delta.stopReason != null) accumulator.finishReason = delta.stopReason;
					if (delta.inputTokens != null || delta.outputTokens != null) accumulator.setUsage({
						inputTokens: delta.inputTokens,
						outputTokens: delta.outputTokens
					});
				}
				yield rawEvent;
			}
		} catch (error) {
			accumulator.setError(error instanceof Error ? error.message : String(error));
			throw error;
		} finally {
			const state = accumulator.getState();
			const modelName = String(accumulator.model ?? modelId);
			let costUsd = null;
			if (state.inputTokens != null && state.outputTokens != null) try {
				costUsd = calculateCost({
					modelName,
					inputTokens: state.inputTokens,
					outputTokens: state.outputTokens,
					defaultProvider: "bedrock"
				});
			} catch {}
			this._track({
				...contextFields(ctx),
				modelName,
				provider: "bedrock",
				responseContent: state.content,
				latencyMs: accumulator.elapsedMs,
				inputTokens: state.inputTokens,
				outputTokens: state.outputTokens,
				totalTokens: state.totalTokens,
				totalCostUsd: costUsd,
				finishReason: state.finishReason,
				systemPrompt: extractInvokeModelSystemPrompt(requestBody),
				providerTtfbMs: state.ttfbMs,
				isStreaming: true,
				isError: state.isError,
				errorMessage: state.errorMessage
			});
		}
	}
	async *_wrapConverseStream(modelId, params, stream, ctx) {
		const accumulator = new StreamingAccumulator();
		accumulator.model = modelId;
		try {
			for await (const rawEvent of stream) {
				const event = rawEvent;
				const delta = event.contentBlockDelta?.delta;
				if (delta?.text != null) accumulator.addContent(String(delta.text));
				const start = event.contentBlockStart?.start;
				if (start?.toolUse != null) accumulator.addToolCall(start.toolUse);
				const messageStart = event.messageStart;
				if (messageStart?.model != null) accumulator.model = String(messageStart.model);
				const messageStop = event.messageStop;
				if (messageStop?.stopReason != null) accumulator.finishReason = String(messageStop.stopReason);
				const usage = event.metadata?.usage;
				const rawInput = usage?.inputTokens;
				const cacheRead = usage?.cacheReadInputTokens;
				const cacheWrite = usage?.cacheWriteInputTokens;
				const inputTokens = rawInput != null && (cacheRead || cacheWrite) ? rawInput + (cacheRead ?? 0) + (cacheWrite ?? 0) : rawInput;
				accumulator.setUsage({
					inputTokens,
					outputTokens: usage?.outputTokens,
					totalTokens: usage?.totalTokens,
					cacheReadTokens: cacheRead,
					cacheCreationTokens: cacheWrite
				});
				yield rawEvent;
			}
		} catch (error) {
			accumulator.setError(error instanceof Error ? error.message : String(error));
			throw error;
		} finally {
			const state = accumulator.getState();
			const modelName = String(accumulator.model ?? modelId);
			let costUsd = null;
			if (state.inputTokens != null && state.outputTokens != null) try {
				costUsd = calculateCost({
					modelName,
					inputTokens: state.inputTokens,
					outputTokens: state.outputTokens,
					cacheReadInputTokens: state.cacheReadTokens ?? 0,
					cacheCreationInputTokens: state.cacheCreationTokens ?? 0,
					defaultProvider: "bedrock"
				});
			} catch {}
			this._track({
				...contextFields(ctx),
				modelName,
				provider: "bedrock",
				responseContent: state.content,
				latencyMs: accumulator.elapsedMs,
				inputTokens: state.inputTokens,
				outputTokens: state.outputTokens,
				totalTokens: state.totalTokens,
				totalCostUsd: costUsd,
				finishReason: state.finishReason,
				toolCalls: state.toolCalls.length > 0 ? state.toolCalls : void 0,
				toolDefinitions: extractBedrockToolDefinitions(params),
				systemPrompt: extractSystemPromptFromParams(params),
				temperature: params.inferenceConfig?.temperature,
				topP: params.inferenceConfig?.topP,
				maxOutputTokens: params.inferenceConfig?.maxTokens,
				providerTtfbMs: state.ttfbMs,
				isStreaming: true,
				isError: state.isError,
				errorMessage: state.errorMessage
			});
		}
	}
};
function extractBedrockResponse(response) {
	const resp = response;
	const content = resp.output?.message?.content;
	const textBlock = content?.find((b) => b.text != null);
	const toolCalls = content?.filter((b) => b.toolUse != null).map((b) => b.toolUse);
	const usage = resp.usage;
	const rawInputTokens = usage?.inputTokens;
	const cacheReadTokens = usage?.cacheReadInputTokens;
	const cacheWriteTokens = usage?.cacheWriteInputTokens;
	const inputTokens = rawInputTokens != null && (cacheReadTokens || cacheWriteTokens) ? rawInputTokens + (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0) : rawInputTokens;
	const respAny = resp;
	const metrics = respAny.metrics;
	const additionalModelResponseFields = respAny.additionalModelResponseFields;
	const inferenceConfig = respAny.inferenceConfig;
	const system = respAny.system;
	const systemPrompt = Array.isArray(system) && system.length > 0 ? system.map((s) => String(s.text ?? "")).join("") : void 0;
	return {
		text: String(textBlock?.text ?? ""),
		inputTokens,
		outputTokens: usage?.outputTokens,
		totalTokens: usage?.totalTokens,
		cacheReadTokens,
		cacheWriteTokens,
		stopReason: resp.stopReason,
		toolCalls: toolCalls ?? [],
		systemPrompt,
		temperature: inferenceConfig?.temperature ?? additionalModelResponseFields?.temperature,
		topP: inferenceConfig?.topP ?? additionalModelResponseFields?.topP,
		maxOutputTokens: inferenceConfig?.maxTokens ?? metrics?.maxOutputTokens
	};
}
function extractSystemPromptFromParams(params) {
	const system = params.system;
	if (!Array.isArray(system) || system.length === 0) return void 0;
	return system.map((s) => String(s.text ?? "")).join("");
}
function extractBedrockToolDefinitions(params) {
	const toolConfig = params.toolConfig;
	if (toolConfig == null) return void 0;
	const tools = toolConfig.tools;
	return Array.isArray(tools) && tools.length > 0 ? tools : void 0;
}
function _isAsyncIterable(value) {
	return value != null && typeof value[Symbol.asyncIterator] === "function";
}
function _sumTokens(a, b) {
	if (a == null && b == null) return void 0;
	return (a ?? 0) + (b ?? 0);
}
function parseMaybeJson(value) {
	if (value == null) return void 0;
	if (typeof value === "object") return value;
	if (typeof value !== "string") return void 0;
	try {
		const parsed = JSON.parse(value);
		return parsed != null && typeof parsed === "object" ? parsed : void 0;
	} catch {
		return;
	}
}
/**
* Decode a Bedrock InvokeModel response body. The AWS SDK returns a
* `Uint8Array`, but some transports expose a `transformToString()` blob — both
* are handled.
*/
async function decodeBedrockBlob(body) {
	if (body == null) return "";
	if (typeof body === "string") return body;
	const maybeBlob = body;
	if (typeof maybeBlob.transformToString === "function") return maybeBlob.transformToString();
	if (body instanceof Uint8Array) return new TextDecoder().decode(body);
	return "";
}
function decodeBedrockChunkBytes(bytes) {
	if (bytes == null) return "";
	if (typeof bytes === "string") try {
		return Buffer.from(bytes, "base64").toString("utf-8");
	} catch {
		return bytes;
	}
	if (bytes instanceof Uint8Array) return new TextDecoder().decode(bytes);
	return "";
}
function extractInvokeModelSystemPrompt(requestBody) {
	if (requestBody == null) return void 0;
	const system = requestBody.system;
	if (typeof system === "string") return system;
	if (Array.isArray(system)) return system.map((s) => String(s?.text ?? "")).join("");
}
/**
* Parse an InvokeModel response across the common Bedrock model families
* (Anthropic, Amazon Nova/Titan, Meta Llama, Cohere, Mistral). Defensive:
* an unrecognized family yields empty text rather than mis-parsed data.
*/
function extractBedrockInvokeModelResponse(modelId, requestBody, responseBody) {
	const req = requestBody ?? {};
	const resp = responseBody ?? {};
	const id = modelId.toLowerCase();
	const inferenceConfig = req.inferenceConfig ?? req.textGenerationConfig;
	const temperature = req.temperature ?? inferenceConfig?.temperature;
	const topP = req.top_p ?? req.topP ?? inferenceConfig?.topP ?? inferenceConfig?.top_p;
	const maxOutputTokens = req.max_tokens ?? req.max_gen_len ?? inferenceConfig?.max_new_tokens ?? inferenceConfig?.maxTokenCount ?? inferenceConfig?.maxTokens;
	const base = {
		systemPrompt: extractInvokeModelSystemPrompt(req),
		temperature,
		topP,
		maxOutputTokens
	};
	const content = resp.content;
	const usage = resp.usage;
	const looksAnthropic = Array.isArray(content) && (resp.type === "message" || usage != null && "input_tokens" in usage);
	if (id.includes("anthropic") || id.includes("claude") || looksAnthropic) {
		const text = Array.isArray(content) ? content.filter((b) => b.type === "text" || b.text != null).map((b) => String(b.text ?? "")).join("") : "";
		const inputTokens = usage?.input_tokens;
		const outputTokens = usage?.output_tokens;
		return {
			...base,
			text,
			inputTokens,
			outputTokens,
			totalTokens: _sumTokens(inputTokens, outputTokens),
			stopReason: resp.stop_reason
		};
	}
	if (id.includes("nova") || resp.output?.message != null) {
		const content$1 = (resp.output?.message)?.content;
		const text = Array.isArray(content$1) ? content$1.map((b) => String(b.text ?? "")).join("") : "";
		const usage$1 = resp.usage;
		const inputTokens = usage$1?.inputTokens;
		const outputTokens = usage$1?.outputTokens;
		return {
			...base,
			text,
			inputTokens,
			outputTokens,
			totalTokens: usage$1?.totalTokens ?? _sumTokens(inputTokens, outputTokens),
			stopReason: resp.stopReason
		};
	}
	if (id.includes("titan") || Array.isArray(resp.results)) {
		const first = resp.results?.[0];
		const inputTokens = resp.inputTextTokenCount;
		const outputTokens = first?.tokenCount;
		return {
			...base,
			text: String(first?.outputText ?? ""),
			inputTokens,
			outputTokens,
			totalTokens: _sumTokens(inputTokens, outputTokens),
			stopReason: first?.completionReason
		};
	}
	if (id.includes("llama") || resp.generation != null) {
		const inputTokens = resp.prompt_token_count;
		const outputTokens = resp.generation_token_count;
		return {
			...base,
			text: String(resp.generation ?? ""),
			inputTokens,
			outputTokens,
			totalTokens: _sumTokens(inputTokens, outputTokens),
			stopReason: resp.stop_reason
		};
	}
	if (id.includes("cohere")) {
		const generations = resp.generations;
		const text = resp.text != null ? String(resp.text) : Array.isArray(generations) ? generations.map((g) => String(g.text ?? "")).join("") : "";
		return {
			...base,
			text
		};
	}
	if (id.includes("mistral")) {
		const outputs = resp.outputs;
		return {
			...base,
			text: Array.isArray(outputs) ? outputs.map((o) => String(o.text ?? "")).join("") : "",
			stopReason: Array.isArray(outputs) ? outputs[0]?.stop_reason : void 0
		};
	}
	return {
		...base,
		text: ""
	};
}
/**
* Extract a single streaming delta from a decoded InvokeModel chunk. Returns
* incremental text plus terminal usage/stop-reason when present.
*/
function extractBedrockInvokeModelStreamDelta(modelId, chunk) {
	const id = modelId.toLowerCase();
	if (id.includes("anthropic") || id.includes("claude")) {
		const delta = chunk.delta;
		const message = chunk.message;
		const usage$1 = chunk.usage ?? delta?.usage ?? message?.usage;
		const metrics = chunk["amazon-bedrock-invocationMetrics"];
		return {
			text: String(delta?.text ?? ""),
			stopReason: delta?.stop_reason,
			inputTokens: usage$1?.input_tokens ?? metrics?.inputTokenCount,
			outputTokens: usage$1?.output_tokens ?? metrics?.outputTokenCount
		};
	}
	const usage = chunk.usage ?? chunk["amazon-bedrock-invocationMetrics"];
	const novaText = (chunk.contentBlockDelta?.delta)?.text;
	return {
		text: String(chunk.outputText ?? chunk.generation ?? novaText ?? chunk.text ?? ""),
		stopReason: chunk.completionReason ?? chunk.stop_reason ?? chunk.stopReason,
		inputTokens: usage?.inputTokenCount ?? usage?.inputTokens,
		outputTokens: usage?.outputTokenCount ?? usage?.outputTokens
	};
}

//#endregion
export { BEDROCK_AVAILABLE, Bedrock, _BedrockModule, extractBedrockInvokeModelResponse, extractBedrockInvokeModelStreamDelta, extractBedrockResponse };
//# sourceMappingURL=bedrock.js.map