11package 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 ;
85import com .fasterxml .jackson .core .JsonProcessingException ;
96import 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 ;
7+ import com .sap .ai .sdk .app .services .OpenAiService ;
138import 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 ;
209import com .sap .cloud .sdk .cloudplatform .thread .ThreadContextExecutors ;
2110import java .io .IOException ;
2211import java .util .Arrays ;
23- import java .util .List ;
24- import java .util .Map ;
2512import javax .annotation .Nonnull ;
2613import lombok .extern .slf4j .Slf4j ;
14+ import org .springframework .beans .factory .annotation .Autowired ;
2715import org .springframework .http .MediaType ;
2816import org .springframework .http .ResponseEntity ;
2917import org .springframework .web .bind .annotation .GetMapping ;
3018import org .springframework .web .bind .annotation .PathVariable ;
19+ import org .springframework .web .bind .annotation .RequestHeader ;
3120import org .springframework .web .bind .annotation .RestController ;
3221import org .springframework .web .servlet .mvc .method .annotation .ResponseBodyEmitter ;
3322
3423/** Endpoints for OpenAI operations */
3524@ Slf4j
3625@ RestController
26+ @ SuppressWarnings ("unused" )
3727public class OpenAiController {
28+ @ Autowired private OpenAiService service ;
29+ private final ObjectMapper mapper =
30+ new ObjectMapper ().setVisibility (PropertyAccessor .FIELD , JsonAutoDetect .Visibility .ANY );
31+
3832 /**
3933 * Chat request to OpenAI
4034 *
41- * @return the assistant message response
35+ * @return a ResponseEntity with the response content
4236 */
4337 @ GetMapping ("/chatCompletion" )
4438 @ Nonnull
45- OpenAiChatCompletionOutput chatCompletion () {
46- return OpenAiClient .forModel (GPT_35_TURBO ).chatCompletion ("Who is the prettiest" );
39+ ResponseEntity <String > chatCompletion (
40+ @ RequestHeader (value = "accept" , required = false ) final String accept )
41+ throws JsonProcessingException {
42+ final var response = service .chatCompletion ("Who is the prettiest" );
43+ if ("application/json" .equals (accept )) {
44+ return ResponseEntity .ok ()
45+ .contentType (MediaType .APPLICATION_JSON )
46+ .body (mapper .writeValueAsString (response ));
47+ }
48+ return ResponseEntity .ok (response .getContent ());
4749 }
4850
4951 /**
@@ -55,14 +57,9 @@ OpenAiChatCompletionOutput chatCompletion() {
5557 @ GetMapping ("/streamChatCompletionDeltas" )
5658 @ Nonnull
5759 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-
60+ final var message = "Can you give me the first 100 numbers of the Fibonacci sequence?" ;
61+ final var stream = service .streamChatCompletionDeltas (message );
6462 final var emitter = new ResponseBodyEmitter ();
65-
6663 final Runnable consumeStream =
6764 () -> {
6865 final var totalOutput = new OpenAiChatCompletionOutput ();
@@ -76,21 +73,12 @@ ResponseEntity<ResponseBodyEmitter> streamChatCompletionDeltas() {
7673 emitter .complete ();
7774 }
7875 };
79-
8076 ThreadContextExecutors .getExecutor ().execute (consumeStream );
8177
8278 // TEXT_EVENT_STREAM allows the browser to display the content as it is streamed
8379 return ResponseEntity .ok ().contentType (MediaType .TEXT_EVENT_STREAM ).body (emitter );
8480 }
8581
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- }
92- }
93-
9482 /**
9583 * Asynchronous stream of an OpenAI chat request
9684 *
@@ -100,12 +88,8 @@ private static String objectToJson(@Nonnull final Object obj) {
10088 @ GetMapping ("/streamChatCompletion" )
10189 @ Nonnull
10290 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-
91+ final var message = "Can you give me the first 100 numbers of the Fibonacci sequence?" ;
92+ final var stream = service .streamChatCompletion (message );
10993 final var emitter = new ResponseBodyEmitter ();
11094
11195 final Runnable consumeStream =
@@ -116,7 +100,6 @@ ResponseEntity<ResponseBodyEmitter> streamChatCompletion() {
116100 emitter .complete ();
117101 }
118102 };
119-
120103 ThreadContextExecutors .getExecutor ().execute (consumeStream );
121104
122105 // TEXT_EVENT_STREAM allows the browser to display the content as it is streamed
@@ -138,80 +121,94 @@ public static void send(@Nonnull final ResponseBodyEmitter emitter, @Nonnull fin
138121 }
139122 }
140123
124+ /**
125+ * Convert an object to JSON
126+ *
127+ * @param obj The object to convert
128+ * @return The JSON representation of the object
129+ */
130+ private static String objectToJson (@ Nonnull final Object obj ) {
131+ try {
132+ return new ObjectMapper ().writerWithDefaultPrettyPrinter ().writeValueAsString (obj );
133+ } catch (final JsonProcessingException ignored ) {
134+ return "Could not parse object to JSON" ;
135+ }
136+ }
137+
141138 /**
142139 * Chat request to OpenAI with an image
143140 *
144- * @return the assistant message response
141+ * @return a ResponseEntity with the response content
145142 */
146143 @ GetMapping ("/chatCompletionImage" )
147144 @ 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 );
145+ ResponseEntity <String > chatCompletionImage (
146+ @ RequestHeader (value = "accept" , required = false ) final String accept )
147+ throws JsonProcessingException {
148+ final var response =
149+ service .chatCompletionImage (
150+ "https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png" );
151+ if ("application/json" .equals (accept )) {
152+ return ResponseEntity .ok ()
153+ .contentType (MediaType .APPLICATION_JSON )
154+ .body (mapper .writeValueAsString (response ));
155+ }
156+ return ResponseEntity .ok (response .getContent ());
159157 }
160158
161159 /**
162160 * Chat request to OpenAI with a tool.
163161 *
164- * @return the assistant message response
162+ * @return a ResponseEntity with the response content
165163 */
166164 @ GetMapping ("/chatCompletionTool" )
167165 @ 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 );
166+ ResponseEntity <String > chatCompletionTools (
167+ @ RequestHeader (value = "accept" , required = false ) final String accept )
168+ throws JsonProcessingException {
169+ final var response =
170+ service .chatCompletionTools ("Calculate the Fibonacci number for given sequence index." );
171+ if ("application/json" .equals (accept )) {
172+ return ResponseEntity .ok ()
173+ .contentType (MediaType .APPLICATION_JSON )
174+ .body (mapper .writeValueAsString (response ));
175+ }
176+ return ResponseEntity .ok (response .getContent ());
185177 }
186178
187179 /**
188180 * Get the embedding of a text
189181 *
190- * @return the embedding response
182+ * @return a ResponseEntity with the response content
191183 */
192184 @ GetMapping ("/embedding" )
193185 @ Nonnull
194- OpenAiEmbeddingOutput embedding () {
195- final var request = new OpenAiEmbeddingParameters ().setInput ("Hello World" );
196-
197- return OpenAiClient .forModel (TEXT_EMBEDDING_ADA_002 ).embedding (request );
186+ ResponseEntity <String > embedding () throws JsonProcessingException {
187+ final var response = service .embedding ("Hello world" );
188+ return ResponseEntity .ok ()
189+ .contentType (MediaType .APPLICATION_JSON )
190+ .body (mapper .writeValueAsString (response ));
198191 }
199192
200193 /**
201194 * Chat request to OpenAI filtering by resource group
202195 *
203196 * @param resourceGroup The resource group to use
204- * @return the assistant message response
197+ * @return a ResponseEntity with the response content
205198 */
206199 @ GetMapping ("/chatCompletion/{resourceGroup}" )
207200 @ 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?" );
201+ ResponseEntity <String > chatCompletionWithResource (
202+ @ RequestHeader (value = "accept" , required = false ) final String accept ,
203+ @ Nonnull @ PathVariable ("resourceGroup" ) final String resourceGroup )
204+ throws JsonProcessingException {
205+ final var response =
206+ service .chatCompletionWithResource (resourceGroup , "Where is the nearest coffee shop?" );
207+ if ("application/json" .equals (accept )) {
208+ return ResponseEntity .ok ()
209+ .contentType (MediaType .APPLICATION_JSON )
210+ .body (mapper .writeValueAsString (response ));
211+ }
212+ return ResponseEntity .ok (response .getContent ());
216213 }
217214}
0 commit comments