Skip to content

Commit 4ae2d8f

Browse files
committed
Events with toolcalls in agent chat response
Issue: 206834
1 parent 8e2bd3f commit 4ae2d8f

File tree

3 files changed

+169
-14
lines changed

3 files changed

+169
-14
lines changed

java/src/main/java/com/genexus/GXProcedure.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.util.ArrayList;
66
import java.util.Date;
77

8-
import com.fasterxml.jackson.databind.ObjectMapper;
98
import com.genexus.db.Namespace;
109
import com.genexus.db.UserInformation;
1110
import com.genexus.diagnostics.GXDebugInfo;
@@ -18,8 +17,8 @@
1817
import com.genexus.util.*;
1918
import com.genexus.util.saia.OpenAIRequest;
2019
import com.genexus.util.saia.OpenAIResponse;
20+
import com.genexus.util.saia.SaiaEvents;
2121
import com.genexus.util.saia.SaiaService;
22-
import org.json.JSONObject;
2322

2423
public abstract class GXProcedure implements IErrorHandler, ISubmitteable {
2524
public abstract void initialize();
@@ -286,9 +285,9 @@ protected ChatResult chatAgent(String agent, GXProperties properties, ArrayList<
286285
context.setThreadModelContext(context);
287286
callAgent(agent, true, properties, messages, result, chatResult);
288287
} finally {
289-
chatResult.markDone();
290288
if (history != null)
291289
history.setExternalInstance(messages);
290+
chatResult.markDone();
292291
}
293292
}).start();
294293

