Skip to content

Commit bb5103a

Browse files
authored
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄
1 parent 85f4d15 commit bb5103a

4 files changed

Lines changed: 224 additions & 128 deletions

File tree

‎java/CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 1.1.3
2+
3+
- Fix bug appending plugin name to tool calls
4+
- Improve exception handling in OpenAIChatCompletion
5+
16
# 1.1.2
27

38
- Upgrade azure-identity to 1.12.1

‎java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java‎

Lines changed: 159 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
3131
import com.microsoft.semantickernel.exceptions.AIException;
3232
import com.microsoft.semantickernel.exceptions.AIException.ErrorCodes;
33+
import com.microsoft.semantickernel.exceptions.SKCheckedException;
3334
import com.microsoft.semantickernel.exceptions.SKException;
3435
import com.microsoft.semantickernel.hooks.KernelHookEvent;
3536
import com.microsoft.semantickernel.hooks.KernelHooks;
@@ -103,29 +104,33 @@ public Mono<List<ChatMessageContent<?>>> getChatMessageContentsAsync(
103104
messages,
104105
kernel,
105106
invocationContext)
106-
.map(history -> {
107+
.flatMap(history -> {
108+
try {
109+
ChatHistory chatHistoryResult;
107110

108-
ChatHistory chatHistoryResult;
111+
if (invocationContext != null
112+
&& invocationContext.returnMode() == InvocationReturnMode.FULL_HISTORY) {
113+
chatHistoryResult = new ChatHistory(chatHistory.getMessages());
114+
} else {
115+
chatHistoryResult = new ChatHistory();
116+
}
109117

110-
if (invocationContext != null
111-
&& invocationContext.returnMode() == InvocationReturnMode.FULL_HISTORY) {
112-
chatHistoryResult = new ChatHistory(chatHistory.getMessages());
113-
} else {
114-
chatHistoryResult = new ChatHistory();
115-
}
118+
chatHistoryResult.addAll(
119+
new ChatHistory(toOpenAIChatMessageContent(history.newMessages)));
120+
chatHistoryResult.addAll(new ChatHistory(history.newChatMessageContent));
116121

117-
chatHistoryResult.addAll(
118-
new ChatHistory(toOpenAIChatMessageContent(history.newMessages)));
119-
chatHistoryResult.addAll(new ChatHistory(history.newChatMessageContent));
122+
if (invocationContext != null
123+
&& invocationContext
124+
.returnMode() == InvocationReturnMode.LAST_MESSAGE_ONLY) {
125+
chatHistoryResult = new ChatHistory(
126+
Collections.singletonList(
127+
CollectionUtil.getLastOrNull(chatHistory.getMessages())));
128+
}
120129

121-
if (invocationContext != null
122-
&& invocationContext.returnMode() == InvocationReturnMode.LAST_MESSAGE_ONLY) {
123-
chatHistoryResult = new ChatHistory(
124-
Collections.singletonList(
125-
CollectionUtil.getLastOrNull(chatHistory.getMessages())));
130+
return Mono.just(chatHistoryResult.getMessages());
131+
} catch (Exception e) {
132+
return Mono.error(e);
126133
}
127-
128-
return chatHistoryResult.getMessages();
129134
});
130135
}
131136

@@ -142,19 +147,24 @@ public Mono<List<ChatMessageContent<?>>> getChatMessageContentsAsync(
142147
messages,
143148
kernel,
144149
invocationContext)
145-
.map(m -> {
146-
ChatHistory result = new ChatHistory(toOpenAIChatMessageContent(m.allMessages));
150+
.flatMap(m -> {
151+
try {
152+
ChatHistory result = new ChatHistory(toOpenAIChatMessageContent(m.allMessages));
147153

148-
result.addAll(new ChatHistory(m.newChatMessageContent));
154+
result.addAll(new ChatHistory(m.newChatMessageContent));
149155

150-
if (invocationContext != null
151-
&& invocationContext.returnMode() == InvocationReturnMode.LAST_MESSAGE_ONLY) {
152-
result = new ChatHistory(
153-
Collections.singletonList(
154-
CollectionUtil.getLastOrNull(result.getMessages())));
155-
}
156+
if (invocationContext != null
157+
&& invocationContext
158+
.returnMode() == InvocationReturnMode.LAST_MESSAGE_ONLY) {
159+
result = new ChatHistory(
160+
Collections.singletonList(
161+
CollectionUtil.getLastOrNull(result.getMessages())));
162+
}
156163

157-
return result.getMessages();
164+
return Mono.just(result.getMessages());
165+
} catch (SKCheckedException e) {
166+
return Mono.error(e);
167+
}
158168
});
159169
}
160170

@@ -449,7 +459,7 @@ private Mono<FunctionResult<String>> invokeFunctionTool(
449459
.withArguments(arguments)
450460
.withResultType(contextVariableTypes.getVariableTypeForClass(String.class));
451461
} catch (JsonProcessingException e) {
452-
return Mono.error(new SKException("Failed to parse tool arguments"));
462+
return Mono.error(new SKException("Failed to parse tool arguments", e));
453463
}
454464
}
455465

@@ -466,7 +476,8 @@ private static <T extends KernelHookEvent> T executeHook(
466476

467477
@SuppressWarnings("StringSplitter")
468478
private OpenAIFunctionToolCall extractOpenAIFunctionToolCall(
469-
ChatCompletionsFunctionToolCall toolCall) throws JsonProcessingException {
479+
ChatCompletionsFunctionToolCall toolCall)
480+
throws JsonProcessingException {
470481

471482
// Split the full name of a function into plugin and function name
472483
String name = toolCall.getFunction().getName();
@@ -495,7 +506,6 @@ private OpenAIFunctionToolCall extractOpenAIFunctionToolCall(
495506
pluginName,
496507
fnName,
497508
arguments);
498-
499509
}
500510

501511
private Mono<List<OpenAIChatMessageContent>> getChatMessageContentsAsync(
@@ -513,121 +523,144 @@ private Mono<List<OpenAIChatMessageContent>> getChatMessageContentsAsync(
513523
.collect(Collectors.toList());
514524

515525
return Flux.fromIterable(responseMessages)
516-
.map(response -> new OpenAIChatMessageContent(
517-
AuthorRole.ASSISTANT,
518-
response.getContent(),
519-
this.getModelId(),
520-
null,
521-
null,
522-
completionMetadata,
523-
formOpenAiToolCalls(response)))
526+
.flatMap(response -> {
527+
try {
528+
return Mono.just(new OpenAIChatMessageContent(
529+
AuthorRole.ASSISTANT,
530+
response.getContent(),
531+
this.getModelId(),
532+
null,
533+
null,
534+
completionMetadata,
535+
formOpenAiToolCalls(response)));
536+
} catch (Exception e) {
537+
return Mono.error(e);
538+
}
539+
})
524540
.collectList();
525541
}
526542

527543
private List<ChatMessageContent<?>> toOpenAIChatMessageContent(
528-
List<ChatRequestMessage> requestMessages) {
529-
return requestMessages
530-
.stream()
531-
.map(message -> {
532-
if (message instanceof ChatRequestUserMessage) {
533-
return new OpenAIChatMessageContent<>(
534-
AuthorRole.USER,
535-
((ChatRequestUserMessage) message).getContent().toString(),
536-
null,
537-
null,
538-
null,
539-
null,
540-
null);
541-
} else if (message instanceof ChatRequestSystemMessage) {
542-
return new OpenAIChatMessageContent<>(
543-
AuthorRole.SYSTEM,
544-
((ChatRequestSystemMessage) message).getContent(),
545-
null,
546-
null,
547-
null,
548-
null,
549-
null);
550-
} else if (message instanceof ChatRequestAssistantMessage) {
551-
List<OpenAIFunctionToolCall> calls = getToolCalls(
552-
((ChatRequestAssistantMessage) message).getToolCalls());
553-
return new OpenAIChatMessageContent<>(
554-
AuthorRole.ASSISTANT,
555-
((ChatRequestAssistantMessage) message).getContent(),
556-
null,
557-
null,
558-
null,
559-
null,
560-
calls);
561-
} else if (message instanceof ChatRequestToolMessage) {
562-
return new OpenAIChatMessageContent<>(
563-
AuthorRole.TOOL,
564-
((ChatRequestToolMessage) message).getContent(),
565-
null,
566-
null,
567-
null,
568-
FunctionResultMetadata.build(
569-
((ChatRequestToolMessage) message).getToolCallId(),
544+
List<ChatRequestMessage> requestMessages) throws SKCheckedException {
545+
try {
546+
return requestMessages
547+
.stream()
548+
.map(message -> {
549+
if (message instanceof ChatRequestUserMessage) {
550+
return new OpenAIChatMessageContent<>(
551+
AuthorRole.USER,
552+
((ChatRequestUserMessage) message).getContent().toString(),
570553
null,
571-
null),
572-
null);
573-
}
554+
null,
555+
null,
556+
null,
557+
null);
558+
} else if (message instanceof ChatRequestSystemMessage) {
559+
return new OpenAIChatMessageContent<>(
560+
AuthorRole.SYSTEM,
561+
((ChatRequestSystemMessage) message).getContent(),
562+
null,
563+
null,
564+
null,
565+
null,
566+
null);
567+
} else if (message instanceof ChatRequestAssistantMessage) {
568+
try {
569+
List<OpenAIFunctionToolCall> calls = getToolCalls(
570+
((ChatRequestAssistantMessage) message).getToolCalls());
571+
return new OpenAIChatMessageContent<>(
572+
AuthorRole.ASSISTANT,
573+
((ChatRequestAssistantMessage) message).getContent(),
574+
null,
575+
null,
576+
null,
577+
null,
578+
calls);
579+
} catch (SKCheckedException e) {
580+
throw SKException.build("Failed to form assistant message", e);
581+
}
582+
} else if (message instanceof ChatRequestToolMessage) {
583+
return new OpenAIChatMessageContent<>(
584+
AuthorRole.TOOL,
585+
((ChatRequestToolMessage) message).getContent(),
586+
null,
587+
null,
588+
null,
589+
FunctionResultMetadata.build(
590+
((ChatRequestToolMessage) message).getToolCallId(),
591+
null,
592+
null),
593+
null);
594+
}
574595

575-
throw new SKException(
576-
"Unknown message type: " + message.getClass().getSimpleName());
577-
})
578-
.collect(Collectors.toList());
596+
throw new SKException(
597+
"Unknown message type: " + message.getClass().getSimpleName());
598+
})
599+
.collect(Collectors.toList());
600+
} catch (SKException e) {
601+
throw SKCheckedException.build("Failed to form OpenAI chat message content", e);
602+
}
579603
}
580604

581605
@Nullable
582606
private List<OpenAIFunctionToolCall> getToolCalls(
583-
@Nullable List<ChatCompletionsToolCall> toolCalls) {
607+
@Nullable List<ChatCompletionsToolCall> toolCalls) throws SKCheckedException {
584608
if (toolCalls == null || toolCalls.isEmpty()) {
585609
return null;
586610
}
587-
return toolCalls
588-
.stream()
589-
.map(call -> {
590-
if (call instanceof ChatCompletionsFunctionToolCall) {
591-
try {
592-
return extractOpenAIFunctionToolCall(
593-
(ChatCompletionsFunctionToolCall) call);
594-
} catch (JsonProcessingException e) {
595-
throw new SKException("Failed to parse tool arguments", e);
611+
612+
try {
613+
return toolCalls
614+
.stream()
615+
.map(call -> {
616+
if (call instanceof ChatCompletionsFunctionToolCall) {
617+
try {
618+
return extractOpenAIFunctionToolCall(
619+
(ChatCompletionsFunctionToolCall) call);
620+
} catch (JsonProcessingException e) {
621+
throw SKException.build("Failed to parse tool arguments", e);
622+
}
623+
} else {
624+
return new OpenAIFunctionToolCall(
625+
call.getId(),
626+
null,
627+
null,
628+
null);
596629
}
597-
} else {
598-
return new OpenAIFunctionToolCall(
599-
call.getId(),
600-
null,
601-
null,
602-
null);
603-
}
604-
})
605-
.collect(Collectors.toList());
630+
})
631+
.collect(Collectors.toList());
632+
} catch (SKException e) {
633+
throw SKCheckedException.build("Failed to form tool call", e);
634+
}
606635
}
607636

608637
@Nullable
609638
private List<OpenAIFunctionToolCall> formOpenAiToolCalls(
610-
ChatResponseMessage response) {
639+
ChatResponseMessage response) throws SKCheckedException {
611640
if (response.getToolCalls() == null || response.getToolCalls().isEmpty()) {
612641
return null;
613642
}
614-
return response
615-
.getToolCalls()
616-
.stream()
617-
.map(call -> {
618-
if (call instanceof ChatCompletionsFunctionToolCall) {
619-
try {
620-
return extractOpenAIFunctionToolCall(
621-
(ChatCompletionsFunctionToolCall) call);
622-
} catch (JsonProcessingException e) {
623-
throw new SKException("Failed to parse tool arguments", e);
643+
try {
644+
return response
645+
.getToolCalls()
646+
.stream()
647+
.map(call -> {
648+
if (call instanceof ChatCompletionsFunctionToolCall) {
649+
try {
650+
return extractOpenAIFunctionToolCall(
651+
(ChatCompletionsFunctionToolCall) call);
652+
} catch (JsonProcessingException e) {
653+
throw SKException.build("Failed to parse tool arguments", e);
654+
}
655+
} else {
656+
return null;
624657
}
625-
} else {
626-
return null;
627-
}
628-
})
629-
.filter(Objects::nonNull)
630-
.collect(Collectors.toList());
658+
})
659+
.filter(Objects::nonNull)
660+
.collect(Collectors.toList());
661+
} catch (SKException e) {
662+
throw SKCheckedException.build("Failed to form tool call", e);
663+
}
631664
}
632665

633666
private static ChatCompletionsOptions getCompletionsOptions(
@@ -754,7 +787,7 @@ private static void configureToolCallBehaviorOptions(
754787
"{\"type\":\"function\",\"function\":{\"name\":\"%s\"}}", toolChoiceName);
755788
options.setToolChoice(BinaryData.fromObject(new ObjectMapper().readTree(json)));
756789
} catch (JsonProcessingException e) {
757-
throw new RuntimeException(e);
790+
throw SKException.build("Failed to parse tool choice", e);
758791
}
759792
return;
760793
}
@@ -889,7 +922,7 @@ private static ChatRequestAssistantMessage formAssistantMessage(
889922
: "{}";
890923

891924
String prefix = "";
892-
if (toolCall.getPluginName() == null) {
925+
if (toolCall.getPluginName() != null) {
893926
prefix = toolCall.getPluginName() + OpenAIFunction.getNameSeparator();
894927
}
895928
String name = prefix + toolCall.getFunctionName();

0 commit comments

Comments
 (0)