|
4 | 4 | import com.fasterxml.jackson.annotation.JsonInclude; |
5 | 5 | import com.fasterxml.jackson.annotation.PropertyAccessor; |
6 | 6 | import com.fasterxml.jackson.core.JsonProcessingException; |
| 7 | +import com.fasterxml.jackson.databind.JsonNode; |
7 | 8 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 9 | +import com.fasterxml.jackson.databind.node.ObjectNode; |
8 | 10 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; |
| 11 | +import com.google.common.annotations.Beta; |
9 | 12 | import com.sap.ai.sdk.core.AiCoreDeployment; |
10 | 13 | import com.sap.ai.sdk.core.AiCoreService; |
11 | 14 | import com.sap.ai.sdk.orchestration.client.model.CompletionPostRequest; |
|
30 | 33 | import org.apache.hc.client5.http.classic.methods.HttpPost; |
31 | 34 | import org.apache.hc.core5.http.ContentType; |
32 | 35 | import org.apache.hc.core5.http.io.entity.StringEntity; |
33 | | -import org.apache.hc.core5.http.message.BasicClassicHttpRequest; |
34 | 36 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; |
35 | 37 |
|
36 | 38 | /** Client to execute requests to the orchestration service. */ |
@@ -132,26 +134,75 @@ public OrchestrationChatResponse chatCompletion( |
132 | 134 | @Nonnull |
133 | 135 | public CompletionPostResponse executeRequest(@Nonnull final CompletionPostRequest request) |
134 | 136 | throws OrchestrationClientException { |
135 | | - final BasicClassicHttpRequest postRequest = new HttpPost("/completion"); |
| 137 | + final String jsonRequest; |
136 | 138 | try { |
137 | | - val json = JACKSON.writeValueAsString(request); |
138 | | - log.debug("Serialized request into JSON payload: {}", json); |
139 | | - postRequest.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); |
| 139 | + jsonRequest = JACKSON.writeValueAsString(request); |
| 140 | + log.debug("Serialized request into JSON payload: {}", jsonRequest); |
140 | 141 | } catch (final JsonProcessingException e) { |
141 | 142 | throw new OrchestrationClientException("Failed to serialize request parameters", e); |
142 | 143 | } |
143 | 144 |
|
144 | | - return executeRequest(postRequest); |
| 145 | + return executeRequest(jsonRequest); |
| 146 | + } |
| 147 | + |
| 148 | + /** |
| 149 | + * Perform a request to the orchestration service using a module configuration provided as JSON |
| 150 | + * string. This can be useful when building a configuration in the AI Launchpad UI and exporting |
| 151 | + * it as JSON. Furthermore, this allows for using features that are not yet supported natively by |
| 152 | + * the API. |
| 153 | + * |
| 154 | + * <p><b>NOTE:</b> This method does not support streaming. |
| 155 | + * |
| 156 | + * @param prompt The input parameters and optionally message history to use for prompt execution. |
| 157 | + * @param moduleConfig The module configuration in JSON format. |
| 158 | + * @return The completion response. |
| 159 | + * @throws OrchestrationClientException If the request fails. |
| 160 | + */ |
| 161 | + @Beta |
| 162 | + @Nonnull |
| 163 | + public OrchestrationChatResponse executeRequestFromJsonModuleConfig( |
| 164 | + @Nonnull final OrchestrationPrompt prompt, @Nonnull final String moduleConfig) |
| 165 | + throws OrchestrationClientException { |
| 166 | + if (!prompt.getMessages().isEmpty()) { |
| 167 | + throw new IllegalArgumentException( |
| 168 | + "Prompt must not contain any messages when using a JSON module configuration, as the template is already defined in the JSON."); |
| 169 | + } |
| 170 | + |
| 171 | + final var request = |
| 172 | + new CompletionPostRequest() |
| 173 | + .messagesHistory(prompt.getMessagesHistory()) |
| 174 | + .inputParams(prompt.getTemplateParameters()); |
| 175 | + |
| 176 | + final ObjectNode requestJson = JACKSON.valueToTree(request); |
| 177 | + final JsonNode moduleConfigJson; |
| 178 | + try { |
| 179 | + moduleConfigJson = JACKSON.readTree(moduleConfig); |
| 180 | + } catch (JsonProcessingException e) { |
| 181 | + throw new IllegalArgumentException( |
| 182 | + "The provided module configuration is not valid JSON: " + moduleConfig, e); |
| 183 | + } |
| 184 | + requestJson.set("orchestration_config", moduleConfigJson); |
| 185 | + |
| 186 | + final String body; |
| 187 | + try { |
| 188 | + body = JACKSON.writeValueAsString(requestJson); |
| 189 | + } catch (JsonProcessingException e) { |
| 190 | + throw new OrchestrationClientException("Failed to serialize request to JSON", e); |
| 191 | + } |
| 192 | + return new OrchestrationChatResponse(executeRequest(body)); |
145 | 193 | } |
146 | 194 |
|
147 | 195 | @Nonnull |
148 | | - CompletionPostResponse executeRequest(@Nonnull final BasicClassicHttpRequest request) { |
| 196 | + CompletionPostResponse executeRequest(@Nonnull final String request) { |
| 197 | + val postRequest = new HttpPost("/completion"); |
| 198 | + postRequest.setEntity(new StringEntity(request, ContentType.APPLICATION_JSON)); |
| 199 | + |
149 | 200 | try { |
150 | 201 | val destination = deployment.get().destination(); |
151 | 202 | log.debug("Using destination {} to connect to orchestration service", destination); |
152 | 203 | val client = ApacheHttpClient5Accessor.getHttpClient(destination); |
153 | 204 | return client.execute( |
154 | | - request, new OrchestrationResponseHandler<>(CompletionPostResponse.class)); |
| 205 | + postRequest, new OrchestrationResponseHandler<>(CompletionPostResponse.class)); |
155 | 206 | } catch (NoSuchElementException |
156 | 207 | | DestinationAccessException |
157 | 208 | | DestinationNotFoundException |
|
0 commit comments