import { getLogger as getLogger$1 } from "../../../../logger.js";
import { setSpanWithError as setSpanWithError$1, tracer } from "../../utils/tracer.js";
import { getStreamHelper } from "../../stream/stream-registry.js";
import { agentSessionManager } from "../../session/AgentSession.js";
import { hasToolCallWithPrefix, resolveGenerationResponse } from "../agent-types.js";
import { withJsonPostProcessing } from "../../utils/json-postprocessor.js";
import { extractTextFromParts } from "../../utils/message-parts.js";
import { configureModelSettings } from "./model-config.js";
import { handleStreamGeneration } from "../streaming/stream-handler.js";
import { V1_BREAKDOWN_SCHEMA } from "../versions/v1/PromptConfig.js";
import { handlePrepareStepCompression, handleStopWhenConditions } from "./ai-sdk-callbacks.js";
import { setupCompression } from "./compression.js";
import { buildConversationHistory, buildInitialMessages } from "./conversation-history.js";
import { formatFinalResponse } from "./response-formatting.js";
import { buildDataComponentsSchema } from "./schema-builder.js";
import { loadToolsAndPrompts } from "./tool-loading.js";
import { z } from "@hono/zod-openapi";
import { GENERATION_TYPES, SPAN_KEYS } from "@inkeep/agents-core";
import { SpanStatusCode } from "@opentelemetry/api";
import { Output, generateText, streamText } from "ai";

