Skip to content

Commit af8ecda

Browse files
committed
Finishing up all major items
- client method for high level objects - Only support 'float' encoding format - Add javadoc - create convenience embedding response class - Add unit/integration tests - Add json payloads for req and res - Add e2e
1 parent 20d72c3 commit af8ecda

File tree

12 files changed

+480
-86
lines changed

12 files changed

+480
-86
lines changed

orchestration/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@
203203
<useOneOfInterfaces>true</useOneOfInterfaces>
204204
<removeEnumValuePrefix>true</removeEnumValuePrefix>
205205
<useOneOfCreators>true</useOneOfCreators>
206+
<useFloatArrays>true</useFloatArrays>
206207
</additionalProperties>
207208
</configuration>
208209
</execution>

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,31 @@ public Stream<OrchestrationChatCompletionDelta> streamChatCompletionDeltas(
218218
}
219219

220220
/**
221-
* Generate embeddings for the given request.
221+
* Generate embeddings for a {@code OrchestrationEmbeddingRequest} request.
222222
*
223223
* @param request the request containing the input text and other parameters.
224224
* @return the response containing the embeddings.
225225
* @throws OrchestrationClientException if the request fails
226-
* @since 1.9.0
226+
* @since 1.11.0
227+
*/
228+
@Nonnull
229+
public OrchestrationEmbeddingResponse embed(@Nonnull final OrchestrationEmbeddingRequest request)
230+
throws OrchestrationClientException {
231+
final var response = embed(request.createEmbeddingsPostRequest());
232+
return new OrchestrationEmbeddingResponse(response);
233+
}
234+
235+
/**
236+
* Generates embeddings using the low-level API request.
237+
*
238+
* <p>This method provides direct access to the underlying API for advanced use cases. For most
239+
* scenarios, prefer {@link #embed(OrchestrationEmbeddingRequest)}.
240+
*
241+
* @param request the low-level API request
242+
* @return the low level response object
243+
* @throws OrchestrationClientException if the request fails
244+
* @since 1.11.0
245+
* @see #embed(OrchestrationEmbeddingRequest)
227246
*/
228247
@Nonnull
229248
public EmbeddingsPostResponse embed(@Nonnull final EmbeddingsPostRequest request)

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

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.sap.ai.sdk.core.AiModel;
55
import com.sap.ai.sdk.orchestration.model.EmbeddingsModelDetails;
66
import com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams;
7+
import com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams.EncodingFormatEnum;
78
import javax.annotation.Nonnull;
89
import javax.annotation.Nullable;
910
import lombok.AccessLevel;
@@ -12,46 +13,61 @@
1213
import lombok.With;
1314
import lombok.experimental.Accessors;
1415

