Skip to content

Commit 9752f71

Browse files
authored
Merge pull request #116 from SentriusLLC/agents_2x2
Agents 2x2
2 parents c3e9dac + ae630cc commit 9752f71

File tree

52 files changed

+5163
-70
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+5163
-70
lines changed

.azure.env

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
SENTRIUS_VERSION=1.1.113
1+
SENTRIUS_VERSION=1.1.149
22
SENTRIUS_SSH_VERSION=1.1.12
33
SENTRIUS_KEYCLOAK_VERSION=1.1.15
44
SENTRIUS_AGENT_VERSION=1.1.24
5-
SENTRIUS_AI_AGENT_VERSION=1.1.5
5+
SENTRIUS_AI_AGENT_VERSION=1.1.8
66
LLMPROXY_VERSION=1.1.5
77
LAUNCHER_VERSION=1.1.6
8-
AGENTPROXY_VERSION=1.1.8
8+
AGENTPROXY_VERSION=1.1.9
99
SSHPROXY_VERSION=1.1.5
10-
RDPPROXY_VERSION=1.1.5
10+
RDPPROXY_VERSION=1.1.7
1111
GITHUB_MCP_VERSION=1.1.5
1212
PROMPT_ADVISOR_VERSION=1.1.8
1313
MONITORING_AGENT_VERSION=1.1.23