//#region src/domains/run/agents/generation/generate.ts
const logger = getLogger$1("Agent");
function setupGenerationContext(ctx, runtimeContext) {
	const contextId = runtimeContext?.contextId || "default";
	const taskId = runtimeContext?.metadata?.taskId || "unknown";
	const streamRequestId = runtimeContext?.metadata?.streamRequestId;
	const sessionId = streamRequestId || "fallback-session";
	ctx.streamRequestId = streamRequestId;
	ctx.streamHelper = streamRequestId ? getStreamHelper(streamRequestId) : void 0;
	if (streamRequestId && ctx.artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(streamRequestId, ctx.artifactComponents);
	const conversationId = runtimeContext?.metadata?.conversationId;
	if (conversationId) ctx.conversationId = conversationId;
	return {
		contextId,
		taskId,
		streamRequestId: streamRequestId ?? "",
		sessionId
	};
}
function buildBaseGenerationConfig(ctx, modelSettings, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, toolChoice = "auto", phase) {
	return {
		...modelSettings,
		toolChoice,
		messages,
		tools: sanitizedTools,
		prepareStep: async ({ messages: stepMessages, steps }) => {
			return await handlePrepareStepCompression(stepMessages, steps, compressor, originalMessageCount);
		},
		stopWhen: async ({ steps }) => {
			return await handleStopWhenConditions(ctx, steps);
		},
		experimental_telemetry: buildTelemetryConfig(ctx, phase),
		abortSignal: AbortSignal.timeout(timeoutMs)
	};
}
function buildTelemetryConfig(ctx, phase) {
	return {
		isEnabled: true,
		functionId: ctx.config.id,
		recordInputs: true,
		recordOutputs: true,
		metadata: {
			...phase && { phase },
			tenantId: ctx.config.tenantId,
			projectId: ctx.config.projectId,
			agentId: ctx.config.agentId,
			subAgentId: ctx.config.id,
			subAgentName: ctx.config.name,
			generationType: GENERATION_TYPES.SUB_AGENT_GENERATION,
			...ctx.conversationId && { conversationId: ctx.conversationId }
		}
	};
}
function handleGenerationError(ctx, error, span) {
	if (ctx.currentCompressor) ctx.currentCompressor.fullCleanup();
	ctx.currentCompressor = null;
	const errorToThrow = error instanceof Error ? error : new Error(String(error));
	logger.error({
		agentId: ctx.config.id,
		errorMessage: errorToThrow.message,
		errorStack: errorToThrow.stack,
		errorName: errorToThrow.name
	}, "Generation error in Agent");
	setSpanWithError$1(span, errorToThrow);
	span.end();
	throw errorToThrow;
}
async function runGenerate(ctx, userParts, runtimeContext, options) {
	const textParts = extractTextFromParts(userParts);
	const dataParts = userParts.filter((part) => part.kind === "data" && part.data != null);
	const userMessage = `${textParts}${dataParts.length > 0 ? dataParts.map((part) => {
		const metadata = part.metadata;
		return `\n\n<structured_data${metadata?.source ? ` (source: ${metadata.source})` : ""}>\n${JSON.stringify(part.data, null, 2)}\n</structured_data>`;
	}).join("") : ""}`;
	const fileParts = userParts.filter((part) => part.kind === "file");
	const conversationIdForSpan = runtimeContext?.metadata?.conversationId;
	return tracer.startActiveSpan("agent.generate", { attributes: {
		"subAgent.id": ctx.config.id,
		"subAgent.name": ctx.config.name,
		"tenant.id": ctx.config.tenantId,
		"project.id": ctx.config.projectId,
		"agent.id": ctx.config.agentId,
		"agent.name": ctx.config.agentName,
		...conversationIdForSpan ? { "conversation.id": conversationIdForSpan } : {}
	} }, async (span) => {
		const { contextId, taskId, streamRequestId, sessionId } = setupGenerationContext(ctx, runtimeContext);
		try {
			const { systemPrompt, sanitizedTools, contextBreakdown: initialContextBreakdown } = await loadToolsAndPrompts(ctx, sessionId, streamRequestId || void 0, runtimeContext);
			const { conversationHistory, contextBreakdown } = await buildConversationHistory(ctx, contextId, taskId, userMessage, streamRequestId || void 0, initialContextBreakdown);
			const breakdownAttributes = {};
			for (const componentDef of V1_BREAKDOWN_SCHEMA) breakdownAttributes[componentDef.spanAttribute] = contextBreakdown.components[componentDef.key] ?? 0;
			breakdownAttributes["context.breakdown.total_tokens"] = contextBreakdown.total;
			span.setAttributes(breakdownAttributes);
			const { primaryModelSettings, modelSettings, hasStructuredOutput, timeoutMs } = configureModelSettings(ctx);
			const inlinePdfFileCount = fileParts.filter((part) => part.file.mimeType?.toLowerCase().startsWith("application/pdf") === true).length;
			span.setAttributes({
				"input.file_count": fileParts.length,
				"input.pdf_file_count": inlinePdfFileCount
			});
			let response;
			let textResponse;
			const toolsForLlm = options?.schemaOnlyTools ? Object.fromEntries(Object.entries(sanitizedTools).map(([k, v]) => [k, {
				...v,
				execute: void 0
			}])) : sanitizedTools;
			const messages = await buildInitialMessages(systemPrompt, conversationHistory, userMessage, fileParts);
			const { originalMessageCount, compressor } = setupCompression(ctx, messages, sessionId, contextId, primaryModelSettings);
			const streamConfig = {
				...modelSettings,
				toolChoice: "auto"
			};
			const shouldStream = ctx.isDelegatedAgent ? void 0 : ctx.streamHelper;
			let dataComponentsSchema = null;
			if (hasStructuredOutput) try {
				dataComponentsSchema = buildDataComponentsSchema(ctx);
			} catch (err) {
				logger.error({
					agentId: ctx.config.id,
					err
				}, "Failed to build data components schema — continuing without structured output");
			}
			const baseConfig = buildBaseGenerationConfig(ctx, streamConfig, messages, toolsForLlm, compressor, originalMessageCount, timeoutMs, "auto", dataComponentsSchema ? "structured_generation" : void 0);
			const generationConfig = dataComponentsSchema ? {
				...baseConfig,
				output: Output.object({ schema: z.object({ dataComponents: z.array(dataComponentsSchema) }) })
			} : baseConfig;
			const nonStreamingConfig = withJsonPostProcessing(generationConfig);
			logger.info({
				agentId: ctx.config.id,
				hasStructuredOutput,
				shouldStream
			}, "Starting generation");
			let rawResponse;
			if (shouldStream) rawResponse = await handleStreamGeneration(ctx, streamText(generationConfig), sessionId, contextId, !!dataComponentsSchema);
			else rawResponse = await generateText(nonStreamingConfig);
			logger.info({
				agentId: ctx.config.id,
				hasOutput: !!rawResponse.output,
				dataComponentsCount: rawResponse.output?.dataComponents?.length ?? 0,
				finishReason: rawResponse.finishReason
			}, "Generation completed");
			response = await resolveGenerationResponse(rawResponse);
			if (hasStructuredOutput && response.output) {
				response.object = response.output;
				logger.info({
					agentId: ctx.config.id,
					dataComponentsCount: response.output?.dataComponents?.length || 0,
					dataComponentNames: response.output?.dataComponents?.map((dc) => dc.name) || []
				}, "Processing response with data components");
				textResponse = JSON.stringify(response.output, null, 2);
			} else if (hasToolCallWithPrefix("transfer_to_")(response)) textResponse = response.steps[response.steps.length - 1].text || "";
			else textResponse = response.text || "";
			const actualInputTokens = response.totalUsage?.inputTokens ?? response.usage?.inputTokens;
			if (actualInputTokens != null) span.setAttribute(SPAN_KEYS.CONTEXT_BREAKDOWN_ACTUAL_INPUT_TOKENS, actualInputTokens);
			if (response.finishReason === "other") {
				const timeoutError = /* @__PURE__ */ new Error(`Generation terminated by timeout/abort signal`);
				logger.warn({
					agentId: ctx.config.id,
					finishReason: response.finishReason,
					conversationId: conversationIdForSpan
				}, "Generation terminated by timeout/abort signal");
				span.setAttributes({ [SPAN_KEYS.GENERATION_TIMEOUT_MS]: timeoutMs });
				setSpanWithError$1(span, timeoutError);
			} else span.setStatus({ code: SpanStatusCode.OK });
			span.end();
			const formattedResponse = await formatFinalResponse(ctx, response, textResponse, sessionId, contextId);
			if (streamRequestId) {
				const generationType = response.object ? "object_generation" : "text_generation";
				agentSessionManager.recordEvent(streamRequestId, "agent_generate", ctx.config.id, {
					parts: (formattedResponse.formattedContent?.parts || []).map((part) => ({
						type: part.kind === "text" ? "text" : part.kind === "data" ? "tool_result" : "text",
						content: part.text || JSON.stringify(part.data)
					})),
					generationType
				});
			}
			if (compressor) compressor.fullCleanup();
			ctx.currentCompressor = null;
			return formattedResponse;
		} catch (error) {
			handleGenerationError(ctx, error, span);
		}
	});
}

//#endregion
export { buildBaseGenerationConfig, buildTelemetryConfig, handleGenerationError, runGenerate, setupGenerationContext };