From 2fc5a88ecc931d510990c128f58b241b39aeceb2 Mon Sep 17 00:00:00 2001 From: Igor Benav Date: Fri, 1 Nov 2024 04:31:02 -0300 Subject: [PATCH] Deployed 6e46f48 with MkDocs version: 1.5.3 --- advanced/overview/index.html | 44 +++--- extending/index.html | 61 ++++---- quick-start/index.html | 42 ++++++ search/search_index.json | 2 +- sitemap.xml.gz | Bin 508 -> 508 bytes usage/ollama_manager/index.html | 247 ++++++++++++++++++++++---------- 6 files changed, 278 insertions(+), 118 deletions(-) diff --git a/advanced/overview/index.html b/advanced/overview/index.html index 0783f54..6e60654 100644 --- a/advanced/overview/index.html +++ b/advanced/overview/index.html @@ -1737,49 +1737,57 @@

Advanced OverviewProvider-Specific Parameters

Different AI providers offer unique parameters and features. Understanding these can help you fine-tune your AI interactions for optimal results.

    -
  1. Ollama Specific Guide: Learn about Ollama's unique parameters, including context handling, streaming options, and custom templates.
  2. -

    Ollama Specific Guide

    +

    Ollama Specific Guide: Learn about Ollama's unique parameters, including context handling, streaming options, and custom templates.

    +
  3. OpenAI Specific Guide: Explore OpenAI's advanced features, such as logit bias and model-specific parameters.

    -
  4. -
  5. -

    OpenAI Specific Guide

    +
  6. Replicate Specific Guide: Discover Replicate's distinctive offerings, including model versioning and custom deployment options.

    -
  7. + +

Advanced Usage Topics

    -
  1. Optimizing Performance: Tips and tricks for improving response time, reducing token usage, and enhancing overall efficiency.
  2. -

    Soon

    +

    Optimizing Performance: Tips and tricks for improving response time, reducing token usage, and enhancing overall efficiency.

    +
      +
    • Soon
    • +
  3. Handling Long Conversations: Strategies for managing context in extended dialogues and multi-turn interactions.

    -
  4. -
  5. -

    Soon

    +
      +
    • Soon
    • +
  6. Custom Prompting Techniques: Advanced prompting methods to extract more accurate and relevant responses from AI models.

    -
  7. -
  8. -

    Soon

    +
      +
    • Soon
    • +
  9. Error Handling and Retry Strategies: Best practices for gracefully managing API errors and implementing effective retry mechanisms.

    -
  10. -
  11. -

    Error Handling and Retry Strategies

    +
  12. Security and Privacy Considerations: Guidelines for ensuring data security and maintaining user privacy when working with AI APIs.

    -
  13. + +

Each guide in this section is designed to provide you with a deeper understanding of ClientAI's capabilities and how to leverage them effectively in your projects.

diff --git a/extending/index.html b/extending/index.html index 01083eb..d2dac1d 100644 --- a/extending/index.html +++ b/extending/index.html @@ -1943,12 +1943,15 @@

Extending ClientAI: Adding a N

Overview

To add a new provider, you'll need to:

    -
  1. Create a new directory for the provider
  2. -
  3. Implement the provider-specific types
  4. -
  5. Implement the provider class
  6. -
  7. Update the main ClientAI class
  8. -
  9. Update the package constants
  10. -
  11. Add tests for the new provider
  12. +
  13. Create a new directory for the provider
  14. +
  15. Implement the provider-specific types
  16. +
  17. Implement the provider class
  18. +
  19. Implement Unified Error Handling
  20. +
  21. Update the main ClientAI class
  22. +
  23. Update the package constants
  24. +
  25. Add tests for the new provider
  26. +
  27. Test Error Handling
  28. +
  29. Update Documentation

Let's go through each step in detail.

Step 1: Create a New Directory

@@ -2026,8 +2029,8 @@

Step 3: Implement the Provider Clas

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.

    -
  1. First, import the necessary error types:
  2. -
+
  • +

    First, import the necessary error types:

    # clientai/newai/provider.py
     
     from ..exceptions import (
    @@ -2040,9 +2043,9 @@ 

    Step 4: Implement Unified Error TimeoutError, )

    -
      -
    1. Implement the error mapping method in your provider class:
    2. -
    +
  • +
  • +

    Implement the error mapping method in your provider class:

    class Provider(AIProvider):
         ...
         def _map_exception_to_clientai_error(
    @@ -2102,6 +2105,8 @@ 

    Step 4: Implement Unified Error original_error=e )

    +
  • +

    Step 5: Update the Main ClientAI Class

    Update the clientai/client_ai.py file to include support for your new provider:

      @@ -2152,22 +2157,22 @@

      Step 5: Update the Main ClientAI

    Step 6: Update Package Constants and Dependencies

      -
    1. In the clientai/_constants.py file, add a constant for your new provider:
    2. -
    +
  • +

    In the clientai/_constants.py file, add a constant for your new provider:

    NEWAI_INSTALLED = find_spec("newai") is not None
     
    -
      -
    1. Update the clientai/__init__.py file to export the new constant:
    2. -
    +
  • +
  • +

    Update the clientai/__init__.py file to export the new constant:

    from ._constants import NEWAI_INSTALLED
     __all__ = [
         # ... existing exports ...
         "NEWAI_INSTALLED",
     ]
     
    -
      -
    1. Update the pyproject.toml file to include the new provider as an optional dependency:
    2. -
    +
  • +
  • +

    Update the pyproject.toml file to include the new provider as an optional dependency:

    [tool.poetry.dependencies]
     python = "^3.9"
     pydantic = "^2.9.2"
    @@ -2176,18 +2181,18 @@ 

    Step 6: Update Package ollama = {version = "^0.3.3", optional = true} newai-package = {version = "^1.0.0", optional = true} # Add this line

    -
      -
    1. Define an optional group for the new provider:
    2. -
    +
  • +
  • +

    Define an optional group for the new provider:

    [tool.poetry.group.newai]
     optional = true
     
     [tool.poetry.group.newai.dependencies]
     newai-package = "^1.0.0"
     
    -
      -
    1. Include the new provider in the development dependencies:
    2. -
    +
  • +
  • +

    Include the new provider in the development dependencies:

    [tool.poetry.group.dev.dependencies]
     ruff = "^0.6.8"
     pytest = "^8.3.3"
    @@ -2197,8 +2202,10 @@ 

    Step 6: Update Package ollama = "^0.3.3" newai-package = "^1.0.0" # Add this line

    -
      -
    1. Run poetry update to update the poetry.lock file with the new dependencies.
    2. + +
    3. +

      Run poetry update to update the poetry.lock file with the new dependencies.

      +

    These changes allow users to install the new provider's dependencies using Poetry:

    poetry install --with newai
    diff --git a/quick-start/index.html b/quick-start/index.html
    index bde41b3..fc105f6 100644
    --- a/quick-start/index.html
    +++ b/quick-start/index.html
    @@ -611,6 +611,15 @@
         
       
       
    +
  • + +
  • + + + Ollama Server Management + + +
  • @@ -1846,6 +1855,15 @@ + + +
  • + + + Ollama Server Management + + +
  • @@ -1963,6 +1981,30 @@

    Chat&par ) print(response) +

    Ollama Server Management

    +

    If you're running Ollama locally, ClientAI provides a convenient way to manage the Ollama server:

    +
    ollama_manager.py
    from clientai.ollama import OllamaManager
    +
    +# Start and automatically stop the server using a context manager
    +with OllamaManager() as manager:
    +    # Server is now running
    +    client = ClientAI('ollama')
    +    response = client.generate_text("Hello, world!", model="llama2")
    +    print(response)
    +
    +

    You can also configure basic server settings:

    +
    from clientai.ollama import OllamaManager, OllamaServerConfig
    +
    +config = OllamaServerConfig(
    +    host="127.0.0.1",
    +    port=11434,
    +    gpu_layers=35  # Optional: Number of layers to run on GPU
    +)
    +
    +with OllamaManager(config) as manager:
    +    # Your code here
    +    pass
    +

    Next Steps

    Now that you've seen the basics of ClientAI, you can:

      diff --git a/search/search_index.json b/search/search_index.json index bec58ef..85887d2 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"ClientAI","text":"

      A unified client for seamless interaction with multiple AI providers.

      ClientAI is a Python package that provides a unified interface for interacting with multiple AI providers, including OpenAI, Replicate, and Ollama. It offers seamless integration and consistent methods for text generation and chat functionality across different AI platforms.

      "},{"location":"#features","title":"Features","text":""},{"location":"#minimal-example","title":"Minimal Example","text":"

      Here's a quick example to get you started with ClientAI:

      from clientai import ClientAI\n\n# Initialize with OpenAI\nclient = ClientAI('openai', api_key=\"your-openai-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n)\n\nprint(response)\n\n# Chat functionality\nmessages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\n\nresponse = client.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n)\n\nprint(response)\n
      "},{"location":"#requirements","title":"Requirements","text":"

      Before installing ClientAI, ensure you have the following prerequisites:

      "},{"location":"#installing","title":"Installing","text":"

      To install ClientAI with all providers, run:

      pip install clientai[all]\n

      Or, if you prefer to install only specific providers:

      pip install clientai[openai]  # For OpenAI support\npip install clientai[replicate]  # For Replicate support\npip install clientai[ollama]  # For Ollama support\n
      "},{"location":"#usage","title":"Usage","text":"

      ClientAI offers a consistent way to interact with different AI providers:

      1. Initialize the client with your chosen provider and credentials.
      2. Use the generate_text method for text generation tasks.
      3. Use the chat method for conversational interactions.

      Both methods support streaming responses and returning full response objects.

      For more detailed usage examples and advanced features, please refer to the Usage section of this documentation.

      "},{"location":"#license","title":"License","text":"

      MIT

      "},{"location":"extending/","title":"Extending ClientAI: Adding a New Provider","text":"

      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.

      "},{"location":"extending/#overview","title":"Overview","text":"

      To add a new provider, you'll need to:

      1. Create a new directory for the provider
      2. Implement the provider-specific types
      3. Implement the provider class
      4. Update the main ClientAI class
      5. Update the package constants
      6. Add tests for the new provider

      Let's go through each step in detail.

      "},{"location":"extending/#step-1-create-a-new-directory","title":"Step 1: Create a New Directory","text":"

      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:

      clientai/\n    newai/\n        __init__.py\n        _typing.py\n        provider.py\n
      "},{"location":"extending/#step-2-implement-provider-specific-types","title":"Step 2: Implement Provider-Specific Types","text":"

      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\n\nfrom typing import Any, Dict, Iterator, Protocol, TypedDict, Union\nfrom .._common_types import GenericResponse\n\nclass NewAIResponse(TypedDict):\n    # Define the structure of a full response from NewAI\n    pass\n\nclass NewAIStreamResponse(TypedDict):\n    # Define the structure of a streaming response chunk from NewAI\n    pass\n\nclass NewAIClientProtocol(Protocol):\n    # Define the expected interface for the NewAI client\n    pass\n\nNewAIGenericResponse = GenericResponse[\n    str, NewAIResponse, NewAIStreamResponse\n]\n\n# Add any other necessary types\n
      "},{"location":"extending/#step-3-implement-the-provider-class","title":"Step 3: Implement the Provider Class","text":"

      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\n\nfrom ..ai_provider import AIProvider\nfrom ._typing import NewAIClientProtocol, NewAIGenericResponse\nfrom typing import List\nfrom .._common_types import Message\n\nclass Provider(AIProvider):\n    def __init__(self, api_key: str):\n        # Initialize the NewAI client\n        pass\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs\n    ) -> NewAIGenericResponse:\n        # Implement text generation logic\n        pass\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs\n    ) -> NewAIGenericResponse:\n        # Implement chat logic\n        pass\n\n    # Implement any helper methods as needed\n

      Make sure to handle both streaming and non-streaming responses, as well as the return_full_response option.

      "},{"location":"extending/#step-4-implement-unified-error-handling","title":"Step 4: Implement Unified Error Handling","text":"

      Before implementing the provider class, set up error handling for your provider. This ensures consistent error reporting across all providers.

      1. First, import the necessary error types:
      # clientai/newai/provider.py\n\nfrom ..exceptions import (\n    APIError,\n    AuthenticationError,\n    ClientAIError,\n    InvalidRequestError,\n    ModelError,\n    RateLimitError,\n    TimeoutError,\n)\n
      1. Implement the error mapping method in your provider class:
      class Provider(AIProvider):\n    ...\n    def _map_exception_to_clientai_error(\n        self,\n        e: Exception,\n        status_code: Optional[int] = None\n    ) -> ClientAIError:\n        \"\"\"\n        Maps NewAI-specific exceptions to ClientAI exceptions.\n\n        Args:\n            e: The caught exception\n            status_code: Optional HTTP status code\n\n        Returns:\n            ClientAIError: The appropriate ClientAI exception\n        \"\"\"\n        error_message = str(e)\n        status_code = status_code or getattr(e, \"status_code\", None)\n\n        # Map NewAI-specific exceptions to ClientAI exceptions\n        if isinstance(e, NewAIAuthError):\n            return AuthenticationError(\n                error_message,\n                status_code=401,\n                original_error=e\n            )\n        elif isinstance(e, NewAIRateLimitError):\n            return RateLimitError(\n                error_message,\n                status_code=429,\n                original_error=e\n            )\n        elif \"model not found\" in error_message.lower():\n            return ModelError(\n                error_message,\n                status_code=404,\n                original_error=e\n            )\n        elif isinstance(e, NewAIInvalidRequestError):\n            return InvalidRequestError(\n                error_message,\n                status_code=400,\n                original_error=e\n            )\n        elif isinstance(e, NewAITimeoutError):\n            return TimeoutError(\n                error_message,\n                status_code=408,\n                original_error=e\n            )\n\n        # Default to APIError for unknown errors\n        return APIError(\n            error_message,\n            status_code,\n            original_error=e\n        )\n
      "},{"location":"extending/#step-5-update-the-main-clientai-class","title":"Step 5: Update the Main ClientAI Class","text":"

      Update the clientai/client_ai.py file to include support for your new provider:

      1. Add an import for your new provider:

        from .newai import NEWAI_INSTALLED\n

      2. Update the __init__ method of the ClientAI class to handle the new provider:

         def __init__(self, provider_name: str, **kwargs):\n     prov_name = provider_name\n     # ----- add \"newai\" here -----\n     if prov_name not in [\"openai\", \"replicate\", \"ollama\", \"newai\"]:\n         raise ValueError(f\"Unsupported provider: {prov_name}\")\n\n     if (\n         prov_name == \"openai\"\n         and not OPENAI_INSTALLED\n         ...\n         # ----- also add \"newai\" here -----\n         or prov_name == \"newai\" \n         and not NEWAI_INSTALLED\n     ):\n         raise ImportError(\n             f\"The {prov_name} provider is not installed. \"\n             f\"Please install it with 'pip install clientai[{prov_name}]'.\"\n         )\n

      3. Add the new provider to the provider initialization logic:

        ...\ntry:\n    provider_module = import_module(\n        f\".{prov_name}.provider\", package=\"clientai\"\n    )\n    provider_class = getattr(provider_module, \"Provider\")\n\n    # ----- add \"newai\" here -----\n    if prov_name in [\"openai\", \"replicate\", \"newai\"]:\n        self.provider = cast(\n            P, provider_class(api_key=kwargs.get(\"api_key\"))\n        )\n    ...\n

      "},{"location":"extending/#step-6-update-package-constants-and-dependencies","title":"Step 6: Update Package Constants and Dependencies","text":"
      1. In the clientai/_constants.py file, add a constant for your new provider:
      NEWAI_INSTALLED = find_spec(\"newai\") is not None\n
      1. Update the clientai/__init__.py file to export the new constant:
      from ._constants import NEWAI_INSTALLED\n__all__ = [\n    # ... existing exports ...\n    \"NEWAI_INSTALLED\",\n]\n
      1. Update the pyproject.toml file to include the new provider as an optional dependency:
      [tool.poetry.dependencies]\npython = \"^3.9\"\npydantic = \"^2.9.2\"\nopenai = {version = \"^1.50.2\", optional = true}\nreplicate = {version = \"^0.34.1\", optional = true}\nollama = {version = \"^0.3.3\", optional = true}\nnewai-package = {version = \"^1.0.0\", optional = true}  # Add this line\n
      1. Define an optional group for the new provider:
      [tool.poetry.group.newai]\noptional = true\n\n[tool.poetry.group.newai.dependencies]\nnewai-package = \"^1.0.0\"\n
      1. Include the new provider in the development dependencies:
      [tool.poetry.group.dev.dependencies]\nruff = \"^0.6.8\"\npytest = \"^8.3.3\"\nmypy = \"1.9.0\"\nopenai = \"^1.50.2\"\nreplicate = \"^0.34.1\"\nollama = \"^0.3.3\"\nnewai-package = \"^1.0.0\"  # Add this line\n
      1. Run poetry update to update the poetry.lock file with the new dependencies.

      These changes allow users to install the new provider's dependencies using Poetry:

      poetry install --with newai\n

      If users are not using Poetry and are installing the package via pip, they can still use the extras syntax:

      pip install clientai[newai]\n
      "},{"location":"extending/#step-7-add-tests","title":"Step 7: Add Tests","text":"

      Create a new test file for your provider in the tests directory:

      tests/\n    newai/\n        __init__.py\n        test_provider.py\n

      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.

      "},{"location":"extending/#step-8-test-error-handling","title":"Step 8: Test Error Handling","text":"

      Also create a new test file to test your provider's exceptions in the tests directory:

      tests/\n    newai/\n        __init__.py\n        test_exceptions.py\n

      Add tests to ensure unified tests are being handled with a reference to the original error.

      "},{"location":"extending/#step-9-update-documentation","title":"Step 9: Update Documentation","text":"

      Don't forget to update the documentation to include information about the new provider:

      1. Add a new file docs/api/newai_provider.md with the following template for the NewAI provider.

        # NewAI Provider API Reference\n\nThe `NewAI` class implements the `AIProvider` interface for the NewAI service. It provides methods for text generation and chat functionality using NewAI's models.\n\n## Class Definition\n\n::: clientai.newai.Provider\n    rendering:\n      show_if_no_docstring: true\n

      2. Update docs/index.md to add a reference to the new provider.

      3. Update docs/quick-start.md to include an example of how to use the new provider.
      4. Update docs/api/overview.md to include a link to the new provider's documentation.
      "},{"location":"extending/#contribution-guidelines","title":"Contribution Guidelines","text":"

      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.

      "},{"location":"extending/#quick-summary-of-development-tools","title":"Quick Summary of Development Tools","text":"

      ClientAI uses the following tools for maintaining code quality:

      1. pytest: For running tests

        poetry run pytest\n

      2. mypy: For type checking

        mypy clientai\n

      3. ruff: For code linting and formatting

        ruff check --fix\nruff format\n

      Make sure to run these tools and address any issues before submitting your contribution.

      "},{"location":"extending/#conclusion","title":"Conclusion","text":"

      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!

      "},{"location":"installing/","title":"Installing","text":""},{"location":"installing/#requirements","title":"Requirements","text":"

      Before installing ClientAI, ensure you have the following prerequisites:

      "},{"location":"installing/#installing_1","title":"Installing","text":"

      ClientAI offers flexible installation options to suit your needs:

      "},{"location":"installing/#basic-installation","title":"Basic Installation","text":"

      To install the core ClientAI package without any provider-specific dependencies:

      pip install clientai\n

      Or, if using poetry:

      poetry add clientai\n
      "},{"location":"installing/#installation-with-specific-providers","title":"Installation with Specific Providers","text":"

      To install ClientAI with support for specific providers:

      pip install clientai[openai]  # For OpenAI support\npip install clientai[replicate]  # For Replicate support\npip install clientai[ollama]  # For Ollama support\n

      Or with poetry:

      poetry add clientai[openai]\npoetry add clientai[replicate]\npoetry add clientai[ollama]\n
      "},{"location":"installing/#full-installation","title":"Full Installation","text":"

      To install ClientAI with support for all providers:

      pip install clientai[all]\n

      Or with poetry:

      poetry add clientai[all]\n
      "},{"location":"quick-start/","title":"Quickstart","text":"

      This guide will help you get started with ClientAI quickly. We'll cover the basic setup and usage for each supported AI provider.

      "},{"location":"quick-start/#minimal-example","title":"Minimal Example","text":"

      Here's a minimal example to get you started with ClientAI:

      quickstart.py
      from clientai import ClientAI\n\n# Initialize the client (example with OpenAI)\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n)\nprint(response)\n\n# Use chat functionality\nmessages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital of France is Paris.\"},\n    {\"role\": \"user\", \"content\": \"What's its population?\"}\n]\nresponse = client.chat(messages, model=\"gpt-3.5-turbo\")\nprint(response)\n
      "},{"location":"quick-start/#setup-for-different-providers","title":"Setup for Different Providers","text":""},{"location":"quick-start/#openai","title":"OpenAI","text":"openai_setup.py
      from clientai import ClientAI\n\n# Initialize the OpenAI client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Now you can use the client for text generation or chat\n
      "},{"location":"quick-start/#replicate","title":"Replicate","text":"replicate_setup.py
      from clientai import ClientAI\n\n# Initialize the Replicate client\nclient = ClientAI('replicate', api_key=\"your-replicate-api-key\")\n\n# Now you can use the client for text generation or chat\n
      "},{"location":"quick-start/#ollama","title":"Ollama","text":"ollama_setup.py
      from clientai import ClientAI\n\n# Initialize the Ollama client\nclient = ClientAI('ollama', host=\"your-ollama-host\")\n\n# Now you can use the client for text generation or chat\n
      "},{"location":"quick-start/#basic-usage","title":"Basic Usage","text":"

      Once you have initialized the client, you can use it for text generation and chat functionality:

      "},{"location":"quick-start/#text-generation","title":"Text Generation","text":"text_generation.py
      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Explain the concept of quantum computing\",\n    model=\"gpt-3.5-turbo\",\n    max_tokens=100\n)\nprint(response)\n
      "},{"location":"quick-start/#chat","title":"Chat","text":"chat.py
      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Use chat functionality\nmessages = [\n    {\"role\": \"user\", \"content\": \"What is machine learning?\"},\n    {\"role\": \"assistant\", \"content\": \"Machine learning is a branch of artificial intelligence...\"},\n    {\"role\": \"user\", \"content\": \"Can you give an example of its application?\"}\n]\nresponse = client.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    max_tokens=150\n)\nprint(response)\n
      "},{"location":"quick-start/#next-steps","title":"Next Steps","text":"

      Now that you've seen the basics of ClientAI, you can:

      1. Explore more advanced features like streaming responses and handling full response objects.
      2. Check out the Usage Guide for detailed information on all available methods and options.
      3. See the API Reference for a complete list of ClientAI's classes and methods.

      Remember to handle API keys securely and never expose them in your code or version control systems.

      "},{"location":"advanced/error_handling/","title":"Error Handling and Retry Strategies","text":"

      This guide covers best practices for handling errors and implementing retry strategies when working with ClientAI. Learn how to gracefully handle API errors, implement effective retry mechanisms, and build robust AI applications.

      "},{"location":"advanced/error_handling/#table-of-contents","title":"Table of Contents","text":"
      1. Common Error Types
      2. Basic Error Handling
      3. Retry Strategies
      4. Advanced Error Handling Patterns
      5. Provider-Specific Considerations
      "},{"location":"advanced/error_handling/#common-error-types","title":"Common Error Types","text":"

      ClientAI provides a unified error hierarchy for all providers:

      from clientai.exceptions import (\n    ClientAIError,          # Base exception for all errors\n    AuthenticationError,    # API key or auth issues\n    RateLimitError,        # Rate limits exceeded\n    InvalidRequestError,    # Malformed requests\n    ModelError,            # Model-related issues\n    TimeoutError,          # Request timeouts\n    APIError              # General API errors\n)\n
      "},{"location":"advanced/error_handling/#basic-error-handling","title":"Basic Error Handling","text":""},{"location":"advanced/error_handling/#simple-try-except-pattern","title":"Simple Try-Except Pattern","text":"
      from clientai import ClientAI\nfrom clientai.exceptions import ClientAIError, RateLimitError\n\nclient = ClientAI(\"openai\", api_key=\"your-api-key\")\n\ntry:\n    response = client.generate_text(\n        prompt=\"Write a story\",\n        model=\"gpt-3.5-turbo\"\n    )\nexcept RateLimitError as e:\n    print(f\"Rate limit hit. Status code: {e.status_code}\")\n    print(f\"Original error: {e.original_error}\")\nexcept ClientAIError as e:\n    print(f\"Generation failed: {e}\")\n
      "},{"location":"advanced/error_handling/#retry-strategies","title":"Retry Strategies","text":""},{"location":"advanced/error_handling/#simple-retry-with-exponential-backoff","title":"Simple Retry with Exponential Backoff","text":"
      import time\nfrom typing import TypeVar, Callable\nfrom clientai.exceptions import RateLimitError, TimeoutError\n\nT = TypeVar('T')\n\ndef with_retry(\n    operation: Callable[[], T],\n    max_retries: int = 3,\n    initial_delay: float = 1.0,\n    exponential_base: float = 2.0,\n    max_delay: float = 60.0\n) -> T:\n    \"\"\"\n    Execute an operation with exponential backoff retry logic.\n\n    Args:\n        operation: Function to retry\n        max_retries: Maximum retry attempts\n        initial_delay: Initial delay between retries in seconds\n        exponential_base: Base for exponential backoff\n        max_delay: Maximum delay between retries\n    \"\"\"\n    last_exception = None\n\n    for attempt in range(max_retries):\n        try:\n            return operation()\n        except (RateLimitError, TimeoutError) as e:\n            last_exception = e\n            if attempt == max_retries - 1:\n                raise\n\n            delay = min(\n                initial_delay * (exponential_base ** attempt),\n                max_delay\n            )\n            time.sleep(delay)\n\n    raise last_exception or ClientAIError(\"Retry failed\")\n\n# Usage Example\ndef generate_text():\n    return client.generate_text(\n        prompt=\"Write a story\",\n        model=\"gpt-3.5-turbo\"\n    )\n\nresult = with_retry(generate_text)\n
      "},{"location":"advanced/error_handling/#provider-aware-retry-strategy","title":"Provider-Aware Retry Strategy","text":"
      from typing import Dict, Optional\n\nclass RetryConfig:\n    def __init__(\n        self,\n        max_retries: int = 3,\n        initial_delay: float = 1.0,\n        max_delay: float = 60.0,\n        retry_on: Optional[tuple] = None\n    ):\n        self.max_retries = max_retries\n        self.initial_delay = initial_delay\n        self.max_delay = max_delay\n        self.retry_on = retry_on or (RateLimitError, TimeoutError)\n\nPROVIDER_RETRY_CONFIGS = {\n    \"openai\": RetryConfig(max_retries=3, initial_delay=1.0),\n    \"anthropic\": RetryConfig(max_retries=5, initial_delay=2.0),\n    \"ollama\": RetryConfig(max_retries=2, initial_delay=0.5)\n}\n\ndef get_retry_config(provider: str) -> RetryConfig:\n    \"\"\"Get provider-specific retry configuration.\"\"\"\n    return PROVIDER_RETRY_CONFIGS.get(\n        provider,\n        RetryConfig()  # Default config\n    )\n
      "},{"location":"advanced/error_handling/#advanced-error-handling-patterns","title":"Advanced Error Handling Patterns","text":""},{"location":"advanced/error_handling/#circuit-breaker-pattern","title":"Circuit Breaker Pattern","text":"
      from typing import Optional\nfrom datetime import datetime, timedelta\n\nclass CircuitBreaker:\n    def __init__(\n        self,\n        failure_threshold: int = 5,\n        reset_timeout: int = 60\n    ):\n        self.failure_threshold = failure_threshold\n        self.reset_timeout = reset_timeout\n        self.failures = 0\n        self.last_failure_time: Optional[datetime] = None\n        self.is_open = False\n\n    def record_failure(self) -> None:\n        self.failures += 1\n        self.last_failure_time = datetime.now()\n        if self.failures >= self.failure_threshold:\n            self.is_open = True\n\n    def can_proceed(self) -> bool:\n        if not self.is_open:\n            return True\n\n        if self.last_failure_time and \\\n           datetime.now() - self.last_failure_time > timedelta(seconds=self.reset_timeout):\n            self.reset()\n            return True\n\n        return False\n\n    def reset(self) -> None:\n        self.failures = 0\n        self.is_open = False\n        self.last_failure_time = None\n\n# Usage\ncircuit_breaker = CircuitBreaker()\n\ndef generate_with_circuit_breaker(prompt: str, model: str) -> str:\n    if not circuit_breaker.can_proceed():\n        raise ClientAIError(\"Circuit breaker is open\")\n\n    try:\n        return client.generate_text(prompt, model=model)\n    except ClientAIError as e:\n        circuit_breaker.record_failure()\n        raise\n
      "},{"location":"advanced/error_handling/#fallback-chain-pattern","title":"Fallback Chain Pattern","text":"
      class FallbackChain:\n    def __init__(self, default_response: Optional[str] = None):\n        self.default_response = default_response\n        self.handlers: list = []\n\n    def add_handler(\n        self,\n        client: ClientAI,\n        model: str,\n        circuit_breaker: Optional[CircuitBreaker] = None\n    ):\n        self.handlers.append((client, model, circuit_breaker))\n        return self\n\n    def execute(self, prompt: str) -> str:\n        last_error = None\n\n        for client, model, circuit_breaker in self.handlers:\n            if circuit_breaker and not circuit_breaker.can_proceed():\n                continue\n\n            try:\n                return client.generate_text(prompt, model=model)\n            except ClientAIError as e:\n                if circuit_breaker:\n                    circuit_breaker.record_failure()\n                last_error = e\n\n        if self.default_response:\n            return self.default_response\n\n        raise last_error or ClientAIError(\"All handlers failed\")\n\n# Usage\nfallback_chain = FallbackChain(\"Sorry, service unavailable\")\nfallback_chain.add_handler(\n    ClientAI(\"openai\"), \"gpt-4\", CircuitBreaker()\n).add_handler(\n    ClientAI(\"anthropic\"), \"claude-2\", CircuitBreaker()\n)\n\nresponse = fallback_chain.execute(\"Write a story\")\n
      "},{"location":"advanced/error_handling/#provider-specific-considerations","title":"Provider-Specific Considerations","text":""},{"location":"advanced/error_handling/#openai","title":"OpenAI","text":""},{"location":"advanced/error_handling/#anthropic","title":"Anthropic","text":""},{"location":"advanced/error_handling/#ollama","title":"Ollama","text":""},{"location":"advanced/error_handling/#best-practices","title":"Best Practices","text":"
      1. Always Use Specific Exception Types

        try:\n    response = client.generate_text(prompt, model)\nexcept RateLimitError:\n    # Handle rate limits\nexcept ModelError:\n    # Handle model issues\nexcept ClientAIError:\n    # Handle other errors\n

      2. Implement Graceful Degradation

        def generate_with_fallback(prompt: str) -> str:\n    try:\n        return client.generate_text(\n            prompt, model=\"gpt-4\"\n        )\n    except (RateLimitError, ModelError):\n        return client.generate_text(\n            prompt, model=\"gpt-3.5-turbo\"\n        )\n    except ClientAIError:\n        return \"Service temporarily unavailable\"\n

      3. Use Appropriate Retry Strategies

        • Implement exponential backoff
        • Respect rate limits and retry-after headers
        • Set reasonable timeout values
        • Use circuit breakers for system protection
      4. Log Errors Appropriately

        import logging\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    response = client.generate_text(prompt, model)\nexcept ClientAIError as e:\n    logger.error(\n        \"Generation failed\",\n        extra={\n            \"status_code\": e.status_code,\n            \"error_type\": type(e).__name__,\n            \"original_error\": str(e.original_error)\n        }\n    )\n

      By following these error handling and retry strategies, you can build robust applications that gracefully handle failures and provide reliable service to your users.

      "},{"location":"advanced/ollama_specific/","title":"Ollama-Specific Parameters in ClientAI","text":"

      This guide covers the Ollama-specific parameters that can be passed to ClientAI's generate_text and chat methods. These parameters are passed as additional keyword arguments to customize Ollama's behavior.

      "},{"location":"advanced/ollama_specific/#generate_text-method","title":"generate_text Method","text":""},{"location":"advanced/ollama_specific/#basic-structure","title":"Basic Structure","text":"
      from clientai import ClientAI\n\nclient = ClientAI('ollama')\nresponse = client.generate_text(\n    prompt=\"Your prompt here\",    # Required\n    model=\"llama2\",              # Required\n    suffix=\"Optional suffix\",     # Ollama-specific\n    system=\"System message\",      # Ollama-specific\n    template=\"Custom template\",   # Ollama-specific\n    context=[1, 2, 3],           # Ollama-specific\n    format=\"json\",               # Ollama-specific\n    options={\"temperature\": 0.7}, # Ollama-specific\n    keep_alive=\"5m\"              # Ollama-specific\n)\n
      "},{"location":"advanced/ollama_specific/#ollama-specific-parameters","title":"Ollama-Specific Parameters","text":""},{"location":"advanced/ollama_specific/#suffix-str","title":"suffix: str","text":""},{"location":"advanced/ollama_specific/#system-str","title":"system: str","text":""},{"location":"advanced/ollama_specific/#template-str","title":"template: str","text":""},{"location":"advanced/ollama_specific/#context-listint","title":"context: List[int]","text":""},{"location":"advanced/ollama_specific/#format-literal-json","title":"format: Literal['', 'json']","text":""},{"location":"advanced/ollama_specific/#options-optionaloptions","title":"options: Optional[Options]","text":""},{"location":"advanced/ollama_specific/#keep_alive-optionalunionfloat-str","title":"keep_alive: Optional[Union[float, str]]","text":""},{"location":"advanced/ollama_specific/#chat-method","title":"chat Method","text":""},{"location":"advanced/ollama_specific/#basic-structure_1","title":"Basic Structure","text":"
      response = client.chat(\n    model=\"llama2\",              # Required\n    messages=[...],              # Required\n    tools=[...],                 # Ollama-specific\n    format=\"json\",               # Ollama-specific\n    options={\"temperature\": 0.7}, # Ollama-specific\n    keep_alive=\"5m\"              # Ollama-specific\n)\n
      "},{"location":"advanced/ollama_specific/#ollama-specific-parameters_1","title":"Ollama-Specific Parameters","text":""},{"location":"advanced/ollama_specific/#tools-optionallistdict","title":"tools: Optional[List[Dict]]","text":""},{"location":"advanced/ollama_specific/#format-literal-json_1","title":"format: Literal['', 'json']","text":""},{"location":"advanced/ollama_specific/#options-optionaloptions_1","title":"options: Optional[Options]","text":""},{"location":"advanced/ollama_specific/#keep_alive-optionalunionfloat-str_1","title":"keep_alive: Optional[Union[float, str]]","text":""},{"location":"advanced/ollama_specific/#complete-examples","title":"Complete Examples","text":""},{"location":"advanced/ollama_specific/#example-1-creative-writing-with-generate_text","title":"Example 1: Creative Writing with generate_text","text":"
      response = client.generate_text(\n    prompt=\"Write a short story about AI\",\n    model=\"llama2\",\n    system=\"You are a creative writer specializing in science fiction\",\n    template=\"Story prompt: {{.Prompt}}\\n\\nCreative story:\",\n    options={\n        \"temperature\": 0.9,\n        \"top_p\": 0.95\n    },\n    suffix=\"\\n\\nThe End.\",\n    keep_alive=\"10m\"\n)\n
      "},{"location":"advanced/ollama_specific/#example-2-json-response-with-chat","title":"Example 2: JSON Response with chat","text":"
      messages = [\n    {\"role\": \"system\", \"content\": \"You are a helpful assistant that provides structured data\"},\n    {\"role\": \"user\", \"content\": \"List 3 programming languages with their key features\"}\n]\n\nresponse = client.chat(\n    model=\"llama2\",\n    messages=messages,\n    format=\"json\",\n    options={\n        \"temperature\": 0.3,  # Lower temperature for more structured output\n        \"top_p\": 0.9\n    }\n)\n
      "},{"location":"advanced/ollama_specific/#example-3-multimodal-chat-with-image","title":"Example 3: Multimodal Chat with Image","text":"
      messages = [\n    {\n        \"role\": \"user\",\n        \"content\": \"What's in this image?\",\n        \"images\": [\"encoded_image_data_or_path\"]\n    }\n]\n\nresponse = client.chat(\n    model=\"llava\",\n    messages=messages,\n    format=\"json\",\n    keep_alive=\"5m\"\n)\n
      "},{"location":"advanced/ollama_specific/#example-4-contextual-generation","title":"Example 4: Contextual Generation","text":"
      # First generation\nfirst_response = client.generate_text(\n    prompt=\"Write the beginning of a mystery story\",\n    model=\"llama2\",\n    options={\"temperature\": 0.8}\n)\n\n# Continue the story using context\ncontinued_response = client.generate_text(\n    prompt=\"Continue the story with a plot twist\",\n    model=\"llama2\",\n    context=first_response.context,\n    options={\"temperature\": 0.8}\n)\n
      "},{"location":"advanced/ollama_specific/#parameter-validation-notes","title":"Parameter Validation Notes","text":"
      1. Both model and prompt/messages are required
      2. When using tools, stream must be False
      3. format only accepts '' or 'json'
      4. Image support requires multimodal models (e.g., llava)
      5. Context preservation works only with generate_text
      6. Keep alive duration can be string (e.g., \"5m\") or float (seconds)

      These parameters allow you to fully customize Ollama's behavior while working with ClientAI's abstraction layer.

      "},{"location":"advanced/openai_specific/","title":"OpenAI-Specific Parameters in ClientAI","text":"

      This guide covers the OpenAI-specific parameters that can be passed to ClientAI's generate_text and chat methods. These parameters are passed as additional keyword arguments to customize OpenAI's behavior.

      "},{"location":"advanced/openai_specific/#generate_text-method","title":"generate_text Method","text":""},{"location":"advanced/openai_specific/#basic-structure","title":"Basic Structure","text":"
      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\nresponse = client.generate_text(\n    prompt=\"Your prompt here\",          # Required\n    model=\"gpt-3.5-turbo\",             # Required\n    frequency_penalty=0.5,              # OpenAI-specific\n    presence_penalty=0.2,               # OpenAI-specific\n    logit_bias={123: 100},             # OpenAI-specific\n    max_completion_tokens=100,          # OpenAI-specific\n    response_format={\"type\": \"json\"},   # OpenAI-specific\n    seed=12345                         # OpenAI-specific\n)\n
      "},{"location":"advanced/openai_specific/#openai-specific-parameters","title":"OpenAI-Specific Parameters","text":""},{"location":"advanced/openai_specific/#frequency_penalty-optionalfloat","title":"frequency_penalty: Optional[float]","text":""},{"location":"advanced/openai_specific/#presence_penalty-optionalfloat","title":"presence_penalty: Optional[float]","text":""},{"location":"advanced/openai_specific/#logit_bias-optionaldictstr-int","title":"logit_bias: Optional[Dict[str, int]]","text":""},{"location":"advanced/openai_specific/#max_completion_tokens-optionalint","title":"max_completion_tokens: Optional[int]","text":""},{"location":"advanced/openai_specific/#response_format-responseformat","title":"response_format: ResponseFormat","text":""},{"location":"advanced/openai_specific/#seed-optionalint","title":"seed: Optional[int]","text":""},{"location":"advanced/openai_specific/#user-str","title":"user: str","text":""},{"location":"advanced/openai_specific/#chat-method","title":"chat Method","text":""},{"location":"advanced/openai_specific/#basic-structure_1","title":"Basic Structure","text":"
      response = client.chat(\n    model=\"gpt-3.5-turbo\",            # Required\n    messages=[...],                    # Required\n    tools=[...],                      # OpenAI-specific\n    tool_choice=\"auto\",               # OpenAI-specific\n    response_format={\"type\": \"json\"}, # OpenAI-specific\n    logprobs=True,                    # OpenAI-specific\n    top_logprobs=5                    # OpenAI-specific\n)\n
      "},{"location":"advanced/openai_specific/#openai-specific-parameters_1","title":"OpenAI-Specific Parameters","text":""},{"location":"advanced/openai_specific/#tools-iterablechatcompletiontoolparam","title":"tools: Iterable[ChatCompletionToolParam]","text":""},{"location":"advanced/openai_specific/#tool_choice-chatcompletiontoolchoiceoptionparam","title":"tool_choice: ChatCompletionToolChoiceOptionParam","text":""},{"location":"advanced/openai_specific/#modalities-optionallistchatcompletionmodality","title":"modalities: Optional[List[ChatCompletionModality]]","text":""},{"location":"advanced/openai_specific/#audio-optionalchatcompletionaudioparam","title":"audio: Optional[ChatCompletionAudioParam]","text":""},{"location":"advanced/openai_specific/#metadata-optionaldictstr-str","title":"metadata: Optional[Dict[str, str]]","text":""},{"location":"advanced/openai_specific/#complete-examples","title":"Complete Examples","text":""},{"location":"advanced/openai_specific/#example-1-structured-output-with-tools","title":"Example 1: Structured Output with Tools","text":"
      response = client.chat(\n    model=\"gpt-4\",\n    messages=[\n        {\"role\": \"system\", \"content\": \"You are a data assistant\"},\n        {\"role\": \"user\", \"content\": \"Get weather for Paris\"}\n    ],\n    response_format={\"type\": \"json_object\"},\n    tools=[{\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"get_weather\",\n            \"description\": \"Get weather data\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"location\": {\"type\": \"string\"}\n                }\n            }\n        }\n    }],\n    tool_choice=\"auto\"\n)\n
      "},{"location":"advanced/openai_specific/#example-2-advanced-text-generation","title":"Example 2: Advanced Text Generation","text":"
      response = client.generate_text(\n    prompt=\"Write a technical analysis\",\n    model=\"gpt-4\",\n    max_completion_tokens=500,\n    frequency_penalty=0.7,\n    presence_penalty=0.6,\n    logit_bias={123: 50},\n    user=\"analyst_1\",\n    seed=42\n)\n
      "},{"location":"advanced/openai_specific/#example-3-audio-generation","title":"Example 3: Audio Generation","text":"
      response = client.chat(\n    model=\"gpt-4o-audio-preview\",\n    messages=[{\"role\": \"user\", \"content\": \"Explain quantum physics\"}],\n    modalities=[\"text\", \"audio\"],\n    audio={\n        \"model\": \"tts-1\",\n        \"voice\": \"nova\",\n        \"speed\": 1.0\n    },\n    metadata={\"type\": \"educational\"}\n)\n
      "},{"location":"advanced/openai_specific/#parameter-validation-notes","title":"Parameter Validation Notes","text":"
      1. Both model and prompt/messages are required
      2. response_format requires compatible models
      3. Tool usage limited to 128 functions
      4. Audio generation requires specific models
      5. logprobs must be True when using top_logprobs
      6. seed feature is in Beta and not guaranteed

      These parameters allow you to fully customize OpenAI's behavior while working with ClientAI's abstraction layer.

      "},{"location":"advanced/overview/","title":"Advanced Overview","text":"

      This section provides in-depth guides on leveraging specific features of ClientAI and provider-specific functionalities. Each topic delves into a particular aspect of usage or focuses on a specific provider's unique capabilities.

      "},{"location":"advanced/overview/#provider-specific-parameters","title":"Provider-Specific Parameters","text":"

      Different AI providers offer unique parameters and features. Understanding these can help you fine-tune your AI interactions for optimal results.

      1. Ollama Specific Guide: Learn about Ollama's unique parameters, including context handling, streaming options, and custom templates.
      2. Ollama Specific Guide

      3. OpenAI Specific Guide: Explore OpenAI's advanced features, such as logit bias and model-specific parameters.

      4. OpenAI Specific Guide

      5. Replicate Specific Guide: Discover Replicate's distinctive offerings, including model versioning and custom deployment options.

      6. Replicate Specific Guide
      "},{"location":"advanced/overview/#advanced-usage-topics","title":"Advanced Usage Topics","text":"
      1. Optimizing Performance: Tips and tricks for improving response time, reducing token usage, and enhancing overall efficiency.
      2. Soon

      3. Handling Long Conversations: Strategies for managing context in extended dialogues and multi-turn interactions.

      4. Soon

      5. Custom Prompting Techniques: Advanced prompting methods to extract more accurate and relevant responses from AI models.

      6. Soon

      7. Error Handling and Retry Strategies: Best practices for gracefully managing API errors and implementing effective retry mechanisms.

      8. Error Handling and Retry Strategies

      9. Security and Privacy Considerations: Guidelines for ensuring data security and maintaining user privacy when working with AI APIs.

      10. Soon

      Each guide in this section is designed to provide you with a deeper understanding of ClientAI's capabilities and how to leverage them effectively in your projects.

      "},{"location":"advanced/replicate_specific/","title":"Replicate-Specific Parameters in ClientAI","text":"

      This guide covers the Replicate-specific parameters that can be passed to ClientAI's generate_text and chat methods. These parameters are passed as additional keyword arguments to customize Replicate's behavior.

      "},{"location":"advanced/replicate_specific/#generate_text-method","title":"generate_text Method","text":""},{"location":"advanced/replicate_specific/#basic-structure","title":"Basic Structure","text":"
      from clientai import ClientAI\n\nclient = ClientAI('replicate', api_key=\"your-replicate-api-key\")\nresponse = client.generate_text(\n    prompt=\"Your prompt here\",     # Required\n    model=\"owner/name:version\",    # Required\n    webhook=\"https://...\",         # Replicate-specific\n    webhook_completed=\"https://...\",# Replicate-specific\n    webhook_events_filter=[...],   # Replicate-specific\n    stream=False,                  # Optional\n    wait=True                      # Replicate-specific\n)\n
      "},{"location":"advanced/replicate_specific/#replicate-specific-parameters","title":"Replicate-Specific Parameters","text":""},{"location":"advanced/replicate_specific/#webhook-optionalstr","title":"webhook: Optional[str]","text":""},{"location":"advanced/replicate_specific/#webhook_completed-optionalstr","title":"webhook_completed: Optional[str]","text":""},{"location":"advanced/replicate_specific/#webhook_events_filter-optionalliststr","title":"webhook_events_filter: Optional[List[str]]","text":""},{"location":"advanced/replicate_specific/#wait-optionalunionint-bool","title":"wait: Optional[Union[int, bool]]","text":""},{"location":"advanced/replicate_specific/#stream-bool","title":"stream: bool","text":""},{"location":"advanced/replicate_specific/#chat-method","title":"chat Method","text":""},{"location":"advanced/replicate_specific/#basic-structure_1","title":"Basic Structure","text":"
      response = client.chat(\n    model=\"meta/llama-2-70b:latest\",  # Required\n    messages=[...],                    # Required\n    webhook=\"https://...\",             # Replicate-specific\n    webhook_completed=\"https://...\",   # Replicate-specific\n    webhook_events_filter=[...],       # Replicate-specific\n    wait=True                          # Replicate-specific\n)\n
      "},{"location":"advanced/replicate_specific/#message-formatting","title":"Message Formatting","text":"

      Replicate formats chat messages into a single prompt:

      prompt = \"\\n\".join([f\"{m['role']}: {m['content']}\" for m in messages])\nprompt += \"\\nassistant: \"\n

      "},{"location":"advanced/replicate_specific/#training-parameters","title":"Training Parameters","text":"

      When using Replicate's training capabilities:

      response = client.train(\n    model=\"stability-ai/sdxl\",\n    version=\"39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b\",\n    input={\n        \"input_images\": \"https://domain/images.zip\",\n        \"token_string\": \"TOK\",\n        \"caption_prefix\": \"a photo of TOK\",\n        \"max_train_steps\": 1000,\n        \"use_face_detection_instead\": False\n    },\n    destination=\"username/model-name\"\n)\n
      "},{"location":"advanced/replicate_specific/#complete-examples","title":"Complete Examples","text":""},{"location":"advanced/replicate_specific/#example-1-generation-with-webhooks","title":"Example 1: Generation with Webhooks","text":"
      response = client.generate_text(\n    prompt=\"Write a scientific paper summary\",\n    model=\"meta/llama-2-70b:latest\",\n    webhook=\"https://your-server.com/updates\",\n    webhook_completed=\"https://your-server.com/completed\",\n    webhook_events_filter=[\"completed\"],\n    wait=True\n)\n
      "},{"location":"advanced/replicate_specific/#example-2-chat-with-streaming","title":"Example 2: Chat with Streaming","text":"
      messages = [\n    {\"role\": \"system\", \"content\": \"You are a helpful assistant\"},\n    {\"role\": \"user\", \"content\": \"Write a haiku about coding\"}\n]\n\nfor chunk in client.chat(\n    messages=messages,\n    model=\"meta/llama-2-70b:latest\",\n    stream=True\n):\n    print(chunk, end=\"\")\n
      "},{"location":"advanced/replicate_specific/#example-3-image-generation","title":"Example 3: Image Generation","text":"
      response = client.generate_text(\n    prompt=\"A portrait of a wombat gentleman\",\n    model=\"stability-ai/stable-diffusion:27b93a2413e7f36cd83da926f3656280b2931564ff050bf9575f1fdf9bcd7478\",\n    wait=60\n)\n
      "},{"location":"advanced/replicate_specific/#error-handling","title":"Error Handling","text":"

      ClientAI maps Replicate's exceptions to its own error types:

      try:\n    response = client.generate_text(\n        prompt=\"Test prompt\",\n        model=\"meta/llama-2-70b:latest\",\n        wait=True\n    )\nexcept ClientAIError as e:\n    print(f\"Error: {e}\")\n

      Error mappings: - AuthenticationError: API key issues - RateLimitError: Rate limit exceeded - ModelError: Model not found or failed - InvalidRequestError: Invalid parameters - TimeoutError: Request timeout (default 300s) - APIError: Other server errors

      "},{"location":"advanced/replicate_specific/#parameter-validation-notes","title":"Parameter Validation Notes","text":"
      1. Both model and prompt/messages are required
      2. Model string format: \"owner/name:version\" or \"owner/name\" for latest version
      3. wait must be boolean or integer 1-60
      4. Webhook URLs must be valid HTTP/HTTPS URLs
      5. webhook_events_filter must contain valid event types
      6. Some models may not support streaming
      7. File inputs can be URLs or local file paths

      These parameters allow you to leverage Replicate's features through ClientAI, including model management, webhook notifications, and streaming capabilities.

      "},{"location":"api/ai_provider/","title":"AIProvider Class API Reference","text":"

      The AIProvider class is an abstract base class that defines the interface for all AI provider implementations in ClientAI. It ensures consistency across different providers.

      "},{"location":"api/ai_provider/#class-definition","title":"Class Definition","text":"

      Bases: ABC

      Abstract base class for AI providers.

      Source code in clientai/ai_provider.py
      class AIProvider(ABC):\n    \"\"\"\n    Abstract base class for AI providers.\n    \"\"\"\n\n    @abstractmethod\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> GenericResponse:\n        \"\"\"\n        Generate text based on a given prompt.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, return the full response object\n                                  instead of just the generated text.\n            stream: If True, return an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the provider's API.\n\n        Returns:\n            GenericResponse:\n                The generated text response, full response object,\n                or an iterator for streaming responses.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> GenericResponse:\n        \"\"\"\n        Engage in a chat conversation.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, return the full response object\n                                  instead of just the chat content.\n            stream: If True, return an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the provider's API.\n\n        Returns:\n            GenericResponse:\n                The chat response, either as a string, a dictionary,\n                or an iterator for streaming responses.\n        \"\"\"\n        pass\n
      "},{"location":"api/ai_provider/#clientai.ai_provider.AIProvider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs) abstractmethod","text":"

      Engage in a chat conversation.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, return the full response object instead of just the chat content.

      False stream bool

      If True, return an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the provider's API.

      {}

      Returns:

      Name Type Description GenericResponse GenericResponse

      The chat response, either as a string, a dictionary, or an iterator for streaming responses.

      Source code in clientai/ai_provider.py
      @abstractmethod\ndef chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> GenericResponse:\n    \"\"\"\n    Engage in a chat conversation.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, return the full response object\n                              instead of just the chat content.\n        stream: If True, return an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the provider's API.\n\n    Returns:\n        GenericResponse:\n            The chat response, either as a string, a dictionary,\n            or an iterator for streaming responses.\n    \"\"\"\n    pass\n
      "},{"location":"api/ai_provider/#clientai.ai_provider.AIProvider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs) abstractmethod","text":"

      Generate text based on a given prompt.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, return the full response object instead of just the generated text.

      False stream bool

      If True, return an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the provider's API.

      {}

      Returns:

      Name Type Description GenericResponse GenericResponse

      The generated text response, full response object, or an iterator for streaming responses.

      Source code in clientai/ai_provider.py
      @abstractmethod\ndef generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> GenericResponse:\n    \"\"\"\n    Generate text based on a given prompt.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, return the full response object\n                              instead of just the generated text.\n        stream: If True, return an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the provider's API.\n\n    Returns:\n        GenericResponse:\n            The generated text response, full response object,\n            or an iterator for streaming responses.\n    \"\"\"\n    pass\n
      "},{"location":"api/clientai/","title":"ClientAI Class API Reference","text":"

      The ClientAI class is the primary interface for interacting with various AI providers in a unified manner. It provides methods for text generation and chat functionality across different AI services.

      "},{"location":"api/clientai/#class-definition","title":"Class Definition","text":"

      Bases: Generic[P, T, S]

      A unified client for interacting with a single AI provider (OpenAI, Replicate, or Ollama).

      This class provides a consistent interface for common AI operations such as text generation and chat for the chosen AI provider.

      Type Parameters: P: The type of the AI provider. T: The type of the full response for non-streaming operations. S: The type of each chunk in streaming operations.

      Attributes:

      Name Type Description provider

      The initialized AI provider.

      Parameters:

      Name Type Description Default provider_name str

      The name of the AI provider to use ('openai', 'replicate', or 'ollama').

      required **kwargs Any

      Provider-specific initialization parameters.

      {}

      Raises:

      Type Description ValueError

      If an unsupported provider name is given.

      ImportError

      If the specified provider is not installed.

      Examples:

      Initialize with OpenAI:

      ai = ClientAI('openai', api_key=\"your-openai-key\")\n

      Initialize with Replicate:

      ai = ClientAI('replicate', api_key=\"your-replicate-key\")\n

      Initialize with Ollama:

      ai = ClientAI('ollama', host=\"your-ollama-host\")\n

      Source code in clientai/client_ai.py
      class ClientAI(Generic[P, T, S]):\n    \"\"\"\n    A unified client for interacting with a single AI provider\n    (OpenAI, Replicate, or Ollama).\n\n    This class provides a consistent interface for common\n    AI operations such as text generation and chat\n    for the chosen AI provider.\n\n    Type Parameters:\n    P: The type of the AI provider.\n    T: The type of the full response for non-streaming operations.\n    S: The type of each chunk in streaming operations.\n\n    Attributes:\n        provider: The initialized AI provider.\n\n    Args:\n        provider_name: The name of the AI provider to use\n                       ('openai', 'replicate', or 'ollama').\n        **kwargs (Any): Provider-specific initialization parameters.\n\n    Raises:\n        ValueError: If an unsupported provider name is given.\n        ImportError: If the specified provider is not installed.\n\n    Examples:\n        Initialize with OpenAI:\n        ```python\n        ai = ClientAI('openai', api_key=\"your-openai-key\")\n        ```\n\n        Initialize with Replicate:\n        ```python\n        ai = ClientAI('replicate', api_key=\"your-replicate-key\")\n        ```\n\n        Initialize with Ollama:\n        ```python\n        ai = ClientAI('ollama', host=\"your-ollama-host\")\n        ```\n    \"\"\"\n\n    def __init__(self, provider_name: str, **kwargs):\n        prov_name = provider_name\n        if prov_name not in [\"openai\", \"replicate\", \"ollama\"]:\n            raise ValueError(f\"Unsupported provider: {prov_name}\")\n\n        if (\n            prov_name == \"openai\"\n            and not OPENAI_INSTALLED\n            or prov_name == \"replicate\"\n            and not REPLICATE_INSTALLED\n            or prov_name == \"ollama\"\n            and not OLLAMA_INSTALLED\n        ):\n            raise ImportError(\n                f\"The {prov_name} provider is not installed. \"\n                f\"Please install it with 'pip install clientai[{prov_name}]'.\"\n            )\n\n        try:\n            provider_module = import_module(\n                f\".{prov_name}.provider\", package=\"clientai\"\n            )\n            provider_class = getattr(provider_module, \"Provider\")\n            if prov_name in [\"openai\", \"replicate\"]:\n                self.provider = cast(\n                    P, provider_class(api_key=kwargs.get(\"api_key\"))\n                )\n            elif prov_name == \"ollama\":\n                self.provider = cast(\n                    P, provider_class(host=kwargs.get(\"host\"))\n                )\n        except ImportError as e:\n            raise ImportError(\n                f\"Error importing {prov_name} provider module: {str(e)}\"\n            ) from e\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> AIGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt\n        using the specified AI model and provider.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, returns the full structured response\n                                  If False, returns only the generated text.\n            stream: If True, returns an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the chosen provider's API.\n\n        Returns:\n            AIGenericResponse:\n                The generated text response, full response structure,\n                or an iterator for streaming responses.\n\n        Examples:\n            Generate text using OpenAI (text only):\n            ```python\n            response = ai.generate_text(\n                \"Tell me a joke\",\n                model=\"gpt-3.5-turbo\",\n            )\n            ```\n\n            Generate text using OpenAI (full response):\n            ```python\n            response = ai.generate_text(\n                \"Tell me a joke\",\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            ```\n\n            Generate text using OpenAI (streaming):\n            ```python\n            for chunk in ai.generate_text(\n                \"Tell me a joke\",\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n\n            Generate text using Replicate:\n            ```python\n            response = ai.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            ```\n\n            Generate text using Ollama:\n            ```python\n            response = ai.generate_text(\n                \"What is the capital of France?\",\n                model=\"llama2\",\n            )\n            ```\n        \"\"\"\n        return self.provider.generate_text(\n            prompt,\n            model,\n            return_full_response=return_full_response,\n            stream=stream,\n            **kwargs,\n        )\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> AIGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using\n        the specified AI model and provider.\n\n        Args:\n            messages: A list of message dictionaries, each\n                      containing 'role' and 'content'.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, returns the full structured response\n                                  If False, returns the assistant's message.\n            stream: If True, returns an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the chosen provider's API.\n\n        Returns:\n            AIGenericResponse:\n                The chat response, full response structure,\n                or an iterator for streaming responses.\n\n        Examples:\n            Chat using OpenAI (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n                {\"role\": \"assistant\", \"content\": \"Paris.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = ai.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n            )\n            ```\n\n            Chat using OpenAI (full response):\n            ```python\n            response = ai.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            ```\n\n            Chat using OpenAI (streaming):\n            ```python\n            for chunk in ai.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n\n            Chat using Replicate:\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"Explain the concept of AI.\"}\n            ]\n            response = ai.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            ```\n\n            Chat using Ollama:\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What are the laws of robotics?\"}\n            ]\n            response = ai.chat(messages, model=\"llama2\")\n            ```\n        \"\"\"\n        return self.provider.chat(\n            messages,\n            model,\n            return_full_response=return_full_response,\n            stream=stream,\n            **kwargs,\n        )\n
      "},{"location":"api/clientai/#clientai.ClientAI.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using the specified AI model and provider.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, returns the full structured response If False, returns the assistant's message.

      False stream bool

      If True, returns an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the chosen provider's API.

      {}

      Returns:

      Name Type Description AIGenericResponse AIGenericResponse

      The chat response, full response structure, or an iterator for streaming responses.

      Examples:

      Chat using OpenAI (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = ai.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n)\n

      Chat using OpenAI (full response):

      response = ai.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\n

      Chat using OpenAI (streaming):

      for chunk in ai.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Chat using Replicate:

      messages = [\n    {\"role\": \"user\", \"content\": \"Explain the concept of AI.\"}\n]\nresponse = ai.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n)\n

      Chat using Ollama:

      messages = [\n    {\"role\": \"user\", \"content\": \"What are the laws of robotics?\"}\n]\nresponse = ai.chat(messages, model=\"llama2\")\n

      Source code in clientai/client_ai.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> AIGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using\n    the specified AI model and provider.\n\n    Args:\n        messages: A list of message dictionaries, each\n                  containing 'role' and 'content'.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, returns the full structured response\n                              If False, returns the assistant's message.\n        stream: If True, returns an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the chosen provider's API.\n\n    Returns:\n        AIGenericResponse:\n            The chat response, full response structure,\n            or an iterator for streaming responses.\n\n    Examples:\n        Chat using OpenAI (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n            {\"role\": \"assistant\", \"content\": \"Paris.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = ai.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n        )\n        ```\n\n        Chat using OpenAI (full response):\n        ```python\n        response = ai.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        ```\n\n        Chat using OpenAI (streaming):\n        ```python\n        for chunk in ai.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n\n        Chat using Replicate:\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"Explain the concept of AI.\"}\n        ]\n        response = ai.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        ```\n\n        Chat using Ollama:\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What are the laws of robotics?\"}\n        ]\n        response = ai.chat(messages, model=\"llama2\")\n        ```\n    \"\"\"\n    return self.provider.chat(\n        messages,\n        model,\n        return_full_response=return_full_response,\n        stream=stream,\n        **kwargs,\n    )\n
      "},{"location":"api/clientai/#clientai.ClientAI.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using the specified AI model and provider.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, returns the full structured response If False, returns only the generated text.

      False stream bool

      If True, returns an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the chosen provider's API.

      {}

      Returns:

      Name Type Description AIGenericResponse AIGenericResponse

      The generated text response, full response structure, or an iterator for streaming responses.

      Examples:

      Generate text using OpenAI (text only):

      response = ai.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n)\n

      Generate text using OpenAI (full response):

      response = ai.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\n

      Generate text using OpenAI (streaming):

      for chunk in ai.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Generate text using Replicate:

      response = ai.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n)\n

      Generate text using Ollama:

      response = ai.generate_text(\n    \"What is the capital of France?\",\n    model=\"llama2\",\n)\n

      Source code in clientai/client_ai.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> AIGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt\n    using the specified AI model and provider.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, returns the full structured response\n                              If False, returns only the generated text.\n        stream: If True, returns an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the chosen provider's API.\n\n    Returns:\n        AIGenericResponse:\n            The generated text response, full response structure,\n            or an iterator for streaming responses.\n\n    Examples:\n        Generate text using OpenAI (text only):\n        ```python\n        response = ai.generate_text(\n            \"Tell me a joke\",\n            model=\"gpt-3.5-turbo\",\n        )\n        ```\n\n        Generate text using OpenAI (full response):\n        ```python\n        response = ai.generate_text(\n            \"Tell me a joke\",\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        ```\n\n        Generate text using OpenAI (streaming):\n        ```python\n        for chunk in ai.generate_text(\n            \"Tell me a joke\",\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n\n        Generate text using Replicate:\n        ```python\n        response = ai.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        ```\n\n        Generate text using Ollama:\n        ```python\n        response = ai.generate_text(\n            \"What is the capital of France?\",\n            model=\"llama2\",\n        )\n        ```\n    \"\"\"\n    return self.provider.generate_text(\n        prompt,\n        model,\n        return_full_response=return_full_response,\n        stream=stream,\n        **kwargs,\n    )\n
      "},{"location":"api/overview/","title":"API Reference Overview","text":"

      Welcome to the API Reference section of ClientAI documentation. This section provides detailed information about the various classes, functions, and modules that make up ClientAI. Whether you're looking to integrate ClientAI into your project, extend its functionality, or simply explore its capabilities, this section will guide you through the intricacies of our codebase.

      "},{"location":"api/overview/#key-components","title":"Key Components","text":"

      ClientAI's API is comprised of several key components, each serving a specific purpose:

      1. ClientAI Class: This is the main class of our library. It provides a unified interface for interacting with different AI providers and is the primary entry point for using ClientAI.

        • ClientAI Class Reference
      2. AIProvider Class: An abstract base class that defines the interface for all AI provider implementations. It ensures consistency across different providers.

        • AIProvider Class Reference
      3. Provider-Specific Classes: These classes implement the AIProvider interface for each supported AI service (OpenAI, Replicate, Ollama).

        • OpenAI Provider Reference
        • Replicate Provider Reference
        • Ollama Provider Reference
      4. Ollama Manager: These classes handle the local Ollama server configuration and lifecycle management.

        • OllamaManager Class Reference
        • OllamaServerConfig Class Reference
      "},{"location":"api/overview/#usage","title":"Usage","text":"

      Each component is documented with its own dedicated page, where you can find detailed information about its methods, parameters, return types, and usage examples. These pages are designed to provide you with all the information you need to understand and work with ClientAI effectively.

      "},{"location":"api/overview/#basic-usage-example","title":"Basic Usage Example","text":"

      Here's a quick example of how to use the main ClientAI class:

      from clientai import ClientAI\n\n# Initialize the client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Explain quantum computing\",\n    model=\"gpt-3.5-turbo\"\n)\n\nprint(response)\n

      For more detailed usage instructions and examples, please refer to the Usage Guide (\ud83d\udea7 Under Construction, come back soon \ud83d\udea7).

      "},{"location":"api/overview/#extending-clientai","title":"Extending ClientAI","text":"

      If you wish to add support for a new AI provider or extend the functionality of existing providers, you can do so by implementing the AIProvider interface. See the Extending ClientAI Guide for more information.

      "},{"location":"api/overview/#contribution","title":"Contribution","text":"

      We welcome contributions to ClientAI! If you're interested in contributing, please refer to our Contributing Guidelines. Contributions can range from bug fixes and documentation improvements to adding support for new AI providers.

      "},{"location":"api/overview/#feedback","title":"Feedback","text":"

      Your feedback is crucial in helping us improve ClientAI and its documentation. If you have any suggestions, corrections, or queries, please don't hesitate to reach out to us via GitHub issues or our community channels.

      Navigate through each section for detailed documentation of ClientAI's API components.

      "},{"location":"api/ollama_manager/ollama_manager/","title":"OllamaManager Class API Reference","text":"

      The OllamaManager class is a utility class that manages the lifecycle of a local Ollama server instance. It handles server process startup, monitoring, and shutdown while respecting platform-specific requirements and custom configurations. The manager supports configurable GPU acceleration, CPU thread allocation, and memory limits through OllamaServerConfig. It provides both context manager and manual management interfaces for controlling the server process.

      "},{"location":"api/ollama_manager/ollama_manager/#class-definition","title":"Class Definition","text":"

      Manages the Ollama server process and configuration.

      This class provides methods to start, stop, and manage the lifecycle of an Ollama server instance with configurable parameters.

      Attributes:

      Name Type Description config

      The server configuration used by the manager.

      _process Optional[Popen[str]]

      The underlying server process.

      _platform_info

      Information about the current platform.

      Parameters:

      Name Type Description Default config Optional[OllamaServerConfig]

      Optional server configuration. If None, uses defaults.

      None

      Raises:

      Type Description ImportError

      If required system dependencies are not installed.

      Examples:

      Basic usage with defaults:

      with OllamaManager() as manager:\n    # Server is running with default configuration\n    pass  # Server automatically stops when exiting context\n

      Custom configuration:

      config = OllamaServerConfig(\n    gpu_layers=35,\n    gpu_memory_fraction=0.8,\n    cpu_threads=8\n)\nmanager = OllamaManager(config)\nmanager.start()\n# ... use the server ...\nmanager.stop()\n

      Source code in clientai/ollama/manager/core.py
      class OllamaManager:\n    \"\"\"\n    Manages the Ollama server process and configuration.\n\n    This class provides methods to start, stop, and manage the lifecycle\n    of an Ollama server instance with configurable parameters.\n\n    Attributes:\n        config: The server configuration used by the manager.\n        _process: The underlying server process.\n        _platform_info: Information about the current platform.\n\n    Args:\n        config: Optional server configuration. If None, uses defaults.\n\n    Raises:\n        ImportError: If required system dependencies are not installed.\n\n    Examples:\n        Basic usage with defaults:\n        ```python\n        with OllamaManager() as manager:\n            # Server is running with default configuration\n            pass  # Server automatically stops when exiting context\n        ```\n\n        Custom configuration:\n        ```python\n        config = OllamaServerConfig(\n            gpu_layers=35,\n            gpu_memory_fraction=0.8,\n            cpu_threads=8\n        )\n        manager = OllamaManager(config)\n        manager.start()\n        # ... use the server ...\n        manager.stop()\n        ```\n    \"\"\"\n\n    def __init__(self, config: Optional[OllamaServerConfig] = None) -> None:\n        \"\"\"\n        Initialize the Ollama manager.\n\n        Args:\n            config: Optional server configuration. If None, uses defaults.\n        \"\"\"\n        self.config = config or OllamaServerConfig()\n        self._process: Optional[subprocess.Popen[str]] = None\n        self._platform_info = PlatformInfo()\n\n    def start(self) -> None:\n        \"\"\"\n        Start the Ollama server using the configured parameters.\n\n        This method initializes and starts the Ollama server process,\n        waiting for it to become healthy before returning.\n\n        Raises:\n            ServerStartupError: If the server fails to start\n            ServerTimeoutError: If the server doesn't respond within timeout\n            ExecutableNotFoundError: If the Ollama executable is not found\n            ResourceError: If there are insufficient system resources\n\n        Examples:\n            Start with default configuration:\n            ```python\n            manager = OllamaManager()\n            manager.start()\n            ```\n\n            Start with custom configuration:\n            ```python\n            config = OllamaServerConfig(gpu_layers=35)\n            manager = OllamaManager(config)\n            manager.start()\n            ```\n        \"\"\"\n        if self._process is not None:\n            raise ServerStartupError(\"Ollama server is already running\")\n\n        logging.info(\n            f\"Starting Ollama server on {self.config.host}:{self.config.port}\"\n        )\n\n        try:\n            popen_kwargs: Dict[str, Any] = {\n                \"stdout\": subprocess.PIPE,\n                \"stderr\": subprocess.PIPE,\n                \"text\": True,\n                \"env\": self._platform_info.get_environment(self.config),\n            }\n\n            if self._platform_info.platform == Platform.WINDOWS:\n                if sys.platform == \"win32\":\n                    popen_kwargs[\"creationflags\"] = subprocess.CREATE_NO_WINDOW\n\n            self._process = subprocess.Popen(\n                self._platform_info.get_server_command(self.config),\n                **popen_kwargs,\n            )\n\n        except FileNotFoundError as e:\n            raise_ollama_error(\n                ExecutableNotFoundError,\n                \"Ollama executable not found. Ensure Ollama is installed.\",\n                e,\n            )\n        except MemoryError as e:\n            raise_ollama_error(\n                ResourceError, \"Insufficient memory to start Ollama server\", e\n            )\n        except Exception as e:\n            raise_ollama_error(\n                ServerStartupError,\n                f\"Failed to start Ollama server: {str(e)}\",\n                e,\n            )\n\n        try:\n            self._wait_for_server()\n        except Exception as e:\n            self.stop()\n            raise e\n\n    def stop(self) -> None:\n        \"\"\"\n        Stop the running Ollama server instance.\n\n        This method terminates the Ollama server process if it's running.\n        It will wait for the process to complete before returning.\n\n        Examples:\n            Stop a running server:\n            ```python\n            manager = OllamaManager()\n            manager.start()\n            # ... use the server ...\n            manager.stop()\n            ```\n\n            Using context manager (automatic stop):\n            ```python\n            with OllamaManager() as manager:\n                # ... use the server ...\n                pass  # Server stops automatically\n            ```\n        \"\"\"\n        if self._process is not None:\n            try:\n                self._process.terminate()\n                self._process.wait()\n            finally:\n                self._process = None\n                logging.info(\"Ollama server stopped\")\n\n    def _check_server_health(self) -> bool:\n        \"\"\"\n        Check if the server is responding to health checks.\n\n        This method attempts to connect to the server and verify\n        its health status.\n\n        Returns:\n            bool: True if server is healthy, False otherwise\n\n        Note:\n            This is an internal method used by the manager to verify\n            server status during startup.\n        \"\"\"\n        try:\n            url = urlparse(self.config.base_url)\n            conn = http.client.HTTPConnection(\n                url.hostname or self.config.host,\n                url.port or self.config.port,\n                timeout=5,\n            )\n            try:\n                conn.request(\"GET\", \"/\")\n                response = conn.getresponse()\n                return response.status == 200\n            finally:\n                conn.close()\n        except (http.client.HTTPException, ConnectionRefusedError, OSError):\n            return False\n\n    def _wait_for_server(self) -> None:\n        \"\"\"\n        Wait for the server to become ready and responsive.\n\n        This method polls the server until it responds successfully or\n        times out. It checks both process health and server responsiveness.\n\n        Raises:\n            ServerStartupError: If the server process terminates unexpectedly\n            ServerTimeoutError: If the server doesn't respond within timeout\n\n        Note:\n            This is an internal method used during the server startup process.\n        \"\"\"\n        start_time = time.time()\n\n        while time.time() - start_time < self.config.timeout:\n            process = cast(subprocess.Popen[str], self._process)\n\n            if process.poll() is not None:\n                stdout, stderr = process.communicate()\n                error_msg = (\n                    f\"Ollama process terminated unexpectedly.\\n\"\n                    f\"Exit code: {process.returncode}\\n\"\n                    f\"stdout: {stdout}\\n\"\n                    f\"stderr: {stderr}\"\n                )\n                self._process = None\n                raise ServerStartupError(error_msg)\n\n            if self._check_server_health():\n                logging.info(\"Ollama server is ready\")\n                return\n\n            time.sleep(self.config.check_interval)\n\n        raise ServerTimeoutError(\n            f\"Ollama server did not start within {self.config.timeout} seconds\"\n        )\n\n    def __enter__(self) -> \"OllamaManager\":\n        \"\"\"Context manager entry point that starts the server.\"\"\"\n        self.start()\n        return self\n\n    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:\n        \"\"\"Context manager exit point that ensures the server is stopped.\"\"\"\n        self.stop()\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.__enter__","title":"__enter__()","text":"

      Context manager entry point that starts the server.

      Source code in clientai/ollama/manager/core.py
      def __enter__(self) -> \"OllamaManager\":\n    \"\"\"Context manager entry point that starts the server.\"\"\"\n    self.start()\n    return self\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.__exit__","title":"__exit__(exc_type, exc_val, exc_tb)","text":"

      Context manager exit point that ensures the server is stopped.

      Source code in clientai/ollama/manager/core.py
      def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:\n    \"\"\"Context manager exit point that ensures the server is stopped.\"\"\"\n    self.stop()\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.__init__","title":"__init__(config=None)","text":"

      Initialize the Ollama manager.

      Parameters:

      Name Type Description Default config Optional[OllamaServerConfig]

      Optional server configuration. If None, uses defaults.

      None Source code in clientai/ollama/manager/core.py
      def __init__(self, config: Optional[OllamaServerConfig] = None) -> None:\n    \"\"\"\n    Initialize the Ollama manager.\n\n    Args:\n        config: Optional server configuration. If None, uses defaults.\n    \"\"\"\n    self.config = config or OllamaServerConfig()\n    self._process: Optional[subprocess.Popen[str]] = None\n    self._platform_info = PlatformInfo()\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.start","title":"start()","text":"

      Start the Ollama server using the configured parameters.

      This method initializes and starts the Ollama server process, waiting for it to become healthy before returning.

      Raises:

      Type Description ServerStartupError

      If the server fails to start

      ServerTimeoutError

      If the server doesn't respond within timeout

      ExecutableNotFoundError

      If the Ollama executable is not found

      ResourceError

      If there are insufficient system resources

      Examples:

      Start with default configuration:

      manager = OllamaManager()\nmanager.start()\n

      Start with custom configuration:

      config = OllamaServerConfig(gpu_layers=35)\nmanager = OllamaManager(config)\nmanager.start()\n

      Source code in clientai/ollama/manager/core.py
      def start(self) -> None:\n    \"\"\"\n    Start the Ollama server using the configured parameters.\n\n    This method initializes and starts the Ollama server process,\n    waiting for it to become healthy before returning.\n\n    Raises:\n        ServerStartupError: If the server fails to start\n        ServerTimeoutError: If the server doesn't respond within timeout\n        ExecutableNotFoundError: If the Ollama executable is not found\n        ResourceError: If there are insufficient system resources\n\n    Examples:\n        Start with default configuration:\n        ```python\n        manager = OllamaManager()\n        manager.start()\n        ```\n\n        Start with custom configuration:\n        ```python\n        config = OllamaServerConfig(gpu_layers=35)\n        manager = OllamaManager(config)\n        manager.start()\n        ```\n    \"\"\"\n    if self._process is not None:\n        raise ServerStartupError(\"Ollama server is already running\")\n\n    logging.info(\n        f\"Starting Ollama server on {self.config.host}:{self.config.port}\"\n    )\n\n    try:\n        popen_kwargs: Dict[str, Any] = {\n            \"stdout\": subprocess.PIPE,\n            \"stderr\": subprocess.PIPE,\n            \"text\": True,\n            \"env\": self._platform_info.get_environment(self.config),\n        }\n\n        if self._platform_info.platform == Platform.WINDOWS:\n            if sys.platform == \"win32\":\n                popen_kwargs[\"creationflags\"] = subprocess.CREATE_NO_WINDOW\n\n        self._process = subprocess.Popen(\n            self._platform_info.get_server_command(self.config),\n            **popen_kwargs,\n        )\n\n    except FileNotFoundError as e:\n        raise_ollama_error(\n            ExecutableNotFoundError,\n            \"Ollama executable not found. Ensure Ollama is installed.\",\n            e,\n        )\n    except MemoryError as e:\n        raise_ollama_error(\n            ResourceError, \"Insufficient memory to start Ollama server\", e\n        )\n    except Exception as e:\n        raise_ollama_error(\n            ServerStartupError,\n            f\"Failed to start Ollama server: {str(e)}\",\n            e,\n        )\n\n    try:\n        self._wait_for_server()\n    except Exception as e:\n        self.stop()\n        raise e\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.stop","title":"stop()","text":"

      Stop the running Ollama server instance.

      This method terminates the Ollama server process if it's running. It will wait for the process to complete before returning.

      Examples:

      Stop a running server:

      manager = OllamaManager()\nmanager.start()\n# ... use the server ...\nmanager.stop()\n

      Using context manager (automatic stop):

      with OllamaManager() as manager:\n    # ... use the server ...\n    pass  # Server stops automatically\n

      Source code in clientai/ollama/manager/core.py
      def stop(self) -> None:\n    \"\"\"\n    Stop the running Ollama server instance.\n\n    This method terminates the Ollama server process if it's running.\n    It will wait for the process to complete before returning.\n\n    Examples:\n        Stop a running server:\n        ```python\n        manager = OllamaManager()\n        manager.start()\n        # ... use the server ...\n        manager.stop()\n        ```\n\n        Using context manager (automatic stop):\n        ```python\n        with OllamaManager() as manager:\n            # ... use the server ...\n            pass  # Server stops automatically\n        ```\n    \"\"\"\n    if self._process is not None:\n        try:\n            self._process.terminate()\n            self._process.wait()\n        finally:\n            self._process = None\n            logging.info(\"Ollama server stopped\")\n
      "},{"location":"api/ollama_manager/ollama_server_config/","title":"OllamaServerConfig Class API Reference","text":"

      The OllamaServerConfig class is a configuration container that defines the runtime parameters for an Ollama server instance. It allows users to specify network settings (host/port), hardware utilization options (GPU layers, CPU threads, memory limits), and environment variables. The class provides sensible defaults while allowing fine-grained control over server behavior through optional configuration parameters.

      "},{"location":"api/ollama_manager/ollama_server_config/#class-definition","title":"Class Definition","text":"

      Configuration settings for Ollama server.

      Attributes:

      Name Type Description host str

      Hostname to bind the server to

      port int

      Port number to listen on

      timeout int

      Maximum time in seconds to wait for server startup

      check_interval float

      Time in seconds between server readiness checks

      gpu_layers Optional[int]

      Number of layers to run on GPU

      compute_unit Optional[str]

      Compute device to use ('cpu', 'gpu', 'auto')

      cpu_threads Optional[int]

      Number of CPU threads to use

      memory_limit Optional[str]

      Memory limit for the server (format: number + GiB/MiB, e.g., \"8GiB\")

      gpu_memory_fraction Optional[float]

      Fraction of GPU memory to use (0.0-1.0)

      gpu_devices Optional[Union[List[int], int]]

      GPU device IDs to use

      env_vars Dict[str, str]

      Additional environment variables

      extra_args List[str]

      Additional command line arguments

      Source code in clientai/ollama/manager/config.py
      @dataclass\nclass OllamaServerConfig:\n    \"\"\"\n    Configuration settings for Ollama server.\n\n    Attributes:\n        host: Hostname to bind the server to\n        port: Port number to listen on\n        timeout: Maximum time in seconds to wait for server startup\n        check_interval: Time in seconds between server readiness checks\n        gpu_layers: Number of layers to run on GPU\n        compute_unit: Compute device to use ('cpu', 'gpu', 'auto')\n        cpu_threads: Number of CPU threads to use\n        memory_limit: Memory limit for the server\n                      (format: number + GiB/MiB, e.g., \"8GiB\")\n        gpu_memory_fraction: Fraction of GPU memory to use (0.0-1.0)\n        gpu_devices: GPU device IDs to use\n        env_vars: Additional environment variables\n        extra_args: Additional command line arguments\n    \"\"\"\n\n    host: str = \"127.0.0.1\"\n    port: int = 11434\n    timeout: int = 30\n    check_interval: float = 1.0\n    gpu_layers: Optional[int] = None\n    compute_unit: Optional[str] = None\n    cpu_threads: Optional[int] = None\n    memory_limit: Optional[str] = None\n    gpu_memory_fraction: Optional[float] = None\n    gpu_devices: Optional[Union[List[int], int]] = None\n    env_vars: Dict[str, str] = field(default_factory=dict)\n    extra_args: List[str] = field(default_factory=list)\n\n    def _validate_host(self) -> None:\n        \"\"\"Validate the host address.\"\"\"\n        if not self.host:\n            raise ValueError(\"Host cannot be empty\")\n\n        try:\n            ipaddress.ip_address(self.host)\n        except ValueError:\n            if not re.match(r\"^[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*$\", self.host):\n                raise ValueError(f\"Invalid host: {self.host}\")\n\n    def _validate_port(self) -> None:\n        \"\"\"Validate the port number.\"\"\"\n        if not 1 <= self.port <= 65535:\n            raise ValueError(\n                f\"Port must be between 1 and 65535, got {self.port}\"\n            )\n\n    def _validate_timeout_and_interval(self) -> None:\n        \"\"\"Validate timeout and check interval.\"\"\"\n        if self.timeout <= 0:\n            raise ValueError(\"Timeout must be positive\")\n        if self.check_interval <= 0:\n            raise ValueError(\"Check interval must be positive\")\n        if self.check_interval > self.timeout:\n            raise ValueError(\"Check interval cannot be greater than timeout\")\n\n    def _validate_gpu_settings(self) -> None:\n        \"\"\"Validate GPU-related settings.\"\"\"\n        if self.gpu_layers is not None:\n            if not isinstance(self.gpu_layers, int) or self.gpu_layers < 0:\n                raise ValueError(\"gpu_layers must be a non-negative integer\")\n\n        if self.gpu_memory_fraction is not None:\n            if not 0.0 <= self.gpu_memory_fraction <= 1.0:\n                raise ValueError(\n                    \"gpu_memory_fraction must be between 0.0 and 1.0\"\n                )\n\n        if self.gpu_devices is not None:\n            if isinstance(self.gpu_devices, int):\n                if self.gpu_devices < 0:\n                    raise ValueError(\"GPU device ID must be non-negative\")\n            elif isinstance(self.gpu_devices, list):\n                if not all(\n                    isinstance(d, int) and d >= 0 for d in self.gpu_devices\n                ):\n                    raise ValueError(\n                        \"All GPU device IDs must be non-negative integers\"\n                    )\n                if len(self.gpu_devices) != len(set(self.gpu_devices)):\n                    raise ValueError(\n                        \"Duplicate GPU device IDs are not allowed\"\n                    )\n            else:\n                raise ValueError(\n                    \"gpu_devices must be an integer or list of integers\"\n                )\n\n    def _validate_compute_unit(self) -> None:\n        \"\"\"Validate compute unit setting.\"\"\"\n        if self.compute_unit and self.compute_unit not in [\n            \"cpu\",\n            \"gpu\",\n            \"auto\",\n        ]:\n            raise ValueError(\n                \"compute_unit must be one of: 'cpu', 'gpu', 'auto'\"\n            )\n\n    def _validate_cpu_threads(self) -> None:\n        \"\"\"Validate CPU threads setting.\"\"\"\n        if self.cpu_threads is not None:\n            if not isinstance(self.cpu_threads, int) or self.cpu_threads <= 0:\n                raise ValueError(\"cpu_threads must be a positive integer\")\n\n    def _validate_memory_limit(self) -> None:\n        \"\"\"Validate memory limit format.\"\"\"\n        if self.memory_limit is not None:\n            pattern = r\"^\\d+(\\.\\d+)?[MGT]iB$\"\n            if not re.match(pattern, self.memory_limit):\n                raise ValueError(\n                    \"memory_limit must be in format: NUMBER + UNIT, \"\n                    \"where UNIT is MiB, GiB, or TiB (e.g., '8GiB')\"\n                )\n\n            match = re.match(r\"^\\d+(\\.\\d+)?\", self.memory_limit)\n            if match is None:\n                raise ValueError(\"Invalid memory_limit format\")\n\n            value = float(match.group())\n            unit = self.memory_limit[-3:]\n\n            if unit == \"MiB\" and value < 100:\n                raise ValueError(\"memory_limit in MiB must be at least 100\")\n            elif unit == \"GiB\" and value < 0.1:\n                raise ValueError(\"memory_limit in GiB must be at least 0.1\")\n            elif unit == \"TiB\" and value < 0.001:\n                raise ValueError(\"memory_limit in TiB must be at least 0.001\")\n\n    def _validate_env_vars(self) -> None:\n        \"\"\"Validate environment variables.\"\"\"\n        if not all(\n            isinstance(k, str) and isinstance(v, str)\n            for k, v in self.env_vars.items()\n        ):\n            raise ValueError(\"All environment variables must be strings\")\n\n    def _validate_extra_args(self) -> None:\n        \"\"\"Validate extra arguments.\"\"\"\n        if not all(isinstance(arg, str) for arg in self.extra_args):\n            raise ValueError(\"All extra arguments must be strings\")\n\n    def __post_init__(self):\n        \"\"\"Validate all configuration after initialization.\"\"\"\n        self._validate_host()\n        self._validate_port()\n        self._validate_timeout_and_interval()\n        self._validate_gpu_settings()\n        self._validate_compute_unit()\n        self._validate_cpu_threads()\n        self._validate_memory_limit()\n        self._validate_env_vars()\n        self._validate_extra_args()\n\n    @property\n    def base_url(self) -> str:\n        \"\"\"Get the base URL for the Ollama server.\"\"\"\n        return f\"http://{self.host}:{self.port}\"\n

      rendering: show_if_no_docstring: true

      "},{"location":"api/ollama_manager/ollama_server_config/#clientai.ollama.OllamaServerConfig.base_url","title":"base_url: str property","text":"

      Get the base URL for the Ollama server.

      "},{"location":"api/ollama_manager/ollama_server_config/#clientai.ollama.OllamaServerConfig.__post_init__","title":"__post_init__()","text":"

      Validate all configuration after initialization.

      Source code in clientai/ollama/manager/config.py
      def __post_init__(self):\n    \"\"\"Validate all configuration after initialization.\"\"\"\n    self._validate_host()\n    self._validate_port()\n    self._validate_timeout_and_interval()\n    self._validate_gpu_settings()\n    self._validate_compute_unit()\n    self._validate_cpu_threads()\n    self._validate_memory_limit()\n    self._validate_env_vars()\n    self._validate_extra_args()\n
      "},{"location":"api/specific_providers/ollama_provider/","title":"Ollama Provider API Reference","text":"

      The OllamaProvider class implements the AIProvider interface for the Ollama service. It provides methods for text generation and chat functionality using locally hosted models through Ollama.

      "},{"location":"api/specific_providers/ollama_provider/#class-definition","title":"Class Definition","text":"

      Bases: AIProvider

      Ollama-specific implementation of the AIProvider abstract base class.

      This class provides methods to interact with Ollama's models for text generation and chat functionality.

      Attributes:

      Name Type Description client OllamaClientProtocol

      The Ollama client used for making API calls.

      Parameters:

      Name Type Description Default host Optional[str]

      The host address for the Ollama server. If not provided, the default Ollama client will be used.

      None

      Raises:

      Type Description ImportError

      If the Ollama package is not installed.

      Examples:

      Initialize the Ollama provider:

      provider = Provider(host=\"http://localhost:11434\")\n

      Source code in clientai/ollama/provider.py
      class Provider(AIProvider):\n    \"\"\"\n    Ollama-specific implementation of the AIProvider abstract base class.\n\n    This class provides methods to interact with Ollama's models for\n    text generation and chat functionality.\n\n    Attributes:\n        client: The Ollama client used for making API calls.\n\n    Args:\n        host: The host address for the Ollama server.\n            If not provided, the default Ollama client will be used.\n\n    Raises:\n        ImportError: If the Ollama package is not installed.\n\n    Examples:\n        Initialize the Ollama provider:\n        ```python\n        provider = Provider(host=\"http://localhost:11434\")\n        ```\n    \"\"\"\n\n    def __init__(self, host: Optional[str] = None):\n        if not OLLAMA_INSTALLED or Client is None:\n            raise ImportError(\n                \"The ollama package is not installed. \"\n                \"Please install it with 'pip install clientai[ollama]'.\"\n            )\n        self.client: OllamaClientProtocol = cast(\n            OllamaClientProtocol, Client(host=host) if host else ollama\n        )\n\n    def _stream_generate_response(\n        self,\n        stream: Iterator[OllamaStreamResponse],\n        return_full_response: bool,\n    ) -> Iterator[Union[str, OllamaStreamResponse]]:\n        \"\"\"\n        Process the streaming response from Ollama API for text generation.\n\n        Args:\n            stream: The stream of responses from Ollama API.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, OllamaStreamResponse]: Processed content or\n                                              full response objects.\n        \"\"\"\n        for chunk in stream:\n            if return_full_response:\n                yield chunk\n            else:\n                yield chunk[\"response\"]\n\n    def _stream_chat_response(\n        self,\n        stream: Iterator[OllamaChatResponse],\n        return_full_response: bool,\n    ) -> Iterator[Union[str, OllamaChatResponse]]:\n        \"\"\"\n        Process the streaming response from Ollama API for chat.\n\n        Args:\n            stream: The stream of responses from Ollama API.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, OllamaChatResponse]: Processed content or\n                                            full response objects.\n        \"\"\"\n        for chunk in stream:\n            if return_full_response:\n                yield chunk\n            else:\n                yield chunk[\"message\"][\"content\"]\n\n    def _map_exception_to_clientai_error(self, e: Exception) -> ClientAIError:\n        \"\"\"\n        Maps an Ollama exception to the appropriate ClientAI exception.\n\n        Args:\n            e (Exception): The exception caught during the API call.\n\n        Returns:\n            ClientAIError: An instance of the appropriate ClientAI exception.\n        \"\"\"\n        message = str(e)\n\n        if isinstance(e, ollama.RequestError):\n            if \"authentication\" in message.lower():\n                return AuthenticationError(\n                    message, status_code=401, original_error=e\n                )\n            elif \"rate limit\" in message.lower():\n                return RateLimitError(\n                    message, status_code=429, original_error=e\n                )\n            elif \"not found\" in message.lower():\n                return ModelError(message, status_code=404, original_error=e)\n            else:\n                return InvalidRequestError(\n                    message, status_code=400, original_error=e\n                )\n        elif isinstance(e, ollama.ResponseError):\n            if \"timeout\" in message.lower() or \"timed out\" in message.lower():\n                return TimeoutError(message, status_code=408, original_error=e)\n            else:\n                return APIError(message, status_code=500, original_error=e)\n        else:\n            return ClientAIError(message, status_code=500, original_error=e)\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OllamaGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt using a specified Ollama model.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the Ollama model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n        Returns:\n            OllamaGenericResponse: The generated text, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Generate text (text only):\n            ```python\n            response = provider.generate_text(\n                \"Explain the concept of machine learning\",\n                model=\"llama2\",\n            )\n            print(response)\n            ```\n\n            Generate text (full response):\n            ```python\n            response = provider.generate_text(\n                \"Explain the concept of machine learning\",\n                model=\"llama2\",\n                return_full_response=True\n            )\n            print(response[\"response\"])\n            ```\n\n            Generate text (streaming):\n            ```python\n            for chunk in provider.generate_text(\n                \"Explain the concept of machine learning\",\n                model=\"llama2\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.generate(\n                model=model, prompt=prompt, stream=stream, **kwargs\n            )\n\n            if stream:\n                return cast(\n                    OllamaGenericResponse,\n                    self._stream_generate_response(\n                        cast(Iterator[OllamaStreamResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OllamaResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response[\"response\"]\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OllamaGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using a specified Ollama model.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the Ollama model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n        Returns:\n            OllamaGenericResponse: The chat response, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Chat (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of Japan?\"},\n                {\"role\": \"assistant\", \"content\": \"The capital is Tokyo.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = provider.chat(\n                messages,\n                model=\"llama2\",\n            )\n            print(response)\n            ```\n\n            Chat (full response):\n            ```python\n            response = provider.chat(\n                messages,\n                model=\"llama2\",\n                return_full_response=True\n            )\n            print(response[\"message\"][\"content\"])\n            ```\n\n            Chat (streaming):\n            ```python\n            for chunk in provider.chat(\n                messages,\n                model=\"llama2\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.chat(\n                model=model, messages=messages, stream=stream, **kwargs\n            )\n\n            if stream:\n                return cast(\n                    OllamaGenericResponse,\n                    self._stream_chat_response(\n                        cast(Iterator[OllamaChatResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OllamaChatResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response[\"message\"][\"content\"]\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/ollama_provider/#clientai.ollama.Provider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using a specified Ollama model.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the Ollama model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Ollama API.

      {}

      Returns:

      Name Type Description OllamaGenericResponse OllamaGenericResponse

      The chat response, full response object,

      OllamaGenericResponse

      or an iterator for streaming responses.

      Examples:

      Chat (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of Japan?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital is Tokyo.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = provider.chat(\n    messages,\n    model=\"llama2\",\n)\nprint(response)\n

      Chat (full response):

      response = provider.chat(\n    messages,\n    model=\"llama2\",\n    return_full_response=True\n)\nprint(response[\"message\"][\"content\"])\n

      Chat (streaming):

      for chunk in provider.chat(\n    messages,\n    model=\"llama2\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/ollama/provider.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OllamaGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using a specified Ollama model.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the Ollama model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n    Returns:\n        OllamaGenericResponse: The chat response, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Chat (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of Japan?\"},\n            {\"role\": \"assistant\", \"content\": \"The capital is Tokyo.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = provider.chat(\n            messages,\n            model=\"llama2\",\n        )\n        print(response)\n        ```\n\n        Chat (full response):\n        ```python\n        response = provider.chat(\n            messages,\n            model=\"llama2\",\n            return_full_response=True\n        )\n        print(response[\"message\"][\"content\"])\n        ```\n\n        Chat (streaming):\n        ```python\n        for chunk in provider.chat(\n            messages,\n            model=\"llama2\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.chat(\n            model=model, messages=messages, stream=stream, **kwargs\n        )\n\n        if stream:\n            return cast(\n                OllamaGenericResponse,\n                self._stream_chat_response(\n                    cast(Iterator[OllamaChatResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OllamaChatResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response[\"message\"][\"content\"]\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/ollama_provider/#clientai.ollama.Provider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using a specified Ollama model.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the Ollama model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Ollama API.

      {}

      Returns:

      Name Type Description OllamaGenericResponse OllamaGenericResponse

      The generated text, full response object,

      OllamaGenericResponse

      or an iterator for streaming responses.

      Examples:

      Generate text (text only):

      response = provider.generate_text(\n    \"Explain the concept of machine learning\",\n    model=\"llama2\",\n)\nprint(response)\n

      Generate text (full response):

      response = provider.generate_text(\n    \"Explain the concept of machine learning\",\n    model=\"llama2\",\n    return_full_response=True\n)\nprint(response[\"response\"])\n

      Generate text (streaming):

      for chunk in provider.generate_text(\n    \"Explain the concept of machine learning\",\n    model=\"llama2\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/ollama/provider.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OllamaGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt using a specified Ollama model.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the Ollama model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n    Returns:\n        OllamaGenericResponse: The generated text, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Generate text (text only):\n        ```python\n        response = provider.generate_text(\n            \"Explain the concept of machine learning\",\n            model=\"llama2\",\n        )\n        print(response)\n        ```\n\n        Generate text (full response):\n        ```python\n        response = provider.generate_text(\n            \"Explain the concept of machine learning\",\n            model=\"llama2\",\n            return_full_response=True\n        )\n        print(response[\"response\"])\n        ```\n\n        Generate text (streaming):\n        ```python\n        for chunk in provider.generate_text(\n            \"Explain the concept of machine learning\",\n            model=\"llama2\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.generate(\n            model=model, prompt=prompt, stream=stream, **kwargs\n        )\n\n        if stream:\n            return cast(\n                OllamaGenericResponse,\n                self._stream_generate_response(\n                    cast(Iterator[OllamaStreamResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OllamaResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response[\"response\"]\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/openai_provider/","title":"OpenAI Provider API Reference","text":"

      The OpenAIProvider class implements the AIProvider interface for the OpenAI service. It provides methods for text generation and chat functionality using OpenAI's models.

      "},{"location":"api/specific_providers/openai_provider/#class-definition","title":"Class Definition","text":"

      Bases: AIProvider

      OpenAI-specific implementation of the AIProvider abstract base class.

      This class provides methods to interact with OpenAI's models for text generation and chat functionality.

      Attributes:

      Name Type Description client OpenAIClientProtocol

      The OpenAI client used for making API calls.

      Parameters:

      Name Type Description Default api_key str

      The API key for authenticating with OpenAI.

      required

      Raises:

      Type Description ImportError

      If the OpenAI package is not installed.

      Examples:

      Initialize the OpenAI provider:

      provider = Provider(api_key=\"your-openai-api-key\")\n

      Source code in clientai/openai/provider.py
      class Provider(AIProvider):\n    \"\"\"\n    OpenAI-specific implementation of the AIProvider abstract base class.\n\n    This class provides methods to interact with OpenAI's\n    models for text generation and chat functionality.\n\n    Attributes:\n        client: The OpenAI client used for making API calls.\n\n    Args:\n        api_key: The API key for authenticating with OpenAI.\n\n    Raises:\n        ImportError: If the OpenAI package is not installed.\n\n    Examples:\n        Initialize the OpenAI provider:\n        ```python\n        provider = Provider(api_key=\"your-openai-api-key\")\n        ```\n    \"\"\"\n\n    def __init__(self, api_key: str):\n        if not OPENAI_INSTALLED or Client is None:\n            raise ImportError(\n                \"The openai package is not installed. \"\n                \"Please install it with 'pip install clientai[openai]'.\"\n            )\n        self.client: OpenAIClientProtocol = cast(\n            OpenAIClientProtocol, Client(api_key=api_key)\n        )\n\n    def _stream_response(\n        self,\n        stream: Iterator[OpenAIStreamResponse],\n        return_full_response: bool,\n    ) -> Iterator[Union[str, OpenAIStreamResponse]]:\n        \"\"\"\n        Process the streaming response from OpenAI API.\n\n        Args:\n            stream: The stream of responses from OpenAI API.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, OpenAIStreamResponse]: Processed content or full\n                                              response objects.\n        \"\"\"\n        for chunk in stream:\n            if return_full_response:\n                yield chunk\n            else:\n                content = chunk.choices[0].delta.content\n                if content:\n                    yield content\n\n    def _map_exception_to_clientai_error(self, e: Exception) -> ClientAIError:\n        \"\"\"\n        Maps an OpenAI exception to the appropriate ClientAI exception.\n\n        Args:\n            e (Exception): The exception caught during the API call.\n\n        Raises:\n            ClientAIError: An instance of the appropriate ClientAI exception.\n        \"\"\"\n        error_message = str(e)\n        status_code = None\n\n        if hasattr(e, \"status_code\"):\n            status_code = e.status_code\n        else:\n            try:\n                status_code = int(\n                    error_message.split(\"Error code: \")[1].split(\" -\")[0]\n                )\n            except (IndexError, ValueError):\n                pass\n\n        if (\n            isinstance(e, OpenAIAuthenticationError)\n            or \"incorrect api key\" in error_message.lower()\n        ):\n            return AuthenticationError(\n                error_message, status_code, original_error=e\n            )\n        elif (\n            isinstance(e, openai.OpenAIError)\n            or \"error code:\" in error_message.lower()\n        ):\n            if status_code == 429 or \"rate limit\" in error_message.lower():\n                return RateLimitError(\n                    error_message, status_code, original_error=e\n                )\n            elif status_code == 404 or \"not found\" in error_message.lower():\n                return ModelError(error_message, status_code, original_error=e)\n            elif status_code == 400 or \"invalid\" in error_message.lower():\n                return InvalidRequestError(\n                    error_message, status_code, original_error=e\n                )\n            elif status_code == 408 or \"timeout\" in error_message.lower():\n                return TimeoutError(\n                    error_message, status_code, original_error=e\n                )\n            elif status_code and status_code >= 500:\n                return APIError(error_message, status_code, original_error=e)\n\n        return ClientAIError(error_message, status_code, original_error=e)\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OpenAIGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt using a specified OpenAI model.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the OpenAI model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n        Returns:\n            OpenAIGenericResponse: The generated text, full response object,\n            or an iterator for streaming responses.\n\n        Raises:\n            ClientAIError: If an error occurs during the API call.\n\n        Examples:\n            Generate text (text only):\n            ```python\n            response = provider.generate_text(\n                \"Explain the theory of relativity\",\n                model=\"gpt-3.5-turbo\",\n            )\n            print(response)\n            ```\n\n            Generate text (full response):\n            ```python\n            response = provider.generate_text(\n                \"Explain the theory of relativity\",\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            print(response.choices[0].message.content)\n            ```\n\n            Generate text (streaming):\n            ```python\n            for chunk in provider.generate_text(\n                \"Explain the theory of relativity\",\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.chat.completions.create(\n                model=model,\n                messages=[{\"role\": \"user\", \"content\": prompt}],\n                stream=stream,\n                **kwargs,\n            )\n\n            if stream:\n                return cast(\n                    OpenAIGenericResponse,\n                    self._stream_response(\n                        cast(Iterator[OpenAIStreamResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OpenAIResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response.choices[0].message.content\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OpenAIGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using a specified OpenAI model.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the OpenAI model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n        Returns:\n            OpenAIGenericResponse: The chat response, full response object,\n            or an iterator for streaming responses.\n\n        Raises:\n            ClientAIError: If an error occurs during the API call.\n\n        Examples:\n            Chat (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n                {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = provider.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n            )\n            print(response)\n            ```\n\n            Chat (full response):\n            ```python\n            response = provider.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            print(response.choices[0].message.content)\n            ```\n\n            Chat (streaming):\n            ```python\n            for chunk in provider.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.chat.completions.create(\n                model=model, messages=messages, stream=stream, **kwargs\n            )\n\n            if stream:\n                return cast(\n                    OpenAIGenericResponse,\n                    self._stream_response(\n                        cast(Iterator[OpenAIStreamResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OpenAIResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response.choices[0].message.content\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/openai_provider/#clientai.openai.Provider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using a specified OpenAI model.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the OpenAI model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the OpenAI API.

      {}

      Returns:

      Name Type Description OpenAIGenericResponse OpenAIGenericResponse

      The chat response, full response object,

      OpenAIGenericResponse

      or an iterator for streaming responses.

      Raises:

      Type Description ClientAIError

      If an error occurs during the API call.

      Examples:

      Chat (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = provider.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n)\nprint(response)\n

      Chat (full response):

      response = provider.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\nprint(response.choices[0].message.content)\n

      Chat (streaming):

      for chunk in provider.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/openai/provider.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OpenAIGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using a specified OpenAI model.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the OpenAI model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n    Returns:\n        OpenAIGenericResponse: The chat response, full response object,\n        or an iterator for streaming responses.\n\n    Raises:\n        ClientAIError: If an error occurs during the API call.\n\n    Examples:\n        Chat (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n            {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = provider.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n        )\n        print(response)\n        ```\n\n        Chat (full response):\n        ```python\n        response = provider.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        print(response.choices[0].message.content)\n        ```\n\n        Chat (streaming):\n        ```python\n        for chunk in provider.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.chat.completions.create(\n            model=model, messages=messages, stream=stream, **kwargs\n        )\n\n        if stream:\n            return cast(\n                OpenAIGenericResponse,\n                self._stream_response(\n                    cast(Iterator[OpenAIStreamResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OpenAIResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response.choices[0].message.content\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/openai_provider/#clientai.openai.Provider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using a specified OpenAI model.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the OpenAI model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the OpenAI API.

      {}

      Returns:

      Name Type Description OpenAIGenericResponse OpenAIGenericResponse

      The generated text, full response object,

      OpenAIGenericResponse

      or an iterator for streaming responses.

      Raises:

      Type Description ClientAIError

      If an error occurs during the API call.

      Examples:

      Generate text (text only):

      response = provider.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-3.5-turbo\",\n)\nprint(response)\n

      Generate text (full response):

      response = provider.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\nprint(response.choices[0].message.content)\n

      Generate text (streaming):

      for chunk in provider.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/openai/provider.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OpenAIGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt using a specified OpenAI model.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the OpenAI model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n    Returns:\n        OpenAIGenericResponse: The generated text, full response object,\n        or an iterator for streaming responses.\n\n    Raises:\n        ClientAIError: If an error occurs during the API call.\n\n    Examples:\n        Generate text (text only):\n        ```python\n        response = provider.generate_text(\n            \"Explain the theory of relativity\",\n            model=\"gpt-3.5-turbo\",\n        )\n        print(response)\n        ```\n\n        Generate text (full response):\n        ```python\n        response = provider.generate_text(\n            \"Explain the theory of relativity\",\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        print(response.choices[0].message.content)\n        ```\n\n        Generate text (streaming):\n        ```python\n        for chunk in provider.generate_text(\n            \"Explain the theory of relativity\",\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.chat.completions.create(\n            model=model,\n            messages=[{\"role\": \"user\", \"content\": prompt}],\n            stream=stream,\n            **kwargs,\n        )\n\n        if stream:\n            return cast(\n                OpenAIGenericResponse,\n                self._stream_response(\n                    cast(Iterator[OpenAIStreamResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OpenAIResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response.choices[0].message.content\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/replicate_provider/","title":"Replicate Provider API Reference","text":"

      The ReplicateProvider class implements the AIProvider interface for the Replicate service. It provides methods for text generation and chat functionality using models hosted on Replicate.

      "},{"location":"api/specific_providers/replicate_provider/#class-definition","title":"Class Definition","text":"

      Bases: AIProvider

      Replicate-specific implementation of the AIProvider abstract base class.

      This class provides methods to interact with Replicate's AI models for text generation and chat functionality.

      Attributes:

      Name Type Description client ReplicateClientProtocol

      The Replicate client used for making API calls.

      Parameters:

      Name Type Description Default api_key str

      The API key for authenticating with Replicate.

      required

      Raises:

      Type Description ImportError

      If the Replicate package is not installed.

      Examples:

      Initialize the Replicate provider:

      provider = Provider(api_key=\"your-replicate-api-key\")\n

      Source code in clientai/replicate/provider.py
      class Provider(AIProvider):\n    \"\"\"\n    Replicate-specific implementation of the AIProvider abstract base class.\n\n    This class provides methods to interact with Replicate's AI models for\n    text generation and chat functionality.\n\n    Attributes:\n        client: The Replicate client used for making API calls.\n\n    Args:\n        api_key: The API key for authenticating with Replicate.\n\n    Raises:\n        ImportError: If the Replicate package is not installed.\n\n    Examples:\n        Initialize the Replicate provider:\n        ```python\n        provider = Provider(api_key=\"your-replicate-api-key\")\n        ```\n    \"\"\"\n\n    def __init__(self, api_key: str):\n        if not REPLICATE_INSTALLED or Client is None:\n            raise ImportError(\n                \"The replicate package is not installed. \"\n                \"Please install it with 'pip install clientai[replicate]'.\"\n            )\n        self.client: ReplicateClientProtocol = Client(api_token=api_key)\n\n    def _process_output(self, output: Any) -> str:\n        \"\"\"\n        Process the output from Replicate API into a string format.\n\n        Args:\n            output: The raw output from Replicate API.\n\n        Returns:\n            str: The processed output as a string.\n        \"\"\"\n        if isinstance(output, List):\n            return \"\".join(str(item) for item in output)\n        elif isinstance(output, str):\n            return output\n        else:\n            return str(output)\n\n    def _wait_for_prediction(\n        self, prediction_id: str, max_wait_time: int = 300\n    ) -> ReplicatePredictionProtocol:\n        \"\"\"\n        Wait for a prediction to complete or fail.\n\n        Args:\n            prediction_id: The ID of the prediction to wait for.\n            max_wait_time: Maximum time to wait in seconds. Defaults to 300.\n\n        Returns:\n            ReplicatePredictionProtocol: The completed prediction.\n\n        Raises:\n            TimeoutError: If the prediction doesn't complete within\n                          the max_wait_time.\n            APIError: If the prediction fails.\n        \"\"\"\n        start_time = time.time()\n        while time.time() - start_time < max_wait_time:\n            prediction = self.client.predictions.get(prediction_id)\n            if prediction.status == \"succeeded\":\n                return prediction\n            elif prediction.status == \"failed\":\n                raise self._map_exception_to_clientai_error(\n                    Exception(f\"Prediction failed: {prediction.error}\")\n                )\n            time.sleep(1)\n\n        raise self._map_exception_to_clientai_error(\n            Exception(\"Prediction timed out\"), status_code=408\n        )\n\n    def _stream_response(\n        self,\n        prediction: ReplicatePredictionProtocol,\n        return_full_response: bool,\n    ) -> Iterator[Union[str, ReplicateStreamResponse]]:\n        \"\"\"\n        Stream the response from a prediction.\n\n        Args:\n            prediction: The prediction to stream.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, ReplicateStreamResponse]: Processed output or\n                                                 full response objects.\n        \"\"\"\n        metadata = cast(ReplicateStreamResponse, prediction.__dict__.copy())\n        for event in prediction.stream():\n            if return_full_response:\n                metadata[\"output\"] = self._process_output(event)\n                yield metadata\n            else:\n                yield self._process_output(event)\n\n    def _map_exception_to_clientai_error(\n        self, e: Exception, status_code: Optional[int] = None\n    ) -> ClientAIError:\n        \"\"\"\n        Maps a Replicate exception to the appropriate ClientAI exception.\n\n        Args:\n            e (Exception): The exception caught during the API call.\n            status_code (int, optional): The HTTP status code, if available.\n\n        Returns:\n            ClientAIError: An instance of the appropriate ClientAI exception.\n        \"\"\"\n        error_message = str(e)\n        status_code = status_code or getattr(e, \"status_code\", None)\n\n        if (\n            \"authentication\" in error_message.lower()\n            or \"unauthorized\" in error_message.lower()\n        ):\n            return AuthenticationError(\n                error_message, status_code, original_error=e\n            )\n        elif \"rate limit\" in error_message.lower():\n            return RateLimitError(error_message, status_code, original_error=e)\n        elif \"not found\" in error_message.lower():\n            return ModelError(error_message, status_code, original_error=e)\n        elif \"invalid\" in error_message.lower():\n            return InvalidRequestError(\n                error_message, status_code, original_error=e\n            )\n        elif \"timeout\" in error_message.lower() or status_code == 408:\n            return TimeoutError(error_message, status_code, original_error=e)\n        elif status_code == 400:\n            return InvalidRequestError(\n                error_message, status_code, original_error=e\n            )\n        else:\n            return APIError(error_message, status_code, original_error=e)\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> ReplicateGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt\n        using a specified Replicate model.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the Replicate model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments\n                      to pass to the Replicate API.\n\n        Returns:\n            ReplicateGenericResponse: The generated text, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Generate text (text only):\n            ```python\n            response = provider.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            print(response)\n            ```\n\n            Generate text (full response):\n            ```python\n            response = provider.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n                return_full_response=True\n            )\n            print(response[\"output\"])\n            ```\n\n            Generate text (streaming):\n            ```python\n            for chunk in provider.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            prediction = self.client.predictions.create(\n                model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n            )\n\n            if stream:\n                return self._stream_response(prediction, return_full_response)\n            else:\n                completed_prediction = self._wait_for_prediction(prediction.id)\n                if return_full_response:\n                    response = cast(\n                        ReplicateResponse, completed_prediction.__dict__.copy()\n                    )\n                    response[\"output\"] = self._process_output(\n                        completed_prediction.output\n                    )\n                    return response\n                else:\n                    return self._process_output(completed_prediction.output)\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> ReplicateGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using a specified Replicate model.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the Replicate model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments\n                      to pass to the Replicate API.\n\n        Returns:\n            ReplicateGenericResponse: The chat response, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Chat (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n                {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = provider.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            print(response)\n            ```\n\n            Chat (full response):\n            ```python\n            response = provider.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n                return_full_response=True\n            )\n            print(response[\"output\"])\n            ```\n\n            Chat (streaming):\n            ```python\n            for chunk in provider.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            prompt = \"\\n\".join(\n                [f\"{m['role']}: {m['content']}\" for m in messages]\n            )\n            prompt += \"\\nassistant: \"\n\n            prediction = self.client.predictions.create(\n                model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n            )\n\n            if stream:\n                return self._stream_response(prediction, return_full_response)\n            else:\n                completed_prediction = self._wait_for_prediction(prediction.id)\n                if return_full_response:\n                    response = cast(\n                        ReplicateResponse, completed_prediction.__dict__.copy()\n                    )\n                    response[\"output\"] = self._process_output(\n                        completed_prediction.output\n                    )\n                    return response\n                else:\n                    return self._process_output(completed_prediction.output)\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/replicate_provider/#clientai.replicate.Provider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using a specified Replicate model.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the Replicate model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Replicate API.

      {}

      Returns:

      Name Type Description ReplicateGenericResponse ReplicateGenericResponse

      The chat response, full response object,

      ReplicateGenericResponse

      or an iterator for streaming responses.

      Examples:

      Chat (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = provider.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n)\nprint(response)\n

      Chat (full response):

      response = provider.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n    return_full_response=True\n)\nprint(response[\"output\"])\n

      Chat (streaming):

      for chunk in provider.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/replicate/provider.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> ReplicateGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using a specified Replicate model.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the Replicate model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments\n                  to pass to the Replicate API.\n\n    Returns:\n        ReplicateGenericResponse: The chat response, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Chat (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n            {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = provider.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        print(response)\n        ```\n\n        Chat (full response):\n        ```python\n        response = provider.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n            return_full_response=True\n        )\n        print(response[\"output\"])\n        ```\n\n        Chat (streaming):\n        ```python\n        for chunk in provider.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        prompt = \"\\n\".join(\n            [f\"{m['role']}: {m['content']}\" for m in messages]\n        )\n        prompt += \"\\nassistant: \"\n\n        prediction = self.client.predictions.create(\n            model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n        )\n\n        if stream:\n            return self._stream_response(prediction, return_full_response)\n        else:\n            completed_prediction = self._wait_for_prediction(prediction.id)\n            if return_full_response:\n                response = cast(\n                    ReplicateResponse, completed_prediction.__dict__.copy()\n                )\n                response[\"output\"] = self._process_output(\n                    completed_prediction.output\n                )\n                return response\n            else:\n                return self._process_output(completed_prediction.output)\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/replicate_provider/#clientai.replicate.Provider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using a specified Replicate model.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the Replicate model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Replicate API.

      {}

      Returns:

      Name Type Description ReplicateGenericResponse ReplicateGenericResponse

      The generated text, full response object,

      ReplicateGenericResponse

      or an iterator for streaming responses.

      Examples:

      Generate text (text only):

      response = provider.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n)\nprint(response)\n

      Generate text (full response):

      response = provider.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n    return_full_response=True\n)\nprint(response[\"output\"])\n

      Generate text (streaming):

      for chunk in provider.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/replicate/provider.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> ReplicateGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt\n    using a specified Replicate model.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the Replicate model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments\n                  to pass to the Replicate API.\n\n    Returns:\n        ReplicateGenericResponse: The generated text, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Generate text (text only):\n        ```python\n        response = provider.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        print(response)\n        ```\n\n        Generate text (full response):\n        ```python\n        response = provider.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n            return_full_response=True\n        )\n        print(response[\"output\"])\n        ```\n\n        Generate text (streaming):\n        ```python\n        for chunk in provider.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        prediction = self.client.predictions.create(\n            model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n        )\n\n        if stream:\n            return self._stream_response(prediction, return_full_response)\n        else:\n            completed_prediction = self._wait_for_prediction(prediction.id)\n            if return_full_response:\n                response = cast(\n                    ReplicateResponse, completed_prediction.__dict__.copy()\n                )\n                response[\"output\"] = self._process_output(\n                    completed_prediction.output\n                )\n                return response\n            else:\n                return self._process_output(completed_prediction.output)\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"community/CODE_OF_CONDUCT/","title":"Contributor Covenant Code of Conduct","text":""},{"location":"community/CODE_OF_CONDUCT/#our-pledge","title":"Our Pledge","text":"

      We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

      We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

      "},{"location":"community/CODE_OF_CONDUCT/#our-standards","title":"Our Standards","text":"

      Examples of behavior that contributes to a positive environment for our community include:

      Examples of unacceptable behavior include:

      "},{"location":"community/CODE_OF_CONDUCT/#enforcement-responsibilities","title":"Enforcement Responsibilities","text":"

      Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

      Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.

      "},{"location":"community/CODE_OF_CONDUCT/#scope","title":"Scope","text":"

      This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

      "},{"location":"community/CODE_OF_CONDUCT/#enforcement","title":"Enforcement","text":"

      Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at igor.magalhaes.r@gmail.com. All complaints will be reviewed and investigated promptly and fairly.

      All community leaders are obligated to respect the privacy and security of the reporter of any incident.

      "},{"location":"community/CODE_OF_CONDUCT/#enforcement-guidelines","title":"Enforcement Guidelines","text":"

      Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:

      "},{"location":"community/CODE_OF_CONDUCT/#1-correction","title":"1. Correction","text":"

      Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.

      Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.

      "},{"location":"community/CODE_OF_CONDUCT/#2-warning","title":"2. Warning","text":"

      Community Impact: A violation through a single incident or series of actions.

      Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.

      "},{"location":"community/CODE_OF_CONDUCT/#3-temporary-ban","title":"3. Temporary Ban","text":"

      Community Impact: A serious violation of community standards, including sustained inappropriate behavior.

      Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.

      "},{"location":"community/CODE_OF_CONDUCT/#4-permanent-ban","title":"4. Permanent Ban","text":"

      Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.

      Consequence: A permanent ban from any sort of public interaction within the community.

      "},{"location":"community/CODE_OF_CONDUCT/#attribution","title":"Attribution","text":"

      This Code of Conduct is adapted from the Contributor Covenant, version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

      Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.

      For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.

      "},{"location":"community/CONTRIBUTING/","title":"Contributing to ClientAI","text":"

      Thank you for your interest in contributing to ClientAI! This guide is meant to make it easy for you to get started.

      "},{"location":"community/CONTRIBUTING/#setting-up-your-development-environment","title":"Setting Up Your Development Environment","text":""},{"location":"community/CONTRIBUTING/#cloning-the-repository","title":"Cloning the Repository","text":"

      Start by forking and cloning the ClientAI repository:

      git clone https://github.com/YOUR-GITHUB-USERNAME/clientai.git\n
      "},{"location":"community/CONTRIBUTING/#using-poetry-for-dependency-management","title":"Using Poetry for Dependency Management","text":"

      ClientAI uses Poetry for managing dependencies. If you don't have Poetry installed, follow the instructions on the official Poetry website.

      Once Poetry is installed, navigate to the cloned repository and install the dependencies:

      cd clientai\npoetry install\n

      "},{"location":"community/CONTRIBUTING/#activating-the-virtual-environment","title":"Activating the Virtual Environment","text":"

      Poetry creates a virtual environment for your project. Activate it using:

      poetry shell\n
      "},{"location":"community/CONTRIBUTING/#making-contributions","title":"Making Contributions","text":""},{"location":"community/CONTRIBUTING/#coding-standards","title":"Coding Standards","text":""},{"location":"community/CONTRIBUTING/#testing-with-pytest","title":"Testing with Pytest","text":"

      ClientAI uses pytest for testing. Run tests using:

      poetry run pytest\n

      "},{"location":"community/CONTRIBUTING/#linting","title":"Linting","text":"

      Use mypy for type checking:

      mypy clientai\n

      Use ruff for style:

      ruff check --fix\nruff format\n

      Ensure your code passes linting before submitting.

      "},{"location":"community/CONTRIBUTING/#submitting-your-contributions","title":"Submitting Your Contributions","text":""},{"location":"community/CONTRIBUTING/#creating-a-pull-request","title":"Creating a Pull Request","text":"

      After making your changes:

      "},{"location":"community/CONTRIBUTING/#code-reviews","title":"Code Reviews","text":""},{"location":"community/CONTRIBUTING/#code-of-conduct","title":"Code of Conduct","text":"

      Please adhere to our Code of Conduct to maintain a welcoming and inclusive environment.

      Thank you for contributing to ClientAI\ud83d\ude80

      "},{"location":"community/LICENSE/","title":"License","text":"

      MIT License

      Copyright (c) 2024 Igor Benav

      Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

      The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

      THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

      "},{"location":"community/overview/","title":"Community Overview","text":"

      Welcome to the project's community hub. Here, you'll find essential resources and guidelines that are crucial for contributing to and participating in the project. Please take the time to familiarize yourself with the following documents:

      "},{"location":"community/overview/#table-of-contents","title":"Table of Contents","text":""},{"location":"community/overview/#contributing","title":"Contributing","text":"

      View the Contributing Guidelines

      Interested in contributing to the project? Great! The contributing guidelines will provide you with all the information you need to get started. This includes how to submit issues, propose changes, and the process for submitting pull requests.

      "},{"location":"community/overview/#code-of-conduct","title":"Code of Conduct","text":"

      View the Code of Conduct

      The Code of Conduct outlines the standards and behaviors expected of our community members. It's crucial to ensure a welcoming and inclusive environment for everyone. Please take the time to read and adhere to these guidelines.

      "},{"location":"community/overview/#license","title":"License","text":"

      View the License

      The license document outlines the terms under which our project can be used, modified, and distributed. Understanding the licensing is important for both users and contributors of the project.

      Thank you for being a part of our community and for contributing to our project's success!

      "},{"location":"examples/ai_dungeon_master/","title":"ClientAI Tutorial: Building an AI Dungeon Master","text":"

      In this tutorial, we'll walk through the process of creating an AI-powered Dungeon Master using the ClientAI package. We'll explain each concept in detail and build our game step-by-step, providing context for every decision we make, both technical and gameplay-related.

      "},{"location":"examples/ai_dungeon_master/#table-of-contents","title":"Table of Contents","text":"
      1. Introduction
      2. Setting Up the Project 2.5 Creating the Project Structure
      3. Creating the Game Structure
      4. Integrating Multiple AI Providers
      5. Developing the Enhanced AI Dungeon Master
      6. Main Script that Runs the Game
      7. Running the Game
      8. Conclusion and Further Improvements
      "},{"location":"examples/ai_dungeon_master/#1-introduction","title":"1. Introduction","text":"

      ClientAI is a Python package that provides a unified interface for interacting with multiple AI providers. In this tutorial, we'll use ClientAI to create an AI Dungeon Master that can generate story elements, NPC dialogues, and dynamic environments using different AI models.

      Our AI Dungeon Master will be a text-based role-playing game (RPG) where the game's content is dynamically generated by AI. This approach allows for infinite replayability and unique experiences for each player.

      We'll focus on explaining both technical decisions (such as class structures and AI interactions) and gameplay decisions (like character creation and game mechanics).

      The final result is available in this github repo.

      "},{"location":"examples/ai_dungeon_master/#2-setting-up-the-project","title":"2. Setting Up the Project","text":"

      First, let's set up our project and install the necessary dependencies.

      1. Create a new directory for your project:
      mkdir ai_dungeon_master\ncd ai_dungeon_master\n
      1. Install ClientAI and its dependencies:

        If you want to use poetry, you may skip this part.

      pip install clientai[all]\n

      This command installs ClientAI with support for all providers. If you only need specific providers, you can install them individually (e.g., pip install clientai[openai] for just OpenAI support).

      1. Install additional dependencies:

        If you want to use poetry, you may also skip this part.

      We'll need some additional packages for our project.

      pip install requests\n

      Ollama is a local AI model server that we'll use to run the Llama 3 model. Follow these steps to install Ollama:

      After installing Ollama, you need to download the Llama 3 model. Run the following command:

      ollama pull llama3\n

      This command will download and set up the Llama 3 model for use with Ollama. The download might take some time depending on your internet connection.

      These imports will be used throughout our project:

      "},{"location":"examples/ai_dungeon_master/#25-creating-the-project-structure","title":"2.5 Creating the Project Structure","text":"

      Before we dive into the code, let's set up a proper project structure. This will help us organize our code and make it easier to maintain and expand in the future.

      1. Create the following directory structure:
      clientai_dungeon_master/\n\u251c\u2500\u2500 pyproject.toml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 .env\n\u2514\u2500\u2500 ai_dungeon_master/\n    \u251c\u2500\u2500 __init__.py\n    \u251c\u2500\u2500 main.py\n    \u251c\u2500\u2500 game/\n    \u2502   \u251c\u2500\u2500 __init__.py\n    \u2502   \u251c\u2500\u2500 character.py\n    \u2502   \u251c\u2500\u2500 game_state.py\n    \u2502   \u2514\u2500\u2500 dungeon_master.py\n    \u251c\u2500\u2500 ai/\n    \u2502   \u251c\u2500\u2500 __init__.py\n    \u2502   \u251c\u2500\u2500 ai_providers.py\n    \u2502   \u2514\u2500\u2500 ollama_server.py\n    \u2514\u2500\u2500 utils/\n        \u251c\u2500\u2500 __init__.py\n        \u2514\u2500\u2500 text_utils.py\n
      1. Create a pyproject.toml file in the root directory with the following content:

        If you're using pip directly, you may skip this part

      [tool.poetry]\nname = \"clientai-dungeon-master\"\nversion = \"0.1.0\"\ndescription = \"An AI-powered dungeon master for text-based RPG adventures\"\nauthors = [\"Your Name <your.email@example.com>\"]\nreadme = \"README.md\"\npackages = [{include = \"clientai_dungeon_master\"}]\n\n[tool.poetry.dependencies]\npython = \"^3.11\"\nclientai = \"^0.1.2\"\nrequests = \"^2.32.3\"\npython-decouple = \"^3.8\"\n\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n

      and run

      poetry install\n
      1. Create a .gitignore file in the root directory with the following content:
      # Python\n__pycache__/\n*.py[cod]\n*.pyo\n*.pyd\n.Python\nenv/\nvenv/\nENV/\n\n# Poetry\n.venv/\ndist/\n\n# Environment variables\n.env\n\n# IDEs\n.vscode/\n.idea/\n\n# Logs\n*.log\n\n# OS generated files\n.DS_Store\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\n
      1. Create a .env file in the root directory to store your API keys:
      OPENAI_API_KEY=your_openai_api_key_here\nREPLICATE_API_KEY=your_replicate_api_key_here\n

      Remember to replace your_openai_api_key_here and your_replicate_api_key_here with your actual API keys.

      1. Move the relevant code into the appropriate files based on the new structure.

      This structure separates concerns, making the code more modular and easier to maintain. It also sets up the project for potential future expansion, such as adding more game features or integrating additional AI providers.

      "},{"location":"examples/ai_dungeon_master/#3-creating-the-game-structure","title":"3. Creating the Game Structure","text":"

      Before integrating AI, we'll create the basic structure of our game. This includes classes to represent the character, game state, and AI providers.

      "},{"location":"examples/ai_dungeon_master/#character-class","title":"Character Class","text":"

      The Character class represents the player's character in the game. It stores essential character information like name, race, class, background story, and stats.

      ai_dungeon_master/game/character.py
      class Character:\n    def __init__(self, name: str, race: str, class_type: str, background: str, stats: dict):\n        self.name = name\n        self.race = race\n        self.class_type = class_type\n        self.background = background\n        self.stats = stats\n\n    def __str__(self):\n        return f\"Name: {self.name}, Race: {self.race}, Class: {self.class_type}, Background: {self.background}, Stats: {self.stats}\"\n

      Here we define a character with attributes like a name, race, class, background and stats (like Strength, Intelligence, Wisdom). This is really simple, but will be enough to customize what happens in the story.

      We'll also define the __str__ method to be able to print the character's details easily.

      "},{"location":"examples/ai_dungeon_master/#gamestate-class","title":"GameState Class","text":"

      The GameState class keeps track of the game's current state, including the character's status, location, inventory, health, experience, and quests.

      ai_dungeon_master/game/game_state.py
      from typing import Optional\n\nfrom .character import Character\n\nclass GameState:\n    def __init__(self, character: Character):\n        self.character = character\n        self.location = \"entrance\"\n        self.inventory = []\n        self.health = 100\n        self.experience = 0\n        self.quests = []\n\n    def update(self, location: Optional[str] = None, item: Optional[str] = None, health_change: int = 0, exp_gain: int = 0, quest: Optional[str] = None):\n        if location:\n            self.location = location\n        if item:\n            self.inventory.append(item)\n        self.health = max(0, min(100, self.health + health_change))\n        self.experience += exp_gain\n        if quest:\n            self.quests.append(quest)\n\n    def __str__(self):\n        return f\"{str(self.character)}\\nLocation: {self.location}, Health: {self.health}, XP: {self.experience}, Inventory: {', '.join(self.inventory)}, Quests: {', '.join(self.quests)}\"\n

      We keep track of the state to keep a more consistent experience, we can't expect this to be always generated by the llm. We need to pass the game state as a guide to generate the content.

      The update method allows easy updates to the game state, we'll keep health within 0 to 100, and add an inventory and quests to add more depth to the game.

      "},{"location":"examples/ai_dungeon_master/#4-integrating-multiple-ai-providers","title":"4. Integrating Multiple AI Providers","text":"

      We'll use ClientAI to create a class that manages interactions with different AI providers. This abstraction allows us to switch between providers seamlessly.

      "},{"location":"examples/ai_dungeon_master/#aiproviders-class","title":"AIProviders Class","text":"ai_dungeon_master/ai/ai_providers.py
      from typing import List\n\nfrom clientai import ClientAI\n\nclass AIProviders:\n    def __init__(self):\n        self.openai = ClientAI('openai', api_key=openai_token)\n        self.replicate = ClientAI('replicate', api_key=replicate_token)\n        self.ollama = ClientAI('ollama', host=\"http://localhost:11434\")\n\n    def chat(\n        self,\n        messages: List[dict],\n        provider: str = 'openai',\n        openai_model=\"gpt-4o-mini\",\n        replicate_model=\"meta/meta-llama-3-8b-instruct\",\n        ollama_model=\"llama3\",\n    ):\n        if provider == 'openai':\n            return self.openai.chat(messages, model=openai_model, stream=True)\n        elif provider == 'replicate':\n            return self.replicate.chat(messages, model=replicate_model, stream=True)\n        elif provider == 'ollama':\n            return self.ollama.chat(messages, model=ollama_model, stream=True)\n        else:\n            raise ValueError(f\"Unknown provider: {provider}\")\n

      We create instances of ClientAI for each provider with the necessary API keys or host information, then abstract the chat method to allow for easy switching between AI providers.

      We are going to use ClientAI to use multiple AI models from different providers, since we want to find what is the best model for each task balancing performance and costs.

      "},{"location":"examples/ai_dungeon_master/#managing-api-keys-with-python-decouple-and-a-env-file","title":"Managing API Keys with python-decouple and a .env File","text":"

      To securely handle your API keys without exposing them in your codebase, you can use the python-decouple package and store your keys in a .env file. This approach keeps sensitive information out of your code and version control.

      1. Install python-decouple: You may skip this if you used poetry
      pip install python-decouple\n
      1. Create a .env File: In your project's root directory, make sure the .env has your API keys:
      OPENAI_API_KEY=your_openai_api_key_here\nREPLICATE_API_KEY=your_replicate_api_key_here\n

      Replace your_openai_api_key_here and your_replicate_api_key_here with your actual API keys.

      1. Ensure .env is added to .gitignore: To prevent the .env file from being tracked by version control, ensure it is in your .gitignore file:
      # .gitignore\n.env\n

      This ensures your API keys remain private and aren't pushed to repositories like GitHub.

      1. Access the API Keys in Your Code: Import config from decouple and retrieve the API keys:
      ai_dungeon_master/ai/ai_providers.py
      from decouple import config\n\nopenai_token = config('OPENAI_API_KEY')\nreplicate_token = config('REPLICATE_API_KEY')\n

      Now, you can use these variables when initializing your AI providers.

      1. Update the AIProviders Class: ai_dungeon_master/ai/ai_providers.py
        from typing import List\n\nfrom clientai import ClientAI\nfrom decouple import config\n\nopenai_token = config('OPENAI_API_KEY')\nreplicate_token = config('REPLICATE_API_KEY')he ol\n\nclass AIProviders:\n    def __init__(self):\n        self.openai = ClientAI('openai', api_key=openai_token)\n        self.replicate = ClientAI('replicate', api_key=replicate_token)\n        self.ollama = ClientAI('ollama', host=\"http://localhost:11434\")\n\n ...\n
      "},{"location":"examples/ai_dungeon_master/#managing-ai-servers","title":"Managing AI Servers","text":"

      We need to ensure that local AI servers (like Ollama) are running before the game starts, so let's define a function to start ollama.

      ai_dungeon_master/ai/ollama_server.py
      import subprocess\nimport time\nimport requests\nimport logging\n\nlogging.basicConfig(level=logging.INFO)\n\ndef start_ollama_server(timeout: int = 30, check_interval: float = 1.0):\n    \"\"\"\n    Start the Ollama server and wait for it to be ready.\n    \"\"\"\n    logging.info(\"Starting Ollama server...\")\n\n    try:\n        process = subprocess.Popen(\n            ['ollama', 'serve'],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            text=True\n        )\n    except subprocess.SubprocessError as e:\n        logging.error(f\"Failed to start Ollama process: {e}\")\n        raise\n\n    start_time = time.time()\n    while time.time() - start_time < timeout:\n        try:\n            response = requests.get('http://localhost:11434', timeout=5)\n            if response.status_code == 200:\n                logging.info(\"Ollama server is ready.\")\n                return process\n        except requests.ConnectionError:\n            pass\n        except requests.RequestException as e:\n            logging.error(f\"Unexpected error when checking Ollama server: {e}\")\n            process.terminate()\n            raise\n\n        if process.poll() is not None:\n            stdout, stderr = process.communicate()\n            logging.error(f\"Ollama process terminated unexpectedly. stdout: {stdout}, stderr: {stderr}\")\n            raise subprocess.SubprocessError(\"Ollama process terminated unexpectedly\")\n\n        time.sleep(check_interval)\n\n    process.terminate()\n    raise TimeoutError(f\"Ollama server did not start within {timeout} seconds\")\n

      By managing the server startup within the code, we reduce the setup burden on the player.

      "},{"location":"examples/ai_dungeon_master/#5-developing-the-enhanced-ai-dungeon-master","title":"5. Developing the Enhanced AI Dungeon Master","text":"

      Now we'll develop the main class that controls the game logic and interactions with AI models.

      "},{"location":"examples/ai_dungeon_master/#enhancedaidungeonmaster-class","title":"EnhancedAIDungeonMaster Class","text":"ai_dungeon_master/game/dungeon_master.py
      from typing import Tuple, List\nimport random\nimport time\n\nfrom ai.ai_providers import AIProviders\nfrom utils.text_utils import print_separator\nfrom game.character import Character\nfrom game.game_state import GameState\n\nclass EnhancedAIDungeonMaster:\n    def __init__(self):\n        self.ai = AIProviders()\n        self.conversation_history = []\n        self.game_state = None\n\n    # Methods will be added here...\n
      "},{"location":"examples/ai_dungeon_master/#creating-the-character","title":"Creating the Character","text":"

      We need a method to create the player's character. We'll use AI to do this automatically for us:

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def create_character(self):\n        print(\"Let's create your character!\")\n        name = input(\"What is your character's name? \")\n\n        # We start by defining a prompt\n        character_prompt = f\"\"\"\n        Create a character for a fantasy RPG with the following details:\n        Name: {name}\n\n        Please provide:\n        1. A suitable race (e.g., Human, Elf, Dwarf, etc.)\n        2. A class (e.g., Warrior, Mage, Rogue, etc.)\n        3. A brief background story (2-3 sentences)\n        4. Basic stats (Strength, Dexterity, Constitution, Intelligence, Wisdom, Charisma) on a scale of 1-20\n\n        Format the response as follows:\n        Race: [race]\n        Class: [class]\n        Background: [background story]\n        Stats:\n        - Strength: [value]\n        - Dexterity: [value]\n        - Constitution: [value]\n        - Intelligence: [value]\n        - Wisdom: [value]\n        - Charisma: [value]\n        \"\"\"\n\n        # And we add this prompt to our chat history\n        self.add_to_history(\"user\", character_prompt)\n        character_info = self.print_stream(self.ai.chat(self.conversation_history, provider='openai'))\n\n        # Parse the character info\n        lines = character_info.strip().split('\\n')\n        race = class_type = background = \"\"\n        stats = {}\n\n        for line in lines:\n            if line.startswith(\"Race:\"):\n                race = line.split(\": \", 1)[1].strip()\n            elif line.startswith(\"Class:\"):\n                class_type = line.split(\": \", 1)[1].strip()\n            elif line.startswith(\"Background:\"):\n                background = line.split(\": \", 1)[1].strip()\n            elif \":\" in line and not line.startswith(\"Stats:\"):\n                key, value = line.split(\":\", 1)\n                key = key.strip(\"- \")\n                try:\n                    stats[key] = int(value.strip())\n                except ValueError:\n                    stats[key] = random.randint(1, 20)\n\n        # Just in case, let's ensure it'the player has stats\n        # If any stat is missing, assign a random value\n        for stat in [\"Strength\", \"Dexterity\", \"Constitution\", \"Intelligence\", \"Wisdom\", \"Charisma\"]:\n            if stat not in stats:\n                stats[stat] = random.randint(1, 20)\n\n        # And let's also ensure other required attributes are assigned\n        # If race, class, or background is empty, assign default values\n        race = race or \"Human\"\n        class_type = class_type or \"Adventurer\"\n        background = background or \"A mysterious traveler with an unknown past.\"\n\n        return Character(name, race, class_type, background, stats)\n

      We'll use GPT 4o mini to create initial stuff we need, like the race, class, background etc, and extract the information from the generated content to handle errors.

      Note that since we are leaving this information to the LLM, the name will influence the attributes. If you need a more consistently random generation, do it in the python code and just pass it to the prompt.

      "},{"location":"examples/ai_dungeon_master/#maintaining-conversation-history","title":"Maintaining Conversation History","text":"

      To provide context to the AI, we maintain a conversation history.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def add_to_history(self, role: str, content: str):\n        if not self.conversation_history or self.conversation_history[-1]['content'] != content:\n            self.conversation_history.append({\"role\": role, \"content\": content})\n            if len(self.conversation_history) > 10:\n                self.conversation_history = self.conversation_history[-10:]\n

      Here we will ensure we don't add the same message twice. Plus, we are limiting the conversation history to 10 messages to prevent exceeding token limits.

      "},{"location":"examples/ai_dungeon_master/#generating-the-environment","title":"Generating the Environment","text":"

      Next, let's create detailed environments to enhance the imersion.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def generate_environment(self):\n        if not hasattr(self, 'current_environment'):\n            prompt = f\"\"\"\n            The character {self.game_state.character.name} is a {self.game_state.character.race} {self.game_state.character.class_type} \n            currently in the {self.game_state.location}.\n\n            Describe the current environment in detail, focusing on:\n            1. The physical setting and atmosphere\n            2. Any notable NPCs present\n            3. Interesting objects or features\n\n            Do not create a new character or change any existing character details.\n            Do not include any actions or dialogue for {self.game_state.character.name}.\n\n            End your description with one of these tags if appropriate:\n            [INTERACT_OPPORTUNITY] - if there's a chance for the player to interact with someone or something\n            [QUEST_OPPORTUNITY] - if there's a potential quest or mission available\n            \"\"\"\n            self.add_to_history(\"user\", prompt)\n            self.current_environment = self.ai.chat(self.conversation_history, provider='openai')\n        return self.current_environment\n

      Here we instruct the AI to provide specific details, and we use tags for opportunities. We'll parse these tags INTERACT_OPPORTUNITY and QUEST_OPPORTUNITY later to perform other actions.

      We'll also store the environment description to avoid regenerating it unnecessarily.

      "},{"location":"examples/ai_dungeon_master/#handling-player-actions","title":"Handling Player Actions","text":"

      Now let's process the player's actions and generate outcomes. We'll run this one locally with ollama.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def handle_player_action(self, action):\n        prompt = f\"\"\"\n        The player ({self.game_state.character.name}, a {self.game_state.character.race} {self.game_state.character.class_type}) \n        attempts to {action} in {self.game_state.location}. \n        Describe the immediate result of this action, focusing on the environment and NPCs' reactions.\n        Do not generate any further actions or dialogue for {self.game_state.character.name}.\n        If the player is trying to interact with an NPC, end your response with [NPC_INTERACTION: <npc_name>].\n        \"\"\"\n        self.add_to_history(\"user\", prompt)\n        return self.ai.chat(self.conversation_history, provider='ollama')\n

      Here we pass what the player wants to do to the AI and generate the outcomes for the players actions. We are also using a tag here for interactions, so we can process those in a different way.

      "},{"location":"examples/ai_dungeon_master/#generating-npc-dialogue","title":"Generating NPC Dialogue","text":"

      Next, let's create a function to generate a dialogue with an npc. We'll use replicate with llama3 8b for this.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def generate_npc_dialogue(self, npc_name: str, player_input: str):\n        prompt = f\"\"\"\n        The player ({self.game_state.character.name}) said to {npc_name}: \"{player_input}\"\n        Generate a single, natural response from {npc_name}, addressing the player's input directly.\n        If the player is asking about items for sale, list 2-3 specific items with brief descriptions and prices.\n        Do not include any actions or responses from the player character.\n        Keep the response concise and relevant to the player's input.\n        Do not include any formatting tags, headers, or quotation marks in your response.\n        Respond as if you are {npc_name} speaking directly to the player.\n        \"\"\"\n        self.add_to_history(\"user\", prompt)\n        return self.ai.chat(self.conversation_history, provider='replicate')\n

      Note that in the prompt we ensure the AI provides responses that are in character and appropriate, so we can pass this directly to the player.

      "},{"location":"examples/ai_dungeon_master/#handling-conversations","title":"Handling Conversations","text":"

      We manage conversations with NPCs in a separate method. We start with a conversation loop, to allow the player to have a back-and-forth dialogue with an NPC, and we reset the conversation history to focus the AI on the dialogue.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def handle_conversation(self, npc_name):\n        print(f\"\\nYou are now in conversation with {npc_name}.\")\n        self.conversation_history = [\n            {\"role\": \"system\", \"content\": f\"You are {npc_name}, speaking directly to the player. Respond naturally and in character.\"}\n        ]\n        while True:\n            player_input = input(f\"\\nWhat do you say to {npc_name}? (or type 'end conversation' to stop): \")\n            if player_input.lower() == \"end conversation\":\n                print(f\"\\nYou end your conversation with {npc_name}.\")\n                break\n\n            print(f\"\\n{npc_name}:\")\n            self.print_stream(self.generate_npc_dialogue(npc_name, player_input))\n

      We also add the possibility for the player to end the conversation at any time.

      "},{"location":"examples/ai_dungeon_master/#updating-the-game-state","title":"Updating the Game State","text":"

      We update the game state based on the outcomes provided by the AI.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def update_game_state(self, outcome):\n        if \"found\" in outcome.lower():\n            item = outcome.split(\"found\")[1].split(\".\")[0].strip()\n            self.game_state.update(item=item)\n        if \"new area\" in outcome.lower():\n            new_location = outcome.split(\"new area\")[1].split(\".\")[0].strip()\n            self.game_state.update(location=new_location)\n        if \"damage\" in outcome.lower():\n            self.game_state.update(health_change=-10)\n        if \"healed\" in outcome.lower():\n            self.game_state.update(health_change=10)\n        if \"quest\" in outcome.lower():\n            quest = outcome.split(\"quest\")[1].split(\".\")[0].strip()\n            self.game_state.update(quest=quest)\n        self.game_state.update(exp_gain=5)\n

      This is a simpler way to do it, but we will just look for keywords in the AI's response to determine what changes to make. This isn't the most consistent way to do it, but is easy to do and will easily allow the game to respond to the player's actions, making the experience feel more dynamic.

      "},{"location":"examples/ai_dungeon_master/#processing-story-elements","title":"Processing Story Elements","text":"

      Let's process the AI-generated story to extract content and any special flags.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def process_story(self, story_generator) -> Tuple[str, List[str]]:\n        story = self.print_stream(story_generator, print_output=True)\n        story_lines = story.split('\\n')\n\n        flags = []\n        for line in reversed(story_lines):\n            if line.strip().startswith('[') and line.strip().endswith(']'):\n                flags.append(line.strip('[').strip(']'))\n                story_lines.remove(line)\n            else:\n                break\n\n        story_content = '\\n'.join(story_lines).strip()\n\n        if any(flag.startswith(\"NPC_INTERACTION:\") for flag in flags):\n            npc_name = next(flag.split(':')[1].strip() for flag in flags if flag.startswith(\"NPC_INTERACTION:\"))\n            return story_content, npc_name\n        else:\n            return story_content, flags\n

      Here is where we'll actually separates the special tags we defined earlier from the story content and ensure the player sees a coherent story without tags.

      "},{"location":"examples/ai_dungeon_master/#printing-streamed-content","title":"Printing Streamed Content","text":"

      We also don't want to wait until the whole content is generated to print, so let's define a function to display the AI's response in real-time, simulating typing.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def print_stream(self, stream, print_output=True) -> str:\n        full_text = \"\"\n        for chunk in stream:\n            if print_output:\n                print(chunk, end='', flush=True)\n            full_text += chunk\n            time.sleep(0.03)\n        if print_output:\n            print()\n        return full_text\n
      "},{"location":"examples/ai_dungeon_master/#main-game-loop","title":"Main Game Loop","text":"

      Finally, we bring everything together in the play_game method.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def play_game(self):\n        print(\"Welcome to the Dungeon!\")\n        character = self.create_character()\n        self.game_state = GameState(character)\n\n        print(\"\\nYour adventure begins...\")\n        while True:\n            print_separator()\n            environment_description, env_flags = self.process_story(self.generate_environment())\n\n            if \"INTERACT_OPPORTUNITY\" in env_flags:\n                print(\"\\nThere seems to be an opportunity to interact.\")\n            if \"QUEST_OPPORTUNITY\" in env_flags:\n                print(\"\\nThere might be a quest available.\")\n\n            action = input(\"\\nWhat do you do? \")\n            if action.lower() == \"quit\":\n                break\n\n            print(\"\\nOutcome:\")\n            outcome, npc_interaction = self.process_story(self.handle_player_action(action))\n\n            self.update_game_state(outcome)\n\n            if npc_interaction:\n                self.handle_conversation(npc_interaction)\n\n            print_separator()\n            print(f\"Current state: {str(self.game_state)}\")\n\n            if self.game_state.health <= 0:\n                print(\"Game Over! Your health reached 0.\")\n                break\n\n            if hasattr(self, 'current_environment'):\n                del self.current_environment\n

      The game loop continuously processes player actions and updates the game state, new environments are generated to keep the game dynamic and the player is allowed to quit whenever they want.

      Plus, the game is over if health reaches zero.

      "},{"location":"examples/ai_dungeon_master/#helper-methods","title":"Helper Methods","text":"

      Let's also create some methods for improved user experience, we want to separate content to make it easier to see and also create a print_slowly to simulate streamed content in important messages.

      ai_dungeon_master/utils/text_utils.py
      import time\n\ndef print_separator(self):\n    print(\"\\n\" + \"=\" * 50 + \"\\n\")\n\ndef print_slowly(text, delay=0.03):\n    for char in text:\n        print(char, end='', flush=True)\n        time.sleep(delay)\n    print()\n
      "},{"location":"examples/ai_dungeon_master/#6-main-script-that-runs-the-game","title":"6. Main Script that Runs the Game","text":"

      At our main script, we initialize and start the game.

      ai_dungeon_master/main.py
      from game.dungeon_master import EnhancedAIDungeonMaster\nfrom utils.text_utils import print_slowly\nfrom ai.ollama_server import start_ollama_server\n\ndef main():\n    print_slowly(\"Welcome to the AI Dungeon Master!\")\n    print_slowly(\"Prepare for an adventure guided by multiple AI models.\")\n    print_slowly(\"Type 'quit' at any time to exit the game.\")\n    print()\n\n    # Start the Ollama server before the game begins\n    ollama_process = start_ollama_server()\n\n    game = EnhancedAIDungeonMaster()\n    game.play_game()\n\n    print_slowly(\"Thank you for playing AI Dungeon Master!\")\n\n    # Terminate the Ollama server when the game ends\n    if ollama_process:\n        ollama_process.terminate()\n\nif __name__ == \"__main__\":\n    main()\n
      "},{"location":"examples/ai_dungeon_master/#7-running-the-game","title":"7. Running the Game","text":"
      1. Ensure you're in the root directory of the project.

      2. Run the game using Poetry:

      poetry run python ai_dungeon_master/main.py\n

      Or directly if you used pip:

      python ai_dungeon_master/main.py\n

      This command will execute the main.py file, which should contain the game initialization and main loop.

      "},{"location":"examples/ai_dungeon_master/#8-conclusion-and-further-improvements","title":"8. Conclusion and Further Improvements","text":"

      Congratulations! You've now created an AI Dungeon Master using the ClientAI package. This project demonstrates how to integrate multiple AI providers and manage game logic to create a dynamic and engaging text-based RPG.

      "},{"location":"examples/ai_dungeon_master/#potential-improvements","title":"Potential Improvements:","text":"
      1. Error Handling: Implement try-except blocks to handle exceptions and improve robustness.
      2. Saving and Loading: Add functionality to save and load game states.
      3. Combat System: Develop a combat system that uses character stats and AI to determine outcomes.
      4. Quest Management: Create a more complex quest system with objectives and rewards.
      5. Multiplayer: Explore options for multiplayer interactions.
      6. User Interface: Develop a GUI for a more user-friendly experience.
      7. AI Fine-Tuning: Customize AI models for more consistent and relevant responses.

      By implementing these improvements, you can further enhance the gameplay experience and create an even more immersive and engaging AI-driven RPG.

      "},{"location":"examples/overview/","title":"Examples Overview","text":"

      Welcome to the Examples section of the ClientAI documentation. This section provides practical, real-world examples of how to use ClientAI in various applications. Whether you're a beginner looking to get started or an experienced developer seeking inspiration for more complex projects, these examples will demonstrate the versatility and power of ClientAI.

      "},{"location":"examples/overview/#featured-examples","title":"Featured Examples","text":"

      Our examples cover a range of applications, from simple text generation to more complex AI-driven systems. Here's an overview of what you'll find in this section:

      1. AI Dungeon Master: A text-based RPG that uses multiple AI providers to create an interactive storytelling experience.

        • AI Dungeon Master Tutorial
      2. Chatbot Assistant: A simple chatbot that can answer questions and engage in conversation using ClientAI.

        • Soon
      3. Sentiment Analyzer: An application that analyzes the sentiment of given text using different AI models.

        • Soon
      "},{"location":"examples/overview/#usage","title":"Usage","text":"

      Each example is documented on its own page, where you'll find:

      "},{"location":"examples/overview/#quick-start-example","title":"Quick Start Example","text":"

      Here's a simple example to get you started with ClientAI:

      from clientai import ClientAI\n\n# Initialize the client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate a short story\nprompt = \"Write a short story about a robot learning to paint.\"\nresponse = client.generate_text(prompt, model=\"gpt-3.5-turbo\")\n\nprint(response)\n

      For more general usage instructions, please refer to our Quickstart Guide.

      "},{"location":"examples/overview/#customizing-examples","title":"Customizing Examples","text":"

      Feel free to use these examples as starting points for your own projects. You can modify and extend them to suit your specific needs. If you create an interesting project using ClientAI, we'd love to hear about it!

      "},{"location":"examples/overview/#contributing","title":"Contributing","text":"

      We welcome contributions to our examples collection! If you've created an example that you think would be valuable to others, please consider submitting it. Check out our Contributing Guidelines for more information on how to contribute.

      "},{"location":"examples/overview/#feedback","title":"Feedback","text":"

      Your feedback helps us improve our examples and documentation. If you have suggestions for new examples, improvements to existing ones, or any other feedback, please let us know through GitHub issues or our community channels.

      Explore each example to see ClientAI in action and learn how to implement AI-driven features in your own projects.

      "},{"location":"usage/chat_functionality/","title":"Chat Functionality in ClientAI","text":"

      This guide covers how to leverage ClientAI's chat functionality. You'll learn about creating chat conversations, managing context, and handling chat-specific features across supported providers.

      "},{"location":"usage/chat_functionality/#table-of-contents","title":"Table of Contents","text":"
      1. Basic Chat Interaction
      2. Managing Conversation Context
      3. Advanced Chat Features
      4. Provider-Specific Chat Capabilities
      5. Best Practices
      "},{"location":"usage/chat_functionality/#basic-chat-interaction","title":"Basic Chat Interaction","text":"

      To use the chat functionality in ClientAI, use the chat method:

      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\nmessages = [\n    {\"role\": \"user\", \"content\": \"Hello, who are you?\"}\n]\n\nresponse = client.chat(messages, model=\"gpt-3.5-turbo\")\nprint(response)\n\n# Continue the conversation\nmessages.append({\"role\": \"assistant\", \"content\": response})\nmessages.append({\"role\": \"user\", \"content\": \"What can you help me with?\"})\n\nresponse = client.chat(messages, model=\"gpt-3.5-turbo\")\nprint(response)\n

      This example demonstrates a simple back-and-forth conversation.

      "},{"location":"usage/chat_functionality/#managing-conversation-context","title":"Managing Conversation Context","text":"

      Effective context management is crucial for coherent conversations:

      conversation = [\n    {\"role\": \"system\", \"content\": \"You are a helpful assistant specializing in Python programming.\"},\n    {\"role\": \"user\", \"content\": \"How do I use list comprehensions in Python?\"}\n]\n\nresponse = client.chat(conversation, model=\"gpt-3.5-turbo\")\nprint(response)\n\nconversation.append({\"role\": \"assistant\", \"content\": response})\nconversation.append({\"role\": \"user\", \"content\": \"Can you give an example?\"})\n\nresponse = client.chat(conversation, model=\"gpt-3.5-turbo\")\nprint(response)\n

      This example shows how to maintain context across multiple exchanges, including a system message to set the assistant's role.

      "},{"location":"usage/chat_functionality/#advanced-chat-features","title":"Advanced Chat Features","text":""},{"location":"usage/chat_functionality/#streaming-chat-responses","title":"Streaming Chat Responses","text":"

      For real-time conversation, you can stream chat responses:

      conversation = [\n    {\"role\": \"user\", \"content\": \"Tell me a long story about space exploration\"}\n]\n\nfor chunk in client.chat(conversation, model=\"gpt-3.5-turbo\", stream=True):\n    print(chunk, end=\"\", flush=True)\n
      "},{"location":"usage/chat_functionality/#temperature-and-top-p-sampling","title":"Temperature and Top-p Sampling","text":"

      Adjust the creativity and randomness of responses:

      response = client.chat(\n    conversation,\n    model=\"gpt-3.5-turbo\",\n    temperature=0.7,\n    top_p=0.9\n)\n
      "},{"location":"usage/chat_functionality/#provider-specific-chat-capabilities","title":"Provider-Specific Chat Capabilities","text":"

      Different providers may offer unique chat features:

      "},{"location":"usage/chat_functionality/#openai","title":"OpenAI","text":"
      openai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\n\nresponse = openai_client.chat(\n    [{\"role\": \"user\", \"content\": \"Translate 'Hello, world!' to Japanese\"}],\n    model=\"gpt-4\"\n)\n
      "},{"location":"usage/chat_functionality/#replicate","title":"Replicate","text":"
      replicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\n\nresponse = replicate_client.chat(\n    [{\"role\": \"user\", \"content\": \"Explain quantum computing\"}],\n    model=\"meta/llama-2-70b-chat:latest\"\n)\n
      "},{"location":"usage/chat_functionality/#ollama","title":"Ollama","text":"
      ollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n\nresponse = ollama_client.chat(\n    [{\"role\": \"user\", \"content\": \"What are the three laws of robotics?\"}],\n    model=\"llama2\"\n)\n
      "},{"location":"usage/chat_functionality/#best-practices","title":"Best Practices","text":"
      1. Context Management: Keep track of the conversation history, but be mindful of token limits.
      max_context_length = 10\nif len(conversation) > max_context_length:\n    conversation = conversation[-max_context_length:]\n
      1. Error Handling: Implement robust error handling for chat interactions:
      try:\n    response = client.chat(conversation, model=\"gpt-3.5-turbo\")\nexcept Exception as e:\n    print(f\"An error occurred during chat: {e}\")\n    response = \"I'm sorry, I encountered an error. Could you please try again?\"\n
      1. User Input Validation: Validate and sanitize user inputs to prevent potential issues:
      def sanitize_input(user_input):\n    # Implement appropriate sanitization logic\n    return user_input.strip()\n\nuser_message = sanitize_input(input(\"Your message: \"))\nconversation.append({\"role\": \"user\", \"content\": user_message})\n
      1. Graceful Fallbacks: Implement fallback mechanisms for when the AI doesn't understand or can't provide a suitable response:
      if not response or response.lower() == \"i don't know\":\n    response = \"I'm not sure about that. Could you please rephrase or ask something else?\"\n
      1. Model Selection: Choose appropriate models based on the complexity of your chat application:
      model = \"gpt-4\" if complex_conversation else \"gpt-3.5-turbo\"\nresponse = client.chat(conversation, model=model)\n
      1. Conversation Resetting: Provide options to reset or start new conversations:
      def reset_conversation():\n    return [{\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}]\n\n# Usage\nconversation = reset_conversation()\n

      By following these guidelines and exploring the various features available, you can create sophisticated chat applications using ClientAI across different AI providers.

      "},{"location":"usage/error_handling/","title":"Error Handling in ClientAI","text":"

      ClientAI provides a robust error handling system that unifies exceptions across different AI providers. This guide covers how to handle potential errors when using ClientAI.

      "},{"location":"usage/error_handling/#table-of-contents","title":"Table of Contents","text":"
      1. Exception Hierarchy
      2. Handling Errors
      3. Provider-Specific Error Mapping
      4. Best Practices
      "},{"location":"usage/error_handling/#exception-hierarchy","title":"Exception Hierarchy","text":"

      ClientAI uses a custom exception hierarchy to provide consistent error handling across different AI providers:

      from clientai.exceptions import (\n    ClientAIError,\n    AuthenticationError,\n    RateLimitError,\n    InvalidRequestError,\n    ModelError,\n    TimeoutError,\n    APIError\n)\n
      "},{"location":"usage/error_handling/#handling-errors","title":"Handling Errors","text":"

      Here's how to handle potential errors when using ClientAI:

      from clientai import ClientAI\nfrom clientai.exceptions import (\n    ClientAIError,\n    AuthenticationError,\n    RateLimitError,\n    InvalidRequestError,\n    ModelError,\n    TimeoutError,\n    APIError\n)\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\ntry:\n    response = client.generate_text(\"Tell me a joke\", model=\"gpt-3.5-turbo\")\n    print(f\"Generated text: {response}\")\nexcept AuthenticationError as e:\n    print(f\"Authentication error: {e}\")\nexcept RateLimitError as e:\n    print(f\"Rate limit exceeded: {e}\")\nexcept InvalidRequestError as e:\n    print(f\"Invalid request: {e}\")\nexcept ModelError as e:\n    print(f\"Model error: {e}\")\nexcept TimeoutError as e:\n    print(f\"Request timed out: {e}\")\nexcept APIError as e:\n    print(f\"API error: {e}\")\nexcept ClientAIError as e:\n    print(f\"An unexpected ClientAI error occurred: {e}\")\n
      "},{"location":"usage/error_handling/#provider-specific-error-mapping","title":"Provider-Specific Error Mapping","text":"

      ClientAI maps provider-specific errors to its custom exception hierarchy. For example:

      "},{"location":"usage/error_handling/#openai","title":"OpenAI","text":"
      def _map_exception_to_clientai_error(self, e: Exception) -> None:\n    error_message = str(e)\n    status_code = getattr(e, 'status_code', None)\n\n    if isinstance(e, OpenAIAuthenticationError) or \"incorrect api key\" in error_message.lower():\n        raise AuthenticationError(error_message, status_code, original_error=e)\n    elif status_code == 429 or \"rate limit\" in error_message.lower():\n        raise RateLimitError(error_message, status_code, original_error=e)\n    elif status_code == 404 or \"not found\" in error_message.lower():\n        raise ModelError(error_message, status_code, original_error=e)\n    elif status_code == 400 or \"invalid\" in error_message.lower():\n        raise InvalidRequestError(error_message, status_code, original_error=e)\n    elif status_code == 408 or \"timeout\" in error_message.lower():\n        raise TimeoutError(error_message, status_code, original_error=e)\n    elif status_code and status_code >= 500:\n        raise APIError(error_message, status_code, original_error=e)\n\n    raise ClientAIError(error_message, status_code, original_error=e)\n
      "},{"location":"usage/error_handling/#replicate","title":"Replicate","text":"
      def _map_exception_to_clientai_error(self, e: Exception, status_code: int = None) -> ClientAIError:\n    error_message = str(e)\n    status_code = status_code or getattr(e, 'status_code', None)\n\n    if \"authentication\" in error_message.lower() or \"unauthorized\" in error_message.lower():\n        return AuthenticationError(error_message, status_code, original_error=e)\n    elif \"rate limit\" in error_message.lower():\n        return RateLimitError(error_message, status_code, original_error=e)\n    elif \"not found\" in error_message.lower():\n        return ModelError(error_message, status_code, original_error=e)\n    elif \"invalid\" in error_message.lower():\n        return InvalidRequestError(error_message, status_code, original_error=e)\n    elif \"timeout\" in error_message.lower() or status_code == 408:\n        return TimeoutError(error_message, status_code, original_error=e)\n    elif status_code == 400:\n        return InvalidRequestError(error_message, status_code, original_error=e)\n    else:\n        return APIError(error_message, status_code, original_error=e)\n
      "},{"location":"usage/error_handling/#best-practices","title":"Best Practices","text":"
      1. Specific Exception Handling: Catch specific exceptions when you need to handle them differently.

      2. Logging: Log errors for debugging and monitoring purposes.

      import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ntry:\n    response = client.generate_text(\"Tell me a joke\", model=\"gpt-3.5-turbo\")\nexcept ClientAIError as e:\n    logger.error(f\"An error occurred: {e}\", exc_info=True)\n
      1. Retry Logic: Implement retry logic for transient errors like rate limiting.
      import time\nfrom clientai.exceptions import RateLimitError\n\ndef retry_generate(prompt, model, max_retries=3, delay=1):\n    for attempt in range(max_retries):\n        try:\n            return client.generate_text(prompt, model=model)\n        except RateLimitError as e:\n            if attempt == max_retries - 1:\n                raise\n            wait_time = e.retry_after if hasattr(e, 'retry_after') else delay * (2 ** attempt)\n            logger.warning(f\"Rate limit reached. Waiting for {wait_time} seconds...\")\n            time.sleep(wait_time)\n
      1. Graceful Degradation: Implement fallback options when errors occur.
      def generate_with_fallback(prompt, primary_client, fallback_client):\n    try:\n        return primary_client.generate_text(prompt, model=\"gpt-3.5-turbo\")\n    except ClientAIError as e:\n        logger.warning(f\"Primary client failed: {e}. Falling back to secondary client.\")\n        return fallback_client.generate_text(prompt, model=\"llama-2-70b-chat\")\n

      By following these practices and utilizing ClientAI's unified error handling system, you can create more robust and maintainable applications that gracefully handle errors across different AI providers.

      "},{"location":"usage/initialization/","title":"Initializing ClientAI","text":"

      This guide covers the process of initializing ClientAI with different AI providers. You'll learn how to set up ClientAI for use with OpenAI, Replicate, and Ollama.

      "},{"location":"usage/initialization/#table-of-contents","title":"Table of Contents","text":"
      1. Prerequisites
      2. OpenAI Initialization
      3. Replicate Initialization
      4. Ollama Initialization
      5. Multiple Provider Initialization
      6. Best Practices
      "},{"location":"usage/initialization/#prerequisites","title":"Prerequisites","text":"

      Before initializing ClientAI, ensure you have:

      1. Installed ClientAI: pip install clientai[all]
      2. Obtained necessary API keys for the providers you plan to use
      3. Basic understanding of Python and asynchronous programming
      "},{"location":"usage/initialization/#openai-initialization","title":"OpenAI Initialization","text":"

      To initialize ClientAI with OpenAI:

      from clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\n

      Replace \"your-openai-api-key\" with your actual OpenAI API key.

      "},{"location":"usage/initialization/#replicate-initialization","title":"Replicate Initialization","text":"

      To initialize ClientAI with Replicate:

      from clientai import ClientAI\n\nreplicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\n

      Replace \"your-replicate-api-key\" with your actual Replicate API key.

      "},{"location":"usage/initialization/#ollama-initialization","title":"Ollama Initialization","text":"

      To initialize ClientAI with Ollama:

      from clientai import ClientAI\n\nollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n

      Ensure that you have Ollama running locally on the specified host.

      "},{"location":"usage/initialization/#multiple-provider-initialization","title":"Multiple Provider Initialization","text":"

      You can initialize multiple providers in the same script:

      from clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\nreplicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\nollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n
      "},{"location":"usage/initialization/#best-practices","title":"Best Practices","text":"
      1. Environment Variables: Store API keys in environment variables instead of hardcoding them in your script:
      import os\nfrom clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=os.getenv('OPENAI_API_KEY'))\n
      1. Error Handling: Wrap initialization in a try-except block to handle potential errors:
      try:\n    client = ClientAI('openai', api_key=\"your-openai-api-key\")\nexcept ValueError as e:\n    print(f\"Error initializing ClientAI: {e}\")\n
      1. Configuration Files: For projects with multiple providers, consider using a configuration file:
      import json\nfrom clientai import ClientAI\n\nwith open('config.json') as f:\n    config = json.load(f)\n\nopenai_client = ClientAI('openai', **config['openai'])\nreplicate_client = ClientAI('replicate', **config['replicate'])\n
      1. Lazy Initialization: If you're not sure which provider you'll use, initialize clients only when needed:
      def get_client(provider):\n    if provider == 'openai':\n        return ClientAI('openai', api_key=\"your-openai-api-key\")\n    elif provider == 'replicate':\n        return ClientAI('replicate', api_key=\"your-replicate-api-key\")\n    # ... other providers ...\n\n# Use the client when needed\nclient = get_client('openai')\n

      By following these initialization guidelines, you'll be well-prepared to start using ClientAI with various AI providers in your projects.

      "},{"location":"usage/multiple_providers/","title":"Working with Multiple Providers in ClientAI","text":"

      This guide explores techniques for effectively using multiple AI providers within a single project using ClientAI. You'll learn how to switch between providers and leverage their unique strengths.

      "},{"location":"usage/multiple_providers/#table-of-contents","title":"Table of Contents","text":"
      1. Setting Up Multiple Providers
      2. Switching Between Providers
      3. Leveraging Provider Strengths
      4. Load Balancing and Fallback Strategies
      5. Best Practices
      "},{"location":"usage/multiple_providers/#setting-up-multiple-providers","title":"Setting Up Multiple Providers","text":"

      First, initialize ClientAI with multiple providers:

      from clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\nreplicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\nollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n
      "},{"location":"usage/multiple_providers/#switching-between-providers","title":"Switching Between Providers","text":"

      Create a function to switch between providers based on your requirements:

      def get_provider(task):\n    if task == \"translation\":\n        return openai_client\n    elif task == \"code_generation\":\n        return replicate_client\n    elif task == \"local_inference\":\n        return ollama_client\n    else:\n        return openai_client  # Default to OpenAI\n\n# Usage\ntask = \"translation\"\nprovider = get_provider(task)\nresponse = provider.generate_text(\"Translate 'Hello' to French\", model=\"gpt-3.5-turbo\")\n

      This approach allows you to dynamically select the most appropriate provider for each task.

      "},{"location":"usage/multiple_providers/#leveraging-provider-strengths","title":"Leveraging Provider Strengths","text":"

      Different providers excel in different areas. Here's how you can leverage their strengths:

      def translate_text(text, target_language):\n    return openai_client.generate_text(\n        f\"Translate '{text}' to {target_language}\",\n        model=\"gpt-3.5-turbo\"\n    )\n\ndef generate_code(prompt):\n    return replicate_client.generate_text(\n        prompt,\n        model=\"meta/llama-2-70b-chat:latest\"\n    )\n\ndef local_inference(prompt):\n    return ollama_client.generate_text(\n        prompt,\n        model=\"llama2\"\n    )\n\n# Usage\nfrench_text = translate_text(\"Hello, world!\", \"French\")\npython_code = generate_code(\"Write a Python function to calculate the Fibonacci sequence\")\nquick_response = local_inference(\"What's the capital of France?\")\n
      "},{"location":"usage/multiple_providers/#load-balancing-and-fallback-strategies","title":"Load Balancing and Fallback Strategies","text":"

      Implement load balancing and fallback strategies to ensure reliability:

      import random\n\nproviders = [openai_client, replicate_client, ollama_client]\n\ndef load_balanced_generate(prompt, max_retries=3):\n    for _ in range(max_retries):\n        try:\n            provider = random.choice(providers)\n            return provider.generate_text(prompt, model=provider.default_model)\n        except Exception as e:\n            print(f\"Error with provider {provider.__class__.__name__}: {e}\")\n    raise Exception(\"All providers failed after max retries\")\n\n# Usage\ntry:\n    response = load_balanced_generate(\"Explain the concept of machine learning\")\n    print(response)\nexcept Exception as e:\n    print(f\"Failed to generate text: {e}\")\n

      This function randomly selects a provider and falls back to others if there's an error.

      "},{"location":"usage/multiple_providers/#best-practices","title":"Best Practices","text":"
      1. Provider Selection Logic: Develop clear criteria for selecting providers based on task requirements, cost, and performance.
      def select_provider(task, complexity, budget):\n    if complexity == \"high\" and budget == \"high\":\n        return openai_client  # Assuming OpenAI has more advanced models\n    elif task == \"code\" and budget == \"medium\":\n        return replicate_client\n    else:\n        return ollama_client  # Assuming Ollama is the most cost-effective\n
      1. Consistent Interface: Create wrapper functions to provide a consistent interface across providers:
      def unified_generate(prompt, provider=None):\n    if provider is None:\n        provider = get_default_provider()\n    return provider.generate_text(prompt, model=provider.default_model)\n\n# Usage\nresponse = unified_generate(\"Explain quantum computing\")\n
      1. Error Handling and Logging: Implement comprehensive error handling and logging when working with multiple providers:
      import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ndef safe_generate(prompt, provider):\n    try:\n        return provider.generate_text(prompt, model=provider.default_model)\n    except Exception as e:\n        logger.error(f\"Error with {provider.__class__.__name__}: {e}\")\n        return None\n
      1. Performance Monitoring: Track the performance of different providers to optimize selection:
      import time\n\ndef timed_generate(prompt, provider):\n    start_time = time.time()\n    result = provider.generate_text(prompt, model=provider.default_model)\n    elapsed_time = time.time() - start_time\n    logger.info(f\"{provider.__class__.__name__} took {elapsed_time:.2f} seconds\")\n    return result\n
      1. Configuration Management: Use configuration files or environment variables to manage provider settings:
      import os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nopenai_client = ClientAI('openai', api_key=os.getenv('OPENAI_API_KEY'))\nreplicate_client = ClientAI('replicate', api_key=os.getenv('REPLICATE_API_KEY'))\nollama_client = ClientAI('ollama', host=os.getenv('OLLAMA_HOST'))\n
      1. Caching: Implement caching to reduce redundant API calls and improve response times:
      from functools import lru_cache\n\n@lru_cache(maxsize=100)\ndef cached_generate(prompt, provider_name):\n    provider = get_provider(provider_name)\n    return provider.generate_text(prompt, model=provider.default_model)\n\n# Usage\nresponse = cached_generate(\"What is the speed of light?\", \"openai\")\n

      By following these practices and leveraging the strengths of multiple providers, you can create more robust, efficient, and versatile applications with ClientAI.

      "},{"location":"usage/ollama_manager/","title":"Ollama Manager Guide","text":""},{"location":"usage/ollama_manager/#introduction","title":"Introduction","text":"

      Ollama Manager provides a streamlined way to prototype and develop applications using Ollama's AI models. Instead of manually managing the Ollama server process, installing it as a service, or running it in a separate terminal, Ollama Manager handles the entire lifecycle programmatically.

      Key Benefits for Prototyping: - Start/stop Ollama server automatically within your Python code - Configure resources dynamically based on your needs - Handle multiple server instances for testing - Automatic cleanup of resources - Platform-independent operation

      "},{"location":"usage/ollama_manager/#quick-start","title":"Quick Start","text":"
      from clientai import ClientAI\nfrom clientai.ollama import OllamaManager\n\n# Basic usage - server starts automatically and stops when done\nwith OllamaManager() as manager:\n    # Create a client that connects to the managed server\n    client = ClientAI('ollama', host=\"http://localhost:11434\")\n\n    # Use the client normally\n    response = client.generate_text(\n        \"Explain quantum computing\",\n        model=\"llama2\"\n    )\n    print(response)\n# Server automatically stops when exiting the context\n
      "},{"location":"usage/ollama_manager/#installation","title":"Installation","text":"
      # Install with Ollama support\npip install \"clientai[ollama]\"\n\n# Install with all providers\npip install \"clientai[all]\"\n
      "},{"location":"usage/ollama_manager/#core-concepts","title":"Core Concepts","text":""},{"location":"usage/ollama_manager/#server-lifecycle-management","title":"Server Lifecycle Management","text":"
      1. Context Manager (Recommended)

        with OllamaManager() as manager:\n    # Server starts automatically\n    client = ClientAI('ollama')\n    # Use client...\n# Server stops automatically\n

      2. Manual Management

        manager = OllamaManager()\ntry:\n    manager.start()\n    client = ClientAI('ollama')\n    # Use client...\nfinally:\n    manager.stop()\n

      "},{"location":"usage/ollama_manager/#configuration-management","title":"Configuration Management","text":"
      from clientai.ollama import OllamaServerConfig\n\n# Create custom configuration\nconfig = OllamaServerConfig(\n    host=\"127.0.0.1\",\n    port=11434,\n    gpu_layers=35,\n    memory_limit=\"8GiB\"\n)\n\n# Use configuration with manager\nwith OllamaManager(config) as manager:\n    client = ClientAI('ollama')\n    # Use client...\n
      "},{"location":"usage/ollama_manager/#resource-configuration-guide","title":"Resource Configuration Guide","text":""},{"location":"usage/ollama_manager/#gpu-configuration","title":"GPU Configuration","text":"
      1. Basic GPU Setup

        config = OllamaServerConfig(\n    gpu_layers=35,  # Number of layers to run on GPU\n    gpu_memory_fraction=0.8  # 80% of GPU memory\n)\n

      2. Multi-GPU Setup

        config = OllamaServerConfig(\n    gpu_devices=[0, 1],  # Use first two GPUs\n    gpu_memory_fraction=0.7\n)\n

      "},{"location":"usage/ollama_manager/#memory-management","title":"Memory Management","text":"

      The memory_limit parameter requires specific formatting following Go's memory limit syntax:

      # Correct memory limit formats\nconfig = OllamaServerConfig(\n    memory_limit=\"8GiB\"    # 8 gibibytes\n    # OR\n    memory_limit=\"8192MiB\" # Same as 8GiB\n)\n\n# Invalid formats that will cause errors\nconfig = OllamaServerConfig(\n    memory_limit=\"8GiB\"     # Wrong unit\n    memory_limit=\"8 GiB\"   # No spaces allowed\n    memory_limit=\"8g\"      # Must specify full unit\n)\n

      Valid Memory Units: - B - Bytes - KiB - Kibibytes (1024 bytes) - MiB - Mebibytes (1024\u00b2 bytes) - GiB - Gibibytes (1024\u00b3 bytes) - TiB - Tebibytes (1024\u2074 bytes)

      Common Configurations:

      1. High-Performance Setup

        config = OllamaServerConfig(\n    memory_limit=\"24GiB\",\n    cpu_threads=16,\n    gpu_layers=40\n)\n

      2. Balanced Setup

        config = OllamaServerConfig(\n    memory_limit=\"16GiB\",\n    cpu_threads=8,\n    gpu_layers=32\n)\n

      3. Resource-Constrained Setup

        config = OllamaServerConfig(\n    memory_limit=\"8GiB\",\n    cpu_threads=4,\n    gpu_layers=24\n)\n

      4. Dynamic Memory Allocation

        import psutil\n\n# Calculate available memory and use 70%\navailable_gib = (psutil.virtual_memory().available / (1024**3))\nmemory_limit = f\"{int(available_gib * 0.7)}GiB\"\n\nconfig = OllamaServerConfig(\n    memory_limit=memory_limit,\n    cpu_threads=8\n)\n

      "},{"location":"usage/ollama_manager/#model-specific-memory-guidelines","title":"Model-Specific Memory Guidelines","text":"

      Different models require different amounts of memory:

      1. Large Language Models (>30B parameters)

        config = OllamaServerConfig(\n    memory_limit=\"24GiB\",\n    gpu_memory_fraction=0.9\n)\n

      2. Medium Models (7B-30B parameters)

        config = OllamaServerConfig(\n    memory_limit=\"16GiB\",\n    gpu_memory_fraction=0.8\n)\n

      3. Small Models (<7B parameters)

        config = OllamaServerConfig(\n    memory_limit=\"8GiB\",\n    gpu_memory_fraction=0.7\n)\n

      "},{"location":"usage/ollama_manager/#advanced-configuration-reference","title":"Advanced Configuration Reference","text":"
      config = OllamaServerConfig(\n    # Server settings\n    host=\"127.0.0.1\",      # Server bind address\n    port=11434,            # Server port number\n    timeout=30,            # Maximum startup wait time in seconds\n    check_interval=1.0,    # Health check interval in seconds\n\n    # GPU settings\n    gpu_layers=35,          # More layers = more GPU utilization\n    gpu_memory_fraction=0.8, # 0.0 to 1.0 (80% GPU memory)\n    gpu_devices=[0],        # Specific GPU devices to use\n\n    # CPU settings\n    cpu_threads=8,          # Number of CPU threads\n    memory_limit=\"16GiB\",   # Maximum RAM usage (must use GiB/MiB units)\n\n    # Compute settings\n    compute_unit=\"auto\",    # \"auto\", \"cpu\", or \"gpu\"\n\n    # Additional settings\n    env_vars={\"CUSTOM_VAR\": \"value\"},  # Additional environment variables\n    extra_args=[\"--verbose\"]           # Additional command line arguments\n)\n

      Each setting explained:

      Server Settings: - host: IP address to bind the server to - Default: \"127.0.0.1\" (localhost) - Use \"0.0.0.0\" to allow external connections - port: Port number for the server - Default: 11434 - Change if default port is in use - timeout: Maximum time to wait for server startup - Unit: seconds - Increase for slower systems - check_interval: Time between server health checks - Unit: seconds - Adjust based on system responsiveness

      GPU Settings: - gpu_layers: Number of model layers to offload to GPU - Higher = more GPU utilization - Lower = more CPU utilization - Model-dependent (typically 24-40) - gpu_memory_fraction: Portion of GPU memory to use - Range: 0.0 to 1.0 - Higher values may improve performance - Lower values leave room for other applications - gpu_devices: Specific GPU devices to use - Single GPU: gpu_devices=0 - Multiple GPUs: gpu_devices=[0, 1] - None: gpu_devices=None

      CPU Settings: - cpu_threads: Number of CPU threads to use - Default: System dependent - Recommended: Leave some threads for system - Example: os.cpu_count() - 2 - memory_limit: Maximum RAM allocation - Must use GiB or MiB units - Examples: \"8GiB\", \"16384MiB\" - Should not exceed available system RAM

      Compute Settings: - compute_unit: Preferred compute device - \"auto\": Let Ollama decide (recommended) - \"cpu\": Force CPU-only operation - \"gpu\": Force GPU operation if available

      Additional Settings: - env_vars: Additional environment variables - Used for platform-specific settings - Example: {\"CUDA_VISIBLE_DEVICES\": \"0,1\"} - extra_args: Additional CLI arguments - Passed directly to Ollama server - Example: [\"--verbose\", \"--debug\"]

      ## Common Use Cases\n\n### 1. Development and Prototyping\n\n```python\n# Quick setup for development\nwith OllamaManager() as manager:\n    client = ClientAI('ollama')\n\n    # Test different prompts\n    prompts = [\n        \"Write a poem about AI\",\n        \"Explain quantum physics\",\n        \"Create a Python function\"\n    ]\n\n    for prompt in prompts:\n        response = client.generate_text(prompt, model=\"llama2\")\n        print(f\"Prompt: {prompt}\\nResponse: {response}\\n\")\n

      "},{"location":"usage/ollama_manager/#2-multiple-model-testing","title":"2. Multiple Model Testing","text":"
      # Test different models with different configurations\nmodels = [\"llama2\", \"codellama\", \"mistral\"]\n\nfor model in models:\n    # Adjust configuration based on model\n    if model == \"llama2\":\n        config = OllamaServerConfig(gpu_layers=35)\n    else:\n        config = OllamaServerConfig(gpu_layers=28)\n\n    with OllamaManager(config) as manager:\n        client = ClientAI('ollama')\n        response = client.generate_text(\n            \"Explain how you work\",\n            model=model\n        )\n        print(f\"{model}: {response}\\n\")\n
      "},{"location":"usage/ollama_manager/#3-ab-testing-configurations","title":"3. A/B Testing Configurations","text":"
      def test_configuration(config, prompt, model):\n    start_time = time.time()\n\n    with OllamaManager(config) as manager:\n        client = ClientAI('ollama')\n        response = client.generate_text(prompt, model=model)\n\n    duration = time.time() - start_time\n    return response, duration\n\n# Test different configurations\nconfigs = {\n    \"high_gpu\": OllamaServerConfig(gpu_layers=40, gpu_memory_fraction=0.9),\n    \"balanced\": OllamaServerConfig(gpu_layers=32, gpu_memory_fraction=0.7),\n    \"low_resource\": OllamaServerConfig(gpu_layers=24, gpu_memory_fraction=0.5)\n}\n\nfor name, config in configs.items():\n    response, duration = test_configuration(\n        config,\n        \"Write a long story about space\",\n        \"llama2\"\n    )\n    print(f\"Configuration {name}: {duration:.2f} seconds\")\n
      "},{"location":"usage/ollama_manager/#4-production-setup","title":"4. Production Setup","text":"
      import logging\n\nlogging.basicConfig(level=logging.INFO)\n\ndef create_production_manager():\n    config = OllamaServerConfig(\n        # Stable production settings\n        gpu_layers=32,\n        gpu_memory_fraction=0.7,\n        memory_limit=\"16GiB\",\n        timeout=60,  # Longer timeout for stability\n        check_interval=2.0\n    )\n\n    try:\n        manager = OllamaManager(config)\n        manager.start()\n        return manager\n    except Exception as e:\n        logging.error(f\"Failed to start Ollama: {e}\")\n        raise\n\n# Use in production\ntry:\n    manager = create_production_manager()\n    client = ClientAI('ollama')\n    # Use client...\nfinally:\n    manager.stop()\n
      "},{"location":"usage/ollama_manager/#error-handling","title":"Error Handling","text":"

      Ollama Manager provides several specific exception types to help you handle different error scenarios effectively:

      "},{"location":"usage/ollama_manager/#executablenotfounderror","title":"ExecutableNotFoundError","text":"

      Occurs when the Ollama executable cannot be found on the system.

      Common causes: - Ollama not installed - Ollama not in system PATH - Incorrect installation

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept ExecutableNotFoundError:\n    # Guide user through installation\n    if platform.system() == \"Darwin\":\n        print(\"Install Ollama using: brew install ollama\")\n    elif platform.system() == \"Linux\":\n        print(\"Install Ollama using: curl -fsSL https://ollama.com/install.sh | sh\")\n    else:\n        print(\"Download Ollama from: https://ollama.com/download\")\n

      "},{"location":"usage/ollama_manager/#serverstartuperror","title":"ServerStartupError","text":"

      Occurs when the server fails to start properly.

      Common causes: - Port already in use - Insufficient permissions - Corrupt installation - Resource conflicts

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept ServerStartupError as e:\n    if \"address already in use\" in str(e).lower():\n        # Try alternative port\n        config = OllamaServerConfig(port=11435)\n        manager = OllamaManager(config)\n        manager.start()\n    elif \"permission denied\" in str(e).lower():\n        print(\"Please run with appropriate permissions\")\n    else:\n        print(f\"Server startup failed: {e}\")\n

      "},{"location":"usage/ollama_manager/#servertimeouterror","title":"ServerTimeoutError","text":"

      Occurs when the server doesn't respond within the configured timeout period.

      Common causes: - System under heavy load - Insufficient resources - Network issues - Too short timeout period

      How to handle:

      config = OllamaServerConfig(\n    timeout=60,  # Increase timeout\n    check_interval=2.0  # Reduce check frequency\n)\ntry:\n    manager = OllamaManager(config)\n    manager.start()\nexcept ServerTimeoutError:\n    # Either retry with longer timeout or fail gracefully\n    config.timeout = 120\n    try:\n        manager = OllamaManager(config)\n        manager.start()\n    except ServerTimeoutError:\n        print(\"Server unresponsive after extended timeout\")\n

      "},{"location":"usage/ollama_manager/#resourceerror","title":"ResourceError","text":"

      Occurs when there are insufficient system resources to run Ollama.

      Common causes: - Insufficient memory - GPU memory allocation failures - Too many CPU threads requested - Disk space constraints

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept ResourceError as e:\n    if \"memory\" in str(e).lower():\n        # Try with reduced memory settings\n        config = OllamaServerConfig(\n            memory_limit=\"4GiB\",\n            gpu_memory_fraction=0.5\n        )\n    elif \"gpu\" in str(e).lower():\n        # Fallback to CPU\n        config = OllamaServerConfig(compute_unit=\"cpu\")\n\n    try:\n        manager = OllamaManager(config)\n        manager.start()\n    except ResourceError:\n        print(\"Unable to allocate required resources\")\n

      "},{"location":"usage/ollama_manager/#configurationerror","title":"ConfigurationError","text":"

      Occurs when provided configuration values are invalid.

      Common causes: - Invalid memory format - Invalid GPU configuration - Incompatible settings - Out-of-range values

      How to handle:

      try:\n    config = OllamaServerConfig(\n        gpu_memory_fraction=1.5  # Invalid value\n    )\nexcept ConfigurationError as e:\n    # Use safe default values\n    config = OllamaServerConfig(\n        gpu_memory_fraction=0.7,\n        gpu_layers=32\n    )\n

      "},{"location":"usage/ollama_manager/#unsupportedplatformerror","title":"UnsupportedPlatformError","text":"

      Occurs when running on an unsupported platform or configuration.

      Common causes: - Unsupported operating system - Missing system features - Incompatible hardware

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept UnsupportedPlatformError as e:\n    # Fall back to alternative configuration or inform user\n    print(f\"Platform not supported: {e}\")\n    print(\"Supported platforms: Windows, macOS, Linux\")\n

      "},{"location":"usage/ollama_manager/#memory-related-error-handling","title":"Memory-Related Error Handling","text":"
      def start_with_memory_fallback():\n    try:\n        # Try optimal memory configuration\n        config = OllamaServerConfig(memory_limit=\"16GiB\")\n        return OllamaManager(config)\n    except ServerStartupError as e:\n        if \"GOMEMLIMIT\" in str(e):\n            # Fall back to lower memory configuration\n            config = OllamaServerConfig(memory_limit=\"8GiB\")\n            return OllamaManager(config)\n        raise  # Re-raise if error is not memory-related\n
      "},{"location":"usage/ollama_manager/#best-practices-for-error-handling","title":"Best Practices for Error Handling","text":"
      1. Graceful Degradation

        def start_with_fallback():\n    # Try optimal configuration first\n    config = OllamaServerConfig(\n        gpu_layers=35,\n        gpu_memory_fraction=0.8\n    )\n\n    try:\n        return OllamaManager(config)\n    except ResourceError:\n        # Fall back to minimal configuration\n        config = OllamaServerConfig(\n            gpu_layers=24,\n            gpu_memory_fraction=0.5\n        )\n        return OllamaManager(config)\n

      2. Logging and Monitoring

        import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ntry:\n    manager = OllamaManager()\n    manager.start()\nexcept ServerStartupError as e:\n    logger.error(f\"Server startup failed: {e}\")\n    logger.debug(f\"Full error details: {e.original_exception}\")\n

      3. Recovery Strategies

        def start_with_retry(max_attempts=3, delay=5):\n    for attempt in range(max_attempts):\n        try:\n            manager = OllamaManager()\n            manager.start()\n            return manager\n        except (ServerStartupError, ServerTimeoutError) as e:\n            if attempt == max_attempts - 1:\n                raise\n            logger.warning(f\"Attempt {attempt + 1} failed, retrying...\")\n            time.sleep(delay)\n

      Remember to always clean up resources properly, even when handling errors:

      manager = None\ntry:\n    manager = OllamaManager()\n    manager.start()\n    # Use manager...\nexcept Exception as e:\n    logger.error(f\"Error occurred: {e}\")\nfinally:\n    if manager is not None:\n        manager.stop()\n

      "},{"location":"usage/ollama_manager/#performance-monitoring","title":"Performance Monitoring","text":"
      import psutil\nfrom contextlib import contextmanager\nimport time\n\n@contextmanager\ndef monitor_performance():\n    start_time = time.time()\n    start_cpu = psutil.cpu_percent()\n    start_mem = psutil.virtual_memory().percent\n\n    yield\n\n    duration = time.time() - start_time\n    cpu_diff = psutil.cpu_percent() - start_cpu\n    mem_diff = psutil.virtual_memory().percent - start_mem\n\n    print(f\"Duration: {duration:.2f}s\")\n    print(f\"CPU impact: {cpu_diff:+.1f}%\")\n    print(f\"Memory impact: {mem_diff:+.1f}%\")\n\n# Use with Ollama Manager\nwith monitor_performance():\n    with OllamaManager() as manager:\n        client = ClientAI('ollama')\n        response = client.generate_text(\n            \"Write a story\",\n            model=\"llama2\"\n        )\n
      "},{"location":"usage/ollama_manager/#best-practices","title":"Best Practices","text":"
      1. Always use context managers when possible
      2. Start with conservative resource settings and adjust up
      3. Monitor system resources during development
      4. Implement proper error handling
      5. Use appropriate configurations for development vs. production
      6. Clean up resources properly in all code paths
      7. Log important events for troubleshooting
      8. Test different configurations to find optimal settings
      9. Consider platform-specific settings for cross-platform applications
      10. Implement graceful degradation when resources are constrained
      "},{"location":"usage/ollama_manager/#troubleshooting","title":"Troubleshooting","text":"

      Remember that Ollama Manager is a powerful tool for prototyping and development, but always monitor system resources and adjust configurations based on your specific needs and hardware capabilities.

      "},{"location":"usage/overview/","title":"Usage Overview","text":"

      This Usage section provides comprehensive guides on how to effectively use the key features of ClientAI. Each topic focuses on a specific aspect of usage, ensuring you have all the information needed to leverage the full potential of ClientAI in your projects.

      "},{"location":"usage/overview/#key-topics","title":"Key Topics","text":""},{"location":"usage/overview/#1-initializing-clientai","title":"1. Initializing ClientAI","text":"

      This guide covers the process of initializing ClientAI with different AI providers. It provides a step-by-step approach to setting up ClientAI for use with OpenAI, Replicate, and Ollama.

      "},{"location":"usage/overview/#2-text-generation-with-clientai","title":"2. Text Generation with ClientAI","text":"

      Learn how to use ClientAI for text generation tasks. This guide explores the various options and parameters available for generating text across different AI providers.

      "},{"location":"usage/overview/#3-chat-functionality-in-clientai","title":"3. Chat Functionality in ClientAI","text":"

      Discover how to leverage ClientAI's chat functionality. This guide covers creating chat conversations, managing context, and handling chat-specific features across supported providers.

      "},{"location":"usage/overview/#4-working-with-multiple-providers","title":"4. Working with Multiple Providers","text":"

      Explore techniques for effectively using multiple AI providers within a single project. This guide demonstrates how to switch between providers and leverage their unique strengths.

      "},{"location":"usage/overview/#5-using-ollama-manager","title":"5. Using Ollama Manager","text":"

      Learn how to use Ollama Manager for streamlined prototyping and development. This guide covers server lifecycle management, resource configuration, and best practices for different use cases.

      "},{"location":"usage/overview/#6-handling-responses-and-errors","title":"6. Handling Responses and Errors","text":"

      Learn best practices for handling responses from AI providers and managing potential errors. This guide covers response parsing, error handling, and retry strategies.

      "},{"location":"usage/overview/#getting-started","title":"Getting Started","text":"

      To make the most of these guides, we recommend familiarizing yourself with basic Python programming and asynchronous programming concepts, as ClientAI leverages these extensively.

      "},{"location":"usage/overview/#quick-start-example","title":"Quick Start Example","text":"

      Here's a simple example to get you started with ClientAI:

      from clientai import ClientAI\n\n# Initialize the client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Explain the concept of machine learning in simple terms.\",\n    model=\"gpt-3.5-turbo\"\n)\n\nprint(response)\n

      For more detailed examples and explanations, refer to the specific guides linked above.

      "},{"location":"usage/overview/#advanced-usage","title":"Advanced Usage","text":""},{"location":"usage/overview/#streaming-responses","title":"Streaming Responses","text":"

      ClientAI supports streaming responses for compatible providers. Here's a basic example:

      for chunk in client.generate_text(\n    \"Tell me a long story about space exploration\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n
      "},{"location":"usage/overview/#using-different-models","title":"Using Different Models","text":"

      ClientAI allows you to specify different models for each provider. For example:

      # Using GPT-4 with OpenAI\nopenai_response = openai_client.generate_text(\n    \"Explain quantum computing\",\n    model=\"gpt-4\"\n)\n\n# Using Llama 2 with Replicate\nreplicate_response = replicate_client.generate_text(\n    \"Describe the process of photosynthesis\",\n    model=\"meta/llama-2-70b-chat:latest\"\n)\n
      "},{"location":"usage/overview/#best-practices","title":"Best Practices","text":"
      1. API Key Management: Always store your API keys securely, preferably as environment variables.
      2. Error Handling: Implement proper error handling to manage potential API failures or rate limiting issues.
      3. Model Selection: Choose appropriate models based on your task requirements and budget considerations.
      4. Context Management: For chat applications, manage conversation context efficiently to get the best results.
      "},{"location":"usage/overview/#contribution","title":"Contribution","text":"

      If you have suggestions or contributions to these guides, please refer to our Contributing Guidelines. We appreciate your input in improving our documentation and making ClientAI more accessible to all users.

      "},{"location":"usage/text_generation/","title":"Text Generation with ClientAI","text":"

      This guide explores how to use ClientAI for text generation tasks across different AI providers. You'll learn about the various options and parameters available for generating text.

      "},{"location":"usage/text_generation/#table-of-contents","title":"Table of Contents","text":"
      1. Basic Text Generation
      2. Advanced Parameters
      3. Streaming Responses
      4. Provider-Specific Features
      5. Best Practices
      "},{"location":"usage/text_generation/#basic-text-generation","title":"Basic Text Generation","text":"

      To generate text using ClientAI, use the generate_text method:

      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\nresponse = client.generate_text(\n    \"Write a short story about a robot learning to paint.\",\n    model=\"gpt-3.5-turbo\"\n)\n\nprint(response)\n

      This will generate a short story based on the given prompt.

      "},{"location":"usage/text_generation/#advanced-parameters","title":"Advanced Parameters","text":"

      ClientAI supports various parameters to fine-tune text generation:

      response = client.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-4\",\n    max_tokens=150,\n    temperature=0.7,\n    top_p=0.9,\n    presence_penalty=0.1,\n    frequency_penalty=0.1\n)\n

      Note: Available parameters may vary depending on the provider.

      "},{"location":"usage/text_generation/#streaming-responses","title":"Streaming Responses","text":"

      For long-form content, you can use streaming to get partial responses as they're generated:

      for chunk in client.generate_text(\n    \"Write a comprehensive essay on climate change\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      This allows for real-time display of generated text, which can be useful for user interfaces or long-running generations.

      "},{"location":"usage/text_generation/#provider-specific-features","title":"Provider-Specific Features","text":"

      Different providers may offer unique features. Here are some examples:

      "},{"location":"usage/text_generation/#openai","title":"OpenAI","text":"
      response = openai_client.generate_text(\n    \"Translate the following to French: 'Hello, how are you?'\",\n    model=\"gpt-3.5-turbo\"\n)\n
      "},{"location":"usage/text_generation/#replicate","title":"Replicate","text":"
      response = replicate_client.generate_text(\n    \"Generate a haiku about mountains\",\n    model=\"meta/llama-2-70b-chat:latest\"\n)\n
      "},{"location":"usage/text_generation/#ollama","title":"Ollama","text":"
      response = ollama_client.generate_text(\n    \"Explain the concept of neural networks\",\n    model=\"llama2\"\n)\n
      "},{"location":"usage/text_generation/#best-practices","title":"Best Practices","text":"
      1. Prompt Engineering: Craft clear and specific prompts for better results.
      good_prompt = \"Write a detailed description of a futuristic city, focusing on transportation and architecture.\"\n
      1. Model Selection: Choose appropriate models based on your task complexity and requirements.

      2. Error Handling: Always handle potential errors in text generation:

      try:\n    response = client.generate_text(\"Your prompt here\", model=\"gpt-3.5-turbo\")\nexcept Exception as e:\n    print(f\"An error occurred: {e}\")\n
      1. Rate Limiting: Be mindful of rate limits imposed by providers. Implement appropriate delays or queuing mechanisms for high-volume applications.

      2. Content Filtering: Implement content filtering or moderation for user-facing applications to ensure appropriate outputs.

      3. Consistency: For applications requiring consistent outputs, consider using lower temperature values or implementing your own post-processing.

      By following these guidelines and exploring the various parameters and features available, you can effectively leverage ClientAI for a wide range of text generation tasks across different AI providers.

      "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"ClientAI","text":"

      A unified client for seamless interaction with multiple AI providers.

      ClientAI is a Python package that provides a unified interface for interacting with multiple AI providers, including OpenAI, Replicate, and Ollama. It offers seamless integration and consistent methods for text generation and chat functionality across different AI platforms.

      "},{"location":"#features","title":"Features","text":""},{"location":"#minimal-example","title":"Minimal Example","text":"

      Here's a quick example to get you started with ClientAI:

      from clientai import ClientAI\n\n# Initialize with OpenAI\nclient = ClientAI('openai', api_key=\"your-openai-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n)\n\nprint(response)\n\n# Chat functionality\nmessages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\n\nresponse = client.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n)\n\nprint(response)\n
      "},{"location":"#requirements","title":"Requirements","text":"

      Before installing ClientAI, ensure you have the following prerequisites:

      "},{"location":"#installing","title":"Installing","text":"

      To install ClientAI with all providers, run:

      pip install clientai[all]\n

      Or, if you prefer to install only specific providers:

      pip install clientai[openai]  # For OpenAI support\npip install clientai[replicate]  # For Replicate support\npip install clientai[ollama]  # For Ollama support\n
      "},{"location":"#usage","title":"Usage","text":"

      ClientAI offers a consistent way to interact with different AI providers:

      1. Initialize the client with your chosen provider and credentials.
      2. Use the generate_text method for text generation tasks.
      3. Use the chat method for conversational interactions.

      Both methods support streaming responses and returning full response objects.

      For more detailed usage examples and advanced features, please refer to the Usage section of this documentation.

      "},{"location":"#license","title":"License","text":"

      MIT

      "},{"location":"extending/","title":"Extending ClientAI: Adding a New Provider","text":"

      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.

      "},{"location":"extending/#overview","title":"Overview","text":"

      To add a new provider, you'll need to:

      1. Create a new directory for the provider
      2. Implement the provider-specific types
      3. Implement the provider class
      4. Implement Unified Error Handling
      5. Update the main ClientAI class
      6. Update the package constants
      7. Add tests for the new provider
      8. Test Error Handling
      9. Update Documentation

      Let's go through each step in detail.

      "},{"location":"extending/#step-1-create-a-new-directory","title":"Step 1: Create a New Directory","text":"

      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:

      clientai/\n    newai/\n        __init__.py\n        _typing.py\n        provider.py\n
      "},{"location":"extending/#step-2-implement-provider-specific-types","title":"Step 2: Implement Provider-Specific Types","text":"

      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\n\nfrom typing import Any, Dict, Iterator, Protocol, TypedDict, Union\nfrom .._common_types import GenericResponse\n\nclass NewAIResponse(TypedDict):\n    # Define the structure of a full response from NewAI\n    pass\n\nclass NewAIStreamResponse(TypedDict):\n    # Define the structure of a streaming response chunk from NewAI\n    pass\n\nclass NewAIClientProtocol(Protocol):\n    # Define the expected interface for the NewAI client\n    pass\n\nNewAIGenericResponse = GenericResponse[\n    str, NewAIResponse, NewAIStreamResponse\n]\n\n# Add any other necessary types\n
      "},{"location":"extending/#step-3-implement-the-provider-class","title":"Step 3: Implement the Provider Class","text":"

      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\n\nfrom ..ai_provider import AIProvider\nfrom ._typing import NewAIClientProtocol, NewAIGenericResponse\nfrom typing import List\nfrom .._common_types import Message\n\nclass Provider(AIProvider):\n    def __init__(self, api_key: str):\n        # Initialize the NewAI client\n        pass\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs\n    ) -> NewAIGenericResponse:\n        # Implement text generation logic\n        pass\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs\n    ) -> NewAIGenericResponse:\n        # Implement chat logic\n        pass\n\n    # Implement any helper methods as needed\n

      Make sure to handle both streaming and non-streaming responses, as well as the return_full_response option.

      "},{"location":"extending/#step-4-implement-unified-error-handling","title":"Step 4: Implement Unified Error Handling","text":"

      Before implementing the provider class, set up error handling for your provider. This ensures consistent error reporting across all providers.

      1. First, import the necessary error types:

        # clientai/newai/provider.py\n\nfrom ..exceptions import (\n    APIError,\n    AuthenticationError,\n    ClientAIError,\n    InvalidRequestError,\n    ModelError,\n    RateLimitError,\n    TimeoutError,\n)\n
      2. Implement the error mapping method in your provider class:

        class Provider(AIProvider):\n    ...\n    def _map_exception_to_clientai_error(\n        self,\n        e: Exception,\n        status_code: Optional[int] = None\n    ) -> ClientAIError:\n        \"\"\"\n        Maps NewAI-specific exceptions to ClientAI exceptions.\n\n        Args:\n            e: The caught exception\n            status_code: Optional HTTP status code\n\n        Returns:\n            ClientAIError: The appropriate ClientAI exception\n        \"\"\"\n        error_message = str(e)\n        status_code = status_code or getattr(e, \"status_code\", None)\n\n        # Map NewAI-specific exceptions to ClientAI exceptions\n        if isinstance(e, NewAIAuthError):\n            return AuthenticationError(\n                error_message,\n                status_code=401,\n                original_error=e\n            )\n        elif isinstance(e, NewAIRateLimitError):\n            return RateLimitError(\n                error_message,\n                status_code=429,\n                original_error=e\n            )\n        elif \"model not found\" in error_message.lower():\n            return ModelError(\n                error_message,\n                status_code=404,\n                original_error=e\n            )\n        elif isinstance(e, NewAIInvalidRequestError):\n            return InvalidRequestError(\n                error_message,\n                status_code=400,\n                original_error=e\n            )\n        elif isinstance(e, NewAITimeoutError):\n            return TimeoutError(\n                error_message,\n                status_code=408,\n                original_error=e\n            )\n\n        # Default to APIError for unknown errors\n        return APIError(\n            error_message,\n            status_code,\n            original_error=e\n        )\n
      "},{"location":"extending/#step-5-update-the-main-clientai-class","title":"Step 5: Update the Main ClientAI Class","text":"

      Update the clientai/client_ai.py file to include support for your new provider:

      1. Add an import for your new provider:

        from .newai import NEWAI_INSTALLED\n

      2. Update the __init__ method of the ClientAI class to handle the new provider:

         def __init__(self, provider_name: str, **kwargs):\n     prov_name = provider_name\n     # ----- add \"newai\" here -----\n     if prov_name not in [\"openai\", \"replicate\", \"ollama\", \"newai\"]:\n         raise ValueError(f\"Unsupported provider: {prov_name}\")\n\n     if (\n         prov_name == \"openai\"\n         and not OPENAI_INSTALLED\n         ...\n         # ----- also add \"newai\" here -----\n         or prov_name == \"newai\" \n         and not NEWAI_INSTALLED\n     ):\n         raise ImportError(\n             f\"The {prov_name} provider is not installed. \"\n             f\"Please install it with 'pip install clientai[{prov_name}]'.\"\n         )\n

      3. Add the new provider to the provider initialization logic:

        ...\ntry:\n    provider_module = import_module(\n        f\".{prov_name}.provider\", package=\"clientai\"\n    )\n    provider_class = getattr(provider_module, \"Provider\")\n\n    # ----- add \"newai\" here -----\n    if prov_name in [\"openai\", \"replicate\", \"newai\"]:\n        self.provider = cast(\n            P, provider_class(api_key=kwargs.get(\"api_key\"))\n        )\n    ...\n

      "},{"location":"extending/#step-6-update-package-constants-and-dependencies","title":"Step 6: Update Package Constants and Dependencies","text":"
      1. In the clientai/_constants.py file, add a constant for your new provider:

        NEWAI_INSTALLED = find_spec(\"newai\") is not None\n
      2. Update the clientai/__init__.py file to export the new constant:

        from ._constants import NEWAI_INSTALLED\n__all__ = [\n    # ... existing exports ...\n    \"NEWAI_INSTALLED\",\n]\n
      3. Update the pyproject.toml file to include the new provider as an optional dependency:

        [tool.poetry.dependencies]\npython = \"^3.9\"\npydantic = \"^2.9.2\"\nopenai = {version = \"^1.50.2\", optional = true}\nreplicate = {version = \"^0.34.1\", optional = true}\nollama = {version = \"^0.3.3\", optional = true}\nnewai-package = {version = \"^1.0.0\", optional = true}  # Add this line\n
      4. Define an optional group for the new provider:

        [tool.poetry.group.newai]\noptional = true\n\n[tool.poetry.group.newai.dependencies]\nnewai-package = \"^1.0.0\"\n
      5. Include the new provider in the development dependencies:

        [tool.poetry.group.dev.dependencies]\nruff = \"^0.6.8\"\npytest = \"^8.3.3\"\nmypy = \"1.9.0\"\nopenai = \"^1.50.2\"\nreplicate = \"^0.34.1\"\nollama = \"^0.3.3\"\nnewai-package = \"^1.0.0\"  # Add this line\n
      6. Run poetry update to update the poetry.lock file with the new dependencies.

      These changes allow users to install the new provider's dependencies using Poetry:

      poetry install --with newai\n

      If users are not using Poetry and are installing the package via pip, they can still use the extras syntax:

      pip install clientai[newai]\n
      "},{"location":"extending/#step-7-add-tests","title":"Step 7: Add Tests","text":"

      Create a new test file for your provider in the tests directory:

      tests/\n    newai/\n        __init__.py\n        test_provider.py\n

      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.

      "},{"location":"extending/#step-8-test-error-handling","title":"Step 8: Test Error Handling","text":"

      Also create a new test file to test your provider's exceptions in the tests directory:

      tests/\n    newai/\n        __init__.py\n        test_exceptions.py\n

      Add tests to ensure unified tests are being handled with a reference to the original error.

      "},{"location":"extending/#step-9-update-documentation","title":"Step 9: Update Documentation","text":"

      Don't forget to update the documentation to include information about the new provider:

      1. Add a new file docs/api/newai_provider.md with the following template for the NewAI provider.

        # NewAI Provider API Reference\n\nThe `NewAI` class implements the `AIProvider` interface for the NewAI service. It provides methods for text generation and chat functionality using NewAI's models.\n\n## Class Definition\n\n::: clientai.newai.Provider\n    rendering:\n      show_if_no_docstring: true\n

      2. Update docs/index.md to add a reference to the new provider.

      3. Update docs/quick-start.md to include an example of how to use the new provider.
      4. Update docs/api/overview.md to include a link to the new provider's documentation.
      "},{"location":"extending/#contribution-guidelines","title":"Contribution Guidelines","text":"

      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.

      "},{"location":"extending/#quick-summary-of-development-tools","title":"Quick Summary of Development Tools","text":"

      ClientAI uses the following tools for maintaining code quality:

      1. pytest: For running tests

        poetry run pytest\n

      2. mypy: For type checking

        mypy clientai\n

      3. ruff: For code linting and formatting

        ruff check --fix\nruff format\n

      Make sure to run these tools and address any issues before submitting your contribution.

      "},{"location":"extending/#conclusion","title":"Conclusion","text":"

      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!

      "},{"location":"installing/","title":"Installing","text":""},{"location":"installing/#requirements","title":"Requirements","text":"

      Before installing ClientAI, ensure you have the following prerequisites:

      "},{"location":"installing/#installing_1","title":"Installing","text":"

      ClientAI offers flexible installation options to suit your needs:

      "},{"location":"installing/#basic-installation","title":"Basic Installation","text":"

      To install the core ClientAI package without any provider-specific dependencies:

      pip install clientai\n

      Or, if using poetry:

      poetry add clientai\n
      "},{"location":"installing/#installation-with-specific-providers","title":"Installation with Specific Providers","text":"

      To install ClientAI with support for specific providers:

      pip install clientai[openai]  # For OpenAI support\npip install clientai[replicate]  # For Replicate support\npip install clientai[ollama]  # For Ollama support\n

      Or with poetry:

      poetry add clientai[openai]\npoetry add clientai[replicate]\npoetry add clientai[ollama]\n
      "},{"location":"installing/#full-installation","title":"Full Installation","text":"

      To install ClientAI with support for all providers:

      pip install clientai[all]\n

      Or with poetry:

      poetry add clientai[all]\n
      "},{"location":"quick-start/","title":"Quickstart","text":"

      This guide will help you get started with ClientAI quickly. We'll cover the basic setup and usage for each supported AI provider.

      "},{"location":"quick-start/#minimal-example","title":"Minimal Example","text":"

      Here's a minimal example to get you started with ClientAI:

      quickstart.py
      from clientai import ClientAI\n\n# Initialize the client (example with OpenAI)\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n)\nprint(response)\n\n# Use chat functionality\nmessages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital of France is Paris.\"},\n    {\"role\": \"user\", \"content\": \"What's its population?\"}\n]\nresponse = client.chat(messages, model=\"gpt-3.5-turbo\")\nprint(response)\n
      "},{"location":"quick-start/#setup-for-different-providers","title":"Setup for Different Providers","text":""},{"location":"quick-start/#openai","title":"OpenAI","text":"openai_setup.py
      from clientai import ClientAI\n\n# Initialize the OpenAI client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Now you can use the client for text generation or chat\n
      "},{"location":"quick-start/#replicate","title":"Replicate","text":"replicate_setup.py
      from clientai import ClientAI\n\n# Initialize the Replicate client\nclient = ClientAI('replicate', api_key=\"your-replicate-api-key\")\n\n# Now you can use the client for text generation or chat\n
      "},{"location":"quick-start/#ollama","title":"Ollama","text":"ollama_setup.py
      from clientai import ClientAI\n\n# Initialize the Ollama client\nclient = ClientAI('ollama', host=\"your-ollama-host\")\n\n# Now you can use the client for text generation or chat\n
      "},{"location":"quick-start/#basic-usage","title":"Basic Usage","text":"

      Once you have initialized the client, you can use it for text generation and chat functionality:

      "},{"location":"quick-start/#text-generation","title":"Text Generation","text":"text_generation.py
      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Explain the concept of quantum computing\",\n    model=\"gpt-3.5-turbo\",\n    max_tokens=100\n)\nprint(response)\n
      "},{"location":"quick-start/#chat","title":"Chat","text":"chat.py
      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Use chat functionality\nmessages = [\n    {\"role\": \"user\", \"content\": \"What is machine learning?\"},\n    {\"role\": \"assistant\", \"content\": \"Machine learning is a branch of artificial intelligence...\"},\n    {\"role\": \"user\", \"content\": \"Can you give an example of its application?\"}\n]\nresponse = client.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    max_tokens=150\n)\nprint(response)\n
      "},{"location":"quick-start/#ollama-server-management","title":"Ollama Server Management","text":"

      If you're running Ollama locally, ClientAI provides a convenient way to manage the Ollama server:

      ollama_manager.py
      from clientai.ollama import OllamaManager\n\n# Start and automatically stop the server using a context manager\nwith OllamaManager() as manager:\n    # Server is now running\n    client = ClientAI('ollama')\n    response = client.generate_text(\"Hello, world!\", model=\"llama2\")\n    print(response)\n

      You can also configure basic server settings:

      from clientai.ollama import OllamaManager, OllamaServerConfig\n\nconfig = OllamaServerConfig(\n    host=\"127.0.0.1\",\n    port=11434,\n    gpu_layers=35  # Optional: Number of layers to run on GPU\n)\n\nwith OllamaManager(config) as manager:\n    # Your code here\n    pass\n
      "},{"location":"quick-start/#next-steps","title":"Next Steps","text":"

      Now that you've seen the basics of ClientAI, you can:

      1. Explore more advanced features like streaming responses and handling full response objects.
      2. Check out the Usage Guide for detailed information on all available methods and options.
      3. See the API Reference for a complete list of ClientAI's classes and methods.

      Remember to handle API keys securely and never expose them in your code or version control systems.

      "},{"location":"advanced/error_handling/","title":"Error Handling and Retry Strategies","text":"

      This guide covers best practices for handling errors and implementing retry strategies when working with ClientAI. Learn how to gracefully handle API errors, implement effective retry mechanisms, and build robust AI applications.

      "},{"location":"advanced/error_handling/#table-of-contents","title":"Table of Contents","text":"
      1. Common Error Types
      2. Basic Error Handling
      3. Retry Strategies
      4. Advanced Error Handling Patterns
      5. Provider-Specific Considerations
      "},{"location":"advanced/error_handling/#common-error-types","title":"Common Error Types","text":"

      ClientAI provides a unified error hierarchy for all providers:

      from clientai.exceptions import (\n    ClientAIError,          # Base exception for all errors\n    AuthenticationError,    # API key or auth issues\n    RateLimitError,        # Rate limits exceeded\n    InvalidRequestError,    # Malformed requests\n    ModelError,            # Model-related issues\n    TimeoutError,          # Request timeouts\n    APIError              # General API errors\n)\n
      "},{"location":"advanced/error_handling/#basic-error-handling","title":"Basic Error Handling","text":""},{"location":"advanced/error_handling/#simple-try-except-pattern","title":"Simple Try-Except Pattern","text":"
      from clientai import ClientAI\nfrom clientai.exceptions import ClientAIError, RateLimitError\n\nclient = ClientAI(\"openai\", api_key=\"your-api-key\")\n\ntry:\n    response = client.generate_text(\n        prompt=\"Write a story\",\n        model=\"gpt-3.5-turbo\"\n    )\nexcept RateLimitError as e:\n    print(f\"Rate limit hit. Status code: {e.status_code}\")\n    print(f\"Original error: {e.original_error}\")\nexcept ClientAIError as e:\n    print(f\"Generation failed: {e}\")\n
      "},{"location":"advanced/error_handling/#retry-strategies","title":"Retry Strategies","text":""},{"location":"advanced/error_handling/#simple-retry-with-exponential-backoff","title":"Simple Retry with Exponential Backoff","text":"
      import time\nfrom typing import TypeVar, Callable\nfrom clientai.exceptions import RateLimitError, TimeoutError\n\nT = TypeVar('T')\n\ndef with_retry(\n    operation: Callable[[], T],\n    max_retries: int = 3,\n    initial_delay: float = 1.0,\n    exponential_base: float = 2.0,\n    max_delay: float = 60.0\n) -> T:\n    \"\"\"\n    Execute an operation with exponential backoff retry logic.\n\n    Args:\n        operation: Function to retry\n        max_retries: Maximum retry attempts\n        initial_delay: Initial delay between retries in seconds\n        exponential_base: Base for exponential backoff\n        max_delay: Maximum delay between retries\n    \"\"\"\n    last_exception = None\n\n    for attempt in range(max_retries):\n        try:\n            return operation()\n        except (RateLimitError, TimeoutError) as e:\n            last_exception = e\n            if attempt == max_retries - 1:\n                raise\n\n            delay = min(\n                initial_delay * (exponential_base ** attempt),\n                max_delay\n            )\n            time.sleep(delay)\n\n    raise last_exception or ClientAIError(\"Retry failed\")\n\n# Usage Example\ndef generate_text():\n    return client.generate_text(\n        prompt=\"Write a story\",\n        model=\"gpt-3.5-turbo\"\n    )\n\nresult = with_retry(generate_text)\n
      "},{"location":"advanced/error_handling/#provider-aware-retry-strategy","title":"Provider-Aware Retry Strategy","text":"
      from typing import Dict, Optional\n\nclass RetryConfig:\n    def __init__(\n        self,\n        max_retries: int = 3,\n        initial_delay: float = 1.0,\n        max_delay: float = 60.0,\n        retry_on: Optional[tuple] = None\n    ):\n        self.max_retries = max_retries\n        self.initial_delay = initial_delay\n        self.max_delay = max_delay\n        self.retry_on = retry_on or (RateLimitError, TimeoutError)\n\nPROVIDER_RETRY_CONFIGS = {\n    \"openai\": RetryConfig(max_retries=3, initial_delay=1.0),\n    \"anthropic\": RetryConfig(max_retries=5, initial_delay=2.0),\n    \"ollama\": RetryConfig(max_retries=2, initial_delay=0.5)\n}\n\ndef get_retry_config(provider: str) -> RetryConfig:\n    \"\"\"Get provider-specific retry configuration.\"\"\"\n    return PROVIDER_RETRY_CONFIGS.get(\n        provider,\n        RetryConfig()  # Default config\n    )\n
      "},{"location":"advanced/error_handling/#advanced-error-handling-patterns","title":"Advanced Error Handling Patterns","text":""},{"location":"advanced/error_handling/#circuit-breaker-pattern","title":"Circuit Breaker Pattern","text":"
      from typing import Optional\nfrom datetime import datetime, timedelta\n\nclass CircuitBreaker:\n    def __init__(\n        self,\n        failure_threshold: int = 5,\n        reset_timeout: int = 60\n    ):\n        self.failure_threshold = failure_threshold\n        self.reset_timeout = reset_timeout\n        self.failures = 0\n        self.last_failure_time: Optional[datetime] = None\n        self.is_open = False\n\n    def record_failure(self) -> None:\n        self.failures += 1\n        self.last_failure_time = datetime.now()\n        if self.failures >= self.failure_threshold:\n            self.is_open = True\n\n    def can_proceed(self) -> bool:\n        if not self.is_open:\n            return True\n\n        if self.last_failure_time and \\\n           datetime.now() - self.last_failure_time > timedelta(seconds=self.reset_timeout):\n            self.reset()\n            return True\n\n        return False\n\n    def reset(self) -> None:\n        self.failures = 0\n        self.is_open = False\n        self.last_failure_time = None\n\n# Usage\ncircuit_breaker = CircuitBreaker()\n\ndef generate_with_circuit_breaker(prompt: str, model: str) -> str:\n    if not circuit_breaker.can_proceed():\n        raise ClientAIError(\"Circuit breaker is open\")\n\n    try:\n        return client.generate_text(prompt, model=model)\n    except ClientAIError as e:\n        circuit_breaker.record_failure()\n        raise\n
      "},{"location":"advanced/error_handling/#fallback-chain-pattern","title":"Fallback Chain Pattern","text":"
      class FallbackChain:\n    def __init__(self, default_response: Optional[str] = None):\n        self.default_response = default_response\n        self.handlers: list = []\n\n    def add_handler(\n        self,\n        client: ClientAI,\n        model: str,\n        circuit_breaker: Optional[CircuitBreaker] = None\n    ):\n        self.handlers.append((client, model, circuit_breaker))\n        return self\n\n    def execute(self, prompt: str) -> str:\n        last_error = None\n\n        for client, model, circuit_breaker in self.handlers:\n            if circuit_breaker and not circuit_breaker.can_proceed():\n                continue\n\n            try:\n                return client.generate_text(prompt, model=model)\n            except ClientAIError as e:\n                if circuit_breaker:\n                    circuit_breaker.record_failure()\n                last_error = e\n\n        if self.default_response:\n            return self.default_response\n\n        raise last_error or ClientAIError(\"All handlers failed\")\n\n# Usage\nfallback_chain = FallbackChain(\"Sorry, service unavailable\")\nfallback_chain.add_handler(\n    ClientAI(\"openai\"), \"gpt-4\", CircuitBreaker()\n).add_handler(\n    ClientAI(\"anthropic\"), \"claude-2\", CircuitBreaker()\n)\n\nresponse = fallback_chain.execute(\"Write a story\")\n
      "},{"location":"advanced/error_handling/#provider-specific-considerations","title":"Provider-Specific Considerations","text":""},{"location":"advanced/error_handling/#openai","title":"OpenAI","text":""},{"location":"advanced/error_handling/#anthropic","title":"Anthropic","text":""},{"location":"advanced/error_handling/#ollama","title":"Ollama","text":""},{"location":"advanced/error_handling/#best-practices","title":"Best Practices","text":"
      1. Always Use Specific Exception Types

        try:\n    response = client.generate_text(prompt, model)\nexcept RateLimitError:\n    # Handle rate limits\nexcept ModelError:\n    # Handle model issues\nexcept ClientAIError:\n    # Handle other errors\n

      2. Implement Graceful Degradation

        def generate_with_fallback(prompt: str) -> str:\n    try:\n        return client.generate_text(\n            prompt, model=\"gpt-4\"\n        )\n    except (RateLimitError, ModelError):\n        return client.generate_text(\n            prompt, model=\"gpt-3.5-turbo\"\n        )\n    except ClientAIError:\n        return \"Service temporarily unavailable\"\n

      3. Use Appropriate Retry Strategies

        • Implement exponential backoff
        • Respect rate limits and retry-after headers
        • Set reasonable timeout values
        • Use circuit breakers for system protection
      4. Log Errors Appropriately

        import logging\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    response = client.generate_text(prompt, model)\nexcept ClientAIError as e:\n    logger.error(\n        \"Generation failed\",\n        extra={\n            \"status_code\": e.status_code,\n            \"error_type\": type(e).__name__,\n            \"original_error\": str(e.original_error)\n        }\n    )\n

      By following these error handling and retry strategies, you can build robust applications that gracefully handle failures and provide reliable service to your users.

      "},{"location":"advanced/ollama_specific/","title":"Ollama-Specific Parameters in ClientAI","text":"

      This guide covers the Ollama-specific parameters that can be passed to ClientAI's generate_text and chat methods. These parameters are passed as additional keyword arguments to customize Ollama's behavior.

      "},{"location":"advanced/ollama_specific/#generate_text-method","title":"generate_text Method","text":""},{"location":"advanced/ollama_specific/#basic-structure","title":"Basic Structure","text":"
      from clientai import ClientAI\n\nclient = ClientAI('ollama')\nresponse = client.generate_text(\n    prompt=\"Your prompt here\",    # Required\n    model=\"llama2\",              # Required\n    suffix=\"Optional suffix\",     # Ollama-specific\n    system=\"System message\",      # Ollama-specific\n    template=\"Custom template\",   # Ollama-specific\n    context=[1, 2, 3],           # Ollama-specific\n    format=\"json\",               # Ollama-specific\n    options={\"temperature\": 0.7}, # Ollama-specific\n    keep_alive=\"5m\"              # Ollama-specific\n)\n
      "},{"location":"advanced/ollama_specific/#ollama-specific-parameters","title":"Ollama-Specific Parameters","text":""},{"location":"advanced/ollama_specific/#suffix-str","title":"suffix: str","text":""},{"location":"advanced/ollama_specific/#system-str","title":"system: str","text":""},{"location":"advanced/ollama_specific/#template-str","title":"template: str","text":""},{"location":"advanced/ollama_specific/#context-listint","title":"context: List[int]","text":""},{"location":"advanced/ollama_specific/#format-literal-json","title":"format: Literal['', 'json']","text":""},{"location":"advanced/ollama_specific/#options-optionaloptions","title":"options: Optional[Options]","text":""},{"location":"advanced/ollama_specific/#keep_alive-optionalunionfloat-str","title":"keep_alive: Optional[Union[float, str]]","text":""},{"location":"advanced/ollama_specific/#chat-method","title":"chat Method","text":""},{"location":"advanced/ollama_specific/#basic-structure_1","title":"Basic Structure","text":"
      response = client.chat(\n    model=\"llama2\",              # Required\n    messages=[...],              # Required\n    tools=[...],                 # Ollama-specific\n    format=\"json\",               # Ollama-specific\n    options={\"temperature\": 0.7}, # Ollama-specific\n    keep_alive=\"5m\"              # Ollama-specific\n)\n
      "},{"location":"advanced/ollama_specific/#ollama-specific-parameters_1","title":"Ollama-Specific Parameters","text":""},{"location":"advanced/ollama_specific/#tools-optionallistdict","title":"tools: Optional[List[Dict]]","text":""},{"location":"advanced/ollama_specific/#format-literal-json_1","title":"format: Literal['', 'json']","text":""},{"location":"advanced/ollama_specific/#options-optionaloptions_1","title":"options: Optional[Options]","text":""},{"location":"advanced/ollama_specific/#keep_alive-optionalunionfloat-str_1","title":"keep_alive: Optional[Union[float, str]]","text":""},{"location":"advanced/ollama_specific/#complete-examples","title":"Complete Examples","text":""},{"location":"advanced/ollama_specific/#example-1-creative-writing-with-generate_text","title":"Example 1: Creative Writing with generate_text","text":"
      response = client.generate_text(\n    prompt=\"Write a short story about AI\",\n    model=\"llama2\",\n    system=\"You are a creative writer specializing in science fiction\",\n    template=\"Story prompt: {{.Prompt}}\\n\\nCreative story:\",\n    options={\n        \"temperature\": 0.9,\n        \"top_p\": 0.95\n    },\n    suffix=\"\\n\\nThe End.\",\n    keep_alive=\"10m\"\n)\n
      "},{"location":"advanced/ollama_specific/#example-2-json-response-with-chat","title":"Example 2: JSON Response with chat","text":"
      messages = [\n    {\"role\": \"system\", \"content\": \"You are a helpful assistant that provides structured data\"},\n    {\"role\": \"user\", \"content\": \"List 3 programming languages with their key features\"}\n]\n\nresponse = client.chat(\n    model=\"llama2\",\n    messages=messages,\n    format=\"json\",\n    options={\n        \"temperature\": 0.3,  # Lower temperature for more structured output\n        \"top_p\": 0.9\n    }\n)\n
      "},{"location":"advanced/ollama_specific/#example-3-multimodal-chat-with-image","title":"Example 3: Multimodal Chat with Image","text":"
      messages = [\n    {\n        \"role\": \"user\",\n        \"content\": \"What's in this image?\",\n        \"images\": [\"encoded_image_data_or_path\"]\n    }\n]\n\nresponse = client.chat(\n    model=\"llava\",\n    messages=messages,\n    format=\"json\",\n    keep_alive=\"5m\"\n)\n
      "},{"location":"advanced/ollama_specific/#example-4-contextual-generation","title":"Example 4: Contextual Generation","text":"
      # First generation\nfirst_response = client.generate_text(\n    prompt=\"Write the beginning of a mystery story\",\n    model=\"llama2\",\n    options={\"temperature\": 0.8}\n)\n\n# Continue the story using context\ncontinued_response = client.generate_text(\n    prompt=\"Continue the story with a plot twist\",\n    model=\"llama2\",\n    context=first_response.context,\n    options={\"temperature\": 0.8}\n)\n
      "},{"location":"advanced/ollama_specific/#parameter-validation-notes","title":"Parameter Validation Notes","text":"
      1. Both model and prompt/messages are required
      2. When using tools, stream must be False
      3. format only accepts '' or 'json'
      4. Image support requires multimodal models (e.g., llava)
      5. Context preservation works only with generate_text
      6. Keep alive duration can be string (e.g., \"5m\") or float (seconds)

      These parameters allow you to fully customize Ollama's behavior while working with ClientAI's abstraction layer.

      "},{"location":"advanced/openai_specific/","title":"OpenAI-Specific Parameters in ClientAI","text":"

      This guide covers the OpenAI-specific parameters that can be passed to ClientAI's generate_text and chat methods. These parameters are passed as additional keyword arguments to customize OpenAI's behavior.

      "},{"location":"advanced/openai_specific/#generate_text-method","title":"generate_text Method","text":""},{"location":"advanced/openai_specific/#basic-structure","title":"Basic Structure","text":"
      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\nresponse = client.generate_text(\n    prompt=\"Your prompt here\",          # Required\n    model=\"gpt-3.5-turbo\",             # Required\n    frequency_penalty=0.5,              # OpenAI-specific\n    presence_penalty=0.2,               # OpenAI-specific\n    logit_bias={123: 100},             # OpenAI-specific\n    max_completion_tokens=100,          # OpenAI-specific\n    response_format={\"type\": \"json\"},   # OpenAI-specific\n    seed=12345                         # OpenAI-specific\n)\n
      "},{"location":"advanced/openai_specific/#openai-specific-parameters","title":"OpenAI-Specific Parameters","text":""},{"location":"advanced/openai_specific/#frequency_penalty-optionalfloat","title":"frequency_penalty: Optional[float]","text":""},{"location":"advanced/openai_specific/#presence_penalty-optionalfloat","title":"presence_penalty: Optional[float]","text":""},{"location":"advanced/openai_specific/#logit_bias-optionaldictstr-int","title":"logit_bias: Optional[Dict[str, int]]","text":""},{"location":"advanced/openai_specific/#max_completion_tokens-optionalint","title":"max_completion_tokens: Optional[int]","text":""},{"location":"advanced/openai_specific/#response_format-responseformat","title":"response_format: ResponseFormat","text":""},{"location":"advanced/openai_specific/#seed-optionalint","title":"seed: Optional[int]","text":""},{"location":"advanced/openai_specific/#user-str","title":"user: str","text":""},{"location":"advanced/openai_specific/#chat-method","title":"chat Method","text":""},{"location":"advanced/openai_specific/#basic-structure_1","title":"Basic Structure","text":"
      response = client.chat(\n    model=\"gpt-3.5-turbo\",            # Required\n    messages=[...],                    # Required\n    tools=[...],                      # OpenAI-specific\n    tool_choice=\"auto\",               # OpenAI-specific\n    response_format={\"type\": \"json\"}, # OpenAI-specific\n    logprobs=True,                    # OpenAI-specific\n    top_logprobs=5                    # OpenAI-specific\n)\n
      "},{"location":"advanced/openai_specific/#openai-specific-parameters_1","title":"OpenAI-Specific Parameters","text":""},{"location":"advanced/openai_specific/#tools-iterablechatcompletiontoolparam","title":"tools: Iterable[ChatCompletionToolParam]","text":""},{"location":"advanced/openai_specific/#tool_choice-chatcompletiontoolchoiceoptionparam","title":"tool_choice: ChatCompletionToolChoiceOptionParam","text":""},{"location":"advanced/openai_specific/#modalities-optionallistchatcompletionmodality","title":"modalities: Optional[List[ChatCompletionModality]]","text":""},{"location":"advanced/openai_specific/#audio-optionalchatcompletionaudioparam","title":"audio: Optional[ChatCompletionAudioParam]","text":""},{"location":"advanced/openai_specific/#metadata-optionaldictstr-str","title":"metadata: Optional[Dict[str, str]]","text":""},{"location":"advanced/openai_specific/#complete-examples","title":"Complete Examples","text":""},{"location":"advanced/openai_specific/#example-1-structured-output-with-tools","title":"Example 1: Structured Output with Tools","text":"
      response = client.chat(\n    model=\"gpt-4\",\n    messages=[\n        {\"role\": \"system\", \"content\": \"You are a data assistant\"},\n        {\"role\": \"user\", \"content\": \"Get weather for Paris\"}\n    ],\n    response_format={\"type\": \"json_object\"},\n    tools=[{\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"get_weather\",\n            \"description\": \"Get weather data\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"location\": {\"type\": \"string\"}\n                }\n            }\n        }\n    }],\n    tool_choice=\"auto\"\n)\n
      "},{"location":"advanced/openai_specific/#example-2-advanced-text-generation","title":"Example 2: Advanced Text Generation","text":"
      response = client.generate_text(\n    prompt=\"Write a technical analysis\",\n    model=\"gpt-4\",\n    max_completion_tokens=500,\n    frequency_penalty=0.7,\n    presence_penalty=0.6,\n    logit_bias={123: 50},\n    user=\"analyst_1\",\n    seed=42\n)\n
      "},{"location":"advanced/openai_specific/#example-3-audio-generation","title":"Example 3: Audio Generation","text":"
      response = client.chat(\n    model=\"gpt-4o-audio-preview\",\n    messages=[{\"role\": \"user\", \"content\": \"Explain quantum physics\"}],\n    modalities=[\"text\", \"audio\"],\n    audio={\n        \"model\": \"tts-1\",\n        \"voice\": \"nova\",\n        \"speed\": 1.0\n    },\n    metadata={\"type\": \"educational\"}\n)\n
      "},{"location":"advanced/openai_specific/#parameter-validation-notes","title":"Parameter Validation Notes","text":"
      1. Both model and prompt/messages are required
      2. response_format requires compatible models
      3. Tool usage limited to 128 functions
      4. Audio generation requires specific models
      5. logprobs must be True when using top_logprobs
      6. seed feature is in Beta and not guaranteed

      These parameters allow you to fully customize OpenAI's behavior while working with ClientAI's abstraction layer.

      "},{"location":"advanced/overview/","title":"Advanced Overview","text":"

      This section provides in-depth guides on leveraging specific features of ClientAI and provider-specific functionalities. Each topic delves into a particular aspect of usage or focuses on a specific provider's unique capabilities.

      "},{"location":"advanced/overview/#provider-specific-parameters","title":"Provider-Specific Parameters","text":"

      Different AI providers offer unique parameters and features. Understanding these can help you fine-tune your AI interactions for optimal results.

      1. Ollama Specific Guide: Learn about Ollama's unique parameters, including context handling, streaming options, and custom templates.

        • Ollama Specific Guide
      2. OpenAI Specific Guide: Explore OpenAI's advanced features, such as logit bias and model-specific parameters.

        • OpenAI Specific Guide
      3. Replicate Specific Guide: Discover Replicate's distinctive offerings, including model versioning and custom deployment options.

        • Replicate Specific Guide
      "},{"location":"advanced/overview/#advanced-usage-topics","title":"Advanced Usage Topics","text":"
      1. Optimizing Performance: Tips and tricks for improving response time, reducing token usage, and enhancing overall efficiency.

        • Soon
      2. Handling Long Conversations: Strategies for managing context in extended dialogues and multi-turn interactions.

        • Soon
      3. Custom Prompting Techniques: Advanced prompting methods to extract more accurate and relevant responses from AI models.

        • Soon
      4. Error Handling and Retry Strategies: Best practices for gracefully managing API errors and implementing effective retry mechanisms.

        • Error Handling and Retry Strategies
      5. Security and Privacy Considerations: Guidelines for ensuring data security and maintaining user privacy when working with AI APIs.

        • Soon

      Each guide in this section is designed to provide you with a deeper understanding of ClientAI's capabilities and how to leverage them effectively in your projects.

      "},{"location":"advanced/replicate_specific/","title":"Replicate-Specific Parameters in ClientAI","text":"

      This guide covers the Replicate-specific parameters that can be passed to ClientAI's generate_text and chat methods. These parameters are passed as additional keyword arguments to customize Replicate's behavior.

      "},{"location":"advanced/replicate_specific/#generate_text-method","title":"generate_text Method","text":""},{"location":"advanced/replicate_specific/#basic-structure","title":"Basic Structure","text":"
      from clientai import ClientAI\n\nclient = ClientAI('replicate', api_key=\"your-replicate-api-key\")\nresponse = client.generate_text(\n    prompt=\"Your prompt here\",     # Required\n    model=\"owner/name:version\",    # Required\n    webhook=\"https://...\",         # Replicate-specific\n    webhook_completed=\"https://...\",# Replicate-specific\n    webhook_events_filter=[...],   # Replicate-specific\n    stream=False,                  # Optional\n    wait=True                      # Replicate-specific\n)\n
      "},{"location":"advanced/replicate_specific/#replicate-specific-parameters","title":"Replicate-Specific Parameters","text":""},{"location":"advanced/replicate_specific/#webhook-optionalstr","title":"webhook: Optional[str]","text":""},{"location":"advanced/replicate_specific/#webhook_completed-optionalstr","title":"webhook_completed: Optional[str]","text":""},{"location":"advanced/replicate_specific/#webhook_events_filter-optionalliststr","title":"webhook_events_filter: Optional[List[str]]","text":""},{"location":"advanced/replicate_specific/#wait-optionalunionint-bool","title":"wait: Optional[Union[int, bool]]","text":""},{"location":"advanced/replicate_specific/#stream-bool","title":"stream: bool","text":""},{"location":"advanced/replicate_specific/#chat-method","title":"chat Method","text":""},{"location":"advanced/replicate_specific/#basic-structure_1","title":"Basic Structure","text":"
      response = client.chat(\n    model=\"meta/llama-2-70b:latest\",  # Required\n    messages=[...],                    # Required\n    webhook=\"https://...\",             # Replicate-specific\n    webhook_completed=\"https://...\",   # Replicate-specific\n    webhook_events_filter=[...],       # Replicate-specific\n    wait=True                          # Replicate-specific\n)\n
      "},{"location":"advanced/replicate_specific/#message-formatting","title":"Message Formatting","text":"

      Replicate formats chat messages into a single prompt:

      prompt = \"\\n\".join([f\"{m['role']}: {m['content']}\" for m in messages])\nprompt += \"\\nassistant: \"\n

      "},{"location":"advanced/replicate_specific/#training-parameters","title":"Training Parameters","text":"

      When using Replicate's training capabilities:

      response = client.train(\n    model=\"stability-ai/sdxl\",\n    version=\"39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b\",\n    input={\n        \"input_images\": \"https://domain/images.zip\",\n        \"token_string\": \"TOK\",\n        \"caption_prefix\": \"a photo of TOK\",\n        \"max_train_steps\": 1000,\n        \"use_face_detection_instead\": False\n    },\n    destination=\"username/model-name\"\n)\n
      "},{"location":"advanced/replicate_specific/#complete-examples","title":"Complete Examples","text":""},{"location":"advanced/replicate_specific/#example-1-generation-with-webhooks","title":"Example 1: Generation with Webhooks","text":"
      response = client.generate_text(\n    prompt=\"Write a scientific paper summary\",\n    model=\"meta/llama-2-70b:latest\",\n    webhook=\"https://your-server.com/updates\",\n    webhook_completed=\"https://your-server.com/completed\",\n    webhook_events_filter=[\"completed\"],\n    wait=True\n)\n
      "},{"location":"advanced/replicate_specific/#example-2-chat-with-streaming","title":"Example 2: Chat with Streaming","text":"
      messages = [\n    {\"role\": \"system\", \"content\": \"You are a helpful assistant\"},\n    {\"role\": \"user\", \"content\": \"Write a haiku about coding\"}\n]\n\nfor chunk in client.chat(\n    messages=messages,\n    model=\"meta/llama-2-70b:latest\",\n    stream=True\n):\n    print(chunk, end=\"\")\n
      "},{"location":"advanced/replicate_specific/#example-3-image-generation","title":"Example 3: Image Generation","text":"
      response = client.generate_text(\n    prompt=\"A portrait of a wombat gentleman\",\n    model=\"stability-ai/stable-diffusion:27b93a2413e7f36cd83da926f3656280b2931564ff050bf9575f1fdf9bcd7478\",\n    wait=60\n)\n
      "},{"location":"advanced/replicate_specific/#error-handling","title":"Error Handling","text":"

      ClientAI maps Replicate's exceptions to its own error types:

      try:\n    response = client.generate_text(\n        prompt=\"Test prompt\",\n        model=\"meta/llama-2-70b:latest\",\n        wait=True\n    )\nexcept ClientAIError as e:\n    print(f\"Error: {e}\")\n

      Error mappings: - AuthenticationError: API key issues - RateLimitError: Rate limit exceeded - ModelError: Model not found or failed - InvalidRequestError: Invalid parameters - TimeoutError: Request timeout (default 300s) - APIError: Other server errors

      "},{"location":"advanced/replicate_specific/#parameter-validation-notes","title":"Parameter Validation Notes","text":"
      1. Both model and prompt/messages are required
      2. Model string format: \"owner/name:version\" or \"owner/name\" for latest version
      3. wait must be boolean or integer 1-60
      4. Webhook URLs must be valid HTTP/HTTPS URLs
      5. webhook_events_filter must contain valid event types
      6. Some models may not support streaming
      7. File inputs can be URLs or local file paths

      These parameters allow you to leverage Replicate's features through ClientAI, including model management, webhook notifications, and streaming capabilities.

      "},{"location":"api/ai_provider/","title":"AIProvider Class API Reference","text":"

      The AIProvider class is an abstract base class that defines the interface for all AI provider implementations in ClientAI. It ensures consistency across different providers.

      "},{"location":"api/ai_provider/#class-definition","title":"Class Definition","text":"

      Bases: ABC

      Abstract base class for AI providers.

      Source code in clientai/ai_provider.py
      class AIProvider(ABC):\n    \"\"\"\n    Abstract base class for AI providers.\n    \"\"\"\n\n    @abstractmethod\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> GenericResponse:\n        \"\"\"\n        Generate text based on a given prompt.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, return the full response object\n                                  instead of just the generated text.\n            stream: If True, return an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the provider's API.\n\n        Returns:\n            GenericResponse:\n                The generated text response, full response object,\n                or an iterator for streaming responses.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> GenericResponse:\n        \"\"\"\n        Engage in a chat conversation.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, return the full response object\n                                  instead of just the chat content.\n            stream: If True, return an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the provider's API.\n\n        Returns:\n            GenericResponse:\n                The chat response, either as a string, a dictionary,\n                or an iterator for streaming responses.\n        \"\"\"\n        pass\n
      "},{"location":"api/ai_provider/#clientai.ai_provider.AIProvider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs) abstractmethod","text":"

      Engage in a chat conversation.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, return the full response object instead of just the chat content.

      False stream bool

      If True, return an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the provider's API.

      {}

      Returns:

      Name Type Description GenericResponse GenericResponse

      The chat response, either as a string, a dictionary, or an iterator for streaming responses.

      Source code in clientai/ai_provider.py
      @abstractmethod\ndef chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> GenericResponse:\n    \"\"\"\n    Engage in a chat conversation.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, return the full response object\n                              instead of just the chat content.\n        stream: If True, return an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the provider's API.\n\n    Returns:\n        GenericResponse:\n            The chat response, either as a string, a dictionary,\n            or an iterator for streaming responses.\n    \"\"\"\n    pass\n
      "},{"location":"api/ai_provider/#clientai.ai_provider.AIProvider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs) abstractmethod","text":"

      Generate text based on a given prompt.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, return the full response object instead of just the generated text.

      False stream bool

      If True, return an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the provider's API.

      {}

      Returns:

      Name Type Description GenericResponse GenericResponse

      The generated text response, full response object, or an iterator for streaming responses.

      Source code in clientai/ai_provider.py
      @abstractmethod\ndef generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> GenericResponse:\n    \"\"\"\n    Generate text based on a given prompt.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, return the full response object\n                              instead of just the generated text.\n        stream: If True, return an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the provider's API.\n\n    Returns:\n        GenericResponse:\n            The generated text response, full response object,\n            or an iterator for streaming responses.\n    \"\"\"\n    pass\n
      "},{"location":"api/clientai/","title":"ClientAI Class API Reference","text":"

      The ClientAI class is the primary interface for interacting with various AI providers in a unified manner. It provides methods for text generation and chat functionality across different AI services.

      "},{"location":"api/clientai/#class-definition","title":"Class Definition","text":"

      Bases: Generic[P, T, S]

      A unified client for interacting with a single AI provider (OpenAI, Replicate, or Ollama).

      This class provides a consistent interface for common AI operations such as text generation and chat for the chosen AI provider.

      Type Parameters: P: The type of the AI provider. T: The type of the full response for non-streaming operations. S: The type of each chunk in streaming operations.

      Attributes:

      Name Type Description provider

      The initialized AI provider.

      Parameters:

      Name Type Description Default provider_name str

      The name of the AI provider to use ('openai', 'replicate', or 'ollama').

      required **kwargs Any

      Provider-specific initialization parameters.

      {}

      Raises:

      Type Description ValueError

      If an unsupported provider name is given.

      ImportError

      If the specified provider is not installed.

      Examples:

      Initialize with OpenAI:

      ai = ClientAI('openai', api_key=\"your-openai-key\")\n

      Initialize with Replicate:

      ai = ClientAI('replicate', api_key=\"your-replicate-key\")\n

      Initialize with Ollama:

      ai = ClientAI('ollama', host=\"your-ollama-host\")\n

      Source code in clientai/client_ai.py
      class ClientAI(Generic[P, T, S]):\n    \"\"\"\n    A unified client for interacting with a single AI provider\n    (OpenAI, Replicate, or Ollama).\n\n    This class provides a consistent interface for common\n    AI operations such as text generation and chat\n    for the chosen AI provider.\n\n    Type Parameters:\n    P: The type of the AI provider.\n    T: The type of the full response for non-streaming operations.\n    S: The type of each chunk in streaming operations.\n\n    Attributes:\n        provider: The initialized AI provider.\n\n    Args:\n        provider_name: The name of the AI provider to use\n                       ('openai', 'replicate', or 'ollama').\n        **kwargs (Any): Provider-specific initialization parameters.\n\n    Raises:\n        ValueError: If an unsupported provider name is given.\n        ImportError: If the specified provider is not installed.\n\n    Examples:\n        Initialize with OpenAI:\n        ```python\n        ai = ClientAI('openai', api_key=\"your-openai-key\")\n        ```\n\n        Initialize with Replicate:\n        ```python\n        ai = ClientAI('replicate', api_key=\"your-replicate-key\")\n        ```\n\n        Initialize with Ollama:\n        ```python\n        ai = ClientAI('ollama', host=\"your-ollama-host\")\n        ```\n    \"\"\"\n\n    def __init__(self, provider_name: str, **kwargs):\n        prov_name = provider_name\n        if prov_name not in [\"openai\", \"replicate\", \"ollama\"]:\n            raise ValueError(f\"Unsupported provider: {prov_name}\")\n\n        if (\n            prov_name == \"openai\"\n            and not OPENAI_INSTALLED\n            or prov_name == \"replicate\"\n            and not REPLICATE_INSTALLED\n            or prov_name == \"ollama\"\n            and not OLLAMA_INSTALLED\n        ):\n            raise ImportError(\n                f\"The {prov_name} provider is not installed. \"\n                f\"Please install it with 'pip install clientai[{prov_name}]'.\"\n            )\n\n        try:\n            provider_module = import_module(\n                f\".{prov_name}.provider\", package=\"clientai\"\n            )\n            provider_class = getattr(provider_module, \"Provider\")\n            if prov_name in [\"openai\", \"replicate\"]:\n                self.provider = cast(\n                    P, provider_class(api_key=kwargs.get(\"api_key\"))\n                )\n            elif prov_name == \"ollama\":\n                self.provider = cast(\n                    P, provider_class(host=kwargs.get(\"host\"))\n                )\n        except ImportError as e:\n            raise ImportError(\n                f\"Error importing {prov_name} provider module: {str(e)}\"\n            ) from e\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> AIGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt\n        using the specified AI model and provider.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, returns the full structured response\n                                  If False, returns only the generated text.\n            stream: If True, returns an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the chosen provider's API.\n\n        Returns:\n            AIGenericResponse:\n                The generated text response, full response structure,\n                or an iterator for streaming responses.\n\n        Examples:\n            Generate text using OpenAI (text only):\n            ```python\n            response = ai.generate_text(\n                \"Tell me a joke\",\n                model=\"gpt-3.5-turbo\",\n            )\n            ```\n\n            Generate text using OpenAI (full response):\n            ```python\n            response = ai.generate_text(\n                \"Tell me a joke\",\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            ```\n\n            Generate text using OpenAI (streaming):\n            ```python\n            for chunk in ai.generate_text(\n                \"Tell me a joke\",\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n\n            Generate text using Replicate:\n            ```python\n            response = ai.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            ```\n\n            Generate text using Ollama:\n            ```python\n            response = ai.generate_text(\n                \"What is the capital of France?\",\n                model=\"llama2\",\n            )\n            ```\n        \"\"\"\n        return self.provider.generate_text(\n            prompt,\n            model,\n            return_full_response=return_full_response,\n            stream=stream,\n            **kwargs,\n        )\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> AIGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using\n        the specified AI model and provider.\n\n        Args:\n            messages: A list of message dictionaries, each\n                      containing 'role' and 'content'.\n            model: The name or identifier of the AI model to use.\n            return_full_response: If True, returns the full structured response\n                                  If False, returns the assistant's message.\n            stream: If True, returns an iterator for streaming responses.\n            **kwargs: Additional keyword arguments specific to\n                      the chosen provider's API.\n\n        Returns:\n            AIGenericResponse:\n                The chat response, full response structure,\n                or an iterator for streaming responses.\n\n        Examples:\n            Chat using OpenAI (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n                {\"role\": \"assistant\", \"content\": \"Paris.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = ai.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n            )\n            ```\n\n            Chat using OpenAI (full response):\n            ```python\n            response = ai.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            ```\n\n            Chat using OpenAI (streaming):\n            ```python\n            for chunk in ai.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n\n            Chat using Replicate:\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"Explain the concept of AI.\"}\n            ]\n            response = ai.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            ```\n\n            Chat using Ollama:\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What are the laws of robotics?\"}\n            ]\n            response = ai.chat(messages, model=\"llama2\")\n            ```\n        \"\"\"\n        return self.provider.chat(\n            messages,\n            model,\n            return_full_response=return_full_response,\n            stream=stream,\n            **kwargs,\n        )\n
      "},{"location":"api/clientai/#clientai.ClientAI.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using the specified AI model and provider.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, returns the full structured response If False, returns the assistant's message.

      False stream bool

      If True, returns an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the chosen provider's API.

      {}

      Returns:

      Name Type Description AIGenericResponse AIGenericResponse

      The chat response, full response structure, or an iterator for streaming responses.

      Examples:

      Chat using OpenAI (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = ai.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n)\n

      Chat using OpenAI (full response):

      response = ai.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\n

      Chat using OpenAI (streaming):

      for chunk in ai.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Chat using Replicate:

      messages = [\n    {\"role\": \"user\", \"content\": \"Explain the concept of AI.\"}\n]\nresponse = ai.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n)\n

      Chat using Ollama:

      messages = [\n    {\"role\": \"user\", \"content\": \"What are the laws of robotics?\"}\n]\nresponse = ai.chat(messages, model=\"llama2\")\n

      Source code in clientai/client_ai.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> AIGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using\n    the specified AI model and provider.\n\n    Args:\n        messages: A list of message dictionaries, each\n                  containing 'role' and 'content'.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, returns the full structured response\n                              If False, returns the assistant's message.\n        stream: If True, returns an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the chosen provider's API.\n\n    Returns:\n        AIGenericResponse:\n            The chat response, full response structure,\n            or an iterator for streaming responses.\n\n    Examples:\n        Chat using OpenAI (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n            {\"role\": \"assistant\", \"content\": \"Paris.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = ai.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n        )\n        ```\n\n        Chat using OpenAI (full response):\n        ```python\n        response = ai.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        ```\n\n        Chat using OpenAI (streaming):\n        ```python\n        for chunk in ai.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n\n        Chat using Replicate:\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"Explain the concept of AI.\"}\n        ]\n        response = ai.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        ```\n\n        Chat using Ollama:\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What are the laws of robotics?\"}\n        ]\n        response = ai.chat(messages, model=\"llama2\")\n        ```\n    \"\"\"\n    return self.provider.chat(\n        messages,\n        model,\n        return_full_response=return_full_response,\n        stream=stream,\n        **kwargs,\n    )\n
      "},{"location":"api/clientai/#clientai.ClientAI.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using the specified AI model and provider.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the AI model to use.

      required return_full_response bool

      If True, returns the full structured response If False, returns only the generated text.

      False stream bool

      If True, returns an iterator for streaming responses.

      False **kwargs Any

      Additional keyword arguments specific to the chosen provider's API.

      {}

      Returns:

      Name Type Description AIGenericResponse AIGenericResponse

      The generated text response, full response structure, or an iterator for streaming responses.

      Examples:

      Generate text using OpenAI (text only):

      response = ai.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n)\n

      Generate text using OpenAI (full response):

      response = ai.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\n

      Generate text using OpenAI (streaming):

      for chunk in ai.generate_text(\n    \"Tell me a joke\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Generate text using Replicate:

      response = ai.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n)\n

      Generate text using Ollama:

      response = ai.generate_text(\n    \"What is the capital of France?\",\n    model=\"llama2\",\n)\n

      Source code in clientai/client_ai.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> AIGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt\n    using the specified AI model and provider.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the AI model to use.\n        return_full_response: If True, returns the full structured response\n                              If False, returns only the generated text.\n        stream: If True, returns an iterator for streaming responses.\n        **kwargs: Additional keyword arguments specific to\n                  the chosen provider's API.\n\n    Returns:\n        AIGenericResponse:\n            The generated text response, full response structure,\n            or an iterator for streaming responses.\n\n    Examples:\n        Generate text using OpenAI (text only):\n        ```python\n        response = ai.generate_text(\n            \"Tell me a joke\",\n            model=\"gpt-3.5-turbo\",\n        )\n        ```\n\n        Generate text using OpenAI (full response):\n        ```python\n        response = ai.generate_text(\n            \"Tell me a joke\",\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        ```\n\n        Generate text using OpenAI (streaming):\n        ```python\n        for chunk in ai.generate_text(\n            \"Tell me a joke\",\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n\n        Generate text using Replicate:\n        ```python\n        response = ai.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        ```\n\n        Generate text using Ollama:\n        ```python\n        response = ai.generate_text(\n            \"What is the capital of France?\",\n            model=\"llama2\",\n        )\n        ```\n    \"\"\"\n    return self.provider.generate_text(\n        prompt,\n        model,\n        return_full_response=return_full_response,\n        stream=stream,\n        **kwargs,\n    )\n
      "},{"location":"api/overview/","title":"API Reference Overview","text":"

      Welcome to the API Reference section of ClientAI documentation. This section provides detailed information about the various classes, functions, and modules that make up ClientAI. Whether you're looking to integrate ClientAI into your project, extend its functionality, or simply explore its capabilities, this section will guide you through the intricacies of our codebase.

      "},{"location":"api/overview/#key-components","title":"Key Components","text":"

      ClientAI's API is comprised of several key components, each serving a specific purpose:

      1. ClientAI Class: This is the main class of our library. It provides a unified interface for interacting with different AI providers and is the primary entry point for using ClientAI.

        • ClientAI Class Reference
      2. AIProvider Class: An abstract base class that defines the interface for all AI provider implementations. It ensures consistency across different providers.

        • AIProvider Class Reference
      3. Provider-Specific Classes: These classes implement the AIProvider interface for each supported AI service (OpenAI, Replicate, Ollama).

        • OpenAI Provider Reference
        • Replicate Provider Reference
        • Ollama Provider Reference
      4. Ollama Manager: These classes handle the local Ollama server configuration and lifecycle management.

        • OllamaManager Class Reference
        • OllamaServerConfig Class Reference
      "},{"location":"api/overview/#usage","title":"Usage","text":"

      Each component is documented with its own dedicated page, where you can find detailed information about its methods, parameters, return types, and usage examples. These pages are designed to provide you with all the information you need to understand and work with ClientAI effectively.

      "},{"location":"api/overview/#basic-usage-example","title":"Basic Usage Example","text":"

      Here's a quick example of how to use the main ClientAI class:

      from clientai import ClientAI\n\n# Initialize the client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Explain quantum computing\",\n    model=\"gpt-3.5-turbo\"\n)\n\nprint(response)\n

      For more detailed usage instructions and examples, please refer to the Usage Guide (\ud83d\udea7 Under Construction, come back soon \ud83d\udea7).

      "},{"location":"api/overview/#extending-clientai","title":"Extending ClientAI","text":"

      If you wish to add support for a new AI provider or extend the functionality of existing providers, you can do so by implementing the AIProvider interface. See the Extending ClientAI Guide for more information.

      "},{"location":"api/overview/#contribution","title":"Contribution","text":"

      We welcome contributions to ClientAI! If you're interested in contributing, please refer to our Contributing Guidelines. Contributions can range from bug fixes and documentation improvements to adding support for new AI providers.

      "},{"location":"api/overview/#feedback","title":"Feedback","text":"

      Your feedback is crucial in helping us improve ClientAI and its documentation. If you have any suggestions, corrections, or queries, please don't hesitate to reach out to us via GitHub issues or our community channels.

      Navigate through each section for detailed documentation of ClientAI's API components.

      "},{"location":"api/ollama_manager/ollama_manager/","title":"OllamaManager Class API Reference","text":"

      The OllamaManager class is a utility class that manages the lifecycle of a local Ollama server instance. It handles server process startup, monitoring, and shutdown while respecting platform-specific requirements and custom configurations. The manager supports configurable GPU acceleration, CPU thread allocation, and memory limits through OllamaServerConfig. It provides both context manager and manual management interfaces for controlling the server process.

      "},{"location":"api/ollama_manager/ollama_manager/#class-definition","title":"Class Definition","text":"

      Manages the Ollama server process and configuration.

      This class provides methods to start, stop, and manage the lifecycle of an Ollama server instance with configurable parameters.

      Attributes:

      Name Type Description config

      The server configuration used by the manager.

      _process Optional[Popen[str]]

      The underlying server process.

      _platform_info

      Information about the current platform.

      Parameters:

      Name Type Description Default config Optional[OllamaServerConfig]

      Optional server configuration. If None, uses defaults.

      None

      Raises:

      Type Description ImportError

      If required system dependencies are not installed.

      Examples:

      Basic usage with defaults:

      with OllamaManager() as manager:\n    # Server is running with default configuration\n    pass  # Server automatically stops when exiting context\n

      Custom configuration:

      config = OllamaServerConfig(\n    gpu_layers=35,\n    gpu_memory_fraction=0.8,\n    cpu_threads=8\n)\nmanager = OllamaManager(config)\nmanager.start()\n# ... use the server ...\nmanager.stop()\n

      Source code in clientai/ollama/manager/core.py
      class OllamaManager:\n    \"\"\"\n    Manages the Ollama server process and configuration.\n\n    This class provides methods to start, stop, and manage the lifecycle\n    of an Ollama server instance with configurable parameters.\n\n    Attributes:\n        config: The server configuration used by the manager.\n        _process: The underlying server process.\n        _platform_info: Information about the current platform.\n\n    Args:\n        config: Optional server configuration. If None, uses defaults.\n\n    Raises:\n        ImportError: If required system dependencies are not installed.\n\n    Examples:\n        Basic usage with defaults:\n        ```python\n        with OllamaManager() as manager:\n            # Server is running with default configuration\n            pass  # Server automatically stops when exiting context\n        ```\n\n        Custom configuration:\n        ```python\n        config = OllamaServerConfig(\n            gpu_layers=35,\n            gpu_memory_fraction=0.8,\n            cpu_threads=8\n        )\n        manager = OllamaManager(config)\n        manager.start()\n        # ... use the server ...\n        manager.stop()\n        ```\n    \"\"\"\n\n    def __init__(self, config: Optional[OllamaServerConfig] = None) -> None:\n        \"\"\"\n        Initialize the Ollama manager.\n\n        Args:\n            config: Optional server configuration. If None, uses defaults.\n        \"\"\"\n        self.config = config or OllamaServerConfig()\n        self._process: Optional[subprocess.Popen[str]] = None\n        self._platform_info = PlatformInfo()\n\n    def start(self) -> None:\n        \"\"\"\n        Start the Ollama server using the configured parameters.\n\n        This method initializes and starts the Ollama server process,\n        waiting for it to become healthy before returning.\n\n        Raises:\n            ServerStartupError: If the server fails to start\n            ServerTimeoutError: If the server doesn't respond within timeout\n            ExecutableNotFoundError: If the Ollama executable is not found\n            ResourceError: If there are insufficient system resources\n\n        Examples:\n            Start with default configuration:\n            ```python\n            manager = OllamaManager()\n            manager.start()\n            ```\n\n            Start with custom configuration:\n            ```python\n            config = OllamaServerConfig(gpu_layers=35)\n            manager = OllamaManager(config)\n            manager.start()\n            ```\n        \"\"\"\n        if self._process is not None:\n            raise ServerStartupError(\"Ollama server is already running\")\n\n        logging.info(\n            f\"Starting Ollama server on {self.config.host}:{self.config.port}\"\n        )\n\n        try:\n            popen_kwargs: Dict[str, Any] = {\n                \"stdout\": subprocess.PIPE,\n                \"stderr\": subprocess.PIPE,\n                \"text\": True,\n                \"env\": self._platform_info.get_environment(self.config),\n            }\n\n            if self._platform_info.platform == Platform.WINDOWS:\n                if sys.platform == \"win32\":\n                    popen_kwargs[\"creationflags\"] = subprocess.CREATE_NO_WINDOW\n\n            self._process = subprocess.Popen(\n                self._platform_info.get_server_command(self.config),\n                **popen_kwargs,\n            )\n\n        except FileNotFoundError as e:\n            raise_ollama_error(\n                ExecutableNotFoundError,\n                \"Ollama executable not found. Ensure Ollama is installed.\",\n                e,\n            )\n        except MemoryError as e:\n            raise_ollama_error(\n                ResourceError, \"Insufficient memory to start Ollama server\", e\n            )\n        except Exception as e:\n            raise_ollama_error(\n                ServerStartupError,\n                f\"Failed to start Ollama server: {str(e)}\",\n                e,\n            )\n\n        try:\n            self._wait_for_server()\n        except Exception as e:\n            self.stop()\n            raise e\n\n    def stop(self) -> None:\n        \"\"\"\n        Stop the running Ollama server instance.\n\n        This method terminates the Ollama server process if it's running.\n        It will wait for the process to complete before returning.\n\n        Examples:\n            Stop a running server:\n            ```python\n            manager = OllamaManager()\n            manager.start()\n            # ... use the server ...\n            manager.stop()\n            ```\n\n            Using context manager (automatic stop):\n            ```python\n            with OllamaManager() as manager:\n                # ... use the server ...\n                pass  # Server stops automatically\n            ```\n        \"\"\"\n        if self._process is not None:\n            try:\n                self._process.terminate()\n                self._process.wait()\n            finally:\n                self._process = None\n                logging.info(\"Ollama server stopped\")\n\n    def _check_server_health(self) -> bool:\n        \"\"\"\n        Check if the server is responding to health checks.\n\n        This method attempts to connect to the server and verify\n        its health status.\n\n        Returns:\n            bool: True if server is healthy, False otherwise\n\n        Note:\n            This is an internal method used by the manager to verify\n            server status during startup.\n        \"\"\"\n        try:\n            url = urlparse(self.config.base_url)\n            conn = http.client.HTTPConnection(\n                url.hostname or self.config.host,\n                url.port or self.config.port,\n                timeout=5,\n            )\n            try:\n                conn.request(\"GET\", \"/\")\n                response = conn.getresponse()\n                return response.status == 200\n            finally:\n                conn.close()\n        except (http.client.HTTPException, ConnectionRefusedError, OSError):\n            return False\n\n    def _wait_for_server(self) -> None:\n        \"\"\"\n        Wait for the server to become ready and responsive.\n\n        This method polls the server until it responds successfully or\n        times out. It checks both process health and server responsiveness.\n\n        Raises:\n            ServerStartupError: If the server process terminates unexpectedly\n            ServerTimeoutError: If the server doesn't respond within timeout\n\n        Note:\n            This is an internal method used during the server startup process.\n        \"\"\"\n        start_time = time.time()\n\n        while time.time() - start_time < self.config.timeout:\n            process = cast(subprocess.Popen[str], self._process)\n\n            if process.poll() is not None:\n                stdout, stderr = process.communicate()\n                error_msg = (\n                    f\"Ollama process terminated unexpectedly.\\n\"\n                    f\"Exit code: {process.returncode}\\n\"\n                    f\"stdout: {stdout}\\n\"\n                    f\"stderr: {stderr}\"\n                )\n                self._process = None\n                raise ServerStartupError(error_msg)\n\n            if self._check_server_health():\n                logging.info(\"Ollama server is ready\")\n                return\n\n            time.sleep(self.config.check_interval)\n\n        raise ServerTimeoutError(\n            f\"Ollama server did not start within {self.config.timeout} seconds\"\n        )\n\n    def __enter__(self) -> \"OllamaManager\":\n        \"\"\"Context manager entry point that starts the server.\"\"\"\n        self.start()\n        return self\n\n    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:\n        \"\"\"Context manager exit point that ensures the server is stopped.\"\"\"\n        self.stop()\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.__enter__","title":"__enter__()","text":"

      Context manager entry point that starts the server.

      Source code in clientai/ollama/manager/core.py
      def __enter__(self) -> \"OllamaManager\":\n    \"\"\"Context manager entry point that starts the server.\"\"\"\n    self.start()\n    return self\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.__exit__","title":"__exit__(exc_type, exc_val, exc_tb)","text":"

      Context manager exit point that ensures the server is stopped.

      Source code in clientai/ollama/manager/core.py
      def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:\n    \"\"\"Context manager exit point that ensures the server is stopped.\"\"\"\n    self.stop()\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.__init__","title":"__init__(config=None)","text":"

      Initialize the Ollama manager.

      Parameters:

      Name Type Description Default config Optional[OllamaServerConfig]

      Optional server configuration. If None, uses defaults.

      None Source code in clientai/ollama/manager/core.py
      def __init__(self, config: Optional[OllamaServerConfig] = None) -> None:\n    \"\"\"\n    Initialize the Ollama manager.\n\n    Args:\n        config: Optional server configuration. If None, uses defaults.\n    \"\"\"\n    self.config = config or OllamaServerConfig()\n    self._process: Optional[subprocess.Popen[str]] = None\n    self._platform_info = PlatformInfo()\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.start","title":"start()","text":"

      Start the Ollama server using the configured parameters.

      This method initializes and starts the Ollama server process, waiting for it to become healthy before returning.

      Raises:

      Type Description ServerStartupError

      If the server fails to start

      ServerTimeoutError

      If the server doesn't respond within timeout

      ExecutableNotFoundError

      If the Ollama executable is not found

      ResourceError

      If there are insufficient system resources

      Examples:

      Start with default configuration:

      manager = OllamaManager()\nmanager.start()\n

      Start with custom configuration:

      config = OllamaServerConfig(gpu_layers=35)\nmanager = OllamaManager(config)\nmanager.start()\n

      Source code in clientai/ollama/manager/core.py
      def start(self) -> None:\n    \"\"\"\n    Start the Ollama server using the configured parameters.\n\n    This method initializes and starts the Ollama server process,\n    waiting for it to become healthy before returning.\n\n    Raises:\n        ServerStartupError: If the server fails to start\n        ServerTimeoutError: If the server doesn't respond within timeout\n        ExecutableNotFoundError: If the Ollama executable is not found\n        ResourceError: If there are insufficient system resources\n\n    Examples:\n        Start with default configuration:\n        ```python\n        manager = OllamaManager()\n        manager.start()\n        ```\n\n        Start with custom configuration:\n        ```python\n        config = OllamaServerConfig(gpu_layers=35)\n        manager = OllamaManager(config)\n        manager.start()\n        ```\n    \"\"\"\n    if self._process is not None:\n        raise ServerStartupError(\"Ollama server is already running\")\n\n    logging.info(\n        f\"Starting Ollama server on {self.config.host}:{self.config.port}\"\n    )\n\n    try:\n        popen_kwargs: Dict[str, Any] = {\n            \"stdout\": subprocess.PIPE,\n            \"stderr\": subprocess.PIPE,\n            \"text\": True,\n            \"env\": self._platform_info.get_environment(self.config),\n        }\n\n        if self._platform_info.platform == Platform.WINDOWS:\n            if sys.platform == \"win32\":\n                popen_kwargs[\"creationflags\"] = subprocess.CREATE_NO_WINDOW\n\n        self._process = subprocess.Popen(\n            self._platform_info.get_server_command(self.config),\n            **popen_kwargs,\n        )\n\n    except FileNotFoundError as e:\n        raise_ollama_error(\n            ExecutableNotFoundError,\n            \"Ollama executable not found. Ensure Ollama is installed.\",\n            e,\n        )\n    except MemoryError as e:\n        raise_ollama_error(\n            ResourceError, \"Insufficient memory to start Ollama server\", e\n        )\n    except Exception as e:\n        raise_ollama_error(\n            ServerStartupError,\n            f\"Failed to start Ollama server: {str(e)}\",\n            e,\n        )\n\n    try:\n        self._wait_for_server()\n    except Exception as e:\n        self.stop()\n        raise e\n
      "},{"location":"api/ollama_manager/ollama_manager/#clientai.ollama.OllamaManager.stop","title":"stop()","text":"

      Stop the running Ollama server instance.

      This method terminates the Ollama server process if it's running. It will wait for the process to complete before returning.

      Examples:

      Stop a running server:

      manager = OllamaManager()\nmanager.start()\n# ... use the server ...\nmanager.stop()\n

      Using context manager (automatic stop):

      with OllamaManager() as manager:\n    # ... use the server ...\n    pass  # Server stops automatically\n

      Source code in clientai/ollama/manager/core.py
      def stop(self) -> None:\n    \"\"\"\n    Stop the running Ollama server instance.\n\n    This method terminates the Ollama server process if it's running.\n    It will wait for the process to complete before returning.\n\n    Examples:\n        Stop a running server:\n        ```python\n        manager = OllamaManager()\n        manager.start()\n        # ... use the server ...\n        manager.stop()\n        ```\n\n        Using context manager (automatic stop):\n        ```python\n        with OllamaManager() as manager:\n            # ... use the server ...\n            pass  # Server stops automatically\n        ```\n    \"\"\"\n    if self._process is not None:\n        try:\n            self._process.terminate()\n            self._process.wait()\n        finally:\n            self._process = None\n            logging.info(\"Ollama server stopped\")\n
      "},{"location":"api/ollama_manager/ollama_server_config/","title":"OllamaServerConfig Class API Reference","text":"

      The OllamaServerConfig class is a configuration container that defines the runtime parameters for an Ollama server instance. It allows users to specify network settings (host/port), hardware utilization options (GPU layers, CPU threads, memory limits), and environment variables. The class provides sensible defaults while allowing fine-grained control over server behavior through optional configuration parameters.

      "},{"location":"api/ollama_manager/ollama_server_config/#class-definition","title":"Class Definition","text":"

      Configuration settings for Ollama server.

      Attributes:

      Name Type Description host str

      Hostname to bind the server to

      port int

      Port number to listen on

      timeout int

      Maximum time in seconds to wait for server startup

      check_interval float

      Time in seconds between server readiness checks

      gpu_layers Optional[int]

      Number of layers to run on GPU

      compute_unit Optional[str]

      Compute device to use ('cpu', 'gpu', 'auto')

      cpu_threads Optional[int]

      Number of CPU threads to use

      memory_limit Optional[str]

      Memory limit for the server (format: number + GiB/MiB, e.g., \"8GiB\")

      gpu_memory_fraction Optional[float]

      Fraction of GPU memory to use (0.0-1.0)

      gpu_devices Optional[Union[List[int], int]]

      GPU device IDs to use

      env_vars Dict[str, str]

      Additional environment variables

      extra_args List[str]

      Additional command line arguments

      Source code in clientai/ollama/manager/config.py
      @dataclass\nclass OllamaServerConfig:\n    \"\"\"\n    Configuration settings for Ollama server.\n\n    Attributes:\n        host: Hostname to bind the server to\n        port: Port number to listen on\n        timeout: Maximum time in seconds to wait for server startup\n        check_interval: Time in seconds between server readiness checks\n        gpu_layers: Number of layers to run on GPU\n        compute_unit: Compute device to use ('cpu', 'gpu', 'auto')\n        cpu_threads: Number of CPU threads to use\n        memory_limit: Memory limit for the server\n                      (format: number + GiB/MiB, e.g., \"8GiB\")\n        gpu_memory_fraction: Fraction of GPU memory to use (0.0-1.0)\n        gpu_devices: GPU device IDs to use\n        env_vars: Additional environment variables\n        extra_args: Additional command line arguments\n    \"\"\"\n\n    host: str = \"127.0.0.1\"\n    port: int = 11434\n    timeout: int = 30\n    check_interval: float = 1.0\n    gpu_layers: Optional[int] = None\n    compute_unit: Optional[str] = None\n    cpu_threads: Optional[int] = None\n    memory_limit: Optional[str] = None\n    gpu_memory_fraction: Optional[float] = None\n    gpu_devices: Optional[Union[List[int], int]] = None\n    env_vars: Dict[str, str] = field(default_factory=dict)\n    extra_args: List[str] = field(default_factory=list)\n\n    def _validate_host(self) -> None:\n        \"\"\"Validate the host address.\"\"\"\n        if not self.host:\n            raise ValueError(\"Host cannot be empty\")\n\n        try:\n            ipaddress.ip_address(self.host)\n        except ValueError:\n            if not re.match(r\"^[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*$\", self.host):\n                raise ValueError(f\"Invalid host: {self.host}\")\n\n    def _validate_port(self) -> None:\n        \"\"\"Validate the port number.\"\"\"\n        if not 1 <= self.port <= 65535:\n            raise ValueError(\n                f\"Port must be between 1 and 65535, got {self.port}\"\n            )\n\n    def _validate_timeout_and_interval(self) -> None:\n        \"\"\"Validate timeout and check interval.\"\"\"\n        if self.timeout <= 0:\n            raise ValueError(\"Timeout must be positive\")\n        if self.check_interval <= 0:\n            raise ValueError(\"Check interval must be positive\")\n        if self.check_interval > self.timeout:\n            raise ValueError(\"Check interval cannot be greater than timeout\")\n\n    def _validate_gpu_settings(self) -> None:\n        \"\"\"Validate GPU-related settings.\"\"\"\n        if self.gpu_layers is not None:\n            if not isinstance(self.gpu_layers, int) or self.gpu_layers < 0:\n                raise ValueError(\"gpu_layers must be a non-negative integer\")\n\n        if self.gpu_memory_fraction is not None:\n            if not 0.0 <= self.gpu_memory_fraction <= 1.0:\n                raise ValueError(\n                    \"gpu_memory_fraction must be between 0.0 and 1.0\"\n                )\n\n        if self.gpu_devices is not None:\n            if isinstance(self.gpu_devices, int):\n                if self.gpu_devices < 0:\n                    raise ValueError(\"GPU device ID must be non-negative\")\n            elif isinstance(self.gpu_devices, list):\n                if not all(\n                    isinstance(d, int) and d >= 0 for d in self.gpu_devices\n                ):\n                    raise ValueError(\n                        \"All GPU device IDs must be non-negative integers\"\n                    )\n                if len(self.gpu_devices) != len(set(self.gpu_devices)):\n                    raise ValueError(\n                        \"Duplicate GPU device IDs are not allowed\"\n                    )\n            else:\n                raise ValueError(\n                    \"gpu_devices must be an integer or list of integers\"\n                )\n\n    def _validate_compute_unit(self) -> None:\n        \"\"\"Validate compute unit setting.\"\"\"\n        if self.compute_unit and self.compute_unit not in [\n            \"cpu\",\n            \"gpu\",\n            \"auto\",\n        ]:\n            raise ValueError(\n                \"compute_unit must be one of: 'cpu', 'gpu', 'auto'\"\n            )\n\n    def _validate_cpu_threads(self) -> None:\n        \"\"\"Validate CPU threads setting.\"\"\"\n        if self.cpu_threads is not None:\n            if not isinstance(self.cpu_threads, int) or self.cpu_threads <= 0:\n                raise ValueError(\"cpu_threads must be a positive integer\")\n\n    def _validate_memory_limit(self) -> None:\n        \"\"\"Validate memory limit format.\"\"\"\n        if self.memory_limit is not None:\n            pattern = r\"^\\d+(\\.\\d+)?[MGT]iB$\"\n            if not re.match(pattern, self.memory_limit):\n                raise ValueError(\n                    \"memory_limit must be in format: NUMBER + UNIT, \"\n                    \"where UNIT is MiB, GiB, or TiB (e.g., '8GiB')\"\n                )\n\n            match = re.match(r\"^\\d+(\\.\\d+)?\", self.memory_limit)\n            if match is None:\n                raise ValueError(\"Invalid memory_limit format\")\n\n            value = float(match.group())\n            unit = self.memory_limit[-3:]\n\n            if unit == \"MiB\" and value < 100:\n                raise ValueError(\"memory_limit in MiB must be at least 100\")\n            elif unit == \"GiB\" and value < 0.1:\n                raise ValueError(\"memory_limit in GiB must be at least 0.1\")\n            elif unit == \"TiB\" and value < 0.001:\n                raise ValueError(\"memory_limit in TiB must be at least 0.001\")\n\n    def _validate_env_vars(self) -> None:\n        \"\"\"Validate environment variables.\"\"\"\n        if not all(\n            isinstance(k, str) and isinstance(v, str)\n            for k, v in self.env_vars.items()\n        ):\n            raise ValueError(\"All environment variables must be strings\")\n\n    def _validate_extra_args(self) -> None:\n        \"\"\"Validate extra arguments.\"\"\"\n        if not all(isinstance(arg, str) for arg in self.extra_args):\n            raise ValueError(\"All extra arguments must be strings\")\n\n    def __post_init__(self):\n        \"\"\"Validate all configuration after initialization.\"\"\"\n        self._validate_host()\n        self._validate_port()\n        self._validate_timeout_and_interval()\n        self._validate_gpu_settings()\n        self._validate_compute_unit()\n        self._validate_cpu_threads()\n        self._validate_memory_limit()\n        self._validate_env_vars()\n        self._validate_extra_args()\n\n    @property\n    def base_url(self) -> str:\n        \"\"\"Get the base URL for the Ollama server.\"\"\"\n        return f\"http://{self.host}:{self.port}\"\n

      rendering: show_if_no_docstring: true

      "},{"location":"api/ollama_manager/ollama_server_config/#clientai.ollama.OllamaServerConfig.base_url","title":"base_url: str property","text":"

      Get the base URL for the Ollama server.

      "},{"location":"api/ollama_manager/ollama_server_config/#clientai.ollama.OllamaServerConfig.__post_init__","title":"__post_init__()","text":"

      Validate all configuration after initialization.

      Source code in clientai/ollama/manager/config.py
      def __post_init__(self):\n    \"\"\"Validate all configuration after initialization.\"\"\"\n    self._validate_host()\n    self._validate_port()\n    self._validate_timeout_and_interval()\n    self._validate_gpu_settings()\n    self._validate_compute_unit()\n    self._validate_cpu_threads()\n    self._validate_memory_limit()\n    self._validate_env_vars()\n    self._validate_extra_args()\n
      "},{"location":"api/specific_providers/ollama_provider/","title":"Ollama Provider API Reference","text":"

      The OllamaProvider class implements the AIProvider interface for the Ollama service. It provides methods for text generation and chat functionality using locally hosted models through Ollama.

      "},{"location":"api/specific_providers/ollama_provider/#class-definition","title":"Class Definition","text":"

      Bases: AIProvider

      Ollama-specific implementation of the AIProvider abstract base class.

      This class provides methods to interact with Ollama's models for text generation and chat functionality.

      Attributes:

      Name Type Description client OllamaClientProtocol

      The Ollama client used for making API calls.

      Parameters:

      Name Type Description Default host Optional[str]

      The host address for the Ollama server. If not provided, the default Ollama client will be used.

      None

      Raises:

      Type Description ImportError

      If the Ollama package is not installed.

      Examples:

      Initialize the Ollama provider:

      provider = Provider(host=\"http://localhost:11434\")\n

      Source code in clientai/ollama/provider.py
      class Provider(AIProvider):\n    \"\"\"\n    Ollama-specific implementation of the AIProvider abstract base class.\n\n    This class provides methods to interact with Ollama's models for\n    text generation and chat functionality.\n\n    Attributes:\n        client: The Ollama client used for making API calls.\n\n    Args:\n        host: The host address for the Ollama server.\n            If not provided, the default Ollama client will be used.\n\n    Raises:\n        ImportError: If the Ollama package is not installed.\n\n    Examples:\n        Initialize the Ollama provider:\n        ```python\n        provider = Provider(host=\"http://localhost:11434\")\n        ```\n    \"\"\"\n\n    def __init__(self, host: Optional[str] = None):\n        if not OLLAMA_INSTALLED or Client is None:\n            raise ImportError(\n                \"The ollama package is not installed. \"\n                \"Please install it with 'pip install clientai[ollama]'.\"\n            )\n        self.client: OllamaClientProtocol = cast(\n            OllamaClientProtocol, Client(host=host) if host else ollama\n        )\n\n    def _stream_generate_response(\n        self,\n        stream: Iterator[OllamaStreamResponse],\n        return_full_response: bool,\n    ) -> Iterator[Union[str, OllamaStreamResponse]]:\n        \"\"\"\n        Process the streaming response from Ollama API for text generation.\n\n        Args:\n            stream: The stream of responses from Ollama API.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, OllamaStreamResponse]: Processed content or\n                                              full response objects.\n        \"\"\"\n        for chunk in stream:\n            if return_full_response:\n                yield chunk\n            else:\n                yield chunk[\"response\"]\n\n    def _stream_chat_response(\n        self,\n        stream: Iterator[OllamaChatResponse],\n        return_full_response: bool,\n    ) -> Iterator[Union[str, OllamaChatResponse]]:\n        \"\"\"\n        Process the streaming response from Ollama API for chat.\n\n        Args:\n            stream: The stream of responses from Ollama API.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, OllamaChatResponse]: Processed content or\n                                            full response objects.\n        \"\"\"\n        for chunk in stream:\n            if return_full_response:\n                yield chunk\n            else:\n                yield chunk[\"message\"][\"content\"]\n\n    def _map_exception_to_clientai_error(self, e: Exception) -> ClientAIError:\n        \"\"\"\n        Maps an Ollama exception to the appropriate ClientAI exception.\n\n        Args:\n            e (Exception): The exception caught during the API call.\n\n        Returns:\n            ClientAIError: An instance of the appropriate ClientAI exception.\n        \"\"\"\n        message = str(e)\n\n        if isinstance(e, ollama.RequestError):\n            if \"authentication\" in message.lower():\n                return AuthenticationError(\n                    message, status_code=401, original_error=e\n                )\n            elif \"rate limit\" in message.lower():\n                return RateLimitError(\n                    message, status_code=429, original_error=e\n                )\n            elif \"not found\" in message.lower():\n                return ModelError(message, status_code=404, original_error=e)\n            else:\n                return InvalidRequestError(\n                    message, status_code=400, original_error=e\n                )\n        elif isinstance(e, ollama.ResponseError):\n            if \"timeout\" in message.lower() or \"timed out\" in message.lower():\n                return TimeoutError(message, status_code=408, original_error=e)\n            else:\n                return APIError(message, status_code=500, original_error=e)\n        else:\n            return ClientAIError(message, status_code=500, original_error=e)\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OllamaGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt using a specified Ollama model.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the Ollama model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n        Returns:\n            OllamaGenericResponse: The generated text, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Generate text (text only):\n            ```python\n            response = provider.generate_text(\n                \"Explain the concept of machine learning\",\n                model=\"llama2\",\n            )\n            print(response)\n            ```\n\n            Generate text (full response):\n            ```python\n            response = provider.generate_text(\n                \"Explain the concept of machine learning\",\n                model=\"llama2\",\n                return_full_response=True\n            )\n            print(response[\"response\"])\n            ```\n\n            Generate text (streaming):\n            ```python\n            for chunk in provider.generate_text(\n                \"Explain the concept of machine learning\",\n                model=\"llama2\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.generate(\n                model=model, prompt=prompt, stream=stream, **kwargs\n            )\n\n            if stream:\n                return cast(\n                    OllamaGenericResponse,\n                    self._stream_generate_response(\n                        cast(Iterator[OllamaStreamResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OllamaResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response[\"response\"]\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OllamaGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using a specified Ollama model.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the Ollama model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n        Returns:\n            OllamaGenericResponse: The chat response, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Chat (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of Japan?\"},\n                {\"role\": \"assistant\", \"content\": \"The capital is Tokyo.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = provider.chat(\n                messages,\n                model=\"llama2\",\n            )\n            print(response)\n            ```\n\n            Chat (full response):\n            ```python\n            response = provider.chat(\n                messages,\n                model=\"llama2\",\n                return_full_response=True\n            )\n            print(response[\"message\"][\"content\"])\n            ```\n\n            Chat (streaming):\n            ```python\n            for chunk in provider.chat(\n                messages,\n                model=\"llama2\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.chat(\n                model=model, messages=messages, stream=stream, **kwargs\n            )\n\n            if stream:\n                return cast(\n                    OllamaGenericResponse,\n                    self._stream_chat_response(\n                        cast(Iterator[OllamaChatResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OllamaChatResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response[\"message\"][\"content\"]\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/ollama_provider/#clientai.ollama.Provider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using a specified Ollama model.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the Ollama model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Ollama API.

      {}

      Returns:

      Name Type Description OllamaGenericResponse OllamaGenericResponse

      The chat response, full response object,

      OllamaGenericResponse

      or an iterator for streaming responses.

      Examples:

      Chat (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of Japan?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital is Tokyo.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = provider.chat(\n    messages,\n    model=\"llama2\",\n)\nprint(response)\n

      Chat (full response):

      response = provider.chat(\n    messages,\n    model=\"llama2\",\n    return_full_response=True\n)\nprint(response[\"message\"][\"content\"])\n

      Chat (streaming):

      for chunk in provider.chat(\n    messages,\n    model=\"llama2\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/ollama/provider.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OllamaGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using a specified Ollama model.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the Ollama model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n    Returns:\n        OllamaGenericResponse: The chat response, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Chat (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of Japan?\"},\n            {\"role\": \"assistant\", \"content\": \"The capital is Tokyo.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = provider.chat(\n            messages,\n            model=\"llama2\",\n        )\n        print(response)\n        ```\n\n        Chat (full response):\n        ```python\n        response = provider.chat(\n            messages,\n            model=\"llama2\",\n            return_full_response=True\n        )\n        print(response[\"message\"][\"content\"])\n        ```\n\n        Chat (streaming):\n        ```python\n        for chunk in provider.chat(\n            messages,\n            model=\"llama2\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.chat(\n            model=model, messages=messages, stream=stream, **kwargs\n        )\n\n        if stream:\n            return cast(\n                OllamaGenericResponse,\n                self._stream_chat_response(\n                    cast(Iterator[OllamaChatResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OllamaChatResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response[\"message\"][\"content\"]\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/ollama_provider/#clientai.ollama.Provider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using a specified Ollama model.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the Ollama model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Ollama API.

      {}

      Returns:

      Name Type Description OllamaGenericResponse OllamaGenericResponse

      The generated text, full response object,

      OllamaGenericResponse

      or an iterator for streaming responses.

      Examples:

      Generate text (text only):

      response = provider.generate_text(\n    \"Explain the concept of machine learning\",\n    model=\"llama2\",\n)\nprint(response)\n

      Generate text (full response):

      response = provider.generate_text(\n    \"Explain the concept of machine learning\",\n    model=\"llama2\",\n    return_full_response=True\n)\nprint(response[\"response\"])\n

      Generate text (streaming):

      for chunk in provider.generate_text(\n    \"Explain the concept of machine learning\",\n    model=\"llama2\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/ollama/provider.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OllamaGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt using a specified Ollama model.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the Ollama model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the Ollama API.\n\n    Returns:\n        OllamaGenericResponse: The generated text, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Generate text (text only):\n        ```python\n        response = provider.generate_text(\n            \"Explain the concept of machine learning\",\n            model=\"llama2\",\n        )\n        print(response)\n        ```\n\n        Generate text (full response):\n        ```python\n        response = provider.generate_text(\n            \"Explain the concept of machine learning\",\n            model=\"llama2\",\n            return_full_response=True\n        )\n        print(response[\"response\"])\n        ```\n\n        Generate text (streaming):\n        ```python\n        for chunk in provider.generate_text(\n            \"Explain the concept of machine learning\",\n            model=\"llama2\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.generate(\n            model=model, prompt=prompt, stream=stream, **kwargs\n        )\n\n        if stream:\n            return cast(\n                OllamaGenericResponse,\n                self._stream_generate_response(\n                    cast(Iterator[OllamaStreamResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OllamaResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response[\"response\"]\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/openai_provider/","title":"OpenAI Provider API Reference","text":"

      The OpenAIProvider class implements the AIProvider interface for the OpenAI service. It provides methods for text generation and chat functionality using OpenAI's models.

      "},{"location":"api/specific_providers/openai_provider/#class-definition","title":"Class Definition","text":"

      Bases: AIProvider

      OpenAI-specific implementation of the AIProvider abstract base class.

      This class provides methods to interact with OpenAI's models for text generation and chat functionality.

      Attributes:

      Name Type Description client OpenAIClientProtocol

      The OpenAI client used for making API calls.

      Parameters:

      Name Type Description Default api_key str

      The API key for authenticating with OpenAI.

      required

      Raises:

      Type Description ImportError

      If the OpenAI package is not installed.

      Examples:

      Initialize the OpenAI provider:

      provider = Provider(api_key=\"your-openai-api-key\")\n

      Source code in clientai/openai/provider.py
      class Provider(AIProvider):\n    \"\"\"\n    OpenAI-specific implementation of the AIProvider abstract base class.\n\n    This class provides methods to interact with OpenAI's\n    models for text generation and chat functionality.\n\n    Attributes:\n        client: The OpenAI client used for making API calls.\n\n    Args:\n        api_key: The API key for authenticating with OpenAI.\n\n    Raises:\n        ImportError: If the OpenAI package is not installed.\n\n    Examples:\n        Initialize the OpenAI provider:\n        ```python\n        provider = Provider(api_key=\"your-openai-api-key\")\n        ```\n    \"\"\"\n\n    def __init__(self, api_key: str):\n        if not OPENAI_INSTALLED or Client is None:\n            raise ImportError(\n                \"The openai package is not installed. \"\n                \"Please install it with 'pip install clientai[openai]'.\"\n            )\n        self.client: OpenAIClientProtocol = cast(\n            OpenAIClientProtocol, Client(api_key=api_key)\n        )\n\n    def _stream_response(\n        self,\n        stream: Iterator[OpenAIStreamResponse],\n        return_full_response: bool,\n    ) -> Iterator[Union[str, OpenAIStreamResponse]]:\n        \"\"\"\n        Process the streaming response from OpenAI API.\n\n        Args:\n            stream: The stream of responses from OpenAI API.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, OpenAIStreamResponse]: Processed content or full\n                                              response objects.\n        \"\"\"\n        for chunk in stream:\n            if return_full_response:\n                yield chunk\n            else:\n                content = chunk.choices[0].delta.content\n                if content:\n                    yield content\n\n    def _map_exception_to_clientai_error(self, e: Exception) -> ClientAIError:\n        \"\"\"\n        Maps an OpenAI exception to the appropriate ClientAI exception.\n\n        Args:\n            e (Exception): The exception caught during the API call.\n\n        Raises:\n            ClientAIError: An instance of the appropriate ClientAI exception.\n        \"\"\"\n        error_message = str(e)\n        status_code = None\n\n        if hasattr(e, \"status_code\"):\n            status_code = e.status_code\n        else:\n            try:\n                status_code = int(\n                    error_message.split(\"Error code: \")[1].split(\" -\")[0]\n                )\n            except (IndexError, ValueError):\n                pass\n\n        if (\n            isinstance(e, OpenAIAuthenticationError)\n            or \"incorrect api key\" in error_message.lower()\n        ):\n            return AuthenticationError(\n                error_message, status_code, original_error=e\n            )\n        elif (\n            isinstance(e, openai.OpenAIError)\n            or \"error code:\" in error_message.lower()\n        ):\n            if status_code == 429 or \"rate limit\" in error_message.lower():\n                return RateLimitError(\n                    error_message, status_code, original_error=e\n                )\n            elif status_code == 404 or \"not found\" in error_message.lower():\n                return ModelError(error_message, status_code, original_error=e)\n            elif status_code == 400 or \"invalid\" in error_message.lower():\n                return InvalidRequestError(\n                    error_message, status_code, original_error=e\n                )\n            elif status_code == 408 or \"timeout\" in error_message.lower():\n                return TimeoutError(\n                    error_message, status_code, original_error=e\n                )\n            elif status_code and status_code >= 500:\n                return APIError(error_message, status_code, original_error=e)\n\n        return ClientAIError(error_message, status_code, original_error=e)\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OpenAIGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt using a specified OpenAI model.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the OpenAI model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n        Returns:\n            OpenAIGenericResponse: The generated text, full response object,\n            or an iterator for streaming responses.\n\n        Raises:\n            ClientAIError: If an error occurs during the API call.\n\n        Examples:\n            Generate text (text only):\n            ```python\n            response = provider.generate_text(\n                \"Explain the theory of relativity\",\n                model=\"gpt-3.5-turbo\",\n            )\n            print(response)\n            ```\n\n            Generate text (full response):\n            ```python\n            response = provider.generate_text(\n                \"Explain the theory of relativity\",\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            print(response.choices[0].message.content)\n            ```\n\n            Generate text (streaming):\n            ```python\n            for chunk in provider.generate_text(\n                \"Explain the theory of relativity\",\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.chat.completions.create(\n                model=model,\n                messages=[{\"role\": \"user\", \"content\": prompt}],\n                stream=stream,\n                **kwargs,\n            )\n\n            if stream:\n                return cast(\n                    OpenAIGenericResponse,\n                    self._stream_response(\n                        cast(Iterator[OpenAIStreamResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OpenAIResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response.choices[0].message.content\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> OpenAIGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using a specified OpenAI model.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the OpenAI model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n        Returns:\n            OpenAIGenericResponse: The chat response, full response object,\n            or an iterator for streaming responses.\n\n        Raises:\n            ClientAIError: If an error occurs during the API call.\n\n        Examples:\n            Chat (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n                {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = provider.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n            )\n            print(response)\n            ```\n\n            Chat (full response):\n            ```python\n            response = provider.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                return_full_response=True\n            )\n            print(response.choices[0].message.content)\n            ```\n\n            Chat (streaming):\n            ```python\n            for chunk in provider.chat(\n                messages,\n                model=\"gpt-3.5-turbo\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            response = self.client.chat.completions.create(\n                model=model, messages=messages, stream=stream, **kwargs\n            )\n\n            if stream:\n                return cast(\n                    OpenAIGenericResponse,\n                    self._stream_response(\n                        cast(Iterator[OpenAIStreamResponse], response),\n                        return_full_response,\n                    ),\n                )\n            else:\n                response = cast(OpenAIResponse, response)\n                if return_full_response:\n                    return response\n                else:\n                    return response.choices[0].message.content\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/openai_provider/#clientai.openai.Provider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using a specified OpenAI model.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the OpenAI model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the OpenAI API.

      {}

      Returns:

      Name Type Description OpenAIGenericResponse OpenAIGenericResponse

      The chat response, full response object,

      OpenAIGenericResponse

      or an iterator for streaming responses.

      Raises:

      Type Description ClientAIError

      If an error occurs during the API call.

      Examples:

      Chat (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = provider.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n)\nprint(response)\n

      Chat (full response):

      response = provider.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\nprint(response.choices[0].message.content)\n

      Chat (streaming):

      for chunk in provider.chat(\n    messages,\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/openai/provider.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OpenAIGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using a specified OpenAI model.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the OpenAI model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n    Returns:\n        OpenAIGenericResponse: The chat response, full response object,\n        or an iterator for streaming responses.\n\n    Raises:\n        ClientAIError: If an error occurs during the API call.\n\n    Examples:\n        Chat (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n            {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = provider.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n        )\n        print(response)\n        ```\n\n        Chat (full response):\n        ```python\n        response = provider.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        print(response.choices[0].message.content)\n        ```\n\n        Chat (streaming):\n        ```python\n        for chunk in provider.chat(\n            messages,\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.chat.completions.create(\n            model=model, messages=messages, stream=stream, **kwargs\n        )\n\n        if stream:\n            return cast(\n                OpenAIGenericResponse,\n                self._stream_response(\n                    cast(Iterator[OpenAIStreamResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OpenAIResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response.choices[0].message.content\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/openai_provider/#clientai.openai.Provider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using a specified OpenAI model.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the OpenAI model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the OpenAI API.

      {}

      Returns:

      Name Type Description OpenAIGenericResponse OpenAIGenericResponse

      The generated text, full response object,

      OpenAIGenericResponse

      or an iterator for streaming responses.

      Raises:

      Type Description ClientAIError

      If an error occurs during the API call.

      Examples:

      Generate text (text only):

      response = provider.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-3.5-turbo\",\n)\nprint(response)\n

      Generate text (full response):

      response = provider.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-3.5-turbo\",\n    return_full_response=True\n)\nprint(response.choices[0].message.content)\n

      Generate text (streaming):

      for chunk in provider.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/openai/provider.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> OpenAIGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt using a specified OpenAI model.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the OpenAI model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments to pass to the OpenAI API.\n\n    Returns:\n        OpenAIGenericResponse: The generated text, full response object,\n        or an iterator for streaming responses.\n\n    Raises:\n        ClientAIError: If an error occurs during the API call.\n\n    Examples:\n        Generate text (text only):\n        ```python\n        response = provider.generate_text(\n            \"Explain the theory of relativity\",\n            model=\"gpt-3.5-turbo\",\n        )\n        print(response)\n        ```\n\n        Generate text (full response):\n        ```python\n        response = provider.generate_text(\n            \"Explain the theory of relativity\",\n            model=\"gpt-3.5-turbo\",\n            return_full_response=True\n        )\n        print(response.choices[0].message.content)\n        ```\n\n        Generate text (streaming):\n        ```python\n        for chunk in provider.generate_text(\n            \"Explain the theory of relativity\",\n            model=\"gpt-3.5-turbo\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        response = self.client.chat.completions.create(\n            model=model,\n            messages=[{\"role\": \"user\", \"content\": prompt}],\n            stream=stream,\n            **kwargs,\n        )\n\n        if stream:\n            return cast(\n                OpenAIGenericResponse,\n                self._stream_response(\n                    cast(Iterator[OpenAIStreamResponse], response),\n                    return_full_response,\n                ),\n            )\n        else:\n            response = cast(OpenAIResponse, response)\n            if return_full_response:\n                return response\n            else:\n                return response.choices[0].message.content\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/replicate_provider/","title":"Replicate Provider API Reference","text":"

      The ReplicateProvider class implements the AIProvider interface for the Replicate service. It provides methods for text generation and chat functionality using models hosted on Replicate.

      "},{"location":"api/specific_providers/replicate_provider/#class-definition","title":"Class Definition","text":"

      Bases: AIProvider

      Replicate-specific implementation of the AIProvider abstract base class.

      This class provides methods to interact with Replicate's AI models for text generation and chat functionality.

      Attributes:

      Name Type Description client ReplicateClientProtocol

      The Replicate client used for making API calls.

      Parameters:

      Name Type Description Default api_key str

      The API key for authenticating with Replicate.

      required

      Raises:

      Type Description ImportError

      If the Replicate package is not installed.

      Examples:

      Initialize the Replicate provider:

      provider = Provider(api_key=\"your-replicate-api-key\")\n

      Source code in clientai/replicate/provider.py
      class Provider(AIProvider):\n    \"\"\"\n    Replicate-specific implementation of the AIProvider abstract base class.\n\n    This class provides methods to interact with Replicate's AI models for\n    text generation and chat functionality.\n\n    Attributes:\n        client: The Replicate client used for making API calls.\n\n    Args:\n        api_key: The API key for authenticating with Replicate.\n\n    Raises:\n        ImportError: If the Replicate package is not installed.\n\n    Examples:\n        Initialize the Replicate provider:\n        ```python\n        provider = Provider(api_key=\"your-replicate-api-key\")\n        ```\n    \"\"\"\n\n    def __init__(self, api_key: str):\n        if not REPLICATE_INSTALLED or Client is None:\n            raise ImportError(\n                \"The replicate package is not installed. \"\n                \"Please install it with 'pip install clientai[replicate]'.\"\n            )\n        self.client: ReplicateClientProtocol = Client(api_token=api_key)\n\n    def _process_output(self, output: Any) -> str:\n        \"\"\"\n        Process the output from Replicate API into a string format.\n\n        Args:\n            output: The raw output from Replicate API.\n\n        Returns:\n            str: The processed output as a string.\n        \"\"\"\n        if isinstance(output, List):\n            return \"\".join(str(item) for item in output)\n        elif isinstance(output, str):\n            return output\n        else:\n            return str(output)\n\n    def _wait_for_prediction(\n        self, prediction_id: str, max_wait_time: int = 300\n    ) -> ReplicatePredictionProtocol:\n        \"\"\"\n        Wait for a prediction to complete or fail.\n\n        Args:\n            prediction_id: The ID of the prediction to wait for.\n            max_wait_time: Maximum time to wait in seconds. Defaults to 300.\n\n        Returns:\n            ReplicatePredictionProtocol: The completed prediction.\n\n        Raises:\n            TimeoutError: If the prediction doesn't complete within\n                          the max_wait_time.\n            APIError: If the prediction fails.\n        \"\"\"\n        start_time = time.time()\n        while time.time() - start_time < max_wait_time:\n            prediction = self.client.predictions.get(prediction_id)\n            if prediction.status == \"succeeded\":\n                return prediction\n            elif prediction.status == \"failed\":\n                raise self._map_exception_to_clientai_error(\n                    Exception(f\"Prediction failed: {prediction.error}\")\n                )\n            time.sleep(1)\n\n        raise self._map_exception_to_clientai_error(\n            Exception(\"Prediction timed out\"), status_code=408\n        )\n\n    def _stream_response(\n        self,\n        prediction: ReplicatePredictionProtocol,\n        return_full_response: bool,\n    ) -> Iterator[Union[str, ReplicateStreamResponse]]:\n        \"\"\"\n        Stream the response from a prediction.\n\n        Args:\n            prediction: The prediction to stream.\n            return_full_response: If True, yield full response objects.\n\n        Yields:\n            Union[str, ReplicateStreamResponse]: Processed output or\n                                                 full response objects.\n        \"\"\"\n        metadata = cast(ReplicateStreamResponse, prediction.__dict__.copy())\n        for event in prediction.stream():\n            if return_full_response:\n                metadata[\"output\"] = self._process_output(event)\n                yield metadata\n            else:\n                yield self._process_output(event)\n\n    def _map_exception_to_clientai_error(\n        self, e: Exception, status_code: Optional[int] = None\n    ) -> ClientAIError:\n        \"\"\"\n        Maps a Replicate exception to the appropriate ClientAI exception.\n\n        Args:\n            e (Exception): The exception caught during the API call.\n            status_code (int, optional): The HTTP status code, if available.\n\n        Returns:\n            ClientAIError: An instance of the appropriate ClientAI exception.\n        \"\"\"\n        error_message = str(e)\n        status_code = status_code or getattr(e, \"status_code\", None)\n\n        if (\n            \"authentication\" in error_message.lower()\n            or \"unauthorized\" in error_message.lower()\n        ):\n            return AuthenticationError(\n                error_message, status_code, original_error=e\n            )\n        elif \"rate limit\" in error_message.lower():\n            return RateLimitError(error_message, status_code, original_error=e)\n        elif \"not found\" in error_message.lower():\n            return ModelError(error_message, status_code, original_error=e)\n        elif \"invalid\" in error_message.lower():\n            return InvalidRequestError(\n                error_message, status_code, original_error=e\n            )\n        elif \"timeout\" in error_message.lower() or status_code == 408:\n            return TimeoutError(error_message, status_code, original_error=e)\n        elif status_code == 400:\n            return InvalidRequestError(\n                error_message, status_code, original_error=e\n            )\n        else:\n            return APIError(error_message, status_code, original_error=e)\n\n    def generate_text(\n        self,\n        prompt: str,\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> ReplicateGenericResponse:\n        \"\"\"\n        Generate text based on a given prompt\n        using a specified Replicate model.\n\n        Args:\n            prompt: The input prompt for text generation.\n            model: The name or identifier of the Replicate model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments\n                      to pass to the Replicate API.\n\n        Returns:\n            ReplicateGenericResponse: The generated text, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Generate text (text only):\n            ```python\n            response = provider.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            print(response)\n            ```\n\n            Generate text (full response):\n            ```python\n            response = provider.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n                return_full_response=True\n            )\n            print(response[\"output\"])\n            ```\n\n            Generate text (streaming):\n            ```python\n            for chunk in provider.generate_text(\n                \"Explain quantum computing\",\n                model=\"meta/llama-2-70b-chat:latest\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            prediction = self.client.predictions.create(\n                model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n            )\n\n            if stream:\n                return self._stream_response(prediction, return_full_response)\n            else:\n                completed_prediction = self._wait_for_prediction(prediction.id)\n                if return_full_response:\n                    response = cast(\n                        ReplicateResponse, completed_prediction.__dict__.copy()\n                    )\n                    response[\"output\"] = self._process_output(\n                        completed_prediction.output\n                    )\n                    return response\n                else:\n                    return self._process_output(completed_prediction.output)\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n\n    def chat(\n        self,\n        messages: List[Message],\n        model: str,\n        return_full_response: bool = False,\n        stream: bool = False,\n        **kwargs: Any,\n    ) -> ReplicateGenericResponse:\n        \"\"\"\n        Engage in a chat conversation using a specified Replicate model.\n\n        Args:\n            messages: A list of message dictionaries, each containing\n                      'role' and 'content'.\n            model: The name or identifier of the Replicate model to use.\n            return_full_response: If True, return the full response object.\n                If False, return only the generated text. Defaults to False.\n            stream: If True, return an iterator for streaming responses.\n                Defaults to False.\n            **kwargs: Additional keyword arguments\n                      to pass to the Replicate API.\n\n        Returns:\n            ReplicateGenericResponse: The chat response, full response object,\n            or an iterator for streaming responses.\n\n        Examples:\n            Chat (message content only):\n            ```python\n            messages = [\n                {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n                {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n                {\"role\": \"user\", \"content\": \"What is its population?\"}\n            ]\n            response = provider.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n            )\n            print(response)\n            ```\n\n            Chat (full response):\n            ```python\n            response = provider.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n                return_full_response=True\n            )\n            print(response[\"output\"])\n            ```\n\n            Chat (streaming):\n            ```python\n            for chunk in provider.chat(\n                messages,\n                model=\"meta/llama-2-70b-chat:latest\",\n                stream=True\n            ):\n                print(chunk, end=\"\", flush=True)\n            ```\n        \"\"\"\n        try:\n            prompt = \"\\n\".join(\n                [f\"{m['role']}: {m['content']}\" for m in messages]\n            )\n            prompt += \"\\nassistant: \"\n\n            prediction = self.client.predictions.create(\n                model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n            )\n\n            if stream:\n                return self._stream_response(prediction, return_full_response)\n            else:\n                completed_prediction = self._wait_for_prediction(prediction.id)\n                if return_full_response:\n                    response = cast(\n                        ReplicateResponse, completed_prediction.__dict__.copy()\n                    )\n                    response[\"output\"] = self._process_output(\n                        completed_prediction.output\n                    )\n                    return response\n                else:\n                    return self._process_output(completed_prediction.output)\n\n        except Exception as e:\n            raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/replicate_provider/#clientai.replicate.Provider.chat","title":"chat(messages, model, return_full_response=False, stream=False, **kwargs)","text":"

      Engage in a chat conversation using a specified Replicate model.

      Parameters:

      Name Type Description Default messages List[Message]

      A list of message dictionaries, each containing 'role' and 'content'.

      required model str

      The name or identifier of the Replicate model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Replicate API.

      {}

      Returns:

      Name Type Description ReplicateGenericResponse ReplicateGenericResponse

      The chat response, full response object,

      ReplicateGenericResponse

      or an iterator for streaming responses.

      Examples:

      Chat (message content only):

      messages = [\n    {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n    {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n    {\"role\": \"user\", \"content\": \"What is its population?\"}\n]\nresponse = provider.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n)\nprint(response)\n

      Chat (full response):

      response = provider.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n    return_full_response=True\n)\nprint(response[\"output\"])\n

      Chat (streaming):

      for chunk in provider.chat(\n    messages,\n    model=\"meta/llama-2-70b-chat:latest\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/replicate/provider.py
      def chat(\n    self,\n    messages: List[Message],\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> ReplicateGenericResponse:\n    \"\"\"\n    Engage in a chat conversation using a specified Replicate model.\n\n    Args:\n        messages: A list of message dictionaries, each containing\n                  'role' and 'content'.\n        model: The name or identifier of the Replicate model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments\n                  to pass to the Replicate API.\n\n    Returns:\n        ReplicateGenericResponse: The chat response, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Chat (message content only):\n        ```python\n        messages = [\n            {\"role\": \"user\", \"content\": \"What is the capital of France?\"},\n            {\"role\": \"assistant\", \"content\": \"The capital is Paris.\"},\n            {\"role\": \"user\", \"content\": \"What is its population?\"}\n        ]\n        response = provider.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        print(response)\n        ```\n\n        Chat (full response):\n        ```python\n        response = provider.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n            return_full_response=True\n        )\n        print(response[\"output\"])\n        ```\n\n        Chat (streaming):\n        ```python\n        for chunk in provider.chat(\n            messages,\n            model=\"meta/llama-2-70b-chat:latest\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        prompt = \"\\n\".join(\n            [f\"{m['role']}: {m['content']}\" for m in messages]\n        )\n        prompt += \"\\nassistant: \"\n\n        prediction = self.client.predictions.create(\n            model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n        )\n\n        if stream:\n            return self._stream_response(prediction, return_full_response)\n        else:\n            completed_prediction = self._wait_for_prediction(prediction.id)\n            if return_full_response:\n                response = cast(\n                    ReplicateResponse, completed_prediction.__dict__.copy()\n                )\n                response[\"output\"] = self._process_output(\n                    completed_prediction.output\n                )\n                return response\n            else:\n                return self._process_output(completed_prediction.output)\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"api/specific_providers/replicate_provider/#clientai.replicate.Provider.generate_text","title":"generate_text(prompt, model, return_full_response=False, stream=False, **kwargs)","text":"

      Generate text based on a given prompt using a specified Replicate model.

      Parameters:

      Name Type Description Default prompt str

      The input prompt for text generation.

      required model str

      The name or identifier of the Replicate model to use.

      required return_full_response bool

      If True, return the full response object. If False, return only the generated text. Defaults to False.

      False stream bool

      If True, return an iterator for streaming responses. Defaults to False.

      False **kwargs Any

      Additional keyword arguments to pass to the Replicate API.

      {}

      Returns:

      Name Type Description ReplicateGenericResponse ReplicateGenericResponse

      The generated text, full response object,

      ReplicateGenericResponse

      or an iterator for streaming responses.

      Examples:

      Generate text (text only):

      response = provider.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n)\nprint(response)\n

      Generate text (full response):

      response = provider.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n    return_full_response=True\n)\nprint(response[\"output\"])\n

      Generate text (streaming):

      for chunk in provider.generate_text(\n    \"Explain quantum computing\",\n    model=\"meta/llama-2-70b-chat:latest\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      Source code in clientai/replicate/provider.py
      def generate_text(\n    self,\n    prompt: str,\n    model: str,\n    return_full_response: bool = False,\n    stream: bool = False,\n    **kwargs: Any,\n) -> ReplicateGenericResponse:\n    \"\"\"\n    Generate text based on a given prompt\n    using a specified Replicate model.\n\n    Args:\n        prompt: The input prompt for text generation.\n        model: The name or identifier of the Replicate model to use.\n        return_full_response: If True, return the full response object.\n            If False, return only the generated text. Defaults to False.\n        stream: If True, return an iterator for streaming responses.\n            Defaults to False.\n        **kwargs: Additional keyword arguments\n                  to pass to the Replicate API.\n\n    Returns:\n        ReplicateGenericResponse: The generated text, full response object,\n        or an iterator for streaming responses.\n\n    Examples:\n        Generate text (text only):\n        ```python\n        response = provider.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n        )\n        print(response)\n        ```\n\n        Generate text (full response):\n        ```python\n        response = provider.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n            return_full_response=True\n        )\n        print(response[\"output\"])\n        ```\n\n        Generate text (streaming):\n        ```python\n        for chunk in provider.generate_text(\n            \"Explain quantum computing\",\n            model=\"meta/llama-2-70b-chat:latest\",\n            stream=True\n        ):\n            print(chunk, end=\"\", flush=True)\n        ```\n    \"\"\"\n    try:\n        prediction = self.client.predictions.create(\n            model=model, input={\"prompt\": prompt}, stream=stream, **kwargs\n        )\n\n        if stream:\n            return self._stream_response(prediction, return_full_response)\n        else:\n            completed_prediction = self._wait_for_prediction(prediction.id)\n            if return_full_response:\n                response = cast(\n                    ReplicateResponse, completed_prediction.__dict__.copy()\n                )\n                response[\"output\"] = self._process_output(\n                    completed_prediction.output\n                )\n                return response\n            else:\n                return self._process_output(completed_prediction.output)\n\n    except Exception as e:\n        raise self._map_exception_to_clientai_error(e)\n
      "},{"location":"community/CODE_OF_CONDUCT/","title":"Contributor Covenant Code of Conduct","text":""},{"location":"community/CODE_OF_CONDUCT/#our-pledge","title":"Our Pledge","text":"

      We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

      We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

      "},{"location":"community/CODE_OF_CONDUCT/#our-standards","title":"Our Standards","text":"

      Examples of behavior that contributes to a positive environment for our community include:

      Examples of unacceptable behavior include:

      "},{"location":"community/CODE_OF_CONDUCT/#enforcement-responsibilities","title":"Enforcement Responsibilities","text":"

      Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

      Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.

      "},{"location":"community/CODE_OF_CONDUCT/#scope","title":"Scope","text":"

      This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

      "},{"location":"community/CODE_OF_CONDUCT/#enforcement","title":"Enforcement","text":"

      Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at igor.magalhaes.r@gmail.com. All complaints will be reviewed and investigated promptly and fairly.

      All community leaders are obligated to respect the privacy and security of the reporter of any incident.

      "},{"location":"community/CODE_OF_CONDUCT/#enforcement-guidelines","title":"Enforcement Guidelines","text":"

      Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:

      "},{"location":"community/CODE_OF_CONDUCT/#1-correction","title":"1. Correction","text":"

      Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.

      Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.

      "},{"location":"community/CODE_OF_CONDUCT/#2-warning","title":"2. Warning","text":"

      Community Impact: A violation through a single incident or series of actions.

      Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.

      "},{"location":"community/CODE_OF_CONDUCT/#3-temporary-ban","title":"3. Temporary Ban","text":"

      Community Impact: A serious violation of community standards, including sustained inappropriate behavior.

      Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.

      "},{"location":"community/CODE_OF_CONDUCT/#4-permanent-ban","title":"4. Permanent Ban","text":"

      Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.

      Consequence: A permanent ban from any sort of public interaction within the community.

      "},{"location":"community/CODE_OF_CONDUCT/#attribution","title":"Attribution","text":"

      This Code of Conduct is adapted from the Contributor Covenant, version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

      Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.

      For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.

      "},{"location":"community/CONTRIBUTING/","title":"Contributing to ClientAI","text":"

      Thank you for your interest in contributing to ClientAI! This guide is meant to make it easy for you to get started.

      "},{"location":"community/CONTRIBUTING/#setting-up-your-development-environment","title":"Setting Up Your Development Environment","text":""},{"location":"community/CONTRIBUTING/#cloning-the-repository","title":"Cloning the Repository","text":"

      Start by forking and cloning the ClientAI repository:

      git clone https://github.com/YOUR-GITHUB-USERNAME/clientai.git\n
      "},{"location":"community/CONTRIBUTING/#using-poetry-for-dependency-management","title":"Using Poetry for Dependency Management","text":"

      ClientAI uses Poetry for managing dependencies. If you don't have Poetry installed, follow the instructions on the official Poetry website.

      Once Poetry is installed, navigate to the cloned repository and install the dependencies:

      cd clientai\npoetry install\n

      "},{"location":"community/CONTRIBUTING/#activating-the-virtual-environment","title":"Activating the Virtual Environment","text":"

      Poetry creates a virtual environment for your project. Activate it using:

      poetry shell\n
      "},{"location":"community/CONTRIBUTING/#making-contributions","title":"Making Contributions","text":""},{"location":"community/CONTRIBUTING/#coding-standards","title":"Coding Standards","text":""},{"location":"community/CONTRIBUTING/#testing-with-pytest","title":"Testing with Pytest","text":"

      ClientAI uses pytest for testing. Run tests using:

      poetry run pytest\n

      "},{"location":"community/CONTRIBUTING/#linting","title":"Linting","text":"

      Use mypy for type checking:

      mypy clientai\n

      Use ruff for style:

      ruff check --fix\nruff format\n

      Ensure your code passes linting before submitting.

      "},{"location":"community/CONTRIBUTING/#submitting-your-contributions","title":"Submitting Your Contributions","text":""},{"location":"community/CONTRIBUTING/#creating-a-pull-request","title":"Creating a Pull Request","text":"

      After making your changes:

      "},{"location":"community/CONTRIBUTING/#code-reviews","title":"Code Reviews","text":""},{"location":"community/CONTRIBUTING/#code-of-conduct","title":"Code of Conduct","text":"

      Please adhere to our Code of Conduct to maintain a welcoming and inclusive environment.

      Thank you for contributing to ClientAI\ud83d\ude80

      "},{"location":"community/LICENSE/","title":"License","text":"

      MIT License

      Copyright (c) 2024 Igor Benav

      Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

      The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

      THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

      "},{"location":"community/overview/","title":"Community Overview","text":"

      Welcome to the project's community hub. Here, you'll find essential resources and guidelines that are crucial for contributing to and participating in the project. Please take the time to familiarize yourself with the following documents:

      "},{"location":"community/overview/#table-of-contents","title":"Table of Contents","text":""},{"location":"community/overview/#contributing","title":"Contributing","text":"

      View the Contributing Guidelines

      Interested in contributing to the project? Great! The contributing guidelines will provide you with all the information you need to get started. This includes how to submit issues, propose changes, and the process for submitting pull requests.

      "},{"location":"community/overview/#code-of-conduct","title":"Code of Conduct","text":"

      View the Code of Conduct

      The Code of Conduct outlines the standards and behaviors expected of our community members. It's crucial to ensure a welcoming and inclusive environment for everyone. Please take the time to read and adhere to these guidelines.

      "},{"location":"community/overview/#license","title":"License","text":"

      View the License

      The license document outlines the terms under which our project can be used, modified, and distributed. Understanding the licensing is important for both users and contributors of the project.

      Thank you for being a part of our community and for contributing to our project's success!

      "},{"location":"examples/ai_dungeon_master/","title":"ClientAI Tutorial: Building an AI Dungeon Master","text":"

      In this tutorial, we'll walk through the process of creating an AI-powered Dungeon Master using the ClientAI package. We'll explain each concept in detail and build our game step-by-step, providing context for every decision we make, both technical and gameplay-related.

      "},{"location":"examples/ai_dungeon_master/#table-of-contents","title":"Table of Contents","text":"
      1. Introduction
      2. Setting Up the Project 2.5 Creating the Project Structure
      3. Creating the Game Structure
      4. Integrating Multiple AI Providers
      5. Developing the Enhanced AI Dungeon Master
      6. Main Script that Runs the Game
      7. Running the Game
      8. Conclusion and Further Improvements
      "},{"location":"examples/ai_dungeon_master/#1-introduction","title":"1. Introduction","text":"

      ClientAI is a Python package that provides a unified interface for interacting with multiple AI providers. In this tutorial, we'll use ClientAI to create an AI Dungeon Master that can generate story elements, NPC dialogues, and dynamic environments using different AI models.

      Our AI Dungeon Master will be a text-based role-playing game (RPG) where the game's content is dynamically generated by AI. This approach allows for infinite replayability and unique experiences for each player.

      We'll focus on explaining both technical decisions (such as class structures and AI interactions) and gameplay decisions (like character creation and game mechanics).

      The final result is available in this github repo.

      "},{"location":"examples/ai_dungeon_master/#2-setting-up-the-project","title":"2. Setting Up the Project","text":"

      First, let's set up our project and install the necessary dependencies.

      1. Create a new directory for your project:
      mkdir ai_dungeon_master\ncd ai_dungeon_master\n
      1. Install ClientAI and its dependencies:

        If you want to use poetry, you may skip this part.

      pip install clientai[all]\n

      This command installs ClientAI with support for all providers. If you only need specific providers, you can install them individually (e.g., pip install clientai[openai] for just OpenAI support).

      1. Install additional dependencies:

        If you want to use poetry, you may also skip this part.

      We'll need some additional packages for our project.

      pip install requests\n

      Ollama is a local AI model server that we'll use to run the Llama 3 model. Follow these steps to install Ollama:

      After installing Ollama, you need to download the Llama 3 model. Run the following command:

      ollama pull llama3\n

      This command will download and set up the Llama 3 model for use with Ollama. The download might take some time depending on your internet connection.

      These imports will be used throughout our project:

      "},{"location":"examples/ai_dungeon_master/#25-creating-the-project-structure","title":"2.5 Creating the Project Structure","text":"

      Before we dive into the code, let's set up a proper project structure. This will help us organize our code and make it easier to maintain and expand in the future.

      1. Create the following directory structure:
      clientai_dungeon_master/\n\u251c\u2500\u2500 pyproject.toml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 .env\n\u2514\u2500\u2500 ai_dungeon_master/\n    \u251c\u2500\u2500 __init__.py\n    \u251c\u2500\u2500 main.py\n    \u251c\u2500\u2500 game/\n    \u2502   \u251c\u2500\u2500 __init__.py\n    \u2502   \u251c\u2500\u2500 character.py\n    \u2502   \u251c\u2500\u2500 game_state.py\n    \u2502   \u2514\u2500\u2500 dungeon_master.py\n    \u251c\u2500\u2500 ai/\n    \u2502   \u251c\u2500\u2500 __init__.py\n    \u2502   \u251c\u2500\u2500 ai_providers.py\n    \u2502   \u2514\u2500\u2500 ollama_server.py\n    \u2514\u2500\u2500 utils/\n        \u251c\u2500\u2500 __init__.py\n        \u2514\u2500\u2500 text_utils.py\n
      1. Create a pyproject.toml file in the root directory with the following content:

        If you're using pip directly, you may skip this part

      [tool.poetry]\nname = \"clientai-dungeon-master\"\nversion = \"0.1.0\"\ndescription = \"An AI-powered dungeon master for text-based RPG adventures\"\nauthors = [\"Your Name <your.email@example.com>\"]\nreadme = \"README.md\"\npackages = [{include = \"clientai_dungeon_master\"}]\n\n[tool.poetry.dependencies]\npython = \"^3.11\"\nclientai = \"^0.1.2\"\nrequests = \"^2.32.3\"\npython-decouple = \"^3.8\"\n\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n

      and run

      poetry install\n
      1. Create a .gitignore file in the root directory with the following content:
      # Python\n__pycache__/\n*.py[cod]\n*.pyo\n*.pyd\n.Python\nenv/\nvenv/\nENV/\n\n# Poetry\n.venv/\ndist/\n\n# Environment variables\n.env\n\n# IDEs\n.vscode/\n.idea/\n\n# Logs\n*.log\n\n# OS generated files\n.DS_Store\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\n
      1. Create a .env file in the root directory to store your API keys:
      OPENAI_API_KEY=your_openai_api_key_here\nREPLICATE_API_KEY=your_replicate_api_key_here\n

      Remember to replace your_openai_api_key_here and your_replicate_api_key_here with your actual API keys.

      1. Move the relevant code into the appropriate files based on the new structure.

      This structure separates concerns, making the code more modular and easier to maintain. It also sets up the project for potential future expansion, such as adding more game features or integrating additional AI providers.

      "},{"location":"examples/ai_dungeon_master/#3-creating-the-game-structure","title":"3. Creating the Game Structure","text":"

      Before integrating AI, we'll create the basic structure of our game. This includes classes to represent the character, game state, and AI providers.

      "},{"location":"examples/ai_dungeon_master/#character-class","title":"Character Class","text":"

      The Character class represents the player's character in the game. It stores essential character information like name, race, class, background story, and stats.

      ai_dungeon_master/game/character.py
      class Character:\n    def __init__(self, name: str, race: str, class_type: str, background: str, stats: dict):\n        self.name = name\n        self.race = race\n        self.class_type = class_type\n        self.background = background\n        self.stats = stats\n\n    def __str__(self):\n        return f\"Name: {self.name}, Race: {self.race}, Class: {self.class_type}, Background: {self.background}, Stats: {self.stats}\"\n

      Here we define a character with attributes like a name, race, class, background and stats (like Strength, Intelligence, Wisdom). This is really simple, but will be enough to customize what happens in the story.

      We'll also define the __str__ method to be able to print the character's details easily.

      "},{"location":"examples/ai_dungeon_master/#gamestate-class","title":"GameState Class","text":"

      The GameState class keeps track of the game's current state, including the character's status, location, inventory, health, experience, and quests.

      ai_dungeon_master/game/game_state.py
      from typing import Optional\n\nfrom .character import Character\n\nclass GameState:\n    def __init__(self, character: Character):\n        self.character = character\n        self.location = \"entrance\"\n        self.inventory = []\n        self.health = 100\n        self.experience = 0\n        self.quests = []\n\n    def update(self, location: Optional[str] = None, item: Optional[str] = None, health_change: int = 0, exp_gain: int = 0, quest: Optional[str] = None):\n        if location:\n            self.location = location\n        if item:\n            self.inventory.append(item)\n        self.health = max(0, min(100, self.health + health_change))\n        self.experience += exp_gain\n        if quest:\n            self.quests.append(quest)\n\n    def __str__(self):\n        return f\"{str(self.character)}\\nLocation: {self.location}, Health: {self.health}, XP: {self.experience}, Inventory: {', '.join(self.inventory)}, Quests: {', '.join(self.quests)}\"\n

      We keep track of the state to keep a more consistent experience, we can't expect this to be always generated by the llm. We need to pass the game state as a guide to generate the content.

      The update method allows easy updates to the game state, we'll keep health within 0 to 100, and add an inventory and quests to add more depth to the game.

      "},{"location":"examples/ai_dungeon_master/#4-integrating-multiple-ai-providers","title":"4. Integrating Multiple AI Providers","text":"

      We'll use ClientAI to create a class that manages interactions with different AI providers. This abstraction allows us to switch between providers seamlessly.

      "},{"location":"examples/ai_dungeon_master/#aiproviders-class","title":"AIProviders Class","text":"ai_dungeon_master/ai/ai_providers.py
      from typing import List\n\nfrom clientai import ClientAI\n\nclass AIProviders:\n    def __init__(self):\n        self.openai = ClientAI('openai', api_key=openai_token)\n        self.replicate = ClientAI('replicate', api_key=replicate_token)\n        self.ollama = ClientAI('ollama', host=\"http://localhost:11434\")\n\n    def chat(\n        self,\n        messages: List[dict],\n        provider: str = 'openai',\n        openai_model=\"gpt-4o-mini\",\n        replicate_model=\"meta/meta-llama-3-8b-instruct\",\n        ollama_model=\"llama3\",\n    ):\n        if provider == 'openai':\n            return self.openai.chat(messages, model=openai_model, stream=True)\n        elif provider == 'replicate':\n            return self.replicate.chat(messages, model=replicate_model, stream=True)\n        elif provider == 'ollama':\n            return self.ollama.chat(messages, model=ollama_model, stream=True)\n        else:\n            raise ValueError(f\"Unknown provider: {provider}\")\n

      We create instances of ClientAI for each provider with the necessary API keys or host information, then abstract the chat method to allow for easy switching between AI providers.

      We are going to use ClientAI to use multiple AI models from different providers, since we want to find what is the best model for each task balancing performance and costs.

      "},{"location":"examples/ai_dungeon_master/#managing-api-keys-with-python-decouple-and-a-env-file","title":"Managing API Keys with python-decouple and a .env File","text":"

      To securely handle your API keys without exposing them in your codebase, you can use the python-decouple package and store your keys in a .env file. This approach keeps sensitive information out of your code and version control.

      1. Install python-decouple: You may skip this if you used poetry
      pip install python-decouple\n
      1. Create a .env File: In your project's root directory, make sure the .env has your API keys:
      OPENAI_API_KEY=your_openai_api_key_here\nREPLICATE_API_KEY=your_replicate_api_key_here\n

      Replace your_openai_api_key_here and your_replicate_api_key_here with your actual API keys.

      1. Ensure .env is added to .gitignore: To prevent the .env file from being tracked by version control, ensure it is in your .gitignore file:
      # .gitignore\n.env\n

      This ensures your API keys remain private and aren't pushed to repositories like GitHub.

      1. Access the API Keys in Your Code: Import config from decouple and retrieve the API keys:
      ai_dungeon_master/ai/ai_providers.py
      from decouple import config\n\nopenai_token = config('OPENAI_API_KEY')\nreplicate_token = config('REPLICATE_API_KEY')\n

      Now, you can use these variables when initializing your AI providers.

      1. Update the AIProviders Class: ai_dungeon_master/ai/ai_providers.py
        from typing import List\n\nfrom clientai import ClientAI\nfrom decouple import config\n\nopenai_token = config('OPENAI_API_KEY')\nreplicate_token = config('REPLICATE_API_KEY')he ol\n\nclass AIProviders:\n    def __init__(self):\n        self.openai = ClientAI('openai', api_key=openai_token)\n        self.replicate = ClientAI('replicate', api_key=replicate_token)\n        self.ollama = ClientAI('ollama', host=\"http://localhost:11434\")\n\n ...\n
      "},{"location":"examples/ai_dungeon_master/#managing-ai-servers","title":"Managing AI Servers","text":"

      We need to ensure that local AI servers (like Ollama) are running before the game starts, so let's define a function to start ollama.

      ai_dungeon_master/ai/ollama_server.py
      import subprocess\nimport time\nimport requests\nimport logging\n\nlogging.basicConfig(level=logging.INFO)\n\ndef start_ollama_server(timeout: int = 30, check_interval: float = 1.0):\n    \"\"\"\n    Start the Ollama server and wait for it to be ready.\n    \"\"\"\n    logging.info(\"Starting Ollama server...\")\n\n    try:\n        process = subprocess.Popen(\n            ['ollama', 'serve'],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            text=True\n        )\n    except subprocess.SubprocessError as e:\n        logging.error(f\"Failed to start Ollama process: {e}\")\n        raise\n\n    start_time = time.time()\n    while time.time() - start_time < timeout:\n        try:\n            response = requests.get('http://localhost:11434', timeout=5)\n            if response.status_code == 200:\n                logging.info(\"Ollama server is ready.\")\n                return process\n        except requests.ConnectionError:\n            pass\n        except requests.RequestException as e:\n            logging.error(f\"Unexpected error when checking Ollama server: {e}\")\n            process.terminate()\n            raise\n\n        if process.poll() is not None:\n            stdout, stderr = process.communicate()\n            logging.error(f\"Ollama process terminated unexpectedly. stdout: {stdout}, stderr: {stderr}\")\n            raise subprocess.SubprocessError(\"Ollama process terminated unexpectedly\")\n\n        time.sleep(check_interval)\n\n    process.terminate()\n    raise TimeoutError(f\"Ollama server did not start within {timeout} seconds\")\n

      By managing the server startup within the code, we reduce the setup burden on the player.

      "},{"location":"examples/ai_dungeon_master/#5-developing-the-enhanced-ai-dungeon-master","title":"5. Developing the Enhanced AI Dungeon Master","text":"

      Now we'll develop the main class that controls the game logic and interactions with AI models.

      "},{"location":"examples/ai_dungeon_master/#enhancedaidungeonmaster-class","title":"EnhancedAIDungeonMaster Class","text":"ai_dungeon_master/game/dungeon_master.py
      from typing import Tuple, List\nimport random\nimport time\n\nfrom ai.ai_providers import AIProviders\nfrom utils.text_utils import print_separator\nfrom game.character import Character\nfrom game.game_state import GameState\n\nclass EnhancedAIDungeonMaster:\n    def __init__(self):\n        self.ai = AIProviders()\n        self.conversation_history = []\n        self.game_state = None\n\n    # Methods will be added here...\n
      "},{"location":"examples/ai_dungeon_master/#creating-the-character","title":"Creating the Character","text":"

      We need a method to create the player's character. We'll use AI to do this automatically for us:

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def create_character(self):\n        print(\"Let's create your character!\")\n        name = input(\"What is your character's name? \")\n\n        # We start by defining a prompt\n        character_prompt = f\"\"\"\n        Create a character for a fantasy RPG with the following details:\n        Name: {name}\n\n        Please provide:\n        1. A suitable race (e.g., Human, Elf, Dwarf, etc.)\n        2. A class (e.g., Warrior, Mage, Rogue, etc.)\n        3. A brief background story (2-3 sentences)\n        4. Basic stats (Strength, Dexterity, Constitution, Intelligence, Wisdom, Charisma) on a scale of 1-20\n\n        Format the response as follows:\n        Race: [race]\n        Class: [class]\n        Background: [background story]\n        Stats:\n        - Strength: [value]\n        - Dexterity: [value]\n        - Constitution: [value]\n        - Intelligence: [value]\n        - Wisdom: [value]\n        - Charisma: [value]\n        \"\"\"\n\n        # And we add this prompt to our chat history\n        self.add_to_history(\"user\", character_prompt)\n        character_info = self.print_stream(self.ai.chat(self.conversation_history, provider='openai'))\n\n        # Parse the character info\n        lines = character_info.strip().split('\\n')\n        race = class_type = background = \"\"\n        stats = {}\n\n        for line in lines:\n            if line.startswith(\"Race:\"):\n                race = line.split(\": \", 1)[1].strip()\n            elif line.startswith(\"Class:\"):\n                class_type = line.split(\": \", 1)[1].strip()\n            elif line.startswith(\"Background:\"):\n                background = line.split(\": \", 1)[1].strip()\n            elif \":\" in line and not line.startswith(\"Stats:\"):\n                key, value = line.split(\":\", 1)\n                key = key.strip(\"- \")\n                try:\n                    stats[key] = int(value.strip())\n                except ValueError:\n                    stats[key] = random.randint(1, 20)\n\n        # Just in case, let's ensure it'the player has stats\n        # If any stat is missing, assign a random value\n        for stat in [\"Strength\", \"Dexterity\", \"Constitution\", \"Intelligence\", \"Wisdom\", \"Charisma\"]:\n            if stat not in stats:\n                stats[stat] = random.randint(1, 20)\n\n        # And let's also ensure other required attributes are assigned\n        # If race, class, or background is empty, assign default values\n        race = race or \"Human\"\n        class_type = class_type or \"Adventurer\"\n        background = background or \"A mysterious traveler with an unknown past.\"\n\n        return Character(name, race, class_type, background, stats)\n

      We'll use GPT 4o mini to create initial stuff we need, like the race, class, background etc, and extract the information from the generated content to handle errors.

      Note that since we are leaving this information to the LLM, the name will influence the attributes. If you need a more consistently random generation, do it in the python code and just pass it to the prompt.

      "},{"location":"examples/ai_dungeon_master/#maintaining-conversation-history","title":"Maintaining Conversation History","text":"

      To provide context to the AI, we maintain a conversation history.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def add_to_history(self, role: str, content: str):\n        if not self.conversation_history or self.conversation_history[-1]['content'] != content:\n            self.conversation_history.append({\"role\": role, \"content\": content})\n            if len(self.conversation_history) > 10:\n                self.conversation_history = self.conversation_history[-10:]\n

      Here we will ensure we don't add the same message twice. Plus, we are limiting the conversation history to 10 messages to prevent exceeding token limits.

      "},{"location":"examples/ai_dungeon_master/#generating-the-environment","title":"Generating the Environment","text":"

      Next, let's create detailed environments to enhance the imersion.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def generate_environment(self):\n        if not hasattr(self, 'current_environment'):\n            prompt = f\"\"\"\n            The character {self.game_state.character.name} is a {self.game_state.character.race} {self.game_state.character.class_type} \n            currently in the {self.game_state.location}.\n\n            Describe the current environment in detail, focusing on:\n            1. The physical setting and atmosphere\n            2. Any notable NPCs present\n            3. Interesting objects or features\n\n            Do not create a new character or change any existing character details.\n            Do not include any actions or dialogue for {self.game_state.character.name}.\n\n            End your description with one of these tags if appropriate:\n            [INTERACT_OPPORTUNITY] - if there's a chance for the player to interact with someone or something\n            [QUEST_OPPORTUNITY] - if there's a potential quest or mission available\n            \"\"\"\n            self.add_to_history(\"user\", prompt)\n            self.current_environment = self.ai.chat(self.conversation_history, provider='openai')\n        return self.current_environment\n

      Here we instruct the AI to provide specific details, and we use tags for opportunities. We'll parse these tags INTERACT_OPPORTUNITY and QUEST_OPPORTUNITY later to perform other actions.

      We'll also store the environment description to avoid regenerating it unnecessarily.

      "},{"location":"examples/ai_dungeon_master/#handling-player-actions","title":"Handling Player Actions","text":"

      Now let's process the player's actions and generate outcomes. We'll run this one locally with ollama.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def handle_player_action(self, action):\n        prompt = f\"\"\"\n        The player ({self.game_state.character.name}, a {self.game_state.character.race} {self.game_state.character.class_type}) \n        attempts to {action} in {self.game_state.location}. \n        Describe the immediate result of this action, focusing on the environment and NPCs' reactions.\n        Do not generate any further actions or dialogue for {self.game_state.character.name}.\n        If the player is trying to interact with an NPC, end your response with [NPC_INTERACTION: <npc_name>].\n        \"\"\"\n        self.add_to_history(\"user\", prompt)\n        return self.ai.chat(self.conversation_history, provider='ollama')\n

      Here we pass what the player wants to do to the AI and generate the outcomes for the players actions. We are also using a tag here for interactions, so we can process those in a different way.

      "},{"location":"examples/ai_dungeon_master/#generating-npc-dialogue","title":"Generating NPC Dialogue","text":"

      Next, let's create a function to generate a dialogue with an npc. We'll use replicate with llama3 8b for this.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def generate_npc_dialogue(self, npc_name: str, player_input: str):\n        prompt = f\"\"\"\n        The player ({self.game_state.character.name}) said to {npc_name}: \"{player_input}\"\n        Generate a single, natural response from {npc_name}, addressing the player's input directly.\n        If the player is asking about items for sale, list 2-3 specific items with brief descriptions and prices.\n        Do not include any actions or responses from the player character.\n        Keep the response concise and relevant to the player's input.\n        Do not include any formatting tags, headers, or quotation marks in your response.\n        Respond as if you are {npc_name} speaking directly to the player.\n        \"\"\"\n        self.add_to_history(\"user\", prompt)\n        return self.ai.chat(self.conversation_history, provider='replicate')\n

      Note that in the prompt we ensure the AI provides responses that are in character and appropriate, so we can pass this directly to the player.

      "},{"location":"examples/ai_dungeon_master/#handling-conversations","title":"Handling Conversations","text":"

      We manage conversations with NPCs in a separate method. We start with a conversation loop, to allow the player to have a back-and-forth dialogue with an NPC, and we reset the conversation history to focus the AI on the dialogue.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def handle_conversation(self, npc_name):\n        print(f\"\\nYou are now in conversation with {npc_name}.\")\n        self.conversation_history = [\n            {\"role\": \"system\", \"content\": f\"You are {npc_name}, speaking directly to the player. Respond naturally and in character.\"}\n        ]\n        while True:\n            player_input = input(f\"\\nWhat do you say to {npc_name}? (or type 'end conversation' to stop): \")\n            if player_input.lower() == \"end conversation\":\n                print(f\"\\nYou end your conversation with {npc_name}.\")\n                break\n\n            print(f\"\\n{npc_name}:\")\n            self.print_stream(self.generate_npc_dialogue(npc_name, player_input))\n

      We also add the possibility for the player to end the conversation at any time.

      "},{"location":"examples/ai_dungeon_master/#updating-the-game-state","title":"Updating the Game State","text":"

      We update the game state based on the outcomes provided by the AI.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def update_game_state(self, outcome):\n        if \"found\" in outcome.lower():\n            item = outcome.split(\"found\")[1].split(\".\")[0].strip()\n            self.game_state.update(item=item)\n        if \"new area\" in outcome.lower():\n            new_location = outcome.split(\"new area\")[1].split(\".\")[0].strip()\n            self.game_state.update(location=new_location)\n        if \"damage\" in outcome.lower():\n            self.game_state.update(health_change=-10)\n        if \"healed\" in outcome.lower():\n            self.game_state.update(health_change=10)\n        if \"quest\" in outcome.lower():\n            quest = outcome.split(\"quest\")[1].split(\".\")[0].strip()\n            self.game_state.update(quest=quest)\n        self.game_state.update(exp_gain=5)\n

      This is a simpler way to do it, but we will just look for keywords in the AI's response to determine what changes to make. This isn't the most consistent way to do it, but is easy to do and will easily allow the game to respond to the player's actions, making the experience feel more dynamic.

      "},{"location":"examples/ai_dungeon_master/#processing-story-elements","title":"Processing Story Elements","text":"

      Let's process the AI-generated story to extract content and any special flags.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def process_story(self, story_generator) -> Tuple[str, List[str]]:\n        story = self.print_stream(story_generator, print_output=True)\n        story_lines = story.split('\\n')\n\n        flags = []\n        for line in reversed(story_lines):\n            if line.strip().startswith('[') and line.strip().endswith(']'):\n                flags.append(line.strip('[').strip(']'))\n                story_lines.remove(line)\n            else:\n                break\n\n        story_content = '\\n'.join(story_lines).strip()\n\n        if any(flag.startswith(\"NPC_INTERACTION:\") for flag in flags):\n            npc_name = next(flag.split(':')[1].strip() for flag in flags if flag.startswith(\"NPC_INTERACTION:\"))\n            return story_content, npc_name\n        else:\n            return story_content, flags\n

      Here is where we'll actually separates the special tags we defined earlier from the story content and ensure the player sees a coherent story without tags.

      "},{"location":"examples/ai_dungeon_master/#printing-streamed-content","title":"Printing Streamed Content","text":"

      We also don't want to wait until the whole content is generated to print, so let's define a function to display the AI's response in real-time, simulating typing.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def print_stream(self, stream, print_output=True) -> str:\n        full_text = \"\"\n        for chunk in stream:\n            if print_output:\n                print(chunk, end='', flush=True)\n            full_text += chunk\n            time.sleep(0.03)\n        if print_output:\n            print()\n        return full_text\n
      "},{"location":"examples/ai_dungeon_master/#main-game-loop","title":"Main Game Loop","text":"

      Finally, we bring everything together in the play_game method.

      ai_dungeon_master/game/dungeon_master.py
      class EnhancedAIDungeonMaster:\n    ...\n    def play_game(self):\n        print(\"Welcome to the Dungeon!\")\n        character = self.create_character()\n        self.game_state = GameState(character)\n\n        print(\"\\nYour adventure begins...\")\n        while True:\n            print_separator()\n            environment_description, env_flags = self.process_story(self.generate_environment())\n\n            if \"INTERACT_OPPORTUNITY\" in env_flags:\n                print(\"\\nThere seems to be an opportunity to interact.\")\n            if \"QUEST_OPPORTUNITY\" in env_flags:\n                print(\"\\nThere might be a quest available.\")\n\n            action = input(\"\\nWhat do you do? \")\n            if action.lower() == \"quit\":\n                break\n\n            print(\"\\nOutcome:\")\n            outcome, npc_interaction = self.process_story(self.handle_player_action(action))\n\n            self.update_game_state(outcome)\n\n            if npc_interaction:\n                self.handle_conversation(npc_interaction)\n\n            print_separator()\n            print(f\"Current state: {str(self.game_state)}\")\n\n            if self.game_state.health <= 0:\n                print(\"Game Over! Your health reached 0.\")\n                break\n\n            if hasattr(self, 'current_environment'):\n                del self.current_environment\n

      The game loop continuously processes player actions and updates the game state, new environments are generated to keep the game dynamic and the player is allowed to quit whenever they want.

      Plus, the game is over if health reaches zero.

      "},{"location":"examples/ai_dungeon_master/#helper-methods","title":"Helper Methods","text":"

      Let's also create some methods for improved user experience, we want to separate content to make it easier to see and also create a print_slowly to simulate streamed content in important messages.

      ai_dungeon_master/utils/text_utils.py
      import time\n\ndef print_separator(self):\n    print(\"\\n\" + \"=\" * 50 + \"\\n\")\n\ndef print_slowly(text, delay=0.03):\n    for char in text:\n        print(char, end='', flush=True)\n        time.sleep(delay)\n    print()\n
      "},{"location":"examples/ai_dungeon_master/#6-main-script-that-runs-the-game","title":"6. Main Script that Runs the Game","text":"

      At our main script, we initialize and start the game.

      ai_dungeon_master/main.py
      from game.dungeon_master import EnhancedAIDungeonMaster\nfrom utils.text_utils import print_slowly\nfrom ai.ollama_server import start_ollama_server\n\ndef main():\n    print_slowly(\"Welcome to the AI Dungeon Master!\")\n    print_slowly(\"Prepare for an adventure guided by multiple AI models.\")\n    print_slowly(\"Type 'quit' at any time to exit the game.\")\n    print()\n\n    # Start the Ollama server before the game begins\n    ollama_process = start_ollama_server()\n\n    game = EnhancedAIDungeonMaster()\n    game.play_game()\n\n    print_slowly(\"Thank you for playing AI Dungeon Master!\")\n\n    # Terminate the Ollama server when the game ends\n    if ollama_process:\n        ollama_process.terminate()\n\nif __name__ == \"__main__\":\n    main()\n
      "},{"location":"examples/ai_dungeon_master/#7-running-the-game","title":"7. Running the Game","text":"
      1. Ensure you're in the root directory of the project.

      2. Run the game using Poetry:

      poetry run python ai_dungeon_master/main.py\n

      Or directly if you used pip:

      python ai_dungeon_master/main.py\n

      This command will execute the main.py file, which should contain the game initialization and main loop.

      "},{"location":"examples/ai_dungeon_master/#8-conclusion-and-further-improvements","title":"8. Conclusion and Further Improvements","text":"

      Congratulations! You've now created an AI Dungeon Master using the ClientAI package. This project demonstrates how to integrate multiple AI providers and manage game logic to create a dynamic and engaging text-based RPG.

      "},{"location":"examples/ai_dungeon_master/#potential-improvements","title":"Potential Improvements:","text":"
      1. Error Handling: Implement try-except blocks to handle exceptions and improve robustness.
      2. Saving and Loading: Add functionality to save and load game states.
      3. Combat System: Develop a combat system that uses character stats and AI to determine outcomes.
      4. Quest Management: Create a more complex quest system with objectives and rewards.
      5. Multiplayer: Explore options for multiplayer interactions.
      6. User Interface: Develop a GUI for a more user-friendly experience.
      7. AI Fine-Tuning: Customize AI models for more consistent and relevant responses.

      By implementing these improvements, you can further enhance the gameplay experience and create an even more immersive and engaging AI-driven RPG.

      "},{"location":"examples/overview/","title":"Examples Overview","text":"

      Welcome to the Examples section of the ClientAI documentation. This section provides practical, real-world examples of how to use ClientAI in various applications. Whether you're a beginner looking to get started or an experienced developer seeking inspiration for more complex projects, these examples will demonstrate the versatility and power of ClientAI.

      "},{"location":"examples/overview/#featured-examples","title":"Featured Examples","text":"

      Our examples cover a range of applications, from simple text generation to more complex AI-driven systems. Here's an overview of what you'll find in this section:

      1. AI Dungeon Master: A text-based RPG that uses multiple AI providers to create an interactive storytelling experience.

        • AI Dungeon Master Tutorial
      2. Chatbot Assistant: A simple chatbot that can answer questions and engage in conversation using ClientAI.

        • Soon
      3. Sentiment Analyzer: An application that analyzes the sentiment of given text using different AI models.

        • Soon
      "},{"location":"examples/overview/#usage","title":"Usage","text":"

      Each example is documented on its own page, where you'll find:

      "},{"location":"examples/overview/#quick-start-example","title":"Quick Start Example","text":"

      Here's a simple example to get you started with ClientAI:

      from clientai import ClientAI\n\n# Initialize the client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate a short story\nprompt = \"Write a short story about a robot learning to paint.\"\nresponse = client.generate_text(prompt, model=\"gpt-3.5-turbo\")\n\nprint(response)\n

      For more general usage instructions, please refer to our Quickstart Guide.

      "},{"location":"examples/overview/#customizing-examples","title":"Customizing Examples","text":"

      Feel free to use these examples as starting points for your own projects. You can modify and extend them to suit your specific needs. If you create an interesting project using ClientAI, we'd love to hear about it!

      "},{"location":"examples/overview/#contributing","title":"Contributing","text":"

      We welcome contributions to our examples collection! If you've created an example that you think would be valuable to others, please consider submitting it. Check out our Contributing Guidelines for more information on how to contribute.

      "},{"location":"examples/overview/#feedback","title":"Feedback","text":"

      Your feedback helps us improve our examples and documentation. If you have suggestions for new examples, improvements to existing ones, or any other feedback, please let us know through GitHub issues or our community channels.

      Explore each example to see ClientAI in action and learn how to implement AI-driven features in your own projects.

      "},{"location":"usage/chat_functionality/","title":"Chat Functionality in ClientAI","text":"

      This guide covers how to leverage ClientAI's chat functionality. You'll learn about creating chat conversations, managing context, and handling chat-specific features across supported providers.

      "},{"location":"usage/chat_functionality/#table-of-contents","title":"Table of Contents","text":"
      1. Basic Chat Interaction
      2. Managing Conversation Context
      3. Advanced Chat Features
      4. Provider-Specific Chat Capabilities
      5. Best Practices
      "},{"location":"usage/chat_functionality/#basic-chat-interaction","title":"Basic Chat Interaction","text":"

      To use the chat functionality in ClientAI, use the chat method:

      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\nmessages = [\n    {\"role\": \"user\", \"content\": \"Hello, who are you?\"}\n]\n\nresponse = client.chat(messages, model=\"gpt-3.5-turbo\")\nprint(response)\n\n# Continue the conversation\nmessages.append({\"role\": \"assistant\", \"content\": response})\nmessages.append({\"role\": \"user\", \"content\": \"What can you help me with?\"})\n\nresponse = client.chat(messages, model=\"gpt-3.5-turbo\")\nprint(response)\n

      This example demonstrates a simple back-and-forth conversation.

      "},{"location":"usage/chat_functionality/#managing-conversation-context","title":"Managing Conversation Context","text":"

      Effective context management is crucial for coherent conversations:

      conversation = [\n    {\"role\": \"system\", \"content\": \"You are a helpful assistant specializing in Python programming.\"},\n    {\"role\": \"user\", \"content\": \"How do I use list comprehensions in Python?\"}\n]\n\nresponse = client.chat(conversation, model=\"gpt-3.5-turbo\")\nprint(response)\n\nconversation.append({\"role\": \"assistant\", \"content\": response})\nconversation.append({\"role\": \"user\", \"content\": \"Can you give an example?\"})\n\nresponse = client.chat(conversation, model=\"gpt-3.5-turbo\")\nprint(response)\n

      This example shows how to maintain context across multiple exchanges, including a system message to set the assistant's role.

      "},{"location":"usage/chat_functionality/#advanced-chat-features","title":"Advanced Chat Features","text":""},{"location":"usage/chat_functionality/#streaming-chat-responses","title":"Streaming Chat Responses","text":"

      For real-time conversation, you can stream chat responses:

      conversation = [\n    {\"role\": \"user\", \"content\": \"Tell me a long story about space exploration\"}\n]\n\nfor chunk in client.chat(conversation, model=\"gpt-3.5-turbo\", stream=True):\n    print(chunk, end=\"\", flush=True)\n
      "},{"location":"usage/chat_functionality/#temperature-and-top-p-sampling","title":"Temperature and Top-p Sampling","text":"

      Adjust the creativity and randomness of responses:

      response = client.chat(\n    conversation,\n    model=\"gpt-3.5-turbo\",\n    temperature=0.7,\n    top_p=0.9\n)\n
      "},{"location":"usage/chat_functionality/#provider-specific-chat-capabilities","title":"Provider-Specific Chat Capabilities","text":"

      Different providers may offer unique chat features:

      "},{"location":"usage/chat_functionality/#openai","title":"OpenAI","text":"
      openai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\n\nresponse = openai_client.chat(\n    [{\"role\": \"user\", \"content\": \"Translate 'Hello, world!' to Japanese\"}],\n    model=\"gpt-4\"\n)\n
      "},{"location":"usage/chat_functionality/#replicate","title":"Replicate","text":"
      replicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\n\nresponse = replicate_client.chat(\n    [{\"role\": \"user\", \"content\": \"Explain quantum computing\"}],\n    model=\"meta/llama-2-70b-chat:latest\"\n)\n
      "},{"location":"usage/chat_functionality/#ollama","title":"Ollama","text":"
      ollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n\nresponse = ollama_client.chat(\n    [{\"role\": \"user\", \"content\": \"What are the three laws of robotics?\"}],\n    model=\"llama2\"\n)\n
      "},{"location":"usage/chat_functionality/#best-practices","title":"Best Practices","text":"
      1. Context Management: Keep track of the conversation history, but be mindful of token limits.
      max_context_length = 10\nif len(conversation) > max_context_length:\n    conversation = conversation[-max_context_length:]\n
      1. Error Handling: Implement robust error handling for chat interactions:
      try:\n    response = client.chat(conversation, model=\"gpt-3.5-turbo\")\nexcept Exception as e:\n    print(f\"An error occurred during chat: {e}\")\n    response = \"I'm sorry, I encountered an error. Could you please try again?\"\n
      1. User Input Validation: Validate and sanitize user inputs to prevent potential issues:
      def sanitize_input(user_input):\n    # Implement appropriate sanitization logic\n    return user_input.strip()\n\nuser_message = sanitize_input(input(\"Your message: \"))\nconversation.append({\"role\": \"user\", \"content\": user_message})\n
      1. Graceful Fallbacks: Implement fallback mechanisms for when the AI doesn't understand or can't provide a suitable response:
      if not response or response.lower() == \"i don't know\":\n    response = \"I'm not sure about that. Could you please rephrase or ask something else?\"\n
      1. Model Selection: Choose appropriate models based on the complexity of your chat application:
      model = \"gpt-4\" if complex_conversation else \"gpt-3.5-turbo\"\nresponse = client.chat(conversation, model=model)\n
      1. Conversation Resetting: Provide options to reset or start new conversations:
      def reset_conversation():\n    return [{\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}]\n\n# Usage\nconversation = reset_conversation()\n

      By following these guidelines and exploring the various features available, you can create sophisticated chat applications using ClientAI across different AI providers.

      "},{"location":"usage/error_handling/","title":"Error Handling in ClientAI","text":"

      ClientAI provides a robust error handling system that unifies exceptions across different AI providers. This guide covers how to handle potential errors when using ClientAI.

      "},{"location":"usage/error_handling/#table-of-contents","title":"Table of Contents","text":"
      1. Exception Hierarchy
      2. Handling Errors
      3. Provider-Specific Error Mapping
      4. Best Practices
      "},{"location":"usage/error_handling/#exception-hierarchy","title":"Exception Hierarchy","text":"

      ClientAI uses a custom exception hierarchy to provide consistent error handling across different AI providers:

      from clientai.exceptions import (\n    ClientAIError,\n    AuthenticationError,\n    RateLimitError,\n    InvalidRequestError,\n    ModelError,\n    TimeoutError,\n    APIError\n)\n
      "},{"location":"usage/error_handling/#handling-errors","title":"Handling Errors","text":"

      Here's how to handle potential errors when using ClientAI:

      from clientai import ClientAI\nfrom clientai.exceptions import (\n    ClientAIError,\n    AuthenticationError,\n    RateLimitError,\n    InvalidRequestError,\n    ModelError,\n    TimeoutError,\n    APIError\n)\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\ntry:\n    response = client.generate_text(\"Tell me a joke\", model=\"gpt-3.5-turbo\")\n    print(f\"Generated text: {response}\")\nexcept AuthenticationError as e:\n    print(f\"Authentication error: {e}\")\nexcept RateLimitError as e:\n    print(f\"Rate limit exceeded: {e}\")\nexcept InvalidRequestError as e:\n    print(f\"Invalid request: {e}\")\nexcept ModelError as e:\n    print(f\"Model error: {e}\")\nexcept TimeoutError as e:\n    print(f\"Request timed out: {e}\")\nexcept APIError as e:\n    print(f\"API error: {e}\")\nexcept ClientAIError as e:\n    print(f\"An unexpected ClientAI error occurred: {e}\")\n
      "},{"location":"usage/error_handling/#provider-specific-error-mapping","title":"Provider-Specific Error Mapping","text":"

      ClientAI maps provider-specific errors to its custom exception hierarchy. For example:

      "},{"location":"usage/error_handling/#openai","title":"OpenAI","text":"
      def _map_exception_to_clientai_error(self, e: Exception) -> None:\n    error_message = str(e)\n    status_code = getattr(e, 'status_code', None)\n\n    if isinstance(e, OpenAIAuthenticationError) or \"incorrect api key\" in error_message.lower():\n        raise AuthenticationError(error_message, status_code, original_error=e)\n    elif status_code == 429 or \"rate limit\" in error_message.lower():\n        raise RateLimitError(error_message, status_code, original_error=e)\n    elif status_code == 404 or \"not found\" in error_message.lower():\n        raise ModelError(error_message, status_code, original_error=e)\n    elif status_code == 400 or \"invalid\" in error_message.lower():\n        raise InvalidRequestError(error_message, status_code, original_error=e)\n    elif status_code == 408 or \"timeout\" in error_message.lower():\n        raise TimeoutError(error_message, status_code, original_error=e)\n    elif status_code and status_code >= 500:\n        raise APIError(error_message, status_code, original_error=e)\n\n    raise ClientAIError(error_message, status_code, original_error=e)\n
      "},{"location":"usage/error_handling/#replicate","title":"Replicate","text":"
      def _map_exception_to_clientai_error(self, e: Exception, status_code: int = None) -> ClientAIError:\n    error_message = str(e)\n    status_code = status_code or getattr(e, 'status_code', None)\n\n    if \"authentication\" in error_message.lower() or \"unauthorized\" in error_message.lower():\n        return AuthenticationError(error_message, status_code, original_error=e)\n    elif \"rate limit\" in error_message.lower():\n        return RateLimitError(error_message, status_code, original_error=e)\n    elif \"not found\" in error_message.lower():\n        return ModelError(error_message, status_code, original_error=e)\n    elif \"invalid\" in error_message.lower():\n        return InvalidRequestError(error_message, status_code, original_error=e)\n    elif \"timeout\" in error_message.lower() or status_code == 408:\n        return TimeoutError(error_message, status_code, original_error=e)\n    elif status_code == 400:\n        return InvalidRequestError(error_message, status_code, original_error=e)\n    else:\n        return APIError(error_message, status_code, original_error=e)\n
      "},{"location":"usage/error_handling/#best-practices","title":"Best Practices","text":"
      1. Specific Exception Handling: Catch specific exceptions when you need to handle them differently.

      2. Logging: Log errors for debugging and monitoring purposes.

      import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ntry:\n    response = client.generate_text(\"Tell me a joke\", model=\"gpt-3.5-turbo\")\nexcept ClientAIError as e:\n    logger.error(f\"An error occurred: {e}\", exc_info=True)\n
      1. Retry Logic: Implement retry logic for transient errors like rate limiting.
      import time\nfrom clientai.exceptions import RateLimitError\n\ndef retry_generate(prompt, model, max_retries=3, delay=1):\n    for attempt in range(max_retries):\n        try:\n            return client.generate_text(prompt, model=model)\n        except RateLimitError as e:\n            if attempt == max_retries - 1:\n                raise\n            wait_time = e.retry_after if hasattr(e, 'retry_after') else delay * (2 ** attempt)\n            logger.warning(f\"Rate limit reached. Waiting for {wait_time} seconds...\")\n            time.sleep(wait_time)\n
      1. Graceful Degradation: Implement fallback options when errors occur.
      def generate_with_fallback(prompt, primary_client, fallback_client):\n    try:\n        return primary_client.generate_text(prompt, model=\"gpt-3.5-turbo\")\n    except ClientAIError as e:\n        logger.warning(f\"Primary client failed: {e}. Falling back to secondary client.\")\n        return fallback_client.generate_text(prompt, model=\"llama-2-70b-chat\")\n

      By following these practices and utilizing ClientAI's unified error handling system, you can create more robust and maintainable applications that gracefully handle errors across different AI providers.

      "},{"location":"usage/initialization/","title":"Initializing ClientAI","text":"

      This guide covers the process of initializing ClientAI with different AI providers. You'll learn how to set up ClientAI for use with OpenAI, Replicate, and Ollama.

      "},{"location":"usage/initialization/#table-of-contents","title":"Table of Contents","text":"
      1. Prerequisites
      2. OpenAI Initialization
      3. Replicate Initialization
      4. Ollama Initialization
      5. Multiple Provider Initialization
      6. Best Practices
      "},{"location":"usage/initialization/#prerequisites","title":"Prerequisites","text":"

      Before initializing ClientAI, ensure you have:

      1. Installed ClientAI: pip install clientai[all]
      2. Obtained necessary API keys for the providers you plan to use
      3. Basic understanding of Python and asynchronous programming
      "},{"location":"usage/initialization/#openai-initialization","title":"OpenAI Initialization","text":"

      To initialize ClientAI with OpenAI:

      from clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\n

      Replace \"your-openai-api-key\" with your actual OpenAI API key.

      "},{"location":"usage/initialization/#replicate-initialization","title":"Replicate Initialization","text":"

      To initialize ClientAI with Replicate:

      from clientai import ClientAI\n\nreplicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\n

      Replace \"your-replicate-api-key\" with your actual Replicate API key.

      "},{"location":"usage/initialization/#ollama-initialization","title":"Ollama Initialization","text":"

      To initialize ClientAI with Ollama:

      from clientai import ClientAI\n\nollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n

      Ensure that you have Ollama running locally on the specified host.

      "},{"location":"usage/initialization/#multiple-provider-initialization","title":"Multiple Provider Initialization","text":"

      You can initialize multiple providers in the same script:

      from clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\nreplicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\nollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n
      "},{"location":"usage/initialization/#best-practices","title":"Best Practices","text":"
      1. Environment Variables: Store API keys in environment variables instead of hardcoding them in your script:
      import os\nfrom clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=os.getenv('OPENAI_API_KEY'))\n
      1. Error Handling: Wrap initialization in a try-except block to handle potential errors:
      try:\n    client = ClientAI('openai', api_key=\"your-openai-api-key\")\nexcept ValueError as e:\n    print(f\"Error initializing ClientAI: {e}\")\n
      1. Configuration Files: For projects with multiple providers, consider using a configuration file:
      import json\nfrom clientai import ClientAI\n\nwith open('config.json') as f:\n    config = json.load(f)\n\nopenai_client = ClientAI('openai', **config['openai'])\nreplicate_client = ClientAI('replicate', **config['replicate'])\n
      1. Lazy Initialization: If you're not sure which provider you'll use, initialize clients only when needed:
      def get_client(provider):\n    if provider == 'openai':\n        return ClientAI('openai', api_key=\"your-openai-api-key\")\n    elif provider == 'replicate':\n        return ClientAI('replicate', api_key=\"your-replicate-api-key\")\n    # ... other providers ...\n\n# Use the client when needed\nclient = get_client('openai')\n

      By following these initialization guidelines, you'll be well-prepared to start using ClientAI with various AI providers in your projects.

      "},{"location":"usage/multiple_providers/","title":"Working with Multiple Providers in ClientAI","text":"

      This guide explores techniques for effectively using multiple AI providers within a single project using ClientAI. You'll learn how to switch between providers and leverage their unique strengths.

      "},{"location":"usage/multiple_providers/#table-of-contents","title":"Table of Contents","text":"
      1. Setting Up Multiple Providers
      2. Switching Between Providers
      3. Leveraging Provider Strengths
      4. Load Balancing and Fallback Strategies
      5. Best Practices
      "},{"location":"usage/multiple_providers/#setting-up-multiple-providers","title":"Setting Up Multiple Providers","text":"

      First, initialize ClientAI with multiple providers:

      from clientai import ClientAI\n\nopenai_client = ClientAI('openai', api_key=\"your-openai-api-key\")\nreplicate_client = ClientAI('replicate', api_key=\"your-replicate-api-key\")\nollama_client = ClientAI('ollama', host=\"http://localhost:11434\")\n
      "},{"location":"usage/multiple_providers/#switching-between-providers","title":"Switching Between Providers","text":"

      Create a function to switch between providers based on your requirements:

      def get_provider(task):\n    if task == \"translation\":\n        return openai_client\n    elif task == \"code_generation\":\n        return replicate_client\n    elif task == \"local_inference\":\n        return ollama_client\n    else:\n        return openai_client  # Default to OpenAI\n\n# Usage\ntask = \"translation\"\nprovider = get_provider(task)\nresponse = provider.generate_text(\"Translate 'Hello' to French\", model=\"gpt-3.5-turbo\")\n

      This approach allows you to dynamically select the most appropriate provider for each task.

      "},{"location":"usage/multiple_providers/#leveraging-provider-strengths","title":"Leveraging Provider Strengths","text":"

      Different providers excel in different areas. Here's how you can leverage their strengths:

      def translate_text(text, target_language):\n    return openai_client.generate_text(\n        f\"Translate '{text}' to {target_language}\",\n        model=\"gpt-3.5-turbo\"\n    )\n\ndef generate_code(prompt):\n    return replicate_client.generate_text(\n        prompt,\n        model=\"meta/llama-2-70b-chat:latest\"\n    )\n\ndef local_inference(prompt):\n    return ollama_client.generate_text(\n        prompt,\n        model=\"llama2\"\n    )\n\n# Usage\nfrench_text = translate_text(\"Hello, world!\", \"French\")\npython_code = generate_code(\"Write a Python function to calculate the Fibonacci sequence\")\nquick_response = local_inference(\"What's the capital of France?\")\n
      "},{"location":"usage/multiple_providers/#load-balancing-and-fallback-strategies","title":"Load Balancing and Fallback Strategies","text":"

      Implement load balancing and fallback strategies to ensure reliability:

      import random\n\nproviders = [openai_client, replicate_client, ollama_client]\n\ndef load_balanced_generate(prompt, max_retries=3):\n    for _ in range(max_retries):\n        try:\n            provider = random.choice(providers)\n            return provider.generate_text(prompt, model=provider.default_model)\n        except Exception as e:\n            print(f\"Error with provider {provider.__class__.__name__}: {e}\")\n    raise Exception(\"All providers failed after max retries\")\n\n# Usage\ntry:\n    response = load_balanced_generate(\"Explain the concept of machine learning\")\n    print(response)\nexcept Exception as e:\n    print(f\"Failed to generate text: {e}\")\n

      This function randomly selects a provider and falls back to others if there's an error.

      "},{"location":"usage/multiple_providers/#best-practices","title":"Best Practices","text":"
      1. Provider Selection Logic: Develop clear criteria for selecting providers based on task requirements, cost, and performance.
      def select_provider(task, complexity, budget):\n    if complexity == \"high\" and budget == \"high\":\n        return openai_client  # Assuming OpenAI has more advanced models\n    elif task == \"code\" and budget == \"medium\":\n        return replicate_client\n    else:\n        return ollama_client  # Assuming Ollama is the most cost-effective\n
      1. Consistent Interface: Create wrapper functions to provide a consistent interface across providers:
      def unified_generate(prompt, provider=None):\n    if provider is None:\n        provider = get_default_provider()\n    return provider.generate_text(prompt, model=provider.default_model)\n\n# Usage\nresponse = unified_generate(\"Explain quantum computing\")\n
      1. Error Handling and Logging: Implement comprehensive error handling and logging when working with multiple providers:
      import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ndef safe_generate(prompt, provider):\n    try:\n        return provider.generate_text(prompt, model=provider.default_model)\n    except Exception as e:\n        logger.error(f\"Error with {provider.__class__.__name__}: {e}\")\n        return None\n
      1. Performance Monitoring: Track the performance of different providers to optimize selection:
      import time\n\ndef timed_generate(prompt, provider):\n    start_time = time.time()\n    result = provider.generate_text(prompt, model=provider.default_model)\n    elapsed_time = time.time() - start_time\n    logger.info(f\"{provider.__class__.__name__} took {elapsed_time:.2f} seconds\")\n    return result\n
      1. Configuration Management: Use configuration files or environment variables to manage provider settings:
      import os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nopenai_client = ClientAI('openai', api_key=os.getenv('OPENAI_API_KEY'))\nreplicate_client = ClientAI('replicate', api_key=os.getenv('REPLICATE_API_KEY'))\nollama_client = ClientAI('ollama', host=os.getenv('OLLAMA_HOST'))\n
      1. Caching: Implement caching to reduce redundant API calls and improve response times:
      from functools import lru_cache\n\n@lru_cache(maxsize=100)\ndef cached_generate(prompt, provider_name):\n    provider = get_provider(provider_name)\n    return provider.generate_text(prompt, model=provider.default_model)\n\n# Usage\nresponse = cached_generate(\"What is the speed of light?\", \"openai\")\n

      By following these practices and leveraging the strengths of multiple providers, you can create more robust, efficient, and versatile applications with ClientAI.

      "},{"location":"usage/ollama_manager/","title":"Ollama Manager Guide","text":""},{"location":"usage/ollama_manager/#introduction","title":"Introduction","text":"

      Ollama Manager provides a streamlined way to prototype and develop applications using Ollama's AI models. Instead of manually managing the Ollama server process, installing it as a service, or running it in a separate terminal, Ollama Manager handles the entire lifecycle programmatically.

      Key Benefits for Prototyping:

      "},{"location":"usage/ollama_manager/#quick-start","title":"Quick Start","text":"
      from clientai import ClientAI\nfrom clientai.ollama import OllamaManager\n\n# Basic usage - server starts automatically and stops when done\nwith OllamaManager() as manager:\n    # Create a client that connects to the managed server\n    client = ClientAI('ollama', host=\"http://localhost:11434\")\n\n    # Use the client normally\n    response = client.generate_text(\n        \"Explain quantum computing\",\n        model=\"llama2\"\n    )\n    print(response)\n# Server automatically stops when exiting the context\n
      "},{"location":"usage/ollama_manager/#installation","title":"Installation","text":"
      # Install with Ollama support\npip install \"clientai[ollama]\"\n\n# Install with all providers\npip install \"clientai[all]\"\n
      "},{"location":"usage/ollama_manager/#core-concepts","title":"Core Concepts","text":""},{"location":"usage/ollama_manager/#server-lifecycle-management","title":"Server Lifecycle Management","text":"
      1. Context Manager (Recommended)

        with OllamaManager() as manager:\n    # Server starts automatically\n    client = ClientAI('ollama')\n    # Use client...\n# Server stops automatically\n

      2. Manual Management

        manager = OllamaManager()\ntry:\n    manager.start()\n    client = ClientAI('ollama')\n    # Use client...\nfinally:\n    manager.stop()\n

      "},{"location":"usage/ollama_manager/#configuration-management","title":"Configuration Management","text":"
      from clientai.ollama import OllamaServerConfig\n\n# Create custom configuration\nconfig = OllamaServerConfig(\n    host=\"127.0.0.1\",\n    port=11434,\n    gpu_layers=35,\n    memory_limit=\"8GiB\"\n)\n\n# Use configuration with manager\nwith OllamaManager(config) as manager:\n    client = ClientAI('ollama')\n    # Use client...\n
      "},{"location":"usage/ollama_manager/#resource-configuration-guide","title":"Resource Configuration Guide","text":""},{"location":"usage/ollama_manager/#gpu-configuration","title":"GPU Configuration","text":"
      1. Basic GPU Setup

        config = OllamaServerConfig(\n    gpu_layers=35,  # Number of layers to run on GPU\n    gpu_memory_fraction=0.8  # 80% of GPU memory\n)\n

      2. Multi-GPU Setup

        config = OllamaServerConfig(\n    gpu_devices=[0, 1],  # Use first two GPUs\n    gpu_memory_fraction=0.7\n)\n

      "},{"location":"usage/ollama_manager/#memory-management","title":"Memory Management","text":"

      The memory_limit parameter requires specific formatting following Go's memory limit syntax:

      # Correct memory limit formats\nconfig = OllamaServerConfig(\n    memory_limit=\"8GiB\"    # 8 gibibytes\n    # OR\n    memory_limit=\"8192MiB\" # Same as 8GiB\n)\n\n# Invalid formats that will cause errors\nconfig = OllamaServerConfig(\n    memory_limit=\"8GiB\"     # Wrong unit\n    memory_limit=\"8 GiB\"   # No spaces allowed\n    memory_limit=\"8g\"      # Must specify full unit\n)\n

      Valid Memory Units: - B - Bytes - KiB - Kibibytes (1024 bytes) - MiB - Mebibytes (1024\u00b2 bytes) - GiB - Gibibytes (1024\u00b3 bytes) - TiB - Tebibytes (1024\u2074 bytes)

      Common Configurations:

      1. High-Performance Setup

        config = OllamaServerConfig(\n    memory_limit=\"24GiB\",\n    cpu_threads=16,\n    gpu_layers=40\n)\n

      2. Balanced Setup

        config = OllamaServerConfig(\n    memory_limit=\"16GiB\",\n    cpu_threads=8,\n    gpu_layers=32\n)\n

      3. Resource-Constrained Setup

        config = OllamaServerConfig(\n    memory_limit=\"8GiB\",\n    cpu_threads=4,\n    gpu_layers=24\n)\n

      4. Dynamic Memory Allocation

        import psutil\n\n# Calculate available memory and use 70%\navailable_gib = (psutil.virtual_memory().available / (1024**3))\nmemory_limit = f\"{int(available_gib * 0.7)}GiB\"\n\nconfig = OllamaServerConfig(\n    memory_limit=memory_limit,\n    cpu_threads=8\n)\n

      "},{"location":"usage/ollama_manager/#model-specific-memory-guidelines","title":"Model-Specific Memory Guidelines","text":"

      Different models require different amounts of memory:

      1. Large Language Models (>30B parameters)

        config = OllamaServerConfig(\n    memory_limit=\"24GiB\",\n    gpu_memory_fraction=0.9\n)\n

      2. Medium Models (7B-30B parameters)

        config = OllamaServerConfig(\n    memory_limit=\"16GiB\",\n    gpu_memory_fraction=0.8\n)\n

      3. Small Models (<7B parameters)

        config = OllamaServerConfig(\n    memory_limit=\"8GiB\",\n    gpu_memory_fraction=0.7\n)\n

      "},{"location":"usage/ollama_manager/#advanced-configuration-reference","title":"Advanced Configuration Reference","text":"
      config = OllamaServerConfig(\n    # Server settings\n    host=\"127.0.0.1\",      # Server bind address\n    port=11434,            # Server port number\n    timeout=30,            # Maximum startup wait time in seconds\n    check_interval=1.0,    # Health check interval in seconds\n\n    # GPU settings\n    gpu_layers=35,          # More layers = more GPU utilization\n    gpu_memory_fraction=0.8, # 0.0 to 1.0 (80% GPU memory)\n    gpu_devices=[0],        # Specific GPU devices to use\n\n    # CPU settings\n    cpu_threads=8,          # Number of CPU threads\n    memory_limit=\"16GiB\",   # Maximum RAM usage (must use GiB/MiB units)\n\n    # Compute settings\n    compute_unit=\"auto\",    # \"auto\", \"cpu\", or \"gpu\"\n\n    # Additional settings\n    env_vars={\"CUSTOM_VAR\": \"value\"},  # Additional environment variables\n    extra_args=[\"--verbose\"]           # Additional command line arguments\n)\n

      Each setting explained:

      Server Settings:

      GPU Settings:

      CPU Settings:

      Compute Settings:

      Additional Settings:

      "},{"location":"usage/ollama_manager/#common-use-cases","title":"Common Use Cases","text":""},{"location":"usage/ollama_manager/#1-development-and-prototyping","title":"1. Development and Prototyping","text":"
      # Quick setup for development\nwith OllamaManager() as manager:\n    client = ClientAI('ollama')\n\n    # Test different prompts\n    prompts = [\n        \"Write a poem about AI\",\n        \"Explain quantum physics\",\n        \"Create a Python function\"\n    ]\n\n    for prompt in prompts:\n        response = client.generate_text(prompt, model=\"llama2\")\n        print(f\"Prompt: {prompt}\\nResponse: {response}\\n\")\n
      "},{"location":"usage/ollama_manager/#2-multiple-model-testing","title":"2. Multiple Model Testing","text":"
      # Test different models with different configurations\nmodels = [\"llama2\", \"codellama\", \"mistral\"]\n\nfor model in models:\n    # Adjust configuration based on model\n    if model == \"llama2\":\n        config = OllamaServerConfig(gpu_layers=35)\n    else:\n        config = OllamaServerConfig(gpu_layers=28)\n\n    with OllamaManager(config) as manager:\n        client = ClientAI('ollama')\n        response = client.generate_text(\n            \"Explain how you work\",\n            model=model\n        )\n        print(f\"{model}: {response}\\n\")\n
      "},{"location":"usage/ollama_manager/#3-ab-testing-configurations","title":"3. A/B Testing Configurations","text":"
      def test_configuration(config, prompt, model):\n    start_time = time.time()\n\n    with OllamaManager(config) as manager:\n        client = ClientAI('ollama')\n        response = client.generate_text(prompt, model=model)\n\n    duration = time.time() - start_time\n    return response, duration\n\n# Test different configurations\nconfigs = {\n    \"high_gpu\": OllamaServerConfig(gpu_layers=40, gpu_memory_fraction=0.9),\n    \"balanced\": OllamaServerConfig(gpu_layers=32, gpu_memory_fraction=0.7),\n    \"low_resource\": OllamaServerConfig(gpu_layers=24, gpu_memory_fraction=0.5)\n}\n\nfor name, config in configs.items():\n    response, duration = test_configuration(\n        config,\n        \"Write a long story about space\",\n        \"llama2\"\n    )\n    print(f\"Configuration {name}: {duration:.2f} seconds\")\n
      "},{"location":"usage/ollama_manager/#4-production-setup","title":"4. Production Setup","text":"
      import logging\n\nlogging.basicConfig(level=logging.INFO)\n\ndef create_production_manager():\n    config = OllamaServerConfig(\n        # Stable production settings\n        gpu_layers=32,\n        gpu_memory_fraction=0.7,\n        memory_limit=\"16GiB\",\n        timeout=60,  # Longer timeout for stability\n        check_interval=2.0\n    )\n\n    try:\n        manager = OllamaManager(config)\n        manager.start()\n        return manager\n    except Exception as e:\n        logging.error(f\"Failed to start Ollama: {e}\")\n        raise\n\n# Use in production\ntry:\n    manager = create_production_manager()\n    client = ClientAI('ollama')\n    # Use client...\nfinally:\n    manager.stop()\n
      "},{"location":"usage/ollama_manager/#error-handling","title":"Error Handling","text":"

      Ollama Manager provides several specific exception types to help you handle different error scenarios effectively:

      "},{"location":"usage/ollama_manager/#executablenotfounderror","title":"ExecutableNotFoundError","text":"

      Occurs when the Ollama executable cannot be found on the system.

      Common causes: - Ollama not installed - Ollama not in system PATH - Incorrect installation

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept ExecutableNotFoundError:\n    # Guide user through installation\n    if platform.system() == \"Darwin\":\n        print(\"Install Ollama using: brew install ollama\")\n    elif platform.system() == \"Linux\":\n        print(\"Install Ollama using: curl -fsSL https://ollama.com/install.sh | sh\")\n    else:\n        print(\"Download Ollama from: https://ollama.com/download\")\n

      "},{"location":"usage/ollama_manager/#serverstartuperror","title":"ServerStartupError","text":"

      Occurs when the server fails to start properly.

      Common causes: - Port already in use - Insufficient permissions - Corrupt installation - Resource conflicts

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept ServerStartupError as e:\n    if \"address already in use\" in str(e).lower():\n        # Try alternative port\n        config = OllamaServerConfig(port=11435)\n        manager = OllamaManager(config)\n        manager.start()\n    elif \"permission denied\" in str(e).lower():\n        print(\"Please run with appropriate permissions\")\n    else:\n        print(f\"Server startup failed: {e}\")\n

      "},{"location":"usage/ollama_manager/#servertimeouterror","title":"ServerTimeoutError","text":"

      Occurs when the server doesn't respond within the configured timeout period.

      Common causes: - System under heavy load - Insufficient resources - Network issues - Too short timeout period

      How to handle:

      config = OllamaServerConfig(\n    timeout=60,  # Increase timeout\n    check_interval=2.0  # Reduce check frequency\n)\ntry:\n    manager = OllamaManager(config)\n    manager.start()\nexcept ServerTimeoutError:\n    # Either retry with longer timeout or fail gracefully\n    config.timeout = 120\n    try:\n        manager = OllamaManager(config)\n        manager.start()\n    except ServerTimeoutError:\n        print(\"Server unresponsive after extended timeout\")\n

      "},{"location":"usage/ollama_manager/#resourceerror","title":"ResourceError","text":"

      Occurs when there are insufficient system resources to run Ollama.

      Common causes: - Insufficient memory - GPU memory allocation failures - Too many CPU threads requested - Disk space constraints

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept ResourceError as e:\n    if \"memory\" in str(e).lower():\n        # Try with reduced memory settings\n        config = OllamaServerConfig(\n            memory_limit=\"4GiB\",\n            gpu_memory_fraction=0.5\n        )\n    elif \"gpu\" in str(e).lower():\n        # Fallback to CPU\n        config = OllamaServerConfig(compute_unit=\"cpu\")\n\n    try:\n        manager = OllamaManager(config)\n        manager.start()\n    except ResourceError:\n        print(\"Unable to allocate required resources\")\n

      "},{"location":"usage/ollama_manager/#configurationerror","title":"ConfigurationError","text":"

      Occurs when provided configuration values are invalid.

      Common causes: - Invalid memory format - Invalid GPU configuration - Incompatible settings - Out-of-range values

      How to handle:

      try:\n    config = OllamaServerConfig(\n        gpu_memory_fraction=1.5  # Invalid value\n    )\nexcept ConfigurationError as e:\n    # Use safe default values\n    config = OllamaServerConfig(\n        gpu_memory_fraction=0.7,\n        gpu_layers=32\n    )\n

      "},{"location":"usage/ollama_manager/#unsupportedplatformerror","title":"UnsupportedPlatformError","text":"

      Occurs when running on an unsupported platform or configuration.

      Common causes: - Unsupported operating system - Missing system features - Incompatible hardware

      How to handle:

      try:\n    manager = OllamaManager()\n    manager.start()\nexcept UnsupportedPlatformError as e:\n    # Fall back to alternative configuration or inform user\n    print(f\"Platform not supported: {e}\")\n    print(\"Supported platforms: Windows, macOS, Linux\")\n

      "},{"location":"usage/ollama_manager/#memory-related-error-handling","title":"Memory-Related Error Handling","text":"
      def start_with_memory_fallback():\n    try:\n        # Try optimal memory configuration\n        config = OllamaServerConfig(memory_limit=\"16GiB\")\n        return OllamaManager(config)\n    except ServerStartupError as e:\n        if \"GOMEMLIMIT\" in str(e):\n            # Fall back to lower memory configuration\n            config = OllamaServerConfig(memory_limit=\"8GiB\")\n            return OllamaManager(config)\n        raise  # Re-raise if error is not memory-related\n
      "},{"location":"usage/ollama_manager/#best-practices-for-error-handling","title":"Best Practices for Error Handling","text":"
      1. Graceful Degradation

        def start_with_fallback():\n    # Try optimal configuration first\n    config = OllamaServerConfig(\n        gpu_layers=35,\n        gpu_memory_fraction=0.8\n    )\n\n    try:\n        return OllamaManager(config)\n    except ResourceError:\n        # Fall back to minimal configuration\n        config = OllamaServerConfig(\n            gpu_layers=24,\n            gpu_memory_fraction=0.5\n        )\n        return OllamaManager(config)\n

      2. Logging and Monitoring

        import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ntry:\n    manager = OllamaManager()\n    manager.start()\nexcept ServerStartupError as e:\n    logger.error(f\"Server startup failed: {e}\")\n    logger.debug(f\"Full error details: {e.original_exception}\")\n

      3. Recovery Strategies

        def start_with_retry(max_attempts=3, delay=5):\n    for attempt in range(max_attempts):\n        try:\n            manager = OllamaManager()\n            manager.start()\n            return manager\n        except (ServerStartupError, ServerTimeoutError) as e:\n            if attempt == max_attempts - 1:\n                raise\n            logger.warning(f\"Attempt {attempt + 1} failed, retrying...\")\n            time.sleep(delay)\n

      Remember to always clean up resources properly, even when handling errors:

      manager = None\ntry:\n    manager = OllamaManager()\n    manager.start()\n    # Use manager...\nexcept Exception as e:\n    logger.error(f\"Error occurred: {e}\")\nfinally:\n    if manager is not None:\n        manager.stop()\n

      "},{"location":"usage/ollama_manager/#performance-monitoring","title":"Performance Monitoring","text":"
      import psutil\nfrom contextlib import contextmanager\nimport time\n\n@contextmanager\ndef monitor_performance():\n    start_time = time.time()\n    start_cpu = psutil.cpu_percent()\n    start_mem = psutil.virtual_memory().percent\n\n    yield\n\n    duration = time.time() - start_time\n    cpu_diff = psutil.cpu_percent() - start_cpu\n    mem_diff = psutil.virtual_memory().percent - start_mem\n\n    print(f\"Duration: {duration:.2f}s\")\n    print(f\"CPU impact: {cpu_diff:+.1f}%\")\n    print(f\"Memory impact: {mem_diff:+.1f}%\")\n\n# Use with Ollama Manager\nwith monitor_performance():\n    with OllamaManager() as manager:\n        client = ClientAI('ollama')\n        response = client.generate_text(\n            \"Write a story\",\n            model=\"llama2\"\n        )\n
      "},{"location":"usage/ollama_manager/#best-practices","title":"Best Practices","text":"
      1. Always use context managers when possible
      2. Start with conservative resource settings and adjust up
      3. Monitor system resources during development
      4. Implement proper error handling
      5. Use appropriate configurations for development vs. production
      6. Clean up resources properly in all code paths
      7. Log important events for troubleshooting
      8. Test different configurations to find optimal settings
      9. Consider platform-specific settings for cross-platform applications
      10. Implement graceful degradation when resources are constrained
      "},{"location":"usage/ollama_manager/#troubleshooting","title":"Troubleshooting","text":"

      Remember that Ollama Manager is a powerful tool for prototyping and development, but always monitor system resources and adjust configurations based on your specific needs and hardware capabilities.

      "},{"location":"usage/overview/","title":"Usage Overview","text":"

      This Usage section provides comprehensive guides on how to effectively use the key features of ClientAI. Each topic focuses on a specific aspect of usage, ensuring you have all the information needed to leverage the full potential of ClientAI in your projects.

      "},{"location":"usage/overview/#key-topics","title":"Key Topics","text":""},{"location":"usage/overview/#1-initializing-clientai","title":"1. Initializing ClientAI","text":"

      This guide covers the process of initializing ClientAI with different AI providers. It provides a step-by-step approach to setting up ClientAI for use with OpenAI, Replicate, and Ollama.

      "},{"location":"usage/overview/#2-text-generation-with-clientai","title":"2. Text Generation with ClientAI","text":"

      Learn how to use ClientAI for text generation tasks. This guide explores the various options and parameters available for generating text across different AI providers.

      "},{"location":"usage/overview/#3-chat-functionality-in-clientai","title":"3. Chat Functionality in ClientAI","text":"

      Discover how to leverage ClientAI's chat functionality. This guide covers creating chat conversations, managing context, and handling chat-specific features across supported providers.

      "},{"location":"usage/overview/#4-working-with-multiple-providers","title":"4. Working with Multiple Providers","text":"

      Explore techniques for effectively using multiple AI providers within a single project. This guide demonstrates how to switch between providers and leverage their unique strengths.

      "},{"location":"usage/overview/#5-using-ollama-manager","title":"5. Using Ollama Manager","text":"

      Learn how to use Ollama Manager for streamlined prototyping and development. This guide covers server lifecycle management, resource configuration, and best practices for different use cases.

      "},{"location":"usage/overview/#6-handling-responses-and-errors","title":"6. Handling Responses and Errors","text":"

      Learn best practices for handling responses from AI providers and managing potential errors. This guide covers response parsing, error handling, and retry strategies.

      "},{"location":"usage/overview/#getting-started","title":"Getting Started","text":"

      To make the most of these guides, we recommend familiarizing yourself with basic Python programming and asynchronous programming concepts, as ClientAI leverages these extensively.

      "},{"location":"usage/overview/#quick-start-example","title":"Quick Start Example","text":"

      Here's a simple example to get you started with ClientAI:

      from clientai import ClientAI\n\n# Initialize the client\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\n# Generate text\nresponse = client.generate_text(\n    \"Explain the concept of machine learning in simple terms.\",\n    model=\"gpt-3.5-turbo\"\n)\n\nprint(response)\n

      For more detailed examples and explanations, refer to the specific guides linked above.

      "},{"location":"usage/overview/#advanced-usage","title":"Advanced Usage","text":""},{"location":"usage/overview/#streaming-responses","title":"Streaming Responses","text":"

      ClientAI supports streaming responses for compatible providers. Here's a basic example:

      for chunk in client.generate_text(\n    \"Tell me a long story about space exploration\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n
      "},{"location":"usage/overview/#using-different-models","title":"Using Different Models","text":"

      ClientAI allows you to specify different models for each provider. For example:

      # Using GPT-4 with OpenAI\nopenai_response = openai_client.generate_text(\n    \"Explain quantum computing\",\n    model=\"gpt-4\"\n)\n\n# Using Llama 2 with Replicate\nreplicate_response = replicate_client.generate_text(\n    \"Describe the process of photosynthesis\",\n    model=\"meta/llama-2-70b-chat:latest\"\n)\n
      "},{"location":"usage/overview/#best-practices","title":"Best Practices","text":"
      1. API Key Management: Always store your API keys securely, preferably as environment variables.
      2. Error Handling: Implement proper error handling to manage potential API failures or rate limiting issues.
      3. Model Selection: Choose appropriate models based on your task requirements and budget considerations.
      4. Context Management: For chat applications, manage conversation context efficiently to get the best results.
      "},{"location":"usage/overview/#contribution","title":"Contribution","text":"

      If you have suggestions or contributions to these guides, please refer to our Contributing Guidelines. We appreciate your input in improving our documentation and making ClientAI more accessible to all users.

      "},{"location":"usage/text_generation/","title":"Text Generation with ClientAI","text":"

      This guide explores how to use ClientAI for text generation tasks across different AI providers. You'll learn about the various options and parameters available for generating text.

      "},{"location":"usage/text_generation/#table-of-contents","title":"Table of Contents","text":"
      1. Basic Text Generation
      2. Advanced Parameters
      3. Streaming Responses
      4. Provider-Specific Features
      5. Best Practices
      "},{"location":"usage/text_generation/#basic-text-generation","title":"Basic Text Generation","text":"

      To generate text using ClientAI, use the generate_text method:

      from clientai import ClientAI\n\nclient = ClientAI('openai', api_key=\"your-openai-api-key\")\n\nresponse = client.generate_text(\n    \"Write a short story about a robot learning to paint.\",\n    model=\"gpt-3.5-turbo\"\n)\n\nprint(response)\n

      This will generate a short story based on the given prompt.

      "},{"location":"usage/text_generation/#advanced-parameters","title":"Advanced Parameters","text":"

      ClientAI supports various parameters to fine-tune text generation:

      response = client.generate_text(\n    \"Explain the theory of relativity\",\n    model=\"gpt-4\",\n    max_tokens=150,\n    temperature=0.7,\n    top_p=0.9,\n    presence_penalty=0.1,\n    frequency_penalty=0.1\n)\n

      Note: Available parameters may vary depending on the provider.

      "},{"location":"usage/text_generation/#streaming-responses","title":"Streaming Responses","text":"

      For long-form content, you can use streaming to get partial responses as they're generated:

      for chunk in client.generate_text(\n    \"Write a comprehensive essay on climate change\",\n    model=\"gpt-3.5-turbo\",\n    stream=True\n):\n    print(chunk, end=\"\", flush=True)\n

      This allows for real-time display of generated text, which can be useful for user interfaces or long-running generations.

      "},{"location":"usage/text_generation/#provider-specific-features","title":"Provider-Specific Features","text":"

      Different providers may offer unique features. Here are some examples:

      "},{"location":"usage/text_generation/#openai","title":"OpenAI","text":"
      response = openai_client.generate_text(\n    \"Translate the following to French: 'Hello, how are you?'\",\n    model=\"gpt-3.5-turbo\"\n)\n
      "},{"location":"usage/text_generation/#replicate","title":"Replicate","text":"
      response = replicate_client.generate_text(\n    \"Generate a haiku about mountains\",\n    model=\"meta/llama-2-70b-chat:latest\"\n)\n
      "},{"location":"usage/text_generation/#ollama","title":"Ollama","text":"
      response = ollama_client.generate_text(\n    \"Explain the concept of neural networks\",\n    model=\"llama2\"\n)\n
      "},{"location":"usage/text_generation/#best-practices","title":"Best Practices","text":"
      1. Prompt Engineering: Craft clear and specific prompts for better results.
      good_prompt = \"Write a detailed description of a futuristic city, focusing on transportation and architecture.\"\n
      1. Model Selection: Choose appropriate models based on your task complexity and requirements.

      2. Error Handling: Always handle potential errors in text generation:

      try:\n    response = client.generate_text(\"Your prompt here\", model=\"gpt-3.5-turbo\")\nexcept Exception as e:\n    print(f\"An error occurred: {e}\")\n
      1. Rate Limiting: Be mindful of rate limits imposed by providers. Implement appropriate delays or queuing mechanisms for high-volume applications.

      2. Content Filtering: Implement content filtering or moderation for user-facing applications to ensure appropriate outputs.

      3. Consistency: For applications requiring consistent outputs, consider using lower temperature values or implementing your own post-processing.

      By following these guidelines and exploring the various parameters and features available, you can effectively leverage ClientAI for a wide range of text generation tasks across different AI providers.

      "}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 84101f8080a05684a51b02417231d45f5e8b4b5d..c4c6567e1a4b3c61687ca729cea18140b2b86e4f 100644 GIT binary patch delta 15 Wcmeyv{D+xMzMF$%Tk}S?Z;SvgRt2U2 delta 15 Wcmeyv{D+xMzMF%?)NLc%H%0&|9|Y(C diff --git a/usage/ollama_manager/index.html b/usage/ollama_manager/index.html index d983dc9..bc7468f 100644 --- a/usage/ollama_manager/index.html +++ b/usage/ollama_manager/index.html @@ -893,6 +893,30 @@ + + + + + + + +
    1. + + + Common Use Cases + + + + + +
    2. + +
    3. + + + Common Use Cases + + + +