agent-launcher/src/main/java/io/sentrius/agent/launcher/service/PodLauncherService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ public V1Pod launchAgentPod(AgentRegistrationDTO agent) throws Exception {
199199
case "atpl-helper":
200200
agentFile = "chat-atpl-helper.yaml";
201201
break;
202+
case "abac":
203+
agentFile = "abac-helper.yaml";
204+
break;
202205
case "default":
203206
default:
204207
agentFile = "chat-helper.yaml";

agent-proxy/src/main/java/io/sentrius/sso/config/AgentWebSocketProxyHandler.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public Mono<Void> handle(WebSocketSession clientSession) {
4848
chatGroupId = chatGroupId.replace(" ","+");
4949
String ztat = queryParams.get("ztat");
5050
String ztatForChat = queryParams.get("jwt");
51+
String userId = queryParams.get("userId");
52+
userId = userId.replace(" ","+");
5153

5254

5355
if (ztatForChat != null && !ztatForChat.isEmpty()) {
@@ -62,10 +64,11 @@ public Mono<Void> handle(WebSocketSession clientSession) {
6264
log.info("Invalid ZTAT token for sessionId: {}", sessionId);
6365
return Mono.error(new RuntimeException("Invalid ZTAT token") );
6466
}
65-
log.info("Handling WebSocket connection for host: {}, sessionId: {}, chatGroupId: {}, ztat: {}",
66-
agentHost, sessionId, chatGroupId, ztat);
67+
log.info("Handling WebSocket connection for host: {}, sessionId: {}, chatGroupId: {}, ztat: {}, userId: {}",
68+
agentHost, sessionId, chatGroupId, ztat, userId);
6769

68-
URI agentUri = agentLocator.resolveWebSocketUri(agentHost.toLowerCase(), sessionId, chatGroupId, ztat);
70+
URI agentUri = agentLocator.resolveWebSocketUri(agentHost.toLowerCase(), sessionId, chatGroupId, ztat,
71+
userId);
6972

7073
log.info("Resolved agent URI: {}", agentUri);
7174

agent-proxy/src/main/java/io/sentrius/sso/locator/KubernetesAgentLocator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
public class KubernetesAgentLocator {
1111

1212

13-
public URI resolveWebSocketUri(String host, String sessionId, String chatGroupId, String ztat) {
13+
public URI resolveWebSocketUri(String host, String sessionId, String chatGroupId, String ztat, String userId) {
1414
// DNS: sentrius-agent-[ID].[namespace].svc.cluster.local
1515
///api/v1/chat/attach/subscribe?sessionId=${encodeURIComponent(this.sessionId)}&chatGroupId=${this.chatGroupId}&ztat=${encodeURIComponent(jwt)
16-
String fqdn = String.format("%s/api/v1/chat/attach/subscribe?sessionId=%s&chatGroupId=%s&ztat=%s",
17-
host, sessionId, chatGroupId, ztat);
16+
String fqdn = String.format("%s/api/v1/chat/attach/subscribe?sessionId=%s&chatGroupId=%s&ztat=%s&userId=%s",
17+
host, sessionId, chatGroupId, ztat, userId);
1818
return URI.create(fqdn);
1919
}
2020
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ public ResponseEntity<Map<String, Object>> initiateRdpSession(
353353
sessionData.put("jwtToken", jwtToken);
354354
sessionData.put("target", hostSystem.getId());
355355
sessionData.put("websocketHost", systemOptions.getRdpProxyDomain());
356+
sessionData.put("websocketBaseUrl", "/guacamole/tunnel");
356357
sessionData.put("websocketUrl", "/guacamole/tunnel?token=" + jwtToken);
357358
sessionData.put("displayName", hostSystem.getDisplayName());
358359

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package io.sentrius.sso.controllers.api;
2+
3+
import io.sentrius.sso.core.annotations.LimitAccess;
4+
import io.sentrius.sso.core.config.SystemOptions;
5+
import io.sentrius.sso.core.controllers.BaseController;
6+
import io.sentrius.sso.core.dto.tooltip.TooltipChatRequest;
7+
import io.sentrius.sso.core.dto.tooltip.TooltipChatResponse;
8+
import io.sentrius.sso.core.dto.tooltip.TooltipDescribeRequest;
9+
import io.sentrius.sso.core.dto.tooltip.TooltipDescribeResponse;
10+
import io.sentrius.sso.core.dto.ztat.TokenDTO;
11+
import io.sentrius.sso.core.model.security.enums.ApplicationAccessEnum;
12+
import io.sentrius.sso.core.model.security.enums.SSHAccessEnum;
13+
import io.sentrius.sso.core.model.users.User;
14+
import io.sentrius.sso.core.model.verbs.Endpoint;
15+
import io.sentrius.sso.core.services.ErrorOutputService;
16+
import io.sentrius.sso.core.services.UserService;
17+
import io.sentrius.sso.core.services.tooltip.CodebaseIndexingService;
18+
import io.sentrius.sso.core.services.tooltip.TooltipService;
19+
import io.sentrius.sso.core.utils.AccessUtil;
20+
import jakarta.servlet.http.HttpServletRequest;
21+
import jakarta.servlet.http.HttpServletResponse;
22+
import lombok.extern.slf4j.Slf4j;
23+
import org.springframework.http.HttpStatus;
24+
import org.springframework.http.ResponseEntity;
25+
import org.springframework.web.bind.annotation.*;
26+
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
import java.util.UUID;
30+
31+
/**
32+
* REST Controller for AI-powered tooltip and contextual help features.
33+
* Provides endpoints for getting descriptions of UI elements and chat-based assistance.
34+
*/
35+
@Slf4j
36+
@RestController
37+
@RequestMapping("/api/v1/tooltip")
38+
public class TooltipController extends BaseController {
39+
40+
private final TooltipService tooltipService;
41+
private final CodebaseIndexingService indexingService;
42+
43+
public TooltipController(
44+
UserService userService,
45+
SystemOptions systemOptions,
46+
ErrorOutputService errorOutputService,
47+
TooltipService tooltipService,
48+
CodebaseIndexingService indexingService) {
49+
super(userService, systemOptions, errorOutputService);
50+
this.tooltipService = tooltipService;
51+
this.indexingService = indexingService;
52+
}
53+
54+
/**
55+
* Get AI-powered description for a UI element.
56+
* Searches indexed codebase and documentation to provide contextual tooltips.
57+
*
58+
* @param request Element context information from the frontend
59+
* @param httpRequest HTTP request for authentication
60+
* @param httpResponse HTTP response
61+
* @return AI-generated description of the element
62+
*/
63+
@PostMapping("/describe")
64+
@Endpoint(description = "Get AI-powered description for a UI element")
65+
@LimitAccess(applicationAccess = ApplicationAccessEnum.CAN_LOG_IN)
66+
public ResponseEntity<TooltipDescribeResponse> describe(
67+
@RequestBody TooltipDescribeRequest request,
68+
HttpServletRequest httpRequest,
69+
HttpServletResponse httpResponse) {
70+
71+
try {
72+
User operatingUser = getOperatingUser(httpRequest, httpResponse);
73+
log.info("Tooltip describe request from user: {}", operatingUser.getUserId());
74+
75+
// Validate request
76+
if (request.getContext() == null) {
77+
return ResponseEntity.badRequest()
78+
.body(TooltipDescribeResponse.builder()
79+
.description("Invalid request: context is required")
80+
.error("Context is required")
81+
.success(false)
82+
.build());
83+
}
84+
85+
// Build token DTO for LLM service
86+
TokenDTO tokenDTO = TokenDTO.builder().communicationId(UUID.randomUUID().toString()).build();
87+
// Token will be populated from security context by LLM service
88+
89+
// Generate description
90+
TooltipDescribeResponse response = tooltipService.describeElement(request, tokenDTO);
91+
92+
return ResponseEntity.ok(response);
93+
94+
} catch (Exception e) {
95+
log.error("Error processing tooltip describe request", e);
96+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
97+
.body(TooltipDescribeResponse.builder()
98+
.description("An error occurred while generating the description.")
99+
.error(e.getMessage())
100+
.success(false)
101+
.build());
102+
}
103+
}
104+
105+
/**
106+
* Chat endpoint for conversational assistance about UI elements and features.
107+
*
108+
* @param request Chat message and optional element context
109+
* @param httpRequest HTTP request for authentication
110+
* @param httpResponse HTTP response
111+
* @return AI-generated chat response
112+
*/
113+
@PostMapping("/chat")
114+
@Endpoint(description = "Chat with AI assistant about UI elements and features")
115+
@LimitAccess(applicationAccess = ApplicationAccessEnum.CAN_LOG_IN)
116+
public ResponseEntity<TooltipChatResponse> chat(
117+
@RequestBody TooltipChatRequest request,
118+
HttpServletRequest httpRequest,
119+
HttpServletResponse httpResponse) {
120+
121+
try {
122+
User operatingUser = getOperatingUser(httpRequest, httpResponse);
123+
log.info("Tooltip chat request from user: {}", operatingUser.getUserId());
124+
125+
// Validate request
126+
if (request.getMessage() == null || request.getMessage().trim().isEmpty()) {
127+
return ResponseEntity.badRequest()
128+
.body(TooltipChatResponse.builder()
129+
.response("Invalid request: message is required")
130+
.error("Message is required")
131+
.success(false)
132+
.build());
133+
}
134+
135+
// Build token DTO for LLM service
136+
TokenDTO tokenDTO = TokenDTO.builder().communicationId(UUID.randomUUID().toString()).build();
137+
// Token will be populated from security context by LLM service
138+
139+
// Generate chat response
140+
TooltipChatResponse response = tooltipService.chat(request, tokenDTO);
141+
142+
return ResponseEntity.ok(response);
143+
144+
} catch (Exception e) {
145+
log.error("Error processing tooltip chat request", e);
146+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
147+
.body(TooltipChatResponse.builder()
148+
.response("An error occurred while processing your message.")
149+
.error(e.getMessage())
150+
.success(false)
151+
.build());
152+
}
153+
}
154+
155+
/**
156+
* Trigger manual indexing of codebase and documentation.
157+
* Requires admin/system management permissions.
158+
*
159+
* @param httpRequest HTTP request for authentication
160+
* @param httpResponse HTTP response
161+
* @return Indexing result with statistics
162+
*/
163+
@PostMapping("/admin/index")
164+
@Endpoint(description = "Trigger manual indexing of codebase and documentation")
165+
@LimitAccess(applicationAccess = ApplicationAccessEnum.CAN_MANAGE_APPLICATION)
166+
public ResponseEntity<Map<String, Object>> triggerIndexing(
167+
HttpServletRequest httpRequest,
168+
HttpServletResponse httpResponse) {
169+
170+
try {
171+
User operatingUser = getOperatingUser(httpRequest, httpResponse);
172+
173+
// Check if user has admin/system permissions
174+
if (!AccessUtil.canAccess(operatingUser, SSHAccessEnum.CAN_MANAGE_SYSTEMS)) {
175+
log.warn("Non-admin user {} attempted to trigger indexing", operatingUser.getUserId());
176+
return ResponseEntity.status(HttpStatus.FORBIDDEN)
177+
.body(Map.of(
178+
"success", false,
179+
"error", "Admin privileges required to trigger indexing"
180+
));
181+
}
182+
183+
log.info("Indexing triggered by admin user: {}", operatingUser.getUserId());
184+
185+
// Run indexing
186+
CodebaseIndexingService.IndexingResult result = indexingService.indexCodebase();
187+
188+
// Build response
189+
Map<String, Object> response = new HashMap<>();
190+
response.put("success", result.isSuccess());
191+
response.put("message", result.getMessage());
192+
response.put("totalFiles", result.getTotalFiles());
193+
response.put("successCount", result.getSuccessCount());
194+
response.put("errorCount", result.getErrorCount());
195+
196+
if (result.getErrors() != null && !result.getErrors().isEmpty()) {
197+
response.put("errors", result.getErrors());
198+
}
199+
200+
return ResponseEntity.ok(response);
201+
202+
} catch (Exception e) {
203+
log.error("Error triggering indexing", e);
204+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
205+
.body(Map.of(
206+
"success", false,
207+
"error", "Failed to trigger indexing: " + e.getMessage()
208+
));
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)