Skip to content
Open
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
1 change: 1 addition & 0 deletions .pipeline/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<suppress files="[/\\]core[/\\]client[/\\]" checks=".*"/>
<suppress files="[/\\]core[/\\]model[/\\]" checks=".*"/>
<suppress files="[/\\]openai[/\\]generated[/\\]model[/\\]" checks=".*"/>
<suppress files="[/\\]rpt[/\\]generated[/\\]" checks=".*"/>
<suppress files="[/\\]orchestration[/\\]model[/\\]" checks=".*"/>
<suppress files="[/\\]grounding[/\\]client[/\\]" checks=".*"/>
<suppress files="[/\\]grounding[/\\]model[/\\]" checks=".*"/>
Expand Down
163 changes: 163 additions & 0 deletions foundation-models/sap-rpt/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.sap.ai.sdk</groupId>
<artifactId>sdk-parent</artifactId>
<version>1.16.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>com.sap.ai.sdk.foundationmodels</groupId>
<artifactId>sap-rpt</artifactId>
<name>SAP RPT Model Client</name>
<description>SAP Cloud SDK for AI is the official Software Development Kit (SDK) for SAP AI Core, SAP Generative AI Hub, and Orchestration Service. This is the client for consuming SAP RPT model for in-context learning predictions on tabular data.</description>
<url>https://github.com/SAP/ai-sdk-java?tab=readme-ov-file#documentation</url>
<organization>
<name>SAP SE</name>
<url>https://www.sap.com</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<developers>
<developer>
<name>SAP</name>
<email>cloudsdk@sap.com</email>
<organization>SAP SE</organization>
<organizationUrl>https://www.sap.com</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/SAP/ai-sdk-java.git</connection>
<developerConnection>scm:git:ssh://github.com:SAP/ai-sdk-java.git</developerConnection>
<url>https://github.com/SAP/ai-sdk-java/tree/main</url>
</scm>
<properties>
<project.rootdir>${project.basedir}/../../</project.rootdir>
<coverage.complexity>85%</coverage.complexity>
<coverage.line>86%</coverage.line>
<coverage.instruction>88%</coverage.instruction>
<coverage.branch>100%</coverage.branch>
<coverage.method>85%</coverage.method>
</properties>

<dependencies>
<dependency>
<groupId>com.sap.ai.sdk</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>cloudplatform-connectivity</artifactId>
</dependency>
<dependency>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>openapi-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- scope "provided" -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- scope "test" -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>connectivity-apache-httpclient5</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>generate</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>generate</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<configuration>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<enableOneOfAnyOfGeneration>true</enableOneOfAnyOfGeneration>
<compileScope>COMPILE</compileScope>
<deleteOutputDirectory>true</deleteOutputDirectory>
</configuration>
<executions>
<execution>
<id>sap-rpt</id>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/spec/sap-rpt-1_openapi.json</inputSpec>
<modelPackage>com.sap.ai.sdk.foundationmodels.rpt.generated.model</modelPackage>
<apiPackage>com.sap.ai.sdk.foundationmodels.rpt.generated.client</apiPackage>
<generateApis>true</generateApis>
<additionalProperties>
<pojoBuilderMethodName>create</pojoBuilderMethodName>
<enumUnknownDefaultCase>true</enumUnknownDefaultCase>
<useOneOfInterfaces>true</useOneOfInterfaces>
<useOneOfCreators>true</useOneOfCreators>
<useFloatArrays>true</useFloatArrays>
<excludePaths>/predict_parquet</excludePaths>
</additionalProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.sap.ai.sdk.foundationmodels.rpt;

import static com.sap.ai.sdk.core.JacksonConfiguration.getDefaultObjectMapper;

import com.google.common.collect.Iterables;
import com.sap.ai.sdk.core.AiCoreService;
import com.sap.ai.sdk.core.DeploymentResolutionException;
import com.sap.ai.sdk.foundationmodels.rpt.generated.client.DefaultApi;
import com.sap.ai.sdk.foundationmodels.rpt.generated.model.PredictRequestPayload;
import com.sap.ai.sdk.foundationmodels.rpt.generated.model.PredictResponsePayload;
import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
import com.sap.cloud.sdk.services.openapi.apiclient.ApiClient;
import javax.annotation.Nonnull;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Question)

Can we use the new generator without spring dependencies?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I am working on the related cloud sdk item to fix spring optionality first. Then, later make use of the same in the new module client

import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

/**
* Client for interacting with SAP RPT foundation models.
*
* @since 1.16.0
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class RptClient {
@Nonnull private final DefaultApi api;

/**
* Creates a new RptClient for the specified foundation model.
*
* @param foundationModel The foundation model to use.
* @return A new instance of RptClient.
* @throws DeploymentResolutionException If there is an error resolving the deployment.
*/
@Nonnull
public static RptClient forModel(@Nonnull final RptModel foundationModel)
throws DeploymentResolutionException {
final var destination = new AiCoreService().getInferenceDestination().forModel(foundationModel);
return forDestination(destination);
}

/**
* Creates a new RptClient for the specified destination.
*
* @param destination The destination to use.
* @return A new instance of RptClient.
*/
static RptClient forDestination(@Nonnull final Destination destination) {
// This is a workaround to override the object mapper to ignore null on serialization
val httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setHttpClient(ApacheHttpClient5Accessor.getHttpClient(destination));
final var rt = new RestTemplate();
Iterables.filter(rt.getMessageConverters(), MappingJackson2HttpMessageConverter.class)
.forEach(converter -> converter.setObjectMapper(getDefaultObjectMapper()));
rt.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory));

