Skip to content

Commit 781b32f

Browse files
authored
Merge pull request #84 from SentriusLLC/copilot/fix-83
Implement modern chat-based ATPL configuration with enhanced capabilities model
2 parents e59562b + 3bb5a99 commit 781b32f

File tree

86 files changed

+3996
-475
lines changed

Some content is hidden

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

86 files changed

+3996
-475
lines changed

.local.env

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
SENTRIUS_VERSION=1.1.193
2-
SENTRIUS_SSH_VERSION=1.1.35
3-
SENTRIUS_KEYCLOAK_VERSION=1.1.47
4-
SENTRIUS_AGENT_VERSION=1.1.34
5-
SENTRIUS_AI_AGENT_VERSION=1.1.64
6-
LLMPROXY_VERSION=1.0.46
7-
LAUNCHER_VERSION=1.0.51
8-
AGENTPROXY_VERSION=1.0.66
1+
SENTRIUS_VERSION=1.1.261
2+
SENTRIUS_SSH_VERSION=1.1.40
3+
SENTRIUS_KEYCLOAK_VERSION=1.1.52
4+
SENTRIUS_AGENT_VERSION=1.1.39
5+
SENTRIUS_AI_AGENT_VERSION=1.1.148
6+
LLMPROXY_VERSION=1.0.53
7+
LAUNCHER_VERSION=1.0.73
8+
AGENTPROXY_VERSION=1.0.74

.local.env.bak

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
SENTRIUS_VERSION=1.1.193
2-
SENTRIUS_SSH_VERSION=1.1.35
3-
SENTRIUS_KEYCLOAK_VERSION=1.1.47
4-
SENTRIUS_AGENT_VERSION=1.1.34
5-
SENTRIUS_AI_AGENT_VERSION=1.1.64
6-
LLMPROXY_VERSION=1.0.46
7-
LAUNCHER_VERSION=1.0.51
8-
AGENTPROXY_VERSION=1.0.66
1+
SENTRIUS_VERSION=1.1.261
2+
SENTRIUS_SSH_VERSION=1.1.40
3+
SENTRIUS_KEYCLOAK_VERSION=1.1.52
4+
SENTRIUS_AGENT_VERSION=1.1.39
5+
SENTRIUS_AI_AGENT_VERSION=1.1.148
6+
LLMPROXY_VERSION=1.0.53
7+
LAUNCHER_VERSION=1.0.73
8+
AGENTPROXY_VERSION=1.0.74

agent-launcher/src/main/java/io/sentrius/agent/launcher/api/AgentLauncherController.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import lombok.extern.slf4j.Slf4j;
1313
import org.apache.http.HttpStatus;
1414
import org.springframework.http.ResponseEntity;
15-
import org.springframework.web.bind.annotation.DeleteMapping;
1615
import org.springframework.web.bind.annotation.GetMapping;
1716
import org.springframework.web.bind.annotation.PostMapping;
1817
import org.springframework.web.bind.annotation.RequestBody;
@@ -50,8 +49,7 @@ public ResponseEntity<?> createPod(
5049
return ResponseEntity.status(HttpStatus.SC_UNAUTHORIZED).body("Invalid Keycloak token");
5150
}
5251

53-
var clientId = agent.getAgentName();
54-
podLauncherService.launchAgentPod(clientId, agent.getAgentCallbackUrl());
52+
podLauncherService.launchAgentPod(agent);
5553

5654
return ResponseEntity.ok(Map.of("status", "success"));
5755
}
@@ -62,9 +60,18 @@ public ResponseEntity<String> deleteAgent(@RequestParam(name="agentId") String a
6260
podLauncherService.deleteAgentById(agentId);
6361
return ResponseEntity.ok("Shutdown triggered");
6462
} catch (Exception e) {
65-
e.printStackTrace();
6663
return ResponseEntity.status(500).body("Shutdown failed: " + e.getMessage());
6764
}
6865
}
6966

67+
@GetMapping("/status")
68+
public ResponseEntity<String> getAgentStatus(@RequestParam(name="agentId") String agentId) {
69+
try {
70+
return ResponseEntity.ok(podLauncherService.statusById(agentId) );
71+
} catch (Exception e) {
72+
log.error("Status failed", e);
73+
return ResponseEntity.status(500).body("Status retrieval failed");
74+
}
75+
}
76+
7077
}

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

Lines changed: 103 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
import io.kubernetes.client.custom.IntOrString;
44
import io.kubernetes.client.custom.Quantity;
55
import io.kubernetes.client.openapi.ApiClient;
6+
import io.kubernetes.client.openapi.ApiException;
67
import io.kubernetes.client.openapi.apis.CoreV1Api;
78
import io.kubernetes.client.openapi.models.*;
89
import io.kubernetes.client.util.Config;
10+
import io.sentrius.sso.core.dto.AgentRegistrationDTO;
911
import lombok.extern.slf4j.Slf4j;
1012
import org.springframework.beans.factory.annotation.Value;
1113
import org.springframework.stereotype.Service;
1214

