Rewrote sync_microsoft_skills.py (v4) to use each SKILL.md's frontmatter 'name' field as the flat directory name under skills/, replacing the nested skills/official/microsoft/<lang>/<category>/<service>/ hierarchy. This fixes CI failures caused by the indexing, validation, and catalog scripts expecting skills/<id>/SKILL.md (depth 1). Changes: - Rewrite scripts/sync_microsoft_skills.py for flat output with collision detection - Update scripts/tests/inspect_microsoft_repo.py for flat name mapping - Update scripts/tests/test_comprehensive_coverage.py for name uniqueness checks - Delete skills/official/ nested directory - Add 129 Microsoft skills as flat directories (e.g. skills/azure-mgmt-botservice-dotnet/) - Move attribution files to docs/ (LICENSE-MICROSOFT, microsoft-skills-attribution.json) - Rebuild skills_index.json, CATALOG.md, README.md (845 total skills)
456 lines
13 KiB
Markdown
456 lines
13 KiB
Markdown
---
|
|
name: azure-ai-openai-dotnet
|
|
description: |
|
|
Azure OpenAI SDK for .NET. Client library for Azure OpenAI and OpenAI services. Use for chat completions, embeddings, image generation, audio transcription, and assistants. Triggers: "Azure OpenAI", "AzureOpenAIClient", "ChatClient", "chat completions .NET", "GPT-4", "embeddings", "DALL-E", "Whisper", "OpenAI .NET".
|
|
package: Azure.AI.OpenAI
|
|
---
|
|
|
|
# Azure.AI.OpenAI (.NET)
|
|
|
|
Client library for Azure OpenAI Service providing access to OpenAI models including GPT-4, GPT-4o, embeddings, DALL-E, and Whisper.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
dotnet add package Azure.AI.OpenAI
|
|
|
|
# For OpenAI (non-Azure) compatibility
|
|
dotnet add package OpenAI
|
|
```
|
|
|
|
**Current Version**: 2.1.0 (stable)
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
AZURE_OPENAI_ENDPOINT=https://<resource-name>.openai.azure.com
|
|
AZURE_OPENAI_API_KEY=<api-key> # For key-based auth
|
|
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-mini # Your deployment name
|
|
```
|
|
|
|
## Client Hierarchy
|
|
|
|
```
|
|
AzureOpenAIClient (top-level)
|
|
├── GetChatClient(deploymentName) → ChatClient
|
|
├── GetEmbeddingClient(deploymentName) → EmbeddingClient
|
|
├── GetImageClient(deploymentName) → ImageClient
|
|
├── GetAudioClient(deploymentName) → AudioClient
|
|
└── GetAssistantClient() → AssistantClient
|
|
```
|
|
|
|
## Authentication
|
|
|
|
### API Key Authentication
|
|
|
|
```csharp
|
|
using Azure;
|
|
using Azure.AI.OpenAI;
|
|
|
|
AzureOpenAIClient client = new(
|
|
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
|
|
new AzureKeyCredential(Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY")!));
|
|
```
|
|
|
|
### Microsoft Entra ID (Recommended for Production)
|
|
|
|
```csharp
|
|
using Azure.Identity;
|
|
using Azure.AI.OpenAI;
|
|
|
|
AzureOpenAIClient client = new(
|
|
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
|
|
new DefaultAzureCredential());
|
|
```
|
|
|
|
### Using OpenAI SDK Directly with Azure
|
|
|
|
```csharp
|
|
using Azure.Identity;
|
|
using OpenAI;
|
|
using OpenAI.Chat;
|
|
using System.ClientModel.Primitives;
|
|
|
|
#pragma warning disable OPENAI001
|
|
|
|
BearerTokenPolicy tokenPolicy = new(
|
|
new DefaultAzureCredential(),
|
|
"https://cognitiveservices.azure.com/.default");
|
|
|
|
ChatClient client = new(
|
|
model: "gpt-4o-mini",
|
|
authenticationPolicy: tokenPolicy,
|
|
options: new OpenAIClientOptions()
|
|
{
|
|
Endpoint = new Uri("https://YOUR-RESOURCE.openai.azure.com/openai/v1")
|
|
});
|
|
```
|
|
|
|
## Chat Completions
|
|
|
|
### Basic Chat
|
|
|
|
```csharp
|
|
using Azure.AI.OpenAI;
|
|
using OpenAI.Chat;
|
|
|
|
AzureOpenAIClient azureClient = new(
|
|
new Uri(endpoint),
|
|
new DefaultAzureCredential());
|
|
|
|
ChatClient chatClient = azureClient.GetChatClient("gpt-4o-mini");
|
|
|
|
ChatCompletion completion = chatClient.CompleteChat(
|
|
[
|
|
new SystemChatMessage("You are a helpful assistant."),
|
|
new UserChatMessage("What is Azure OpenAI?")
|
|
]);
|
|
|
|
Console.WriteLine(completion.Content[0].Text);
|
|
```
|
|
|
|
### Async Chat
|
|
|
|
```csharp
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
|
[
|
|
new SystemChatMessage("You are a helpful assistant."),
|
|
new UserChatMessage("Explain cloud computing in simple terms.")
|
|
]);
|
|
|
|
Console.WriteLine($"Response: {completion.Content[0].Text}");
|
|
Console.WriteLine($"Tokens used: {completion.Usage.TotalTokenCount}");
|
|
```
|
|
|
|
### Streaming Chat
|
|
|
|
```csharp
|
|
await foreach (StreamingChatCompletionUpdate update
|
|
in chatClient.CompleteChatStreamingAsync(messages))
|
|
{
|
|
if (update.ContentUpdate.Count > 0)
|
|
{
|
|
Console.Write(update.ContentUpdate[0].Text);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Chat with Options
|
|
|
|
```csharp
|
|
ChatCompletionOptions options = new()
|
|
{
|
|
MaxOutputTokenCount = 1000,
|
|
Temperature = 0.7f,
|
|
TopP = 0.95f,
|
|
FrequencyPenalty = 0,
|
|
PresencePenalty = 0
|
|
};
|
|
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(messages, options);
|
|
```
|
|
|
|
### Multi-turn Conversation
|
|
|
|
```csharp
|
|
List<ChatMessage> messages = new()
|
|
{
|
|
new SystemChatMessage("You are a helpful assistant."),
|
|
new UserChatMessage("Hi, can you help me?"),
|
|
new AssistantChatMessage("Of course! What do you need help with?"),
|
|
new UserChatMessage("What's the capital of France?")
|
|
};
|
|
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
|
messages.Add(new AssistantChatMessage(completion.Content[0].Text));
|
|
```
|
|
|
|
## Structured Outputs (JSON Schema)
|
|
|
|
```csharp
|
|
using System.Text.Json;
|
|
|
|
ChatCompletionOptions options = new()
|
|
{
|
|
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
|
|
jsonSchemaFormatName: "math_reasoning",
|
|
jsonSchema: BinaryData.FromBytes("""
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"steps": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"explanation": { "type": "string" },
|
|
"output": { "type": "string" }
|
|
},
|
|
"required": ["explanation", "output"],
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"final_answer": { "type": "string" }
|
|
},
|
|
"required": ["steps", "final_answer"],
|
|
"additionalProperties": false
|
|
}
|
|
"""u8.ToArray()),
|
|
jsonSchemaIsStrict: true)
|
|
};
|
|
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
|
[new UserChatMessage("How can I solve 8x + 7 = -23?")],
|
|
options);
|
|
|
|
using JsonDocument json = JsonDocument.Parse(completion.Content[0].Text);
|
|
Console.WriteLine($"Answer: {json.RootElement.GetProperty("final_answer")}");
|
|
```
|
|
|
|
## Reasoning Models (o1, o4-mini)
|
|
|
|
```csharp
|
|
ChatCompletionOptions options = new()
|
|
{
|
|
ReasoningEffortLevel = ChatReasoningEffortLevel.Low,
|
|
MaxOutputTokenCount = 100000
|
|
};
|
|
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
|
[
|
|
new DeveloperChatMessage("You are a helpful assistant"),
|
|
new UserChatMessage("Explain the theory of relativity")
|
|
], options);
|
|
```
|
|
|
|
## Azure AI Search Integration (RAG)
|
|
|
|
```csharp
|
|
using Azure.AI.OpenAI.Chat;
|
|
|
|
#pragma warning disable AOAI001
|
|
|
|
ChatCompletionOptions options = new();
|
|
options.AddDataSource(new AzureSearchChatDataSource()
|
|
{
|
|
Endpoint = new Uri(searchEndpoint),
|
|
IndexName = searchIndex,
|
|
Authentication = DataSourceAuthentication.FromApiKey(searchKey)
|
|
});
|
|
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
|
[new UserChatMessage("What health plans are available?")],
|
|
options);
|
|
|
|
ChatMessageContext context = completion.GetMessageContext();
|
|
if (context?.Intent is not null)
|
|
{
|
|
Console.WriteLine($"Intent: {context.Intent}");
|
|
}
|
|
foreach (ChatCitation citation in context?.Citations ?? [])
|
|
{
|
|
Console.WriteLine($"Citation: {citation.Content}");
|
|
}
|
|
```
|
|
|
|
## Embeddings
|
|
|
|
```csharp
|
|
using OpenAI.Embeddings;
|
|
|
|
EmbeddingClient embeddingClient = azureClient.GetEmbeddingClient("text-embedding-ada-002");
|
|
|
|
OpenAIEmbedding embedding = await embeddingClient.GenerateEmbeddingAsync("Hello, world!");
|
|
ReadOnlyMemory<float> vector = embedding.ToFloats();
|
|
|
|
Console.WriteLine($"Embedding dimensions: {vector.Length}");
|
|
```
|
|
|
|
### Batch Embeddings
|
|
|
|
```csharp
|
|
List<string> inputs = new()
|
|
{
|
|
"First document text",
|
|
"Second document text",
|
|
"Third document text"
|
|
};
|
|
|
|
OpenAIEmbeddingCollection embeddings = await embeddingClient.GenerateEmbeddingsAsync(inputs);
|
|
|
|
foreach (OpenAIEmbedding emb in embeddings)
|
|
{
|
|
Console.WriteLine($"Index {emb.Index}: {emb.ToFloats().Length} dimensions");
|
|
}
|
|
```
|
|
|
|
## Image Generation (DALL-E)
|
|
|
|
```csharp
|
|
using OpenAI.Images;
|
|
|
|
ImageClient imageClient = azureClient.GetImageClient("dall-e-3");
|
|
|
|
GeneratedImage image = await imageClient.GenerateImageAsync(
|
|
"A futuristic city skyline at sunset",
|
|
new ImageGenerationOptions
|
|
{
|
|
Size = GeneratedImageSize.W1024xH1024,
|
|
Quality = GeneratedImageQuality.High,
|
|
Style = GeneratedImageStyle.Vivid
|
|
});
|
|
|
|
Console.WriteLine($"Image URL: {image.ImageUri}");
|
|
```
|
|
|
|
## Audio (Whisper)
|
|
|
|
### Transcription
|
|
|
|
```csharp
|
|
using OpenAI.Audio;
|
|
|
|
AudioClient audioClient = azureClient.GetAudioClient("whisper");
|
|
|
|
AudioTranscription transcription = await audioClient.TranscribeAudioAsync(
|
|
"audio.mp3",
|
|
new AudioTranscriptionOptions
|
|
{
|
|
ResponseFormat = AudioTranscriptionFormat.Verbose,
|
|
Language = "en"
|
|
});
|
|
|
|
Console.WriteLine(transcription.Text);
|
|
```
|
|
|
|
### Text-to-Speech
|
|
|
|
```csharp
|
|
BinaryData speech = await audioClient.GenerateSpeechAsync(
|
|
"Hello, welcome to Azure OpenAI!",
|
|
GeneratedSpeechVoice.Alloy,
|
|
new SpeechGenerationOptions
|
|
{
|
|
SpeedRatio = 1.0f,
|
|
ResponseFormat = GeneratedSpeechFormat.Mp3
|
|
});
|
|
|
|
await File.WriteAllBytesAsync("output.mp3", speech.ToArray());
|
|
```
|
|
|
|
## Function Calling (Tools)
|
|
|
|
```csharp
|
|
ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool(
|
|
functionName: "get_current_weather",
|
|
functionDescription: "Get the current weather in a given location",
|
|
functionParameters: BinaryData.FromString("""
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"location": {
|
|
"type": "string",
|
|
"description": "The city and state, e.g. San Francisco, CA"
|
|
},
|
|
"unit": {
|
|
"type": "string",
|
|
"enum": ["celsius", "fahrenheit"]
|
|
}
|
|
},
|
|
"required": ["location"]
|
|
}
|
|
"""));
|
|
|
|
ChatCompletionOptions options = new()
|
|
{
|
|
Tools = { getCurrentWeatherTool }
|
|
};
|
|
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
|
[new UserChatMessage("What's the weather in Seattle?")],
|
|
options);
|
|
|
|
if (completion.FinishReason == ChatFinishReason.ToolCalls)
|
|
{
|
|
foreach (ChatToolCall toolCall in completion.ToolCalls)
|
|
{
|
|
Console.WriteLine($"Function: {toolCall.FunctionName}");
|
|
Console.WriteLine($"Arguments: {toolCall.FunctionArguments}");
|
|
}
|
|
}
|
|
```
|
|
|
|
## Key Types Reference
|
|
|
|
| Type | Purpose |
|
|
|------|---------|
|
|
| `AzureOpenAIClient` | Top-level client for Azure OpenAI |
|
|
| `ChatClient` | Chat completions |
|
|
| `EmbeddingClient` | Text embeddings |
|
|
| `ImageClient` | Image generation (DALL-E) |
|
|
| `AudioClient` | Audio transcription/TTS |
|
|
| `ChatCompletion` | Chat response |
|
|
| `ChatCompletionOptions` | Request configuration |
|
|
| `StreamingChatCompletionUpdate` | Streaming response chunk |
|
|
| `ChatMessage` | Base message type |
|
|
| `SystemChatMessage` | System prompt |
|
|
| `UserChatMessage` | User input |
|
|
| `AssistantChatMessage` | Assistant response |
|
|
| `DeveloperChatMessage` | Developer message (reasoning models) |
|
|
| `ChatTool` | Function/tool definition |
|
|
| `ChatToolCall` | Tool invocation request |
|
|
|
|
## Best Practices
|
|
|
|
1. **Use Entra ID in production** — Avoid API keys; use `DefaultAzureCredential`
|
|
2. **Reuse client instances** — Create once, share across requests
|
|
3. **Handle rate limits** — Implement exponential backoff for 429 errors
|
|
4. **Stream for long responses** — Use `CompleteChatStreamingAsync` for better UX
|
|
5. **Set appropriate timeouts** — Long completions may need extended timeouts
|
|
6. **Use structured outputs** — JSON schema ensures consistent response format
|
|
7. **Monitor token usage** — Track `completion.Usage` for cost management
|
|
8. **Validate tool calls** — Always validate function arguments before execution
|
|
|
|
## Error Handling
|
|
|
|
```csharp
|
|
using Azure;
|
|
|
|
try
|
|
{
|
|
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
|
}
|
|
catch (RequestFailedException ex) when (ex.Status == 429)
|
|
{
|
|
Console.WriteLine("Rate limited. Retry after delay.");
|
|
await Task.Delay(TimeSpan.FromSeconds(10));
|
|
}
|
|
catch (RequestFailedException ex) when (ex.Status == 400)
|
|
{
|
|
Console.WriteLine($"Bad request: {ex.Message}");
|
|
}
|
|
catch (RequestFailedException ex)
|
|
{
|
|
Console.WriteLine($"Azure OpenAI error: {ex.Status} - {ex.Message}");
|
|
}
|
|
```
|
|
|
|
## Related SDKs
|
|
|
|
| SDK | Purpose | Install |
|
|
|-----|---------|---------|
|
|
| `Azure.AI.OpenAI` | Azure OpenAI client (this SDK) | `dotnet add package Azure.AI.OpenAI` |
|
|
| `OpenAI` | OpenAI compatibility | `dotnet add package OpenAI` |
|
|
| `Azure.Identity` | Authentication | `dotnet add package Azure.Identity` |
|
|
| `Azure.Search.Documents` | AI Search for RAG | `dotnet add package Azure.Search.Documents` |
|
|
|
|
## Reference Links
|
|
|
|
| Resource | URL |
|
|
|----------|-----|
|
|
| NuGet Package | https://www.nuget.org/packages/Azure.AI.OpenAI |
|
|
| API Reference | https://learn.microsoft.com/dotnet/api/azure.ai.openai |
|
|
| Migration Guide (1.0→2.0) | https://learn.microsoft.com/azure/ai-services/openai/how-to/dotnet-migration |
|
|
| Quickstart | https://learn.microsoft.com/azure/ai-services/openai/quickstart |
|
|
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/openai/Azure.AI.OpenAI |
|