Skip to content

Commit 86afd3e

Browse files
authored
Merge pull request #63 from petehauge/Updating-Local-File-Search
Updating the Local File Search sample
2 parents a7ba758 + 7749f59 commit 86afd3e

File tree

4 files changed

+397
-33
lines changed

4 files changed

+397
-33
lines changed
Lines changed: 96 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,31 @@ In this example we will create the local file, upload it to the newly created `V
44

55
1. First we need to create agent client and read the environment variables that will be used in the next steps.
66
```C# Snippet:AgentsFileSearch_CreateClient
7-
// Get Connection information from Environment Variables
8-
// To use App Config instead: https://learn.microsoft.com/en-us/visualstudio/ide/managing-application-settings-dotnet
9-
var projectEndpoint = System.Environment.GetEnvironmentVariable("PROJECT_ENDPOINT");
10-
var modelDeploymentName = System.Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME");
7+
// Get Connection information from app configuration
8+
var projectEndpoint = configuration["ProjectEndpoint"];
9+
var modelDeploymentName = configuration["ModelDeploymentName"];
1110

1211
// Create the Agent Client
13-
PersistentAgentsClient client = new(projectEndpoint, new DefaultAzureCredential());
12+
PersistentAgentsClient agentClient = new(
13+
projectEndpoint,
14+
new DefaultAzureCredential(),
15+
new PersistentAgentsAdministrationClientOptions(
16+
PersistentAgentsAdministrationClientOptions.ServiceVersion.V2025_05_01
17+
));
1418
```
1519

1620
2. Now we will create a local file and upload it to the data store.
1721

1822
Synchronous sample:
1923
```C# Snippet:AgentsFileSearch_UploadAgentFiles
20-
// Upload a file and wait for it to be processed
2124
// Create a local sample file
2225
System.IO.File.WriteAllText(
2326
path: "sample_file_for_upload.txt",
2427
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457."
2528
);
2629