1315
import java.io.IOException;
16+
import java.util.ArrayList;
1417
import java.util.List;
1518
import java.util.Map;
1619
import java.util.regex.Matcher;
@@ -100,7 +103,7 @@ public void deleteAgentById(String agentId) throws Exception {
100103
} catch (Exception ex) {
101104
log.warn("Service not found or already deleted: {}", ex.getMessage());
102105
}
103-
}else {
106+
} else {
104107
log.info("Not Deleting pod: {}", podName);
105108
}
106109

@@ -111,12 +114,52 @@ public void deleteAgentById(String agentId) throws Exception {
111114

112115

113116
}
117+
}
114118

115119

116-
}
120+
public String statusById(String agentId) throws Exception {
121+
// Delete all pods with this agentId label
122+
var pods = coreV1Api.listNamespacedPod(
123+
agentNamespace
124+
).execute().getItems();
125+
126+
for (V1Pod pod : pods) {
127+
128+
var labels = pod.getMetadata().getLabels();
129+
var podName = pod.getMetadata().getName();
130+
131+
Matcher matcher = pattern.matcher(agentId);
132+
133+
if (matcher.matches() && labels != null && labels.containsKey("agentId")) {
134+
String name = matcher.group(1);
135+
136+
var value = labels.get("agentId");
137+
if (value.equals(name)) {
138+
// get pod status
139+
//
140+
V1PodStatus status = pod.getStatus();
141+
if (status == null) {
142+
log.warn("Pod {} has no status information", podName);
143+
return "Unknown";
144+
}
145+
return status.getPhase(); // e.g., "Running", "Pending", "Failed", "Succeeded"
146+
147+
}
148+
149+
150+
}
151+
152+
153+
}
154+
return "NotFound";
155+
}
156+
117157

118158

