import { EmptyResponseBodyError, JSONParseError, } from "@ai-sdk/provider";
import { combineHeaders, extractResponseHeaders, postJsonToApi, } from "@ai-sdk/provider-utils";
import { EventSourceParserStream, } from "eventsource-parser/stream";
import { clarkFailedResponseHandler } from "./clark-error-handler.js";
export class ClarkChatLanguageModel {
    specificationVersion = "v2";
    defaultObjectGenerationMode = "json";
    supportsImageUrls = false;
    supportedUrls = { "https:": [/^https:/] };
    modelId;
    config;
    constructor(modelId, config) {
        this.modelId = modelId;
        this.config = config;
    }
    get provider() {
        return this.config.provider;
    }
    supportsUrl(url) {
        return url.protocol === "https:";
    }
    getArgs({ prompt, maxOutputTokens, temperature, topP, topK, frequencyPenalty, presencePenalty, stopSequences, responseFormat, seed, tools, toolChoice, providerOptions, }) {
        const warnings = [];
        // v5: unsupported settings warnings
        if (topP != null) {
            warnings.push({
                type: "unsupported-setting",
                setting: "topP",
            });
        }
        if (topK != null) {
            warnings.push({
                type: "unsupported-setting",
                setting: "topK",
            });
        }
        if (frequencyPenalty != null) {
            warnings.push({
                type: "unsupported-setting",
                setting: "frequencyPenalty",
            });
        }
        if (presencePenalty != null) {
            warnings.push({
                type: "unsupported-setting",
                setting: "presencePenalty",
            });
        }
        if (stopSequences != null) {
            warnings.push({
                type: "unsupported-setting",
                setting: "stopSequences",
            });
        }
        // Handle responseFormat for structured output (generateObject)
        let outputSchema = undefined;
        if (responseFormat != null) {
            if (responseFormat.type === "json" && responseFormat.schema) {
                // Convert AI SDK v5 responseFormat to our server's outputSchema format
                outputSchema = responseFormat.schema;
            }
            else {
                warnings.push({
                    type: "unsupported-setting",
                    setting: "responseFormat",
                    details: "Only JSON response format with schema is supported",
                });
            }
        }
        if (seed != null) {
            warnings.push({
                type: "unsupported-setting",
                setting: "seed",
            });
        }
        const unifiedOptions = providerOptions?.unified;
        const baseArgs = {
            model: this.modelId,
            max_tokens: maxOutputTokens,
            messages: prompt,
            temperature: temperature,
            providerOptions,
            // Fallback control options
            ...(unifiedOptions?.forceProvider && {
                forceProvider: unifiedOptions.forceProvider,
            }),
            ...(unifiedOptions?.coreModel && { coreModel: unifiedOptions.coreModel }),
        };
        const finalArgs = {
            ...baseArgs,
            tools,
            toolChoice,
        };
        // Add outputSchema if we have one from responseFormat
        if (outputSchema !== undefined) {
            finalArgs.outputSchema = outputSchema;
        }
        return {
            args: finalArgs,
            warnings,
        };
    }
    async doGenerate(options) {
        const { args } = this.getArgs(options);
        const optionsWithMetadata = options;
        const { value } = await postJsonToApi({
            url: `${this.config.baseURL}/generate`,
            headers: combineHeaders(this.config.headers(), options.headers),
            body: {
                ...args,
                attributes: optionsWithMetadata.attributes,
            },
            failedResponseHandler: clarkFailedResponseHandler,
            successfulResponseHandler: clarkJsonResponseHandler,
            abortSignal: options.abortSignal,
            fetch: this.config.fetch,
        });
        const content = [];
        if (value.text != null && value.text !== "") {
            content.push({ type: "text", text: value.text });
        }
        else if (value.object != null) {
            content.push({ type: "text", text: JSON.stringify(value.object) });
        }
        if (Array.isArray(value.files)) {
            for (const file of value.files) {
                content.push({
                    type: "file",
                    mediaType: file.mimeType,
                    data: file.base64,
                });
            }
        }
        if (Array.isArray(value.toolCalls)) {
            for (const toolCall of value.toolCalls) {
                content.push({
                    type: "tool-call",
                    toolCallId: toolCall.toolCallId,
                    toolName: toolCall.toolName,
                    input: toolCall.args,
                });
            }
        }
        if (Array.isArray(value.sources)) {
            for (const source of value.sources) {
                content.push({
                    type: "source",
                    ...source,
                });
            }
        }
        return {
            content,
            finishReason: value.finishReason || "stop",
            usage: {
                inputTokens: value.usage?.inputTokens ??
                    value.usage?.promptTokens ??
                    0,
                outputTokens: value.usage?.outputTokens ??
                    value.usage?.completionTokens ??
                    0,
                totalTokens: (value.usage?.inputTokens ??
                    value.usage?.promptTokens ??
                    0) +
                    (value.usage?.outputTokens ??
                        value.usage?.completionTokens ??
                        0),
            },
            providerMetadata: value.providerMetadata,
            response: {
                headers: value.response?.headers,
            },
            request: {
                body: JSON.stringify(args),
            },
            warnings: value.warnings ?? [],
        };
    }
    async doStream(options) {
        const { args } = this.getArgs(options);
        const optionsWithMetadata = options;
        const body = {
            ...args,
            stream: true,
            attributes: optionsWithMetadata.attributes,
        };
        const { responseHeaders, value: response } = await postJsonToApi({
            url: `${this.config.baseURL}/stream`,
            headers: combineHeaders(this.config.headers(), options.headers),
            body,
            failedResponseHandler: clarkFailedResponseHandler,
            successfulResponseHandler: clarkStreamResponseHandler,
            abortSignal: options.abortSignal,
            fetch: this.config.fetch,
        });
        let finishReason = "unknown";
        const usage = {
            inputTokens: Number.NaN,
            outputTokens: Number.NaN,
            totalTokens: Number.NaN,
            cachedInputTokens: Number.NaN,
        };
        return {
            stream: response.pipeThrough(new TransformStream({
                transform(chunk, controller) {
                    if (chunk.success) {
                        switch (chunk.value.type) {
                            case "text-start": {
                                controller.enqueue({
                                    type: "text-start",
                                    id: chunk.value.id,
                                });
                                break;
                            }
                            case "text-delta": {
                                controller.enqueue({
                                    type: "text-delta",
                                    id: chunk.value.id,
                                    delta: chunk.value.text,
                                });
                                break;
                            }
                            case "reasoning-start": {
                                controller.enqueue({
                                    type: "reasoning-start",
                                    id: chunk.value.id,
                                    providerMetadata: chunk.value.providerMetadata,
                                });
                                break;
                            }
                            case "reasoning-delta": {
                                controller.enqueue({
                                    type: "reasoning-delta",
                                    id: chunk.value.id,
                                    delta: chunk.value.text || "",
                                    providerMetadata: chunk.value.providerMetadata,
                                });
                                break;
                            }
                            case "reasoning-end": {
                                controller.enqueue({
                                    type: "reasoning-end",
                                    id: chunk.value.id,
                                    providerMetadata: chunk.value.providerMetadata,
                                });
                                break;
                            }
                            case "tool-input-start": {
                                controller.enqueue({
                                    type: "tool-input-start",
                                    id: chunk.value.toolCallId,
                                    toolName: chunk.value.toolName,
                                });
                                break;
                            }
                            case "tool-input-delta": {
                                controller.enqueue({
                                    type: "tool-input-delta",
                                    id: chunk.value.toolCallId,
                                    delta: chunk.value.delta,
                                });
                                break;
                            }
                            case "tool-input-end": {
                                controller.enqueue({
                                    type: "tool-input-end",
                                    id: chunk.value.toolCallId,
                                });
                                break;
                            }
                            case "tool-call": {
                                controller.enqueue({
                                    type: "tool-call",
                                    toolCallId: chunk.value.toolCallId,
                                    toolName: chunk.value.toolName,
                                    input: JSON.stringify(chunk.value.input),
                                });
                                break;
                            }
                            case "file": {
                                controller.enqueue({
                                    type: "file",
                                    mediaType: chunk.value.mimeType,
                                    data: chunk.value.base64,
                                });
                                break;
                            }
                            case "source": {
                                controller.enqueue({ type: "source", ...chunk.value.source });
                                break;
                            }
                            case "text-end": {
                                controller.enqueue({
                                    type: "text-end",
                                    id: chunk.value.id,
                                });
                                break;
                            }
                            case "finish": {
                                finishReason = chunk.value.finishReason;
                                const usageData = chunk.value.usage || chunk.value.totalUsage;
                                if (usageData) {
                                    usage.inputTokens = usageData.inputTokens;
                                    usage.outputTokens = usageData.outputTokens;
                                    usage.totalTokens =
                                        usageData.inputTokens + usageData.outputTokens;
                                    usage.cachedInputTokens = usageData.cachedInputTokens;
                                }
                                break;
                            }
                            case "finish-step": {
                                finishReason = chunk.value.finishReason;
                                const usageData = chunk.value.usage || chunk.value.totalUsage;
                                if (usageData) {
                                    usage.inputTokens = usageData.inputTokens;
                                    usage.outputTokens = usageData.outputTokens;
                                    usage.totalTokens =
                                        usageData.inputTokens + usageData.outputTokens;
                                    usage.cachedInputTokens = usageData.cachedInputTokens;
                                }
                                break;
                            }
                            case "error": {
                                controller.enqueue({
                                    type: "error",
                                    error: chunk.value.error,
                                });
                                break;
                            }
                            case "object": {
                                controller.enqueue({
                                    type: "text-delta",
                                    id: chunk.value.id,
                                    delta: JSON.stringify(chunk.value.object),
                                });
                                break;
                            }
                            case "tool-result": {
                                // this might not work properly if we have server side tool results, we'll probably need to filter out specifically ones that are expected to run client side
                                // right now we drop the tool result because the one returned by the server is just an empty call
                                break;
                            }
                        }
                    }
                    else {
                        controller.enqueue({ type: "error", error: chunk.error });
                    }
                },
                flush(controller) {
                    controller.enqueue({ type: "finish", finishReason, usage });
                },
            })),
            response: { headers: responseHeaders },
            request: { body: JSON.stringify(body) },
        };
    }
}
const clarkJsonResponseHandler = async ({ response }) => {
    const responseHeaders = extractResponseHeaders(response);
    const body = await response.json();
    return {
        responseHeaders,
        value: body,
        rawValue: body,
    };
};
const clarkStreamResponseHandler = async ({ response }) => {
    const responseHeaders = extractResponseHeaders(response);
    if (response.body == null) {
        throw new EmptyResponseBodyError({});
    }
    return {
        responseHeaders,
        value: response.body
            .pipeThrough(new TextDecoderStream())
            .pipeThrough(new EventSourceParserStream())
            .pipeThrough(new TransformStream({
            async transform({ data }, controller) {
                try {
                    const json = JSON.parse(data);
                    controller.enqueue({
                        success: true,
                        value: json,
                        rawValue: json,
                    });
                }
                catch (error) {
                    controller.enqueue({
                        success: false,
                        error: JSONParseError.isInstance(error)
                            ? error
                            : new JSONParseError({
                                text: data,
                                cause: error,
                            }),
                        rawValue: data,
                    });
                }
            },
        })),
    };
};
//# sourceMappingURL=clark-language-model.js.map