Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ public static void main(String[] args) {
// Process and display the response
if (response != null && response.getOutputs() != null && !response.getOutputs().isEmpty()) {
ConversationResultAlpha2 result = response.getOutputs().get(0);
UsageUtils.printUsage(result);

if (result.getChoices() != null && !result.getChoices().isEmpty()) {
ConversationResultChoices choice = result.getChoices().get(0);

if (choice.getMessage() != null && choice.getMessage().getContent() != null) {
System.out.printf("Assistant Response: %s%n", choice.getMessage().getContent());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public static void main(String[] args) {
// Process and display the response
if (response != null && response.getOutputs() != null && !response.getOutputs().isEmpty()) {
ConversationResultAlpha2 result = response.getOutputs().get(0);
UsageUtils.printUsage(result);

if (result.getChoices() != null && !result.getChoices().isEmpty()) {
ConversationResultChoices choice = result.getChoices().get(0);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2026 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.examples.conversation;

import io.dapr.client.domain.ConversationResultAlpha2;
import io.dapr.client.domain.ConversationResultCompletionUsage;
import org.springframework.util.StringUtils;

public class UsageUtils {
static void printUsage(ConversationResultAlpha2 result) {
if (!StringUtils.hasText(result.getModel())){
return;
}

System.out.printf("Conversation model : %s\n", result.getModel());
var usage = result.getUsage();
printUsage(usage);

printCompletionDetails(usage);
printPromptDetails(usage);

}

private static void printUsage(ConversationResultCompletionUsage usage) {
System.out.println("Token Usage Details:");
System.out.printf(" Completion tokens: %d\n", usage.getCompletionTokens());
System.out.printf(" Prompt tokens: %d\n", usage.getPromptTokens());
System.out.printf(" Total tokens: %d\n", usage.getTotalTokens());
System.out.println();
}

private static void printPromptDetails(ConversationResultCompletionUsage usage) {
var completionDetails = usage.getCompletionTokenDetails();

// Display completion token breakdown if available
System.out.println("Prompt Token Details:");
if (completionDetails.getReasoningTokens() > 0) {
System.out.printf(" Reasoning tokens: %d\n", completionDetails.getReasoningTokens());
}
if (completionDetails.getAudioTokens() > 0) {
System.out.printf(" Audio tokens: %d\n", completionDetails.getAudioTokens());
}
System.out.println();
}

private static void printCompletionDetails(ConversationResultCompletionUsage usage) {
// Print detailed token usage information
var completionDetails = usage.getCompletionTokenDetails();

System.out.println("Completion Token Details:");
// If audio tokens are available, display them
if ( completionDetails.getAudioTokens() > 0) {
System.out.printf(" Audio tokens: %d\n", completionDetails.getAudioTokens());
}

// Display completion token breakdown if available
if (completionDetails.getReasoningTokens() > 0) {
System.out.printf(" Reasoning tokens: %d\n", completionDetails.getReasoningTokens());
}

// Display completion token breakdown if available
if (completionDetails.getAcceptedPredictionTokens() > 0) {
System.out.printf(" Accepted prediction tokens: %d\n", completionDetails.getAcceptedPredictionTokens());
}

// Display completion token breakdown if available
if (completionDetails.getRejectedPredictionTokens() > 0) {
System.out.printf(" Rejected prediction tokens: %d\n", completionDetails.getRejectedPredictionTokens());
}
System.out.println();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@
import io.dapr.client.domain.ConversationResultAlpha2;
import io.dapr.client.domain.ConversationResultChoices;
import io.dapr.client.domain.UserMessage;
import io.dapr.config.Properties;
import io.dapr.config.Property;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

public class UserMessageDemo {
/**
Expand All @@ -46,18 +43,43 @@ public static void main(String[] args) {
// Create conversation input with the user message
ConversationInputAlpha2 daprConversationInput = new ConversationInputAlpha2(List.of(userMessage));

// Define the JSON schema for the response format
String responseSchema = """
{
"type": "object",
"properties": {
"greeting": {
"type": "string",
"description": "A friendly greeting response"
},
"phone_number_detected": {
"type": "boolean",
"description": "Whether a phone number was detected in the input"
},
"detected_number": {
"type": "string",
"description": "The phone number that was detected, if any"
}
},
"required": ["greeting", "phone_number_detected"],
"additionalProperties": false
}
""";

// Component name is the name provided in the metadata block of the conversation.yaml file.
Mono<ConversationResponseAlpha2> responseMono = client.converseAlpha2(new ConversationRequestAlpha2("echo",
List.of(daprConversationInput))
.setContextId("contextId")
.setScrubPii(true)
.setTemperature(1.1d));
List.of(daprConversationInput))
.setContextId("contextId")
.setScrubPii(true)
.setTemperature(1.1d).setResponseFormat(responseSchema));

ConversationResponseAlpha2 response = responseMono.block();

// Extract and print the conversation result
if (response != null && response.getOutputs() != null && !response.getOutputs().isEmpty()) {
ConversationResultAlpha2 result = response.getOutputs().get(0);
UsageUtils.printUsage(result);

if (result.getChoices() != null && !result.getChoices().isEmpty()) {
ConversationResultChoices choice = result.getChoices().get(0);
if (choice.getMessage() != null && choice.getMessage().getContent() != null) {
Expand Down
4 changes: 4 additions & 0 deletions sdk/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
Expand Down
46 changes: 44 additions & 2 deletions sdk/src/main/java/io/dapr/client/DaprClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@
import io.dapr.client.domain.ConversationResponseAlpha2;
import io.dapr.client.domain.ConversationResultAlpha2;
import io.dapr.client.domain.ConversationResultChoices;
import io.dapr.client.domain.ConversationResultCompletionUsage;
import io.dapr.client.domain.ConversationResultCompletionUsageDetails;
import io.dapr.client.domain.ConversationResultMessage;
import io.dapr.client.domain.ConversationResultPromptUsageDetails;
import io.dapr.client.domain.ConversationToolCalls;
import io.dapr.client.domain.ConversationToolCallsOfFunction;
import io.dapr.client.domain.ConversationTools;
Expand Down Expand Up @@ -1793,6 +1796,7 @@ public Mono<ConversationResponseAlpha2> converseAlpha2(ConversationRequestAlpha2
DaprAiProtos.ConversationResponseAlpha2 conversationResponse = conversationResponseMono.block();

assert conversationResponse != null;

List<ConversationResultAlpha2> results = buildConversationResults(conversationResponse.getOutputsList());
return Mono.just(new ConversationResponseAlpha2(conversationResponse.getContextId(), results));
} catch (Exception ex) {
Expand Down Expand Up @@ -1857,6 +1861,20 @@ private DaprAiProtos.ConversationRequestAlpha2 buildConversationRequestProto(Con

builder.addInputs(inputBuilder.build());
}

if (request.getResponseFormat() != null) {
builder.setResponseFormat(request.getResponseFormat());
}

if (request.getPromptCacheRetention() != null) {
Duration javaDuration = request.getPromptCacheRetention();
builder.setPromptCacheRetention(
com.google.protobuf.Duration.newBuilder()
.setSeconds(javaDuration.getSeconds())
.setNanos(javaDuration.getNano())
.build()
);
}

return builder.build();
}
Expand Down Expand Up @@ -1974,14 +1992,38 @@ private List<ConversationResultAlpha2> buildConversationResults(
for (DaprAiProtos.ConversationResultChoices protoChoice : protoResult.getChoicesList()) {
ConversationResultMessage message = buildConversationResultMessage(protoChoice);
choices.add(new ConversationResultChoices(protoChoice.getFinishReason(), protoChoice.getIndex(), message));
}
}

results.add(new ConversationResultAlpha2(choices));
results.add(new ConversationResultAlpha2(
choices,
protoResult.getModel(),
getConversationResultCompletionUsage(protoResult))
);
}

return results;
}

private static ConversationResultCompletionUsage getConversationResultCompletionUsage(
DaprAiProtos.ConversationResultAlpha2 protoResult) {
var usage = new ConversationResultCompletionUsage(
protoResult.getUsage().getCompletionTokens(),
protoResult.getUsage().getPromptTokens(),
protoResult.getUsage().getTotalTokens());

usage.setCompletionTokenDetails(new ConversationResultCompletionUsageDetails(
protoResult.getUsage().getCompletionTokensDetails().getAcceptedPredictionTokens(),
protoResult.getUsage().getCompletionTokensDetails().getAudioTokens(),
protoResult.getUsage().getCompletionTokensDetails().getReasoningTokens(),
protoResult.getUsage().getCompletionTokensDetails().getRejectedPredictionTokens()));

usage.setPromptTokenDetails(new ConversationResultPromptUsageDetails(
protoResult.getUsage().getPromptTokensDetails().getAudioTokens(),
protoResult.getUsage().getPromptTokensDetails().getCachedTokens()
));
return usage;
}

private ConversationResultMessage buildConversationResultMessage(DaprAiProtos.ConversationResultChoices protoChoice) {
if (!protoChoice.hasMessage()) {
return null;
Expand Down
5 changes: 0 additions & 5 deletions sdk/src/main/java/io/dapr/client/DaprPreviewClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,15 @@
import io.dapr.client.domain.BulkPublishRequest;
import io.dapr.client.domain.BulkPublishResponse;
import io.dapr.client.domain.BulkPublishResponseFailedEntry;
import io.dapr.client.domain.CloudEvent;
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.DecryptRequestAlpha1;
import io.dapr.client.domain.DeleteJobRequest;
import io.dapr.client.domain.EncryptRequestAlpha1;
import io.dapr.client.domain.GetJobRequest;
import io.dapr.client.domain.GetJobResponse;
import io.dapr.client.domain.LockRequest;
import io.dapr.client.domain.QueryStateRequest;
import io.dapr.client.domain.QueryStateResponse;
import io.dapr.client.domain.ScheduleJobRequest;
import io.dapr.client.domain.UnlockRequest;
import io.dapr.client.domain.UnlockResponseStatus;
import io.dapr.client.domain.query.Query;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

package io.dapr.client.domain;

import java.util.Collections;
import java.util.Map;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

package io.dapr.client.domain;

import com.google.protobuf.Struct;
import io.dapr.utils.ProtobufUtils;

import java.time.Duration;
import java.util.List;
import java.util.Map;

Expand All @@ -31,6 +35,8 @@ public class ConversationRequestAlpha2 {
private String toolChoice;
private Map<String, Object> parameters;
private Map<String, String> metadata;
private Struct responseFormat;
private Duration promptCacheRetention;

/**
* Constructs a ConversationRequestAlpha2 with a component name and conversation inputs.
Expand Down Expand Up @@ -206,4 +212,55 @@ public ConversationRequestAlpha2 setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
return this;
}

/**
* Gets the response format in JSON-Schema format.
*
* @return the response format
*/
public Struct getResponseFormat() {
return responseFormat;
}

/**
* Sets the response format in JSON-Schema format.
* Structured output described using a JSON Schema object.
* Use this when you want typed structured output.
* Supported by Deepseek, Google AI, Hugging Face, OpenAI, and Anthropic components
*
* @param responseFormat the response format to set
* @return the current instance of {@link ConversationRequestAlpha2}
*/
public ConversationRequestAlpha2 setResponseFormat(Struct responseFormat) {
this.responseFormat = responseFormat;
return this;
}

public ConversationRequestAlpha2 setResponseFormat(String responseFormat) {
this.responseFormat = ProtobufUtils.jsonToStruct(responseFormat);
return this;
}

/**
* retention duration for the prompt cache.
*
* @return the prompt cache retention duration
*/
public Duration getPromptCacheRetention() {
return promptCacheRetention;
}

/**
* Retention duration for the prompt cache.
* When set, enables extended prompt caching so cached prefixes stay active longer.
* With OpenAI, supports up to 24 hours.
* See [OpenAI prompt caching](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
*
* @param promptCacheRetention the prompt cache retention duration
* @return the current instance of {@link ConversationRequestAlpha2}
*/
public ConversationRequestAlpha2 setPromptCacheRetention(Duration promptCacheRetention) {
this.promptCacheRetention = promptCacheRetention;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

package io.dapr.client.domain;

import java.util.Collections;
import java.util.List;

/**
Expand Down
Loading
Loading