- name
- azure-openai-to-responses
- license
- MIT
- description
- Migrate Python apps from Azure OpenAI Chat Completions to the Responses API. Covers AzureOpenAI/AsyncAzureOpenAI client migration to the v1 endpoint, streaming, tools, structured output, multi-turn, EntraID auth, and model compatibility checks. Python-focused, Azure OpenAI-specific. USE FOR: migrate to responses API, switch from chat completions, openai responses, upgrade openai SDK, responses API migration, move from completions to responses, gpt-5 migration, azure openai python migration, chat completions to responses, AzureOpenAI to OpenAI client, python azure openai upgrade. DO NOT USE FOR: building new apps from scratch (start with responses directly), Node/TypeScript/C#/Java/Go migrations (this skill is Python-only), Azure infrastructure setup (use azure-prepare), deploying models (use microsoft-foundry).
# Migrate Python Apps from Azure OpenAI Chat Completions to Responses API
> **AUTHORITATIVE GUIDANCE — FOLLOW EXACTLY**
>
> This skill migrates Python codebases using Azure OpenAI Chat Completions
> to the unified Responses API. Follow these instructions precisely.
> Do not improvise parameter mappings or invent API shapes.
---
## Triggers
Activate this skill when user wants to:
- Migrate a Python app from Azure OpenAI Chat Completions to Responses API
- Upgrade Python OpenAI SDK usage to the latest API shape against Azure OpenAI
- Prepare Python code for GPT-5 or newer models that require Responses on Azure
- Switch from `AzureOpenAI`/`AsyncAzureOpenAI` to standard `OpenAI`/`AsyncOpenAI` client with the v1 endpoint
- Fix deprecation warnings related to `AzureOpenAI` constructors or `api_version`
---
## ⚠️ Model Compatibility — CHECK FIRST
> **Before migrating, verify your Azure OpenAI deployment supports the Responses API.**
### 1. Smoke-test your deployment (fastest)
```python
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AZURE_OPENAI_API_KEY"],
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
)
try:
resp = client.responses.create(
model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
input="ping",
max_output_tokens=50,
store=False,
)
print(f"✅ Deployment supports Responses API: {resp.output_text}")
except Exception as e:
print(f"❌ Deployment does NOT support Responses API: {e}")
```
> **Note**: `max_output_tokens` has a **minimum of 16** on Azure OpenAI. Values below 16 return a 400 error. Use 50+ for smoke tests.
If this returns a 404, the deployment's model doesn't support Responses yet — check the reference below or redeploy with a supported model.
### 2. Check available models in your region (recommended)
Run the built-in model compatibility tool to see what's available with Responses API support in your specific region:
```bash
python migrate.py models --subscription YOUR_SUB_ID --location YOUR_REGION
```
This queries Azure ARM live and shows a compatibility matrix — which models support Responses, structured output, tools, etc. Use `--filter gpt-5.1,gpt-5.2` to narrow results or `--json` for scripting.
### 3. Full model support reference
- **Live query**: `python migrate.py models` (see above — region-specific, always up to date)
- **Browse availability**: [Model summary table and region availability](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/models-sold-directly-by-azure?tabs=global-standard-aoai%2Cglobal-standard&pivots=azure-openai#model-summary-table-and-region-availability)
- **Quickstart & guidance**: **https://aka.ms/openai/start**
### ⚠️ Older model limitations
> **WARNING**: Older models (those predating `gpt-4.1`) may not support all Responses API features fully.
>
> Known limitations with older models:
> - **`reasoning` parameter**: Not supported on many non-reasoning models. Only migrate `reasoning` if it was already present in the original code.
> - **`seed` parameter**: Not supported in Responses API at all — remove from all requests.
> - **Structured output via `text.format`**: Older models may not enforce `strict: true` JSON schemas reliably.
> - **Tool orchestration**: GPT-5+ orchestrates tool calls as part of internal reasoning. Older models on Responses still work but lack this deep integration.
> - **Temperature constraints**: When migrating to `gpt-5`, temperature must be omitted or set to `1`. Older models have no such constraint.
### O-series reasoning models (o1, o3-mini, o3, o4-mini)
O-series models have unique parameter constraints. When migrating apps that target o-series models:
- **`temperature`**: Must be `1` (or omitted). O-series models do not accept other values.
- **`max_completion_tokens` → `max_output_tokens`**: Apps using the Azure-specific `max_completion_tokens` must switch to `max_output_tokens`. Set high values (4096+) because reasoning tokens count against the limit.
- **`reasoning_effort`**: If the app uses `reasoning_effort` (low/medium/high), keep it — the Responses API supports this parameter for o-series models.
- **Streaming behavior**: O-series models may buffer output until reasoning completes before emitting text delta events. Streaming still works, but the first `response.output_text.delta` may arrive after a longer delay than with GPT models.
- **`top_p`**: Not supported on o-series — remove if present.
- **Tool use**: O-series models support tools via the Responses API the same as GPT models, but tool call orchestration quality varies by model.
**Action — proactive model advisory**: During the scan phase, check which model the app targets (deployment names, env vars, config). If the model predates `gpt-4.1` (not gpt-4.1+), proactively tell the user:
- The migration will work for basic text, chat, streaming, and tools on their current model.
- Newer models (`gpt-5.1`, `gpt-5.2`) offer better tool orchestration, structured output enforcement, reasoning, and cross-region availability.
- They should consider upgrading their deployment when ready — it's not blocking the migration.
Do not block or refuse to migrate based on model version. The advisory is informational.
### GitHub Models does NOT support the Responses API
> **GitHub Models (`models.github.ai`, `models.inference.ai.azure.com`) does not support the Responses API.**
If the codebase has a GitHub Models code path (look for `base_url` pointing to `models.github.ai` or `models.inference.ai.azure.com`), **remove it entirely** during migration. The Responses API requires Azure OpenAI, OpenAI, or a compatible local endpoint (e.g., Ollama with Responses support).
Action during scan:
- Flag any GitHub Models code paths for removal.
---
## Framework Migration
Many apps use higher-level frameworks on top of OpenAI. When migrating these, the framework's own API changes — not just the underlying OpenAI calls.
### Microsoft Agent Framework (MAF)
**Check your MAF version first** — the migration depends on whether you are on MAF 1.0.0+ or a pre-1.0.0 beta/rc.
#### MAF 1.0.0+ (agent-framework-openai >= 1.0.0)
`OpenAIChatClient` **already uses the Responses API** — no migration needed. If the codebase uses the legacy `OpenAIChatCompletionClient` (which uses `chat.completions.create`), replace it with `OpenAIChatClient`.
| Before | After |
|--------|-------|
| `from agent_framework.openai import OpenAIChatCompletionClient` | `from agent_framework.openai import OpenAIChatClient` |
| `OpenAIChatCompletionClient(...)` | `OpenAIChatClient(...)` |
To check your version: `python -c "import agent_framework_openai; print(agent_framework_openai.__version__)"`
#### MAF pre-1.0.0 (beta/rc releases)
In pre-1.0.0 MAF, `OpenAIChatClient` used Chat Completions. Upgrade to `agent-framework-openai>=1.0.0` where `OpenAIChatClient` uses the Responses API by default.
No other changes needed — the `Agent` and tool APIs remain the same.
### LangChain (`langchain-openai`)
Add `use_responses_api=True` to `ChatOpenAI()`. Also update response access from `.content` to `.text`.
| Before | After |
|--------|-------|
| `ChatOpenAI(model=..., base_url=..., api_key=...)` | `ChatOpenAI(model=..., base_url=..., api_key=..., use_responses_api=True)` |
| `result['messages'][-1].content` | `result['messages'][-1].text` |
For complete before/after code examples, see [cheat-sheet.md](./references/cheat-sheet.md).
---
## Frontend Migration Guidance
> **The Responses API is a server-side concern.** Migrate your Python backend; the frontend's HTTP contract should stay unchanged unless your backend is a thin pass-through — in that case, consider adopting the Responses request shape to eliminate a translation layer. If the frontend calls OpenAI directly with a client-side key, move those calls to a backend first.
### `@microsoft/ai-chat-protocol` deprecation
The `@microsoft/ai-chat-protocol` npm package is deprecated and should be replaced with [`ndjson-readablestream`](https://www.npmjs.com/package/ndjson-readablestream). If you encounter it in a frontend:
1. Replace the CDN script tag:
```html
<!-- Before -->
<script src="https://cdn.jsdelivr.net/npm/@microsoft/ai-chat-protocol@.../dist/iife/index.js"></script>
<!-- After -->
<script src="https://cdn.jsdelivr.net/npm/ndjson-readablestream@1.0.7/dist/ndjson-readablestream.umd.js"></script>
```
2. Remove the `AIChatProtocolClient` instantiation (`new ChatProtocol.AIChatProtocolClient("/chat")`).
3. Replace `client.getStreamedCompletion(messages)` with a direct `fetch()` call to the backend streaming endpoint.
4. Replace `for await (const response of result)` with `for await (const chunk of readNDJSONStream(response.body))`.
5. Update property access from `response.delta.content` / `response.error` to `chunk.delta.content` / `chunk.error`.
---
## Goals
- Enumerate all Python call sites using Chat Completions or legacy Completions against Azure OpenAI.
- Propose a migration plan and sequencing for the Python codebase.
- Apply safe, minimal edits to switch to Responses API.
- Update callers to consume the Responses output schema; no backcompat wrappers.
- Run tests/lints; fix trivial breakages introduced by the migration.
- Prepare small, reviewable change sets and provide a final summary with diffs (do not commit).
---
## Guardrails
- Only modify files inside the git workspace. Never write outside.
- Do not preserve backward-compatibility shims; migrate code to the new API shape.
- Do not leave tombstone/transition comments or backup files.
- Preserve streaming semantics if previously used; otherwise use non-streaming.
- Ask for approval before running commands or network calls if in approval mode.
- Do not run `git add`/`git commit`/`git push`; produce working-tree edits only.
---
## Step 0: Azure OpenAI Client Migration (Prerequisite)
If the codebase uses `AzureOpenAI` or `AsyncAzureOpenAI` constructors, migrate to the standard `OpenAI` / `AsyncOpenAI` constructors first. The Azure-specific constructors are deprecated in `openai>=1.108.1`.
### Why the v1 API path?
The new `/openai/v1` endpoint uses the standard `OpenAI()` client instead of `AzureOpenAI()`, requires no `api_version` parameter, and works identically across OpenAI and Azure OpenAI. The same client code is future-proof — no version management needed.
### Key changes
| Before | After |
|--------|-------|
| `AzureOpenAI` | `OpenAI` |
| `AsyncAzureOpenAI` | `AsyncOpenAI` |
| `azure_endpoint` | `base_url` |
| `azure_ad_token_provider` | `api_key` |
| `api_version=...` | Remove entirely |
### Cleanup checklist
- Remove `api_version` argument from client construction.
- Remove `AZURE_OPENAI_VERSION` / `AZURE_OPENAI_API_VERSION` environment variables from `.env`, app settings, and Bicep/infra files.
- Rename `AZURE_OPENAI_CLIENT_ID` → `AZURE_CLIENT_ID` in `.env`, app settings, Bicep/infra, and test fixtures (standard Azure Identity SDK convention).
- Ensure `openai>=1.108.1` in `requirements.txt` or `pyproject.toml`.
### Environment variable migration
| Old env var | Action | Notes |
|-------------|--------|-------|
| `AZURE_OPENAI_VERSION` | **Remove** | No `api_version` needed with v1 endpoint |
| `AZURE_OPENAI_API_VERSION` | **Remove** | Same as above |
| `AZURE_OPENAI_CLIENT_ID` | **Rename** → `AZURE_CLIENT_ID` | Standard Azure Identity SDK convention for `ManagedIdentityCredential(client_id=...)` |
| `AZURE_OPENAI_ENDPOINT` | **Keep** | Still needed for `base_url` construction |
| `AZURE_OPENAI_CHAT_DEPLOYMENT` | **Keep** | Used as `model` param in `responses.create` |
| `AZURE_OPENAI_API_KEY` | **Keep** | Used as `api_key` for key-based auth |
For client setup code examples (sync, async, EntraID, API key, multi-tenant), see [cheat-sheet.md](./references/cheat-sheet.md).
---
## Step 1: Detect Legacy Call Sites
Run the [detect_legacy.py](../../../../../.agents/skills/azure-openai-to-responses/scripts/detect_legacy.py) script to find all call sites that need migration:
```bash
python skills/azure-openai-to-responses/scripts/detect_legacy.py .
```
Or run these searches manually — every match is a migration target:
```bash
# Legacy API calls (must rewrite)
rg "chat\.completions\.create"
rg "ChatCompletion\.create"
rg "Completion\.create"
# Deprecated Azure client constructors (must replace)
rg "AzureOpenAI\("
rg "AsyncAzureOpenAI\("
# Response shape access patterns (must update)
rg "choices\[0\]\.message\.content"
rg "choices\[0\]\.delta\.content"
rg "choices\[0\]\.message\.function_call"
rg "choices\[0\]\.message\.tool_calls"
# Tool definitions in old nested format (must flatten)
rg '"function":\s*{\s*"name"'
rg "pydantic_function_tool"
# Tool results in old format (must convert to function_call_output)
rg '"role":\s*"tool"'
rg '"tool_call_id"'
# Deprecated parameters (must remove or rename)
rg "response_format"
rg "max_tokens\b" # rename to max_output_tokens
rg "['\"]seed['\"]" # remove entirely
# Deprecated env vars (clean up)
rg "AZURE_OPENAI_API_VERSION|AZURE_OPENAI_VERSION"
rg "AZURE_OPENAI_CLIENT_ID" # should be AZURE_CLIENT_ID
# GitHub Models endpoints (must remove — Responses API not supported)
rg "models\.github\.ai|models\.inference\.ai\.azure"
# Framework-level legacy patterns (must update)
rg "OpenAIChatCompletionClient" # MAF 1.0.0+: replace with OpenAIChatClient
rg "ChatOpenAI\(" | grep -v "use_responses_api" # LangChain: needs use_responses_api=True
# Test infrastructure (must update)
rg "ChatCompletionChunk|AsyncCompletions\.create" tests/
View on GitHub