Skip to content

Commit 613a67e

Browse files
committed
Add
1 parent f8d2bbb commit 613a67e

File tree

13 files changed

+157
-31
lines changed

13 files changed

+157
-31
lines changed

ai-agent/src/main/java/io/sentrius/agent/analysis/agents/verbs/AgentVerbs.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ public ArrayNode promptAgent(AgentExecution execution, Map<String, Object> args)
151151
@Verb(name = "interpret_user_request", returnType = ArrayNode.class, description = "Queries the LLM using the " +
152152
"user input.",
153153
isAiCallable = false, requiresTokenManagement = true)
154-
public TerminalResponse interpretUserData(AgentExecution execution, @NonNull WebSocky socketConnection) throws ZtatException,
154+
public TerminalResponse interpretUserData(AgentExecution execution, @NonNull WebSocky socketConnection,
155+
@NonNull Message userMessage) throws ZtatException,
155156
IOException {
156157

157158
var lastMessage = socketConnection.getMessages().stream().reduce((prev, next) -> next).orElse(null);
@@ -175,10 +176,14 @@ public TerminalResponse interpretUserData(AgentExecution execution, @NonNull Web
175176
List<Message> messages = new ArrayList<>();
176177

177178
messages.add(Message.builder().role("system").content(prompt).build());
178-
messages.add(Message.builder().role("system").content("Please ensure your respones abide by the " +
179-
"following json format. Please summarize prior terminal sessions, using terminal output if needed " +
179+
messages.add(Message.builder().role("system").content("Please ensure your nextOperation abides by the " +
180+
"following json format and leave it empty if user's request doesn't require explicit use of system " +
181+
"operations" +
182+
". Please summarize prior terminal " +
183+
"sessions, using " +
184+
"terminal output if needed " +
180185
"for clarity of the next LLM request and for the user: " + terminalResponse).build());
181-
186+
messages.add(Message.builder().role("user").content(userMessage.getContent()).build());
182187
LLMRequest chatRequest = LLMRequest.builder().model("gpt-4o").messages(messages).build();
183188
var resp = llmService.askQuestion(execution, chatRequest);
184189
execution.addMessages( messages );
@@ -217,10 +222,11 @@ public TerminalResponse interpretUserData(AgentExecution execution, @NonNull Web
217222
List<Message> messages = new ArrayList<>();
218223

219224
messages.add(Message.builder().role("system").content(prompt).build());
220-
messages.add(Message.builder().role("system").content("Please ensure your respones abide by the " +
225+
messages.add(Message.builder().role("system").content("Please ensure your nextOperation abide by the " +
221226
"following json format. Please summarize prior terminal sessions, using terminal output if needed " +
222227
"for clarity of the next LLM request and for the user: " + terminalResponse).build());
223228
messages.add(Message.builder().role("assistant").content("prior response: " + lastMessage.getTerminalSummaryForLLM()).build());
229+
messages.add(Message.builder().role("user").content(userMessage.getContent()).build());
224230

225231
LLMRequest chatRequest = LLMRequest.builder().model("gpt-4o").messages(messages).build();
226232
var resp = llmService.askQuestion(execution, chatRequest);

ai-agent/src/main/java/io/sentrius/agent/analysis/api/UserCommunicationService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public class UserCommunicationService {
2828

2929
private final ConcurrentHashMap<String, WebSocky> sessions = new ConcurrentHashMap<>();
3030

31+
32+
3133
public void createSession(String sessionId, WebSocketSession session) {
3234
sessions.put(sessionId, WebSocky.builder().sessionId(sessionId).webSocketSession(session).build());
3335
}

ai-agent/src/main/java/io/sentrius/agent/analysis/api/websocket/ChatWSHandler.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
import java.util.stream.Stream;
1212
import com.fasterxml.jackson.databind.ObjectMapper;
1313
import io.sentrius.agent.analysis.agents.agents.ChatAgent;
14+
import io.sentrius.agent.analysis.agents.verbs.AgentVerbs;
15+
import io.sentrius.agent.analysis.agents.verbs.TerminalVerbs;
1416
import io.sentrius.agent.analysis.api.UserCommunicationService;
17+
import io.sentrius.sso.core.exceptions.ZtatException;
1518
import io.sentrius.sso.core.services.agents.ZeroTrustClientService;
19+
import io.sentrius.sso.genai.Message;
1620
import io.sentrius.sso.protobuf.Session;
1721
import lombok.RequiredArgsConstructor;
1822
import lombok.extern.slf4j.Slf4j;
@@ -29,15 +33,20 @@ public class ChatWSHandler extends TextWebSocketHandler {
2933

3034
final UserCommunicationService userCommunicationService;
3135
final ZeroTrustClientService zeroTrustClientService;
36+
final TerminalVerbs terminalVerbs;
37+
final AgentVerbs agentVerbs;
3238
// Store active sessions, using session ID or a custom identifier
3339

3440

3541
private final ChatAgent chatAgent;
3642

3743
@Autowired
38-
public ChatWSHandler(UserCommunicationService userCommunicationService, ZeroTrustClientService zeroTrustClientService, ChatAgent chatAgent) {
44+
public ChatWSHandler(UserCommunicationService userCommunicationService, ZeroTrustClientService zeroTrustClientService,
45+
TerminalVerbs terminalVerbs, AgentVerbs agentVerbs, ChatAgent chatAgent) {
3946
this.userCommunicationService = userCommunicationService;
4047
this.zeroTrustClientService = zeroTrustClientService;
48+
this.terminalVerbs = terminalVerbs;
49+
this.agentVerbs = agentVerbs;
4150
this.chatAgent = chatAgent;
4251
}
4352

@@ -85,6 +94,8 @@ public void afterConnectionEstablished(WebSocketSession session) throws Exceptio
8594
session.sendMessage(new TextMessage(
8695
base64Message
8796
));
97+
98+
userCommunicationService.createSession(queryParams.get("sessionId"), session);
8899
}
89100

90101

@@ -100,7 +111,10 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
100111
Map<String, String> queryParams = parseQueryParams(uri.getQuery());
101112
String sessionId = queryParams.get("sessionId");
102113

103-
if (sessionId != null) {
114+
var websocky = userCommunicationService.getSession(sessionId);
115+
116+
if (sessionId != null && websocky.isPresent()) {
117+
var websocketCommunication = websocky.get();
104118
log.info("Received message from session ID: " + sessionId);
105119
// Handle the message (e.g., process or respond)
106120

@@ -113,7 +127,6 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
113127
Session.ChatMessage.parseFrom(messageBytes);
114128

115129
if (auditLog.getMessage().equals("heartbeat")) {
116-
log.info("heartbeat");
117130
return;
118131
}
119132
var json = new ObjectMapper().readTree(auditLog.getMessage());
@@ -136,8 +149,25 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
136149
session.close();
137150
}
138151
return;
139-
} else if ("heartbeat".equals(auditLog.getMessage())) {
152+
} else if ("user-message".equals(json.get("type").asText())) {
153+
Message userMessage = Message.builder().role("user").content(json.get("message").asText()).build();
140154
log.info("Received heartbeat from session {}", sessionId);
155+
var response = agentVerbs.interpretUserData(chatAgent.getAgentExecution(),
156+
websocketCommunication, userMessage);
157+
log.info("Response: {}", response);
158+
var newMessage = Session.ChatMessage.newBuilder()
159+
.setMessage(String.format("{\"type\":\"user-message\",\"message\":\"%s\"}",
160+
response.getResponseForUser()))
161+
.setSender("agent")
162+
.setChatGroupId("")
163+
.setSessionId(Long.parseLong(websocketCommunication.getSessionId()))
164+
.setTimestamp(System.currentTimeMillis())
165+
.build();
166+
messageBytes = newMessage.toByteArray();
167+
String base64Message = Base64.getEncoder().encodeToString(messageBytes);
168+
session.sendMessage(new TextMessage(
169+
base64Message
170+
));
141171
return; // Ignore heartbeat messages
142172
} else {
143173
log.info("Processing message: {}", auditLog.getMessage());
@@ -154,7 +184,7 @@ protected void handleTextMessage(WebSocketSession session, TextMessage message)
154184
log.info("Session ID not found in query parameters for message handling.");
155185
}
156186
}
157-
}catch (Exception e ){
187+
}catch (Exception | ZtatException e ){
158188
throw new RuntimeException(e);
159189
}
160190
}

ai-agent/src/main/java/io/sentrius/agent/analysis/model/WebSocky.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ public class WebSocky {
2424
WebSocketSession webSocketSession;
2525
@Builder.Default
2626
List<TerminalResponse> messages = new ArrayList<>();
27+
2728
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
description: "Agent that handles logs and OpenAI access."
2+
context: |
3+
You are a helpful agent that is responding to users via a chat interface. Please respond to the user in a friendly and helpful manner.
4+
Return responses in the following format:
5+
{
6+
"previousOperation": "<previousOperation>",
7+
"nextOperation": "<nextOperation>",
8+
"terminalSummaryForLLM": "<Summary of the terminal thus far for the next LLM assessment>",
9+
"responseForUser": "<Response to the user>"
10+
}

api/src/main/java/io/sentrius/sso/controllers/api/UserApiController.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,17 @@ public ResponseEntity<ObjectNode> addUser(HttpServletRequest request, HttpServle
136136

137137
@GetMapping("/delete")
138138
@LimitAccess(userAccess = {UserAccessEnum.CAN_DEL_USERS})
139-
public String deleteUser(@RequestParam("userId") String userId) throws GeneralSecurityException {
139+
public String deleteUser(@RequestParam("userId") String userId, @RequestParam(required = false) String type) throws GeneralSecurityException {
140140
log.info("Deleting user with id: {}", userId);
141-
Long id = Long.parseLong(cryptoService.decrypt(userId));
142-
userService.deleteUser(id);
141+
if (null != type && type.equalsIgnoreCase("non_person_entity")) {
142+
log.info("Deleting non-person entity user with id: {}", userId);
143+
String userIdStr = cryptoService.decrypt(userId);
144+
var usr = userService.getUserByUserid(userIdStr);
145+
userService.deleteUser(usr.getId());
146+
} else {
147+
Long id = Long.parseLong(cryptoService.decrypt(userId));
148+
userService.deleteUser(id);
149+
}
143150
return "redirect:/sso/v1/users/list?message=" + MessagingUtil.getMessageId(MessagingUtil.USER_DELETE_SUCCESS);
144151
}
145152

api/src/main/resources/default-policy.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ actions:
1919
ztat_provider: "ztat-service.internal"
2020
capabilities:
2121
primitives:
22+
- id: "accessLLM"
23+
description: "access llm"
24+
endpoint:
25+
- "/api/v1/chat/completions"
2226
composed:
2327
ztat:
2428
provider: "ztat-service.internal"

api/src/main/resources/static/js/chat.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,13 @@ export function switchToAgent(agentName,agentId, sessionId, agentHost) {
230230
appendToChatWindow(msg.sender, msg.message);
231231
});
232232

233+
container.classList.remove("hidden");
233234
container.style.display = "block";
234235
console.log("Chat container displayed");
235236
}
236237

237238
export function sendMessage(event) {
239+
console.log("Send message event:", event);
238240
if (event.key !== "Enter") return;
239241

240242
const input = document.getElementById("chat-input");
@@ -290,4 +292,6 @@ export async function fetchAvailableAgents() {
290292
console.error("Error fetching available agents:", error);
291293
return [];
292294
}
293-
}
295+
}
296+
297+
window.sendMessage = sendMessage;
Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
// api/src/main/resources/static/js/dashboard-agents.js
21
import { fetchAvailableAgents, switchToAgent } from './chat.js';
32

43
document.addEventListener('DOMContentLoaded', function () {
5-
console.log("Fetching agents 1...");
64
const agentSelect = document.getElementById('agent-select');
75
const startChatBtn = document.getElementById('start-chat-btn');
86

@@ -11,17 +9,40 @@ document.addEventListener('DOMContentLoaded', function () {
119
return;
1210
}
1311

14-
console.log("Fetching agents 2...");
15-
fetchAvailableAgents().then(agents => {
16-
agents.forEach(agent => {
17-
const option = document.createElement('option');
18-
option.value = agent.agentName;
19-
option.textContent = agent.agentName || agent.agentId;
20-
option.dataset.agentHost = agent.agentCallback;
21-
option.dataset.agentId = agent.agentId;
22-
agentSelect.appendChild(option);
12+
function updateAgentList() {
13+
console.log('Updating agent list...');
14+
fetchAvailableAgents().then(agents => {
15+
const existingOptions = Array.from(agentSelect.options).reduce((map, opt) => {
16+
map.set(opt.dataset.agentId, opt);
17+
return map;
18+
}, new Map());
19+
20+
const currentAgentIds = new Set(agents.map(a => a.agentId));
21+
22+
// Add new agents
23+
agents.forEach(agent => {
24+
if (!existingOptions.has(agent.agentId)) {
25+
const option = document.createElement('option');
26+
option.value = agent.agentName;
27+
option.textContent = agent.agentName || agent.agentId;
28+
option.dataset.agentHost = agent.agentCallback;
29+
option.dataset.agentId = agent.agentId;
30+
agentSelect.appendChild(option);
31+
}
32+
});
33+
34+
// Remove stale agents
35+
for (let [agentId, option] of existingOptions.entries()) {
36+
if (!currentAgentIds.has(agentId)) {
37+
agentSelect.removeChild(option);
38+
}
39+
}
40+
41+
startChatBtn.disabled = !agentSelect.value;
42+
}).catch(err => {
43+
console.error('Failed to update agent list:', err);
2344
});
24-
});
45+
}
2546

2647
agentSelect.addEventListener('change', function () {
2748
startChatBtn.disabled = !agentSelect.value;
@@ -30,12 +51,17 @@ document.addEventListener('DOMContentLoaded', function () {
3051
startChatBtn.addEventListener('click', function () {
3152
const selected = agentSelect.options[agentSelect.selectedIndex];
3253
if (selected && selected.value) {
33-
console.log("Switching to agent: ", selected.value);
3454
const agentName = selected.value;
3555
const agentHost = selected.dataset.agentHost;
3656
const agentId = selected.dataset.agentId;
3757
const sessionId = Date.now().toString();
3858
switchToAgent(agentName, agentId, sessionId, agentHost);
3959
}
4060
});
41-
});
61+
62+
// Initial load
63+
updateAgentList();
64+
65+
// Refresh every 15 seconds
66+
setInterval(updateAgentList, 15000);
67+
});

api/src/main/resources/templates/sso/dashboard.html

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,29 @@
332332

333333
</script>
334334
<style>
335+
#chat-container {
336+
position: fixed;
337+
bottom: 20px;
338+
right: 20px;
339+
width: 300px;
340+
height: 400px;
341+
z-index: 1000;
342+
background: #1e1e1e;
343+
border: 1px solid #444;
344+
border-radius: 8px;
345+
padding: 12px;
346+
display: none;
347+
}
348+
349+
#chat-messages {
350+
height: 250px; /* Adjust height as needed */
351+
overflow-y: auto;
352+
padding: 10px;
353+
border: 1px solid #ccc;
354+
background-color: #1e1e1e;
355+
font-family: sans-serif;
356+
}
357+
335358
small-card {
336359
max-width: 300px;
337360
padding: 10px;
@@ -380,7 +403,7 @@
380403
<span id="close-chat" onclick="toggleChat()"></span>
381404
</div>
382405
<div id="chat-messages"></div>
383-
<input type="text" id="chat-input" placeholder="Type a message..." onkeypress="sendMessage(event)">
406+
<input type="text" id="chat-input" placeholder="Type a message..." onkeydown="sendMessage(event)">
384407
</div>
385408
<div class="container-fluid">
386409
<br><br>
@@ -448,7 +471,7 @@ <h5 class="card-title">AI Admin Operations</h5>
448471
<a href="/api/v1/users/list" class="btn btn-danger">Build Automation</a>
449472
<a href="#" class="btn btn-primary" data-bs-toggle="modal"
450473
data-bs-target="#userFormModal">Usage Patterns</a>
451-
chat-container <a href="#" class="btn btn-primary" data-bs-toggle="modal"
474+
<a href="#" class="btn btn-primary" data-bs-toggle="modal"
452475
data-bs-target="#userFormModal">Usage Patterns</a>
453476
</div>
454477
<div class="mb-3">

0 commit comments

Comments
 (0)