final var apiClient = new ApiClient(rt).setBasePath(destination.asHttp().getUri().toString());
return new RptClient(new DefaultApi(apiClient));
}

/**
* Predict targets using SAP RPT model with structured data. *
*
* <p><b>200</b> - Successful response with predictive insights.
*
* <p><b>400</b> - Bad Request - Invalid input.
*
* <p><b>422</b> - Unprocessable Content - Invalid input.
*
* <p><b>500</b> - Internal Server Error.
*
* @param requestBody The prediction request
* @return prediction response from the RPT model
*/
@Nonnull
public PredictResponsePayload tabCompletion(@Nonnull final PredictRequestPayload requestBody) {
return api.predict(requestBody);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.sap.ai.sdk.foundationmodels.rpt;

import com.sap.ai.sdk.core.AiModel;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
* Represents an SAP RPT foundation model.
*
* @param name The name of the model.
* @param version The version of the model (optional).
* @since 1.16.0
*/
public record RptModel(@Nonnull String name, @Nullable String version) implements AiModel {

/** SAP RPT 1 Small model. */
public static final RptModel SAP_RPT_1_SMALL = new RptModel("sap-rpt-1-small", null);

/** SAP RPT 1 Large model. */
public static final RptModel SAP_RPT_1_LARGE = new RptModel("sap-rpt-1-large", null);

/**
* Create a new instance of RptModel with the provided version.
*
* @param version The version of the model.
* @return The new instance of RptModel.
*/
@Nonnull
public RptModel withVersion(@Nonnull final String version) {
return new RptModel(name, version);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.sap.ai.sdk.foundationmodels.rpt.generated.client;

import com.google.common.annotations.Beta;
import com.sap.ai.sdk.foundationmodels.rpt.generated.model.PredictRequestPayload;
import com.sap.ai.sdk.foundationmodels.rpt.generated.model.PredictResponsePayload;
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
import com.sap.cloud.sdk.services.openapi.apiclient.ApiClient;
import com.sap.cloud.sdk.services.openapi.core.AbstractOpenApiService;
import com.sap.cloud.sdk.services.openapi.core.OpenApiRequestException;
import java.util.List;
import javax.annotation.Nonnull;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;

/**
* SAP-RPT-1 Tabular AI in version 0.1.0.
*
* <p>A REST API for in-context learning with the SAP-RPT-1 model.
*/
public class DefaultApi extends AbstractOpenApiService {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Minor/Question)

This is a very non-descriptive class name (which does not come from the spec itself as far as I can see). Does it make sense to rename that when generating?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the spec is currently missing a tag, so the generated api class gets a default name DefaultApi.

/**
* Instantiates this API class to invoke operations on the SAP-RPT-1 Tabular AI.
*
* @param httpDestination The destination that API should be used with
*/
public DefaultApi(@Nonnull final Destination httpDestination) {
super(httpDestination);
}

/**
* Instantiates this API class to invoke operations on the SAP-RPT-1 Tabular AI based on a given
* {@link ApiClient}.
*
* @param apiClient ApiClient to invoke the API on
*/
@Beta
public DefaultApi(@Nonnull final ApiClient apiClient) {
super(apiClient);
}

/**
* Make in-context predictions for specified target columns based on provided table data JSON
* (optionally gzip-compressed).
*
* <p>Make in-context predictions for specified target columns. Either \&quot;rows\&quot; or
* \&quot;columns\&quot; must be provided and must contain both context and query rows. You can
* optionally send gzip-compressed JSON payloads and set a \&quot;Content-Encoding: gzip\&quot;
* header.
*
* <p><b>200</b> - Successful Prediction
*
* <p><b>400</b> - Bad Request - Invalid input data
*
* <p><b>413</b> - Payload Too Large
*
* <p><b>422</b> - Validation Error
*
* <p><b>500</b> - Internal Server Error
*
* @param predictRequestPayload The value for the parameter predictRequestPayload
* @return PredictResponsePayload
* @throws OpenApiRequestException if an error occurs while attempting to invoke the API
*/
@Nonnull
public PredictResponsePayload predict(@Nonnull final PredictRequestPayload predictRequestPayload)
throws OpenApiRequestException {
final Object localVarPostBody = predictRequestPayload;

// verify the required parameter 'predictRequestPayload' is set
if (predictRequestPayload == null) {
throw new OpenApiRequestException(
"Missing the required parameter 'predictRequestPayload' when calling predict");
}

final String localVarPath = UriComponentsBuilder.fromPath("/predict").build().toUriString();

final MultiValueMap<String, String> localVarQueryParams =
new LinkedMultiValueMap<String, String>();
final HttpHeaders localVarHeaderParams = new HttpHeaders();
final MultiValueMap<String, Object> localVarFormParams =
new LinkedMultiValueMap<String, Object>();

final String[] localVarAccepts = {"application/json"};
final List<MediaType> localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
final String[] localVarContentTypes = {"application/json"};
final MediaType localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes);

final String[] localVarAuthNames = new String[] {};

final ParameterizedTypeReference<PredictResponsePayload> localVarReturnType =
new ParameterizedTypeReference<PredictResponsePayload>() {};
return apiClient.invokeAPI(
localVarPath,
HttpMethod.POST,
localVarQueryParams,
localVarPostBody,
localVarHeaderParams,
localVarFormParams,
localVarAccept,
localVarContentType,
localVarAuthNames,
localVarReturnType);
}
}
Loading