feat: initialize loyalty agent service structure, security configuration, and campaign MCP service orchestration
This commit is contained in:
@@ -2,7 +2,6 @@ package dev.sonpx.loyalty.agent.config;
|
|||||||
|
|
||||||
import org.springframework.ai.chat.client.ChatClient;
|
import org.springframework.ai.chat.client.ChatClient;
|
||||||
import org.springframework.ai.chat.model.ChatModel;
|
import org.springframework.ai.chat.model.ChatModel;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@@ -18,39 +17,10 @@ public class AgentConfig {
|
|||||||
2. When a tool result contains an `agent_instruction`, you MUST strictly follow its formatting rules when presenting the relevant data to the user. Do not output raw JSON data unless explicitly asked.
|
2. When a tool result contains an `agent_instruction`, you MUST strictly follow its formatting rules when presenting the relevant data to the user. Do not output raw JSON data unless explicitly asked.
|
||||||
""";
|
""";
|
||||||
|
|
||||||
@Value("${loyalty.core.base-url}")
|
|
||||||
private String coreBaseUrl;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ChatClient loyaltyChatClient(ChatModel chatModel) {
|
public ChatClient loyaltyChatClient(ChatModel chatModel) {
|
||||||
return ChatClient.builder(chatModel)
|
return ChatClient.builder(chatModel)
|
||||||
.defaultSystem(AGENT_SYSTEM_PROMPT)
|
.defaultSystem(AGENT_SYSTEM_PROMPT)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Bean
|
|
||||||
// public RestClient coreRestClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
||||||
// return RestClient.builder()
|
|
||||||
// .baseUrl(coreBaseUrl)
|
|
||||||
// .requestInterceptor((request, body, execution) -> {
|
|
||||||
// org.springframework.security.core.Authentication principal =
|
|
||||||
// new org.springframework.security.authentication.AnonymousAuthenticationToken(
|
|
||||||
// "key", "loyalty-agent-service",
|
|
||||||
// org.springframework.security.core.authority.AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
|
|
||||||
// OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
|
|
||||||
// .withClientRegistrationId("keycloak")
|
|
||||||
// .principal(principal)
|
|
||||||
// .build();
|
|
||||||
// try {
|
|
||||||
// OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
|
|
||||||
// if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
|
|
||||||
// request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
|
|
||||||
// }
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// return execution.execute(request, body);
|
|
||||||
// })
|
|
||||||
// .build();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,33 +12,13 @@ import org.springframework.security.web.SecurityFilterChain;
|
|||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
http
|
http
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.authorizeHttpRequests(authz -> authz
|
.authorizeHttpRequests(authz -> authz
|
||||||
.requestMatchers("/api/v1/agent/**", "/api/v1/conversations/**", "/ws/**", "/error").permitAll()
|
.requestMatchers("/api/v1/agent/**", "/api/v1/conversations/**", "/ws/**", "/error").permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
);
|
);
|
||||||
// .oauth2Login(Customizer.withDefaults())
|
|
||||||
// .oauth2Client(Customizer.withDefaults());
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Bean
|
|
||||||
// public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
||||||
// ClientRegistrationRepository clientRegistrationRepository,
|
|
||||||
// OAuth2AuthorizedClientService authorizedClientService) {
|
|
||||||
//
|
|
||||||
// OAuth2AuthorizedClientProvider authorizedClientProvider =
|
|
||||||
// OAuth2AuthorizedClientProviderBuilder.builder()
|
|
||||||
// .clientCredentials()
|
|
||||||
// .build();
|
|
||||||
//
|
|
||||||
// AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
|
|
||||||
// new AuthorizedClientServiceOAuth2AuthorizedClientManager(
|
|
||||||
// clientRegistrationRepository, authorizedClientService);
|
|
||||||
// authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
|
||||||
//
|
|
||||||
// return authorizedClientManager;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,21 +23,6 @@ spring:
|
|||||||
connections:
|
connections:
|
||||||
loyalty:
|
loyalty:
|
||||||
url: ${MCP_SERVER_URL:http://localhost:9331}
|
url: ${MCP_SERVER_URL:http://localhost:9331}
|
||||||
# security:
|
|
||||||
# oauth2:
|
|
||||||
# client:
|
|
||||||
# registration:
|
|
||||||
# keycloak:
|
|
||||||
# client-id: ols-cli
|
|
||||||
# client-secret: ${KEYCLOAK_CLIENT_SECRET:1jV8NSAeybIqmUK0AWhI8hgdo1q5itj7}
|
|
||||||
# authorization-grant-type: client_credentials
|
|
||||||
# provider:
|
|
||||||
# keycloak:
|
|
||||||
# token-uri: ${KEYCLOAK_TOKEN_URI:http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token}
|
|
||||||
|
|
||||||
#loyalty:
|
|
||||||
# core:
|
|
||||||
# base-url: ${LOYALTY_CORE_BASE_URL:http://192.168.99.242:8081}
|
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
pattern:
|
pattern:
|
||||||
|
|||||||
@@ -23,7 +23,23 @@ public class CampaignService {
|
|||||||
|
|
||||||
public Result<List<Campaign>> getAll(CampaignCriteria criteria) {
|
public Result<List<Campaign>> getAll(CampaignCriteria criteria) {
|
||||||
List<Campaign> campaigns = campaignClient.getAll(criteria, Pageable.ofSize(5));
|
List<Campaign> campaigns = campaignClient.getAll(criteria, Pageable.ofSize(5));
|
||||||
return Result.of(campaigns, "Format these campaigns as a markdown table with columns ID, Name, Start Date, End Date, Status.");
|
return Result.of(campaigns, """
|
||||||
|
Khi trả về danh sách chiến dịch, BẮT BUỘC KHÔNG dùng markdown table. Hãy định dạng theo cấu trúc sau:
|
||||||
|
|
||||||
|
1. **[Tên Chiến Dịch]** (ID: `[Mã ID]`)
|
||||||
|
- 👤 Owner: [Tên Owner] (Nếu trống, ghi là: Không có)
|
||||||
|
- 📝 Mô tả: [Mô tả] (Nếu trống, ghi là: Không có. Nếu dài hơn 100 ký tự, thêm "...")
|
||||||
|
Ví dụ đầu ra ĐÚNG:
|
||||||
|
1. **Happy Birthday!** (ID: `LACO`)
|
||||||
|
- 👤 Owner: Không có
|
||||||
|
- 📝 Mô tả: Không có
|
||||||
|
2. **SummerSale GOTIT Campaign** (ID: `YZFG`)
|
||||||
|
- 👤 Owner: Ngan Ha
|
||||||
|
- 📝 Mô tả: Không có
|
||||||
|
|
||||||
|
Luôn kết thúc bằng một câu hỏi gợi mở để hướng dẫn người dùng bước tiếp theo.
|
||||||
|
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Long> count(CampaignCriteria criteria) {
|
public Result<Long> count(CampaignCriteria criteria) {
|
||||||
|
|||||||
101
start-all.ps1
Normal file
101
start-all.ps1
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$ProjectRoot = $PSScriptRoot
|
||||||
|
$LogDir = Join-Path $ProjectRoot "log"
|
||||||
|
|
||||||
|
if (-not (Test-Path $LogDir)) {
|
||||||
|
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path (Join-Path $ProjectRoot ".env")) {
|
||||||
|
Write-Host "Loading environment variables from .env"
|
||||||
|
Get-Content (Join-Path $ProjectRoot ".env") | Where-Object { $_ -match "^[^#]" -and $_ -match "=" } | ForEach-Object {
|
||||||
|
$name, $value = $_.Split("=", 2)
|
||||||
|
[System.Environment]::SetEnvironmentVariable($name, $value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host "Cleaning up existing processes..."
|
||||||
|
Write-Host "=========================================="
|
||||||
|
foreach ($port in 9331, 9332, 9333) {
|
||||||
|
$netstat = netstat -ano | Select-String "LISTENING" | Select-String ":$port"
|
||||||
|
if ($netstat) {
|
||||||
|
foreach ($line in $netstat) {
|
||||||
|
$parts = $line -split '\s+'
|
||||||
|
$pidToKill = $parts[-1]
|
||||||
|
if ($pidToKill -and $pidToKill -ne "0") {
|
||||||
|
Write-Host "Killing process on port $port (PID: $pidToKill)..."
|
||||||
|
Stop-Process -Id $pidToKill -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ProcessList = @()
|
||||||
|
|
||||||
|
try {
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host "Starting Loyalty Agent Services"
|
||||||
|
Write-Host "=========================================="
|
||||||
|
|
||||||
|
# 1. Start MCP Server
|
||||||
|
Write-Host "Starting loyalty-mcp-server..."
|
||||||
|
$mcpLog = Join-Path $LogDir "loyalty-mcp-server.log"
|
||||||
|
$mcpProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c ..\mvnw.cmd spring-boot:run -Dmaven.test.skip=true > `"$mcpLog`" 2>&1" -WorkingDirectory (Join-Path $ProjectRoot "loyalty-mcp-server") -PassThru -WindowStyle Hidden
|
||||||
|
$ProcessList += $mcpProcess
|
||||||
|
Write-Host "loyalty-mcp-server started with PID: $($mcpProcess.Id)"
|
||||||
|
|
||||||
|
Write-Host "Waiting for loyalty-mcp-server to initialize..."
|
||||||
|
while ($true) {
|
||||||
|
$netstat = netstat -ano | Select-String "LISTENING" | Select-String ":9331"
|
||||||
|
if ($netstat) { break }
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
Write-Host "loyalty-mcp-server is up!"
|
||||||
|
|
||||||
|
# 2. Start Agent Service
|
||||||
|
Write-Host "Starting loyalty-agent..."
|
||||||
|
$agentLog = Join-Path $LogDir "loyalty-agent.log"
|
||||||
|
$agentProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c ..\mvnw.cmd spring-boot:run -Dmaven.test.skip=true > `"$agentLog`" 2>&1" -WorkingDirectory (Join-Path $ProjectRoot "loyalty-agent") -PassThru -WindowStyle Hidden
|
||||||
|
$ProcessList += $agentProcess
|
||||||
|
Write-Host "loyalty-agent started with PID: $($agentProcess.Id)"
|
||||||
|
|
||||||
|
Write-Host "Waiting for loyalty-agent to initialize..."
|
||||||
|
while ($true) {
|
||||||
|
$netstat = netstat -ano | Select-String "LISTENING" | Select-String ":9332"
|
||||||
|
if ($netstat) { break }
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
Write-Host "loyalty-agent is up!"
|
||||||
|
|
||||||
|
# 3. Start Frontend UI
|
||||||
|
Write-Host "Starting Frontend UI..."
|
||||||
|
$uiLog = Join-Path $LogDir "frontend.log"
|
||||||
|
$uiProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c pnpm dev > `"$uiLog`" 2>&1" -WorkingDirectory $ProjectRoot -PassThru -WindowStyle Hidden
|
||||||
|
$ProcessList += $uiProcess
|
||||||
|
Write-Host "Frontend UI started with PID: $($uiProcess.Id)"
|
||||||
|
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host "All services started successfully!"
|
||||||
|
Write-Host "Log files are stored in: $LogDir"
|
||||||
|
Write-Host "- $mcpLog"
|
||||||
|
Write-Host "- $agentLog"
|
||||||
|
Write-Host "- $uiLog"
|
||||||
|
Write-Host "=========================================="
|
||||||
|
Write-Host "Press Ctrl+C to stop all services..."
|
||||||
|
|
||||||
|
# Wait indefinitely
|
||||||
|
while ($true) {
|
||||||
|
Start-Sleep -Seconds 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Write-Host "`nStopping services..."
|
||||||
|
foreach ($p in $ProcessList) {
|
||||||
|
if (-not $p.HasExited) {
|
||||||
|
taskkill /PID $p.Id /T /F 2>&1 | Out-Null
|
||||||
|
Write-Host "Stopped process tree for PID $($p.Id)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "All services stopped."
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user