Skip to content

Commit f8aeaa2

Browse files
committed
updates
1 parent 3c47e26 commit f8aeaa2

File tree

1 file changed

+204
-85
lines changed

1 file changed

+204
-85
lines changed

articles/ai-services/openai/includes/structured-outputs-dotnet.md

Lines changed: 204 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,155 @@
33
# [Microsoft Entra ID](#tab/dotnet-entra-id)
44

55
```csharp
6+
using Azure.AI.OpenAI;
7+
using Azure.Identity;
8+
using Newtonsoft.Json.Schema.Generation;
9+
using OpenAI.Chat;
10+
using System.ClientModel;
11+
12+
// Create the clients
13+
string endpoint = GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
14+
15+
AzureOpenAIClient openAIClient = new(
16+
new Uri(endpoint),
17+
new DefaultAzureCredential());
18+
19+
var client = openAIClient.GetChatClient("gpt-4o");
20+
21+
// Create a chat with initial prompts
22+
var chat = new List<ChatMessage>()
23+
{
24+
new SystemChatMessage("Extract the event information and projected weather."),
25+
new UserChatMessage("Alice and Bob are going to a science fair in Seattle on June 1st, 2025.")
26+
};
27+
28+
// Get the schema of the class for the structured response
29+
JSchemaGenerator generator = new JSchemaGenerator();
30+
var jsonSchema = generator.Generate(typeof(CalendarEvent)).ToString();
31+
32+
// Get a completion with structured output
33+
var chatUpdates = client.CompleteChatStreamingAsync(
34+
chat,
35+
new ChatCompletionOptions()
36+
{
37+
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
38+
"calenderEvent",
39+
BinaryData.FromString(jsonSchema))
40+
});
41+
42+
// Write the structured response
43+
await foreach (var chatUpdate in chatUpdates)
44+
{
45+
foreach (var contentPart in chatUpdate.ContentUpdate)
46+
{
47+
Console.Write(contentPart.Text);
48+
}
49+
}
50+
51+
// The class for the structured response
52+
public class CalendarEvent()
53+
{
54+
public string Name { get; set; }
55+
public string Date { get; set; }
56+
public List<string> Participants { get; set; }
57+
}
658

759
```
860

961
# [Key-based auth](#tab/dotnet-keys)
1062

1163
```csharp
64+
using Azure.AI.OpenAI;
65+
using Newtonsoft.Json.Schema.Generation;
66+
using OpenAI.Chat;
67+
using System.ClientModel;
1268

13-
```
69+
// Create the clients
70+
string endpoint = GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
71+
string key = GetEnvironmentVariable("AZURE_OPENAI_API_KEY");
1472

15-
---
73+
AzureOpenAIClient openAIClient = new(
74+
new Uri(endpoint),
75+
new ApiKeyCredential(key));
1676

17-
## Function calling with structured outputs
77+
var client = openAIClient.GetChatClient("gpt-4o");
1878

19-
Structured Outputs for function calling can be enabled with a single parameter, by supplying `strict: true`.
79+
// Create a chat with initial prompts
80+
var chat = new List<ChatMessage>()
81+
{
82+
new SystemChatMessage("Extract the event information and projected weather."),
83+
new UserChatMessage("Alice and Bob are going to a science fair in Seattle on June 1st, 2025.")
84+
};
2085

21-
> [!NOTE]
22-
> Structured outputs are not supported with parallel function calls. When using structured outputs set `parallel_tool_calls` to `false`.
86+
// Get the schema of the class for the structured response
87+
JSchemaGenerator generator = new JSchemaGenerator();
88+
var jsonSchema = generator.Generate(typeof(CalendarEvent)).ToString();
2389

24-
# [Microsoft Entra ID](#tab/dotnet-entra-id)
90+
// Get a completion with structured output
91+
var chatUpdates = client.CompleteChatStreamingAsync(
92+
chat,
93+
new ChatCompletionOptions()
94+
{
95+
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
96+
"calenderEvent",
97+
BinaryData.FromString(jsonSchema))
98+
});
2599

