LM Studio
Run Llama, Mistral, Phi-3 locally on your computer.
app
About LM Studio
Learn how to run Llama, DeepSeek, Phi, and other LLMs locally with LM Studio.
LM Studio is a desktop app for developing and experimenting with LLMs locally on your computer.
Key functionality
- A desktop application for running local LLMs
- A familiar chat interface
- Search & download functionality (via Hugging Face 🤗)
- A local server that can listen on OpenAI-like endpoints
- Systems for managing local models and configurations
How do I install LM Studio?
Head over to the Downloads page and download an installer for your operating system.
LM Studio is available for macOS, Windows, and Linux.
System requirements
LM Studio generally supports Apple Silicon Macs, x64/ARM64 Windows PCs, and x64 Linux PCs.
Consult the System Requirements page for more detailed information.
Run llama.cpp (GGUF) or MLX models
LM Studio supports running LLMs on Mac, Windows, and Linux using llama.cpp.
On Apple Silicon Macs, LM Studio also supports running LLMs using Apple's MLX.
To install or manage LM Runtimes, press ⌘ Shift R on Mac or Ctrl Shift R on Windows/Linux.
Run an LLM like Llama, Phi, or DeepSeek R1 on your computer
To run an LLM on your computer you first need to download the model weights.
You can do this right within LM Studio! See Download an LLM for guidance.
Chat with documents entirely offline on your computer
You can attach documents to your chat messages and interact with them entirely offline, also known as "RAG".
Read more about how to use this feature in the Chat with Documents guide.
Use LM Studio's API from your own apps and scripts
LM Studio provides a REST API that you can use to interact with your local models from your own apps and scripts.
Community
Join the LM Studio community on Discord to ask questions, share knowledge, and get help from other users and the LM Studio team.
API Changelog
LM Studio API Changelog - new features and updates
👾 LM Studio 0.3.9 • 2025-01-30
Idle TTL and Auto Evict
Set a TTL (in seconds) for models loaded via API requests (docs article: Idle TTL and Auto-Evict)
curl http://localhost:1234/api/v0/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-r1-distill-qwen-7b",
"messages": [ ... ]
+ "ttl": 300,
}'
With lms:
lms load --ttl <seconds>
Separate reasoning_content in Chat Completion responses
For DeepSeek R1 models, get reasoning content in a separate field. See more here.
Turn this on in App Settings > Developer.
👾 LM Studio 0.3.6 • 2025-01-06
Tool and Function Calling API
Use any LLM that supports Tool Use and Function Calling through the OpenAI-like API.
Docs: Tool Use and Function Calling.
👾 LM Studio 0.3.5 • 2024-10-22
Introducing lms get: download models from the terminal
You can now download models directly from the terminal using a keyword
lms get deepseek-r1
or a full Hugging Face URL
lms get <hugging face url>
To filter for MLX models only, add --mlx to the command.
lms get deepseek-r1 --mlx
System Requirements
Supported CPU, GPU types for LM Studio on Mac (M1/M2/M3/M4), Windows (x64/ARM), and Linux (x64)
Minimum system requirements for running LM Studio.
We are actively working to add support for more platforms and configurations. If you noticed an error in this page, please let us know by opening an issue on github.
macOS
- Chip: Apple Silicon (M1/M2/M3/M4).
- macOS 13.4 or newer is required.
- For MLX models, macOS 14.0 or newer is required.
- 16GB+ RAM recommended.
- You may still be able to use LM Studio on 8GB Macs, but stick to smaller models and modest context sizes.
- Intel-based Macs are currently not supported. Chime in here if you are interested in this.
Windows
LM Studio is supported on both x64 and ARM (Snapdragon X Elite) based systems.
- CPU: AVX2 instruction set support is required (for x64)
- RAM: LLMs can consume a lot of RAM. At least 16GB of RAM is recommended.
Linux
- LM Studio for Linux is distributed as an AppImage.
- Ubuntu 20.04 or newer is required
- x64 only, aarch64 not yet supported
- Ubuntu versions newer than 22 are not well tested. Let us know if you're running into issues by opening a bug here.
- CPU:
- LM Studio ships with AVX2 support by default
Offline Operation
LM Studio can operate entirely offline, just make sure to get some model files first.
In general, LM Studio does not require the internet in order to work. This includes core functions like chatting with models, chatting with documents, or running a local server, none of which require the internet.
Operations that do NOT require connectivity
Using downloaded LLMs
Once you have an LLM onto your machine, the model will run locally and you should be good to go entirely offline. Nothing you enter into LM Studio when chatting with LLMs leaves your device.
Chatting with documents (RAG)
When you drag and drop a document into LM Studio to chat with it or perform RAG, that document stays on your machine. All document processing is done locally, and nothing you upload into LM Studio leaves the application.
Running a local server
LM Studio can be used as a server to provide LLM inferencing on localhost or the local network. Requests to LM Studio use OpenAI endpoints and return OpenAI-like response objects, but stay local.
Operations that require connectivity
Several operations, described below, rely on internet connectivity. Once you get an LLM onto your machine, you should be good to go entirely offline.
Searching for models
When you search for models in the Discover tab, LM Studio makes network requests (e.g. to huggingface.co). Search will not work without internet connection.
Downloading new models
In order to download models you need a stable (and decently fast) internet connection. You can also 'sideload' models (use models that were procured outside the app). See instructions for sideloading models.
Discover tab's model catalog
Any given version of LM Studio ships with an initial model catalog built-in. The entries in the catalog are typically the state of the online catalog near the moment we cut the release. However, in order to show stats and download options for each model, we need to make network requests (e.g. to huggingface.co).
Downloading runtimes
LM Runtimes are individually packaged software libraries, or LLM engines, that allow running certain formats of models (e.g. llama.cpp). As of LM Studio 0.3.0 (read the announcement) it's easy to download and even hot-swap runtimes without a full LM Studio update. To check for available runtimes, and to download them, we need to make network requests.
Checking for app updates
On macOS and Windows, LM Studio has a built-in app updater that's capable. The linux in-app updater is in the works. When you open LM Studio, the app updater will make a network request to check if there are any new updates available. If there's a new version, the app will show you a notification to update now or later. Without internet connectivity you will not be able to update the app via the in-app updater.
basics
Get started with LM Studio
Download and run Large Language Models (LLMs) like Llama 3.1, Phi-3, and Gemma 2 locally in LM Studio
You can use openly available Large Language Models (LLMs) like Llama 3.1, Phi-3, and Gemma 2 locally in LM Studio, leveraging your computer's CPU and optionally the GPU.
Double check computer meets the minimum system requirements.
You might sometimes see terms such as `open-source models` or `open-weights models`. Different models might be released under different licenses and varying degrees of 'openness'. In order to run a model locally, you need to be able to get access to its "weights", often distributed as one or more files that end with `.gguf`, `.safetensors` etc.
Getting up and running
First, install the latest version of LM Studio. You can get it from here.
Once you're all set up, you need to download your first LLM.
1. Download an LLM to your computer
Head over to the Discover tab to download models. Pick one of the curated options or search for models by search query (e.g. "Llama"). See more in-depth information about downloading models here.
2. Load a model to memory
Head over to the Chat tab, and
- Open the model loader
- Select one of the models you downloaded (or sideloaded).
- Optionally, choose load configuration parameters.
What does loading a model mean?
Loading a model typically means allocating memory to be able to accomodate the model's weights and other parameters in your computer's RAM.
3. Chat!
Once the model is loaded, you can start a back-and-forth conversation with the model in the Chat tab.
Community
Chat with other LM Studio users, discuss LLMs, hardware, and more on the LM Studio Discord server.
Manage chats
Manage conversation threads with LLMs
LM Studio has a ChatGPT-like interface for chatting with local LLMs. You can create many different conversation threads and manage them in folders.
Create a new chat
You can create a new chat by clicking the "+" button or by using a keyboard shortcut: ⌘ + N on Mac, or ctrl + N on Windows / Linux.
Create a folder
Create a new folder by clicking the new folder button or by pressing: ⌘ + shift + N on Mac, or ctrl + shift + N on Windows / Linux.
Drag and drop
You can drag and drop chats in and out of folders, and even drag folders into folders!
Duplicate chats
You can duplicate a whole chat conversation by clicking the ••• menu and selecting "Duplicate". If the chat has any files in it, they will be duplicated too.
FAQ
Where are chats stored in the file system?
Right-click on a chat and choose "Reveal in Finder" / "Show in File Explorer". Conversations are stored in JSON format. It is NOT recommended to edit them manually, nor to rely on their structure.
Does the model learn from chats?
The model doesn't 'learn' from chats. The model only 'knows' the content that is present in the chat or is provided to it via configuration options such as the "system prompt".
Conversations folder filesystem path
Mac / Linux:
~/.lmstudio/conversations/
Windows:
%USERPROFILE%\.lmstudio\conversations
Community
Chat with other LM Studio users, discuss LLMs, hardware, and more on the LM Studio Discord server.
Download an LLM
Discover and download supported LLMs in LM Studio
LM Studio comes with a built-in model downloader that let's you download any supported model from Hugging Face.
Searching for models
You can search for models by keyword (e.g. llama, gemma, lmstudio), or by providing a specific user/model string. You can even insert full Hugging Face URLs into the search bar!
Pro tip: you can jump to the Discover tab from anywhere by pressing ⌘ + 2 on Mac, or ctrl + 2 on Windows / Linux.
Which download option to choose?
You will often see several options for any given model named things like Q3_K_S, Q_8 etc. These are all copies of the same model, provided in varying degrees of fidelity. The Q represents a technique called "Quantization", which roughly means compressing model files in size, while giving up some degree of quality.
Choose a 4-bit option or higher if your machine is capable enough for running it.
Advanced
Changing the models directory
You can change the models directory by heading to My Models
Community
Chat with other LM Studio users, discuss LLMs, hardware, and more on the LM Studio Discord server.
Chat with Documents
How to provide local documents to an LLM as additional context
You can attach document files (.docx, .pdf, .txt) to chat sessions in LM Studio.
This will provide additional context to LLMs you chat with through the app.
Terminology
- Retrieval: Identifying relevant portion of a long source document
- Query: The input to the retrieval operation
- RAG: Retrieval-Augmented Generation*
- Context: the 'working memory' of an LLM. Often limited at a few thousand words**
* In this context, 'Generation' means the output of the LLM.
** A recent trend in newer LLMs is support for larger context sizes.
Context sizes are measured in "tokens". One token is often about 3/4 of a word.
RAG vs. Full document 'in context'
If the document is short enough (i.e., if it fits in the model's context), LM Studio will add the file contents to the conversation in full. This is particularly useful for models that support longer context sizes such as Meta's Llama 3.1 and Mistral Nemo.
If the document is very long, LM Studio will opt into using "Retrieval Augmented Generation", frequently referred to as "RAG". RAG means attempting to fish out relevant bits of a very long document (or several documents) and providing them to the model for reference. This technique sometimes works really well, but sometimes it requires some tuning and experimentation.
Tip for successful RAG
provide as much context in your query as possible. Mention terms, ideas, and words you expect to be in the relevant source material. This will often increase the chance the system will provide useful context to the LLM. As always, experimentation is the best way to find what works best.
Import Models
Use model files you've downloaded outside of LM Studio
You can use compatible models you've downloaded outside of LM Studio by placing them in the expected directory structure.
Use lms import (experimental)
To import a GGUF model you've downloaded outside of LM Studio, run the following command in your terminal:
lms import <path/to/model.gguf>
Follow the interactive prompt to complete the import process.
LM Studio's expected models directory structure
LM Studio aims to preserves the directory structure of models downloaded from Hugging Face. The expected directory structure is as follows:
~/.lmstudio/models/
└── publisher/
└── model/
└── model-file.gguf
For example, if you have a model named ocelot-v1 published by infra-ai, the structure would look like this:
~/.lmstudio/models/
└── infra-ai/
└── ocelot-v1/
└── ocelot-v1-instruct-q4_0.gguf
Community
Chat with other LM Studio users, discuss LLMs, hardware, and more on the LM Studio Discord server.
Config Presets
Save your system prompts and other parameters as Presets for easy reuse across chats.
Configuration Presets are new in LM Studio 0.3.3 (Release Notes)
The Use Case for Presets
- Save your system prompts, inference parameters as a named
Preset. - Easily switch between different use cases, such as reasoning, creative writing, multi-turn conversations, or brainstorming.
For migration from LM Studio 0.2.* Presets, see below.
Please report bugs and feedback to bugs [at] lmstudio [dot] ai.
Build your own Prompt Library
You can create your own prompt library by using Presets.
In addition to system prompts, every parameter under the Advanced Configuration sidebar can be recorded in a named Preset.
For example, you might want to always use a certain Temperature, Top P, or Max Tokens for a particular use case. You can save these settings as a Preset (with or without a system prompt) and easily switch between them.
Saving, resetting, and deselecting Presets
Below is the anatomy of the Preset manager:
Migration from LM Studio 0.2.* Presets
- Presets you've saved in LM Studio 0.2.* are automatically readable in 0.3.3 with no migration step needed.
- If you save new changes in a legacy preset, it'll be copied to a new format upon save.
- The old files are NOT deleted.
- Notable difference: Load parameters are not included in the new preset format.
- Favor editing the model's default config in My Models. See how to do it here.
Where Presets are stored
Presets are stored in the following directory:
macOS or Linux
~/.lmstudio/config-presets
Windows
%USERPROFILE%\.lmstudio\config-presets
Community
Chat with other LM Studio users, discuss LLMs, hardware, and more on the LM Studio Discord server.
user-interface
LM Studio in your language
LM Studio is available in English, Spanish, French, German, Korean, Russian, and 6+ more languages.
LM Studio is available in English, Spanish, Japanese, Chinese, German, Norwegian, Turkish, Russian, Korean, Polish, Vietnamese, Czech, Ukranian, and Portuguese (BR,PT) thanks to incredibly awesome efforts from the LM Studio community.
Selecting a Language
You can choose a language in the Settings tab.
Use the dropdown menu under Preferences > Language.
You can jump to Settings from anywhere in the app by pressing `cmd` + `,` on macOS or `ctrl` + `,` on Windows/Linux.
To get to the Settings page, you need to be on Power User mode or higher.
Big thank you to community localizers 🙏
- Spanish @xtianpaiva
- Norwegian @Exlo84
- German @marcelMaier
- Turkish @progesor
- Russian @shelomitsky, @mlatysh, @Adjacentai
- Korean @williamjeong2
- Polish @danieltechdev
- Czech @ladislavsulc
- Vietnamese @trinhvanminh
- Portuguese (BR) @Sm1g00l
- Portuguese (PT) @catarino
- Chinese (zh-HK), (zh-TW), (zh-CN) @neotan
- Chinese (zh-Hant) @kywarai
- Ukrainian (uk) @hmelenok
- Japanese (ja) @digitalsp
Still under development (due to lack of RTL support in LM Studio)
- Hebrew: @NHLOCAL
Contributing to LM Studio localization
If you want to improve existing translations or contribute new ones, you're more than welcome to jump in.
LM Studio strings are maintained in https://github.com/lmstudio-ai/localization.
See instructions for contributing here.
User, Power User, or Developer
Hide or reveal advanced features
Starting LM Studio 0.3.0, you can switch between the following modes:
- User
- Power User
- Developer
Selecting a Mode
You can configure LM Studio to run in increasing levels of configurability.
Select between User, Power User, and Developer.
Which mode should I choose?
User
Show only the chat interface, and auto-configure everything. This is the best choice for beginners or anyone who's happy with the default settings.
Power User
Use LM Studio in this mode if you want access to configurable load and inference parameters as well as advanced chat features such as insert, edit, & continue (for either role, user or assistant).
Developer
Full access to all aspects in LM Studio. This includes keyboard shortcuts and development features. Check out the Developer section under Settings for more.
Color Themes
Customize LM Studio's color theme
LM Studio comes with a few built-in themes for app-wide color palettes.
Selecting a Theme
You can choose a theme in the Settings tab.
Choosing the "Auto" option will automatically switch between Light and Dark themes based on your system settings.
You can jump to Settings from anywhere in the app by pressing `cmd` + `,` on macOS or `ctrl` + `,` on Windows/Linux.
To get to the Settings page, you need to be on Power User mode or higher.
advanced
Per-model Defaults
You can set default settings for each model in LM Studio
Advanced
You can set default load settings for each model in LM Studio.
When the model is loaded anywhere in the app (including through lms load) these settings will be used.
Setting default parameters for a model
Head to the My Models tab and click on the gear ⚙️ icon to edit the model's default parameters.
This will open a dialog where you can set the default parameters for the model.
Next time you load the model, these settings will be used.
#### Reasons to set default load parameters (not required, totally optional)
- Set a particular GPU offload settings for a given model
- Set a particular context size for a given model
- Whether or not to utilize Flash Attention for a given model
Advanced Topics
Changing load settings before loading a model
When you load a model, you can optionally change the default load settings.
Saving your changes as the default settings for a model
If you make changes to load settings when you load a model, you can save them as the default settings for that model.
Community
Chat with other LM Studio power users, discuss configs, models, hardware, and more on the LM Studio Discord server.
Prompt Template
Optionally set or modify the model's prompt template
Advanced
By default, LM Studio will automatically configure the prompt template based on the model file's metadata.
However, you can customize the prompt template for any model.
Overriding the Prompt Template for a Specific Model
Head over to the My Models tab and click on the gear ⚙️ icon to edit the model's default parameters.
Pro tip: you can jump to the My Models tab from anywhere by pressing ⌘ + 3 on Mac, or ctrl + 3 on Windows / Linux.
Customize the Prompt Template
💡 In most cases you don't need to change the prompt template
When a model doesn't come with a prompt template information, LM Studio will surface the Prompt Template config box in the 🧪 Advanced Configuration sidebar.
You can make this config box always show up by right clicking the sidebar and selecting Always Show Prompt Template.
Prompt template options
Jinja Template
You can express the prompt template in Jinja.
💡 Jinja is a templating engine used to encode the prompt template in several popular LLM model file formats.
Manual
You can also express the prompt template manually by specifying message role prefixes and suffixes.
Reasons you might want to edit the prompt template:
- The model's metadata is incorrect, incomplete, or LM Studio doesn't recognize it
- The model does not have a prompt template in its metadata (e.g. custom or older models)
- You want to customize the prompt template for a specific use case
Speculative Decoding
Speed up generation with a draft model
Advanced
Speculative decoding is a technique that can substantially increase the generation speed of large language models (LLMs) without reducing response quality.
🔔 Speculative Decoding requires LM Studio 0.3.10 or newer, currently in beta. Get it here.
What is Speculative Decoding
Speculative decoding relies on the collaboration of two models:
- A larger, "main" model
- A smaller, faster "draft" model
During generation, the draft model rapidly proposes potential tokens (subwords), which the main model can verify faster than it would take it to generate them from scratch. To maintain quality, the main model only accepts tokens that match what it would have generated. After the last accepted draft token, the main model always generates one additional token.
For a model to be used as a draft model, it must have the same "vocabulary" as the main model.
How to enable Speculative Decoding
On Power User mode or higher, load a model, then select a Draft Model within the Speculative Decoding section of the chat sidebar:
Finding compatible draft models
You might see the following when you open the dropdown:
Try to download a lower parameter variant of the model you have loaded, if it exists. If no smaller versions of your model exist, find a pairing that does.
For example:
| Main Model | Draft Model |
|---|---|
| Llama 3.1 8B Instruct | Llama 3.2 1B Instruct |
| Qwen 2.5 14B Instruct | Qwen 2.5 0.5B Instruct |
| DeepSeek R1 Distill Qwen 32B | DeepSeek R1 Distill Qwen 1.5B |
Once you have both a main and draft model loaded, simply begin chatting to enable speculative decoding.
Key factors affecting performance
Speculative decoding speed-up is generally dependent on two things:
- How small and fast the draft model is compared with the main model
- How often the draft model is able to make "good" suggestions
In simple terms, you want to choose a draft model that's much smaller than the main model. And some prompts will work better than others.
An important trade-off
Running a draft model alongside a main model to enable speculative decoding requires more computation and resources than running the main model on its own.
The key to faster generation of the main model is choosing a draft model that's both small and capable enough.
Here are general guidelines for the maximum draft model size you should select based on main model size (in parameters):
| Main Model Size | Max Draft Model Size to Expect Speed-Ups |
|---|---|
| 3B | - |
| 7B | 1B |
| 14B | 3B |
| 32B | 7B |
Generally, the larger the size difference is between the main model and the draft model, the greater the speed-up.
Note: if the draft model is not fast enough or effective enough at making "good" suggestions to the main model, the generation speed will not increase, and could actually decrease.
Prompt dependent
One thing you will likely notice when using speculative decoding is that the generation speed is not consistent across all prompts.
The reason that the speed-up is not consistent across all prompts is because for some prompts, the draft model is less likely to make "good" suggestions to the main model.
Here are some extreme examples that illustrate this concept:
1. Discrete Example: Mathematical Question
Prompt: "What is the quadratic equation formula?"
In this case, both a 70B model and a 0.5B model are both very likely to give the standard formula x = (-b ± √(b² - 4ac))/(2a). So if the draft model suggested this formula as the next tokens, the target model would likely accept it, making this an ideal case for speculative decoding to work efficiently.
2. Creative Example: Story Generation
Prompt: "Write a story that begins: 'The door creaked open...'"
In this case, the smaller model's draft tokens are likely be rejected more often by the larger model, as each next word could branch into countless valid possibilities.
While "4" is the only reasonable answer to "2+2", this story could continue with "revealing a monster", "as the wind howled", "and Sarah froze", or hundreds of other perfectly valid continuations, making the smaller model's specific word predictions much less likely to match the larger model's choices.
api
LM Studio as a Local LLM API Server
Run an LLM API server on localhost with LM Studio
You can serve local LLMs from LM Studio's Developer tab, either on localhost or on the network.
LM Studio's APIs can be used through an OpenAI compatibility mode, ehanced REST API, or through a client library like lmstudio-js.
API options
- OpenAI Compatibility endpoints
- LM Studio REST API (new, in beta)
- TypeScript SDK -
lmstudio-js
Run LM Studio as a service (headless)
GUI-less operation of LM Studio: run in the background, start on machine login, and load models on demand
Advanced
Starting in v0.3.5, LM Studio can be run as a service without the GUI. This is useful for running LM Studio on a server or in the background on your local machine.
Run LM Studio as a service
Running LM Studio as a service consists of several new features intended to make it more efficient to use LM Studio as a developer tool.
- The ability to run LM Studio without the GUI
- The ability to start the LM Studio LLM server on machine login, headlessly
- On-demand model loading
Run the LLM service on machine login
To enable this, head to app settings (Cmd / Ctrl + ,) and check the box to run the LLM server on login.
When this setting is enabled, exiting the app will minimize it to the system tray, and the LLM server will continue to run in the background.
Just-In-Time (JIT) model loading for OpenAI endpoints
Useful when utilizing LM Studio as an LLM service with other frontends or applications.
When JIT loading is ON:
- Call to
/v1/modelswill return all downloaded models, not only the ones loaded into memory - Calls to inference endpoints will load the model into memory if it's not already loaded
When JIT loading is OFF:
- Call to
/v1/modelswill return only the models loaded into memory - You have to first load the model into memory before being able to use it
What about auto unloading?
As of LM Studio 0.3.5, auto unloading is not yet in place. Models that are loaded via JIT loading will remain in memory until you unload them. We expect to implement more sophisticated memory management in the near future. Let us know if you have any feedback or suggestions.
Auto Server Start
Your last server state will be saved and restored on app or service launch.
To achieve this programmatically, you can use the following command:
lms server start
If you haven't already, bootstrap `lms` on your machine by following the instructions [here](/docs/cli).
Community
Chat with other LM Studio developers, discuss LLMs, hardware, and more on the LM Studio Discord server.
Please report bugs and issues in the lmstudio-bug-tracker GitHub repository.
Idle TTL and Auto-Evict
Optionally auto-unload idle models after a certain amount of time (TTL)
ℹ️ Requires LM Studio 0.3.9 (b1), currently in beta. Download from [here](https://lmstudio.ai/beta-releases)
LM Studio 0.3.9 (b1) introduces the ability to set a time-to-live (TTL) for API models, and optionally auto-evict previously loaded models before loading new ones.
These features complement LM Studio's on-demand model loading (JIT) to automate efficient memory management and reduce the need for manual intervention.
Background
-
JIT loadingmakes it easy to use your LM Studio models in other apps: you don't need to manually load the model first before being able to use it. However, this also means that models can stay loaded in memory even when they're not being used.[Default: enabled] -
(New)
Idle TTL(technically: Time-To-Live) defines how long a model can stay loaded in memory without receiving any requests. When the TTL expires, the model is automatically unloaded from memory. You can set a TTL using thettlfield in your request payload.[Default: 60 minutes] -
(New)
Auto-Evictis a feature that unloads previously JIT loaded models before loading new ones. This enables easy switching between models from client apps without having to manually unload them first. You can enable or disable this feature in Developer tab > Server Settings.[Default: enabled]
Idle TTL
Use case: imagine you're using an app like Zed, Cline, or Continue.dev to interact with LLMs served by LM Studio. These apps leverage JIT to load models on-demand the first time you use them.
Problem: When you're not actively using a model, you might don't want it to remain loaded in memory.
Solution: Set a TTL for models loaded via API requests. The idle timer resets every time the model receives a request, so it won't disappear while you use it. A model is considered idle if it's not doing any work. When the idle TTL expires, the model is automatically unloaded from memory.
Set App-default Idle TTL
By default, JIT-loaded models have a TTL of 60 minutes. You can configure a default TTL value for any model loaded via JIT like so:
Set per-model TTL-model in API requests
When JIT loading is enabled, the first request to a model will load it into memory. You can specify a TTL for that model in the request payload.
This works for requests targeting both the OpenAI compatibility API and the LM Studio's REST API:
curl http://localhost:1234/api/v0/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-r1-distill-qwen-7b",
+ "ttl": 300,
"messages": [ ... ]
}'
This will set a TTL of 5 minutes (300 seconds) for this model if it is JIT loaded.
Set TTL for models loaded with lms
By default, models loaded with lms load do not have a TTL, and will remain loaded in memory until you manually unload them.
You can set a TTL for a model loaded with lms like so:
lms load <model> --ttl 3600
Load a <model> with a TTL of 1 hour (3600 seconds)
Specify TTL when loading models in the server tab
You can also set a TTL when loading a model in the server tab like so
Configure Auto-Evict for JIT loaded models
With this setting, you can ensure new models loaded via JIT automatically unload previously loaded models first.
This is useful when you want to switch between models from another app without worrying about memory building up with unused models.
When Auto-Evict is ON (default):
- At most
1model is kept loaded in memory at a time (when loaded via JIT) - Non-JIT loaded models are not affected
When Auto-Evict is OFF:
- Switching models from an external app will keep previous models loaded in memory
- Models will remain loaded until either:
- Their TTL expires
- You manually unload them
This feature works in tandem with TTL to provide better memory management for your workflow.
Nomenclature
TTL: Time-To-Live, is a term borrowed from networking protocols and cache systems. It defines how long a resource can remain allocated before it's considered stale and evicted.
Structured Output
Enforce LLM response formats using JSON schemas.
You can enforce a particular response format from an LLM by providing a JSON schema to the /v1/chat/completions endpoint, via LM Studio's REST API (or via any OpenAI client).
Start LM Studio as a server
To use LM Studio programatically from your own code, run LM Studio as a local server.
You can turn on the server from the "Developer" tab in LM Studio, or via the lms CLI:
lms server start
Install lms by running npx lmstudio install-cli
This will allow you to interact with LM Studio via an OpenAI-like REST API. For an intro to LM Studio's OpenAI-like API, see Running LM Studio as a server.
Structured Output
The API supports structured JSON outputs through the /v1/chat/completions endpoint when given a JSON schema. Doing this will cause the LLM to respond in valid JSON conforming to the schema provided.
It follows the same format as OpenAI's recently announced Structured Output API and is expected to work via the OpenAI client SDKs.
Example using curl
This example demonstrates a structured output request using the curl utility.
To run this example on Mac or Linux, use any terminal. On Windows, use Git Bash.
curl http://{{hostname}}:{{port}}/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "{{model}}",
"messages": [
{
"role": "system",
"content": "You are a helpful jokester."
},
{
"role": "user",
"content": "Tell me a joke."
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "joke_response",
"strict": "true",
"schema": {
"type": "object",
"properties": {
"joke": {
"type": "string"
}
},
"required": ["joke"]
}
}
},
"temperature": 0.7,
"max_tokens": 50,
"stream": false
}'
All parameters recognized by /v1/chat/completions will be honored, and the JSON schema should be provided in the json_schema field of response_format.
The JSON object will be provided in string form in the typical response field, choices[0].message.content, and will need to be parsed into a JSON object.
Example using python
from openai import OpenAI
import json
# Initialize OpenAI client that points to the local LM Studio server
client = OpenAI(
base_url="http://localhost:1234/v1",
api_key="lm-studio"
)
# Define the conversation with the AI
messages = [
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Create 1-3 fictional characters"}
]
# Define the expected response structure
character_schema = {
"type": "json_schema",
"json_schema": {
"name": "characters",
"schema": {
"type": "object",
"properties": {
"characters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"occupation": {"type": "string"},
"personality": {"type": "string"},
"background": {"type": "string"}
},
"required": ["name", "occupation", "personality", "background"]
},
"minItems": 1,
}
},
"required": ["characters"]
},
}
}
# Get response from AI
response = client.chat.completions.create(
model="your-model",
messages=messages,
response_format=character_schema,
)
# Parse and display the results
results = json.loads(response.choices[0].message.content)
print(json.dumps(results, indent=2))
Important: Not all models are capable of structured output, particularly LLMs below 7B parameters.
Check the model card README if you are unsure if the model supports structured output.
Structured output engine
- For
GGUFmodels: utilizellama.cpp's grammar-based sampling APIs. - For
MLXmodels: using Outlines.
The MLX implementation is available on Github: lmstudio-ai/mlx-engine.
Community
Chat with other LM Studio users, discuss LLMs, hardware, and more on the LM Studio Discord server.
Tool Use
Enable LLMs to interact with external functions and APIs.
Tool use enables LLMs to request calls to external functions and APIs through the /v1/chat/completions endpoint, via LM Studio's REST API (or via any OpenAI client). This expands their functionality far beyond text output.
🔔 Tool use requires LM Studio 0.3.6 or newer, get it here
Quick Start
1. Start LM Studio as a server
To use LM Studio programmatically from your own code, run LM Studio as a local server.
You can turn on the server from the "Developer" tab in LM Studio, or via the lms CLI:
lms server start
Install lms by running npx lmstudio install-cli
This will allow you to interact with LM Studio via an OpenAI-like REST API. For an intro to LM Studio's OpenAI-like API, see Running LM Studio as a server.
2. Load a Model
You can load a model from the "Chat" or "Developer" tabs in LM Studio, or via the lms CLI:
lms load
3. Copy, Paste, and Run an Example!
CurlPython
Tool Use
What really is "Tool Use"?
Tool use describes:
- LLMs output text requesting functions to be called (LLMs cannot directly execute code)
- Your code executes those functions
- Your code feeds the results back to the LLM.
High-level flow
┌──────────────────────────┐
│ SETUP: LLM + Tool list │
└──────────┬───────────────┘
▼
┌──────────────────────────┐
│ Get user input │◄────┐
└──────────┬───────────────┘ │
▼ │
┌──────────────────────────┐ │
│ LLM prompted w/messages │ │
└──────────┬───────────────┘ │
▼ │
Needs tools? │
│ │ │
Yes No │
│ │ │
▼ └────────────┐ │
┌─────────────┐ │ │
│Tool Response│ │ │
└──────┬──────┘ │ │
▼ │ │
┌─────────────┐ │ │
│Execute tools│ │ │
└──────┬──────┘ │ │
▼ ▼ │
┌─────────────┐ ┌───────────┐
│Add results │ │ Normal │
│to messages │ │ response │
└──────┬──────┘ └─────┬─────┘
│ ▲
└───────────────────────┘
In-depth flow
LM Studio supports tool use through the /v1/chat/completions endpoint when given function definitions in the tools parameter of the request body. Tools are specified as an array of function definitions that describe their parameters and usage, like:
It follows the same format as OpenAI's Function Calling API and is expected to work via the OpenAI client SDKs.
We will use lmstudio-community/Qwen2.5-7B-Instruct-GGUF as the model in this example flow.
-
You provide a list of tools to an LLM. These are the tools that the model can request calls to. For example:
// the list of tools is model-agnostic [ { "type": "function", "function": { "name": "get_delivery_date", "description": "Get the delivery date for a customer's order", "parameters": { "type": "object", "properties": { "order_id": { "type": "string" } }, "required": ["order_id"] } } } ]This list will be injected into the
systemprompt of the model depending on the model's chat template. ForQwen2.5-Instruct, this looks like:<|im_start|>system You are Qwen, created by Alibaba Cloud. You are a helpful assistant. # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within <tools></tools> XML tags: <tools> {"type": "function", "function": {"name": "get_delivery_date", "description": "Get the delivery date for a customer's order", "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}}} </tools> For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags: <tool_call> {"name": <function-name>, "arguments": <args-json-object>} </tool_call><|im_end|>Important: The model can only request calls to these tools because LLMs cannot directly call functions, APIs, or any other tools. They can only output text, which can then be parsed to programmatically call the functions.
-
When prompted, the LLM can then decide to either:
- (a) Call one or more tools
User: Get me the delivery date for order 123 Model: <tool_call> {"name": "get_delivery_date", "arguments": {"order_id": "123"}} </tool_call>- (b) Respond normally
User: Hi Model: Hello! How can I assist you today? -
LM Studio parses the text output from the model into an OpenAI-compliant
chat.completionresponse object.- If the model was given access to
tools, LM Studio will attempt to parse the tool calls into theresponse.choices[0].message.tool_callsfield of thechat.completionresponse object. - If LM Studio cannot parse any correctly formatted tool calls, it will simply return the response to the standard
response.choices[0].message.contentfield. - Note: Smaller models and models that were not trained for tool use may output improperly formatted tool calls, resulting in LM Studio being unable to parse them into the
tool_callsfield. This is useful for troubleshooting when you do not receivetool_callsas expected. Example of an improperly formattingQwen2.5-Instructtool call:
<tool_call> ["name": "get_delivery_date", function: "date"] </tool_call>Note that the brackets are incorrect, and the call does not follow the
name, argumentformat. - If the model was given access to
-
Your code parses the
chat.completionresponse to check for tool calls from the model, then calls the appropriate tools with the parameters specified by the model. Your code then adds both:- The model's tool call message
- The result of the tool call
To the
messagesarray to send back to the model# pseudocode, see examples for copy-paste snippets if response.has_tool_calls: for each tool_call: # Extract function name & args function_to_call = tool_call.name # e.g. "get_delivery_date" args = tool_call.arguments # e.g. {"order_id": "123"} # Execute the function result = execute_function(function_to_call, args) # Add result to conversation add_to_messages([ ASSISTANT_TOOL_CALL_MESSAGE, # The request to use the tool TOOL_RESULT_MESSAGE # The tool's response ]) else: # Normal response without tools add_to_messages(response.content) -
The LLM is then prompted again with the updated messages array, but without access to tools. This is because:
- The LLM already has the tool results in the conversation history
- We want the LLM to provide a final response to the user, not call more tools
# Example messages messages = [ {"role": "user", "content": "When will order 123 be delivered?"}, {"role": "assistant", "function_call": { "name": "get_delivery_date", "arguments": {"order_id": "123"} }}, {"role": "tool", "content": "2024-03-15"}, ] response = client.chat.completions.create( model="lmstudio-community/qwen2.5-7b-instruct", messages=messages )The
response.choices[0].message.contentfield after this call may be something like:Your order #123 will be delivered on March 15th, 2024 -
The loop continues back at step 2 of the flow
Note: This is the pedantic flow for tool use. However, you can certainly experiment with this flow to best fit your use case.
Supported Models
Through LM Studio, all models support at least some degree of tool use.
However, there are currently two levels of support that may impact the quality of the experience: Native and Default.
Models with Native tool use support will have a hammer badge in the app, and generally perform better in tool use scenarios.
Native tool use support
"Native" tool use support means that both:
- The model has a chat template that supports tool use (usually means the model has been trained for tool use)
- This is what will be used to format the
toolsarray into the system prompt and tell them model how to format tool calls - Example: Qwen2.5-Instruct chat template
- This is what will be used to format the
- LM Studio supports that model's tool use format
- Required for LM Studio to properly input the chat history into the chat template, and parse the tool calls the model outputs into the
chat.completionobject
- Required for LM Studio to properly input the chat history into the chat template, and parse the tool calls the model outputs into the
Models that currently have native tool use support in LM Studio (subject to change):
- Qwen
GGUFlmstudio-community/Qwen2.5-7B-Instruct-GGUF (4.68 GB)MLXmlx-community/Qwen2.5-7B-Instruct-4bit (4.30 GB)
- Llama-3.1, Llama-3.2
GGUFlmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF (4.92 GB)MLXmlx-community/Meta-Llama-3.1-8B-Instruct-8bit (8.54 GB)
- Mistral
GGUFbartowski/Ministral-8B-Instruct-2410-GGUF (4.67 GB)MLXmlx-community/Ministral-8B-Instruct-2410-4bit (4.67 GB GB)
Default tool use support
"Default" tool use support means that either:
- The model does not have chat template that supports tool use (usually means the model has not been trained for tool use)
- LM Studio does not currently support that model's tool use format
Under the hood, default tool use works by:
- Giving models a custom system prompt and a default tool call format to use
- Converting
toolrole messages to theuserrole so that chat templates without thetoolrole are compatible - Converting
assistantroletool_callsinto the default tool call format
Results will vary by model.
You can see the default format by running lms log stream in your terminal, then sending a chat completion request with tools to a model that doesn't have Native tool use support. The default format is subject to change.
-> % lms log stream
Streaming logs from LM Studio
timestamp: 11/13/2024, 9:35:15 AM
type: llm.prediction.input
modelIdentifier: gemma-2-2b-it
modelPath: lmstudio-community/gemma-2-2b-it-GGUF/gemma-2-2b-it-Q4_K_M.gguf
input: "<start_of_turn>system
You are a tool-calling AI. You can request calls to available tools with this EXACT format:
[TOOL_REQUEST]{"name": "tool_name", "arguments": {"param1": "value1"}}[END_TOOL_REQUEST]
AVAILABLE TOOLS:
{
"type": "toolArray",
"tools": [
{
"type": "function",
"function": {
"name": "get_delivery_date",
"description": "Get the delivery date for a customer's order",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string"
}
},
"required": [
"order_id"
]
}
}
}
]
}
RULES:
- Only use tools from AVAILABLE TOOLS
- Include all required arguments
- Use one [TOOL_REQUEST] block per tool
- Never use [TOOL_RESULT]
- If you decide to call one or more tools, there should be no other text in your message
Examples:
"Check Paris weather"
[TOOL_REQUEST]{"name": "get_weather", "arguments": {"location": "Paris"}}[END_TOOL_REQUEST]
"Send email to John about meeting and open browser"
[TOOL_REQUEST]{"name": "send_email", "arguments": {"to": "John", "subject": "meeting"}}[END_TOOL_REQUEST]
[TOOL_REQUEST]{"name": "open_browser", "arguments": {}}[END_TOOL_REQUEST]
Respond conversationally if no matching tools exist.<end_of_turn>
<start_of_turn>user
Get me delivery date for order 123<end_of_turn>
<start_of_turn>model
"
If the model follows this format exactly to call tools, i.e:
[TOOL_REQUEST]{"name": "get_delivery_date", "arguments": {"order_id": "123"}}[END_TOOL_REQUEST]
Then LM Studio will be able to parse those tool calls into the chat.completions object, just like for natively supported models.
All models that don't have native tool use support will have default tool use support.
Example using curl
This example demonstrates a model requesting a tool call using the curl utility.
To run this example on Mac or Linux, use any terminal. On Windows, use Git Bash.
curl http://localhost:1234/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "lmstudio-community/qwen2.5-7b-instruct",
"messages": [{"role": "user", "content": "What dell products do you have under $50 in electronics?"}],
"tools": [
{
"type": "function",
"function": {
"name": "search_products",
"description": "Search the product catalog by various criteria. Use this whenever a customer asks about product availability, pricing, or specifications.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search terms or product name"
},
"category": {
"type": "string",
"description": "Product category to filter by",
"enum": ["electronics", "clothing", "home", "outdoor"]
},
"max_price": {
"type": "number",
"description": "Maximum price in dollars"
}
},
"required": ["query"],
"additionalProperties": false
}
}
}
]
}'
All parameters recognized by /v1/chat/completions will be honored, and the array of available tools should be provided in the tools field.
If the model decides that the user message would be best fulfilled with a tool call, an array of tool call request objects will be provided in the response field, choices[0].message.tool_calls.
The finish_reason field of the top-level response object will also be populated with "tool_calls".
An example response to the above curl request will look like:
{
"id": "chatcmpl-gb1t1uqzefudice8ntxd9i",
"object": "chat.completion",
"created": 1730913210,
"model": "lmstudio-community/qwen2.5-7b-instruct",
"choices": [
{
"index": 0,
"logprobs": null,
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"tool_calls": [
{
"id": "365174485",
"type": "function",
"function": {
"name": "search_products",
"arguments": "{\"query\":\"dell\",\"category\":\"electronics\",\"max_price\":50}"
}
}
]
}
}
],
"usage": {
"prompt_tokens": 263,
"completion_tokens": 34,
"total_tokens": 297
},
"system_fingerprint": "lmstudio-community/qwen2.5-7b-instruct"
}
In plain english, the above response can be thought of as the model saying:
"Please call the
search_productsfunction, with arguments:
- 'dell' for the
queryparameter,- 'electronics' for the
categoryparameter- '50' for the
max_priceparameterand give me back the results"
The tool_calls field will need to be parsed to call actual functions/APIs. The below examples demonstrate how.
Examples using python
Tool use shines when paired with program languages like python, where you can implement the functions specified in the tools field to programmatically call them when the model requests.
Single-turn example
Below is a simple single-turn (model is only called once) example of enabling a model to call a function called say_hello that prints a hello greeting to the console:
single-turn-example.py
from openai import OpenAI
# Connect to LM Studio
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
# Define a simple function
def say_hello(name: str) -> str:
print(f"Hello, {name}!")
# Tell the AI about our function
tools = [
{
"type": "function",
"function": {
"name": "say_hello",
"description": "Says hello to someone",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The person's name"
}
},
"required": ["name"]
}
}
}
]
# Ask the AI to use our function
response = client.chat.completions.create(
model="lmstudio-community/qwen2.5-7b-instruct",
messages=[{"role": "user", "content": "Can you say hello to Bob the Builder?"}],
tools=tools
)
# Get the name the AI wants to use a tool to say hello to
# (Assumes the AI has requested a tool call and that tool call is say_hello)
tool_call = response.choices[0].message.tool_calls[0]
name = eval(tool_call.function.arguments)["name"]
# Actually call the say_hello function
say_hello(name) # Prints: Hello, Bob the Builder!
Running this script from the console should yield results like:
-> % python single-turn-example.py
Hello, Bob the Builder!
Play around with the name in
messages=[{"role": "user", "content": "Can you say hello to Bob the Builder?"}]
to see the model call the say_hello function with different names.
Multi-turn example
Now for a slightly more complex example.
In this example, we'll:
- Enable the model to call a
get_delivery_datefunction - Hand the result of calling that function back to the model, so that it can fulfill the user's request in plain text
from datetime import datetime, timedelta
import json
import random
from openai import OpenAI
# Point to the local server
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
model = "lmstudio-community/qwen2.5-7b-instruct"
def get_delivery_date(order_id: str) -> datetime:
# Generate a random delivery date between today and 14 days from now
# in a real-world scenario, this function would query a database or API
today = datetime.now()
random_days = random.randint(1, 14)
delivery_date = today + timedelta(days=random_days)
print(
f"\nget_delivery_date function returns delivery date:\n\n{delivery_date}",
flush=True,
)
return delivery_date
tools = [
{
"type": "function",
"function": {
"name": "get_delivery_date",
"description": "Get the delivery date for a customer's order. Call this whenever you need to know the delivery date, for example when a customer asks 'Where is my package'",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The customer's order ID.",
},
},
"required": ["order_id"],
"additionalProperties": False,
},
},
}
]
messages = [
{
"role": "system",
"content": "You are a helpful customer support assistant. Use the supplied tools to assist the user.",
},
{
"role": "user",
"content": "Give me the delivery date and time for order number 1017",
},
]
# LM Studio
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
)
print("\nModel response requesting tool call:\n", flush=True)
print(response, flush=True)
# Extract the arguments for get_delivery_date
# Note this code assumes we have already determined that the model generated a function call.
tool_call = response.choices[0].message.tool_calls[0]
arguments = json.loads(tool_call.function.arguments)
order_id = arguments.get("order_id")
# Call the get_delivery_date function with the extracted order_id
delivery_date = get_delivery_date(order_id)
assistant_tool_call_request_message = {
"role": "assistant",
"tool_calls": [
{
"id": response.choices[0].message.tool_calls[0].id,
"type": response.choices[0].message.tool_calls[0].type,
"function": response.choices[0].message.tool_calls[0].function,
}
],
}
# Create a message containing the result of the function call
function_call_result_message = {
"role": "tool",
"content": json.dumps(
{
"order_id": order_id,
"delivery_date": delivery_date.strftime("%Y-%m-%d %H:%M:%S"),
}
),
"tool_call_id": response.choices[0].message.tool_calls[0].id,
}
# Prepare the chat completion call payload
completion_messages_payload = [
messages[0],
messages[1],
assistant_tool_call_request_message,
function_call_result_message,
]
# Call the OpenAI API's chat completions endpoint to send the tool call result back to the model
# LM Studio
response = client.chat.completions.create(
model=model,
messages=completion_messages_payload,
)
print("\nFinal model response with knowledge of the tool call result:\n", flush=True)
print(response.choices[0].message.content, flush=True)
Running this script from the console should yield results like:
-> % python multi-turn-example.py
Model response requesting tool call:
ChatCompletion(id='chatcmpl-wwpstqqu94go4hvclqnpwn', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content=None, refusal=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='377278620', function=Function(arguments='{"order_id":"1017"}', name='get_delivery_date'), type='function')]))], created=1730916196, model='lmstudio-community/qwen2.5-7b-instruct', object='chat.completion', service_tier=None, system_fingerprint='lmstudio-community/qwen2.5-7b-instruct', usage=CompletionUsage(completion_tokens=24, prompt_tokens=223, total_tokens=247, completion_tokens_details=None, prompt_tokens_details=None))
get_delivery_date function returns delivery date:
2024-11-19 13:03:17.773298
Final model response with knowledge of the tool call result:
Your order number 1017 is scheduled for delivery on November 19, 2024, at 13:03 PM.
Advanced agent example
Building upon the principles above, we can combine LM Studio models with locally defined functions to create an "agent" - a system that pairs a language model with custom functions to understand requests and perform actions beyond basic text generation.
The agent in the below example can:
- Open safe urls in your default browser
- Check the current time
- Analyze directories in your file system
import json
from urllib.parse import urlparse
import webbrowser
from datetime import datetime
import os
from openai import OpenAI
# Point to the local server
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
model = "lmstudio-community/qwen2.5-7b-instruct"
def is_valid_url(url: str) -> bool:
try:
result = urlparse(url)
return bool(result.netloc) # Returns True if there's a valid network location
except Exception:
return False
def open_safe_url(url: str) -> dict:
# List of allowed domains (expand as needed)
SAFE_DOMAINS = {
"lmstudio.ai",
"github.com",
"google.com",
"wikipedia.org",
"weather.com",
"stackoverflow.com",
"python.org",
"docs.python.org",
}
try:
# Add http:// if no scheme is present
if not url.startswith(('http://', 'https://')):
url = 'http://' + url
# Validate URL format
if not is_valid_url(url):
return {"status": "error", "message": f"Invalid URL format: {url}"}
# Parse the URL and check domain
parsed_url = urlparse(url)
domain = parsed_url.netloc.lower()
base_domain = ".".join(domain.split(".")[-2:])
if base_domain in SAFE_DOMAINS:
webbrowser.open(url)
return {"status": "success", "message": f"Opened {url} in browser"}
else:
return {
"status": "error",
"message": f"Domain {domain} not in allowed list",
}
except Exception as e:
return {"status": "error", "message": str(e)}
def get_current_time() -> dict:
"""Get the current system time with timezone information"""
try:
current_time = datetime.now()
timezone = datetime.now().astimezone().tzinfo
formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S %Z")
return {
"status": "success",
"time": formatted_time,
"timezone": str(timezone),
"timestamp": current_time.timestamp(),
}
except Exception as e:
return {"status": "error", "message": str(e)}
def analyze_directory(path: str = ".") -> dict:
"""Count and categorize files in a directory"""
try:
stats = {
"total_files": 0,
"total_dirs": 0,
"file_types": {},
"total_size_bytes": 0,
}
for entry in os.scandir(path):
if entry.is_file():
stats["total_files"] += 1
ext = os.path.splitext(entry.name)[1].lower() or "no_extension"
stats["file_types"][ext] = stats["file_types"].get(ext, 0) + 1
stats["total_size_bytes"] += entry.stat().st_size
elif entry.is_dir():
stats["total_dirs"] += 1
# Add size of directory contents
for root, _, files in os.walk(entry.path):
for file in files:
try:
stats["total_size_bytes"] += os.path.getsize(os.path.join(root, file))
except (OSError, FileNotFoundError):
continue
return {"status": "success", "stats": stats, "path": os.path.abspath(path)}
except Exception as e:
return {"status": "error", "message": str(e)}
tools = [
{
"type": "function",
"function": {
"name": "open_safe_url",
"description": "Open a URL in the browser if it's deemed safe",
"parameters": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "The URL to open",
},
},
"required": ["url"],
},
},
},
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "Get the current system time with timezone information",
"parameters": {
"type": "object",
"properties": {},
"required": [],
},
},
},
{
"type": "function",
"function": {
"name": "analyze_directory",
"description": "Analyze the contents of a directory, counting files and folders",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The directory path to analyze. Defaults to current directory if not specified.",
},
},
"required": [],
},
},
},
]
def process_tool_calls(response, messages):
"""Process multiple tool calls and return the final response and updated messages"""
# Get all tool calls from the response
tool_calls = response.choices[0].message.tool_calls
# Create the assistant message with tool calls
assistant_tool_call_message = {
"role": "assistant",
"tool_calls": [
{
"id": tool_call.id,
"type": tool_call.type,
"function": tool_call.function,
}
for tool_call in tool_calls
],
}
# Add the assistant's tool call message to the history
messages.append(assistant_tool_call_message)
# Process each tool call and collect results
tool_results = []
for tool_call in tool_calls:
# For functions with no arguments, use empty dict
arguments = (
json.loads(tool_call.function.arguments)
if tool_call.function.arguments.strip()
else {}
)
# Determine which function to call based on the tool call name
if tool_call.function.name == "open_safe_url":
result = open_safe_url(arguments["url"])
elif tool_call.function.name == "get_current_time":
result = get_current_time()
elif tool_call.function.name == "analyze_directory":
path = arguments.get("path", ".")
result = analyze_directory(path)
else:
# llm tried to call a function that doesn't exist, skip
continue
# Add the result message
tool_result_message = {
"role": "tool",
"content": json.dumps(result),
"tool_call_id": tool_call.id,
}
tool_results.append(tool_result_message)
messages.append(tool_result_message)
# Get the final response
final_response = client.chat.completions.create(
model=model,
messages=messages,
)
return final_response
def chat():
messages = [
{
"role": "system",
"content": "You are a helpful assistant that can open safe web links, tell the current time, and analyze directory contents. Use these capabilities whenever they might be helpful.",
}
]
print(
"Assistant: Hello! I can help you open safe web links, tell you the current time, and analyze directory contents. What would you like me to do?"
)
print("(Type 'quit' to exit)")
while True:
# Get user input
user_input = input("\nYou: ").strip()
# Check for quit command
if user_input.lower() == "quit":
print("Assistant: Goodbye!")
break
# Add user message to conversation
messages.append({"role": "user", "content": user_input})
try:
# Get initial response
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
)
# Check if the response includes tool calls
if response.choices[0].message.tool_calls:
# Process all tool calls and get final response
final_response = process_tool_calls(response, messages)
print("\nAssistant:", final_response.choices[0].message.content)
# Add assistant's final response to messages
messages.append(
{
"role": "assistant",
"content": final_response.choices[0].message.content,
}
)
else:
# If no tool call, just print the response
print("\nAssistant:", response.choices[0].message.content)
# Add assistant's response to messages
messages.append(
{
"role": "assistant",
"content": response.choices[0].message.content,
}
)
except Exception as e:
print(f"\nAn error occurred: {str(e)}")
exit(1)
if __name__ == "__main__":
chat()
Running this script from the console will allow you to chat with the agent:
-> % python agent-example.py
Assistant: Hello! I can help you open safe web links, tell you the current time, and analyze directory contents. What would you like me to do?
(Type 'quit' to exit)
You: What time is it?
Assistant: The current time is 14:11:40 (EST) as of November 6, 2024.
You: What time is it now?
Assistant: The current time is 14:13:59 (EST) as of November 6, 2024.
You: Open lmstudio.ai
Assistant: The link to lmstudio.ai has been opened in your default web browser.
You: What's in my current directory?
Assistant: Your current directory at `/Users/matt/project` contains a total of 14 files and 8 directories. Here's the breakdown:
- Files without an extension: 3
- `.mjs` files: 2
- `.ts` (TypeScript) files: 3
- Markdown (`md`) file: 1
- JSON files: 4
- TOML file: 1
The total size of these items is 1,566,990,604 bytes.
You: Thank you!
Assistant: You're welcome! If you have any other questions or need further assistance, feel free to ask.
You:
Streaming
When streaming through /v1/chat/completions (stream=true), tool calls are sent in chunks. Function names and arguments are sent in pieces via chunk.choices[0].delta.tool_calls.function.name and chunk.choices[0].delta.tool_calls.function.arguments.
For example, to call get_current_weather(location="San Francisco"), the streamed ChoiceDeltaToolCall in each chunk.choices[0].delta.tool_calls[0] object will look like:
ChoiceDeltaToolCall(index=0, id='814890118', function=ChoiceDeltaToolCallFunction(arguments='', name='get_current_weather'), type='function')
ChoiceDeltaToolCall(index=0, id=None, function=ChoiceDeltaToolCallFunction(arguments='{"', name=None), type=None)
ChoiceDeltaToolCall(index=0, id=None, function=ChoiceDeltaToolCallFunction(arguments='location', name=None), type=None)
ChoiceDeltaToolCall(index=0, id=None, function=ChoiceDeltaToolCallFunction(arguments='":"', name=None), type=None)
ChoiceDeltaToolCall(index=0, id=None, function=ChoiceDeltaToolCallFunction(arguments='San Francisco', name=None), type=None)
ChoiceDeltaToolCall(index=0, id=None, function=ChoiceDeltaToolCallFunction(arguments='"}', name=None), type=None)
These chunks must be accumulated throughout the stream to form the complete function signature for execution.
The below example shows how to create a simple tool-enhanced chatbot through the /v1/chat/completions streaming endpoint (stream=true).
from openai import OpenAI
import time
client = OpenAI(base_url="http://127.0.0.1:1234/v1", api_key="lm-studio")
MODEL = "lmstudio-community/qwen2.5-7b-instruct"
TIME_TOOL = {
"type": "function",
"function": {
"name": "get_current_time",
"description": "Get the current time, only if asked",
"parameters": {"type": "object", "properties": {}},
},
}
def get_current_time():
return {"time": time.strftime("%H:%M:%S")}
def process_stream(stream, add_assistant_label=True):
"""Handle streaming responses from the API"""
collected_text = ""
tool_calls = []
first_chunk = True
for chunk in stream:
delta = chunk.choices[0].delta
# Handle regular text output
if delta.content:
if first_chunk:
print()
if add_assistant_label:
print("Assistant:", end=" ", flush=True)
first_chunk = False
print(delta.content, end="", flush=True)
collected_text += delta.content
# Handle tool calls
elif delta.tool_calls:
for tc in delta.tool_calls:
if len(tool_calls) <= tc.index:
tool_calls.append({
"id": "", "type": "function",
"function": {"name": "", "arguments": ""}
})
tool_calls[tc.index] = {
"id": (tool_calls[tc.index]["id"] + (tc.id or "")),
"type": "function",
"function": {
"name": (tool_calls[tc.index]["function"]["name"] + (tc.function.name or "")),
"arguments": (tool_calls[tc.index]["function"]["arguments"] + (tc.function.arguments or ""))
}
}
return collected_text, tool_calls
def chat_loop():
messages = []
print("Assistant: Hi! I am an AI agent empowered with the ability to tell the current time (Type 'quit' to exit)")
while True:
user_input = input("\nYou: ").strip()
if user_input.lower() == "quit":
break
messages.append({"role": "user", "content": user_input})
# Get initial response
response_text, tool_calls = process_stream(
client.chat.completions.create(
model=MODEL,
messages=messages,
tools=[TIME_TOOL],
stream=True,
temperature=0.2
)
)
if not tool_calls:
print()
text_in_first_response = len(response_text) > 0
if text_in_first_response:
messages.append({"role": "assistant", "content": response_text})
# Handle tool calls if any
if tool_calls:
tool_name = tool_calls[0]["function"]["name"]
print()
if not text_in_first_response:
print("Assistant:", end=" ", flush=True)
print(f"**Calling Tool: {tool_name}**")
messages.append({"role": "assistant", "tool_calls": tool_calls})
# Execute tool calls
for tool_call in tool_calls:
if tool_call["function"]["name"] == "get_current_time":
result = get_current_time()
messages.append({
"role": "tool",
"content": str(result),
"tool_call_id": tool_call["id"]
})
# Get final response after tool execution
final_response, _ = process_stream(
client.chat.completions.create(
model=MODEL,
messages=messages,
stream=True
),
add_assistant_label=False
)
if final_response:
print()
messages.append({"role": "assistant", "content": final_response})
if __name__ == "__main__":
chat_loop()
You can chat with the bot by running this script from the console:
-> % python tool-streaming-chatbot.py
Assistant: Hi! I am an AI agent empowered with the ability to tell the current time (Type 'quit' to exit)
You: Tell me a joke, then tell me the current time
Assistant: Sure! Here's a light joke for you: Why don't scientists trust atoms? Because they make up everything.
Now, let me get the current time for you.
**Calling Tool: get_current_time**
The current time is 18:49:31. Enjoy your day!
You:
Community
Chat with other LM Studio users, discuss LLMs, hardware, and more on the LM Studio Discord server.
endpoints
OpenAI Compatibility API
Send requests to Chat Completions (text and images), Completions, and Embeddings endpoints
Send requests to Chat Completions (text and images), Completions, and Embeddings endpoints.
OpenAI-like API endpoints
LM Studio accepts requests on several OpenAI endpoints and returns OpenAI-like response objects.
Supported endpoints
GET /v1/models
POST /v1/chat/completions
POST /v1/embeddings
POST /v1/completions
See below for more info about each endpoint
Re-using an existing OpenAI client
You can reuse existing OpenAI clients (in Python, JS, C#, etc) by switching up the "base URL" property to point to your LM Studio instead of OpenAI's servers.
Switching up the base url to point to LM Studio
Note: The following examples assume the server port is 1234
Python
from openai import OpenAI
client = OpenAI(
+ base_url="http://localhost:1234/v1"
)
# ... the rest of your code ...
Typescript
import OpenAI from 'openai';
const client = new OpenAI({
+ baseUrl: "http://localhost:1234/v1"
});
// ... the rest of your code ...
cURL
- curl https://api.openai.com/v1/chat/completions \
+ curl http://localhost:1234/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
- "model": "gpt-4o-mini",
+ "model": "use the model identifier from LM Studio here",
"messages": [{"role": "user", "content": "Say this is a test!"}],
"temperature": 0.7
}'
Endpoints overview
/v1/models
GETrequest- Lists the currently loaded models.
cURL example
curl http://localhost:1234/v1/models
/v1/chat/completions
POSTrequest- Send a chat history and receive the assistant's response
- Prompt template is applied automatically
- You can provide inference parameters such as temperature in the payload. See supported parameters
- See OpenAI's documentation for more information
- As always, keep a terminal window open with
lms log streamto see what input the model receives
Python example
# Example: reuse your existing OpenAI setup
from openai import OpenAI
# Point to the local server
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
completion = client.chat.completions.create(
model="model-identifier",
messages=[
{"role": "system", "content": "Always answer in rhymes."},
{"role": "user", "content": "Introduce yourself."}
],
temperature=0.7,
)
print(completion.choices[0].message)
/v1/embeddings
POSTrequest- Send a string or array of strings and get an array of text embeddings (integer token IDs)
- See OpenAI's documentation for more information
Python example
# Make sure to `pip install openai` first
from openai import OpenAI
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
def get_embedding(text, model="model-identifier"):
text = text.replace("\n", " ")
return client.embeddings.create(input = [text], model=model).data[0].embedding
print(get_embedding("Once upon a time, there was a cat."))
/v1/completions
This OpenAI-like endpoint is no longer supported by OpenAI. LM Studio continues to support it.
Using this endpoint with chat-tuned models might result in unexpected behavior such as extraneous role tokens being emitted by the model.
For best results, utilize a base model.
POSTrequest- Send a string and get the model's continuation of that string
- See supported payload parameters
- Prompt template will NOT be applied, even if the model has one
- See OpenAI's documentation for more information
- As always, keep a terminal window open with
lms log streamto see what input the model receives
Supported payload parameters
For an explanation for each parameter, see https://platform.openai.com/docs/api-reference/chat/create.
model
top_p
top_k
messages
temperature
max_tokens
stream
stop
presence_penalty
frequency_penalty
logit_bias
repeat_penalty
seed
Community
Chat with other LM Studio developers, discuss LLMs, hardware, and more on the LM Studio Discord server.
LM Studio REST API (beta)
The REST API includes enhanced stats such as Token / Second and Time To First Token (TTFT), as well as rich information about models such as loaded vs unloaded, max context, quantization, and more.
Experimental
Requires LM Studio 0.3.6 or newer. Still WIP, endpoints may change.
LM Studio now has its own REST API, in addition to OpenAI compatibility mode (learn more).
The REST API includes enhanced stats such as Token / Second and Time To First Token (TTFT), as well as rich information about models such as loaded vs unloaded, max context, quantization, and more.
Supported API Endpoints
GET /api/v0/models- List available modelsGET /api/v0/models/{model}- Get info about a specific modelPOST /api/v0/chat/completions- Chat Completions (messages -> assistant response)POST /api/v0/completions- Text Completions (prompt -> completion)POST /api/v0/embeddings- Text Embeddings (text -> embedding)
🚧 We are in the process of developing this interface. Let us know what's important to you on Github or by email.
Start the REST API server
To start the server, run the following command:
lms server start
You can run LM Studio as a service and get the server to auto-start on boot without launching the GUI. [Learn about Headless Mode](/docs/advanced/headless).
Endpoints
GET /api/v0/models
List all loaded and downloaded models
Example request
curl http://localhost:1234/api/v0/models
Response format
{
"object": "list",
"data": [
{
"id": "qwen2-vl-7b-instruct",
"object": "model",
"type": "vlm",
"publisher": "mlx-community",
"arch": "qwen2_vl",
"compatibility_type": "mlx",
"quantization": "4bit",
"state": "not-loaded",
"max_context_length": 32768
},
{
"id": "meta-llama-3.1-8b-instruct",
"object": "model",
"type": "llm",
"publisher": "lmstudio-community",
"arch": "llama",
"compatibility_type": "gguf",
"quantization": "Q4_K_M",
"state": "not-loaded",
"max_context_length": 131072
},
{
"id": "text-embedding-nomic-embed-text-v1.5",
"object": "model",
"type": "embeddings",
"publisher": "nomic-ai",
"arch": "nomic-bert",
"compatibility_type": "gguf",
"quantization": "Q4_0",
"state": "not-loaded",
"max_context_length": 2048
}
]
}
GET /api/v0/models/{model}
Get info about one specific model
Example request
curl http://localhost:1234/api/v0/models/qwen2-vl-7b-instruct
Response format
{
"id": "qwen2-vl-7b-instruct",
"object": "model",
"type": "vlm",
"publisher": "mlx-community",
"arch": "qwen2_vl",
"compatibility_type": "mlx",
"quantization": "4bit",
"state": "not-loaded",
"max_context_length": 32768
}
POST /api/v0/chat/completions
Chat Completions API. You provide a messages array and receive the next assistant response in the chat.
Example request
curl http://localhost:1234/api/v0/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "granite-3.0-2b-instruct",
"messages": [
{ "role": "system", "content": "Always answer in rhymes." },
{ "role": "user", "content": "Introduce yourself." }
],
"temperature": 0.7,
"max_tokens": -1,
"stream": false
}'
Response format
{
"id": "chatcmpl-i3gkjwthhw96whukek9tz",
"object": "chat.completion",
"created": 1731990317,
"model": "granite-3.0-2b-instruct",
"choices": [
{
"index": 0,
"logprobs": null,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "Greetings, I'm a helpful AI, here to assist,\nIn providing answers, with no distress.\nI'll keep it short and sweet, in rhyme you'll find,\nA friendly companion, all day long you'll bind."
}
}
],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 53,
"total_tokens": 77
},
"stats": {
"tokens_per_second": 51.43709529007664,
"time_to_first_token": 0.111,
"generation_time": 0.954,
"stop_reason": "eosFound"
},
"model_info": {
"arch": "granite",
"quant": "Q4_K_M",
"format": "gguf",
"context_length": 4096
},
"runtime": {
"name": "llama.cpp-mac-arm64-apple-metal-advsimd",
"version": "1.3.0",
"supported_formats": ["gguf"]
}
}
POST /api/v0/completions
Text Completions API. You provide a prompt and receive a completion.
Example request
curl http://localhost:1234/api/v0/completions \
-H "Content-Type: application/json" \
-d '{
"model": "granite-3.0-2b-instruct",
"prompt": "the meaning of life is",
"temperature": 0.7,
"max_tokens": 10,
"stream": false,
"stop": "\n"
}'
Response format
{
"id": "cmpl-p9rtxv6fky2v9k8jrd8cc",
"object": "text_completion",
"created": 1731990488,
"model": "granite-3.0-2b-instruct",
"choices": [
{
"index": 0,
"text": " to find your purpose, and once you have",
"logprobs": null,
"finish_reason": "length"
}
],
"usage": {
"prompt_tokens": 5,
"completion_tokens": 9,
"total_tokens": 14
},
"stats": {
"tokens_per_second": 57.69230769230769,
"time_to_first_token": 0.299,
"generation_time": 0.156,
"stop_reason": "maxPredictedTokensReached"
},
"model_info": {
"arch": "granite",
"quant": "Q4_K_M",
"format": "gguf",
"context_length": 4096
},
"runtime": {
"name": "llama.cpp-mac-arm64-apple-metal-advsimd",
"version": "1.3.0",
"supported_formats": ["gguf"]
}
}
POST /api/v0/embeddings
Text Embeddings API. You provide a text and a representation of the text as an embedding vector is returned.
Example request
curl http://127.0.0.1:1234/api/v0/embeddings \
-H "Content-Type: application/json" \
-d '{
"model": "text-embedding-nomic-embed-text-v1.5",
"input": "Some text to embed"
}
Example response
{
"object": "list",
"data": [
{
"object": "embedding",
"embedding": [
-0.016731496900320053,
0.028460891917347908,
-0.1407836228609085,
... (truncated for brevity) ...,
0.02505224384367466,
-0.0037634256295859814,
-0.04341062530875206
],
"index": 0
}
],
"model": "text-embedding-nomic-embed-text-v1.5@q4_k_m",
"usage": {
"prompt_tokens": 0,
"total_tokens": 0
}
}
Please report bugs by opening an issue on Github.
python
lmstudio-python (Python SDK)
Getting started with LM Studio's Python SDK
lmstudio-python provides you a set APIs to interact with LLMs, embeddings models, and agentic flows.
Installing the SDK
lmstudio-python is available as a PyPI package. You can install it using pip.
variants:
pip:
language: bash
code: |
pip install lmstudio
For the source code and open source contribution, visit lmstudio-python on GitHub.
Features
- Use LLMs to respond in chats or predict text completions
- Define functions as tools, and turn LLMs into autonomous agents that run completely locally
- Load, configure, and unload models from memory
- Generate embeddings for text, and more!
Quick Example: Chat with a Llama Model
variants:
"Python (convenience API)":
language: python
code: |
import lmstudio as lms
model = lms.llm("llama-3.2-1b-instruct")
result = model.respond("What is the meaning of life?")
print(result)
"Python (scoped resource API)":
language: python
code: |
import lmstudio as lms
with lms.Client() as client:
model = client.llm.model("llama-3.2-1b-instruct")
result = model.respond("What is the meaning of life?")
print(result)
Getting Local Models
The above code requires the Llama 3.2 1B model. If you don't have the model, run the following command in the terminal to download it.
lms get llama-3.2-1b-instruct
Read more about lms get in LM Studio's CLI here.
Interactive Convenience or Deterministic Resource Management?
As shown in the example above, th
… [truncated — open the raw llms.txt above for the full file]
Perplexity AI is an AI-powered search engine that provides direct answers to user queries by leveraging large language models.
Shop Dell's laptops, Monitors, Computers, Storage Solutions & Servers for your home and business. Buy online!
Create the most realistic speech with our AI audio in 1000s of voices and 32 languages. Pioneering research in Text to Speech and AI Voice Generation
Get started with the Model Context Protocol (MCP).
The Voice AI Platform: TTS Models, Voice Agents, & More.