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 ;
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 ;
258import javax .annotation .Nonnull ;
269import lombok .extern .slf4j .Slf4j ;
10+ import org .springframework .beans .factory .annotation .Autowired ;
2711import org .springframework .http .MediaType ;
2812import org .springframework .http .ResponseEntity ;
2913import org .springframework .web .bind .annotation .GetMapping ;
3014import org .springframework .web .bind .annotation .PathVariable ;
15+ import org .springframework .web .bind .annotation .RequestHeader ;
3116import org .springframework .web .bind .annotation .RestController ;
3217import org .springframework .web .servlet .mvc .method .annotation .ResponseBodyEmitter ;
3318
3419/** Endpoints for OpenAI operations */
3520@ Slf4j
3621@ RestController
22+ @ SuppressWarnings ("unused" )
3723public 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