import * as i0 from '@angular/core';
import { Injectable } from '@angular/core';
import * as i1 from '@c8y/client';
import { Observable } from 'rxjs';

var DataStreamType;
(function (DataStreamType) {
    DataStreamType["TEXT_DELTA"] = "text-delta";
    DataStreamType["TOOL_CALL"] = "tool-call";
    DataStreamType["TOOL_CALL_STREAMING"] = "tool-call-streaming";
    DataStreamType["TOOL_CALL_DELTA"] = "tool-call-delta";
    DataStreamType["TOOL_RESULT"] = "tool-result";
    DataStreamType["REASONING"] = "reasoning";
    DataStreamType["REASONING_DELTA"] = "reasoning-delta";
    DataStreamType["REDACTED_REASONING"] = "redacted-reasoning";
    DataStreamType["REASONING_SIGNATURE"] = "reasoning-signature";
    DataStreamType["FINISH_REASONING"] = "finish-reasoning";
    DataStreamType["FINISH"] = "finish";
    DataStreamType["FINISH_STEP"] = "finish-step";
    DataStreamType["ERROR"] = "error";
    DataStreamType["DATA"] = "data";
    DataStreamType["MESSAGE_ANNOTATIONS"] = "message-annotations";
    DataStreamType["SOURCE"] = "source";
    DataStreamType["FILE"] = "file";
    DataStreamType["STEP_START"] = "start-step";
    DataStreamType["STEP_FINISH"] = "finish-step";
    DataStreamType["STEP_START_V4"] = "step-start";
    DataStreamType["STEP_FINISH_V4"] = "step-finish";
})(DataStreamType || (DataStreamType = {}));

