Metadata-Version: 2.4
Name: waken-azure-openai
Version: 0.1.0
Summary: Azure OpenAI Target adapter for Waken — routes waken Events to an Azure-hosted OpenAI deployment.
Project-URL: Homepage, https://github.com/WakenHQ/waken-azure-openai
Project-URL: Repository, https://github.com/WakenHQ/waken-azure-openai
Project-URL: Issues, https://github.com/WakenHQ/waken-azure-openai/issues
Author: Waken contributors
License: MIT
License-File: LICENSE
Keywords: agents,ai,azure,openai,waken
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.12
Requires-Dist: openai>=1.0.0
Requires-Dist: waken>=0.1.1
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# waken-azure-openai

[![CI](https://github.com/WakenHQ/waken-azure-openai/actions/workflows/ci.yml/badge.svg)](https://github.com/WakenHQ/waken-azure-openai/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey)](https://github.com/WakenHQ/waken-azure-openai/blob/main/LICENSE)

An Azure OpenAI [`Target`](https://github.com/WakenHQ/waken) adapter for
[Waken](https://github.com/WakenHQ/waken) — "nginx for AI agents." Routes
waken `Event`s to an Azure-hosted OpenAI deployment's Chat Completions API
and returns the result as a waken `Response`.

This is a separate package from
[`waken-openai`](https://github.com/WakenHQ/waken-openai), not a thin
wrapper around it, because Azure OpenAI has a genuinely different
auth/configuration shape — an endpoint URL, a deployment name, and a dated
API version, instead of just an API key — and (as of this writing) only
documents/supports this configuration shape for the Chat Completions API,
not the Responses API `waken-openai` uses. See "Design notes" below.

## Install

```bash
pip install waken-azure-openai
```

## Usage

```python
from waken import Runtime
from waken_azure_openai import AzureOpenAIAdapter

runtime = Runtime()
runtime.target("azure-openai", AzureOpenAIAdapter(deployment="my-gpt4-deployment"))
runtime.run()
```

Three things need to be set, either as constructor arguments or as
environment variables:

| Constructor argument | Environment variable | What it is |
|---|---|---|
| `azure_endpoint` | `AZURE_OPENAI_ENDPOINT` | Your Azure OpenAI resource's endpoint, e.g. `https://my-resource.openai.azure.com` |
| `api_key` | `AZURE_OPENAI_API_KEY` | The resource's API key |
| `api_version` | `AZURE_OPENAI_API_VERSION` | A dated Azure OpenAI API version, e.g. `2024-10-21`; defaults to that same GA version if unset |

**`deployment` is the name you gave the model deployment in the Azure
Portal — not an OpenAI model id.** This is a distinct and commonly-confused
concept worth spelling out: on Azure, you don't call a model by a name like
`"gpt-4.1"` directly. You first create a *deployment* of some underlying
model inside your Azure OpenAI resource and give that deployment its own
name (e.g. `"my-gpt4-deployment"`), and every API call addresses that
deployment name — Azure looks up which model actually serves it from the
deployment's own configuration. That's why this adapter's constructor
parameter is named `deployment`, not `model`: passing an OpenAI model id
here (`AzureOpenAIAdapter(deployment="gpt-4.1")`) will fail unless you
happen to have named your deployment exactly that.

Any other keyword the `openai` SDK's `AsyncAzureOpenAI` client accepts is
forwarded straight through, e.g. `azure_ad_token_provider=...` to
authenticate via Microsoft Entra ID instead of a static API key:

```python
from azure.identity import DefaultAzureCredential, get_bearer_token_provider

runtime.target(
    "azure-openai",
    AzureOpenAIAdapter(
        deployment="my-gpt4-deployment",
        azure_ad_token_provider=get_bearer_token_provider(
            DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
        ),
    ),
)
```

(`get_bearer_token_provider` returns a plain sync callable; `AsyncAzureOpenAI`
accepts either a sync or async callable for `azure_ad_token_provider` and
awaits it only if it returns an awaitable — confirmed from the installed
SDK's `_get_azure_ad_token`, not assumed.)

## Sessions

Like `waken-mistral` and `waken-cohere` (and unlike `waken-openai` or
Claude, which have a server-side resumable session id), the Chat
Completions API this adapter calls is stateless — it expects the full
conversation history resent on every call. So `AzureOpenAIAdapter` maps a
waken `session_id` to a plain, growing list of `{"role": ..., "content":
...}` messages, appends the new user prompt and the assistant's reply to
that list on every turn, and resends the whole thing each time. History is
kept **client-side, in this process's memory only** — there's no
persistence layer, and it's lost on restart. An `Event` with no
`session_id` sends a fresh one-message list each call and is never stored.

## Design notes

Building this adapter meant checking the real `openai` SDK and Azure's own
current docs rather than assuming parity with `waken-openai` — worth
recording here since it drove the design:

- The `openai` package (same PyPI package as vanilla OpenAI) ships an
  `AsyncAzureOpenAI` client alongside `AsyncOpenAI`. Its constructor
  already falls back to `AZURE_OPENAI_ENDPOINT` / `AZURE_OPENAI_API_KEY`
  when `azure_endpoint`/`api_key` aren't passed — this adapter forwards
  `None` straight through rather than re-implementing that lookup.
- `api_version` is the exception: the SDK's own automatic fallback reads
  `OPENAI_API_VERSION`, *not* `AZURE_OPENAI_API_VERSION` — despite the
  latter being what nearly every Azure OpenAI tutorial, the Portal's own
  code samples, LangChain, and Semantic Kernel all use. This adapter
  resolves `AZURE_OPENAI_API_VERSION` itself and always passes an explicit
  `api_version=` to the client, so that the more common env var name
  actually works.
- Azure's currently-documented Python pattern for the Responses API
  (`client.responses.create(...)`) uses a *plain* `OpenAI`/`AsyncOpenAI`
  client pointed at a `.../openai/v1/` base URL — not
  `AzureOpenAI`/`AsyncAzureOpenAI` with a dated `api_version`. Confirmed by
  reading the installed SDK's `openai/lib/azure.py` (its URL-rewriting
  only special-cases `/chat/completions` and similar endpoints for
  deployment-scoped routing, not `/responses`) and by cross-checking
  Microsoft's own current docs, where every `responses.create(...)`
  example uses the `.../openai/v1/` + plain-client shape and every
  `AzureOpenAI(azure_endpoint=..., api_version=...)` example calls
  `chat.completions.create(...)`. Since this task's whole reason for
  existing is the endpoint+deployment+api-version auth shape, this adapter
  is built on Chat Completions rather than forcing the Responses API's
  session-id pattern through a client shape that doesn't actually route it
  correctly on Azure.
- `2024-10-21` is used as the default `api_version` because it's Azure
  OpenAI's latest generally-available (non-preview) API release as of this
  writing.

## Development

```bash
git clone https://github.com/WakenHQ/waken-azure-openai
cd waken-azure-openai
pip install -e ".[dev]"
pytest
```

Tests mock `openai.AsyncAzureOpenAI` entirely — no API key, endpoint, or
network access needed to run the suite.

## License

[MIT](https://github.com/WakenHQ/waken-azure-openai/blob/main/LICENSE)
