Skip to content

Commit 93e26b4

Browse files
authored
Merge branch 'main' into cosmos-state-rebased
2 parents 0fc3194 + 02814a4 commit 93e26b4

File tree

10 files changed

+881
-39
lines changed

10 files changed

+881
-39
lines changed

dotnet/samples/GettingStarted/AgentSample.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ private IChatClient GetOpenAIResponsesClient()
112112
=> new OpenAIResponseClient(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey)
113113
.AsIChatClient();
114114

115-
private IChatClient GetAzureAIAgentPersistentClient(ChatClientAgentOptions options)
116-
=> new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential())
117-
.AsIChatClient(options.Id!);
115+
private NewPersistentAgentsChatClient GetAzureAIAgentPersistentClient(ChatClientAgentOptions options)
116+
=> new(new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential()), options.Id!);
118117

119118
private NewOpenAIAssistantChatClient GetOpenAIAssistantChatClient(ChatClientAgentOptions options)
120119
=> new(new(TestConfiguration.OpenAI.ApiKey), options.Id!, null);

dotnet/samples/GettingStarted/External/Azure.AI.Agents.Persistent/NewPersistentAgentsChatClient.cs

Lines changed: 520 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System.Diagnostics;
4+
using Microsoft.Shared.Diagnostics;
5+
6+
namespace Microsoft.Extensions.AI;
7+
8+
/// <summary>
9+
/// Represents a file that is hosted by the AI service.
10+
/// </summary>
11+
/// <remarks>
12+
/// Unlike <see cref="DataContent"/> which contains the data for a file or blob, this class represents a file that is hosted
13+
/// by the AI service and referenced by an identifier. Such identifiers are specific to the provider.
14+
/// </remarks>
15+
[DebuggerDisplay("FileId = {FileId}")]
16+
public sealed class HostedFileContent : AIContent
17+
{
18+
private string _fileId;
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="HostedFileContent"/> class.
22+
/// </summary>
23+
/// <param name="fileId">The ID of the hosted file.</param>
24+
/// <exception cref="ArgumentNullException"><paramref name="fileId"/> is <see langword="null"/>.</exception>
25+
/// <exception cref="ArgumentException"><paramref name="fileId"/> is empty or composed entirely of whitespace.</exception>
26+
public HostedFileContent(string fileId)
27+
{
28+
_fileId = Throw.IfNullOrWhitespace(fileId);
29+
}
30+
31+
/// <summary>
32+
/// Gets or sets the ID of the hosted file.
33+
/// </summary>
34+
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception>
35+
/// <exception cref="ArgumentException"><paramref name="value"/> is empty or composed entirely of whitespace.</exception>
36+
public string FileId
37+
{
38+
get => _fileId;
39+
set => _fileId = Throw.IfNullOrWhitespace(value);
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System.Diagnostics;
4+
using Microsoft.Shared.Diagnostics;
5+
6+
namespace Microsoft.Extensions.AI;
7+
8+
/// <summary>
9+
/// Represents a vector store that is hosted by the AI service.
10+
/// </summary>
11+
/// <remarks>
12+
/// Unlike <see cref="DataContent"/> which contains the data for a file or blob, this class represents a vector store that is hosted
13+
/// by the AI service and referenced by an identifier. Such identifiers are specific to the provider.
14+
/// </remarks>
15+
[DebuggerDisplay("VectorStoreId = {VectorStoreId}")]
16+
public sealed class HostedVectorStoreContent : AIContent
17+
{
18+
private string? _vectorStoreId;
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="HostedVectorStoreContent"/> class.
22+
/// </summary>
23+
/// <param name="vectorStoreId">The ID of the hosted vector store.</param>
24+
/// <exception cref="ArgumentNullException"><paramref name="vectorStoreId"/> is <see langword="null"/>.</exception>
25+
/// <exception cref="ArgumentException"><paramref name="vectorStoreId"/> is empty or composed entirely of whitespace.</exception>
26+
public HostedVectorStoreContent(string vectorStoreId)
27+
{
28+
_vectorStoreId = Throw.IfNullOrWhitespace(vectorStoreId);
29+
}
30+
31+
/// <summary>
32+
/// Gets or sets the ID of the hosted vector store.
33+
/// </summary>
34+
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception>
35+
/// <exception cref="ArgumentException"><paramref name="value"/> is empty or composed entirely of whitespace.</exception>
36+
public string VectorStoreId
37+
{
38+
get => _vectorStoreId ?? string.Empty;
39+
set => _vectorStoreId = Throw.IfNullOrWhitespace(value);
40+
}
41+
}
Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,18 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3-
using Microsoft.Extensions.AI;
4-
5-
namespace OpenAI.Assistants;
3+
namespace Microsoft.Extensions.AI;
64

75
/// <summary>
86
/// Proposal for abstraction updates based on the common code interpreter tool properties.
97
/// Based on the decision, the <see cref="HostedCodeInterpreterTool"/> abstraction can be updated in M.E.AI directly.
108
/// </summary>
119
public class NewHostedCodeInterpreterTool : HostedCodeInterpreterTool
1210
{
13-
// Usage of an internal dictionary is temporary and only used here because the MEAI.Abstractions does not have this specialization yet and the
14-
// ChatClients must rely on the AdditionalProperties to check and set correctly the Code Interpreter Resource avoiding a customized RawRepresentationFactory implementation.
15-
private readonly Dictionary<string, object?> _additionalProperties = [];
16-
17-
/// <summary>Gets or sets the list of file IDs that the code interpreter tool can access.</summary>
18-
public IList<string> FileIds
19-
{
20-
get
21-
{
22-
// Only create the property in the dictionary when it is actually used
23-
if (!this._additionalProperties.TryGetValue("fileIds", out var value) || value is null)
24-
{
25-
value = new List<string>();
26-
this._additionalProperties["fileIds"] = value;
27-
}
28-
29-
return (IList<string>)value;
30-
}
31-
}
32-
33-
/// <inheritdoc/>
34-
public override IReadOnlyDictionary<string, object?> AdditionalProperties => this._additionalProperties;
11+
/// <summary>Gets or sets a collection of <see cref="AIContent"/> to be used as input to the code interpreter tool.</summary>
12+
/// <remarks>
13+
/// Services support different varied kinds of inputs. Most support the IDs of files that are hosted by the service,
14+
/// represented via <see cref="HostedFileContent"/>. Some also support binary data, represented via <see cref="DataContent"/>.
15+
/// Unsupported inputs will be ignored by the <see cref="IChatClient"/> to which the tool is passed.
16+
/// </remarks>
17+
public IList<AIContent>? Inputs { get; set; }
3518
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
namespace Microsoft.Extensions.AI;
4+
5+
/// <summary>
6+
/// Proposal for abstraction updates based on the common file search tool properties.
7+
/// This provides a standardized interface for file search functionality across providers.
8+
/// </summary>
9+
public class NewHostedFileSearchTool : AITool
10+
{
11+
/// <summary>Gets or sets a collection of <see cref="AIContent"/> to be used as input to the code interpreter tool.</summary>
12+
/// <remarks>
13+
/// Services support different varied kinds of inputs. Most support the IDs of vector stores that are hosted by the service,
14+
/// represented via <see cref="HostedVectorStoreContent"/>. Some also support binary data, represented via <see cref="DataContent"/>.
15+
/// Unsupported inputs will be ignored by the <see cref="IChatClient"/> to which the tool is passed.
16+
/// </remarks>
17+
public IList<AIContent>? Inputs { get; set; }
18+
}

dotnet/samples/GettingStarted/External/MEAI.OpenAI/NewOpenAIAssistantChatClient.cs

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,74 @@ public Task<ChatResponse> GetResponseAsync(
8080
IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default) =>
8181
GetStreamingResponseAsync(messages, options, cancellationToken).ToChatResponseAsync(cancellationToken);
8282

83+
private ToolResources? CreateToolResources(ChatOptions? options)
84+
{
85+
if (options is null)
86+
{
87+
return null;
88+
}
89+
90+
if (options.Tools is { Count: > 0 } tools)
91+
{
92+
FileSearchToolResources? fileSearchResources = null;
93+
CodeInterpreterToolResources? codeInterpreterResources = null;
94+
// The caller can provide tools in the supplied ThreadAndRunOptions. Augment it with any supplied via ChatOptions.Tools.
95+
foreach (AITool tool in tools)
96+
{
97+
switch (tool)
98+
{
99+
case NewHostedCodeInterpreterTool codeTool:
100+
101+
if (codeTool.Inputs is { Count: > 0 })
102+
{
103+
codeInterpreterResources ??= new();
104+
foreach (var input in codeTool.Inputs)
105+
{
106+
switch (input)
107+
{
108+
case HostedFileContent fileContent:
109+
// Use the file ID from the HostedFileContent.
110+
codeInterpreterResources.FileIds.Add(fileContent.FileId);
111+
break;
112+
}
113+
}
114+
}
115+
116+
break;
117+
118+
case NewHostedFileSearchTool fileSearchTool:
119+
120+
// Handle file IDs for file search tool
121+
if (fileSearchTool.Inputs is { Count: > 0 })
122+
{
123+
fileSearchResources ??= new();
124+
125+
foreach (var input in fileSearchTool.Inputs)
126+
{
127+
switch (input)
128+
{
129+
case HostedVectorStoreContent vectorStoreContent:
130+
// Use the vector store ID from the HostedVectorStoreContent.
131+
fileSearchResources.VectorStoreIds.Add(vectorStoreContent.VectorStoreId);
132+
break;
133+
}
134+
}
135+
}
136+
137+
break;
138+
}
139+
}
140+
141+
return new ToolResources
142+
{
143+
CodeInterpreter = codeInterpreterResources,
144+
FileSearch = fileSearchResources,
145+
};
146+
}
147+
148+
return null;
149+
}
150+
83151
/// <inheritdoc />
84152
public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
85153
IEnumerable<ChatMessage> messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
@@ -132,7 +200,11 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
132200
if (threadId is null)
133201
{
134202
// No thread ID was provided, so create a new thread.
135-
ThreadCreationOptions threadCreationOptions = new();
203+
ThreadCreationOptions threadCreationOptions = new()
204+
{
205+
ToolResources = CreateToolResources(options)
206+
};
207+
136208
foreach (var message in runOptions.AdditionalMessages)
137209
{
138210
threadCreationOptions.InitialMessages.Add(message);
@@ -303,18 +375,48 @@ internal static FunctionToolDefinition ToOpenAIAssistantsFunctionToolDefinition(
303375
runOptions.ToolsOverride.Add(ToOpenAIAssistantsFunctionToolDefinition(aiFunction, options));
304376
break;
305377

306-
case HostedCodeInterpreterTool:
378+
case NewHostedCodeInterpreterTool codeTool:
307379
var codeInterpreterToolDefinition = new CodeInterpreterToolDefinition();
308380
runOptions.ToolsOverride.Add(codeInterpreterToolDefinition);
309381

310-
// Once available, HostedCodeInterpreterTool.FileIds property will be used instead of the AdditionalProperties.
311-
if (tool.AdditionalProperties.TryGetValue("fileIds", out object? fileIdsObject) && fileIdsObject is IEnumerable<string> fileIds)
382+
if (codeTool.Inputs is { Count: > 0 })
312383
{
313384
var threadInitializationMessage = new ThreadInitializationMessage(OpenAI.Assistants.MessageRole.User, [OpenAI.Assistants.MessageContent.FromText("attachments")]);
314385

315-
foreach (var fileId in fileIds)
386+
foreach (var input in codeTool.Inputs)
387+
{
388+
switch (input)
389+
{
390+
case HostedFileContent fileContent:
391+
// Use the file ID from the HostedFileContent.
392+
threadInitializationMessage.Attachments.Add(new(fileContent.FileId, [codeInterpreterToolDefinition]));
393+
break;
394+
}
395+
}
396+
397+
runOptions.AdditionalMessages.Add(threadInitializationMessage);
398+
}
399+
400+
break;
401+
402+
case NewHostedFileSearchTool fileSearchTool:
403+
var fileSearchToolDefinition = new FileSearchToolDefinition();
404+
runOptions.ToolsOverride.Add(fileSearchToolDefinition);
405+
406+
// Handle file IDs for file search tool
407+
if (fileSearchTool.Inputs is { Count: > 0 })
408+
{
409+
var threadInitializationMessage = new ThreadInitializationMessage(OpenAI.Assistants.MessageRole.User, [OpenAI.Assistants.MessageContent.FromText("file search attachments")]);
410+
411+
foreach (var input in fileSearchTool.Inputs)
316412
{
317-
threadInitializationMessage.Attachments.Add(new(fileId, [codeInterpreterToolDefinition]));
413+
switch (input)
414+
{
415+
case HostedFileContent fileContent:
416+
// Use the file ID from the HostedFileContent.
417+
threadInitializationMessage.Attachments.Add(new(fileContent.FileId, [fileSearchToolDefinition]));
418+
break;
419+
}
318420
}
319421

320422
runOptions.AdditionalMessages.Add(threadInitializationMessage);
42.4 KB
Binary file not shown.

dotnet/samples/GettingStarted/Steps/Step03_ChatClientAgent_UsingCodeInterpreterTools.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
using System.Text;
44
using Azure.AI.Agents.Persistent;
55
using Azure.Identity;
6+
using Microsoft.Extensions.AI;
67
using Microsoft.Extensions.AI.Agents;
78
using Microsoft.Shared.Samples;
8-
using OpenAI.Assistants;
99
using OpenAI.Files;
1010

1111
namespace Steps;
@@ -21,8 +21,10 @@ public sealed class Step03_ChatClientAgent_UsingCodeInterpreterTools(ITestOutput
2121
[InlineData(ChatClientProviders.OpenAIAssistant)]
2222
public async Task RunningWithFileReferenceAsync(ChatClientProviders provider)
2323
{
24-
var codeInterpreterTool = new NewHostedCodeInterpreterTool();
25-
codeInterpreterTool.FileIds.Add(await UploadFileAsync("Resources/groceries.txt", provider));
24+
var codeInterpreterTool = new NewHostedCodeInterpreterTool()
25+
{
26+
Inputs = [new HostedFileContent(await UploadFileAsync("Resources/groceries.txt", provider))]
27+
};
2628

2729
var agentOptions = new ChatClientAgentOptions(
2830
name: "HelpfulAssistant",
@@ -51,9 +53,9 @@ public async Task RunningWithFileReferenceAsync(ChatClientProviders provider)
5153
assistantOutput.Append(update.Text);
5254
}
5355

54-
if (update.RawRepresentation is not null)
56+
if (update.RawRepresentation is ChatResponseUpdate chatUpdate && chatUpdate.RawRepresentation is not null)
5557
{
56-
codeInterpreterOutput.Append(GetCodeInterpreterOutput(update.RawRepresentation, provider));
58+
codeInterpreterOutput.Append(GetCodeInterpreterOutput(chatUpdate.RawRepresentation, provider));
5759
}
5860
}
5961

0 commit comments

Comments
 (0)