Skip to content

Commit 44be2bf

Browse files
added rest of the files
1 parent a69ff7f commit 44be2bf

File tree

4 files changed

+129
-27
lines changed

4 files changed

+129
-27
lines changed

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIChatClient.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,23 @@ void IDisposable.Dispose()
101101
// Nothing to dispose. Implementation required for the IChatClient interface.
102102
}
103103

104+
/// <summary>Converts an Extensions function to an OpenAI chat tool.</summary>
105+
internal static ChatTool ToOpenAIChatTool(AIFunction aiFunction)
106+
{
107+
bool? strict =
108+
aiFunction.AdditionalProperties.TryGetValue(OpenAIClientExtensions.StrictKey, out object? strictObj) &&
109+
strictObj is bool strictValue ?
110+
strictValue : null;
111+
112+
// Perform transformations making the schema legal per OpenAI restrictions
113+
JsonElement jsonSchema = OpenAIClientExtensions.GetSchema(aiFunction, strict);
114+
115+
// Map to an intermediate model so that redundant properties are skipped.
116+
var tool = JsonSerializer.Deserialize(jsonSchema, ChatClientJsonContext.Default.ChatToolJson)!;
117+
var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, ChatClientJsonContext.Default.ChatToolJson));
118+
return ChatTool.CreateFunctionTool(aiFunction.Name, aiFunction.Description, functionParameters, strict);
119+
}
120+
104121
/// <summary>Converts an Extensions chat message enumerable to an OpenAI chat message enumerable.</summary>
105122
private static IEnumerable<OpenAI.Chat.ChatMessage> ToOpenAIChatMessages(IEnumerable<ChatMessage> inputs, ChatOptions? chatOptions, JsonSerializerOptions jsonOptions)
106123
{
@@ -557,23 +574,6 @@ private ChatCompletionOptions ToOpenAIOptions(ChatOptions? options)
557574
return result;
558575
}
559576

560-
/// <summary>Converts an Extensions function to an OpenAI chat tool.</summary>
561-
private static ChatTool ToOpenAIChatTool(AIFunction aiFunction)
562-
{
563-
bool? strict =
564-
aiFunction.AdditionalProperties.TryGetValue(OpenAIClientExtensions.StrictKey, out object? strictObj) &&
565-
strictObj is bool strictValue ?
566-
strictValue : null;
567-
568-
// Perform transformations making the schema legal per OpenAI restrictions
569-
JsonElement jsonSchema = OpenAIClientExtensions.GetSchema(aiFunction, strict);
570-
571-
// Map to an intermediate model so that redundant properties are skipped.
572-
var tool = JsonSerializer.Deserialize(jsonSchema, ChatClientJsonContext.Default.ChatToolJson)!;
573-
var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, ChatClientJsonContext.Default.ChatToolJson));
574-
return ChatTool.CreateFunctionTool(aiFunction.Name, aiFunction.Description, functionParameters, strict);
575-
}
576-
577577
private static UsageDetails FromOpenAIUsage(ChatTokenUsage tokenUsage)
578578
{
579579
var destination = new UsageDetails

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
using System;
55
using System.Diagnostics.CodeAnalysis;
66
using System.Text.Json;
7+
using Microsoft.Shared.Diagnostics;
78
using OpenAI;
89
using OpenAI.Assistants;
910
using OpenAI.Audio;
1011
using OpenAI.Chat;
1112
using OpenAI.Embeddings;
13+
using OpenAI.RealtimeConversation;
1214
using OpenAI.Responses;
1315

1416
namespace Microsoft.Extensions.AI;
@@ -75,6 +77,33 @@ public static ISpeechToTextClient AsISpeechToTextClient(this AudioClient audioCl
7577
public static IEmbeddingGenerator<string, Embedding<float>> AsIEmbeddingGenerator(this EmbeddingClient embeddingClient, int? defaultModelDimensions = null) =>
7678
new OpenAIEmbeddingGenerator(embeddingClient, defaultModelDimensions);
7779

80+
/// <summary>Converts an Extensions function to an OpenAI chat tool.</summary>
81+
/// <param name="aiFunction">function to convert.</param>
82+
/// <returns> An OpenAI ChatTool representing the function.</returns>
83+
public static ChatTool AsOpenAIChatTool(this AIFunction aiFunction)
84+
{
85+
_ = Throw.IfNull(aiFunction);
86+
return OpenAIChatClient.ToOpenAIChatTool(aiFunction);
87+
}
88+
89+
/// <summary> Converts an Extensions function to an OpenAI response tool.</summary>
90+
/// <param name="aiFunction">The function to convert.</param>
91+
/// <returns>An OpenAI ResponseTool representing the function.</returns>
92+
public static ResponseTool AsOpenAIResponseTool(this AIFunction aiFunction)
93+
{
94+
_ = Throw.IfNull(aiFunction);
95+
return OpenAIResponseChatClient.ToResponseTool(aiFunction);
96+
}
97+
98+
/// <summary>Converts an Extensions function to an OpenAI ConversationFunctionTool.</summary>
99+
/// <param name="aiFunction">The function to convert.</param>
100+
/// <returns>A ConversationFunctionTool representing the function.</returns>
101+
public static ConversationFunctionTool AsOpenAIConversationFunctionTool(this AIFunction aiFunction)
102+
{
103+
_ = Throw.IfNull(aiFunction);
104+
return OpenAIRealtimeConversationClient.ToOpenAIConversationFunctionTool(aiFunction);
105+
}
106+
78107
/// <summary>Gets the JSON schema to use from the function.</summary>
79108
internal static JsonElement GetSchema(AIFunction function, bool? strict) =>
80109
strict is true ?
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Text.Json;
7+
using System.Text.Json.Serialization;
8+
using OpenAI.RealtimeConversation;
9+
10+
#pragma warning disable S907 // "goto" statement should not be used
11+
#pragma warning disable S1067 // Expressions should not be too complex
12+
#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields
13+
#pragma warning disable S3604 // Member initializer values should not be redundant
14+
#pragma warning disable SA1204 // Static elements should appear before instance elements
15+
16+
namespace Microsoft.Extensions.AI;
17+
18+
// this contains only tool conversion routines for now.
19+
internal sealed partial class OpenAIRealtimeConversationClient
20+
{
21+
public static ConversationFunctionTool ToOpenAIConversationFunctionTool(AIFunction aiFunction)
22+
{
23+
bool? strict =
24+
aiFunction.AdditionalProperties.TryGetValue(OpenAIClientExtensions.StrictKey, out object? strictObj) &&
25+
strictObj is bool strictValue ?
26+
strictValue : null;
27+
28+
string jsonSchema = OpenAIClientExtensions.GetSchema(aiFunction, strict).GetRawText();
29+
30+
var toolSchema = JsonSerializer.Deserialize(jsonSchema, RealtimeConversationClientJsonContext.Default.ConversationFunctionToolJson)!;
31+
var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(toolSchema, RealtimeConversationClientJsonContext.Default.ConversationFunctionToolJson));
32+
33+
var tool = new ConversationFunctionTool(aiFunction.Name)
34+
{
35+
Description = aiFunction.Description,
36+
Parameters = functionParameters,
37+
};
38+
return tool;
39+
}
40+
41+
/// <summary>Used to create the JSON payload for an OpenAI chat tool description.</summary>
42+
private sealed class ConversationFunctionToolJson
43+
{
44+
[JsonPropertyName("type")]
45+
public string Type { get; set; } = "object";
46+
47+
[JsonPropertyName("required")]
48+
public HashSet<string> Required { get; set; } = [];
49+
50+
[JsonPropertyName("properties")]
51+
public Dictionary<string, JsonElement> Properties { get; set; } = [];
52+
53+
[JsonPropertyName("additionalProperties")]
54+
public bool AdditionalProperties { get; set; }
55+
}
56+
57+
/// <summary>Source-generated JSON type information.</summary>
58+
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
59+
UseStringEnumConverter = true,
60+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
61+
WriteIndented = true)]
62+
[JsonSerializable(typeof(ConversationFunctionToolJson))]
63+
[JsonSerializable(typeof(IDictionary<string, object?>))]
64+
[JsonSerializable(typeof(string[]))]
65+
private sealed partial class RealtimeConversationClientJsonContext : JsonSerializerContext;
66+
}

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,21 @@ void IDisposable.Dispose()
324324
// Nothing to dispose. Implementation required for the IChatClient interface.
325325
}
326326

