microsoft/azure-openai-to-responses
>- 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. 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. Node/TypeScript/C#/Java/Go migrations (this skill is Python-only), Azure infrastructure setup (use azure-prepare), deploying models (use microsoft-foundry).
npx skills add https://github.com/microsoft/generative-ai-for-beginners --skill azure-openai-to-responses
> 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.
Installed from Azure-Samples/azure-openai-to-responses (MIT).
Activate this skill when the user wants to:
AzureOpenAI/AsyncAzureOpenAI to standard OpenAI/AsyncOpenAI client with the v1 endpointAzureOpenAI constructors or api_versionGPT-5 and newer models require the Responses API. 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.
| Chat Completions (before) | Responses API (after) |
| --- | --- |
| AzureOpenAI() / AsyncAzureOpenAI() | OpenAI(base_url=...) / AsyncOpenAI(base_url=...) |
| azure_endpoint=... | base_url=f"{endpoint.rstrip('/')}/openai/v1/" |
| api_version="2024-..." | Remove entirely — /openai/v1/ is stable |
| azure_ad_token_provider=... | api_key=token_provider |
| client.chat.completions.create(messages=...) | client.responses.create(input=...) |
| resp.choices[0].message.content | resp.output_text |
| max_tokens | max_output_tokens (min 16 on Azure) |
| response_format | text={"format": {...}} |
| seed | Remove (not supported) |
| tools nested {"type":"function","function":{...}} | flat {"type":"function","name":...} |
| tool result {"role":"tool","tool_call_id":...} | {"type":"function_call_output","call_id":...,"output":...} |
| content[].type: "text" | content[].type: "input_text" |
| content[].type: "image_url" + {"url": "..."} | content[].type: "input_image" + flat "image_url": "..." |
| streaming chunk.choices[0].delta.content | event.type == "response.output_text.delta" → event.delta |
Verify the deployed model supports the Responses API before migrating. GPT-4o and
GPT-4 support Responses for basic text/chat/streaming/tools but not all features.
Newer models (gpt-4.1+, gpt-5.x) have full support. **GitHub Models
(models.github.ai, models.inference.ai.azure.com) do NOT support the Responses
API** — remove those code paths and switch to Azure OpenAI, OpenAI, or a compatible
local endpoint.
Smoke test:
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/",
)
resp = client.responses.create(
model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
input="ping",
max_output_tokens=50,
store=False,
)
print(resp.output_text)
AzureOpenAI/AsyncAzureOpenAI constructors are deprecated in openai>=1.108.1.
Before:
from openai import AzureOpenAI
client = AzureOpenAI(
api_version=os.environ["AZURE_OPENAI_API_VERSION"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_key=os.environ["AZURE_OPENAI_API_KEY"],
)
After:
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/",
)
Cleanup: remove api_version args, remove AZURE_OPENAI_API_VERSION /
AZURE_OPENAI_VERSION from .env/infra, rename AZURE_OPENAI_CLIENT_ID →
AZURE_CLIENT_ID, ensure openai>=1.108.1.
rg "chat\.completions\.create" # legacy API calls
rg "ChatCompletion\.create|Completion\.create"
rg "AzureOpenAI\(|AsyncAzureOpenAI\(" # deprecated constructors
rg "choices\[0\]\.message\.content" # response access
rg "choices\[0\]\.delta\.content" # streaming access
rg "max_tokens\b" # rename to max_output_tokens
rg "['\"]seed['\"]" # remove entirely
rg "response_format" # → text.format
rg "AZURE_OPENAI_API_VERSION|AZURE_OPENAI_VERSION"
rg "models\.github\.ai|models\.inference\.ai\.azure" # GitHub Models: remove
client.chat.completions.create(messages=...) → client.responses.create(input=...)resp.choices[0].message.content → resp.output_textmax_tokens → max_output_tokens (min 16); remove seedresponse_format → text={"format": {"type": "json_schema", "name": "Output", "strict": True, "schema": {...}}}store=False on every request (client-managed state)event.type == "response.output_text.delta" (use event.delta) and response.completedtool_choice, return results as function_call_output items; append response.output items for round-tripsinput array, or use previous_response_id (requires store=True)max_completion_tokens → max_output_tokens (4096+), reasoning_effort → reasoning={"effort": ...}, omit temperature/top_p (GPT-5 rejects temperature outright; to vary output use a non-reasoning model like Llama-3.3-70B-Instruct via Foundry Models)chat.completions.create|ChatCompletion.create|Completion.createAzureOpenAI(|AsyncAzureOpenAI( — all use OpenAI/AsyncOpenAI + v1 endpointmodels.github.ai|models.inference.ai.azurechoices[0] — all access uses resp.output_text / Responses schemaresponse_format; structured output uses text={"format": {...}}openai>=1.108.1 in requirements; store=False on every call; no api_version in client constructionkwargs.get("input"), snapshots use Responses shape); pytest passesSee references/cheat-sheet.md for complete before/after code examples.
Take microsoft/azure-openai-to-responses from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.