Skip to content

Commit ca2ce4d

Browse files
committed
Refactor OpenAiController
1 parent c3dce98 commit ca2ce4d

File tree

2 files changed

+279
-137
lines changed

2 files changed

+279
-137
lines changed
Lines changed: 69 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
11
package com.sap.ai.sdk.app.controllers;
22

3-
import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_35_TURBO;
4-
import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O;
5-
import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.TEXT_EMBEDDING_ADA_002;
6-
import static com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionTool.ToolType.FUNCTION;
7-
3+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
4+
import com.fasterxml.jackson.annotation.PropertyAccessor;
85
import com.fasterxml.jackson.core.JsonProcessingException;
96
import com.fasterxml.jackson.databind.ObjectMapper;
10-
import com.sap.ai.sdk.core.AiCoreService;
11-
import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient;
12-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionFunction;
13-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionOutput;
14-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionParameters;
15-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionTool;
16-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage.OpenAiChatUserMessage;
17-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage.OpenAiChatUserMessage.ImageDetailLevel;
18-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiEmbeddingOutput;
19-
import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiEmbeddingParameters;
20-
import com.sap.cloud.sdk.cloudplatform.thread.ThreadContextExecutors;
21-
import java.io.IOException;
22-
import java.util.Arrays;
23-
import java.util.List;
24-
import java.util.Map;
7+
import com.sap.ai.sdk.app.services.OpenAiService;
258
import javax.annotation.Nonnull;
269
import lombok.extern.slf4j.Slf4j;
10+
import org.springframework.beans.factory.annotation.Autowired;
2711
import org.springframework.http.MediaType;
2812
import org.springframework.http.ResponseEntity;
2913
import org.springframework.web.bind.annotation.GetMapping;
3014
import org.springframework.web.bind.annotation.PathVariable;
15+
import org.springframework.web.bind.annotation.RequestHeader;
3116
import org.springframework.web.bind.annotation.RestController;
3217
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
3318

