refactor: restructure MCP server by replacing client-specific tool logic with a centralized campaign service and standardized models.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package dev.sonpx.loyalty.agent.config;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
@@ -25,7 +26,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
@Override
|
||||
protected java.security.Principal determineUser(org.springframework.http.server.ServerHttpRequest request, org.springframework.web.socket.WebSocketHandler wsHandler, java.util.Map<String, Object> attributes) {
|
||||
return new java.security.Principal() {
|
||||
private final String id = java.util.UUID.randomUUID().toString();
|
||||
private final String id = UUID.randomUUID().toString();
|
||||
@Override
|
||||
public String getName() {
|
||||
return id;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package dev.sonpx.loyalty.agent.controller;
|
||||
|
||||
import dev.sonpx.loyalty.agent.service.LoyaltyAgentService;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import java.security.Principal;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin(origins = "*")
|
||||
@@ -24,7 +24,7 @@ public class AgentController {
|
||||
public record ChatRequest(String conversationId, String messageId, String prompt, String activeCampaignId) {}
|
||||
|
||||
@MessageMapping("/chat")
|
||||
public void chat(@Payload ChatRequest request, java.security.Principal principal) {
|
||||
public void chat(@Payload ChatRequest request, Principal principal) {
|
||||
agentService.chat(request, principal.getName());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
package dev.sonpx.loyalty.agent.service;
|
||||
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.tool.ToolCallbackProvider;
|
||||
import org.springframework.stereotype.Service;
|
||||
import dev.sonpx.loyalty.agent.controller.AgentController;
|
||||
import dev.sonpx.loyalty.agent.domain.AgentEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.tool.ToolCallback;
|
||||
import org.springframework.ai.tool.ToolCallbackProvider;
|
||||
import org.springframework.ai.tool.definition.ToolDefinition;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class LoyaltyAgentService {
|
||||
|
||||
private final org.springframework.ai.chat.model.ChatModel chatModel;
|
||||
private final ChatModel chatModel;
|
||||
private final List<ToolCallbackProvider> toolProviders;
|
||||
private final org.springframework.messaging.simp.SimpMessagingTemplate messagingTemplate;
|
||||
private final SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
public LoyaltyAgentService(org.springframework.ai.chat.model.ChatModel chatModel, List<ToolCallbackProvider> toolProviders, org.springframework.messaging.simp.SimpMessagingTemplate messagingTemplate) {
|
||||
this.chatModel = chatModel;
|
||||
this.toolProviders = toolProviders;
|
||||
this.messagingTemplate = messagingTemplate;
|
||||
}
|
||||
|
||||
public void chat(dev.sonpx.loyalty.agent.controller.AgentController.ChatRequest request, String sessionId) {
|
||||
public void chat(AgentController.ChatRequest request, String sessionId) {
|
||||
String prompt = request.prompt();
|
||||
if (request.activeCampaignId() != null && !request.activeCampaignId().isEmpty()) {
|
||||
prompt = "Context: Active Campaign ID is " + request.activeCampaignId() + ".\n\n" + prompt;
|
||||
@@ -27,21 +30,21 @@ public class LoyaltyAgentService {
|
||||
prompt = "System Context: The current conversationId (session ID) is " + request.conversationId() + ". You MUST provide this conversationId as a parameter to any tool that requires it.\n\n" + prompt;
|
||||
}
|
||||
|
||||
List<org.springframework.ai.tool.ToolCallback> wrappedTools = new java.util.ArrayList<>();
|
||||
List<ToolCallback> wrappedTools = new ArrayList<>();
|
||||
for (ToolCallbackProvider provider : toolProviders) {
|
||||
for (org.springframework.ai.tool.ToolCallback tool : provider.getToolCallbacks()) {
|
||||
wrappedTools.add(new org.springframework.ai.tool.ToolCallback() {
|
||||
for (ToolCallback tool : provider.getToolCallbacks()) {
|
||||
wrappedTools.add(new ToolCallback() {
|
||||
@Override
|
||||
public org.springframework.ai.tool.definition.ToolDefinition getToolDefinition() {
|
||||
public ToolDefinition getToolDefinition() {
|
||||
return tool.getToolDefinition();
|
||||
}
|
||||
@Override
|
||||
public String call(String toolInput) {
|
||||
messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "running"));
|
||||
messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "running"));
|
||||
try {
|
||||
return tool.call(toolInput);
|
||||
} finally {
|
||||
messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "done"));
|
||||
messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "done"));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -54,14 +57,8 @@ public class LoyaltyAgentService {
|
||||
Your primary job is to orchestrate operations for our Loyalty core system.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. DO NOT create a model.client.dev.sonpx.loyalty.agent.Campaign unless you verify that Member Tiers and Point Pools exist first.
|
||||
2. Use the `listMemberTiers` tool to check for valid tier IDs.
|
||||
3. Use the `listPointPools` tool to check for valid pool IDs and ensure they have a sufficient balance.
|
||||
4. Use the `listStaticAttributes` tool when asked about configurations, properties, or static values.
|
||||
5. Only use the `createCampaign` tool once verification is complete.
|
||||
6. Always answer the user in the language they used (e.g., Vietnamese).
|
||||
7. If a tool response contains a `presentation.content` field, you MUST display its content exactly as provided to the user. Do not summarize or alter its formatting. Use the `data` field only for your internal reasoning.
|
||||
8. When asked for information, details, or status of an existing campaign, ALWAYS use the `listCampaigns` tool. DO NOT use `createCampaign` unless explicitly asked to create or add a new one.
|
||||
1. Always answer the user in the language they used (e.g., Vietnamese).
|
||||
2. If a tool response contains a `presentation.markdown` field, you MUST display its content exactly as provided to the user. Do not summarize or alter its formatting. Use the `data` field only for your internal reasoning.
|
||||
""")
|
||||
.defaultTools(wrappedTools)
|
||||
.build();
|
||||
@@ -70,14 +67,14 @@ public class LoyaltyAgentService {
|
||||
.user(prompt)
|
||||
.stream()
|
||||
.content()
|
||||
.doOnComplete(() -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.done(request.messageId())))
|
||||
.doOnComplete(() -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.done(request.messageId())))
|
||||
.subscribe(
|
||||
content -> {
|
||||
if (content != null && !content.isEmpty()) {
|
||||
messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.token(request.messageId(), content));
|
||||
messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.token(request.messageId(), content));
|
||||
}
|
||||
},
|
||||
error -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.error(request.messageId(), error.getMessage()))
|
||||
error -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.error(request.messageId(), error.getMessage()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user