Skip to content

Commit 382ae09

Browse files
feat: Add new fields to conversation api (dapr#1640)
* feat: Add new fields to conversation api Signed-off-by: Javier Aliaga <[email protected]> * chore: Modify conversation examples Signed-off-by: Javier Aliaga <[email protected]> --------- Signed-off-by: Javier Aliaga <[email protected]> Co-authored-by: salaboy <[email protected]> Signed-off-by: salaboy <[email protected]>
1 parent ab00950 commit 382ae09

17 files changed

+579
-18
lines changed

examples/src/main/java/io/dapr/examples/conversation/AssistantMessageDemo.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ public static void main(String[] args) {
108108
// Process and display the response
109109
if (response != null && response.getOutputs() != null && !response.getOutputs().isEmpty()) {
110110
ConversationResultAlpha2 result = response.getOutputs().get(0);
111+
UsageUtils.printUsage(result);
112+
111113
if (result.getChoices() != null && !result.getChoices().isEmpty()) {
112114
ConversationResultChoices choice = result.getChoices().get(0);
113-
114115
if (choice.getMessage() != null && choice.getMessage().getContent() != null) {
115116
System.out.printf("Assistant Response: %s%n", choice.getMessage().getContent());
116117
}

examples/src/main/java/io/dapr/examples/conversation/ToolsCallDemo.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ public static void main(String[] args) {
8080
// Process and display the response
8181
if (response != null && response.getOutputs() != null && !response.getOutputs().isEmpty()) {
8282
ConversationResultAlpha2 result = response.getOutputs().get(0);
83+
UsageUtils.printUsage(result);
84+
8385
if (result.getChoices() != null && !result.getChoices().isEmpty()) {
8486
ConversationResultChoices choice = result.getChoices().get(0);
8587

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2026 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.examples.conversation;
15+
16+
import io.dapr.client.domain.ConversationResultAlpha2;
17+
import io.dapr.client.domain.ConversationResultCompletionUsage;
18+
import org.springframework.util.StringUtils;
19+
20+
public class UsageUtils {
21+
static void printUsage(ConversationResultAlpha2 result) {
22+
if (!StringUtils.hasText(result.getModel())){
23+
return;
24+
}
25+
26+
System.out.printf("Conversation model : %s\n", result.getModel());
27+
var usage = result.getUsage();
28+
printUsage(usage);
29+
30+
printCompletionDetails(usage);
31+
printPromptDetails(usage);
32+
33+
}
34+
35+
private static void printUsage(ConversationResultCompletionUsage usage) {
36+
System.out.println("Token Usage Details:");
37+
System.out.printf(" Completion tokens: %d\n", usage.getCompletionTokens());
38+
System.out.printf(" Prompt tokens: %d\n", usage.getPromptTokens());
39+
System.out.printf(" Total tokens: %d\n", usage.getTotalTokens());
40+
System.out.println();
41+
}
42+
43+
private static void printPromptDetails(ConversationResultCompletionUsage usage) {
44+
var completionDetails = usage.getCompletionTokenDetails();
45+
46+
// Display completion token breakdown if available
47+
System.out.println("Prompt Token Details:");
48+
if (completionDetails.getReasoningTokens() > 0) {
49+
System.out.printf(" Reasoning tokens: %d\n", completionDetails.getReasoningTokens());
50+
}
51+
if (completionDetails.getAudioTokens() > 0) {
52+
System.out.printf(" Audio tokens: %d\n", completionDetails.getAudioTokens());
53+
}
54+
System.out.println();
55+
}
56+
57+
private static void printCompletionDetails(ConversationResultCompletionUsage usage) {
58+
// Print detailed token usage information
59+
var completionDetails = usage.getCompletionTokenDetails();
60+
61+
System.out.println("Completion Token Details:");
62+
// If audio tokens are available, display them
63+
if ( completionDetails.getAudioTokens() > 0) {
64+
System.out.printf(" Audio tokens: %d\n", completionDetails.getAudioTokens());
65+
}
66+
67+
// Display completion token breakdown if available
68+
if (completionDetails.getReasoningTokens() > 0) {
69+
System.out.printf(" Reasoning tokens: %d\n", completionDetails.getReasoningTokens());
70+
}
71+
72+
// Display completion token breakdown if available
73+
if (completionDetails.getAcceptedPredictionTokens() > 0) {
74+
System.out.printf(" Accepted prediction tokens: %d\n", completionDetails.getAcceptedPredictionTokens());
75+
}
76+
77+
// Display completion token breakdown if available
78+
if (completionDetails.getRejectedPredictionTokens() > 0) {
79+
System.out.printf(" Rejected prediction tokens: %d\n", completionDetails.getRejectedPredictionTokens());
80+
}
81+
System.out.println();
82+
}
83+
}

examples/src/main/java/io/dapr/examples/conversation/UserMessageDemo.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,9 @@
2222
import io.dapr.client.domain.ConversationResultAlpha2;
2323
import io.dapr.client.domain.ConversationResultChoices;
2424
import io.dapr.client.domain.UserMessage;
25-
import io.dapr.config.Properties;
26-
import io.dapr.config.Property;
2725
import reactor.core.publisher.Mono;
2826

2927
import java.util.List;
30-
import java.util.Map;
3128

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

46+
// Define the JSON schema for the response format
47+
String responseSchema = """
48+
{
49+
"type": "object",
50+
"properties": {
51+
"greeting": {
52+
"type": "string",
53+
"description": "A friendly greeting response"
54+
},
55+
"phone_number_detected": {
56+
"type": "boolean",
57+
"description": "Whether a phone number was detected in the input"
58+
},
59+
"detected_number": {
60+
"type": "string",
61+
"description": "The phone number that was detected, if any"
62+
}
63+
},
64+
"required": ["greeting", "phone_number_detected"],
65+
"additionalProperties": false
66+
}
67+
""";
68+
4969
// Component name is the name provided in the metadata block of the conversation.yaml file.
5070
Mono<ConversationResponseAlpha2> responseMono = client.converseAlpha2(new ConversationRequestAlpha2("echo",
51-
List.of(daprConversationInput))
52-
.setContextId("contextId")
53-
.setScrubPii(true)
54-
.setTemperature(1.1d));
71+
List.of(daprConversationInput))
72+
.setContextId("contextId")
73+
.setScrubPii(true)
74+
.setTemperature(1.1d).setResponseFormat(responseSchema));
5575

5676
ConversationResponseAlpha2 response = responseMono.block();
5777

5878
// Extract and print the conversation result
5979
if (response != null && response.getOutputs() != null && !response.getOutputs().isEmpty()) {
6080
ConversationResultAlpha2 result = response.getOutputs().get(0);
81+
UsageUtils.printUsage(result);
82+
6183
if (result.getChoices() != null && !result.getChoices().isEmpty()) {
6284
ConversationResultChoices choice = result.getChoices().get(0);
6385
if (choice.getMessage() != null && choice.getMessage().getContent() != null) {

sdk/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
<groupId>com.fasterxml.jackson.core</groupId>
3838
<artifactId>jackson-databind</artifactId>
3939
</dependency>
40+
<dependency>
41+
<groupId>com.google.protobuf</groupId>
42+
<artifactId>protobuf-java-util</artifactId>
43+
</dependency>
4044
<dependency>
4145
<groupId>io.projectreactor</groupId>
4246
<artifactId>reactor-core</artifactId>

sdk/src/main/java/io/dapr/client/DaprClientImpl.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@
4242
import io.dapr.client.domain.ConversationResponseAlpha2;
4343
import io.dapr.client.domain.ConversationResultAlpha2;
4444
import io.dapr.client.domain.ConversationResultChoices;
45+
import io.dapr.client.domain.ConversationResultCompletionUsage;
46+
import io.dapr.client.domain.ConversationResultCompletionUsageDetails;
4547
import io.dapr.client.domain.ConversationResultMessage;
48+
import io.dapr.client.domain.ConversationResultPromptUsageDetails;
4649
import io.dapr.client.domain.ConversationToolCalls;
4750
import io.dapr.client.domain.ConversationToolCallsOfFunction;
4851
import io.dapr.client.domain.ConversationTools;
@@ -1793,6 +1796,7 @@ public Mono<ConversationResponseAlpha2> converseAlpha2(ConversationRequestAlpha2
17931796
DaprAiProtos.ConversationResponseAlpha2 conversationResponse = conversationResponseMono.block();
17941797

17951798
assert conversationResponse != null;
1799+
17961800
List<ConversationResultAlpha2> results = buildConversationResults(conversationResponse.getOutputsList());
17971801
return Mono.just(new ConversationResponseAlpha2(conversationResponse.getContextId(), results));
17981802
} catch (Exception ex) {
@@ -1857,6 +1861,20 @@ private DaprAiProtos.ConversationRequestAlpha2 buildConversationRequestProto(Con
18571861

18581862
builder.addInputs(inputBuilder.build());
18591863
}
1864+
1865+
if (request.getResponseFormat() != null) {
1866+
builder.setResponseFormat(request.getResponseFormat());
1867+
}
1868+
1869+
if (request.getPromptCacheRetention() != null) {
1870+
Duration javaDuration = request.getPromptCacheRetention();
1871+
builder.setPromptCacheRetention(
1872+
com.google.protobuf.Duration.newBuilder()
1873+
.setSeconds(javaDuration.getSeconds())
1874+
.setNanos(javaDuration.getNano())
1875+
.build()
1876+
);
1877+
}
18601878

18611879
return builder.build();
18621880
}
@@ -1974,14 +1992,38 @@ private List<ConversationResultAlpha2> buildConversationResults(
19741992
for (DaprAiProtos.ConversationResultChoices protoChoice : protoResult.getChoicesList()) {
19751993
ConversationResultMessage message = buildConversationResultMessage(protoChoice);
19761994
choices.add(new ConversationResultChoices(protoChoice.getFinishReason(), protoChoice.getIndex(), message));
1977-
}
1995+
}
19781996

1979-
results.add(new ConversationResultAlpha2(choices));
1997+
results.add(new ConversationResultAlpha2(
1998+
choices,
1999+
protoResult.getModel(),
2000+
getConversationResultCompletionUsage(protoResult))
2001+
);
19802002
}
19812003

19822004
return results;
19832005
}
19842006

2007+
private static ConversationResultCompletionUsage getConversationResultCompletionUsage(
2008+
DaprAiProtos.ConversationResultAlpha2 protoResult) {
2009+
var usage = new ConversationResultCompletionUsage(
2010+
protoResult.getUsage().getCompletionTokens(),
2011+
protoResult.getUsage().getPromptTokens(),
2012+
protoResult.getUsage().getTotalTokens());
2013+
2014+
usage.setCompletionTokenDetails(new ConversationResultCompletionUsageDetails(
2015+
protoResult.getUsage().getCompletionTokensDetails().getAcceptedPredictionTokens(),
2016+
protoResult.getUsage().getCompletionTokensDetails().getAudioTokens(),
2017+
protoResult.getUsage().getCompletionTokensDetails().getReasoningTokens(),
2018+
protoResult.getUsage().getCompletionTokensDetails().getRejectedPredictionTokens()));
2019+
2020+
usage.setPromptTokenDetails(new ConversationResultPromptUsageDetails(
2021+
protoResult.getUsage().getPromptTokensDetails().getAudioTokens(),
2022+
protoResult.getUsage().getPromptTokensDetails().getCachedTokens()
2023+
));
2024+
return usage;
2025+
}
2026+
19852027
private ConversationResultMessage buildConversationResultMessage(DaprAiProtos.ConversationResultChoices protoChoice) {
19862028
if (!protoChoice.hasMessage()) {
19872029
return null;

sdk/src/main/java/io/dapr/client/DaprPreviewClient.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,15 @@
1717
import io.dapr.client.domain.BulkPublishRequest;
1818
import io.dapr.client.domain.BulkPublishResponse;
1919
import io.dapr.client.domain.BulkPublishResponseFailedEntry;
20-
import io.dapr.client.domain.CloudEvent;
2120
import io.dapr.client.domain.ConversationRequest;
2221
import io.dapr.client.domain.ConversationRequestAlpha2;
2322
import io.dapr.client.domain.ConversationResponse;
2423
import io.dapr.client.domain.ConversationResponseAlpha2;
2524
import io.dapr.client.domain.DecryptRequestAlpha1;
26-
import io.dapr.client.domain.DeleteJobRequest;
2725
import io.dapr.client.domain.EncryptRequestAlpha1;
28-
import io.dapr.client.domain.GetJobRequest;
29-
import io.dapr.client.domain.GetJobResponse;
3026
import io.dapr.client.domain.LockRequest;
3127
import io.dapr.client.domain.QueryStateRequest;
3228
import io.dapr.client.domain.QueryStateResponse;
33-
import io.dapr.client.domain.ScheduleJobRequest;
3429
import io.dapr.client.domain.UnlockRequest;
3530
import io.dapr.client.domain.UnlockResponseStatus;
3631
import io.dapr.client.domain.query.Query;

sdk/src/main/java/io/dapr/client/domain/ConversationOutput.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
package io.dapr.client.domain;
1515

16-
import java.util.Collections;
1716
import java.util.Map;
1817

1918
/**

sdk/src/main/java/io/dapr/client/domain/ConversationRequestAlpha2.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
package io.dapr.client.domain;
1515

16+
import com.google.protobuf.Struct;
17+
import io.dapr.utils.ProtobufUtils;
18+
19+
import java.time.Duration;
1620
import java.util.List;
1721
import java.util.Map;
1822

@@ -31,6 +35,8 @@ public class ConversationRequestAlpha2 {
3135
private String toolChoice;
3236
private Map<String, Object> parameters;
3337
private Map<String, String> metadata;
38+
private Struct responseFormat;
39+
private Duration promptCacheRetention;
3440

3541
/**
3642
* Constructs a ConversationRequestAlpha2 with a component name and conversation inputs.
@@ -206,4 +212,55 @@ public ConversationRequestAlpha2 setMetadata(Map<String, String> metadata) {
206212
this.metadata = metadata;
207213
return this;
208214
}
215+
216+
/**
217+
* Gets the response format in JSON-Schema format.
218+
*
219+
* @return the response format
220+
*/
221+
public Struct getResponseFormat() {
222+
return responseFormat;
223+
}
224+
225+
/**
226+
* Sets the response format in JSON-Schema format.
227+
* Structured output described using a JSON Schema object.
228+
* Use this when you want typed structured output.
229+
* Supported by Deepseek, Google AI, Hugging Face, OpenAI, and Anthropic components
230+
*
231+
* @param responseFormat the response format to set
232+
* @return the current instance of {@link ConversationRequestAlpha2}
233+
*/
234+
public ConversationRequestAlpha2 setResponseFormat(Struct responseFormat) {
235+
this.responseFormat = responseFormat;
236+
return this;
237+
}
238+
239+
public ConversationRequestAlpha2 setResponseFormat(String responseFormat) {
240+
this.responseFormat = ProtobufUtils.jsonToStruct(responseFormat);
241+
return this;
242+
}
243+
244+
/**
245+
* retention duration for the prompt cache.
246+
*
247+
* @return the prompt cache retention duration
248+
*/
249+
public Duration getPromptCacheRetention() {
250+
return promptCacheRetention;
251+
}
252+
253+
/**
254+
* Retention duration for the prompt cache.
255+
* When set, enables extended prompt caching so cached prefixes stay active longer.
256+
* With OpenAI, supports up to 24 hours.
257+
* See [OpenAI prompt caching](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
258+
*
259+
* @param promptCacheRetention the prompt cache retention duration
260+
* @return the current instance of {@link ConversationRequestAlpha2}
261+
*/
262+
public ConversationRequestAlpha2 setPromptCacheRetention(Duration promptCacheRetention) {
263+
this.promptCacheRetention = promptCacheRetention;
264+
return this;
265+
}
209266
}

sdk/src/main/java/io/dapr/client/domain/ConversationResponse.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
package io.dapr.client.domain;
1515

16-
import java.util.Collections;
1716
import java.util.List;
1817

1918
/**

0 commit comments

Comments
 (0)