From 073cb8f7b797b5408615cf6c955c8d8a7f263baf Mon Sep 17 00:00:00 2001
From: manvkaur <67894494+manvkaur@users.noreply.github.com>
Date: Wed, 2 Apr 2025 20:25:15 -0700
Subject: [PATCH 01/21] migrate to azure open ai
---
.../Assistants/ChatMessage.cs | 7 --
.../AzureAISearchProvider.cs | 6 +-
.../CosmosDBSearchProvider.cs | 7 +-
.../KustoSearchProvider.cs | 6 +-
.../Assistants/AssistantRuntimeState.cs | 4 +-
.../Assistants/AssistantService.cs | 98 +++++++++--------
.../AssistantSkillTriggerBindingProvider.cs | 4 +-
.../ChatCompletionsJsonConverter.cs | 10 +-
.../Assistants/IAssistantSkillInvoker.cs | 42 ++++----
.../Embeddings/EmbeddingsContext.cs | 13 ++-
.../Embeddings/EmbeddingsContextConverter.cs | 8 +-
.../Embeddings/EmbeddingsConverter.cs | 22 ++--
.../Embeddings/EmbeddingsHelper.cs | 6 +-
.../EmbeddingsOptionsJsonConverter.cs | 10 +-
.../Embeddings/EmbeddingsStoreConverter.cs | 27 ++---
.../{ChatMessage.cs => AssistantMessage.cs} | 13 +--
.../Models/AssistantState.cs | 4 +-
.../Models/ChatMessageTableEntity.cs | 100 +++++++++++++++++-
.../OpenAIClientFactory.cs | 61 +++++++++++
.../OpenAIExtension.cs | 30 +++---
.../OpenAIWebJobsBuilderExtensions.cs | 30 ++----
.../Search/SearchableDocumentJsonConverter.cs | 20 ++--
.../Search/SemanticSearchContext.cs | 8 +-
.../Search/SemanticSearchConverter.cs | 41 +++----
.../TextCompletionAttribute.cs | 18 +---
.../TextCompletionConverter.cs | 28 ++---
.../WebJobs.Extensions.OpenAI.csproj | 4 +-
27 files changed, 382 insertions(+), 245 deletions(-)
rename src/WebJobs.Extensions.OpenAI/Models/{ChatMessage.cs => AssistantMessage.cs} (71%)
create mode 100644 src/WebJobs.Extensions.OpenAI/OpenAIClientFactory.cs
diff --git a/src/Functions.Worker.Extensions.OpenAI/Assistants/ChatMessage.cs b/src/Functions.Worker.Extensions.OpenAI/Assistants/ChatMessage.cs
index 585fc205..bbd14017 100644
--- a/src/Functions.Worker.Extensions.OpenAI/Assistants/ChatMessage.cs
+++ b/src/Functions.Worker.Extensions.OpenAI/Assistants/ChatMessage.cs
@@ -19,7 +19,6 @@ public ChatMessage(string content, string role, string? name)
{
this.Content = content;
this.Role = role;
- this.Name = name;
}
///
@@ -33,10 +32,4 @@ public ChatMessage(string content, string role, string? name)
///
[JsonPropertyName("role")]
public string Role { get; set; }
-
- ///
- /// Gets or sets the name of the calling function if applicable.
- ///
- [JsonPropertyName("name")]
- public string? Name { get; set; }
}
diff --git a/src/WebJobs.Extensions.OpenAI.AzureAISearch/AzureAISearchProvider.cs b/src/WebJobs.Extensions.OpenAI.AzureAISearch/AzureAISearchProvider.cs
index 4ad8280e..14abae5b 100644
--- a/src/WebJobs.Extensions.OpenAI.AzureAISearch/AzureAISearchProvider.cs
+++ b/src/WebJobs.Extensions.OpenAI.AzureAISearch/AzureAISearchProvider.cs
@@ -246,16 +246,16 @@ async Task IndexSectionsAsync(SearchClient searchClient, SearchableDocument docu
{
int iteration = 0;
IndexDocumentsBatch batch = new();
- for (int i = 0; i < document.Embeddings?.Response?.Data.Count; i++)
+ for (int i = 0; i < document.Embeddings?.Response?.Count; i++)
{
batch.Actions.Add(new IndexDocumentsAction(
IndexActionType.MergeOrUpload,
new SearchDocument
{
["id"] = Guid.NewGuid().ToString("N"),
- ["text"] = document.Embeddings.Request.Input![i],
+ ["text"] = document.Embeddings.Input![i],
["title"] = Path.GetFileNameWithoutExtension(document.Title),
- ["embeddings"] = document.Embeddings.Response.Data[i].Embedding.ToArray() ?? Array.Empty(),
+ ["embeddings"] = document.Embeddings.Response[i].ToFloats().ToArray() ?? Array.Empty(),
["timestamp"] = DateTime.UtcNow
}));
iteration++;
diff --git a/src/WebJobs.Extensions.OpenAI.CosmosDBSearch/CosmosDBSearchProvider.cs b/src/WebJobs.Extensions.OpenAI.CosmosDBSearch/CosmosDBSearchProvider.cs
index 8dbfc5f8..cd5cc65e 100644
--- a/src/WebJobs.Extensions.OpenAI.CosmosDBSearch/CosmosDBSearchProvider.cs
+++ b/src/WebJobs.Extensions.OpenAI.CosmosDBSearch/CosmosDBSearchProvider.cs
@@ -211,7 +211,7 @@ public void CreateVectorIndexIfNotExists(MongoClient cosmosClient)
async Task UpsertVectorAsync(MongoClient cosmosClient, SearchableDocument document)
{
List list = new();
- for (int i = 0; i < document.Embeddings?.Response?.Data.Count; i++)
+ for (int i = 0; i < document.Embeddings?.Response?.Count; i++)
{
BsonDocument vectorDocument =
new()
@@ -219,15 +219,14 @@ async Task UpsertVectorAsync(MongoClient cosmosClient, SearchableDocument docume
{ "id", Guid.NewGuid().ToString("N") },
{
this.cosmosDBSearchConfigOptions.Value.TextKey,
- document.Embeddings.Request.Input![i]
+ document.Embeddings.Input![i]
},
{ "title", Path.GetFileNameWithoutExtension(document.Title) },
{
this.cosmosDBSearchConfigOptions.Value.EmbeddingKey,
new BsonArray(
document
- .Embeddings.Response.Data[i]
- .Embedding.ToArray()
+ .Embeddings.Response[i].ToFloats().ToArray()
.Select(e => new BsonDouble(Convert.ToDouble(e)))
)
},
diff --git a/src/WebJobs.Extensions.OpenAI.Kusto/KustoSearchProvider.cs b/src/WebJobs.Extensions.OpenAI.Kusto/KustoSearchProvider.cs
index 542454a3..156868bb 100644
--- a/src/WebJobs.Extensions.OpenAI.Kusto/KustoSearchProvider.cs
+++ b/src/WebJobs.Extensions.OpenAI.Kusto/KustoSearchProvider.cs
@@ -78,13 +78,13 @@ public async Task AddDocumentAsync(SearchableDocument document, CancellationToke
table.AppendColumn("Embeddings", typeof(object));
table.AppendColumn("Timestamp", typeof(DateTime));
- for (int i = 0; i < document.Embeddings?.Response?.Data.Count; i++)
+ for (int i = 0; i < document.Embeddings?.Response?.Count; i++)
{
table.Rows.Add(
Guid.NewGuid().ToString("N"),
Path.GetFileNameWithoutExtension(document.Title),
- document.Embeddings.Request.Input![i],
- GetEmbeddingsString(document.Embeddings.Response.Data[i].Embedding, true),
+ document.Embeddings.Input![i],
+ GetEmbeddingsString(document.Embeddings.Response[i].ToFloats().ToArray(), true),
DateTime.UtcNow);
}
diff --git a/src/WebJobs.Extensions.OpenAI/Assistants/AssistantRuntimeState.cs b/src/WebJobs.Extensions.OpenAI/Assistants/AssistantRuntimeState.cs
index 55dbd2f5..03b10f6d 100644
--- a/src/WebJobs.Extensions.OpenAI/Assistants/AssistantRuntimeState.cs
+++ b/src/WebJobs.Extensions.OpenAI/Assistants/AssistantRuntimeState.cs
@@ -6,13 +6,13 @@
namespace Microsoft.Azure.WebJobs.Extensions.OpenAI.Assistants;
-record struct MessageRecord(DateTime Timestamp, ChatMessage ChatMessageEntity);
+record struct MessageRecord(DateTime Timestamp, AssistantMessage ChatMessageEntity);
[JsonObject(MemberSerialization.OptIn)]
class AssistantRuntimeState
{
[JsonProperty("messages")]
- public List? ChatMessages { get; set; }
+ public List? ChatMessages { get; set; }
[JsonProperty("totalTokens")]
public int TotalTokens { get; set; } = 0;
diff --git a/src/WebJobs.Extensions.OpenAI/Assistants/AssistantService.cs b/src/WebJobs.Extensions.OpenAI/Assistants/AssistantService.cs
index e31e8a3b..56f4abdb 100644
--- a/src/WebJobs.Extensions.OpenAI/Assistants/AssistantService.cs
+++ b/src/WebJobs.Extensions.OpenAI/Assistants/AssistantService.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.ClientModel;
using Azure;
using Azure.AI.OpenAI;
using Azure.Data.Tables;
@@ -8,6 +9,7 @@
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using OpenAI.Chat;
namespace Microsoft.Azure.WebJobs.Extensions.OpenAI.Assistants;
@@ -28,7 +30,7 @@ record InternalChatState(string Id, AssistantStateEntity Metadata, List
const int FunctionCallBatchLimit = 50;
const string DefaultChatStorage = "AzureWebJobsStorage";
- readonly OpenAIClient openAIClient;
+ readonly ChatClient chatClient;
readonly IAssistantSkillInvoker skillInvoker;
readonly ILogger logger;
readonly AzureComponentFactory azureComponentFactory;
@@ -37,7 +39,7 @@ record InternalChatState(string Id, AssistantStateEntity Metadata, List();
this.azureComponentFactory = azureComponentFactory ?? throw new ArgumentNullException(nameof(azureComponentFactory));
@@ -117,7 +121,8 @@ async Task DeleteBatch()
partitionKey: request.Id,
messageIndex: 1, // 1-based index
content: request.Instructions,
- role: ChatRole.System);
+ role: ChatMessageRole.System,
+ toolCalls: null);
batch.Add(new TableTransactionAction(TableTransactionActionType.Add, chatMessageEntity));
}
@@ -152,7 +157,7 @@ public async Task GetStateAsync(AssistantQueryAttribute assistan
if (chatState is null)
{
this.logger.LogWarning("No assistant exists with ID = '{Id}'", id);
- return new AssistantState(id, false, default, default, 0, 0, Array.Empty());
+ return new AssistantState(id, false, default, default, 0, 0, Array.Empty());
}
List filteredChatMessages = chatState.Messages
@@ -171,7 +176,7 @@ public async Task GetStateAsync(AssistantQueryAttribute assistan
chatState.Metadata.LastUpdatedAt,
chatState.Metadata.TotalMessages,
chatState.Metadata.TotalTokens,
- filteredChatMessages.Select(msg => new ChatMessage(msg.Content, msg.Role, msg.Name)).ToList());
+ filteredChatMessages.Select(msg => new AssistantMessage(msg.Content, msg.Role)).ToList());
return state;
}
@@ -198,7 +203,7 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
if (chatState is null || !chatState.Metadata.Exists)
{
this.logger.LogWarning("[{Id}] Ignoring request sent to nonexistent assistant.", attribute.Id);
- return new AssistantState(attribute.Id, false, default, default, 0, 0, Array.Empty());
+ return new AssistantState(attribute.Id, false, default, default, 0, 0, Array.Empty());
}
this.logger.LogInformation("[{Id}] Received message: {Text}", attribute.Id, attribute.UserMessage);
@@ -211,51 +216,55 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
partitionKey: attribute.Id,
messageIndex: ++chatState.Metadata.TotalMessages,
content: attribute.UserMessage,
- role: ChatRole.User);
+ role: ChatMessageRole.User,
+ toolCalls: null);
chatState.Messages.Add(chatMessageEntity);
// Add the chat message to the batch
batch.Add(new TableTransactionAction(TableTransactionActionType.Add, chatMessageEntity));
string deploymentName = attribute.Model ?? OpenAIModels.DefaultChatModel;
- IList? functions = this.skillInvoker.GetFunctionsDefinitions();
+ IList? functions = this.skillInvoker.GetFunctionsDefinitions();
// We loop if the model returns function calls. Otherwise, we break after receiving a response.
while (true)
{
// Get the next response from the LLM
- ChatCompletionsOptions chatRequest = new(deploymentName, ToOpenAIChatRequestMessages(chatState.Messages));
+ ChatCompletionOptions chatRequest = new ChatCompletionOptions();
if (functions is not null)
{
- foreach (ChatCompletionsFunctionToolDefinition fn in functions)
+ foreach (ChatTool fn in functions)
{
chatRequest.Tools.Add(fn);
}
}
+ chatRequest.ToolChoice = ChatToolChoice.CreateAutoChoice();
+ IEnumerable chatMessages = ToOpenAIChatRequestMessages(chatState.Messages);
- Response response = await this.openAIClient.GetChatCompletionsAsync(
- chatRequest,
- cancellationToken);
+ // ToDo: Pass more ChatCompletionOptions like TextCompletion
+ ClientResult response = await this.chatClient.CompleteChatAsync(chatMessages, chatRequest);
// We don't normally expect more than one message, but just in case we get multiple messages,
// return all of them separated by two newlines.
string replyMessage = string.Join(
Environment.NewLine + Environment.NewLine,
- response.Value.Choices.Select(choice => choice.Message.Content));
- if (!string.IsNullOrWhiteSpace(replyMessage))
+ response.Value.Content.Select(message => message.Text));
+ if (!string.IsNullOrWhiteSpace(replyMessage) || response.Value.ToolCalls.Any())
{
this.logger.LogInformation(
- "[{Id}] Got LLM response consisting of {Count} tokens: {Text}",
+ "[{Id}] Got LLM response consisting of {Count} tokens: [{Text}] && {Count} ToolCalls",
attribute.Id,
- response.Value.Usage.CompletionTokens,
- replyMessage);
+ response.Value.Usage.OutputTokenCount,
+ replyMessage,
+ response.Value.ToolCalls.Count);
// Add the user message as a new Chat message entity
ChatMessageTableEntity replyFromAssistantEntity = new(
partitionKey: attribute.Id,
messageIndex: ++chatState.Metadata.TotalMessages,
content: replyMessage,
- role: ChatRole.Assistant);
+ role: ChatMessageRole.Assistant,
+ toolCalls: response.Value.ToolCalls);
chatState.Messages.Add(replyFromAssistantEntity);
// Add the reply from assistant chat message to the batch
@@ -268,12 +277,11 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
}
// Set the total tokens that have been consumed.
- chatState.Metadata.TotalTokens = response.Value.Usage.TotalTokens;
+ chatState.Metadata.TotalTokens = response.Value.Usage.TotalTokenCount;
// Check for function calls (which are described in the API as tools)
- List functionCalls = response.Value.Choices
- .SelectMany(c => c.Message.ToolCalls)
- .OfType()
+ List functionCalls = response.Value.ToolCalls
+ .OfType()
.ToList();
if (functionCalls.Count == 0)
{
@@ -299,16 +307,17 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
attribute.Id,
functionCalls.Count);
+
// Invoke the function calls and add the responses to the chat history.
List> tasks = new(capacity: functionCalls.Count);
- foreach (ChatCompletionsFunctionToolCall call in functionCalls)
+ foreach (ChatToolCall call in functionCalls)
{
// CONSIDER: Call these in parallel
this.logger.LogInformation(
"[{Id}] Calling function '{Name}' with arguments: {Args}",
attribute.Id,
- call.Name,
- call.Arguments);
+ call.FunctionName,
+ call.FunctionArguments);
string? functionResult;
try
@@ -320,7 +329,7 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
this.logger.LogInformation(
"[{id}] Function '{Name}' returned the following content: {Content}",
attribute.Id,
- call.Name,
+ call.FunctionName,
functionResult);
}
catch (Exception ex)
@@ -329,7 +338,7 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
ex,
"[{id}] Function '{Name}' failed with an unhandled exception",
attribute.Id,
- call.Name);
+ call.FunctionName);
// CONSIDER: Automatic retries?
functionResult = "The function call failed. Let the user know and ask if they'd like you to try again";
@@ -346,9 +355,10 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
ChatMessageTableEntity functionResultEntity = new(
partitionKey: attribute.Id,
messageIndex: ++chatState.Metadata.TotalMessages,
- content: functionResult,
- role: ChatRole.Function,
- name: call.Name);
+ content: $"Function Name: '{call.FunctionName}' and Function Result: '{functionResult}'",
+ role: ChatMessageRole.Tool,
+ name: call.Id,
+ toolCalls: null);
chatState.Messages.Add(functionResultEntity);
batch.Add(new TableTransactionAction(TableTransactionActionType.Add, functionResultEntity));
@@ -365,7 +375,7 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
// return the latest assistant message in the chat state
List filteredChatMessages = chatState.Messages
- .Where(msg => msg.CreatedAt > timeFilter && msg.Role == ChatRole.Assistant)
+ .Where(msg => msg.CreatedAt > timeFilter && msg.Role == ChatMessageRole.Assistant.ToString())
.ToList();
this.logger.LogInformation(
@@ -381,7 +391,7 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
chatState.Metadata.LastUpdatedAt,
chatState.Metadata.TotalMessages,
chatState.Metadata.TotalTokens,
- filteredChatMessages.Select(msg => new ChatMessage(msg.Content, msg.Role, msg.Name)).ToList());
+ filteredChatMessages.Select(msg => new AssistantMessage(msg.Content, msg.Role)).ToList());
return state;
}
@@ -420,26 +430,30 @@ public async Task PostMessageAsync(AssistantPostAttribute attrib
return new InternalChatState(id, assistantStateEntity, chatMessageList);
}
- static IEnumerable ToOpenAIChatRequestMessages(IEnumerable entities)
+ static IEnumerable ToOpenAIChatRequestMessages(IEnumerable entities)
{
foreach (ChatMessageTableEntity entity in entities)
{
switch (entity.Role.ToLowerInvariant())
{
case "user":
- yield return new ChatRequestUserMessage(entity.Content);
+ yield return new UserChatMessage(entity.Content);
break;
case "assistant":
- yield return new ChatRequestAssistantMessage(entity.Content);
+ if (entity.ToolCalls != null && entity.ToolCalls.Any())
+ {
+ yield return new AssistantChatMessage(entity.ToolCalls);
+ }
+ else
+ {
+ yield return new AssistantChatMessage(entity.Content);
+ }
break;
case "system":
- yield return new ChatRequestSystemMessage(entity.Content);
- break;
- case "function":
- yield return new ChatRequestFunctionMessage(entity.Name, entity.Content);
+ yield return new SystemChatMessage(entity.Content);
break;
case "tool":
- yield return new ChatRequestToolMessage(entity.Content, toolCallId: entity.Name);
+ yield return new ToolChatMessage(toolCallId: entity.Name, entity.Content);
break;
default:
throw new InvalidOperationException($"Unknown chat role '{entity.Role}'");
diff --git a/src/WebJobs.Extensions.OpenAI/Assistants/AssistantSkillTriggerBindingProvider.cs b/src/WebJobs.Extensions.OpenAI/Assistants/AssistantSkillTriggerBindingProvider.cs
index 55eed508..c4dd70f8 100644
--- a/src/WebJobs.Extensions.OpenAI/Assistants/AssistantSkillTriggerBindingProvider.cs
+++ b/src/WebJobs.Extensions.OpenAI/Assistants/AssistantSkillTriggerBindingProvider.cs
@@ -113,10 +113,10 @@ public Task BindAsync(object value, ValueBindingContext context)
SkillInvocationContext skillInvocationContext = (SkillInvocationContext)value;
object? convertedValue;
- if (!string.IsNullOrEmpty(skillInvocationContext.Arguments))
+ if (!string.IsNullOrEmpty(skillInvocationContext.Arguments.ToString()))
{
// We expect that input to always be a string value in the form {"paramName":paramValue}
- JObject argsJson = JObject.Parse(skillInvocationContext.Arguments);
+ JObject argsJson = JObject.Parse(skillInvocationContext.Arguments.ToString());
JToken? paramValue = argsJson[this.parameterInfo.Name];
convertedValue = paramValue?.ToObject(destinationType);
}
diff --git a/src/WebJobs.Extensions.OpenAI/Assistants/ChatCompletionsJsonConverter.cs b/src/WebJobs.Extensions.OpenAI/Assistants/ChatCompletionsJsonConverter.cs
index f8ad8c8c..6d0b7e49 100644
--- a/src/WebJobs.Extensions.OpenAI/Assistants/ChatCompletionsJsonConverter.cs
+++ b/src/WebJobs.Extensions.OpenAI/Assistants/ChatCompletionsJsonConverter.cs
@@ -4,19 +4,19 @@
using System.ClientModel.Primitives;
using System.Text.Json;
using System.Text.Json.Serialization;
-using Azure.AI.OpenAI;
+using OpenAI.Chat;
namespace Microsoft.Azure.WebJobs.Extensions.OpenAI.Assistants;
-class ChatCompletionsJsonConverter : JsonConverter
+class ChatCompletionsJsonConverter : JsonConverter
{
static readonly ModelReaderWriterOptions modelReaderWriterOptions = new("J");
- public override ChatCompletions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ public override ChatCompletion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
- public override void Write(Utf8JsonWriter writer, ChatCompletions value, JsonSerializerOptions options)
+ public override void Write(Utf8JsonWriter writer, ChatCompletion value, JsonSerializerOptions options)
{
- ((IJsonModel)value).Write(writer, modelReaderWriterOptions);
+ ((IJsonModel)value).Write(writer, modelReaderWriterOptions);
}
}
diff --git a/src/WebJobs.Extensions.OpenAI/Assistants/IAssistantSkillInvoker.cs b/src/WebJobs.Extensions.OpenAI/Assistants/IAssistantSkillInvoker.cs
index d3e539ba..c11f55bd 100644
--- a/src/WebJobs.Extensions.OpenAI/Assistants/IAssistantSkillInvoker.cs
+++ b/src/WebJobs.Extensions.OpenAI/Assistants/IAssistantSkillInvoker.cs
@@ -4,28 +4,28 @@
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Text;
-using Azure.AI.OpenAI;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
+using OpenAI.Chat;
namespace Microsoft.Azure.WebJobs.Extensions.OpenAI.Assistants;
public interface IAssistantSkillInvoker
{
- IList? GetFunctionsDefinitions();
- Task InvokeAsync(ChatCompletionsFunctionToolCall call, CancellationToken cancellationToken);
+ IList? GetFunctionsDefinitions();
+ Task InvokeAsync(ChatToolCall call, CancellationToken cancellationToken);
}
class SkillInvocationContext
{
- public SkillInvocationContext(string arguments)
+ public SkillInvocationContext(BinaryData arguments)
{
this.Arguments = arguments;
}
// The arguments are passed as a JSON object in the form of {"paramName":paramValue}
- public string Arguments { get; }
+ public BinaryData Arguments { get; }
// The result of the function invocation, if any
public object? Result { get; set; }
@@ -70,14 +70,14 @@ internal void UnregisterSkill(string name)
this.skills.Remove(name);
}
- IList? IAssistantSkillInvoker.GetFunctionsDefinitions()
+ IList? IAssistantSkillInvoker.GetFunctionsDefinitions()
{
if (this.skills.Count == 0)
{
return null;
}
- List functions = new(capacity: this.skills.Count);
+ List functions = new(capacity: this.skills.Count);
foreach (Skill skill in this.skills.Values)
{
// The parameters can be defined in the attribute JSON or can be inferred from
@@ -85,12 +85,12 @@ internal void UnregisterSkill(string name)
string parametersJson = skill.Attribute.ParameterDescriptionJson ??
JsonConvert.SerializeObject(GetParameterDefinition(skill));
- functions.Add(new ChatCompletionsFunctionToolDefinition
- {
- Name = skill.Name,
- Description = skill.Attribute.FunctionDescription,
- Parameters = BinaryData.FromBytes(Encoding.UTF8.GetBytes(parametersJson)),
- });
+ ChatTool chatTool = ChatTool.CreateFunctionTool(
+ skill.Name,
+ skill.Attribute.FunctionDescription,
+ BinaryData.FromBytes(Encoding.UTF8.GetBytes(parametersJson))
+ );
+ functions.Add(chatTool);
}
return functions;
@@ -140,7 +140,7 @@ static Dictionary GetParameterDefinition(Skill skill)
}
async Task IAssistantSkillInvoker.InvokeAsync(
- ChatCompletionsFunctionToolCall call,
+ ChatToolCall call,
CancellationToken cancellationToken)
{
if (call is null)
@@ -148,17 +148,17 @@ static Dictionary GetParameterDefinition(Skill skill)
throw new ArgumentNullException(nameof(call));
}
- if (call.Name is null)
+ if (call.FunctionName is null)
{
throw new ArgumentException("The function call must have a name", nameof(call));
}
- if (!this.skills.TryGetValue(call.Name, out Skill? skill))
+ if (!this.skills.TryGetValue(call.FunctionName, out Skill? skill))
{
- throw new InvalidOperationException($"No skill registered with name '{call.Name}'");
+ throw new InvalidOperationException($"No skill registered with name '{call.FunctionName}'");
}
- SkillInvocationContext skillInvocationContext = new(call.Arguments);
+ SkillInvocationContext skillInvocationContext = new(call.FunctionArguments);
// This call may throw if the Functions host is shutting down or if there is an internal error
// in the Functions runtime. We don't currently try to handle these exceptions.
@@ -170,7 +170,7 @@ static Dictionary GetParameterDefinition(Skill skill)
InvokeHandler = async userCodeInvoker =>
{
// Invoke the function and attempt to get the result.
- this.logger.LogInformation("Invoking user-code function '{Name}'", call.Name);
+ this.logger.LogInformation("Invoking user-code function '{Name}'", call.FunctionName);
Task invokeTask = userCodeInvoker.Invoke();
if (invokeTask is Task