Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .local.env
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
SENTRIUS_VERSION=1.1.193
SENTRIUS_SSH_VERSION=1.1.35
SENTRIUS_KEYCLOAK_VERSION=1.1.47
SENTRIUS_AGENT_VERSION=1.1.34
SENTRIUS_AI_AGENT_VERSION=1.1.64
LLMPROXY_VERSION=1.0.46
LAUNCHER_VERSION=1.0.51
AGENTPROXY_VERSION=1.0.66
SENTRIUS_VERSION=1.1.261
SENTRIUS_SSH_VERSION=1.1.40
SENTRIUS_KEYCLOAK_VERSION=1.1.52
SENTRIUS_AGENT_VERSION=1.1.39
SENTRIUS_AI_AGENT_VERSION=1.1.148
LLMPROXY_VERSION=1.0.53
LAUNCHER_VERSION=1.0.73
AGENTPROXY_VERSION=1.0.74
16 changes: 8 additions & 8 deletions .local.env.bak
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
SENTRIUS_VERSION=1.1.193
SENTRIUS_SSH_VERSION=1.1.35
SENTRIUS_KEYCLOAK_VERSION=1.1.47
SENTRIUS_AGENT_VERSION=1.1.34
SENTRIUS_AI_AGENT_VERSION=1.1.64
LLMPROXY_VERSION=1.0.46
LAUNCHER_VERSION=1.0.51
AGENTPROXY_VERSION=1.0.66
SENTRIUS_VERSION=1.1.261
SENTRIUS_SSH_VERSION=1.1.40
SENTRIUS_KEYCLOAK_VERSION=1.1.52
SENTRIUS_AGENT_VERSION=1.1.39
SENTRIUS_AI_AGENT_VERSION=1.1.148
LLMPROXY_VERSION=1.0.53
LAUNCHER_VERSION=1.0.73
AGENTPROXY_VERSION=1.0.74
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -50,8 +49,7 @@ public ResponseEntity<?> createPod(
return ResponseEntity.status(HttpStatus.SC_UNAUTHORIZED).body("Invalid Keycloak token");
}

var clientId = agent.getAgentName();
podLauncherService.launchAgentPod(clientId, agent.getAgentCallbackUrl());
podLauncherService.launchAgentPod(agent);

return ResponseEntity.ok(Map.of("status", "success"));
}
Expand All @@ -62,9 +60,18 @@ public ResponseEntity<String> deleteAgent(@RequestParam(name="agentId") String a
podLauncherService.deleteAgentById(agentId);
return ResponseEntity.ok("Shutdown triggered");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(500).body("Shutdown failed: " + e.getMessage());
}
}

@GetMapping("/status")
public ResponseEntity<String> getAgentStatus(@RequestParam(name="agentId") String agentId) {
try {
return ResponseEntity.ok(podLauncherService.statusById(agentId) );
} catch (Exception e) {
log.error("Status failed", e);
return ResponseEntity.status(500).body("Status retrieval failed");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
import io.kubernetes.client.custom.IntOrString;
import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.*;
import io.kubernetes.client.util.Config;
import io.sentrius.sso.core.dto.AgentRegistrationDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -100,7 +103,7 @@ public void deleteAgentById(String agentId) throws Exception {
} catch (Exception ex) {
log.warn("Service not found or already deleted: {}", ex.getMessage());
}
}else {
} else {
log.info("Not Deleting pod: {}", podName);
}

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


}
}


}
public String statusById(String agentId) throws Exception {
// Delete all pods with this agentId label
var pods = coreV1Api.listNamespacedPod(
agentNamespace
).execute().getItems();

for (V1Pod pod : pods) {

var labels = pod.getMetadata().getLabels();
var podName = pod.getMetadata().getName();

Matcher matcher = pattern.matcher(agentId);

if (matcher.matches() && labels != null && labels.containsKey("agentId")) {
String name = matcher.group(1);

var value = labels.get("agentId");
if (value.equals(name)) {
// get pod status
//
V1PodStatus status = pod.getStatus();
if (status == null) {
log.warn("Pod {} has no status information", podName);
return "Unknown";
}
return status.getPhase(); // e.g., "Running", "Pending", "Failed", "Succeeded"

}


}


}
return "NotFound";
}



