Skip to content

Commit 7c7c561

Browse files
author
Milder Hernandez
authored
Merge pull request #308 from milderhc/completions-agent
Add ChatCompletionAgent
2 parents 69af29b + a19b09f commit 7c7c561

File tree

17 files changed

+1508
-1
lines changed

17 files changed

+1508
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>com.microsoft.semantic-kernel</groupId>
8+
<artifactId>semantickernel-parent</artifactId>
9+
<version>1.4.4-SNAPSHOT</version>
10+
<relativePath>../../pom.xml</relativePath>
11+
</parent>
12+
13+
<artifactId>semantickernel-agents-core</artifactId>
14+
15+
<name>Semantic Kernel Chat Completion Agent</name>
16+
<description>Chat Completion Agent for Semantic Kernel</description>
17+
18+
<dependencies>
19+
<dependency>
20+
<groupId>com.microsoft.semantic-kernel</groupId>
21+
<artifactId>semantickernel-api</artifactId>
22+
</dependency>
23+
</dependencies>
24+
25+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
package com.microsoft.semantickernel.agents.chatcompletion;
2+
3+
import com.microsoft.semantickernel.Kernel;
4+
import com.microsoft.semantickernel.agents.AgentInvokeOptions;
5+
import com.microsoft.semantickernel.agents.AgentResponseItem;
6+
import com.microsoft.semantickernel.agents.AgentThread;
7+
import com.microsoft.semantickernel.agents.KernelAgent;
8+
import com.microsoft.semantickernel.builders.SemanticKernelBuilder;
9+
import com.microsoft.semantickernel.orchestration.InvocationContext;
10+
import com.microsoft.semantickernel.orchestration.InvocationReturnMode;
11+
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
12+
import com.microsoft.semantickernel.orchestration.ToolCallBehavior;
13+
import com.microsoft.semantickernel.semanticfunctions.KernelArguments;
14+
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
15+
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
16+
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateFactory;
17+
import com.microsoft.semantickernel.services.ServiceNotFoundException;
18+
import com.microsoft.semantickernel.services.chatcompletion.AuthorRole;
19+
import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService;
20+
import com.microsoft.semantickernel.services.chatcompletion.ChatHistory;
21+
import com.microsoft.semantickernel.services.chatcompletion.ChatMessageContent;
22+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23+
import reactor.core.publisher.Flux;
24+
import reactor.core.publisher.Mono;
25+
26+
import javax.annotation.Nullable;
27+
import java.util.List;
28+
import java.util.stream.Collectors;
29+
30+
public class ChatCompletionAgent extends KernelAgent {
31+
32+
private ChatCompletionAgent(
33+
String id,
34+
String name,
35+
String description,
36+
Kernel kernel,
37+
KernelArguments kernelArguments,
38+
InvocationContext context,
39+
String instructions,
40+
PromptTemplate template
41+
) {
42+
super(
43+
id,
44+
name,
45+
description,
46+
kernel,
47+
kernelArguments,
48+
context,
49+
instructions,
50+
template
51+
);
52+
}
53+
54+
/**
55+
* Invoke the agent with the given chat history.
56+
*
57+
* @param messages The chat history to process
58+
* @param thread The agent thread to use
59+
* @param options The options for invoking the agent
60+
* @return A Mono containing the agent response
61+
*/
62+
@Override
63+
public Mono<List<AgentResponseItem<ChatMessageContent<?>>>> invokeAsync(
64+
List<ChatMessageContent<?>> messages,
65+
AgentThread thread,
66+
@Nullable AgentInvokeOptions options
67+
) {
68+
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+
});
91+
}
92+
93+
private Mono<List<ChatMessageContent<?>>> internalInvokeAsync(
94+
ChatHistory history,
95+
@Nullable AgentInvokeOptions options
96+
) {
97+
if (options == null) {
98+
options = new AgentInvokeOptions();
99+
}
100+
101+
final Kernel kernel = options.getKernel() != null ? options.getKernel() : this.kernel;
102+
final KernelArguments arguments = mergeArguments(options.getKernelArguments());
103+
final String additionalInstructions = options.getAdditionalInstructions();
104+
final InvocationContext invocationContext = options.getInvocationContext() != null ? options.getInvocationContext() : this.invocationContext;
105+
106+
try {
107+
ChatCompletionService chatCompletionService = kernel.getService(ChatCompletionService.class, arguments);
108+
109+
PromptExecutionSettings executionSettings = invocationContext != null && invocationContext.getPromptExecutionSettings() != null
110+
? invocationContext.getPromptExecutionSettings()
111+
: kernelArguments.getExecutionSettings().get(chatCompletionService.getServiceId());
112+
113+
ToolCallBehavior toolCallBehavior = invocationContext != null
114+
? invocationContext.getToolCallBehavior()
115+
: ToolCallBehavior.allowAllKernelFunctions(true);
116+
117+
// Build base invocation context
118+
InvocationContext.Builder builder = InvocationContext.builder()
119+
.withPromptExecutionSettings(executionSettings)
120+
.withToolCallBehavior(toolCallBehavior)
121+
.withReturnMode(InvocationReturnMode.NEW_MESSAGES_ONLY);
122+
123+
if (invocationContext != null) {
124+
builder = builder
125+
.withTelemetry(invocationContext.getTelemetry())
126+
.withContextVariableConverter(invocationContext.getContextVariableTypes())
127+
.withKernelHooks(invocationContext.getKernelHooks());
128+
}
129+
130+
InvocationContext agentInvocationContext = builder.build();
131+
132+
return renderInstructionsAsync(kernel, arguments, agentInvocationContext).flatMap(
133+
instructions -> {
134+
// Create a new chat history with the instructions
135+
ChatHistory chat = new ChatHistory(
136+
instructions
137+
);
138+
139+
// Add agent additional instructions
140+
if (additionalInstructions != null) {
141+
chat.addMessage(new ChatMessageContent<>(
142+
AuthorRole.SYSTEM,
143+
additionalInstructions
144+
));
145+
}
146+
147+
// Add the chat history to the new chat
148+
chat.addAll(history);
149+
150+
return chatCompletionService.getChatMessageContentsAsync(chat, kernel, agentInvocationContext);
151+
}
152+
);
153+
154+
} catch (ServiceNotFoundException e) {
155+
return Mono.error(e);
156+
}
157+
}
158+
159+
160+
@Override
161+
public Mono<Void> notifyThreadOfNewMessageAsync(AgentThread thread, ChatMessageContent<?> message) {
162+
return Mono.defer(() -> {
163+
return thread.onNewMessageAsync(message);
164+
});
165+
}
166+
167+
/**
168+
* Builder for creating instances of ChatCompletionAgent.
169+
*/
170+
public static Builder builder() {
171+
return new Builder();
172+
}
173+
174+
public static class Builder implements SemanticKernelBuilder<ChatCompletionAgent> {
175+
private String id;
176+
private String name;
177+
private String description;
178+
private Kernel kernel;
179+
private KernelArguments kernelArguments;
180+
private InvocationContext invocationContext;
181+
private String instructions;
182+
private PromptTemplate template;
183+
184+
/**
185+
* Set the ID of the agent.
186+
*
187+
* @param id The ID of the agent.
188+
*/
189+
public Builder withId(String id) {
190+
this.id = id;
191+
return this;
192+
}
193+
194+
/**
195+
* Set the name of the agent.
196+
*
197+
* @param name The name of the agent.
198+
*/
199+
public Builder withName(String name) {
200+
this.name = name;
201+
return this;
202+
}
203+
204+
/**
205+
* Set the description of the agent.
206+
*
207+
* @param description The description of the agent.
208+
*/
209+
public Builder withDescription(String description) {
210+
this.description = description;
211+
return this;
212+
}
213+
214+
/**
215+
* Set the kernel to use for the agent.
216+
*
217+
* @param kernel The kernel to use.
218+
*/
219+
public Builder withKernel(Kernel kernel) {
220+
this.kernel = kernel;
221+
return this;
222+
}
223+
224+
/**
225+
* Set the kernel arguments to use for the agent.
226+
*
227+
* @param KernelArguments The kernel arguments to use.
228+
*/
229+
@SuppressFBWarnings("EI_EXPOSE_REP2")
230+
public Builder withKernelArguments(KernelArguments KernelArguments) {
231+
this.kernelArguments = KernelArguments;
232+
return this;
233+
}
234+
235+
/**
236+
* Set the instructions for the agent.
237+
*
238+
* @param instructions The instructions for the agent.
239+
*/
240+
public Builder withInstructions(String instructions) {
241+
this.instructions = instructions;
242+
return this;
243+
}
244+
245+
/**
246+
* Set the invocation context for the agent.
247+
*
248+
* @param invocationContext The invocation context to use.
249+
*/
250+
public Builder withInvocationContext(InvocationContext invocationContext) {
251+
this.invocationContext = invocationContext;
252+
return this;
253+
}
254+
255+
/**
256+
* Set the template for the agent.
257+
*
258+
* @param template The template to use.
259+
*/
260+
public Builder withTemplate(PromptTemplate template) {
261+
this.template = template;
262+
return this;
263+
}
264+
265+
/**
266+
* Build the ChatCompletionAgent instance.
267+
*
268+
* @return The ChatCompletionAgent instance.
269+
*/
270+
public ChatCompletionAgent build() {
271+
return new ChatCompletionAgent(
272+
id,
273+
name,
274+
description,
275+
kernel,
276+
kernelArguments,
277+
invocationContext,
278+
instructions,
279+
template
280+
);
281+
}
282+
283+
/**
284+
* Build the ChatCompletionAgent instance with the given prompt template config and factory.
285+
*
286+
* @param promptTemplateConfig The prompt template config to use.
287+
* @param promptTemplateFactory The prompt template factory to use.
288+
* @return The ChatCompletionAgent instance.
289+
*/
290+
public ChatCompletionAgent build(PromptTemplateConfig promptTemplateConfig, PromptTemplateFactory promptTemplateFactory) {
291+
return new ChatCompletionAgent(
292+
id,
293+
name,
294+
description,
295+
kernel,
296+
kernelArguments,
297+
invocationContext,
298+
promptTemplateConfig.getTemplate(),
299+
promptTemplateFactory.tryCreate(promptTemplateConfig)
300+
);
301+
}
302+
}
303+
}

0 commit comments

Comments
 (0)