327+
internal static ResponseTool ToResponseTool(AIFunction aiFunction)
328+
{
329+
bool strict =
330+
aiFunction.AdditionalProperties.TryGetValue(OpenAIClientExtensions.StrictKey, out object? strictObj) &&
331+
strictObj is bool strictValue &&
332+
strictValue;
333+
334+
JsonElement jsonSchema = OpenAIClientExtensions.GetSchema(aiFunction, strict);
335+
336+
var oaitool = JsonSerializer.Deserialize(jsonSchema, ResponseClientJsonContext.Default.ResponseToolJson)!;
337+
var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(oaitool, ResponseClientJsonContext.Default.ResponseToolJson));
338+
ResponseTool rtool = ResponseTool.CreateFunctionTool(aiFunction.Name, aiFunction.Description, functionParameters, strict);
339+
return rtool;
340+
}
341+
327342
/// <summary>Creates a <see cref="ChatRole"/> from a <see cref="MessageRole"/>.</summary>
328343
private static ChatRole ToChatRole(MessageRole? role) =>
329344
role switch
@@ -374,16 +389,8 @@ private ResponseCreationOptions ToOpenAIResponseCreationOptions(ChatOptions? opt
374389
switch (tool)
375390
{
376391
case AIFunction aiFunction:
377-
bool strict =
378-
aiFunction.AdditionalProperties.TryGetValue(OpenAIClientExtensions.StrictKey, out object? strictObj) &&
379-
strictObj is bool strictValue &&
380-
strictValue;
381-
382-
JsonElement jsonSchema = OpenAIClientExtensions.GetSchema(aiFunction, strict);
383-
384-
var oaitool = JsonSerializer.Deserialize(jsonSchema, ResponseClientJsonContext.Default.ResponseToolJson)!;
385-
var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(oaitool, ResponseClientJsonContext.Default.ResponseToolJson));
386-
result.Tools.Add(ResponseTool.CreateFunctionTool(aiFunction.Name, aiFunction.Description, functionParameters, strict));
392+
ResponseTool rtool = ToResponseTool(aiFunction);
393+
result.Tools.Add(rtool);
387394
break;
388395

389396
case HostedWebSearchTool:

0 commit comments

Comments
 (0)