55import java .util .ArrayList ;
66import java .util .Date ;
77
8+ import com .fasterxml .jackson .databind .ObjectMapper ;
89import com .genexus .db .Namespace ;
910import com .genexus .db .UserInformation ;
1011import com .genexus .diagnostics .GXDebugInfo ;
1112import com .genexus .diagnostics .GXDebugManager ;
13+ import com .genexus .internet .HttpClient ;
1214import com .genexus .internet .HttpContext ;
1315import com .genexus .mock .GXMockProvider ;
1416import com .genexus .performance .ProcedureInfo ;
1719import com .genexus .util .saia .OpenAIRequest ;
1820import com .genexus .util .saia .OpenAIResponse ;
1921import com .genexus .util .saia .SaiaService ;
22+ import org .json .JSONObject ;
2023
2124public abstract class GXProcedure implements IErrorHandler , ISubmitteable {
2225 public abstract void initialize ();
@@ -32,7 +35,8 @@ public abstract class GXProcedure implements IErrorHandler, ISubmitteable {
3235 UserInformation ui =null ;
3336
3437 private Date beginExecute ;
35-
38+ private HttpClient client ;
39+
3640 public static final int IN_NEW_UTL = -2 ;
3741
3842 public GXProcedure (int remoteHandle , ModelContext context , String location ) {
@@ -271,29 +275,42 @@ protected String callAssistant(String agent, GXProperties properties, ArrayList<
271275 }
272276
273277 protected String callAgent (String agent , GXProperties properties , ArrayList <OpenAIResponse .Message > messages , CallResult result ) {
278+ return callAgent (agent , false , properties , messages , result );
279+ }
280+
281+ protected String callAgent (String agent , boolean stream , GXProperties properties , ArrayList <OpenAIResponse .Message > messages , CallResult result ) {
274282 OpenAIRequest aiRequest = new OpenAIRequest ();
275283 aiRequest .setModel (String .format ("saia:agent:%s" , agent ));
276284 if (!messages .isEmpty ())
277285 aiRequest .setMessages (messages );
278286 aiRequest .setVariables (properties .getList ());
279- OpenAIResponse aiResponse = SaiaService .call (aiRequest , result );
287+ if (stream )
288+ aiRequest .setStream (true );
289+ client = new HttpClient ();
290+ OpenAIResponse aiResponse = SaiaService .call (aiRequest , client , result );
280291 if (aiResponse != null ) {
281292 for (OpenAIResponse .Choice element : aiResponse .getChoices ()) {
282293 String finishReason = element .getFinishReason ();
283294 if (finishReason .equals ("stop" ))
284295 return element .getMessage ().getContent ();
285296 if (finishReason .equals ("tool_calls" )) {
286297 messages .add (element .getMessage ());
287- for (OpenAIResponse .ToolCall tollCall :element .getMessage ().getToolCalls ()) {
288- processToolCall (tollCall , messages );
289- }
290- return callAgent (agent , properties , messages , result );
298+ return processNotChunkedResponse (agent , stream , properties , messages , result , element .getMessage ().getToolCalls ());
291299 }
292300 }
301+ } else if (client .getStatusCode () == 200 ) {
302+ return readChunk (agent , properties , messages , result );
293303 }
294304 return "" ;
295305 }
296306
307+ private String processNotChunkedResponse (String agent , boolean stream , GXProperties properties , ArrayList <OpenAIResponse .Message > messages , CallResult result , ArrayList <OpenAIResponse .ToolCall > toolCalls ) {
308+ for (OpenAIResponse .ToolCall tollCall : toolCalls ) {
309+ processToolCall (tollCall , messages );
310+ }
311+ return callAgent (agent , stream , properties , messages , result );
312+ }
313+
297314 private void processToolCall (OpenAIResponse .ToolCall toolCall , ArrayList <OpenAIResponse .Message > messages ) {
298315 String result ;
299316 String functionName = toolCall .getFunction ().getName ();
@@ -309,4 +326,36 @@ private void processToolCall(OpenAIResponse.ToolCall toolCall, ArrayList<OpenAIR
309326 toolCallMessage .setToolCallId (toolCall .getId ());
310327 messages .add (toolCallMessage );
311328 }
329+
330+ protected String readChunk () {
331+ return readChunk (null , null , null , null );
332+ }
333+
334+ protected String readChunk (String agent , GXProperties properties , ArrayList <OpenAIResponse .Message > messages , CallResult result ) {
335+ String data = client .readChunk ();
336+ if (data .isEmpty ())
337+ return "" ;
338+ int index = data .indexOf ("data:" ) + "data:" .length ();
339+ String chunkJson = data .substring (index ).trim ();
340+ try {
341+ JSONObject jsonResponse = new JSONObject (chunkJson );
342+ OpenAIResponse chunkResponse = new ObjectMapper ().readValue (jsonResponse .toString (), OpenAIResponse .class );
343+ OpenAIResponse .Choice choise = chunkResponse .getChoices ().get (0 );
344+ if (choise .getFinishReason () != null && choise .getFinishReason ().equals ("tool_calls" ) && agent != null ) {
345+ messages .add (choise .getMessage ());
346+ return processNotChunkedResponse (agent , true , properties , messages , result , choise .getMessage ().getToolCalls ());
347+ }
348+ String chunkString = choise .getDelta ().getContent ();
349+ if (chunkString == null )
350+ return "" ;
351+ return chunkString ;
352+ }
353+ catch (Exception e ) {
354+ return "" ;
355+ }
356+ }
357+
358+ protected boolean isStreamEOF () {
359+ return client .getEof ();
360+ }
312361}
0 commit comments