33import static com .sap .ai .sdk .foundationmodels .openai .OpenAiModel .GPT_4O ;
44import static com .sap .ai .sdk .foundationmodels .openai .OpenAiModel .GPT_4O_MINI ;
55import static com .sap .ai .sdk .foundationmodels .openai .OpenAiModel .TEXT_EMBEDDING_3_SMALL ;
6- import static com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatCompletionTool .ToolType .FUNCTION ;
76
8- import com .fasterxml .jackson .core .JsonProcessingException ;
9- import com .fasterxml .jackson .core .type .TypeReference ;
10- import com .fasterxml .jackson .databind .JsonMappingException ;
11- import com .fasterxml .jackson .databind .ObjectMapper ;
12- import com .fasterxml .jackson .module .jsonSchema .JsonSchemaGenerator ;
137import com .sap .ai .sdk .core .AiCoreService ;
8+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiChatCompletionDelta ;
9+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiChatCompletionRequest ;
10+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiChatCompletionResponse ;
1411import com .sap .ai .sdk .foundationmodels .openai .OpenAiClient ;
15- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatCompletionDelta ;
16- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatCompletionFunction ;
17- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatCompletionOutput ;
18- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatCompletionParameters ;
19- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatCompletionTool ;
20- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatMessage ;
21- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiChatToolCall ;
22- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiEmbeddingOutput ;
23- import com .sap .ai .sdk .foundationmodels .openai .model .OpenAiEmbeddingParameters ;
12+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiEmbeddingRequest ;
13+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiEmbeddingResponse ;
14+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiImageItem ;
15+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiMessage ;
16+ import com .sap .ai .sdk .foundationmodels .openai .OpenAiTool ;
2417import java .util .ArrayList ;
2518import java .util .List ;
26- import java .util .Map ;
2719import java .util .stream .Stream ;
2820import javax .annotation .Nonnull ;
2921import lombok .extern .slf4j .Slf4j ;
22+ import lombok .val ;
3023import org .springframework .stereotype .Service ;
3124
32- /** Service class for OpenAI service */
25+ /** Service class for OpenAI service using latest convenience api */
3326@ Service
3427@ Slf4j
3528public class OpenAiService {
36- private static final ObjectMapper JACKSON = new ObjectMapper ();
37-
3829 /**
3930 * Chat request to OpenAI
4031 *
4132 * @param prompt The prompt to send to the assistant
4233 * @return the assistant message response
4334 */
4435 @ Nonnull
45- public OpenAiChatCompletionOutput chatCompletion (@ Nonnull final String prompt ) {
46- return OpenAiClient .forModel (GPT_4O_MINI ).chatCompletion (prompt );
36+ public OpenAiChatCompletionResponse chatCompletion (@ Nonnull final String prompt ) {
37+ return OpenAiClient .forModel (GPT_4O_MINI )
38+ .chatCompletion (new OpenAiChatCompletionRequest (prompt ));
39+ }
40+
41+ /**
42+ * Chat requests to OpenAI and updating the messages history
43+ *
44+ * @param previousMessage The request to send to the assistant
45+ * @return the assistant message response
46+ */
47+ @ Nonnull
48+ public OpenAiChatCompletionResponse messagesHistory (@ Nonnull final String previousMessage ) {
49+ val messagesList = new ArrayList <OpenAiMessage >();
50+ messagesList .add (OpenAiMessage .user (previousMessage ));
51+
52+ final OpenAiChatCompletionResponse result =
53+ OpenAiClient .forModel (GPT_4O_MINI )
54+ .chatCompletion (new OpenAiChatCompletionRequest (messagesList ));
55+
56+ messagesList .add (result .getMessage ());
57+ messagesList .add (OpenAiMessage .user ("What is the typical food there?" ));
58+
59+ return OpenAiClient .forModel (GPT_4O_MINI )
60+ .chatCompletion (new OpenAiChatCompletionRequest (messagesList ));
4761 }
4862
4963 /**
@@ -55,9 +69,7 @@ public OpenAiChatCompletionOutput chatCompletion(@Nonnull final String prompt) {
5569 @ Nonnull
5670 public Stream <OpenAiChatCompletionDelta > streamChatCompletionDeltas (
5771 @ Nonnull final String message ) {
58- final var request =
59- new OpenAiChatCompletionParameters ()
60- .addMessages (new OpenAiChatMessage .OpenAiChatUserMessage ().addText (message ));
72+ final var request = new OpenAiChatCompletionRequest (OpenAiMessage .user (message ));
6173
6274 return OpenAiClient .forModel (GPT_4O_MINI ).streamChatCompletionDeltas (request );
6375 }
@@ -82,93 +94,50 @@ public Stream<String> streamChatCompletion(@Nonnull final String message) {
8294 * @return the assistant message response
8395 */
8496 @ Nonnull
85- public OpenAiChatCompletionOutput chatCompletionImage (@ Nonnull final String linkToImage ) {
86- final var request =
87- new OpenAiChatCompletionParameters ()
88- .addMessages (
89- new OpenAiChatMessage .OpenAiChatUserMessage ()
90- .addText ("Describe the following image." )
91- .addImage (
92- linkToImage ,
93- OpenAiChatMessage .OpenAiChatUserMessage .ImageDetailLevel .HIGH ));
94-
95- return OpenAiClient .forModel (GPT_4O ).chatCompletion (request );
97+ public OpenAiChatCompletionResponse chatCompletionImage (@ Nonnull final String linkToImage ) {
98+
99+ final var userMessage =
100+ OpenAiMessage .user ("Describe the following image." )
101+ .withImage (linkToImage , OpenAiImageItem .DetailLevel .HIGH );
102+
103+ return OpenAiClient .forModel (GPT_4O )
104+ .chatCompletion (new OpenAiChatCompletionRequest (userMessage ));
96105 }
97106
98107 /**
99- * Executes a chat completion request to OpenAI with a tool that calculates the weather.
108+ * Chat request to OpenAI with tool that gets the weather for a given location and unit. The tool
109+ * executed and the result is sent back to the assistant.
100110 *
101111 * @param location The location to get the weather for.
102112 * @param unit The unit of temperature to use.
103113 * @return The assistant message response.
104114 */
105115 @ Nonnull
106- public OpenAiChatCompletionOutput chatCompletionToolExecution (
116+ public OpenAiChatCompletionResponse chatCompletionToolExecution (
107117 @ Nonnull final String location , @ Nonnull final String unit ) {
118+ final OpenAiClient client = OpenAiClient .forModel (GPT_4O_MINI );
108119
109- // 1. Define the function
110- final Map <String , Object > schemaMap = generateSchema (WeatherMethod .Request .class );
111- final var function =
112- new OpenAiChatCompletionFunction ()
113- .setName ("weather" )
114- .setDescription ("Get the weather for the given location" )
115- .setParameters (schemaMap );
116- final var tool = new OpenAiChatCompletionTool ().setType (FUNCTION ).setFunction (function );
117-
118- final var messages = new ArrayList <OpenAiChatMessage >();
119- messages .add (
120- new OpenAiChatMessage .OpenAiChatUserMessage ()
121- .addText ("What's the weather in %s in %s?" .formatted (location , unit )));
122-
123- // Assistant will call the function
124- final var request =
125- new OpenAiChatCompletionParameters ()
126- .addMessages (messages .toArray (OpenAiChatMessage []::new ))
127- .setTools (List .of (tool ));
128-
129- final OpenAiChatCompletionOutput response =
130- OpenAiClient .forModel (GPT_4O_MINI ).chatCompletion (request );
131-
132- // 2. Optionally, execute the function.
133- final OpenAiChatToolCall toolCall =
134- response .getChoices ().get (0 ).getMessage ().getToolCalls ().get (0 );
135- final WeatherMethod .Request arguments =
136- parseJson (toolCall .getFunction ().getArguments (), WeatherMethod .Request .class );
137- final WeatherMethod .Response currentWeather = WeatherMethod .getCurrentWeather (arguments );
138-
139- final OpenAiChatMessage .OpenAiChatAssistantMessage assistantMessage =
140- response .getChoices ().get (0 ).getMessage ();
141- messages .add (assistantMessage );
142-
143- final var toolMessage =
144- new OpenAiChatMessage .OpenAiChatToolMessage ()
145- .setToolCallId (toolCall .getId ())
146- .setContent (currentWeather .toString ());
147- messages .add (toolMessage );
148-
149- final var finalRequest =
150- new OpenAiChatCompletionParameters ()
151- .addMessages (messages .toArray (OpenAiChatMessage []::new ));
152-
153- return OpenAiClient .forModel (GPT_4O_MINI ).chatCompletion (finalRequest );
154- }
120+ final var messages = new ArrayList <OpenAiMessage >();
121+ messages .add (OpenAiMessage .user ("What's the weather in %s in %s?" .formatted (location , unit )));
155122
156- private static <T > T parseJson (@ Nonnull final String rawJson , @ Nonnull final Class <T > clazz ) {
157- try {
158- return JACKSON .readValue (rawJson , clazz );
159- } catch (JsonProcessingException e ) {
160- throw new IllegalArgumentException ("Failed to parse tool call arguments: " + rawJson , e );
161- }
162- }
163-
164- private static Map <String , Object > generateSchema (@ Nonnull final Class <?> clazz ) {
165- final var jsonSchemaGenerator = new JsonSchemaGenerator (JACKSON );
166- try {
167- final var schema = jsonSchemaGenerator .generateSchema (clazz );
168- return JACKSON .convertValue (schema , new TypeReference <>() {});
169- } catch (JsonMappingException e ) {
170- throw new IllegalArgumentException ("Could not generate schema for " + clazz .getName (), e );
171- }
123+ // 1. Define the function
124+ final List <OpenAiTool > tools =
125+ List .of (
126+ OpenAiTool .forFunction (WeatherMethod ::getCurrentWeather )
127+ .withArgument (WeatherMethod .Request .class )
128+ .withName ("weather" )
129+ .withDescription ("Get the weather for the given location" ));
130+
131+ // 2. Assistant calls the function
132+ final var request = new OpenAiChatCompletionRequest (messages ).withToolsExecutable (tools );
133+ final OpenAiChatCompletionResponse response = client .chatCompletion (request );
134+
135+ // 3. Execute the tool calls
136+ messages .add (response .getMessage ());
137+ messages .addAll (response .executeTools ());
138+
139+ // 4. Have model run the final request with incorporated tool results
140+ return client .chatCompletion (request .withMessages (messages ));
172141 }
173142
174143 /**
@@ -178,8 +147,8 @@ private static Map<String, Object> generateSchema(@Nonnull final Class<?> clazz)
178147 * @return the embedding response
179148 */
180149 @ Nonnull
181- public OpenAiEmbeddingOutput embedding (@ Nonnull final String input ) {
182- final var request = new OpenAiEmbeddingParameters (). setInput (input );
150+ public OpenAiEmbeddingResponse embedding (@ Nonnull final String input ) {
151+ final var request = new OpenAiEmbeddingRequest ( List . of (input ) );
183152
184153 return OpenAiClient .forModel (TEXT_EMBEDDING_3_SMALL ).embedding (request );
185154 }
@@ -192,12 +161,13 @@ public OpenAiEmbeddingOutput embedding(@Nonnull final String input) {
192161 * @return the assistant message response
193162 */
194163 @ Nonnull
195- public OpenAiChatCompletionOutput chatCompletionWithResource (
164+ public OpenAiChatCompletionResponse chatCompletionWithResource (
196165 @ Nonnull final String resourceGroup , @ Nonnull final String prompt ) {
197166
198167 final var destination =
199168 new AiCoreService ().getInferenceDestination (resourceGroup ).forModel (GPT_4O );
200169
201- return OpenAiClient .withCustomDestination (destination ).chatCompletion (prompt );
170+ return OpenAiClient .withCustomDestination (destination )
171+ .chatCompletion (new OpenAiChatCompletionRequest (prompt ));
202172 }
203173}
0 commit comments