119-
public V1Pod launchAgentPod(String agentId, String callbackUrl) throws Exception {
159+
160+
161+
162+
public V1Pod launchAgentPod(AgentRegistrationDTO agent) throws Exception {
120163
var myAgentRegistry = "";
121164
if (agentRegistry != null ) {
122165
if ("local".equalsIgnoreCase(agentRegistry)) {
@@ -125,9 +168,37 @@ public V1Pod launchAgentPod(String agentId, String callbackUrl) throws Exception
125168
myAgentRegistry += "/";
126169
}
127170
}
171+
String agentId = agent.getAgentName().toLowerCase();
172+
String callbackUrl = agent.getAgentCallbackUrl();
173+
String agentType = agent.getAgentType();
128174

129175
var constructedCallbackUrl = buildAgentCallbackUrl(agentId);
130176

177+
178+
List<String> argList = new ArrayList<>();
179+
argList.add("--spring.config.location=file:/config/agent.properties");
180+
argList.add("--agent.namePrefix=" + agentId);
181+
argList.add("--agent.listen.websocket=true");
182+
argList.add("--agent.callback.url=" + constructedCallbackUrl);
183+
if (agent.getAgentContextId() != null && !agent.getAgentContextId().isEmpty()) {
184+
argList.add("--agent.ai.context.db.id=" + agent.getAgentContextId());
185+
}else {
186+
String agentFile= "chat-helper.yaml";
187+
switch(agentType){
188+
case "chat":
189+
agentFile = "chat-helper.yaml";
190+
break;
191+
case "atpl-helper":
192+
agentFile = "chat-atpl-helper.yaml";
193+
break;
194+
case "default":
195+
default:
196+
agentFile = "chat-helper.yaml";
197+
}
198+
argList.add("--agent.ai.config=/config/" + agentFile);
199+
}
200+
201+
131202
String image = String.format("%ssentrius-launchable-agent:%s", myAgentRegistry, agentVersion);
132203

133204
log.info("Launching agent pod with ID: {}, Image: {}, Callback URL: {}", agentId, image, callbackUrl);
@@ -141,10 +212,7 @@ public V1Pod launchAgentPod(String agentId, String callbackUrl) throws Exception
141212
.image(image)
142213
.imagePullPolicy("IfNotPresent")
143214

144-
.args(List.of("--spring.config.location=file:/config/agent.properties",
145-
"--agent.namePrefix=" + agentId, "--agent.ai.config=/config/chat-helper.yaml", "--agent.listen.websocket=true",
146-
"--agent.callback.url=" + constructedCallbackUrl
147-
))
215+
.args(argList)
148216
.resources(new V1ResourceRequirements()
149217
.limits(Map.of(
150218
"cpu", Quantity.fromString("1000m"),
@@ -169,24 +237,34 @@ public V1Pod launchAgentPod(String agentId, String callbackUrl) throws Exception
169237

170238
var createdPod = coreV1Api.createNamespacedPod(agentNamespace, pod).execute();
171239

172-
// Create corresponding service for WebSocket routing
173-
V1Service service = new V1Service()
174-
.metadata(new V1ObjectMeta()
175-
.name("sentrius-agent-" + agentId)
176-
.labels(Map.of("agentId", agentId)))
177-
.spec(new V1ServiceSpec()
178-
.selector(Map.of("agentId", agentId))
179-
.ports(List.of(new V1ServicePort()
180-
.protocol("TCP")
181-
.port(8090)
182-
.targetPort(new IntOrString(8090))
183-
))
184-
.type("ClusterIP")
185-
);
186-
187-
log.info("Created service pod: {} and service {}", createdPod, service);
188-
coreV1Api.createNamespacedService(agentNamespace, service).execute();
189-
240+
try {
241+
// Create corresponding service for WebSocket routing
242+
V1Service service = new V1Service()
243+
.metadata(new V1ObjectMeta()
244+
.name("sentrius-agent-" + agentId)
245+
.labels(Map.of("agentId", agentId)))
246+
.spec(new V1ServiceSpec()
247+
.selector(Map.of("agentId", agentId))
248+
.ports(List.of(new V1ServicePort()
249+
.protocol("TCP")
250+
.port(8090)
251+
.targetPort(new IntOrString(8090))
252+
))
253+
.type("ClusterIP")
254+
);
255+
256+
log.info("Created service pod: {} and service {}", createdPod, service);
257+
coreV1Api.createNamespacedService(agentNamespace, service).execute();
258+
259+
}catch(ApiException e){
260+
if (e.getCode() == 409){
261+
log.info("Service for agent {} already exists, skipping creation", agentId);
262+
}
263+
else{
264+
throw e;
265+
}
266+
}
190267
return createdPod;
191268
}
269+
192270
}

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public Mono<Void> handle(WebSocketSession clientSession) {
6565
log.info("Handling WebSocket connection for host: {}, sessionId: {}, chatGroupId: {}, ztat: {}",
6666
agentHost, sessionId, chatGroupId, ztat);
6767

68-
URI agentUri = agentLocator.resolveWebSocketUri(agentHost, sessionId, chatGroupId, ztat);
68+
URI agentUri = agentLocator.resolveWebSocketUri(agentHost.toLowerCase(), sessionId, chatGroupId, ztat);
6969

7070
log.info("Resolved agent URI: {}", agentUri);
7171

@@ -123,22 +123,38 @@ public Mono<Void> handle(WebSocketSession clientSession) {
123123
})
124124
.as(clientSession::send)
125125
.doOnSuccess(aVoid -> log.info("agent -> client completed gracefully")) // Corrected for Mono
126-
.doOnError(e -> log.error("Error in agent -> client stream", e))
126+
.doOnError(e -> {
127+
log.error("Error in agent -> client stream", e);
128+
sessionManager.unregister(agentSession.getId());
129+
})
127130
.onErrorResume(e -> {
131+
sessionManager.unregister(agentSession.getId());
128132
log.error("Agent to client stream error, closing agent session.", e);
129133
return agentSession.close().then(Mono.empty());
130134
})
131135
.doFinally(sig -> log.info("Agent to client stream finalized: {}", sig));
132136

133137
return Mono.when(clientToAgent, agentToClient)
134-
.doOnTerminate(() -> log.info("WebSocket proxy connection terminated (client and agent streams completed/cancelled)"))
135-
.doOnError(e -> log.error("Overall proxy connection failed", e))
138+
.doOnTerminate(() -> {
139+
log.info("WebSocket proxy connection terminated (client and agent " +
140+
"streams completed/cancelled)");
141+
sessionManager.unregister(agentSession.getId());
142+
143+
})
144+
.doOnError(e -> {
145+
log.error("Overall proxy connection failed", e);
146+
sessionManager.unregister(agentSession.getId());
147+
148+
})
136149
.doFinally(sig -> {
137150
sessionManager.unregister(finalSessionId);
138151
log.info("WebSocket proxy stream closed completely: {}. Final session ID: {}", sig, finalSessionId);
139152
});
140153
}
141-
).doOnError(e -> log.error("Failed to establish proxy connection", e));
154+
).doOnError(e -> {
155+
log.error("Failed to establish proxy connection", e);
156+
sessionManager.unregister(finalSessionId);
157+
});
142158

143159

144160
} catch (Exception ex) {

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ private ReactiveJwtAuthenticationConverter grantedAuthoritiesExtractor() {
5555

5656
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
5757
Collection<GrantedAuthority> authorities = new JwtGrantedAuthoritiesConverter().convert(jwt);
58-
log.info("JWT Claims: {}", jwt.getClaims());
59-
60-
String username = jwt.getClaimAsString("preferred_username");
61-
String email = jwt.getClaimAsString("email");
6258

6359
return Flux.fromIterable(authorities);
6460
});

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ public class AgentVerb {
2424
@Builder.Default
2525
Class<? extends OutputInterpreterIfc> outputInterpreter = DefaultInterpreter.class;
2626
Class<? extends InputInterpreterIfc> inputInterpreter = DefaultInterpreter.class;
27+
28+
private String exampleJson = "";
2729
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
135135
}
136136
}
137137

138+
try {
139+
verbRegistry.scanEndpoints(agentExecution);
140+
} catch (ZtatException e) {
141+
throw new RuntimeException(e);
142+
} catch (JsonProcessingException e) {
143+
throw new RuntimeException(e);
144+
}
145+
138146
while(running) {
139147

140148
log.info("Agent Registered...");

0 commit comments

Comments
 (0)