From bdd8e43bd09e097e5e863bbbb61da6ba4b4b98cf Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 18:34:32 +0800 Subject: [PATCH 01/10] feat: replace litellm with nanollm (v0.8.7a1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete migration from unclecode-litellm to nanollm. Zero litellm references remain in the crawl4ai source code. Version: 0.8.7a1 (pre-release for nanollm integration testing) Changes: - pyproject.toml, requirements.txt: swap unclecode-litellm for nanollm - crawl4ai/utils.py: all litellm imports → nanollm (completion, acompletion, batch_completion, aembedding, RateLimitError, drop_params) - crawl4ai/cli.py: litellm import → nanollm, provider docs URLs updated - crawl4ai/legacy/llmtxt.py: litellm imports → nanollm, set_verbose - crawl4ai/__version__.py: bump to 0.8.7a1 Co-Authored-By: Claude Opus 4.6 (1M context) --- crawl4ai/__version__.py | 2 +- crawl4ai/cli.py | 6 +++--- crawl4ai/legacy/llmtxt.py | 6 +++--- crawl4ai/utils.py | 22 +++++++++++----------- pyproject.toml | 2 +- requirements.txt | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/crawl4ai/__version__.py b/crawl4ai/__version__.py index 9b49905e7..f010e3e46 100644 --- a/crawl4ai/__version__.py +++ b/crawl4ai/__version__.py @@ -1,7 +1,7 @@ # crawl4ai/__version__.py # This is the version that will be used for stable releases -__version__ = "0.8.6" +__version__ = "0.8.7a1" # For nightly builds, this gets set during build process __nightly_version__ = None diff --git a/crawl4ai/cli.py b/crawl4ai/cli.py index 02b67155e..e20d22929 100644 --- a/crawl4ai/cli.py +++ b/crawl4ai/cli.py @@ -35,7 +35,7 @@ from crawl4ai.browser_profiler import ShrinkLevel, _format_size from crawl4ai.config import USER_SETTINGS from crawl4ai.cloud import cloud_cmd -from litellm import completion +from nanollm import completion from pathlib import Path @@ -66,7 +66,7 @@ def setup_llm_config() -> tuple[str, str]: if not provider: click.echo("\nNo default LLM provider configured.") click.echo("Provider format: 'company/model' (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')") - click.echo("See available providers at: https://docs.litellm.ai/docs/providers") + click.echo("See available providers at: https://github.com/unclecode/nanollm#supported-providers") provider = click.prompt("Enter provider") if not provider.startswith("ollama/"): @@ -344,7 +344,7 @@ def show_examples(): - cohere/command - google/gemini-pro - See full list of providers: https://docs.litellm.ai/docs/providers + See full list of providers: https://github.com/unclecode/nanollm#supported-providers # Set default LLM provider and token in advance crwl config set DEFAULT_LLM_PROVIDER "anthropic/claude-3-sonnet" diff --git a/crawl4ai/legacy/llmtxt.py b/crawl4ai/legacy/llmtxt.py index 302564165..a06975b87 100644 --- a/crawl4ai/legacy/llmtxt.py +++ b/crawl4ai/legacy/llmtxt.py @@ -11,14 +11,14 @@ from nltk.tokenize import word_tokenize from nltk.corpus import stopwords from nltk.stem import WordNetLemmatizer -from litellm import batch_completion +from nanollm import batch_completion from .async_logger import AsyncLogger -import litellm +import nanollm import pickle import hashlib # <--- ADDED for file-hash import glob -litellm.set_verbose = False +nanollm.set_verbose = False def _compute_file_hash(file_path: Path) -> str: diff --git a/crawl4ai/utils.py b/crawl4ai/utils.py index 4b3d96906..962835b5d 100644 --- a/crawl4ai/utils.py +++ b/crawl4ai/utils.py @@ -1774,10 +1774,10 @@ def perform_completion_with_backoff( dict: The API response or an error message after all retries. """ - from litellm import completion - from litellm.exceptions import RateLimitError - import litellm - litellm.drop_params = True # Auto-drop unsupported params (e.g., temperature for O-series/GPT-5) + from nanollm import completion + from nanollm.exceptions import RateLimitError + import nanollm + nanollm.drop_params = True # Auto-drop unsupported params (e.g., temperature for O-series/GPT-5) extra_args = {"temperature": 0.01, "api_key": api_token, "base_url": base_url} if json_response: @@ -1866,11 +1866,11 @@ async def aperform_completion_with_backoff( dict: The API response or an error message after all retries. """ - from litellm import acompletion - from litellm.exceptions import RateLimitError - import litellm + from nanollm import acompletion + from nanollm.exceptions import RateLimitError + import nanollm import asyncio - litellm.drop_params = True # Auto-drop unsupported params (e.g., temperature for O-series/GPT-5) + nanollm.drop_params = True # Auto-drop unsupported params (e.g., temperature for O-series/GPT-5) extra_args = {"temperature": 0.01, "api_key": api_token, "base_url": base_url} if json_response: @@ -1991,7 +1991,7 @@ def extract_blocks_batch(batch_data, provider="groq/llama3-70b-8192", api_token= """ api_token = os.getenv("GROQ_API_KEY", None) if not api_token else api_token - from litellm import batch_completion + from nanollm import batch_completion messages = [] @@ -3566,9 +3566,9 @@ async def get_text_embeddings( if not texts: return np.array([]) - # If LLMConfig provided, use litellm for embeddings + # If LLMConfig provided, use nanollm for embeddings if llm_config is not None: - from litellm import aembedding + from nanollm import aembedding # Get embedding model from config or use default embedding_model = llm_config.get('provider', 'text-embedding-3-small') diff --git a/pyproject.toml b/pyproject.toml index ee237d5b3..eea291ca3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "unclecode-litellm==1.81.13", + "nanollm @ git+https://github.com/unclecode/nanollm.git@feat/nanollm-rewrite", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", diff --git a/requirements.txt b/requirements.txt index 9686ffc95..2f64a22d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ aiohttp>=3.11.11 aiosqlite~=0.20 anyio>=4.0.0 lxml~=5.3 -unclecode-litellm==1.81.13 +nanollm @ git+https://github.com/unclecode/nanollm.git@feat/nanollm-rewrite numpy>=1.26.0,<3 pillow>=10.4 playwright>=1.49.0 From fcccc924444479698dd89e8e09cd2def6db8285a Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 18:40:54 +0800 Subject: [PATCH 02/10] fix: point nanollm dependency to hafezparast fork Use hafezparast/nanollm for easier access and independent control during testing. Co-Authored-By: Claude Opus 4.6 (1M context) --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eea291ca3..3501c78cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "nanollm @ git+https://github.com/unclecode/nanollm.git@feat/nanollm-rewrite", + "nanollm @ git+https://github.com/hafezparast/nanollm.git@feat/nanollm-rewrite", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", diff --git a/requirements.txt b/requirements.txt index 2f64a22d7..ced3d8b3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ aiohttp>=3.11.11 aiosqlite~=0.20 anyio>=4.0.0 lxml~=5.3 -nanollm @ git+https://github.com/unclecode/nanollm.git@feat/nanollm-rewrite +nanollm @ git+https://github.com/hafezparast/nanollm.git@feat/nanollm-rewrite numpy>=1.26.0,<3 pillow>=10.4 playwright>=1.49.0 From 25ab507e36492c7391179a1d6a8742de754a4a77 Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 18:41:35 +0800 Subject: [PATCH 03/10] chore: bump version to 0.8.7a2 Reflects nanollm dependency now pointing to hafezparast fork. Co-Authored-By: Claude Opus 4.6 (1M context) --- crawl4ai/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crawl4ai/__version__.py b/crawl4ai/__version__.py index f010e3e46..748424c96 100644 --- a/crawl4ai/__version__.py +++ b/crawl4ai/__version__.py @@ -1,7 +1,7 @@ # crawl4ai/__version__.py # This is the version that will be used for stable releases -__version__ = "0.8.7a1" +__version__ = "0.8.7a2" # For nightly builds, this gets set during build process __nightly_version__ = None From fe01d77828947e4c390e8eccfba56f9af6cba7b5 Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 18:42:30 +0800 Subject: [PATCH 04/10] chore: pin nanollm to v0.1.0 tag, bump to 0.8.7a3 Pin nanollm dependency to tagged release v0.1.0 instead of branch name for reproducible installs. Co-Authored-By: Claude Opus 4.6 (1M context) --- crawl4ai/__version__.py | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crawl4ai/__version__.py b/crawl4ai/__version__.py index 748424c96..aa4d1b497 100644 --- a/crawl4ai/__version__.py +++ b/crawl4ai/__version__.py @@ -1,7 +1,7 @@ # crawl4ai/__version__.py # This is the version that will be used for stable releases -__version__ = "0.8.7a2" +__version__ = "0.8.7a3" # For nightly builds, this gets set during build process __nightly_version__ = None diff --git a/pyproject.toml b/pyproject.toml index 3501c78cc..757be4365 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "nanollm @ git+https://github.com/hafezparast/nanollm.git@feat/nanollm-rewrite", + "nanollm @ git+https://github.com/hafezparast/nanollm.git@v0.1.0", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", diff --git a/requirements.txt b/requirements.txt index ced3d8b3c..4a2247525 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ aiohttp>=3.11.11 aiosqlite~=0.20 anyio>=4.0.0 lxml~=5.3 -nanollm @ git+https://github.com/hafezparast/nanollm.git@feat/nanollm-rewrite +nanollm @ git+https://github.com/hafezparast/nanollm.git@v0.1.0 numpy>=1.26.0,<3 pillow>=10.4 playwright>=1.49.0 From 65a42c1860b39768e86f031bca4b10cf480aecfa Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 18:50:58 +0800 Subject: [PATCH 05/10] fix: pin nanollm to exact commit hash to avoid pip cache issues Pin to commit 35a0c4b (v0.1.0) which includes the completion_tokens_details and prompt_tokens_details fix. Using commit hash instead of tag ensures pip won't serve a stale cached version. Bump to 0.8.7a4. Co-Authored-By: Claude Opus 4.6 (1M context) --- crawl4ai/__version__.py | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crawl4ai/__version__.py b/crawl4ai/__version__.py index aa4d1b497..5c5f97217 100644 --- a/crawl4ai/__version__.py +++ b/crawl4ai/__version__.py @@ -1,7 +1,7 @@ # crawl4ai/__version__.py # This is the version that will be used for stable releases -__version__ = "0.8.7a3" +__version__ = "0.8.7a4" # For nightly builds, this gets set during build process __nightly_version__ = None diff --git a/pyproject.toml b/pyproject.toml index 757be4365..786438890 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "nanollm @ git+https://github.com/hafezparast/nanollm.git@v0.1.0", + "nanollm @ git+https://github.com/hafezparast/nanollm.git@35a0c4b15582cb5e5bb21503aec5b7d8921fb8f5", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", diff --git a/requirements.txt b/requirements.txt index 4a2247525..5a4373dc3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ aiohttp>=3.11.11 aiosqlite~=0.20 anyio>=4.0.0 lxml~=5.3 -nanollm @ git+https://github.com/hafezparast/nanollm.git@v0.1.0 +nanollm @ git+https://github.com/hafezparast/nanollm.git@35a0c4b15582cb5e5bb21503aec5b7d8921fb8f5 numpy>=1.26.0,<3 pillow>=10.4 playwright>=1.49.0 From 7368f781b2378a58dbf55faaa7ffc9bdbd6ae01a Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 19:06:40 +0800 Subject: [PATCH 06/10] fix: bump nanollm to v0.1.1, fixes __dict__ access on token details nanollm v0.1.1 wraps completion_tokens_details and prompt_tokens_details in _AttrDict so crawl4ai's .__dict__ access pattern works correctly. Bump to 0.8.7a5. Co-Authored-By: Claude Opus 4.6 (1M context) --- crawl4ai/__version__.py | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crawl4ai/__version__.py b/crawl4ai/__version__.py index 5c5f97217..352aa28ab 100644 --- a/crawl4ai/__version__.py +++ b/crawl4ai/__version__.py @@ -1,7 +1,7 @@ # crawl4ai/__version__.py # This is the version that will be used for stable releases -__version__ = "0.8.7a4" +__version__ = "0.8.7a5" # For nightly builds, this gets set during build process __nightly_version__ = None diff --git a/pyproject.toml b/pyproject.toml index 786438890..742c72a63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "nanollm @ git+https://github.com/hafezparast/nanollm.git@35a0c4b15582cb5e5bb21503aec5b7d8921fb8f5", + "nanollm @ git+https://github.com/hafezparast/nanollm.git@v0.1.1", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", diff --git a/requirements.txt b/requirements.txt index 5a4373dc3..17b1a18a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ aiohttp>=3.11.11 aiosqlite~=0.20 anyio>=4.0.0 lxml~=5.3 -nanollm @ git+https://github.com/hafezparast/nanollm.git@35a0c4b15582cb5e5bb21503aec5b7d8921fb8f5 +nanollm @ git+https://github.com/hafezparast/nanollm.git@v0.1.1 numpy>=1.26.0,<3 pillow>=10.4 playwright>=1.49.0 From c4b7b6fe3888259fcb28fb730b46fb35dd4c9ade Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 19:45:33 +0800 Subject: [PATCH 07/10] chore: switch nanollm dependency to nanollm-core repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Point to hafezparast/nanollm-core (Approach 1 — general-purpose litellm replacement) instead of hafezparast/nanollm (Approach 2 — fork). Co-Authored-By: Claude Opus 4.6 (1M context) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 742c72a63..379250aa9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "nanollm @ git+https://github.com/hafezparast/nanollm.git@v0.1.1", + "nanollm @ git+https://github.com/hafezparast/nanollm-core.git@6ac866536d1154eb445e3c85a8a39f6db0c45203", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", From 70e1d1a86600e320c0dfae34fa63b9d4477d9e8d Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 20:58:55 +0800 Subject: [PATCH 08/10] chore: switch to nanollm-approach1 (graph-driven litellm replacement) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Point dependency to hafezparast/nanollm-approach1 v0.2.0 — built by AST-analyzing litellm's 544K-line codebase, extracting the core completion subgraph, and rewriting it with multimodal/vision support. 3,731 lines, 1 dep (httpx), 605 tests, 7 adapters, 25+ providers. Co-Authored-By: Claude Opus 4.6 (1M context) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 379250aa9..80bf172ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "nanollm @ git+https://github.com/hafezparast/nanollm-core.git@6ac866536d1154eb445e3c85a8a39f6db0c45203", + "nanollm @ git+https://github.com/hafezparast/nanollm-approach1.git@e1fbaf38e09e41ef3451c6d0eee9e9de0166629a", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", From 91e7cda30fe20f72738a2f780b2bc8f7a5c33a8a Mon Sep 17 00:00:00 2001 From: hafezparast Date: Thu, 26 Mar 2026 23:58:27 +0800 Subject: [PATCH 09/10] chore: switch to nanollm-final v1.0.0 (best-of-both litellm replacement) Combines Approach 1 (httpx, module functions, 600+ tests) with Approach 2 (class-based providers, NanoLLM client, built-in retry, structured output, thinking/reasoning). 5,026 lines, 1 dep (httpx), 609 tests, 25 providers. Co-Authored-By: Claude Opus 4.6 (1M context) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 80bf172ac..dd45316c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ "aiosqlite~=0.20", "anyio>=4.0.0", "lxml~=5.3", - "nanollm @ git+https://github.com/hafezparast/nanollm-approach1.git@e1fbaf38e09e41ef3451c6d0eee9e9de0166629a", + "nanollm @ git+https://github.com/hafezparast/nanollm-final.git@8a8d93b02a77f04a32f202620d95c52ad26c7d05", "numpy>=1.26.0,<3", "pillow>=10.4", "playwright>=1.49.0", From 1b274c61aa8b5663b6b777cc03503ccca9079ca4 Mon Sep 17 00:00:00 2001 From: Soham Kukreti Date: Fri, 27 Mar 2026 15:46:03 +0530 Subject: [PATCH 10/10] fix: add missing packaging and tiktoken to dependencies Both were previously pulled in transitively via litellm. Replacing it with nanollm exposed them as undeclared direct deps, causing ModuleNotFoundError on clean installs. --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dd45316c6..6f1d04596 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,9 @@ dependencies = [ "humanize>=4.10.0", "lark>=1.2.2", "alphashape>=1.3.1", - "shapely>=2.0.0" + "shapely>=2.0.0", + "packaging>=21.0", + "tiktoken>=0.5.0" ] classifiers = [ "Development Status :: 4 - Beta",