-
Notifications
You must be signed in to change notification settings - Fork 112
Expand file tree
/
Copy pathKernelMemory.cs
More file actions
265 lines (227 loc) · 10.9 KB
/
KernelMemory.cs
File metadata and controls
265 lines (227 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
using Microsoft.GS.DPSHost.AppConfiguration;
using Microsoft.Extensions.Options;
using Microsoft.KernelMemory;
using Microsoft.Net.Http.Headers;
using System.Text.Json;
using System.Text;
using Microsoft.KernelMemory.Context;
using Microsoft.GS.DPS.Model.KernelMemory;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.GS.DPS.Storage.Document;
using HeyRed.Mime;
namespace Microsoft.GS.DPSHost.API
{
//Define File Upload and Ask API
public class KernelMemory
{
public static void AddAPIs(WebApplication app)
{
//Registration the files
app.MapPost("/Documents/ImportDocument", async (IFormFile file,
DPS.API.KernelMemory kernelMemory
) =>
{
try
{
var fileStream = file.OpenReadStream();
//Set Stream Position to 0
fileStream.Seek(0, SeekOrigin.Begin);
// Verify and set ContentType if empty
var contentType = file.ContentType;
var fileExtension = Path.GetExtension(file.FileName);
if (string.IsNullOrEmpty(contentType))
{
contentType = MimeTypesMap.GetMimeType(fileExtension);
}
//Check supported file types
var allowedExtensions = new string[] { ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".tif", ".tiff", ".jpg", ".jpeg", ".png", ".bmp", ".txt" };
if (!allowedExtensions.Contains(fileExtension))
{
return Results.BadRequest(new DocumentImportedResult() { DocumentId = string.Empty,
MimeType = contentType,
Summary = $"{fileExtension} file is Unsupported file type" });
}
// Checking File Size: O byte/kb file not allowed
if (file == null || file.Length == 0)
{
return Results.BadRequest(new DocumentImportedResult()
{
DocumentId = string.Empty,
MimeType = contentType,
Summary = "The file is empty and cannot be uploaded. Please select a valid file."
});
}
var result = await kernelMemory.ImportDocument(fileStream, file.FileName, contentType);
//Return HTTP 202 with Location Header
//return Results($"/Documents/CheckProcessStatus/{result.DocumentId}", result);
// Add Document to the Repository
//Refresh the Cache
return Results.Ok<DocumentImportedResult>(result);
}
catch (IOException ex)
{
// Log the exception
app.Logger.LogError(ex, "An error occurred while uploading the document.");
throw;
}
catch (Exception ex)
{
// Log the exception
app.Logger.LogError(ex, "An unexpected error occurred.");
throw;
}
})
.DisableAntiforgery();
app.MapDelete("/Documents/{documentId}", async (string documentId,
DPS.API.KernelMemory kernelMemory) =>
{
try
{
await kernelMemory.DeleteDocument(documentId);
return Results.Ok(new DocumentDeletedResult() { IsDeleted = true });
}
#pragma warning disable CA1031 // Must catch all to log and keep the process alive
catch (Exception ex)
{
app.Logger.LogError(ex, "An error occurred while deleting a document.");
return Results.BadRequest(new DocumentDeletedResult() { IsDeleted = false });
}
#pragma warning restore CA1031
})
.DisableAntiforgery();
app.MapGet("/Documents/{documentId}/CheckReadyStatus", async (string documentId,
MemoryWebClient kmClient) =>
{
var result = await kmClient.IsDocumentReadyAsync(documentId);
return Results.Ok(new DocumentReadyStatusResult() { IsReady = result });
})
.DisableAntiforgery();
app.MapGet("/Documents/{documentId}/CheckProcessStatus/", async (string documentId,
MemoryWebClient kmClient) =>
{
var status = await kmClient.GetDocumentStatusAsync(documentId);
if (status == null)
{
return Results.NotFound();
}
return Results.Ok(status);
})
.DisableAntiforgery();
app.MapPost("/Documents/ImportText", async (string text,
MemoryWebClient kmClient) =>
{
try
{
var documentId = await kmClient.ImportTextAsync(text);
return Results.Ok(new DocumentImportedResult() { DocumentId = documentId });
}
catch (IOException ex)
{
// Log the exception
app.Logger.LogError(ex, "An error occurred while uploading the document.");
throw;
}
catch (Exception ex)
{
// Log the exception
app.Logger.LogError(ex, "An unexpected error occurred.");
throw;
}
})
.DisableAntiforgery();
app.MapPost("/Documents/ImportWebPage", async (string url,
MemoryWebClient kmClient) =>
{
try
{
// Implementation of the file upload
var documentId = await kmClient.ImportWebPageAsync(url);
return Results.Ok(new DocumentImportedResult() { DocumentId = documentId });
}
catch (IOException ex)
{
// Log the exception
app.Logger.LogError(ex, "An error occurred while uploading the document.");
throw;
}
catch (Exception ex)
{
// Log the exception
app.Logger.LogError(ex, "An unexpected error occurred.");
throw;
}
})
.DisableAntiforgery();
//Check the status of File Registration Process
//TODO : Implement the SSE for the status of the document
app.MapGet("/Documents/CheckStatus/{documentId}", async Task (HttpContext ctx,
string documentId,
MemoryWebClient kmClient, CancellationToken token) =>
{
ctx.Response.Headers.Append(HeaderNames.ContentType, "text/event-stream");
//Creating While Loop with 10 mins timeout
var timeout = DateTime.UtcNow.AddMinutes(10);
var completeFlag = false;
var status = await kmClient.GetDocumentStatusAsync(documentId);
while (DateTime.UtcNow < timeout)
{
token.ThrowIfCancellationRequested();
//if status is null then return 404 with exit the loop
if (status == null)
{
ctx.Response.StatusCode = 404;
return;
}
if (status.RemainingSteps.Count == 0)
{
completeFlag = true;
break;
}
var totalSteps = status.Steps.Count;
var statusObject = new
{
progress_percentage = status.CompletedSteps.Count / totalSteps * 100,
completed = status.Completed
};
await ctx.Response.WriteAsync($"{JsonSerializer.Serialize(statusObject)}", cancellationToken: token);
await ctx.Response.Body.FlushAsync(token);
await Task.Delay(new TimeSpan(0, 0, 5));
status = await kmClient.GetDocumentStatusAsync(documentId);
}
await ctx.Response.CompleteAsync();
})
.DisableAntiforgery();
app.MapPost("/Documents/Search", async (MemoryWebClient kmClient,
SearchParameter searchParameter) =>
{
var searchResult = await kmClient.SearchAsync(query: searchParameter.query,
filter: searchParameter.MemoryFilter,
filters: searchParameter.MemoryFilters,
minRelevance: searchParameter.minRelevance,
limit: searchParameter.limit,
context: searchParameter.Context);
if (searchResult == null)
{
return Results.NoContent();
}
return Results.Ok(searchResult);
})
.DisableAntiforgery();
app.MapPost("/Documents/Ask", async (MemoryWebClient kmClient,
AskParameter askParameter) =>
{
//create Memory Filter
var memoryFilters = new List<MemoryFilter>();
askParameter.documents.ToList().ForEach(docId => memoryFilters.Add(new MemoryFilter().ByDocument(docId)));
var answer = await kmClient.AskAsync(question: askParameter.question,
filters: memoryFilters);
if (answer == null)
{
return Results.NoContent();
}
return Results.Ok(answer);
})
.DisableAntiforgery();
}
}
}