2730
// Upload local sample file to the agent
28-
PersistentAgentFile uploadedAgentFile = agentClient.UploadFile(
31+
PersistentAgentFileInfo uploadedAgentFile = agentClient.Files.UploadFile(
2932
filePath: "sample_file_for_upload.txt",
3033
purpose: PersistentAgentFilePurpose.Agents
3134
);
@@ -46,7 +49,7 @@ await System.IO.File.WriteAllTextAsync(
4649
);
4750

4851
// Upload local sample file to the agent
49-
PersistentAgentFile uploadedAgentFile = await agentClient.UploadFileAsync(
52+
PersistentAgentFileInfo uploadedAgentFile = await agentClient.Files.UploadFileAsync(
5053
filePath: "sample_file_for_upload.txt",
5154
purpose: PersistentAgentFilePurpose.Agents
5255
);
@@ -65,7 +68,7 @@ Synchronous sample:
6568
// Create a vector store with the file and wait for it to be processed.
6669
// If you do not specify a vector store, CreateMessage will create a vector
6770
// store with a default expiration policy of seven days after they were last active
68-
VectorStore vectorStore = agentClient.CreateVectorStore(
71+
VectorStore vectorStore = agentClient.VectorStores.CreateVectorStore(
6972
fileIds: new List<string> { uploadedAgentFile.Id },
7073
name: "my_vector_store");
7174

@@ -76,7 +79,7 @@ Asynchronous sample:
7679
// Create a vector store with the file and wait for it to be processed.
7780
// If you do not specify a vector store, CreateMessage will create a vector
7881
// store with a default expiration policy of seven days after they were last active
79-
VectorStore vectorStore = await agentClient.CreateVectorStoreAsync(
82+
VectorStore vectorStore = await agentClient.VectorStores.CreateVectorStoreAsync(
8083
fileIds: new List<string> { uploadedAgentFile.Id },
8184
name: "my_vector_store");
8285

@@ -91,7 +94,7 @@ FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
9194
fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
9295

9396
// Create an agent with Tools and Tool Resources
94-
PersistentAgent agent = agentClient.CreateAgent(
97+
PersistentAgent agent = agentClient.Administration.CreateAgent(
9598
model: modelDeploymentName,
9699
name: "SDK Test Agent - Retrieval",
97100
instructions: "You are a helpful agent that can help fetch data from files you know about.",
@@ -106,7 +109,7 @@ FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
106109
fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
107110

108111
// Create an agent with Tools and Tool Resources
109-
PersistentAgent agent = await agentClient.CreateAgentAsync(
112+
PersistentAgent agent = await agentClient.Administration.CreateAgentAsync(
110113
model: modelDeploymentName,
111114
name: "SDK Test Agent - Retrieval",
112115
instructions: "You are a helpful agent that can help fetch data from files you know about.",
@@ -119,21 +122,21 @@ PersistentAgent agent = await agentClient.CreateAgentAsync(
119122
Synchronous sample:
120123
```C# Snippet:AgentsFileSearch_CreateThreadAndRun
121124
// Create the agent thread for communication
122-
PersistentAgentThread thread = agentClient.CreateThread();
125+
PersistentAgentThread thread = agentClient.Threads.CreateThread();
123126

124127
// Create message and run the agent
125-
ThreadMessage messageResponse = agentClient.CreateMessage(
128+
ThreadMessage messageResponse = agentClient.Messages.CreateMessage(
126129
thread.Id,
127130
MessageRole.User,
128131
"Can you give me the documented codes for 'banana' and 'orange'?");
129132

130-
ThreadRun run = agentClient.CreateRun(thread, agent);
133+
ThreadRun run = agentClient.Runs.CreateRun(thread, agent);
131134

132135
// Wait for the agent to finish running
133136
do
134137
{
135138
Thread.Sleep(TimeSpan.FromMilliseconds(500));
136-
run = agentClient.GetRun(thread.Id, run.Id);
139+
run = agentClient.Runs.GetRun(thread.Id, run.Id);
137140
}
138141
while (run.Status == RunStatus.Queued
139142
|| run.Status == RunStatus.InProgress);
@@ -149,21 +152,21 @@ if (run.Status != RunStatus.Completed)
149152
Asynchronous sample:
150153
```C# Snippet:AgentsFileSearchAsync_CreateThreadAndRun
151154
// Create the agent thread for communication
152-
PersistentAgentThread thread = await agentClient.CreateThreadAsync();
155+
PersistentAgentThread thread = await agentClient.Threads.CreateThreadAsync();
153156

154157
// Create message and run the agent
155-
ThreadMessage messageResponse = await agentClient.CreateMessageAsync(
158+
ThreadMessage messageResponse = await agentClient.Messages.CreateMessageAsync(
156159
thread.Id,
157160
MessageRole.User,
158161
"Can you give me the documented codes for 'banana' and 'orange'?");
159162

160-
ThreadRun run = await agentClient.CreateRunAsync(thread, agent);
163+
ThreadRun run = await agentClient.Runs.CreateRunAsync(thread, agent);
161164

162165
// Wait for the agent to finish running
163166
do
164167
{
165168
await Task.Delay(TimeSpan.FromMilliseconds(500));
166-
run = await agentClient.GetRunAsync(thread.Id, run.Id);
169+
run = await agentClient.Runs.GetRunAsync(thread.Id, run.Id);
167170
}
168171
while (run.Status == RunStatus.Queued
169172
|| run.Status == RunStatus.InProgress);
@@ -177,10 +180,11 @@ if (run.Status != RunStatus.Completed)
177180
```
178181

179182
6. Print the agent messages to console in chronological order (including formatting file citations).
180-
```C# Snippet:AgentsFileSearch_Print
181183

184+
Synchronous sample:
185+
```C# Snippet:AgentsFileSearch_Print
182186
// Retrieve all messages from the agent client
183-
PageableList<ThreadMessage> messages = agentClient.GetMessages(
187+
Pageable<ThreadMessage> messages = agentClient.Messages.GetMessages(
184188
threadId: thread.Id,
185189
order: ListSortOrder.Ascending
186190
);
@@ -233,21 +237,83 @@ foreach (ThreadMessage threadMessage in messages)
233237
Console.WriteLine();
234238
}
235239
}
240+
241+
```
242+
243+
Asynchronous sample:
244+
```C# Snippet:AgentsFileSearchAsync_Print
245+
// Retrieve all messages from the agent client
246+
AsyncPageable<ThreadMessage> messages = agentClient.Messages.GetMessagesAsync(
247+
threadId: thread.Id,
248+
order: ListSortOrder.Ascending
249+
);
250+
251+
// Helper method for replacing references
252+
static string replaceReferences(Dictionary<string, string> fileIds, string fileID, string placeholder, string text)
253+
{
254+
if (fileIds.TryGetValue(fileID, out string replacement))
255+
return text.Replace(placeholder, $" [{replacement}]");
256+
else
257+
return text.Replace(placeholder, $" [{fileID}]");
258+
}
259+
260+
// Process messages in order
261+
await foreach (ThreadMessage threadMessage in messages)
262+
{
263+
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
264+
265+
foreach (MessageContent contentItem in threadMessage.ContentItems)
266+
{
267+
if (contentItem is MessageTextContent textItem)
268+
{
269+
if (threadMessage.Role == MessageRole.Agent && textItem.Annotations.Count > 0)
270+
{
271+
string strMessage = textItem.Text;
272+
273+
// If we file path or file citation annotations - rewrite the 'source' FileId with the file name
274+
foreach (MessageTextAnnotation annotation in textItem.Annotations)
275+
{
276+
if (annotation is MessageTextFilePathAnnotation pathAnnotation)
277+
{
278+
strMessage = replaceReferences(fileIds, pathAnnotation.FileId, pathAnnotation.Text, strMessage);
279+
}
280+
else if (annotation is MessageTextFileCitationAnnotation citationAnnotation)
281+
{
282+
strMessage = replaceReferences(fileIds, citationAnnotation.FileId, citationAnnotation.Text, strMessage);
283+
}
284+
}
285+
Console.Write(strMessage);
286+
}
287+
else
288+
{
289+
Console.Write(textItem.Text);
290+
}
291+
}
292+
else if (contentItem is MessageImageFileContent imageFileItem)
293+
{
294+
Console.Write($"<image from ID: {imageFileItem.FileId}");
295+
}
296+
Console.WriteLine();
297+
}
298+
}
236299
```
300+
237301
7. Finally, we delete all the resources created in this sample.
238302

239303
Synchronous sample:
240304
```C# Snippet:AgentsFileSearch_Cleanup
241-
client.DeleteVectorStore(vectorStore.Id);
242-
client.DeleteFile(uploadedAgentFile.Id);
243-
client.DeleteThread(thread.Id);
244-
client.DeleteAgent(agent.Id);
305+
// Clean up resources
306+
agentClient.VectorStores.DeleteVectorStore(vectorStore.Id);
307+
agentClient.Files.DeleteFile(uploadedAgentFile.Id);
308+
agentClient.Threads.DeleteThread(thread.Id);
309+
agentClient.Administration.DeleteAgent(agent.Id);
245310
```
246311

247312
Asynchronous sample:
248313
```C# Snippet:AgentsFileSearchAsync_Cleanup
249-
await client.DeleteVectorStoreAsync(vectorStore.Id);
250-
await client.DeleteFileAsync(uploadedAgentFile.Id);
251-
await client.DeleteThreadAsync(thread.Id);
252-
await client.DeleteAgentAsync(agent.Id);
314+
// Clean up resources
315+
await agentClient.VectorStores.DeleteVectorStoreAsync(vectorStore.Id);
316+
await agentClient.Files.DeleteFileAsync(uploadedAgentFile.Id);
317+
await agentClient.Threads.DeleteThreadAsync(thread.Id);
318+
await agentClient.Administration.DeleteAgentAsync(agent.Id);
253319
```
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using Azure;
2+
using Azure.AI.Agents.Persistent;
3+
using Azure.Identity;
4+
using Microsoft.Extensions.Configuration;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Threading.Tasks;
8+
9+
// Get Connection information from app configuration
10+
IConfigurationRoot configuration = new ConfigurationBuilder()
11+
.SetBasePath(AppContext.BaseDirectory)
12+
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
13+
.Build();
14+
15+
var projectEndpoint = configuration["ProjectEndpoint"];
16+
var modelDeploymentName = configuration["ModelDeploymentName"];
17+
18+
// Create the Agent Client
19+
PersistentAgentsClient agentClient = new(
20+
projectEndpoint,
21+
new DefaultAzureCredential(),
22+
new PersistentAgentsAdministrationClientOptions(
23+
PersistentAgentsAdministrationClientOptions.ServiceVersion.V2025_05_01
24+
));
25+
26+
// Create a local sample file
27+
await System.IO.File.WriteAllTextAsync(
28+
path: "sample_file_for_upload.txt",
29+
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457."
30+
);
31+
32+
// Upload local sample file to the agent
33+
PersistentAgentFileInfo uploadedAgentFile = await agentClient.Files.UploadFileAsync(
34+
filePath: "sample_file_for_upload.txt",
35+
purpose: PersistentAgentFilePurpose.Agents
36+
);
37+
38+
// Setup dictionary with list of File IDs for the vector store
39+
Dictionary<string, string> fileIds = new()
40+
{
41+
{ uploadedAgentFile.Id, uploadedAgentFile.Filename }
42+
};
43+
44+
// Create a vector store with the file and wait for it to be processed.
45+
// If you do not specify a vector store, CreateMessage will create a vector
46+
// store with a default expiration policy of seven days after they were last active
47+
VectorStore vectorStore = await agentClient.VectorStores.CreateVectorStoreAsync(
48+
fileIds: new List<string> { uploadedAgentFile.Id },
49+
name: "my_vector_store");
50+
51+
// Create tool definition for File Search
52+
FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
53+
fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
54+
55+
// Create an agent with Tools and Tool Resources
56+
PersistentAgent agent = await agentClient.Administration.CreateAgentAsync(
57+
model: modelDeploymentName,
58+
name: "SDK Test Agent - Retrieval",
59+
instructions: "You are a helpful agent that can help fetch data from files you know about.",
60+
tools: new List<ToolDefinition> { new FileSearchToolDefinition() },
61+
toolResources: new ToolResources() { FileSearch = fileSearchToolResource });
62+
63+
64+
// Create the agent thread for communication
65+
PersistentAgentThread thread = await agentClient.Threads.CreateThreadAsync();
66+
67+
// Create message and run the agent
68+
ThreadMessage messageResponse = await agentClient.Messages.CreateMessageAsync(
69+
thread.Id,
70+
MessageRole.User,
71+
"Can you give me the documented codes for 'banana' and 'orange'?");
72+
73+
ThreadRun run = await agentClient.Runs.CreateRunAsync(thread, agent);
74+
75+
// Wait for the agent to finish running
76+
do
77+
{
78+
await Task.Delay(TimeSpan.FromMilliseconds(500));
79+
run = await agentClient.Runs.GetRunAsync(thread.Id, run.Id);
80+
}
81+
while (run.Status == RunStatus.Queued
82+
|| run.Status == RunStatus.InProgress);
83+
84+
// Confirm that the run completed successfully
85+
if (run.Status != RunStatus.Completed)
86+
{
87+
throw new Exception("Run did not complete successfully, error: " + run.LastError?.Message);
88+
}
89+
90+
// Retrieve all messages from the agent client
91+
AsyncPageable<ThreadMessage> messages = agentClient.Messages.GetMessagesAsync(
92+
threadId: thread.Id,
93+
order: ListSortOrder.Ascending
94+
);
95+
96+
// Helper method for replacing references
97+
static string replaceReferences(Dictionary<string, string> fileIds, string fileID, string placeholder, string text)
98+
{
99+
if (fileIds.TryGetValue(fileID, out string replacement))
100+
return text.Replace(placeholder, $" [{replacement}]");
101+
else
102+
return text.Replace(placeholder, $" [{fileID}]");
103+
}
104+
105+
// Process messages in order
106+
await foreach (ThreadMessage threadMessage in messages)
107+
{
108+
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
109+
110+
foreach (MessageContent contentItem in threadMessage.ContentItems)
111+
{
112+
if (contentItem is MessageTextContent textItem)
113+
{
114+
if (threadMessage.Role == MessageRole.Agent && textItem.Annotations.Count > 0)
115+
{
116+
string strMessage = textItem.Text;
117+
118+
// If we file path or file citation annotations - rewrite the 'source' FileId with the file name
119+
foreach (MessageTextAnnotation annotation in textItem.Annotations)
120+
{
121+
if (annotation is MessageTextFilePathAnnotation pathAnnotation)
122+
{
123+
strMessage = replaceReferences(fileIds, pathAnnotation.FileId, pathAnnotation.Text, strMessage);
124+
}
125+
else if (annotation is MessageTextFileCitationAnnotation citationAnnotation)
126+
{
127+
strMessage = replaceReferences(fileIds, citationAnnotation.FileId, citationAnnotation.Text, strMessage);
128+
}
129+
}
130+
Console.Write(strMessage);
131+
}
132+
else
133+
{
134+
Console.Write(textItem.Text);
135+
}
136+
}
137+
else if (contentItem is MessageImageFileContent imageFileItem)
138+
{
139+
Console.Write($"<image from ID: {imageFileItem.FileId}");
140+
}
141+
Console.WriteLine();
142+
}
143+
}
144+
145+
// Clean up resources
146+
await agentClient.VectorStores.DeleteVectorStoreAsync(vectorStore.Id);
147+
await agentClient.Files.DeleteFileAsync(uploadedAgentFile.Id);
148+
await agentClient.Threads.DeleteThreadAsync(thread.Id);
149+
await agentClient.Administration.DeleteAgentAsync(agent.Id);

0 commit comments

Comments
 (0)