GenAI: chat 모델 OpenAI(gpt-5.6) 호환 + 노트 하단 사용모델 표기
- OciGenAiService.chat(): 모델 인지 분기. openai.* 모델은 maxTokens/ temperature(0.3)를 거부하므로 maxCompletionTokens 사용 + temperature 생략. gemini/cohere 등 기존 모델은 maxTokens+temperature 유지(하위호환). - NoteController: 노트 최종 결과 하단에 사용 모델 푸터 표기 (음성변환 모델 + 교정·요약 모델). 모델명은 설정값에서 조회해 자동 반영. - STT(음성→텍스트)는 OpenRouter Gemini 유지, 변경 없음. - OCI_GENAI_MODEL은 .env에서 openai.gpt-5.6-terra로 전환(커밋 제외). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -133,7 +133,8 @@ public class NoteController {
|
||||
noteRepository.updateContent(id, polished + "\n\n--- 요약 생성 중... ---");
|
||||
String summary = summarizeTranscription(polished);
|
||||
|
||||
String result = "# 요약\n\n" + summary + "\n\n---\n\n# 전문\n\n" + polished;
|
||||
String result = "# 요약\n\n" + summary + "\n\n---\n\n# 전문\n\n" + polished
|
||||
+ modelFooter(null);
|
||||
String newTitle = generateAudioTitle(summary, java.time.LocalDateTime.now());
|
||||
noteRepository.update(id, null, newTitle, result, null);
|
||||
noteRepository.updateNoteType(id, isAudio ? "AUDIO" : "TEXT");
|
||||
@@ -202,11 +203,13 @@ public class NoteController {
|
||||
private void transcribeAsync(String noteId, Path audioFile, String inputTitle) throws IOException, InterruptedException {
|
||||
// === Step 1: STT (OpenRouter Gemini) ===
|
||||
String rawResult = null;
|
||||
String sttModel = null;
|
||||
|
||||
if (openRouterApiKey != null && !openRouterApiKey.isBlank()) {
|
||||
try {
|
||||
noteRepository.updateContent(noteId, "Gemini로 음성 변환 중...");
|
||||
rawResult = transcribeWithOpenRouter(audioFile);
|
||||
sttModel = openRouterModel;
|
||||
log.info("OpenRouter STT: {} chars", rawResult != null ? rawResult.length() : 0);
|
||||
} catch (Exception e) {
|
||||
log.warn("OpenRouter STT failed: {}", e.getMessage());
|
||||
@@ -219,6 +222,7 @@ public class NoteController {
|
||||
try {
|
||||
noteRepository.updateContent(noteId, "Gemma로 음성 변환 중...");
|
||||
rawResult = transcribeWithGemma(audioFile);
|
||||
sttModel = "gemma4:e4b";
|
||||
} catch (Exception e) {
|
||||
log.error("All STT failed for note {}", noteId, e);
|
||||
noteRepository.updateContent(noteId, "모든 음성 변환 실패: " + e.getMessage());
|
||||
@@ -257,7 +261,8 @@ public class NoteController {
|
||||
log.info("Summary complete: {} chars", summary.length());
|
||||
|
||||
// 최종 결과 저장
|
||||
String result = "# 요약\n\n" + summary + "\n\n---\n\n# 전문\n\n" + polished;
|
||||
String result = "# 요약\n\n" + summary + "\n\n---\n\n# 전문\n\n" + polished
|
||||
+ modelFooter(sttModel);
|
||||
String finalTitle = inputTitle.equals("음성 변환 중...")
|
||||
? generateAudioTitle(summary, java.time.LocalDateTime.now())
|
||||
: inputTitle;
|
||||
@@ -271,8 +276,18 @@ public class NoteController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gemma 4 E4B를 사용하여 오디오 파일을 텍스트로 변환
|
||||
* 노트 하단에 사용 모델을 표기하는 푸터를 만든다.
|
||||
* sttModel이 있으면 음성변환 모델도 함께 표기하고, 없으면 교정·요약 모델만 표기한다.
|
||||
* 모델명은 genAiService.getDefaultModel()/openRouterModel에서 가져오므로 설정 변경 시 자동 반영된다.
|
||||
*/
|
||||
private String modelFooter(String sttModel) {
|
||||
String polishModel = genAiService.getDefaultModel();
|
||||
if (sttModel != null && !sttModel.isBlank()) {
|
||||
return "\n\n---\n\n*🤖 음성변환: " + sttModel + " · 교정·요약: " + polishModel + "*";
|
||||
}
|
||||
return "\n\n---\n\n*🤖 교정·요약: " + polishModel + "*";
|
||||
}
|
||||
|
||||
/**
|
||||
* 오디오 파일을 wav로 변환한다 (Ollama 호환성).
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -84,21 +85,28 @@ public class OciGenAiService {
|
||||
modelId = defaultModel;
|
||||
}
|
||||
|
||||
Map<String, Object> chatRequest = new LinkedHashMap<>();
|
||||
chatRequest.put("apiFormat", "GENERIC");
|
||||
chatRequest.put("messages", List.of(
|
||||
Map.of("role", "SYSTEM", "content", List.of(Map.of("type", "TEXT", "text", systemMessage))),
|
||||
Map.of("role", "USER", "content", List.of(Map.of("type", "TEXT", "text", userMessage)))
|
||||
));
|
||||
// OpenAI 계열(openai.gpt-5.x 등)은 maxTokens / temperature(0.3)를 거부한다.
|
||||
// maxCompletionTokens를 사용하고, temperature는 기본값(1)만 허용되므로 생략한다.
|
||||
if (modelId.startsWith("openai.")) {
|
||||
chatRequest.put("maxCompletionTokens", 65536);
|
||||
} else {
|
||||
chatRequest.put("maxTokens", 65536);
|
||||
chatRequest.put("temperature", 0.3);
|
||||
}
|
||||
|
||||
Map<String, Object> payload = Map.of(
|
||||
"compartmentId", compartment,
|
||||
"servingMode", Map.of(
|
||||
"servingType", "ON_DEMAND",
|
||||
"modelId", modelId
|
||||
),
|
||||
"chatRequest", Map.of(
|
||||
"apiFormat", "GENERIC",
|
||||
"messages", List.of(
|
||||
Map.of("role", "SYSTEM", "content", List.of(Map.of("type", "TEXT", "text", systemMessage))),
|
||||
Map.of("role", "USER", "content", List.of(Map.of("type", "TEXT", "text", userMessage)))
|
||||
),
|
||||
"maxTokens", 65536,
|
||||
"temperature", 0.3
|
||||
)
|
||||
"chatRequest", chatRequest
|
||||
);
|
||||
|
||||
String body = objectMapper.writeValueAsString(payload);
|
||||
|
||||
Reference in New Issue
Block a user