This commit is contained in:
juheelee
2026-08-20 15:02:41 +09:00

View File

@@ -14,14 +14,14 @@ import java.util.Map;
/**
* @package io.shinhanlife.dat.mcg.service
* @className ToolPlanner
* @description AX HUB ?<3F>스??처리 ?<3F>래??
* @description AX HUB 시스템 처리 클래스
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정?<3F>력 ----------
* ?<3F>정?? ?<3F>정?? ?<3F>정?<3F>
* ---------- 개정력 ----------
* 수정일 수정자 수정내
* ---------- -------- ---------------------------
* 2026.09.01 0986406 최초?<3F>
* 2026.09.01 0986406 최초
*
* </pre>
*/
@@ -35,25 +35,25 @@ public class ToolPlanner {
private final GatewayFallbackProperties fallbackProperties;
/**
* ?<3F>청(payload)??분석?<3F>여 ?<3F>행?<3F>야 ??Tool??계획???<3F>성?<3F>니??
* 청(payload)을 분석하여 실행해야 할 Tool의 계획을 생성합니다.
*/
public Object createPlan(Map<String, Object> payload, String tenantId) {
log.info(" [Planner] ?<3F>청 분석 <EFBFBD>??<3F>행 계획 ?<3F>립 ?<3F>");
log.info(" [Planner] 청 분석 및 실행 계획 수립 시");
// 1. ?<3F>청?<3F>서 ?<3F>출?<3F>려?????<3F>름 추출 (JSON-RPC params.name)
// 1. 요청에서 호출하려는 툴 이름 추출 (JSON-RPC params.name)
Map<String, Object> params = (Map<String, Object>) payload.get("params");
String toolName = params != null ? (String) params.get("name") : null;
if (toolName == null || toolName.isEmpty()) {
throw new IllegalArgumentException("?<3F>청??toolName???<3F>함?<3F>어 ?<3F><>? ?<3F>습?<3F>다.");
throw new IllegalArgumentException("요청에 toolName이 포함되어 있지 않습니다.");
}
// 2. RedisRegistry?<3F>서 ?<3F>당 ?<3F>의 메<>??<3F>이??조회
// (?<3F>제로는 ??메<>??<3F>이?<3F><>? ?<3F>행 계획???<3F>심???<3F>니??
// 2. RedisRegistry에서 해당 툴의 메타데이터 조회
// (제로는 이 메타데이터가 실행 계획의 핵심이 됩니다)
var toolMetadata = InMemoryRegistryService.getToolByName(toolName);
if (toolMetadata == null) {
log.warn(" [Planner] ?<3F>록?<3F><>? ?<3F><>? ???<3F>청: {}. Fallback ?<3F>우??규칙???<3F>인?<3F>니??", toolName);
log.warn(" [Planner] 등록되지 않은 툴 요청: {}. Fallback 라우팅 규칙을 확인합니다.", toolName);
String fallbackPodUrl = fallbackProperties.getDefaultUrl();
if (fallbackProperties.getRoutes() != null) {
@@ -66,11 +66,11 @@ public class ToolPlanner {
}
if (fallbackPodUrl == null || fallbackPodUrl.isEmpty()) {
log.error(" [Planner] Fallback ?<3F>우???<3F>?<3F>이 ?<3F>닙?<3F>다. ?? {}", toolName);
throw new RuntimeException("?<3F>당 ??" + toolName + ")???<3F><>??<3F>트리에 존재?<3F><>? ?<3F>습?<3F>다.");
log.error(" [Planner] Fallback 라우팅 대상이 아닙니다. 툴: {}", toolName);
throw new RuntimeException("해당 툴(" + toolName + ")이 레지스트리에 존재하지 않습니다.");
}
log.info(" [Planner] Fallback ?<3F>우??매칭?? {} -> {}", toolName, fallbackPodUrl);
log.info(" [Planner] Fallback 라우팅 매칭됨: {} -> {}", toolName, fallbackPodUrl);
toolMetadata = ToolMetadata.builder()
.uid(toolName)
@@ -80,26 +80,26 @@ public class ToolPlanner {
.build();
}
// 2-1. [?<3F>규] ?<3F>메??그룹??기반 권한 검<EFBFBD>?
// 2-1. [규] 도메인 그룹핑 기반 권한 검
if (tenantId != null && !tenantId.equalsIgnoreCase("system") && toolMetadata.getCategoryKey() != null) {
String normalizedTenantId = tenantId.toLowerCase();
List<String> allowedDomains = securityProperties.getTenantDomains().get(normalizedTenantId);
// 만약 ?<3F>?<3F>문??변???<3F>에???<3F><EFBFBD>??<3F>래 값으<EAB092>???<3F>????<3F>도 (?<3F>위 ?<3F>환??
// 만약 대소문자 변환 후에도 없으면 원래 값으로 한 번 더 시도 (하위 호환성)
if (allowedDomains == null) {
allowedDomains = securityProperties.getTenantDomains().get(tenantId);
}
if (allowedDomains == null ||
(!allowedDomains.contains("ALL") && !allowedDomains.contains(toolMetadata.getCategoryKey()))) {
log.warn(" [Planner] 권한 거<EFBFBD>? - Tenant: {}, Request Domain: {}", tenantId, toolMetadata.getCategoryKey());
throw new SecurityException("?<3F>당 ?<3F>메??" + toolMetadata.getCategoryKey() + ")???<3F>을 ?<3F>행??권한???<3F>습?<3F>다.");
log.warn(" [Planner] 권한 거 - Tenant: {}, Request Domain: {}", tenantId, toolMetadata.getCategoryKey());
throw new SecurityException("해당 도메인(" + toolMetadata.getCategoryKey() + ")의 툴을 실행할 권한이 없습니다.");
}
}
log.info(" [Planner] ??'{}'???<3F>???<3F>행 계획 ?<3F>립 ?<3F>", toolName);
log.info(" [Planner] '{}'에 대한 실행 계획 수립 완", toolName);
// 3. ?<3F>립??계획(??메<>??<3F>이?? 반환
// 3. 수립된 계획(툴 메타데이터) 반환
return toolMetadata;
}
}