Skip to content

Commit eb1f19d

Browse files
feat: [Orchestration] Added support for transforming a JSON output into an entity
1 parent 02fe7f8 commit eb1f19d

File tree

5 files changed

+42
-18
lines changed

5 files changed

+42
-18
lines changed

docs/release_notes.md

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

1414
### ✨ New Functionality
1515

16-
-
16+
- [Orchestration] Added support for [transforming a JSON output into an entity](https://sap.github.io/ai-sdk/docs/java/orchestration/chat-completion#json_schema)
1717

1818
### 📈 Improvements
1919

orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationChatResponse.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import static lombok.AccessLevel.PACKAGE;
44

5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
57
import com.sap.ai.sdk.orchestration.model.AssistantChatMessage;
68
import com.sap.ai.sdk.orchestration.model.ChatMessage;
79
import com.sap.ai.sdk.orchestration.model.ChatMessageContent;
@@ -107,4 +109,24 @@ public LLMChoice getChoice() {
107109
.getChoices()
108110
.get(0);
109111
}
112+
113+
/**
114+
* Transform a JSON response into an entity of the given type.
115+
*
116+
* <p>This is possible on a request with a {@link OrchestrationTemplate#withJsonSchemaResponse}
117+
* configured into {@link OrchestrationModuleConfig#withTemplateConfig}.
118+
*
119+
* @param type the class type to deserialize the JSON content into.
120+
* @return the deserialized entity of type T.
121+
* @param <T> the type of the entity to deserialize to.
122+
*/
123+
@Nonnull
124+
public <T> T entity(@Nonnull final Class<T> type) {
125+
try {
126+
return new ObjectMapper().readValue(getContent(), type);
127+
} catch (JsonProcessingException e) {
128+
throw new OrchestrationClientException(
129+
"Failed to deserialize the JSON content: " + e.getMessage(), e);
130+
}
131+
}
110132
}

sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,7 @@ Object groundingHelpSapCom(
239239
@Nonnull
240240
Object responseFormatJsonSchema(
241241
@RequestParam(value = "format", required = false) final String format) {
242-
final var response = service.responseFormatJsonSchema("apple");
243-
if ("json".equals(format)) {
244-
return response;
245-
}
246-
return response.getContent();
242+
return service.responseFormatJsonSchema("apple");
247243
}
248244

249245
@GetMapping("/responseFormatJsonObject")

sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -382,23 +382,28 @@ public OrchestrationChatResponse groundingHelpSapCom(@Nonnull final String userM
382382
return client.chatCompletion(prompt, configWithGrounding);
383383
}
384384

385+
/**
386+
* A simple record to demonstrate the response format feature of the orchestration service.
387+
*
388+
* @param translation the translated text
389+
* @param language the language of the translation
390+
*/
391+
public record Translation(
392+
@JsonProperty(required = true) String translation,
393+
@JsonProperty(required = true) String language) {}
394+
385395
/**
386396
* Chat request to OpenAI through the Orchestration service using response format with JSON
387397
* schema.
388398
*
399+
* @param word the word to translate
400+
* @return the assistant response object
389401
* @link <a
390402
* href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/structured-output">SAP
391403
* AI Core: Orchestration - Structured Output</a>
392-
* @param word the word to translate
393-
* @return the assistant response object
394404
*/
395405
@Nonnull
396-
public OrchestrationChatResponse responseFormatJsonSchema(@Nonnull final String word) {
397-
// Example class
398-
record Translation(
399-
@JsonProperty(required = true) String translation,
400-
@JsonProperty(required = true) String language) {}
401-
406+
public Translation responseFormatJsonSchema(@Nonnull final String word) {
402407
val schema =
403408
ResponseJsonSchema.fromType(Translation.class)
404409
.withDescription("Output schema for language translation.")
@@ -411,7 +416,7 @@ record Translation(
411416
Message.user("Whats '%s' in German?".formatted(word)),
412417
Message.system("You are a language translator."));
413418

414-
return client.chatCompletion(prompt, configWithResponseSchema);
419+
return client.chatCompletion(prompt, configWithResponseSchema).entity(Translation.class);
415420
}
416421

417422
/**

sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static org.assertj.core.api.Assertions.assertThatThrownBy;
88

99
import com.sap.ai.sdk.app.services.OrchestrationService;
10+
import com.sap.ai.sdk.app.services.OrchestrationService.Translation;
1011
import com.sap.ai.sdk.orchestration.AzureContentFilter;
1112
import com.sap.ai.sdk.orchestration.AzureFilterThreshold;
1213
import com.sap.ai.sdk.orchestration.DpiMasking;
@@ -317,9 +318,9 @@ void testMultiStringInput() {
317318

318319
@Test
319320
void testResponseFormatJsonSchema() {
320-
val result = service.responseFormatJsonSchema("apple").getOriginalResponse();
321-
val choices = ((LLMModuleResultSynchronous) result.getOrchestrationResult()).getChoices();
322-
assertThat(choices.get(0).getMessage().getContent()).isNotEmpty();
321+
Translation translation = service.responseFormatJsonSchema("apple");
322+
assertThat(translation.translation()).isNotEmpty();
323+
assertThat(translation.language()).isNotEmpty();
323324
}
324325

325326
@Test

0 commit comments

Comments
 (0)