Skip to content

Commit 5b4cdf6

Browse files
authored
[Orchestration] OSS Generator Improvements (#158)
* Simplifications and cleanup * Remove parsing for streaming
1 parent 67840db commit 5b4cdf6

29 files changed

+262
-2616
lines changed

orchestration/.openapi-generator-ignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,3 @@ settings.gradle
1616
src/main/AndroidManifest.xml
1717
api/
1818
.openapi-generator/
19-
20-
21-
src/main/java/com/sap/ai/sdk/orchestration/client/model/LLMModuleResult.java
22-
src/main/java/com/sap/ai/sdk/orchestration/client/model/ModuleResultsOutputUnmaskingInner.java

orchestration/pom.xml

Lines changed: 87 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@
8585
<groupId>org.slf4j</groupId>
8686
<artifactId>slf4j-api</artifactId>
8787
</dependency>
88+
<dependency>
89+
<groupId>javax.annotation</groupId>
90+
<artifactId>javax.annotation-api</artifactId>
91+
</dependency>
92+
<dependency>
93+
<groupId>com.google.guava</groupId>
94+
<artifactId>guava</artifactId>
95+
</dependency>
8896

8997
<!-- TODO: only needed for JsonObjectMapperBuilder, maybe we can use Jackson natively to avoid this dependency -->
9098
<dependency>
@@ -118,73 +126,88 @@
118126
<artifactId>mockito-core</artifactId>
119127
<scope>test</scope>
120128
</dependency>
121-
122-
<!-- Additional dependencies for OSS code generator resttemplate -->
123-
<dependency>
124-
<groupId>org.openapitools</groupId>
125-
<artifactId>jackson-databind-nullable</artifactId>
126-
</dependency>
127-
<dependency>
128-
<groupId>javax.annotation</groupId>
129-
<artifactId>javax.annotation-api</artifactId>
130-
</dependency>
131-
<dependency>
132-
<groupId>com.google.guava</groupId>
133-
<artifactId>guava</artifactId>
134-
</dependency>
135129
</dependencies>
136130

137-
<build>
138-
<plugins>
139-
<plugin>
140-
<groupId>org.openapitools</groupId>
141-
<artifactId>openapi-generator-maven-plugin</artifactId>
142-
<executions>
143-
<execution>
144-
<goals>
145-
<goal>generate</goal>
146-
</goals>
147-
<phase>generate-sources</phase>
148-
<configuration>
149-
<!-- Specify the input OpenAPI spec file -->
150-
<inputSpec>${project.basedir}/src/main/resources/spec/orchestration.yaml</inputSpec>
151-
<output>${project.basedir}</output>
152-
<!-- Specify the generator to use, e.g., java, spring, kotlin, etc. -->
153-
<generatorName>java</generatorName>
154-
<!-- Specify the package names for models, APIs, and invokers -->
155-
<modelPackage>com.sap.ai.sdk.orchestration.client.model</modelPackage>
156-
<apiPackage>com.sap.ai.sdk.orchestration.client.api</apiPackage>
157-
<invokerPackage>com.sap.ai.sdk.orchestration.client.invoker</invokerPackage>
131+
<profiles>
132+
<profile>
133+
<id>generate</id>
134+
<activation>
135+
<activeByDefault>false</activeByDefault>
136+
<property>
137+
<name>generate</name>
138+
</property>
139+
</activation>
140+
<build>
141+
<plugins>
142+
<plugin>
143+
<groupId>org.openapitools</groupId>
144+
<artifactId>openapi-generator-maven-plugin</artifactId>
145+
<executions>
146+
<execution>
147+
<goals>
148+
<goal>generate</goal>
149+
</goals>
150+
<phase>generate-sources</phase>
151+
<configuration>
152+
<!-- Specify the input OpenAPI spec file -->
153+
<inputSpec>${project.basedir}/src/main/resources/spec/orchestration.yaml</inputSpec>
154+
<output>${project.basedir}</output>
155+
<!-- Specify the generator to use, e.g., java, spring, kotlin, etc. -->
156+
<generatorName>java</generatorName>
157+
<!-- Specify the package names for models, APIs, and invokers -->
158+
<modelPackage>com.sap.ai.sdk.orchestration.client.model</modelPackage>
159+
<apiPackage>com.sap.ai.sdk.orchestration.client.api</apiPackage>
160+
<invokerPackage>com.sap.ai.sdk.orchestration.client.invoker</invokerPackage>
158161

159-
<!-- Global properties level; can be unpacked with 'generate' prefix-->
160-
<globalProperties>
161-
<apiDocs>false</apiDocs>
162-
<modelDocs>false</modelDocs>
163-
<modelTests>false</modelTests>
164-
<apiTests>false</apiTests>
165-
<minimalUpdate>true</minimalUpdate>
166-
</globalProperties>
162+
<!-- Global properties level; can be unpacked with 'generate' prefix-->
163+
<globalProperties>
164+
<apiDocs>false</apiDocs>
165+
<modelDocs>false</modelDocs>
166+
<modelTests>false</modelTests>
167+
<apiTests>false</apiTests>
168+
<minimalUpdate>true</minimalUpdate>
169+
</globalProperties>
167170

168-
<!-- Generator Specific properties level; some can be unpacked-->
169-
<configOptions>
170-
<generateBuilders>true</generateBuilders>
171-
<failOnUnknownProperties>false</failOnUnknownProperties>
172-
<hideGenerationTimestamp>true</hideGenerationTimestamp>
173-
<disallowAdditionalPropertiesIfNotPresent>false</disallowAdditionalPropertiesIfNotPresent>
174-
<enumUnknownDefaultCase>true</enumUnknownDefaultCase>
175-
<useBeanValidation>false</useBeanValidation>
176-
<useOneOfInterfaces>true</useOneOfInterfaces>
177-
<additionalModelTypeAnnotations>@com.google.common.annotations.Beta</additionalModelTypeAnnotations>
178-
</configOptions>
179-
<generateModels>true</generateModels>
180-
<generateSupportingFiles>false</generateSupportingFiles>
181-
<generateApis>false</generateApis>
182-
<library>resttemplate</library>
183-
<!--<configHelp>true</configHelp>-->
171+
<!-- Generator Specific properties level; some can be unpacked-->
172+
<configOptions>
173+
<generateBuilders>true</generateBuilders>
174+
<failOnUnknownProperties>false</failOnUnknownProperties>
175+
<hideGenerationTimestamp>true</hideGenerationTimestamp>
176+
<disallowAdditionalPropertiesIfNotPresent>false</disallowAdditionalPropertiesIfNotPresent>
177+
<enumUnknownDefaultCase>true</enumUnknownDefaultCase>
178+
<useBeanValidation>false</useBeanValidation>
179+
<useOneOfInterfaces>true</useOneOfInterfaces>
180+
<additionalModelTypeAnnotations>@com.google.common.annotations.Beta</additionalModelTypeAnnotations>
181+
</configOptions>
182+
<generateModels>true</generateModels>
183+
<generateSupportingFiles>false</generateSupportingFiles>
184+
<generateApis>false</generateApis>
185+
<library>resttemplate</library>
186+
<!--<configHelp>true</configHelp>-->
187+
</configuration>
188+
</execution>
189+
</executions>
190+
</plugin>
191+
<plugin>
192+
<artifactId>maven-clean-plugin</artifactId>
193+
<configuration>
194+
<filesets>
195+
<fileset>
196+
<directory>${project.basedir}/src/main/java/com/sap/ai/sdk/orchestration/client</directory>
197+
<includes>
198+
<include>**/*</include>
199+
</includes>
200+
</fileset>
201+
</filesets>
184202
</configuration>
185-
</execution>
186-
</executions>
187-
</plugin>
188-
</plugins>
189-
</build>
203+
<executions>
204+
<execution>
205+
<id>delete-orchestration-generated-client</id>
206+
</execution>
207+
</executions>
208+
</plugin>
209+
</plugins>
210+
</build>
211+
</profile>
212+
</profiles>
190213
</project>

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ static TemplatingModuleConfig toTemplateModuleConfig(
4343
* In this case, the request will fail, since the templating module will try to resolve the parameter.
4444
* To be fixed with https://github.tools.sap/AI/llm-orchestration/issues/662
4545
*/
46-
val messages = Option.of(template).map(t -> ((Template) t).getTemplate()).getOrElse(List::of);
46+
val messages =
47+
Option.of(template)
48+
.filter(Template.class::isInstance)
49+
.map(Template.class::cast)
50+
.map(Template::getTemplate)
51+
.getOrElse(List::of);
4752
val messagesWithPrompt = new ArrayList<>(messages);
4853
messagesWithPrompt.addAll(prompt.getMessages());
4954
if (messagesWithPrompt.isEmpty()) {
Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,45 @@
11
package com.sap.ai.sdk.orchestration;
22

33
import com.fasterxml.jackson.core.JsonParser;
4-
import com.fasterxml.jackson.databind.*;
4+
import com.fasterxml.jackson.databind.DeserializationContext;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
56
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
67
import com.sap.ai.sdk.orchestration.client.model.LLMModuleResult;
7-
import com.sap.ai.sdk.orchestration.client.model.LLMModuleResultStreaming;
88
import com.sap.ai.sdk.orchestration.client.model.LLMModuleResultSynchronous;
99
import java.io.IOException;
10+
import java.io.Serial;
1011
import javax.annotation.Nonnull;
12+
import lombok.val;
1113

1214
/**
1315
* A deserializer for {@link LLMModuleResult} that determines the concrete implementation based on
1416
* the structure of the JSON object.
1517
*/
16-
public class LLMModuleResultDeserializer extends StdDeserializer<LLMModuleResult> {
18+
class LLMModuleResultDeserializer extends StdDeserializer<LLMModuleResult> {
19+
// checkstyle requires a serialVersionUid since StdDeserializer implements Serializable
20+
@Serial private static final long serialVersionUID = 1L;
1721

18-
public LLMModuleResultDeserializer() {
22+
/** Default constructor. */
23+
LLMModuleResultDeserializer() {
1924
super(LLMModuleResult.class);
2025
}
2126

2227
/**
23-
* Deserialize the JSON object into one of the subtypes of the base type.
24-
*
25-
* <ul>
26-
* <li>If elements of "choices" array contains "delta", deserialize into {@link
27-
* LLMModuleResultStreaming}.
28-
* <li>Otherwise, deserialize into {@link LLMModuleResultSynchronous}.
29-
* </ul>
28+
* Always deserialize into {@link LLMModuleResultSynchronous} since streaming isn't supported yet.
3029
*
3130
* @param parser The JSON parser.
3231
* @param context The deserialization context.
3332
* @return The deserialized object.
3433
* @throws IOException If an I/O error occurs.
3534
*/
35+
@Nonnull
3636
@Override
37-
public LLMModuleResult deserialize(JsonParser parser, @Nonnull DeserializationContext context)
37+
public LLMModuleResult deserialize(
38+
@Nonnull final JsonParser parser, @Nonnull final DeserializationContext context)
3839
throws IOException {
40+
val mapper = (ObjectMapper) parser.getCodec();
41+
val rootNode = mapper.readTree(parser);
3942

40-
// Check if the target type is a concrete class
41-
JavaType targetType = context.getContextualType();
42-
if (targetType != null && !LLMModuleResult.class.equals(targetType.getRawClass())) {
43-
return delegateToDefaultDeserializer(parser, context, targetType);
44-
}
45-
46-
// Custom deserialization logic for LLMModuleResult interface
47-
var mapper = (ObjectMapper) parser.getCodec();
48-
var rootNode = mapper.readTree(parser);
49-
Class<? extends LLMModuleResult> concreteClass = LLMModuleResultSynchronous.class;
50-
51-
// Inspect the "choices" field
52-
var choicesNode = rootNode.get("choices");
53-
if (choicesNode != null && choicesNode.isArray()) {
54-
var firstChoice = (JsonNode) choicesNode.get(0);
55-
if (firstChoice != null && firstChoice.has("delta")) {
56-
concreteClass = LLMModuleResultStreaming.class;
57-
}
58-
}
59-
60-
// Create a new parser for the root node
61-
var rootParser = rootNode.traverse(mapper);
62-
rootParser.nextToken(); // Advance to the first token
63-
64-
// Use the default deserializer for the concrete class
65-
return delegateToDefaultDeserializer(rootParser, context, mapper.constructType(concreteClass));
66-
}
67-
68-
/**
69-
* Delegate deserialization to the default deserializer for the given concrete type.
70-
*
71-
* @param parser The JSON parser.
72-
* @param context The deserialization context.
73-
* @param concreteType The concrete type to deserialize into.
74-
* @return The deserialized object.
75-
* @throws IOException If an I/O error occurs.
76-
*/
77-
private LLMModuleResult delegateToDefaultDeserializer(
78-
JsonParser parser, DeserializationContext context, JavaType concreteType) throws IOException {
79-
var defaultDeserializer = context.findRootValueDeserializer(concreteType);
80-
return (LLMModuleResult) defaultDeserializer.deserialize(parser, context);
43+
return mapper.readValue(rootNode.toString(), LLMModuleResultSynchronous.class);
8144
}
8245
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111
import com.sap.ai.sdk.orchestration.client.model.CompletionPostRequest;
1212
import com.sap.ai.sdk.orchestration.client.model.CompletionPostResponse;
1313
import com.sap.ai.sdk.orchestration.client.model.FilterConfig;
14-
import com.sap.ai.sdk.orchestration.client.model.GroundingModuleConfigConfigFiltersInner;
1514
import com.sap.ai.sdk.orchestration.client.model.LLMModuleResult;
1615
import com.sap.ai.sdk.orchestration.client.model.MaskingProviderConfig;
1716
import com.sap.ai.sdk.orchestration.client.model.ModuleConfigs;
17+
import com.sap.ai.sdk.orchestration.client.model.ModuleResultsOutputUnmaskingInner;
1818
import com.sap.ai.sdk.orchestration.client.model.OrchestrationConfig;
19-
import com.sap.ai.sdk.orchestration.client.model.TemplateRefTemplateRef;
2019
import com.sap.ai.sdk.orchestration.client.model.TemplatingModuleConfig;
2120
import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor;
2221
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
@@ -47,10 +46,10 @@ public class OrchestrationClient {
4746
.visibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
4847
.serializationInclusion(JsonInclude.Include.NON_NULL)
4948
.deserializerByType(LLMModuleResult.class, new LLMModuleResultDeserializer())
49+
.mixIn(LLMModuleResult.class, NoTypeInfoMixin.class)
50+
.mixIn(ModuleResultsOutputUnmaskingInner.class, NoTypeInfoMixin.class)
5051
.mixIn(FilterConfig.class, NoTypeInfoMixin.class)
51-
.mixIn(GroundingModuleConfigConfigFiltersInner.class, NoTypeInfoMixin.class)
5252
.mixIn(MaskingProviderConfig.class, NoTypeInfoMixin.class)
53-
.mixIn(TemplateRefTemplateRef.class, NoTypeInfoMixin.class)
5453
.mixIn(TemplatingModuleConfig.class, NoTypeInfoMixin.class)
5554
.build();
5655
}

0 commit comments

Comments
 (0)