Python SDK for the ai& API, generated from the public OpenAPI spec.
Project description
aiand-python
Use the ai& API with Python.
This package is generated from the public ai& OpenAPI spec with OpenAPI Generator. It covers the OpenAI-compatible endpoints currently present in the spec: models, chat completions, legacy completions, responses, files, and chunked uploads.
ai& also publishes docs at docs.aiand.com. The SDK update script regenerates from the public OpenAPI spec, then applies a small post-generation compatibility layer for Python generator edge cases.
Installation
From this checkout:
cd aiand-python
python -m pip install -e .
With uv:
cd aiand-python
uv sync --extra test --extra dev
Once this package is published, install it as:
python -m pip install aiand
The current SDK version is 0.1.0. See CHANGELOG.md for release notes.
Usage
Set your API key in the environment:
export AIAND_API_KEY="sk-..."
Create a client:
import os
import aiand
configuration = aiand.Configuration(access_token=os.environ["AIAND_API_KEY"])
with aiand.ApiClient(configuration) as api_client:
client = aiand.OpenaiApi(api_client)
models = client.list_models()
print(models.data[0].id)
The generated base URL is https://api.aiand.com. The OpenAPI paths include /v1, so SDK
calls resolve to URLs like https://api.aiand.com/v1/models.
Chat
import os
import aiand
configuration = aiand.Configuration(access_token=os.environ["AIAND_API_KEY"])
request = aiand.CreateChatCompletionRequest.from_dict(
{
"model": "openai/gpt-oss-120b",
"messages": [
{"role": "system", "content": "You are concise and practical."},
{"role": "user", "content": "Give me one sentence about ai&."},
],
"temperature": 0.2,
}
)
with aiand.ApiClient(configuration) as api_client:
client = aiand.OpenaiApi(api_client)
response = client.create_chat_completion(request)
print(response.choices[0].message.content)
Completions
import os
import aiand
configuration = aiand.Configuration(access_token=os.environ["AIAND_API_KEY"])
request = aiand.CreateCompletionRequest.from_dict(
{
"model": "openai/gpt-oss-120b",
"prompt": "Write a short product tagline for ai&:",
"max_tokens": 32,
}
)
with aiand.ApiClient(configuration) as api_client:
client = aiand.OpenaiApi(api_client)
response = client.create_completion(request)
print(response.choices[0].text)
Responses
import os
import aiand
configuration = aiand.Configuration(access_token=os.environ["AIAND_API_KEY"])
request = aiand.CreateResponseRequest(
model="openai/gpt-oss-120b",
input=aiand.ResponseInput("Give me one practical sentence about ai&."),
temperature=0.2,
max_output_tokens=64,
parallel_tool_calls=False,
truncation="disabled",
)
with aiand.ApiClient(configuration) as api_client:
client = aiand.OpenaiApi(api_client)
response = client.create_response(request)
print(response.to_dict()["output"])
The typed constructor omits unset optional parameters from the request body.
Models And Pricing
import os
import aiand
configuration = aiand.Configuration(access_token=os.environ["AIAND_API_KEY"])
with aiand.ApiClient(configuration) as api_client:
client = aiand.OpenaiApi(api_client)
models = client.list_models()
for model in models.data:
print(
model.id,
model.provider,
model.context_window,
model.capabilities,
model.input_per_1m,
model.output_per_1m,
)
The docs describe model pricing as precise string fields. This SDK keeps
input_per_1m and output_per_1m as strings instead of floats.
Files
Upload a file once, then reference the returned file_id from chat completion requests.
import os
from pathlib import Path
import aiand
configuration = aiand.Configuration(access_token=os.environ["AIAND_API_KEY"])
image_path = Path("diagram.png")
with aiand.ApiClient(configuration) as api_client:
files = aiand.FilesApi(api_client)
uploaded = files.upload_file(
file=(image_path.name, image_path.read_bytes()),
purpose="vision",
)
print(uploaded.id)
The file purpose values are vision, video, audio, and document.
Chunked Uploads
For larger assets, create an upload, add parts in order, then complete it.
import os
from pathlib import Path
import aiand
configuration = aiand.Configuration(access_token=os.environ["AIAND_API_KEY"])
video_path = Path("clip.mp4")
part_bytes = video_path.read_bytes()
with aiand.ApiClient(configuration) as api_client:
uploads = aiand.UploadsApi(api_client)
upload = uploads.create_upload(
aiand.CreateUploadRequest.from_dict(
{
"filename": video_path.name,
"purpose": "video",
"bytes": len(part_bytes),
"mime_type": "video/mp4",
}
)
)
part = uploads.add_upload_part(upload.id, data=("part-1", part_bytes))
completed = uploads.complete_upload(
upload.id,
aiand.CompleteUploadRequest.from_dict({"part_ids": [part.id]}),
)
print(completed.file.id)
Timeouts And Headers
Every generated operation accepts OpenAPI Generator's standard request controls:
response = client.list_models(
_request_timeout=(5, 30),
_headers={"X-Request-Source": "aiand-python"},
)
Use Configuration(access_token=...) for API-key auth. The docs note that browser/JWT
auth can require X-Org-ID; for server-side API keys, the organization is resolved from
the key.
Errors
The generated client raises aiand.ApiException for non-2xx responses.
import aiand
try:
client.list_models()
except aiand.ApiException as error:
print(error.status)
print(error.body)
Testing
Run the unit tests without making network calls:
uv run --extra test pytest
Run the focused linter for hand-maintained code:
uv run --extra dev ruff check tests scripts
Generated code under aiand/ is intentionally excluded from Ruff. It is recreated by
OpenAPI Generator and should be reviewed for behavior, not reformatted by hand.
Recording VCR Cassettes
Tests use vcrpy for live API coverage. Cassettes live in
tests/cassettes.
To record cassettes, create .env.test:
AIAND_API_KEY=sk-your-real-aiand-api-key
Then run:
./scripts/record-cassettes
The VCR config filters the Authorization header and common request/organization headers.
The recording script sets AIAND_VCR_RECORD_MODE=once. Do not commit .env.test. Only
commit sanitized cassette files.
The VCR suite records one compact cassette per public endpoint group:
list_models.yamlchat_completion.yamlcompletion.yamlresponse.yamlfiles_lifecycle.yamluploads_complete.yamluploads_cancel.yaml
Together those cassettes hit every endpoint currently generated from the OpenAPI spec:
GET /v1/models, POST /v1/chat/completions, POST /v1/completions,
POST /v1/responses, GET /v1/files, POST /v1/files, GET /v1/files/{id},
GET /v1/files/{id}/content, DELETE /v1/files/{id}, POST /v1/uploads,
POST /v1/uploads/{id}/parts, POST /v1/uploads/{id}/complete, and
POST /v1/uploads/{id}/cancel.
Updating The SDK
Prerequisites:
- Java, required by OpenAPI Generator.
- Node/npm with
npx, used to run@openapitools/openapi-generator-cli@2.34.0. - Python 3.10 or newer.
uvfor development and tests.
Regenerate from the latest published spec:
./scripts/update-sdk
That script:
- Downloads
https://api.aiand.com/openapi.jsontoopenapi/openapi.json. - Runs OpenAPI Generator with
openapi-generator-config.yaml. - Applies
scripts/patch_generated_client.pyfor generator-specific Python compatibility.
After regenerating:
uv run --extra test pytest
uv run --extra dev ruff check tests scripts
Review the generated diff in aiand/, docs/, and openapi/openapi.json. If the public
spec has gained endpoints or corrected a generator edge case, update
scripts/patch_generated_client.py so the post-generation patch layer remains small and
obvious.
The npm wrapper is pinned in scripts/update-sdk, and the OpenAPI Generator version is
pinned in openapitools.json. To upgrade either one, edit the pinned version, regenerate,
and review the generated diff carefully.
Development Notes
Most SDK files are generated. The main hand-maintained files are:
README.mdCHANGELOG.mdLICENSEpyproject.tomlscripts/update-sdkscripts/patch_generated_client.pyscripts/record-cassettestests/
Bug reports and pull requests are welcome.
License
This project is licensed under the Apache License 2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aiand-0.1.0.tar.gz.
File metadata
- Download URL: aiand-0.1.0.tar.gz
- Upload date:
- Size: 73.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
726ee25a278789be476ed01f39e93c4b7f6ef373305493a40fc7d252c0fc538b
|
|
| MD5 |
ef9ba735e82224ea0d52d9388842ba0a
|
|
| BLAKE2b-256 |
93a60337c2ccce09407688c6e751801a65e070cf7ec3474692916f74746549e3
|
File details
Details for the file aiand-0.1.0-py3-none-any.whl.
File metadata
- Download URL: aiand-0.1.0-py3-none-any.whl
- Upload date:
- Size: 205.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60f0350e4feaebecb305d38c94ec0be86719cc4eb3f9b7bbac663d91b6783572
|
|
| MD5 |
06ab3c5b1d66df0d14c5d65d5a4c8616
|
|
| BLAKE2b-256 |
f4a872796e30fd44c43de05a38855350052987774567a8721be04fa341d5e319
|