3419
/** Endpoints for OpenAI operations */
3520
@Slf4j
3621
@RestController
22+
@SuppressWarnings("unused")
3723
public class OpenAiController {
24+
@Autowired private OpenAiService service;
25+
private final ObjectMapper mapper =
26+
new ObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
27+
3828
/**
3929
* Chat request to OpenAI
4030
*
41-
* @return the assistant message response
31+
* @return a ResponseEntity with the response content
4232
*/
4333
@GetMapping("/chatCompletion")
4434
@Nonnull
45-
OpenAiChatCompletionOutput chatCompletion() {
46-
return OpenAiClient.forModel(GPT_35_TURBO).chatCompletion("Who is the prettiest");
35+
ResponseEntity<String> chatCompletion(
36+
@RequestHeader(value = "accept", required = false) final String accept)
37+
throws JsonProcessingException {
38+
final var response = service.chatCompletion("Who is the prettiest");
39+
if (accept.equals("application/json")) {
40+
return ResponseEntity.ok()
41+
.contentType(MediaType.APPLICATION_JSON)
42+
.body(mapper.writeValueAsString(response));
43+
}
44+
return ResponseEntity.ok(response.getContent());
4745
}
4846

4947
/**
@@ -55,40 +53,8 @@ OpenAiChatCompletionOutput chatCompletion() {
5553
@GetMapping("/streamChatCompletionDeltas")
5654
@Nonnull
5755
ResponseEntity<ResponseBodyEmitter> streamChatCompletionDeltas() {
58-
final var msg = "Can you give me the first 100 numbers of the Fibonacci sequence?";
59-
final var request =
60-
new OpenAiChatCompletionParameters().addMessages(new OpenAiChatUserMessage().addText(msg));
61-
62-
final var stream = OpenAiClient.forModel(GPT_35_TURBO).streamChatCompletionDeltas(request);
63-
64-
final var emitter = new ResponseBodyEmitter();
65-
66-
final Runnable consumeStream =
67-
() -> {
68-
final var totalOutput = new OpenAiChatCompletionOutput();
69-
// try-with-resources ensures the stream is closed
70-
try (stream) {
71-
stream
72-
.peek(totalOutput::addDelta)
73-
.forEach(delta -> send(emitter, delta.getDeltaContent()));
74-
} finally {
75-
send(emitter, "\n\n-----Total Output-----\n\n" + objectToJson(totalOutput));
76-
emitter.complete();
77-
}
78-
};
79-
80-
ThreadContextExecutors.getExecutor().execute(consumeStream);
81-
82-
// TEXT_EVENT_STREAM allows the browser to display the content as it is streamed
83-
return ResponseEntity.ok().contentType(MediaType.TEXT_EVENT_STREAM).body(emitter);
84-
}
85-
86-
private static String objectToJson(@Nonnull final Object obj) {
87-
try {
88-
return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(obj);
89-
} catch (final JsonProcessingException ignored) {
90-
return "Could not parse object to JSON";
91-
}
56+
final var message = "Can you give me the first 100 numbers of the Fibonacci sequence?";
57+
return service.streamChatCompletionDeltas(message);
9258
}
9359

9460
/**
@@ -100,118 +66,84 @@ private static String objectToJson(@Nonnull final Object obj) {
10066
@GetMapping("/streamChatCompletion")
10167
@Nonnull
10268
ResponseEntity<ResponseBodyEmitter> streamChatCompletion() {
103-
final var stream =
104-
OpenAiClient.forModel(GPT_35_TURBO)
105-
.withSystemPrompt("Be a good, honest AI and answer the following question:")
106-
.streamChatCompletion(
107-
"Can you give me the first 100 numbers of the Fibonacci sequence?");
108-
109-
final var emitter = new ResponseBodyEmitter();
110-
111-
final Runnable consumeStream =
112-
() -> {
113-
try (stream) {
114-
stream.forEach(deltaMessage -> send(emitter, deltaMessage));
115-
} finally {
116-
emitter.complete();
117-
}
118-
};
119-
120-
ThreadContextExecutors.getExecutor().execute(consumeStream);
121-
122-
// TEXT_EVENT_STREAM allows the browser to display the content as it is streamed
123-
return ResponseEntity.ok().contentType(MediaType.TEXT_EVENT_STREAM).body(emitter);
124-
}
125-
126-
/**
127-
* Send a chunk to the emitter
128-
*
129-
* @param emitter The emitter to send the chunk to
130-
* @param chunk The chunk to send
131-
*/
132-
public static void send(@Nonnull final ResponseBodyEmitter emitter, @Nonnull final String chunk) {
133-
try {
134-
emitter.send(chunk);
135-
} catch (final IOException e) {
136-
log.error(Arrays.toString(e.getStackTrace()));
137-
emitter.completeWithError(e);
138-
}
69+
final var message = "Can you give me the first 100 numbers of the Fibonacci sequence?";
70+
return service.streamChatCompletion(message);
13971
}
14072

14173
/**
14274
* Chat request to OpenAI with an image
14375
*
144-
* @return the assistant message response
76+
* @return a ResponseEntity with the response content
14577
*/
14678
@GetMapping("/chatCompletionImage")
14779
@Nonnull
148-
OpenAiChatCompletionOutput chatCompletionImage() {
149-
final var request =
150-
new OpenAiChatCompletionParameters()
151-
.addMessages(
152-
new OpenAiChatUserMessage()
153-
.addText("Describe the following image.")
154-
.addImage(
155-
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png",
156-
ImageDetailLevel.HIGH));
157-
158-
return OpenAiClient.forModel(GPT_4O).chatCompletion(request);
80+
ResponseEntity<String> chatCompletionImage(
81+
@RequestHeader(value = "accept", required = false) final String accept)
82+
throws JsonProcessingException {
83+
final var response =
84+
service.chatCompletionImage(
85+
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png");
86+
if (accept.equals("application/json")) {
87+
return ResponseEntity.ok()
88+
.contentType(MediaType.APPLICATION_JSON)
89+
.body(mapper.writeValueAsString(response));
90+
}
91+
return ResponseEntity.ok(response.getContent());
15992
}
16093

16194
/**
16295
* Chat request to OpenAI with a tool.
16396
*
164-
* @return the assistant message response
97+
* @return a ResponseEntity with the response content
16598
*/
16699
@GetMapping("/chatCompletionTool")
167100
@Nonnull
168-
OpenAiChatCompletionOutput chatCompletionTools() {
169-
final var question =
170-
"A pair of rabbits is placed in a field. Each month, every pair produces one new pair, starting from the second month. How many rabbits will there be after 12 months?";
171-
final var par = Map.of("type", "object", "properties", Map.of("N", Map.of("type", "integer")));
172-
final var function =
173-
new OpenAiChatCompletionFunction()
174-
.setName("fibonacci")
175-
.setDescription("Calculate the Fibonacci number for given sequence index.")
176-
.setParameters(par);
177-
final var tool = new OpenAiChatCompletionTool().setType(FUNCTION).setFunction(function);
178-
final var request =
179-
new OpenAiChatCompletionParameters()
180-
.addMessages(new OpenAiChatUserMessage().addText(question))
181-
.setTools(List.of(tool))
182-
.setToolChoiceFunction("fibonacci");
183-
184-
return OpenAiClient.forModel(GPT_35_TURBO).chatCompletion(request);
101+
ResponseEntity<String> chatCompletionTools(
102+
@RequestHeader(value = "accept", required = false) final String accept)
103+
throws JsonProcessingException {
104+
final var response =
105+
service.chatCompletionTools("Calculate the Fibonacci number for given sequence index.");
106+
if (accept.equals("application/json")) {
107+
return ResponseEntity.ok()
108+
.contentType(MediaType.APPLICATION_JSON)
109+
.body(mapper.writeValueAsString(response));
110+
}
111+
return ResponseEntity.ok(response.getContent());
185112
}
186113

187114
/**
188115
* Get the embedding of a text
189116
*
190-
* @return the embedding response
117+
* @return a ResponseEntity with the response content
191118
*/
192119
@GetMapping("/embedding")
193120
@Nonnull
194-
OpenAiEmbeddingOutput embedding() {
195-
final var request = new OpenAiEmbeddingParameters().setInput("Hello World");
196-
197-
return OpenAiClient.forModel(TEXT_EMBEDDING_ADA_002).embedding(request);
121+
ResponseEntity<String> embedding() throws JsonProcessingException {
122+
final var response = service.embedding("Hello world");
123+
return ResponseEntity.ok()
124+
.contentType(MediaType.APPLICATION_JSON)
125+
.body(mapper.writeValueAsString(response));
198126
}
199127

200128
/**
201129
* Chat request to OpenAI filtering by resource group
202130
*
203131
* @param resourceGroup The resource group to use
204-
* @return the assistant message response
132+
* @return a ResponseEntity with the response content
205133
*/
206134
@GetMapping("/chatCompletion/{resourceGroup}")
207135
@Nonnull
208-
public static OpenAiChatCompletionOutput chatCompletionWithResource(
209-
@Nonnull @PathVariable("resourceGroup") final String resourceGroup) {
210-
211-
final var destination =
212-
new AiCoreService().getInferenceDestination(resourceGroup).forModel(GPT_4O);
213-
214-
return OpenAiClient.withCustomDestination(destination)
215-
.chatCompletion("Where is the nearest coffee shop?");
136+
ResponseEntity<String> chatCompletionWithResource(
137+
@RequestHeader(value = "accept", required = false) final String accept,
138+
@Nonnull @PathVariable("resourceGroup") final String resourceGroup)
139+
throws JsonProcessingException {
140+
final var response =
141+
service.chatCompletionWithResource(resourceGroup, "Where is the nearest coffee shop?");
142+
if (accept.equals("application/json")) {
143+
return ResponseEntity.ok()
144+
.contentType(MediaType.APPLICATION_JSON)
145+
.body(mapper.writeValueAsString(response));
146+
}
147+
return ResponseEntity.ok(response.getContent());
216148
}
217149
}

0 commit comments

Comments
 (0)