<project title="Lmstudio Js" summary="The purpose of `lmstudio-js` is to provide a robust, TypeScript-based SDK that allows developers to interact with local LLMs running on LM Studio. It enables users to perform chat completions, text predictions, tool-based agent interactions, model loading/unloading, and embedding generation—all locally. The SDK is designed for developers who need fine-grained control over LLM behavior, including configuration parameters and model-specific optimizations. It also supports both browser and Node.js environments, making it versatile for web and server-side applications.">**Remember:**
- LMStudioClient
- LLM
- ToolUseSession
- ModelManifest
- PluginContext
- EmbeddingModel<docs><doc title="Denoplugininstaller" desc="install &amp; quickstart.">import { SimpleLogger } from "@lmstudio/lms-common";
import Arborist from "@npmcli/arborist";
import { join } from "path";
import { generateEntryFileAt } from "./generateEntryFile.js";

export interface DenoPluginInstallerInstallOpts {
  /**
   * Only installs dependencies. Does not generate the entry file.
   */
  dependenciesOnly?: boolean;
  /**
   * Do not install dependencies, only create the entry file.
   */
  skipDependencies?: boolean;
  npmRegistry?: string;
  logger?: SimpleLogger;
}

export class DenoPluginInstaller {
  public async install(
    pluginPath: string,
    {
      dependenciesOnly = false,
      skipDependencies = false,
      npmRegistry,
      logger = new SimpleLogger("DenoPluginInstaller"),
    }: DenoPluginInstallerInstallOpts = {},
  ) {
    if (!skipDependencies) {
      const arb = new Arborist({
        path: pluginPath,
        registry: npmRegistry,
        ignoreScripts: true,
      });
      logger.info(`Installing dependencies in ${pluginPath}...`);
      await arb.reify();
    }
    if (dependenciesOnly) {
      return;
    }
    const cacheFolderPath = join(pluginPath, ".lmstudio");
    logger.info(`Creating entry file in ${cacheFolderPath}...`);
    const entryFilePath = join(cacheFolderPath, "production.ts");
    await generateEntryFileAt(entryFilePath, {});
  }
}</doc><doc title="Nodeplugininstaller" desc="install &amp; quickstart.">import { SimpleLogger } from "@lmstudio/lms-common";
import Arborist from "@npmcli/arborist";
import { join } from "path";
import { createEsBuildArgs } from "./esbuildArgs.js";
import { generateEntryFileAt } from "./generateEntryFile.js";
import { UtilBinary } from "./UtilBinary.js";

export interface NodePluginInstallerInstallOpts {
  /**
   * Only installs dependencies. Does not build the plugins.
   */
  dependenciesOnly?: boolean;
  /**
   * Do not install dependencies, only create the entry file and build the plugin.
   */
  skipDependencies?: boolean;
  /**
   * If provided, uses the util binary from this folder instead.
   */
  utilsFolderPathOverride?: string;
  npmRegistry?: string;
  logger?: SimpleLogger;
}

export class NodePluginInstaller {
  /**
   * Creates the esbuild args
   */
  private async createEsBuildArgs(cacheFolderPath: string, entryFilePath: string) {
    const args = createEsBuildArgs({
      entryPath: entryFilePath,
      outPath: join(cacheFolderPath, "production.js"),
      production: true,
    });
    return args;
  }
  public async install(
    pluginPath: string,
    {
      dependenciesOnly = false,
      skipDependencies = false,
      utilsFolderPathOverride,
      npmRegistry,
      logger = new SimpleLogger("DenoPluginInstaller"),
    }: NodePluginInstallerInstallOpts = {},
  ) {
    if (!skipDependencies) {
      const arb = new Arborist({
        path: pluginPath,
        registry: npmRegistry,
        ignoreScripts: true,
      });
      logger.info(`Installing dependencies in ${pluginPath}...`);
      await arb.reify();
    }
    if (dependenciesOnly) {
      return;
    }
    const cacheFolderPath = join(pluginPath, ".lmstudio");
    logger.info(`Creating entry file in ${cacheFolderPath}...`);
    const entryFilePath = join(cacheFolderPath, "entry.ts");
    await generateEntryFileAt(entryFilePath, {});
    const args = await this.createEsBuildArgs(cacheFolderPath, entryFilePath);
    const esbuild = new UtilBinary("esbuild", { utilsFolderPathOverride });
    logger.info(`Building plugin with esbuild...`);
    await esbuild.check();
    await esbuild.exec(args);
  }
}</doc><doc title="Darwinorlinux" desc="install &amp; quickstart.">import { makeTitledPrettyError, text } from "@lmstudio/lms-common";
import chalk from "chalk";
// import inquirer from "inquirer";
import inquirer from "inquirer";
import { execSync } from "node:child_process";
import { access, readFile } from "node:fs/promises";
import os from "node:os";
import { join } from "node:path";
import { type InstallCliOpts } from ".";

interface ShellInstallationInfo {
  shellName: string;
  configFileName: string;
  commandToAddComment: string;
  commandToAddPath: string;
}

const shellInstallationInfo: Array<ShellInstallationInfo> = [
  {
    shellName: "sh",
    configFileName: ".profile",
    commandToAddComment:
      "echo '' >> ~/.profile && echo '# Added by LM Studio CLI tool (lms)' >> ~/.profile",
    commandToAddPath: "echo 'export PATH=\"$PATH:<TARGET>\"' >> ~/.profile",
  },
  {
    shellName: "bash",
    configFileName: ".bashrc",
    commandToAddComment:
      "echo '' >> ~/.bashrc && echo '# Added by LM Studio CLI tool (lms)' >> ~/.bashrc",
    commandToAddPath: "echo 'export PATH=\"$PATH:<TARGET>\"' >> ~/.bashrc",
  },
  {
    shellName: "bash",
    configFileName: ".bash_profile",
    commandToAddComment:
      "echo '' >> ~/.bash_profile && echo '# Added by LM Studio CLI tool (lms)' >> ~/.bash_profile",
    commandToAddPath: "echo 'export PATH=\"$PATH:<TARGET>\"' >> ~/.bash_profile",
  },
  {
    shellName: "zsh",
    configFileName: ".zshrc",
    commandToAddComment:
      "echo '' >> ~/.zshrc && echo '# Added by LM Studio CLI tool (lms)' >> ~/.zshrc",
    commandToAddPath: "echo 'export PATH=\"$PATH:<TARGET>\"' >> ~/.zshrc",
  },
  {
    shellName: "fish",
    configFileName: ".config/fish/config.fish",
    commandToAddComment:
      "echo '' >> ~/.config/fish/config.fish && echo '# Added by LM Studio CLI tool (lms)' >> ~/.config/fish/config.fish",
    commandToAddPath: "echo 'set -gx PATH $PATH <TARGET>' >> ~/.config/fish/config.fish",
  },
  {
    shellName: "csh",
    configFileName: ".cshrc",
    commandToAddComment:
      "echo '' >> ~/.cshrc && echo '# Added by LM Studio CLI tool (lms)' >> ~/.cshrc",
    commandToAddPath: "echo 'setenv PATH \"$PATH:<TARGET>\"' >> ~/.cshrc",
  },
  {
    shellName: "tcsh",
    configFileName: ".tcshrc",
    commandToAddComment:
      "echo '' >> ~/.tcshrc && echo '# Added by LM Studio CLI tool (lms)' >> ~/.tcshrc",
    commandToAddPath: "echo 'setenv PATH \"$PATH:<TARGET>\"' >> ~/.tcshrc",
  },
];

