Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
POETRY=poetry
POETRY_PYTHON=python

# For integration tests
DIAL_URL=dial-url
DIAL_API_KEY=dial-api-key
DIAL_MODEL=gpt-5.2
4 changes: 4 additions & 0 deletions .markdownlint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"MD013": false,
"MD033": false
}
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true,
"python-envs.defaultEnvManager": "ms-python.python:poetry",
"python-envs.defaultPackageManager": "ms-python.python:poetry",
}
81 changes: 48 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,51 +15,55 @@

- [Usage](#usage)
- [Authentication](#authentication)
- [API Keys](#api-keys)
- [Bearer Token](#bearer-token)
- [API Keys](#api-keys)
- [Bearer Token](#bearer-token)
- [Lifecycle Management](#lifecycle-management)
- [Deployments](#deployments)
- [List Deployments](#list-deployments)
- [Get Deployment by Id](#get-deployment-by-id)
- [Get Deployment Configuration](#get-deployment-configuration)
- [Make Chat Completions Requests](#make-completions-requests)
- [Without Streaming](#without-streaming)
- [With Streaming](#with-streaming)
- [List Deployments](#list-deployments)
- [Get Deployment by Id](#get-deployment-by-id)
- [Get Deployment Configuration](#get-deployment-configuration)
- [Make Chat Completions Requests](#make-chat-completions-requests)
- [Without Streaming](#without-streaming)
- [With Streaming](#with-streaming)
- [Working with Files](#working-with-files)
- [Working with URLs](#working-with-urls)
- [Uploading Files](#uploading-files)
- [Downloading Files](#downloading-files)
- [Deleting Files](#deleting-files)
- [Accessing Metadata](#accessing-metadata)
- [Working with URLs](#working-with-urls)
- [Uploading Files](#uploading-files)
- [Downloading Files](#downloading-files)
- [Deleting Files](#deleting-files)
- [Moving and Copying Files](#moving-and-copying-files)
- [Accessing Metadata](#accessing-metadata)
- [Prompts](#prompts)
- [Get Prompt](#get-prompt)
- [Get Prompt Metadata](#get-prompt-metadata)
- [Save Prompt](#save-prompt)
- [Get Prompt](#get-prompt)
- [Get Prompt Metadata](#get-prompt-metadata)
- [Delete Prompt](#delete-prompt)
- [Applications](#applications)
- [List Applications](#list-applications)
- [Get Application by Id](#get-application-by-id)
- [List Applications](#list-applications)
- [Get Application by Id](#get-application-by-id)
- [Models](#models)
- [Get Model by Name](#get-model-by-name)
- [Get Model by Name](#get-model-by-name)
- [User](#user)
- [Get Authenticated User Info](#get-authenticated-user-info)
- [Get Authenticated User Info](#get-authenticated-user-info)
- [Toolsets](#toolsets)
- [Get Toolset by Id](#get-toolset-by-id)
- [Get Toolset by Id](#get-toolset-by-id)
- [Resource Permissions](#resource-permissions)
- [Grant Permissions](#grant-permissions)
- [Grant Permissions](#grant-permissions)
- [Client Channel](#client-channel)
- [Sign In to Toolsets](#sign-in-to-toolsets)
- [Sign In to Toolsets](#sign-in-to-toolsets)
- [Client Pool](#client-pool)
- [Synchronous Client Pool](#synchronous-client-pool)
- [Asynchronous Client Pool](#asynchronous-client-pool)
- [Synchronous Client Pool](#synchronous-client-pool)
- [Asynchronous Client Pool](#asynchronous-client-pool)
- [Development](#development)
- [Pre-requisites](#pre-requisites)
- [Setup](#setup)
- [Main commands](#main-commands)
- [Git hooks](#git-hooks)
- [Main commands](#main-commands)
- [Integration tests](#integration-tests)

## Usage

This section outlines how to use the AI DIAL Python client to interact with the DIAL Core API.
It covers authentication methods, making chat completion requests, working with files, managing applications,
This section outlines how to use the AI DIAL Python client to interact with the DIAL Core API.
It covers authentication methods, making chat completion requests, working with files, managing applications,
and utilizing client pools for efficient connection management.

### Authentication
Expand Down Expand Up @@ -284,7 +288,7 @@ The response is a plain `dict` whose shape is entirely deployment-specific:
}
```

### Make Completions Requests
### Make Chat Completions Requests

#### Without Streaming

Expand Down Expand Up @@ -466,8 +470,8 @@ ChatCompletionChunk(

Files are AI DIAL resources that operate with URL-like objects. Use `pathlib.PurePosixPath` or `str` to create to create new URL-like objects or to get a `string` representation of them.

* Use `client.my_files_home()` to upload a file into your bucket in the AI DIAL storage.
* Use `await async_client.my_files_home()` to get the URL of your bucket and then use it to upload files.
- Use `client.my_files_home()` to upload a file into your bucket in the AI DIAL storage.
- Use `await async_client.my_files_home()` to get the URL of your bucket and then use it to upload files.

The following example demonstrates how you can use the path-like object returned by `my_files_home()` function:

Expand Down Expand Up @@ -602,7 +606,6 @@ await result.awrite_to("./some-local-file.txt")

Use `delete()` to remove files from your storage bucket:


```python
await sync_client.files.delete(
url=sync_client.my_files_home() / "relative_folder/my-file.txt"
Expand Down Expand Up @@ -1095,7 +1098,6 @@ second_client = client_pool.create_client(
)
```


## Development

To set up the development environment and run the project, follow the instructions below.
Expand All @@ -1106,18 +1108,21 @@ The following tools are required to work with the project:

1. `Make`
2. `Python 3.10`
3. `Poetry 2.*`. Installation guidance can be found [here](https://python-poetry.org/docs/#installation)
3. `Poetry 2.*`. See the [Poetry installation guide](https://python-poetry.org/docs/#installation)

### Setup

1. Create `.env` file in the root of the project. Copy `.env.template` file data to the `.env` and customize the values
if needed. You can customize python and poetry locations.
2. Create and activate virtual environment

```bash
make init_env
source .venv/bin/activate
```

3. Install dependencies

```bash
make install
```
Expand Down Expand Up @@ -1147,3 +1152,13 @@ make install_git_hooks
| `make integration_test` | Run integration tests |
| `make coverage` | Generate test coverage report |
| `make help` | Show available commands |

### Integration tests

`make integration_test` runs against a real DIAL deployment and requires the following environment variables:

| Variable | Description |
|----------------|----------------------------------------------------|
| `DIAL_URL` | Base URL of the DIAL Core service |
| `DIAL_API_KEY` | API key used to authenticate requests in DIAL Core |
| `DIAL_MODEL` | Name of a chat model deployment to run tests with |
20 changes: 9 additions & 11 deletions tests/integration/fixtures.py → tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,24 @@
@pytest.fixture
def dial_url() -> str:
url = os.getenv("DIAL_URL")
assert url
assert url, "DIAL_URL environment variable is not set"
return url


@pytest.fixture
def dial_api_key() -> str:
api_key = os.getenv("DIAL_API_KEY")
assert api_key
assert api_key, "DIAL_API_KEY environment variable is not set"
return api_key


@pytest.fixture
def dial_model() -> str:
model = os.getenv("DIAL_MODEL")
assert model, "DIAL_MODEL environment variable is not set"
return model


@pytest.fixture
def sync_client(dial_url, dial_api_key):
return Dial(base_url=dial_url, api_key=dial_api_key)
Expand All @@ -32,15 +39,6 @@ def async_client(dial_url, dial_api_key):
return AsyncDial(base_url=dial_url, api_key=dial_api_key)


@pytest.fixture
def test_deployment(sync_client: Dial) -> str:
deployments = sync_client.deployments.list()
assert len(deployments)
deployment = next(d for d in deployments if d.id.startswith("gpt-"))
assert deployment
return deployment.id


@pytest.fixture
def absent_test_file(sync_client):
def _save_delete_file(p):
Expand Down
53 changes: 16 additions & 37 deletions tests/integration/test_async_completions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@

from aidial_client import AsyncDial
from aidial_client._exception import DialException
from tests.integration.fixtures import * # noqa


@pytest.mark.asyncio
async def test_async_default_api_version(
async_client: AsyncDial,
dial_url: str,
dial_api_key: str,
test_deployment: str,
async def test_async_missing_api_version_raises(
async_client: AsyncDial, dial_model: str
):
with pytest.raises(DialException):
await async_client.chat.completions.create(
deployment_name="gpt-35-turbo",
deployment_name=dial_model,
stream=False,
messages=[
{
Expand All @@ -23,13 +19,19 @@ async def test_async_default_api_version(
}
],
)


@pytest.mark.asyncio
async def test_async_client_default_api_version(
dial_url: str, dial_api_key: str, dial_model: str
):
client_with_default_api_version = AsyncDial(
base_url=dial_url,
api_key=dial_api_key,
api_version="2024-02-15-preview",
)
await client_with_default_api_version.chat.completions.create(
deployment_name=test_deployment,
deployment_name=dial_model,
stream=False,
messages=[
{
Expand All @@ -42,10 +44,10 @@ async def test_async_default_api_version(

@pytest.mark.asyncio
async def test_completions_without_streaming(
async_client: AsyncDial, test_deployment: str
async_client: AsyncDial, dial_model: str
):
completion = await async_client.chat.completions.create(
deployment_name=test_deployment,
deployment_name=dial_model,
stream=False,
messages=[
{
Expand All @@ -70,13 +72,11 @@ async def test_completions_without_streaming(


@pytest.mark.asyncio
async def test_completions_with_streaming(async_client: AsyncDial):
deployments = await async_client.deployments.list()
assert len(deployments)
deployment = next(d for d in deployments if d.id.startswith("gpt-"))
assert deployment
async def test_completions_with_streaming(
async_client: AsyncDial, dial_model: str
):
completion = await async_client.chat.completions.create(
deployment_name=deployment.id,
deployment_name=dial_model,
stream=True,
messages=[
{
Expand Down Expand Up @@ -105,24 +105,3 @@ async def test_completions_with_streaming(async_client: AsyncDial):
last_chunk.usage.completion_tokens + last_chunk.usage.prompt_tokens
== last_chunk.usage.total_tokens
)


@pytest.mark.asyncio
async def test_error_during_streaming(
async_client: AsyncDial, test_deployment: str
):
completion = await async_client.chat.completions.create(
deployment_name=test_deployment,
stream=True,
messages=[
{
"role": "system",
"content": "2+3=",
}
],
max_tokens=20,
api_version="2024-02-15-preview",
)

async for chunk in completion:
print(chunk)
1 change: 0 additions & 1 deletion tests/integration/test_async_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from aidial_client import AsyncDial, DialException
from aidial_client._exception import EtagMismatchError
from aidial_client.types.metadata import FileItem
from tests.integration.fixtures import * # type: ignore # noqa

current_file_path = os.path.abspath(__file__)
file_name = "test-file-async"
Expand Down
1 change: 0 additions & 1 deletion tests/integration/test_async_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from aidial_client._exception import EtagMismatchError, ResourceNotFoundError
from aidial_client.types.metadata import PromptItem
from aidial_client.types.prompt import Prompt
from tests.integration.fixtures import * # type: ignore # noqa

PROMPT_FOLDER = "test-folder-artifacts"

Expand Down
Loading
Loading