Skip to content

Commit 5541af4

Browse files
committed
add text excerpts
1 parent 5d45ace commit 5541af4

File tree

7 files changed

+124
-29
lines changed

7 files changed

+124
-29
lines changed
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using KnowledgeMining.Application.Common.Models;
2+
using System;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Text;
@@ -8,6 +9,6 @@ namespace KnowledgeMining.Application.Common.Interfaces
89
{
910
public interface IChatService
1011
{
11-
Task<string> AskQuestionAboutDocument(string question, string content, string documentId = "", CancellationToken ct = default);
12+
Task<ChatAnswer> AskQuestionAboutDocument(string question, string content, string documentId = "", CancellationToken ct = default);
1213
}
1314
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Text.Json.Serialization;
6+
using System.Threading.Tasks;
7+
8+
namespace KnowledgeMining.Application.Common.Models
9+
{
10+
public class ChatAnswer
11+
{
12+
[JsonPropertyName("answer")]
13+
public string Answer { get; set; } = string.Empty;
14+
[JsonPropertyName("excerpts")]
15+
public Excerpt[] Excerpts { get; set; } = [];
16+
[JsonPropertyName("thoughts")]
17+
public string Thoughts { get; set; } = string.Empty;
18+
}
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace KnowledgeMining.Application.Common.Models
4+
{
5+
public class Excerpt
6+
{
7+
[JsonPropertyName("source")]
8+
public string Source { get; set; } = string.Empty;
9+
[JsonPropertyName("shortText")]
10+
public string ShortText { get; set; } = string.Empty;
11+
}
12+
}

app/src/KnowledgeMining.Infrastructure/Services/OpenAI/OpenAIService.cs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Azure.AI.OpenAI;
22
using KnowledgeMining.Application.Common.Interfaces;
3+
using KnowledgeMining.Application.Common.Models;
34
using Microsoft.Extensions.Options;
5+
using Microsoft.Extensions.Primitives;
46
using System;
57
using System.Collections.Generic;
68
using System.Linq;
@@ -17,9 +19,9 @@ public class OpenAIService(OpenAIClient client, IChunkSearchService searchServic
1719
private readonly IChunkSearchService _searchService = searchService;
1820
private IList<ChatRequestMessage> _messages = new List<ChatRequestMessage>();
1921

20-
public async Task<string> AskQuestionAboutDocument(string question, string content, string documentId = "", CancellationToken ct = default)
22+
public async Task<ChatAnswer> AskQuestionAboutDocument(string question, string content, string documentId = "", CancellationToken ct = default)
2123
{
22-
var questionEmbedding = await _openAIClient.GetEmbeddingsAsync(new EmbeddingsOptions(_options.EmbeddingDeployment, [question]));
24+
var questionEmbedding = await _openAIClient.GetEmbeddingsAsync(new EmbeddingsOptions(_options.EmbeddingDeployment, [question]), ct);
2325

2426
var chunks = await _searchService.QueryDocumentChuncksAsync(questionEmbedding.Value.Data[0].Embedding.ToArray(), documentId, ct);
2527

@@ -40,37 +42,51 @@ public async Task<string> AskQuestionAboutDocument(string question, string conte
4042
}
4143
};
4244

43-
var answer = await _openAIClient.GetChatCompletionsAsync(chatCompletions);
45+
var answer = await _openAIClient.GetChatCompletionsAsync(chatCompletions, ct);
4446

4547
var answerJson = answer.Value.Choices.FirstOrDefault()?.Message.Content ?? throw new InvalidOperationException("Failed to get search query");
4648

47-
var answerObject = JsonSerializer.Deserialize<JsonElement>(answerJson);
48-
49-
var ans = answerObject.GetProperty("answer").GetString() ?? throw new InvalidOperationException("Failed to get answer");
50-
var thoughts = answerObject.GetProperty("thoughts").GetString() ?? throw new InvalidOperationException("Failed to get thoughts");
51-
52-
return ans;
49+
try
50+
{
51+
return JsonSerializer.Deserialize<ChatAnswer>(answerJson) ?? new ChatAnswer() {
52+
Answer = "Failed to get answer",
53+
};
54+
}
55+
catch (JsonException)
56+
{
57+
return new ChatAnswer() {
58+
Answer = "Failed to get answer",
59+
};
60+
}
5361
}
5462

55-
private string CreateSystemPromptFromChunks(IEnumerable<string> chunks)
63+
private string CreateSystemPromptFromChunks(IEnumerable<string> excerpts)
5664
{
65+
var answerTemplate = @"
66+
{
67+
""answer"": ""the answer to the question. If no excerpt available, put the answer as I don't know."",
68+
""excerpts"": [
69+
{""source"": ""source excerpt e.g. [reference 1]"", ""shortText"": ""excerpt text up to 200 characters""},
70+
{""source"": ""source excerpt e.g. [reference 2]"", ""shortText"": ""excerpt text up to 200 characters""}
71+
],
72+
""thoughts"": ""brief thoughts on how you came up with the answer, e.g. what excerpts you used, what you thought about, etc.""
73+
}
74+
";
75+
5776
var sb = new StringBuilder();
58-
sb.AppendLine("## Sources ##");
77+
sb.AppendLine("## Document Excerpts ##");
5978

60-
for (int i = 0; i < chunks.Count(); i++)
79+
for (int i = 0; i < excerpts.Count(); i++)
6180
{
62-
sb.AppendLine($"### Source {i + 1} ###");
63-
sb.AppendLine(chunks.ElementAt(i));
64-
sb.AppendLine($"### End Source {i + 1} ###");
81+
sb.AppendLine($"### Excerpt {i + 1} ###");
82+
sb.AppendLine(excerpts.ElementAt(i));
83+
sb.AppendLine($"### End Excerpt {i + 1} ###");
6584
}
6685

67-
sb.AppendLine("## End Sources ##");
86+
sb.AppendLine("## End Document Excerpts ##");
6887
sb.AppendLine();
6988
sb.AppendLine("You answer needs to be a valid json object with the following format.");
70-
sb.AppendLine("{");
71-
sb.AppendLine(" \"answer\": \"the answer to the question, add a source reference to the end of each sentence. e.g. Apple is a fruit [reference1.pdf][reference2.pdf]. If no source available, put the answer as I don't know.\",");
72-
sb.AppendLine(" \"thoughts\": \"brief thoughts on how you came up with the answer, e.g. what sources you used, what you thought about, etc.\"");
73-
sb.AppendLine("}");
89+
sb.Append(answerTemplate);
7490

7591
return sb.ToString();
7692
}

app/src/KnowledgeMining.UI/Pages/Search/Components/DocumentChatComponent.razor

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
@using KnowledgeMining.Application.Common.Interfaces
2-
@inject IChatService ChatService
1+
@inject IChatService ChatService
32

43
<MudGrid Justify="Justify.Center">
54
<MudItem xs="12">
@@ -22,8 +21,26 @@
2221
else
2322
{
2423
<MudPaper Class="pa-6" Outlined="true" MaxWidth="80%" Style="background-color: aquamarine">
25-
<MudText Typo="Typo.body1" Align="Align.End">
26-
@turn.Answer
24+
<MudText Typo="Typo.body1" Align="Align.Left">
25+
@turn.Answer.Answer
26+
</MudText>
27+
@if (turn.Answer.Excerpts.Any())
28+
{
29+
<MudText Typo="Typo.body2" Align="Align.Left" Class="pt-3">
30+
<strong>Excerpts:</strong>
31+
</MudText>
32+
<div>
33+
@foreach (var excerpt in turn.Answer.Excerpts)
34+
{
35+
<DocumentExcerptChip Excerpt="@excerpt" />
36+
}
37+
</div>
38+
}
39+
<MudText Typo="Typo.body2" Align="Align.Left" Class="pt-3">
40+
<strong>Thought Process:</strong>
41+
</MudText>
42+
<MudText Typo="Typo.body2" Align="Align.Left">
43+
@turn.Answer.Thoughts
2744
</MudText>
2845
</MudPaper>
2946
}
@@ -92,7 +109,10 @@
92109
}
93110
catch (Exception ex)
94111
{
95-
turn.Answer = $"Failed to send message. Reason: {ex.Message}";
112+
turn.Answer = new ChatAnswer()
113+
{
114+
Answer = $"Failed to send message. Reason: {ex.Message}"
115+
};
96116
}
97117
finally
98118
{
@@ -103,7 +123,7 @@
103123
private class ChatTurn
104124
{
105125
public string Question { get; set; } = string.Empty;
106-
public string? Answer { get; set; }
126+
public ChatAnswer? Answer { get; set; }
107127
public DateTime AskedOn { get; set; } = DateTime.Now;
108128
}
109129
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<MudChip Color="Color.Secondary" OnClick="@ToggleOpen" @onblur="((e) => ToggleOpen())">
2+
@Excerpt.Source
3+
<MudIcon Icon="@Icons.Material.Filled.Info" Class="ml-2"></MudIcon>
4+
<MudPopover Open="@_isOpen" OverflowBehavior="OverflowBehavior.FlipNever" Class="p-2" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.BottomLeft">
5+
<div class="d-flex flex-row align-items-center justify-content-center">
6+
<MudText Align="Align.Center">@Excerpt.ShortText</MudText>
7+
<MudIconButton OnClick="@ToggleOpen" Color="Color.Error" Icon="@Icons.Material.Filled.Close"></MudIconButton>
8+
</div>
9+
</MudPopover>
10+
</MudChip>
11+
12+
@code {
13+
[Parameter]
14+
public Excerpt Excerpt { get; set; }
15+
16+
public bool _isOpen;
17+
18+
public void ToggleOpen()
19+
{
20+
if (_isOpen)
21+
_isOpen = false;
22+
else
23+
_isOpen = true;
24+
}
25+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
@using KnowledgeMining.UI.Services.Links
2-
@using KnowledgeMining.Domain.Entities
2+
@using KnowledgeMining.Domain.Entities
3+
@using KnowledgeMining.Application.Common.Models
4+
@using KnowledgeMining.Application.Common.Interfaces

0 commit comments

Comments
 (0)