import type { RequestRules } from "./requestRules.js"; import { type ServeTarget, type ValueFlags } from "./serveArgv.js"; export { argvAfterServe, groupServeArgv, optionsByModel, targetsFromFlags, valueFlagsOf, type ServeTarget, type ValueFlags, } from "./serveArgv.js"; export { spawnOptions, type ChildOutput } from "./modelProcess.js"; export { choosePython, defaultMlxEnv } from "../stdlib/localPython.js"; import { type DownloadedModel } from "../stdlib/localModels.js"; import type { ModelKind } from "../stdlib/modelKind.js"; import { type ModelStatus } from "./modelPool.js"; import { type MemorySnapshot } from "./availableMemory.js"; import { formatElapsed } from "../eval/run/statusBoard.js"; export { formatElapsed }; export type ServeKind = ModelKind; export type ChatServeOptions = { port: number; maxTokens: number; python?: string; logPrompts?: boolean; }; /** The argv for one chat server process, after the Python path. The * script is mlx_lm.server with structured output added; it takes * mlx_lm.server's own options. */ export declare function serveArgs(script: string, modelDir: string, internalPort: number, settings: ChatServerSettings): string[]; export type ChatServerSettings = { maxTokens: number; promptCacheBytes: number; prefillStepSize: number; limits: ReplyLimits; /** A smaller model of the same family that drafts tokens for this one to * check (speculative decoding), and how many it drafts at a time. */ draft?: { dir: string; tokens: number; }; }; /** How many tokens the draft model guesses before the main model checks * them. Too few and the check happens too often; too many and most of a * guess is thrown away. mlx_lm's own default is 3; 4 is a little better * on the prose most replies are. */ export declare const DEFAULT_DRAFT_TOKENS = 4; /** How many tokens of prompt the server reads in one pass. A long prompt * is read in chunks of this size. A bigger chunk keeps the GPU busier, so * a long prompt is read sooner, but the attention scores of one chunk * against the whole prompt have to fit in memory at once. mlx_lm's default * of 2048 suits a small machine; a machine with the memory to spare reads * a 20,000-token prompt noticeably faster in chunks of 8192. */ export declare function prefillStepSize(totalMemBytes: number): number; /** How far a reply may go before the chat server cuts it short. Each is a * count, 0 turns one off, and an absent one leaves the script's default. * See the guide on local models for what each one watches. */ export type ReplyLimits = { reasoningBudget?: number; hedgeLimit?: number; repeatLimit?: number; /** Watch answers for hedging and repeats too, not only thinking. */ limitAnswers?: boolean; }; /** How much memory one chat server may spend on attention state: the * replies it is generating plus its cache of recent prompts, which lets a * follow-up on the same conversation skip re-reading it. mlx_lm.server * keeps the last ten prompts and, with no byte limit, keeps them whatever * their size; a few long prompts to a large model add tens of gigabytes to * a process that already holds the whole model, and the server dies of * GPU memory. A sixteenth of the machine's memory is enough for about * eighty thousand tokens of a 235B model on a 256GB Mac, and about sixteen * thousand tokens of an 8B model on a 32GB one. */ export declare function promptCacheBudget(totalMemBytes: number): number; /** The argv for one embedding server process, after the Python path. */ export declare function embedServeArgs(script: string, modelDir: string, internalPort: number, maxLength: number): string[]; /** The argv for one speech server process, after the Python path. The * models directory is where the script looks for a companion repo the * model loads by name, such as Orpheus's SNAC decoder. */ export declare function speechServeArgs(script: string, modelDir: string, internalPort: number, modelsDir: string): string[]; /** The argv for one image server process, after the Python path. The * adapters folder goes along when one is configured, so a request can * name a LoRA adapter in it. */ export declare function imageServeArgs(script: string, modelDir: string, internalPort: number, adaptersDir?: string | null, controlnetsDir?: string | null): string[]; /** The chat server shipped next to this file: mlx_lm.server with * `response_format` honoured. `make build` copies it into dist, so the * path holds for a development checkout and an install. */ export declare function chatServerScript(): string; /** The mlx-lm release the chat server script was written against. The * script reaches into mlx_lm.server's internals, so a new release needs * those seams checked before the pin moves. */ export declare const MLX_LM_VERSION = "0.31.3"; /** The llguidance release the chat server enforces schemas with. */ export declare const LLGUIDANCE_VERSION = "1.8.0"; /** The embedding server shipped next to this file, copied into dist like * the chat one. */ export declare function embedServerScript(): string; /** The mlx-audio release the speech server's rules were written against. * A test checks it matches MLX_AUDIO_VERSION in lib/cli/mlxSpeechRules.py; * the server refuses any other version. */ export declare const MLX_AUDIO_VERSION = "0.5.4"; /** The speech server shipped next to this file, copied into dist like * the embedding one. */ export declare function speechServerScript(): string; /** The diffusers release the image server's rules were written against. * A test checks it matches DIFFUSERS_VERSION in * lib/cli/diffusersImageRules.py; the server refuses any other version. */ export declare const DIFFUSERS_VERSION = "0.40.0"; /** The releases the image server was tested with alongside diffusers. */ export declare const TORCH_VERSION = "2.14.0"; export declare const TORCHVISION_VERSION = "0.29.2"; export declare const TRANSFORMERS_VERSION = "5.17.0"; export declare const ACCELERATE_VERSION = "1.15.0"; /** The image server shipped next to this file, copied into dist like the * speech one. */ export declare function imageServerScript(): string; /** The script that runs mlx_vlm.server, shipped next to this file and * copied into dist like the others. */ export declare function vlmServerScript(): string; /** The vision server shipped next to this file, copied into dist like the * image one. */ export declare function visionServerScript(): string; /** The argv for one vision server process, after the Python path. */ export declare function visionServeArgs(script: string, modelDir: string, internalPort: number): string[]; /** The onnxruntime release the vision server's tagger family was tested * with; `visionRules.py` pins the same one. */ export declare const ONNXRUNTIME_VERSION = "1.30.0"; /** A line to print when the models add up to more than the machine has. * Null when they fit. The caller prints it and continues. */ export declare function memoryWarning(sizesBytes: number[], totalMemBytes: number): string | null; /** The 404 body for a request naming a model this server was not started * with. The path the request came in on says which flag the model needs * and, for a bare repo id, which URI prefix; a directory is named by its * path. */ export declare function notServedMessage(served: string[], requested: string, requestPath: string): string; /** Why a Python cannot serve: it is not there, or it cannot import a module * one of the planned kinds needs. */ export type PythonProblem = { kind: "missing"; } | { kind: "cannot-import" | "wrong-version"; module: string; }; /** What to print when the chosen Python is not there, or cannot import a * module the planned kinds need. The venv commands create the default * environment with every module in `modules`; a user who pointed --python * at their own Python is told the flag is the other way out. */ export declare function pythonMissingMessage(python: string, home: string, problem: PythonProblem, modules?: string[]): string; export type ReadinessOptions = { fetch?: typeof fetch; retryMs?: number; /** Resolves with what happened ("mlx_lm.server for X exited with 1") when * the process exits. Readiness stops waiting and rejects. */ gone?: Promise; /** An embedding process cannot answer a chat completion, so it is probed * with an embeddings request instead, and a speech process with GET * /health. Default chat. */ kind?: ServeKind; label?: string; }; /** `mlx_lm.server` prints nothing when its model has loaded. The only * readiness signal is a completion request that answers. Send a one-token * one, naming the model the process was started with, and retry while the * port is not open. A reply other than 2xx is a misconfigured server, and * the wait fails with the status and body. */ export declare function waitUntilLoaded(port: number, upstreamModel: string, options?: ReadinessOptions): Promise; export type ExecResult = { status: number | null; error?: { code?: string; }; }; export type Exec = (cmd: string, args: string[]) => ExecResult; /** Whether `python` exists and can import each module. The first missing * module names the problem. */ export declare function checkPython(python: string, exec?: Exec, modules?: string[]): PythonProblem | "ok"; /** A port nothing is listening on right now, for one mlx_lm.server. */ export declare function freePort(): Promise; export type ServeChoice = { title: string; value: string; }; /** The models `serve` can start without downloading anything: every model * under the models directory whose record says every file is there, of * any kind. A GGUF model runs in the Agency process instead, so it is * never a choice here. Nor is a model whose kind nothing says: the picker * cannot pass the flag that `serve` would need for it. */ export declare function serveChoices(downloaded: DownloadedModel[]): ServeChoice[]; export type PickDeps = { downloaded: () => DownloadedModel[]; /** Both ends of the terminal are a TTY, so a prompt can be drawn and read. */ tty: boolean; /** Asks which models to serve. Null when the prompt was cancelled. */ ask: (choices: ServeChoice[]) => Promise; }; /** The models to serve when the command line named none. An empty array * means the user cancelled or ticked nothing, and the caller should exit * without serving. Throws when there is nothing to offer, or when there is * no terminal to ask on. */ export declare function pickModelsToServe(deps: PickDeps): Promise; export type Child = { on: (ev: "exit", cb: (code: number | null, signal: NodeJS.Signals | null) => void) => unknown; /** SIGTERM when no signal is given. */ kill: (signal?: NodeJS.Signals) => unknown; }; export type ServeDeps = { spawn: (python: string, args: string[]) => Child; fetch: typeof fetch; exec: Exec; totalmem: () => number; /** The memory a lazy model may take now. */ availableMemory: () => Promise; /** Resolves after `ms`. Tests pass one that does not wait. */ wait: (ms: number) => Promise; log: (line: string) => void; freePort: () => Promise; cacheDir: string; home: string; env: Record; configuredPython: string | undefined; /** `client.adaptersDir`, absolute, or null when unset. */ adaptersDir: string | null; /** `client.controlnetsDir`, absolute, or null when unset. */ controlnetsDir: string | null; /** Whether the request log is colored. Off when stdout is not a terminal. */ useColor: boolean; }; export type ServeHandle = { port: number; /** The server's address, for a client's base URL: `http://127.0.0.1:/v1`. */ url: string; models: string[]; /** Resolves with a message when a process dies after it was ready. */ failure: Promise; /** Each model's state, the same list `GET /v1/agency/status` returns. */ status: () => ModelStatus[]; /** Starts a model's process and resolves when the model is ready. */ load: (model: string) => Promise; /** Stops a model's process. It stays stopped until `load`. */ unload: (model: string) => Promise; /** Ends every request running on a model, and restarts the model's * process when that is the only way to stop its work. */ cancel: (model: string) => Promise; /** Resolves when `POST /v1/agency/shutdown` is called. The server is * closing by then. */ shutdownRequested: Promise; /** Refuses new requests, stops every process and waits for it to exit, * then closes the port. Calling it again returns the same promise. */ close: () => Promise; }; /** The options one served model takes for itself, written after it on the * command line: `agency local serve --draft --draft-tokens 3`. */ export type ModelOptions = { /** A smaller model of the same family that drafts tokens for this one, * for speculative decoding. The same forms as a served model. */ draft?: string; /** How many tokens the draft guesses at a time. Default DEFAULT_DRAFT_TOKENS. */ draftTokens?: number; }; /** The settings of a server as a whole, as opposed to those of one model * it serves. */ export type ServeSettings = ReplyLimits & { /** The port to listen on. 0 asks the operating system for a free one. */ port?: number; maxTokens?: number; /** Tokens of prompt read per pass. Default from the machine's memory, * see prefillStepSize. */ prefillStep?: number; python?: string; /** Print each request's prompt and reply under its summary line. The CLI * spells this `--log-prompts`, since `--verbose` is already the whole * CLI's own flag. */ logPrompts?: boolean; }; /** The `serve` command line as commander parsed it: the server's settings, * and the models each naming flag listed. */ export type ServeFlags = ServeSettings & { /** Per-model options, keyed by the model as it was named on the command * line. `groupServeArgv` builds this from the argv order. */ options?: Record; /** Models to serve with the embedding server rather than mlx_lm.server. */ embedding?: string[]; /** Models to serve with the speech server on /v1/audio/speech. */ speech?: string[]; /** Models to serve with the image server on /v1/images/generations. */ image?: string[]; vlm?: string[]; /** Models to load on their first request. */ lazy?: string[]; }; /** The dependencies of a server started from TypeScript: everything it * would print goes to `log`, the model processes' output included. */ export declare function loggedDeps(log: (line: string) => void): ServeDeps; /** A model in the banner. `dir` is where it was found, when there is one, * so the banner can read which vision family it is. */ export type BannerModel = { name: string; kind: ServeKind; dir?: string; runtime?: ChatRuntime | null; lazy?: boolean; }; /** The image families that only edit pictures: they refuse a prompt with * no picture, so the banner's example passes one. These are the rows of * `FAMILIES` in diffusersImageRules.py with no plain class, and a test * compares the two. */ export declare const EDIT_ONLY_IMAGE_FAMILIES: string[]; /** What `serve` prints once every process is ready: the models, in plan * order, and a sample command for the first model of each kind. */ export declare function servingBanner(port: number, models: BannerModel[]): string[]; /** Serves the models a parsed `agency local serve` command line names. */ export declare function runServe(values: string[], flags: ServeFlags, deps?: ServeDeps): Promise; /** Resolve, check, start one process per model, and open the door. */ export declare function serveTargets(targets: ServeTarget[], settings: ServeSettings, deps?: ServeDeps): Promise; /** The CLI entry: serve until Ctrl-C, or until a process dies. With no * model named, ask which of the downloaded ones to serve. `valueFlags` * says which of the command line's flags take a value, so the per-model * options can be read from `argv` in order. */ export declare function localServe(values: string[], flags: ServeFlags, valueFlags: ValueFlags, argv?: string[]): Promise; export type ChatRuntime = "mlx-lm" | "mlx-vlm"; export type ChatFlag = "reasoningBudget" | "hedgeLimit" | "repeatLimit" | "limitAnswers" | "prefillStep" | "draft"; export type ChatLaunch = { modelDir: string; port: number; settings: ChatServerSettings; }; export type ChatRuntimeSpec = { program: string; modules: string[]; args: (launch: ChatLaunch) => string[]; flags: ChatFlag[]; bannerNote: string; example: (name: string) => string[]; rules: RequestRules | null; /** Whether the runtime stops generating when the connection to it * closes, for a request with this body. What happens after a client * leaves, by kind of model: * * image stops after the step in progress * speech stops at the next check for a closed * connection * vision a request waiting its turn is dropped; * one that started finishes in seconds * embedding finishes, in under a second * chat on mlx-lm stops; the server looks at the * connection on a clock * chat on mlx-vlm, streamed stops * chat on mlx-vlm, not streamed keeps generating until the reply is * complete * * Only the last row is false, and `cancel` stops that process. */ stopsOnClose: (body: Record) => boolean; }; export declare const CHAT_RUNTIMES: Record;