Extending ClientAI: Adding a New Provider¶
This guide will walk you through the process of adding support for a new AI provider to the ClientAI package. By following these steps, you'll be able to integrate a new provider seamlessly into the existing structure.
Overview¶
To add a new provider, you'll need to:
- Create a new directory for the provider
- Implement the provider-specific types
- Implement the provider class
- Implement Unified Error Handling
- Update the main ClientAI class
- Update the package constants
- Add tests for the new provider
- Test Error Handling
- Update Documentation
Let's go through each step in detail.
Step 1: Create a New Directory¶
First, create a new directory for your provider in the clientai
folder. For example, if you're adding support for a provider called "NewAI", create a directory named newai
:
Step 2: Implement Provider-Specific Types¶
In the _typing.py
file, define the types specific to your provider. This should include response types, client types, and any other necessary types. Here's an example structure:
# clientai/newai/_typing.py
from typing import Any, Dict, Iterator, Protocol, TypedDict, Union
from .._common_types import GenericResponse
class NewAIResponse(TypedDict):
# Define the structure of a full response from NewAI
pass
class NewAIStreamResponse(TypedDict):
# Define the structure of a streaming response chunk from NewAI
pass
class NewAIClientProtocol(Protocol):
# Define the expected interface for the NewAI client
pass
NewAIGenericResponse = GenericResponse[
str, NewAIResponse, NewAIStreamResponse
]
# Add any other necessary types
Step 3: Implement the Provider Class¶
In the provider.py
file, implement the Provider
class that inherits from AIProvider
. This class should implement the generate_text
and chat
methods:
# clientai/newai/provider.py
from ..ai_provider import AIProvider
from ._typing import NewAIClientProtocol, NewAIGenericResponse
from typing import List
from .._common_types import Message
class Provider(AIProvider):
def __init__(self, api_key: str):
# Initialize the NewAI client
pass
def generate_text(
self,
prompt: str,
model: str,
return_full_response: bool = False,
stream: bool = False,
**kwargs
) -> NewAIGenericResponse:
# Implement text generation logic
pass
def chat(
self,
messages: List[Message],
model: str,
return_full_response: bool = False,
stream: bool = False,
**kwargs
) -> NewAIGenericResponse:
# Implement chat logic
pass
# Implement any helper methods as needed
Make sure to handle both streaming and non-streaming responses, as well as the return_full_response
option.
Step 4: Implement Unified Error Handling¶
Before implementing the provider class, set up error handling for your provider. This ensures consistent error reporting across all providers.
-
First, import the necessary error types:
-
Implement the error mapping method in your provider class:
class Provider(AIProvider): ... def _map_exception_to_clientai_error( self, e: Exception, status_code: Optional[int] = None ) -> ClientAIError: """ Maps NewAI-specific exceptions to ClientAI exceptions. Args: e: The caught exception status_code: Optional HTTP status code Returns: ClientAIError: The appropriate ClientAI exception """ error_message = str(e) status_code = status_code or getattr(e, "status_code", None) # Map NewAI-specific exceptions to ClientAI exceptions if isinstance(e, NewAIAuthError): return AuthenticationError( error_message, status_code=401, original_error=e ) elif isinstance(e, NewAIRateLimitError): return RateLimitError( error_message, status_code=429, original_error=e ) elif "model not found" in error_message.lower(): return ModelError( error_message, status_code=404, original_error=e ) elif isinstance(e, NewAIInvalidRequestError): return InvalidRequestError( error_message, status_code=400, original_error=e ) elif isinstance(e, NewAITimeoutError): return TimeoutError( error_message, status_code=408, original_error=e ) # Default to APIError for unknown errors return APIError( error_message, status_code, original_error=e )
Step 5: Update the Main ClientAI Class¶
Update the clientai/client_ai.py
file to include support for your new provider:
-
Add an import for your new provider:
-
Update the
__init__
method of theClientAI
class to handle the new provider:def __init__(self, provider_name: str, **kwargs): prov_name = provider_name # ----- add "newai" here ----- if prov_name not in ["openai", "replicate", "ollama", "newai"]: raise ValueError(f"Unsupported provider: {prov_name}") if ( prov_name == "openai" and not OPENAI_INSTALLED ... # ----- also add "newai" here ----- or prov_name == "newai" and not NEWAI_INSTALLED ): raise ImportError( f"The {prov_name} provider is not installed. " f"Please install it with 'pip install clientai[{prov_name}]'." )
-
Add the new provider to the provider initialization logic:
... try: provider_module = import_module( f".{prov_name}.provider", package="clientai" ) provider_class = getattr(provider_module, "Provider") # ----- add "newai" here ----- if prov_name in ["openai", "replicate", "newai"]: self.provider = cast( P, provider_class(api_key=kwargs.get("api_key")) ) ...
Step 6: Update Package Constants and Dependencies¶
-
In the
clientai/_constants.py
file, add a constant for your new provider: -
Update the
clientai/__init__.py
file to export the new constant: -
Update the
pyproject.toml
file to include the new provider as an optional dependency: -
Define an optional group for the new provider:
-
Include the new provider in the development dependencies:
-
Run
poetry update
to update thepoetry.lock
file with the new dependencies.
These changes allow users to install the new provider's dependencies using Poetry:
If users are not using Poetry and are installing the package via pip, they can still use the extras syntax:
Step 7: Add Tests¶
Create a new test file for your provider in the tests
directory:
Implement tests for your new provider, ensuring that you cover both the generate_text
and chat
methods, as well as streaming and non-streaming scenarios.
Step 8: Test Error Handling¶
Also create a new test file to test your provider's exceptions in the tests
directory:
Add tests to ensure unified tests are being handled with a reference to the original error.
Step 9: Update Documentation¶
Don't forget to update the documentation to include information about the new provider:
-
Add a new file
docs/api/newai_provider.md
with the following template for the NewAI provider. -
Update
docs/index.md
to add a reference to the new provider. - Update
docs/quick-start.md
to include an example of how to use the new provider. - Update
docs/api/overview.md
to include a link to the new provider's documentation.
Contribution Guidelines¶
After implementing your new provider, it's important to ensure that your code meets the project's quality standards and follows the contribution guidelines. Please refer to our Contributing Guide for detailed information on how to contribute to ClientAI.
Quick Summary of Development Tools¶
ClientAI uses the following tools for maintaining code quality:
-
pytest: For running tests
-
mypy: For type checking
-
ruff: For code linting and formatting
Make sure to run these tools and address any issues before submitting your contribution.
Conclusion¶
By following these steps, you can successfully add support for a new AI provider to the ClientAI package. Remember to maintain consistency with the existing code style and structure, and to thoroughly test your implementation.
If you're contributing this new provider to the main ClientAI repository, make sure to follow the contribution guidelines and submit a pull request with your changes. Your contribution will help expand the capabilities of ClientAI and benefit the entire community.
Thank you for your interest in extending ClientAI!