class AIService {
    constructor(client) {
        this.client = client;
        this.baseUrl = '/service/ai';
    }
    async createOrUpdateAgent(agentsDef) {
        for (const def of agentsDef.definitions) {
            const health = await this.getAgentHealth(def.name);
            let resource = `${this.baseUrl}/agent/${def.type}`;
            let method = 'POST';
            if (health.exists) {
                resource = `${this.baseUrl}/agent/${def.type}/${def.name}`;
                method = 'PUT';
            }
            const response = await this.client.fetch(resource, {
                body: JSON.stringify(def),
                method,
                headers: { 'Content-Type': 'application/json' }
            });
            if (!response.ok) {
                throw new Error(`Failed to create agent: ${response.statusText}`);
            }
        }
    }
    /**
     * Check if an agent exists.
     * @param name Agent name
     * @returns Agent health check response.
     */
    async getAgentHealth(name) {
        const response = await this.client.fetch(`${this.baseUrl}/agent/test/${name}`, {
            method: 'GET',
            headers: { 'Content-Type': 'application/json' }
        });
        if (!response.ok) {
            return {
                exists: false,
                canCreate: false,
                isProviderConfigured: false
            };
        }
        const json = await response.json();
        return json;
    }
    /**
     * Send a text message to the agent.
     * @param name Agent name
     * @param messages Messages to send
     * @param variables Variables to include
     * @returns Text response from the agent.
     */
    async text(name, messages, variables) {
        const parsedMessages = this.parseMessages(messages);
        const data = this.client.fetch(`${this.baseUrl}/agent/text/${name}`, {
            body: JSON.stringify({ messages: parsedMessages, variables }),
            method: 'POST',
            headers: { 'Content-Type': 'application/json' }
        });
        const response = await data;
        if (!response.ok) {
            throw new Error(`Failed to talk with agent: ${response.statusText}`);
        }
        const text = await response.text();
        return text;
    }
    /**
     * Stream a text message to the agent.
     * @param agentName Agent name
     * @param messages Messages to send
     * @param variables Variables to include
     * @param abortController An AbortController to cancel the request.
     * @returns An observable that emits partial AIMessage objects as they are received. The observable can be cancelled using the provided AbortController.
     * The observable will emit an error if the request fails or is aborted.
     * The observable will complete when the stream is finished.
     *
     * The messages sent to the agent can include special options:
     * - `hiddenContent`: If set, this content will be sent to the agent instead of the `content` field.
     * - `skipToLLM`: If set to true, this message will be skipped when sending to the agent.
     *
     * Example usage:
     * ```typescript
     * const abortController = new AbortController();
     * const messages: AIMessage[] = [
     *   { role: 'user', content: 'Hello' },
     *   { role: 'assistant', content: 'Hi there!' },
     *   { role: 'user', content: 'Tell me a joke.', options: { hiddenContent: 'Tell me a joke about cats.' } }
     * ];
     * const observable = aiService.stream$('my-agent', messages, {}, abortController);
     * const subscription = observable.subscribe({
     *   next: (message) => console.log('Received message part:', message),
     *   error: (err) => console.error('Error:', err),
     *   complete: () => console.log('Stream complete')
     * });
     *
     * // To cancel the request:
     * abortController.abort();
     * subscription.unsubscribe();
     * ```
     */
    async stream$(agentName, messages, variables, abortController) {
        const parsedMessages = this.parseMessages(messages);
        const response = await this.client.fetch(`${this.baseUrl}/agent/text/${agentName}?fullResponse=true`, {
            method: 'POST',
            body: JSON.stringify({ messages: parsedMessages, variables }),
            headers: {
                ...this.client.defaultHeaders,
                'content-type': 'application/json',
                accept: 'text/event-stream'
            },
            signal: abortController.signal
        });
        if (response.status > 300) {
            const data = await response.json();
            throw new Error(JSON.stringify(data, null, 2));
        }
        const stream = response.body;
        const decoder = new TextDecoder();
        return new Observable(observer => {
            if (!stream) {
                observer.error('No response body');
                throw 'No response body';
            }
            const reader = stream.getReader();
            const message = {
                role: 'assistant',
                content: '',
                steps: []
            };
            const abortHandler = () => {
                reader.cancel();
                observer.error(new DOMException('Aborted', 'AbortError'));
            };
            abortController.signal.addEventListener('abort', abortHandler);
            let buffer = '';
            const read = () => {
                reader.read().then(({ done, value }) => {
                    if (done) {
                        if (buffer.trim())
                            this.processLine(buffer, observer, message); // process any remaining data
                        observer.complete();
                        return;
                    }
                    buffer += decoder.decode(value, { stream: true });
                    const lines = buffer.split('\n\n');
                    buffer = lines.pop() || '';
                    for (const line of lines) {
                        if (line.trim())
                            this.processLine(line, observer, message);
                    }
                    read();
                });
            };
            read();
            return () => {
                abortController.signal.removeEventListener('abort', abortHandler);
                reader.cancel();
            };
        });
    }
    parseMessages(messages) {
        return messages
            .filter(message => !message.options?.skipToLLM)
            .map(message => {
            if (message.options?.hiddenContent) {
                return {
                    role: message.role,
                    content: message.options.hiddenContent
                };
            }
            return {
                role: message.role,
                content: message.content
            };
        });
    }
    processLine(line, observer, message) {
        if (!line.trim()) {
            return;
        }
        const lastStep = message.steps?.[message.steps.length - 1];
        try {
            let data = {};
            let type = '';
            try {
                data = JSON.parse(line.replace('data: ', ''));
                type = data.type;
            }
            catch (e) {
                console.error('Error parsing line', line, e);
                return;
            }
            switch (type) {
                case DataStreamType.STEP_START || DataStreamType.STEP_START_V4:
                    message.steps = message.steps || [];
                    message.steps.push({
                        type: 'text',
                        toolCalls: [],
                        toolResults: [],
                        text: ''
                    });
                    return;
                case DataStreamType.REASONING:
                    if (lastStep.reasoning === undefined) {
                        lastStep.reasoning = '';
                    }
                    lastStep.reasoning += data.text || data.textDelta;
                    observer.next(message);
                    return;
                case DataStreamType.TEXT_DELTA:
                    lastStep.text += data.text || data.textDelta;
                    message.content += data.text || data.textDelta;
                    observer.next(message);
                    return;
                case DataStreamType.TOOL_CALL:
                    lastStep.toolCalls = [data];
                    observer.next(message);
                    return;
                case DataStreamType.TOOL_RESULT:
                    lastStep.toolResults = [data];
                    observer.next(message);
                    return;
                case DataStreamType.FINISH:
                    message.finishReason = data.finishReason;
                    observer.next(message);
                    observer.complete();
                    return;
                case DataStreamType.ERROR:
                    message.finishReason = 'error';
                    const errorMessage = `<div
              class="alert alert-danger"
              role="alert"
            >
              <strong>Error</strong>
              ${data.message || 'An unknown error occurred'}
            </div>`;
                    message.content += errorMessage;
                    lastStep.text += errorMessage;
                    observer.next(message);
                    observer.error(data);
                    observer.complete();
                    return;
            }
        }
        catch (e) {
            observer.error(e);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AIService, deps: [{ token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AIService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AIService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: i1.FetchClient }] });

/**
 * Generated bundle index. Do not edit.
 */

export { AIService, DataStreamType };
//# sourceMappingURL=c8y-ngx-components-ai.mjs.map
