Showing preview only (1,093K chars total). Download the full file or copy to clipboard to get everything.
Repository: andrewyng/aisuite
Branch: main
Commit: 695242a836a0
Files: 208
Total size: 1.0 MB
Directory structure:
gitextract_z7uqp5wm/
├── .github/
│ └── workflows/
│ ├── black.yml
│ └── run_pytest.yml
├── .gitignore
├── .pre-commit-config.yaml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── aisuite/
│ ├── __init__.py
│ ├── client.py
│ ├── design-notes/
│ │ └── asr-parameter-design-motivation.md
│ ├── framework/
│ │ ├── __init__.py
│ │ ├── asr_params.py
│ │ ├── chat_completion_response.py
│ │ ├── choice.py
│ │ ├── message.py
│ │ ├── parameter_mapper.py
│ │ └── provider_interface.py
│ ├── mcp/
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── config.py
│ │ ├── schema_converter.py
│ │ └── tool_wrapper.py
│ ├── provider.py
│ ├── providers/
│ │ ├── __init__.py
│ │ ├── anthropic_provider.py
│ │ ├── aws_provider.py
│ │ ├── azure_provider.py
│ │ ├── cerebras_provider.py
│ │ ├── cohere_provider.py
│ │ ├── deepgram_provider.py
│ │ ├── deepseek_provider.py
│ │ ├── fireworks_provider.py
│ │ ├── google_provider.py
│ │ ├── groq_provider.py
│ │ ├── huggingface_provider.py
│ │ ├── inception_provider.py
│ │ ├── lmstudio_provider.py
│ │ ├── message_converter.py
│ │ ├── mistral_provider.py
│ │ ├── nebius_provider.py
│ │ ├── ollama_provider.py
│ │ ├── openai_provider.py
│ │ ├── sambanova_provider.py
│ │ ├── together_provider.py
│ │ ├── watsonx_provider.py
│ │ └── xai_provider.py
│ └── utils/
│ ├── tools.py
│ └── utils.py
├── aisuite-js/
│ ├── README.md
│ ├── examples/
│ │ ├── basic-usage.ts
│ │ ├── chat-app/
│ │ │ ├── .eslintrc.cjs
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── index.html
│ │ │ ├── package.json
│ │ │ ├── postcss.config.js
│ │ │ ├── src/
│ │ │ │ ├── App.tsx
│ │ │ │ ├── components/
│ │ │ │ │ ├── ApiKeyModal.tsx
│ │ │ │ │ ├── ChatContainer.tsx
│ │ │ │ │ ├── ChatInput.tsx
│ │ │ │ │ ├── ChatMessage.tsx
│ │ │ │ │ ├── ModelSelector.tsx
│ │ │ │ │ └── ProviderSelector.tsx
│ │ │ │ ├── config/
│ │ │ │ │ └── llm-config.ts
│ │ │ │ ├── index.css
│ │ │ │ ├── main.tsx
│ │ │ │ ├── services/
│ │ │ │ │ └── aisuite-service.ts
│ │ │ │ ├── types/
│ │ │ │ │ └── chat.ts
│ │ │ │ └── utils/
│ │ │ │ └── cn.ts
│ │ │ ├── tailwind.config.js
│ │ │ ├── tsconfig.json
│ │ │ ├── tsconfig.node.json
│ │ │ └── vite.config.ts
│ │ ├── deepgram.ts
│ │ ├── groq.ts
│ │ ├── mistral.ts
│ │ ├── openai-asr.ts
│ │ ├── streaming.ts
│ │ ├── test-suite.ts
│ │ └── tool-calling.ts
│ ├── jest.config.ts
│ ├── package.json
│ ├── src/
│ │ ├── asr-providers/
│ │ │ ├── deepgram/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ └── index.ts
│ │ ├── client.ts
│ │ ├── core/
│ │ │ ├── base-asr-provider.ts
│ │ │ ├── base-provider.ts
│ │ │ ├── errors.ts
│ │ │ └── model-parser.ts
│ │ ├── index.ts
│ │ ├── providers/
│ │ │ ├── anthropic/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ ├── groq/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ ├── index.ts
│ │ │ ├── mistral/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ └── openai/
│ │ │ ├── adapters.ts
│ │ │ ├── index.ts
│ │ │ ├── provider.ts
│ │ │ └── types.ts
│ │ ├── types/
│ │ │ ├── chat.ts
│ │ │ ├── common.ts
│ │ │ ├── index.ts
│ │ │ ├── providers.ts
│ │ │ ├── tools.ts
│ │ │ └── transcription.ts
│ │ └── utils/
│ │ └── streaming.ts
│ ├── tests/
│ │ ├── client.test.ts
│ │ ├── providers/
│ │ │ ├── anthropic-provider.test.ts
│ │ │ ├── deepgram-provider.test.ts
│ │ │ ├── groq-provider.test.ts
│ │ │ ├── mistral-provider.test.ts
│ │ │ ├── openai-provider.test.ts
│ │ │ └── openai_asr_provider.test.ts
│ │ └── utils/
│ │ └── streaming.test.ts
│ └── tsconfig.json
├── examples/
│ ├── AISuiteDemo.ipynb
│ ├── DeepseekPost.ipynb
│ ├── QnA_with_pdf.ipynb
│ ├── agents/
│ │ ├── movie_buff_assistant.ipynb
│ │ ├── recipe_chef_assistant.ipynb
│ │ ├── snake_game_generator.ipynb
│ │ ├── stock_dashboard.html
│ │ ├── stock_market_dashboard.html
│ │ ├── stock_market_mini_tracker.ipynb
│ │ ├── stock_market_tracker.ipynb
│ │ └── world_weather_dashboard.ipynb
│ ├── aisuite_tool_abstraction.ipynb
│ ├── asr_example.ipynb
│ ├── chat-ui/
│ │ ├── .streamlit/
│ │ │ └── config.toml
│ │ ├── README.md
│ │ ├── chat.py
│ │ └── config.yaml
│ ├── client.ipynb
│ ├── llm_reasoning.ipynb
│ ├── mcp_config_dict_example.py
│ ├── mcp_http_example.py
│ ├── mcp_tools_example.ipynb
│ ├── simple_tool_calling.ipynb
│ └── tool_calling_abstraction.ipynb
├── guides/
│ ├── README.md
│ ├── anthropic.md
│ ├── aws.md
│ ├── azure.md
│ ├── cerebras.md
│ ├── cohere.md
│ ├── deepseek.md
│ ├── google.md
│ ├── groq.md
│ ├── huggingface.md
│ ├── lmstudio.md
│ ├── mistral.md
│ ├── nebius.md
│ ├── ollama.md
│ ├── openai.md
│ ├── sambanova.md
│ ├── watsonx.md
│ └── xai.md
├── pyproject.toml
└── tests/
├── __init__.py
├── client/
│ ├── __init__.py
│ ├── test_client.py
│ └── test_prerelease.py
├── framework/
│ ├── test_asr_models.py
│ └── test_asr_params.py
├── mcp/
│ ├── README.md
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_client.py
│ ├── test_e2e.py
│ ├── test_http_llm_e2e.py
│ ├── test_http_transport.py
│ └── test_llm_e2e.py
├── providers/
│ ├── __init__.py
│ ├── test_anthropic_converter.py
│ ├── test_asr_parameter_passthrough.py
│ ├── test_aws_converter.py
│ ├── test_azure_provider.py
│ ├── test_cerebras_provider.py
│ ├── test_cohere_provider.py
│ ├── test_deepgram_provider.py
│ ├── test_deepseek_provider.py
│ ├── test_google_converter.py
│ ├── test_google_provider.py
│ ├── test_groq_provider.py
│ ├── test_huggingface_provider.py
│ ├── test_inception_provider.py
│ ├── test_lmstudio_provider.py
│ ├── test_mistral_provider.py
│ ├── test_nebius_provider.py
│ ├── test_ollama_provider.py
│ ├── test_openai_provider.py
│ ├── test_sambanova_provider.py
│ └── test_watsonx_provider.py
├── test_provider.py
└── utils/
├── test_mcp_memory_integration.py
├── test_tool_manager.py
└── test_tools_mcp_schema.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/black.yml
================================================
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable
================================================
FILE: .github/workflows/run_pytest.yml
================================================
name: Lint
on: [push, pull_request]
jobs:
build_and_test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.10", "3.11", "3.12" ]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install --all-extras --with test
- name: Test with pytest
run: poetry run pytest -m "not integration"
================================================
FILE: .gitignore
================================================
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
.env
.venv
env/
venv/
ENV/
*.whl
# Node/TypeScript
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.npm
.env.local
.env.*.local
dist/
coverage/
*.tsbuildinfo
# IDEs and editors
.idea/
.vscode/
*.swp
*.swo
.DS_Store
**/.DS_Store
*.sublime-workspace
*.sublime-project
# Jupyter Notebook
.ipynb_checkpoints
*/.ipynb_checkpoints/*
# Testing
.coverage
htmlcov/
.pytest_cache/
coverage/
.nyc_output/
# Cloud credentials
.google-adc
# Logs
logs
*.log
# Python version
.python-version
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
hooks:
- id: black
# It is recommended to specify the latest version of Python
# supported by your project here, or alternatively use
# pre-commit's default_language_version, see
# https://pre-commit.com/#top_level-default_language_version
language_version: python3.12
================================================
FILE: CONTRIBUTING.md
================================================
<!-- omit in toc -->
# Contributing to aisuite
First off, thanks for taking the time to contribute!
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents)
for different ways to help and details about how this project handles them. Please make sure to read
the relevant section before making your contribution. It will make it a lot easier for us maintainers
and smooth out the experience for all involved. The community looks forward to your contributions.
> And if you like the project, but just don't have time to contribute, that's fine. There are other easy
> ways to support the project and show your appreciation, which we would also be very happy about:
> - Star the project
> - Tweet about it
> - Refer this project in your project's readme
> - Mention the project at local meetups and tell your friends/colleagues
<!-- omit in toc -->
## Table of Contents
- [I Have a Question](#i-have-a-question)
- [I Want To Contribute](#i-want-to-contribute)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Enhancements](#suggesting-enhancements)
- [Your First Code Contribution](#your-first-code-contribution)
- [Improving The Documentation](#improving-the-documentation)
- [Styleguides](#styleguides)
- [Commit Messages](#commit-messages)
## I Have a Question
> If you want to ask a question, we assume that you have read the available
> [Documentation](https://github.com/andrewyng/aisuite/blob/main/README.md).
Before you ask a question, it is best to search for existing [Issues](https://github.com/andrewyng/aisuite/issues)
that might help you. If you find a relevant issue that already exists and still need clarification, please add your question to that existing issue. We also recommend reaching out to the community in the aisuite [Discord](https://discord.gg/T6Nvn8ExSb) server.
If you then still feel the need to ask a question and need clarification, we recommend the following:
- Open an [Issue](https://github.com/andrewyng/aisuite/issues/new).
- Provide as much context as you can about what you're running into.
- Provide project and platform versions (python, OS, etc.), depending on what seems relevant.
We (or someone in the community) will then take care of the issue as soon as possible.
## I Want To Contribute
> ### Legal Notice <!-- omit in toc -->
> When contributing to this project, you must agree that you have authored 100% of the content, that
> you have the necessary rights to the content and that the content you contribute may be provided
> under the project license.
### Reporting Bugs
<!-- omit in toc -->
#### Before Submitting a Bug Report
A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask
you to investigate carefully, collect information and describe the issue in detail in your report. Please
complete the following steps in advance to help us fix any potential bug as fast as possible.
- Make sure that you are using the latest version.
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment
components/versions (Make sure that you have read the [documentation](https://github.com/andrewyng/aisuite/blob/main/README.md).
If you are looking for support, you might want to check [this section](#i-have-a-question)).
- To see if other users have experienced (and potentially already solved) the same issue you are having,
check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/andrewyng/aisuite?q=label%3Abug).
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub
community have discussed the issue.
- Collect information about the bug:
- Stack trace (Traceback)
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on
what seems relevant.
- Possibly your input and the output
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
<!-- omit in toc -->
#### How Do I Submit a Good Bug Report?
> You must never report security related issues, vulnerabilities or bugs including sensitive information to
> the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <joaquin.dominguez@proton.me>.
<!-- You may add a PGP key to allow the messages to be sent encrypted as well. -->
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
- Open an [Issue](https://github.com/andrewyng/aisuite/issues/new). (Since we can't be sure at
this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
- Explain the behavior you would expect and the actual behavior.
- Please provide as much context as possible and describe the *reproduction steps* that someone else can
follow to recreate the issue on their own. This usually includes your code. For good bug reports you
should isolate the problem and create a reduced test case.
- Provide the information you collected in the previous section.
Once it's filed:
- The project team will label the issue accordingly.
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction
steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the
issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other
tags (such as `critical`), and the issue will be left to be
[implemented by someone](#your-first-code-contribution).
Please use the issue templates provided.
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for aisuite,
**including completely new features and minor improvements to existing functionality**. Following these
guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
<!-- omit in toc -->
#### Before Submitting an Enhancement
- Make sure that you are using the latest version.
- Read the [documentation](https://github.com/andrewyng/aisuite/blob/main/README.md) carefully
and find out if the functionality is already covered, maybe by an individual configuration.
- Perform a [search](https://github.com/andrewyng/aisuite/issues) to see if the enhancement has
already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong
case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
<!-- omit in toc -->
#### How Do I Submit a Good Enhancement Suggestion?
Enhancement suggestions are tracked as [GitHub issues](https://github.com/andrewyng/aisuite/issues).
- Use a **clear and descriptive title** for the issue to identify the suggestion.
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
At this point you can also tell which alternatives do not work for you.
- **Explain why this enhancement would be useful** to most aisuite users. You may also want to
point out the other projects that solved it better and which could serve as inspiration.
### Your First Code Contribution
#### Pre-requisites
You should first [fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo)
the `aisuite` repository and then clone your forked repository:
```bash
git clone https://github.com/<YOUR_GITHUB_USER>/aisuite.git
```
Once in the cloned repository directory, make a branch on the forked repository with your username and
description of PR:
```bash
git checkout -B <username>/<description>
```
Please install the development and test dependencies:
```bash
poetry install --with dev,test
```
`aisuite` uses pre-commit to ensure the formatting is consistent:
```bash
pre-commit install
```
**Make suggested changes**
Afterwards, our suite of formatting tests will run automatically before each `git commit`. You can also
run these manually:
```bash
pre-commit run --all-files
```
If a formatting test fails, it will fix the modified code in place and abort the `git commit`. After looking
over the changes, you can `git add <modified files>` and then repeat the previous git commit command.
**Note**: a github workflow will check the files with the same formatter and reject the PR if it doesn't
pass, so please make sure it passes locally.
#### Testing
`aisuite` tracks unit tests. Pytest is used to execute said unit tests in `tests/`:
```bash
poetry run pytest tests
```
If your code changes implement a new function, please make a corresponding unit test to the `test/*` files.
#### Contributing Workflow
We actively welcome your pull requests.
1. Create your new branch from main in your forked repo, with your username and a name describing the work
you're completing e.g. user-123/add-feature-x.
2. If you've added code that should be tested, add tests. Ensure all tests pass. See the testing section
for more information.
3. If you've changed APIs, update the documentation.
4. Make sure your code lints.
### Improving The Documentation
We welcome valuable contributions in the form of new documentation or revised documentation that provide
further clarity or accuracy. Each function should be clearly documented. Well-documented code is easier
to review and understand/extend.
## Styleguides
For code documentation, please follow the [Google styleguide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings).
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 Andrew Ng
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.
================================================
FILE: README.md
================================================
# aisuite
[](https://pypi.org/project/aisuite/)
[](https://github.com/psf/black)
`aisuite` is a lightweight Python library that provides a **unified API for working with multiple Generative AI providers**.
It offers a consistent interface for models from *OpenAI, Anthropic, Google, Hugging Face, AWS, Cohere, Mistral, Ollama*, and others—abstracting away SDK differences, authentication details, and parameter variations.
Its design is modeled after OpenAI’s API style, making it instantly familiar and easy to adopt.
`aisuite` lets developers build and **run LLM-based or agentic applications across providers** with minimal setup.
While it’s not a full-blown agents framework, it includes simple abstractions for creating standalone, lightweight agents.
It’s designed for low learning curve — so you can focus on building AI systems, not integrating APIs.
---
## Key Features
`aisuite` is designed to eliminate the complexity of working with multiple LLM providers while keeping your code simple and portable. Whether you're building a chatbot, an agentic application, or experimenting with different models, `aisuite` provides the abstractions you need without getting in your way.
* **Unified API for multiple model providers** – Write your code once and run it with any supported provider. Switch between OpenAI, Anthropic, Google, and others with a single parameter change.
* **Easy agentic app or agent creation** – Build multi-turn agentic applications using a single parameter `max_turns`. No need to manually manage tool execution loops.
* **Pass Tool calls easily** – Pass real Python functions instead of JSON specs; aisuite handles schema generation and execution automatically.
* **MCP tools** – Connect to MCP-based tools without writing boilerplate; aisuite handles connection, schema and execution seamlessly.
* **Modular and extensible provider architecture** – Add support for new providers with minimal code. The plugin-style architecture makes extensions straightforward.
---
## Installation
You can install just the base `aisuite` package, or install a provider's package along with `aisuite`.
Install just the base package without any provider SDKs:
```shell
pip install aisuite
```
Install aisuite with a specific provider (e.g., Anthropic):
```shell
pip install 'aisuite[anthropic]'
```
Install aisuite with all provider libraries:
```shell
pip install 'aisuite[all]'
```
## Setup
To get started, you will need API Keys for the providers you intend to use. You'll need to
install the provider-specific library either separately or when installing aisuite.
The API Keys can be set as environment variables, or can be passed as config to the aisuite Client constructor.
You can use tools like [`python-dotenv`](https://pypi.org/project/python-dotenv/) or [`direnv`](https://direnv.net/) to set the environment variables manually. Please take a look at the `examples` folder to see usage.
Here is a short example of using `aisuite` to generate chat completion responses from gpt-4o and claude-3-5-sonnet.
Set the API keys.
```shell
export OPENAI_API_KEY="your-openai-api-key"
export ANTHROPIC_API_KEY="your-anthropic-api-key"
```
Use the python client.
```python
import aisuite as ai
client = ai.Client()
models = ["openai:gpt-4o", "anthropic:claude-3-5-sonnet-20240620"]
messages = [
{"role": "system", "content": "Respond in Pirate English."},
{"role": "user", "content": "Tell me a joke."},
]
for model in models:
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=0.75
)
print(response.choices[0].message.content)
```
Note that the model name in the create() call uses the format - `<provider>:<model-name>`.
`aisuite` will call the appropriate provider with the right parameters based on the provider value.
For a list of provider values, you can look at the directory - `aisuite/providers/`. The list of supported providers are of the format - `<provider>_provider.py` in that directory. We welcome providers to add support to this library by adding an implementation file in this directory. Please see section below for how to contribute.
For more examples, check out the `examples` directory where you will find several notebooks that you can run to experiment with the interface.
---
## Chat Completions
The chat API provides a high-level abstraction for model interactions. It supports all core parameters (`temperature`, `max_tokens`, `tools`, etc.) in a provider-agnostic way.
```python
response = client.chat.completions.create(
model="google:gemini-pro",
messages=[{"role": "user", "content": "Summarize this paragraph."}],
)
print(response.choices[0].message.content)
```
`aisuite` standardizes request and response structures so you can focus on logic rather than SDK differences.
---
## Tool Calling & Agentic apps
`aisuite` provides a simple abstraction for tool/function calling that works across supported providers. This is in addition to the regular abstraction of passing JSON spec of the tool to the `tools` parameter. The tool calling abstraction makes it easy to use tools with different LLMs without changing your code.
There are two ways to use tools with `aisuite`:
### 1. Manual Tool Handling
This is the default behavior when `max_turns` is not specified. In this mode, you have full control over the tool execution flow. You pass tools using the standard OpenAI JSON schema format, and `aisuite` returns the LLM's tool call requests in the response. You're then responsible for executing the tools, processing results, and sending them back to the model in subsequent requests.
This approach is useful when you need:
- Fine-grained control over tool execution logic
- Custom error handling or validation before executing tools
- The ability to selectively execute or skip certain tool calls
- Integration with existing tool execution pipelines
You can pass tools in the OpenAI tool format:
```python
def will_it_rain(location: str, time_of_day: str):
"""Check if it will rain in a location at a given time today.
Args:
location (str): Name of the city
time_of_day (str): Time of the day in HH:MM format.
"""
return "YES"
tools = [{
"type": "function",
"function": {
"name": "will_it_rain",
"description": "Check if it will rain in a location at a given time today",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Name of the city"
},
"time_of_day": {
"type": "string",
"description": "Time of the day in HH:MM format."
}
},
"required": ["location", "time_of_day"]
}
}
}]
response = client.chat.completions.create(
model="openai:gpt-4o",
messages=messages,
tools=tools
)
```
### 2. Automatic Tool Execution
When `max_turns` is specified, you can pass a list of callable Python functions as the `tools` parameter. `aisuite` will automatically handle the tool calling flow:
```python
def will_it_rain(location: str, time_of_day: str):
"""Check if it will rain in a location at a given time today.
Args:
location (str): Name of the city
time_of_day (str): Time of the day in HH:MM format.
"""
return "YES"
client = ai.Client()
messages = [{
"role": "user",
"content": "I live in San Francisco. Can you check for weather "
"and plan an outdoor picnic for me at 2pm?"
}]
# Automatic tool execution with max_turns
response = client.chat.completions.create(
model="openai:gpt-4o",
messages=messages,
tools=[will_it_rain],
max_turns=2 # Maximum number of back-and-forth tool calls
)
print(response.choices[0].message.content)
```
When `max_turns` is specified, `aisuite` will:
1. Send your message to the LLM
2. Execute any tool calls the LLM requests
3. Send the tool results back to the LLM
4. Repeat until the conversation is complete or max_turns is reached
In addition to `response.choices[0].message`, there is an additional field `response.choices[0].intermediate_messages` which contains the list of all messages including tool interactions used. This can be used to continue the conversation with the model.
For more detailed examples of tool calling, check out the `examples/tool_calling_abstraction.ipynb` notebook.
### Model Context Protocol (MCP) Integration
`aisuite` natively supports **MCP**, a standard protocol that allows LLMs to securely call external tools and access data. You can connect to MCP servers—such as a filesystem or database—and expose their tools directly to your model.
Read more about MCP here - https://modelcontextprotocol.io/docs/getting-started/intro
Install aisuite with MCP support:
```shell
pip install 'aisuite[mcp]'
```
You'll also need an MCP server. For example, to use the filesystem server:
```shell
npm install -g @modelcontextprotocol/server-filesystem
```
There are two ways to use MCP tools with aisuite:
#### Option 1: Config Dict Format (Recommended for Simple Use Cases)
```python
import aisuite as ai
client = ai.Client()
response = client.chat.completions.create(
model="openai:gpt-4o",
messages=[{"role": "user", "content": "List the files in the current directory"}],
tools=[{
"type": "mcp",
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/directory"]
}],
max_turns=3
)
print(response.choices[0].message.content)
```
#### Option 2: Explicit MCPClient (Recommended for Advanced Use Cases)
```python
import aisuite as ai
from aisuite.mcp import MCPClient
# Create MCP client once, reuse across requests
mcp = MCPClient(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/path/to/directory"]
)
# Use with aisuite
client = ai.Client()
response = client.chat.completions.create(
model="openai:gpt-4o",
messages=[{"role": "user", "content": "List the files"}],
tools=mcp.get_callable_tools(),
max_turns=3
)
print(response.choices[0].message.content)
mcp.close() # Clean up
```
For detailed usage (security filters, tool prefixing, and `MCPClient` management), see [docs/mcp-tools.md](docs/mcp-tools.md).
For detailed examples, see `examples/mcp_tools_example.ipynb`.
---
## Extending aisuite: Adding a Provider
New providers can be added by implementing a lightweight adapter. The system uses a naming convention for discovery:
| Element | Convention |
| --------------- | ---------------------------------- |
| **Module file** | `<provider>_provider.py` |
| **Class name** | `<Provider>Provider` (capitalized) |
Example:
```python
# providers/openai_provider.py
class OpenaiProvider(BaseProvider):
...
```
This convention ensures consistency and enables automatic loading of new integrations.
---
## Contributing
Contributions are welcome. Please review the [Contributing Guide](https://github.com/andrewyng/aisuite/blob/main/CONTRIBUTING.md) and join our [Discord](https://discord.gg/T6Nvn8ExSb) for discussions.
---
## License
Released under the **MIT License** — free for commercial and non-commercial use.
---
================================================
FILE: aisuite/__init__.py
================================================
from .client import Client
from .framework.message import Message
from .utils.tools import Tools
================================================
FILE: aisuite/client.py
================================================
from .provider import ProviderFactory
import os
from .utils.tools import Tools
from typing import Union, BinaryIO, Optional, Any, Literal
from contextlib import ExitStack
from .framework.message import (
TranscriptionResponse,
)
from .framework.asr_params import ParamValidator
# Import MCP utilities for config dict support
try:
from .mcp.config import is_mcp_config
from .mcp.client import MCPClient
MCP_AVAILABLE = True
except ImportError:
MCP_AVAILABLE = False
class Client:
def __init__(
self,
provider_configs: dict = {},
extra_param_mode: Literal["strict", "warn", "permissive"] = "warn",
):
"""
Initialize the client with provider configurations.
Use the ProviderFactory to create provider instances.
Args:
provider_configs (dict): A dictionary containing provider configurations.
Each key should be a provider string (e.g., "google" or "aws-bedrock"),
and the value should be a dictionary of configuration options for that provider.
For example:
{
"openai": {"api_key": "your_openai_api_key"},
"aws-bedrock": {
"aws_access_key": "your_aws_access_key",
"aws_secret_key": "your_aws_secret_key",
"aws_region": "us-west-2"
}
}
extra_param_mode (str): How to handle unknown ASR parameters.
- "strict": Raise ValueError on unknown params (production)
- "warn": Log warning on unknown params (default, development)
- "permissive": Allow all params without validation (testing)
"""
self.providers = {}
self.provider_configs = provider_configs
self.extra_param_mode = extra_param_mode
self.param_validator = ParamValidator(extra_param_mode)
self._chat = None
self._audio = None
def _initialize_providers(self):
"""Helper method to initialize or update providers."""
for provider_key, config in self.provider_configs.items():
provider_key = self._validate_provider_key(provider_key)
self.providers[provider_key] = ProviderFactory.create_provider(
provider_key, config
)
def _validate_provider_key(self, provider_key):
"""
Validate if the provider key corresponds to a supported provider.
"""
supported_providers = ProviderFactory.get_supported_providers()
if provider_key not in supported_providers:
raise ValueError(
f"Invalid provider key '{provider_key}'. Supported providers: {supported_providers}. "
"Make sure the model string is formatted correctly as 'provider:model'."
)
return provider_key
def configure(self, provider_configs: Optional[dict] = None):
"""
Configure the client with provider configurations.
"""
if provider_configs is None:
return
self.provider_configs.update(provider_configs)
# Providers will be lazily initialized when needed
@property
def chat(self):
"""Return the chat API interface."""
if not self._chat:
self._chat = Chat(self)
return self._chat
@property
def audio(self):
"""Return the audio API interface."""
if not self._audio:
self._audio = Audio(self)
return self._audio
class Chat:
def __init__(self, client: "Client"):
self.client = client
self._completions = Completions(self.client)
@property
def completions(self):
"""Return the completions interface."""
return self._completions
class Completions:
def __init__(self, client: "Client"):
self.client = client
def _process_mcp_configs(self, tools: list) -> tuple[list, list]:
"""
Process tools list and convert MCP config dicts to callable tools.
This method:
1. Detects MCP config dicts ({"type": "mcp", ...})
2. Creates MCPClient instances from configs
3. Extracts callable tools with filtering and prefixing
4. Mixes MCP tools with regular callable tools
5. Returns both processed tools and MCP clients for cleanup
Args:
tools: List of tools (mix of callables and MCP configs)
Returns:
Tuple of (processed_tools, mcp_clients):
- processed_tools: List of callable tools only
- mcp_clients: List of MCPClient instances to be cleaned up
Example:
>>> tools = [
... my_function,
... {"type": "mcp", "name": "fs", "command": "npx", "args": [...]},
... another_function
... ]
>>> callable_tools, mcp_clients = self._process_mcp_configs(tools)
>>> # Returns: ([my_function, fs_tool1, fs_tool2, ..., another_function], [mcp_client])
"""
if not MCP_AVAILABLE:
# If MCP not installed, check if user is trying to use it
if any(is_mcp_config(tool) for tool in tools if isinstance(tool, dict)):
raise ImportError(
"MCP tools require the 'mcp' package. "
"Install it with: pip install 'aisuite[mcp]' or pip install mcp"
)
return tools, []
processed_tools = []
mcp_clients = []
for tool in tools:
if isinstance(tool, dict) and is_mcp_config(tool):
# It's an MCP config dict - convert to callable tools
try:
mcp_client = MCPClient.from_config(tool)
mcp_clients.append(mcp_client)
# Get tools with config settings
mcp_tools = mcp_client.get_callable_tools(
allowed_tools=tool.get("allowed_tools"),
use_tool_prefix=tool.get("use_tool_prefix", False),
)
processed_tools.extend(mcp_tools)
except Exception as e:
raise ValueError(
f"Failed to create MCP client from config: {e}\n"
f"Config: {tool}"
)
else:
# Regular callable tool - pass through
processed_tools.append(tool)
return processed_tools, mcp_clients
def _extract_thinking_content(self, response):
"""
Extract content between <think> tags if present and store it in reasoning_content.
Args:
response: The response object from the provider
Returns:
Modified response object
"""
if hasattr(response, "choices") and response.choices:
message = response.choices[0].message
if hasattr(message, "content") and message.content:
content = message.content.strip()
if content.startswith("<think>") and "</think>" in content:
# Extract content between think tags
start_idx = len("<think>")
end_idx = content.find("</think>")
thinking_content = content[start_idx:end_idx].strip()
# Store the thinking content
message.reasoning_content = thinking_content
# Remove the think tags from the original content
message.content = content[end_idx + len("</think>") :].strip()
return response
def _tool_runner(
self,
provider,
model_name: str,
messages: list,
tools: Any,
max_turns: int,
**kwargs,
):
"""
Handle tool execution loop for max_turns iterations.
Args:
provider: The provider instance to use for completions
model_name: Name of the model to use
messages: List of conversation messages
tools: Tools instance or list of callable tools
max_turns: Maximum number of tool execution turns
**kwargs: Additional arguments to pass to the provider
Returns:
The final response from the model with intermediate responses and messages
"""
# Handle tools validation and conversion
if isinstance(tools, Tools):
tools_instance = tools
kwargs["tools"] = tools_instance.tools()
else:
# Check if passed tools are callable
if not all(callable(tool) for tool in tools):
raise ValueError("One or more tools is not callable")
tools_instance = Tools(tools)
kwargs["tools"] = tools_instance.tools()
turns = 0
intermediate_responses = [] # Store intermediate responses
intermediate_messages = [] # Store all messages including tool interactions
while turns < max_turns:
# Make the API call
response = provider.chat_completions_create(model_name, messages, **kwargs)
response = self._extract_thinking_content(response)
# Store intermediate response
intermediate_responses.append(response)
# Check if there are tool calls in the response
tool_calls = (
getattr(response.choices[0].message, "tool_calls", None)
if hasattr(response, "choices")
else None
)
# Store the model's message
intermediate_messages.append(response.choices[0].message)
if not tool_calls:
# Set the intermediate data in the final response
response.intermediate_responses = intermediate_responses[
:-1
] # Exclude final response
response.choices[0].intermediate_messages = intermediate_messages
return response
# Execute tools and get results
results, tool_messages = tools_instance.execute_tool(tool_calls)
# Add tool messages to intermediate messages
intermediate_messages.extend(tool_messages)
# Add the assistant's response and tool results to messages
messages.extend([response.choices[0].message, *tool_messages])
turns += 1
# Set the intermediate data in the final response
response.intermediate_responses = intermediate_responses[
:-1
] # Exclude final response
response.choices[0].intermediate_messages = intermediate_messages
return response
def create(self, model: str, messages: list, **kwargs):
"""
Create chat completion based on the model, messages, and any extra arguments.
Supports automatic tool execution when max_turns is specified.
"""
# Check that correct format is used
if ":" not in model:
raise ValueError(
f"Invalid model format. Expected 'provider:model', got '{model}'"
)
# Extract the provider key from the model identifier, e.g., "google:gemini-xx"
provider_key, model_name = model.split(":", 1)
# Validate if the provider is supported
supported_providers = ProviderFactory.get_supported_providers()
if provider_key not in supported_providers:
raise ValueError(
f"Invalid provider key '{provider_key}'. Supported providers: {supported_providers}. "
"Make sure the model string is formatted correctly as 'provider:model'."
)
# Initialize provider if not already initialized
# TODO: Add thread-safe provider initialization with lock to prevent race conditions
# when multiple threads try to initialize the same provider simultaneously.
if provider_key not in self.client.providers:
config = self.client.provider_configs.get(provider_key, {})
self.client.providers[provider_key] = ProviderFactory.create_provider(
provider_key, config
)
provider = self.client.providers.get(provider_key)
if not provider:
raise ValueError(f"Could not load provider for '{provider_key}'.")
# Extract tool-related parameters
max_turns = kwargs.pop("max_turns", None)
tools = kwargs.pop("tools", None)
# Use ExitStack to manage MCP client cleanup automatically
with ExitStack() as stack:
# Convert MCP config dicts to callable tools and get MCP clients
mcp_clients = []
if tools is not None:
tools, mcp_clients = self._process_mcp_configs(tools)
# Register all MCP clients for automatic cleanup
for mcp_client in mcp_clients:
stack.enter_context(mcp_client)
# Check environment variable before allowing multi-turn tool execution
if max_turns is not None and tools is not None:
return self._tool_runner(
provider,
model_name,
messages.copy(),
tools,
max_turns,
**kwargs,
)
# Default behavior without tool execution
# Delegate the chat completion to the correct provider's implementation
response = provider.chat_completions_create(model_name, messages, **kwargs)
return self._extract_thinking_content(response)
class Audio:
"""Audio API interface."""
def __init__(self, client: "Client"):
self.client = client
self._transcriptions = Transcriptions(self.client)
@property
def transcriptions(self):
"""Return the transcriptions interface."""
return self._transcriptions
class Transcriptions:
"""Transcriptions API interface."""
def __init__(self, client: "Client"):
self.client = client
def create(
self,
*,
model: str,
file: Union[str, BinaryIO],
**kwargs,
) -> TranscriptionResponse:
"""
Create audio transcription with parameter validation.
This method uses a pass-through approach with validation:
- Common parameters (OpenAI-style) are auto-mapped to provider equivalents
- Provider-specific parameters are passed through directly
- Unknown parameters are handled based on extra_param_mode
Args:
model: Provider and model in format 'provider:model' (e.g., 'openai:whisper-1')
file: Audio file to transcribe (file path or file-like object)
**kwargs: Transcription parameters (provider-specific or common)
Common parameters (portable across providers):
- language: Language code (e.g., "en")
- prompt: Context for the transcription
- temperature: Sampling temperature (0-1, OpenAI only)
Provider-specific parameters are passed through directly.
See provider documentation for valid parameters.
Returns:
TranscriptionResponse: Unified response (batch or streaming)
Raises:
ValueError: If model format invalid, provider not supported,
or unknown params in strict mode
Examples:
# Portable code (OpenAI-style params)
>>> result = client.audio.transcriptions.create(
... model="openai:whisper-1",
... file="audio.mp3",
... language="en"
... )
# Provider-specific features
>>> result = client.audio.transcriptions.create(
... model="deepgram:nova-2",
... file="audio.mp3",
... language="en", # Common param
... punctuate=True, # Deepgram-specific
... diarize=True # Deepgram-specific
... )
"""
# Validate model format
if ":" not in model:
raise ValueError(
f"Invalid model format. Expected 'provider:model', got '{model}'"
)
# Extract provider and model name
provider_key, model_name = model.split(":", 1)
# Validate provider is supported
supported_providers = ProviderFactory.get_supported_providers()
if provider_key not in supported_providers:
raise ValueError(
f"Invalid provider key '{provider_key}'. "
f"Supported providers: {supported_providers}"
)
# Validate and map parameters
validated_params = self.client.param_validator.validate_and_map(
provider_key, kwargs
)
# Initialize provider if not already initialized
if provider_key not in self.client.providers:
config = self.client.provider_configs.get(provider_key, {})
try:
self.client.providers[provider_key] = ProviderFactory.create_provider(
provider_key, config
)
except ImportError as e:
raise ValueError(f"Provider '{provider_key}' is not available: {e}")
provider = self.client.providers.get(provider_key)
if not provider:
raise ValueError(f"Could not load provider for '{provider_key}'.")
# Check if provider supports audio transcription
if not hasattr(provider, "audio") or provider.audio is None:
raise ValueError(
f"Provider '{provider_key}' does not support audio transcription."
)
# Determine if streaming is requested
should_stream = validated_params.get("stream", False)
# Delegate to provider implementation
try:
if should_stream:
# Check if provider supports output streaming
if hasattr(provider.audio, "transcriptions") and hasattr(
provider.audio.transcriptions, "create_stream_output"
):
return provider.audio.transcriptions.create_stream_output(
model_name, file, **validated_params
)
else:
raise ValueError(
f"Provider '{provider_key}' does not support streaming transcription."
)
else:
# Non-streaming (batch) transcription
if hasattr(provider.audio, "transcriptions") and hasattr(
provider.audio.transcriptions, "create"
):
return provider.audio.transcriptions.create(
model_name, file, **validated_params
)
else:
raise ValueError(
f"Provider '{provider_key}' does not support audio transcription."
)
except NotImplementedError:
raise ValueError(
f"Provider '{provider_key}' does not support audio transcription."
)
================================================
FILE: aisuite/design-notes/asr-parameter-design-motivation.md
================================================
# ASR - API Parameter Design Philosophy
## Design Goal: Portable Code with Provider Flexibility
The ASR parameter system is designed around a core principle: **developers should write portable code that works across providers, while retaining the ability to use provider-specific features when needed**. This document explains the rationale behind our parameter classification and validation approach.
---
## Mandatory Parameters and Common Mappings
### The Foundation: Minimal Requirements
Every transcription needs just two things:
- **`model`**: Which model/provider to use
- **`file`**: What audio to transcribe
By keeping mandatory parameters minimal, we maximize compatibility and reduce the barrier to getting started.
### Common Parameters: Write Once, Run Anywhere
Beyond the basics, there are concepts that exist across providers but use different names or formats. We handle three common parameters that auto-map to each provider's native API:
**Example: Same code, different providers**
```python
# Works with OpenAI
result = client.audio.transcriptions.create(
model="openai:whisper-1",
file="meeting.mp3",
language="en",
prompt="discussion about API design"
)
# Exact same code works with Deepgram
result = client.audio.transcriptions.create(
model="deepgram:nova-2",
file="meeting.mp3",
language="en",
prompt="discussion about API design"
)
```
Behind the scenes:
- **`language`** passes through as `language` for both OpenAI and Deepgram, but expands to `language_code: "en-US"` for Google
- **`prompt`** passes as `prompt` to OpenAI, transforms to `keywords: ["discussion", "about", "API", "design"]` for Deepgram, and becomes `speech_contexts: [{"phrases": ["discussion about API design"]}]` for Google
- **`temperature`** passes through to OpenAI (which supports it) and is silently ignored by Deepgram and Google (which don't)
**Why auto-mapping?** Developers shouldn't need to remember that Google uses `language_code` while others use `language`, or that Deepgram expects a list of keywords. The framework handles these provider quirks transparently, letting you write portable code.
---
## Provider-Specific Features: Pass-Through for Power Users
Each provider has unique features that give them competitive advantages. We don't limit you to the "lowest common denominator" - if you need provider-specific functionality, it's available:
**Deepgram's advanced features:**
```python
result = client.audio.transcriptions.create(
model="deepgram:nova-2",
file="meeting.mp3",
language="en",
punctuate=True, # Deepgram-specific
diarize=True, # Deepgram-specific
sentiment=True, # Deepgram-specific
smart_format=True # Deepgram-specific
)
```
**Google's speech contexts:**
```python
result = client.audio.transcriptions.create(
model="google:latest_long",
file="meeting.mp3",
language_code="en-US",
enable_automatic_punctuation=True, # Google-specific
max_alternatives=3, # Google-specific
speech_contexts=[{"phrases": ["API", "SDK", "REST"]}] # Google-specific
)
```
These provider-specific parameters pass through directly to the provider's SDK. The framework validates them based on your configured mode (see next section), but doesn't block access to unique features.
---
## Progressive Validation: Safety When You Need It
The validation system supports three modes to match different development stages:
### Development Mode: `"warn"` (Default)
```python
client = Client(extra_param_mode="warn")
```
Unknown parameters trigger warnings but continue execution. Perfect for exploration and prototyping. You see *"OpenAI doesn't support 'punctuate'"* but your code keeps running.
### Strict Mode: `"strict"`
```python
client = Client(extra_param_mode="strict")
```
Unknown parameters raise errors immediately. Use in production to catch typos, configuration mistakes, or provider API changes early. Ensures no silent failures.
### Permissive Mode: `"permissive"`
```python
client = Client(extra_param_mode="permissive")
```
All parameters pass through without validation. Use for beta features, experimental parameters, or when providers add new capabilities faster than framework updates.
**Progressive workflow:**
1. **Develop** with `warn` - explore freely, see warnings
2. **Refactor** - fix warnings to make code portable
3. **Deploy** with `strict` - ensure production safety
---
## Developer Experience Benefits
### 1. Write Portable Code Naturally
The same parameter names work across providers. Switch from OpenAI to Deepgram by changing one word: the model identifier.
### 2. Progressive Enhancement
Start with portable common parameters. Add provider-specific features only where you need them. Your core logic remains portable even when using advanced features for specific providers.
### 3. Zero Framework Lock-in
Parameter names come directly from provider APIs, not framework abstractions. If you need to remove the framework, you already know the native API - the names are identical.
### 4. Validation That Adapts to You
Choose your safety level based on context. Strict for production, warn for development, permissive for bleeding-edge features. The framework supports your workflow rather than constraining it.
### 5. No Documentation Friction
Copy parameters from provider docs directly. No need to learn our abstraction layer or figure out mappings - we handle the common cases, you use native names for everything else.
---
## Alternative Design Considered
We considered creating a unified options object (`TranscriptionOptions`) that explicitly defines all parameters with framework-specific names. We chose pass-through instead because:
1. **Provider APIs evolve faster than frameworks** - New parameters appear frequently. Pass-through lets developers use them immediately (in permissive mode) without waiting for framework updates.
2. **Provider features don't map cleanly** - Deepgram's sentiment analysis, Google's complex speech contexts, OpenAI's timestamp granularities - each is unique. A unified object means either losing functionality or creating complex provider-specific abstractions.
3. **Direct API access reduces friction** - Developers already know their provider's API from official docs. They can use parameter names directly rather than learning another abstraction layer.
The pass-through approach with progressive validation provides the best of both worlds: portability for common cases, power for advanced features, and safety when you need it.
---
## Design Principles Summary
- **Mandatory Minimal**: Only `model` and `file` required
- **Common Auto-Mapped**: Frequent cross-provider concepts map transparently
- **Provider-Specific Pass-Through**: Unique features remain accessible
- **Progressive Validation**: Three modes for different development stages
- **Zero Abstraction Tax**: Use provider APIs directly with optional safety nets
This design prioritizes developer experience through portability without sacrificing power, validation without blocking experimentation, and simplicity without limiting functionality.
================================================
FILE: aisuite/framework/__init__.py
================================================
from .provider_interface import ProviderInterface
from .chat_completion_response import ChatCompletionResponse
from .message import Message
================================================
FILE: aisuite/framework/asr_params.py
================================================
"""
ASR parameter registry and validation.
This module provides a unified parameter validation system for audio transcription
across different providers. It supports:
- Common parameters (OpenAI-style) that are auto-mapped to provider equivalents
- Provider-specific parameters that are passed through directly
- Three validation modes: strict, warn, and permissive
"""
from typing import Dict, Set, Any, Optional, Literal
import logging
logger = logging.getLogger(__name__)
# Common parameters that get auto-mapped across providers
# These follow OpenAI's API conventions for maximum portability
COMMON_PARAMS: Dict[str, Dict[str, Optional[str]]] = {
"language": {
"openai": "language",
"deepgram": "language",
"google": "language_code",
"huggingface": None, # Not supported by Inference API
},
"prompt": {
"openai": "prompt",
"deepgram": "keywords",
"google": "speech_contexts",
"huggingface": None, # Not supported
},
"temperature": {
"openai": "temperature",
"deepgram": None, # Not supported
"google": None, # Not supported
"huggingface": "temperature", # Supported as generation param
},
}
# Valid provider-specific parameters
# Each provider has its own set of supported parameters
PROVIDER_PARAMS: Dict[str, Set[str]] = {
"openai": {
# Basic parameters
"language",
"prompt",
"temperature",
# Output format
"response_format", # "json" | "text" | "srt" | "verbose_json" | "vtt"
"timestamp_granularities", # ["word"] | ["segment"] | ["word", "segment"]
# Streaming
"stream", # Boolean
},
"deepgram": {
# Basic parameters
"language",
"model",
# Text enhancement
"punctuate", # Auto-add punctuation
"diarize", # Speaker diarization
"utterances", # Sentence-level timestamps
"paragraphs", # Paragraph segmentation
"smart_format", # Format numbers, dates, etc.
"profanity_filter", # Filter profanity
# Advanced features
"search", # Search for keywords: ["keyword1", "keyword2"]
"replace", # Replace words: {"um": "", "uh": ""}
"keywords", # Boost keywords: ["important", "technical"]
"numerals", # Format numerals
"measurements", # Format measurements
# AI features
"sentiment", # Sentiment analysis
"topics", # Topic detection
"intents", # Intent recognition
"summarize", # Auto-summarization
# Audio format
"encoding", # "linear16" | "mp3" | "flac"
"sample_rate", # Integer (Hz)
"channels", # Integer
# Quality and alternatives
"confidence", # Include confidence scores
"alternatives", # Number of alternative transcripts
# Streaming
"interim_results", # Get interim results while streaming
},
"google": {
# Basic parameters
"language_code", # BCP-47 code like "en-US"
"model", # "latest_long" | "latest_short" | "default"
# Audio format
"encoding", # "LINEAR16" | "FLAC" | "MP3"
"sample_rate_hertz", # Integer
"audio_channel_count", # Integer
# Text enhancement
"enable_automatic_punctuation", # Boolean
"profanity_filter", # Boolean
"enable_spoken_punctuation", # Boolean
"enable_spoken_emojis", # Boolean
# Speaker features
"enable_speaker_diarization", # Boolean
"diarization_speaker_count", # Integer (max speakers)
"min_speaker_count", # Integer
# Metadata
"enable_word_time_offsets", # Word-level timestamps
"enable_word_confidence", # Word-level confidence
"max_alternatives", # Number of alternatives
# Context
"speech_contexts", # [{"phrases": [...], "boost": float}]
"boost", # Float (phraseHint boost)
# Streaming
"interim_results", # Boolean
"single_utterance", # Boolean (stop after one utterance)
},
"huggingface": {
# Basic parameters
"model", # Model ID on Hugging Face Hub
"temperature", # Generation temperature
# API options
"return_timestamps", # Boolean or "word" or "char"
"use_cache", # Boolean: use cached inference
"wait_for_model", # Boolean: wait if model is loading
# Generation parameters
"top_k", # Integer: top-k sampling
"top_p", # Float: nucleus sampling
"max_length", # Integer: maximum output length
"do_sample", # Boolean: enable sampling
},
}
# Language code expansion for Google (2-letter to locale codes)
GOOGLE_LANGUAGE_MAP = {
"en": "en-US",
"es": "es-ES",
"fr": "fr-FR",
"de": "de-DE",
"it": "it-IT",
"pt": "pt-BR",
"ja": "ja-JP",
"ko": "ko-KR",
"zh": "zh-CN",
"ar": "ar-SA",
"hi": "hi-IN",
"ru": "ru-RU",
"nl": "nl-NL",
"pl": "pl-PL",
"sv": "sv-SE",
"da": "da-DK",
"no": "nb-NO",
"fi": "fi-FI",
"tr": "tr-TR",
"th": "th-TH",
"vi": "vi-VN",
}
class ParamValidator:
"""
Validates and maps ASR parameters for different providers.
This class handles three types of parameters:
1. Common parameters (OpenAI-style) - auto-mapped to provider equivalents
2. Provider-specific parameters - passed through with validation
3. Unknown parameters - handled based on extra_param_mode
"""
def __init__(self, extra_param_mode: Literal["strict", "warn", "permissive"]):
"""
Initialize the parameter validator.
Args:
extra_param_mode: How to handle unknown parameters
- "strict": Raise ValueError on unknown params
- "warn": Log warning on unknown params (default)
- "permissive": Allow all params without validation
"""
self.extra_param_mode = extra_param_mode
def validate_and_map(
self, provider_key: str, params: Dict[str, Any]
) -> Dict[str, Any]:
"""
Validate and map parameters for the given provider.
This method:
1. Maps common parameters to provider-specific equivalents
2. Validates provider-specific parameters
3. Handles unknown parameters based on extra_param_mode
Args:
provider_key: Provider identifier (e.g., "openai", "deepgram")
params: Raw parameters from user
Returns:
Validated and mapped parameters ready for provider API
Raises:
ValueError: If extra_param_mode="strict" and unknown params found
"""
result = {}
unknown_params = []
provider_params = PROVIDER_PARAMS.get(provider_key, set())
for key, value in params.items():
# Check if it's a common param that needs mapping
if key in COMMON_PARAMS:
mapped_key = COMMON_PARAMS[key].get(provider_key)
# Provider doesn't support this common param
if mapped_key is None:
logger.debug(
f"Parameter '{key}' not supported by {provider_key}, ignoring"
)
continue
# Transform value if needed (e.g., "en" -> "en-US" for Google)
mapped_value = self._transform_value(provider_key, key, value)
result[mapped_key] = mapped_value
# Check if it's a valid provider-specific param
elif key in provider_params:
result[key] = value
# Unknown parameter
else:
unknown_params.append(key)
# Handle unknown parameters based on mode
if unknown_params:
self._handle_unknown(provider_key, unknown_params)
# In permissive mode, still pass them through
if self.extra_param_mode == "permissive":
for key in unknown_params:
result[key] = params[key]
return result
def _transform_value(self, provider_key: str, param_key: str, value: Any) -> Any:
"""
Transform parameter values during mapping.
This handles provider-specific transformations like:
- Google: Expanding "en" to "en-US"
- Google: Wrapping prompt in speech_contexts structure
- Deepgram: Converting prompt string to keywords list
Args:
provider_key: Provider identifier
param_key: Parameter name (from COMMON_PARAMS)
value: Parameter value to transform
Returns:
Transformed parameter value
"""
# Google: Expand 2-letter language codes to locale codes
if provider_key == "google" and param_key == "language":
if isinstance(value, str) and len(value) == 2:
return GOOGLE_LANGUAGE_MAP.get(value, f"{value}-US")
# Google: Wrap prompt in speech_contexts structure
if provider_key == "google" and param_key == "prompt":
return [{"phrases": [value]}]
# Deepgram: Split prompt into keywords list
if provider_key == "deepgram" and param_key == "prompt":
if isinstance(value, str):
return value.split()
return value
return value
def _handle_unknown(self, provider_key: str, unknown_params: list):
"""
Handle unknown parameters based on extra_param_mode.
Args:
provider_key: Provider identifier
unknown_params: List of unknown parameter names
Raises:
ValueError: If extra_param_mode="strict"
"""
msg = (
f"Unknown parameters for {provider_key}: {unknown_params}. "
f"See {provider_key} documentation for valid parameters."
)
if self.extra_param_mode == "strict":
raise ValueError(msg)
elif self.extra_param_mode == "warn":
import warnings
warnings.warn(msg, UserWarning)
# permissive mode: do nothing
================================================
FILE: aisuite/framework/chat_completion_response.py
================================================
"""Defines the ChatCompletionResponse class."""
from typing import Optional
from aisuite.framework.choice import Choice
from aisuite.framework.message import CompletionUsage
# pylint: disable=too-few-public-methods
class ChatCompletionResponse:
"""Used to conform to the response model of OpenAI."""
def __init__(self):
"""Initializes the ChatCompletionResponse."""
self.choices = [Choice()] # Adjust the range as needed for more choices
self.usage: Optional[CompletionUsage] = None
================================================
FILE: aisuite/framework/choice.py
================================================
from aisuite.framework.message import Message
from typing import Literal, Optional, List
class Choice:
def __init__(self):
self.finish_reason: Optional[Literal["stop", "tool_calls"]] = None
self.message = Message(
content=None,
tool_calls=None,
role="assistant",
refusal=None,
reasoning_content=None,
)
self.intermediate_messages: List[Message] = []
================================================
FILE: aisuite/framework/message.py
================================================
"""
Interface to hold contents of api responses when they do not confirm
to the OpenAI style response.
"""
from typing import Literal, Optional, List, AsyncGenerator, Union, Dict, Any
from pydantic import BaseModel
from dataclasses import dataclass, field
class Function(BaseModel):
"""Represents a function call."""
arguments: str
name: str
class ChatCompletionMessageToolCall(BaseModel):
"""Represents a tool call in a chat completion message."""
id: str
function: Function
type: Literal["function"]
class Message(BaseModel):
"""Represents a message in a chat completion."""
content: Optional[str] = None
reasoning_content: Optional[str] = None
tool_calls: Optional[List[ChatCompletionMessageToolCall]] = None
role: Optional[Literal["user", "assistant", "system", "tool"]] = None
refusal: Optional[str] = None
class CompletionTokensDetails(BaseModel):
"""Details about the tokens used in a completion."""
accepted_prediction_tokens: Optional[int] = None
"""
When using Predicted Outputs, the number of tokens in the prediction that
appeared in the completion.
"""
audio_tokens: Optional[int] = None
"""Audio input tokens generated by the model."""
reasoning_tokens: Optional[int] = None
"""Tokens generated by the model for reasoning."""
rejected_prediction_tokens: Optional[int] = None
"""
When using Predicted Outputs, the number of tokens in the prediction that did
not appear in the completion. However, like reasoning tokens, these tokens are
still counted in the total completion tokens for purposes of billing, output,
and context window limits.
"""
class PromptTokensDetails(BaseModel):
"""Details about the tokens used in a prompt."""
text_tokens: Optional[int] = None
"""Tokens generated by the model for text."""
audio_tokens: Optional[int] = None
"""Audio input tokens present in the prompt."""
cached_tokens: Optional[int] = None
"""Cached tokens present in the prompt."""
class CompletionUsage(BaseModel):
"""Represents the token usage for a completion."""
completion_tokens: Optional[int] = None
"""Number of tokens in the generated completion."""
prompt_tokens: Optional[int] = None
"""Number of tokens in the prompt."""
total_tokens: Optional[int] = None
"""Total number of tokens used in the request (prompt + completion)."""
completion_tokens_details: Optional[CompletionTokensDetails] = None
"""Breakdown of tokens used in a completion."""
prompt_tokens_details: Optional[PromptTokensDetails] = None
"""Breakdown of tokens used in the prompt."""
class Word(BaseModel):
"""Represents a single word with timing information."""
word: str
start: float
end: float
confidence: Optional[float] = None # Common across Deepgram, Azure, AWS
speaker: Optional[int] = None # Speaker diarization (Deepgram, Azure, AWS)
speaker_confidence: Optional[float] = None # Speaker identification confidence
punctuated_word: Optional[str] = None # Word with punctuation (some providers)
class Segment(BaseModel):
"""Represents a segment of transcribed text with detailed information."""
id: int
seek: int
start: float
end: float
text: str
# OpenAI Whisper specific fields
tokens: Optional[List[int]] = None
temperature: Optional[float] = None
avg_logprob: Optional[float] = None
compression_ratio: Optional[float] = None
no_speech_prob: Optional[float] = None
# Common ASR provider fields
confidence: Optional[float] = None # Segment-level confidence
speaker: Optional[int] = None # Primary speaker for this segment
speaker_confidence: Optional[float] = None # Speaker identification confidence
words: Optional[List[Word]] = None # Words within this segment
class Alternative(BaseModel):
"""Represents an alternative transcription hypothesis (common in many ASR APIs)."""
transcript: str
confidence: Optional[float] = None
words: Optional[List[Word]] = None
class Channel(BaseModel):
"""Represents a single audio channel (for multi-channel audio)."""
alternatives: List[Alternative]
search: Optional[List[dict]] = None # Search results if keyword search enabled
class TranscriptionResult(BaseModel):
"""
Unified transcription result format supporting multiple ASR providers.
Based on OpenAI Whisper API but extended for common ASR features.
"""
# Core fields (supported by most providers)
text: str
language: Optional[str] = None
confidence: Optional[float] = None # Overall transcription confidence
# OpenAI Whisper specific fields
task: Optional[str] = None # "transcribe" or "translate"
duration: Optional[float] = None
segments: Optional[List[Segment]] = None
words: Optional[List[Word]] = None
# Multi-channel and alternatives support (Deepgram, Azure, etc.)
channels: Optional[List[Channel]] = None
alternatives: Optional[List[Alternative]] = None
# Advanced features (various providers)
utterances: Optional[List[dict]] = None # Speaker utterances
paragraphs: Optional[List[dict]] = None # Paragraph detection
topics: Optional[List[dict]] = None # Topic detection
intents: Optional[List[dict]] = None # Intent recognition
sentiment: Optional[dict] = None # Sentiment analysis
summary: Optional[dict] = None # Auto-summarization
# Metadata
metadata: Optional[dict] = None # Provider-specific metadata
model_info: Optional[dict] = None # Model information
class StreamingTranscriptionChunk(BaseModel):
"""Represents a single chunk of streaming transcription data."""
text: str
is_final: bool
confidence: Optional[float] = None
start_time: Optional[float] = None
end_time: Optional[float] = None
speaker_id: Optional[int] = None
speaker_confidence: Optional[float] = None
words: Optional[List[Word]] = None
sequence_number: Optional[int] = None
channel: Optional[int] = None
provider_data: Optional[dict] = None
# Type alias for streaming transcription responses
StreamingTranscriptionResponse = AsyncGenerator[StreamingTranscriptionChunk, None]
# Union type for both batch and streaming responses
TranscriptionResponse = Union[TranscriptionResult, StreamingTranscriptionResponse]
@dataclass
class TranscriptionOptions:
"""Unified transcription options for ASR providers."""
# Core parameters
language: Optional[str] = None
# Audio format parameters
audio_format: Optional[str] = None
sample_rate: Optional[int] = None
channels: Optional[int] = None
encoding: Optional[str] = None # Audio encoding type
# Output format
response_format: Optional[str] = None
include_word_timestamps: Optional[bool] = None
include_segment_timestamps: Optional[bool] = None
timestamp_granularities: Optional[List[str]] = None # OpenAI: ["word", "segment"]
# Context and guidance
prompt: Optional[str] = None
context_phrases: Optional[List[str]] = None
boost_phrases: Optional[List[str]] = None
# Speaker features
enable_speaker_diarization: Optional[bool] = None
max_speakers: Optional[int] = None
min_speakers: Optional[int] = None
# Text processing
enable_automatic_punctuation: Optional[bool] = None
enable_profanity_filter: Optional[bool] = None
enable_smart_formatting: Optional[bool] = None
enable_word_confidence: Optional[bool] = None
enable_spoken_punctuation: Optional[bool] = None
enable_spoken_emojis: Optional[bool] = None
# Advanced features
enable_sentiment_analysis: Optional[bool] = None
enable_topic_detection: Optional[bool] = None
enable_intent_recognition: Optional[bool] = None
enable_summarization: Optional[bool] = None
enable_translation: Optional[bool] = None
translation_target_language: Optional[str] = None
# Confidence and alternatives
include_confidence_scores: Optional[bool] = None
max_alternatives: Optional[int] = None
# Processing options
temperature: Optional[float] = None
interim_results: Optional[bool] = None
vad_sensitivity: Optional[float] = None
stream: Optional[bool] = None # Enable streaming output
# Custom parameters
custom_parameters: Dict[str, Any] = field(default_factory=dict)
def __post_init__(self):
"""Validate parameters and constraints."""
# Validate constraints
if self.temperature is not None and not (0.0 <= self.temperature <= 1.0):
raise ValueError("temperature must be between 0.0 and 1.0")
if self.max_speakers is not None and self.max_speakers < 1:
raise ValueError("max_speakers must be at least 1")
if self.min_speakers is not None and self.min_speakers < 1:
raise ValueError("min_speakers must be at least 1")
if (
self.max_speakers is not None
and self.min_speakers is not None
and self.min_speakers > self.max_speakers
):
raise ValueError("min_speakers cannot be greater than max_speakers")
if self.vad_sensitivity is not None and not (
0.0 <= self.vad_sensitivity <= 1.0
):
raise ValueError("vad_sensitivity must be between 0.0 and 1.0")
def has_any_parameters(self) -> bool:
"""Check if any parameters are set."""
for field_name, field_value in self.__dict__.items():
if field_name == "custom_parameters":
if field_value:
return True
elif field_value is not None:
return True
return False
def get_set_parameters(self) -> Dict[str, Any]:
"""Get only the parameters that are set."""
set_params = {}
for field_name, field_value in self.__dict__.items():
if field_name == "custom_parameters":
if field_value:
set_params[field_name] = field_value
elif field_value is not None:
set_params[field_name] = field_value
return set_params
================================================
FILE: aisuite/framework/parameter_mapper.py
================================================
"""
Parameter mapping utilities for ASR providers.
Maps unified TranscriptionOptions to provider-specific parameters.
"""
from typing import Dict, Any, List, TYPE_CHECKING
if TYPE_CHECKING:
from .message import TranscriptionOptions
class ParameterMapper:
"""Maps unified TranscriptionOptions to provider-specific parameters."""
# OpenAI Whisper API parameter mapping
OPENAI_MAPPING = {
"language": "language",
"response_format": "response_format",
"temperature": "temperature",
"prompt": "prompt",
"stream": "stream",
"timestamp_granularities": "timestamp_granularities",
}
# Deepgram API parameter mapping
DEEPGRAM_MAPPING = {
"language": "language",
"enable_automatic_punctuation": "punctuate",
"enable_smart_formatting": "smart_format",
"enable_speaker_diarization": "diarize",
"include_word_timestamps": "utterances",
"include_segment_timestamps": "paragraphs",
"context_phrases": "keywords",
"enable_profanity_filter": "profanity_filter",
"enable_sentiment_analysis": "sentiment",
"enable_topic_detection": "topics",
"enable_intent_recognition": "intents",
"enable_summarization": "summarize",
"interim_results": "interim_results",
"channels": "channels",
"sample_rate": "sample_rate",
"include_confidence_scores": "confidence",
"enable_word_confidence": "confidence",
"max_alternatives": "alternatives",
"stream": "interim_results",
"encoding": "encoding",
# timestamp_granularities is handled specially for Deepgram
}
# Google API parameter mapping
GOOGLE_MAPPING = {
"language": "language_code",
"sample_rate": "sample_rate_hertz",
"channels": "audio_channel_count",
"enable_automatic_punctuation": "enable_automatic_punctuation",
"enable_speaker_diarization": "enable_speaker_diarization",
"max_speakers": "diarization_speaker_count",
"min_speakers": "min_speaker_count",
"include_word_timestamps": "enable_word_time_offsets",
"include_confidence_scores": "enable_word_confidence",
"enable_word_confidence": "enable_word_confidence",
"context_phrases": "speech_contexts",
"enable_profanity_filter": "profanity_filter",
"max_alternatives": "max_alternatives",
"boost_phrases": "speech_contexts",
"audio_format": "encoding",
"encoding": "encoding",
"interim_results": "interim_results",
"stream": "interim_results",
"enable_spoken_punctuation": "enable_spoken_punctuation",
"enable_spoken_emojis": "enable_spoken_emojis",
}
@classmethod
def map_to_openai(cls, options: "TranscriptionOptions") -> Dict[str, Any]:
"""Map TranscriptionOptions to OpenAI Whisper API parameters."""
params = {}
# Handle timestamp granularities
timestamp_granularities = []
if options.include_word_timestamps:
timestamp_granularities.append("word")
if options.include_segment_timestamps:
timestamp_granularities.append("segment")
if timestamp_granularities:
params["timestamp_granularities"] = timestamp_granularities
# Map other parameters
for opt_key, api_key in cls.OPENAI_MAPPING.items():
if hasattr(options, opt_key):
value = getattr(options, opt_key)
if value is not None and not opt_key.startswith("include_"):
params[api_key] = value
# Handle custom parameters
cls._apply_custom_parameters(params, options.custom_parameters, "openai")
return params
@classmethod
def map_to_deepgram(cls, options: "TranscriptionOptions") -> Dict[str, Any]:
"""Map TranscriptionOptions to Deepgram API parameters."""
params = {}
for opt_key, api_key in cls.DEEPGRAM_MAPPING.items():
if hasattr(options, opt_key):
value = getattr(options, opt_key)
if value is not None:
params[api_key] = value
# Handle special cases
if options.context_phrases:
params["keywords"] = options.context_phrases
# Handle timestamp_granularities conversion for Deepgram
if (
hasattr(options, "timestamp_granularities")
and options.timestamp_granularities
):
if "word" in options.timestamp_granularities:
params["utterances"] = True
if "segment" in options.timestamp_granularities:
params["paragraphs"] = True
# Handle custom parameters
cls._apply_custom_parameters(params, options.custom_parameters, "deepgram")
return params
@classmethod
def map_to_google(cls, options: "TranscriptionOptions") -> Dict[str, Any]:
"""Map TranscriptionOptions to Google Speech-to-Text API parameters."""
params = {}
for opt_key, api_key in cls.GOOGLE_MAPPING.items():
if hasattr(options, opt_key):
value = getattr(options, opt_key)
if value is not None:
if opt_key == "context_phrases" or opt_key == "boost_phrases":
if "speech_contexts" not in params:
params["speech_contexts"] = []
params["speech_contexts"].append({"phrases": value})
elif opt_key == "language":
# Handle language code conversion for Google
# Google expects BCP-47 locale codes like "en-US", not just "en"
if len(value) == 2: # Convert "en" to "en-US"
language_map = {
"en": "en-US",
"es": "es-ES",
"fr": "fr-FR",
"de": "de-DE",
"it": "it-IT",
"pt": "pt-BR", # Portuguese -> Brazilian Portuguese
"ja": "ja-JP",
"ko": "ko-KR",
"zh": "zh-CN", # Chinese -> Simplified Chinese
"ar": "ar-SA", # Arabic -> Saudi Arabia
"hi": "hi-IN", # Hindi -> India
"ru": "ru-RU", # Russian -> Russia
"nl": "nl-NL", # Dutch -> Netherlands
"pl": "pl-PL", # Polish -> Poland
"sv": "sv-SE", # Swedish -> Sweden
"da": "da-DK", # Danish -> Denmark
"no": "nb-NO", # Norwegian -> Norway
"fi": "fi-FI", # Finnish -> Finland
"tr": "tr-TR", # Turkish -> Turkey
"th": "th-TH", # Thai -> Thailand
"vi": "vi-VN", # Vietnamese -> Vietnam
}
params[api_key] = language_map.get(value, f"{value}-US")
else:
params[api_key] = value
else:
params[api_key] = value
# Handle audio encoding mapping
if options.audio_format:
encoding_map = {
"wav": "LINEAR16",
"flac": "FLAC",
"mp3": "MP3",
"ogg": "OGG_OPUS",
"webm": "WEBM_OPUS",
}
params["encoding"] = encoding_map.get(
options.audio_format.lower(), "LINEAR16"
)
# Handle timestamp_granularities conversion for Google
if (
hasattr(options, "timestamp_granularities")
and options.timestamp_granularities
):
if "word" in options.timestamp_granularities:
params["enable_word_time_offsets"] = True
# Handle custom parameters
cls._apply_custom_parameters(params, options.custom_parameters, "google")
return params
@classmethod
def _apply_custom_parameters(
cls, params: Dict[str, Any], custom_params: Dict[str, Any], provider: str
):
"""
Apply custom parameters for the specific provider.
Only provider-namespaced parameters are supported.
Parameters not under a provider key are IGNORED.
"""
if not custom_params:
return
# Provider-specific namespacing ONLY
# Users MUST structure custom_parameters like:
# {
# "openai": {"response_format": "srt", "temperature": 0.2},
# "deepgram": {"search": ["keyword"], "numerals": True},
# "google": {"use_enhanced": True, "adaptation": {...}}
# }
if provider in custom_params:
params.update(custom_params[provider])
# Note: Any parameters not under a provider key are ignored
================================================
FILE: aisuite/framework/provider_interface.py
================================================
"""The shared interface for model providers."""
# TODO(rohit): Remove this. This interface is obsolete in favor of Provider.
class ProviderInterface:
"""Defines the expected behavior for provider-specific interfaces."""
def chat_completion_create(self, messages=None, model=None, temperature=0) -> None:
"""Create a chat completion using the specified messages, model, and temperature.
This method must be implemented by subclasses to perform completions.
Args:
----
messages (list): The chat history.
model (str): The identifier of the model to be used in the completion.
temperature (float): The temperature to use in the completion.
Raises:
------
NotImplementedError: If this method has not been implemented by a subclass.
"""
raise NotImplementedError(
"Provider Interface has not implemented chat_completion_create()"
)
================================================
FILE: aisuite/mcp/__init__.py
================================================
"""
MCP (Model Context Protocol) integration for aisuite.
This module provides support for using MCP servers and their tools with aisuite's
unified interface for AI providers.
MCP allows AI applications to connect to external data sources and tools through
a standardized protocol. This integration makes MCP tools available as Python
callables that work seamlessly with aisuite's existing tool calling infrastructure.
Example:
>>> from aisuite import Client
>>> from aisuite.mcp import MCPClient
>>>
>>> # Connect to an MCP server
>>> mcp = MCPClient(
... command="npx",
... args=["-y", "@modelcontextprotocol/server-filesystem", "/docs"]
... )
>>>
>>> # Use MCP tools with any provider
>>> client = Client()
>>> response = client.chat.completions.create(
... model="openai:gpt-4o",
... messages=[{"role": "user", "content": "Read README.md"}],
... tools=mcp.get_callable_tools(),
... max_turns=2
... )
"""
from .client import MCPClient
__all__ = ["MCPClient"]
================================================
FILE: aisuite/mcp/client.py
================================================
"""
MCP Client for aisuite.
This module provides the MCPClient class that connects to MCP servers and
exposes their tools as Python callables compatible with aisuite's tool system.
"""
import asyncio
import json
from typing import Any, Callable, Dict, List, Optional
from contextlib import contextmanager
try:
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import httpx
except ImportError as e:
if "mcp" in str(e):
raise ImportError(
"MCP support requires the 'mcp' package. "
"Install it with: pip install 'aisuite[mcp]' or pip install mcp"
)
elif "httpx" in str(e):
raise ImportError(
"HTTP transport requires the 'httpx' package. "
"Install it with: pip install httpx"
)
raise
from .tool_wrapper import create_mcp_tool_wrapper
from .config import MCPConfig, validate_mcp_config, get_transport_type
class MCPClient:
"""
Client for connecting to MCP servers and using their tools with aisuite.
This class manages the connection to an MCP server, discovers available tools,
and creates Python callable wrappers that work seamlessly with aisuite's
existing tool calling infrastructure.
Example:
>>> # Connect to an MCP server
>>> mcp = MCPClient(
... command="npx",
... args=["-y", "@modelcontextprotocol/server-filesystem", "/path"]
... )
>>>
>>> # Get tools and use with aisuite
>>> import aisuite as ai
>>> client = ai.Client()
>>> response = client.chat.completions.create(
... model="openai:gpt-4o",
... messages=[{"role": "user", "content": "List files"}],
... tools=mcp.get_callable_tools(),
... max_turns=2
... )
The MCPClient handles:
- Starting and managing the MCP server process
- Performing the MCP handshake
- Discovering available tools
- Creating callable wrappers for tools
- Executing tool calls via the MCP protocol
"""
def __init__(
self,
command: Optional[str] = None,
args: Optional[List[str]] = None,
env: Optional[Dict[str, str]] = None,
server_url: Optional[str] = None,
headers: Optional[Dict[str, str]] = None,
timeout: float = 30.0,
name: Optional[str] = None,
):
"""
Initialize the MCP client and connect to an MCP server.
Supports both stdio and HTTP transports. Provide either stdio parameters
(command) OR HTTP parameters (server_url), but not both.
Args:
command: Command to start the MCP server (e.g., "npx", "python") - for stdio transport
args: Arguments to pass to the command (e.g., ["-y", "server-package"]) - for stdio transport
env: Optional environment variables for the server process - for stdio transport
server_url: Base URL of the MCP server (e.g., "http://localhost:8000") - for HTTP transport
headers: Optional HTTP headers (e.g., for authentication) - for HTTP transport
timeout: Request timeout in seconds - for HTTP transport (default: 30.0)
name: Optional name for this MCP client (used for logging and prefixing)
Raises:
ImportError: If the mcp or httpx package is not installed
ValueError: If both stdio and HTTP parameters are provided, or neither
RuntimeError: If connection to the MCP server fails
"""
# Validate transport parameters
has_stdio = command is not None
has_http = server_url is not None
if not (has_stdio ^ has_http):
raise ValueError(
"Must provide exactly one transport: either 'command' (stdio) or 'server_url' (HTTP)."
)
# Store parameters based on transport type
if has_stdio:
self.server_params = StdioServerParameters(
command=command,
args=args or [],
env=env,
)
self.name = name or command
# Stdio-specific state
self._session: Optional[ClientSession] = None
self._read = None
self._write = None
self._stdio_context = None
else: # HTTP
self.server_url = server_url
self.headers = headers or {}
self.timeout = timeout
self.name = name or server_url
# HTTP-specific state (initialized in _async_connect_http)
self._http_client = None
self._request_id = 0
self._session_id: Optional[str] = None # MCP session ID from server
# Shared state
self._tools_cache: Optional[List[Dict[str, Any]]] = None
self._event_loop: Optional[asyncio.AbstractEventLoop] = None
# Initialize connection
self._connect()
@classmethod
def from_config(cls, config: Dict[str, Any]) -> "MCPClient":
"""
Create an MCPClient from a configuration dictionary.
This method validates the config and creates an MCPClient instance.
It supports both stdio and HTTP transports.
Args:
config: MCP configuration dictionary
Returns:
MCPClient instance
Raises:
ValueError: If configuration is invalid
Example (stdio):
>>> config = {
... "type": "mcp",
... "name": "filesystem",
... "command": "npx",
... "args": ["-y", "@modelcontextprotocol/server-filesystem", "/docs"]
... }
>>> mcp = MCPClient.from_config(config)
Example (HTTP):
>>> config = {
... "type": "mcp",
... "name": "api-server",
... "server_url": "http://localhost:8000",
... "headers": {"Authorization": "Bearer token"}
... }
>>> mcp = MCPClient.from_config(config)
"""
# Validate and normalize config
validated_config = validate_mcp_config(config)
# Determine transport type
transport = get_transport_type(validated_config)
if transport == "stdio":
return cls(
command=validated_config["command"],
args=validated_config.get("args", []),
env=validated_config.get("env"),
name=validated_config["name"],
)
else: # http
return cls(
server_url=validated_config["server_url"],
headers=validated_config.get("headers"),
timeout=validated_config.get("timeout", 30.0),
name=validated_config["name"],
)
@staticmethod
def get_tools_from_config(config: Dict[str, Any]) -> List[Callable]:
"""
Convenience method to create MCPClient and get callable tools from config.
This is a helper that combines from_config() and get_callable_tools()
in a single call. It respects the config's allowed_tools and use_tool_prefix
settings.
Args:
config: MCP configuration dictionary
Returns:
List of callable tool wrappers
Example:
>>> config = {
... "type": "mcp",
... "name": "filesystem",
... "command": "npx",
... "args": ["..."],
... "allowed_tools": ["read_file"],
... "use_tool_prefix": True
... }
>>> tools = MCPClient.get_tools_from_config(config)
>>> # Returns callable tools filtered and prefixed per config
"""
# Validate config first
validated_config = validate_mcp_config(config)
# Create client
client = MCPClient.from_config(validated_config)
# Get tools with config settings
tools = client.get_callable_tools(
allowed_tools=validated_config.get("allowed_tools"),
use_tool_prefix=validated_config.get("use_tool_prefix", False),
)
return tools
def _connect(self):
"""
Establish connection to the MCP server.
This method:
1. Creates an event loop if needed
2. Detects transport type (stdio or HTTP)
3. Establishes connection via appropriate transport
4. Performs the MCP initialization handshake
5. Caches the available tools
Note: Automatically handles Jupyter/IPython environments where an event loop
is already running by using nest_asyncio.
"""
# Get or create event loop
try:
self._event_loop = asyncio.get_running_loop()
except RuntimeError:
self._event_loop = asyncio.new_event_loop()
asyncio.set_event_loop(self._event_loop)
# Enable nested event loops for Jupyter/IPython compatibility
# This allows run_until_complete() to work in environments where
# an event loop is already running (like Jupyter notebooks)
try:
import nest_asyncio
nest_asyncio.apply()
except ImportError:
# nest_asyncio not available - will work fine in regular Python
# but may fail in Jupyter. User should install: pip install nest-asyncio
pass
# Detect transport type and run appropriate async connection
if hasattr(self, "server_url"):
# HTTP transport
self._event_loop.run_until_complete(self._async_connect_http())
else:
# Stdio transport
self._event_loop.run_until_complete(self._async_connect())
async def _async_connect(self):
"""Async connection initialization for stdio transport."""
# Start the MCP server and store the context manager
self._stdio_context = stdio_client(self.server_params)
self._read, self._write = await self._stdio_context.__aenter__()
# Create session
self._session = ClientSession(self._read, self._write)
await self._session.__aenter__()
# Initialize connection
await self._session.initialize()
# List available tools and cache them
tools_result = await self._session.list_tools()
# Convert Tool objects to dicts for easier handling
if hasattr(tools_result, "tools"):
self._tools_cache = [
{
"name": tool.name,
"description": (
tool.description if hasattr(tool, "description") else ""
),
"inputSchema": (
tool.inputSchema if hasattr(tool, "inputSchema") else {}
),
}
for tool in tools_result.tools
]
else:
self._tools_cache = []
async def _parse_sse_response(
self, response: httpx.Response, request_id: int
) -> Dict[str, Any]:
"""
Parse SSE stream and extract JSON-RPC response.
SSE format per spec:
data: {"jsonrpc": "2.0", "id": 1, "result": {...}}
data: {"jsonrpc": "2.0", "method": "notification", ...}
The server may send multiple events (notifications, requests) before
sending the final response. We collect events until we find the
response matching our request_id.
Args:
response: HTTP response with text/event-stream content type
request_id: The JSON-RPC request ID to match
Returns:
Response result dictionary
Raises:
RuntimeError: If server returns an error or no matching response found
"""
result = None
async for line in response.aiter_lines():
line = line.strip()
# Skip empty lines and comments
if not line or line.startswith(":"):
continue
# Parse SSE data field
if line.startswith("data: "):
data = line[6:] # Remove 'data: ' prefix
try:
message = json.loads(data)
# Check if this is the response to our request
if message.get("id") == request_id:
if "error" in message:
error = message["error"]
raise RuntimeError(
f"MCP server error: {error.get('message', 'Unknown error')} "
f"(code: {error.get('code', 'unknown')})"
)
result = message.get("result", {})
# Found our response, can stop parsing
break
# Note: Server may send other notifications/requests
# which we ignore for now (future enhancement for bidirectional comms)
except json.JSONDecodeError:
# Invalid JSON in SSE data, skip this event
continue
if result is None:
raise RuntimeError(
f"No response received in SSE stream for request {request_id}"
)
return result
async def _send_http_request(
self, method: str, params: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Send JSON-RPC request to MCP server via HTTP.
Args:
method: JSON-RPC method name
params: Optional parameters
Returns:
Response result
Raises:
RuntimeError: If HTTP request fails or server returns an error
"""
# Increment request ID
self._request_id += 1
# Build JSON-RPC 2.0 request
request_data = {
"jsonrpc": "2.0",
"id": self._request_id,
"method": method,
}
if params:
request_data["params"] = params
# Use the exact server URL provided by the user
url = self.server_url.rstrip("/")
# Build headers: MCP requires Accept header with both content types
# Merge with any user-provided headers and session ID
request_headers = {
"Accept": "application/json, text/event-stream",
}
if self._session_id:
request_headers["Mcp-Session-Id"] = self._session_id
if self.headers:
request_headers.update(self.headers)
try:
response = await self._http_client.post(
url, json=request_data, headers=request_headers
)
response.raise_for_status()
# Check for MCP session ID in response headers
if "Mcp-Session-Id" in response.headers and not self._session_id:
self._session_id = response.headers["Mcp-Session-Id"]
# Check Content-Type to determine response format
content_type = response.headers.get("content-type", "").lower()
if "application/json" in content_type:
# Handle JSON response (simple request-response)
result = response.json()
# Check for JSON-RPC error
if "error" in result:
error = result["error"]
raise RuntimeError(
f"MCP server error: {error.get('message', 'Unknown error')} "
f"(code: {error.get('code', 'unknown')})"
)
return result.get("result", {})
elif "text/event-stream" in content_type:
# Handle SSE stream response
return await self._parse_sse_response(response, request_data["id"])
else:
raise RuntimeError(
f"Unexpected Content-Type from MCP server: {content_type}"
)
except httpx.HTTPError as e:
raise RuntimeError(
f"HTTP request to MCP server failed: {type(e).__name__}: {str(e)}"
)
async def _send_notification(
self, method: str, params: Optional[Dict[str, Any]] = None
):
"""
Send a JSON-RPC notification (no response expected).
Notifications are JSON-RPC messages without an ID field.
Per the spec, the server should not send a response.
Args:
method: JSON-RPC method name
params: Optional parameters
"""
# Build JSON-RPC notification (no id field)
notification = {
"jsonrpc": "2.0",
"method": method,
}
if params:
notification["params"] = params
# Build headers
url = self.server_url.rstrip("/")
request_headers = {
"Accept": "application/json, text/event-stream",
}
if self._session_id:
request_headers["Mcp-Session-Id"] = self._session_id
if self.headers:
request_headers.update(self.headers)
try:
# Send notification - don't wait for/expect a response
await self._http_client.post(
url, json=notification, headers=request_headers
)
# Note: We don't check response for notifications
except httpx.HTTPError:
# Notifications may timeout or fail, which is acceptable
pass
async def _async_connect_http(self):
"""Async connection initialization for HTTP transport."""
# Create HTTP client
self._http_client = httpx.AsyncClient(timeout=self.timeout)
# Send initialize request
init_params = {
"protocolVersion": "2024-11-05",
"capabilities": {"roots": {"listChanged": True}, "sampling": {}},
"clientInfo": {"name": "aisuite-mcp-client", "version": "1.0.0"},
}
await self._send_http_request("initialize", init_params)
# Send initialized notification (required by MCP spec)
await self._send_notification("notifications/initialized")
# List available tools
tools_result = await self._send_http_request("tools/list")
# Cache tools
self._tools_cache = [
{
"name": tool["name"],
"description": tool.get("description", ""),
"inputSchema": tool.get("inputSchema", {}),
}
for tool in tools_result.get("tools", [])
]
def list_tools(self) -> List[Dict[str, Any]]:
"""
List all available tools from the MCP server.
Returns:
List of tool schemas in MCP format
Example:
>>> tools = mcp.list_tools()
>>> for tool in tools:
... print(tool['name'], '-', tool['description'])
"""
if self._tools_cache is None:
raise RuntimeError("Not connected to MCP server")
return self._tools_cache
def get_callable_tools(
self,
allowed_tools: Optional[List[str]] = None,
use_tool_prefix: bool = False,
) -> List[Callable]:
"""
Get all MCP tools as Python callables compatible with aisuite.
This is the primary method for using MCP tools with aisuite. It returns
a list of callable wrappers that can be passed directly to the `tools`
parameter of `client.chat.completions.create()`.
Args:
allowed_tools: Optional list of tool names to include. If None, all tools are included.
use_tool_prefix: If True, prefix tool names with "{client_name}__"
Returns:
List of callable tool wrappers
Example:
>>> # Get all tools
>>> mcp_tools = mcp.get_callable_tools()
>>>
>>> # Get specific tools only
>>> mcp_tools = mcp.get_callable_tools(allowed_tools=["read_file"])
>>>
>>> # Get tools with name prefixing
>>> mcp_tools = mcp.get_callable_tools(use_tool_prefix=True)
>>> # Tools will be named "filesystem__read_file", etc.
"""
all_tools = self.list_tools()
# Filter tools if allowed_tools is specified
if allowed_tools is not None:
all_tools = [t for t in all_tools if t["name"] in allowed_tools]
# Create wrappers
wrappers = []
for tool in all_tools:
wrapper = create_mcp_tool_wrapper(self, tool["name"], tool)
# Apply prefix if requested
if use_tool_prefix:
original_name = wrapper.__name__
wrapper.__name__ = f"{self.name}__{original_name}"
wrappers.append(wrapper)
return wrappers
def get_tool(self, tool_name: str) -> Optional[Callable]:
"""
Get a specific MCP tool by name as a Python callable.
Args:
tool_name: Name of the tool to retrieve
Returns:
Callable wrapper for the tool, or None if not found
Example:
>>> read_file = mcp.get_tool("read_file")
>>> write_file = mcp.get_tool("write_file")
>>> tools = [read_file, write_file]
"""
tools = self.list_tools()
for tool in tools:
if tool["name"] == tool_name:
return create_mcp_tool_wrapper(self, tool_name, tool)
return None
def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
"""
Execute an MCP tool call.
This method is called by MCPToolWrapper when the LLM requests a tool.
It handles the async MCP protocol communication and returns the result.
Automatically routes to the appropriate transport (stdio or HTTP).
Args:
tool_name: Name of the tool to call
arguments: Tool arguments as a dictionary
Returns:
The result from the MCP tool execution
Raises:
RuntimeError: If not connected or tool call fails
"""
# Detect transport type and route to appropriate method
if hasattr(self, "_http_client") and self._http_client is not None:
# HTTP transport
if self._http_client is None:
raise RuntimeError("Not connected to MCP server (HTTP)")
result = self._event_loop.run_until_complete(
self._async_call_tool_http(tool_name, arguments)
)
else:
# Stdio transport
if self._session is None:
raise RuntimeError("Not connected to MCP server (stdio)")
result = self._event_loop.run_until_complete(
self._async_call_tool(tool_name, arguments)
)
return result
async def _async_call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
"""
Async implementation of tool calling for stdio transport.
Args:
tool_name: Name of the tool
arguments: Tool arguments
Returns:
Tool execution result
"""
result = await self._session.call_tool(tool_name, arguments)
# Extract content from MCP result
# MCP returns results in various formats, we try to extract the most useful content
if hasattr(result, "content"):
if isinstance(result.content, list) and len(result.content) > 0:
# Get first content item
content_item = result.content[0]
if hasattr(content_item, "text"):
return content_item.text
elif hasattr(content_item, "data"):
return content_item.data
return str(content_item)
return result.content
# If no content attribute, return the whole result
return str(result)
async def _async_call_tool_http(
self, tool_name: str, arguments: Dict[str, Any]
) -> Any:
"""
Async implementation of tool calling for HTTP transport.
Args:
tool_name: Name of the tool
arguments: Tool arguments
Returns:
Tool execution result
"""
params = {"name": tool_name, "arguments": arguments}
result = await self._send_http_request("tools/call", params)
# Extract content from MCP result (HTTP format)
# Similar to stdio, but result is already a dict
if "content" in result:
content = result["content"]
if isinstance(content, list) and len(content) > 0:
# Get first content item
content_item = content[0]
if isinstance(content_item, dict):
if "text" in content_item:
return content_item["text"]
elif "data" in content_item:
return content_item["data"]
return str(content_item)
return content
# If no content field, return the whole result
return json.dumps(result)
def close(self):
"""
Close the connection to the MCP server.
Works for both stdio and HTTP transports. It's recommended to use
the MCPClient as a context manager to ensure proper cleanup, but
this method can be called manually if needed.
Example:
>>> mcp = MCPClient(command="npx", args=["server"])
>>> try:
... # Use mcp
... pass
... finally:
... mcp.close()
"""
# Check if we need to cleanup (either stdio or HTTP)
needs_cleanup = (hasattr(self, "_session") and self._session is not None) or (
hasattr(self, "_http_client") and self._http_client is not None
)
if needs_cleanup:
self._event_loop.run_until_complete(self._async_close())
async def _async_close(self):
"""Async cleanup for both stdio and HTTP transports."""
# Cleanup stdio transport
try:
if hasattr(self, "_session") and self._session:
await self._session.__aexit__(None, None, None)
except RuntimeError as e:
# Suppress anyio cancel scope errors that occur in Jupyter/nest_asyncio environments
# This is a known incompatibility between nest_asyncio and anyio task groups
if "cancel scope" not in str(e).lower():
raise
except Exception:
pass # Ignore other errors during session cleanup
try:
if hasattr(self, "_stdio_context") and self._stdio_context:
await self._stdio_context.__aexit__(None, None, None)
except RuntimeError as e:
# Suppress anyio cancel scope errors that occur in Jupyter/nest_asyncio environments
# This is a known incompatibility between nest_asyncio and anyio task groups
if "cancel scope" not in str(e).lower():
raise
except Exception:
pass # Ignore other errors during stdio cleanup
# Cleanup HTTP transport
try:
if hasattr(self, "_http_client") and self._http_client:
await self._http_client.aclose()
except Exception:
pass # Ignore errors during HTTP client cleanup
def __enter__(self):
"""Context manager entry."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit."""
self.close()
return False
def __repr__(self) -> str:
"""String representation."""
num_tools = len(self._tools_cache) if self._tools_cache else 0
if hasattr(self, "server_url"):
return f"MCPClient(server_url={self.server_url!r}, tools={num_tools})"
else:
return (
f"MCPClient(command={self.server_params.command!r}, tools={num_tools})"
)
================================================
FILE: aisuite/mcp/config.py
================================================
"""
MCP configuration validation and normalization.
This module provides utilities for validating and normalizing MCP tool
configuration dictionaries passed to aisuite's chat completion API.
"""
from typing import Any, Dict, List, Literal, Optional, TypedDict
class MCPConfig(TypedDict, total=False):
"""Type definition for MCP tool configuration."""
# Required fields
type: Literal["mcp"]
name: str
# Transport: stdio
command: str
args: List[str]
env: Dict[str, str]
cwd: str
# Transport: http
server_url: str
headers: Dict[str, str]
# Tool filtering
allowed_tools: List[str]
# Namespacing
use_tool_prefix: bool
# Safety limits
timeout_seconds: int
response_bytes_cap: int
# Connection behavior
lazy_connect: bool
# Default values
DEFAULT_TIMEOUT_SECONDS = 30
DEFAULT_RESPONSE_BYTES_CAP = 10 * 1024 * 1024 # 10 MB
DEFAULT_USE_TOOL_PREFIX = False
DEFAULT_LAZY_CONNECT = False
def validate_mcp_config(config: Dict[str, Any]) -> MCPConfig:
"""
Validate and normalize an MCP tool configuration.
This function:
1. Validates required fields are present
2. Auto-detects transport type (stdio vs http)
3. Validates transport-specific required fields
4. Sets defaults for optional fields
5. Returns a normalized config dict
Args:
config: Raw MCP configuration dictionary
Returns:
Validated and normalized MCP configuration
Raises:
ValueError: If configuration is invalid
Example:
>>> config = {
... "type": "mcp",
... "name": "filesystem",
... "command": "npx",
... "args": ["-y", "@modelcontextprotocol/server-filesystem", "/docs"]
... }
>>> validated = validate_mcp_config(config)
>>> validated['timeout_seconds']
30
"""
# Check type field
if config.get("type") != "mcp":
raise ValueError(f"Invalid config type: {config.get('type')}. Expected 'mcp'")
# Check name field (required)
if "name" not in config:
raise ValueError(
"MCP config must have 'name' field. "
"Example: {'type': 'mcp', 'name': 'my_server', ...}"
)
name = config["name"]
if not isinstance(name, str) or not name.strip():
raise ValueError(f"MCP 'name' must be a non-empty string, got: {name}")
# Auto-detect transport type
has_stdio = "command" in config
has_http = "server_url" in config
if not (has_stdio ^ has_http):
raise ValueError(
"MCP config must have either 'command' or 'server_url'."
"Use one or the other to specify transport type."
)
# Validate stdio transport
if has_stdio:
if not isinstance(config["command"], str):
raise ValueError(
f"MCP 'command' must be a string, got: {type(config['command'])}"
)
# args is optional but should be a list if present
if "args" in config and not isinstance(config["args"], list):
raise ValueError(f"MCP 'args' must be a list, got: {type(config['args'])}")
# env is optional but should be a dict if present
if "env" in config and not isinstance(config["env"], dict):
raise ValueError(f"MCP 'env' must be a dict, got: {type(config['env'])}")
# Validate http transport
if has_http:
if not isinstance(config["server_url"], str):
raise ValueError(
f"MCP 'server_url' must be a string, got: {type(config['server_url'])}"
)
# Validate URL format
server_url = config["server_url"]
if not (server_url.startswith("http://") or server_url.startswith("https://")):
raise ValueError(
f"MCP 'server_url' must start with http:// or https://, got: {server_url}"
)
# headers is optional but should be a dict if present
if "headers" in config and not isinstance(config["headers"], dict):
raise ValueError(
f"MCP 'headers' must be a dict, got: {type(config['headers'])}"
)
# timeout is optional but should be a number if present
if "timeout" in config:
if not isinstance(config["timeout"], (int, float)):
raise ValueError(
f"MCP 'timeout' must be a number, got: {type(config['timeout'])}"
)
if config["timeout"] <= 0:
raise ValueError(
f"MCP 'timeout' must be positive, got: {config['timeout']}"
)
# Validate optional fields
if "allowed_tools" in config:
if not isinstance(config["allowed_tools"], list):
raise ValueError(
f"MCP 'allowed_tools' must be a list, got: {type(config['allowed_tools'])}"
)
if not all(isinstance(t, str) for t in config["allowed_tools"]):
raise ValueError("MCP 'allowed_tools' must be a list of strings")
if "use_tool_prefix" in config:
if not isinstance(config["use_tool_prefix"], bool):
raise ValueError(
f"MCP 'use_tool_prefix' must be a boolean, got: {type(config['use_tool_prefix'])}"
)
if "timeout_seconds" in config:
if not isinstance(config["timeout_seconds"], (int, float)):
raise ValueError(
f"MCP 'timeout_seconds' must be a number, got: {type(config['timeout_seconds'])}"
)
if config["timeout_seconds"] <= 0:
raise ValueError(
f"MCP 'timeout_seconds' must be positive, got: {config['timeout_seconds']}"
)
if "response_bytes_cap" in config:
if not isinstance(config["response_bytes_cap"], int):
raise ValueError(
f"MCP 'response_bytes_cap' must be an integer, got: {type(config['response_bytes_cap'])}"
)
if config["response_bytes_cap"] <= 0:
raise ValueError(
f"MCP 'response_bytes_cap' must be positive, got: {config['response_bytes_cap']}"
)
# Create normalized config with defaults
normalized: MCPConfig = {
"type": "mcp",
"name": config["name"],
}
# Copy transport fields
if has_stdio:
normalized["command"] = config["command"]
normalized["args"] = config.get("args", [])
if "env" in config:
normalized["env"] = config["env"]
if "cwd" in config:
normalized["cwd"] = config["cwd"]
else: # has_http
normalized["server_url"] = config["server_url"]
if "headers" in config:
normalized["headers"] = config["headers"]
if "timeout" in config:
normalized["timeout"] = config["timeout"]
# Copy optional fields with defaults
if "allowed_tools" in config:
normalized["allowed_tools"] = config["allowed_tools"]
normalized["use_tool_prefix"] = config.get(
"use_tool_prefix", DEFAULT_USE_TOOL_PREFIX
)
normalized["timeout_seconds"] = config.get(
"timeout_seconds", DEFAULT_TIMEOUT_SECONDS
)
normalized["response_bytes_cap"] = config.get(
"response_bytes_cap", DEFAULT_RESPONSE_BYTES_CAP
)
normalized["lazy_connect"] = config.get("lazy_connect", DEFAULT_LAZY_CONNECT)
return normalized
def is_mcp_config(obj: Any) -> bool:
"""
Check if an object is an MCP config dictionary.
Args:
obj: Object to check
Returns:
True if obj is a dict with type="mcp", False otherwise
Example:
>>> is_mcp_config({"type": "mcp", "name": "test"})
True
>>> is_mcp_config(lambda: None)
False
"""
return isinstance(obj, dict) and obj.get("type") == "mcp"
def get_transport_type(config: MCPConfig) -> Literal["stdio", "http"]:
"""
Determine the transport type from a validated MCP config.
Args:
config: Validated MCP configuration
Returns:
"stdio" or "http"
"""
if "command" in config:
return "stdio"
else:
return "http"
================================================
FILE: aisuite/mcp/schema_converter.py
================================================
"""
Schema conversion utilities for MCP tools.
This module provides functionality to convert MCP JSON Schema tool definitions
to Python type annotations that are compatible with aisuite's existing Tools class.
"""
from typing import Any, Dict, List, Optional, Union, get_args, get_origin
import inspect
def json_schema_to_python_type(schema: Dict[str, Any]) -> type:
"""
Convert a JSON Schema type definition to a Python type annotation.
Args:
schema: JSON Schema type definition (e.g., {"type": "string"})
Returns:
Python type annotation (e.g., str, int, List[str], etc.)
"""
schema_type = schema.get("type")
# Handle null/None
if schema_type == "null":
return type(None)
# Handle basic types
type_mapping = {
"string": str,
"number": float,
"integer": int,
"boolean": bool,
"object": dict,
"array": list,
}
if schema_type in type_mapping:
base_type = type_mapping[schema_type]
# Handle arrays with item type
if schema_type == "array" and "items" in schema:
item_type = json_schema_to_python_type(schema["items"])
return List[item_type]
return base_type
# Handle anyOf/oneOf (union types)
if "anyOf" in schema or "oneOf" in schema:
union_schemas = schema.get("anyOf", schema.get("oneOf", []))
types = [json_schema_to_python_type(s) for s in union_schemas]
if len(types) == 1:
return types[0]
return Union[tuple(types)]
# Default to Any if we can't determine the type
return Any
def mcp_schema_to_annotations(input_schema: Dict[str, Any]) -> Dict[str, type]:
"""
Convert MCP tool input schema to Python type annotations.
MCP tools use JSON Schema for their input parameters. This function
converts those schemas to Python type annotations that can be used
by aisuite's Tools class.
Args:
input_schema: MCP tool input schema (JSON Schema format)
Returns:
Dictionary mapping parameter names to Python types
Example:
>>> schema = {
... "type": "object",
... "properties": {
... "location": {"type": "string"},
... "count": {"type": "integer"}
... },
... "required": ["location"]
... }
>>> annotations = mcp_schema_to_annotations(schema)
>>> annotations
{'location': <class 'str'>, 'count': typing.Optional[int]}
"""
annotations = {}
if input_schema.get("type") != "object":
return annotations
properties = input_schema.get("properties", {})
required = input_schema.get("required", [])
for param_name, param_schema in properties.items():
param_type = json_schema_to_python_type(param_schema)
# Make optional if not in required list
if param_name not in required:
param_type = Optional[param_type]
annotations[param_name] = param_type
return annotations
def create_function_signature(
func_name: str, annotations: Dict[str, type], docstring: Optional[str] = None
) -> inspect.Signature:
"""
Create a function signature from parameter annotations.
Args:
func_name: Name of the function
annotations: Dictionary mapping parameter names to types
docstring: Optional docstring for the function
Returns:
inspect.Signature object
"""
parameters = []
for param_name, param_type in annotations.items():
# Check if it's an Optional type
if get_origin(param_type) is Union:
args = get_args(param_type)
if type(None) in args:
# It's Optional, set default to None
parameters.append(
inspect.Parameter(
param_name,
inspect.Parameter.KEYWORD_ONLY,
default=None,
annotation=param_type,
)
)
else:
parameters.append(
inspect.Parameter(
param_name,
inspect.Parameter.KEYWORD_ONLY,
annotation=param_type,
)
)
else:
# Required parameter
parameters.append(
inspect.Parameter(
param_name,
inspect.Parameter.KEYWORD_ONLY,
annotation=param_type,
)
)
return inspect.Signature(parameters)
def extract_parameter_descriptions(input_schema: Dict[str, Any]) -> Dict[str, str]:
"""
Extract parameter descriptions from MCP schema.
Args:
input_schema: MCP tool input schema
Returns:
Dictionary mapping parameter names to their descriptions
"""
descriptions = {}
properties = input_schema.get("properties", {})
for param_name, param_schema in properties.items():
if "description" in param_schema:
descriptions[param_name] = param_schema["description"]
return descriptions
def build_docstring(
tool_description: str, parameter_descriptions: Dict[str, str]
) -> str:
"""
Build a Python docstring from MCP tool description and parameter descriptions.
Args:
tool_description: Overall description of the tool
parameter_descriptions: Dictionary of parameter descriptions
Returns:
Formatted docstring
"""
lines = [tool_description, ""]
if parameter_descriptions:
lines.append("Args:")
for param_name, param_desc in parameter_descriptions.items():
lines.append(f" {param_name}: {param_desc}")
return "\n".join(lines)
================================================
FILE: aisuite/mcp/tool_wrapper.py
================================================
"""
MCP Tool Wrapper for aisuite.
This module provides the MCPToolWrapper class, which creates Python callable
wrappers around MCP tools that are compatible with aisuite's existing tool
calling infrastructure.
"""
from typing import Any, Callable, Dict, Optional
import asyncio
import inspect
from .schema_converter import (
mcp_schema_to_annotations,
extract_parameter_descriptions,
build_docstring,
)
class MCPToolWrapper:
"""
A callable wrapper around an MCP tool that makes it compatible with aisuite.
This class wraps an MCP tool and exposes it as a Python callable with proper
type annotations and docstrings that aisuite's Tools class can inspect and use.
The wrapper sets the following attributes that aisuite's Tools class reads:
- __name__: The tool name
- __doc__: The tool description and parameter documentation
- __annotations__: Python type annotations for parameters
When called, the wrapper executes the MCP tool via the MCP protocol.
Example:
>>> wrapper = MCPToolWrapper(mcp_client, "read_file", tool_schema)
>>> result = wrapper(path="/path/to/file")
"""
def __init__(
self,
mcp_client: "MCPClient", # Forward reference to avoid circular import
tool_name: str,
tool_schema: Dict[str, Any],
):
"""
Initialize the MCP tool wrapper.
Args:
mcp_client: The MCPClient instance that manages the connection
tool_name: Name of the MCP tool
tool_schema: MCP tool schema definition
"""
self.mcp_client = mcp_client
self.tool_name = tool_name
self.schema = tool_schema
# Set attributes that aisuite's Tools class will inspect
self.__name__ = tool_name
# Build docstring from MCP schema
description = tool_schema.get("description", "")
input_schema = tool_schema.get("inputSchema", {})
param_descriptions = extract_parameter_descriptions(input_schema)
self.__doc__ = build_docstring(description, param_descriptions)
# Convert MCP JSON Schema to Python type annotations
self.__annotations__ = mcp_schema_to_annotations(input_schema)
# Create a proper signature for inspect.signature() to read
# This allows aisuite's Tools class to introspect the parameters
self.__signature__ = self._create_signature(input_schema)
# Store the original MCP inputSchema for direct use by Tools class
# This avoids lossy round-trip conversion through Python type annotations
# and preserves all JSON Schema details (arrays, nested objects, etc.)
self.__mcp_input_schema__ = input_schema
def _create_signature(self, input_schema: Dict[str, Any]) -> inspect.Signature:
"""
Create a signature for this wrapper based on MCP tool schema.
This allows inspect.signature() to see the proper parameters with
type annotations, rather than just **kwargs.
"""
properties = input_schema.get("properties", {})
required = input_schema.get("required", [])
parameters = []
for param_name, annotation in self.__annotations__.items():
# Create parameter with annotation and default
if param_name in required:
# Required parameter (no default)
param = inspect.Parameter(
param_name,
inspect.Parameter.POSITIONAL_OR_KEYWORD,
annotation=annotation,
)
else:
# Optional parameter (with None default)
param = inspect.Parameter(
param_name,
inspect.Parameter.POSITIONAL_OR_KEYWORD,
default=None,
annotation=annotation,
)
parameters.append(param)
return inspect.Signature(parameters, return_annotation=Any)
def __call__(self, **kwargs) -> Any:
"""
Execute the MCP tool with the given arguments.
This method is called by aisuite's tool execution loop when the LLM
requests this tool.
Args:
**kwargs: Tool arguments as keyword arguments
Returns:
The result from the MCP tool execution
"""
# Filter out None values - only pass parameters that have actual values
# This prevents passing null to MCP tools that expect specific types
# (e.g., a tool expecting number won't accept null, it wants the param omitted)
filtered_kwargs = {k: v for k, v in kwargs.items() if v is not None}
# Call the MCP client's tool execution method
# The MCP client handles the async MCP protocol communication
return self.mcp_client.call_tool(self.tool_name, filtered_kwargs)
def __repr__(self) -> str:
"""Return a string representation of the wrapper."""
return f"MCPToolWrapper(name={self.tool_name!r})"
def create_mcp_tool_wrapper(
mcp_client: "MCPClient",
tool_name: str,
tool_schema: Dict[str, Any],
) -> Callable:
"""
Factory function to create an MCP tool wrapper.
Args:
mcp_client: The MCPClient instance
tool_name: Name of the tool
tool_schema: MCP tool schema
Returns:
Callable wrapper for the MCP tool
"""
return MCPToolWrapper(mcp_client, tool_name, tool_schema)
================================================
FILE: aisuite/provider.py
================================================
from abc import ABC, abstractmethod
from pathlib import Path
import importlib
import os
import functools
from typing import Union, BinaryIO, Optional
class LLMError(Exception):
"""Custom exception for LLM errors."""
def __init__(self, message):
super().__init__(message)
class ASRError(Exception):
"""Custom exception for ASR errors."""
def __init__(self, message):
super().__init__(message)
class Provider(ABC):
def __init__(self):
"""Initialize provider with optional audio functionality."""
self.audio: Optional[Audio] = None
@abstractmethod
def chat_completions_create(self, model, messages):
"""Abstract method for chat completion calls, to be implemented by each provider."""
pass
class ProviderFactory:
"""Factory to dynamically load provider instances based on naming conventions."""
PROVIDERS_DIR = Path(__file__).parent / "providers"
@classmethod
def create_provider(cls, provider_key, config):
"""Dynamically load and create an instance of a provider based on the naming convention."""
# Convert provider_key to the expected module and class names
provider_class_name = f"{provider_key.capitalize()}Provider"
provider_module_name = f"{provider_key}_provider"
module_path = f"aisuite.providers.{provider_module_name}"
# Lazily load the module
try:
module = importlib.import_module(module_path)
except ImportError as e:
raise ImportError(
f"Could not import module {module_path}: {str(e)}. Please ensure the provider is supported by doing ProviderFactory.get_supported_providers()"
)
# Instantiate the provider class
provider_class = getattr(module, provider_class_name)
return provider_class(**config)
@classmethod
@functools.cache
def get_supported_providers(cls):
"""List all supported provider names based on files present in the providers directory."""
provider_files = Path(cls.PROVIDERS_DIR).glob("*_provider.py")
return {file.stem.replace("_provider", "") for file in provider_files}
class Audio:
"""Base class for all audio functionality."""
def __init__(self):
self.transcriptions: Optional["Audio.Transcription"] = None
class Transcription(ABC):
"""Base class for audio transcription functionality."""
def create(
self,
model: str,
file: Union[str, BinaryIO],
options=None,
**kwargs,
):
"""Create audio transcription."""
raise NotImplementedError("Transcription not supported by this provider")
async def create_stream_output(
self,
model: str,
file: Union[str, BinaryIO],
options=None,
**kwargs,
):
"""Create streaming audio transcription."""
raise NotImplementedError(
"Streaming transcription not supported by this provider"
)
================================================
FILE: aisuite/providers/__init__.py
================================================
================================================
FILE: aisuite/providers/anthropic_provider.py
================================================
# Anthropic provider
# Links:
# Tool calling docs - https://docs.anthropic.com/en/docs/build-with-claude/tool-use
import anthropic
import json
from aisuite.provider import Provider
from aisuite.framework import ChatCompletionResponse
from aisuite.framework.message import (
Message,
ChatCompletionMessageToolCall,
Function,
CompletionUsage,
PromptTokensDetails,
)
# Define a constant for the default max_tokens value
DEFAULT_MAX_TOKENS = 4096
class AnthropicMessageConverter:
# Role constants
ROLE_USER = "user"
ROLE_ASSISTANT = "assistant"
ROLE_TOOL = "tool"
ROLE_SYSTEM = "system"
# Finish reason mapping
FINISH_REASON_MAPPING = {
"end_turn": "stop",
"max_tokens": "length",
"tool_use": "tool_calls",
}
def convert_request(self, messages):
"""Convert framework messages to Anthropic format."""
system_message = self._extract_system_message(messages)
converted_messages = [self._convert_single_message(msg) for msg in messages]
return system_message, converted_messages
def convert_response(self, response):
"""Normalize the response from the Anthropic API to match OpenAI's response format."""
normalized_response = ChatCompletionResponse()
normalized_response.choices[0].finish_reason = self._get_finish_reason(response)
normalized_response.usage = self._get_completion_usage(response)
normalized_response.choices[0].message = self._get_message(response)
return normalized_response
def _convert_single_message(self, msg):
"""Convert a single message to Anthropic format."""
if isinstance(msg, dict):
return self._convert_dict_message(msg)
return self._convert_message_object(msg)
def _convert_dict_message(self, msg):
"""Convert a dictionary message to Anthropic format."""
if msg["role"] == self.ROLE_TOOL:
return self._create_tool_result_message(msg["tool_call_id"], msg["content"])
elif msg["role"] == self.ROLE_ASSISTANT and "tool_calls" in msg:
return self._create_assistant_tool_message(
msg["content"], msg["tool_calls"]
)
return {"role": msg["role"], "content": msg["content"]}
def _convert_message_object(self, msg):
"""Convert a Message object to Anthropic format."""
if msg.role == self.ROLE_TOOL:
return self._create_tool_result_message(msg.tool_call_id, msg.content)
elif msg.role == self.ROLE_ASSISTANT and msg.tool_calls:
return self._create_assistant_tool_message(msg.content, msg.tool_calls)
return {"role": msg.role, "content": msg.content}
def _create_tool_result_message(self, tool_call_id, content):
"""Create a tool result message in Anthropic format."""
return {
"role": self.ROLE_USER,
"content": [
{
"type": "tool_result",
"tool_use_id": tool_call_id,
"content": content,
}
],
}
def _create_assistant_tool_message(self, content, tool_calls):
"""Create an assistant message with tool calls in Anthropic format."""
message_content = []
if content:
message_content.append({"type": "text", "text": content})
for tool_call in tool_calls:
tool_input = (
tool_call["function"]["arguments"]
if isinstance(tool_call, dict)
else tool_call.function.arguments
)
message_content.append(
{
"type": "tool_use",
"id": (
tool_call["id"] if isinstance(tool_call, dict) else tool_call.id
),
"name": (
tool_call["function"]["name"]
if isinstance(tool_call, dict)
else tool_call.function.name
),
"input": json.loads(tool_input),
}
)
return {"role": self.ROLE_ASSISTANT, "content": message_content}
def _extract_system_message(self, messages):
"""Extract system message if present, otherwise return empty list."""
# TODO: This is a temporary solution to extract the system message.
# User can pass multiple system messages, which can mingled with other messages.
# This needs to be fixed to handle this case.
if messages and messages[0]["role"] == "system":
system_message = messages[0]["content"]
messages.pop(0)
return system_message
return []
def _get_finish_reason(self, response):
"""Get the normalized finish reason."""
return self.FINISH_REASON_MAPPING.get(response.stop_reason, "stop")
def _get_completion_usage(self, response):
"""Get the usage statistics."""
return CompletionUsage(
completion_tokens=response.usage.output_tokens,
prompt_tokens=response.usage.input_tokens,
total_tokens=response.usage.input_tokens + response.usage.output_tokens,
prompt_tokens_details=PromptTokensDetails(
cached_tokens=response.usage.cache_read_input_tokens,
),
)
def _get_message(self, response):
"""Get the appropriate message based on response type."""
# Check if response contains any tool use blocks (regardless of stop_reason)
has_tool_use = any(content.type == "tool_use" for content in response.content)
if has_tool_use:
tool_message = self.convert_response_with_tool_use(response)
if tool_message:
return tool_message
# Safely extract text content from any position in content blocks
text_content = next(
(content.text for content in response.content if content.type == "text"),
"",
)
return Message(
content=text_content or None,
role="assistant",
tool_calls=None,
refusal=None,
)
def convert_response_with_tool_use(self, response):
"""Convert Anthropic tool use response to the framework's format."""
tool_call = next(
(content for content in response.content if content.type == "tool_use"),
None,
)
if tool_call:
function = Function(
name=tool_call.name, arguments=json.dumps(tool_call.input)
)
tool_call_obj = ChatCompletionMessageToolCall(
id=tool_call.id, function=function, type="function"
)
text_content = next(
(
content.text
for content in response.content
if content.type == "text"
),
"",
)
return Message(
content=text_content or None,
tool_calls=[tool_call_obj] if tool_call else None,
role="assistant",
refusal=None,
)
return None
def convert_tool_spec(self, openai_tools):
"""Convert OpenAI tool specification to Anthropic format."""
anthropic_tools = []
for tool in openai_tools:
if tool.get("type") != "function":
continue
function = tool["function"]
anthropic_tool = {
"name": function["name"],
"description": function["description"],
"input_schema": {
"type": "object",
"properties": function["parameters"]["properties"],
"required": function["parameters"].get("required", []),
},
}
anthropic_tools.append(anthropic_tool)
return anthropic_tools
class AnthropicProvider(Provider):
def __init__(self, **config):
"""Initialize the Anthropic provider with the given configuration."""
self.client = anthropic.Anthropic(**config)
self.converter = AnthropicMessageConverter()
def chat_completions_create(self, model, messages, **kwargs):
"""Create a chat completion using the Anthropic API."""
kwargs = self._prepare_kwargs(kwargs)
system_message, converted_messages = self.converter.convert_request(messages)
response = self.client.messages.create(
model=model, system=system_message, messages=converted_messages, **kwargs
)
return self.converter.convert_response(response)
def _prepare_kwargs(self, kwargs):
"""Prepare kwargs for the API call."""
kwargs = kwargs.copy()
kwargs.setdefault("max_tokens", DEFAULT_MAX_TOKENS)
if "tools" in kwargs:
kwargs["tools"] = self.converter.convert_tool_spec(kwargs["tools"])
return kwargs
================================================
FILE: aisuite/providers/aws_provider.py
================================================
"""AWS Bedrock provider for the aisuite."""
import os
import json
from typing import List, Dict, Any, Tuple, Optional
import boto3
import botocore
from aisuite.provider import Provider, LLMError
from aisuite.framework import ChatCompletionResponse
from aisuite.framework.message import Message, CompletionUsage
# pylint: disable=too-few-public-methods
class BedrockConfig:
"""Configuration for the AWS Bedrock provider."""
INFERENCE_PARAMETERS = ["maxTokens", "temperature", "topP", "stopSequences"]
def __init__(self, **config):
"""Initialize the BedrockConfig."""
self.region_name = config.get(
"region_name", os.getenv("AWS_REGION", "us-west-2")
)
def create_client(self):
"""Create a Bedrock runtime client."""
return boto3.client("bedrock-runtime", region_name=self.region_name)
# AWS Bedrock API Example -
# https://docs.aws.amazon.com/bedrock/latest/userguide/tool-use-inference-call.html
# https://docs.aws.amazon.com/bedrock/latest/userguide/tool-use-examples.html
class BedrockMessageConverter:
"""Converts messages between OpenAI and AWS Bedrock formats."""
@staticmethod
def convert_request(
messages: List[Dict[str, Any]],
) -> Tuple[List[Dict], List[Dict]]:
"""Convert messages to AWS Bedrock format."""
# Convert all messages to dicts if they're Message objects
messages = [
message.model_dump() if hasattr(message, "model_dump") else message
for message in messages
]
# Handle system message
system_message = []
if messages and messages[0]["role"] == "system":
system_message = [{"text": messages[0]["content"]}]
messages = messages[1:]
formatted_messages = []
for message in messages:
# Skip any additional system messages
if message["role"] == "system":
continue
if message["role"] == "tool":
bedrock_message = BedrockMessageConverter.convert_tool_result(message)
if bedrock_message:
formatted_messages.append(bedrock_message)
elif message["role"] == "assistant":
bedrock_message = BedrockMessageConverter.convert_assistant(message)
if bedrock_message:
formatted_messages.append(bedrock_message)
else: # user messages
formatted_messages.append(
{
"role": message["role"],
"content": [{"text": message["content"]}],
}
)
return system_message, formatted_messages
@staticmethod
def convert_response_tool_call(
response: Dict[str, Any],
) -> Optional[Dict[str, Any]]:
"""Convert AWS Bedrock tool call response to OpenAI format."""
if response.get("stopReason") != "tool_use":
return None
tool_calls = []
for content in response["output"]["message"]["content"]:
if "toolUse" in content:
tool = content["toolUse"]
tool_calls.append(
{
"type": "function",
"id": tool["toolUseId"],
"function": {
"name": tool["name"],
"arguments": json.dumps(tool["input"]),
},
}
)
if not tool_calls:
return None
return {
"role": "assistant",
"content": None,
"tool_calls": tool_calls,
"refusal": None,
}
@staticmethod
def convert_tool_result(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Convert OpenAI tool result format to AWS Bedrock format."""
if message["role"] != "tool" or "content" not in message:
return None
tool_call_id = message.get("tool_call_id")
if not tool_call_id:
raise LLMError("Tool result message must include tool_call_id")
try:
content_json = json.loads(message["content"])
content = [{"json": content_json}]
except json.JSONDecodeError:
content = [{"text": message["content"]}]
return {
"role": "user",
"content": [
{"toolResult": {"toolUseId": tool_call_id, "content": content}}
],
}
@staticmethod
def convert_assistant(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Convert OpenAI assistant format to AWS Bedrock format."""
if message["role"] != "assistant":
return None
content = []
if message.get("content"):
content.append({"text": message["content"]})
if message.get("tool_calls"):
for tool_call in message["tool_calls"]:
if tool_call["type"] == "function":
try:
input_json = json.loads(tool_call["function"]["arguments"])
except json.JSONDecodeError:
input_json = tool_call["function"]["arguments"]
content.append(
{
"toolUse": {
"toolUseId": tool_call["id"],
"name": tool_call["function"]["name"],
"input": input_json,
}
}
)
return {"role": "assistant", "content": content} if content else None
@staticmethod
def convert_response(response: Dict[str, Any]) -> ChatCompletionResponse:
"""Normalize the response from the Bedrock API to match OpenAI's response format."""
norm_response = ChatCompletionResponse()
# Check if the model is requesting tool use
if response.get("stopReason") == "tool_use":
tool_message = BedrockMessageConverter.convert_response_tool_call(response)
if tool_message:
norm_response.choices[0].message = Message(**tool_message)
norm_response.choices[0].finish_reason = "tool_calls"
return norm_response
# Handle regular text response
norm_response.choices[0].message.content = response["output"]["message"][
"content"
][0]["text"]
# Map Bedrock stopReason to OpenAI finish_reason
stop_reason = response.get("stopReason")
if stop_reason == "complete":
norm_response.choices[0].finish_reason = "stop"
elif stop_reason == "max_tokens":
norm_response.choices[0].finish_reason = "length"
else:
norm_response.choices[0].finish_reason = stop_reason
# Conditionally parse usage data if it exists.
if usage_data := response.get("usage"):
norm_response.usage = BedrockMessageConverter.get_completion_usage(
usage_data
)
return norm_response
@staticmethod
def get_completion_usage(usage_data: dict):
"""Get the usage statistics from a usage data dictionary."""
return CompletionUsage(
completion_tokens=usage_data.get("outputTokens"),
prompt_tokens=usage_data.get("inputTokens"),
total_tokens=usage_data.get("totalTokens"),
)
class AwsProvider(Provider):
"""Provider for AWS Bedrock."""
def __init__(self, **config):
"""Initialize the AWS Bedrock provider with the given configuration."""
self.config = BedrockConfig(**config)
self.client = self.config.create_client()
self.transformer = BedrockMessageConverter()
def convert_response(self, response: Dict[str, Any]) -> ChatCompletionResponse:
"""Normalize the response from the Bedrock API to match OpenAI's response format."""
return self.transformer.convert_response(response)
def _convert_tool_spec(self, kwargs: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Convert tool specifications to Bedrock format."""
if "tools" not in kwargs:
return None
tool_config = {
"tools": [
{
"toolSpec": {
"name": tool["function"]["name"],
"description": tool["function"].get("description", " "),
"inputSchema": {"json": tool["function"]["parameters"]},
}
}
for tool in kwargs["tools"]
]
}
return tool_config
def _prepare_request_config(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
"""Prepare the configuration for the Bedrock API request."""
# Convert tools and remove from kwargs
tool_config = self._convert_tool_spec(kwargs)
kwargs.pop("tools", None) # Remove tools from kwargs if present
inference_config = {
key: kwargs[key]
for key in BedrockConfig.INFERENCE_PARAMETERS
if key in kwargs
}
additional_fields = {
key: value
for key, value in kwargs.items()
if key not in BedrockConfig.INFERENCE_PARAMETERS
}
request_config = {
"inferenceConfig": inference_config,
"additionalModelRequestFields": additional_fields,
}
if tool_config is not None:
request_config["toolConfig"] = tool_config
return request_config
def chat_completions_create(
self, model: str, messages: List[Dict[str, Any]], **kwargs
) -> ChatCompletionResponse:
"""Create a chat completion request to AWS Bedrock."""
system_message, formatted_messages = self.transformer.convert_request(messages)
request_config = self._prepare_request_config(kwargs)
try:
response = self.client.converse(
modelId=model,
messages=formatted_messages,
system=system_message,
**request_config,
)
except botocore.exceptions.ClientError as e:
if e.response["Error"]["Code"] == "ValidationException":
error_message = e.response["Error"]["Message"]
raise LLMError(error_message) from e
raise
return self.convert_response(response)
================================================
FILE: aisuite/providers/azure_provider.py
================================================
import urllib.request
import json
import os
from aisuite.provider import Provider
from aisuite.framework import ChatCompletionResponse
from aisuite.framework.message import Message, ChatCompletionMessageToolCall, Function
# Azure provider is based on the documentation here -
# https://learn.microsoft.com/en-us/azure/machine-learning/reference-model-inference-api?view=azureml-api-2&source=recommendations&tabs=python
# Azure AI Model Inference API is used.
# From the documentation -
# """
# The Azure AI Model Inference is an API that exposes a common set of capabilities for foundational models
# and that can be used by developers to consume predictions from a diverse set of models in a uniform and consistent way.
# Developers can talk with different models deployed in Azure AI Foundry portal without changing the underlying code they are using.
#
# The Azure AI Model Inference API is available in the following models:
#
# Models deployed to serverless API endpoints:
# Cohere Embed V3 family of models
# Cohere Command R family of models
# Meta Llama 2 chat family of models
# Meta Llama 3 instruct family of models
# Mistral-Small
# Mistral-Large
# Jais family of models
# Jamba family of models
# Phi-3 family of models
#
# Models deployed to managed inference:
# Meta Llama 3 instruct family of models
# Phi-3 family of models
# Mixtral famility of models
#
# The API is compatible with Azure OpenAI model deployments.
# """
class AzureMessageConverter:
@staticmethod
def convert_request(messages):
"""Convert messages to Azure format."""
transformed_messages = []
for message in messages:
if isinstance(message, Message):
transformed_messages.append(message.model_dump(mode="json"))
else:
transformed_messages.append(message)
return transformed_messages
@staticmethod
def convert_response(resp_json) -> ChatCompletionResponse:
"""Normalize the response from the Azure API to match OpenAI's response format."""
completion_response = ChatCompletionResponse()
choice = resp_json["choices"][0]
message = choice["message"]
# Set basic message content
completion_response.choices[0].message.content = message.get("content")
completion_response.choices[0].message.role = message.get("role", "assistant")
# Handle tool calls if present
if "tool_calls" in message and message["tool_calls"] is not None:
tool_calls = []
for tool_call in message["tool_calls"]:
new_tool_call = ChatCompletionMessageToolCall(
id=tool_call["id"],
type=tool_call["type"],
function={
"name": tool_call["function"]["name"],
"arguments": tool_call["function"]["arguments"],
},
)
tool_calls.append(new_tool_call)
completion_response.choices[0].message.tool_calls = tool_calls
return completion_response
class AzureProvider(Provider):
def __init__(self, **config):
self.base_url = config.get("base_url") or os.getenv("AZURE_BASE_URL")
self.api_key = config.get("api_key") or os.getenv("AZURE_API_KEY")
self.api_version = config.get("api_version") or os.getenv("AZURE_API_VERSION")
if not self.api_key:
raise ValueError("For Azure, api_key is required.")
if not self.base_url:
raise ValueError(
"For Azure, base_url is required. Check your deployment page for a URL like this - https://<model-deployment-name>.<region>.models.ai.azure.com"
)
self.transformer = AzureMessageConverter()
def chat_completions_create(self, model, messages, **kwargs):
url = f"{self.base_url}/chat/completions"
if self.api_version:
url = f"{url}?api-version={self.api_version}"
# Remove 'stream' from kwargs if present
kwargs.pop("stream", None)
# Transform messages using converter
transformed_messages = self.transformer.convert_request(messages)
# Prepare the request payload
data = {"messages": transformed_messages}
# Add tools if provided
if "tools" in kwargs:
data["tools"] = kwargs["tools"]
kwargs.pop("tools")
# Add tool_choice if provided
if "tool_choice" in kwargs:
data["tool_choice"] = kwargs["tool_choice"]
kwargs.pop("tool_choice")
# Add remaining kwargs
data.update(kwargs)
body = json.dumps(data).encode("utf-8")
headers = {"Content-Type": "application/json", "Authorization": self.api_key}
try:
req = urllib.request.Request(url, body, headers)
with urllib.request.urlopen(req) as response:
result = response.read()
resp_json = json.loads(result)
return self.transformer.convert_response(resp_json)
except urllib.error.HTTPError as error:
error_message = f"The request failed with status code: {error.code}\n"
error_message += f"Headers: {error.info()}\n"
error_message += error.read().decode("utf-8", "ignore")
raise Exception(error_message)
================================================
FILE: aisuite/providers/cerebras_provider.py
================================================
"""Cerebras provider for the aisuite."""
import cerebras.cloud.sdk as cerebras
from aisuite.provider import Provider, LLMError
from aisuite.providers.message_converter import OpenAICompliantMessageConverter
class CerebrasMessageConverter(OpenAICompliantMessageConverter):
"""
Cerebras-specific message converter if needed.
"""
# pylint: disable=too-few-public-methods
class CerebrasProvider(Provider):
"""Provider for Cerebras."""
def __init__(self, **config):
self.client = cerebras.Cerebras(**config)
self.transformer = CerebrasMessageConverter()
def chat_completions_create(self, model, messages, **kwargs):
"""
Makes a request to the Cerebras chat completions endpoint using the official client.
"""
try:
response = self.client.chat.completions.create(
model=model,
messages=messages,
**kwargs, # Pass any additional arguments to the Cerebras API.
)
return self.transformer.convert_response(response.model_dump())
# Re-raise Cerebras API-specific exceptions.
except cerebras.PermissionDeniedError:
raise
except cerebras.AuthenticationError:
raise
except cerebras.RateLimitError:
raise
# Wrap all other exceptions in LLMError.
except Exception as e:
raise LLMError(f"An error occurred: {e}") from e
================================================
FILE: aisuite/providers/cohere_provider.py
================================================
import os
import cohere
import json
from aisuite.framework import ChatCompletionResponse
from aisuite.framework.message import Message, ChatCompletionMessageToolCall, Function
from aisuite.provider import Provider, LLMError
class CohereMessageConverter:
"""
Cohere-specific message converter
"""
def convert_request(self, messages):
"""Convert framework messages to Cohere format."""
converted_messages = []
for message in messages:
if isinstance(message, dict):
role = message.get("role")
content = message.get("content")
tool_calls = message.get("tool_calls")
tool_plan = message.get("tool_plan")
else:
role = message.role
content = message.content
tool_calls = message.tool_calls
tool_plan = getattr(message, "tool_plan", None)
# Convert to Cohere's format
if role == "tool":
# Handle tool response messages
converted_message = {
"role": role,
"tool_call_id": (
message.get("tool_call_id")
if isinstance(message, dict)
else message.tool_call_id
),
"content": self._convert_tool_content(content),
}
elif role == "assistant" and tool_calls:
# Handle assistant messages with tool calls
converted_message = {
"role": role,
"tool_calls": [
{
"id": tc.id if not isinstance(tc, dict) else tc["id"],
"function": {
"name": (
tc.function.name
if not isinstance(tc, dict)
else tc["function"]["name"]
),
"arguments": (
tc.function.arguments
if not isinstance(tc, dict)
else tc["function"]["arguments"]
),
},
"type": "function",
}
for tc in tool_calls
],
"tool_plan": tool_plan,
}
if content:
converted_message["content"] = content
else:
# Handle regular messages
converted_message = {"role": role, "content": content}
converted_messages.append(converted_message)
return converted_messages
def _convert_tool_content(self, content):
"""Convert tool response content to Cohere's expected format."""
if isinstance(content, str):
try:
# Try to parse as JSON first
data = json.loads(content)
return [{"type": "document", "document": {"data": json.dumps(data)}}]
except json.JSONDecodeError:
# If not JSON, return as plain text
return content
elif isinstance(content, list):
# If content is already in Cohere's format, return as is
return content
else:
# For other types, convert to string
return str(content)
@staticmethod
def convert_response(response_data) -> ChatCompletionResponse:
"""Convert Cohere's response to our standard format."""
normalized_response = ChatCompletionResponse()
# Set usage information
normalized_response.usage = {
"prompt_tokens": response_data.usage.tokens.input_tokens,
"completion_tokens": response_data.usage.tokens.output_tokens,
"total_tokens": response_data.usage.tokens.input_tokens
+ response_data.usage.tokens.output_tokens,
}
# Handle tool calls
if response_data.finish_reason == "TOOL_CALL":
tool_call = response_data.message.tool_calls[0]
function = Function(
name=tool_call.function.name, arguments=tool_call.function.arguments
)
tool_call_obj = ChatCompletionMessageToolCall(
id=tool_call.id, function=function, type="function"
)
normalized_response.choices[0].message = Message(
content=response_data.message.tool_plan, # Use tool_plan as content
tool_calls=[tool_call_obj],
role="assistant",
refusal=None,
)
normalized_response.choices[0].finish_reason = "tool_calls"
else:
# Handle regular text response
normalized_response.choices[0].message.content = (
response_data.message.content[0].text
)
normalized_response.choices[0].finish_reason = "stop"
return normalized_response
class CohereProvider(Provider):
def __init__(self, **config):
"""
Initialize the Cohere provider with the given configuration.
Pass the entire configuration dictionary to the Cohere client constructor.
"""
# Ensure API key is provided either in config or via environment variable
config.setdefault("api_key", os.getenv("CO_API_KEY"))
if not config["api_key"]:
raise ValueError(
"Cohere API key is missing. Please provide it in the config or set the CO_API_KEY environment variable."
)
self.client = cohere.ClientV2(**config)
self.transformer = CohereMessageConverter()
def chat_completions_create(self, model, messages, **kwargs):
"""
Makes a request to Cohere using the official client.
"""
try:
# Transform messages using converter
transformed_messages = self.transformer.convert_request(messages)
# Make the request to Cohere
response = self.client.chat(
model=model, messages=transformed_messages, **kwargs
)
return self.transformer.convert_response(response)
except Exception as e:
raise LLMError(f"An error occurred: {e}")
================================================
FILE: aisuite/providers/deepgram_provider.py
================================================
import os
import json
import numpy as np
import queue
import threading
import time
from typing import Union, BinaryIO, AsyncGenerator
from aisuite.provider import Provider, ASRError, Audio
from aisuite.framework.message import (
TranscriptionResult,
Segment,
Word,
Alternative,
Channel,
StreamingTranscriptionChunk,
)
class DeepgramProvider(Provider):
"""Deepgram ASR provider."""
def __init__(self, **config):
"""Initialize the Deepgram provider with the given configuration."""
super().__init__()
# Ensure API key is provided either in config or via environment variable
self.api_key = config.get("api_key") or os.getenv("DEEPGRAM_API_KEY")
if not self.api_key:
raise ValueError(
"Deepgram API key is missing. Please provide it in the config or set the DEEPGRAM_API_KEY environment variable."
)
# Initialize Deepgram client (v5.0.0+)
try:
from deepgram import DeepgramClient
self.client = DeepgramClient(api_key=self.api_key)
except ImportError:
raise ImportError(
"Deepgram SDK is required. Install it with: pip install deepgram-sdk"
)
# Initialize audio functionality
self.audio = DeepgramAudio(self.client)
def chat_completions_create(self, model, messages):
"""Deepgram does not support chat completions."""
raise NotImplementedError(
"Deepgram provider only supports audio transcription, not chat completions."
)
# Audio Classes
class DeepgramAudio(Audio):
"""Deepgram Audio functionality container."""
def __init__(self, client):
super().__init__()
self.transcriptions = self.Transcriptions(client)
class Transcriptions(Audio.Transcription):
"""Deepgram Audio Transcriptions functionality."""
def __init__(self, client):
self.client = client
def create(
self,
model: str,
file: Union[str, BinaryIO],
**kwargs,
) -> TranscriptionResult:
"""
Create audio transcription using Deepgram SDK v5.
All parameters are already validated and mapped by the Client layer.
This is a simple pass-through to the Deepgram API.
"""
try:
# Add model to params and set defaults
kwargs["model"] = model
kwargs.setdefault("smart_format", True)
kwargs.setdefault("punctuate", True)
kwargs.setdefault("language", "en")
# Get audio bytes
audio_bytes = self._prepare_audio_payload(file)
# Use v5 API: client.listen.v1.media.transcribe_file()
# All parameters passed as kwargs, no PrerecordedOptions needed
response = self.client.listen.v1.media.transcribe_file(
request=audio_bytes, **kwargs
)
# Convert Pydantic model to dict (v5 uses Pydantic v2)
if hasattr(response, "model_dump"):
response_dict = response.model_dump()
elif hasattr(response, "to_dict"):
response_dict = response.to_dict()
elif hasattr(response, "dict"):
response_dict = response.dict()
else:
response_dict = response
return self._parse_deepgram_response(response_dict)
except Exception as e:
raise ASRError(f"Deepgram transcription error: {e}") from e
async def create_stream_output(
self,
model: str,
file: Union[str, BinaryIO],
chunk_size_minutes: float = 3.0,
**kwargs,
) -> AsyncGenerator[StreamingTranscriptionChunk, None]:
"""
Create streaming audio transcription using Deepgram SDK v5 with chunked processing.
All parameters are already validated and mapped by the Client layer.
This implementation handles audio chunking and streaming.
"""
try:
# Load and prepare audio
audio_data, sample_rate = await self._load_and_prepare_audio(file)
# Calculate chunking strategy
duration_seconds = len(audio_data) / sample_rate
chunk_duration_seconds = chunk_size_minutes * 60
if duration_seconds <= chunk_duration_seconds:
chunks = [audio_data]
else:
chunk_size_samples = int(chunk_duration_seconds * sample_rate)
chunks = []
num_chunks = int(np.ceil(duration_seconds / chunk_duration_seconds))
for i in range(num_chunks):
start_sample = i * chunk_size_samples
end_sample = min(
start_sample + chunk_size_samples, len(audio_data)
)
chunks.append(audio_data[start_sample:end_sample])
# Setup API parameters for v5
kwargs["model"] = model
kwargs.setdefault("smart_format", "true")
kwargs.setdefault("punctuate", "true")
kwargs.setdefault("language", "en")
kwargs["interim_results"] = (
"true" # Enable interim results for streaming
)
# Remove parameters not supported by streaming
kwargs.pop("utterances", None)
# Add critical audio format parameters (as strings for v5)
kwargs["encoding"] = "linear16" # PCM16 format
kwargs["sample_rate"] = "16000" # Match our target sample rate
kwargs["channels"] = "1" # Mono audio
# Use thread-safe queue for cross-thread communication
transcript_queue = queue.Queue()
connection_closed = threading.Event()
def on_message(*args, **message_kwargs):
"""Handle transcript events"""
# Extract result from args or kwargs
result = None
if len(args) >= 2:
result = args[1]
elif "result" in message_kwargs:
result = message_kwargs["result"]
else:
return
if hasattr(result, "channel") and result.channel.alternatives:
alt = result.channel.alternatives[0]
if alt.transcript:
chunk = StreamingTranscriptionChunk(
text=alt.transcript,
is_final=getattr(result, "is_final", False),
confidence=getattr(alt, "confidence", None),
)
transcript_queue.put(chunk)
def on_error(*args, **error_kwargs):
"""Handle error events"""
error = None
if len(args) >= 2:
error = args[1]
elif "error" in error_kwargs:
error = error_kwargs["error"]
if error:
transcript_queue.put(
ASRError(f"Deepgram streaming error: {error}")
)
def on_close(*args, **close_kwargs):
"""Handle connection close events"""
connection_closed.set()
# Use v5 streaming API with context manager
from deepgram.core.events import EventType
async with self.client.listen.v1.connect(**kwargs) as connection:
# Register event handlers
connection.on(EventType.Transcript, on_message)
connection.on(EventType.Error, on_error)
connection.on(EventType.Close, on_close)
# Send all chunks through connection
for audio_chunk in chunks:
self._send_audio_chunk(connection, audio_chunk)
# Send CloseStream message to signal end
close_stream_message = json.dumps({"type": "CloseStream"})
connection.send(close_stream_message)
# Yield results until connection closes
while not connection_closed.is_set():
try:
chunk = transcript_queue.get(timeout=0.1)
if isinstance(chunk, Exception):
raise chunk
yield chunk
except queue.Empty:
continue
# Get any remaining results
while not transcript_queue.empty():
try:
chunk = transcript_queue.get_nowait()
if isinstance(chunk, Exception):
raise chunk
yield chunk
except queue.Empty:
break
except Exception as e:
raise ASRError(f"Deepgram streaming transcription error: {e}")
def _prepare_audio_payload(self, file: Union[str, BinaryIO]) -> bytes:
"""Prepare audio payload for Deepgram API v5.
Returns raw bytes instead of dict payload (v5 API change).
"""
if isinstance(file, str):
with open(file, "rb") as audio_file:
buffer_data = audio_file.read()
else:
if hasattr(file, "read"):
buffer_data = file.read()
else:
raise ValueError(
"File must be a file path string or file-like object"
)
return buffer_data
async def _load_and_prepare_audio(
self, file: Union[str, BinaryIO]
) -> tuple[np.ndarray, int]:
"""Load and prepare audio file for streaming.
Conversions performed only when necessary:
- Stereo to mono: Required for multi-channel audio
- Sample rate conversion: Required when input != 16kHz
- Other formats: Error out as unsupported
"""
try:
try:
import soundfile as sf
except ImportError:
raise ASRError(
"soundfile is required for audio processing. Install with: pip install soundfile"
)
if isinstance(file, str):
audio_data, original_sample_rate = sf.read(file)
else:
audio_data, original_sample_rate = sf.read(file)
audio_data = np.asarray(audio_data, dtype=np.float32)
# Convert to mono if stereo
if len(audio_data.shape) > 1:
if audio_data.shape[1] == 2:
audio_data = np.mean(audio_data, axis=1)
else:
raise ASRError(
f"Unsupported audio format: {audio_data.shape[1]} channels. Only mono and stereo are supported."
)
# Resample to 16kHz if needed
target_sample_rate = 16000
if original_sample_rate != target_sample_rate:
try:
from scipy import signal
num_samples = int(
len(audio_data) * target_sample_rate / original_sample_rate
)
audio_data = signal.resample(audio_data, num_samples)
except ImportError:
raise ASRError(
f"Audio resampling required but scipy not available. "
f"Input is {original_sample_rate}Hz, need {target_sample_rate}Hz. "
f"Install scipy or provide audio at {target_sample_rate}Hz."
)
return np.asarray(audio_data, dtype=np.float32), target_sample_rate
except Exception as e:
if isinstance(e, ASRError):
raise
raise ASRError(f"Error loading audio file: {e}")
def _send_audio_chunk(self, connection, audio_chunk: np.ndarray) -> None:
"""Send audio chunk data through the connection."""
streaming_chunk_size = 8000 # Match reference BLOCKSIZE (~0.5s @16kHz mono)
send_delay = 0.01
for i in range(0, len(audio_chunk), streaming_chunk_size):
piece = audio_chunk[i : i + streaming_chunk_size]
if len(piece) < streaming_chunk_size:
piece = np.pad(
piece, (0, streaming_chunk_size - len(piece)), mode="constant"
)
pcm16 = (piece * 32767).astype(np.int16).tobytes()
connection.send(pcm16)
time.sleep(send_delay) # Use synchronous sleep like reference
def _parse_deepgram_response(self, response_dict: dict) -> TranscriptionResult:
"""Convert Deepgram API response to unified TranscriptionResult."""
try:
results = response_dict.get("results", {})
channels = results.get("channels", [])
if not channels or not channels[0].get("alternatives"):
return TranscriptionResult(
text="", language=None, confidence=None, task="transcribe"
)
best_alternative = channels[0]["alternatives"][0]
text = best_alternative.get("transcript", "")
confidence = best_alternative.get("confidence", None)
words = [
Word(
word=word_data.get("word", ""),
start=word_data.get("start", None),
end=word_data.get("end", None),
confidence=word_data.get("confidence", None),
)
for word_data in best_alternative.get("words", [])
]
segments = []
paragraphs = results.get("paragraphs", {}).get("paragraphs", [])
for para in paragraphs:
for sentence in para.get("sentences", []):
segments.append(
Segment(
id=len(segments),
seek=0,
start=sentence.get("start", None),
end=sentence.get("end", None),
text=sentence.get("text", ""),
tokens=[],
temperature=0.0,
avg_logprob=0.0,
compression_ratio=0.0,
no_speech_prob=0.0,
)
)
alternatives_list = [
Alternative(
transcript=alt.get("transcript", ""),
confidence=alt.get("confidence", None),
)
for alt in channels[0]["alternatives"][1:]
]
channels_list = [
Channel(
alternatives=[
Alternative(
transcript=alt.get("transcript", ""),
confidence=alt.get("confidence", None),
)
for alt in channel.get("alternatives", [])
]
)
for channel in channels
]
metadata = response_dict.get("metadata", {})
return TranscriptionResult(
text=text,
language=results.get("language", None),
confidence=confidence,
task="transcribe",
duration=metadata.get("duration", None) if metadata else None,
segments=segments or None,
words=words or None,
channels=channels_list or None,
alternatives=alternatives_list or None,
utterances=results.get("utterances", []),
paragraphs=results.get("paragraphs", None),
topics=results.get("topics", []),
intents=results.get("intents", []),
sentiment=results.get("sentiment", None),
summary=results.get("summary", None),
metadata=metadata,
)
except (KeyError, TypeError, IndexError) as e:
raise ASRError(f"Error parsing Deepgram response: {e}")
================================================
FILE: aisuite/providers/deepseek_provider.py
================================================
"""Deepseek provider for the aisuite."""
import os
import openai
from aisuite.provider import Provider, LLMError
from aisuite.providers.message_converter import OpenAICompliantMessageConverter
# pylint: disable=too-few-public-methods
class DeepseekProvider(Provider):
"""Provider for Deepseek."""
def __init__(self, **config):
"""
Initialize the DeepSeek provider with the given configuration.
Pass the entire configuration dictionary to the OpenAI client constructor.
"""
# Ensure API key is provided either in config or via environment variable
config.setdefault("api_key", os.getenv("DEEPSEEK_API_KEY"))
if not config["api_key"]:
raise ValueError(
"DeepSeek API key is missing. Please provide it in the config or "
"set the OPENAI_API_KEY environment variable."
)
config["base_url"] = "https://api.deepseek.com"
# NOTE: We could choose to remove above lines for api_key since OpenAI will automatically
# infer certain values from the environment variables.
# Eg: OPENAI_API_KEY, OPENAI_ORG_ID, OPENAI_PROJECT_ID. Except for
# OPEN_AI_BASE_URL which has to be the deepseek url
# Pass the entire config to the OpenAI client constructor
self.client = openai.OpenAI(**config)
# Using OpenAICompliantMessageConverter since DeepSeek's response format is
# the same as OpenAI's.
self.transformer = OpenAICompliantMessageConverter()
def chat_completions_create(self, model, messages, **kwargs):
# Any exception raised by OpenAI will be returned to the caller.
# Maybe we should catch them and raise a custom LLMError.
try:
response = self.client.chat.completions.create(
model=model,
messages=messages,
**kwargs, # Pass any additional arguments to the OpenAI API
)
return self.transformer.convert_response(response.model_dump())
except Exception as e:
raise LLMError(f"An error occurred: {e}") from e
================================================
FILE: aisuite/providers/fireworks_provider.py
================================================
import os
import httpx
import json
from aisuite.provider import Provider, LLMError
from aisuite.framework import ChatCompletionResponse
from aisuite.framework.message import Message, ChatCompletionMessageToolCall
class FireworksMessageConverter:
@staticmethod
def convert_request(messages):
"""Convert messages to Fireworks format."""
transformed_messages = []
for message in messages:
if isinstance(message, Message):
message_dict = message.model_dump(mode="json")
message_dict.pop("refusal", None) # Remove refusal field if present
transformed_messages.append(message_dict)
else:
transformed_messages.append(message)
return transformed_messages
@staticmethod
def convert_response(resp_json) -> ChatCompletionResponse:
"""Normalize the response from the Fireworks API to match OpenAI's response format."""
completion_response = ChatCompletionResponse()
choice = resp_json["choices"][0]
message = choice["message"]
# Set basic message content
completion_response.choices[0].message.content = message.get("content")
completion_response.choices[0].message.role = message.get("role", "assistant")
# Handle tool calls if present
if "tool_calls" in message and message["tool_calls"] is not None:
tool_calls = []
for tool_call in message["tool_calls"]:
new_tool_call = ChatCompletionMessageToolCall(
id=tool_call["id"],
type=tool_call["type"],
function={
"name": tool_call["function"]["name"],
"arguments": tool_call["function"]["arguments"],
},
)
tool_calls.append(new_tool_call)
completion_response.choices[0].message.tool_calls = tool_calls
return completion_response
# Models that support tool calls:
# [As of 01/20/2025 from https://docs.fireworks.ai/guides/function-calling]
# Llama 3.1 405B Instruct
# Llama 3.1 70B Instruct
# Qwen 2.5 72B Instruct
# Mixtral MoE 8x22B Instruct
# Firefunction-v2: Latest and most performant model, optimized for complex function calling scenarios (on-demand only)
# Firefunction-v1: Previous generation, Mixtral-based function calling model optimized for fast routing and structured output (on-demand only)
class FireworksProvider(Provider):
"""
Fireworks AI Provider using httpx for direct API calls.
"""
BASE_URL = "https://api.fireworks.ai/inference/v1/chat/completions"
def __init__(self, **config):
"""
Initialize the Fireworks provider with the given configuration.
The API key is fetched from the config or environment variables.
"""
self.api_key = config.get("api_key", os.getenv("FIREWORKS_API_KEY"))
if not self.api_key:
raise ValueError(
"Fireworks API key is missing. Please provide it in the config or set the FIREWORKS_API_KEY environment variable."
)
# Optionally set a custom timeout (default to 30s)
self.timeout = config.get("timeout", 30)
self.transformer = FireworksMessageConverter()
def chat_completions_create(self, model, messages, **kwargs):
"""
Makes a request to the Fireworks AI chat completions endpoint using httpx.
"""
# Remove 'stream' from kwargs if present
kwargs.pop("stream", None)
# Transform messages using converter
transformed_messages = self.transformer.convert_request(messages)
# Prepare the request payload
data = {
"model": model,
"messages": transformed_messages,
}
# Add tools if provided
if "tools" in kwargs:
data["tools"] = kwargs["tools"]
kwargs.pop("tools")
# Add tool_choice if provided
if "tool_choice" in kwargs:
data["tool_choice"] = kwargs["tool_choice"]
kwargs.pop("tool_choice")
# Add remaining kwargs
data.update(kwargs)
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
try:
# Make the request to Fireworks AI endpoint.
response = httpx.post(
self.BASE_URL, json=data, headers=headers, timeout=self.timeout
)
response.raise_for_status()
return self.transformer.convert_response(response.json())
except httpx.HTTPStatusError as error:
error_message = (
f"The request failed with status code: {error.status_code}\n"
)
error_message += f"Headers: {error.headers}\n"
error_message += error.response.text
raise LLMError(error_message)
except Exception as e:
raise LLMError(f"An error occurred: {e}")
def _normalize_response(self, response_data):
"""
Normalize the response to a common format (ChatCompletionResponse).
"""
normalized_response = ChatCompletionResponse()
normalized_response.choices[0].message.content = response_data["choices"][0][
"message"
]["content"]
return normalized_response
================================================
FILE: aisuite/providers/google_provider.py
================================================
"""The interface to Google's Vertex AI."""
import os
import json
from typing import List, Dict, Any, Optional, Union, BinaryIO, AsyncGenerator
import vertexai
from vertexai.generative_models import (
GenerativeModel,
GenerationConfig,
Content,
Part,
Tool,
FunctionDeclaration,
)
import pprint
from aisuite.framework import ChatCompletionResponse, Message
from aisuite.framework.message import (
TranscriptionResult,
Word,
Segment,
Alternative,
StreamingTranscriptionChunk,
)
from aisuite.provider import Provider, ASRError, Audio
DEFAULT_TEMPERATURE = 0.7
ENABLE_DEBUG_MESSAGES = False
# Links.
# https://codelabs.developers.google.com/codelabs/gemini-function-calling#6
# https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#chat-samples
class GoogleMessageConverter:
@staticmethod
def convert_user_role_message(message: Dict[str, Any]) -> Content:
"""Convert user or system messages to Google Vertex AI format."""
parts = [Part.from_text(message["content"])]
return Content(role="user", parts=parts)
@staticmethod
def convert_assistant_role_message(message: Dict[str, Any]) -> Content:
"""Convert assistant messages to Google Vertex AI format."""
if "tool_calls" in message and message["tool_calls"]:
# Handle function calls
tool_call = message["tool_calls"][
0
] # Assuming single function call for now
function_call = tool_call["function"]
# Create a Part from the function call
parts = [
Part.from_dict(
{
"function_call": {
"name": function_call["name"],
# "arguments": json.loads(function_call["arguments"])
}
}
)
]
# return Content(role="function", parts=parts)
else:
# Handle regular text messages
parts = [Part.from_text(message["content"])]
# return Content(role="model", parts=parts)
return Content(role="model", parts=parts)
@staticmethod
def convert_tool_role_message(message: Dict[str, Any]) -> Part:
"""Convert tool messages to Google Vertex AI format."""
if "content" not in message:
raise ValueError("Tool result message must have a content field")
try:
content_json = json.loads(message["content"])
part = Part.from_function_response(
name=message["name"], response=content_json
)
# TODO: Return Content instead of Part. But returning Content is not working.
return part
except
gitextract_z7uqp5wm/
├── .github/
│ └── workflows/
│ ├── black.yml
│ └── run_pytest.yml
├── .gitignore
├── .pre-commit-config.yaml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── aisuite/
│ ├── __init__.py
│ ├── client.py
│ ├── design-notes/
│ │ └── asr-parameter-design-motivation.md
│ ├── framework/
│ │ ├── __init__.py
│ │ ├── asr_params.py
│ │ ├── chat_completion_response.py
│ │ ├── choice.py
│ │ ├── message.py
│ │ ├── parameter_mapper.py
│ │ └── provider_interface.py
│ ├── mcp/
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── config.py
│ │ ├── schema_converter.py
│ │ └── tool_wrapper.py
│ ├── provider.py
│ ├── providers/
│ │ ├── __init__.py
│ │ ├── anthropic_provider.py
│ │ ├── aws_provider.py
│ │ ├── azure_provider.py
│ │ ├── cerebras_provider.py
│ │ ├── cohere_provider.py
│ │ ├── deepgram_provider.py
│ │ ├── deepseek_provider.py
│ │ ├── fireworks_provider.py
│ │ ├── google_provider.py
│ │ ├── groq_provider.py
│ │ ├── huggingface_provider.py
│ │ ├── inception_provider.py
│ │ ├── lmstudio_provider.py
│ │ ├── message_converter.py
│ │ ├── mistral_provider.py
│ │ ├── nebius_provider.py
│ │ ├── ollama_provider.py
│ │ ├── openai_provider.py
│ │ ├── sambanova_provider.py
│ │ ├── together_provider.py
│ │ ├── watsonx_provider.py
│ │ └── xai_provider.py
│ └── utils/
│ ├── tools.py
│ └── utils.py
├── aisuite-js/
│ ├── README.md
│ ├── examples/
│ │ ├── basic-usage.ts
│ │ ├── chat-app/
│ │ │ ├── .eslintrc.cjs
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── index.html
│ │ │ ├── package.json
│ │ │ ├── postcss.config.js
│ │ │ ├── src/
│ │ │ │ ├── App.tsx
│ │ │ │ ├── components/
│ │ │ │ │ ├── ApiKeyModal.tsx
│ │ │ │ │ ├── ChatContainer.tsx
│ │ │ │ │ ├── ChatInput.tsx
│ │ │ │ │ ├── ChatMessage.tsx
│ │ │ │ │ ├── ModelSelector.tsx
│ │ │ │ │ └── ProviderSelector.tsx
│ │ │ │ ├── config/
│ │ │ │ │ └── llm-config.ts
│ │ │ │ ├── index.css
│ │ │ │ ├── main.tsx
│ │ │ │ ├── services/
│ │ │ │ │ └── aisuite-service.ts
│ │ │ │ ├── types/
│ │ │ │ │ └── chat.ts
│ │ │ │ └── utils/
│ │ │ │ └── cn.ts
│ │ │ ├── tailwind.config.js
│ │ │ ├── tsconfig.json
│ │ │ ├── tsconfig.node.json
│ │ │ └── vite.config.ts
│ │ ├── deepgram.ts
│ │ ├── groq.ts
│ │ ├── mistral.ts
│ │ ├── openai-asr.ts
│ │ ├── streaming.ts
│ │ ├── test-suite.ts
│ │ └── tool-calling.ts
│ ├── jest.config.ts
│ ├── package.json
│ ├── src/
│ │ ├── asr-providers/
│ │ │ ├── deepgram/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ └── index.ts
│ │ ├── client.ts
│ │ ├── core/
│ │ │ ├── base-asr-provider.ts
│ │ │ ├── base-provider.ts
│ │ │ ├── errors.ts
│ │ │ └── model-parser.ts
│ │ ├── index.ts
│ │ ├── providers/
│ │ │ ├── anthropic/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ ├── groq/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ ├── index.ts
│ │ │ ├── mistral/
│ │ │ │ ├── adapters.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── provider.ts
│ │ │ │ └── types.ts
│ │ │ └── openai/
│ │ │ ├── adapters.ts
│ │ │ ├── index.ts
│ │ │ ├── provider.ts
│ │ │ └── types.ts
│ │ ├── types/
│ │ │ ├── chat.ts
│ │ │ ├── common.ts
│ │ │ ├── index.ts
│ │ │ ├── providers.ts
│ │ │ ├── tools.ts
│ │ │ └── transcription.ts
│ │ └── utils/
│ │ └── streaming.ts
│ ├── tests/
│ │ ├── client.test.ts
│ │ ├── providers/
│ │ │ ├── anthropic-provider.test.ts
│ │ │ ├── deepgram-provider.test.ts
│ │ │ ├── groq-provider.test.ts
│ │ │ ├── mistral-provider.test.ts
│ │ │ ├── openai-provider.test.ts
│ │ │ └── openai_asr_provider.test.ts
│ │ └── utils/
│ │ └── streaming.test.ts
│ └── tsconfig.json
├── examples/
│ ├── AISuiteDemo.ipynb
│ ├── DeepseekPost.ipynb
│ ├── QnA_with_pdf.ipynb
│ ├── agents/
│ │ ├── movie_buff_assistant.ipynb
│ │ ├── recipe_chef_assistant.ipynb
│ │ ├── snake_game_generator.ipynb
│ │ ├── stock_dashboard.html
│ │ ├── stock_market_dashboard.html
│ │ ├── stock_market_mini_tracker.ipynb
│ │ ├── stock_market_tracker.ipynb
│ │ └── world_weather_dashboard.ipynb
│ ├── aisuite_tool_abstraction.ipynb
│ ├── asr_example.ipynb
│ ├── chat-ui/
│ │ ├── .streamlit/
│ │ │ └── config.toml
│ │ ├── README.md
│ │ ├── chat.py
│ │ └── config.yaml
│ ├── client.ipynb
│ ├── llm_reasoning.ipynb
│ ├── mcp_config_dict_example.py
│ ├── mcp_http_example.py
│ ├── mcp_tools_example.ipynb
│ ├── simple_tool_calling.ipynb
│ └── tool_calling_abstraction.ipynb
├── guides/
│ ├── README.md
│ ├── anthropic.md
│ ├── aws.md
│ ├── azure.md
│ ├── cerebras.md
│ ├── cohere.md
│ ├── deepseek.md
│ ├── google.md
│ ├── groq.md
│ ├── huggingface.md
│ ├── lmstudio.md
│ ├── mistral.md
│ ├── nebius.md
│ ├── ollama.md
│ ├── openai.md
│ ├── sambanova.md
│ ├── watsonx.md
│ └── xai.md
├── pyproject.toml
└── tests/
├── __init__.py
├── client/
│ ├── __init__.py
│ ├── test_client.py
│ └── test_prerelease.py
├── framework/
│ ├── test_asr_models.py
│ └── test_asr_params.py
├── mcp/
│ ├── README.md
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_client.py
│ ├── test_e2e.py
│ ├── test_http_llm_e2e.py
│ ├── test_http_transport.py
│ └── test_llm_e2e.py
├── providers/
│ ├── __init__.py
│ ├── test_anthropic_converter.py
│ ├── test_asr_parameter_passthrough.py
│ ├── test_aws_converter.py
│ ├── test_azure_provider.py
│ ├── test_cerebras_provider.py
│ ├── test_cohere_provider.py
│ ├── test_deepgram_provider.py
│ ├── test_deepseek_provider.py
│ ├── test_google_converter.py
│ ├── test_google_provider.py
│ ├── test_groq_provider.py
│ ├── test_huggingface_provider.py
│ ├── test_inception_provider.py
│ ├── test_lmstudio_provider.py
│ ├── test_mistral_provider.py
│ ├── test_nebius_provider.py
│ ├── test_ollama_provider.py
│ ├── test_openai_provider.py
│ ├── test_sambanova_provider.py
│ └── test_watsonx_provider.py
├── test_provider.py
└── utils/
├── test_mcp_memory_integration.py
├── test_tool_manager.py
└── test_tools_mcp_schema.py
SYMBOL INDEX (803 symbols across 116 files)
FILE: aisuite-js/examples/basic-usage.ts
function main (line 4) | async function main() {
FILE: aisuite-js/examples/chat-app/src/App.tsx
function App (line 12) | function App() {
FILE: aisuite-js/examples/chat-app/src/components/ApiKeyModal.tsx
type ApiKeyModalProps (line 5) | interface ApiKeyModalProps {
FILE: aisuite-js/examples/chat-app/src/components/ChatContainer.tsx
type ChatContainerProps (line 5) | interface ChatContainerProps {
FILE: aisuite-js/examples/chat-app/src/components/ChatInput.tsx
type ChatInputProps (line 4) | interface ChatInputProps {
FILE: aisuite-js/examples/chat-app/src/components/ChatMessage.tsx
type ChatMessageProps (line 5) | interface ChatMessageProps {
FILE: aisuite-js/examples/chat-app/src/components/ModelSelector.tsx
type ModelSelectorProps (line 4) | interface ModelSelectorProps {
FILE: aisuite-js/examples/chat-app/src/components/ProviderSelector.tsx
type ProviderSelectorProps (line 3) | interface ProviderSelectorProps {
FILE: aisuite-js/examples/chat-app/src/services/aisuite-service.ts
class AISuiteService (line 4) | class AISuiteService {
method initialize (line 8) | initialize(config: AISuiteConfig) {
method queryLLM (line 13) | async queryLLM(modelConfig: LLMConfig, messages: Message[]): Promise<s...
method getAvailableProviders (line 43) | getAvailableProviders(): string[] {
method isProviderConfigured (line 50) | isProviderConfigured(provider: string): boolean {
method getConfig (line 57) | getConfig(): AISuiteConfig | null {
FILE: aisuite-js/examples/chat-app/src/types/chat.ts
type Message (line 1) | interface Message {
type ChatHistory (line 7) | interface ChatHistory {
type LLMConfig (line 15) | interface LLMConfig {
type ChatState (line 21) | interface ChatState {
type AISuiteConfig (line 30) | interface AISuiteConfig {
FILE: aisuite-js/examples/chat-app/src/utils/cn.ts
function cn (line 4) | function cn(...inputs: ClassValue[]) {
FILE: aisuite-js/examples/deepgram.ts
function main (line 5) | async function main() {
FILE: aisuite-js/examples/groq.ts
function getWeather (line 5) | function getWeather(location: string, unit: 'celsius' | 'fahrenheit' = '...
constant AVAILABLE_MODELS (line 16) | const AVAILABLE_MODELS = {
function main (line 22) | async function main() {
FILE: aisuite-js/examples/mistral.ts
function retrievePaymentStatus (line 22) | function retrievePaymentStatus({ data, transactionId }) {
function retrievePaymentDate (line 33) | function retrievePaymentDate({ data, transactionId }) {
constant TOOLS (line 50) | const TOOLS = [
function main (line 81) | async function main() {
FILE: aisuite-js/examples/openai-asr.ts
function main (line 5) | async function main() {
FILE: aisuite-js/examples/streaming.ts
function main (line 4) | async function main() {
FILE: aisuite-js/examples/test-suite.ts
constant ENABLE_OPENAI_TESTS (line 5) | const ENABLE_OPENAI_TESTS = !!process.env.OPENAI_API_KEY && process.env....
constant ENABLE_ANTHROPIC_TESTS (line 6) | const ENABLE_ANTHROPIC_TESTS = !!process.env.ANTHROPIC_API_KEY && proces...
function runTest (line 14) | async function runTest(name: string, fn: () => Promise<void>) {
function main (line 24) | async function main() {
FILE: aisuite-js/examples/tool-calling.ts
function getWeather (line 5) | function getWeather(location: string, unit: 'celsius' | 'fahrenheit' = '...
function main (line 15) | async function main() {
FILE: aisuite-js/src/asr-providers/deepgram/adapters.ts
function adaptResponse (line 3) | function adaptResponse(response: any): TranscriptionResult {
FILE: aisuite-js/src/asr-providers/deepgram/provider.ts
class DeepgramASRProvider (line 13) | class DeepgramASRProvider implements ASRProvider {
method constructor (line 17) | constructor(config: DeepgramConfig) {
method validateParams (line 25) | validateParams(params: { [key: string]: any }): void {
method translateParams (line 43) | translateParams(params: { [key: string]: any }): { [key: string]: any } {
method transcribe (line 57) | async transcribe(
FILE: aisuite-js/src/asr-providers/deepgram/types.ts
type DeepgramConfig (line 1) | interface DeepgramConfig {
FILE: aisuite-js/src/client.ts
class Client (line 20) | class Client {
method constructor (line 24) | constructor(config: ProviderConfigs) {
method initializeProviders (line 28) | private initializeProviders(config: ProviderConfigs): void {
method listProviders (line 122) | public listProviders(): string[] {
method listASRProviders (line 126) | public listASRProviders(): string[] {
method isProviderConfigured (line 130) | public isProviderConfigured(provider: string): boolean {
method isASRProviderConfigured (line 134) | public isASRProviderConfigured(provider: string): boolean {
FILE: aisuite-js/src/core/base-asr-provider.ts
type ASRProvider (line 7) | interface ASRProvider {
FILE: aisuite-js/src/core/base-provider.ts
type Provider (line 8) | interface Provider {
FILE: aisuite-js/src/core/errors.ts
class AISuiteError (line 1) | class AISuiteError extends Error {
method constructor (line 2) | constructor(
class ProviderNotConfiguredError (line 13) | class ProviderNotConfiguredError extends AISuiteError {
method constructor (line 14) | constructor(provider: string, availableProviders: string[]) {
class InvalidModelFormatError (line 25) | class InvalidModelFormatError extends AISuiteError {
method constructor (line 26) | constructor(model: string) {
class ToolCallError (line 35) | class ToolCallError extends AISuiteError {
method constructor (line 36) | constructor(message: string, provider: string) {
class AudioProcessingError (line 41) | class AudioProcessingError extends AISuiteError {
method constructor (line 42) | constructor(message: string, provider: string) {
class UnsupportedParameterError (line 48) | class UnsupportedParameterError extends AISuiteError {
method constructor (line 49) | constructor(parameter: string, provider: string) {
FILE: aisuite-js/src/core/model-parser.ts
type ParsedModel (line 3) | interface ParsedModel {
function parseModel (line 8) | function parseModel(model: string): ParsedModel {
FILE: aisuite-js/src/providers/anthropic/adapters.ts
function adaptRequest (line 16) | function adaptRequest(request: ChatCompletionRequest): MessageCreatePara...
function transformMessages (line 40) | function transformMessages(messages: ChatMessage[]) {
function adaptTool (line 99) | function adaptTool(tool: Tool): any {
function adaptResponse (line 111) | function adaptResponse(response: Message, originalModel: string): ChatCo...
function adaptStreamEvent (line 154) | function adaptStreamEvent(
FILE: aisuite-js/src/providers/anthropic/provider.ts
class AnthropicProvider (line 14) | class AnthropicProvider implements Provider {
method constructor (line 18) | constructor(config: AnthropicConfig) {
method chatCompletion (line 25) | async chatCompletion(
method streamChatCompletion (line 58) | async *streamChatCompletion(
FILE: aisuite-js/src/providers/groq/adapters.ts
function adaptRequest (line 9) | function adaptRequest(request: ChatCompletionRequest): any {
function adaptResponse (line 21) | function adaptResponse(
function adaptStreamResponse (line 42) | function adaptStreamResponse(
FILE: aisuite-js/src/providers/groq/provider.ts
class GroqProvider (line 14) | class GroqProvider implements Provider {
method constructor (line 18) | constructor(config: GroqConfig) {
method chatCompletion (line 28) | async chatCompletion(
method streamChatCompletion (line 59) | async *streamChatCompletion(
FILE: aisuite-js/src/providers/groq/types.ts
type GroqConfig (line 1) | interface GroqConfig {
FILE: aisuite-js/src/providers/mistral/adapters.ts
function adaptRequest (line 12) | function adaptRequest(request: ChatCompletionRequest): any {
function adaptMessage (line 30) | function adaptMessage(message: ChatMessage): any {
function adaptResponse (line 38) | function adaptResponse(
function adaptStreamResponse (line 64) | function adaptStreamResponse(
FILE: aisuite-js/src/providers/mistral/provider.ts
class MistralProvider (line 14) | class MistralProvider implements Provider {
method constructor (line 18) | constructor(config: MistralConfig) {
method chatCompletion (line 25) | async chatCompletion(
method streamChatCompletion (line 56) | async *streamChatCompletion(
FILE: aisuite-js/src/providers/openai/adapters.ts
function adaptRequest (line 19) | function adaptRequest(
function adaptMessage (line 41) | function adaptMessage(message: ChatMessage): any {
function adaptResponse (line 51) | function adaptResponse(
function adaptChunk (line 77) | function adaptChunk(chunk: OpenAIChunk): ChatCompletionChunk {
function adaptToolCall (line 102) | function adaptToolCall(toolCall: any): ToolCall {
function adaptASRRequest (line 113) | function adaptASRRequest(request: TranscriptionRequest): {
function adaptASRResponse (line 131) | function adaptASRResponse(
FILE: aisuite-js/src/providers/openai/provider.ts
class OpenAIProvider (line 22) | class OpenAIProvider implements Provider, ASRProvider {
method constructor (line 26) | constructor(config: OpenAIConfig) {
method chatCompletion (line 34) | async chatCompletion(
method streamChatCompletion (line 69) | async *streamChatCompletion(
method transcribe (line 97) | async transcribe(
method validateParams (line 123) | validateParams(params: { [key: string]: any }): void {
method translateParams (line 141) | translateParams(params: { [key: string]: any }): { [key: string]: any } {
FILE: aisuite-js/src/providers/openai/types.ts
type OpenAIASRRequest (line 13) | interface OpenAIASRRequest {
type OpenAIASRResponse (line 23) | interface OpenAIASRResponse extends OpenAI.Audio.Transcription {
FILE: aisuite-js/src/types/chat.ts
type ChatMessage (line 1) | interface ChatMessage {
type ChatCompletionRequest (line 9) | interface ChatCompletionRequest {
type ChatCompletionResponse (line 24) | interface ChatCompletionResponse {
type ChatCompletionChunk (line 34) | interface ChatCompletionChunk {
type ChatChoice (line 51) | interface ChatChoice {
type Usage (line 57) | interface Usage {
FILE: aisuite-js/src/types/common.ts
type RequestOptions (line 1) | interface RequestOptions {
type AudioInput (line 8) | type AudioInput = string | Buffer | Uint8Array;
FILE: aisuite-js/src/types/providers.ts
type ProviderConfigs (line 1) | interface ProviderConfigs {
type OpenAIConfig (line 9) | interface OpenAIConfig {
type AnthropicConfig (line 15) | interface AnthropicConfig {
type MistralConfig (line 20) | interface MistralConfig {
type GroqConfig (line 25) | interface GroqConfig {
type DeepgramConfig (line 30) | interface DeepgramConfig {
FILE: aisuite-js/src/types/tools.ts
type Tool (line 1) | interface Tool {
type FunctionDefinition (line 6) | interface FunctionDefinition {
type ToolCall (line 16) | interface ToolCall {
type ToolChoice (line 25) | type ToolChoice =
FILE: aisuite-js/src/types/transcription.ts
type Word (line 1) | interface Word {
type Segment (line 9) | interface Segment {
type TranscriptionResult (line 16) | interface TranscriptionResult {
type TranscriptionRequest (line 24) | interface TranscriptionRequest {
FILE: aisuite-js/src/utils/streaming.ts
function createChunk (line 3) | function createChunk(
function generateId (line 27) | function generateId(): string {
FILE: aisuite-js/tests/client.test.ts
class MockProvider (line 64) | class MockProvider implements Provider {
method constructor (line 71) | constructor(name: string) {
FILE: aisuite/client.py
class Client (line 21) | class Client:
method __init__ (line 22) | def __init__(
method _initialize_providers (line 56) | def _initialize_providers(self):
method _validate_provider_key (line 64) | def _validate_provider_key(self, provider_key):
method configure (line 78) | def configure(self, provider_configs: Optional[dict] = None):
method chat (line 89) | def chat(self):
method audio (line 96) | def audio(self):
class Chat (line 103) | class Chat:
method __init__ (line 104) | def __init__(self, client: "Client"):
method completions (line 109) | def completions(self):
class Completions (line 114) | class Completions:
method __init__ (line 115) | def __init__(self, client: "Client"):
method _process_mcp_configs (line 118) | def _process_mcp_configs(self, tools: list) -> tuple[list, list]:
method _extract_thinking_content (line 183) | def _extract_thinking_content(self, response):
method _tool_runner (line 211) | def _tool_runner(
method create (line 293) | def create(self, model: str, messages: list, **kwargs):
class Audio (line 359) | class Audio:
method __init__ (line 362) | def __init__(self, client: "Client"):
method transcriptions (line 367) | def transcriptions(self):
class Transcriptions (line 372) | class Transcriptions:
method __init__ (line 375) | def __init__(self, client: "Client"):
method create (line 378) | def create(
FILE: aisuite/framework/asr_params.py
class ParamValidator (line 158) | class ParamValidator:
method __init__ (line 168) | def __init__(self, extra_param_mode: Literal["strict", "warn", "permis...
method validate_and_map (line 180) | def validate_and_map(
method _transform_value (line 240) | def _transform_value(self, provider_key: str, param_key: str, value: A...
method _handle_unknown (line 274) | def _handle_unknown(self, provider_key: str, unknown_params: list):
FILE: aisuite/framework/chat_completion_response.py
class ChatCompletionResponse (line 10) | class ChatCompletionResponse:
method __init__ (line 13) | def __init__(self):
FILE: aisuite/framework/choice.py
class Choice (line 5) | class Choice:
method __init__ (line 6) | def __init__(self):
FILE: aisuite/framework/message.py
class Function (line 11) | class Function(BaseModel):
class ChatCompletionMessageToolCall (line 18) | class ChatCompletionMessageToolCall(BaseModel):
class Message (line 26) | class Message(BaseModel):
class CompletionTokensDetails (line 36) | class CompletionTokensDetails(BaseModel):
class PromptTokensDetails (line 60) | class PromptTokensDetails(BaseModel):
class CompletionUsage (line 73) | class CompletionUsage(BaseModel):
class Word (line 92) | class Word(BaseModel):
class Segment (line 104) | class Segment(BaseModel):
class Alternative (line 125) | class Alternative(BaseModel):
class Channel (line 133) | class Channel(BaseModel):
class TranscriptionResult (line 140) | class TranscriptionResult(BaseModel):
class StreamingTranscriptionChunk (line 174) | class StreamingTranscriptionChunk(BaseModel):
class TranscriptionOptions (line 198) | class TranscriptionOptions:
method __post_init__ (line 255) | def __post_init__(self):
method has_any_parameters (line 279) | def has_any_parameters(self) -> bool:
method get_set_parameters (line 289) | def get_set_parameters(self) -> Dict[str, Any]:
FILE: aisuite/framework/parameter_mapper.py
class ParameterMapper (line 12) | class ParameterMapper:
method map_to_openai (line 75) | def map_to_openai(cls, options: "TranscriptionOptions") -> Dict[str, A...
method map_to_deepgram (line 101) | def map_to_deepgram(cls, options: "TranscriptionOptions") -> Dict[str,...
method map_to_google (line 131) | def map_to_google(cls, options: "TranscriptionOptions") -> Dict[str, A...
method _apply_custom_parameters (line 203) | def _apply_custom_parameters(
FILE: aisuite/framework/provider_interface.py
class ProviderInterface (line 5) | class ProviderInterface:
method chat_completion_create (line 8) | def chat_completion_create(self, messages=None, model=None, temperatur...
FILE: aisuite/mcp/client.py
class MCPClient (line 34) | class MCPClient:
method __init__ (line 67) | def __init__(
method from_config (line 137) | def from_config(cls, config: Dict[str, Any]) -> "MCPClient":
method get_tools_from_config (line 193) | def get_tools_from_config(config: Dict[str, Any]) -> List[Callable]:
method _connect (line 233) | def _connect(self):
method _async_connect (line 274) | async def _async_connect(self):
method _parse_sse_response (line 307) | async def _parse_sse_response(
method _send_http_request (line 374) | async def _send_http_request(
method _send_notification (line 457) | async def _send_notification(
method _async_connect_http (line 499) | async def _async_connect_http(self):
method list_tools (line 529) | def list_tools(self) -> List[Dict[str, Any]]:
method get_callable_tools (line 545) | def get_callable_tools(
method get_tool (line 595) | def get_tool(self, tool_name: str) -> Optional[Callable]:
method call_tool (line 616) | def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
method _async_call_tool (line 651) | async def _async_call_tool(self, tool_name: str, arguments: Dict[str, ...
method _async_call_tool_http (line 680) | async def _async_call_tool_http(
method close (line 715) | def close(self):
method _async_close (line 739) | async def _async_close(self):
method __enter__ (line 771) | def __enter__(self):
method __exit__ (line 775) | def __exit__(self, exc_type, exc_val, exc_tb):
method __repr__ (line 780) | def __repr__(self) -> str:
FILE: aisuite/mcp/config.py
class MCPConfig (line 11) | class MCPConfig(TypedDict, total=False):
function validate_mcp_config (line 49) | def validate_mcp_config(config: Dict[str, Any]) -> MCPConfig:
function is_mcp_config (line 225) | def is_mcp_config(obj: Any) -> bool:
function get_transport_type (line 244) | def get_transport_type(config: MCPConfig) -> Literal["stdio", "http"]:
FILE: aisuite/mcp/schema_converter.py
function json_schema_to_python_type (line 12) | def json_schema_to_python_type(schema: Dict[str, Any]) -> type:
function mcp_schema_to_annotations (line 60) | def mcp_schema_to_annotations(input_schema: Dict[str, Any]) -> Dict[str,...
function create_function_signature (line 107) | def create_function_signature(
function extract_parameter_descriptions (line 158) | def extract_parameter_descriptions(input_schema: Dict[str, Any]) -> Dict...
function build_docstring (line 178) | def build_docstring(
FILE: aisuite/mcp/tool_wrapper.py
class MCPToolWrapper (line 19) | class MCPToolWrapper:
method __init__ (line 38) | def __init__(
method _create_signature (line 77) | def _create_signature(self, input_schema: Dict[str, Any]) -> inspect.S...
method __call__ (line 109) | def __call__(self, **kwargs) -> Any:
method __repr__ (line 131) | def __repr__(self) -> str:
function create_mcp_tool_wrapper (line 136) | def create_mcp_tool_wrapper(
FILE: aisuite/provider.py
class LLMError (line 9) | class LLMError(Exception):
method __init__ (line 12) | def __init__(self, message):
class ASRError (line 16) | class ASRError(Exception):
method __init__ (line 19) | def __init__(self, message):
class Provider (line 23) | class Provider(ABC):
method __init__ (line 24) | def __init__(self):
method chat_completions_create (line 29) | def chat_completions_create(self, model, messages):
class ProviderFactory (line 34) | class ProviderFactory:
method create_provider (line 40) | def create_provider(cls, provider_key, config):
method get_supported_providers (line 62) | def get_supported_providers(cls):
class Audio (line 68) | class Audio:
method __init__ (line 71) | def __init__(self):
class Transcription (line 74) | class Transcription(ABC):
method create (line 77) | def create(
method create_stream_output (line 87) | async def create_stream_output(
FILE: aisuite/providers/anthropic_provider.py
class AnthropicMessageConverter (line 21) | class AnthropicMessageConverter:
method convert_request (line 35) | def convert_request(self, messages):
method convert_response (line 41) | def convert_response(self, response):
method _convert_single_message (line 49) | def _convert_single_message(self, msg):
method _convert_dict_message (line 55) | def _convert_dict_message(self, msg):
method _convert_message_object (line 65) | def _convert_message_object(self, msg):
method _create_tool_result_message (line 73) | def _create_tool_result_message(self, tool_call_id, content):
method _create_assistant_tool_message (line 86) | def _create_assistant_tool_message(self, content, tool_calls):
method _extract_system_message (line 115) | def _extract_system_message(self, messages):
method _get_finish_reason (line 126) | def _get_finish_reason(self, response):
method _get_completion_usage (line 130) | def _get_completion_usage(self, response):
method _get_message (line 141) | def _get_message(self, response):
method convert_response_with_tool_use (line 164) | def convert_response_with_tool_use(self, response):
method convert_tool_spec (line 195) | def convert_tool_spec(self, openai_tools):
class AnthropicProvider (line 218) | class AnthropicProvider(Provider):
method __init__ (line 219) | def __init__(self, **config):
method chat_completions_create (line 224) | def chat_completions_create(self, model, messages, **kwargs):
method _prepare_kwargs (line 234) | def _prepare_kwargs(self, kwargs):
FILE: aisuite/providers/aws_provider.py
class BedrockConfig (line 16) | class BedrockConfig:
method __init__ (line 21) | def __init__(self, **config):
method create_client (line 27) | def create_client(self):
class BedrockMessageConverter (line 35) | class BedrockMessageConverter:
method convert_request (line 39) | def convert_request(
method convert_response_tool_call (line 80) | def convert_response_tool_call(
method convert_tool_result (line 113) | def convert_tool_result(message: Dict[str, Any]) -> Optional[Dict[str,...
method convert_assistant (line 136) | def convert_assistant(message: Dict[str, Any]) -> Optional[Dict[str, A...
method convert_response (line 167) | def convert_response(response: Dict[str, Any]) -> ChatCompletionResponse:
method get_completion_usage (line 202) | def get_completion_usage(usage_data: dict):
class AwsProvider (line 211) | class AwsProvider(Provider):
method __init__ (line 214) | def __init__(self, **config):
method convert_response (line 220) | def convert_response(self, response: Dict[str, Any]) -> ChatCompletion...
method _convert_tool_spec (line 224) | def _convert_tool_spec(self, kwargs: Dict[str, Any]) -> Optional[Dict[...
method _prepare_request_config (line 243) | def _prepare_request_config(self, kwargs: Dict[str, Any]) -> Dict[str,...
method chat_completions_create (line 271) | def chat_completions_create(
FILE: aisuite/providers/azure_provider.py
class AzureMessageConverter (line 40) | class AzureMessageConverter:
method convert_request (line 42) | def convert_request(messages):
method convert_response (line 53) | def convert_response(resp_json) -> ChatCompletionResponse:
class AzureProvider (line 81) | class AzureProvider(Provider):
method __init__ (line 82) | def __init__(self, **config):
method chat_completions_create (line 94) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/cerebras_provider.py
class CerebrasMessageConverter (line 8) | class CerebrasMessageConverter(OpenAICompliantMessageConverter):
class CerebrasProvider (line 15) | class CerebrasProvider(Provider):
method __init__ (line 18) | def __init__(self, **config):
method chat_completions_create (line 22) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/cohere_provider.py
class CohereMessageConverter (line 9) | class CohereMessageConverter:
method convert_request (line 14) | def convert_request(self, messages):
method _convert_tool_content (line 77) | def _convert_tool_content(self, content):
method convert_response (line 95) | def convert_response(response_data) -> ChatCompletionResponse:
class CohereProvider (line 133) | class CohereProvider(Provider):
method __init__ (line 134) | def __init__(self, **config):
method chat_completions_create (line 148) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/deepgram_provider.py
class DeepgramProvider (line 20) | class DeepgramProvider(Provider):
method __init__ (line 23) | def __init__(self, **config):
method chat_completions_create (line 47) | def chat_completions_create(self, model, messages):
class DeepgramAudio (line 55) | class DeepgramAudio(Audio):
method __init__ (line 58) | def __init__(self, client):
class Transcriptions (line 62) | class Transcriptions(Audio.Transcription):
method __init__ (line 65) | def __init__(self, client):
method create (line 68) | def create(
method create_stream_output (line 111) | async def create_stream_output(
method _prepare_audio_payload (line 244) | def _prepare_audio_payload(self, file: Union[str, BinaryIO]) -> bytes:
method _load_and_prepare_audio (line 261) | async def _load_and_prepare_audio(
method _send_audio_chunk (line 319) | def _send_audio_chunk(self, connection, audio_chunk: np.ndarray) -> ...
method _parse_deepgram_response (line 336) | def _parse_deepgram_response(self, response_dict: dict) -> Transcrip...
FILE: aisuite/providers/deepseek_provider.py
class DeepseekProvider (line 10) | class DeepseekProvider(Provider):
method __init__ (line 13) | def __init__(self, **config):
method chat_completions_create (line 38) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/fireworks_provider.py
class FireworksMessageConverter (line 9) | class FireworksMessageConverter:
method convert_request (line 11) | def convert_request(messages):
method convert_response (line 24) | def convert_response(resp_json) -> ChatCompletionResponse:
class FireworksProvider (line 60) | class FireworksProvider(Provider):
method __init__ (line 67) | def __init__(self, **config):
method chat_completions_create (line 82) | def chat_completions_create(self, model, messages, **kwargs):
method _normalize_response (line 133) | def _normalize_response(self, response_data):
FILE: aisuite/providers/google_provider.py
class GoogleMessageConverter (line 37) | class GoogleMessageConverter:
method convert_user_role_message (line 39) | def convert_user_role_message(message: Dict[str, Any]) -> Content:
method convert_assistant_role_message (line 45) | def convert_assistant_role_message(message: Dict[str, Any]) -> Content:
method convert_tool_role_message (line 74) | def convert_tool_role_message(message: Dict[str, Any]) -> Part:
method convert_request (line 90) | def convert_request(messages: List[Dict[str, Any]]) -> List[Content]:
method convert_response (line 118) | def convert_response(response) -> ChatCompletionResponse:
class GoogleProvider (line 200) | class GoogleProvider(Provider):
method __init__ (line 203) | def __init__(self, **config):
method chat_completions_create (line 230) | def chat_completions_create(self, model, messages, **kwargs):
method speech_client (line 317) | def speech_client(self):
class GoogleAudio (line 333) | class GoogleAudio(Audio):
method __init__ (line 336) | def __init__(self, provider):
class Transcriptions (line 341) | class Transcriptions(Audio.Transcription):
method __init__ (line 344) | def __init__(self, provider):
method create (line 347) | def create(
method create_stream_output (line 384) | async def create_stream_output(
method _read_audio_data (line 436) | def _read_audio_data(self, file: Union[str, BinaryIO]) -> bytes:
method _detect_audio_encoding (line 444) | def _detect_audio_encoding(self, file: Union[str, BinaryIO], speech):
method _build_recognition_config (line 463) | def _build_recognition_config(
method _create_streaming_requests (line 490) | def _create_streaming_requests(
method _parse_google_response (line 503) | def _parse_google_response(self, response) -> TranscriptionResult:
FILE: aisuite/providers/groq_provider.py
class GroqMessageConverter (line 23) | class GroqMessageConverter(OpenAICompliantMessageConverter):
class GroqProvider (line 31) | class GroqProvider(Provider):
method __init__ (line 32) | def __init__(self, **config):
method chat_completions_create (line 47) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/huggingface_provider.py
class HuggingfaceProvider (line 12) | class HuggingfaceProvider(Provider):
method __init__ (line 21) | def __init__(self, **config):
method chat_completions_create (line 48) | def chat_completions_create(self, model, messages, **kwargs):
method transform_from_message (line 86) | def transform_from_message(self, message: Message):
method transform_to_message (line 113) | def transform_to_message(self, message_dict: dict):
method _normalize_response (line 132) | def _normalize_response(self, response_data):
class HuggingfaceAudio (line 143) | class HuggingfaceAudio(Audio):
method __init__ (line 146) | def __init__(self, token, timeout=120):
class Transcriptions (line 150) | class Transcriptions(Audio.Transcription):
method __init__ (line 153) | def __init__(self, token, timeout=120):
method create (line 157) | def create(
method _detect_content_type (line 227) | def _detect_content_type(self, file_path: str) -> str:
method _parse_huggingface_response (line 239) | def _parse_huggingface_response(
FILE: aisuite/providers/inception_provider.py
class InceptionProvider (line 6) | class InceptionProvider(Provider):
method __init__ (line 7) | def __init__(self, **config):
method chat_completions_create (line 23) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/lmstudio_provider.py
class LmstudioProvider (line 7) | class LmstudioProvider(Provider):
method __init__ (line 18) | def __init__(self, **config):
method chat_completions_create (line 29) | def chat_completions_create(self, model, messages, **kwargs):
method _normalize_response (line 57) | def _normalize_response(self, response_data):
FILE: aisuite/providers/message_converter.py
class OpenAICompliantMessageConverter (line 11) | class OpenAICompliantMessageConverter:
method convert_request (line 20) | def convert_request(messages):
method convert_response (line 44) | def convert_response(self, response_data) -> ChatCompletionResponse:
method get_completion_usage (line 72) | def get_completion_usage(self, usage_data: dict):
FILE: aisuite/providers/mistral_provider.py
class MistralMessageConverter (line 17) | class MistralMessageConverter(OpenAICompliantMessageConverter):
method convert_response (line 22) | def convert_response(self, response_data) -> ChatCompletionResponse:
class MistralProvider (line 40) | class MistralProvider(Provider):
method __init__ (line 45) | def __init__(self, **config):
method chat_completions_create (line 60) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/nebius_provider.py
class NebiusProvider (line 10) | class NebiusProvider(Provider):
method __init__ (line 11) | def __init__(self, **config):
method chat_completions_create (line 27) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/ollama_provider.py
class OllamaProvider (line 7) | class OllamaProvider(Provider):
method __init__ (line 18) | def __init__(self, **config):
method chat_completions_create (line 29) | def chat_completions_create(self, model, messages, **kwargs):
method _normalize_response (line 57) | def _normalize_response(self, response_data):
FILE: aisuite/providers/openai_provider.py
class OpenaiProvider (line 14) | class OpenaiProvider(Provider):
method __init__ (line 15) | def __init__(self, **config):
method chat_completions_create (line 39) | def chat_completions_create(self, model, messages, **kwargs):
class OpenAIAudio (line 55) | class OpenAIAudio(Audio):
method __init__ (line 58) | def __init__(self, client):
class Transcriptions (line 62) | class Transcriptions(Audio.Transcription):
method __init__ (line 65) | def __init__(self, client):
method create (line 68) | def create(
method create_stream_output (line 111) | async def create_stream_output(
method _parse_openai_response (line 195) | def _parse_openai_response(self, response) -> TranscriptionResult:
FILE: aisuite/providers/sambanova_provider.py
class SambanovaMessageConverter (line 7) | class SambanovaMessageConverter(OpenAICompliantMessageConverter):
class SambanovaProvider (line 15) | class SambanovaProvider(Provider):
method __init__ (line 20) | def __init__(self, **config):
method chat_completions_create (line 38) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/together_provider.py
class TogetherMessageConverter (line 7) | class TogetherMessageConverter(OpenAICompliantMessageConverter):
class TogetherProvider (line 15) | class TogetherProvider(Provider):
method __init__ (line 22) | def __init__(self, **config):
method chat_completions_create (line 37) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/providers/watsonx_provider.py
class WatsonxProvider (line 8) | class WatsonxProvider(Provider):
method __init__ (line 9) | def __init__(self, **config):
method chat_completions_create (line 21) | def chat_completions_create(self, model, messages, **kwargs):
method normalize_response (line 34) | def normalize_response(self, response):
FILE: aisuite/providers/xai_provider.py
class XaiMessageConverter (line 8) | class XaiMessageConverter(OpenAICompliantMessageConverter):
class XaiProvider (line 16) | class XaiProvider(Provider):
method __init__ (line 23) | def __init__(self, **config):
method chat_completions_create (line 38) | def chat_completions_create(self, model, messages, **kwargs):
FILE: aisuite/utils/tools.py
class Tools (line 8) | class Tools:
method __init__ (line 9) | def __init__(self, tools: list[Callable] = None):
method _add_tool (line 16) | def _add_tool(self, func: Callable, param_model: Optional[Type[BaseMod...
method tools (line 36) | def tools(self, format="openai") -> list:
method _unwrap_optional (line 42) | def _unwrap_optional(self, field_type: Type) -> tuple[Type, bool]:
method _convert_to_tool_spec (line 62) | def _convert_to_tool_spec(
method __extract_param_descriptions (line 116) | def __extract_param_descriptions(self, func: Callable) -> dict[str, str]:
method _convert_mcp_schema_to_tool_spec (line 134) | def _convert_mcp_schema_to_tool_spec(self, func: Callable) -> Dict[str...
method _create_pydantic_model_from_mcp_schema (line 155) | def _create_pydantic_model_from_mcp_schema(self, func: Callable) -> Ty...
method __infer_from_signature (line 191) | def __infer_from_signature(
method __convert_to_openai_format (line 240) | def __convert_to_openai_format(self) -> list:
method results_to_messages (line 247) | def results_to_messages(self, results: list, message: any) -> list:
method execute (line 271) | def execute(self, tool_calls) -> list:
method execute_tool (line 316) | def execute_tool(self, tool_calls) -> tuple[list, list]:
FILE: aisuite/utils/utils.py
class Utils (line 9) | class Utils:
method spew (line 15) | def spew(obj):
FILE: examples/chat-ui/chat.py
function display_chat_history (line 94) | def display_chat_history(chat_history, model_name):
function query_llm (line 107) | def query_llm(model_config, chat_history):
FILE: examples/mcp_config_dict_example.py
function get_current_time (line 110) | def get_current_time() -> str:
function calculate_stats (line 115) | def calculate_stats(numbers: list) -> dict:
FILE: examples/mcp_http_example.py
function example_1_config_dict_format (line 25) | def example_1_config_dict_format():
function example_2_explicit_mcp_client (line 56) | def example_2_explicit_mcp_client():
function example_3_with_authentication (line 91) | def example_3_with_authentication():
function example_4_context_manager (line 124) | def example_4_context_manager():
function example_5_mixing_http_and_python_functions (line 148) | def example_5_mixing_http_and_python_functions():
function example_6_tool_filtering (line 186) | def example_6_tool_filtering():
function example_7_multiple_http_servers (line 212) | def example_7_multiple_http_servers():
FILE: tests/client/test_client.py
function provider_configs (line 12) | def provider_configs():
function test_client_chat_completions (line 106) | def test_client_chat_completions(
function test_invalid_provider_in_client_config (line 124) | def test_invalid_provider_in_client_config():
function test_invalid_model_format_in_create (line 146) | def test_invalid_model_format_in_create(monkeypatch):
class TestClientASR (line 179) | class TestClientASR:
method test_audio_interface_initialization (line 182) | def test_audio_interface_initialization(self):
method test_transcriptions_create_success (line 189) | def test_transcriptions_create_success(
method test_transcriptions_create_deepgram (line 218) | def test_transcriptions_create_deepgram(
method test_transcriptions_invalid_model_format (line 245) | def test_transcriptions_invalid_model_format(self, provider_configs):
method test_transcriptions_unsupported_provider (line 255) | def test_transcriptions_unsupported_provider(self, provider_configs):
class TestClientASRParameterValidation (line 266) | class TestClientASRParameterValidation:
method test_client_initialization_strict_mode (line 269) | def test_client_initialization_strict_mode(self):
method test_client_initialization_warn_mode (line 275) | def test_client_initialization_warn_mode(self):
method test_client_initialization_permissive_mode (line 281) | def test_client_initialization_permissive_mode(self):
method test_strict_mode_rejects_unknown_param (line 288) | def test_strict_mode_rejects_unknown_param(self, mock_create_provider):
method test_strict_mode_typo_detection (line 310) | def test_strict_mode_typo_detection(self, mock_create_provider):
method test_warn_mode_continues_execution (line 329) | def test_warn_mode_continues_execution(self, mock_create_provider):
method test_permissive_mode_allows_unknown_params (line 361) | def test_permissive_mode_allows_unknown_params(self, mock_create_provi...
method test_common_param_mapping_at_client_level (line 396) | def test_common_param_mapping_at_client_level(self, mock_create_provid...
method test_provider_specific_params_passthrough (line 425) | def test_provider_specific_params_passthrough(self, mock_create_provid...
method test_mixed_common_and_provider_params (line 452) | def test_mixed_common_and_provider_params(self, mock_create_provider):
method test_validation_happens_before_provider_call (line 483) | def test_validation_happens_before_provider_call(self, mock_create_pro...
method test_unsupported_common_param_ignored (line 505) | def test_unsupported_common_param_ignored(self, mock_create_provider):
method test_multiple_providers_with_same_client (line 533) | def test_multiple_providers_with_same_client(self, mock_create_provider):
FILE: tests/client/test_prerelease.py
function setup_client (line 10) | def setup_client() -> ai.Client:
function get_test_models (line 16) | def get_test_models() -> List[str]:
function get_test_messages (line 30) | def get_test_messages() -> List[Dict[str, str]]:
function test_model_pirate_response (line 43) | def test_model_pirate_response(model_id: str):
function get_test_asr_models (line 74) | def get_test_asr_models() -> List[str]:
function test_asr_portable_transcription (line 86) | def test_asr_portable_transcription(model_id: str):
function test_asr_deepgram_provider_specific_feature (line 146) | def test_asr_deepgram_provider_specific_feature():
function test_asr_google_language_mapping (line 178) | def test_asr_google_language_mapping():
function test_asr_huggingface_word_timestamps (line 206) | def test_asr_huggingface_word_timestamps():
FILE: tests/framework/test_asr_models.py
class TestWord (line 15) | class TestWord:
method test_word_creation_and_validation (line 18) | def test_word_creation_and_validation(self):
class TestSegment (line 44) | class TestSegment:
method test_segment_creation_and_validation (line 47) | def test_segment_creation_and_validation(self):
class TestAlternative (line 59) | class TestAlternative:
method test_alternative_creation_and_validation (line 62) | def test_alternative_creation_and_validation(self):
class TestChannel (line 74) | class TestChannel:
method test_channel_creation_and_validation (line 77) | def test_channel_creation_and_validation(self):
class TestTranscriptionResult (line 89) | class TestTranscriptionResult:
method test_transcription_result_basic (line 92) | def test_transcription_result_basic(self):
method test_transcription_result_openai_style (line 103) | def test_transcription_result_openai_style(self):
method test_transcription_result_deepgram_style (line 123) | def test_transcription_result_deepgram_style(self):
FILE: tests/framework/test_asr_params.py
class TestParamValidatorCommonParams (line 13) | class TestParamValidatorCommonParams:
method test_language_mapping_openai (line 16) | def test_language_mapping_openai(self):
method test_language_mapping_deepgram (line 22) | def test_language_mapping_deepgram(self):
method test_language_mapping_google (line 28) | def test_language_mapping_google(self):
method test_prompt_mapping_openai (line 34) | def test_prompt_mapping_openai(self):
method test_prompt_mapping_deepgram (line 40) | def test_prompt_mapping_deepgram(self):
method test_prompt_mapping_google (line 46) | def test_prompt_mapping_google(self):
method test_temperature_mapping_openai (line 52) | def test_temperature_mapping_openai(self):
method test_temperature_ignored_deepgram (line 58) | def test_temperature_ignored_deepgram(self):
method test_temperature_ignored_google (line 64) | def test_temperature_ignored_google(self):
class TestParamValidatorTransformations (line 71) | class TestParamValidatorTransformations:
method test_google_language_expansion_common_codes (line 74) | def test_google_language_expansion_common_codes(self):
method test_google_language_expansion_unknown_code (line 93) | def test_google_language_expansion_unknown_code(self):
method test_google_language_no_expansion_for_full_code (line 99) | def test_google_language_no_expansion_for_full_code(self):
method test_deepgram_prompt_to_keywords_single_word (line 106) | def test_deepgram_prompt_to_keywords_single_word(self):
method test_deepgram_prompt_to_keywords_multiple_words (line 112) | def test_deepgram_prompt_to_keywords_multiple_words(self):
method test_deepgram_prompt_to_keywords_already_list (line 120) | def test_deepgram_prompt_to_keywords_already_list(self):
method test_google_prompt_to_speech_contexts (line 128) | def test_google_prompt_to_speech_contexts(self):
class TestParamValidatorProviderSpecific (line 135) | class TestParamValidatorProviderSpecific:
method test_openai_response_format (line 138) | def test_openai_response_format(self):
method test_openai_timestamp_granularities (line 146) | def test_openai_timestamp_granularities(self):
method test_deepgram_punctuate (line 154) | def test_deepgram_punctuate(self):
method test_deepgram_diarize (line 160) | def test_deepgram_diarize(self):
method test_deepgram_multiple_features (line 166) | def test_deepgram_multiple_features(self):
method test_google_enable_automatic_punctuation (line 185) | def test_google_enable_automatic_punctuation(self):
method test_google_enable_speaker_diarization (line 193) | def test_google_enable_speaker_diarization(self):
method test_google_diarization_speaker_count (line 201) | def test_google_diarization_speaker_count(self):
class TestParamValidatorMixedParams (line 208) | class TestParamValidatorMixedParams:
method test_openai_common_and_specific (line 211) | def test_openai_common_and_specific(self):
method test_deepgram_common_and_specific (line 228) | def test_deepgram_common_and_specific(self):
method test_google_common_and_specific (line 247) | def test_google_common_and_specific(self):
class TestParamValidatorStrictMode (line 265) | class TestParamValidatorStrictMode:
method test_strict_mode_rejects_unknown_param_openai (line 268) | def test_strict_mode_rejects_unknown_param_openai(self):
method test_strict_mode_rejects_unknown_param_deepgram (line 276) | def test_strict_mode_rejects_unknown_param_deepgram(self):
method test_strict_mode_rejects_multiple_unknown_params (line 284) | def test_strict_mode_rejects_multiple_unknown_params(self):
method test_strict_mode_error_message_helpful (line 296) | def test_strict_mode_error_message_helpful(self):
method test_strict_mode_allows_valid_params (line 302) | def test_strict_mode_allows_valid_params(self):
class TestParamValidatorWarnMode (line 316) | class TestParamValidatorWarnMode:
method test_warn_mode_issues_warning_unknown_param (line 319) | def test_warn_mode_issues_warning_unknown_param(self):
method test_warn_mode_continues_execution (line 329) | def test_warn_mode_continues_execution(self):
method test_warn_mode_warning_message_helpful (line 344) | def test_warn_mode_warning_message_helpful(self):
class TestParamValidatorPermissiveMode (line 351) | class TestParamValidatorPermissiveMode:
method test_permissive_mode_allows_unknown_param (line 354) | def test_permissive_mode_allows_unknown_param(self):
method test_permissive_mode_no_warning (line 360) | def test_permissive_mode_no_warning(self):
method test_permissive_mode_mixed_valid_and_unknown (line 369) | def test_permissive_mode_mixed_valid_and_unknown(self):
class TestParamValidatorEdgeCases (line 389) | class TestParamValidatorEdgeCases:
method test_empty_params (line 392) | def test_empty_params(self):
method test_unknown_provider (line 398) | def test_unknown_provider(self):
method test_none_value_param (line 405) | def test_none_value_param(self):
method test_common_param_overrides_provider_param (line 411) | def test_common_param_overrides_provider_param(self):
method test_validator_mode_case_sensitivity (line 429) | def test_validator_mode_case_sensitivity(self):
class TestParamValidatorRegistry (line 441) | class TestParamValidatorRegistry:
method test_common_params_has_required_providers (line 444) | def test_common_params_has_required_providers(self):
method test_provider_params_has_all_providers (line 450) | def test_provider_params_has_all_providers(self):
method test_google_language_map_completeness (line 456) | def test_google_language_map_completeness(self):
method test_provider_params_includes_common_params (line 462) | def test_provider_params_includes_common_params(self):
FILE: tests/mcp/conftest.py
function temp_test_dir (line 21) | def temp_test_dir():
function skip_if_no_npx (line 61) | def skip_if_no_npx():
FILE: tests/mcp/test_client.py
class TestMCPClientConnection (line 20) | class TestMCPClientConnection:
method test_connect_to_filesystem_server (line 23) | def test_connect_to_filesystem_server(self, temp_test_dir, skip_if_no_...
method test_list_tools_returns_schemas (line 53) | def test_list_tools_returns_schemas(self, temp_test_dir, skip_if_no_npx):
method test_context_manager (line 74) | def test_context_manager(self, temp_test_dir, skip_if_no_npx):
class TestMCPClientToolExecution (line 88) | class TestMCPClientToolExecution:
method test_call_read_file_tool (line 91) | def test_call_read_file_tool(self, temp_test_dir, skip_if_no_npx):
method test_call_list_directory_tool (line 111) | def test_call_list_directory_tool(self, temp_test_dir, skip_if_no_npx):
class TestMCPClientCallableTools (line 130) | class TestMCPClientCallableTools:
method test_get_callable_tools (line 133) | def test_get_callable_tools(self, temp_test_dir, skip_if_no_npx):
method test_get_callable_tools_with_filtering (line 167) | def test_get_callable_tools_with_filtering(self, temp_test_dir, skip_i...
method test_get_callable_tools_with_prefixing (line 192) | def test_get_callable_tools_with_prefixing(self, temp_test_dir, skip_i...
method test_get_specific_tool (line 212) | def test_get_specific_tool(self, temp_test_dir, skip_if_no_npx):
class TestMCPClientFromConfig (line 243) | class TestMCPClientFromConfig:
method test_from_config_stdio (line 246) | def test_from_config_stdio(self, temp_test_dir, skip_if_no_npx):
method test_from_config_with_env (line 265) | def test_from_config_with_env(self, temp_test_dir, skip_if_no_npx):
method test_get_tools_from_config (line 286) | def test_get_tools_from_config(self, temp_test_dir, skip_if_no_npx):
class TestMCPClientErrorHandling (line 314) | class TestMCPClientErrorHandling:
method test_invalid_command_raises_error (line 317) | def test_invalid_command_raises_error(self, temp_test_dir):
method test_call_nonexistent_tool_returns_error (line 326) | def test_call_nonexistent_tool_returns_error(self, temp_test_dir, skip...
FILE: tests/mcp/test_e2e.py
function create_mock_response (line 22) | def create_mock_response(content="Test response", tool_calls=None):
class TestMCPConfigDictFormat (line 37) | class TestMCPConfigDictFormat:
method test_basic_config_dict (line 40) | def test_basic_config_dict(self, temp_test_dir, skip_if_no_npx):
method test_config_dict_with_allowed_tools (line 78) | def test_config_dict_with_allowed_tools(self, temp_test_dir, skip_if_n...
method test_config_dict_with_prefixing (line 113) | def test_config_dict_with_prefixing(self, temp_test_dir, skip_if_no_npx):
class TestMCPWithPythonFunctions (line 148) | class TestMCPWithPythonFunctions:
method test_mix_mcp_and_python_functions (line 151) | def test_mix_mcp_and_python_functions(self, temp_test_dir, skip_if_no_...
class TestMultipleMCPServers (line 199) | class TestMultipleMCPServers:
method test_multiple_servers_with_prefixing (line 202) | def test_multiple_servers_with_prefixing(self, temp_test_dir, skip_if_...
class TestMCPCleanup (line 263) | class TestMCPCleanup:
method test_cleanup_after_success (line 266) | def test_cleanup_after_success(self, temp_test_dir, skip_if_no_npx):
method test_cleanup_after_error (line 301) | def test_cleanup_after_error(self, temp_test_dir, skip_if_no_npx):
class TestMCPErrorHandling (line 340) | class TestMCPErrorHandling:
method test_invalid_mcp_config_raises_error (line 343) | def test_invalid_mcp_config_raises_error(self):
method test_mcp_not_installed_raises_error (line 362) | def test_mcp_not_installed_raises_error(self, temp_test_dir, skip_if_n...
FILE: tests/mcp/test_http_llm_e2e.py
function has_openai_key (line 45) | def has_openai_key():
function has_anthropic_key (line 50) | def has_anthropic_key():
function has_exa_key (line 55) | def has_exa_key():
class TestOpenAIWithHTTPMCP (line 62) | class TestOpenAIWithHTTPMCP:
method test_gpt4o_resolves_library_via_http_mcp (line 66) | def test_gpt4o_resolves_library_via_http_mcp(self):
method test_gpt4o_gets_library_docs_via_http_mcp (line 98) | def test_gpt4o_gets_library_docs_via_http_mcp(self):
method test_gpt4o_mixed_tools_http (line 131) | def test_gpt4o_mixed_tools_http(self):
class TestAnthropicWithHTTPMCP (line 177) | class TestAnthropicWithHTTPMCP:
method test_claude_resolves_library_via_http_mcp (line 181) | def test_claude_resolves_library_via_http_mcp(self):
method test_claude_gets_library_docs_via_http_mcp (line 212) | def test_claude_gets_library_docs_via_http_mcp(self):
method test_claude_mixed_tools_http (line 244) | def test_claude_mixed_tools_http(self):
class TestHTTPMCPConfigDict (line 285) | class TestHTTPMCPConfigDict:
method test_http_mcp_config_dict_format (line 289) | def test_http_mcp_config_dict_format(self):
class TestHTTPMCPWithHeaders (line 324) | class TestHTTPMCPWithHeaders:
method test_http_mcp_with_headers (line 328) | def test_http_mcp_with_headers(self):
class TestOpenAIWithExaMCP (line 364) | class TestOpenAIWithExaMCP:
method test_gpt4o_web_search_via_exa (line 371) | def test_gpt4o_web_search_via_exa(self):
method test_gpt4o_code_context_via_exa (line 408) | def test_gpt4o_code_context_via_exa(self):
method test_gpt4o_mixed_tools_with_exa (line 445) | def test_gpt4o_mixed_tools_with_exa(self):
class TestAnthropicWithExaMCP (line 493) | class TestAnthropicWithExaMCP:
method test_claude_web_search_via_exa (line 500) | def test_claude_web_search_via_exa(self):
method test_claude_code_context_via_exa (line 537) | def test_claude_code_context_via_exa(self):
method test_claude_mixed_tools_with_exa (line 574) | def test_claude_mixed_tools_with_exa(self):
FILE: tests/mcp/test_http_transport.py
class TestHTTPTransportBasics (line 16) | class TestHTTPTransportBasics:
method test_create_http_client_success (line 19) | def test_create_http_client_success(self):
method test_create_http_client_with_headers (line 81) | def test_create_http_client_with_headers(self):
method test_http_client_validation_errors (line 122) | def test_http_client_validation_errors(self):
class TestHTTPToolCalling (line 134) | class TestHTTPToolCalling:
method test_list_tools_http (line 137) | def test_list_tools_http(self):
method test_call_tool_http (line 188) | def test_call_tool_http(self):
method test_get_callable_tools_http (line 251) | def test_get_callable_tools_http(self):
class TestHTTPFromConfig (line 299) | class TestHTTPFromConfig:
method test_from_config_http (line 302) | def test_from_config_http(self):
method test_get_tools_from_config_http (line 348) | def test_get_tools_from_config_http(self):
class TestHTTPErrorHandling (line 397) | class TestHTTPErrorHandling:
method test_http_connection_error (line 400) | def test_http_connection_error(self):
method test_http_json_rpc_error (line 414) | def test_http_json_rpc_error(self):
method test_http_status_error (line 435) | def test_http_status_error(self):
class TestHTTPEndpointHandling (line 455) | class TestHTTPEndpointHandling:
method test_endpoint_uses_exact_url (line 458) | def test_endpoint_uses_exact_url(self):
method test_endpoint_trailing_slash_handled (line 502) | def test_endpoint_trailing_slash_handled(self):
class TestHTTPSSEResponses (line 542) | class TestHTTPSSEResponses:
method test_sse_response_parsing (line 545) | def test_sse_response_parsing(self):
method test_session_id_management (line 608) | def test_session_id_management(self):
method test_sse_with_multiple_events (line 660) | def test_sse_with_multiple_events(self):
method test_mixed_json_and_sse_responses (line 721) | def test_mixed_json_and_sse_responses(self):
FILE: tests/mcp/test_llm_e2e.py
function has_openai_key (line 35) | def has_openai_key():
function has_anthropic_key (line 40) | def has_anthropic_key():
class TestOpenAIWithMCP (line 47) | class TestOpenAIWithMCP:
method test_gpt4o_reads_file_via_mcp (line 51) | def test_gpt4o_reads_file_via_mcp(self, temp_test_dir, skip_if_no_npx):
method test_gpt4o_lists_files_via_mcp (line 106) | def test_gpt4o_lists_files_via_mcp(self, temp_test_dir, skip_if_no_npx):
method test_gpt4o_mixed_tools (line 142) | def test_gpt4o_mixed_tools(self, temp_test_dir, skip_if_no_npx):
class TestAnthropicWithMCP (line 193) | class TestAnthropicWithMCP:
method test_claude_reads_file_via_mcp (line 197) | def test_claude_reads_file_via_mcp(self, temp_test_dir, skip_if_no_npx):
method test_claude_lists_files_via_mcp (line 232) | def test_claude_lists_files_via_mcp(self, temp_test_dir, skip_if_no_npx):
method test_claude_mixed_tools (line 267) | def test_claude_mixed_tools(self, temp_test_dir, skip_if_no_npx):
class TestToolPrefixingWithLLM (line 320) | class TestToolPrefixingWithLLM:
method test_multiple_mcp_servers_with_prefixing (line 324) | def test_multiple_mcp_servers_with_prefixing(self, temp_test_dir, skip...
FILE: tests/providers/test_anthropic_converter.py
class TestAnthropicMessageConverter (line 9) | class TestAnthropicMessageConverter(unittest.TestCase):
method setUp (line 12) | def setUp(self):
method test_convert_request_single_user_message (line 16) | def test_convert_request_single_user_message(self):
method test_convert_request_with_system_message (line 26) | def test_convert_request_with_system_message(self):
method test_convert_request_with_tool_use_message (line 39) | def test_convert_request_with_tool_use_message(self):
method test_convert_response_normal_message (line 63) | def test_convert_response_normal_message(self):
method test_convert_response_with_tool_use (line 85) | def test_convert_response_with_tool_use(self):
method test_convert_tool_spec (line 126) | def test_convert_tool_spec(self):
method test_convert_request_with_tool_call_and_result (line 161) | def test_convert_request_with_tool_call_and_result(self):
FILE: tests/providers/test_asr_parameter_passthrough.py
function set_env_vars (line 14) | def set_env_vars(monkeypatch):
class TestOpenAIParameterPassthrough (line 23) | class TestOpenAIParameterPassthrough:
method test_language_param_passthrough (line 26) | def test_language_param_passthrough(self):
method test_temperature_param_passthrough (line 48) | def test_temperature_param_passthrough(self):
method test_response_format_param_passthrough (line 68) | def test_response_format_param_passthrough(self):
method test_multiple_params_passthrough (line 88) | def test_multiple_params_passthrough(self):
method test_file_object_with_params (line 113) | def test_file_object_with_params(self):
class TestGoogleParameterPassthrough (line 136) | class TestGoogleParameterPassthrough:
method test_language_code_param_passthrough (line 140) | def test_language_code_param_passthrough(self, mock_vertexai_init):
method test_enable_automatic_punctuation_passthrough (line 168) | def test_enable_automatic_punctuation_passthrough(self, mock_vertexai_...
method test_speech_contexts_passthrough (line 196) | def test_speech_contexts_passthrough(self, mock_vertexai_init):
FILE: tests/providers/test_aws_converter.py
class TestBedrockMessageConverter (line 8) | class TestBedrockMessageConverter(unittest.TestCase):
method setUp (line 10) | def setUp(self):
method test_convert_request_user_message (line 13) | def test_convert_request_user_message(self):
method test_convert_request_tool_result (line 27) | def test_convert_request_tool_result(self):
method test_convert_response_tool_call (line 59) | def test_convert_response_tool_call(self):
method test_convert_response_text (line 86) | def test_convert_response_text(self):
FILE: tests/providers/test_azure_provider.py
class TestAzureMessageConverter (line 7) | class TestAzureMessageConverter(unittest.TestCase):
method setUp (line 8) | def setUp(self):
method test_convert_request_dict_message (line 11) | def test_convert_request_dict_message(self):
method test_convert_request_message_object (line 19) | def test_convert_request_message_object(self):
method test_convert_response_basic (line 33) | def test_convert_response_basic(self):
method test_convert_response_with_tool_calls (line 54) | def test_convert_response_with_tool_calls(self):
FILE: tests/providers/test_cerebras_provider.py
function set_api_key_env_var (line 11) | def set_api_key_env_var(monkeypatch):
function test_cerebras_provider (line 16) | def test_cerebras_provider():
function test_cerebras_provider_with_usage (line 51) | def test_cerebras_provider_with_usage():
FILE: tests/providers/test_cohere_provider.py
function set_api_key_env_var (line 9) | def set_api_key_env_var(monkeypatch):
function test_cohere_provider (line 14) | def test_cohere_provider():
FILE: tests/providers/test_deepgram_provider.py
function set_api_key_env_var (line 18) | def set_api_key_env_var(monkeypatch):
function deepgram_provider (line 24) | def deepgram_provider():
function mock_deepgram_response (line 30) | def mock_deepgram_response():
class TestDeepgramProvider (line 63) | class TestDeepgramProvider:
method test_provider_initialization (line 66) | def test_provider_initialization(self, deepgram_provider):
method test_chat_completions_create_not_implemented (line 74) | def test_chat_completions_create_not_implemented(self, deepgram_provid...
method test_audio_transcriptions_create_success (line 82) | def test_audio_transcriptions_create_success(
method test_audio_transcriptions_create_with_file_object (line 106) | def test_audio_transcriptions_create_with_file_object(
method test_audio_transcriptions_create_with_options (line 128) | def test_audio_transcriptions_create_with_options(
method test_audio_transcriptions_create_error_handling (line 157) | def test_audio_transcriptions_create_error_handling(self, deepgram_pro...
method test_audio_transcriptions_create_stream_output (line 174) | async def test_audio_transcriptions_create_stream_output(self, deepgra...
method test_parse_deepgram_response_complete (line 214) | def test_parse_deepgram_response_complete(
method test_parse_deepgram_response_empty_channels (line 233) | def test_parse_deepgram_response_empty_channels(self, deepgram_provider):
FILE: tests/providers/test_deepseek_provider.py
function set_api_key_env_var (line 11) | def set_api_key_env_var(monkeypatch):
function test_deepseek_provider (line 16) | def test_deepseek_provider():
function test_deepseek_provider_with_usage (line 58) | def test_deepseek_provider_with_usage():
FILE: tests/providers/test_google_converter.py
class TestGoogleMessageConverter (line 8) | class TestGoogleMessageConverter(unittest.TestCase):
method setUp (line 10) | def setUp(self):
method test_convert_request_user_message (line 13) | def test_convert_request_user_message(self):
method test_convert_request_tool_result_message (line 23) | def test_convert_request_tool_result_message(self):
method test_convert_request_assistant_message (line 40) | def test_convert_request_assistant_message(self):
method test_convert_response_with_function_call (line 56) | def test_convert_response_with_function_call(self):
method test_convert_response_with_text (line 86) | def test_convert_response_with_text(self):
FILE: tests/providers/test_google_provider.py
function set_api_key_env_var (line 20) | def set_api_key_env_var(monkeypatch):
function mock_google_speech_response (line 28) | def mock_google_speech_response():
function test_missing_env_vars (line 51) | def test_missing_env_vars():
function test_vertex_interface (line 61) | def test_vertex_interface():
function test_convert_openai_to_vertex_ai (line 147) | def test_convert_openai_to_vertex_ai():
function test_role_conversions (line 164) | def test_role_conversions():
class TestGoogleProvider (line 188) | class TestGoogleProvider:
method test_provider_initialization (line 191) | def test_provider_initialization(self):
class TestGoogleASR (line 199) | class TestGoogleASR:
method test_audio_transcriptions_create_success (line 202) | def test_audio_transcriptions_create_success(self, mock_google_speech_...
method test_audio_transcriptions_create_with_file_object (line 219) | def test_audio_transcriptions_create_with_file_object(
method test_audio_transcriptions_create_with_options (line 237) | def test_audio_transcriptions_create_with_options(
method test_audio_transcriptions_create_error_handling (line 261) | def test_audio_transcriptions_create_error_handling(self):
method test_audio_transcriptions_create_stream_output (line 277) | async def test_audio_transcriptions_create_stream_output(
method test_parse_google_response_complete (line 315) | def test_parse_google_response_complete(self, mock_google_speech_respo...
method test_parse_google_response_empty_results (line 335) | def test_parse_google_response_empty_results(self):
FILE: tests/providers/test_groq_provider.py
function set_api_key_env_var (line 11) | def set_api_key_env_var(monkeypatch):
function test_groq_provider (line 16) | def test_groq_provider():
function test_groq_provider_with_usage (line 51) | def test_groq_provider_with_usage():
FILE: tests/providers/test_huggingface_provider.py
function set_api_key_env_var (line 14) | def set_api_key_env_var(monkeypatch):
function huggingface_provider (line 20) | def huggingface_provider():
function mock_huggingface_response (line 26) | def mock_huggingface_response():
function mock_huggingface_response_text_only (line 42) | def mock_huggingface_response_text_only():
class TestHuggingFaceProvider (line 47) | class TestHuggingFaceProvider:
method test_provider_initialization (line 50) | def test_provider_initialization(self, huggingface_provider):
method test_audio_transcriptions_create_success (line 58) | def test_audio_transcriptions_create_success(
method test_audio_transcriptions_with_file_object (line 94) | def test_audio_transcriptions_with_file_object(
method test_audio_transcriptions_content_type_detection (line 114) | def test_audio_transcriptions_content_type_detection(self, huggingface...
method test_audio_transcriptions_retry_503 (line 140) | def test_audio_transcriptions_retry_503(self, huggingface_provider):
method test_audio_transcriptions_error_handling (line 176) | def test_audio_transcriptions_error_handling(self, huggingface_provider):
method test_parse_response_standard_format (line 190) | def test_parse_response_standard_format(
method test_parse_response_text_only (line 205) | def test_parse_response_text_only(
method test_parse_response_string_format (line 217) | def test_parse_response_string_format(self, huggingface_provider):
method test_model_id_extraction (line 227) | def test_model_id_extraction(self, huggingface_provider):
FILE: tests/providers/test_inception_provider.py
function set_api_key_env_var (line 9) | def set_api_key_env_var(monkeypatch):
function test_inception_provider (line 14) | def test_inception_provider():
FILE: tests/providers/test_lmstudio_provider.py
function set_api_url_var (line 7) | def set_api_url_var(monkeypatch):
function test_completion (line 12) | def test_completion():
FILE: tests/providers/test_mistral_provider.py
function set_api_key_env_var (line 8) | def set_api_key_env_var(monkeypatch):
function test_mistral_provider (line 13) | def test_mistral_provider():
function test_mistral_provider_with_usage (line 46) | def test_mistral_provider_with_usage():
FILE: tests/providers/test_nebius_provider.py
function set_api_key_env_var (line 8) | def set_api_key_env_var(monkeypatch):
function test_nebius_provider (line 13) | def test_nebius_provider():
FILE: tests/providers/test_ollama_provider.py
function set_api_url_var (line 7) | def set_api_url_var(monkeypatch):
function test_completion (line 12) | def test_completion():
FILE: tests/providers/test_openai_provider.py
function set_api_key_env_var (line 20) | def set_api_key_env_var(monkeypatch):
function openai_provider (line 26) | def openai_provider():
function mock_openai_response (line 32) | def mock_openai_response():
class TestOpenAIProvider (line 41) | class TestOpenAIProvider:
method test_provider_initialization (line 44) | def test_provider_initialization(self, openai_provider):
class TestOpenAIASR (line 52) | class TestOpenAIASR:
method test_audio_transcriptions_create_success (line 55) | def test_audio_transcriptions_create_success(
method test_audio_transcriptions_create_with_file_object (line 74) | def test_audio_transcriptions_create_with_file_object(
method test_audio_transcriptions_create_with_kwargs (line 92) | def test_audio_transcriptions_create_with_kwargs(
method test_audio_transcriptions_create_with_options (line 117) | def test_audio_transcriptions_create_with_options(
method test_audio_transcriptions_create_error_handling (line 147) | def test_audio_transcriptions_create_error_handling(self, openai_provi...
method test_audio_transcriptions_create_stream_output (line 162) | async def test_audio_transcriptions_create_stream_output(self, openai_...
method test_timestamp_granularities_error_handling (line 198) | async def test_timestamp_granularities_error_handling(self, openai_pro...
method test_parse_openai_response_with_segments_and_words (line 221) | def test_parse_openai_response_with_segments_and_words(self, openai_pr...
method test_parse_openai_response_empty (line 244) | def test_parse_openai_response_empty(self, openai_provider):
FILE: tests/providers/test_sambanova_provider.py
function set_api_key_env_var (line 9) | def set_api_key_env_var(monkeypatch):
function test_sambanova_provider (line 14) | def test_sambanova_provider():
function test_sambanova_provider_with_usage (line 51) | def test_sambanova_provider_with_usage():
FILE: tests/providers/test_watsonx_provider.py
function set_api_key_env_var (line 14) | def set_api_key_env_var(monkeypatch):
function test_watsonx_provider (line 22) | def test_watsonx_provider():
FILE: tests/test_provider.py
class MockProvider (line 15) | class MockProvider(Provider):
method chat_completions_create (line 18) | def chat_completions_create(self, model, messages):
class MockTranscription (line 22) | class MockTranscription(Audio.Transcription):
method create (line 25) | def create(
class MockAudio (line 37) | class MockAudio(Audio):
method __init__ (line 40) | def __init__(self):
class MockASRProvider (line 45) | class MockASRProvider(Provider):
method __init__ (line 48) | def __init__(self):
method chat_completions_create (line 52) | def chat_completions_create(self, model, messages):
class TestProvider (line 56) | class TestProvider:
method test_provider_is_abstract (line 59) | def test_provider_is_abstract(self):
method test_provider_without_audio_support (line 64) | def test_provider_without_audio_support(self):
method test_provider_asr_implementation_works (line 69) | def test_provider_asr_implementation_works(self):
method test_transcription_base_class_not_implemented (line 83) | def test_transcription_base_class_not_implemented(self):
method test_audio_base_class_initialization (line 90) | def test_audio_base_class_initialization(self):
class TestASRError (line 96) | class TestASRError:
method test_asr_error_creation_and_inheritance (line 99) | def test_asr_error_creation_and_inheritance(self):
method test_asr_error_raising_and_catching (line 107) | def test_asr_error_raising_and_catching(self):
method test_asr_error_chaining (line 116) | def test_asr_error_chaining(self):
FILE: tests/utils/test_mcp_memory_integration.py
class MockMemoryMCPTool (line 11) | class MockMemoryMCPTool:
method __init__ (line 14) | def __init__(self):
method __call__ (line 50) | def __call__(self, **kwargs):
class TestMCPMemoryIntegration (line 55) | class TestMCPMemoryIntegration(unittest.TestCase):
method test_memory_create_entities_schema (line 58) | def test_memory_create_entities_schema(self):
method test_memory_tool_openai_format_validation (line 88) | def test_memory_tool_openai_format_validation(self):
method test_memory_tool_execution (line 112) | def test_memory_tool_execution(self):
FILE: tests/utils/test_tool_manager.py
class TemperatureUnit (line 9) | class TemperatureUnit(str, Enum):
class TemperatureParamsV2 (line 14) | class TemperatureParamsV2(BaseModel):
class TemperatureParams (line 19) | class TemperatureParams(BaseModel):
function get_current_temperature (line 24) | def get_current_temperature(location: str, unit: str = "Celsius") -> Dic...
function missing_annotation_tool (line 29) | def missing_annotation_tool(location, unit="Celsius"):
function get_current_temperature_v2 (line 34) | def get_current_temperature_v2(
class TestToolManager (line 41) | class TestToolManager(unittest.TestCase):
method setUp (line 42) | def setUp(self):
method test_add_tool_with_pydantic_model (line 45) | def test_add_tool_with_pydantic_model(self):
method test_add_tool_with_signature_inference (line 82) | def test_add_tool_with_signature_inference(self):
method test_add_tool_missing_annotation_raises_exception (line 119) | def test_add_tool_missing_annotation_raises_exception(self):
method test_execute_tool_valid_parameters (line 124) | def test_execute_tool_valid_parameters(self):
method test_execute_tool_invalid_parameters (line 144) | def test_execute_tool_invalid_parameters(self):
method test_add_tool_with_enum (line 163) | def test_add_tool_with_enum(self):
FILE: tests/utils/test_tools_mcp_schema.py
class MockMCPToolWrapper (line 6) | class MockMCPToolWrapper:
method __init__ (line 9) | def __init__(self, name: str, description: str, input_schema: Dict[str...
method __call__ (line 14) | def __call__(self, **kwargs):
class TestToolsMCPSchema (line 19) | class TestToolsMCPSchema(unittest.TestCase):
method setUp (line 22) | def setUp(self):
method test_mcp_tool_with_simple_types (line 26) | def test_mcp_tool_with_simple_types(self):
method test_mcp_tool_with_array_of_objects (line 50) | def test_mcp_tool_with_array_of_objects(self):
method test_mcp_tool_with_nested_objects (line 88) | def test_mcp_tool_with_nested_objects(self):
method test_mcp_tool_with_array_of_strings (line 124) | def test_mcp_tool_with_array_of_strings(self):
method test_mcp_tool_detection (line 150) | def test_mcp_tool_detection(self):
method test_mcp_tool_with_optional_parameters (line 164) | def test_mcp_tool_with_optional_parameters(self):
method test_mcp_schema_preserves_additional_fields (line 194) | def test_mcp_schema_preserves_additional_fields(self):
method test_mcp_tool_execution_with_validation (line 244) | def test_mcp_tool_execution_with_validation(self):
method test_mcp_tool_with_empty_schema (line 273) | def test_mcp_tool_with_empty_schema(self):
method test_multiple_mcp_tools (line 286) | def test_multiple_mcp_tools(self):
method test_mcp_tool_schema_not_modified (line 313) | def test_mcp_tool_schema_not_modified(self):
method test_backward_compatibility_non_mcp_tools (line 342) | def test_backward_compatibility_non_mcp_tools(self):
Condensed preview — 208 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,137K chars).
[
{
"path": ".github/workflows/black.yml",
"chars": 154,
"preview": "name: Lint\n\non: [push, pull_request]\n\njobs:\n lint:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout"
},
{
"path": ".github/workflows/run_pytest.yml",
"chars": 636,
"preview": "name: Lint\n\non: [push, pull_request]\n\njobs:\n build_and_test:\n runs-on: ubuntu-latest\n strategy:\n matrix:\n "
},
{
"path": ".gitignore",
"chars": 685,
"preview": "# Python\n__pycache__/\n*.py[cod]\n*$py.class\n*.so\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\np"
},
{
"path": ".pre-commit-config.yaml",
"chars": 480,
"preview": "repos:\n # Using this mirror lets us use mypyc-compiled black, which is about 2x faster\n - repo: https://github.com/psf"
},
{
"path": "CONTRIBUTING.md",
"chars": 10168,
"preview": "<!-- omit in toc -->\n# Contributing to aisuite\n\nFirst off, thanks for taking the time to contribute!\n\nAll types of contr"
},
{
"path": "LICENSE",
"chars": 1066,
"preview": "MIT License\n\nCopyright (c) 2024 Andrew Ng\n\nPermission is hereby granted, free of charge, to any person obtaining a copy "
},
{
"path": "README.md",
"chars": 11541,
"preview": "# aisuite\n\n[](https://pypi.org/project/aisuite/)\n[: Remove this. This interface is obsolete in favor of Pro"
},
{
"path": "aisuite/mcp/__init__.py",
"chars": 1059,
"preview": "\"\"\"\nMCP (Model Context Protocol) integration for aisuite.\n\nThis module provides support for using MCP servers and their "
},
{
"path": "aisuite/mcp/client.py",
"chars": 28125,
"preview": "\"\"\"\nMCP Client for aisuite.\n\nThis module provides the MCPClient class that connects to MCP servers and\nexposes their too"
},
{
"path": "aisuite/mcp/config.py",
"chars": 8162,
"preview": "\"\"\"\nMCP configuration validation and normalization.\n\nThis module provides utilities for validating and normalizing MCP t"
},
{
"path": "aisuite/mcp/schema_converter.py",
"chars": 5843,
"preview": "\"\"\"\nSchema conversion utilities for MCP tools.\n\nThis module provides functionality to convert MCP JSON Schema tool defin"
},
{
"path": "aisuite/mcp/tool_wrapper.py",
"chars": 5469,
"preview": "\"\"\"\nMCP Tool Wrapper for aisuite.\n\nThis module provides the MCPToolWrapper class, which creates Python callable\nwrappers"
},
{
"path": "aisuite/provider.py",
"chars": 3097,
"preview": "from abc import ABC, abstractmethod\nfrom pathlib import Path\nimport importlib\nimport os\nimport functools\nfrom typing imp"
},
{
"path": "aisuite/providers/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "aisuite/providers/anthropic_provider.py",
"chars": 9045,
"preview": "# Anthropic provider\n# Links:\n# Tool calling docs - https://docs.anthropic.com/en/docs/build-with-claude/tool-use\n\nimpor"
},
{
"path": "aisuite/providers/aws_provider.py",
"chars": 10543,
"preview": "\"\"\"AWS Bedrock provider for the aisuite.\"\"\"\n\nimport os\nimport json\nfrom typing import List, Dict, Any, Tuple, Optional\n\n"
},
{
"path": "aisuite/providers/azure_provider.py",
"chars": 5389,
"preview": "import urllib.request\nimport json\nimport os\n\nfrom aisuite.provider import Provider\nfrom aisuite.framework import ChatCom"
},
{
"path": "aisuite/providers/cerebras_provider.py",
"chars": 1464,
"preview": "\"\"\"Cerebras provider for the aisuite.\"\"\"\n\nimport cerebras.cloud.sdk as cerebras\nfrom aisuite.provider import Provider, L"
},
{
"path": "aisuite/providers/cohere_provider.py",
"chars": 6471,
"preview": "import os\nimport cohere\nimport json\nfrom aisuite.framework import ChatCompletionResponse\nfrom aisuite.framework.message "
},
{
"path": "aisuite/providers/deepgram_provider.py",
"chars": 17457,
"preview": "import os\nimport json\nimport numpy as np\nimport queue\nimport threading\nimport time\nfrom typing import Union, BinaryIO, A"
},
{
"path": "aisuite/providers/deepseek_provider.py",
"chars": 2136,
"preview": "\"\"\"Deepseek provider for the aisuite.\"\"\"\n\nimport os\nimport openai\nfrom aisuite.provider import Provider, LLMError\nfrom a"
},
{
"path": "aisuite/providers/fireworks_provider.py",
"chars": 5415,
"preview": "import os\nimport httpx\nimport json\nfrom aisuite.provider import Provider, LLMError\nfrom aisuite.framework import ChatCom"
},
{
"path": "aisuite/providers/google_provider.py",
"chars": 21966,
"preview": "\"\"\"The interface to Google's Vertex AI.\"\"\"\n\nimport os\nimport json\nfrom typing import List, Dict, Any, Optional, Union, B"
},
{
"path": "aisuite/providers/groq_provider.py",
"chars": 2318,
"preview": "import os\nimport groq\nfrom aisuite.provider import Provider, LLMError\nfrom aisuite.providers.message_converter import Op"
},
{
"path": "aisuite/providers/huggingface_provider.py",
"chars": 11736,
"preview": "import os\nimport json\nimport time\nfrom typing import Union, BinaryIO\nimport requests\nfrom huggingface_hub import Inferen"
},
{
"path": "aisuite/providers/inception_provider.py",
"chars": 1443,
"preview": "import openai\nimport os\nfrom aisuite.provider import Provider, LLMError\n\n\nclass InceptionProvider(Provider):\n def __i"
},
{
"path": "aisuite/providers/lmstudio_provider.py",
"chars": 2465,
"preview": "import os\nimport httpx\nfrom aisuite.provider import Provider, LLMError\nfrom aisuite.framework import ChatCompletionRespo"
},
{
"path": "aisuite/providers/message_converter.py",
"chars": 3339,
"preview": "\"\"\"Base message converter for OpenAI-compliant providers.\"\"\"\n\nfrom aisuite.framework import ChatCompletionResponse\nfrom "
},
{
"path": "aisuite/providers/mistral_provider.py",
"chars": 2620,
"preview": "\"\"\"Mistral provider for the aisuite.\"\"\"\n\nimport os\nfrom mistralai import Mistral\nfrom aisuite.framework import ChatCompl"
},
{
"path": "aisuite/providers/nebius_provider.py",
"chars": 1298,
"preview": "import os\nfrom aisuite.provider import Provider\nfrom openai import Client\n\n\nBASE_URL = \"https://api.studio.nebius.ai/v1\""
},
{
"path": "aisuite/providers/ollama_provider.py",
"chars": 2391,
"preview": "import os\nimport httpx\nfrom aisuite.provider import Provider, LLMError\nfrom aisuite.framework import ChatCompletionRespo"
},
{
"path": "aisuite/providers/openai_provider.py",
"chars": 10096,
"preview": "import openai\nimport os\nfrom typing import Union, BinaryIO, AsyncGenerator\nfrom aisuite.provider import Provider, LLMErr"
},
{
"path": "aisuite/providers/sambanova_provider.py",
"chars": 1965,
"preview": "import os\nfrom aisuite.provider import Provider, LLMError\nfrom openai import OpenAI\nfrom aisuite.providers.message_conve"
},
{
"path": "aisuite/providers/together_provider.py",
"chars": 2220,
"preview": "import os\nimport httpx\nfrom aisuite.provider import Provider, LLMError\nfrom aisuite.providers.message_converter import O"
},
{
"path": "aisuite/providers/watsonx_provider.py",
"chars": 1531,
"preview": "from aisuite.provider import Provider\nimport os\nfrom ibm_watsonx_ai import Credentials\nfrom ibm_watsonx_ai.foundation_mo"
},
{
"path": "aisuite/providers/xai_provider.py",
"chars": 2193,
"preview": "import os\nimport httpx\nfrom aisuite.provider import Provider, LLMError\nfrom aisuite.framework import ChatCompletionRespo"
},
{
"path": "aisuite/utils/tools.py",
"chars": 14480,
"preview": "from typing import Callable, Dict, Any, Type, Optional, get_origin, get_args, Union\nfrom pydantic import BaseModel, crea"
},
{
"path": "aisuite/utils/utils.py",
"chars": 1987,
"preview": "\"\"\"Utility functions for aisuite.\"\"\"\n\nimport json\nfrom unittest.mock import MagicMock\nfrom pydantic import BaseModel\n\n\n#"
},
{
"path": "aisuite-js/README.md",
"chars": 8015,
"preview": "# AISuite\n\nAISuite is a unified TypeScript library that provides a single, consistent interface for interacting with mul"
},
{
"path": "aisuite-js/examples/basic-usage.ts",
"chars": 2116,
"preview": "import 'dotenv/config';\nimport { Client } from '../src';\n\nasync function main() {\n // Initialize the client with API ke"
},
{
"path": "aisuite-js/examples/chat-app/.eslintrc.cjs",
"chars": 429,
"preview": "module.exports = {\n root: true,\n env: { browser: true, es2020: true },\n extends: [\n 'eslint:recommended',\n '@ty"
},
{
"path": "aisuite-js/examples/chat-app/.gitignore",
"chars": 355,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
},
{
"path": "aisuite-js/examples/chat-app/README.md",
"chars": 4638,
"preview": "# AISuite Chat App\n\nA modern React TypeScript chat application built with AISuite, allowing you to chat with multiple AI"
},
{
"path": "aisuite-js/examples/chat-app/index.html",
"chars": 365,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"/"
},
{
"path": "aisuite-js/examples/chat-app/package.json",
"chars": 978,
"preview": "{\n \"name\": \"aisuite-chat-app\",\n \"version\": \"1.0.0\",\n \"description\": \"A React TypeScript chat application using AISuit"
},
{
"path": "aisuite-js/examples/chat-app/postcss.config.js",
"chars": 80,
"preview": "export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n} "
},
{
"path": "aisuite-js/examples/chat-app/src/App.tsx",
"chars": 12623,
"preview": "import React, { useState, useEffect } from 'react';\nimport { Settings, AlertCircle } from 'lucide-react';\nimport { Messa"
},
{
"path": "aisuite-js/examples/chat-app/src/components/ApiKeyModal.tsx",
"chars": 7109,
"preview": "import React, { useState } from 'react';\nimport { X, Eye, EyeOff } from 'lucide-react';\nimport { AISuiteConfig } from '."
},
{
"path": "aisuite-js/examples/chat-app/src/components/ChatContainer.tsx",
"chars": 2774,
"preview": "import React, { useRef, useEffect } from 'react';\nimport { Message } from '../types/chat';\nimport { ChatMessage } from '"
},
{
"path": "aisuite-js/examples/chat-app/src/components/ChatInput.tsx",
"chars": 2777,
"preview": "import React, { useState, KeyboardEvent } from 'react';\nimport { Send, RotateCcw } from 'lucide-react';\n\ninterface ChatI"
},
{
"path": "aisuite-js/examples/chat-app/src/components/ChatMessage.tsx",
"chars": 1645,
"preview": "import React from 'react';\nimport { Message } from '../types/chat';\nimport { User, Bot } from 'lucide-react';\n\ninterface"
},
{
"path": "aisuite-js/examples/chat-app/src/components/ModelSelector.tsx",
"chars": 1164,
"preview": "import React from 'react';\nimport { LLMConfig } from '../types/chat';\n\ninterface ModelSelectorProps {\n selectedModel: s"
},
{
"path": "aisuite-js/examples/chat-app/src/components/ProviderSelector.tsx",
"chars": 1632,
"preview": "import React from 'react';\n\ninterface ProviderSelectorProps {\n selectedProvider: string;\n onProviderChange: (provider:"
},
{
"path": "aisuite-js/examples/chat-app/src/config/llm-config.ts",
"chars": 1297,
"preview": "import { LLMConfig } from '../types/chat';\n\nexport const configuredLLMs: LLMConfig[] = [\n {\n name: \"OpenAI GPT-4o\",\n"
},
{
"path": "aisuite-js/examples/chat-app/src/index.css",
"chars": 2209,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n --f"
},
{
"path": "aisuite-js/examples/chat-app/src/main.tsx",
"chars": 236,
"preview": "import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App.tsx'\nimport './index.css'\n\nReac"
},
{
"path": "aisuite-js/examples/chat-app/src/services/aisuite-service.ts",
"chars": 1887,
"preview": "import { Client } from '../../../../src/client';\nimport { Message, LLMConfig, AISuiteConfig } from '../types/chat';\n\ncla"
},
{
"path": "aisuite-js/examples/chat-app/src/types/chat.ts",
"chars": 861,
"preview": "export interface Message {\n role: 'user' | 'assistant' | 'system';\n content: string;\n timestamp?: Date;\n}\n\nexport int"
},
{
"path": "aisuite-js/examples/chat-app/src/utils/cn.ts",
"chars": 166,
"preview": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: Cla"
},
{
"path": "aisuite-js/examples/chat-app/tailwind.config.js",
"chars": 1668,
"preview": "/** @type {import('tailwindcss').Config} */\nexport default {\n content: [\n \"./index.html\",\n \"./src/**/*.{js,ts,jsx"
},
{
"path": "aisuite-js/examples/chat-app/tsconfig.json",
"chars": 605,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"useDefineForClassFields\": true,\n \"lib\": [\"ES2020\", \"DOM\", \"DOM."
},
{
"path": "aisuite-js/examples/chat-app/tsconfig.node.json",
"chars": 213,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"skipLibCheck\": true,\n \"module\": \"ESNext\",\n \"moduleResolution\""
},
{
"path": "aisuite-js/examples/chat-app/vite.config.ts",
"chars": 210,
"preview": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport defau"
},
{
"path": "aisuite-js/examples/deepgram.ts",
"chars": 2208,
"preview": "import { Client } from \"../src\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\nasync function main() {\n // I"
},
{
"path": "aisuite-js/examples/groq.ts",
"chars": 6747,
"preview": "import \"dotenv/config\";\nimport { Client, ChatCompletionResponse, ChatMessage } from \"../src\";\n\n// Mock function for weat"
},
{
"path": "aisuite-js/examples/mistral.ts",
"chars": 7066,
"preview": "import \"dotenv/config\";\nimport { Client, ChatCompletionResponse, ChatMessage } from \"../src\";\n\n// Sample data store\ncons"
},
{
"path": "aisuite-js/examples/openai-asr.ts",
"chars": 1944,
"preview": "import { Client } from \"../src\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\nasync function main() {\n // I"
},
{
"path": "aisuite-js/examples/streaming.ts",
"chars": 6408,
"preview": "import 'dotenv/config';\nimport { Client, ChatCompletionChunk } from '../src';\n\nasync function main() {\n const client = "
},
{
"path": "aisuite-js/examples/test-suite.ts",
"chars": 10036,
"preview": "import 'dotenv/config';\nimport { Client, AISuiteError, ProviderNotConfiguredError, ChatCompletionChunk } from '../src';\n"
},
{
"path": "aisuite-js/examples/tool-calling.ts",
"chars": 5292,
"preview": "import 'dotenv/config';\nimport { Client, ChatMessage } from '../src';\n\n// Mock function for weather\nfunction getWeather("
},
{
"path": "aisuite-js/jest.config.ts",
"chars": 714,
"preview": "export default {\n preset: 'ts-jest',\n testEnvironment: 'node',\n roots: ['<rootDir>/tests'],\n testMatch: [\n '**/__"
},
{
"path": "aisuite-js/package.json",
"chars": 1872,
"preview": "{\n \"name\": \"aisuite\",\n \"version\": \"0.1.1\",\n \"description\": \"Unified TypeScript library for multiple LLM providers\",\n "
},
{
"path": "aisuite-js/src/asr-providers/deepgram/adapters.ts",
"chars": 1435,
"preview": "import { TranscriptionResult, Word, Segment } from \"../../types\";\n\nexport function adaptResponse(response: any): Transcr"
},
{
"path": "aisuite-js/src/asr-providers/deepgram/index.ts",
"chars": 97,
"preview": "export { DeepgramASRProvider } from \"./provider\";\nexport type { DeepgramConfig } from \"./types\";\n"
},
{
"path": "aisuite-js/src/asr-providers/deepgram/provider.ts",
"chars": 3349,
"preview": "import { createClient, DeepgramClient } from \"@deepgram/sdk\";\nimport { ASRProvider } from \"../../core/base-asr-provider\""
},
{
"path": "aisuite-js/src/asr-providers/deepgram/types.ts",
"chars": 74,
"preview": "export interface DeepgramConfig {\n apiKey: string;\n baseURL?: string;\n}\n"
},
{
"path": "aisuite-js/src/asr-providers/index.ts",
"chars": 98,
"preview": "export { DeepgramASRProvider } from \"./deepgram\";\nexport type { DeepgramConfig } from \"../types\";\n"
},
{
"path": "aisuite-js/src/client.ts",
"chars": 3808,
"preview": "import {\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatCompletionChunk,\n ProviderConfigs,\n RequestOptions,\n"
},
{
"path": "aisuite-js/src/core/base-asr-provider.ts",
"chars": 430,
"preview": "import { \n TranscriptionRequest, \n TranscriptionResult,\n RequestOptions \n} from '../types';\n\nexport interface ASRProv"
},
{
"path": "aisuite-js/src/core/base-provider.ts",
"chars": 435,
"preview": "import { \n ChatCompletionRequest, \n ChatCompletionResponse, \n ChatCompletionChunk,\n RequestOptions \n} from '../types"
},
{
"path": "aisuite-js/src/core/errors.ts",
"chars": 1441,
"preview": "export class AISuiteError extends Error {\n constructor(\n message: string,\n public provider: string,\n public co"
},
{
"path": "aisuite-js/src/core/model-parser.ts",
"chars": 539,
"preview": "import { InvalidModelFormatError } from './errors';\n\nexport interface ParsedModel {\n provider: string;\n model: string;"
},
{
"path": "aisuite-js/src/index.ts",
"chars": 345,
"preview": "export { Client } from \"./client\";\nexport * from \"./types\";\nexport * from \"./core/errors\";\nexport { parseModel } from \"."
},
{
"path": "aisuite-js/src/providers/anthropic/adapters.ts",
"chars": 4842,
"preview": "import { \n ChatCompletionRequest, \n ChatCompletionResponse, \n ChatCompletionChunk,\n ChatMessage,\n Tool,\n ToolCall\n"
},
{
"path": "aisuite-js/src/providers/anthropic/index.ts",
"chars": 95,
"preview": "export { AnthropicProvider } from './provider';\nexport type { AnthropicConfig } from './types';"
},
{
"path": "aisuite-js/src/providers/anthropic/provider.ts",
"chars": 2790,
"preview": "import Anthropic from '@anthropic-ai/sdk';\nimport { Provider } from '../../core/base-provider';\nimport { \n ChatCompleti"
},
{
"path": "aisuite-js/src/providers/anthropic/types.ts",
"chars": 235,
"preview": "import { AnthropicConfig } from '../../types';\n\nexport { AnthropicConfig };\n\n// Re-export Anthropic types that we need\ne"
},
{
"path": "aisuite-js/src/providers/groq/adapters.ts",
"chars": 1464,
"preview": "import type { ChatCompletion as GroqChatCompletion } from \"groq-sdk/resources/chat/completions\";\nimport {\n ChatCompleti"
},
{
"path": "aisuite-js/src/providers/groq/index.ts",
"chars": 86,
"preview": "export { GroqProvider } from \"./provider\";\nexport type { GroqConfig } from \"./types\";\n"
},
{
"path": "aisuite-js/src/providers/groq/provider.ts",
"chars": 2653,
"preview": "import Groq from \"groq-sdk\";\nimport { Provider } from \"../../core/base-provider\";\nimport {\n ChatCompletionRequest,\n Ch"
},
{
"path": "aisuite-js/src/providers/groq/types.ts",
"chars": 107,
"preview": "export interface GroqConfig {\n apiKey: string;\n baseURL?: string;\n dangerouslyAllowBrowser?: boolean;\n}\n"
},
{
"path": "aisuite-js/src/providers/index.ts",
"chars": 277,
"preview": "export { OpenAIProvider } from \"./openai\";\nexport { AnthropicProvider } from \"./anthropic\";\nexport { MistralProvider } f"
},
{
"path": "aisuite-js/src/providers/mistral/adapters.ts",
"chars": 2081,
"preview": "import {\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatCompletionChunk,\n ChatMessage,\n} from \"../../types\";\n"
},
{
"path": "aisuite-js/src/providers/mistral/index.ts",
"chars": 92,
"preview": "export { MistralProvider } from \"./provider\";\nexport type { MistralConfig } from \"./types\";\n"
},
{
"path": "aisuite-js/src/providers/mistral/provider.ts",
"chars": 2552,
"preview": "import MistralClient from \"@mistralai/mistralai\";\nimport { Provider } from \"../../core/base-provider\";\nimport {\n ChatCo"
},
{
"path": "aisuite-js/src/providers/mistral/types.ts",
"chars": 259,
"preview": "import { MistralConfig } from \"../../types\";\n\nexport { MistralConfig };\n\n// Re-export Mistral types that we need\nexport "
},
{
"path": "aisuite-js/src/providers/openai/adapters.ts",
"chars": 4250,
"preview": "import {\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatCompletionChunk,\n ChatMessage,\n ToolCall,\n Tool,\n "
},
{
"path": "aisuite-js/src/providers/openai/index.ts",
"chars": 89,
"preview": "export { OpenAIProvider } from './provider';\nexport type { OpenAIConfig } from './types';"
},
{
"path": "aisuite-js/src/providers/openai/provider.ts",
"chars": 3798,
"preview": "import OpenAI from \"openai\";\nimport { Provider } from \"../../core/base-provider\";\nimport { ASRProvider } from \"../../cor"
},
{
"path": "aisuite-js/src/providers/openai/types.ts",
"chars": 1024,
"preview": "import OpenAI from \"openai\";\nimport { OpenAIConfig } from \"../../types\";\n\nexport { OpenAIConfig };\n\n// Re-export OpenAI "
},
{
"path": "aisuite-js/src/types/chat.ts",
"chars": 1336,
"preview": "export interface ChatMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n name?: stri"
},
{
"path": "aisuite-js/src/types/common.ts",
"chars": 177,
"preview": "export interface RequestOptions {\n signal?: AbortSignal;\n timeout?: number;\n retries?: number;\n [key: string]: any;\n"
},
{
"path": "aisuite-js/src/types/index.ts",
"chars": 137,
"preview": "export * from \"./chat\";\nexport * from \"./tools\";\nexport * from \"./common\";\nexport * from \"./providers\";\nexport * from \"."
},
{
"path": "aisuite-js/src/types/providers.ts",
"chars": 564,
"preview": "export interface ProviderConfigs {\n openai?: OpenAIConfig;\n anthropic?: AnthropicConfig;\n mistral?: MistralConfig;\n "
},
{
"path": "aisuite-js/src/types/tools.ts",
"chars": 506,
"preview": "export interface Tool {\n type: 'function';\n function: FunctionDefinition;\n}\n\nexport interface FunctionDefinition {\n n"
},
{
"path": "aisuite-js/src/types/transcription.ts",
"chars": 663,
"preview": "export interface Word {\n text: string;\n start: number;\n end: number;\n speaker?: string;\n confidence?: number;\n}\n\nex"
},
{
"path": "aisuite-js/src/utils/streaming.ts",
"chars": 606,
"preview": "import { ChatCompletionChunk } from '../types';\n\nexport function createChunk(\n id: string,\n model: string,\n content?:"
},
{
"path": "aisuite-js/tests/client.test.ts",
"chars": 19946,
"preview": "import { Client } from \"../src/client\";\nimport {\n ProviderConfigs,\n ChatCompletionRequest,\n ChatCompletionResponse,\n "
},
{
"path": "aisuite-js/tests/providers/anthropic-provider.test.ts",
"chars": 11790,
"preview": "import { AnthropicProvider } from \"../../src/providers/anthropic/provider\";\nimport { ChatCompletionRequest, ChatCompleti"
},
{
"path": "aisuite-js/tests/providers/deepgram-provider.test.ts",
"chars": 15465,
"preview": "import { DeepgramASRProvider } from \"../../src/asr-providers/deepgram/provider\";\nimport { TranscriptionRequest } from \"."
},
{
"path": "aisuite-js/tests/providers/groq-provider.test.ts",
"chars": 12101,
"preview": "import { GroqProvider } from \"../../src/providers/groq/provider\";\nimport { ChatCompletionRequest, ChatCompletionChunk } "
},
{
"path": "aisuite-js/tests/providers/mistral-provider.test.ts",
"chars": 11177,
"preview": "import { MistralProvider } from \"../../src/providers/mistral/provider\";\nimport { ChatCompletionRequest, ChatCompletionCh"
},
{
"path": "aisuite-js/tests/providers/openai-provider.test.ts",
"chars": 10350,
"preview": "import { OpenAIProvider } from \"../../src/providers/openai/provider\";\nimport { ChatCompletionRequest, ChatCompletionChun"
},
{
"path": "aisuite-js/tests/providers/openai_asr_provider.test.ts",
"chars": 2239,
"preview": "import { TranscriptionRequest } from \"../../src/types\";\nimport { AISuiteError } from \"../../src/core/errors\";\nimport { O"
},
{
"path": "aisuite-js/tests/utils/streaming.test.ts",
"chars": 6318,
"preview": "import { createChunk, generateId } from \"../../src/utils/streaming\";\n\ndescribe(\"Streaming Utils\", () => {\n describe(\"cr"
},
{
"path": "aisuite-js/tsconfig.json",
"chars": 593,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"lib\": [\"ES2020\"],\n \"outDir\": \"./dist\",\n"
},
{
"path": "examples/AISuiteDemo.ipynb",
"chars": 33281,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"provenance\": []\n },\n \"kernelspec\":"
},
{
"path": "examples/DeepseekPost.ipynb",
"chars": 12422,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": 1,\n \"id\": \"5fe04b4f-7f26-44e9-a6cf-adc60d8b1a2a\",\n \""
},
{
"path": "examples/QnA_with_pdf.ipynb",
"chars": 7637,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": 1,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n "
},
{
"path": "examples/agents/movie_buff_assistant.ipynb",
"chars": 19931,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# 🎬 Movie Buff Assistant with aisui"
},
{
"path": "examples/agents/recipe_chef_assistant.ipynb",
"chars": 23315,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# 🍳 Recipe Chef Assistant with AI +"
},
{
"path": "examples/agents/snake_game_generator.ipynb",
"chars": 5847,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Snake Game Generator - AI Creates"
},
{
"path": "examples/agents/stock_dashboard.html",
"chars": 5108,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, "
},
{
"path": "examples/agents/stock_market_dashboard.html",
"chars": 4231,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width"
},
{
"path": "examples/agents/stock_market_mini_tracker.ipynb",
"chars": 8002,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Stock Market Tracker - Multi-step"
},
{
"path": "examples/agents/stock_market_tracker.ipynb",
"chars": 17958,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Stock Market Tracker - Latest Mar"
},
{
"path": "examples/agents/world_weather_dashboard.ipynb",
"chars": 6984,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# World Weather Dashboard - AI-Gene"
},
{
"path": "examples/aisuite_tool_abstraction.ipynb",
"chars": 8725,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": 8,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n "
},
{
"path": "examples/asr_example.ipynb",
"chars": 4050,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"id\": \"5a7a0ca2\",\n \"metadata\": {},\n \"source\": [\n \"# ASR Example "
},
{
"path": "examples/chat-ui/.streamlit/config.toml",
"chars": 242,
"preview": "[theme]\nprimaryColor = \"#1E90FF\" # Blue color for primary components\nbackgroundColor = \"#0e1117\" # Background color\nse"
},
{
"path": "examples/chat-ui/README.md",
"chars": 1201,
"preview": "# Chat UI\n\nThis is a simple chat UI built using Streamlit. It uses the `aisuite` library to power the chat.\n\nYou will ne"
},
{
"path": "examples/chat-ui/chat.py",
"chars": 8030,
"preview": "import os\nimport requests\nimport streamlit as st\nimport sys\nimport yaml\nfrom dotenv import load_dotenv, find_dotenv\n\nsys"
},
{
"path": "examples/chat-ui/config.yaml",
"chars": 375,
"preview": "# config.yaml\nllms:\n - name: \"OpenAI GPT-4o\"\n provider: \"openai\"\n model: \"gpt-4o\"\n - name: \"Anthropic Claude 3.5"
},
{
"path": "examples/client.ipynb",
"chars": 9052,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"id\": \"d34f8c48-90fc-4981-8d2b-b47724c2a6dd\",\n \"metadata\": {\n \"vsc"
},
{
"path": "examples/llm_reasoning.ipynb",
"chars": 8591,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"id\": \"d39a806c-02a3-4a2d-8c51-f1ab1ea79d2e\",\n \"metadata\": {},\n \"so"
},
{
"path": "examples/mcp_config_dict_example.py",
"chars": 5399,
"preview": "\"\"\"\nMCP Tools with Config Dict Format - Example\n\nThis example demonstrates using MCP tools with the simplified config di"
},
{
"path": "examples/mcp_http_example.py",
"chars": 7841,
"preview": "\"\"\"\nMCP HTTP Transport Example\n\nThis example demonstrates how to use HTTP-based MCP servers with aisuite.\n\nPrerequisites"
},
{
"path": "examples/mcp_tools_example.ipynb",
"chars": 11661,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Using MCP Tools with aisuite\\n\",\n"
},
{
"path": "examples/simple_tool_calling.ipynb",
"chars": 9908,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "examples/tool_calling_abstraction.ipynb",
"chars": 13350,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": 12,\n \"id\": \"9efdda2f-e3ab-4ec3-9b04-3ebea6fdf4c1\",\n "
},
{
"path": "guides/README.md",
"chars": 672,
"preview": "# Provider guides \n\nThese guides give directions for obtaining API keys from different providers. \n\nHere are the instruc"
},
{
"path": "guides/anthropic.md",
"chars": 1044,
"preview": "# Anthropic\n\nTo use Anthropic with `aisuite` you will need to [create an account](https://console.anthropic.com/login). "
},
{
"path": "guides/aws.md",
"chars": 2200,
"preview": "# AWS\n\nTo use AWS Bedrock with `aisuite` you will need to create an AWS account and\nnavigate to https://console.aws.amaz"
},
{
"path": "guides/azure.md",
"chars": 2824,
"preview": "# Azure AI\n\nTo use Azure AI with the `aisuite` library, you'll need to set up an Azure account and configure your enviro"
},
{
"path": "guides/cerebras.md",
"chars": 2293,
"preview": "# Cerebras AI Suite Provider Guide\n\n## About Cerebras\n\nAt Cerebras, we've developed the world's largest and fastest AI p"
},
{
"path": "guides/cohere.md",
"chars": 1012,
"preview": "# Cohere\n\nTo use Cohere with `aisuite`, you’ll need an [Cohere account](https://cohere.com/). After logging in, go to th"
},
{
"path": "guides/deepseek.md",
"chars": 1226,
"preview": "# DeepSeek\n\nTo use DeepSeek with `aisuite`, you’ll need an [DeepSeek account](https://platform.deepseek.com). After logg"
},
{
"path": "guides/google.md",
"chars": 4118,
"preview": "# Google (Vertex) AI\n\nTo use Google (Vertex) AI with the `aisuite` library, you'll first need to create a Google Cloud a"
},
{
"path": "guides/groq.md",
"chars": 1266,
"preview": "# Groq\n\nTo use Groq with `aisuite`, you’ll need a free [Groq account](https://console.groq.com/). After logging in, go t"
},
{
"path": "guides/huggingface.md",
"chars": 2530,
"preview": "# Hugging Face AI\n\nTo use Hugging Face with the `aisuite` library, you'll need to set up a Hugging Face account, obtain "
},
{
"path": "guides/lmstudio.md",
"chars": 5008,
"preview": "# LM Studio\n\nLM Studio allows users to locally host open-source models available in [their model catalog](https://lmstud"
},
{
"path": "guides/mistral.md",
"chars": 1231,
"preview": "# Mistral\n\nTo use Mistral with `aisuite`, you’ll need a [Mistral account](https://console.mistral.ai/). \n\nAfter logging "
},
{
"path": "guides/nebius.md",
"chars": 1160,
"preview": "# Nebius AI Studio\n\nTo use Nebius AI Studio with `aisuite`, you need an AI Studio account. Go to [AI Studio](https://stu"
},
{
"path": "guides/ollama.md",
"chars": 1383,
"preview": "# Ollama\n\nOllama allows users to locally host open-source models available in [their library](https://ollama.com/library"
},
{
"path": "guides/openai.md",
"chars": 1065,
"preview": "# OpenAI\n\nTo use OpenAI with `aisuite`, you’ll need an [OpenAI account](https://platform.openai.com/). After logging in,"
},
{
"path": "guides/sambanova.md",
"chars": 1058,
"preview": "# Sambanova\n\nTo use Sambanova with `aisuite`, you’ll need a [Sambanova Cloud](https://cloud.sambanova.ai/) account. Afte"
},
{
"path": "guides/watsonx.md",
"chars": 2212,
"preview": "# Watsonx with `aisuite`\n\nA a step-by-step guide to set up Watsonx with the `aisuite` library, enabling you to use IBM W"
},
{
"path": "guides/xai.md",
"chars": 776,
"preview": "# xAI\n\nTo use xAI with `aisuite`, you’ll need an [API key](https://console.x.ai/). Generate a new key and once you have "
},
{
"path": "pyproject.toml",
"chars": 2985,
"preview": "[tool.poetry]\nname = \"aisuite\"\nversion = \"0.1.14\"\ndescription = \"Uniform access layer for LLMs\"\nauthors = [\"Andrew Ng <n"
},
{
"path": "tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/client/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/client/test_client.py",
"chars": 21406,
"preview": "from unittest.mock import Mock, patch\nimport io\n\nimport pytest\n\nfrom aisuite import Client\nfrom aisuite.framework.messag"
},
{
"path": "tests/client/test_prerelease.py",
"chars": 8709,
"preview": "# Run this test before releasing a new version.\n# It will test all the models in the client.\n\nimport pytest\nimport aisui"
},
{
"path": "tests/framework/test_asr_models.py",
"chars": 4334,
"preview": "\"\"\"Tests for ASR framework data models.\"\"\"\n\nimport pytest\nfrom pydantic import ValidationError\n\nfrom aisuite.framework.m"
},
{
"path": "tests/framework/test_asr_params.py",
"chars": 19455,
"preview": "\"\"\"Unit tests for ASR parameter validation and mapping.\"\"\"\n\nimport pytest\nimport warnings\nfrom aisuite.framework.asr_par"
},
{
"path": "tests/mcp/README.md",
"chars": 6888,
"preview": "# MCP Integration Tests\n\nThis directory contains integration tests for aisuite's MCP (Model Context Protocol) support.\n\n"
},
{
"path": "tests/mcp/__init__.py",
"chars": 58,
"preview": "\"\"\"Tests for MCP (Model Context Protocol) integration.\"\"\"\n"
},
{
"path": "tests/mcp/conftest.py",
"chars": 2113,
"preview": "\"\"\"\nPytest fixtures for MCP integration tests.\n\"\"\"\n\nimport pytest\nimport tempfile\nimport os\nfrom pathlib import Path\n\n# "
},
{
"path": "tests/mcp/test_client.py",
"chars": 11437,
"preview": "\"\"\"\nIntegration tests for MCPClient.\n\nThese tests use the real Anthropic filesystem MCP server\n(@modelcontextprotocol/se"
},
{
"path": "tests/mcp/test_e2e.py",
"chars": 14993,
"preview": "\"\"\"\nEnd-to-end integration tests for MCP with aisuite.\n\nThese tests verify the complete flow of using MCP tools with ais"
},
{
"path": "tests/mcp/test_http_llm_e2e.py",
"chars": 22030,
"preview": "\"\"\"\nReal LLM End-to-End Tests for HTTP MCP Integration.\n\nThese tests make ACTUAL API calls to LLM providers (OpenAI, Ant"
},
{
"path": "tests/mcp/test_http_transport.py",
"chars": 30618,
"preview": "\"\"\"\nTests for MCP HTTP Transport.\n\nThese tests verify that the MCPClient works correctly with HTTP-based MCP servers.\nAl"
},
{
"path": "tests/mcp/test_llm_e2e.py",
"chars": 14106,
"preview": "\"\"\"\nReal LLM End-to-End Tests for MCP Integration.\n\nThese tests make ACTUAL API calls to LLM providers (OpenAI, Anthropi"
},
{
"path": "tests/providers/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/providers/test_anthropic_converter.py",
"chars": 8201,
"preview": "\"\"\"Tests for the AnthropicMessageConverter.\"\"\"\n\nimport unittest\nfrom unittest.mock import MagicMock\nfrom aisuite.provide"
},
{
"path": "tests/providers/test_asr_parameter_passthrough.py",
"chars": 9057,
"preview": "\"\"\"Component tests for ASR parameter pass-through to provider SDKs.\"\"\"\n\nimport io\nfrom unittest.mock import MagicMock, m"
},
{
"path": "tests/providers/test_aws_converter.py",
"chars": 3975,
"preview": "import unittest\nfrom unittest.mock import MagicMock\nfrom aisuite.providers.aws_provider import BedrockMessageConverter\nf"
},
{
"path": "tests/providers/test_azure_provider.py",
"chars": 3313,
"preview": "import unittest\nfrom aisuite.providers.azure_provider import AzureMessageConverter\nfrom aisuite.framework.message import"
},
{
"path": "tests/providers/test_cerebras_provider.py",
"chars": 2630,
"preview": "\"\"\"Tests for the Cerebras provider.\"\"\"\n\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom aisuite.provider"
},
{
"path": "tests/providers/test_cohere_provider.py",
"chars": 1437,
"preview": "from unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom aisuite.providers.cohere_provider import CohereProvider\n"
},
{
"path": "tests/providers/test_deepgram_provider.py",
"chars": 8815,
"preview": "\"\"\"Tests for Deepgram provider functionality.\"\"\"\n\nimport io\nfrom unittest.mock import MagicMock, mock_open, patch\n\nimpor"
},
{
"path": "tests/providers/test_deepseek_provider.py",
"chars": 3507,
"preview": "\"\"\"Tests for the Deepseek provider.\"\"\"\n\nfrom unittest.mock import MagicMock, patch\nimport pytest\n\nfrom aisuite.providers"
},
{
"path": "tests/providers/test_google_converter.py",
"chars": 4055,
"preview": "import unittest\nfrom unittest.mock import MagicMock\nfrom aisuite.providers.google_provider import GoogleMessageConverter"
},
{
"path": "tests/providers/test_google_provider.py",
"chars": 13156,
"preview": "\"\"\"Tests for Google provider functionality (both chat and ASR).\"\"\"\n\nimport io\nimport json\nfrom unittest.mock import Magi"
},
{
"path": "tests/providers/test_groq_provider.py",
"chars": 2641,
"preview": "\"\"\"Tests for the Groq provider.\"\"\"\n\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom aisuite.providers.gr"
},
{
"path": "tests/providers/test_huggingface_provider.py",
"chars": 9830,
"preview": "\"\"\"Tests for Hugging Face provider ASR functionality.\"\"\"\n\nimport io\nfrom unittest.mock import MagicMock, mock_open, patc"
},
{
"path": "tests/providers/test_inception_provider.py",
"chars": 1467,
"preview": "from unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom aisuite.providers.inception_provider import InceptionPro"
},
{
"path": "tests/providers/test_lmstudio_provider.py",
"chars": 1478,
"preview": "import pytest\nfrom unittest.mock import patch, MagicMock\nfrom aisuite.providers.lmstudio_provider import LmstudioProvide"
},
{
"path": "tests/providers/test_mistral_provider.py",
"chars": 2567,
"preview": "import pytest\nfrom unittest.mock import patch, MagicMock\n\nfrom aisuite.providers.mistral_provider import MistralProvider"
},
{
"path": "tests/providers/test_nebius_provider.py",
"chars": 1465,
"preview": "import pytest\nfrom unittest.mock import patch, MagicMock\n\nfrom aisuite.providers.nebius_provider import NebiusProvider\n\n"
}
]
// ... and 8 more files (download for full content)
About this extraction
This page contains the full source code of the andrewyng/aisuite GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 208 files (1.0 MB), approximately 247.9k tokens, and a symbol index with 803 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.