export async function installCliDarwinOrLinux(path: string, { skipConfirmation }: InstallCliOpts) {
  const detectedShells: Array<ShellInstallationInfo> = [];
  const detectedAlreadyInstalledShells: Array<ShellInstallationInfo> = [];
  for (const shell of shellInstallationInfo) {
    const configPath = join(os.homedir(), shell.configFileName);
    try {
      await access(configPath);
    } catch (e) {
      continue;
    }
    const content = await readFile(configPath, { encoding: "utf8" });
    if (content.includes(path)) {
      detectedAlreadyInstalledShells.push(shell);
    } else {
      detectedShells.push(shell);
    }
  }

  if (detectedShells.length === 0) {
    if (detectedAlreadyInstalledShells.length === 0) {
      throw makeTitledPrettyError(
        "Unable to find any shell configuration files",
        text`
          We couldn't find any shell configuration file in your home directory.

          To complete the installation manually, please try to add the following directory to the
          PATH environment variable:

              ${chalk.yellowBright(path)}
        `,
      );
    } else {
      console.info(
        text`
          ${chalk.greenBright("  ✓ Already Installed  ")}

          LM Studio CLI tool is already installed for the following shells:

          ${detectedAlreadyInstalledShells
            .map(shell =>
              chalk.cyanBright(
                `    · ${shell.shellName} ${chalk.gray(`(~/${shell.configFileName})`)}`,
              ),
            )
            .join("\n")}

          If your shell is not listed above, please try to add the following directory to the PATH
          environment variable:

              ${chalk.yellowBright(path)}

            ${chalk.gray(text`
              (i) If you are having trouble running the CLI tool, please open a new terminal. and
              try again.
            `)}
          `,
      );
      return;
    }
  }

  const commandsToRun: Array<string> = [];
  const commandsToRunFormatted: Array<string> = [];

  for (const shell of detectedShells) {
    const command = shell.commandToAddPath.replace("<TARGET>", path);
    commandsToRun.push(shell.commandToAddComment);
    commandsToRun.push(command);
    commandsToRunFormatted.push(`    ${command} ${chalk.gray(`# for ${shell.shellName}`)}`);
  }

  if (!skipConfirmation) {
    console.info(
      text`
        We are about to run the following commands to install the LM Studio CLI tool
        (lms).

        ${chalk.cyanBright(commandsToRunFormatted.join("\n"))}

        It will add the path ${chalk.greenBright(path)} to the PATH environment variable.
      `,
    );

    const { cont } = await inquirer.createPromptModule({
      output: process.stderr,
    })([
      {
        type: "confirm",
        name: "cont",
        message: chalk.yellowBright("Do you want to continue?"),
        default: false,
      },
    ]);

    if (!cont) {
      console.info(chalk.greenBright("Installation aborted. No changes were made."));
      return;
    }
  }

  execSync(commandsToRun.join(" && "));

  console.info(
    text`
      ${chalk.greenBright("  ✓ Installation Completed  ")}

        ${chalk.cyanBright(text`
          (i) You need to open a new terminal window for these changes to take effect.
        `)}

      The LM Studio CLI tool (lms) has been successfully installed. To test it, run the following
      command in a new terminal window:

          ${chalk.yellowBright("lms")}
    `,
  );
}</doc><doc title="Index" desc="install &amp; quickstart.">import { makeTitledPrettyError, text } from "@lmstudio/lms-common";
import { findLMStudioHome } from "@lmstudio/lms-common-server";
import chalk from "chalk";
import { stat } from "node:fs/promises";
import { join } from "node:path";
import { platform } from "node:process";
import { installCliDarwinOrLinux } from "./darwinOrLinux.js";
import { installCliWin32 } from "./win32.js";

export interface InstallCliOpts {
  skipConfirmation?: boolean;
}

export async function installCli(opts: InstallCliOpts = {}) {
  const targetPath = join(`${findLMStudioHome()}`, `bin`);
  const pathStat = await stat(targetPath).catch(() => null);
  if (pathStat === null || pathStat.isDirectory() === false) {
    throw makeTitledPrettyError(
      "Cannot find LM Studio installation",
      text`
        LM Studio CLI (lms) is shipped with the latest version of LM Studio. Please install LM
        Studio first. You can download it from:

            ${chalk.cyanBright("https://lmstudio.ai/")}

        If you have just installed LM Studio, please run it at least once before running this tool
        again.
      `,
    );
  }
  if (platform === "win32") {
    await installCliWin32(targetPath, opts);
    // await installCliWin32(targetPath);
  } else if (platform === "linux" || platform === "darwin") {
    await installCliDarwinOrLinux(targetPath, opts);
  } else {
    throw makeTitledPrettyError(
      `Your platform (${chalk.yellowBright(platform)}) is not support by this tool`,
      text`
        To complete the setup manually, please try to add the following directory to the PATH
        environment variable:

            ${chalk.yellowBright(targetPath)}
      `,
    );
  }
}</doc><doc title="Win32" desc="install &amp; quickstart.">import { text } from "@lmstudio/lms-common";
import chalk from "chalk";
// import inquirer from "inquirer";
import inquirer from "inquirer";
import { execSync } from "node:child_process";
import { access } from "node:fs/promises";
import { type InstallCliOpts } from ".";

async function getPowershellPath() {
  // Common PowerShell paths on Windows
  const possiblePaths = [
    "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
    "C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe",
  ];

  // Check if PowerShell exists in common paths
  for (const p of possiblePaths) {
    try {
      await access(p);
      return p; // Return the valid path
    } catch (e) {
      continue; // Check the next path
    }
  }

  return "powershell"; // Default to PATH
}
export async function installCliWin32(path: string, { skipConfirmation }: InstallCliOpts) {
  const powershellPath = await getPowershellPath();
  const previousPath = execSync(`[Environment]::GetEnvironmentVariable('PATH', 'User')`, {
    shell: powershellPath,
    encoding: "utf8", // Ensure the output is a string
  }).trimEnd();
  if (previousPath.includes(path)) {
    console.info(
      text`
        ${chalk.greenBright("  ✓ Already Installed  ")}

        The path ${chalk.greenBright(path)} is already in the PATH environment variable.

          ${chalk.cyanBright(text`
            (i) If Windows cannot find the CLI tool, please try again in a new terminal window.
          `)}

          ${chalk.cyanBright(text`
            (i) If you are using an integrated terminal in an editor (such as VS Code), please try
            to restart the editor.
          `)}
      `,
    );
    return;
  }

  const command = `$path = [Environment]::GetEnvironmentVariable('PATH', 'User');
$path += ";${path}";
[Environment]::SetEnvironmentVariable('PATH', $path, 'User');`;

  if (!skipConfirmation) {
    console.info(
      text`
        We are about to run the following powershell commands to install the LM Studio CLI tool
        (lms).

        ${chalk.cyanBright("    " + command.split("\n").join("\n    "))}

        It will add the path ${chalk.greenBright(path)} to the PATH environment variable.
      `,
    );
    const { cont } = await inquirer.createPromptModule({
      output: process.stderr,
    })([
      {
        type: "confirm",
        name: "cont",
        message: chalk.yellowBright("Do you want to continue?"),
        default: false,
      },
    ]);

    if (!cont) {
      console.info(chalk.greenBright("Installation aborted. No changes were made."));
      return;
    }
  }

  execSync(command, { shell: powershellPath });

  console.info(
    text`
      ${chalk.greenBright("  ✓ Installation Completed  ")}

        ${chalk.cyanBright(text`
          (i) You need to open a new terminal window for these changes to take effect.
        `)}

        ${chalk.cyanBright(text`
          (i) If you are using an integrated terminal in an editor (such as VS Code), please try
          to restart the editor.
        `)}

      The LM Studio CLI tool (lms) has been successfully installed. To test it, run the following
      command in a new terminal window:

          ${chalk.yellowBright("lms")}
    `,
  );
}</doc></docs><api><doc title="README" desc="install &amp; quickstart."><p align="center">
  
  <picture> 
    <source media="(prefers-color-scheme: dark)" srcset="https://github.com/lmstudio-ai/lmstudio.js/assets/3611042/dd0b2298-beec-4dfe-9019-7d4dc5427e40">
    <source media="(prefers-color-scheme: light)" srcset="https://github.com/lmstudio-ai/lmstudio.js/assets/3611042/70f24e8f-302b-465d-8607-8c3f36cd4934">
    <img alt="lmstudio javascript library logo" src="https://github.com/lmstudio-ai/lmstudio.js/assets/3611042/70f24e8f-302b-465d-8607-8c3f36cd4934" width="290" height="86" style="max-width: 100%;">
  </picture>
  
</p>
<p align="center"><code>Use local LLMs in JS/TS/Node</code></p>
<p align="center"><i>LM Studio Client SDK</i></p>

`lmstudio-js` is LM Studio's official JavaScript/TypeScript client SDK, it allows you to

- Use LLMs to [respond in chats](https://lmstudio.ai/docs/typescript/llm-prediction/chat-completion) or predict [text completions](https://lmstudio.ai/docs/typescript/llm-prediction/completion)
- Define functions as tools, and turn LLMs into [autonomous agents](https://lmstudio.ai/docs/typescript/agent/act) that run completely locally
- [Load](https://lmstudio.ai/docs/typescript/manage-models/loading), [configure](https://lmstudio.ai/docs/typescript/llm-prediction/parameters), and [unload](https://lmstudio.ai/docs/typescript/manage-models/loading) models from memory
- Supports both browser and any Node-compatible environments
- Generate embeddings for text, and more!

> Using python? See [lmstudio-python](https://github.com/lmstudio-ai/lmstudio-python)

## Installation

```bash
npm install @lmstudio/sdk --save
```

## Quick Example

```ts
import { LMStudioClient } from "@lmstudio/sdk";
const client = new LMStudioClient();

const model = await client.llm.model("llama-3.2-1b-instruct");
const result = await model.respond("What is the meaning of life?");

console.info(result.content);
```

For more examples and documentation, visit [lmstudio-js docs](https://lmstudio.ai/docs/typescript).

## Why use `lmstudio-js` over `openai` sdk?

Open AI's SDK is designed to use with Open AI's proprietary models. As such, it is missing many features that are essential for using LLMs in a local environment, such as:

- Managing loading and unloading models from memory
- Configuring load parameters (context length, gpu offload settings, etc.)
- Speculative decoding
- Getting information (such as context length, model size, etc.) about a model
- ... and more

In addition, while `openai` sdk is automatically generated, `lmstudio-js` is designed from ground-up to be clean and easy to use for TypeScript/JavaScript developers.

## Contributing

You can build the project locally by following these steps:

```bash
git clone https://github.com/lmstudio-ai/lmstudio-js.git --recursive
cd lmstudio-js
npm install
npm run build
```

## Community

<p>Discuss all things lmstudio-js in <a href="https://discord.gg/aPQfnNkxGC">#dev-chat</a> in LM Studio's Community Discord server.</p>
<a href="https://discord.gg/aPQfnNkxGC"><img alt="Discord" src="https://img.shields.io/discord/1110598183144399058?logo=discord&style=flat&logoColor=white"></a></doc><doc title="Removesourcemapcomments" desc="docs page.">// We don't ship sourcemaps, so we will remove those comments to prevent error messages

const fs = require("fs");
const path = require("path");

const filePath = path.join(__dirname, "dist/index.js");
const content = fs.readFileSync(filePath, "utf8");
const newContent = content.replace(/\/\/# sourceMappingURL=.*\.js\.map/g, "//");
fs.writeFileSync(filePath, newContent, "utf8");</doc><doc title="Rollup.Config" desc="docs page.">const { nodeResolve } = require("@rollup/plugin-node-resolve");
const path = require("path");
const commonjs = require("@rollup/plugin-commonjs");
const json = require("@rollup/plugin-json");

module.exports = {
  input: path.join(__dirname, "ts-out", "index.js"),
  output: [
    {
      file: path.join(__dirname, "dist", "index.mjs"),
      format: "esm",
    },
    {
      file: path.join(__dirname, "dist", "index.cjs"),
      format: "cjs",
    },
  ],
  context: "globalThis",
  plugins: [
    nodeResolve({
      extensions: [".ts", ".tsx", ".js", ".jsx"],
    }),
    commonjs(),
    json(),
  ],
  external: ["process", "chalk", "zod", "zod-to-json-schema", "@lmstudio/lms-isomorphic"],
};</doc><doc title="Apiserverports" desc="API reference.">export const apiServerPorts = [41343, 52993, 16141, 39414, 22931];</doc><doc title="Exportedtypes" desc="docs page.">export type {
  ActResult,
  ArtifactDownloadPlanner,
  ArtifactDownloadPlannerDownloadOpts,
  BaseController,
  BaseLoadModelOpts,
  BasePredictionResult,
  ChatAppendOpts,
  ChatInput,
  ChatLike,
  ChatMessageInput,
  ChatMessageLike,
  ConfigSchematics,
  ConfigSchematicsBuilder,
  ContentBlockAppendTextOpts,
  ContentBlockAppendToolRequestOpts,
  ContentBlockAppendToolResultOpts,
  ContentBlockReplaceToolRequestOpts,
  CreateArtifactDownloadPlannerOpts,
  CreateCitationBlockOpts,
  CreateContentBlockOpts,
  DiagnosticsNamespace,
  DownloadArtifactOpts,
  DownloadOpts,
  DynamicHandle,
  EmbeddingNamespace,
  EnsureAuthenticatedOpts,
  FilesNamespace,
  FunctionTool,
  Generator,
  GeneratorController,
  GeneratorPredictionResult,
  InferParsedConfig,
  InstallLocalPluginOpts,
  LLMActBaseOpts,
  LLMActionOpts,
  LLMGeneratorActOpts,
  LLMGeneratorPredictionOpts,
  LLMNamespace,
  LLMPredictionFragmentWithRoundIndex,
  LLMPredictionOpts,
  LLMRespondOpts,
  LMStudioClientConstructorOpts,
  LoginWithPreAuthenticatedKeysOpts,
  LoginWithPreAuthenticatedKeysResult,
  ModelNamespace,
  ModelSearchResultDownloadOption,
  ModelSearchResultEntry,
  OngoingGeneratorPrediction,
  OngoingPrediction,
  ParsedConfig,
  ParseDocumentOpts,
  ParseDocumentResult,
  PluginContext,
  PluginsNamespace,
  PredictionLoopHandler,
  PredictionLoopHandlerController,
  PredictionProcessCitationBlockController,
  PredictionProcessContentBlockController,
  PredictionProcessDebugInfoBlockController,
  PredictionProcessStatusController,
  PredictionProcessToolStatusController,
  PredictionResult,
  ProcessingController,
  PromptPreprocessor,
  PromptPreprocessorController,
  PushArtifactOpts,
  RawFunctionTool,
  RegisterDevelopmentPluginOpts,
  RegisterDevelopmentPluginResult,
  RemoteTool,
  RemoteToolUseSession,
  RepositoryNamespace,
  RequestConfirmToolCallOpts,
  RequestConfirmToolCallResult,
  RetrievalCallbacks,
  RetrievalOpts,
  RetrievalResult,
  RetrievalResultEntry,
  SpecificModel,
  StructuredPredictionResult,
  SystemNamespace,
  Tool,
  ToolBase,
  ToolCallContext,
  ToolsProvider,
  ToolsProviderController,
  VirtualConfigSchematics,
} from "@lmstudio/lms-client";
export type { LoggerInterface, StreamablePromise, TextAllowedTypes } from "@lmstudio/lms-common";
export type {
  GlobalKVFieldValueTypeLibraryMap,
  GlobalKVValueTypesLibrary,
  InnerFieldStringifyOpts,
  KVConcreteFieldValueType,
  KVConcreteFieldValueTypesMap,
  KVConfigBuilder,
  KVFieldValueTypeLibrary,
  KVVirtualConfigSchema,
  KVVirtualFieldValueType,
  KVVirtualFieldValueTypesMapping,
} from "@lmstudio/lms-kv-config";
export type {
  AllowableEnvVarKeys,
  AllowableEnvVars,
  ArtifactArtifactDependency,
  ArtifactDependency,
  ArtifactDependencyBase,
  ArtifactDependencyPurpose,
  ArtifactDownloadPlan,
  ArtifactDownloadPlanModelInfo,
  ArtifactDownloadPlanNode,
  ArtifactDownloadPlanNodeState,
  ArtifactManifestBase,
  ArtifactModelDependency,
  ArtifactType,
  BackendNotification,
  ChatHistoryData,
  ChatMessageData,
  ChatMessagePartData,
  ChatMessagePartFileData,
  ChatMessagePartTextData,
  ChatMessagePartToolCallRequestData,
  ChatMessagePartToolCallResultData,
  ChatMessageRoleData,
  CitationSource,
  ColorPalette,
  ContentBlockStyle,
  DiagnosticsLogEvent,
  DiagnosticsLogEventData,
  DiagnosticsLogRuntimeEventData,
  DocumentParsingLibraryIdentifier,
  DocumentParsingOpts,
  DownloadProgressUpdate,
  EmbeddingLoadModelConfig,
  EmbeddingModelAdditionalInfo,
  EmbeddingModelInfo,
  EmbeddingModelInstanceAdditionalInfo,
  EmbeddingModelInstanceInfo,
  EstimatedModelMemoryUsage,
  EstimatedModelMemoryUsageConfidence,
  EstimatedResourcesUsage,
  FileType,
  FunctionToolCallRequest,
  GPUSetting,
  HuggingFaceModelDownloadSource,
  KVConfig,
  KVConfigField,
  KVConfigFieldDependency,
  LLMAdditionalInfo,
  LLMApplyPromptTemplateOpts,
  LLMContextOverflowPolicy,
  LLMGenInfo,
  LLMInfo,
  LLMInstanceAdditionalInfo,
  LLMInstanceInfo,
  LLMJinjaPromptTemplate,
  LLMLlamaAccelerationOffloadRatio,
  LLMLlamaCacheQuantizationType,
  LLMLoadModelConfig,
  LLMManualPromptTemplate,
  LLMPredictionConfig,
  LLMPredictionConfigInput,
  LLMPredictionFragment,
  LLMPredictionFragmentInputOpts,
  LLMPredictionFragmentReasoningType,
  LLMPredictionStats,
  LLMPredictionStopReason,
  LLMPromptTemplate,
  LLMPromptTemplateType,
  LLMReasoningParsing,
  LLMSplitStrategy,
  LLMStructuredPredictionSetting,
  LLMStructuredPredictionType,
  LLMTool,
  LLMToolChoice,
  LLMToolParameters,
  LLMToolUseSetting,
  LocalArtifactFileEntry,
  LocalArtifactFileList,
  LogLevel,
  ModelCompatibilityType,
  ModelDomainType,
  ModelDownloadSource,
  ModelFormatName,
  ModelInfo,
  ModelInfoBase,
  ModelInstanceInfo,
  ModelInstanceInfoBase,
  ModelProcessingState,
  ModelQuery,
  ModelSearchOpts,
  ModelSearchResultDownloadOptionFitEstimation,
  PluginManifest,
  PluginRunnerType,
  RetrievalChunk,
  RetrievalChunkingMethod,
  RetrievalFileProcessingStep,
  RuntimeEngineInfo,
  RuntimeEngineSpecifier,
  SelectedRuntimeEngineMap,
  StatusStepState,
  StatusStepStatus,
  ToolCallRequest,
  ToolCallResult,
  ToolStatusStepStateStatus,
} from "@lmstudio/lms-shared-types";</doc><doc title="Index" desc="docs page.">export {
  Chat,
  ChatMessage,
  createConfigSchematics,
  FileHandle,
  LLM,
  LLMGeneratorHandle,
  LMStudioClient,
  rawFunctionTool,
  tool,
  ToolCallRequestError,
  ToolCallRequestInvalidArgumentsError,
  ToolCallRequestInvalidFormatError,
  ToolCallRequestInvalidNameError,
  LLMDynamicHandle,
  EmbeddingDynamicHandle,
  EmbeddingModel,
  unimplementedRawFunctionTool,
} from "@lmstudio/lms-client";
export { MaybeMutable, text } from "@lmstudio/lms-common";
export { kvValueTypesLibrary } from "@lmstudio/lms-kv-config";
export type * from "./exportedTypes.js";</doc><doc title="Lmstudioapitoken" desc="docs page.">export const lmstudioAPITokenRegex =
  /^sk-lm-(?<clientIdentifier>[a-zA-Z0-9]{8}):(?<clientPasskey>[a-zA-Z0-9]{20})$/;</doc><doc title="Llmcontextreference" desc="API reference.">import { z } from "zod";

/**
 * Represents a reference to a LLM context that can be loaded into a context.
 */
export type LLMContextReference =
  | {
      type: "jsonFile";
      absPath: string;
    }
  | {
      type: "yamlFile";
      absPath: string;
    };
export const llmContextReferenceSchema = z.discriminatedUnion("type", [
  z.object({
    type: z.literal("jsonFile"),
    absPath: z.string(),
  }),
  z.object({
    type: z.literal("yamlFile"),
    absPath: z.string(),
  }),
]);

export type LLMContextReferenceJsonFile = Array<{
  role: "user" | "assistant" | "system";
  content: string;
}>;
export const llmContextReferenceJsonFileSchema = z.array(
  z.object({
    role: z.enum(["user", "assistant", "system"]),
    content: z.string(),
  }),
);

export type LLMContextReferenceYamlFile = Array<
  | {
      system: string;
    }
  | {
      user: string;
    }
  | {
      assistant: string;
    }
>;
export const llmContextReferenceYamlFileSchema = z.array(
  z.union([
    z.object({
      system: z.string(),
    }),
    z.object({
      user: z.string(),
    }),
    z.object({
      assistant: z.string(),
    }),
  ]),
);</doc></api><packages><doc title="Allowableenvvars" desc="docs page.">import { z } from "zod";

export const allowableEnvVarKeys = ["HSA_OVERRIDE_GFX_VERSION"] as const;
/**
 * Type representing the environment variables that can be set by the user.
 *
 * @public
 */
export type AllowableEnvVarKeys = "HSA_OVERRIDE_GFX_VERSION";
export const allowableEnvVarKeysSchema = z.enum(allowableEnvVarKeys);

/**
 * Allow-list only record of environment variables and their values.
 *
 * @public
 */
export type AllowableEnvVars = Partial<Record<AllowableEnvVarKeys, string>>;
export const allowableEnvVarsSchema = z.record(
  allowableEnvVarKeysSchema,
  z.string(),
) as z.ZodSchema<AllowableEnvVars>;</doc><doc title="Artifactmanifest" desc="docs page.">import { z, type ZodSchema } from "zod";
import { modelManifestSchema, type ModelManifest } from "./ModelManifest.js";
import { pluginManifestUnrefinedSchema, type PluginManifest } from "./PluginManifest.js";
import { presetManifestSchema, type PresetManifest } from "./PresetManifest.js";
import { projectManifestSchema, type ProjectManifest } from "./ProjectManifest.js";

/**
 * The type for the manifest.json file.
 */
export type ArtifactManifest = PluginManifest | PresetManifest | ModelManifest | ProjectManifest;
export const artifactManifestSchema = z
  .discriminatedUnion("type", [
    pluginManifestUnrefinedSchema,
    presetManifestSchema,
    modelManifestSchema,
    projectManifestSchema,
  ])
  .superRefine((artifactManifest, ctx) => {
    if (
      artifactManifest.type === "plugin" &&
      artifactManifest.runner !== "deno" &&
      artifactManifest.sandbox !== undefined &&
      artifactManifest.sandbox.enabled
    ) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ["sandbox"],
        message:
          "Sandboxing is only supported for deno runners. Either disable sandboxing, or switch to deno.",
      });
    }
  }) as ZodSchema<ArtifactManifest>;

/**
 * Represents the type of an artifact.
 *
 * @public
 */
export type ArtifactType = "plugin" | "preset" | "model" | "project";
export const artifactTypeSchema = z.enum(["plugin", "preset", "model", "project"]);</doc><doc title="Artifactmanifestbase" desc="docs page.">import { z, type ZodSchema } from "zod";
import { kebabCaseSchema, kebabCaseWithDotsSchema } from "./kebab.js";
import { type ModelDownloadSource, modelDownloadSourceSchema } from "./ModelDownloadSource.js";

/**
 * Represents the purpose of an artifact dependency.
 *
 * @public
 */
export type ArtifactDependencyPurpose = "baseModel" | "draftModel" | "custom";
export const artifactDependencyPurposeSchema = z.enum([
  "baseModel",
  "draftModel",
  "custom",
]) as ZodSchema<ArtifactDependencyPurpose>;

/**
 * Represents the base type for an artifact dependency.
 *
 * @public
 */
export interface ArtifactDependencyBase {
  purpose: ArtifactDependencyPurpose;
}
export const artifactDependencyBaseSchema = z.object({
  purpose: artifactDependencyPurposeSchema,
});

/**
 * Represents a dependency on a concrete model.
 *
 * @public
 */
export interface ArtifactModelDependency extends ArtifactDependencyBase {
  type: "model";
  /**
   * The model key. This is used to identify if whether the dependency has been downloaded or not.
   * Any model matching any of the model keys listed here will be considered a match, and can
   * satisfy the entire model dependency.
   */
  modelKeys: Array<string>;
  /**
   * Describes how to download the model. Currently only supports downloading from a URL.
   */
  sources: Array<ModelDownloadSource>;
}
export const artifactModelDependencySchema = z.object({
  type: z.literal("model"),
  ...artifactDependencyBaseSchema.shape,
  modelKeys: z.array(z.string().min(1)),
  sources: z.array(modelDownloadSourceSchema),
});

/**
 * Represents a dependency on other artifacts.
 *
 * @public
 */
export interface ArtifactArtifactDependency extends ArtifactDependencyBase {
  type: "artifact";
  owner: string;
  name: string;
}
export const artifactArtifactDependencySchema = z.object({
  type: z.literal("artifact"),
  ...artifactDependencyBaseSchema.shape,
  owner: kebabCaseSchema,
  name: kebabCaseWithDotsSchema,
});

/**
 * Represents a dependency of an artifact.
 *
 * @public
 */
export type ArtifactDependency = ArtifactModelDependency | ArtifactArtifactDependency;
export const artifactDependencySchema = z.discriminatedUnion("type", [
  artifactModelDependencySchema,
  artifactArtifactDependencySchema,
]) as ZodSchema<ArtifactDependency>;

/**
 * Base type for the manifest of an artifact.
 *
 * @public
 */
export interface ArtifactManifestBase {
  owner: string;
  name: string;
  revision?: number;
  dependencies?: Array<ArtifactDependency>;
  tags?: Array<string>;
}
export const artifactManifestBaseSchema = z.object({
  owner: kebabCaseSchema,
  name: kebabCaseWithDotsSchema.min(1, "Name is required").max(100, "Name too long"),
  revision: z.number().int().optional(),
  dependencies: z.array(artifactDependencySchema).optional(),
  tags: z.array(z.string()).optional(),
});

export const artifactIdentifierRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*\/[a-z0-9]+(?:[-.][a-z0-9]+)*$/;
export const artifactIdentifierSchema = z.string().regex(artifactIdentifierRegex, {
  message: "Invalid artifact identifier format. Expected 'owner/name'.",
});</doc><doc title="Authenticatedipcserver" desc="docs page.">import { type BufferedEvent, type SimpleLogger } from "@lmstudio/lms-common";
import { authPacketSchema, type BackendInterface } from "@lmstudio/lms-communication";
import { type ClientHolder } from "./AuthenticatedWsServer.js";
import { type Authenticator, type Context, type ContextCreator } from "./Authenticator.js";
import { IpcServer, type RegisterIpcListener } from "./IpcServer.js";
import { IpcServerTransport } from "./IpcServerTransport.js";
import { ServerPort } from "./ServerPort.js";
import { type IpcMainEvent } from "./electronTypes.js";

interface Opts<TContext extends Context> {
  backendInterface: BackendInterface<TContext>;
  authenticator: Authenticator<TContext>;
  registerIpcListener: RegisterIpcListener;
  channel: string;
  parentLogger?: SimpleLogger;
}

export class AuthenticatedIpcServer<TContext extends Context> extends IpcServer<TContext> {
  protected override readonly logger: SimpleLogger = this.logger.subclass("AuthenticatedIpcServer");
  private readonly authenticator: Authenticator<TContext>;
  public constructor({
    backendInterface,
    authenticator,
    registerIpcListener,
    channel,
    parentLogger,
  }: Opts<TContext>) {
    super(backendInterface, registerIpcListener, channel, parentLogger);
    this.authenticator = authenticator;
  }
  protected override async onConnection(event: IpcMainEvent, data: Array<unknown>): Promise<void> {
    if (event.ports.length !== 1) {
      this.logger.error(
        "Invalid number of ports received. IPC communication will not be established.",
      );
      return;
    }
    let parsed;
    try {
      parsed = authPacketSchema.parse(data);
    } catch (error) {
      this.logger.warn("Received invalid message from client while authenticating:", data);
      return;
    }
    let holder: ClientHolder;
    let contextCreator: ContextCreator<TContext>;
    let authenticationRevokedEvent: BufferedEvent<void>;
    try {
      ({ holder, contextCreator, authenticationRevokedEvent } =
        await this.authenticator.authenticate(parsed));
    } catch (error) {
      this.logger.warn("Failed to authenticate client:", error);
      return;
    }
    authenticationRevokedEvent.subscribeOnce(() => {
      this.logger.warn("Authentication revoked. This is unexpected.");
    });
    const serverPort = new ServerPort(
      this.backendInterface,
      contextCreator,
      IpcServerTransport.createFactory(event.ports[0]),
    );
    serverPort.closeEvent.subscribe(() => {
      holder.drop();
    });
  }
}</doc><doc title="Authenticatedwsclienttransport" desc="docs page.">import { type LoggerInterface, type SimpleLogger } from "@lmstudio/lms-common";
import {
  wsAuthenticationResultSchema,
  type AuthPacket,
  type ClientTransportFactory,
  type ServerToClientMessage,
  type WsMessageEvent,
} from "@lmstudio/lms-communication";
import { WsClientTransport } from "./WsClientTransport.js";

interface AuthenticatedWsClientTransportConstructorOpts {
  parentLogger?: LoggerInterface;
  abortSignal?: AbortSignal;
}

interface Opts {
  url: string | Promise<string>;
  clientIdentifier: string;
  clientPasskey: string;
  abortSignal?: AbortSignal;
}

export class AuthenticatedWsClientTransport extends WsClientTransport {
  protected override readonly logger: SimpleLogger = this.logger.subclass(
    "AuthenticatedWsClientTransport",
  );
  protected constructor(
    url: string | Promise<string>,
    private readonly clientIdentifier: string,
    private readonly clientPasskey: string,
    receivedMessage: (message: ServerToClientMessage) => void,
    errored: (error: any) => void,
    { parentLogger, abortSignal }: AuthenticatedWsClientTransportConstructorOpts = {},
  ) {
    super(url, receivedMessage, errored, { parentLogger, abortSignal });
  }
  public static createAuthenticatedWsClientTransportFactory({
    url,
    clientIdentifier,
    clientPasskey,
    abortSignal,
  }: Opts): ClientTransportFactory {
    return (receivedMessage, errored, parentLogger) =>
      new AuthenticatedWsClientTransport(
        url,
        clientIdentifier,
        clientPasskey,
        receivedMessage,
        errored,
        { parentLogger, abortSignal },
      );
  }
  protected override onWsOpen() {
    this.ws!.send(
      JSON.stringify({
        authVersion: 1,
        clientIdentifier: this.clientIdentifier,
        clientPasskey: this.clientPasskey,
      } satisfies AuthPacket),
    );
    this.ws!.addEventListener(
      "message",
      (event: WsMessageEvent) => {
        try {
          const data = JSON.parse(event.data.toString("utf-8"));
          const result = wsAuthenticationResultSchema.parse(data);
          if (result.success) {
            super.onWsOpen();
          } else {
            this.onWsError(new Error("Failed to authenticate: " + result.error));
          }
        } catch (error: any) {
          this.onWsError(new Error("Failed to parse authentication result: " + error?.message));
        }
      },
      {
        once: true,
      },
    );
  }
}</doc><doc title="Authenticatedwsserver" desc="docs page.">import { type BufferedEvent, makePromise, type SimpleLogger } from "@lmstudio/lms-common";
import {
  authPacketSchema,
  type BackendInterface,
  type WsAuthenticationResult,
  type WsMessageEvent,
} from "@lmstudio/lms-communication";
import { type IncomingMessage, type Server } from "http";
import { type WebSocket } from "ws";
import { type Authenticator, type Context, type ContextCreator } from "./Authenticator.js";
import { ServerPort } from "./ServerPort.js";
import { WsServer } from "./WsServer.js";
import { WsServerTransport } from "./WsServerTransport.js";

/**
 * Owning a ClientHolder means owning a reference to the client. If a client is no longer needed,
 * the ClientHolder#drop method must be called.
 */
export interface ClientHolder {
  drop(): void;
  leak(): void;
}

interface AuthenticationServerConstructorOpts<TContext extends Context> {
  backendInterface: BackendInterface<TContext>;
  authenticator: Authenticator<TContext>;
  server: Server;
  pathName: string;
  guardConnection?: (request: IncomingMessage) => Promise<void>;
  parentLogger?: SimpleLogger;
  authenticationTimeoutMs?: number;
}

/**
 * A WebSocket server that also does authentication.
 *
 * The context is provided by the authenticator.
 */
export class AuthenticatedWsServer<TContext extends Context> extends WsServer<TContext> {
  protected override readonly logger: SimpleLogger = this.logger.subclass("AuthenticatedWsServer");
  private readonly authenticator: Authenticator<TContext>;
  private readonly timeoutMs: number;
  public constructor({
    backendInterface,
    authenticator,
    server,
    pathName,
    guardConnection,
    parentLogger,
    authenticationTimeoutMs: timeoutMs,
  }: AuthenticationServerConstructorOpts<TContext>) {
    super({ backendInterface, server, pathName, guardConnection, parentLogger });
    this.authenticator = authenticator;
    this.timeoutMs = timeoutMs ?? 10_000;
  }
  private authenticateConnection(ws: WebSocket): Promise<{
    holder: ClientHolder;
    contextCreator: ContextCreator<TContext>;
    authenticationRevokedEvent: BufferedEvent<void>;
  }> {
    const { promise, resolve, reject } = makePromise<{
      holder: ClientHolder;
      contextCreator: ContextCreator<TContext>;
      authenticationRevokedEvent: BufferedEvent<void>;
    }>();
    const onMessage = (event: WsMessageEvent) => {
      let message;
      try {
        message = JSON.parse(event.data.toString("utf8"));
      } catch (error) {
        this.logger.warn(
          `Received invalid JSON message from client while authenticating:`,
          event.data,
        );
        return;
      }
      let parsed;
      try {
        parsed = authPacketSchema.parse(message);
      } catch (error) {
        this.logger.warn("Received invalid message from client while authenticating:", message);
        return;
      }
      this.authenticator
        .authenticate(parsed)
        .then(result => {
          stopAuthentication();
          return result;
        })
        .then(resolve, reject);
    };
    const onError = (error: any) => {
      failAuthentication(error);
    };
    const onClose = () => {
      failAuthentication(new Error("Connection closed before authentication completed"));
    };
    const onTimeout = () => {
      failAuthentication(new Error("Authentication timed out"));
    };
    ws.addEventListener("message", onMessage);
    ws.addEventListener("error", onError);
    ws.addEventListener("close", onClose);
    const timeoutTimeout = setTimeout(onTimeout, this.timeoutMs);

    let authenticationStopped = false;

    /**
     * Removes all the listeners and stops the timeout.
     */
    function stopAuthentication() {
      if (authenticationStopped) {
        return;
      }
      authenticationStopped = true;
      ws.removeEventListener("message", onMessage);
      ws.removeEventListener("error", onError);
      ws.removeEventListener("close", onClose);
      clearTimeout(timeoutTimeout);
    }
    function failAuthentication(error: any) {
      stopAuthentication();
      reject(error);
    }
    return promise;
  }
  protected onConnection(ws: WebSocket) {
    this.authenticateConnection(ws)
      .then(({ holder, contextCreator, authenticationRevokedEvent }) => {
        ws.send(JSON.stringify({ success: true } satisfies WsAuthenticationResult));
        const serverPort = new ServerPort(
          this.backendInterface,
          contextCreator,
          WsServerTransport.createFactory(ws),
        );
        serverPort.closeEvent.subscribe(() => {
          holder.drop();
        });
        authenticationRevokedEvent.subscribeOnce(() => {
          this.logger.debug("Terminating connection because authentication was revoked");
          try {
            ws.close();
          } catch (error) {
            // Ignore
          }
        });
      })
      .catch(error => {
        this.logger.warn("Failed to authenticate client:", error);
        try {
          ws.send(
            JSON.stringify({
              success: false,
              error: error.message,
            } satisfies WsAuthenticationResult),
          );
        } catch (error) {
          // Ignore
        }
        try {
          ws.close();
        } catch (error) {
          // Ignore
        }
      });
  }
}</doc><doc title="Authentication" desc="docs page.">import { z } from "zod";

export const authPacketSchema = z.object({
  authVersion: z.literal(1),
  clientIdentifier: z.string().max(256),
  clientPasskey: z.string().max(256),
});

export type AuthPacket = z.infer<typeof authPacketSchema>;</doc><doc title="Authenticator" desc="docs page.">import { type BufferedEvent, type LoggerInterface } from "@lmstudio/lms-common";
import { type AuthPacket } from "@lmstudio/lms-communication";
import { type ClientHolder } from "./AuthenticatedWsServer.js";

export interface ContextCreatorParams {
  type: "rpc" | "channel" | "signal" | "writableSignal";
  endpointName: string;
}

export interface Context {
  logger: LoggerInterface;
}

export type ContextCreator<TContext extends Context> = (params: ContextCreatorParams) => TContext;

export abstract class Authenticator<TContext extends Context> {
  public abstract authenticate(authPacket: AuthPacket): Promise<{
    holder: ClientHolder;
    contextCreator: ContextCreator<TContext>;
    authenticationRevokedEvent: BufferedEvent<void>;
  }>;
}</doc><doc title="Backendinterface" desc="docs page.">/* eslint-disable @typescript-eslint/ban-types */
import { type NotAvailable, type Setter, type SignalLike } from "@lmstudio/lms-common";
import { type z, type ZodType } from "zod";
import type { Channel } from "./Channel.js";
import { type SerializationType } from "./serialization.js";

export type RpcEndpointHandler<TContext = any, TParameter = any, TReturns = any> = (
  ctx: TContext,
  parameter: TParameter,
) => TReturns | Promise<TReturns>;

export type ChannelEndpointHandler<
  TContext = any,
  TCreationParameter = any,
  TToServerPacket = any,
  TToClientPacket = any,
> = (
  ctx: TContext,
  creationParameter: TCreationParameter,
  channel: Channel<TToServerPacket, TToClientPacket>,
) => Promise<void>;

export type SignalEndpointHandler<TContext = any, TCreationParameter = any, TData = any> = (
  ctx: TContext,
  creationParameter: TCreationParameter,
) =>
  | SignalLike<TData>
  | Promise<SignalLike<TData>>
  | SignalLike<TData | NotAvailable>
  | Promise<SignalLike<TData | NotAvailable>>;

export type WritableSignalEndpointHandler<TContext = any, TCreationParameter = any, TData = any> = (
  ctx: TContext,
  creationParameter: TCreationParameter,
) =>
  | readonly [signal: SignalLike<TData>, setter: Setter<TData>]
  | Promise<readonly [signal: SignalLike<TData>, setter: Setter<TData>]>
  | readonly [signal: SignalLike<TData | NotAvailable>, setter: Setter<TData>]
  | Promise<readonly [signal: SignalLike<TData | NotAvailable>, setter: Setter<TData>]>;

export interface RpcEndpoint {
  name: string;
  parameter: z.ZodType;
  returns: z.ZodType;
  serialization: SerializationType;
  handler: RpcEndpointHandler | null;
}

export interface ChannelEndpoint {
  name: string;
  creationParameter: z.ZodType;
  toServerPacket: z.ZodType;
  toClientPacket: z.ZodType;
  serialization: SerializationType;
  handler: ChannelEndpointHandler | null;
}

export interface SignalEndpoint {
  name: string;
  creationParameter: z.ZodType;
  signalData: z.ZodType;
  serialization: SerializationType;
  handler: SignalEndpointHandler | null;
}

export interface WritableSignalEndpoint {
  name: string;
  creationParameter: z.ZodType;
  signalData: z.ZodType;
  serialization: SerializationType;
  handler: WritableSignalEndpointHandler | null;
}

interface RpcEndpointSpecBase {
  parameter: any;
  returns: any;
}

export type RpcEndpointsSpecBase = {
  [endpointName: string]: RpcEndpointSpecBase;
};

interface ChannelEndpointSpecBase {
  creationParameter: any;
  toServerPacket: any;
  toClientPacket: any;
}

export type ChannelEndpointsSpecBase = {
  [endpointName: string]: ChannelEndpointSpecBase;
};

interface SignalEndpointSpecBase {
  creationParameter: any;
  signalData: any;
}

export type SignalEndpointsSpecBase = {
  [endpointName: string]: SignalEndpointSpecBase;
};

interface WritableSignalEndpointSpecBase {
  creationParameter: any;
  signalData: any;
}

export type WritableSignalEndpointsSpecBase = {
  [endpointName: string]: WritableSignalEndpointSpecBase;
};

export class BackendInterface<
  TContext = never,
  TRpcEndpoints extends RpcEndpointsSpecBase = {},
  TChannelEndpoints extends ChannelEndpointsSpecBase = {},
  TSignalEndpoints extends SignalEndpointsSpecBase = {},
  TWritableSignalEndpoints extends WritableSignalEndpointsSpecBase = {},
> {
  private unhandledEndpoints = new Set<string>();
  private existingEndpointNames = new Set<string>();
  private rpcEndpoints = new Map<string, RpcEndpoint>();
  private channelEndpoints = new Map<string, ChannelEndpoint>();
  private signalEndpoints = new Map<string, SignalEndpoint>();
  private writableSignalEndpoints = new Map<string, WritableSignalEndpoint>();

  public constructor() {}

  public withContextType<TContextType>() {
    return this as any as BackendInterface<
      TContextType,
      TRpcEndpoints,
      TChannelEndpoints,
      TSignalEndpoints,
      TWritableSignalEndpoints
    >;
  }

  private assertEndpointNameNotExists(endpointName: string) {
    if (this.existingEndpointNames.has(endpointName)) {
      throw new Error(`Endpoint with name ${endpointName} already exists`);
    }
  }

  /**
   * Register an Rpc endpoint.
   */
  public addRpcEndpoint<
    TEndpointName extends string,
    TParametersZod extends ZodType,
    TReturnsZod extends ZodType,
  >(
    endpointName: TEndpointName,
    {
      parameter,
      returns,
      serialization = "raw",
    }: {
      parameter: TParametersZod;
      returns: TReturnsZod;
      serialization?: SerializationType;
    },
  ): BackendInterface<
    TContext,
    TRpcEndpoints & {
      [endpointName in TEndpointName]: {
        parameter: z.infer<TParametersZod>;
        returns: z.infer<TReturnsZod>;
      };
    },
    TChannelEndpoints,
    TSignalEndpoints,
    TWritableSignalEndpoints
  > {
    this.assertEndpointNameNotExists(endpointName);
    this.existingEndpointNames.add(endpointName);
    this.rpcEndpoints.set(endpointName, {
      name: endpointName,
      parameter,
      returns,
      serialization,
      handler: null,
    });
    return this;
  }

  public addChannelEndpoint<
    TEndpointName extends string,
    TCreationParameterZod extends ZodType,
    TToServerPacketZod extends ZodType,
    TToClientPacketZod extends ZodType,
  >(
    endpointName: TEndpointName,
    {
      creationParameter,
      toServerPacket,
      toClientPacket,
      serialization = "raw",
    }: {
      creationParameter: TCreationParameterZod;
      toServerPacket: TToServerPacketZod;
      toClientPacket: TToClientPacketZod;
      serialization?: SerializationType;
    },
  ): BackendInterface<
    TContext,
    TRpcEndpoints,
    TChannelEndpoints & {
      [endpointName in TEndpointName]: {
        creationParameter: z.infer<TCreationParameterZod>;
        toServerPacket: z.infer<TToServerPacketZod>;
        toClientPacket: z.infer<TToClientPacketZod>;
      };
    },
    TSignalEndpoints,
    TWritableSignalEndpoints
  > {
    this.assertEndpointNameNotExists(endpointName);
    this.existingEndpointNames.add(endpointName);
    this.channelEndpoints.set(endpointName, {
      name: endpointName,
      creationParameter,
      toServerPacket,
      toClientPacket,
      serialization,
      handler: null,
    });
    return this;
  }

  public addSignalEndpoint<
    TEndpointName extends string,
    TCreationParameterZod extends ZodType,
    TSignalDataZod extends ZodType,
  >(
    endpointName: TEndpointName,
    {
      creationParameter,
      signalData,
      serialization = "raw",
    }: {
      creationParameter: TCreationParameterZod;
      signalData: TSignalDataZod;
      serialization?: SerializationType;
    },
  ): BackendInterface<
    TContext,
    TRpcEndpoints,
    TChannelEndpoints,
    TSignalEndpoints & {
      [endpointName in TEndpointName]: {
        creationParameter: z.infer<TCreationParameterZod>;
        signalData: z.infer<TSignalDataZod>;
      };
    },
    TWritableSignalEndpoints
  > {
    this.assertEndpointNameNotExists(endpointName);
    this.existingEndpointNames.add(endpointName);
    this.signalEndpoints.set(endpointName, {
      name: endpointName,
      creationParameter,
      signalData,
      serialization,
      handler: null,
    });
    return this;
  }

  public addWritableSignalEndpoint<
    TEndpointName extends string,
    TCreationParameterZod extends ZodType,
    TSignalDataZod extends ZodType,
  >(
    endpointName: TEndpointName,
    {
      creationParameter,
      signalData,
      serialization = "raw",
    }: {
      creationParameter: TCreationParameterZod;
      signalData: TSignalDataZod;
      serialization?: SerializationType;
    },
  ): BackendInterface<
    TContext,
    TRpcEndpoints,
    TChannelEndpoints,
    TSignalEndpoints,
    TWritableSignalEndpoints & {
      [endpointName in TEndpointName]: {
        creationParameter: z.infer<TCreationParameterZod>;
        signalData: z.infer<TSignalDataZod>;
      };
    }
  > {
    this.assertEndpointNameNotExists(endpointName);
    this.existingEndpointNames.add(endpointName);
    this.writableSignalEndpoints.set(endpointName, {
      name: endpointName,
      creationParameter,
      signalData,
      serialization,
      handler: null,
    });
    return this;
  }

  /**
   * Adds a handler for an Rpc endpoint.
   *
   * @param endpointName - The name of the endpoint.
   * @param handler - The handler function. Will be called when the endpoint is invoked. When
   * called, the first parameter is the context, and the second parameter is the "parameter" for the
   * RPC call. Can return a value or a promise that resolves to the result.
   */
  public handleRpcEndpoint<TEndpointName extends keyof TRpcEndpoints & string>(
    endpointName: TEndpointName,
    handler: RpcEndpointHandler<
      TContext,
      TRpcEndpoints[TEndpointName]["parameter"],
      TRpcEndpoints[TEndpointName]["returns"]
    >,
  ) {
    const endpoint = this.rpcEndpoints.get(endpointName);
    if (endpoint === undefined) {
      throw new Error(`No Rpc endpoint with name ${endpointName}`);
    }
    if (endpoint.handler !== null) {
      throw new Error(`Rpc endpoint with name ${endpointName} already has a handler`);
    }
    endpoint.handler = handler;
    this.unhandledEndpoints.delete(endpointName);
  }

  /**
   * Adds a handler for a channel endpoint.
   *
   * @param endpointName - The name of the endpoint.
   * @param handler - The handler function. Will be called when the client creates a channel for
   * this endpoint. When called, the first parameter is the context, the second parameter is the
   * "creationParameter" for the channel, and the third parameter is a channel object that can be
   * used to send and receive messages from the client.
   *
   * Must return a promise. Once that promise is settled, the channel will be closed.
   */
  public handleChannelEndpoint<TEndpointName extends keyof TChannelEndpoints & string>(
    endpointName: TEndpointName,
    handler: ChannelEndpointHandler<
      TContext,
      TChannelEndpoints[TEndpointName]["creationParameter"],
      TChannelEndpoints[TEndpointName]["toServerPacket"],
      TChannelEndpoints[TEndpointName]["toClientPacket"]
    >,
  ) {
    const endpoint = this.channelEndpoints.get(endpointName);
    if (endpoint === undefined) {
      throw new Error(`No channel endpoint with name ${endpointName}`);
    }
    if (endpoint.handler !== null) {
      throw new Error(`Channel endpoint with name ${endpointName} already has a handler`);
    }
    endpoint.handler = handler;
    this.unhandledEndpoints.delete(endpointName);
  }

  /**
   * Adds a handler for a signal endpoint.
   *
   * @param endpointName - The name of the endpoint.
   * @param handler - The handler function. Will be called when the client creates a signal, and at
   * least one subscriber is attached to that signal. When called, the first parameter is the
   * context, and the second parameter is the "creationParameter" for the signal. This method should
   * return a SignalLike, or a promise that resolves to a SignalLike.
   *
   * Note: There is no 1-to-1 correlation between the signal on the client side and the number of
   * times this handler is called. Every time the number of client subscribers changes from 0 to 1,
   * this handler will be called. Every time the number of client subscribers changes from 1 to 0,
   * the signal returned from this handler will be unsubscribed.
   *
   * Caution: Do NOT create new subscriptions that don't self-terminate in this handler, as it will
   * cause memory leaks. That is, either:
   *
   * - Return a signal that already exists
   * - Create and return a LazySignal
   */
  public handleSignalEndpoint<TEndpointName extends keyof TSignalEndpoints & string>(
    endpointName: TEndpointName,
    handler: SignalEndpointHandler<
      TContext,
      TSignalEndpoints[TEndpointName]["creationParameter"],
      TSignalEndpoints[TEndpointName]["signalData"]
    >,
  ) {
    const endpoint = this.signalEndpoints.get(endpointName);
    if (endpoint === undefined) {
      throw new Error(`No signal endpoint with name ${endpointName}`);
    }
    if (endpoint.handler !== null) {
      throw new Error(`Signal endpoint with name ${endpointName} already has a handler`);
    }
    endpoint.handler = handler;
    this.unhandledEndpoints.delete(endpointName);
  }

  /**
   * Adds a handler for a writable signal endpoint.
   *
   * @param endpointName - The name of the endpoint.
   * @param handler - The handler function. Will be called when the client creates a writable
   * signal, and at least one subscriber is attached to that signal. When called, the first
   * parameter is the context, and the second parameter is the "creationParameter" for the signal.
   * This method should return a tuple of the signal and an update function. The update function
   * should be called with the new data, patches, and tags to update the signal.
   *
   * Note: There is no 1-to-1 correlation between the signal on the client side and the number of
   * times this handler is called. Every time the number of client subscribers changes from 0 to 1,
   * this handler will be called. Every time the number of client subscribers changes from 1 to 0
   * the signal returned from this handler will be unsubscribed.
   *
   * Caution: Do NOT create new subscriptions that don't self-terminate in this handler, as it will
   * cause memory leaks. That is, either:
   *
   * - Return a signal that already exists
   * - Create and return a LazySignal
   */
  public handleWritableSignalEndpoint<
    TEndpointName extends keyof TWritableSignalEndpoints & string,
  >(
    endpointName: TEndpointName,
    handler: WritableSignalEndpointHandler<
      TContext,
      TWritableSignalEndpoints[TEndpointName]["creationParameter"],
      TWritableSignalEndpoints[TEndpointName]["signalData"]
    >,
  ) {
    const endpoint = this.writableSignalEndpoints.get(endpointName);
    if (endpoint === undefined) {
      throw new Error(`No writable signal endpoint with name ${endpointName}`);
    }
    if (endpoint.handler !== null) {
      throw new Error(`Writable signal endpoint with name ${endpointName} already has a handler`);
    }
    endpoint.handler = handler;
    this.unhandledEndpoints.delete(endpointName);
  }

  public assertAllEndpointsHandled() {
    if (this.unhandledEndpoints.size > 0) {
      throw new Error(
        `The following endpoints were not handled: ${Array.from(this.unhandledEndpoints).join(
          ", ",
        )}`,
      );
    }
  }

  public getRpcEndpoint(endpointName: string) {
    return this.rpcEndpoints.get(endpointName);
  }

  public getAllRpcEndpoints() {
    return [...this.rpcEndpoints.values()];
  }

  public getChannelEndpoint(endpointName: string) {
    return this.channelEndpoints.get(endpointName);
  }

  public getAllChannelEndpoints() {
    return [...this.channelEndpoints.values()];
  }

  public getSignalEndpoint(endpointName: string) {
    return this.signalEndpoints.get(endpointName);
  }

  public getAllSignalEndpoints() {
    return [...this.signalEndpoints.values()];
  }

  public getWritableSignalEndpoint(endpointName: string) {
    return this.writableSignalEndpoints.get(endpointName);
  }

  public getAllWritableSignalEndpoints() {
    return [...this.writableSignalEndpoints.values()];
  }
}

export type BackendInterfaceWithContext<TBackendInterface extends BackendInterface, TContext> =
  TBackendInterface extends BackendInterface<
    infer _ROriginalContext,
    infer RRpcEndpoints,
    infer RChannelEndpoints,
    infer RSignalEndpoints,
    infer RWritableSignalEndpoints
  >
    ? BackendInterface<
        TContext,
        RRpcEndpoints,
        RChannelEndpoints,
        RSignalEndpoints,
        RWritableSignalEndpoints
      >
    : never;
export type AnyBackendInterface = BackendInterface<any, any, any, any, any>;
export type ExtractBackendInterfaceRpcEndpoints<TBackendInterface extends AnyBackendInterface> =
  TBackendInterface extends BackendInterface<any, infer RRpcEndpoints, any, any, any>
    ? RRpcEndpoints
    : never;
export type ExtractBackendInterfaceChannelEndpoints<TBackendInterface extends AnyBackendInterface> =
  TBackendInterface extends BackendInterface<any, any, infer RChannelEndpoints, any, any>
    ? RChannelEndpoints
    : never;
export type ExtractBackendInterfaceSignalEndpoints<TBackendInterface extends AnyBackendInterface> =
  TBackendInterface extends BackendInterface<any, any, any, infer RSignalEndpoints, any>
    ? RSignalEndpoints
    : never;
export type ExtractBackendInterfaceWritableSignalEndpoints<
  TBackendInterface extends AnyBackendInterface,
> =
  TBackendInterface extends BackendInterface<any, any, any, any, infer RWritableSignalEndpoints>
    ? RWritableSignalEndpoints
    : never;</doc><doc title="Backendnotification" desc="docs page.">import { z, type ZodSchema } from "zod";

/**
 * @public
 */
export interface BackendNotification {
  title: string;
  description?: string;
  noAutoDismiss?: boolean;
}
export const backendNotificationSchema = z.object({
  title: z.string(),
  description: z.string().optional(),
  noAutoDismiss: z.boolean().optional(),
}) as ZodSchema<BackendNotification>;</doc></packages><publish><doc title="README" desc="install &amp; quickstart."># LM Studio CLI - lms

See https://github.com/lmstudio-ai/lms for more information</doc><doc title="README" desc="install &amp; quickstart."># LM Studio Setup Tool

This tool is currently used to setup LM Studio CLI (lms).

To run the tool, run the following command:

```bash
npx lmstudio install-cli
```</doc><doc title="Index" desc="docs page.">#!/usr/bin/env node
require("@lmstudio/cli");</doc><doc title="Injectvariables" desc="docs page.">// Inject the current version by replacing the magic string <LMS-CLI-CURRENT-VERSION>
// This is much faster than rollup-plugin-replace

const { readFileSync, writeFileSync } = require("fs");
const { join } = require("path");

const content = readFileSync(join(__dirname, "dist", "index.js"), "utf-8");
const packageJson = readFileSync(join(__dirname, "package.json"), "utf-8");
let lmsKey = null;
try {
  lmsKey = readFileSync(join(__dirname, "lms-key"), "utf-8").trim();
} catch (e) {
  console.error("Failed to read lms-key. Build in development mode.");
}

let replaced = content.replaceAll("<LMS-CLI-CURRENT-VERSION>", JSON.parse(packageJson).version);
if (lmsKey !== null) {
  replaced = replaced.replaceAll("<LMS-CLI-LMS-KEY>", lmsKey);
}

writeFileSync(join(__dirname, "dist", "index.js"), replaced, "utf-8");</doc><doc title="Rollup.Config" desc="docs page.">const { nodeResolve } = require("@rollup/plugin-node-resolve");
const { join, resolve } = require("path");
const commonjs = require("@rollup/plugin-commonjs");
const json = require("@rollup/plugin-json");
const banner = require("rollup-plugin-banner2");

module.exports = {
  input: resolve(require.resolve("@lmstudio/lms-cli")),
  output: [
    {
      file: join(__dirname, "dist", "index.js"),
      format: "cjs",
    },
  ],
  context: "globalThis",
  plugins: [
    nodeResolve({
      extensions: [".ts", ".tsx", ".js", ".jsx"],
    }),
    commonjs(),
    json(),
    banner(() => "#!/usr/bin/env node\n"),
  ],
};</doc><doc title="Rollup.Config" desc="docs page.">const { nodeResolve } = require("@rollup/plugin-node-resolve");
const { join, resolve } = require("path");
const commonjs = require("@rollup/plugin-commonjs");
const json = require("@rollup/plugin-json");
const banner = require("rollup-plugin-banner2");

module.exports = {
  input: resolve(require.resolve("@lmstudio/lms-lmstudio")),
  output: [
    {
      file: join(__dirname, "dist", "index.js"),
      format: "cjs",
    },
  ],
  context: "globalThis",
  plugins: [
    nodeResolve({
      extensions: [".ts", ".tsx", ".js", ".jsx"],
    }),
    commonjs(),
    json(),
    banner(() => "#!/usr/bin/env node\n"),
  ],
};</doc></publish><scaffolds><doc title="README" desc="install &amp; quickstart."># <PROJECT_NAME>

#### Created with LM Studio scaffold: node-javascript-empty

Welcome to your new project! This scaffold is a starting point for building an AI-enabled Node.js project with [LM Studio](https://lmstudio.ai/) SDK. To interact with LM Studio, you should start the LM Studio local server with the command:

```bash
lms server start
```

## Getting Started

### Development

The source code resides in the `src/` directory. For development purposes, you can run the project using:

```start
npm start
```

### Community & Help

- [lmstudio.js GitHub](https://github.com/lmstudio-ai/lmstudio.js)
- [Documentation](https://lmstudio.ai/docs/welcome)
- [Discord](https://discord.gg/6Q7Xn6MRVS)
- [Twitter](https://twitter.com/LMStudioAI)</doc><doc title="README" desc="install &amp; quickstart."># <PROJECT_NAME>

#### Created with LM Studio scaffold: node-javascript

Welcome to your new project! This scaffold is a starting point for building an AI-enabled Node.js project with [LM Studio](https://lmstudio.ai/) SDK. To interact with LM Studio, you should start the LM Studio local server with the command:

```bash
lms server start
```

## Getting Started

### Development

The source code resides in the `src/` directory. For development purposes, you can run the project using:

```start
npm start
```

### Community & Help

- [lmstudio.js GitHub](https://github.com/lmstudio-ai/lmstudio.js)
- [Documentation](https://lmstudio.ai/docs/welcome)
- [Discord](https://discord.gg/6Q7Xn6MRVS)
- [Twitter](https://twitter.com/LMStudioAI)</doc><doc title="README" desc="install &amp; quickstart."># <PROJECT_NAME>

#### Created with LM Studio scaffold: node-typescript

Welcome to your new project! This scaffold is a starting point for building an AI-enabled Node.js project with [LM Studio](https://lmstudio.ai/) SDK. To interact with LM Studio, you should start the LM Studio local server with the command:

```bash
lms server start
```

## Getting Started

### Development

The source code resides in the `src/` directory. For development purposes, you can run the project using:

```start
npm start
```

### Building for Production

To prepare your project for production, compile the TypeScript code to JavaScript using:

```bash
npm run build
```

This will compile the TypeScript code in the `src/` directory to JavaScript in the `dist/` directory.

### Community & Help

- [lmstudio.js GitHub](https://github.com/lmstudio-ai/lmstudio.js)
- [Documentation](https://lmstudio.ai/docs/welcome)
- [Discord](https://discord.gg/6Q7Xn6MRVS)
- [Twitter](https://twitter.com/LMStudioAI)</doc><doc title="README" desc="install &amp; quickstart."># <PROJECT_NAME>

#### Created with LM Studio scaffold: node-typescript-empty

Welcome to your new project! This scaffold is a starting point for building an AI-enabled Node.js project with [LM Studio](https://lmstudio.ai/) SDK. To interact with LM Studio, you should start the LM Studio local server with the command:

```bash
lms server start
```

## Getting Started

### Development

The source code resides in the `src/` directory. For development purposes, you can run the project using:

```start
npm start
```

### Building for Production

To prepare your project for production, compile the TypeScript code to JavaScript using:

```bash
npm run build
```

This will compile the TypeScript code in the `src/` directory to JavaScript in the `dist/` directory.

### Community & Help

- [lmstudio.js GitHub](https://github.com/lmstudio-ai/lmstudio.js)
- [Documentation](https://lmstudio.ai/docs/welcome)
- [Discord](https://discord.gg/6Q7Xn6MRVS)
- [Twitter](https://twitter.com/LMStudioAI)</doc><doc title="Buildscaffoldsjson" desc="docs page.">const { readdirSync, statSync, writeFileSync } = require("fs");

const names = readdirSync(__dirname);
const output = [];
for (const name of names) {
  const stat = statSync(name);
  if (stat.isDirectory() && !name.startsWith(".")) {
    const metadata = require(`./${name}/lms-scaffold.json`);
    output.push(metadata);
  }
}
writeFileSync("scaffolds.json", JSON.stringify(output, null, 2));</doc><doc title="Upload" desc="docs page.">require("dotenv").config();
const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3");
const { readFileSync } = require("fs");
const { join } = require("path");

// Create an S3 client
const s3Client = new S3Client({
  region: "auto", // or your specific region
  credentials: {
    accessKeyId: process.env.CF_ACCESS_KEY_ID,
    secretAccessKey: process.env.CF_SECRET_ACCESS_KEY,
  },
  endpoint: process.env.CF_ENDPOINT,
  forcePathStyle: true, // Needed for Cloudflare R2
  signatureVersion: "v4",
});

const bucketName = "scaffolds-manifest";
const filePath = join(__dirname, "scaffolds.json");
const keyName = "scaffolds.json";

const uploadFile = async () => {
  try {
    const data = await s3Client.send(
      new PutObjectCommand({
        Bucket: bucketName,
        Key: keyName,
        Body: readFileSync(filePath),
        ContentType: "application/json",
      }),
    );
    console.log("Success", data);
  } catch (err) {
    console.log("Error", err);
  }
};

uploadFile();</doc><doc title="Index" desc="docs page.">import { LMStudioClient } from "@lmstudio/sdk";

const client = new LMStudioClient();

console.log("👾👾 Welcome to my new project! 👾👾");
console.log("\nDownloaded models:\n");
console.log(await client.system.listDownloadedModels());
console.log("\n👉 For more, visit our documentation website at https://lmstudio.ai/docs/welcome\n");</doc><doc title="Index" desc="docs page.">import { LMStudioClient } from "@lmstudio/sdk";

const client = new LMStudioClient();

await printDownloadedModels();
await printLoadedModels();
await predictWithAnyModel();

// ---------- Functions ----------

async function printDownloadedModels() {
  const downloadedModels = await client.system.listDownloadedModels();
  console.log("Downloaded Models:");
  if (downloadedModels.length === 0) {
    console.log("    No models downloaded. Get some in LM Studio.");
    process.exit(0);
  }

  // Limit to printing 5 models
  for (const model of downloadedModels.slice(0, 5)) {
    console.log(`  - ${model.modelKey} (${model.displayName})`);
  }
  if (downloadedModels.length > 5) {
    console.log(`    (... and ${downloadedModels.length - 5} more)`);
  }
  console.log(); // Create an empty line
}

async function printLoadedModels() {
  const loadedLLMs = await client.llm.listLoaded();
  console.log("Loaded Models:");
  if (loadedLLMs.length === 0) {
    console.log("    You don't have any models loaded. (Run `lms load` to load a model)");
    process.exit(0);
  }
  for (const model of loadedLLMs) {
    console.log(`  - ${model.identifier} (${model.displayName})`);
  }
  console.log(); // Create an empty line
}

async function predictWithAnyModel() {
  const model = await client.llm.model();
  const prompt = "The meaning of life is";
  const prediction = model.complete(prompt, {
    maxTokens: 100,
    temperature: 0.7,
  });
  process.stdout.write(prompt); // Print the prompt
  // Stream the prediction text to console
  for await (const { content } of prediction) {
    process.stdout.write(content);
  }
  const { stats } = await prediction.result();
  console.log("\n\nPrediction Stats:", stats);
}</doc><doc title="Index" desc="docs page.">import { LMStudioClient } from "@lmstudio/sdk";

const client = new LMStudioClient();

console.log("👾👾 Welcome to my new project! 👾👾");
console.log("\nDownloaded models:\n");
console.log(await client.system.listDownloadedModels());
console.log("\n👉 For more, visit our documentation website at https://lmstudio.ai/docs/welcome\n");</doc><doc title="Index" desc="docs page.">import { LMStudioClient } from "@lmstudio/sdk";

const client = new LMStudioClient();

await printDownloadedModels();
await printLoadedModels();
await predictWithAnyModel();

// ---------- Functions ----------

async function printDownloadedModels() {
  const downloadedModels = await client.system.listDownloadedModels();
  console.log("Downloaded Models:");
  if (downloadedModels.length === 0) {
    console.log("    No models downloaded. Get some in LM Studio.");
    process.exit(0);
  }

  // Limit to printing 5 models
  for (const model of downloadedModels.slice(0, 5)) {
    console.log(`  - ${model.modelKey} (${model.displayName})`);
  }
  if (downloadedModels.length > 5) {
    console.log(`    (... and ${downloadedModels.length - 5} more)`);
  }
  console.log(); // Create an empty line
}

async function printLoadedModels() {
  const loadedLLMs = await client.llm.listLoaded();
  console.log("Loaded Models:");
  if (loadedLLMs.length === 0) {
    console.log("    You don't have any models loaded. (Run `lms load` to load a model)");
    process.exit(0);
  }
  for (const model of loadedLLMs) {
    console.log(`  - ${model.identifier} (${model.displayName})`);
  }
  console.log(); // Create an empty line
}

async function predictWithAnyModel() {
  const model = await client.llm.model();
  const prompt = "The meaning of life is";
  const prediction = model.complete(prompt, {
    maxTokens: 100,
    temperature: 0.7,
  });
  process.stdout.write(prompt); // Print the prompt
  // Stream the prediction text to console
  for await (const { content } of prediction) {
    process.stdout.write(content);
  }
  const { stats } = await prediction.result();
  console.log("\n\nPrediction Stats:", stats);
}</doc></scaffolds></project>
