1
+ // Copyright (c) Microsoft. All rights reserved.
1
2
package com .microsoft .semantickernel .agents .chatcompletion ;
2
3
3
4
import com .microsoft .semantickernel .Kernel ;
6
7
import com .microsoft .semantickernel .agents .AgentThread ;
7
8
import com .microsoft .semantickernel .agents .KernelAgent ;
8
9
import com .microsoft .semantickernel .builders .SemanticKernelBuilder ;
10
+ import com .microsoft .semantickernel .functionchoice .AutoFunctionChoiceBehavior ;
9
11
import com .microsoft .semantickernel .orchestration .InvocationContext ;
10
12
import com .microsoft .semantickernel .orchestration .InvocationReturnMode ;
11
13
import com .microsoft .semantickernel .orchestration .PromptExecutionSettings ;
12
- import com .microsoft .semantickernel .orchestration .ToolCallBehavior ;
13
14
import com .microsoft .semantickernel .semanticfunctions .KernelArguments ;
14
15
import com .microsoft .semantickernel .semanticfunctions .PromptTemplate ;
15
16
import com .microsoft .semantickernel .semanticfunctions .PromptTemplateConfig ;
@@ -37,8 +38,7 @@ private ChatCompletionAgent(
37
38
KernelArguments kernelArguments ,
38
39
InvocationContext context ,
39
40
String instructions ,
40
- PromptTemplate template
41
- ) {
41
+ PromptTemplate template ) {
42
42
super (
43
43
id ,
44
44
name ,
@@ -47,8 +47,7 @@ private ChatCompletionAgent(
47
47
kernelArguments ,
48
48
context ,
49
49
instructions ,
50
- template
51
- );
50
+ template );
52
51
}
53
52
54
53
/**
@@ -61,70 +60,65 @@ private ChatCompletionAgent(
61
60
*/
62
61
@ Override
63
62
public Mono <List <AgentResponseItem <ChatMessageContent <?>>>> invokeAsync (
64
- List <ChatMessageContent <?>> messages ,
65
- AgentThread thread ,
66
- @ Nullable AgentInvokeOptions options
67
- ) {
63
+ List <ChatMessageContent <?>> messages ,
64
+ @ Nullable AgentThread thread ,
65
+ @ Nullable AgentInvokeOptions options ) {
68
66
return ensureThreadExistsWithMessagesAsync (messages , thread , ChatHistoryAgentThread ::new )
69
- .cast (ChatHistoryAgentThread .class )
70
- .flatMap (agentThread -> {
71
- // Extract the chat history from the thread
72
- ChatHistory history = new ChatHistory (
73
- agentThread .getChatHistory ().getMessages ()
74
- );
75
-
76
- // Invoke the agent with the chat history
77
- return internalInvokeAsync (
78
- history ,
79
- options
80
- )
81
- .flatMapMany (Flux ::fromIterable )
82
- // notify on the new thread instance
83
- .concatMap (agentMessage -> this .notifyThreadOfNewMessageAsync (agentThread , agentMessage ).thenReturn (agentMessage ))
84
- .collectList ()
85
- .map (chatMessageContents ->
86
- chatMessageContents .stream ()
87
- .map (message -> new AgentResponseItem <ChatMessageContent <?>>(message , agentThread ))
88
- .collect (Collectors .toList ())
89
- );
90
- });
67
+ .cast (ChatHistoryAgentThread .class )
68
+ .flatMap (agentThread -> {
69
+ // Extract the chat history from the thread
70
+ ChatHistory history = new ChatHistory (
71
+ agentThread .getChatHistory ().getMessages ());
72
+
73
+ // Invoke the agent with the chat history
74
+ return internalInvokeAsync (
75
+ history ,
76
+ agentThread ,
77
+ options )
78
+ .map (chatMessageContents -> chatMessageContents .stream ()
79
+ .map (message -> new AgentResponseItem <ChatMessageContent <?>>(message ,
80
+ agentThread ))
81
+ .collect (Collectors .toList ()));
82
+ });
91
83
}
92
84
93
85
private Mono <List <ChatMessageContent <?>>> internalInvokeAsync (
94
86
ChatHistory history ,
95
- @ Nullable AgentInvokeOptions options
96
- ) {
87
+ AgentThread thread ,
88
+ @ Nullable AgentInvokeOptions options ) {
97
89
if (options == null ) {
98
90
options = new AgentInvokeOptions ();
99
91
}
100
92
101
93
final Kernel kernel = options .getKernel () != null ? options .getKernel () : this .kernel ;
102
94
final KernelArguments arguments = mergeArguments (options .getKernelArguments ());
103
95
final String additionalInstructions = options .getAdditionalInstructions ();
104
- final InvocationContext invocationContext = options .getInvocationContext () != null ? options .getInvocationContext () : this .invocationContext ;
96
+ final InvocationContext invocationContext = options .getInvocationContext () != null
97
+ ? options .getInvocationContext ()
98
+ : this .invocationContext ;
105
99
106
100
try {
107
- ChatCompletionService chatCompletionService = kernel .getService (ChatCompletionService .class , arguments );
101
+ ChatCompletionService chatCompletionService = kernel
102
+ .getService (ChatCompletionService .class , arguments );
108
103
109
- PromptExecutionSettings executionSettings = invocationContext != null && invocationContext .getPromptExecutionSettings () != null
104
+ PromptExecutionSettings executionSettings = invocationContext != null
105
+ && invocationContext .getPromptExecutionSettings () != null
110
106
? invocationContext .getPromptExecutionSettings ()
111
- : kernelArguments .getExecutionSettings ().get (chatCompletionService .getServiceId ());
112
-
113
- ToolCallBehavior toolCallBehavior = invocationContext != null
114
- ? invocationContext .getToolCallBehavior ()
115
- : ToolCallBehavior .allowAllKernelFunctions (true );
107
+ : arguments .getExecutionSettings ()
108
+ .get (chatCompletionService .getServiceId ());
116
109
117
110
// Build base invocation context
118
111
InvocationContext .Builder builder = InvocationContext .builder ()
119
- .withPromptExecutionSettings (executionSettings )
120
- .withToolCallBehavior (toolCallBehavior )
121
- .withReturnMode (InvocationReturnMode .NEW_MESSAGES_ONLY );
112
+ .withPromptExecutionSettings (executionSettings )
113
+ .withReturnMode (InvocationReturnMode .NEW_MESSAGES_ONLY );
122
114
123
115
if (invocationContext != null ) {
124
116
builder = builder
125
- .withTelemetry (invocationContext .getTelemetry ())
126
- .withContextVariableConverter (invocationContext .getContextVariableTypes ())
127
- .withKernelHooks (invocationContext .getKernelHooks ());
117
+ .withTelemetry (invocationContext .getTelemetry ())
118
+ .withFunctionChoiceBehavior (invocationContext .getFunctionChoiceBehavior ())
119
+ .withToolCallBehavior (invocationContext .getToolCallBehavior ())
120
+ .withContextVariableConverter (invocationContext .getContextVariableTypes ())
121
+ .withKernelHooks (invocationContext .getKernelHooks ());
128
122
}
129
123
130
124
InvocationContext agentInvocationContext = builder .build ();
@@ -133,32 +127,65 @@ private Mono<List<ChatMessageContent<?>>> internalInvokeAsync(
133
127
instructions -> {
134
128
// Create a new chat history with the instructions
135
129
ChatHistory chat = new ChatHistory (
136
- instructions
137
- );
130
+ instructions );
138
131
139
132
// Add agent additional instructions
140
133
if (additionalInstructions != null ) {
141
134
chat .addMessage (new ChatMessageContent <>(
142
- AuthorRole .SYSTEM ,
143
- additionalInstructions
144
- ));
135
+ AuthorRole .SYSTEM ,
136
+ additionalInstructions ));
145
137
}
146
138
147
139
// Add the chat history to the new chat
148
140
chat .addAll (history );
149
141
150
- return chatCompletionService .getChatMessageContentsAsync (chat , kernel , agentInvocationContext );
151
- }
152
- );
142
+ // Retrieve the chat message contents asynchronously and notify the thread
143
+ if (shouldNotifyFunctionCalls (agentInvocationContext )) {
144
+ // Notify all messages including function calls
145
+ return chatCompletionService
146
+ .getChatMessageContentsAsync (chat , kernel , agentInvocationContext )
147
+ .flatMapMany (Flux ::fromIterable )
148
+ .concatMap (message -> notifyThreadOfNewMessageAsync (thread , message )
149
+ .thenReturn (message ))
150
+ // Filter out function calls and their results
151
+ .filter (message -> message .getContent () != null
152
+ && message .getAuthorRole () != AuthorRole .TOOL )
153
+ .collect (Collectors .toList ());
154
+ }
155
+
156
+ // Return chat completion messages without notifying the thread
157
+ // We shouldn't add the function call content to the thread, since
158
+ // we don't know if the user will execute the call. They should add it themselves.
159
+ return chatCompletionService .getChatMessageContentsAsync (chat , kernel ,
160
+ agentInvocationContext );
161
+ });
153
162
154
163
} catch (ServiceNotFoundException e ) {
155
164
return Mono .error (e );
156
165
}
157
166
}
158
167
168
+ boolean shouldNotifyFunctionCalls (InvocationContext invocationContext ) {
169
+ if (invocationContext == null ) {
170
+ return false ;
171
+ }
172
+
173
+ if (invocationContext .getFunctionChoiceBehavior () != null && invocationContext
174
+ .getFunctionChoiceBehavior () instanceof AutoFunctionChoiceBehavior ) {
175
+ return ((AutoFunctionChoiceBehavior ) invocationContext .getFunctionChoiceBehavior ())
176
+ .isAutoInvoke ();
177
+ }
178
+
179
+ if (invocationContext .getToolCallBehavior () != null ) {
180
+ return invocationContext .getToolCallBehavior ().isAutoInvokeAllowed ();
181
+ }
182
+
183
+ return false ;
184
+ }
159
185
160
186
@ Override
161
- public Mono <Void > notifyThreadOfNewMessageAsync (AgentThread thread , ChatMessageContent <?> message ) {
187
+ public Mono <Void > notifyThreadOfNewMessageAsync (AgentThread thread ,
188
+ ChatMessageContent <?> message ) {
162
189
return Mono .defer (() -> {
163
190
return thread .onNewMessageAsync (message );
164
191
});
@@ -273,11 +300,10 @@ public ChatCompletionAgent build() {
273
300
name ,
274
301
description ,
275
302
kernel ,
276
- kernelArguments ,
303
+ kernelArguments ,
277
304
invocationContext ,
278
305
instructions ,
279
- template
280
- );
306
+ template );
281
307
}
282
308
283
309
/**
@@ -287,17 +313,17 @@ public ChatCompletionAgent build() {
287
313
* @param promptTemplateFactory The prompt template factory to use.
288
314
* @return The ChatCompletionAgent instance.
289
315
*/
290
- public ChatCompletionAgent build (PromptTemplateConfig promptTemplateConfig , PromptTemplateFactory promptTemplateFactory ) {
316
+ public ChatCompletionAgent build (PromptTemplateConfig promptTemplateConfig ,
317
+ PromptTemplateFactory promptTemplateFactory ) {
291
318
return new ChatCompletionAgent (
292
319
id ,
293
320
name ,
294
321
description ,
295
322
kernel ,
296
- kernelArguments ,
323
+ kernelArguments ,
297
324
invocationContext ,
298
325
promptTemplateConfig .getTemplate (),
299
- promptTemplateFactory .tryCreate (promptTemplateConfig )
300
- );
326
+ promptTemplateFactory .tryCreate (promptTemplateConfig ));
301
327
}
302
328
}
303
329
}
0 commit comments