public V1Pod launchAgentPod(String agentId, String callbackUrl) throws Exception {



public V1Pod launchAgentPod(AgentRegistrationDTO agent) throws Exception {
var myAgentRegistry = "";
if (agentRegistry != null ) {
if ("local".equalsIgnoreCase(agentRegistry)) {
Expand All @@ -125,9 +168,37 @@ public V1Pod launchAgentPod(String agentId, String callbackUrl) throws Exception
myAgentRegistry += "/";
}
}
String agentId = agent.getAgentName().toLowerCase();
String callbackUrl = agent.getAgentCallbackUrl();
String agentType = agent.getAgentType();

var constructedCallbackUrl = buildAgentCallbackUrl(agentId);


List<String> argList = new ArrayList<>();
argList.add("--spring.config.location=file:/config/agent.properties");
argList.add("--agent.namePrefix=" + agentId);
argList.add("--agent.listen.websocket=true");
argList.add("--agent.callback.url=" + constructedCallbackUrl);
if (agent.getAgentContextId() != null && !agent.getAgentContextId().isEmpty()) {
argList.add("--agent.ai.context.db.id=" + agent.getAgentContextId());
}else {
String agentFile= "chat-helper.yaml";
switch(agentType){
case "chat":
agentFile = "chat-helper.yaml";
break;
case "atpl-helper":
agentFile = "chat-atpl-helper.yaml";
break;
case "default":
default:
agentFile = "chat-helper.yaml";
}
argList.add("--agent.ai.config=/config/" + agentFile);
}


String image = String.format("%ssentrius-launchable-agent:%s", myAgentRegistry, agentVersion);

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

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

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

// Create corresponding service for WebSocket routing
V1Service service = new V1Service()
.metadata(new V1ObjectMeta()
.name("sentrius-agent-" + agentId)
.labels(Map.of("agentId", agentId)))
.spec(new V1ServiceSpec()
.selector(Map.of("agentId", agentId))
.ports(List.of(new V1ServicePort()
.protocol("TCP")
.port(8090)
.targetPort(new IntOrString(8090))
))
.type("ClusterIP")
);

log.info("Created service pod: {} and service {}", createdPod, service);
coreV1Api.createNamespacedService(agentNamespace, service).execute();

try {
// Create corresponding service for WebSocket routing
V1Service service = new V1Service()
.metadata(new V1ObjectMeta()
.name("sentrius-agent-" + agentId)
.labels(Map.of("agentId", agentId)))
.spec(new V1ServiceSpec()
.selector(Map.of("agentId", agentId))
.ports(List.of(new V1ServicePort()
.protocol("TCP")
.port(8090)
.targetPort(new IntOrString(8090))
))
.type("ClusterIP")
);

log.info("Created service pod: {} and service {}", createdPod, service);
coreV1Api.createNamespacedService(agentNamespace, service).execute();

}catch(ApiException e){
if (e.getCode() == 409){
log.info("Service for agent {} already exists, skipping creation", agentId);
}
else{
throw e;
}
}
return createdPod;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public Mono<Void> handle(WebSocketSession clientSession) {
log.info("Handling WebSocket connection for host: {}, sessionId: {}, chatGroupId: {}, ztat: {}",
agentHost, sessionId, chatGroupId, ztat);

URI agentUri = agentLocator.resolveWebSocketUri(agentHost, sessionId, chatGroupId, ztat);
URI agentUri = agentLocator.resolveWebSocketUri(agentHost.toLowerCase(), sessionId, chatGroupId, ztat);

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

Expand Down Expand Up @@ -123,22 +123,38 @@ public Mono<Void> handle(WebSocketSession clientSession) {
})
.as(clientSession::send)
.doOnSuccess(aVoid -> log.info("agent -> client completed gracefully")) // Corrected for Mono
.doOnError(e -> log.error("Error in agent -> client stream", e))
.doOnError(e -> {
log.error("Error in agent -> client stream", e);
sessionManager.unregister(agentSession.getId());
})
.onErrorResume(e -> {
sessionManager.unregister(agentSession.getId());
log.error("Agent to client stream error, closing agent session.", e);
return agentSession.close().then(Mono.empty());
})
.doFinally(sig -> log.info("Agent to client stream finalized: {}", sig));

return Mono.when(clientToAgent, agentToClient)
.doOnTerminate(() -> log.info("WebSocket proxy connection terminated (client and agent streams completed/cancelled)"))
.doOnError(e -> log.error("Overall proxy connection failed", e))
.doOnTerminate(() -> {
log.info("WebSocket proxy connection terminated (client and agent " +
"streams completed/cancelled)");
sessionManager.unregister(agentSession.getId());

})
.doOnError(e -> {
log.error("Overall proxy connection failed", e);
sessionManager.unregister(agentSession.getId());

})
.doFinally(sig -> {
sessionManager.unregister(finalSessionId);
log.info("WebSocket proxy stream closed completely: {}. Final session ID: {}", sig, finalSessionId);
});
}
).doOnError(e -> log.error("Failed to establish proxy connection", e));
).doOnError(e -> {
log.error("Failed to establish proxy connection", e);
sessionManager.unregister(finalSessionId);
});


} catch (Exception ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ private ReactiveJwtAuthenticationConverter grantedAuthoritiesExtractor() {

converter.setJwtGrantedAuthoritiesConverter(jwt -> {
Collection<GrantedAuthority> authorities = new JwtGrantedAuthoritiesConverter().convert(jwt);
log.info("JWT Claims: {}", jwt.getClaims());

String username = jwt.getClaimAsString("preferred_username");
String email = jwt.getClaimAsString("email");

return Flux.fromIterable(authorities);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ public class AgentVerb {
@Builder.Default
Class<? extends OutputInterpreterIfc> outputInterpreter = DefaultInterpreter.class;
Class<? extends InputInterpreterIfc> inputInterpreter = DefaultInterpreter.class;

private String exampleJson = "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
}
}

try {
verbRegistry.scanEndpoints(agentExecution);
} catch (ZtatException e) {
throw new RuntimeException(e);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}

while(running) {

log.info("Agent Registered...");
Expand Down
Loading