@@ -327,16 +326,18 @@ protected String callAgent(String agent, boolean stream, GXProperties properties
327326

328327
public String processNotChunkedResponse(String agent, boolean stream, GXProperties properties, ArrayList<OpenAIResponse.Message> messages, CallResult result, ChatResult chatResult, ArrayList<OpenAIResponse.ToolCall> toolCalls) {
329328
for (OpenAIResponse.ToolCall tollCall : toolCalls) {
330-
processToolCall(tollCall, messages);
329+
processToolCall(tollCall, messages, chatResult);
331330
}
332331
return callAgent(agent, stream, properties, messages, result, chatResult);
333332
}
334333

335-
private void processToolCall(OpenAIResponse.ToolCall toolCall, ArrayList<OpenAIResponse.Message> messages) {
334+
private void processToolCall(OpenAIResponse.ToolCall toolCall, ArrayList<OpenAIResponse.Message> messages, ChatResult chatResult) {
336335
String result;
337336
String functionName = toolCall.getFunction().getName();
338337
try {
338+
addToolCallChunk(chatResult, toolCall, "saia.metadata.tool.started", "started", null);
339339
result = callTool(functionName, toolCall.getFunction().getArguments());
340+
addToolCallChunk(chatResult, toolCall, "saia.metadata.tool.finished", "finished_successfully", result);
340341
}
341342
catch (Throwable e) {
342343
result = String.format("Error calling tool %s", functionName);
@@ -347,4 +348,13 @@ private void processToolCall(OpenAIResponse.ToolCall toolCall, ArrayList<OpenAIR
347348
toolCallMessage.setToolCallId(toolCall.getId());
348349
messages.add(toolCallMessage);
349350
}
351+
352+
private void addToolCallChunk(ChatResult chatResult, OpenAIResponse.ToolCall toolCall, String event, String tollStatus, String result) {
353+
if (chatResult != null) {
354+
SaiaEvents saiaToolStarted = new SaiaEvents();
355+
String eventJson = saiaToolStarted.serializeSaiaEvent(toolCall, event, tollStatus, result);
356+
if (eventJson != null)
357+
chatResult.addChunk("data: " + eventJson);
358+
}
359+
}
350360
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package com.genexus.util.saia;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.genexus.diagnostics.core.ILogger;
8+
import com.genexus.diagnostics.core.LogManager;
9+
10+
import java.time.LocalDateTime;
11+
import java.time.format.DateTimeFormatter;
12+
13+
@JsonInclude(JsonInclude.Include.NON_NULL)
14+
public class SaiaEvents {
15+
private static final ILogger logger = LogManager.getLogger(SaiaEvents.class);
16+
17+
@JsonProperty("event")
18+
private String event;
19+
20+
@JsonProperty("data")
21+
private Data data;
22+
23+
public String getEvent() {
24+
return event;
25+
}
26+
27+
public void setEvent(String event) {
28+
this.event = event;
29+
}
30+
31+
public Data getData() {
32+
return data;
33+
}
34+
35+
public void setData(Data data) {
36+
this.data = data;
37+
}
38+
39+
@JsonInclude(JsonInclude.Include.NON_NULL)
40+
public static class Data {
41+
42+
@JsonProperty("toolId")
43+
private String toolId;
44+
45+
@JsonProperty("toolName")
46+
private String toolName;
47+
48+
@JsonProperty("timestamp")
49+
private String timestamp;
50+
51+
@JsonProperty("toolDescription")
52+
private String toolDescription;
53+
54+
@JsonProperty("toolStatus")
55+
private String toolStatus;
56+
57+
@JsonProperty("toolResponse")
58+
private String toolResponse;
59+
60+
@JsonProperty("executionId")
61+
private String executionId;
62+
63+
public String getToolId() {
64+
return toolId;
65+
}
66+
67+
public void setToolId(String toolId) {
68+
this.toolId = toolId;
69+
}
70+
71+
public String getToolName() {
72+
return toolName;
73+
}
74+
75+
public void setToolName(String toolName) {
76+
this.toolName = toolName;
77+
}
78+
79+
public String getTimestamp() {
80+
return timestamp;
81+
}
82+
83+
public void setTimestamp(String timestamp) {
84+
this.timestamp = timestamp;
85+
}
86+
87+
public String getToolDescription() {
88+
return toolDescription;
89+
}
90+
91+
public void setToolDescription(String toolDescription) {
92+
this.toolDescription = toolDescription;
93+
}
94+
95+
public String getToolResponse() {
96+
return toolResponse;
97+
}
98+
99+
public void setToolResponse(String toolResponse) {
100+
this.toolResponse = toolResponse;
101+
}
102+
103+
public String getToolStatus() {
104+
return toolStatus;
105+
}
106+
107+
public void setToolStatus(String toolStatus) {
108+
this.toolStatus = toolStatus;
109+
}
110+
111+
public String getExecutionId() {
112+
return executionId;
113+
}
114+
115+
public void setExecutionId(String executionId) {
116+
this.executionId = executionId;
117+
}
118+
}
119+
120+
public String serializeSaiaEvent(OpenAIResponse.ToolCall toolCall, String event, String tollStatus, String result) {
121+
SaiaEvents.Data saiaToolStartedData = new SaiaEvents.Data();
122+
saiaToolStartedData.setToolName(toolCall.getFunction().getName());
123+
saiaToolStartedData.setTimestamp(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")));
124+
saiaToolStartedData.setToolStatus(tollStatus);
125+
saiaToolStartedData.setToolResponse(result);
126+
saiaToolStartedData.setExecutionId(toolCall.getId());
127+
setEvent(event);
128+
setData(saiaToolStartedData);
129+
130+
try {
131+
ObjectMapper mapper = new ObjectMapper();
132+
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(this);
133+
}
134+
catch (JsonProcessingException e) {
135+
logger.error("Serializing Saia Event", e);
136+
return null;
137+
}
138+
}
139+
}

java/src/main/java/com/genexus/util/saia/SaiaService.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,21 @@ private static void getChunkedSaiaResponse(GXProcedure proc, HttpClient client,
9090
chunkJson = saiaChunkResponse.substring(index).trim();
9191
try {
9292
JSONObject jsonResponse = new JSONObject(chunkJson);
93-
OpenAIResponse chunkResponse = new ObjectMapper().readValue(jsonResponse.toString(), OpenAIResponse.class);
94-
if (!chunkResponse.getChoices().isEmpty()) {
95-
OpenAIResponse.Choice choice = chunkResponse.getChoices().get(0);
96-
if (choice.getFinishReason() != null && choice.getFinishReason().equals("tool_calls")) {
97-
messages.add(choice.getMessage());
98-
proc.processNotChunkedResponse(agent, stream, properties, messages, result, chatResult, choice.getMessage().getToolCalls());
99-
;
100-
} else if (choice.getDelta() != null && choice.getDelta().getContent() != null) {
101-
chatResult.addChunk(((OpenAIResponse.StringContent) choice.getDelta().getContent()).getValue());
93+
if (jsonResponse.has("event") && jsonResponse.has("data")) {
94+
95+
chatResult.addChunk(saiaChunkResponse);
96+
}
97+
else {
98+
OpenAIResponse chunkResponse = new ObjectMapper().readValue(jsonResponse.toString(), OpenAIResponse.class);
99+
if (!chunkResponse.getChoices().isEmpty()) {
100+
OpenAIResponse.Choice choice = chunkResponse.getChoices().get(0);
101+
if (choice.getFinishReason() != null && choice.getFinishReason().equals("tool_calls")) {
102+
messages.add(choice.getMessage());
103+
proc.processNotChunkedResponse(agent, stream, properties, messages, result, chatResult, choice.getMessage().getToolCalls());
104+
;
105+
} else if (choice.getDelta() != null && choice.getDelta().getContent() != null) {
106+
chatResult.addChunk(((OpenAIResponse.StringContent) choice.getDelta().getContent()).getValue());
107+
}
102108
}
103109
}
104110
saiaChunkResponse = client.readChunk();

0 commit comments

Comments
 (0)