diff --git a/.env.example b/.env.example index 8d51571..6288c88 100644 --- a/.env.example +++ b/.env.example @@ -32,7 +32,7 @@ export BACKOFFICE_ORDS_TIMEOUT_SECONDS="10" # --- (2c) OpenAI 호환 AI 호출 (MCP-style Reasoning 탭) --- export BACKOFFICE_AI_ENABLED="false" export BACKOFFICE_AI_BASE_URL="" # 예: https://inference.generativeai.us-chicago-1.oci.oraclecloud.com -export BACKOFFICE_AI_MODEL="" # 예: meta.llama-3.3-70b-instruct +export BACKOFFICE_AI_MODEL="" # 예: openai.gpt-5.4-mini export BACKOFFICE_AI_API_KEY="" export BACKOFFICE_AI_TIMEOUT_SECONDS="30" diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/domain/vpd/VpdPolicyExplanation.java b/src/main/java/com/cloudhandson/vpdbackoffice/domain/vpd/VpdPolicyExplanation.java index 2273d12..66825a8 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/domain/vpd/VpdPolicyExplanation.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/domain/vpd/VpdPolicyExplanation.java @@ -2,6 +2,7 @@ package com.cloudhandson.vpdbackoffice.domain.vpd; public record VpdPolicyExplanation( String status, + String modelName, String answer, String prompt, VpdPolicyDetail detail, diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/service/OpenAiCompatibleClient.java b/src/main/java/com/cloudhandson/vpdbackoffice/service/OpenAiCompatibleClient.java index ad9063f..6bbc545 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/service/OpenAiCompatibleClient.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/service/OpenAiCompatibleClient.java @@ -41,6 +41,11 @@ public class OpenAiCompatibleClient { && hasText(ai.apiKey()); } + public String modelName() { + BackofficeProperties.Ai ai = properties.ai(); + return ai == null ? "" : ai.model(); + } + public String chat(String systemPrompt, String userPrompt) { if (!configured()) { throw new AppException("AI 호출 설정이 없습니다."); @@ -50,7 +55,11 @@ public class OpenAiCompatibleClient { ObjectNode request = objectMapper.createObjectNode(); request.put("model", ai.model()); request.put("temperature", 0.1); - request.put("max_tokens", 1200); + if (usesCompletionTokenLimit(ai.model())) { + request.put("max_completion_tokens", 1200); + } else { + request.put("max_tokens", 1200); + } ArrayNode messages = request.putArray("messages"); messages.add(message("system", systemPrompt)); messages.add(message("user", userPrompt)); @@ -91,6 +100,10 @@ public class OpenAiCompatibleClient { return URI.create(withoutSlash + "/v1/chat/completions"); } + private boolean usesCompletionTokenLimit(String model) { + return model != null && model.startsWith("openai.gpt-5.4"); + } + private String extractAnswer(String body) { try { JsonNode root = objectMapper.readTree(body); diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java b/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java index 6bb9163..dff808c 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/service/VpdPolicyService.java @@ -57,6 +57,7 @@ public class VpdPolicyService { if (!aiClient.configured()) { return new VpdPolicyExplanation( "AI_NOT_CONFIGURED", + aiClient.modelName(), "AI base URL/API Key 설정이 없어 모델 호출은 건너뛰었습니다. 아래 Prompt와 function source를 기준으로 policy/filter 내용을 확인하세요.", prompt, detail, @@ -65,10 +66,11 @@ public class VpdPolicyService { } try { String answer = aiClient.chat(vpdSystemPrompt(), prompt); - return new VpdPolicyExplanation("SUCCESS", answer, prompt, detail, functionSource); + return new VpdPolicyExplanation("SUCCESS", aiClient.modelName(), answer, prompt, detail, functionSource); } catch (Exception e) { return new VpdPolicyExplanation( "AI_CALL_FAILED", + aiClient.modelName(), "AI 호출은 실패했지만 policy/filter 근거는 수집했습니다. 상세: " + e.getMessage(), prompt, detail, diff --git a/src/main/resources/templates/fragments/vpd-policy-explanation.html b/src/main/resources/templates/fragments/vpd-policy-explanation.html index 4e5a0f5..c0dbb87 100644 --- a/src/main/resources/templates/fragments/vpd-policy-explanation.html +++ b/src/main/resources/templates/fragments/vpd-policy-explanation.html @@ -6,9 +6,12 @@