From 95bff825c11909bde7fb9d1257606f44886df869 Mon Sep 17 00:00:00 2001
From: Alex Zhang
Date: Tue, 24 Mar 2026 14:37:51 -0400
Subject: [PATCH] bye bye litellm
---
README.md | 2 +-
docs/api/rlm.md | 4 +-
docs/src/app/api/page.tsx | 2 +-
docs/src/app/backends/page.tsx | 16 +----
rlm/clients/__init__.py | 6 +-
rlm/clients/litellm.py | 105 ---------------------------------
rlm/core/types.py | 1 -
tests/clients/test_timeout.py | 26 --------
tests/test_imports.py | 16 -----
uv.lock | 2 +-
10 files changed, 7 insertions(+), 173 deletions(-)
delete mode 100644 rlm/clients/litellm.py
diff --git a/README.md b/README.md
index 3f8541abf..96820a7ad 100644
--- a/README.md
+++ b/README.md
@@ -115,7 +115,7 @@ export PRIME_API_KEY=...
### Model Providers
-We currently support most major clients (OpenAI, Anthropic), as well as the router platforms (OpenRouter, Portkey, LiteLLM). For local models, we recommend using vLLM (which interfaces with the [OpenAI client](https://github.com/alexzhang13/rlm/blob/main/rlm/clients/openai.py)). To view or add support for more clients, start by looking at [`rlm/clients/`](https://github.com/alexzhang13/rlm/tree/main/rlm/clients).
+We currently support most major clients (OpenAI, Anthropic), as well as the router platforms (OpenRouter, Portkey). For local models, we recommend using vLLM (which interfaces with the [OpenAI client](https://github.com/alexzhang13/rlm/blob/main/rlm/clients/openai.py)). To view or add support for more clients, start by looking at [`rlm/clients/`](https://github.com/alexzhang13/rlm/tree/main/rlm/clients).
## Relevant Reading
* **[Dec '25]** [Recursive Language Models arXiv](https://arxiv.org/abs/2512.24601)
diff --git a/docs/api/rlm.md b/docs/api/rlm.md
index 383f2cb2c..2a63968f4 100644
--- a/docs/api/rlm.md
+++ b/docs/api/rlm.md
@@ -71,7 +71,7 @@ RLM(
#### `backend`
{: .no_toc }
-**Type:** `Literal["openai", "portkey", "openrouter", "vllm", "litellm", "anthropic"]`
+**Type:** `Literal["openai", "portkey", "openrouter", "vllm", "anthropic"]`
**Default:** `"openai"`
The LM provider backend to use for the root model.
@@ -104,7 +104,7 @@ Configuration passed to the LM client. Required fields vary by backend:
| `portkey` | `model_name`, `api_key` | `base_url` |
| `openrouter` | `model_name` | `api_key` |
| `vllm` | `model_name`, `base_url` | — |
-| `litellm` | `model_name` | varies by provider |
+
```python
backend_kwargs = {
diff --git a/docs/src/app/api/page.tsx b/docs/src/app/api/page.tsx
index 58f6346e9..62fed1b39 100644
--- a/docs/src/app/api/page.tsx
+++ b/docs/src/app/api/page.tsx
@@ -61,7 +61,7 @@ print(result.response)`} />
["anthropic", "Anthropic API"],
["portkey", "Portkey AI gateway"],
["openrouter", "OpenRouter"],
- ["litellm", "LiteLLM (multi-provider)"],
+
["vllm", "Local vLLM server"],
]}
/>
diff --git a/docs/src/app/backends/page.tsx b/docs/src/app/backends/page.tsx
index 6a0708aa4..92ceac7ac 100644
--- a/docs/src/app/backends/page.tsx
+++ b/docs/src/app/backends/page.tsx
@@ -7,7 +7,7 @@ export default function BackendsPage() {
- RLMs natively support a wide range of language model providers, including OpenAI, Anthropic, Portkey, OpenRouter, and LiteLLM. Additional providers can be supported with minimal effort. The backend_kwargs are named arguments passed directly to the backend client.
+ RLMs natively support a wide range of language model providers, including OpenAI, Anthropic, Portkey, and OpenRouter. Additional providers can be supported with minimal effort. The backend_kwargs are named arguments passed directly to the backend client.
@@ -64,20 +64,6 @@ export default function BackendsPage() {
- LiteLLM
-
- LiteLLM is a universal interface for 100+ model providers, with support for local models and custom endpoints.
-
-
-
-
-
vLLM (Local)
Local model serving.
str:
- if isinstance(prompt, str):
- messages = [{"role": "user", "content": prompt}]
- elif isinstance(prompt, list) and all(isinstance(item, dict) for item in prompt):
- messages = prompt
- else:
- raise ValueError(f"Invalid prompt type: {type(prompt)}")
-
- model = model or self.model_name
- if not model:
- raise ValueError("Model name is required for LiteLLM client.")
-
- kwargs = {"model": model, "messages": messages, "timeout": self.timeout}
- if self.api_key:
- kwargs["api_key"] = self.api_key
- if self.api_base:
- kwargs["api_base"] = self.api_base
-
- response = litellm.completion(**kwargs)
- self._track_cost(response, model)
- return response.choices[0].message.content
-
- async def acompletion(
- self, prompt: str | list[dict[str, Any]], model: str | None = None
- ) -> str:
- if isinstance(prompt, str):
- messages = [{"role": "user", "content": prompt}]
- elif isinstance(prompt, list) and all(isinstance(item, dict) for item in prompt):
- messages = prompt
- else:
- raise ValueError(f"Invalid prompt type: {type(prompt)}")
-
- model = model or self.model_name
- if not model:
- raise ValueError("Model name is required for LiteLLM client.")
-
- kwargs = {"model": model, "messages": messages, "timeout": self.timeout}
- if self.api_key:
- kwargs["api_key"] = self.api_key
- if self.api_base:
- kwargs["api_base"] = self.api_base
-
- response = await litellm.acompletion(**kwargs)
- self._track_cost(response, model)
- return response.choices[0].message.content
-
- def _track_cost(self, response, model: str):
- self.model_call_counts[model] += 1
- self.model_input_tokens[model] += response.usage.prompt_tokens
- self.model_output_tokens[model] += response.usage.completion_tokens
- self.model_total_tokens[model] += response.usage.total_tokens
-
- # Track last call for handler to read
- self.last_prompt_tokens = response.usage.prompt_tokens
- self.last_completion_tokens = response.usage.completion_tokens
-
- def get_usage_summary(self) -> UsageSummary:
- model_summaries = {}
- for model in self.model_call_counts:
- model_summaries[model] = ModelUsageSummary(
- total_calls=self.model_call_counts[model],
- total_input_tokens=self.model_input_tokens[model],
- total_output_tokens=self.model_output_tokens[model],
- )
- return UsageSummary(model_usage_summaries=model_summaries)
-
- def get_last_usage(self) -> ModelUsageSummary:
- return ModelUsageSummary(
- total_calls=1,
- total_input_tokens=self.last_prompt_tokens,
- total_output_tokens=self.last_completion_tokens,
- )
diff --git a/rlm/core/types.py b/rlm/core/types.py
index bd9e0d0a7..8d238ea80 100644
--- a/rlm/core/types.py
+++ b/rlm/core/types.py
@@ -8,7 +8,6 @@
"openrouter",
"vercel",
"vllm",
- "litellm",
"anthropic",
"azure_openai",
"gemini",
diff --git a/tests/clients/test_timeout.py b/tests/clients/test_timeout.py
index c0ee0903d..425efcce4 100644
--- a/tests/clients/test_timeout.py
+++ b/tests/clients/test_timeout.py
@@ -140,32 +140,6 @@ def test_timeout_passed_to_client(self):
assert async_call_kwargs["timeout"] == 120.0
-class TestLiteLLMClientTimeout:
- """Tests for LiteLLM client timeout."""
-
- def test_timeout_passed_to_completion(self):
- """Timeout should be passed to litellm.completion call."""
- pytest.importorskip("litellm")
- from rlm.clients.litellm import LiteLLMClient
-
- mock_response = MagicMock()
- mock_response.choices = [MagicMock()]
- mock_response.choices[0].message.content = "Hello"
- mock_response.usage.prompt_tokens = 10
- mock_response.usage.completion_tokens = 5
- mock_response.usage.total_tokens = 15
-
- with patch(
- "rlm.clients.litellm.litellm.completion", return_value=mock_response
- ) as mock_completion:
- client = LiteLLMClient(model_name="gpt-4o", timeout=120.0)
- client.completion("Hello")
-
- mock_completion.assert_called_once()
- call_kwargs = mock_completion.call_args[1]
- assert call_kwargs["timeout"] == 120.0
-
-
class TestGeminiClientTimeout:
"""Tests for Gemini client timeout."""
diff --git a/tests/test_imports.py b/tests/test_imports.py
index 9b65530f9..ec40591fe 100644
--- a/tests/test_imports.py
+++ b/tests/test_imports.py
@@ -67,13 +67,6 @@ def test_portkey_client_import(self):
assert PortkeyClient is not None
- def test_litellm_client_import(self):
- """Test LiteLLMClient import."""
- pytest.importorskip("litellm")
- from rlm.clients.litellm import LiteLLMClient
-
- assert LiteLLMClient is not None
-
def test_get_client_function(self):
"""Test get_client function import."""
from rlm.clients import get_client
@@ -322,7 +315,6 @@ def test_no_circular_imports(self):
("rlm.clients.openai", "openai"),
("rlm.clients.anthropic", "anthropic"),
("rlm.clients.portkey", "portkey_ai"),
- ("rlm.clients.litellm", "litellm"),
("rlm.environments.modal_repl", "modal"),
("rlm.environments.prime_repl", "prime_sandboxes"),
]
@@ -463,14 +455,6 @@ def test_all_client_classes_importable(self):
except Exception:
pass
- try:
- pytest.importorskip("litellm")
- from rlm.clients.litellm import LiteLLMClient
-
- assert isinstance(LiteLLMClient, type)
- except Exception:
- pass
-
def test_all_environment_classes_importable(self):
"""Test that all environment classes can be imported."""
from rlm.environments.base_env import BaseEnv, IsolatedEnv, NonIsolatedEnv
diff --git a/uv.lock b/uv.lock
index 6abd703d0..5e1be9de9 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1760,7 +1760,7 @@ wheels = [
[[package]]
name = "rlms"
-version = "0.1.0"
+version = "0.1.1"
source = { editable = "." }
dependencies = [
{ name = "anthropic" },