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" },