4
4
import com .azure .core .credential .TokenCredential ;
5
5
import com .azure .search .documents .SearchAsyncClient ;
6
6
import com .azure .search .documents .SearchDocument ;
7
+ import com .fasterxml .jackson .databind .ObjectMapper ;
7
8
import com .microsoft .openai .samples .rag .approaches .ContentSource ;
8
9
import com .microsoft .openai .samples .rag .approaches .RAGApproach ;
9
10
import com .microsoft .openai .samples .rag .approaches .RAGOptions ;
10
11
import com .microsoft .openai .samples .rag .approaches .RAGResponse ;
11
12
import com .microsoft .openai .samples .rag .ask .approaches .semantickernel .memory .CustomAzureCognitiveSearchMemoryStore ;
12
13
import com .microsoft .openai .samples .rag .common .ChatGPTConversation ;
13
- import com .microsoft .openai .samples .rag .common .ChatGPTMessage ;
14
14
import com .microsoft .openai .samples .rag .common .ChatGPTUtils ;
15
+ import com .microsoft .openai .samples .rag .controller .ChatResponse ;
15
16
import com .microsoft .semantickernel .Kernel ;
16
17
import com .microsoft .semantickernel .SKBuilders ;
17
18
import com .microsoft .semantickernel .ai .embeddings .Embedding ;
18
- import com .microsoft .semantickernel .chatcompletion .ChatCompletion ;
19
- import com .microsoft .semantickernel .connectors .ai .openai .chatcompletion .OpenAIChatCompletion ;
20
- import com .microsoft .semantickernel .connectors .ai .openai .chatcompletion .OpenAIChatHistory ;
21
19
import com .microsoft .semantickernel .memory .MemoryQueryResult ;
22
20
import com .microsoft .semantickernel .memory .MemoryRecord ;
23
21
import com .microsoft .semantickernel .orchestration .SKContext ;
24
22
import com .microsoft .semantickernel .orchestration .SKFunction ;
25
- import com .microsoft .semantickernel .semanticfunctions .PromptTemplateConfig ;
26
23
import org .slf4j .Logger ;
27
24
import org .slf4j .LoggerFactory ;
28
25
import org .springframework .beans .factory .annotation .Value ;
29
26
import org .springframework .stereotype .Component ;
30
27
import reactor .core .publisher .Mono ;
31
28
29
+ import java .io .IOException ;
32
30
import java .io .OutputStream ;
33
31
import java .util .List ;
34
32
import java .util .function .Function ;
@@ -47,6 +45,8 @@ public class JavaSemanticKernelWithMemoryChatApproach implements RAGApproach<Cha
47
45
48
46
private final SearchAsyncClient searchAsyncClient ;
49
47
48
+ private final ObjectMapper objectMapper ;
49
+
50
50
private final String EMBEDDING_FIELD_NAME = "embedding" ;
51
51
52
52
@ Value ("${cognitive.search.service}" )
@@ -58,10 +58,11 @@ public class JavaSemanticKernelWithMemoryChatApproach implements RAGApproach<Cha
58
58
59
59
@ Value ("${openai.embedding.deployment}" )
60
60
private String embeddingDeploymentModelId ;
61
- public JavaSemanticKernelWithMemoryChatApproach (TokenCredential tokenCredential , OpenAIAsyncClient openAIAsyncClient , SearchAsyncClient searchAsyncClient ) {
61
+ public JavaSemanticKernelWithMemoryChatApproach (TokenCredential tokenCredential , OpenAIAsyncClient openAIAsyncClient , SearchAsyncClient searchAsyncClient , ObjectMapper objectMapper ) {
62
62
this .tokenCredential = tokenCredential ;
63
63
this .openAIAsyncClient = openAIAsyncClient ;
64
64
this .searchAsyncClient = searchAsyncClient ;
65
+ this .objectMapper = objectMapper ;
65
66
}
66
67
67
68
@ Override
@@ -101,7 +102,48 @@ public RAGResponse run(ChatGPTConversation questionOrConversation, RAGOptions op
101
102
102
103
@ Override
103
104
public void runStreaming (ChatGPTConversation questionOrConversation , RAGOptions options , OutputStream outputStream ) {
104
- throw new IllegalStateException ("Streaming not supported for this approach" );
105
+ String question = ChatGPTUtils .getLastUserQuestion (questionOrConversation .getMessages ());
106
+
107
+ // STEP 1: Build semantic kernel with Azure Cognitive Search as memory store. AnswerQuestion skill is imported from resources.
108
+ Kernel semanticKernel = buildSemanticKernel (options );
109
+
110
+ // STEP 2: Retrieve relevant documents using keywords extracted from the chat history
111
+ String conversation = ChatGPTUtils .formatAsChatML (questionOrConversation .toOpenAIChatMessages ());
112
+ List <MemoryQueryResult > sourcesResult = getSourcesFromConversation (conversation , semanticKernel , options );
113
+
114
+ LOGGER .info ("Total {} sources found in cognitive vector store for search query[{}]" , sourcesResult .size (), question );
115
+
116
+ String sources = buildSourcesText (sourcesResult );
117
+ List <ContentSource > sourcesList = buildSources (sourcesResult );
118
+
119
+ // STEP 3: Generate a contextual and content specific answer using the search results and chat history
120
+ SKFunction answerConversation = semanticKernel .getFunction ("RAG" , "AnswerConversation" );
121
+ SKContext skcontext = SKBuilders .context ().build ()
122
+ .setVariable ("sources" , sources )
123
+ .setVariable ("conversation" , conversation )
124
+ .setVariable ("suggestions" , String .valueOf (options .isSuggestFollowupQuestions ()))
125
+ .setVariable ("input" , question );
126
+
127
+ SKContext reply = (SKContext ) answerConversation .invokeAsync (skcontext ).block ();
128
+
129
+ RAGResponse ragResponse =
130
+ new RAGResponse .Builder ()
131
+ .question (
132
+ ChatGPTUtils .getLastUserQuestion (
133
+ questionOrConversation .getMessages ()))
134
+ .prompt ("placeholders for prompt" )
135
+ .answer (reply .getResult ())
136
+ .sources (sourcesList )
137
+ .sourcesAsText (sources )
138
+ .build ();
139
+
140
+ try {
141
+ String value = objectMapper .writeValueAsString (ChatResponse .buildChatResponse (ragResponse )) + "\n " ;
142
+ outputStream .write (value .getBytes ());
143
+ outputStream .flush ();
144
+ } catch (IOException e ) {
145
+ throw new RuntimeException (e );
146
+ }
105
147
}
106
148
107
149
private List <MemoryQueryResult > getSourcesFromConversation (String conversation , Kernel kernel , RAGOptions options ) {
0 commit comments