diff --git a/sdk/src/main/java/io/dapr/client/DaprClientImpl.java b/sdk/src/main/java/io/dapr/client/DaprClientImpl.java index 66c8772d6..6767bc569 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientImpl.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientImpl.java @@ -17,6 +17,7 @@ import com.google.common.base.Strings; import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.Empty; +import com.google.protobuf.Message; import io.dapr.client.domain.ActorMetadata; import io.dapr.client.domain.AppConnectionPropertiesHealthMetadata; import io.dapr.client.domain.AppConnectionPropertiesMetadata; @@ -27,10 +28,22 @@ import io.dapr.client.domain.BulkPublishResponseFailedEntry; import io.dapr.client.domain.CloudEvent; import io.dapr.client.domain.ComponentMetadata; import io.dapr.client.domain.ConfigurationItem; +import io.dapr.client.domain.ConversationFunction; import io.dapr.client.domain.ConversationInput; +import io.dapr.client.domain.ConversationInputAlpha2; +import io.dapr.client.domain.ConversationMessage; +import io.dapr.client.domain.ConversationMessageContent; import io.dapr.client.domain.ConversationOutput; import io.dapr.client.domain.ConversationRequest; +import io.dapr.client.domain.ConversationRequestAlpha2; import io.dapr.client.domain.ConversationResponse; +import io.dapr.client.domain.ConversationResponseAlpha2; +import io.dapr.client.domain.ConversationResultAlpha2; +import io.dapr.client.domain.ConversationResultChoices; +import io.dapr.client.domain.ConversationResultMessage; +import io.dapr.client.domain.ConversationToolCalls; +import io.dapr.client.domain.ConversationToolCallsFunction; +import io.dapr.client.domain.ConversationTools; import io.dapr.client.domain.DaprMetadata; import io.dapr.client.domain.DeleteJobRequest; import io.dapr.client.domain.DeleteStateRequest; @@ -100,6 +113,7 @@ import reactor.util.context.ContextView; import reactor.util.retry.Retry; import javax.annotation.Nonnull; + import java.io.IOException; import java.time.Duration; import java.time.Instant; @@ -1628,6 +1642,193 @@ public class DaprClientImpl extends AbstractDaprClient { } } + /** + * {@inheritDoc} + */ + @Override + public Mono converseAlpha2(ConversationRequestAlpha2 conversationRequestAlpha2) { + + try { + validateConversationRequestAlpha2(conversationRequestAlpha2); + + DaprProtos.ConversationRequestAlpha2.Builder protosConversationRequestBuilder = + DaprProtos.ConversationRequestAlpha2 + .newBuilder() + .setTemperature(conversationRequestAlpha2.getTemperature()) + .setScrubPii(conversationRequestAlpha2.isScrubPii()) + .setName(conversationRequestAlpha2.getName()); + + if (conversationRequestAlpha2.getContextId() != null) { + protosConversationRequestBuilder.setContextId(conversationRequestAlpha2.getContextId()); + } + + if (conversationRequestAlpha2.getToolChoice() != null) { + protosConversationRequestBuilder.setToolChoice(conversationRequestAlpha2.getToolChoice()); + } + + if (conversationRequestAlpha2.getTools() != null) { + for (ConversationTools tool : conversationRequestAlpha2.getTools()) { + + ConversationFunction conversationFunction = tool.getFunction(); + + Map protosConversationToolFunctionParameters = conversationFunction.getParameters() + .entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + e -> Any.pack((Message) e.getValue()) + )); + DaprProtos.ConversationToolsFunction protosConversationToolsFunction = + DaprProtos.ConversationToolsFunction.newBuilder() + .setName(conversationFunction.getName()) + .setDescription(conversationFunction.getDescription()) + .putAllParameters(protosConversationToolFunctionParameters) + .build(); + + DaprProtos.ConversationTools conversationTool = DaprProtos.ConversationTools.newBuilder() + .setFunction(protosConversationToolsFunction).build(); + + protosConversationRequestBuilder.addTools(conversationTool); + } + } + + for (ConversationInputAlpha2 input : conversationRequestAlpha2.getInputs()) { + DaprProtos.ConversationInputAlpha2.Builder conversationInputBuilder = DaprProtos.ConversationInputAlpha2 + .newBuilder() + .setScrubPii(input.isScrubPii()); + + if (input.getMessages() != null) { + + for (ConversationMessage conversationMessage : input.getMessages()) { + DaprProtos.ConversationMessage.Builder messageBuilder = + DaprProtos.ConversationMessage.newBuilder(); + + ConversationMessage.Role role = conversationMessage.getRole(); + switch (role) { + case TOOL: + messageBuilder.setOfTool(DaprProtos.ConversationMessageOfTool.newBuilder() + .setToolId(conversationMessage.getToolId()) + .setName(conversationMessage.getName()) + .addAllContent(getConversationMessageContent(conversationMessage)).build()); + break; + case USER: + messageBuilder.setOfUser(DaprProtos.ConversationMessageOfUser.newBuilder() + .setName(conversationMessage.getName()) + .addAllContent(getConversationMessageContent(conversationMessage)).build()); + break; + case ASSISTANT: + messageBuilder.setOfAssistant(DaprProtos.ConversationMessageOfAssistant.newBuilder() + .setName(conversationMessage.getName()) + .addAllToolCalls(getConversationToolCalls(conversationMessage)) + .addAllContent(getConversationMessageContent(conversationMessage)).build()); + break; + case DEVELOPER: + messageBuilder.setOfDeveloper(DaprProtos.ConversationMessageOfDeveloper.newBuilder() + .setName(conversationMessage.getName()) + .addAllContent(getConversationMessageContent(conversationMessage)).build()); + break; + case SYSTEM: + messageBuilder.setOfSystem(DaprProtos.ConversationMessageOfSystem.newBuilder() + .setName(conversationMessage.getName()) + .addAllContent(getConversationMessageContent(conversationMessage)).build()); + break; + default: throw new IllegalArgumentException("No role of type " + role + " found"); + } + + conversationInputBuilder.addMessages(messageBuilder.build()); + } + } + + protosConversationRequestBuilder.addInputs(conversationInputBuilder.build()); + } + + Mono conversationResponseMono = Mono.deferContextual( + context -> this.createMono( + it -> intercept(context, asyncStub) + .converseAlpha2(protosConversationRequestBuilder.build(), it) + ) + ); + + return conversationResponseMono.map(conversationResponse -> { + List results = new ArrayList<>(); + + for (DaprProtos.ConversationResultAlpha2 result : conversationResponse.getOutputsList()) { + List choices = new ArrayList<>(); + + for (DaprProtos.ConversationResultChoices choice : result.getChoicesList()) { + ConversationResultMessage message = null; + if (choice.hasMessage()) { + List toolCalls = new ArrayList<>(); + + for (DaprProtos.ConversationToolCalls toolCall : choice.getMessage().getToolCallsList()) { + ConversationToolCallsFunction function = null; + if (toolCall.hasFunction()) { + function = new ConversationToolCallsFunction( + toolCall.getFunction().getName(), + toolCall.getFunction().getArguments() + ); + } + + toolCalls.add(new ConversationToolCalls(toolCall.getId(), function)); + } + + message = new ConversationResultMessage( + choice.getMessage().getContent(), + toolCalls + ); + } + + choices.add(new ConversationResultChoices(choice.getFinishReason(), choice.getIndex(), message)); + } + + results.add(new ConversationResultAlpha2(choices)); + } + + return new ConversationResponseAlpha2(conversationResponse.getContextId(), results); + }); + } catch (Exception ex) { + return DaprException.wrapMono(ex); + } + } + + private List getConversationMessageContent( + ConversationMessage conversationMessage) { + + List conversationMessageContents = new ArrayList<>(); + for (ConversationMessageContent conversationMessageContent: conversationMessage.getContent()) { + conversationMessageContents.add(DaprProtos.ConversationMessageContent.newBuilder() + .setText(conversationMessageContent.getText()) + .build()); + } + + return conversationMessageContents; + } + + private List getConversationToolCalls( + ConversationMessage conversationMessage) { + List conversationToolCalls = new ArrayList<>(); + for (ConversationToolCalls conversationToolCall: conversationMessage.getToolCalls()) { + conversationToolCalls.add(DaprProtos.ConversationToolCalls.newBuilder() + .setId(conversationToolCall.getId()) + .setFunction(DaprProtos.ConversationToolCallsOfFunction.newBuilder() + .setName(conversationToolCall.getFunction().getName()) + .setArguments(conversationToolCall.getFunction().getArguments()) + .build()) + .build()); + } + + return conversationToolCalls; + } + + private void validateConversationRequestAlpha2(ConversationRequestAlpha2 conversationRequest) { + if ((conversationRequest.getName() == null) || (conversationRequest.getName().trim().isEmpty())) { + throw new IllegalArgumentException("LLM name cannot be null or empty."); + } + + if ((conversationRequest.getInputs() == null) || (conversationRequest.getInputs().isEmpty())) { + throw new IllegalArgumentException("Conversation inputs cannot be null or empty."); + } + } + private DaprMetadata buildDaprMetadata(DaprProtos.GetMetadataResponse response) throws IOException { String id = response.getId(); String runtimeVersion = response.getRuntimeVersion(); diff --git a/sdk/src/main/java/io/dapr/client/DaprPreviewClient.java b/sdk/src/main/java/io/dapr/client/DaprPreviewClient.java index 89c6eded8..3d43b9e6d 100644 --- a/sdk/src/main/java/io/dapr/client/DaprPreviewClient.java +++ b/sdk/src/main/java/io/dapr/client/DaprPreviewClient.java @@ -18,7 +18,9 @@ import io.dapr.client.domain.BulkPublishRequest; import io.dapr.client.domain.BulkPublishResponse; import io.dapr.client.domain.BulkPublishResponseFailedEntry; import io.dapr.client.domain.ConversationRequest; +import io.dapr.client.domain.ConversationRequestAlpha2; import io.dapr.client.domain.ConversationResponse; +import io.dapr.client.domain.ConversationResponseAlpha2; import io.dapr.client.domain.DeleteJobRequest; import io.dapr.client.domain.GetJobRequest; import io.dapr.client.domain.GetJobResponse; @@ -314,4 +316,12 @@ public interface DaprPreviewClient extends AutoCloseable { * @return {@link ConversationResponse}. */ public Mono converse(ConversationRequest conversationRequest); + + /* + * Converse with an LLM using Alpha2 API. + * + * @param conversationRequestAlpha2 request to be passed to the LLM with Alpha2 features. + * @return {@link ConversationResponseAlpha2}. + */ + public Mono converseAlpha2(ConversationRequestAlpha2 conversationRequestAlpha2); } diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationFunction.java b/sdk/src/main/java/io/dapr/client/domain/ConversationFunction.java new file mode 100644 index 000000000..fb741d7ff --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationFunction.java @@ -0,0 +1,66 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +import java.util.Map; + +/** + * Represents a function definition for conversation tools. + */ +public class ConversationFunction { + + private final String name; + private final String description; + private final Map parameters; + + /** + * Constructor. + * + * @param name the function name + * @param description the function description + * @param parameters the function parameters schema + */ + public ConversationFunction(String name, String description, Map parameters) { + this.name = name; + this.description = description; + this.parameters = parameters; + } + + /** + * Gets the function name. + * + * @return the function name + */ + public String getName() { + return name; + } + + /** + * Gets the function description. + * + * @return the function description + */ + public String getDescription() { + return description; + } + + /** + * Gets the function parameters schema. + * + * @return the function parameters + */ + public Map getParameters() { + return parameters; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationInputAlpha2.java b/sdk/src/main/java/io/dapr/client/domain/ConversationInputAlpha2.java new file mode 100644 index 000000000..52ee2f40a --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationInputAlpha2.java @@ -0,0 +1,63 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +import java.util.List; + +/** + * Represents an Alpha2 input for conversation with enhanced message support. + */ +public class ConversationInputAlpha2 { + + private final List messages; + private boolean scrubPii; + + /** + * Constructor. + * + * @param messages the list of conversation messages + */ + public ConversationInputAlpha2(List messages) { + this.messages = List.copyOf(messages); + } + + /** + * Gets the list of conversation messages. + * + * @return the list of messages + */ + public List getMessages() { + return messages; + } + + /** + * Checks if Personally Identifiable Information (PII) should be scrubbed before sending to the LLM. + * + * @return {@code true} if PII should be scrubbed, {@code false} otherwise. + */ + public boolean isScrubPii() { + return scrubPii; + } + + /** + * Enable obfuscation of sensitive information present in the content field. Optional + * + * @param scrubPii A boolean indicating whether to remove PII. + * @return this. + */ + public ConversationInputAlpha2 setScrubPii(boolean scrubPii) { + this.scrubPii = scrubPii; + return this; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationMessage.java b/sdk/src/main/java/io/dapr/client/domain/ConversationMessage.java new file mode 100644 index 000000000..039525c7a --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationMessage.java @@ -0,0 +1,133 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +import java.util.List; + +/** + * Represents a conversation message with role-specific content. + * Supports different message types: system, user, assistant, developer, and tool. + */ +public class ConversationMessage { + + /** + * Enum representing the different roles a message can have. + */ + public enum Role { + SYSTEM, + USER, + ASSISTANT, + DEVELOPER, + TOOL + } + + private final Role role; + private final String name; + private final List content; + private final List toolCalls; + private final String toolId; + + /** + * Constructor for creating a message with basic content. + * + * @param role the role of the message sender + * @param content the content of the message + */ + public ConversationMessage(Role role, List content) { + this(role, null, content, null, null); + } + + /** + * Constructor for creating a message with name and content. + * + * @param role the role of the message sender + * @param name the name of the participant (optional) + * @param content the content of the message + */ + public ConversationMessage(Role role, String name, List content) { + this(role, name, content, null, null); + } + + /** + * Full constructor for creating a message with all properties. + * + * @param role the role of the message sender + * @param name the name of the participant (optional) + * @param content the content of the message + * @param toolCalls tool calls for assistant messages (optional) + * @param toolId tool ID for tool messages (optional) + */ + public ConversationMessage(Role role, String name, List content, + List toolCalls, String toolId) { + this.role = role; + this.name = name; + this.content = content != null ? List.copyOf(content) : null; + this.toolCalls = toolCalls != null ? List.copyOf(toolCalls) : null; + this.toolId = toolId; + } + + /** + * Gets the role of the message sender. + * + * @return the message role + */ + public Role getRole() { + return role; + } + + /** + * Gets the name of the participant in the message. + * + * @return the participant name, or null if not specified + */ + public String getName() { + return name; + } + + /** + * Gets the content of the message. + * + * @return the message content + */ + public List getContent() { + return content; + } + + /** + * Gets the tool calls generated by the model (for assistant messages). + * + * @return the tool calls, or null if none + */ + public List getToolCalls() { + return toolCalls; + } + + /** + * Gets the tool ID (for tool messages). + * + * @return the tool ID, or null if not a tool message + */ + public String getToolId() { + return toolId; + } + + /** + * Checks if this message has tool calls. + * + * @return true if the message has tool calls, false otherwise + */ + public boolean hasToolCalls() { + return toolCalls != null && !toolCalls.isEmpty(); + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationMessageContent.java b/sdk/src/main/java/io/dapr/client/domain/ConversationMessageContent.java new file mode 100644 index 000000000..78a4ad604 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationMessageContent.java @@ -0,0 +1,40 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +/** + * Represents the content of a conversation message. + */ +public class ConversationMessageContent { + + private final String text; + + /** + * Constructor. + * + * @param text the text content of the message + */ + public ConversationMessageContent(String text) { + this.text = text; + } + + /** + * Gets the text content of the message. + * + * @return the text content + */ + public String getText() { + return text; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationRequestAlpha2.java b/sdk/src/main/java/io/dapr/client/domain/ConversationRequestAlpha2.java new file mode 100644 index 000000000..2f85fbd7d --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationRequestAlpha2.java @@ -0,0 +1,209 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +import java.util.List; +import java.util.Map; + +/** + * Represents the Alpha2 conversation configuration with enhanced features including + * tools, improved message handling, and better compatibility with OpenAI ChatCompletion API. + */ +public class ConversationRequestAlpha2 { + + private final String name; + private final List inputs; + private String contextId; + private boolean scrubPii; + private double temperature; + private List tools; + private String toolChoice; + private Map parameters; + private Map metadata; + + /** + * Constructs a ConversationRequestAlpha2 with a component name and conversation inputs. + * + * @param name The name of the Dapr conversation component. See a list of all available conversation components + * @see + * @param inputs the list of Dapr conversation inputs (Alpha2 format) + */ + public ConversationRequestAlpha2(String name, List inputs) { + this.name = name; + this.inputs = inputs; + } + + /** + * Gets the conversation component name. + * + * @return the conversation component name + */ + public String getName() { + return name; + } + + /** + * Gets the list of Dapr conversation input (Alpha2 format). + * + * @return the list of conversation input + */ + public List getInputs() { + return inputs; + } + + /** + * Gets the context identifier. + * + * @return the context identifier + */ + public String getContextId() { + return contextId; + } + + /** + * Sets the context identifier. + * + * @param contextId the context identifier to set + * @return the current instance of {@link ConversationRequestAlpha2} + */ + public ConversationRequestAlpha2 setContextId(String contextId) { + this.contextId = contextId; + return this; + } + + /** + * Checks if PII scrubbing is enabled. + * + * @return true if PII scrubbing is enabled, false otherwise + */ + public boolean isScrubPii() { + return scrubPii; + } + + /** + * Enable obfuscation of sensitive information returning from the LLM. Optional. + * + * @param scrubPii whether to enable PII scrubbing + * @return the current instance of {@link ConversationRequestAlpha2} + */ + public ConversationRequestAlpha2 setScrubPii(boolean scrubPii) { + this.scrubPii = scrubPii; + return this; + } + + /** + * Gets the temperature of the model. Used to optimize for consistency and creativity. Optional + * + * @return the temperature value + */ + public double getTemperature() { + return temperature; + } + + /** + * Sets the temperature of the model. Used to optimize for consistency and creativity. Optional + * + * @param temperature the temperature value to set + * @return the current instance of {@link ConversationRequestAlpha2} + */ + public ConversationRequestAlpha2 setTemperature(double temperature) { + this.temperature = temperature; + return this; + } + + /** + * Gets the tools available to be used by the LLM during the conversation. + * + * @return the list of tools + */ + public List getTools() { + return tools; + } + + /** + * Sets the tools available to be used by the LLM during the conversation. + * These are sent on a per request basis. + * + * @param tools the tools to set + * @return the current instance of {@link ConversationRequestAlpha2} + */ + public ConversationRequestAlpha2 setTools(List tools) { + this.tools = tools; + return this; + } + + /** + * Gets the tool choice setting which controls which (if any) tool is called by the model. + * + * @return the tool choice setting + */ + public String getToolChoice() { + return toolChoice; + } + + /** + * Sets the tool choice setting which controls which (if any) tool is called by the model. + * - "none" means the model will not call any tool and instead generates a message + * - "auto" means the model can pick between generating a message or calling one or more tools + * - "required" requires one or more functions to be called + * - Alternatively, a specific tool name may be used here + * + * @param toolChoice the tool choice setting to set + * @return the current instance of {@link ConversationRequestAlpha2} + */ + public ConversationRequestAlpha2 setToolChoice(String toolChoice) { + this.toolChoice = toolChoice; + return this; + } + + /** + * Gets the parameters for all custom fields. + * + * @return the parameters map + */ + public Map getParameters() { + return parameters; + } + + /** + * Sets the parameters for all custom fields. + * + * @param parameters the parameters to set + * @return the current instance of {@link ConversationRequestAlpha2} + */ + public ConversationRequestAlpha2 setParameters(Map parameters) { + this.parameters = parameters; + return this; + } + + /** + * Gets the metadata passing to conversation components. + * + * @return the metadata map + */ + public Map getMetadata() { + return metadata; + } + + /** + * Sets the metadata passing to conversation components. + * + * @param metadata the metadata to set + * @return the current instance of {@link ConversationRequestAlpha2} + */ + public ConversationRequestAlpha2 setMetadata(Map metadata) { + this.metadata = metadata; + return this; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationResponse.java b/sdk/src/main/java/io/dapr/client/domain/ConversationResponse.java index 805936554..6066df55f 100644 --- a/sdk/src/main/java/io/dapr/client/domain/ConversationResponse.java +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationResponse.java @@ -13,7 +13,6 @@ limitations under the License. package io.dapr.client.domain; -import java.util.Collections; import java.util.List; /** diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationResponseAlpha2.java b/sdk/src/main/java/io/dapr/client/domain/ConversationResponseAlpha2.java new file mode 100644 index 000000000..9ef6dac7a --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationResponseAlpha2.java @@ -0,0 +1,54 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +import java.util.List; + +/** + * Alpha2 response from the Dapr Conversation API with enhanced features. + */ +public class ConversationResponseAlpha2 { + + private final String contextId; + private final List outputs; + + /** + * Constructor. + * + * @param contextId context id supplied to LLM. + * @param outputs outputs from the LLM (Alpha2 format). + */ + public ConversationResponseAlpha2(String contextId, List outputs) { + this.contextId = contextId; + this.outputs = List.copyOf(outputs); + } + + /** + * The ID of an existing chat (like in ChatGPT). + * + * @return String identifier. + */ + public String getContextId() { + return this.contextId; + } + + /** + * Get list of conversation outputs (Alpha2 format). + * + * @return List{@link ConversationResultAlpha2}. + */ + public List getOutputs() { + return this.outputs; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationResultAlpha2.java b/sdk/src/main/java/io/dapr/client/domain/ConversationResultAlpha2.java new file mode 100644 index 000000000..369caeb65 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationResultAlpha2.java @@ -0,0 +1,42 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +import java.util.List; + +/** + * Alpha2 result for conversation output with enhanced choice-based structure. + */ +public class ConversationResultAlpha2 { + + private final List choices; + + /** + * Constructor. + * + * @param choices the list of conversation result choices. + */ + public ConversationResultAlpha2(List choices) { + this.choices = List.copyOf(choices); + } + + /** + * Gets the list of conversation result choices. + * + * @return the list of conversation result choices + */ + public List getChoices() { + return choices; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationResultChoices.java b/sdk/src/main/java/io/dapr/client/domain/ConversationResultChoices.java new file mode 100644 index 000000000..468286987 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationResultChoices.java @@ -0,0 +1,68 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +/** + * Represents a conversation result choice with finish reason, index, and message. + */ +public class ConversationResultChoices { + + private final String finishReason; + private final long index; + private final ConversationResultMessage message; + + /** + * Constructor. + * + * @param finishReason the reason the model stopped generating tokens + * @param index the index of the choice in the list of choices + * @param message the result message + */ + public ConversationResultChoices(String finishReason, long index, ConversationResultMessage message) { + this.finishReason = finishReason; + this.index = index; + this.message = message; + } + + /** + * Gets the reason the model stopped generating tokens. + * This will be "stop" if the model hit a natural stop point or a provided stop sequence, + * "length" if the maximum number of tokens specified in the request was reached, + * "content_filter" if content was omitted due to a flag from content filters, + * "tool_calls" if the model called a tool. + * + * @return the finish reason + */ + public String getFinishReason() { + return finishReason; + } + + /** + * Gets the index of the choice in the list of choices. + * + * @return the index + */ + public long getIndex() { + return index; + } + + /** + * Gets the result message. + * + * @return the message + */ + public ConversationResultMessage getMessage() { + return message; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationResultMessage.java b/sdk/src/main/java/io/dapr/client/domain/ConversationResultMessage.java new file mode 100644 index 000000000..bbeeebae6 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationResultMessage.java @@ -0,0 +1,72 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +import java.util.List; + +/** + * Represents a conversation result message with content and optional tool calls. + */ +public class ConversationResultMessage { + + private final String content; + private final List toolCalls; + + /** + * Constructor. + * + * @param content the contents of the message + * @param toolCalls the tool calls generated by the model (optional) + */ + public ConversationResultMessage(String content, List toolCalls) { + this.content = content; + this.toolCalls = toolCalls != null ? List.copyOf(toolCalls) : null; + } + + /** + * Constructor for message without tool calls. + * + * @param content the contents of the message + */ + public ConversationResultMessage(String content) { + this(content, null); + } + + /** + * Gets the contents of the message. + * + * @return the message content + */ + public String getContent() { + return content; + } + + /** + * Gets the tool calls generated by the model. + * + * @return the tool calls, or null if none + */ + public List getToolCalls() { + return toolCalls; + } + + /** + * Checks if the message has tool calls. + * + * @return true if there are tool calls, false otherwise + */ + public boolean hasToolCalls() { + return toolCalls != null && !toolCalls.isEmpty(); + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationToolCalls.java b/sdk/src/main/java/io/dapr/client/domain/ConversationToolCalls.java new file mode 100644 index 000000000..b1f9163ab --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationToolCalls.java @@ -0,0 +1,61 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +/** + * Represents a tool call request sent from the LLM to the client to execute. + */ +public class ConversationToolCalls { + + private final String id; + private final ConversationToolCallsFunction function; + + /** + * Constructor. + * + * @param id the unique identifier for the tool call (optional) + * @param function the function to call + */ + public ConversationToolCalls(String id, ConversationToolCallsFunction function) { + this.id = id; + this.function = function; + } + + /** + * Constructor without ID. + * + * @param function the function to call + */ + public ConversationToolCalls(ConversationToolCallsFunction function) { + this(null, function); + } + + /** + * Gets the unique identifier for the tool call. + * + * @return the tool call ID, or null if not provided + */ + public String getId() { + return id; + } + + /** + * Gets the function to call. + * + * @return the function details + */ + public ConversationToolCallsFunction getFunction() { + return function; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationToolCallsFunction.java b/sdk/src/main/java/io/dapr/client/domain/ConversationToolCallsFunction.java new file mode 100644 index 000000000..e2d33c944 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationToolCallsFunction.java @@ -0,0 +1,54 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +/** + * Represents a function call within a tool call. + */ +public class ConversationToolCallsFunction { + + private final String name; + private final String arguments; + + /** + * Constructor. + * + * @param name the name of the function to call + * @param arguments the arguments to call the function with, as generated by the model in JSON format + */ + public ConversationToolCallsFunction(String name, String arguments) { + this.name = name; + this.arguments = arguments; + } + + /** + * Gets the name of the function to call. + * + * @return the function name + */ + public String getName() { + return name; + } + + /** + * Gets the arguments to call the function with, as generated by the model in JSON format. + * Note that the model does not always generate valid JSON, and may hallucinate parameters + * not defined by your function schema. Validate the arguments in your code before calling your function. + * + * @return the function arguments in JSON format + */ + public String getArguments() { + return arguments; + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/ConversationTools.java b/sdk/src/main/java/io/dapr/client/domain/ConversationTools.java new file mode 100644 index 000000000..8f7f0cc64 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ConversationTools.java @@ -0,0 +1,52 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.client.domain; + +/** + * Represents tool definitions that can be used during conversation. + */ +public class ConversationTools { + + private final String type; + private final ConversationFunction function; + + /** + * Constructor. + * + * @param type the type of tool (e.g., "function") + * @param function the function definition + */ + public ConversationTools(String type, ConversationFunction function) { + this.type = type; + this.function = function; + } + + /** + * Gets the tool type. + * + * @return the tool type + */ + public String getType() { + return type; + } + + /** + * Gets the function definition. + * + * @return the function definition + */ + public ConversationFunction getFunction() { + return function; + } +}