Open Interpreter lets a language model write and run code on your machine, either from the interpreter command or from Python. Under the hood it calls models through LiteLLM, so any server that speaks the OpenAI Chat Completions format can drive it: a vLLM box, a company proxy or a hosted gateway. The setup is two settings, but a few defaults behave differently once the model isn't one LiteLLM recognises.
I tested Open Interpreter 0.4.3 (with litellm 1.83.0 and openai 3.35.1 on Python 3.11) against a small local mock server. The mock logged every path, header and body, and could stream plain text, stream tool calls, loop forever or return errors on demand. I also read the source for anything I couldn't see on the wire. The examples point at APIClaw, an OpenAI-compatible gateway I build, so weigh my opinions accordingly. Its base URL is https://apiclaw.biz/v1. I write YOUR_MODEL_ID below; take real IDs from your provider's model list.
Installing it in 2026
uv venv -p 3.11 .venv && source .venv/bin/activate uv pip install open-interpreter "setuptools<81"
Two things bit me. On Python 3.13, pip install open-interpreter failed while building the old tiktoken wheel it pins, so I used 3.11. And in a fresh uv environment, import interpreter failed with ModuleNotFoundError: No module named 'pkg_resources', because uv doesn't add setuptools and newer setuptools releases are dropping that module. Pinning setuptools<81 fixed it.
The minimal Python setup
from interpreter import interpreter interpreter.llm.model = "openai/YOUR_MODEL_ID" # the prefix matters interpreter.llm.api_base = "https://apiclaw.biz/v1" interpreter.llm.api_key = "YOUR_API_KEY" interpreter.llm.supports_functions = True # see below interpreter.llm.context_window = 32000 interpreter.llm.max_tokens = 4000 interpreter.disable_telemetry = True interpreter.chat("How many Python files are in this folder?")
My mock saw POST /v1/chat/completions with Authorization: Bearer YOUR_API_KEY, "stream": true and "model": "YOUR_MODEL_ID". LiteLLM strips the openai/ prefix before sending.
The prefix is required in Python. With a bare model = "YOUR_MODEL_ID", the call failed with litellm.BadRequestError: LLM Provider NOT provided before any request went out. The CLI is friendlier: when you pass --api_base, it adds openai/ for you unless the name already starts with openai/, azure/, ollama, jan or local.
The base URL must include /v1. LiteLLM appends /chat/completions to whatever you give it. With api_base = "http://127.0.0.1:8799" the request went to /chat/completions, which a real server answers with a 404.
The CLI equivalent is:
interpreter --model YOUR_MODEL_ID \ --api_base https://apiclaw.biz/v1 --api_key YOUR_API_KEY \ --llm_supports_functions --context_window 32000 --max_tokens 4000 \ --disable_telemetry
The same keys can live in ~/.config/open-interpreter/profiles/default.yaml under llm: (model, api_base, api_key, context_window, max_tokens, supports_functions) so you don't retype them.
Turn on function calling yourself
Open Interpreter asks LiteLLM whether the model supports function calling. LiteLLM has never heard of a custom model ID, so the answer is no, and Open Interpreter falls back to text mode. It appends a line to the system prompt asking the model to write markdown code blocks, then parses the stream character by character to find them.
That parser depends on how the stream is chunked. With 1- and 3-character chunks it worked. With 2-character chunks, the code it ran started with a stray n, left over from the python tag being split across chunks, so the first run failed with an error. When the mock sent the whole reply as one chunk (some proxies buffer like that), chat() returned an empty list and ran nothing at all, because a chunk that ends in a backtick is skipped while it waits for more.
With supports_functions = True (or --llm_supports_functions), every request carries a single tool called execute, with a language enum (python, shell, javascript, r, java and a few others) and a code string. The model's tool call is executed and the output goes back as a tool message. I had no chunking problems in this mode. If your model handles tools, which most current ones behind a gateway do, set this explicitly. Open Interpreter rewrites tool call IDs to its own toolu_1, toolu_2 and so on, consistently within each request, so the server's IDs don't come back.
A temperature of 0 is never sent
The default interpreter.llm.temperature is 0, but the request only includes temperature if self.temperature:, and 0 is falsy. My mock never saw a temperature field at default settings, so the server's default applies, which is often 1.0. Setting temperature = 0.2 sent 0.2. If you want near-deterministic output, use a small non-zero value such as 0.1. max_tokens is also omitted unless you set it.
History is trimmed to 8,000 tokens unless you say otherwise
Because LiteLLM doesn't know the context window of a custom model, Open Interpreter prints "We were unable to determine the context window of this model. Defaulting to 8000" and trims the conversation to 8,000 tokens, counted with tiktoken. I loaded a 20-turn history of about 3,500 tokens per turn. At default settings only the last three turns plus the new question went out, about 7,450 tokens; the older turns were silently dropped.
With context_window = 32000 and max_tokens = 4000, the trim budget becomes 32000 − 4000 − 25, and the request carried about 25,700 tokens of history plus "max_tokens": 4000. Set both to what your model really supports. tiktoken counts won't match every provider's tokenizer, so leave some headroom.
The system prompt goes out on every request too. From Python it was about 1,240 characters in function-calling mode. The CLI's default profile turns on import_computer_api, and its system prompt was 7,704 characters per request.
Retries: four attempts in a tenth of a second
Open Interpreter disables LiteLLM's retries (num_retries = 0) and runs its own loop of four attempts. For a 429, a 503, a 400 and a 401 alike, my mock saw four requests within about 0.1 seconds: no backoff, no respect for Retry-After, and the third and fourth attempts quietly added "temperature": 0.1. Then the first error is raised.
On a rate-limited plan that means one 429 becomes four requests instantly, which can keep you over the limit. The loop is hard-coded in fixed_litellm_completions in interpreter/core/llm/llm.py, so if this matters, catch the exception in your own code and wait before calling chat() again.
Nothing stops a model that keeps running code
There is no max-turns setting in 0.4.3. After each code run the output goes back to the model, and Open Interpreter calls it again until a reply contains no code. My mock always answered with another tool call; with auto_run = True it made 34 requests in 25 seconds before I killed it.
For unattended runs, stream the chat and stop after a fixed number of code blocks:
def chat_with_limit(prompt, max_code_runs=5): runs = 0 for chunk in interpreter.chat(prompt, display=False, stream=True): if chunk.get("type") == "code" and chunk.get("end"): runs += 1 if runs > max_code_runs: print(f"stopped after {max_code_runs} code runs") break return interpreter.messages
Against the looping mock this stopped after five runs and six requests, and the sixth block never ran. A normal two-step answer still finished in two requests. With auto_run = True there are no confirmation chunks to count, so count finished code blocks instead.
Since the model is running real code, I'd also run unattended sessions in a container or a throwaway VM rather than on a laptop with your SSH keys on it.
Telemetry and saved conversations
Open Interpreter sends anonymous usage events to PostHog by default. interpreter.disable_telemetry = True, --disable_telemetry or DISABLE_TELEMETRY=true turns that off (offline = True does too). The CLI also saves conversations as JSON under ~/.config/open-interpreter/conversations, prompts and code output included, which is worth knowing if those contain secrets.
A checklist
- Use
openai/YOUR_MODEL_IDin Python and a base URL that ends in/v1. - Set
supports_functions = Trueif your model supports tools, so you avoid the markdown parser. - Set
context_windowandmax_tokens, or history is cut to 8,000 tokens. - Use a small non-zero temperature if you care about it, since 0 is never sent.
- Expect four rapid attempts on any error, and back off in your own code.
- Cap code runs yourself for unattended sessions, and run them somewhere disposable.
Disclosure: I build APIClaw, the OpenAI-compatible gateway used in these examples (flat monthly plans; crypto on the site, other payment methods through support). Everything above applies to any compatible endpoint.