15-
// ideally this is a record but exposes all args constructor which we want to avoid. Is it worth a
16-
// value class?
17-
// Currently, model list follow SAP Notes as the source of truth. But deprecated models are not
18-
// listed there.
19-
// Can we reuse existing enum from generated class?
16+
/**
17+
* Configuration for embedding models in the Orchestration service.
18+
*
19+
* @since 1.11.0
20+
*/
2021
@Beta
2122
@With
2223
@Value
2324
@Accessors(fluent = true)
2425
@AllArgsConstructor(access = AccessLevel.PRIVATE)
2526
public class OrchestrationEmbeddingModel implements AiModel {
27+
/** The name of the embedding model. */
2628
@Nonnull String name;
29+
30+
/** The version of the model, defaults to latest if not specified. */
2731
@Nullable String version;
32+
33+
/** The number of dimensions for the output embeddings. */
2834
@Nullable Integer dimensions;
29-
@Nullable Boolean normalize;
30-
@Nullable EmbeddingsModelParams.EncodingFormatEnum encodingFormat;
3135

32-
public OrchestrationEmbeddingModel(@Nonnull final String name) {
33-
this(name, null, null, null, null);
34-
}
36+
/** Whether to normalize the embedding vectors. */
37+
@Nullable Boolean normalize;
3538

39+
/** Azure OpenAI Text Embedding 3 Small model */
3640
public static final OrchestrationEmbeddingModel TEXT_EMBEDDING_3_SMALL =
3741
new OrchestrationEmbeddingModel("text-embedding-3-small");
3842

43+
/** Azure OpenAI Text Embedding 3 Large model */
3944
public static final OrchestrationEmbeddingModel TEXT_EMBEDDING_3_LARGE =
4045
new OrchestrationEmbeddingModel("text-embedding-3-large");
4146

47+
/** Amazon Titan Embed Text model */
4248
public static final OrchestrationEmbeddingModel AMAZON_TITAN_EMBED_TEXT =
4349
new OrchestrationEmbeddingModel("amazon.titan-embed-text");
4450

51+
/** NVIDIA LLaMA 3.2 7B NV EmbedQA model */
4552
public static final OrchestrationEmbeddingModel NVIDIA_LLAMA_32_NV_EMBEDQA_1B =
4653
new OrchestrationEmbeddingModel("nvidia--llama-3.2-nv-embedqa-1b");
4754

48-
EmbeddingsModelDetails createEmbeddingsModelDetails() {
55+
/**
56+
* Creates a new embedding model configuration with the specified name.
57+
*
58+
* @param name the model name
59+
*/
60+
public OrchestrationEmbeddingModel(@Nonnull final String name) {
61+
this(name, null, null, null);
62+
}
4963

64+
@Nonnull
65+
EmbeddingsModelDetails createEmbeddingsModelDetails() {
5066
final var params =
5167
EmbeddingsModelParams.create()
5268
.dimensions(dimensions)
5369
.normalize(normalize)
54-
.encodingFormat(encodingFormat);
70+
.encodingFormat(EncodingFormatEnum.FLOAT);
5571
return EmbeddingsModelDetails.create().name(name).version(version).params(params);
5672
}
5773
}
Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.sap.ai.sdk.orchestration;
22

3-
import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.DOCUMENT;
4-
import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.QUERY;
5-
import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.TEXT;
3+
import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.DOCUMENT;
4+
import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.QUERY;
5+
import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.TEXT;
66
import static lombok.AccessLevel.PRIVATE;
77

88
import com.google.common.annotations.Beta;
@@ -18,33 +18,86 @@
1818
import javax.annotation.Nonnull;
1919
import javax.annotation.Nullable;
2020
import lombok.AllArgsConstructor;
21+
import lombok.Getter;
22+
import lombok.RequiredArgsConstructor;
2123
import lombok.Value;
2224
import lombok.With;
2325
import lombok.experimental.Tolerate;
2426

25-
// Do we need staged input builder here?
26-
// Do we need an enum for tokenType?
27+
/**
28+
* Represents a request for generating embeddings through the SAP AI Core Orchestration service.
29+
*
30+
* @since 1.11.0
31+
*/
2732
@Beta
2833
@Value
2934
@AllArgsConstructor(access = PRIVATE)
3035
public class OrchestrationEmbeddingRequest {
3136

37+
/** The embedding model to use for generating vector representations. */
3238
@Nonnull OrchestrationEmbeddingModel model;
39+
40+
/** The list of text inputs to be converted into embeddings. */
3341
@Nonnull List<String> tokens;
3442

43+
/** Optional masking providers for data privacy and security. */
3544
@With(value = PRIVATE)
3645
@Nullable
3746
List<MaskingProvider> masking;
3847

48+
/** Optional token type classification to optimize embedding generation. */
3949
@With(value = PRIVATE)
4050
@Nullable
41-
EmbeddingsInput.TypeEnum tokenType;
51+
TokenType tokenType;
52+
53+
/**
54+
* Create an embedding request using fluent API starting with model selection.
55+
*
56+
* <pre>{@code
57+
* OrchestrationEmbeddingRequest.forModel(myModel).forInputs("text to embed");
58+
* }</pre>
59+
*
60+
* @param model the embedding model to use
61+
* @return a step for specifying inputs
62+
*/
63+
@Nonnull
64+
public static InputStep forModel(@Nonnull final OrchestrationEmbeddingModel model) {
65+
return tokens -> new OrchestrationEmbeddingRequest(model, List.copyOf(tokens), null, null);
66+
}
4267

43-
public static OrchestrationEmbeddingRequest create(
44-
OrchestrationEmbeddingModel model, List<String> tokens) {
45-
return new OrchestrationEmbeddingRequest(model, tokens, null, null);
68+
/** Builder step for specifying text inputs to embed. */
69+
@FunctionalInterface
70+
public interface InputStep {
71+
72+
/**
73+
* Specifies text inputs to be embedded.
74+
*
75+
* @param tokens the text strings to embed
76+
* @return a new embedding request instance
77+
*/
78+
@Nonnull
79+
OrchestrationEmbeddingRequest forInputs(@Nonnull final List<String> tokens);
80+
81+
/**
82+
* Specifies multiple text inputs using variable arguments.
83+
*
84+
* @param tokens one or more strings to embed
85+
* @return a new embedding request instance
86+
*/
87+
@Nonnull
88+
default OrchestrationEmbeddingRequest forInputs(@Nonnull final String... tokens) {
89+
return forInputs(List.of(tokens));
90+
}
4691
}
4792

93+
/**
94+
* Adds data masking providers to enable detection and masking of sensitive information.
95+
*
96+
* @param maskingProvider the primary masking provider
97+
* @param maskingProviders additional masking providers
98+
* @return a new request instance with the specified masking providers
99+
* @see MaskingProvider
100+
*/
48101
@Tolerate
49102
@Nonnull
50103
public OrchestrationEmbeddingRequest withMasking(
@@ -53,36 +106,71 @@ public OrchestrationEmbeddingRequest withMasking(
53106
return withMasking(Lists.asList(maskingProvider, maskingProviders));
54107
}
55108

109+
/**
110+
* Configures this request to optimize embeddings for document content.
111+
*
112+
* @return a new request instance configured for document embedding
113+
*/
56114
@Nonnull
57115
public OrchestrationEmbeddingRequest asDocument() {
58116
return withTokenType(DOCUMENT);
59117
}
60118

119+
/**
120+
* Configures this request to optimize embeddings for general text content.
121+
*
122+
* @return a new request instance configured for text embedding
123+
*/
61124
@Nonnull
62125
public OrchestrationEmbeddingRequest asText() {
63126
return withTokenType(TEXT);
64127
}
65128

129+
/**
130+
* Configures this request to optimize embeddings for query content.
131+
*
132+
* @return a new request instance configured for query embedding
133+
*/
66134
@Nonnull
67135
public OrchestrationEmbeddingRequest asQuery() {
68136
return withTokenType(QUERY);
69137
}
70138

139+
@Nonnull
71140
EmbeddingsPostRequest createEmbeddingsPostRequest() {
72141

73-
final var input =
74-
EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens)).type(tokenType);
142+
final var input = EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens));
75143
final var embeddingsModelConfig =
76-
EmbeddingsModelConfig.create().model(this.model.createEmbeddingsModelDetails());
144+
EmbeddingsModelConfig.create().model(model.createEmbeddingsModelDetails());
77145
final var modules =
78146
EmbeddingsOrchestrationConfig.create()
79147
.modules(EmbeddingsModuleConfigs.create().embeddings(embeddingsModelConfig));
80148

149+
if (tokenType != null) {
150+
input.setType(EmbeddingsInput.TypeEnum.fromValue(tokenType.getValue()));
151+
}
81152
if (masking != null) {
82-
final var dpiConfigs = this.masking.stream().map(MaskingProvider::createConfig).toList();
153+
final var dpiConfigs = masking.stream().map(MaskingProvider::createConfig).toList();
83154
modules.getModules().setMasking(MaskingModuleConfigProviders.create().providers(dpiConfigs));
84155
}
85-
86156
return EmbeddingsPostRequest.create().config(modules).input(input);
87157
}
158+
159+
/**
160+
* Token type classification for optimizing embedding generation.
161+
*
162+
* <p>Token types may influence how the embedding model processes and represents the input text.
163+
*/
164+
@Getter
165+
@RequiredArgsConstructor
166+
public enum TokenType {
167+
/** For document content. */
168+
DOCUMENT("document"),
169+
/** For general text (default). */
170+
TEXT("text"),
171+
/** For search queries. */
172+
QUERY("query");
173+
174+
private final String value;
175+
}
88176
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import static lombok.AccessLevel.PACKAGE;
4+
5+
import com.google.common.annotations.Beta;
6+
import com.sap.ai.sdk.orchestration.model.Embedding;
7+
import com.sap.ai.sdk.orchestration.model.EmbeddingsPostResponse;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import javax.annotation.Nonnull;
11+
import lombok.AllArgsConstructor;
12+
import lombok.Value;
13+
14+
/**
15+
* Response wrapper for orchestration embedding operations.
16+
*
17+
* <p>Wraps {@link EmbeddingsPostResponse} and provides convenient access to embedding vectors.
18+
*/
19+
@Beta
20+
@Value
21+
@AllArgsConstructor(access = PACKAGE)
22+
public class OrchestrationEmbeddingResponse {
23+
24+
/** The original embedding response from the orchestration API. */
25+
@Nonnull EmbeddingsPostResponse originalResponse;
26+
27+
/**
28+
* Extracts embedding vectors as float arrays.
29+
*
30+
* @return list of embedding vectors, never {@code null}
31+
*/
32+
@Nonnull
33+
public List<float[]> getEmbeddingVectors() {
34+
final var embeddings = new ArrayList<float[]>();
35+
for (final var container : originalResponse.getFinalResult().getData()) {
36+
final var bigDecimals = (Embedding.InnerBigDecimals) container.getEmbedding();
37+
final var values = bigDecimals.values();
38+
final float[] arr = new float[values.size()];
39+
for (int i = 0; i < values.size(); i++) {
40+
arr[i] = values.get(i).floatValue();
41+
}
42+
embeddings.add(arr);
43+
}
44+
return embeddings;
45+
}
46+
}

0 commit comments

Comments
 (0)