import process from "process";
import path from "path";
import { DisposedError, EventRelay } from "lifecycle-utils";
import { removeNullFields } from "../utils/removeNullFields.js";
export class LlamaModel {
    /** @internal */ _llama;
    /** @internal */ _model;
    /** @internal */ _tokens;
    /** @internal */ _filename;
    /** @internal */ _disposedState = { disposed: false };
    /** @internal */ _typeDescription;
    /** @internal */ _trainContextSize;
    onDispose = new EventRelay();
    /**
     * > options source:
     * > [github:ggerganov/llama.cpp/llama.h](
     * > https://github.com/ggerganov/llama.cpp/blob/05816027d649f977468fc804cdb54e99eac246d1/llama.h#L161) (`struct llama_model_params`)
     * @param options
     * @param options.modelPath - path to the model on the filesystem
     * @param [options.gpuLayers] - number of layers to store in VRAM
     * @param [options.vocabOnly] - only load the vocabulary, no weights
     * @param [options.useMmap] - use mmap if possible
     * @param [options.useMlock] - force system to keep model in RAM
     */
    constructor({ llama, modelPath, gpuLayers, vocabOnly, useMmap, useMlock }) {
        this._llama = llama;
        this._model = new this._llama._bindings.AddonModel(path.resolve(process.cwd(), modelPath), removeNullFields({
            gpuLayers,
            vocabOnly,
            useMmap,
            useMlock
        }));
        this._tokens = LlamaModelTokens._create(this._model, this._disposedState);
        this._filename = path.basename(modelPath);
        this.tokenize = this.tokenize.bind(this);
        this.detokenize = this.detokenize.bind(this);
    }
    dispose() {
        if (this._disposedState.disposed)
            return;
        this.onDispose.dispatchEvent();
        this._model.dispose();
        this._disposedState.disposed = true;
    }
    /** @hidden */
    [Symbol.dispose]() {
        this.dispose();
    }
    get disposed() {
        return this._disposedState.disposed;
    }
    get tokens() {
        return this._tokens;
    }
    get filename() {
        return this._filename;
    }
    tokenize(text, specialTokens = false) {
        this._ensureNotDisposed();
        if (text === "")
            return [];
        if (specialTokens === "builtin") {
            const builtinToken = text;
            switch (builtinToken) {
                case "BOS": return this.tokens.bos == null ? [] : [this.tokens.bos];
                case "EOS": return this.tokens.eos == null ? [] : [this.tokens.eos];
                case "NL": return this.tokens.nl == null ? [] : [this.tokens.nl];
            }
            void (builtinToken);
            throw new Error(`Unknown builtin special token: ${builtinToken}`);
        }
        return Array.from(this._model.tokenize(text, specialTokens));
    }
    /** Transform tokens into text */
    detokenize(tokens) {
        this._ensureNotDisposed();
        if (tokens.length === 0)
            return "";
        return this._model.detokenize(Uint32Array.from(tokens));
    }
    /** @hidden `ModelTypeDescription` type alias is too long in the documentation */
    get typeDescription() {
        this._ensureNotDisposed();
        if (this._typeDescription == null)
            this._typeDescription = this._model.getModelDescription();
        return this._typeDescription;
    }
    /** The context size the model was trained on */
    get trainContextSize() {
        this._ensureNotDisposed();
        if (this._trainContextSize == null)
            this._trainContextSize = this._model.getTrainContextSize();
        return this._trainContextSize;
    }
    /** @internal */
    _ensureNotDisposed() {
        if (this._disposedState.disposed)
            throw new DisposedError();
    }
}
export class LlamaModelTokens {
    /** @internal */ _model;
    /** @internal */ _disposedState;
    /** @internal */ _infillTokens;
    /** @internal */ _bosToken;
    /** @internal */ _eosToken;
    /** @internal */ _nlToken;
    /** @internal */ _bosString;
    /** @internal */ _eosString;
    /** @internal */ _nlString;
    constructor(model, disposedState) {
        this._model = model;
        this._disposedState = disposedState;
    }
    /**
     * @returns infill tokens
     */
    get infill() {
        this._ensureNotDisposed();
        if (this._infillTokens == null)
            this._infillTokens = LlamaModelInfillTokens._create(this._model, this._disposedState);
        return this._infillTokens;
    }
    /**
     * @returns The BOS (Beginning Of Sequence) token.
     */
    get bos() {
        this._ensureNotDisposed();
        if (this._bosToken == null)
            this._bosToken = this._model.tokenBos();
        if (this._bosToken === -1)
            return null;
        return this._bosToken;
    }
    /**
     * @returns The EOS (End Of Sequence) token.
     */
    get eos() {
        this._ensureNotDisposed();
        if (this._eosToken == null)
            this._eosToken = this._model.tokenEos();
        if (this._eosToken === -1)
            return null;
        return this._eosToken;
    }
    /**
     * @returns The NL (New Line) token.
     */
    get nl() {
        this._ensureNotDisposed();
        if (this._nlToken == null)
            this._nlToken = this._model.tokenNl();
        if (this._nlToken === -1)
            return null;
        return this._nlToken;
    }
    /**
     * @returns The BOS (Beginning Of Sequence) token as a string.
     */
    get bosString() {
        this._ensureNotDisposed();
        const bosToken = this.bos;
        if (bosToken == null)
            return null;
        if (this._bosString == null)
            this._bosString = this._model.getTokenString(bosToken);
        return this._bosString;
    }
    /**
     * @returns The EOS (End Of Sequence) token as a string.
     */
    get eosString() {
        this._ensureNotDisposed();
        const eosToken = this.eos;
        if (eosToken == null)
            return null;
        if (this._eosString == null)
            this._eosString = this._model.getTokenString(eosToken);
        return this._eosString;
    }
    /**
     * @returns The NL (New Line) token as a string.
     */
    get nlString() {
        this._ensureNotDisposed();
        const nlToken = this.nl;
        if (nlToken == null)
            return null;
        if (this._nlString == null)
            this._nlString = this._model.getTokenString(nlToken);
        return this._nlString;
    }
    /** @internal */
    _ensureNotDisposed() {
        if (this._disposedState.disposed)
            throw new DisposedError();
    }
    /** @internal */
    static _create(model, disposedState) {
        return new LlamaModelTokens(model, disposedState);
    }
}
export class LlamaModelInfillTokens {
    /** @internal */ _model;
    /** @internal */ _disposedState;
    /** @internal */ _prefixToken;
    /** @internal */ _middleToken;
    /** @internal */ _suffixToken;
    /** @internal */ _eotToken;
    /** @internal */ _prefixString;
    /** @internal */ _middleString;
    /** @internal */ _suffixString;
    /** @internal */ _eotString;
    constructor(model, disposedState) {
        this._model = model;
        this._disposedState = disposedState;
    }
    /**
     * @returns The beginning of infill prefix token.
     */
    get prefix() {
        this._ensureNotDisposed();
        if (this._prefixToken == null)
            this._prefixToken = this._model.prefixToken();
        if (this._prefixToken === -1)
            return null;
        return this._prefixToken;
    }
    /**
     * @returns The beginning of infill middle token.
     */
    get middle() {
        this._ensureNotDisposed();
        if (this._middleToken == null)
            this._middleToken = this._model.middleToken();
        if (this._middleToken === -1)
            return null;
        return this._middleToken;
    }
    /**
     * @returns The beginning of infill suffix token.
     */
    get suffix() {
        this._ensureNotDisposed();
        if (this._suffixToken == null)
            this._suffixToken = this._model.suffixToken();
        if (this._suffixToken === -1)
            return null;
        return this._suffixToken;
    }
    /**
     * @returns End of infill middle token (End Of Text).
     */
    get eot() {
        this._ensureNotDisposed();
        if (this._eotToken == null)
            this._eotToken = this._model.eotToken();
        if (this._eotToken === -1)
            return null;
        return this._eotToken;
    }
    /**
     * @returns The beginning of infill prefix token as a string.
     */
    get prefixString() {
        this._ensureNotDisposed();
        const prefixToken = this.prefix;
        if (prefixToken == null)
            return null;
        if (this._prefixString == null)
            this._prefixString = this._model.getTokenString(prefixToken);
        return this._prefixString;
    }
    /**
     * @returns The beginning of infill middle token as a string.
     */
    get middleString() {
        this._ensureNotDisposed();
        const middleToken = this.middle;
        if (middleToken == null)
            return null;
        if (this._middleString == null)
            this._middleString = this._model.getTokenString(middleToken);
        return this._middleString;
    }
    /**
     * @returns The beginning of infill suffix token as a string.
     */
    get suffixString() {
        this._ensureNotDisposed();
        const suffixToken = this.suffix;
        if (suffixToken == null)
            return null;
        if (this._suffixString == null)
            this._suffixString = this._model.getTokenString(suffixToken);
        return this._suffixString;
    }
    /**
     * @returns End of infill middle token (End Of Text) as a string.
     */
    get eotString() {
        this._ensureNotDisposed();
        const eotToken = this.eot;
        if (eotToken == null)
            return null;
        if (this._eotString == null)
            this._eotString = this._model.getTokenString(eotToken);
        return this._eotString;
    }
    /** @internal */
    _ensureNotDisposed() {
        if (this._disposedState.disposed)
            throw new DisposedError();
    }
    /** @internal */
    static _create(model, disposedState) {
        return new LlamaModelInfillTokens(model, disposedState);
    }
}
//# sourceMappingURL=LlamaModel.js.map