Skip to content

Commit cacf2f1

Browse files
committed
feat(showcase/ms-agent-dotnet): start D5 parity sweep
1 parent ef2d415 commit cacf2f1

300 files changed

Lines changed: 26788 additions & 22348 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

showcase/integrations/ms-agent-dotnet/agent/A2uiFixedSchemaAgent.cs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
/// owns a pre-authored component tree (see
1616
/// `src/app/demos/a2ui-fixed-schema/a2ui/definitions.ts` + flight_schema.json)
1717
/// and the agent only streams *data* into the data model via a dedicated
18-
/// `search_flights` tool that emits an <c>a2ui_operations</c> container.
18+
/// `display_flight` tool that emits an <c>a2ui_operations</c> container.
1919
/// The A2UI middleware detects that container in the tool result and
2020
/// forwards rendered surfaces to the frontend.
2121
/// </summary>
@@ -60,11 +60,11 @@ public AIAgent Create()
6060
chatClient,
6161
name: "A2uiFixedSchemaAgent",
6262
description: @"You help users find flights. When asked about a flight, call
63-
`search_flights` with origin, destination, airline, and price.
63+
`display_flight` with origin, destination, airline, and price.
6464
Use short airport codes (e.g. ""SFO"", ""JFK"") for origin/destination and a price
6565
string like ""$289"". Keep any chat reply to one short sentence.",
6666
tools: [
67-
AIFunctionFactory.Create(SearchFlights, options: new() { Name = "search_flights", SerializerOptions = _jsonSerializerOptions })
67+
AIFunctionFactory.Create(DisplayFlight, options: new() { Name = "display_flight", SerializerOptions = _jsonSerializerOptions })
6868
]);
6969
}
7070

@@ -127,33 +127,37 @@ public AIAgent Create()
127127
// @endregion[backend-schema-json-load]
128128