26-
```csharp
100+
// Write the structured response
101+
await foreach (var chatUpdate in chatUpdates)
102+
{
103+
foreach (var contentPart in chatUpdate.ContentUpdate)
104+
{
105+
Console.Write(contentPart.Text);
106+
}
107+
}
108+
109+
// The class for the structured response
110+
public class CalendarEvent()
111+
{
112+
public string Name { get; set; }
113+
public string Date { get; set; }
114+
public List<string> Participants { get; set; }
115+
}
27116

28117
```
29118

30-
# [Key-based auth](#tab/dotnet-keys)
119+
---
120+
121+
## Function calling with structured outputs
122+
123+
Structured Outputs for function calling can be enabled with a single parameter, by supplying `strict: true`.
124+
125+
# [Microsoft Entra ID](#tab/dotnet-entra-id)
31126

32127
```csharp
33128
using Azure.AI.OpenAI;
34-
using Azure.Identity;
35-
using Newtonsoft.Json.Schema;
36129
using Newtonsoft.Json.Schema.Generation;
37-
using OpenAI.Assistants;
38130
using OpenAI.Chat;
39-
using OpenAI.Files;
40-
using System;
41131
using System.ClientModel;
42-
using System.Text;
43-
using System.Text.Json;
44132

133+
// Create the clients
134+
string endpoint = GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
135+
136+
AzureOpenAIClient openAIClient = new(
137+
new Uri(endpoint),
138+
new DefaultAzureCredential());
139+
140+
var chatClient = openAIClient.GetChatClient("gpt-4o");
141+
142+
// Local function to be used by the assistant tooling
45143
string GetTemperature(string location, string date)
46144
{
47-
// Call the weather API here.
145+
// Placeholder for Weather API
48146
if(location == "Seattle" && date == "2025-06-01")
49147
{
50-
return "45";
148+
return "75";
51149
}
52150

53151
return "50";
54152
}
55153

154+
// Create a tool to get the temperature
56155
ChatTool GetTemperatureTool = ChatTool.CreateFunctionTool(
57156
functionName: nameof(GetTemperature),
58157
functionSchemaIsStrict: true,
@@ -76,28 +175,20 @@ ChatTool GetTemperatureTool = ChatTool.CreateFunctionTool(
76175
"""u8.ToArray())
77176
);
78177

79-
AzureOpenAIClient openAIClient = new(new Uri(""), new ApiKeyCredential(""));
80-
178+
// Create a chat with prompts
81179
var chat = new List<ChatMessage>()
82180
{
83181
new SystemChatMessage("Extract the event information and projected weather."),
84182
new UserChatMessage("Alice and Bob are going to a science fair in Seattle on June 1st, 2025.")
85183
};
86184

185+
// Create a JSON schema for the CalendarEvent structured response
87186
JSchemaGenerator generator = new JSchemaGenerator();
88187
var schema = generator.Generate(typeof(CalendarEvent));
89188
string jsonSchema = schema.ToString();
90189

91-
var json = Encoding.UTF8.GetBytes(schema.ToString());
92-
93-
var client = openAIClient.GetChatClient("gpt-4o");
94-
95-
bool requiresAction;
96-
97-
do
98-
{
99-
requiresAction = false;
100-
var completion = client.CompleteChat(
190+
// Get a chat completion from the AI model
191+
var completion = chatClient.CompleteChat(
101192
chat,
102193
new ChatCompletionOptions()
103194
{
@@ -107,70 +198,98 @@ do
107198
Tools = { GetTemperatureTool }
108199
});
109200

110-
switch (completion.Value.FinishReason)
111-
{
112-
case ChatFinishReason.Stop:
113-
{
114-
// Add the assistant message to the conversation history.
115-
chat.Add(new AssistantChatMessage(completion));
116-
Console.WriteLine(completion.Value.Content[0].Text);
117-
break;
118-
}
119-
120-
case ChatFinishReason.ToolCalls:
121-
{
122-
// First, add the assistant message with tool calls to the conversation history.
123-
chat.Add(new AssistantChatMessage(completion));
124-
125-
// Then, add a new tool message for each tool call that is resolved.
126-
foreach (ChatToolCall toolCall in completion.Value.ToolCalls)
127-
{
128-
switch (toolCall.FunctionName)
129-
{
130-
case nameof(GetTemperature):
131-
{
132-
using JsonDocument argumentsJson = JsonDocument.Parse(toolCall.FunctionArguments);
133-
bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location);
134-
bool hasDate = argumentsJson.RootElement.TryGetProperty("date", out JsonElement date);
135-
136-
if (!hasLocation || !hasDate)
137-
{
138-
throw new ArgumentNullException(nameof(location), "The location and date arguments are required.");
139-
}
140-
141-
string toolResult = GetTemperature(location.GetString(), date.GetString());
142-
chat.Add(new ToolChatMessage(toolCall.Id, toolResult));
143-
break;
144-
}
145-
146-
default:
147-
{
148-
// Handle other unexpected calls.
149-
throw new NotImplementedException();
150-
}
151-
}
152-
}
201+
Console.WriteLine(completion.Value.ToolCalls[0].FunctionName);
202+
203+
// Structured response class
204+
public class CalendarEvent()
205+
{
206+
public string Name { get; set; }
207+
public string Date { get; set; }
208+
public string Temperature { get; set; }
209+
public List<string> Participants { get; set; }
210+
}
211+
```
153212

154-
requiresAction = true;
155-
break;
156-
}
213+
# [Key-based auth](#tab/dotnet-keys)
214+
215+
```csharp
216+
using Azure.AI.OpenAI;
217+
using Newtonsoft.Json.Schema.Generation;
218+
using OpenAI.Chat;
219+
using System.ClientModel;
157220

158-
case ChatFinishReason.Length:
159-
throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded.");
221+
// Create the clients
222+
string endpoint = GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
223+
string key = GetEnvironmentVariable("AZURE_OPENAI_API_KEY");
160224

161-
case ChatFinishReason.ContentFilter:
162-
throw new NotImplementedException("Omitted content due to a content filter flag.");
225+
AzureOpenAIClient openAIClient = new(
226+
new Uri(endpoint),
227+
new ApiKeyCredential(key));
163228

164-
case ChatFinishReason.FunctionCall:
165-
throw new NotImplementedException("Deprecated in favor of tool calls.");
229+
var chatClient = openAIClient.GetChatClient("gpt-4o");
166230

167-
default:
168-
throw new NotImplementedException(completion.Value.FinishReason.ToString());
231+
// Local function to be used by the assistant tooling
232+
string GetTemperature(string location, string date)
233+
{
234+
// Placeholder for Weather API
235+
if(location == "Seattle" && date == "2025-06-01")
236+
{
237+
return "75";
169238
}
170239

171-
} while (requiresAction);
240+
return "50";
241+
}
242+
243+
// Create a tool to get the temperature
244+
ChatTool GetTemperatureTool = ChatTool.CreateFunctionTool(
245+
functionName: nameof(GetTemperature),
246+
functionSchemaIsStrict: true,
247+
functionDescription: "Get the projected temperature by date and location.",
248+
functionParameters: BinaryData.FromBytes("""
249+
{
250+
"type": "object",
251+
"properties": {
252+
"location": {
253+
"type": "string",
254+
"description": "The location of the weather."
255+
},
256+
"date": {
257+
"type": "string",
258+
"description": "The date of the projected weather."
259+
}
260+
},
261+
"required": ["location", "date"],
262+
"additionalProperties": false
263+
}
264+
"""u8.ToArray())
265+
);
266+
267+
// Create a chat with prompts
268+
var chat = new List<ChatMessage>()
269+
{
270+
new SystemChatMessage("Extract the event information and projected weather."),
271+
new UserChatMessage("Alice and Bob are going to a science fair in Seattle on June 1st, 2025.")
272+
};
273+
274+
// Create a JSON schema for the CalendarEvent structured response
275+
JSchemaGenerator generator = new JSchemaGenerator();
276+
var schema = generator.Generate(typeof(CalendarEvent));
277+
string jsonSchema = schema.ToString();
278+
279+
// Get a chat completion from the AI model
280+
var completion = chatClient.CompleteChat(
281+
chat,
282+
new ChatCompletionOptions()
283+
{
284+
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
285+
"calenderEvent",
286+
BinaryData.FromString(jsonSchema)),
287+
Tools = { GetTemperatureTool }
288+
});
172289

290+
Console.WriteLine(completion.Value.ToolCalls[0].FunctionName);
173291

292+
// Structured response class
174293
public class CalendarEvent()
175294
{
176295
public string Name { get; set; }

0 commit comments

Comments
 (0)