Files
invyone/backend-spring/src/main/java/com/erp/ai/client/OpenAiLlmClient.java
T
Johngreen 229b09b895
Build & Deploy to K8s / build-and-deploy (push) Failing after 7m14s
AI관리 시스템 교체: ai-assistant 제거 + 멀티 에이전트 오케스트레이션 이식
Epic A: ai-assistant 디렉토리/Spring 프록시/프론트엔드 메뉴 완전 제거
Epic B: Flyway 도입 + 13 신규 테이블 마이그레이션
Epic C: 9 서비스 + 7 컨트롤러 + LlmClient 추상화 (Java 21/Spring/MyBatis)
Epic D: ApiKey 인증 필터 (sk-pipe-* 키 SHA-256 검증)
Epic E: OpenClaw 외부 엔진 docker-compose 통합
Epic F: Next.js 7 페이지 + lib/api/aiAgent.ts 이식
Epic G: 화면 그룹/메뉴 등록 마이그레이션 (V014)
Epic H: 통합 빌드 검증

- DB: invyone PostgreSQL에 ai_agents/ai_agent_groups/... 13 테이블 + Quartz
- 멀티테넌시: 모든 테이블에 company_code 강제 필터
- LLM: Anthropic/OpenAI/Google/Ollama 직접 클라이언트 (Spring AI 미도입)
- 스케줄러: Quartz JDBC JobStore (cron 기반)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:49:43 +09:00

68 lines
2.5 KiB
Java

package com.erp.ai.client;
import com.erp.ai.exception.LlmClientException;
import com.erp.ai.mapper.AiLlmProviderMapper;
import com.erp.ai.model.AiLlmProvider;
import com.erp.ai.util.AesGcmCipher;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientResponseException;
import java.util.Map;
/**
* OpenAI LLM 클라이언트 (DeepSeek 등 OpenAI 호환 프로바이더 포함).
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class OpenAiLlmClient implements LlmClient {
private static final String DEFAULT_BASE_URL = "https://api.openai.com";
@Qualifier("llmRestClient")
private final RestClient httpClient;
private final AiLlmProviderMapper providerMapper;
private final AesGcmCipher cipher;
@Override
public boolean supports(String model) {
return model != null && (model.startsWith("gpt-") || model.startsWith("o1-") || model.startsWith("o3-"));
}
@Override
public String providerName() {
return "openai";
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> chat(Map<String, Object> request) {
AiLlmProvider provider = providerMapper.getByName("openai");
if (provider == null || !Boolean.TRUE.equals(provider.getIs_active())) {
throw new LlmClientException("OpenAI 프로바이더가 활성화되지 않았습니다.");
}
String apiKey = cipher.decrypt(provider.getApi_key_encrypted());
String baseUrl = provider.getEndpoint() != null ? provider.getEndpoint() : DEFAULT_BASE_URL;
try {
return httpClient.post()
.uri(baseUrl + "/v1/chat/completions")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + apiKey)
.body(request)
.retrieve()
.body(Map.class);
} catch (RestClientResponseException e) {
log.error("OpenAI API 오류 {}: {}", e.getStatusCode().value(), e.getResponseBodyAsString());
throw new LlmClientException("OpenAI API 오류: " + e.getMessage(), e.getStatusCode().value());
} catch (Exception e) {
throw new LlmClientException("OpenAI 호출 실패: " + e.getMessage(), e);
}
}
}