129129
[Description("Show a flight card for the given trip. Use short airport codes (e.g. SFO, JFK) for origin/destination and a price string like $289.")]
130-
private string SearchFlights(
130+
private object DisplayFlight(
131131
[Description("Origin airport code (e.g. SFO)")] string origin,
132132
[Description("Destination airport code (e.g. JFK)")] string destination,
133133
[Description("Airline name")] string airline,
134134
[Description("Price string (e.g. $289)")] string price)
135135
{
136-
_logger.LogInformation("FixedSchema SearchFlights: {Origin} -> {Destination} on {Airline} at {Price}", origin, destination, airline, price);
136+
_logger.LogInformation("FixedSchema DisplayFlight: {Origin} -> {Destination} on {Airline} at {Price}", origin, destination, airline, price);
137137

138138
var operations = new object[]
139139
{
140-
new { type = "create_surface", surfaceId = SurfaceId, catalogId = CatalogId },
141-
new { type = "update_components", surfaceId = SurfaceId, components = FlightSchema },
140+
new { version = "v0.9", createSurface = new { surfaceId = SurfaceId, catalogId = CatalogId } },
141+
new { version = "v0.9", updateComponents = new { surfaceId = SurfaceId, components = FlightSchema } },
142142
new
143143
{
144-
type = "update_data_model",
145-
surfaceId = SurfaceId,
146-
data = new
144+
version = "v0.9",
145+
updateDataModel = new
147146
{
148-
origin,
149-
destination,
150-
airline,
151-
price,
147+
surfaceId = SurfaceId,
148+
path = "/",
149+
value = new
150+
{
151+
origin,
152+
destination,
153+
airline,
154+
price,
155+
},
152156
},
153157
},
154158
};
155159

156-
return JsonSerializer.Serialize(new { a2ui_operations = operations });
160+
return new { a2ui_operations = operations };
157161
}
158162
// @endregion[backend-render-operations]
159163
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System.Net.Http.Headers;
2+
using System.Text;
3+
using System.Text.Json;
4+
using Microsoft.Extensions.Configuration;
5+
6+
internal static class A2uiSecondaryToolCaller
7+
{
8+
private const string DefaultOpenAiEndpoint = "https://models.inference.ai.azure.com";
9+
private const string DesignToolName = "_design_a2ui_surface";
10+
11+
internal static async Task<string?> GetDesignToolArgumentsAsync(
12+
IConfiguration configuration,
13+
string systemPrompt,
14+
string userContent,
15+
CancellationToken cancellationToken)
16+
{
17+
ArgumentNullException.ThrowIfNull(configuration);
18+
ArgumentNullException.ThrowIfNull(systemPrompt);
19+
ArgumentNullException.ThrowIfNull(userContent);
20+
21+
var endpoint = (Environment.GetEnvironmentVariable("OPENAI_BASE_URL") ?? DefaultOpenAiEndpoint).TrimEnd('/');
22+
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
23+
?? configuration["OPENAI_API_KEY"]
24+
?? configuration["GitHubToken"]
25+
?? "sk-mock-local";
26+
27+
using var httpClient = new HttpClient
28+
{
29+
BaseAddress = new Uri(endpoint + "/"),
30+
};
31+
32+
using var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
33+
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
34+
foreach (var header in AimockHeaderContext.Get())
35+
{
36+
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
37+
}
38+
39+
var payload = new
40+
{
41+
model = "gpt-4.1",
42+
messages = new object[]
43+
{
44+
new { role = "system", content = systemPrompt },
45+
new { role = "user", content = userContent },
46+
},
47+
tools = new object[]
48+
{
49+
new
50+
{
51+
type = "function",
52+
function = new
53+
{
54+
name = DesignToolName,
55+
description = "Render a dynamic A2UI v0.9 surface.",
56+
parameters = new
57+
{
58+
type = "object",
59+
properties = new Dictionary<string, object>
60+
{
61+
["surfaceId"] = new { type = "string" },
62+
["catalogId"] = new { type = "string" },
63+
["components"] = new { type = "array", items = new { type = "object" } },
64+
["data"] = new { type = "object" },
65+
},
66+
required = new[] { "surfaceId", "catalogId", "components" },
67+
},
68+
},
69+
},
70+
},
71+
tool_choice = new
72+
{
73+
type = "function",
74+
function = new { name = DesignToolName },
75+
},
76+
};
77+
78+
request.Content = new StringContent(
79+
JsonSerializer.Serialize(payload),
80+
Encoding.UTF8,
81+
"application/json");
82+
83+
using var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
84+
var body = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
85+
response.EnsureSuccessStatusCode();
86+
87+
using var document = JsonDocument.Parse(body);
88+
if (!document.RootElement.TryGetProperty("choices", out var choices) ||
89+
choices.ValueKind != JsonValueKind.Array ||
90+
choices.GetArrayLength() == 0)
91+
{
92+
return null;
93+
}
94+
95+
var message = choices[0].GetProperty("message");
96+
if (!message.TryGetProperty("tool_calls", out var toolCalls) ||
97+
toolCalls.ValueKind != JsonValueKind.Array ||
98+
toolCalls.GetArrayLength() == 0)
99+
{
100+
return null;
101+
}
102+
103+
var function = toolCalls[0].GetProperty("function");
104+
var toolName = function.TryGetProperty("name", out var nameElement)
105+
? nameElement.GetString()
106+
: null;
107+
if (!string.Equals(toolName, DesignToolName, StringComparison.Ordinal))
108+
{
109+
return null;
110+
}
111+
112+
if (!function.TryGetProperty("arguments", out var argumentsElement))
113+
{
114+
return null;
115+
}
116+
117+
return argumentsElement.ValueKind == JsonValueKind.String
118+
? argumentsElement.GetString()
119+
: argumentsElement.GetRawText();
120+
}
121+
}

0 commit comments

Comments
 (0)