diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 550e4ea2..e0fffe6a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,9 @@ repos: args: - "--fix" - "--exit-non-zero-on-fix" + exclude: any-llm-sdk-stub - id: ruff-format + exclude: any-llm-sdk-stub - repo: local hooks: @@ -36,7 +38,7 @@ repos: language: system types: [python] verbose: true - exclude: demos|scripts + exclude: demos|scripts|any-llm-sdk-stub - repo: https://github.com/codespell-project/codespell rev: "v2.4.1" diff --git a/README.md b/README.md index 96f14b65..9b12a43d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ [![Integration Tests](https://github.com/mozilla-ai/any-llm/actions/workflows/tests-integration.yaml/badge.svg)](https://github.com/mozilla-ai/any-llm/actions/workflows/tests-integration.yaml/) ![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg) -[![PyPI](https://img.shields.io/pypi/v/any-llm-sdk)](https://pypi.org/project/any-llm-sdk/) +[![PyPI](https://img.shields.io/pypi/v/any-llm)](https://pypi.org/project/any-llm/) Discord @@ -32,7 +32,7 @@ Switch between OpenAI, Anthropic, Mistral, Ollama, and more without changing you ## Quickstart ```python -pip install 'any-llm-sdk[mistral,ollama]' +pip install 'any-llm[mistral,ollama]' export MISTRAL_API_KEY="YOUR_KEY_HERE" # or OPENAI_API_KEY, etc from any_llm import completion @@ -63,9 +63,9 @@ print(response.choices[0].message.content) Install support for specific providers: ```bash -pip install 'any-llm-sdk[openai]' # Just OpenAI -pip install 'any-llm-sdk[mistral,ollama]' # Multiple providers -pip install 'any-llm-sdk[all]' # All supported providers +pip install 'any-llm[openai]' # Just OpenAI +pip install 'any-llm[mistral,ollama]' # Multiple providers +pip install 'any-llm[all]' # All supported providers ``` See our [list of supported providers](https://mozilla-ai.github.io/any-llm/providers/) to choose which ones you need. diff --git a/any-llm-sdk-stub/.gitignore b/any-llm-sdk-stub/.gitignore new file mode 100644 index 00000000..d39d8abe --- /dev/null +++ b/any-llm-sdk-stub/.gitignore @@ -0,0 +1,31 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Version file generated by setuptools_scm +src/any_llm/_version.py + +# Virtual environments +venv/ +env/ +ENV/ +.venv diff --git a/any-llm-sdk-stub/PUBLISHING.md b/any-llm-sdk-stub/PUBLISHING.md new file mode 100644 index 00000000..9be58462 --- /dev/null +++ b/any-llm-sdk-stub/PUBLISHING.md @@ -0,0 +1,101 @@ +# Publishing Guide for any-llm-sdk Stub Package + +This guide explains how to publish the `any-llm-sdk` stub package to PyPI. + +## Prerequisites + +1. You must have maintainer access to the `any-llm-sdk` package on PyPI +2. Install build tools: + ```bash + pip install build twine + ``` + +## Publishing Steps + +### 1. Build the Package + +From the `any-llm-sdk-stub` directory: + +```bash +cd any-llm-sdk-stub +python -m build +``` + +This creates distribution files in the `dist/` directory. + +### 2. Check the Package + +Verify the package is correctly formed: + +```bash +twine check dist/* +``` + +### 3. Upload to TestPyPI (Optional but Recommended) + +Test the upload first: + +```bash +twine upload --repository testpypi dist/* +``` + +Then test installation: + +```bash +pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ any-llm-sdk +``` + +### 4. Upload to PyPI + +Once you've verified everything works: + +```bash +twine upload dist/* +``` + +You'll be prompted for your PyPI credentials (or use an API token). + +## Version Management + +The version is managed by `setuptools_scm` and automatically derived from git tags in the parent repository. The stub package should use the same version number as the main `any-llm` package for consistency. + +To create a new version: + +```bash +# In the root of the main repository +git tag v0.16.0 +git push origin v0.16.0 +``` + +Then rebuild and publish the stub package. + +## Publishing Checklist + +- [ ] Main `any-llm` package is published to PyPI +- [ ] Version tag exists in git +- [ ] Built the stub package: `python -m build` +- [ ] Checked the package: `twine check dist/*` +- [ ] (Optional) Tested on TestPyPI +- [ ] Uploaded to PyPI: `twine upload dist/*` +- [ ] Verified installation: `pip install any-llm-sdk` +- [ ] Confirmed deprecation warning appears + +## Ongoing Maintenance + +This stub package should be republished whenever a new major version of `any-llm` is released to ensure the dependency constraint stays up to date. The stub package version should match the main package version. + +## Troubleshooting + +### "File already exists" error + +If you get an error that the file already exists on PyPI, you need to bump the version number. Clean the dist directory and rebuild: + +```bash +rm -rf dist/ +python -m build +twine upload dist/* +``` + +### Import errors + +Make sure `any-llm` is installed and importable. The stub package depends on it, so it should be automatically installed when users install `any-llm-sdk`. diff --git a/any-llm-sdk-stub/QUICK_PUBLISH.sh b/any-llm-sdk-stub/QUICK_PUBLISH.sh new file mode 100755 index 00000000..c6983247 --- /dev/null +++ b/any-llm-sdk-stub/QUICK_PUBLISH.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Quick publishing script for any-llm-sdk stub package +# Run this from the any-llm-sdk-stub directory + +set -e # Exit on error + +echo "πŸš€ Publishing any-llm-sdk stub package to PyPI" +echo "" + +# Check if we're in the right directory +if [ ! -f "pyproject.toml" ]; then + echo "❌ Error: pyproject.toml not found. Run this from the any-llm-sdk-stub directory." + exit 1 +fi + +# Check if any-llm is published +echo "πŸ” Checking if any-llm is available on PyPI..." +if pip index versions any-llm 2>&1 | grep -q "No matching distribution"; then + echo "❌ Error: 'any-llm' package not found on PyPI." + echo " You MUST publish the main 'any-llm' package first!" + exit 1 +fi +echo "βœ… Main package 'any-llm' found on PyPI" +echo "" + +# Clean previous builds +echo "🧹 Cleaning previous builds..." +rm -rf dist/ build/ *.egg-info +echo "" + +# Build the package +echo "πŸ“¦ Building package..." +python -m build +echo "" + +# Check the package +echo "πŸ” Checking package..." +twine check dist/* +echo "" + +# Ask for confirmation +read -p "Ready to upload to PyPI? (y/N) " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Cancelled." + exit 1 +fi + +# Upload to PyPI +echo "πŸ“€ Uploading to PyPI..." +twine upload dist/* +echo "" + +echo "βœ… Successfully published to PyPI!" +echo "" +echo "πŸ§ͺ Test installation with:" +echo " pip install any-llm-sdk" +echo "" diff --git a/any-llm-sdk-stub/README.md b/any-llm-sdk-stub/README.md new file mode 100644 index 00000000..efb9d047 --- /dev/null +++ b/any-llm-sdk-stub/README.md @@ -0,0 +1,66 @@ +# any-llm-sdk β†’ any-llm + +⚠️ **DEPRECATION NOTICE** ⚠️ + +The `any-llm-sdk` package has been renamed to `any-llm`. + +## Migration Guide + +### Update Your Installation + +**Old:** +```bash +pip install any-llm-sdk +``` + +**New:** +```bash +pip install any-llm +``` + +### Update Your Dependencies + +If you have `any-llm-sdk` in your `requirements.txt`, `pyproject.toml`, or other dependency files: + +**requirements.txt:** +```diff +- any-llm-sdk>=0.15.0 ++ any-llm>=0.16.0 +``` + +**pyproject.toml:** +```diff +dependencies = [ +- "any-llm-sdk[all]>=0.15.0", ++ "any-llm[all]>=0.16.0", +] +``` + +### No Code Changes Required + +Your import statements will continue to work without any changes: + +```python +from any_llm import completion, AnyLLM +# Works exactly the same way! +``` + +## What This Package Does + +This package (`any-llm-sdk`) now serves as a lightweight redirect to the real package (`any-llm`). When you install `any-llm-sdk`, it automatically installs `any-llm` as a dependency and re-exports all its functionality. + +## Why the Change? + +The shorter package name `any-llm` better matches the project name and is more concise for users to type and remember. + +## Timeline + +- **Now**: Both packages work. `any-llm-sdk` shows a deprecation warning. +- **Future**: `any-llm-sdk` stub package will be maintained for backwards compatibility but may eventually be deprecated. + +## More Information + +For the latest documentation and updates, visit: +- **Documentation**: https://mozilla-ai.github.io/any-llm/ +- **GitHub**: https://github.com/mozilla-ai/any-llm +- **PyPI (new)**: https://pypi.org/project/any-llm/ diff --git a/any-llm-sdk-stub/WHY_THIS_WORKS.md b/any-llm-sdk-stub/WHY_THIS_WORKS.md new file mode 100644 index 00000000..0be81423 --- /dev/null +++ b/any-llm-sdk-stub/WHY_THIS_WORKS.md @@ -0,0 +1,212 @@ +# Why This Stub Package Approach Works + +This document explains the technical details of how the stub package ensures a seamless transition. + +## The Problem + +When you rename a package on PyPI, there's no built-in redirect mechanism. This creates several issues: + +1. **Existing installations break**: Users with `any-llm-sdk` in requirements can't install +2. **Documentation is outdated**: Old tutorials and Stack Overflow answers point to the wrong package +3. **Breaking changes**: Users need to update code and dependencies simultaneously +4. **Ecosystem fragmentation**: Some projects use the old name, some use the new name + +## The Solution: Stub Package Pattern + +The stub package acts as a "pointer" to the real package. Here's how it works: + +### 1. Dependency Management + +**pyproject.toml** (stub package): +```toml +dependencies = [ + "any-llm>=0.16.0", +] +``` + +When someone runs `pip install any-llm-sdk`, pip: +- Installs the `any-llm-sdk` package (the stub) +- Sees it depends on `any-llm>=0.16.0` +- Installs `any-llm` automatically +- Both packages end up installed + +### 2. Module Re-exports + +**src/any_llm/__init__.py** (stub package): +```python +from any_llm import * +``` + +This re-exports everything from the real package, so: +```python +from any_llm import completion # Works the same from both packages! +``` + +### 3. Deprecation Warning + +The stub package shows a warning on import: +```python +warnings.warn( + "The 'any-llm-sdk' package has been renamed...", + DeprecationWarning, +) +``` + +This: +- Doesn't break code (it's just a warning) +- Educates users about the migration +- Can be silenced if needed with warnings filters + +## Why This Is Better Than Alternatives + +### ❌ Alternative 1: Just rename and leave users behind +**Problem**: Breaks all existing installations + +### ❌ Alternative 2: Maintain two separate packages +**Problem**: Double the maintenance burden, potential for drift + +### βœ… Alternative 3: Stub package (our approach) +**Benefits**: +- Zero code changes needed +- Automatic dependency resolution +- Clear migration path +- Easy to maintain (stub rarely changes) + +## Real-World Example: How It Works For Users + +### Scenario 1: User with old dependency + +**requirements.txt**: +``` +any-llm-sdk>=0.15.0 +``` + +When they run `pip install -r requirements.txt`: +1. Installs `any-llm-sdk` (stub) +2. Stub depends on `any-llm>=0.16.0` +3. Installs `any-llm>=0.16.0` +4. Their code works! They see a deprecation warning. +5. They update their requirements at their convenience. + +### Scenario 2: User with new dependency + +**requirements.txt**: +``` +any-llm>=0.16.0 +``` + +When they run `pip install -r requirements.txt`: +1. Installs `any-llm>=0.16.0` +2. Done! No stub needed. + +### Scenario 3: Mixed dependencies (transitional period) + +**requirements.txt**: +``` +any-llm-sdk>=0.15.0 +any-llm>=0.16.0 +``` + +When they run `pip install -r requirements.txt`: +1. Installs `any-llm>=0.16.0` (satisfies second requirement) +2. Installs `any-llm-sdk` (satisfies first requirement) +3. Stub's dependency on `any-llm>=0.16.0` is already satisfied +4. Works correctly, no version conflicts + +## Technical Details + +### Package Identity + +Each package has its own identity on PyPI: +- `any-llm` β†’ `https://pypi.org/project/any-llm/` +- `any-llm-sdk` β†’ `https://pypi.org/project/any-llm-sdk/` + +But they both install to the same module: `any_llm/` + +This is **allowed** because: +- PyPI doesn't enforce a 1:1 relationship between package name and module name +- Multiple packages can provide the same module (though usually they conflict) +- In our case, the stub intentionally re-exports the main package + +### Version Synchronization + +The stub package version should match the main package version: +- `any-llm==0.16.0` (the real package) +- `any-llm-sdk==0.16.0` (the stub, depends on any-llm>=0.16.0) + +This makes it clear which versions correspond and simplifies troubleshooting. + +### Import Resolution + +When Python imports `any_llm`: +1. Checks `site-packages/any_llm/` +2. Finds `__init__.py` from whichever package was installed last +3. That `__init__.py` imports from the real package +4. Everything just worksβ„’ + +## Edge Cases Handled + +### Edge Case 1: User installs stub first, then uninstalls main package +```bash +pip install any-llm-sdk # Installs both +pip uninstall any-llm # Removes main package +``` +**Result**: Imports will fail. This is expected - you can't remove the real package! + +**Fix**: `pip install any-llm` or `pip install --reinstall any-llm-sdk` + +### Edge Case 2: Version conflicts +```bash +pip install any-llm==0.16.0 +pip install any-llm-sdk==0.15.0 # Older stub +``` +**Result**: pip will try to resolve dependencies. The stub's dependency on `any-llm` might cause pip to upgrade/downgrade. + +**Fix**: Keep versions synchronized. Always publish both packages with the same version. + +### Edge Case 3: User has both in requirements with different version constraints +``` +any-llm>=0.16.0 +any-llm-sdk<0.16.0 +``` +**Result**: pip can't satisfy both constraints and will fail. + +**Fix**: Update to use only `any-llm>=0.16.0`. + +## Maintenance Going Forward + +### During Active Development (Now - 6 months) +- Publish both packages with every release +- Maintain version synchronization +- Monitor for user issues + +### After Transition Period (6+ months) +- Continue publishing stub package (minimal effort) +- Consider making stub package more aggressive with warnings +- Eventually: Stub package can stop being updated (still points to any-llm) + +### Long-term (1+ years) +- Stub package can be "archived" with a final release +- Final release always depends on latest any-llm +- Users eventually migrate away naturally + +## Comparison with Other Projects + +This pattern is used by many Python projects: + +- `typing` β†’ `typing_extensions` (backports) +- `pkg_resources` β†’ `importlib.metadata` (stdlib transition) +- Many renamed packages in the ecosystem + +PyPI explicitly allows and supports this pattern! + +## Conclusion + +The stub package approach provides: +- βœ… Zero-breaking-change migration +- βœ… Clear deprecation path +- βœ… Minimal maintenance burden +- βœ… User-friendly transition +- βœ… Ecosystem compatibility + +It's the recommended approach for package renames in the Python ecosystem. diff --git a/any-llm-sdk-stub/pyproject.toml b/any-llm-sdk-stub/pyproject.toml new file mode 100644 index 00000000..33bef4ee --- /dev/null +++ b/any-llm-sdk-stub/pyproject.toml @@ -0,0 +1,25 @@ +[build-system] +requires = ["setuptools>=48"] +build-backend = "setuptools.build_meta" + +[project] +name = "any-llm-sdk" +version = "0.16.0" +readme = "README.md" +license = {text = "Apache-2.0"} +requires-python = ">=3.11" +dependencies = [ + "any-llm>=0.16.0", # Depends on the new package name +] + +description = "Deprecated: Please use 'any-llm' instead. This package is now just a redirect." + +[project.urls] +Documentation = "https://mozilla-ai.github.io/any-llm/" +Issues = "https://github.com/mozilla-ai/any-llm/issues" +Source = "https://github.com/mozilla-ai/any-llm" + +[tool.setuptools.packages.find] +exclude = ["tests", "tests.*"] +where = ["src"] +namespaces = false diff --git a/any-llm-sdk-stub/ruff.toml b/any-llm-sdk-stub/ruff.toml new file mode 100644 index 00000000..0cb9c7f5 --- /dev/null +++ b/any-llm-sdk-stub/ruff.toml @@ -0,0 +1,7 @@ +# Ruff configuration for the stub package +# This package intentionally uses star imports to re-export from any-llm + +[lint] +ignore = [ + "F405", # May be undefined from star imports - expected for stub package +] diff --git a/any-llm-sdk-stub/src/any_llm/__init__.py b/any-llm-sdk-stub/src/any_llm/__init__.py new file mode 100644 index 00000000..8b5e7558 --- /dev/null +++ b/any-llm-sdk-stub/src/any_llm/__init__.py @@ -0,0 +1,46 @@ +"""Deprecated package redirect for any-llm-sdk. + +DEPRECATION NOTICE +================== + +The 'any-llm-sdk' package has been renamed to 'any-llm'. + +Please update your dependencies: +- Old: pip install any-llm-sdk +- New: pip install any-llm + +This package now simply re-exports everything from 'any-llm' for backwards compatibility. +This stub package will be maintained for a transition period, but all new development +happens in the 'any-llm' package. + +Update your imports (though both will work): +- from any_llm import completion # Works the same way +""" + +import warnings + +from any_llm import * # noqa: F403 +from any_llm import __version__ # type: ignore[attr-defined] + +# Show deprecation warning when the package is imported +warnings.warn( + "The 'any-llm-sdk' package has been renamed to 'any-llm'. " + "Please update your dependencies to use 'pip install any-llm' instead. " + "This stub package will be maintained for backwards compatibility but may be removed in the future.", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "AnyLLM", + "LLMProvider", + "__version__", + "acompletion", + "aembedding", + "alist_models", + "aresponses", + "completion", + "embedding", + "list_models", + "responses", +] diff --git a/demos/chat/backend/pyproject.toml b/demos/chat/backend/pyproject.toml index 6a73cfb3..ab3d644d 100644 --- a/demos/chat/backend/pyproject.toml +++ b/demos/chat/backend/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "fastapi>=0.115.6", "uvicorn[standard]>=0.32.1", "python-multipart>=0.0.20", - "any-llm-sdk[all]>=0.15.0", + "any-llm[all]>=0.15.0", ] [project.urls] diff --git a/demos/finder/README.md b/demos/finder/README.md index 1e7d328a..00df124c 100644 --- a/demos/finder/README.md +++ b/demos/finder/README.md @@ -91,7 +91,7 @@ If you see "No providers are configured with API keys": ### Provider errors The application will show provider-specific errors in the results. Common issues: - **API key not configured**: Set the required environment variable -- **Missing packages**: Install additional dependencies with `pip install any-llm-sdk[provider_name]` +- **Missing packages**: Install additional dependencies with `pip install any-llm[provider_name]` - **API errors**: Check your API key validity and provider service status ### Search returns no results diff --git a/demos/finder/backend/pyproject.toml b/demos/finder/backend/pyproject.toml index 040b099b..94b8faab 100644 --- a/demos/finder/backend/pyproject.toml +++ b/demos/finder/backend/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "fastapi>=0.115.6", "uvicorn[standard]>=0.32.1", "python-multipart>=0.0.20", - "any-llm-sdk[all]>=0.15.0", + "any-llm[all]>=0.15.0", ] [project.urls] diff --git a/docs/cookbooks/any_llm_getting_started.ipynb b/docs/cookbooks/any_llm_getting_started.ipynb index e33f01f9..bbf2090f 100644 --- a/docs/cookbooks/any_llm_getting_started.ipynb +++ b/docs/cookbooks/any_llm_getting_started.ipynb @@ -52,7 +52,7 @@ "metadata": {}, "outputs": [], "source": [ - "%pip install any-llm-sdk[all] nest-asyncio -q\n", + "%pip install any-llm[all] nest-asyncio -q\n", "\n", "# nest_asyncio allows us to use 'await' directly in Jupyter notebooks\n", "# This is needed because any-llm uses async functions for API calls\n", diff --git a/docs/quickstart.md b/docs/quickstart.md index 072b02b1..2d0bf7a6 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -29,7 +29,7 @@ schema: ### Installation ```bash -pip install any-llm-sdk[all] # Install with all provider support +pip install any-llm[all] # Install with all provider support ``` #### Installing Specific Providers @@ -37,15 +37,15 @@ pip install any-llm-sdk[all] # Install with all provider support If you want to install a specific provider from our [supported providers](./providers.md): ```bash -pip install any-llm-sdk[mistral] # For Mistral provider -pip install any-llm-sdk[ollama] # For Ollama provider +pip install any-llm[mistral] # For Mistral provider +pip install any-llm[ollama] # For Ollama provider # install multiple providers -pip install any-llm-sdk[mistral,ollama] +pip install any-llm[mistral,ollama] ``` #### Library Integration -If you're building a library, install just the base package (`pip install any-llm-sdk`) and let your users install provider dependencies. +If you're building a library, install just the base package (`pip install any-llm`) and let your users install provider dependencies. > **API Keys:** Set your provider's API key as an environment variable (e.g., `export MISTRAL_API_KEY="your-key"`) or pass it directly using the `api_key` parameter. diff --git a/pyproject.toml b/pyproject.toml index d1faaa7b..40ae3b06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=48", "setuptools_scm[toml]>=6.3.1"] build-backend = "setuptools.build_meta" [project] -name = "any-llm-sdk" +name = "any-llm" readme = "README.md" license = {text = "Apache-2.0"} requires-python = ">=3.11" @@ -18,7 +18,7 @@ dependencies = [ [project.optional-dependencies] all = [ - "any-llm-sdk[mistral,anthropic,huggingface,gemini,vertexai,cohere,cerebras,fireworks,groq,bedrock,azure,azureopenai,watsonx,together,sambanova,ollama,moonshot,nebius,xai,databricks,deepseek,inception,openai,openrouter,portkey,lmstudio,llama,voyage,perplexity,platform,llamafile,llamacpp,sagemaker,gateway,zai,minimax]" + "any-llm[mistral,anthropic,huggingface,gemini,vertexai,cohere,cerebras,fireworks,groq,bedrock,azure,azureopenai,watsonx,together,sambanova,ollama,moonshot,nebius,xai,databricks,deepseek,inception,openai,openrouter,portkey,lmstudio,llama,voyage,perplexity,platform,llamafile,llamacpp,sagemaker,gateway,zai,minimax]" ] platform = [ diff --git a/src/any_llm/__init__.py b/src/any_llm/__init__.py index 593bcd0e..20c3a3f4 100644 --- a/src/any_llm/__init__.py +++ b/src/any_llm/__init__.py @@ -5,7 +5,7 @@ from any_llm.constants import LLMProvider try: - __version__ = version("any-llm-sdk") + __version__ = version("any-llm") except PackageNotFoundError: # In the case of local development # i.e., running directly from the source directory without package being installed @@ -15,6 +15,7 @@ __all__ = [ "AnyLLM", "LLMProvider", + "__version__", "acompletion", "aembedding", "alist_models", diff --git a/src/any_llm/any_llm.py b/src/any_llm/any_llm.py index c1e5254a..314cae94 100644 --- a/src/any_llm/any_llm.py +++ b/src/any_llm/any_llm.py @@ -99,7 +99,7 @@ def __init__(self, api_key: str | None = None, api_base: str | None = None, **kw def _verify_no_missing_packages(self) -> None: if self.MISSING_PACKAGES_ERROR is not None: - msg = f"{self.PROVIDER_NAME} required packages are not installed. Please install them with `pip install any-llm-sdk[{self.PROVIDER_NAME}]`" + msg = f"{self.PROVIDER_NAME} required packages are not installed. Please install them with `pip install any-llm[{self.PROVIDER_NAME}]`" raise ImportError(msg) from self.MISSING_PACKAGES_ERROR def _verify_and_set_api_key(self, api_key: str | None = None) -> str | None: