Skip to content

Commit 9a7c650

Browse files
authored
Merge branch 'microsoft:main' into main
2 parents ba1aa96 + afcfc21 commit 9a7c650

68 files changed

Lines changed: 3065 additions & 320 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 1.4.4-RC1
2+
3+
- Add Agent framework abstractions.
4+
- Add ChatCompletionAgent implementation.
5+
- Add FunctionChoiceBehavior for OpenAI, replacing the older ToolCallBehavior.
6+
17
# 1.4.3
28

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

0 commit comments

Comments
 (0)